From 587a97e9f5fc49952d7520829d719614477e2717 Mon Sep 17 00:00:00 2001
From: Stanley Yip <stanley_yip@brown.edu>
Date: Sun, 19 Apr 2020 01:50:59 -0700
Subject: generate snap lines

---
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 70 +++++++++++++++++++++-
 1 file changed, 69 insertions(+), 1 deletion(-)

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

diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 87cf716e5..36ec4c72e 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -7,7 +7,7 @@ import { computedFn } from "mobx-utils";
 import { Doc, HeightSym, Opt, WidthSym, DocListCast } from "../../../../new_fields/Doc";
 import { documentSchema, positionSchema } from "../../../../new_fields/documentSchemas";
 import { Id } from "../../../../new_fields/FieldSymbols";
-import { InkData, InkField, InkTool } from "../../../../new_fields/InkField";
+import { InkData, InkField, InkTool, PointData } from "../../../../new_fields/InkField";
 import { List } from "../../../../new_fields/List";
 import { RichTextField } from "../../../../new_fields/RichTextField";
 import { createSchema, listSpec, makeInterface } from "../../../../new_fields/Schema";
@@ -1089,6 +1089,73 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument, u
         ContextMenu.Instance.addItem({ description: "Freeform Options ...", subitems: layoutItems, icon: "eye" });
     }
 
+    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);
+    }
+
+    onPointerOver = (e: React.PointerEvent) => {
+        if (SelectionManager.GetIsDragging()) {
+            const size = this.props.ScreenToLocalTransform().transformDirection(this.props.PanelWidth(), this.props.PanelHeight());
+            const selRect = { left: this.panX() - size[0] / 2, top: this.panY() - size[1] / 2, width: size[0], height: size[1] };
+            console.log(selRect);
+            const selection: Doc[] = [];
+            this.getActiveDocuments().filter(doc => !doc.isBackground && doc.z === undefined).map(doc => {
+                const layoutDoc = Doc.Layout(doc);
+                const x = NumCast(doc.x);
+                const y = NumCast(doc.y);
+                const w = NumCast(layoutDoc._width);
+                const h = NumCast(layoutDoc._height);
+                if (this.intersectRect({ left: x, top: y, width: w, height: h }, selRect)) {
+                    selection.push(doc);
+                }
+            });
+            if (!selection.length) {
+                this.getActiveDocuments().filter(doc => doc.z === undefined).map(doc => {
+                    const layoutDoc = Doc.Layout(doc);
+                    const x = NumCast(doc.x);
+                    const y = NumCast(doc.y);
+                    const w = NumCast(layoutDoc._width);
+                    const h = NumCast(layoutDoc._height);
+                    if (this.intersectRect({ left: x, top: y, width: w, height: h }, selRect)) {
+                        selection.push(doc);
+                    }
+                });
+            }
+            if (!selection.length) {
+                const left = this._downX < this._lastX ? this._downX : this._lastX;
+                const top = this._downY < this._lastY ? this._downY : this._lastY;
+                const topLeft = this.getContainerTransform().transformPoint(left, top);
+                const size = this.getContainerTransform().transformDirection(this._lastX - this._downX, this._lastY - this._downY);
+                const otherBounds = { left: topLeft[0], top: topLeft[1], width: Math.abs(size[0]), height: Math.abs(size[1]) };
+                this.getActiveDocuments().filter(doc => doc.z !== undefined).map(doc => {
+                    const layoutDoc = Doc.Layout(doc);
+                    const x = NumCast(doc.x);
+                    const y = NumCast(doc.y);
+                    const w = NumCast(layoutDoc._width);
+                    const h = NumCast(layoutDoc._height);
+                    if (this.intersectRect({ left: x, top: y, width: w, height: h }, otherBounds)) {
+                        selection.push(doc);
+                    }
+                });
+            }
+            const lines: [PointData, PointData][] = [];
+            selection.forEach(doc => {
+                const x = NumCast(doc.x);
+                const y = NumCast(doc.y);
+                const w = doc[WidthSym]();
+                const h = doc[HeightSym]();
+                lines.push([{ X: x, Y: y }, { X: x + w, Y: y }]); // top line
+                lines.push([{ X: x, Y: y }, { X: x, Y: y + h }]); // left line
+                lines.push([{ X: x + w, Y: y }, { X: x + w, Y: y + h }]); // right line
+                lines.push([{ X: x, Y: y + h }, { X: x + w, Y: y + h }]); // bottom line
+                lines.push([{ X: x + w / 2, Y: y }, { X: x + w / 2, Y: y + h }]); // horizontal center line
+                lines.push([{ X: x, Y: y + h / 2 }, { X: x + w, Y: y + h / 2 }]); // vertical center line
+            })
+            DragManager.SetSnapLines(lines);
+        }
+    }
+
     private childViews = () => {
         const children = typeof this.props.children === "function" ? (this.props.children as any)() as JSX.Element[] : [];
         return [
@@ -1150,6 +1217,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument, u
         // otherwise, they are stored in fieldKey.  All annotations to this document are stored in the extension document
         return <div className={"collectionfreeformview-container"}
             ref={this.createDashEventsTarget}
+            onPointerOver={this.onPointerOver}
             onWheel={this.onPointerWheel} onClick={this.onClick}  //pointerEvents: SelectionManager.GetIsDragging() ? "all" : undefined,
             onPointerDown={this.onPointerDown} onPointerMove={this.onCursorMove} onDrop={this.onExternalDrop.bind(this)} onContextMenu={this.onContextMenu}
             style={{
-- 
cgit v1.2.3-70-g09d2


From 4bdffbc63bcfac5db15e5bb58fb3a41be617b2a1 Mon Sep 17 00:00:00 2001
From: Stanley Yip <stanley_yip@brown.edu>
Date: Mon, 20 Apr 2020 20:06:53 -0700
Subject: better snapping, scaling is off still

---
 src/client/util/DragManager.ts                     | 67 +++++++++++++++++++---
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 55 ++++++++++++------
 2 files changed, 96 insertions(+), 26 deletions(-)

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

diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 21306c339..b0cabfaad 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -74,7 +74,8 @@ export function SetupDrag(
 
 export namespace DragManager {
     let dragDiv: HTMLDivElement;
-    let snapLines: [PointData, PointData][];
+    let horizSnapLines: number[];
+    let vertSnapLines: number[];
 
     export function Root() {
         const root = document.getElementById("root");
@@ -283,8 +284,9 @@ export namespace DragManager {
         StartDrag([ele], {}, downX, downY);
     }
 
-    export function SetSnapLines() {
-        snapLines = [];
+    export function SetSnapLines(horizLines: number[], vertLines: number[]) {
+        horizSnapLines = horizLines;
+        vertSnapLines = vertLines;
     }
 
     function StartDrag(eles: HTMLElement[], dragData: { [id: string]: any }, downX: number, downY: number, options?: DragOptions, finishDrag?: (dropData: DragCompleteEvent) => void) {
@@ -302,12 +304,22 @@ export namespace DragManager {
         const ys: number[] = [];
 
         const docs = dragData instanceof DocumentDragData ? dragData.draggedDocuments : dragData instanceof PdfAnnoDragData ? [dragData.dragDocument] : [];
+        const elesCont = {
+            left: Number.MAX_SAFE_INTEGER,
+            top: Number.MAX_SAFE_INTEGER,
+            right: Number.MIN_SAFE_INTEGER,
+            bottom: Number.MIN_SAFE_INTEGER
+        };
         const dragElements = eles.map(ele => {
             if (!ele.parentNode) dragDiv.appendChild(ele);
             const dragElement = ele.parentNode === dragDiv ? ele : ele.cloneNode(true) as HTMLElement;
             const rect = ele.getBoundingClientRect();
             const scaleX = rect.width / ele.offsetWidth,
                 scaleY = rect.height / ele.offsetHeight;
+            elesCont.left = Math.min(rect.left, elesCont.left);
+            elesCont.top = Math.min(rect.top, elesCont.top);
+            elesCont.right = Math.max(rect.right, elesCont.right);
+            elesCont.bottom = Math.max(rect.bottom, elesCont.bottom);
             xs.push(rect.left);
             ys.push(rect.top);
             scaleXs.push(scaleX);
@@ -357,6 +369,12 @@ export namespace DragManager {
 
         let lastX = downX;
         let lastY = downY;
+        const xFromLeft = downX - elesCont.left;
+        const yFromTop = downY - elesCont.top;
+        const xFromRight = elesCont.right - downX;
+        const yFromBottom = elesCont.bottom - downY;
+        console.log(elesCont);
+        console.log(xFromLeft, yFromTop);
         const moveHandler = (e: PointerEvent) => {
             e.preventDefault(); // required or dragging text menu link item ends up dragging the link button as native drag/drop
             if (dragData instanceof DocumentDragData) {
@@ -373,10 +391,45 @@ export namespace DragManager {
                 }, dragData.droppedDocuments);
             }
             //TODO: Why can't we use e.movementX and e.movementY?
-            const moveX = e.pageX - lastX;
-            const moveY = e.pageY - lastY;
-            lastX = e.pageX;
-            lastY = e.pageY;
+            let thisX = e.pageX;
+            let thisY = e.pageY;
+            const currLeft = e.pageX - xFromLeft;
+            const currTop = e.pageY - yFromTop;
+            const currRight = e.pageX + xFromRight;
+            const currBottom = e.pageY + yFromBottom;
+            const closestLeft = vertSnapLines.reduce((prev, curr) => Math.abs(prev - currLeft) > Math.abs(curr - currLeft) ? curr : prev);
+            const closestTop = horizSnapLines.reduce((prev, curr) => Math.abs(prev - currTop) > Math.abs(curr - currTop) ? curr : prev);
+            const closestRight = vertSnapLines.reduce((prev, curr) => Math.abs(prev - currRight) > Math.abs(curr - currRight) ? curr : prev);
+            const closestBottom = horizSnapLines.reduce((prev, curr) => Math.abs(prev - currBottom) > Math.abs(curr - currBottom) ? curr : prev);
+            const distFromClosestLeft = Math.abs(e.pageX - xFromLeft - closestLeft);
+            const distFromClosestTop = Math.abs(e.pageY - yFromTop - closestTop);
+            const distFromClosestRight = Math.abs(e.pageX + xFromRight - closestRight);
+            const distFromClosestBottom = Math.abs(e.pageY + yFromBottom - closestBottom);
+            if (distFromClosestLeft < 10 && distFromClosestLeft < distFromClosestRight) {
+                thisX = closestLeft + xFromLeft;
+            }
+            else if (distFromClosestRight < 10) {
+                thisX = closestRight - xFromRight;
+            }
+            if (distFromClosestTop < 10 && distFromClosestTop < distFromClosestRight) {
+                thisY = closestTop + yFromTop;
+            }
+            else if (distFromClosestBottom < 10) {
+                thisY = closestBottom - yFromBottom;
+            }
+
+            // const closestHoriz = horizSnapLines.reduce((prev, curr) => Math.abs(prev - thisY) > Math.abs(curr - thisY) ? curr : prev);
+            // const closestVert = vertSnapLines.reduce((prev, curr) => Math.abs(prev - thisX) > Math.abs(curr - thisX) ? curr : prev);
+            // if (Math.abs(thisY - closestHoriz) < 10) {
+            //     thisY = closestHoriz;
+            // }
+            // if (Math.abs(thisX - closestVert) < 10) {
+            //     thisX = closestVert;
+            // }
+            const moveX = thisX - lastX;
+            const moveY = thisY - lastY;
+            lastX = thisX;
+            lastY = thisY;
             dragElements.map((dragElement, i) => (dragElement.style.transform =
                 `translate(${(xs[i] += moveX) + (options?.offsetX || 0)}px, ${(ys[i] += moveY) + (options?.offsetY || 0)}px)  scale(${scaleXs[i]}, ${scaleYs[i]})`)
             );
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 36ec4c72e..c4c37141b 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1094,6 +1094,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument, u
         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);
     }
 
+    @action
     onPointerOver = (e: React.PointerEvent) => {
         if (SelectionManager.GetIsDragging()) {
             const size = this.props.ScreenToLocalTransform().transformDirection(this.props.PanelWidth(), this.props.PanelHeight());
@@ -1123,11 +1124,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument, u
                 });
             }
             if (!selection.length) {
-                const left = this._downX < this._lastX ? this._downX : this._lastX;
-                const top = this._downY < this._lastY ? this._downY : this._lastY;
-                const topLeft = this.getContainerTransform().transformPoint(left, top);
-                const size = this.getContainerTransform().transformDirection(this._lastX - this._downX, this._lastY - this._downY);
-                const otherBounds = { left: topLeft[0], top: topLeft[1], width: Math.abs(size[0]), height: Math.abs(size[1]) };
+                const otherBounds = { left: this.panX(), top: this.panY(), width: Math.abs(size[0]), height: Math.abs(size[1]) };
                 this.getActiveDocuments().filter(doc => doc.z !== undefined).map(doc => {
                     const layoutDoc = Doc.Layout(doc);
                     const x = NumCast(doc.x);
@@ -1139,23 +1136,38 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument, u
                     }
                 });
             }
-            const lines: [PointData, PointData][] = [];
+            const horizLines: number[] = [];
+            const vertLines: number[] = [];
             selection.forEach(doc => {
-                const x = NumCast(doc.x);
-                const y = NumCast(doc.y);
-                const w = doc[WidthSym]();
-                const h = doc[HeightSym]();
-                lines.push([{ X: x, Y: y }, { X: x + w, Y: y }]); // top line
-                lines.push([{ X: x, Y: y }, { X: x, Y: y + h }]); // left line
-                lines.push([{ X: x + w, Y: y }, { X: x + w, Y: y + h }]); // right line
-                lines.push([{ X: x, Y: y + h }, { X: x + w, Y: y + h }]); // bottom line
-                lines.push([{ X: x + w / 2, Y: y }, { X: x + w / 2, Y: y + h }]); // horizontal center line
-                lines.push([{ X: x, Y: y + h / 2 }, { X: x + w, Y: y + h / 2 }]); // vertical center line
-            })
-            DragManager.SetSnapLines(lines);
+                const layoutDoc = Doc.Layout(doc);
+                const x = NumCast(doc.x) - selRect.left;
+                const y = NumCast(doc.y) - selRect.top;
+                const w = NumCast(layoutDoc._width);
+                const h = NumCast(layoutDoc._height);
+                // const s = this._mainCont!.getBoundingClientRect().width / selRect.width;
+                // const tLFromCorner = [s * x, s * y];
+                const topLeft = this.getLocalTransform().inverse().transformDirection(x, y);
+                console.log(topLeft);
+                const topLeftInScreen = [this._mainCont!.getBoundingClientRect().left + topLeft[0], this._mainCont!.getBoundingClientRect().top + topLeft[1]];
+                const docSize = this.getLocalTransform().inverse().transformDirection(w, h);
+                console.log(topLeftInScreen);
+                horizLines.push(topLeftInScreen[1]); // top line
+                horizLines.push(topLeftInScreen[1] + docSize[1]); // bottom line
+                horizLines.push(topLeftInScreen[1] + docSize[1] / 2); // horiz center line
+                vertLines.push(topLeftInScreen[0]);//left line
+                vertLines.push(topLeftInScreen[0] + docSize[0]);// right line
+                vertLines.push(topLeftInScreen[0] + docSize[0] / 2);// vert center line
+            });
+            // console.log(horizLines, vertLines);
+            // this._hLines = horizLines;
+            // this._vLines = vertLines;
+            DragManager.SetSnapLines(horizLines, vertLines);
         }
     }
 
+    @observable private _hLines: number[] | undefined;
+    @observable private _vLines: number[] | undefined;
+
     private childViews = () => {
         const children = typeof this.props.children === "function" ? (this.props.children as any)() as JSX.Element[] : [];
         return [
@@ -1242,7 +1254,12 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument, u
 
                 }}>
             </div>
-
+            <div className="snapLines" style={{ position: "absolute", top: 0, left: 0, width: "100%", height: "100%", pointerEvents: "none" }}>
+                <svg style={{ width: "100%", height: "100%" }}>
+                    {this._hLines?.map(l => <line x1="0" y1={l} x2="1000" y2={l} stroke="black" />)}
+                    {this._vLines?.map(l => <line y1="0" x1={l} y2="1000" x2={l} stroke="black" />)}
+                </svg>
+            </div>
         </div >;
     }
 }
-- 
cgit v1.2.3-70-g09d2


From 58ba643f2642432ca6041f5d348aadf005323b73 Mon Sep 17 00:00:00 2001
From: Stanley Yip <stanley_yip@brown.edu>
Date: Wed, 22 Apr 2020 20:36:06 -0700
Subject: snapping!

---
 src/client/util/DragManager.ts                     | 10 ++++++----
 src/client/views/MainView.tsx                      | 10 ++++++++++
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 22 +++++++---------------
 3 files changed, 23 insertions(+), 19 deletions(-)

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

diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index b0cabfaad..02d9f7200 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -19,6 +19,7 @@ import { DateField } from "../../new_fields/DateField";
 import { DocumentView } from "../views/nodes/DocumentView";
 import { UndoManager } from "./UndoManager";
 import { PointData } from "../../new_fields/InkField";
+import { MainView } from "../views/MainView";
 
 export type dropActionType = "place" | "alias" | "copy" | undefined;
 export function SetupDrag(
@@ -74,8 +75,8 @@ export function SetupDrag(
 
 export namespace DragManager {
     let dragDiv: HTMLDivElement;
-    let horizSnapLines: number[];
-    let vertSnapLines: number[];
+    export let horizSnapLines: number[];
+    export let vertSnapLines: number[];
 
     export function Root() {
         const root = document.getElementById("root");
@@ -284,9 +285,12 @@ export namespace DragManager {
         StartDrag([ele], {}, downX, downY);
     }
 
+    @action
     export function SetSnapLines(horizLines: number[], vertLines: number[]) {
         horizSnapLines = horizLines;
         vertSnapLines = vertLines;
+        MainView.Instance._hLines = horizLines;
+        MainView.Instance._vLines = vertLines;
     }
 
     function StartDrag(eles: HTMLElement[], dragData: { [id: string]: any }, downX: number, downY: number, options?: DragOptions, finishDrag?: (dropData: DragCompleteEvent) => void) {
@@ -373,8 +377,6 @@ export namespace DragManager {
         const yFromTop = downY - elesCont.top;
         const xFromRight = elesCont.right - downX;
         const yFromBottom = elesCont.bottom - downY;
-        console.log(elesCont);
-        console.log(xFromLeft, yFromTop);
         const moveHandler = (e: PointerEvent) => {
             e.preventDefault(); // required or dragging text menu link item ends up dragging the link button as native drag/drop
             if (dragData instanceof DocumentDragData) {
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 40cabcf83..1f88410b8 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -42,6 +42,7 @@ import { OverlayView } from './OverlayView';
 import PDFMenu from './pdf/PDFMenu';
 import { PreviewCursor } from './PreviewCursor';
 import { ScriptField } from '../../new_fields/ScriptField';
+import { DragManager } from '../util/DragManager';
 
 @observer
 export class MainView extends React.Component {
@@ -563,6 +564,9 @@ export class MainView extends React.Component {
         return this._mainViewRef;
     }
 
+    @observable public _hLines: any;
+    @observable public _vLines: any;
+
     render() {
         return (<div className={"mainView-container" + (this.darkScheme ? "-dark" : "")} ref={this._mainViewRef}>
             <DictationOverlay />
@@ -580,6 +584,12 @@ export class MainView extends React.Component {
             <MarqueeOptionsMenu />
             <RichTextMenu />
             <OverlayView />
+            <div className="snapLines" style={{ position: "absolute", top: 0, left: 0, width: "100%", height: "100%", pointerEvents: "none" }}>
+                <svg style={{ width: "100%", height: "100%" }}>
+                    {this._hLines?.map(l => <line x1="0" y1={l} x2="1000" y2={l} stroke="black" />)}
+                    {this._vLines?.map(l => <line y1="0" x1={l} y2="1000" x2={l} stroke="black" />)}
+                </svg>
+            </div>
         </div >);
     }
 }
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index c4c37141b..ef49970e2 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1099,7 +1099,6 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument, u
         if (SelectionManager.GetIsDragging()) {
             const size = this.props.ScreenToLocalTransform().transformDirection(this.props.PanelWidth(), this.props.PanelHeight());
             const selRect = { left: this.panX() - size[0] / 2, top: this.panY() - size[1] / 2, width: size[0], height: size[1] };
-            console.log(selRect);
             const selection: Doc[] = [];
             this.getActiveDocuments().filter(doc => !doc.isBackground && doc.z === undefined).map(doc => {
                 const layoutDoc = Doc.Layout(doc);
@@ -1140,17 +1139,12 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument, u
             const vertLines: number[] = [];
             selection.forEach(doc => {
                 const layoutDoc = Doc.Layout(doc);
-                const x = NumCast(doc.x) - selRect.left;
-                const y = NumCast(doc.y) - selRect.top;
+                const x = NumCast(doc.x);
+                const y = NumCast(doc.y);
                 const w = NumCast(layoutDoc._width);
                 const h = NumCast(layoutDoc._height);
-                // const s = this._mainCont!.getBoundingClientRect().width / selRect.width;
-                // const tLFromCorner = [s * x, s * y];
-                const topLeft = this.getLocalTransform().inverse().transformDirection(x, y);
-                console.log(topLeft);
-                const topLeftInScreen = [this._mainCont!.getBoundingClientRect().left + topLeft[0], this._mainCont!.getBoundingClientRect().top + topLeft[1]];
-                const docSize = this.getLocalTransform().inverse().transformDirection(w, h);
-                console.log(topLeftInScreen);
+                const topLeftInScreen = this.getTransform().inverse().transformPoint(x, y);
+                const docSize = this.getTransform().inverse().transformDirection(w, h);
                 horizLines.push(topLeftInScreen[1]); // top line
                 horizLines.push(topLeftInScreen[1] + docSize[1]); // bottom line
                 horizLines.push(topLeftInScreen[1] + docSize[1] / 2); // horiz center line
@@ -1158,11 +1152,9 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument, u
                 vertLines.push(topLeftInScreen[0] + docSize[0]);// right line
                 vertLines.push(topLeftInScreen[0] + docSize[0] / 2);// vert center line
             });
-            // console.log(horizLines, vertLines);
-            // this._hLines = horizLines;
-            // this._vLines = vertLines;
             DragManager.SetSnapLines(horizLines, vertLines);
         }
+        e.stopPropagation();
     }
 
     @observable private _hLines: number[] | undefined;
@@ -1254,12 +1246,12 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument, u
 
                 }}>
             </div>
-            <div className="snapLines" style={{ position: "absolute", top: 0, left: 0, width: "100%", height: "100%", pointerEvents: "none" }}>
+            {/* <div className="snapLines" style={{ position: "absolute", top: 0, left: 0, width: "100%", height: "100%", pointerEvents: "none" }}>
                 <svg style={{ width: "100%", height: "100%" }}>
                     {this._hLines?.map(l => <line x1="0" y1={l} x2="1000" y2={l} stroke="black" />)}
                     {this._vLines?.map(l => <line y1="0" x1={l} y2="1000" x2={l} stroke="black" />)}
                 </svg>
-            </div>
+            </div> */}
         </div >;
     }
 }
-- 
cgit v1.2.3-70-g09d2


From 37b5878ac3a8f0bd5168431b71e58eee27a3ec99 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Wed, 29 Apr 2020 22:27:59 -0400
Subject: fixed problems with snapping so that it snaps on finishDrag.  cleaned
 up code a bit.

---
 src/client/util/DragManager.ts                     | 81 ++++++++++++----------
 src/client/views/MainView.tsx                      | 14 ++--
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 60 +++++-----------
 3 files changed, 68 insertions(+), 87 deletions(-)

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

diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index a905dff0a..36c26fe2c 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -75,8 +75,8 @@ export function SetupDrag(
 
 export namespace DragManager {
     let dragDiv: HTMLDivElement;
-    export let horizSnapLines: number[];
-    export let vertSnapLines: number[];
+    export let horizSnapLines: number[] = [];
+    export let vertSnapLines: number[] = [];
 
     export function Root() {
         const root = document.getElementById("root");
@@ -296,7 +296,6 @@ export namespace DragManager {
         StartDrag([ele], {}, downX, downY);
     }
 
-    @action
     export function SetSnapLines(horizLines: number[], vertLines: number[]) {
         horizSnapLines = horizLines;
         vertSnapLines = vertLines;
@@ -304,6 +303,36 @@ export namespace DragManager {
         MainView.Instance._vLines = vertLines;
     }
 
+    function snapDrag(e: PointerEvent, xFromLeft: number, yFromTop: number, xFromRight: number, yFromBottom: number) {
+        let thisX = e.pageX;
+        let thisY = e.pageY;
+        const currLeft = e.pageX - xFromLeft;
+        const currTop = e.pageY - yFromTop;
+        const currRight = e.pageX + xFromRight;
+        const currBottom = e.pageY + yFromBottom;
+        const closestLeft = vertSnapLines.reduce((prev, curr) => Math.abs(prev - currLeft) > Math.abs(curr - currLeft) ? curr : prev);
+        const closestTop = horizSnapLines.reduce((prev, curr) => Math.abs(prev - currTop) > Math.abs(curr - currTop) ? curr : prev);
+        const closestRight = vertSnapLines.reduce((prev, curr) => Math.abs(prev - currRight) > Math.abs(curr - currRight) ? curr : prev);
+        const closestBottom = horizSnapLines.reduce((prev, curr) => Math.abs(prev - currBottom) > Math.abs(curr - currBottom) ? curr : prev);
+        const distFromClosestLeft = Math.abs(e.pageX - xFromLeft - closestLeft);
+        const distFromClosestTop = Math.abs(e.pageY - yFromTop - closestTop);
+        const distFromClosestRight = Math.abs(e.pageX + xFromRight - closestRight);
+        const distFromClosestBottom = Math.abs(e.pageY + yFromBottom - closestBottom);
+        if (distFromClosestLeft < 10 && distFromClosestLeft < distFromClosestRight) {
+            thisX = closestLeft + xFromLeft;
+        }
+        else if (distFromClosestRight < 10) {
+            thisX = closestRight - xFromRight;
+        }
+        if (distFromClosestTop < 10 && distFromClosestTop < distFromClosestRight) {
+            thisY = closestTop + yFromTop;
+        }
+        else if (distFromClosestBottom < 10) {
+            thisY = closestBottom - yFromBottom;
+        }
+        return { thisX, thisY };
+    }
+    export let docsBeingDragged: Doc[] = [];
     function StartDrag(eles: HTMLElement[], dragData: { [id: string]: any }, downX: number, downY: number, options?: DragOptions, finishDrag?: (dropData: DragCompleteEvent) => void) {
         eles = eles.filter(e => e);
         if (!dragDiv) {
@@ -318,7 +347,7 @@ export namespace DragManager {
         const xs: number[] = [];
         const ys: number[] = [];
 
-        const docs = dragData instanceof DocumentDragData ? dragData.draggedDocuments : dragData instanceof PdfAnnoDragData ? [dragData.dragDocument] : [];
+        docsBeingDragged = dragData instanceof DocumentDragData ? dragData.draggedDocuments : dragData instanceof PdfAnnoDragData ? [dragData.dragDocument] : [];
         const elesCont = {
             left: Number.MAX_SAFE_INTEGER,
             top: Number.MAX_SAFE_INTEGER,
@@ -354,7 +383,7 @@ export namespace DragManager {
             dragElement.style.width = `${rect.width / scaleX}px`;
             dragElement.style.height = `${rect.height / scaleY}px`;
 
-            if (docs.length) {
+            if (docsBeingDragged.length) {
                 const pdfBox = dragElement.getElementsByTagName("canvas");
                 const pdfBoxSrc = ele.getElementsByTagName("canvas");
                 Array.from(pdfBox).map((pb, i) => pb.getContext('2d')!.drawImage(pdfBoxSrc[i], 0, 0));
@@ -408,32 +437,8 @@ export namespace DragManager {
                     button: 0
                 }, dragData.droppedDocuments);
             }
-            let thisX = e.pageX;
-            let thisY = e.pageY;
-            const currLeft = e.pageX - xFromLeft;
-            const currTop = e.pageY - yFromTop;
-            const currRight = e.pageX + xFromRight;
-            const currBottom = e.pageY + yFromBottom;
-            const closestLeft = vertSnapLines.reduce((prev, curr) => Math.abs(prev - currLeft) > Math.abs(curr - currLeft) ? curr : prev);
-            const closestTop = horizSnapLines.reduce((prev, curr) => Math.abs(prev - currTop) > Math.abs(curr - currTop) ? curr : prev);
-            const closestRight = vertSnapLines.reduce((prev, curr) => Math.abs(prev - currRight) > Math.abs(curr - currRight) ? curr : prev);
-            const closestBottom = horizSnapLines.reduce((prev, curr) => Math.abs(prev - currBottom) > Math.abs(curr - currBottom) ? curr : prev);
-            const distFromClosestLeft = Math.abs(e.pageX - xFromLeft - closestLeft);
-            const distFromClosestTop = Math.abs(e.pageY - yFromTop - closestTop);
-            const distFromClosestRight = Math.abs(e.pageX + xFromRight - closestRight);
-            const distFromClosestBottom = Math.abs(e.pageY + yFromBottom - closestBottom);
-            if (distFromClosestLeft < 10 && distFromClosestLeft < distFromClosestRight) {
-                thisX = closestLeft + xFromLeft;
-            }
-            else if (distFromClosestRight < 10) {
-                thisX = closestRight - xFromRight;
-            }
-            if (distFromClosestTop < 10 && distFromClosestTop < distFromClosestRight) {
-                thisY = closestTop + yFromTop;
-            }
-            else if (distFromClosestBottom < 10) {
-                thisY = closestBottom - yFromBottom;
-            }
+
+            const { thisX, thisY } = snapDrag(e, xFromLeft, yFromTop, xFromRight, yFromBottom);
 
             alias = "move";
             const moveX = thisX - lastX;
@@ -462,7 +467,7 @@ export namespace DragManager {
         };
         const upHandler = (e: PointerEvent) => {
             hideDragShowOriginalElements();
-            dispatchDrag(eles, e, dragData, options, finishDrag);
+            dispatchDrag(eles, e, dragData, xFromLeft, yFromTop, xFromRight, yFromBottom, options, finishDrag);
             SelectionManager.SetIsDragging(false);
             endDrag();
             options?.dragComplete?.(new DragCompleteEvent(false, dragData));
@@ -471,7 +476,8 @@ export namespace DragManager {
         document.addEventListener("pointerup", upHandler);
     }
 
-    function dispatchDrag(dragEles: HTMLElement[], e: PointerEvent, dragData: { [index: string]: any }, options?: DragOptions, finishDrag?: (e: DragCompleteEvent) => void) {
+    function dispatchDrag(dragEles: HTMLElement[], e: PointerEvent, dragData: { [index: string]: any },
+        xFromLeft: number, yFromTop: number, xFromRight: number, yFromBottom: number, options?: DragOptions, finishDrag?: (e: DragCompleteEvent) => void) {
         const removed = dragData.dontHideOnDrop ? [] : dragEles.map(dragEle => {
             const ret = { ele: dragEle, w: dragEle.style.width, h: dragEle.style.height, o: dragEle.style.overflow };
             dragEle.style.width = "0";
@@ -485,14 +491,15 @@ export namespace DragManager {
             r.ele.style.height = r.h;
             r.ele.style.overflow = r.o;
         });
+        const { thisX, thisY } = snapDrag(e, xFromLeft, yFromTop, xFromRight, yFromBottom);
         if (target) {
             const complete = new DragCompleteEvent(false, dragData);
             target.dispatchEvent(
                 new CustomEvent<DropEvent>("dashPreDrop", {
                     bubbles: true,
                     detail: {
-                        x: e.x,
-                        y: e.y,
+                        x: thisX,
+                        y: thisY,
                         complete: complete,
                         shiftKey: e.shiftKey,
                         altKey: e.altKey,
@@ -506,8 +513,8 @@ export namespace DragManager {
                 new CustomEvent<DropEvent>("dashOnDrop", {
                     bubbles: true,
                     detail: {
-                        x: e.x,
-                        y: e.y,
+                        x: thisX,
+                        y: thisY,
                         complete: complete,
                         shiftKey: e.shiftKey,
                         altKey: e.altKey,
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 0102d1327..62b2d1d18 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -588,13 +588,13 @@ export class MainView extends React.Component {
             <MarqueeOptionsMenu />
             <RichTextMenu />
             <OverlayView />
-            {/* TO VIEW SNAP LINES
-            <div className="snapLines" style={{ position: "absolute", top: 0, left: 0, width: "100%", height: "100%", pointerEvents: "none" }}>
-                <svg style={{ width: "100%", height: "100%" }}>
-                    {this._hLines?.map(l => <line x1="0" y1={l} x2="2000" y2={l} stroke="black" />)}
-                    {this._vLines?.map(l => <line y1="0" x1={l} y2="2000" x2={l} stroke="black" />)}
-                </svg>
-            </div> */}
+            {// TO VIEW SNAP LINES
+             /* <div className="snapLines" style={{ position: "absolute", top: 0, left: 0, width: "100%", height: "100%", pointerEvents: "none" }}>
+                    <svg style={{ width: "100%", height: "100%" }}>
+                        {this._hLines?.map(l => <line x1="0" y1={l} x2="2000" y2={l} stroke="black" />)}
+                        {this._vLines?.map(l => <line y1="0" x1={l} y2="2000" x2={l} stroke="black" />)}
+                    </svg>
+            </div>*/}
             <TimelineMenu />
         </div >);
     }
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index d291cad21..0c9403429 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1129,7 +1129,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
                 input.click();
             }
         });
-        ContextMenu.Instance.addItem({ description: "Options ...", subitems: optionItems, icon: "eye" });
+        ContextMenu.Instance.addItem({ description: "Options...", subitems: optionItems, icon: "eye" });
         this._timelineRef.current!.timelineContextMenu(e);
     }
 
@@ -1144,51 +1144,24 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
             const size = this.props.ScreenToLocalTransform().transformDirection(this.props.PanelWidth(), this.props.PanelHeight());
             const selRect = { left: this.panX() - size[0] / 2, top: this.panY() - size[1] / 2, width: size[0], height: size[1] };
             const selection: Doc[] = [];
-            this.getActiveDocuments().filter(doc => !doc.isBackground && doc.z === undefined).map(doc => {
-                const layoutDoc = Doc.Layout(doc);
-                const x = NumCast(doc.x);
-                const y = NumCast(doc.y);
-                const w = NumCast(layoutDoc._width);
-                const h = NumCast(layoutDoc._height);
-                if (this.intersectRect({ left: x, top: y, width: w, height: h }, selRect)) {
+            const docDims = (doc: Doc, layoutDoc: Doc) => ({ left: NumCast(doc.x), top: NumCast(doc.y), width: NumCast(layoutDoc._width), height: NumCast(layoutDoc._height) });
+            const compareDoc = (doc: Doc, rect: { left: number, top: number, width: number, height: number }) => {
+                if (this.intersectRect(docDims(doc, Doc.Layout(doc)), rect)) {
                     selection.push(doc);
                 }
-            });
-            if (!selection.length) {
-                this.getActiveDocuments().filter(doc => doc.z === undefined).map(doc => {
-                    const layoutDoc = Doc.Layout(doc);
-                    const x = NumCast(doc.x);
-                    const y = NumCast(doc.y);
-                    const w = NumCast(layoutDoc._width);
-                    const h = NumCast(layoutDoc._height);
-                    if (this.intersectRect({ left: x, top: y, width: w, height: h }, selRect)) {
-                        selection.push(doc);
-                    }
-                });
-            }
-            if (!selection.length) {
-                const otherBounds = { left: this.panX(), top: this.panY(), width: Math.abs(size[0]), height: Math.abs(size[1]) };
-                this.getActiveDocuments().filter(doc => doc.z !== undefined).map(doc => {
-                    const layoutDoc = Doc.Layout(doc);
-                    const x = NumCast(doc.x);
-                    const y = NumCast(doc.y);
-                    const w = NumCast(layoutDoc._width);
-                    const h = NumCast(layoutDoc._height);
-                    if (this.intersectRect({ left: x, top: y, width: w, height: h }, otherBounds)) {
-                        selection.push(doc);
-                    }
-                });
             }
+            const otherBounds = { left: this.panX(), top: this.panY(), width: Math.abs(size[0]), height: Math.abs(size[1]) };
+            this.getActiveDocuments().filter(doc => !doc.isBackground && doc.z === undefined).map(doc => compareDoc(doc, selRect));  // first try foreground docs
+            !selection.length && this.getActiveDocuments().filter(doc => doc.z === undefined).map(doc => compareDoc(doc, selRect)); // then background docs
+            !selection.length && this.getActiveDocuments().filter(doc => doc.z !== undefined).map(doc => compareDoc(doc, otherBounds)); // then floating docs
+
             const horizLines: number[] = [];
             const vertLines: number[] = [];
-            selection.forEach(doc => {
-                const layoutDoc = Doc.Layout(doc);
-                const x = NumCast(doc.x);
-                const y = NumCast(doc.y);
-                const w = NumCast(layoutDoc._width);
-                const h = NumCast(layoutDoc._height);
-                const topLeftInScreen = this.getTransform().inverse().transformPoint(x, y);
-                const docSize = this.getTransform().inverse().transformDirection(w, h);
+            selection.filter(doc => !DragManager.docsBeingDragged.includes(doc)).forEach(doc => {
+                const { left, top, width, height } = docDims(doc, Doc.Layout(doc));
+                const topLeftInScreen = this.getTransform().inverse().transformPoint(left, top);
+                const docSize = this.getTransform().inverse().transformDirection(width, height);
+
                 horizLines.push(topLeftInScreen[1]); // top line
                 horizLines.push(topLeftInScreen[1] + docSize[1]); // bottom line
                 horizLines.push(topLeftInScreen[1] + docSize[1] / 2); // horiz center line
@@ -1292,12 +1265,13 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
 
                 }}>
             </div>
-            {/* <div className="snapLines" style={{ position: "absolute", top: 0, left: 0, width: "100%", height: "100%", pointerEvents: "none" }}>
+            {// uncomment to show snap lines
+             /*<div className="snapLines" style={{ position: "absolute", top: 0, left: 0, width: "100%", height: "100%", pointerEvents: "none" }}>
                 <svg style={{ width: "100%", height: "100%" }}>
                     {this._hLines?.map(l => <line x1="0" y1={l} x2="1000" y2={l} stroke="black" />)}
                     {this._vLines?.map(l => <line y1="0" x1={l} y2="1000" x2={l} stroke="black" />)}
                 </svg>
-            </div> */}
+            </div>*/}
         </div >;
     }
 }
-- 
cgit v1.2.3-70-g09d2


From 90c45914694a971c1b3cb356921c04f337625db5 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Thu, 30 Apr 2020 00:06:08 -0400
Subject: fixes for snapping & timeline.  changed looi of document decorations

---
 src/client/util/DragManager.ts                     |  8 ++---
 src/client/views/DocumentDecorations.scss          | 34 +++++++++++++++++++++-
 src/client/views/DocumentDecorations.tsx           |  9 +++---
 src/client/views/MainView.tsx                      |  1 -
 src/client/views/MetadataEntryMenu.scss            |  9 +++---
 src/client/views/animationtimeline/Timeline.tsx    | 33 ++++++++-------------
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 14 ++++++---
 .../views/nodes/formattedText/FormattedTextBox.tsx |  8 +++--
 8 files changed, 74 insertions(+), 42 deletions(-)

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

diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 36c26fe2c..bccdf38ce 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -310,10 +310,10 @@ export namespace DragManager {
         const currTop = e.pageY - yFromTop;
         const currRight = e.pageX + xFromRight;
         const currBottom = e.pageY + yFromBottom;
-        const closestLeft = vertSnapLines.reduce((prev, curr) => Math.abs(prev - currLeft) > Math.abs(curr - currLeft) ? curr : prev);
-        const closestTop = horizSnapLines.reduce((prev, curr) => Math.abs(prev - currTop) > Math.abs(curr - currTop) ? curr : prev);
-        const closestRight = vertSnapLines.reduce((prev, curr) => Math.abs(prev - currRight) > Math.abs(curr - currRight) ? curr : prev);
-        const closestBottom = horizSnapLines.reduce((prev, curr) => Math.abs(prev - currBottom) > Math.abs(curr - currBottom) ? curr : prev);
+        const closestLeft = vertSnapLines.reduce((prev, curr) => Math.abs(prev - currLeft) > Math.abs(curr - currLeft) ? curr : prev, currLeft);
+        const closestTop = horizSnapLines.reduce((prev, curr) => Math.abs(prev - currTop) > Math.abs(curr - currTop) ? curr : prev, currTop);
+        const closestRight = vertSnapLines.reduce((prev, curr) => Math.abs(prev - currRight) > Math.abs(curr - currRight) ? curr : prev, currRight);
+        const closestBottom = horizSnapLines.reduce((prev, curr) => Math.abs(prev - currBottom) > Math.abs(curr - currBottom) ? curr : prev, currBottom);
         const distFromClosestLeft = Math.abs(e.pageX - xFromLeft - closestLeft);
         const distFromClosestTop = Math.abs(e.pageY - yFromTop - closestTop);
         const distFromClosestRight = Math.abs(e.pageX + xFromRight - closestRight);
diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss
index 28cf9fd47..61d517d43 100644
--- a/src/client/views/DocumentDecorations.scss
+++ b/src/client/views/DocumentDecorations.scss
@@ -21,9 +21,13 @@ $linkGap : 3px;
         background: none;
     }
 
+
     .documentDecorations-resizer {
         pointer-events: auto;
         background: $alt-accent;
+        opacity: 0.1;
+    }
+    .documentDecorations-resizer:hover {
         opacity: 1;
     }
 
@@ -80,7 +84,20 @@ $linkGap : 3px;
     #documentDecorations-topLeftResizer,
     #documentDecorations-bottomRightResizer {
         cursor: nwse-resize;
-        background: dimGray;
+        background: unset;
+        opacity: 1;
+    }
+    #documentDecorations-topLeftResizer {
+        border-left: black 2px solid;
+        border-top: black solid 2px;
+    }
+    #documentDecorations-bottomRightResizer {
+        border-right: black 2px solid;
+        border-bottom: black solid 2px;
+    }
+    #documentDecorations-topLeftResizer:hover,
+    #documentDecorations-bottomRightResizer:hover {
+        opacity: 1;
     }
 
     #documentDecorations-bottomRightResizer {
@@ -89,8 +106,23 @@ $linkGap : 3px;
 
     #documentDecorations-topRightResizer,
     #documentDecorations-bottomLeftResizer {
+        cursor: nesw-resize;
+        background: unset;
+        opacity: 1;
+    }
+    #documentDecorations-topRightResizer {
+        border-right: black 2px solid;
+        border-top: black 2px solid;
+    }
+    #documentDecorations-bottomLeftResizer {
+        border-left: black 2px solid;
+        border-bottom: black 2px solid;
+    }
+    #documentDecorations-topRightResizer:hover,
+    #documentDecorations-bottomLeftResizer:hover {
         cursor: nesw-resize;
         background: dimGray;
+        opacity: 1;
     }
 
     #documentDecorations-topResizer,
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 312acd5b2..973ec2e89 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -473,10 +473,11 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
                     onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
                 <div id="documentDecorations-bottomRightResizer" className="documentDecorations-resizer"
                     onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
-                {seldoc.props.renderDepth <= 1 || !seldoc.props.ContainingCollectionView ? (null) : <div id="documentDecorations-levelSelector" className="documentDecorations-selector" title="tap to select containing document"
-                    onPointerDown={this.onSelectorUp} onContextMenu={(e) => e.preventDefault()}>
-                    <FontAwesomeIcon className="documentdecorations-times" icon={faArrowAltCircleUp} size="lg" />
-                </div>}
+                {seldoc.props.renderDepth <= 1 || !seldoc.props.ContainingCollectionView ? (null) :
+                    <div id="documentDecorations-levelSelector" className="documentDecorations-selector"
+                        title="tap to select containing document" onPointerDown={this.onSelectorUp} onContextMenu={e => e.preventDefault()}>
+                        <FontAwesomeIcon className="documentdecorations-times" icon={faArrowAltCircleUp} size="lg" />
+                    </div>}
                 <div id="documentDecorations-borderRadius" className="documentDecorations-radius"
                     onPointerDown={this.onRadiusDown} onContextMenu={(e) => e.preventDefault()}></div>
 
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 62b2d1d18..e5a8ebcb5 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -42,7 +42,6 @@ import { OverlayView } from './OverlayView';
 import PDFMenu from './pdf/PDFMenu';
 import { PreviewCursor } from './PreviewCursor';
 import { ScriptField } from '../../new_fields/ScriptField';
-import { DragManager } from '../util/DragManager';
 import { TimelineMenu } from './animationtimeline/TimelineMenu';
 
 @observer
diff --git a/src/client/views/MetadataEntryMenu.scss b/src/client/views/MetadataEntryMenu.scss
index 5776cf070..28de0b7a5 100644
--- a/src/client/views/MetadataEntryMenu.scss
+++ b/src/client/views/MetadataEntryMenu.scss
@@ -9,9 +9,10 @@
 }
 
 .metadataEntry-autoSuggester {
-    width: 100%;
+    width: 80%;
     height: 100%;
-    padding-right: 10px;
+    margin: 0;
+    display: inline-block;
 }
 
 #metadataEntry-outer {
@@ -25,7 +26,7 @@
     flex-direction: column;
 }
 .metadataEntry-inputArea {
-    display:flex;
+    display:inline-block;
     flex-direction: row;
 }
 
@@ -44,7 +45,7 @@
 .react-autosuggest__input {
     border: 1px solid #aaa;
     border-radius: 4px;
-    width: 100%;
+    width: 75%;
 }
 
 .react-autosuggest__input--focused {
diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx
index fe1e40778..77656b85f 100644
--- a/src/client/views/animationtimeline/Timeline.tsx
+++ b/src/client/views/animationtimeline/Timeline.tsx
@@ -71,7 +71,6 @@ export class Timeline extends React.Component<FieldViewProps> {
     @observable private _tickIncrement = this.DEFAULT_TICK_INCREMENT;
     @observable private _time = 100000; //DEFAULT
     @observable private _playButton = faPlayCircle;
-    @observable private _timelineVisible = false;
     @observable private _mouseToggled = false;
     @observable private _doubleClickEnabled = false;
     @observable private _titleHeight = 0;
@@ -336,20 +335,6 @@ export class Timeline extends React.Component<FieldViewProps> {
     }
 
 
-    /**
-     * context menu function. 
-     * opens the timeline or closes the timeline. 
-     * Used in: Freeform
-     */
-    timelineContextMenu = (e: React.MouseEvent): void => {
-        ContextMenu.Instance.addItem({
-            description: (this._timelineVisible ? "Close" : "Open") + " Animation Timeline", event: action(() => {
-                this._timelineVisible = !this._timelineVisible;
-            }), icon: this._timelineVisible ? faEyeSlash : faEye
-        });
-    }
-
-
     /**
      * timeline zoom function 
      * use mouse middle button to zoom in/out the timeline
@@ -463,7 +448,7 @@ export class Timeline extends React.Component<FieldViewProps> {
                             <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: this._timelineVisible ? "flex" : "none" }}>
+                    <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._isAuthoring ? "flex" : "none" }} title="Set Default View" onClick={() => this.setView(this.props.Document)}><FontAwesomeIcon icon="expand-arrows-alt" size="lg" /></div>
@@ -481,10 +466,16 @@ export class Timeline extends React.Component<FieldViewProps> {
             );
         }
         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" }}>{`Current: ${this.getCurrentTime()}`}</div>
-                    <div className="animation-text" style={{ fontSize: "10px", width: "100%", display: !this.props.Document.isATOn ? "block" : "none" }}>{`Total: ${this.toReadTime(this._time)}`}</div>
+                    <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" }}>
+                        {ttime}
+                    </div>
                 </div>
             );
         }
@@ -601,8 +592,8 @@ export class Timeline extends React.Component<FieldViewProps> {
         trace();
         // change visible and total width
         return (
-            <div style={{ visibility: this._timelineVisible ? "visible" : "hidden" }}>
-                <div key="timeline_wrapper" style={{ visibility: BoolCast(this.props.Document.isATOn && this._timelineVisible) ? "visible" : "hidden", left: "0px", top: "0px", position: "absolute", width: "100%", transform: "translate(0px, 0px)" }}>
+            <div style={{ visibility: "visible" }}>
+                <div key="timeline_wrapper" style={{ visibility: BoolCast(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" ref={this._infoContainer} onWheel={this.onWheelZoom}>
                             {this.drawTicks()}
@@ -611,7 +602,7 @@ export class Timeline extends React.Component<FieldViewProps> {
                             </div>
                             <div key="timeline_trackbox" className="trackbox" ref={this._trackbox} onPointerDown={this.onPanDown} style={{ width: `${this._totalLength}px` }}>
                                 {DocListCast(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={this._timelineVisible} />
+                                    <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>
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 0c9403429..77de486d9 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1,5 +1,5 @@
 import { library } from "@fortawesome/fontawesome-svg-core";
-import { faEye } from "@fortawesome/free-regular-svg-icons";
+import { faEye, faEyeSlash } from "@fortawesome/free-regular-svg-icons";
 import { faBraille, faChalkboard, faCompass, faCompressArrowsAlt, faExpandArrowsAlt, faFileUpload, faPaintBrush, faTable, faUpload } from "@fortawesome/free-solid-svg-icons";
 import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from "mobx";
 import { observer } from "mobx-react";
@@ -1093,7 +1093,6 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         const options = ContextMenu.Instance.findByDescription("Options...");
         const optionItems: ContextMenuProps[] = options && "subitems" in options ? options.subitems : [];
 
-        this._timelineRef.current!.timelineContextMenu(e);
         optionItems.push({ description: "reset view", event: () => { this.props.Document._panX = this.props.Document._panY = 0; this.props.Document.scale = 1; }, icon: "compress-arrows-alt" });
         optionItems.push({ description: `${this.Document._LODdisable ? "Enable LOD" : "Disable LOD"}`, event: () => this.Document._LODdisable = !this.Document._LODdisable, icon: "table" });
         optionItems.push({ description: `${this.fitToContent ? "Unset" : "Set"} Fit To Container`, event: () => this.Document._fitToBox = !this.fitToContent, icon: !this.fitToContent ? "expand-arrows-alt" : "compress-arrows-alt" });
@@ -1130,8 +1129,15 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
             }
         });
         ContextMenu.Instance.addItem({ description: "Options...", subitems: optionItems, icon: "eye" });
-        this._timelineRef.current!.timelineContextMenu(e);
+
+
+        ContextMenu.Instance.addItem({
+            description: (this._timelineVisible ? "Close" : "Open") + " Animation Timeline", event: action(() => {
+                this._timelineVisible = !this._timelineVisible;
+            }), icon: this._timelineVisible ? faEyeSlash : faEye
+        });
     }
+    @observable _timelineVisible = false;
 
     intersectRect(r1: { left: number, top: number, width: number, height: number },
         r2: { left: number, top: number, width: number, height: number }) {
@@ -1215,7 +1221,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
                 easing={this.easing} viewDefDivClick={this.props.viewDefDivClick} zoomScaling={this.zoomScaling} panX={this.panX} panY={this.panY}>
                 {this.children}
             </CollectionFreeFormViewPannableContents>
-            <Timeline ref={this._timelineRef} {...this.props} />
+            {this._timelineVisible ? <Timeline ref={this._timelineRef} {...this.props} /> : (null)}
         </MarqueeView>;
     }
 
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 2038efbc6..4df693c9a 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -435,9 +435,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                 this.rootDoc.isTemplateForField = "";
                 this.rootDoc.layoutKey = "layout";
                 this.rootDoc.isTemplateDoc = makeTemplate(this.rootDoc, true, title);
-                this.rootDoc._width = this.layoutDoc._width || 300;  // the width and height are stored on the template, since we're getting rid of the old template
-                this.rootDoc._height = this.layoutDoc._height || 200;  // we need to copy them over to the root.  This should probably apply to all '_' fields
-                this.rootDoc._backgroundColor = Cast(this.layoutDoc._backgroundColor, "string", null);
+                setTimeout(() => {
+                    this.rootDoc._width = this.layoutDoc._width || 300;  // the width and height are stored on the template, since we're getting rid of the old template
+                    this.rootDoc._height = this.layoutDoc._height || 200;  // we need to copy them over to the root.  This should probably apply to all '_' fields
+                    this.rootDoc._backgroundColor = Cast(this.layoutDoc._backgroundColor, "string", null);
+                }, 10);
                 Doc.AddDocToList(Cast(Doc.UserDoc()["template-notes"], Doc, null), "data", this.rootDoc);
             }, icon: "eye"
         });
-- 
cgit v1.2.3-70-g09d2


From 0f4b9e541e4e1332bbed95f4a99113162ffef806 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Thu, 30 Apr 2020 11:10:27 -0400
Subject: added double click script support

---
 src/client/documents/Documents.ts                  |  2 ++
 src/client/views/ScriptBox.tsx                     |  4 ++--
 .../views/collections/CollectionStackingView.tsx   |  7 ++++---
 src/client/views/collections/CollectionSubView.tsx |  2 ++
 src/client/views/collections/CollectionView.tsx    |  7 ++++---
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  3 ++-
 .../CollectionMulticolumnView.tsx                  |  2 ++
 .../CollectionMultirowView.tsx                     |  3 ++-
 .../views/nodes/ContentFittingDocumentView.tsx     |  1 +
 src/client/views/nodes/DocumentView.tsx            | 23 ++++++++++++++++------
 src/new_fields/Types.ts                            |  4 ++--
 src/new_fields/documentSchemas.ts                  |  1 +
 .../authentication/models/current_user_utils.ts    |  8 ++++++--
 13 files changed, 47 insertions(+), 20 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 0d1d73ca3..436e59daf 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -95,6 +95,7 @@ export interface DocumentOptions {
     hideHeadings?: boolean; // whether stacking view column headings should be hidden
     isTemplateForField?: string; // the field key for which the containing document is a rendering template
     isTemplateDoc?: boolean;
+    scriptKey?: string; // the script key that a child click func script template document will write into
     templates?: List<string>;
     backgroundColor?: string | ScriptField;  // background color for data doc 
     _backgroundColor?: string | ScriptField; // background color for each template layout doc ( overrides backgroundColor )
@@ -132,6 +133,7 @@ export interface DocumentOptions {
     activePen?: Doc; // which pen document is currently active (used as the radio button state for the 'unhecked' pen tool scripts)
     onClick?: ScriptField;
     onChildClick?: ScriptField; // script given to children of a collection to execute when they are clicked
+    onChildDoubleClick?: ScriptField; // script given to children of a collection to execute when they are double clicked
     onPointerDown?: ScriptField;
     onPointerUp?: ScriptField;
     dropConverter?: ScriptField; // script to run when documents are dropped on this Document.
diff --git a/src/client/views/ScriptBox.tsx b/src/client/views/ScriptBox.tsx
index 153b81876..66d3b937e 100644
--- a/src/client/views/ScriptBox.tsx
+++ b/src/client/views/ScriptBox.tsx
@@ -81,9 +81,9 @@ export class ScriptBox extends React.Component<ScriptBoxProps> {
         );
     }
     //let l = docList(this.source[0].data).length; if (l) { let ind = this.target[0].index !== undefined ? (this.target[0].index+1) % l : 0;  this.target[0].index = ind;  this.target[0].proto = getProto(docList(this.source[0].data)[ind]);}
-    public static EditButtonScript(title: string, doc: Doc, fieldKey: string, clientX: number, clientY: number, contextParams?: { [name: string]: string }) {
+    public static EditButtonScript(title: string, doc: Doc, fieldKey: string, clientX: number, clientY: number, contextParams?: { [name: string]: string }, defaultScript?: ScriptField) {
         let overlayDisposer: () => void = emptyFunction;
-        const script = ScriptCast(doc[fieldKey]);
+        const script = ScriptCast(doc[fieldKey]) || defaultScript;
         let originalText: string | undefined = undefined;
         if (script) {
             originalText = script.script.originalScript;
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index e3720bf01..556d7df5c 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -150,8 +150,8 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
         this.createDashEventsTarget(ele!); //so the whole grid is the drop target?
     }
 
-    @computed get onChildClickHandler() { return ScriptCast(this.Document.onChildClick); }
-    @computed get onClickHandler() { return ScriptCast(this.Document.onChildClick); }
+    @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) {
@@ -178,7 +178,8 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
             fitToBox={BoolCast(this.props.Document._freezeChildDimensions)}
             rootSelected={this.rootSelected}
             dropAction={StrCast(this.props.Document.childDropAction) as dropActionType}
-            onClick={layoutDoc.isTemplateDoc ? this.onClickHandler : this.onChildClickHandler}
+            onClick={this.onChildClickHandler}
+            onDoubleClick={this.onChildDoubleClickHandler}
             getTransform={dxf}
             focus={this.props.focus}
             CollectionDoc={this.props.CollectionView?.props.Document}
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 1bfd408f8..8cc1af55b 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -43,6 +43,8 @@ export interface CollectionViewProps extends FieldViewProps {
 export interface SubCollectionViewProps extends CollectionViewProps {
     CollectionView: Opt<CollectionView>;
     children?: never | (() => JSX.Element[]) | React.ReactNode;
+    childClickScript?: ScriptField;
+    childDoubleClickScript?: ScriptField;
     freezeChildDimensions?: boolean; // used by TimeView to coerce documents to treat their width height as their native width/height
     overrideDocuments?: Doc[]; // used to override the documents shown by the sub collection to an explicit list (see LinkBox)
     ignoreFields?: string[]; // used in TreeView to ignore specified fields (see LinkBox)
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 801704673..2c52097aa 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -247,13 +247,14 @@ export class CollectionView extends Touchable<FieldViewProps> {
 
             const existingOnClick = ContextMenu.Instance.findByDescription("OnClick...");
             const onClicks = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : [];
-            const funcs = [{ key: "onChildClick", name: "On Child Clicked", script: undefined as any as ScriptField }];
+            const funcs = [{ key: "onChildClick", name: "On Child Clicked", script: undefined as any as ScriptField },
+            { key: "onChildDoubleClick", name: "On Child Double Clicked", script: undefined as any as ScriptField }];
             DocListCast(Cast(Doc.UserDoc().childClickFuncs, Doc, null).data).forEach(childClick =>
-                funcs.push({ key: "onChildClick", name: StrCast(childClick.title), script: ScriptCast(childClick.script) }));
+                funcs.push({ key: StrCast(childClick.scriptKey), name: StrCast(childClick.title), script: ScriptCast(childClick.data) }));
             funcs.map(func => onClicks.push({
                 description: `Edit ${func.name} script`, icon: "edit", event: (obj: any) => {
                     func.script && (this.props.Document[func.key] = ObjectField.MakeCopy(func.script));
-                    ScriptBox.EditButtonScript(func.name + "...", this.props.Document, func.key, obj.x, obj.y, { thisContainer: Doc.name });
+                    ScriptBox.EditButtonScript(func.name + "...", this.props.Document, func.key, obj.x, obj.y, { thisContainer: Doc.name }, func.script);
                 }
             }));
             !existingOnClick && ContextMenu.Instance.addItem({ description: "OnClick...", subitems: onClicks, icon: "hand-point-right" });
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 77de486d9..11d0f298d 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -70,7 +70,6 @@ type PanZoomDocument = makeInterface<[typeof panZoomSchema, typeof documentSchem
 const PanZoomDocument = makeInterface(panZoomSchema, documentSchema, positionSchema, pageSchema);
 export type collectionFreeformViewProps = {
     forceScaling?: boolean; // whether to force scaling of content (needed by ImageBox)
-    childClickScript?: ScriptField;
     viewDefDivClick?: ScriptField;
 };
 
@@ -855,6 +854,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
 
     @computed get libraryPath() { return this.props.LibraryPath ? [...this.props.LibraryPath, this.props.Document] : []; }
     @computed get onChildClickHandler() { return this.props.childClickScript || ScriptCast(this.Document.onChildClick); }
+    @computed get onChildDoubleClickHandler() { return this.props.childDoubleClickScript || ScriptCast(this.Document.onChildDoubleClick); }
     backgroundHalo = () => BoolCast(this.Document.useClusters);
     @computed get backgroundActive() { return this.layoutDoc.isBackground && (this.props.ContainingCollectionView?.active() || this.props.active()); }
     parentActive = () => this.props.active() || this.backgroundActive ? true : false;
@@ -873,6 +873,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
             dropAction: StrCast(this.props.Document.childDropAction) as dropActionType,
             //onClick: undefined, // this.props.onClick,  // bcz: check this out -- I don't think we want to inherit click handlers, or we at least need a way to ignore them
             onClick: this.onChildClickHandler,
+            onDoubleClick: this.onChildDoubleClickHandler,
             ScreenToLocalTransform: childLayout.z ? this.getTransformOverlay : this.getTransform,
             renderDepth: this.props.renderDepth + 1,
             PanelWidth: childLayout[WidthSym],
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
index 9d09ecc3b..66d441115 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -203,6 +203,7 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu
 
 
     @computed get onChildClickHandler() { return ScriptCast(this.Document.onChildClick); }
+    @computed get onChildDoubleClickHandler() { return ScriptCast(this.Document.onChildDoubleClick); }
 
 
     addDocTab = (doc: Doc, where: string) => {
@@ -229,6 +230,7 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu
             rootSelected={this.rootSelected}
             dropAction={StrCast(this.props.Document.childDropAction) as dropActionType}
             onClick={this.onChildClickHandler}
+            onDoubleClick={this.onChildDoubleClickHandler}
             getTransform={dxf}
             focus={this.props.focus}
             CollectionDoc={this.props.CollectionView?.props.Document}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
index af0cc3b5c..615efdb39 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
@@ -203,7 +203,7 @@ export class CollectionMultirowView extends CollectionSubView(MultirowDocument)
 
 
     @computed get onChildClickHandler() { return ScriptCast(this.Document.onChildClick); }
-
+    @computed get onChildDoubleClickHandler() { return ScriptCast(this.Document.onChildDoubleClick); }
 
     addDocTab = (doc: Doc, where: string) => {
         if (where === "inPlace" && this.layoutDoc.isInPlaceContainer) {
@@ -229,6 +229,7 @@ export class CollectionMultirowView extends CollectionSubView(MultirowDocument)
             rootSelected={this.rootSelected}
             dropAction={StrCast(this.props.Document.childDropAction) as dropActionType}
             onClick={this.onChildClickHandler}
+            onDoubleClick={this.onChildDoubleClickHandler}
             getTransform={dxf}
             focus={this.props.focus}
             CollectionDoc={this.props.CollectionView?.props.Document}
diff --git a/src/client/views/nodes/ContentFittingDocumentView.tsx b/src/client/views/nodes/ContentFittingDocumentView.tsx
index 641797cac..d0b0c8ee6 100644
--- a/src/client/views/nodes/ContentFittingDocumentView.tsx
+++ b/src/client/views/nodes/ContentFittingDocumentView.tsx
@@ -32,6 +32,7 @@ interface ContentFittingDocumentViewProps {
     CollectionView?: CollectionView;
     CollectionDoc?: Doc;
     onClick?: ScriptField;
+    onDoubleClick?: ScriptField;
     backgroundColor?: (doc: Doc) => string | undefined;
     getTransform: () => Transform;
     addDocument?: (document: Doc) => boolean;
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index fdcaa2df3..7b28a45f8 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -64,6 +64,7 @@ export interface DocumentViewProps {
     contextMenuItems?: () => { script: ScriptField, label: string }[];
     rootSelected: (outsideReaction?: boolean) => boolean; // whether the root of a template has been selected
     onClick?: ScriptField;
+    onDoubleClick?: ScriptField;
     onPointerDown?: ScriptField;
     onPointerUp?: ScriptField;
     dropAction?: dropActionType;
@@ -116,6 +117,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     @computed get nativeWidth() { return NumCast(this.layoutDoc._nativeWidth, this.props.NativeWidth() || (this.freezeDimensions ? this.layoutDoc[WidthSym]() : 0)); }
     @computed get nativeHeight() { return NumCast(this.layoutDoc._nativeHeight, this.props.NativeHeight() || (this.freezeDimensions ? this.layoutDoc[HeightSym]() : 0)); }
     @computed get onClickHandler() { return this.props.onClick || Cast(this.layoutDoc.onClick, ScriptField, null) || this.Document.onClick; }
+    @computed get onDoubleClickHandler() { return this.props.onDoubleClick || Cast(this.layoutDoc.onDoubleClick, ScriptField, null) || this.Document.onDoubleClick; }
     @computed get onPointerDownHandler() { return this.props.onPointerDown ? this.props.onPointerDown : this.Document.onPointerDown; }
     @computed get onPointerUpHandler() { return this.props.onPointerUp ? this.props.onPointerUp : this.Document.onPointerUp; }
     NativeWidth = () => this.nativeWidth;
@@ -289,13 +291,22 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             !this.props.Document.isBackground && this.props.bringToFront(this.props.Document);
             if (this._doubleTap && this.props.renderDepth && !this.onClickHandler?.script) { // disable double-click to show full screen for things that have an on click behavior since clicking them twice can be misinterpreted as a double click
                 if (!(e.nativeEvent as any).formattedHandled) {
-                    const fullScreenAlias = Doc.MakeAlias(this.props.Document);
-                    if (StrCast(fullScreenAlias.layoutKey) !== "layout_fullScreen" && fullScreenAlias.layout_fullScreen) {
-                        fullScreenAlias.layoutKey = "layout_fullScreen";
+                    if (this.onDoubleClickHandler?.script && !StrCast(Doc.LayoutField(this.layoutDoc))?.includes("ScriptingBox")) { // bcz: hack? don't execute script if you're clicking on a scripting box itself
+                        const func = () => this.onDoubleClickHandler.script.run({
+                            this: this.layoutDoc,
+                            self: this.rootDoc,
+                            thisContainer: this.props.ContainingCollectionDoc, shiftKey: e.shiftKey
+                        }, console.log);
+                        func();
+                    } else {
+                        const fullScreenAlias = Doc.MakeAlias(this.props.Document);
+                        if (StrCast(fullScreenAlias.layoutKey) !== "layout_fullScreen" && fullScreenAlias.layout_fullScreen) {
+                            fullScreenAlias.layoutKey = "layout_fullScreen";
+                        }
+                        UndoManager.RunInBatch(() => this.props.addDocTab(fullScreenAlias, "inTab"), "double tap");
+                        SelectionManager.DeselectAll();
+                        Doc.UnBrushDoc(this.props.Document);
                     }
-                    UndoManager.RunInBatch(() => this.props.addDocTab(fullScreenAlias, "inTab"), "double tap");
-                    SelectionManager.DeselectAll();
-                    Doc.UnBrushDoc(this.props.Document);
                 }
             } else if (this.onClickHandler?.script && !StrCast(Doc.LayoutField(this.layoutDoc))?.includes("ScriptingBox")) { // bcz: hack? don't execute script if you're clicking on a scripting box itself
                 //SelectionManager.DeselectAll();
diff --git a/src/new_fields/Types.ts b/src/new_fields/Types.ts
index aa44cefa0..3d784448d 100644
--- a/src/new_fields/Types.ts
+++ b/src/new_fields/Types.ts
@@ -88,8 +88,8 @@ export function DateCast(field: FieldResult) {
     return Cast(field, DateField, null);
 }
 
-export function ScriptCast(field: FieldResult) {
-    return Cast(field, ScriptField, null);
+export function ScriptCast(field: FieldResult, defaultVal: ScriptField | null = null) {
+    return Cast(field, ScriptField, defaultVal);
 }
 
 type WithoutList<T extends Field> = T extends List<infer R> ? (R extends RefField ? (R | Promise<R>)[] : R[]) : T;
diff --git a/src/new_fields/documentSchemas.ts b/src/new_fields/documentSchemas.ts
index 7a0be8863..5ca0d681e 100644
--- a/src/new_fields/documentSchemas.ts
+++ b/src/new_fields/documentSchemas.ts
@@ -81,6 +81,7 @@ export const collectionSchema = createSchema({
     childLayout: Doc, // layout template for children of a collecion
     childDetailView: Doc, // layout template to apply to a child when its clicked on in a collection and opened (requires onChildClick or other script to use this field)
     onChildClick: ScriptField, // script to run for each child when its clicked
+    onChildDoubleClick: ScriptField, // script to run for each child when its clicked
     onCheckedClick: ScriptField, // script to run when a checkbox is clicked next to a child in a tree view
 });
 
diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts
index eedd3ee67..1be977e4f 100644
--- a/src/server/authentication/models/current_user_utils.ts
+++ b/src/server/authentication/models/current_user_utils.ts
@@ -594,9 +594,13 @@ export class CurrentUserUtils {
         if (doc.childClickFuncs === undefined) {
             const openInTarget = Docs.Create.ScriptingDocument(ScriptField.MakeScript(
                 "docCast(thisContainer.target).then((target) => { target && docCast(this.source).then((source) => { target.proto.data = new List([source || this]); } ); } )",
-                { target: Doc.name }), { title: "On Child Clicked (open in target)", _width: 300, _height: 200 });
+                { target: Doc.name }), { title: "On Child Clicked (open in target)", _width: 300, _height: 200, scriptKey: "onChildClick" });
 
-            doc.childClickFuncs = Docs.Create.TreeDocument([openInTarget], { title: "on Child Click function templates" });
+            const openDetail = Docs.Create.ScriptingDocument(ScriptField.MakeScript(
+                "openOnRight(self.doubleClickView)",
+                { target: Doc.name }), { title: "On Child Dbl Clicked (open double click view)", _width: 300, _height: 200, scriptKey: "onChildDoubleClick" });
+
+            doc.childClickFuncs = Docs.Create.TreeDocument([openInTarget, openDetail], { title: "on Child Click function templates" });
         }
         // this is equivalent to using PrefetchProxies to make sure all the childClickFuncs have been retrieved.
         PromiseValue(Cast(doc.childClickFuncs, Doc)).then(func => func && PromiseValue(func.data).then(DocListCast));
-- 
cgit v1.2.3-70-g09d2


From 22748f8d35235941fc6622b19a2d4d3f809ccee7 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Thu, 30 Apr 2020 17:16:14 -0400
Subject: working version of snapping with resize / templates / centers

---
 .VSCodeCounter/details.md                          | 661 +++++++++++++++++
 .VSCodeCounter/results.csv                         | 648 ++++++++++++++++
 .VSCodeCounter/results.md                          | 164 +++++
 .VSCodeCounter/results.txt                         | 813 +++++++++++++++++++++
 package-lock.json                                  |  81 +-
 src/Utils.ts                                       |   4 +-
 src/client/util/DragManager.ts                     |  41 +-
 src/client/views/MainView.tsx                      |   8 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  70 +-
 .../views/nodes/formattedText/DashFieldView.tsx    |   4 -
 .../authentication/models/current_user_utils.ts    |   6 +-
 11 files changed, 2395 insertions(+), 105 deletions(-)
 create mode 100644 .VSCodeCounter/details.md
 create mode 100644 .VSCodeCounter/results.csv
 create mode 100644 .VSCodeCounter/results.md
 create mode 100644 .VSCodeCounter/results.txt

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

diff --git a/.VSCodeCounter/details.md b/.VSCodeCounter/details.md
new file mode 100644
index 000000000..2f988953b
--- /dev/null
+++ b/.VSCodeCounter/details.md
@@ -0,0 +1,661 @@
+# Details
+
+Date : 2020-04-30 14:40:16
+
+Directory /Users/bcz/Documents/GitHub/Dash-Web
+
+Total : 646 files,  224911 codes, 32987 comments, 15880 blanks, all 273778 lines
+
+[summary](results.md)
+
+## Files
+| filename | language | code | comment | blank | total |
+| :--- | :--- | ---: | ---: | ---: | ---: |
+| [README.md](file:///Users/bcz/Documents/GitHub/Dash-Web/README.md) | Markdown | 6 | 0 | 3 | 9 |
+| [build/index.html](file:///Users/bcz/Documents/GitHub/Dash-Web/build/index.html) | HTML | 9 | 0 | 3 | 12 |
+| [dash.bat](file:///Users/bcz/Documents/GitHub/Dash-Web/dash.bat) | Batch | 2 | 0 | 1 | 3 |
+| [deploy/assets/env.json](file:///Users/bcz/Documents/GitHub/Dash-Web/deploy/assets/env.json) | JSON | 15 | 0 | 0 | 15 |
+| [deploy/assets/pdf.worker.js](file:///Users/bcz/Documents/GitHub/Dash-Web/deploy/assets/pdf.worker.js) | JavaScript | 55,662 | 174 | 686 | 56,522 |
+| [deploy/debug/repl.html](file:///Users/bcz/Documents/GitHub/Dash-Web/deploy/debug/repl.html) | HTML | 11 | 0 | 3 | 14 |
+| [deploy/debug/test.html](file:///Users/bcz/Documents/GitHub/Dash-Web/deploy/debug/test.html) | HTML | 10 | 0 | 3 | 13 |
+| [deploy/debug/viewer.html](file:///Users/bcz/Documents/GitHub/Dash-Web/deploy/debug/viewer.html) | HTML | 11 | 0 | 3 | 14 |
+| [deploy/index.html](file:///Users/bcz/Documents/GitHub/Dash-Web/deploy/index.html) | HTML | 13 | 0 | 3 | 16 |
+| [deploy/mobile/image.html](file:///Users/bcz/Documents/GitHub/Dash-Web/deploy/mobile/image.html) | HTML | 12 | 0 | 3 | 15 |
+| [deploy/mobile/ink.html](file:///Users/bcz/Documents/GitHub/Dash-Web/deploy/mobile/ink.html) | HTML | 10 | 0 | 3 | 13 |
+| [package-lock.json](file:///Users/bcz/Documents/GitHub/Dash-Web/package-lock.json) | JSON | 18,689 | 0 | 1 | 18,690 |
+| [package.json](file:///Users/bcz/Documents/GitHub/Dash-Web/package.json) | JSON | 266 | 0 | 1 | 267 |
+| [sentence_parser.py](file:///Users/bcz/Documents/GitHub/Dash-Web/sentence_parser.py) | Python | 6 | 0 | 1 | 7 |
+| [session.config.json](file:///Users/bcz/Documents/GitHub/Dash-Web/session.config.json) | JSON | 12 | 0 | 1 | 13 |
+| [solr-8.3.1/bin/install_solr_service.sh](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/bin/install_solr_service.sh) | Shell Script | 307 | 29 | 35 | 371 |
+| [solr-8.3.1/bin/oom_solr.sh](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/bin/oom_solr.sh) | Shell Script | 13 | 15 | 3 | 31 |
+| [solr-8.3.1/bin/solr.cmd](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/bin/solr.cmd) | Batch | 1,782 | 43 | 210 | 2,035 |
+| [solr-8.3.1/bin/solr.in.cmd](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/bin/solr.in.cmd) | Batch | 16 | 133 | 29 | 178 |
+| [solr-8.3.1/bin/solr.in.sh](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/bin/solr.in.sh) | Shell Script | 0 | 172 | 34 | 206 |
+| [solr-8.3.1/contrib/prometheus-exporter/bin/solr-exporter.cmd](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/contrib/prometheus-exporter/bin/solr-exporter.cmd) | Batch | 82 | 0 | 26 | 108 |
+| [solr-8.3.1/contrib/prometheus-exporter/conf/grafana-solr-dashboard.json](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/contrib/prometheus-exporter/conf/grafana-solr-dashboard.json) | JSON | 4,465 | 0 | 1 | 4,466 |
+| [solr-8.3.1/contrib/prometheus-exporter/conf/solr-exporter-config.xml](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/contrib/prometheus-exporter/conf/solr-exporter-config.xml) | XML | 1,734 | 63 | 10 | 1,807 |
+| [solr-8.3.1/docs/images/solr.svg](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/docs/images/solr.svg) | XML | 39 | 0 | 1 | 40 |
+| [solr-8.3.1/docs/index.html](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/docs/index.html) | HTML | 20 | 0 | 1 | 21 |
+| [solr-8.3.1/example/example-DIH/solr/atom/conf/atom-data-config.xml](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/example/example-DIH/solr/atom/conf/atom-data-config.xml) | XML | 21 | 6 | 9 | 36 |
+| [solr-8.3.1/example/example-DIH/solr/atom/conf/solrconfig.xml](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/example/example-DIH/solr/atom/conf/solrconfig.xml) | XML | 20 | 37 | 8 | 65 |
+| [solr-8.3.1/example/example-DIH/solr/atom/core.properties](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/example/example-DIH/solr/atom/core.properties) | Properties | 0 | 0 | 2 | 2 |
+| [solr-8.3.1/example/example-DIH/solr/db/conf/clustering/carrot2/kmeans-attributes.xml](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/example/example-DIH/solr/db/conf/clustering/carrot2/kmeans-attributes.xml) | XML | 13 | 6 | 1 | 20 |
+| [solr-8.3.1/example/example-DIH/solr/db/conf/clustering/carrot2/lingo-attributes.xml](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/example/example-DIH/solr/db/conf/clustering/carrot2/lingo-attributes.xml) | XML | 13 | 11 | 1 | 25 |
+| [solr-8.3.1/example/example-DIH/solr/db/conf/clustering/carrot2/stc-attributes.xml](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/example/example-DIH/solr/db/conf/clustering/carrot2/stc-attributes.xml) | XML | 13 | 6 | 1 | 20 |
+| [solr-8.3.1/example/example-DIH/solr/db/conf/currency.xml](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/example/example-DIH/solr/db/conf/currency.xml) | XML | 45 | 19 | 4 | 68 |
+| [solr-8.3.1/example/example-DIH/solr/db/conf/db-data-config.xml](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/example/example-DIH/solr/db/conf/db-data-config.xml) | XML | 26 | 0 | 4 | 30 |
+| [solr-8.3.1/example/example-DIH/solr/db/conf/elevate.xml](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/example/example-DIH/solr/db/conf/elevate.xml) | XML | 3 | 37 | 3 | 43 |
+| [solr-8.3.1/example/example-DIH/solr/db/conf/solrconfig.xml](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/example/example-DIH/solr/db/conf/solrconfig.xml) | XML | 292 | 958 | 104 | 1,354 |
+| [solr-8.3.1/example/example-DIH/solr/db/conf/update-script.js](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/example/example-DIH/solr/db/conf/update-script.js) | JavaScript | 15 | 26 | 13 | 54 |
+| [solr-8.3.1/example/example-DIH/solr/db/conf/xslt/example.xsl](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/example/example-DIH/solr/db/conf/xslt/example.xsl) | XSL | 98 | 20 | 15 | 133 |
+| [solr-8.3.1/example/example-DIH/solr/db/conf/xslt/example_atom.xsl](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/example/example-DIH/solr/db/conf/xslt/example_atom.xsl) | XSL | 40 | 20 | 8 | 68 |
+| [solr-8.3.1/example/example-DIH/solr/db/conf/xslt/example_rss.xsl](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/example/example-DIH/solr/db/conf/xslt/example_rss.xsl) | XSL | 41 | 20 | 6 | 67 |
+| [solr-8.3.1/example/example-DIH/solr/db/conf/xslt/luke.xsl](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/example/example-DIH/solr/db/conf/xslt/luke.xsl) | XSL | 301 | 19 | 18 | 338 |
+| [solr-8.3.1/example/example-DIH/solr/db/conf/xslt/updateXml.xsl](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/example/example-DIH/solr/db/conf/xslt/updateXml.xsl) | XSL | 35 | 25 | 11 | 71 |
+| [solr-8.3.1/example/example-DIH/solr/db/core.properties](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/example/example-DIH/solr/db/core.properties) | Properties | 0 | 0 | 2 | 2 |
+| [solr-8.3.1/example/example-DIH/solr/mail/conf/clustering/carrot2/kmeans-attributes.xml](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/example/example-DIH/solr/mail/conf/clustering/carrot2/kmeans-attributes.xml) | XML | 13 | 6 | 1 | 20 |
+| [solr-8.3.1/example/example-DIH/solr/mail/conf/clustering/carrot2/lingo-attributes.xml](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/example/example-DIH/solr/mail/conf/clustering/carrot2/lingo-attributes.xml) | XML | 13 | 11 | 1 | 25 |
+| [solr-8.3.1/example/example-DIH/solr/mail/conf/clustering/carrot2/stc-attributes.xml](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/example/example-DIH/solr/mail/conf/clustering/carrot2/stc-attributes.xml) | XML | 13 | 6 | 1 | 20 |
+| [solr-8.3.1/example/example-DIH/solr/mail/conf/currency.xml](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/example/example-DIH/solr/mail/conf/currency.xml) | XML | 45 | 19 | 4 | 68 |
+| [solr-8.3.1/example/example-DIH/solr/mail/conf/elevate.xml](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/example/example-DIH/solr/mail/conf/elevate.xml) | XML | 3 | 37 | 3 | 43 |
+| [solr-8.3.1/example/example-DIH/solr/mail/conf/mail-data-config.xml](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/example/example-DIH/solr/mail/conf/mail-data-config.xml) | XML | 8 | 4 | 1 | 13 |
+| [solr-8.3.1/example/example-DIH/solr/mail/conf/solrconfig.xml](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/example/example-DIH/solr/mail/conf/solrconfig.xml) | XML | 294 | 958 | 105 | 1,357 |
+| [solr-8.3.1/example/example-DIH/solr/mail/conf/update-script.js](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/example/example-DIH/solr/mail/conf/update-script.js) | JavaScript | 15 | 26 | 13 | 54 |
+| [solr-8.3.1/example/example-DIH/solr/mail/conf/xslt/example.xsl](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/example/example-DIH/solr/mail/conf/xslt/example.xsl) | XSL | 98 | 20 | 15 | 133 |
+| [solr-8.3.1/example/example-DIH/solr/mail/conf/xslt/example_atom.xsl](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/example/example-DIH/solr/mail/conf/xslt/example_atom.xsl) | XSL | 40 | 20 | 8 | 68 |
+| [solr-8.3.1/example/example-DIH/solr/mail/conf/xslt/example_rss.xsl](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/example/example-DIH/solr/mail/conf/xslt/example_rss.xsl) | XSL | 41 | 20 | 6 | 67 |
+| [solr-8.3.1/example/example-DIH/solr/mail/conf/xslt/luke.xsl](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/example/example-DIH/solr/mail/conf/xslt/luke.xsl) | XSL | 301 | 19 | 18 | 338 |
+| [solr-8.3.1/example/example-DIH/solr/mail/conf/xslt/updateXml.xsl](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/example/example-DIH/solr/mail/conf/xslt/updateXml.xsl) | XSL | 35 | 25 | 11 | 71 |
+| [solr-8.3.1/example/example-DIH/solr/mail/core.properties](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/example/example-DIH/solr/mail/core.properties) | Properties | 0 | 0 | 2 | 2 |
+| [solr-8.3.1/example/example-DIH/solr/solr.xml](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/example/example-DIH/solr/solr.xml) | XML | 2 | 0 | 1 | 3 |
+| [solr-8.3.1/example/example-DIH/solr/solr/conf/clustering/carrot2/kmeans-attributes.xml](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/example/example-DIH/solr/solr/conf/clustering/carrot2/kmeans-attributes.xml) | XML | 13 | 6 | 1 | 20 |
+| [solr-8.3.1/example/example-DIH/solr/solr/conf/clustering/carrot2/lingo-attributes.xml](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/example/example-DIH/solr/solr/conf/clustering/carrot2/lingo-attributes.xml) | XML | 13 | 11 | 1 | 25 |
+| [solr-8.3.1/example/example-DIH/solr/solr/conf/clustering/carrot2/stc-attributes.xml](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/example/example-DIH/solr/solr/conf/clustering/carrot2/stc-attributes.xml) | XML | 13 | 6 | 1 | 20 |
+| [solr-8.3.1/example/example-DIH/solr/solr/conf/currency.xml](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/example/example-DIH/solr/solr/conf/currency.xml) | XML | 45 | 19 | 4 | 68 |
+| [solr-8.3.1/example/example-DIH/solr/solr/conf/elevate.xml](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/example/example-DIH/solr/solr/conf/elevate.xml) | XML | 3 | 37 | 3 | 43 |
+| [solr-8.3.1/example/example-DIH/solr/solr/conf/solr-data-config.xml](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/example/example-DIH/solr/solr/conf/solr-data-config.xml) | XML | 8 | 16 | 2 | 26 |
+| [solr-8.3.1/example/example-DIH/solr/solr/conf/solrconfig.xml](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/example/example-DIH/solr/solr/conf/solrconfig.xml) | XML | 292 | 958 | 102 | 1,352 |
+| [solr-8.3.1/example/example-DIH/solr/solr/conf/update-script.js](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/example/example-DIH/solr/solr/conf/update-script.js) | JavaScript | 15 | 26 | 13 | 54 |
+| [solr-8.3.1/example/example-DIH/solr/solr/conf/xslt/example.xsl](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/example/example-DIH/solr/solr/conf/xslt/example.xsl) | XSL | 98 | 20 | 15 | 133 |
+| [solr-8.3.1/example/example-DIH/solr/solr/conf/xslt/example_atom.xsl](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/example/example-DIH/solr/solr/conf/xslt/example_atom.xsl) | XSL | 40 | 20 | 8 | 68 |
+| [solr-8.3.1/example/example-DIH/solr/solr/conf/xslt/example_rss.xsl](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/example/example-DIH/solr/solr/conf/xslt/example_rss.xsl) | XSL | 41 | 20 | 6 | 67 |
+| [solr-8.3.1/example/example-DIH/solr/solr/conf/xslt/luke.xsl](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/example/example-DIH/solr/solr/conf/xslt/luke.xsl) | XSL | 301 | 19 | 18 | 338 |
+| [solr-8.3.1/example/example-DIH/solr/solr/conf/xslt/updateXml.xsl](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/example/example-DIH/solr/solr/conf/xslt/updateXml.xsl) | XSL | 35 | 25 | 11 | 71 |
+| [solr-8.3.1/example/example-DIH/solr/solr/core.properties](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/example/example-DIH/solr/solr/core.properties) | Properties | 0 | 0 | 2 | 2 |
+| [solr-8.3.1/example/example-DIH/solr/tika/conf/solrconfig.xml](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/example/example-DIH/solr/tika/conf/solrconfig.xml) | XML | 17 | 38 | 7 | 62 |
+| [solr-8.3.1/example/example-DIH/solr/tika/conf/tika-data-config.xml](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/example/example-DIH/solr/tika/conf/tika-data-config.xml) | XML | 17 | 3 | 7 | 27 |
+| [solr-8.3.1/example/example-DIH/solr/tika/core.properties](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/example/example-DIH/solr/tika/core.properties) | Properties | 0 | 0 | 2 | 2 |
+| [solr-8.3.1/example/exampledocs/books.json](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/example/exampledocs/books.json) | JSON | 51 | 0 | 1 | 52 |
+| [solr-8.3.1/example/exampledocs/gb18030-example.xml](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/example/exampledocs/gb18030-example.xml) | XML | 14 | 16 | 3 | 33 |
+| [solr-8.3.1/example/exampledocs/hd.xml](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/example/exampledocs/hd.xml) | XML | 33 | 20 | 4 | 57 |
+| [solr-8.3.1/example/exampledocs/ipod_other.xml](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/example/exampledocs/ipod_other.xml) | XML | 32 | 20 | 9 | 61 |
+| [solr-8.3.1/example/exampledocs/ipod_video.xml](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/example/exampledocs/ipod_video.xml) | XML | 21 | 18 | 2 | 41 |
+| [solr-8.3.1/example/exampledocs/manufacturers.xml](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/example/exampledocs/manufacturers.xml) | XML | 57 | 16 | 3 | 76 |
+| [solr-8.3.1/example/exampledocs/mem.xml](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/example/exampledocs/mem.xml) | XML | 45 | 24 | 9 | 78 |
+| [solr-8.3.1/example/exampledocs/money.xml](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/example/exampledocs/money.xml) | XML | 42 | 17 | 7 | 66 |
+| [solr-8.3.1/example/exampledocs/monitor.xml](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/example/exampledocs/monitor.xml) | XML | 14 | 18 | 3 | 35 |
+| [solr-8.3.1/example/exampledocs/monitor2.xml](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/example/exampledocs/monitor2.xml) | XML | 13 | 18 | 3 | 34 |
+| [solr-8.3.1/example/exampledocs/mp500.xml](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/example/exampledocs/mp500.xml) | XML | 23 | 18 | 3 | 44 |
+| [solr-8.3.1/example/exampledocs/sample.html](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/example/exampledocs/sample.html) | HTML | 13 | 0 | 1 | 14 |
+| [solr-8.3.1/example/exampledocs/sd500.xml](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/example/exampledocs/sd500.xml) | XML | 19 | 18 | 2 | 39 |
+| [solr-8.3.1/example/exampledocs/solr.xml](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/example/exampledocs/solr.xml) | XML | 20 | 16 | 3 | 39 |
+| [solr-8.3.1/example/exampledocs/test_utf8.sh](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/example/exampledocs/test_utf8.sh) | Shell Script | 57 | 21 | 16 | 94 |
+| [solr-8.3.1/example/exampledocs/utf8-example.xml](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/example/exampledocs/utf8-example.xml) | XML | 19 | 20 | 4 | 43 |
+| [solr-8.3.1/example/exampledocs/vidcard.xml](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/example/exampledocs/vidcard.xml) | XML | 40 | 21 | 2 | 63 |
+| [solr-8.3.1/example/files/browse-resources/velocity/resources.properties](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/example/files/browse-resources/velocity/resources.properties) | Properties | 72 | 6 | 5 | 83 |
+| [solr-8.3.1/example/files/browse-resources/velocity/resources_de_DE.properties](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/example/files/browse-resources/velocity/resources_de_DE.properties) | Properties | 18 | 0 | 1 | 19 |
+| [solr-8.3.1/example/files/browse-resources/velocity/resources_fr_FR.properties](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/example/files/browse-resources/velocity/resources_fr_FR.properties) | Properties | 18 | 0 | 3 | 21 |
+| [solr-8.3.1/example/files/conf/currency.xml](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/example/files/conf/currency.xml) | XML | 45 | 19 | 4 | 68 |
+| [solr-8.3.1/example/files/conf/elevate.xml](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/example/files/conf/elevate.xml) | XML | 3 | 37 | 3 | 43 |
+| [solr-8.3.1/example/files/conf/params.json](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/example/files/conf/params.json) | JSON | 34 | 0 | 1 | 35 |
+| [solr-8.3.1/example/files/conf/solrconfig.xml](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/example/files/conf/solrconfig.xml) | XML | 298 | 979 | 102 | 1,379 |
+| [solr-8.3.1/example/files/conf/update-script.js](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/example/files/conf/update-script.js) | JavaScript | 80 | 13 | 23 | 116 |
+| [solr-8.3.1/example/files/conf/velocity/dropit.js](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/example/files/conf/velocity/dropit.js) | JavaScript | 0 | 0 | 2 | 2 |
+| [solr-8.3.1/example/files/conf/velocity/jquery.tx3-tag-cloud.js](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/example/files/conf/velocity/jquery.tx3-tag-cloud.js) | JavaScript | 0 | 0 | 2 | 2 |
+| [solr-8.3.1/example/files/conf/velocity/js/dropit.js](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/example/files/conf/velocity/js/dropit.js) | JavaScript | 64 | 15 | 19 | 98 |
+| [solr-8.3.1/example/files/conf/velocity/js/jquery.autocomplete.js](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/example/files/conf/velocity/js/jquery.autocomplete.js) | JavaScript | 620 | 68 | 76 | 764 |
+| [solr-8.3.1/example/files/conf/velocity/js/jquery.tx3-tag-cloud.js](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/example/files/conf/velocity/js/jquery.tx3-tag-cloud.js) | JavaScript | 46 | 16 | 9 | 71 |
+| [solr-8.3.1/example/films/film_data_generator.py](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/example/films/film_data_generator.py) | Python | 82 | 24 | 12 | 118 |
+| [solr-8.3.1/example/films/films.json](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/example/films/films.json) | JSON | 15,830 | 0 | 1 | 15,831 |
+| [solr-8.3.1/example/films/films.xml](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/example/films/films.xml) | XML | 11,438 | 0 | 1 | 11,439 |
+| [solr-8.3.1/server/contexts/solr-jetty-context.xml](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/contexts/solr-jetty-context.xml) | XML | 8 | 0 | 1 | 9 |
+| [solr-8.3.1/server/etc/jetty-http.xml](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/etc/jetty-http.xml) | XML | 33 | 15 | 4 | 52 |
+| [solr-8.3.1/server/etc/jetty-https.xml](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/etc/jetty-https.xml) | XML | 56 | 16 | 5 | 77 |
+| [solr-8.3.1/server/etc/jetty-https8.xml](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/etc/jetty-https8.xml) | XML | 34 | 32 | 4 | 70 |
+| [solr-8.3.1/server/etc/jetty-ssl.xml](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/etc/jetty-ssl.xml) | XML | 23 | 11 | 4 | 38 |
+| [solr-8.3.1/server/etc/jetty.xml](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/etc/jetty.xml) | XML | 110 | 94 | 18 | 222 |
+| [solr-8.3.1/server/etc/webdefault.xml](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/etc/webdefault.xml) | XML | 272 | 232 | 24 | 528 |
+| [solr-8.3.1/server/resources/jetty-logging.properties](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/resources/jetty-logging.properties) | Properties | 1 | 0 | 1 | 2 |
+| [solr-8.3.1/server/resources/log4j2-console.xml](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/resources/log4j2-console.xml) | XML | 19 | 43 | 6 | 68 |
+| [solr-8.3.1/server/resources/log4j2.xml](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/resources/log4j2.xml) | XML | 54 | 80 | 9 | 143 |
+| [solr-8.3.1/server/scripts/cloud-scripts/snapshotscli.sh](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/scripts/cloud-scripts/snapshotscli.sh) | Shell Script | 152 | 2 | 23 | 177 |
+| [solr-8.3.1/server/scripts/cloud-scripts/zkcli.bat](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/scripts/cloud-scripts/zkcli.bat) | Batch | 11 | 8 | 7 | 26 |
+| [solr-8.3.1/server/scripts/cloud-scripts/zkcli.sh](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/scripts/cloud-scripts/zkcli.sh) | Shell Script | 9 | 9 | 9 | 27 |
+| [solr-8.3.1/server/solr-webapp/webapp/WEB-INF/web.xml](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/WEB-INF/web.xml) | XML | 62 | 42 | 11 | 115 |
+| [solr-8.3.1/server/solr-webapp/webapp/css/angular/analysis.css](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/css/angular/analysis.css) | CSS | 237 | 19 | 48 | 304 |
+| [solr-8.3.1/server/solr-webapp/webapp/css/angular/chosen.css](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/css/angular/chosen.css) | CSS | 402 | 55 | 9 | 466 |
+| [solr-8.3.1/server/solr-webapp/webapp/css/angular/cloud.css](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/css/angular/cloud.css) | CSS | 594 | 23 | 106 | 723 |
+| [solr-8.3.1/server/solr-webapp/webapp/css/angular/collections.css](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/css/angular/collections.css) | CSS | 296 | 18 | 65 | 379 |
+| [solr-8.3.1/server/solr-webapp/webapp/css/angular/common.css](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/css/angular/common.css) | CSS | 647 | 19 | 106 | 772 |
+| [solr-8.3.1/server/solr-webapp/webapp/css/angular/cores.css](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/css/angular/cores.css) | CSS | 171 | 18 | 37 | 226 |
+| [solr-8.3.1/server/solr-webapp/webapp/css/angular/dashboard.css](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/css/angular/dashboard.css) | CSS | 134 | 18 | 28 | 180 |
+| [solr-8.3.1/server/solr-webapp/webapp/css/angular/dataimport.css](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/css/angular/dataimport.css) | CSS | 292 | 18 | 61 | 371 |
+| [solr-8.3.1/server/solr-webapp/webapp/css/angular/documents.css](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/css/angular/documents.css) | CSS | 131 | 23 | 26 | 180 |
+| [solr-8.3.1/server/solr-webapp/webapp/css/angular/files.css](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/css/angular/files.css) | CSS | 29 | 18 | 7 | 54 |
+| [solr-8.3.1/server/solr-webapp/webapp/css/angular/index.css](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/css/angular/index.css) | CSS | 164 | 18 | 35 | 217 |
+| [solr-8.3.1/server/solr-webapp/webapp/css/angular/java-properties.css](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/css/angular/java-properties.css) | CSS | 24 | 18 | 6 | 48 |
+| [solr-8.3.1/server/solr-webapp/webapp/css/angular/jquery-ui.min.css](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/css/angular/jquery-ui.min.css) | CSS | 1 | 26 | 2 | 29 |
+| [solr-8.3.1/server/solr-webapp/webapp/css/angular/jquery-ui.structure.min.css](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/css/angular/jquery-ui.structure.min.css) | CSS | 1 | 22 | 2 | 25 |
+| [solr-8.3.1/server/solr-webapp/webapp/css/angular/logging.css](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/css/angular/logging.css) | CSS | 303 | 19 | 63 | 385 |
+| [solr-8.3.1/server/solr-webapp/webapp/css/angular/login.css](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/css/angular/login.css) | CSS | 80 | 18 | 12 | 110 |
+| [solr-8.3.1/server/solr-webapp/webapp/css/angular/menu.css](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/css/angular/menu.css) | CSS | 257 | 18 | 56 | 331 |
+| [solr-8.3.1/server/solr-webapp/webapp/css/angular/overview.css](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/css/angular/overview.css) | CSS | 20 | 18 | 5 | 43 |
+| [solr-8.3.1/server/solr-webapp/webapp/css/angular/plugins.css](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/css/angular/plugins.css) | CSS | 172 | 18 | 31 | 221 |
+| [solr-8.3.1/server/solr-webapp/webapp/css/angular/query.css](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/css/angular/query.css) | CSS | 120 | 18 | 25 | 163 |
+| [solr-8.3.1/server/solr-webapp/webapp/css/angular/replication.css](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/css/angular/replication.css) | CSS | 404 | 18 | 79 | 501 |
+| [solr-8.3.1/server/solr-webapp/webapp/css/angular/schema.css](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/css/angular/schema.css) | CSS | 596 | 20 | 112 | 728 |
+| [solr-8.3.1/server/solr-webapp/webapp/css/angular/segments.css](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/css/angular/segments.css) | CSS | 133 | 18 | 22 | 173 |
+| [solr-8.3.1/server/solr-webapp/webapp/css/angular/stream.css](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/css/angular/stream.css) | CSS | 178 | 22 | 34 | 234 |
+| [solr-8.3.1/server/solr-webapp/webapp/css/angular/suggestions.css](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/css/angular/suggestions.css) | CSS | 43 | 18 | 4 | 65 |
+| [solr-8.3.1/server/solr-webapp/webapp/css/angular/threads.css](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/css/angular/threads.css) | CSS | 119 | 18 | 24 | 161 |
+| [solr-8.3.1/server/solr-webapp/webapp/img/solr.svg](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/img/solr.svg) | XML | 39 | 0 | 1 | 40 |
+| [solr-8.3.1/server/solr-webapp/webapp/index.html](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/index.html) | HTML | 203 | 16 | 38 | 257 |
+| [solr-8.3.1/server/solr-webapp/webapp/js/angular/app.js](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/js/angular/app.js) | JavaScript | 512 | 19 | 31 | 562 |
+| [solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/alias-overview.js](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/alias-overview.js) | JavaScript | 8 | 16 | 4 | 28 |
+| [solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/analysis.js](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/analysis.js) | JavaScript | 161 | 18 | 23 | 202 |
+| [solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/cloud.js](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/cloud.js) | JavaScript | 847 | 51 | 124 | 1,022 |
+| [solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/cluster-suggestions.js](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/cluster-suggestions.js) | JavaScript | 43 | 18 | 2 | 63 |
+| [solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/collection-overview.js](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/collection-overview.js) | JavaScript | 18 | 16 | 6 | 40 |
+| [solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/collections.js](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/collections.js) | JavaScript | 244 | 19 | 27 | 290 |
+| [solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/core-overview.js](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/core-overview.js) | JavaScript | 69 | 16 | 9 | 94 |
+| [solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/cores.js](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/cores.js) | JavaScript | 151 | 16 | 14 | 181 |
+| [solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/dataimport.js](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/dataimport.js) | JavaScript | 234 | 25 | 44 | 303 |
+| [solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/documents.js](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/documents.js) | JavaScript | 107 | 18 | 13 | 138 |
+| [solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/files.js](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/files.js) | JavaScript | 72 | 16 | 13 | 101 |
+| [solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/index.js](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/index.js) | JavaScript | 61 | 23 | 14 | 98 |
+| [solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/java-properties.js](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/java-properties.js) | JavaScript | 27 | 16 | 3 | 46 |
+| [solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/logging.js](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/logging.js) | JavaScript | 112 | 35 | 12 | 159 |
+| [solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/login.js](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/login.js) | JavaScript | 269 | 30 | 19 | 318 |
+| [solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/plugins.js](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/plugins.js) | JavaScript | 130 | 19 | 19 | 168 |
+| [solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/query.js](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/query.js) | JavaScript | 88 | 19 | 14 | 121 |
+| [solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/replication.js](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/replication.js) | JavaScript | 178 | 18 | 40 | 236 |
+| [solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/schema.js](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/schema.js) | JavaScript | 524 | 19 | 69 | 612 |
+| [solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/segments.js](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/segments.js) | JavaScript | 64 | 16 | 20 | 100 |
+| [solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/stream.js](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/stream.js) | JavaScript | 173 | 16 | 51 | 240 |
+| [solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/threads.js](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/threads.js) | JavaScript | 33 | 16 | 2 | 51 |
+| [solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/unknown.js](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/unknown.js) | JavaScript | 14 | 22 | 2 | 38 |
+| [solr-8.3.1/server/solr-webapp/webapp/js/angular/services.js](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/js/angular/services.js) | JavaScript | 311 | 18 | 11 | 340 |
+| [solr-8.3.1/server/solr-webapp/webapp/libs/angular-chosen.js](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/libs/angular-chosen.js) | JavaScript | 112 | 24 | 4 | 140 |
+| [solr-8.3.1/server/solr-webapp/webapp/libs/angular-cookies.js](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/libs/angular-cookies.js) | JavaScript | 73 | 135 | 22 | 230 |
+| [solr-8.3.1/server/solr-webapp/webapp/libs/angular-cookies.min.js](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/libs/angular-cookies.min.js) | JavaScript | 2 | 29 | 1 | 32 |
+| [solr-8.3.1/server/solr-webapp/webapp/libs/angular-resource.min.js](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/libs/angular-resource.min.js) | JavaScript | 7 | 29 | 1 | 37 |
+| [solr-8.3.1/server/solr-webapp/webapp/libs/angular-route.js](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/libs/angular-route.js) | JavaScript | 316 | 636 | 67 | 1,019 |
+| [solr-8.3.1/server/solr-webapp/webapp/libs/angular-route.min.js](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/libs/angular-route.min.js) | JavaScript | 9 | 29 | 1 | 39 |
+| [solr-8.3.1/server/solr-webapp/webapp/libs/angular-sanitize.js](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/libs/angular-sanitize.js) | JavaScript | 311 | 328 | 65 | 704 |
+| [solr-8.3.1/server/solr-webapp/webapp/libs/angular-sanitize.min.js](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/libs/angular-sanitize.min.js) | JavaScript | 10 | 29 | 1 | 40 |
+| [solr-8.3.1/server/solr-webapp/webapp/libs/angular-utf8-base64.js](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/libs/angular-utf8-base64.js) | JavaScript | 157 | 48 | 13 | 218 |
+| [solr-8.3.1/server/solr-webapp/webapp/libs/angular-utf8-base64.min.js](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/libs/angular-utf8-base64.min.js) | JavaScript | 1 | 42 | 3 | 46 |
+| [solr-8.3.1/server/solr-webapp/webapp/libs/angular.js](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/libs/angular.js) | JavaScript | 10,845 | 13,211 | 2,038 | 26,094 |
+| [solr-8.3.1/server/solr-webapp/webapp/libs/angular.min.js](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/libs/angular.min.js) | JavaScript | 73 | 201 | 0 | 274 |
+| [solr-8.3.1/server/solr-webapp/webapp/libs/chosen.jquery.js](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/libs/chosen.jquery.js) | JavaScript | 1,151 | 36 | 8 | 1,195 |
+| [solr-8.3.1/server/solr-webapp/webapp/libs/chosen.jquery.min.js](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/libs/chosen.jquery.min.js) | JavaScript | 2 | 29 | 0 | 31 |
+| [solr-8.3.1/server/solr-webapp/webapp/libs/d3.js](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/libs/d3.js) | JavaScript | 7,720 | 519 | 1,135 | 9,374 |
+| [solr-8.3.1/server/solr-webapp/webapp/libs/highlight.js](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/libs/highlight.js) | JavaScript | 2 | 29 | 1 | 32 |
+| [solr-8.3.1/server/solr-webapp/webapp/libs/jquery-1.7.2.min.js](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/libs/jquery-1.7.2.min.js) | JavaScript | 3 | 26 | 2 | 31 |
+| [solr-8.3.1/server/solr-webapp/webapp/libs/jquery-2.1.3.min.js](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/libs/jquery-2.1.3.min.js) | JavaScript | 3 | 26 | 1 | 30 |
+| [solr-8.3.1/server/solr-webapp/webapp/libs/jquery-ui.min.js](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/libs/jquery-ui.min.js) | JavaScript | 2 | 26 | 3 | 31 |
+| [solr-8.3.1/server/solr-webapp/webapp/libs/jquery.jstree.js](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/libs/jquery.jstree.js) | JavaScript | 3,222 | 228 | 85 | 3,535 |
+| [solr-8.3.1/server/solr-webapp/webapp/libs/ngtimeago.js](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/libs/ngtimeago.js) | JavaScript | 66 | 23 | 13 | 102 |
+| [solr-8.3.1/server/solr-webapp/webapp/partials/alias_overview.html](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/partials/alias_overview.html) | HTML | 21 | 16 | 10 | 47 |
+| [solr-8.3.1/server/solr-webapp/webapp/partials/analysis.html](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/partials/analysis.html) | HTML | 87 | 16 | 26 | 129 |
+| [solr-8.3.1/server/solr-webapp/webapp/partials/cloud.html](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/partials/cloud.html) | HTML | 263 | 16 | 24 | 303 |
+| [solr-8.3.1/server/solr-webapp/webapp/partials/cluster_suggestions.html](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/partials/cluster_suggestions.html) | HTML | 30 | 16 | 4 | 50 |
+| [solr-8.3.1/server/solr-webapp/webapp/partials/collection_overview.html](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/partials/collection_overview.html) | HTML | 48 | 16 | 22 | 86 |
+| [solr-8.3.1/server/solr-webapp/webapp/partials/collections.html](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/partials/collections.html) | HTML | 301 | 16 | 79 | 396 |
+| [solr-8.3.1/server/solr-webapp/webapp/partials/core_overview.html](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/partials/core_overview.html) | HTML | 125 | 16 | 66 | 207 |
+| [solr-8.3.1/server/solr-webapp/webapp/partials/cores.html](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/partials/cores.html) | HTML | 142 | 16 | 67 | 225 |
+| [solr-8.3.1/server/solr-webapp/webapp/partials/dataimport.html](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/partials/dataimport.html) | HTML | 142 | 16 | 52 | 210 |
+| [solr-8.3.1/server/solr-webapp/webapp/partials/documents.html](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/partials/documents.html) | HTML | 83 | 20 | 9 | 112 |
+| [solr-8.3.1/server/solr-webapp/webapp/partials/files.html](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/partials/files.html) | HTML | 17 | 16 | 15 | 48 |
+| [solr-8.3.1/server/solr-webapp/webapp/partials/index.html](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/partials/index.html) | HTML | 135 | 42 | 85 | 262 |
+| [solr-8.3.1/server/solr-webapp/webapp/partials/java-properties.html](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/partials/java-properties.html) | HTML | 10 | 16 | 2 | 28 |
+| [solr-8.3.1/server/solr-webapp/webapp/partials/logging-levels.html](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/partials/logging-levels.html) | HTML | 35 | 16 | 6 | 57 |
+| [solr-8.3.1/server/solr-webapp/webapp/partials/logging.html](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/partials/logging.html) | HTML | 40 | 16 | 2 | 58 |
+| [solr-8.3.1/server/solr-webapp/webapp/partials/login.html](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/partials/login.html) | HTML | 134 | 16 | 11 | 161 |
+| [solr-8.3.1/server/solr-webapp/webapp/partials/plugins.html](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/partials/plugins.html) | HTML | 48 | 17 | 8 | 73 |
+| [solr-8.3.1/server/solr-webapp/webapp/partials/query.html](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/partials/query.html) | HTML | 270 | 16 | 84 | 370 |
+| [solr-8.3.1/server/solr-webapp/webapp/partials/replication.html](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/partials/replication.html) | HTML | 153 | 16 | 71 | 240 |
+| [solr-8.3.1/server/solr-webapp/webapp/partials/schema.html](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/partials/schema.html) | HTML | 336 | 16 | 104 | 456 |
+| [solr-8.3.1/server/solr-webapp/webapp/partials/segments.html](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/partials/segments.html) | HTML | 70 | 16 | 14 | 100 |
+| [solr-8.3.1/server/solr-webapp/webapp/partials/stream.html](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/partials/stream.html) | HTML | 40 | 16 | 9 | 65 |
+| [solr-8.3.1/server/solr-webapp/webapp/partials/threads.html](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/partials/threads.html) | HTML | 36 | 16 | 14 | 66 |
+| [solr-8.3.1/server/solr-webapp/webapp/partials/unknown.html](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr-webapp/webapp/partials/unknown.html) | HTML | 5 | 16 | 3 | 24 |
+| [solr-8.3.1/server/solr/configsets/_default/conf/params.json](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr/configsets/_default/conf/params.json) | JSON | 20 | 0 | 1 | 21 |
+| [solr-8.3.1/server/solr/configsets/_default/conf/solrconfig.xml](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr/configsets/_default/conf/solrconfig.xml) | XML | 296 | 976 | 98 | 1,370 |
+| [solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/_rest_managed.json](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/_rest_managed.json) | JSON | 1 | 0 | 1 | 2 |
+| [solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/_schema_analysis_stopwords_english.json](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/_schema_analysis_stopwords_english.json) | JSON | 38 | 0 | 1 | 39 |
+| [solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/_schema_analysis_synonyms_english.json](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/_schema_analysis_synonyms_english.json) | JSON | 11 | 0 | 1 | 12 |
+| [solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/clustering/carrot2/kmeans-attributes.xml](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/clustering/carrot2/kmeans-attributes.xml) | XML | 13 | 6 | 1 | 20 |
+| [solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/clustering/carrot2/lingo-attributes.xml](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/clustering/carrot2/lingo-attributes.xml) | XML | 13 | 11 | 1 | 25 |
+| [solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/clustering/carrot2/stc-attributes.xml](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/clustering/carrot2/stc-attributes.xml) | XML | 13 | 6 | 1 | 20 |
+| [solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/currency.xml](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/currency.xml) | XML | 45 | 19 | 4 | 68 |
+| [solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/elevate.xml](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/elevate.xml) | XML | 3 | 37 | 3 | 43 |
+| [solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/params.json](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/params.json) | JSON | 11 | 0 | 1 | 12 |
+| [solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/solrconfig.xml](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/solrconfig.xml) | XML | 410 | 1,097 | 124 | 1,631 |
+| [solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/update-script.js](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/update-script.js) | JavaScript | 15 | 26 | 13 | 54 |
+| [solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/velocity/jquery.autocomplete.css](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/velocity/jquery.autocomplete.css) | CSS | 34 | 9 | 6 | 49 |
+| [solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/velocity/jquery.autocomplete.js](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/velocity/jquery.autocomplete.js) | JavaScript | 620 | 68 | 76 | 764 |
+| [solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/velocity/main.css](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/velocity/main.css) | CSS | 185 | 0 | 47 | 232 |
+| [solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/xslt/example.xsl](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/xslt/example.xsl) | XSL | 98 | 20 | 15 | 133 |
+| [solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/xslt/example_atom.xsl](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/xslt/example_atom.xsl) | XSL | 40 | 20 | 8 | 68 |
+| [solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/xslt/example_rss.xsl](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/xslt/example_rss.xsl) | XSL | 41 | 20 | 6 | 67 |
+| [solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/xslt/luke.xsl](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/xslt/luke.xsl) | XSL | 301 | 19 | 18 | 338 |
+| [solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/xslt/updateXml.xsl](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/xslt/updateXml.xsl) | XSL | 35 | 25 | 11 | 71 |
+| [solr-8.3.1/server/solr/dash/conf/params.json](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr/dash/conf/params.json) | JSON | 20 | 0 | 0 | 20 |
+| [solr-8.3.1/server/solr/dash/conf/schema.xml](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr/dash/conf/schema.xml) | XML | 51 | 4 | 7 | 62 |
+| [solr-8.3.1/server/solr/dash/conf/solrconfig.xml](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr/dash/conf/solrconfig.xml) | XML | 270 | 962 | 97 | 1,329 |
+| [solr-8.3.1/server/solr/dash/core.properties](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr/dash/core.properties) | Properties | 4 | 2 | 1 | 7 |
+| [solr-8.3.1/server/solr/solr.xml](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr/solr.xml) | XML | 21 | 25 | 11 | 57 |
+| [solr-8.3.1/server/solr/zoo.cfg](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/solr/zoo.cfg) | Properties | 3 | 25 | 4 | 32 |
+| [solr-8.3.1/server/tmp/start_3204295554151338130.properties](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/tmp/start_3204295554151338130.properties) | Properties | 9 | 2 | 1 | 12 |
+| [solr-8.3.1/server/tmp/start_5812170489311981381.properties](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/tmp/start_5812170489311981381.properties) | Properties | 9 | 2 | 1 | 12 |
+| [solr-8.3.1/server/tmp/start_6476327636763392575.properties](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/tmp/start_6476327636763392575.properties) | Properties | 9 | 2 | 1 | 12 |
+| [solr-8.3.1/server/tmp/start_7329004517204835686.properties](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/tmp/start_7329004517204835686.properties) | Properties | 9 | 2 | 1 | 12 |
+| [solr-8.3.1/server/tmp/start_9067375725008958788.properties](file:///Users/bcz/Documents/GitHub/Dash-Web/solr-8.3.1/server/tmp/start_9067375725008958788.properties) | Properties | 9 | 2 | 1 | 12 |
+| [src/Utils.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/Utils.ts) | TypeScript | 441 | 23 | 81 | 545 |
+| [src/client/ClientRecommender.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/ClientRecommender.scss) | SCSS | 9 | 1 | 2 | 12 |
+| [src/client/ClientRecommender.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/ClientRecommender.tsx) | TypeScript React | 320 | 61 | 44 | 425 |
+| [src/client/DocServer.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/DocServer.ts) | TypeScript | 279 | 136 | 65 | 480 |
+| [src/client/Network.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/Network.ts) | TypeScript | 34 | 0 | 5 | 39 |
+| [src/client/apis/GoogleAuthenticationManager.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/apis/GoogleAuthenticationManager.scss) | SCSS | 16 | 0 | 3 | 19 |
+| [src/client/apis/GoogleAuthenticationManager.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/apis/GoogleAuthenticationManager.tsx) | TypeScript React | 115 | 2 | 11 | 128 |
+| [src/client/apis/IBM_Recommender.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/apis/IBM_Recommender.ts) | TypeScript | 0 | 33 | 7 | 40 |
+| [src/client/apis/google_docs/GoogleApiClientUtils.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/apis/google_docs/GoogleApiClientUtils.ts) | TypeScript | 225 | 9 | 27 | 261 |
+| [src/client/apis/google_docs/GooglePhotosClientUtils.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/apis/google_docs/GooglePhotosClientUtils.ts) | TypeScript | 318 | 0 | 46 | 364 |
+| [src/client/apis/youtube/YoutubeBox.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/apis/youtube/YoutubeBox.scss) | SCSS | 105 | 5 | 16 | 126 |
+| [src/client/apis/youtube/YoutubeBox.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/apis/youtube/YoutubeBox.tsx) | TypeScript React | 274 | 48 | 40 | 362 |
+| [src/client/cognitive_services/CognitiveServices.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/cognitive_services/CognitiveServices.ts) | TypeScript | 342 | 10 | 57 | 409 |
+| [src/client/documents/DocumentTypes.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/documents/DocumentTypes.ts) | TypeScript | 32 | 2 | 3 | 37 |
+| [src/client/documents/Documents.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/documents/Documents.ts) | TypeScript | 807 | 141 | 87 | 1,035 |
+| [src/client/goldenLayout.d.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/goldenLayout.d.ts) | TypeScript | 2 | 0 | 1 | 3 |
+| [src/client/goldenLayout.js](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/goldenLayout.js) | JavaScript | 3,084 | 1,571 | 720 | 5,375 |
+| [src/client/util/DictationManager.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/util/DictationManager.ts) | TypeScript | 312 | 25 | 51 | 388 |
+| [src/client/util/DocumentManager.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/util/DocumentManager.ts) | TypeScript | 207 | 16 | 21 | 244 |
+| [src/client/util/DragManager.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/util/DragManager.ts) | TypeScript | 504 | 11 | 35 | 550 |
+| [src/client/util/DropConverter.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/util/DropConverter.ts) | TypeScript | 70 | 6 | 2 | 78 |
+| [src/client/util/History.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/util/History.ts) | TypeScript | 166 | 13 | 27 | 206 |
+| [src/client/util/Import & Export/DirectoryImportBox.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/util/Import%20%26%20Export/DirectoryImportBox.scss) | SCSS | 6 | 0 | 0 | 6 |
+| [src/client/util/Import & Export/DirectoryImportBox.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/util/Import%20%26%20Export/DirectoryImportBox.tsx) | TypeScript React | 393 | 0 | 31 | 424 |
+| [src/client/util/Import & Export/ImageUtils.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/util/Import%20%26%20Export/ImageUtils.ts) | TypeScript | 35 | 0 | 4 | 39 |
+| [src/client/util/Import & Export/ImportMetadataEntry.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/util/Import%20%26%20Export/ImportMetadataEntry.tsx) | TypeScript React | 132 | 0 | 17 | 149 |
+| [src/client/util/InteractionUtils.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/util/InteractionUtils.tsx) | TypeScript React | 122 | 112 | 21 | 255 |
+| [src/client/util/KeyCodes.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/util/KeyCodes.ts) | TypeScript | 100 | 36 | 0 | 136 |
+| [src/client/util/LinkManager.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/util/LinkManager.ts) | TypeScript | 161 | 30 | 23 | 214 |
+| [src/client/util/ProsemirrorCopy/prompt.js](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/util/ProsemirrorCopy/prompt.js) | JavaScript | 128 | 30 | 22 | 180 |
+| [src/client/util/Scripting.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/util/Scripting.ts) | TypeScript | 247 | 15 | 30 | 292 |
+| [src/client/util/ScrollBox.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/util/ScrollBox.tsx) | TypeScript React | 19 | 0 | 2 | 21 |
+| [src/client/util/SearchUtil.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/util/SearchUtil.ts) | TypeScript | 123 | 4 | 18 | 145 |
+| [src/client/util/SelectionManager.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/util/SelectionManager.ts) | TypeScript | 69 | 5 | 15 | 89 |
+| [src/client/util/SerializationHelper.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/util/SerializationHelper.ts) | TypeScript | 113 | 15 | 15 | 143 |
+| [src/client/util/SettingsManager.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/util/SettingsManager.scss) | SCSS | 111 | 0 | 25 | 136 |
+| [src/client/util/SettingsManager.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/util/SettingsManager.tsx) | TypeScript React | 114 | 0 | 17 | 131 |
+| [src/client/util/SharingManager.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/util/SharingManager.scss) | SCSS | 122 | 0 | 18 | 140 |
+| [src/client/util/SharingManager.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/util/SharingManager.tsx) | TypeScript React | 273 | 0 | 25 | 298 |
+| [src/client/util/Transform.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/util/Transform.ts) | TypeScript | 76 | 0 | 23 | 99 |
+| [src/client/util/TypedEvent.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/util/TypedEvent.ts) | TypeScript | 29 | 3 | 8 | 40 |
+| [src/client/util/UndoManager.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/util/UndoManager.ts) | TypeScript | 167 | 1 | 27 | 195 |
+| [src/client/util/clamp.js](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/util/clamp.js) | JavaScript | 14 | 0 | 1 | 15 |
+| [src/client/util/convertToCSSPTValue.js](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/util/convertToCSSPTValue.js) | JavaScript | 31 | 4 | 8 | 43 |
+| [src/client/util/jsx-decl.d.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/util/jsx-decl.d.ts) | TypeScript | 1 | 0 | 1 | 2 |
+| [src/client/util/request-image-size.js](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/util/request-image-size.js) | JavaScript | 51 | 10 | 14 | 75 |
+| [src/client/util/toCSSLineSpacing.js](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/util/toCSSLineSpacing.js) | JavaScript | 35 | 15 | 14 | 64 |
+| [src/client/views/AntimodeMenu.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/AntimodeMenu.scss) | SCSS | 35 | 1 | 6 | 42 |
+| [src/client/views/AntimodeMenu.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/AntimodeMenu.tsx) | TypeScript React | 121 | 14 | 22 | 157 |
+| [src/client/views/ContextMenu.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/ContextMenu.scss) | SCSS | 130 | 17 | 14 | 161 |
+| [src/client/views/ContextMenu.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/ContextMenu.tsx) | TypeScript React | 258 | 3 | 33 | 294 |
+| [src/client/views/ContextMenuItem.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/ContextMenuItem.tsx) | TypeScript React | 107 | 0 | 10 | 117 |
+| [src/client/views/DictationOverlay.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/DictationOverlay.tsx) | TypeScript React | 64 | 0 | 7 | 71 |
+| [src/client/views/DocComponent.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/DocComponent.tsx) | TypeScript React | 87 | 20 | 15 | 122 |
+| [src/client/views/DocumentButtonBar.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/DocumentButtonBar.scss) | SCSS | 89 | 0 | 16 | 105 |
+| [src/client/views/DocumentButtonBar.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/DocumentButtonBar.tsx) | TypeScript React | 284 | 4 | 27 | 315 |
+| [src/client/views/DocumentDecorations.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/DocumentDecorations.scss) | SCSS | 319 | 0 | 46 | 365 |
+| [src/client/views/DocumentDecorations.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/DocumentDecorations.tsx) | TypeScript React | 479 | 2 | 26 | 507 |
+| [src/client/views/EditableView.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/EditableView.scss) | SCSS | 22 | 0 | 3 | 25 |
+| [src/client/views/EditableView.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/EditableView.tsx) | TypeScript React | 149 | 19 | 16 | 184 |
+| [src/client/views/GestureOverlay.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/GestureOverlay.scss) | SCSS | 56 | 0 | 8 | 64 |
+| [src/client/views/GestureOverlay.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/GestureOverlay.tsx) | TypeScript React | 711 | 45 | 67 | 823 |
+| [src/client/views/GlobalKeyHandler.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/GlobalKeyHandler.ts) | TypeScript | 232 | 10 | 27 | 269 |
+| [src/client/views/InkingControl.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/InkingControl.scss) | SCSS | 127 | 4 | 0 | 131 |
+| [src/client/views/InkingControl.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/InkingControl.tsx) | TypeScript React | 78 | 3 | 10 | 91 |
+| [src/client/views/InkingStroke.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/InkingStroke.scss) | SCSS | 7 | 0 | 0 | 7 |
+| [src/client/views/InkingStroke.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/InkingStroke.tsx) | TypeScript React | 63 | 0 | 5 | 68 |
+| [src/client/views/KeyphraseQueryView.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/KeyphraseQueryView.scss) | SCSS | 7 | 0 | 1 | 8 |
+| [src/client/views/KeyphraseQueryView.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/KeyphraseQueryView.tsx) | TypeScript React | 30 | 2 | 3 | 35 |
+| [src/client/views/Main.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/Main.scss) | SCSS | 51 | 8 | 10 | 69 |
+| [src/client/views/Main.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/Main.tsx) | TypeScript React | 22 | 1 | 2 | 25 |
+| [src/client/views/MainView.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/MainView.scss) | SCSS | 138 | 1 | 21 | 160 |
+| [src/client/views/MainView.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/MainView.tsx) | TypeScript React | 553 | 13 | 36 | 602 |
+| [src/client/views/MainViewModal.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/MainViewModal.scss) | SCSS | 24 | 0 | 1 | 25 |
+| [src/client/views/MainViewModal.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/MainViewModal.tsx) | TypeScript React | 39 | 0 | 5 | 44 |
+| [src/client/views/MainViewNotifs.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/MainViewNotifs.scss) | SCSS | 17 | 0 | 1 | 18 |
+| [src/client/views/MainViewNotifs.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/MainViewNotifs.tsx) | TypeScript React | 29 | 0 | 4 | 33 |
+| [src/client/views/MetadataEntryMenu.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/MetadataEntryMenu.scss) | SCSS | 79 | 0 | 14 | 93 |
+| [src/client/views/MetadataEntryMenu.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/MetadataEntryMenu.tsx) | TypeScript React | 207 | 0 | 16 | 223 |
+| [src/client/views/OCRUtils.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/OCRUtils.ts) | TypeScript | 2 | 2 | 4 | 8 |
+| [src/client/views/OverlayView.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/OverlayView.scss) | SCSS | 41 | 0 | 6 | 47 |
+| [src/client/views/OverlayView.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/OverlayView.tsx) | TypeScript React | 194 | 4 | 19 | 217 |
+| [src/client/views/Palette.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/Palette.scss) | SCSS | 26 | 0 | 4 | 30 |
+| [src/client/views/Palette.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/Palette.tsx) | TypeScript React | 65 | 0 | 5 | 70 |
+| [src/client/views/PreviewCursor.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/PreviewCursor.scss) | SCSS | 9 | 0 | 1 | 10 |
+| [src/client/views/PreviewCursor.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/PreviewCursor.tsx) | TypeScript React | 115 | 8 | 9 | 132 |
+| [src/client/views/RecommendationsBox.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/RecommendationsBox.scss) | SCSS | 52 | 10 | 8 | 70 |
+| [src/client/views/RecommendationsBox.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/RecommendationsBox.tsx) | TypeScript React | 116 | 67 | 17 | 200 |
+| [src/client/views/ScriptBox.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/ScriptBox.scss) | SCSS | 15 | 0 | 2 | 17 |
+| [src/client/views/ScriptBox.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/ScriptBox.tsx) | TypeScript React | 112 | 2 | 12 | 126 |
+| [src/client/views/ScriptingRepl.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/ScriptingRepl.scss) | SCSS | 42 | 0 | 9 | 51 |
+| [src/client/views/ScriptingRepl.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/ScriptingRepl.tsx) | TypeScript React | 220 | 1 | 24 | 245 |
+| [src/client/views/SearchDocBox.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/SearchDocBox.tsx) | TypeScript React | 359 | 17 | 55 | 431 |
+| [src/client/views/TemplateMenu.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/TemplateMenu.scss) | SCSS | 46 | 0 | 5 | 51 |
+| [src/client/views/TemplateMenu.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/TemplateMenu.tsx) | TypeScript React | 167 | 1 | 14 | 182 |
+| [src/client/views/Templates.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/Templates.tsx) | TypeScript React | 34 | 0 | 8 | 42 |
+| [src/client/views/TouchScrollableMenu.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/TouchScrollableMenu.tsx) | TypeScript React | 52 | 0 | 7 | 59 |
+| [src/client/views/Touchable.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/Touchable.tsx) | TypeScript React | 172 | 28 | 39 | 239 |
+| [src/client/views/_nodeModuleOverrides.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/_nodeModuleOverrides.scss) | SCSS | 9 | 9 | 4 | 22 |
+| [src/client/views/animationtimeline/Keyframe.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/animationtimeline/Keyframe.scss) | SCSS | 83 | 5 | 17 | 105 |
+| [src/client/views/animationtimeline/Keyframe.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/animationtimeline/Keyframe.tsx) | TypeScript React | 468 | 50 | 42 | 560 |
+| [src/client/views/animationtimeline/Timeline.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/animationtimeline/Timeline.scss) | SCSS | 264 | 14 | 44 | 322 |
+| [src/client/views/animationtimeline/Timeline.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/animationtimeline/Timeline.tsx) | TypeScript React | 467 | 97 | 58 | 622 |
+| [src/client/views/animationtimeline/TimelineMenu.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/animationtimeline/TimelineMenu.scss) | SCSS | 75 | 2 | 17 | 94 |
+| [src/client/views/animationtimeline/TimelineMenu.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/animationtimeline/TimelineMenu.tsx) | TypeScript React | 68 | 0 | 10 | 78 |
+| [src/client/views/animationtimeline/TimelineOverview.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/animationtimeline/TimelineOverview.scss) | SCSS | 89 | 6 | 12 | 107 |
+| [src/client/views/animationtimeline/TimelineOverview.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/animationtimeline/TimelineOverview.tsx) | TypeScript React | 155 | 0 | 27 | 182 |
+| [src/client/views/animationtimeline/Track.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/animationtimeline/Track.scss) | SCSS | 13 | 0 | 2 | 15 |
+| [src/client/views/animationtimeline/Track.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/animationtimeline/Track.tsx) | TypeScript React | 284 | 63 | 33 | 380 |
+| [src/client/views/collections/CollectionCarouselView.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/collections/CollectionCarouselView.scss) | SCSS | 37 | 0 | 1 | 38 |
+| [src/client/views/collections/CollectionCarouselView.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/collections/CollectionCarouselView.tsx) | TypeScript React | 110 | 1 | 10 | 121 |
+| [src/client/views/collections/CollectionDockingView.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/collections/CollectionDockingView.scss) | SCSS | 391 | 7 | 60 | 458 |
+| [src/client/views/collections/CollectionDockingView.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/collections/CollectionDockingView.tsx) | TypeScript React | 707 | 61 | 65 | 833 |
+| [src/client/views/collections/CollectionLinearView.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/collections/CollectionLinearView.scss) | SCSS | 68 | 0 | 10 | 78 |
+| [src/client/views/collections/CollectionLinearView.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/collections/CollectionLinearView.tsx) | TypeScript React | 125 | 1 | 9 | 135 |
+| [src/client/views/collections/CollectionMapView.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/collections/CollectionMapView.scss) | SCSS | 27 | 0 | 3 | 30 |
+| [src/client/views/collections/CollectionMapView.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/collections/CollectionMapView.tsx) | TypeScript React | 235 | 10 | 18 | 263 |
+| [src/client/views/collections/CollectionMasonryViewFieldRow.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/collections/CollectionMasonryViewFieldRow.tsx) | TypeScript React | 308 | 0 | 24 | 332 |
+| [src/client/views/collections/CollectionPileView.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/collections/CollectionPileView.scss) | SCSS | 8 | 0 | 1 | 9 |
+| [src/client/views/collections/CollectionPileView.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/collections/CollectionPileView.tsx) | TypeScript React | 112 | 5 | 11 | 128 |
+| [src/client/views/collections/CollectionSchemaCells.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/collections/CollectionSchemaCells.tsx) | TypeScript React | 274 | 17 | 38 | 329 |
+| [src/client/views/collections/CollectionSchemaHeaders.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/collections/CollectionSchemaHeaders.tsx) | TypeScript React | 318 | 5 | 41 | 364 |
+| [src/client/views/collections/CollectionSchemaMovableTableHOC.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx) | TypeScript React | 216 | 0 | 27 | 243 |
+| [src/client/views/collections/CollectionSchemaView.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/collections/CollectionSchemaView.scss) | SCSS | 406 | 4 | 88 | 498 |
+| [src/client/views/collections/CollectionSchemaView.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/collections/CollectionSchemaView.tsx) | TypeScript React | 651 | 20 | 82 | 753 |
+| [src/client/views/collections/CollectionStackingView.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/collections/CollectionStackingView.scss) | SCSS | 353 | 1 | 50 | 404 |
+| [src/client/views/collections/CollectionStackingView.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/collections/CollectionStackingView.tsx) | TypeScript React | 430 | 5 | 25 | 460 |
+| [src/client/views/collections/CollectionStackingViewFieldColumn.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/collections/CollectionStackingViewFieldColumn.tsx) | TypeScript React | 367 | 0 | 27 | 394 |
+| [src/client/views/collections/CollectionStaffView.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/collections/CollectionStaffView.scss) | SCSS | 12 | 0 | 1 | 13 |
+| [src/client/views/collections/CollectionStaffView.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/collections/CollectionStaffView.tsx) | TypeScript React | 46 | 0 | 7 | 53 |
+| [src/client/views/collections/CollectionSubView.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/collections/CollectionSubView.tsx) | TypeScript React | 390 | 7 | 25 | 422 |
+| [src/client/views/collections/CollectionTimeView.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/collections/CollectionTimeView.scss) | SCSS | 80 | 0 | 13 | 93 |
+| [src/client/views/collections/CollectionTimeView.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/collections/CollectionTimeView.tsx) | TypeScript React | 177 | 0 | 15 | 192 |
+| [src/client/views/collections/CollectionTreeView.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/collections/CollectionTreeView.scss) | SCSS | 125 | 4 | 23 | 152 |
+| [src/client/views/collections/CollectionTreeView.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/collections/CollectionTreeView.tsx) | TypeScript React | 801 | 19 | 40 | 860 |
+| [src/client/views/collections/CollectionView.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/collections/CollectionView.scss) | SCSS | 70 | 0 | 8 | 78 |
+| [src/client/views/collections/CollectionView.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/collections/CollectionView.tsx) | TypeScript React | 457 | 13 | 34 | 504 |
+| [src/client/views/collections/CollectionViewChromes.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/collections/CollectionViewChromes.scss) | SCSS | 308 | 4 | 45 | 357 |
+| [src/client/views/collections/CollectionViewChromes.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/collections/CollectionViewChromes.tsx) | TypeScript React | 393 | 67 | 47 | 507 |
+| [src/client/views/collections/KeyRestrictionRow.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/collections/KeyRestrictionRow.tsx) | TypeScript React | 49 | 2 | 4 | 55 |
+| [src/client/views/collections/ParentDocumentSelector.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/collections/ParentDocumentSelector.scss) | SCSS | 54 | 0 | 2 | 56 |
+| [src/client/views/collections/ParentDocumentSelector.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/collections/ParentDocumentSelector.tsx) | TypeScript React | 120 | 0 | 11 | 131 |
+| [src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx) | TypeScript React | 422 | 10 | 27 | 459 |
+| [src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss) | SCSS | 19 | 0 | 1 | 20 |
+| [src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx) | TypeScript React | 110 | 5 | 4 | 119 |
+| [src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.scss) | SCSS | 11 | 0 | 0 | 11 |
+| [src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx) | TypeScript React | 44 | 0 | 2 | 46 |
+| [src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.scss) | SCSS | 20 | 1 | 3 | 24 |
+| [src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx) | TypeScript React | 67 | 0 | 12 | 79 |
+| [src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss) | SCSS | 95 | 9 | 17 | 121 |
+| [src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx) | TypeScript React | 1,186 | 47 | 97 | 1,330 |
+| [src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx) | TypeScript React | 52 | 0 | 5 | 57 |
+| [src/client/views/collections/collectionFreeForm/MarqueeView.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/collections/collectionFreeForm/MarqueeView.scss) | SCSS | 30 | 0 | 2 | 32 |
+| [src/client/views/collections/collectionFreeForm/MarqueeView.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/collections/collectionFreeForm/MarqueeView.tsx) | TypeScript React | 484 | 105 | 33 | 622 |
+| [src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.scss) | SCSS | 28 | 0 | 6 | 34 |
+| [src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx) | TypeScript React | 202 | 72 | 23 | 297 |
+| [src/client/views/collections/collectionMulticolumn/CollectionMultirowView.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.scss) | SCSS | 29 | 0 | 6 | 35 |
+| [src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx) | TypeScript React | 204 | 72 | 22 | 298 |
+| [src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx) | TypeScript React | 94 | 0 | 9 | 103 |
+| [src/client/views/collections/collectionMulticolumn/MulticolumnWidthLabel.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/collections/collectionMulticolumn/MulticolumnWidthLabel.tsx) | TypeScript React | 51 | 0 | 5 | 56 |
+| [src/client/views/collections/collectionMulticolumn/MultirowHeightLabel.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/collections/collectionMulticolumn/MultirowHeightLabel.tsx) | TypeScript React | 51 | 0 | 5 | 56 |
+| [src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx) | TypeScript React | 92 | 0 | 9 | 101 |
+| [src/client/views/globalCssVariables.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/globalCssVariables.scss) | SCSS | 30 | 10 | 3 | 43 |
+| [src/client/views/globalCssVariables.scss.d.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/globalCssVariables.scss.d.ts) | TypeScript | 9 | 0 | 2 | 11 |
+| [src/client/views/linking/LinkEditor.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/linking/LinkEditor.scss) | SCSS | 124 | 1 | 25 | 150 |
+| [src/client/views/linking/LinkEditor.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/linking/LinkEditor.tsx) | TypeScript React | 252 | 7 | 50 | 309 |
+| [src/client/views/linking/LinkMenu.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/linking/LinkMenu.scss) | SCSS | 40 | 0 | 13 | 53 |
+| [src/client/views/linking/LinkMenu.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/linking/LinkMenu.tsx) | TypeScript React | 65 | 1 | 10 | 76 |
+| [src/client/views/linking/LinkMenuGroup.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/linking/LinkMenuGroup.tsx) | TypeScript React | 84 | 0 | 10 | 94 |
+| [src/client/views/linking/LinkMenuItem.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/linking/LinkMenuItem.scss) | SCSS | 75 | 1 | 11 | 87 |
+| [src/client/views/linking/LinkMenuItem.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/linking/LinkMenuItem.tsx) | TypeScript React | 107 | 0 | 19 | 126 |
+| [src/client/views/nodes/AudioBox.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/nodes/AudioBox.scss) | SCSS | 146 | 0 | 0 | 146 |
+| [src/client/views/nodes/AudioBox.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/nodes/AudioBox.tsx) | TypeScript React | 261 | 2 | 24 | 287 |
+| [src/client/views/nodes/CollectionFreeFormDocumentView.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/nodes/CollectionFreeFormDocumentView.scss) | SCSS | 8 | 0 | 0 | 8 |
+| [src/client/views/nodes/CollectionFreeFormDocumentView.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/nodes/CollectionFreeFormDocumentView.tsx) | TypeScript React | 124 | 0 | 7 | 131 |
+| [src/client/views/nodes/ColorBox.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/nodes/ColorBox.scss) | SCSS | 22 | 0 | 1 | 23 |
+| [src/client/views/nodes/ColorBox.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/nodes/ColorBox.tsx) | TypeScript React | 28 | 0 | 4 | 32 |
+| [src/client/views/nodes/ContentFittingDocumentView.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/nodes/ContentFittingDocumentView.scss) | SCSS | 20 | 0 | 4 | 24 |
+| [src/client/views/nodes/ContentFittingDocumentView.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/nodes/ContentFittingDocumentView.tsx) | TypeScript React | 117 | 0 | 7 | 124 |
+| [src/client/views/nodes/DocumentBox.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/nodes/DocumentBox.scss) | SCSS | 14 | 0 | 0 | 14 |
+| [src/client/views/nodes/DocumentBox.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/nodes/DocumentBox.tsx) | TypeScript React | 154 | 0 | 4 | 158 |
+| [src/client/views/nodes/DocumentContentsView.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/nodes/DocumentContentsView.tsx) | TypeScript React | 183 | 10 | 17 | 210 |
+| [src/client/views/nodes/DocumentIcon.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/nodes/DocumentIcon.tsx) | TypeScript React | 60 | 0 | 5 | 65 |
+| [src/client/views/nodes/DocumentView.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/nodes/DocumentView.scss) | SCSS | 110 | 1 | 15 | 126 |
+| [src/client/views/nodes/DocumentView.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/nodes/DocumentView.tsx) | TypeScript React | 1,041 | 54 | 94 | 1,189 |
+| [src/client/views/nodes/FaceRectangle.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/nodes/FaceRectangle.tsx) | TypeScript React | 25 | 0 | 4 | 29 |
+| [src/client/views/nodes/FaceRectangles.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/nodes/FaceRectangles.tsx) | TypeScript React | 41 | 0 | 5 | 46 |
+| [src/client/views/nodes/FieldTextBox.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/nodes/FieldTextBox.scss) | SCSS | 12 | 0 | 3 | 15 |
+| [src/client/views/nodes/FieldView.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/nodes/FieldView.tsx) | TypeScript React | 89 | 43 | 4 | 136 |
+| [src/client/views/nodes/FontIconBox.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/nodes/FontIconBox.scss) | SCSS | 25 | 0 | 2 | 27 |
+| [src/client/views/nodes/FontIconBox.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/nodes/FontIconBox.tsx) | TypeScript React | 58 | 0 | 5 | 63 |
+| [src/client/views/nodes/ImageBox.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/nodes/ImageBox.scss) | SCSS | 135 | 0 | 17 | 152 |
+| [src/client/views/nodes/ImageBox.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/nodes/ImageBox.tsx) | TypeScript React | 430 | 10 | 35 | 475 |
+| [src/client/views/nodes/KeyValueBox.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/nodes/KeyValueBox.scss) | SCSS | 120 | 0 | 3 | 123 |
+| [src/client/views/nodes/KeyValueBox.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/nodes/KeyValueBox.tsx) | TypeScript React | 244 | 1 | 26 | 271 |
+| [src/client/views/nodes/KeyValuePair.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/nodes/KeyValuePair.scss) | SCSS | 55 | 1 | 4 | 60 |
+| [src/client/views/nodes/KeyValuePair.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/nodes/KeyValuePair.tsx) | TypeScript React | 125 | 2 | 8 | 135 |
+| [src/client/views/nodes/LabelBox.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/nodes/LabelBox.scss) | SCSS | 31 | 0 | 4 | 35 |
+| [src/client/views/nodes/LabelBox.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/nodes/LabelBox.tsx) | TypeScript React | 86 | 1 | 9 | 96 |
+| [src/client/views/nodes/LinkAnchorBox.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/nodes/LinkAnchorBox.scss) | SCSS | 27 | 0 | 2 | 29 |
+| [src/client/views/nodes/LinkAnchorBox.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/nodes/LinkAnchorBox.tsx) | TypeScript React | 141 | 0 | 9 | 150 |
+| [src/client/views/nodes/LinkBox.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/nodes/LinkBox.scss) | SCSS | 3 | 0 | 0 | 3 |
+| [src/client/views/nodes/LinkBox.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/nodes/LinkBox.tsx) | TypeScript React | 33 | 0 | 3 | 36 |
+| [src/client/views/nodes/PDFBox.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/nodes/PDFBox.scss) | SCSS | 200 | 0 | 19 | 219 |
+| [src/client/views/nodes/PDFBox.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/nodes/PDFBox.tsx) | TypeScript React | 246 | 0 | 19 | 265 |
+| [src/client/views/nodes/PresBox.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/nodes/PresBox.scss) | SCSS | 52 | 0 | 1 | 53 |
+| [src/client/views/nodes/PresBox.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/nodes/PresBox.tsx) | TypeScript React | 268 | 31 | 32 | 331 |
+| [src/client/views/nodes/QueryBox.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/nodes/QueryBox.scss) | SCSS | 5 | 0 | 0 | 5 |
+| [src/client/views/nodes/QueryBox.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/nodes/QueryBox.tsx) | TypeScript React | 37 | 0 | 4 | 41 |
+| [src/client/views/nodes/RadialMenu.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/nodes/RadialMenu.scss) | SCSS | 60 | 3 | 7 | 70 |
+| [src/client/views/nodes/RadialMenu.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/nodes/RadialMenu.tsx) | TypeScript React | 174 | 26 | 36 | 236 |
+| [src/client/views/nodes/RadialMenuItem.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/nodes/RadialMenuItem.tsx) | TypeScript React | 101 | 0 | 16 | 117 |
+| [src/client/views/nodes/ScreenshotBox.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/nodes/ScreenshotBox.scss) | SCSS | 40 | 6 | 5 | 51 |
+| [src/client/views/nodes/ScreenshotBox.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/nodes/ScreenshotBox.tsx) | TypeScript React | 174 | 2 | 18 | 194 |
+| [src/client/views/nodes/ScriptingBox.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/nodes/ScriptingBox.scss) | SCSS | 33 | 0 | 3 | 36 |
+| [src/client/views/nodes/ScriptingBox.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/nodes/ScriptingBox.tsx) | TypeScript React | 87 | 0 | 12 | 99 |
+| [src/client/views/nodes/SliderBox-components.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/nodes/SliderBox-components.tsx) | TypeScript React | 220 | 12 | 25 | 257 |
+| [src/client/views/nodes/SliderBox-tooltip.css](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/nodes/SliderBox-tooltip.css) | CSS | 30 | 0 | 3 | 33 |
+| [src/client/views/nodes/SliderBox.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/nodes/SliderBox.scss) | SCSS | 7 | 0 | 0 | 7 |
+| [src/client/views/nodes/SliderBox.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/nodes/SliderBox.tsx) | TypeScript React | 117 | 0 | 8 | 125 |
+| [src/client/views/nodes/VideoBox.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/nodes/VideoBox.scss) | SCSS | 65 | 3 | 6 | 74 |
+| [src/client/views/nodes/VideoBox.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/nodes/VideoBox.tsx) | TypeScript React | 343 | 2 | 34 | 379 |
+| [src/client/views/nodes/WebBox.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/nodes/WebBox.scss) | SCSS | 109 | 0 | 18 | 127 |
+| [src/client/views/nodes/WebBox.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/nodes/WebBox.tsx) | TypeScript React | 345 | 15 | 35 | 395 |
+| [src/client/views/nodes/formattedText/DashDocCommentView.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/nodes/formattedText/DashDocCommentView.tsx) | TypeScript React | 82 | 0 | 13 | 95 |
+| [src/client/views/nodes/formattedText/DashDocView.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/nodes/formattedText/DashDocView.tsx) | TypeScript React | 223 | 9 | 37 | 269 |
+| [src/client/views/nodes/formattedText/DashFieldView.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/nodes/formattedText/DashFieldView.scss) | SCSS | 34 | 0 | 2 | 36 |
+| [src/client/views/nodes/formattedText/DashFieldView.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/nodes/formattedText/DashFieldView.tsx) | TypeScript React | 178 | 13 | 20 | 211 |
+| [src/client/views/nodes/formattedText/FootnoteView.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/nodes/formattedText/FootnoteView.tsx) | TypeScript React | 131 | 13 | 19 | 163 |
+| [src/client/views/nodes/formattedText/FormattedTextBox.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/nodes/formattedText/FormattedTextBox.scss) | SCSS | 220 | 11 | 34 | 265 |
+| [src/client/views/nodes/formattedText/FormattedTextBox.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/nodes/formattedText/FormattedTextBox.tsx) | TypeScript React | 1,169 | 68 | 93 | 1,330 |
+| [src/client/views/nodes/formattedText/FormattedTextBoxComment.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/nodes/formattedText/FormattedTextBoxComment.scss) | SCSS | 33 | 0 | 0 | 33 |
+| [src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx) | TypeScript React | 218 | 11 | 8 | 237 |
+| [src/client/views/nodes/formattedText/ImageResizeView.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/nodes/formattedText/ImageResizeView.tsx) | TypeScript React | 113 | 0 | 25 | 138 |
+| [src/client/views/nodes/formattedText/ParagraphNodeSpec.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/nodes/formattedText/ParagraphNodeSpec.ts) | TypeScript | 108 | 9 | 26 | 143 |
+| [src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts) | TypeScript | 231 | 0 | 11 | 242 |
+| [src/client/views/nodes/formattedText/RichTextMenu.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/nodes/formattedText/RichTextMenu.scss) | SCSS | 100 | 2 | 19 | 121 |
+| [src/client/views/nodes/formattedText/RichTextMenu.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/nodes/formattedText/RichTextMenu.tsx) | TypeScript React | 754 | 12 | 109 | 875 |
+| [src/client/views/nodes/formattedText/RichTextRules.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/nodes/formattedText/RichTextRules.ts) | TypeScript | 308 | 7 | 5 | 320 |
+| [src/client/views/nodes/formattedText/RichTextSchema.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/nodes/formattedText/RichTextSchema.tsx) | TypeScript React | 465 | 33 | 39 | 537 |
+| [src/client/views/nodes/formattedText/SummaryView.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/nodes/formattedText/SummaryView.tsx) | TypeScript React | 67 | 0 | 14 | 81 |
+| [src/client/views/nodes/formattedText/TooltipTextMenu.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/nodes/formattedText/TooltipTextMenu.scss) | SCSS | 306 | 6 | 61 | 373 |
+| [src/client/views/nodes/formattedText/marks_rts.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/nodes/formattedText/marks_rts.ts) | TypeScript | 259 | 15 | 23 | 297 |
+| [src/client/views/nodes/formattedText/nodes_rts.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/nodes/formattedText/nodes_rts.ts) | TypeScript | 224 | 21 | 19 | 264 |
+| [src/client/views/nodes/formattedText/prosemirrorPatches.js](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/nodes/formattedText/prosemirrorPatches.js) | JavaScript | 118 | 12 | 9 | 139 |
+| [src/client/views/nodes/formattedText/schema_rts.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/nodes/formattedText/schema_rts.ts) | TypeScript | 12 | 8 | 6 | 26 |
+| [src/client/views/pdf/Annotation.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/pdf/Annotation.scss) | SCSS | 6 | 0 | 0 | 6 |
+| [src/client/views/pdf/Annotation.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/pdf/Annotation.tsx) | TypeScript React | 114 | 0 | 16 | 130 |
+| [src/client/views/pdf/PDFMenu.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/pdf/PDFMenu.scss) | SCSS | 6 | 0 | 0 | 6 |
+| [src/client/views/pdf/PDFMenu.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/pdf/PDFMenu.tsx) | TypeScript React | 102 | 0 | 21 | 123 |
+| [src/client/views/pdf/PDFViewer.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/pdf/PDFViewer.scss) | SCSS | 74 | 4 | 10 | 88 |
+| [src/client/views/pdf/PDFViewer.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/pdf/PDFViewer.tsx) | TypeScript React | 662 | 23 | 46 | 731 |
+| [src/client/views/presentationview/PresElementBox.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/presentationview/PresElementBox.scss) | SCSS | 93 | 0 | 10 | 103 |
+| [src/client/views/presentationview/PresElementBox.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/presentationview/PresElementBox.tsx) | TypeScript React | 179 | 31 | 14 | 224 |
+| [src/client/views/search/CheckBox.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/search/CheckBox.scss) | SCSS | 50 | 1 | 8 | 59 |
+| [src/client/views/search/CheckBox.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/search/CheckBox.tsx) | TypeScript React | 42 | 74 | 15 | 131 |
+| [src/client/views/search/CollectionFilters.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/search/CollectionFilters.scss) | SCSS | 17 | 0 | 3 | 20 |
+| [src/client/views/search/CollectionFilters.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/search/CollectionFilters.tsx) | TypeScript React | 69 | 0 | 14 | 83 |
+| [src/client/views/search/FieldFilters.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/search/FieldFilters.scss) | SCSS | 10 | 1 | 1 | 12 |
+| [src/client/views/search/FieldFilters.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/search/FieldFilters.tsx) | TypeScript React | 34 | 0 | 7 | 41 |
+| [src/client/views/search/FilterBox.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/search/FilterBox.scss) | SCSS | 153 | 0 | 25 | 178 |
+| [src/client/views/search/FilterBox.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/search/FilterBox.tsx) | TypeScript React | 354 | 24 | 54 | 432 |
+| [src/client/views/search/IconBar.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/search/IconBar.scss) | SCSS | 9 | 0 | 1 | 10 |
+| [src/client/views/search/IconBar.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/search/IconBar.tsx) | TypeScript React | 69 | 1 | 17 | 87 |
+| [src/client/views/search/IconButton.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/search/IconButton.scss) | SCSS | 46 | 1 | 6 | 53 |
+| [src/client/views/search/IconButton.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/search/IconButton.tsx) | TypeScript React | 170 | 2 | 19 | 191 |
+| [src/client/views/search/NaviconButton.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/search/NaviconButton.scss) | SCSS | 58 | 0 | 11 | 69 |
+| [src/client/views/search/NaviconButton.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/search/NaviconButton.tsx) | TypeScript React | 32 | 0 | 5 | 37 |
+| [src/client/views/search/SearchBox.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/search/SearchBox.scss) | SCSS | 203 | 82 | 51 | 336 |
+| [src/client/views/search/SearchBox.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/search/SearchBox.tsx) | TypeScript React | 530 | 47 | 94 | 671 |
+| [src/client/views/search/SearchItem.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/search/SearchItem.scss) | SCSS | 138 | 0 | 25 | 163 |
+| [src/client/views/search/SearchItem.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/search/SearchItem.tsx) | TypeScript React | 272 | 2 | 29 | 303 |
+| [src/client/views/search/SelectorContextMenu.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/search/SelectorContextMenu.scss) | SCSS | 12 | 1 | 3 | 16 |
+| [src/client/views/search/ToggleBar.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/search/ToggleBar.scss) | SCSS | 35 | 2 | 4 | 41 |
+| [src/client/views/search/ToggleBar.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/search/ToggleBar.tsx) | TypeScript React | 77 | 0 | 9 | 86 |
+| [src/client/views/webcam/DashWebRTCVideo.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/webcam/DashWebRTCVideo.scss) | SCSS | 70 | 4 | 9 | 83 |
+| [src/client/views/webcam/DashWebRTCVideo.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/webcam/DashWebRTCVideo.tsx) | TypeScript React | 67 | 6 | 16 | 89 |
+| [src/client/views/webcam/WebCamLogic.js](file:///Users/bcz/Documents/GitHub/Dash-Web/src/client/views/webcam/WebCamLogic.js) | JavaScript | 234 | 7 | 51 | 292 |
+| [src/debug/Repl.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/debug/Repl.tsx) | TypeScript React | 59 | 0 | 7 | 66 |
+| [src/debug/Test.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/debug/Test.tsx) | TypeScript React | 12 | 0 | 2 | 14 |
+| [src/debug/Viewer.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/debug/Viewer.tsx) | TypeScript React | 173 | 0 | 19 | 192 |
+| [src/extensions/ArrayExtensions.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/extensions/ArrayExtensions.ts) | TypeScript | 26 | 5 | 6 | 37 |
+| [src/extensions/General/Extensions.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/extensions/General/Extensions.ts) | TypeScript | 7 | 0 | 2 | 9 |
+| [src/extensions/General/ExtensionsTypings.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/extensions/General/ExtensionsTypings.ts) | TypeScript | 7 | 0 | 1 | 8 |
+| [src/extensions/StringExtensions.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/extensions/StringExtensions.ts) | TypeScript | 13 | 0 | 4 | 17 |
+| [src/mobile/ImageUpload.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/mobile/ImageUpload.scss) | SCSS | 30 | 0 | 4 | 34 |
+| [src/mobile/ImageUpload.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/mobile/ImageUpload.tsx) | TypeScript React | 78 | 41 | 12 | 131 |
+| [src/mobile/InkControls.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/mobile/InkControls.tsx) | TypeScript React | 0 | 0 | 1 | 1 |
+| [src/mobile/MobileInkOverlay.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/mobile/MobileInkOverlay.scss) | SCSS | 33 | 1 | 5 | 39 |
+| [src/mobile/MobileInkOverlay.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/mobile/MobileInkOverlay.tsx) | TypeScript React | 162 | 3 | 26 | 191 |
+| [src/mobile/MobileInterface.scss](file:///Users/bcz/Documents/GitHub/Dash-Web/src/mobile/MobileInterface.scss) | SCSS | 17 | 0 | 2 | 19 |
+| [src/mobile/MobileInterface.tsx](file:///Users/bcz/Documents/GitHub/Dash-Web/src/mobile/MobileInterface.tsx) | TypeScript React | 297 | 15 | 32 | 344 |
+| [src/new_fields/CursorField.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/new_fields/CursorField.ts) | TypeScript | 54 | 0 | 12 | 66 |
+| [src/new_fields/DateField.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/new_fields/DateField.ts) | TypeScript | 30 | 0 | 7 | 37 |
+| [src/new_fields/Doc.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/new_fields/Doc.ts) | TypeScript | 897 | 85 | 76 | 1,058 |
+| [src/new_fields/FieldSymbols.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/new_fields/FieldSymbols.ts) | TypeScript | 11 | 0 | 2 | 13 |
+| [src/new_fields/HtmlField.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/new_fields/HtmlField.ts) | TypeScript | 22 | 0 | 5 | 27 |
+| [src/new_fields/IconField.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/new_fields/IconField.ts) | TypeScript | 22 | 0 | 5 | 27 |
+| [src/new_fields/InkField.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/new_fields/InkField.ts) | TypeScript | 41 | 0 | 10 | 51 |
+| [src/new_fields/List.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/new_fields/List.ts) | TypeScript | 244 | 38 | 20 | 302 |
+| [src/new_fields/ListSpec.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/new_fields/ListSpec.ts) | TypeScript | 0 | 0 | 1 | 1 |
+| [src/new_fields/ObjectField.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/new_fields/ObjectField.ts) | TypeScript | 16 | 0 | 4 | 20 |
+| [src/new_fields/PresField.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/new_fields/PresField.ts) | TypeScript | 3 | 1 | 2 | 6 |
+| [src/new_fields/Proxy.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/new_fields/Proxy.ts) | TypeScript | 95 | 2 | 14 | 111 |
+| [src/new_fields/RefField.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/new_fields/RefField.ts) | TypeScript | 17 | 0 | 5 | 22 |
+| [src/new_fields/RichTextField.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/new_fields/RichTextField.ts) | TypeScript | 33 | 0 | 8 | 41 |
+| [src/new_fields/RichTextUtils.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/new_fields/RichTextUtils.ts) | TypeScript | 455 | 8 | 56 | 519 |
+| [src/new_fields/Schema.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/new_fields/Schema.ts) | TypeScript | 107 | 5 | 8 | 120 |
+| [src/new_fields/SchemaHeaderField.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/new_fields/SchemaHeaderField.ts) | TypeScript | 104 | 4 | 14 | 122 |
+| [src/new_fields/ScriptField.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/new_fields/ScriptField.ts) | TypeScript | 137 | 21 | 19 | 177 |
+| [src/new_fields/Types.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/new_fields/Types.ts) | TypeScript | 86 | 5 | 17 | 108 |
+| [src/new_fields/URLField.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/new_fields/URLField.ts) | TypeScript | 45 | 0 | 9 | 54 |
+| [src/new_fields/documentSchemas.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/new_fields/documentSchemas.ts) | TypeScript | 87 | 0 | 6 | 93 |
+| [src/new_fields/util.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/new_fields/util.ts) | TypeScript | 176 | 3 | 15 | 194 |
+| [src/pen-gestures/GestureUtils.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/pen-gestures/GestureUtils.ts) | TypeScript | 41 | 0 | 5 | 46 |
+| [src/pen-gestures/ndollar.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/pen-gestures/ndollar.ts) | TypeScript | 356 | 172 | 22 | 550 |
+| [src/scraping/acm/.gitignore](file:///Users/bcz/Documents/GitHub/Dash-Web/src/scraping/acm/.gitignore) | Ignore | 2 | 0 | 0 | 2 |
+| [src/scraping/acm/debug.log](file:///Users/bcz/Documents/GitHub/Dash-Web/src/scraping/acm/debug.log) | log | 38 | 0 | 1 | 39 |
+| [src/scraping/acm/index.js](file:///Users/bcz/Documents/GitHub/Dash-Web/src/scraping/acm/index.js) | JavaScript | 82 | 185 | 13 | 280 |
+| [src/scraping/acm/package.json](file:///Users/bcz/Documents/GitHub/Dash-Web/src/scraping/acm/package.json) | JSON | 17 | 0 | 1 | 18 |
+| [src/scraping/buxton/.idea/buxton.iml](file:///Users/bcz/Documents/GitHub/Dash-Web/src/scraping/buxton/.idea/buxton.iml) | XML | 8 | 0 | 0 | 8 |
+| [src/scraping/buxton/.idea/inspectionProfiles/profiles_settings.xml](file:///Users/bcz/Documents/GitHub/Dash-Web/src/scraping/buxton/.idea/inspectionProfiles/profiles_settings.xml) | XML | 6 | 0 | 0 | 6 |
+| [src/scraping/buxton/.idea/misc.xml](file:///Users/bcz/Documents/GitHub/Dash-Web/src/scraping/buxton/.idea/misc.xml) | XML | 4 | 0 | 0 | 4 |
+| [src/scraping/buxton/.idea/modules.xml](file:///Users/bcz/Documents/GitHub/Dash-Web/src/scraping/buxton/.idea/modules.xml) | XML | 8 | 0 | 0 | 8 |
+| [src/scraping/buxton/.idea/vcs.xml](file:///Users/bcz/Documents/GitHub/Dash-Web/src/scraping/buxton/.idea/vcs.xml) | XML | 6 | 0 | 0 | 6 |
+| [src/scraping/buxton/.idea/workspace.xml](file:///Users/bcz/Documents/GitHub/Dash-Web/src/scraping/buxton/.idea/workspace.xml) | XML | 173 | 0 | 0 | 173 |
+| [src/scraping/buxton/final/BuxtonImporter.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/scraping/buxton/final/BuxtonImporter.ts) | TypeScript | 228 | 142 | 26 | 396 |
+| [src/scraping/buxton/jsonifier.py](file:///Users/bcz/Documents/GitHub/Dash-Web/src/scraping/buxton/jsonifier.py) | Python | 183 | 1 | 48 | 232 |
+| [src/scraping/buxton/narratives.py](file:///Users/bcz/Documents/GitHub/Dash-Web/src/scraping/buxton/narratives.py) | Python | 11 | 19 | 9 | 39 |
+| [src/scraping/buxton/narratives/chord_keyboards.json](file:///Users/bcz/Documents/GitHub/Dash-Web/src/scraping/buxton/narratives/chord_keyboards.json) | JSON | 39 | 0 | 0 | 39 |
+| [src/scraping/buxton/scraper.py](file:///Users/bcz/Documents/GitHub/Dash-Web/src/scraping/buxton/scraper.py) | Python | 350 | 5 | 78 | 433 |
+| [src/server/ActionUtilities.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/server/ActionUtilities.ts) | TypeScript | 136 | 1 | 23 | 160 |
+| [src/server/ApiManagers/ApiManager.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/server/ApiManagers/ApiManager.ts) | TypeScript | 8 | 0 | 3 | 11 |
+| [src/server/ApiManagers/DeleteManager.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/server/ApiManagers/DeleteManager.ts) | TypeScript | 71 | 0 | 11 | 82 |
+| [src/server/ApiManagers/DownloadManager.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/server/ApiManagers/DownloadManager.ts) | TypeScript | 173 | 80 | 16 | 269 |
+| [src/server/ApiManagers/GeneralGoogleManager.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/server/ApiManagers/GeneralGoogleManager.ts) | TypeScript | 53 | 0 | 8 | 61 |
+| [src/server/ApiManagers/GooglePhotosManager.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/server/ApiManagers/GooglePhotosManager.ts) | TypeScript | 190 | 119 | 22 | 331 |
+| [src/server/ApiManagers/PDFManager.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/server/ApiManagers/PDFManager.ts) | TypeScript | 103 | 0 | 12 | 115 |
+| [src/server/ApiManagers/SearchManager.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/server/ApiManagers/SearchManager.ts) | TypeScript | 200 | 0 | 15 | 215 |
+| [src/server/ApiManagers/SessionManager.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/server/ApiManagers/SessionManager.ts) | TypeScript | 56 | 0 | 11 | 67 |
+| [src/server/ApiManagers/UploadManager.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/server/ApiManagers/UploadManager.ts) | TypeScript | 226 | 1 | 20 | 247 |
+| [src/server/ApiManagers/UserManager.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/server/ApiManagers/UserManager.ts) | TypeScript | 101 | 4 | 21 | 126 |
+| [src/server/ApiManagers/UtilManager.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/server/ApiManagers/UtilManager.ts) | TypeScript | 42 | 22 | 10 | 74 |
+| [src/server/Client.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/server/Client.ts) | TypeScript | 8 | 0 | 3 | 11 |
+| [src/server/DashSession/DashSessionAgent.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/server/DashSession/DashSessionAgent.ts) | TypeScript | 155 | 52 | 23 | 230 |
+| [src/server/DashSession/Session/agents/applied_session_agent.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/server/DashSession/Session/agents/applied_session_agent.ts) | TypeScript | 47 | 2 | 9 | 58 |
+| [src/server/DashSession/Session/agents/monitor.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/server/DashSession/Session/agents/monitor.ts) | TypeScript | 213 | 59 | 26 | 298 |
+| [src/server/DashSession/Session/agents/process_message_router.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/server/DashSession/Session/agents/process_message_router.ts) | TypeScript | 24 | 10 | 7 | 41 |
+| [src/server/DashSession/Session/agents/promisified_ipc_manager.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/server/DashSession/Session/agents/promisified_ipc_manager.ts) | TypeScript | 106 | 52 | 15 | 173 |
+| [src/server/DashSession/Session/agents/server_worker.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/server/DashSession/Session/agents/server_worker.ts) | TypeScript | 99 | 46 | 15 | 160 |
+| [src/server/DashSession/Session/utilities/repl.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/server/DashSession/Session/utilities/repl.ts) | TypeScript | 116 | 0 | 12 | 128 |
+| [src/server/DashSession/Session/utilities/session_config.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/server/DashSession/Session/utilities/session_config.ts) | TypeScript | 119 | 0 | 10 | 129 |
+| [src/server/DashSession/Session/utilities/utilities.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/server/DashSession/Session/utilities/utilities.ts) | TypeScript | 24 | 8 | 5 | 37 |
+| [src/server/DashUploadUtils.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/server/DashUploadUtils.ts) | TypeScript | 285 | 52 | 30 | 367 |
+| [src/server/GarbageCollector.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/server/GarbageCollector.ts) | TypeScript | 138 | 2 | 11 | 151 |
+| [src/server/IDatabase.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/server/IDatabase.ts) | TypeScript | 17 | 0 | 8 | 25 |
+| [src/server/MemoryDatabase.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/server/MemoryDatabase.ts) | TypeScript | 87 | 0 | 14 | 101 |
+| [src/server/Message.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/server/Message.ts) | TypeScript | 86 | 0 | 18 | 104 |
+| [src/server/PdfTypes.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/server/PdfTypes.ts) | TypeScript | 19 | 0 | 2 | 21 |
+| [src/server/ProcessFactory.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/server/ProcessFactory.ts) | TypeScript | 34 | 0 | 10 | 44 |
+| [src/server/Recommender.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/server/Recommender.ts) | TypeScript | 0 | 120 | 18 | 138 |
+| [src/server/RouteManager.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/server/RouteManager.ts) | TypeScript | 187 | 4 | 19 | 210 |
+| [src/server/RouteSubscriber.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/server/RouteSubscriber.ts) | TypeScript | 21 | 0 | 5 | 26 |
+| [src/server/Search.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/server/Search.ts) | TypeScript | 71 | 2 | 8 | 81 |
+| [src/server/SharedMediaTypes.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/server/SharedMediaTypes.ts) | TypeScript | 41 | 0 | 10 | 51 |
+| [src/server/Websocket/Websocket.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/server/Websocket/Websocket.ts) | TypeScript | 263 | 5 | 46 | 314 |
+| [src/server/apis/google/GoogleApiServerUtils.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/server/apis/google/GoogleApiServerUtils.ts) | TypeScript | 172 | 168 | 25 | 365 |
+| [src/server/apis/google/SharedTypes.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/server/apis/google/SharedTypes.ts) | TypeScript | 19 | 0 | 2 | 21 |
+| [src/server/apis/youtube/youtubeApiSample.d.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/server/apis/youtube/youtubeApiSample.d.ts) | TypeScript | 2 | 0 | 0 | 2 |
+| [src/server/apis/youtube/youtubeApiSample.js](file:///Users/bcz/Documents/GitHub/Dash-Web/src/server/apis/youtube/youtubeApiSample.js) | JavaScript | 135 | 30 | 14 | 179 |
+| [src/server/authentication/config/passport.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/server/authentication/config/passport.ts) | TypeScript | 23 | 2 | 4 | 29 |
+| [src/server/authentication/controllers/user_controller.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/server/authentication/controllers/user_controller.ts) | TypeScript | 218 | 25 | 25 | 268 |
+| [src/server/authentication/models/current_user_utils.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/server/authentication/models/current_user_utils.ts) | TypeScript | 586 | 30 | 57 | 673 |
+| [src/server/authentication/models/user_model.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/server/authentication/models/user_model.ts) | TypeScript | 63 | 9 | 14 | 86 |
+| [src/server/credentials/CredentialsLoader.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/server/credentials/CredentialsLoader.ts) | TypeScript | 24 | 0 | 6 | 30 |
+| [src/server/credentials/google_project_credentials.json](file:///Users/bcz/Documents/GitHub/Dash-Web/src/server/credentials/google_project_credentials.json) | JSON | 11 | 0 | 0 | 11 |
+| [src/server/database.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/server/database.ts) | TypeScript | 312 | 0 | 38 | 350 |
+| [src/server/downsize.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/server/downsize.ts) | TypeScript | 34 | 5 | 1 | 40 |
+| [src/server/index.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/server/index.ts) | TypeScript | 107 | 36 | 16 | 159 |
+| [src/server/remapUrl.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/server/remapUrl.ts) | TypeScript | 53 | 4 | 6 | 63 |
+| [src/server/server_Initialization.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/server/server_Initialization.ts) | TypeScript | 138 | 6 | 24 | 168 |
+| [src/server/slides.json](file:///Users/bcz/Documents/GitHub/Dash-Web/src/server/slides.json) | JSON | 10,820 | 0 | 0 | 10,820 |
+| [src/server/updateProtos.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/server/updateProtos.ts) | TypeScript | 11 | 0 | 3 | 14 |
+| [src/typings/index.d.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/src/typings/index.d.ts) | TypeScript | 219 | 72 | 38 | 329 |
+| [test/test.ts](file:///Users/bcz/Documents/GitHub/Dash-Web/test/test.ts) | TypeScript | 141 | 0 | 20 | 161 |
+| [tsconfig.json](file:///Users/bcz/Documents/GitHub/Dash-Web/tsconfig.json) | JSON | 21 | 5 | 0 | 26 |
+| [tslint.json](file:///Users/bcz/Documents/GitHub/Dash-Web/tslint.json) | JSON | 30 | 32 | 1 | 63 |
+| [views/forgot.pug](file:///Users/bcz/Documents/GitHub/Dash-Web/views/forgot.pug) | Pug | 19 | 1 | 2 | 22 |
+| [views/layout.pug](file:///Users/bcz/Documents/GitHub/Dash-Web/views/layout.pug) | Pug | 13 | 0 | 1 | 14 |
+| [views/login.pug](file:///Users/bcz/Documents/GitHub/Dash-Web/views/login.pug) | Pug | 24 | 0 | 2 | 26 |
+| [views/reset.pug](file:///Users/bcz/Documents/GitHub/Dash-Web/views/reset.pug) | Pug | 20 | 0 | 2 | 22 |
+| [views/signup.pug](file:///Users/bcz/Documents/GitHub/Dash-Web/views/signup.pug) | Pug | 25 | 0 | 2 | 27 |
+| [views/stylesheets/authentication.css](file:///Users/bcz/Documents/GitHub/Dash-Web/views/stylesheets/authentication.css) | CSS | 185 | 4 | 34 | 223 |
+| [views/user_activity.pug](file:///Users/bcz/Documents/GitHub/Dash-Web/views/user_activity.pug) | Pug | 18 | 0 | 1 | 19 |
+| [webpack.config.js](file:///Users/bcz/Documents/GitHub/Dash-Web/webpack.config.js) | JavaScript | 116 | 0 | 5 | 121 |
+
+[summary](results.md)
\ No newline at end of file
diff --git a/.VSCodeCounter/results.csv b/.VSCodeCounter/results.csv
new file mode 100644
index 000000000..b31bc8262
--- /dev/null
+++ b/.VSCodeCounter/results.csv
@@ -0,0 +1,648 @@
+filename, language, Markdown, JavaScript, JSON, Batch, Python, TypeScript, HTML, Pug, Shell Script, CSS, TypeScript React, SCSS, XML, Properties, Ignore, log, XSL, comment, blank, total
+README.md, Markdown, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 9
+build/index.html, HTML, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 12
+dash.bat, Batch, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3
+deploy/assets/env.json, JSON, 0, 0, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15
+deploy/assets/pdf.worker.js, JavaScript, 0, 55662, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 174, 686, 56522
+deploy/debug/repl.html, HTML, 0, 0, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 14
+deploy/debug/test.html, HTML, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 13
+deploy/debug/viewer.html, HTML, 0, 0, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 14
+deploy/index.html, HTML, 0, 0, 0, 0, 0, 0, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 16
+deploy/mobile/image.html, HTML, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 15
+deploy/mobile/ink.html, HTML, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 13
+package-lock.json, JSON, 0, 0, 18689, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 18690
+package.json, JSON, 0, 0, 266, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 267
+sentence_parser.py, Python, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 7
+session.config.json, JSON, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 13
+solr-8.3.1/bin/install_solr_service.sh, Shell Script, 0, 0, 0, 0, 0, 0, 0, 0, 307, 0, 0, 0, 0, 0, 0, 0, 0, 29, 35, 371
+solr-8.3.1/bin/oom_solr.sh, Shell Script, 0, 0, 0, 0, 0, 0, 0, 0, 13, 0, 0, 0, 0, 0, 0, 0, 0, 15, 3, 31
+solr-8.3.1/bin/solr.cmd, Batch, 0, 0, 0, 1782, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 43, 210, 2035
+solr-8.3.1/bin/solr.in.cmd, Batch, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 133, 29, 178
+solr-8.3.1/bin/solr.in.sh, Shell Script, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 172, 34, 206
+solr-8.3.1/contrib/prometheus-exporter/bin/solr-exporter.cmd, Batch, 0, 0, 0, 82, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 108
+solr-8.3.1/contrib/prometheus-exporter/conf/grafana-solr-dashboard.json, JSON, 0, 0, 4465, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 4466
+solr-8.3.1/contrib/prometheus-exporter/conf/solr-exporter-config.xml, XML, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1734, 0, 0, 0, 0, 63, 10, 1807
+solr-8.3.1/docs/images/solr.svg, XML, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 39, 0, 0, 0, 0, 0, 1, 40
+solr-8.3.1/docs/index.html, HTML, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 21
+solr-8.3.1/example/example-DIH/solr/atom/conf/atom-data-config.xml, XML, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 0, 0, 0, 0, 6, 9, 36
+solr-8.3.1/example/example-DIH/solr/atom/conf/solrconfig.xml, XML, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 0, 37, 8, 65
+solr-8.3.1/example/example-DIH/solr/atom/core.properties, Properties, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2
+solr-8.3.1/example/example-DIH/solr/db/conf/clustering/carrot2/kmeans-attributes.xml, XML, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 0, 0, 0, 0, 6, 1, 20
+solr-8.3.1/example/example-DIH/solr/db/conf/clustering/carrot2/lingo-attributes.xml, XML, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 0, 0, 0, 0, 11, 1, 25
+solr-8.3.1/example/example-DIH/solr/db/conf/clustering/carrot2/stc-attributes.xml, XML, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 0, 0, 0, 0, 6, 1, 20
+solr-8.3.1/example/example-DIH/solr/db/conf/currency.xml, XML, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 45, 0, 0, 0, 0, 19, 4, 68
+solr-8.3.1/example/example-DIH/solr/db/conf/db-data-config.xml, XML, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 0, 0, 0, 0, 0, 4, 30
+solr-8.3.1/example/example-DIH/solr/db/conf/elevate.xml, XML, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 37, 3, 43
+solr-8.3.1/example/example-DIH/solr/db/conf/solrconfig.xml, XML, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 292, 0, 0, 0, 0, 958, 104, 1354
+solr-8.3.1/example/example-DIH/solr/db/conf/update-script.js, JavaScript, 0, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 13, 54
+solr-8.3.1/example/example-DIH/solr/db/conf/xslt/example.xsl, XSL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 98, 20, 15, 133
+solr-8.3.1/example/example-DIH/solr/db/conf/xslt/example_atom.xsl, XSL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 20, 8, 68
+solr-8.3.1/example/example-DIH/solr/db/conf/xslt/example_rss.xsl, XSL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 41, 20, 6, 67
+solr-8.3.1/example/example-DIH/solr/db/conf/xslt/luke.xsl, XSL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 301, 19, 18, 338
+solr-8.3.1/example/example-DIH/solr/db/conf/xslt/updateXml.xsl, XSL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, 25, 11, 71
+solr-8.3.1/example/example-DIH/solr/db/core.properties, Properties, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2
+solr-8.3.1/example/example-DIH/solr/mail/conf/clustering/carrot2/kmeans-attributes.xml, XML, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 0, 0, 0, 0, 6, 1, 20
+solr-8.3.1/example/example-DIH/solr/mail/conf/clustering/carrot2/lingo-attributes.xml, XML, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 0, 0, 0, 0, 11, 1, 25
+solr-8.3.1/example/example-DIH/solr/mail/conf/clustering/carrot2/stc-attributes.xml, XML, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 0, 0, 0, 0, 6, 1, 20
+solr-8.3.1/example/example-DIH/solr/mail/conf/currency.xml, XML, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 45, 0, 0, 0, 0, 19, 4, 68
+solr-8.3.1/example/example-DIH/solr/mail/conf/elevate.xml, XML, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 37, 3, 43
+solr-8.3.1/example/example-DIH/solr/mail/conf/mail-data-config.xml, XML, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 4, 1, 13
+solr-8.3.1/example/example-DIH/solr/mail/conf/solrconfig.xml, XML, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 294, 0, 0, 0, 0, 958, 105, 1357
+solr-8.3.1/example/example-DIH/solr/mail/conf/update-script.js, JavaScript, 0, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 13, 54
+solr-8.3.1/example/example-DIH/solr/mail/conf/xslt/example.xsl, XSL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 98, 20, 15, 133
+solr-8.3.1/example/example-DIH/solr/mail/conf/xslt/example_atom.xsl, XSL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 20, 8, 68
+solr-8.3.1/example/example-DIH/solr/mail/conf/xslt/example_rss.xsl, XSL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 41, 20, 6, 67
+solr-8.3.1/example/example-DIH/solr/mail/conf/xslt/luke.xsl, XSL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 301, 19, 18, 338
+solr-8.3.1/example/example-DIH/solr/mail/conf/xslt/updateXml.xsl, XSL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, 25, 11, 71
+solr-8.3.1/example/example-DIH/solr/mail/core.properties, Properties, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2
+solr-8.3.1/example/example-DIH/solr/solr.xml, XML, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 1, 3
+solr-8.3.1/example/example-DIH/solr/solr/conf/clustering/carrot2/kmeans-attributes.xml, XML, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 0, 0, 0, 0, 6, 1, 20
+solr-8.3.1/example/example-DIH/solr/solr/conf/clustering/carrot2/lingo-attributes.xml, XML, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 0, 0, 0, 0, 11, 1, 25
+solr-8.3.1/example/example-DIH/solr/solr/conf/clustering/carrot2/stc-attributes.xml, XML, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 0, 0, 0, 0, 6, 1, 20
+solr-8.3.1/example/example-DIH/solr/solr/conf/currency.xml, XML, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 45, 0, 0, 0, 0, 19, 4, 68
+solr-8.3.1/example/example-DIH/solr/solr/conf/elevate.xml, XML, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 37, 3, 43
+solr-8.3.1/example/example-DIH/solr/solr/conf/solr-data-config.xml, XML, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 16, 2, 26
+solr-8.3.1/example/example-DIH/solr/solr/conf/solrconfig.xml, XML, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 292, 0, 0, 0, 0, 958, 102, 1352
+solr-8.3.1/example/example-DIH/solr/solr/conf/update-script.js, JavaScript, 0, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 13, 54
+solr-8.3.1/example/example-DIH/solr/solr/conf/xslt/example.xsl, XSL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 98, 20, 15, 133
+solr-8.3.1/example/example-DIH/solr/solr/conf/xslt/example_atom.xsl, XSL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 20, 8, 68
+solr-8.3.1/example/example-DIH/solr/solr/conf/xslt/example_rss.xsl, XSL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 41, 20, 6, 67
+solr-8.3.1/example/example-DIH/solr/solr/conf/xslt/luke.xsl, XSL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 301, 19, 18, 338
+solr-8.3.1/example/example-DIH/solr/solr/conf/xslt/updateXml.xsl, XSL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, 25, 11, 71
+solr-8.3.1/example/example-DIH/solr/solr/core.properties, Properties, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2
+solr-8.3.1/example/example-DIH/solr/tika/conf/solrconfig.xml, XML, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 0, 38, 7, 62
+solr-8.3.1/example/example-DIH/solr/tika/conf/tika-data-config.xml, XML, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 0, 3, 7, 27
+solr-8.3.1/example/example-DIH/solr/tika/core.properties, Properties, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2
+solr-8.3.1/example/exampledocs/books.json, JSON, 0, 0, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 52
+solr-8.3.1/example/exampledocs/gb18030-example.xml, XML, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 0, 16, 3, 33
+solr-8.3.1/example/exampledocs/hd.xml, XML, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 33, 0, 0, 0, 0, 20, 4, 57
+solr-8.3.1/example/exampledocs/ipod_other.xml, XML, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 20, 9, 61
+solr-8.3.1/example/exampledocs/ipod_video.xml, XML, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 0, 0, 0, 0, 18, 2, 41
+solr-8.3.1/example/exampledocs/manufacturers.xml, XML, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 57, 0, 0, 0, 0, 16, 3, 76
+solr-8.3.1/example/exampledocs/mem.xml, XML, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 45, 0, 0, 0, 0, 24, 9, 78
+solr-8.3.1/example/exampledocs/money.xml, XML, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 0, 0, 0, 0, 17, 7, 66
+solr-8.3.1/example/exampledocs/monitor.xml, XML, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 0, 18, 3, 35
+solr-8.3.1/example/exampledocs/monitor2.xml, XML, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 0, 0, 0, 0, 18, 3, 34
+solr-8.3.1/example/exampledocs/mp500.xml, XML, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23, 0, 0, 0, 0, 18, 3, 44
+solr-8.3.1/example/exampledocs/sample.html, HTML, 0, 0, 0, 0, 0, 0, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 14
+solr-8.3.1/example/exampledocs/sd500.xml, XML, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 18, 2, 39
+solr-8.3.1/example/exampledocs/solr.xml, XML, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 0, 16, 3, 39
+solr-8.3.1/example/exampledocs/test_utf8.sh, Shell Script, 0, 0, 0, 0, 0, 0, 0, 0, 57, 0, 0, 0, 0, 0, 0, 0, 0, 21, 16, 94
+solr-8.3.1/example/exampledocs/utf8-example.xml, XML, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 20, 4, 43
+solr-8.3.1/example/exampledocs/vidcard.xml, XML, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 0, 0, 0, 0, 21, 2, 63
+solr-8.3.1/example/files/browse-resources/velocity/resources.properties, Properties, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 72, 0, 0, 0, 6, 5, 83
+solr-8.3.1/example/files/browse-resources/velocity/resources_de_DE.properties, Properties, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 0, 0, 0, 0, 1, 19
+solr-8.3.1/example/files/browse-resources/velocity/resources_fr_FR.properties, Properties, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 0, 0, 0, 0, 3, 21
+solr-8.3.1/example/files/conf/currency.xml, XML, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 45, 0, 0, 0, 0, 19, 4, 68
+solr-8.3.1/example/files/conf/elevate.xml, XML, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 37, 3, 43
+solr-8.3.1/example/files/conf/params.json, JSON, 0, 0, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 35
+solr-8.3.1/example/files/conf/solrconfig.xml, XML, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 298, 0, 0, 0, 0, 979, 102, 1379
+solr-8.3.1/example/files/conf/update-script.js, JavaScript, 0, 80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 23, 116
+solr-8.3.1/example/files/conf/velocity/dropit.js, JavaScript, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2
+solr-8.3.1/example/files/conf/velocity/jquery.tx3-tag-cloud.js, JavaScript, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2
+solr-8.3.1/example/files/conf/velocity/js/dropit.js, JavaScript, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 19, 98
+solr-8.3.1/example/files/conf/velocity/js/jquery.autocomplete.js, JavaScript, 0, 620, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 68, 76, 764
+solr-8.3.1/example/files/conf/velocity/js/jquery.tx3-tag-cloud.js, JavaScript, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 9, 71
+solr-8.3.1/example/films/film_data_generator.py, Python, 0, 0, 0, 0, 82, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 12, 118
+solr-8.3.1/example/films/films.json, JSON, 0, 0, 15830, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 15831
+solr-8.3.1/example/films/films.xml, XML, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11438, 0, 0, 0, 0, 0, 1, 11439
+solr-8.3.1/server/contexts/solr-jetty-context.xml, XML, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 1, 9
+solr-8.3.1/server/etc/jetty-http.xml, XML, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 33, 0, 0, 0, 0, 15, 4, 52
+solr-8.3.1/server/etc/jetty-https.xml, XML, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 0, 16, 5, 77
+solr-8.3.1/server/etc/jetty-https8.xml, XML, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 0, 0, 0, 0, 32, 4, 70
+solr-8.3.1/server/etc/jetty-ssl.xml, XML, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23, 0, 0, 0, 0, 11, 4, 38
+solr-8.3.1/server/etc/jetty.xml, XML, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 110, 0, 0, 0, 0, 94, 18, 222
+solr-8.3.1/server/etc/webdefault.xml, XML, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 272, 0, 0, 0, 0, 232, 24, 528
+solr-8.3.1/server/resources/jetty-logging.properties, Properties, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 2
+solr-8.3.1/server/resources/log4j2-console.xml, XML, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 43, 6, 68
+solr-8.3.1/server/resources/log4j2.xml, XML, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 54, 0, 0, 0, 0, 80, 9, 143
+solr-8.3.1/server/scripts/cloud-scripts/snapshotscli.sh, Shell Script, 0, 0, 0, 0, 0, 0, 0, 0, 152, 0, 0, 0, 0, 0, 0, 0, 0, 2, 23, 177
+solr-8.3.1/server/scripts/cloud-scripts/zkcli.bat, Batch, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 7, 26
+solr-8.3.1/server/scripts/cloud-scripts/zkcli.sh, Shell Script, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 27
+solr-8.3.1/server/solr-webapp/webapp/WEB-INF/web.xml, XML, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 0, 0, 0, 0, 42, 11, 115
+solr-8.3.1/server/solr-webapp/webapp/css/angular/analysis.css, CSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 237, 0, 0, 0, 0, 0, 0, 0, 19, 48, 304
+solr-8.3.1/server/solr-webapp/webapp/css/angular/chosen.css, CSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 402, 0, 0, 0, 0, 0, 0, 0, 55, 9, 466
+solr-8.3.1/server/solr-webapp/webapp/css/angular/cloud.css, CSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 594, 0, 0, 0, 0, 0, 0, 0, 23, 106, 723
+solr-8.3.1/server/solr-webapp/webapp/css/angular/collections.css, CSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 296, 0, 0, 0, 0, 0, 0, 0, 18, 65, 379
+solr-8.3.1/server/solr-webapp/webapp/css/angular/common.css, CSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 647, 0, 0, 0, 0, 0, 0, 0, 19, 106, 772
+solr-8.3.1/server/solr-webapp/webapp/css/angular/cores.css, CSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 171, 0, 0, 0, 0, 0, 0, 0, 18, 37, 226
+solr-8.3.1/server/solr-webapp/webapp/css/angular/dashboard.css, CSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 134, 0, 0, 0, 0, 0, 0, 0, 18, 28, 180
+solr-8.3.1/server/solr-webapp/webapp/css/angular/dataimport.css, CSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 292, 0, 0, 0, 0, 0, 0, 0, 18, 61, 371
+solr-8.3.1/server/solr-webapp/webapp/css/angular/documents.css, CSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 131, 0, 0, 0, 0, 0, 0, 0, 23, 26, 180
+solr-8.3.1/server/solr-webapp/webapp/css/angular/files.css, CSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, 0, 0, 0, 0, 0, 0, 0, 18, 7, 54
+solr-8.3.1/server/solr-webapp/webapp/css/angular/index.css, CSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 164, 0, 0, 0, 0, 0, 0, 0, 18, 35, 217
+solr-8.3.1/server/solr-webapp/webapp/css/angular/java-properties.css, CSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 0, 0, 0, 0, 0, 0, 0, 18, 6, 48
+solr-8.3.1/server/solr-webapp/webapp/css/angular/jquery-ui.min.css, CSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 26, 2, 29
+solr-8.3.1/server/solr-webapp/webapp/css/angular/jquery-ui.structure.min.css, CSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 22, 2, 25
+solr-8.3.1/server/solr-webapp/webapp/css/angular/logging.css, CSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 303, 0, 0, 0, 0, 0, 0, 0, 19, 63, 385
+solr-8.3.1/server/solr-webapp/webapp/css/angular/login.css, CSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 80, 0, 0, 0, 0, 0, 0, 0, 18, 12, 110
+solr-8.3.1/server/solr-webapp/webapp/css/angular/menu.css, CSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 257, 0, 0, 0, 0, 0, 0, 0, 18, 56, 331
+solr-8.3.1/server/solr-webapp/webapp/css/angular/overview.css, CSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 18, 5, 43
+solr-8.3.1/server/solr-webapp/webapp/css/angular/plugins.css, CSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 172, 0, 0, 0, 0, 0, 0, 0, 18, 31, 221
+solr-8.3.1/server/solr-webapp/webapp/css/angular/query.css, CSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120, 0, 0, 0, 0, 0, 0, 0, 18, 25, 163
+solr-8.3.1/server/solr-webapp/webapp/css/angular/replication.css, CSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 404, 0, 0, 0, 0, 0, 0, 0, 18, 79, 501
+solr-8.3.1/server/solr-webapp/webapp/css/angular/schema.css, CSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 596, 0, 0, 0, 0, 0, 0, 0, 20, 112, 728
+solr-8.3.1/server/solr-webapp/webapp/css/angular/segments.css, CSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 133, 0, 0, 0, 0, 0, 0, 0, 18, 22, 173
+solr-8.3.1/server/solr-webapp/webapp/css/angular/stream.css, CSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 178, 0, 0, 0, 0, 0, 0, 0, 22, 34, 234
+solr-8.3.1/server/solr-webapp/webapp/css/angular/suggestions.css, CSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 43, 0, 0, 0, 0, 0, 0, 0, 18, 4, 65
+solr-8.3.1/server/solr-webapp/webapp/css/angular/threads.css, CSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 119, 0, 0, 0, 0, 0, 0, 0, 18, 24, 161
+solr-8.3.1/server/solr-webapp/webapp/img/solr.svg, XML, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 39, 0, 0, 0, 0, 0, 1, 40
+solr-8.3.1/server/solr-webapp/webapp/index.html, HTML, 0, 0, 0, 0, 0, 0, 203, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 38, 257
+solr-8.3.1/server/solr-webapp/webapp/js/angular/app.js, JavaScript, 0, 512, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 31, 562
+solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/alias-overview.js, JavaScript, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 4, 28
+solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/analysis.js, JavaScript, 0, 161, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 23, 202
+solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/cloud.js, JavaScript, 0, 847, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 51, 124, 1022
+solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/cluster-suggestions.js, JavaScript, 0, 43, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 2, 63
+solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/collection-overview.js, JavaScript, 0, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 6, 40
+solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/collections.js, JavaScript, 0, 244, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 27, 290
+solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/core-overview.js, JavaScript, 0, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 9, 94
+solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/cores.js, JavaScript, 0, 151, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 14, 181
+solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/dataimport.js, JavaScript, 0, 234, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25, 44, 303
+solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/documents.js, JavaScript, 0, 107, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 13, 138
+solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/files.js, JavaScript, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 13, 101
+solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/index.js, JavaScript, 0, 61, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23, 14, 98
+solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/java-properties.js, JavaScript, 0, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 3, 46
+solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/logging.js, JavaScript, 0, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, 12, 159
+solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/login.js, JavaScript, 0, 269, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 19, 318
+solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/plugins.js, JavaScript, 0, 130, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 19, 168
+solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/query.js, JavaScript, 0, 88, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 14, 121
+solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/replication.js, JavaScript, 0, 178, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 40, 236
+solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/schema.js, JavaScript, 0, 524, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 69, 612
+solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/segments.js, JavaScript, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 20, 100
+solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/stream.js, JavaScript, 0, 173, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 51, 240
+solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/threads.js, JavaScript, 0, 33, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 2, 51
+solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/unknown.js, JavaScript, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 22, 2, 38
+solr-8.3.1/server/solr-webapp/webapp/js/angular/services.js, JavaScript, 0, 311, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 11, 340
+solr-8.3.1/server/solr-webapp/webapp/libs/angular-chosen.js, JavaScript, 0, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 4, 140
+solr-8.3.1/server/solr-webapp/webapp/libs/angular-cookies.js, JavaScript, 0, 73, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 135, 22, 230
+solr-8.3.1/server/solr-webapp/webapp/libs/angular-cookies.min.js, JavaScript, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, 1, 32
+solr-8.3.1/server/solr-webapp/webapp/libs/angular-resource.min.js, JavaScript, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, 1, 37
+solr-8.3.1/server/solr-webapp/webapp/libs/angular-route.js, JavaScript, 0, 316, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 636, 67, 1019
+solr-8.3.1/server/solr-webapp/webapp/libs/angular-route.min.js, JavaScript, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, 1, 39
+solr-8.3.1/server/solr-webapp/webapp/libs/angular-sanitize.js, JavaScript, 0, 311, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 328, 65, 704
+solr-8.3.1/server/solr-webapp/webapp/libs/angular-sanitize.min.js, JavaScript, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, 1, 40
+solr-8.3.1/server/solr-webapp/webapp/libs/angular-utf8-base64.js, JavaScript, 0, 157, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 13, 218
+solr-8.3.1/server/solr-webapp/webapp/libs/angular-utf8-base64.min.js, JavaScript, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 3, 46
+solr-8.3.1/server/solr-webapp/webapp/libs/angular.js, JavaScript, 0, 10845, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13211, 2038, 26094
+solr-8.3.1/server/solr-webapp/webapp/libs/angular.min.js, JavaScript, 0, 73, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 201, 0, 274
+solr-8.3.1/server/solr-webapp/webapp/libs/chosen.jquery.js, JavaScript, 0, 1151, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, 8, 1195
+solr-8.3.1/server/solr-webapp/webapp/libs/chosen.jquery.min.js, JavaScript, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, 0, 31
+solr-8.3.1/server/solr-webapp/webapp/libs/d3.js, JavaScript, 0, 7720, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 519, 1135, 9374
+solr-8.3.1/server/solr-webapp/webapp/libs/highlight.js, JavaScript, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, 1, 32
+solr-8.3.1/server/solr-webapp/webapp/libs/jquery-1.7.2.min.js, JavaScript, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 2, 31
+solr-8.3.1/server/solr-webapp/webapp/libs/jquery-2.1.3.min.js, JavaScript, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 1, 30
+solr-8.3.1/server/solr-webapp/webapp/libs/jquery-ui.min.js, JavaScript, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 3, 31
+solr-8.3.1/server/solr-webapp/webapp/libs/jquery.jstree.js, JavaScript, 0, 3222, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 228, 85, 3535
+solr-8.3.1/server/solr-webapp/webapp/libs/ngtimeago.js, JavaScript, 0, 66, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23, 13, 102
+solr-8.3.1/server/solr-webapp/webapp/partials/alias_overview.html, HTML, 0, 0, 0, 0, 0, 0, 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 10, 47
+solr-8.3.1/server/solr-webapp/webapp/partials/analysis.html, HTML, 0, 0, 0, 0, 0, 0, 87, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 26, 129
+solr-8.3.1/server/solr-webapp/webapp/partials/cloud.html, HTML, 0, 0, 0, 0, 0, 0, 263, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 24, 303
+solr-8.3.1/server/solr-webapp/webapp/partials/cluster_suggestions.html, HTML, 0, 0, 0, 0, 0, 0, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 4, 50
+solr-8.3.1/server/solr-webapp/webapp/partials/collection_overview.html, HTML, 0, 0, 0, 0, 0, 0, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 22, 86
+solr-8.3.1/server/solr-webapp/webapp/partials/collections.html, HTML, 0, 0, 0, 0, 0, 0, 301, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 79, 396
+solr-8.3.1/server/solr-webapp/webapp/partials/core_overview.html, HTML, 0, 0, 0, 0, 0, 0, 125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 66, 207
+solr-8.3.1/server/solr-webapp/webapp/partials/cores.html, HTML, 0, 0, 0, 0, 0, 0, 142, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 67, 225
+solr-8.3.1/server/solr-webapp/webapp/partials/dataimport.html, HTML, 0, 0, 0, 0, 0, 0, 142, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 52, 210
+solr-8.3.1/server/solr-webapp/webapp/partials/documents.html, HTML, 0, 0, 0, 0, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 9, 112
+solr-8.3.1/server/solr-webapp/webapp/partials/files.html, HTML, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 15, 48
+solr-8.3.1/server/solr-webapp/webapp/partials/index.html, HTML, 0, 0, 0, 0, 0, 0, 135, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 85, 262
+solr-8.3.1/server/solr-webapp/webapp/partials/java-properties.html, HTML, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 2, 28
+solr-8.3.1/server/solr-webapp/webapp/partials/logging-levels.html, HTML, 0, 0, 0, 0, 0, 0, 35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 6, 57
+solr-8.3.1/server/solr-webapp/webapp/partials/logging.html, HTML, 0, 0, 0, 0, 0, 0, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 2, 58
+solr-8.3.1/server/solr-webapp/webapp/partials/login.html, HTML, 0, 0, 0, 0, 0, 0, 134, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 11, 161
+solr-8.3.1/server/solr-webapp/webapp/partials/plugins.html, HTML, 0, 0, 0, 0, 0, 0, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 8, 73
+solr-8.3.1/server/solr-webapp/webapp/partials/query.html, HTML, 0, 0, 0, 0, 0, 0, 270, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 84, 370
+solr-8.3.1/server/solr-webapp/webapp/partials/replication.html, HTML, 0, 0, 0, 0, 0, 0, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 71, 240
+solr-8.3.1/server/solr-webapp/webapp/partials/schema.html, HTML, 0, 0, 0, 0, 0, 0, 336, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 104, 456
+solr-8.3.1/server/solr-webapp/webapp/partials/segments.html, HTML, 0, 0, 0, 0, 0, 0, 70, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 14, 100
+solr-8.3.1/server/solr-webapp/webapp/partials/stream.html, HTML, 0, 0, 0, 0, 0, 0, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 9, 65
+solr-8.3.1/server/solr-webapp/webapp/partials/threads.html, HTML, 0, 0, 0, 0, 0, 0, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 14, 66
+solr-8.3.1/server/solr-webapp/webapp/partials/unknown.html, HTML, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 3, 24
+solr-8.3.1/server/solr/configsets/_default/conf/params.json, JSON, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 21
+solr-8.3.1/server/solr/configsets/_default/conf/solrconfig.xml, XML, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 296, 0, 0, 0, 0, 976, 98, 1370
+solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/_rest_managed.json, JSON, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2
+solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/_schema_analysis_stopwords_english.json, JSON, 0, 0, 38, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 39
+solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/_schema_analysis_synonyms_english.json, JSON, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 12
+solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/clustering/carrot2/kmeans-attributes.xml, XML, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 0, 0, 0, 0, 6, 1, 20
+solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/clustering/carrot2/lingo-attributes.xml, XML, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 0, 0, 0, 0, 11, 1, 25
+solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/clustering/carrot2/stc-attributes.xml, XML, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 0, 0, 0, 0, 6, 1, 20
+solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/currency.xml, XML, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 45, 0, 0, 0, 0, 19, 4, 68
+solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/elevate.xml, XML, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 37, 3, 43
+solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/params.json, JSON, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 12
+solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/solrconfig.xml, XML, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 410, 0, 0, 0, 0, 1097, 124, 1631
+solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/update-script.js, JavaScript, 0, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 13, 54
+solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/velocity/jquery.autocomplete.css, CSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 0, 0, 0, 0, 0, 0, 0, 9, 6, 49
+solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/velocity/jquery.autocomplete.js, JavaScript, 0, 620, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 68, 76, 764
+solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/velocity/main.css, CSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 185, 0, 0, 0, 0, 0, 0, 0, 0, 47, 232
+solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/xslt/example.xsl, XSL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 98, 20, 15, 133
+solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/xslt/example_atom.xsl, XSL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 20, 8, 68
+solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/xslt/example_rss.xsl, XSL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 41, 20, 6, 67
+solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/xslt/luke.xsl, XSL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 301, 19, 18, 338
+solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/xslt/updateXml.xsl, XSL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, 25, 11, 71
+solr-8.3.1/server/solr/dash/conf/params.json, JSON, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20
+solr-8.3.1/server/solr/dash/conf/schema.xml, XML, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 0, 0, 4, 7, 62
+solr-8.3.1/server/solr/dash/conf/solrconfig.xml, XML, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 270, 0, 0, 0, 0, 962, 97, 1329
+solr-8.3.1/server/solr/dash/core.properties, Properties, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 2, 1, 7
+solr-8.3.1/server/solr/solr.xml, XML, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 0, 0, 0, 0, 25, 11, 57
+solr-8.3.1/server/solr/zoo.cfg, Properties, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 25, 4, 32
+solr-8.3.1/server/tmp/start_3204295554151338130.properties, Properties, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 2, 1, 12
+solr-8.3.1/server/tmp/start_5812170489311981381.properties, Properties, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 2, 1, 12
+solr-8.3.1/server/tmp/start_6476327636763392575.properties, Properties, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 2, 1, 12
+solr-8.3.1/server/tmp/start_7329004517204835686.properties, Properties, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 2, 1, 12
+solr-8.3.1/server/tmp/start_9067375725008958788.properties, Properties, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 2, 1, 12
+src/Utils.ts, TypeScript, 0, 0, 0, 0, 0, 441, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23, 81, 545
+src/client/ClientRecommender.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 1, 2, 12
+src/client/ClientRecommender.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 320, 0, 0, 0, 0, 0, 0, 61, 44, 425
+src/client/DocServer.ts, TypeScript, 0, 0, 0, 0, 0, 279, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 136, 65, 480
+src/client/Network.ts, TypeScript, 0, 0, 0, 0, 0, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 39
+src/client/apis/GoogleAuthenticationManager.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 3, 19
+src/client/apis/GoogleAuthenticationManager.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 115, 0, 0, 0, 0, 0, 0, 2, 11, 128
+src/client/apis/IBM_Recommender.ts, TypeScript, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 33, 7, 40
+src/client/apis/google_docs/GoogleApiClientUtils.ts, TypeScript, 0, 0, 0, 0, 0, 225, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 27, 261
+src/client/apis/google_docs/GooglePhotosClientUtils.ts, TypeScript, 0, 0, 0, 0, 0, 318, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 46, 364
+src/client/apis/youtube/YoutubeBox.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 105, 0, 0, 0, 0, 0, 5, 16, 126
+src/client/apis/youtube/YoutubeBox.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 274, 0, 0, 0, 0, 0, 0, 48, 40, 362
+src/client/cognitive_services/CognitiveServices.ts, TypeScript, 0, 0, 0, 0, 0, 342, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 57, 409
+src/client/documents/DocumentTypes.ts, TypeScript, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 37
+src/client/documents/Documents.ts, TypeScript, 0, 0, 0, 0, 0, 807, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 141, 87, 1035
+src/client/goldenLayout.d.ts, TypeScript, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3
+src/client/goldenLayout.js, JavaScript, 0, 3084, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1571, 720, 5375
+src/client/util/DictationManager.ts, TypeScript, 0, 0, 0, 0, 0, 312, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25, 51, 388
+src/client/util/DocumentManager.ts, TypeScript, 0, 0, 0, 0, 0, 207, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 21, 244
+src/client/util/DragManager.ts, TypeScript, 0, 0, 0, 0, 0, 504, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 35, 550
+src/client/util/DropConverter.ts, TypeScript, 0, 0, 0, 0, 0, 70, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 2, 78
+src/client/util/History.ts, TypeScript, 0, 0, 0, 0, 0, 166, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 27, 206
+src/client/util/Import & Export/DirectoryImportBox.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 6
+src/client/util/Import & Export/DirectoryImportBox.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 393, 0, 0, 0, 0, 0, 0, 0, 31, 424
+src/client/util/Import & Export/ImageUtils.ts, TypeScript, 0, 0, 0, 0, 0, 35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 39
+src/client/util/Import & Export/ImportMetadataEntry.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 132, 0, 0, 0, 0, 0, 0, 0, 17, 149
+src/client/util/InteractionUtils.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 122, 0, 0, 0, 0, 0, 0, 112, 21, 255
+src/client/util/KeyCodes.ts, TypeScript, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, 0, 136
+src/client/util/LinkManager.ts, TypeScript, 0, 0, 0, 0, 0, 161, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 23, 214
+src/client/util/ProsemirrorCopy/prompt.js, JavaScript, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 22, 180
+src/client/util/Scripting.ts, TypeScript, 0, 0, 0, 0, 0, 247, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 30, 292
+src/client/util/ScrollBox.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 2, 21
+src/client/util/SearchUtil.ts, TypeScript, 0, 0, 0, 0, 0, 123, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 18, 145
+src/client/util/SelectionManager.ts, TypeScript, 0, 0, 0, 0, 0, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 15, 89
+src/client/util/SerializationHelper.ts, TypeScript, 0, 0, 0, 0, 0, 113, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 15, 143
+src/client/util/SettingsManager.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 111, 0, 0, 0, 0, 0, 0, 25, 136
+src/client/util/SettingsManager.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 114, 0, 0, 0, 0, 0, 0, 0, 17, 131
+src/client/util/SharingManager.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 122, 0, 0, 0, 0, 0, 0, 18, 140
+src/client/util/SharingManager.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 273, 0, 0, 0, 0, 0, 0, 0, 25, 298
+src/client/util/Transform.ts, TypeScript, 0, 0, 0, 0, 0, 76, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23, 99
+src/client/util/TypedEvent.ts, TypeScript, 0, 0, 0, 0, 0, 29, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 8, 40
+src/client/util/UndoManager.ts, TypeScript, 0, 0, 0, 0, 0, 167, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 27, 195
+src/client/util/clamp.js, JavaScript, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 15
+src/client/util/convertToCSSPTValue.js, JavaScript, 0, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 8, 43
+src/client/util/jsx-decl.d.ts, TypeScript, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2
+src/client/util/request-image-size.js, JavaScript, 0, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 14, 75
+src/client/util/toCSSLineSpacing.js, JavaScript, 0, 35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 14, 64
+src/client/views/AntimodeMenu.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, 0, 0, 0, 0, 0, 1, 6, 42
+src/client/views/AntimodeMenu.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 121, 0, 0, 0, 0, 0, 0, 14, 22, 157
+src/client/views/ContextMenu.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 130, 0, 0, 0, 0, 0, 17, 14, 161
+src/client/views/ContextMenu.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 258, 0, 0, 0, 0, 0, 0, 3, 33, 294
+src/client/views/ContextMenuItem.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, 0, 0, 0, 10, 117
+src/client/views/DictationOverlay.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 7, 71
+src/client/views/DocComponent.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 87, 0, 0, 0, 0, 0, 0, 20, 15, 122
+src/client/views/DocumentButtonBar.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 89, 0, 0, 0, 0, 0, 0, 16, 105
+src/client/views/DocumentButtonBar.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 284, 0, 0, 0, 0, 0, 0, 4, 27, 315
+src/client/views/DocumentDecorations.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 319, 0, 0, 0, 0, 0, 0, 46, 365
+src/client/views/DocumentDecorations.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 479, 0, 0, 0, 0, 0, 0, 2, 26, 507
+src/client/views/EditableView.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 22, 0, 0, 0, 0, 0, 0, 3, 25
+src/client/views/EditableView.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 149, 0, 0, 0, 0, 0, 0, 19, 16, 184
+src/client/views/GestureOverlay.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 8, 64
+src/client/views/GestureOverlay.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 711, 0, 0, 0, 0, 0, 0, 45, 67, 823
+src/client/views/GlobalKeyHandler.ts, TypeScript, 0, 0, 0, 0, 0, 232, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 27, 269
+src/client/views/InkingControl.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 0, 0, 0, 0, 0, 4, 0, 131
+src/client/views/InkingControl.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 78, 0, 0, 0, 0, 0, 0, 3, 10, 91
+src/client/views/InkingStroke.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 7
+src/client/views/InkingStroke.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 63, 0, 0, 0, 0, 0, 0, 0, 5, 68
+src/client/views/KeyphraseQueryView.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 1, 8
+src/client/views/KeyphraseQueryView.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 0, 0, 0, 0, 0, 0, 2, 3, 35
+src/client/views/Main.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 0, 0, 0, 8, 10, 69
+src/client/views/Main.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 22, 0, 0, 0, 0, 0, 0, 1, 2, 25
+src/client/views/MainView.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 138, 0, 0, 0, 0, 0, 1, 21, 160
+src/client/views/MainView.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 553, 0, 0, 0, 0, 0, 0, 13, 36, 602
+src/client/views/MainViewModal.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 0, 0, 0, 0, 0, 0, 1, 25
+src/client/views/MainViewModal.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 39, 0, 0, 0, 0, 0, 0, 0, 5, 44
+src/client/views/MainViewNotifs.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 0, 0, 0, 1, 18
+src/client/views/MainViewNotifs.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, 0, 0, 0, 0, 0, 0, 0, 4, 33
+src/client/views/MetadataEntryMenu.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 79, 0, 0, 0, 0, 0, 0, 14, 93
+src/client/views/MetadataEntryMenu.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 207, 0, 0, 0, 0, 0, 0, 0, 16, 223
+src/client/views/OCRUtils.ts, TypeScript, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 8
+src/client/views/OverlayView.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 41, 0, 0, 0, 0, 0, 0, 6, 47
+src/client/views/OverlayView.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 194, 0, 0, 0, 0, 0, 0, 4, 19, 217
+src/client/views/Palette.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 0, 0, 0, 0, 0, 0, 4, 30
+src/client/views/Palette.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65, 0, 0, 0, 0, 0, 0, 0, 5, 70
+src/client/views/PreviewCursor.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 1, 10
+src/client/views/PreviewCursor.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 115, 0, 0, 0, 0, 0, 0, 8, 9, 132
+src/client/views/RecommendationsBox.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 10, 8, 70
+src/client/views/RecommendationsBox.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 116, 0, 0, 0, 0, 0, 0, 67, 17, 200
+src/client/views/ScriptBox.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 0, 0, 0, 2, 17
+src/client/views/ScriptBox.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 112, 0, 0, 0, 0, 0, 0, 2, 12, 126
+src/client/views/ScriptingRepl.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 0, 0, 0, 0, 0, 0, 9, 51
+src/client/views/ScriptingRepl.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 220, 0, 0, 0, 0, 0, 0, 1, 24, 245
+src/client/views/SearchDocBox.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 359, 0, 0, 0, 0, 0, 0, 17, 55, 431
+src/client/views/TemplateMenu.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 5, 51
+src/client/views/TemplateMenu.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 167, 0, 0, 0, 0, 0, 0, 1, 14, 182
+src/client/views/Templates.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 0, 0, 0, 0, 0, 0, 0, 8, 42
+src/client/views/TouchScrollableMenu.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 7, 59
+src/client/views/Touchable.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 172, 0, 0, 0, 0, 0, 0, 28, 39, 239
+src/client/views/_nodeModuleOverrides.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 9, 4, 22
+src/client/views/animationtimeline/Keyframe.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 83, 0, 0, 0, 0, 0, 5, 17, 105
+src/client/views/animationtimeline/Keyframe.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 468, 0, 0, 0, 0, 0, 0, 50, 42, 560
+src/client/views/animationtimeline/Timeline.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 264, 0, 0, 0, 0, 0, 14, 44, 322
+src/client/views/animationtimeline/Timeline.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 467, 0, 0, 0, 0, 0, 0, 97, 58, 622
+src/client/views/animationtimeline/TimelineMenu.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 75, 0, 0, 0, 0, 0, 2, 17, 94
+src/client/views/animationtimeline/TimelineMenu.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 10, 78
+src/client/views/animationtimeline/TimelineOverview.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 89, 0, 0, 0, 0, 0, 6, 12, 107
+src/client/views/animationtimeline/TimelineOverview.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 155, 0, 0, 0, 0, 0, 0, 0, 27, 182
+src/client/views/animationtimeline/Track.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 0, 0, 0, 0, 0, 0, 2, 15
+src/client/views/animationtimeline/Track.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 284, 0, 0, 0, 0, 0, 0, 63, 33, 380
+src/client/views/collections/CollectionCarouselView.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 37, 0, 0, 0, 0, 0, 0, 1, 38
+src/client/views/collections/CollectionCarouselView.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 110, 0, 0, 0, 0, 0, 0, 1, 10, 121
+src/client/views/collections/CollectionDockingView.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 391, 0, 0, 0, 0, 0, 7, 60, 458
+src/client/views/collections/CollectionDockingView.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 707, 0, 0, 0, 0, 0, 0, 61, 65, 833
+src/client/views/collections/CollectionLinearView.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 10, 78
+src/client/views/collections/CollectionLinearView.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 125, 0, 0, 0, 0, 0, 0, 1, 9, 135
+src/client/views/collections/CollectionMapView.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 27, 0, 0, 0, 0, 0, 0, 3, 30
+src/client/views/collections/CollectionMapView.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 235, 0, 0, 0, 0, 0, 0, 10, 18, 263
+src/client/views/collections/CollectionMasonryViewFieldRow.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 308, 0, 0, 0, 0, 0, 0, 0, 24, 332
+src/client/views/collections/CollectionPileView.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 1, 9
+src/client/views/collections/CollectionPileView.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 112, 0, 0, 0, 0, 0, 0, 5, 11, 128
+src/client/views/collections/CollectionSchemaCells.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 274, 0, 0, 0, 0, 0, 0, 17, 38, 329
+src/client/views/collections/CollectionSchemaHeaders.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 318, 0, 0, 0, 0, 0, 0, 5, 41, 364
+src/client/views/collections/CollectionSchemaMovableTableHOC.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 216, 0, 0, 0, 0, 0, 0, 0, 27, 243
+src/client/views/collections/CollectionSchemaView.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 406, 0, 0, 0, 0, 0, 4, 88, 498
+src/client/views/collections/CollectionSchemaView.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 651, 0, 0, 0, 0, 0, 0, 20, 82, 753
+src/client/views/collections/CollectionStackingView.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 353, 0, 0, 0, 0, 0, 1, 50, 404
+src/client/views/collections/CollectionStackingView.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 430, 0, 0, 0, 0, 0, 0, 5, 25, 460
+src/client/views/collections/CollectionStackingViewFieldColumn.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 367, 0, 0, 0, 0, 0, 0, 0, 27, 394
+src/client/views/collections/CollectionStaffView.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 1, 13
+src/client/views/collections/CollectionStaffView.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 7, 53
+src/client/views/collections/CollectionSubView.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 390, 0, 0, 0, 0, 0, 0, 7, 25, 422
+src/client/views/collections/CollectionTimeView.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 80, 0, 0, 0, 0, 0, 0, 13, 93
+src/client/views/collections/CollectionTimeView.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 177, 0, 0, 0, 0, 0, 0, 0, 15, 192
+src/client/views/collections/CollectionTreeView.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 125, 0, 0, 0, 0, 0, 4, 23, 152
+src/client/views/collections/CollectionTreeView.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 801, 0, 0, 0, 0, 0, 0, 19, 40, 860
+src/client/views/collections/CollectionView.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 0, 0, 0, 0, 0, 8, 78
+src/client/views/collections/CollectionView.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 457, 0, 0, 0, 0, 0, 0, 13, 34, 504
+src/client/views/collections/CollectionViewChromes.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 308, 0, 0, 0, 0, 0, 4, 45, 357
+src/client/views/collections/CollectionViewChromes.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 393, 0, 0, 0, 0, 0, 0, 67, 47, 507
+src/client/views/collections/KeyRestrictionRow.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 49, 0, 0, 0, 0, 0, 0, 2, 4, 55
+src/client/views/collections/ParentDocumentSelector.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 54, 0, 0, 0, 0, 0, 0, 2, 56
+src/client/views/collections/ParentDocumentSelector.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120, 0, 0, 0, 0, 0, 0, 0, 11, 131
+src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 422, 0, 0, 0, 0, 0, 0, 10, 27, 459
+src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 1, 20
+src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 110, 0, 0, 0, 0, 0, 0, 5, 4, 119
+src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 11
+src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 44, 0, 0, 0, 0, 0, 0, 0, 2, 46
+src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 0, 0, 1, 3, 24
+src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 0, 0, 0, 0, 0, 0, 0, 12, 79
+src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 95, 0, 0, 0, 0, 0, 9, 17, 121
+src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1186, 0, 0, 0, 0, 0, 0, 47, 97, 1330
+src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 5, 57
+src/client/views/collections/collectionFreeForm/MarqueeView.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 0, 0, 0, 0, 0, 0, 2, 32
+src/client/views/collections/collectionFreeForm/MarqueeView.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 484, 0, 0, 0, 0, 0, 0, 105, 33, 622
+src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 0, 0, 0, 0, 0, 0, 6, 34
+src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 202, 0, 0, 0, 0, 0, 0, 72, 23, 297
+src/client/views/collections/collectionMulticolumn/CollectionMultirowView.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, 0, 0, 0, 0, 0, 0, 6, 35
+src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 204, 0, 0, 0, 0, 0, 0, 72, 22, 298
+src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 94, 0, 0, 0, 0, 0, 0, 0, 9, 103
+src/client/views/collections/collectionMulticolumn/MulticolumnWidthLabel.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 0, 0, 0, 0, 0, 5, 56
+src/client/views/collections/collectionMulticolumn/MultirowHeightLabel.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 0, 0, 0, 0, 0, 5, 56
+src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 92, 0, 0, 0, 0, 0, 0, 0, 9, 101
+src/client/views/globalCssVariables.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 0, 0, 0, 0, 0, 10, 3, 43
+src/client/views/globalCssVariables.scss.d.ts, TypeScript, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 11
+src/client/views/linking/LinkEditor.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 124, 0, 0, 0, 0, 0, 1, 25, 150
+src/client/views/linking/LinkEditor.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 252, 0, 0, 0, 0, 0, 0, 7, 50, 309
+src/client/views/linking/LinkMenu.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 0, 0, 0, 0, 0, 0, 13, 53
+src/client/views/linking/LinkMenu.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65, 0, 0, 0, 0, 0, 0, 1, 10, 76
+src/client/views/linking/LinkMenuGroup.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, 0, 0, 0, 0, 0, 0, 0, 10, 94
+src/client/views/linking/LinkMenuItem.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 75, 0, 0, 0, 0, 0, 1, 11, 87
+src/client/views/linking/LinkMenuItem.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, 0, 0, 0, 19, 126
+src/client/views/nodes/AudioBox.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 146, 0, 0, 0, 0, 0, 0, 0, 146
+src/client/views/nodes/AudioBox.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 261, 0, 0, 0, 0, 0, 0, 2, 24, 287
+src/client/views/nodes/CollectionFreeFormDocumentView.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 8
+src/client/views/nodes/CollectionFreeFormDocumentView.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 124, 0, 0, 0, 0, 0, 0, 0, 7, 131
+src/client/views/nodes/ColorBox.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 22, 0, 0, 0, 0, 0, 0, 1, 23
+src/client/views/nodes/ColorBox.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 0, 0, 0, 0, 0, 0, 0, 4, 32
+src/client/views/nodes/ContentFittingDocumentView.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0, 4, 24
+src/client/views/nodes/ContentFittingDocumentView.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 117, 0, 0, 0, 0, 0, 0, 0, 7, 124
+src/client/views/nodes/DocumentBox.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 14
+src/client/views/nodes/DocumentBox.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 154, 0, 0, 0, 0, 0, 0, 0, 4, 158
+src/client/views/nodes/DocumentContentsView.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 183, 0, 0, 0, 0, 0, 0, 10, 17, 210
+src/client/views/nodes/DocumentIcon.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 60, 0, 0, 0, 0, 0, 0, 0, 5, 65
+src/client/views/nodes/DocumentView.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 110, 0, 0, 0, 0, 0, 1, 15, 126
+src/client/views/nodes/DocumentView.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1041, 0, 0, 0, 0, 0, 0, 54, 94, 1189
+src/client/views/nodes/FaceRectangle.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25, 0, 0, 0, 0, 0, 0, 0, 4, 29
+src/client/views/nodes/FaceRectangles.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 41, 0, 0, 0, 0, 0, 0, 0, 5, 46
+src/client/views/nodes/FieldTextBox.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 3, 15
+src/client/views/nodes/FieldView.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 89, 0, 0, 0, 0, 0, 0, 43, 4, 136
+src/client/views/nodes/FontIconBox.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25, 0, 0, 0, 0, 0, 0, 2, 27
+src/client/views/nodes/FontIconBox.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 58, 0, 0, 0, 0, 0, 0, 0, 5, 63
+src/client/views/nodes/ImageBox.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 135, 0, 0, 0, 0, 0, 0, 17, 152
+src/client/views/nodes/ImageBox.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 430, 0, 0, 0, 0, 0, 0, 10, 35, 475
+src/client/views/nodes/KeyValueBox.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120, 0, 0, 0, 0, 0, 0, 3, 123
+src/client/views/nodes/KeyValueBox.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 244, 0, 0, 0, 0, 0, 0, 1, 26, 271
+src/client/views/nodes/KeyValuePair.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 0, 0, 0, 0, 0, 1, 4, 60
+src/client/views/nodes/KeyValuePair.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 125, 0, 0, 0, 0, 0, 0, 2, 8, 135
+src/client/views/nodes/LabelBox.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 31, 0, 0, 0, 0, 0, 0, 4, 35
+src/client/views/nodes/LabelBox.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 0, 0, 0, 0, 0, 0, 1, 9, 96
+src/client/views/nodes/LinkAnchorBox.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 27, 0, 0, 0, 0, 0, 0, 2, 29
+src/client/views/nodes/LinkAnchorBox.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 141, 0, 0, 0, 0, 0, 0, 0, 9, 150
+src/client/views/nodes/LinkBox.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 3
+src/client/views/nodes/LinkBox.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, 0, 3, 36
+src/client/views/nodes/PDFBox.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 200, 0, 0, 0, 0, 0, 0, 19, 219
+src/client/views/nodes/PDFBox.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 246, 0, 0, 0, 0, 0, 0, 0, 19, 265
+src/client/views/nodes/PresBox.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 1, 53
+src/client/views/nodes/PresBox.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 268, 0, 0, 0, 0, 0, 0, 31, 32, 331
+src/client/views/nodes/QueryBox.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 5
+src/client/views/nodes/QueryBox.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 37, 0, 0, 0, 0, 0, 0, 0, 4, 41
+src/client/views/nodes/RadialMenu.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 60, 0, 0, 0, 0, 0, 3, 7, 70
+src/client/views/nodes/RadialMenu.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 174, 0, 0, 0, 0, 0, 0, 26, 36, 236
+src/client/views/nodes/RadialMenuItem.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, 0, 0, 16, 117
+src/client/views/nodes/ScreenshotBox.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 0, 0, 0, 0, 0, 6, 5, 51
+src/client/views/nodes/ScreenshotBox.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 174, 0, 0, 0, 0, 0, 0, 2, 18, 194
+src/client/views/nodes/ScriptingBox.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, 3, 36
+src/client/views/nodes/ScriptingBox.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 87, 0, 0, 0, 0, 0, 0, 0, 12, 99
+src/client/views/nodes/SliderBox-components.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 220, 0, 0, 0, 0, 0, 0, 12, 25, 257
+src/client/views/nodes/SliderBox-tooltip.css, CSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 0, 0, 0, 0, 0, 0, 0, 0, 3, 33
+src/client/views/nodes/SliderBox.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 7
+src/client/views/nodes/SliderBox.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 117, 0, 0, 0, 0, 0, 0, 0, 8, 125
+src/client/views/nodes/VideoBox.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65, 0, 0, 0, 0, 0, 3, 6, 74
+src/client/views/nodes/VideoBox.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 343, 0, 0, 0, 0, 0, 0, 2, 34, 379
+src/client/views/nodes/WebBox.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 109, 0, 0, 0, 0, 0, 0, 18, 127
+src/client/views/nodes/WebBox.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 345, 0, 0, 0, 0, 0, 0, 15, 35, 395
+src/client/views/nodes/formattedText/DashDocCommentView.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 82, 0, 0, 0, 0, 0, 0, 0, 13, 95
+src/client/views/nodes/formattedText/DashDocView.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 223, 0, 0, 0, 0, 0, 0, 9, 37, 269
+src/client/views/nodes/formattedText/DashFieldView.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 0, 0, 0, 0, 0, 0, 2, 36
+src/client/views/nodes/formattedText/DashFieldView.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 178, 0, 0, 0, 0, 0, 0, 13, 20, 211
+src/client/views/nodes/formattedText/FootnoteView.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 131, 0, 0, 0, 0, 0, 0, 13, 19, 163
+src/client/views/nodes/formattedText/FormattedTextBox.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 220, 0, 0, 0, 0, 0, 11, 34, 265
+src/client/views/nodes/formattedText/FormattedTextBox.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1169, 0, 0, 0, 0, 0, 0, 68, 93, 1330
+src/client/views/nodes/formattedText/FormattedTextBoxComment.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, 0, 33
+src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 218, 0, 0, 0, 0, 0, 0, 11, 8, 237
+src/client/views/nodes/formattedText/ImageResizeView.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 113, 0, 0, 0, 0, 0, 0, 0, 25, 138
+src/client/views/nodes/formattedText/ParagraphNodeSpec.ts, TypeScript, 0, 0, 0, 0, 0, 108, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 26, 143
+src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts, TypeScript, 0, 0, 0, 0, 0, 231, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 242
+src/client/views/nodes/formattedText/RichTextMenu.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 2, 19, 121
+src/client/views/nodes/formattedText/RichTextMenu.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 754, 0, 0, 0, 0, 0, 0, 12, 109, 875
+src/client/views/nodes/formattedText/RichTextRules.ts, TypeScript, 0, 0, 0, 0, 0, 308, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 5, 320
+src/client/views/nodes/formattedText/RichTextSchema.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 465, 0, 0, 0, 0, 0, 0, 33, 39, 537
+src/client/views/nodes/formattedText/SummaryView.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 0, 0, 0, 0, 0, 0, 0, 14, 81
+src/client/views/nodes/formattedText/TooltipTextMenu.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 306, 0, 0, 0, 0, 0, 6, 61, 373
+src/client/views/nodes/formattedText/marks_rts.ts, TypeScript, 0, 0, 0, 0, 0, 259, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 23, 297
+src/client/views/nodes/formattedText/nodes_rts.ts, TypeScript, 0, 0, 0, 0, 0, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 19, 264
+src/client/views/nodes/formattedText/prosemirrorPatches.js, JavaScript, 0, 118, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 9, 139
+src/client/views/nodes/formattedText/schema_rts.ts, TypeScript, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 6, 26
+src/client/views/pdf/Annotation.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 6
+src/client/views/pdf/Annotation.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 114, 0, 0, 0, 0, 0, 0, 0, 16, 130
+src/client/views/pdf/PDFMenu.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 6
+src/client/views/pdf/PDFMenu.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 102, 0, 0, 0, 0, 0, 0, 0, 21, 123
+src/client/views/pdf/PDFViewer.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 74, 0, 0, 0, 0, 0, 4, 10, 88
+src/client/views/pdf/PDFViewer.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 662, 0, 0, 0, 0, 0, 0, 23, 46, 731
+src/client/views/presentationview/PresElementBox.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 0, 10, 103
+src/client/views/presentationview/PresElementBox.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 179, 0, 0, 0, 0, 0, 0, 31, 14, 224
+src/client/views/search/CheckBox.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 1, 8, 59
+src/client/views/search/CheckBox.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 0, 0, 0, 0, 0, 0, 74, 15, 131
+src/client/views/search/CollectionFilters.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 0, 0, 0, 3, 20
+src/client/views/search/CollectionFilters.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 69, 0, 0, 0, 0, 0, 0, 0, 14, 83
+src/client/views/search/FieldFilters.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 1, 1, 12
+src/client/views/search/FieldFilters.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 0, 0, 0, 0, 0, 0, 0, 7, 41
+src/client/views/search/FilterBox.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 153, 0, 0, 0, 0, 0, 0, 25, 178
+src/client/views/search/FilterBox.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 354, 0, 0, 0, 0, 0, 0, 24, 54, 432
+src/client/views/search/IconBar.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 1, 10
+src/client/views/search/IconBar.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 69, 0, 0, 0, 0, 0, 0, 1, 17, 87
+src/client/views/search/IconButton.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 46, 0, 0, 0, 0, 0, 1, 6, 53
+src/client/views/search/IconButton.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 170, 0, 0, 0, 0, 0, 0, 2, 19, 191
+src/client/views/search/NaviconButton.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 58, 0, 0, 0, 0, 0, 0, 11, 69
+src/client/views/search/NaviconButton.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 5, 37
+src/client/views/search/SearchBox.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 203, 0, 0, 0, 0, 0, 82, 51, 336
+src/client/views/search/SearchBox.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 530, 0, 0, 0, 0, 0, 0, 47, 94, 671
+src/client/views/search/SearchItem.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 138, 0, 0, 0, 0, 0, 0, 25, 163
+src/client/views/search/SearchItem.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 272, 0, 0, 0, 0, 0, 0, 2, 29, 303
+src/client/views/search/SelectorContextMenu.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 1, 3, 16
+src/client/views/search/ToggleBar.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, 0, 0, 0, 0, 0, 2, 4, 41
+src/client/views/search/ToggleBar.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 77, 0, 0, 0, 0, 0, 0, 0, 9, 86
+src/client/views/webcam/DashWebRTCVideo.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 0, 0, 0, 0, 4, 9, 83
+src/client/views/webcam/DashWebRTCVideo.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 0, 0, 0, 0, 0, 0, 6, 16, 89
+src/client/views/webcam/WebCamLogic.js, JavaScript, 0, 234, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 51, 292
+src/debug/Repl.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 59, 0, 0, 0, 0, 0, 0, 0, 7, 66
+src/debug/Test.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 2, 14
+src/debug/Viewer.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 173, 0, 0, 0, 0, 0, 0, 0, 19, 192
+src/extensions/ArrayExtensions.ts, TypeScript, 0, 0, 0, 0, 0, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 6, 37
+src/extensions/General/Extensions.ts, TypeScript, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 9
+src/extensions/General/ExtensionsTypings.ts, TypeScript, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 8
+src/extensions/StringExtensions.ts, TypeScript, 0, 0, 0, 0, 0, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 17
+src/mobile/ImageUpload.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 0, 0, 0, 0, 0, 0, 4, 34
+src/mobile/ImageUpload.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 78, 0, 0, 0, 0, 0, 0, 41, 12, 131
+src/mobile/InkControls.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1
+src/mobile/MobileInkOverlay.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 33, 0, 0, 0, 0, 0, 1, 5, 39
+src/mobile/MobileInkOverlay.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 162, 0, 0, 0, 0, 0, 0, 3, 26, 191
+src/mobile/MobileInterface.scss, SCSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 0, 0, 0, 2, 19
+src/mobile/MobileInterface.tsx, TypeScript React, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 297, 0, 0, 0, 0, 0, 0, 15, 32, 344
+src/new_fields/CursorField.ts, TypeScript, 0, 0, 0, 0, 0, 54, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 66
+src/new_fields/DateField.ts, TypeScript, 0, 0, 0, 0, 0, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 37
+src/new_fields/Doc.ts, TypeScript, 0, 0, 0, 0, 0, 897, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 85, 76, 1058
+src/new_fields/FieldSymbols.ts, TypeScript, 0, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 13
+src/new_fields/HtmlField.ts, TypeScript, 0, 0, 0, 0, 0, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 27
+src/new_fields/IconField.ts, TypeScript, 0, 0, 0, 0, 0, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 27
+src/new_fields/InkField.ts, TypeScript, 0, 0, 0, 0, 0, 41, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 51
+src/new_fields/List.ts, TypeScript, 0, 0, 0, 0, 0, 244, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 38, 20, 302
+src/new_fields/ListSpec.ts, TypeScript, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1
+src/new_fields/ObjectField.ts, TypeScript, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 20
+src/new_fields/PresField.ts, TypeScript, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 6
+src/new_fields/Proxy.ts, TypeScript, 0, 0, 0, 0, 0, 95, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 14, 111
+src/new_fields/RefField.ts, TypeScript, 0, 0, 0, 0, 0, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 22
+src/new_fields/RichTextField.ts, TypeScript, 0, 0, 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 41
+src/new_fields/RichTextUtils.ts, TypeScript, 0, 0, 0, 0, 0, 455, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 56, 519
+src/new_fields/Schema.ts, TypeScript, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 8, 120
+src/new_fields/SchemaHeaderField.ts, TypeScript, 0, 0, 0, 0, 0, 104, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 14, 122
+src/new_fields/ScriptField.ts, TypeScript, 0, 0, 0, 0, 0, 137, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 19, 177
+src/new_fields/Types.ts, TypeScript, 0, 0, 0, 0, 0, 86, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 17, 108
+src/new_fields/URLField.ts, TypeScript, 0, 0, 0, 0, 0, 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 54
+src/new_fields/documentSchemas.ts, TypeScript, 0, 0, 0, 0, 0, 87, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 93
+src/new_fields/util.ts, TypeScript, 0, 0, 0, 0, 0, 176, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 15, 194
+src/pen-gestures/GestureUtils.ts, TypeScript, 0, 0, 0, 0, 0, 41, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 46
+src/pen-gestures/ndollar.ts, TypeScript, 0, 0, 0, 0, 0, 356, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 172, 22, 550
+src/scraping/acm/.gitignore, Ignore, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 2
+src/scraping/acm/debug.log, log, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 38, 0, 0, 1, 39
+src/scraping/acm/index.js, JavaScript, 0, 82, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 185, 13, 280
+src/scraping/acm/package.json, JSON, 0, 0, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 18
+src/scraping/buxton/.idea/buxton.iml, XML, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 8
+src/scraping/buxton/.idea/inspectionProfiles/profiles_settings.xml, XML, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 6
+src/scraping/buxton/.idea/misc.xml, XML, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 4
+src/scraping/buxton/.idea/modules.xml, XML, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 8
+src/scraping/buxton/.idea/vcs.xml, XML, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 6
+src/scraping/buxton/.idea/workspace.xml, XML, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 173, 0, 0, 0, 0, 0, 0, 173
+src/scraping/buxton/final/BuxtonImporter.ts, TypeScript, 0, 0, 0, 0, 0, 228, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 142, 26, 396
+src/scraping/buxton/jsonifier.py, Python, 0, 0, 0, 0, 183, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 48, 232
+src/scraping/buxton/narratives.py, Python, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 9, 39
+src/scraping/buxton/narratives/chord_keyboards.json, JSON, 0, 0, 39, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 39
+src/scraping/buxton/scraper.py, Python, 0, 0, 0, 0, 350, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 78, 433
+src/server/ActionUtilities.ts, TypeScript, 0, 0, 0, 0, 0, 136, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 23, 160
+src/server/ApiManagers/ApiManager.ts, TypeScript, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 11
+src/server/ApiManagers/DeleteManager.ts, TypeScript, 0, 0, 0, 0, 0, 71, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 82
+src/server/ApiManagers/DownloadManager.ts, TypeScript, 0, 0, 0, 0, 0, 173, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 80, 16, 269
+src/server/ApiManagers/GeneralGoogleManager.ts, TypeScript, 0, 0, 0, 0, 0, 53, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 61
+src/server/ApiManagers/GooglePhotosManager.ts, TypeScript, 0, 0, 0, 0, 0, 190, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 119, 22, 331
+src/server/ApiManagers/PDFManager.ts, TypeScript, 0, 0, 0, 0, 0, 103, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 115
+src/server/ApiManagers/SearchManager.ts, TypeScript, 0, 0, 0, 0, 0, 200, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 215
+src/server/ApiManagers/SessionManager.ts, TypeScript, 0, 0, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 67
+src/server/ApiManagers/UploadManager.ts, TypeScript, 0, 0, 0, 0, 0, 226, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 20, 247
+src/server/ApiManagers/UserManager.ts, TypeScript, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 21, 126
+src/server/ApiManagers/UtilManager.ts, TypeScript, 0, 0, 0, 0, 0, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 22, 10, 74
+src/server/Client.ts, TypeScript, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 11
+src/server/DashSession/DashSessionAgent.ts, TypeScript, 0, 0, 0, 0, 0, 155, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 23, 230
+src/server/DashSession/Session/agents/applied_session_agent.ts, TypeScript, 0, 0, 0, 0, 0, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 9, 58
+src/server/DashSession/Session/agents/monitor.ts, TypeScript, 0, 0, 0, 0, 0, 213, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 59, 26, 298
+src/server/DashSession/Session/agents/process_message_router.ts, TypeScript, 0, 0, 0, 0, 0, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 7, 41
+src/server/DashSession/Session/agents/promisified_ipc_manager.ts, TypeScript, 0, 0, 0, 0, 0, 106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 15, 173
+src/server/DashSession/Session/agents/server_worker.ts, TypeScript, 0, 0, 0, 0, 0, 99, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 46, 15, 160
+src/server/DashSession/Session/utilities/repl.ts, TypeScript, 0, 0, 0, 0, 0, 116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 128
+src/server/DashSession/Session/utilities/session_config.ts, TypeScript, 0, 0, 0, 0, 0, 119, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 129
+src/server/DashSession/Session/utilities/utilities.ts, TypeScript, 0, 0, 0, 0, 0, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 5, 37
+src/server/DashUploadUtils.ts, TypeScript, 0, 0, 0, 0, 0, 285, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 30, 367
+src/server/GarbageCollector.ts, TypeScript, 0, 0, 0, 0, 0, 138, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 11, 151
+src/server/IDatabase.ts, TypeScript, 0, 0, 0, 0, 0, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 25
+src/server/MemoryDatabase.ts, TypeScript, 0, 0, 0, 0, 0, 87, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 101
+src/server/Message.ts, TypeScript, 0, 0, 0, 0, 0, 86, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 104
+src/server/PdfTypes.ts, TypeScript, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 21
+src/server/ProcessFactory.ts, TypeScript, 0, 0, 0, 0, 0, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 44
+src/server/Recommender.ts, TypeScript, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120, 18, 138
+src/server/RouteManager.ts, TypeScript, 0, 0, 0, 0, 0, 187, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 19, 210
+src/server/RouteSubscriber.ts, TypeScript, 0, 0, 0, 0, 0, 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 26
+src/server/Search.ts, TypeScript, 0, 0, 0, 0, 0, 71, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 8, 81
+src/server/SharedMediaTypes.ts, TypeScript, 0, 0, 0, 0, 0, 41, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 51
+src/server/Websocket/Websocket.ts, TypeScript, 0, 0, 0, 0, 0, 263, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 46, 314
+src/server/apis/google/GoogleApiServerUtils.ts, TypeScript, 0, 0, 0, 0, 0, 172, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 168, 25, 365
+src/server/apis/google/SharedTypes.ts, TypeScript, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 21
+src/server/apis/youtube/youtubeApiSample.d.ts, TypeScript, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2
+src/server/apis/youtube/youtubeApiSample.js, JavaScript, 0, 135, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 14, 179
+src/server/authentication/config/passport.ts, TypeScript, 0, 0, 0, 0, 0, 23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 29
+src/server/authentication/controllers/user_controller.ts, TypeScript, 0, 0, 0, 0, 0, 218, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25, 25, 268
+src/server/authentication/models/current_user_utils.ts, TypeScript, 0, 0, 0, 0, 0, 586, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 57, 673
+src/server/authentication/models/user_model.ts, TypeScript, 0, 0, 0, 0, 0, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 14, 86
+src/server/credentials/CredentialsLoader.ts, TypeScript, 0, 0, 0, 0, 0, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 30
+src/server/credentials/google_project_credentials.json, JSON, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11
+src/server/database.ts, TypeScript, 0, 0, 0, 0, 0, 312, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 38, 350
+src/server/downsize.ts, TypeScript, 0, 0, 0, 0, 0, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 1, 40
+src/server/index.ts, TypeScript, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, 16, 159
+src/server/remapUrl.ts, TypeScript, 0, 0, 0, 0, 0, 53, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 6, 63
+src/server/server_Initialization.ts, TypeScript, 0, 0, 0, 0, 0, 138, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 24, 168
+src/server/slides.json, JSON, 0, 0, 10820, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10820
+src/server/updateProtos.ts, TypeScript, 0, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 14
+src/typings/index.d.ts, TypeScript, 0, 0, 0, 0, 0, 219, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 72, 38, 329
+test/test.ts, TypeScript, 0, 0, 0, 0, 0, 141, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 161
+tsconfig.json, JSON, 0, 0, 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 26
+tslint.json, JSON, 0, 0, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 1, 63
+views/forgot.pug, Pug, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 22
+views/layout.pug, Pug, 0, 0, 0, 0, 0, 0, 0, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 14
+views/login.pug, Pug, 0, 0, 0, 0, 0, 0, 0, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 26
+views/reset.pug, Pug, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 22
+views/signup.pug, Pug, 0, 0, 0, 0, 0, 0, 0, 25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 27
+views/stylesheets/authentication.css, CSS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 185, 0, 0, 0, 0, 0, 0, 0, 4, 34, 223
+views/user_activity.pug, Pug, 0, 0, 0, 0, 0, 0, 0, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 19
+webpack.config.js, JavaScript, 0, 116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 121
+Total, -, 6, 89717, 50401, 1893, 632, 15246, 2883, 119, 538, 5982, 30250, 7726, 17257, 161, 2, 38, 2060, 32987, 15880, 273778
\ No newline at end of file
diff --git a/.VSCodeCounter/results.md b/.VSCodeCounter/results.md
new file mode 100644
index 000000000..3265d800b
--- /dev/null
+++ b/.VSCodeCounter/results.md
@@ -0,0 +1,164 @@
+# Summary
+
+Date : 2020-04-30 14:40:17
+
+Directory /Users/bcz/Documents/GitHub/Dash-Web
+
+Total : 646 files,  224911 codes, 32987 comments, 15880 blanks, all 273778 lines
+
+[details](details.md)
+
+## Languages
+| language | files | code | comment | blank | total |
+| :--- | ---: | ---: | ---: | ---: | ---: |
+| JavaScript | 69 | 89,717 | 18,520 | 5,866 | 114,103 |
+| JSON | 20 | 50,401 | 37 | 14 | 50,452 |
+| TypeScript React | 140 | 30,250 | 1,878 | 3,154 | 35,282 |
+| XML | 73 | 17,257 | 8,281 | 992 | 26,530 |
+| TypeScript | 115 | 15,246 | 2,095 | 1,958 | 19,299 |
+| SCSS | 104 | 7,726 | 255 | 1,136 | 9,117 |
+| CSS | 30 | 5,982 | 549 | 1,095 | 7,626 |
+| HTML | 34 | 2,883 | 431 | 848 | 4,162 |
+| XSL | 20 | 2,060 | 416 | 232 | 2,708 |
+| Batch | 5 | 1,893 | 184 | 273 | 2,350 |
+| Python | 5 | 632 | 49 | 148 | 829 |
+| Shell Script | 6 | 538 | 248 | 120 | 906 |
+| Properties | 16 | 161 | 43 | 30 | 234 |
+| Pug | 6 | 119 | 1 | 10 | 130 |
+| log | 1 | 38 | 0 | 1 | 39 |
+| Markdown | 1 | 6 | 0 | 3 | 9 |
+| Ignore | 1 | 2 | 0 | 0 | 2 |
+
+## Directories
+| path | files | code | comment | blank | total |
+| :--- | ---: | ---: | ---: | ---: | ---: |
+| . | 646 | 224,911 | 32,987 | 15,880 | 273,778 |
+| build | 1 | 9 | 0 | 3 | 12 |
+| deploy | 8 | 55,744 | 174 | 704 | 56,622 |
+| deploy/assets | 2 | 55,677 | 174 | 686 | 56,537 |
+| deploy/debug | 3 | 32 | 0 | 9 | 41 |
+| deploy/mobile | 2 | 22 | 0 | 6 | 28 |
+| solr-8.3.1 | 236 | 80,866 | 26,654 | 7,861 | 115,381 |
+| solr-8.3.1/bin | 5 | 2,118 | 392 | 311 | 2,821 |
+| solr-8.3.1/contrib | 3 | 6,281 | 63 | 37 | 6,381 |
+| solr-8.3.1/contrib/prometheus-exporter | 3 | 6,281 | 63 | 37 | 6,381 |
+| solr-8.3.1/contrib/prometheus-exporter/bin | 1 | 82 | 0 | 26 | 108 |
+| solr-8.3.1/contrib/prometheus-exporter/conf | 2 | 6,199 | 63 | 11 | 6,273 |
+| solr-8.3.1/docs | 2 | 59 | 0 | 2 | 61 |
+| solr-8.3.1/docs/images | 1 | 39 | 0 | 1 | 40 |
+| solr-8.3.1/example | 82 | 32,009 | 5,063 | 942 | 38,014 |
+| solr-8.3.1/example/example-DIH | 49 | 2,848 | 3,605 | 603 | 7,056 |
+| solr-8.3.1/example/example-DIH/solr | 49 | 2,848 | 3,605 | 603 | 7,056 |
+| solr-8.3.1/example/example-DIH/solr/atom | 3 | 41 | 43 | 19 | 103 |
+| solr-8.3.1/example/example-DIH/solr/atom/conf | 2 | 41 | 43 | 17 | 101 |
+| solr-8.3.1/example/example-DIH/solr/db | 14 | 935 | 1,167 | 191 | 2,293 |
+| solr-8.3.1/example/example-DIH/solr/db/conf | 13 | 935 | 1,167 | 189 | 2,291 |
+| solr-8.3.1/example/example-DIH/solr/db/conf/clustering | 3 | 39 | 23 | 3 | 65 |
+| solr-8.3.1/example/example-DIH/solr/db/conf/clustering/carrot2 | 3 | 39 | 23 | 3 | 65 |
+| solr-8.3.1/example/example-DIH/solr/db/conf/xslt | 5 | 515 | 104 | 58 | 677 |
+| solr-8.3.1/example/example-DIH/solr/mail | 14 | 919 | 1,171 | 189 | 2,279 |
+| solr-8.3.1/example/example-DIH/solr/mail/conf | 13 | 919 | 1,171 | 187 | 2,277 |
+| solr-8.3.1/example/example-DIH/solr/mail/conf/clustering | 3 | 39 | 23 | 3 | 65 |
+| solr-8.3.1/example/example-DIH/solr/mail/conf/clustering/carrot2 | 3 | 39 | 23 | 3 | 65 |
+| solr-8.3.1/example/example-DIH/solr/mail/conf/xslt | 5 | 515 | 104 | 58 | 677 |
+| solr-8.3.1/example/example-DIH/solr/solr | 14 | 917 | 1,183 | 187 | 2,287 |
+| solr-8.3.1/example/example-DIH/solr/solr/conf | 13 | 917 | 1,183 | 185 | 2,285 |
+| solr-8.3.1/example/example-DIH/solr/solr/conf/clustering | 3 | 39 | 23 | 3 | 65 |
+| solr-8.3.1/example/example-DIH/solr/solr/conf/clustering/carrot2 | 3 | 39 | 23 | 3 | 65 |
+| solr-8.3.1/example/example-DIH/solr/solr/conf/xslt | 5 | 515 | 104 | 58 | 677 |
+| solr-8.3.1/example/example-DIH/solr/tika | 3 | 34 | 41 | 16 | 91 |
+| solr-8.3.1/example/example-DIH/solr/tika/conf | 2 | 34 | 41 | 14 | 89 |
+| solr-8.3.1/example/exampledocs | 17 | 513 | 281 | 75 | 869 |
+| solr-8.3.1/example/files | 13 | 1,298 | 1,153 | 250 | 2,701 |
+| solr-8.3.1/example/files/browse-resources | 3 | 108 | 6 | 9 | 123 |
+| solr-8.3.1/example/files/browse-resources/velocity | 3 | 108 | 6 | 9 | 123 |
+| solr-8.3.1/example/files/conf | 10 | 1,190 | 1,147 | 241 | 2,578 |
+| solr-8.3.1/example/files/conf/velocity | 5 | 730 | 99 | 108 | 937 |
+| solr-8.3.1/example/files/conf/velocity/js | 3 | 730 | 99 | 104 | 933 |
+| solr-8.3.1/example/films | 3 | 27,350 | 24 | 14 | 27,388 |
+| solr-8.3.1/server | 144 | 40,399 | 21,136 | 6,569 | 68,104 |
+| solr-8.3.1/server/contexts | 1 | 8 | 0 | 1 | 9 |
+| solr-8.3.1/server/etc | 6 | 528 | 400 | 59 | 987 |
+| solr-8.3.1/server/resources | 3 | 74 | 123 | 16 | 213 |
+| solr-8.3.1/server/scripts | 3 | 172 | 19 | 39 | 230 |
+| solr-8.3.1/server/scripts/cloud-scripts | 3 | 172 | 19 | 39 | 230 |
+| solr-8.3.1/server/solr | 27 | 2,612 | 3,377 | 557 | 6,546 |
+| solr-8.3.1/server/solr-webapp | 99 | 36,960 | 17,207 | 5,892 | 60,059 |
+| solr-8.3.1/server/solr-webapp/webapp | 99 | 36,960 | 17,207 | 5,892 | 60,059 |
+| solr-8.3.1/server/solr-webapp/webapp/WEB-INF | 1 | 62 | 42 | 11 | 115 |
+| solr-8.3.1/server/solr-webapp/webapp/css | 26 | 5,548 | 536 | 1,005 | 7,089 |
+| solr-8.3.1/server/solr-webapp/webapp/css/angular | 26 | 5,548 | 536 | 1,005 | 7,089 |
+| solr-8.3.1/server/solr-webapp/webapp/img | 1 | 39 | 0 | 1 | 40 |
+| solr-8.3.1/server/solr-webapp/webapp/js | 25 | 4,450 | 515 | 586 | 5,551 |
+| solr-8.3.1/server/solr-webapp/webapp/js/angular | 25 | 4,450 | 515 | 586 | 5,551 |
+| solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers | 23 | 3,627 | 478 | 544 | 4,649 |
+| solr-8.3.1/server/solr-webapp/webapp/libs | 21 | 24,087 | 15,683 | 3,464 | 43,234 |
+| solr-8.3.1/server/solr-webapp/webapp/partials | 24 | 2,571 | 415 | 787 | 3,773 |
+| solr-8.3.1/server/solr/configsets | 21 | 2,243 | 2,359 | 437 | 5,039 |
+| solr-8.3.1/server/solr/configsets/_default | 2 | 316 | 976 | 99 | 1,391 |
+| solr-8.3.1/server/solr/configsets/_default/conf | 2 | 316 | 976 | 99 | 1,391 |
+| solr-8.3.1/server/solr/configsets/sample_techproducts_configs | 19 | 1,927 | 1,383 | 338 | 3,648 |
+| solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf | 19 | 1,927 | 1,383 | 338 | 3,648 |
+| solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/clustering | 3 | 39 | 23 | 3 | 65 |
+| solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/clustering/carrot2 | 3 | 39 | 23 | 3 | 65 |
+| solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/velocity | 3 | 839 | 77 | 129 | 1,045 |
+| solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/xslt | 5 | 515 | 104 | 58 | 677 |
+| solr-8.3.1/server/solr/dash | 4 | 345 | 968 | 105 | 1,418 |
+| solr-8.3.1/server/solr/dash/conf | 3 | 341 | 966 | 104 | 1,411 |
+| solr-8.3.1/server/tmp | 5 | 45 | 10 | 5 | 60 |
+| src | 384 | 68,699 | 6,117 | 7,234 | 82,050 |
+| src/client | 277 | 46,644 | 4,305 | 5,743 | 56,692 |
+| src/client/apis | 7 | 1,053 | 97 | 150 | 1,300 |
+| src/client/apis/google_docs | 2 | 543 | 9 | 73 | 625 |
+| src/client/apis/youtube | 2 | 379 | 53 | 56 | 488 |
+| src/client/cognitive_services | 1 | 342 | 10 | 57 | 409 |
+| src/client/documents | 2 | 839 | 143 | 90 | 1,072 |
+| src/client/util | 30 | 3,931 | 351 | 515 | 4,797 |
+| src/client/util/Import & Export | 4 | 566 | 0 | 52 | 618 |
+| src/client/util/ProsemirrorCopy | 1 | 128 | 30 | 22 | 180 |
+| src/client/views | 231 | 36,751 | 1,935 | 4,094 | 42,780 |
+| src/client/views/animationtimeline | 10 | 1,966 | 237 | 262 | 2,465 |
+| src/client/views/collections | 53 | 11,516 | 574 | 1,153 | 13,243 |
+| src/client/views/collections/collectionFreeForm | 12 | 2,540 | 177 | 203 | 2,920 |
+| src/client/views/collections/collectionMulticolumn | 8 | 751 | 144 | 85 | 980 |
+| src/client/views/linking | 7 | 747 | 10 | 138 | 895 |
+| src/client/views/nodes | 75 | 12,034 | 475 | 1,218 | 13,727 |
+| src/client/views/nodes/formattedText | 22 | 5,353 | 250 | 592 | 6,195 |
+| src/client/views/pdf | 6 | 964 | 27 | 93 | 1,084 |
+| src/client/views/presentationview | 2 | 272 | 31 | 24 | 327 |
+| src/client/views/search | 21 | 2,380 | 238 | 401 | 3,019 |
+| src/client/views/webcam | 3 | 371 | 17 | 76 | 464 |
+| src/debug | 3 | 244 | 0 | 28 | 272 |
+| src/extensions | 4 | 53 | 5 | 13 | 71 |
+| src/extensions/General | 2 | 14 | 0 | 3 | 17 |
+| src/mobile | 7 | 617 | 60 | 82 | 759 |
+| src/new_fields | 22 | 2,682 | 172 | 315 | 3,169 |
+| src/pen-gestures | 2 | 397 | 172 | 27 | 596 |
+| src/scraping | 15 | 1,155 | 352 | 176 | 1,683 |
+| src/scraping/acm | 4 | 139 | 185 | 15 | 339 |
+| src/scraping/buxton | 11 | 1,016 | 167 | 161 | 1,344 |
+| src/scraping/buxton/.idea | 6 | 205 | 0 | 0 | 205 |
+| src/scraping/buxton/.idea/inspectionProfiles | 1 | 6 | 0 | 0 | 6 |
+| src/scraping/buxton/final | 1 | 228 | 142 | 26 | 396 |
+| src/scraping/buxton/narratives | 1 | 39 | 0 | 0 | 39 |
+| src/server | 52 | 16,247 | 956 | 731 | 17,934 |
+| src/server/ApiManagers | 11 | 1,223 | 226 | 149 | 1,598 |
+| src/server/DashSession | 9 | 903 | 229 | 122 | 1,254 |
+| src/server/DashSession/Session | 8 | 748 | 177 | 99 | 1,024 |
+| src/server/DashSession/Session/agents | 5 | 489 | 169 | 72 | 730 |
+| src/server/DashSession/Session/utilities | 3 | 259 | 8 | 27 | 294 |
+| src/server/Websocket | 1 | 263 | 5 | 46 | 314 |
+| src/server/apis | 4 | 328 | 198 | 41 | 567 |
+| src/server/apis/google | 2 | 191 | 168 | 27 | 386 |
+| src/server/apis/youtube | 2 | 137 | 30 | 14 | 181 |
+| src/server/authentication | 4 | 890 | 66 | 100 | 1,056 |
+| src/server/authentication/config | 1 | 23 | 2 | 4 | 29 |
+| src/server/authentication/controllers | 1 | 218 | 25 | 25 | 268 |
+| src/server/authentication/models | 2 | 649 | 39 | 71 | 759 |
+| src/server/credentials | 2 | 35 | 0 | 6 | 41 |
+| src/typings | 1 | 219 | 72 | 38 | 329 |
+| test | 1 | 141 | 0 | 20 | 161 |
+| views | 7 | 304 | 5 | 44 | 353 |
+| views/stylesheets | 1 | 185 | 4 | 34 | 223 |
+
+[details](details.md)
\ No newline at end of file
diff --git a/.VSCodeCounter/results.txt b/.VSCodeCounter/results.txt
new file mode 100644
index 000000000..aaf54b147
--- /dev/null
+++ b/.VSCodeCounter/results.txt
@@ -0,0 +1,813 @@
+Date : 2020-04-30 14:40:16
+Directory : /Users/bcz/Documents/GitHub/Dash-Web
+Total : 646 files,  224911 codes, 32987 comments, 15880 blanks, all 273778 lines
+
+Languages
++------------------+------------+------------+------------+------------+------------+
+| language         | files      | code       | comment    | blank      | total      |
++------------------+------------+------------+------------+------------+------------+
+| JavaScript       |         69 |     89,717 |     18,520 |      5,866 |    114,103 |
+| JSON             |         20 |     50,401 |         37 |         14 |     50,452 |
+| TypeScript React |        140 |     30,250 |      1,878 |      3,154 |     35,282 |
+| XML              |         73 |     17,257 |      8,281 |        992 |     26,530 |
+| TypeScript       |        115 |     15,246 |      2,095 |      1,958 |     19,299 |
+| SCSS             |        104 |      7,726 |        255 |      1,136 |      9,117 |
+| CSS              |         30 |      5,982 |        549 |      1,095 |      7,626 |
+| HTML             |         34 |      2,883 |        431 |        848 |      4,162 |
+| XSL              |         20 |      2,060 |        416 |        232 |      2,708 |
+| Batch            |          5 |      1,893 |        184 |        273 |      2,350 |
+| Python           |          5 |        632 |         49 |        148 |        829 |
+| Shell Script     |          6 |        538 |        248 |        120 |        906 |
+| Properties       |         16 |        161 |         43 |         30 |        234 |
+| Pug              |          6 |        119 |          1 |         10 |        130 |
+| log              |          1 |         38 |          0 |          1 |         39 |
+| Markdown         |          1 |          6 |          0 |          3 |          9 |
+| Ignore           |          1 |          2 |          0 |          0 |          2 |
++------------------+------------+------------+------------+------------+------------+
+
+Directories
++-------------------------------------------------------------------------------------------------------------+------------+------------+------------+------------+------------+
+| path                                                                                                        | files      | code       | comment    | blank      | total      |
++-------------------------------------------------------------------------------------------------------------+------------+------------+------------+------------+------------+
+| .                                                                                                           |        646 |    224,911 |     32,987 |     15,880 |    273,778 |
+| solr-8.3.1                                                                                                  |        236 |     80,866 |     26,654 |      7,861 |    115,381 |
+| src                                                                                                         |        384 |     68,699 |      6,117 |      7,234 |     82,050 |
+| deploy                                                                                                      |          8 |     55,744 |        174 |        704 |     56,622 |
+| deploy/assets                                                                                               |          2 |     55,677 |        174 |        686 |     56,537 |
+| src/client                                                                                                  |        277 |     46,644 |      4,305 |      5,743 |     56,692 |
+| solr-8.3.1/server                                                                                           |        144 |     40,399 |     21,136 |      6,569 |     68,104 |
+| solr-8.3.1/server/solr-webapp/webapp                                                                        |         99 |     36,960 |     17,207 |      5,892 |     60,059 |
+| solr-8.3.1/server/solr-webapp                                                                               |         99 |     36,960 |     17,207 |      5,892 |     60,059 |
+| src/client/views                                                                                            |        231 |     36,751 |      1,935 |      4,094 |     42,780 |
+| solr-8.3.1/example                                                                                          |         82 |     32,009 |      5,063 |        942 |     38,014 |
+| solr-8.3.1/example/films                                                                                    |          3 |     27,350 |         24 |         14 |     27,388 |
+| solr-8.3.1/server/solr-webapp/webapp/libs                                                                   |         21 |     24,087 |     15,683 |      3,464 |     43,234 |
+| src/server                                                                                                  |         52 |     16,247 |        956 |        731 |     17,934 |
+| src/client/views/nodes                                                                                      |         75 |     12,034 |        475 |      1,218 |     13,727 |
+| src/client/views/collections                                                                                |         53 |     11,516 |        574 |      1,153 |     13,243 |
+| solr-8.3.1/contrib                                                                                          |          3 |      6,281 |         63 |         37 |      6,381 |
+| solr-8.3.1/contrib/prometheus-exporter                                                                      |          3 |      6,281 |         63 |         37 |      6,381 |
+| solr-8.3.1/contrib/prometheus-exporter/conf                                                                 |          2 |      6,199 |         63 |         11 |      6,273 |
+| solr-8.3.1/server/solr-webapp/webapp/css/angular                                                            |         26 |      5,548 |        536 |      1,005 |      7,089 |
+| solr-8.3.1/server/solr-webapp/webapp/css                                                                    |         26 |      5,548 |        536 |      1,005 |      7,089 |
+| src/client/views/nodes/formattedText                                                                        |         22 |      5,353 |        250 |        592 |      6,195 |
+| solr-8.3.1/server/solr-webapp/webapp/js/angular                                                             |         25 |      4,450 |        515 |        586 |      5,551 |
+| solr-8.3.1/server/solr-webapp/webapp/js                                                                     |         25 |      4,450 |        515 |        586 |      5,551 |
+| src/client/util                                                                                             |         30 |      3,931 |        351 |        515 |      4,797 |
+| solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers                                                 |         23 |      3,627 |        478 |        544 |      4,649 |
+| solr-8.3.1/example/example-DIH/solr                                                                         |         49 |      2,848 |      3,605 |        603 |      7,056 |
+| solr-8.3.1/example/example-DIH                                                                              |         49 |      2,848 |      3,605 |        603 |      7,056 |
+| src/new_fields                                                                                              |         22 |      2,682 |        172 |        315 |      3,169 |
+| solr-8.3.1/server/solr                                                                                      |         27 |      2,612 |      3,377 |        557 |      6,546 |
+| solr-8.3.1/server/solr-webapp/webapp/partials                                                               |         24 |      2,571 |        415 |        787 |      3,773 |
+| src/client/views/collections/collectionFreeForm                                                             |         12 |      2,540 |        177 |        203 |      2,920 |
+| src/client/views/search                                                                                     |         21 |      2,380 |        238 |        401 |      3,019 |
+| solr-8.3.1/server/solr/configsets                                                                           |         21 |      2,243 |      2,359 |        437 |      5,039 |
+| solr-8.3.1/bin                                                                                              |          5 |      2,118 |        392 |        311 |      2,821 |
+| src/client/views/animationtimeline                                                                          |         10 |      1,966 |        237 |        262 |      2,465 |
+| solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf                                          |         19 |      1,927 |      1,383 |        338 |      3,648 |
+| solr-8.3.1/server/solr/configsets/sample_techproducts_configs                                               |         19 |      1,927 |      1,383 |        338 |      3,648 |
+| solr-8.3.1/example/files                                                                                    |         13 |      1,298 |      1,153 |        250 |      2,701 |
+| src/server/ApiManagers                                                                                      |         11 |      1,223 |        226 |        149 |      1,598 |
+| solr-8.3.1/example/files/conf                                                                               |         10 |      1,190 |      1,147 |        241 |      2,578 |
+| src/scraping                                                                                                |         15 |      1,155 |        352 |        176 |      1,683 |
+| src/client/apis                                                                                             |          7 |      1,053 |         97 |        150 |      1,300 |
+| src/scraping/buxton                                                                                         |         11 |      1,016 |        167 |        161 |      1,344 |
+| src/client/views/pdf                                                                                        |          6 |        964 |         27 |         93 |      1,084 |
+| solr-8.3.1/example/example-DIH/solr/db                                                                      |         14 |        935 |      1,167 |        191 |      2,293 |
+| solr-8.3.1/example/example-DIH/solr/db/conf                                                                 |         13 |        935 |      1,167 |        189 |      2,291 |
+| solr-8.3.1/example/example-DIH/solr/mail                                                                    |         14 |        919 |      1,171 |        189 |      2,279 |
+| solr-8.3.1/example/example-DIH/solr/mail/conf                                                               |         13 |        919 |      1,171 |        187 |      2,277 |
+| solr-8.3.1/example/example-DIH/solr/solr/conf                                                               |         13 |        917 |      1,183 |        185 |      2,285 |
+| solr-8.3.1/example/example-DIH/solr/solr                                                                    |         14 |        917 |      1,183 |        187 |      2,287 |
+| src/server/DashSession                                                                                      |          9 |        903 |        229 |        122 |      1,254 |
+| src/server/authentication                                                                                   |          4 |        890 |         66 |        100 |      1,056 |
+| solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/velocity                                 |          3 |        839 |         77 |        129 |      1,045 |
+| src/client/documents                                                                                        |          2 |        839 |        143 |         90 |      1,072 |
+| src/client/views/collections/collectionMulticolumn                                                          |          8 |        751 |        144 |         85 |        980 |
+| src/server/DashSession/Session                                                                              |          8 |        748 |        177 |         99 |      1,024 |
+| src/client/views/linking                                                                                    |          7 |        747 |         10 |        138 |        895 |
+| solr-8.3.1/example/files/conf/velocity                                                                      |          5 |        730 |         99 |        108 |        937 |
+| solr-8.3.1/example/files/conf/velocity/js                                                                   |          3 |        730 |         99 |        104 |        933 |
+| src/server/authentication/models                                                                            |          2 |        649 |         39 |         71 |        759 |
+| src/mobile                                                                                                  |          7 |        617 |         60 |         82 |        759 |
+| src/client/util/Import & Export                                                                             |          4 |        566 |          0 |         52 |        618 |
+| src/client/apis/google_docs                                                                                 |          2 |        543 |          9 |         73 |        625 |
+| solr-8.3.1/server/etc                                                                                       |          6 |        528 |        400 |         59 |        987 |
+| solr-8.3.1/example/example-DIH/solr/mail/conf/xslt                                                          |          5 |        515 |        104 |         58 |        677 |
+| solr-8.3.1/example/example-DIH/solr/db/conf/xslt                                                            |          5 |        515 |        104 |         58 |        677 |
+| solr-8.3.1/example/example-DIH/solr/solr/conf/xslt                                                          |          5 |        515 |        104 |         58 |        677 |
+| solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/xslt                                     |          5 |        515 |        104 |         58 |        677 |
+| solr-8.3.1/example/exampledocs                                                                              |         17 |        513 |        281 |         75 |        869 |
+| src/server/DashSession/Session/agents                                                                       |          5 |        489 |        169 |         72 |        730 |
+| src/pen-gestures                                                                                            |          2 |        397 |        172 |         27 |        596 |
+| src/client/apis/youtube                                                                                     |          2 |        379 |         53 |         56 |        488 |
+| src/client/views/webcam                                                                                     |          3 |        371 |         17 |         76 |        464 |
+| solr-8.3.1/server/solr/dash                                                                                 |          4 |        345 |        968 |        105 |      1,418 |
+| src/client/cognitive_services                                                                               |          1 |        342 |         10 |         57 |        409 |
+| solr-8.3.1/server/solr/dash/conf                                                                            |          3 |        341 |        966 |        104 |      1,411 |
+| src/server/apis                                                                                             |          4 |        328 |        198 |         41 |        567 |
+| solr-8.3.1/server/solr/configsets/_default/conf                                                             |          2 |        316 |        976 |         99 |      1,391 |
+| solr-8.3.1/server/solr/configsets/_default                                                                  |          2 |        316 |        976 |         99 |      1,391 |
+| views                                                                                                       |          7 |        304 |          5 |         44 |        353 |
+| src/client/views/presentationview                                                                           |          2 |        272 |         31 |         24 |        327 |
+| src/server/Websocket                                                                                        |          1 |        263 |          5 |         46 |        314 |
+| src/server/DashSession/Session/utilities                                                                    |          3 |        259 |          8 |         27 |        294 |
+| src/debug                                                                                                   |          3 |        244 |          0 |         28 |        272 |
+| src/scraping/buxton/final                                                                                   |          1 |        228 |        142 |         26 |        396 |
+| src/typings                                                                                                 |          1 |        219 |         72 |         38 |        329 |
+| src/server/authentication/controllers                                                                       |          1 |        218 |         25 |         25 |        268 |
+| src/scraping/buxton/.idea                                                                                   |          6 |        205 |          0 |          0 |        205 |
+| src/server/apis/google                                                                                      |          2 |        191 |        168 |         27 |        386 |
+| views/stylesheets                                                                                           |          1 |        185 |          4 |         34 |        223 |
+| solr-8.3.1/server/scripts/cloud-scripts                                                                     |          3 |        172 |         19 |         39 |        230 |
+| solr-8.3.1/server/scripts                                                                                   |          3 |        172 |         19 |         39 |        230 |
+| test                                                                                                        |          1 |        141 |          0 |         20 |        161 |
+| src/scraping/acm                                                                                            |          4 |        139 |        185 |         15 |        339 |
+| src/server/apis/youtube                                                                                     |          2 |        137 |         30 |         14 |        181 |
+| src/client/util/ProsemirrorCopy                                                                             |          1 |        128 |         30 |         22 |        180 |
+| solr-8.3.1/example/files/browse-resources/velocity                                                          |          3 |        108 |          6 |          9 |        123 |
+| solr-8.3.1/example/files/browse-resources                                                                   |          3 |        108 |          6 |          9 |        123 |
+| solr-8.3.1/contrib/prometheus-exporter/bin                                                                  |          1 |         82 |          0 |         26 |        108 |
+| solr-8.3.1/server/resources                                                                                 |          3 |         74 |        123 |         16 |        213 |
+| solr-8.3.1/server/solr-webapp/webapp/WEB-INF                                                                |          1 |         62 |         42 |         11 |        115 |
+| solr-8.3.1/docs                                                                                             |          2 |         59 |          0 |          2 |         61 |
+| src/extensions                                                                                              |          4 |         53 |          5 |         13 |         71 |
+| solr-8.3.1/server/tmp                                                                                       |          5 |         45 |         10 |          5 |         60 |
+| solr-8.3.1/example/example-DIH/solr/atom/conf                                                               |          2 |         41 |         43 |         17 |        101 |
+| solr-8.3.1/example/example-DIH/solr/atom                                                                    |          3 |         41 |         43 |         19 |        103 |
+| solr-8.3.1/server/solr-webapp/webapp/img                                                                    |          1 |         39 |          0 |          1 |         40 |
+| solr-8.3.1/docs/images                                                                                      |          1 |         39 |          0 |          1 |         40 |
+| src/scraping/buxton/narratives                                                                              |          1 |         39 |          0 |          0 |         39 |
+| solr-8.3.1/example/example-DIH/solr/mail/conf/clustering/carrot2                                            |          3 |         39 |         23 |          3 |         65 |
+| solr-8.3.1/example/example-DIH/solr/mail/conf/clustering                                                    |          3 |         39 |         23 |          3 |         65 |
+| solr-8.3.1/example/example-DIH/solr/solr/conf/clustering/carrot2                                            |          3 |         39 |         23 |          3 |         65 |
+| solr-8.3.1/example/example-DIH/solr/solr/conf/clustering                                                    |          3 |         39 |         23 |          3 |         65 |
+| solr-8.3.1/example/example-DIH/solr/db/conf/clustering/carrot2                                              |          3 |         39 |         23 |          3 |         65 |
+| solr-8.3.1/example/example-DIH/solr/db/conf/clustering                                                      |          3 |         39 |         23 |          3 |         65 |
+| solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/clustering/carrot2                       |          3 |         39 |         23 |          3 |         65 |
+| solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/clustering                               |          3 |         39 |         23 |          3 |         65 |
+| src/server/credentials                                                                                      |          2 |         35 |          0 |          6 |         41 |
+| solr-8.3.1/example/example-DIH/solr/tika                                                                    |          3 |         34 |         41 |         16 |         91 |
+| solr-8.3.1/example/example-DIH/solr/tika/conf                                                               |          2 |         34 |         41 |         14 |         89 |
+| deploy/debug                                                                                                |          3 |         32 |          0 |          9 |         41 |
+| src/server/authentication/config                                                                            |          1 |         23 |          2 |          4 |         29 |
+| deploy/mobile                                                                                               |          2 |         22 |          0 |          6 |         28 |
+| src/extensions/General                                                                                      |          2 |         14 |          0 |          3 |         17 |
+| build                                                                                                       |          1 |          9 |          0 |          3 |         12 |
+| solr-8.3.1/server/contexts                                                                                  |          1 |          8 |          0 |          1 |          9 |
+| src/scraping/buxton/.idea/inspectionProfiles                                                                |          1 |          6 |          0 |          0 |          6 |
++-------------------------------------------------------------------------------------------------------------+------------+------------+------------+------------+------------+
+
+Files
++-------------------------------------------------------------------------------------------------------------+------------------+------------+------------+------------+------------+
+| filename                                                                                                    | language         | code       | comment    | blank      | total      |
++-------------------------------------------------------------------------------------------------------------+------------------+------------+------------+------------+------------+
+| README.md                                                                                                   | Markdown         |          6 |          0 |          3 |          9 |
+| build/index.html                                                                                            | HTML             |          9 |          0 |          3 |         12 |
+| dash.bat                                                                                                    | Batch            |          2 |          0 |          1 |          3 |
+| deploy/assets/env.json                                                                                      | JSON             |         15 |          0 |          0 |         15 |
+| deploy/assets/pdf.worker.js                                                                                 | JavaScript       |     55,662 |        174 |        686 |     56,522 |
+| deploy/debug/repl.html                                                                                      | HTML             |         11 |          0 |          3 |         14 |
+| deploy/debug/test.html                                                                                      | HTML             |         10 |          0 |          3 |         13 |
+| deploy/debug/viewer.html                                                                                    | HTML             |         11 |          0 |          3 |         14 |
+| deploy/index.html                                                                                           | HTML             |         13 |          0 |          3 |         16 |
+| deploy/mobile/image.html                                                                                    | HTML             |         12 |          0 |          3 |         15 |
+| deploy/mobile/ink.html                                                                                      | HTML             |         10 |          0 |          3 |         13 |
+| package-lock.json                                                                                           | JSON             |     18,689 |          0 |          1 |     18,690 |
+| package.json                                                                                                | JSON             |        266 |          0 |          1 |        267 |
+| sentence_parser.py                                                                                          | Python           |          6 |          0 |          1 |          7 |
+| session.config.json                                                                                         | JSON             |         12 |          0 |          1 |         13 |
+| solr-8.3.1/bin/install_solr_service.sh                                                                      | Shell Script     |        307 |         29 |         35 |        371 |
+| solr-8.3.1/bin/oom_solr.sh                                                                                  | Shell Script     |         13 |         15 |          3 |         31 |
+| solr-8.3.1/bin/solr.cmd                                                                                     | Batch            |      1,782 |         43 |        210 |      2,035 |
+| solr-8.3.1/bin/solr.in.cmd                                                                                  | Batch            |         16 |        133 |         29 |        178 |
+| solr-8.3.1/bin/solr.in.sh                                                                                   | Shell Script     |          0 |        172 |         34 |        206 |
+| solr-8.3.1/contrib/prometheus-exporter/bin/solr-exporter.cmd                                                | Batch            |         82 |          0 |         26 |        108 |
+| solr-8.3.1/contrib/prometheus-exporter/conf/grafana-solr-dashboard.json                                     | JSON             |      4,465 |          0 |          1 |      4,466 |
+| solr-8.3.1/contrib/prometheus-exporter/conf/solr-exporter-config.xml                                        | XML              |      1,734 |         63 |         10 |      1,807 |
+| solr-8.3.1/docs/images/solr.svg                                                                             | XML              |         39 |          0 |          1 |         40 |
+| solr-8.3.1/docs/index.html                                                                                  | HTML             |         20 |          0 |          1 |         21 |
+| solr-8.3.1/example/example-DIH/solr/atom/conf/atom-data-config.xml                                          | XML              |         21 |          6 |          9 |         36 |
+| solr-8.3.1/example/example-DIH/solr/atom/conf/solrconfig.xml                                                | XML              |         20 |         37 |          8 |         65 |
+| solr-8.3.1/example/example-DIH/solr/atom/core.properties                                                    | Properties       |          0 |          0 |          2 |          2 |
+| solr-8.3.1/example/example-DIH/solr/db/conf/clustering/carrot2/kmeans-attributes.xml                        | XML              |         13 |          6 |          1 |         20 |
+| solr-8.3.1/example/example-DIH/solr/db/conf/clustering/carrot2/lingo-attributes.xml                         | XML              |         13 |         11 |          1 |         25 |
+| solr-8.3.1/example/example-DIH/solr/db/conf/clustering/carrot2/stc-attributes.xml                           | XML              |         13 |          6 |          1 |         20 |
+| solr-8.3.1/example/example-DIH/solr/db/conf/currency.xml                                                    | XML              |         45 |         19 |          4 |         68 |
+| solr-8.3.1/example/example-DIH/solr/db/conf/db-data-config.xml                                              | XML              |         26 |          0 |          4 |         30 |
+| solr-8.3.1/example/example-DIH/solr/db/conf/elevate.xml                                                     | XML              |          3 |         37 |          3 |         43 |
+| solr-8.3.1/example/example-DIH/solr/db/conf/solrconfig.xml                                                  | XML              |        292 |        958 |        104 |      1,354 |
+| solr-8.3.1/example/example-DIH/solr/db/conf/update-script.js                                                | JavaScript       |         15 |         26 |         13 |         54 |
+| solr-8.3.1/example/example-DIH/solr/db/conf/xslt/example.xsl                                                | XSL              |         98 |         20 |         15 |        133 |
+| solr-8.3.1/example/example-DIH/solr/db/conf/xslt/example_atom.xsl                                           | XSL              |         40 |         20 |          8 |         68 |
+| solr-8.3.1/example/example-DIH/solr/db/conf/xslt/example_rss.xsl                                            | XSL              |         41 |         20 |          6 |         67 |
+| solr-8.3.1/example/example-DIH/solr/db/conf/xslt/luke.xsl                                                   | XSL              |        301 |         19 |         18 |        338 |
+| solr-8.3.1/example/example-DIH/solr/db/conf/xslt/updateXml.xsl                                              | XSL              |         35 |         25 |         11 |         71 |
+| solr-8.3.1/example/example-DIH/solr/db/core.properties                                                      | Properties       |          0 |          0 |          2 |          2 |
+| solr-8.3.1/example/example-DIH/solr/mail/conf/clustering/carrot2/kmeans-attributes.xml                      | XML              |         13 |          6 |          1 |         20 |
+| solr-8.3.1/example/example-DIH/solr/mail/conf/clustering/carrot2/lingo-attributes.xml                       | XML              |         13 |         11 |          1 |         25 |
+| solr-8.3.1/example/example-DIH/solr/mail/conf/clustering/carrot2/stc-attributes.xml                         | XML              |         13 |          6 |          1 |         20 |
+| solr-8.3.1/example/example-DIH/solr/mail/conf/currency.xml                                                  | XML              |         45 |         19 |          4 |         68 |
+| solr-8.3.1/example/example-DIH/solr/mail/conf/elevate.xml                                                   | XML              |          3 |         37 |          3 |         43 |
+| solr-8.3.1/example/example-DIH/solr/mail/conf/mail-data-config.xml                                          | XML              |          8 |          4 |          1 |         13 |
+| solr-8.3.1/example/example-DIH/solr/mail/conf/solrconfig.xml                                                | XML              |        294 |        958 |        105 |      1,357 |
+| solr-8.3.1/example/example-DIH/solr/mail/conf/update-script.js                                              | JavaScript       |         15 |         26 |         13 |         54 |
+| solr-8.3.1/example/example-DIH/solr/mail/conf/xslt/example.xsl                                              | XSL              |         98 |         20 |         15 |        133 |
+| solr-8.3.1/example/example-DIH/solr/mail/conf/xslt/example_atom.xsl                                         | XSL              |         40 |         20 |          8 |         68 |
+| solr-8.3.1/example/example-DIH/solr/mail/conf/xslt/example_rss.xsl                                          | XSL              |         41 |         20 |          6 |         67 |
+| solr-8.3.1/example/example-DIH/solr/mail/conf/xslt/luke.xsl                                                 | XSL              |        301 |         19 |         18 |        338 |
+| solr-8.3.1/example/example-DIH/solr/mail/conf/xslt/updateXml.xsl                                            | XSL              |         35 |         25 |         11 |         71 |
+| solr-8.3.1/example/example-DIH/solr/mail/core.properties                                                    | Properties       |          0 |          0 |          2 |          2 |
+| solr-8.3.1/example/example-DIH/solr/solr.xml                                                                | XML              |          2 |          0 |          1 |          3 |
+| solr-8.3.1/example/example-DIH/solr/solr/conf/clustering/carrot2/kmeans-attributes.xml                      | XML              |         13 |          6 |          1 |         20 |
+| solr-8.3.1/example/example-DIH/solr/solr/conf/clustering/carrot2/lingo-attributes.xml                       | XML              |         13 |         11 |          1 |         25 |
+| solr-8.3.1/example/example-DIH/solr/solr/conf/clustering/carrot2/stc-attributes.xml                         | XML              |         13 |          6 |          1 |         20 |
+| solr-8.3.1/example/example-DIH/solr/solr/conf/currency.xml                                                  | XML              |         45 |         19 |          4 |         68 |
+| solr-8.3.1/example/example-DIH/solr/solr/conf/elevate.xml                                                   | XML              |          3 |         37 |          3 |         43 |
+| solr-8.3.1/example/example-DIH/solr/solr/conf/solr-data-config.xml                                          | XML              |          8 |         16 |          2 |         26 |
+| solr-8.3.1/example/example-DIH/solr/solr/conf/solrconfig.xml                                                | XML              |        292 |        958 |        102 |      1,352 |
+| solr-8.3.1/example/example-DIH/solr/solr/conf/update-script.js                                              | JavaScript       |         15 |         26 |         13 |         54 |
+| solr-8.3.1/example/example-DIH/solr/solr/conf/xslt/example.xsl                                              | XSL              |         98 |         20 |         15 |        133 |
+| solr-8.3.1/example/example-DIH/solr/solr/conf/xslt/example_atom.xsl                                         | XSL              |         40 |         20 |          8 |         68 |
+| solr-8.3.1/example/example-DIH/solr/solr/conf/xslt/example_rss.xsl                                          | XSL              |         41 |         20 |          6 |         67 |
+| solr-8.3.1/example/example-DIH/solr/solr/conf/xslt/luke.xsl                                                 | XSL              |        301 |         19 |         18 |        338 |
+| solr-8.3.1/example/example-DIH/solr/solr/conf/xslt/updateXml.xsl                                            | XSL              |         35 |         25 |         11 |         71 |
+| solr-8.3.1/example/example-DIH/solr/solr/core.properties                                                    | Properties       |          0 |          0 |          2 |          2 |
+| solr-8.3.1/example/example-DIH/solr/tika/conf/solrconfig.xml                                                | XML              |         17 |         38 |          7 |         62 |
+| solr-8.3.1/example/example-DIH/solr/tika/conf/tika-data-config.xml                                          | XML              |         17 |          3 |          7 |         27 |
+| solr-8.3.1/example/example-DIH/solr/tika/core.properties                                                    | Properties       |          0 |          0 |          2 |          2 |
+| solr-8.3.1/example/exampledocs/books.json                                                                   | JSON             |         51 |          0 |          1 |         52 |
+| solr-8.3.1/example/exampledocs/gb18030-example.xml                                                          | XML              |         14 |         16 |          3 |         33 |
+| solr-8.3.1/example/exampledocs/hd.xml                                                                       | XML              |         33 |         20 |          4 |         57 |
+| solr-8.3.1/example/exampledocs/ipod_other.xml                                                               | XML              |         32 |         20 |          9 |         61 |
+| solr-8.3.1/example/exampledocs/ipod_video.xml                                                               | XML              |         21 |         18 |          2 |         41 |
+| solr-8.3.1/example/exampledocs/manufacturers.xml                                                            | XML              |         57 |         16 |          3 |         76 |
+| solr-8.3.1/example/exampledocs/mem.xml                                                                      | XML              |         45 |         24 |          9 |         78 |
+| solr-8.3.1/example/exampledocs/money.xml                                                                    | XML              |         42 |         17 |          7 |         66 |
+| solr-8.3.1/example/exampledocs/monitor.xml                                                                  | XML              |         14 |         18 |          3 |         35 |
+| solr-8.3.1/example/exampledocs/monitor2.xml                                                                 | XML              |         13 |         18 |          3 |         34 |
+| solr-8.3.1/example/exampledocs/mp500.xml                                                                    | XML              |         23 |         18 |          3 |         44 |
+| solr-8.3.1/example/exampledocs/sample.html                                                                  | HTML             |         13 |          0 |          1 |         14 |
+| solr-8.3.1/example/exampledocs/sd500.xml                                                                    | XML              |         19 |         18 |          2 |         39 |
+| solr-8.3.1/example/exampledocs/solr.xml                                                                     | XML              |         20 |         16 |          3 |         39 |
+| solr-8.3.1/example/exampledocs/test_utf8.sh                                                                 | Shell Script     |         57 |         21 |         16 |         94 |
+| solr-8.3.1/example/exampledocs/utf8-example.xml                                                             | XML              |         19 |         20 |          4 |         43 |
+| solr-8.3.1/example/exampledocs/vidcard.xml                                                                  | XML              |         40 |         21 |          2 |         63 |
+| solr-8.3.1/example/files/browse-resources/velocity/resources.properties                                     | Properties       |         72 |          6 |          5 |         83 |
+| solr-8.3.1/example/files/browse-resources/velocity/resources_de_DE.properties                               | Properties       |         18 |          0 |          1 |         19 |
+| solr-8.3.1/example/files/browse-resources/velocity/resources_fr_FR.properties                               | Properties       |         18 |          0 |          3 |         21 |
+| solr-8.3.1/example/files/conf/currency.xml                                                                  | XML              |         45 |         19 |          4 |         68 |
+| solr-8.3.1/example/files/conf/elevate.xml                                                                   | XML              |          3 |         37 |          3 |         43 |
+| solr-8.3.1/example/files/conf/params.json                                                                   | JSON             |         34 |          0 |          1 |         35 |
+| solr-8.3.1/example/files/conf/solrconfig.xml                                                                | XML              |        298 |        979 |        102 |      1,379 |
+| solr-8.3.1/example/files/conf/update-script.js                                                              | JavaScript       |         80 |         13 |         23 |        116 |
+| solr-8.3.1/example/files/conf/velocity/dropit.js                                                            | JavaScript       |          0 |          0 |          2 |          2 |
+| solr-8.3.1/example/files/conf/velocity/jquery.tx3-tag-cloud.js                                              | JavaScript       |          0 |          0 |          2 |          2 |
+| solr-8.3.1/example/files/conf/velocity/js/dropit.js                                                         | JavaScript       |         64 |         15 |         19 |         98 |
+| solr-8.3.1/example/files/conf/velocity/js/jquery.autocomplete.js                                            | JavaScript       |        620 |         68 |         76 |        764 |
+| solr-8.3.1/example/files/conf/velocity/js/jquery.tx3-tag-cloud.js                                           | JavaScript       |         46 |         16 |          9 |         71 |
+| solr-8.3.1/example/films/film_data_generator.py                                                             | Python           |         82 |         24 |         12 |        118 |
+| solr-8.3.1/example/films/films.json                                                                         | JSON             |     15,830 |          0 |          1 |     15,831 |
+| solr-8.3.1/example/films/films.xml                                                                          | XML              |     11,438 |          0 |          1 |     11,439 |
+| solr-8.3.1/server/contexts/solr-jetty-context.xml                                                           | XML              |          8 |          0 |          1 |          9 |
+| solr-8.3.1/server/etc/jetty-http.xml                                                                        | XML              |         33 |         15 |          4 |         52 |
+| solr-8.3.1/server/etc/jetty-https.xml                                                                       | XML              |         56 |         16 |          5 |         77 |
+| solr-8.3.1/server/etc/jetty-https8.xml                                                                      | XML              |         34 |         32 |          4 |         70 |
+| solr-8.3.1/server/etc/jetty-ssl.xml                                                                         | XML              |         23 |         11 |          4 |         38 |
+| solr-8.3.1/server/etc/jetty.xml                                                                             | XML              |        110 |         94 |         18 |        222 |
+| solr-8.3.1/server/etc/webdefault.xml                                                                        | XML              |        272 |        232 |         24 |        528 |
+| solr-8.3.1/server/resources/jetty-logging.properties                                                        | Properties       |          1 |          0 |          1 |          2 |
+| solr-8.3.1/server/resources/log4j2-console.xml                                                              | XML              |         19 |         43 |          6 |         68 |
+| solr-8.3.1/server/resources/log4j2.xml                                                                      | XML              |         54 |         80 |          9 |        143 |
+| solr-8.3.1/server/scripts/cloud-scripts/snapshotscli.sh                                                     | Shell Script     |        152 |          2 |         23 |        177 |
+| solr-8.3.1/server/scripts/cloud-scripts/zkcli.bat                                                           | Batch            |         11 |          8 |          7 |         26 |
+| solr-8.3.1/server/scripts/cloud-scripts/zkcli.sh                                                            | Shell Script     |          9 |          9 |          9 |         27 |
+| solr-8.3.1/server/solr-webapp/webapp/WEB-INF/web.xml                                                        | XML              |         62 |         42 |         11 |        115 |
+| solr-8.3.1/server/solr-webapp/webapp/css/angular/analysis.css                                               | CSS              |        237 |         19 |         48 |        304 |
+| solr-8.3.1/server/solr-webapp/webapp/css/angular/chosen.css                                                 | CSS              |        402 |         55 |          9 |        466 |
+| solr-8.3.1/server/solr-webapp/webapp/css/angular/cloud.css                                                  | CSS              |        594 |         23 |        106 |        723 |
+| solr-8.3.1/server/solr-webapp/webapp/css/angular/collections.css                                            | CSS              |        296 |         18 |         65 |        379 |
+| solr-8.3.1/server/solr-webapp/webapp/css/angular/common.css                                                 | CSS              |        647 |         19 |        106 |        772 |
+| solr-8.3.1/server/solr-webapp/webapp/css/angular/cores.css                                                  | CSS              |        171 |         18 |         37 |        226 |
+| solr-8.3.1/server/solr-webapp/webapp/css/angular/dashboard.css                                              | CSS              |        134 |         18 |         28 |        180 |
+| solr-8.3.1/server/solr-webapp/webapp/css/angular/dataimport.css                                             | CSS              |        292 |         18 |         61 |        371 |
+| solr-8.3.1/server/solr-webapp/webapp/css/angular/documents.css                                              | CSS              |        131 |         23 |         26 |        180 |
+| solr-8.3.1/server/solr-webapp/webapp/css/angular/files.css                                                  | CSS              |         29 |         18 |          7 |         54 |
+| solr-8.3.1/server/solr-webapp/webapp/css/angular/index.css                                                  | CSS              |        164 |         18 |         35 |        217 |
+| solr-8.3.1/server/solr-webapp/webapp/css/angular/java-properties.css                                        | CSS              |         24 |         18 |          6 |         48 |
+| solr-8.3.1/server/solr-webapp/webapp/css/angular/jquery-ui.min.css                                          | CSS              |          1 |         26 |          2 |         29 |
+| solr-8.3.1/server/solr-webapp/webapp/css/angular/jquery-ui.structure.min.css                                | CSS              |          1 |         22 |          2 |         25 |
+| solr-8.3.1/server/solr-webapp/webapp/css/angular/logging.css                                                | CSS              |        303 |         19 |         63 |        385 |
+| solr-8.3.1/server/solr-webapp/webapp/css/angular/login.css                                                  | CSS              |         80 |         18 |         12 |        110 |
+| solr-8.3.1/server/solr-webapp/webapp/css/angular/menu.css                                                   | CSS              |        257 |         18 |         56 |        331 |
+| solr-8.3.1/server/solr-webapp/webapp/css/angular/overview.css                                               | CSS              |         20 |         18 |          5 |         43 |
+| solr-8.3.1/server/solr-webapp/webapp/css/angular/plugins.css                                                | CSS              |        172 |         18 |         31 |        221 |
+| solr-8.3.1/server/solr-webapp/webapp/css/angular/query.css                                                  | CSS              |        120 |         18 |         25 |        163 |
+| solr-8.3.1/server/solr-webapp/webapp/css/angular/replication.css                                            | CSS              |        404 |         18 |         79 |        501 |
+| solr-8.3.1/server/solr-webapp/webapp/css/angular/schema.css                                                 | CSS              |        596 |         20 |        112 |        728 |
+| solr-8.3.1/server/solr-webapp/webapp/css/angular/segments.css                                               | CSS              |        133 |         18 |         22 |        173 |
+| solr-8.3.1/server/solr-webapp/webapp/css/angular/stream.css                                                 | CSS              |        178 |         22 |         34 |        234 |
+| solr-8.3.1/server/solr-webapp/webapp/css/angular/suggestions.css                                            | CSS              |         43 |         18 |          4 |         65 |
+| solr-8.3.1/server/solr-webapp/webapp/css/angular/threads.css                                                | CSS              |        119 |         18 |         24 |        161 |
+| solr-8.3.1/server/solr-webapp/webapp/img/solr.svg                                                           | XML              |         39 |          0 |          1 |         40 |
+| solr-8.3.1/server/solr-webapp/webapp/index.html                                                             | HTML             |        203 |         16 |         38 |        257 |
+| solr-8.3.1/server/solr-webapp/webapp/js/angular/app.js                                                      | JavaScript       |        512 |         19 |         31 |        562 |
+| solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/alias-overview.js                               | JavaScript       |          8 |         16 |          4 |         28 |
+| solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/analysis.js                                     | JavaScript       |        161 |         18 |         23 |        202 |
+| solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/cloud.js                                        | JavaScript       |        847 |         51 |        124 |      1,022 |
+| solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/cluster-suggestions.js                          | JavaScript       |         43 |         18 |          2 |         63 |
+| solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/collection-overview.js                          | JavaScript       |         18 |         16 |          6 |         40 |
+| solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/collections.js                                  | JavaScript       |        244 |         19 |         27 |        290 |
+| solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/core-overview.js                                | JavaScript       |         69 |         16 |          9 |         94 |
+| solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/cores.js                                        | JavaScript       |        151 |         16 |         14 |        181 |
+| solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/dataimport.js                                   | JavaScript       |        234 |         25 |         44 |        303 |
+| solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/documents.js                                    | JavaScript       |        107 |         18 |         13 |        138 |
+| solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/files.js                                        | JavaScript       |         72 |         16 |         13 |        101 |
+| solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/index.js                                        | JavaScript       |         61 |         23 |         14 |         98 |
+| solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/java-properties.js                              | JavaScript       |         27 |         16 |          3 |         46 |
+| solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/logging.js                                      | JavaScript       |        112 |         35 |         12 |        159 |
+| solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/login.js                                        | JavaScript       |        269 |         30 |         19 |        318 |
+| solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/plugins.js                                      | JavaScript       |        130 |         19 |         19 |        168 |
+| solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/query.js                                        | JavaScript       |         88 |         19 |         14 |        121 |
+| solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/replication.js                                  | JavaScript       |        178 |         18 |         40 |        236 |
+| solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/schema.js                                       | JavaScript       |        524 |         19 |         69 |        612 |
+| solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/segments.js                                     | JavaScript       |         64 |         16 |         20 |        100 |
+| solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/stream.js                                       | JavaScript       |        173 |         16 |         51 |        240 |
+| solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/threads.js                                      | JavaScript       |         33 |         16 |          2 |         51 |
+| solr-8.3.1/server/solr-webapp/webapp/js/angular/controllers/unknown.js                                      | JavaScript       |         14 |         22 |          2 |         38 |
+| solr-8.3.1/server/solr-webapp/webapp/js/angular/services.js                                                 | JavaScript       |        311 |         18 |         11 |        340 |
+| solr-8.3.1/server/solr-webapp/webapp/libs/angular-chosen.js                                                 | JavaScript       |        112 |         24 |          4 |        140 |
+| solr-8.3.1/server/solr-webapp/webapp/libs/angular-cookies.js                                                | JavaScript       |         73 |        135 |         22 |        230 |
+| solr-8.3.1/server/solr-webapp/webapp/libs/angular-cookies.min.js                                            | JavaScript       |          2 |         29 |          1 |         32 |
+| solr-8.3.1/server/solr-webapp/webapp/libs/angular-resource.min.js                                           | JavaScript       |          7 |         29 |          1 |         37 |
+| solr-8.3.1/server/solr-webapp/webapp/libs/angular-route.js                                                  | JavaScript       |        316 |        636 |         67 |      1,019 |
+| solr-8.3.1/server/solr-webapp/webapp/libs/angular-route.min.js                                              | JavaScript       |          9 |         29 |          1 |         39 |
+| solr-8.3.1/server/solr-webapp/webapp/libs/angular-sanitize.js                                               | JavaScript       |        311 |        328 |         65 |        704 |
+| solr-8.3.1/server/solr-webapp/webapp/libs/angular-sanitize.min.js                                           | JavaScript       |         10 |         29 |          1 |         40 |
+| solr-8.3.1/server/solr-webapp/webapp/libs/angular-utf8-base64.js                                            | JavaScript       |        157 |         48 |         13 |        218 |
+| solr-8.3.1/server/solr-webapp/webapp/libs/angular-utf8-base64.min.js                                        | JavaScript       |          1 |         42 |          3 |         46 |
+| solr-8.3.1/server/solr-webapp/webapp/libs/angular.js                                                        | JavaScript       |     10,845 |     13,211 |      2,038 |     26,094 |
+| solr-8.3.1/server/solr-webapp/webapp/libs/angular.min.js                                                    | JavaScript       |         73 |        201 |          0 |        274 |
+| solr-8.3.1/server/solr-webapp/webapp/libs/chosen.jquery.js                                                  | JavaScript       |      1,151 |         36 |          8 |      1,195 |
+| solr-8.3.1/server/solr-webapp/webapp/libs/chosen.jquery.min.js                                              | JavaScript       |          2 |         29 |          0 |         31 |
+| solr-8.3.1/server/solr-webapp/webapp/libs/d3.js                                                             | JavaScript       |      7,720 |        519 |      1,135 |      9,374 |
+| solr-8.3.1/server/solr-webapp/webapp/libs/highlight.js                                                      | JavaScript       |          2 |         29 |          1 |         32 |
+| solr-8.3.1/server/solr-webapp/webapp/libs/jquery-1.7.2.min.js                                               | JavaScript       |          3 |         26 |          2 |         31 |
+| solr-8.3.1/server/solr-webapp/webapp/libs/jquery-2.1.3.min.js                                               | JavaScript       |          3 |         26 |          1 |         30 |
+| solr-8.3.1/server/solr-webapp/webapp/libs/jquery-ui.min.js                                                  | JavaScript       |          2 |         26 |          3 |         31 |
+| solr-8.3.1/server/solr-webapp/webapp/libs/jquery.jstree.js                                                  | JavaScript       |      3,222 |        228 |         85 |      3,535 |
+| solr-8.3.1/server/solr-webapp/webapp/libs/ngtimeago.js                                                      | JavaScript       |         66 |         23 |         13 |        102 |
+| solr-8.3.1/server/solr-webapp/webapp/partials/alias_overview.html                                           | HTML             |         21 |         16 |         10 |         47 |
+| solr-8.3.1/server/solr-webapp/webapp/partials/analysis.html                                                 | HTML             |         87 |         16 |         26 |        129 |
+| solr-8.3.1/server/solr-webapp/webapp/partials/cloud.html                                                    | HTML             |        263 |         16 |         24 |        303 |
+| solr-8.3.1/server/solr-webapp/webapp/partials/cluster_suggestions.html                                      | HTML             |         30 |         16 |          4 |         50 |
+| solr-8.3.1/server/solr-webapp/webapp/partials/collection_overview.html                                      | HTML             |         48 |         16 |         22 |         86 |
+| solr-8.3.1/server/solr-webapp/webapp/partials/collections.html                                              | HTML             |        301 |         16 |         79 |        396 |
+| solr-8.3.1/server/solr-webapp/webapp/partials/core_overview.html                                            | HTML             |        125 |         16 |         66 |        207 |
+| solr-8.3.1/server/solr-webapp/webapp/partials/cores.html                                                    | HTML             |        142 |         16 |         67 |        225 |
+| solr-8.3.1/server/solr-webapp/webapp/partials/dataimport.html                                               | HTML             |        142 |         16 |         52 |        210 |
+| solr-8.3.1/server/solr-webapp/webapp/partials/documents.html                                                | HTML             |         83 |         20 |          9 |        112 |
+| solr-8.3.1/server/solr-webapp/webapp/partials/files.html                                                    | HTML             |         17 |         16 |         15 |         48 |
+| solr-8.3.1/server/solr-webapp/webapp/partials/index.html                                                    | HTML             |        135 |         42 |         85 |        262 |
+| solr-8.3.1/server/solr-webapp/webapp/partials/java-properties.html                                          | HTML             |         10 |         16 |          2 |         28 |
+| solr-8.3.1/server/solr-webapp/webapp/partials/logging-levels.html                                           | HTML             |         35 |         16 |          6 |         57 |
+| solr-8.3.1/server/solr-webapp/webapp/partials/logging.html                                                  | HTML             |         40 |         16 |          2 |         58 |
+| solr-8.3.1/server/solr-webapp/webapp/partials/login.html                                                    | HTML             |        134 |         16 |         11 |        161 |
+| solr-8.3.1/server/solr-webapp/webapp/partials/plugins.html                                                  | HTML             |         48 |         17 |          8 |         73 |
+| solr-8.3.1/server/solr-webapp/webapp/partials/query.html                                                    | HTML             |        270 |         16 |         84 |        370 |
+| solr-8.3.1/server/solr-webapp/webapp/partials/replication.html                                              | HTML             |        153 |         16 |         71 |        240 |
+| solr-8.3.1/server/solr-webapp/webapp/partials/schema.html                                                   | HTML             |        336 |         16 |        104 |        456 |
+| solr-8.3.1/server/solr-webapp/webapp/partials/segments.html                                                 | HTML             |         70 |         16 |         14 |        100 |
+| solr-8.3.1/server/solr-webapp/webapp/partials/stream.html                                                   | HTML             |         40 |         16 |          9 |         65 |
+| solr-8.3.1/server/solr-webapp/webapp/partials/threads.html                                                  | HTML             |         36 |         16 |         14 |         66 |
+| solr-8.3.1/server/solr-webapp/webapp/partials/unknown.html                                                  | HTML             |          5 |         16 |          3 |         24 |
+| solr-8.3.1/server/solr/configsets/_default/conf/params.json                                                 | JSON             |         20 |          0 |          1 |         21 |
+| solr-8.3.1/server/solr/configsets/_default/conf/solrconfig.xml                                              | XML              |        296 |        976 |         98 |      1,370 |
+| solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/_rest_managed.json                       | JSON             |          1 |          0 |          1 |          2 |
+| solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/_schema_analysis_stopwords_english.json  | JSON             |         38 |          0 |          1 |         39 |
+| solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/_schema_analysis_synonyms_english.json   | JSON             |         11 |          0 |          1 |         12 |
+| solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/clustering/carrot2/kmeans-attributes.xml | XML              |         13 |          6 |          1 |         20 |
+| solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/clustering/carrot2/lingo-attributes.xml  | XML              |         13 |         11 |          1 |         25 |
+| solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/clustering/carrot2/stc-attributes.xml    | XML              |         13 |          6 |          1 |         20 |
+| solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/currency.xml                             | XML              |         45 |         19 |          4 |         68 |
+| solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/elevate.xml                              | XML              |          3 |         37 |          3 |         43 |
+| solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/params.json                              | JSON             |         11 |          0 |          1 |         12 |
+| solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/solrconfig.xml                           | XML              |        410 |      1,097 |        124 |      1,631 |
+| solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/update-script.js                         | JavaScript       |         15 |         26 |         13 |         54 |
+| solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/velocity/jquery.autocomplete.css         | CSS              |         34 |          9 |          6 |         49 |
+| solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/velocity/jquery.autocomplete.js          | JavaScript       |        620 |         68 |         76 |        764 |
+| solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/velocity/main.css                        | CSS              |        185 |          0 |         47 |        232 |
+| solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/xslt/example.xsl                         | XSL              |         98 |         20 |         15 |        133 |
+| solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/xslt/example_atom.xsl                    | XSL              |         40 |         20 |          8 |         68 |
+| solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/xslt/example_rss.xsl                     | XSL              |         41 |         20 |          6 |         67 |
+| solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/xslt/luke.xsl                            | XSL              |        301 |         19 |         18 |        338 |
+| solr-8.3.1/server/solr/configsets/sample_techproducts_configs/conf/xslt/updateXml.xsl                       | XSL              |         35 |         25 |         11 |         71 |
+| solr-8.3.1/server/solr/dash/conf/params.json                                                                | JSON             |         20 |          0 |          0 |         20 |
+| solr-8.3.1/server/solr/dash/conf/schema.xml                                                                 | XML              |         51 |          4 |          7 |         62 |
+| solr-8.3.1/server/solr/dash/conf/solrconfig.xml                                                             | XML              |        270 |        962 |         97 |      1,329 |
+| solr-8.3.1/server/solr/dash/core.properties                                                                 | Properties       |          4 |          2 |          1 |          7 |
+| solr-8.3.1/server/solr/solr.xml                                                                             | XML              |         21 |         25 |         11 |         57 |
+| solr-8.3.1/server/solr/zoo.cfg                                                                              | Properties       |          3 |         25 |          4 |         32 |
+| solr-8.3.1/server/tmp/start_3204295554151338130.properties                                                  | Properties       |          9 |          2 |          1 |         12 |
+| solr-8.3.1/server/tmp/start_5812170489311981381.properties                                                  | Properties       |          9 |          2 |          1 |         12 |
+| solr-8.3.1/server/tmp/start_6476327636763392575.properties                                                  | Properties       |          9 |          2 |          1 |         12 |
+| solr-8.3.1/server/tmp/start_7329004517204835686.properties                                                  | Properties       |          9 |          2 |          1 |         12 |
+| solr-8.3.1/server/tmp/start_9067375725008958788.properties                                                  | Properties       |          9 |          2 |          1 |         12 |
+| src/Utils.ts                                                                                                | TypeScript       |        441 |         23 |         81 |        545 |
+| src/client/ClientRecommender.scss                                                                           | SCSS             |          9 |          1 |          2 |         12 |
+| src/client/ClientRecommender.tsx                                                                            | TypeScript React |        320 |         61 |         44 |        425 |
+| src/client/DocServer.ts                                                                                     | TypeScript       |        279 |        136 |         65 |        480 |
+| src/client/Network.ts                                                                                       | TypeScript       |         34 |          0 |          5 |         39 |
+| src/client/apis/GoogleAuthenticationManager.scss                                                            | SCSS             |         16 |          0 |          3 |         19 |
+| src/client/apis/GoogleAuthenticationManager.tsx                                                             | TypeScript React |        115 |          2 |         11 |        128 |
+| src/client/apis/IBM_Recommender.ts                                                                          | TypeScript       |          0 |         33 |          7 |         40 |
+| src/client/apis/google_docs/GoogleApiClientUtils.ts                                                         | TypeScript       |        225 |          9 |         27 |        261 |
+| src/client/apis/google_docs/GooglePhotosClientUtils.ts                                                      | TypeScript       |        318 |          0 |         46 |        364 |
+| src/client/apis/youtube/YoutubeBox.scss                                                                     | SCSS             |        105 |          5 |         16 |        126 |
+| src/client/apis/youtube/YoutubeBox.tsx                                                                      | TypeScript React |        274 |         48 |         40 |        362 |
+| src/client/cognitive_services/CognitiveServices.ts                                                          | TypeScript       |        342 |         10 |         57 |        409 |
+| src/client/documents/DocumentTypes.ts                                                                       | TypeScript       |         32 |          2 |          3 |         37 |
+| src/client/documents/Documents.ts                                                                           | TypeScript       |        807 |        141 |         87 |      1,035 |
+| src/client/goldenLayout.d.ts                                                                                | TypeScript       |          2 |          0 |          1 |          3 |
+| src/client/goldenLayout.js                                                                                  | JavaScript       |      3,084 |      1,571 |        720 |      5,375 |
+| src/client/util/DictationManager.ts                                                                         | TypeScript       |        312 |         25 |         51 |        388 |
+| src/client/util/DocumentManager.ts                                                                          | TypeScript       |        207 |         16 |         21 |        244 |
+| src/client/util/DragManager.ts                                                                              | TypeScript       |        504 |         11 |         35 |        550 |
+| src/client/util/DropConverter.ts                                                                            | TypeScript       |         70 |          6 |          2 |         78 |
+| src/client/util/History.ts                                                                                  | TypeScript       |        166 |         13 |         27 |        206 |
+| src/client/util/Import & Export/DirectoryImportBox.scss                                                     | SCSS             |          6 |          0 |          0 |          6 |
+| src/client/util/Import & Export/DirectoryImportBox.tsx                                                      | TypeScript React |        393 |          0 |         31 |        424 |
+| src/client/util/Import & Export/ImageUtils.ts                                                               | TypeScript       |         35 |          0 |          4 |         39 |
+| src/client/util/Import & Export/ImportMetadataEntry.tsx                                                     | TypeScript React |        132 |          0 |         17 |        149 |
+| src/client/util/InteractionUtils.tsx                                                                        | TypeScript React |        122 |        112 |         21 |        255 |
+| src/client/util/KeyCodes.ts                                                                                 | TypeScript       |        100 |         36 |          0 |        136 |
+| src/client/util/LinkManager.ts                                                                              | TypeScript       |        161 |         30 |         23 |        214 |
+| src/client/util/ProsemirrorCopy/prompt.js                                                                   | JavaScript       |        128 |         30 |         22 |        180 |
+| src/client/util/Scripting.ts                                                                                | TypeScript       |        247 |         15 |         30 |        292 |
+| src/client/util/ScrollBox.tsx                                                                               | TypeScript React |         19 |          0 |          2 |         21 |
+| src/client/util/SearchUtil.ts                                                                               | TypeScript       |        123 |          4 |         18 |        145 |
+| src/client/util/SelectionManager.ts                                                                         | TypeScript       |         69 |          5 |         15 |         89 |
+| src/client/util/SerializationHelper.ts                                                                      | TypeScript       |        113 |         15 |         15 |        143 |
+| src/client/util/SettingsManager.scss                                                                        | SCSS             |        111 |          0 |         25 |        136 |
+| src/client/util/SettingsManager.tsx                                                                         | TypeScript React |        114 |          0 |         17 |        131 |
+| src/client/util/SharingManager.scss                                                                         | SCSS             |        122 |          0 |         18 |        140 |
+| src/client/util/SharingManager.tsx                                                                          | TypeScript React |        273 |          0 |         25 |        298 |
+| src/client/util/Transform.ts                                                                                | TypeScript       |         76 |          0 |         23 |         99 |
+| src/client/util/TypedEvent.ts                                                                               | TypeScript       |         29 |          3 |          8 |         40 |
+| src/client/util/UndoManager.ts                                                                              | TypeScript       |        167 |          1 |         27 |        195 |
+| src/client/util/clamp.js                                                                                    | JavaScript       |         14 |          0 |          1 |         15 |
+| src/client/util/convertToCSSPTValue.js                                                                      | JavaScript       |         31 |          4 |          8 |         43 |
+| src/client/util/jsx-decl.d.ts                                                                               | TypeScript       |          1 |          0 |          1 |          2 |
+| src/client/util/request-image-size.js                                                                       | JavaScript       |         51 |         10 |         14 |         75 |
+| src/client/util/toCSSLineSpacing.js                                                                         | JavaScript       |         35 |         15 |         14 |         64 |
+| src/client/views/AntimodeMenu.scss                                                                          | SCSS             |         35 |          1 |          6 |         42 |
+| src/client/views/AntimodeMenu.tsx                                                                           | TypeScript React |        121 |         14 |         22 |        157 |
+| src/client/views/ContextMenu.scss                                                                           | SCSS             |        130 |         17 |         14 |        161 |
+| src/client/views/ContextMenu.tsx                                                                            | TypeScript React |        258 |          3 |         33 |        294 |
+| src/client/views/ContextMenuItem.tsx                                                                        | TypeScript React |        107 |          0 |         10 |        117 |
+| src/client/views/DictationOverlay.tsx                                                                       | TypeScript React |         64 |          0 |          7 |         71 |
+| src/client/views/DocComponent.tsx                                                                           | TypeScript React |         87 |         20 |         15 |        122 |
+| src/client/views/DocumentButtonBar.scss                                                                     | SCSS             |         89 |          0 |         16 |        105 |
+| src/client/views/DocumentButtonBar.tsx                                                                      | TypeScript React |        284 |          4 |         27 |        315 |
+| src/client/views/DocumentDecorations.scss                                                                   | SCSS             |        319 |          0 |         46 |        365 |
+| src/client/views/DocumentDecorations.tsx                                                                    | TypeScript React |        479 |          2 |         26 |        507 |
+| src/client/views/EditableView.scss                                                                          | SCSS             |         22 |          0 |          3 |         25 |
+| src/client/views/EditableView.tsx                                                                           | TypeScript React |        149 |         19 |         16 |        184 |
+| src/client/views/GestureOverlay.scss                                                                        | SCSS             |         56 |          0 |          8 |         64 |
+| src/client/views/GestureOverlay.tsx                                                                         | TypeScript React |        711 |         45 |         67 |        823 |
+| src/client/views/GlobalKeyHandler.ts                                                                        | TypeScript       |        232 |         10 |         27 |        269 |
+| src/client/views/InkingControl.scss                                                                         | SCSS             |        127 |          4 |          0 |        131 |
+| src/client/views/InkingControl.tsx                                                                          | TypeScript React |         78 |          3 |         10 |         91 |
+| src/client/views/InkingStroke.scss                                                                          | SCSS             |          7 |          0 |          0 |          7 |
+| src/client/views/InkingStroke.tsx                                                                           | TypeScript React |         63 |          0 |          5 |         68 |
+| src/client/views/KeyphraseQueryView.scss                                                                    | SCSS             |          7 |          0 |          1 |          8 |
+| src/client/views/KeyphraseQueryView.tsx                                                                     | TypeScript React |         30 |          2 |          3 |         35 |
+| src/client/views/Main.scss                                                                                  | SCSS             |         51 |          8 |         10 |         69 |
+| src/client/views/Main.tsx                                                                                   | TypeScript React |         22 |          1 |          2 |         25 |
+| src/client/views/MainView.scss                                                                              | SCSS             |        138 |          1 |         21 |        160 |
+| src/client/views/MainView.tsx                                                                               | TypeScript React |        553 |         13 |         36 |        602 |
+| src/client/views/MainViewModal.scss                                                                         | SCSS             |         24 |          0 |          1 |         25 |
+| src/client/views/MainViewModal.tsx                                                                          | TypeScript React |         39 |          0 |          5 |         44 |
+| src/client/views/MainViewNotifs.scss                                                                        | SCSS             |         17 |          0 |          1 |         18 |
+| src/client/views/MainViewNotifs.tsx                                                                         | TypeScript React |         29 |          0 |          4 |         33 |
+| src/client/views/MetadataEntryMenu.scss                                                                     | SCSS             |         79 |          0 |         14 |         93 |
+| src/client/views/MetadataEntryMenu.tsx                                                                      | TypeScript React |        207 |          0 |         16 |        223 |
+| src/client/views/OCRUtils.ts                                                                                | TypeScript       |          2 |          2 |          4 |          8 |
+| src/client/views/OverlayView.scss                                                                           | SCSS             |         41 |          0 |          6 |         47 |
+| src/client/views/OverlayView.tsx                                                                            | TypeScript React |        194 |          4 |         19 |        217 |
+| src/client/views/Palette.scss                                                                               | SCSS             |         26 |          0 |          4 |         30 |
+| src/client/views/Palette.tsx                                                                                | TypeScript React |         65 |          0 |          5 |         70 |
+| src/client/views/PreviewCursor.scss                                                                         | SCSS             |          9 |          0 |          1 |         10 |
+| src/client/views/PreviewCursor.tsx                                                                          | TypeScript React |        115 |          8 |          9 |        132 |
+| src/client/views/RecommendationsBox.scss                                                                    | SCSS             |         52 |         10 |          8 |         70 |
+| src/client/views/RecommendationsBox.tsx                                                                     | TypeScript React |        116 |         67 |         17 |        200 |
+| src/client/views/ScriptBox.scss                                                                             | SCSS             |         15 |          0 |          2 |         17 |
+| src/client/views/ScriptBox.tsx                                                                              | TypeScript React |        112 |          2 |         12 |        126 |
+| src/client/views/ScriptingRepl.scss                                                                         | SCSS             |         42 |          0 |          9 |         51 |
+| src/client/views/ScriptingRepl.tsx                                                                          | TypeScript React |        220 |          1 |         24 |        245 |
+| src/client/views/SearchDocBox.tsx                                                                           | TypeScript React |        359 |         17 |         55 |        431 |
+| src/client/views/TemplateMenu.scss                                                                          | SCSS             |         46 |          0 |          5 |         51 |
+| src/client/views/TemplateMenu.tsx                                                                           | TypeScript React |        167 |          1 |         14 |        182 |
+| src/client/views/Templates.tsx                                                                              | TypeScript React |         34 |          0 |          8 |         42 |
+| src/client/views/TouchScrollableMenu.tsx                                                                    | TypeScript React |         52 |          0 |          7 |         59 |
+| src/client/views/Touchable.tsx                                                                              | TypeScript React |        172 |         28 |         39 |        239 |
+| src/client/views/_nodeModuleOverrides.scss                                                                  | SCSS             |          9 |          9 |          4 |         22 |
+| src/client/views/animationtimeline/Keyframe.scss                                                            | SCSS             |         83 |          5 |         17 |        105 |
+| src/client/views/animationtimeline/Keyframe.tsx                                                             | TypeScript React |        468 |         50 |         42 |        560 |
+| src/client/views/animationtimeline/Timeline.scss                                                            | SCSS             |        264 |         14 |         44 |        322 |
+| src/client/views/animationtimeline/Timeline.tsx                                                             | TypeScript React |        467 |         97 |         58 |        622 |
+| src/client/views/animationtimeline/TimelineMenu.scss                                                        | SCSS             |         75 |          2 |         17 |         94 |
+| src/client/views/animationtimeline/TimelineMenu.tsx                                                         | TypeScript React |         68 |          0 |         10 |         78 |
+| src/client/views/animationtimeline/TimelineOverview.scss                                                    | SCSS             |         89 |          6 |         12 |        107 |
+| src/client/views/animationtimeline/TimelineOverview.tsx                                                     | TypeScript React |        155 |          0 |         27 |        182 |
+| src/client/views/animationtimeline/Track.scss                                                               | SCSS             |         13 |          0 |          2 |         15 |
+| src/client/views/animationtimeline/Track.tsx                                                                | TypeScript React |        284 |         63 |         33 |        380 |
+| src/client/views/collections/CollectionCarouselView.scss                                                    | SCSS             |         37 |          0 |          1 |         38 |
+| src/client/views/collections/CollectionCarouselView.tsx                                                     | TypeScript React |        110 |          1 |         10 |        121 |
+| src/client/views/collections/CollectionDockingView.scss                                                     | SCSS             |        391 |          7 |         60 |        458 |
+| src/client/views/collections/CollectionDockingView.tsx                                                      | TypeScript React |        707 |         61 |         65 |        833 |
+| src/client/views/collections/CollectionLinearView.scss                                                      | SCSS             |         68 |          0 |         10 |         78 |
+| src/client/views/collections/CollectionLinearView.tsx                                                       | TypeScript React |        125 |          1 |          9 |        135 |
+| src/client/views/collections/CollectionMapView.scss                                                         | SCSS             |         27 |          0 |          3 |         30 |
+| src/client/views/collections/CollectionMapView.tsx                                                          | TypeScript React |        235 |         10 |         18 |        263 |
+| src/client/views/collections/CollectionMasonryViewFieldRow.tsx                                              | TypeScript React |        308 |          0 |         24 |        332 |
+| src/client/views/collections/CollectionPileView.scss                                                        | SCSS             |          8 |          0 |          1 |          9 |
+| src/client/views/collections/CollectionPileView.tsx                                                         | TypeScript React |        112 |          5 |         11 |        128 |
+| src/client/views/collections/CollectionSchemaCells.tsx                                                      | TypeScript React |        274 |         17 |         38 |        329 |
+| src/client/views/collections/CollectionSchemaHeaders.tsx                                                    | TypeScript React |        318 |          5 |         41 |        364 |
+| src/client/views/collections/CollectionSchemaMovableTableHOC.tsx                                            | TypeScript React |        216 |          0 |         27 |        243 |
+| src/client/views/collections/CollectionSchemaView.scss                                                      | SCSS             |        406 |          4 |         88 |        498 |
+| src/client/views/collections/CollectionSchemaView.tsx                                                       | TypeScript React |        651 |         20 |         82 |        753 |
+| src/client/views/collections/CollectionStackingView.scss                                                    | SCSS             |        353 |          1 |         50 |        404 |
+| src/client/views/collections/CollectionStackingView.tsx                                                     | TypeScript React |        430 |          5 |         25 |        460 |
+| src/client/views/collections/CollectionStackingViewFieldColumn.tsx                                          | TypeScript React |        367 |          0 |         27 |        394 |
+| src/client/views/collections/CollectionStaffView.scss                                                       | SCSS             |         12 |          0 |          1 |         13 |
+| src/client/views/collections/CollectionStaffView.tsx                                                        | TypeScript React |         46 |          0 |          7 |         53 |
+| src/client/views/collections/CollectionSubView.tsx                                                          | TypeScript React |        390 |          7 |         25 |        422 |
+| src/client/views/collections/CollectionTimeView.scss                                                        | SCSS             |         80 |          0 |         13 |         93 |
+| src/client/views/collections/CollectionTimeView.tsx                                                         | TypeScript React |        177 |          0 |         15 |        192 |
+| src/client/views/collections/CollectionTreeView.scss                                                        | SCSS             |        125 |          4 |         23 |        152 |
+| src/client/views/collections/CollectionTreeView.tsx                                                         | TypeScript React |        801 |         19 |         40 |        860 |
+| src/client/views/collections/CollectionView.scss                                                            | SCSS             |         70 |          0 |          8 |         78 |
+| src/client/views/collections/CollectionView.tsx                                                             | TypeScript React |        457 |         13 |         34 |        504 |
+| src/client/views/collections/CollectionViewChromes.scss                                                     | SCSS             |        308 |          4 |         45 |        357 |
+| src/client/views/collections/CollectionViewChromes.tsx                                                      | TypeScript React |        393 |         67 |         47 |        507 |
+| src/client/views/collections/KeyRestrictionRow.tsx                                                          | TypeScript React |         49 |          2 |          4 |         55 |
+| src/client/views/collections/ParentDocumentSelector.scss                                                    | SCSS             |         54 |          0 |          2 |         56 |
+| src/client/views/collections/ParentDocumentSelector.tsx                                                     | TypeScript React |        120 |          0 |         11 |        131 |
+| src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx                         | TypeScript React |        422 |         10 |         27 |        459 |
+| src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss                             | SCSS             |         19 |          0 |          1 |         20 |
+| src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx                              | TypeScript React |        110 |          5 |          4 |        119 |
+| src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.scss                            | SCSS             |         11 |          0 |          0 |         11 |
+| src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx                             | TypeScript React |         44 |          0 |          2 |         46 |
+| src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.scss                        | SCSS             |         20 |          1 |          3 |         24 |
+| src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx                         | TypeScript React |         67 |          0 |         12 |         79 |
+| src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss                                 | SCSS             |         95 |          9 |         17 |        121 |
+| src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx                                  | TypeScript React |      1,186 |         47 |         97 |      1,330 |
+| src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx                                      | TypeScript React |         52 |          0 |          5 |         57 |
+| src/client/views/collections/collectionFreeForm/MarqueeView.scss                                            | SCSS             |         30 |          0 |          2 |         32 |
+| src/client/views/collections/collectionFreeForm/MarqueeView.tsx                                             | TypeScript React |        484 |        105 |         33 |        622 |
+| src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.scss                           | SCSS             |         28 |          0 |          6 |         34 |
+| src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx                            | TypeScript React |        202 |         72 |         23 |        297 |
+| src/client/views/collections/collectionMulticolumn/CollectionMultirowView.scss                              | SCSS             |         29 |          0 |          6 |         35 |
+| src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx                               | TypeScript React |        204 |         72 |         22 |        298 |
+| src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx                                   | TypeScript React |         94 |          0 |          9 |        103 |
+| src/client/views/collections/collectionMulticolumn/MulticolumnWidthLabel.tsx                                | TypeScript React |         51 |          0 |          5 |         56 |
+| src/client/views/collections/collectionMulticolumn/MultirowHeightLabel.tsx                                  | TypeScript React |         51 |          0 |          5 |         56 |
+| src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx                                      | TypeScript React |         92 |          0 |          9 |        101 |
+| src/client/views/globalCssVariables.scss                                                                    | SCSS             |         30 |         10 |          3 |         43 |
+| src/client/views/globalCssVariables.scss.d.ts                                                               | TypeScript       |          9 |          0 |          2 |         11 |
+| src/client/views/linking/LinkEditor.scss                                                                    | SCSS             |        124 |          1 |         25 |        150 |
+| src/client/views/linking/LinkEditor.tsx                                                                     | TypeScript React |        252 |          7 |         50 |        309 |
+| src/client/views/linking/LinkMenu.scss                                                                      | SCSS             |         40 |          0 |         13 |         53 |
+| src/client/views/linking/LinkMenu.tsx                                                                       | TypeScript React |         65 |          1 |         10 |         76 |
+| src/client/views/linking/LinkMenuGroup.tsx                                                                  | TypeScript React |         84 |          0 |         10 |         94 |
+| src/client/views/linking/LinkMenuItem.scss                                                                  | SCSS             |         75 |          1 |         11 |         87 |
+| src/client/views/linking/LinkMenuItem.tsx                                                                   | TypeScript React |        107 |          0 |         19 |        126 |
+| src/client/views/nodes/AudioBox.scss                                                                        | SCSS             |        146 |          0 |          0 |        146 |
+| src/client/views/nodes/AudioBox.tsx                                                                         | TypeScript React |        261 |          2 |         24 |        287 |
+| src/client/views/nodes/CollectionFreeFormDocumentView.scss                                                  | SCSS             |          8 |          0 |          0 |          8 |
+| src/client/views/nodes/CollectionFreeFormDocumentView.tsx                                                   | TypeScript React |        124 |          0 |          7 |        131 |
+| src/client/views/nodes/ColorBox.scss                                                                        | SCSS             |         22 |          0 |          1 |         23 |
+| src/client/views/nodes/ColorBox.tsx                                                                         | TypeScript React |         28 |          0 |          4 |         32 |
+| src/client/views/nodes/ContentFittingDocumentView.scss                                                      | SCSS             |         20 |          0 |          4 |         24 |
+| src/client/views/nodes/ContentFittingDocumentView.tsx                                                       | TypeScript React |        117 |          0 |          7 |        124 |
+| src/client/views/nodes/DocumentBox.scss                                                                     | SCSS             |         14 |          0 |          0 |         14 |
+| src/client/views/nodes/DocumentBox.tsx                                                                      | TypeScript React |        154 |          0 |          4 |        158 |
+| src/client/views/nodes/DocumentContentsView.tsx                                                             | TypeScript React |        183 |         10 |         17 |        210 |
+| src/client/views/nodes/DocumentIcon.tsx                                                                     | TypeScript React |         60 |          0 |          5 |         65 |
+| src/client/views/nodes/DocumentView.scss                                                                    | SCSS             |        110 |          1 |         15 |        126 |
+| src/client/views/nodes/DocumentView.tsx                                                                     | TypeScript React |      1,041 |         54 |         94 |      1,189 |
+| src/client/views/nodes/FaceRectangle.tsx                                                                    | TypeScript React |         25 |          0 |          4 |         29 |
+| src/client/views/nodes/FaceRectangles.tsx                                                                   | TypeScript React |         41 |          0 |          5 |         46 |
+| src/client/views/nodes/FieldTextBox.scss                                                                    | SCSS             |         12 |          0 |          3 |         15 |
+| src/client/views/nodes/FieldView.tsx                                                                        | TypeScript React |         89 |         43 |          4 |        136 |
+| src/client/views/nodes/FontIconBox.scss                                                                     | SCSS             |         25 |          0 |          2 |         27 |
+| src/client/views/nodes/FontIconBox.tsx                                                                      | TypeScript React |         58 |          0 |          5 |         63 |
+| src/client/views/nodes/ImageBox.scss                                                                        | SCSS             |        135 |          0 |         17 |        152 |
+| src/client/views/nodes/ImageBox.tsx                                                                         | TypeScript React |        430 |         10 |         35 |        475 |
+| src/client/views/nodes/KeyValueBox.scss                                                                     | SCSS             |        120 |          0 |          3 |        123 |
+| src/client/views/nodes/KeyValueBox.tsx                                                                      | TypeScript React |        244 |          1 |         26 |        271 |
+| src/client/views/nodes/KeyValuePair.scss                                                                    | SCSS             |         55 |          1 |          4 |         60 |
+| src/client/views/nodes/KeyValuePair.tsx                                                                     | TypeScript React |        125 |          2 |          8 |        135 |
+| src/client/views/nodes/LabelBox.scss                                                                        | SCSS             |         31 |          0 |          4 |         35 |
+| src/client/views/nodes/LabelBox.tsx                                                                         | TypeScript React |         86 |          1 |          9 |         96 |
+| src/client/views/nodes/LinkAnchorBox.scss                                                                   | SCSS             |         27 |          0 |          2 |         29 |
+| src/client/views/nodes/LinkAnchorBox.tsx                                                                    | TypeScript React |        141 |          0 |          9 |        150 |
+| src/client/views/nodes/LinkBox.scss                                                                         | SCSS             |          3 |          0 |          0 |          3 |
+| src/client/views/nodes/LinkBox.tsx                                                                          | TypeScript React |         33 |          0 |          3 |         36 |
+| src/client/views/nodes/PDFBox.scss                                                                          | SCSS             |        200 |          0 |         19 |        219 |
+| src/client/views/nodes/PDFBox.tsx                                                                           | TypeScript React |        246 |          0 |         19 |        265 |
+| src/client/views/nodes/PresBox.scss                                                                         | SCSS             |         52 |          0 |          1 |         53 |
+| src/client/views/nodes/PresBox.tsx                                                                          | TypeScript React |        268 |         31 |         32 |        331 |
+| src/client/views/nodes/QueryBox.scss                                                                        | SCSS             |          5 |          0 |          0 |          5 |
+| src/client/views/nodes/QueryBox.tsx                                                                         | TypeScript React |         37 |          0 |          4 |         41 |
+| src/client/views/nodes/RadialMenu.scss                                                                      | SCSS             |         60 |          3 |          7 |         70 |
+| src/client/views/nodes/RadialMenu.tsx                                                                       | TypeScript React |        174 |         26 |         36 |        236 |
+| src/client/views/nodes/RadialMenuItem.tsx                                                                   | TypeScript React |        101 |          0 |         16 |        117 |
+| src/client/views/nodes/ScreenshotBox.scss                                                                   | SCSS             |         40 |          6 |          5 |         51 |
+| src/client/views/nodes/ScreenshotBox.tsx                                                                    | TypeScript React |        174 |          2 |         18 |        194 |
+| src/client/views/nodes/ScriptingBox.scss                                                                    | SCSS             |         33 |          0 |          3 |         36 |
+| src/client/views/nodes/ScriptingBox.tsx                                                                     | TypeScript React |         87 |          0 |         12 |         99 |
+| src/client/views/nodes/SliderBox-components.tsx                                                             | TypeScript React |        220 |         12 |         25 |        257 |
+| src/client/views/nodes/SliderBox-tooltip.css                                                                | CSS              |         30 |          0 |          3 |         33 |
+| src/client/views/nodes/SliderBox.scss                                                                       | SCSS             |          7 |          0 |          0 |          7 |
+| src/client/views/nodes/SliderBox.tsx                                                                        | TypeScript React |        117 |          0 |          8 |        125 |
+| src/client/views/nodes/VideoBox.scss                                                                        | SCSS             |         65 |          3 |          6 |         74 |
+| src/client/views/nodes/VideoBox.tsx                                                                         | TypeScript React |        343 |          2 |         34 |        379 |
+| src/client/views/nodes/WebBox.scss                                                                          | SCSS             |        109 |          0 |         18 |        127 |
+| src/client/views/nodes/WebBox.tsx                                                                           | TypeScript React |        345 |         15 |         35 |        395 |
+| src/client/views/nodes/formattedText/DashDocCommentView.tsx                                                 | TypeScript React |         82 |          0 |         13 |         95 |
+| src/client/views/nodes/formattedText/DashDocView.tsx                                                        | TypeScript React |        223 |          9 |         37 |        269 |
+| src/client/views/nodes/formattedText/DashFieldView.scss                                                     | SCSS             |         34 |          0 |          2 |         36 |
+| src/client/views/nodes/formattedText/DashFieldView.tsx                                                      | TypeScript React |        178 |         13 |         20 |        211 |
+| src/client/views/nodes/formattedText/FootnoteView.tsx                                                       | TypeScript React |        131 |         13 |         19 |        163 |
+| src/client/views/nodes/formattedText/FormattedTextBox.scss                                                  | SCSS             |        220 |         11 |         34 |        265 |
+| src/client/views/nodes/formattedText/FormattedTextBox.tsx                                                   | TypeScript React |      1,169 |         68 |         93 |      1,330 |
+| src/client/views/nodes/formattedText/FormattedTextBoxComment.scss                                           | SCSS             |         33 |          0 |          0 |         33 |
+| src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx                                            | TypeScript React |        218 |         11 |          8 |        237 |
+| src/client/views/nodes/formattedText/ImageResizeView.tsx                                                    | TypeScript React |        113 |          0 |         25 |        138 |
+| src/client/views/nodes/formattedText/ParagraphNodeSpec.ts                                                   | TypeScript       |        108 |          9 |         26 |        143 |
+| src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts                                          | TypeScript       |        231 |          0 |         11 |        242 |
+| src/client/views/nodes/formattedText/RichTextMenu.scss                                                      | SCSS             |        100 |          2 |         19 |        121 |
+| src/client/views/nodes/formattedText/RichTextMenu.tsx                                                       | TypeScript React |        754 |         12 |        109 |        875 |
+| src/client/views/nodes/formattedText/RichTextRules.ts                                                       | TypeScript       |        308 |          7 |          5 |        320 |
+| src/client/views/nodes/formattedText/RichTextSchema.tsx                                                     | TypeScript React |        465 |         33 |         39 |        537 |
+| src/client/views/nodes/formattedText/SummaryView.tsx                                                        | TypeScript React |         67 |          0 |         14 |         81 |
+| src/client/views/nodes/formattedText/TooltipTextMenu.scss                                                   | SCSS             |        306 |          6 |         61 |        373 |
+| src/client/views/nodes/formattedText/marks_rts.ts                                                           | TypeScript       |        259 |         15 |         23 |        297 |
+| src/client/views/nodes/formattedText/nodes_rts.ts                                                           | TypeScript       |        224 |         21 |         19 |        264 |
+| src/client/views/nodes/formattedText/prosemirrorPatches.js                                                  | JavaScript       |        118 |         12 |          9 |        139 |
+| src/client/views/nodes/formattedText/schema_rts.ts                                                          | TypeScript       |         12 |          8 |          6 |         26 |
+| src/client/views/pdf/Annotation.scss                                                                        | SCSS             |          6 |          0 |          0 |          6 |
+| src/client/views/pdf/Annotation.tsx                                                                         | TypeScript React |        114 |          0 |         16 |        130 |
+| src/client/views/pdf/PDFMenu.scss                                                                           | SCSS             |          6 |          0 |          0 |          6 |
+| src/client/views/pdf/PDFMenu.tsx                                                                            | TypeScript React |        102 |          0 |         21 |        123 |
+| src/client/views/pdf/PDFViewer.scss                                                                         | SCSS             |         74 |          4 |         10 |         88 |
+| src/client/views/pdf/PDFViewer.tsx                                                                          | TypeScript React |        662 |         23 |         46 |        731 |
+| src/client/views/presentationview/PresElementBox.scss                                                       | SCSS             |         93 |          0 |         10 |        103 |
+| src/client/views/presentationview/PresElementBox.tsx                                                        | TypeScript React |        179 |         31 |         14 |        224 |
+| src/client/views/search/CheckBox.scss                                                                       | SCSS             |         50 |          1 |          8 |         59 |
+| src/client/views/search/CheckBox.tsx                                                                        | TypeScript React |         42 |         74 |         15 |        131 |
+| src/client/views/search/CollectionFilters.scss                                                              | SCSS             |         17 |          0 |          3 |         20 |
+| src/client/views/search/CollectionFilters.tsx                                                               | TypeScript React |         69 |          0 |         14 |         83 |
+| src/client/views/search/FieldFilters.scss                                                                   | SCSS             |         10 |          1 |          1 |         12 |
+| src/client/views/search/FieldFilters.tsx                                                                    | TypeScript React |         34 |          0 |          7 |         41 |
+| src/client/views/search/FilterBox.scss                                                                      | SCSS             |        153 |          0 |         25 |        178 |
+| src/client/views/search/FilterBox.tsx                                                                       | TypeScript React |        354 |         24 |         54 |        432 |
+| src/client/views/search/IconBar.scss                                                                        | SCSS             |          9 |          0 |          1 |         10 |
+| src/client/views/search/IconBar.tsx                                                                         | TypeScript React |         69 |          1 |         17 |         87 |
+| src/client/views/search/IconButton.scss                                                                     | SCSS             |         46 |          1 |          6 |         53 |
+| src/client/views/search/IconButton.tsx                                                                      | TypeScript React |        170 |          2 |         19 |        191 |
+| src/client/views/search/NaviconButton.scss                                                                  | SCSS             |         58 |          0 |         11 |         69 |
+| src/client/views/search/NaviconButton.tsx                                                                   | TypeScript React |         32 |          0 |          5 |         37 |
+| src/client/views/search/SearchBox.scss                                                                      | SCSS             |        203 |         82 |         51 |        336 |
+| src/client/views/search/SearchBox.tsx                                                                       | TypeScript React |        530 |         47 |         94 |        671 |
+| src/client/views/search/SearchItem.scss                                                                     | SCSS             |        138 |          0 |         25 |        163 |
+| src/client/views/search/SearchItem.tsx                                                                      | TypeScript React |        272 |          2 |         29 |        303 |
+| src/client/views/search/SelectorContextMenu.scss                                                            | SCSS             |         12 |          1 |          3 |         16 |
+| src/client/views/search/ToggleBar.scss                                                                      | SCSS             |         35 |          2 |          4 |         41 |
+| src/client/views/search/ToggleBar.tsx                                                                       | TypeScript React |         77 |          0 |          9 |         86 |
+| src/client/views/webcam/DashWebRTCVideo.scss                                                                | SCSS             |         70 |          4 |          9 |         83 |
+| src/client/views/webcam/DashWebRTCVideo.tsx                                                                 | TypeScript React |         67 |          6 |         16 |         89 |
+| src/client/views/webcam/WebCamLogic.js                                                                      | JavaScript       |        234 |          7 |         51 |        292 |
+| src/debug/Repl.tsx                                                                                          | TypeScript React |         59 |          0 |          7 |         66 |
+| src/debug/Test.tsx                                                                                          | TypeScript React |         12 |          0 |          2 |         14 |
+| src/debug/Viewer.tsx                                                                                        | TypeScript React |        173 |          0 |         19 |        192 |
+| src/extensions/ArrayExtensions.ts                                                                           | TypeScript       |         26 |          5 |          6 |         37 |
+| src/extensions/General/Extensions.ts                                                                        | TypeScript       |          7 |          0 |          2 |          9 |
+| src/extensions/General/ExtensionsTypings.ts                                                                 | TypeScript       |          7 |          0 |          1 |          8 |
+| src/extensions/StringExtensions.ts                                                                          | TypeScript       |         13 |          0 |          4 |         17 |
+| src/mobile/ImageUpload.scss                                                                                 | SCSS             |         30 |          0 |          4 |         34 |
+| src/mobile/ImageUpload.tsx                                                                                  | TypeScript React |         78 |         41 |         12 |        131 |
+| src/mobile/InkControls.tsx                                                                                  | TypeScript React |          0 |          0 |          1 |          1 |
+| src/mobile/MobileInkOverlay.scss                                                                            | SCSS             |         33 |          1 |          5 |         39 |
+| src/mobile/MobileInkOverlay.tsx                                                                             | TypeScript React |        162 |          3 |         26 |        191 |
+| src/mobile/MobileInterface.scss                                                                             | SCSS             |         17 |          0 |          2 |         19 |
+| src/mobile/MobileInterface.tsx                                                                              | TypeScript React |        297 |         15 |         32 |        344 |
+| src/new_fields/CursorField.ts                                                                               | TypeScript       |         54 |          0 |         12 |         66 |
+| src/new_fields/DateField.ts                                                                                 | TypeScript       |         30 |          0 |          7 |         37 |
+| src/new_fields/Doc.ts                                                                                       | TypeScript       |        897 |         85 |         76 |      1,058 |
+| src/new_fields/FieldSymbols.ts                                                                              | TypeScript       |         11 |          0 |          2 |         13 |
+| src/new_fields/HtmlField.ts                                                                                 | TypeScript       |         22 |          0 |          5 |         27 |
+| src/new_fields/IconField.ts                                                                                 | TypeScript       |         22 |          0 |          5 |         27 |
+| src/new_fields/InkField.ts                                                                                  | TypeScript       |         41 |          0 |         10 |         51 |
+| src/new_fields/List.ts                                                                                      | TypeScript       |        244 |         38 |         20 |        302 |
+| src/new_fields/ListSpec.ts                                                                                  | TypeScript       |          0 |          0 |          1 |          1 |
+| src/new_fields/ObjectField.ts                                                                               | TypeScript       |         16 |          0 |          4 |         20 |
+| src/new_fields/PresField.ts                                                                                 | TypeScript       |          3 |          1 |          2 |          6 |
+| src/new_fields/Proxy.ts                                                                                     | TypeScript       |         95 |          2 |         14 |        111 |
+| src/new_fields/RefField.ts                                                                                  | TypeScript       |         17 |          0 |          5 |         22 |
+| src/new_fields/RichTextField.ts                                                                             | TypeScript       |         33 |          0 |          8 |         41 |
+| src/new_fields/RichTextUtils.ts                                                                             | TypeScript       |        455 |          8 |         56 |        519 |
+| src/new_fields/Schema.ts                                                                                    | TypeScript       |        107 |          5 |          8 |        120 |
+| src/new_fields/SchemaHeaderField.ts                                                                         | TypeScript       |        104 |          4 |         14 |        122 |
+| src/new_fields/ScriptField.ts                                                                               | TypeScript       |        137 |         21 |         19 |        177 |
+| src/new_fields/Types.ts                                                                                     | TypeScript       |         86 |          5 |         17 |        108 |
+| src/new_fields/URLField.ts                                                                                  | TypeScript       |         45 |          0 |          9 |         54 |
+| src/new_fields/documentSchemas.ts                                                                           | TypeScript       |         87 |          0 |          6 |         93 |
+| src/new_fields/util.ts                                                                                      | TypeScript       |        176 |          3 |         15 |        194 |
+| src/pen-gestures/GestureUtils.ts                                                                            | TypeScript       |         41 |          0 |          5 |         46 |
+| src/pen-gestures/ndollar.ts                                                                                 | TypeScript       |        356 |        172 |         22 |        550 |
+| src/scraping/acm/.gitignore                                                                                 | Ignore           |          2 |          0 |          0 |          2 |
+| src/scraping/acm/debug.log                                                                                  | log              |         38 |          0 |          1 |         39 |
+| src/scraping/acm/index.js                                                                                   | JavaScript       |         82 |        185 |         13 |        280 |
+| src/scraping/acm/package.json                                                                               | JSON             |         17 |          0 |          1 |         18 |
+| src/scraping/buxton/.idea/buxton.iml                                                                        | XML              |          8 |          0 |          0 |          8 |
+| src/scraping/buxton/.idea/inspectionProfiles/profiles_settings.xml                                          | XML              |          6 |          0 |          0 |          6 |
+| src/scraping/buxton/.idea/misc.xml                                                                          | XML              |          4 |          0 |          0 |          4 |
+| src/scraping/buxton/.idea/modules.xml                                                                       | XML              |          8 |          0 |          0 |          8 |
+| src/scraping/buxton/.idea/vcs.xml                                                                           | XML              |          6 |          0 |          0 |          6 |
+| src/scraping/buxton/.idea/workspace.xml                                                                     | XML              |        173 |          0 |          0 |        173 |
+| src/scraping/buxton/final/BuxtonImporter.ts                                                                 | TypeScript       |        228 |        142 |         26 |        396 |
+| src/scraping/buxton/jsonifier.py                                                                            | Python           |        183 |          1 |         48 |        232 |
+| src/scraping/buxton/narratives.py                                                                           | Python           |         11 |         19 |          9 |         39 |
+| src/scraping/buxton/narratives/chord_keyboards.json                                                         | JSON             |         39 |          0 |          0 |         39 |
+| src/scraping/buxton/scraper.py                                                                              | Python           |        350 |          5 |         78 |        433 |
+| src/server/ActionUtilities.ts                                                                               | TypeScript       |        136 |          1 |         23 |        160 |
+| src/server/ApiManagers/ApiManager.ts                                                                        | TypeScript       |          8 |          0 |          3 |         11 |
+| src/server/ApiManagers/DeleteManager.ts                                                                     | TypeScript       |         71 |          0 |         11 |         82 |
+| src/server/ApiManagers/DownloadManager.ts                                                                   | TypeScript       |        173 |         80 |         16 |        269 |
+| src/server/ApiManagers/GeneralGoogleManager.ts                                                              | TypeScript       |         53 |          0 |          8 |         61 |
+| src/server/ApiManagers/GooglePhotosManager.ts                                                               | TypeScript       |        190 |        119 |         22 |        331 |
+| src/server/ApiManagers/PDFManager.ts                                                                        | TypeScript       |        103 |          0 |         12 |        115 |
+| src/server/ApiManagers/SearchManager.ts                                                                     | TypeScript       |        200 |          0 |         15 |        215 |
+| src/server/ApiManagers/SessionManager.ts                                                                    | TypeScript       |         56 |          0 |         11 |         67 |
+| src/server/ApiManagers/UploadManager.ts                                                                     | TypeScript       |        226 |          1 |         20 |        247 |
+| src/server/ApiManagers/UserManager.ts                                                                       | TypeScript       |        101 |          4 |         21 |        126 |
+| src/server/ApiManagers/UtilManager.ts                                                                       | TypeScript       |         42 |         22 |         10 |         74 |
+| src/server/Client.ts                                                                                        | TypeScript       |          8 |          0 |          3 |         11 |
+| src/server/DashSession/DashSessionAgent.ts                                                                  | TypeScript       |        155 |         52 |         23 |        230 |
+| src/server/DashSession/Session/agents/applied_session_agent.ts                                              | TypeScript       |         47 |          2 |          9 |         58 |
+| src/server/DashSession/Session/agents/monitor.ts                                                            | TypeScript       |        213 |         59 |         26 |        298 |
+| src/server/DashSession/Session/agents/process_message_router.ts                                             | TypeScript       |         24 |         10 |          7 |         41 |
+| src/server/DashSession/Session/agents/promisified_ipc_manager.ts                                            | TypeScript       |        106 |         52 |         15 |        173 |
+| src/server/DashSession/Session/agents/server_worker.ts                                                      | TypeScript       |         99 |         46 |         15 |        160 |
+| src/server/DashSession/Session/utilities/repl.ts                                                            | TypeScript       |        116 |          0 |         12 |        128 |
+| src/server/DashSession/Session/utilities/session_config.ts                                                  | TypeScript       |        119 |          0 |         10 |        129 |
+| src/server/DashSession/Session/utilities/utilities.ts                                                       | TypeScript       |         24 |          8 |          5 |         37 |
+| src/server/DashUploadUtils.ts                                                                               | TypeScript       |        285 |         52 |         30 |        367 |
+| src/server/GarbageCollector.ts                                                                              | TypeScript       |        138 |          2 |         11 |        151 |
+| src/server/IDatabase.ts                                                                                     | TypeScript       |         17 |          0 |          8 |         25 |
+| src/server/MemoryDatabase.ts                                                                                | TypeScript       |         87 |          0 |         14 |        101 |
+| src/server/Message.ts                                                                                       | TypeScript       |         86 |          0 |         18 |        104 |
+| src/server/PdfTypes.ts                                                                                      | TypeScript       |         19 |          0 |          2 |         21 |
+| src/server/ProcessFactory.ts                                                                                | TypeScript       |         34 |          0 |         10 |         44 |
+| src/server/Recommender.ts                                                                                   | TypeScript       |          0 |        120 |         18 |        138 |
+| src/server/RouteManager.ts                                                                                  | TypeScript       |        187 |          4 |         19 |        210 |
+| src/server/RouteSubscriber.ts                                                                               | TypeScript       |         21 |          0 |          5 |         26 |
+| src/server/Search.ts                                                                                        | TypeScript       |         71 |          2 |          8 |         81 |
+| src/server/SharedMediaTypes.ts                                                                              | TypeScript       |         41 |          0 |         10 |         51 |
+| src/server/Websocket/Websocket.ts                                                                           | TypeScript       |        263 |          5 |         46 |        314 |
+| src/server/apis/google/GoogleApiServerUtils.ts                                                              | TypeScript       |        172 |        168 |         25 |        365 |
+| src/server/apis/google/SharedTypes.ts                                                                       | TypeScript       |         19 |          0 |          2 |         21 |
+| src/server/apis/youtube/youtubeApiSample.d.ts                                                               | TypeScript       |          2 |          0 |          0 |          2 |
+| src/server/apis/youtube/youtubeApiSample.js                                                                 | JavaScript       |        135 |         30 |         14 |        179 |
+| src/server/authentication/config/passport.ts                                                                | TypeScript       |         23 |          2 |          4 |         29 |
+| src/server/authentication/controllers/user_controller.ts                                                    | TypeScript       |        218 |         25 |         25 |        268 |
+| src/server/authentication/models/current_user_utils.ts                                                      | TypeScript       |        586 |         30 |         57 |        673 |
+| src/server/authentication/models/user_model.ts                                                              | TypeScript       |         63 |          9 |         14 |         86 |
+| src/server/credentials/CredentialsLoader.ts                                                                 | TypeScript       |         24 |          0 |          6 |         30 |
+| src/server/credentials/google_project_credentials.json                                                      | JSON             |         11 |          0 |          0 |         11 |
+| src/server/database.ts                                                                                      | TypeScript       |        312 |          0 |         38 |        350 |
+| src/server/downsize.ts                                                                                      | TypeScript       |         34 |          5 |          1 |         40 |
+| src/server/index.ts                                                                                         | TypeScript       |        107 |         36 |         16 |        159 |
+| src/server/remapUrl.ts                                                                                      | TypeScript       |         53 |          4 |          6 |         63 |
+| src/server/server_Initialization.ts                                                                         | TypeScript       |        138 |          6 |         24 |        168 |
+| src/server/slides.json                                                                                      | JSON             |     10,820 |          0 |          0 |     10,820 |
+| src/server/updateProtos.ts                                                                                  | TypeScript       |         11 |          0 |          3 |         14 |
+| src/typings/index.d.ts                                                                                      | TypeScript       |        219 |         72 |         38 |        329 |
+| test/test.ts                                                                                                | TypeScript       |        141 |          0 |         20 |        161 |
+| tsconfig.json                                                                                               | JSON             |         21 |          5 |          0 |         26 |
+| tslint.json                                                                                                 | JSON             |         30 |         32 |          1 |         63 |
+| views/forgot.pug                                                                                            | Pug              |         19 |          1 |          2 |         22 |
+| views/layout.pug                                                                                            | Pug              |         13 |          0 |          1 |         14 |
+| views/login.pug                                                                                             | Pug              |         24 |          0 |          2 |         26 |
+| views/reset.pug                                                                                             | Pug              |         20 |          0 |          2 |         22 |
+| views/signup.pug                                                                                            | Pug              |         25 |          0 |          2 |         27 |
+| views/stylesheets/authentication.css                                                                        | CSS              |        185 |          4 |         34 |        223 |
+| views/user_activity.pug                                                                                     | Pug              |         18 |          0 |          1 |         19 |
+| webpack.config.js                                                                                           | JavaScript       |        116 |          0 |          5 |        121 |
+| Total                                                                                                       |                  |    224,911 |     32,987 |     15,880 |    273,778 |
++-------------------------------------------------------------------------------------------------------------+------------------+------------+------------+------------+------------+
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index bc8ca01f5..959fc41ef 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -2723,6 +2723,43 @@
                 }
             }
         },
+        "canvas": {
+            "version": "2.6.1",
+            "resolved": "https://registry.npmjs.org/canvas/-/canvas-2.6.1.tgz",
+            "integrity": "sha512-S98rKsPcuhfTcYbtF53UIJhcbgIAK533d1kJKMwsMwAIFgfd58MOyxRud3kktlzWiEkFliaJtvyZCBtud/XVEA==",
+            "requires": {
+                "nan": "^2.14.0",
+                "node-pre-gyp": "^0.11.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"
+                    }
+                }
+            }
+        },
         "capture-stack-trace": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.1.tgz",
@@ -2848,8 +2885,7 @@
                         },
                         "ansi-regex": {
                             "version": "2.1.1",
-                            "bundled": true,
-                            "optional": true
+                            "bundled": true
                         },
                         "aproba": {
                             "version": "1.2.0",
@@ -2867,13 +2903,11 @@
                         },
                         "balanced-match": {
                             "version": "1.0.0",
-                            "bundled": true,
-                            "optional": true
+                            "bundled": true
                         },
                         "brace-expansion": {
                             "version": "1.1.11",
                             "bundled": true,
-                            "optional": true,
                             "requires": {
                                 "balanced-match": "^1.0.0",
                                 "concat-map": "0.0.1"
@@ -2886,18 +2920,15 @@
                         },
                         "code-point-at": {
                             "version": "1.1.0",
-                            "bundled": true,
-                            "optional": true
+                            "bundled": true
                         },
                         "concat-map": {
                             "version": "0.0.1",
-                            "bundled": true,
-                            "optional": true
+                            "bundled": true
                         },
                         "console-control-strings": {
                             "version": "1.1.0",
-                            "bundled": true,
-                            "optional": true
+                            "bundled": true
                         },
                         "core-util-is": {
                             "version": "1.0.2",
@@ -3000,8 +3031,7 @@
                         },
                         "inherits": {
                             "version": "2.0.4",
-                            "bundled": true,
-                            "optional": true
+                            "bundled": true
                         },
                         "ini": {
                             "version": "1.3.5",
@@ -3011,7 +3041,6 @@
                         "is-fullwidth-code-point": {
                             "version": "1.0.0",
                             "bundled": true,
-                            "optional": true,
                             "requires": {
                                 "number-is-nan": "^1.0.0"
                             }
@@ -3024,20 +3053,17 @@
                         "minimatch": {
                             "version": "3.0.4",
                             "bundled": true,
-                            "optional": true,
                             "requires": {
                                 "brace-expansion": "^1.1.7"
                             }
                         },
                         "minimist": {
                             "version": "1.2.5",
-                            "bundled": true,
-                            "optional": true
+                            "bundled": true
                         },
                         "minipass": {
                             "version": "2.9.0",
                             "bundled": true,
-                            "optional": true,
                             "requires": {
                                 "safe-buffer": "^5.1.2",
                                 "yallist": "^3.0.0"
@@ -3054,7 +3080,6 @@
                         "mkdirp": {
                             "version": "0.5.3",
                             "bundled": true,
-                            "optional": true,
                             "requires": {
                                 "minimist": "^1.2.5"
                             }
@@ -3110,8 +3135,7 @@
                         },
                         "npm-normalize-package-bin": {
                             "version": "1.0.1",
-                            "bundled": true,
-                            "optional": true
+                            "bundled": true
                         },
                         "npm-packlist": {
                             "version": "1.4.8",
@@ -3136,8 +3160,7 @@
                         },
                         "number-is-nan": {
                             "version": "1.0.1",
-                            "bundled": true,
-                            "optional": true
+                            "bundled": true
                         },
                         "object-assign": {
                             "version": "4.1.1",
@@ -3147,7 +3170,6 @@
                         "once": {
                             "version": "1.4.0",
                             "bundled": true,
-                            "optional": true,
                             "requires": {
                                 "wrappy": "1"
                             }
@@ -3216,8 +3238,7 @@
                         },
                         "safe-buffer": {
                             "version": "5.1.2",
-                            "bundled": true,
-                            "optional": true
+                            "bundled": true
                         },
                         "safer-buffer": {
                             "version": "2.1.2",
@@ -3247,7 +3268,6 @@
                         "string-width": {
                             "version": "1.0.2",
                             "bundled": true,
-                            "optional": true,
                             "requires": {
                                 "code-point-at": "^1.0.0",
                                 "is-fullwidth-code-point": "^1.0.0",
@@ -3265,7 +3285,6 @@
                         "strip-ansi": {
                             "version": "3.0.1",
                             "bundled": true,
-                            "optional": true,
                             "requires": {
                                 "ansi-regex": "^2.0.0"
                             }
@@ -3304,13 +3323,11 @@
                         },
                         "wrappy": {
                             "version": "1.0.2",
-                            "bundled": true,
-                            "optional": true
+                            "bundled": true
                         },
                         "yallist": {
                             "version": "3.1.1",
-                            "bundled": true,
-                            "optional": true
+                            "bundled": true
                         }
                     }
                 }
diff --git a/src/Utils.ts b/src/Utils.ts
index ad12c68a1..23b59ac9d 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -4,7 +4,7 @@ import { Socket, Room } from 'socket.io';
 import { Message } from './server/Message';
 
 export namespace Utils {
-    export const DRAG_THRESHOLD = 4;
+    export let DRAG_THRESHOLD = 4;
 
     export function GenerateGuid(): string {
         return v4();
@@ -512,7 +512,7 @@ export function setupMoveUpEvents(
     (target as any)._downY = (target as any)._lastY = e.clientY;
 
     const _moveEvent = (e: PointerEvent): void => {
-        if (Math.abs(e.clientX - (target as any)._downX) > 4 || Math.abs(e.clientY - (target as any)._downY) > 4) {
+        if (Math.abs(e.clientX - (target as any)._downX) > Utils.DRAG_THRESHOLD || Math.abs(e.clientY - (target as any)._downY) > Utils.DRAG_THRESHOLD) {
             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);
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index c03d9ea1b..c48611eff 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -1,5 +1,5 @@
 import { Doc, Field, DocListCast } from "../../new_fields/Doc";
-import { Cast, ScriptCast, StrCast } from "../../new_fields/Types";
+import { Cast, ScriptCast, StrCast, NumCast } from "../../new_fields/Types";
 import { emptyFunction } from "../../Utils";
 import { CollectionDockingView } from "../views/collections/CollectionDockingView";
 import * as globalCssVariables from "../views/globalCssVariables.scss";
@@ -305,33 +305,20 @@ export namespace DragManager {
     }
 
     export function snapDrag(e: PointerEvent, xFromLeft: number, yFromTop: number, xFromRight: number, yFromBottom: number) {
-        let thisX = e.pageX;
-        let thisY = e.pageY;
-        const currLeft = e.pageX - xFromLeft;
-        const currTop = e.pageY - yFromTop;
-        const currRight = e.pageX + xFromRight;
-        const currBottom = e.pageY + yFromBottom;
-        const closestLeft = vertSnapLines.length ? vertSnapLines.reduce((prev, curr) => Math.abs(prev - currLeft) > Math.abs(curr - currLeft) ? curr : prev) : currLeft;
-        const closestTop = horizSnapLines.length ? horizSnapLines.reduce((prev, curr) => Math.abs(prev - currTop) > Math.abs(curr - currTop) ? curr : prev) : currTop;
-        const closestRight = vertSnapLines.length ? vertSnapLines.reduce((prev, curr) => Math.abs(prev - currRight) > Math.abs(curr - currRight) ? curr : prev) : currRight;
-        const closestBottom = horizSnapLines.length ? horizSnapLines.reduce((prev, curr) => Math.abs(prev - currBottom) > Math.abs(curr - currBottom) ? curr : prev) : currBottom;
-        const distFromClosestLeft = Math.abs(e.pageX - xFromLeft - closestLeft);
-        const distFromClosestTop = Math.abs(e.pageY - yFromTop - closestTop);
-        const distFromClosestRight = Math.abs(e.pageX + xFromRight - closestRight);
-        const distFromClosestBottom = Math.abs(e.pageY + yFromBottom - closestBottom);
-        if (distFromClosestLeft < 10 && distFromClosestLeft < distFromClosestRight) {
-            thisX = closestLeft + xFromLeft;
-        }
-        else if (distFromClosestRight < 10) {
-            thisX = closestRight - xFromRight;
-        }
-        if (distFromClosestTop < 10 && distFromClosestTop < distFromClosestBottom) {
-            thisY = closestTop + yFromTop;
-        }
-        else if (distFromClosestBottom < 10) {
-            thisY = closestBottom - yFromBottom;
+        const snapThreshold = NumCast(Doc.UserDoc()["constants-snapThreshold"], 10);
+        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
+                const rangePts = [drag - offs[0], drag - offs[1], drag - offs[2]]; // left, mid, right or  top, mid, bottom pts to try to snap to snaplines
+                const closestPts = rangePts.map(pt => snapLines.reduce((nearest, curr) => Math.abs(nearest - pt) > Math.abs(curr - pt) ? curr : nearest));
+                const closestDists = rangePts.map((pt, i) => Math.abs(pt - closestPts[i]));
+                const minIndex = closestDists[0] < closestDists[1] && closestDists[0] < closestDists[2] ? 0 : closestDists[1] < closestDists[2] ? 1 : 2;
+                return closestDists[minIndex] < snapThreshold ? closestPts[minIndex] + offs[minIndex] : drag;
+            }
+            return drag;
         }
-        return { thisX, thisY };
+
+        return { thisX: snapVal([xFromLeft, xFromRight], e.pageX, vertSnapLines), thisY: snapVal([yFromTop, yFromBottom], e.pageY, horizSnapLines) };
     }
     export let docsBeingDragged: Doc[] = [];
     function StartDrag(eles: HTMLElement[], dragData: { [id: string]: any }, downX: number, downY: number, options?: DragOptions, finishDrag?: (dropData: DragCompleteEvent) => void) {
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index e5a8ebcb5..72dfdf75c 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -588,12 +588,12 @@ export class MainView extends React.Component {
             <RichTextMenu />
             <OverlayView />
             {// TO VIEW SNAP LINES
-             /* <div className="snapLines" style={{ position: "absolute", top: 0, left: 0, width: "100%", height: "100%", pointerEvents: "none" }}>
+                <div className="snapLines" style={{ position: "absolute", top: 0, left: 0, width: "100%", height: "100%", pointerEvents: "none" }}>
                     <svg style={{ width: "100%", height: "100%" }}>
-                        {this._hLines?.map(l => <line x1="0" y1={l} x2="2000" y2={l} stroke="black" />)}
-                        {this._vLines?.map(l => <line y1="0" x1={l} y2="2000" x2={l} stroke="black" />)}
+                        {this._hLines?.map(l => <line x1="0" y1={l} x2="2000" y2={l} stroke="black" opacity={0.3} strokeWidth={0.5} strokeDasharray={"1 1"} />)}
+                        {this._vLines?.map(l => <line y1="0" x1={l} y2="2000" x2={l} stroke="black" opacity={0.3} strokeWidth={0.5} strokeDasharray={"1 1"} />)}
                     </svg>
-            </div>*/}
+                </div>}
             <TimelineMenu />
         </div >);
     }
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 11d0f298d..763a6c605 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -869,6 +869,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
             LibraryPath: this.libraryPath,
             FreezeDimensions: this.props.freezeChildDimensions,
             layoutKey: undefined,
+            setupDragLines: this.setupDragLines,
             rootSelected: childData ? this.rootSelected : returnFalse,
             dropAction: StrCast(this.props.Document.childDropAction) as dropActionType,
             //onClick: undefined, // this.props.onClick,  // bcz: check this out -- I don't think we want to inherit click handlers, or we at least need a way to ignore them
@@ -1146,37 +1147,36 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
     }
 
     @action
+    setupDragLines = () => {
+        const size = this.props.ScreenToLocalTransform().transformDirection(this.props.PanelWidth(), this.props.PanelHeight());
+        const selRect = { left: this.panX() - size[0] / 2, top: this.panY() - size[1] / 2, width: size[0], height: size[1] };
+        const docDims = (doc: Doc) => ({ left: NumCast(doc.x), top: NumCast(doc.y), width: NumCast(doc._width), height: NumCast(doc._height) });
+        const isDocInView = (doc: Doc, rect: { left: number, top: number, width: number, height: number }) => {
+            if (this.intersectRect(docDims(doc), rect)) {
+                snappableDocs.push(doc);
+            }
+        }
+        const snappableDocs: Doc[] = [];  // the set of documents in the visible viewport that we will try to snap to;
+        const otherBounds = { left: this.panX(), top: this.panY(), width: Math.abs(size[0]), height: Math.abs(size[1]) };
+        this.getActiveDocuments().filter(doc => !doc.isBackground && doc.z === undefined).map(doc => isDocInView(doc, selRect));  // first see if there are any foreground docs to snap to
+        !snappableDocs.length && this.getActiveDocuments().filter(doc => doc.z === undefined).map(doc => isDocInView(doc, selRect)); // if not, see if there are background docs to snap to
+        !snappableDocs.length && this.getActiveDocuments().filter(doc => doc.z !== undefined).map(doc => isDocInView(doc, otherBounds)); // if not, then why not snap to floating docs
+
+        const horizLines: number[] = [];
+        const vertLines: number[] = [];
+        snappableDocs.filter(doc => !DragManager.docsBeingDragged.includes(Cast(doc.rootDocument, Doc, null) || doc)).forEach(doc => {
+            const { left, top, width, height } = docDims(doc);
+            const topLeftInScreen = this.getTransform().inverse().transformPoint(left, top);
+            const docSize = this.getTransform().inverse().transformDirection(width, height);
+
+            horizLines.push(topLeftInScreen[1], topLeftInScreen[1] + docSize[1] / 2, topLeftInScreen[1] + docSize[1]); // horiz center line
+            vertLines.push(topLeftInScreen[0], topLeftInScreen[0] + docSize[0] / 2, topLeftInScreen[0] + docSize[0]);// right line
+        });
+        DragManager.SetSnapLines(horizLines, vertLines);
+    }
     onPointerOver = (e: React.PointerEvent) => {
         if (SelectionManager.GetIsDragging()) {
-            const size = this.props.ScreenToLocalTransform().transformDirection(this.props.PanelWidth(), this.props.PanelHeight());
-            const selRect = { left: this.panX() - size[0] / 2, top: this.panY() - size[1] / 2, width: size[0], height: size[1] };
-            const selection: Doc[] = [];
-            const docDims = (doc: Doc, layoutDoc: Doc) => ({ left: NumCast(doc.x), top: NumCast(doc.y), width: NumCast(layoutDoc._width), height: NumCast(layoutDoc._height) });
-            const compareDoc = (doc: Doc, rect: { left: number, top: number, width: number, height: number }) => {
-                if (this.intersectRect(docDims(doc, Doc.Layout(doc)), rect)) {
-                    selection.push(doc);
-                }
-            }
-            const otherBounds = { left: this.panX(), top: this.panY(), width: Math.abs(size[0]), height: Math.abs(size[1]) };
-            this.getActiveDocuments().filter(doc => !doc.isBackground && doc.z === undefined).map(doc => compareDoc(doc, selRect));  // first try foreground docs
-            !selection.length && this.getActiveDocuments().filter(doc => doc.z === undefined).map(doc => compareDoc(doc, selRect)); // then background docs
-            !selection.length && this.getActiveDocuments().filter(doc => doc.z !== undefined).map(doc => compareDoc(doc, otherBounds)); // then floating docs
-
-            const horizLines: number[] = [];
-            const vertLines: number[] = [];
-            selection.filter(doc => !DragManager.docsBeingDragged.includes(doc)).forEach(doc => {
-                const { left, top, width, height } = docDims(doc, Doc.Layout(doc));
-                const topLeftInScreen = this.getTransform().inverse().transformPoint(left, top);
-                const docSize = this.getTransform().inverse().transformDirection(width, height);
-
-                horizLines.push(topLeftInScreen[1]); // top line
-                horizLines.push(topLeftInScreen[1] + docSize[1]); // bottom line
-                horizLines.push(topLeftInScreen[1] + docSize[1] / 2); // horiz center line
-                vertLines.push(topLeftInScreen[0]);//left line
-                vertLines.push(topLeftInScreen[0] + docSize[0]);// right line
-                vertLines.push(topLeftInScreen[0] + docSize[0] / 2);// vert center line
-            });
-            DragManager.SetSnapLines(horizLines, vertLines);
+            this.setupDragLines(e);
         }
         e.stopPropagation();
     }
@@ -1273,12 +1273,12 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
                 }}>
             </div>
             {// uncomment to show snap lines
-             /*<div className="snapLines" style={{ position: "absolute", top: 0, left: 0, width: "100%", height: "100%", pointerEvents: "none" }}>
-                <svg style={{ width: "100%", height: "100%" }}>
-                    {this._hLines?.map(l => <line x1="0" y1={l} x2="1000" y2={l} stroke="black" />)}
-                    {this._vLines?.map(l => <line y1="0" x1={l} y2="1000" x2={l} stroke="black" />)}
-                </svg>
-            </div>*/}
+                <div className="snapLines" style={{ position: "absolute", top: 0, left: 0, width: "100%", height: "100%", pointerEvents: "none" }}>
+                    <svg style={{ width: "100%", height: "100%" }}>
+                        {this._hLines?.map(l => <line x1="0" y1={l} x2="1000" y2={l} stroke="black" />)}
+                        {this._vLines?.map(l => <line y1="0" x1={l} y2="1000" x2={l} stroke="black" />)}
+                    </svg>
+                </div>}
         </div >;
     }
 }
diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx
index 422710c3e..1b22ed4cd 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.tsx
+++ b/src/client/views/nodes/formattedText/DashFieldView.tsx
@@ -34,8 +34,6 @@ export class DashFieldView {
             docid={node.attrs.docid}
             width={node.attrs.width}
             height={node.attrs.height}
-            view={view}
-            getPos={getPos}
             tbox={tbox}
         />, this._fieldWrapper);
         (this as any).dom = this._fieldWrapper;
@@ -49,8 +47,6 @@ export class DashFieldView {
 interface IDashFieldViewInternal {
     fieldKey: string;
     docid: string;
-    view: any;
-    getPos: any;
     tbox: FormattedTextBox;
     width: number;
     height: number;
diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts
index e49cc4804..663343f47 100644
--- a/src/server/authentication/models/current_user_utils.ts
+++ b/src/server/authentication/models/current_user_utils.ts
@@ -1,5 +1,6 @@
 import { action, computed, observable, reaction } from "mobx";
 import * as rp from 'request-promise';
+import { Utils } from "../../../Utils";
 import { DocServer } from "../../../client/DocServer";
 import { Docs, DocumentOptions } from "../../../client/documents/Documents";
 import { UndoManager } from "../../../client/util/UndoManager";
@@ -7,7 +8,7 @@ import { Doc, DocListCast, DocListCastAsync } from "../../../new_fields/Doc";
 import { List } from "../../../new_fields/List";
 import { listSpec } from "../../../new_fields/Schema";
 import { ScriptField, ComputedField } from "../../../new_fields/ScriptField";
-import { Cast, PromiseValue, StrCast } from "../../../new_fields/Types";
+import { Cast, PromiseValue, StrCast, NumCast } from "../../../new_fields/Types";
 import { Utils } from "../../../Utils";
 import { nullAudio, ImageField } from "../../../new_fields/URLField";
 import { DragManager } from "../../../client/util/DragManager";
@@ -628,6 +629,9 @@ export class CurrentUserUtils {
         new InkingControl();
         doc.title = Doc.CurrentUserEmail;
         doc.activePen = doc;
+        doc["constants-snapThreshold"] = NumCast(doc["constants-snapThreshold"], 10); // 
+        doc["constants-dragThreshold"] = NumCast(doc["constants-dragThreshold"], 4); // 
+        Utils.DRAG_THRESHOLD = NumCast(doc["constants-dragThreshold"]);
         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.setupRightSidebar(doc);  // sets up the right sidebar collection for mobile upload documents and sharing
-- 
cgit v1.2.3-70-g09d2


From ff7c7d40b1fcdf74b539c7d97f36707ff1521d2e Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Fri, 1 May 2020 01:46:07 -0400
Subject: fixed presentations to allow drag and drop.  fixed pres box to use
 RenderData instead of modifying presentation elements with unnecessary info
 like their containing PresBox and their presentation index position. 
 COnverted COntentFIttingDocumentView to use DocumentView's props

---
 src/client/documents/Documents.ts                  |  1 +
 src/client/views/MainView.tsx                      |  4 +-
 src/client/views/SearchDocBox.tsx                  | 10 ++++-
 .../views/collections/CollectionCarouselView.tsx   |  8 +++-
 .../views/collections/CollectionSchemaView.tsx     | 14 ++++---
 .../views/collections/CollectionStackingView.tsx   | 15 ++++---
 .../views/collections/CollectionTreeView.tsx       | 20 +++++----
 src/client/views/collections/CollectionView.tsx    | 14 ++++---
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  3 +-
 .../CollectionMulticolumnView.tsx                  | 14 ++++---
 .../CollectionMultirowView.tsx                     | 14 ++++---
 .../views/nodes/CollectionFreeFormDocumentView.tsx |  8 ++--
 .../views/nodes/ContentFittingDocumentView.tsx     | 49 ++++------------------
 src/client/views/nodes/DocumentBox.tsx             | 15 ++++---
 src/client/views/nodes/DocumentView.tsx            | 20 ++++-----
 src/client/views/nodes/FieldView.tsx               |  1 +
 src/client/views/nodes/PresBox.tsx                 | 21 ++++------
 .../formattedText/FormattedTextBoxComment.tsx      | 12 ++++--
 .../views/nodes/formattedText/RichTextMenu.scss    |  3 ++
 .../views/presentationview/PresElementBox.tsx      | 25 +++++++----
 src/client/views/search/SearchItem.tsx             | 12 ++++--
 src/new_fields/documentSchemas.ts                  |  2 +-
 .../authentication/models/current_user_utils.ts    |  2 +-
 23 files changed, 150 insertions(+), 137 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 2e81d5fa6..228a6af97 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -81,6 +81,7 @@ export interface DocumentOptions {
     author?: string;
     dropAction?: dropActionType;
     childDropAction?: dropActionType;
+    targetDropAction?: dropActionType;
     layoutKey?: string;
     type?: string;
     title?: string;
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 65e4eb036..a29a6baac 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -597,8 +597,8 @@ export class MainView extends React.Component {
             {// TO VIEW SNAP LINES
                 <div className="snapLines" style={{ position: "absolute", top: 0, left: 0, width: "100%", height: "100%", pointerEvents: "none" }}>
                     <svg style={{ width: "100%", height: "100%" }}>
-                        {this._hLines?.map(l => <line x1="0" y1={l} x2="2000" y2={l} stroke="black" opacity={0.3} strokeWidth={0.5} strokeDasharray={"1 1"} />)}
-                        {this._vLines?.map(l => <line y1="0" x1={l} y2="2000" x2={l} stroke="black" opacity={0.3} strokeWidth={0.5} strokeDasharray={"1 1"} />)}
+                        {this._hLines?.map((l: any) => <line x1="0" y1={l} x2="2000" y2={l} stroke="black" opacity={0.3} strokeWidth={0.5} strokeDasharray={"1 1"} />)}
+                        {this._vLines?.map((l: any) => <line y1="0" x1={l} y2="2000" x2={l} stroke="black" opacity={0.3} strokeWidth={0.5} strokeDasharray={"1 1"} />)}
                     </svg>
                 </div>}
             <TimelineMenu />
diff --git a/src/client/views/SearchDocBox.tsx b/src/client/views/SearchDocBox.tsx
index 799fa9d85..7bd689b19 100644
--- a/src/client/views/SearchDocBox.tsx
+++ b/src/client/views/SearchDocBox.tsx
@@ -6,7 +6,7 @@ import { observer } from "mobx-react";
 import { Doc, DocListCast } from "../../new_fields/Doc";
 import { Id } from "../../new_fields/FieldSymbols";
 import { BoolCast, Cast, NumCast, StrCast } from "../../new_fields/Types";
-import { returnFalse } from "../../Utils";
+import { returnFalse, returnZero } from "../../Utils";
 import { Docs } from "../documents/Documents";
 import { SearchUtil } from "../util/SearchUtil";
 import { EditableView } from "./EditableView";
@@ -399,7 +399,13 @@ export class SearchDocBox extends React.Component<FieldViewProps> {
                 <ContentFittingDocumentView {...this.props}
                     Document={this.content}
                     rootSelected={returnFalse}
-                    getTransform={this.props.ScreenToLocalTransform}>
+                    bringToFront={returnFalse}
+                    ContainingCollectionDoc={undefined}
+                    ContainingCollectionView={undefined}
+                    NativeWidth={returnZero}
+                    NativeHeight={returnZero}
+                    parentActive={this.props.active}
+                    ScreenToLocalTransform={this.props.ScreenToLocalTransform}>
                 </ContentFittingDocumentView>
                 <div
                     style={{
diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx
index eda8e5684..bfb7134b2 100644
--- a/src/client/views/collections/CollectionCarouselView.tsx
+++ b/src/client/views/collections/CollectionCarouselView.tsx
@@ -14,6 +14,7 @@ import { Doc } from '../../../new_fields/Doc';
 import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
 import { ContextMenu } from '../ContextMenu';
 import { ObjectField } from '../../../new_fields/ObjectField';
+import { returnFalse } from '../../../Utils';
 
 type CarouselDocument = makeInterface<[typeof documentSchema,]>;
 const CarouselDocument = makeInterface(documentSchema);
@@ -49,9 +50,12 @@ export class CollectionCarouselView extends CollectionSubView(CarouselDocument)
                     <ContentFittingDocumentView {...this.props}
                         renderDepth={this.props.renderDepth + 1}
                         Document={this.childLayoutPairs[index].layout}
-                        DataDocument={this.childLayoutPairs[index].data}
+                        DataDoc={this.childLayoutPairs[index].data}
                         PanelHeight={this.panelHeight}
-                        getTransform={this.props.ScreenToLocalTransform} />
+                        ScreenToLocalTransform={this.props.ScreenToLocalTransform}
+                        bringToFront={returnFalse}
+                        parentActive={this.props.active}
+                    />
                 </div>
                 <div className="collectionCarouselView-caption" key="caption"
                     style={{
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 380d91d2f..c0024293f 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -27,7 +27,7 @@ import "./CollectionSchemaView.scss";
 import { CollectionSubView } from "./CollectionSubView";
 import { CollectionView } from "./CollectionView";
 import { ContentFittingDocumentView } from "../nodes/ContentFittingDocumentView";
-import { setupMoveUpEvents, emptyFunction, returnZero, returnOne } from "../../../Utils";
+import { setupMoveUpEvents, emptyFunction, returnZero, returnOne, returnFalse } from "../../../Utils";
 import { DocumentView } from "../nodes/DocumentView";
 
 library.add(faCog, faPlus, faSortUp, faSortDown);
@@ -121,7 +121,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
             {!this.previewDocument ? (null) :
                 <ContentFittingDocumentView
                     Document={this.previewDocument}
-                    DataDocument={undefined}
+                    DataDoc={undefined}
                     NativeHeight={returnZero}
                     NativeWidth={returnZero}
                     fitToBox={true}
@@ -132,16 +132,18 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
                     rootSelected={this.rootSelected}
                     PanelWidth={this.previewWidth}
                     PanelHeight={this.previewHeight}
-                    getTransform={this.getPreviewTransform}
-                    CollectionDoc={this.props.CollectionView?.props.Document}
-                    CollectionView={this.props.CollectionView}
+                    ScreenToLocalTransform={this.getPreviewTransform}
+                    ContainingCollectionDoc={this.props.CollectionView?.props.Document}
+                    ContainingCollectionView={this.props.CollectionView}
                     moveDocument={this.props.moveDocument}
                     addDocument={this.props.addDocument}
                     removeDocument={this.props.removeDocument}
-                    active={this.props.active}
+                    parentActive={this.props.active}
                     whenActiveChanged={this.props.whenActiveChanged}
                     addDocTab={this.props.addDocTab}
                     pinToPres={this.props.pinToPres}
+                    bringToFront={returnFalse}
+                    ContentScaling={returnOne}
                 />}
         </div>;
     }
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 556d7df5c..6c230d5b1 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -11,7 +11,7 @@ import { listSpec } from "../../../new_fields/Schema";
 import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField";
 import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from "../../../new_fields/Types";
 import { TraceMobx } from "../../../new_fields/util";
-import { Utils, setupMoveUpEvents, emptyFunction, returnZero, returnOne } from "../../../Utils";
+import { Utils, setupMoveUpEvents, emptyFunction, returnZero, returnOne, returnFalse } from "../../../Utils";
 import { DragManager, dropActionType } from "../../util/DragManager";
 import { Transform } from "../../util/Transform";
 import { undoBatch } from "../../util/UndoManager";
@@ -165,12 +165,13 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
         const height = () => this.getDocHeight(doc);
         return <ContentFittingDocumentView
             Document={doc}
-            DataDocument={dataDoc || (doc[DataSym] !== doc && doc[DataSym])}
+            DataDoc={dataDoc || (doc[DataSym] !== doc && doc[DataSym])}
             backgroundColor={this.props.backgroundColor}
             LayoutDoc={this.props.childLayoutTemplate}
             LibraryPath={this.props.LibraryPath}
             FreezeDimensions={this.props.freezeChildDimensions}
             renderDepth={this.props.renderDepth + 1}
+            RenderData={this.props.RenderData}
             PanelWidth={width}
             PanelHeight={height}
             NativeHeight={returnZero}
@@ -180,16 +181,18 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
             dropAction={StrCast(this.props.Document.childDropAction) as dropActionType}
             onClick={this.onChildClickHandler}
             onDoubleClick={this.onChildDoubleClickHandler}
-            getTransform={dxf}
+            ScreenToLocalTransform={dxf}
             focus={this.props.focus}
-            CollectionDoc={this.props.CollectionView?.props.Document}
-            CollectionView={this.props.CollectionView}
+            ContainingCollectionDoc={this.props.CollectionView?.props.Document}
+            ContainingCollectionView={this.props.CollectionView}
             addDocument={this.props.addDocument}
             moveDocument={this.props.moveDocument}
             removeDocument={this.props.removeDocument}
-            active={this.props.active}
+            parentActive={this.props.active}
             whenActiveChanged={this.props.whenActiveChanged}
             addDocTab={this.addDocTab}
+            bringToFront={returnFalse}
+            ContentScaling={returnOne}
             pinToPres={this.props.pinToPres}
         />;
     }
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index d938bd7ad..71358a8ec 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -353,27 +353,31 @@ class TreeView extends React.Component<TreeViewProps> {
             return <div ref={this._dref} style={{ display: "inline-block", height: panelHeight() }} key={this.props.document[Id] + this.props.document.title}>
                 <ContentFittingDocumentView
                     Document={layoutDoc}
-                    DataDocument={this.templateDataDoc}
+                    DataDoc={this.templateDataDoc}
                     LibraryPath={emptyPath}
                     renderDepth={this.props.renderDepth + 1}
                     rootSelected={returnTrue}
                     backgroundColor={this.props.backgroundColor}
                     fitToBox={this.boundsOfCollectionDocument !== undefined}
                     FreezeDimensions={true}
-                    NativeWidth={layoutDoc.type === DocumentType.RTF ? this.rtfWidth : undefined}
-                    NativeHeight={layoutDoc.type === DocumentType.RTF ? this.rtfHeight : undefined}
+                    NativeWidth={layoutDoc.type === DocumentType.RTF ? this.rtfWidth : returnZero}
+                    NativeHeight={layoutDoc.type === DocumentType.RTF ? this.rtfHeight : returnZero}
                     PanelWidth={panelWidth}
                     PanelHeight={panelHeight}
-                    getTransform={this.docTransform}
-                    CollectionDoc={this.props.containingCollection}
-                    CollectionView={undefined}
+                    focus={returnFalse}
+                    ScreenToLocalTransform={this.docTransform}
+                    ContainingCollectionDoc={this.props.containingCollection}
+                    ContainingCollectionView={undefined}
                     addDocument={returnFalse}
                     moveDocument={this.props.moveDocument}
                     removeDocument={returnFalse}
-                    active={this.props.active}
+                    parentActive={this.props.active}
                     whenActiveChanged={emptyFunction}
                     addDocTab={this.props.addDocTab}
-                    pinToPres={this.props.pinToPres} />
+                    pinToPres={this.props.pinToPres}
+                    bringToFront={returnFalse}
+                    ContentScaling={returnOne}
+                />
             </div>;
         }
     }
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 8d8c321e8..561226de5 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -113,12 +113,16 @@ export class CollectionView extends Touchable<FieldViewProps> {
 
     @action.bound
     addDocument(doc: Doc): boolean {
-        const targetDataDoc = this.props.Document[DataSym];
-        const docList = DocListCast(targetDataDoc[this.props.fieldKey]);
-        !docList.includes(doc) && (targetDataDoc[this.props.fieldKey] = new List<Doc>([...docList, doc]));  // DocAddToList may write to targetdataDoc's parent ... we don't want this. should really change GetProto to GetDataDoc and test for resolvedDataDoc there
-        // Doc.AddDocToList(targetDataDoc, this.props.fieldKey, doc);
+        if (this.props.addDocument) {
+            this.props.addDocument(doc);
+        } else {
+            const targetDataDoc = this.props.Document[DataSym];
+            const docList = DocListCast(targetDataDoc[this.props.fieldKey]);
+            !docList.includes(doc) && (targetDataDoc[this.props.fieldKey] = new List<Doc>([...docList, doc]));  // DocAddToList may write to targetdataDoc's parent ... we don't want this. should really change GetProto to GetDataDoc and test for resolvedDataDoc there
+            // Doc.AddDocToList(targetDataDoc, this.props.fieldKey, doc);
+            targetDataDoc[this.props.fieldKey + "-lastModified"] = new DateField(new Date(Date.now()));
+        }
         doc.context = this.props.Document;
-        targetDataDoc[this.props.fieldKey + "-lastModified"] = new DateField(new Date(Date.now()));
         Doc.GetProto(doc).lastOpened = new DateField;
         return true;
     }
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 763a6c605..b4eb22444 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -877,6 +877,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
             onDoubleClick: this.onChildDoubleClickHandler,
             ScreenToLocalTransform: childLayout.z ? this.getTransformOverlay : this.getTransform,
             renderDepth: this.props.renderDepth + 1,
+            RenderData: this.props.RenderData,
             PanelWidth: childLayout[WidthSym],
             PanelHeight: childLayout[HeightSym],
             ContentScaling: returnOne,
@@ -1176,7 +1177,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
     }
     onPointerOver = (e: React.PointerEvent) => {
         if (SelectionManager.GetIsDragging()) {
-            this.setupDragLines(e);
+            this.setupDragLines();
         }
         e.stopPropagation();
     }
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
index 66d441115..b3a6a9deb 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -14,7 +14,7 @@ import "./collectionMulticolumnView.scss";
 import ResizeBar from './MulticolumnResizer';
 import WidthLabel from './MulticolumnWidthLabel';
 import { List } from '../../../../new_fields/List';
-import { returnZero } from '../../../../Utils';
+import { returnZero, returnFalse, returnOne } from '../../../../Utils';
 
 type MulticolumnDocument = makeInterface<[typeof documentSchema]>;
 const MulticolumnDocument = makeInterface(documentSchema);
@@ -216,7 +216,7 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu
     getDisplayDoc(layout: Doc, dxf: () => Transform, width: () => number, height: () => number) {
         return <ContentFittingDocumentView
             Document={layout}
-            DataDocument={layout.resolvedDataDoc as Doc}
+            DataDoc={layout.resolvedDataDoc as Doc}
             backgroundColor={this.props.backgroundColor}
             LayoutDoc={this.props.childLayoutTemplate}
             LibraryPath={this.props.LibraryPath}
@@ -231,17 +231,19 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu
             dropAction={StrCast(this.props.Document.childDropAction) as dropActionType}
             onClick={this.onChildClickHandler}
             onDoubleClick={this.onChildDoubleClickHandler}
-            getTransform={dxf}
+            ScreenToLocalTransform={dxf}
             focus={this.props.focus}
-            CollectionDoc={this.props.CollectionView?.props.Document}
-            CollectionView={this.props.CollectionView}
+            ContainingCollectionDoc={this.props.CollectionView?.props.Document}
+            ContainingCollectionView={this.props.CollectionView}
             addDocument={this.props.addDocument}
             moveDocument={this.props.moveDocument}
             removeDocument={this.props.removeDocument}
-            active={this.props.active}
+            parentActive={this.props.active}
             whenActiveChanged={this.props.whenActiveChanged}
             addDocTab={this.addDocTab}
             pinToPres={this.props.pinToPres}
+            bringToFront={returnFalse}
+            ContentScaling={returnOne}
         />;
     }
     /**
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
index 615efdb39..0fb29ca61 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
@@ -6,7 +6,7 @@ import * as React from "react";
 import { Doc } from '../../../../new_fields/Doc';
 import { NumCast, StrCast, BoolCast, ScriptCast } from '../../../../new_fields/Types';
 import { ContentFittingDocumentView } from '../../nodes/ContentFittingDocumentView';
-import { Utils, returnZero } from '../../../../Utils';
+import { Utils, returnZero, returnFalse, returnOne } from '../../../../Utils';
 import "./collectionMultirowView.scss";
 import { computed, trace, observable, action } from 'mobx';
 import { Transform } from '../../../util/Transform';
@@ -215,7 +215,7 @@ export class CollectionMultirowView extends CollectionSubView(MultirowDocument)
     getDisplayDoc(layout: Doc, dxf: () => Transform, width: () => number, height: () => number) {
         return <ContentFittingDocumentView
             Document={layout}
-            DataDocument={layout.resolvedDataDoc as Doc}
+            DataDoc={layout.resolvedDataDoc as Doc}
             backgroundColor={this.props.backgroundColor}
             LayoutDoc={this.props.childLayoutTemplate}
             LibraryPath={this.props.LibraryPath}
@@ -230,17 +230,19 @@ export class CollectionMultirowView extends CollectionSubView(MultirowDocument)
             dropAction={StrCast(this.props.Document.childDropAction) as dropActionType}
             onClick={this.onChildClickHandler}
             onDoubleClick={this.onChildDoubleClickHandler}
-            getTransform={dxf}
+            ScreenToLocalTransform={dxf}
             focus={this.props.focus}
-            CollectionDoc={this.props.CollectionView?.props.Document}
-            CollectionView={this.props.CollectionView}
+            ContainingCollectionDoc={this.props.CollectionView?.props.Document}
+            ContainingCollectionView={this.props.CollectionView}
             addDocument={this.props.addDocument}
             moveDocument={this.props.moveDocument}
             removeDocument={this.props.removeDocument}
-            active={this.props.active}
+            parentActive={this.props.active}
             whenActiveChanged={this.props.whenActiveChanged}
             addDocTab={this.addDocTab}
             pinToPres={this.props.pinToPres}
+            bringToFront={returnFalse}
+            ContentScaling={returnOne}
         />;
     }
     /**
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 1c7d116c5..24468dcc1 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -115,13 +115,11 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
                     PanelWidth={this.panelWidth}
                     PanelHeight={this.panelHeight} />
                 : <ContentFittingDocumentView {...this.props}
-                    CollectionDoc={this.props.ContainingCollectionDoc}
-                    DataDocument={this.props.DataDoc}
-                    getTransform={this.getTransform}
+                    ContainingCollectionDoc={this.props.ContainingCollectionDoc}
+                    DataDoc={this.props.DataDoc}
+                    ScreenToLocalTransform={this.getTransform}
                     NativeHeight={this.NativeHeight}
                     NativeWidth={this.NativeWidth}
-                    active={this.props.parentActive}
-                    focus={this.focusDoc}
                     PanelWidth={this.panelWidth}
                     PanelHeight={this.panelHeight}
                 />}
diff --git a/src/client/views/nodes/ContentFittingDocumentView.tsx b/src/client/views/nodes/ContentFittingDocumentView.tsx
index d0b0c8ee6..637fd5acc 100644
--- a/src/client/views/nodes/ContentFittingDocumentView.tsx
+++ b/src/client/views/nodes/ContentFittingDocumentView.tsx
@@ -3,51 +3,16 @@ import { computed } from "mobx";
 import { observer } from "mobx-react";
 import "react-table/react-table.css";
 import { Doc, Opt, WidthSym, HeightSym } from "../../../new_fields/Doc";
-import { ScriptField } from "../../../new_fields/ScriptField";
 import { NumCast, StrCast } from "../../../new_fields/Types";
 import { TraceMobx } from "../../../new_fields/util";
 import { emptyFunction, returnOne } from "../../../Utils";
-import { Transform } from "../../util/Transform";
-import { CollectionView } from "../collections/CollectionView";
 import '../DocumentDecorations.scss';
-import { DocumentView } from "../nodes/DocumentView";
+import { DocumentView, DocumentViewProps } from "../nodes/DocumentView";
 import "./ContentFittingDocumentView.scss";
-import { dropActionType } from "../../util/DragManager";
 
-interface ContentFittingDocumentViewProps {
-    Document: Doc;
-    DataDocument?: Doc;
-    LayoutDoc?: () => Opt<Doc>;
-    NativeWidth?: () => number;
-    NativeHeight?: () => number;
-    FreezeDimensions?: boolean;
-    LibraryPath: Doc[];
-    renderDepth: number;
-    fitToBox?: boolean;
-    layoutKey?: string;
-    dropAction?: dropActionType;
-    PanelWidth: () => number;
-    PanelHeight: () => number;
-    focus?: (doc: Doc) => void;
-    CollectionView?: CollectionView;
-    CollectionDoc?: Doc;
-    onClick?: ScriptField;
-    onDoubleClick?: ScriptField;
-    backgroundColor?: (doc: Doc) => string | undefined;
-    getTransform: () => Transform;
-    addDocument?: (document: Doc) => boolean;
-    moveDocument?: (document: Doc, target: Doc | undefined, addDoc: ((doc: Doc) => boolean)) => boolean;
-    removeDocument?: (document: Doc) => boolean;
-    active: (outsideReaction: boolean) => boolean;
-    whenActiveChanged: (isActive: boolean) => void;
-    addDocTab: (document: Doc, where: string) => boolean;
-    pinToPres: (document: Doc) => void;
-    dontRegisterView?: boolean;
-    rootSelected: (outsideReaction?: boolean) => boolean;
-}
 
 @observer
-export class ContentFittingDocumentView extends React.Component<ContentFittingDocumentViewProps>{
+export class ContentFittingDocumentView extends React.Component<DocumentViewProps>{
     public get displayName() { return "DocumentView(" + this.props.Document?.title + ")"; } // this makes mobx trace() statements more descriptive
     private get layoutDoc() { return this.props.LayoutDoc?.() || Doc.Layout(this.props.Document); }
     @computed get freezeDimensions() { return this.props.FreezeDimensions; }
@@ -68,7 +33,7 @@ export class ContentFittingDocumentView extends React.Component<ContentFittingDo
     @computed get panelWidth() { return this.nativeWidth && !this.props.Document._fitWidth ? this.nativeWidth() * this.contentScaling() : this.props.PanelWidth(); }
     @computed get panelHeight() { return this.nativeHeight && !this.props.Document._fitWidth ? this.nativeHeight() * this.contentScaling() : this.props.PanelHeight(); }
 
-    private getTransform = () => this.props.getTransform().translate(-this.centeringOffset, -this.centeringYOffset).scale(1 / this.contentScaling());
+    private getTransform = () => this.props.ScreenToLocalTransform().translate(-this.centeringOffset, -this.centeringYOffset).scale(1 / this.contentScaling());
     private get centeringOffset() { return this.nativeWidth() && !this.props.Document._fitWidth ? (this.props.PanelWidth() - this.nativeWidth() * this.contentScaling()) / 2 : 0; }
     private get centeringYOffset() { return Math.abs(this.centeringOffset) < 0.001 ? (this.props.PanelHeight() - this.nativeHeight() * this.contentScaling()) / 2 : 0; }
 
@@ -90,7 +55,7 @@ export class ContentFittingDocumentView extends React.Component<ContentFittingDo
                     }}>
                     <DocumentView {...this.props}
                         Document={this.props.Document}
-                        DataDoc={this.props.DataDocument}
+                        DataDoc={this.props.DataDoc}
                         LayoutDoc={this.props.LayoutDoc}
                         LibraryPath={this.props.LibraryPath}
                         NativeWidth={this.nativeWidth}
@@ -107,11 +72,11 @@ export class ContentFittingDocumentView extends React.Component<ContentFittingDo
                         removeDocument={this.props.removeDocument}
                         moveDocument={this.props.moveDocument}
                         whenActiveChanged={this.props.whenActiveChanged}
-                        ContainingCollectionView={this.props.CollectionView}
-                        ContainingCollectionDoc={this.props.CollectionDoc}
+                        ContainingCollectionView={this.props.ContainingCollectionView}
+                        ContainingCollectionDoc={this.props.ContainingCollectionDoc}
                         addDocTab={this.props.addDocTab}
                         pinToPres={this.props.pinToPres}
-                        parentActive={this.props.active}
+                        parentActive={this.props.parentActive}
                         ScreenToLocalTransform={this.getTransform}
                         renderDepth={this.props.renderDepth}
                         focus={this.props.focus || emptyFunction}
diff --git a/src/client/views/nodes/DocumentBox.tsx b/src/client/views/nodes/DocumentBox.tsx
index d4d997120..88eeb1137 100644
--- a/src/client/views/nodes/DocumentBox.tsx
+++ b/src/client/views/nodes/DocumentBox.tsx
@@ -6,7 +6,7 @@ import { documentSchema } from "../../../new_fields/documentSchemas";
 import { makeInterface } from "../../../new_fields/Schema";
 import { ComputedField } from "../../../new_fields/ScriptField";
 import { Cast, NumCast, StrCast } from "../../../new_fields/Types";
-import { emptyPath } from "../../../Utils";
+import { emptyPath, returnFalse, returnOne, returnZero } from "../../../Utils";
 import { ContextMenu } from "../ContextMenu";
 import { ContextMenuProps } from "../ContextMenuItem";
 import { ViewBoxAnnotatableComponent } from "../DocComponent";
@@ -114,9 +114,10 @@ export class DocHolderBox extends ViewBoxAnnotatableComponent<FieldViewProps, Do
         }
         const contents = !(containedDoc instanceof Doc) ? (null) : <ContentFittingDocumentView
             Document={containedDoc}
-            DataDocument={undefined}
+            DataDoc={undefined}
             LibraryPath={emptyPath}
-            CollectionView={this as any} // bcz: hack!  need to pass a prop that can be used to select the container (ie, 'this') when the up selector in document decorations is clicked.  currently, the up selector allows only a containing collection to be selected
+            ContainingCollectionView={this as any} // bcz: hack!  need to pass a prop that can be used to select the container (ie, 'this') when the up selector in document decorations is clicked.  currently, the up selector allows only a containing collection to be selected
+            ContainingCollectionDoc={undefined}
             fitToBox={true}
             layoutKey={childTemplateName ? "layout_" + childTemplateName : "layout"}
             rootSelected={this.props.isSelected}
@@ -125,14 +126,18 @@ export class DocHolderBox extends ViewBoxAnnotatableComponent<FieldViewProps, Do
             removeDocument={this.props.removeDocument}
             addDocTab={this.props.addDocTab}
             pinToPres={this.props.pinToPres}
-            getTransform={this.getTransform}
+            ScreenToLocalTransform={this.getTransform}
             renderDepth={this.props.renderDepth + 1}
+            NativeHeight={returnZero}
+            NativeWidth={returnZero}
             PanelWidth={this.pwidth}
             PanelHeight={this.pheight}
             focus={this.props.focus}
-            active={this.props.active}
+            parentActive={this.props.active}
             dontRegisterView={!this.isSelectionLocked()}
             whenActiveChanged={this.props.whenActiveChanged}
+            bringToFront={returnFalse}
+            ContentScaling={returnOne}
         />;
         return contents;
     }
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 085637440..f555d6eef 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -77,6 +77,7 @@ export interface DocumentViewProps {
     setupDragLines?: () => void;
     renderDepth: number;
     ContentScaling: () => number;
+    RenderData?: () => Doc;
     PanelWidth: () => number;
     PanelHeight: () => number;
     pointerEvents?: boolean;
@@ -992,6 +993,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 LayoutDoc={this.props.LayoutDoc}
                 makeLink={this.makeLink}
                 rootSelected={this.rootSelected}
+                RenderData={this.props.RenderData}
                 dontRegisterView={this.props.dontRegisterView}
                 fitToBox={this.props.fitToBox}
                 LibraryPath={this.props.LibraryPath}
@@ -1112,17 +1114,13 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             Doc.makeCustomViewClicked(this.props.Document, Docs.Create.StackingDocument, layout, undefined);
         }
     }
-    @observable _animate = 0;
+    @observable _animateScalingTo = 0;
     switchViews = action((custom: boolean, view: string) => {
-        SelectionManager.SetIsDragging(true);
-        this._animate = 0.1;
+        this._animateScalingTo = 0.1;  // shrink doc
         setTimeout(action(() => {
             this.setCustomView(custom, view);
-            this._animate = 1;
-            setTimeout(action(() => {
-                this._animate = 0;
-                SelectionManager.SetIsDragging(false);
-            }), 400);
+            this._animateScalingTo = 1; // expand it
+            setTimeout(action(() => this._animateScalingTo = 0), 400);
         }), 400);
     });
 
@@ -1156,9 +1154,9 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 !entered && Doc.UnBrushDoc(this.props.Document);
             })}
             style={{
-                transformOrigin: this._animate ? "center center" : undefined,
-                transform: this._animate ? `scale(${this._animate})` : undefined,
-                transition: !this._animate ? StrCast(this.Document.transition) : this._animate < 1 ? "transform 0.5s ease-in" : "transform 0.5s ease-out",
+                transformOrigin: this._animateScalingTo ? "center center" : undefined,
+                transform: this._animateScalingTo ? `scale(${this._animateScalingTo})` : undefined,
+                transition: !this._animateScalingTo ? StrCast(this.Document.transition) : this._animateScalingTo < 1 ? "transform 0.5s ease-in" : "transform 0.5s ease-out",
                 pointerEvents: this.ignorePointerEvents ? "none" : undefined,
                 color: StrCast(this.layoutDoc.color, "inherit"),
                 outline: highlighting && !borderRounding ? `${highlightColors[fullDegree]} ${highlightStyles[fullDegree]} ${localScale}px` : "solid 0px",
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 0b9edbcd3..1efee4f5a 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -56,6 +56,7 @@ export interface FieldViewProps {
     width?: number;
     background?: string;
     color?: string;
+    RenderData?: () => Doc;
 }
 
 @observer
diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx
index f91a809bb..3fcc97473 100644
--- a/src/client/views/nodes/PresBox.tsx
+++ b/src/client/views/nodes/PresBox.tsx
@@ -16,6 +16,7 @@ import { FieldView, FieldViewProps } from './FieldView';
 import "./PresBox.scss";
 import { ViewBoxBaseComponent } from "../DocComponent";
 import { makeInterface } from "../../../new_fields/Schema";
+import { List } from "../../../new_fields/List";
 
 type PresBoxSchema = makeInterface<[typeof documentSchema]>;
 const PresBoxDocument = makeInterface(documentSchema);
@@ -23,20 +24,15 @@ const PresBoxDocument = makeInterface(documentSchema);
 @observer
 export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>(PresBoxDocument) {
     public static LayoutString(fieldKey: string) { return FieldView.LayoutString(PresBox, fieldKey); }
-    private _childReaction: IReactionDisposer | undefined;
     @observable _isChildActive = false;
     @computed get childDocs() { return DocListCast(this.dataDoc[this.fieldKey]); }
     @computed get currentIndex() { return NumCast(this.rootDoc._itemIndex); }
 
     componentDidMount() {
+        this.rootDoc.presBox = this.rootDoc;
         this.rootDoc._forceRenderEngine = "timeline";
         this.rootDoc._replacedChrome = "replaced";
-        this._childReaction = reaction(() => this.childDocs.slice(), (children) => children.forEach((child, i) => child.presentationIndex = i), { fireImmediately: true });
     }
-    componentWillUnmount() {
-        this._childReaction?.();
-    }
-
     updateCurrentPresentation = () => Doc.UserDoc().activePresentation = this.rootDoc;
 
     @undoBatch
@@ -247,16 +243,12 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
 
     initializeViewAliases = (docList: Doc[], viewtype: CollectionViewType) => {
         const hgt = (viewtype === CollectionViewType.Tree) ? 50 : 46;
-        docList.forEach(doc => {
-            doc.presBox = this.rootDoc; // give contained documents a reference to the presentation
-            doc.presCollapsedHeight = hgt;  //  set the collpased height for documents based on the type of view (Tree or Stack) they will be displaye din
-        });
+        this.rootDoc.presCollapsedHeight = hgt;
     }
 
     addDocument = (doc: Doc) => {
-        const newPinDoc = Doc.MakeAlias(doc);
-        newPinDoc.presentationTargetDoc = doc;
-        return Doc.AddDocToList(this.dataDoc, this.fieldKey, newPinDoc);
+        doc.presentationTargetDoc = doc.aliasOf;
+        return Doc.AddDocToList(this.dataDoc, this.fieldKey, doc);
     }
 
     removeDocument = (doc: Doc) => Doc.RemoveDocFromList(this.dataDoc, this.fieldKey, doc);
@@ -280,8 +272,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
         this.updateMinimize(e, this.rootDoc._viewType = viewType);
     });
 
+    returnSelf = () => this.rootDoc;
     childLayoutTemplate = () => this.rootDoc._viewType === CollectionViewType.Stacking ? Cast(Doc.UserDoc()["template-presentation"], Doc, null) : undefined;
     render() {
+        this.rootDoc.presOrderedDocs = new List<Doc>(this.childDocs.map((child, i) => child));
         const mode = StrCast(this.rootDoc._viewType) as CollectionViewType;
         this.initializeViewAliases(this.childDocs, mode);
         return <div className="presBox-cont" style={{ minWidth: this.layoutDoc.inOverlay ? 240 : undefined }} >
@@ -314,6 +308,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
                         childLayoutTemplate={this.childLayoutTemplate}
                         addDocument={this.addDocument}
                         removeDocument={returnFalse}
+                        RenderData={this.returnSelf}
                         focus={this.selectElement}
                         ScreenToLocalTransform={this.getTransform} />
                     : (null)
diff --git a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
index f9e4c5210..9ad5aafb8 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
@@ -4,7 +4,7 @@ import { EditorView } from "prosemirror-view";
 import * as ReactDOM from 'react-dom';
 import { Doc, DocCastAsync } from "../../../../new_fields/Doc";
 import { Cast, FieldValue, NumCast } from "../../../../new_fields/Types";
-import { emptyFunction, returnEmptyString, returnFalse, Utils, emptyPath } from "../../../../Utils";
+import { emptyFunction, returnEmptyString, returnFalse, Utils, emptyPath, returnZero, returnOne } from "../../../../Utils";
 import { DocServer } from "../../../DocServer";
 import { DocumentManager } from "../../../util/DocumentManager";
 import { schema } from "./schema_rts";
@@ -192,18 +192,24 @@ export class FormattedTextBoxComment {
                                     fitToBox={true}
                                     moveDocument={returnFalse}
                                     rootSelected={returnFalse}
-                                    getTransform={Transform.Identity}
-                                    active={returnFalse}
+                                    ScreenToLocalTransform={Transform.Identity}
+                                    parentActive={returnFalse}
                                     addDocument={returnFalse}
                                     removeDocument={returnFalse}
                                     addDocTab={returnFalse}
                                     pinToPres={returnFalse}
                                     dontRegisterView={true}
+                                    ContainingCollectionDoc={undefined}
+                                    ContainingCollectionView={undefined}
                                     renderDepth={1}
                                     PanelWidth={() => Math.min(350, NumCast(target._width, 350))}
                                     PanelHeight={() => Math.min(250, NumCast(target._height, 250))}
                                     focus={emptyFunction}
                                     whenActiveChanged={returnFalse}
+                                    bringToFront={returnFalse}
+                                    ContentScaling={returnOne}
+                                    NativeWidth={returnZero}
+                                    NativeHeight={returnZero}
                                 />, FormattedTextBoxComment.tooltipText);
                                 FormattedTextBoxComment.tooltip.style.width = NumCast(target.width) ? `${NumCast(target.width)}` : "100%";
                                 FormattedTextBoxComment.tooltip.style.height = NumCast(target.height) ? `${NumCast(target.height)}` : "100%";
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.scss b/src/client/views/nodes/formattedText/RichTextMenu.scss
index 3a16171de..7a0718c16 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.scss
+++ b/src/client/views/nodes/formattedText/RichTextMenu.scss
@@ -55,6 +55,9 @@
         color: black;
     }
 
+}
+
+.richTextMenu {
     select {
         background-color: #323232;
         color: white;
diff --git a/src/client/views/presentationview/PresElementBox.tsx b/src/client/views/presentationview/PresElementBox.tsx
index 66f251b93..1887c8d45 100644
--- a/src/client/views/presentationview/PresElementBox.tsx
+++ b/src/client/views/presentationview/PresElementBox.tsx
@@ -1,12 +1,12 @@
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { action, computed, IReactionDisposer, reaction } from "mobx";
 import { observer } from "mobx-react";
-import { Doc, DataSym } from "../../../new_fields/Doc";
+import { Doc, DataSym, DocListCast } from "../../../new_fields/Doc";
 import { documentSchema } from '../../../new_fields/documentSchemas';
 import { Id } from "../../../new_fields/FieldSymbols";
 import { createSchema, makeInterface } from '../../../new_fields/Schema';
 import { Cast, NumCast } from "../../../new_fields/Types";
-import { emptyFunction, emptyPath, returnFalse, returnTrue } from "../../../Utils";
+import { emptyFunction, emptyPath, returnFalse, returnTrue, returnOne, returnZero } from "../../../Utils";
 import { Transform } from "../../util/Transform";
 import { CollectionViewType } from '../collections/CollectionView';
 import { ViewBoxBaseComponent } from '../DocComponent';
@@ -38,13 +38,14 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps, PresDoc
     public static LayoutString(fieldKey: string) { return FieldView.LayoutString(PresElementBox, fieldKey); }
 
     _heightDisposer: IReactionDisposer | undefined;
-    @computed get indexInPres() { return NumCast(this.rootDoc.presentationIndex); }
-    @computed get presBoxDoc() { return Cast(this.rootDoc.presBox, Doc) as Doc; }
+    @computed get indexInPres() { return DocListCast(this.presBoxDoc.presOrderedDocs).findIndex(d => d === this.rootDoc); }
+    @computed get presBoxDoc() { return Cast(this.props.RenderData?.().presBox, Doc) as Doc; }
     @computed get targetDoc() { return this.rootDoc.presentationTargetDoc as Doc; }
     @computed get currentIndex() { return NumCast(this.presBoxDoc?._itemIndex); }
+    @computed get collapsedHeight() { return NumCast(this.presBoxDoc?.presCollapsedHeight); }
 
     componentDidMount() {
-        this._heightDisposer = reaction(() => [this.rootDoc.presExpandInlineButton, this.rootDoc.presCollapsedHeight],
+        this._heightDisposer = reaction(() => [this.rootDoc.presExpandInlineButton, this.collapsedHeight],
             params => this.layoutDoc._height = NumCast(params[1]) + (Number(params[0]) ? 100 : 0), { fireImmediately: true });
     }
     componentWillUnmount() {
@@ -147,7 +148,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps, PresDoc
      */
     ScreenToLocalListTransform = (xCord: number, yCord: number) => [xCord, yCord];
 
-    embedHeight = () => Math.min(this.props.PanelWidth() - 20, this.props.PanelHeight() - NumCast(this.rootDoc.presCollapsedHeight));
+    embedHeight = () => Math.min(this.props.PanelWidth() - 20, this.props.PanelHeight() - this.collapsedHeight);
     embedWidth = () => this.props.PanelWidth() - 20;
     /**
      * The function that is responsible for rendering the a preview or not for this
@@ -158,7 +159,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps, PresDoc
             <div className="presElementBox-embedded" style={{ height: this.embedHeight(), width: this.embedWidth() }}>
                 <ContentFittingDocumentView
                     Document={this.targetDoc}
-                    DataDocument={this.targetDoc[DataSym] !== this.targetDoc && this.targetDoc[DataSym]}
+                    DataDoc={this.targetDoc[DataSym] !== this.targetDoc && this.targetDoc[DataSym]}
                     LibraryPath={emptyPath}
                     fitToBox={true}
                     rootSelected={returnTrue}
@@ -168,12 +169,18 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps, PresDoc
                     pinToPres={returnFalse}
                     PanelWidth={this.embedWidth}
                     PanelHeight={this.embedHeight}
-                    getTransform={Transform.Identity}
-                    active={this.props.active}
+                    ScreenToLocalTransform={Transform.Identity}
+                    parentActive={this.props.active}
                     moveDocument={this.props.moveDocument!}
                     renderDepth={this.props.renderDepth + 1}
                     focus={emptyFunction}
                     whenActiveChanged={returnFalse}
+                    bringToFront={returnFalse}
+                    ContainingCollectionView={undefined}
+                    ContainingCollectionDoc={undefined}
+                    ContentScaling={returnOne}
+                    NativeHeight={returnZero}
+                    NativeWidth={returnZero}
                 />
                 <div className="presElementBox-embeddedMask" />
             </div>;
diff --git a/src/client/views/search/SearchItem.tsx b/src/client/views/search/SearchItem.tsx
index fe2000700..96f43e931 100644
--- a/src/client/views/search/SearchItem.tsx
+++ b/src/client/views/search/SearchItem.tsx
@@ -7,7 +7,7 @@ import { observer } from "mobx-react";
 import { Doc } from "../../../new_fields/Doc";
 import { Id } from "../../../new_fields/FieldSymbols";
 import { Cast, NumCast, StrCast } from "../../../new_fields/Types";
-import { emptyFunction, emptyPath, returnFalse, Utils, returnTrue } from "../../../Utils";
+import { emptyFunction, emptyPath, returnFalse, Utils, returnTrue, returnOne, returnZero } from "../../../Utils";
 import { DocumentType } from "../../documents/DocumentTypes";
 import { DocumentManager } from "../../util/DocumentManager";
 import { DragManager, SetupDrag } from "../../util/DragManager";
@@ -164,14 +164,20 @@ export class SearchItem extends React.Component<SearchItemProps> {
                     removeDocument={returnFalse}
                     addDocTab={returnFalse}
                     pinToPres={returnFalse}
-                    getTransform={Transform.Identity}
+                    ContainingCollectionDoc={undefined}
+                    ContainingCollectionView={undefined}
+                    ScreenToLocalTransform={Transform.Identity}
                     renderDepth={1}
                     PanelWidth={returnXDimension}
                     PanelHeight={returnYDimension}
+                    NativeWidth={returnZero}
+                    NativeHeight={returnZero}
                     focus={emptyFunction}
                     moveDocument={returnFalse}
-                    active={returnFalse}
+                    parentActive={returnFalse}
                     whenActiveChanged={returnFalse}
+                    bringToFront={returnFalse}
+                    ContentScaling={returnOne}
                 />
             </div>;
             return docview;
diff --git a/src/new_fields/documentSchemas.ts b/src/new_fields/documentSchemas.ts
index 5ca0d681e..cd4b9d591 100644
--- a/src/new_fields/documentSchemas.ts
+++ b/src/new_fields/documentSchemas.ts
@@ -9,7 +9,7 @@ export const documentSchema = createSchema({
     layoutKey: "string",        // holds the field key for the field that actually holds the current lyoat
     title: "string",            // document title (can be on either data document or layout)
     dropAction: "string",       // override specifying what should happen when this document is dropped (can be "alias", "copy", "move")
-    targetDropAction: "string",  // allows the target of a drop event to specify the dropAction ("alias", "copy", "move")
+    targetDropAction: "string", // allows the target of a drop event to specify the dropAction ("alias", "copy", "move")
     childDropAction: "string",  // specify the override for what should happen when the child of a collection is dragged from it and dropped (can be "alias" or "copy")
     _autoHeight: "boolean",     // whether the height of the document should be computed automatically based on its contents
     _nativeWidth: "number",     // native width of document which determines how much document contents are scaled when the document's width is set
diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts
index d7cc1e6bf..f37538252 100644
--- a/src/server/authentication/models/current_user_utils.ts
+++ b/src/server/authentication/models/current_user_utils.ts
@@ -578,7 +578,7 @@ export class CurrentUserUtils {
         }
         if (doc.activePresentation === undefined) {
             doc.activePresentation = Docs.Create.PresDocument(new List<Doc>(), {
-                title: "Presentation", _viewType: CollectionViewType.Stacking,
+                title: "Presentation", _viewType: CollectionViewType.Stacking, targetDropAction: "alias",
                 _LODdisable: true, _chromeStatus: "replaced", _showTitle: "title", boxShadow: "0 0"
             });
         }
-- 
cgit v1.2.3-70-g09d2


From 5e6352c78be5b2a9fe791bd87da9b2415ced4839 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Fri, 1 May 2020 19:35:37 -0400
Subject: added childLayoutTemplate to render collection children w/o modifying
 them. fixed docComponent's layoutDoc to use LayoutDoc prop.  change buxton
 template to use new template mechanisms. fixed   fixed lint errors.

---
 src/client/documents/Documents.ts                  |  5 +-
 src/client/util/DragManager.ts                     |  2 +-
 src/client/views/DocComponent.tsx                  |  2 +-
 src/client/views/animationtimeline/Timeline.tsx    |  4 +-
 .../views/animationtimeline/TimelineMenu.tsx       | 81 +++++++++++-----------
 src/client/views/animationtimeline/Track.tsx       | 50 ++++++-------
 .../views/collections/CollectionStackingView.tsx   |  2 +-
 src/client/views/collections/CollectionSubView.tsx | 17 -----
 .../views/collections/CollectionTreeView.tsx       | 36 ++++------
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 11 ++-
 src/client/views/nodes/PresBox.tsx                 |  2 +-
 .../views/nodes/formattedText/DashFieldView.tsx    |  2 +-
 src/new_fields/documentSchemas.ts                  |  2 +-
 13 files changed, 94 insertions(+), 122 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index a8b10bc7b..434b26312 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -93,7 +93,8 @@ export interface DocumentOptions {
     scale?: number;
     isDisplayPanel?: boolean; // whether the panel functions as GoldenLayout "stack" used to display documents
     forceActive?: boolean;
-    layout?: string | Doc;
+    layout?: string | Doc; // default layout string for a document
+    childLayoutTemplate?: Doc; // template for collection to use to render its children (see PresBox or Buxton layout in tree view)
     hideFilterView?: boolean; // whether to hide the filter popout on collections
     hideHeadings?: boolean; // whether stacking view column headings should be hidden
     isTemplateForField?: string; // the field key for which the containing document is a rendering template
@@ -123,7 +124,7 @@ export interface DocumentOptions {
     borderRounding?: string;
     boxShadow?: string;
     dontRegisterChildren?: boolean;
-    "onDoubleClick-rawScript"?: string // onDoubleClick script in raw text form
+    "onDoubleClick-rawScript"?: string; // onDoubleClick 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
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index c48611eff..c06ad3d60 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -316,7 +316,7 @@ export namespace DragManager {
                 return closestDists[minIndex] < snapThreshold ? closestPts[minIndex] + offs[minIndex] : drag;
             }
             return drag;
-        }
+        };
 
         return { thisX: snapVal([xFromLeft, xFromRight], e.pageX, vertSnapLines), thisY: snapVal([yFromTop, yFromBottom], e.pageY, horizSnapLines) };
     }
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index 0a8f0c9a7..629b0f447 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -20,7 +20,7 @@ export function DocComponent<P extends DocComponentProps, T>(schemaCtor: (doc: D
         // 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 Doc.Layout(this.props.Document); }
+        @computed get layoutDoc() { return Doc.Layout(this.props.Document, this.props.LayoutDoc?.()); }
         // 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; }
 
diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx
index 77656b85f..466cbb867 100644
--- a/src/client/views/animationtimeline/Timeline.tsx
+++ b/src/client/views/animationtimeline/Timeline.tsx
@@ -525,8 +525,8 @@ export class Timeline extends React.Component<FieldViewProps> {
     @action.bound
     changeLengths() {
         if (this._infoContainer.current) {
-            this._visibleLength = this._infoContainer.current!.getBoundingClientRect().width; //the visible length of the timeline (the length that you current see)
-            this._visibleStart = this._infoContainer.current!.scrollLeft; //where the div starts
+            this._visibleLength = this._infoContainer.current.getBoundingClientRect().width; //the visible length of the timeline (the length that you current see)
+            this._visibleStart = this._infoContainer.current.scrollLeft; //where the div starts
         }
     }
 
diff --git a/src/client/views/animationtimeline/TimelineMenu.tsx b/src/client/views/animationtimeline/TimelineMenu.tsx
index 59c25596e..53ca9acad 100644
--- a/src/client/views/animationtimeline/TimelineMenu.tsx
+++ b/src/client/views/animationtimeline/TimelineMenu.tsx
@@ -1,7 +1,7 @@
 import * as React from "react";
-import {observable, action, runInAction} from "mobx"; 
-import {observer} from "mobx-react"; 
-import "./TimelineMenu.scss"; 
+import { observable, action, runInAction } from "mobx";
+import { observer } from "mobx-react";
+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";
@@ -9,67 +9,66 @@ import { Utils } from "../../../Utils";
 
 @observer
 export class TimelineMenu extends React.Component {
-    public static Instance:TimelineMenu; 
+    public static Instance: TimelineMenu;
 
     @observable private _opacity = 0;
-    @observable private _x = 0; 
-    @observable private _y = 0; 
-    @observable private _currentMenu:JSX.Element[] = []; 
+    @observable private _x = 0;
+    @observable private _y = 0;
+    @observable private _currentMenu: JSX.Element[] = [];
 
-    constructor (props:Readonly<{}>){
-        super(props); 
-        TimelineMenu.Instance = this; 
+    constructor(props: Readonly<{}>) {
+        super(props);
+        TimelineMenu.Instance = this;
     }
-    
+
     @action
-    openMenu = (x?:number, y?:number) => {
-        this._opacity = 1; 
-        x ? this._x = x : this._x = 0; 
-        y ? this._y = y : this._y = 0; 
+    openMenu = (x?: number, y?: number) => {
+        this._opacity = 1;
+        x ? this._x = x : this._x = 0;
+        y ? this._y = y : this._y = 0;
     }
 
     @action
     closeMenu = () => {
-        this._opacity = 0; 
-        this._currentMenu = []; 
-        this._x = -1000000; 
-        this._y = -1000000; 
+        this._opacity = 0;
+        this._currentMenu = [];
+        this._x = -1000000;
+        this._y = -1000000;
     }
 
     @action
-    addItem = (type: "input" | "button", title: string, event: (e:any, ...args:any[]) => void) => {
-        if (type === "input"){
-            let 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) => {
+    addItem = (type: "input" | "button", title: string, event: (e: any, ...args: any[]) => void) => {
+        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) => {
                 e.stopPropagation();
                 text = e.target.value;
             }} onKeyDown={(e) => {
                 if (e.keyCode === 13) {
-                    event(text); 
-                    this.closeMenu();                    
-                     e.stopPropagation(); 
-                } 
-            }}/></div>); 
+                    event(text);
+                    this.closeMenu();
+                    e.stopPropagation();
+                }
+            }} /></div>);
         } else if (type === "button") {
-            let buttonRef = React.createRef<HTMLDivElement>(); 
-            this._currentMenu.push( <div key={Utils.GenerateGuid()} className="timeline-menu-item"><FontAwesomeIcon icon={faChartLine}size="lg"/><p className="timeline-menu-desc" onClick={(e) => {
-                e.preventDefault(); 
-                e.stopPropagation(); 
-                event(e); 
-                this.closeMenu(); 
-            }}>{title}</p></div>); 
-        }  
+            this._currentMenu.push(<div key={Utils.GenerateGuid()} className="timeline-menu-item"><FontAwesomeIcon icon={faChartLine} size="lg" /><p className="timeline-menu-desc" onClick={(e) => {
+                e.preventDefault();
+                e.stopPropagation();
+                event(e);
+                this.closeMenu();
+            }}>{title}</p></div>);
+        }
     }
 
-    @action 
-    addMenu = (title:string) => {
-        this._currentMenu.unshift(<div key={Utils.GenerateGuid()} className="timeline-menu-header"><p className="timeline-menu-header-desc">{title}</p></div>);     
+    @action
+    addMenu = (title: string) => {
+        this._currentMenu.unshift(<div key={Utils.GenerateGuid()} className="timeline-menu-header"><p className="timeline-menu-header-desc">{title}</p></div>);
     }
 
     render() {
         return (
-            <div key={Utils.GenerateGuid()} className="timeline-menu-container" style={{opacity: this._opacity, left: this._x, top: this._y}} >
+            <div key={Utils.GenerateGuid()} className="timeline-menu-container" style={{ opacity: this._opacity, left: this._x, top: this._y }} >
                 {this._currentMenu}
             </div>
         );
diff --git a/src/client/views/animationtimeline/Track.tsx b/src/client/views/animationtimeline/Track.tsx
index 79eb60fae..461db4858 100644
--- a/src/client/views/animationtimeline/Track.tsx
+++ b/src/client/views/animationtimeline/Track.tsx
@@ -90,9 +90,9 @@ export class Track extends React.Component<IProps> {
      */
     @action
     saveKeyframe = async () => {
-        let keyframes = Cast(this.saveStateRegion?.keyframes, listSpec(Doc)) as List<Doc>;
-        let kfIndex = keyframes.indexOf(this.saveStateKf!);
-        let kf = keyframes[kfIndex] as Doc; //index in the keyframe
+        const keyframes = Cast(this.saveStateRegion?.keyframes, listSpec(Doc)) as List<Doc>;
+        const kfIndex = keyframes.indexOf(this.saveStateKf!);
+        const kf = keyframes[kfIndex] as Doc; //index in the keyframe
         if (this._newKeyframe) {
             DocListCast(this.saveStateRegion?.keyframes).forEach((kf, index) => {
                 this.copyDocDataToKeyFrame(kf);
@@ -103,17 +103,17 @@ export class Track extends React.Component<IProps> {
         if (!kf) return;
         if (kf.type === KeyframeFunc.KeyframeType.default) { // only save for non-fades
             this.copyDocDataToKeyFrame(kf);
-            let leftkf = KeyframeFunc.calcMinLeft(this.saveStateRegion!, this.time, kf); // lef keyframe, if it exists
-            let rightkf = KeyframeFunc.calcMinRight(this.saveStateRegion!, this.time, kf); //right keyframe, if it exists 
+            const leftkf = KeyframeFunc.calcMinLeft(this.saveStateRegion!, this.time, kf); // lef keyframe, if it exists
+            const rightkf = KeyframeFunc.calcMinRight(this.saveStateRegion!, this.time, kf); //right keyframe, if it exists 
             if (leftkf?.type === KeyframeFunc.KeyframeType.fade) { //replicating this keyframe to fades
-                let edge = KeyframeFunc.calcMinLeft(this.saveStateRegion!, this.time, leftkf);
+                const edge = KeyframeFunc.calcMinLeft(this.saveStateRegion!, this.time, leftkf);
                 edge && this.copyDocDataToKeyFrame(edge);
                 leftkf && this.copyDocDataToKeyFrame(leftkf);
-                edge && (edge!.opacity = 0.1);
-                leftkf && (leftkf!.opacity = 1);
+                edge && (edge.opacity = 0.1);
+                leftkf && (leftkf.opacity = 1);
             }
             if (rightkf?.type === KeyframeFunc.KeyframeType.fade) {
-                let edge = KeyframeFunc.calcMinRight(this.saveStateRegion!, this.time, rightkf);
+                const edge = KeyframeFunc.calcMinRight(this.saveStateRegion!, this.time, rightkf);
                 edge && this.copyDocDataToKeyFrame(edge);
                 rightkf && this.copyDocDataToKeyFrame(rightkf);
                 edge && (edge.opacity = 0.1);
@@ -142,7 +142,7 @@ export class Track extends React.Component<IProps> {
             //check for region 
             const region = this.findRegion(this.time);
             if (region !== undefined) { //if region at scrub time exist
-                let r = region as RegionData; //for some region is returning undefined... which is not the case
+                const r = region as RegionData; //for some region is returning undefined... which is not the case
                 if (DocListCast(r.keyframes).find(kf => kf.time === this.time) === undefined) { //basically when there is no additional keyframe at that timespot 
                     this.makeKeyData(r, this.time, KeyframeFunc.KeyframeType.default);
                 }
@@ -222,11 +222,11 @@ export class Track extends React.Component<IProps> {
         } else if (this._newKeyframe) {
             await this.saveKeyframe();
         }
-        let regiondata = await this.findRegion(Math.round(this.time)); //finds a region that the scrubber is on
+        const regiondata = await this.findRegion(Math.round(this.time)); //finds a region that the scrubber is on
         if (regiondata) {
-            let leftkf: (Doc | undefined) = await KeyframeFunc.calcMinLeft(regiondata, this.time); // lef keyframe, if it exists
-            let rightkf: (Doc | undefined) = await KeyframeFunc.calcMinRight(regiondata, this.time); //right keyframe, if it exists        
-            let currentkf: (Doc | undefined) = await this.calcCurrent(regiondata); //if the scrubber is on top of the keyframe
+            const leftkf: (Doc | undefined) = await KeyframeFunc.calcMinLeft(regiondata, this.time); // lef keyframe, if it exists
+            const rightkf: (Doc | undefined) = await KeyframeFunc.calcMinRight(regiondata, this.time); //right keyframe, if it exists        
+            const currentkf: (Doc | undefined) = await this.calcCurrent(regiondata); //if the scrubber is on top of the keyframe
             if (currentkf) {
                 console.log("is current");
                 await this.applyKeys(currentkf);
@@ -248,7 +248,7 @@ export class Track extends React.Component<IProps> {
             if (!kf[key]) {
                 this.props.node[key] = undefined;
             } else {
-                let stored = kf[key];
+                const stored = kf[key];
                 this.props.node[key] = stored instanceof ObjectField ? stored[Copy]() : stored;
             }
         });
@@ -261,7 +261,7 @@ export class Track extends React.Component<IProps> {
     @action
     calcCurrent = (region: Doc) => {
         let currentkf: (Doc | undefined) = undefined;
-        let keyframes = DocListCast(region.keyframes!);
+        const keyframes = DocListCast(region.keyframes!);
         keyframes.forEach((kf) => {
             if (NumCast(kf.time) === Math.round(this.time)) currentkf = kf;
         });
@@ -276,12 +276,12 @@ export class Track extends React.Component<IProps> {
     interpolate = async (left: Doc, right: Doc) => {
         this.primitiveWhitelist.forEach(key => {
             if (left[key] && right[key] && typeof (left[key]) === "number" && typeof (right[key]) === "number") { //if it is number, interpolate
-                let dif = NumCast(right[key]) - NumCast(left[key]);
-                let deltaLeft = this.time - NumCast(left.time);
-                let ratio = deltaLeft / (NumCast(right.time) - NumCast(left.time));
+                const dif = NumCast(right[key]) - NumCast(left[key]);
+                const deltaLeft = this.time - NumCast(left.time);
+                const ratio = deltaLeft / (NumCast(right.time) - NumCast(left.time));
                 this.props.node[key] = NumCast(left[key]) + (dif * ratio);
             } else { // case data 
-                let stored = left[key];
+                const stored = left[key];
                 this.props.node[key] = stored instanceof ObjectField ? stored[Copy]() : stored;
             }
         });
@@ -301,8 +301,8 @@ export class Track extends React.Component<IProps> {
      */
     @action
     onInnerDoubleClick = (e: React.MouseEvent) => {
-        let inner = this._inner.current!;
-        let offsetX = Math.round((e.clientX - inner.getBoundingClientRect().left) * this.props.transform.Scale);
+        const inner = this._inner.current!;
+        const offsetX = Math.round((e.clientX - inner.getBoundingClientRect().left) * this.props.transform.Scale);
         this.createRegion(KeyframeFunc.convertPixelTime(offsetX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement));
     }
 
@@ -313,10 +313,10 @@ export class Track extends React.Component<IProps> {
     @action
     createRegion = (time: number) => {
         if (this.findRegion(time) === undefined) {  //check if there is a region where double clicking (prevents phantom regions)
-            let regiondata = KeyframeFunc.defaultKeyframe(); //create keyframe data
+            const regiondata = KeyframeFunc.defaultKeyframe(); //create keyframe data
 
             regiondata.position = time; //set position
-            let rightRegion = KeyframeFunc.findAdjacentRegion(KeyframeFunc.Direction.right, regiondata, this.regions);
+            const rightRegion = KeyframeFunc.findAdjacentRegion(KeyframeFunc.Direction.right, regiondata, this.regions);
 
             if (rightRegion && rightRegion.position - regiondata.position <= 4000) { //edge case when there is less than default 4000 duration space between this and right region
                 regiondata.duration = rightRegion.position - regiondata.position;
@@ -332,7 +332,7 @@ export class Track extends React.Component<IProps> {
 
     @action
     makeKeyData = (regiondata: RegionData, time: number, type: KeyframeFunc.KeyframeType = KeyframeFunc.KeyframeType.default) => { //Kfpos is mouse offsetX, representing time 
-        const trackKeyFrames = DocListCast(regiondata.keyframes)!;
+        const trackKeyFrames = DocListCast(regiondata.keyframes);
         const existingkf = trackKeyFrames.find(TK => TK.time === time);
         if (existingkf) return existingkf;
         //else creates a new doc. 
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 6c230d5b1..01766f65f 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -128,7 +128,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
         return layoutDoc._fitWidth ? wid * NumCast(layoutDoc.scrollHeight, nh) / (nw || 1) : layoutDoc[HeightSym]();
     }
     componentDidMount() {
-        super.componentDidMount();
+        super.componentDidMount?.();
 
         // reset section headers when a new filter is inputted
         this._pivotFieldDisposer = reaction(
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 8cc1af55b..e44bbae78 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -58,7 +58,6 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
         private dropDisposer?: DragManager.DragDropDisposer;
         private gestureDisposer?: GestureUtils.GestureEventDisposer;
         protected multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer;
-        private _childLayoutDisposer?: IReactionDisposer;
         protected _mainCont?: HTMLDivElement;
         protected createDashEventsTarget = (ele: HTMLDivElement) => { //used for stacking and masonry view
             this.dropDisposer?.();
@@ -75,25 +74,9 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
             this.createDashEventsTarget(ele);
         }
 
-        componentDidMount() {
-            this._childLayoutDisposer = reaction(() => ({ childDocs: this.childDocs, childLayout: Cast(this.props.Document.childLayout, Doc) }),
-                ({ childDocs, childLayout }) => {
-                    if (childLayout instanceof Doc) {
-                        childDocs.map(doc => {
-                            doc.layout_fromParent = childLayout;
-                            doc.layoutKey = "layout_fromParent";
-                        });
-                    }
-                    else if (!(childLayout instanceof Promise)) {
-                        childDocs.filter(d => !d.isTemplateForField).map(doc => doc.layoutKey === "layout_fromParent" && (doc.layoutKey = "layout"));
-                    }
-                }, { fireImmediately: true });
-
-        }
         componentWillUnmount() {
             this.gestureDisposer?.();
             this.multiTouchDisposer?.();
-            this._childLayoutDisposer?.();
         }
 
         @computed get dataDoc() {
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 71358a8ec..296c1a39c 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -725,14 +725,6 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
         }
         ContextMenu.Instance.addItem({
             description: "Buxton Layout", icon: "eye", event: () => {
-                DocListCast(this.dataDoc[this.props.fieldKey]).map(d => {
-                    DocListCast(d.data).map((img, i) => {
-                        const caption = (d.captions as any)[i];
-                        if (caption) {
-                            Doc.GetProto(img).caption = caption;
-                        }
-                    });
-                });
                 const { ImageDocument } = Docs.Create;
                 const { Document } = this.props;
                 const fallbackImg = "http://www.cs.brown.edu/~bcz/face.gif";
@@ -742,21 +734,19 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
                 heroView._showTitle = "title";
                 heroView._showTitleHover = "titlehover";
 
-                Doc.AddDocToList(Doc.UserDoc().dockedBtns as Doc, "data",
-                    Docs.Create.FontIconDocument({
-                        title: "hero view", _nativeWidth: 100, _nativeHeight: 100, _width: 100, _height: 100, dropAction: "alias",
-                        dragFactory: heroView, removeDropProperties: new List<string>(["dropAction"]), icon: "portrait",
-                        onDragStart: ScriptField.MakeFunction('getCopy(this.dragFactory, true)'),
-                    }));
-
-                Doc.AddDocToList(Doc.UserDoc().dockedBtns as Doc, "data",
-                    Docs.Create.FontIconDocument({
-                        title: "detail view", _nativeWidth: 100, _nativeHeight: 100, _width: 100, _height: 100, dropAction: "alias",
-                        dragFactory: detailView, removeDropProperties: new List<string>(["dropAction"]), icon: "file-alt",
-                        onDragStart: ScriptField.MakeFunction('getCopy(this.dragFactory, true)'),
-                    }));
-
-                Document.childLayout = heroView;
+                const doubleClickView = ImageDocument("http://cs.brown.edu/~bcz/face.gif", { _width: 400 });  // replace with desired double click target
+                DocListCast(this.dataDoc[this.props.fieldKey]).map(d => {
+                    DocListCast(d.data).map((img, i) => {
+                        const caption = (d.captions as any)[i];
+                        if (caption) {
+                            Doc.GetProto(img).caption = caption;
+                            Doc.GetProto(img).doubleClickView = doubleClickView;
+                        }
+                    });
+                    d.layout = ImageBox.LayoutString("hero");
+                });
+
+                Document.childLayoutTemplate = heroView;
                 Document.childDetailView = detailView;
                 Document._viewType = CollectionViewType.Time;
                 Document._forceActive = true;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index b4eb22444..45ef0455e 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -855,9 +855,10 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
     @computed get libraryPath() { return this.props.LibraryPath ? [...this.props.LibraryPath, this.props.Document] : []; }
     @computed get onChildClickHandler() { return this.props.childClickScript || ScriptCast(this.Document.onChildClick); }
     @computed get onChildDoubleClickHandler() { return this.props.childDoubleClickScript || ScriptCast(this.Document.onChildDoubleClick); }
-    backgroundHalo = () => BoolCast(this.Document.useClusters);
     @computed get backgroundActive() { return this.layoutDoc.isBackground && (this.props.ContainingCollectionView?.active() || this.props.active()); }
+    backgroundHalo = () => BoolCast(this.Document.useClusters);
     parentActive = () => this.props.active() || this.backgroundActive ? true : false;
+    childLayoutFunc = () => this.props.childLayoutTemplate?.() || Cast(this.props.Document.childLayoutTemplate, Doc, null);
     getChildDocumentViewProps(childLayout: Doc, childData?: Doc): DocumentViewProps {
         return {
             ...this.props,
@@ -867,12 +868,12 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
             DataDoc: childData,
             Document: childLayout,
             LibraryPath: this.libraryPath,
+            LayoutDoc: this.childLayoutFunc,
             FreezeDimensions: this.props.freezeChildDimensions,
             layoutKey: undefined,
             setupDragLines: this.setupDragLines,
             rootSelected: childData ? this.rootSelected : returnFalse,
             dropAction: StrCast(this.props.Document.childDropAction) as dropActionType,
-            //onClick: undefined, // this.props.onClick,  // bcz: check this out -- I don't think we want to inherit click handlers, or we at least need a way to ignore them
             onClick: this.onChildClickHandler,
             onDoubleClick: this.onChildDoubleClickHandler,
             ScreenToLocalTransform: childLayout.z ? this.getTransformOverlay : this.getTransform,
@@ -999,7 +1000,6 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         return { newPool, computedElementData: this.doFreeformLayout(newPool) };
     }
 
-    childLayoutDocFunc = () => this.props.childLayoutTemplate?.() || Cast(this.props.Document.childLayoutTemplate, Doc, null);
     get doLayoutComputation() {
         const { newPool, computedElementData } = this.doInternalLayoutComputation;
         runInAction(() =>
@@ -1025,7 +1025,6 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
                     replica={entry[1].replica}
                     dataProvider={this.childDataProvider}
                     sizeProvider={this.childSizeProvider}
-                    LayoutDoc={this.childLayoutDocFunc}
                     pointerEvents={
                         this.backgroundActive ?
                             true :
@@ -1042,7 +1041,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
     }
 
     componentDidMount() {
-        super.componentDidMount();
+        super.componentDidMount?.();
         this._layoutComputeReaction = reaction(() => this.doLayoutComputation,
             (elements) => this._layoutElements = elements || [],
             { fireImmediately: true, name: "doLayout" });
@@ -1156,7 +1155,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
             if (this.intersectRect(docDims(doc), rect)) {
                 snappableDocs.push(doc);
             }
-        }
+        };
         const snappableDocs: Doc[] = [];  // the set of documents in the visible viewport that we will try to snap to;
         const otherBounds = { left: this.panX(), top: this.panY(), width: Math.abs(size[0]), height: Math.abs(size[1]) };
         this.getActiveDocuments().filter(doc => !doc.isBackground && doc.z === undefined).map(doc => isDocInView(doc, selRect));  // first see if there are any foreground docs to snap to
diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx
index 72bbc9e4b..6e3420f22 100644
--- a/src/client/views/nodes/PresBox.tsx
+++ b/src/client/views/nodes/PresBox.tsx
@@ -260,7 +260,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
     panelHeight = () => this.props.PanelHeight() - 20;
 
     active = (outsideReaction?: boolean) => ((InkingControl.Instance.selectedTool === InkTool.None && !this.layoutDoc.isBackground) &&
-        (this.layoutDoc.forceActive || this.props.isSelected(outsideReaction) || this._isChildActive || this.props.renderDepth === 0) ? true : false);
+        (this.layoutDoc.forceActive || this.props.isSelected(outsideReaction) || this._isChildActive || this.props.renderDepth === 0) ? true : false)
 
     whenActiveChanged = action((isActive: boolean) => this.props.whenActiveChanged(this._isChildActive = isActive));
 
diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx
index 1b22ed4cd..d87d6e424 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.tsx
+++ b/src/client/views/nodes/formattedText/DashFieldView.tsx
@@ -104,7 +104,7 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
                     r?.addEventListener("pointerdown", action((e) => this._showEnumerables = true));
                 }} >
                     {strVal}
-                </span>
+                </span>;
             }
         }
     }
diff --git a/src/new_fields/documentSchemas.ts b/src/new_fields/documentSchemas.ts
index 7bf1c03c8..fd9a304f9 100644
--- a/src/new_fields/documentSchemas.ts
+++ b/src/new_fields/documentSchemas.ts
@@ -78,7 +78,7 @@ export const positionSchema = createSchema({
 });
 
 export const collectionSchema = createSchema({
-    childLayout: Doc, // layout template for children of a collecion
+    childLayoutTemplate: Doc, // layout template for children of a collecion
     childDetailView: Doc, // layout template to apply to a child when its clicked on in a collection and opened (requires onChildClick or other script to use this field)
     onChildClick: ScriptField, // script to run for each child when its clicked
     onChildDoubleClick: ScriptField, // script to run for each child when its clicked
-- 
cgit v1.2.3-70-g09d2


From 40d5c3acab6dbdc67f6d4bfd15c802da9fe08ca0 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Sat, 2 May 2020 00:17:21 -0400
Subject: cleaned up a lot of layoutTemplate/String props.  fixed link drawing.

---
 src/client/documents/Documents.ts                  |  7 +-
 src/client/util/DocumentManager.ts                 |  2 +-
 src/client/views/DocComponent.tsx                  |  4 +-
 src/client/views/DocumentDecorations.scss          | 24 +++---
 src/client/views/DocumentDecorations.tsx           |  4 +-
 src/client/views/MainView.scss                     |  6 +-
 .../views/collections/CollectionCarouselView.tsx   | 11 +--
 .../views/collections/CollectionStackingView.tsx   | 29 ++++---
 src/client/views/collections/CollectionSubView.tsx |  2 +
 .../views/collections/CollectionTimeView.tsx       |  4 +-
 .../views/collections/CollectionTreeView.tsx       |  8 +-
 src/client/views/collections/CollectionView.tsx    | 14 +++-
 .../views/collections/CollectionViewChromes.tsx    |  6 +-
 .../CollectionFreeFormLinkView.tsx                 |  2 +-
 .../CollectionFreeFormLinksView.tsx                |  2 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 10 +--
 .../CollectionMulticolumnView.tsx                  |  5 +-
 .../CollectionMultirowView.tsx                     |  5 +-
 src/client/views/nodes/AudioBox.tsx                |  4 +-
 .../views/nodes/CollectionFreeFormDocumentView.tsx |  4 +-
 .../views/nodes/ContentFittingDocumentView.tsx     |  5 +-
 src/client/views/nodes/DocumentBox.tsx             | 94 ++++++++++++++-------
 src/client/views/nodes/DocumentContentsView.tsx    | 44 +++++-----
 src/client/views/nodes/DocumentView.tsx            | 29 +++----
 src/client/views/nodes/FieldView.tsx               |  3 +-
 src/client/views/nodes/KeyValueBox.tsx             |  2 +-
 src/client/views/nodes/LinkAnchorBox.tsx           |  1 -
 src/client/views/nodes/ScreenshotBox.tsx           |  6 +-
 src/client/views/nodes/VideoBox.tsx                |  6 +-
 src/new_fields/Doc.ts                              |  2 +-
 src/new_fields/documentSchemas.ts                  | 95 ++++++++++++----------
 .../authentication/models/current_user_utils.ts    |  2 +-
 32 files changed, 247 insertions(+), 195 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 434b26312..672f94f75 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -95,6 +95,7 @@ export interface DocumentOptions {
     forceActive?: boolean;
     layout?: string | Doc; // default layout string for a document
     childLayoutTemplate?: Doc; // template for collection to use to render its children (see PresBox or Buxton layout in tree view)
+    childLayoutString?: string; // template string for collection to use to render its children
     hideFilterView?: boolean; // whether to hide the filter popout on collections
     hideHeadings?: boolean; // whether stacking view column headings should be hidden
     isTemplateForField?: string; // the field key for which the containing document is a rendering template
@@ -123,7 +124,7 @@ export interface DocumentOptions {
     displayTimecode?: number; // the time that a document should be displayed (e.g., time an annotation should be displayed on a video)
     borderRounding?: string;
     boxShadow?: string;
-    dontRegisterChildren?: boolean;
+    dontRegisterChildViews?: boolean;
     "onDoubleClick-rawScript"?: string; // onDoubleClick script in raw text form
     "onClick-rawScript"?: string; // onClick script in raw text form
     "onCheckedClick-rawScript"?: string; // onChecked script in raw text form
@@ -592,9 +593,7 @@ export namespace Docs {
             linkDocProto.anchor1_timecode = source.doc.currentTimecode || source.doc.displayTimecode;
             linkDocProto.anchor2_timecode = target.doc.currentTimecode || target.doc.displayTimecode;
 
-            if (linkDocProto.layout_key1 === undefined) {
-                Cast(linkDocProto.proto, Doc, null).layout_key1 = LinkAnchorBox.LayoutString("anchor1");
-                Cast(linkDocProto.proto, Doc, null).layout_key2 = LinkAnchorBox.LayoutString("anchor2");
+            if (linkDocProto.linkBoxExcludedKeys === undefined) {
                 Cast(linkDocProto.proto, Doc, null).linkBoxExcludedKeys = new List(["treeViewExpandedView", "treeViewHideTitle", "removeDropProperties", "linkBoxExcludedKeys", "treeViewOpen", "aliasNumber", "isPrototype", "lastOpened", "creationDate", "author"]);
                 Cast(linkDocProto.proto, Doc, null).layoutKey = undefined;
             }
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 4683e77a8..1ba6f0248 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -117,7 +117,7 @@ export class DocumentManager {
                 pairs.push(...linksList.reduce((pairs, link) => {
                     const linkToDoc = link && LinkManager.Instance.getOppositeAnchor(link, dv.props.Document);
                     linkToDoc && DocumentManager.Instance.getDocumentViews(linkToDoc).map(docView1 => {
-                        if (dv.props.Document.type !== DocumentType.LINK || dv.props.layoutKey !== docView1.props.layoutKey) {
+                        if (dv.props.Document.type !== DocumentType.LINK || dv.props.LayoutTemplateString !== docView1.props.LayoutTemplateString) {
                             pairs.push({ a: dv, b: docView1, l: link });
                         }
                     });
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index 629b0f447..fd0d2bdbb 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -11,7 +11,7 @@ import { InteractionUtils } from '../util/InteractionUtils';
 ///  DocComponent returns a generic React base class used by views that don't have 'fieldKey' props (e.g.,CollectionFreeFormDocumentView, DocumentView)
 interface DocComponentProps {
     Document: Doc;
-    LayoutDoc?: () => Opt<Doc>;
+    LayoutTemplate?: () => Opt<Doc>;
 }
 export function DocComponent<P extends DocComponentProps, T>(schemaCtor: (doc: Doc) => T) {
     class Component extends Touchable<P> {
@@ -20,7 +20,7 @@ export function DocComponent<P extends DocComponentProps, T>(schemaCtor: (doc: D
         // 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 Doc.Layout(this.props.Document, this.props.LayoutDoc?.()); }
+        @computed get layoutDoc() { return 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; }
 
diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss
index 61d517d43..4f34eb9e3 100644
--- a/src/client/views/DocumentDecorations.scss
+++ b/src/client/views/DocumentDecorations.scss
@@ -44,7 +44,6 @@ $linkGap : 3px;
 
     .documentDecorations-radius {
         pointer-events: auto;
-        background: black;
         opacity: 1;
         transform: translate(10px, 10px);
         grid-row: 4;
@@ -88,12 +87,12 @@ $linkGap : 3px;
         opacity: 1;
     }
     #documentDecorations-topLeftResizer {
-        border-left: black 2px solid;
-        border-top: black solid 2px;
+        border-left:  2px solid;
+        border-top:  solid 2px;
     }
     #documentDecorations-bottomRightResizer {
-        border-right: black 2px solid;
-        border-bottom: black solid 2px;
+        border-right:  2px solid;
+        border-bottom:  solid 2px;
     }
     #documentDecorations-topLeftResizer:hover,
     #documentDecorations-bottomRightResizer:hover {
@@ -111,12 +110,12 @@ $linkGap : 3px;
         opacity: 1;
     }
     #documentDecorations-topRightResizer {
-        border-right: black 2px solid;
-        border-top: black 2px solid;
+        border-right:  2px solid;
+        border-top:  2px solid;
     }
     #documentDecorations-bottomLeftResizer {
-        border-left: black 2px solid;
-        border-bottom: black 2px solid;
+        border-left:  2px solid;
+        border-bottom:  2px solid;
     }
     #documentDecorations-topRightResizer:hover,
     #documentDecorations-bottomLeftResizer:hover {
@@ -136,7 +135,6 @@ $linkGap : 3px;
     }
 
     .documentDecorations-contextMenu {
-        background: $alt-accent;
         width: 25px;
         height: calc(100% + 8px); // 8px for the height of the top resizer bar
         grid-column-start: 1;
@@ -144,14 +142,14 @@ $linkGap : 3px;
         pointer-events: all;
     }
     .documentDecorations-title {
-        background: $alt-accent;
         opacity: 1;
         grid-column-start: 3;
         grid-column-end: 4;
         pointer-events: auto;
         overflow: hidden;
         text-align: center;
-        display:flex;
+        display: flex;    
+        border-bottom: solid 1px;
     }
     .publishBox {
         width: 20px;
@@ -168,7 +166,6 @@ $linkGap : 3px;
 
 
 .documentDecorations-closeButton {
-    background: $alt-accent;
     opacity: 1;
     grid-column-start: 4;
     grid-column-end: 6;
@@ -178,7 +175,6 @@ $linkGap : 3px;
 }
 
 .documentDecorations-minimizeButton {
-    background: $alt-accent;
     opacity: 1;
     grid-column-start: 1;
     grid-column-end: 3;
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index e2759291a..396fe12b5 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -4,7 +4,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { action, computed, observable, reaction, runInAction } from "mobx";
 import { observer } from "mobx-react";
 import { Doc, DataSym, Field } from "../../new_fields/Doc";
-import { PositionDocument } from '../../new_fields/documentSchemas';
+import { Document } from '../../new_fields/documentSchemas';
 import { ScriptField } from '../../new_fields/ScriptField';
 import { Cast, StrCast, NumCast } from "../../new_fields/Types";
 import { Utils, setupMoveUpEvents, emptyFunction, returnFalse, simulateMouseClick } from "../../Utils";
@@ -301,7 +301,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
 
         SelectionManager.SelectedDocuments().forEach(action((element: DocumentView) => {
             if (dX !== 0 || dY !== 0 || dW !== 0 || dH !== 0) {
-                const doc = PositionDocument(element.rootDoc);
+                const doc = Document(element.rootDoc);
                 let nwidth = doc._nativeWidth || 0;
                 let nheight = doc._nativeHeight || 0;
                 const width = (doc._width || 0);
diff --git a/src/client/views/MainView.scss b/src/client/views/MainView.scss
index 81d427f64..04288a9e1 100644
--- a/src/client/views/MainView.scss
+++ b/src/client/views/MainView.scss
@@ -39,7 +39,12 @@
     }
 }
 
+.mainView-container {
+    color:dimgray;
+}
+
 .mainView-container-dark {
+    color: lightgray;
     .lm_goldenlayout {
         background: dimgray;
     }
@@ -54,7 +59,6 @@
     }
     .contextMenu-cont, .contextMenu-item {
         background: dimGray;
-        color: lightgray;
     }
     .contextMenu-item:hover {
         background: gray;
diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx
index 4086294ad..769b323ae 100644
--- a/src/client/views/collections/CollectionCarouselView.tsx
+++ b/src/client/views/collections/CollectionCarouselView.tsx
@@ -2,9 +2,9 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { observable, computed } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
-import { documentSchema } from '../../../new_fields/documentSchemas';
+import { documentSchema, collectionSchema } from '../../../new_fields/documentSchemas';
 import { makeInterface } from '../../../new_fields/Schema';
-import { NumCast, StrCast, ScriptCast } from '../../../new_fields/Types';
+import { NumCast, StrCast, ScriptCast, Cast } from '../../../new_fields/Types';
 import { DragManager } from '../../util/DragManager';
 import { ContentFittingDocumentView } from '../nodes/ContentFittingDocumentView';
 import "./CollectionCarouselView.scss";
@@ -16,8 +16,8 @@ import { ContextMenu } from '../ContextMenu';
 import { ObjectField } from '../../../new_fields/ObjectField';
 import { returnFalse } from '../../../Utils';
 
-type CarouselDocument = makeInterface<[typeof documentSchema,]>;
-const CarouselDocument = makeInterface(documentSchema);
+type CarouselDocument = makeInterface<[typeof documentSchema, typeof collectionSchema]>;
+const CarouselDocument = makeInterface(documentSchema, collectionSchema);
 
 @observer
 export class CollectionCarouselView extends CollectionSubView(CarouselDocument) {
@@ -40,7 +40,6 @@ export class CollectionCarouselView extends CollectionSubView(CarouselDocument)
         e.stopPropagation();
         this.layoutDoc._itemIndex = (NumCast(this.layoutDoc._itemIndex) - 1 + this.childLayoutPairs.length) % this.childLayoutPairs.length;
     }
-
     panelHeight = () => this.props.PanelHeight() - 50;
     @computed get content() {
         const index = NumCast(this.layoutDoc._itemIndex);
@@ -51,6 +50,8 @@ export class CollectionCarouselView extends CollectionSubView(CarouselDocument)
                         onDoubleClick={ScriptCast(this.layoutDoc.onChildDoubleClick)}
                         onClick={ScriptCast(this.layoutDoc.onChildClick)}
                         renderDepth={this.props.renderDepth + 1}
+                        LayoutTemplate={this.props.ChildLayoutTemplate}
+                        LayoutTemplateString={this.props.ChildLayoutString}
                         Document={this.childLayoutPairs[index].layout}
                         DataDoc={this.childLayoutPairs[index].data}
                         PanelHeight={this.panelHeight}
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 01766f65f..eb70cec9d 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -4,15 +4,17 @@ import { CursorProperty } from "csstype";
 import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx";
 import { observer } from "mobx-react";
 import Switch from 'rc-switch';
-import { Doc, HeightSym, WidthSym, DataSym } from "../../../new_fields/Doc";
+import { DataSym, Doc, HeightSym, WidthSym } from "../../../new_fields/Doc";
+import { collectionSchema, documentSchema } from "../../../new_fields/documentSchemas";
 import { Id } from "../../../new_fields/FieldSymbols";
 import { List } from "../../../new_fields/List";
-import { listSpec } from "../../../new_fields/Schema";
+import { listSpec, makeInterface } from "../../../new_fields/Schema";
 import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField";
 import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from "../../../new_fields/Types";
 import { TraceMobx } from "../../../new_fields/util";
-import { Utils, setupMoveUpEvents, emptyFunction, returnZero, returnOne, returnFalse } from "../../../Utils";
+import { emptyFunction, returnFalse, returnOne, returnZero, setupMoveUpEvents, Utils } from "../../../Utils";
 import { DragManager, dropActionType } from "../../util/DragManager";
+import { SelectionManager } from "../../util/SelectionManager";
 import { Transform } from "../../util/Transform";
 import { undoBatch } from "../../util/UndoManager";
 import { ContextMenu } from "../ContextMenu";
@@ -24,11 +26,14 @@ import "./CollectionStackingView.scss";
 import { CollectionStackingViewFieldColumn } from "./CollectionStackingViewFieldColumn";
 import { CollectionSubView } from "./CollectionSubView";
 import { CollectionViewType } from "./CollectionView";
-import { SelectionManager } from "../../util/SelectionManager";
+import { ScriptField } from "../../../new_fields/ScriptField";
 const _global = (window /* browser */ || global /* node */) as any;
 
+type StackingDocument = makeInterface<[typeof collectionSchema, typeof documentSchema]>;
+const StackingDocument = makeInterface(collectionSchema, documentSchema);
+
 @observer
-export class CollectionStackingView extends CollectionSubView(doc => doc) {
+export class CollectionStackingView extends CollectionSubView(StackingDocument) {
     _masonryGridRef: HTMLDivElement | null = null;
     _draggerRef = React.createRef<HTMLDivElement>();
     _pivotFieldDisposer?: IReactionDisposer;
@@ -116,7 +121,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
 
     getSimpleDocHeight(d?: Doc) {
         if (!d) return 0;
-        const layoutDoc = Doc.Layout(d, this.props.childLayoutTemplate?.());
+        const layoutDoc = Doc.Layout(d, this.props.ChildLayoutTemplate?.());
         const nw = NumCast(layoutDoc._nativeWidth);
         const nh = NumCast(layoutDoc._nativeHeight);
         let wid = this.columnWidth / (this.isStackingView ? this.numGroupColumns : 1);
@@ -160,14 +165,16 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
         }
         return this.props.addDocTab(doc, where);
     }
+
     getDisplayDoc(doc: Doc, dataDoc: Doc | undefined, dxf: () => Transform, width: () => number) {
-        const layoutDoc = Doc.Layout(doc, this.props.childLayoutTemplate?.());
+        const layoutDoc = Doc.Layout(doc, this.props.ChildLayoutTemplate?.());
         const height = () => this.getDocHeight(doc);
         return <ContentFittingDocumentView
             Document={doc}
             DataDoc={dataDoc || (doc[DataSym] !== doc && doc[DataSym])}
             backgroundColor={this.props.backgroundColor}
-            LayoutDoc={this.props.childLayoutTemplate}
+            LayoutTemplate={this.props.ChildLayoutTemplate}
+            LayoutTemplateString={this.props.ChildLayoutString}
             LibraryPath={this.props.LibraryPath}
             FreezeDimensions={this.props.freezeChildDimensions}
             renderDepth={this.props.renderDepth + 1}
@@ -176,7 +183,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
             PanelHeight={height}
             NativeHeight={returnZero}
             NativeWidth={returnZero}
-            fitToBox={BoolCast(this.props.Document._freezeChildDimensions)}
+            fitToBox={false}
             rootSelected={this.rootSelected}
             dropAction={StrCast(this.props.Document.childDropAction) as dropActionType}
             onClick={this.onChildClickHandler}
@@ -199,13 +206,13 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
 
     getDocWidth(d?: Doc) {
         if (!d) return 0;
-        const layoutDoc = Doc.Layout(d, this.props.childLayoutTemplate?.());
+        const layoutDoc = Doc.Layout(d, this.props.ChildLayoutTemplate?.());
         const nw = NumCast(layoutDoc._nativeWidth);
         return Math.min(nw && !this.props.Document.fillColumn ? d[WidthSym]() : Number.MAX_VALUE, this.columnWidth / this.numGroupColumns);
     }
     getDocHeight(d?: Doc) {
         if (!d) return 0;
-        const layoutDoc = Doc.Layout(d, this.props.childLayoutTemplate?.());
+        const layoutDoc = Doc.Layout(d, this.props.ChildLayoutTemplate?.());
         const nw = NumCast(layoutDoc._nativeWidth);
         const nh = NumCast(layoutDoc._nativeHeight);
         let wid = this.columnWidth / (this.isStackingView ? this.numGroupColumns : 1);
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index e44bbae78..aaea13ded 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -43,6 +43,8 @@ export interface CollectionViewProps extends FieldViewProps {
 export interface SubCollectionViewProps extends CollectionViewProps {
     CollectionView: Opt<CollectionView>;
     children?: never | (() => JSX.Element[]) | React.ReactNode;
+    ChildLayoutTemplate?: () => Doc;
+    ChildLayoutString?: string;
     childClickScript?: ScriptField;
     childDoubleClickScript?: ScriptField;
     freezeChildDimensions?: boolean; // used by TimeView to coerce documents to treat their width height as their native width/height
diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx
index 045134225..a2d4774c8 100644
--- a/src/client/views/collections/CollectionTimeView.tsx
+++ b/src/client/views/collections/CollectionTimeView.tsx
@@ -28,7 +28,7 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) {
     @observable _childClickedScript: Opt<ScriptField>;
     @observable _viewDefDivClick: Opt<ScriptField>;
     async componentDidMount() {
-        const detailView = (await DocCastAsync(this.props.Document.childDetailView)) || Doc.findTemplate("detailView", StrCast(this.props.Document.type), "");
+        const detailView = (await DocCastAsync(this.props.Document.childClickedOpenTemplateView)) || Doc.findTemplate("detailView", StrCast(this.props.Document.type), "");
         const childText = "const alias = getAlias(self); switchView(alias, detailView); alias.dropAction='alias'; alias.removeDropProperties=new List<string>(['dropAction']); useRightSplit(alias, shiftKey); ";
         runInAction(() => {
             this._childClickedScript = ScriptField.MakeScript(childText, { this: Doc.name, shiftKey: "boolean" }, { detailView: detailView! });
@@ -84,7 +84,7 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) {
 
     @computed get contents() {
         return <div className="collectionTimeView-innards" key="timeline" style={{ width: "100%", pointerEvents: this.props.active() ? undefined : "none" }} onPointerDown={this.contentsDown}>
-            <CollectionFreeFormView {...this.props} childClickScript={this._childClickedScript} viewDefDivClick={this._viewDefDivClick} fitToBox={true} freezeChildDimensions={BoolCast(this.layoutDoc._freezeChildDimensions, true)} layoutEngine={this.layoutEngine} />
+            <CollectionFreeFormView {...this.props} childClickScript={this._childClickedScript} viewDefDivClick={this._viewDefDivClick} fitToBox={true} freezeChildDimensions={true} layoutEngine={this.layoutEngine} />
         </div>;
     }
 
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 296c1a39c..dc9348664 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -6,7 +6,6 @@ import { observer } from "mobx-react";
 import { DataSym, Doc, DocListCast, Field, HeightSym, Opt, WidthSym } from '../../../new_fields/Doc';
 import { Id } from '../../../new_fields/FieldSymbols';
 import { List } from '../../../new_fields/List';
-import { RichTextField } from '../../../new_fields/RichTextField';
 import { Document, listSpec } from '../../../new_fields/Schema';
 import { ComputedField, ScriptField } from '../../../new_fields/ScriptField';
 import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../new_fields/Types';
@@ -15,7 +14,6 @@ import { Docs, DocUtils } from '../../documents/Documents';
 import { DocumentType } from "../../documents/DocumentTypes";
 import { DocumentManager } from '../../util/DocumentManager';
 import { DragManager, dropActionType, SetupDrag } from "../../util/DragManager";
-import { makeTemplate } from '../../util/DropConverter';
 import { Scripting } from '../../util/Scripting';
 import { SelectionManager } from '../../util/SelectionManager';
 import { Transform } from '../../util/Transform';
@@ -481,7 +479,7 @@ class TreeView extends React.Component<TreeViewProps> {
                         parentActive={returnTrue}
                         whenActiveChanged={emptyFunction}
                         bringToFront={emptyFunction}
-                        dontRegisterView={BoolCast(this.props.treeViewId.dontRegisterChildren)}
+                        dontRegisterView={BoolCast(this.props.treeViewId.dontRegisterChildViews)}
                         ContainingCollectionView={undefined}
                         ContainingCollectionDoc={this.props.containingCollection}
                     />}
@@ -743,11 +741,11 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
                             Doc.GetProto(img).doubleClickView = doubleClickView;
                         }
                     });
-                    d.layout = ImageBox.LayoutString("hero");
+                    Doc.GetProto(d).layout = ImageBox.LayoutString("hero");
                 });
 
                 Document.childLayoutTemplate = heroView;
-                Document.childDetailView = detailView;
+                Document.childClickedOpenTemplateView = detailView;
                 Document._viewType = CollectionViewType.Time;
                 Document._forceActive = true;
                 Document._pivotField = "company";
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 91018980c..d2afb4855 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -72,7 +72,8 @@ export enum CollectionViewType {
     Pile = "pileup"
 }
 export interface CollectionViewCustomProps {
-    filterAddDocument: (doc: Doc) => 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)
+    childLayoutTemplate?: () => Opt<Doc>;  // specify a layout Doc template to use for children of the collection
 }
 
 export interface CollectionRenderProps {
@@ -82,6 +83,8 @@ export interface CollectionRenderProps {
     active: () => boolean;
     whenActiveChanged: (isActive: boolean) => void;
     PanelWidth: () => number;
+    ChildLayoutTemplate?: () => Doc;
+    ChildLayoutString?: string;
 }
 
 @observer
@@ -245,8 +248,8 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
             if (this.props.Document.childLayout instanceof Doc) {
                 layoutItems.push({ description: "View Child Layout", event: () => this.props.addDocTab(this.props.Document.childLayout as Doc, "onRight"), icon: "project-diagram" });
             }
-            if (this.props.Document.childDetailView instanceof Doc) {
-                layoutItems.push({ description: "View Child Detailed Layout", event: () => this.props.addDocTab(this.props.Document.childDetailView as Doc, "onRight"), icon: "project-diagram" });
+            if (this.props.Document.childClickedOpenTemplateView instanceof Doc) {
+                layoutItems.push({ description: "View Child Detailed Layout", event: () => this.props.addDocTab(this.props.Document.childClickedOpenTemplateView as Doc, "onRight"), icon: "project-diagram" });
             }
             layoutItems.push({ description: `${this.props.Document.isInPlaceContainer ? "Unset" : "Set"} inPlace Container`, event: () => this.props.Document.isInPlaceContainer = !this.props.Document.isInPlaceContainer, icon: "project-diagram" });
 
@@ -474,6 +477,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
                 </div>
             </div>;
     }
+    childLayoutTemplate = () => this.props.childLayoutTemplate?.() || Cast(this.props.Document.childLayoutTemplate, Doc, null);
 
     render() {
         TraceMobx();
@@ -483,7 +487,9 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
             moveDocument: this.moveDocument,
             active: this.active,
             whenActiveChanged: this.whenActiveChanged,
-            PanelWidth: this.bodyPanelWidth
+            PanelWidth: this.bodyPanelWidth,
+            ChildLayoutTemplate: this.childLayoutTemplate,
+            ChildLayoutString: StrCast(this.props.Document.childLayoutString),
         };
         return (<div className={"collectionView"}
             style={{
diff --git a/src/client/views/collections/CollectionViewChromes.tsx b/src/client/views/collections/CollectionViewChromes.tsx
index d26e3a38b..62b03bbdc 100644
--- a/src/client/views/collections/CollectionViewChromes.tsx
+++ b/src/client/views/collections/CollectionViewChromes.tsx
@@ -44,9 +44,9 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
         initialize: emptyFunction,
     };
     _narrativeCommand = {
-        params: ["target", "source"], title: "=> click item view",
-        script: "this.target.childDetailView = getDocTemplate(this.source?.[0])",
-        immediate: (source: Doc[]) => this.target.childDetailView = Doc.getDocTemplate(source?.[0]),
+        params: ["target", "source"], title: "=> click clicked open view",
+        script: "this.target.childClickedOpenTemplateView = getDocTemplate(this.source?.[0])",
+        immediate: (source: Doc[]) => this.target.childClickedOpenTemplateView = Doc.getDocTemplate(source?.[0]),
         initialize: emptyFunction,
     };
     _contentCommand = {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
index cf12ef382..d67d1993e 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
@@ -40,7 +40,7 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
                 const bpt = Utils.closestPtBetweenRectangles(bbounds.left, bbounds.top, bbounds.width, bbounds.height,
                     abounds.left, abounds.top, abounds.width, abounds.height,
                     apt.point.x, apt.point.y);
-                const afield = StrCast(this.props.A.props.Document[StrCast(this.props.A.props.layoutKey, "layout")]).indexOf("anchor1") === -1 ? "anchor2" : "anchor1";
+                const afield = this.props.A.props.LayoutTemplateString?.indexOf("anchor1") === -1 ? "anchor2" : "anchor1";
                 const bfield = afield === "anchor1" ? "anchor2" : "anchor1";
 
                 // really hacky stuff to make the LinkAnchorBox display where we want it to:
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
index 4b5e977df..7df1e909f 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
@@ -30,7 +30,7 @@ export class CollectionFreeFormLinksView extends React.Component {
             return drawnPairs;
         }, [] as { a: DocumentView, b: DocumentView, l: Doc[] }[]);
         return connections.filter(c =>
-            c.a.props.layoutKey && c.b.props.layoutKey && c.a.props.Document.type === DocumentType.LINK &&
+            c.a.props.Document.type === DocumentType.LINK &&
             c.a.props.bringToFront !== emptyFunction && c.b.props.bringToFront !== emptyFunction // bcz: this prevents links to be drawn to anchors in CollectionTree views -- this is a hack that should be fixed
         ).map(c => <CollectionFreeFormLinkView key={Utils.GenerateGuid()} A={c.a} B={c.b} LinkDocs={c.l} />);
     }
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 45ef0455e..707e103fb 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -5,7 +5,7 @@ import { action, computed, IReactionDisposer, observable, ObservableMap, reactio
 import { observer } from "mobx-react";
 import { computedFn } from "mobx-utils";
 import { Doc, HeightSym, Opt, WidthSym, DocListCast } from "../../../../new_fields/Doc";
-import { documentSchema, positionSchema } from "../../../../new_fields/documentSchemas";
+import { documentSchema, collectionSchema } from "../../../../new_fields/documentSchemas";
 import { Id } from "../../../../new_fields/FieldSymbols";
 import { InkData, InkField, InkTool, PointData } from "../../../../new_fields/InkField";
 import { List } from "../../../../new_fields/List";
@@ -66,8 +66,8 @@ export const panZoomSchema = createSchema({
     fitH: "number"
 });
 
-type PanZoomDocument = makeInterface<[typeof panZoomSchema, typeof documentSchema, typeof positionSchema, typeof pageSchema]>;
-const PanZoomDocument = makeInterface(panZoomSchema, documentSchema, positionSchema, pageSchema);
+type PanZoomDocument = makeInterface<[typeof panZoomSchema, typeof collectionSchema, typeof documentSchema, typeof pageSchema]>;
+const PanZoomDocument = makeInterface(panZoomSchema, collectionSchema, documentSchema, pageSchema);
 export type collectionFreeformViewProps = {
     forceScaling?: boolean; // whether to force scaling of content (needed by ImageBox)
     viewDefDivClick?: ScriptField;
@@ -858,7 +858,6 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
     @computed get backgroundActive() { return this.layoutDoc.isBackground && (this.props.ContainingCollectionView?.active() || this.props.active()); }
     backgroundHalo = () => BoolCast(this.Document.useClusters);
     parentActive = () => this.props.active() || this.backgroundActive ? true : false;
-    childLayoutFunc = () => this.props.childLayoutTemplate?.() || Cast(this.props.Document.childLayoutTemplate, Doc, null);
     getChildDocumentViewProps(childLayout: Doc, childData?: Doc): DocumentViewProps {
         return {
             ...this.props,
@@ -868,7 +867,8 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
             DataDoc: childData,
             Document: childLayout,
             LibraryPath: this.libraryPath,
-            LayoutDoc: this.childLayoutFunc,
+            LayoutTemplate: this.props.ChildLayoutTemplate,
+            LayoutTemplateString: this.props.ChildLayoutString,
             FreezeDimensions: this.props.freezeChildDimensions,
             layoutKey: undefined,
             setupDragLines: this.setupDragLines,
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
index b3a6a9deb..38b16e184 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -218,7 +218,8 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu
             Document={layout}
             DataDoc={layout.resolvedDataDoc as Doc}
             backgroundColor={this.props.backgroundColor}
-            LayoutDoc={this.props.childLayoutTemplate}
+            LayoutTemplate={this.props.ChildLayoutTemplate}
+            LayoutTemplateString={this.props.ChildLayoutString}
             LibraryPath={this.props.LibraryPath}
             FreezeDimensions={this.props.freezeChildDimensions}
             renderDepth={this.props.renderDepth + 1}
@@ -226,7 +227,7 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu
             PanelHeight={height}
             NativeHeight={returnZero}
             NativeWidth={returnZero}
-            fitToBox={BoolCast(this.props.Document._freezeChildDimensions)}
+            fitToBox={false}
             rootSelected={this.rootSelected}
             dropAction={StrCast(this.props.Document.childDropAction) as dropActionType}
             onClick={this.onChildClickHandler}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
index 0fb29ca61..b55b67a9e 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
@@ -217,7 +217,8 @@ export class CollectionMultirowView extends CollectionSubView(MultirowDocument)
             Document={layout}
             DataDoc={layout.resolvedDataDoc as Doc}
             backgroundColor={this.props.backgroundColor}
-            LayoutDoc={this.props.childLayoutTemplate}
+            LayoutTemplate={this.props.ChildLayoutTemplate}
+            LayoutTemplateString={this.props.ChildLayoutString}
             LibraryPath={this.props.LibraryPath}
             FreezeDimensions={this.props.freezeChildDimensions}
             renderDepth={this.props.renderDepth + 1}
@@ -225,7 +226,7 @@ export class CollectionMultirowView extends CollectionSubView(MultirowDocument)
             PanelHeight={height}
             NativeHeight={returnZero}
             NativeWidth={returnZero}
-            fitToBox={BoolCast(this.props.Document._freezeChildDimensions)}
+            fitToBox={false}
             rootSelected={this.rootSelected}
             dropAction={StrCast(this.props.Document.childDropAction) as dropActionType}
             onClick={this.onChildClickHandler}
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index 6ff6d1b42..3feb533a0 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -21,6 +21,7 @@ import { Docs } from "../../documents/Documents";
 import { ComputedField } from "../../../new_fields/ScriptField";
 import { Networking } from "../../Network";
 import { Upload } from "../../../server/SharedMediaTypes";
+import { LinkAnchorBox } from "./LinkAnchorBox";
 
 // testing testing 
 
@@ -266,7 +267,8 @@ export class AudioBox extends ViewBoxBaseComponent<FieldViewProps, AudioDocument
                                                 NativeHeight={returnZero}
                                                 NativeWidth={returnZero}
                                                 rootSelected={returnFalse}
-                                                layoutKey={Doc.LinkEndpoint(l, la2)}
+                                                LayoutTemplate={undefined}
+                                                LayoutTemplateString={LinkAnchorBox.LayoutString(`anchor${Doc.LinkEndpoint(l, la2)}`)}
                                                 ContainingCollectionDoc={this.props.Document}
                                                 parentActive={returnTrue}
                                                 bringToFront={emptyFunction}
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 24468dcc1..2894d9b65 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -7,7 +7,7 @@ import { DocComponent } from "../DocComponent";
 import "./CollectionFreeFormDocumentView.scss";
 import { DocumentView, DocumentViewProps } from "./DocumentView";
 import React = require("react");
-import { PositionDocument } from "../../../new_fields/documentSchemas";
+import { Document } from "../../../new_fields/documentSchemas";
 import { TraceMobx } from "../../../new_fields/util";
 import { ContentFittingDocumentView } from "./ContentFittingDocumentView";
 
@@ -28,7 +28,7 @@ export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps {
 }
 
 @observer
-export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeFormDocumentViewProps, PositionDocument>(PositionDocument) {
+export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeFormDocumentViewProps, Document>(Document) {
     @observable _animPos: number[] | undefined = undefined;
     random(min: number, max: number) { // min should not be equal to max
         const mseed = Math.abs(this.X * this.Y);
diff --git a/src/client/views/nodes/ContentFittingDocumentView.tsx b/src/client/views/nodes/ContentFittingDocumentView.tsx
index 637fd5acc..3c2c6c87e 100644
--- a/src/client/views/nodes/ContentFittingDocumentView.tsx
+++ b/src/client/views/nodes/ContentFittingDocumentView.tsx
@@ -14,7 +14,7 @@ import "./ContentFittingDocumentView.scss";
 @observer
 export class ContentFittingDocumentView extends React.Component<DocumentViewProps>{
     public get displayName() { return "DocumentView(" + this.props.Document?.title + ")"; } // this makes mobx trace() statements more descriptive
-    private get layoutDoc() { return this.props.LayoutDoc?.() || Doc.Layout(this.props.Document); }
+    private get layoutDoc() { return this.props.LayoutTemplate?.() || Doc.Layout(this.props.Document); }
     @computed get freezeDimensions() { return this.props.FreezeDimensions; }
     nativeWidth = () => NumCast(this.layoutDoc?._nativeWidth, this.props.NativeWidth?.() || (this.freezeDimensions && this.layoutDoc ? this.layoutDoc[WidthSym]() : this.props.PanelWidth()));
     nativeHeight = () => NumCast(this.layoutDoc?._nativeHeight, this.props.NativeHeight?.() || (this.freezeDimensions && this.layoutDoc ? this.layoutDoc[HeightSym]() : this.props.PanelHeight()));
@@ -56,7 +56,8 @@ export class ContentFittingDocumentView extends React.Component<DocumentViewProp
                     <DocumentView {...this.props}
                         Document={this.props.Document}
                         DataDoc={this.props.DataDoc}
-                        LayoutDoc={this.props.LayoutDoc}
+                        LayoutTemplate={this.props.LayoutTemplate}
+                        LayoutTemplateString={this.props.LayoutTemplateString}
                         LibraryPath={this.props.LibraryPath}
                         NativeWidth={this.nativeWidth}
                         NativeHeight={this.nativeHeight}
diff --git a/src/client/views/nodes/DocumentBox.tsx b/src/client/views/nodes/DocumentBox.tsx
index 88eeb1137..8d422fe67 100644
--- a/src/client/views/nodes/DocumentBox.tsx
+++ b/src/client/views/nodes/DocumentBox.tsx
@@ -2,7 +2,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { IReactionDisposer, reaction, computed } from "mobx";
 import { observer } from "mobx-react";
 import { Doc, Field } from "../../../new_fields/Doc";
-import { documentSchema } from "../../../new_fields/documentSchemas";
+import { documentSchema, collectionSchema } from "../../../new_fields/documentSchemas";
 import { makeInterface } from "../../../new_fields/Schema";
 import { ComputedField } from "../../../new_fields/ScriptField";
 import { Cast, NumCast, StrCast } from "../../../new_fields/Types";
@@ -16,9 +16,11 @@ import { FieldView, FieldViewProps } from "./FieldView";
 import React = require("react");
 import { TraceMobx } from "../../../new_fields/util";
 import { Docs } from "../../documents/Documents";
+import { KeyValueBox } from "./KeyValueBox";
+import { DocumentView } from "./DocumentView";
 
-type DocHolderBoxSchema = makeInterface<[typeof documentSchema]>;
-const DocHolderBoxDocument = makeInterface(documentSchema);
+type DocHolderBoxSchema = makeInterface<[typeof documentSchema, typeof collectionSchema]>;
+const DocHolderBoxDocument = makeInterface(documentSchema, collectionSchema);
 
 @observer
 export class DocHolderBox extends ViewBoxAnnotatableComponent<FieldViewProps, DocHolderBoxSchema>(DocHolderBoxDocument) {
@@ -43,7 +45,7 @@ export class DocHolderBox extends ViewBoxAnnotatableComponent<FieldViewProps, Do
         funcs.push({ description: (this.isSelectionLocked() ? "Show" : "Lock") + " Selection", event: () => this.toggleLockSelection, icon: "expand-arrows-alt" });
         funcs.push({ description: (this.layoutDoc.excludeCollections ? "Include" : "Exclude") + " Collections", event: () => this.layoutDoc.excludeCollections = !this.layoutDoc.excludeCollections, icon: "expand-arrows-alt" });
         funcs.push({ description: `${this.layoutDoc.forceActive ? "Select" : "Force"} Contents Active`, event: () => this.layoutDoc.forceActive = !this.layoutDoc.forceActive, icon: "project-diagram" });
-        funcs.push({ description: `Show ${this.layoutDoc.childTemplateName !== "keyValue" ? "key values" : "contents"}`, event: () => this.layoutDoc.childTemplateName = this.layoutDoc.childTemplateName ? undefined : "keyValue", icon: "project-diagram" });
+        funcs.push({ description: `Show ${this.layoutDoc.childLayoutTemplateName !== "keyValue" ? "key values" : "contents"}`, event: () => this.layoutDoc.childLayoutString = this.layoutDoc.childLayoutString ? undefined : "<KeyValueBox {...props} />", icon: "project-diagram" });
 
         ContextMenu.Instance.addItem({ description: "Options...", subitems: funcs, icon: "asterisk" });
     }
@@ -103,6 +105,7 @@ export class DocHolderBox extends ViewBoxAnnotatableComponent<FieldViewProps, Do
     pwidth = () => this.props.PanelWidth() - 2 * this.xPad;
     pheight = () => this.props.PanelHeight() - 2 * this.yPad;
     getTransform = () => this.props.ScreenToLocalTransform().translate(-this.xPad, -this.yPad);
+    isActive = () => this.active() || !this.props.renderDepth;
     get renderContents() {
         const containedDoc = Cast(this.dataDoc[this.props.fieldKey], Doc, null);
         const childTemplateName = StrCast(this.layoutDoc.childTemplateName);
@@ -112,33 +115,62 @@ export class DocHolderBox extends ViewBoxAnnotatableComponent<FieldViewProps, Do
                 Doc.expandTemplateLayout(Cast(containedDoc["layout_" + childTemplateName], Doc, null), containedDoc, undefined);
             }, 0);
         }
-        const contents = !(containedDoc instanceof Doc) ? (null) : <ContentFittingDocumentView
-            Document={containedDoc}
-            DataDoc={undefined}
-            LibraryPath={emptyPath}
-            ContainingCollectionView={this as any} // bcz: hack!  need to pass a prop that can be used to select the container (ie, 'this') when the up selector in document decorations is clicked.  currently, the up selector allows only a containing collection to be selected
-            ContainingCollectionDoc={undefined}
-            fitToBox={true}
-            layoutKey={childTemplateName ? "layout_" + childTemplateName : "layout"}
-            rootSelected={this.props.isSelected}
-            addDocument={this.props.addDocument}
-            moveDocument={this.props.moveDocument}
-            removeDocument={this.props.removeDocument}
-            addDocTab={this.props.addDocTab}
-            pinToPres={this.props.pinToPres}
-            ScreenToLocalTransform={this.getTransform}
-            renderDepth={this.props.renderDepth + 1}
-            NativeHeight={returnZero}
-            NativeWidth={returnZero}
-            PanelWidth={this.pwidth}
-            PanelHeight={this.pheight}
-            focus={this.props.focus}
-            parentActive={this.props.active}
-            dontRegisterView={!this.isSelectionLocked()}
-            whenActiveChanged={this.props.whenActiveChanged}
-            bringToFront={returnFalse}
-            ContentScaling={returnOne}
-        />;
+        const contents = !(containedDoc instanceof Doc) ? (null) : this.layoutDoc.childLayoutString ?
+            <DocumentView
+                Document={containedDoc}
+                DataDoc={undefined}
+                LibraryPath={emptyPath}
+                ContainingCollectionView={this as any} // bcz: hack!  need to pass a prop that can be used to select the container (ie, 'this') when the up selector in document decorations is clicked.  currently, the up selector allows only a containing collection to be selected
+                ContainingCollectionDoc={undefined}
+                fitToBox={true}
+                LayoutTemplateString={StrCast(this.layoutDoc.childLayoutString)}
+                //layoutKey={childTemplateName ? "layout_" + childTemplateName : "layout"}
+                rootSelected={this.props.isSelected}
+                addDocument={this.props.addDocument}
+                moveDocument={this.props.moveDocument}
+                removeDocument={this.props.removeDocument}
+                addDocTab={this.props.addDocTab}
+                pinToPres={this.props.pinToPres}
+                ScreenToLocalTransform={this.getTransform}
+                renderDepth={this.props.renderDepth + 1}
+                NativeHeight={returnZero}
+                NativeWidth={returnZero}
+                PanelWidth={this.pwidth}
+                PanelHeight={this.pheight}
+                focus={this.props.focus}
+                parentActive={this.isActive}
+                dontRegisterView={!this.isSelectionLocked()}
+                whenActiveChanged={this.props.whenActiveChanged}
+                bringToFront={returnFalse}
+                ContentScaling={returnOne} /> :
+            <ContentFittingDocumentView
+                Document={containedDoc}
+                DataDoc={undefined}
+                LibraryPath={emptyPath}
+                ContainingCollectionView={this as any} // bcz: hack!  need to pass a prop that can be used to select the container (ie, 'this') when the up selector in document decorations is clicked.  currently, the up selector allows only a containing collection to be selected
+                ContainingCollectionDoc={undefined}
+                fitToBox={true}
+                LayoutTemplateString={StrCast(this.layoutDoc.childLayoutString)}
+                //layoutKey={childTemplateName ? "layout_" + childTemplateName : "layout"}
+                rootSelected={this.props.isSelected}
+                addDocument={this.props.addDocument}
+                moveDocument={this.props.moveDocument}
+                removeDocument={this.props.removeDocument}
+                addDocTab={this.props.addDocTab}
+                pinToPres={this.props.pinToPres}
+                ScreenToLocalTransform={this.getTransform}
+                renderDepth={this.props.renderDepth + 1}
+                NativeHeight={returnZero}
+                NativeWidth={returnZero}
+                PanelWidth={this.pwidth}
+                PanelHeight={this.pheight}
+                focus={this.props.focus}
+                parentActive={this.isActive}
+                dontRegisterView={!this.isSelectionLocked()}
+                whenActiveChanged={this.props.whenActiveChanged}
+                bringToFront={returnFalse}
+                ContentScaling={returnOne}
+            />;
         return contents;
     }
     render() {
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 4d20d3e2c..749fb98be 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -96,8 +96,6 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
     isSelected: (outsideReaction: boolean) => boolean,
     select: (ctrl: boolean) => void,
     layoutKey: string,
-    forceLayout?: string,
-    forceFieldKey?: string,
     hideOnLeave?: boolean,
     makeLink?: () => Opt<Doc>,  // function to call when a link is made
 }> {
@@ -105,6 +103,7 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
         TraceMobx();
         if (!this.layoutDoc) return "<p>awaiting layout</p>";
         // const layout = Cast(this.layoutDoc[StrCast(this.layoutDoc.layoutKey, this.layoutDoc === this.props.Document ? this.props.layoutKey : "layout")], "string");  // bcz: replaced this with below... is it right?
+        if (this.props.LayoutTemplateString) return this.props.LayoutTemplateString;
         const layout = Cast(this.layoutDoc[this.layoutDoc === this.props.Document && this.props.layoutKey ? this.props.layoutKey : StrCast(this.layoutDoc.layoutKey, "layout")], "string");
         if (this.props.layoutKey === "layout_keyValue") {
             return StrCast(this.props.Document.layout_keyValue, KeyValueBox.LayoutString("data"));
@@ -127,8 +126,8 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
     get layoutDoc() {
         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.LayoutDoc?.() || Doc.Layout(this.props.Document, this.props.layoutKey ? Cast(this.props.Document[this.props.layoutKey], Doc, null) : undefined);
-        const template: Doc = this.props.LayoutDoc?.() ||
+        // 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) ||
             (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);
@@ -186,25 +185,22 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
         //  layoutFrame = splits.length > 1 ? splits[0] + splits[1].replace(/{([^{}]|(?R))*}/, replacer4) : ""; // might have been more elegant if javascript supported recursive patterns
 
         return (this.props.renderDepth > 12 || !layoutFrame || !this.layoutDoc) ? (null) :
-            this.props.forceLayout === "FormattedTextBox" && this.props.forceFieldKey ?
-                <FormattedTextBox {...bindings.props} fieldKey={this.props.forceFieldKey} />
-                :
-                <ObserverJsxParser
-                    key={42}
-                    blacklistedAttrs={[]}
-                    renderInWrapper={false}
-                    components={{
-                        FormattedTextBox, ImageBox, DirectoryImportBox, FontIconBox, LabelBox, SliderBox, FieldView,
-                        CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, WebBox, KeyValueBox,
-                        PDFBox, VideoBox, AudioBox, PresBox, YoutubeBox, PresElementBox, QueryBox,
-                        ColorBox, DashWebRTCVideo, LinkAnchorBox, InkingStroke, DocHolderBox, LinkBox, ScriptingBox,
-                        RecommendationsBox, ScreenshotBox, HTMLtag
-                    }}
-                    bindings={bindings}
-                    jsx={layoutFrame}
-                    showWarnings={true}
-
-                    onError={(test: any) => { console.log(test); }}
-                />;
+            <ObserverJsxParser
+                key={42}
+                blacklistedAttrs={[]}
+                renderInWrapper={false}
+                components={{
+                    FormattedTextBox, ImageBox, DirectoryImportBox, FontIconBox, LabelBox, SliderBox, FieldView,
+                    CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, WebBox, KeyValueBox,
+                    PDFBox, VideoBox, AudioBox, PresBox, YoutubeBox, PresElementBox, QueryBox,
+                    ColorBox, DashWebRTCVideo, LinkAnchorBox, InkingStroke, DocHolderBox, LinkBox, ScriptingBox,
+                    RecommendationsBox, ScreenshotBox, HTMLtag
+                }}
+                bindings={bindings}
+                jsx={layoutFrame}
+                showWarnings={true}
+
+                onError={(test: any) => { console.log(test); }}
+            />;
     }
 }
\ No newline at end of file
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index f1efa48f4..7c7c03db2 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -5,22 +5,21 @@ import { action, computed, observable, runInAction } from "mobx";
 import { observer } from "mobx-react";
 import * as rp from "request-promise";
 import { Doc, DocListCast, HeightSym, Opt, WidthSym } from "../../../new_fields/Doc";
-import { Document, PositionDocument } from '../../../new_fields/documentSchemas';
+import { Document } from '../../../new_fields/documentSchemas';
 import { Id } from '../../../new_fields/FieldSymbols';
 import { InkTool } from '../../../new_fields/InkField';
-import { RichTextField } from '../../../new_fields/RichTextField';
 import { listSpec } from "../../../new_fields/Schema";
 import { SchemaHeaderField } from '../../../new_fields/SchemaHeaderField';
 import { ScriptField } from '../../../new_fields/ScriptField';
 import { BoolCast, Cast, NumCast, StrCast } from "../../../new_fields/Types";
-import { AudioField, ImageField, PdfField, VideoField } from '../../../new_fields/URLField';
+import { ImageField } from '../../../new_fields/URLField';
 import { TraceMobx } from '../../../new_fields/util';
 import { GestureUtils } from '../../../pen-gestures/GestureUtils';
 import { emptyFunction, OmitKeys, returnOne, returnTransparent, Utils } from "../../../Utils";
 import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils';
 import { ClientRecommender } from '../../ClientRecommender';
 import { DocServer } from "../../DocServer";
-import { Docs, DocumentOptions, DocUtils } from "../../documents/Documents";
+import { Docs, DocUtils } from "../../documents/Documents";
 import { DocumentType } from '../../documents/DocumentTypes';
 import { ClientUtils } from '../../util/ClientUtils';
 import { DocumentManager } from "../../util/DocumentManager";
@@ -42,6 +41,7 @@ import { InkingControl } from '../InkingControl';
 import { KeyphraseQueryView } from '../KeyphraseQueryView';
 import { DocumentContentsView } from "./DocumentContentsView";
 import "./DocumentView.scss";
+import { LinkAnchorBox } from './LinkAnchorBox';
 import { RadialMenu } from './RadialMenu';
 import React = require("react");
 
@@ -58,7 +58,8 @@ export interface DocumentViewProps {
     NativeHeight: () => number;
     Document: Doc;
     DataDoc?: Doc;
-    LayoutDoc?: () => Opt<Doc>;
+    LayoutTemplateString?: string;
+    LayoutTemplate?: () => Opt<Doc>;
     LibraryPath: Doc[];
     fitToBox?: boolean;
     contextMenuItems?: () => { script: ScriptField, label: string }[];
@@ -454,8 +455,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             const dY = -1 * Math.sign(dH);
 
             if (dX !== 0 || dY !== 0 || dW !== 0 || dH !== 0) {
-                const doc = PositionDocument(this.props.Document);
-                const layoutDoc = PositionDocument(Doc.Layout(this.props.Document));
+                const doc = Document(this.props.Document);
+                const layoutDoc = Document(Doc.Layout(this.props.Document));
                 let nwidth = layoutDoc._nativeWidth || 0;
                 let nheight = layoutDoc._nativeHeight || 0;
                 const width = (layoutDoc._width || 0);
@@ -984,13 +985,15 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     @computed get contents() {
         TraceMobx();
         return (<>
-            <DocumentContentsView key={1} ContainingCollectionView={this.props.ContainingCollectionView}
+            <DocumentContentsView key={1}
+                ContainingCollectionView={this.props.ContainingCollectionView}
                 ContainingCollectionDoc={this.props.ContainingCollectionDoc}
                 NativeWidth={this.NativeWidth}
                 NativeHeight={this.NativeHeight}
                 Document={this.props.Document}
                 DataDoc={this.props.DataDoc}
-                LayoutDoc={this.props.LayoutDoc}
+                LayoutTemplateString={this.props.LayoutTemplateString}
+                LayoutTemplate={this.props.LayoutTemplate}
                 makeLink={this.makeLink}
                 rootSelected={this.rootSelected}
                 RenderData={this.props.RenderData}
@@ -1021,7 +1024,6 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         </>
         );
     }
-    linkEndpoint = (linkDoc: Doc) => Doc.LinkEndpoint(linkDoc, this.props.Document);
 
     // used to decide whether a link anchor view should be created or not.
     // if it's a tempoarl link (currently just for Audio), then the audioBox will display the anchor and we don't want to display it here.
@@ -1049,12 +1051,12 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 ContainingCollectionDoc={this.props.Document} // bcz: hack this.props.Document is not a collection  Need a better prop for passing the containing document to the LinkAnchorBox
                 PanelWidth={this.anchorPanelWidth}
                 PanelHeight={this.anchorPanelHeight}
-                layoutKey={this.linkEndpoint(d)}
                 ContentScaling={returnOne}
                 backgroundColor={returnTransparent}
                 removeDocument={this.hideLinkAnchor}
                 pointerEvents={false}
-                LayoutDoc={undefined}
+                LayoutTemplate={undefined}
+                LayoutTemplateString={LinkAnchorBox.LayoutString(`anchor${Doc.LinkEndpoint(d, this.props.Document)}`)}
             />);
     }
     @computed get innards() {
@@ -1073,8 +1075,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             <div className="documentView-captionWrapper" style={{ backgroundColor: StrCast(this.layoutDoc["caption-backgroundColor"]), color: StrCast(this.layoutDoc["caption-color"]) }}>
                 <DocumentContentsView {...OmitKeys(this.props, ['children']).omit}
                     hideOnLeave={true}
-                    forceLayout={"FormattedTextBox"}
-                    forceFieldKey={showCaption}
+                    layoutTemplateString={`<FormattedTextBox {...props} fieldKey={'${showCaption}'}/>`}
                     ContentScaling={this.childScaling}
                     ChromeHeight={this.chromeHeight}
                     isSelected={this.isSelected}
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 1efee4f5a..40d55ce38 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -50,13 +50,12 @@ export interface FieldViewProps {
     setVideoBox?: (player: VideoBox) => void;
     ContentScaling: () => number;
     ChromeHeight?: () => number;
-    childLayoutTemplate?: () => Opt<Doc>;
+    RenderData?: () => Doc;
     // properties intended to be used from within layout strings (otherwise use the function equivalents that work more efficiently with React)
     height?: number;
     width?: number;
     background?: string;
     color?: string;
-    RenderData?: () => Doc;
 }
 
 @observer
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index 2970674a2..d43936949 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -36,7 +36,7 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
     @observable private _keyInput: string = "";
     @observable private _valueInput: string = "";
     @computed get splitPercentage() { return NumCast(this.props.Document.schemaSplitPercentage, 50); }
-    get fieldDocToLayout() { return this.props.fieldKey ? Cast(this.props.Document[this.props.fieldKey], Doc, null) : this.props.Document; }
+    get fieldDocToLayout() { return this.props.Document; }
 
     @action
     onEnterKey = (e: React.KeyboardEvent): void => {
diff --git a/src/client/views/nodes/LinkAnchorBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx
index 6c50abf21..bc36e056e 100644
--- a/src/client/views/nodes/LinkAnchorBox.tsx
+++ b/src/client/views/nodes/LinkAnchorBox.tsx
@@ -17,7 +17,6 @@ import { LinkEditor } from "../linking/LinkEditor";
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { SelectionManager } from "../../util/SelectionManager";
 import { TraceMobx } from "../../../new_fields/util";
-import { DocumentView } from "./DocumentView";
 const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx
index 125690dc7..a0ecc9ff5 100644
--- a/src/client/views/nodes/ScreenshotBox.tsx
+++ b/src/client/views/nodes/ScreenshotBox.tsx
@@ -5,7 +5,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { action, computed, IReactionDisposer, observable, runInAction } from "mobx";
 import { observer } from "mobx-react";
 import * as rp from 'request-promise';
-import { documentSchema, positionSchema } from "../../../new_fields/documentSchemas";
+import { documentSchema } from "../../../new_fields/documentSchemas";
 import { makeInterface } from "../../../new_fields/Schema";
 import { Cast, NumCast } from "../../../new_fields/Types";
 import { VideoField } from "../../../new_fields/URLField";
@@ -20,8 +20,8 @@ import { FieldView, FieldViewProps } from './FieldView';
 import "./ScreenshotBox.scss";
 const path = require('path');
 
-type ScreenshotDocument = makeInterface<[typeof documentSchema, typeof positionSchema]>;
-const ScreenshotDocument = makeInterface(documentSchema, positionSchema);
+type ScreenshotDocument = makeInterface<[typeof documentSchema]>;
+const ScreenshotDocument = makeInterface(documentSchema);
 
 library.add(faVideo);
 
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 613929bca..266b7f43f 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -21,14 +21,14 @@ import { DocumentDecorations } from "../DocumentDecorations";
 import { InkingControl } from "../InkingControl";
 import { FieldView, FieldViewProps } from './FieldView';
 import "./VideoBox.scss";
-import { documentSchema, positionSchema } from "../../../new_fields/documentSchemas";
+import { documentSchema } from "../../../new_fields/documentSchemas";
 const path = require('path');
 
 export const timeSchema = createSchema({
     currentTimecode: "number",  // the current time of a video or other linear, time-based document.  Note, should really get set on an extension field, but that's more complicated when it needs to be set since the extension doc needs to be found first
 });
-type VideoDocument = makeInterface<[typeof documentSchema, typeof positionSchema, typeof timeSchema]>;
-const VideoDocument = makeInterface(documentSchema, positionSchema, timeSchema);
+type VideoDocument = makeInterface<[typeof documentSchema, typeof timeSchema]>;
+const VideoDocument = makeInterface(documentSchema, timeSchema);
 
 library.add(faVideo);
 
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index 71325d94f..9256f82c2 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -772,7 +772,7 @@ export namespace Doc {
 
 
     export function LinkOtherAnchor(linkDoc: Doc, anchorDoc: Doc) { return Doc.AreProtosEqual(anchorDoc, Cast(linkDoc.anchor1, Doc) as Doc) ? Cast(linkDoc.anchor2, Doc) as Doc : Cast(linkDoc.anchor1, Doc) as Doc; }
-    export function LinkEndpoint(linkDoc: Doc, anchorDoc: Doc) { return Doc.AreProtosEqual(anchorDoc, Cast(linkDoc.anchor1, Doc) as Doc) ? "layout_key1" : "layout_key2"; }
+    export function LinkEndpoint(linkDoc: Doc, anchorDoc: Doc) { return Doc.AreProtosEqual(anchorDoc, Cast(linkDoc.anchor1, Doc) as Doc) ? "1" : "2"; }
 
     export function linkFollowUnhighlight() {
         Doc.UnhighlightAll();
diff --git a/src/new_fields/documentSchemas.ts b/src/new_fields/documentSchemas.ts
index fd9a304f9..e71fc27f3 100644
--- a/src/new_fields/documentSchemas.ts
+++ b/src/new_fields/documentSchemas.ts
@@ -4,13 +4,25 @@ import { Doc } from "./Doc";
 import { DateField } from "./DateField";
 
 export const documentSchema = createSchema({
+    // content properties
     type: "string",             // enumerated type of document -- should be template-specific (ie, start with an '_')
-    layout: "string",           // this is the native layout string for the document.  templates can be added using other fields and setting layoutKey below
-    layoutKey: "string",        // holds the field key for the field that actually holds the current lyoat
     title: "string",            // document title (can be on either data document or layout)
-    dropAction: "string",       // override specifying what should happen when this document is dropped (can be "alias", "copy", "move")
-    targetDropAction: "string", // allows the target of a drop event to specify the dropAction ("alias", "copy", "move")
-    childDropAction: "string",  // specify the override for what should happen when the child of a collection is dragged from it and dropped (can be "alias" or "copy")
+    isTemplateForField: "string",// if specified, it indicates the document is a template that renders the specified field
+    creationDate: DateField,    // when the document was created
+    links: listSpec(Doc),       // computed (readonly) list of links associated with this document
+
+    // "Location" properties in a very general sense
+    currentTimecode: "number",  // current play back time of a temporal document (video / audio)
+    displayTimecode: "number",  // the time that a document should be displayed (e.g., time an annotation should be displayed on a video)
+    inOverlay: "boolean",       // whether the document is rendered in an OverlayView which handles selection/dragging differently
+    x: "number",                // x coordinate when in a freeform view 
+    y: "number",                // y coordinate when in a freeform view 
+    z: "number",                // z "coordinate" - non-zero specifies the overlay layer of a freeformview
+    zIndex: "number",           // zIndex of a document in a freeform view
+    scrollY: "number",          // "command" to scroll a document to a position on load (the value will be reset to 0 after that )
+    scrollTop: "number",        // scroll position of a scrollable document (pdf, text, web)
+
+    // appearance properties on the layout
     _autoHeight: "boolean",     // whether the height of the document should be computed automatically based on its contents
     _nativeWidth: "number",     // native width of document which determines how much document contents are scaled when the document's width is set
     _nativeHeight: "number",    // "
@@ -27,59 +39,57 @@ export const documentSchema = createSchema({
     _showAudio: "boolean",      // whether to show the audio record icon on documents
     _freeformLayoutEngine: "string",// the string ID for the layout engine to use to layout freeform view documents
     _LODdisable: "boolean",     // whether to disbale LOD switching for CollectionFreeFormViews
-    _pivotField: "string",      // specifies which field should be used as the timeline/pivot axis
+    _pivotField: "string",      // specifies which field key should be used as the timeline/pivot axis
     _replacedChrome: "string",  // what the default chrome is replaced with. Currently only supports the value of 'replaced' for PresBox's.
     _chromeStatus: "string",    // determines the state of the collection chrome. values allowed are 'replaced', 'enabled', 'disabled', 'collapsed'
-    _freezeChildDimensions: "boolean", // freezes child document dimensions (e.g., used by time/pivot view to make sure all children will be scaled to fit their display rectangle)
     _fontSize: "number",
     _fontFamily: "string",
-    isInPlaceContainer: "boolean",// whether the marked object will display addDocTab() calls that target "inPlace" destinations
-    color: "string",            // foreground color of document
+
+    // appearance properties on the data document
     backgroundColor: "string",  // background color of document
+    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
+    fontSize: "string",
+    layout: "string",           // this is the native layout string for the document.  templates can be added using other fields and setting layoutKey below
+    layoutKey: "string",        // holds the field key for the field that actually holds the current lyoat
+    letterSpacing: "string",
     opacity: "number",          // opacity of document
-    creationDate: DateField,    // when the document was created
-    links: listSpec(Doc),       // computed (readonly) list of links associated with this document
+    strokeWidth: "number",
+    textTransform: "string",
+    treeViewOpen: "boolean",    //  flag denoting whether the documents sub-tree (contents) is visible or hidden
+    treeViewExpandedView: "string", // name of field whose contents are being displayed as the document's subtree
+    treeViewPreventOpen: "boolean", // ignores the treeViewOpen flag (for allowing a view to not be slaved to other views of the document)
+
+    // interaction and linking properties
+    ignoreClick: "boolean",     // whether documents ignores input clicks (but does not ignore manipulation and other events) 
     onClick: ScriptField,       // script to run when document is clicked (can be overriden by an onClick prop)
     onPointerDown: ScriptField, // script to run when document is clicked (can be overriden by an onClick prop)
     onPointerUp: ScriptField,   // script to run when document is clicked (can be overriden by an onClick prop)
     onDragStart: ScriptField,   // script to run when document is dragged (without being selected).  the script should return the Doc to be dropped.
-    dragFactory: Doc,           // the document that serves as the "template" for the onDragStart script.  ie, to drag out copies of the dragFactory document.
-    removeDropProperties: listSpec("string"), // properties that should be removed from the alias/copy/etc of this document when it is dropped
-    isTemplateForField: "string",// when specifies a field key, then the containing document is a template that renders the specified field
-    isBackground: "boolean",    // whether document is a background element and ignores input events (can only select with marquee)
-    treeViewOpen: "boolean",    //  flag denoting whether the documents sub-tree (contents) is visible or hidden
-    treeViewExpandedView: "string", // name of field whose contents are being displayed as the document's subtree
-    treeViewPreventOpen: "boolean", // ignores the treeViewOpen flag (for allowing a view to not be slaved to other views of the document)
-    currentTimecode: "number",  // current play back time of a temporal document (video / audio)
     followLinkLocation: "string",// flag for where to place content when following a click interaction (e.g., onRight, inPlace, inTab, ) 
+    isInPlaceContainer: "boolean",// whether the marked object will display addDocTab() calls that target "inPlace" destinations
+    isLinkButton: "boolean",    // whether document functions as a link follow button to follow the first link on the document when clicked   
+    isBackground: "boolean",    // whether document is a background element and ignores input events (can only select with marquee)
     lockedPosition: "boolean",  // whether the document can be moved (dragged)
     lockedTransform: "boolean", // whether the document can be panned/zoomed
-    inOverlay: "boolean",       // whether the document is rendered in an OverlayView which handles selection/dragging differently
-    borderRounding: "string",   // border radius rounding of document
-    heading: "number",          // the logical layout 'heading' of this document (used by rule provider to stylize h1 header elements, from h2, etc)
-    isLinkButton: "boolean",    // whether document functions as a link follow button to follow the first link on the document when clicked   
-    ignoreClick: "boolean",     // whether documents ignores input clicks (but does not ignore manipulation and other events) 
-    scrollToLinkID: "string",   // id of link being traversed. allows this doc to scroll/highlight/etc its link anchor. scrollToLinkID should be set to undefined by this doc after it sets up its scroll,etc.
-    scrollY: "number",          // "command" to scroll a document to a position on load (the value will be reset to 0 after that )
-    scrollTop: "number",        // scroll position of a scrollable document (pdf, text, web)
-    strokeWidth: "number",
-    fontSize: "string",
-    fitToBox: "boolean",        // whether freeform view contents should be zoomed/panned to fill the area of the document view
-    letterSpacing: "string",
-    textTransform: "string",
-    childTemplateName: "string" // the name of a template to use to override the layoutKey when rendering a document in DocHolderBox 
-});
 
-export const positionSchema = createSchema({
-    zIndex: "number",
-    x: "number",
-    y: "number",
-    z: "number",
+    // drag drop properties
+    dragFactory: Doc,           // the document that serves as the "template" for the onDragStart script.  ie, to drag out copies of the dragFactory document.
+    dropAction: "string",       // override specifying what should happen when this document is dropped (can be "alias", "copy", "move")
+    targetDropAction: "string", // allows the target of a drop event to specify the dropAction ("alias", "copy", "move")
+    childDropAction: "string",  // specify the override for what should happen when the child of a collection is dragged from it and dropped (can be "alias" or "copy")
+    removeDropProperties: listSpec("string"), // properties that should be removed from the alias/copy/etc of this document when it is dropped
 });
 
+
 export const collectionSchema = createSchema({
-    childLayoutTemplate: Doc, // layout template for children of a collecion
-    childDetailView: Doc, // layout template to apply to a child when its clicked on in a collection and opened (requires onChildClick or other script to use this field)
+    childLayoutTemplateName: "string", // the name of a template to use to override the layoutKey when rendering a document -- ONLY used in DocHolderBox 
+    childLayoutTemplate: Doc, // layout template to use to render children of a collecion
+    childLayoutString: "string", //layout string to use to render children of a collection
+    childClickedOpenTemplateView: Doc, // layout template to apply to a child when its clicked on in a collection and opened (requires onChildClick or other script to read this value and apply template)
+    dontRegisterChildViews: "boolean", // whether views made of this document are registered so that they can be found when drawing links scrollToLinkID: "string",   // id of link being traversed. allows this doc to scroll/highlight/etc its link anchor. scrollToLinkID should be set to undefined by this doc after it sets up its scroll,etc.
     onChildClick: ScriptField, // script to run for each child when its clicked
     onChildDoubleClick: ScriptField, // script to run for each child when its clicked
     onCheckedClick: ScriptField, // script to run when a checkbox is clicked next to a child in a tree view
@@ -87,6 +97,3 @@ export const collectionSchema = createSchema({
 
 export type Document = makeInterface<[typeof documentSchema]>;
 export const Document = makeInterface(documentSchema);
-
-export type PositionDocument = makeInterface<[typeof documentSchema, typeof positionSchema]>;
-export const PositionDocument = makeInterface(documentSchema, positionSchema);
diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts
index 2ae42bf52..b9de93559 100644
--- a/src/server/authentication/models/current_user_utils.ts
+++ b/src/server/authentication/models/current_user_utils.ts
@@ -485,7 +485,7 @@ export class CurrentUserUtils {
                 _width: 50, _height: 25, title: "Library", _fontSize: 10,
                 letterSpacing: "0px", textTransform: "unset", borderRounding: "5px 5px 0px 0px", boxShadow: "3px 3px 0px rgb(34, 34, 34)",
                 sourcePanel: new PrefetchProxy(Docs.Create.TreeDocument([workspaces, documents, recentlyClosed, doc], {
-                    title: "Library", _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, childDropAction: "move", lockedPosition: true, boxShadow: "0 0", dontRegisterChildren: true
+                    title: "Library", _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, childDropAction: "move", lockedPosition: true, boxShadow: "0 0", dontRegisterChildViews: true
                 })) as any as Doc,
                 targetContainer: new PrefetchProxy(sidebarContainer) as any as Doc,
                 onClick: ScriptField.MakeScript("this.targetContainer.proto = this.sourcePanel;")
-- 
cgit v1.2.3-70-g09d2


From 100ad0da00f2a5cea13508abc0c3a8c644095d65 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Sat, 2 May 2020 14:31:16 -0400
Subject: turn off targetDropAction when dropping in same colleciton.  cleaned
 up PresBox stuff to use single template to render all contents (which are
 otherwise unmodified).

---
 src/client/util/DragManager.ts                     |  4 +-
 .../views/collections/CollectionCarouselView.tsx   |  2 +-
 .../views/collections/CollectionStackingView.tsx   |  1 -
 src/client/views/collections/CollectionSubView.tsx | 11 +++-
 src/client/views/collections/CollectionView.tsx    |  4 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  1 -
 src/client/views/nodes/DocumentView.tsx            |  2 -
 src/client/views/nodes/FieldView.tsx               |  3 +-
 src/client/views/nodes/PresBox.tsx                 | 72 ++++++++++++----------
 .../views/nodes/formattedText/FormattedTextBox.tsx |  6 +-
 .../views/presentationview/PresElementBox.tsx      | 16 ++---
 src/new_fields/documentSchemas.ts                  |  2 +-
 .../authentication/models/current_user_utils.ts    |  7 +--
 13 files changed, 71 insertions(+), 60 deletions(-)

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

diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index c06ad3d60..041f2fc2c 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -185,7 +185,8 @@ export namespace DragManager {
     export function MakeDropTarget(
         element: HTMLElement,
         dropFunc: (e: Event, de: DropEvent) => void,
-        doc?: Doc
+        doc?: Doc,
+        preDropFunc?: (e: Event, de: DropEvent) => void,
     ): DragDropDisposer {
         if ("canDrop" in element.dataset) {
             throw new Error(
@@ -199,6 +200,7 @@ export namespace DragManager {
             if (de.complete.docDragData && doc?.targetDropAction) {
                 de.complete.docDragData.dropAction = StrCast(doc.targetDropAction) as dropActionType;
             }
+            preDropFunc?.(e, de);
         };
         element.addEventListener("dashOnDrop", handler);
         doc && element.addEventListener("dashPreDrop", preDropHandler);
diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx
index 769b323ae..a04136e51 100644
--- a/src/client/views/collections/CollectionCarouselView.tsx
+++ b/src/client/views/collections/CollectionCarouselView.tsx
@@ -69,7 +69,7 @@ export class CollectionCarouselView extends CollectionSubView(CarouselDocument)
                     <FormattedTextBox key={index} {...this.props}
                         xMargin={NumCast(this.layoutDoc["_carousel-caption-xMargin"])}
                         yMargin={NumCast(this.layoutDoc["_carousel-caption-yMargin"])}
-                        Document={this.childLayoutPairs[index].layout} DataDoc={undefined} fieldKey={"caption"}></FormattedTextBox>
+                        Document={this.childLayoutPairs[index].layout} DataDoc={undefined} fieldKey={"caption"} />
                 </div>
             </>;
     }
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index eb70cec9d..1fd5c3f44 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -178,7 +178,6 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument)
             LibraryPath={this.props.LibraryPath}
             FreezeDimensions={this.props.freezeChildDimensions}
             renderDepth={this.props.renderDepth + 1}
-            RenderData={this.props.RenderData}
             PanelWidth={width}
             PanelHeight={height}
             NativeHeight={returnZero}
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index aaea13ded..af642bc52 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -67,7 +67,7 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
             this.multiTouchDisposer?.();
             if (ele) {
                 this._mainCont = ele;
-                this.dropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.layoutDoc);
+                this.dropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.layoutDoc, this.onInternalPreDrop.bind(this));
                 this.gestureDisposer = GestureUtils.MakeGestureTarget(ele, this.onGesture.bind(this));
                 this.multiTouchDisposer = InteractionUtils.MakeMultiTouchTarget(ele, this.onTouchStart.bind(this));
             }
@@ -195,6 +195,15 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
         protected onGesture(e: Event, ge: GestureUtils.GestureEvent) {
         }
 
+        protected onInternalPreDrop(e: Event, de: DragManager.DropEvent) {
+            if (de.complete.docDragData) {
+                if (de.complete.docDragData.draggedDocuments.some(d => this.childDocs.includes(d))) {
+                    de.complete.docDragData.dropAction = "move";
+                }
+                e.stopPropagation();
+            }
+        }
+
         @undoBatch
         @action
         protected onInternalDrop(e: Event, de: DragManager.DropEvent): boolean {
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index d2afb4855..cb7d86e00 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -74,6 +74,7 @@ export enum CollectionViewType {
 export interface CollectionViewCustomProps {
     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)
     childLayoutTemplate?: () => Opt<Doc>;  // specify a layout Doc template to use for children of the collection
+    childLayoutString?: string;  // specify a layout string to use for children of the collection
 }
 
 export interface CollectionRenderProps {
@@ -478,6 +479,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
             </div>;
     }
     childLayoutTemplate = () => this.props.childLayoutTemplate?.() || Cast(this.props.Document.childLayoutTemplate, Doc, null);
+    childLayoutString = this.props.childLayoutString || StrCast(this.props.Document.childLayoutString);
 
     render() {
         TraceMobx();
@@ -489,7 +491,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
             whenActiveChanged: this.whenActiveChanged,
             PanelWidth: this.bodyPanelWidth,
             ChildLayoutTemplate: this.childLayoutTemplate,
-            ChildLayoutString: StrCast(this.props.Document.childLayoutString),
+            ChildLayoutString: this.childLayoutString,
         };
         return (<div className={"collectionView"}
             style={{
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 707e103fb..411fbd9e2 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -878,7 +878,6 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
             onDoubleClick: this.onChildDoubleClickHandler,
             ScreenToLocalTransform: childLayout.z ? this.getTransformOverlay : this.getTransform,
             renderDepth: this.props.renderDepth + 1,
-            RenderData: this.props.RenderData,
             PanelWidth: childLayout[WidthSym],
             PanelHeight: childLayout[HeightSym],
             ContentScaling: returnOne,
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 7c7c03db2..7a16e8836 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -78,7 +78,6 @@ export interface DocumentViewProps {
     setupDragLines?: () => void;
     renderDepth: number;
     ContentScaling: () => number;
-    RenderData?: () => Doc;
     PanelWidth: () => number;
     PanelHeight: () => number;
     pointerEvents?: boolean;
@@ -996,7 +995,6 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 LayoutTemplate={this.props.LayoutTemplate}
                 makeLink={this.makeLink}
                 rootSelected={this.rootSelected}
-                RenderData={this.props.RenderData}
                 dontRegisterView={this.props.dontRegisterView}
                 fitToBox={this.props.fitToBox}
                 LibraryPath={this.props.LibraryPath}
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 40d55ce38..016d2a1ae 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -50,12 +50,13 @@ export interface FieldViewProps {
     setVideoBox?: (player: VideoBox) => void;
     ContentScaling: () => number;
     ChromeHeight?: () => number;
-    RenderData?: () => Doc;
     // properties intended to be used from within layout strings (otherwise use the function equivalents that work more efficiently with React)
     height?: number;
     width?: number;
     background?: string;
     color?: string;
+    xMargin?: number;
+    yMargin?: number;
 }
 
 @observer
diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx
index 6e3420f22..53b6aa408 100644
--- a/src/client/views/nodes/PresBox.tsx
+++ b/src/client/views/nodes/PresBox.tsx
@@ -17,6 +17,8 @@ import "./PresBox.scss";
 import { ViewBoxBaseComponent } from "../DocComponent";
 import { makeInterface } from "../../../new_fields/Schema";
 import { List } from "../../../new_fields/List";
+import { Docs } from "../../documents/Documents";
+import { PrefetchProxy } from "../../../new_fields/Proxy";
 
 type PresBoxSchema = makeInterface<[typeof documentSchema]>;
 const PresBoxDocument = makeInterface(documentSchema);
@@ -24,14 +26,32 @@ const PresBoxDocument = makeInterface(documentSchema);
 @observer
 export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>(PresBoxDocument) {
     public static LayoutString(fieldKey: string) { return FieldView.LayoutString(PresBox, fieldKey); }
+    _docsDisposer: IReactionDisposer | undefined;
+    _viewDisposer: IReactionDisposer | undefined;
     @observable _isChildActive = false;
     @computed get childDocs() { return DocListCast(this.dataDoc[this.fieldKey]); }
-    @computed get currentIndex() { return NumCast(this.rootDoc._itemIndex); }
+    @computed get currentIndex() { return NumCast(this.presElement?.currentIndex); }
+    @computed get presElement() { return Cast(this.rootDoc.presElement, Doc, null); }
+    constructor(props: any) {
+        super(props);
+        if (!this.presElement) {
+            this.rootDoc.presElement = new PrefetchProxy(Docs.Create.PresElementBoxDocument({
+                title: "pres element template", backgroundColor: "transparent", _xMargin: 5, _height: 46, isTemplateDoc: true, isTemplateForField: "data"
+            }));
+        }
+    }
+
+    componentWillUnmount() {
+        this._docsDisposer?.();
+        this._viewDisposer?.();
+    }
 
     componentDidMount() {
         this.rootDoc.presBox = this.rootDoc;
         this.rootDoc._forceRenderEngine = "timeline";
         this.rootDoc._replacedChrome = "replaced";
+        this._docsDisposer = reaction(() => this.childDocs, docs => this.presElement.presOrderedDocs = new List<Doc>(docs), { fireImmediately: true });
+        this._viewDisposer = reaction(() => this.rootDoc._viewType, viewType => this.presElement.presCollapsedHeight = viewType === CollectionViewType.Tree ? 50 : 46, { fireImmediately: true });
     }
     updateCurrentPresentation = () => Doc.UserDoc().activePresentation = this.rootDoc;
 
@@ -166,17 +186,16 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
         }
     }
 
-
     //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 = (index: number, fromDoc: number) => {
         this.updateCurrentPresentation();
         Doc.UnBrushAllDocs();
         if (index >= 0 && index < this.childDocs.length) {
-            this.rootDoc._itemIndex = index;
+            this.presElement.currentIndex = index;
 
-            if (!this.layoutDoc.presStatus) {
-                this.layoutDoc.presStatus = true;
+            if (!this.presElement.presStatus) {
+                this.presElement.presStatus = true;
                 this.startPresentation(index);
             }
 
@@ -189,10 +208,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
     //The function that starts or resets presentaton functionally, depending on status flag.
     startOrResetPres = () => {
         this.updateCurrentPresentation();
-        if (this.layoutDoc.presStatus) {
+        if (this.presElement.presStatus) {
             this.resetPresentation();
         } else {
-            this.layoutDoc.presStatus = true;
+            this.presElement.presStatus = true;
             this.startPresentation(0);
             this.gotoDocument(0, this.currentIndex);
         }
@@ -204,7 +223,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
         this.updateCurrentPresentation();
         this.childDocs.forEach(doc => (doc.presentationTargetDoc as Doc).opacity = 1);
         this.rootDoc._itemIndex = 0;
-        this.layoutDoc.presStatus = false;
+        this.presElement.presStatus = false;
     }
 
     //The function that starts the presentation, also checking if actions should be applied
@@ -241,43 +260,31 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
         }
     });
 
-    initializeViewAliases = (docList: Doc[], viewtype: CollectionViewType) => {
-        const hgt = (viewtype === CollectionViewType.Tree) ? 50 : 46;
-        this.rootDoc.presCollapsedHeight = hgt;
-    }
+    @undoBatch
+    viewChanged = action((e: React.ChangeEvent) => {
+        //@ts-ignore
+        const viewType = e.target.selectedOptions[0].value as CollectionViewType;
+        viewType === CollectionViewType.Stacking && (this.rootDoc._pivotField = undefined); // pivot field may be set by the user in timeline view (or some other way) -- need to reset it here
+        this.updateMinimize(e, this.rootDoc._viewType = viewType);
+    });
 
+    whenActiveChanged = action((isActive: boolean) => this.props.whenActiveChanged(this._isChildActive = isActive));
     addDocumentFilter = (doc: Doc) => {
-        doc.presentationTargetDoc = doc.aliasOf;
+        doc.aliasOf instanceof Doc && (doc.presentationTargetDoc = doc.aliasOf);
+        !this.childDocs.includes(doc) && (doc.presZoomButton = true);
         return true;
     }
-
+    childLayoutTemplate = () => this.rootDoc._viewType !== CollectionViewType.Stacking ? undefined : this.presElement;
     removeDocument = (doc: Doc) => Doc.RemoveDocFromList(this.dataDoc, this.fieldKey, doc);
-
     selectElement = (doc: Doc) => this.gotoDocument(this.childDocs.indexOf(doc), NumCast(this.rootDoc._itemIndex));
-
     getTransform = () => this.props.ScreenToLocalTransform().translate(-5, -65);// listBox padding-left and pres-box-cont minHeight
-
     panelHeight = () => this.props.PanelHeight() - 20;
-
     active = (outsideReaction?: boolean) => ((InkingControl.Instance.selectedTool === InkTool.None && !this.layoutDoc.isBackground) &&
         (this.layoutDoc.forceActive || this.props.isSelected(outsideReaction) || this._isChildActive || this.props.renderDepth === 0) ? true : false)
 
-    whenActiveChanged = action((isActive: boolean) => this.props.whenActiveChanged(this._isChildActive = isActive));
-
-    @undoBatch
-    viewChanged = action((e: React.ChangeEvent) => {
-        //@ts-ignore
-        const viewType = e.target.selectedOptions[0].value as CollectionViewType;
-        viewType === CollectionViewType.Stacking && (this.rootDoc._pivotField = undefined); // pivot field may be set by the user in timeline view (or some other way) -- need to reset it here
-        this.updateMinimize(e, this.rootDoc._viewType = viewType);
-    });
-
-    returnSelf = () => this.rootDoc;
-    childLayoutTemplate = () => this.rootDoc._viewType === CollectionViewType.Stacking ? Cast(Doc.UserDoc()["template-presentation"], Doc, null) : undefined;
     render() {
         this.rootDoc.presOrderedDocs = new List<Doc>(this.childDocs.map((child, i) => child));
         const mode = StrCast(this.rootDoc._viewType) as CollectionViewType;
-        this.initializeViewAliases(this.childDocs, mode);
         return <div className="presBox-cont" style={{ minWidth: this.layoutDoc.inOverlay ? 240 : undefined }} >
             <div className="presBox-buttons" style={{ display: this.rootDoc._chromeStatus === "disabled" ? "none" : undefined }}>
                 <select className="presBox-viewPicker"
@@ -308,7 +315,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
                         childLayoutTemplate={this.childLayoutTemplate}
                         filterAddDocument={this.addDocumentFilter}
                         removeDocument={returnFalse}
-                        RenderData={this.returnSelf}
                         focus={this.selectElement}
                         ScreenToLocalTransform={this.getTransform} />
                     : (null)
@@ -316,4 +322,4 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
             </div>
         </div>;
     }
-}
\ 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 3fb3f5644..4a75816a8 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -63,9 +63,9 @@ library.add(faEdit);
 library.add(faSmile, faTextHeight, faUpload);
 
 export interface FormattedTextBoxProps {
-    hideOnLeave?: boolean;
-    makeLink?: () => Opt<Doc>;
-    xMargin?: number;
+    makeLink?: () => Opt<Doc>;  // bcz: hack: notifies the text document when the container has made a link.  allows the text doc to react and setup a hyeprlink for any selected text
+    hideOnLeave?: boolean;  // used by DocumentView for setting caption's hide on leave (bcz: would prefer to have caption-hideOnLeave field set or something similar)
+    xMargin?: number;   // used to override document's settings for xMargin --- see CollectionCarouselView
     yMargin?: number;
 }
 
diff --git a/src/client/views/presentationview/PresElementBox.tsx b/src/client/views/presentationview/PresElementBox.tsx
index 1887c8d45..fd202cdc1 100644
--- a/src/client/views/presentationview/PresElementBox.tsx
+++ b/src/client/views/presentationview/PresElementBox.tsx
@@ -5,7 +5,7 @@ import { Doc, DataSym, DocListCast } from "../../../new_fields/Doc";
 import { documentSchema } from '../../../new_fields/documentSchemas';
 import { Id } from "../../../new_fields/FieldSymbols";
 import { createSchema, makeInterface } from '../../../new_fields/Schema';
-import { Cast, NumCast } from "../../../new_fields/Types";
+import { Cast, NumCast, BoolCast } from "../../../new_fields/Types";
 import { emptyFunction, emptyPath, returnFalse, returnTrue, returnOne, returnZero } from "../../../Utils";
 import { Transform } from "../../util/Transform";
 import { CollectionViewType } from '../collections/CollectionView';
@@ -38,11 +38,11 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps, PresDoc
     public static LayoutString(fieldKey: string) { return FieldView.LayoutString(PresElementBox, fieldKey); }
 
     _heightDisposer: IReactionDisposer | undefined;
-    @computed get indexInPres() { return DocListCast(this.presBoxDoc.presOrderedDocs).findIndex(d => d === this.rootDoc); }
-    @computed get presBoxDoc() { return Cast(this.props.RenderData?.().presBox, Doc) as Doc; }
+    @computed get indexInPres() { return DocListCast(this.layoutDoc.presOrderedDocs).findIndex(d => d === this.rootDoc); }
+    @computed get collapsedHeight() { return NumCast(this.layoutDoc.presCollapsedHeight); }
+    @computed get presStatus() { return BoolCast(this.layoutDoc.presStatus); }
+    @computed get currentIndex() { return NumCast(this.layoutDoc.currentIndex); }
     @computed get targetDoc() { return this.rootDoc.presentationTargetDoc as Doc; }
-    @computed get currentIndex() { return NumCast(this.presBoxDoc?._itemIndex); }
-    @computed get collapsedHeight() { return NumCast(this.presBoxDoc?.presCollapsedHeight); }
 
     componentDidMount() {
         this._heightDisposer = reaction(() => [this.rootDoc.presExpandInlineButton, this.collapsedHeight],
@@ -65,7 +65,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps, PresDoc
                 this.targetDoc.opacity = 1;
             }
         } else {
-            if (this.presBoxDoc.presStatus && this.indexInPres > this.currentIndex && this.targetDoc) {
+            if (this.presStatus && this.indexInPres > this.currentIndex && this.targetDoc) {
                 this.targetDoc.opacity = 0;
             }
         }
@@ -86,7 +86,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps, PresDoc
             }
         } else {
             if (this.rootDoc.presFadeButton) this.rootDoc.presFadeButton = false;
-            if (this.presBoxDoc.presStatus && this.indexInPres < this.currentIndex && this.targetDoc) {
+            if (this.presStatus && this.indexInPres < this.currentIndex && this.targetDoc) {
                 this.targetDoc.opacity = 0;
             }
         }
@@ -107,7 +107,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps, PresDoc
             }
         } else {
             this.rootDoc.presHideAfterButton = false;
-            if (this.presBoxDoc.presStatus && (this.indexInPres < this.currentIndex) && this.targetDoc) {
+            if (this.presStatus && (this.indexInPres < this.currentIndex) && this.targetDoc) {
                 this.targetDoc.opacity = 0.5;
             }
         }
diff --git a/src/new_fields/documentSchemas.ts b/src/new_fields/documentSchemas.ts
index e71fc27f3..e7d27d81e 100644
--- a/src/new_fields/documentSchemas.ts
+++ b/src/new_fields/documentSchemas.ts
@@ -78,7 +78,7 @@ export const documentSchema = createSchema({
     // drag drop properties
     dragFactory: Doc,           // the document that serves as the "template" for the onDragStart script.  ie, to drag out copies of the dragFactory document.
     dropAction: "string",       // override specifying what should happen when this document is dropped (can be "alias", "copy", "move")
-    targetDropAction: "string", // allows the target of a drop event to specify the dropAction ("alias", "copy", "move")
+    targetDropAction: "string", // allows the target of a drop event to specify the dropAction ("alias", "copy", "move") NOTE: if the document is dropped within the same collection, the dropAction is coerced to 'move'
     childDropAction: "string",  // specify the override for what should happen when the child of a collection is dragged from it and dropped (can be "alias" or "copy")
     removeDropProperties: listSpec("string"), // properties that should be removed from the alias/copy/etc of this document when it is dropped
 });
diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts
index b9de93559..7f0fd5a8d 100644
--- a/src/server/authentication/models/current_user_utils.ts
+++ b/src/server/authentication/models/current_user_utils.ts
@@ -257,7 +257,7 @@ export class CurrentUserUtils {
     }[] {
         if (doc.emptyPresentation === undefined) {
             doc.emptyPresentation = Docs.Create.PresDocument(new List<Doc>(),
-                { title: "Presentation", _viewType: CollectionViewType.Stacking, _LODdisable: true, _chromeStatus: "replaced", _showTitle: "title", boxShadow: "0 0" });
+                { title: "Presentation", _viewType: CollectionViewType.Stacking, targetDropAction: "alias", _LODdisable: true, _chromeStatus: "replaced", _showTitle: "title", boxShadow: "0 0" });
         }
         if (doc.emptyCollection === undefined) {
             doc.emptyCollection = Docs.Create.FreeformDocument([],
@@ -576,11 +576,6 @@ export class CurrentUserUtils {
 
     // 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, _height: 46, isTemplateDoc: true, isTemplateForField: "data"
-            }));
-        }
         if (doc.activePresentation === undefined) {
             doc.activePresentation = Docs.Create.PresDocument(new List<Doc>(), {
                 title: "Presentation", _viewType: CollectionViewType.Stacking, targetDropAction: "alias",
-- 
cgit v1.2.3-70-g09d2


From 3d5bc514936ba804eaa24e9ca1f4619bcaf81eb5 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Sun, 3 May 2020 23:59:35 -0400
Subject: extended documentBox's to support drag and drop of style. 
 reorganized imports to avoid some cycles

---
 src/client/documents/Documents.ts                  |  10 +-
 src/client/util/DragManager.ts                     | 112 +++++++--------------
 src/client/util/DropConverter.ts                   |   2 +
 src/client/util/SelectionManager.ts                |   5 -
 src/client/views/DocumentDecorations.tsx           |   5 +-
 src/client/views/MainView.tsx                      |   8 +-
 .../views/collections/CollectionDockingView.tsx    |   5 +-
 .../collections/CollectionMasonryViewFieldRow.tsx  |   2 +-
 .../views/collections/CollectionPileView.tsx       |   5 +-
 .../views/collections/CollectionSchemaCells.tsx    |   2 +-
 .../CollectionSchemaMovableTableHOC.tsx            |   4 +-
 .../views/collections/CollectionStackingView.tsx   |   4 +-
 .../CollectionStackingViewFieldColumn.tsx          |   4 +-
 .../views/collections/CollectionTreeView.tsx       |   4 +-
 .../CollectionFreeFormLinksView.tsx                |   3 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |   6 +-
 src/client/views/linking/LinkMenuGroup.tsx         |   4 +-
 src/client/views/linking/LinkMenuItem.tsx          |  39 ++++++-
 src/client/views/nodes/AudioBox.tsx                |  10 +-
 .../views/nodes/ContentFittingDocumentView.tsx     |   8 +-
 src/client/views/nodes/DocumentBox.scss            |   3 +-
 src/client/views/nodes/DocumentBox.tsx             |  57 +++++++----
 src/client/views/nodes/DocumentView.tsx            |   2 +-
 src/client/views/nodes/QueryBox.tsx                |   3 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx |  13 ++-
 .../authentication/models/current_user_utils.ts    |  27 +++--
 26 files changed, 189 insertions(+), 158 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 8ea8ded0f..45687f597 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -139,6 +139,7 @@ export interface DocumentOptions {
     ischecked?: ScriptField; // returns whether a font icon box is checked
     activePen?: Doc; // which pen document is currently active (used as the radio button state for the 'unhecked' pen tool scripts)
     onClick?: ScriptField;
+    onDoubleClick?: ScriptField;
     onChildClick?: ScriptField; // script given to children of a collection to execute when they are clicked
     onChildDoubleClick?: ScriptField; // script given to children of a collection to execute when they are double clicked
     onPointerDown?: ScriptField;
@@ -493,7 +494,7 @@ export namespace Docs {
             const dataDoc = MakeDataDelegate(proto, protoProps, data, fieldKey);
             const viewDoc = Doc.MakeDelegate(dataDoc, delegId);
 
-            viewDoc.type !== DocumentType.LINK && AudioBox.ActiveRecordings.map(d => DocUtils.MakeLink({ doc: viewDoc }, { doc: d }, "audio link", "audio timeline"));
+            viewDoc.type !== DocumentType.LINK && DocUtils.MakeLinkToActiveAudio(viewDoc);
 
             return Doc.assign(viewDoc, delegateProps, true);
         }
@@ -645,7 +646,7 @@ export namespace Docs {
         }
 
         export function DocumentDocument(document?: Doc, options: DocumentOptions = {}) {
-            return InstanceFromProto(Prototypes.get(DocumentType.DOCHOLDER), document, { title: document ? document.title + "" : "container", ...options });
+            return InstanceFromProto(Prototypes.get(DocumentType.DOCHOLDER), document, { title: document ? document.title + "" : "container", targetDropAction: "move", ...options });
         }
 
         export function FreeformDocument(documents: Array<Doc>, options: DocumentOptions, id?: string) {
@@ -1005,6 +1006,11 @@ export namespace DocUtils {
         });
     }
 
+    export let ActiveRecordings: Doc[] = [];
+
+    export function MakeLinkToActiveAudio(doc: Doc) {
+        DocUtils.ActiveRecordings.map(d => DocUtils.MakeLink({ doc: doc }, { doc: d }, "audio link", "audio timeline"));
+    }
     export function MakeLink(source: { doc: Doc }, target: { doc: Doc }, linkRelationship: string = "", id?: string) {
         const sv = DocumentManager.Instance.getDocumentView(source.doc);
         if (sv && sv.props.ContainingCollectionDoc === target.doc) return;
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 041f2fc2c..d91eb60ef 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -1,26 +1,16 @@
-import { Doc, Field, DocListCast } from "../../new_fields/Doc";
-import { Cast, ScriptCast, StrCast, NumCast } from "../../new_fields/Types";
-import { emptyFunction } from "../../Utils";
-import { CollectionDockingView } from "../views/collections/CollectionDockingView";
-import * as globalCssVariables from "../views/globalCssVariables.scss";
-import { DocumentManager } from "./DocumentManager";
-import { LinkManager } from "./LinkManager";
-import { SelectionManager } from "./SelectionManager";
-import { SchemaHeaderField } from "../../new_fields/SchemaHeaderField";
-import { Docs, DocUtils } from "../documents/Documents";
-import { ScriptField } from "../../new_fields/ScriptField";
+import { action, observable, runInAction } from "mobx";
+import { DateField } from "../../new_fields/DateField";
+import { Doc, Field, Opt } from "../../new_fields/Doc";
 import { List } from "../../new_fields/List";
 import { PrefetchProxy } from "../../new_fields/Proxy";
 import { listSpec } from "../../new_fields/Schema";
-import { Scripting } from "./Scripting";
-import { convertDropDataToButtons } from "./DropConverter";
-import { AudioBox } from "../views/nodes/AudioBox";
-import { DateField } from "../../new_fields/DateField";
-import { DocumentView } from "../views/nodes/DocumentView";
+import { SchemaHeaderField } from "../../new_fields/SchemaHeaderField";
+import { ScriptField } from "../../new_fields/ScriptField";
+import { Cast, NumCast, ScriptCast, StrCast } from "../../new_fields/Types";
+import { emptyFunction } from "../../Utils";
+import { Docs, DocUtils } from "../documents/Documents";
+import * as globalCssVariables from "../views/globalCssVariables.scss";
 import { UndoManager } from "./UndoManager";
-import { PointData } from "../../new_fields/InkField";
-import { MainView } from "../views/MainView";
-import { action } from "mobx";
 
 export type dropActionType = "alias" | "copy" | "move" | undefined; // undefined = move
 export function SetupDrag(
@@ -56,10 +46,10 @@ export function SetupDrag(
     const onItemDown = async (e: React.PointerEvent) => {
         if (e.button === 0) {
             e.stopPropagation();
-            if (e.shiftKey && CollectionDockingView.Instance) {
+            if (e.shiftKey) {
                 e.persist();
                 const dragDoc = await docFunc();
-                dragDoc && CollectionDockingView.Instance.StartOtherDrag({
+                dragDoc && DragManager.Vals.Instance.StartWindowDrag?.({
                     pageX: e.pageX,
                     pageY: e.pageY,
                     preventDefault: emptyFunction,
@@ -76,8 +66,19 @@ export function SetupDrag(
 
 export namespace DragManager {
     let dragDiv: HTMLDivElement;
-    export let horizSnapLines: number[] = [];
-    export let vertSnapLines: number[] = [];
+    export class Vals {
+        static Instance: Vals = new Vals();
+        @observable public IsDragging: boolean = false;
+        @observable public horizSnapLines: number[] = [];
+        @observable public vertSnapLines: number[] = [];
+        public StartWindowDrag: Opt<((e: any, dragDocs: Doc[]) => void)> = undefined;
+        public SetIsDragging(dragging: boolean) { runInAction(() => this.IsDragging = dragging); }
+        public GetIsDragging() { return this.IsDragging; }
+        public clearSnapLines() {
+            this.vertSnapLines.length = 0;
+            this.horizSnapLines.length = 0;
+        }
+    }
 
     export function Root() {
         const root = document.getElementById("root");
@@ -215,7 +216,7 @@ export namespace DragManager {
     export function StartDocumentDrag(eles: HTMLElement[], dragData: DocumentDragData, downX: number, downY: number, options?: DragOptions) {
         const addAudioTag = (dropDoc: any) => {
             dropDoc && !dropDoc.creationDate && (dropDoc.creationDate = new DateField);
-            dropDoc instanceof Doc && AudioBox.ActiveRecordings.map(d => DocUtils.MakeLink({ doc: dropDoc }, { doc: d }, "audio link", "audio timeline"));
+            dropDoc instanceof Doc && DocUtils.MakeLinkToActiveAudio(dropDoc);
             return dropDoc;
         };
         const batch = UndoManager.StartBatch("dragging");
@@ -246,40 +247,6 @@ export namespace DragManager {
         StartDrag(eles, new DragManager.DocumentDragData([]), downX, downY, options, finishDrag);
     }
 
-    // drag links and drop link targets (aliasing them if needed)
-    export async function StartLinkTargetsDrag(dragEle: HTMLElement, docView: DocumentView, downX: number, downY: number, sourceDoc: Doc, specificLinks?: Doc[]) {
-        const draggedDocs = (specificLinks ? specificLinks : DocListCast(sourceDoc.links)).map(link => LinkManager.Instance.getOppositeAnchor(link, sourceDoc)).filter(l => l) as Doc[];
-
-        if (draggedDocs.length) {
-            const moddrag: Doc[] = [];
-            for (const draggedDoc of draggedDocs) {
-                const doc = await Cast(draggedDoc.annotationOn, Doc);
-                if (doc) moddrag.push(doc);
-            }
-
-            const dragData = new DragManager.DocumentDragData(moddrag.length ? moddrag : draggedDocs);
-            dragData.moveDocument = (doc: Doc, targetCollection: Doc | undefined, addDocument: (doc: Doc) => boolean): boolean => {
-                docView.props.removeDocument?.(doc);
-                addDocument(doc);
-                return true;
-            };
-            const containingView = docView.props.ContainingCollectionView;
-            const finishDrag = (e: DragCompleteEvent) =>
-                e.docDragData && (e.docDragData.droppedDocuments =
-                    dragData.draggedDocuments.reduce((droppedDocs, d) => {
-                        const dvs = DocumentManager.Instance.getDocumentViews(d).filter(dv => dv.props.ContainingCollectionView === containingView);
-                        if (dvs.length) {
-                            dvs.forEach(dv => droppedDocs.push(dv.props.Document));
-                        } else {
-                            droppedDocs.push(Doc.MakeAlias(d));
-                        }
-                        return droppedDocs;
-                    }, [] as Doc[]));
-
-            StartDrag([dragEle], dragData, downX, downY, undefined, finishDrag);
-        }
-    }
-
     // drag&drop the pdf annotation anchor which will create a text note  on drop via a dropCompleted() DragOption 
     export function StartPdfAnnoDrag(eles: HTMLElement[], dragData: PdfAnnoDragData, downX: number, downY: number, options?: DragOptions) {
         StartDrag(eles, dragData, downX, downY, options);
@@ -300,10 +267,8 @@ export namespace DragManager {
     }
 
     export function SetSnapLines(horizLines: number[], vertLines: number[]) {
-        horizSnapLines = horizLines;
-        vertSnapLines = vertLines;
-        MainView.Instance._hLines = horizLines;
-        MainView.Instance._vLines = vertLines;
+        DragManager.Vals.Instance.horizSnapLines.push(...horizLines);
+        DragManager.Vals.Instance.vertSnapLines.push(...vertLines);
     }
 
     export function snapDrag(e: PointerEvent, xFromLeft: number, yFromTop: number, xFromRight: number, yFromBottom: number) {
@@ -320,10 +285,13 @@ export namespace DragManager {
             return drag;
         };
 
-        return { thisX: snapVal([xFromLeft, xFromRight], e.pageX, vertSnapLines), thisY: snapVal([yFromTop, yFromBottom], e.pageY, horizSnapLines) };
+        return {
+            thisX: snapVal([xFromLeft, xFromRight], e.pageX, DragManager.Vals.Instance.vertSnapLines),
+            thisY: snapVal([yFromTop, yFromBottom], e.pageY, DragManager.Vals.Instance.horizSnapLines)
+        };
     }
     export let docsBeingDragged: Doc[] = [];
-    function StartDrag(eles: HTMLElement[], dragData: { [id: string]: any }, downX: number, downY: number, options?: DragOptions, finishDrag?: (dropData: DragCompleteEvent) => void) {
+    export function StartDrag(eles: HTMLElement[], dragData: { [id: string]: any }, downX: number, downY: number, options?: DragOptions, finishDrag?: (dropData: DragCompleteEvent) => void) {
         eles = eles.filter(e => e);
         if (!dragDiv) {
             dragDiv = document.createElement("div");
@@ -331,7 +299,7 @@ export namespace DragManager {
             dragDiv.style.pointerEvents = "none";
             DragManager.Root().appendChild(dragDiv);
         }
-        SelectionManager.SetIsDragging(true);
+        DragManager.Vals.Instance.SetIsDragging(true);
         const scaleXs: number[] = [];
         const scaleYs: number[] = [];
         const xs: number[] = [];
@@ -413,14 +381,14 @@ export namespace DragManager {
             if (dragData instanceof DocumentDragData) {
                 dragData.userDropAction = e.ctrlKey && e.altKey ? "copy" : e.ctrlKey ? "alias" : undefined;
             }
-            if (e.shiftKey && CollectionDockingView.Instance && dragData.droppedDocuments.length === 1) {
+            if (e.shiftKey && dragData.droppedDocuments.length === 1) {
                 !dragData.dropAction && (dragData.dropAction = alias);
                 if (dragData.dropAction === "move") {
                     dragData.removeDocument?.(dragData.draggedDocuments[0]);
                 }
                 AbortDrag();
                 finishDrag?.(new DragCompleteEvent(true, dragData));
-                CollectionDockingView.Instance.StartOtherDrag({
+                DragManager.Vals.Instance.StartWindowDrag?.({
                     pageX: e.pageX,
                     pageY: e.pageY,
                     preventDefault: emptyFunction,
@@ -447,22 +415,19 @@ export namespace DragManager {
         const endDrag = action(() => {
             document.removeEventListener("pointermove", moveHandler, true);
             document.removeEventListener("pointerup", upHandler);
-            MainView.Instance._hLines = [];
-            MainView.Instance._vLines = [];
-            vertSnapLines.length = 0;
-            horizSnapLines.length = 0;
+            DragManager.Vals.Instance.clearSnapLines();
         });
 
         AbortDrag = () => {
             hideDragShowOriginalElements();
-            SelectionManager.SetIsDragging(false);
+            DragManager.Vals.Instance.SetIsDragging(false);
             options?.dragComplete?.(new DragCompleteEvent(true, dragData));
             endDrag();
         };
         const upHandler = (e: PointerEvent) => {
             hideDragShowOriginalElements();
             dispatchDrag(eles, e, dragData, xFromLeft, yFromTop, xFromRight, yFromBottom, options, finishDrag);
-            SelectionManager.SetIsDragging(false);
+            DragManager.Vals.Instance.SetIsDragging(false);
             endDrag();
             options?.dragComplete?.(new DragCompleteEvent(false, dragData));
         };
@@ -520,4 +485,3 @@ export namespace DragManager {
         }
     }
 }
-Scripting.addGlobal(function convertToButtons(dragData: any) { convertDropDataToButtons(dragData as DragManager.DocumentDragData); });
diff --git a/src/client/util/DropConverter.ts b/src/client/util/DropConverter.ts
index b1993d401..d6db882b8 100644
--- a/src/client/util/DropConverter.ts
+++ b/src/client/util/DropConverter.ts
@@ -7,6 +7,7 @@ import { Docs } from "../documents/Documents";
 import { ScriptField, ComputedField } from "../../new_fields/ScriptField";
 import { RichTextField } from "../../new_fields/RichTextField";
 import { ImageField } from "../../new_fields/URLField";
+import { Scripting } from "./Scripting";
 
 // 
 // converts 'doc' into a template that can be used to render other documents.
@@ -75,3 +76,4 @@ export function convertDropDataToButtons(data: DragManager.DocumentDragData) {
         data.droppedDocuments[i] = dbox;
     });
 }
+Scripting.addGlobal(function convertToButtons(dragData: any) { convertDropDataToButtons(dragData as DragManager.DocumentDragData); });
\ No newline at end of file
diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index a49977c42..d509168b6 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -8,10 +8,8 @@ export namespace SelectionManager {
 
     class Manager {
 
-        @observable IsDragging: boolean = false;
         SelectedDocuments: ObservableMap<DocumentView, boolean> = new ObservableMap();
 
-
         @action
         SelectDoc(docView: DocumentView, ctrlPressed: boolean): void {
             // if doc is not in SelectedDocuments, add it
@@ -78,9 +76,6 @@ export namespace SelectionManager {
         if (found) manager.SelectDoc(found, false);
     }
 
-    export function SetIsDragging(dragging: boolean) { runInAction(() => manager.IsDragging = dragging); }
-    export function GetIsDragging() { return manager.IsDragging; }
-
     export function SelectedDocuments(): Array<DocumentView> {
         return Array.from(manager.SelectedDocuments.keys());
     }
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 396fe12b5..f8e77d90a 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -364,8 +364,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
         this.Interacting = false;
         (e.button === 0) && this._resizeUndo?.end();
         this._resizeUndo = undefined;
-        MainView.Instance._hLines = [];
-        MainView.Instance._vLines = [];
+        DragManager.Vals.Instance.clearSnapLines();
     }
 
     @computed
@@ -404,7 +403,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
         const darkScheme = Cast(Doc.UserDoc().activeWorkspace, Doc, null)?.darkScheme ? "dimgray" : undefined;
         const bounds = this.Bounds;
         const seldoc = SelectionManager.SelectedDocuments().length ? SelectionManager.SelectedDocuments()[0] : undefined;
-        if (SelectionManager.GetIsDragging() || bounds.r - bounds.x < 2 || bounds.x === Number.MAX_VALUE || !seldoc || this._hidden || isNaN(bounds.r) || isNaN(bounds.b) || isNaN(bounds.x) || isNaN(bounds.y)) {
+        if (DragManager.Vals.Instance.GetIsDragging() || bounds.r - bounds.x < 2 || bounds.x === Number.MAX_VALUE || !seldoc || this._hidden || isNaN(bounds.r) || isNaN(bounds.b) || isNaN(bounds.x) || isNaN(bounds.y)) {
             return (null);
         }
         const minimal = bounds.r - bounds.x < 100 ? true : false;
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index a29a6baac..71c2bf245 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -43,6 +43,7 @@ import PDFMenu from './pdf/PDFMenu';
 import { PreviewCursor } from './PreviewCursor';
 import { ScriptField } from '../../new_fields/ScriptField';
 import { TimelineMenu } from './animationtimeline/TimelineMenu';
+import { DragManager } from '../util/DragManager';
 
 @observer
 export class MainView extends React.Component {
@@ -574,9 +575,6 @@ export class MainView extends React.Component {
         return this._mainViewRef;
     }
 
-    @observable public _hLines: any;
-    @observable public _vLines: any;
-
     render() {
         return (<div className={"mainView-container" + (this.darkScheme ? "-dark" : "")} ref={this._mainViewRef}>
             <DictationOverlay />
@@ -597,8 +595,8 @@ export class MainView extends React.Component {
             {// TO VIEW SNAP LINES
                 <div className="snapLines" style={{ position: "absolute", top: 0, left: 0, width: "100%", height: "100%", pointerEvents: "none" }}>
                     <svg style={{ width: "100%", height: "100%" }}>
-                        {this._hLines?.map((l: any) => <line x1="0" y1={l} x2="2000" y2={l} stroke="black" opacity={0.3} strokeWidth={0.5} strokeDasharray={"1 1"} />)}
-                        {this._vLines?.map((l: any) => <line y1="0" x1={l} y2="2000" x2={l} stroke="black" opacity={0.3} strokeWidth={0.5} strokeDasharray={"1 1"} />)}
+                        {DragManager.Vals.Instance.horizSnapLines.map((l: any) => <line x1="0" y1={l} x2="2000" y2={l} stroke="black" opacity={0.3} strokeWidth={0.5} strokeDasharray={"1 1"} />)}
+                        {DragManager.Vals.Instance.vertSnapLines.map((l: any) => <line y1="0" x1={l} y2="2000" x2={l} stroke="black" opacity={0.3} strokeWidth={0.5} strokeDasharray={"1 1"} />)}
                     </svg>
                 </div>}
             <TimelineMenu />
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 822f44f5a..33ece13cc 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -68,10 +68,11 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
         //Why is this here?
         (window as any).React = React;
         (window as any).ReactDOM = ReactDOM;
+        DragManager.Vals.Instance.StartWindowDrag = this.StartOtherDrag;
     }
     hack: boolean = false;
     undohack: any = null;
-    public StartOtherDrag(e: any, dragDocs: Doc[]) {
+    public StartOtherDrag = (e: any, dragDocs: Doc[]) => {
         let config: any;
         if (dragDocs.length === 1) {
             config = CollectionDockingView.makeDocumentConfig(dragDocs[0]);
@@ -499,7 +500,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
                 const stack = tab.contentItem.parent;
                 // shifts the focus to this tab when another tab is dragged over it
                 tab.element[0].onmouseenter = (e: any) => {
-                    if (!this._isPointerDown || !SelectionManager.GetIsDragging()) return;
+                    if (!this._isPointerDown || !DragManager.Vals.Instance.GetIsDragging()) return;
                     const activeContentItem = tab.header.parent.getActiveContentItem();
                     if (tab.contentItem !== activeContentItem) {
                         tab.header.parent.setActiveContentItem(tab.contentItem);
diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
index 7ad15ef41..c74cfbcf4 100644
--- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
+++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
@@ -132,7 +132,7 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
         this._color = color;
     }
 
-    pointerEnteredRow = action(() => SelectionManager.GetIsDragging() && (this._background = "#b4b4b4"));
+    pointerEnteredRow = action(() => DragManager.Vals.Instance.GetIsDragging() && (this._background = "#b4b4b4"));
 
     @action
     pointerLeaveRow = () => {
diff --git a/src/client/views/collections/CollectionPileView.tsx b/src/client/views/collections/CollectionPileView.tsx
index 8b8cbc6e8..0a10c24b3 100644
--- a/src/client/views/collections/CollectionPileView.tsx
+++ b/src/client/views/collections/CollectionPileView.tsx
@@ -12,6 +12,7 @@ import React = require("react");
 import { setupMoveUpEvents, emptyFunction, returnFalse } from "../../../Utils";
 import { SelectionManager } from "../../util/SelectionManager";
 import { UndoManager } from "../../util/UndoManager";
+import { DragManager } from "../../util/DragManager";
 
 @observer
 export class CollectionPileView extends CollectionSubView(doc => doc) {
@@ -78,7 +79,7 @@ export class CollectionPileView extends CollectionSubView(doc => doc) {
     _undoBatch: UndoManager.Batch | undefined;
     pointerDown = (e: React.PointerEvent) => {
         let dist = 0;
-        SelectionManager.SetIsDragging(true);
+        DragManager.Vals.Instance.SetIsDragging(true);
         // this._lastTap should be set to 0, and this._doubleTap should be set to false in the class header
         setupMoveUpEvents(this, e, (e: PointerEvent, down: number[], delta: number[]) => {
             if (this.layoutEngine() === "pass" && this.childDocs.length && this.props.isSelected(true)) {
@@ -98,7 +99,7 @@ export class CollectionPileView extends CollectionSubView(doc => doc) {
         }, () => {
             this._undoBatch?.end();
             this._undoBatch = undefined;
-            SelectionManager.SetIsDragging(false);
+            DragManager.Vals.Instance.SetIsDragging(false);
             if (!this.childDocs.length) {
                 this.props.ContainingCollectionView?.removeDocument(this.props.Document);
             }
diff --git a/src/client/views/collections/CollectionSchemaCells.tsx b/src/client/views/collections/CollectionSchemaCells.tsx
index 82204ca7b..0e6489947 100644
--- a/src/client/views/collections/CollectionSchemaCells.tsx
+++ b/src/client/views/collections/CollectionSchemaCells.tsx
@@ -189,7 +189,7 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
                 this._document[props.fieldKey] instanceof Doc ? "alias" : this.props.Document.schemaDoc ? "copy" : undefined)(e);
         };
         const onPointerEnter = (e: React.PointerEvent): void => {
-            if (e.buttons === 1 && SelectionManager.GetIsDragging() && (type === "document" || type === undefined)) {
+            if (e.buttons === 1 && DragManager.Vals.Instance.GetIsDragging() && (type === "document" || type === undefined)) {
                 dragRef.current!.className = "collectionSchemaView-cellContainer doc-drag-over";
             }
         };
diff --git a/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx b/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx
index 972714e34..f9cd9a628 100644
--- a/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx
+++ b/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx
@@ -32,7 +32,7 @@ export class MovableColumn extends React.Component<MovableColumnProps> {
     private _dragRef: React.RefObject<HTMLDivElement> = React.createRef();
 
     onPointerEnter = (e: React.PointerEvent): void => {
-        if (e.buttons === 1 && SelectionManager.GetIsDragging()) {
+        if (e.buttons === 1 && DragManager.Vals.Instance.GetIsDragging()) {
             this._header!.current!.className = "collectionSchema-col-wrapper";
             document.addEventListener("pointermove", this.onDragMove, true);
         }
@@ -143,7 +143,7 @@ export class MovableRow extends React.Component<MovableRowProps> {
     private _rowDropDisposer?: DragManager.DragDropDisposer;
 
     onPointerEnter = (e: React.PointerEvent): void => {
-        if (e.buttons === 1 && SelectionManager.GetIsDragging()) {
+        if (e.buttons === 1 && DragManager.Vals.Instance.GetIsDragging()) {
             this._header!.current!.className = "collectionSchema-row-wrapper";
             document.addEventListener("pointermove", this.onDragMove, true);
         }
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 1fd5c3f44..f6cdebc9b 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -312,7 +312,7 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument)
                     this.refList.push(ref);
                     const doc = this.props.DataDoc && this.props.DataDoc.layout === this.layoutDoc ? this.props.DataDoc : this.layoutDoc;
                     this.observer = new _global.ResizeObserver(action((entries: any) => {
-                        if (this.props.Document._autoHeight && ref && this.refList.length && !SelectionManager.GetIsDragging()) {
+                        if (this.props.Document._autoHeight && ref && this.refList.length && !DragManager.Vals.Instance.GetIsDragging()) {
                             Doc.Layout(doc)._height = Math.min(1200, Math.max(...this.refList.map(r => Number(getComputedStyle(r).height.replace("px", "")))));
                         }
                     }));
@@ -359,7 +359,7 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument)
                     this.refList.push(ref);
                     const doc = this.props.DataDoc && this.props.DataDoc.layout === this.layoutDoc ? this.props.DataDoc : this.layoutDoc;
                     this.observer = new _global.ResizeObserver(action((entries: any) => {
-                        if (this.props.Document._autoHeight && ref && this.refList.length && !SelectionManager.GetIsDragging()) {
+                        if (this.props.Document._autoHeight && ref && this.refList.length && !DragManager.Vals.Instance.GetIsDragging()) {
                             Doc.Layout(doc)._height = this.refList.reduce((p, r) => p + Number(getComputedStyle(r).height.replace("px", "")), 0);
                         }
                     }));
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index 8a9539eb0..1d16a5478 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -120,7 +120,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
 
     @action
     pointerEntered = () => {
-        if (SelectionManager.GetIsDragging()) {
+        if (DragManager.Vals.Instance.GetIsDragging()) {
             this._background = "#b4b4b4";
         }
     }
@@ -355,7 +355,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
             <div className="collectionStackingViewFieldColumn" key={heading}
                 style={{
                     width: `${100 / ((uniqueHeadings.length + ((chromeStatus !== 'view-mode' && chromeStatus !== 'disabled') ? 1 : 0)) || 1)}%`,
-                    height: undefined, // SelectionManager.GetIsDragging() ? "100%" : undefined,
+                    height: undefined, // DragManager.Vals.Instance.GetIsDragging() ? "100%" : undefined,
                     background: this._background
                 }}
                 ref={this.createColumnDropRef} onPointerEnter={this.pointerEntered} onPointerLeave={this.pointerLeave}>
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 6d6f8d316..288fa8794 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -147,7 +147,7 @@ class TreeView extends React.Component<TreeViewProps> {
 
     onPointerEnter = (e: React.PointerEvent): void => {
         this.props.active(true) && Doc.BrushDoc(this.dataDoc);
-        if (e.buttons === 1 && SelectionManager.GetIsDragging()) {
+        if (e.buttons === 1 && DragManager.Vals.Instance.GetIsDragging()) {
             this._header!.current!.className = "treeViewItem-header";
             document.addEventListener("pointermove", this.onDragMove, true);
         }
@@ -451,7 +451,7 @@ class TreeView extends React.Component<TreeViewProps> {
                     fontWeight: this.props.document.searchMatch ? "bold" : undefined,
                     textDecoration: Doc.GetT(this.props.document, "title", "string", true) ? "underline" : undefined,
                     outline: BoolCast(this.props.document.workspaceBrush) ? "dashed 1px #06123232" : undefined,
-                    pointerEvents: this.props.active() || SelectionManager.GetIsDragging() ? undefined : "none"
+                    pointerEvents: this.props.active() || DragManager.Vals.Instance.GetIsDragging() ? undefined : "none"
                 }} >
                 {Doc.GetT(this.props.document, "editTitle", "boolean", true) ?
                     this.editableView("title") :
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
index 7df1e909f..0873fd1bb 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
@@ -10,6 +10,7 @@ import React = require("react");
 import { Utils, emptyFunction } from "../../../../Utils";
 import { SelectionManager } from "../../../util/SelectionManager";
 import { DocumentType } from "../../../documents/DocumentTypes";
+import { DragManager } from "../../../util/DragManager";
 
 @observer
 export class CollectionFreeFormLinksView extends React.Component {
@@ -36,7 +37,7 @@ export class CollectionFreeFormLinksView extends React.Component {
     }
 
     render() {
-        return SelectionManager.GetIsDragging() ? (null) : <div className="collectionfreeformlinksview-container">
+        return DragManager.Vals.Instance.GetIsDragging() ? (null) : <div className="collectionfreeformlinksview-container">
             <svg className="collectionfreeformlinksview-svgCanvas">
                 {this.uniqueConnections}
             </svg>
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 411fbd9e2..b2401e69e 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1174,7 +1174,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         DragManager.SetSnapLines(horizLines, vertLines);
     }
     onPointerOver = (e: React.PointerEvent) => {
-        if (SelectionManager.GetIsDragging()) {
+        if (DragManager.Vals.Instance.GetIsDragging()) {
             this.setupDragLines();
         }
         e.stopPropagation();
@@ -1233,7 +1233,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         const wscale = nw ? this.props.PanelWidth() / nw : 1;
         return wscale < hscale ? wscale : hscale;
     }
-    @computed get backgroundEvents() { return this.layoutDoc.isBackground && SelectionManager.GetIsDragging(); }
+    @computed get backgroundEvents() { return this.layoutDoc.isBackground && DragManager.Vals.Instance.GetIsDragging(); }
     render() {
         TraceMobx();
         const clientRect = this._mainCont?.getBoundingClientRect();
@@ -1247,7 +1247,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         return <div className={"collectionfreeformview-container"}
             ref={this.createDashEventsTarget}
             onPointerOver={this.onPointerOver}
-            onWheel={this.onPointerWheel} onClick={this.onClick}  //pointerEvents: SelectionManager.GetIsDragging() ? "all" : undefined,
+            onWheel={this.onPointerWheel} onClick={this.onClick}  //pointerEvents: DragManager.Vals.Instance.GetIsDragging() ? "all" : undefined,
             onPointerDown={this.onPointerDown} onPointerMove={this.onCursorMove} onDrop={this.onExternalDrop.bind(this)} onContextMenu={this.onContextMenu}
             style={{
                 pointerEvents: this.backgroundEvents ? "all" : undefined,
diff --git a/src/client/views/linking/LinkMenuGroup.tsx b/src/client/views/linking/LinkMenuGroup.tsx
index 928413a11..c97e1062d 100644
--- a/src/client/views/linking/LinkMenuGroup.tsx
+++ b/src/client/views/linking/LinkMenuGroup.tsx
@@ -9,7 +9,7 @@ import { DragManager, SetupDrag } from "../../util/DragManager";
 import { LinkManager } from "../../util/LinkManager";
 import { DocumentView } from "../nodes/DocumentView";
 import './LinkMenu.scss';
-import { LinkMenuItem } from "./LinkMenuItem";
+import { LinkMenuItem, StartLinkTargetsDrag } from "./LinkMenuItem";
 import React = require("react");
 
 interface LinkMenuGroupProps {
@@ -47,7 +47,7 @@ export class LinkMenuGroup extends React.Component<LinkMenuGroupProps> {
             document.removeEventListener("pointerup", this.onLinkButtonUp);
 
             const targets = this.props.group.map(l => LinkManager.Instance.getOppositeAnchor(l, this.props.sourceDoc)).filter(d => d) as Doc[];
-            DragManager.StartLinkTargetsDrag(this._drag.current, this.props.docView, e.x, e.y, this.props.sourceDoc, targets);
+            StartLinkTargetsDrag(this._drag.current, this.props.docView, e.x, e.y, this.props.sourceDoc, targets);
         }
         e.stopPropagation();
     }
diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx
index d091e06ef..d4e58fa8e 100644
--- a/src/client/views/linking/LinkMenuItem.tsx
+++ b/src/client/views/linking/LinkMenuItem.tsx
@@ -3,7 +3,7 @@ import { faArrowRight, faChevronDown, faChevronUp, faEdit, faEye, faTimes } from
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { action, observable } from 'mobx';
 import { observer } from "mobx-react";
-import { Doc } from '../../../new_fields/Doc';
+import { Doc, DocListCast } from '../../../new_fields/Doc';
 import { Cast, StrCast } from '../../../new_fields/Types';
 import { DragManager } from '../../util/DragManager';
 import { LinkManager } from '../../util/LinkManager';
@@ -26,6 +26,41 @@ interface LinkMenuItemProps {
     addDocTab: (document: Doc, where: string) => boolean;
 }
 
+// drag links and drop link targets (aliasing them if needed)
+export async function StartLinkTargetsDrag(dragEle: HTMLElement, docView: DocumentView, downX: number, downY: number, sourceDoc: Doc, specificLinks?: Doc[]) {
+    const draggedDocs = (specificLinks ? specificLinks : DocListCast(sourceDoc.links)).map(link => LinkManager.Instance.getOppositeAnchor(link, sourceDoc)).filter(l => l) as Doc[];
+
+    if (draggedDocs.length) {
+        const moddrag: Doc[] = [];
+        for (const draggedDoc of draggedDocs) {
+            const doc = await Cast(draggedDoc.annotationOn, Doc);
+            if (doc) moddrag.push(doc);
+        }
+
+        const dragData = new DragManager.DocumentDragData(moddrag.length ? moddrag : draggedDocs);
+        dragData.moveDocument = (doc: Doc, targetCollection: Doc | undefined, addDocument: (doc: Doc) => boolean): boolean => {
+            docView.props.removeDocument?.(doc);
+            addDocument(doc);
+            return true;
+        };
+        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[]));
+
+        DragManager.StartDrag([dragEle], dragData, downX, downY, undefined, finishDrag);
+    }
+}
+
+
 @observer
 export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
     private _drag = React.createRef<HTMLDivElement>();
@@ -83,7 +118,7 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
             document.removeEventListener("pointerup", this.onLinkButtonUp);
 
             this._eleClone.style.transform = `translate(${e.x}px, ${e.y}px)`;
-            DragManager.StartLinkTargetsDrag(this._eleClone, this.props.docView, e.x, e.y, this.props.sourceDoc, [this.props.linkDoc]);
+            StartLinkTargetsDrag(this._eleClone, this.props.docView, e.x, e.y, this.props.sourceDoc, [this.props.linkDoc]);
         }
         e.stopPropagation();
     }
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index 3feb533a0..1c5e13620 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -17,10 +17,9 @@ import { ContextMenu } from "../ContextMenu";
 import { Id } from "../../../new_fields/FieldSymbols";
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { DocumentView } from "./DocumentView";
-import { Docs } from "../../documents/Documents";
+import { Docs, DocUtils } from "../../documents/Documents";
 import { ComputedField } from "../../../new_fields/ScriptField";
 import { Networking } from "../../Network";
-import { Upload } from "../../../server/SharedMediaTypes";
 import { LinkAnchorBox } from "./LinkAnchorBox";
 
 // testing testing 
@@ -57,7 +56,6 @@ export class AudioBox extends ViewBoxBaseComponent<FieldViewProps, AudioDocument
     @computed get audioState(): undefined | "recording" | "paused" | "playing" { return this.dataDoc.audioState as (undefined | "recording" | "paused" | "playing"); }
     set audioState(value) { this.dataDoc.audioState = value; }
     public static SetScrubTime = (timeInMillisFrom1970: number) => { runInAction(() => AudioBox._scrubTime = 0); runInAction(() => AudioBox._scrubTime = timeInMillisFrom1970); };
-    public static ActiveRecordings: Doc[] = [];
 
     @computed get recordingStart() { return Cast(this.dataDoc[this.props.fieldKey + "-recordingStart"], DateField)?.date.getTime(); }
     async slideTemplate() { return (await Cast((await Cast(Doc.UserDoc().slidesBtn, Doc) as Doc).dragFactory, Doc) as Doc); }
@@ -145,7 +143,7 @@ export class AudioBox extends ViewBoxBaseComponent<FieldViewProps, AudioDocument
         this._stream = await navigator.mediaDevices.getUserMedia({ audio: true });
         this._recorder = new MediaRecorder(this._stream);
         this.dataDoc[this.props.fieldKey + "-recordingStart"] = new DateField(new Date());
-        AudioBox.ActiveRecordings.push(this.props.Document);
+        DocUtils.ActiveRecordings.push(this.props.Document);
         this._recorder.ondataavailable = async (e: any) => {
             const [{ result }] = await Networking.UploadFilesToServer(e.data);
             if (!(result instanceof Error)) {
@@ -172,8 +170,8 @@ export class AudioBox extends ViewBoxBaseComponent<FieldViewProps, AudioDocument
         this.dataDoc.duration = (new Date().getTime() - this._recordStart) / 1000;
         this.audioState = "paused";
         this._stream?.getAudioTracks()[0].stop();
-        const ind = AudioBox.ActiveRecordings.indexOf(this.props.Document);
-        ind !== -1 && (AudioBox.ActiveRecordings.splice(ind, 1));
+        const ind = DocUtils.ActiveRecordings.indexOf(this.props.Document);
+        ind !== -1 && (DocUtils.ActiveRecordings.splice(ind, 1));
     });
 
     recordClick = (e: React.MouseEvent) => {
diff --git a/src/client/views/nodes/ContentFittingDocumentView.tsx b/src/client/views/nodes/ContentFittingDocumentView.tsx
index 3c2c6c87e..1c6250b94 100644
--- a/src/client/views/nodes/ContentFittingDocumentView.tsx
+++ b/src/client/views/nodes/ContentFittingDocumentView.tsx
@@ -3,7 +3,7 @@ import { computed } from "mobx";
 import { observer } from "mobx-react";
 import "react-table/react-table.css";
 import { Doc, Opt, WidthSym, HeightSym } from "../../../new_fields/Doc";
-import { NumCast, StrCast } from "../../../new_fields/Types";
+import { NumCast, StrCast, Cast } from "../../../new_fields/Types";
 import { TraceMobx } from "../../../new_fields/util";
 import { emptyFunction, returnOne } from "../../../Utils";
 import '../DocumentDecorations.scss';
@@ -14,7 +14,11 @@ import "./ContentFittingDocumentView.scss";
 @observer
 export class ContentFittingDocumentView extends React.Component<DocumentViewProps>{
     public get displayName() { return "DocumentView(" + this.props.Document?.title + ")"; } // this makes mobx trace() statements more descriptive
-    private get layoutDoc() { return this.props.LayoutTemplate?.() || Doc.Layout(this.props.Document); }
+    private get layoutDoc() {
+        return this.props.LayoutTemplate?.() ||
+            (this.props.layoutKey && Doc.Layout(this.props.Document, Cast(this.props.Document[this.props.layoutKey], Doc, null))) ||
+            Doc.Layout(this.props.Document);
+    }
     @computed get freezeDimensions() { return this.props.FreezeDimensions; }
     nativeWidth = () => NumCast(this.layoutDoc?._nativeWidth, this.props.NativeWidth?.() || (this.freezeDimensions && this.layoutDoc ? this.layoutDoc[WidthSym]() : this.props.PanelWidth()));
     nativeHeight = () => NumCast(this.layoutDoc?._nativeHeight, this.props.NativeHeight?.() || (this.freezeDimensions && this.layoutDoc ? this.layoutDoc[HeightSym]() : this.props.PanelHeight()));
diff --git a/src/client/views/nodes/DocumentBox.scss b/src/client/views/nodes/DocumentBox.scss
index ce21391ce..3a27c16c1 100644
--- a/src/client/views/nodes/DocumentBox.scss
+++ b/src/client/views/nodes/DocumentBox.scss
@@ -2,7 +2,8 @@
     width: 100%;
     height: 100%;
     pointer-events: all;
-    background: gray;
+    background: rgb(241, 239, 235);
+    position: absolute;
     .documentBox-lock {
         margin: auto;
         color: white;
diff --git a/src/client/views/nodes/DocumentBox.tsx b/src/client/views/nodes/DocumentBox.tsx
index 8d422fe67..b53c7cfe6 100644
--- a/src/client/views/nodes/DocumentBox.tsx
+++ b/src/client/views/nodes/DocumentBox.tsx
@@ -1,23 +1,24 @@
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { IReactionDisposer, reaction, computed } from "mobx";
+import { action, IReactionDisposer, reaction } from "mobx";
 import { observer } from "mobx-react";
 import { Doc, Field } from "../../../new_fields/Doc";
-import { documentSchema, collectionSchema } from "../../../new_fields/documentSchemas";
+import { collectionSchema, documentSchema } from "../../../new_fields/documentSchemas";
 import { makeInterface } from "../../../new_fields/Schema";
 import { ComputedField } from "../../../new_fields/ScriptField";
 import { Cast, NumCast, StrCast } from "../../../new_fields/Types";
+import { TraceMobx } from "../../../new_fields/util";
 import { emptyPath, returnFalse, returnOne, returnZero } from "../../../Utils";
+import { DocumentType } from "../../documents/DocumentTypes";
+import { DragManager } from "../../util/DragManager";
+import { undoBatch } from "../../util/UndoManager";
 import { ContextMenu } from "../ContextMenu";
 import { ContextMenuProps } from "../ContextMenuItem";
 import { ViewBoxAnnotatableComponent } from "../DocComponent";
 import { ContentFittingDocumentView } from "./ContentFittingDocumentView";
 import "./DocumentBox.scss";
+import { DocumentView } from "./DocumentView";
 import { FieldView, FieldViewProps } from "./FieldView";
 import React = require("react");
-import { TraceMobx } from "../../../new_fields/util";
-import { Docs } from "../../documents/Documents";
-import { KeyValueBox } from "./KeyValueBox";
-import { DocumentView } from "./DocumentView";
 
 type DocHolderBoxSchema = makeInterface<[typeof documentSchema, typeof collectionSchema]>;
 const DocHolderBoxDocument = makeInterface(documentSchema, collectionSchema);
@@ -26,7 +27,9 @@ const DocHolderBoxDocument = makeInterface(documentSchema, collectionSchema);
 export class DocHolderBox extends ViewBoxAnnotatableComponent<FieldViewProps, DocHolderBoxSchema>(DocHolderBoxDocument) {
     public static LayoutString(fieldKey: string) { return FieldView.LayoutString(DocHolderBox, fieldKey); }
     _prevSelectionDisposer: IReactionDisposer | undefined;
+    _dropDisposer?: DragManager.DragDropDisposer;
     _selections: Doc[] = [];
+    _contRef = React.createRef<HTMLDivElement>();
     _curSelection = -1;
     componentDidMount() {
         this._prevSelectionDisposer = reaction(() => this.layoutDoc[this.props.fieldKey], (data) => {
@@ -101,21 +104,15 @@ export class DocHolderBox extends ViewBoxAnnotatableComponent<FieldViewProps, Do
             e.stopPropagation();
         }
     }
-    _contRef = React.createRef<HTMLDivElement>();
     pwidth = () => this.props.PanelWidth() - 2 * this.xPad;
     pheight = () => this.props.PanelHeight() - 2 * this.yPad;
     getTransform = () => this.props.ScreenToLocalTransform().translate(-this.xPad, -this.yPad);
     isActive = () => this.active() || !this.props.renderDepth;
+    layoutTemplateDoc = () => Cast(this.props.Document.childLayoutTemplate, Doc, null);
     get renderContents() {
         const containedDoc = Cast(this.dataDoc[this.props.fieldKey], Doc, null);
-        const childTemplateName = StrCast(this.layoutDoc.childTemplateName);
-        if (containedDoc && childTemplateName && !containedDoc["layout_" + childTemplateName]) {
-            setTimeout(() => {
-                Doc.createCustomView(containedDoc, Docs.Create.StackingDocument, childTemplateName);
-                Doc.expandTemplateLayout(Cast(containedDoc["layout_" + childTemplateName], Doc, null), containedDoc, undefined);
-            }, 0);
-        }
-        const contents = !(containedDoc instanceof Doc) ? (null) : this.layoutDoc.childLayoutString ?
+        const layoutTemplate = StrCast(this.layoutDoc.childLayoutString);
+        const contents = !(containedDoc instanceof Doc) ? (null) : this.layoutDoc.childLayoutString || this.layoutTemplateDoc() ?
             <DocumentView
                 Document={containedDoc}
                 DataDoc={undefined}
@@ -123,8 +120,8 @@ export class DocHolderBox extends ViewBoxAnnotatableComponent<FieldViewProps, Do
                 ContainingCollectionView={this as any} // bcz: hack!  need to pass a prop that can be used to select the container (ie, 'this') when the up selector in document decorations is clicked.  currently, the up selector allows only a containing collection to be selected
                 ContainingCollectionDoc={undefined}
                 fitToBox={true}
-                LayoutTemplateString={StrCast(this.layoutDoc.childLayoutString)}
-                //layoutKey={childTemplateName ? "layout_" + childTemplateName : "layout"}
+                LayoutTemplateString={layoutTemplate}
+                LayoutTemplate={this.layoutTemplateDoc}
                 rootSelected={this.props.isSelected}
                 addDocument={this.props.addDocument}
                 moveDocument={this.props.moveDocument}
@@ -139,7 +136,7 @@ export class DocHolderBox extends ViewBoxAnnotatableComponent<FieldViewProps, Do
                 PanelHeight={this.pheight}
                 focus={this.props.focus}
                 parentActive={this.isActive}
-                dontRegisterView={!this.isSelectionLocked()}
+                dontRegisterView={true}
                 whenActiveChanged={this.props.whenActiveChanged}
                 bringToFront={returnFalse}
                 ContentScaling={returnOne} /> :
@@ -150,8 +147,8 @@ export class DocHolderBox extends ViewBoxAnnotatableComponent<FieldViewProps, Do
                 ContainingCollectionView={this as any} // bcz: hack!  need to pass a prop that can be used to select the container (ie, 'this') when the up selector in document decorations is clicked.  currently, the up selector allows only a containing collection to be selected
                 ContainingCollectionDoc={undefined}
                 fitToBox={true}
-                LayoutTemplateString={StrCast(this.layoutDoc.childLayoutString)}
-                //layoutKey={childTemplateName ? "layout_" + childTemplateName : "layout"}
+                LayoutTemplateString={layoutTemplate}
+                LayoutTemplate={this.layoutTemplateDoc}
                 rootSelected={this.props.isSelected}
                 addDocument={this.props.addDocument}
                 moveDocument={this.props.moveDocument}
@@ -166,7 +163,7 @@ export class DocHolderBox extends ViewBoxAnnotatableComponent<FieldViewProps, Do
                 PanelHeight={this.pheight}
                 focus={this.props.focus}
                 parentActive={this.isActive}
-                dontRegisterView={!this.isSelectionLocked()}
+                dontRegisterView={true}
                 whenActiveChanged={this.props.whenActiveChanged}
                 bringToFront={returnFalse}
                 ContentScaling={returnOne}
@@ -185,10 +182,26 @@ export class DocHolderBox extends ViewBoxAnnotatableComponent<FieldViewProps, Do
                 borderBottom: `#0000005e solid ${this.yPad}px`,
             }}>
             {this.renderContents}
-            <div className="documentBox-lock" onClick={this.onLockClick}
+            <div className="documentBox-lock" onClick={this.onLockClick} ref={this.createDropTarget}
                 style={{ marginTop: - this.yPad }}>
                 <FontAwesomeIcon icon={this.isSelectionLocked() ? "lock" : "unlock"} size="sm" />
             </div>
         </div >;
     }
+
+    @undoBatch
+    @action
+    drop = (e: Event, de: DragManager.DropEvent) => {
+        if (de.complete.docDragData) {
+            if (de.complete.docDragData.draggedDocuments[0].type === DocumentType.FONTICON) {
+                const doc = Cast(de.complete.docDragData.draggedDocuments[0].dragFactory, Doc, null);
+                this.props.Document.childLayoutTemplate = doc;
+            }
+        }
+    }
+    protected createDropTarget = (ele: HTMLDivElement) => {
+        this._dropDisposer?.();
+        ele && (this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this), this.props.Document));
+    }
+
 }
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 7a16e8836..c4cd5978a 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1102,7 +1102,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     }
     @computed get ignorePointerEvents() {
         return this.props.pointerEvents === false ||
-            (this.Document.isBackground && !this.isSelected() && !SelectionManager.GetIsDragging()) ||
+            (this.Document.isBackground && !this.isSelected() && !DragManager.Vals.Instance.GetIsDragging()) ||
             (this.Document.type === DocumentType.INK && InkingControl.Instance.selectedTool !== InkTool.None);
     }
     @undoBatch
diff --git a/src/client/views/nodes/QueryBox.tsx b/src/client/views/nodes/QueryBox.tsx
index 76885eada..d248b098c 100644
--- a/src/client/views/nodes/QueryBox.tsx
+++ b/src/client/views/nodes/QueryBox.tsx
@@ -11,6 +11,7 @@ import { SearchBox } from "../search/SearchBox";
 import { FieldView, FieldViewProps } from './FieldView';
 import "./QueryBox.scss";
 import { List } from "../../../new_fields/List";
+import { DragManager } from "../../util/DragManager";
 
 type QueryDocument = makeInterface<[typeof documentSchema]>;
 const QueryDocument = makeInterface(documentSchema);
@@ -27,7 +28,7 @@ export class QueryBox extends ViewBoxAnnotatableComponent<FieldViewProps, QueryD
     }
 
     render() {
-        const dragging = !SelectionManager.GetIsDragging() ? "" : "-dragging";
+        const dragging = !DragManager.Vals.Instance.GetIsDragging() ? "" : "-dragging";
         return <div className={`queryBox${dragging}`} onWheel={(e) => e.stopPropagation()} >
             <SearchBox
                 id={this.props.Document[Id]}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 30c4a45ac..658a55f51 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -198,15 +198,18 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             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 = Cast(this.props.Document[this.props.fieldKey + "-textTemplate"], RichTextField);
-            const curProto = Cast(Cast(this.dataDoc.proto, Doc, null)?.[this.fieldKey], RichTextField, null);
+            const curTemp = Cast(this.props.Document[this.props.fieldKey + "-textTemplate"], RichTextField);               // 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
+            const curLayout = this.rootDoc !== this.layoutDoc ? Cast(this.layoutDoc[this.fieldKey], RichTextField, null) : undefined; // the default text stored in a layout template
             const json = JSON.stringify(state.toJSON());
             if (!this._applyingChange && json.replace(/"selection":.*/, "") !== curProto?.Data.replace(/"selection":.*/, "")) {
                 this._applyingChange = true;
                 this.dataDoc[this.props.fieldKey + "-lastModified"] = new DateField(new Date(Date.now()));
-                if ((!curTemp && !curProto) || curText) { // if no template, or there's text, write it to the document. (if this is driven by a template, then this overwrites the template text which is intended)
-                    this.dataDoc[this.props.fieldKey] = new RichTextField(json, curText);
-                    this.dataDoc[this.props.fieldKey + "-noTemplate"] = (curTemp?.Text || "") !== curText; // mark the data field as being split from the template if it has been edited
+                if ((!curTemp && !curProto) || curText) { // if no template, or there's text that didn't come from the layout template, write it to the document. (if this is driven by a template, then this overwrites the template text which is intended)
+                    if (curText !== curLayout?.Text) {
+                        this.dataDoc[this.props.fieldKey] = new RichTextField(json, curText);
+                        this.dataDoc[this.props.fieldKey + "-noTemplate"] = (curTemp?.Text || "") !== curText; // mark the data field as being split from the template if it has been edited
+                    }
                 } else { // if we've deleted all the text in a note driven by a template, then restore the template data
                     this.dataDoc[this.props.fieldKey] = undefined;
                     this._editorView.updateState(EditorState.fromJSON(this.config, JSON.parse((curProto || curTemp).Data)));
diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts
index b63a3dbbf..bf99f449f 100644
--- a/src/server/authentication/models/current_user_utils.ts
+++ b/src/server/authentication/models/current_user_utils.ts
@@ -217,28 +217,29 @@ export class CurrentUserUtils {
     static setupDefaultIconTemplates(doc: Doc) {
         if (doc["template-icon-view"] === undefined) {
             const iconView = Docs.Create.TextDocument("", {
-                title: "icon", _width: 150, _height: 30, isTemplateDoc: true,
-                onClick: ScriptField.MakeScript("deiconifyView(self)")
+                title: "icon", _width: 150, _height: 30, isTemplateDoc: true, onDoubleClick: ScriptField.MakeScript("deiconifyView(self)")
             });
             Doc.GetProto(iconView).icon = new RichTextField('{"doc":{"type":"doc","content":[{"type":"paragraph","attrs":{"align":null,"color":null,"id":null,"indent":null,"inset":null,"lineSpacing":null,"paddingBottom":null,"paddingTop":null},"content":[{"type":"dashField","attrs":{"fieldKey":"title","docid":""}}]}]},"selection":{"type":"text","anchor":2,"head":2},"storedMarks":[]}', "");
             iconView.isTemplateDoc = makeTemplate(iconView);
             doc["template-icon-view"] = new PrefetchProxy(iconView);
         }
         if (doc["template-icon-view-rtf"] === undefined) {
-            const iconRtfView = Docs.Create.LabelDocument({ title: "icon_" + DocumentType.RTF, textTransform: "unset", letterSpacing: "unset", _width: 150, _height: 70, _xPadding: 10, _yPadding: 10, isTemplateDoc: true, onClick: ScriptField.MakeScript("deiconifyView(self)") });
+            const iconRtfView = Docs.Create.LabelDocument({
+                title: "icon_" + DocumentType.RTF, textTransform: "unset", letterSpacing: "unset",
+                _width: 150, _height: 70, _xPadding: 10, _yPadding: 10, isTemplateDoc: true, onDoubleClick: ScriptField.MakeScript("deiconifyView(self)")
+            });
             iconRtfView.isTemplateDoc = makeTemplate(iconRtfView, true, "icon_" + DocumentType.RTF);
             doc["template-icon-view-rtf"] = new PrefetchProxy(iconRtfView);
         }
         if (doc["template-icon-view-img"] === undefined) {
             const iconImageView = Docs.Create.ImageDocument("http://www.cs.brown.edu/~bcz/face.gif", {
-                title: "data", _width: 50, isTemplateDoc: true,
-                onClick: ScriptField.MakeScript("deiconifyView(self)")
+                title: "data", _width: 50, isTemplateDoc: true, onDoubleClick: ScriptField.MakeScript("deiconifyView(self)")
             });
             iconImageView.isTemplateDoc = makeTemplate(iconImageView, true, "icon_" + DocumentType.IMG);
             doc["template-icon-view-img"] = new PrefetchProxy(iconImageView);
         }
         if (doc["template-icon-view-col"] === undefined) {
-            const iconColView = Docs.Create.TreeDocument([], { title: "data", _width: 180, _height: 80, onClick: ScriptField.MakeScript("deiconifyView(self)") });
+            const iconColView = Docs.Create.TreeDocument([], { title: "data", _width: 180, _height: 80, onDoubleClick: ScriptField.MakeScript("deiconifyView(self)") });
             iconColView.isTemplateDoc = makeTemplate(iconColView, true, "icon_" + DocumentType.COL);
             doc["template-icon-view-col"] = new PrefetchProxy(iconColView);
         }
@@ -265,9 +266,17 @@ export class CurrentUserUtils {
             doc.emptyCollection = Docs.Create.FreeformDocument([],
                 { _nativeWidth: undefined, _nativeHeight: undefined, _LODdisable: true, _width: 150, _height: 100, title: "freeform" });
         }
+        if (doc.emptyDocHolder === undefined) {
+            doc.emptyDocHolder = Docs.Create.DocumentDocument(
+                ComputedField.MakeFunction("selectedDocs(this,this.excludeCollections,[_last_])?.[0]") as any,
+                { _width: 250, _height: 250, title: "container" });
+        }
+        if (doc.emptyWebpage === undefined) {
+            doc.emptyWebpage = Docs.Create.WebDocument("", { title: "New Webpage", _width: 600 })
+        }
         return [
             { title: "Drag a collection", label: "Col", icon: "folder", click: 'openOnRight(getCopy(this.dragFactory, true))', drag: 'getCopy(this.dragFactory, true)', dragFactory: doc.emptyCollection as Doc },
-            { title: "Drag a web page", label: "Web", icon: "globe-asia", ignoreClick: true, drag: 'Docs.Create.WebDocument("", { title: "New Webpage" })' },
+            { title: "Drag a web page", label: "Web", icon: "globe-asia", click: 'openOnRight(getCopy(this.dragFactory, true))', drag: 'getCopy(this.dragFactory, true)', dragFactory: doc.emptyWebpage as Doc },
             { title: "Drag a cat image", label: "Img", icon: "cat", ignoreClick: true, drag: '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" })' },
             { title: "Drag a screenshot", label: "Grab", icon: "photo-video", ignoreClick: true, drag: 'Docs.Create.ScreenshotDocument("", { _width: 400, _height: 200, title: "screen snapshot" })' },
             { title: "Drag a webcam", label: "Cam", icon: "video", ignoreClick: true, drag: 'Docs.Create.WebCamDocument("", { _width: 400, _height: 400, title: "a test cam" })' },
@@ -284,7 +293,7 @@ export class CurrentUserUtils {
             // { title: "use stamp", icon: "stamp", click: 'activateStamp(this.activePen.inkPen = sameDocs(this.activePen.inkPen, this) ? undefined : this)', backgroundColor: "orange", ischecked: `sameDocs(this.activePen.inkPen, this)`, activePen: doc },
             // { title: "use eraser", icon: "eraser", click: 'activateEraser(this.activePen.inkPen = sameDocs(this.activePen.inkPen, this) ? undefined : this);', ischecked: `sameDocs(this.activePen.inkPen, this)`, backgroundColor: "pink", activePen: doc },
             // { title: "use drag", icon: "mouse-pointer", click: 'deactivateInk();this.activePen.inkPen = this;', ischecked: `sameDocs(this.activePen.inkPen, this)`, backgroundColor: "white", activePen: doc },
-            { title: "Drag a document previewer", label: "Prev", icon: "expand", ignoreClick: true, drag: 'Docs.Create.DocumentDocument(ComputedField.MakeFunction("selectedDocs(this,this.excludeCollections,[_last_])?.[0]"), { _width: 250, _height: 250, title: "container" })' },
+            { title: "Drag a document previewer", label: "Prev", icon: "expand", click: 'openOnRight(getCopy(this.dragFactory, true))', drag: 'getCopy(this.dragFactory,true)', dragFactory: doc.emptyDocHolder as Doc },
             { title: "Drag a Calculator REPL", label: "repl", icon: "calculator", click: 'addOverlayWindow("ScriptingRepl", { x: 300, y: 100, width: 200, height: 200, title: "Scripting REPL" })' },
         ];
 
@@ -308,7 +317,7 @@ export class CurrentUserUtils {
                 title: data.title,
                 label: data.label,
                 ignoreClick: data.ignoreClick,
-                dropAction: data.click ? "copy" : undefined,
+                dropAction: "copy",
                 onDragStart: data.drag ? ScriptField.MakeFunction(data.drag) : undefined,
                 onClick: data.click ? ScriptField.MakeScript(data.click) : undefined,
                 ischecked: data.ischecked ? ComputedField.MakeFunction(data.ischecked) : undefined,
-- 
cgit v1.2.3-70-g09d2


From 1027f0b2e40df0ca13dfa2db97f278e804dadb68 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Mon, 4 May 2020 00:28:52 -0400
Subject: fixed warning

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

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

diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index b2401e69e..2aa9b1f5f 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -857,7 +857,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
     @computed get onChildDoubleClickHandler() { return this.props.childDoubleClickScript || ScriptCast(this.Document.onChildDoubleClick); }
     @computed get backgroundActive() { return this.layoutDoc.isBackground && (this.props.ContainingCollectionView?.active() || this.props.active()); }
     backgroundHalo = () => BoolCast(this.Document.useClusters);
-    parentActive = () => this.props.active() || this.backgroundActive ? true : false;
+    parentActive = (outsideReaction: boolean) => this.props.active(outsideReaction) || this.backgroundActive ? true : false;
     getChildDocumentViewProps(childLayout: Doc, childData?: Doc): DocumentViewProps {
         return {
             ...this.props,
-- 
cgit v1.2.3-70-g09d2


From d9227908ba8e8db5084c468a242b2839ab11a33d Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Mon, 4 May 2020 12:50:46 -0400
Subject: fixed links and snap lines broken by moving things into DragManager.

---
 src/client/documents/Documents.ts                  |   2 +-
 src/client/util/DragManager.ts                     |  34 ++--
 src/client/util/SelectionManager.ts                |   3 +
 src/client/util/SnappingManager.ts                 |  29 +++
 src/client/views/DocumentDecorations.tsx           |   5 +-
 src/client/views/MainView.scss                     |   9 +
 src/client/views/MainView.tsx                      |  18 +-
 .../views/collections/CollectionDockingView.tsx    |   5 +-
 .../collections/CollectionMasonryViewFieldRow.tsx  |   4 +-
 .../views/collections/CollectionPileView.tsx       |   6 +-
 .../views/collections/CollectionSchemaCells.tsx    |   4 +-
 .../CollectionSchemaMovableTableHOC.tsx            |   6 +-
 .../views/collections/CollectionStackingView.tsx   |   7 +-
 .../CollectionStackingViewFieldColumn.tsx          |   6 +-
 .../views/collections/CollectionTreeView.tsx       |   7 +-
 .../CollectionFreeFormLinkView.tsx                 |   9 +-
 .../CollectionFreeFormLinksView.tsx                |   4 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |   7 +-
 src/client/views/nodes/DocHolderBox.scss           |  15 ++
 src/client/views/nodes/DocHolderBox.tsx            | 207 +++++++++++++++++++++
 src/client/views/nodes/DocumentBox.scss            |  15 --
 src/client/views/nodes/DocumentBox.tsx             | 207 ---------------------
 src/client/views/nodes/DocumentContentsView.tsx    |   2 +-
 src/client/views/nodes/DocumentView.tsx            |   3 +-
 src/client/views/nodes/QueryBox.tsx                |   5 +-
 25 files changed, 328 insertions(+), 291 deletions(-)
 create mode 100644 src/client/util/SnappingManager.ts
 create mode 100644 src/client/views/nodes/DocHolderBox.scss
 create mode 100644 src/client/views/nodes/DocHolderBox.tsx
 delete mode 100644 src/client/views/nodes/DocumentBox.scss
 delete mode 100644 src/client/views/nodes/DocumentBox.tsx

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 45687f597..f5d6cd7f6 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -37,7 +37,7 @@ import { DashWebRTCVideo } from "../views/webcam/DashWebRTCVideo";
 import { QueryBox } from "../views/nodes/QueryBox";
 import { ColorBox } from "../views/nodes/ColorBox";
 import { LinkAnchorBox } from "../views/nodes/LinkAnchorBox";
-import { DocHolderBox } from "../views/nodes/DocumentBox";
+import { DocHolderBox } from "../views/nodes/DocHolderBox";
 import { InkingStroke } from "../views/InkingStroke";
 import { InkField } from "../../new_fields/InkField";
 import { InkingControl } from "../views/InkingControl";
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 348aba588..0d208cf1b 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -11,6 +11,7 @@ import { emptyFunction } from "../../Utils";
 import { Docs, DocUtils } from "../documents/Documents";
 import * as globalCssVariables from "../views/globalCssVariables.scss";
 import { UndoManager } from "./UndoManager";
+import { SnappingManager } from "./SnappingManager";
 
 export type dropActionType = "alias" | "copy" | "move" | undefined; // undefined = move
 export function SetupDrag(
@@ -49,7 +50,7 @@ export function SetupDrag(
             if (e.shiftKey) {
                 e.persist();
                 const dragDoc = await docFunc();
-                dragDoc && DragManager.Vals.Instance.StartWindowDrag?.({
+                dragDoc && DragManager.StartWindowDrag?.({
                     pageX: e.pageX,
                     pageY: e.pageY,
                     preventDefault: emptyFunction,
@@ -66,19 +67,7 @@ export function SetupDrag(
 
 export namespace DragManager {
     let dragDiv: HTMLDivElement;
-    export class Vals {
-        static Instance: Vals = new Vals();
-        @observable public IsDragging: boolean = false;
-        @observable public horizSnapLines: number[] = [];
-        @observable public vertSnapLines: number[] = [];
-        public StartWindowDrag: Opt<((e: any, dragDocs: Doc[]) => void)> = undefined;
-        public SetIsDragging(dragging: boolean) { runInAction(() => this.IsDragging = dragging); }
-        public GetIsDragging() { return this.IsDragging; }
-        @action public clearSnapLines() {
-            this.vertSnapLines.length = 0;
-            this.horizSnapLines.length = 0;
-        }
-    }
+    export let StartWindowDrag: Opt<((e: any, dragDocs: Doc[]) => void)> = undefined;
 
     export function Root() {
         const root = document.getElementById("root");
@@ -267,8 +256,7 @@ export namespace DragManager {
     }
 
     export function SetSnapLines(horizLines: number[], vertLines: number[]) {
-        DragManager.Vals.Instance.horizSnapLines.push(...horizLines);
-        DragManager.Vals.Instance.vertSnapLines.push(...vertLines);
+        SnappingManager.setSnapLines(horizLines, vertLines);
     }
 
     export function snapDrag(e: PointerEvent, xFromLeft: number, yFromTop: number, xFromRight: number, yFromBottom: number) {
@@ -286,8 +274,8 @@ export namespace DragManager {
         };
 
         return {
-            thisX: snapVal([xFromLeft, xFromRight], e.pageX, DragManager.Vals.Instance.vertSnapLines),
-            thisY: snapVal([yFromTop, yFromBottom], e.pageY, DragManager.Vals.Instance.horizSnapLines)
+            thisX: snapVal([xFromLeft, xFromRight], e.pageX, SnappingManager.vertSnapLines()),
+            thisY: snapVal([yFromTop, yFromBottom], e.pageY, SnappingManager.horizSnapLines())
         };
     }
     export let docsBeingDragged: Doc[] = [];
@@ -299,7 +287,7 @@ export namespace DragManager {
             dragDiv.style.pointerEvents = "none";
             DragManager.Root().appendChild(dragDiv);
         }
-        DragManager.Vals.Instance.SetIsDragging(true);
+        SnappingManager.SetIsDragging(true);
         const scaleXs: number[] = [];
         const scaleYs: number[] = [];
         const xs: number[] = [];
@@ -388,7 +376,7 @@ export namespace DragManager {
                 }
                 AbortDrag();
                 finishDrag?.(new DragCompleteEvent(true, dragData));
-                DragManager.Vals.Instance.StartWindowDrag?.({
+                DragManager.StartWindowDrag?.({
                     pageX: e.pageX,
                     pageY: e.pageY,
                     preventDefault: emptyFunction,
@@ -415,19 +403,19 @@ export namespace DragManager {
         const endDrag = action(() => {
             document.removeEventListener("pointermove", moveHandler, true);
             document.removeEventListener("pointerup", upHandler);
-            DragManager.Vals.Instance.clearSnapLines();
+            SnappingManager.clearSnapLines();
         });
 
         AbortDrag = () => {
             hideDragShowOriginalElements();
-            DragManager.Vals.Instance.SetIsDragging(false);
+            SnappingManager.SetIsDragging(false);
             options?.dragComplete?.(new DragCompleteEvent(true, dragData));
             endDrag();
         };
         const upHandler = (e: PointerEvent) => {
             hideDragShowOriginalElements();
             dispatchDrag(eles, e, dragData, xFromLeft, yFromTop, xFromRight, yFromBottom, options, finishDrag);
-            DragManager.Vals.Instance.SetIsDragging(false);
+            SnappingManager.SetIsDragging(false);
             endDrag();
             options?.dragComplete?.(new DragCompleteEvent(false, dragData));
         };
diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index d509168b6..11d2cafb2 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -8,6 +8,7 @@ export namespace SelectionManager {
 
     class Manager {
 
+        @observable IsDragging: boolean = false;
         SelectedDocuments: ObservableMap<DocumentView, boolean> = new ObservableMap();
 
         @action
@@ -53,6 +54,8 @@ export namespace SelectionManager {
         manager.SelectDoc(docView, ctrlPressed);
     }
 
+    export function SetIsDragging(dragging: boolean) { runInAction(() => manager.IsDragging = dragging); }
+    export function GetIsDragging() { return manager.IsDragging; }
     // 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.
diff --git a/src/client/util/SnappingManager.ts b/src/client/util/SnappingManager.ts
new file mode 100644
index 000000000..fc07e8ab4
--- /dev/null
+++ b/src/client/util/SnappingManager.ts
@@ -0,0 +1,29 @@
+import { observable, action, runInAction } from "mobx";
+
+export namespace SnappingManager {
+
+    class Manager {
+        @observable IsDragging: boolean = false;
+        @observable public horizSnapLines: number[] = [];
+        @observable public vertSnapLines: number[] = [];
+        @action public clearSnapLines() {
+            this.vertSnapLines = [];
+            this.horizSnapLines = [];
+        }
+        @action public setSnapLines(horizLines: number[], vertLines: number[]) {
+            this.horizSnapLines = horizLines;
+            this.vertSnapLines = vertLines;
+        }
+    }
+
+    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; }
+}
+
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index f8e77d90a..b89806656 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -21,6 +21,7 @@ import { Id } from '../../new_fields/FieldSymbols';
 import e = require('express');
 import { CollectionDockingView } from './collections/CollectionDockingView';
 import { MainView } from './MainView';
+import { SnappingManager } from '../util/SnappingManager';
 
 library.add(faCaretUp);
 library.add(faObjectGroup);
@@ -364,7 +365,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
         this.Interacting = false;
         (e.button === 0) && this._resizeUndo?.end();
         this._resizeUndo = undefined;
-        DragManager.Vals.Instance.clearSnapLines();
+        SnappingManager.clearSnapLines();
     }
 
     @computed
@@ -403,7 +404,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
         const darkScheme = Cast(Doc.UserDoc().activeWorkspace, Doc, null)?.darkScheme ? "dimgray" : undefined;
         const bounds = this.Bounds;
         const seldoc = SelectionManager.SelectedDocuments().length ? SelectionManager.SelectedDocuments()[0] : undefined;
-        if (DragManager.Vals.Instance.GetIsDragging() || bounds.r - bounds.x < 2 || bounds.x === Number.MAX_VALUE || !seldoc || this._hidden || isNaN(bounds.r) || isNaN(bounds.b) || isNaN(bounds.x) || isNaN(bounds.y)) {
+        if (SnappingManager.GetIsDragging() || bounds.r - bounds.x < 2 || bounds.x === Number.MAX_VALUE || !seldoc || this._hidden || isNaN(bounds.r) || isNaN(bounds.b) || isNaN(bounds.x) || isNaN(bounds.y)) {
             return (null);
         }
         const minimal = bounds.r - bounds.x < 100 ? true : false;
diff --git a/src/client/views/MainView.scss b/src/client/views/MainView.scss
index 04288a9e1..753ba700c 100644
--- a/src/client/views/MainView.scss
+++ b/src/client/views/MainView.scss
@@ -22,6 +22,15 @@
     z-index: 1;
 }
 
+.mainView-snapLines {
+    position: absolute;
+    top: 0;
+    left: 0;
+    width: 100%;
+    height: 100%;
+    pointer-events:none;
+}
+
 .mainView-container, .mainView-container-dark {
     input {
         color: unset !important;
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 71c2bf245..9ebd60c52 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -44,6 +44,7 @@ import { PreviewCursor } from './PreviewCursor';
 import { ScriptField } from '../../new_fields/ScriptField';
 import { TimelineMenu } from './animationtimeline/TimelineMenu';
 import { DragManager } from '../util/DragManager';
+import { SnappingManager } from '../util/SnappingManager';
 
 @observer
 export class MainView extends React.Component {
@@ -575,6 +576,15 @@ export class MainView extends React.Component {
         return this._mainViewRef;
     }
 
+    @computed get snapLines() {
+        return <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>
+    }
+
     render() {
         return (<div className={"mainView-container" + (this.darkScheme ? "-dark" : "")} ref={this._mainViewRef}>
             <DictationOverlay />
@@ -592,14 +602,8 @@ export class MainView extends React.Component {
             <MarqueeOptionsMenu />
             <RichTextMenu />
             <OverlayView />
-            {// TO VIEW SNAP LINES
-                <div className="snapLines" style={{ position: "absolute", top: 0, left: 0, width: "100%", height: "100%", pointerEvents: "none" }}>
-                    <svg style={{ width: "100%", height: "100%" }}>
-                        {DragManager.Vals.Instance.horizSnapLines.map((l: any) => <line x1="0" y1={l} x2="2000" y2={l} stroke="black" opacity={0.3} strokeWidth={0.5} strokeDasharray={"1 1"} />)}
-                        {DragManager.Vals.Instance.vertSnapLines.map((l: any) => <line y1="0" x1={l} y2="2000" x2={l} stroke="black" opacity={0.3} strokeWidth={0.5} strokeDasharray={"1 1"} />)}
-                    </svg>
-                </div>}
             <TimelineMenu />
+            {this.snapLines}
         </div >);
     }
 }
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 33ece13cc..6cd5d1b1b 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -30,6 +30,7 @@ import { SubCollectionViewProps } from "./CollectionSubView";
 import { DockingViewButtonSelector } from './ParentDocumentSelector';
 import React = require("react");
 import { CollectionViewType } from './CollectionView';
+import { SnappingManager } from '../../util/SnappingManager';
 library.add(faFile);
 const _global = (window /* browser */ || global /* node */) as any;
 
@@ -68,7 +69,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
         //Why is this here?
         (window as any).React = React;
         (window as any).ReactDOM = ReactDOM;
-        DragManager.Vals.Instance.StartWindowDrag = this.StartOtherDrag;
+        DragManager.StartWindowDrag = this.StartOtherDrag;
     }
     hack: boolean = false;
     undohack: any = null;
@@ -500,7 +501,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
                 const stack = tab.contentItem.parent;
                 // shifts the focus to this tab when another tab is dragged over it
                 tab.element[0].onmouseenter = (e: any) => {
-                    if (!this._isPointerDown || !DragManager.Vals.Instance.GetIsDragging()) return;
+                    if (!this._isPointerDown || !SnappingManager.GetIsDragging()) return;
                     const activeContentItem = tab.header.parent.getActiveContentItem();
                     if (tab.contentItem !== activeContentItem) {
                         tab.header.parent.setActiveContentItem(tab.contentItem);
diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
index c74cfbcf4..95c7643c9 100644
--- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
+++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
@@ -12,12 +12,12 @@ import { numberRange, setupMoveUpEvents, emptyFunction } from "../../../Utils";
 import { Docs } from "../../documents/Documents";
 import { DragManager } from "../../util/DragManager";
 import { CompileScript } from "../../util/Scripting";
-import { SelectionManager } from "../../util/SelectionManager";
 import { Transform } from "../../util/Transform";
 import { undoBatch } from "../../util/UndoManager";
 import { EditableView } from "../EditableView";
 import { CollectionStackingView } from "./CollectionStackingView";
 import "./CollectionStackingView.scss";
+import { SnappingManager } from "../../util/SnappingManager";
 const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
@@ -132,7 +132,7 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
         this._color = color;
     }
 
-    pointerEnteredRow = action(() => DragManager.Vals.Instance.GetIsDragging() && (this._background = "#b4b4b4"));
+    pointerEnteredRow = action(() => SnappingManager.GetIsDragging() && (this._background = "#b4b4b4"));
 
     @action
     pointerLeaveRow = () => {
diff --git a/src/client/views/collections/CollectionPileView.tsx b/src/client/views/collections/CollectionPileView.tsx
index 0a10c24b3..d3ae21f3a 100644
--- a/src/client/views/collections/CollectionPileView.tsx
+++ b/src/client/views/collections/CollectionPileView.tsx
@@ -12,7 +12,7 @@ import React = require("react");
 import { setupMoveUpEvents, emptyFunction, returnFalse } from "../../../Utils";
 import { SelectionManager } from "../../util/SelectionManager";
 import { UndoManager } from "../../util/UndoManager";
-import { DragManager } from "../../util/DragManager";
+import { SnappingManager } from "../../util/SnappingManager";
 
 @observer
 export class CollectionPileView extends CollectionSubView(doc => doc) {
@@ -79,7 +79,7 @@ export class CollectionPileView extends CollectionSubView(doc => doc) {
     _undoBatch: UndoManager.Batch | undefined;
     pointerDown = (e: React.PointerEvent) => {
         let dist = 0;
-        DragManager.Vals.Instance.SetIsDragging(true);
+        SnappingManager.SetIsDragging(true);
         // this._lastTap should be set to 0, and this._doubleTap should be set to false in the class header
         setupMoveUpEvents(this, e, (e: PointerEvent, down: number[], delta: number[]) => {
             if (this.layoutEngine() === "pass" && this.childDocs.length && this.props.isSelected(true)) {
@@ -99,7 +99,7 @@ export class CollectionPileView extends CollectionSubView(doc => doc) {
         }, () => {
             this._undoBatch?.end();
             this._undoBatch = undefined;
-            DragManager.Vals.Instance.SetIsDragging(false);
+            SnappingManager.SetIsDragging(false);
             if (!this.childDocs.length) {
                 this.props.ContainingCollectionView?.removeDocument(this.props.Document);
             }
diff --git a/src/client/views/collections/CollectionSchemaCells.tsx b/src/client/views/collections/CollectionSchemaCells.tsx
index 0e6489947..5253ee0b9 100644
--- a/src/client/views/collections/CollectionSchemaCells.tsx
+++ b/src/client/views/collections/CollectionSchemaCells.tsx
@@ -18,11 +18,11 @@ import "./CollectionSchemaView.scss";
 import { CollectionView } from "./CollectionView";
 import { NumCast, StrCast, BoolCast, FieldValue, Cast } from "../../../new_fields/Types";
 import { Docs } from "../../documents/Documents";
-import { SelectionManager } from "../../util/SelectionManager";
 import { library } from '@fortawesome/fontawesome-svg-core';
 import { faExpand } from '@fortawesome/free-solid-svg-icons';
 import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField";
 import { undoBatch } from "../../util/UndoManager";
+import { SnappingManager } from "../../util/SnappingManager";
 
 library.add(faExpand);
 
@@ -189,7 +189,7 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
                 this._document[props.fieldKey] instanceof Doc ? "alias" : this.props.Document.schemaDoc ? "copy" : undefined)(e);
         };
         const onPointerEnter = (e: React.PointerEvent): void => {
-            if (e.buttons === 1 && DragManager.Vals.Instance.GetIsDragging() && (type === "document" || type === undefined)) {
+            if (e.buttons === 1 && SnappingManager.GetIsDragging() && (type === "document" || type === undefined)) {
                 dragRef.current!.className = "collectionSchemaView-cellContainer doc-drag-over";
             }
         };
diff --git a/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx b/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx
index f9cd9a628..8636e3eb5 100644
--- a/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx
+++ b/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx
@@ -4,7 +4,6 @@ import "./CollectionSchemaView.scss";
 import { Transform } from "../../util/Transform";
 import { Doc } from "../../../new_fields/Doc";
 import { DragManager, SetupDrag, dropActionType } from "../../util/DragManager";
-import { SelectionManager } from "../../util/SelectionManager";
 import { Cast, FieldValue, StrCast } from "../../../new_fields/Types";
 import { ContextMenu } from "../ContextMenu";
 import { action } from "mobx";
@@ -14,6 +13,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { DocumentManager } from "../../util/DocumentManager";
 import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField";
 import { undoBatch } from "../../util/UndoManager";
+import { SnappingManager } from "../../util/SnappingManager";
 
 library.add(faGripVertical, faTrash);
 
@@ -32,7 +32,7 @@ export class MovableColumn extends React.Component<MovableColumnProps> {
     private _dragRef: React.RefObject<HTMLDivElement> = React.createRef();
 
     onPointerEnter = (e: React.PointerEvent): void => {
-        if (e.buttons === 1 && DragManager.Vals.Instance.GetIsDragging()) {
+        if (e.buttons === 1 && SnappingManager.GetIsDragging()) {
             this._header!.current!.className = "collectionSchema-col-wrapper";
             document.addEventListener("pointermove", this.onDragMove, true);
         }
@@ -143,7 +143,7 @@ export class MovableRow extends React.Component<MovableRowProps> {
     private _rowDropDisposer?: DragManager.DragDropDisposer;
 
     onPointerEnter = (e: React.PointerEvent): void => {
-        if (e.buttons === 1 && DragManager.Vals.Instance.GetIsDragging()) {
+        if (e.buttons === 1 && SnappingManager.GetIsDragging()) {
             this._header!.current!.className = "collectionSchema-row-wrapper";
             document.addEventListener("pointermove", this.onDragMove, true);
         }
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index f6cdebc9b..b6faea845 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -14,7 +14,6 @@ import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from "../../../new_field
 import { TraceMobx } from "../../../new_fields/util";
 import { emptyFunction, returnFalse, returnOne, returnZero, setupMoveUpEvents, Utils } from "../../../Utils";
 import { DragManager, dropActionType } from "../../util/DragManager";
-import { SelectionManager } from "../../util/SelectionManager";
 import { Transform } from "../../util/Transform";
 import { undoBatch } from "../../util/UndoManager";
 import { ContextMenu } from "../ContextMenu";
@@ -26,7 +25,7 @@ import "./CollectionStackingView.scss";
 import { CollectionStackingViewFieldColumn } from "./CollectionStackingViewFieldColumn";
 import { CollectionSubView } from "./CollectionSubView";
 import { CollectionViewType } from "./CollectionView";
-import { ScriptField } from "../../../new_fields/ScriptField";
+import { SnappingManager } from "../../util/SnappingManager";
 const _global = (window /* browser */ || global /* node */) as any;
 
 type StackingDocument = makeInterface<[typeof collectionSchema, typeof documentSchema]>;
@@ -312,7 +311,7 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument)
                     this.refList.push(ref);
                     const doc = this.props.DataDoc && this.props.DataDoc.layout === this.layoutDoc ? this.props.DataDoc : this.layoutDoc;
                     this.observer = new _global.ResizeObserver(action((entries: any) => {
-                        if (this.props.Document._autoHeight && ref && this.refList.length && !DragManager.Vals.Instance.GetIsDragging()) {
+                        if (this.props.Document._autoHeight && ref && this.refList.length && !SnappingManager.GetIsDragging()) {
                             Doc.Layout(doc)._height = Math.min(1200, Math.max(...this.refList.map(r => Number(getComputedStyle(r).height.replace("px", "")))));
                         }
                     }));
@@ -359,7 +358,7 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument)
                     this.refList.push(ref);
                     const doc = this.props.DataDoc && this.props.DataDoc.layout === this.layoutDoc ? this.props.DataDoc : this.layoutDoc;
                     this.observer = new _global.ResizeObserver(action((entries: any) => {
-                        if (this.props.Document._autoHeight && ref && this.refList.length && !DragManager.Vals.Instance.GetIsDragging()) {
+                        if (this.props.Document._autoHeight && ref && this.refList.length && !SnappingManager.GetIsDragging()) {
                             Doc.Layout(doc)._height = this.refList.reduce((p, r) => p + Number(getComputedStyle(r).height.replace("px", "")), 0);
                         }
                     }));
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index 1d16a5478..dccef7983 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -13,7 +13,6 @@ import { ImageField } from "../../../new_fields/URLField";
 import { TraceMobx } from "../../../new_fields/util";
 import { Docs, DocUtils } from "../../documents/Documents";
 import { DragManager } from "../../util/DragManager";
-import { SelectionManager } from "../../util/SelectionManager";
 import { Transform } from "../../util/Transform";
 import { undoBatch } from "../../util/UndoManager";
 import { ContextMenu } from "../ContextMenu";
@@ -23,6 +22,7 @@ import { CollectionStackingView } from "./CollectionStackingView";
 import { setupMoveUpEvents, emptyFunction } from "../../../Utils";
 import "./CollectionStackingView.scss";
 import { listSpec } from "../../../new_fields/Schema";
+import { SnappingManager } from "../../util/SnappingManager";
 const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
@@ -120,7 +120,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
 
     @action
     pointerEntered = () => {
-        if (DragManager.Vals.Instance.GetIsDragging()) {
+        if (SnappingManager.GetIsDragging()) {
             this._background = "#b4b4b4";
         }
     }
@@ -355,7 +355,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
             <div className="collectionStackingViewFieldColumn" key={heading}
                 style={{
                     width: `${100 / ((uniqueHeadings.length + ((chromeStatus !== 'view-mode' && chromeStatus !== 'disabled') ? 1 : 0)) || 1)}%`,
-                    height: undefined, // DragManager.Vals.Instance.GetIsDragging() ? "100%" : undefined,
+                    height: undefined, // DraggingManager.GetIsDragging() ? "100%" : undefined,
                     background: this._background
                 }}
                 ref={this.createColumnDropRef} onPointerEnter={this.pointerEntered} onPointerLeave={this.pointerLeave}>
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 288fa8794..f2b0e3155 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -6,6 +6,7 @@ import { observer } from "mobx-react";
 import { DataSym, Doc, DocListCast, Field, HeightSym, Opt, WidthSym } from '../../../new_fields/Doc';
 import { Id } from '../../../new_fields/FieldSymbols';
 import { List } from '../../../new_fields/List';
+import { PrefetchProxy } from '../../../new_fields/Proxy';
 import { Document, listSpec } from '../../../new_fields/Schema';
 import { ComputedField, ScriptField } from '../../../new_fields/ScriptField';
 import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../new_fields/Types';
@@ -13,6 +14,7 @@ import { emptyFunction, emptyPath, returnFalse, returnOne, returnTrue, returnZer
 import { Docs, DocUtils } from '../../documents/Documents';
 import { DocumentType } from "../../documents/DocumentTypes";
 import { DocumentManager } from '../../util/DocumentManager';
+import { SnappingManager } from '../../util/SnappingManager';
 import { DragManager, dropActionType, SetupDrag } from "../../util/DragManager";
 import { Scripting } from '../../util/Scripting';
 import { SelectionManager } from '../../util/SelectionManager';
@@ -31,7 +33,6 @@ import { CollectionSubView } from "./CollectionSubView";
 import "./CollectionTreeView.scss";
 import { CollectionViewType } from './CollectionView';
 import React = require("react");
-import { PrefetchProxy } from '../../../new_fields/Proxy';
 
 
 export interface TreeViewProps {
@@ -147,7 +148,7 @@ class TreeView extends React.Component<TreeViewProps> {
 
     onPointerEnter = (e: React.PointerEvent): void => {
         this.props.active(true) && Doc.BrushDoc(this.dataDoc);
-        if (e.buttons === 1 && DragManager.Vals.Instance.GetIsDragging()) {
+        if (e.buttons === 1 && SnappingManager.GetIsDragging()) {
             this._header!.current!.className = "treeViewItem-header";
             document.addEventListener("pointermove", this.onDragMove, true);
         }
@@ -451,7 +452,7 @@ class TreeView extends React.Component<TreeViewProps> {
                     fontWeight: this.props.document.searchMatch ? "bold" : undefined,
                     textDecoration: Doc.GetT(this.props.document, "title", "string", true) ? "underline" : undefined,
                     outline: BoolCast(this.props.document.workspaceBrush) ? "dashed 1px #06123232" : undefined,
-                    pointerEvents: this.props.active() || DragManager.Vals.Instance.GetIsDragging() ? undefined : "none"
+                    pointerEvents: this.props.active() || SnappingManager.GetIsDragging() ? undefined : "none"
                 }} >
                 {Doc.GetT(this.props.document, "editTitle", "boolean", true) ?
                     this.editableView("title") :
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
index 0c9a1aa9a..695898ca9 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
@@ -9,7 +9,7 @@ import { DocumentType } from "../../../documents/DocumentTypes";
 import { observable, action, reaction, IReactionDisposer } from "mobx";
 import { StrCast } from "../../../../new_fields/Types";
 import { Id } from "../../../../new_fields/FieldSymbols";
-import { DragManager } from "../../../util/DragManager";
+import { SnappingManager } from "../../../util/SnappingManager";
 
 export interface CollectionFreeFormLinkViewProps {
     A: DocumentView;
@@ -25,7 +25,7 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
     componentDidMount() {
         this._anchorDisposer = reaction(() => [this.props.A.props.ScreenToLocalTransform(), this.props.B.props.ScreenToLocalTransform(), this.props.A.isSelected() || Doc.IsBrushed(this.props.A.props.Document), this.props.A.isSelected() || Doc.IsBrushed(this.props.A.props.Document)],
             action(() => {
-                if (DragManager.Vals.Instance.GetIsDragging()) return;
+                if (SnappingManager.GetIsDragging()) return;
                 setTimeout(action(() => this._opacity = 1), 0); // since the render code depends on querying the Dom through getBoudndingClientRect, we need to delay triggering render()
                 setTimeout(action(() => (!this.props.LinkDocs.length || !this.props.LinkDocs[0].linkDisplay) && (this._opacity = 0.05)), 750); // this will unhighlight the link line.
                 const acont = this.props.A.props.Document.type === DocumentType.LINK ? this.props.A.ContentDiv!.getElementsByClassName("linkAnchorBox-cont") : [];
@@ -83,7 +83,7 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
     }
 
     render() {
-        if (DragManager.Vals.Instance.GetIsDragging()) return null;
+        if (SnappingManager.GetIsDragging()) return null;
         this.props.A.props.ScreenToLocalTransform().transform(this.props.B.props.ScreenToLocalTransform());
         const acont = this.props.A.props.Document.type === DocumentType.LINK ? this.props.A.ContentDiv!.getElementsByClassName("linkAnchorBox-cont") : [];
         const bcont = this.props.B.props.Document.type === DocumentType.LINK ? this.props.B.ContentDiv!.getElementsByClassName("linkAnchorBox-cont") : [];
@@ -107,8 +107,9 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
         const aActive = this.props.A.isSelected() || Doc.IsBrushed(this.props.A.props.Document);
         const bActive = this.props.A.isSelected() || Doc.IsBrushed(this.props.A.props.Document);
         const text = StrCast(this.props.A.props.Document.linkRelationship);
+
         return !a.width || !b.width || ((!this.props.LinkDocs.length || !this.props.LinkDocs[0].linkDisplay) && !aActive && !bActive) ? (null) : (<>
-            <text x={(pt1[0] + pt2[0]) / 2} y={(pt1[1] + pt2[1]) / 2}>
+            <text x={(Math.min(pt1[0], pt2[0]) * 2 + Math.max(pt1[0], pt2[0])) / 3} y={(pt1[1] + pt2[1]) / 2}>
                 {text !== "-ungrouped-" ? text : ""}
             </text>
             <path className="collectionfreeformlinkview-linkLine" style={{ opacity: this._opacity, strokeDasharray: "2 2" }}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
index 6c6dbf653..5e5ad7db5 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
@@ -9,7 +9,7 @@ import { CollectionFreeFormLinkView } from "./CollectionFreeFormLinkView";
 import React = require("react");
 import { Utils, emptyFunction } from "../../../../Utils";
 import { DocumentType } from "../../../documents/DocumentTypes";
-import { DragManager } from "../../../util/DragManager";
+import { SnappingManager } from "../../../util/SnappingManager";
 
 @observer
 export class CollectionFreeFormLinksView extends React.Component {
@@ -36,7 +36,7 @@ export class CollectionFreeFormLinksView extends React.Component {
     }
 
     render() {
-        return DragManager.Vals.Instance.GetIsDragging() ? (null) : <div className="collectionfreeformlinksview-container">
+        return SnappingManager.GetIsDragging() ? (null) : <div className="collectionfreeformlinksview-container">
             <svg className="collectionfreeformlinksview-svgCanvas">
                 {this.uniqueConnections}
             </svg>
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 2aa9b1f5f..37957b0f6 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -45,6 +45,7 @@ import { MarqueeView } from "./MarqueeView";
 import React = require("react");
 import { CollectionViewType } from "../CollectionView";
 import { Timeline } from "../../animationtimeline/Timeline";
+import { SnappingManager } from "../../../util/SnappingManager";
 
 library.add(faEye as any, faTable, faPaintBrush, faExpandArrowsAlt, faCompressArrowsAlt, faCompass, faUpload, faBraille, faChalkboard, faFileUpload);
 
@@ -1174,7 +1175,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         DragManager.SetSnapLines(horizLines, vertLines);
     }
     onPointerOver = (e: React.PointerEvent) => {
-        if (DragManager.Vals.Instance.GetIsDragging()) {
+        if (SnappingManager.GetIsDragging()) {
             this.setupDragLines();
         }
         e.stopPropagation();
@@ -1233,7 +1234,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         const wscale = nw ? this.props.PanelWidth() / nw : 1;
         return wscale < hscale ? wscale : hscale;
     }
-    @computed get backgroundEvents() { return this.layoutDoc.isBackground && DragManager.Vals.Instance.GetIsDragging(); }
+    @computed get backgroundEvents() { return this.layoutDoc.isBackground && SnappingManager.GetIsDragging(); }
     render() {
         TraceMobx();
         const clientRect = this._mainCont?.getBoundingClientRect();
@@ -1247,7 +1248,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         return <div className={"collectionfreeformview-container"}
             ref={this.createDashEventsTarget}
             onPointerOver={this.onPointerOver}
-            onWheel={this.onPointerWheel} onClick={this.onClick}  //pointerEvents: DragManager.Vals.Instance.GetIsDragging() ? "all" : undefined,
+            onWheel={this.onPointerWheel} onClick={this.onClick}  //pointerEvents: DraggingManager.GetIsDragging() ? "all" : undefined,
             onPointerDown={this.onPointerDown} onPointerMove={this.onCursorMove} onDrop={this.onExternalDrop.bind(this)} onContextMenu={this.onContextMenu}
             style={{
                 pointerEvents: this.backgroundEvents ? "all" : undefined,
diff --git a/src/client/views/nodes/DocHolderBox.scss b/src/client/views/nodes/DocHolderBox.scss
new file mode 100644
index 000000000..3a27c16c1
--- /dev/null
+++ b/src/client/views/nodes/DocHolderBox.scss
@@ -0,0 +1,15 @@
+.documentBox-container {
+    width: 100%;
+    height: 100%;
+    pointer-events: all;
+    background: rgb(241, 239, 235);
+    position: absolute;
+    .documentBox-lock {
+        margin: auto;
+        color: white;
+        position: absolute;
+    }
+    .contentFittingDocumentView {
+        position: absolute;
+    }
+}
\ No newline at end of file
diff --git a/src/client/views/nodes/DocHolderBox.tsx b/src/client/views/nodes/DocHolderBox.tsx
new file mode 100644
index 000000000..9787877cc
--- /dev/null
+++ b/src/client/views/nodes/DocHolderBox.tsx
@@ -0,0 +1,207 @@
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { action, IReactionDisposer, reaction } from "mobx";
+import { observer } from "mobx-react";
+import { Doc, Field } from "../../../new_fields/Doc";
+import { collectionSchema, documentSchema } from "../../../new_fields/documentSchemas";
+import { makeInterface } from "../../../new_fields/Schema";
+import { ComputedField } from "../../../new_fields/ScriptField";
+import { Cast, NumCast, StrCast } from "../../../new_fields/Types";
+import { TraceMobx } from "../../../new_fields/util";
+import { emptyPath, returnFalse, returnOne, returnZero } from "../../../Utils";
+import { DocumentType } from "../../documents/DocumentTypes";
+import { DragManager } from "../../util/DragManager";
+import { undoBatch } from "../../util/UndoManager";
+import { ContextMenu } from "../ContextMenu";
+import { ContextMenuProps } from "../ContextMenuItem";
+import { ViewBoxAnnotatableComponent } from "../DocComponent";
+import { ContentFittingDocumentView } from "./ContentFittingDocumentView";
+import "./DocHolderBox.scss";
+import { DocumentView } from "./DocumentView";
+import { FieldView, FieldViewProps } from "./FieldView";
+import React = require("react");
+
+type DocHolderBoxSchema = makeInterface<[typeof documentSchema, typeof collectionSchema]>;
+const DocHolderBoxDocument = makeInterface(documentSchema, collectionSchema);
+
+@observer
+export class DocHolderBox extends ViewBoxAnnotatableComponent<FieldViewProps, DocHolderBoxSchema>(DocHolderBoxDocument) {
+    public static LayoutString(fieldKey: string) { return FieldView.LayoutString(DocHolderBox, fieldKey); }
+    _prevSelectionDisposer: IReactionDisposer | undefined;
+    _dropDisposer?: DragManager.DragDropDisposer;
+    _selections: Doc[] = [];
+    _contRef = React.createRef<HTMLDivElement>();
+    _curSelection = -1;
+    componentDidMount() {
+        this._prevSelectionDisposer = reaction(() => this.layoutDoc[this.props.fieldKey], (data) => {
+            if (data instanceof Doc && !this.isSelectionLocked()) {
+                this._selections.indexOf(data) !== -1 && this._selections.splice(this._selections.indexOf(data), 1);
+                this._selections.push(data);
+                this._curSelection = this._selections.length - 1;
+            }
+        });
+    }
+    componentWillUnmount() {
+        this._prevSelectionDisposer?.();
+    }
+    specificContextMenu = (e: React.MouseEvent): void => {
+        const funcs: ContextMenuProps[] = [];
+        funcs.push({ description: (this.isSelectionLocked() ? "Show" : "Lock") + " Selection", event: () => this.toggleLockSelection, icon: "expand-arrows-alt" });
+        funcs.push({ description: (this.layoutDoc.excludeCollections ? "Include" : "Exclude") + " Collections", event: () => this.layoutDoc.excludeCollections = !this.layoutDoc.excludeCollections, icon: "expand-arrows-alt" });
+        funcs.push({ description: `${this.layoutDoc.forceActive ? "Select" : "Force"} Contents Active`, event: () => this.layoutDoc.forceActive = !this.layoutDoc.forceActive, icon: "project-diagram" });
+        funcs.push({ description: `Show ${this.layoutDoc.childLayoutTemplateName !== "keyValue" ? "key values" : "contents"}`, event: () => this.layoutDoc.childLayoutString = this.layoutDoc.childLayoutString ? undefined : "<KeyValueBox {...props} />", icon: "project-diagram" });
+
+        ContextMenu.Instance.addItem({ description: "Options...", subitems: funcs, icon: "asterisk" });
+    }
+    lockSelection = () => {
+        this.layoutDoc[this.props.fieldKey] = this.layoutDoc[this.props.fieldKey];
+    }
+    showSelection = () => {
+        this.layoutDoc[this.props.fieldKey] = ComputedField.MakeFunction(`selectedDocs(self,this.excludeCollections,[_last_])?.[0]`);
+    }
+    isSelectionLocked = () => {
+        const kvpstring = Field.toKeyValueString(this.layoutDoc, this.props.fieldKey);
+        return !kvpstring || kvpstring.includes("DOC");
+    }
+    toggleLockSelection = () => {
+        !this.isSelectionLocked() ? this.lockSelection() : this.showSelection();
+        return true;
+    }
+    prevSelection = () => {
+        this.lockSelection();
+        if (this._curSelection > 0) {
+            this.layoutDoc[this.props.fieldKey] = this._selections[--this._curSelection];
+            return true;
+        }
+    }
+    nextSelection = () => {
+        if (this._curSelection < this._selections.length - 1 && this._selections.length) {
+            this.layoutDoc[this.props.fieldKey] = this._selections[++this._curSelection];
+            return true;
+        }
+    }
+    onPointerDown = (e: React.PointerEvent) => {
+        if (this.active() && e.button === 0 && !e.ctrlKey) {
+            e.stopPropagation();
+        }
+    }
+    onLockClick = (e: React.MouseEvent) => {
+        this.toggleLockSelection();
+        (e.nativeEvent as any).formattedHandled = true;
+        e.stopPropagation();
+    }
+    get xPad() { return NumCast(this.props.Document._xPadding); }
+    get yPad() { return NumCast(this.props.Document._yPadding); }
+    onClick = (e: React.MouseEvent) => {
+        let hitWidget: boolean | undefined = false;
+        if (this._contRef.current!.getBoundingClientRect().top + this.yPad > e.clientY) hitWidget = (() => { this.props.select(false); return true; })();
+        else if (this._contRef.current!.getBoundingClientRect().bottom - this.yPad < e.clientY) hitWidget = (() => { this.props.select(false); return true; })();
+        else {
+            if (this._contRef.current!.getBoundingClientRect().left + this.xPad > e.clientX) hitWidget = this.prevSelection();
+            if (this._contRef.current!.getBoundingClientRect().right - this.xPad < e.clientX) hitWidget = this.nextSelection();
+        }
+        if (hitWidget) {
+            (e.nativeEvent as any).formattedHandled = true;
+            e.stopPropagation();
+        }
+    }
+    pwidth = () => this.props.PanelWidth() - 2 * this.xPad;
+    pheight = () => this.props.PanelHeight() - 2 * this.yPad;
+    getTransform = () => this.props.ScreenToLocalTransform().translate(-this.xPad, -this.yPad);
+    isActive = () => this.active() || !this.props.renderDepth;
+    layoutTemplateDoc = () => Cast(this.props.Document.childLayoutTemplate, Doc, null);
+    get renderContents() {
+        const containedDoc = Cast(this.dataDoc[this.props.fieldKey], Doc, null);
+        const layoutTemplate = StrCast(this.layoutDoc.childLayoutString);
+        const contents = !(containedDoc instanceof Doc) ? (null) : this.layoutDoc.childLayoutString || this.layoutTemplateDoc() ?
+            <DocumentView
+                Document={containedDoc}
+                DataDoc={undefined}
+                LibraryPath={emptyPath}
+                ContainingCollectionView={this as any} // bcz: hack!  need to pass a prop that can be used to select the container (ie, 'this') when the up selector in document decorations is clicked.  currently, the up selector allows only a containing collection to be selected
+                ContainingCollectionDoc={undefined}
+                fitToBox={true}
+                LayoutTemplateString={layoutTemplate}
+                LayoutTemplate={this.layoutTemplateDoc}
+                rootSelected={this.props.isSelected}
+                addDocument={this.props.addDocument}
+                moveDocument={this.props.moveDocument}
+                removeDocument={this.props.removeDocument}
+                addDocTab={this.props.addDocTab}
+                pinToPres={this.props.pinToPres}
+                ScreenToLocalTransform={this.getTransform}
+                renderDepth={this.props.renderDepth + 1}
+                NativeHeight={returnZero}
+                NativeWidth={returnZero}
+                PanelWidth={this.pwidth}
+                PanelHeight={this.pheight}
+                focus={this.props.focus}
+                parentActive={this.isActive}
+                dontRegisterView={true}
+                whenActiveChanged={this.props.whenActiveChanged}
+                bringToFront={returnFalse}
+                ContentScaling={returnOne} /> :
+            <ContentFittingDocumentView
+                Document={containedDoc}
+                DataDoc={undefined}
+                LibraryPath={emptyPath}
+                ContainingCollectionView={this as any} // bcz: hack!  need to pass a prop that can be used to select the container (ie, 'this') when the up selector in document decorations is clicked.  currently, the up selector allows only a containing collection to be selected
+                ContainingCollectionDoc={undefined}
+                fitToBox={true}
+                LayoutTemplateString={layoutTemplate}
+                LayoutTemplate={this.layoutTemplateDoc}
+                rootSelected={this.props.isSelected}
+                addDocument={this.props.addDocument}
+                moveDocument={this.props.moveDocument}
+                removeDocument={this.props.removeDocument}
+                addDocTab={this.props.addDocTab}
+                pinToPres={this.props.pinToPres}
+                ScreenToLocalTransform={this.getTransform}
+                renderDepth={this.props.renderDepth + 1}
+                NativeHeight={returnZero}
+                NativeWidth={returnZero}
+                PanelWidth={this.pwidth}
+                PanelHeight={this.pheight}
+                focus={this.props.focus}
+                parentActive={this.isActive}
+                dontRegisterView={true}
+                whenActiveChanged={this.props.whenActiveChanged}
+                bringToFront={returnFalse}
+                ContentScaling={returnOne}
+            />;
+        return contents;
+    }
+    render() {
+        TraceMobx();
+        return <div className="documentBox-container" ref={this._contRef}
+            onContextMenu={this.specificContextMenu}
+            onPointerDown={this.onPointerDown} onClick={this.onClick}
+            style={{
+                background: StrCast(this.layoutDoc.backgroundColor),
+                border: `#00000021 solid ${this.xPad}px`,
+                borderTop: `#0000005e solid ${this.yPad}px`,
+                borderBottom: `#0000005e solid ${this.yPad}px`,
+            }}>
+            {this.renderContents}
+            <div className="documentBox-lock" onClick={this.onLockClick} ref={this.createDropTarget}
+                style={{ marginTop: - this.yPad }}>
+                <FontAwesomeIcon icon={this.isSelectionLocked() ? "lock" : "unlock"} size="sm" />
+            </div>
+        </div >;
+    }
+
+    @undoBatch
+    @action
+    drop = (e: Event, de: DragManager.DropEvent) => {
+        if (de.complete.docDragData) {
+            if (de.complete.docDragData.draggedDocuments[0].type === DocumentType.FONTICON) {
+                const doc = Cast(de.complete.docDragData.draggedDocuments[0].dragFactory, Doc, null);
+                this.props.Document.childLayoutTemplate = doc;
+            }
+        }
+    }
+    protected createDropTarget = (ele: HTMLDivElement) => {
+        this._dropDisposer?.();
+        ele && (this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this), this.props.Document));
+    }
+
+}
diff --git a/src/client/views/nodes/DocumentBox.scss b/src/client/views/nodes/DocumentBox.scss
deleted file mode 100644
index 3a27c16c1..000000000
--- a/src/client/views/nodes/DocumentBox.scss
+++ /dev/null
@@ -1,15 +0,0 @@
-.documentBox-container {
-    width: 100%;
-    height: 100%;
-    pointer-events: all;
-    background: rgb(241, 239, 235);
-    position: absolute;
-    .documentBox-lock {
-        margin: auto;
-        color: white;
-        position: absolute;
-    }
-    .contentFittingDocumentView {
-        position: absolute;
-    }
-}
\ No newline at end of file
diff --git a/src/client/views/nodes/DocumentBox.tsx b/src/client/views/nodes/DocumentBox.tsx
deleted file mode 100644
index b53c7cfe6..000000000
--- a/src/client/views/nodes/DocumentBox.tsx
+++ /dev/null
@@ -1,207 +0,0 @@
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action, IReactionDisposer, reaction } from "mobx";
-import { observer } from "mobx-react";
-import { Doc, Field } from "../../../new_fields/Doc";
-import { collectionSchema, documentSchema } from "../../../new_fields/documentSchemas";
-import { makeInterface } from "../../../new_fields/Schema";
-import { ComputedField } from "../../../new_fields/ScriptField";
-import { Cast, NumCast, StrCast } from "../../../new_fields/Types";
-import { TraceMobx } from "../../../new_fields/util";
-import { emptyPath, returnFalse, returnOne, returnZero } from "../../../Utils";
-import { DocumentType } from "../../documents/DocumentTypes";
-import { DragManager } from "../../util/DragManager";
-import { undoBatch } from "../../util/UndoManager";
-import { ContextMenu } from "../ContextMenu";
-import { ContextMenuProps } from "../ContextMenuItem";
-import { ViewBoxAnnotatableComponent } from "../DocComponent";
-import { ContentFittingDocumentView } from "./ContentFittingDocumentView";
-import "./DocumentBox.scss";
-import { DocumentView } from "./DocumentView";
-import { FieldView, FieldViewProps } from "./FieldView";
-import React = require("react");
-
-type DocHolderBoxSchema = makeInterface<[typeof documentSchema, typeof collectionSchema]>;
-const DocHolderBoxDocument = makeInterface(documentSchema, collectionSchema);
-
-@observer
-export class DocHolderBox extends ViewBoxAnnotatableComponent<FieldViewProps, DocHolderBoxSchema>(DocHolderBoxDocument) {
-    public static LayoutString(fieldKey: string) { return FieldView.LayoutString(DocHolderBox, fieldKey); }
-    _prevSelectionDisposer: IReactionDisposer | undefined;
-    _dropDisposer?: DragManager.DragDropDisposer;
-    _selections: Doc[] = [];
-    _contRef = React.createRef<HTMLDivElement>();
-    _curSelection = -1;
-    componentDidMount() {
-        this._prevSelectionDisposer = reaction(() => this.layoutDoc[this.props.fieldKey], (data) => {
-            if (data instanceof Doc && !this.isSelectionLocked()) {
-                this._selections.indexOf(data) !== -1 && this._selections.splice(this._selections.indexOf(data), 1);
-                this._selections.push(data);
-                this._curSelection = this._selections.length - 1;
-            }
-        });
-    }
-    componentWillUnmount() {
-        this._prevSelectionDisposer?.();
-    }
-    specificContextMenu = (e: React.MouseEvent): void => {
-        const funcs: ContextMenuProps[] = [];
-        funcs.push({ description: (this.isSelectionLocked() ? "Show" : "Lock") + " Selection", event: () => this.toggleLockSelection, icon: "expand-arrows-alt" });
-        funcs.push({ description: (this.layoutDoc.excludeCollections ? "Include" : "Exclude") + " Collections", event: () => this.layoutDoc.excludeCollections = !this.layoutDoc.excludeCollections, icon: "expand-arrows-alt" });
-        funcs.push({ description: `${this.layoutDoc.forceActive ? "Select" : "Force"} Contents Active`, event: () => this.layoutDoc.forceActive = !this.layoutDoc.forceActive, icon: "project-diagram" });
-        funcs.push({ description: `Show ${this.layoutDoc.childLayoutTemplateName !== "keyValue" ? "key values" : "contents"}`, event: () => this.layoutDoc.childLayoutString = this.layoutDoc.childLayoutString ? undefined : "<KeyValueBox {...props} />", icon: "project-diagram" });
-
-        ContextMenu.Instance.addItem({ description: "Options...", subitems: funcs, icon: "asterisk" });
-    }
-    lockSelection = () => {
-        this.layoutDoc[this.props.fieldKey] = this.layoutDoc[this.props.fieldKey];
-    }
-    showSelection = () => {
-        this.layoutDoc[this.props.fieldKey] = ComputedField.MakeFunction(`selectedDocs(self,this.excludeCollections,[_last_])?.[0]`);
-    }
-    isSelectionLocked = () => {
-        const kvpstring = Field.toKeyValueString(this.layoutDoc, this.props.fieldKey);
-        return !kvpstring || kvpstring.includes("DOC");
-    }
-    toggleLockSelection = () => {
-        !this.isSelectionLocked() ? this.lockSelection() : this.showSelection();
-        return true;
-    }
-    prevSelection = () => {
-        this.lockSelection();
-        if (this._curSelection > 0) {
-            this.layoutDoc[this.props.fieldKey] = this._selections[--this._curSelection];
-            return true;
-        }
-    }
-    nextSelection = () => {
-        if (this._curSelection < this._selections.length - 1 && this._selections.length) {
-            this.layoutDoc[this.props.fieldKey] = this._selections[++this._curSelection];
-            return true;
-        }
-    }
-    onPointerDown = (e: React.PointerEvent) => {
-        if (this.active() && e.button === 0 && !e.ctrlKey) {
-            e.stopPropagation();
-        }
-    }
-    onLockClick = (e: React.MouseEvent) => {
-        this.toggleLockSelection();
-        (e.nativeEvent as any).formattedHandled = true;
-        e.stopPropagation();
-    }
-    get xPad() { return NumCast(this.props.Document._xPadding); }
-    get yPad() { return NumCast(this.props.Document._yPadding); }
-    onClick = (e: React.MouseEvent) => {
-        let hitWidget: boolean | undefined = false;
-        if (this._contRef.current!.getBoundingClientRect().top + this.yPad > e.clientY) hitWidget = (() => { this.props.select(false); return true; })();
-        else if (this._contRef.current!.getBoundingClientRect().bottom - this.yPad < e.clientY) hitWidget = (() => { this.props.select(false); return true; })();
-        else {
-            if (this._contRef.current!.getBoundingClientRect().left + this.xPad > e.clientX) hitWidget = this.prevSelection();
-            if (this._contRef.current!.getBoundingClientRect().right - this.xPad < e.clientX) hitWidget = this.nextSelection();
-        }
-        if (hitWidget) {
-            (e.nativeEvent as any).formattedHandled = true;
-            e.stopPropagation();
-        }
-    }
-    pwidth = () => this.props.PanelWidth() - 2 * this.xPad;
-    pheight = () => this.props.PanelHeight() - 2 * this.yPad;
-    getTransform = () => this.props.ScreenToLocalTransform().translate(-this.xPad, -this.yPad);
-    isActive = () => this.active() || !this.props.renderDepth;
-    layoutTemplateDoc = () => Cast(this.props.Document.childLayoutTemplate, Doc, null);
-    get renderContents() {
-        const containedDoc = Cast(this.dataDoc[this.props.fieldKey], Doc, null);
-        const layoutTemplate = StrCast(this.layoutDoc.childLayoutString);
-        const contents = !(containedDoc instanceof Doc) ? (null) : this.layoutDoc.childLayoutString || this.layoutTemplateDoc() ?
-            <DocumentView
-                Document={containedDoc}
-                DataDoc={undefined}
-                LibraryPath={emptyPath}
-                ContainingCollectionView={this as any} // bcz: hack!  need to pass a prop that can be used to select the container (ie, 'this') when the up selector in document decorations is clicked.  currently, the up selector allows only a containing collection to be selected
-                ContainingCollectionDoc={undefined}
-                fitToBox={true}
-                LayoutTemplateString={layoutTemplate}
-                LayoutTemplate={this.layoutTemplateDoc}
-                rootSelected={this.props.isSelected}
-                addDocument={this.props.addDocument}
-                moveDocument={this.props.moveDocument}
-                removeDocument={this.props.removeDocument}
-                addDocTab={this.props.addDocTab}
-                pinToPres={this.props.pinToPres}
-                ScreenToLocalTransform={this.getTransform}
-                renderDepth={this.props.renderDepth + 1}
-                NativeHeight={returnZero}
-                NativeWidth={returnZero}
-                PanelWidth={this.pwidth}
-                PanelHeight={this.pheight}
-                focus={this.props.focus}
-                parentActive={this.isActive}
-                dontRegisterView={true}
-                whenActiveChanged={this.props.whenActiveChanged}
-                bringToFront={returnFalse}
-                ContentScaling={returnOne} /> :
-            <ContentFittingDocumentView
-                Document={containedDoc}
-                DataDoc={undefined}
-                LibraryPath={emptyPath}
-                ContainingCollectionView={this as any} // bcz: hack!  need to pass a prop that can be used to select the container (ie, 'this') when the up selector in document decorations is clicked.  currently, the up selector allows only a containing collection to be selected
-                ContainingCollectionDoc={undefined}
-                fitToBox={true}
-                LayoutTemplateString={layoutTemplate}
-                LayoutTemplate={this.layoutTemplateDoc}
-                rootSelected={this.props.isSelected}
-                addDocument={this.props.addDocument}
-                moveDocument={this.props.moveDocument}
-                removeDocument={this.props.removeDocument}
-                addDocTab={this.props.addDocTab}
-                pinToPres={this.props.pinToPres}
-                ScreenToLocalTransform={this.getTransform}
-                renderDepth={this.props.renderDepth + 1}
-                NativeHeight={returnZero}
-                NativeWidth={returnZero}
-                PanelWidth={this.pwidth}
-                PanelHeight={this.pheight}
-                focus={this.props.focus}
-                parentActive={this.isActive}
-                dontRegisterView={true}
-                whenActiveChanged={this.props.whenActiveChanged}
-                bringToFront={returnFalse}
-                ContentScaling={returnOne}
-            />;
-        return contents;
-    }
-    render() {
-        TraceMobx();
-        return <div className="documentBox-container" ref={this._contRef}
-            onContextMenu={this.specificContextMenu}
-            onPointerDown={this.onPointerDown} onClick={this.onClick}
-            style={{
-                background: StrCast(this.layoutDoc.backgroundColor),
-                border: `#00000021 solid ${this.xPad}px`,
-                borderTop: `#0000005e solid ${this.yPad}px`,
-                borderBottom: `#0000005e solid ${this.yPad}px`,
-            }}>
-            {this.renderContents}
-            <div className="documentBox-lock" onClick={this.onLockClick} ref={this.createDropTarget}
-                style={{ marginTop: - this.yPad }}>
-                <FontAwesomeIcon icon={this.isSelectionLocked() ? "lock" : "unlock"} size="sm" />
-            </div>
-        </div >;
-    }
-
-    @undoBatch
-    @action
-    drop = (e: Event, de: DragManager.DropEvent) => {
-        if (de.complete.docDragData) {
-            if (de.complete.docDragData.draggedDocuments[0].type === DocumentType.FONTICON) {
-                const doc = Cast(de.complete.docDragData.draggedDocuments[0].dragFactory, Doc, null);
-                this.props.Document.childLayoutTemplate = doc;
-            }
-        }
-    }
-    protected createDropTarget = (ele: HTMLDivElement) => {
-        this._dropDisposer?.();
-        ele && (this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this), this.props.Document));
-    }
-
-}
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 81667e549..395a6e0b2 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -14,7 +14,7 @@ import { LabelBox } from "./LabelBox";
 import { SliderBox } from "./SliderBox";
 import { LinkBox } from "./LinkBox";
 import { ScriptingBox } from "./ScriptingBox";
-import { DocHolderBox } from "./DocumentBox";
+import { DocHolderBox } from "./DocHolderBox";
 import { DocumentViewProps } from "./DocumentView";
 import "./DocumentView.scss";
 import { FontIconBox } from "./FontIconBox";
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 7a81801e1..86e58ca7a 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -23,6 +23,7 @@ import { Docs, DocUtils } from "../../documents/Documents";
 import { DocumentType } from '../../documents/DocumentTypes';
 import { ClientUtils } from '../../util/ClientUtils';
 import { DocumentManager } from "../../util/DocumentManager";
+import { SnappingManager } from '../../util/SnappingManager';
 import { DragManager, dropActionType } from "../../util/DragManager";
 import { InteractionUtils } from '../../util/InteractionUtils';
 import { Scripting } from '../../util/Scripting';
@@ -1105,7 +1106,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     }
     @computed get ignorePointerEvents() {
         return this.props.pointerEvents === false ||
-            (this.Document.isBackground && !this.isSelected() && !DragManager.Vals.Instance.GetIsDragging()) ||
+            (this.Document.isBackground && !this.isSelected() && !SnappingManager.GetIsDragging()) ||
             (this.Document.type === DocumentType.INK && InkingControl.Instance.selectedTool !== InkTool.None);
     }
     @undoBatch
diff --git a/src/client/views/nodes/QueryBox.tsx b/src/client/views/nodes/QueryBox.tsx
index d248b098c..2a21a380d 100644
--- a/src/client/views/nodes/QueryBox.tsx
+++ b/src/client/views/nodes/QueryBox.tsx
@@ -5,13 +5,12 @@ import { documentSchema } from "../../../new_fields/documentSchemas";
 import { Id } from '../../../new_fields/FieldSymbols';
 import { makeInterface, listSpec } from "../../../new_fields/Schema";
 import { StrCast, Cast } from "../../../new_fields/Types";
-import { SelectionManager } from "../../util/SelectionManager";
 import { ViewBoxAnnotatableComponent } from '../DocComponent';
 import { SearchBox } from "../search/SearchBox";
 import { FieldView, FieldViewProps } from './FieldView';
 import "./QueryBox.scss";
 import { List } from "../../../new_fields/List";
-import { DragManager } from "../../util/DragManager";
+import { SnappingManager } from "../../util/SnappingManager";
 
 type QueryDocument = makeInterface<[typeof documentSchema]>;
 const QueryDocument = makeInterface(documentSchema);
@@ -28,7 +27,7 @@ export class QueryBox extends ViewBoxAnnotatableComponent<FieldViewProps, QueryD
     }
 
     render() {
-        const dragging = !DragManager.Vals.Instance.GetIsDragging() ? "" : "-dragging";
+        const dragging = !SnappingManager.GetIsDragging() ? "" : "-dragging";
         return <div className={`queryBox${dragging}`} onWheel={(e) => e.stopPropagation()} >
             <SearchBox
                 id={this.props.Document[Id]}
-- 
cgit v1.2.3-70-g09d2


From 34db0a78d2dc8989313decf8993691f40847b231 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Thu, 7 May 2020 18:13:16 -0400
Subject: fixes for adding/remove documents -- needs to be extended for views
 other than freeform.

---
 .../apis/google_docs/GooglePhotosClientUtils.ts    |  23 +++--
 src/client/views/collections/CollectionSubView.tsx |  26 +++---
 src/client/views/collections/CollectionView.scss   |   2 +-
 src/client/views/collections/CollectionView.tsx    |  54 ++++++-----
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 101 +++++++++++++--------
 .../collections/collectionFreeForm/MarqueeView.tsx |  13 +--
 6 files changed, 123 insertions(+), 96 deletions(-)

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

diff --git a/src/client/apis/google_docs/GooglePhotosClientUtils.ts b/src/client/apis/google_docs/GooglePhotosClientUtils.ts
index ff471853a..1e4c120bc 100644
--- a/src/client/apis/google_docs/GooglePhotosClientUtils.ts
+++ b/src/client/apis/google_docs/GooglePhotosClientUtils.ts
@@ -153,21 +153,20 @@ export namespace GooglePhotos {
             }
             const tagMapping = new Map<string, string>();
             const images = (await DocListCastAsync(collection.data))!.map(Doc.GetProto);
-            images && images.forEach(image => tagMapping.set(image[Id], ContentCategories.NONE));
-            const values = Object.values(ContentCategories);
+            images?.forEach(image => tagMapping.set(image[Id], ContentCategories.NONE));
+            const values = Object.values(ContentCategories).filter(value => value !== ContentCategories.NONE);
             for (const value of values) {
-                if (value === ContentCategories.NONE) {
-                    continue;
-                }
-                for (const id of (await ContentSearch({ included: [value] }))?.mediaItems?.map(({ id }) => id)) {
+                const searched = (await ContentSearch({ included: [value] }))?.mediaItems?.map(({ id }) => id);
+                console.log("Searching " + value);
+                console.log(searched);
+                searched?.forEach(async id => {
                     const image = await Cast(idMapping[id], Doc);
-                    if (!image) {
-                        continue;
+                    if (image) {
+                        const key = image[Id];
+                        const tags = tagMapping.get(key);
+                        !tags?.includes(value) && tagMapping.set(key, tags + delimiter + value);
                     }
-                    const key = image[Id];
-                    const tags = tagMapping.get(key);
-                    !tags?.includes(value) && tagMapping.set(key, tags + delimiter + value);
-                }
+                });
             }
             images?.forEach(image => {
                 const concatenated = tagMapping.get(image[Id])!;
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 8c78e568c..250c9b3b0 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -6,7 +6,7 @@ import { Id } from "../../../new_fields/FieldSymbols";
 import { List } from "../../../new_fields/List";
 import { listSpec } from "../../../new_fields/Schema";
 import { ScriptField } from "../../../new_fields/ScriptField";
-import { Cast } from "../../../new_fields/Types";
+import { Cast, ScriptCast } from "../../../new_fields/Types";
 import { GestureUtils } from "../../../pen-gestures/GestureUtils";
 import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils";
 import { Upload } from "../../../server/SharedMediaTypes";
@@ -208,19 +208,18 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
         @action
         protected onInternalDrop(e: Event, de: DragManager.DropEvent): boolean {
             const docDragData = de.complete.docDragData;
-            (this.props.Document.dropConverter instanceof ScriptField) &&
-                this.props.Document.dropConverter.script.run({ dragData: docDragData }); /// bcz: check this 
+            ScriptCast(this.props.Document.dropConverter)?.script.run({ dragData: docDragData });
             if (docDragData) {
                 let added = false;
                 if (docDragData.dropAction || docDragData.userDropAction) {
-                    added = docDragData.droppedDocuments.reduce((added: boolean, d) => this.props.addDocument(d) || added, false);
+                    added = this.props.addDocument(docDragData.droppedDocuments as any as Doc);
                 } else if (docDragData.moveDocument) {
-                    const movedDocs = docDragData.draggedDocuments;
-                    added = movedDocs.reduce((added: boolean, d, i) =>
-                        docDragData.droppedDocuments[i] !== d ? this.props.addDocument(docDragData.droppedDocuments[i]) :
-                            docDragData.moveDocument?.(d, this.props.Document, this.props.addDocument) || added, false);
+                    const movedDocs = docDragData.droppedDocuments.filter((d, i) => docDragData.draggedDocuments[i] === d);
+                    const addedDocs = docDragData.droppedDocuments.filter((d, i) => docDragData.draggedDocuments[i] !== d);
+                    const res = addedDocs.length ? this.props.addDocument(addedDocs as any as Doc) : true;
+                    added = movedDocs.length ? docDragData.moveDocument(movedDocs as any as Doc, this.props.Document, this.props.addDocument) : res;
                 } else {
-                    added = docDragData.droppedDocuments.reduce((added: boolean, d) => this.props.addDocument(d) || added, false);
+                    added = this.props.addDocument(docDragData.droppedDocuments as any as Doc);
                 }
                 e.stopPropagation();
                 return added;
@@ -389,8 +388,13 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
                         console.log(result);
                         const json = JSON.parse(result as string) as any;
                         addDocument(Docs.Create.TreeDocument(
-                            json["rectangular-puzzle"].crossword.clues[0].clue.map((c: any) =>
-                                Docs.Create.LabelDocument({ title: c["#text"], _width: 120, _height: 20 })
+                            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 }));
                     });
                 }
diff --git a/src/client/views/collections/CollectionView.scss b/src/client/views/collections/CollectionView.scss
index d43dd387a..7877fe155 100644
--- a/src/client/views/collections/CollectionView.scss
+++ b/src/client/views/collections/CollectionView.scss
@@ -70,7 +70,7 @@
     height: 30px;
     position: absolute;
     bottom: 15px;
-    left: 15px;
+    right: 15px;
     border: 2px solid black;
     border-radius: 50%;
     padding: 3px;
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 43da1c44f..ccfc660cc 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -15,9 +15,7 @@ import { ImageField } from '../../../new_fields/URLField';
 import { TraceMobx } from '../../../new_fields/util';
 import { Utils, setupMoveUpEvents, returnFalse, returnZero, emptyPath, emptyFunction, returnOne } from '../../../Utils';
 import { DocumentType } from '../../documents/DocumentTypes';
-import { DocumentManager } from '../../util/DocumentManager';
 import { ImageUtils } from '../../util/Import & Export/ImageUtils';
-import { SelectionManager } from '../../util/SelectionManager';
 import { ContextMenu } from "../ContextMenu";
 import { FieldView, FieldViewProps } from '../nodes/FieldView';
 import { ScriptBox } from '../ScriptBox';
@@ -45,7 +43,6 @@ import { ScriptField, ComputedField } from '../../../new_fields/ScriptField';
 import { InteractionUtils } from '../../util/InteractionUtils';
 import { ObjectField } from '../../../new_fields/ObjectField';
 import CollectionMapView from './CollectionMapView';
-import { Transform } from 'prosemirror-transform';
 import { CollectionPileView } from './CollectionPileView';
 const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
@@ -119,35 +116,32 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
     whenActiveChanged = (isActive: boolean) => this.props.whenActiveChanged(this._isChildActive = isActive);
 
     @action.bound
-    addDocument(doc: Doc): boolean {
-        if (this.props.filterAddDocument?.(doc) === false) {
-            return false;
+    addDocument = (doc: Doc): boolean => {
+        if (doc instanceof Doc) {
+            if (this.props.filterAddDocument?.(doc) === false) {
+                return false;
+            }
         }
-
+        const docs = doc instanceof Doc ? [doc] : doc as any as Doc[];
         const targetDataDoc = this.props.Document[DataSym];
         const docList = DocListCast(targetDataDoc[this.props.fieldKey]);
-        !docList.includes(doc) && (targetDataDoc[this.props.fieldKey] = new List<Doc>([...docList, doc]));  // DocAddToList may write to targetdataDoc's parent ... we don't want this. should really change GetProto to GetDataDoc and test for resolvedDataDoc there
-        // Doc.AddDocToList(targetDataDoc, this.props.fieldKey, doc);
-        targetDataDoc[this.props.fieldKey + "-lastModified"] = new DateField(new Date(Date.now()));
-        doc.context = this.props.Document;
-        Doc.GetProto(doc).lastOpened = new DateField;
+        const added = docs.filter(d => !docList.includes(d));
+        if (added.length) {
+            added.map(doc => doc.context = this.props.Document);
+            targetDataDoc[this.props.fieldKey] = new List<Doc>([...docList, ...added]);
+            targetDataDoc[this.props.fieldKey + "-lastModified"] = new DateField(new Date(Date.now()));
+        }
         return true;
     }
 
     @action.bound
-    removeDocument(doc: Doc): boolean {
+    removeDocument = (doc: any): boolean => {
+        const docs = doc instanceof Doc ? [doc] : doc as Doc[];
         const targetDataDoc = this.props.Document[DataSym];
-        const docView = DocumentManager.Instance.getDocumentView(doc, this.props.ContainingCollectionView);
-        docView && SelectionManager.DeselectDoc(docView);
         const value = DocListCast(targetDataDoc[this.props.fieldKey]);
-        let index = value.reduce((p, v, i) => (v instanceof Doc && v === doc) ? i : p, -1);
-        index = index !== -1 ? index : value.reduce((p, v, i) => (v instanceof Doc && Doc.AreProtosEqual(v, doc)) ? i : p, -1);
-
-        doc.context = undefined;
-        ContextMenu.Instance?.clearItems();
-        if (index !== -1) {
-            value.splice(index, 1);
-            targetDataDoc[this.props.fieldKey] = new List<Doc>(value);
+        const result = value.filter(v => !docs.includes(v));
+        if (result.length !== value.length) {
+            targetDataDoc[this.props.fieldKey] = new List<Doc>(result);
             return true;
         }
         return false;
@@ -158,7 +152,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
     // otherwise, the document being moved must be able to be removed from its container before
     // moving it into the target.  
     @action.bound
-    moveDocument(doc: Doc, targetCollection: Doc | undefined, addDocument: (doc: Doc) => boolean): boolean {
+    moveDocument = (doc: Doc, targetCollection: Doc | undefined, addDocument: (doc: Doc) => boolean): boolean => {
         if (Doc.AreProtosEqual(this.props.Document, targetCollection)) {
             return true;
         }
@@ -166,10 +160,14 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
     }
 
     showIsTagged = () => {
-        const children = DocListCast(this.props.Document[this.props.fieldKey]);
-        const imageProtos = children.filter(doc => Cast(doc.data, ImageField)).map(Doc.GetProto);
-        const allTagged = imageProtos.length > 0 && imageProtos.every(image => image.googlePhotosTags);
-        return !allTagged ? (null) : <img id={"google-tags"} src={"/assets/google_tags.png"} />;
+        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.
+        // const children = DocListCast(this.props.Document[this.props.fieldKey]);
+        // const imageProtos = children.filter(doc => Cast(doc.data, ImageField)).map(Doc.GetProto);
+        // const allTagged = imageProtos.length > 0 && imageProtos.every(image => image.googlePhotosTags);
+        // return !allTagged ? (null) : <img id={"google-tags"} src={"/assets/google_tags.png"} />;
     }
 
     private SubViewHelper = (type: CollectionViewType, renderProps: CollectionRenderProps) => {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 37957b0f6..6011d5f9c 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -124,10 +124,15 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         this.addDocument(newBox);
     }
     private addDocument = (newBox: Doc) => {
-        const added = this.props.addDocument(newBox);
-        added && this.bringToFront(newBox);
-        added && this.updateCluster(newBox);
-        return added;
+        if (newBox instanceof Doc) {
+            const added = this.props.addDocument(newBox);
+            added && this.bringToFront(newBox);
+            added && this.updateCluster(newBox);
+            return added;
+        } else {
+            return this.props.addDocument(newBox);
+            // bcz: deal with clusters
+        }
     }
     private selectDocuments = (docs: Doc[]) => {
         SelectionManager.DeselectAll();
@@ -153,6 +158,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         const xfo = this.getTransformOverlay();
         const [xp, yp] = xf.transformPoint(de.x, de.y);
         const [xpo, ypo] = xfo.transformPoint(de.x, de.y);
+        const zsorted = this.childLayoutPairs.map(pair => pair.layout).slice().sort((doc1, doc2) => NumCast(doc1.zIndex) - NumCast(doc2.zIndex));
         if (super.onInternalDrop(e, de)) {
             if (de.complete.docDragData) {
                 if (de.complete.docDragData.droppedDocuments.length) {
@@ -162,20 +168,25 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
                     const y = (z ? ypo : yp) - de.complete.docDragData.offset[1];
                     const dropX = NumCast(firstDoc.x);
                     const dropY = NumCast(firstDoc.y);
-                    de.complete.docDragData.droppedDocuments.forEach(action((d: Doc) => {
-                        const layoutDoc = Doc.Layout(d);
-                        d.x = x + NumCast(d.x) - dropX;
-                        d.y = y + NumCast(d.y) - dropY;
-                        if (!NumCast(layoutDoc._width)) {
-                            layoutDoc._width = 300;
-                        }
-                        if (!NumCast(layoutDoc._height)) {
-                            const nw = NumCast(layoutDoc._nativeWidth);
-                            const nh = NumCast(layoutDoc._nativeHeight);
-                            layoutDoc._height = nw && nh ? nh / nw * NumCast(layoutDoc._width) : 300;
+                    const droppedDocs = de.complete.docDragData.droppedDocuments;
+                    runInAction(() => {
+                        zsorted.forEach((doc, index) => doc.zIndex = index + 1);
+                        for (let i = 0; i < droppedDocs.length; i++) {
+                            const d = droppedDocs[i];
+                            const layoutDoc = Doc.Layout(d);
+                            d.x = x + NumCast(d.x) - dropX;
+                            d.y = y + NumCast(d.y) - dropY;
+                            if (!NumCast(layoutDoc._width)) {
+                                layoutDoc._width = 300;
+                            }
+                            if (!NumCast(layoutDoc._height)) {
+                                const nw = NumCast(layoutDoc._nativeWidth);
+                                const nh = NumCast(layoutDoc._nativeHeight);
+                                layoutDoc._height = nw && nh ? nh / nw * NumCast(layoutDoc._width) : 300;
+                            }
+                            d.isBackground === undefined && (d.zIndex = zsorted.length + 1 + i);
                         }
-                        d.isBackground === undefined && this.bringToFront(d);
-                    }));
+                    });
 
                     (de.complete.docDragData.droppedDocuments.length === 1 || de.shiftKey) && this.updateClusterDocs(de.complete.docDragData.droppedDocuments);
                 }
@@ -760,20 +771,21 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         }
     }
 
-    bringToFront = (doc: Doc, sendToBack?: boolean) => {
+    bringToFront = action((doc: Doc, sendToBack?: boolean) => {
         if (sendToBack || doc.isBackground) {
             doc.zIndex = 0;
         }
         else {
             const docs = this.childLayoutPairs.map(pair => pair.layout);
-            docs.slice().sort((doc1, doc2) => {
-                if (doc1 === doc) return 1;
-                if (doc2 === doc) return -1;
-                return NumCast(doc1.zIndex) - NumCast(doc2.zIndex);
-            }).forEach((doc, index) => doc.zIndex = index + 1);
-            doc.zIndex = docs.length + 1;
+            docs.slice().sort((doc1, doc2) => NumCast(doc1.zIndex) - NumCast(doc2.zIndex));
+            let zlast = docs.length ? NumCast(docs[docs.length - 1].zIndex) : 1;
+            if (zlast - docs.length > 100) {
+                for (let i = 0; i < docs.length; i++) doc.zIndex = i + 1;
+                zlast = docs.length + 1;
+            }
+            doc.zIndex = zlast + 1;
         }
-    }
+    })
 
     scaleAtPt(docpt: number[], scale: number) {
         const screenXY = this.getTransform().inverse().transformPoint(docpt[0], docpt[1]);
@@ -895,14 +907,22 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
 
     addDocTab = action((doc: Doc, where: string) => {
         if (where === "inParent") {
-            const pt = this.getTransform().transformPoint(NumCast(doc.x), NumCast(doc.y));
-            doc.x = pt[0];
-            doc.y = pt[1];
-            this.props.addDocument(doc);
-            return true;
+            if (doc instanceof Doc) {
+                const pt = this.getTransform().transformPoint(NumCast(doc.x), NumCast(doc.y));
+                doc.x = pt[0];
+                doc.y = pt[1];
+                return this.props.addDocument(doc);
+            } else {
+                (doc as any as Doc[]).forEach(doc => {
+                    const pt = this.getTransform().transformPoint(NumCast(doc.x), NumCast(doc.y));
+                    doc.x = pt[0];
+                    doc.y = pt[1];
+                });
+                return this.props.addDocument(doc);
+            }
         }
         if (where === "inPlace" && this.layoutDoc.isInPlaceContainer) {
-            this.dataDoc[this.props.fieldKey] = new List<Doc>([doc]);
+            this.dataDoc[this.props.fieldKey] = doc instanceof Doc ? doc : new List<Doc>(doc as any as Doc[]);
             return true;
         }
         return this.props.addDocTab(doc, where);
@@ -1002,8 +1022,10 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
 
     get doLayoutComputation() {
         const { newPool, computedElementData } = this.doInternalLayoutComputation;
-        runInAction(() =>
-            Array.from(newPool.entries()).map(entry => {
+        const array = Array.from(newPool.entries());
+        runInAction(() => {
+            for (let i = 0; i < array.length; i++) {
+                const entry = array[i];
                 const lastPos = this._cachedPool.get(entry[0]); // last computed pos
                 const newPos = entry[1];
                 if (!lastPos || newPos.x !== lastPos.x || newPos.y !== lastPos.y || newPos.z !== lastPos.z || newPos.zIndex !== lastPos.zIndex) {
@@ -1012,7 +1034,8 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
                 if (!lastPos || newPos.height !== lastPos.height || newPos.width !== lastPos.width) {
                     this._layoutSizeData.set(entry[0], { width: newPos.width, height: newPos.height });
                 }
-            }));
+            }
+        });
         this._cachedPool.clear();
         Array.from(newPool.entries()).forEach(k => this._cachedPool.set(k[0], k[1]));
         const elements: ViewDefResult[] = computedElementData.slice();
@@ -1058,12 +1081,13 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
     }
 
     promoteCollection = undoBatch(action(() => {
-        this.childDocs.forEach(doc => {
+        const childDocs = this.childDocs.slice();
+        childDocs.forEach(doc => {
             const scr = this.getTransform().inverse().transformPoint(NumCast(doc.x), NumCast(doc.y));
             doc.x = scr?.[0];
             doc.y = scr?.[1];
-            this.props.addDocTab(doc, "inParent") && this.props.removeDocument(doc);
         });
+        this.props.addDocTab(childDocs as any as Doc, "inParent");
         this.props.ContainingCollectionView?.removeDocument(this.props.Document);
     }));
     layoutDocsInGrid = () => {
@@ -1148,6 +1172,11 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
 
     @action
     setupDragLines = () => {
+        const activeDocs = this.getActiveDocuments();
+        if (activeDocs.length > 50) {
+            DragManager.SetSnapLines([], []);
+            return;
+        }
         const size = this.props.ScreenToLocalTransform().transformDirection(this.props.PanelWidth(), this.props.PanelHeight());
         const selRect = { left: this.panX() - size[0] / 2, top: this.panY() - size[1] / 2, width: size[0], height: size[1] };
         const docDims = (doc: Doc) => ({ left: NumCast(doc.x), top: NumCast(doc.y), width: NumCast(doc._width), height: NumCast(doc._height) });
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 5bac075b4..43d4203ad 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -198,7 +198,6 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
 
     @action
     onPointerUp = (e: PointerEvent): void => {
-        if (!this.props.active(true)) this.props.selectDocuments([this.props.Document], []);
         if (this._visible) {
             const mselect = this.marqueeSelect();
             if (!e.shiftKey) {
@@ -300,10 +299,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
 
     @action
     delete = () => {
-        this.marqueeSelect(false).map(d => this.props.removeDocument(d));
-        if (this.ink) {
-            // this.marqueeInkDelete(this.ink.inkData);
-        }
+        this.props.removeDocument(this.marqueeSelect(false) as any as Doc);
         SelectionManager.DeselectAll();
         this.cleanupInteractions(false);
         MarqueeOptionsMenu.Instance.fadeOut(true);
@@ -349,13 +345,14 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
         const bounds = this.Bounds;
         const selected = this.marqueeSelect(false);
         if (e instanceof KeyboardEvent ? e.key === "c" : true) {
-            selected.map(d => {
-                this.props.removeDocument(d);
+            selected.map(action(d => {
+                //this.props.removeDocument(d);
                 d.x = NumCast(d.x) - bounds.left - bounds.width / 2;
                 d.y = NumCast(d.y) - bounds.top - bounds.height / 2;
                 d.displayTimecode = undefined;  // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
                 return d;
-            });
+            }));
+            this.props.removeDocument(selected as any as Doc);
         }
         const newCollection = this.getCollection(selected, (e as KeyboardEvent)?.key === "t" ? Docs.Create.StackingDocument : undefined);
         this.props.addDocument(newCollection);
-- 
cgit v1.2.3-70-g09d2


From fcd0895e5933270718b9c1a262e7e377eaabb024 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Thu, 7 May 2020 19:18:52 -0400
Subject: fixed most add/remove/move documents to support doc[]'s

---
 src/client/util/DragManager.ts                     |  4 +-
 src/client/views/DocComponent.tsx                  | 37 +++++++++----
 src/client/views/MainView.tsx                      |  6 +-
 src/client/views/TemplateMenu.tsx                  |  6 +-
 src/client/views/collections/CollectionSubView.tsx | 14 ++---
 .../views/collections/CollectionTreeView.tsx       | 64 +++++++++++++---------
 src/client/views/collections/CollectionView.tsx    |  4 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  4 +-
 .../collections/collectionFreeForm/MarqueeView.tsx |  4 +-
 src/client/views/nodes/DocumentView.tsx            | 12 ++--
 src/client/views/nodes/FieldView.tsx               |  6 +-
 src/client/views/nodes/PresBox.tsx                 |  9 ++-
 src/client/views/nodes/VideoBox.tsx                |  9 ++-
 13 files changed, 107 insertions(+), 72 deletions(-)

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

diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 0d208cf1b..cc8cc38dd 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -77,8 +77,8 @@ export namespace DragManager {
         return root;
     }
     export let AbortDrag: () => void = emptyFunction;
-    export type MoveFunction = (document: Doc, targetCollection: Doc | undefined, addDocument: (document: Doc) => boolean) => boolean;
-    export type RemoveFunction = (document: Doc) => boolean;
+    export type MoveFunction = (document: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[]) => boolean) => boolean;
+    export type RemoveFunction = (document: Doc | Doc[]) => boolean;
 
     export interface DragDropDisposer { (): void; }
     export interface DragOptions {
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index 11866aebe..f602cbb15 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -1,4 +1,4 @@
-import { Doc, Opt, DataSym } from '../../new_fields/Doc';
+import { Doc, Opt, DataSym, DocListCast } from '../../new_fields/Doc';
 import { Touchable } from './Touchable';
 import { computed, action, observable } from 'mobx';
 import { Cast, BoolCast, ScriptCast } from '../../new_fields/Types';
@@ -6,6 +6,8 @@ import { listSpec } from '../../new_fields/Schema';
 import { InkingControl } from './InkingControl';
 import { InkTool } from '../../new_fields/InkField';
 import { InteractionUtils } from '../util/InteractionUtils';
+import { List } from '../../new_fields/List';
+import { DateField } from '../../new_fields/DateField';
 
 
 ///  DocComponent returns a generic React base class used by views that don't have 'fieldKey' props (e.g.,CollectionFreeFormDocumentView, DocumentView)
@@ -99,22 +101,37 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps, T
         public get annotationKey() { return this._annotationKey; }
 
         @action.bound
-        removeDocument(doc: Doc): boolean {
-            Doc.GetProto(doc).annotationOn = undefined;
-            const value = Cast(this.dataDoc[this.props.fieldKey + "-" + this._annotationKey], listSpec(Doc), []);
-            const index = value ? Doc.IndexOf(doc, value.map(d => d as Doc), true) : -1;
-            return index !== -1 && value && value.splice(index, 1) ? true : false;
+        removeDocument(doc: Doc | Doc[]): boolean {
+            const docs = doc instanceof Doc ? [doc] : doc;
+            docs.map(doc => doc.annotationOn = undefined);
+            const targetDataDoc = this.dataDoc;
+            const value = DocListCast(targetDataDoc[this.props.fieldKey + "-" + this._annotationKey]);
+            const result = value.filter(v => !docs.includes(v));
+            if (result.length !== value.length) {
+                targetDataDoc[this.props.fieldKey] = new List<Doc>(result);
+                return true;
+            }
+            return false;
         }
         // if the moved document is already in this overlay collection nothing needs to be done.
         // otherwise, if the document can be removed from where it was, it will then be added to this document's overlay collection. 
         @action.bound
-        moveDocument(doc: Doc, targetCollection: Doc | undefined, addDocument: (doc: Doc) => boolean): boolean {
+        moveDocument(doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[]) => boolean): boolean {
             return Doc.AreProtosEqual(this.props.Document, targetCollection) ? true : this.removeDocument(doc) ? addDocument(doc) : false;
         }
         @action.bound
-        addDocument(doc: Doc): boolean {
-            doc.context = Doc.GetProto(doc).annotationOn = this.props.Document;
-            return Doc.AddDocToList(this.dataDoc, this.props.fieldKey + "-" + this._annotationKey, doc) ? true : false;
+        addDocument(doc: Doc | Doc[]): boolean {
+            const docs = doc instanceof Doc ? [doc] : doc;
+            docs.map(doc => doc.context = Doc.GetProto(doc).annotationOn = this.props.Document);
+            const targetDataDoc = this.props.Document[DataSym];
+            const docList = DocListCast(targetDataDoc[this.props.fieldKey + "-" + this._annotationKey]);
+            const added = docs.filter(d => !docList.includes(d));
+            if (added.length) {
+                added.map(doc => doc.context = this.props.Document);
+                targetDataDoc[this.props.fieldKey + "-" + this._annotationKey] = new List<Doc>([...docList, ...added]);
+                targetDataDoc[this.props.fieldKey + "-" + this._annotationKey + "-lastModified"] = new DateField(new Date(Date.now()));
+            }
+            return true;
         }
 
         whenActiveChanged = action((isActive: boolean) => this.props.whenActiveChanged(this._isChildActive = isActive));
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index e67f2f3a2..5b838446d 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -520,9 +520,9 @@ export class MainView extends React.Component {
         return !this._flyoutTranslate ? (<div className="mainView-expandFlyoutButton" title="Re-attach sidebar" onPointerDown={MainView.expandFlyout}><FontAwesomeIcon icon="chevron-right" color="grey" size="lg" /></div>) : (null);
     }
 
-    addButtonDoc = (doc: Doc) => Doc.AddDocToList(Doc.UserDoc().dockedBtns as Doc, "data", doc);
-    remButtonDoc = (doc: Doc) => Doc.RemoveDocFromList(Doc.UserDoc().dockedBtns as Doc, "data", doc);
-    moveButtonDoc = (doc: Doc, targetCollection: Doc | undefined, addDocument: (document: 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);
+    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);
 
     buttonBarXf = () => {
         if (!this._docBtnRef.current) return Transform.Identity();
diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx
index 665ab4e41..5f300372f 100644
--- a/src/client/views/TemplateMenu.tsx
+++ b/src/client/views/TemplateMenu.tsx
@@ -158,9 +158,9 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
                 annotationsKey={""}
                 dontRegisterView={true}
                 fieldKey={"data"}
-                moveDocument={(doc: Doc) => false}
-                removeDocument={(doc: Doc) => false}
-                addDocument={(doc: Doc) => false} />
+                moveDocument={returnFalse}
+                removeDocument={returnFalse}
+                addDocument={returnFalse} />
         </ul>;
     }
 }
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 250c9b3b0..ae84b1923 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -27,9 +27,9 @@ import { CollectionView } from "./CollectionView";
 import React = require("react");
 
 export interface CollectionViewProps extends FieldViewProps {
-    addDocument: (document: Doc) => boolean;
-    removeDocument: (document: Doc) => boolean;
-    moveDocument: (document: Doc, targetCollection: Doc | undefined, addDocument: (document: Doc) => boolean) => boolean;
+    addDocument: (document: Doc | Doc[]) => boolean;
+    removeDocument: (document: Doc | Doc[]) => boolean;
+    moveDocument: (document: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[]) => boolean) => boolean;
     PanelWidth: () => number;
     PanelHeight: () => number;
     VisibleHeight?: () => number;
@@ -212,14 +212,14 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
             if (docDragData) {
                 let added = false;
                 if (docDragData.dropAction || docDragData.userDropAction) {
-                    added = this.props.addDocument(docDragData.droppedDocuments as any as Doc);
+                    added = this.props.addDocument(docDragData.droppedDocuments);
                 } else if (docDragData.moveDocument) {
                     const movedDocs = docDragData.droppedDocuments.filter((d, i) => docDragData.draggedDocuments[i] === d);
                     const addedDocs = docDragData.droppedDocuments.filter((d, i) => docDragData.draggedDocuments[i] !== d);
-                    const res = addedDocs.length ? this.props.addDocument(addedDocs as any as Doc) : true;
-                    added = movedDocs.length ? docDragData.moveDocument(movedDocs as any as Doc, this.props.Document, this.props.addDocument) : res;
+                    const res = addedDocs.length ? this.props.addDocument(addedDocs) : true;
+                    added = movedDocs.length ? docDragData.moveDocument(movedDocs, this.props.Document, this.props.addDocument) : res;
                 } else {
-                    added = this.props.addDocument(docDragData.droppedDocuments as any as Doc);
+                    added = this.props.addDocument(docDragData.droppedDocuments);
                 }
                 e.stopPropagation();
                 return added;
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 4fb54cc07..815202cfe 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -44,7 +44,7 @@ export interface TreeViewProps {
     containingCollection: Doc;
     prevSibling?: Doc;
     renderDepth: number;
-    deleteDoc: (doc: Doc) => boolean;
+    deleteDoc: (doc: Doc | Doc[]) => boolean;
     moveDocument: DragManager.MoveFunction;
     dropAction: dropActionType;
     addDocTab: (doc: Doc, where: string, libraryPath?: Doc[]) => boolean;
@@ -52,7 +52,7 @@ export interface TreeViewProps {
     panelWidth: () => number;
     panelHeight: () => number;
     ChromeHeight: undefined | (() => number);
-    addDocument: (doc: Doc, relativeTo?: Doc, before?: boolean) => boolean;
+    addDocument: (doc: Doc | Doc[], relativeTo?: Doc, before?: boolean) => boolean;
     indentDocument?: () => void;
     outdentDocument?: () => void;
     ScreenToLocalTransform: () => Transform;
@@ -133,14 +133,16 @@ class TreeView extends React.Component<TreeViewProps> {
     @undoBatch delete = () => this.props.deleteDoc(this.props.document);
     @undoBatch openRight = () => this.props.addDocTab(this.props.dropAction === "alias" ? Doc.MakeAlias(this.props.document) : this.props.document, "onRight", this.props.libraryPath);
     @undoBatch indent = () => this.props.addDocument(this.props.document) && this.delete();
-    @undoBatch move = (doc: Doc, target: Doc | undefined, addDoc: (doc: Doc) => boolean) => {
+    @undoBatch move = (doc: Doc | Doc[], target: Doc | undefined, addDoc: (doc: Doc | Doc[]) => boolean) => {
         return this.props.document !== target && this.props.deleteDoc(doc) && addDoc(doc);
     }
-    @undoBatch @action remove = (document: Document, key: string) => {
-        return Doc.RemoveDocFromList(this.dataDoc, key, document);
+    @undoBatch @action remove = (doc: Doc | Doc[], key: string) => {
+        return (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) =>
+            flg && Doc.RemoveDocFromList(this.dataDoc, key, doc), true);
     }
-    @undoBatch @action removeDoc = (document: Document) => {
-        return Doc.RemoveDocFromList(this.props.containingCollection, Doc.LayoutFieldKey(this.props.containingCollection), document);
+    @undoBatch @action removeDoc = (doc: Doc | Doc[]) => {
+        return (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) =>
+            flg && Doc.RemoveDocFromList(this.props.containingCollection, Doc.LayoutFieldKey(this.props.containingCollection), doc), true);
     }
 
     protected createTreeDropTarget = (ele: HTMLDivElement) => {
@@ -224,9 +226,10 @@ class TreeView extends React.Component<TreeViewProps> {
         if (de.complete.docDragData) {
             e.stopPropagation();
             if (de.complete.docDragData.draggedDocuments[0] === this.props.document) return true;
-            let addDoc = (doc: Doc) => this.props.addDocument(doc, undefined, before);
+            let addDoc = (doc: Doc | Doc[]) => this.props.addDocument(doc, undefined, before);
             if (inside) {
-                addDoc = (doc: Doc) => Doc.AddDocToList(this.dataDoc, this.fieldKey, doc) || addDoc(doc);
+                addDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce(
+                    ((flg: boolean, doc) => flg && Doc.AddDocToList(this.dataDoc, this.fieldKey, doc)), true) || addDoc(doc);
             }
             const movedDocs = (de.complete.docDragData.treeViewId === this.props.treeViewId[Id] ? de.complete.docDragData.draggedDocuments : de.complete.docDragData.droppedDocuments);
             const move = de.complete.docDragData.dropAction === "move" || de.complete.docDragData.dropAction;
@@ -286,8 +289,9 @@ class TreeView extends React.Component<TreeViewProps> {
             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))) {
-                const remDoc = (doc: Doc) => this.remove(doc, key);
-                const addDoc = (doc: Doc, addBefore?: Doc, before?: boolean) => Doc.AddDocToList(this.dataDoc, key, doc, addBefore, before, false, true);
+                const remDoc = (doc: Doc | Doc[]) => this.remove(doc, key);
+                const addDoc = (doc: Doc | Doc[], addBefore?: Doc, before?: boolean) => (doc instanceof Doc ? [doc] : doc).reduce(
+                    (flg, doc) => flg && Doc.AddDocToList(this.dataDoc, key, doc, addBefore, before, false, true), true);
                 contentElement = TreeView.GetChildElements(contents instanceof Doc ? [contents] :
                     DocListCast(contents), this.props.treeViewId, doc, undefined, key, this.props.containingCollection, this.props.prevSibling, addDoc, remDoc, this.move,
                     this.props.dropAction, this.props.addDocTab, this.props.pinToPres, this.props.backgroundColor, this.props.ScreenToLocalTransform, this.props.outerXf, this.props.active,
@@ -330,8 +334,9 @@ class TreeView extends React.Component<TreeViewProps> {
         TraceMobx();
         const expandKey = this.treeViewExpandedView === this.fieldKey ? this.fieldKey : this.treeViewExpandedView === "links" ? "links" : undefined;
         if (expandKey !== undefined) {
-            const remDoc = (doc: Doc) => this.remove(doc, expandKey);
-            const addDoc = (doc: Doc, addBefore?: Doc, before?: boolean) => Doc.AddDocToList(this.dataDoc, expandKey, doc, addBefore, before, false, true);
+            const remDoc = (doc: Doc | Doc[]) => this.remove(doc, expandKey);
+            const addDoc = (doc: Doc | Doc[], addBefore?: Doc, before?: boolean) =>
+                (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && Doc.AddDocToList(this.dataDoc, expandKey, doc, addBefore, before, false, true), true);
             const docs = expandKey === "links" ? this.childLinks : this.childDocs;
             const sortKey = `${this.fieldKey}-sortAscending`;
             return <ul key={expandKey + "more"} onClick={(e) => {
@@ -464,6 +469,7 @@ class TreeView extends React.Component<TreeViewProps> {
                         ref={this._docRef}
                         Document={this.props.document}
                         DataDoc={undefined}
+                        treeViewId={this.props.treeViewId[Id]}
                         LibraryPath={this.props.libraryPath || emptyPath}
                         addDocument={undefined}
                         addDocTab={this.props.addDocTab}
@@ -529,8 +535,8 @@ class TreeView extends React.Component<TreeViewProps> {
         key: string,
         parentCollectionDoc: Doc | undefined,
         parentPrevSibling: Doc | undefined,
-        add: (doc: Doc, relativeTo?: Doc, before?: boolean) => boolean,
-        remove: ((doc: Doc) => boolean),
+        add: (doc: Doc | Doc[], relativeTo?: Doc, before?: boolean) => boolean,
+        remove: ((doc: Doc | Doc[]) => boolean),
         move: DragManager.MoveFunction,
         dropAction: dropActionType,
         addDocTab: (doc: Doc, where: string) => boolean,
@@ -619,7 +625,7 @@ class TreeView extends React.Component<TreeViewProps> {
                     remove(child);
                 }
             };
-            const addDocument = (doc: Doc, relativeTo?: Doc, before?: boolean) => {
+            const addDocument = (doc: Doc | Doc[], relativeTo?: Doc, before?: boolean) => {
                 return add(doc, relativeTo ? relativeTo : docs[i], before !== undefined ? before : false);
             };
             const childLayout = Doc.Layout(pair.layout);
@@ -689,22 +695,26 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
     }
 
     @action
-    remove = (document: Document): boolean => {
-        const children = Cast(this.props.Document[DataSym][this.props.fieldKey], listSpec(Doc), []);
-        if (children.indexOf(document) !== -1) {
-            children.splice(children.indexOf(document), 1);
+    remove = (doc: Doc | Doc[]): boolean => {
+        const docs = doc instanceof Doc ? [doc] : doc as Doc[];
+        const targetDataDoc = this.props.Document[DataSym];
+        const value = DocListCast(targetDataDoc[this.props.fieldKey]);
+        const result = value.filter(v => !docs.includes(v));
+        if (result.length !== value.length) {
+            targetDataDoc[this.props.fieldKey] = new List<Doc>(result);
             return true;
         }
         return false;
     }
     @action
-    addDoc = (doc: Document, relativeTo: Opt<Doc>, before?: boolean): boolean => {
-        const doAddDoc = () =>
-            Doc.AddDocToList(this.props.Document[DataSym], this.props.fieldKey, doc, relativeTo, before, false, false, false);
+    addDoc = (doc: Doc | Doc[], relativeTo: Opt<Doc>, before?: boolean): boolean => {
+        const doAddDoc = (doc: Doc | Doc[]) =>
+            (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) =>
+                flg && Doc.AddDocToList(this.props.Document[DataSym], this.props.fieldKey, doc, relativeTo, before, false, false, false), true);
         if (this.props.Document.resolvedDataDoc instanceof Promise) {
-            this.props.Document.resolvedDataDoc.then((resolved: any) => doAddDoc());
+            this.props.Document.resolvedDataDoc.then((resolved: any) => doAddDoc(doc));
         } else {
-            doAddDoc();
+            doAddDoc(doc);
         }
         return true;
     }
@@ -788,8 +798,8 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
     render() {
         if (!(this.props.Document instanceof Doc)) return (null);
         const dropAction = StrCast(this.props.Document.childDropAction) as dropActionType;
-        const addDoc = (doc: Doc, relativeTo?: Doc, before?: boolean) => this.addDoc(doc, relativeTo, before);
-        const moveDoc = (d: Doc, target: Doc | undefined, addDoc: (doc: Doc) => boolean) => this.props.moveDocument(d, target, addDoc);
+        const addDoc = (doc: Doc | Doc[], relativeTo?: Doc, before?: boolean) => this.addDoc(doc, relativeTo, before);
+        const moveDoc = (d: Doc | Doc[], target: Doc | undefined, addDoc: (doc: Doc | Doc[]) => boolean) => this.props.moveDocument(d, target, addDoc);
         const childDocs = this.props.overrideDocuments ? this.props.overrideDocuments : this.childDocs;
         return !childDocs ? (null) : (
             <div className="collectionTreeView-dropTarget" id="body"
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index ccfc660cc..b96f8150d 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -116,13 +116,13 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
     whenActiveChanged = (isActive: boolean) => this.props.whenActiveChanged(this._isChildActive = isActive);
 
     @action.bound
-    addDocument = (doc: Doc): boolean => {
+    addDocument = (doc: Doc | Doc[]): boolean => {
         if (doc instanceof Doc) {
             if (this.props.filterAddDocument?.(doc) === false) {
                 return false;
             }
         }
-        const docs = doc instanceof Doc ? [doc] : doc as any as Doc[];
+        const docs = doc instanceof Doc ? [doc] : doc;
         const targetDataDoc = this.props.Document[DataSym];
         const docList = DocListCast(targetDataDoc[this.props.fieldKey]);
         const added = docs.filter(d => !docList.includes(d));
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 6011d5f9c..b32318d66 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -123,7 +123,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         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
         this.addDocument(newBox);
     }
-    private addDocument = (newBox: Doc) => {
+    private addDocument = (newBox: Doc | Doc[]) => {
         if (newBox instanceof Doc) {
             const added = this.props.addDocument(newBox);
             added && this.bringToFront(newBox);
@@ -184,7 +184,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
                                 const nh = NumCast(layoutDoc._nativeHeight);
                                 layoutDoc._height = nw && nh ? nh / nw * NumCast(layoutDoc._width) : 300;
                             }
-                            d.isBackground === undefined && (d.zIndex = zsorted.length + 1 + i);
+                            d.isBackground === undefined && (d.zIndex = zsorted.length + 1 + i); // bringToFront
                         }
                     });
 
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 43d4203ad..2230c391c 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -299,7 +299,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
 
     @action
     delete = () => {
-        this.props.removeDocument(this.marqueeSelect(false) as any as Doc);
+        this.props.removeDocument(this.marqueeSelect(false));
         SelectionManager.DeselectAll();
         this.cleanupInteractions(false);
         MarqueeOptionsMenu.Instance.fadeOut(true);
@@ -352,7 +352,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
                 d.displayTimecode = undefined;  // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
                 return d;
             }));
-            this.props.removeDocument(selected as any as Doc);
+            this.props.removeDocument(selected);
         }
         const newCollection = this.getCollection(selected, (e as KeyboardEvent)?.key === "t" ? Docs.Create.StackingDocument : undefined);
         this.props.addDocument(newCollection);
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 1724d39cc..042869004 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -70,12 +70,13 @@ export interface DocumentViewProps {
     onDoubleClick?: ScriptField;
     onPointerDown?: ScriptField;
     onPointerUp?: ScriptField;
+    treeViewId?: string;
     dropAction?: dropActionType;
     dragDivName?: string;
     nudge?: (x: number, y: number) => void;
-    addDocument?: (doc: Doc) => boolean;
-    removeDocument?: (doc: Doc) => boolean;
-    moveDocument?: (doc: Doc, targetCollection: Doc | undefined, addDocument: (document: Doc) => boolean) => boolean;
+    addDocument?: (doc: Doc | Doc[]) => boolean;
+    removeDocument?: (doc: Doc | Doc[]) => boolean;
+    moveDocument?: (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[]) => boolean) => boolean;
     ScreenToLocalTransform: () => Transform;
     setupDragLines?: () => void;
     renderDepth: number;
@@ -243,6 +244,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             dragData.removeDocument = this.props.removeDocument;
             dragData.moveDocument = this.props.moveDocument;//  this.Document.onDragStart ? undefined : this.props.moveDocument;
             dragData.dragDivName = this.props.dragDivName;
+            dragData.treeViewId = this.props.treeViewId;
             DragManager.StartDocumentDrag([this._mainCont.current], dragData, x, y, { hideSource: !dropAction && !this.Document.onDragStart });
         }
     }
@@ -1043,7 +1045,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     makeLink = () => this._link; // pass the link placeholde to child views so they can react to make a specialized anchor.  This is essentially a function call to the descendants since the value of the _link variable will immediately get set back to undefined.
 
     @undoBatch
-    hideLinkAnchor = (doc: Doc) => doc.hidden = true
+    hideLinkAnchor = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg: boolean, doc) => flg && (doc.hidden = true), true)
     anchorPanelWidth = () => this.props.PanelWidth() || 1;
     anchorPanelHeight = () => this.props.PanelHeight() || 1;
     @computed get anchors() {
@@ -1143,7 +1145,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         const highlightStyles = ["solid", "dashed", "solid", "solid", "solid", "solid", "solid"];
         let highlighting = fullDegree && this.layoutDoc.type !== DocumentType.FONTICON && this.layoutDoc._viewType !== CollectionViewType.Linear;
         highlighting = highlighting && this.props.focus !== emptyFunction;  // bcz: hack to turn off highlighting onsidebar panel documents.  need to flag a document as not highlightable in a more direct way
-        return <div className={`documentView-node${this.topMost ? "-topmost" : ""}`} 
+        return <div className={`documentView-node${this.topMost ? "-topmost" : ""}`}
             id={this.props.Document[Id]}
             ref={this._mainCont} onKeyDown={this.onKeyDown}
             onContextMenu={this.onContextMenu} onPointerDown={this.onPointerDown} onClick={this.onClick}
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 7db634e74..2ab0a416d 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -31,11 +31,11 @@ export interface FieldViewProps {
     select: (isCtrlPressed: boolean) => void;
     rootSelected: (outsideReaction?: boolean) => boolean;
     renderDepth: number;
-    addDocument?: (document: Doc) => boolean;
+    addDocument?: (document: Doc | Doc[]) => boolean;
     addDocTab: (document: Doc, where: string) => boolean;
     pinToPres: (document: Doc) => void;
-    removeDocument?: (document: Doc) => boolean;
-    moveDocument?: (document: Doc, targetCollection: Doc | undefined, addDocument: (document: Doc) => boolean) => boolean;
+    removeDocument?: (document: Doc | Doc[]) => boolean;
+    moveDocument?: (document: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[]) => boolean) => boolean;
     backgroundColor?: (document: Doc) => string | undefined;
     ScreenToLocalTransform: () => Transform;
     bringToFront: (doc: Doc, sendToBack?: boolean) => void;
diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx
index 343e74c87..49862d39a 100644
--- a/src/client/views/nodes/PresBox.tsx
+++ b/src/client/views/nodes/PresBox.tsx
@@ -271,9 +271,12 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
     });
 
     whenActiveChanged = action((isActive: boolean) => this.props.whenActiveChanged(this._isChildActive = isActive));
-    addDocumentFilter = (doc: Doc) => {
-        doc.aliasOf instanceof Doc && (doc.presentationTargetDoc = doc.aliasOf);
-        !this.childDocs.includes(doc) && (doc.presZoomButton = true);
+    addDocumentFilter = (doc: Doc|Doc[]) => {
+        const docs = doc instanceof Doc ? [doc]: doc;
+        docs.forEach(doc => {
+            doc.aliasOf instanceof Doc && (doc.presentationTargetDoc = doc.aliasOf);
+            !this.childDocs.includes(doc) && (doc.presZoomButton = true);
+        });
         return true;
     }
     childLayoutTemplate = () => this.rootDoc._viewType !== CollectionViewType.Stacking ? undefined : this.presElement;
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 266b7f43f..208b93ba6 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -337,9 +337,12 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
     }
 
     @action.bound
-    addDocumentWithTimestamp(doc: Doc): boolean {
-        const curTime = (this.layoutDoc.currentTimecode || -1);
-        curTime !== -1 && (doc.displayTimecode = curTime);
+    addDocumentWithTimestamp(doc: Doc|Doc[]): boolean {
+        const docs = doc instanceof Doc ? [doc] : doc;
+        docs.forEach(doc => {
+            const curTime = (this.layoutDoc.currentTimecode || -1);
+            curTime !== -1 && (doc.displayTimecode = curTime);
+        })
         return this.addDocument(doc);
     }
 
-- 
cgit v1.2.3-70-g09d2


From 2043fc6d4bf74397e4ccf378137e0ff1704167ca Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Fri, 8 May 2020 01:48:55 -0400
Subject: fixed webbox exception.  fixed marquee div not to userSelect.  fixed
 snap lines when zoomed out.

---
 .../collections/collectionFreeForm/CollectionFreeFormView.tsx  |  2 +-
 .../views/collections/collectionFreeForm/MarqueeView.scss      |  1 +
 src/client/views/nodes/DocumentView.tsx                        |  2 --
 src/client/views/nodes/WebBox.tsx                              | 10 ++++++----
 4 files changed, 8 insertions(+), 7 deletions(-)

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

diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index b32318d66..17c1dd44a 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1177,7 +1177,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
             DragManager.SetSnapLines([], []);
             return;
         }
-        const size = this.props.ScreenToLocalTransform().transformDirection(this.props.PanelWidth(), this.props.PanelHeight());
+        const size = this.getTransform().transformDirection(this.props.PanelWidth(), this.props.PanelHeight());
         const selRect = { left: this.panX() - size[0] / 2, top: this.panY() - size[1] / 2, width: size[0], height: size[1] };
         const docDims = (doc: Doc) => ({ left: NumCast(doc.x), top: NumCast(doc.y), width: NumCast(doc._width), height: NumCast(doc._height) });
         const isDocInView = (doc: Doc, rect: { left: number, top: number, width: number, height: number }) => {
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.scss b/src/client/views/collections/collectionFreeForm/MarqueeView.scss
index 1291e7dc1..a811dd15a 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.scss
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.scss
@@ -7,6 +7,7 @@
     height:100%;
     overflow: hidden;
     border-radius: inherit;
+    user-select: none;
 }
 
 .marqueeView:focus-within {
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index b97b607b9..e3b69b8f3 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1149,8 +1149,6 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             id={this.props.Document[Id]}
             ref={this._mainCont} onKeyDown={this.onKeyDown}
             onContextMenu={this.onContextMenu} onPointerDown={this.onPointerDown} onClick={this.onClick}
-            // onPointerEnter={e => Doc.BrushDoc(this.props.Document)} 
-            // onPointerLeave={e => Doc.BrushDoc(this.props.Document)}
             onPointerEnter={action(() => Doc.BrushDoc(this.props.Document))}
             onPointerLeave={action((e: React.PointerEvent<HTMLDivElement>) => {
                 let entered = false;
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 80e15fd5f..8239d6fdf 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -48,10 +48,12 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
     private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean) => void);
 
     iframeLoaded = action((e: any) => {
-        this._iframeRef.current!.contentDocument?.addEventListener('pointerdown', this.iframedown, false);
-        this._iframeRef.current!.contentDocument?.addEventListener('scroll', this.iframeScrolled, false);
-        this.layoutDoc.scrollHeight = this._iframeRef.current!.contentDocument?.children?.[0].scrollHeight || 1000;
-        this._iframeRef.current!.contentDocument && (this._iframeRef.current!.contentDocument.children[0].scrollTop = NumCast(this.layoutDoc.scrollTop));
+        if (this._iframeRef.current?.contentDocument) {
+            this._iframeRef.current.contentDocument.addEventListener('pointerdown', this.iframedown, false);
+            this._iframeRef.current.contentDocument.addEventListener('scroll', this.iframeScrolled, false);
+            this.layoutDoc.scrollHeight = this._iframeRef.current.contentDocument.children?.[0].scrollHeight || 1000;
+            this._iframeRef.current.contentDocument.children[0].scrollTop = NumCast(this.layoutDoc.scrollTop);
+        }
         this._reactionDisposer?.();
         this._reactionDisposer = reaction(() => this.layoutDoc.scrollY,
             (scrollY) => {
-- 
cgit v1.2.3-70-g09d2


From c1f49a0974982b803c565b6ac3c8931be7c8972f Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Fri, 8 May 2020 16:57:30 -0400
Subject: added " " for switching between pan/zoom & marquee.

---
 src/client/views/DocumentButtonBar.tsx             | 11 +----------
 src/client/views/GlobalKeyHandler.ts               |  5 +++++
 src/client/views/PreviewCursor.tsx                 |  2 +-
 .../views/collections/CollectionDockingView.tsx    | 23 ++++++++++++----------
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  2 +-
 .../collections/collectionFreeForm/MarqueeView.tsx | 14 ++++++-------
 6 files changed, 27 insertions(+), 30 deletions(-)

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

diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index d58eb5875..48685302a 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -229,16 +229,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
         return !targetDoc ? (null) : <div className="documentButtonBar-linker"
             title={Doc.isDocPinned(targetDoc) ? "Unpin from presentation" : "Pin to presentation"}
             style={{ backgroundColor: isPinned ? "black" : "white", color: isPinned ? "white" : "black" }}
-
-            onClick={e => {
-                if (isPinned) {
-                    DockedFrameRenderer.UnpinDoc(targetDoc);
-                }
-                else {
-                    targetDoc.sourceContext = this.view0?.props.ContainingCollectionDoc; // bcz: !! Shouldn't need this ... use search to lookup contexts dynamically
-                    DockedFrameRenderer.PinDoc(targetDoc);
-                }
-            }}>
+            onClick={e => DockedFrameRenderer.PinDoc(targetDoc, isPinned)}>
             <FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="map-pin"
             />
         </div>;
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index 6cca4d69f..e6dd014c0 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -13,6 +13,8 @@ import { InkingControl } from "./InkingControl";
 import { InkTool } from "../../new_fields/InkField";
 import { DocumentView } from "./nodes/DocumentView";
 import GoogleAuthenticationManager from "../apis/GoogleAuthenticationManager";
+import { CollectionFreeFormView } from "./collections/collectionFreeForm/CollectionFreeFormView";
+import { MarqueeView } from "./collections/collectionFreeForm/MarqueeView";
 
 const modifiers = ["control", "meta", "shift", "alt"];
 type KeyHandler = (keycode: string, e: KeyboardEvent) => KeyControlInfo | Promise<KeyControlInfo>;
@@ -65,6 +67,9 @@ export default class KeyManager {
 
     private unmodified = action((keyname: string, e: KeyboardEvent) => {
         switch (keyname) {
+            case " ":
+                MarqueeView.DragState = !MarqueeView.DragState;
+                break;
             case "escape":
                 const main = MainView.Instance;
                 InkingControl.Instance.switchTool(InkTool.None);
diff --git a/src/client/views/PreviewCursor.tsx b/src/client/views/PreviewCursor.tsx
index df30c1215..80c7f3d25 100644
--- a/src/client/views/PreviewCursor.tsx
+++ b/src/client/views/PreviewCursor.tsx
@@ -81,7 +81,7 @@ export class PreviewCursor extends React.Component<{}> {
         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 !== "NumLock" && e.key !== " " &&
             (e.keyCode < 112 || e.keyCode > 123) && // F1 thru F12 keys
             !e.key.startsWith("Arrow") &&
             !e.defaultPrevented) {
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 3c33d4481..c1edc0fe7 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -683,16 +683,19 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
      **/
     @undoBatch
     @action
-    public static PinDoc(doc: Doc) {
-        //add this new doc to props.Document
-        const curPres = Cast(Doc.UserDoc().activePresentation, Doc) as Doc;
-        if (curPres) {
-            const pinDoc = Doc.MakeAlias(doc);
-            pinDoc.presentationTargetDoc = doc;
-            pinDoc.presZoomButton = true;
-            Doc.AddDocToList(curPres, "data", pinDoc);
-            if (!DocumentManager.Instance.getDocumentView(curPres)) {
-                CollectionDockingView.AddRightSplit(curPres);
+    public static PinDoc(doc: Doc, unpin = false) {
+        if (unpin) DockedFrameRenderer.UnpinDoc(doc);
+        else {
+            //add this new doc to props.Document
+            const curPres = Cast(Doc.UserDoc().activePresentation, Doc) as Doc;
+            if (curPres) {
+                const pinDoc = Doc.MakeAlias(doc);
+                pinDoc.presentationTargetDoc = doc;
+                pinDoc.presZoomButton = true;
+                Doc.AddDocToList(curPres, "data", pinDoc);
+                if (!DocumentManager.Instance.getDocumentView(curPres)) {
+                    CollectionDockingView.AddRightSplit(curPres);
+                }
             }
         }
     }
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 17c1dd44a..5d3d8eb4f 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -563,7 +563,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
                     document.removeEventListener("pointerup", this.onPointerUp);
                     return;
                 }
-                this.pan(e);
+                (MarqueeView.DragState || e.altKey) && this.pan(e);
             }
             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();
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index a777f6fe5..3c67f8c6a 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -19,8 +19,6 @@ import React = require("react");
 import { CognitiveServices } from "../../../cognitive_services/CognitiveServices";
 import { RichTextField } from "../../../../new_fields/RichTextField";
 import { CollectionView } from "../CollectionView";
-import { FormattedTextBox } from "../../nodes/formattedText/FormattedTextBox";
-import { ScriptField } from "../../../../new_fields/ScriptField";
 
 interface MarqueeViewProps {
     getContainerTransform: () => Transform;
@@ -39,7 +37,7 @@ interface MarqueeViewProps {
 @observer
 export class MarqueeView extends React.Component<SubCollectionViewProps & MarqueeViewProps>
 {
-    private _mainCont = React.createRef<HTMLDivElement>();
+    @observable public static DragState = true;
     @observable _lastX: number = 0;
     @observable _lastY: number = 0;
     @observable _downX: number = 0;
@@ -168,9 +166,9 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
     onPointerDown = (e: React.PointerEvent): void => {
         this._downX = this._lastX = e.clientX;
         this._downY = this._lastY = e.clientY;
-        if (e.button === 2 || (e.button === 0 && e.altKey)) {
+        if (e.button === 2 || (e.button === 0 && (e.altKey || !MarqueeView.DragState))) {
             this.setPreviewCursor(e.clientX, e.clientY, true);
-            if (e.altKey) {
+            if (e.altKey || !MarqueeView.DragState) {
                 //e.stopPropagation(); // bcz: removed so that you can alt-click on button in a collection to switch link following behaviors.
                 e.preventDefault();
             }
@@ -195,7 +193,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
         } else {
             this.cleanupInteractions(true); // stop listening for events if another lower-level handle (e.g. another Marquee) has stopPropagated this
         }
-        if (e.altKey) {
+        if (e.altKey || !MarqueeView.DragState) {
             e.preventDefault();
         }
     }
@@ -230,7 +228,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
         };
         document.addEventListener("pointerdown", hideMarquee);
 
-        if (e.altKey) {
+        if (e.altKey || !MarqueeView.DragState) {
             e.preventDefault();
         }
     }
@@ -614,7 +612,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
 
     render() {
         return <div className="marqueeView"
-            style={{ overflow: StrCast(this.props.Document._overflow), }}
+            style={{ overflow: StrCast(this.props.Document._overflow), cursor: MarqueeView.DragState ? "hand" : "crosshair" }}
             onScroll={(e) => e.currentTarget.scrollTop = e.currentTarget.scrollLeft = 0} onClick={this.onClick} onPointerDown={this.onPointerDown}>
             {this._visible ? this.marqueeDiv : null}
             {this.props.children}
-- 
cgit v1.2.3-70-g09d2


From e50914de0657d1f0d48297e9fbdbed3d0731d9ee Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Fri, 8 May 2020 19:01:39 -0400
Subject: enabled two-finger pan, pinch zoom and marquee drag without a
 modifier key

---
 src/client/views/GlobalKeyHandler.ts                         |  2 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx            |  7 ++++---
 .../views/collections/collectionFreeForm/MarqueeView.tsx     | 12 ++++++------
 3 files changed, 11 insertions(+), 10 deletions(-)

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

diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index e6dd014c0..c73e1a66a 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -68,7 +68,7 @@ export default class KeyManager {
     private unmodified = action((keyname: string, e: KeyboardEvent) => {
         switch (keyname) {
             case " ":
-                MarqueeView.DragState = !MarqueeView.DragState;
+                MarqueeView.DragMarquee = !MarqueeView.DragMarquee;
                 break;
             case "escape":
                 const main = MainView.Instance;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 5d3d8eb4f..d4da87568 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -563,7 +563,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
                     document.removeEventListener("pointerup", this.onPointerUp);
                     return;
                 }
-                (MarqueeView.DragState || e.altKey) && this.pan(e);
+                (!MarqueeView.DragMarquee || e.altKey) && this.pan(e);
             }
             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();
@@ -709,7 +709,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
 
     @action
     zoom = (pointX: number, pointY: number, deltaY: number): void => {
-        let deltaScale = deltaY > 0 ? (1 / 1.1) : 1.1;
+        let deltaScale = deltaY > 0 ? (1 / 1.05) : 1.05;
         if (deltaScale * this.zoomScaling() < 1 && this.isAnnotationOverlay) {
             deltaScale = 1 / this.zoomScaling();
         }
@@ -732,7 +732,8 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         }
         else if (this.props.active(true)) {
             e.stopPropagation();
-            this.zoom(e.clientX, e.clientY, e.deltaY);
+            if (!e.ctrlKey) this.setPan(this.panX() + e.deltaX, this.panY() + e.deltaY, "None", true);
+            else this.zoom(e.clientX, e.clientY, e.deltaY);
         }
         this.props.Document.targetScale = NumCast(this.props.Document.scale);
     }
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 3c67f8c6a..a4e474e96 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -37,7 +37,7 @@ interface MarqueeViewProps {
 @observer
 export class MarqueeView extends React.Component<SubCollectionViewProps & MarqueeViewProps>
 {
-    @observable public static DragState = true;
+    @observable public static DragMarquee = false;
     @observable _lastX: number = 0;
     @observable _lastY: number = 0;
     @observable _downX: number = 0;
@@ -166,9 +166,9 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
     onPointerDown = (e: React.PointerEvent): void => {
         this._downX = this._lastX = e.clientX;
         this._downY = this._lastY = e.clientY;
-        if (e.button === 2 || (e.button === 0 && (e.altKey || !MarqueeView.DragState))) {
+        if (e.button === 2 || (e.button === 0 && (e.altKey || MarqueeView.DragMarquee))) {
             this.setPreviewCursor(e.clientX, e.clientY, true);
-            if (e.altKey || !MarqueeView.DragState) {
+            if (e.altKey || MarqueeView.DragMarquee) {
                 //e.stopPropagation(); // bcz: removed so that you can alt-click on button in a collection to switch link following behaviors.
                 e.preventDefault();
             }
@@ -193,7 +193,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
         } else {
             this.cleanupInteractions(true); // stop listening for events if another lower-level handle (e.g. another Marquee) has stopPropagated this
         }
-        if (e.altKey || !MarqueeView.DragState) {
+        if (e.altKey || MarqueeView.DragMarquee) {
             e.preventDefault();
         }
     }
@@ -228,7 +228,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
         };
         document.addEventListener("pointerdown", hideMarquee);
 
-        if (e.altKey || !MarqueeView.DragState) {
+        if (e.altKey || MarqueeView.DragMarquee) {
             e.preventDefault();
         }
     }
@@ -612,7 +612,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
 
     render() {
         return <div className="marqueeView"
-            style={{ overflow: StrCast(this.props.Document._overflow), cursor: MarqueeView.DragState ? "hand" : "crosshair" }}
+            style={{ overflow: StrCast(this.props.Document._overflow), cursor: MarqueeView.DragMarquee ? "crosshair" : "hand" }}
             onScroll={(e) => e.currentTarget.scrollTop = e.currentTarget.scrollLeft = 0} onClick={this.onClick} onPointerDown={this.onPointerDown}>
             {this._visible ? this.marqueeDiv : null}
             {this.props.children}
-- 
cgit v1.2.3-70-g09d2


From 09967d3ce1fe107eab663999f59a3796aaebbe04 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Fri, 8 May 2020 20:58:57 -0400
Subject: from last

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

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

diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index d4da87568..dfcb30588 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -732,7 +732,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         }
         else if (this.props.active(true)) {
             e.stopPropagation();
-            if (!e.ctrlKey) this.setPan(this.panX() + e.deltaX, this.panY() + e.deltaY, "None", true);
+            if (!e.ctrlKey && MarqueeView.DragMarquee) this.setPan(this.panX() + e.deltaX, this.panY() + e.deltaY, "None", true);
             else this.zoom(e.clientX, e.clientY, e.deltaY);
         }
         this.props.Document.targetScale = NumCast(this.props.Document.scale);
-- 
cgit v1.2.3-70-g09d2


From 5c876f2f456f75e9886946f738f07a730688f38a Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Sat, 9 May 2020 17:44:14 -0400
Subject: lint fixes

---
 src/Utils.ts                                                 |  2 +-
 src/client/DocServer.ts                                      |  2 +-
 src/client/views/DocComponent.tsx                            |  2 +-
 src/client/views/collections/CollectionSchemaCells.tsx       |  6 +++---
 .../views/collections/CollectionSchemaMovableTableHOC.tsx    |  8 ++++----
 src/client/views/collections/CollectionSchemaView.tsx        |  6 +++---
 src/client/views/collections/CollectionSubView.tsx           |  6 +++---
 src/client/views/collections/CollectionTreeView.tsx          |  2 +-
 src/client/views/collections/CollectionView.tsx              |  2 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx            |  2 +-
 src/client/views/linking/LinkMenuItem.tsx                    |  2 +-
 src/client/views/nodes/VideoBox.tsx                          |  2 +-
 src/client/views/nodes/formattedText/DashFieldView.tsx       |  5 +++--
 src/new_fields/Doc.ts                                        | 12 ++++++------
 14 files changed, 30 insertions(+), 29 deletions(-)

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

diff --git a/src/Utils.ts b/src/Utils.ts
index 732f74bfc..bcb215804 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -323,7 +323,7 @@ export function OmitKeys(obj: any, keys: string[], pattern?: string, addKeyFunc?
     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 };
 }
diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts
index bf9fd232c..34ef502ad 100644
--- a/src/client/DocServer.ts
+++ b/src/client/DocServer.ts
@@ -250,7 +250,7 @@ export namespace DocServer {
         if (cached !== undefined && !(cached instanceof Promise)) {
             return cached;
         }
-    }
+    };
 
     let _GetRefField: (id: string) => Promise<Opt<RefField>> = errorFunc;
     let _GetCachedRefField: (id: string) => Opt<RefField> = errorFunc;
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index 4e7d4c5ed..881e352a6 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -103,7 +103,7 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps, T
         removeDocument(doc: Doc | Doc[]): boolean {
             const docs = doc instanceof Doc ? [doc] : doc;
             docs.map(doc => doc.annotationOn = undefined);
-            const targetDataDoc = this.dataDoc; this
+            const targetDataDoc = this.dataDoc;
             const value = DocListCast(targetDataDoc[this.annotationKey]);
             const result = value.filter(v => !docs.includes(v));
             if (result.length !== value.length) {
diff --git a/src/client/views/collections/CollectionSchemaCells.tsx b/src/client/views/collections/CollectionSchemaCells.tsx
index 5253ee0b9..8a5450b0c 100644
--- a/src/client/views/collections/CollectionSchemaCells.tsx
+++ b/src/client/views/collections/CollectionSchemaCells.tsx
@@ -37,8 +37,8 @@ export interface CellProps {
     renderDepth: number;
     addDocTab: (document: Doc, where: string) => boolean;
     pinToPres: (document: Doc) => void;
-    moveDocument: (document: Doc, targetCollection: Doc | undefined,
-        addDocument: (document: Doc) => boolean) => boolean;
+    moveDocument: (document: Doc | Doc[], targetCollection: Doc | undefined,
+        addDocument: (document: Doc | Doc[]) => boolean) => boolean;
     isFocused: boolean;
     changeFocusedCellByIndex: (row: number, col: number) => void;
     setIsEditing: (isEditing: boolean) => void;
@@ -185,7 +185,7 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
         const onItemDown = (e: React.PointerEvent) => {
             fieldIsDoc && SetupDrag(this._focusRef,
                 () => this._document[props.fieldKey] instanceof Doc ? this._document[props.fieldKey] : this._document,
-                this._document[props.fieldKey] instanceof Doc ? (doc: Doc, target: Doc | undefined, addDoc: (newDoc: Doc) => any) => addDoc(doc) : this.props.moveDocument,
+                this._document[props.fieldKey] instanceof Doc ? (doc: Doc | Doc[], target: Doc | undefined, addDoc: (newDoc: Doc | Doc[]) => any) => addDoc(doc) : this.props.moveDocument,
                 this._document[props.fieldKey] instanceof Doc ? "alias" : this.props.Document.schemaDoc ? "copy" : undefined)(e);
         };
         const onPointerEnter = (e: React.PointerEvent): void => {
diff --git a/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx b/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx
index 8636e3eb5..5aec46a83 100644
--- a/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx
+++ b/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx
@@ -130,8 +130,8 @@ export class MovableColumn extends React.Component<MovableColumnProps> {
 export interface MovableRowProps {
     rowInfo: RowInfo;
     ScreenToLocalTransform: () => Transform;
-    addDoc: (doc: Doc, relativeTo?: Doc, before?: boolean) => boolean;
-    removeDoc: (doc: Doc) => boolean;
+    addDoc: (doc: Doc | Doc[], relativeTo?: Doc, before?: boolean) => boolean;
+    removeDoc: (doc: Doc | Doc[]) => boolean;
     rowFocused: boolean;
     textWrapRow: (doc: Doc) => void;
     rowWrapped: boolean;
@@ -183,7 +183,7 @@ export class MovableRow extends React.Component<MovableRowProps> {
         if (docDragData) {
             e.stopPropagation();
             if (docDragData.draggedDocuments[0] === rowDoc) return true;
-            const addDocument = (doc: Doc) => this.props.addDoc(doc, rowDoc, before);
+            const addDocument = (doc: Doc | Doc[]) => this.props.addDoc(doc, rowDoc, before);
             const movedDocs = docDragData.draggedDocuments;
             return (docDragData.dropAction || docDragData.userDropAction) ?
                 docDragData.droppedDocuments.reduce((added: boolean, d) => this.props.addDoc(d, rowDoc, before) || added, false)
@@ -201,7 +201,7 @@ export class MovableRow extends React.Component<MovableRowProps> {
 
     @undoBatch
     @action
-    move: DragManager.MoveFunction = (doc: Doc, targetCollection: Doc | undefined, addDoc) => {
+    move: DragManager.MoveFunction = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDoc) => {
         const targetView = targetCollection && DocumentManager.Instance.getDocumentView(targetCollection);
         if (targetView && targetView.props.ContainingCollectionDoc) {
             return doc !== targetCollection && doc !== targetView.props.ContainingCollectionDoc && this.props.removeDoc(doc) && addDoc(doc);
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index c0024293f..51ad6c81b 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -207,9 +207,9 @@ export interface SchemaTableProps {
     ContainingCollectionDoc: Opt<Doc>;
     fieldKey: string;
     renderDepth: number;
-    deleteDocument: (document: Doc) => boolean;
-    addDocument: (document: Doc) => boolean;
-    moveDocument: (document: Doc, targetCollection: Doc | undefined, addDocument: (document: Doc) => boolean) => boolean;
+    deleteDocument: (document: Doc | Doc[]) => boolean;
+    addDocument: (document: Doc | Doc[]) => boolean;
+    moveDocument: (document: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[]) => boolean) => boolean;
     ScreenToLocalTransform: () => Transform;
     active: (outsideReaction: boolean) => boolean;
     onDrop: (e: React.DragEvent<Element>, options: DocumentOptions, completed?: (() => void) | undefined) => void;
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 4fef16315..8b50bd8fc 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -246,7 +246,7 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
                 };
                 temporaryFileReader.readAsText(inputFile);
             });
-        };
+        }
         @undoBatch
         @action
         protected async onExternalDrop(e: React.DragEvent, options: DocumentOptions, completed?: () => void) {
@@ -388,11 +388,11 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
 
                     file?.type === "application/json" && this.readUploadedFileAsText(file).then(result => {
                         console.log(result);
-                        const json = JSON.parse(result as string) as any;
+                        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)
+                                const proto = Doc.GetProto(label);
                                 proto._width = 120;
                                 proto._height = 20;
                                 return proto;
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 7086ba380..f6fcc1ac4 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -673,7 +673,7 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
 
     @action
     remove = (doc: Doc | Doc[]): boolean => {
-        const docs = doc instanceof Doc ? [doc] : doc as Doc[];
+        const docs = doc instanceof Doc ? [doc] : doc;
         const targetDataDoc = this.props.Document[DataSym];
         const value = DocListCast(targetDataDoc[this.props.fieldKey]);
         const result = value.filter(v => !docs.includes(v));
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 6c96baede..0841d9680 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -152,7 +152,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
     // otherwise, the document being moved must be able to be removed from its container before
     // moving it into the target.  
     @action.bound
-    moveDocument = (doc: Doc, targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[]) => boolean): boolean => {
+    moveDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[]) => boolean): boolean => {
         if (Doc.AreProtosEqual(this.props.Document, targetCollection)) {
             return true;
         }
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index dfcb30588..6cc0cfcd2 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -786,7 +786,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
             }
             doc.zIndex = zlast + 1;
         }
-    })
+    });
 
     scaleAtPt(docpt: number[], scale: number) {
         const screenXY = this.getTransform().inverse().transformPoint(docpt[0], docpt[1]);
diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx
index d4e58fa8e..b0c61cb04 100644
--- a/src/client/views/linking/LinkMenuItem.tsx
+++ b/src/client/views/linking/LinkMenuItem.tsx
@@ -38,7 +38,7 @@ export async function StartLinkTargetsDrag(dragEle: HTMLElement, docView: Docume
         }
 
         const dragData = new DragManager.DocumentDragData(moddrag.length ? moddrag : draggedDocs);
-        dragData.moveDocument = (doc: Doc, targetCollection: Doc | undefined, addDocument: (doc: Doc) => boolean): boolean => {
+        dragData.moveDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[]) => boolean): boolean => {
             docView.props.removeDocument?.(doc);
             addDocument(doc);
             return true;
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 208b93ba6..9602eac52 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -342,7 +342,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
         docs.forEach(doc => {
             const curTime = (this.layoutDoc.currentTimecode || -1);
             curTime !== -1 && (doc.displayTimecode = curTime);
-        })
+        });
         return this.addDocument(doc);
     }
 
diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx
index f28057fd7..d5a28fd14 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.tsx
+++ b/src/client/views/nodes/formattedText/DashFieldView.tsx
@@ -82,7 +82,7 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
     // 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._fieldKey === "PARAMS" ? this._textBoxDoc[this._fieldKey] : "");
+            const dashVal = this._dashDoc[this._fieldKey] || (this._fieldKey === "PARAMS" ? this._textBoxDoc[this._fieldKey] : "");
             const fval = StrCast(dashVal).startsWith(":=") || dashVal === "" ? Doc.Layout(this._textBoxDoc)[this._fieldKey] : dashVal;
             const boolVal = Cast(fval, "boolean", null);
             const strVal = Field.toString(fval as Field) || "";
@@ -153,8 +153,9 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
                 } else if (nodeText.startsWith("=:=")) {
                     Doc.Layout(this._textBoxDoc)[this._fieldKey] = ComputedField.MakeFunction(nodeText.substring(3));
                 } else {
-                    if (this._fieldKey !== "PARAMS" || !this._textBoxDoc[this._fieldKey] || this._dashDoc?.PARAMS)
+                    if (this._fieldKey !== "PARAMS" || !this._textBoxDoc[this._fieldKey] || this._dashDoc?.PARAMS) {
                         this._dashDoc![this._fieldKey] = newText;
+                    }
                 }
             });
         }
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index 2d4a4539e..e0627fb37 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -592,8 +592,8 @@ export namespace Doc {
     }
 
     export function MakeClone(doc: Doc): Doc {
-        let cloneMap = new Map<string, Doc>();
-        let rtfMap: { copy: Doc, key: string, field: RichTextField }[] = [];
+        const cloneMap = new Map<string, Doc>();
+        const rtfMap: { copy: Doc, key: string, field: RichTextField }[] = [];
         const copy = Doc.makeClone(doc, cloneMap, rtfMap);
         rtfMap.map(({ copy, key, field }) => {
             const replacer = (match: any, attr: string, id: string, offset: any, string: any) => {
@@ -605,7 +605,7 @@ export namespace Doc {
                 return href + (mapped ? mapped[Id] : id);
             };
             const regex = `(${Utils.prepend("/doc/")})([^"]*)`;
-            var re = new RegExp(regex, "g");
+            const re = new RegExp(regex, "g");
             copy[key] = new RichTextField(field.Data.replace(/("docid":|"targetId":|"linkId":)"([^"]+)"/g, replacer).replace(re, replacer2), field.Text);
         });
         return copy;
@@ -626,9 +626,9 @@ export namespace Doc {
                 const list = Cast(doc[key], listSpec(Doc));
                 if (list !== undefined && !(list instanceof Promise)) {
                     copy[key] = new List<Doc>(list.filter(d => d instanceof Doc).map(d => Doc.makeClone(d as Doc, cloneMap, rtfs)));
-                } else if (doc[key] instanceof Doc)
+                } else if (doc[key] instanceof Doc) {
                     copy[key] = key.includes("layout[") ? undefined : Doc.makeClone(doc[key] as Doc, cloneMap, rtfs); // reference documents except copy documents that are expanded teplate fields 
-                else {
+                } else {
                     copy[key] = ObjectField.MakeCopy(field);
                     if (field instanceof RichTextField) {
                         if (field.Data.includes('"docid":') || field.Data.includes('"targetId":') || field.Data.includes('"linkId":')) {
@@ -636,7 +636,7 @@ export namespace Doc {
                         }
                     }
                 }
-            }
+            };
             if (key === "proto") {
                 if (doc[key] instanceof Doc) {
                     copy[key] = Doc.makeClone(doc[key]!, cloneMap, rtfs);
-- 
cgit v1.2.3-70-g09d2


From 638fb6b16c4d69090e3806cca0a1a1909ec612c9 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Sat, 9 May 2020 21:43:04 -0400
Subject: added document clone and paste for all collections

---
 src/client/views/DocumentDecorations.tsx           |  4 +-
 src/client/views/GlobalKeyHandler.ts               | 65 +++++++++++++---
 src/client/views/MainView.tsx                      |  2 +
 src/client/views/PreviewCursor.tsx                 | 91 ++++++++++++----------
 .../views/collections/CollectionTreeView.tsx       |  6 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 12 ++-
 .../collections/collectionFreeForm/MarqueeView.tsx |  5 +-
 7 files changed, 127 insertions(+), 58 deletions(-)

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

diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 9b6105811..1054822c7 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -174,8 +174,8 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
     }
     @undoBatch
     @action
-    onCloseClick = async (e: PointerEvent) => {
-        if (e.button === 0) {
+    onCloseClick = async (e: PointerEvent | undefined) => {
+        if (!e?.button) {
             const recent = Cast(Doc.UserDoc().myRecentlyClosed, Doc) as Doc;
             const selected = SelectionManager.SelectedDocuments().slice();
             SelectionManager.DeselectAll();
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index d9281ac24..3fd14735b 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -1,10 +1,10 @@
-import { UndoManager } from "../util/UndoManager";
+import { UndoManager, undoBatch } from "../util/UndoManager";
 import { SelectionManager } from "../util/SelectionManager";
 import { CollectionDockingView } from "./collections/CollectionDockingView";
 import { MainView } from "./MainView";
 import { DragManager } from "../util/DragManager";
 import { action, runInAction } from "mobx";
-import { Doc } from "../../new_fields/Doc";
+import { Doc, DocListCast } from "../../new_fields/Doc";
 import { DictationManager } from "../util/DictationManager";
 import SharingManager from "../util/SharingManager";
 import { Cast, PromiseValue, NumCast } from "../../new_fields/Types";
@@ -16,6 +16,11 @@ import GoogleAuthenticationManager from "../apis/GoogleAuthenticationManager";
 import { CollectionFreeFormView } from "./collections/collectionFreeForm/CollectionFreeFormView";
 import { MarqueeView } from "./collections/collectionFreeForm/MarqueeView";
 import { Id } from "../../new_fields/FieldSymbols";
+import { DocumentDecorations } from "./DocumentDecorations";
+import { DocumentType } from "../documents/DocumentTypes";
+import { DocServer } from "../DocServer";
+import { List } from "../../new_fields/List";
+import { DateField } from "../../new_fields/DateField";
 
 const modifiers = ["control", "meta", "shift", "alt"];
 type KeyHandler = (keycode: string, e: KeyboardEvent) => KeyControlInfo | Promise<KeyControlInfo>;
@@ -245,15 +250,25 @@ export default class KeyManager {
                 preventDefault = false;
                 break;
             case "x":
+                if (SelectionManager.SelectedDocuments().length) {
+                    const bds = DocumentDecorations.Instance.Bounds;
+                    const pt = [bds.x + (bds.r - bds.x) / 2, bds.y + (bds.b - bds.y) / 2];
+                    const text = `__DashDocId(${pt[0]},${pt[1]}):` + SelectionManager.SelectedDocuments().map(dv => dv.Document[Id]).join(":");
+                    SelectionManager.SelectedDocuments().length && navigator.clipboard.writeText(text);
+                    DocumentDecorations.Instance.onCloseClick(undefined);
+                    stopPropagation = false;
+                    preventDefault = false;
+                }
+                break;
             case "c":
-                SelectionManager.SelectedDocuments().length && navigator.clipboard.writeText("__DashDocId:" + SelectionManager.SelectedDocuments()[0].Document[Id]);
-                // window.getSelection()?.removeAllRanges();
-                // let range = document.createRange();
-                // range.selectNode(SelectionManager.SelectedDocuments()[0].ContentDiv!);
-                // window.getSelection()?.addRange(range);
-                // document.execCommand('copy');
-                stopPropagation = false;
-                preventDefault = false;
+                if (SelectionManager.SelectedDocuments().length) {
+                    const bds = DocumentDecorations.Instance.Bounds;
+                    const pt = [bds.x + (bds.r - bds.x) / 2, bds.y + (bds.b - bds.y) / 2];
+                    const text = `__DashDocId(${pt[0]},${pt[1]}):` + SelectionManager.SelectedDocuments().map(dv => dv.Document[Id]).join(":");
+                    SelectionManager.SelectedDocuments().length && navigator.clipboard.writeText(text);;
+                    stopPropagation = false;
+                    preventDefault = false;
+                }
                 break;
         }
 
@@ -263,6 +278,36 @@ export default class KeyManager {
         };
     });
 
+    public paste(e: ClipboardEvent) {
+        if (e.clipboardData?.getData("text/plain") !== "" && e.clipboardData?.getData("text/plain").startsWith("__DashDocId(")) {
+            const first = SelectionManager.SelectedDocuments().length ? SelectionManager.SelectedDocuments()[0] : undefined;
+            if (first?.props.Document.type === DocumentType.COL) {
+                const docids = e.clipboardData.getData("text/plain").split(":");
+                let count = 1;
+                const list: Doc[] = [];
+                const targetDataDoc = Doc.GetProto(first.props.Document);
+                const fieldKey = Doc.LayoutFieldKey(first.props.Document);
+                const docList = DocListCast(targetDataDoc[fieldKey]);
+                docids.map((did, i) => i && DocServer.GetRefField(did).then(doc => {
+                    count++;
+                    if (doc instanceof Doc) {
+                        list.push(doc);
+                    }
+                    if (count === docids.length) {
+                        const added = list.filter(d => !docList.includes(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()));
+                            })();
+                        }
+                    }
+                }));
+            }
+        }
+    }
+
     async printClipboard() {
         const text: string = await navigator.clipboard.readText();
     }
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index b6dfe60e5..cbaae63bc 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -83,12 +83,14 @@ export class MainView extends React.Component {
         firstScriptTag.parentNode!.insertBefore(tag, firstScriptTag);
         window.removeEventListener("keydown", KeyManager.Instance.handle);
         window.addEventListener("keydown", KeyManager.Instance.handle);
+        window.addEventListener("paste", KeyManager.Instance.paste);
     }
 
     componentWillUnMount() {
         window.removeEventListener("keydown", KeyManager.Instance.handle);
         window.removeEventListener("pointerdown", this.globalPointerDown);
         window.removeEventListener("pointerup", this.globalPointerUp);
+        window.removeEventListener("paste", KeyManager.Instance.paste);
     }
 
     constructor(props: Readonly<{}>) {
diff --git a/src/client/views/PreviewCursor.tsx b/src/client/views/PreviewCursor.tsx
index b9036bf1e..14b5b3fce 100644
--- a/src/client/views/PreviewCursor.tsx
+++ b/src/client/views/PreviewCursor.tsx
@@ -7,13 +7,15 @@ import { Docs } from '../documents/Documents';
 import { Doc } from '../../new_fields/Doc';
 import { Transform } from "../util/Transform";
 import { DocServer } from '../DocServer';
+import { NumCast } from '../../new_fields/Types';
+import { undoBatch } from '../util/UndoManager';
 
 @observer
 export class PreviewCursor extends React.Component<{}> {
     static _onKeyPress?: (e: KeyboardEvent) => void;
     static _getTransform: () => Transform;
+    static _addDocument: (doc: Doc | Doc[]) => void;
     static _addLiveTextDoc: (doc: Doc) => void;
-    static _addDocument: (doc: Doc) => boolean;
     static _nudge: (x: number, y: number) => boolean;
     @observable static _clickPoint = [0, 0];
     @observable public static Visible = false;
@@ -28,62 +30,73 @@ export class PreviewCursor extends React.Component<{}> {
             const newPoint = PreviewCursor._getTransform().transformPoint(PreviewCursor._clickPoint[0], PreviewCursor._clickPoint[1]);
             runInAction(() => PreviewCursor.Visible = false);
 
+            // tests for URL and makes web document
+            const re: any = /^https?:\/\//g;
             if (e.clipboardData.getData("text/plain") !== "") {
                 // tests for youtube and makes video document
                 if (e.clipboardData.getData("text/plain").indexOf("www.youtube.com/watch") !== -1) {
                     const url = e.clipboardData.getData("text/plain").replace("youtube.com/watch?v=", "youtube.com/embed/");
-                    return PreviewCursor._addDocument(Docs.Create.VideoDocument(url, {
+                    undoBatch(() => PreviewCursor._addDocument(Docs.Create.VideoDocument(url, {
                         title: url, _width: 400, _height: 315,
                         _nativeWidth: 600, _nativeHeight: 472.5,
                         x: newPoint[0], y: newPoint[1]
-                    }));
+                    })))();
                 }
 
-                // tests for URL and makes web document
-                const re: any = /^https?:\/\//g;
-                if (re.test(e.clipboardData.getData("text/plain"))) {
+                else if (re.test(e.clipboardData.getData("text/plain"))) {
                     const url = e.clipboardData.getData("text/plain");
-                    return PreviewCursor._addDocument(Docs.Create.WebDocument(url, {
+                    undoBatch(() => PreviewCursor._addDocument(Docs.Create.WebDocument(url, {
                         title: url, _width: 500, _height: 300,
                         // nativeWidth: 300, nativeHeight: 472.5,
                         x: newPoint[0], y: newPoint[1]
-                    }));
+                    })))();
                 }
 
-                if (e.clipboardData.getData("text/plain").includes("__DashDocId:")) {
-                    const docid = e.clipboardData.getData("text/plain").split("__DashDocId:")[1];
-                    return DocServer.GetRefField(docid).then(doc => {
+                else if (e.clipboardData.getData("text/plain").startsWith("__DashDocId(")) {
+                    const docids = e.clipboardData.getData("text/plain").split(":");
+                    const strs = docids[0].split(",");
+                    const ptx = Number(strs[0].substring("__DashDocId(".length));
+                    const pty = Number(strs[1].substring(0, strs[1].length - 1));
+                    const center = PreviewCursor._getTransform().transformPoint(ptx, pty);
+                    let count = 1;
+                    const list: Doc[] = [];
+                    docids.map((did, i) => i && DocServer.GetRefField(did).then(doc => {
+                        count++;
                         if (doc instanceof Doc) {
-                            const alias = Doc.MakeAlias(doc);
-                            alias.x = newPoint[0];
-                            alias.y = newPoint[1];
-                            PreviewCursor._addDocument(alias);
+                            const alias = Doc.MakeClone(doc);
+                            alias.x = newPoint[0] + NumCast(doc.x) - center[0];
+                            alias.y = newPoint[1] + NumCast(doc.y) - center[1];
+                            list.push(alias);
                         }
-                    });
-                }
+                        if (count === docids.length) {
+                            undoBatch(() => PreviewCursor._addDocument(list))();
+                        }
+                    }));
 
-                // creates text document
-                return PreviewCursor._addLiveTextDoc(Docs.Create.TextDocument("", {
-                    _width: 500,
-                    limitHeight: 400,
-                    _autoHeight: true,
-                    x: newPoint[0],
-                    y: newPoint[1],
-                    title: "-pasted text-"
-                }));
-            }
-            //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"));
+                } else {
+                    // creates text document
+                    undoBatch(() => PreviewCursor._addLiveTextDoc(Docs.Create.TextDocument("", {
+                        _width: 500,
+                        limitHeight: 400,
+                        _autoHeight: true,
+                        x: newPoint[0],
+                        y: newPoint[1],
+                        title: "-pasted text-"
+                    })))();
+                }
+            } 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"));
 
-                return PreviewCursor._addDocument(Docs.Create.ImageDocument(
-                    arr[1], {
-                    _width: 300, title: arr[1],
-                    x: newPoint[0],
-                    y: newPoint[1],
-                }));
-            }
+                    undoBatch(() => PreviewCursor._addDocument(Docs.Create.ImageDocument(
+                        arr[1], {
+                        _width: 300, title: arr[1],
+                        x: newPoint[0],
+                        y: newPoint[1],
+                    })))();
+                }
         }
     }
 
@@ -125,7 +138,7 @@ export class PreviewCursor extends React.Component<{}> {
         onKeyPress: (e: KeyboardEvent) => void,
         addLiveText: (doc: Doc) => void,
         getTransform: () => Transform,
-        addDocument: (doc: Doc) => boolean,
+        addDocument: (doc: Doc | Doc[]) => boolean,
         nudge: (nudgeX: number, nudgeY: number) => boolean) {
         this._clickPoint = [x, y];
         this._onKeyPress = onKeyPress;
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index f6fcc1ac4..8cd34e7ed 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -396,7 +396,7 @@ class TreeView extends React.Component<TreeViewProps> {
     }
 
     showContextMenu = (e: React.MouseEvent) => {
-        simulateMouseClick(this._docRef.current!.ContentDiv!, e.clientX, e.clientY + 30, e.screenX, e.screenY + 30);
+        this._docRef.current?.ContentDiv && simulateMouseClick(this._docRef.current.ContentDiv, e.clientX, e.clientY + 30, e.screenX, e.screenY + 30);
         e.stopPropagation();
     }
     focusOnDoc = (doc: Doc) => DocumentManager.Instance.getFirstDocumentView(doc)?.props.focus(doc, true);
@@ -772,6 +772,9 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
         </div >;
     }
 
+    onKeyPress = (e: React.KeyboardEvent) => {
+        console.log(e);
+    }
     render() {
         if (!(this.props.Document instanceof Doc)) return (null);
         const dropAction = StrCast(this.props.Document.childDropAction) as dropActionType;
@@ -786,6 +789,7 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
                     paddingRight: `${NumCast(this.props.Document._xPadding, 10)}px`,
                     paddingTop: `${NumCast(this.props.Document._yPadding, 20)}px`
                 }}
+                onKeyPress={this.onKeyPress}
                 onContextMenu={this.onContextMenu}
                 onWheel={(e: React.WheelEvent) => this._mainEle && this._mainEle.scrollHeight > this._mainEle.clientHeight && e.stopPropagation()}
                 onDrop={this.onTreeDrop}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 6cc0cfcd2..1bc59c727 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1246,8 +1246,16 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         return false;
     });
     @computed get marqueeView() {
-        return <MarqueeView {...this.props} nudge={this.nudge} activeDocuments={this.getActiveDocuments} selectDocuments={this.selectDocuments} addDocument={this.addDocument}
-            addLiveTextDocument={this.addLiveTextBox} getContainerTransform={this.getContainerTransform} getTransform={this.getTransform} isAnnotationOverlay={this.isAnnotationOverlay}>
+        return <MarqueeView {...this.props}
+            nudge={this.nudge}
+            addDocTab={this.addDocTab}
+            activeDocuments={this.getActiveDocuments}
+            selectDocuments={this.selectDocuments}
+            addDocument={this.addDocument}
+            addLiveTextDocument={this.addLiveTextBox}
+            getContainerTransform={this.getContainerTransform}
+            getTransform={this.getTransform}
+            isAnnotationOverlay={this.isAnnotationOverlay}>
             <CollectionFreeFormViewPannableContents centeringShiftX={this.centeringShiftX} centeringShiftY={this.centeringShiftY} shifted={!this.nativeHeight && !this.isAnnotationOverlay}
                 easing={this.easing} viewDefDivClick={this.props.viewDefDivClick} zoomScaling={this.zoomScaling} panX={this.panX} panY={this.panY}>
                 {this.children}
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 1addea6a1..35d925bca 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -24,13 +24,10 @@ import React = require("react");
 interface MarqueeViewProps {
     getContainerTransform: () => Transform;
     getTransform: () => Transform;
-    addDocument: (doc: Doc) => boolean;
     activeDocuments: () => Doc[];
     selectDocuments: (docs: Doc[], ink: { Document: Doc, Ink: Map<any, any> }[]) => void;
-    removeDocument: (doc: Doc) => boolean;
     addLiveTextDocument: (doc: Doc) => void;
     isSelected: () => boolean;
-    isAnnotationOverlay?: boolean;
     nudge: (x: number, y: number) => boolean;
     setPreviewCursor?: (func: (x: number, y: number, drag: boolean) => void) => void;
 }
@@ -248,7 +245,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
         } else {
             this._downX = x;
             this._downY = y;
-            PreviewCursor.Show(x, y, this.onKeyPress, this.props.addLiveTextDocument, this.props.getTransform, this.props.addDocument, this.props.nudge);
+            PreviewCursor.Show(x, y, this.onKeyPress, this.props.addLiveTextDocument, this.props.getTransform, this.props.addDocument, this.props.addDocTab, this.props.nudge);
         }
     });
 
-- 
cgit v1.2.3-70-g09d2


From 9ec7cdfe8ee823afbf6eb80a0e586d99457a50d1 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Sun, 10 May 2020 10:31:07 -0400
Subject: fixed scaling of overlay collections

---
 .../collectionFreeForm/CollectionFreeFormView.tsx            | 12 +++++++++++-
 1 file changed, 11 insertions(+), 1 deletion(-)

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

diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 1bc59c727..ed3e50f45 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1,7 +1,7 @@
 import { library } from "@fortawesome/fontawesome-svg-core";
 import { faEye, faEyeSlash } from "@fortawesome/free-regular-svg-icons";
 import { faBraille, faChalkboard, faCompass, faCompressArrowsAlt, faExpandArrowsAlt, faFileUpload, faPaintBrush, faTable, faUpload } from "@fortawesome/free-solid-svg-icons";
-import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from "mobx";
+import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction, _allowStateChangesInsideComputed } from "mobx";
 import { observer } from "mobx-react";
 import { computedFn } from "mobx-utils";
 import { Doc, HeightSym, Opt, WidthSym, DocListCast } from "../../../../new_fields/Doc";
@@ -1061,9 +1061,19 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
                 bounds: this.childDataProvider(entry[1].pair.layout, entry[1].replica)
             }));
 
+        // size contents according to dimensions of containing panel and current scale factor of collection
+        if (this.props.isAnnotationOverlay) {
+            this.props.Document.scale = Math.max(1, NumCast(this.props.Document.scale));
+        } else if (this.props.PanelWidth() && this.props.Document[WidthSym]() && !this.props.isAnnotationOverlay) {
+            this.props.Document.scale = this.props.PanelWidth() / this.props.Document[WidthSym]() * NumCast(this.props.Document.scale, 1);
+            this.props.Document._width = this.props.PanelWidth();
+            this.props.Document._height = this.props.PanelHeight();
+        }
+
         return elements;
     }
 
+    @action
     componentDidMount() {
         super.componentDidMount?.();
         this._layoutComputeReaction = reaction(() => this.doLayoutComputation,
-- 
cgit v1.2.3-70-g09d2


From 5726d19a615385b0bcdf1e3a944056d31074edbb Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Sun, 10 May 2020 17:02:04 -0400
Subject: made opening docs on right not create aliases.  made clicking on an
 original document trigger replacing it with an alias.

---
 .../views/collections/CollectionDockingView.tsx    | 38 +++++++++++++++++-----
 .../views/collections/CollectionTreeView.tsx       |  2 +-
 src/client/views/collections/CollectionView.tsx    |  1 +
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  7 ++++
 src/client/views/nodes/DocumentView.tsx            | 13 +++++---
 5 files changed, 47 insertions(+), 14 deletions(-)

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

diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 6fdb96f0d..6e9b2386d 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -71,8 +71,6 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
         (window as any).ReactDOM = ReactDOM;
         DragManager.StartWindowDrag = this.StartOtherDrag;
     }
-    hack: boolean = false;
-    undohack: any = null;
     public StartOtherDrag = (e: any, dragDocs: Doc[]) => {
         let config: any;
         if (dragDocs.length === 1) {
@@ -192,6 +190,29 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
         return retVal;
     }
 
+    @undoBatch
+    @action
+    public static ReplaceTab(document: Doc, stack: any): Opt<Doc> {
+        if (!CollectionDockingView.Instance) return undefined;
+        const instance = CollectionDockingView.Instance;
+        const replaceTab = (doc: Doc, child: any): Opt<Doc> => {
+            for (let i = 0; i < child.contentItems.length; i++) {
+                if (child.contentItems[i].isRow || child.contentItems[i].isColumn || child.contentItems[i].isStack) {
+                    const val = replaceTab(doc, child.contentItems[i]);
+                    if (val) return val;
+                } else if (child.contentItems[i].config.component === "DocumentFrameRenderer" &&
+                    child.contentItems[i].config.props.documentId === doc[Id]) {
+                    const alias = Doc.MakeAlias(doc);
+                    child.contentItems[i].config.props.documentId = alias[Id];
+                    child.contentItems[i].config.title = alias.title;
+                    instance.stateChanged();
+                    return alias;
+                }
+            }
+            return undefined;
+        }
+        return replaceTab(document, instance._goldenLayout.root);
+    }
 
     //
     //  Creates a vertical split on the right side of the docking view, and then adds the Document to the right of that split
@@ -455,12 +476,6 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
         const json = JSON.stringify(this._goldenLayout.toConfig());
         this.props.Document.dockingConfig = json;
         this.updateDataField(json);
-
-        if (this.undohack && !this.hack) {
-            this.undohack.end();
-            this.undohack = undefined;
-        }
-        this.hack = false;
     }
 
     itemDropped = () => {
@@ -789,6 +804,13 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
             return CollectionDockingView.AddRightSplit(doc, libraryPath);
         } else if (location === "close") {
             return CollectionDockingView.CloseRightSplit(doc);
+        } else if (location === "replace") {
+            const alias = CollectionDockingView.ReplaceTab(doc, this._stack);
+            if (alias) {
+                runInAction(() => this._document = alias);
+                return true;
+            }
+            return false;
         } else {// if (location === "inPlace") {
             return CollectionDockingView.Instance.AddTab(this._stack, doc, libraryPath);
         }
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 8cd34e7ed..2f332e77d 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -108,7 +108,7 @@ class TreeView extends React.Component<TreeViewProps> {
             Doc.ComputeContentBounds(DocListCast(this.props.document[this.fieldKey]));
     }
 
-    @undoBatch openRight = () => this.props.addDocTab(this.props.dropAction === "alias" ? Doc.MakeAlias(this.props.document) : this.props.document, "onRight", this.props.libraryPath);
+    @undoBatch openRight = () => this.props.addDocTab(this.props.document, "onRight", this.props.libraryPath);
     @undoBatch move = (doc: Doc | Doc[], target: Doc | undefined, addDoc: (doc: Doc | Doc[]) => boolean) => {
         return this.props.document !== target && this.props.deleteDoc(doc) && addDoc(doc);
     }
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 0841d9680..ac9f64d94 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -128,6 +128,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
         const added = docs.filter(d => !docList.includes(d));
         if (added.length) {
             added.map(doc => doc.context = this.props.Document);
+            added.map(add => Doc.AddDocToList(Cast(Doc.UserDoc().myCatalog, Doc, null), "data", add));
             targetDataDoc[this.props.fieldKey] = new List<Doc>([...docList, ...added]);
             targetDataDoc[this.props.fieldKey + "-lastModified"] = new DateField(new Date(Date.now()));
         }
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index ed3e50f45..61081618a 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -339,6 +339,13 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         }
         this._hitCluster = this.props.Document.useClusters ? this.pickCluster(this.getTransform().transformPoint(e.clientX, e.clientY)) !== -1 : false;
         if (e.button === 0 && (!e.shiftKey || this._hitCluster) && !e.altKey && !e.ctrlKey && this.props.active(true)) {
+
+            if (!this.props.Document.aliasOf && !this.props.ContainingCollectionView) {
+                this.props.addDocTab(this.props.Document, "replace");
+                e.stopPropagation();
+                e.preventDefault();
+                return;
+            }
             document.removeEventListener("pointermove", this.onPointerMove);
             document.removeEventListener("pointerup", this.onPointerUp);
             document.addEventListener("pointermove", this.onPointerMove);
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index ebfc38a54..d2dabe7ec 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -305,11 +305,14 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                         }, console.log);
                         func();
                     } else {
-                        const fullScreenAlias = Doc.MakeAlias(this.props.Document);
-                        if (StrCast(fullScreenAlias.layoutKey) !== "layout_fullScreen" && fullScreenAlias.layout_fullScreen) {
-                            fullScreenAlias.layoutKey = "layout_fullScreen";
-                        }
-                        UndoManager.RunInBatch(() => this.props.addDocTab(fullScreenAlias, "inTab"), "double tap");
+                        UndoManager.RunInBatch(() => {
+                            if (StrCast(this.props.Document.layoutKey) !== "layout_fullScreen" && this.props.Document.layout_fullScreen) {
+                                const fullScreenAlias = Doc.MakeAlias(this.props.Document);
+                                fullScreenAlias.layoutKey = "layout_fullScreen";
+                                this.props.addDocTab(fullScreenAlias, "inTab");
+                                this.props.addDocTab(this.props.Document, "inTab");
+                            }
+                        }, "double tap");
                         SelectionManager.DeselectAll();
                         Doc.UnBrushDoc(this.props.Document);
                     }
-- 
cgit v1.2.3-70-g09d2


From 56161b9541a8232fca11e69ffb1bf22864a941a1 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Mon, 11 May 2020 01:51:12 -0400
Subject: compile error fixes. fix to freeform view zooming when zoomed way
 out.

---
 src/client/views/GlobalKeyHandler.ts                                  | 2 +-
 src/client/views/TemplateMenu.tsx                                     | 4 ++--
 src/client/views/collections/CollectionDockingView.tsx                | 2 +-
 .../views/collections/collectionFreeForm/CollectionFreeFormView.tsx   | 2 +-
 src/client/views/collections/collectionFreeForm/MarqueeView.tsx       | 2 +-
 src/client/views/nodes/DocumentView.tsx                               | 1 +
 6 files changed, 7 insertions(+), 6 deletions(-)

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

diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index 3fd14735b..1b16b15bb 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -265,7 +265,7 @@ export default class KeyManager {
                     const bds = DocumentDecorations.Instance.Bounds;
                     const pt = [bds.x + (bds.r - bds.x) / 2, bds.y + (bds.b - bds.y) / 2];
                     const text = `__DashDocId(${pt[0]},${pt[1]}):` + SelectionManager.SelectedDocuments().map(dv => dv.Document[Id]).join(":");
-                    SelectionManager.SelectedDocuments().length && navigator.clipboard.writeText(text);;
+                    SelectionManager.SelectedDocuments().length && navigator.clipboard.writeText(text);
                     stopPropagation = false;
                     preventDefault = false;
                 }
diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx
index 5f300372f..43debfe99 100644
--- a/src/client/views/TemplateMenu.tsx
+++ b/src/client/views/TemplateMenu.tsx
@@ -113,8 +113,8 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
     render() {
         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));
-        const addedTypes = DocListCast(Cast(Doc.UserDoc().templateButtons, Doc, null)?.data);
+        const noteTypes = DocListCast(Cast(Doc.UserDoc()["template-notes"], Doc, null)?.data);
+        const addedTypes = 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) =>
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 6e9b2386d..296b2453b 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -210,7 +210,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
                 }
             }
             return undefined;
-        }
+        };
         return replaceTab(document, instance._goldenLayout.root);
     }
 
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 61081618a..d24ba6bb0 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -724,7 +724,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         const [x, y] = this.getTransform().transformPoint(pointX, pointY);
         const localTransform = this.getLocalTransform().inverse().scaleAbout(deltaScale, x, y);
 
-        if (localTransform.Scale >= 0.15) {
+        if (localTransform.Scale >= 0.15 || localTransform.Scale > this.zoomScaling()) {
             const safeScale = Math.min(Math.max(0.15, localTransform.Scale), 40);
             this.props.Document.scale = Math.abs(safeScale);
             this.setPan(-localTransform.TranslateX / safeScale, -localTransform.TranslateY / safeScale);
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 35d925bca..8e20b39d2 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -245,7 +245,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
         } else {
             this._downX = x;
             this._downY = y;
-            PreviewCursor.Show(x, y, this.onKeyPress, this.props.addLiveTextDocument, this.props.getTransform, this.props.addDocument, this.props.addDocTab, this.props.nudge);
+            PreviewCursor.Show(x, y, this.onKeyPress, this.props.addLiveTextDocument, this.props.getTransform, this.props.addDocument, this.props.nudge);
         }
     });
 
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index d2dabe7ec..aca0433bb 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -310,6 +310,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                                 const fullScreenAlias = Doc.MakeAlias(this.props.Document);
                                 fullScreenAlias.layoutKey = "layout_fullScreen";
                                 this.props.addDocTab(fullScreenAlias, "inTab");
+                            } else {
                                 this.props.addDocTab(this.props.Document, "inTab");
                             }
                         }, "double tap");
-- 
cgit v1.2.3-70-g09d2


From a3839d4b5a2180e2e80fa79f6eb4459e2976f380 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Mon, 11 May 2020 14:59:54 -0400
Subject: changed Cors behavior for WebBox(On for everything except
 GoogldDocs).  Update Freeze to work for ctrl-resizing text, and showing full
 screen.  reorged context menus.

---
 src/client/views/DocumentButtonBar.tsx             |  2 +-
 src/client/views/DocumentDecorations.scss          |  1 +
 src/client/views/DocumentDecorations.tsx           |  1 +
 src/client/views/MainView.tsx                      |  1 +
 src/client/views/PreviewCursor.tsx                 |  2 +-
 .../views/collections/CollectionDockingView.tsx    | 12 ++++----
 .../views/collections/CollectionStackingView.tsx   |  1 +
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 27 +++++++++---------
 src/client/views/nodes/DocumentView.tsx            | 25 ++++++----------
 src/client/views/nodes/ImageBox.tsx                | 33 +++++++++++++---------
 src/client/views/nodes/WebBox.tsx                  |  8 ++----
 .../views/nodes/formattedText/FormattedTextBox.tsx | 18 +++++++++++-
 .../formattedText/FormattedTextBoxComment.tsx      |  2 +-
 src/new_fields/Doc.ts                              | 26 +++++++++++------
 .../authentication/models/current_user_utils.ts    |  2 +-
 15 files changed, 94 insertions(+), 67 deletions(-)

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

diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index 48685302a..10d9ec401 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -194,7 +194,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
                     e.preventDefault();
                     let googleDoc = await Cast(dataDoc.googleDoc, Doc);
                     if (!googleDoc) {
-                        const options = { _width: 600, _nativeWidth: 960, _nativeHeight: 800, isAnnotating: false };
+                        const options = { _width: 600, _nativeWidth: 960, _nativeHeight: 800, isAnnotating: false, UseCors: false };
                         googleDoc = Docs.Create.WebDocument(googleDocUrl, options);
                         dataDoc.googleDoc = googleDoc;
                     }
diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss
index 4f34eb9e3..15eb537da 100644
--- a/src/client/views/DocumentDecorations.scss
+++ b/src/client/views/DocumentDecorations.scss
@@ -72,6 +72,7 @@ $linkGap : 3px;
         grid-column-start: 5;
         grid-column-end: 7;
         border-radius: 100%;
+        background: dimgray;
 
         .borderRadiusTooltip {
             width: 10px;
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 1054822c7..9669c21a9 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -301,6 +301,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
         }
 
         SelectionManager.SelectedDocuments().forEach(action((element: DocumentView) => {
+            if (e.ctrlKey && !element.props.Document._nativeHeight) element.toggleNativeDimensions();
             if (dX !== 0 || dY !== 0 || dW !== 0 || dH !== 0) {
                 const doc = Document(element.rootDoc);
                 let nwidth = doc._nativeWidth || 0;
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index cbaae63bc..560dd5d11 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -249,6 +249,7 @@ export class MainView extends React.Component {
             _width: this._panelWidth * .7,
             _height: this._panelHeight,
             title: "Collection " + workspaceCount,
+            _LODdisable: true
         };
         const freeformDoc = CurrentUserUtils.GuestTarget || Docs.Create.FreeformDocument([], freeformOptions);
         const mainDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600, path: [Doc.UserDoc().myCatalog as Doc] }], { title: `Workspace ${workspaceCount}` }, id, "row");
diff --git a/src/client/views/PreviewCursor.tsx b/src/client/views/PreviewCursor.tsx
index 14b5b3fce..2ddca369a 100644
--- a/src/client/views/PreviewCursor.tsx
+++ b/src/client/views/PreviewCursor.tsx
@@ -46,7 +46,7 @@ export class PreviewCursor extends React.Component<{}> {
                 else if (re.test(e.clipboardData.getData("text/plain"))) {
                     const url = e.clipboardData.getData("text/plain");
                     undoBatch(() => PreviewCursor._addDocument(Docs.Create.WebDocument(url, {
-                        title: url, _width: 500, _height: 300,
+                        title: url, _width: 500, _height: 300, UseCors: true,
                         // nativeWidth: 300, nativeHeight: 472.5,
                         x: newPoint[0], y: newPoint[1]
                     })))();
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 296b2453b..50e3b73eb 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -755,8 +755,10 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
     }
 
     get layoutDoc() { return this._document && Doc.Layout(this._document); }
-    panelWidth = () => this.layoutDoc && this.layoutDoc.maxWidth ? Math.min(Math.max(NumCast(this.layoutDoc._width), NumCast(this.layoutDoc._nativeWidth)), this._panelWidth) : this._panelWidth;
-    panelHeight = () => this._panelHeight;
+    nativeAspect = () => this.nativeWidth() ? this.nativeWidth() / this.nativeHeight() : 0;
+    panelWidth = () => this.layoutDoc?.maxWidth ? Math.min(Math.max(NumCast(this.layoutDoc._width), NumCast(this.layoutDoc._nativeWidth)), this._panelWidth) :
+        (this.nativeAspect() && this.nativeAspect() < this._panelWidth / this._panelHeight ? this._panelHeight * this.nativeAspect() : this._panelWidth);
+    panelHeight = () => this.nativeAspect() && this.nativeAspect() > this._panelWidth / this._panelHeight ? this._panelWidth / this.nativeAspect() : this._panelHeight;
 
     nativeWidth = () => !this.layoutDoc!._fitWidth ? NumCast(this.layoutDoc!._nativeWidth) || this._panelWidth : 0;
     nativeHeight = () => !this.layoutDoc!._fitWidth ? NumCast(this.layoutDoc!._nativeHeight) || this._panelHeight : 0;
@@ -794,7 +796,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
         return Transform.Identity();
     }
     get previewPanelCenteringOffset() { return this.nativeWidth() ? (this._panelWidth - this.nativeWidth() * this.contentScaling()) / 2 : 0; }
-    get widthpercent() { return this.nativeWidth() ? `${(this.nativeWidth() * this.contentScaling()) / this.panelWidth() * 100}%` : undefined; }
+    get widthpercent() { return this.nativeWidth() ? `${(this.nativeWidth() * this.contentScaling()) / this._panelWidth * 100}%` : undefined; }
 
     addDocTab = (doc: Doc, location: string, libraryPath?: Doc[]) => {
         SelectionManager.DeselectAll();
@@ -832,8 +834,8 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
             ContentScaling={this.contentScaling}
             PanelWidth={this.panelWidth}
             PanelHeight={this.panelHeight}
-            NativeHeight={returnZero}
-            NativeWidth={returnZero}
+            NativeHeight={this.nativeHeight}
+            NativeWidth={this.nativeWidth}
             ScreenToLocalTransform={this.ScreenToLocalTransform}
             renderDepth={0}
             parentActive={returnTrue}
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index c199988c0..979e9e3f8 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -432,6 +432,7 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument)
         if (!e.isPropagationStopped()) {
             const subItems: ContextMenuProps[] = [];
             subItems.push({ description: `${this.props.Document.fillColumn ? "Variable Size" : "Autosize"} Column`, event: () => this.props.Document.fillColumn = !this.props.Document.fillColumn, icon: "plus" });
+            subItems.push({ description: `${this.Document._autoHeight ? "Variable Height" : "Auto Height"}`, event: () => this.layoutDoc._autoHeight = !this.layoutDoc._autoHeight, icon: "plus" });
             ContextMenu.Instance.addItem({ description: "Options...", subitems: subItems, icon: "eye" });
         }
     }
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index d24ba6bb0..129b245b8 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -113,8 +113,8 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
             this.props.PanelWidth() / (this.contentBounds.r - this.contentBounds.x)) :
         this.Document.scale || 1)
 
-    private centeringShiftX = () => !this.nativeWidth && !this.isAnnotationOverlay ? this.props.PanelWidth() / 2 / this.parentScaling : 0;  // shift so pan position is at center of window for non-overlay collections
-    private centeringShiftY = () => !this.nativeHeight && !this.isAnnotationOverlay ? this.props.PanelHeight() / 2 / this.parentScaling : 0;// shift so pan position is at center of window for non-overlay collections
+    private centeringShiftX = () => !this.isAnnotationOverlay ? this.props.PanelWidth() / 2 / this.parentScaling / this.contentScaling : 0;  // shift so pan position is at center of window for non-overlay collections
+    private centeringShiftY = () => !this.isAnnotationOverlay ? this.props.PanelHeight() / 2 / this.parentScaling / this.contentScaling : 0;// shift so pan position is at center of window for non-overlay collections
     private getTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-this.borderWidth + 1, -this.borderWidth + 1).translate(-this.centeringShiftX(), -this.centeringShiftY()).transform(this.getLocalTransform());
     private getTransformOverlay = (): Transform => this.props.ScreenToLocalTransform().translate(-this.borderWidth + 1, -this.borderWidth + 1);
     private getContainerTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-this.borderWidth, -this.borderWidth);
@@ -340,12 +340,12 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         this._hitCluster = this.props.Document.useClusters ? this.pickCluster(this.getTransform().transformPoint(e.clientX, e.clientY)) !== -1 : false;
         if (e.button === 0 && (!e.shiftKey || this._hitCluster) && !e.altKey && !e.ctrlKey && this.props.active(true)) {
 
-            if (!this.props.Document.aliasOf && !this.props.ContainingCollectionView) {
-                this.props.addDocTab(this.props.Document, "replace");
-                e.stopPropagation();
-                e.preventDefault();
-                return;
-            }
+            // if (!this.props.Document.aliasOf && !this.props.ContainingCollectionView) {
+            //     this.props.addDocTab(this.props.Document, "replace");
+            //     e.stopPropagation();
+            //     e.preventDefault();
+            //     return;
+            // }
             document.removeEventListener("pointermove", this.onPointerMove);
             document.removeEventListener("pointerup", this.onPointerUp);
             document.addEventListener("pointermove", this.onPointerMove);
@@ -1068,13 +1068,8 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
                 bounds: this.childDataProvider(entry[1].pair.layout, entry[1].replica)
             }));
 
-        // size contents according to dimensions of containing panel and current scale factor of collection
         if (this.props.isAnnotationOverlay) {
             this.props.Document.scale = Math.max(1, NumCast(this.props.Document.scale));
-        } else if (this.props.PanelWidth() && this.props.Document[WidthSym]() && !this.props.isAnnotationOverlay) {
-            this.props.Document.scale = this.props.PanelWidth() / this.props.Document[WidthSym]() * NumCast(this.props.Document.scale, 1);
-            this.props.Document._width = this.props.PanelWidth();
-            this.props.Document._height = this.props.PanelHeight();
         }
 
         return elements;
@@ -1129,6 +1124,11 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
             });
         }, "arrange contents");
     }
+    @undoBatch
+    @action
+    toggleNativeDimensions = () => {
+        Doc.toggleNativeDimensions(this.layoutDoc, this.props.ContentScaling(), this.props.NativeWidth(), this.props.NativeHeight());
+    }
 
     private thumbIdentifier?: number;
 
@@ -1138,6 +1138,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         const optionItems: ContextMenuProps[] = options && "subitems" in options ? options.subitems : [];
 
         optionItems.push({ description: "reset view", event: () => { this.props.Document._panX = this.props.Document._panY = 0; this.props.Document.scale = 1; }, icon: "compress-arrows-alt" });
+        optionItems.push({ description: !this.layoutDoc._nativeWidth || !this.layoutDoc._nativeHeight ? "Freeze" : "Unfreeze", event: this.toggleNativeDimensions, icon: "snowflake" });
         optionItems.push({ description: `${this.Document._LODdisable ? "Enable LOD" : "Disable LOD"}`, event: () => this.Document._LODdisable = !this.Document._LODdisable, icon: "table" });
         optionItems.push({ description: `${this.fitToContent ? "Unset" : "Set"} Fit To Container`, event: () => this.Document._fitToBox = !this.fitToContent, icon: !this.fitToContent ? "expand-arrows-alt" : "compress-arrows-alt" });
         optionItems.push({ description: `${this.Document.useClusters ? "Uncluster" : "Use Clusters"}`, event: () => this.updateClusters(!this.Document.useClusters), icon: "braille" });
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index aca0433bb..6c9ade83f 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -648,18 +648,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
 
     @undoBatch
     @action
-    public static unfreezeNativeDimensions(layoutDoc: Doc) {
-        layoutDoc._nativeWidth = undefined;
-        layoutDoc._nativeHeight = undefined;
-    }
-
     toggleNativeDimensions = () => {
-        if (this.Document._nativeWidth || this.Document._nativeHeight) {
-            DocumentView.unfreezeNativeDimensions(this.layoutDoc);
-        }
-        else {
-            Doc.freezeNativeDimensions(this.layoutDoc, this.props.PanelWidth(), this.props.PanelHeight());
-        }
+        Doc.toggleNativeDimensions(this.layoutDoc, this.props.ContentScaling(), this.props.PanelWidth(), this.props.PanelHeight());
     }
 
     @undoBatch
@@ -735,10 +725,6 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
 
         let options = cm.findByDescription("Options...");
         const optionItems: ContextMenuProps[] = options && "subitems" in options ? options.subitems : [];
-        optionItems.push({ description: `${this.Document._chromeStatus !== "disabled" ? "Hide" : "Show"} Chrome`, event: () => this.Document._chromeStatus = (this.Document._chromeStatus !== "disabled" ? "disabled" : "enabled"), icon: "project-diagram" });
-        optionItems.push({ description: `${this.Document._autoHeight ? "Variable Height" : "Auto Height"}`, event: () => this.layoutDoc._autoHeight = !this.layoutDoc._autoHeight, icon: "plus" });
-        optionItems.push({ description: this.Document.lockedPosition ? "Unlock Position" : "Lock Position", event: this.toggleLockPosition, icon: BoolCast(this.Document.lockedPosition) ? "unlock" : "lock" });
-        optionItems.push({ description: this.Document.lockedTransform ? "Unlock Transform" : "Lock Transform", event: this.toggleLockTransform, icon: BoolCast(this.Document.lockedTransform) ? "unlock" : "lock" });
         if (!options) {
             options = { description: "Options...", subitems: optionItems, icon: "compass" };
             cm.addItem(options);
@@ -757,6 +743,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         onClicks.push({ description: "Edit onClick Script", event: () => UndoManager.RunInBatch(() => Doc.makeCustomViewClicked(this.props.Document, undefined, "onClick"), "edit onClick"), icon: "edit" });
         !existingOnClick && cm.addItem({ description: "OnClick...", subitems: onClicks, icon: "hand-point-right" });
 
+
         const funcs: ContextMenuProps[] = [];
         if (this.Document.onDragStart) {
             funcs.push({ description: "Drag an Alias", icon: "edit", event: () => this.Document.dragFactory && (this.Document.onDragStart = ScriptField.MakeFunction('getAlias(this.dragFactory)')) });
@@ -768,7 +755,9 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         const more = cm.findByDescription("More...");
         const moreItems: ContextMenuProps[] = more && "subitems" in more ? more.subitems : [];
         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._nativeWidth || !this.Document._nativeHeight ? "Freeze" : "Unfreeze", event: this.toggleNativeDimensions, icon: "snowflake" });
+        moreItems.push({ description: `${this.Document._chromeStatus !== "disabled" ? "Hide" : "Show"} Chrome`, event: () => this.Document._chromeStatus = (this.Document._chromeStatus !== "disabled" ? "disabled" : "enabled"), icon: "project-diagram" });
+        moreItems.push({ description: this.Document.lockedTransform ? "Unlock Transform" : "Lock Transform", event: this.toggleLockTransform, icon: BoolCast(this.Document.lockedTransform) ? "unlock" : "lock" });
+        moreItems.push({ description: this.Document.lockedPosition ? "Unlock Position" : "Lock Position", event: this.toggleLockPosition, icon: BoolCast(this.Document.lockedPosition) ? "unlock" : "lock" });
 
         if (!ClientUtils.RELEASE) {
             // let copies: ContextMenuProps[] = [];
@@ -794,6 +783,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             // a.download = `DocExport-${this.props.Document[Id]}.zip`;
             // a.click();
         });
+
         const recommender_subitems: ContextMenuProps[] = [];
 
         recommender_subitems.push({
@@ -826,6 +816,9 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         moreItems.push({ description: "Publish", event: () => DocUtils.Publish(this.props.Document, this.Document.title || "", this.props.addDocument, this.props.removeDocument), icon: "file" });
         moreItems.push({ description: "Undo Debug Test", event: () => UndoManager.TraceOpenBatches(), icon: "exclamation" });
         !more && cm.addItem({ description: "More...", subitems: moreItems, icon: "hand-point-right" });
+
+        cm.moveAfter(cm.findByDescription("More...")!, cm.findByDescription("OnClick...")!);
+
         runInAction(() => {
             if (!ClientUtils.RELEASE) {
                 const setWriteMode = (mode: DocServer.WriteMode) => {
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 4153c184e..d85373e98 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -157,21 +157,20 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageD
         const field = Cast(this.dataDoc[this.fieldKey], ImageField);
         if (field) {
             const funcs: ContextMenuProps[] = [];
-            funcs.push({ description: "Copy path", event: () => Utils.CopyText(field.url.href), icon: "expand-arrows-alt" });
             funcs.push({ description: "Rotate", event: this.rotate, icon: "expand-arrows-alt" });
-            funcs.push({
-                description: "Reset Native Dimensions", event: action(async () => {
-                    const curNW = NumCast(this.dataDoc[this.fieldKey + "-nativeWidth"]);
-                    const curNH = NumCast(this.dataDoc[this.fieldKey + "-nativeHeight"]);
-                    if (this.props.PanelWidth() / this.props.PanelHeight() > curNW / curNH) {
-                        this.dataDoc[this.fieldKey + "-nativeWidth"] = this.props.PanelHeight() * curNW / curNH;
-                        this.dataDoc[this.fieldKey + "-nativeHeight"] = this.props.PanelHeight();
-                    } else {
-                        this.dataDoc[this.fieldKey + "-nativeWidth"] = this.props.PanelWidth();
-                        this.dataDoc[this.fieldKey + "-nativeHeight"] = this.props.PanelWidth() * curNH / curNW;
-                    }
-                }), icon: "expand-arrows-alt"
-            });
+            // funcs.push({
+            //     description: "Reset Native Dimensions", event: action(async () => {
+            //         const curNW = NumCast(this.dataDoc[this.fieldKey + "-nativeWidth"]);
+            //         const curNH = NumCast(this.dataDoc[this.fieldKey + "-nativeHeight"]);
+            //         if (this.props.PanelWidth() / this.props.PanelHeight() > curNW / curNH) {
+            //             this.dataDoc[this.fieldKey + "-nativeWidth"] = this.props.PanelHeight() * curNW / curNH;
+            //             this.dataDoc[this.fieldKey + "-nativeHeight"] = this.props.PanelHeight();
+            //         } else {
+            //             this.dataDoc[this.fieldKey + "-nativeWidth"] = this.props.PanelWidth();
+            //             this.dataDoc[this.fieldKey + "-nativeHeight"] = this.props.PanelWidth() * curNH / curNW;
+            //         }
+            //     }), icon: "expand-arrows-alt"
+            // });
 
             const existingAnalyze = ContextMenu.Instance.findByDescription("Analyzers...");
             const modes: ContextMenuProps[] = existingAnalyze && "subitems" in existingAnalyze ? existingAnalyze.subitems : [];
@@ -181,6 +180,12 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageD
             !existingAnalyze && ContextMenu.Instance.addItem({ description: "Analyzers...", subitems: modes, icon: "hand-point-right" });
 
             ContextMenu.Instance.addItem({ description: "Options...", subitems: funcs, icon: "asterisk" });
+
+
+            const existingMore = ContextMenu.Instance.findByDescription("More...");
+            const mores: ContextMenuProps[] = existingMore && "subitems" in existingMore ? existingMore.subitems : [];
+            mores.push({ description: "Copy path", event: () => Utils.CopyText(field.url.href), icon: "expand-arrows-alt" });
+            !existingMore && ContextMenu.Instance.addItem({ description: "More...", subitems: mores, icon: "hand-point-right" });
         }
     }
 
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 8239d6fdf..384a6e8a5 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -129,14 +129,12 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
         }
     }
 
-    toggleNativeDimensions = () => {
+    toggleAnnotationMode = () => {
         if (!this.layoutDoc.isAnnotating) {
-            //DocumentView.unfreezeNativeDimensions(this.layoutDoc);
             this.layoutDoc.lockedTransform = false;
             this.layoutDoc.isAnnotating = true;
         }
         else {
-            //Doc.freezeNativeDimensions(this.layoutDoc, this.props.PanelWidth(), this.props.PanelHeight());
             this.layoutDoc.lockedTransform = true;
             this.layoutDoc.isAnnotating = false;
         }
@@ -158,10 +156,10 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
                             <FontAwesomeIcon icon="caret-up" size="2x" />
                         </button>
                         <div className="webBox-buttons" style={{ display: this._collapsed ? "none" : "flex" }}>
-                            <div className="webBox-freeze" title={"Annotate"} style={{ background: this.layoutDoc.isAnnotating ? "lightBlue" : "gray" }} onClick={this.toggleNativeDimensions} >
+                            <div className="webBox-freeze" title={"Annotate"} style={{ background: this.layoutDoc.isAnnotating ? "lightBlue" : "gray" }} onClick={this.toggleAnnotationMode} >
                                 <FontAwesomeIcon icon={faPen} size={"2x"} />
                             </div>
-                            <div className="webBox-freeze" title={"Select"} style={{ background: !this.layoutDoc.isAnnotating ? "lightBlue" : "gray" }} onClick={this.toggleNativeDimensions} >
+                            <div className="webBox-freeze" title={"Select"} style={{ background: !this.layoutDoc.isAnnotating ? "lightBlue" : "gray" }} onClick={this.toggleAnnotationMode} >
                                 <FontAwesomeIcon icon={faMousePointer} size={"2x"} />
                             </div>
                             <input className="webpage-urlInput"
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index f3c277e20..2c51397eb 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -405,6 +405,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         this.layoutDoc._sidebarWidthPercent = "" + 100 * (1 - (e.clientX - bounds.left) / bounds.width) + "%";
         return false;
     }
+    @undoBatch
+    @action
+    toggleNativeDimensions = () => {
+        Doc.toggleNativeDimensions(this.layoutDoc, this.props.ContentScaling(), this.props.NativeWidth(), this.props.NativeHeight());
+    }
 
     public static get DefaultLayout(): Doc | string | undefined {
         return Cast(Doc.UserDoc().defaultTextLayout, Doc, null) || StrCast(Doc.UserDoc().defaultTextLayout, null);
@@ -438,6 +443,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                 Doc.AddDocToList(Cast(Doc.UserDoc()["template-notes"], Doc, null), "data", this.rootDoc);
             }, icon: "eye"
         });
+        //funcs.push({ description: `${this.Document._autoHeight ? "Variable Height" : "Auto Height"}`, event: () => this.layoutDoc._autoHeight = !this.layoutDoc._autoHeight, icon: "plus" });
+        funcs.push({ description: !this.layoutDoc._nativeWidth || !this.layoutDoc._nativeHeight ? "Freeze" : "Unfreeze", event: this.toggleNativeDimensions, icon: "snowflake" });
         funcs.push({ description: "Toggle Single Line", event: () => this.layoutDoc._singleLine = !this.layoutDoc._singleLine, icon: "expand-arrows-alt" });
         funcs.push({ description: "Toggle Sidebar", event: () => this.layoutDoc._showSidebar = !this.layoutDoc._showSidebar, icon: "expand-arrows-alt" });
         funcs.push({ description: "Toggle Dictation Icon", event: () => this.layoutDoc._showAudio = !this.layoutDoc._showAudio, icon: "expand-arrows-alt" });
@@ -649,10 +656,14 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                 }
             }
         );
-        this._disposers.height = reaction(
+        this._disposers.autoHeight = reaction(
             () => [this.layoutDoc[WidthSym](), this.layoutDoc._autoHeight],
             () => this.tryUpdateHeight()
         );
+        this._disposers.height = reaction(
+            () => this.layoutDoc[HeightSym](),
+            height => height <= 20 && (this.layoutDoc._autoHeight = true)
+        );
 
         this.setupEditor(this.config, this.props.fieldKey);
 
@@ -1206,6 +1217,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
     render() {
         TraceMobx();
         const style: { [key: string]: any } = {};
+        const scale = this.props.ContentScaling() * NumCast(this.layoutDoc.scale, 1);
         const divKeys = ["width", "height", "background"];
         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 })?.script.run({ self: this.rootDoc, this: this.layoutDoc }).result as string || "";
@@ -1258,6 +1270,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                 <div className={`formattedTextBox-outer`} style={{ width: `calc(100% - ${this.sidebarWidthPercent})`, }} onScroll={this.onscrolled} ref={this._scrollRef}>
                     <div className={`formattedTextBox-inner${rounded}`} ref={this.createDropTarget}
                         style={{
+                            transform: `scale(${scale})`,
+                            transformOrigin: "top left",
+                            width: `${100 / scale}%`,
+                            height: `${100 / scale}%`,
                             padding: `${NumCast(this.layoutDoc._yMargin, this.props.yMargin || 0)}px  ${NumCast(this.layoutDoc._xMargin, this.props.xMargin || 0)}px`,
                             pointerEvents: ((this.layoutDoc.isLinkButton || this.props.onClick) && !this.props.isSelected()) ? "none" : undefined
                         }} />
diff --git a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
index 9ad5aafb8..a33717855 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
@@ -91,7 +91,7 @@ export class FormattedTextBoxComment {
                             (doc: Doc, followLinkLocation: string) => textBox.props.addDocTab(doc, e.ctrlKey ? "inTab" : followLinkLocation));
                     }
                 } else if (textBox && (FormattedTextBoxComment.tooltipText as any).href) {
-                    textBox.props.addDocTab(Docs.Create.WebDocument((FormattedTextBoxComment.tooltipText as any).href, { title: (FormattedTextBoxComment.tooltipText as any).href, _width: 200, _height: 400 }), "onRight");
+                    textBox.props.addDocTab(Docs.Create.WebDocument((FormattedTextBoxComment.tooltipText as any).href, { title: (FormattedTextBoxComment.tooltipText as any).href, _width: 200, _height: 400, UseCors: true }), "onRight");
                 }
                 keep && textBox && FormattedTextBoxComment.start !== undefined && textBox.adoptAnnotation(
                     FormattedTextBoxComment.start, FormattedTextBoxComment.end, FormattedTextBoxComment.mark);
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index e0627fb37..a1e1e11b1 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -20,7 +20,6 @@ import { deleteProperty, getField, getter, makeEditable, makeReadOnly, setter, u
 import { Docs, DocumentOptions } from "../client/documents/Documents";
 import { PdfField, VideoField, AudioField, ImageField } from "./URLField";
 import { LinkManager } from "../client/util/LinkManager";
-import { string32 } from "../../deploy/assets/pdf.worker";
 
 export namespace Field {
     export function toKeyValueString(doc: Doc, key: string): string {
@@ -936,19 +935,28 @@ export namespace Doc {
             }
         }
     }
-
-    export function freezeNativeDimensions(layoutDoc: Doc, width: number, height: number): void {
-        layoutDoc._autoHeight = false;
-        if (!layoutDoc._nativeWidth) {
-            layoutDoc._nativeWidth = NumCast(layoutDoc._width, width);
-            layoutDoc._nativeHeight = NumCast(layoutDoc._height, height);
-        }
-    }
     export function assignDocToField(doc: Doc, field: string, id: string) {
         DocServer.GetRefField(id).then(layout => layout instanceof Doc && (doc[field] = layout));
         return id;
     }
 
+    export function toggleNativeDimensions(layoutDoc: Doc, contentScale: number, panelWidth: number, panelHeight: number) {
+        runInAction(() => {
+            if (layoutDoc._nativeWidth || layoutDoc._nativeHeight) {
+                layoutDoc.scale = NumCast(layoutDoc.scale, 1) * contentScale;
+                layoutDoc._nativeWidth = undefined;
+                layoutDoc._nativeHeight = undefined;
+            }
+            else {
+                layoutDoc._autoHeight = false;
+                if (!layoutDoc._nativeWidth) {
+                    layoutDoc._nativeWidth = NumCast(layoutDoc._width, panelWidth);
+                    layoutDoc._nativeHeight = NumCast(layoutDoc._height, panelHeight);
+                }
+            }
+        });
+    }
+
     export function isDocPinned(doc: Doc) {
         //add this new doc to props.Document
         const curPres = Cast(Doc.UserDoc().activePresentation, Doc) as Doc;
diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts
index bebf77a8c..b6ff10bab 100644
--- a/src/server/authentication/models/current_user_utils.ts
+++ b/src/server/authentication/models/current_user_utils.ts
@@ -315,7 +315,7 @@ export class CurrentUserUtils {
                 { _width: 250, _height: 250, title: "container" });
         }
         if (doc.emptyWebpage === undefined) {
-            doc.emptyWebpage = Docs.Create.WebDocument("", { title: "New Webpage", _width: 600 });
+            doc.emptyWebpage = Docs.Create.WebDocument("", { title: "New Webpage", _width: 600, UseCors: true });
         }
         return [
             { title: "Drag a collection", label: "Col", icon: "folder", click: 'openOnRight(getCopy(this.dragFactory, true))', drag: 'getCopy(this.dragFactory, true)', dragFactory: doc.emptyCollection as Doc },
-- 
cgit v1.2.3-70-g09d2


From f1473fc5bf7f2b3109be358ae14d28725240284c Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Mon, 11 May 2020 22:30:33 -0400
Subject: cleaned up menu items.  improved snapping with fixed aspect items,
 but not perfect.

---
 src/client/documents/Documents.ts                  |  1 +
 src/client/util/DragManager.ts                     | 14 ++--
 src/client/views/DocumentDecorations.tsx           | 41 ++++++++++--
 src/client/views/MainView.tsx                      |  4 +-
 .../views/collections/CollectionDockingView.tsx    |  2 +-
 src/client/views/collections/CollectionView.tsx    |  1 -
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 21 +++---
 src/client/views/nodes/DocumentView.tsx            | 75 ++++++++--------------
 src/client/views/nodes/ImageBox.tsx                |  4 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx | 37 ++++-------
 10 files changed, 101 insertions(+), 99 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index a87a77e1d..95b8daafc 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -150,6 +150,7 @@ export interface DocumentOptions {
     dragFactory?: Doc; // document to create when dragging with a suitable onDragStart 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
     clipboard?: Doc;
+    UseCors?: boolean;
     icon?: string;
     sourcePanel?: Doc; // panel to display in 'targetContainer' as the result of a button onClick script
     targetContainer?: Doc; // document whose proto will be set to 'panel' as the result of a onClick click script
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 4f547e2f7..1ee4c57a2 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -256,7 +256,8 @@ export namespace DragManager {
         SnappingManager.setSnapLines(horizLines, vertLines);
     }
 
-    export function snapDrag(e: PointerEvent, xFromLeft: number, yFromTop: number, xFromRight: number, yFromBottom: number) {
+    // 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, oneAxis: boolean = false) {
         const snapThreshold = NumCast(Doc.UserDoc()["constants-snapThreshold"], 10);
         const snapVal = (pts: number[], drag: number, snapLines: number[]) => {
             if (snapLines.length) {
@@ -265,14 +266,15 @@ export namespace DragManager {
                 const closestPts = rangePts.map(pt => snapLines.reduce((nearest, curr) => Math.abs(nearest - pt) > Math.abs(curr - pt) ? curr : nearest));
                 const closestDists = rangePts.map((pt, i) => Math.abs(pt - closestPts[i]));
                 const minIndex = closestDists[0] < closestDists[1] && closestDists[0] < closestDists[2] ? 0 : closestDists[1] < closestDists[2] ? 1 : 2;
-                return closestDists[minIndex] < snapThreshold ? closestPts[minIndex] + offs[minIndex] : drag;
+                return closestDists[minIndex] < snapThreshold ? [closestDists[minIndex], closestPts[minIndex] + offs[minIndex]] : [Number.MAX_VALUE, drag];
             }
-            return drag;
+            return [Number.MAX_VALUE, drag];
         };
-
+        const xsnap = snapVal([xFromLeft, xFromRight], e.pageX, SnappingManager.vertSnapLines());
+        const ysnap = snapVal([yFromTop, yFromBottom], e.pageY, SnappingManager.horizSnapLines());
         return {
-            thisX: snapVal([xFromLeft, xFromRight], e.pageX, SnappingManager.vertSnapLines()),
-            thisY: snapVal([yFromTop, yFromBottom], e.pageY, SnappingManager.horizSnapLines())
+            thisX: !oneAxis || xsnap[0] < ysnap[0] ? xsnap[1] : e.pageX,
+            thisY: !oneAxis || xsnap[0] > ysnap[0] ? ysnap[1] : e.pageY
         };
     }
     export let docsBeingDragged: Doc[] = [];
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index a3c476125..b939d96b6 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -20,7 +20,6 @@ import React = require("react");
 import { Id } from '../../new_fields/FieldSymbols';
 import e = require('express');
 import { CollectionDockingView } from './collections/CollectionDockingView';
-import { MainView } from './MainView';
 import { SnappingManager } from '../util/SnappingManager';
 
 library.add(faCaretUp);
@@ -238,6 +237,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
     }
 
     _initialAutoHeight = false;
+    _dragHeights = new Map<Doc, number>();
     @action
     onPointerDown = (e: React.PointerEvent): void => {
         setupMoveUpEvents(this, e, this.onPointerMove, this.onPointerUp, (e) => { });
@@ -253,17 +253,38 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
         this._snapX = e.pageX;
         this._snapY = e.pageY;
         this._initialAutoHeight = true;
+        DragManager.docsBeingDragged = SelectionManager.SelectedDocuments().map(dv => dv.rootDoc);
+        SelectionManager.SelectedDocuments().map(dv => {
+            this._dragHeights.set(dv.layoutDoc, NumCast(dv.layoutDoc._height));
+            dv.layoutDoc._delayAutoHeight = dv.layoutDoc._height;
+        });
     }
 
     onPointerMove = (e: PointerEvent, down: number[], move: number[]): boolean => {
-        const { thisX, thisY } = DragManager.snapDrag(e, -this._offX, -this._offY, this._offX, this._offY);
+        const first = SelectionManager.SelectedDocuments()[0];
+        const fixedAspect = NumCast(first.layoutDoc._nativeWidth) !== 0;
+        let { thisX, thisY } = DragManager.snapDrag(e, -this._offX, -this._offY, this._offX, this._offY, fixedAspect);
+        if (fixedAspect) {  // if aspect is set, then any snapped movement must be coerced to match the aspect ratio
+            const aspect = NumCast(first.layoutDoc._nativeWidth) / NumCast(first.layoutDoc._nativeHeight);
+            const deltaX = thisX - this._snapX;
+            const deltaY = thisY - this._snapY;
+            if (thisX !== e.pageX) {
+                const snapY = deltaX / aspect + this._snapY;
+                thisY = Math.abs(deltaX / aspect) < 10 ? snapY : thisY;
+            } else {
+                const snapX = deltaY * aspect + this._snapX;
+                thisX = Math.abs(deltaY * aspect) < 10 ? snapX : thisX;
+            }
+        }
         move[0] = thisX - this._snapX;
         move[1] = thisY - this._snapY;
         this._snapX = thisX;
         this._snapY = thisY;
 
         let dX = 0, dY = 0, dW = 0, dH = 0;
-
+        const unfreeze = () =>
+            SelectionManager.SelectedDocuments().forEach(action((element: DocumentView) =>
+                (element.rootDoc.type === DocumentType.RTF && element.layoutDoc._nativeHeight) && element.toggleNativeDimensions()));
         switch (this._resizeHdlId) {
             case "": break;
             case "documentDecorations-topLeftResizer":
@@ -278,6 +299,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
                 dH = -move[1];
                 break;
             case "documentDecorations-topResizer":
+                unfreeze();
                 dY = -1;
                 dH = -move[1];
                 break;
@@ -291,13 +313,16 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
                 dH = move[1];
                 break;
             case "documentDecorations-bottomResizer":
+                unfreeze();
                 dH = move[1];
                 break;
             case "documentDecorations-leftResizer":
+                unfreeze();
                 dX = -1;
                 dW = -move[0];
                 break;
             case "documentDecorations-rightResizer":
+                unfreeze();
                 dW = move[0];
                 break;
         }
@@ -309,9 +334,12 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
                 let nwidth = doc._nativeWidth || 0;
                 let nheight = doc._nativeHeight || 0;
                 const width = (doc._width || 0);
-                const height = (doc._height || (nheight / nwidth * width));
+                let height = (doc._height || (nheight / nwidth * width));
                 const scale = element.props.ScreenToLocalTransform().Scale * element.props.ContentScaling();
                 if (nwidth && nheight) {
+                    if (nwidth / nheight !== width / height) {
+                        height = nheight / nwidth * width;
+                    }
                     if (Math.abs(dW) > Math.abs(dH)) dH = dW * nheight / nwidth;
                     else dW = dH * nwidth / nheight;
                 }
@@ -365,7 +393,10 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
     @action
     onPointerUp = (e: PointerEvent): void => {
         SelectionManager.SelectedDocuments().map(dv => {
-            dv.layoutDoc._delayAutoHeight && (dv.layoutDoc._autoHeight = true);
+            if (NumCast(dv.layoutDoc._delayAutoHeight) < this._dragHeights.get(dv.layoutDoc)!) {
+                dv.nativeWidth > 0 && Doc.toggleNativeDimensions(dv.layoutDoc, dv.props.ContentScaling(), dv.panelWidth(), dv.panelHeight());
+                dv.layoutDoc._autoHeight = true;
+            }
             dv.layoutDoc._delayAutoHeight = undefined;
         });
         this._resizeHdlId = "";
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 62496b01f..9bfef06b4 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -83,14 +83,14 @@ export class MainView extends React.Component {
         firstScriptTag.parentNode!.insertBefore(tag, firstScriptTag);
         window.removeEventListener("keydown", KeyManager.Instance.handle);
         window.addEventListener("keydown", KeyManager.Instance.handle);
-        window.addEventListener("paste", KeyManager.Instance.paste);
+        window.addEventListener("paste", KeyManager.Instance.paste as any);
     }
 
     componentWillUnMount() {
         window.removeEventListener("keydown", KeyManager.Instance.handle);
         window.removeEventListener("pointerdown", this.globalPointerDown);
         window.removeEventListener("pointerup", this.globalPointerUp);
-        window.removeEventListener("paste", KeyManager.Instance.paste);
+        window.removeEventListener("paste", KeyManager.Instance.paste as any);
     }
 
     constructor(props: Readonly<{}>) {
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 50e3b73eb..2e24cbb12 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -757,7 +757,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
     get layoutDoc() { return this._document && Doc.Layout(this._document); }
     nativeAspect = () => this.nativeWidth() ? this.nativeWidth() / this.nativeHeight() : 0;
     panelWidth = () => this.layoutDoc?.maxWidth ? Math.min(Math.max(NumCast(this.layoutDoc._width), NumCast(this.layoutDoc._nativeWidth)), this._panelWidth) :
-        (this.nativeAspect() && this.nativeAspect() < this._panelWidth / this._panelHeight ? this._panelHeight * this.nativeAspect() : this._panelWidth);
+        (this.nativeAspect() && this.nativeAspect() < this._panelWidth / this._panelHeight ? this._panelHeight * this.nativeAspect() : this._panelWidth)
     panelHeight = () => this.nativeAspect() && this.nativeAspect() > this._panelWidth / this._panelHeight ? this._panelWidth / this.nativeAspect() : this._panelHeight;
 
     nativeWidth = () => !this.layoutDoc!._fitWidth ? NumCast(this.layoutDoc!._nativeWidth) || this._panelWidth : 0;
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index ac9f64d94..0f239d385 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -234,7 +234,6 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
     onContextMenu = (e: React.MouseEvent): void => {
         if (!e.isPropagationStopped() && this.props.Document[Id] !== CurrentUserUtils.MainDocId) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7
 
-            this.setupViewTypes("Change Perspective...", (vtype => { this.props.Document._viewType = vtype; return this.props.Document; }), true);
             this.setupViewTypes("Add a Perspective...", vtype => {
                 const newRendition = Doc.MakeAlias(this.props.Document);
                 newRendition._viewType = vtype;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 129b245b8..6caee960d 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1133,19 +1133,25 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
     private thumbIdentifier?: number;
 
     onContextMenu = (e: React.MouseEvent) => {
-        if (this.props.children && this.props.annotationsKey) return;
+        if (this.props.annotationsKey) return;
+
+        ContextMenu.Instance.addItem({
+            description: (this._timelineVisible ? "Close" : "Open") + " Animation Timeline", event: action(() => {
+                this._timelineVisible = !this._timelineVisible;
+            }), icon: this._timelineVisible ? faEyeSlash : faEye
+        });
+
         const options = ContextMenu.Instance.findByDescription("Options...");
         const optionItems: ContextMenuProps[] = options && "subitems" in options ? options.subitems : [];
 
         optionItems.push({ description: "reset view", event: () => { this.props.Document._panX = this.props.Document._panY = 0; this.props.Document.scale = 1; }, icon: "compress-arrows-alt" });
-        optionItems.push({ description: !this.layoutDoc._nativeWidth || !this.layoutDoc._nativeHeight ? "Freeze" : "Unfreeze", event: this.toggleNativeDimensions, icon: "snowflake" });
-        optionItems.push({ description: `${this.Document._LODdisable ? "Enable LOD" : "Disable LOD"}`, event: () => this.Document._LODdisable = !this.Document._LODdisable, icon: "table" });
+        optionItems.push({ description: "Reset default note style", event: () => Doc.UserDoc().defaultTextLayout = undefined, icon: "eye" });
+        optionItems.push({ description: (!this.layoutDoc._nativeWidth || !this.layoutDoc._nativeHeight ? "Freeze" : "Unfreeze") + " Aspect", event: this.toggleNativeDimensions, icon: "snowflake" });
         optionItems.push({ description: `${this.fitToContent ? "Unset" : "Set"} Fit To Container`, event: () => this.Document._fitToBox = !this.fitToContent, icon: !this.fitToContent ? "expand-arrows-alt" : "compress-arrows-alt" });
         optionItems.push({ description: `${this.Document.useClusters ? "Uncluster" : "Use Clusters"}`, event: () => this.updateClusters(!this.Document.useClusters), icon: "braille" });
         this.props.ContainingCollectionView && optionItems.push({ description: "Promote Collection", event: this.promoteCollection, icon: "table" });
         optionItems.push({ description: "Arrange contents in grid", event: this.layoutDocsInGrid, icon: "table" });
         // layoutItems.push({ description: "Analyze Strokes", event: this.analyzeStrokes, icon: "paint-brush" });
-        optionItems.push({ description: "Jitter Rotation", event: action(() => this.props.Document._jitterRotation = (this.props.Document._jitterRotation ? 0 : 10)), icon: "paint-brush" });
         optionItems.push({
             description: "Import document", icon: "upload", event: ({ x, y }) => {
                 const input = document.createElement("input");
@@ -1173,14 +1179,9 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
                 input.click();
             }
         });
+        optionItems.push({ description: `${this.Document._LODdisable ? "Enable LOD" : "Disable LOD"}`, event: () => this.Document._LODdisable = !this.Document._LODdisable, icon: "table" });
         ContextMenu.Instance.addItem({ description: "Options...", subitems: optionItems, icon: "eye" });
 
-
-        ContextMenu.Instance.addItem({
-            description: (this._timelineVisible ? "Close" : "Open") + " Animation Timeline", event: action(() => {
-                this._timelineVisible = !this._timelineVisible;
-            }), icon: this._timelineVisible ? faEyeSlash : faEye
-        });
     }
     @observable _timelineVisible = false;
 
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 5cccec776..1bf297796 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -704,7 +704,6 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         }
 
         const cm = ContextMenu.Instance;
-        const templateDoc = Cast(this.props.Document[StrCast(this.props.Document.layoutKey)], Doc, null);
 
         const customScripts = Cast(this.props.Document.contextMenuScripts, listSpec(ScriptField), []);
         Cast(this.props.Document.contextMenuLabels, listSpec("string"), []).forEach((label, i) =>
@@ -713,25 +712,16 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             cm.addItem({ description: item.label, event: () => item.script.script.run({ this: this.layoutDoc, self: this.rootDoc }), icon: "sticky-note" }));
 
 
-        let open = cm.findByDescription("Add a Perspective...");
-        const openItems: ContextMenuProps[] = open && "subitems" in open ? open.subitems : [];
-        openItems.push({ description: "New Echo        ", event: () => this.props.addDocTab(Doc.MakeAlias(this.props.Document), "onRight"), icon: "layer-group" });
-        openItems.push({ description: "Open Fields     ", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { _width: 300, _height: 300 }), "onRight"), icon: "layer-group" });
-        templateDoc && openItems.push({ description: "Open Template   ", event: () => this.props.addDocTab(templateDoc, "onRight"), icon: "eye" });
-        if (!open) {
-            open = { description: "Add a Perspective....", subitems: openItems, icon: "external-link-alt" };
-            cm.addItem(open);
-        }
-
         let options = cm.findByDescription("Options...");
         const optionItems: ContextMenuProps[] = options && "subitems" in options ? options.subitems : [];
+        const templateDoc = Cast(this.props.Document[StrCast(this.props.Document.layoutKey)], Doc, null);
+        optionItems.push({ description: "Open Fields ", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { _width: 300, _height: 300 }), "onRight"), icon: "layer-group" });
+        templateDoc && optionItems.push({ description: "Open Template   ", event: () => this.props.addDocTab(templateDoc, "onRight"), icon: "eye" });
         if (!options) {
             options = { description: "Options...", subitems: optionItems, icon: "compass" };
             cm.addItem(options);
         }
 
-        cm.moveAfter(options, open);
-
         const existingOnClick = cm.findByDescription("OnClick...");
         const onClicks: ContextMenuProps[] = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : [];
         onClicks.push({ description: "Enter Portal", event: this.makeIntoPortal, icon: "window-restore" });
@@ -764,9 +754,6 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             moreItems.push({ description: "Copy ID", event: () => Utils.CopyText(this.props.Document[Id]), icon: "fingerprint" });
             // cm.addItem({ description: "Copy...", subitems: copies, icon: "copy" });
         }
-        if (Cast(this.props.Document.data, ImageField)) {
-            moreItems.push({ description: "Export to Google Photos", event: () => GooglePhotos.Transactions.UploadImages([this.props.Document]), icon: "caret-square-right" });
-        }
         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" });
@@ -820,47 +807,38 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         cm.moveAfter(cm.findByDescription("More...")!, cm.findByDescription("OnClick...")!);
 
         runInAction(() => {
-            if (!ClientUtils.RELEASE) {
-                const setWriteMode = (mode: DocServer.WriteMode) => {
-                    DocServer.AclsMode = mode;
-                    const mode1 = mode;
-                    const mode2 = mode === DocServer.WriteMode.Default ? mode : DocServer.WriteMode.Playground;
-                    DocServer.setFieldWriteMode("x", mode1);
-                    DocServer.setFieldWriteMode("y", mode1);
-                    DocServer.setFieldWriteMode("_width", mode1);
-                    DocServer.setFieldWriteMode("_height", mode1);
-
-                    DocServer.setFieldWriteMode("_panX", mode2);
-                    DocServer.setFieldWriteMode("_panY", mode2);
-                    DocServer.setFieldWriteMode("scale", mode2);
-                    DocServer.setFieldWriteMode("_viewType", mode2);
-                };
-                const aclsMenu: ContextMenuProps[] = [];
-                aclsMenu.push({ description: "Default (write/read all)", event: () => setWriteMode(DocServer.WriteMode.Default), icon: DocServer.AclsMode === DocServer.WriteMode.Default ? "check" : "exclamation" });
-                aclsMenu.push({ description: "Playground (write own/no read)", event: () => setWriteMode(DocServer.WriteMode.Playground), icon: DocServer.AclsMode === DocServer.WriteMode.Playground ? "check" : "exclamation" });
-                aclsMenu.push({ description: "Live Playground (write own/read others)", event: () => setWriteMode(DocServer.WriteMode.LivePlayground), icon: DocServer.AclsMode === DocServer.WriteMode.LivePlayground ? "check" : "exclamation" });
-                aclsMenu.push({ description: "Live Readonly (no write/read others)", event: () => setWriteMode(DocServer.WriteMode.LiveReadonly), icon: DocServer.AclsMode === DocServer.WriteMode.LiveReadonly ? "check" : "exclamation" });
-                cm.addItem({ description: "Collaboration ACLs...", subitems: aclsMenu, icon: "share" });
-            }
+            const setWriteMode = (mode: DocServer.WriteMode) => {
+                DocServer.AclsMode = mode;
+                const mode1 = mode;
+                const mode2 = mode === DocServer.WriteMode.Default ? mode : DocServer.WriteMode.Playground;
+                DocServer.setFieldWriteMode("x", mode1);
+                DocServer.setFieldWriteMode("y", mode1);
+                DocServer.setFieldWriteMode("_width", mode1);
+                DocServer.setFieldWriteMode("_height", mode1);
+
+                DocServer.setFieldWriteMode("_panX", mode2);
+                DocServer.setFieldWriteMode("_panY", mode2);
+                DocServer.setFieldWriteMode("scale", mode2);
+                DocServer.setFieldWriteMode("_viewType", mode2);
+            };
+            const aclsMenu: ContextMenuProps[] = [];
+            aclsMenu.push({ description: "Share", event: () => SharingManager.Instance.open(this), icon: "external-link-alt" });
+            aclsMenu.push({ description: "Default (write/read all)", event: () => setWriteMode(DocServer.WriteMode.Default), icon: DocServer.AclsMode === DocServer.WriteMode.Default ? "check" : "exclamation" });
+            aclsMenu.push({ description: "Playground (write own/no read)", event: () => setWriteMode(DocServer.WriteMode.Playground), icon: DocServer.AclsMode === DocServer.WriteMode.Playground ? "check" : "exclamation" });
+            aclsMenu.push({ description: "Live Playground (write own/read others)", event: () => setWriteMode(DocServer.WriteMode.LivePlayground), icon: DocServer.AclsMode === DocServer.WriteMode.LivePlayground ? "check" : "exclamation" });
+            aclsMenu.push({ description: "Live Readonly (no write/read others)", event: () => setWriteMode(DocServer.WriteMode.LiveReadonly), icon: DocServer.AclsMode === DocServer.WriteMode.LiveReadonly ? "check" : "exclamation" });
+            cm.addItem({ description: "Collaboration ...", subitems: aclsMenu, icon: "share" });
         });
         runInAction(() => {
-            cm.addItem({
-                description: "Share",
-                event: () => SharingManager.Instance.open(this),
-                icon: "external-link-alt"
-            });
-
             if (!this.topMost && !(e instanceof Touch)) {
                 // DocumentViews should stop propagation of this event
                 e.stopPropagation();
             }
             ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15);
-            if (!SelectionManager.IsSelected(this, true)) {
-                SelectionManager.SelectDoc(this, false);
-            }
+            !SelectionManager.IsSelected(this, true) && SelectionManager.SelectDoc(this, false);
         });
         const path = this.props.LibraryPath.reduce((p: string, d: Doc) => p + "/" + (Doc.AreProtosEqual(d, (Doc.UserDoc()["tabs-button-library"] as Doc).sourcePanel as Doc) ? "" : d.title), "");
-        cm.addItem({
+        const item = ({
             description: `path: ${path}`, event: () => {
                 if (this.props.LibraryPath !== emptyPath) {
                     this.props.LibraryPath.map(lp => Doc.GetProto(lp).treeViewOpen = lp.treeViewOpen = true);
@@ -870,6 +848,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 }
             }, icon: "check"
         });
+        //cm.addItem(item);
     }
 
     recommender = async () => {
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index d85373e98..aaebceaa2 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -29,6 +29,7 @@ import FaceRectangles from './FaceRectangles';
 import { FieldView, FieldViewProps } from './FieldView';
 import "./ImageBox.scss";
 import React = require("react");
+import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils';
 const requestImageSize = require('../../util/request-image-size');
 const path = require('path');
 const { Howl } = require('howler');
@@ -158,6 +159,8 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageD
         if (field) {
             const funcs: ContextMenuProps[] = [];
             funcs.push({ description: "Rotate", event: this.rotate, icon: "expand-arrows-alt" });
+            funcs.push({ description: "Export to Google Photos", event: () => GooglePhotos.Transactions.UploadImages([this.props.Document]), icon: "caret-square-right" });
+            funcs.push({ description: "Copy path", event: () => Utils.CopyText(field.url.href), icon: "expand-arrows-alt" });
             // funcs.push({
             //     description: "Reset Native Dimensions", event: action(async () => {
             //         const curNW = NumCast(this.dataDoc[this.fieldKey + "-nativeWidth"]);
@@ -184,7 +187,6 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageD
 
             const existingMore = ContextMenu.Instance.findByDescription("More...");
             const mores: ContextMenuProps[] = existingMore && "subitems" in existingMore ? existingMore.subitems : [];
-            mores.push({ description: "Copy path", event: () => Utils.CopyText(field.url.href), icon: "expand-arrows-alt" });
             !existingMore && ContextMenu.Instance.addItem({ description: "More...", subitems: mores, icon: "hand-point-right" });
         }
     }
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 3a586ff66..23bf86a32 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -419,9 +419,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
 
         const funcs: ContextMenuProps[] = [];
         this.rootDoc.isTemplateDoc && funcs.push({ description: "Make Default Layout", event: async () => Doc.UserDoc().defaultTextLayout = new PrefetchProxy(this.rootDoc), icon: "eye" });
-        funcs.push({ description: "Reset Default Layout", event: () => Doc.UserDoc().defaultTextLayout = undefined, icon: "eye" });
         !this.layoutDoc.isTemplateDoc && funcs.push({
-            description: "Make Template", event: () => {
+            description: "Convert to use as a style", event: () => {
                 this.rootDoc.isTemplateDoc = makeTemplate(this.rootDoc);
                 Doc.AddDocToList(Cast(Doc.UserDoc()["template-notes"], Doc, null), "data", this.rootDoc);
             }, icon: "eye"
@@ -444,11 +443,15 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             }, icon: "eye"
         });
         //funcs.push({ description: `${this.Document._autoHeight ? "Variable Height" : "Auto Height"}`, event: () => this.layoutDoc._autoHeight = !this.layoutDoc._autoHeight, icon: "plus" });
-        funcs.push({ description: !this.layoutDoc._nativeWidth || !this.layoutDoc._nativeHeight ? "Freeze" : "Unfreeze", event: this.toggleNativeDimensions, icon: "snowflake" });
+        funcs.push({ description: (!this.layoutDoc._nativeWidth || !this.layoutDoc._nativeHeight ? "Freeze" : "Unfreeze") + " Aspect", event: this.toggleNativeDimensions, icon: "snowflake" });
         funcs.push({ description: "Toggle Single Line", event: () => this.layoutDoc._singleLine = !this.layoutDoc._singleLine, icon: "expand-arrows-alt" });
-        funcs.push({ description: "Toggle Sidebar", event: () => this.layoutDoc._showSidebar = !this.layoutDoc._showSidebar, icon: "expand-arrows-alt" });
-        funcs.push({ description: "Toggle Dictation Icon", event: () => this.layoutDoc._showAudio = !this.layoutDoc._showAudio, icon: "expand-arrows-alt" });
-        funcs.push({ description: "Toggle Menubar", event: () => this.toggleMenubar(), icon: "expand-arrows-alt" });
+
+        const uicontrols: ContextMenuProps[] = [];
+        uicontrols.push({ description: "Toggle Sidebar", event: () => this.layoutDoc._showSidebar = !this.layoutDoc._showSidebar, icon: "expand-arrows-alt" });
+        uicontrols.push({ description: "Toggle Dictation Icon", event: () => this.layoutDoc._showAudio = !this.layoutDoc._showAudio, icon: "expand-arrows-alt" });
+        uicontrols.push({ description: "Toggle Menubar", event: () => this.toggleMenubar(), icon: "expand-arrows-alt" });
+
+        funcs.push({ description: "UI Controls...", subitems: uicontrols, icon: "asterisk" });
 
         const highlighting: ContextMenuProps[] = [];
         ["My Text", "Text from Others", "Todo Items", "Important Items", "Ignore Items", "Disagree Items", "By Recent Minute", "By Recent Hour"].forEach(option =>
@@ -481,19 +484,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         });
         changeItems.push({ description: "FreeForm", event: undoBatch(() => Doc.makeCustomViewClicked(this.rootDoc, Docs.Create.FreeformDocument, "freeform"), "change view"), icon: "eye" });
         !change && cm.addItem({ description: "Change Perspective...", subitems: changeItems, icon: "external-link-alt" });
-
-        const open = cm.findByDescription("Add a Perspective...");
-        const openItems: ContextMenuProps[] = open && "subitems" in open ? open.subitems : [];
-
-        openItems.push({
-            description: "FreeForm", event: undoBatch(() => {
-                const alias = Doc.MakeAlias(this.rootDoc);
-                Doc.makeCustomViewClicked(alias, Docs.Create.FreeformDocument, "freeform");
-                this.props.addDocTab(alias, "onRight");
-            }), icon: "eye"
-        });
-        !open && cm.addItem({ description: "Add a Perspective...", subitems: openItems, icon: "external-link-alt" });
-
     }
 
     recordDictation = () => {
@@ -663,11 +653,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         this._disposers.height = reaction(
             () => this.layoutDoc[HeightSym](),
             action(height => {
-                if (height <= 20) {
-                    if (this.layoutDoc._nativeWidth || this.layoutDoc._nativeHeight) {
-                        Doc.toggleNativeDimensions(this.layoutDoc, this.props.ContentScaling(), this.props.PanelWidth(), this.props.PanelHeight());
-                    }
-                    this.layoutDoc._delayAutoHeight = true;
+                if (height <= 20 && height < NumCast(this.layoutDoc._delayAutoHeight, 20)) {
+                    this.layoutDoc._delayAutoHeight = height;
                 }
             })
         );
@@ -1202,7 +1189,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             const dh = NumCast(this.rootDoc._height, 0);
             const newHeight = Math.max(10, (nh ? dh / nh * scrollHeight : scrollHeight) + (this.props.ChromeHeight ? this.props.ChromeHeight() : 0));
             if (Math.abs(newHeight - dh) > 1) { // bcz: Argh!  without this, we get into a React crash if the same document is opened in a freeform view and in the treeview.  no idea why, but after dragging the freeform document, selecting it, and selecting text, it will compute to 1 pixel higher than the treeview which causes a cycle
-                if (this.rootDoc !== this.layoutDoc && !this.layoutDoc.resolvedDataDoc) {
+                if (this.rootDoc !== this.layoutDoc.doc && !this.layoutDoc.resolvedDataDoc) {
                     // if we have a template that hasn't been resolved yet, we can't set the height or we'd be setting it on the unresolved template.  So set a timeout and hope its arrived...
                     console.log("Delayed height adjustment...");
                     setTimeout(() => {
-- 
cgit v1.2.3-70-g09d2


From 408c8beb55e774f466abe455bb1296c89fe0c256 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Wed, 13 May 2020 11:40:58 -0400
Subject: fixed presentations to not find items in the presentation list. 
 cleaned up webbox css

---
 src/client/util/DocumentManager.ts                 | 40 +++++++----------
 .../views/collections/CollectionStackingView.tsx   |  1 +
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  1 +
 src/client/views/nodes/PresBox.tsx                 |  1 +
 src/client/views/nodes/WebBox.scss                 | 52 +++++++++++++---------
 src/client/views/nodes/WebBox.tsx                  |  7 +--
 6 files changed, 53 insertions(+), 49 deletions(-)

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

diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 1ba6f0248..99d85125a 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -94,37 +94,29 @@ export class DocumentManager {
         // heuristic to return the "best" documents first:
         //   choose an exact match over an alias match
         //   choose documents that have a PanelWidth() over those that don't (the treeview documents have no panelWidth)
-        docViews.map(view => !view.props.Document.presBox && view.props.PanelWidth() > 1 && view.props.Document === toFind && toReturn.push(view));
-        docViews.map(view => !view.props.Document.presBox && view.props.PanelWidth() <= 1 && view.props.Document === toFind && toReturn.push(view));
-        docViews.map(view => !view.props.Document.presBox && view.props.PanelWidth() > 1 && view.props.Document !== toFind && Doc.AreProtosEqual(view.props.Document, toFind) && toReturn.push(view));
-        docViews.map(view => !view.props.Document.presBox && view.props.PanelWidth() <= 1 && view.props.Document !== toFind && Doc.AreProtosEqual(view.props.Document, toFind) && toReturn.push(view));
+        docViews.map(view => view.props.PanelWidth() > 1 && view.props.Document === toFind && toReturn.push(view));
+        docViews.map(view => view.props.PanelWidth() <= 1 && view.props.Document === toFind && toReturn.push(view));
+        docViews.map(view => view.props.PanelWidth() > 1 && view.props.Document !== toFind && Doc.AreProtosEqual(view.props.Document, toFind) && toReturn.push(view));
+        docViews.map(view => view.props.PanelWidth() <= 1 && view.props.Document !== toFind && Doc.AreProtosEqual(view.props.Document, toFind) && toReturn.push(view));
 
         return toReturn;
     }
 
     @computed
     public get LinkedDocumentViews() {
-        const pairs = DocumentManager.Instance.DocumentViews
-            //.filter(dv => (dv.isSelected() || Doc.IsBrushed(dv.props.Document))) // draw links from DocumentViews that are selected or brushed OR
-            // || DocumentManager.Instance.DocumentViews.some(dv2 => {                                                  // Documentviews which
-            //     const rest = DocListCast(dv2.props.Document.links).some(l => Doc.AreProtosEqual(l, dv.props.Document));// are link doc anchors 
-            //     const init = (dv2.isSelected() || Doc.IsBrushed(dv2.props.Document)) && dv2.Document.type !== DocumentType.AUDIO;  // on a view that is selected or brushed
-            //     return init && rest;
-            // }
-            // )
-            .reduce((pairs, dv) => {
-                const linksList = LinkManager.Instance.getAllRelatedLinks(dv.props.Document);
-                pairs.push(...linksList.reduce((pairs, link) => {
-                    const linkToDoc = link && LinkManager.Instance.getOppositeAnchor(link, dv.props.Document);
-                    linkToDoc && DocumentManager.Instance.getDocumentViews(linkToDoc).map(docView1 => {
-                        if (dv.props.Document.type !== DocumentType.LINK || dv.props.LayoutTemplateString !== docView1.props.LayoutTemplateString) {
-                            pairs.push({ a: dv, b: docView1, l: link });
-                        }
-                    });
-                    return pairs;
-                }, [] as { a: DocumentView, b: DocumentView, l: Doc }[]));
+        const pairs = DocumentManager.Instance.DocumentViews.reduce((pairs, dv) => {
+            const linksList = LinkManager.Instance.getAllRelatedLinks(dv.props.Document);
+            pairs.push(...linksList.reduce((pairs, link) => {
+                const linkToDoc = link && LinkManager.Instance.getOppositeAnchor(link, dv.props.Document);
+                linkToDoc && DocumentManager.Instance.getDocumentViews(linkToDoc).map(docView1 => {
+                    if (dv.props.Document.type !== DocumentType.LINK || dv.props.LayoutTemplateString !== docView1.props.LayoutTemplateString) {
+                        pairs.push({ a: dv, b: docView1, l: link });
+                    }
+                });
                 return pairs;
-            }, [] as { a: DocumentView, b: DocumentView, l: Doc }[]);
+            }, [] as { a: DocumentView, b: DocumentView, l: Doc }[]));
+            return pairs;
+        }, [] as { a: DocumentView, b: DocumentView, l: Doc }[]);
 
         return pairs;
     }
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 821a6d476..d97f26daf 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -207,6 +207,7 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument)
             NativeHeight={returnZero}
             NativeWidth={returnZero}
             fitToBox={false}
+            dontRegisterView={this.props.dontRegisterView}
             rootSelected={this.rootSelected}
             dropAction={StrCast(this.props.Document.childDropAction) as dropActionType}
             onClick={this.onChildClickHandler}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 6caee960d..cfa1a046f 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -893,6 +893,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
             FreezeDimensions: this.props.freezeChildDimensions,
             layoutKey: undefined,
             setupDragLines: this.setupDragLines,
+            dontRegisterView: this.props.dontRegisterView,
             rootSelected: childData ? this.rootSelected : returnFalse,
             dropAction: StrCast(this.props.Document.childDropAction) as dropActionType,
             onClick: this.onChildClickHandler,
diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx
index e203c7efa..42552ec73 100644
--- a/src/client/views/nodes/PresBox.tsx
+++ b/src/client/views/nodes/PresBox.tsx
@@ -319,6 +319,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
                         childLayoutTemplate={this.childLayoutTemplate}
                         filterAddDocument={this.addDocumentFilter}
                         removeDocument={returnFalse}
+                        dontRegisterView={true}
                         focus={this.selectElement}
                         ScreenToLocalTransform={this.getTransform} />
                     : (null)
diff --git a/src/client/views/nodes/WebBox.scss b/src/client/views/nodes/WebBox.scss
index 4c05d4627..4623444b9 100644
--- a/src/client/views/nodes/WebBox.scss
+++ b/src/client/views/nodes/WebBox.scss
@@ -5,6 +5,36 @@
     transform-origin: top left;
     width: 100%;
     height: 100%;
+
+    .webBox-htmlSpan {
+        position: absolute;
+        top: 0;
+        left: 0;
+    }
+    .webBox-cont {
+        pointer-events: none;
+    }
+    .webBox-cont, .webBox-cont-interactive {
+        padding: 0vw;
+        position: absolute;
+        top: 0;
+        left: 0;
+        width: 100%;
+        height: 100%;
+        transform-origin: top left;
+        overflow: auto;
+        .webBox-iframe {
+            width: 100%;
+            height: 100%;
+            position: absolute;
+            top:0;
+        }
+    }
+    .webBox-cont-interactive {
+        span {
+            user-select: text !important;
+        }
+    }
     .webBox-outerContent {
         width: 100%;
         height: 100%;
@@ -20,29 +50,7 @@
         display:none;
     }
 }
-.webBox-cont {
-    padding: 0vw;
-    position: absolute;
-    top: 0;
-    left: 0;
-    width: 100%;
-    height: 100%;
-    transform-origin: top left;
-    overflow: auto;
-    pointer-events: none;
-}
-
-.webBox-cont-interactive {
-    span {
-        user-select: text !important;
-    }
-}
 
-#webBox-htmlSpan {
-    position: absolute;
-    top: 0;
-    left: 0;
-}
 
 .webBox-overlay {
     width: 100%;
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 384a6e8a5..b0abb2479 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -308,18 +308,19 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
         }
     }
 
+    //const href = "https://brown365-my.sharepoint.com/personal/bcz_ad_brown_edu/_layouts/15/Doc.aspx?sourcedoc={31aa3178-4c21-4474-b367-877d0a7135e4}&action=embedview&wdStartOn=1";
 
     @computed
     get content() {
         const field = this.dataDoc[this.props.fieldKey];
         let view;
         if (field instanceof HtmlField) {
-            view = <span id="webBox-htmlSpan" dangerouslySetInnerHTML={{ __html: field.html }} />;
+            view = <span className="webBox-htmlSpan" dangerouslySetInnerHTML={{ __html: field.html }} />;
         } else if (field instanceof WebField) {
             const url = this.layoutDoc.UseCors ? Utils.CorsProxy(field.url.href) : field.url.href;
-            view = <iframe ref={this._iframeRef} onLoad={this.iframeLoaded} src={url} style={{ position: "absolute", width: "100%", height: "100%", top: 0 }} />;
+            view = <iframe className="webBox-iframe" ref={this._iframeRef} src={url} onLoad={this.iframeLoaded} />;
         } else {
-            view = <iframe ref={this._iframeRef} src={"https://crossorigin.me/https://cs.brown.edu"} style={{ position: "absolute", width: "100%", height: "100%", top: 0 }} />;
+            view = <iframe className="webBox-iframe" ref={this._iframeRef} src={"https://crossorigin.me/https://cs.brown.edu"} />;
         }
 
         const decInteracting = DocumentDecorations.Instance?.Interacting;
-- 
cgit v1.2.3-70-g09d2


From f6a55c0d613787f60e4a40b728a1acbc6c39c552 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Thu, 14 May 2020 16:33:40 -0400
Subject: added setter scripts for fields.  fixed color selection

---
 src/client/views/InkingControl.tsx                 |  2 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  1 +
 src/new_fields/ScriptField.ts                      | 30 +++++++++++++++++-----
 src/new_fields/util.ts                             |  5 ++++
 4 files changed, 30 insertions(+), 8 deletions(-)

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

diff --git a/src/client/views/InkingControl.tsx b/src/client/views/InkingControl.tsx
index 81d99e009..7bea69fb1 100644
--- a/src/client/views/InkingControl.tsx
+++ b/src/client/views/InkingControl.tsx
@@ -35,7 +35,7 @@ export class InkingControl {
     @undoBatch
     switchColor = action((color: ColorState): void => {
         Doc.UserDoc().backgroundColor = color.hex.startsWith("#") ?
-            color.hex + (color.rgb.a !== undefined ? this.decimalToHexString(Math.round(color.rgb.a * 255)) : "ff") : color.hex;
+            color.hex + (color.rgb.a ? this.decimalToHexString(Math.round(color.rgb.a * 255)) : "ff") : color.hex;
 
         if (InkingControl.Instance.selectedTool === InkTool.None) {
             const selected = SelectionManager.SelectedDocuments();
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index cfa1a046f..6c4660c39 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -945,6 +945,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         const { x, y, z, color, zIndex } = params.pair.layout;
         return {
             x: NumCast(x), y: NumCast(y), z: Cast(z, "number"), color: StrCast(color), zIndex: Cast(zIndex, "number"),
+            transition: StrCast(layoutDoc.transition),
             width: Cast(layoutDoc._width, "number"), height: Cast(layoutDoc._height, "number"), pair: params.pair, replica: ""
         };
     }
diff --git a/src/new_fields/ScriptField.ts b/src/new_fields/ScriptField.ts
index 8d0ddf94c..4b790f483 100644
--- a/src/new_fields/ScriptField.ts
+++ b/src/new_fields/ScriptField.ts
@@ -1,10 +1,10 @@
 import { ObjectField } from "./ObjectField";
-import { CompiledScript, CompileScript, scriptingGlobal, ScriptOptions } from "../client/util/Scripting";
+import { CompiledScript, CompileScript, scriptingGlobal, ScriptOptions, CompileError, CompileResult } 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 } from "../new_fields/Doc";
-import { Plugins } from "./util";
+import { Plugins, setter } from "./util";
 import { computedFn } from "mobx-utils";
 import { ProxyField } from "./Proxy";
 import { Cast } from "./Types";
@@ -59,18 +59,20 @@ async function deserializeScript(script: ScriptField) {
 export class ScriptField extends ObjectField {
     @serializable(object(scriptSchema))
     readonly script: CompiledScript;
+    @serializable(object(scriptSchema))
+    readonly setterscript: CompiledScript | undefined;
 
     @serializable(autoObject())
     private captures?: ProxyField<Doc>;
 
-    constructor(script: CompiledScript) {
+    constructor(script: CompiledScript, setterscript?: CompileResult) {
         super();
 
-        if (script && script.options.capturedVariables) {
+        if (script?.options.capturedVariables) {
             const doc = Doc.assign(new Doc, script.options.capturedVariables);
             this.captures = new ProxyField(doc);
         }
-
+        this.setterscript = setterscript?.compiled ? setterscript : undefined;
         this.script = script;
     }
 
@@ -136,13 +138,27 @@ export class ComputedField extends ScriptField {
     //TODO maybe add an observable cache based on what is passed in for doc, considering there shouldn't really be that many possible values for doc
     value = computedFn((doc: Doc) => this._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;
+
+
+    constructor(script: CompiledScript, setterscript?: CompiledScript) {
+        super(script,
+            !setterscript && script?.originalScript.includes("self.timecode") ?
+                ScriptField.CompileScript("self['x' + self.timecode] = value", { value: "any" }, true) : setterscript);
+    }
+
     public static MakeScript(script: string, params: object = {}) {
         const compiled = ScriptField.CompileScript(script, params, false);
         return compiled.compiled ? new ComputedField(compiled) : undefined;
     }
-    public static MakeFunction(script: string, params: object = {}, capturedVariables?: { [name: string]: Field }) {
+    public static MakeFunction(script: string, params: object = {}, capturedVariables?: { [name: string]: Field }, setterScript?: string) {
         const compiled = ScriptField.CompileScript(script, params, true, capturedVariables);
-        return compiled.compiled ? new ComputedField(compiled) : undefined;
+        const setCompiled = setterScript ? ScriptField.CompileScript(setterScript, params, true, capturedVariables) : undefined;
+        return compiled.compiled ? new ComputedField(compiled, setCompiled?.compiled ? setCompiled : undefined) : undefined;
+    }
+    public static MakeInterpolated(fieldKey: string, interpolatorKey: string) {
+        const getField = ScriptField.CompileScript(`(self['${fieldKey}-indexed'])[self.${interpolatorKey}]`, {}, true, {});
+        const setField = ScriptField.CompileScript(`(self['${fieldKey}-indexed'])[self.${interpolatorKey}] = value`, { value: "any" }, true, {});
+        return getField.compiled ? new ComputedField(getField, setField?.compiled ? setField : undefined) : undefined;
     }
 }
 
diff --git a/src/new_fields/util.ts b/src/new_fields/util.ts
index 8c719ccd8..a287b0210 100644
--- a/src/new_fields/util.ts
+++ b/src/new_fields/util.ts
@@ -7,6 +7,8 @@ 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 } from "./Types";
 
 function _readOnlySetter(): never {
     throw new Error("Documents can't be modified in read-only mode");
@@ -114,6 +116,9 @@ export function setter(target: any, in_prop: string | symbol | number, value: an
             return true;
         }
     }
+    if (target.__fields[prop] instanceof ComputedField && target.__fields[prop].setterscript) {
+        return ScriptCast(target.__fields[prop])?.setterscript?.run({ self: target[SelfProxy], this: target[SelfProxy], value }).success ? true : false;
+    }
     return _setter(target, prop, value, receiver);
 }
 
-- 
cgit v1.2.3-70-g09d2


From e1ba2357ad8afd460fd8a24bff3353b84488b7e8 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Thu, 14 May 2020 19:34:10 -0400
Subject: added frame animation for colelction views.

---
 src/client/util/DragManager.ts                     | 10 ++---
 src/client/views/DocumentDecorations.tsx           | 14 +++---
 src/client/views/collections/CollectionMapView.tsx |  2 +-
 src/client/views/collections/CollectionSubView.tsx | 14 +++---
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 52 ++++++++++++++++++++--
 5 files changed, 70 insertions(+), 22 deletions(-)

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

diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index d1d7f2a8a..c997cf744 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -263,16 +263,16 @@ export namespace DragManager {
             const denominator = ((y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1));
             if (denominator === 0) return undefined;  // Lines are parallel
 
-            let ua = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)) / denominator;
+            const ua = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)) / denominator;
             // let ub = ((x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3)) / denominator;
             //if (ua < 0 || ua > 1 || ub < 0 || ub > 1)  return undefined;  // is the intersection along the segments
 
             // Return a object with the x and y coordinates of the intersection
-            let x = x1 + ua * (x2 - x1)
-            let y = y1 + ua * (y2 - y1)
+            const x = x1 + ua * (x2 - x1);
+            const y = y1 + ua * (y2 - y1);
             const dist = Math.sqrt((dragx - x) * (dragx - x) + (dragy - y) * (dragy - y));
-            return { pt: [x, y], dist }
-        }
+            return { pt: [x, y], dist };
+        };
         SnappingManager.vertSnapLines().forEach((xCoord, i) => {
             const pt = intersect(dragPt[0], dragPt[1], dragPt[0] + snapAspect, dragPt[1] + 1, xCoord, -1, xCoord, 1, dragPt[0], dragPt[1]);
             if (pt && pt.dist < closest) {
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 313d8be23..481431428 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -266,16 +266,16 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
         const fixedAspect = first.layoutDoc._nativeWidth ? NumCast(first.layoutDoc._nativeWidth) / NumCast(first.layoutDoc._nativeHeight) : 0;
         if (fixedAspect && (this._resizeHdlId === "documentDecorations-bottomRightResizer" || this._resizeHdlId === "documentDecorations-topLeftResizer")) { // need to generalize for bl and tr drag handles
             const project = (p: number[], a: number[], b: number[]) => {
-                var atob = [b[0] - a[0], b[1] - a[1]];
-                var atop = [p[0] - a[0], p[1] - a[1]];
-                var len = atob[0] * atob[0] + atob[1] * atob[1];
-                var dot = atop[0] * atob[0] + atop[1] * atob[1];
-                var t = dot / len;
+                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])
+            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);
diff --git a/src/client/views/collections/CollectionMapView.tsx b/src/client/views/collections/CollectionMapView.tsx
index 618285293..67a2d9d6a 100644
--- a/src/client/views/collections/CollectionMapView.tsx
+++ b/src/client/views/collections/CollectionMapView.tsx
@@ -1,4 +1,4 @@
-import { GoogleApiWrapper, Map as GeoMap, MapProps as IMapProps, Marker } from "google-maps-react";
+import { GoogleApiWrapper, Map as GeoMap, IMapProps, Marker } from "google-maps-react";
 import { observer } from "mobx-react";
 import { Doc, Opt, DocListCast, FieldResult, Field } from "../../../new_fields/Doc";
 import { documentSchema } from "../../../new_fields/documentSchemas";
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index bff6d121b..07e94b6ae 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -206,6 +206,8 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
             }
         }
 
+        addDocument = (doc: Doc | Doc[]) => this.props.addDocument(doc);
+
         @undoBatch
         @action
         protected onInternalDrop(e: Event, de: DragManager.DropEvent): boolean {
@@ -214,21 +216,21 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
             if (docDragData) {
                 let added = false;
                 if (docDragData.dropAction || docDragData.userDropAction) {
-                    added = this.props.addDocument(docDragData.droppedDocuments);
+                    added = this.addDocument(docDragData.droppedDocuments);
                 } else if (docDragData.moveDocument) {
                     const movedDocs = docDragData.droppedDocuments.filter((d, i) => docDragData.draggedDocuments[i] === d);
                     const addedDocs = docDragData.droppedDocuments.filter((d, i) => docDragData.draggedDocuments[i] !== d);
-                    const res = addedDocs.length ? this.props.addDocument(addedDocs) : true;
-                    added = movedDocs.length ? docDragData.moveDocument(movedDocs, this.props.Document, this.props.addDocument) : res;
+                    const res = addedDocs.length ? this.addDocument(addedDocs) : true;
+                    added = movedDocs.length ? docDragData.moveDocument(movedDocs, this.props.Document, this.addDocument) : res;
                 } else {
-                    added = this.props.addDocument(docDragData.droppedDocuments);
+                    added = this.addDocument(docDragData.droppedDocuments);
                 }
                 e.stopPropagation();
                 return added;
             }
             else if (de.complete.annoDragData) {
                 e.stopPropagation();
-                return this.props.addDocument(de.complete.annoDragData.dropDocument);
+                return this.addDocument(de.complete.annoDragData.dropDocument);
             }
             return false;
         }
@@ -265,7 +267,7 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
 
             e.stopPropagation();
             e.preventDefault();
-            const { addDocument } = this.props;
+            const { addDocument } = this;
             if (!addDocument) {
                 alert("this.props.addDocument does not exist. Aborting drop operation.");
                 return;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 6c4660c39..3a55e805e 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -11,11 +11,11 @@ import { InkData, InkField, InkTool, PointData } from "../../../../new_fields/In
 import { List } from "../../../../new_fields/List";
 import { RichTextField } from "../../../../new_fields/RichTextField";
 import { createSchema, listSpec, makeInterface } from "../../../../new_fields/Schema";
-import { ScriptField } from "../../../../new_fields/ScriptField";
-import { BoolCast, Cast, FieldValue, NumCast, ScriptCast, StrCast } from "../../../../new_fields/Types";
+import { ScriptField, ComputedField } from "../../../../new_fields/ScriptField";
+import { BoolCast, Cast, FieldValue, NumCast, ScriptCast, StrCast, PromiseValue } from "../../../../new_fields/Types";
 import { TraceMobx } from "../../../../new_fields/util";
 import { GestureUtils } from "../../../../pen-gestures/GestureUtils";
-import { aggregateBounds, intersectRect, returnOne, Utils, returnZero, returnFalse } from "../../../../Utils";
+import { aggregateBounds, intersectRect, returnOne, Utils, returnZero, returnFalse, numberRange } from "../../../../Utils";
 import { CognitiveServices } from "../../../cognitive_services/CognitiveServices";
 import { DocServer } from "../../../DocServer";
 import { Docs } from "../../../documents/Documents";
@@ -124,6 +124,17 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         this.addDocument(newBox);
     }
     private addDocument = (newBox: Doc | Doc[]) => {
+        const timecode = Cast(this.props.Document.timecode, "number", null);
+        if (timecode !== undefined) {
+            ((newBox instanceof Doc) ? [newBox] : newBox).map(doc => {
+                doc["x-indexed"] = new List<number>(numberRange(timecode + 1).map(i => NumCast(doc.x)));
+                doc["x-indexed"] = new List<number>(numberRange(timecode + 1).map(i => NumCast(doc.y)));
+                doc.timecode = ComputedField.MakeFunction("collection.timecode", {}, { collection: this.props.Document });
+                doc.x = ComputedField.MakeInterpolated("x", "timecode");
+                doc.y = ComputedField.MakeInterpolated("y", "timecode");
+            });
+        }
+
         if (newBox instanceof Doc) {
             const added = this.props.addDocument(newBox);
             added && this.bringToFront(newBox);
@@ -1132,6 +1143,39 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         Doc.toggleNativeDimensions(this.layoutDoc, this.props.ContentScaling(), this.props.NativeWidth(), this.props.NativeHeight());
     }
 
+    @undoBatch
+    @action
+    snaphsotInterpolated = (): void => {
+        if (this.props.Document.timecode === undefined) {
+            this.childDocs.map(doc => {
+                this.props.Document.timecode = 0;
+                doc["x-indexed"] = new List<number>([NumCast(doc.x)]);
+                doc["y-indexed"] = new List<number>([NumCast(doc.y)]);
+                doc.timecode = ComputedField.MakeFunction("collection.timecode", {}, { collection: this.props.Document });
+                doc.x = ComputedField.MakeInterpolated("x", "timecode");
+                doc.y = ComputedField.MakeInterpolated("y", "timecode");
+            });
+        }
+        const timecode = NumCast(this.props.Document.timecode);
+        this.childDocs.map(doc => {
+            const xindexed = Cast(doc['x-indexed'], listSpec("number"), null);
+            const yindexed = Cast(doc['y-indexed'], listSpec("number"), null);
+            xindexed.length <= timecode && xindexed.push(NumCast(doc.x));
+            yindexed.length <= timecode && yindexed.push(NumCast(doc.y));
+        });
+        this.childDocs.map(doc => doc.transition = "transform 1s");
+        this.props.Document.timecode = Math.max(0, timecode + 1);
+        setTimeout(() => this.childDocs.map(doc => doc.transition = undefined), 1010);
+    }
+    @undoBatch
+    @action
+    backupInterpolated = (): void => {
+        this.childDocs.map(doc => doc.transition = "transform 1s");
+        this.props.Document.timecode = Math.max(0, NumCast(this.props.Document.timecode) - 1);
+        setTimeout(() => this.childDocs.map(doc => doc.transition = undefined), 1010);
+    }
+
+
     private thumbIdentifier?: number;
 
     onContextMenu = (e: React.MouseEvent) => {
@@ -1142,6 +1186,8 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
                 this._timelineVisible = !this._timelineVisible;
             }), icon: this._timelineVisible ? faEyeSlash : faEye
         });
+        ContextMenu.Instance.addItem({ description: "Advance", event: this.snaphsotInterpolated, icon: BoolCast(this.Document.lockedTransform) ? "unlock" : "lock" });
+        ContextMenu.Instance.addItem({ description: "Backup ", event: this.backupInterpolated, icon: BoolCast(this.Document.lockedTransform) ? "unlock" : "lock" });
 
         const options = ContextMenu.Instance.findByDescription("Options...");
         const optionItems: ContextMenuProps[] = options && "subitems" in options ? options.subitems : [];
-- 
cgit v1.2.3-70-g09d2


From f23fde615bbeb98a93923eab1ee11c2230e67cd5 Mon Sep 17 00:00:00 2001
From: Stanley Yip <stanley_yip@brown.edu>
Date: Thu, 14 May 2020 17:23:34 -0700
Subject: fixed colorbox and added width slider

---
 package-lock.json                                  | 44 ++++++++++++++++------
 src/client/documents/Documents.ts                  |  2 +-
 src/client/util/CurrentUserUtils.ts                |  4 +-
 src/client/util/InteractionUtils.tsx               |  8 ++--
 src/client/views/GestureOverlay.tsx                | 24 ++++++------
 src/client/views/InkingControl.tsx                 | 11 +++---
 src/client/views/InkingStroke.tsx                  |  2 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  3 +-
 .../collections/collectionFreeForm/MarqueeView.tsx | 14 ++++---
 src/client/views/nodes/ColorBox.tsx                |  4 ++
 10 files changed, 72 insertions(+), 44 deletions(-)

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

diff --git a/package-lock.json b/package-lock.json
index 9ac02f404..e3f7fa018 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -2895,7 +2895,8 @@
                         },
                         "ansi-regex": {
                             "version": "2.1.1",
-                            "bundled": true
+                            "bundled": true,
+                            "optional": true
                         },
                         "aproba": {
                             "version": "1.2.0",
@@ -2913,11 +2914,13 @@
                         },
                         "balanced-match": {
                             "version": "1.0.0",
-                            "bundled": true
+                            "bundled": true,
+                            "optional": true
                         },
                         "brace-expansion": {
                             "version": "1.1.11",
                             "bundled": true,
+                            "optional": true,
                             "requires": {
                                 "balanced-match": "^1.0.0",
                                 "concat-map": "0.0.1"
@@ -2930,15 +2933,18 @@
                         },
                         "code-point-at": {
                             "version": "1.1.0",
-                            "bundled": true
+                            "bundled": true,
+                            "optional": true
                         },
                         "concat-map": {
                             "version": "0.0.1",
-                            "bundled": true
+                            "bundled": true,
+                            "optional": true
                         },
                         "console-control-strings": {
                             "version": "1.1.0",
-                            "bundled": true
+                            "bundled": true,
+                            "optional": true
                         },
                         "core-util-is": {
                             "version": "1.0.2",
@@ -3041,7 +3047,8 @@
                         },
                         "inherits": {
                             "version": "2.0.4",
-                            "bundled": true
+                            "bundled": true,
+                            "optional": true
                         },
                         "ini": {
                             "version": "1.3.5",
@@ -3051,6 +3058,7 @@
                         "is-fullwidth-code-point": {
                             "version": "1.0.0",
                             "bundled": true,
+                            "optional": true,
                             "requires": {
                                 "number-is-nan": "^1.0.0"
                             }
@@ -3063,17 +3071,20 @@
                         "minimatch": {
                             "version": "3.0.4",
                             "bundled": true,
+                            "optional": true,
                             "requires": {
                                 "brace-expansion": "^1.1.7"
                             }
                         },
                         "minimist": {
                             "version": "1.2.5",
-                            "bundled": true
+                            "bundled": true,
+                            "optional": true
                         },
                         "minipass": {
                             "version": "2.9.0",
                             "bundled": true,
+                            "optional": true,
                             "requires": {
                                 "safe-buffer": "^5.1.2",
                                 "yallist": "^3.0.0"
@@ -3090,6 +3101,7 @@
                         "mkdirp": {
                             "version": "0.5.3",
                             "bundled": true,
+                            "optional": true,
                             "requires": {
                                 "minimist": "^1.2.5"
                             }
@@ -3145,7 +3157,8 @@
                         },
                         "npm-normalize-package-bin": {
                             "version": "1.0.1",
-                            "bundled": true
+                            "bundled": true,
+                            "optional": true
                         },
                         "npm-packlist": {
                             "version": "1.4.8",
@@ -3170,7 +3183,8 @@
                         },
                         "number-is-nan": {
                             "version": "1.0.1",
-                            "bundled": true
+                            "bundled": true,
+                            "optional": true
                         },
                         "object-assign": {
                             "version": "4.1.1",
@@ -3180,6 +3194,7 @@
                         "once": {
                             "version": "1.4.0",
                             "bundled": true,
+                            "optional": true,
                             "requires": {
                                 "wrappy": "1"
                             }
@@ -3248,7 +3263,8 @@
                         },
                         "safe-buffer": {
                             "version": "5.1.2",
-                            "bundled": true
+                            "bundled": true,
+                            "optional": true
                         },
                         "safer-buffer": {
                             "version": "2.1.2",
@@ -3278,6 +3294,7 @@
                         "string-width": {
                             "version": "1.0.2",
                             "bundled": true,
+                            "optional": true,
                             "requires": {
                                 "code-point-at": "^1.0.0",
                                 "is-fullwidth-code-point": "^1.0.0",
@@ -3295,6 +3312,7 @@
                         "strip-ansi": {
                             "version": "3.0.1",
                             "bundled": true,
+                            "optional": true,
                             "requires": {
                                 "ansi-regex": "^2.0.0"
                             }
@@ -3333,11 +3351,13 @@
                         },
                         "wrappy": {
                             "version": "1.0.2",
-                            "bundled": true
+                            "bundled": true,
+                            "optional": true
                         },
                         "yallist": {
                             "version": "3.1.1",
-                            "bundled": true
+                            "bundled": true,
+                            "optional": true
                         }
                     }
                 }
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 125a531f4..61df9fbd1 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -607,7 +607,7 @@ export namespace Docs {
             return doc;
         }
 
-        export function InkDocument(color: string, tool: number, strokeWidth: number, points: { X: number, Y: number }[], options: DocumentOptions = {}) {
+        export function InkDocument(color: string, tool: number, strokeWidth: string, points: { X: number, Y: number }[], options: DocumentOptions = {}) {
             const I = new Doc();
             I.type = DocumentType.INK;
             I.layout = InkingStroke.LayoutString("data");
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index b67c3ea60..92fffd512 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -611,7 +611,7 @@ export class CurrentUserUtils {
     static setupDockedButtons(doc: Doc) {
         if (doc["dockedBtn-pen"] === undefined) {
             doc["dockedBtn-pen"] = CurrentUserUtils.ficon({
-                onClick: ScriptField.MakeScript("activatePen(this.activePen.inkPen = sameDocs(this.activePen.inkPen, this) ? undefined : this,2, this.backgroundColor)"),
+                onClick: ScriptField.MakeScript("activatePen(this.activePen.inkPen = sameDocs(this.activePen.inkPen, this) ? undefined : this, this.inkWidth, this.backgroundColor)"),
                 author: "systemTemplates", title: "ink mode", icon: "pen-nib", ischecked: ComputedField.MakeFunction(`sameDocs(this.activePen.inkPen,  this)`), activePen: doc
             });
         }
@@ -690,7 +690,7 @@ export class CurrentUserUtils {
         new InkingControl();
         doc.title = Doc.CurrentUserEmail;
         doc.activePen = doc;
-        doc.inkColor = StrCast(doc.backgroundColor, "");
+        doc.inkColor = StrCast(doc.backgroundColor, "rgb(0, 0, 0)");
         doc.fontSize = NumCast(doc.fontSize, 12);
         doc["constants-snapThreshold"] = NumCast(doc["constants-snapThreshold"], 10); // 
         doc["constants-dragThreshold"] = NumCast(doc["constants-dragThreshold"], 4); // 
diff --git a/src/client/util/InteractionUtils.tsx b/src/client/util/InteractionUtils.tsx
index b1f136430..3a5345c80 100644
--- a/src/client/util/InteractionUtils.tsx
+++ b/src/client/util/InteractionUtils.tsx
@@ -87,15 +87,17 @@ export namespace InteractionUtils {
         return myTouches;
     }
 
-    export function CreatePolyline(points: { X: number, Y: number }[], left: number, top: number, color: string, width: number) {
+    export function CreatePolyline(points: { X: number, Y: number }[], left: number, top: number, color: string, width: string) {
         const pts = points.reduce((acc: string, pt: { X: number, Y: number }) => acc + `${pt.X - left},${pt.Y - top} `, "");
         return (
             <polyline
                 points={pts}
                 style={{
                     fill: "none",
-                    stroke: color,
-                    strokeWidth: width
+                    stroke: color ?? "rgb(0, 0, 0)",
+                    strokeWidth: parseInt(width),
+                    strokeLinejoin: "round",
+                    strokeLinecap: "round"
                 }}
             />
         );
diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx
index 812ff3b93..b94fcde2d 100644
--- a/src/client/views/GestureOverlay.tsx
+++ b/src/client/views/GestureOverlay.tsx
@@ -38,10 +38,8 @@ import { SelectionManager } from "../util/SelectionManager";
 export default class GestureOverlay extends Touchable {
     static Instance: GestureOverlay;
 
-    @observable public Color: string = "rgb(0, 0, 0)";
-    @observable public Width: number = 2;
     @observable public SavedColor?: string;
-    @observable public SavedWidth?: number;
+    @observable public SavedWidth?: string;
     @observable public Tool: ToolglassTools = ToolglassTools.None;
 
     @observable private _thumbX?: number;
@@ -711,12 +709,12 @@ export default class GestureOverlay extends Touchable {
             this._palette,
             [this._strokes.map(l => {
                 const b = this.getBounds(l);
-                return <svg key={b.left} width={b.width} height={b.height} style={{ transform: `translate(${b.left}px, ${b.top}px)`, pointerEvents: "none", position: "absolute", zIndex: 30000 }}>
-                    {InteractionUtils.CreatePolyline(l, b.left, b.top, GestureOverlay.Instance.Color, GestureOverlay.Instance.Width)}
+                return <svg key={b.left} 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, InkingControl.Instance.selectedColor, InkingControl.Instance.selectedWidth)}
                 </svg>;
             }),
-            this._points.length <= 1 ? (null) : <svg width={B.width} height={B.height} style={{ transform: `translate(${B.left}px, ${B.top}px)`, pointerEvents: "none", position: "absolute", zIndex: 30000 }}>
-                {InteractionUtils.CreatePolyline(this._points, B.left, B.top, GestureOverlay.Instance.Color, GestureOverlay.Instance.Width)}
+            this._points.length <= 1 ? (null) : <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, B.left, B.top, InkingControl.Instance.selectedColor, InkingControl.Instance.selectedWidth)}
             </svg>]
         ];
     }
@@ -806,16 +804,16 @@ Scripting.addGlobal(function setToolglass(tool: any) {
 });
 Scripting.addGlobal(function setPen(width: any, color: any) {
     runInAction(() => {
-        GestureOverlay.Instance.SavedColor = GestureOverlay.Instance.Color;
-        GestureOverlay.Instance.Color = color;
-        GestureOverlay.Instance.SavedWidth = GestureOverlay.Instance.Width;
-        GestureOverlay.Instance.Width = width;
+        GestureOverlay.Instance.SavedColor = InkingControl.Instance.selectedColor;
+        InkingControl.Instance.updateSelectedColor(color);
+        GestureOverlay.Instance.SavedWidth = InkingControl.Instance.selectedWidth;
+        InkingControl.Instance.switchWidth(width);
     });
 });
 Scripting.addGlobal(function resetPen() {
     runInAction(() => {
-        GestureOverlay.Instance.Color = GestureOverlay.Instance.SavedColor ?? "rgb(0, 0, 0)";
-        GestureOverlay.Instance.Width = GestureOverlay.Instance.SavedWidth ?? 2;
+        InkingControl.Instance.updateSelectedColor(GestureOverlay.Instance.SavedColor ?? "rgb(0, 0, 0)");
+        InkingControl.Instance.switchWidth(GestureOverlay.Instance.SavedWidth ?? "2");
     });
 });
 Scripting.addGlobal(function createText(text: any, x: any, y: any) {
diff --git a/src/client/views/InkingControl.tsx b/src/client/views/InkingControl.tsx
index 5115995b0..16ccefa42 100644
--- a/src/client/views/InkingControl.tsx
+++ b/src/client/views/InkingControl.tsx
@@ -13,8 +13,8 @@ import { FormattedTextBox } from "./nodes/formattedText/FormattedTextBox";
 export class InkingControl {
     @observable static Instance: InkingControl;
     @computed private get _selectedTool(): InkTool { return FieldValue(NumCast(Doc.UserDoc().inkTool)) ?? InkTool.None; }
-    @computed private get _selectedColor(): string { return GestureOverlay.Instance.Color ?? FieldValue(StrCast(Doc.UserDoc().inkColor)) ?? "rgb(244, 67, 54)"; }
-    @computed private get _selectedWidth(): string { return GestureOverlay.Instance.Width?.toString() ?? FieldValue(StrCast(Doc.UserDoc().inkWidth)) ?? "5"; }
+    @computed private get _selectedColor(): string { return CurrentUserUtils.ActivePen ? FieldValue(StrCast(CurrentUserUtils.ActivePen.backgroundColor)) ?? "rgb(0, 0, 0)" : "rgb(0, 0, 0)"; }
+    @computed private get _selectedWidth(): string { return FieldValue(StrCast(Doc.UserDoc().inkWidth)) ?? "2"; }
     @observable public _open: boolean = false;
 
     constructor() {
@@ -36,6 +36,7 @@ export class InkingControl {
     switchColor = action((color: ColorState): void => {
         Doc.UserDoc().backgroundColor = color.hex.startsWith("#") ?
             color.hex + (color.rgb.a ? this.decimalToHexString(Math.round(color.rgb.a * 255)) : "ff") : color.hex;
+        CurrentUserUtils.ActivePen && (CurrentUserUtils.ActivePen.backgroundColor = color.hex);
 
         if (InkingControl.Instance.selectedTool === InkTool.None) {
             const selected = SelectionManager.SelectedDocuments();
@@ -51,14 +52,14 @@ export class InkingControl {
                     }
                 }
             });
-        } else {
-            CurrentUserUtils.ActivePen && (CurrentUserUtils.ActivePen.backgroundColor = this._selectedColor);
         }
     });
     @action
     switchWidth = (width: string): void => {
         // this._selectedWidth = width;
-        Doc.UserDoc().inkWidth = width;
+        if (!isNaN(parseInt(width))) {
+            Doc.UserDoc().inkWidth = width;
+        }
     }
 
     @computed
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index 7a318d5c2..98b8371af 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -40,7 +40,7 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocume
         const bottom = Math.max(...ys);
         const points = InteractionUtils.CreatePolyline(data, left, top,
             StrCast(this.layoutDoc.color, InkingControl.Instance.selectedColor),
-            NumCast(this.layoutDoc.strokeWidth, parseInt(InkingControl.Instance.selectedWidth)));
+            StrCast(this.layoutDoc.strokeWidth, InkingControl.Instance.selectedWidth));
         const width = right - left;
         const height = bottom - top;
         const scaleX = this.props.PanelWidth() / width;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 6c4660c39..c6bae66a4 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -46,6 +46,7 @@ import React = require("react");
 import { CollectionViewType } from "../CollectionView";
 import { Timeline } from "../../animationtimeline/Timeline";
 import { SnappingManager } from "../../../util/SnappingManager";
+import GestureOverlay from "../../GestureOverlay";
 
 library.add(faEye as any, faTable, faPaintBrush, faExpandArrowsAlt, faCompressArrowsAlt, faCompass, faUpload, faBraille, faChalkboard, faFileUpload);
 
@@ -401,7 +402,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
             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(InkingControl.Instance.selectedColor, InkingControl.Instance.selectedTool, parseInt(InkingControl.Instance.selectedWidth), points, { title: "ink stroke", x: B.x, y: B.y, _width: B.width, _height: B.height });
+                const inkDoc = Docs.Create.InkDocument(InkingControl.Instance.selectedColor, InkingControl.Instance.selectedTool, InkingControl.Instance.selectedWidth, points, { title: "ink stroke", x: B.x, y: B.y, _width: B.width, _height: B.height });
                 this.addDocument(inkDoc);
                 e.stopPropagation();
                 break;
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 8e20b39d2..659509663 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -20,6 +20,7 @@ import { CollectionView } from "../CollectionView";
 import MarqueeOptionsMenu from "./MarqueeOptionsMenu";
 import "./MarqueeView.scss";
 import React = require("react");
+import { InteractionUtils } from "../../../util/InteractionUtils";
 
 interface MarqueeViewProps {
     getContainerTransform: () => Transform;
@@ -164,12 +165,13 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
     onPointerDown = (e: React.PointerEvent): void => {
         this._downX = this._lastX = e.clientX;
         this._downY = this._lastY = e.clientY;
-        if (e.button === 2 || (e.button === 0 && (e.altKey || MarqueeView.DragMarquee))) {
-            if (e.altKey || (MarqueeView.DragMarquee && this.props.active(true))) {
-                this.setPreviewCursor(e.clientX, e.clientY, true);
-                // (!e.altKey) && e.stopPropagation(); // bcz: removed so that you can alt-click on button in a collection to switch link following behaviors.
-                e.preventDefault();
-            }
+        // allow marquee if right click OR alt+left click OR space bar + left click
+        if (e.button === 2 || (e.button === 0 && (e.altKey || (MarqueeView.DragMarquee && this.props.active(true))))) {
+            // if (e.altKey || (MarqueeView.DragMarquee && this.props.active(true))) {
+            this.setPreviewCursor(e.clientX, e.clientY, true);
+            // (!e.altKey) && e.stopPropagation(); // bcz: removed so that you can alt-click on button in a collection to switch link following behaviors.
+            e.preventDefault();
+            // }
             // bcz: do we need this?   it kills the context menu on the main collection if !altKey
             // e.stopPropagation();
         }
diff --git a/src/client/views/nodes/ColorBox.tsx b/src/client/views/nodes/ColorBox.tsx
index bc84204b9..f899b6bc8 100644
--- a/src/client/views/nodes/ColorBox.tsx
+++ b/src/client/views/nodes/ColorBox.tsx
@@ -27,6 +27,10 @@ export class ColorBox extends ViewBoxBaseComponent<FieldViewProps, ColorDocument
             <SketchPicker onChange={InkingControl.Instance.switchColor} presetColors={['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505', '#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B', '#FFFFFF', '#f1efeb', 'transparent']}
                 color={StrCast(CurrentUserUtils.ActivePen ? CurrentUserUtils.ActivePen.backgroundColor : undefined,
                     StrCast(selDoc?._backgroundColor, StrCast(selDoc?.backgroundColor, "black")))} />
+            <div style={{ display: "grid", gridTemplateColumns: "20% 80%", paddingTop: "10px" }}>
+                <div>{InkingControl.Instance.selectedWidth ?? 2}</div>
+                <input type="range" value={InkingControl.Instance.selectedWidth ?? 2} defaultValue={2} min={1} max={100} onChange={(e: React.ChangeEvent<HTMLInputElement>) => InkingControl.Instance.switchWidth(e.target.value)} />
+            </div>
         </div>;
     }
 } 
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2


From 2b79008596351f6948d8de80c7887446d97b068c Mon Sep 17 00:00:00 2001
From: Sam Wilkins <samwilkins333@gmail.com>
Date: Fri, 15 May 2020 00:03:41 -0700
Subject: renamed new_fields to fields

---
 src/client/ClientRecommender.tsx                   |   16 +-
 src/client/DocServer.ts                            |    6 +-
 src/client/apis/GoogleAuthenticationManager.tsx    |    2 +-
 src/client/apis/IBM_Recommender.ts                 |    2 +-
 .../apis/google_docs/GoogleApiClientUtils.ts       |    2 +-
 .../apis/google_docs/GooglePhotosClientUtils.ts    |   12 +-
 src/client/apis/youtube/YoutubeBox.tsx             |    4 +-
 src/client/cognitive_services/CognitiveServices.ts |    8 +-
 src/client/documents/Documents.ts                  |   22 +-
 src/client/util/CurrentUserUtils.ts                |   18 +-
 src/client/util/DictationManager.ts                |   12 +-
 src/client/util/DocumentManager.ts                 |    6 +-
 src/client/util/DragManager.ts                     |   16 +-
 src/client/util/DropConverter.ts                   |   12 +-
 src/client/util/History.ts                         |    2 +-
 .../util/Import & Export/DirectoryImportBox.tsx    |   12 +-
 src/client/util/Import & Export/ImageUtils.ts      |    8 +-
 .../util/Import & Export/ImportMetadataEntry.tsx   |    4 +-
 src/client/util/LinkManager.ts                     |    8 +-
 src/client/util/Scripting.ts                       |    2 +-
 src/client/util/SearchUtil.ts                      |    4 +-
 src/client/util/SelectionManager.ts                |    4 +-
 src/client/util/SerializationHelper.ts             |    2 +-
 src/client/util/SharingManager.tsx                 |    6 +-
 src/client/views/DocComponent.tsx                  |   14 +-
 src/client/views/DocumentButtonBar.tsx             |    6 +-
 src/client/views/DocumentDecorations.tsx           |   24 +-
 src/client/views/EditableView.tsx                  |    4 +-
 src/client/views/GestureOverlay.tsx                |   12 +-
 src/client/views/GlobalKeyHandler.ts               |   14 +-
 src/client/views/InkingControl.tsx                 |    6 +-
 src/client/views/InkingStroke.tsx                  |   10 +-
 src/client/views/MainView.tsx                      |   14 +-
 src/client/views/MainViewNotifs.tsx                |    2 +-
 src/client/views/MetadataEntryMenu.tsx             |    2 +-
 src/client/views/OverlayView.tsx                   |    6 +-
 src/client/views/Palette.tsx                       |    4 +-
 src/client/views/PreviewCursor.tsx                 |    4 +-
 src/client/views/RecommendationsBox.tsx            |    8 +-
 src/client/views/ScriptBox.tsx                     |    6 +-
 src/client/views/SearchDocBox.tsx                  |    6 +-
 src/client/views/TemplateMenu.tsx                  |    8 +-
 src/client/views/animationtimeline/Keyframe.tsx    |    8 +-
 src/client/views/animationtimeline/Timeline.tsx    |    8 +-
 src/client/views/animationtimeline/Track.tsx       |   12 +-
 .../views/collections/CollectionCarouselView.tsx   |   10 +-
 .../views/collections/CollectionDockingView.tsx    |   29 +-
 .../views/collections/CollectionLinearView.tsx     |   10 +-
 src/client/views/collections/CollectionMapView.tsx |   10 +-
 .../collections/CollectionMasonryViewFieldRow.tsx  |    8 +-
 .../views/collections/CollectionPileView.tsx       |    6 +-
 .../views/collections/CollectionSchemaCells.tsx    |    8 +-
 .../views/collections/CollectionSchemaHeaders.tsx  |    2 +-
 .../CollectionSchemaMovableTableHOC.tsx            |    6 +-
 .../views/collections/CollectionSchemaView.tsx     |   14 +-
 .../views/collections/CollectionStackingView.tsx   |   16 +-
 .../CollectionStackingViewFieldColumn.tsx          |   16 +-
 .../views/collections/CollectionStaffView.tsx      |    2 +-
 src/client/views/collections/CollectionSubView.tsx |   14 +-
 .../views/collections/CollectionTimeView.tsx       |   12 +-
 .../views/collections/CollectionTreeView.tsx       |   16 +-
 src/client/views/collections/CollectionView.tsx    |   20 +-
 .../views/collections/CollectionViewChromes.tsx    |   10 +-
 .../views/collections/ParentDocumentSelector.tsx   |    6 +-
 .../CollectionFreeFormLayoutEngines.tsx            |   14 +-
 .../CollectionFreeFormLinkView.tsx                 |    6 +-
 .../CollectionFreeFormLinksView.tsx                |    4 +-
 .../CollectionFreeFormRemoteCursors.tsx            |   10 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |   23 +-
 .../collections/collectionFreeForm/MarqueeView.tsx |   12 +-
 .../CollectionMulticolumnView.tsx                  |   10 +-
 .../CollectionMultirowView.tsx                     |   10 +-
 .../collectionMulticolumn/MulticolumnResizer.tsx   |    4 +-
 .../MulticolumnWidthLabel.tsx                      |    4 +-
 .../collectionMulticolumn/MultirowHeightLabel.tsx  |    4 +-
 .../collectionMulticolumn/MultirowResizer.tsx      |    4 +-
 src/client/views/linking/LinkEditor.tsx            |    4 +-
 src/client/views/linking/LinkMenu.tsx              |    2 +-
 src/client/views/linking/LinkMenuGroup.tsx         |    6 +-
 src/client/views/linking/LinkMenuItem.tsx          |    4 +-
 src/client/views/nodes/AudioBox.tsx                |   16 +-
 .../views/nodes/CollectionFreeFormDocumentView.tsx |    8 +-
 src/client/views/nodes/ColorBox.tsx                |    6 +-
 .../views/nodes/ContentFittingDocumentView.tsx     |    6 +-
 src/client/views/nodes/DocHolderBox.tsx            |   12 +-
 src/client/views/nodes/DocumentContentsView.tsx    |    8 +-
 src/client/views/nodes/DocumentIcon.tsx            |    2 +-
 src/client/views/nodes/DocumentView.tsx            |   20 +-
 src/client/views/nodes/FaceRectangles.tsx          |    6 +-
 src/client/views/nodes/FieldView.tsx               |   10 +-
 src/client/views/nodes/FontIconBox.tsx             |    8 +-
 src/client/views/nodes/ImageBox.tsx                |   20 +-
 src/client/views/nodes/KeyValueBox.tsx             |   14 +-
 src/client/views/nodes/KeyValuePair.tsx            |    2 +-
 src/client/views/nodes/LabelBox.tsx                |   10 +-
 src/client/views/nodes/LinkAnchorBox.tsx           |   10 +-
 src/client/views/nodes/LinkBox.tsx                 |    6 +-
 src/client/views/nodes/PDFBox.tsx                  |   12 +-
 src/client/views/nodes/PresBox.tsx                 |   16 +-
 src/client/views/nodes/QueryBox.tsx                |   10 +-
 src/client/views/nodes/ScreenshotBox.tsx           |    8 +-
 src/client/views/nodes/ScriptingBox.tsx            |   10 +-
 src/client/views/nodes/SliderBox.tsx               |    8 +-
 src/client/views/nodes/VideoBox.tsx                |   16 +-
 src/client/views/nodes/WebBox.tsx                  |   14 +-
 .../nodes/formattedText/DashDocCommentView.tsx     |   16 +-
 .../views/nodes/formattedText/DashDocView.tsx      |   10 +-
 .../views/nodes/formattedText/DashFieldView.tsx    |   12 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx |   24 +-
 .../formattedText/FormattedTextBoxComment.tsx      |    4 +-
 .../views/nodes/formattedText/ImageResizeView.tsx  |    2 +-
 .../formattedText/ProsemirrorExampleTransfer.ts    |    6 +-
 .../views/nodes/formattedText/RichTextMenu.tsx     |    6 +-
 .../views/nodes/formattedText/RichTextRules.ts     |   10 +-
 .../views/nodes/formattedText/RichTextSchema.tsx   |   16 +-
 src/client/views/nodes/formattedText/marks_rts.ts  |    2 +-
 src/client/views/pdf/Annotation.tsx                |    8 +-
 src/client/views/pdf/PDFMenu.tsx                   |    2 +-
 src/client/views/pdf/PDFViewer.tsx                 |   20 +-
 .../views/presentationview/PresElementBox.tsx      |   10 +-
 src/client/views/search/FilterBox.tsx              |    6 +-
 src/client/views/search/SearchBox.tsx              |    8 +-
 src/client/views/search/SearchItem.tsx             |    6 +-
 src/debug/Repl.tsx                                 |    6 +-
 src/debug/Test.tsx                                 |    2 +-
 src/debug/Viewer.tsx                               |   16 +-
 src/fields/CursorField.ts                          |   66 ++
 src/fields/DateField.ts                            |   36 +
 src/fields/Doc.ts                                  | 1109 ++++++++++++++++++++
 src/fields/FieldSymbols.ts                         |   12 +
 src/fields/HtmlField.ts                            |   26 +
 src/fields/IconField.ts                            |   26 +
 src/fields/InkField.ts                             |   50 +
 src/fields/List.ts                                 |  330 ++++++
 src/fields/ListSpec.ts                             |    0
 src/fields/ObjectField.ts                          |   20 +
 src/fields/PresField.ts                            |    6 +
 src/fields/Proxy.ts                                |  126 +++
 src/fields/RefField.ts                             |   21 +
 src/fields/RichTextField.ts                        |   41 +
 src/fields/RichTextUtils.ts                        |  519 +++++++++
 src/fields/Schema.ts                               |  120 +++
 src/fields/SchemaHeaderField.ts                    |  122 +++
 src/fields/ScriptField.ts                          |  193 ++++
 src/fields/Types.ts                                |  108 ++
 src/fields/URLField.ts                             |   53 +
 src/fields/documentSchemas.ts                      |  100 ++
 src/fields/util.ts                                 |  199 ++++
 src/mobile/ImageUpload.tsx                         |    8 +-
 src/mobile/MobileInkOverlay.tsx                    |    6 +-
 src/mobile/MobileInterface.tsx                     |   12 +-
 src/new_fields/CursorField.ts                      |   66 --
 src/new_fields/DateField.ts                        |   36 -
 src/new_fields/Doc.ts                              | 1109 --------------------
 src/new_fields/FieldSymbols.ts                     |   12 -
 src/new_fields/HtmlField.ts                        |   26 -
 src/new_fields/IconField.ts                        |   26 -
 src/new_fields/InkField.ts                         |   50 -
 src/new_fields/List.ts                             |  330 ------
 src/new_fields/ListSpec.ts                         |    0
 src/new_fields/ObjectField.ts                      |   20 -
 src/new_fields/PresField.ts                        |    6 -
 src/new_fields/Proxy.ts                            |  126 ---
 src/new_fields/RefField.ts                         |   21 -
 src/new_fields/RichTextField.ts                    |   41 -
 src/new_fields/RichTextUtils.ts                    |  519 ---------
 src/new_fields/Schema.ts                           |  120 ---
 src/new_fields/SchemaHeaderField.ts                |  122 ---
 src/new_fields/ScriptField.ts                      |  193 ----
 src/new_fields/Types.ts                            |  108 --
 src/new_fields/URLField.ts                         |   53 -
 src/new_fields/documentSchemas.ts                  |  100 --
 src/new_fields/util.ts                             |  199 ----
 src/pen-gestures/GestureUtils.ts                   |    6 +-
 src/server/ApiManagers/GooglePhotosManager.ts      |    2 +-
 src/server/ApiManagers/UserManager.ts              |    2 +-
 src/server/DashUploadUtils.ts                      |    2 +-
 src/server/Message.ts                              |    2 +-
 src/server/Recommender.ts                          |    6 +-
 src/server/apis/google/GoogleApiServerUtils.ts     |    2 +-
 src/server/database.ts                             |    2 +-
 test/test.ts                                       |    8 +-
 182 files changed, 3889 insertions(+), 3889 deletions(-)
 create mode 100644 src/fields/CursorField.ts
 create mode 100644 src/fields/DateField.ts
 create mode 100644 src/fields/Doc.ts
 create mode 100644 src/fields/FieldSymbols.ts
 create mode 100644 src/fields/HtmlField.ts
 create mode 100644 src/fields/IconField.ts
 create mode 100644 src/fields/InkField.ts
 create mode 100644 src/fields/List.ts
 create mode 100644 src/fields/ListSpec.ts
 create mode 100644 src/fields/ObjectField.ts
 create mode 100644 src/fields/PresField.ts
 create mode 100644 src/fields/Proxy.ts
 create mode 100644 src/fields/RefField.ts
 create mode 100644 src/fields/RichTextField.ts
 create mode 100644 src/fields/RichTextUtils.ts
 create mode 100644 src/fields/Schema.ts
 create mode 100644 src/fields/SchemaHeaderField.ts
 create mode 100644 src/fields/ScriptField.ts
 create mode 100644 src/fields/Types.ts
 create mode 100644 src/fields/URLField.ts
 create mode 100644 src/fields/documentSchemas.ts
 create mode 100644 src/fields/util.ts
 delete mode 100644 src/new_fields/CursorField.ts
 delete mode 100644 src/new_fields/DateField.ts
 delete mode 100644 src/new_fields/Doc.ts
 delete mode 100644 src/new_fields/FieldSymbols.ts
 delete mode 100644 src/new_fields/HtmlField.ts
 delete mode 100644 src/new_fields/IconField.ts
 delete mode 100644 src/new_fields/InkField.ts
 delete mode 100644 src/new_fields/List.ts
 delete mode 100644 src/new_fields/ListSpec.ts
 delete mode 100644 src/new_fields/ObjectField.ts
 delete mode 100644 src/new_fields/PresField.ts
 delete mode 100644 src/new_fields/Proxy.ts
 delete mode 100644 src/new_fields/RefField.ts
 delete mode 100644 src/new_fields/RichTextField.ts
 delete mode 100644 src/new_fields/RichTextUtils.ts
 delete mode 100644 src/new_fields/Schema.ts
 delete mode 100644 src/new_fields/SchemaHeaderField.ts
 delete mode 100644 src/new_fields/ScriptField.ts
 delete mode 100644 src/new_fields/Types.ts
 delete mode 100644 src/new_fields/URLField.ts
 delete mode 100644 src/new_fields/documentSchemas.ts
 delete mode 100644 src/new_fields/util.ts

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

diff --git a/src/client/ClientRecommender.tsx b/src/client/ClientRecommender.tsx
index 537e331ab..d18669b02 100644
--- a/src/client/ClientRecommender.tsx
+++ b/src/client/ClientRecommender.tsx
@@ -1,6 +1,6 @@
-import { Doc, FieldResult } from "../new_fields/Doc";
-import { StrCast, Cast } from "../new_fields/Types";
-import { List } from "../new_fields/List";
+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";
@@ -11,11 +11,11 @@ import { observable, action, computed, reaction } from "mobx";
 // var https = require('https');
 import "./ClientRecommender.scss";
 import { JSXElement } from "babel-types";
-import { RichTextField } from "../new_fields/RichTextField";
-import { ToPlainText } from "../new_fields/FieldSymbols";
-import { listSpec } from "../new_fields/Schema";
-import { ComputedField } from "../new_fields/ScriptField";
-import { ImageField } from "../new_fields/URLField";
+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";
 
diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts
index ae125694b..ac5b7a218 100644
--- a/src/client/DocServer.ts
+++ b/src/client/DocServer.ts
@@ -1,10 +1,10 @@
 import * as OpenSocket from 'socket.io-client';
 import { MessageStore, YoutubeQueryTypes, GestureContent, MobileInkOverlayContent, UpdateMobileInkOverlayPositionContent, MobileDocumentUploadContent } from "./../server/Message";
-import { Opt, Doc } from '../new_fields/Doc';
+import { Opt, Doc } from '../fields/Doc';
 import { Utils, emptyFunction } from '../Utils';
 import { SerializationHelper } from './util/SerializationHelper';
-import { RefField } from '../new_fields/RefField';
-import { Id, HandleUpdate } from '../new_fields/FieldSymbols';
+import { RefField } from '../fields/RefField';
+import { Id, HandleUpdate } from '../fields/FieldSymbols';
 import GestureOverlay from './views/GestureOverlay';
 import MobileInkOverlay from '../mobile/MobileInkOverlay';
 import { runInAction } from 'mobx';
diff --git a/src/client/apis/GoogleAuthenticationManager.tsx b/src/client/apis/GoogleAuthenticationManager.tsx
index 22d6fb582..bf4469aeb 100644
--- a/src/client/apis/GoogleAuthenticationManager.tsx
+++ b/src/client/apis/GoogleAuthenticationManager.tsx
@@ -2,7 +2,7 @@ import { observable, action, reaction, runInAction, IReactionDisposer } from "mo
 import { observer } from "mobx-react";
 import * as React from "react";
 import MainViewModal from "../views/MainViewModal";
-import { Opt } from "../../new_fields/Doc";
+import { Opt } from "../../fields/Doc";
 import { Networking } from "../Network";
 import "./GoogleAuthenticationManager.scss";
 import { Scripting } from "../util/Scripting";
diff --git a/src/client/apis/IBM_Recommender.ts b/src/client/apis/IBM_Recommender.ts
index 4e1c541c8..480b9cb1c 100644
--- a/src/client/apis/IBM_Recommender.ts
+++ b/src/client/apis/IBM_Recommender.ts
@@ -1,4 +1,4 @@
-// import { Opt } from "../../new_fields/Doc";
+// import { Opt } from "../../fields/Doc";
 
 // const NaturalLanguageUnderstandingV1 = require('ibm-watson/natural-language-understanding/v1');
 // const { IamAuthenticator } = require('ibm-watson/auth');
diff --git a/src/client/apis/google_docs/GoogleApiClientUtils.ts b/src/client/apis/google_docs/GoogleApiClientUtils.ts
index 2f3cac8d3..551dca073 100644
--- a/src/client/apis/google_docs/GoogleApiClientUtils.ts
+++ b/src/client/apis/google_docs/GoogleApiClientUtils.ts
@@ -1,5 +1,5 @@
 import { docs_v1 } from "googleapis";
-import { Opt } from "../../../new_fields/Doc";
+import { Opt } from "../../../fields/Doc";
 import { isArray } from "util";
 import { EditorState } from "prosemirror-state";
 import { Networking } from "../../Network";
diff --git a/src/client/apis/google_docs/GooglePhotosClientUtils.ts b/src/client/apis/google_docs/GooglePhotosClientUtils.ts
index 1e4c120bc..fef71ffeb 100644
--- a/src/client/apis/google_docs/GooglePhotosClientUtils.ts
+++ b/src/client/apis/google_docs/GooglePhotosClientUtils.ts
@@ -1,11 +1,11 @@
 import { AssertionError } from "assert";
 import { EditorState } from "prosemirror-state";
-import { Doc, DocListCastAsync, Opt } from "../../../new_fields/Doc";
-import { Id } from "../../../new_fields/FieldSymbols";
-import { RichTextField } from "../../../new_fields/RichTextField";
-import { RichTextUtils } from "../../../new_fields/RichTextUtils";
-import { Cast, StrCast } from "../../../new_fields/Types";
-import { ImageField } from "../../../new_fields/URLField";
+import { Doc, DocListCastAsync, Opt } from "../../../fields/Doc";
+import { Id } from "../../../fields/FieldSymbols";
+import { RichTextField } from "../../../fields/RichTextField";
+import { RichTextUtils } from "../../../fields/RichTextUtils";
+import { Cast, StrCast } from "../../../fields/Types";
+import { ImageField } from "../../../fields/URLField";
 import { MediaItem, NewMediaItemResult } from "../../../server/apis/google/SharedTypes";
 import { Utils } from "../../../Utils";
 import { Docs, DocumentOptions } from "../../documents/Documents";
diff --git a/src/client/apis/youtube/YoutubeBox.tsx b/src/client/apis/youtube/YoutubeBox.tsx
index 1575e53fc..ce7f49e64 100644
--- a/src/client/apis/youtube/YoutubeBox.tsx
+++ b/src/client/apis/youtube/YoutubeBox.tsx
@@ -1,7 +1,7 @@
 import { action, observable, runInAction } from 'mobx';
 import { observer } from "mobx-react";
-import { Doc, DocListCastAsync } from "../../../new_fields/Doc";
-import { Cast, NumCast, StrCast } from "../../../new_fields/Types";
+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";
diff --git a/src/client/cognitive_services/CognitiveServices.ts b/src/client/cognitive_services/CognitiveServices.ts
index 8c63ae906..d4df7ce57 100644
--- a/src/client/cognitive_services/CognitiveServices.ts
+++ b/src/client/cognitive_services/CognitiveServices.ts
@@ -1,12 +1,12 @@
 import * as request from "request-promise";
-import { Doc, Field } from "../../new_fields/Doc";
-import { Cast } from "../../new_fields/Types";
+import { Doc, Field } from "../../fields/Doc";
+import { Cast } from "../../fields/Types";
 import { Docs } from "../documents/Documents";
 import { Utils } from "../../Utils";
-import { InkData } from "../../new_fields/InkField";
+import { InkData } from "../../fields/InkField";
 import { UndoManager } from "../util/UndoManager";
 import requestPromise = require("request-promise");
-import { List } from "../../new_fields/List";
+import { List } from "../../fields/List";
 import { ClientRecommender } from "../ClientRecommender";
 
 type APIManager<D> = { converter: BodyConverter<D>, requester: RequestExecutor };
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 125a531f4..13687e4e8 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -9,14 +9,14 @@ import { ScriptingBox } from "../views/nodes/ScriptingBox";
 import { VideoBox } from "../views/nodes/VideoBox";
 import { WebBox } from "../views/nodes/WebBox";
 import { OmitKeys, JSONUtils, Utils } from "../../Utils";
-import { Field, Doc, Opt, DocListCastAsync, FieldResult, DocListCast } from "../../new_fields/Doc";
-import { ImageField, VideoField, AudioField, PdfField, WebField, YoutubeField } from "../../new_fields/URLField";
-import { HtmlField } from "../../new_fields/HtmlField";
-import { List } from "../../new_fields/List";
-import { Cast, NumCast, StrCast } from "../../new_fields/Types";
+import { Field, Doc, Opt, DocListCastAsync, FieldResult, DocListCast } from "../../fields/Doc";
+import { ImageField, VideoField, AudioField, PdfField, WebField, YoutubeField } from "../../fields/URLField";
+import { HtmlField } from "../../fields/HtmlField";
+import { List } from "../../fields/List";
+import { Cast, NumCast, StrCast } from "../../fields/Types";
 import { DocServer } from "../DocServer";
 import { dropActionType } from "../util/DragManager";
-import { DateField } from "../../new_fields/DateField";
+import { DateField } from "../../fields/DateField";
 import { YoutubeBox } from "../apis/youtube/YoutubeBox";
 import { CollectionDockingView } from "../views/collections/CollectionDockingView";
 import { LinkManager } from "../util/LinkManager";
@@ -26,10 +26,10 @@ import { Scripting } from "../util/Scripting";
 import { LabelBox } from "../views/nodes/LabelBox";
 import { SliderBox } from "../views/nodes/SliderBox";
 import { FontIconBox } from "../views/nodes/FontIconBox";
-import { SchemaHeaderField } from "../../new_fields/SchemaHeaderField";
+import { SchemaHeaderField } from "../../fields/SchemaHeaderField";
 import { PresBox } from "../views/nodes/PresBox";
-import { ComputedField, ScriptField } from "../../new_fields/ScriptField";
-import { ProxyField } from "../../new_fields/Proxy";
+import { ComputedField, ScriptField } from "../../fields/ScriptField";
+import { ProxyField } from "../../fields/Proxy";
 import { DocumentType } from "./DocumentTypes";
 import { RecommendationsBox } from "../views/RecommendationsBox";
 import { PresElementBox } from "../views/presentationview/PresElementBox";
@@ -39,9 +39,9 @@ import { ColorBox } from "../views/nodes/ColorBox";
 import { LinkAnchorBox } from "../views/nodes/LinkAnchorBox";
 import { DocHolderBox } from "../views/nodes/DocHolderBox";
 import { InkingStroke } from "../views/InkingStroke";
-import { InkField } from "../../new_fields/InkField";
+import { InkField } from "../../fields/InkField";
 import { InkingControl } from "../views/InkingControl";
-import { RichTextField } from "../../new_fields/RichTextField";
+import { RichTextField } from "../../fields/RichTextField";
 import { extname } from "path";
 import { MessageStore } from "../../server/Message";
 import { ContextMenuProps } from "../views/ContextMenuItem";
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index b67c3ea60..d9faed36d 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -4,23 +4,23 @@ import { Utils } from "../../Utils";
 import { DocServer } from "../DocServer";
 import { Docs, DocumentOptions } from "../documents/Documents";
 import { UndoManager } from "./UndoManager";
-import { Doc, DocListCast, DocListCastAsync } from "../../new_fields/Doc";
-import { List } from "../../new_fields/List";
-import { listSpec } from "../../new_fields/Schema";
-import { ScriptField, ComputedField } from "../../new_fields/ScriptField";
-import { Cast, PromiseValue, StrCast, NumCast } from "../../new_fields/Types";
-import { nullAudio } from "../../new_fields/URLField";
+import { Doc, DocListCast, DocListCastAsync } from "../../fields/Doc";
+import { List } from "../../fields/List";
+import { listSpec } from "../../fields/Schema";
+import { ScriptField, ComputedField } from "../../fields/ScriptField";
+import { Cast, PromiseValue, StrCast, NumCast } from "../../fields/Types";
+import { nullAudio } from "../../fields/URLField";
 import { DragManager } from "./DragManager";
 import { InkingControl } from "../views/InkingControl";
 import { Scripting } from "./Scripting";
 import { CollectionViewType } from "../views/collections/CollectionView";
 import { makeTemplate } from "./DropConverter";
-import { RichTextField } from "../../new_fields/RichTextField";
-import { PrefetchProxy } from "../../new_fields/Proxy";
+import { RichTextField } from "../../fields/RichTextField";
+import { PrefetchProxy } from "../../fields/Proxy";
 import { FormattedTextBox } from "../views/nodes/formattedText/FormattedTextBox";
 import { MainView } from "../views/MainView";
 import { DocumentType } from "../documents/DocumentTypes";
-import { SchemaHeaderField } from "../../new_fields/SchemaHeaderField";
+import { SchemaHeaderField } from "../../fields/SchemaHeaderField";
 import { DimUnit } from "../views/collections/collectionMulticolumn/CollectionMulticolumnView";
 
 export class CurrentUserUtils {
diff --git a/src/client/util/DictationManager.ts b/src/client/util/DictationManager.ts
index b3295ece0..e46225b4a 100644
--- a/src/client/util/DictationManager.ts
+++ b/src/client/util/DictationManager.ts
@@ -3,15 +3,15 @@ import { DocumentView } from "../views/nodes/DocumentView";
 import { UndoManager } from "./UndoManager";
 import * as interpreter from "words-to-numbers";
 import { DocumentType } from "../documents/DocumentTypes";
-import { Doc, Opt } from "../../new_fields/Doc";
-import { List } from "../../new_fields/List";
+import { Doc, Opt } from "../../fields/Doc";
+import { List } from "../../fields/List";
 import { Docs } from "../documents/Documents";
 import { CollectionViewType } from "../views/collections/CollectionView";
-import { Cast, CastCtor } from "../../new_fields/Types";
-import { listSpec } from "../../new_fields/Schema";
-import { AudioField, ImageField } from "../../new_fields/URLField";
+import { Cast, CastCtor } from "../../fields/Types";
+import { listSpec } from "../../fields/Schema";
+import { AudioField, ImageField } from "../../fields/URLField";
 import { Utils } from "../../Utils";
-import { RichTextField } from "../../new_fields/RichTextField";
+import { RichTextField } from "../../fields/RichTextField";
 import { DictationOverlay } from "../views/DictationOverlay";
 
 /**
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 99d85125a..ab087335e 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -1,7 +1,7 @@
 import { action, computed, observable } from 'mobx';
-import { Doc, DocListCastAsync, DocListCast, Opt } from '../../new_fields/Doc';
-import { Id } from '../../new_fields/FieldSymbols';
-import { Cast, NumCast, StrCast } from '../../new_fields/Types';
+import { Doc, DocListCastAsync, DocListCast, Opt } from '../../fields/Doc';
+import { Id } from '../../fields/FieldSymbols';
+import { Cast, NumCast, StrCast } from '../../fields/Types';
 import { CollectionDockingView } from '../views/collections/CollectionDockingView';
 import { CollectionView } from '../views/collections/CollectionView';
 import { DocumentView, DocFocusFunc } from '../views/nodes/DocumentView';
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index d1d7f2a8a..66c7ecf7d 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -1,12 +1,12 @@
 import { action, observable, runInAction } from "mobx";
-import { DateField } from "../../new_fields/DateField";
-import { Doc, Field, Opt } from "../../new_fields/Doc";
-import { List } from "../../new_fields/List";
-import { PrefetchProxy } from "../../new_fields/Proxy";
-import { listSpec } from "../../new_fields/Schema";
-import { SchemaHeaderField } from "../../new_fields/SchemaHeaderField";
-import { ScriptField } from "../../new_fields/ScriptField";
-import { Cast, NumCast, ScriptCast, StrCast } from "../../new_fields/Types";
+import { DateField } from "../../fields/DateField";
+import { Doc, Field, Opt } from "../../fields/Doc";
+import { List } from "../../fields/List";
+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 { emptyFunction } from "../../Utils";
 import { Docs, DocUtils } from "../documents/Documents";
 import * as globalCssVariables from "../views/globalCssVariables.scss";
diff --git a/src/client/util/DropConverter.ts b/src/client/util/DropConverter.ts
index d6db882b8..752c1cfc5 100644
--- a/src/client/util/DropConverter.ts
+++ b/src/client/util/DropConverter.ts
@@ -1,12 +1,12 @@
 import { DragManager } from "./DragManager";
-import { Doc, DocListCast, Opt } from "../../new_fields/Doc";
+import { Doc, DocListCast, Opt } from "../../fields/Doc";
 import { DocumentType } from "../documents/DocumentTypes";
-import { ObjectField } from "../../new_fields/ObjectField";
-import { StrCast } from "../../new_fields/Types";
+import { ObjectField } from "../../fields/ObjectField";
+import { StrCast } from "../../fields/Types";
 import { Docs } from "../documents/Documents";
-import { ScriptField, ComputedField } from "../../new_fields/ScriptField";
-import { RichTextField } from "../../new_fields/RichTextField";
-import { ImageField } from "../../new_fields/URLField";
+import { ScriptField, ComputedField } from "../../fields/ScriptField";
+import { RichTextField } from "../../fields/RichTextField";
+import { ImageField } from "../../fields/URLField";
 import { Scripting } from "./Scripting";
 
 // 
diff --git a/src/client/util/History.ts b/src/client/util/History.ts
index 2c53d7e52..7b7d4b835 100644
--- a/src/client/util/History.ts
+++ b/src/client/util/History.ts
@@ -1,4 +1,4 @@
-import { Doc } from "../../new_fields/Doc";
+import { Doc } from "../../fields/Doc";
 import { DocServer } from "../DocServer";
 import { MainView } from "../views/MainView";
 import * as qs from 'query-string';
diff --git a/src/client/util/Import & Export/DirectoryImportBox.tsx b/src/client/util/Import & Export/DirectoryImportBox.tsx
index 438904688..1e8f07049 100644
--- a/src/client/util/Import & Export/DirectoryImportBox.tsx	
+++ b/src/client/util/Import & Export/DirectoryImportBox.tsx	
@@ -1,6 +1,6 @@
 import "fs";
 import React = require("react");
-import { Doc, DocListCast, DocListCastAsync, Opt } from "../../../new_fields/Doc";
+import { Doc, DocListCast, DocListCastAsync, Opt } from "../../../fields/Doc";
 import { action, observable, runInAction, computed, reaction, IReactionDisposer } from "mobx";
 import { FieldViewProps, FieldView } from "../../views/nodes/FieldView";
 import Measure, { ContentRect } from "react-measure";
@@ -12,12 +12,12 @@ import { observer } from "mobx-react";
 import ImportMetadataEntry, { keyPlaceholder, valuePlaceholder } from "./ImportMetadataEntry";
 import { Utils } from "../../../Utils";
 import { DocumentManager } from "../DocumentManager";
-import { Id } from "../../../new_fields/FieldSymbols";
-import { List } from "../../../new_fields/List";
-import { Cast, BoolCast, NumCast } from "../../../new_fields/Types";
-import { listSpec } from "../../../new_fields/Schema";
+import { Id } from "../../../fields/FieldSymbols";
+import { List } from "../../../fields/List";
+import { Cast, BoolCast, NumCast } from "../../../fields/Types";
+import { listSpec } from "../../../fields/Schema";
 import { GooglePhotos } from "../../apis/google_docs/GooglePhotosClientUtils";
-import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField";
+import { SchemaHeaderField } from "../../../fields/SchemaHeaderField";
 import "./DirectoryImportBox.scss";
 import { Networking } from "../../Network";
 import { BatchedArray } from "array-batcher";
diff --git a/src/client/util/Import & Export/ImageUtils.ts b/src/client/util/Import & Export/ImageUtils.ts
index c8d1530b3..072e5f58a 100644
--- a/src/client/util/Import & Export/ImageUtils.ts	
+++ b/src/client/util/Import & Export/ImageUtils.ts	
@@ -1,9 +1,9 @@
-import { Doc } from "../../../new_fields/Doc";
-import { ImageField } from "../../../new_fields/URLField";
-import { Cast, StrCast } from "../../../new_fields/Types";
+import { Doc } from "../../../fields/Doc";
+import { ImageField } from "../../../fields/URLField";
+import { Cast, StrCast } from "../../../fields/Types";
 import { Docs } from "../../documents/Documents";
 import { Networking } from "../../Network";
-import { Id } from "../../../new_fields/FieldSymbols";
+import { Id } from "../../../fields/FieldSymbols";
 import { Utils } from "../../../Utils";
 
 export namespace ImageUtils {
diff --git a/src/client/util/Import & Export/ImportMetadataEntry.tsx b/src/client/util/Import & Export/ImportMetadataEntry.tsx
index 8e1c50bea..dcb94e2e0 100644
--- a/src/client/util/Import & Export/ImportMetadataEntry.tsx	
+++ b/src/client/util/Import & Export/ImportMetadataEntry.tsx	
@@ -5,8 +5,8 @@ import { action, computed } from "mobx";
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { faPlus } from "@fortawesome/free-solid-svg-icons";
 import { library } from '@fortawesome/fontawesome-svg-core';
-import { Doc } from "../../../new_fields/Doc";
-import { StrCast, BoolCast } from "../../../new_fields/Types";
+import { Doc } from "../../../fields/Doc";
+import { StrCast, BoolCast } from "../../../fields/Types";
 
 interface KeyValueProps {
     Document: Doc;
diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts
index e236c7f47..8e6ccf098 100644
--- a/src/client/util/LinkManager.ts
+++ b/src/client/util/LinkManager.ts
@@ -1,7 +1,7 @@
-import { Doc, DocListCast } from "../../new_fields/Doc";
-import { List } from "../../new_fields/List";
-import { listSpec } from "../../new_fields/Schema";
-import { Cast, StrCast } from "../../new_fields/Types";
+import { Doc, DocListCast } from "../../fields/Doc";
+import { List } from "../../fields/List";
+import { listSpec } from "../../fields/Schema";
+import { Cast, StrCast } from "../../fields/Types";
 import { Docs } from "../documents/Documents";
 import { Scripting } from "./Scripting";
 
diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts
index 8b7b9c9c7..ab577315c 100644
--- a/src/client/util/Scripting.ts
+++ b/src/client/util/Scripting.ts
@@ -9,7 +9,7 @@ export { ts };
 
 // @ts-ignore
 import * as typescriptlib from '!!raw-loader!./type_decls.d';
-import { Doc, Field } from '../../new_fields/Doc';
+import { Doc, Field } from '../../fields/Doc';
 
 export interface ScriptSucccess {
     success: true;
diff --git a/src/client/util/SearchUtil.ts b/src/client/util/SearchUtil.ts
index 6501da34a..5679c0a14 100644
--- a/src/client/util/SearchUtil.ts
+++ b/src/client/util/SearchUtil.ts
@@ -1,7 +1,7 @@
 import * as rp from 'request-promise';
 import { DocServer } from '../DocServer';
-import { Doc } from '../../new_fields/Doc';
-import { Id } from '../../new_fields/FieldSymbols';
+import { Doc } from '../../fields/Doc';
+import { Id } from '../../fields/FieldSymbols';
 import { Utils } from '../../Utils';
 import { DocumentType } from '../documents/DocumentTypes';
 
diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index 11d2cafb2..bd743c28e 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -1,8 +1,8 @@
 import { observable, action, runInAction, ObservableMap } from "mobx";
-import { Doc } from "../../new_fields/Doc";
+import { Doc } from "../../fields/Doc";
 import { DocumentView } from "../views/nodes/DocumentView";
 import { computedFn } from "mobx-utils";
-import { List } from "../../new_fields/List";
+import { List } from "../../fields/List";
 
 export namespace SelectionManager {
 
diff --git a/src/client/util/SerializationHelper.ts b/src/client/util/SerializationHelper.ts
index 1f6b939d3..ad0309fa7 100644
--- a/src/client/util/SerializationHelper.ts
+++ b/src/client/util/SerializationHelper.ts
@@ -1,5 +1,5 @@
 import { PropSchema, serialize, deserialize, custom, setDefaultModelSchema, getDefaultModelSchema } from "serializr";
-import { Field } from "../../new_fields/Doc";
+import { Field } from "../../fields/Doc";
 import { ClientUtils } from "./ClientUtils";
 
 let serializing = 0;
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index 3ce6de80d..dc67145fc 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -1,13 +1,13 @@
 import { observable, runInAction, action } from "mobx";
 import * as React from "react";
 import MainViewModal from "../views/MainViewModal";
-import { Doc, Opt, DocCastAsync } from "../../new_fields/Doc";
+import { Doc, Opt, DocCastAsync } from "../../fields/Doc";
 import { DocServer } from "../DocServer";
-import { Cast, StrCast } from "../../new_fields/Types";
+import { Cast, StrCast } from "../../fields/Types";
 import * as RequestPromise from "request-promise";
 import { Utils } from "../../Utils";
 import "./SharingManager.scss";
-import { Id } from "../../new_fields/FieldSymbols";
+import { Id } from "../../fields/FieldSymbols";
 import { observer } from "mobx-react";
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { library } from '@fortawesome/fontawesome-svg-core';
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index 6517f2fb9..1ba9fcc32 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -1,14 +1,14 @@
-import { Doc, Opt, DataSym, DocListCast } from '../../new_fields/Doc';
+import { Doc, Opt, DataSym, DocListCast } from '../../fields/Doc';
 import { Touchable } from './Touchable';
 import { computed, action, observable } from 'mobx';
-import { Cast, BoolCast, ScriptCast } from '../../new_fields/Types';
-import { listSpec } from '../../new_fields/Schema';
+import { Cast, BoolCast, ScriptCast } from '../../fields/Types';
+import { listSpec } from '../../fields/Schema';
 import { InkingControl } from './InkingControl';
-import { InkTool } from '../../new_fields/InkField';
+import { InkTool } from '../../fields/InkField';
 import { InteractionUtils } from '../util/InteractionUtils';
-import { List } from '../../new_fields/List';
-import { DateField } from '../../new_fields/DateField';
-import { ScriptField } from '../../new_fields/ScriptField';
+import { List } from '../../fields/List';
+import { DateField } from '../../fields/DateField';
+import { ScriptField } from '../../fields/ScriptField';
 
 
 ///  DocComponent returns a generic React base class used by views that don't have 'fieldKey' props (e.g.,CollectionFreeFormDocumentView, DocumentView)
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index 10d9ec401..2db5cd3ba 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -3,9 +3,9 @@ import { faArrowAltCircleDown, faPhotoVideo, faArrowAltCircleUp, faArrowAltCircl
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { action, computed, observable, runInAction } from "mobx";
 import { observer } from "mobx-react";
-import { Doc, DocListCast } from "../../new_fields/Doc";
-import { RichTextField } from '../../new_fields/RichTextField';
-import { NumCast, StrCast, Cast } from "../../new_fields/Types";
+import { Doc, DocListCast } from "../../fields/Doc";
+import { RichTextField } from '../../fields/RichTextField';
+import { NumCast, StrCast, Cast } from "../../fields/Types";
 import { emptyFunction, setupMoveUpEvents } from "../../Utils";
 import { Pulls, Pushes } from '../apis/google_docs/GoogleApiClientUtils';
 import { UndoManager } from "../util/UndoManager";
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 313d8be23..1f4f45f0e 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -3,10 +3,10 @@ import { faCaretUp, faFilePdf, faFilm, faImage, faObjectGroup, faStickyNote, faT
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { action, computed, observable, reaction, runInAction } from "mobx";
 import { observer } from "mobx-react";
-import { Doc, DataSym, Field, WidthSym, HeightSym } from "../../new_fields/Doc";
-import { Document } from '../../new_fields/documentSchemas';
-import { ScriptField } from '../../new_fields/ScriptField';
-import { Cast, StrCast, NumCast } from "../../new_fields/Types";
+import { Doc, DataSym, Field, WidthSym, HeightSym } from "../../fields/Doc";
+import { Document } from '../../fields/documentSchemas';
+import { ScriptField } from '../../fields/ScriptField';
+import { Cast, StrCast, NumCast } from "../../fields/Types";
 import { Utils, setupMoveUpEvents, emptyFunction, returnFalse, simulateMouseClick } from "../../Utils";
 import { DocUtils } from "../documents/Documents";
 import { DocumentType } from '../documents/DocumentTypes';
@@ -17,7 +17,7 @@ import { DocumentButtonBar } from './DocumentButtonBar';
 import './DocumentDecorations.scss';
 import { DocumentView } from "./nodes/DocumentView";
 import React = require("react");
-import { Id } from '../../new_fields/FieldSymbols';
+import { Id } from '../../fields/FieldSymbols';
 import e = require('express');
 import { CollectionDockingView } from './collections/CollectionDockingView';
 import { SnappingManager } from '../util/SnappingManager';
@@ -266,16 +266,16 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
         const fixedAspect = first.layoutDoc._nativeWidth ? NumCast(first.layoutDoc._nativeWidth) / NumCast(first.layoutDoc._nativeHeight) : 0;
         if (fixedAspect && (this._resizeHdlId === "documentDecorations-bottomRightResizer" || this._resizeHdlId === "documentDecorations-topLeftResizer")) { // need to generalize for bl and tr drag handles
             const project = (p: number[], a: number[], b: number[]) => {
-                var atob = [b[0] - a[0], b[1] - a[1]];
-                var atop = [p[0] - a[0], p[1] - a[1]];
-                var len = atob[0] * atob[0] + atob[1] * atob[1];
-                var dot = atop[0] * atob[0] + atop[1] * atob[1];
-                var t = dot / len;
+                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])
+            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);
diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx
index c51173ad3..e0e205df9 100644
--- a/src/client/views/EditableView.tsx
+++ b/src/client/views/EditableView.tsx
@@ -2,8 +2,8 @@ import React = require('react');
 import { action, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as Autosuggest from 'react-autosuggest';
-import { ObjectField } from '../../new_fields/ObjectField';
-import { SchemaHeaderField } from '../../new_fields/SchemaHeaderField';
+import { ObjectField } from '../../fields/ObjectField';
+import { SchemaHeaderField } from '../../fields/SchemaHeaderField';
 import "./EditableView.scss";
 
 export interface EditableProps {
diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx
index 812ff3b93..aadf5f535 100644
--- a/src/client/views/GestureOverlay.tsx
+++ b/src/client/views/GestureOverlay.tsx
@@ -6,13 +6,13 @@ import { computed, observable, action, runInAction, IReactionDisposer, reaction,
 import { GestureUtils } from "../../pen-gestures/GestureUtils";
 import { InteractionUtils } from "../util/InteractionUtils";
 import { InkingControl } from "./InkingControl";
-import { InkTool, InkData } from "../../new_fields/InkField";
-import { Doc } from "../../new_fields/Doc";
+import { InkTool, InkData } from "../../fields/InkField";
+import { Doc } from "../../fields/Doc";
 import { LinkManager } from "../util/LinkManager";
 import { DocUtils, Docs } from "../documents/Documents";
 import { undoBatch } from "../util/UndoManager";
 import { Scripting } from "../util/Scripting";
-import { FieldValue, Cast, NumCast, BoolCast } from "../../new_fields/Types";
+import { FieldValue, Cast, NumCast, BoolCast } from "../../fields/Types";
 import { CurrentUserUtils } from "../util/CurrentUserUtils";
 import HorizontalPalette from "./Palette";
 import { Utils, emptyPath, emptyFunction, returnFalse, returnOne, returnEmptyString, returnTrue, numberRange, returnZero } from "../../Utils";
@@ -22,9 +22,9 @@ import { DocumentContentsView } from "./nodes/DocumentContentsView";
 import { CognitiveServices } from "../cognitive_services/CognitiveServices";
 import { DocServer } from "../DocServer";
 import htmlToImage from "html-to-image";
-import { ScriptField } from "../../new_fields/ScriptField";
-import { listSpec } from "../../new_fields/Schema";
-import { List } from "../../new_fields/List";
+import { ScriptField } from "../../fields/ScriptField";
+import { listSpec } from "../../fields/Schema";
+import { List } from "../../fields/List";
 import { CollectionViewType } from "./collections/CollectionView";
 import TouchScrollableMenu, { TouchScrollableMenuItem } from "./TouchScrollableMenu";
 import MobileInterface from "../../mobile/MobileInterface";
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index b52a0063b..255142771 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -4,23 +4,23 @@ import { CollectionDockingView } from "./collections/CollectionDockingView";
 import { MainView } from "./MainView";
 import { DragManager } from "../util/DragManager";
 import { action, runInAction } from "mobx";
-import { Doc, DocListCast } from "../../new_fields/Doc";
+import { Doc, DocListCast } from "../../fields/Doc";
 import { DictationManager } from "../util/DictationManager";
 import SharingManager from "../util/SharingManager";
-import { Cast, PromiseValue, NumCast } from "../../new_fields/Types";
-import { ScriptField } from "../../new_fields/ScriptField";
+import { Cast, PromiseValue, NumCast } from "../../fields/Types";
+import { ScriptField } from "../../fields/ScriptField";
 import { InkingControl } from "./InkingControl";
-import { InkTool } from "../../new_fields/InkField";
+import { InkTool } from "../../fields/InkField";
 import { DocumentView } from "./nodes/DocumentView";
 import GoogleAuthenticationManager from "../apis/GoogleAuthenticationManager";
 import { CollectionFreeFormView } from "./collections/collectionFreeForm/CollectionFreeFormView";
 import { MarqueeView } from "./collections/collectionFreeForm/MarqueeView";
-import { Id } from "../../new_fields/FieldSymbols";
+import { Id } from "../../fields/FieldSymbols";
 import { DocumentDecorations } from "./DocumentDecorations";
 import { DocumentType } from "../documents/DocumentTypes";
 import { DocServer } from "../DocServer";
-import { List } from "../../new_fields/List";
-import { DateField } from "../../new_fields/DateField";
+import { List } from "../../fields/List";
+import { DateField } from "../../fields/DateField";
 
 const modifiers = ["control", "meta", "shift", "alt"];
 type KeyHandler = (keycode: string, e: KeyboardEvent) => KeyControlInfo | Promise<KeyControlInfo>;
diff --git a/src/client/views/InkingControl.tsx b/src/client/views/InkingControl.tsx
index 5115995b0..156f58ec1 100644
--- a/src/client/views/InkingControl.tsx
+++ b/src/client/views/InkingControl.tsx
@@ -1,8 +1,8 @@
 import { action, computed, observable } from "mobx";
 import { ColorState } from 'react-color';
-import { Doc } from "../../new_fields/Doc";
-import { InkTool } from "../../new_fields/InkField";
-import { FieldValue, NumCast, StrCast } from "../../new_fields/Types";
+import { Doc } from "../../fields/Doc";
+import { InkTool } from "../../fields/InkField";
+import { FieldValue, NumCast, StrCast } from "../../fields/Types";
 import { CurrentUserUtils } from "../util/CurrentUserUtils";
 import { Scripting } from "../util/Scripting";
 import { SelectionManager } from "../util/SelectionManager";
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index 7a318d5c2..25ea50a27 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -1,14 +1,14 @@
 import { observer } from "mobx-react";
-import { documentSchema } from "../../new_fields/documentSchemas";
-import { InkData, InkField, InkTool } from "../../new_fields/InkField";
-import { makeInterface } from "../../new_fields/Schema";
-import { Cast, StrCast, NumCast } from "../../new_fields/Types";
+import { documentSchema } from "../../fields/documentSchemas";
+import { InkData, InkField, InkTool } from "../../fields/InkField";
+import { makeInterface } from "../../fields/Schema";
+import { Cast, StrCast, NumCast } from "../../fields/Types";
 import { ViewBoxBaseComponent } from "./DocComponent";
 import { InkingControl } from "./InkingControl";
 import "./InkingStroke.scss";
 import { FieldView, FieldViewProps } from "./nodes/FieldView";
 import React = require("react");
-import { TraceMobx } from "../../new_fields/util";
+import { TraceMobx } from "../../fields/util";
 import { InteractionUtils } from "../util/InteractionUtils";
 import { ContextMenu } from "./ContextMenu";
 import { CognitiveServices } from "../cognitive_services/CognitiveServices";
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 9bc08de3e..978cf7868 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -13,12 +13,12 @@ import { observer } from 'mobx-react';
 import "normalize.css";
 import * as React from 'react';
 import Measure from 'react-measure';
-import { Doc, DocListCast, Field, Opt } from '../../new_fields/Doc';
-import { Id } from '../../new_fields/FieldSymbols';
-import { List } from '../../new_fields/List';
-import { listSpec } from '../../new_fields/Schema';
-import { BoolCast, Cast, FieldValue, StrCast } from '../../new_fields/Types';
-import { TraceMobx } from '../../new_fields/util';
+import { Doc, DocListCast, Field, Opt } from '../../fields/Doc';
+import { Id } from '../../fields/FieldSymbols';
+import { List } from '../../fields/List';
+import { listSpec } from '../../fields/Schema';
+import { BoolCast, Cast, FieldValue, StrCast } from '../../fields/Types';
+import { TraceMobx } from '../../fields/util';
 import { CurrentUserUtils } from '../util/CurrentUserUtils';
 import { emptyFunction, emptyPath, returnFalse, returnOne, returnZero, returnTrue, Utils } from '../../Utils';
 import GoogleAuthenticationManager from '../apis/GoogleAuthenticationManager';
@@ -48,7 +48,7 @@ import { RadialMenu } from './nodes/RadialMenu';
 import { OverlayView } from './OverlayView';
 import PDFMenu from './pdf/PDFMenu';
 import { PreviewCursor } from './PreviewCursor';
-import { ScriptField } from '../../new_fields/ScriptField';
+import { ScriptField } from '../../fields/ScriptField';
 import { TimelineMenu } from './animationtimeline/TimelineMenu';
 import { DragManager } from '../util/DragManager';
 import { SnappingManager } from '../util/SnappingManager';
diff --git a/src/client/views/MainViewNotifs.tsx b/src/client/views/MainViewNotifs.tsx
index 82e07c449..05f890485 100644
--- a/src/client/views/MainViewNotifs.tsx
+++ b/src/client/views/MainViewNotifs.tsx
@@ -2,7 +2,7 @@ import { action, computed, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import "normalize.css";
 import * as React from 'react';
-import { Doc, DocListCast, Opt } from '../../new_fields/Doc';
+import { Doc, DocListCast, Opt } from '../../fields/Doc';
 import { emptyFunction } from '../../Utils';
 import { SetupDrag } from '../util/DragManager';
 import "./MainViewNotifs.scss";
diff --git a/src/client/views/MetadataEntryMenu.tsx b/src/client/views/MetadataEntryMenu.tsx
index 8bc80ed06..e100d3f52 100644
--- a/src/client/views/MetadataEntryMenu.tsx
+++ b/src/client/views/MetadataEntryMenu.tsx
@@ -3,7 +3,7 @@ import "./MetadataEntryMenu.scss";
 import { observer } from 'mobx-react';
 import { observable, action, runInAction, trace, computed, IReactionDisposer, reaction } from 'mobx';
 import { KeyValueBox } from './nodes/KeyValueBox';
-import { Doc, Field, DocListCastAsync } from '../../new_fields/Doc';
+import { Doc, Field, DocListCastAsync } from '../../fields/Doc';
 import * as Autosuggest from 'react-autosuggest';
 import { undoBatch } from '../util/UndoManager';
 import { emptyFunction, emptyPath } from '../../Utils';
diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx
index afb6bfb7d..bfa44fe47 100644
--- a/src/client/views/OverlayView.tsx
+++ b/src/client/views/OverlayView.tsx
@@ -1,9 +1,9 @@
 import { action, computed, observable } from "mobx";
 import { observer } from "mobx-react";
 import * as React from "react";
-import { Doc, DocListCast, Opt } from "../../new_fields/Doc";
-import { Id } from "../../new_fields/FieldSymbols";
-import { NumCast } from "../../new_fields/Types";
+import { Doc, DocListCast, Opt } from "../../fields/Doc";
+import { Id } from "../../fields/FieldSymbols";
+import { NumCast } from "../../fields/Types";
 import { emptyFunction, emptyPath, returnEmptyString, returnFalse, returnOne, returnTrue, returnZero, Utils } from "../../Utils";
 import { Transform } from "../util/Transform";
 import { CollectionFreeFormLinksView } from "./collections/collectionFreeForm/CollectionFreeFormLinksView";
diff --git a/src/client/views/Palette.tsx b/src/client/views/Palette.tsx
index 63744cb50..108eb83d6 100644
--- a/src/client/views/Palette.tsx
+++ b/src/client/views/Palette.tsx
@@ -1,8 +1,8 @@
 import { IReactionDisposer, observable, reaction } from "mobx";
 import { observer } from "mobx-react";
 import * as React from "react";
-import { Doc } from "../../new_fields/Doc";
-import { NumCast } from "../../new_fields/Types";
+import { Doc } from "../../fields/Doc";
+import { NumCast } from "../../fields/Types";
 import { emptyFunction, emptyPath, returnEmptyString, returnZero, returnFalse, returnOne, returnTrue } from "../../Utils";
 import { Transform } from "../util/Transform";
 import { DocumentView } from "./nodes/DocumentView";
diff --git a/src/client/views/PreviewCursor.tsx b/src/client/views/PreviewCursor.tsx
index f50ac34c8..dd65681d4 100644
--- a/src/client/views/PreviewCursor.tsx
+++ b/src/client/views/PreviewCursor.tsx
@@ -4,11 +4,11 @@ import "normalize.css";
 import * as React from 'react';
 import "./PreviewCursor.scss";
 import { Docs } from '../documents/Documents';
-import { Doc } from '../../new_fields/Doc';
+import { Doc } from '../../fields/Doc';
 import { Transform } from "../util/Transform";
 import { DocServer } from '../DocServer';
 import { undoBatch } from '../util/UndoManager';
-import { NumCast } from '../../new_fields/Types';
+import { NumCast } from '../../fields/Types';
 
 @observer
 export class PreviewCursor extends React.Component<{}> {
diff --git a/src/client/views/RecommendationsBox.tsx b/src/client/views/RecommendationsBox.tsx
index e66fd3eb4..8ca81c070 100644
--- a/src/client/views/RecommendationsBox.tsx
+++ b/src/client/views/RecommendationsBox.tsx
@@ -3,17 +3,17 @@ import React = require("react");
 import { observable, action, computed, runInAction } from "mobx";
 import Measure from "react-measure";
 import "./RecommendationsBox.scss";
-import { Doc, DocListCast, WidthSym, HeightSym } from "../../new_fields/Doc";
+import { Doc, DocListCast, WidthSym, HeightSym } from "../../fields/Doc";
 import { DocumentIcon } from "./nodes/DocumentIcon";
-import { StrCast, NumCast } from "../../new_fields/Types";
+import { StrCast, NumCast } from "../../fields/Types";
 import { returnFalse, emptyFunction, returnEmptyString, returnOne, emptyPath, returnZero } from "../../Utils";
 import { Transform } from "../util/Transform";
-import { ObjectField } from "../../new_fields/ObjectField";
+import { ObjectField } from "../../fields/ObjectField";
 import { DocumentView } from "./nodes/DocumentView";
 import { DocumentType } from '../documents/DocumentTypes';
 import { ClientRecommender } from "../ClientRecommender";
 import { DocServer } from "../DocServer";
-import { Id } from "../../new_fields/FieldSymbols";
+import { Id } from "../../fields/FieldSymbols";
 import { FieldView, FieldViewProps } from "./nodes/FieldView";
 import { DocumentManager } from "../util/DocumentManager";
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
diff --git a/src/client/views/ScriptBox.tsx b/src/client/views/ScriptBox.tsx
index 66d3b937e..888f84dfa 100644
--- a/src/client/views/ScriptBox.tsx
+++ b/src/client/views/ScriptBox.tsx
@@ -5,11 +5,11 @@ import { observable, action } from "mobx";
 import "./ScriptBox.scss";
 import { OverlayView } from "./OverlayView";
 import { DocumentIconContainer } from "./nodes/DocumentIcon";
-import { Opt, Doc } from "../../new_fields/Doc";
+import { Opt, Doc } from "../../fields/Doc";
 import { emptyFunction } from "../../Utils";
-import { ScriptCast } from "../../new_fields/Types";
+import { ScriptCast } from "../../fields/Types";
 import { CompileScript } from "../util/Scripting";
-import { ScriptField } from "../../new_fields/ScriptField";
+import { ScriptField } from "../../fields/ScriptField";
 import { DragManager } from "../util/DragManager";
 import { EditableView } from "./EditableView";
 import { getEffectiveTypeRoots } from "typescript";
diff --git a/src/client/views/SearchDocBox.tsx b/src/client/views/SearchDocBox.tsx
index 7bd689b19..e038d8213 100644
--- a/src/client/views/SearchDocBox.tsx
+++ b/src/client/views/SearchDocBox.tsx
@@ -3,9 +3,9 @@ import { faBullseye, faLink } from "@fortawesome/free-solid-svg-icons";
 import { action, computed, observable, runInAction } from "mobx";
 import { observer } from "mobx-react";
 //import "./SearchBoxDoc.scss";
-import { Doc, DocListCast } from "../../new_fields/Doc";
-import { Id } from "../../new_fields/FieldSymbols";
-import { BoolCast, Cast, NumCast, StrCast } from "../../new_fields/Types";
+import { Doc, DocListCast } from "../../fields/Doc";
+import { Id } from "../../fields/FieldSymbols";
+import { BoolCast, Cast, NumCast, StrCast } from "../../fields/Types";
 import { returnFalse, returnZero } from "../../Utils";
 import { Docs } from "../documents/Documents";
 import { SearchUtil } from "../util/SearchUtil";
diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx
index 43debfe99..f5e95e4fd 100644
--- a/src/client/views/TemplateMenu.tsx
+++ b/src/client/views/TemplateMenu.tsx
@@ -6,15 +6,15 @@ import './TemplateMenu.scss';
 import { DocumentView } from "./nodes/DocumentView";
 import { Template } from "./Templates";
 import React = require("react");
-import { Doc, DocListCast } from "../../new_fields/Doc";
+import { Doc, DocListCast } from "../../fields/Doc";
 import { Docs, } from "../documents/Documents";
-import { StrCast, Cast } from "../../new_fields/Types";
+import { StrCast, Cast } from "../../fields/Types";
 import { CollectionTreeView } from "./collections/CollectionTreeView";
 import { returnTrue, emptyFunction, returnFalse, returnOne, emptyPath, returnZero } from "../../Utils";
 import { Transform } from "../util/Transform";
-import { ScriptField, ComputedField } from "../../new_fields/ScriptField";
+import { ScriptField, ComputedField } from "../../fields/ScriptField";
 import { Scripting } from "../util/Scripting";
-import { List } from "../../new_fields/List";
+import { List } from "../../fields/List";
 
 @observer
 class TemplateToggle extends React.Component<{ template: Template, checked: boolean, toggle: (event: React.ChangeEvent<HTMLInputElement>, template: Template) => void }> {
diff --git a/src/client/views/animationtimeline/Keyframe.tsx b/src/client/views/animationtimeline/Keyframe.tsx
index bbd7b2676..92b0f05b3 100644
--- a/src/client/views/animationtimeline/Keyframe.tsx
+++ b/src/client/views/animationtimeline/Keyframe.tsx
@@ -4,10 +4,10 @@ import "./Timeline.scss";
 import "../globalCssVariables.scss";
 import { observer } from "mobx-react";
 import { observable, reaction, action, IReactionDisposer, observe, computed, runInAction, trace } from "mobx";
-import { Doc, DocListCast, DocListCastAsync, Opt } from "../../../new_fields/Doc";
-import { Cast, NumCast } from "../../../new_fields/Types";
-import { List } from "../../../new_fields/List";
-import { createSchema, defaultSpec, makeInterface, listSpec } from "../../../new_fields/Schema";
+import { Doc, DocListCast, DocListCastAsync, Opt } from "../../../fields/Doc";
+import { Cast, NumCast } from "../../../fields/Types";
+import { List } from "../../../fields/List";
+import { createSchema, defaultSpec, makeInterface, listSpec } from "../../../fields/Schema";
 import { Transform } from "../../util/Transform";
 import { TimelineMenu } from "./TimelineMenu";
 import { Docs } from "../../documents/Documents";
diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx
index 466cbb867..30692944d 100644
--- a/src/client/views/animationtimeline/Timeline.tsx
+++ b/src/client/views/animationtimeline/Timeline.tsx
@@ -1,12 +1,12 @@
 import * as React from "react";
 import "./Timeline.scss";
-import { listSpec } from "../../../new_fields/Schema";
+import { listSpec } from "../../../fields/Schema";
 import { observer } from "mobx-react";
 import { Track } from "./Track";
 import { observable, action, computed, runInAction, IReactionDisposer, reaction, trace } from "mobx";
-import { Cast, NumCast, StrCast, BoolCast } from "../../../new_fields/Types";
-import { List } from "../../../new_fields/List";
-import { Doc, DocListCast } from "../../../new_fields/Doc";
+import { Cast, NumCast, StrCast, BoolCast } from "../../../fields/Types";
+import { List } from "../../../fields/List";
+import { Doc, DocListCast } from "../../../fields/Doc";
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { faPlayCircle, faBackward, faForward, faGripLines, faPauseCircle, faEyeSlash, faEye, faCheckCircle, faTimesCircle } from "@fortawesome/free-solid-svg-icons";
 import { ContextMenu } from "../ContextMenu";
diff --git a/src/client/views/animationtimeline/Track.tsx b/src/client/views/animationtimeline/Track.tsx
index 461db4858..fc96c320a 100644
--- a/src/client/views/animationtimeline/Track.tsx
+++ b/src/client/views/animationtimeline/Track.tsx
@@ -1,12 +1,12 @@
 import { action, computed, intercept, observable, reaction, runInAction } from "mobx";
 import { observer } from "mobx-react";
 import * as React from "react";
-import { Doc, DocListCast, Opt, DocListCastAsync } from "../../../new_fields/Doc";
-import { Copy } from "../../../new_fields/FieldSymbols";
-import { List } from "../../../new_fields/List";
-import { ObjectField } from "../../../new_fields/ObjectField";
-import { listSpec } from "../../../new_fields/Schema";
-import { Cast, NumCast } from "../../../new_fields/Types";
+import { Doc, DocListCast, Opt, DocListCastAsync } from "../../../fields/Doc";
+import { Copy } from "../../../fields/FieldSymbols";
+import { List } from "../../../fields/List";
+import { ObjectField } from "../../../fields/ObjectField";
+import { listSpec } from "../../../fields/Schema";
+import { Cast, NumCast } from "../../../fields/Types";
 import { Transform } from "../../util/Transform";
 import { Keyframe, KeyframeFunc, RegionData } from "./Keyframe";
 import "./Track.scss";
diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx
index a04136e51..39bb9bc23 100644
--- a/src/client/views/collections/CollectionCarouselView.tsx
+++ b/src/client/views/collections/CollectionCarouselView.tsx
@@ -2,18 +2,18 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { observable, computed } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
-import { documentSchema, collectionSchema } from '../../../new_fields/documentSchemas';
-import { makeInterface } from '../../../new_fields/Schema';
-import { NumCast, StrCast, ScriptCast, Cast } from '../../../new_fields/Types';
+import { documentSchema, collectionSchema } from '../../../fields/documentSchemas';
+import { makeInterface } from '../../../fields/Schema';
+import { NumCast, StrCast, ScriptCast, Cast } from '../../../fields/Types';
 import { DragManager } from '../../util/DragManager';
 import { ContentFittingDocumentView } from '../nodes/ContentFittingDocumentView';
 import "./CollectionCarouselView.scss";
 import { CollectionSubView } from './CollectionSubView';
 import { faCaretLeft, faCaretRight } from '@fortawesome/free-solid-svg-icons';
-import { Doc } from '../../../new_fields/Doc';
+import { Doc } from '../../../fields/Doc';
 import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
 import { ContextMenu } from '../ContextMenu';
-import { ObjectField } from '../../../new_fields/ObjectField';
+import { ObjectField } from '../../../fields/ObjectField';
 import { returnFalse } from '../../../Utils';
 
 type CarouselDocument = makeInterface<[typeof documentSchema, typeof collectionSchema]>;
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 581625222..745476ef7 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -7,13 +7,13 @@ import { observer } from "mobx-react";
 import * as ReactDOM from 'react-dom';
 import Measure from "react-measure";
 import * as GoldenLayout from "../../../client/goldenLayout";
-import { DateField } from '../../../new_fields/DateField';
-import { Doc, DocListCast, Field, Opt, DataSym } from "../../../new_fields/Doc";
-import { Id } from '../../../new_fields/FieldSymbols';
-import { List } from '../../../new_fields/List';
-import { FieldId } from "../../../new_fields/RefField";
-import { Cast, NumCast, StrCast } from "../../../new_fields/Types";
-import { TraceMobx } from '../../../new_fields/util';
+import { DateField } from '../../../fields/DateField';
+import { Doc, DocListCast, Field, Opt, DataSym } from "../../../fields/Doc";
+import { Id } from '../../../fields/FieldSymbols';
+import { List } from '../../../fields/List';
+import { FieldId } from "../../../fields/RefField";
+import { Cast, NumCast, StrCast } from "../../../fields/Types";
+import { TraceMobx } from '../../../fields/util';
 import { emptyFunction, returnOne, returnTrue, Utils, returnZero } from "../../../Utils";
 import { DocServer } from "../../DocServer";
 import { Docs } from '../../documents/Documents';
@@ -196,15 +196,16 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
         if (!CollectionDockingView.Instance) return undefined;
         const instance = CollectionDockingView.Instance;
         const replaceTab = (doc: Doc, child: any): Opt<Doc> => {
-            for (let i = 0; i < child.contentItems.length; i++) {
-                if (child.contentItems[i].isRow || child.contentItems[i].isColumn || child.contentItems[i].isStack) {
-                    const val = replaceTab(doc, child.contentItems[i]);
+            for (const contentItem of child.contentItems) {
+                const { config, isStack, isRow, isColumn } = contentItem;
+                if (isRow || isColumn || isStack) {
+                    const val = replaceTab(doc, contentItem);
                     if (val) return val;
-                } else if (child.contentItems[i].config.component === "DocumentFrameRenderer" &&
-                    child.contentItems[i].config.props.documentId === doc[Id]) {
+                } else if (config.component === "DocumentFrameRenderer" &&
+                    config.props.documentId === doc[Id]) {
                     const alias = Doc.MakeAlias(doc);
-                    child.contentItems[i].config.props.documentId = alias[Id];
-                    child.contentItems[i].config.title = alias.title;
+                    config.props.documentId = alias[Id];
+                    config.title = alias.title;
                     instance.stateChanged();
                     return alias;
                 }
diff --git a/src/client/views/collections/CollectionLinearView.tsx b/src/client/views/collections/CollectionLinearView.tsx
index 344dca23a..f1002044a 100644
--- a/src/client/views/collections/CollectionLinearView.tsx
+++ b/src/client/views/collections/CollectionLinearView.tsx
@@ -1,9 +1,9 @@
 import { action, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
-import { Doc, HeightSym, WidthSym } from '../../../new_fields/Doc';
-import { makeInterface } from '../../../new_fields/Schema';
-import { BoolCast, NumCast, StrCast, Cast, ScriptCast } from '../../../new_fields/Types';
+import { Doc, HeightSym, WidthSym } from '../../../fields/Doc';
+import { makeInterface } from '../../../fields/Schema';
+import { BoolCast, NumCast, StrCast, Cast, ScriptCast } from '../../../fields/Types';
 import { emptyFunction, returnEmptyString, returnOne, returnTrue, Utils, returnFalse, returnZero } from '../../../Utils';
 import { DragManager } from '../../util/DragManager';
 import { Transform } from '../../util/Transform';
@@ -11,8 +11,8 @@ import "./CollectionLinearView.scss";
 import { CollectionViewType } from './CollectionView';
 import { CollectionSubView } from './CollectionSubView';
 import { DocumentView } from '../nodes/DocumentView';
-import { documentSchema } from '../../../new_fields/documentSchemas';
-import { Id } from '../../../new_fields/FieldSymbols';
+import { documentSchema } from '../../../fields/documentSchemas';
+import { Id } from '../../../fields/FieldSymbols';
 
 
 type LinearDocument = makeInterface<[typeof documentSchema,]>;
diff --git a/src/client/views/collections/CollectionMapView.tsx b/src/client/views/collections/CollectionMapView.tsx
index 618285293..da902cecd 100644
--- a/src/client/views/collections/CollectionMapView.tsx
+++ b/src/client/views/collections/CollectionMapView.tsx
@@ -1,10 +1,10 @@
 import { GoogleApiWrapper, Map as GeoMap, MapProps as IMapProps, Marker } from "google-maps-react";
 import { observer } from "mobx-react";
-import { Doc, Opt, DocListCast, FieldResult, Field } from "../../../new_fields/Doc";
-import { documentSchema } from "../../../new_fields/documentSchemas";
-import { Id } from "../../../new_fields/FieldSymbols";
-import { makeInterface } from "../../../new_fields/Schema";
-import { Cast, NumCast, ScriptCast, StrCast } from "../../../new_fields/Types";
+import { Doc, Opt, DocListCast, FieldResult, Field } from "../../../fields/Doc";
+import { documentSchema } from "../../../fields/documentSchemas";
+import { Id } from "../../../fields/FieldSymbols";
+import { makeInterface } from "../../../fields/Schema";
+import { Cast, NumCast, ScriptCast, StrCast } from "../../../fields/Types";
 import "./CollectionMapView.scss";
 import { CollectionSubView } from "./CollectionSubView";
 import React = require("react");
diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
index 95c7643c9..d6cb174cc 100644
--- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
+++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
@@ -4,10 +4,10 @@ import { faPalette } 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 { Doc } from "../../../new_fields/Doc";
-import { PastelSchemaPalette, SchemaHeaderField } from "../../../new_fields/SchemaHeaderField";
-import { ScriptField } from "../../../new_fields/ScriptField";
-import { StrCast, NumCast } from "../../../new_fields/Types";
+import { Doc } from "../../../fields/Doc";
+import { PastelSchemaPalette, SchemaHeaderField } from "../../../fields/SchemaHeaderField";
+import { ScriptField } from "../../../fields/ScriptField";
+import { StrCast, NumCast } from "../../../fields/Types";
 import { numberRange, setupMoveUpEvents, emptyFunction } from "../../../Utils";
 import { Docs } from "../../documents/Documents";
 import { DragManager } from "../../util/DragManager";
diff --git a/src/client/views/collections/CollectionPileView.tsx b/src/client/views/collections/CollectionPileView.tsx
index d3ae21f3a..e3bcf2a21 100644
--- a/src/client/views/collections/CollectionPileView.tsx
+++ b/src/client/views/collections/CollectionPileView.tsx
@@ -1,8 +1,8 @@
 import { action, computed, observable, runInAction } from "mobx";
 import { observer } from "mobx-react";
-import { HeightSym, Opt, WidthSym } from "../../../new_fields/Doc";
-import { ScriptField } from "../../../new_fields/ScriptField";
-import { BoolCast, NumCast, StrCast } from "../../../new_fields/Types";
+import { HeightSym, Opt, WidthSym } from "../../../fields/Doc";
+import { ScriptField } from "../../../fields/ScriptField";
+import { BoolCast, NumCast, StrCast } from "../../../fields/Types";
 import { ContextMenu } from "../ContextMenu";
 import { ContextMenuProps } from "../ContextMenuItem";
 import { CollectionFreeFormView } from "./collectionFreeForm/CollectionFreeFormView";
diff --git a/src/client/views/collections/CollectionSchemaCells.tsx b/src/client/views/collections/CollectionSchemaCells.tsx
index 8a5450b0c..62aed67ed 100644
--- a/src/client/views/collections/CollectionSchemaCells.tsx
+++ b/src/client/views/collections/CollectionSchemaCells.tsx
@@ -4,8 +4,8 @@ import { observer } from "mobx-react";
 import { CellInfo } from "react-table";
 import "react-table/react-table.css";
 import { emptyFunction, returnFalse, returnZero, returnOne } from "../../../Utils";
-import { Doc, DocListCast, Field, Opt } from "../../../new_fields/Doc";
-import { Id } from "../../../new_fields/FieldSymbols";
+import { Doc, DocListCast, Field, Opt } from "../../../fields/Doc";
+import { Id } from "../../../fields/FieldSymbols";
 import { KeyCodes } from "../../util/KeyCodes";
 import { SetupDrag, DragManager } from "../../util/DragManager";
 import { CompileScript } from "../../util/Scripting";
@@ -16,11 +16,11 @@ import { EditableView } from "../EditableView";
 import { FieldView, FieldViewProps } from "../nodes/FieldView";
 import "./CollectionSchemaView.scss";
 import { CollectionView } from "./CollectionView";
-import { NumCast, StrCast, BoolCast, FieldValue, Cast } from "../../../new_fields/Types";
+import { NumCast, StrCast, BoolCast, FieldValue, Cast } from "../../../fields/Types";
 import { Docs } from "../../documents/Documents";
 import { library } from '@fortawesome/fontawesome-svg-core';
 import { faExpand } from '@fortawesome/free-solid-svg-icons';
-import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField";
+import { SchemaHeaderField } from "../../../fields/SchemaHeaderField";
 import { undoBatch } from "../../util/UndoManager";
 import { SnappingManager } from "../../util/SnappingManager";
 
diff --git a/src/client/views/collections/CollectionSchemaHeaders.tsx b/src/client/views/collections/CollectionSchemaHeaders.tsx
index 507ee89e4..dae0600b1 100644
--- a/src/client/views/collections/CollectionSchemaHeaders.tsx
+++ b/src/client/views/collections/CollectionSchemaHeaders.tsx
@@ -7,7 +7,7 @@ import { library, IconProp } from "@fortawesome/fontawesome-svg-core";
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { ColumnType } from "./CollectionSchemaView";
 import { faFile } from "@fortawesome/free-regular-svg-icons";
-import { SchemaHeaderField, PastelSchemaPalette } from "../../../new_fields/SchemaHeaderField";
+import { SchemaHeaderField, PastelSchemaPalette } from "../../../fields/SchemaHeaderField";
 import { undoBatch } from "../../util/UndoManager";
 const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
diff --git a/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx b/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx
index 5aec46a83..6f1e8ac1f 100644
--- a/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx
+++ b/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx
@@ -2,16 +2,16 @@ import React = require("react");
 import { ReactTableDefaults, TableCellRenderer, RowInfo } from "react-table";
 import "./CollectionSchemaView.scss";
 import { Transform } from "../../util/Transform";
-import { Doc } from "../../../new_fields/Doc";
+import { Doc } from "../../../fields/Doc";
 import { DragManager, SetupDrag, dropActionType } from "../../util/DragManager";
-import { Cast, FieldValue, StrCast } from "../../../new_fields/Types";
+import { Cast, FieldValue, StrCast } from "../../../fields/Types";
 import { ContextMenu } from "../ContextMenu";
 import { action } from "mobx";
 import { library } from '@fortawesome/fontawesome-svg-core';
 import { faGripVertical, faTrash } from '@fortawesome/free-solid-svg-icons';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { DocumentManager } from "../../util/DocumentManager";
-import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField";
+import { SchemaHeaderField } from "../../../fields/SchemaHeaderField";
 import { undoBatch } from "../../util/UndoManager";
 import { SnappingManager } from "../../util/SnappingManager";
 
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 51ad6c81b..35f892d65 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -6,13 +6,13 @@ import { action, computed, observable, untracked } from "mobx";
 import { observer } from "mobx-react";
 import ReactTable, { CellInfo, Column, ComponentPropsGetterR, Resize, SortingRule } from "react-table";
 import "react-table/react-table.css";
-import { Doc, DocListCast, Field, Opt } from "../../../new_fields/Doc";
-import { Id } from "../../../new_fields/FieldSymbols";
-import { List } from "../../../new_fields/List";
-import { listSpec } from "../../../new_fields/Schema";
-import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField";
-import { ComputedField } from "../../../new_fields/ScriptField";
-import { Cast, FieldValue, NumCast, StrCast, BoolCast } from "../../../new_fields/Types";
+import { Doc, DocListCast, Field, Opt } from "../../../fields/Doc";
+import { Id } from "../../../fields/FieldSymbols";
+import { List } from "../../../fields/List";
+import { listSpec } from "../../../fields/Schema";
+import { SchemaHeaderField } from "../../../fields/SchemaHeaderField";
+import { ComputedField } from "../../../fields/ScriptField";
+import { Cast, FieldValue, NumCast, StrCast, BoolCast } from "../../../fields/Types";
 import { Docs, DocumentOptions } from "../../documents/Documents";
 import { CompileScript, Transformer, ts } from "../../util/Scripting";
 import { Transform } from "../../util/Transform";
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index d97f26daf..cc6077d98 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -4,14 +4,14 @@ import { CursorProperty } from "csstype";
 import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx";
 import { observer } from "mobx-react";
 import Switch from 'rc-switch';
-import { DataSym, Doc, HeightSym, WidthSym } from "../../../new_fields/Doc";
-import { collectionSchema, documentSchema } from "../../../new_fields/documentSchemas";
-import { Id } from "../../../new_fields/FieldSymbols";
-import { List } from "../../../new_fields/List";
-import { listSpec, makeInterface } from "../../../new_fields/Schema";
-import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField";
-import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from "../../../new_fields/Types";
-import { TraceMobx } from "../../../new_fields/util";
+import { DataSym, Doc, HeightSym, WidthSym } from "../../../fields/Doc";
+import { collectionSchema, documentSchema } from "../../../fields/documentSchemas";
+import { Id } from "../../../fields/FieldSymbols";
+import { List } from "../../../fields/List";
+import { listSpec, makeInterface } 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, returnOne, returnZero, setupMoveUpEvents, Utils, smoothScroll } from "../../../Utils";
 import { DragManager, dropActionType } from "../../util/DragManager";
 import { Transform } from "../../util/Transform";
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index dccef7983..53435ccc9 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -4,13 +4,13 @@ import { faPalette } from '@fortawesome/free-solid-svg-icons';
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { action, observable, runInAction } from "mobx";
 import { observer } from "mobx-react";
-import { Doc, DocListCast } from "../../../new_fields/Doc";
-import { RichTextField } from "../../../new_fields/RichTextField";
-import { PastelSchemaPalette, SchemaHeaderField } from "../../../new_fields/SchemaHeaderField";
-import { ScriptField } from "../../../new_fields/ScriptField";
-import { NumCast, StrCast, Cast } from "../../../new_fields/Types";
-import { ImageField } from "../../../new_fields/URLField";
-import { TraceMobx } from "../../../new_fields/util";
+import { Doc, DocListCast } from "../../../fields/Doc";
+import { RichTextField } from "../../../fields/RichTextField";
+import { PastelSchemaPalette, SchemaHeaderField } from "../../../fields/SchemaHeaderField";
+import { ScriptField } from "../../../fields/ScriptField";
+import { NumCast, StrCast, Cast } from "../../../fields/Types";
+import { ImageField } from "../../../fields/URLField";
+import { TraceMobx } from "../../../fields/util";
 import { Docs, DocUtils } from "../../documents/Documents";
 import { DragManager } from "../../util/DragManager";
 import { Transform } from "../../util/Transform";
@@ -21,7 +21,7 @@ import { EditableView } from "../EditableView";
 import { CollectionStackingView } from "./CollectionStackingView";
 import { setupMoveUpEvents, emptyFunction } from "../../../Utils";
 import "./CollectionStackingView.scss";
-import { listSpec } from "../../../new_fields/Schema";
+import { listSpec } from "../../../fields/Schema";
 import { SnappingManager } from "../../util/SnappingManager";
 const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
diff --git a/src/client/views/collections/CollectionStaffView.tsx b/src/client/views/collections/CollectionStaffView.tsx
index 5b9a69bf7..c5c3f96e8 100644
--- a/src/client/views/collections/CollectionStaffView.tsx
+++ b/src/client/views/collections/CollectionStaffView.tsx
@@ -1,7 +1,7 @@
 import { CollectionSubView } from "./CollectionSubView";
 import React = require("react");
 import { computed, action, IReactionDisposer, reaction, runInAction, observable } from "mobx";
-import { NumCast } from "../../../new_fields/Types";
+import { NumCast } from "../../../fields/Types";
 import "./CollectionStaffView.scss";
 import { observer } from "mobx-react";
 
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index bff6d121b..a4e843c8e 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -1,12 +1,12 @@
 import { action, computed, IReactionDisposer, reaction } from "mobx";
 import { basename } from 'path';
-import CursorField from "../../../new_fields/CursorField";
-import { Doc, Opt } from "../../../new_fields/Doc";
-import { Id } from "../../../new_fields/FieldSymbols";
-import { List } from "../../../new_fields/List";
-import { listSpec } from "../../../new_fields/Schema";
-import { ScriptField } from "../../../new_fields/ScriptField";
-import { Cast, ScriptCast } from "../../../new_fields/Types";
+import CursorField from "../../../fields/CursorField";
+import { Doc, Opt } 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 } from "../../../fields/Types";
 import { GestureUtils } from "../../../pen-gestures/GestureUtils";
 import { CurrentUserUtils } from "../../util/CurrentUserUtils";
 import { Upload } from "../../../server/SharedMediaTypes";
diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx
index a2d4774c8..15bc0bfd5 100644
--- a/src/client/views/collections/CollectionTimeView.tsx
+++ b/src/client/views/collections/CollectionTimeView.tsx
@@ -1,11 +1,11 @@
 import { action, computed, observable, runInAction } from "mobx";
 import { observer } from "mobx-react";
-import { Doc, Opt, DocCastAsync } from "../../../new_fields/Doc";
-import { List } from "../../../new_fields/List";
-import { ObjectField } from "../../../new_fields/ObjectField";
-import { RichTextField } from "../../../new_fields/RichTextField";
-import { ComputedField, ScriptField } from "../../../new_fields/ScriptField";
-import { NumCast, StrCast, BoolCast, Cast } from "../../../new_fields/Types";
+import { Doc, Opt, DocCastAsync } from "../../../fields/Doc";
+import { List } from "../../../fields/List";
+import { ObjectField } from "../../../fields/ObjectField";
+import { RichTextField } from "../../../fields/RichTextField";
+import { ComputedField, ScriptField } from "../../../fields/ScriptField";
+import { NumCast, StrCast, BoolCast, Cast } from "../../../fields/Types";
 import { emptyFunction, returnFalse, setupMoveUpEvents } from "../../../Utils";
 import { Scripting } from "../../util/Scripting";
 import { ContextMenu } from "../ContextMenu";
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 2f332e77d..3e99af724 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -1,13 +1,13 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { action, computed, observable } from "mobx";
 import { observer } from "mobx-react";
-import { DataSym, Doc, DocListCast, Field, HeightSym, Opt, WidthSym } from '../../../new_fields/Doc';
-import { Id } from '../../../new_fields/FieldSymbols';
-import { List } from '../../../new_fields/List';
-import { PrefetchProxy } from '../../../new_fields/Proxy';
-import { Document, listSpec } from '../../../new_fields/Schema';
-import { ComputedField, ScriptField } from '../../../new_fields/ScriptField';
-import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../new_fields/Types';
+import { DataSym, Doc, DocListCast, Field, HeightSym, Opt, WidthSym } from '../../../fields/Doc';
+import { Id } from '../../../fields/FieldSymbols';
+import { List } from '../../../fields/List';
+import { PrefetchProxy } from '../../../fields/Proxy';
+import { Document, listSpec } from '../../../fields/Schema';
+import { ComputedField, ScriptField } from '../../../fields/ScriptField';
+import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
 import { emptyFunction, emptyPath, returnFalse, returnOne, returnTrue, returnZero, simulateMouseClick, Utils } from '../../../Utils';
 import { Docs, DocUtils } from '../../documents/Documents';
 import { DocumentType } from "../../documents/DocumentTypes";
@@ -32,7 +32,7 @@ import "./CollectionTreeView.scss";
 import { CollectionViewType } from './CollectionView';
 import React = require("react");
 import { makeTemplate } from '../../util/DropConverter';
-import { TraceMobx } from '../../../new_fields/util';
+import { TraceMobx } from '../../../fields/util';
 
 export interface TreeViewProps {
     document: Doc;
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 4b3b2e234..3b2e5e4fc 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -7,12 +7,12 @@ import { observer } from "mobx-react";
 import * as React from 'react';
 import Lightbox from 'react-image-lightbox-with-rotate';
 import 'react-image-lightbox-with-rotate/style.css'; // This only needs to be imported once in your app
-import { DateField } from '../../../new_fields/DateField';
-import { DataSym, Doc, DocListCast, Field, Opt } from '../../../new_fields/Doc';
-import { List } from '../../../new_fields/List';
-import { BoolCast, Cast, NumCast, StrCast, ScriptCast } from '../../../new_fields/Types';
-import { ImageField } from '../../../new_fields/URLField';
-import { TraceMobx } from '../../../new_fields/util';
+import { DateField } from '../../../fields/DateField';
+import { DataSym, Doc, DocListCast, Field, Opt } from '../../../fields/Doc';
+import { List } from '../../../fields/List';
+import { BoolCast, Cast, NumCast, StrCast, ScriptCast } from '../../../fields/Types';
+import { ImageField } from '../../../fields/URLField';
+import { TraceMobx } from '../../../fields/util';
 import { Utils, setupMoveUpEvents, returnFalse, returnZero, emptyPath, emptyFunction, returnOne } from '../../../Utils';
 import { DocumentType } from '../../documents/DocumentTypes';
 import { ImageUtils } from '../../util/Import & Export/ImageUtils';
@@ -36,12 +36,12 @@ import { CollectionTreeView } from "./CollectionTreeView";
 import './CollectionView.scss';
 import { CollectionViewBaseChrome } from './CollectionViewChromes';
 import { CurrentUserUtils } from '../../util/CurrentUserUtils';
-import { Id } from '../../../new_fields/FieldSymbols';
-import { listSpec } from '../../../new_fields/Schema';
+import { Id } from '../../../fields/FieldSymbols';
+import { listSpec } from '../../../fields/Schema';
 import { Docs } from '../../documents/Documents';
-import { ScriptField, ComputedField } from '../../../new_fields/ScriptField';
+import { ScriptField, ComputedField } from '../../../fields/ScriptField';
 import { InteractionUtils } from '../../util/InteractionUtils';
-import { ObjectField } from '../../../new_fields/ObjectField';
+import { ObjectField } from '../../../fields/ObjectField';
 import CollectionMapView from './CollectionMapView';
 import { CollectionPileView } from './CollectionPileView';
 const higflyout = require("@hig/flyout");
diff --git a/src/client/views/collections/CollectionViewChromes.tsx b/src/client/views/collections/CollectionViewChromes.tsx
index 62b03bbdc..5dc0b09ac 100644
--- a/src/client/views/collections/CollectionViewChromes.tsx
+++ b/src/client/views/collections/CollectionViewChromes.tsx
@@ -2,11 +2,11 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { action, computed, observable, runInAction } from "mobx";
 import { observer } from "mobx-react";
 import * as React from "react";
-import { Doc, DocListCast } from "../../../new_fields/Doc";
-import { Id } from "../../../new_fields/FieldSymbols";
-import { List } from "../../../new_fields/List";
-import { listSpec } from "../../../new_fields/Schema";
-import { BoolCast, Cast, NumCast, StrCast } from "../../../new_fields/Types";
+import { Doc, DocListCast } from "../../../fields/Doc";
+import { Id } from "../../../fields/FieldSymbols";
+import { List } from "../../../fields/List";
+import { listSpec } from "../../../fields/Schema";
+import { BoolCast, Cast, NumCast, StrCast } from "../../../fields/Types";
 import { Utils, emptyFunction, setupMoveUpEvents } from "../../../Utils";
 import { DragManager } from "../../util/DragManager";
 import { undoBatch } from "../../util/UndoManager";
diff --git a/src/client/views/collections/ParentDocumentSelector.tsx b/src/client/views/collections/ParentDocumentSelector.tsx
index 6e4f801c0..649406e6c 100644
--- a/src/client/views/collections/ParentDocumentSelector.tsx
+++ b/src/client/views/collections/ParentDocumentSelector.tsx
@@ -1,12 +1,12 @@
 import * as React from "react";
 import './ParentDocumentSelector.scss';
-import { Doc } from "../../../new_fields/Doc";
+import { Doc } from "../../../fields/Doc";
 import { observer } from "mobx-react";
 import { observable, action, runInAction, trace, computed, reaction, IReactionDisposer } from "mobx";
-import { Id } from "../../../new_fields/FieldSymbols";
+import { Id } from "../../../fields/FieldSymbols";
 import { SearchUtil } from "../../util/SearchUtil";
 import { CollectionDockingView } from "./CollectionDockingView";
-import { NumCast, StrCast } from "../../../new_fields/Types";
+import { NumCast, StrCast } from "../../../fields/Types";
 import { CollectionViewType } from "./CollectionView";
 import { DocumentButtonBar } from "../DocumentButtonBar";
 import { DocumentManager } from "../../util/DocumentManager";
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
index 9a864078a..3860ce2d7 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
@@ -1,15 +1,15 @@
-import { Doc, Field, FieldResult, WidthSym, HeightSym } from "../../../../new_fields/Doc";
-import { NumCast, StrCast, Cast } from "../../../../new_fields/Types";
+import { Doc, Field, FieldResult, WidthSym, HeightSym } from "../../../../fields/Doc";
+import { NumCast, StrCast, Cast } from "../../../../fields/Types";
 import { ScriptBox } from "../../ScriptBox";
 import { CompileScript } from "../../../util/Scripting";
-import { ScriptField } from "../../../../new_fields/ScriptField";
+import { ScriptField } from "../../../../fields/ScriptField";
 import { OverlayView, OverlayElementOptions } from "../../OverlayView";
 import { emptyFunction, aggregateBounds } from "../../../../Utils";
 import React = require("react");
-import { Id, ToString } from "../../../../new_fields/FieldSymbols";
-import { ObjectField } from "../../../../new_fields/ObjectField";
-import { RefField } from "../../../../new_fields/RefField";
-import { listSpec } from "../../../../new_fields/Schema";
+import { Id, ToString } from "../../../../fields/FieldSymbols";
+import { ObjectField } from "../../../../fields/ObjectField";
+import { RefField } from "../../../../fields/RefField";
+import { listSpec } from "../../../../fields/Schema";
 
 export interface ViewDefBounds {
     type: string;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
index ba71aff32..f3fc04752 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
@@ -1,5 +1,5 @@
 import { observer } from "mobx-react";
-import { Doc } from "../../../../new_fields/Doc";
+import { Doc } from "../../../../fields/Doc";
 import { Utils } from '../../../../Utils';
 import { DocumentView } from "../../nodes/DocumentView";
 import "./CollectionFreeFormLinkView.scss";
@@ -7,8 +7,8 @@ import React = require("react");
 import v5 = require("uuid/v5");
 import { DocumentType } from "../../../documents/DocumentTypes";
 import { observable, action, reaction, IReactionDisposer } from "mobx";
-import { StrCast, Cast } from "../../../../new_fields/Types";
-import { Id } from "../../../../new_fields/FieldSymbols";
+import { StrCast, Cast } from "../../../../fields/Types";
+import { Id } from "../../../../fields/FieldSymbols";
 import { SnappingManager } from "../../../util/SnappingManager";
 
 export interface CollectionFreeFormLinkViewProps {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
index 1208fb324..ae81b4b36 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
@@ -1,7 +1,7 @@
 import { computed } from "mobx";
 import { observer } from "mobx-react";
-import { Doc } from "../../../../new_fields/Doc";
-import { Id } from "../../../../new_fields/FieldSymbols";
+import { Doc } from "../../../../fields/Doc";
+import { Id } from "../../../../fields/FieldSymbols";
 import { DocumentManager } from "../../../util/DocumentManager";
 import { DocumentView } from "../../nodes/DocumentView";
 import "./CollectionFreeFormLinksView.scss";
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
index 9a5b2c27c..548ad78a5 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
@@ -1,16 +1,16 @@
 import { observer } from "mobx-react";
 import * as mobxUtils from 'mobx-utils';
-import CursorField from "../../../../new_fields/CursorField";
-import { listSpec } from "../../../../new_fields/Schema";
-import { Cast } from "../../../../new_fields/Types";
+import CursorField from "../../../../fields/CursorField";
+import { listSpec } from "../../../../fields/Schema";
+import { Cast } from "../../../../fields/Types";
 import { CurrentUserUtils } from "../../../util/CurrentUserUtils";
 import { CollectionViewProps } from "../CollectionSubView";
 import "./CollectionFreeFormView.scss";
 import React = require("react");
 import v5 = require("uuid/v5");
 import { computed } from "mobx";
-import { FieldResult } from "../../../../new_fields/Doc";
-import { List } from "../../../../new_fields/List";
+import { FieldResult } from "../../../../fields/Doc";
+import { List } from "../../../../fields/List";
 
 @observer
 export class CollectionFreeFormRemoteCursors extends React.Component<CollectionViewProps> {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 6c4660c39..fb8bd25da 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -4,16 +4,16 @@ import { faBraille, faChalkboard, faCompass, faCompressArrowsAlt, faExpandArrows
 import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction, _allowStateChangesInsideComputed } from "mobx";
 import { observer } from "mobx-react";
 import { computedFn } from "mobx-utils";
-import { Doc, HeightSym, Opt, WidthSym, DocListCast } from "../../../../new_fields/Doc";
-import { documentSchema, collectionSchema } from "../../../../new_fields/documentSchemas";
-import { Id } from "../../../../new_fields/FieldSymbols";
-import { InkData, InkField, InkTool, PointData } from "../../../../new_fields/InkField";
-import { List } from "../../../../new_fields/List";
-import { RichTextField } from "../../../../new_fields/RichTextField";
-import { createSchema, listSpec, makeInterface } from "../../../../new_fields/Schema";
-import { ScriptField } from "../../../../new_fields/ScriptField";
-import { BoolCast, Cast, FieldValue, NumCast, ScriptCast, StrCast } from "../../../../new_fields/Types";
-import { TraceMobx } from "../../../../new_fields/util";
+import { Doc, HeightSym, Opt, WidthSym, DocListCast } from "../../../../fields/Doc";
+import { documentSchema, collectionSchema } from "../../../../fields/documentSchemas";
+import { Id } from "../../../../fields/FieldSymbols";
+import { InkData, InkField, InkTool, PointData } from "../../../../fields/InkField";
+import { List } from "../../../../fields/List";
+import { RichTextField } from "../../../../fields/RichTextField";
+import { createSchema, listSpec, makeInterface } from "../../../../fields/Schema";
+import { ScriptField } from "../../../../fields/ScriptField";
+import { BoolCast, Cast, FieldValue, NumCast, ScriptCast, StrCast } from "../../../../fields/Types";
+import { TraceMobx } from "../../../../fields/util";
 import { GestureUtils } from "../../../../pen-gestures/GestureUtils";
 import { aggregateBounds, intersectRect, returnOne, Utils, returnZero, returnFalse } from "../../../../Utils";
 import { CognitiveServices } from "../../../cognitive_services/CognitiveServices";
@@ -1034,8 +1034,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         const { newPool, computedElementData } = this.doInternalLayoutComputation;
         const array = Array.from(newPool.entries());
         runInAction(() => {
-            for (let i = 0; i < array.length; i++) {
-                const entry = array[i];
+            for (const entry of array) {
                 const lastPos = this._cachedPool.get(entry[0]); // last computed pos
                 const newPos = entry[1];
                 if (!lastPos || newPos.x !== lastPos.x || newPos.y !== lastPos.y || newPos.z !== lastPos.z || newPos.zIndex !== lastPos.zIndex) {
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 8e20b39d2..04d37934e 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -1,11 +1,11 @@
 import { action, computed, observable } from "mobx";
 import { observer } from "mobx-react";
-import { Doc, Opt } from "../../../../new_fields/Doc";
-import { InkData, InkField } from "../../../../new_fields/InkField";
-import { List } from "../../../../new_fields/List";
-import { RichTextField } from "../../../../new_fields/RichTextField";
-import { SchemaHeaderField } from "../../../../new_fields/SchemaHeaderField";
-import { Cast, FieldValue, NumCast, StrCast } from "../../../../new_fields/Types";
+import { Doc, Opt } from "../../../../fields/Doc";
+import { InkData, InkField } 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 { Utils } from "../../../../Utils";
 import { CognitiveServices } from "../../../cognitive_services/CognitiveServices";
 import { Docs, DocumentOptions, DocUtils } from "../../../documents/Documents";
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
index a09124304..c0e1a0232 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -1,10 +1,10 @@
 import { action, computed } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from "react";
-import { Doc } from '../../../../new_fields/Doc';
-import { documentSchema } from '../../../../new_fields/documentSchemas';
-import { makeInterface } from '../../../../new_fields/Schema';
-import { BoolCast, NumCast, ScriptCast, StrCast, Cast } from '../../../../new_fields/Types';
+import { Doc } from '../../../../fields/Doc';
+import { documentSchema } from '../../../../fields/documentSchemas';
+import { makeInterface } from '../../../../fields/Schema';
+import { BoolCast, NumCast, ScriptCast, StrCast, Cast } from '../../../../fields/Types';
 import { DragManager, dropActionType } from '../../../util/DragManager';
 import { Transform } from '../../../util/Transform';
 import { undoBatch } from '../../../util/UndoManager';
@@ -13,7 +13,7 @@ import { CollectionSubView } from '../CollectionSubView';
 import "./collectionMulticolumnView.scss";
 import ResizeBar from './MulticolumnResizer';
 import WidthLabel from './MulticolumnWidthLabel';
-import { List } from '../../../../new_fields/List';
+import { List } from '../../../../fields/List';
 import { returnZero, returnFalse, returnOne } from '../../../../Utils';
 
 type MulticolumnDocument = makeInterface<[typeof documentSchema]>;
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
index 4326723b1..602246d07 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
@@ -1,10 +1,10 @@
 import { observer } from 'mobx-react';
-import { makeInterface } from '../../../../new_fields/Schema';
-import { documentSchema } from '../../../../new_fields/documentSchemas';
+import { makeInterface } from '../../../../fields/Schema';
+import { documentSchema } from '../../../../fields/documentSchemas';
 import { CollectionSubView, SubCollectionViewProps } from '../CollectionSubView';
 import * as React from "react";
-import { Doc } from '../../../../new_fields/Doc';
-import { NumCast, StrCast, BoolCast, ScriptCast } from '../../../../new_fields/Types';
+import { Doc } from '../../../../fields/Doc';
+import { NumCast, StrCast, BoolCast, ScriptCast } from '../../../../fields/Types';
 import { ContentFittingDocumentView } from '../../nodes/ContentFittingDocumentView';
 import { Utils, returnZero, returnFalse, returnOne } from '../../../../Utils';
 import "./collectionMultirowView.scss";
@@ -14,7 +14,7 @@ import HeightLabel from './MultirowHeightLabel';
 import ResizeBar from './MultirowResizer';
 import { undoBatch } from '../../../util/UndoManager';
 import { DragManager, dropActionType } from '../../../util/DragManager';
-import { List } from '../../../../new_fields/List';
+import { List } from '../../../../fields/List';
 
 type MultirowDocument = makeInterface<[typeof documentSchema]>;
 const MultirowDocument = makeInterface(documentSchema);
diff --git a/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx b/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx
index a24eb1631..734915a93 100644
--- a/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx
+++ b/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx
@@ -1,8 +1,8 @@
 import * as React from "react";
 import { observer } from "mobx-react";
 import { observable, action } from "mobx";
-import { Doc } from "../../../../new_fields/Doc";
-import { NumCast, StrCast } from "../../../../new_fields/Types";
+import { Doc } from "../../../../fields/Doc";
+import { NumCast, StrCast } from "../../../../fields/Types";
 import { DimUnit } from "./CollectionMulticolumnView";
 import { UndoManager } from "../../../util/UndoManager";
 
diff --git a/src/client/views/collections/collectionMulticolumn/MulticolumnWidthLabel.tsx b/src/client/views/collections/collectionMulticolumn/MulticolumnWidthLabel.tsx
index 5b2054428..9985a9fba 100644
--- a/src/client/views/collections/collectionMulticolumn/MulticolumnWidthLabel.tsx
+++ b/src/client/views/collections/collectionMulticolumn/MulticolumnWidthLabel.tsx
@@ -1,8 +1,8 @@
 import * as React from "react";
 import { observer } from "mobx-react";
 import { computed } from "mobx";
-import { Doc } from "../../../../new_fields/Doc";
-import { NumCast, StrCast, BoolCast } from "../../../../new_fields/Types";
+import { Doc } from "../../../../fields/Doc";
+import { NumCast, StrCast, BoolCast } from "../../../../fields/Types";
 import { EditableView } from "../../EditableView";
 import { DimUnit } from "./CollectionMulticolumnView";
 
diff --git a/src/client/views/collections/collectionMulticolumn/MultirowHeightLabel.tsx b/src/client/views/collections/collectionMulticolumn/MultirowHeightLabel.tsx
index 899577fd5..aa5439fa4 100644
--- a/src/client/views/collections/collectionMulticolumn/MultirowHeightLabel.tsx
+++ b/src/client/views/collections/collectionMulticolumn/MultirowHeightLabel.tsx
@@ -1,8 +1,8 @@
 import * as React from "react";
 import { observer } from "mobx-react";
 import { computed } from "mobx";
-import { Doc } from "../../../../new_fields/Doc";
-import { NumCast, StrCast, BoolCast } from "../../../../new_fields/Types";
+import { Doc } from "../../../../fields/Doc";
+import { NumCast, StrCast, BoolCast } from "../../../../fields/Types";
 import { EditableView } from "../../EditableView";
 import { DimUnit } from "./CollectionMultirowView";
 
diff --git a/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx b/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx
index 5f00b18b9..d0bc4d01c 100644
--- a/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx
+++ b/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx
@@ -1,8 +1,8 @@
 import * as React from "react";
 import { observer } from "mobx-react";
 import { observable, action } from "mobx";
-import { Doc } from "../../../../new_fields/Doc";
-import { NumCast, StrCast } from "../../../../new_fields/Types";
+import { Doc } from "../../../../fields/Doc";
+import { NumCast, StrCast } from "../../../../fields/Types";
 import { DimUnit } from "./CollectionMultirowView";
 import { UndoManager } from "../../../util/UndoManager";
 
diff --git a/src/client/views/linking/LinkEditor.tsx b/src/client/views/linking/LinkEditor.tsx
index b7f3dd995..5fb4cf3c6 100644
--- a/src/client/views/linking/LinkEditor.tsx
+++ b/src/client/views/linking/LinkEditor.tsx
@@ -3,8 +3,8 @@ import { faArrowLeft, faCog, faEllipsisV, faExchangeAlt, faPlus, faTable, faTime
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { action, observable } from "mobx";
 import { observer } from "mobx-react";
-import { Doc } from "../../../new_fields/Doc";
-import { StrCast } from "../../../new_fields/Types";
+import { Doc } from "../../../fields/Doc";
+import { StrCast } from "../../../fields/Types";
 import { Utils } from "../../../Utils";
 import { LinkManager } from "../../util/LinkManager";
 import './LinkEditor.scss';
diff --git a/src/client/views/linking/LinkMenu.tsx b/src/client/views/linking/LinkMenu.tsx
index b768eacc3..786d6be47 100644
--- a/src/client/views/linking/LinkMenu.tsx
+++ b/src/client/views/linking/LinkMenu.tsx
@@ -4,7 +4,7 @@ import { DocumentView } from "../nodes/DocumentView";
 import { LinkEditor } from "./LinkEditor";
 import './LinkMenu.scss';
 import React = require("react");
-import { Doc } from "../../../new_fields/Doc";
+import { Doc } from "../../../fields/Doc";
 import { LinkManager } from "../../util/LinkManager";
 import { LinkMenuGroup } from "./LinkMenuGroup";
 import { faTrash } from '@fortawesome/free-solid-svg-icons';
diff --git a/src/client/views/linking/LinkMenuGroup.tsx b/src/client/views/linking/LinkMenuGroup.tsx
index c97e1062d..89deb3a55 100644
--- a/src/client/views/linking/LinkMenuGroup.tsx
+++ b/src/client/views/linking/LinkMenuGroup.tsx
@@ -1,9 +1,9 @@
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { action } from "mobx";
 import { observer } from "mobx-react";
-import { Doc } from "../../../new_fields/Doc";
-import { Id } from "../../../new_fields/FieldSymbols";
-import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField";
+import { Doc } from "../../../fields/Doc";
+import { Id } from "../../../fields/FieldSymbols";
+import { SchemaHeaderField } from "../../../fields/SchemaHeaderField";
 import { Docs } from "../../documents/Documents";
 import { DragManager, SetupDrag } from "../../util/DragManager";
 import { LinkManager } from "../../util/LinkManager";
diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx
index b0c61cb04..17cd33241 100644
--- a/src/client/views/linking/LinkMenuItem.tsx
+++ b/src/client/views/linking/LinkMenuItem.tsx
@@ -3,8 +3,8 @@ import { faArrowRight, faChevronDown, faChevronUp, faEdit, faEye, faTimes } from
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { action, observable } from 'mobx';
 import { observer } from "mobx-react";
-import { Doc, DocListCast } from '../../../new_fields/Doc';
-import { Cast, StrCast } from '../../../new_fields/Types';
+import { Doc, DocListCast } from '../../../fields/Doc';
+import { Cast, StrCast } from '../../../fields/Types';
 import { DragManager } from '../../util/DragManager';
 import { LinkManager } from '../../util/LinkManager';
 import { ContextMenu } from '../ContextMenu';
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index 1c5e13620..1a935d9b0 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -2,23 +2,23 @@ import React = require("react");
 import { FieldViewProps, FieldView } from './FieldView';
 import { observer } from "mobx-react";
 import "./AudioBox.scss";
-import { Cast, DateCast, NumCast } from "../../../new_fields/Types";
-import { AudioField, nullAudio } from "../../../new_fields/URLField";
+import { Cast, DateCast, NumCast } from "../../../fields/Types";
+import { AudioField, nullAudio } from "../../../fields/URLField";
 import { ViewBoxBaseComponent } from "../DocComponent";
-import { makeInterface, createSchema } from "../../../new_fields/Schema";
-import { documentSchema } from "../../../new_fields/documentSchemas";
+import { makeInterface, createSchema } from "../../../fields/Schema";
+import { documentSchema } from "../../../fields/documentSchemas";
 import { Utils, returnTrue, emptyFunction, returnOne, returnTransparent, returnFalse, returnZero } from "../../../Utils";
 import { runInAction, observable, reaction, IReactionDisposer, computed, action } from "mobx";
-import { DateField } from "../../../new_fields/DateField";
+import { DateField } from "../../../fields/DateField";
 import { SelectionManager } from "../../util/SelectionManager";
-import { Doc, DocListCast } from "../../../new_fields/Doc";
+import { Doc, DocListCast } from "../../../fields/Doc";
 import { ContextMenuProps } from "../ContextMenuItem";
 import { ContextMenu } from "../ContextMenu";
-import { Id } from "../../../new_fields/FieldSymbols";
+import { Id } from "../../../fields/FieldSymbols";
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { DocumentView } from "./DocumentView";
 import { Docs, DocUtils } from "../../documents/Documents";
-import { ComputedField } from "../../../new_fields/ScriptField";
+import { ComputedField } from "../../../fields/ScriptField";
 import { Networking } from "../../Network";
 import { LinkAnchorBox } from "./LinkAnchorBox";
 
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index cdbe506a5..5d6e587d9 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -1,14 +1,14 @@
 import { computed, IReactionDisposer, observable, reaction, trace } from "mobx";
 import { observer } from "mobx-react";
-import { Doc, HeightSym, WidthSym } from "../../../new_fields/Doc";
-import { Cast, NumCast, StrCast } from "../../../new_fields/Types";
+import { Doc, HeightSym, WidthSym } from "../../../fields/Doc";
+import { Cast, NumCast, StrCast } from "../../../fields/Types";
 import { Transform } from "../../util/Transform";
 import { DocComponent } from "../DocComponent";
 import "./CollectionFreeFormDocumentView.scss";
 import { DocumentView, DocumentViewProps } from "./DocumentView";
 import React = require("react");
-import { Document } from "../../../new_fields/documentSchemas";
-import { TraceMobx } from "../../../new_fields/util";
+import { Document } from "../../../fields/documentSchemas";
+import { TraceMobx } from "../../../fields/util";
 import { ContentFittingDocumentView } from "./ContentFittingDocumentView";
 
 export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps {
diff --git a/src/client/views/nodes/ColorBox.tsx b/src/client/views/nodes/ColorBox.tsx
index bc84204b9..bef6609c4 100644
--- a/src/client/views/nodes/ColorBox.tsx
+++ b/src/client/views/nodes/ColorBox.tsx
@@ -1,9 +1,9 @@
 import React = require("react");
 import { observer } from "mobx-react";
 import { SketchPicker } from 'react-color';
-import { documentSchema } from "../../../new_fields/documentSchemas";
-import { makeInterface } from "../../../new_fields/Schema";
-import { StrCast } from "../../../new_fields/Types";
+import { documentSchema } from "../../../fields/documentSchemas";
+import { makeInterface } from "../../../fields/Schema";
+import { StrCast } from "../../../fields/Types";
 import { CurrentUserUtils } from "../../util/CurrentUserUtils";
 import { SelectionManager } from "../../util/SelectionManager";
 import { ViewBoxBaseComponent } from "../DocComponent";
diff --git a/src/client/views/nodes/ContentFittingDocumentView.tsx b/src/client/views/nodes/ContentFittingDocumentView.tsx
index 1c6250b94..a90b4668e 100644
--- a/src/client/views/nodes/ContentFittingDocumentView.tsx
+++ b/src/client/views/nodes/ContentFittingDocumentView.tsx
@@ -2,9 +2,9 @@ import React = require("react");
 import { computed } from "mobx";
 import { observer } from "mobx-react";
 import "react-table/react-table.css";
-import { Doc, Opt, WidthSym, HeightSym } from "../../../new_fields/Doc";
-import { NumCast, StrCast, Cast } from "../../../new_fields/Types";
-import { TraceMobx } from "../../../new_fields/util";
+import { Doc, Opt, WidthSym, HeightSym } from "../../../fields/Doc";
+import { NumCast, StrCast, Cast } from "../../../fields/Types";
+import { TraceMobx } from "../../../fields/util";
 import { emptyFunction, returnOne } from "../../../Utils";
 import '../DocumentDecorations.scss';
 import { DocumentView, DocumentViewProps } from "../nodes/DocumentView";
diff --git a/src/client/views/nodes/DocHolderBox.tsx b/src/client/views/nodes/DocHolderBox.tsx
index af8dc704c..0c5239d66 100644
--- a/src/client/views/nodes/DocHolderBox.tsx
+++ b/src/client/views/nodes/DocHolderBox.tsx
@@ -1,12 +1,12 @@
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { action, IReactionDisposer, reaction } from "mobx";
 import { observer } from "mobx-react";
-import { Doc, Field } from "../../../new_fields/Doc";
-import { collectionSchema, documentSchema } from "../../../new_fields/documentSchemas";
-import { makeInterface, listSpec } from "../../../new_fields/Schema";
-import { ComputedField } from "../../../new_fields/ScriptField";
-import { Cast, NumCast, StrCast } from "../../../new_fields/Types";
-import { TraceMobx } from "../../../new_fields/util";
+import { Doc, Field } from "../../../fields/Doc";
+import { collectionSchema, documentSchema } from "../../../fields/documentSchemas";
+import { makeInterface, listSpec } from "../../../fields/Schema";
+import { ComputedField } from "../../../fields/ScriptField";
+import { Cast, NumCast, StrCast } from "../../../fields/Types";
+import { TraceMobx } from "../../../fields/util";
 import { emptyPath, returnFalse, returnOne, returnZero } from "../../../Utils";
 import { DocumentType } from "../../documents/DocumentTypes";
 import { DragManager } from "../../util/DragManager";
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 2fc82e524..f4785bb0c 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -1,7 +1,7 @@
 import { computed } from "mobx";
 import { observer } from "mobx-react";
-import { Doc, Opt, Field } from "../../../new_fields/Doc";
-import { Cast, StrCast, NumCast } from "../../../new_fields/Types";
+import { Doc, Opt, Field } from "../../../fields/Doc";
+import { Cast, StrCast, NumCast } from "../../../fields/Types";
 import { OmitKeys, Without, emptyPath } from "../../../Utils";
 import DirectoryImportBox from "../../util/Import & Export/DirectoryImportBox";
 import { CollectionDockingView } from "../collections/CollectionDockingView";
@@ -35,8 +35,8 @@ import { WebBox } from "./WebBox";
 import { InkingStroke } from "../InkingStroke";
 import React = require("react");
 import { RecommendationsBox } from "../RecommendationsBox";
-import { TraceMobx } from "../../../new_fields/util";
-import { ScriptField } from "../../../new_fields/ScriptField";
+import { TraceMobx } from "../../../fields/util";
+import { ScriptField } from "../../../fields/ScriptField";
 import XRegExp = require("xregexp");
 
 const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this?
diff --git a/src/client/views/nodes/DocumentIcon.tsx b/src/client/views/nodes/DocumentIcon.tsx
index f56f5e829..fb54f18e8 100644
--- a/src/client/views/nodes/DocumentIcon.tsx
+++ b/src/client/views/nodes/DocumentIcon.tsx
@@ -3,7 +3,7 @@ import * as React from "react";
 import { DocumentView } from "./DocumentView";
 import { DocumentManager } from "../../util/DocumentManager";
 import { Transformer, Scripting, ts } from "../../util/Scripting";
-import { Field } from "../../../new_fields/Doc";
+import { Field } from "../../../fields/Doc";
 
 @observer
 export class DocumentIcon extends React.Component<{ view: DocumentView, index: number }> {
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index d821bb8ac..340fa06a8 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -4,16 +4,16 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { action, computed, observable, runInAction } from "mobx";
 import { observer } from "mobx-react";
 import * as rp from "request-promise";
-import { Doc, DocListCast, HeightSym, Opt, WidthSym, DataSym } from "../../../new_fields/Doc";
-import { Document } from '../../../new_fields/documentSchemas';
-import { Id } from '../../../new_fields/FieldSymbols';
-import { InkTool } from '../../../new_fields/InkField';
-import { listSpec } from "../../../new_fields/Schema";
-import { SchemaHeaderField } from '../../../new_fields/SchemaHeaderField';
-import { ScriptField } from '../../../new_fields/ScriptField';
-import { BoolCast, Cast, NumCast, StrCast } from "../../../new_fields/Types";
-import { ImageField } from '../../../new_fields/URLField';
-import { TraceMobx } from '../../../new_fields/util';
+import { Doc, DocListCast, HeightSym, Opt, WidthSym, DataSym } from "../../../fields/Doc";
+import { Document } from '../../../fields/documentSchemas';
+import { Id } from '../../../fields/FieldSymbols';
+import { InkTool } from '../../../fields/InkField';
+import { listSpec } from "../../../fields/Schema";
+import { SchemaHeaderField } from '../../../fields/SchemaHeaderField';
+import { ScriptField } from '../../../fields/ScriptField';
+import { BoolCast, Cast, NumCast, StrCast } from "../../../fields/Types";
+import { ImageField } from '../../../fields/URLField';
+import { TraceMobx } from '../../../fields/util';
 import { GestureUtils } from '../../../pen-gestures/GestureUtils';
 import { emptyFunction, OmitKeys, returnOne, returnTransparent, Utils, emptyPath } from "../../../Utils";
 import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils';
diff --git a/src/client/views/nodes/FaceRectangles.tsx b/src/client/views/nodes/FaceRectangles.tsx
index 3c7f1f206..92ca276cb 100644
--- a/src/client/views/nodes/FaceRectangles.tsx
+++ b/src/client/views/nodes/FaceRectangles.tsx
@@ -1,8 +1,8 @@
 import React = require("react");
-import { Doc, DocListCast } from "../../../new_fields/Doc";
-import { Cast, NumCast } from "../../../new_fields/Types";
+import { Doc, DocListCast } from "../../../fields/Doc";
+import { Cast, NumCast } from "../../../fields/Types";
 import { observer } from "mobx-react";
-import { Id } from "../../../new_fields/FieldSymbols";
+import { Id } from "../../../fields/FieldSymbols";
 import FaceRectangle from "./FaceRectangle";
 
 interface FaceRectanglesProps {
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 2ab0a416d..e9dc43bd8 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -1,11 +1,11 @@
 import React = require("react");
 import { computed } from "mobx";
 import { observer } from "mobx-react";
-import { DateField } from "../../../new_fields/DateField";
-import { Doc, FieldResult, Opt, Field } from "../../../new_fields/Doc";
-import { List } from "../../../new_fields/List";
-import { ScriptField } from "../../../new_fields/ScriptField";
-import { AudioField, VideoField } from "../../../new_fields/URLField";
+import { DateField } from "../../../fields/DateField";
+import { Doc, FieldResult, Opt, Field } from "../../../fields/Doc";
+import { List } from "../../../fields/List";
+import { ScriptField } from "../../../fields/ScriptField";
+import { AudioField, VideoField } from "../../../fields/URLField";
 import { Transform } from "../../util/Transform";
 import { CollectionView } from "../collections/CollectionView";
 import { AudioBox } from "./AudioBox";
diff --git a/src/client/views/nodes/FontIconBox.tsx b/src/client/views/nodes/FontIconBox.tsx
index 0d597b3a8..cf0b16c7c 100644
--- a/src/client/views/nodes/FontIconBox.tsx
+++ b/src/client/views/nodes/FontIconBox.tsx
@@ -1,16 +1,16 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { observer } from 'mobx-react';
 import * as React from 'react';
-import { createSchema, makeInterface } from '../../../new_fields/Schema';
+import { createSchema, makeInterface } from '../../../fields/Schema';
 import { DocComponent } from '../DocComponent';
 import './FontIconBox.scss';
 import { FieldView, FieldViewProps } from './FieldView';
-import { StrCast, Cast } from '../../../new_fields/Types';
+import { StrCast, Cast } from '../../../fields/Types';
 import { Utils } from "../../../Utils";
 import { runInAction, observable, reaction, IReactionDisposer } from 'mobx';
-import { Doc } from '../../../new_fields/Doc';
+import { Doc } from '../../../fields/Doc';
 import { ContextMenu } from '../ContextMenu';
-import { ScriptField } from '../../../new_fields/ScriptField';
+import { ScriptField } from '../../../fields/ScriptField';
 const FontIconSchema = createSchema({
     icon: "string"
 });
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index aaebceaa2..47e7607d6 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -4,16 +4,16 @@ import { faAsterisk, faBrain, faFileAudio, faImage, faPaintBrush } from '@fortaw
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { action, computed, observable, runInAction } from 'mobx';
 import { observer } from "mobx-react";
-import { DataSym, Doc, DocListCast, HeightSym, WidthSym } from '../../../new_fields/Doc';
-import { documentSchema } from '../../../new_fields/documentSchemas';
-import { Id } from '../../../new_fields/FieldSymbols';
-import { List } from '../../../new_fields/List';
-import { ObjectField } from '../../../new_fields/ObjectField';
-import { createSchema, listSpec, makeInterface } from '../../../new_fields/Schema';
-import { ComputedField } from '../../../new_fields/ScriptField';
-import { Cast, NumCast, StrCast } from '../../../new_fields/Types';
-import { AudioField, ImageField } from '../../../new_fields/URLField';
-import { TraceMobx } from '../../../new_fields/util';
+import { DataSym, Doc, DocListCast, HeightSym, WidthSym } from '../../../fields/Doc';
+import { documentSchema } from '../../../fields/documentSchemas';
+import { Id } from '../../../fields/FieldSymbols';
+import { List } from '../../../fields/List';
+import { ObjectField } from '../../../fields/ObjectField';
+import { createSchema, listSpec, makeInterface } from '../../../fields/Schema';
+import { ComputedField } from '../../../fields/ScriptField';
+import { Cast, NumCast, StrCast } from '../../../fields/Types';
+import { AudioField, ImageField } from '../../../fields/URLField';
+import { TraceMobx } from '../../../fields/util';
 import { emptyFunction, returnOne, Utils, returnZero } from '../../../Utils';
 import { CognitiveServices, Confidence, Service, Tag } from '../../cognitive_services/CognitiveServices';
 import { Docs } from '../../documents/Documents';
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index 39d7109b1..e983852ea 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -1,13 +1,13 @@
 
 import { action, computed, observable } from "mobx";
 import { observer } from "mobx-react";
-import { Doc, Field, FieldResult } from "../../../new_fields/Doc";
-import { List } from "../../../new_fields/List";
-import { RichTextField } from "../../../new_fields/RichTextField";
-import { listSpec } from "../../../new_fields/Schema";
-import { ComputedField, ScriptField } from "../../../new_fields/ScriptField";
-import { Cast, FieldValue, NumCast } from "../../../new_fields/Types";
-import { ImageField } from "../../../new_fields/URLField";
+import { Doc, Field, FieldResult } 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, FieldValue, NumCast } from "../../../fields/Types";
+import { ImageField } from "../../../fields/URLField";
 import { Docs } from "../../documents/Documents";
 import { SetupDrag } from "../../util/DragManager";
 import { CompiledScript, CompileScript, ScriptOptions } from "../../util/Scripting";
diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx
index 6dc4ae578..956d6556b 100644
--- a/src/client/views/nodes/KeyValuePair.tsx
+++ b/src/client/views/nodes/KeyValuePair.tsx
@@ -1,6 +1,6 @@
 import { action, observable } from 'mobx';
 import { observer } from "mobx-react";
-import { Doc, Field, Opt } from '../../../new_fields/Doc';
+import { Doc, Field, Opt } from '../../../fields/Doc';
 import { emptyFunction, returnFalse, returnOne, returnZero } from '../../../Utils';
 import { Docs } from '../../documents/Documents';
 import { Transform } from '../../util/Transform';
diff --git a/src/client/views/nodes/LabelBox.tsx b/src/client/views/nodes/LabelBox.tsx
index ac27640bd..2d27ec441 100644
--- a/src/client/views/nodes/LabelBox.tsx
+++ b/src/client/views/nodes/LabelBox.tsx
@@ -3,11 +3,11 @@ import { faEdit } from '@fortawesome/free-regular-svg-icons';
 import { action } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
-import { Doc, DocListCast } from '../../../new_fields/Doc';
-import { documentSchema } from '../../../new_fields/documentSchemas';
-import { List } from '../../../new_fields/List';
-import { createSchema, listSpec, makeInterface } from '../../../new_fields/Schema';
-import { Cast, NumCast, StrCast } from '../../../new_fields/Types';
+import { Doc, DocListCast } from '../../../fields/Doc';
+import { documentSchema } from '../../../fields/documentSchemas';
+import { List } from '../../../fields/List';
+import { createSchema, listSpec, makeInterface } from '../../../fields/Schema';
+import { Cast, NumCast, StrCast } from '../../../fields/Types';
 import { DragManager } from '../../util/DragManager';
 import { undoBatch } from '../../util/UndoManager';
 import { ContextMenu } from '../ContextMenu';
diff --git a/src/client/views/nodes/LinkAnchorBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx
index bc36e056e..098aa58e9 100644
--- a/src/client/views/nodes/LinkAnchorBox.tsx
+++ b/src/client/views/nodes/LinkAnchorBox.tsx
@@ -1,9 +1,9 @@
 import { action, observable } from "mobx";
 import { observer } from "mobx-react";
-import { Doc, DocListCast } from "../../../new_fields/Doc";
-import { documentSchema } from "../../../new_fields/documentSchemas";
-import { makeInterface } from "../../../new_fields/Schema";
-import { Cast, NumCast, StrCast } from "../../../new_fields/Types";
+import { Doc, DocListCast } from "../../../fields/Doc";
+import { documentSchema } from "../../../fields/documentSchemas";
+import { makeInterface } from "../../../fields/Schema";
+import { Cast, NumCast, StrCast } from "../../../fields/Types";
 import { Utils, setupMoveUpEvents, emptyFunction } from '../../../Utils';
 import { DocumentManager } from "../../util/DocumentManager";
 import { DragManager } from "../../util/DragManager";
@@ -16,7 +16,7 @@ import { ContextMenu } from "../ContextMenu";
 import { LinkEditor } from "../linking/LinkEditor";
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { SelectionManager } from "../../util/SelectionManager";
-import { TraceMobx } from "../../../new_fields/util";
+import { TraceMobx } from "../../../fields/util";
 const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx
index 740f2ef04..3f942e87b 100644
--- a/src/client/views/nodes/LinkBox.tsx
+++ b/src/client/views/nodes/LinkBox.tsx
@@ -1,13 +1,13 @@
 import React = require("react");
 import { observer } from "mobx-react";
-import { documentSchema } from "../../../new_fields/documentSchemas";
-import { makeInterface, listSpec } from "../../../new_fields/Schema";
+import { documentSchema } from "../../../fields/documentSchemas";
+import { makeInterface, listSpec } from "../../../fields/Schema";
 import { returnFalse, returnZero } from "../../../Utils";
 import { CollectionTreeView } from "../collections/CollectionTreeView";
 import { ViewBoxBaseComponent } from "../DocComponent";
 import { FieldView, FieldViewProps } from './FieldView';
 import "./LinkBox.scss";
-import { Cast } from "../../../new_fields/Types";
+import { Cast } from "../../../fields/Types";
 
 type LinkDocument = makeInterface<[typeof documentSchema]>;
 const LinkDocument = makeInterface(documentSchema);
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 3712c648e..493f23dc4 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -3,11 +3,11 @@ import { action, observable, runInAction, reaction, IReactionDisposer, trace, un
 import { observer } from "mobx-react";
 import * as Pdfjs from "pdfjs-dist";
 import "pdfjs-dist/web/pdf_viewer.css";
-import { Opt, WidthSym, Doc, HeightSym } from "../../../new_fields/Doc";
-import { makeInterface } from "../../../new_fields/Schema";
-import { ScriptField } from '../../../new_fields/ScriptField';
-import { Cast, NumCast, StrCast } from "../../../new_fields/Types";
-import { PdfField, URLField } from "../../../new_fields/URLField";
+import { Opt, WidthSym, Doc, HeightSym } from "../../../fields/Doc";
+import { makeInterface } from "../../../fields/Schema";
+import { ScriptField } from '../../../fields/ScriptField';
+import { Cast, NumCast, StrCast } from "../../../fields/Types";
+import { PdfField, URLField } from "../../../fields/URLField";
 import { Utils } from '../../../Utils';
 import { undoBatch } from '../../util/UndoManager';
 import { panZoomSchema } from '../collections/collectionFreeForm/CollectionFreeFormView';
@@ -20,7 +20,7 @@ import { pageSchema } from "./ImageBox";
 import { KeyCodes } from '../../util/KeyCodes';
 import "./PDFBox.scss";
 import React = require("react");
-import { documentSchema } from '../../../new_fields/documentSchemas';
+import { documentSchema } from '../../../fields/documentSchemas';
 
 type PdfDocument = makeInterface<[typeof documentSchema, typeof panZoomSchema, typeof pageSchema]>;
 const PdfDocument = makeInterface(documentSchema, panZoomSchema, pageSchema);
diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx
index 42552ec73..342a8a215 100644
--- a/src/client/views/nodes/PresBox.tsx
+++ b/src/client/views/nodes/PresBox.tsx
@@ -2,11 +2,11 @@ import React = require("react");
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx";
 import { observer } from "mobx-react";
-import { Doc, DocListCast, DocCastAsync } from "../../../new_fields/Doc";
-import { InkTool } from "../../../new_fields/InkField";
-import { BoolCast, Cast, NumCast, StrCast } from "../../../new_fields/Types";
+import { Doc, DocListCast, DocCastAsync } from "../../../fields/Doc";
+import { InkTool } from "../../../fields/InkField";
+import { BoolCast, Cast, NumCast, StrCast } from "../../../fields/Types";
 import { returnFalse } from "../../../Utils";
-import { documentSchema } from "../../../new_fields/documentSchemas";
+import { documentSchema } from "../../../fields/documentSchemas";
 import { DocumentManager } from "../../util/DocumentManager";
 import { undoBatch } from "../../util/UndoManager";
 import { CollectionDockingView } from "../collections/CollectionDockingView";
@@ -15,11 +15,11 @@ import { InkingControl } from "../InkingControl";
 import { FieldView, FieldViewProps } from './FieldView';
 import "./PresBox.scss";
 import { ViewBoxBaseComponent } from "../DocComponent";
-import { makeInterface } from "../../../new_fields/Schema";
-import { List } from "../../../new_fields/List";
+import { makeInterface } from "../../../fields/Schema";
+import { List } from "../../../fields/List";
 import { Docs } from "../../documents/Documents";
-import { PrefetchProxy } from "../../../new_fields/Proxy";
-import { ScriptField } from "../../../new_fields/ScriptField";
+import { PrefetchProxy } from "../../../fields/Proxy";
+import { ScriptField } from "../../../fields/ScriptField";
 import { Scripting } from "../../util/Scripting";
 
 type PresBoxSchema = makeInterface<[typeof documentSchema]>;
diff --git a/src/client/views/nodes/QueryBox.tsx b/src/client/views/nodes/QueryBox.tsx
index 2a21a380d..0fff0b57f 100644
--- a/src/client/views/nodes/QueryBox.tsx
+++ b/src/client/views/nodes/QueryBox.tsx
@@ -1,15 +1,15 @@
 import React = require("react");
 import { IReactionDisposer } from "mobx";
 import { observer } from "mobx-react";
-import { documentSchema } from "../../../new_fields/documentSchemas";
-import { Id } from '../../../new_fields/FieldSymbols';
-import { makeInterface, listSpec } from "../../../new_fields/Schema";
-import { StrCast, Cast } from "../../../new_fields/Types";
+import { documentSchema } from "../../../fields/documentSchemas";
+import { Id } from '../../../fields/FieldSymbols';
+import { makeInterface, listSpec } from "../../../fields/Schema";
+import { StrCast, Cast } from "../../../fields/Types";
 import { ViewBoxAnnotatableComponent } from '../DocComponent';
 import { SearchBox } from "../search/SearchBox";
 import { FieldView, FieldViewProps } from './FieldView';
 import "./QueryBox.scss";
-import { List } from "../../../new_fields/List";
+import { List } from "../../../fields/List";
 import { SnappingManager } from "../../util/SnappingManager";
 
 type QueryDocument = makeInterface<[typeof documentSchema]>;
diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx
index a0ecc9ff5..5d4af2d77 100644
--- a/src/client/views/nodes/ScreenshotBox.tsx
+++ b/src/client/views/nodes/ScreenshotBox.tsx
@@ -5,10 +5,10 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { action, computed, IReactionDisposer, observable, runInAction } from "mobx";
 import { observer } from "mobx-react";
 import * as rp from 'request-promise';
-import { documentSchema } from "../../../new_fields/documentSchemas";
-import { makeInterface } from "../../../new_fields/Schema";
-import { Cast, NumCast } from "../../../new_fields/Types";
-import { VideoField } from "../../../new_fields/URLField";
+import { documentSchema } from "../../../fields/documentSchemas";
+import { makeInterface } from "../../../fields/Schema";
+import { Cast, NumCast } from "../../../fields/Types";
+import { VideoField } from "../../../fields/URLField";
 import { emptyFunction, returnFalse, returnOne, Utils, returnZero } from "../../../Utils";
 import { Docs, DocUtils } from "../../documents/Documents";
 import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView";
diff --git a/src/client/views/nodes/ScriptingBox.tsx b/src/client/views/nodes/ScriptingBox.tsx
index c607d6614..0944edf60 100644
--- a/src/client/views/nodes/ScriptingBox.tsx
+++ b/src/client/views/nodes/ScriptingBox.tsx
@@ -1,10 +1,10 @@
 import { action, observable, computed } from "mobx";
 import { observer } from "mobx-react";
 import * as React from "react";
-import { documentSchema } from "../../../new_fields/documentSchemas";
-import { createSchema, makeInterface, listSpec } from "../../../new_fields/Schema";
-import { ScriptField } from "../../../new_fields/ScriptField";
-import { StrCast, ScriptCast, Cast } from "../../../new_fields/Types";
+import { documentSchema } from "../../../fields/documentSchemas";
+import { createSchema, makeInterface, listSpec } from "../../../fields/Schema";
+import { ScriptField } from "../../../fields/ScriptField";
+import { StrCast, ScriptCast, Cast } from "../../../fields/Types";
 import { InteractionUtils } from "../../util/InteractionUtils";
 import { CompileScript, isCompileError, ScriptParam } from "../../util/Scripting";
 import { ViewBoxAnnotatableComponent } from "../DocComponent";
@@ -13,7 +13,7 @@ import { FieldView, FieldViewProps } from "../nodes/FieldView";
 import "./ScriptingBox.scss";
 import { OverlayView } from "../OverlayView";
 import { DocumentIconContainer } from "./DocumentIcon";
-import { List } from "../../../new_fields/List";
+import { List } from "../../../fields/List";
 
 const ScriptingSchema = createSchema({});
 type ScriptingDocument = makeInterface<[typeof ScriptingSchema, typeof documentSchema]>;
diff --git a/src/client/views/nodes/SliderBox.tsx b/src/client/views/nodes/SliderBox.tsx
index cb2526769..9a1aefba9 100644
--- a/src/client/views/nodes/SliderBox.tsx
+++ b/src/client/views/nodes/SliderBox.tsx
@@ -4,10 +4,10 @@ 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 '../../../new_fields/documentSchemas';
-import { createSchema, makeInterface } from '../../../new_fields/Schema';
-import { ScriptField } from '../../../new_fields/ScriptField';
-import { Cast, NumCast, StrCast } from '../../../new_fields/Types';
+import { documentSchema } from '../../../fields/documentSchemas';
+import { createSchema, makeInterface } from '../../../fields/Schema';
+import { ScriptField } from '../../../fields/ScriptField';
+import { Cast, NumCast, StrCast } from '../../../fields/Types';
 import { ContextMenu } from '../ContextMenu';
 import { ContextMenuProps } from '../ContextMenuItem';
 import { ViewBoxBaseComponent } from '../DocComponent';
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 9602eac52..ccf1f5588 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -5,12 +5,12 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { action, computed, IReactionDisposer, observable, reaction, runInAction, untracked } from "mobx";
 import { observer } from "mobx-react";
 import * as rp from 'request-promise';
-import { Doc } from "../../../new_fields/Doc";
-import { InkTool } from "../../../new_fields/InkField";
-import { createSchema, makeInterface } from "../../../new_fields/Schema";
-import { ScriptField } from "../../../new_fields/ScriptField";
-import { Cast, StrCast } from "../../../new_fields/Types";
-import { VideoField } from "../../../new_fields/URLField";
+import { Doc } from "../../../fields/Doc";
+import { InkTool } from "../../../fields/InkField";
+import { createSchema, makeInterface } from "../../../fields/Schema";
+import { ScriptField } from "../../../fields/ScriptField";
+import { Cast, StrCast } from "../../../fields/Types";
+import { VideoField } from "../../../fields/URLField";
 import { Utils, emptyFunction, returnOne, returnZero } from "../../../Utils";
 import { Docs, DocUtils } from "../../documents/Documents";
 import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView";
@@ -21,7 +21,7 @@ import { DocumentDecorations } from "../DocumentDecorations";
 import { InkingControl } from "../InkingControl";
 import { FieldView, FieldViewProps } from './FieldView';
 import "./VideoBox.scss";
-import { documentSchema } from "../../../new_fields/documentSchemas";
+import { documentSchema } from "../../../fields/documentSchemas";
 const path = require('path');
 
 export const timeSchema = createSchema({
@@ -337,7 +337,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
     }
 
     @action.bound
-    addDocumentWithTimestamp(doc: Doc|Doc[]): boolean {
+    addDocumentWithTimestamp(doc: Doc | Doc[]): boolean {
         const docs = doc instanceof Doc ? [doc] : doc;
         docs.forEach(doc => {
             const curTime = (this.layoutDoc.currentTimecode || -1);
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 6d4cc1a07..82f05012a 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -2,13 +2,13 @@ import { library } from "@fortawesome/fontawesome-svg-core";
 import { faStickyNote, faPen, faMousePointer } from '@fortawesome/free-solid-svg-icons';
 import { action, computed, observable, trace, IReactionDisposer, reaction } from "mobx";
 import { observer } from "mobx-react";
-import { Doc, FieldResult } from "../../../new_fields/Doc";
-import { documentSchema } from "../../../new_fields/documentSchemas";
-import { HtmlField } from "../../../new_fields/HtmlField";
-import { InkTool } from "../../../new_fields/InkField";
-import { makeInterface } from "../../../new_fields/Schema";
-import { Cast, NumCast, BoolCast, StrCast } from "../../../new_fields/Types";
-import { WebField } from "../../../new_fields/URLField";
+import { Doc, FieldResult } from "../../../fields/Doc";
+import { documentSchema } from "../../../fields/documentSchemas";
+import { HtmlField } from "../../../fields/HtmlField";
+import { InkTool } from "../../../fields/InkField";
+import { makeInterface } from "../../../fields/Schema";
+import { Cast, NumCast, BoolCast, StrCast } from "../../../fields/Types";
+import { WebField } from "../../../fields/URLField";
 import { Utils, returnOne, emptyFunction, returnZero } from "../../../Utils";
 import { Docs } from "../../documents/Documents";
 import { DragManager } from "../../util/DragManager";
diff --git a/src/client/views/nodes/formattedText/DashDocCommentView.tsx b/src/client/views/nodes/formattedText/DashDocCommentView.tsx
index d94fe7fc6..d56b87ae5 100644
--- a/src/client/views/nodes/formattedText/DashDocCommentView.tsx
+++ b/src/client/views/nodes/formattedText/DashDocCommentView.tsx
@@ -8,14 +8,14 @@ import { EditorState, NodeSelection, Plugin, TextSelection } from "prosemirror-s
 import { StepMap } from "prosemirror-transform";
 import { EditorView } from "prosemirror-view";
 import * as ReactDOM from 'react-dom';
-import { Doc, DocListCast, Field, HeightSym, WidthSym } from "../../../../new_fields/Doc";
-import { Id } from "../../../../new_fields/FieldSymbols";
-import { List } from "../../../../new_fields/List";
-import { ObjectField } from "../../../../new_fields/ObjectField";
-import { listSpec } from "../../../../new_fields/Schema";
-import { SchemaHeaderField } from "../../../../new_fields/SchemaHeaderField";
-import { ComputedField } from "../../../../new_fields/ScriptField";
-import { BoolCast, Cast, NumCast, StrCast } from "../../../../new_fields/Types";
+import { Doc, DocListCast, Field, HeightSym, WidthSym } from "../../../../fields/Doc";
+import { Id } from "../../../../fields/FieldSymbols";
+import { List } from "../../../../fields/List";
+import { ObjectField } from "../../../../fields/ObjectField";
+import { listSpec } from "../../../../fields/Schema";
+import { SchemaHeaderField } from "../../../../fields/SchemaHeaderField";
+import { ComputedField } from "../../../../fields/ScriptField";
+import { BoolCast, Cast, NumCast, StrCast } from "../../../../fields/Types";
 import { emptyFunction, returnEmptyString, returnFalse, returnOne, Utils, returnZero } from "../../../../Utils";
 import { DocServer } from "../../../DocServer";
 
diff --git a/src/client/views/nodes/formattedText/DashDocView.tsx b/src/client/views/nodes/formattedText/DashDocView.tsx
index 7130fee2b..05e6a5959 100644
--- a/src/client/views/nodes/formattedText/DashDocView.tsx
+++ b/src/client/views/nodes/formattedText/DashDocView.tsx
@@ -1,10 +1,10 @@
 import { IReactionDisposer, reaction } from "mobx";
 import { NodeSelection } from "prosemirror-state";
-import { Doc, HeightSym, WidthSym } from "../../../../new_fields/Doc";
-import { Id } from "../../../../new_fields/FieldSymbols";
-import { ObjectField } from "../../../../new_fields/ObjectField";
-import { ComputedField } from "../../../../new_fields/ScriptField";
-import { BoolCast, Cast, NumCast, StrCast } from "../../../../new_fields/Types";
+import { Doc, HeightSym, WidthSym } from "../../../../fields/Doc";
+import { Id } from "../../../../fields/FieldSymbols";
+import { ObjectField } from "../../../../fields/ObjectField";
+import { ComputedField } from "../../../../fields/ScriptField";
+import { BoolCast, Cast, NumCast, StrCast } from "../../../../fields/Types";
 import { emptyFunction, returnEmptyString, returnFalse, Utils, returnZero } from "../../../../Utils";
 import { DocServer } from "../../../DocServer";
 import { Docs } from "../../../documents/Documents";
diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx
index 3c6841f08..d05e8f1ea 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.tsx
+++ b/src/client/views/nodes/formattedText/DashFieldView.tsx
@@ -1,10 +1,10 @@
 import { IReactionDisposer, observable, runInAction, computed, action } from "mobx";
-import { Doc, DocListCast, Field } from "../../../../new_fields/Doc";
-import { List } from "../../../../new_fields/List";
-import { listSpec } from "../../../../new_fields/Schema";
-import { SchemaHeaderField } from "../../../../new_fields/SchemaHeaderField";
-import { ComputedField } from "../../../../new_fields/ScriptField";
-import { Cast, StrCast } from "../../../../new_fields/Types";
+import { Doc, DocListCast, Field } from "../../../../fields/Doc";
+import { List } from "../../../../fields/List";
+import { listSpec } from "../../../../fields/Schema";
+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 { FormattedTextBox } from "./FormattedTextBox";
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 206c3db8c..5e33e7e70 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -12,17 +12,17 @@ import { Fragment, Mark, Node, Slice } from "prosemirror-model";
 import { EditorState, NodeSelection, Plugin, TextSelection, Transaction } from "prosemirror-state";
 import { ReplaceStep } from 'prosemirror-transform';
 import { EditorView } from "prosemirror-view";
-import { DateField } from '../../../../new_fields/DateField';
-import { DataSym, Doc, DocListCast, DocListCastAsync, Field, HeightSym, Opt, WidthSym } from "../../../../new_fields/Doc";
-import { documentSchema } from '../../../../new_fields/documentSchemas';
-import { Id } from '../../../../new_fields/FieldSymbols';
-import { InkTool } from '../../../../new_fields/InkField';
-import { PrefetchProxy } from '../../../../new_fields/Proxy';
-import { RichTextField } from "../../../../new_fields/RichTextField";
-import { RichTextUtils } from '../../../../new_fields/RichTextUtils';
-import { createSchema, makeInterface } from "../../../../new_fields/Schema";
-import { Cast, DateCast, NumCast, StrCast } from "../../../../new_fields/Types";
-import { TraceMobx } from '../../../../new_fields/util';
+import { DateField } from '../../../../fields/DateField';
+import { DataSym, Doc, DocListCast, DocListCastAsync, Field, HeightSym, Opt, WidthSym } 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 { createSchema, makeInterface } from "../../../../fields/Schema";
+import { Cast, DateCast, NumCast, StrCast } from "../../../../fields/Types";
+import { TraceMobx } from '../../../../fields/util';
 import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, numberRange, returnOne, returnZero, Utils, setupMoveUpEvents } from '../../../../Utils';
 import { GoogleApiClientUtils, Pulls, Pushes } from '../../../apis/google_docs/GoogleApiClientUtils';
 import { DocServer } from "../../../DocServer";
@@ -58,7 +58,7 @@ import { FieldView, FieldViewProps } from "../FieldView";
 import "./FormattedTextBox.scss";
 import { FormattedTextBoxComment, formattedTextBoxCommentPlugin } from './FormattedTextBoxComment';
 import React = require("react");
-import { ScriptField } from '../../../../new_fields/ScriptField';
+import { ScriptField } from '../../../../fields/ScriptField';
 import GoogleAuthenticationManager from '../../../apis/GoogleAuthenticationManager';
 
 library.add(faEdit);
diff --git a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
index 07aecf148..d47ae63af 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
@@ -2,8 +2,8 @@ import { Mark, ResolvedPos } from "prosemirror-model";
 import { EditorState, Plugin } from "prosemirror-state";
 import { EditorView } from "prosemirror-view";
 import * as ReactDOM from 'react-dom';
-import { Doc, DocCastAsync } from "../../../../new_fields/Doc";
-import { Cast, FieldValue, NumCast } from "../../../../new_fields/Types";
+import { Doc, DocCastAsync } from "../../../../fields/Doc";
+import { Cast, FieldValue, NumCast } from "../../../../fields/Types";
 import { emptyFunction, returnEmptyString, returnFalse, Utils, emptyPath, returnZero, returnOne } from "../../../../Utils";
 import { DocServer } from "../../../DocServer";
 import { DocumentManager } from "../../../util/DocumentManager";
diff --git a/src/client/views/nodes/formattedText/ImageResizeView.tsx b/src/client/views/nodes/formattedText/ImageResizeView.tsx
index 8f98da0fd..401ecd7e6 100644
--- a/src/client/views/nodes/formattedText/ImageResizeView.tsx
+++ b/src/client/views/nodes/formattedText/ImageResizeView.tsx
@@ -1,5 +1,5 @@
 import { NodeSelection } from "prosemirror-state";
-import { Doc } from "../../../../new_fields/Doc";
+import { Doc } from "../../../../fields/Doc";
 import { DocServer } from "../../../DocServer";
 import { DocumentManager } from "../../../util/DocumentManager";
 import React = require("react");
diff --git a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
index a0b02880e..2f7d23021 100644
--- a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
+++ b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
@@ -7,10 +7,10 @@ import { splitListItem, wrapInList, } from "prosemirror-schema-list";
 import { EditorState, Transaction, TextSelection } from "prosemirror-state";
 import { SelectionManager } from "../../../util/SelectionManager";
 import { Docs } from "../../../documents/Documents";
-import { NumCast, BoolCast, Cast, StrCast } from "../../../../new_fields/Types";
-import { Doc } from "../../../../new_fields/Doc";
+import { NumCast, BoolCast, Cast, StrCast } from "../../../../fields/Types";
+import { Doc } from "../../../../fields/Doc";
 import { FormattedTextBox } from "./FormattedTextBox";
-import { Id } from "../../../../new_fields/FieldSymbols";
+import { Id } from "../../../../fields/FieldSymbols";
 
 const mac = typeof navigator !== "undefined" ? /Mac/.test(navigator.platform) : false;
 
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index 170a39801..fd1b26208 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -11,14 +11,14 @@ import { IconProp, library } from '@fortawesome/fontawesome-svg-core';
 import { faBold, faItalic, faChevronLeft, faUnderline, faStrikethrough, faSubscript, faSuperscript, faIndent, faEyeDropper, faCaretDown, faPalette, faHighlighter, faLink, faPaintRoller, faSleigh } from "@fortawesome/free-solid-svg-icons";
 import { updateBullets } from "./ProsemirrorExampleTransfer";
 import { FieldViewProps } from "../FieldView";
-import { Cast, StrCast } from "../../../../new_fields/Types";
+import { Cast, StrCast } from "../../../../fields/Types";
 import { FormattedTextBoxProps } from "./FormattedTextBox";
 import { unimplementedFunction, Utils } from "../../../../Utils";
 import { wrapInList } from "prosemirror-schema-list";
-import { PastelSchemaPalette, DarkPastelSchemaPalette } from '../../../../new_fields/SchemaHeaderField';
+import { PastelSchemaPalette, DarkPastelSchemaPalette } from '../../../../fields/SchemaHeaderField';
 import "./RichTextMenu.scss";
 import { DocServer } from "../../../DocServer";
-import { Doc } from "../../../../new_fields/Doc";
+import { Doc } from "../../../../fields/Doc";
 import { SelectionManager } from "../../../util/SelectionManager";
 import { LinkManager } from "../../../util/LinkManager";
 const { toggleMark, setBlockType } = require("prosemirror-commands");
diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts
index 0ba591fec..fbd6c87bb 100644
--- a/src/client/views/nodes/formattedText/RichTextRules.ts
+++ b/src/client/views/nodes/formattedText/RichTextRules.ts
@@ -1,9 +1,9 @@
 import { ellipsis, emDash, InputRule, smartQuotes, textblockTypeInputRule } from "prosemirror-inputrules";
 import { NodeSelection, TextSelection } from "prosemirror-state";
-import { DataSym, Doc } from "../../../../new_fields/Doc";
-import { Id } from "../../../../new_fields/FieldSymbols";
-import { ComputedField } from "../../../../new_fields/ScriptField";
-import { Cast, NumCast } from "../../../../new_fields/Types";
+import { DataSym, Doc } from "../../../../fields/Doc";
+import { Id } from "../../../../fields/FieldSymbols";
+import { ComputedField } from "../../../../fields/ScriptField";
+import { Cast, NumCast } from "../../../../fields/Types";
 import { returnFalse, Utils } from "../../../../Utils";
 import { DocServer } from "../../../DocServer";
 import { Docs, DocUtils } from "../../../documents/Documents";
@@ -11,7 +11,7 @@ import { FormattedTextBox } from "./FormattedTextBox";
 import { wrappingInputRule } from "./prosemirrorPatches";
 import RichTextMenu from "./RichTextMenu";
 import { schema } from "./schema_rts";
-import { List } from "../../../../new_fields/List";
+import { List } from "../../../../fields/List";
 
 export class RichTextRules {
     public Document: Doc;
diff --git a/src/client/views/nodes/formattedText/RichTextSchema.tsx b/src/client/views/nodes/formattedText/RichTextSchema.tsx
index 7edd191cf..91280dea4 100644
--- a/src/client/views/nodes/formattedText/RichTextSchema.tsx
+++ b/src/client/views/nodes/formattedText/RichTextSchema.tsx
@@ -8,14 +8,14 @@ import { EditorState, NodeSelection, Plugin, TextSelection } from "prosemirror-s
 import { StepMap } from "prosemirror-transform";
 import { EditorView } from "prosemirror-view";
 import * as ReactDOM from 'react-dom';
-import { Doc, DocListCast, Field, HeightSym, WidthSym } from "../../../../new_fields/Doc";
-import { Id } from "../../../../new_fields/FieldSymbols";
-import { List } from "../../../../new_fields/List";
-import { ObjectField } from "../../../../new_fields/ObjectField";
-import { listSpec } from "../../../../new_fields/Schema";
-import { SchemaHeaderField } from "../../../../new_fields/SchemaHeaderField";
-import { ComputedField } from "../../../../new_fields/ScriptField";
-import { BoolCast, Cast, NumCast, StrCast, FieldValue } from "../../../../new_fields/Types";
+import { Doc, DocListCast, Field, HeightSym, WidthSym } from "../../../../fields/Doc";
+import { Id } from "../../../../fields/FieldSymbols";
+import { List } from "../../../../fields/List";
+import { ObjectField } from "../../../../fields/ObjectField";
+import { listSpec } from "../../../../fields/Schema";
+import { SchemaHeaderField } from "../../../../fields/SchemaHeaderField";
+import { ComputedField } from "../../../../fields/ScriptField";
+import { BoolCast, Cast, NumCast, StrCast, FieldValue } from "../../../../fields/Types";
 import { emptyFunction, returnEmptyString, returnFalse, returnOne, Utils, returnZero } from "../../../../Utils";
 import { DocServer } from "../../../DocServer";
 import { Docs } from "../../../documents/Documents";
diff --git a/src/client/views/nodes/formattedText/marks_rts.ts b/src/client/views/nodes/formattedText/marks_rts.ts
index 46bf481fb..ebaa23e99 100644
--- a/src/client/views/nodes/formattedText/marks_rts.ts
+++ b/src/client/views/nodes/formattedText/marks_rts.ts
@@ -1,6 +1,6 @@
 import React = require("react");
 import { DOMOutputSpecArray, Fragment, MarkSpec, Node, NodeSpec, Schema, Slice } from "prosemirror-model";
-import { Doc } from "../../../../new_fields/Doc";
+import { Doc } from "../../../../fields/Doc";
 
 
 const emDOM: DOMOutputSpecArray = ["em", 0];
diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx
index 672d3adb8..cb6a15f36 100644
--- a/src/client/views/pdf/Annotation.tsx
+++ b/src/client/views/pdf/Annotation.tsx
@@ -1,10 +1,10 @@
 import React = require("react");
 import { action, IReactionDisposer, observable, reaction, runInAction } from "mobx";
 import { observer } from "mobx-react";
-import { Doc, DocListCast, HeightSym, WidthSym } from "../../../new_fields/Doc";
-import { Id } from "../../../new_fields/FieldSymbols";
-import { List } from "../../../new_fields/List";
-import { Cast, FieldValue, NumCast, StrCast } from "../../../new_fields/Types";
+import { Doc, DocListCast, HeightSym, WidthSym } from "../../../fields/Doc";
+import { Id } from "../../../fields/FieldSymbols";
+import { List } from "../../../fields/List";
+import { Cast, FieldValue, NumCast, StrCast } from "../../../fields/Types";
 import { DocumentManager } from "../../util/DocumentManager";
 import PDFMenu from "./PDFMenu";
 import "./Annotation.scss";
diff --git a/src/client/views/pdf/PDFMenu.tsx b/src/client/views/pdf/PDFMenu.tsx
index 2a6eff7ff..ff328068b 100644
--- a/src/client/views/pdf/PDFMenu.tsx
+++ b/src/client/views/pdf/PDFMenu.tsx
@@ -5,7 +5,7 @@ import { observer } from "mobx-react";
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { unimplementedFunction, returnFalse } from "../../../Utils";
 import AntimodeMenu from "../AntimodeMenu";
-import { Doc, Opt } from "../../../new_fields/Doc";
+import { Doc, Opt } from "../../../fields/Doc";
 
 @observer
 export default class PDFMenu extends AntimodeMenu {
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index acaa4363e..c50969493 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -4,16 +4,16 @@ import * as Pdfjs from "pdfjs-dist";
 import "pdfjs-dist/web/pdf_viewer.css";
 import * as rp from "request-promise";
 import { Dictionary } from "typescript-collections";
-import { Doc, DocListCast, FieldResult, HeightSym, Opt, WidthSym } from "../../../new_fields/Doc";
-import { documentSchema } from "../../../new_fields/documentSchemas";
-import { Id } from "../../../new_fields/FieldSymbols";
-import { InkTool } from "../../../new_fields/InkField";
-import { List } from "../../../new_fields/List";
-import { createSchema, makeInterface } from "../../../new_fields/Schema";
-import { ScriptField } from "../../../new_fields/ScriptField";
-import { Cast, NumCast } from "../../../new_fields/Types";
-import { PdfField } from "../../../new_fields/URLField";
-import { TraceMobx } from "../../../new_fields/util";
+import { Doc, DocListCast, FieldResult, HeightSym, Opt, 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 { createSchema, makeInterface } from "../../../fields/Schema";
+import { ScriptField } from "../../../fields/ScriptField";
+import { Cast, NumCast } from "../../../fields/Types";
+import { PdfField } from "../../../fields/URLField";
+import { TraceMobx } from "../../../fields/util";
 import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, emptyPath, intersectRect, returnZero, smoothScroll, Utils } from "../../../Utils";
 import { Docs, DocUtils } from "../../documents/Documents";
 import { DocumentType } from "../../documents/DocumentTypes";
diff --git a/src/client/views/presentationview/PresElementBox.tsx b/src/client/views/presentationview/PresElementBox.tsx
index e13a5f2f7..280ba9093 100644
--- a/src/client/views/presentationview/PresElementBox.tsx
+++ b/src/client/views/presentationview/PresElementBox.tsx
@@ -1,11 +1,11 @@
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { action, computed, IReactionDisposer, reaction } from "mobx";
 import { observer } from "mobx-react";
-import { Doc, DataSym, DocListCast } from "../../../new_fields/Doc";
-import { documentSchema } from '../../../new_fields/documentSchemas';
-import { Id } from "../../../new_fields/FieldSymbols";
-import { createSchema, makeInterface } from '../../../new_fields/Schema';
-import { Cast, NumCast, BoolCast, ScriptCast } from "../../../new_fields/Types";
+import { Doc, DataSym, DocListCast } from "../../../fields/Doc";
+import { documentSchema } from '../../../fields/documentSchemas';
+import { Id } from "../../../fields/FieldSymbols";
+import { createSchema, makeInterface } from '../../../fields/Schema';
+import { Cast, NumCast, BoolCast, ScriptCast } from "../../../fields/Types";
 import { emptyFunction, emptyPath, returnFalse, returnTrue, returnOne, returnZero } from "../../../Utils";
 import { Transform } from "../../util/Transform";
 import { CollectionViewType } from '../collections/CollectionView';
diff --git a/src/client/views/search/FilterBox.tsx b/src/client/views/search/FilterBox.tsx
index 662b37d77..4b53963a5 100644
--- a/src/client/views/search/FilterBox.tsx
+++ b/src/client/views/search/FilterBox.tsx
@@ -4,10 +4,10 @@ import { observable, action } from 'mobx';
 import "./SearchBox.scss";
 import { faTimes, faCheckCircle, faObjectGroup } from '@fortawesome/free-solid-svg-icons';
 import { library } from '@fortawesome/fontawesome-svg-core';
-import { Doc } from '../../../new_fields/Doc';
-import { Id } from '../../../new_fields/FieldSymbols';
+import { Doc } from '../../../fields/Doc';
+import { Id } from '../../../fields/FieldSymbols';
 import { DocumentType } from "../../documents/DocumentTypes";
-import { Cast, StrCast } from '../../../new_fields/Types';
+import { Cast, StrCast } from '../../../fields/Types';
 import * as _ from "lodash";
 import { IconBar } from './IconBar';
 import { FieldFilters } from './FieldFilters';
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index e41b725b1..eea7b528b 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -5,9 +5,9 @@ import { action, computed, observable, runInAction, IReactionDisposer, reaction
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import * as rp from 'request-promise';
-import { Doc } from '../../../new_fields/Doc';
-import { Id } from '../../../new_fields/FieldSymbols';
-import { Cast, NumCast, StrCast } from '../../../new_fields/Types';
+import { Doc } from '../../../fields/Doc';
+import { Id } from '../../../fields/FieldSymbols';
+import { Cast, NumCast, StrCast } from '../../../fields/Types';
 import { Utils } from '../../../Utils';
 import { Docs } from '../../documents/Documents';
 import { SetupDrag } from '../../util/DragManager';
@@ -19,7 +19,7 @@ import { FieldView } from '../nodes/FieldView';
 import { DocumentType } from "../../documents/DocumentTypes";
 import { DocumentView } from '../nodes/DocumentView';
 import { SelectionManager } from '../../util/SelectionManager';
-import { listSpec } from '../../../new_fields/Schema';
+import { listSpec } from '../../../fields/Schema';
 
 library.add(faTimes);
 
diff --git a/src/client/views/search/SearchItem.tsx b/src/client/views/search/SearchItem.tsx
index 96f43e931..24d6e9d6f 100644
--- a/src/client/views/search/SearchItem.tsx
+++ b/src/client/views/search/SearchItem.tsx
@@ -4,9 +4,9 @@ import { faCaretUp, faChartBar, faFile, faFilePdf, faFilm, faFingerprint, faGlob
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { action, computed, observable, runInAction } from "mobx";
 import { observer } from "mobx-react";
-import { Doc } from "../../../new_fields/Doc";
-import { Id } from "../../../new_fields/FieldSymbols";
-import { Cast, NumCast, StrCast } from "../../../new_fields/Types";
+import { Doc } from "../../../fields/Doc";
+import { Id } from "../../../fields/FieldSymbols";
+import { Cast, NumCast, StrCast } from "../../../fields/Types";
 import { emptyFunction, emptyPath, returnFalse, Utils, returnTrue, returnOne, returnZero } from "../../../Utils";
 import { DocumentType } from "../../documents/DocumentTypes";
 import { DocumentManager } from "../../util/DocumentManager";
diff --git a/src/debug/Repl.tsx b/src/debug/Repl.tsx
index fd6b47ff0..d541c8009 100644
--- a/src/debug/Repl.tsx
+++ b/src/debug/Repl.tsx
@@ -3,9 +3,9 @@ import * as ReactDOM from 'react-dom';
 import { observer } from 'mobx-react';
 import { observable, computed } from 'mobx';
 import { CompileScript } from '../client/util/Scripting';
-import { makeInterface } from '../new_fields/Schema';
-import { ObjectField } from '../new_fields/ObjectField';
-import { RefField } from '../new_fields/RefField';
+import { makeInterface } from '../fields/Schema';
+import { ObjectField } from '../fields/ObjectField';
+import { RefField } from '../fields/RefField';
 import { DocServer } from '../client/DocServer';
 
 @observer
diff --git a/src/debug/Test.tsx b/src/debug/Test.tsx
index 3baedce4b..17d3db8fd 100644
--- a/src/debug/Test.tsx
+++ b/src/debug/Test.tsx
@@ -1,7 +1,7 @@
 import * as React from 'react';
 import * as ReactDOM from 'react-dom';
 import { DocServer } from '../client/DocServer';
-import { Doc } from '../new_fields/Doc';
+import { Doc } from '../fields/Doc';
 import * as Pdfjs from "pdfjs-dist";
 import "pdfjs-dist/web/pdf_viewer.css";
 import { Utils } from '../Utils';
diff --git a/src/debug/Viewer.tsx b/src/debug/Viewer.tsx
index a26d2e06a..6ce39d533 100644
--- a/src/debug/Viewer.tsx
+++ b/src/debug/Viewer.tsx
@@ -3,17 +3,17 @@ import "normalize.css";
 import * as React from 'react';
 import * as ReactDOM from 'react-dom';
 import { observer } from 'mobx-react';
-import { Doc, Field, FieldResult, Opt } from '../new_fields/Doc';
+import { Doc, Field, FieldResult, Opt } from '../fields/Doc';
 import { DocServer } from '../client/DocServer';
-import { Id } from '../new_fields/FieldSymbols';
-import { List } from '../new_fields/List';
-import { URLField } from '../new_fields/URLField';
+import { Id } from '../fields/FieldSymbols';
+import { List } from '../fields/List';
+import { URLField } from '../fields/URLField';
 import { EditableView } from '../client/views/EditableView';
 import { CompileScript } from '../client/util/Scripting';
-import { RichTextField } from '../new_fields/RichTextField';
-import { DateField } from '../new_fields/DateField';
-import { ScriptField } from '../new_fields/ScriptField';
-import CursorField from '../new_fields/CursorField';
+import { RichTextField } from '../fields/RichTextField';
+import { DateField } from '../fields/DateField';
+import { ScriptField } from '../fields/ScriptField';
+import CursorField from '../fields/CursorField';
 
 DateField;
 URLField;
diff --git a/src/fields/CursorField.ts b/src/fields/CursorField.ts
new file mode 100644
index 000000000..28467377b
--- /dev/null
+++ b/src/fields/CursorField.ts
@@ -0,0 +1,66 @@
+import { ObjectField } from "./ObjectField";
+import { observable } from "mobx";
+import { Deserializable } from "../client/util/SerializationHelper";
+import { serializable, createSimpleSchema, object, date } from "serializr";
+import { OnUpdate, ToScriptString, ToString, Copy } from "./FieldSymbols";
+
+export type CursorPosition = {
+    x: number,
+    y: number
+};
+
+export type CursorMetadata = {
+    id: string,
+    identifier: string,
+    timestamp: number
+};
+
+export type CursorData = {
+    metadata: CursorMetadata,
+    position: CursorPosition
+};
+
+const PositionSchema = createSimpleSchema({
+    x: true,
+    y: true
+});
+
+const MetadataSchema = createSimpleSchema({
+    id: true,
+    identifier: true,
+    timestamp: true
+});
+
+const CursorSchema = createSimpleSchema({
+    metadata: object(MetadataSchema),
+    position: object(PositionSchema)
+});
+
+@Deserializable("cursor")
+export default class CursorField extends ObjectField {
+
+    @serializable(object(CursorSchema))
+    readonly data: CursorData;
+
+    constructor(data: CursorData) {
+        super();
+        this.data = data;
+    }
+
+    setPosition(position: CursorPosition) {
+        this.data.position = position;
+        this.data.metadata.timestamp = Date.now();
+        this[OnUpdate]();
+    }
+
+    [Copy]() {
+        return new CursorField(this.data);
+    }
+
+    [ToScriptString]() {
+        return "invalid";
+    }
+    [ToString]() {
+        return "invalid";
+    }
+}
\ No newline at end of file
diff --git a/src/fields/DateField.ts b/src/fields/DateField.ts
new file mode 100644
index 000000000..a925148c2
--- /dev/null
+++ b/src/fields/DateField.ts
@@ -0,0 +1,36 @@
+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";
+
+@scriptingGlobal
+@Deserializable("date")
+export class DateField extends ObjectField {
+    @serializable(date())
+    readonly date: Date;
+
+    constructor(date: Date = new Date()) {
+        super();
+        this.date = date;
+    }
+
+    [Copy]() {
+        return new DateField(this.date);
+    }
+
+    toString() {
+        return `${this.date.toISOString()}`;
+    }
+
+    [ToScriptString]() {
+        return `new DateField(new Date(${this.date.toISOString()}))`;
+    }
+    [ToString]() {
+        return this.date.toISOString();
+    }
+}
+
+Scripting.addGlobal(function d(...dateArgs: any[]) {
+    return new DateField(new (Date as any)(...dateArgs));
+});
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
new file mode 100644
index 000000000..a1e1e11b1
--- /dev/null
+++ b/src/fields/Doc.ts
@@ -0,0 +1,1109 @@
+import { action, computed, observable, ObservableMap, runInAction } from "mobx";
+import { computedFn } from "mobx-utils";
+import { alias, map, serializable } from "serializr";
+import { DocServer } from "../client/DocServer";
+import { DocumentType } from "../client/documents/DocumentTypes";
+import { Scripting, scriptingGlobal } from "../client/util/Scripting";
+import { afterDocDeserialize, autoObject, Deserializable, SerializationHelper } from "../client/util/SerializationHelper";
+import { UndoManager } from "../client/util/UndoManager";
+import { intersectRect, Utils } from "../Utils";
+import { HandleUpdate, Id, OnUpdate, Parent, Self, SelfProxy, ToScriptString, ToString, Update, Copy } from "./FieldSymbols";
+import { List } from "./List";
+import { ObjectField } from "./ObjectField";
+import { PrefetchProxy, ProxyField } from "./Proxy";
+import { FieldId, RefField } from "./RefField";
+import { RichTextField } from "./RichTextField";
+import { listSpec } from "./Schema";
+import { ComputedField, ScriptField } from "./ScriptField";
+import { Cast, FieldValue, NumCast, StrCast, ToConstructor, ScriptCast } from "./Types";
+import { deleteProperty, getField, getter, makeEditable, makeReadOnly, setter, updateFunction } from "./util";
+import { Docs, DocumentOptions } from "../client/documents/Documents";
+import { PdfField, VideoField, AudioField, ImageField } from "./URLField";
+import { LinkManager } from "../client/util/LinkManager";
+
+export namespace Field {
+    export function toKeyValueString(doc: Doc, key: string): string {
+        const onDelegate = Object.keys(doc).includes(key);
+
+        const field = ComputedField.WithoutComputed(() => FieldValue(doc[key]));
+        if (Field.IsField(field)) {
+            return (onDelegate ? "=" : "") + (field instanceof ComputedField ? `:=${field.script.originalScript}` : Field.toScriptString(field));
+        }
+        return "";
+    }
+    export function toScriptString(field: Field): string {
+        if (typeof field === "string") {
+            return `"${field}"`;
+        } else if (typeof field === "number" || typeof field === "boolean") {
+            return String(field);
+        } else {
+            return field[ToScriptString]();
+        }
+    }
+    export function toString(field: Field): string {
+        if (typeof field === "string") {
+            return field;
+        } else if (typeof field === "number" || typeof field === "boolean") {
+            return String(field);
+        } else if (field instanceof ObjectField) {
+            return field[ToString]();
+        } else if (field instanceof RefField) {
+            return field[ToString]();
+        }
+        return "";
+    }
+    export function IsField(field: any): field is Field;
+    export function IsField(field: any, includeUndefined: true): field is Field | undefined;
+    export function IsField(field: any, includeUndefined: boolean = false): field is Field | undefined {
+        return (typeof field === "string")
+            || (typeof field === "number")
+            || (typeof field === "boolean")
+            || (field instanceof ObjectField)
+            || (field instanceof RefField)
+            || (includeUndefined && field === undefined);
+    }
+}
+export type Field = number | string | boolean | ObjectField | RefField;
+export type Opt<T> = T | undefined;
+export type FieldWaiting<T extends RefField = RefField> = T extends undefined ? never : Promise<T | undefined>;
+export type FieldResult<T extends Field = Field> = Opt<T> | FieldWaiting<Extract<T, RefField>>;
+
+/**
+ * Cast any field to either a List of Docs or undefined if the given field isn't a List of Docs.  
+ * If a default value is given, that will be returned instead of undefined.  
+ * If a default value is given, the returned value should not be modified as it might be a temporary value.  
+ * If no default value is given, and the returned value is not undefined, it can be safely modified.  
+ */
+export function DocListCastAsync(field: FieldResult): Promise<Doc[] | undefined>;
+export function DocListCastAsync(field: FieldResult, defaultValue: Doc[]): Promise<Doc[]>;
+export function DocListCastAsync(field: FieldResult, defaultValue?: Doc[]) {
+    const list = Cast(field, listSpec(Doc));
+    return list ? Promise.all(list).then(() => list) : Promise.resolve(defaultValue);
+}
+
+export async function DocCastAsync(field: FieldResult): Promise<Opt<Doc>> {
+    return Cast(field, Doc);
+}
+
+export function DocListCast(field: FieldResult): Doc[] {
+    return Cast(field, listSpec(Doc), []).filter(d => d instanceof Doc) as Doc[];
+}
+
+export const WidthSym = Symbol("Width");
+export const HeightSym = Symbol("Height");
+export const DataSym = Symbol("Data");
+export const LayoutSym = Symbol("Layout");
+export const UpdatingFromServer = Symbol("UpdatingFromServer");
+const CachedUpdates = Symbol("Cached updates");
+
+
+function fetchProto(doc: Doc) {
+    const proto = doc.proto;
+    if (proto instanceof Promise) {
+        return proto;
+    }
+}
+
+@scriptingGlobal
+@Deserializable("Doc", fetchProto).withFields(["id"])
+export class Doc extends RefField {
+    constructor(id?: FieldId, forceSave?: boolean) {
+        super(id);
+        const doc = new Proxy<this>(this, {
+            set: setter,
+            get: getter,
+            // getPrototypeOf: (target) => Cast(target[SelfProxy].proto, Doc) || null, // TODO this might be able to replace the proto logic in getter
+            has: (target, key) => key in target.__fields,
+            ownKeys: target => {
+                const obj = {} as any;
+                Object.assign(obj, target.___fields);
+                runInAction(() => obj.__LAYOUT__ = target.__LAYOUT__);
+                return Object.keys(obj);
+            },
+            getOwnPropertyDescriptor: (target, prop) => {
+                if (prop.toString() === "__LAYOUT__") {
+                    return Reflect.getOwnPropertyDescriptor(target, prop);
+                }
+                if (prop in target.__fields) {
+                    return {
+                        configurable: true,//TODO Should configurable be true?
+                        enumerable: true,
+                        value: target.__fields[prop]
+                    };
+                }
+                return Reflect.getOwnPropertyDescriptor(target, prop);
+            },
+            deleteProperty: deleteProperty,
+            defineProperty: () => { throw new Error("Currently properties can't be defined on documents using Object.defineProperty"); },
+        });
+        this[SelfProxy] = doc;
+        if (!id || forceSave) {
+            DocServer.CreateField(doc);
+        }
+        return doc;
+    }
+
+    proto: Opt<Doc>;
+    [key: string]: FieldResult;
+
+    @serializable(alias("fields", map(autoObject(), { afterDeserialize: afterDocDeserialize })))
+    private get __fields() { return this.___fields; }
+    private set __fields(value) {
+        this.___fields = value;
+        for (const key in value) {
+            const field = value[key];
+            if (!(field instanceof ObjectField)) continue;
+            field[Parent] = this[Self];
+            field[OnUpdate] = updateFunction(this[Self], key, field, this[SelfProxy]);
+        }
+    }
+
+    @observable
+    //{ [key: string]: Field | FieldWaiting | undefined }
+    private ___fields: any = {};
+
+    private [UpdatingFromServer]: boolean = false;
+
+    private [Update] = (diff: any) => {
+        !this[UpdatingFromServer] && DocServer.UpdateField(this[Id], diff);
+    }
+
+    private [Self] = this;
+    private [SelfProxy]: any;
+    public [WidthSym] = () => NumCast(this[SelfProxy]._width);
+    public [HeightSym] = () => NumCast(this[SelfProxy]._height);
+    public get [LayoutSym]() { return this[SelfProxy].__LAYOUT__; }
+    public get [DataSym]() {
+        const self = this[SelfProxy];
+        return self.resolvedDataDoc && !self.isTemplateForField ? self :
+            Doc.GetProto(Cast(Doc.Layout(self).resolvedDataDoc, Doc, null) || self);
+    }
+    @computed get __LAYOUT__() {
+        const templateLayoutDoc = Cast(Doc.LayoutField(this[SelfProxy]), Doc, null);
+        if (templateLayoutDoc) {
+            let renderFieldKey: any;
+            const layoutField = templateLayoutDoc[StrCast(templateLayoutDoc.layoutKey, "layout")];
+            if (typeof layoutField === "string") {
+                renderFieldKey = layoutField.split("fieldKey={'")[1].split("'")[0];//layoutField.split("'")[1];
+            } else {
+                return Cast(layoutField, Doc, null);
+            }
+            return Cast(this[SelfProxy][renderFieldKey + "-layout[" + templateLayoutDoc[Id] + "]"], Doc, null) || templateLayoutDoc;
+        }
+        return undefined;
+    }
+
+    [ToScriptString]() { return `DOC-"${this[Self][Id]}"-`; }
+    [ToString]() { return `Doc(${this.title})`; }
+
+    private [CachedUpdates]: { [key: string]: () => void | Promise<any> } = {};
+    public static CurrentUserEmail: string = "";
+    public async [HandleUpdate](diff: any) {
+        const set = diff.$set;
+        const sameAuthor = this.author === Doc.CurrentUserEmail;
+        if (set) {
+            for (const key in set) {
+                if (!key.startsWith("fields.")) {
+                    continue;
+                }
+                const fKey = key.substring(7);
+                const fn = async () => {
+                    const value = await SerializationHelper.Deserialize(set[key]);
+                    this[UpdatingFromServer] = true;
+                    this[fKey] = value;
+                    this[UpdatingFromServer] = false;
+                };
+                if (sameAuthor || DocServer.getFieldWriteMode(fKey) !== DocServer.WriteMode.Playground) {
+                    delete this[CachedUpdates][fKey];
+                    await fn();
+                } else {
+                    this[CachedUpdates][fKey] = fn;
+                }
+            }
+        }
+        const unset = diff.$unset;
+        if (unset) {
+            for (const key in unset) {
+                if (!key.startsWith("fields.")) {
+                    continue;
+                }
+                const fKey = key.substring(7);
+                const fn = () => {
+                    this[UpdatingFromServer] = true;
+                    delete this[fKey];
+                    this[UpdatingFromServer] = false;
+                };
+                if (sameAuthor || DocServer.getFieldWriteMode(fKey) !== DocServer.WriteMode.Playground) {
+                    delete this[CachedUpdates][fKey];
+                    await fn();
+                } else {
+                    this[CachedUpdates][fKey] = fn;
+                }
+            }
+        }
+    }
+}
+
+export namespace Doc {
+    // export function GetAsync(doc: Doc, key: string, ignoreProto: boolean = false): Promise<Field | undefined> {
+    //     const self = doc[Self];
+    //     return new Promise(res => getField(self, key, ignoreProto, res));
+    // }
+    // export function GetTAsync<T extends Field>(doc: Doc, key: string, ctor: ToConstructor<T>, ignoreProto: boolean = false): Promise<T | undefined> {
+    //     return new Promise(async res => {
+    //         const field = await GetAsync(doc, key, ignoreProto);
+    //         return Cast(field, ctor);
+    //     });
+    // }
+
+    export function RunCachedUpdate(doc: Doc, field: string) {
+        const update = doc[CachedUpdates][field];
+        if (update) {
+            update();
+            delete doc[CachedUpdates][field];
+        }
+    }
+    export function AddCachedUpdate(doc: Doc, field: string, oldValue: any) {
+        const val = oldValue;
+        doc[CachedUpdates][field] = () => {
+            doc[UpdatingFromServer] = true;
+            doc[field] = val;
+            doc[UpdatingFromServer] = false;
+        };
+    }
+    export function MakeReadOnly(): { end(): void } {
+        makeReadOnly();
+        return {
+            end() {
+                makeEditable();
+            }
+        };
+    }
+
+    export function Get(doc: Doc, key: string, ignoreProto: boolean = false): FieldResult {
+        try {
+            return getField(doc[Self], key, ignoreProto);
+        } catch  {
+            return doc;
+        }
+    }
+    export function GetT<T extends Field>(doc: Doc, key: string, ctor: ToConstructor<T>, ignoreProto: boolean = false): FieldResult<T> {
+        return Cast(Get(doc, key, ignoreProto), ctor) as FieldResult<T>;
+    }
+    export function IsPrototype(doc: Doc) {
+        return GetT(doc, "isPrototype", "boolean", true);
+    }
+    export function IsBaseProto(doc: Doc) {
+        return GetT(doc, "baseProto", "boolean", true);
+    }
+    export async function SetInPlace(doc: Doc, key: string, value: Field | undefined, defaultProto: boolean) {
+        const hasProto = doc.proto instanceof Doc;
+        const onDeleg = Object.getOwnPropertyNames(doc).indexOf(key) !== -1;
+        const onProto = hasProto && Object.getOwnPropertyNames(doc.proto).indexOf(key) !== -1;
+        if (onDeleg || !hasProto || (!onProto && !defaultProto)) {
+            doc[key] = value;
+        } else doc.proto![key] = value;
+    }
+    export async function SetOnPrototype(doc: Doc, key: string, value: Field) {
+        const proto = Object.getOwnPropertyNames(doc).indexOf("isPrototype") === -1 ? doc.proto : doc;
+
+        if (proto) {
+            proto[key] = value;
+        }
+    }
+    export function GetAllPrototypes(doc: Doc): Doc[] {
+        const protos: Doc[] = [];
+        let d: Opt<Doc> = doc;
+        while (d) {
+            protos.push(d);
+            d = FieldValue(d.proto);
+        }
+        return protos;
+    }
+
+    /**
+     * This function is intended to model Object.assign({}, {}) [https://mzl.la/1Mo3l21], which copies
+     * the values of the properties of a source object into the target.
+     * 
+     * This is just a specific, Dash-authored version that serves the same role for our
+     * Doc class.
+     * 
+     * @param doc the target document into which you'd like to insert the new fields 
+     * @param fields the fields to project onto the target. Its type signature defines a mapping from some string key
+     * to a potentially undefined field, where each entry in this mapping is optional. 
+     */
+    export function assign<K extends string>(doc: Doc, fields: Partial<Record<K, Opt<Field>>>, skipUndefineds: boolean = false) {
+        for (const key in fields) {
+            if (fields.hasOwnProperty(key)) {
+                const value = fields[key];
+                if (!skipUndefineds || value !== undefined) { // Do we want to filter out undefineds?
+                    doc[key] = value;
+                }
+            }
+        }
+        return doc;
+    }
+
+    // compare whether documents or their protos match
+    export function AreProtosEqual(doc?: Doc, other?: Doc) {
+        if (!doc || !other) return false;
+        const r = (doc === other);
+        const r2 = (Doc.GetProto(doc) === other);
+        const r3 = (Doc.GetProto(other) === doc);
+        const r4 = (Doc.GetProto(doc) === Doc.GetProto(other) && Doc.GetProto(other) !== undefined);
+        return r || r2 || r3 || r4;
+    }
+
+    // Gets the data document for the document.  Note: this is mis-named -- it does not specifically
+    // return the doc's proto, but rather recursively searches through the proto inheritance chain 
+    // and returns the document who's proto is undefined or whose proto is marked as a base prototype ('isPrototype').
+    export function GetProto(doc: Doc): Doc {
+        if (doc instanceof Promise) {
+            console.log("GetProto: error: got Promise insead of Doc");
+        }
+        const proto = doc && (Doc.GetT(doc, "isPrototype", "boolean", true) ? doc : (doc.proto || doc));
+        return proto === doc ? proto : Doc.GetProto(proto);
+    }
+    export function GetDataDoc(doc: Doc): Doc {
+        const proto = Doc.GetProto(doc);
+        return proto === doc ? proto : Doc.GetDataDoc(proto);
+    }
+
+    export function allKeys(doc: Doc): string[] {
+        const results: Set<string> = new Set;
+
+        let proto: Doc | undefined = doc;
+        while (proto) {
+            Object.keys(proto).forEach(key => results.add(key));
+            proto = proto.proto;
+        }
+
+        return Array.from(results);
+    }
+
+    export function IndexOf(toFind: Doc, list: Doc[], allowProtos: boolean = true) {
+        let index = list.reduce((p, v, i) => (v instanceof Doc && v === toFind) ? i : p, -1);
+        index = allowProtos && index !== -1 ? index : list.reduce((p, v, i) => (v instanceof Doc && Doc.AreProtosEqual(v, toFind)) ? i : p, -1);
+        return index; // list.findIndex(doc => doc === toFind || Doc.AreProtosEqual(doc, toFind));
+    }
+    export function RemoveDocFromList(listDoc: Doc, fieldKey: string | undefined, doc: Doc) {
+        const key = fieldKey ? fieldKey : Doc.LayoutFieldKey(listDoc);
+        if (listDoc[key] === undefined) {
+            Doc.GetProto(listDoc)[key] = new List<Doc>();
+        }
+        const list = Cast(listDoc[key], listSpec(Doc));
+        if (list) {
+            const ind = list.indexOf(doc);
+            if (ind !== -1) {
+                list.splice(ind, 1);
+                return true;
+            }
+        }
+        return false;
+    }
+    export function AddDocToList(listDoc: Doc, fieldKey: string | undefined, doc: Doc, relativeTo?: Doc, before?: boolean, first?: boolean, allowDuplicates?: boolean, reversed?: boolean) {
+        const key = fieldKey ? fieldKey : Doc.LayoutFieldKey(listDoc);
+        if (listDoc[key] === undefined) {
+            Doc.GetProto(listDoc)[key] = new List<Doc>();
+        }
+        const list = Cast(listDoc[key], listSpec(Doc));
+        if (list) {
+            if (allowDuplicates !== true) {
+                const pind = list.reduce((l, d, i) => d instanceof Doc && d[Id] === doc[Id] ? i : l, -1);
+                if (pind !== -1) {
+                    list.splice(pind, 1);
+                }
+            }
+            if (first) {
+                list.splice(0, 0, doc);
+            }
+            else {
+                const ind = relativeTo ? list.indexOf(relativeTo) : -1;
+                if (ind === -1) {
+                    if (reversed) list.splice(0, 0, doc);
+                    else list.push(doc);
+                }
+                else {
+                    if (reversed) list.splice(before ? (list.length - ind) + 1 : list.length - ind, 0, doc);
+                    else list.splice(before ? ind : ind + 1, 0, doc);
+                }
+            }
+            return true;
+        }
+        return false;
+    }
+
+    //
+    // Computes the bounds of the contents of a set of documents.
+    //
+    export function ComputeContentBounds(docList: Doc[]) {
+        const bounds = docList.reduce((bounds, doc) => {
+            const [sptX, sptY] = [NumCast(doc.x), NumCast(doc.y)];
+            const [bptX, bptY] = [sptX + doc[WidthSym](), sptY + doc[HeightSym]()];
+            return {
+                x: Math.min(sptX, bounds.x), y: Math.min(sptY, bounds.y),
+                r: Math.max(bptX, bounds.r), b: Math.max(bptY, bounds.b)
+            };
+        }, { x: Number.MAX_VALUE, y: Number.MAX_VALUE, r: -Number.MAX_VALUE, b: -Number.MAX_VALUE });
+        return bounds;
+    }
+
+    export function MakeAlias(doc: Doc, id?: string) {
+        const alias = !GetT(doc, "isPrototype", "boolean", true) ? Doc.MakeCopy(doc, undefined, id) : Doc.MakeDelegate(doc, id);
+        const layout = Doc.LayoutField(alias);
+        if (layout instanceof Doc && layout !== alias && layout === Doc.Layout(alias)) {
+            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})`);
+        return alias;
+    }
+
+    //
+    // Determines whether the layout needs to be expanded (as a template).
+    // template expansion is rquired when the layout is a template doc/field and there's a datadoc which isn't equal to the layout template
+    //
+    export function WillExpandTemplateLayout(layoutDoc: Doc, dataDoc?: Doc) {
+        return (layoutDoc.isTemplateForField || layoutDoc.isTemplateDoc) && dataDoc && layoutDoc !== dataDoc;
+    }
+
+    const _pendingMap: Map<string, boolean> = new Map();
+    //
+    // Returns an expanded template layout for a target data document if there is a template relationship
+    // between the two. If so, the layoutDoc is expanded into a new document that inherits the properties 
+    // of the original layout while allowing for individual layout properties to be overridden in the expanded layout.
+    // templateArgs should be equivalent to the layout key that generates the template since that's where the template parameters are stored in ()'s at the end of the key.
+    // NOTE:  the template will have references to "@params" -- the template arguments will be assigned to the '@params' field
+    // so that when the @params key is accessed, it will be rewritten as the key that is stored in the 'params' field and
+    // the derefence will then occur on the rootDocument (the original document).
+    // in the future, field references could be written as @<someparam> and then arguments would be passed in the layout key as:
+    //   layout_mytemplate(somparam=somearg).   
+    // then any references to @someparam would be rewritten as accesses to 'somearg' on the rootDocument
+    export function expandTemplateLayout(templateLayoutDoc: Doc, targetDoc?: Doc, templateArgs?: string) {
+        const args = templateArgs?.match(/\(([a-zA-Z0-9._\-]*)\)/)?.[1].replace("()", "") || StrCast(templateLayoutDoc.PARAMS);
+        if (!args && !WillExpandTemplateLayout(templateLayoutDoc, targetDoc) || !targetDoc) return templateLayoutDoc;
+
+        const templateField = StrCast(templateLayoutDoc.isTemplateForField);  // the field that the template renders
+        // First it checks if an expanded layout already exists -- if so it will be stored on the dataDoc
+        // using the template layout doc's id as the field key.
+        // If it doesn't find the expanded layout, then it makes a delegate of the template layout and
+        // saves it on the data doc indexed by the template layout's id.
+        //
+        const params = args.split("=").length > 1 ? args.split("=")[0] : "PARAMS";
+        const layoutFielddKey = Doc.LayoutFieldKey(templateLayoutDoc);
+        const expandedLayoutFieldKey = (templateField || layoutFielddKey) + "-layout[" + templateLayoutDoc[Id] + (args ? `(${args})` : "") + "]";
+        let expandedTemplateLayout = targetDoc?.[expandedLayoutFieldKey];
+
+        if (templateLayoutDoc.resolvedDataDoc instanceof Promise) {
+            expandedTemplateLayout = undefined;
+            _pendingMap.set(targetDoc[Id] + expandedLayoutFieldKey, true);
+        }
+        else if (expandedTemplateLayout === undefined && !_pendingMap.get(targetDoc[Id] + expandedLayoutFieldKey + args)) {
+            if (templateLayoutDoc.resolvedDataDoc === (targetDoc.rootDocument || Doc.GetProto(targetDoc)) && templateLayoutDoc.PARAMS === StrCast(targetDoc.PARAMS)) {
+                expandedTemplateLayout = templateLayoutDoc; // reuse an existing template layout if its for the same document with the same params
+            } else {
+                _pendingMap.set(targetDoc[Id] + expandedLayoutFieldKey + args, true);
+                templateLayoutDoc.resolvedDataDoc && (templateLayoutDoc = Cast(templateLayoutDoc.proto, Doc, null) || templateLayoutDoc); // if the template has already been applied (ie, a nested template), then use the template's prototype
+                setTimeout(action(() => {
+                    if (!targetDoc[expandedLayoutFieldKey]) {
+                        const newLayoutDoc = Doc.MakeDelegate(templateLayoutDoc, undefined, "[" + templateLayoutDoc.title + "]");
+                        // the template's arguments are stored in params which is derefenced to find
+                        // the actual field key where the parameterized template data is stored.
+                        newLayoutDoc[params] = args !== "..." ? args : ""; // ... signifies the layout has sub template(s) -- so we have to expand the layout for them so that they can get the correct 'rootDocument' field, but we don't need to reassign their params.  it would be better if the 'rootDocument' field could be passed dynamically to avoid have to create instances
+                        newLayoutDoc.rootDocument = targetDoc;
+                        targetDoc[expandedLayoutFieldKey] = newLayoutDoc;
+                        const dataDoc = Doc.GetProto(targetDoc);
+                        newLayoutDoc.resolvedDataDoc = dataDoc;
+                        if (dataDoc[templateField] === undefined && templateLayoutDoc[templateField] instanceof List) {
+                            dataDoc[templateField] = ComputedField.MakeFunction(`ObjectField.MakeCopy(templateLayoutDoc["${templateField}"] as List)`, { templateLayoutDoc: Doc.name }, { templateLayoutDoc });
+                        }
+                        _pendingMap.delete(targetDoc[Id] + expandedLayoutFieldKey + args);
+                    }
+                }), 0);
+            }
+        }
+        return expandedTemplateLayout instanceof Doc ? expandedTemplateLayout : undefined; // layout is undefined if the expandedTemplateLayout is pending.
+    }
+
+    // if the childDoc is a template for a field, then this will return the expanded layout with its data doc.
+    // otherwise, it just returns the childDoc
+    export function GetLayoutDataDocPair(containerDoc: Doc, containerDataDoc: Opt<Doc>, childDoc: Doc) {
+        if (!childDoc || childDoc instanceof Promise || !Doc.GetProto(childDoc)) {
+            console.log("No, no, no!");
+            return { layout: childDoc, data: childDoc };
+        }
+        const resolvedDataDoc = (Doc.AreProtosEqual(containerDataDoc, containerDoc) || (!childDoc.isTemplateDoc && !childDoc.isTemplateForField && !childDoc.PARAMS) ? undefined : containerDataDoc);
+        return { layout: Doc.expandTemplateLayout(childDoc, resolvedDataDoc, "(" + StrCast(containerDoc.PARAMS) + ")"), data: resolvedDataDoc };
+    }
+
+    export function Overwrite(doc: Doc, overwrite: Doc, copyProto: boolean = false): Doc {
+        Object.keys(doc).forEach(key => {
+            const field = ProxyField.WithoutProxy(() => doc[key]);
+            if (key === "proto" && copyProto) {
+                if (doc.proto instanceof Doc && overwrite.proto instanceof Doc) {
+                    overwrite[key] = Doc.Overwrite(doc[key]!, overwrite.proto);
+                }
+            } else {
+                if (field instanceof RefField) {
+                    overwrite[key] = field;
+                } else if (field instanceof ObjectField) {
+                    overwrite[key] = ObjectField.MakeCopy(field);
+                } else if (field instanceof Promise) {
+                    debugger; //This shouldn't happend...
+                } else {
+                    overwrite[key] = field;
+                }
+            }
+        });
+
+        return overwrite;
+    }
+
+    export function MakeCopy(doc: Doc, copyProto: boolean = false, copyProtoId?: string): Doc {
+        const copy = new Doc(copyProtoId, true);
+        const exclude = Cast(doc.excludeFields, listSpec("string"), []);
+        Object.keys(doc).forEach(key => {
+            if (exclude.includes(key)) return;
+            const cfield = ComputedField.WithoutComputed(() => FieldValue(doc[key]));
+            const field = ProxyField.WithoutProxy(() => doc[key]);
+            if (key === "proto" && copyProto) {
+                if (doc[key] instanceof Doc) {
+                    copy[key] = Doc.MakeCopy(doc[key]!, false);
+                }
+            } else {
+                if (field instanceof RefField) {
+                    copy[key] = field;
+                } else if (cfield instanceof ComputedField) {
+                    copy[key] = ComputedField.MakeFunction(cfield.script.originalScript);
+                } else if (field instanceof ObjectField) {
+                    copy[key] = doc[key] instanceof Doc ?
+                        key.includes("layout[") ? Doc.MakeCopy(doc[key] as Doc, false) : doc[key] : // reference documents except copy documents that are expanded teplate fields 
+                        ObjectField.MakeCopy(field);
+                } else if (field instanceof Promise) {
+                    debugger; //This shouldn't happend...
+                } else {
+                    copy[key] = field;
+                }
+            }
+        });
+
+        return copy;
+    }
+
+    export function MakeClone(doc: Doc): Doc {
+        const cloneMap = new Map<string, Doc>();
+        const rtfMap: { copy: Doc, key: string, field: RichTextField }[] = [];
+        const copy = Doc.makeClone(doc, cloneMap, rtfMap);
+        rtfMap.map(({ copy, key, field }) => {
+            const replacer = (match: any, attr: string, id: string, offset: any, string: any) => {
+                const mapped = cloneMap.get(id);
+                return attr + "\"" + (mapped ? mapped[Id] : id) + "\"";
+            };
+            const replacer2 = (match: any, href: string, id: string, offset: any, string: any) => {
+                const mapped = cloneMap.get(id);
+                return href + (mapped ? mapped[Id] : id);
+            };
+            const regex = `(${Utils.prepend("/doc/")})([^"]*)`;
+            const re = new RegExp(regex, "g");
+            copy[key] = new RichTextField(field.Data.replace(/("docid":|"targetId":|"linkId":)"([^"]+)"/g, replacer).replace(re, replacer2), field.Text);
+        });
+        return copy;
+    }
+
+    export function makeClone(doc: Doc, cloneMap: Map<string, Doc>, rtfs: { copy: Doc, key: string, field: RichTextField }[]): Doc {
+        if (Doc.IsBaseProto(doc)) return doc;
+        if (cloneMap.get(doc[Id])) return cloneMap.get(doc[Id])!;
+        const copy = new Doc(undefined, true);
+        cloneMap.set(doc[Id], copy);
+        if (LinkManager.Instance.getAllLinks().includes(doc) && LinkManager.Instance.getAllLinks().indexOf(copy) === -1) LinkManager.Instance.addLink(copy);
+        const exclude = Cast(doc.excludeFields, listSpec("string"), []);
+        Object.keys(doc).forEach(key => {
+            if (exclude.includes(key)) return;
+            const cfield = ComputedField.WithoutComputed(() => FieldValue(doc[key]));
+            const field = ProxyField.WithoutProxy(() => doc[key]);
+            const copyObjectField = (field: ObjectField) => {
+                const list = Cast(doc[key], listSpec(Doc));
+                if (list !== undefined && !(list instanceof Promise)) {
+                    copy[key] = new List<Doc>(list.filter(d => d instanceof Doc).map(d => Doc.makeClone(d as Doc, cloneMap, rtfs)));
+                } else if (doc[key] instanceof Doc) {
+                    copy[key] = key.includes("layout[") ? undefined : Doc.makeClone(doc[key] as Doc, cloneMap, rtfs); // reference documents except copy documents that are expanded teplate fields 
+                } else {
+                    copy[key] = ObjectField.MakeCopy(field);
+                    if (field instanceof RichTextField) {
+                        if (field.Data.includes('"docid":') || field.Data.includes('"targetId":') || field.Data.includes('"linkId":')) {
+                            rtfs.push({ copy, key, field });
+                        }
+                    }
+                }
+            };
+            if (key === "proto") {
+                if (doc[key] instanceof Doc) {
+                    copy[key] = Doc.makeClone(doc[key]!, cloneMap, rtfs);
+                }
+            } else {
+                if (field instanceof RefField) {
+                    copy[key] = field;
+                } else if (cfield instanceof ComputedField) {
+                    copy[key] = ComputedField.MakeFunction(cfield.script.originalScript);
+                    (key === "links" && field instanceof ObjectField) && copyObjectField(field);
+                } else if (field instanceof ObjectField) {
+                    copyObjectField(field);
+                } else if (field instanceof Promise) {
+                    debugger; //This shouldn't happend...
+                } else {
+                    copy[key] = field;
+                }
+            }
+        });
+        Doc.SetInPlace(copy, "title", "CLONE: " + doc.title, true);
+        copy.cloneOf = doc;
+        cloneMap.set(doc[Id], copy);
+        return copy;
+    }
+
+    export function MakeDelegate(doc: Doc, id?: string, title?: string): Doc;
+    export function MakeDelegate(doc: Opt<Doc>, id?: string, title?: string): Opt<Doc>;
+    export function MakeDelegate(doc: Opt<Doc>, id?: string, title?: string): Opt<Doc> {
+        if (doc) {
+            const delegate = new Doc(id, true);
+            delegate.proto = doc;
+            title && (delegate.title = title);
+            return delegate;
+        }
+        return undefined;
+    }
+
+    let _applyCount: number = 0;
+    export function ApplyTemplate(templateDoc: Doc) {
+        if (templateDoc) {
+            const target = Doc.MakeDelegate(new Doc());
+            const targetKey = StrCast(templateDoc.layoutKey, "layout");
+            const applied = ApplyTemplateTo(templateDoc, target, targetKey, templateDoc.title + "(..." + _applyCount++ + ")");
+            target.layoutKey = targetKey;
+            applied && (Doc.GetProto(applied).type = templateDoc.type);
+            return applied;
+        }
+        return undefined;
+    }
+    export function ApplyTemplateTo(templateDoc: Doc, target: Doc, targetKey: string, titleTarget: string | undefined) {
+        if (!Doc.AreProtosEqual(target[targetKey] as Doc, templateDoc)) {
+            if (target.resolvedDataDoc) {
+                target[targetKey] = new PrefetchProxy(templateDoc);
+            } else {
+                titleTarget && (Doc.GetProto(target).title = titleTarget);
+                Doc.GetProto(target)[targetKey] = new PrefetchProxy(templateDoc);
+            }
+        }
+        return target;
+    }
+
+    //
+    //  This function converts a generic field layout display into a field layout that displays a specific
+    // metadata field indicated by the title of the template field (not the default field that it was rendering)
+    //
+    export function MakeMetadataFieldTemplate(templateField: Doc, templateDoc: Opt<Doc>): boolean {
+
+        // find the metadata field key that this template field doc will display (indicated by its title)
+        const metadataFieldKey = StrCast(templateField.isTemplateForField) || StrCast(templateField.title).replace(/^-/, "");
+
+        // update the original template to mark it as a template
+        templateField.isTemplateForField = metadataFieldKey;
+        templateField.title = metadataFieldKey;
+
+        const templateFieldValue = templateField[metadataFieldKey] || templateField[Doc.LayoutFieldKey(templateField)];
+        const templateCaptionValue = templateField.caption;
+        // move any data that the template field had been rendering over to the template doc so that things will still be rendered
+        // when the template field is adjusted to point to the new metadatafield key.
+        // note 1: if the template field contained a list of documents, each of those documents will be converted to templates as well.
+        // note 2: this will not overwrite any field that already exists on the template doc at the field key
+        if (!templateDoc?.[metadataFieldKey] && templateFieldValue instanceof ObjectField) {
+            Cast(templateFieldValue, listSpec(Doc), [])?.map(d => d instanceof Doc && MakeMetadataFieldTemplate(d, templateDoc));
+            (Doc.GetProto(templateField)[metadataFieldKey] = ObjectField.MakeCopy(templateFieldValue));
+        }
+        // copy the textTemplates from 'this' (not 'self') because the layout contains the template info, not the original doc
+        if (templateCaptionValue instanceof RichTextField && !templateCaptionValue.Empty()) {
+            templateField["caption-textTemplate"] = ComputedField.MakeFunction(`copyField(this.caption)`);
+        }
+        if (templateFieldValue instanceof RichTextField && !templateFieldValue.Empty()) {
+            templateField[metadataFieldKey + "-textTemplate"] = ComputedField.MakeFunction(`copyField(this.${metadataFieldKey})`);
+        }
+
+        // get the layout string that the template uses to specify its layout
+        const templateFieldLayoutString = StrCast(Doc.LayoutField(Doc.Layout(templateField)));
+
+        // change it to render the target metadata field instead of what it was rendering before and assign it to the template field layout document.
+        Doc.Layout(templateField).layout = templateFieldLayoutString.replace(/fieldKey={'[^']*'}/, `fieldKey={'${metadataFieldKey}'}`);
+
+        // assign the template field doc a delegate of any extension document that was previously used to render the template field (since extension doc's carry rendering informatino)
+        Doc.Layout(templateField)[metadataFieldKey + "_ext"] = Doc.MakeDelegate(templateField[templateFieldLayoutString?.split("'")[1] + "_ext"] as Doc);
+
+        return true;
+    }
+
+    export function overlapping(doc1: Doc, doc2: Doc, clusterDistance: number) {
+        const doc2Layout = Doc.Layout(doc2);
+        const doc1Layout = Doc.Layout(doc1);
+        const x2 = NumCast(doc2.x) - clusterDistance;
+        const y2 = NumCast(doc2.y) - clusterDistance;
+        const w2 = NumCast(doc2Layout._width) + clusterDistance;
+        const h2 = NumCast(doc2Layout._height) + clusterDistance;
+        const x = NumCast(doc1.x) - clusterDistance;
+        const y = NumCast(doc1.y) - clusterDistance;
+        const w = NumCast(doc1Layout._width) + clusterDistance;
+        const h = NumCast(doc1Layout._height) + clusterDistance;
+        return doc1.z === doc2.z && intersectRect({ left: x, top: y, width: w, height: h }, { left: x2, top: y2, width: w2, height: h2 });
+    }
+
+    export function isBrushedHighlightedDegree(doc: Doc) {
+        if (Doc.IsHighlighted(doc)) {
+            return 6;
+        }
+        else {
+            return Doc.IsBrushedDegree(doc);
+        }
+    }
+
+    export class DocBrush {
+        BrushedDoc: ObservableMap<Doc, boolean> = new ObservableMap();
+    }
+    const brushManager = new DocBrush();
+
+    export class DocData {
+        @observable _user_doc: Doc = undefined!;
+        @observable _searchQuery: string = "";
+    }
+
+    // the document containing the view layout information - will be the Document itself unless the Document has
+    // a layout field or 'layout' is given.  
+    export function Layout(doc: Doc, layout?: Doc): Doc {
+        const overrideLayout = layout && Cast(doc[`${StrCast(layout.isTemplateForField, "data")}-layout[` + layout[Id] + "]"], Doc, null);
+        return overrideLayout || doc[LayoutSym] || doc;
+    }
+    export function SetLayout(doc: Doc, layout: Doc | string) { doc[StrCast(doc.layoutKey, "layout")] = layout; }
+    export function LayoutField(doc: Doc) { return doc[StrCast(doc.layoutKey, "layout")]; }
+    export function LayoutFieldKey(doc: Doc): string { return StrCast(Doc.Layout(doc).layout).split("'")[1]; }
+    const manager = new DocData();
+    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 SetUserDoc(doc: Doc) { manager._user_doc = doc; }
+    export function IsBrushed(doc: Doc) {
+        return computedFn(function IsBrushed(doc: Doc) {
+            return brushManager.BrushedDoc.has(doc) || brushManager.BrushedDoc.has(Doc.GetProto(doc));
+        })(doc);
+    }
+    // don't bother memoizing (caching) the result if called from a non-reactive context. (plus this avoids a warning message)
+    export function IsBrushedDegreeUnmemoized(doc: Doc) {
+        return brushManager.BrushedDoc.has(doc) ? 2 : brushManager.BrushedDoc.has(Doc.GetProto(doc)) ? 1 : 0;
+    }
+    export function IsBrushedDegree(doc: Doc) {
+        return computedFn(function IsBrushDegree(doc: Doc) {
+            return Doc.IsBrushedDegreeUnmemoized(doc);
+        })(doc);
+    }
+    export function BrushDoc(doc: Doc) {
+        brushManager.BrushedDoc.set(doc, true);
+        brushManager.BrushedDoc.set(Doc.GetProto(doc), true);
+        return doc;
+    }
+    export function UnBrushDoc(doc: Doc) {
+        brushManager.BrushedDoc.delete(doc);
+        brushManager.BrushedDoc.delete(Doc.GetProto(doc));
+        return doc;
+    }
+
+
+    export function LinkOtherAnchor(linkDoc: Doc, anchorDoc: Doc) { return Doc.AreProtosEqual(anchorDoc, Cast(linkDoc.anchor1, Doc) as Doc) ? Cast(linkDoc.anchor2, Doc) as Doc : Cast(linkDoc.anchor1, Doc) as Doc; }
+    export function LinkEndpoint(linkDoc: Doc, anchorDoc: Doc) { return Doc.AreProtosEqual(anchorDoc, Cast(linkDoc.anchor1, Doc) as Doc) ? "1" : "2"; }
+
+    export function linkFollowUnhighlight() {
+        Doc.UnhighlightAll();
+        document.removeEventListener("pointerdown", linkFollowUnhighlight);
+    }
+
+    let _lastDate = 0;
+    export function linkFollowHighlight(destDoc: Doc, dataAndDisplayDocs = true) {
+        linkFollowUnhighlight();
+        Doc.HighlightDoc(destDoc, dataAndDisplayDocs);
+        document.removeEventListener("pointerdown", linkFollowUnhighlight);
+        document.addEventListener("pointerdown", linkFollowUnhighlight);
+        const lastDate = _lastDate = Date.now();
+        window.setTimeout(() => _lastDate === lastDate && linkFollowUnhighlight(), 5000);
+    }
+
+    export class HighlightBrush {
+        @observable HighlightedDoc: Map<Doc, boolean> = new Map();
+    }
+    const highlightManager = new HighlightBrush();
+    export function IsHighlighted(doc: Doc) {
+        return highlightManager.HighlightedDoc.get(doc) || highlightManager.HighlightedDoc.get(Doc.GetProto(doc));
+    }
+    export function HighlightDoc(doc: Doc, dataAndDisplayDocs = true) {
+        runInAction(() => {
+            highlightManager.HighlightedDoc.set(doc, true);
+            dataAndDisplayDocs && highlightManager.HighlightedDoc.set(Doc.GetProto(doc), true);
+        });
+    }
+    export function UnHighlightDoc(doc: Doc) {
+        runInAction(() => {
+            highlightManager.HighlightedDoc.set(doc, false);
+            highlightManager.HighlightedDoc.set(Doc.GetProto(doc), false);
+        });
+    }
+    export function UnhighlightAll() {
+        const mapEntries = highlightManager.HighlightedDoc.keys();
+        let docEntry: IteratorResult<Doc>;
+        while (!(docEntry = mapEntries.next()).done) {
+            const targetDoc = docEntry.value;
+            targetDoc && Doc.UnHighlightDoc(targetDoc);
+        }
+
+    }
+    export function UnBrushAllDocs() {
+        brushManager.BrushedDoc.clear();
+    }
+
+    export function getDocTemplate(doc?: Doc) {
+        return doc?.isTemplateDoc ? doc :
+            Cast(doc?.dragFactory, Doc, null)?.isTemplateDoc ? doc?.dragFactory :
+                Cast(doc?.layout, Doc, null)?.isTemplateDoc ? doc?.layout : undefined;
+    }
+
+    export function matchFieldValue(doc: Doc, key: string, value: any): boolean {
+        const fieldVal = doc[key];
+        if (Cast(fieldVal, listSpec("string"), []).length) {
+            const vals = Cast(fieldVal, listSpec("string"), []);
+            return vals.some(v => v === value);
+        }
+        const fieldStr = Field.toString(fieldVal as Field);
+        return fieldStr === value;
+    }
+
+    export function deiconifyView(doc: any) {
+        StrCast(doc.layoutKey).split("_")[1] === "icon" && setNativeView(doc);
+    }
+
+    export function setNativeView(doc: any) {
+        const prevLayout = StrCast(doc.layoutKey).split("_")[1];
+        const deiconify = prevLayout === "icon" && StrCast(doc.deiconifyLayout) ? "layout_" + StrCast(doc.deiconifyLayout) : "";
+        prevLayout === "icon" && (doc.deiconifyLayout = undefined);
+        doc.layoutKey = deiconify || "layout";
+    }
+    export function setDocFilterRange(target: Doc, key: string, range?: number[]) {
+        const docRangeFilters = Cast(target._docRangeFilters, listSpec("string"), []);
+        for (let i = 0; i < docRangeFilters.length; i += 3) {
+            if (docRangeFilters[i] === key) {
+                docRangeFilters.splice(i, 3);
+                break;
+            }
+        }
+        if (range !== undefined) {
+            docRangeFilters.push(key);
+            docRangeFilters.push(range[0].toString());
+            docRangeFilters.push(range[1].toString());
+            target._docRangeFilters = new List<string>(docRangeFilters);
+        }
+    }
+
+    export function aliasDocs(field: any) {
+        return new List<Doc>(field.map((d: any) => Doc.MakeAlias(d)));
+    }
+
+    // filters document in a container collection:
+    // all documents with the specified value for the specified key are included/excluded 
+    // based on the modifiers :"check", "x", undefined
+    export function setDocFilter(container: Doc, key: string, value: any, modifiers?: "check" | "x" | undefined) {
+        const docFilters = Cast(container._docFilters, listSpec("string"), []);
+        for (let i = 0; i < docFilters.length; i += 3) {
+            if (docFilters[i] === key && docFilters[i + 1] === value) {
+                docFilters.splice(i, 3);
+                break;
+            }
+        }
+        if (typeof modifiers === "string") {
+            docFilters.push(key);
+            docFilters.push(value);
+            docFilters.push(modifiers);
+            container._docFilters = new List<string>(docFilters);
+        }
+    }
+    export function readDocRangeFilter(doc: Doc, key: string) {
+        const docRangeFilters = Cast(doc._docRangeFilters, listSpec("string"), []);
+        for (let i = 0; i < docRangeFilters.length; i += 3) {
+            if (docRangeFilters[i] === key) {
+                return [Number(docRangeFilters[i + 1]), Number(docRangeFilters[i + 2])];
+            }
+        }
+    }
+    export function assignDocToField(doc: Doc, field: string, id: string) {
+        DocServer.GetRefField(id).then(layout => layout instanceof Doc && (doc[field] = layout));
+        return id;
+    }
+
+    export function toggleNativeDimensions(layoutDoc: Doc, contentScale: number, panelWidth: number, panelHeight: number) {
+        runInAction(() => {
+            if (layoutDoc._nativeWidth || layoutDoc._nativeHeight) {
+                layoutDoc.scale = NumCast(layoutDoc.scale, 1) * contentScale;
+                layoutDoc._nativeWidth = undefined;
+                layoutDoc._nativeHeight = undefined;
+            }
+            else {
+                layoutDoc._autoHeight = false;
+                if (!layoutDoc._nativeWidth) {
+                    layoutDoc._nativeWidth = NumCast(layoutDoc._width, panelWidth);
+                    layoutDoc._nativeHeight = NumCast(layoutDoc._height, panelHeight);
+                }
+            }
+        });
+    }
+
+    export function isDocPinned(doc: Doc) {
+        //add this new doc to props.Document
+        const curPres = Cast(Doc.UserDoc().activePresentation, Doc) as Doc;
+        if (curPres) {
+            return DocListCast(curPres.data).findIndex((val) => Doc.AreProtosEqual(val, doc)) !== -1;
+        }
+        return false;
+    }
+
+    // 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;
+            if (doc[doc.layoutKey] === undefined) {
+                createCustomView(doc, creator, templateSignature, docLayoutTemplate);
+            }
+        });
+        batch.end();
+    }
+    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 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);
+        // 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 && (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.type), 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 };
+
+        let fieldTemplate: Opt<Doc>;
+        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);
+        } else if (doc.data instanceof VideoField) {
+            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);
+        } else if (doc.data instanceof ImageField) {
+            fieldTemplate = Docs.Create.ImageDocument("http://www.cs.brown.edu", options);
+        }
+        const docTemplate = docLayoutTemplate || 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);
+    }
+    export function makeCustomView(doc: Doc, custom: boolean, layout: string) {
+        Doc.setNativeView(doc);
+        if (custom) {
+            makeCustomViewClicked(doc, Docs.Create.StackingDocument, layout, undefined);
+        }
+    }
+    export function iconify(doc: Doc) {
+        const layoutKey = Cast(doc.layoutKey, "string", null);
+        Doc.makeCustomViewClicked(doc, Docs.Create.StackingDocument, "icon", undefined);
+        if (layoutKey && layoutKey !== "layout" && layoutKey !== "layout_icon") doc.deiconifyLayout = layoutKey.replace("layout_", "");
+    }
+
+    export function pileup(selected: Doc[], x: number, y: number) {
+        const newCollection = Docs.Create.PileDocument(selected, { title: "pileup", x: x - 55, y: y - 55, _width: 110, _height: 100, _LODdisable: true });
+        let w = 0, h = 0;
+        selected.forEach((d, i) => {
+            Doc.iconify(d);
+            w = Math.max(d[WidthSym](), w);
+            h = Math.max(d[HeightSym](), 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
+        selected.forEach((d, i) => {
+            d.x = Math.cos(Math.PI * 2 * i / selected.length) * 10 - w / 2;
+            d.y = Math.sin(Math.PI * 2 * i / selected.length) * 10 - h / 2;
+            d.displayTimecode = undefined;  // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
+        });
+        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;
+        //newCollection.borderRounding = "40px";
+        newCollection._jitterRotation = 10;
+        newCollection._backgroundColor = "gray";
+        newCollection._overflow = "visible";
+        return newCollection;
+    }
+
+
+    export async function addFieldEnumerations(doc: Opt<Doc>, enumeratedFieldKey: string, enumerations: { title: string, _backgroundColor?: string, color?: string }[]) {
+        let optionsCollection = await DocServer.GetRefField(enumeratedFieldKey);
+        if (!(optionsCollection instanceof Doc)) {
+            optionsCollection = Docs.Create.StackingDocument([], { title: `${enumeratedFieldKey} field set` }, enumeratedFieldKey);
+            Doc.AddDocToList((Doc.UserDoc().fieldTypes as Doc), "data", optionsCollection as Doc);
+        }
+        const options = optionsCollection as Doc;
+        const targetDoc = doc && Doc.GetProto(Cast(doc.rootDocument, Doc, null) || doc);
+        const docFind = `options.data.find(doc => doc.title === (this.rootDocument||this)["${enumeratedFieldKey}"])?`;
+        targetDoc && (targetDoc.backgroundColor = ComputedField.MakeFunction(docFind + `._backgroundColor || "white"`, undefined, { options }));
+        targetDoc && (targetDoc.color = ComputedField.MakeFunction(docFind + `.color || "black"`, undefined, { options }));
+        targetDoc && (targetDoc.borderRounding = ComputedField.MakeFunction(docFind + `.borderRounding`, undefined, { options }));
+        enumerations.map(enumeration => {
+            const found = DocListCast(options.data).find(d => d.title === enumeration.title);
+            if (found) {
+                found._backgroundColor = enumeration._backgroundColor || found._backgroundColor;
+                found._color = enumeration.color || found._color;
+            } else {
+                Doc.AddDocToList(options, "data", Docs.Create.TextDocument(enumeration.title, enumeration));
+            }
+        });
+        return optionsCollection;
+    }
+}
+
+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 copyField(field: any) { return ObjectField.MakeCopy(field); });
+Scripting.addGlobal(function aliasDocs(field: any) { return Doc.aliasDocs(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() { return UndoManager.Undo(); });
+Scripting.addGlobal(function redo() { 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() {
+    const curPres = Doc.UserDoc().activePresentation as Doc;
+    return curPres && DocListCast(curPres[Doc.LayoutFieldKey(curPres)])[NumCast(curPres._itemIndex)];
+});
+Scripting.addGlobal(function selectDoc(doc: any) { Doc.UserDoc().activeSelection = new List([doc]); });
+Scripting.addGlobal(function selectedDocs(container: Doc, excludeCollections: boolean, prevValue: any) {
+    const docs = DocListCast(Doc.UserDoc().activeSelection).
+        filter(d => !Doc.AreProtosEqual(d, container) && !d.annotationOn && d.type !== DocumentType.DOCHOLDER && 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?: "check" | "x" | undefined) { Doc.setDocFilter(container, key, value, modifiers); });
+Scripting.addGlobal(function setDocFilterRange(container: Doc, key: string, range: number[]) { Doc.setDocFilterRange(container, key, range); });
\ No newline at end of file
diff --git a/src/fields/FieldSymbols.ts b/src/fields/FieldSymbols.ts
new file mode 100644
index 000000000..8d040f493
--- /dev/null
+++ b/src/fields/FieldSymbols.ts
@@ -0,0 +1,12 @@
+
+export const Update = Symbol("Update");
+export const Self = Symbol("Self");
+export const SelfProxy = Symbol("SelfProxy");
+export const HandleUpdate = Symbol("HandleUpdate");
+export const Id = Symbol("Id");
+export const OnUpdate = Symbol("OnUpdate");
+export const Parent = Symbol("Parent");
+export const Copy = Symbol("Copy");
+export const ToScriptString = Symbol("ToScriptString");
+export const ToPlainText = Symbol("ToPlainText");
+export const ToString = Symbol("ToString");
diff --git a/src/fields/HtmlField.ts b/src/fields/HtmlField.ts
new file mode 100644
index 000000000..6e8bba977
--- /dev/null
+++ b/src/fields/HtmlField.ts
@@ -0,0 +1,26 @@
+import { Deserializable } from "../client/util/SerializationHelper";
+import { serializable, primitive } from "serializr";
+import { ObjectField } from "./ObjectField";
+import { Copy, ToScriptString, ToString} from "./FieldSymbols";
+
+@Deserializable("html")
+export class HtmlField extends ObjectField {
+    @serializable(primitive())
+    readonly html: string;
+
+    constructor(html: string) {
+        super();
+        this.html = html;
+    }
+
+    [Copy]() {
+        return new HtmlField(this.html);
+    }
+
+    [ToScriptString]() {
+        return "invalid";
+    }
+    [ToString]() {
+        return this.html;
+    }
+}
diff --git a/src/fields/IconField.ts b/src/fields/IconField.ts
new file mode 100644
index 000000000..76c4ddf1b
--- /dev/null
+++ b/src/fields/IconField.ts
@@ -0,0 +1,26 @@
+import { Deserializable } from "../client/util/SerializationHelper";
+import { serializable, primitive } from "serializr";
+import { ObjectField } from "./ObjectField";
+import { Copy, ToScriptString, ToString } from "./FieldSymbols";
+
+@Deserializable("icon")
+export class IconField extends ObjectField {
+    @serializable(primitive())
+    readonly icon: string;
+
+    constructor(icon: string) {
+        super();
+        this.icon = icon;
+    }
+
+    [Copy]() {
+        return new IconField(this.icon);
+    }
+
+    [ToScriptString]() {
+        return "invalid";
+    }
+    [ToString]() {
+        return "ICONfield";
+    }
+}
diff --git a/src/fields/InkField.ts b/src/fields/InkField.ts
new file mode 100644
index 000000000..bb93de5ac
--- /dev/null
+++ b/src/fields/InkField.ts
@@ -0,0 +1,50 @@
+import { Deserializable } from "../client/util/SerializationHelper";
+import { serializable, custom, createSimpleSchema, list, object, map } from "serializr";
+import { ObjectField } from "./ObjectField";
+import { Copy, ToScriptString, ToString } from "./FieldSymbols";
+
+export enum InkTool {
+    None,
+    Pen,
+    Highlighter,
+    Eraser,
+    Stamp
+}
+
+export interface PointData {
+    X: number;
+    Y: number;
+}
+
+export type InkData = Array<PointData>;
+
+const pointSchema = createSimpleSchema({
+    X: true, Y: true
+});
+
+const strokeDataSchema = createSimpleSchema({
+    pathData: list(object(pointSchema)),
+    "*": true
+});
+
+@Deserializable("ink")
+export class InkField extends ObjectField {
+    @serializable(list(object(strokeDataSchema)))
+    readonly inkData: InkData;
+
+    constructor(data: InkData) {
+        super();
+        this.inkData = data;
+    }
+
+    [Copy]() {
+        return new InkField(this.inkData);
+    }
+
+    [ToScriptString]() {
+        return "invalid";
+    }
+    [ToString]() {
+        return "InkField";
+    }
+}
diff --git a/src/fields/List.ts b/src/fields/List.ts
new file mode 100644
index 000000000..fdabea365
--- /dev/null
+++ b/src/fields/List.ts
@@ -0,0 +1,330 @@
+import { Deserializable, autoObject, afterDocDeserialize } from "../client/util/SerializationHelper";
+import { Field } from "./Doc";
+import { setter, getter, deleteProperty, updateFunction } from "./util";
+import { serializable, alias, list } from "serializr";
+import { observable, action, runInAction } from "mobx";
+import { ObjectField } from "./ObjectField";
+import { RefField } from "./RefField";
+import { ProxyField } from "./Proxy";
+import { Self, Update, Parent, OnUpdate, SelfProxy, ToScriptString, ToString, Copy, Id } from "./FieldSymbols";
+import { Scripting } from "../client/util/Scripting";
+import { DocServer } from "../client/DocServer";
+
+const listHandlers: any = {
+    /// Mutator methods
+    copyWithin() {
+        throw new Error("copyWithin not supported yet");
+    },
+    fill(value: any, start?: number, end?: number) {
+        if (value instanceof RefField) {
+            throw new Error("fill with RefFields not supported yet");
+        }
+        const res = this[Self].__fields.fill(value, start, end);
+        this[Update]();
+        return res;
+    },
+    pop(): any {
+        const field = toRealField(this[Self].__fields.pop());
+        this[Update]();
+        return field;
+    },
+    push: action(function (this: any, ...items: any[]) {
+        items = items.map(toObjectField);
+        const list = this[Self];
+        const length = list.__fields.length;
+        for (let i = 0; i < items.length; i++) {
+            const item = items[i];
+            //TODO Error checking to make sure parent doesn't already exist
+            if (item instanceof ObjectField) {
+                item[Parent] = list;
+                item[OnUpdate] = updateFunction(list, i + length, item, this);
+            }
+        }
+        const res = list.__fields.push(...items);
+        this[Update]();
+        return res;
+    }),
+    reverse() {
+        const res = this[Self].__fields.reverse();
+        this[Update]();
+        return res;
+    },
+    shift() {
+        const res = toRealField(this[Self].__fields.shift());
+        this[Update]();
+        return res;
+    },
+    sort(cmpFunc: any) {
+        this[Self].__realFields(); // coerce retrieving entire array
+        const res = this[Self].__fields.sort(cmpFunc ? (first: any, second: any) => cmpFunc(toRealField(first), toRealField(second)) : undefined);
+        this[Update]();
+        return res;
+    },
+    splice: action(function (this: any, start: number, deleteCount: number, ...items: any[]) {
+        this[Self].__realFields(); // coerce retrieving entire array
+        items = items.map(toObjectField);
+        const list = this[Self];
+        for (let i = 0; i < items.length; i++) {
+            const item = items[i];
+            //TODO Error checking to make sure parent doesn't already exist
+            //TODO Need to change indices of other fields in array
+            if (item instanceof ObjectField) {
+                item[Parent] = list;
+                item[OnUpdate] = updateFunction(list, i + start, item, this);
+            }
+        }
+        const res = list.__fields.splice(start, deleteCount, ...items);
+        this[Update]();
+        return res.map(toRealField);
+    }),
+    unshift(...items: any[]) {
+        items = items.map(toObjectField);
+        const list = this[Self];
+        const length = list.__fields.length;
+        for (let i = 0; i < items.length; i++) {
+            const item = items[i];
+            //TODO Error checking to make sure parent doesn't already exist
+            //TODO Need to change indices of other fields in array
+            if (item instanceof ObjectField) {
+                item[Parent] = list;
+                item[OnUpdate] = updateFunction(list, i, item, this);
+            }
+        }
+        const res = this[Self].__fields.unshift(...items);
+        this[Update]();
+        return res;
+
+    },
+    /// Accessor methods
+    concat: action(function (this: any, ...items: any[]) {
+        this[Self].__realFields();
+        return this[Self].__fields.map(toRealField).concat(...items);
+    }),
+    includes(valueToFind: any, fromIndex: number) {
+        if (valueToFind instanceof RefField) {
+            return this[Self].__realFields().includes(valueToFind, fromIndex);
+        } else {
+            return this[Self].__fields.includes(valueToFind, fromIndex);
+        }
+    },
+    indexOf(valueToFind: any, fromIndex: number) {
+        if (valueToFind instanceof RefField) {
+            return this[Self].__realFields().indexOf(valueToFind, fromIndex);
+        } else {
+            return this[Self].__fields.indexOf(valueToFind, fromIndex);
+        }
+    },
+    join(separator: any) {
+        this[Self].__realFields();
+        return this[Self].__fields.map(toRealField).join(separator);
+    },
+    lastIndexOf(valueToFind: any, fromIndex: number) {
+        if (valueToFind instanceof RefField) {
+            return this[Self].__realFields().lastIndexOf(valueToFind, fromIndex);
+        } else {
+            return this[Self].__fields.lastIndexOf(valueToFind, fromIndex);
+        }
+    },
+    slice(begin: number, end: number) {
+        this[Self].__realFields();
+        return this[Self].__fields.slice(begin, end).map(toRealField);
+    },
+
+    /// Iteration methods
+    entries() {
+        return this[Self].__realFields().entries();
+    },
+    every(callback: any, thisArg: any) {
+        return this[Self].__realFields().every(callback, thisArg);
+        // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway.
+        // If we don't want to support the array parameter, we should use this version instead
+        // return this[Self].__fields.every((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg);
+    },
+    filter(callback: any, thisArg: any) {
+        return this[Self].__realFields().filter(callback, thisArg);
+        // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway.
+        // If we don't want to support the array parameter, we should use this version instead
+        // return this[Self].__fields.filter((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg);
+    },
+    find(callback: any, thisArg: any) {
+        return this[Self].__realFields().find(callback, thisArg);
+        // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway.
+        // If we don't want to support the array parameter, we should use this version instead
+        // return this[Self].__fields.find((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg);
+    },
+    findIndex(callback: any, thisArg: any) {
+        return this[Self].__realFields().findIndex(callback, thisArg);
+        // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway.
+        // If we don't want to support the array parameter, we should use this version instead
+        // return this[Self].__fields.findIndex((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg);
+    },
+    forEach(callback: any, thisArg: any) {
+        return this[Self].__realFields().forEach(callback, thisArg);
+        // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway.
+        // If we don't want to support the array parameter, we should use this version instead
+        // return this[Self].__fields.forEach((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg);
+    },
+    map(callback: any, thisArg: any) {
+        return this[Self].__realFields().map(callback, thisArg);
+        // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway.
+        // If we don't want to support the array parameter, we should use this version instead
+        // return this[Self].__fields.map((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg);
+    },
+    reduce(callback: any, initialValue: any) {
+        return this[Self].__realFields().reduce(callback, initialValue);
+        // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway.
+        // If we don't want to support the array parameter, we should use this version instead
+        // return this[Self].__fields.reduce((acc:any, element:any, index:number, array:any) => callback(acc, toRealField(element), index, array), initialValue);
+    },
+    reduceRight(callback: any, initialValue: any) {
+        return this[Self].__realFields().reduceRight(callback, initialValue);
+        // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway.
+        // If we don't want to support the array parameter, we should use this version instead
+        // return this[Self].__fields.reduceRight((acc:any, element:any, index:number, array:any) => callback(acc, toRealField(element), index, array), initialValue);
+    },
+    some(callback: any, thisArg: any) {
+        return this[Self].__realFields().some(callback, thisArg);
+        // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway.
+        // If we don't want to support the array parameter, we should use this version instead
+        // return this[Self].__fields.some((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg);
+    },
+    values() {
+        return this[Self].__realFields().values();
+    },
+    [Symbol.iterator]() {
+        return this[Self].__realFields().values();
+    }
+};
+
+function toObjectField(field: Field) {
+    return field instanceof RefField ? new ProxyField(field) : field;
+}
+
+function toRealField(field: Field) {
+    return field instanceof ProxyField ? field.value() : field;
+}
+
+function listGetter(target: any, prop: string | number | symbol, receiver: any): any {
+    if (listHandlers.hasOwnProperty(prop)) {
+        return listHandlers[prop];
+    }
+    return getter(target, prop, receiver);
+}
+
+interface ListSpliceUpdate<T> {
+    type: "splice";
+    index: number;
+    added: T[];
+    removedCount: number;
+}
+
+interface ListIndexUpdate<T> {
+    type: "update";
+    index: number;
+    newValue: T;
+}
+
+type ListUpdate<T> = ListSpliceUpdate<T> | ListIndexUpdate<T>;
+
+type StoredType<T extends Field> = T extends RefField ? ProxyField<T> : T;
+
+@Deserializable("list")
+class ListImpl<T extends Field> extends ObjectField {
+    constructor(fields?: T[]) {
+        super();
+        const list = new Proxy<this>(this, {
+            set: setter,
+            get: listGetter,
+            ownKeys: target => Object.keys(target.__fields),
+            getOwnPropertyDescriptor: (target, prop) => {
+                if (prop in target.__fields) {
+                    return {
+                        configurable: true,//TODO Should configurable be true?
+                        enumerable: true,
+                    };
+                }
+                return Reflect.getOwnPropertyDescriptor(target, prop);
+            },
+            deleteProperty: deleteProperty,
+            defineProperty: () => { throw new Error("Currently properties can't be defined on documents using Object.defineProperty"); },
+        });
+        this[SelfProxy] = list;
+        if (fields) {
+            (list as any).push(...fields);
+        }
+        return list;
+    }
+
+    [key: number]: T | (T extends RefField ? Promise<T> : never);
+
+    // this requests all ProxyFields at the same time to avoid the overhead
+    // of separate network requests and separate updates to the React dom.
+    private __realFields() {
+        const waiting = this.__fields.filter(f => f instanceof ProxyField && f.promisedValue());
+        const promised = waiting.map(f => f instanceof ProxyField ? f.promisedValue() : "");
+        // if we find any ProxyFields that don't have a current value, then
+        // start the server request for all of them
+        if (promised.length) {
+            const promise = DocServer.GetRefFields(promised);
+            // as soon as we get the fields from the server, set all the list values in one
+            // action to generate one React dom update.
+            promise.then(fields => runInAction(() => {
+                waiting.map((w, i) => w instanceof ProxyField && w.setValue(fields[promised[i]]));
+            }));
+            // we also have to mark all lists items with this promise so that any calls to them
+            // will await the batch request.
+            // This counts on the handler for 'promise' in the call above being invoked before the
+            // handler for 'promise' in the lines below.
+            waiting.map((w, i) => {
+                w instanceof ProxyField && w.setPromise(promise.then(fields => fields[promised[i]]));
+            });
+        }
+        return this.__fields.map(toRealField);
+    }
+
+    @serializable(alias("fields", list(autoObject(), { afterDeserialize: afterDocDeserialize })))
+    private get __fields() {
+        return this.___fields;
+    }
+
+    private set __fields(value) {
+        this.___fields = value;
+        for (const key in value) {
+            const field = value[key];
+            if (!(field instanceof ObjectField)) continue;
+            (field as ObjectField)[Parent] = this[Self];
+            (field as ObjectField)[OnUpdate] = updateFunction(this[Self], key, field, this[SelfProxy]);
+        }
+    }
+
+    [Copy]() {
+        const copiedData = this[Self].__fields.map(f => f instanceof ObjectField ? f[Copy]() : f);
+        const deepCopy = new ListImpl<T>(copiedData as any);
+        return deepCopy;
+    }
+
+    // @serializable(alias("fields", list(autoObject())))
+    @observable
+    private ___fields: StoredType<T>[] = [];
+
+    private [Update] = (diff: any) => {
+        // console.log(diff);
+        const update = this[OnUpdate];
+        // update && update(diff);
+        update?.();
+    }
+
+    private [Self] = this;
+    private [SelfProxy]: any;
+
+    [ToScriptString]() {
+        return `new List([${(this as any).map((field: any) => Field.toScriptString(field))}])`;
+    }
+    [ToString]() {
+        return "List";
+    }
+}
+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);
\ No newline at end of file
diff --git a/src/fields/ListSpec.ts b/src/fields/ListSpec.ts
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/fields/ObjectField.ts b/src/fields/ObjectField.ts
new file mode 100644
index 000000000..9aa1c9b04
--- /dev/null
+++ b/src/fields/ObjectField.ts
@@ -0,0 +1,20 @@
+import { RefField } from "./RefField";
+import { OnUpdate, Parent, Copy, ToScriptString, ToString } from "./FieldSymbols";
+import { Scripting } from "../client/util/Scripting";
+
+export abstract class ObjectField {
+    protected [OnUpdate](diff?: any) { }
+    private [Parent]?: RefField | ObjectField;
+    abstract [Copy](): ObjectField;
+
+    abstract [ToScriptString](): string;
+    abstract [ToString](): string;
+}
+
+export namespace ObjectField {
+    export function MakeCopy<T extends ObjectField>(field: T) {
+        return field?.[Copy]();
+    }
+}
+
+Scripting.addGlobal(ObjectField);
\ No newline at end of file
diff --git a/src/fields/PresField.ts b/src/fields/PresField.ts
new file mode 100644
index 000000000..f236a04fd
--- /dev/null
+++ b/src/fields/PresField.ts
@@ -0,0 +1,6 @@
+//insert code here
+import { ObjectField } from "./ObjectField";
+
+export abstract class PresField extends ObjectField {
+
+}
\ No newline at end of file
diff --git a/src/fields/Proxy.ts b/src/fields/Proxy.ts
new file mode 100644
index 000000000..555faaad0
--- /dev/null
+++ b/src/fields/Proxy.ts
@@ -0,0 +1,126 @@
+import { Deserializable } from "../client/util/SerializationHelper";
+import { FieldWaiting } from "./Doc";
+import { primitive, serializable } from "serializr";
+import { observable, action, runInAction } from "mobx";
+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 { Plugins } from "./util";
+
+@Deserializable("proxy")
+export class ProxyField<T extends RefField> extends ObjectField {
+    constructor();
+    constructor(value: T);
+    constructor(fieldId: string);
+    constructor(value?: T | string) {
+        super();
+        if (typeof value === "string") {
+            this.fieldId = value;
+        } else if (value) {
+            this.cache = value;
+            this.fieldId = value[Id];
+        }
+    }
+
+    [Copy]() {
+        if (this.cache) return new ProxyField<T>(this.cache);
+        return new ProxyField<T>(this.fieldId);
+    }
+
+    [ToScriptString]() {
+        return "invalid";
+    }
+    [ToString]() {
+        return "ProxyField";
+    }
+
+    @serializable(primitive())
+    readonly fieldId: string = "";
+
+    // This getter/setter and nested object thing is 
+    // because mobx doesn't play well with observable proxies
+    @observable.ref
+    private _cache: { readonly field: T | undefined } = { field: undefined };
+    private get cache(): T | undefined {
+        return this._cache.field;
+    }
+    private set cache(field: T | undefined) {
+        this._cache = { field };
+    }
+
+    private failed = false;
+    private promise?: Promise<any>;
+
+    value(): T | undefined | FieldWaiting<T> {
+        if (this.cache) {
+            return this.cache;
+        }
+        if (this.failed) {
+            return undefined;
+        }
+        if (!this.promise) {
+            const cached = DocServer.GetCachedRefField(this.fieldId);
+            if (cached !== undefined) {
+                runInAction(() => this.cache = cached as any);
+                return cached as any;
+            }
+            this.promise = DocServer.GetRefField(this.fieldId).then(action((field: any) => {
+                this.promise = undefined;
+                this.cache = field;
+                if (field === undefined) this.failed = true;
+                return field;
+            }));
+        }
+        return this.promise as any;
+    }
+    promisedValue(): string { return !this.cache && !this.failed && !this.promise ? this.fieldId : ""; }
+    setPromise(promise: any) {
+        this.promise = promise;
+    }
+    @action
+    setValue(field: any) {
+        this.promise = undefined;
+        this.cache = field;
+        if (field === undefined) this.failed = true;
+        return field;
+    }
+}
+
+export namespace ProxyField {
+    let useProxy = true;
+    export function DisableProxyFields() {
+        useProxy = false;
+    }
+
+    export function EnableProxyFields() {
+        useProxy = true;
+    }
+
+    export function WithoutProxy<T>(fn: () => T) {
+        DisableProxyFields();
+        try {
+            return fn();
+        } finally {
+            EnableProxyFields();
+        }
+    }
+
+    export function initPlugin() {
+        Plugins.addGetterPlugin((doc, _, value) => {
+            if (useProxy && value instanceof ProxyField) {
+                return { value: value.value() };
+            }
+        });
+    }
+}
+
+function prefetchValue(proxy: PrefetchProxy<RefField>) {
+    return proxy.value() as any;
+}
+
+@scriptingGlobal
+@Deserializable("prefetch_proxy", prefetchValue)
+export class PrefetchProxy<T extends RefField> extends ProxyField<T> {
+}
diff --git a/src/fields/RefField.ts b/src/fields/RefField.ts
new file mode 100644
index 000000000..b6ef69750
--- /dev/null
+++ b/src/fields/RefField.ts
@@ -0,0 +1,21 @@
+import { serializable, primitive, alias } from "serializr";
+import { Utils } from "../Utils";
+import { Id, HandleUpdate, ToScriptString, ToString } from "./FieldSymbols";
+import { afterDocDeserialize } from "../client/util/SerializationHelper";
+
+export type FieldId = string;
+export abstract class RefField {
+    @serializable(alias("id", primitive({ afterDeserialize: afterDocDeserialize })))
+    private __id: FieldId;
+    readonly [Id]: FieldId;
+
+    constructor(id?: FieldId) {
+        this.__id = id || Utils.GenerateGuid();
+        this[Id] = this.__id;
+    }
+
+    protected [HandleUpdate]?(diff: any): void | Promise<void>;
+
+    abstract [ToScriptString](): string;
+    abstract [ToString](): string;
+}
diff --git a/src/fields/RichTextField.ts b/src/fields/RichTextField.ts
new file mode 100644
index 000000000..5cf0e0cc3
--- /dev/null
+++ b/src/fields/RichTextField.ts
@@ -0,0 +1,41 @@
+import { ObjectField } from "./ObjectField";
+import { serializable } from "serializr";
+import { Deserializable } from "../client/util/SerializationHelper";
+import { Copy, ToScriptString, ToPlainText, ToString } from "./FieldSymbols";
+import { scriptingGlobal } from "../client/util/Scripting";
+
+@scriptingGlobal
+@Deserializable("RichTextField")
+export class RichTextField extends ObjectField {
+    @serializable(true)
+    readonly Data: string;
+
+    @serializable(true)
+    readonly Text: string;
+
+    constructor(data: string, text: string = "") {
+        super();
+        this.Data = data;
+        this.Text = text;
+    }
+
+    Empty() {
+        return !(this.Text || this.Data.toString().includes("dashField"));
+    }
+
+    [Copy]() {
+        return new RichTextField(this.Data, this.Text);
+    }
+
+    [ToScriptString]() {
+        return `new RichTextField("${this.Data}", "${this.Text}")`;
+    }
+    [ToString]() {
+        return this.Text;
+    }
+
+    public static DashField(fieldKey: string) {
+        return new RichTextField(`{"doc":{"type":"doc","content":[{"type":"paragraph","attrs":{"align":null,"color":null,"id":null,"indent":null,"inset":null,"lineSpacing":null,"paddingBottom":null,"paddingTop":null},"content":[{"type":"dashField","attrs":{"fieldKey":"${fieldKey}","docid":""}}]}]},"selection":{"type":"text","anchor":2,"head":2},"storedMarks":[]}`, "");
+    }
+
+}
\ No newline at end of file
diff --git a/src/fields/RichTextUtils.ts b/src/fields/RichTextUtils.ts
new file mode 100644
index 000000000..c475d0d73
--- /dev/null
+++ b/src/fields/RichTextUtils.ts
@@ -0,0 +1,519 @@
+import { AssertionError } from "assert";
+import { docs_v1 } from "googleapis";
+import { Fragment, Mark, Node } from "prosemirror-model";
+import { sinkListItem } from "prosemirror-schema-list";
+import { Utils } from "../Utils";
+import { Docs } from "../client/documents/Documents";
+import { schema } from "../client/views/nodes/formattedText/schema_rts";
+import { GooglePhotos } from "../client/apis/google_docs/GooglePhotosClientUtils";
+import { DocServer } from "../client/DocServer";
+import { Networking } from "../client/Network";
+import { FormattedTextBox } from "../client/views/nodes/formattedText/FormattedTextBox";
+import { Doc, Opt } from "./Doc";
+import { Id } from "./FieldSymbols";
+import { RichTextField } from "./RichTextField";
+import { Cast, StrCast } from "./Types";
+import Color = require('color');
+import { EditorState, TextSelection, Transaction } from "prosemirror-state";
+import { GoogleApiClientUtils } from "../client/apis/google_docs/GoogleApiClientUtils";
+
+export namespace RichTextUtils {
+
+    const delimiter = "\n";
+    const joiner = "";
+
+
+    export const Initialize = (initial?: string) => {
+        const content: any[] = [];
+        const state = {
+            doc: {
+                type: "doc",
+                content,
+            },
+            selection: {
+                type: "text",
+                anchor: 0,
+                head: 0
+            }
+        };
+        if (initial && initial.length) {
+            content.push({
+                type: "paragraph",
+                content: {
+                    type: "text",
+                    text: initial
+                }
+            });
+            state.selection.anchor = state.selection.head = initial.length + 1;
+        }
+        return JSON.stringify(state);
+    };
+
+    export const Synthesize = (plainText: string, oldState?: RichTextField) => {
+        return new RichTextField(ToProsemirrorState(plainText, oldState), plainText);
+    };
+
+    export const ToPlainText = (state: EditorState) => {
+        // Because we're working with plain text, just concatenate all paragraphs
+        const content = state.doc.content;
+        const paragraphs: Node<any>[] = [];
+        content.forEach(node => node.type.name === "paragraph" && paragraphs.push(node));
+
+        // Functions to flatten ProseMirror paragraph objects (and their components) to plain text
+        // Concatentate paragraphs and string the result together
+        const textParagraphs: string[] = paragraphs.map(paragraph => {
+            const text: string[] = [];
+            paragraph.content.forEach(node => node.text && text.push(node.text));
+            return text.join(joiner) + delimiter;
+        });
+        const plainText = textParagraphs.join(joiner);
+        return plainText.substring(0, plainText.length - 1);
+    };
+
+    export const ToProsemirrorState = (plainText: string, oldState?: RichTextField) => {
+        // Remap the text, creating blocks split on newlines
+        const elements = plainText.split(delimiter);
+
+        // Google Docs adds in an extra carriage return automatically, so this counteracts it
+        !elements[elements.length - 1].length && elements.pop();
+
+        // Preserve the current state, but re-write the content to be the blocks
+        const parsed = JSON.parse(oldState ? oldState.Data : Initialize());
+        parsed.doc.content = elements.map(text => {
+            const paragraph: any = { type: "paragraph" };
+            text.length && (paragraph.content = [{ type: "text", marks: [], text }]); // An empty paragraph gets treated as a line break
+            return paragraph;
+        });
+
+        // If the new content is shorter than the previous content and selection is unchanged, may throw an out of bounds exception, so we reset it
+        parsed.selection = { type: "text", anchor: 1, head: 1 };
+
+        // Export the ProseMirror-compatible state object we've just built
+        return JSON.stringify(parsed);
+    };
+
+    export namespace GoogleDocs {
+
+        export const Export = async (state: EditorState): Promise<GoogleApiClientUtils.Docs.Content> => {
+            const nodes: (Node<any> | null)[] = [];
+            const text = ToPlainText(state);
+            state.doc.content.forEach(node => {
+                if (!node.childCount) {
+                    nodes.push(null);
+                } else {
+                    node.content.forEach(child => nodes.push(child));
+                }
+            });
+            const requests = await marksToStyle(nodes);
+            return { text, requests };
+        };
+
+        interface ImageTemplate {
+            width: number;
+            title: string;
+            url: string;
+            agnostic: string;
+        }
+
+        const parseInlineObjects = async (document: docs_v1.Schema$Document): Promise<Map<string, ImageTemplate>> => {
+            const inlineObjectMap = new Map<string, ImageTemplate>();
+            const inlineObjects = document.inlineObjects;
+
+            if (inlineObjects) {
+                const objects = Object.keys(inlineObjects).map(objectId => inlineObjects[objectId]);
+                const mediaItems: MediaItem[] = objects.map(object => {
+                    const embeddedObject = object.inlineObjectProperties!.embeddedObject!;
+                    return { baseUrl: embeddedObject.imageProperties!.contentUri! };
+                });
+
+                const uploads = await Networking.PostToServer("/googlePhotosMediaGet", { mediaItems });
+
+                if (uploads.length !== mediaItems.length) {
+                    throw new AssertionError({ expected: mediaItems.length, actual: uploads.length, message: "Error with internally uploading inlineObjects!" });
+                }
+
+                for (let i = 0; i < objects.length; i++) {
+                    const object = objects[i];
+                    const { accessPaths } = uploads[i];
+                    const { agnostic, _m } = accessPaths;
+                    const embeddedObject = object.inlineObjectProperties!.embeddedObject!;
+                    const size = embeddedObject.size!;
+                    const width = size.width!.magnitude!;
+
+                    inlineObjectMap.set(object.objectId!, {
+                        title: embeddedObject.title || `Imported Image from ${document.title}`,
+                        width,
+                        url: Utils.prepend(_m.client),
+                        agnostic: Utils.prepend(agnostic.client)
+                    });
+                }
+            }
+            return inlineObjectMap;
+        };
+
+        type BulletPosition = { value: number, sinks: number };
+
+        interface MediaItem {
+            baseUrl: string;
+        }
+
+        export const Import = async (documentId: GoogleApiClientUtils.Docs.DocumentId, textNote: Doc): Promise<Opt<GoogleApiClientUtils.Docs.ImportResult>> => {
+            const document = await GoogleApiClientUtils.Docs.retrieve({ documentId });
+            if (!document) {
+                return undefined;
+            }
+            const inlineObjectMap = await parseInlineObjects(document);
+            const title = document.title!;
+            const { text, paragraphs } = GoogleApiClientUtils.Docs.Utils.extractText(document);
+            let state = FormattedTextBox.blankState();
+            const structured = parseLists(paragraphs);
+
+            let position = 3;
+            const lists: ListGroup[] = [];
+            const indentMap = new Map<ListGroup, BulletPosition[]>();
+            let globalOffset = 0;
+            const nodes: Node<any>[] = [];
+            for (const element of structured) {
+                if (Array.isArray(element)) {
+                    lists.push(element);
+                    const positions: BulletPosition[] = [];
+                    const items = element.map(paragraph => {
+                        const item = listItem(state.schema, paragraph.contents);
+                        const sinks = paragraph.bullet!;
+                        positions.push({
+                            value: position + globalOffset,
+                            sinks
+                        });
+                        position += item.nodeSize;
+                        globalOffset += 2 * sinks;
+                        return item;
+                    });
+                    indentMap.set(element, positions);
+                    nodes.push(list(state.schema, items));
+                } else {
+                    if (element.contents.some(child => "inlineObjectId" in child)) {
+                        const group = element.contents;
+                        group.forEach((child, i) => {
+                            let node: Opt<Node<any>>;
+                            if ("inlineObjectId" in child) {
+                                node = imageNode(state.schema, inlineObjectMap.get(child.inlineObjectId!)!, textNote);
+                            } else if ("content" in child && (i !== group.length - 1 || child.content!.removeTrailingNewlines().length)) {
+                                node = paragraphNode(state.schema, [child]);
+                            }
+                            if (node) {
+                                position += node.nodeSize;
+                                nodes.push(node);
+                            }
+                        });
+                    } else {
+                        const paragraph = paragraphNode(state.schema, element.contents);
+                        nodes.push(paragraph);
+                        position += paragraph.nodeSize;
+                    }
+                }
+            }
+            state = state.apply(state.tr.replaceWith(0, 2, nodes));
+
+            const sink = sinkListItem(state.schema.nodes.list_item);
+            const dispatcher = (tr: Transaction) => state = state.apply(tr);
+            for (const list of lists) {
+                for (const pos of indentMap.get(list)!) {
+                    const resolved = state.doc.resolve(pos.value);
+                    state = state.apply(state.tr.setSelection(new TextSelection(resolved)));
+                    for (let i = 0; i < pos.sinks; i++) {
+                        sink(state, dispatcher);
+                    }
+                }
+            }
+
+            return { title, text, state };
+        };
+
+        type Paragraph = GoogleApiClientUtils.Docs.Utils.DeconstructedParagraph;
+        type ListGroup = Paragraph[];
+        type PreparedParagraphs = (ListGroup | Paragraph)[];
+
+        const parseLists = (paragraphs: ListGroup) => {
+            const groups: PreparedParagraphs = [];
+            let group: ListGroup = [];
+            for (const paragraph of paragraphs) {
+                if (paragraph.bullet !== undefined) {
+                    group.push(paragraph);
+                } else {
+                    if (group.length) {
+                        groups.push(group);
+                        group = [];
+                    }
+                    groups.push(paragraph);
+                }
+            }
+            group.length && groups.push(group);
+            return groups;
+        };
+
+        const listItem = (schema: any, runs: docs_v1.Schema$TextRun[]): Node => {
+            return schema.node("list_item", null, paragraphNode(schema, runs));
+        };
+
+        const list = (schema: any, items: Node[]): Node => {
+            return schema.node("bullet_list", null, items);
+        };
+
+        const paragraphNode = (schema: any, runs: docs_v1.Schema$TextRun[]): Node => {
+            const children = runs.map(run => textNode(schema, run)).filter(child => child !== undefined);
+            const fragment = children.length ? Fragment.from(children) : undefined;
+            return schema.node("paragraph", null, fragment);
+        };
+
+        const imageNode = (schema: any, image: ImageTemplate, textNote: Doc) => {
+            const { url: src, width, agnostic } = image;
+            let docid: string;
+            const guid = Utils.GenerateDeterministicGuid(agnostic);
+            const backingDocId = StrCast(textNote[guid]);
+            if (!backingDocId) {
+                const backingDoc = Docs.Create.ImageDocument(agnostic, { _width: 300, _height: 300 });
+                Doc.makeCustomViewClicked(backingDoc, Docs.Create.FreeformDocument);
+                docid = backingDoc[Id];
+                textNote[guid] = docid;
+            } else {
+                docid = backingDocId;
+            }
+            return schema.node("image", { src, agnostic, width, docid, float: null, location: "onRight" });
+        };
+
+        const textNode = (schema: any, run: docs_v1.Schema$TextRun) => {
+            const text = run.content!.removeTrailingNewlines();
+            return text.length ? schema.text(text, styleToMarks(schema, run.textStyle)) : undefined;
+        };
+
+        const StyleToMark = new Map<keyof docs_v1.Schema$TextStyle, keyof typeof schema.marks>([
+            ["bold", "strong"],
+            ["italic", "em"],
+            ["foregroundColor", "pFontColor"],
+            ["fontSize", "pFontSize"]
+        ]);
+
+        const styleToMarks = (schema: any, textStyle?: docs_v1.Schema$TextStyle) => {
+            if (!textStyle) {
+                return undefined;
+            }
+            const marks: Mark[] = [];
+            Object.keys(textStyle).forEach(key => {
+                let value: any;
+                const targeted = key as keyof docs_v1.Schema$TextStyle;
+                if (value = textStyle[targeted]) {
+                    const attributes: any = {};
+                    let converted = StyleToMark.get(targeted) || targeted;
+
+                    value.url && (attributes.href = value.url);
+                    if (value.color) {
+                        const object = value.color.rgbColor;
+                        attributes.color = Color.rgb(["red", "green", "blue"].map(color => object[color] * 255 || 0)).hex();
+                    }
+                    if (value.magnitude) {
+                        attributes.fontSize = value.magnitude;
+                    }
+
+                    if (converted === "weightedFontFamily") {
+                        converted = ImportFontFamilyMapping.get(value.fontFamily) || "timesNewRoman";
+                    }
+
+                    const mapped = schema.marks[converted];
+                    if (!mapped) {
+                        alert(`No mapping found for ${converted}!`);
+                        return;
+                    }
+
+                    const mark = schema.mark(mapped, attributes);
+                    mark && marks.push(mark);
+                }
+            });
+            return marks;
+        };
+
+        const MarkToStyle = new Map<keyof typeof schema.marks, keyof docs_v1.Schema$TextStyle>([
+            ["strong", "bold"],
+            ["em", "italic"],
+            ["pFontColor", "foregroundColor"],
+            ["pFontSize", "fontSize"],
+            ["timesNewRoman", "weightedFontFamily"],
+            ["georgia", "weightedFontFamily"],
+            ["comicSans", "weightedFontFamily"],
+            ["tahoma", "weightedFontFamily"],
+            ["impact", "weightedFontFamily"]
+        ]);
+
+        const ExportFontFamilyMapping = new Map<string, string>([
+            ["timesNewRoman", "Times New Roman"],
+            ["arial", "Arial"],
+            ["georgia", "Georgia"],
+            ["comicSans", "Comic Sans MS"],
+            ["tahoma", "Tahoma"],
+            ["impact", "Impact"]
+        ]);
+
+        const ImportFontFamilyMapping = new Map<string, string>([
+            ["Times New Roman", "timesNewRoman"],
+            ["Arial", "arial"],
+            ["Georgia", "georgia"],
+            ["Comic Sans MS", "comicSans"],
+            ["Tahoma", "tahoma"],
+            ["Impact", "impact"]
+        ]);
+
+        const ignored = ["user_mark"];
+
+        const marksToStyle = async (nodes: (Node<any> | null)[]): Promise<docs_v1.Schema$Request[]> => {
+            const requests: docs_v1.Schema$Request[] = [];
+            let position = 1;
+            for (const node of nodes) {
+                if (node === null) {
+                    position += 2;
+                    continue;
+                }
+                const { marks, attrs, nodeSize } = node;
+                const textStyle: docs_v1.Schema$TextStyle = {};
+                const information: LinkInformation = {
+                    startIndex: position,
+                    endIndex: position + nodeSize,
+                    textStyle
+                };
+                let mark: Mark<any>;
+                const markMap = BuildMarkMap(marks);
+                for (const markName of Object.keys(schema.marks)) {
+                    if (ignored.includes(markName) || !(mark = markMap[markName])) {
+                        continue;
+                    }
+                    let converted = MarkToStyle.get(markName) || markName as keyof docs_v1.Schema$TextStyle;
+                    let value: any = true;
+                    if (!converted) {
+                        continue;
+                    }
+                    const { attrs } = mark;
+                    switch (converted) {
+                        case "link":
+                            let url = attrs.href;
+                            const delimiter = "/doc/";
+                            const alreadyShared = "?sharing=true";
+                            if (new RegExp(window.location.origin + delimiter).test(url) && !url.endsWith(alreadyShared)) {
+                                const linkDoc = await DocServer.GetRefField(url.split(delimiter)[1]);
+                                if (linkDoc instanceof Doc) {
+                                    let exported = (await Cast(linkDoc.anchor2, Doc))!;
+                                    if (!exported.customLayout) {
+                                        exported = Doc.MakeAlias(exported);
+                                        Doc.makeCustomViewClicked(exported, Docs.Create.FreeformDocument);
+                                        linkDoc.anchor2 = exported;
+                                    }
+                                    url = Utils.shareUrl(exported[Id]);
+                                }
+                            }
+                            value = { url };
+                            textStyle.foregroundColor = fromRgb.blue;
+                            textStyle.bold = true;
+                            break;
+                        case "fontSize":
+                            value = { magnitude: attrs.fontSize, unit: "PT" };
+                            break;
+                        case "foregroundColor":
+                            value = fromHex(attrs.color);
+                            break;
+                        case "weightedFontFamily":
+                            value = { fontFamily: ExportFontFamilyMapping.get(markName) };
+                    }
+                    let matches: RegExpExecArray | null;
+                    if ((matches = /p(\d+)/g.exec(markName)) !== null) {
+                        converted = "fontSize";
+                        value = { magnitude: parseInt(matches[1].replace("px", "")), unit: "PT" };
+                    }
+                    textStyle[converted] = value;
+                }
+                if (Object.keys(textStyle).length) {
+                    requests.push(EncodeStyleUpdate(information));
+                }
+                if (node.type.name === "image") {
+                    const width = attrs.width;
+                    requests.push(await EncodeImage({
+                        startIndex: position + nodeSize - 1,
+                        uri: attrs.agnostic,
+                        width: Number(typeof width === "string" ? width.replace("px", "") : width)
+                    }));
+                }
+                position += nodeSize;
+            }
+            return requests;
+        };
+
+        const BuildMarkMap = (marks: Mark<any>[]) => {
+            const markMap: { [type: string]: Mark<any> } = {};
+            marks.forEach(mark => markMap[mark.type.name] = mark);
+            return markMap;
+        };
+
+        interface LinkInformation {
+            startIndex: number;
+            endIndex: number;
+            textStyle: docs_v1.Schema$TextStyle;
+        }
+
+        interface ImageInformation {
+            startIndex: number;
+            width: number;
+            uri: string;
+        }
+
+        namespace fromRgb {
+
+            export const convert = (red: number, green: number, blue: number): docs_v1.Schema$OptionalColor => {
+                return {
+                    color: {
+                        rgbColor: {
+                            red: red / 255,
+                            green: green / 255,
+                            blue: blue / 255
+                        }
+                    }
+                };
+            };
+
+            export const red = convert(255, 0, 0);
+            export const green = convert(0, 255, 0);
+            export const blue = convert(0, 0, 255);
+
+        }
+
+        const fromHex = (color: string): docs_v1.Schema$OptionalColor => {
+            const c = Color(color);
+            return fromRgb.convert(c.red(), c.green(), c.blue());
+        };
+
+        const EncodeStyleUpdate = (information: LinkInformation): docs_v1.Schema$Request => {
+            const { startIndex, endIndex, textStyle } = information;
+            return {
+                updateTextStyle: {
+                    fields: "*",
+                    range: { startIndex, endIndex },
+                    textStyle
+                } as docs_v1.Schema$UpdateTextStyleRequest
+            };
+        };
+
+        const EncodeImage = async ({ uri, width, startIndex }: ImageInformation) => {
+            if (!uri) {
+                return {};
+            }
+            const source = [Docs.Create.ImageDocument(uri)];
+            const baseUrls = await GooglePhotos.Transactions.UploadThenFetch(source);
+            if (baseUrls) {
+                return {
+                    insertInlineImage: {
+                        uri: baseUrls[0],
+                        objectSize: { width: { magnitude: width, unit: "PT" } },
+                        location: { index: startIndex }
+                    }
+                };
+            }
+            return {};
+        };
+    }
+
+}
\ No newline at end of file
diff --git a/src/fields/Schema.ts b/src/fields/Schema.ts
new file mode 100644
index 000000000..72bce283d
--- /dev/null
+++ b/src/fields/Schema.ts
@@ -0,0 +1,120 @@
+import { Interface, ToInterface, Cast, ToConstructor, HasTail, Head, Tail, ListSpec, ToType, DefaultFieldConstructor } from "./Types";
+import { Doc, Field } from "./Doc";
+import { ObjectField } from "./ObjectField";
+import { RefField } from "./RefField";
+import { SelfProxy } from "./FieldSymbols";
+
+type AllToInterface<T extends Interface[]> = {
+    1: ToInterface<Head<T>> & AllToInterface<Tail<T>>,
+    0: ToInterface<Head<T>>
+}[HasTail<T> extends true ? 1 : 0];
+
+export const emptySchema = createSchema({});
+export const Document = makeInterface(emptySchema);
+export type Document = makeInterface<[typeof emptySchema]>;
+
+export interface InterfaceFunc<T extends Interface[]> {
+    (docs: Doc[]): makeInterface<T>[];
+    (): makeInterface<T>;
+    (doc: Doc): makeInterface<T>;
+}
+
+export type makeInterface<T extends Interface[]> = AllToInterface<T> & Doc & { proto: Doc | undefined };
+// export function makeInterface<T extends Interface[], U extends Doc>(schemas: T): (doc: U) => All<T, U>;
+// export function makeInterface<T extends Interface, U extends Doc>(schema: T): (doc: U) => makeInterface<T, U>; 
+export function makeInterface<T extends Interface[]>(...schemas: T): InterfaceFunc<T> {
+    const schema: Interface = {};
+    for (const s of schemas) {
+        for (const key in s) {
+            schema[key] = s[key];
+        }
+    }
+    const proto = new Proxy({}, {
+        get(target: any, prop, receiver) {
+            const field = receiver.doc[prop];
+            if (prop in schema) {
+                const desc = prop === "proto" ? Doc : (schema as any)[prop]; // bcz: proto doesn't appear in schemas ... maybe it should?
+                if (typeof desc === "object" && "defaultVal" in desc && "type" in desc) {//defaultSpec
+                    return Cast(field, desc.type, desc.defaultVal);
+                } else if (typeof desc === "function" && !ObjectField.isPrototypeOf(desc) && !RefField.isPrototypeOf(desc)) {
+                    const doc = Cast(field, Doc);
+                    if (doc === undefined) {
+                        return undefined;
+                    } else if (doc instanceof Doc) {
+                        return desc(doc);
+                    } else {
+                        return doc.then(doc => doc && desc(doc));
+                    }
+                } else {
+                    return Cast(field, desc);
+                }
+            }
+            return field;
+        },
+        set(target: any, prop, value, receiver) {
+            receiver.doc[prop] = value;
+            return true;
+        }
+    });
+    const fn = (doc: Doc) => {
+        doc = doc[SelfProxy];
+        // if (!(doc instanceof Doc)) {
+        //     throw new Error("Currently wrapping a schema in another schema isn't supported");
+        // }
+        const obj = Object.create(proto, { doc: { value: doc, writable: false } });
+        return obj;
+    };
+    return function (doc?: Doc | Doc[]) {
+        doc = doc || new Doc;
+        if (doc instanceof Doc) {
+            return fn(doc);
+        } else {
+            return doc.map(fn);
+        }
+    };
+}
+
+export type makeStrictInterface<T extends Interface> = Partial<ToInterface<T>>;
+export function makeStrictInterface<T extends Interface>(schema: T): (doc: Doc) => makeStrictInterface<T> {
+    const proto = {};
+    for (const key in schema) {
+        const type = schema[key];
+        Object.defineProperty(proto, key, {
+            get() {
+                return Cast(this.__doc[key], type as any);
+            },
+            set(value) {
+                value = Cast(value, type as any);
+                if (value !== undefined) {
+                    this.__doc[key] = value;
+                    return;
+                }
+                throw new TypeError("Expected type " + type);
+            }
+        });
+    }
+    return function (doc: any) {
+        if (!(doc instanceof Doc)) {
+            throw new Error("Currently wrapping a schema in another schema isn't supported");
+        }
+        const obj = Object.create(proto);
+        obj.__doc = doc;
+        return obj;
+    };
+}
+
+export function createSchema<T extends Interface>(schema: T): T & { proto: ToConstructor<Doc> } {
+    (schema as any).proto = Doc;
+    return schema as any;
+}
+
+export function listSpec<U extends ToConstructor<Field>>(type: U): ListSpec<ToType<U>> {
+    return { List: type as any };//TODO Types
+}
+
+export function defaultSpec<T extends ToConstructor<Field>>(type: T, defaultVal: ToType<T>): DefaultFieldConstructor<ToType<T>> {
+    return {
+        type: type as any,
+        defaultVal
+    };
+}
\ No newline at end of file
diff --git a/src/fields/SchemaHeaderField.ts b/src/fields/SchemaHeaderField.ts
new file mode 100644
index 000000000..07c90f5a2
--- /dev/null
+++ b/src/fields/SchemaHeaderField.ts
@@ -0,0 +1,122 @@
+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 { ColumnType } from "../client/views/collections/CollectionSchemaView";
+
+export const PastelSchemaPalette = new Map<string, string>([
+    // ["pink1", "#FFB4E8"],
+    ["pink2", "#ff9cee"],
+    ["pink3", "#ffccf9"],
+    ["pink4", "#fcc2ff"],
+    ["pink5", "#f6a6ff"],
+    ["purple1", "#b28dff"],
+    ["purple2", "#c5a3ff"],
+    ["purple3", "#d5aaff"],
+    ["purple4", "#ecd4ff"],
+    // ["purple5", "#fb34ff"],
+    ["purple6", "#dcd3ff"],
+    ["purple7", "#a79aff"],
+    ["purple8", "#b5b9ff"],
+    ["purple9", "#97a2ff"],
+    ["bluegreen1", "#afcbff"],
+    ["bluegreen2", "#aff8db"],
+    ["bluegreen3", "#c4faf8"],
+    ["bluegreen4", "#85e3ff"],
+    ["bluegreen5", "#ace7ff"],
+    // ["bluegreen6", "#6eb5ff"],
+    ["bluegreen7", "#bffcc6"],
+    ["bluegreen8", "#dbffd6"],
+    ["yellow1", "#f3ffe3"],
+    ["yellow2", "#e7ffac"],
+    ["yellow3", "#ffffd1"],
+    ["yellow4", "#fff5ba"],
+    // ["red1", "#ffc9de"],
+    ["red2", "#ffabab"],
+    ["red3", "#ffbebc"],
+    ["red4", "#ffcbc1"],
+    ["orange1", "#ffd5b3"],
+]);
+
+export const RandomPastel = () => Array.from(PastelSchemaPalette.values())[Math.floor(Math.random() * PastelSchemaPalette.size)];
+
+export const DarkPastelSchemaPalette = new Map<string, string>([
+    ["pink2", "#c932b0"],
+    ["purple4", "#913ad6"],
+    ["bluegreen1", "#3978ed"],
+    ["bluegreen7", "#2adb3e"],
+    ["bluegreen5", "#21b0eb"],
+    ["yellow4", "#edcc0c"],
+    ["red2", "#eb3636"],
+    ["orange1", "#f2740f"],
+]);
+
+@scriptingGlobal
+@Deserializable("schemaheader")
+export class SchemaHeaderField extends ObjectField {
+    @serializable(primitive())
+    heading: string;
+    @serializable(primitive())
+    color: string;
+    @serializable(primitive())
+    type: number;
+    @serializable(primitive())
+    width: number;
+    @serializable(primitive())
+    collapsed: boolean | undefined;
+    @serializable(primitive())
+    desc: boolean | undefined; // boolean determines sort order, undefined when no sort
+
+    constructor(heading: string = "", color: string = RandomPastel(), type?: ColumnType, width?: number, desc?: boolean, collapsed?: boolean) {
+        super();
+
+        this.heading = heading;
+        this.color = color;
+        this.type = type ? type : 0;
+        this.width = width ? width : -1;
+        this.desc = desc;
+        this.collapsed = collapsed;
+    }
+
+    setHeading(heading: string) {
+        this.heading = heading;
+        this[OnUpdate]();
+    }
+
+    setColor(color: string) {
+        this.color = color;
+        this[OnUpdate]();
+    }
+
+    setType(type: ColumnType) {
+        this.type = type;
+        this[OnUpdate]();
+    }
+
+    setWidth(width: number) {
+        this.width = width;
+        this[OnUpdate]();
+    }
+
+    setDesc(desc: boolean | undefined) {
+        this.desc = desc;
+        this[OnUpdate]();
+    }
+
+    setCollapsed(collapsed: boolean | undefined) {
+        this.collapsed = collapsed;
+        this[OnUpdate]();
+    }
+
+    [Copy]() {
+        return new SchemaHeaderField(this.heading, this.color, this.type);
+    }
+
+    [ToScriptString]() {
+        return `invalid`;
+    }
+    [ToString]() {
+        return `SchemaHeaderField`;
+    }
+}
\ No newline at end of file
diff --git a/src/fields/ScriptField.ts b/src/fields/ScriptField.ts
new file mode 100644
index 000000000..f05f431ac
--- /dev/null
+++ b/src/fields/ScriptField.ts
@@ -0,0 +1,193 @@
+import { ObjectField } from "./ObjectField";
+import { CompiledScript, CompileScript, scriptingGlobal, ScriptOptions, CompileError, CompileResult } 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 } from "./Doc";
+import { Plugins, setter } from "./util";
+import { computedFn } from "mobx-utils";
+import { ProxyField } from "./Proxy";
+import { Cast } from "./Types";
+
+function optional(propSchema: PropSchema) {
+    return custom(value => {
+        if (value !== undefined) {
+            return propSchema.serializer(value);
+        }
+        return SKIP;
+    }, (jsonValue: any, context: any, oldValue: any, callback: (err: any, result: any) => void) => {
+        if (jsonValue !== undefined) {
+            return propSchema.deserializer(jsonValue, callback, context, oldValue);
+        }
+        return SKIP;
+    });
+}
+
+const optionsSchema = createSimpleSchema({
+    requiredType: true,
+    addReturn: true,
+    typecheck: true,
+    editable: true,
+    readonly: true,
+    params: optional(map(primitive()))
+});
+
+const scriptSchema = createSimpleSchema({
+    options: object(optionsSchema),
+    originalScript: true
+});
+
+async function deserializeScript(script: ScriptField) {
+    const captures: ProxyField<Doc> = (script as any).captures;
+    if (captures) {
+        const doc = (await captures.value())!;
+        const captured: any = {};
+        const keys = Object.keys(doc);
+        const vals = await Promise.all(keys.map(key => doc[key]) as any);
+        keys.forEach((key, i) => captured[key] = vals[i]);
+        (script.script.options as any).capturedVariables = captured;
+    }
+    const comp = CompileScript(script.script.originalScript, script.script.options);
+    if (!comp.compiled) {
+        throw new Error("Couldn't compile loaded script");
+    }
+    (script as any).script = comp;
+}
+
+@scriptingGlobal
+@Deserializable("script", deserializeScript)
+export class ScriptField extends ObjectField {
+    @serializable(object(scriptSchema))
+    readonly script: CompiledScript;
+    @serializable(object(scriptSchema))
+    readonly setterscript: CompiledScript | undefined;
+
+    @serializable(autoObject())
+    private captures?: ProxyField<Doc>;
+
+    constructor(script: CompiledScript, setterscript?: CompileResult) {
+        super();
+
+        if (script?.options.capturedVariables) {
+            const doc = Doc.assign(new Doc, script.options.capturedVariables);
+            this.captures = new ProxyField(doc);
+        }
+        this.setterscript = setterscript?.compiled ? setterscript : undefined;
+        this.script = script;
+    }
+
+    //     init(callback: (res: Field) => any) {
+    //         const options = this.options!;
+    //         const keys = Object.keys(options.options.capturedIds);
+    //         Server.GetFields(keys).then(fields => {
+    //             let captured: { [name: string]: Field } = {};
+    //             keys.forEach(key => captured[options.options.capturedIds[key]] = fields[key]);
+    //             const opts: ScriptOptions = {
+    //                 addReturn: options.options.addReturn,
+    //                 params: options.options.params,
+    //                 requiredType: options.options.requiredType,
+    //                 capturedVariables: captured
+    //             };
+    //             const script = CompileScript(options.script, opts);
+    //             if (!script.compiled) {
+    //                 throw new Error("Can't compile script");
+    //             }
+    //             this._script = script;
+    //             callback(this);
+    //         });
+    //     }
+
+    [Copy](): ObjectField {
+        return new ScriptField(this.script);
+    }
+    toString() {
+        return `${this.script.originalScript}`;
+    }
+
+    [ToScriptString]() {
+        return "script field";
+    }
+    [ToString]() {
+        return this.script.originalScript;
+    }
+    public static CompileScript(script: string, params: object = {}, addReturn = false, capturedVariables?: { [name: string]: Field }) {
+        const compiled = CompileScript(script, {
+            params: { this: Doc.name, self: Doc.name, _last_: "any", ...params },
+            typecheck: false,
+            editable: true,
+            addReturn: addReturn,
+            capturedVariables
+        });
+        return compiled;
+    }
+    public static MakeFunction(script: string, params: object = {}, capturedVariables?: { [name: string]: Field }) {
+        const compiled = ScriptField.CompileScript(script, params, true, capturedVariables);
+        return compiled.compiled ? new ScriptField(compiled) : undefined;
+    }
+
+    public static MakeScript(script: string, params: object = {}, capturedVariables?: { [name: string]: Field }) {
+        const compiled = ScriptField.CompileScript(script, params, false, capturedVariables);
+        return compiled.compiled ? new ScriptField(compiled) : undefined;
+    }
+}
+
+@scriptingGlobal
+@Deserializable("computed", deserializeScript)
+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;
+
+
+    constructor(script: CompiledScript, setterscript?: CompiledScript) {
+        super(script,
+            !setterscript && script?.originalScript.includes("self.timecode") ?
+                ScriptField.CompileScript("self['x' + self.timecode] = value", { value: "any" }, true) : setterscript);
+    }
+
+    public static MakeScript(script: string, params: object = {}) {
+        const compiled = ScriptField.CompileScript(script, params, false);
+        return compiled.compiled ? new ComputedField(compiled) : undefined;
+    }
+    public static MakeFunction(script: string, params: object = {}, capturedVariables?: { [name: string]: Field }, setterScript?: string) {
+        const compiled = ScriptField.CompileScript(script, params, true, capturedVariables);
+        const setCompiled = setterScript ? ScriptField.CompileScript(setterScript, params, true, capturedVariables) : undefined;
+        return compiled.compiled ? new ComputedField(compiled, setCompiled?.compiled ? setCompiled : undefined) : undefined;
+    }
+    public static MakeInterpolated(fieldKey: string, interpolatorKey: string) {
+        const getField = ScriptField.CompileScript(`(self['${fieldKey}-indexed'])[self.${interpolatorKey}]`, {}, true, {});
+        const setField = ScriptField.CompileScript(`(self['${fieldKey}-indexed'])[self.${interpolatorKey}] = value`, { value: "any" }, true, {});
+        return getField.compiled ? new ComputedField(getField, setField?.compiled ? setField : undefined) : undefined;
+    }
+}
+
+export namespace ComputedField {
+    let useComputed = true;
+    export function DisableComputedFields() {
+        useComputed = false;
+    }
+
+    export function EnableComputedFields() {
+        useComputed = true;
+    }
+
+    export const undefined = "__undefined";
+
+    export function WithoutComputed<T>(fn: () => T) {
+        DisableComputedFields();
+        try {
+            return fn();
+        } finally {
+            EnableComputedFields();
+        }
+    }
+
+    export function initPlugin() {
+        Plugins.addGetterPlugin((doc, _, value) => {
+            if (useComputed && value instanceof ComputedField) {
+                return { value: value._valueOutsideReaction(doc), shouldReturn: true };
+            }
+        });
+    }
+}
\ No newline at end of file
diff --git a/src/fields/Types.ts b/src/fields/Types.ts
new file mode 100644
index 000000000..3d784448d
--- /dev/null
+++ b/src/fields/Types.ts
@@ -0,0 +1,108 @@
+import { Field, Opt, FieldResult, Doc } from "./Doc";
+import { List } from "./List";
+import { RefField } from "./RefField";
+import { DateField } from "./DateField";
+import { ScriptField } from "./ScriptField";
+
+export type ToType<T extends InterfaceValue> =
+    T extends "string" ? string :
+    T extends "number" ? number :
+    T extends "boolean" ? boolean :
+    T extends ListSpec<infer U> ? List<U> :
+    // T extends { new(...args: any[]): infer R } ? (R | Promise<R>) : never;
+    T extends DefaultFieldConstructor<infer _U> ? never :
+    T extends { new(...args: any[]): List<Field> } ? never :
+    T extends { new(...args: any[]): infer R } ? R :
+    T extends (doc?: Doc) => infer R ? R : never;
+
+export type ToConstructor<T extends Field> =
+    T extends string ? "string" :
+    T extends number ? "number" :
+    T extends boolean ? "boolean" :
+    T extends List<infer U> ? ListSpec<U> :
+    new (...args: any[]) => T;
+
+export type ToInterface<T extends Interface> = {
+    [P in Exclude<keyof T, "proto">]: T[P] extends DefaultFieldConstructor<infer F> ? Exclude<FieldResult<F>, undefined> : FieldResult<ToType<T[P]>>;
+};
+
+// type ListSpec<T extends Field[]> = { List: ToContructor<Head<T>> | ListSpec<Tail<T>> };
+export type ListSpec<T extends Field> = { List: ToConstructor<T> };
+
+export type DefaultFieldConstructor<T extends Field> = {
+    type: ToConstructor<T>,
+    defaultVal: T
+};
+
+// type ListType<U extends Field[]> = { 0: List<ListType<Tail<U>>>, 1: ToType<Head<U>> }[HasTail<U> extends true ? 0 : 1];
+
+export type Head<T extends any[]> = T extends [any, ...any[]] ? T[0] : never;
+export type Tail<T extends any[]> =
+    ((...t: T) => any) extends ((_: any, ...tail: infer TT) => any) ? TT : [];
+export type HasTail<T extends any[]> = T extends ([] | [any]) ? false : true;
+
+export type InterfaceValue = ToConstructor<Field> | ListSpec<Field> | DefaultFieldConstructor<Field> | ((doc?: Doc) => any);
+//TODO Allow you to optionally specify default values for schemas, which should then make that field not be partial
+export interface Interface {
+    [key: string]: InterfaceValue;
+    // [key: string]: ToConstructor<Field> | ListSpec<Field[]>;
+}
+export type WithoutRefField<T extends Field> = T extends RefField ? never : T;
+
+export type CastCtor = ToConstructor<Field> | ListSpec<Field>;
+
+export function Cast<T extends CastCtor>(field: FieldResult, ctor: T): FieldResult<ToType<T>>;
+export function Cast<T extends CastCtor>(field: FieldResult, ctor: T, defaultVal: WithoutList<WithoutRefField<ToType<T>>> | null): WithoutList<ToType<T>>;
+export function Cast<T extends CastCtor>(field: FieldResult, ctor: T, defaultVal?: ToType<T> | null): FieldResult<ToType<T>> | undefined {
+    if (field instanceof Promise) {
+        return defaultVal === undefined ? field.then(f => Cast(f, ctor) as any) as any : defaultVal === null ? undefined : defaultVal;
+    }
+    if (field !== undefined && !(field instanceof Promise)) {
+        if (typeof ctor === "string") {
+            if (typeof field === ctor) {
+                return field as ToType<T>;
+            }
+        } else if (typeof ctor === "object") {
+            if (field instanceof List) {
+                return field as any;
+            }
+        } else if (field instanceof (ctor as any)) {
+            return field as ToType<T>;
+        }
+    }
+    return defaultVal === null ? undefined : defaultVal;
+}
+
+export function NumCast(field: FieldResult, defaultVal: number | null = 0) {
+    return Cast(field, "number", defaultVal);
+}
+
+export function StrCast(field: FieldResult, defaultVal: string | null = "") {
+    return Cast(field, "string", defaultVal);
+}
+
+export function BoolCast(field: FieldResult, defaultVal: boolean | null = false) {
+    return Cast(field, "boolean", defaultVal);
+}
+export function DateCast(field: FieldResult) {
+    return Cast(field, DateField, null);
+}
+
+export function ScriptCast(field: FieldResult, defaultVal: ScriptField | null = null) {
+    return Cast(field, ScriptField, defaultVal);
+}
+
+type WithoutList<T extends Field> = T extends List<infer R> ? (R extends RefField ? (R | Promise<R>)[] : R[]) : T;
+
+export function FieldValue<T extends Field, U extends WithoutList<T>>(field: FieldResult<T>, defaultValue: U): WithoutList<T>;
+export function FieldValue<T extends Field>(field: FieldResult<T>): Opt<T>;
+export function FieldValue<T extends Field>(field: FieldResult<T>, defaultValue?: T): Opt<T> {
+    return (field instanceof Promise || field === undefined) ? defaultValue : field;
+}
+
+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); } };
+}
\ No newline at end of file
diff --git a/src/fields/URLField.ts b/src/fields/URLField.ts
new file mode 100644
index 000000000..fb71160ca
--- /dev/null
+++ b/src/fields/URLField.ts
@@ -0,0 +1,53 @@
+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";
+
+function url() {
+    return custom(
+        function (value: URL) {
+            return value.href;
+        },
+        function (jsonValue: string) {
+            return new URL(jsonValue);
+        }
+    );
+}
+
+export abstract class URLField extends ObjectField {
+    @serializable(url())
+    readonly url: URL;
+
+    constructor(url: string);
+    constructor(url: URL);
+    constructor(url: URL | string) {
+        super();
+        if (typeof url === "string") {
+            url = new URL(url);
+        }
+        this.url = url;
+    }
+
+    [ToScriptString]() {
+        return `new ${this.constructor.name}("${this.url.href}")`;
+    }
+    [ToString]() {
+        return this.url.href;
+    }
+
+    [Copy](): this {
+        return new (this.constructor as any)(this.url);
+    }
+}
+
+export const nullAudio = "https://actions.google.com/sounds/v1/alarms/beep_short.ogg";
+
+@scriptingGlobal @Deserializable("audio") export class AudioField 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 { }
+@scriptingGlobal @Deserializable("web") export class WebField extends URLField { }
+@scriptingGlobal @Deserializable("youtube") export class YoutubeField extends URLField { }
+@scriptingGlobal @Deserializable("webcam") export class WebCamField extends URLField { }
+
diff --git a/src/fields/documentSchemas.ts b/src/fields/documentSchemas.ts
new file mode 100644
index 000000000..cacba43b6
--- /dev/null
+++ b/src/fields/documentSchemas.ts
@@ -0,0 +1,100 @@
+import { makeInterface, createSchema, listSpec } from "./Schema";
+import { ScriptField } from "./ScriptField";
+import { Doc } from "./Doc";
+import { DateField } from "./DateField";
+
+export const documentSchema = createSchema({
+    // content properties
+    type: "string",             // enumerated type of document -- should be template-specific (ie, start with an '_')
+    title: "string",            // document title (can be on either data document or layout)
+    isTemplateForField: "string",// if specified, it indicates the document is a template that renders the specified field
+    creationDate: DateField,    // when the document was created
+    links: listSpec(Doc),       // computed (readonly) list of links associated with this document
+
+    // "Location" properties in a very general sense
+    currentTimecode: "number",  // current play back time of a temporal document (video / audio)
+    displayTimecode: "number",  // the time that a document should be displayed (e.g., time an annotation should be displayed on a video)
+    inOverlay: "boolean",       // whether the document is rendered in an OverlayView which handles selection/dragging differently
+    x: "number",                // x coordinate when in a freeform view 
+    y: "number",                // y coordinate when in a freeform view 
+    z: "number",                // z "coordinate" - non-zero specifies the overlay layer of a freeformview
+    zIndex: "number",           // zIndex of a document in a freeform view
+    scrollY: "number",          // "command" to scroll a document to a position on load (the value will be reset to 0 after that )
+    scrollTop: "number",        // scroll position of a scrollable document (pdf, text, web)
+
+    // appearance properties on the layout
+    _autoHeight: "boolean",     // whether the height of the document should be computed automatically based on its contents
+    _nativeWidth: "number",     // native width of document which determines how much document contents are scaled when the document's width is set
+    _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
+    _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
+    _showCaption: "string",     // whether editable caption text is overlayed at the bottom of the document 
+    _showTitle: "string",       // the fieldkey whose contents should be displayed at the top of the document
+    _showTitleHover: "string",  // the showTitle should be shown only on hover
+    _showAudio: "boolean",      // whether to show the audio record icon on documents
+    _freeformLayoutEngine: "string",// the string ID for the layout engine to use to layout freeform view documents
+    _LODdisable: "boolean",     // whether to disbale LOD switching for CollectionFreeFormViews
+    _pivotField: "string",      // specifies which field key should be used as the timeline/pivot axis
+    _replacedChrome: "string",  // what the default chrome is replaced with. Currently only supports the value of 'replaced' for PresBox's.
+    _chromeStatus: "string",    // determines the state of the collection chrome. values allowed are 'replaced', 'enabled', 'disabled', 'collapsed'
+    _fontSize: "number",
+    _fontFamily: "string",
+    _sidebarWidthPercent: "string", // percent of text window width taken up by sidebar
+
+    // appearance properties on the data document
+    backgroundColor: "string",  // background color of document
+    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
+    fontSize: "string",
+    layout: "string",           // this is the native layout string for the document.  templates can be added using other fields and setting layoutKey below
+    layoutKey: "string",        // holds the field key for the field that actually holds the current lyoat
+    letterSpacing: "string",
+    opacity: "number",          // opacity of document
+    strokeWidth: "number",
+    textTransform: "string",
+    treeViewOpen: "boolean",    //  flag denoting whether the documents sub-tree (contents) is visible or hidden
+    treeViewExpandedView: "string", // name of field whose contents are being displayed as the document's subtree
+    treeViewPreventOpen: "boolean", // ignores the treeViewOpen flag (for allowing a view to not be slaved to other views of the document)
+
+    // interaction and linking properties
+    ignoreClick: "boolean",     // whether documents ignores input clicks (but does not ignore manipulation and other events) 
+    onClick: ScriptField,       // script to run when document is clicked (can be overriden by an onClick prop)
+    onPointerDown: ScriptField, // script to run when document is clicked (can be overriden by an onClick prop)
+    onPointerUp: ScriptField,   // script to run when document is clicked (can be overriden by an onClick prop)
+    onDragStart: ScriptField,   // script to run when document is dragged (without being selected).  the script should return the Doc to be dropped.
+    followLinkLocation: "string",// flag for where to place content when following a click interaction (e.g., onRight, inPlace, inTab, ) 
+    isInPlaceContainer: "boolean",// whether the marked object will display addDocTab() calls that target "inPlace" destinations
+    isLinkButton: "boolean",    // whether document functions as a link follow button to follow the first link on the document when clicked   
+    isBackground: "boolean",    // whether document is a background element and ignores input events (can only select with marquee)
+    lockedPosition: "boolean",  // whether the document can be moved (dragged)
+    lockedTransform: "boolean", // whether the document can be panned/zoomed
+
+    // drag drop properties
+    dragFactory: Doc,           // the document that serves as the "template" for the onDragStart script.  ie, to drag out copies of the dragFactory document.
+    dropAction: "string",       // override specifying what should happen when this document is dropped (can be "alias", "copy", "move")
+    targetDropAction: "string", // allows the target of a drop event to specify the dropAction ("alias", "copy", "move") NOTE: if the document is dropped within the same collection, the dropAction is coerced to 'move'
+    childDropAction: "string",  // specify the override for what should happen when the child of a collection is dragged from it and dropped (can be "alias" or "copy")
+    removeDropProperties: listSpec("string"), // properties that should be removed from the alias/copy/etc of this document when it is dropped
+});
+
+
+export const collectionSchema = createSchema({
+    childLayoutTemplateName: "string", // the name of a template to use to override the layoutKey when rendering a document -- ONLY used in DocHolderBox 
+    childLayoutTemplate: Doc, // layout template to use to render children of a collecion
+    childLayoutString: "string", //layout string to use to render children of a collection
+    childClickedOpenTemplateView: Doc, // layout template to apply to a child when its clicked on in a collection and opened (requires onChildClick or other script to read this value and apply template)
+    dontRegisterChildViews: "boolean", // whether views made of this document are registered so that they can be found when drawing links scrollToLinkID: "string",   // id of link being traversed. allows this doc to scroll/highlight/etc its link anchor. scrollToLinkID should be set to undefined by this doc after it sets up its scroll,etc.
+    onChildClick: ScriptField, // script to run for each child when its clicked
+    onChildDoubleClick: ScriptField, // script to run for each child when its clicked
+    onCheckedClick: ScriptField, // script to run when a checkbox is clicked next to a child in a tree view
+});
+
+export type Document = makeInterface<[typeof documentSchema]>;
+export const Document = makeInterface(documentSchema);
diff --git a/src/fields/util.ts b/src/fields/util.ts
new file mode 100644
index 000000000..a287b0210
--- /dev/null
+++ b/src/fields/util.ts
@@ -0,0 +1,199 @@
+import { UndoManager } from "../client/util/UndoManager";
+import { Doc, Field, FieldResult, UpdatingFromServer, LayoutSym } 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 } from "./Types";
+
+function _readOnlySetter(): never {
+    throw new Error("Documents can't be modified in read-only mode");
+}
+
+const tracing = false;
+export function TraceMobx() {
+    tracing && trace();
+}
+
+export interface GetterResult {
+    value: FieldResult;
+    shouldReturn?: boolean;
+}
+export type GetterPlugin = (receiver: any, prop: string | number, currentValue: any) => GetterResult | undefined;
+const getterPlugins: GetterPlugin[] = [];
+
+export namespace Plugins {
+    export function addGetterPlugin(plugin: GetterPlugin) {
+        getterPlugins.push(plugin);
+    }
+}
+
+const _setterImpl = action(function (target: any, prop: string | symbol | number, value: any, receiver: any): boolean {
+    //console.log("-set " + target[SelfProxy].title + "(" + target[SelfProxy][prop] + ")." + prop.toString() + " = " + value);
+    if (SerializationHelper.IsSerializing()) {
+        target[prop] = value;
+        return true;
+    }
+
+    if (typeof prop === "symbol") {
+        target[prop] = value;
+        return true;
+    }
+    if (value !== undefined) {
+        value = value[SelfProxy] || value;
+    }
+    const curValue = target.__fields[prop];
+    if (curValue === value || (curValue instanceof ProxyField && value instanceof RefField && curValue.fieldId === value[Id])) {
+        // TODO This kind of checks correctly in the case that curValue is a ProxyField and value is a RefField, but technically
+        // curValue should get filled in with value if it isn't already filled in, in case we fetched the referenced field some other way
+        return true;
+    }
+    if (value instanceof RefField) {
+        value = new ProxyField(value);
+    }
+    if (value instanceof ObjectField) {
+        if (value[Parent] && value[Parent] !== receiver && !(value instanceof PrefetchProxy)) {
+            throw new Error("Can't put the same object in multiple documents at the same time");
+        }
+        value[Parent] = receiver;
+        value[OnUpdate] = updateFunction(target, prop, value, receiver);
+    }
+    if (curValue instanceof ObjectField) {
+        delete curValue[Parent];
+        delete curValue[OnUpdate];
+    }
+    const writeMode = DocServer.getFieldWriteMode(prop as string);
+    const fromServer = target[UpdatingFromServer];
+    const sameAuthor = fromServer || (receiver.author === Doc.CurrentUserEmail);
+    const writeToDoc = sameAuthor || (writeMode !== DocServer.WriteMode.LiveReadonly);
+    const writeToServer = sameAuthor || (writeMode === DocServer.WriteMode.Default);
+    if (writeToDoc) {
+        if (value === undefined) {
+            delete target.__fields[prop];
+        } else {
+            target.__fields[prop] = value;
+        }
+        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) } });
+        } else {
+            DocServer.registerDocWithCachedUpdate(receiver, prop as string, curValue);
+        }
+        UndoManager.AddEvent({
+            redo: () => receiver[prop] = value,
+            undo: () => receiver[prop] = curValue
+        });
+    }
+    return true;
+});
+
+let _setter: (target: any, prop: string | symbol | number, value: any, receiver: any) => boolean = _setterImpl;
+
+export function makeReadOnly() {
+    _setter = _readOnlySetter;
+}
+
+export function makeEditable() {
+    _setter = _setterImpl;
+}
+
+const layoutProps = ["panX", "panY", "width", "height", "nativeWidth", "nativeHeight", "fitWidth", "fitToBox",
+    "LODdisable", "chromeStatus", "viewType", "gridGap", "xMargin", "yMargin", "autoHeight"];
+export function setter(target: any, in_prop: string | symbol | number, value: any, receiver: any): boolean {
+    let prop = in_prop;
+    if (typeof prop === "string" && prop !== "__id" && prop !== "__fields" && (prop.startsWith("_") || layoutProps.includes(prop))) {
+        if (!prop.startsWith("_")) {
+            console.log(prop + " is deprecated - switch to _" + prop);
+            prop = "_" + prop;
+        }
+        if (target.__LAYOUT__) {
+            target.__LAYOUT__[prop] = value;
+            return true;
+        }
+    }
+    if (target.__fields[prop] instanceof ComputedField && target.__fields[prop].setterscript) {
+        return ScriptCast(target.__fields[prop])?.setterscript?.run({ self: target[SelfProxy], this: target[SelfProxy], value }).success ? true : false;
+    }
+    return _setter(target, prop, value, receiver);
+}
+
+export function getter(target: any, in_prop: string | symbol | number, receiver: any): any {
+    let prop = in_prop;
+    if (prop === LayoutSym) {
+        return target.__LAYOUT__;
+    }
+    if (typeof prop === "string" && prop !== "__id" && prop !== "__fields" && (prop.startsWith("_") || layoutProps.includes(prop))) {
+        if (!prop.startsWith("_")) {
+            console.log(prop + " is deprecated - switch to _" + prop);
+            prop = "_" + prop;
+        }
+        if (target.__LAYOUT__) return target.__LAYOUT__[prop];
+    }
+    if (prop === "then") {//If we're being awaited
+        return undefined;
+    }
+    if (typeof prop === "symbol") {
+        return target.__fields[prop] || target[prop];
+    }
+    if (SerializationHelper.IsSerializing()) {
+        return target[prop];
+    }
+    return getFieldImpl(target, prop, receiver);
+}
+
+function getFieldImpl(target: any, prop: string | number, receiver: any, ignoreProto: boolean = false): any {
+    receiver = receiver || target[SelfProxy];
+    let field = target.__fields[prop];
+    for (const plugin of getterPlugins) {
+        const res = plugin(receiver, prop, field);
+        if (res === undefined) continue;
+        if (res.shouldReturn) {
+            return res.value;
+        } else {
+            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 (proto instanceof Doc) {
+            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") {
+        delete target[prop];
+        return true;
+    }
+    target[SelfProxy][prop] = undefined;
+    return true;
+}
+
+export function updateFunction(target: any, prop: any, value: any, receiver: any) {
+    let current = ObjectField.MakeCopy(value);
+    return (diff?: any) => {
+        if (true || !diff) {
+            diff = { '$set': { ["fields." + prop]: SerializationHelper.Serialize(value) } };
+            const oldValue = current;
+            const newValue = ObjectField.MakeCopy(value);
+            current = newValue;
+            UndoManager.AddEvent({
+                redo() { receiver[prop] = newValue; },
+                undo() { receiver[prop] = oldValue; }
+            });
+        }
+        target[Update](diff);
+    };
+}
\ No newline at end of file
diff --git a/src/mobile/ImageUpload.tsx b/src/mobile/ImageUpload.tsx
index f30e9869a..231870531 100644
--- a/src/mobile/ImageUpload.tsx
+++ b/src/mobile/ImageUpload.tsx
@@ -4,10 +4,10 @@ import { Docs } from '../client/documents/Documents';
 import "./ImageUpload.scss";
 import React = require('react');
 import { DocServer } from '../client/DocServer';
-import { Opt, Doc } from '../new_fields/Doc';
-import { Cast } from '../new_fields/Types';
-import { listSpec } from '../new_fields/Schema';
-import { List } from '../new_fields/List';
+import { Opt, Doc } from '../fields/Doc';
+import { Cast } from '../fields/Types';
+import { listSpec } from '../fields/Schema';
+import { List } from '../fields/List';
 import { observer } from 'mobx-react';
 import { observable } from 'mobx';
 import { Utils } from '../Utils';
diff --git a/src/mobile/MobileInkOverlay.tsx b/src/mobile/MobileInkOverlay.tsx
index 1537ae034..973931615 100644
--- a/src/mobile/MobileInkOverlay.tsx
+++ b/src/mobile/MobileInkOverlay.tsx
@@ -4,11 +4,11 @@ import { MobileInkOverlayContent, GestureContent, UpdateMobileInkOverlayPosition
 import { observable, action } from "mobx";
 import { GestureUtils } from "../pen-gestures/GestureUtils";
 import "./MobileInkOverlay.scss";
-import { StrCast, Cast } from '../new_fields/Types';
+import { StrCast, Cast } from '../fields/Types';
 import { DragManager } from "../client/util/DragManager";
 import { DocServer } from '../client/DocServer';
-import { Doc, DocListCastAsync } from '../new_fields/Doc';
-import { listSpec } from '../new_fields/Schema';
+import { Doc, DocListCastAsync } from '../fields/Doc';
+import { listSpec } from '../fields/Schema';
 
 
 @observer
diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx
index 9d4d58ad1..6c2e797d6 100644
--- a/src/mobile/MobileInterface.tsx
+++ b/src/mobile/MobileInterface.tsx
@@ -16,12 +16,12 @@ import { InkingControl } from '../client/views/InkingControl';
 import { DocumentView } from '../client/views/nodes/DocumentView';
 import { RadialMenu } from '../client/views/nodes/RadialMenu';
 import { PreviewCursor } from '../client/views/PreviewCursor';
-import { Doc, DocListCast, FieldResult } from '../new_fields/Doc';
-import { Id } from '../new_fields/FieldSymbols';
-import { InkTool } from '../new_fields/InkField';
-import { listSpec } from '../new_fields/Schema';
-import { Cast, FieldValue } from '../new_fields/Types';
-import { WebField } from "../new_fields/URLField";
+import { Doc, DocListCast, FieldResult } from '../fields/Doc';
+import { Id } from '../fields/FieldSymbols';
+import { InkTool } from '../fields/InkField';
+import { listSpec } from '../fields/Schema';
+import { Cast, FieldValue } from '../fields/Types';
+import { WebField } from "../fields/URLField";
 import { CurrentUserUtils } from '../client/util/CurrentUserUtils';
 import { emptyFunction, emptyPath, returnEmptyString, returnFalse, returnOne, returnTrue, returnZero } from '../Utils';
 import "./MobileInterface.scss";
diff --git a/src/new_fields/CursorField.ts b/src/new_fields/CursorField.ts
deleted file mode 100644
index 28467377b..000000000
--- a/src/new_fields/CursorField.ts
+++ /dev/null
@@ -1,66 +0,0 @@
-import { ObjectField } from "./ObjectField";
-import { observable } from "mobx";
-import { Deserializable } from "../client/util/SerializationHelper";
-import { serializable, createSimpleSchema, object, date } from "serializr";
-import { OnUpdate, ToScriptString, ToString, Copy } from "./FieldSymbols";
-
-export type CursorPosition = {
-    x: number,
-    y: number
-};
-
-export type CursorMetadata = {
-    id: string,
-    identifier: string,
-    timestamp: number
-};
-
-export type CursorData = {
-    metadata: CursorMetadata,
-    position: CursorPosition
-};
-
-const PositionSchema = createSimpleSchema({
-    x: true,
-    y: true
-});
-
-const MetadataSchema = createSimpleSchema({
-    id: true,
-    identifier: true,
-    timestamp: true
-});
-
-const CursorSchema = createSimpleSchema({
-    metadata: object(MetadataSchema),
-    position: object(PositionSchema)
-});
-
-@Deserializable("cursor")
-export default class CursorField extends ObjectField {
-
-    @serializable(object(CursorSchema))
-    readonly data: CursorData;
-
-    constructor(data: CursorData) {
-        super();
-        this.data = data;
-    }
-
-    setPosition(position: CursorPosition) {
-        this.data.position = position;
-        this.data.metadata.timestamp = Date.now();
-        this[OnUpdate]();
-    }
-
-    [Copy]() {
-        return new CursorField(this.data);
-    }
-
-    [ToScriptString]() {
-        return "invalid";
-    }
-    [ToString]() {
-        return "invalid";
-    }
-}
\ No newline at end of file
diff --git a/src/new_fields/DateField.ts b/src/new_fields/DateField.ts
deleted file mode 100644
index a925148c2..000000000
--- a/src/new_fields/DateField.ts
+++ /dev/null
@@ -1,36 +0,0 @@
-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";
-
-@scriptingGlobal
-@Deserializable("date")
-export class DateField extends ObjectField {
-    @serializable(date())
-    readonly date: Date;
-
-    constructor(date: Date = new Date()) {
-        super();
-        this.date = date;
-    }
-
-    [Copy]() {
-        return new DateField(this.date);
-    }
-
-    toString() {
-        return `${this.date.toISOString()}`;
-    }
-
-    [ToScriptString]() {
-        return `new DateField(new Date(${this.date.toISOString()}))`;
-    }
-    [ToString]() {
-        return this.date.toISOString();
-    }
-}
-
-Scripting.addGlobal(function d(...dateArgs: any[]) {
-    return new DateField(new (Date as any)(...dateArgs));
-});
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
deleted file mode 100644
index a1e1e11b1..000000000
--- a/src/new_fields/Doc.ts
+++ /dev/null
@@ -1,1109 +0,0 @@
-import { action, computed, observable, ObservableMap, runInAction } from "mobx";
-import { computedFn } from "mobx-utils";
-import { alias, map, serializable } from "serializr";
-import { DocServer } from "../client/DocServer";
-import { DocumentType } from "../client/documents/DocumentTypes";
-import { Scripting, scriptingGlobal } from "../client/util/Scripting";
-import { afterDocDeserialize, autoObject, Deserializable, SerializationHelper } from "../client/util/SerializationHelper";
-import { UndoManager } from "../client/util/UndoManager";
-import { intersectRect, Utils } from "../Utils";
-import { HandleUpdate, Id, OnUpdate, Parent, Self, SelfProxy, ToScriptString, ToString, Update, Copy } from "./FieldSymbols";
-import { List } from "./List";
-import { ObjectField } from "./ObjectField";
-import { PrefetchProxy, ProxyField } from "./Proxy";
-import { FieldId, RefField } from "./RefField";
-import { RichTextField } from "./RichTextField";
-import { listSpec } from "./Schema";
-import { ComputedField, ScriptField } from "./ScriptField";
-import { Cast, FieldValue, NumCast, StrCast, ToConstructor, ScriptCast } from "./Types";
-import { deleteProperty, getField, getter, makeEditable, makeReadOnly, setter, updateFunction } from "./util";
-import { Docs, DocumentOptions } from "../client/documents/Documents";
-import { PdfField, VideoField, AudioField, ImageField } from "./URLField";
-import { LinkManager } from "../client/util/LinkManager";
-
-export namespace Field {
-    export function toKeyValueString(doc: Doc, key: string): string {
-        const onDelegate = Object.keys(doc).includes(key);
-
-        const field = ComputedField.WithoutComputed(() => FieldValue(doc[key]));
-        if (Field.IsField(field)) {
-            return (onDelegate ? "=" : "") + (field instanceof ComputedField ? `:=${field.script.originalScript}` : Field.toScriptString(field));
-        }
-        return "";
-    }
-    export function toScriptString(field: Field): string {
-        if (typeof field === "string") {
-            return `"${field}"`;
-        } else if (typeof field === "number" || typeof field === "boolean") {
-            return String(field);
-        } else {
-            return field[ToScriptString]();
-        }
-    }
-    export function toString(field: Field): string {
-        if (typeof field === "string") {
-            return field;
-        } else if (typeof field === "number" || typeof field === "boolean") {
-            return String(field);
-        } else if (field instanceof ObjectField) {
-            return field[ToString]();
-        } else if (field instanceof RefField) {
-            return field[ToString]();
-        }
-        return "";
-    }
-    export function IsField(field: any): field is Field;
-    export function IsField(field: any, includeUndefined: true): field is Field | undefined;
-    export function IsField(field: any, includeUndefined: boolean = false): field is Field | undefined {
-        return (typeof field === "string")
-            || (typeof field === "number")
-            || (typeof field === "boolean")
-            || (field instanceof ObjectField)
-            || (field instanceof RefField)
-            || (includeUndefined && field === undefined);
-    }
-}
-export type Field = number | string | boolean | ObjectField | RefField;
-export type Opt<T> = T | undefined;
-export type FieldWaiting<T extends RefField = RefField> = T extends undefined ? never : Promise<T | undefined>;
-export type FieldResult<T extends Field = Field> = Opt<T> | FieldWaiting<Extract<T, RefField>>;
-
-/**
- * Cast any field to either a List of Docs or undefined if the given field isn't a List of Docs.  
- * If a default value is given, that will be returned instead of undefined.  
- * If a default value is given, the returned value should not be modified as it might be a temporary value.  
- * If no default value is given, and the returned value is not undefined, it can be safely modified.  
- */
-export function DocListCastAsync(field: FieldResult): Promise<Doc[] | undefined>;
-export function DocListCastAsync(field: FieldResult, defaultValue: Doc[]): Promise<Doc[]>;
-export function DocListCastAsync(field: FieldResult, defaultValue?: Doc[]) {
-    const list = Cast(field, listSpec(Doc));
-    return list ? Promise.all(list).then(() => list) : Promise.resolve(defaultValue);
-}
-
-export async function DocCastAsync(field: FieldResult): Promise<Opt<Doc>> {
-    return Cast(field, Doc);
-}
-
-export function DocListCast(field: FieldResult): Doc[] {
-    return Cast(field, listSpec(Doc), []).filter(d => d instanceof Doc) as Doc[];
-}
-
-export const WidthSym = Symbol("Width");
-export const HeightSym = Symbol("Height");
-export const DataSym = Symbol("Data");
-export const LayoutSym = Symbol("Layout");
-export const UpdatingFromServer = Symbol("UpdatingFromServer");
-const CachedUpdates = Symbol("Cached updates");
-
-
-function fetchProto(doc: Doc) {
-    const proto = doc.proto;
-    if (proto instanceof Promise) {
-        return proto;
-    }
-}
-
-@scriptingGlobal
-@Deserializable("Doc", fetchProto).withFields(["id"])
-export class Doc extends RefField {
-    constructor(id?: FieldId, forceSave?: boolean) {
-        super(id);
-        const doc = new Proxy<this>(this, {
-            set: setter,
-            get: getter,
-            // getPrototypeOf: (target) => Cast(target[SelfProxy].proto, Doc) || null, // TODO this might be able to replace the proto logic in getter
-            has: (target, key) => key in target.__fields,
-            ownKeys: target => {
-                const obj = {} as any;
-                Object.assign(obj, target.___fields);
-                runInAction(() => obj.__LAYOUT__ = target.__LAYOUT__);
-                return Object.keys(obj);
-            },
-            getOwnPropertyDescriptor: (target, prop) => {
-                if (prop.toString() === "__LAYOUT__") {
-                    return Reflect.getOwnPropertyDescriptor(target, prop);
-                }
-                if (prop in target.__fields) {
-                    return {
-                        configurable: true,//TODO Should configurable be true?
-                        enumerable: true,
-                        value: target.__fields[prop]
-                    };
-                }
-                return Reflect.getOwnPropertyDescriptor(target, prop);
-            },
-            deleteProperty: deleteProperty,
-            defineProperty: () => { throw new Error("Currently properties can't be defined on documents using Object.defineProperty"); },
-        });
-        this[SelfProxy] = doc;
-        if (!id || forceSave) {
-            DocServer.CreateField(doc);
-        }
-        return doc;
-    }
-
-    proto: Opt<Doc>;
-    [key: string]: FieldResult;
-
-    @serializable(alias("fields", map(autoObject(), { afterDeserialize: afterDocDeserialize })))
-    private get __fields() { return this.___fields; }
-    private set __fields(value) {
-        this.___fields = value;
-        for (const key in value) {
-            const field = value[key];
-            if (!(field instanceof ObjectField)) continue;
-            field[Parent] = this[Self];
-            field[OnUpdate] = updateFunction(this[Self], key, field, this[SelfProxy]);
-        }
-    }
-
-    @observable
-    //{ [key: string]: Field | FieldWaiting | undefined }
-    private ___fields: any = {};
-
-    private [UpdatingFromServer]: boolean = false;
-
-    private [Update] = (diff: any) => {
-        !this[UpdatingFromServer] && DocServer.UpdateField(this[Id], diff);
-    }
-
-    private [Self] = this;
-    private [SelfProxy]: any;
-    public [WidthSym] = () => NumCast(this[SelfProxy]._width);
-    public [HeightSym] = () => NumCast(this[SelfProxy]._height);
-    public get [LayoutSym]() { return this[SelfProxy].__LAYOUT__; }
-    public get [DataSym]() {
-        const self = this[SelfProxy];
-        return self.resolvedDataDoc && !self.isTemplateForField ? self :
-            Doc.GetProto(Cast(Doc.Layout(self).resolvedDataDoc, Doc, null) || self);
-    }
-    @computed get __LAYOUT__() {
-        const templateLayoutDoc = Cast(Doc.LayoutField(this[SelfProxy]), Doc, null);
-        if (templateLayoutDoc) {
-            let renderFieldKey: any;
-            const layoutField = templateLayoutDoc[StrCast(templateLayoutDoc.layoutKey, "layout")];
-            if (typeof layoutField === "string") {
-                renderFieldKey = layoutField.split("fieldKey={'")[1].split("'")[0];//layoutField.split("'")[1];
-            } else {
-                return Cast(layoutField, Doc, null);
-            }
-            return Cast(this[SelfProxy][renderFieldKey + "-layout[" + templateLayoutDoc[Id] + "]"], Doc, null) || templateLayoutDoc;
-        }
-        return undefined;
-    }
-
-    [ToScriptString]() { return `DOC-"${this[Self][Id]}"-`; }
-    [ToString]() { return `Doc(${this.title})`; }
-
-    private [CachedUpdates]: { [key: string]: () => void | Promise<any> } = {};
-    public static CurrentUserEmail: string = "";
-    public async [HandleUpdate](diff: any) {
-        const set = diff.$set;
-        const sameAuthor = this.author === Doc.CurrentUserEmail;
-        if (set) {
-            for (const key in set) {
-                if (!key.startsWith("fields.")) {
-                    continue;
-                }
-                const fKey = key.substring(7);
-                const fn = async () => {
-                    const value = await SerializationHelper.Deserialize(set[key]);
-                    this[UpdatingFromServer] = true;
-                    this[fKey] = value;
-                    this[UpdatingFromServer] = false;
-                };
-                if (sameAuthor || DocServer.getFieldWriteMode(fKey) !== DocServer.WriteMode.Playground) {
-                    delete this[CachedUpdates][fKey];
-                    await fn();
-                } else {
-                    this[CachedUpdates][fKey] = fn;
-                }
-            }
-        }
-        const unset = diff.$unset;
-        if (unset) {
-            for (const key in unset) {
-                if (!key.startsWith("fields.")) {
-                    continue;
-                }
-                const fKey = key.substring(7);
-                const fn = () => {
-                    this[UpdatingFromServer] = true;
-                    delete this[fKey];
-                    this[UpdatingFromServer] = false;
-                };
-                if (sameAuthor || DocServer.getFieldWriteMode(fKey) !== DocServer.WriteMode.Playground) {
-                    delete this[CachedUpdates][fKey];
-                    await fn();
-                } else {
-                    this[CachedUpdates][fKey] = fn;
-                }
-            }
-        }
-    }
-}
-
-export namespace Doc {
-    // export function GetAsync(doc: Doc, key: string, ignoreProto: boolean = false): Promise<Field | undefined> {
-    //     const self = doc[Self];
-    //     return new Promise(res => getField(self, key, ignoreProto, res));
-    // }
-    // export function GetTAsync<T extends Field>(doc: Doc, key: string, ctor: ToConstructor<T>, ignoreProto: boolean = false): Promise<T | undefined> {
-    //     return new Promise(async res => {
-    //         const field = await GetAsync(doc, key, ignoreProto);
-    //         return Cast(field, ctor);
-    //     });
-    // }
-
-    export function RunCachedUpdate(doc: Doc, field: string) {
-        const update = doc[CachedUpdates][field];
-        if (update) {
-            update();
-            delete doc[CachedUpdates][field];
-        }
-    }
-    export function AddCachedUpdate(doc: Doc, field: string, oldValue: any) {
-        const val = oldValue;
-        doc[CachedUpdates][field] = () => {
-            doc[UpdatingFromServer] = true;
-            doc[field] = val;
-            doc[UpdatingFromServer] = false;
-        };
-    }
-    export function MakeReadOnly(): { end(): void } {
-        makeReadOnly();
-        return {
-            end() {
-                makeEditable();
-            }
-        };
-    }
-
-    export function Get(doc: Doc, key: string, ignoreProto: boolean = false): FieldResult {
-        try {
-            return getField(doc[Self], key, ignoreProto);
-        } catch  {
-            return doc;
-        }
-    }
-    export function GetT<T extends Field>(doc: Doc, key: string, ctor: ToConstructor<T>, ignoreProto: boolean = false): FieldResult<T> {
-        return Cast(Get(doc, key, ignoreProto), ctor) as FieldResult<T>;
-    }
-    export function IsPrototype(doc: Doc) {
-        return GetT(doc, "isPrototype", "boolean", true);
-    }
-    export function IsBaseProto(doc: Doc) {
-        return GetT(doc, "baseProto", "boolean", true);
-    }
-    export async function SetInPlace(doc: Doc, key: string, value: Field | undefined, defaultProto: boolean) {
-        const hasProto = doc.proto instanceof Doc;
-        const onDeleg = Object.getOwnPropertyNames(doc).indexOf(key) !== -1;
-        const onProto = hasProto && Object.getOwnPropertyNames(doc.proto).indexOf(key) !== -1;
-        if (onDeleg || !hasProto || (!onProto && !defaultProto)) {
-            doc[key] = value;
-        } else doc.proto![key] = value;
-    }
-    export async function SetOnPrototype(doc: Doc, key: string, value: Field) {
-        const proto = Object.getOwnPropertyNames(doc).indexOf("isPrototype") === -1 ? doc.proto : doc;
-
-        if (proto) {
-            proto[key] = value;
-        }
-    }
-    export function GetAllPrototypes(doc: Doc): Doc[] {
-        const protos: Doc[] = [];
-        let d: Opt<Doc> = doc;
-        while (d) {
-            protos.push(d);
-            d = FieldValue(d.proto);
-        }
-        return protos;
-    }
-
-    /**
-     * This function is intended to model Object.assign({}, {}) [https://mzl.la/1Mo3l21], which copies
-     * the values of the properties of a source object into the target.
-     * 
-     * This is just a specific, Dash-authored version that serves the same role for our
-     * Doc class.
-     * 
-     * @param doc the target document into which you'd like to insert the new fields 
-     * @param fields the fields to project onto the target. Its type signature defines a mapping from some string key
-     * to a potentially undefined field, where each entry in this mapping is optional. 
-     */
-    export function assign<K extends string>(doc: Doc, fields: Partial<Record<K, Opt<Field>>>, skipUndefineds: boolean = false) {
-        for (const key in fields) {
-            if (fields.hasOwnProperty(key)) {
-                const value = fields[key];
-                if (!skipUndefineds || value !== undefined) { // Do we want to filter out undefineds?
-                    doc[key] = value;
-                }
-            }
-        }
-        return doc;
-    }
-
-    // compare whether documents or their protos match
-    export function AreProtosEqual(doc?: Doc, other?: Doc) {
-        if (!doc || !other) return false;
-        const r = (doc === other);
-        const r2 = (Doc.GetProto(doc) === other);
-        const r3 = (Doc.GetProto(other) === doc);
-        const r4 = (Doc.GetProto(doc) === Doc.GetProto(other) && Doc.GetProto(other) !== undefined);
-        return r || r2 || r3 || r4;
-    }
-
-    // Gets the data document for the document.  Note: this is mis-named -- it does not specifically
-    // return the doc's proto, but rather recursively searches through the proto inheritance chain 
-    // and returns the document who's proto is undefined or whose proto is marked as a base prototype ('isPrototype').
-    export function GetProto(doc: Doc): Doc {
-        if (doc instanceof Promise) {
-            console.log("GetProto: error: got Promise insead of Doc");
-        }
-        const proto = doc && (Doc.GetT(doc, "isPrototype", "boolean", true) ? doc : (doc.proto || doc));
-        return proto === doc ? proto : Doc.GetProto(proto);
-    }
-    export function GetDataDoc(doc: Doc): Doc {
-        const proto = Doc.GetProto(doc);
-        return proto === doc ? proto : Doc.GetDataDoc(proto);
-    }
-
-    export function allKeys(doc: Doc): string[] {
-        const results: Set<string> = new Set;
-
-        let proto: Doc | undefined = doc;
-        while (proto) {
-            Object.keys(proto).forEach(key => results.add(key));
-            proto = proto.proto;
-        }
-
-        return Array.from(results);
-    }
-
-    export function IndexOf(toFind: Doc, list: Doc[], allowProtos: boolean = true) {
-        let index = list.reduce((p, v, i) => (v instanceof Doc && v === toFind) ? i : p, -1);
-        index = allowProtos && index !== -1 ? index : list.reduce((p, v, i) => (v instanceof Doc && Doc.AreProtosEqual(v, toFind)) ? i : p, -1);
-        return index; // list.findIndex(doc => doc === toFind || Doc.AreProtosEqual(doc, toFind));
-    }
-    export function RemoveDocFromList(listDoc: Doc, fieldKey: string | undefined, doc: Doc) {
-        const key = fieldKey ? fieldKey : Doc.LayoutFieldKey(listDoc);
-        if (listDoc[key] === undefined) {
-            Doc.GetProto(listDoc)[key] = new List<Doc>();
-        }
-        const list = Cast(listDoc[key], listSpec(Doc));
-        if (list) {
-            const ind = list.indexOf(doc);
-            if (ind !== -1) {
-                list.splice(ind, 1);
-                return true;
-            }
-        }
-        return false;
-    }
-    export function AddDocToList(listDoc: Doc, fieldKey: string | undefined, doc: Doc, relativeTo?: Doc, before?: boolean, first?: boolean, allowDuplicates?: boolean, reversed?: boolean) {
-        const key = fieldKey ? fieldKey : Doc.LayoutFieldKey(listDoc);
-        if (listDoc[key] === undefined) {
-            Doc.GetProto(listDoc)[key] = new List<Doc>();
-        }
-        const list = Cast(listDoc[key], listSpec(Doc));
-        if (list) {
-            if (allowDuplicates !== true) {
-                const pind = list.reduce((l, d, i) => d instanceof Doc && d[Id] === doc[Id] ? i : l, -1);
-                if (pind !== -1) {
-                    list.splice(pind, 1);
-                }
-            }
-            if (first) {
-                list.splice(0, 0, doc);
-            }
-            else {
-                const ind = relativeTo ? list.indexOf(relativeTo) : -1;
-                if (ind === -1) {
-                    if (reversed) list.splice(0, 0, doc);
-                    else list.push(doc);
-                }
-                else {
-                    if (reversed) list.splice(before ? (list.length - ind) + 1 : list.length - ind, 0, doc);
-                    else list.splice(before ? ind : ind + 1, 0, doc);
-                }
-            }
-            return true;
-        }
-        return false;
-    }
-
-    //
-    // Computes the bounds of the contents of a set of documents.
-    //
-    export function ComputeContentBounds(docList: Doc[]) {
-        const bounds = docList.reduce((bounds, doc) => {
-            const [sptX, sptY] = [NumCast(doc.x), NumCast(doc.y)];
-            const [bptX, bptY] = [sptX + doc[WidthSym](), sptY + doc[HeightSym]()];
-            return {
-                x: Math.min(sptX, bounds.x), y: Math.min(sptY, bounds.y),
-                r: Math.max(bptX, bounds.r), b: Math.max(bptY, bounds.b)
-            };
-        }, { x: Number.MAX_VALUE, y: Number.MAX_VALUE, r: -Number.MAX_VALUE, b: -Number.MAX_VALUE });
-        return bounds;
-    }
-
-    export function MakeAlias(doc: Doc, id?: string) {
-        const alias = !GetT(doc, "isPrototype", "boolean", true) ? Doc.MakeCopy(doc, undefined, id) : Doc.MakeDelegate(doc, id);
-        const layout = Doc.LayoutField(alias);
-        if (layout instanceof Doc && layout !== alias && layout === Doc.Layout(alias)) {
-            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})`);
-        return alias;
-    }
-
-    //
-    // Determines whether the layout needs to be expanded (as a template).
-    // template expansion is rquired when the layout is a template doc/field and there's a datadoc which isn't equal to the layout template
-    //
-    export function WillExpandTemplateLayout(layoutDoc: Doc, dataDoc?: Doc) {
-        return (layoutDoc.isTemplateForField || layoutDoc.isTemplateDoc) && dataDoc && layoutDoc !== dataDoc;
-    }
-
-    const _pendingMap: Map<string, boolean> = new Map();
-    //
-    // Returns an expanded template layout for a target data document if there is a template relationship
-    // between the two. If so, the layoutDoc is expanded into a new document that inherits the properties 
-    // of the original layout while allowing for individual layout properties to be overridden in the expanded layout.
-    // templateArgs should be equivalent to the layout key that generates the template since that's where the template parameters are stored in ()'s at the end of the key.
-    // NOTE:  the template will have references to "@params" -- the template arguments will be assigned to the '@params' field
-    // so that when the @params key is accessed, it will be rewritten as the key that is stored in the 'params' field and
-    // the derefence will then occur on the rootDocument (the original document).
-    // in the future, field references could be written as @<someparam> and then arguments would be passed in the layout key as:
-    //   layout_mytemplate(somparam=somearg).   
-    // then any references to @someparam would be rewritten as accesses to 'somearg' on the rootDocument
-    export function expandTemplateLayout(templateLayoutDoc: Doc, targetDoc?: Doc, templateArgs?: string) {
-        const args = templateArgs?.match(/\(([a-zA-Z0-9._\-]*)\)/)?.[1].replace("()", "") || StrCast(templateLayoutDoc.PARAMS);
-        if (!args && !WillExpandTemplateLayout(templateLayoutDoc, targetDoc) || !targetDoc) return templateLayoutDoc;
-
-        const templateField = StrCast(templateLayoutDoc.isTemplateForField);  // the field that the template renders
-        // First it checks if an expanded layout already exists -- if so it will be stored on the dataDoc
-        // using the template layout doc's id as the field key.
-        // If it doesn't find the expanded layout, then it makes a delegate of the template layout and
-        // saves it on the data doc indexed by the template layout's id.
-        //
-        const params = args.split("=").length > 1 ? args.split("=")[0] : "PARAMS";
-        const layoutFielddKey = Doc.LayoutFieldKey(templateLayoutDoc);
-        const expandedLayoutFieldKey = (templateField || layoutFielddKey) + "-layout[" + templateLayoutDoc[Id] + (args ? `(${args})` : "") + "]";
-        let expandedTemplateLayout = targetDoc?.[expandedLayoutFieldKey];
-
-        if (templateLayoutDoc.resolvedDataDoc instanceof Promise) {
-            expandedTemplateLayout = undefined;
-            _pendingMap.set(targetDoc[Id] + expandedLayoutFieldKey, true);
-        }
-        else if (expandedTemplateLayout === undefined && !_pendingMap.get(targetDoc[Id] + expandedLayoutFieldKey + args)) {
-            if (templateLayoutDoc.resolvedDataDoc === (targetDoc.rootDocument || Doc.GetProto(targetDoc)) && templateLayoutDoc.PARAMS === StrCast(targetDoc.PARAMS)) {
-                expandedTemplateLayout = templateLayoutDoc; // reuse an existing template layout if its for the same document with the same params
-            } else {
-                _pendingMap.set(targetDoc[Id] + expandedLayoutFieldKey + args, true);
-                templateLayoutDoc.resolvedDataDoc && (templateLayoutDoc = Cast(templateLayoutDoc.proto, Doc, null) || templateLayoutDoc); // if the template has already been applied (ie, a nested template), then use the template's prototype
-                setTimeout(action(() => {
-                    if (!targetDoc[expandedLayoutFieldKey]) {
-                        const newLayoutDoc = Doc.MakeDelegate(templateLayoutDoc, undefined, "[" + templateLayoutDoc.title + "]");
-                        // the template's arguments are stored in params which is derefenced to find
-                        // the actual field key where the parameterized template data is stored.
-                        newLayoutDoc[params] = args !== "..." ? args : ""; // ... signifies the layout has sub template(s) -- so we have to expand the layout for them so that they can get the correct 'rootDocument' field, but we don't need to reassign their params.  it would be better if the 'rootDocument' field could be passed dynamically to avoid have to create instances
-                        newLayoutDoc.rootDocument = targetDoc;
-                        targetDoc[expandedLayoutFieldKey] = newLayoutDoc;
-                        const dataDoc = Doc.GetProto(targetDoc);
-                        newLayoutDoc.resolvedDataDoc = dataDoc;
-                        if (dataDoc[templateField] === undefined && templateLayoutDoc[templateField] instanceof List) {
-                            dataDoc[templateField] = ComputedField.MakeFunction(`ObjectField.MakeCopy(templateLayoutDoc["${templateField}"] as List)`, { templateLayoutDoc: Doc.name }, { templateLayoutDoc });
-                        }
-                        _pendingMap.delete(targetDoc[Id] + expandedLayoutFieldKey + args);
-                    }
-                }), 0);
-            }
-        }
-        return expandedTemplateLayout instanceof Doc ? expandedTemplateLayout : undefined; // layout is undefined if the expandedTemplateLayout is pending.
-    }
-
-    // if the childDoc is a template for a field, then this will return the expanded layout with its data doc.
-    // otherwise, it just returns the childDoc
-    export function GetLayoutDataDocPair(containerDoc: Doc, containerDataDoc: Opt<Doc>, childDoc: Doc) {
-        if (!childDoc || childDoc instanceof Promise || !Doc.GetProto(childDoc)) {
-            console.log("No, no, no!");
-            return { layout: childDoc, data: childDoc };
-        }
-        const resolvedDataDoc = (Doc.AreProtosEqual(containerDataDoc, containerDoc) || (!childDoc.isTemplateDoc && !childDoc.isTemplateForField && !childDoc.PARAMS) ? undefined : containerDataDoc);
-        return { layout: Doc.expandTemplateLayout(childDoc, resolvedDataDoc, "(" + StrCast(containerDoc.PARAMS) + ")"), data: resolvedDataDoc };
-    }
-
-    export function Overwrite(doc: Doc, overwrite: Doc, copyProto: boolean = false): Doc {
-        Object.keys(doc).forEach(key => {
-            const field = ProxyField.WithoutProxy(() => doc[key]);
-            if (key === "proto" && copyProto) {
-                if (doc.proto instanceof Doc && overwrite.proto instanceof Doc) {
-                    overwrite[key] = Doc.Overwrite(doc[key]!, overwrite.proto);
-                }
-            } else {
-                if (field instanceof RefField) {
-                    overwrite[key] = field;
-                } else if (field instanceof ObjectField) {
-                    overwrite[key] = ObjectField.MakeCopy(field);
-                } else if (field instanceof Promise) {
-                    debugger; //This shouldn't happend...
-                } else {
-                    overwrite[key] = field;
-                }
-            }
-        });
-
-        return overwrite;
-    }
-
-    export function MakeCopy(doc: Doc, copyProto: boolean = false, copyProtoId?: string): Doc {
-        const copy = new Doc(copyProtoId, true);
-        const exclude = Cast(doc.excludeFields, listSpec("string"), []);
-        Object.keys(doc).forEach(key => {
-            if (exclude.includes(key)) return;
-            const cfield = ComputedField.WithoutComputed(() => FieldValue(doc[key]));
-            const field = ProxyField.WithoutProxy(() => doc[key]);
-            if (key === "proto" && copyProto) {
-                if (doc[key] instanceof Doc) {
-                    copy[key] = Doc.MakeCopy(doc[key]!, false);
-                }
-            } else {
-                if (field instanceof RefField) {
-                    copy[key] = field;
-                } else if (cfield instanceof ComputedField) {
-                    copy[key] = ComputedField.MakeFunction(cfield.script.originalScript);
-                } else if (field instanceof ObjectField) {
-                    copy[key] = doc[key] instanceof Doc ?
-                        key.includes("layout[") ? Doc.MakeCopy(doc[key] as Doc, false) : doc[key] : // reference documents except copy documents that are expanded teplate fields 
-                        ObjectField.MakeCopy(field);
-                } else if (field instanceof Promise) {
-                    debugger; //This shouldn't happend...
-                } else {
-                    copy[key] = field;
-                }
-            }
-        });
-
-        return copy;
-    }
-
-    export function MakeClone(doc: Doc): Doc {
-        const cloneMap = new Map<string, Doc>();
-        const rtfMap: { copy: Doc, key: string, field: RichTextField }[] = [];
-        const copy = Doc.makeClone(doc, cloneMap, rtfMap);
-        rtfMap.map(({ copy, key, field }) => {
-            const replacer = (match: any, attr: string, id: string, offset: any, string: any) => {
-                const mapped = cloneMap.get(id);
-                return attr + "\"" + (mapped ? mapped[Id] : id) + "\"";
-            };
-            const replacer2 = (match: any, href: string, id: string, offset: any, string: any) => {
-                const mapped = cloneMap.get(id);
-                return href + (mapped ? mapped[Id] : id);
-            };
-            const regex = `(${Utils.prepend("/doc/")})([^"]*)`;
-            const re = new RegExp(regex, "g");
-            copy[key] = new RichTextField(field.Data.replace(/("docid":|"targetId":|"linkId":)"([^"]+)"/g, replacer).replace(re, replacer2), field.Text);
-        });
-        return copy;
-    }
-
-    export function makeClone(doc: Doc, cloneMap: Map<string, Doc>, rtfs: { copy: Doc, key: string, field: RichTextField }[]): Doc {
-        if (Doc.IsBaseProto(doc)) return doc;
-        if (cloneMap.get(doc[Id])) return cloneMap.get(doc[Id])!;
-        const copy = new Doc(undefined, true);
-        cloneMap.set(doc[Id], copy);
-        if (LinkManager.Instance.getAllLinks().includes(doc) && LinkManager.Instance.getAllLinks().indexOf(copy) === -1) LinkManager.Instance.addLink(copy);
-        const exclude = Cast(doc.excludeFields, listSpec("string"), []);
-        Object.keys(doc).forEach(key => {
-            if (exclude.includes(key)) return;
-            const cfield = ComputedField.WithoutComputed(() => FieldValue(doc[key]));
-            const field = ProxyField.WithoutProxy(() => doc[key]);
-            const copyObjectField = (field: ObjectField) => {
-                const list = Cast(doc[key], listSpec(Doc));
-                if (list !== undefined && !(list instanceof Promise)) {
-                    copy[key] = new List<Doc>(list.filter(d => d instanceof Doc).map(d => Doc.makeClone(d as Doc, cloneMap, rtfs)));
-                } else if (doc[key] instanceof Doc) {
-                    copy[key] = key.includes("layout[") ? undefined : Doc.makeClone(doc[key] as Doc, cloneMap, rtfs); // reference documents except copy documents that are expanded teplate fields 
-                } else {
-                    copy[key] = ObjectField.MakeCopy(field);
-                    if (field instanceof RichTextField) {
-                        if (field.Data.includes('"docid":') || field.Data.includes('"targetId":') || field.Data.includes('"linkId":')) {
-                            rtfs.push({ copy, key, field });
-                        }
-                    }
-                }
-            };
-            if (key === "proto") {
-                if (doc[key] instanceof Doc) {
-                    copy[key] = Doc.makeClone(doc[key]!, cloneMap, rtfs);
-                }
-            } else {
-                if (field instanceof RefField) {
-                    copy[key] = field;
-                } else if (cfield instanceof ComputedField) {
-                    copy[key] = ComputedField.MakeFunction(cfield.script.originalScript);
-                    (key === "links" && field instanceof ObjectField) && copyObjectField(field);
-                } else if (field instanceof ObjectField) {
-                    copyObjectField(field);
-                } else if (field instanceof Promise) {
-                    debugger; //This shouldn't happend...
-                } else {
-                    copy[key] = field;
-                }
-            }
-        });
-        Doc.SetInPlace(copy, "title", "CLONE: " + doc.title, true);
-        copy.cloneOf = doc;
-        cloneMap.set(doc[Id], copy);
-        return copy;
-    }
-
-    export function MakeDelegate(doc: Doc, id?: string, title?: string): Doc;
-    export function MakeDelegate(doc: Opt<Doc>, id?: string, title?: string): Opt<Doc>;
-    export function MakeDelegate(doc: Opt<Doc>, id?: string, title?: string): Opt<Doc> {
-        if (doc) {
-            const delegate = new Doc(id, true);
-            delegate.proto = doc;
-            title && (delegate.title = title);
-            return delegate;
-        }
-        return undefined;
-    }
-
-    let _applyCount: number = 0;
-    export function ApplyTemplate(templateDoc: Doc) {
-        if (templateDoc) {
-            const target = Doc.MakeDelegate(new Doc());
-            const targetKey = StrCast(templateDoc.layoutKey, "layout");
-            const applied = ApplyTemplateTo(templateDoc, target, targetKey, templateDoc.title + "(..." + _applyCount++ + ")");
-            target.layoutKey = targetKey;
-            applied && (Doc.GetProto(applied).type = templateDoc.type);
-            return applied;
-        }
-        return undefined;
-    }
-    export function ApplyTemplateTo(templateDoc: Doc, target: Doc, targetKey: string, titleTarget: string | undefined) {
-        if (!Doc.AreProtosEqual(target[targetKey] as Doc, templateDoc)) {
-            if (target.resolvedDataDoc) {
-                target[targetKey] = new PrefetchProxy(templateDoc);
-            } else {
-                titleTarget && (Doc.GetProto(target).title = titleTarget);
-                Doc.GetProto(target)[targetKey] = new PrefetchProxy(templateDoc);
-            }
-        }
-        return target;
-    }
-
-    //
-    //  This function converts a generic field layout display into a field layout that displays a specific
-    // metadata field indicated by the title of the template field (not the default field that it was rendering)
-    //
-    export function MakeMetadataFieldTemplate(templateField: Doc, templateDoc: Opt<Doc>): boolean {
-
-        // find the metadata field key that this template field doc will display (indicated by its title)
-        const metadataFieldKey = StrCast(templateField.isTemplateForField) || StrCast(templateField.title).replace(/^-/, "");
-
-        // update the original template to mark it as a template
-        templateField.isTemplateForField = metadataFieldKey;
-        templateField.title = metadataFieldKey;
-
-        const templateFieldValue = templateField[metadataFieldKey] || templateField[Doc.LayoutFieldKey(templateField)];
-        const templateCaptionValue = templateField.caption;
-        // move any data that the template field had been rendering over to the template doc so that things will still be rendered
-        // when the template field is adjusted to point to the new metadatafield key.
-        // note 1: if the template field contained a list of documents, each of those documents will be converted to templates as well.
-        // note 2: this will not overwrite any field that already exists on the template doc at the field key
-        if (!templateDoc?.[metadataFieldKey] && templateFieldValue instanceof ObjectField) {
-            Cast(templateFieldValue, listSpec(Doc), [])?.map(d => d instanceof Doc && MakeMetadataFieldTemplate(d, templateDoc));
-            (Doc.GetProto(templateField)[metadataFieldKey] = ObjectField.MakeCopy(templateFieldValue));
-        }
-        // copy the textTemplates from 'this' (not 'self') because the layout contains the template info, not the original doc
-        if (templateCaptionValue instanceof RichTextField && !templateCaptionValue.Empty()) {
-            templateField["caption-textTemplate"] = ComputedField.MakeFunction(`copyField(this.caption)`);
-        }
-        if (templateFieldValue instanceof RichTextField && !templateFieldValue.Empty()) {
-            templateField[metadataFieldKey + "-textTemplate"] = ComputedField.MakeFunction(`copyField(this.${metadataFieldKey})`);
-        }
-
-        // get the layout string that the template uses to specify its layout
-        const templateFieldLayoutString = StrCast(Doc.LayoutField(Doc.Layout(templateField)));
-
-        // change it to render the target metadata field instead of what it was rendering before and assign it to the template field layout document.
-        Doc.Layout(templateField).layout = templateFieldLayoutString.replace(/fieldKey={'[^']*'}/, `fieldKey={'${metadataFieldKey}'}`);
-
-        // assign the template field doc a delegate of any extension document that was previously used to render the template field (since extension doc's carry rendering informatino)
-        Doc.Layout(templateField)[metadataFieldKey + "_ext"] = Doc.MakeDelegate(templateField[templateFieldLayoutString?.split("'")[1] + "_ext"] as Doc);
-
-        return true;
-    }
-
-    export function overlapping(doc1: Doc, doc2: Doc, clusterDistance: number) {
-        const doc2Layout = Doc.Layout(doc2);
-        const doc1Layout = Doc.Layout(doc1);
-        const x2 = NumCast(doc2.x) - clusterDistance;
-        const y2 = NumCast(doc2.y) - clusterDistance;
-        const w2 = NumCast(doc2Layout._width) + clusterDistance;
-        const h2 = NumCast(doc2Layout._height) + clusterDistance;
-        const x = NumCast(doc1.x) - clusterDistance;
-        const y = NumCast(doc1.y) - clusterDistance;
-        const w = NumCast(doc1Layout._width) + clusterDistance;
-        const h = NumCast(doc1Layout._height) + clusterDistance;
-        return doc1.z === doc2.z && intersectRect({ left: x, top: y, width: w, height: h }, { left: x2, top: y2, width: w2, height: h2 });
-    }
-
-    export function isBrushedHighlightedDegree(doc: Doc) {
-        if (Doc.IsHighlighted(doc)) {
-            return 6;
-        }
-        else {
-            return Doc.IsBrushedDegree(doc);
-        }
-    }
-
-    export class DocBrush {
-        BrushedDoc: ObservableMap<Doc, boolean> = new ObservableMap();
-    }
-    const brushManager = new DocBrush();
-
-    export class DocData {
-        @observable _user_doc: Doc = undefined!;
-        @observable _searchQuery: string = "";
-    }
-
-    // the document containing the view layout information - will be the Document itself unless the Document has
-    // a layout field or 'layout' is given.  
-    export function Layout(doc: Doc, layout?: Doc): Doc {
-        const overrideLayout = layout && Cast(doc[`${StrCast(layout.isTemplateForField, "data")}-layout[` + layout[Id] + "]"], Doc, null);
-        return overrideLayout || doc[LayoutSym] || doc;
-    }
-    export function SetLayout(doc: Doc, layout: Doc | string) { doc[StrCast(doc.layoutKey, "layout")] = layout; }
-    export function LayoutField(doc: Doc) { return doc[StrCast(doc.layoutKey, "layout")]; }
-    export function LayoutFieldKey(doc: Doc): string { return StrCast(Doc.Layout(doc).layout).split("'")[1]; }
-    const manager = new DocData();
-    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 SetUserDoc(doc: Doc) { manager._user_doc = doc; }
-    export function IsBrushed(doc: Doc) {
-        return computedFn(function IsBrushed(doc: Doc) {
-            return brushManager.BrushedDoc.has(doc) || brushManager.BrushedDoc.has(Doc.GetProto(doc));
-        })(doc);
-    }
-    // don't bother memoizing (caching) the result if called from a non-reactive context. (plus this avoids a warning message)
-    export function IsBrushedDegreeUnmemoized(doc: Doc) {
-        return brushManager.BrushedDoc.has(doc) ? 2 : brushManager.BrushedDoc.has(Doc.GetProto(doc)) ? 1 : 0;
-    }
-    export function IsBrushedDegree(doc: Doc) {
-        return computedFn(function IsBrushDegree(doc: Doc) {
-            return Doc.IsBrushedDegreeUnmemoized(doc);
-        })(doc);
-    }
-    export function BrushDoc(doc: Doc) {
-        brushManager.BrushedDoc.set(doc, true);
-        brushManager.BrushedDoc.set(Doc.GetProto(doc), true);
-        return doc;
-    }
-    export function UnBrushDoc(doc: Doc) {
-        brushManager.BrushedDoc.delete(doc);
-        brushManager.BrushedDoc.delete(Doc.GetProto(doc));
-        return doc;
-    }
-
-
-    export function LinkOtherAnchor(linkDoc: Doc, anchorDoc: Doc) { return Doc.AreProtosEqual(anchorDoc, Cast(linkDoc.anchor1, Doc) as Doc) ? Cast(linkDoc.anchor2, Doc) as Doc : Cast(linkDoc.anchor1, Doc) as Doc; }
-    export function LinkEndpoint(linkDoc: Doc, anchorDoc: Doc) { return Doc.AreProtosEqual(anchorDoc, Cast(linkDoc.anchor1, Doc) as Doc) ? "1" : "2"; }
-
-    export function linkFollowUnhighlight() {
-        Doc.UnhighlightAll();
-        document.removeEventListener("pointerdown", linkFollowUnhighlight);
-    }
-
-    let _lastDate = 0;
-    export function linkFollowHighlight(destDoc: Doc, dataAndDisplayDocs = true) {
-        linkFollowUnhighlight();
-        Doc.HighlightDoc(destDoc, dataAndDisplayDocs);
-        document.removeEventListener("pointerdown", linkFollowUnhighlight);
-        document.addEventListener("pointerdown", linkFollowUnhighlight);
-        const lastDate = _lastDate = Date.now();
-        window.setTimeout(() => _lastDate === lastDate && linkFollowUnhighlight(), 5000);
-    }
-
-    export class HighlightBrush {
-        @observable HighlightedDoc: Map<Doc, boolean> = new Map();
-    }
-    const highlightManager = new HighlightBrush();
-    export function IsHighlighted(doc: Doc) {
-        return highlightManager.HighlightedDoc.get(doc) || highlightManager.HighlightedDoc.get(Doc.GetProto(doc));
-    }
-    export function HighlightDoc(doc: Doc, dataAndDisplayDocs = true) {
-        runInAction(() => {
-            highlightManager.HighlightedDoc.set(doc, true);
-            dataAndDisplayDocs && highlightManager.HighlightedDoc.set(Doc.GetProto(doc), true);
-        });
-    }
-    export function UnHighlightDoc(doc: Doc) {
-        runInAction(() => {
-            highlightManager.HighlightedDoc.set(doc, false);
-            highlightManager.HighlightedDoc.set(Doc.GetProto(doc), false);
-        });
-    }
-    export function UnhighlightAll() {
-        const mapEntries = highlightManager.HighlightedDoc.keys();
-        let docEntry: IteratorResult<Doc>;
-        while (!(docEntry = mapEntries.next()).done) {
-            const targetDoc = docEntry.value;
-            targetDoc && Doc.UnHighlightDoc(targetDoc);
-        }
-
-    }
-    export function UnBrushAllDocs() {
-        brushManager.BrushedDoc.clear();
-    }
-
-    export function getDocTemplate(doc?: Doc) {
-        return doc?.isTemplateDoc ? doc :
-            Cast(doc?.dragFactory, Doc, null)?.isTemplateDoc ? doc?.dragFactory :
-                Cast(doc?.layout, Doc, null)?.isTemplateDoc ? doc?.layout : undefined;
-    }
-
-    export function matchFieldValue(doc: Doc, key: string, value: any): boolean {
-        const fieldVal = doc[key];
-        if (Cast(fieldVal, listSpec("string"), []).length) {
-            const vals = Cast(fieldVal, listSpec("string"), []);
-            return vals.some(v => v === value);
-        }
-        const fieldStr = Field.toString(fieldVal as Field);
-        return fieldStr === value;
-    }
-
-    export function deiconifyView(doc: any) {
-        StrCast(doc.layoutKey).split("_")[1] === "icon" && setNativeView(doc);
-    }
-
-    export function setNativeView(doc: any) {
-        const prevLayout = StrCast(doc.layoutKey).split("_")[1];
-        const deiconify = prevLayout === "icon" && StrCast(doc.deiconifyLayout) ? "layout_" + StrCast(doc.deiconifyLayout) : "";
-        prevLayout === "icon" && (doc.deiconifyLayout = undefined);
-        doc.layoutKey = deiconify || "layout";
-    }
-    export function setDocFilterRange(target: Doc, key: string, range?: number[]) {
-        const docRangeFilters = Cast(target._docRangeFilters, listSpec("string"), []);
-        for (let i = 0; i < docRangeFilters.length; i += 3) {
-            if (docRangeFilters[i] === key) {
-                docRangeFilters.splice(i, 3);
-                break;
-            }
-        }
-        if (range !== undefined) {
-            docRangeFilters.push(key);
-            docRangeFilters.push(range[0].toString());
-            docRangeFilters.push(range[1].toString());
-            target._docRangeFilters = new List<string>(docRangeFilters);
-        }
-    }
-
-    export function aliasDocs(field: any) {
-        return new List<Doc>(field.map((d: any) => Doc.MakeAlias(d)));
-    }
-
-    // filters document in a container collection:
-    // all documents with the specified value for the specified key are included/excluded 
-    // based on the modifiers :"check", "x", undefined
-    export function setDocFilter(container: Doc, key: string, value: any, modifiers?: "check" | "x" | undefined) {
-        const docFilters = Cast(container._docFilters, listSpec("string"), []);
-        for (let i = 0; i < docFilters.length; i += 3) {
-            if (docFilters[i] === key && docFilters[i + 1] === value) {
-                docFilters.splice(i, 3);
-                break;
-            }
-        }
-        if (typeof modifiers === "string") {
-            docFilters.push(key);
-            docFilters.push(value);
-            docFilters.push(modifiers);
-            container._docFilters = new List<string>(docFilters);
-        }
-    }
-    export function readDocRangeFilter(doc: Doc, key: string) {
-        const docRangeFilters = Cast(doc._docRangeFilters, listSpec("string"), []);
-        for (let i = 0; i < docRangeFilters.length; i += 3) {
-            if (docRangeFilters[i] === key) {
-                return [Number(docRangeFilters[i + 1]), Number(docRangeFilters[i + 2])];
-            }
-        }
-    }
-    export function assignDocToField(doc: Doc, field: string, id: string) {
-        DocServer.GetRefField(id).then(layout => layout instanceof Doc && (doc[field] = layout));
-        return id;
-    }
-
-    export function toggleNativeDimensions(layoutDoc: Doc, contentScale: number, panelWidth: number, panelHeight: number) {
-        runInAction(() => {
-            if (layoutDoc._nativeWidth || layoutDoc._nativeHeight) {
-                layoutDoc.scale = NumCast(layoutDoc.scale, 1) * contentScale;
-                layoutDoc._nativeWidth = undefined;
-                layoutDoc._nativeHeight = undefined;
-            }
-            else {
-                layoutDoc._autoHeight = false;
-                if (!layoutDoc._nativeWidth) {
-                    layoutDoc._nativeWidth = NumCast(layoutDoc._width, panelWidth);
-                    layoutDoc._nativeHeight = NumCast(layoutDoc._height, panelHeight);
-                }
-            }
-        });
-    }
-
-    export function isDocPinned(doc: Doc) {
-        //add this new doc to props.Document
-        const curPres = Cast(Doc.UserDoc().activePresentation, Doc) as Doc;
-        if (curPres) {
-            return DocListCast(curPres.data).findIndex((val) => Doc.AreProtosEqual(val, doc)) !== -1;
-        }
-        return false;
-    }
-
-    // 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;
-            if (doc[doc.layoutKey] === undefined) {
-                createCustomView(doc, creator, templateSignature, docLayoutTemplate);
-            }
-        });
-        batch.end();
-    }
-    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 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);
-        // 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 && (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.type), 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 };
-
-        let fieldTemplate: Opt<Doc>;
-        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);
-        } else if (doc.data instanceof VideoField) {
-            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);
-        } else if (doc.data instanceof ImageField) {
-            fieldTemplate = Docs.Create.ImageDocument("http://www.cs.brown.edu", options);
-        }
-        const docTemplate = docLayoutTemplate || 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);
-    }
-    export function makeCustomView(doc: Doc, custom: boolean, layout: string) {
-        Doc.setNativeView(doc);
-        if (custom) {
-            makeCustomViewClicked(doc, Docs.Create.StackingDocument, layout, undefined);
-        }
-    }
-    export function iconify(doc: Doc) {
-        const layoutKey = Cast(doc.layoutKey, "string", null);
-        Doc.makeCustomViewClicked(doc, Docs.Create.StackingDocument, "icon", undefined);
-        if (layoutKey && layoutKey !== "layout" && layoutKey !== "layout_icon") doc.deiconifyLayout = layoutKey.replace("layout_", "");
-    }
-
-    export function pileup(selected: Doc[], x: number, y: number) {
-        const newCollection = Docs.Create.PileDocument(selected, { title: "pileup", x: x - 55, y: y - 55, _width: 110, _height: 100, _LODdisable: true });
-        let w = 0, h = 0;
-        selected.forEach((d, i) => {
-            Doc.iconify(d);
-            w = Math.max(d[WidthSym](), w);
-            h = Math.max(d[HeightSym](), 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
-        selected.forEach((d, i) => {
-            d.x = Math.cos(Math.PI * 2 * i / selected.length) * 10 - w / 2;
-            d.y = Math.sin(Math.PI * 2 * i / selected.length) * 10 - h / 2;
-            d.displayTimecode = undefined;  // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
-        });
-        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;
-        //newCollection.borderRounding = "40px";
-        newCollection._jitterRotation = 10;
-        newCollection._backgroundColor = "gray";
-        newCollection._overflow = "visible";
-        return newCollection;
-    }
-
-
-    export async function addFieldEnumerations(doc: Opt<Doc>, enumeratedFieldKey: string, enumerations: { title: string, _backgroundColor?: string, color?: string }[]) {
-        let optionsCollection = await DocServer.GetRefField(enumeratedFieldKey);
-        if (!(optionsCollection instanceof Doc)) {
-            optionsCollection = Docs.Create.StackingDocument([], { title: `${enumeratedFieldKey} field set` }, enumeratedFieldKey);
-            Doc.AddDocToList((Doc.UserDoc().fieldTypes as Doc), "data", optionsCollection as Doc);
-        }
-        const options = optionsCollection as Doc;
-        const targetDoc = doc && Doc.GetProto(Cast(doc.rootDocument, Doc, null) || doc);
-        const docFind = `options.data.find(doc => doc.title === (this.rootDocument||this)["${enumeratedFieldKey}"])?`;
-        targetDoc && (targetDoc.backgroundColor = ComputedField.MakeFunction(docFind + `._backgroundColor || "white"`, undefined, { options }));
-        targetDoc && (targetDoc.color = ComputedField.MakeFunction(docFind + `.color || "black"`, undefined, { options }));
-        targetDoc && (targetDoc.borderRounding = ComputedField.MakeFunction(docFind + `.borderRounding`, undefined, { options }));
-        enumerations.map(enumeration => {
-            const found = DocListCast(options.data).find(d => d.title === enumeration.title);
-            if (found) {
-                found._backgroundColor = enumeration._backgroundColor || found._backgroundColor;
-                found._color = enumeration.color || found._color;
-            } else {
-                Doc.AddDocToList(options, "data", Docs.Create.TextDocument(enumeration.title, enumeration));
-            }
-        });
-        return optionsCollection;
-    }
-}
-
-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 copyField(field: any) { return ObjectField.MakeCopy(field); });
-Scripting.addGlobal(function aliasDocs(field: any) { return Doc.aliasDocs(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() { return UndoManager.Undo(); });
-Scripting.addGlobal(function redo() { 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() {
-    const curPres = Doc.UserDoc().activePresentation as Doc;
-    return curPres && DocListCast(curPres[Doc.LayoutFieldKey(curPres)])[NumCast(curPres._itemIndex)];
-});
-Scripting.addGlobal(function selectDoc(doc: any) { Doc.UserDoc().activeSelection = new List([doc]); });
-Scripting.addGlobal(function selectedDocs(container: Doc, excludeCollections: boolean, prevValue: any) {
-    const docs = DocListCast(Doc.UserDoc().activeSelection).
-        filter(d => !Doc.AreProtosEqual(d, container) && !d.annotationOn && d.type !== DocumentType.DOCHOLDER && 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?: "check" | "x" | undefined) { Doc.setDocFilter(container, key, value, modifiers); });
-Scripting.addGlobal(function setDocFilterRange(container: Doc, key: string, range: number[]) { Doc.setDocFilterRange(container, key, range); });
\ No newline at end of file
diff --git a/src/new_fields/FieldSymbols.ts b/src/new_fields/FieldSymbols.ts
deleted file mode 100644
index 8d040f493..000000000
--- a/src/new_fields/FieldSymbols.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-
-export const Update = Symbol("Update");
-export const Self = Symbol("Self");
-export const SelfProxy = Symbol("SelfProxy");
-export const HandleUpdate = Symbol("HandleUpdate");
-export const Id = Symbol("Id");
-export const OnUpdate = Symbol("OnUpdate");
-export const Parent = Symbol("Parent");
-export const Copy = Symbol("Copy");
-export const ToScriptString = Symbol("ToScriptString");
-export const ToPlainText = Symbol("ToPlainText");
-export const ToString = Symbol("ToString");
diff --git a/src/new_fields/HtmlField.ts b/src/new_fields/HtmlField.ts
deleted file mode 100644
index 6e8bba977..000000000
--- a/src/new_fields/HtmlField.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-import { Deserializable } from "../client/util/SerializationHelper";
-import { serializable, primitive } from "serializr";
-import { ObjectField } from "./ObjectField";
-import { Copy, ToScriptString, ToString} from "./FieldSymbols";
-
-@Deserializable("html")
-export class HtmlField extends ObjectField {
-    @serializable(primitive())
-    readonly html: string;
-
-    constructor(html: string) {
-        super();
-        this.html = html;
-    }
-
-    [Copy]() {
-        return new HtmlField(this.html);
-    }
-
-    [ToScriptString]() {
-        return "invalid";
-    }
-    [ToString]() {
-        return this.html;
-    }
-}
diff --git a/src/new_fields/IconField.ts b/src/new_fields/IconField.ts
deleted file mode 100644
index 76c4ddf1b..000000000
--- a/src/new_fields/IconField.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-import { Deserializable } from "../client/util/SerializationHelper";
-import { serializable, primitive } from "serializr";
-import { ObjectField } from "./ObjectField";
-import { Copy, ToScriptString, ToString } from "./FieldSymbols";
-
-@Deserializable("icon")
-export class IconField extends ObjectField {
-    @serializable(primitive())
-    readonly icon: string;
-
-    constructor(icon: string) {
-        super();
-        this.icon = icon;
-    }
-
-    [Copy]() {
-        return new IconField(this.icon);
-    }
-
-    [ToScriptString]() {
-        return "invalid";
-    }
-    [ToString]() {
-        return "ICONfield";
-    }
-}
diff --git a/src/new_fields/InkField.ts b/src/new_fields/InkField.ts
deleted file mode 100644
index bb93de5ac..000000000
--- a/src/new_fields/InkField.ts
+++ /dev/null
@@ -1,50 +0,0 @@
-import { Deserializable } from "../client/util/SerializationHelper";
-import { serializable, custom, createSimpleSchema, list, object, map } from "serializr";
-import { ObjectField } from "./ObjectField";
-import { Copy, ToScriptString, ToString } from "./FieldSymbols";
-
-export enum InkTool {
-    None,
-    Pen,
-    Highlighter,
-    Eraser,
-    Stamp
-}
-
-export interface PointData {
-    X: number;
-    Y: number;
-}
-
-export type InkData = Array<PointData>;
-
-const pointSchema = createSimpleSchema({
-    X: true, Y: true
-});
-
-const strokeDataSchema = createSimpleSchema({
-    pathData: list(object(pointSchema)),
-    "*": true
-});
-
-@Deserializable("ink")
-export class InkField extends ObjectField {
-    @serializable(list(object(strokeDataSchema)))
-    readonly inkData: InkData;
-
-    constructor(data: InkData) {
-        super();
-        this.inkData = data;
-    }
-
-    [Copy]() {
-        return new InkField(this.inkData);
-    }
-
-    [ToScriptString]() {
-        return "invalid";
-    }
-    [ToString]() {
-        return "InkField";
-    }
-}
diff --git a/src/new_fields/List.ts b/src/new_fields/List.ts
deleted file mode 100644
index fdabea365..000000000
--- a/src/new_fields/List.ts
+++ /dev/null
@@ -1,330 +0,0 @@
-import { Deserializable, autoObject, afterDocDeserialize } from "../client/util/SerializationHelper";
-import { Field } from "./Doc";
-import { setter, getter, deleteProperty, updateFunction } from "./util";
-import { serializable, alias, list } from "serializr";
-import { observable, action, runInAction } from "mobx";
-import { ObjectField } from "./ObjectField";
-import { RefField } from "./RefField";
-import { ProxyField } from "./Proxy";
-import { Self, Update, Parent, OnUpdate, SelfProxy, ToScriptString, ToString, Copy, Id } from "./FieldSymbols";
-import { Scripting } from "../client/util/Scripting";
-import { DocServer } from "../client/DocServer";
-
-const listHandlers: any = {
-    /// Mutator methods
-    copyWithin() {
-        throw new Error("copyWithin not supported yet");
-    },
-    fill(value: any, start?: number, end?: number) {
-        if (value instanceof RefField) {
-            throw new Error("fill with RefFields not supported yet");
-        }
-        const res = this[Self].__fields.fill(value, start, end);
-        this[Update]();
-        return res;
-    },
-    pop(): any {
-        const field = toRealField(this[Self].__fields.pop());
-        this[Update]();
-        return field;
-    },
-    push: action(function (this: any, ...items: any[]) {
-        items = items.map(toObjectField);
-        const list = this[Self];
-        const length = list.__fields.length;
-        for (let i = 0; i < items.length; i++) {
-            const item = items[i];
-            //TODO Error checking to make sure parent doesn't already exist
-            if (item instanceof ObjectField) {
-                item[Parent] = list;
-                item[OnUpdate] = updateFunction(list, i + length, item, this);
-            }
-        }
-        const res = list.__fields.push(...items);
-        this[Update]();
-        return res;
-    }),
-    reverse() {
-        const res = this[Self].__fields.reverse();
-        this[Update]();
-        return res;
-    },
-    shift() {
-        const res = toRealField(this[Self].__fields.shift());
-        this[Update]();
-        return res;
-    },
-    sort(cmpFunc: any) {
-        this[Self].__realFields(); // coerce retrieving entire array
-        const res = this[Self].__fields.sort(cmpFunc ? (first: any, second: any) => cmpFunc(toRealField(first), toRealField(second)) : undefined);
-        this[Update]();
-        return res;
-    },
-    splice: action(function (this: any, start: number, deleteCount: number, ...items: any[]) {
-        this[Self].__realFields(); // coerce retrieving entire array
-        items = items.map(toObjectField);
-        const list = this[Self];
-        for (let i = 0; i < items.length; i++) {
-            const item = items[i];
-            //TODO Error checking to make sure parent doesn't already exist
-            //TODO Need to change indices of other fields in array
-            if (item instanceof ObjectField) {
-                item[Parent] = list;
-                item[OnUpdate] = updateFunction(list, i + start, item, this);
-            }
-        }
-        const res = list.__fields.splice(start, deleteCount, ...items);
-        this[Update]();
-        return res.map(toRealField);
-    }),
-    unshift(...items: any[]) {
-        items = items.map(toObjectField);
-        const list = this[Self];
-        const length = list.__fields.length;
-        for (let i = 0; i < items.length; i++) {
-            const item = items[i];
-            //TODO Error checking to make sure parent doesn't already exist
-            //TODO Need to change indices of other fields in array
-            if (item instanceof ObjectField) {
-                item[Parent] = list;
-                item[OnUpdate] = updateFunction(list, i, item, this);
-            }
-        }
-        const res = this[Self].__fields.unshift(...items);
-        this[Update]();
-        return res;
-
-    },
-    /// Accessor methods
-    concat: action(function (this: any, ...items: any[]) {
-        this[Self].__realFields();
-        return this[Self].__fields.map(toRealField).concat(...items);
-    }),
-    includes(valueToFind: any, fromIndex: number) {
-        if (valueToFind instanceof RefField) {
-            return this[Self].__realFields().includes(valueToFind, fromIndex);
-        } else {
-            return this[Self].__fields.includes(valueToFind, fromIndex);
-        }
-    },
-    indexOf(valueToFind: any, fromIndex: number) {
-        if (valueToFind instanceof RefField) {
-            return this[Self].__realFields().indexOf(valueToFind, fromIndex);
-        } else {
-            return this[Self].__fields.indexOf(valueToFind, fromIndex);
-        }
-    },
-    join(separator: any) {
-        this[Self].__realFields();
-        return this[Self].__fields.map(toRealField).join(separator);
-    },
-    lastIndexOf(valueToFind: any, fromIndex: number) {
-        if (valueToFind instanceof RefField) {
-            return this[Self].__realFields().lastIndexOf(valueToFind, fromIndex);
-        } else {
-            return this[Self].__fields.lastIndexOf(valueToFind, fromIndex);
-        }
-    },
-    slice(begin: number, end: number) {
-        this[Self].__realFields();
-        return this[Self].__fields.slice(begin, end).map(toRealField);
-    },
-
-    /// Iteration methods
-    entries() {
-        return this[Self].__realFields().entries();
-    },
-    every(callback: any, thisArg: any) {
-        return this[Self].__realFields().every(callback, thisArg);
-        // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway.
-        // If we don't want to support the array parameter, we should use this version instead
-        // return this[Self].__fields.every((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg);
-    },
-    filter(callback: any, thisArg: any) {
-        return this[Self].__realFields().filter(callback, thisArg);
-        // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway.
-        // If we don't want to support the array parameter, we should use this version instead
-        // return this[Self].__fields.filter((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg);
-    },
-    find(callback: any, thisArg: any) {
-        return this[Self].__realFields().find(callback, thisArg);
-        // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway.
-        // If we don't want to support the array parameter, we should use this version instead
-        // return this[Self].__fields.find((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg);
-    },
-    findIndex(callback: any, thisArg: any) {
-        return this[Self].__realFields().findIndex(callback, thisArg);
-        // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway.
-        // If we don't want to support the array parameter, we should use this version instead
-        // return this[Self].__fields.findIndex((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg);
-    },
-    forEach(callback: any, thisArg: any) {
-        return this[Self].__realFields().forEach(callback, thisArg);
-        // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway.
-        // If we don't want to support the array parameter, we should use this version instead
-        // return this[Self].__fields.forEach((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg);
-    },
-    map(callback: any, thisArg: any) {
-        return this[Self].__realFields().map(callback, thisArg);
-        // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway.
-        // If we don't want to support the array parameter, we should use this version instead
-        // return this[Self].__fields.map((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg);
-    },
-    reduce(callback: any, initialValue: any) {
-        return this[Self].__realFields().reduce(callback, initialValue);
-        // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway.
-        // If we don't want to support the array parameter, we should use this version instead
-        // return this[Self].__fields.reduce((acc:any, element:any, index:number, array:any) => callback(acc, toRealField(element), index, array), initialValue);
-    },
-    reduceRight(callback: any, initialValue: any) {
-        return this[Self].__realFields().reduceRight(callback, initialValue);
-        // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway.
-        // If we don't want to support the array parameter, we should use this version instead
-        // return this[Self].__fields.reduceRight((acc:any, element:any, index:number, array:any) => callback(acc, toRealField(element), index, array), initialValue);
-    },
-    some(callback: any, thisArg: any) {
-        return this[Self].__realFields().some(callback, thisArg);
-        // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway.
-        // If we don't want to support the array parameter, we should use this version instead
-        // return this[Self].__fields.some((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg);
-    },
-    values() {
-        return this[Self].__realFields().values();
-    },
-    [Symbol.iterator]() {
-        return this[Self].__realFields().values();
-    }
-};
-
-function toObjectField(field: Field) {
-    return field instanceof RefField ? new ProxyField(field) : field;
-}
-
-function toRealField(field: Field) {
-    return field instanceof ProxyField ? field.value() : field;
-}
-
-function listGetter(target: any, prop: string | number | symbol, receiver: any): any {
-    if (listHandlers.hasOwnProperty(prop)) {
-        return listHandlers[prop];
-    }
-    return getter(target, prop, receiver);
-}
-
-interface ListSpliceUpdate<T> {
-    type: "splice";
-    index: number;
-    added: T[];
-    removedCount: number;
-}
-
-interface ListIndexUpdate<T> {
-    type: "update";
-    index: number;
-    newValue: T;
-}
-
-type ListUpdate<T> = ListSpliceUpdate<T> | ListIndexUpdate<T>;
-
-type StoredType<T extends Field> = T extends RefField ? ProxyField<T> : T;
-
-@Deserializable("list")
-class ListImpl<T extends Field> extends ObjectField {
-    constructor(fields?: T[]) {
-        super();
-        const list = new Proxy<this>(this, {
-            set: setter,
-            get: listGetter,
-            ownKeys: target => Object.keys(target.__fields),
-            getOwnPropertyDescriptor: (target, prop) => {
-                if (prop in target.__fields) {
-                    return {
-                        configurable: true,//TODO Should configurable be true?
-                        enumerable: true,
-                    };
-                }
-                return Reflect.getOwnPropertyDescriptor(target, prop);
-            },
-            deleteProperty: deleteProperty,
-            defineProperty: () => { throw new Error("Currently properties can't be defined on documents using Object.defineProperty"); },
-        });
-        this[SelfProxy] = list;
-        if (fields) {
-            (list as any).push(...fields);
-        }
-        return list;
-    }
-
-    [key: number]: T | (T extends RefField ? Promise<T> : never);
-
-    // this requests all ProxyFields at the same time to avoid the overhead
-    // of separate network requests and separate updates to the React dom.
-    private __realFields() {
-        const waiting = this.__fields.filter(f => f instanceof ProxyField && f.promisedValue());
-        const promised = waiting.map(f => f instanceof ProxyField ? f.promisedValue() : "");
-        // if we find any ProxyFields that don't have a current value, then
-        // start the server request for all of them
-        if (promised.length) {
-            const promise = DocServer.GetRefFields(promised);
-            // as soon as we get the fields from the server, set all the list values in one
-            // action to generate one React dom update.
-            promise.then(fields => runInAction(() => {
-                waiting.map((w, i) => w instanceof ProxyField && w.setValue(fields[promised[i]]));
-            }));
-            // we also have to mark all lists items with this promise so that any calls to them
-            // will await the batch request.
-            // This counts on the handler for 'promise' in the call above being invoked before the
-            // handler for 'promise' in the lines below.
-            waiting.map((w, i) => {
-                w instanceof ProxyField && w.setPromise(promise.then(fields => fields[promised[i]]));
-            });
-        }
-        return this.__fields.map(toRealField);
-    }
-
-    @serializable(alias("fields", list(autoObject(), { afterDeserialize: afterDocDeserialize })))
-    private get __fields() {
-        return this.___fields;
-    }
-
-    private set __fields(value) {
-        this.___fields = value;
-        for (const key in value) {
-            const field = value[key];
-            if (!(field instanceof ObjectField)) continue;
-            (field as ObjectField)[Parent] = this[Self];
-            (field as ObjectField)[OnUpdate] = updateFunction(this[Self], key, field, this[SelfProxy]);
-        }
-    }
-
-    [Copy]() {
-        const copiedData = this[Self].__fields.map(f => f instanceof ObjectField ? f[Copy]() : f);
-        const deepCopy = new ListImpl<T>(copiedData as any);
-        return deepCopy;
-    }
-
-    // @serializable(alias("fields", list(autoObject())))
-    @observable
-    private ___fields: StoredType<T>[] = [];
-
-    private [Update] = (diff: any) => {
-        // console.log(diff);
-        const update = this[OnUpdate];
-        // update && update(diff);
-        update?.();
-    }
-
-    private [Self] = this;
-    private [SelfProxy]: any;
-
-    [ToScriptString]() {
-        return `new List([${(this as any).map((field: any) => Field.toScriptString(field))}])`;
-    }
-    [ToString]() {
-        return "List";
-    }
-}
-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);
\ No newline at end of file
diff --git a/src/new_fields/ListSpec.ts b/src/new_fields/ListSpec.ts
deleted file mode 100644
index e69de29bb..000000000
diff --git a/src/new_fields/ObjectField.ts b/src/new_fields/ObjectField.ts
deleted file mode 100644
index 9aa1c9b04..000000000
--- a/src/new_fields/ObjectField.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-import { RefField } from "./RefField";
-import { OnUpdate, Parent, Copy, ToScriptString, ToString } from "./FieldSymbols";
-import { Scripting } from "../client/util/Scripting";
-
-export abstract class ObjectField {
-    protected [OnUpdate](diff?: any) { }
-    private [Parent]?: RefField | ObjectField;
-    abstract [Copy](): ObjectField;
-
-    abstract [ToScriptString](): string;
-    abstract [ToString](): string;
-}
-
-export namespace ObjectField {
-    export function MakeCopy<T extends ObjectField>(field: T) {
-        return field?.[Copy]();
-    }
-}
-
-Scripting.addGlobal(ObjectField);
\ No newline at end of file
diff --git a/src/new_fields/PresField.ts b/src/new_fields/PresField.ts
deleted file mode 100644
index f236a04fd..000000000
--- a/src/new_fields/PresField.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-//insert code here
-import { ObjectField } from "./ObjectField";
-
-export abstract class PresField extends ObjectField {
-
-}
\ No newline at end of file
diff --git a/src/new_fields/Proxy.ts b/src/new_fields/Proxy.ts
deleted file mode 100644
index 555faaad0..000000000
--- a/src/new_fields/Proxy.ts
+++ /dev/null
@@ -1,126 +0,0 @@
-import { Deserializable } from "../client/util/SerializationHelper";
-import { FieldWaiting } from "./Doc";
-import { primitive, serializable } from "serializr";
-import { observable, action, runInAction } from "mobx";
-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 { Plugins } from "./util";
-
-@Deserializable("proxy")
-export class ProxyField<T extends RefField> extends ObjectField {
-    constructor();
-    constructor(value: T);
-    constructor(fieldId: string);
-    constructor(value?: T | string) {
-        super();
-        if (typeof value === "string") {
-            this.fieldId = value;
-        } else if (value) {
-            this.cache = value;
-            this.fieldId = value[Id];
-        }
-    }
-
-    [Copy]() {
-        if (this.cache) return new ProxyField<T>(this.cache);
-        return new ProxyField<T>(this.fieldId);
-    }
-
-    [ToScriptString]() {
-        return "invalid";
-    }
-    [ToString]() {
-        return "ProxyField";
-    }
-
-    @serializable(primitive())
-    readonly fieldId: string = "";
-
-    // This getter/setter and nested object thing is 
-    // because mobx doesn't play well with observable proxies
-    @observable.ref
-    private _cache: { readonly field: T | undefined } = { field: undefined };
-    private get cache(): T | undefined {
-        return this._cache.field;
-    }
-    private set cache(field: T | undefined) {
-        this._cache = { field };
-    }
-
-    private failed = false;
-    private promise?: Promise<any>;
-
-    value(): T | undefined | FieldWaiting<T> {
-        if (this.cache) {
-            return this.cache;
-        }
-        if (this.failed) {
-            return undefined;
-        }
-        if (!this.promise) {
-            const cached = DocServer.GetCachedRefField(this.fieldId);
-            if (cached !== undefined) {
-                runInAction(() => this.cache = cached as any);
-                return cached as any;
-            }
-            this.promise = DocServer.GetRefField(this.fieldId).then(action((field: any) => {
-                this.promise = undefined;
-                this.cache = field;
-                if (field === undefined) this.failed = true;
-                return field;
-            }));
-        }
-        return this.promise as any;
-    }
-    promisedValue(): string { return !this.cache && !this.failed && !this.promise ? this.fieldId : ""; }
-    setPromise(promise: any) {
-        this.promise = promise;
-    }
-    @action
-    setValue(field: any) {
-        this.promise = undefined;
-        this.cache = field;
-        if (field === undefined) this.failed = true;
-        return field;
-    }
-}
-
-export namespace ProxyField {
-    let useProxy = true;
-    export function DisableProxyFields() {
-        useProxy = false;
-    }
-
-    export function EnableProxyFields() {
-        useProxy = true;
-    }
-
-    export function WithoutProxy<T>(fn: () => T) {
-        DisableProxyFields();
-        try {
-            return fn();
-        } finally {
-            EnableProxyFields();
-        }
-    }
-
-    export function initPlugin() {
-        Plugins.addGetterPlugin((doc, _, value) => {
-            if (useProxy && value instanceof ProxyField) {
-                return { value: value.value() };
-            }
-        });
-    }
-}
-
-function prefetchValue(proxy: PrefetchProxy<RefField>) {
-    return proxy.value() as any;
-}
-
-@scriptingGlobal
-@Deserializable("prefetch_proxy", prefetchValue)
-export class PrefetchProxy<T extends RefField> extends ProxyField<T> {
-}
diff --git a/src/new_fields/RefField.ts b/src/new_fields/RefField.ts
deleted file mode 100644
index b6ef69750..000000000
--- a/src/new_fields/RefField.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-import { serializable, primitive, alias } from "serializr";
-import { Utils } from "../Utils";
-import { Id, HandleUpdate, ToScriptString, ToString } from "./FieldSymbols";
-import { afterDocDeserialize } from "../client/util/SerializationHelper";
-
-export type FieldId = string;
-export abstract class RefField {
-    @serializable(alias("id", primitive({ afterDeserialize: afterDocDeserialize })))
-    private __id: FieldId;
-    readonly [Id]: FieldId;
-
-    constructor(id?: FieldId) {
-        this.__id = id || Utils.GenerateGuid();
-        this[Id] = this.__id;
-    }
-
-    protected [HandleUpdate]?(diff: any): void | Promise<void>;
-
-    abstract [ToScriptString](): string;
-    abstract [ToString](): string;
-}
diff --git a/src/new_fields/RichTextField.ts b/src/new_fields/RichTextField.ts
deleted file mode 100644
index 5cf0e0cc3..000000000
--- a/src/new_fields/RichTextField.ts
+++ /dev/null
@@ -1,41 +0,0 @@
-import { ObjectField } from "./ObjectField";
-import { serializable } from "serializr";
-import { Deserializable } from "../client/util/SerializationHelper";
-import { Copy, ToScriptString, ToPlainText, ToString } from "./FieldSymbols";
-import { scriptingGlobal } from "../client/util/Scripting";
-
-@scriptingGlobal
-@Deserializable("RichTextField")
-export class RichTextField extends ObjectField {
-    @serializable(true)
-    readonly Data: string;
-
-    @serializable(true)
-    readonly Text: string;
-
-    constructor(data: string, text: string = "") {
-        super();
-        this.Data = data;
-        this.Text = text;
-    }
-
-    Empty() {
-        return !(this.Text || this.Data.toString().includes("dashField"));
-    }
-
-    [Copy]() {
-        return new RichTextField(this.Data, this.Text);
-    }
-
-    [ToScriptString]() {
-        return `new RichTextField("${this.Data}", "${this.Text}")`;
-    }
-    [ToString]() {
-        return this.Text;
-    }
-
-    public static DashField(fieldKey: string) {
-        return new RichTextField(`{"doc":{"type":"doc","content":[{"type":"paragraph","attrs":{"align":null,"color":null,"id":null,"indent":null,"inset":null,"lineSpacing":null,"paddingBottom":null,"paddingTop":null},"content":[{"type":"dashField","attrs":{"fieldKey":"${fieldKey}","docid":""}}]}]},"selection":{"type":"text","anchor":2,"head":2},"storedMarks":[]}`, "");
-    }
-
-}
\ No newline at end of file
diff --git a/src/new_fields/RichTextUtils.ts b/src/new_fields/RichTextUtils.ts
deleted file mode 100644
index c475d0d73..000000000
--- a/src/new_fields/RichTextUtils.ts
+++ /dev/null
@@ -1,519 +0,0 @@
-import { AssertionError } from "assert";
-import { docs_v1 } from "googleapis";
-import { Fragment, Mark, Node } from "prosemirror-model";
-import { sinkListItem } from "prosemirror-schema-list";
-import { Utils } from "../Utils";
-import { Docs } from "../client/documents/Documents";
-import { schema } from "../client/views/nodes/formattedText/schema_rts";
-import { GooglePhotos } from "../client/apis/google_docs/GooglePhotosClientUtils";
-import { DocServer } from "../client/DocServer";
-import { Networking } from "../client/Network";
-import { FormattedTextBox } from "../client/views/nodes/formattedText/FormattedTextBox";
-import { Doc, Opt } from "./Doc";
-import { Id } from "./FieldSymbols";
-import { RichTextField } from "./RichTextField";
-import { Cast, StrCast } from "./Types";
-import Color = require('color');
-import { EditorState, TextSelection, Transaction } from "prosemirror-state";
-import { GoogleApiClientUtils } from "../client/apis/google_docs/GoogleApiClientUtils";
-
-export namespace RichTextUtils {
-
-    const delimiter = "\n";
-    const joiner = "";
-
-
-    export const Initialize = (initial?: string) => {
-        const content: any[] = [];
-        const state = {
-            doc: {
-                type: "doc",
-                content,
-            },
-            selection: {
-                type: "text",
-                anchor: 0,
-                head: 0
-            }
-        };
-        if (initial && initial.length) {
-            content.push({
-                type: "paragraph",
-                content: {
-                    type: "text",
-                    text: initial
-                }
-            });
-            state.selection.anchor = state.selection.head = initial.length + 1;
-        }
-        return JSON.stringify(state);
-    };
-
-    export const Synthesize = (plainText: string, oldState?: RichTextField) => {
-        return new RichTextField(ToProsemirrorState(plainText, oldState), plainText);
-    };
-
-    export const ToPlainText = (state: EditorState) => {
-        // Because we're working with plain text, just concatenate all paragraphs
-        const content = state.doc.content;
-        const paragraphs: Node<any>[] = [];
-        content.forEach(node => node.type.name === "paragraph" && paragraphs.push(node));
-
-        // Functions to flatten ProseMirror paragraph objects (and their components) to plain text
-        // Concatentate paragraphs and string the result together
-        const textParagraphs: string[] = paragraphs.map(paragraph => {
-            const text: string[] = [];
-            paragraph.content.forEach(node => node.text && text.push(node.text));
-            return text.join(joiner) + delimiter;
-        });
-        const plainText = textParagraphs.join(joiner);
-        return plainText.substring(0, plainText.length - 1);
-    };
-
-    export const ToProsemirrorState = (plainText: string, oldState?: RichTextField) => {
-        // Remap the text, creating blocks split on newlines
-        const elements = plainText.split(delimiter);
-
-        // Google Docs adds in an extra carriage return automatically, so this counteracts it
-        !elements[elements.length - 1].length && elements.pop();
-
-        // Preserve the current state, but re-write the content to be the blocks
-        const parsed = JSON.parse(oldState ? oldState.Data : Initialize());
-        parsed.doc.content = elements.map(text => {
-            const paragraph: any = { type: "paragraph" };
-            text.length && (paragraph.content = [{ type: "text", marks: [], text }]); // An empty paragraph gets treated as a line break
-            return paragraph;
-        });
-
-        // If the new content is shorter than the previous content and selection is unchanged, may throw an out of bounds exception, so we reset it
-        parsed.selection = { type: "text", anchor: 1, head: 1 };
-
-        // Export the ProseMirror-compatible state object we've just built
-        return JSON.stringify(parsed);
-    };
-
-    export namespace GoogleDocs {
-
-        export const Export = async (state: EditorState): Promise<GoogleApiClientUtils.Docs.Content> => {
-            const nodes: (Node<any> | null)[] = [];
-            const text = ToPlainText(state);
-            state.doc.content.forEach(node => {
-                if (!node.childCount) {
-                    nodes.push(null);
-                } else {
-                    node.content.forEach(child => nodes.push(child));
-                }
-            });
-            const requests = await marksToStyle(nodes);
-            return { text, requests };
-        };
-
-        interface ImageTemplate {
-            width: number;
-            title: string;
-            url: string;
-            agnostic: string;
-        }
-
-        const parseInlineObjects = async (document: docs_v1.Schema$Document): Promise<Map<string, ImageTemplate>> => {
-            const inlineObjectMap = new Map<string, ImageTemplate>();
-            const inlineObjects = document.inlineObjects;
-
-            if (inlineObjects) {
-                const objects = Object.keys(inlineObjects).map(objectId => inlineObjects[objectId]);
-                const mediaItems: MediaItem[] = objects.map(object => {
-                    const embeddedObject = object.inlineObjectProperties!.embeddedObject!;
-                    return { baseUrl: embeddedObject.imageProperties!.contentUri! };
-                });
-
-                const uploads = await Networking.PostToServer("/googlePhotosMediaGet", { mediaItems });
-
-                if (uploads.length !== mediaItems.length) {
-                    throw new AssertionError({ expected: mediaItems.length, actual: uploads.length, message: "Error with internally uploading inlineObjects!" });
-                }
-
-                for (let i = 0; i < objects.length; i++) {
-                    const object = objects[i];
-                    const { accessPaths } = uploads[i];
-                    const { agnostic, _m } = accessPaths;
-                    const embeddedObject = object.inlineObjectProperties!.embeddedObject!;
-                    const size = embeddedObject.size!;
-                    const width = size.width!.magnitude!;
-
-                    inlineObjectMap.set(object.objectId!, {
-                        title: embeddedObject.title || `Imported Image from ${document.title}`,
-                        width,
-                        url: Utils.prepend(_m.client),
-                        agnostic: Utils.prepend(agnostic.client)
-                    });
-                }
-            }
-            return inlineObjectMap;
-        };
-
-        type BulletPosition = { value: number, sinks: number };
-
-        interface MediaItem {
-            baseUrl: string;
-        }
-
-        export const Import = async (documentId: GoogleApiClientUtils.Docs.DocumentId, textNote: Doc): Promise<Opt<GoogleApiClientUtils.Docs.ImportResult>> => {
-            const document = await GoogleApiClientUtils.Docs.retrieve({ documentId });
-            if (!document) {
-                return undefined;
-            }
-            const inlineObjectMap = await parseInlineObjects(document);
-            const title = document.title!;
-            const { text, paragraphs } = GoogleApiClientUtils.Docs.Utils.extractText(document);
-            let state = FormattedTextBox.blankState();
-            const structured = parseLists(paragraphs);
-
-            let position = 3;
-            const lists: ListGroup[] = [];
-            const indentMap = new Map<ListGroup, BulletPosition[]>();
-            let globalOffset = 0;
-            const nodes: Node<any>[] = [];
-            for (const element of structured) {
-                if (Array.isArray(element)) {
-                    lists.push(element);
-                    const positions: BulletPosition[] = [];
-                    const items = element.map(paragraph => {
-                        const item = listItem(state.schema, paragraph.contents);
-                        const sinks = paragraph.bullet!;
-                        positions.push({
-                            value: position + globalOffset,
-                            sinks
-                        });
-                        position += item.nodeSize;
-                        globalOffset += 2 * sinks;
-                        return item;
-                    });
-                    indentMap.set(element, positions);
-                    nodes.push(list(state.schema, items));
-                } else {
-                    if (element.contents.some(child => "inlineObjectId" in child)) {
-                        const group = element.contents;
-                        group.forEach((child, i) => {
-                            let node: Opt<Node<any>>;
-                            if ("inlineObjectId" in child) {
-                                node = imageNode(state.schema, inlineObjectMap.get(child.inlineObjectId!)!, textNote);
-                            } else if ("content" in child && (i !== group.length - 1 || child.content!.removeTrailingNewlines().length)) {
-                                node = paragraphNode(state.schema, [child]);
-                            }
-                            if (node) {
-                                position += node.nodeSize;
-                                nodes.push(node);
-                            }
-                        });
-                    } else {
-                        const paragraph = paragraphNode(state.schema, element.contents);
-                        nodes.push(paragraph);
-                        position += paragraph.nodeSize;
-                    }
-                }
-            }
-            state = state.apply(state.tr.replaceWith(0, 2, nodes));
-
-            const sink = sinkListItem(state.schema.nodes.list_item);
-            const dispatcher = (tr: Transaction) => state = state.apply(tr);
-            for (const list of lists) {
-                for (const pos of indentMap.get(list)!) {
-                    const resolved = state.doc.resolve(pos.value);
-                    state = state.apply(state.tr.setSelection(new TextSelection(resolved)));
-                    for (let i = 0; i < pos.sinks; i++) {
-                        sink(state, dispatcher);
-                    }
-                }
-            }
-
-            return { title, text, state };
-        };
-
-        type Paragraph = GoogleApiClientUtils.Docs.Utils.DeconstructedParagraph;
-        type ListGroup = Paragraph[];
-        type PreparedParagraphs = (ListGroup | Paragraph)[];
-
-        const parseLists = (paragraphs: ListGroup) => {
-            const groups: PreparedParagraphs = [];
-            let group: ListGroup = [];
-            for (const paragraph of paragraphs) {
-                if (paragraph.bullet !== undefined) {
-                    group.push(paragraph);
-                } else {
-                    if (group.length) {
-                        groups.push(group);
-                        group = [];
-                    }
-                    groups.push(paragraph);
-                }
-            }
-            group.length && groups.push(group);
-            return groups;
-        };
-
-        const listItem = (schema: any, runs: docs_v1.Schema$TextRun[]): Node => {
-            return schema.node("list_item", null, paragraphNode(schema, runs));
-        };
-
-        const list = (schema: any, items: Node[]): Node => {
-            return schema.node("bullet_list", null, items);
-        };
-
-        const paragraphNode = (schema: any, runs: docs_v1.Schema$TextRun[]): Node => {
-            const children = runs.map(run => textNode(schema, run)).filter(child => child !== undefined);
-            const fragment = children.length ? Fragment.from(children) : undefined;
-            return schema.node("paragraph", null, fragment);
-        };
-
-        const imageNode = (schema: any, image: ImageTemplate, textNote: Doc) => {
-            const { url: src, width, agnostic } = image;
-            let docid: string;
-            const guid = Utils.GenerateDeterministicGuid(agnostic);
-            const backingDocId = StrCast(textNote[guid]);
-            if (!backingDocId) {
-                const backingDoc = Docs.Create.ImageDocument(agnostic, { _width: 300, _height: 300 });
-                Doc.makeCustomViewClicked(backingDoc, Docs.Create.FreeformDocument);
-                docid = backingDoc[Id];
-                textNote[guid] = docid;
-            } else {
-                docid = backingDocId;
-            }
-            return schema.node("image", { src, agnostic, width, docid, float: null, location: "onRight" });
-        };
-
-        const textNode = (schema: any, run: docs_v1.Schema$TextRun) => {
-            const text = run.content!.removeTrailingNewlines();
-            return text.length ? schema.text(text, styleToMarks(schema, run.textStyle)) : undefined;
-        };
-
-        const StyleToMark = new Map<keyof docs_v1.Schema$TextStyle, keyof typeof schema.marks>([
-            ["bold", "strong"],
-            ["italic", "em"],
-            ["foregroundColor", "pFontColor"],
-            ["fontSize", "pFontSize"]
-        ]);
-
-        const styleToMarks = (schema: any, textStyle?: docs_v1.Schema$TextStyle) => {
-            if (!textStyle) {
-                return undefined;
-            }
-            const marks: Mark[] = [];
-            Object.keys(textStyle).forEach(key => {
-                let value: any;
-                const targeted = key as keyof docs_v1.Schema$TextStyle;
-                if (value = textStyle[targeted]) {
-                    const attributes: any = {};
-                    let converted = StyleToMark.get(targeted) || targeted;
-
-                    value.url && (attributes.href = value.url);
-                    if (value.color) {
-                        const object = value.color.rgbColor;
-                        attributes.color = Color.rgb(["red", "green", "blue"].map(color => object[color] * 255 || 0)).hex();
-                    }
-                    if (value.magnitude) {
-                        attributes.fontSize = value.magnitude;
-                    }
-
-                    if (converted === "weightedFontFamily") {
-                        converted = ImportFontFamilyMapping.get(value.fontFamily) || "timesNewRoman";
-                    }
-
-                    const mapped = schema.marks[converted];
-                    if (!mapped) {
-                        alert(`No mapping found for ${converted}!`);
-                        return;
-                    }
-
-                    const mark = schema.mark(mapped, attributes);
-                    mark && marks.push(mark);
-                }
-            });
-            return marks;
-        };
-
-        const MarkToStyle = new Map<keyof typeof schema.marks, keyof docs_v1.Schema$TextStyle>([
-            ["strong", "bold"],
-            ["em", "italic"],
-            ["pFontColor", "foregroundColor"],
-            ["pFontSize", "fontSize"],
-            ["timesNewRoman", "weightedFontFamily"],
-            ["georgia", "weightedFontFamily"],
-            ["comicSans", "weightedFontFamily"],
-            ["tahoma", "weightedFontFamily"],
-            ["impact", "weightedFontFamily"]
-        ]);
-
-        const ExportFontFamilyMapping = new Map<string, string>([
-            ["timesNewRoman", "Times New Roman"],
-            ["arial", "Arial"],
-            ["georgia", "Georgia"],
-            ["comicSans", "Comic Sans MS"],
-            ["tahoma", "Tahoma"],
-            ["impact", "Impact"]
-        ]);
-
-        const ImportFontFamilyMapping = new Map<string, string>([
-            ["Times New Roman", "timesNewRoman"],
-            ["Arial", "arial"],
-            ["Georgia", "georgia"],
-            ["Comic Sans MS", "comicSans"],
-            ["Tahoma", "tahoma"],
-            ["Impact", "impact"]
-        ]);
-
-        const ignored = ["user_mark"];
-
-        const marksToStyle = async (nodes: (Node<any> | null)[]): Promise<docs_v1.Schema$Request[]> => {
-            const requests: docs_v1.Schema$Request[] = [];
-            let position = 1;
-            for (const node of nodes) {
-                if (node === null) {
-                    position += 2;
-                    continue;
-                }
-                const { marks, attrs, nodeSize } = node;
-                const textStyle: docs_v1.Schema$TextStyle = {};
-                const information: LinkInformation = {
-                    startIndex: position,
-                    endIndex: position + nodeSize,
-                    textStyle
-                };
-                let mark: Mark<any>;
-                const markMap = BuildMarkMap(marks);
-                for (const markName of Object.keys(schema.marks)) {
-                    if (ignored.includes(markName) || !(mark = markMap[markName])) {
-                        continue;
-                    }
-                    let converted = MarkToStyle.get(markName) || markName as keyof docs_v1.Schema$TextStyle;
-                    let value: any = true;
-                    if (!converted) {
-                        continue;
-                    }
-                    const { attrs } = mark;
-                    switch (converted) {
-                        case "link":
-                            let url = attrs.href;
-                            const delimiter = "/doc/";
-                            const alreadyShared = "?sharing=true";
-                            if (new RegExp(window.location.origin + delimiter).test(url) && !url.endsWith(alreadyShared)) {
-                                const linkDoc = await DocServer.GetRefField(url.split(delimiter)[1]);
-                                if (linkDoc instanceof Doc) {
-                                    let exported = (await Cast(linkDoc.anchor2, Doc))!;
-                                    if (!exported.customLayout) {
-                                        exported = Doc.MakeAlias(exported);
-                                        Doc.makeCustomViewClicked(exported, Docs.Create.FreeformDocument);
-                                        linkDoc.anchor2 = exported;
-                                    }
-                                    url = Utils.shareUrl(exported[Id]);
-                                }
-                            }
-                            value = { url };
-                            textStyle.foregroundColor = fromRgb.blue;
-                            textStyle.bold = true;
-                            break;
-                        case "fontSize":
-                            value = { magnitude: attrs.fontSize, unit: "PT" };
-                            break;
-                        case "foregroundColor":
-                            value = fromHex(attrs.color);
-                            break;
-                        case "weightedFontFamily":
-                            value = { fontFamily: ExportFontFamilyMapping.get(markName) };
-                    }
-                    let matches: RegExpExecArray | null;
-                    if ((matches = /p(\d+)/g.exec(markName)) !== null) {
-                        converted = "fontSize";
-                        value = { magnitude: parseInt(matches[1].replace("px", "")), unit: "PT" };
-                    }
-                    textStyle[converted] = value;
-                }
-                if (Object.keys(textStyle).length) {
-                    requests.push(EncodeStyleUpdate(information));
-                }
-                if (node.type.name === "image") {
-                    const width = attrs.width;
-                    requests.push(await EncodeImage({
-                        startIndex: position + nodeSize - 1,
-                        uri: attrs.agnostic,
-                        width: Number(typeof width === "string" ? width.replace("px", "") : width)
-                    }));
-                }
-                position += nodeSize;
-            }
-            return requests;
-        };
-
-        const BuildMarkMap = (marks: Mark<any>[]) => {
-            const markMap: { [type: string]: Mark<any> } = {};
-            marks.forEach(mark => markMap[mark.type.name] = mark);
-            return markMap;
-        };
-
-        interface LinkInformation {
-            startIndex: number;
-            endIndex: number;
-            textStyle: docs_v1.Schema$TextStyle;
-        }
-
-        interface ImageInformation {
-            startIndex: number;
-            width: number;
-            uri: string;
-        }
-
-        namespace fromRgb {
-
-            export const convert = (red: number, green: number, blue: number): docs_v1.Schema$OptionalColor => {
-                return {
-                    color: {
-                        rgbColor: {
-                            red: red / 255,
-                            green: green / 255,
-                            blue: blue / 255
-                        }
-                    }
-                };
-            };
-
-            export const red = convert(255, 0, 0);
-            export const green = convert(0, 255, 0);
-            export const blue = convert(0, 0, 255);
-
-        }
-
-        const fromHex = (color: string): docs_v1.Schema$OptionalColor => {
-            const c = Color(color);
-            return fromRgb.convert(c.red(), c.green(), c.blue());
-        };
-
-        const EncodeStyleUpdate = (information: LinkInformation): docs_v1.Schema$Request => {
-            const { startIndex, endIndex, textStyle } = information;
-            return {
-                updateTextStyle: {
-                    fields: "*",
-                    range: { startIndex, endIndex },
-                    textStyle
-                } as docs_v1.Schema$UpdateTextStyleRequest
-            };
-        };
-
-        const EncodeImage = async ({ uri, width, startIndex }: ImageInformation) => {
-            if (!uri) {
-                return {};
-            }
-            const source = [Docs.Create.ImageDocument(uri)];
-            const baseUrls = await GooglePhotos.Transactions.UploadThenFetch(source);
-            if (baseUrls) {
-                return {
-                    insertInlineImage: {
-                        uri: baseUrls[0],
-                        objectSize: { width: { magnitude: width, unit: "PT" } },
-                        location: { index: startIndex }
-                    }
-                };
-            }
-            return {};
-        };
-    }
-
-}
\ No newline at end of file
diff --git a/src/new_fields/Schema.ts b/src/new_fields/Schema.ts
deleted file mode 100644
index 72bce283d..000000000
--- a/src/new_fields/Schema.ts
+++ /dev/null
@@ -1,120 +0,0 @@
-import { Interface, ToInterface, Cast, ToConstructor, HasTail, Head, Tail, ListSpec, ToType, DefaultFieldConstructor } from "./Types";
-import { Doc, Field } from "./Doc";
-import { ObjectField } from "./ObjectField";
-import { RefField } from "./RefField";
-import { SelfProxy } from "./FieldSymbols";
-
-type AllToInterface<T extends Interface[]> = {
-    1: ToInterface<Head<T>> & AllToInterface<Tail<T>>,
-    0: ToInterface<Head<T>>
-}[HasTail<T> extends true ? 1 : 0];
-
-export const emptySchema = createSchema({});
-export const Document = makeInterface(emptySchema);
-export type Document = makeInterface<[typeof emptySchema]>;
-
-export interface InterfaceFunc<T extends Interface[]> {
-    (docs: Doc[]): makeInterface<T>[];
-    (): makeInterface<T>;
-    (doc: Doc): makeInterface<T>;
-}
-
-export type makeInterface<T extends Interface[]> = AllToInterface<T> & Doc & { proto: Doc | undefined };
-// export function makeInterface<T extends Interface[], U extends Doc>(schemas: T): (doc: U) => All<T, U>;
-// export function makeInterface<T extends Interface, U extends Doc>(schema: T): (doc: U) => makeInterface<T, U>; 
-export function makeInterface<T extends Interface[]>(...schemas: T): InterfaceFunc<T> {
-    const schema: Interface = {};
-    for (const s of schemas) {
-        for (const key in s) {
-            schema[key] = s[key];
-        }
-    }
-    const proto = new Proxy({}, {
-        get(target: any, prop, receiver) {
-            const field = receiver.doc[prop];
-            if (prop in schema) {
-                const desc = prop === "proto" ? Doc : (schema as any)[prop]; // bcz: proto doesn't appear in schemas ... maybe it should?
-                if (typeof desc === "object" && "defaultVal" in desc && "type" in desc) {//defaultSpec
-                    return Cast(field, desc.type, desc.defaultVal);
-                } else if (typeof desc === "function" && !ObjectField.isPrototypeOf(desc) && !RefField.isPrototypeOf(desc)) {
-                    const doc = Cast(field, Doc);
-                    if (doc === undefined) {
-                        return undefined;
-                    } else if (doc instanceof Doc) {
-                        return desc(doc);
-                    } else {
-                        return doc.then(doc => doc && desc(doc));
-                    }
-                } else {
-                    return Cast(field, desc);
-                }
-            }
-            return field;
-        },
-        set(target: any, prop, value, receiver) {
-            receiver.doc[prop] = value;
-            return true;
-        }
-    });
-    const fn = (doc: Doc) => {
-        doc = doc[SelfProxy];
-        // if (!(doc instanceof Doc)) {
-        //     throw new Error("Currently wrapping a schema in another schema isn't supported");
-        // }
-        const obj = Object.create(proto, { doc: { value: doc, writable: false } });
-        return obj;
-    };
-    return function (doc?: Doc | Doc[]) {
-        doc = doc || new Doc;
-        if (doc instanceof Doc) {
-            return fn(doc);
-        } else {
-            return doc.map(fn);
-        }
-    };
-}
-
-export type makeStrictInterface<T extends Interface> = Partial<ToInterface<T>>;
-export function makeStrictInterface<T extends Interface>(schema: T): (doc: Doc) => makeStrictInterface<T> {
-    const proto = {};
-    for (const key in schema) {
-        const type = schema[key];
-        Object.defineProperty(proto, key, {
-            get() {
-                return Cast(this.__doc[key], type as any);
-            },
-            set(value) {
-                value = Cast(value, type as any);
-                if (value !== undefined) {
-                    this.__doc[key] = value;
-                    return;
-                }
-                throw new TypeError("Expected type " + type);
-            }
-        });
-    }
-    return function (doc: any) {
-        if (!(doc instanceof Doc)) {
-            throw new Error("Currently wrapping a schema in another schema isn't supported");
-        }
-        const obj = Object.create(proto);
-        obj.__doc = doc;
-        return obj;
-    };
-}
-
-export function createSchema<T extends Interface>(schema: T): T & { proto: ToConstructor<Doc> } {
-    (schema as any).proto = Doc;
-    return schema as any;
-}
-
-export function listSpec<U extends ToConstructor<Field>>(type: U): ListSpec<ToType<U>> {
-    return { List: type as any };//TODO Types
-}
-
-export function defaultSpec<T extends ToConstructor<Field>>(type: T, defaultVal: ToType<T>): DefaultFieldConstructor<ToType<T>> {
-    return {
-        type: type as any,
-        defaultVal
-    };
-}
\ No newline at end of file
diff --git a/src/new_fields/SchemaHeaderField.ts b/src/new_fields/SchemaHeaderField.ts
deleted file mode 100644
index 07c90f5a2..000000000
--- a/src/new_fields/SchemaHeaderField.ts
+++ /dev/null
@@ -1,122 +0,0 @@
-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 { ColumnType } from "../client/views/collections/CollectionSchemaView";
-
-export const PastelSchemaPalette = new Map<string, string>([
-    // ["pink1", "#FFB4E8"],
-    ["pink2", "#ff9cee"],
-    ["pink3", "#ffccf9"],
-    ["pink4", "#fcc2ff"],
-    ["pink5", "#f6a6ff"],
-    ["purple1", "#b28dff"],
-    ["purple2", "#c5a3ff"],
-    ["purple3", "#d5aaff"],
-    ["purple4", "#ecd4ff"],
-    // ["purple5", "#fb34ff"],
-    ["purple6", "#dcd3ff"],
-    ["purple7", "#a79aff"],
-    ["purple8", "#b5b9ff"],
-    ["purple9", "#97a2ff"],
-    ["bluegreen1", "#afcbff"],
-    ["bluegreen2", "#aff8db"],
-    ["bluegreen3", "#c4faf8"],
-    ["bluegreen4", "#85e3ff"],
-    ["bluegreen5", "#ace7ff"],
-    // ["bluegreen6", "#6eb5ff"],
-    ["bluegreen7", "#bffcc6"],
-    ["bluegreen8", "#dbffd6"],
-    ["yellow1", "#f3ffe3"],
-    ["yellow2", "#e7ffac"],
-    ["yellow3", "#ffffd1"],
-    ["yellow4", "#fff5ba"],
-    // ["red1", "#ffc9de"],
-    ["red2", "#ffabab"],
-    ["red3", "#ffbebc"],
-    ["red4", "#ffcbc1"],
-    ["orange1", "#ffd5b3"],
-]);
-
-export const RandomPastel = () => Array.from(PastelSchemaPalette.values())[Math.floor(Math.random() * PastelSchemaPalette.size)];
-
-export const DarkPastelSchemaPalette = new Map<string, string>([
-    ["pink2", "#c932b0"],
-    ["purple4", "#913ad6"],
-    ["bluegreen1", "#3978ed"],
-    ["bluegreen7", "#2adb3e"],
-    ["bluegreen5", "#21b0eb"],
-    ["yellow4", "#edcc0c"],
-    ["red2", "#eb3636"],
-    ["orange1", "#f2740f"],
-]);
-
-@scriptingGlobal
-@Deserializable("schemaheader")
-export class SchemaHeaderField extends ObjectField {
-    @serializable(primitive())
-    heading: string;
-    @serializable(primitive())
-    color: string;
-    @serializable(primitive())
-    type: number;
-    @serializable(primitive())
-    width: number;
-    @serializable(primitive())
-    collapsed: boolean | undefined;
-    @serializable(primitive())
-    desc: boolean | undefined; // boolean determines sort order, undefined when no sort
-
-    constructor(heading: string = "", color: string = RandomPastel(), type?: ColumnType, width?: number, desc?: boolean, collapsed?: boolean) {
-        super();
-
-        this.heading = heading;
-        this.color = color;
-        this.type = type ? type : 0;
-        this.width = width ? width : -1;
-        this.desc = desc;
-        this.collapsed = collapsed;
-    }
-
-    setHeading(heading: string) {
-        this.heading = heading;
-        this[OnUpdate]();
-    }
-
-    setColor(color: string) {
-        this.color = color;
-        this[OnUpdate]();
-    }
-
-    setType(type: ColumnType) {
-        this.type = type;
-        this[OnUpdate]();
-    }
-
-    setWidth(width: number) {
-        this.width = width;
-        this[OnUpdate]();
-    }
-
-    setDesc(desc: boolean | undefined) {
-        this.desc = desc;
-        this[OnUpdate]();
-    }
-
-    setCollapsed(collapsed: boolean | undefined) {
-        this.collapsed = collapsed;
-        this[OnUpdate]();
-    }
-
-    [Copy]() {
-        return new SchemaHeaderField(this.heading, this.color, this.type);
-    }
-
-    [ToScriptString]() {
-        return `invalid`;
-    }
-    [ToString]() {
-        return `SchemaHeaderField`;
-    }
-}
\ No newline at end of file
diff --git a/src/new_fields/ScriptField.ts b/src/new_fields/ScriptField.ts
deleted file mode 100644
index 4b790f483..000000000
--- a/src/new_fields/ScriptField.ts
+++ /dev/null
@@ -1,193 +0,0 @@
-import { ObjectField } from "./ObjectField";
-import { CompiledScript, CompileScript, scriptingGlobal, ScriptOptions, CompileError, CompileResult } 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 } from "../new_fields/Doc";
-import { Plugins, setter } from "./util";
-import { computedFn } from "mobx-utils";
-import { ProxyField } from "./Proxy";
-import { Cast } from "./Types";
-
-function optional(propSchema: PropSchema) {
-    return custom(value => {
-        if (value !== undefined) {
-            return propSchema.serializer(value);
-        }
-        return SKIP;
-    }, (jsonValue: any, context: any, oldValue: any, callback: (err: any, result: any) => void) => {
-        if (jsonValue !== undefined) {
-            return propSchema.deserializer(jsonValue, callback, context, oldValue);
-        }
-        return SKIP;
-    });
-}
-
-const optionsSchema = createSimpleSchema({
-    requiredType: true,
-    addReturn: true,
-    typecheck: true,
-    editable: true,
-    readonly: true,
-    params: optional(map(primitive()))
-});
-
-const scriptSchema = createSimpleSchema({
-    options: object(optionsSchema),
-    originalScript: true
-});
-
-async function deserializeScript(script: ScriptField) {
-    const captures: ProxyField<Doc> = (script as any).captures;
-    if (captures) {
-        const doc = (await captures.value())!;
-        const captured: any = {};
-        const keys = Object.keys(doc);
-        const vals = await Promise.all(keys.map(key => doc[key]) as any);
-        keys.forEach((key, i) => captured[key] = vals[i]);
-        (script.script.options as any).capturedVariables = captured;
-    }
-    const comp = CompileScript(script.script.originalScript, script.script.options);
-    if (!comp.compiled) {
-        throw new Error("Couldn't compile loaded script");
-    }
-    (script as any).script = comp;
-}
-
-@scriptingGlobal
-@Deserializable("script", deserializeScript)
-export class ScriptField extends ObjectField {
-    @serializable(object(scriptSchema))
-    readonly script: CompiledScript;
-    @serializable(object(scriptSchema))
-    readonly setterscript: CompiledScript | undefined;
-
-    @serializable(autoObject())
-    private captures?: ProxyField<Doc>;
-
-    constructor(script: CompiledScript, setterscript?: CompileResult) {
-        super();
-
-        if (script?.options.capturedVariables) {
-            const doc = Doc.assign(new Doc, script.options.capturedVariables);
-            this.captures = new ProxyField(doc);
-        }
-        this.setterscript = setterscript?.compiled ? setterscript : undefined;
-        this.script = script;
-    }
-
-    //     init(callback: (res: Field) => any) {
-    //         const options = this.options!;
-    //         const keys = Object.keys(options.options.capturedIds);
-    //         Server.GetFields(keys).then(fields => {
-    //             let captured: { [name: string]: Field } = {};
-    //             keys.forEach(key => captured[options.options.capturedIds[key]] = fields[key]);
-    //             const opts: ScriptOptions = {
-    //                 addReturn: options.options.addReturn,
-    //                 params: options.options.params,
-    //                 requiredType: options.options.requiredType,
-    //                 capturedVariables: captured
-    //             };
-    //             const script = CompileScript(options.script, opts);
-    //             if (!script.compiled) {
-    //                 throw new Error("Can't compile script");
-    //             }
-    //             this._script = script;
-    //             callback(this);
-    //         });
-    //     }
-
-    [Copy](): ObjectField {
-        return new ScriptField(this.script);
-    }
-    toString() {
-        return `${this.script.originalScript}`;
-    }
-
-    [ToScriptString]() {
-        return "script field";
-    }
-    [ToString]() {
-        return this.script.originalScript;
-    }
-    public static CompileScript(script: string, params: object = {}, addReturn = false, capturedVariables?: { [name: string]: Field }) {
-        const compiled = CompileScript(script, {
-            params: { this: Doc.name, self: Doc.name, _last_: "any", ...params },
-            typecheck: false,
-            editable: true,
-            addReturn: addReturn,
-            capturedVariables
-        });
-        return compiled;
-    }
-    public static MakeFunction(script: string, params: object = {}, capturedVariables?: { [name: string]: Field }) {
-        const compiled = ScriptField.CompileScript(script, params, true, capturedVariables);
-        return compiled.compiled ? new ScriptField(compiled) : undefined;
-    }
-
-    public static MakeScript(script: string, params: object = {}, capturedVariables?: { [name: string]: Field }) {
-        const compiled = ScriptField.CompileScript(script, params, false, capturedVariables);
-        return compiled.compiled ? new ScriptField(compiled) : undefined;
-    }
-}
-
-@scriptingGlobal
-@Deserializable("computed", deserializeScript)
-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;
-
-
-    constructor(script: CompiledScript, setterscript?: CompiledScript) {
-        super(script,
-            !setterscript && script?.originalScript.includes("self.timecode") ?
-                ScriptField.CompileScript("self['x' + self.timecode] = value", { value: "any" }, true) : setterscript);
-    }
-
-    public static MakeScript(script: string, params: object = {}) {
-        const compiled = ScriptField.CompileScript(script, params, false);
-        return compiled.compiled ? new ComputedField(compiled) : undefined;
-    }
-    public static MakeFunction(script: string, params: object = {}, capturedVariables?: { [name: string]: Field }, setterScript?: string) {
-        const compiled = ScriptField.CompileScript(script, params, true, capturedVariables);
-        const setCompiled = setterScript ? ScriptField.CompileScript(setterScript, params, true, capturedVariables) : undefined;
-        return compiled.compiled ? new ComputedField(compiled, setCompiled?.compiled ? setCompiled : undefined) : undefined;
-    }
-    public static MakeInterpolated(fieldKey: string, interpolatorKey: string) {
-        const getField = ScriptField.CompileScript(`(self['${fieldKey}-indexed'])[self.${interpolatorKey}]`, {}, true, {});
-        const setField = ScriptField.CompileScript(`(self['${fieldKey}-indexed'])[self.${interpolatorKey}] = value`, { value: "any" }, true, {});
-        return getField.compiled ? new ComputedField(getField, setField?.compiled ? setField : undefined) : undefined;
-    }
-}
-
-export namespace ComputedField {
-    let useComputed = true;
-    export function DisableComputedFields() {
-        useComputed = false;
-    }
-
-    export function EnableComputedFields() {
-        useComputed = true;
-    }
-
-    export const undefined = "__undefined";
-
-    export function WithoutComputed<T>(fn: () => T) {
-        DisableComputedFields();
-        try {
-            return fn();
-        } finally {
-            EnableComputedFields();
-        }
-    }
-
-    export function initPlugin() {
-        Plugins.addGetterPlugin((doc, _, value) => {
-            if (useComputed && value instanceof ComputedField) {
-                return { value: value._valueOutsideReaction(doc), shouldReturn: true };
-            }
-        });
-    }
-}
\ No newline at end of file
diff --git a/src/new_fields/Types.ts b/src/new_fields/Types.ts
deleted file mode 100644
index 3d784448d..000000000
--- a/src/new_fields/Types.ts
+++ /dev/null
@@ -1,108 +0,0 @@
-import { Field, Opt, FieldResult, Doc } from "./Doc";
-import { List } from "./List";
-import { RefField } from "./RefField";
-import { DateField } from "./DateField";
-import { ScriptField } from "./ScriptField";
-
-export type ToType<T extends InterfaceValue> =
-    T extends "string" ? string :
-    T extends "number" ? number :
-    T extends "boolean" ? boolean :
-    T extends ListSpec<infer U> ? List<U> :
-    // T extends { new(...args: any[]): infer R } ? (R | Promise<R>) : never;
-    T extends DefaultFieldConstructor<infer _U> ? never :
-    T extends { new(...args: any[]): List<Field> } ? never :
-    T extends { new(...args: any[]): infer R } ? R :
-    T extends (doc?: Doc) => infer R ? R : never;
-
-export type ToConstructor<T extends Field> =
-    T extends string ? "string" :
-    T extends number ? "number" :
-    T extends boolean ? "boolean" :
-    T extends List<infer U> ? ListSpec<U> :
-    new (...args: any[]) => T;
-
-export type ToInterface<T extends Interface> = {
-    [P in Exclude<keyof T, "proto">]: T[P] extends DefaultFieldConstructor<infer F> ? Exclude<FieldResult<F>, undefined> : FieldResult<ToType<T[P]>>;
-};
-
-// type ListSpec<T extends Field[]> = { List: ToContructor<Head<T>> | ListSpec<Tail<T>> };
-export type ListSpec<T extends Field> = { List: ToConstructor<T> };
-
-export type DefaultFieldConstructor<T extends Field> = {
-    type: ToConstructor<T>,
-    defaultVal: T
-};
-
-// type ListType<U extends Field[]> = { 0: List<ListType<Tail<U>>>, 1: ToType<Head<U>> }[HasTail<U> extends true ? 0 : 1];
-
-export type Head<T extends any[]> = T extends [any, ...any[]] ? T[0] : never;
-export type Tail<T extends any[]> =
-    ((...t: T) => any) extends ((_: any, ...tail: infer TT) => any) ? TT : [];
-export type HasTail<T extends any[]> = T extends ([] | [any]) ? false : true;
-
-export type InterfaceValue = ToConstructor<Field> | ListSpec<Field> | DefaultFieldConstructor<Field> | ((doc?: Doc) => any);
-//TODO Allow you to optionally specify default values for schemas, which should then make that field not be partial
-export interface Interface {
-    [key: string]: InterfaceValue;
-    // [key: string]: ToConstructor<Field> | ListSpec<Field[]>;
-}
-export type WithoutRefField<T extends Field> = T extends RefField ? never : T;
-
-export type CastCtor = ToConstructor<Field> | ListSpec<Field>;
-
-export function Cast<T extends CastCtor>(field: FieldResult, ctor: T): FieldResult<ToType<T>>;
-export function Cast<T extends CastCtor>(field: FieldResult, ctor: T, defaultVal: WithoutList<WithoutRefField<ToType<T>>> | null): WithoutList<ToType<T>>;
-export function Cast<T extends CastCtor>(field: FieldResult, ctor: T, defaultVal?: ToType<T> | null): FieldResult<ToType<T>> | undefined {
-    if (field instanceof Promise) {
-        return defaultVal === undefined ? field.then(f => Cast(f, ctor) as any) as any : defaultVal === null ? undefined : defaultVal;
-    }
-    if (field !== undefined && !(field instanceof Promise)) {
-        if (typeof ctor === "string") {
-            if (typeof field === ctor) {
-                return field as ToType<T>;
-            }
-        } else if (typeof ctor === "object") {
-            if (field instanceof List) {
-                return field as any;
-            }
-        } else if (field instanceof (ctor as any)) {
-            return field as ToType<T>;
-        }
-    }
-    return defaultVal === null ? undefined : defaultVal;
-}
-
-export function NumCast(field: FieldResult, defaultVal: number | null = 0) {
-    return Cast(field, "number", defaultVal);
-}
-
-export function StrCast(field: FieldResult, defaultVal: string | null = "") {
-    return Cast(field, "string", defaultVal);
-}
-
-export function BoolCast(field: FieldResult, defaultVal: boolean | null = false) {
-    return Cast(field, "boolean", defaultVal);
-}
-export function DateCast(field: FieldResult) {
-    return Cast(field, DateField, null);
-}
-
-export function ScriptCast(field: FieldResult, defaultVal: ScriptField | null = null) {
-    return Cast(field, ScriptField, defaultVal);
-}
-
-type WithoutList<T extends Field> = T extends List<infer R> ? (R extends RefField ? (R | Promise<R>)[] : R[]) : T;
-
-export function FieldValue<T extends Field, U extends WithoutList<T>>(field: FieldResult<T>, defaultValue: U): WithoutList<T>;
-export function FieldValue<T extends Field>(field: FieldResult<T>): Opt<T>;
-export function FieldValue<T extends Field>(field: FieldResult<T>, defaultValue?: T): Opt<T> {
-    return (field instanceof Promise || field === undefined) ? defaultValue : field;
-}
-
-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); } };
-}
\ No newline at end of file
diff --git a/src/new_fields/URLField.ts b/src/new_fields/URLField.ts
deleted file mode 100644
index fb71160ca..000000000
--- a/src/new_fields/URLField.ts
+++ /dev/null
@@ -1,53 +0,0 @@
-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";
-
-function url() {
-    return custom(
-        function (value: URL) {
-            return value.href;
-        },
-        function (jsonValue: string) {
-            return new URL(jsonValue);
-        }
-    );
-}
-
-export abstract class URLField extends ObjectField {
-    @serializable(url())
-    readonly url: URL;
-
-    constructor(url: string);
-    constructor(url: URL);
-    constructor(url: URL | string) {
-        super();
-        if (typeof url === "string") {
-            url = new URL(url);
-        }
-        this.url = url;
-    }
-
-    [ToScriptString]() {
-        return `new ${this.constructor.name}("${this.url.href}")`;
-    }
-    [ToString]() {
-        return this.url.href;
-    }
-
-    [Copy](): this {
-        return new (this.constructor as any)(this.url);
-    }
-}
-
-export const nullAudio = "https://actions.google.com/sounds/v1/alarms/beep_short.ogg";
-
-@scriptingGlobal @Deserializable("audio") export class AudioField 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 { }
-@scriptingGlobal @Deserializable("web") export class WebField extends URLField { }
-@scriptingGlobal @Deserializable("youtube") export class YoutubeField extends URLField { }
-@scriptingGlobal @Deserializable("webcam") export class WebCamField extends URLField { }
-
diff --git a/src/new_fields/documentSchemas.ts b/src/new_fields/documentSchemas.ts
deleted file mode 100644
index cacba43b6..000000000
--- a/src/new_fields/documentSchemas.ts
+++ /dev/null
@@ -1,100 +0,0 @@
-import { makeInterface, createSchema, listSpec } from "./Schema";
-import { ScriptField } from "./ScriptField";
-import { Doc } from "./Doc";
-import { DateField } from "./DateField";
-
-export const documentSchema = createSchema({
-    // content properties
-    type: "string",             // enumerated type of document -- should be template-specific (ie, start with an '_')
-    title: "string",            // document title (can be on either data document or layout)
-    isTemplateForField: "string",// if specified, it indicates the document is a template that renders the specified field
-    creationDate: DateField,    // when the document was created
-    links: listSpec(Doc),       // computed (readonly) list of links associated with this document
-
-    // "Location" properties in a very general sense
-    currentTimecode: "number",  // current play back time of a temporal document (video / audio)
-    displayTimecode: "number",  // the time that a document should be displayed (e.g., time an annotation should be displayed on a video)
-    inOverlay: "boolean",       // whether the document is rendered in an OverlayView which handles selection/dragging differently
-    x: "number",                // x coordinate when in a freeform view 
-    y: "number",                // y coordinate when in a freeform view 
-    z: "number",                // z "coordinate" - non-zero specifies the overlay layer of a freeformview
-    zIndex: "number",           // zIndex of a document in a freeform view
-    scrollY: "number",          // "command" to scroll a document to a position on load (the value will be reset to 0 after that )
-    scrollTop: "number",        // scroll position of a scrollable document (pdf, text, web)
-
-    // appearance properties on the layout
-    _autoHeight: "boolean",     // whether the height of the document should be computed automatically based on its contents
-    _nativeWidth: "number",     // native width of document which determines how much document contents are scaled when the document's width is set
-    _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
-    _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
-    _showCaption: "string",     // whether editable caption text is overlayed at the bottom of the document 
-    _showTitle: "string",       // the fieldkey whose contents should be displayed at the top of the document
-    _showTitleHover: "string",  // the showTitle should be shown only on hover
-    _showAudio: "boolean",      // whether to show the audio record icon on documents
-    _freeformLayoutEngine: "string",// the string ID for the layout engine to use to layout freeform view documents
-    _LODdisable: "boolean",     // whether to disbale LOD switching for CollectionFreeFormViews
-    _pivotField: "string",      // specifies which field key should be used as the timeline/pivot axis
-    _replacedChrome: "string",  // what the default chrome is replaced with. Currently only supports the value of 'replaced' for PresBox's.
-    _chromeStatus: "string",    // determines the state of the collection chrome. values allowed are 'replaced', 'enabled', 'disabled', 'collapsed'
-    _fontSize: "number",
-    _fontFamily: "string",
-    _sidebarWidthPercent: "string", // percent of text window width taken up by sidebar
-
-    // appearance properties on the data document
-    backgroundColor: "string",  // background color of document
-    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
-    fontSize: "string",
-    layout: "string",           // this is the native layout string for the document.  templates can be added using other fields and setting layoutKey below
-    layoutKey: "string",        // holds the field key for the field that actually holds the current lyoat
-    letterSpacing: "string",
-    opacity: "number",          // opacity of document
-    strokeWidth: "number",
-    textTransform: "string",
-    treeViewOpen: "boolean",    //  flag denoting whether the documents sub-tree (contents) is visible or hidden
-    treeViewExpandedView: "string", // name of field whose contents are being displayed as the document's subtree
-    treeViewPreventOpen: "boolean", // ignores the treeViewOpen flag (for allowing a view to not be slaved to other views of the document)
-
-    // interaction and linking properties
-    ignoreClick: "boolean",     // whether documents ignores input clicks (but does not ignore manipulation and other events) 
-    onClick: ScriptField,       // script to run when document is clicked (can be overriden by an onClick prop)
-    onPointerDown: ScriptField, // script to run when document is clicked (can be overriden by an onClick prop)
-    onPointerUp: ScriptField,   // script to run when document is clicked (can be overriden by an onClick prop)
-    onDragStart: ScriptField,   // script to run when document is dragged (without being selected).  the script should return the Doc to be dropped.
-    followLinkLocation: "string",// flag for where to place content when following a click interaction (e.g., onRight, inPlace, inTab, ) 
-    isInPlaceContainer: "boolean",// whether the marked object will display addDocTab() calls that target "inPlace" destinations
-    isLinkButton: "boolean",    // whether document functions as a link follow button to follow the first link on the document when clicked   
-    isBackground: "boolean",    // whether document is a background element and ignores input events (can only select with marquee)
-    lockedPosition: "boolean",  // whether the document can be moved (dragged)
-    lockedTransform: "boolean", // whether the document can be panned/zoomed
-
-    // drag drop properties
-    dragFactory: Doc,           // the document that serves as the "template" for the onDragStart script.  ie, to drag out copies of the dragFactory document.
-    dropAction: "string",       // override specifying what should happen when this document is dropped (can be "alias", "copy", "move")
-    targetDropAction: "string", // allows the target of a drop event to specify the dropAction ("alias", "copy", "move") NOTE: if the document is dropped within the same collection, the dropAction is coerced to 'move'
-    childDropAction: "string",  // specify the override for what should happen when the child of a collection is dragged from it and dropped (can be "alias" or "copy")
-    removeDropProperties: listSpec("string"), // properties that should be removed from the alias/copy/etc of this document when it is dropped
-});
-
-
-export const collectionSchema = createSchema({
-    childLayoutTemplateName: "string", // the name of a template to use to override the layoutKey when rendering a document -- ONLY used in DocHolderBox 
-    childLayoutTemplate: Doc, // layout template to use to render children of a collecion
-    childLayoutString: "string", //layout string to use to render children of a collection
-    childClickedOpenTemplateView: Doc, // layout template to apply to a child when its clicked on in a collection and opened (requires onChildClick or other script to read this value and apply template)
-    dontRegisterChildViews: "boolean", // whether views made of this document are registered so that they can be found when drawing links scrollToLinkID: "string",   // id of link being traversed. allows this doc to scroll/highlight/etc its link anchor. scrollToLinkID should be set to undefined by this doc after it sets up its scroll,etc.
-    onChildClick: ScriptField, // script to run for each child when its clicked
-    onChildDoubleClick: ScriptField, // script to run for each child when its clicked
-    onCheckedClick: ScriptField, // script to run when a checkbox is clicked next to a child in a tree view
-});
-
-export type Document = makeInterface<[typeof documentSchema]>;
-export const Document = makeInterface(documentSchema);
diff --git a/src/new_fields/util.ts b/src/new_fields/util.ts
deleted file mode 100644
index a287b0210..000000000
--- a/src/new_fields/util.ts
+++ /dev/null
@@ -1,199 +0,0 @@
-import { UndoManager } from "../client/util/UndoManager";
-import { Doc, Field, FieldResult, UpdatingFromServer, LayoutSym } 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 } from "./Types";
-
-function _readOnlySetter(): never {
-    throw new Error("Documents can't be modified in read-only mode");
-}
-
-const tracing = false;
-export function TraceMobx() {
-    tracing && trace();
-}
-
-export interface GetterResult {
-    value: FieldResult;
-    shouldReturn?: boolean;
-}
-export type GetterPlugin = (receiver: any, prop: string | number, currentValue: any) => GetterResult | undefined;
-const getterPlugins: GetterPlugin[] = [];
-
-export namespace Plugins {
-    export function addGetterPlugin(plugin: GetterPlugin) {
-        getterPlugins.push(plugin);
-    }
-}
-
-const _setterImpl = action(function (target: any, prop: string | symbol | number, value: any, receiver: any): boolean {
-    //console.log("-set " + target[SelfProxy].title + "(" + target[SelfProxy][prop] + ")." + prop.toString() + " = " + value);
-    if (SerializationHelper.IsSerializing()) {
-        target[prop] = value;
-        return true;
-    }
-
-    if (typeof prop === "symbol") {
-        target[prop] = value;
-        return true;
-    }
-    if (value !== undefined) {
-        value = value[SelfProxy] || value;
-    }
-    const curValue = target.__fields[prop];
-    if (curValue === value || (curValue instanceof ProxyField && value instanceof RefField && curValue.fieldId === value[Id])) {
-        // TODO This kind of checks correctly in the case that curValue is a ProxyField and value is a RefField, but technically
-        // curValue should get filled in with value if it isn't already filled in, in case we fetched the referenced field some other way
-        return true;
-    }
-    if (value instanceof RefField) {
-        value = new ProxyField(value);
-    }
-    if (value instanceof ObjectField) {
-        if (value[Parent] && value[Parent] !== receiver && !(value instanceof PrefetchProxy)) {
-            throw new Error("Can't put the same object in multiple documents at the same time");
-        }
-        value[Parent] = receiver;
-        value[OnUpdate] = updateFunction(target, prop, value, receiver);
-    }
-    if (curValue instanceof ObjectField) {
-        delete curValue[Parent];
-        delete curValue[OnUpdate];
-    }
-    const writeMode = DocServer.getFieldWriteMode(prop as string);
-    const fromServer = target[UpdatingFromServer];
-    const sameAuthor = fromServer || (receiver.author === Doc.CurrentUserEmail);
-    const writeToDoc = sameAuthor || (writeMode !== DocServer.WriteMode.LiveReadonly);
-    const writeToServer = sameAuthor || (writeMode === DocServer.WriteMode.Default);
-    if (writeToDoc) {
-        if (value === undefined) {
-            delete target.__fields[prop];
-        } else {
-            target.__fields[prop] = value;
-        }
-        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) } });
-        } else {
-            DocServer.registerDocWithCachedUpdate(receiver, prop as string, curValue);
-        }
-        UndoManager.AddEvent({
-            redo: () => receiver[prop] = value,
-            undo: () => receiver[prop] = curValue
-        });
-    }
-    return true;
-});
-
-let _setter: (target: any, prop: string | symbol | number, value: any, receiver: any) => boolean = _setterImpl;
-
-export function makeReadOnly() {
-    _setter = _readOnlySetter;
-}
-
-export function makeEditable() {
-    _setter = _setterImpl;
-}
-
-const layoutProps = ["panX", "panY", "width", "height", "nativeWidth", "nativeHeight", "fitWidth", "fitToBox",
-    "LODdisable", "chromeStatus", "viewType", "gridGap", "xMargin", "yMargin", "autoHeight"];
-export function setter(target: any, in_prop: string | symbol | number, value: any, receiver: any): boolean {
-    let prop = in_prop;
-    if (typeof prop === "string" && prop !== "__id" && prop !== "__fields" && (prop.startsWith("_") || layoutProps.includes(prop))) {
-        if (!prop.startsWith("_")) {
-            console.log(prop + " is deprecated - switch to _" + prop);
-            prop = "_" + prop;
-        }
-        if (target.__LAYOUT__) {
-            target.__LAYOUT__[prop] = value;
-            return true;
-        }
-    }
-    if (target.__fields[prop] instanceof ComputedField && target.__fields[prop].setterscript) {
-        return ScriptCast(target.__fields[prop])?.setterscript?.run({ self: target[SelfProxy], this: target[SelfProxy], value }).success ? true : false;
-    }
-    return _setter(target, prop, value, receiver);
-}
-
-export function getter(target: any, in_prop: string | symbol | number, receiver: any): any {
-    let prop = in_prop;
-    if (prop === LayoutSym) {
-        return target.__LAYOUT__;
-    }
-    if (typeof prop === "string" && prop !== "__id" && prop !== "__fields" && (prop.startsWith("_") || layoutProps.includes(prop))) {
-        if (!prop.startsWith("_")) {
-            console.log(prop + " is deprecated - switch to _" + prop);
-            prop = "_" + prop;
-        }
-        if (target.__LAYOUT__) return target.__LAYOUT__[prop];
-    }
-    if (prop === "then") {//If we're being awaited
-        return undefined;
-    }
-    if (typeof prop === "symbol") {
-        return target.__fields[prop] || target[prop];
-    }
-    if (SerializationHelper.IsSerializing()) {
-        return target[prop];
-    }
-    return getFieldImpl(target, prop, receiver);
-}
-
-function getFieldImpl(target: any, prop: string | number, receiver: any, ignoreProto: boolean = false): any {
-    receiver = receiver || target[SelfProxy];
-    let field = target.__fields[prop];
-    for (const plugin of getterPlugins) {
-        const res = plugin(receiver, prop, field);
-        if (res === undefined) continue;
-        if (res.shouldReturn) {
-            return res.value;
-        } else {
-            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 (proto instanceof Doc) {
-            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") {
-        delete target[prop];
-        return true;
-    }
-    target[SelfProxy][prop] = undefined;
-    return true;
-}
-
-export function updateFunction(target: any, prop: any, value: any, receiver: any) {
-    let current = ObjectField.MakeCopy(value);
-    return (diff?: any) => {
-        if (true || !diff) {
-            diff = { '$set': { ["fields." + prop]: SerializationHelper.Serialize(value) } };
-            const oldValue = current;
-            const newValue = ObjectField.MakeCopy(value);
-            current = newValue;
-            UndoManager.AddEvent({
-                redo() { receiver[prop] = newValue; },
-                undo() { receiver[prop] = oldValue; }
-            });
-        }
-        target[Update](diff);
-    };
-}
\ No newline at end of file
diff --git a/src/pen-gestures/GestureUtils.ts b/src/pen-gestures/GestureUtils.ts
index b8a82ab4d..3b6170f68 100644
--- a/src/pen-gestures/GestureUtils.ts
+++ b/src/pen-gestures/GestureUtils.ts
@@ -1,9 +1,9 @@
 import { NDollarRecognizer } from "./ndollar";
 import { Type } from "typescript";
-import { InkField, PointData } from "../new_fields/InkField";
+import { InkField, PointData } from "../fields/InkField";
 import { Docs } from "../client/documents/Documents";
-import { Doc, WidthSym, HeightSym } from "../new_fields/Doc";
-import { NumCast } from "../new_fields/Types";
+import { Doc, WidthSym, HeightSym } from "../fields/Doc";
+import { NumCast } from "../fields/Types";
 import { CollectionFreeFormView } from "../client/views/collections/collectionFreeForm/CollectionFreeFormView";
 import { Rect } from "react-measure";
 import { Scripting } from "../client/util/Scripting";
diff --git a/src/server/ApiManagers/GooglePhotosManager.ts b/src/server/ApiManagers/GooglePhotosManager.ts
index 11841a603..be17b698e 100644
--- a/src/server/ApiManagers/GooglePhotosManager.ts
+++ b/src/server/ApiManagers/GooglePhotosManager.ts
@@ -3,7 +3,7 @@ import { Method, _error, _success, _invalid } from "../RouteManager";
 import * as path from "path";
 import { GoogleApiServerUtils } from "../apis/google/GoogleApiServerUtils";
 import { BatchedArray, TimeUnit } from "array-batcher";
-import { Opt } from "../../new_fields/Doc";
+import { Opt } from "../../fields/Doc";
 import { DashUploadUtils, InjectSize, SizeSuffix } from "../DashUploadUtils";
 import { Database } from "../database";
 import { red } from "colors";
diff --git a/src/server/ApiManagers/UserManager.ts b/src/server/ApiManagers/UserManager.ts
index 68b3107ae..0d1d8f218 100644
--- a/src/server/ApiManagers/UserManager.ts
+++ b/src/server/ApiManagers/UserManager.ts
@@ -3,7 +3,7 @@ import { Method } from "../RouteManager";
 import { Database } from "../database";
 import { msToTime } from "../ActionUtilities";
 import * as bcrypt from "bcrypt-nodejs";
-import { Opt } from "../../new_fields/Doc";
+import { Opt } from "../../fields/Doc";
 
 export const timeMap: { [id: string]: number } = {};
 interface ActivityUnit {
diff --git a/src/server/DashUploadUtils.ts b/src/server/DashUploadUtils.ts
index 8567631cd..b74904ada 100644
--- a/src/server/DashUploadUtils.ts
+++ b/src/server/DashUploadUtils.ts
@@ -4,7 +4,7 @@ import * as path from 'path';
 import * as sharp from 'sharp';
 import request = require('request-promise');
 import { ExifImage } from 'exif';
-import { Opt } from '../new_fields/Doc';
+import { Opt } from '../fields/Doc';
 import { AcceptibleMedia, Upload } from './SharedMediaTypes';
 import { filesDirectory, publicDirectory } from '.';
 import { File } from 'formidable';
diff --git a/src/server/Message.ts b/src/server/Message.ts
index 01aae5de7..80f372733 100644
--- a/src/server/Message.ts
+++ b/src/server/Message.ts
@@ -1,6 +1,6 @@
 import { Utils } from "../Utils";
 import { Point } from "../pen-gestures/ndollar";
-import { Doc } from "../new_fields/Doc";
+import { Doc } from "../fields/Doc";
 import { Image } from "canvas";
 import { AnalysisResult, ImportResults } from "../scraping/buxton/final/BuxtonImporter";
 
diff --git a/src/server/Recommender.ts b/src/server/Recommender.ts
index aacdb4053..423ce9b46 100644
--- a/src/server/Recommender.ts
+++ b/src/server/Recommender.ts
@@ -1,6 +1,6 @@
-// //import { Doc } from "../new_fields/Doc";
-// //import { StrCast } from "../new_fields/Types";
-// //import { List } from "../new_fields/List";
+// //import { Doc } from "../fields/Doc";
+// //import { StrCast } from "../fields/Types";
+// //import { List } from "../fields/List";
 // //import { CognitiveServices } from "../client/cognitive_services/CognitiveServices";
 
 // // var w2v = require('word2vec');
diff --git a/src/server/apis/google/GoogleApiServerUtils.ts b/src/server/apis/google/GoogleApiServerUtils.ts
index 2e4811c86..20f96f432 100644
--- a/src/server/apis/google/GoogleApiServerUtils.ts
+++ b/src/server/apis/google/GoogleApiServerUtils.ts
@@ -1,6 +1,6 @@
 import { google } from "googleapis";
 import { OAuth2Client, Credentials, OAuth2ClientOptions } from "google-auth-library";
-import { Opt } from "../../../new_fields/Doc";
+import { Opt } from "../../../fields/Doc";
 import { GaxiosResponse } from "gaxios";
 import request = require('request-promise');
 import * as qs from "query-string";
diff --git a/src/server/database.ts b/src/server/database.ts
index ed9a246e3..a5f23c4b1 100644
--- a/src/server/database.ts
+++ b/src/server/database.ts
@@ -1,6 +1,6 @@
 import * as mongodb from 'mongodb';
 import { Transferable } from './Message';
-import { Opt } from '../new_fields/Doc';
+import { Opt } from '../fields/Doc';
 import { Utils, emptyFunction } from '../Utils';
 import { Credentials } from 'google-auth-library';
 import { GoogleApiServerUtils } from './apis/google/GoogleApiServerUtils';
diff --git a/test/test.ts b/test/test.ts
index 245733e9b..5fc156b46 100644
--- a/test/test.ts
+++ b/test/test.ts
@@ -8,10 +8,10 @@ const dom = new JSDOM("", {
 
 
 import { autorun, reaction } from "mobx";
-import { Doc } from '../src/new_fields/Doc';
-import { Cast } from '../src/new_fields/Types';
-import { createSchema, makeInterface, defaultSpec } from '../src/new_fields/Schema';
-import { ImageField } from '../src/new_fields/URLField';
+import { Doc } from '../src/fields/Doc';
+import { Cast } from '../src/fields/Types';
+import { createSchema, makeInterface, defaultSpec } from '../src/fields/Schema';
+import { ImageField } from '../src/fields/URLField';
 describe("Document", () => {
     it('should hold fields', () => {
         const key = "Test";
-- 
cgit v1.2.3-70-g09d2


From fb922ed8ddad8e088f44886385772d55ea7971bb Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Fri, 15 May 2020 11:05:04 -0400
Subject: fixed frame animation

---
 .../views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

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

diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 3a55e805e..84a5bd733 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -128,7 +128,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         if (timecode !== undefined) {
             ((newBox instanceof Doc) ? [newBox] : newBox).map(doc => {
                 doc["x-indexed"] = new List<number>(numberRange(timecode + 1).map(i => NumCast(doc.x)));
-                doc["x-indexed"] = new List<number>(numberRange(timecode + 1).map(i => NumCast(doc.y)));
+                doc["y-indexed"] = new List<number>(numberRange(timecode + 1).map(i => NumCast(doc.y)));
                 doc.timecode = ComputedField.MakeFunction("collection.timecode", {}, { collection: this.props.Document });
                 doc.x = ComputedField.MakeInterpolated("x", "timecode");
                 doc.y = ComputedField.MakeInterpolated("y", "timecode");
@@ -1160,8 +1160,8 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         this.childDocs.map(doc => {
             const xindexed = Cast(doc['x-indexed'], listSpec("number"), null);
             const yindexed = Cast(doc['y-indexed'], listSpec("number"), null);
-            xindexed.length <= timecode && xindexed.push(NumCast(doc.x));
-            yindexed.length <= timecode && yindexed.push(NumCast(doc.y));
+            xindexed.length <= timecode + 1 && xindexed.push(NumCast(doc.x));
+            yindexed.length <= timecode + 1 && yindexed.push(NumCast(doc.y));
         });
         this.childDocs.map(doc => doc.transition = "transform 1s");
         this.props.Document.timecode = Math.max(0, timecode + 1);
-- 
cgit v1.2.3-70-g09d2

