From b7d02ec188ecf043300ed858fdb68148b3e52a71 Mon Sep 17 00:00:00 2001
From: Sam Wilkins <abdullah_ahmed@brown.edu>
Date: Tue, 9 Apr 2019 17:24:11 -0400
Subject: exploring in-editor undo-redo

---
 src/client/views/nodes/FormattedTextBox.tsx | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

(limited to 'src/client/views/nodes/FormattedTextBox.tsx')

diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 512ad7d70..be534099c 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -15,6 +15,7 @@ import { Decoration, DecorationSet } from 'prosemirror-view'
 import { TooltipTextMenu } from "../../util/TooltipTextMenu"
 import { ContextMenu } from "../../views/ContextMenu";
 import { inpRules } from "../../util/RichTextRules";
+import { Schema } from "prosemirror-model";
 const { buildMenuItems } = require("prosemirror-example-setup");
 const { menuBar } = require("prosemirror-menu");
 
@@ -61,6 +62,12 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
         }
     }
 
+    undo = <S extends Schema = any>(state: EditorState<S>, dispatch?: (tr: Transaction<S>) => void): boolean => {
+        console.log(state);
+        console.log(dispatch);
+        return true;
+    }
+
     componentDidMount() {
         let state: EditorState;
         const config = {
@@ -68,7 +75,7 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
             inpRules, //these currently don't do anything, but could eventually be helpful
             plugins: [
                 history(),
-                keymap({ "Mod-z": undo, "Mod-y": redo }),
+                keymap({ "Mod-z": this.undo, "Mod-y": redo }),
                 keymap(baseKeymap),
                 this.tooltipMenuPlugin()
             ]
@@ -151,7 +158,7 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
         e.stopPropagation();
     }
 
-    tooltipMenuPlugin() {
+    tooltipMenuPlugin(): Plugin<any, any> {
         return new Plugin({
             view(_editorView) {
                 return new TooltipTextMenu(_editorView)
-- 
cgit v1.2.3-70-g09d2


From e0b3c759880639bf56f9b8b39ea2e38c5cbad8a6 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Wed, 10 Apr 2019 14:28:25 -0400
Subject: fixed extra rendering for collections.

---
 src/client/Server.ts                               | 10 +++----
 .../dash-nodes/HistogramBoxPrimitives.tsx          |  6 ++--
 src/client/util/DragManager.ts                     |  4 +--
 src/client/util/SelectionManager.ts                |  2 +-
 src/client/views/DocumentDecorations.tsx           |  5 ++--
 src/client/views/Main.scss                         | 24 +++++++--------
 src/client/views/Main.tsx                          | 19 ++++++++----
 .../views/collections/CollectionDockingView.tsx    |  4 +--
 src/client/views/collections/CollectionPDFView.tsx |  3 +-
 .../views/collections/CollectionSchemaView.tsx     |  4 +--
 src/client/views/collections/CollectionSubView.tsx |  3 +-
 .../views/collections/CollectionVideoView.tsx      |  3 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  7 +++--
 src/client/views/nodes/DocumentView.tsx            | 35 ++++++++++++++--------
 src/client/views/nodes/FieldView.tsx               | 10 +++----
 src/client/views/nodes/FormattedTextBox.tsx        |  4 +--
 src/client/views/nodes/VideoBox.tsx                |  2 +-
 17 files changed, 84 insertions(+), 61 deletions(-)

(limited to 'src/client/views/nodes/FormattedTextBox.tsx')

diff --git a/src/client/Server.ts b/src/client/Server.ts
index 857101a33..228d94a7b 100644
--- a/src/client/Server.ts
+++ b/src/client/Server.ts
@@ -1,10 +1,10 @@
 import { Key } from "../fields/Key";
-import { ObservableMap, action, reaction } from "mobx";
+import { ObservableMap, action, reaction, runInAction } from "mobx";
 import { Field, FieldWaiting, FIELD_WAITING, Opt, FieldId } from "../fields/Field";
 import { Document } from "../fields/Document";
 import { SocketStub, FieldMap } from "./SocketStub";
 import * as OpenSocket from 'socket.io-client';
-import { Utils } from "./../Utils";
+import { Utils, emptyFunction } from "./../Utils";
 import { MessageStore, Types } from "./../server/Message";
 
 export class Server {
@@ -59,7 +59,7 @@ export class Server {
     public static GetFields(fieldIds: FieldId[]): Promise<{ [id: string]: Field }>;
     public static GetFields(fieldIds: FieldId[], callback: (fields: FieldMap) => any): void;
     public static GetFields(fieldIds: FieldId[], callback?: (fields: FieldMap) => any): Promise<FieldMap> | void {
-        let fn = (cb: (fields: FieldMap) => void) => {
+        let fn = (cb: (fields: FieldMap) => void) => runInAction(() => {
 
             let neededFieldIds: FieldId[] = [];
             let waitingFieldIds: FieldId[] = [];
@@ -104,7 +104,7 @@ export class Server {
                         }
                     }, { fireImmediately: true });
             }));
-        };
+        });
         if (callback) {
             fn(callback);
         } else {
@@ -167,7 +167,7 @@ export class Server {
             if (f) {
                 // console.log("Applying        : " + field._id);
                 f.UpdateFromServer(field.data);
-                f.init(() => { });
+                f.init(emptyFunction);
             } else {
                 // console.log("Not applying wa : " + field._id);
             }
diff --git a/src/client/northstar/dash-nodes/HistogramBoxPrimitives.tsx b/src/client/northstar/dash-nodes/HistogramBoxPrimitives.tsx
index 4c5bdb14b..721bf6a89 100644
--- a/src/client/northstar/dash-nodes/HistogramBoxPrimitives.tsx
+++ b/src/client/northstar/dash-nodes/HistogramBoxPrimitives.tsx
@@ -1,7 +1,7 @@
 import React = require("react");
 import { computed, observable, reaction, runInAction, trace, action } from "mobx";
 import { observer } from "mobx-react";
-import { Utils as DashUtils } from '../../../Utils';
+import { Utils as DashUtils, emptyFunction } from '../../../Utils';
 import { FilterModel } from "../../northstar/core/filter/FilterModel";
 import { ModelHelpers } from "../../northstar/model/ModelHelpers";
 import { ArrayUtil } from "../../northstar/utils/ArrayUtil";
@@ -49,7 +49,7 @@ export class HistogramBoxPrimitives extends React.Component<HistogramPrimitivesP
     private getSelectionToggle(binPrimitives: HistogramBinPrimitive[], allBrushIndex: number, filterModel: FilterModel) {
         let rawAllBrushPrim = ArrayUtil.FirstOrDefault(binPrimitives, bp => bp.BrushIndex === allBrushIndex);
         if (!rawAllBrushPrim) {
-            return () => { };
+            return emptyFunction;
         }
         let allBrushPrim = rawAllBrushPrim;
         return () => runInAction(() => {
@@ -97,7 +97,7 @@ export class HistogramBoxPrimitives extends React.Component<HistogramPrimitivesP
         let trans1Ypercent = `${yFrom / this.renderDimension * 100}%`;
         return <line className="histogramboxprimitives-line" key={DashUtils.GenerateGuid()} x1={trans1Xpercent} x2={`${trans2Xpercent}`} y1={trans1Ypercent} y2={`${trans2Ypercent}`} />;
     }
-    drawRect(r: PIXIRectangle, barAxis: number, color: number | undefined, classExt: string, tapHandler: () => void = () => { }) {
+    drawRect(r: PIXIRectangle, barAxis: number, color: number | undefined, classExt: string, tapHandler: () => void = emptyFunction) {
         if (r.height < 0) {
             r.y += r.height;
             r.height = -r.height;
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 4849ae9f7..f7395578f 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -4,7 +4,7 @@ import { CollectionDockingView } from "../views/collections/CollectionDockingVie
 import { CollectionView } from "../views/collections/CollectionView";
 import { DocumentDecorations } from "../views/DocumentDecorations";
 import { DocumentView } from "../views/nodes/DocumentView";
-import { returnFalse } from "../../Utils";
+import { returnFalse, emptyFunction } from "../../Utils";
 
 export function setupDrag(_reference: React.RefObject<HTMLDivElement>, docFunc: () => Document, moveFunc?: DragManager.MoveFunction, copyOnDrop: boolean = false) {
     let onRowMove = action((e: PointerEvent): void => {
@@ -224,7 +224,7 @@ export namespace DragManager {
                 CollectionDockingView.Instance.StartOtherDrag(docs, {
                     pageX: e.pageX,
                     pageY: e.pageY,
-                    preventDefault: () => { },
+                    preventDefault: emptyFunction,
                     button: 0
                 });
             }
diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index 5ddaafc72..c6b8c7b0d 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -48,7 +48,7 @@ export namespace SelectionManager {
 
         manager.DeselectAll();
         if (found) manager.SelectDoc(found, false);
-        Main.Instance.SetTextDoc(undefined, undefined);
+        Main.Instance.SetTextDoc(undefined, undefined, undefined);
     }
 
     export function SelectedDocuments(): Array<DocumentView> {
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 28af46358..b7bf727f1 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -15,6 +15,7 @@ import { DocumentView } from "./nodes/DocumentView";
 import { LinkMenu } from "./nodes/LinkMenu";
 import React = require("react");
 import { FieldWaiting } from "../../fields/Field";
+import { emptyFunction } from "../../Utils";
 const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
@@ -217,7 +218,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
             let dragData = new DragManager.LinkDragData(SelectionManager.SelectedDocuments()[0]);
             DragManager.StartLinkDrag(this._linkerButton.current, dragData, e.pageX, e.pageY, {
                 handlers: {
-                    dragComplete: action(() => { }),
+                    dragComplete: action(emptyFunction),
                 },
                 hideSource: false
             });
@@ -262,7 +263,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
                 let dragData = new DragManager.DocumentDragData(moddrag.length ? moddrag : draggedDocs);
                 DragManager.StartDocumentDrag([this._linkButton.current], dragData, e.x, e.y, {
                     handlers: {
-                        dragComplete: action(() => { }),
+                        dragComplete: action(emptyFunction),
                     },
                     hideSource: false
                 });
diff --git a/src/client/views/Main.scss b/src/client/views/Main.scss
index fe7f007b0..8ef6b83f1 100644
--- a/src/client/views/Main.scss
+++ b/src/client/views/Main.scss
@@ -169,21 +169,21 @@ button:hover {
     overflow: scroll;
 }
 .mainDiv-textInput {
-    background:pink;
-        width: 200px;
-        height: 200px;
+    background-color: rgba(248, 6, 6, 0.1);
+    width: 200px;
+    height: 200px;
+    position:absolute;
+    overflow: visible;
+    top: 0;
+    left: 0;
+    .formattedTextBox-cont {
+        background-color: rgba(248, 6, 6, 0.1);
+        width: 100%;
+        height: 100%;
         position:absolute;
-        overflow: visible;
         top: 0;
         left: 0;
-        .formattedTextBox-cont {
-            background:pink;
-            width: 100%;
-            height: 100%;
-            position:absolute;
-            top: 0;
-            left: 0;
-    }
+}
 }
 #mainContent-div {
     width:100%;
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index 012633bed..2751b1899 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -23,7 +23,7 @@ import { Documents } from '../documents/Documents';
 import { ColumnAttributeModel } from '../northstar/core/attribute/AttributeModel';
 import { AttributeTransformationModel } from '../northstar/core/attribute/AttributeTransformationModel';
 import { Gateway, Settings } from '../northstar/manager/Gateway';
-import { AggregateFunction, Catalog } from '../northstar/model/idea/idea';
+import { AggregateFunction, Catalog, Point } from '../northstar/model/idea/idea';
 import '../northstar/model/ModelExtensions';
 import { HistogramOperation } from '../northstar/operations/HistogramOperation';
 import '../northstar/utils/Extensions';
@@ -204,10 +204,12 @@ export class Main extends React.Component {
 
     @observable _textDoc?: Document = undefined;
     _textRect: any;
+    _textXf: Transform = Transform.Identity();
     @action
-    SetTextDoc(textDoc?: Document, div?: HTMLDivElement) {
+    SetTextDoc(textDoc?: Document, div?: HTMLDivElement, tx?: Transform) {
         this._textDoc = undefined;
         this._textDoc = textDoc;
+        this._textXf = tx ? tx : Transform.Identity();
         if (div) {
             this._textRect = div.getBoundingClientRect();
         }
@@ -220,8 +222,15 @@ export class Main extends React.Component {
             let y: number = this._textRect.y;
             let w: number = this._textRect.width;
             let h: number = this._textRect.height;
-            return <div className="mainDiv-textInput" style={{ transform: `translate(${x}px, ${y}px)`, width: `${w}px`, height: `${h}px` }} >
-                <FormattedTextBox fieldKey={KeyStore.Archives} Document={this._textDoc} isSelected={returnTrue} select={emptyFunction} isTopMost={true} selectOnLoad={true} onActiveChanged={emptyFunction} active={returnTrue} ScreenToLocalTransform={Transform.Identity} focus={(doc) => { }} />
+            let t = this._textXf.transformPoint(0, 0);
+            let s = this._textXf.transformPoint(1, 0);
+            s[0] = Math.sqrt((s[0] - t[0]) * (s[0] - t[0]) + (s[1] - t[1]) * (s[1] - t[1]));
+            return <div className="mainDiv-textInput" style={{ transform: `translate(${x}px, ${y}px) scale(${s[0]},${s[0]})` }} >
+                <div className="mainDiv-textInput" style={{ transform: `scale(${1 / s[0]}, ${1 / s[0]})`, width: `${w / s[0]}px`, height: `${h / s[0]}px` }}>
+
+                    <FormattedTextBox fieldKey={KeyStore.Archives} Document={this._textDoc} isSelected={returnTrue} select={emptyFunction} isTopMost={true} selectOnLoad={true} onActiveChanged={emptyFunction} active={returnTrue} ScreenToLocalTransform={Transform.Identity} focus={(doc) => { }} />
+
+                </div>
             </ div>;
         }
         else return (null);
@@ -310,7 +319,7 @@ export class Main extends React.Component {
             <div className="main-buttonDiv" key="workspaces" style={{ top: '34px', left: '2px', position: 'absolute' }} ref={workspacesRef}>
                 <button onClick={this.toggleWorkspaces}>Workspaces</button></div>,
             <div className="main-buttonDiv" key="logout" style={{ top: '34px', right: '1px', position: 'absolute' }} ref={logoutRef}>
-                <button onClick={() => request.get(ServerUtils.prepend(RouteStore.logout), () => { })}>Log Out</button></div>
+                <button onClick={() => request.get(ServerUtils.prepend(RouteStore.logout), emptyFunction)}>Log Out</button></div>
         ];
     }
 
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index ea6d3a247..212cf8a69 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -50,7 +50,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
     public StartOtherDrag(dragDocs: Document[], e: any) {
         dragDocs.map(dragDoc =>
             this.AddRightSplit(dragDoc, true).contentItems[0].tab._dragListener.
-                onMouseDown({ pageX: e.pageX, pageY: e.pageY, preventDefault: () => { }, button: 0 }));
+                onMouseDown({ pageX: e.pageX, pageY: e.pageY, preventDefault: emptyFunction, button: 0 }));
     }
 
     @action
@@ -204,7 +204,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
                     DragManager.StartDocumentDrag([tab], new DragManager.DocumentDragData([f]), e.pageX, e.pageY,
                         {
                             handlers: {
-                                dragComplete: action(() => { }),
+                                dragComplete: action(emptyFunction),
                             },
                             hideSource: false
                         });
diff --git a/src/client/views/collections/CollectionPDFView.tsx b/src/client/views/collections/CollectionPDFView.tsx
index 97bac745c..6cbe59012 100644
--- a/src/client/views/collections/CollectionPDFView.tsx
+++ b/src/client/views/collections/CollectionPDFView.tsx
@@ -7,6 +7,7 @@ import React = require("react");
 import { CollectionFreeFormView } from "./collectionFreeForm/CollectionFreeFormView";
 import { FieldView, FieldViewProps } from "../nodes/FieldView";
 import { CollectionRenderProps, CollectionBaseView, CollectionViewType } from "./CollectionBaseView";
+import { emptyFunction } from "../../../Utils";
 
 
 @observer
@@ -33,7 +34,7 @@ export class CollectionPDFView extends React.Component<FieldViewProps> {
 
     onContextMenu = (e: React.MouseEvent): void => {
         if (!e.isPropagationStopped() && this.props.Document.Id !== "mainDoc") { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7
-            ContextMenu.Instance.addItem({ description: "PDFOptions", event: () => { } });
+            ContextMenu.Instance.addItem({ description: "PDFOptions", event: emptyFunction });
         }
     }
 
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 587f60b3d..c67f8cd97 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -77,8 +77,8 @@ export class CollectionSchemaView extends CollectionSubView {
         let props: FieldViewProps = {
             Document: rowProps.value[0],
             fieldKey: rowProps.value[1],
-            isSelected: () => false,
-            select: () => { },
+            isSelected: returnFalse,
+            select: emptyFunction,
             isTopMost: false,
             selectOnLoad: false,
             ScreenToLocalTransform: Transform.Identity,
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 588ff9483..ef0684955 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -15,6 +15,7 @@ import { ServerUtils } from "../../../server/ServerUtil";
 import { Server } from "../../Server";
 import { FieldViewProps } from "../nodes/FieldView";
 import * as rp from 'request-promise';
+import { emptyFunction } from "../../../Utils";
 
 export interface CollectionViewProps extends FieldViewProps {
     addDocument: (document: Document, allowDuplicates?: boolean) => boolean;
@@ -213,7 +214,7 @@ export class CollectionSubView extends React.Component<SubCollectionViewProps> {
         }
 
         if (promises.length) {
-            Promise.all(promises).catch(() => { }).then(() => batch.end());
+            Promise.all(promises).catch(emptyFunction).then(() => batch.end());
         } else {
             batch.end();
         }
diff --git a/src/client/views/collections/CollectionVideoView.tsx b/src/client/views/collections/CollectionVideoView.tsx
index b02983a2e..6c9780adb 100644
--- a/src/client/views/collections/CollectionVideoView.tsx
+++ b/src/client/views/collections/CollectionVideoView.tsx
@@ -7,6 +7,7 @@ import React = require("react");
 import "./CollectionVideoView.scss";
 import { CollectionFreeFormView } from "./collectionFreeForm/CollectionFreeFormView";
 import { FieldView, FieldViewProps } from "../nodes/FieldView";
+import { emptyFunction } from "../../../Utils";
 
 
 @observer
@@ -100,7 +101,7 @@ export class CollectionVideoView extends React.Component<FieldViewProps> {
 
     onContextMenu = (e: React.MouseEvent): void => {
         if (!e.isPropagationStopped() && this.props.Document.Id !== "mainDoc") { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7
-            ContextMenu.Instance.addItem({ description: "VideoOptions", event: () => { } });
+            ContextMenu.Instance.addItem({ description: "VideoOptions", event: emptyFunction });
         }
     }
 
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 1feb30db1..102455d27 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -25,6 +25,7 @@ import { SelectionManager } from "../../../util/SelectionManager";
 import { NumberField } from "../../../../fields/NumberField";
 import { Main } from "../../Main";
 import Measure from "react-measure";
+import { returnFalse, emptyFunction } from "../../../../Utils";
 
 @observer
 export class CollectionFreeFormView extends CollectionSubView {
@@ -200,7 +201,7 @@ export class CollectionFreeFormView extends CollectionSubView {
 
     @action
     private SetPan(panX: number, panY: number) {
-        Main.Instance.SetTextDoc(undefined, undefined);
+        Main.Instance.SetTextDoc(undefined, undefined, undefined);
         var x1 = this.getLocalTransform().inverse().Scale;
         const newPanX = Math.min((1 - 1 / x1) * this.nativeWidth, Math.max(0, panX));
         const newPanY = Math.min((1 - 1 / x1) * this.nativeHeight, Math.max(0, panY));
@@ -290,13 +291,13 @@ export class CollectionFreeFormView extends CollectionSubView {
     get backgroundView() {
         return !this.backgroundLayout ? (null) :
             (<DocumentContentsView {...this.getDocumentViewProps(this.props.Document)}
-                layoutKey={KeyStore.BackgroundLayout} isTopMost={this.props.isTopMost} isSelected={() => false} select={() => { }} />);
+                layoutKey={KeyStore.BackgroundLayout} isTopMost={this.props.isTopMost} isSelected={returnFalse} select={emptyFunction} />);
     }
     @computed
     get overlayView() {
         return !this.overlayLayout ? (null) :
             (<DocumentContentsView {...this.getDocumentViewProps(this.props.Document)}
-                layoutKey={KeyStore.OverlayLayout} isTopMost={this.props.isTopMost} isSelected={() => false} select={() => { }} />);
+                layoutKey={KeyStore.OverlayLayout} isTopMost={this.props.isTopMost} isSelected={returnFalse} select={emptyFunction} />);
     }
 
     getTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-COLLECTION_BORDER_WIDTH, -COLLECTION_BORDER_WIDTH).translate(-this.centeringShiftX, -this.centeringShiftY).transform(this.getLocalTransform());
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 9670ca6b2..42967eb22 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1,4 +1,4 @@
-import { action, computed, IReactionDisposer, reaction, runInAction } from "mobx";
+import { action, computed, IReactionDisposer, reaction, runInAction, trace } from "mobx";
 import { observer } from "mobx-react";
 import { Document } from "../../../fields/Document";
 import { Field, FieldWaiting, Opt } from "../../../fields/Field";
@@ -8,7 +8,7 @@ import { ListField } from "../../../fields/ListField";
 import { BooleanField } from "../../../fields/BooleanField";
 import { TextField } from "../../../fields/TextField";
 import { ServerUtils } from "../../../server/ServerUtil";
-import { Utils } from "../../../Utils";
+import { Utils, emptyFunction } from "../../../Utils";
 import { Documents } from "../../documents/Documents";
 import { DocumentManager } from "../../util/DocumentManager";
 import { DragManager } from "../../util/DragManager";
@@ -62,12 +62,12 @@ export function FakeJsxArgs(keys: string[], fields: string[] = []): JsxArgs {
     let Keys: { [name: string]: any } = {};
     let Fields: { [name: string]: any } = {};
     for (const key of keys) {
-        let fn = () => { };
+        let fn = emptyFunction;
         Object.defineProperty(fn, "name", { value: key + "Key" });
         Keys[key] = fn;
     }
     for (const field of fields) {
-        let fn = () => { };
+        let fn = emptyFunction;
         Object.defineProperty(fn, "name", { value: field });
         Fields[field] = fn;
     }
@@ -157,7 +157,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
             dragData.moveDocument = this.props.moveDocument;
             DragManager.StartDocumentDrag([this._mainCont.current], dragData, x, y, {
                 handlers: {
-                    dragComplete: action(() => { })
+                    dragComplete: action(emptyFunction)
                 },
                 hideSource: !dropAliasOfDraggedDoc
             });
@@ -377,14 +377,28 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         SelectionManager.SelectDoc(this, ctrlPressed);
     }
 
+    @computed get nativeWidth(): number { return this.props.Document.GetNumber(KeyStore.NativeWidth, 0); }
+    @computed get nativeHeight(): number { return this.props.Document.GetNumber(KeyStore.NativeHeight, 0); }
+    @computed
+    get contents() {
+        trace();
+        return (<DocumentContentsView
+            {...this.props}
+            isSelected={this.isSelected}
+            select={this.select}
+            layoutKey={KeyStore.Layout}
+        />);
+    }
+
     render() {
         if (!this.props.Document) {
             return null;
         }
+        trace();
 
         var scaling = this.props.ContentScaling();
-        var nativeWidth = this.props.Document.GetNumber(KeyStore.NativeWidth, 0);
-        var nativeHeight = this.props.Document.GetNumber(KeyStore.NativeHeight, 0);
+        var nativeWidth = this.nativeWidth;
+        var nativeHeight = this.nativeHeight;
 
         if (this.isMinimized()) {
             return (
@@ -420,12 +434,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
                     onContextMenu={this.onContextMenu}
                     onPointerDown={this.onPointerDown}
                 >
-                    <DocumentContentsView
-                        {...this.props}
-                        isSelected={this.isSelected}
-                        select={this.select}
-                        layoutKey={KeyStore.Layout}
-                    />
+                    {this.contents}
                 </div>
             );
         }
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 07c5b332c..40b44aae5 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -19,7 +19,7 @@ import { ListField } from "../../../fields/ListField";
 import { DocumentContentsView } from "./DocumentContentsView";
 import { Transform } from "../../util/Transform";
 import { KeyStore } from "../../../fields/KeyStore";
-import { returnFalse } from "../../../Utils";
+import { returnFalse, emptyFunction } from "../../../Utils";
 
 
 //
@@ -85,9 +85,9 @@ export class FieldView extends React.Component<FieldViewProps> {
                     PanelHeight={() => 100}
                     isTopMost={true} //TODO Why is this top most?
                     selectOnLoad={false}
-                    focus={() => { }}
-                    isSelected={() => false}
-                    select={() => false}
+                    focus={emptyFunction}
+                    isSelected={returnFalse}
+                    select={returnFalse}
                     layoutKey={KeyStore.Layout}
                     ContainingCollectionView={undefined}
                     parentActive={this.props.active}
@@ -111,7 +111,7 @@ export class FieldView extends React.Component<FieldViewProps> {
         }
         else {
             return <p> {"Waiting for server..."} </p>;
- }
+        }
     }
 
 }
\ No newline at end of file
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index beca6cdc6..bb1bc4fb0 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -161,13 +161,13 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
         }
         if (this.props.fieldKey !== KeyStore.Archives) {
             e.preventDefault();
-            Main.Instance.SetTextDoc(this.props.Document, this._ref.current!);
+            Main.Instance.SetTextDoc(this.props.Document, this._ref.current!, this.props.ScreenToLocalTransform());
         }
     }
 
     onFocused = (e: React.FocusEvent): void => {
         if (this.props.fieldKey !== KeyStore.Archives) {
-            Main.Instance.SetTextDoc(this.props.Document, this._ref.current!);
+            Main.Instance.SetTextDoc(this.props.Document, this._ref.current!, this.props.ScreenToLocalTransform());
         }
     }
 
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 314af64c9..9d7c2bc56 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -66,7 +66,7 @@ export class VideoBox extends React.Component<FieldViewProps> {
             <Measure onResize={this.setScaling}>
                 {({ measureRef }) =>
                     <div style={{ width: "100%", height: "auto" }} ref={measureRef}>
-                        <video className="videobox-cont" onClick={() => { }} ref={this.setVideoRef}>
+                        <video className="videobox-cont" ref={this.setVideoRef}>
                             <source src={path} type="video/mp4" />
                             Not supported.
                         </video>
-- 
cgit v1.2.3-70-g09d2


From d4326a225f3e801c98e4acdc6558af1c3fe6ae07 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Wed, 10 Apr 2019 15:18:10 -0400
Subject: improved text editing.

---
 src/client/util/DragManager.ts              |  2 ++
 src/client/views/DocumentDecorations.tsx    |  2 ++
 src/client/views/Main.scss                  |  4 ++--
 src/client/views/Main.tsx                   | 18 +++++++++++++++---
 src/client/views/nodes/DocumentView.tsx     |  2 --
 src/client/views/nodes/FormattedTextBox.tsx | 10 +++++++---
 6 files changed, 28 insertions(+), 10 deletions(-)

(limited to 'src/client/views/nodes/FormattedTextBox.tsx')

diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index f7395578f..b8a35a4d1 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -5,6 +5,7 @@ import { CollectionView } from "../views/collections/CollectionView";
 import { DocumentDecorations } from "../views/DocumentDecorations";
 import { DocumentView } from "../views/nodes/DocumentView";
 import { returnFalse, emptyFunction } from "../../Utils";
+import { Main } from "../views/Main";
 
 export function setupDrag(_reference: React.RefObject<HTMLDivElement>, docFunc: () => Document, moveFunc?: DragManager.MoveFunction, copyOnDrop: boolean = false) {
     let onRowMove = action((e: PointerEvent): void => {
@@ -147,6 +148,7 @@ export namespace DragManager {
             dragDiv.className = "dragManager-dragDiv";
             DragManager.Root().appendChild(dragDiv);
         }
+        Main.Instance.SetTextDoc(undefined, undefined, undefined);
 
         let scaleXs: number[] = [];
         let scaleYs: number[] = [];
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index b7bf727f1..fa521b7e2 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -16,6 +16,7 @@ import { LinkMenu } from "./nodes/LinkMenu";
 import React = require("react");
 import { FieldWaiting } from "../../fields/Field";
 import { emptyFunction } from "../../Utils";
+import { Main } from "./Main";
 const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
@@ -321,6 +322,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
                 break;
         }
 
+        Main.Instance.SetTextDoc(undefined, undefined, undefined);
         SelectionManager.SelectedDocuments().forEach(element => {
             const rect = element.screenRect();
             if (rect.width !== 0) {
diff --git a/src/client/views/Main.scss b/src/client/views/Main.scss
index 8ef6b83f1..c4da47e0e 100644
--- a/src/client/views/Main.scss
+++ b/src/client/views/Main.scss
@@ -169,7 +169,7 @@ button:hover {
     overflow: scroll;
 }
 .mainDiv-textInput {
-    background-color: rgba(248, 6, 6, 0.1);
+    background-color: rgba(248, 6, 6, 0.001);
     width: 200px;
     height: 200px;
     position:absolute;
@@ -177,7 +177,7 @@ button:hover {
     top: 0;
     left: 0;
     .formattedTextBox-cont {
-        background-color: rgba(248, 6, 6, 0.1);
+        background-color: rgba(248, 6, 6, 0.001);
         width: 100%;
         height: 100%;
         position:absolute;
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index 2751b1899..e96e4cbf8 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -66,6 +66,7 @@ export class Main extends React.Component {
 
     constructor(props: Readonly<{}>) {
         super(props);
+        this._textProxyDiv = React.createRef();
         Main.Instance = this;
         // causes errors to be generated when modifying an observable outside of an action
         configure({ enforceActions: "observed" });
@@ -205,13 +206,25 @@ export class Main extends React.Component {
     @observable _textDoc?: Document = undefined;
     _textRect: any;
     _textXf: Transform = Transform.Identity();
+    _textScroll: number = 0;
+    _textTargetDiv: HTMLDivElement | undefined;
+    _textProxyDiv: React.RefObject<HTMLDivElement>;
     @action
     SetTextDoc(textDoc?: Document, div?: HTMLDivElement, tx?: Transform) {
         this._textDoc = undefined;
         this._textDoc = textDoc;
         this._textXf = tx ? tx : Transform.Identity();
+        this._textTargetDiv = div;
         if (div) {
             this._textRect = div.getBoundingClientRect();
+            this._textScroll = div.scrollTop;
+        }
+    }
+
+    @action
+    textScroll = (e: React.UIEvent) => {
+        if (this._textProxyDiv.current && this._textTargetDiv) {
+            this._textTargetDiv.scrollTop = this._textScroll = this._textProxyDiv.current.children[0].scrollTop
         }
     }
 
@@ -225,9 +238,8 @@ export class Main extends React.Component {
             let t = this._textXf.transformPoint(0, 0);
             let s = this._textXf.transformPoint(1, 0);
             s[0] = Math.sqrt((s[0] - t[0]) * (s[0] - t[0]) + (s[1] - t[1]) * (s[1] - t[1]));
-            return <div className="mainDiv-textInput" style={{ transform: `translate(${x}px, ${y}px) scale(${s[0]},${s[0]})` }} >
-                <div className="mainDiv-textInput" style={{ transform: `scale(${1 / s[0]}, ${1 / s[0]})`, width: `${w / s[0]}px`, height: `${h / s[0]}px` }}>
-
+            return <div className="mainDiv-textInput" style={{ transform: `translate(${x}px, ${y}px) scale(${1 / s[0]},${1 / s[0]})`, width: "auto", height: "auto" }} >
+                <div className="mainDiv-textInput" ref={this._textProxyDiv} onScroll={this.textScroll} style={{ transform: `scale(${1}, ${1})`, width: `${w * s[0]}px`, height: `${h * s[0]}px` }}>
                     <FormattedTextBox fieldKey={KeyStore.Archives} Document={this._textDoc} isSelected={returnTrue} select={emptyFunction} isTopMost={true} selectOnLoad={true} onActiveChanged={emptyFunction} active={returnTrue} ScreenToLocalTransform={Transform.Identity} focus={(doc) => { }} />
 
                 </div>
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 42967eb22..6b585ec4b 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -381,7 +381,6 @@ export class DocumentView extends React.Component<DocumentViewProps> {
     @computed get nativeHeight(): number { return this.props.Document.GetNumber(KeyStore.NativeHeight, 0); }
     @computed
     get contents() {
-        trace();
         return (<DocumentContentsView
             {...this.props}
             isSelected={this.isSelected}
@@ -394,7 +393,6 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         if (!this.props.Document) {
             return null;
         }
-        trace();
 
         var scaling = this.props.ContentScaling();
         var nativeWidth = this.nativeWidth;
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index bb1bc4fb0..eb40a03a5 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -92,7 +92,7 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
 
         this._reactionDisposer = reaction(
             () => {
-                const field = this.FieldDoc.GetT(this.FieldKey, RichTextField);
+                const field = this.FieldDoc ? this.FieldDoc.GetT(this.FieldKey, RichTextField) : undefined;
                 return field && field !== FieldWaiting ? field.Data : undefined;
             },
             field => {
@@ -107,9 +107,8 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
     }
 
     private setupEditor(config: any) {
-
         let state: EditorState;
-        let field = this.FieldDoc.GetT(this.FieldKey, RichTextField);
+        let field = this.FieldDoc ? this.FieldDoc.GetT(this.FieldKey, RichTextField) : undefined;
         if (field && field !== FieldWaiting && field.Data) {
             state = EditorState.fromJSON(config, JSON.parse(field.Data));
         } else {
@@ -168,6 +167,10 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
     onFocused = (e: React.FocusEvent): void => {
         if (this.props.fieldKey !== KeyStore.Archives) {
             Main.Instance.SetTextDoc(this.props.Document, this._ref.current!, this.props.ScreenToLocalTransform());
+        } else {
+            if (this._ref.current) {
+                this._ref.current.scrollTop = Main.Instance._textScroll;
+            }
         }
     }
 
@@ -219,6 +222,7 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
                 onKeyPress={this.onKeyPress}
                 onPointerUp={this.onPointerUp}
                 onPointerDown={this.onPointerDown}
+                onFocus={this.onFocused}
                 onContextMenu={this.specificContextMenu}
                 // tfs: do we need this event handler
                 onWheel={this.onPointerWheel}
-- 
cgit v1.2.3-70-g09d2


From 4e45ad641bd34c4703188e69a93a23243f3659a4 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Wed, 10 Apr 2019 21:35:12 -0400
Subject: fixed text menu and text dragging.

---
 src/client/util/TooltipTextMenu.tsx         | 14 +++++++++-----
 src/client/views/DocumentDecorations.scss   |  3 +++
 src/client/views/Main.scss                  |  1 +
 src/client/views/Main.tsx                   | 23 +++++++++++++++++++----
 src/client/views/nodes/FormattedTextBox.tsx | 13 +++++++++----
 5 files changed, 41 insertions(+), 13 deletions(-)

(limited to 'src/client/views/nodes/FormattedTextBox.tsx')

diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx
index bd5753093..7951e5686 100644
--- a/src/client/util/TooltipTextMenu.tsx
+++ b/src/client/util/TooltipTextMenu.tsx
@@ -12,19 +12,23 @@ const { toggleMark, setBlockType, wrapIn } = require("prosemirror-commands");
 import { library } from '@fortawesome/fontawesome-svg-core';
 import { wrapInList, bulletList } from 'prosemirror-schema-list';
 import { faListUl } from '@fortawesome/free-solid-svg-icons';
+import { FieldViewProps } from "../views/nodes/FieldView";
+import { throwStatement } from "babel-types";
 
 
 //appears above a selection of text in a RichTextBox to give user options such as Bold, Italics, etc.
 export class TooltipTextMenu {
 
     private tooltip: HTMLElement;
+    private editorProps: FieldViewProps;
 
-    constructor(view: EditorView) {
+    constructor(view: EditorView, editorProps: FieldViewProps) {
+        this.editorProps = editorProps;
         this.tooltip = document.createElement("div");
         this.tooltip.className = "tooltipMenu";
 
         //add the div which is the tooltip
-        view.dom.parentNode!.appendChild(this.tooltip);
+        view.dom.parentNode!.parentNode!.appendChild(this.tooltip);
 
         //add additional icons
         library.add(faListUl);
@@ -124,13 +128,13 @@ export class TooltipTextMenu {
         // Find a center-ish x position from the selection endpoints (when
         // crossing lines, end may be more to the left)
         let left = Math.max((start.left + end.left) / 2, start.left + 3);
-        this.tooltip.style.left = (left - box.left) + "px";
-        let width = Math.abs(start.left - end.left) / 2;
+        this.tooltip.style.left = (left - box.left) * this.editorProps.ScreenToLocalTransform().Scale + "px";
+        let width = Math.abs(start.left - end.left) / 2 * this.editorProps.ScreenToLocalTransform().Scale;
         let mid = Math.min(start.left, end.left) + width;
 
         //THIS WIDTH IS 15 * NUMBER OF ICONS + 15
         this.tooltip.style.width = 122 + "px";
-        this.tooltip.style.bottom = (box.bottom - start.top) + "px";
+        this.tooltip.style.bottom = (box.bottom - start.top) * this.editorProps.ScreenToLocalTransform().Scale + "px";
     }
 
     destroy() { this.tooltip.remove(); }
diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss
index c4e4aed8e..b2096bccb 100644
--- a/src/client/views/DocumentDecorations.scss
+++ b/src/client/views/DocumentDecorations.scss
@@ -1,5 +1,8 @@
 @import "global_variables";
 
+.documentDecorations {
+    position: absolute;
+}
 #documentDecorations-container {
     position: absolute;
     top: 0;
diff --git a/src/client/views/Main.scss b/src/client/views/Main.scss
index c4da47e0e..a68b90dd4 100644
--- a/src/client/views/Main.scss
+++ b/src/client/views/Main.scss
@@ -176,6 +176,7 @@ button:hover {
     overflow: visible;
     top: 0;
     left: 0;
+    z-index: 1000;
     .formattedTextBox-cont {
         background-color: rgba(248, 6, 6, 0.001);
         width: 100%;
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index e96e4cbf8..b73b55fb1 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -28,7 +28,7 @@ import '../northstar/model/ModelExtensions';
 import { HistogramOperation } from '../northstar/operations/HistogramOperation';
 import '../northstar/utils/Extensions';
 import { Server } from '../Server';
-import { setupDrag } from '../util/DragManager';
+import { setupDrag, DragManager } from '../util/DragManager';
 import { Transform } from '../util/Transform';
 import { UndoManager } from '../util/UndoManager';
 import { CollectionDockingView } from './collections/CollectionDockingView';
@@ -38,6 +38,7 @@ import { InkingControl } from './InkingControl';
 import "./Main.scss";
 import { DocumentView } from './nodes/DocumentView';
 import { FormattedTextBox } from './nodes/FormattedTextBox';
+import { REPLCommand } from 'repl';
 
 @observer
 export class Main extends React.Component {
@@ -228,6 +229,21 @@ export class Main extends React.Component {
         }
     }
 
+    textBoxDown = (e: React.PointerEvent) => {
+        let dragData = new DragManager.DocumentDragData([this._textDoc!]);
+        const [left, top] = this._textXf
+            .inverse()
+            .transformPoint(0, 0);
+        dragData.xOffset = e.clientX - left;
+        dragData.yOffset = e.clientY - top;
+        DragManager.StartDocumentDrag([this._textTargetDiv!], dragData, e.clientX, e.clientY, {
+            handlers: {
+                dragComplete: action(emptyFunction),
+            },
+            hideSource: false
+        });
+    }
+
     @computed
     get activeTextBox() {
         if (this._textDoc) {
@@ -239,9 +255,8 @@ export class Main extends React.Component {
             let s = this._textXf.transformPoint(1, 0);
             s[0] = Math.sqrt((s[0] - t[0]) * (s[0] - t[0]) + (s[1] - t[1]) * (s[1] - t[1]));
             return <div className="mainDiv-textInput" style={{ transform: `translate(${x}px, ${y}px) scale(${1 / s[0]},${1 / s[0]})`, width: "auto", height: "auto" }} >
-                <div className="mainDiv-textInput" ref={this._textProxyDiv} onScroll={this.textScroll} style={{ transform: `scale(${1}, ${1})`, width: `${w * s[0]}px`, height: `${h * s[0]}px` }}>
-                    <FormattedTextBox fieldKey={KeyStore.Archives} Document={this._textDoc} isSelected={returnTrue} select={emptyFunction} isTopMost={true} selectOnLoad={true} onActiveChanged={emptyFunction} active={returnTrue} ScreenToLocalTransform={Transform.Identity} focus={(doc) => { }} />
-
+                <div className="mainDiv-textInput" onPointerDown={this.textBoxDown} ref={this._textProxyDiv} onScroll={this.textScroll} style={{ transform: `scale(${1}, ${1})`, width: `${w * s[0]}px`, height: `${h * s[0]}px` }}>
+                    <FormattedTextBox fieldKey={KeyStore.Archives} Document={this._textDoc} isSelected={returnTrue} select={emptyFunction} isTopMost={true} selectOnLoad={true} onActiveChanged={emptyFunction} active={returnTrue} ScreenToLocalTransform={() => this._textXf} focus={(doc) => { }} />
                 </div>
             </ div>;
         }
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index eb40a03a5..69086df42 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -70,12 +70,16 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
         const config = {
             schema,
             inpRules, //these currently don't do anything, but could eventually be helpful
-            plugins: [
+            plugins: this.props.fieldKey === KeyStore.Archives ? [
                 history(),
                 keymap({ "Mod-z": undo, "Mod-y": redo }),
                 keymap(baseKeymap),
                 this.tooltipMenuPlugin()
-            ]
+            ] : [
+                    history(),
+                    keymap({ "Mod-z": undo, "Mod-y": redo }),
+                    keymap(baseKeymap),
+                ]
         };
 
         if (this.props.fieldKey === KeyStore.Archives) {
@@ -150,7 +154,7 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
         // doc.SetData(fieldKey, e.target.value, RichTextField);
     }
     onPointerDown = (e: React.PointerEvent): void => {
-        if (e.buttons === 1 && this.props.isSelected() && !e.altKey) {
+        if (e.buttons === 1 && this.props.isSelected() && !e.altKey && !e.ctrlKey && !e.metaKey) {
             e.stopPropagation();
         }
     }
@@ -202,9 +206,10 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
     }
 
     tooltipMenuPlugin() {
+        let myprops = this.props;
         return new Plugin({
             view(_editorView) {
-                return new TooltipTextMenu(_editorView);
+                return new TooltipTextMenu(_editorView, myprops);
             }
         });
     }
-- 
cgit v1.2.3-70-g09d2


From a006717f7a847c1a230a334fa645b7382aa067d2 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Wed, 10 Apr 2019 23:44:10 -0400
Subject: switched text proxy to appear on selection, not pointerdown

---
 src/client/views/nodes/FormattedTextBox.tsx | 15 ++++++++++-----
 1 file changed, 10 insertions(+), 5 deletions(-)

(limited to 'src/client/views/nodes/FormattedTextBox.tsx')

diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 69086df42..17125a298 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -42,6 +42,7 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
     private _editorView: Opt<EditorView>;
     private _reactionDisposer: Opt<IReactionDisposer>;
     private _inputReactionDisposer: Opt<IReactionDisposer>;
+    private _proxyReactionDisposer: Opt<IReactionDisposer>;
 
     constructor(props: FieldViewProps) {
         super(props);
@@ -92,6 +93,11 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
                     this.setupEditor(config);
                 }
             );
+        } else {
+            this._proxyReactionDisposer = reaction(() => this.props.isSelected(),
+                () =>
+                    this.props.isSelected() && Main.Instance.SetTextDoc(this.props.Document, this._ref.current!, this.props.ScreenToLocalTransform())
+            );
         }
 
         this._reactionDisposer = reaction(
@@ -141,6 +147,9 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
         if (this._inputReactionDisposer) {
             this._inputReactionDisposer();
         }
+        if (this._proxyReactionDisposer) {
+            this._proxyReactionDisposer();
+        }
     }
 
     shouldComponentUpdate() {
@@ -154,7 +163,7 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
         // doc.SetData(fieldKey, e.target.value, RichTextField);
     }
     onPointerDown = (e: React.PointerEvent): void => {
-        if (e.buttons === 1 && this.props.isSelected() && !e.altKey && !e.ctrlKey && !e.metaKey) {
+        if (e.button === 1 && this.props.isSelected() && !e.altKey && !e.ctrlKey && !e.metaKey) {
             e.stopPropagation();
         }
     }
@@ -162,10 +171,6 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
         if (e.buttons === 1 && this.props.isSelected() && !e.altKey) {
             e.stopPropagation();
         }
-        if (this.props.fieldKey !== KeyStore.Archives) {
-            e.preventDefault();
-            Main.Instance.SetTextDoc(this.props.Document, this._ref.current!, this.props.ScreenToLocalTransform());
-        }
     }
 
     onFocused = (e: React.FocusEvent): void => {
-- 
cgit v1.2.3-70-g09d2


From e5411598c4d2ecc7a0b4d8584b576ff5f45ad2a0 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 11 Apr 2019 01:05:12 -0400
Subject: tried to fix some context menu and other conflicting interactions

---
 src/client/util/SelectionManager.ts                          |  2 +-
 src/client/views/Main.tsx                                    |  4 ++--
 .../collectionFreeForm/CollectionFreeFormView.tsx            |  3 ---
 src/client/views/nodes/DocumentView.tsx                      |  3 ++-
 src/client/views/nodes/FormattedTextBox.scss                 |  1 +
 src/client/views/nodes/FormattedTextBox.tsx                  | 12 ++++++++----
 6 files changed, 14 insertions(+), 11 deletions(-)

(limited to 'src/client/views/nodes/FormattedTextBox.tsx')

diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index c6b8c7b0d..2638e3b7d 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -25,6 +25,7 @@ export namespace SelectionManager {
         DeselectAll(): void {
             manager.SelectedDocuments.map(dv => dv.props.onActiveChanged(false));
             manager.SelectedDocuments = [];
+            Main.Instance.SetTextDoc(undefined, undefined, undefined);
         }
     }
 
@@ -48,7 +49,6 @@ export namespace SelectionManager {
 
         manager.DeselectAll();
         if (found) manager.SelectDoc(found, false);
-        Main.Instance.SetTextDoc(undefined, undefined, undefined);
     }
 
     export function SelectedDocuments(): Array<DocumentView> {
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index 8153250c7..b9e209b3c 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -274,8 +274,8 @@ export class Main extends React.Component {
             let t = this._textXf.transformPoint(0, 0);
             let s = this._textXf.transformPoint(1, 0);
             s[0] = Math.sqrt((s[0] - t[0]) * (s[0] - t[0]) + (s[1] - t[1]) * (s[1] - t[1]));
-            return <div className="mainDiv-textInput" style={{ transform: `translate(${x}px, ${y}px) scale(${1 / s[0]},${1 / s[0]})`, width: "auto", height: "auto" }} >
-                <div className="mainDiv-textInput" onPointerDown={this.textBoxDown} ref={this._textProxyDiv} onScroll={this.textScroll} style={{ transform: `scale(${1}, ${1})`, width: `${w * s[0]}px`, height: `${h * s[0]}px` }}>
+            return <div className="mainDiv-textInput" style={{ pointerEvents: "none", transform: `translate(${x}px, ${y}px) scale(${1 / s[0]},${1 / s[0]})`, width: "auto", height: "auto" }} >
+                <div className="mainDiv-textInput" onPointerDown={this.textBoxDown} ref={this._textProxyDiv} onScroll={this.textScroll} style={{ pointerEvents: "none", transform: `scale(${1}, ${1})`, width: `${w * s[0]}px`, height: `${h * s[0]}px` }}>
                     <FormattedTextBox fieldKey={KeyStore.Archives} Document={this._textDoc} isSelected={returnTrue} select={emptyFunction} isTopMost={true} selectOnLoad={true} onActiveChanged={emptyFunction} active={returnTrue} ScreenToLocalTransform={() => this._textXf} focus={(doc) => { }} />
                 </div>
             </ div>;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index db8c370df..4e1164ee5 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -135,9 +135,6 @@ export class CollectionFreeFormView extends CollectionSubView {
             document.addEventListener("pointerup", this.onPointerUp);
             this._lastX = this.DownX = e.pageX;
             this._lastY = this.DownY = e.pageY;
-            if (this.props.isSelected()) {
-                e.stopPropagation();
-            }
         }
     }
 
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 6b585ec4b..1465d589c 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -105,7 +105,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
             }
             e.stopPropagation();
         } else {
-            if (this.active && !e.isDefaultPrevented()) {
+            if (this.active) {
                 e.stopPropagation();
                 document.removeEventListener("pointermove", this.onPointerMove);
                 document.addEventListener("pointermove", this.onPointerMove);
@@ -186,6 +186,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         document.removeEventListener("pointerup", this.onPointerUp);
         e.stopPropagation();
         if (!SelectionManager.IsSelected(this) &&
+            e.button != 2 &&
             Math.abs(e.clientX - this._downX) < 4 &&
             Math.abs(e.clientY - this._downY) < 4
         ) {
diff --git a/src/client/views/nodes/FormattedTextBox.scss b/src/client/views/nodes/FormattedTextBox.scss
index 32da2632e..d2ba52cf9 100644
--- a/src/client/views/nodes/FormattedTextBox.scss
+++ b/src/client/views/nodes/FormattedTextBox.scss
@@ -22,6 +22,7 @@
   overflow-x: hidden;
   color: initial;
   height: 100%;
+  pointer-events: all;
 }
 
 .menuicon {
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 17125a298..14b22e9f5 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -14,6 +14,7 @@ import { ContextMenu } from "../../views/ContextMenu";
 import { Main } from "../Main";
 import { FieldView, FieldViewProps } from "./FieldView";
 import "./FormattedTextBox.scss";
+import { emptyFunction } from '../../../Utils';
 import React = require("react");
 const { buildMenuItems } = require("prosemirror-example-setup");
 const { menuBar } = require("prosemirror-menu");
@@ -95,8 +96,10 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
             );
         } else {
             this._proxyReactionDisposer = reaction(() => this.props.isSelected(),
-                () =>
-                    this.props.isSelected() && Main.Instance.SetTextDoc(this.props.Document, this._ref.current!, this.props.ScreenToLocalTransform())
+                () => {
+                    if (this.props.isSelected())
+                        Main.Instance.SetTextDoc(this.props.Document, this._ref.current!, this.props.ScreenToLocalTransform());
+                }
             );
         }
 
@@ -166,6 +169,8 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
         if (e.button === 1 && this.props.isSelected() && !e.altKey && !e.ctrlKey && !e.metaKey) {
             e.stopPropagation();
         }
+        if (e.button === 2)
+            e.preventDefault();
     }
     onPointerUp = (e: React.PointerEvent): void => {
         if (e.buttons === 1 && this.props.isSelected() && !e.altKey) {
@@ -227,12 +232,11 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
     render() {
         return (
             <div
-                className="formattedTextBox-cont"
+                className={`formattedTextBox-cont`}
                 onKeyDown={this.onKeyPress}
                 onKeyPress={this.onKeyPress}
                 onPointerUp={this.onPointerUp}
                 onPointerDown={this.onPointerDown}
-                onFocus={this.onFocused}
                 onContextMenu={this.specificContextMenu}
                 // tfs: do we need this event handler
                 onWheel={this.onPointerWheel}
-- 
cgit v1.2.3-70-g09d2


From cbe23091b72c472be3e9a6de1cac5325171c282f Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 11 Apr 2019 08:51:39 -0400
Subject: fixed zoom not always rendering.   fixed documents (images, pdfs) not
 appearing.  fixed lint warnings.

---
 src/client/views/Main.tsx                                    |  7 ++++---
 .../collectionFreeForm/CollectionFreeFormView.tsx            | 12 +++++++-----
 src/client/views/nodes/DocumentView.tsx                      |  2 +-
 src/client/views/nodes/FormattedTextBox.tsx                  |  9 +++------
 4 files changed, 15 insertions(+), 15 deletions(-)

(limited to 'src/client/views/nodes/FormattedTextBox.tsx')

diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index b9e209b3c..5fd778c7e 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -213,8 +213,9 @@ export class Main extends React.Component {
     _textProxyDiv: React.RefObject<HTMLDivElement>;
     @action
     SetTextDoc(textDoc?: Document, div?: HTMLDivElement, tx?: Transform) {
-        if (this._textTargetDiv)
+        if (this._textTargetDiv) {
             this._textTargetDiv.style.color = this._textColor;
+        }
 
         this._textDoc = undefined;
         this._textDoc = textDoc;
@@ -231,12 +232,12 @@ export class Main extends React.Component {
     @action
     textScroll = (e: React.UIEvent) => {
         if (this._textProxyDiv.current && this._textTargetDiv) {
-            this._textTargetDiv.scrollTop = this._textScroll = this._textProxyDiv.current.children[0].scrollTop
+            this._textTargetDiv.scrollTop = this._textScroll = this._textProxyDiv.current.children[0].scrollTop;
         }
     }
 
     textBoxDown = (e: React.PointerEvent) => {
-        if (e.button != 0 || e.metaKey || e.altKey) {
+        if (e.button !== 0 || e.metaKey || e.altKey) {
             document.addEventListener("pointermove", this.textBoxMove);
             document.addEventListener('pointerup', this.textBoxUp);
         }
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 4e1164ee5..b40e36e77 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -304,10 +304,12 @@ export class CollectionFreeFormView extends CollectionSubView {
     childViews = () => this.views;
 
     render() {
-        let [dx, dy] = [this.centeringShiftX, this.centeringShiftY];
-
+        const [dx, dy] = [this.centeringShiftX, this.centeringShiftY];
         const panx: number = -this.props.Document.GetNumber(KeyStore.PanX, 0);
         const pany: number = -this.props.Document.GetNumber(KeyStore.PanY, 0);
+        const zoom: number = this.zoomScaling;
+        const blay = this.backgroundView;
+        const olay = this.overlayView;
 
         return (
             <Measure onResize={(r: any) => runInAction(() => { this._pwidth = r.entry.width; this._pheight = r.entry.height; })}>
@@ -323,8 +325,8 @@ export class CollectionFreeFormView extends CollectionSubView {
                                 <PreviewCursor container={this} addLiveTextDocument={this.addLiveTextBox}
                                     getContainerTransform={this.getContainerTransform} getTransform={this.getTransform} >
                                     <div className="collectionfreeformview" ref={this._canvasRef}
-                                        style={{ transform: `translate(${dx}px, ${dy}px) scale(${this.zoomScaling}, ${this.zoomScaling}) translate(${panx}px, ${pany}px)` }}>
-                                        {this.backgroundView}
+                                        style={{ transform: `translate(${dx}px, ${dy}px) scale(${zoom}, ${zoom}) translate(${panx}px, ${pany}px)` }}>
+                                        {blay}
                                         <CollectionFreeFormLinksView {...this.props}>
                                             <InkingCanvas getScreenTransform={this.getTransform} Document={this.props.Document} >
                                                 {this.childViews}
@@ -332,7 +334,7 @@ export class CollectionFreeFormView extends CollectionSubView {
                                         </CollectionFreeFormLinksView>
                                         <CollectionFreeFormRemoteCursors {...this.props} />
                                     </div>
-                                    {this.overlayView}
+                                    {olay}
                                 </PreviewCursor>
                             </MarqueeView>
                         </div>
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 1465d589c..9c31a83c1 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -186,7 +186,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         document.removeEventListener("pointerup", this.onPointerUp);
         e.stopPropagation();
         if (!SelectionManager.IsSelected(this) &&
-            e.button != 2 &&
+            e.button !== 2 &&
             Math.abs(e.clientX - this._downX) < 4 &&
             Math.abs(e.clientY - this._downY) < 4
         ) {
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 14b22e9f5..468cbcd1e 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -96,11 +96,7 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
             );
         } else {
             this._proxyReactionDisposer = reaction(() => this.props.isSelected(),
-                () => {
-                    if (this.props.isSelected())
-                        Main.Instance.SetTextDoc(this.props.Document, this._ref.current!, this.props.ScreenToLocalTransform());
-                }
-            );
+                () => this.props.isSelected() && Main.Instance.SetTextDoc(this.props.Document, this._ref.current!, this.props.ScreenToLocalTransform()));
         }
 
         this._reactionDisposer = reaction(
@@ -169,8 +165,9 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
         if (e.button === 1 && this.props.isSelected() && !e.altKey && !e.ctrlKey && !e.metaKey) {
             e.stopPropagation();
         }
-        if (e.button === 2)
+        if (e.button === 2) {
             e.preventDefault();
+        }
     }
     onPointerUp = (e: React.PointerEvent): void => {
         if (e.buttons === 1 && this.props.isSelected() && !e.altKey) {
-- 
cgit v1.2.3-70-g09d2


From 50be8cb7a93110821c972c679567ddb6aae8bc6f Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Thu, 11 Apr 2019 11:56:40 -0400
Subject: made css globals avabile from code.  cleaned up formattedText overlay
 api..

---
 src/client/util/DragManager.ts                     |  12 +-
 src/client/util/SelectionManager.ts                |   2 +-
 src/client/views/DocumentDecorations.tsx           |   2 +-
 src/client/views/Main.tsx                          |   7 +-
 src/client/views/_global_variables.scss            |   2 +-
 src/client/views/_global_variables.scss.d.ts       |  11 +-
 src/client/views/_global_variables.ts              |   8 +
 .../collectionFreeForm/CollectionFreeFormView.tsx  |   2 +-
 src/client/views/nodes/FormattedTextBox.tsx        |  31 ++--
 tsconfig.json                                      |  42 ++---
 webpack.config.js                                  | 180 ++++++++++-----------
 11 files changed, 154 insertions(+), 145 deletions(-)
 create mode 100644 src/client/views/_global_variables.ts

(limited to 'src/client/views/nodes/FormattedTextBox.tsx')

diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index e500b0274..d66c6e90f 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -1,12 +1,12 @@
 import { action } from "mobx";
 import { Document } from "../../fields/Document";
+import { emptyFunction } from "../../Utils";
 import { CollectionDockingView } from "../views/collections/CollectionDockingView";
-import { CollectionView } from "../views/collections/CollectionView";
 import { DocumentDecorations } from "../views/DocumentDecorations";
-import { DocumentView } from "../views/nodes/DocumentView";
-import { returnFalse, emptyFunction } from "../../Utils";
 import { Main } from "../views/Main";
-import globalStyles from '../views/_global_variables.scss';
+import { DocumentView } from "../views/nodes/DocumentView";
+import globalStyles from "../views/_global_variables";
+// import globalStyleVariables from "../views/_global_variables.scss"; // bcz: why doesn't this work?
 
 export function setupDrag(_reference: React.RefObject<HTMLDivElement>, docFunc: () => Document, moveFunc?: DragManager.MoveFunction, copyOnDrop: boolean = false) {
     let onRowMove = action((e: PointerEvent): void => {
@@ -149,7 +149,7 @@ export namespace DragManager {
             dragDiv.className = "dragManager-dragDiv";
             DragManager.Root().appendChild(dragDiv);
         }
-        Main.Instance.SetTextDoc(undefined, undefined, undefined);
+        Main.Instance.SetTextDoc();
 
         let scaleXs: number[] = [];
         let scaleYs: number[] = [];
@@ -178,7 +178,7 @@ export namespace DragManager {
             dragElement.style.bottom = "";
             dragElement.style.left = "0";
             dragElement.style.transformOrigin = "0 0";
-            dragElement.style.zIndex = "1000";// globalStyles.contextMenuZindex.toString();
+            dragElement.style.zIndex = globalStyles.contextMenuZindex;// "1000";
             dragElement.style.transform = `translate(${x}px, ${y}px) scale(${scaleX}, ${scaleY})`;
             dragElement.style.width = `${rect.width / scaleX}px`;
             dragElement.style.height = `${rect.height / scaleY}px`;
diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index 2638e3b7d..2fa45a086 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -25,7 +25,7 @@ export namespace SelectionManager {
         DeselectAll(): void {
             manager.SelectedDocuments.map(dv => dv.props.onActiveChanged(false));
             manager.SelectedDocuments = [];
-            Main.Instance.SetTextDoc(undefined, undefined, undefined);
+            Main.Instance.SetTextDoc();
         }
     }
 
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index fa521b7e2..29cca286d 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -322,7 +322,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
                 break;
         }
 
-        Main.Instance.SetTextDoc(undefined, undefined, undefined);
+        Main.Instance.SetTextDoc();
         SelectionManager.SelectedDocuments().forEach(element => {
             const rect = element.screenRect();
             if (rect.width !== 0) {
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index 5fd778c7e..c4c4a6bf9 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -39,6 +39,7 @@ import "./Main.scss";
 import { DocumentView } from './nodes/DocumentView';
 import { FormattedTextBox } from './nodes/FormattedTextBox';
 import { REPLCommand } from 'repl';
+import { Key } from '../../fields/Key';
 
 @observer
 export class Main extends React.Component {
@@ -208,17 +209,19 @@ export class Main extends React.Component {
     _textRect: any;
     _textXf: Transform = Transform.Identity();
     _textScroll: number = 0;
+    _textFieldKey: Key = KeyStore.Data;
     _textColor: string | null = null;
     _textTargetDiv: HTMLDivElement | undefined;
     _textProxyDiv: React.RefObject<HTMLDivElement>;
     @action
-    SetTextDoc(textDoc?: Document, div?: HTMLDivElement, tx?: Transform) {
+    SetTextDoc(textDoc?: Document, textFieldKey?: Key, div?: HTMLDivElement, tx?: Transform) {
         if (this._textTargetDiv) {
             this._textTargetDiv.style.color = this._textColor;
         }
 
         this._textDoc = undefined;
         this._textDoc = textDoc;
+        this._textFieldKey = textFieldKey!;
         this._textXf = tx ? tx : Transform.Identity();
         this._textTargetDiv = div;
         if (div) {
@@ -277,7 +280,7 @@ export class Main extends React.Component {
             s[0] = Math.sqrt((s[0] - t[0]) * (s[0] - t[0]) + (s[1] - t[1]) * (s[1] - t[1]));
             return <div className="mainDiv-textInput" style={{ pointerEvents: "none", transform: `translate(${x}px, ${y}px) scale(${1 / s[0]},${1 / s[0]})`, width: "auto", height: "auto" }} >
                 <div className="mainDiv-textInput" onPointerDown={this.textBoxDown} ref={this._textProxyDiv} onScroll={this.textScroll} style={{ pointerEvents: "none", transform: `scale(${1}, ${1})`, width: `${w * s[0]}px`, height: `${h * s[0]}px` }}>
-                    <FormattedTextBox fieldKey={KeyStore.Archives} Document={this._textDoc} isSelected={returnTrue} select={emptyFunction} isTopMost={true} selectOnLoad={true} onActiveChanged={emptyFunction} active={returnTrue} ScreenToLocalTransform={() => this._textXf} focus={(doc) => { }} />
+                    <FormattedTextBox fieldKey={this._textFieldKey!} isOverlay={true} Document={this._textDoc} isSelected={returnTrue} select={emptyFunction} isTopMost={true} selectOnLoad={true} onActiveChanged={emptyFunction} active={returnTrue} ScreenToLocalTransform={() => this._textXf} focus={(doc) => { }} />
                 </div>
             </ div>;
         }
diff --git a/src/client/views/_global_variables.scss b/src/client/views/_global_variables.scss
index 238351a77..cd6af2dac 100644
--- a/src/client/views/_global_variables.scss
+++ b/src/client/views/_global_variables.scss
@@ -24,5 +24,5 @@ $docDecorations-zindex: 998; // then doc decorations appear over everything else
 $remoteCursors-zindex: 997; // ... not sure what level the remote cursors should go -- is this right?
 
 :export  {
-    contextMenuZindex: $contextMenu-zindex
+    contextMenuZindex: $contextMenu-zindex;
 }
\ No newline at end of file
diff --git a/src/client/views/_global_variables.scss.d.ts b/src/client/views/_global_variables.scss.d.ts
index 12008aeef..c902d473f 100644
--- a/src/client/views/_global_variables.scss.d.ts
+++ b/src/client/views/_global_variables.scss.d.ts
@@ -1,10 +1,7 @@
+
 export interface I_globalScss {
-    contextMenuZindex: number;  // context menu shows up over everything
-    mainTextInputZindex: number; // then text input overlay so that it's context menu will appear over decorations, etc
-    docDecorationsZindex: number; // then doc decorations appear over everything else
-    remoteCursorsZindex: number; // ... not sure what level the remote cursors should go -- is this right?
+    contextMenuZindex: string;  // context menu shows up over everything
 }
+export const globalStyleVariables: I_globalScss;
 
-export const globalStyles: I_globalScss;
-
-export default globalStyles;
\ No newline at end of file
+export default globalStyleVariables;
\ No newline at end of file
diff --git a/src/client/views/_global_variables.ts b/src/client/views/_global_variables.ts
new file mode 100644
index 000000000..e70bfd56c
--- /dev/null
+++ b/src/client/views/_global_variables.ts
@@ -0,0 +1,8 @@
+import * as globalStyleVariables from "../views/_global_variables.scss"
+
+export interface I_globalScss {
+    contextMenuZindex: string;  // context menu shows up over everything
+}
+let globalStyles = globalStyleVariables as any as I_globalScss;
+
+export default globalStyles;
\ No newline at end of file
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index b40e36e77..01ebbe0e1 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -198,7 +198,7 @@ export class CollectionFreeFormView extends CollectionSubView {
 
     @action
     private SetPan(panX: number, panY: number) {
-        Main.Instance.SetTextDoc(undefined, undefined, undefined);
+        Main.Instance.SetTextDoc();
         var x1 = this.getLocalTransform().inverse().Scale;
         const newPanX = Math.min((1 - 1 / x1) * this.nativeWidth, Math.max(0, panX));
         const newPanY = Math.min((1 - 1 / x1) * this.nativeHeight, Math.max(0, panY));
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 468cbcd1e..8ea747b1c 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -5,7 +5,6 @@ import { keymap } from "prosemirror-keymap";
 import { EditorState, Plugin, Transaction } from "prosemirror-state";
 import { EditorView } from "prosemirror-view";
 import { FieldWaiting, Opt } from "../../../fields/Field";
-import { KeyStore } from "../../../fields/KeyStore";
 import { RichTextField } from "../../../fields/RichTextField";
 import { inpRules } from "../../util/RichTextRules";
 import { schema } from "../../util/RichTextSchema";
@@ -14,7 +13,6 @@ import { ContextMenu } from "../../views/ContextMenu";
 import { Main } from "../Main";
 import { FieldView, FieldViewProps } from "./FieldView";
 import "./FormattedTextBox.scss";
-import { emptyFunction } from '../../../Utils';
 import React = require("react");
 const { buildMenuItems } = require("prosemirror-example-setup");
 const { menuBar } = require("prosemirror-menu");
@@ -35,7 +33,12 @@ const { menuBar } = require("prosemirror-menu");
 //  specified Key and assigns it to an HTML input node.  When changes are made to this node,
 //  this will edit the document and assign the new value to that field.
 //]
-export class FormattedTextBox extends React.Component<FieldViewProps> {
+
+export interface FormattedTextBoxOverlay {
+    isOverlay?: boolean;
+}
+
+export class FormattedTextBox extends React.Component<(FieldViewProps & FormattedTextBoxOverlay)> {
     public static LayoutString(fieldStr: string = "DataKey") {
         return FieldView.LayoutString(FormattedTextBox, fieldStr);
     }
@@ -56,8 +59,8 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
         if (this._editorView) {
             const state = this._editorView.state.apply(tx);
             this._editorView.updateState(state);
-            this.FieldDoc.SetDataOnPrototype(
-                this.FieldKey,
+            this.props.Document.SetDataOnPrototype(
+                this.props.fieldKey,
                 JSON.stringify(state.toJSON()),
                 RichTextField
             );
@@ -65,14 +68,11 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
         }
     }
 
-    get FieldDoc() { return this.props.fieldKey === KeyStore.Archives ? Main.Instance._textDoc! : this.props.Document; }
-    get FieldKey() { return this.props.fieldKey === KeyStore.Archives ? KeyStore.Data : this.props.fieldKey; }
-
     componentDidMount() {
         const config = {
             schema,
             inpRules, //these currently don't do anything, but could eventually be helpful
-            plugins: this.props.fieldKey === KeyStore.Archives ? [
+            plugins: this.props.isOverlay ? [
                 history(),
                 keymap({ "Mod-z": undo, "Mod-y": redo }),
                 keymap(baseKeymap),
@@ -84,7 +84,7 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
                 ]
         };
 
-        if (this.props.fieldKey === KeyStore.Archives) {
+        if (this.props.isOverlay) {
             this._inputReactionDisposer = reaction(() => Main.Instance._textDoc && Main.Instance._textDoc.Id,
                 () => {
                     if (this._editorView) {
@@ -96,12 +96,12 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
             );
         } else {
             this._proxyReactionDisposer = reaction(() => this.props.isSelected(),
-                () => this.props.isSelected() && Main.Instance.SetTextDoc(this.props.Document, this._ref.current!, this.props.ScreenToLocalTransform()));
+                () => this.props.isSelected() && Main.Instance.SetTextDoc(this.props.Document, this.props.fieldKey, this._ref.current!, this.props.ScreenToLocalTransform()));
         }
 
         this._reactionDisposer = reaction(
             () => {
-                const field = this.FieldDoc ? this.FieldDoc.GetT(this.FieldKey, RichTextField) : undefined;
+                const field = this.props.Document ? this.props.Document.GetT(this.props.fieldKey, RichTextField) : undefined;
                 return field && field !== FieldWaiting ? field.Data : undefined;
             },
             field => {
@@ -117,7 +117,7 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
 
     private setupEditor(config: any) {
         let state: EditorState;
-        let field = this.FieldDoc ? this.FieldDoc.GetT(this.FieldKey, RichTextField) : undefined;
+        let field = this.props.Document ? this.props.Document.GetT(this.props.fieldKey, RichTextField) : undefined;
         if (field && field !== FieldWaiting && field.Data) {
             state = EditorState.fromJSON(config, JSON.parse(field.Data));
         } else {
@@ -176,8 +176,8 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
     }
 
     onFocused = (e: React.FocusEvent): void => {
-        if (this.props.fieldKey !== KeyStore.Archives) {
-            Main.Instance.SetTextDoc(this.props.Document, this._ref.current!, this.props.ScreenToLocalTransform());
+        if (!this.props.isOverlay) {
+            Main.Instance.SetTextDoc(this.props.Document, this.props.fieldKey, this._ref.current!, this.props.ScreenToLocalTransform());
         } else {
             if (this._ref.current) {
                 this._ref.current.scrollTop = Main.Instance._textScroll;
@@ -232,6 +232,7 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
                 className={`formattedTextBox-cont`}
                 onKeyDown={this.onKeyPress}
                 onKeyPress={this.onKeyPress}
+                onFocus={this.onFocused}
                 onPointerUp={this.onPointerUp}
                 onPointerDown={this.onPointerDown}
                 onContextMenu={this.specificContextMenu}
diff --git a/tsconfig.json b/tsconfig.json
index 41db1d0a7..0d4d77002 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -1,23 +1,23 @@
 {
-  "compilerOptions": {
-    "target": "es5",
-    "removeComments": true,
-    "experimentalDecorators": true,
-    "strict": true,
-    "jsx": "react",
-    "sourceMap": true,
-    "outDir": "dist",
-    "lib": [
-      "dom",
-      "es2015"
-    ],
-  },
-  // "exclude": [
-  //   "node_modules",
-  //   "static"
-  // ],
-  "typeRoots": [
-    "./node_modules/@types",
-    "./src/typings"
-  ]
+    "compilerOptions": {
+        "target": "es5",
+        "removeComments": true,
+        "experimentalDecorators": true,
+        "strict": true,
+        "jsx": "react",
+        "sourceMap": true,
+        "outDir": "dist",
+        "lib": [
+            "dom",
+            "es2015"
+        ],
+    },
+    // "exclude": [
+    //   "node_modules",
+    //   "static"
+    // ],
+    "typeRoots": [
+        "./node_modules/@types",
+        "./src/typings"
+    ]
 }
\ No newline at end of file
diff --git a/webpack.config.js b/webpack.config.js
index 50079255f..574401807 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -3,95 +3,95 @@ var webpack = require('webpack');
 const CopyWebpackPlugin = require("copy-webpack-plugin");
 
 module.exports = {
-  mode: 'development',
-  entry: {
-    bundle: ["./src/client/views/Main.tsx", 'webpack-hot-middleware/client?reload=true'],
-    viewer: ["./src/debug/Viewer.tsx", 'webpack-hot-middleware/client?reload=true'],
-    test: ["./src/debug/Test.tsx", 'webpack-hot-middleware/client?reload=true'],
-    inkControls: ["./src/mobile/InkControls.tsx", 'webpack-hot-middleware/client?reload=true'],
-    imageUpload: ["./src/mobile/ImageUpload.tsx", 'webpack-hot-middleware/client?reload=true'],
-  },
-  devtool: "source-map",
-  node: {
-    fs: 'empty',
-    module: 'empty',
-    dns: 'mock',
-    tls: 'mock',
-    net: 'mock'
-  },
-  output: {
-    filename: "[name].js",
-    path: path.resolve(__dirname, "build"),
-    publicPath: "/"
-  },
-  resolve: {
-    extensions: ['.js', '.ts', '.tsx']
-  },
-  module: {
-    rules: [
-      {
-        test: [/\.tsx?$/, /\.ts?$/,],
-        enforce: 'pre',
-        use: [
-          {
-            loader: "tslint-loader",
-          }
-        ]
-      }, {
-        test: [/\.tsx?$/, /\.ts?$/,],
-        loader: "awesome-typescript-loader",
-        include: path.join(__dirname, 'src')
-      },
-      {
-        test: /\.scss|css$/,
-        use: [
-          {
-            loader: "style-loader"
-          },
-          {
-            loader: "css-loader"
-          },
-          {
-            loader: "sass-loader"
-          }
-        ]
-      },
-      {
-        test: /\.(jpg|png|pdf)$/,
-        use: [
-          {
-            loader: 'file-loader'
-          }
-        ]
-      },
-      {
-        test: /\.(png|jpg|gif)$/i,
-        use: [
-          {
-            loader: 'url-loader',
-            options: {
-              limit: 8192
-            }
-          }
-        ]
-      }]
-  },
-  plugins: [
-    new CopyWebpackPlugin([{ from: "deploy", to: path.join(__dirname, "build") }]),
-    new webpack.optimize.OccurrenceOrderPlugin(),
-    new webpack.HotModuleReplacementPlugin(),
-    new webpack.NoEmitOnErrorsPlugin()
-  ],
-  devServer: {
-    compress: false,
-    host: "localhost",
-    contentBase: path.join(__dirname, 'deploy'),
-    port: 4321,
-    hot: true,
-    https: false,
-    overlay: {
-      warnings: true,
-      errors: true
+    mode: 'development',
+    entry: {
+        bundle: ["./src/client/views/Main.tsx", 'webpack-hot-middleware/client?reload=true'],
+        viewer: ["./src/debug/Viewer.tsx", 'webpack-hot-middleware/client?reload=true'],
+        test: ["./src/debug/Test.tsx", 'webpack-hot-middleware/client?reload=true'],
+        inkControls: ["./src/mobile/InkControls.tsx", 'webpack-hot-middleware/client?reload=true'],
+        imageUpload: ["./src/mobile/ImageUpload.tsx", 'webpack-hot-middleware/client?reload=true'],
+    },
+    devtool: "source-map",
+    node: {
+        fs: 'empty',
+        module: 'empty',
+        dns: 'mock',
+        tls: 'mock',
+        net: 'mock'
+    },
+    output: {
+        filename: "[name].js",
+        path: path.resolve(__dirname, "build"),
+        publicPath: "/"
+    },
+    resolve: {
+        extensions: ['.js', '.ts', '.tsx']
+    },
+    module: {
+        rules: [
+            {
+                test: [/\.tsx?$/, /\.ts?$/,],
+                enforce: 'pre',
+                use: [
+                    {
+                        loader: "tslint-loader",
+                    }
+                ]
+            }, {
+                test: [/\.tsx?$/, /\.ts?$/,],
+                loader: "awesome-typescript-loader",
+                include: path.join(__dirname, 'src')
+            },
+            {
+                test: /\.scss|css$/,
+                use: [
+                    {
+                        loader: "style-loader"
+                    },
+                    {
+                        loader: "css-loader"
+                    },
+                    {
+                        loader: "sass-loader"
+                    }
+                ]
+            },
+            {
+                test: /\.(jpg|png|pdf)$/,
+                use: [
+                    {
+                        loader: 'file-loader'
+                    }
+                ]
+            },
+            {
+                test: /\.(png|jpg|gif)$/i,
+                use: [
+                    {
+                        loader: 'url-loader',
+                        options: {
+                            limit: 8192
+                        }
+                    }
+                ]
+            }]
+    },
+    plugins: [
+        new CopyWebpackPlugin([{ from: "deploy", to: path.join(__dirname, "build") }]),
+        new webpack.optimize.OccurrenceOrderPlugin(),
+        new webpack.HotModuleReplacementPlugin(),
+        new webpack.NoEmitOnErrorsPlugin()
+    ],
+    devServer: {
+        compress: false,
+        host: "localhost",
+        contentBase: path.join(__dirname, 'deploy'),
+        port: 4321,
+        hot: true,
+        https: false,
+        overlay: {
+            warnings: true,
+            errors: true
+        }
     }
-  }
 };
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2


From 15514b0f3d685764d1bd7ebeac9cdee1f778e184 Mon Sep 17 00:00:00 2001
From: laurawilsonri <laura_wilson@brown.edu>
Date: Thu, 11 Apr 2019 12:06:46 -0400
Subject: font style and size dropdowns work

---
 package.json                                | 332 +++++++++---------
 src/client/util/RichTextSchema.tsx          | 507 ++++++++++++++++------------
 src/client/util/TooltipTextMenu.scss        | 238 ++++++++++++-
 src/client/util/TooltipTextMenu.tsx         | 173 +++++++---
 src/client/views/nodes/FormattedTextBox.tsx |   7 +-
 5 files changed, 830 insertions(+), 427 deletions(-)

(limited to 'src/client/views/nodes/FormattedTextBox.tsx')

diff --git a/package.json b/package.json
index 135bd4a91..11eb6f0f5 100644
--- a/package.json
+++ b/package.json
@@ -1,168 +1,168 @@
 {
-  "name": "dash",
-  "version": "1.0.0",
-  "description": "",
-  "main": "index.js",
-  "scripts": {
-    "start": "nodemon --watch src/server/**/*.ts --exec ts-node src/server/index.ts",
-    "build": "webpack --env production",
-    "test": "mocha -r ts-node/register test/**/*.ts",
-    "tsc": "tsc"
-  },
-  "devDependencies": {
-    "@types/chai": "^4.1.7",
-    "@types/mocha": "^5.2.6",
-    "@types/react-dom": "^16.8.2",
-    "@types/webpack-dev-middleware": "^2.0.2",
-    "@types/webpack-hot-middleware": "^2.16.4",
-    "awesome-typescript-loader": "^5.2.1",
-    "chai": "^4.2.0",
-    "copy-webpack-plugin": "^4.6.0",
-    "css-loader": "^2.1.1",
-    "file-loader": "^3.0.1",
-    "mocha": "^5.2.0",
-    "sass-loader": "^7.1.0",
-    "scss-loader": "0.0.1",
-    "style-loader": "^0.23.1",
-    "ts-node": "^7.0.1",
-    "typescript": "^3.4.1",
-    "webpack": "^4.29.6",
-    "webpack-cli": "^3.2.3",
-    "webpack-dev-middleware": "^3.6.1",
-    "webpack-dev-server": "^3.2.1",
-    "webpack-hot-middleware": "^2.24.3"
-  },
-  "dependencies": {
-    "@fortawesome/fontawesome-free-solid": "^5.0.13",
-    "@fortawesome/fontawesome-svg-core": "^1.2.15",
-    "@fortawesome/free-solid-svg-icons": "^5.7.2",
-    "@fortawesome/react-fontawesome": "^0.1.4",
-    "@hig/flyout": "^1.0.3",
-    "@hig/theme-context": "^2.1.3",
-    "@hig/theme-data": "^2.3.3",
-    "@trendmicro/react-dropdown": "^1.3.0",
-    "@types/async": "^2.4.1",
-    "@types/bcrypt-nodejs": "0.0.30",
-    "@types/bluebird": "^3.5.25",
-    "@types/body-parser": "^1.17.0",
-    "@types/connect-flash": "0.0.34",
-    "@types/cookie-parser": "^1.4.1",
-    "@types/cookie-session": "^2.0.36",
-    "@types/d3-format": "^1.3.1",
-    "@types/express": "^4.16.1",
-    "@types/express-flash": "0.0.0",
-    "@types/express-session": "^1.15.12",
-    "@types/express-validator": "^3.0.0",
-    "@types/formidable": "^1.0.31",
-    "@types/jquery": "^3.3.29",
-    "@types/jsonwebtoken": "^8.3.2",
-    "@types/lodash": "^4.14.121",
-    "@types/mobile-detect": "^1.3.4",
-    "@types/mongodb": "^3.1.22",
-    "@types/mongoose": "^5.3.21",
-    "@types/node": "^10.12.30",
-    "@types/nodemailer": "^4.6.6",
-    "@types/passport": "^1.0.0",
-    "@types/passport-local": "^1.0.33",
-    "@types/prosemirror-commands": "^1.0.1",
-    "@types/prosemirror-history": "^1.0.1",
-    "@types/prosemirror-inputrules": "^1.0.2",
-    "@types/prosemirror-keymap": "^1.0.1",
-    "@types/prosemirror-menu": "^1.0.1",
-    "@types/prosemirror-model": "^1.7.0",
-    "@types/prosemirror-schema-basic": "^1.0.1",
-    "@types/prosemirror-schema-list": "^1.0.1",
-    "@types/prosemirror-state": "^1.2.3",
-    "@types/prosemirror-transform": "^1.1.0",
-    "@types/prosemirror-view": "^1.3.1",
-    "@types/pug": "^2.0.4",
-    "@types/react": "^16.8.7",
-    "@types/react-color": "^2.14.1",
-    "@types/react-measure": "^2.0.4",
-    "@types/react-table": "^6.7.22",
-    "@types/request": "^2.48.1",
-    "@types/request-promise": "^4.1.42",
-    "@types/socket.io": "^2.1.2",
-    "@types/socket.io-client": "^1.4.32",
-    "@types/typescript": "^2.0.0",
-    "@types/uuid": "^3.4.4",
-    "@types/webpack": "^4.4.25",
-    "async": "^2.6.2",
-    "babel-runtime": "^6.26.0",
-    "bcrypt-nodejs": "0.0.3",
-    "bluebird": "^3.5.3",
-    "body-parser": "^1.18.3",
-    "bootstrap": "^4.3.1",
-    "class-transformer": "^0.2.0",
-    "connect-flash": "^0.1.1",
-    "connect-mongo": "^2.0.3",
-    "cookie-parser": "^1.4.4",
-    "cookie-session": "^2.0.0-beta.3",
-    "crypto-browserify": "^3.11.0",
-    "d3-format": "^1.3.2",
-    "express": "^4.16.4",
-    "express-flash": "0.0.2",
-    "express-session": "^1.15.6",
-    "express-validator": "^5.3.1",
-    "expressjs": "^1.0.1",
-    "flexlayout-react": "^0.3.3",
-    "font-awesome": "^4.7.0",
-    "formidable": "^1.2.1",
-    "golden-layout": "^1.5.9",
-    "html-to-image": "^0.1.0",
-    "i": "^0.3.6",
-    "jsonwebtoken": "^8.5.0",
-    "jsx-to-string": "^1.4.0",
-    "lodash": "^4.17.11",
-    "mobile-detect": "^1.4.3",
-    "mobx": "^5.9.0",
-    "mobx-react": "^5.3.5",
-    "mobx-react-devtools": "^6.1.1",
-    "mongodb": "^3.1.13",
-    "mongoose": "^5.4.18",
-    "node-sass": "^4.11.0",
-    "nodemailer": "^5.1.1",
-    "nodemon": "^1.18.10",
-    "normalize.css": "^8.0.1",
-    "npm": "^6.9.0",
-    "passport": "^0.4.0",
-    "passport-local": "^1.0.0",
-    "prosemirror-commands": "^1.0.7",
-    "prosemirror-example-setup": "^1.0.1",
-    "prosemirror-history": "^1.0.4",
-    "prosemirror-keymap": "^1.0.1",
-    "prosemirror-model": "^1.7.0",
-    "prosemirror-schema-basic": "^1.0.0",
-    "prosemirror-schema-list": "^1.0.2",
-    "prosemirror-state": "^1.2.2",
-    "prosemirror-transform": "^1.1.3",
-    "prosemirror-view": "^1.8.3",
-    "pug": "^2.0.3",
-    "raw-loader": "^1.0.0",
-    "react": "^16.8.4",
-    "react-bootstrap": "^1.0.0-beta.5",
-    "react-bootstrap-dropdown-menu": "^1.1.15",
-    "react-color": "^2.17.0",
-    "react-dimensions": "^1.3.1",
-    "react-dom": "^16.8.4",
-    "react-golden-layout": "^1.0.6",
-    "react-image-lightbox": "^5.1.0",
-    "react-jsx-parser": "^1.15.0",
-    "react-measure": "^2.2.4",
-    "react-mosaic": "0.0.20",
-    "react-pdf": "^4.0.2",
-    "react-pdf-highlighter": "^2.1.2",
-    "react-pdf-js": "^4.0.2",
-    "react-simple-dropdown": "^3.2.3",
-    "react-split-pane": "^0.1.85",
-    "react-table": "^6.9.2",
-    "request": "^2.88.0",
-    "request-promise": "^4.2.4",
-    "socket.io": "^2.2.0",
-    "socket.io-client": "^2.2.0",
-    "typescript-collections": "^1.3.2",
-    "url-loader": "^1.1.2",
-    "uuid": "^3.3.2",
-    "xoauth2": "^1.2.0"
-  }
+    "name": "dash",
+    "version": "1.0.0",
+    "description": "",
+    "main": "index.js",
+    "scripts": {
+        "start": "nodemon --watch src/server/**/*.ts --exec ts-node src/server/index.ts",
+        "build": "webpack --env production",
+        "test": "mocha -r ts-node/register test/**/*.ts",
+        "tsc": "tsc"
+    },
+    "devDependencies": {
+        "@types/chai": "^4.1.7",
+        "@types/mocha": "^5.2.6",
+        "@types/react-dom": "^16.8.2",
+        "@types/webpack-dev-middleware": "^2.0.2",
+        "@types/webpack-hot-middleware": "^2.16.4",
+        "awesome-typescript-loader": "^5.2.1",
+        "chai": "^4.2.0",
+        "copy-webpack-plugin": "^4.6.0",
+        "css-loader": "^2.1.1",
+        "file-loader": "^3.0.1",
+        "mocha": "^5.2.0",
+        "sass-loader": "^7.1.0",
+        "scss-loader": "0.0.1",
+        "style-loader": "^0.23.1",
+        "ts-node": "^7.0.1",
+        "typescript": "^3.4.1",
+        "webpack": "^4.29.6",
+        "webpack-cli": "^3.2.3",
+        "webpack-dev-middleware": "^3.6.1",
+        "webpack-dev-server": "^3.2.1",
+        "webpack-hot-middleware": "^2.24.3"
+    },
+    "dependencies": {
+        "@fortawesome/fontawesome-free-solid": "^5.0.13",
+        "@fortawesome/fontawesome-svg-core": "^1.2.15",
+        "@fortawesome/free-solid-svg-icons": "^5.7.2",
+        "@fortawesome/react-fontawesome": "^0.1.4",
+        "@hig/flyout": "^1.0.3",
+        "@hig/theme-context": "^2.1.3",
+        "@hig/theme-data": "^2.3.3",
+        "@trendmicro/react-dropdown": "^1.3.0",
+        "@types/async": "^2.4.1",
+        "@types/bcrypt-nodejs": "0.0.30",
+        "@types/bluebird": "^3.5.25",
+        "@types/body-parser": "^1.17.0",
+        "@types/connect-flash": "0.0.34",
+        "@types/cookie-parser": "^1.4.1",
+        "@types/cookie-session": "^2.0.36",
+        "@types/d3-format": "^1.3.1",
+        "@types/express": "^4.16.1",
+        "@types/express-flash": "0.0.0",
+        "@types/express-session": "^1.15.12",
+        "@types/express-validator": "^3.0.0",
+        "@types/formidable": "^1.0.31",
+        "@types/jquery": "^3.3.29",
+        "@types/jsonwebtoken": "^8.3.2",
+        "@types/lodash": "^4.14.121",
+        "@types/mobile-detect": "^1.3.4",
+        "@types/mongodb": "^3.1.22",
+        "@types/mongoose": "^5.3.21",
+        "@types/node": "^10.12.30",
+        "@types/nodemailer": "^4.6.6",
+        "@types/passport": "^1.0.0",
+        "@types/passport-local": "^1.0.33",
+        "@types/prosemirror-commands": "^1.0.1",
+        "@types/prosemirror-history": "^1.0.1",
+        "@types/prosemirror-inputrules": "^1.0.2",
+        "@types/prosemirror-keymap": "^1.0.1",
+        "@types/prosemirror-menu": "^1.0.1",
+        "@types/prosemirror-model": "^1.7.0",
+        "@types/prosemirror-schema-basic": "^1.0.1",
+        "@types/prosemirror-schema-list": "^1.0.1",
+        "@types/prosemirror-state": "^1.2.3",
+        "@types/prosemirror-transform": "^1.1.0",
+        "@types/prosemirror-view": "^1.3.1",
+        "@types/pug": "^2.0.4",
+        "@types/react": "^16.8.7",
+        "@types/react-color": "^2.14.1",
+        "@types/react-measure": "^2.0.4",
+        "@types/react-table": "^6.7.22",
+        "@types/request": "^2.48.1",
+        "@types/request-promise": "^4.1.42",
+        "@types/socket.io": "^2.1.2",
+        "@types/socket.io-client": "^1.4.32",
+        "@types/typescript": "^2.0.0",
+        "@types/uuid": "^3.4.4",
+        "@types/webpack": "^4.4.25",
+        "async": "^2.6.2",
+        "babel-runtime": "^6.26.0",
+        "bcrypt-nodejs": "0.0.3",
+        "bluebird": "^3.5.3",
+        "body-parser": "^1.18.3",
+        "bootstrap": "^4.3.1",
+        "class-transformer": "^0.2.0",
+        "connect-flash": "^0.1.1",
+        "connect-mongo": "^2.0.3",
+        "cookie-parser": "^1.4.4",
+        "cookie-session": "^2.0.0-beta.3",
+        "crypto-browserify": "^3.11.0",
+        "d3-format": "^1.3.2",
+        "express": "^4.16.4",
+        "express-flash": "0.0.2",
+        "express-session": "^1.15.6",
+        "express-validator": "^5.3.1",
+        "expressjs": "^1.0.1",
+        "flexlayout-react": "^0.3.3",
+        "font-awesome": "^4.7.0",
+        "formidable": "^1.2.1",
+        "golden-layout": "^1.5.9",
+        "html-to-image": "^0.1.0",
+        "i": "^0.3.6",
+        "jsonwebtoken": "^8.5.0",
+        "jsx-to-string": "^1.4.0",
+        "lodash": "^4.17.11",
+        "mobile-detect": "^1.4.3",
+        "mobx": "^5.9.0",
+        "mobx-react": "^5.3.5",
+        "mobx-react-devtools": "^6.1.1",
+        "mongodb": "^3.1.13",
+        "mongoose": "^5.4.18",
+        "node-sass": "^4.11.0",
+        "nodemailer": "^5.1.1",
+        "nodemon": "^1.18.10",
+        "normalize.css": "^8.0.1",
+        "npm": "^6.9.0",
+        "passport": "^0.4.0",
+        "passport-local": "^1.0.0",
+        "prosemirror-commands": "^1.0.7",
+        "prosemirror-example-setup": "^1.0.1",
+        "prosemirror-history": "^1.0.4",
+        "prosemirror-keymap": "^1.0.1",
+        "prosemirror-model": "^1.7.0",
+        "prosemirror-schema-basic": "^1.0.0",
+        "prosemirror-schema-list": "^1.0.2",
+        "prosemirror-state": "^1.2.2",
+        "prosemirror-transform": "^1.1.3",
+        "prosemirror-view": "^1.8.3",
+        "pug": "^2.0.3",
+        "raw-loader": "^1.0.0",
+        "react": "^16.8.4",
+        "react-bootstrap": "^1.0.0-beta.5",
+        "react-bootstrap-dropdown-menu": "^1.1.15",
+        "react-color": "^2.17.0",
+        "react-dimensions": "^1.3.1",
+        "react-dom": "^16.8.4",
+        "react-golden-layout": "^1.0.6",
+        "react-image-lightbox": "^5.1.0",
+        "react-jsx-parser": "^1.15.0",
+        "react-measure": "^2.2.4",
+        "react-mosaic": "0.0.20",
+        "react-pdf": "^4.0.2",
+        "react-pdf-highlighter": "^2.1.2",
+        "react-pdf-js": "^4.0.2",
+        "react-simple-dropdown": "^3.2.3",
+        "react-split-pane": "^0.1.85",
+        "react-table": "^6.9.2",
+        "request": "^2.88.0",
+        "request-promise": "^4.2.4",
+        "socket.io": "^2.2.0",
+        "socket.io-client": "^2.2.0",
+        "typescript-collections": "^1.3.2",
+        "url-loader": "^1.1.2",
+        "uuid": "^3.3.2",
+        "xoauth2": "^1.2.0"
+    }
 }
diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx
index 489c22a57..765ac0ae3 100644
--- a/src/client/util/RichTextSchema.tsx
+++ b/src/client/util/RichTextSchema.tsx
@@ -7,134 +7,134 @@ import { EditorState, Transaction, NodeSelection, } from "prosemirror-state";
 import { EditorView, } from "prosemirror-view";
 
 const pDOM: DOMOutputSpecArray = ["p", 0], blockquoteDOM: DOMOutputSpecArray = ["blockquote", 0], hrDOM: DOMOutputSpecArray = ["hr"],
-  preDOM: DOMOutputSpecArray = ["pre", ["code", 0]], brDOM: DOMOutputSpecArray = ["br"], ulDOM: DOMOutputSpecArray = ["ul", 0]
+    preDOM: DOMOutputSpecArray = ["pre", ["code", 0]], brDOM: DOMOutputSpecArray = ["br"], ulDOM: DOMOutputSpecArray = ["ul", 0]
 
 // :: Object
 // [Specs](#model.NodeSpec) for the nodes defined in this schema.
 export const nodes: { [index: string]: NodeSpec } = {
-  // :: NodeSpec The top level document node.
-  doc: {
-    content: "block+"
-  },
-
-  // :: NodeSpec A plain paragraph textblock. Represented in the DOM
-  // as a `<p>` element.
-  paragraph: {
-    content: "inline*",
-    group: "block",
-    parseDOM: [{ tag: "p" }],
-    toDOM() { return pDOM }
-  },
-
-  // :: NodeSpec A blockquote (`<blockquote>`) wrapping one or more blocks.
-  blockquote: {
-    content: "block+",
-    group: "block",
-    defining: true,
-    parseDOM: [{ tag: "blockquote" }],
-    toDOM() { return blockquoteDOM }
-  },
-
-  // :: NodeSpec A horizontal rule (`<hr>`).
-  horizontal_rule: {
-    group: "block",
-    parseDOM: [{ tag: "hr" }],
-    toDOM() { return hrDOM }
-  },
-
-  // :: NodeSpec A heading textblock, with a `level` attribute that
-  // should hold the number 1 to 6. Parsed and serialized as `<h1>` to
-  // `<h6>` elements.
-  heading: {
-    attrs: { level: { default: 1 } },
-    content: "inline*",
-    group: "block",
-    defining: true,
-    parseDOM: [{ tag: "h1", attrs: { level: 1 } },
-    { tag: "h2", attrs: { level: 2 } },
-    { tag: "h3", attrs: { level: 3 } },
-    { tag: "h4", attrs: { level: 4 } },
-    { tag: "h5", attrs: { level: 5 } },
-    { tag: "h6", attrs: { level: 6 } }],
-    toDOM(node: any) { return ["h" + node.attrs.level, 0] }
-  },
-
-  // :: NodeSpec A code listing. Disallows marks or non-text inline
-  // nodes by default. Represented as a `<pre>` element with a
-  // `<code>` element inside of it.
-  code_block: {
-    content: "text*",
-    marks: "",
-    group: "block",
-    code: true,
-    defining: true,
-    parseDOM: [{ tag: "pre", preserveWhitespace: "full" }],
-    toDOM() { return preDOM }
-  },
-
-  // :: NodeSpec The text node.
-  text: {
-    group: "inline"
-  },
-
-  // :: NodeSpec An inline image (`<img>`) node. Supports `src`,
-  // `alt`, and `href` attributes. The latter two default to the empty
-  // string.
-  image: {
-    inline: true,
-    attrs: {
-      src: {},
-      alt: { default: null },
-      title: { default: null }
-    },
-    group: "inline",
-    draggable: true,
-    parseDOM: [{
-      tag: "img[src]", getAttrs(dom: any) {
-        return {
-          src: dom.getAttribute("src"),
-          title: dom.getAttribute("title"),
-          alt: dom.getAttribute("alt")
-        }
-      }
-    }],
-    toDOM(node: any) { return ["img", node.attrs] }
-  },
-
-  // :: NodeSpec A hard line break, represented in the DOM as `<br>`.
-  hard_break: {
-    inline: true,
-    group: "inline",
-    selectable: false,
-    parseDOM: [{ tag: "br" }],
-    toDOM() { return brDOM }
-  },
-
-  ordered_list: {
-    ...orderedList,
-    content: 'list_item+',
-    group: 'block'
-  },
-  //this doesn't currently work for some reason
-  bullet_list: {
-    ...bulletList,
-    content: 'list_item+',
-    group: 'block',
-    // parseDOM: [{ tag: "ul" }, { style: 'list-style-type=disc' }],
-    // toDOM() { return ulDOM }
-  },
-  //bullet_list: {
-  //  content: 'list_item+',
-  // group: 'block',
-  //active: blockActive(schema.nodes.bullet_list),
-  //enable: wrapInList(schema.nodes.bullet_list),
-  //run: wrapInList(schema.nodes.bullet_list),
-  //select: state => true,
-  // },
-  list_item: {
-    ...listItem,
-    content: 'paragraph block*'
-  }
+    // :: NodeSpec The top level document node.
+    doc: {
+        content: "block+"
+    },
+
+    // :: NodeSpec A plain paragraph textblock. Represented in the DOM
+    // as a `<p>` element.
+    paragraph: {
+        content: "inline*",
+        group: "block",
+        parseDOM: [{ tag: "p" }],
+        toDOM() { return pDOM }
+    },
+
+    // :: NodeSpec A blockquote (`<blockquote>`) wrapping one or more blocks.
+    blockquote: {
+        content: "block+",
+        group: "block",
+        defining: true,
+        parseDOM: [{ tag: "blockquote" }],
+        toDOM() { return blockquoteDOM }
+    },
+
+    // :: NodeSpec A horizontal rule (`<hr>`).
+    horizontal_rule: {
+        group: "block",
+        parseDOM: [{ tag: "hr" }],
+        toDOM() { return hrDOM }
+    },
+
+    // :: NodeSpec A heading textblock, with a `level` attribute that
+    // should hold the number 1 to 6. Parsed and serialized as `<h1>` to
+    // `<h6>` elements.
+    heading: {
+        attrs: { level: { default: 1 } },
+        content: "inline*",
+        group: "block",
+        defining: true,
+        parseDOM: [{ tag: "h1", attrs: { level: 1 } },
+        { tag: "h2", attrs: { level: 2 } },
+        { tag: "h3", attrs: { level: 3 } },
+        { tag: "h4", attrs: { level: 4 } },
+        { tag: "h5", attrs: { level: 5 } },
+        { tag: "h6", attrs: { level: 6 } }],
+        toDOM(node: any) { return ["h" + node.attrs.level, 0] }
+    },
+
+    // :: NodeSpec A code listing. Disallows marks or non-text inline
+    // nodes by default. Represented as a `<pre>` element with a
+    // `<code>` element inside of it.
+    code_block: {
+        content: "text*",
+        marks: "",
+        group: "block",
+        code: true,
+        defining: true,
+        parseDOM: [{ tag: "pre", preserveWhitespace: "full" }],
+        toDOM() { return preDOM }
+    },
+
+    // :: NodeSpec The text node.
+    text: {
+        group: "inline"
+    },
+
+    // :: NodeSpec An inline image (`<img>`) node. Supports `src`,
+    // `alt`, and `href` attributes. The latter two default to the empty
+    // string.
+    image: {
+        inline: true,
+        attrs: {
+            src: {},
+            alt: { default: null },
+            title: { default: null }
+        },
+        group: "inline",
+        draggable: true,
+        parseDOM: [{
+            tag: "img[src]", getAttrs(dom: any) {
+                return {
+                    src: dom.getAttribute("src"),
+                    title: dom.getAttribute("title"),
+                    alt: dom.getAttribute("alt")
+                }
+            }
+        }],
+        toDOM(node: any) { return ["img", node.attrs] }
+    },
+
+    // :: NodeSpec A hard line break, represented in the DOM as `<br>`.
+    hard_break: {
+        inline: true,
+        group: "inline",
+        selectable: false,
+        parseDOM: [{ tag: "br" }],
+        toDOM() { return brDOM }
+    },
+
+    ordered_list: {
+        ...orderedList,
+        content: 'list_item+',
+        group: 'block'
+    },
+    //this doesn't currently work for some reason
+    bullet_list: {
+        ...bulletList,
+        content: 'list_item+',
+        group: 'block',
+        // parseDOM: [{ tag: "ul" }, { style: 'list-style-type=disc' }],
+        // toDOM() { return ulDOM }
+    },
+    //bullet_list: {
+    //  content: 'list_item+',
+    // group: 'block',
+    //active: blockActive(schema.nodes.bullet_list),
+    //enable: wrapInList(schema.nodes.bullet_list),
+    //run: wrapInList(schema.nodes.bullet_list),
+    //select: state => true,
+    // },
+    list_item: {
+        ...listItem,
+        content: 'paragraph block*'
+    }
 }
 
 const emDOM: DOMOutputSpecArray = ["em", 0];
@@ -144,92 +144,179 @@ const underlineDOM: DOMOutputSpecArray = ["underline", 0];
 
 // :: Object [Specs](#model.MarkSpec) for the marks in the schema.
 export const marks: { [index: string]: MarkSpec } = {
-  // :: MarkSpec A link. Has `href` and `title` attributes. `title`
-  // defaults to the empty string. Rendered and parsed as an `<a>`
-  // element.
-  link: {
-    attrs: {
-      href: {},
-      title: { default: null }
-    },
-    inclusive: false,
-    parseDOM: [{
-      tag: "a[href]", getAttrs(dom: any) {
-        return { href: dom.getAttribute("href"), title: dom.getAttribute("title") }
-      }
-    }],
-    toDOM(node: any) { return ["a", node.attrs, 0] }
-  },
-
-  // :: MarkSpec An emphasis mark. Rendered as an `<em>` element.
-  // Has parse rules that also match `<i>` and `font-style: italic`.
-  em: {
-    parseDOM: [{ tag: "i" }, { tag: "em" }, { style: "font-style=italic" }],
-    toDOM() { return emDOM }
-  },
-
-  // :: MarkSpec A strong mark. Rendered as `<strong>`, parse rules
-  // also match `<b>` and `font-weight: bold`.
-  strong: {
-    parseDOM: [{ tag: "strong" },
-    { tag: "b" },
-    { style: "font-weight" }],
-    toDOM() { return strongDOM }
-  },
-
-  underline: {
-    parseDOM: [
-      { tag: 'u' },
-      { style: 'text-decoration=underline' }
-    ],
-    toDOM: () => ['span', {
-      style: 'text-decoration:underline'
-    }]
-  },
-
-  strikethrough: {
-    parseDOM: [
-      { tag: 'strike' },
-      { style: 'text-decoration=line-through' },
-      { style: 'text-decoration-line=line-through' }
-    ],
-    toDOM: () => ['span', {
-      style: 'text-decoration-line:line-through'
-    }]
-  },
-
-  subscript: {
-    excludes: 'superscript',
-    parseDOM: [
-      { tag: 'sub' },
-      { style: 'vertical-align=sub' }
-    ],
-    toDOM: () => ['sub']
-  },
-
-  superscript: {
-    excludes: 'subscript',
-    parseDOM: [
-      { tag: 'sup' },
-      { style: 'vertical-align=super' }
-    ],
-    toDOM: () => ['sup']
-  },
-
-
-  // :: MarkSpec Code font mark. Represented as a `<code>` element.
-  code: {
-    parseDOM: [{ tag: "code" }],
-    toDOM() { return codeDOM }
-  },
-
-
-  timesNewRoman: {
-    parseDOM: [{ style: 'font-family: "Times New Roman", Times, serif;' }],
-    toDOM: () => ['span', {
-      style: 'font-family: "Times New Roman", Times, serif;'
-    }]
-  },
+    // :: MarkSpec A link. Has `href` and `title` attributes. `title`
+    // defaults to the empty string. Rendered and parsed as an `<a>`
+    // element.
+    link: {
+        attrs: {
+            href: {},
+            title: { default: null }
+        },
+        inclusive: false,
+        parseDOM: [{
+            tag: "a[href]", getAttrs(dom: any) {
+                return { href: dom.getAttribute("href"), title: dom.getAttribute("title") }
+            }
+        }],
+        toDOM(node: any) { return ["a", node.attrs, 0] }
+    },
+
+    // :: MarkSpec An emphasis mark. Rendered as an `<em>` element.
+    // Has parse rules that also match `<i>` and `font-style: italic`.
+    em: {
+        parseDOM: [{ tag: "i" }, { tag: "em" }, { style: "font-style=italic" }],
+        toDOM() { return emDOM }
+    },
+
+    // :: MarkSpec A strong mark. Rendered as `<strong>`, parse rules
+    // also match `<b>` and `font-weight: bold`.
+    strong: {
+        parseDOM: [{ tag: "strong" },
+        { tag: "b" },
+        { style: "font-weight" }],
+        toDOM() { return strongDOM }
+    },
+
+    underline: {
+        parseDOM: [
+            { tag: 'u' },
+            { style: 'text-decoration=underline' }
+        ],
+        toDOM: () => ['span', {
+            style: 'text-decoration:underline'
+        }]
+    },
+
+    strikethrough: {
+        parseDOM: [
+            { tag: 'strike' },
+            { style: 'text-decoration=line-through' },
+            { style: 'text-decoration-line=line-through' }
+        ],
+        toDOM: () => ['span', {
+            style: 'text-decoration-line:line-through'
+        }]
+    },
+
+    subscript: {
+        excludes: 'superscript',
+        parseDOM: [
+            { tag: 'sub' },
+            { style: 'vertical-align=sub' }
+        ],
+        toDOM: () => ['sub']
+    },
+
+    superscript: {
+        excludes: 'subscript',
+        parseDOM: [
+            { tag: 'sup' },
+            { style: 'vertical-align=super' }
+        ],
+        toDOM: () => ['sup']
+    },
+
+
+    // :: MarkSpec Code font mark. Represented as a `<code>` element.
+    code: {
+        parseDOM: [{ tag: "code" }],
+        toDOM() { return codeDOM }
+    },
+
+
+    /* FONTS */
+    timesNewRoman: {
+        parseDOM: [{ style: 'font-family: "Times New Roman", Times, serif;' }],
+        toDOM: () => ['span', {
+            style: 'font-family: "Times New Roman", Times, serif;'
+        }]
+    },
+
+    arial: {
+        parseDOM: [{ style: 'font-family: Arial, Helvetica, sans-serif;' }],
+        toDOM: () => ['span', {
+            style: 'font-family: Arial, Helvetica, sans-serif;'
+        }]
+    },
+
+    georgia: {
+        parseDOM: [{ style: 'font-family: Georgia, serif;' }],
+        toDOM: () => ['span', {
+            style: 'font-family: Georgia, serif;'
+        }]
+    },
+
+    comicSans: {
+        parseDOM: [{ style: 'font-family: "Comic Sans MS", cursive, sans-serif;' }],
+        toDOM: () => ['span', {
+            style: 'font-family: "Comic Sans MS", cursive, sans-serif;'
+        }]
+    },
+
+    tahoma: {
+        parseDOM: [{ style: 'font-family: Tahoma, Geneva, sans-serif;' }],
+        toDOM: () => ['span', {
+            style: 'font-family: Tahoma, Geneva, sans-serif;'
+        }]
+    },
+
+    impact: {
+        parseDOM: [{ style: 'font-family: Impact, Charcoal, sans-serif;' }],
+        toDOM: () => ['span', {
+            style: 'font-family: Impact, Charcoal, sans-serif;'
+        }]
+    },
+
+    /** FONT SIZES */
+
+    p10: {
+        parseDOM: [{ style: 'font-size: 10px;' }],
+        toDOM: () => ['span', {
+            style: 'font-size: 10px;'
+        }]
+    },
+
+    p12: {
+        parseDOM: [{ style: 'font-size: 12px;' }],
+        toDOM: () => ['span', {
+            style: 'font-size: 12px;'
+        }]
+    },
+
+    p16: {
+        parseDOM: [{ style: 'font-size: 16px;' }],
+        toDOM: () => ['span', {
+            style: 'font-size: 16px;'
+        }]
+    },
+
+    p24: {
+        parseDOM: [{ style: 'font-size: 24px;' }],
+        toDOM: () => ['span', {
+            style: 'font-size: 24px;'
+        }]
+    },
+
+    p32: {
+        parseDOM: [{ style: 'font-size: 32px;' }],
+        toDOM: () => ['span', {
+            style: 'font-size: 32px;'
+        }]
+    },
+
+    p48: {
+        parseDOM: [{ style: 'font-size: 48px;' }],
+        toDOM: () => ['span', {
+            style: 'font-size: 48px;'
+        }]
+    },
+
+    p72: {
+        parseDOM: [{ style: 'font-size: 72px;' }],
+        toDOM: () => ['span', {
+            style: 'font-size: 72px;'
+        }]
+    },
 }
 
 // :: Schema
diff --git a/src/client/util/TooltipTextMenu.scss b/src/client/util/TooltipTextMenu.scss
index ea580d104..b23074239 100644
--- a/src/client/util/TooltipTextMenu.scss
+++ b/src/client/util/TooltipTextMenu.scss
@@ -1,5 +1,241 @@
 @import "../views/global_variables";
 
+.ProseMirror-textblock-dropdown {
+    min-width: 3em;
+  }
+  
+  .ProseMirror-menu {
+    margin: 0 -4px;
+    line-height: 1;
+  }
+  
+  .ProseMirror-tooltip .ProseMirror-menu {
+    width: -webkit-fit-content;
+    width: fit-content;
+    white-space: pre;
+  }
+  
+  .ProseMirror-menuitem {
+    margin-right: 3px;
+    display: inline-block;
+  }
+  
+  .ProseMirror-menuseparator {
+   // border-right: 1px solid #ddd;
+    margin-right: 3px;
+  }
+  
+  .ProseMirror-menu-dropdown, .ProseMirror-menu-dropdown-menu {
+    font-size: 90%;
+    white-space: nowrap;
+  }
+  
+  .ProseMirror-menu-dropdown {
+    vertical-align: 1px;
+    cursor: pointer;
+    position: relative;
+    padding-right: 15px;
+  }
+  
+  .ProseMirror-menu-dropdown-wrap {
+    padding: 1px 0 1px 4px;
+    display: inline-block;
+    position: relative;
+  }
+  
+  .ProseMirror-menu-dropdown:after {
+    content: "";
+    border-left: 4px solid transparent;
+    border-right: 4px solid transparent;
+    border-top: 4px solid currentColor;
+    opacity: .6;
+    position: absolute;
+    right: 4px;
+    top: calc(50% - 2px);
+  }
+  
+  .ProseMirror-menu-dropdown-menu, .ProseMirror-menu-submenu {
+    position: absolute;
+    background: $dark-color;
+    color:white;
+    border: 1px solid #aaa;
+    padding: 2px;
+  }
+  
+  .ProseMirror-menu-dropdown-menu {
+    z-index: 15;
+    min-width: 6em;
+  }
+  
+  .ProseMirror-menu-dropdown-item {
+    cursor: pointer;
+    padding: 2px 8px 2px 4px;
+    width: auto;
+  }
+  
+  .ProseMirror-menu-dropdown-item:hover {
+    background: #2e2b2b;
+  }
+  
+  .ProseMirror-menu-submenu-wrap {
+    position: relative;
+    margin-right: -4px;
+  }
+  
+  .ProseMirror-menu-submenu-label:after {
+    content: "";
+    border-top: 4px solid transparent;
+    border-bottom: 4px solid transparent;
+    border-left: 4px solid currentColor;
+    opacity: .6;
+    position: absolute;
+    right: 4px;
+    top: calc(50% - 4px);
+  }
+  
+  .ProseMirror-menu-submenu {
+    display: none;
+    min-width: 4em;
+    left: 100%;
+    top: -3px;
+  }
+  
+  .ProseMirror-menu-active {
+    background: #eee;
+    border-radius: 4px;
+  }
+  
+  .ProseMirror-menu-active {
+    background: #eee;
+    border-radius: 4px;
+  }
+  
+  .ProseMirror-menu-disabled {
+    opacity: .3;
+  }
+  
+  .ProseMirror-menu-submenu-wrap:hover .ProseMirror-menu-submenu, .ProseMirror-menu-submenu-wrap-active .ProseMirror-menu-submenu {
+    display: block;
+  }
+  
+  .ProseMirror-menubar {
+    border-top-left-radius: inherit;
+    border-top-right-radius: inherit;
+    position: relative;
+    min-height: 1em;
+    color: white;
+    padding: 1px 6px;
+    top: 0; left: 0; right: 0;
+    border-bottom: 1px solid silver;
+    background:$dark-color;
+    z-index: 10;
+    -moz-box-sizing: border-box;
+    box-sizing: border-box;
+    overflow: visible;
+  }
+  
+  .ProseMirror-icon {
+    display: inline-block;
+    line-height: .8;
+    vertical-align: -2px; /* Compensate for padding */
+    padding: 2px 8px;
+    cursor: pointer;
+  }
+  
+  .ProseMirror-menu-disabled.ProseMirror-icon {
+    cursor: default;
+  }
+  
+  .ProseMirror-icon svg {
+    fill: currentColor;
+    height: 1em;
+  }
+  
+  .ProseMirror-icon span {
+    vertical-align: text-top;
+  }  
+.ProseMirror-example-setup-style hr {
+    padding: 2px 10px;
+    border: none;
+    margin: 1em 0;
+  }
+  
+  .ProseMirror-example-setup-style hr:after {
+    content: "";
+    display: block;
+    height: 1px;
+    background-color: silver;
+    line-height: 2px;
+  }
+  
+  .ProseMirror ul, .ProseMirror ol {
+    padding-left: 30px;
+  }
+  
+  .ProseMirror blockquote {
+    padding-left: 1em;
+    border-left: 3px solid #eee;
+    margin-left: 0; margin-right: 0;
+  }
+  
+  .ProseMirror-example-setup-style img {
+    cursor: default;
+  }
+  
+  .ProseMirror-prompt {
+    background: white;
+    padding: 5px 10px 5px 15px;
+    border: 1px solid silver;
+    position: fixed;
+    border-radius: 3px;
+    z-index: 11;
+    box-shadow: -.5px 2px 5px rgba(0, 0, 0, .2);
+  }
+  
+  .ProseMirror-prompt h5 {
+    margin: 0;
+    font-weight: normal;
+    font-size: 100%;
+    color: #444;
+  }
+  
+  .ProseMirror-prompt input[type="text"],
+  .ProseMirror-prompt textarea {
+    background: #eee;
+    border: none;
+    outline: none;
+  }
+  
+  .ProseMirror-prompt input[type="text"] {
+    padding: 0 4px;
+  }
+  
+  .ProseMirror-prompt-close {
+    position: absolute;
+    left: 2px; top: 1px;
+    color: #666;
+    border: none; background: transparent; padding: 0;
+  }
+  
+  .ProseMirror-prompt-close:after {
+    content: "✕";
+    font-size: 12px;
+  }
+  
+  .ProseMirror-invalid {
+    background: #ffc;
+    border: 1px solid #cc7;
+    border-radius: 4px;
+    padding: 5px 10px;
+    position: absolute;
+    min-width: 10em;
+  }
+  
+  .ProseMirror-prompt-buttons {
+    margin-top: 5px;
+    display: none;
+  }
+
 .tooltipMenu {
     position: absolute;
     z-index: 20;
@@ -39,7 +275,7 @@
     display: inline-block;
     border-right: 1px solid rgba(0, 0, 0, 0.2);
     //color: rgb(19, 18, 18);
-    color: $light-color;
+    color: white;
     line-height: 1;
     padding: 0px 2px;
     margin: 1px;
diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx
index 82e9d1bac..777abe030 100644
--- a/src/client/util/TooltipTextMenu.tsx
+++ b/src/client/util/TooltipTextMenu.tsx
@@ -1,12 +1,12 @@
 import { action, IReactionDisposer, reaction } from "mobx";
-import { Dropdown, DropdownSubmenu, MenuItem } from "prosemirror-menu";
+import { Dropdown, DropdownSubmenu, MenuItem, MenuItemSpec, renderGrouped, icons, } from "prosemirror-menu"; //no import css
 import { baseKeymap, lift } from "prosemirror-commands";
 import { history, redo, undo } from "prosemirror-history";
 import { keymap } from "prosemirror-keymap";
-import { EditorState, Transaction, NodeSelection } from "prosemirror-state";
+import { EditorState, Transaction, NodeSelection, TextSelection } from "prosemirror-state";
 import { EditorView } from "prosemirror-view";
 import { schema } from "./RichTextSchema";
-import { Schema, NodeType } from "prosemirror-model";
+import { Schema, NodeType, MarkType } from "prosemirror-model";
 import React = require("react");
 import "./TooltipTextMenu.scss";
 const { toggleMark, setBlockType, wrapIn } = require("prosemirror-commands");
@@ -24,8 +24,12 @@ export class TooltipTextMenu {
 
     private tooltip: HTMLElement;
     private num_icons = 0;
+    private view: EditorView;
+    private fontStyles: MarkType[];
+    private fontSizes: MarkType[];
 
     constructor(view: EditorView) {
+        this.view = view;
         this.tooltip = document.createElement("div");
         this.tooltip.className = "tooltipMenu";
 
@@ -34,7 +38,6 @@ export class TooltipTextMenu {
 
         //add additional icons
         library.add(faListUl);
-
         //add the buttons to the tooltip
         let items = [
             { command: toggleMark(schema.marks.strong), dom: this.icon("B", "strong") },
@@ -44,44 +47,142 @@ export class TooltipTextMenu {
             { command: toggleMark(schema.marks.superscript), dom: this.icon("s", "superscript") },
             { command: toggleMark(schema.marks.subscript), dom: this.icon("s", "subscript") },
             { command: wrapInList(schema.nodes.bullet_list), dom: this.icon(":", "bullets") },
-            { command: toggleMark(schema.marks.timesNewRoman), dom: this.icon("x", "TNR") },
             { command: lift, dom: this.icon("<", "lift") },
         ]
         //add menu items
         items.forEach(({ dom, command }) => {
             this.tooltip.appendChild(dom);
-        });
 
-        //add dropdowns
-
-        //pointer down handler to activate button effects
-        this.tooltip.addEventListener("pointerdown", e => {
-            e.preventDefault();
-            view.focus();
-            //update view of icons
-            this.num_icons = 0;
-            items.forEach(({ command, dom }) => {
-                if (e.srcElement && dom.contains(e.srcElement as Node)) {
-                    //let active = command(view.state, view.dispatch, view);
-                    let active = command(view.state, view.dispatch, view);
-                    //uncomment this if we want the bullet button to disappear if current selection is bulleted
-                    //dom.style.display = active ? "" : "none";
-                }
+            //pointer down handler to activate button effects
+            dom.addEventListener("pointerdown", e => {
+                e.preventDefault();
+                view.focus();
+                command(view.state, view.dispatch, view);
             })
-        })
+
+        });
+
+        //dropdowns
+        //list of font btns to add
+        this.fontStyles = [
+            schema.marks.timesNewRoman,
+            schema.marks.arial,
+            schema.marks.georgia,
+            schema.marks.comicSans,
+            schema.marks.tahoma,
+            schema.marks.impact,
+        ]
+        this.fontSizes = [
+            schema.marks.p10,
+            schema.marks.p12,
+            schema.marks.p16,
+            schema.marks.p24,
+            schema.marks.p32,
+            schema.marks.p48,
+            schema.marks.p72,
+        ]
+        this.addFontDropdowns();
 
         this.update(view, undefined);
     }
 
+    //adds font size and font style dropdowns
+    addFontDropdowns() {
+        //filtering function - might be unecessary
+        let cut = (arr: MenuItem[]) => arr.filter(x => x);
+        let fontBtns = [
+            this.dropdownBtn("Times New Roman", "font-family: Times New Roman, Times, serif; width: 120px;", schema.marks.timesNewRoman, this.view, this.changeToMarkInGroup, this.fontStyles),
+            this.dropdownBtn("Arial", "font-family: Arial, Helvetica, sans-serif; width: 120px;", schema.marks.arial, this.view, this.changeToMarkInGroup, this.fontStyles),
+            this.dropdownBtn("Georgia", "font-family: Georgia, serif; width: 120px; width: 120px;", schema.marks.georgia, this.view, this.changeToMarkInGroup, this.fontStyles),
+            this.dropdownBtn("ComicSans", "font-family: Comic Sans MS, cursive, sans-serif; width: 120px;", schema.marks.comicSans, this.view, this.changeToMarkInGroup, this.fontStyles),
+            this.dropdownBtn("Tahoma", "font-family: Tahoma, Geneva, sans-serif; width: 120px;", schema.marks.tahoma, this.view, this.changeToMarkInGroup, this.fontStyles),
+            this.dropdownBtn("Impact", "font-family: Impact, Charcoal, sans-serif; width: 120px;", schema.marks.impact, this.view, this.changeToMarkInGroup, this.fontStyles),
+        ]
+
+        let fontSizeBtns = [
+            this.dropdownBtn("10", "width: 50px;", schema.marks.p10, this.view, this.changeToMarkInGroup, this.fontSizes),
+            this.dropdownBtn("12", "width: 50px;", schema.marks.p12, this.view, this.changeToMarkInGroup, this.fontSizes),
+            this.dropdownBtn("16", "width: 50px;", schema.marks.p16, this.view, this.changeToMarkInGroup, this.fontSizes),
+            this.dropdownBtn("24", "width: 50px;", schema.marks.p24, this.view, this.changeToMarkInGroup, this.fontSizes),
+            this.dropdownBtn("32", "width: 50px;", schema.marks.p32, this.view, this.changeToMarkInGroup, this.fontSizes),
+            this.dropdownBtn("48", "width: 50px;", schema.marks.p48, this.view, this.changeToMarkInGroup, this.fontSizes),
+            this.dropdownBtn("72", "width: 50px;", schema.marks.p72, this.view, this.changeToMarkInGroup, this.fontSizes),
+        ]
+
+        //dropdown to hold font btns
+        let dd_fontStyle = new Dropdown(cut(fontBtns), { label: "Font Style", css: "color:white;" }) as MenuItem;
+        let dd_fontSize = new Dropdown(cut(fontSizeBtns), { label: "Font Size", css: "color:white;" }) as MenuItem;
+        this.tooltip.appendChild(dd_fontStyle.render(this.view).dom);
+        this.tooltip.appendChild(dd_fontSize.render(this.view).dom);
+    }
+
+    //for a specific grouping of marks (passed in), remove all and apply the passed-in one to the selected text
+    changeToMarkInGroup(markType: MarkType, view: EditorView, fontMarks: MarkType[]) {
+        let { empty, $cursor, ranges } = view.state.selection as TextSelection;
+        let state = view.state;
+        let dispatch = view.dispatch;
+
+        //remove all other active font marks
+        fontMarks.forEach((type) => {
+            if (dispatch) {
+                if ($cursor) {
+                    if (type.isInSet(state.storedMarks || $cursor.marks())) {
+                        dispatch(state.tr.removeStoredMark(type));
+                    }
+                } else {
+                    let has = false, tr = state.tr
+                    for (let i = 0; !has && i < ranges.length; i++) {
+                        let { $from, $to } = ranges[i]
+                        has = state.doc.rangeHasMark($from.pos, $to.pos, type)
+                    }
+                    for (let i = 0; i < ranges.length; i++) {
+                        let { $from, $to } = ranges[i]
+                        if (has) {
+                            toggleMark(type)(view.state, view.dispatch, view);
+                        }
+                    }
+                }
+            }
+        }); //actually apply font
+        return toggleMark(markType)(view.state, view.dispatch, view);
+    }
+
+    //makes a button for the drop down
+    //css is the style you want applied to the button
+    dropdownBtn(label: string, css: string, markType: MarkType, view: EditorView, changeToMarkInGroup: (markType: MarkType<any>, view: EditorView, groupMarks: MarkType[]) => any, groupMarks: MarkType[]) {
+        return new MenuItem({
+            title: "",
+            label: label,
+            execEvent: "",
+            class: "menuicon",
+            css: css,
+            enable(state) { return true; },
+            run() {
+                changeToMarkInGroup(markType, view, groupMarks);
+            }
+        });
+    }
     // Helper function to create menu icons
     icon(text: string, name: string) {
         let span = document.createElement("span");
         span.className = "menuicon " + name;
         span.title = name;
         span.textContent = text;
+        span.style.color = "white";
         return span;
     }
 
+    //method for checking whether node can be inserted
+    canInsert(state: EditorState, nodeType: NodeType<Schema<string, string>>) {
+        let $from = state.selection.$from
+        for (let d = $from.depth; d >= 0; d--) {
+            let index = $from.index(d)
+            if ($from.node(d).canReplaceWith(index, index, nodeType)) return true
+        }
+        return false
+    }
+
+
     //adapted this method - use it to check if block has a tag (ie bulleting)
     blockActive(type: NodeType<Schema<string, string>>, state: EditorState) {
         let attrs = {};
@@ -100,30 +201,6 @@ export class TooltipTextMenu {
         }
     }
 
-    //this doesn't currently work but could be used to use icons for buttons
-    unorderedListIcon(): HTMLSpanElement {
-        let span = document.createElement("span");
-        //let icon = document.createElement("FontAwesomeIcon");
-        //icon.className = "menuicon";
-        //icon.style.color = "white";
-        //span.appendChild(<i style={{ color: "white" }} icon="list-ul" size="lg" />);
-        let i = document.createElement("i");
-        i.className = "fa falist-ul";
-        span.appendChild(i);
-        //span.appendChild(icon);
-        //return liftItem.spec.icon.sty
-
-        //let sym = document.createElementNS(SVG, "symbol")
-        // sym.id = name
-        //sym.style.color = "white";
-        //width then height
-        //sym.setAttribute("viewBox", "0 0 " + 1024 + " " + 1024);
-        //let path = sym.appendChild(document.createElementNS(SVG, "path"));
-        //path.setAttribute("d", "M219 310v329q0 7-5 12t-12 5q-8 0-13-5l-164-164q-5-5-5-13t5-13l164-164q5-5 13-5 7 0 12 5t5 12zM1024 749v109q0 7-5 12t-12 5h-987q-7 0-12-5t-5-12v-109q0-7 5-12t12-5h987q7 0 12 5t5 12zM1024 530v109q0 7-5 12t-12 5h-621q-7 0-12-5t-5-12v-109q0-7 5-12t12-5h621q7 0 12 5t5 12zM1024 310v109q0 7-5 12t-12 5h-621q-7 0-12-5t-5-12v-109q0-7 5-12t12-5h621q7 0 12 5t5 12zM1024 91v109q0 7-5 12t-12 5h-987q-7 0-12-5t-5-12v-109q0-7 5-12t12-5h987q7 0 12 5t5 12z");
-        //span.appendChild(sym);
-        return span;
-    }
-
     // Create an icon for a heading at the given level
     heading(level: number) {
         return {
@@ -156,10 +233,8 @@ export class TooltipTextMenu {
         let left = Math.max((start.left + end.left) / 2, start.left + 3)
         this.tooltip.style.left = (left - box.left) + "px"
         //let width = Math.abs(start.left - end.left) / 2;
-        let width = 8 * 16 + 15;
+        let width = 220;
         let mid = Math.min(start.left, end.left) + width;
-
-        //THIS WIDTH IS 15 * NUMBER OF ICONS + 15
         this.tooltip.style.width = width + "px";
         this.tooltip.style.bottom = (box.bottom - start.top) + "px";
     }
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index e59179874..fdb1b97be 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -74,7 +74,12 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
                 history(),
                 keymap({ "Mod-z": undo, "Mod-y": redo }),
                 keymap(baseKeymap),
-                this.tooltipMenuPlugin()
+                this.tooltipMenuPlugin(),
+                new Plugin({
+                    props: {
+                        attributes: { class: "ProseMirror-example-setup-style" }
+                    }
+                })
             ]
         };
 
-- 
cgit v1.2.3-70-g09d2


From 22888445b70e374cec082933505017c66bf1fba6 Mon Sep 17 00:00:00 2001
From: Tyler Schicke <tyler_schicke@brown.edu>
Date: Sat, 13 Apr 2019 17:46:14 -0400
Subject: Re-fixed database stuff and added DocumentText to formatted text

---
 src/client/views/nodes/FormattedTextBox.tsx | 3 +++
 src/server/database.ts                      | 7 ++++---
 2 files changed, 7 insertions(+), 3 deletions(-)

(limited to 'src/client/views/nodes/FormattedTextBox.tsx')

diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 8ea747b1c..3a2a8a554 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -14,6 +14,8 @@ import { Main } from "../Main";
 import { FieldView, FieldViewProps } from "./FieldView";
 import "./FormattedTextBox.scss";
 import React = require("react");
+import { TextField } from "../../../fields/TextField";
+import { KeyStore } from "../../../fields/KeyStore";
 const { buildMenuItems } = require("prosemirror-example-setup");
 const { menuBar } = require("prosemirror-menu");
 
@@ -64,6 +66,7 @@ export class FormattedTextBox extends React.Component<(FieldViewProps & Formatte
                 JSON.stringify(state.toJSON()),
                 RichTextField
             );
+            this.props.Document.SetDataOnPrototype(KeyStore.DocumentText, state.doc.textBetween(0, state.doc.content.size, "\n\n"), TextField);
             // doc.SetData(fieldKey, JSON.stringify(state.toJSON()), RichTextField);
         }
     }
diff --git a/src/server/database.ts b/src/server/database.ts
index e08385d98..3290edde0 100644
--- a/src/server/database.ts
+++ b/src/server/database.ts
@@ -16,8 +16,9 @@ export class Database {
         if (this.db) {
             let collection = this.db.collection(collectionName);
             const prom = this.currentWrites[id];
+            let newProm: Promise<void>;
             const run = (): Promise<void> => {
-                let newProm = new Promise<void>(resolve => {
+                return new Promise<void>(resolve => {
                     collection.updateOne({ _id: id }, { $set: value }, { upsert: true }
                         , (err, res) => {
                             if (err) {
@@ -34,9 +35,9 @@ export class Database {
                             callback();
                         });
                 });
-                return newProm;
             };
-            this.currentWrites[id] = prom ? prom.then(run) : run();
+            newProm = prom ? prom.then(run) : run();
+            this.currentWrites[id] = newProm;
         }
     }
 
-- 
cgit v1.2.3-70-g09d2


From b7252aeacbbd73120bc6f80d551f4505646a883b Mon Sep 17 00:00:00 2001
From: Tyler Schicke <tyler_schicke@brown.edu>
Date: Sat, 13 Apr 2019 18:55:28 -0400
Subject: Fixed rich text undo

---
 src/client/views/nodes/FormattedTextBox.tsx | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

(limited to 'src/client/views/nodes/FormattedTextBox.tsx')

diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 3a2a8a554..a49497f8f 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -57,16 +57,20 @@ export class FormattedTextBox extends React.Component<(FieldViewProps & Formatte
         this.onChange = this.onChange.bind(this);
     }
 
+    _applyingChange: boolean = false;
+
     dispatchTransaction = (tx: Transaction) => {
         if (this._editorView) {
             const state = this._editorView.state.apply(tx);
             this._editorView.updateState(state);
+            this._applyingChange = true;
             this.props.Document.SetDataOnPrototype(
                 this.props.fieldKey,
                 JSON.stringify(state.toJSON()),
                 RichTextField
             );
             this.props.Document.SetDataOnPrototype(KeyStore.DocumentText, state.doc.textBetween(0, state.doc.content.size, "\n\n"), TextField);
+            this._applyingChange = false;
             // doc.SetData(fieldKey, JSON.stringify(state.toJSON()), RichTextField);
         }
     }
@@ -108,7 +112,7 @@ export class FormattedTextBox extends React.Component<(FieldViewProps & Formatte
                 return field && field !== FieldWaiting ? field.Data : undefined;
             },
             field => {
-                if (field && this._editorView) {
+                if (field && this._editorView && !this._applyingChange) {
                     this._editorView.updateState(
                         EditorState.fromJSON(config, JSON.parse(field))
                     );
-- 
cgit v1.2.3-70-g09d2


From d0379269b3261f846c42e30f306419044b6025a7 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Sun, 14 Apr 2019 10:52:27 -0400
Subject: fixed some scrolling artifacts

---
 build/index.html                                         |  6 +++---
 deploy/index.html                                        | 16 ++++++++--------
 src/Utils.ts                                             |  2 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx        |  8 ++++++--
 src/client/views/nodes/FormattedTextBox.tsx              |  5 ++++-
 5 files changed, 22 insertions(+), 15 deletions(-)

(limited to 'src/client/views/nodes/FormattedTextBox.tsx')

diff --git a/build/index.html b/build/index.html
index fda212af4..a738d1092 100644
--- a/build/index.html
+++ b/build/index.html
@@ -1,12 +1,12 @@
 <html>
 
 <head>
-  <title>Dash Web</title>
+    <title>Dash Web Build</title>
 </head>
 
 <body>
-  <div id="root"></div>
-  <script src="./bundle.js"></script>
+    <div id="root"></div>
+    <script src="./bundle.js"></script>
 </body>
 
 </html>
\ No newline at end of file
diff --git a/deploy/index.html b/deploy/index.html
index ca5c13e98..532b995f8 100644
--- a/deploy/index.html
+++ b/deploy/index.html
@@ -1,16 +1,16 @@
-<html>
+<html style="overflow: hidden;">
 
 <head>
-  <title>Dash Web</title>
-  <link href="https://fonts.googleapis.com/css?family=Fjalla+One|Hind+Siliguri:300" rel="stylesheet">
-  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"
-    integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
-  <script src="https://cdnjs.cloudflare.com/ajax/libs/typescript/3.3.1/typescript.min.js"></script>
+    <title>Dash Web</title>
+    <link href="https://fonts.googleapis.com/css?family=Fjalla+One|Hind+Siliguri:300" rel="stylesheet">
+    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"
+        integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
+    <script src="https://cdnjs.cloudflare.com/ajax/libs/typescript/3.3.1/typescript.min.js"></script>
 </head>
 
 <body>
-  <div id="root"></div>
-  <script src="/bundle.js"></script>
+    <div id="root"></div>
+    <script src="/bundle.js"></script>
 </body>
 
 </html>
\ No newline at end of file
diff --git a/src/Utils.ts b/src/Utils.ts
index 2e672db9a..e07d4e82d 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -48,7 +48,7 @@ export class Utils {
         if (this.logFilter !== undefined && this.logFilter !== message.type) {
             return;
         }
-        let idString = (message.id || message.id || "").padStart(36, ' ');
+        let idString = (message.id || "").padStart(36, ' ');
         prefix = prefix.padEnd(16, ' ');
         console.log(`${prefix}: ${idString}, ${receiving ? 'receiving' : 'sending'} ${messageName} with data ${JSON.stringify(message)}`);
     }
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index d0b1e7f2c..0da6fe49c 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -38,7 +38,7 @@ export class CollectionFreeFormView extends CollectionSubView {
     }
 
     public addDocument = (newBox: Document, allowDuplicates: boolean) =>
-        this.props.addDocument(this.bringToFront(newBox), false);
+        this.props.addDocument(this.bringToFront(newBox), false)
 
     public selectDocuments = (docs: Document[]) => {
         SelectionManager.DeselectAll;
@@ -161,7 +161,9 @@ export class CollectionFreeFormView extends CollectionSubView {
 
     @action
     onPointerWheel = (e: React.WheelEvent): void => {
-        this.props.select(false);
+        if (!this.props.active()) {
+            return;
+        }
         e.stopPropagation();
         let coefficient = 1000;
 
@@ -191,6 +193,8 @@ export class CollectionFreeFormView extends CollectionSubView {
 
             this.props.Document.SetNumber(KeyStore.Scale, localTransform.Scale);
             this.SetPan(-localTransform.TranslateX / localTransform.Scale, -localTransform.TranslateY / localTransform.Scale);
+            e.stopPropagation();
+            e.preventDefault();
         }
     }
 
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index a49497f8f..2e6272836 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -216,7 +216,9 @@ export class FormattedTextBox extends React.Component<(FieldViewProps & Formatte
     }
 
     onPointerWheel = (e: React.WheelEvent): void => {
-        e.stopPropagation();
+        if (this.props.isSelected()) {
+            e.stopPropagation();
+        }
     }
 
     tooltipMenuPlugin() {
@@ -236,6 +238,7 @@ export class FormattedTextBox extends React.Component<(FieldViewProps & Formatte
     render() {
         return (
             <div
+                style={{ overflowY: this.props.isSelected() || this.props.isOverlay ? "scroll" : "hidden" }}
                 className={`formattedTextBox-cont`}
                 onKeyDown={this.onKeyPress}
                 onKeyPress={this.onKeyPress}
-- 
cgit v1.2.3-70-g09d2


From 845057ef78f272faf488b5bbc2fe79d64fb64120 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Sun, 14 Apr 2019 19:44:38 -0400
Subject: mostly northstar cleanup, plus cleaning up main.tsx

---
 src/client/northstar/dash-nodes/HistogramBox.tsx   |   2 +-
 src/client/northstar/manager/Gateway.ts            |  16 +-
 .../model/binRanges/VisualBinRangeHelper.ts        |   2 +-
 src/client/util/DragManager.ts                     |   9 +-
 src/client/util/SelectionManager.ts                |   3 +-
 src/client/views/DocumentDecorations.tsx           |  16 +-
 src/client/views/Main.scss                         |  18 --
 src/client/views/Main.tsx                          | 279 ++++++---------------
 src/client/views/MainOverlayTextBox.scss           |  19 ++
 src/client/views/MainOverlayTextBox.tsx            | 110 ++++++++
 .../collectionFreeForm/CollectionFreeFormView.tsx  |   3 +-
 src/client/views/nodes/FormattedTextBox.tsx        |   9 +-
 src/fields/KeyStore.ts                             |   2 +-
 .../authentication/models/current_user_utils.ts    |  99 +++-----
 src/server/authentication/models/user_model.ts     |  21 +-
 15 files changed, 286 insertions(+), 322 deletions(-)
 create mode 100644 src/client/views/MainOverlayTextBox.scss
 create mode 100644 src/client/views/MainOverlayTextBox.tsx

(limited to 'src/client/views/nodes/FormattedTextBox.tsx')

diff --git a/src/client/northstar/dash-nodes/HistogramBox.tsx b/src/client/northstar/dash-nodes/HistogramBox.tsx
index 2084fc346..3e94fed81 100644
--- a/src/client/northstar/dash-nodes/HistogramBox.tsx
+++ b/src/client/northstar/dash-nodes/HistogramBox.tsx
@@ -8,7 +8,7 @@ import { KeyStore } from "../../../fields/KeyStore";
 import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils";
 import { ChartType, VisualBinRange } from '../../northstar/model/binRanges/VisualBinRange';
 import { VisualBinRangeHelper } from "../../northstar/model/binRanges/VisualBinRangeHelper";
-import { AggregateBinRange, AggregateFunction, BinRange, Catalog, DoubleValueAggregateResult, HistogramResult, Result } from "../../northstar/model/idea/idea";
+import { AggregateBinRange, AggregateFunction, BinRange, Catalog, DoubleValueAggregateResult, HistogramResult } from "../../northstar/model/idea/idea";
 import { ModelHelpers } from "../../northstar/model/ModelHelpers";
 import { HistogramOperation } from "../../northstar/operations/HistogramOperation";
 import { SizeConverter } from "../../northstar/utils/SizeConverter";
diff --git a/src/client/northstar/manager/Gateway.ts b/src/client/northstar/manager/Gateway.ts
index 207a9ad19..d26f2724f 100644
--- a/src/client/northstar/manager/Gateway.ts
+++ b/src/client/northstar/manager/Gateway.ts
@@ -36,7 +36,7 @@ export class Gateway {
 
     public async ClearCatalog(): Promise<void> {
         try {
-            const json = await this.MakePostJsonRequest("Datamart/ClearAllAugmentations", {});
+            await this.MakePostJsonRequest("Datamart/ClearAllAugmentations", {});
         }
         catch (error) {
             throw new Error("can not reach northstar's backend");
@@ -180,18 +180,18 @@ export class Gateway {
 
 
     public static ConstructUrl(appendix: string): string {
-        let base = Settings.Instance.ServerUrl;
+        let base = NorthstarSettings.Instance.ServerUrl;
         if (base.slice(-1) === "/") {
             base = base.slice(0, -1);
         }
-        let url = base + "/" + Settings.Instance.ServerApiPath + "/" + appendix;
+        let url = base + "/" + NorthstarSettings.Instance.ServerApiPath + "/" + appendix;
         return url;
     }
 }
 
 declare var ENV: any;
 
-export class Settings {
+export class NorthstarSettings {
     private _environment: any;
 
     @observable
@@ -248,10 +248,10 @@ export class Settings {
         return window.location.origin + "/";
     }
 
-    private static _instance: Settings;
+    private static _instance: NorthstarSettings;
 
     @action
-    public Update(environment: any): void {
+    public UpdateEnvironment(environment: any): void {
         /*let serverParam = new URL(document.URL).searchParams.get("serverUrl");
         if (serverParam) {
             if (serverParam === "debug") {
@@ -278,9 +278,9 @@ export class Settings {
         this.DegreeOfParallelism = environment.DEGREE_OF_PARALLISM;
     }
 
-    public static get Instance(): Settings {
+    public static get Instance(): NorthstarSettings {
         if (!this._instance) {
-            this._instance = new Settings();
+            this._instance = new NorthstarSettings();
         }
         return this._instance;
     }
diff --git a/src/client/northstar/model/binRanges/VisualBinRangeHelper.ts b/src/client/northstar/model/binRanges/VisualBinRangeHelper.ts
index 9671e55f8..a92412686 100644
--- a/src/client/northstar/model/binRanges/VisualBinRangeHelper.ts
+++ b/src/client/northstar/model/binRanges/VisualBinRangeHelper.ts
@@ -4,7 +4,7 @@ import { NominalVisualBinRange } from "./NominalVisualBinRange";
 import { QuantitativeVisualBinRange } from "./QuantitativeVisualBinRange";
 import { AlphabeticVisualBinRange } from "./AlphabeticVisualBinRange";
 import { DateTimeVisualBinRange } from "./DateTimeVisualBinRange";
-import { Settings } from "../../manager/Gateway";
+import { NorthstarSettings } from "../../manager/Gateway";
 import { ModelHelpers } from "../ModelHelpers";
 import { AttributeTransformationModel } from "../../core/attribute/AttributeTransformationModel";
 
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 2ee36d2ec..4bd654e15 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -1,13 +1,12 @@
 import { action } from "mobx";
 import { Document } from "../../fields/Document";
+import { FieldWaiting } from "../../fields/Field";
+import { KeyStore } from "../../fields/KeyStore";
 import { emptyFunction } from "../../Utils";
 import { CollectionDockingView } from "../views/collections/CollectionDockingView";
 import { DocumentDecorations } from "../views/DocumentDecorations";
-import { Main } from "../views/Main";
-import { DocumentView } from "../views/nodes/DocumentView";
 import * as globalCssVariables from "../views/globalCssVariables.scss";
-import { KeyStore } from "../../fields/KeyStore";
-import { FieldWaiting } from "../../fields/Field";
+import { MainOverlayTextBox } from "../views/MainOverlayTextBox";
 
 export function SetupDrag(_reference: React.RefObject<HTMLDivElement>, docFunc: () => Document, moveFunc?: DragManager.MoveFunction, copyOnDrop: boolean = false) {
     let onRowMove = action((e: PointerEvent): void => {
@@ -177,7 +176,7 @@ export namespace DragManager {
             dragDiv.className = "dragManager-dragDiv";
             DragManager.Root().appendChild(dragDiv);
         }
-        Main.Instance.SetTextDoc();
+        MainOverlayTextBox.Instance.SetTextDoc();
 
         let scaleXs: number[] = [];
         let scaleYs: number[] = [];
diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index 2fa45a086..c56f6a4ff 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -2,6 +2,7 @@ import { observable, action } from "mobx";
 import { DocumentView } from "../views/nodes/DocumentView";
 import { Document } from "../../fields/Document";
 import { Main } from "../views/Main";
+import { MainOverlayTextBox } from "../views/MainOverlayTextBox";
 
 export namespace SelectionManager {
     class Manager {
@@ -25,7 +26,7 @@ export namespace SelectionManager {
         DeselectAll(): void {
             manager.SelectedDocuments.map(dv => dv.props.onActiveChanged(false));
             manager.SelectedDocuments = [];
-            Main.Instance.SetTextDoc();
+            MainOverlayTextBox.Instance.SetTextDoc();
         }
     }
 
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 2dc496bc1..b97a47a3c 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -1,24 +1,20 @@
-import { action, computed, observable, trace, runInAction } from "mobx";
+import { action, computed, observable } from "mobx";
 import { observer } from "mobx-react";
 import { Key } from "../../fields/Key";
 //import ContentEditable from 'react-contenteditable'
 import { KeyStore } from "../../fields/KeyStore";
 import { ListField } from "../../fields/ListField";
 import { NumberField } from "../../fields/NumberField";
-import { Document } from "../../fields/Document";
 import { TextField } from "../../fields/TextField";
-import { DragManager, DragLinksAsDocuments } from "../util/DragManager";
+import { emptyFunction } from "../../Utils";
+import { DragLinksAsDocuments, DragManager } from "../util/DragManager";
 import { SelectionManager } from "../util/SelectionManager";
-import { CollectionView } from "./collections/CollectionView";
+import { undoBatch } from "../util/UndoManager";
 import './DocumentDecorations.scss';
+import { MainOverlayTextBox } from "./MainOverlayTextBox";
 import { DocumentView } from "./nodes/DocumentView";
 import { LinkMenu } from "./nodes/LinkMenu";
 import React = require("react");
-import { FieldWaiting } from "../../fields/Field";
-import { emptyFunction } from "../../Utils";
-import { Main } from "./Main";
-import { undo } from "prosemirror-history";
-import { undoBatch } from "../util/UndoManager";
 const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
@@ -304,7 +300,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
                 break;
         }
 
-        Main.Instance.SetTextDoc();
+        MainOverlayTextBox.Instance.SetTextDoc();
         SelectionManager.SelectedDocuments().forEach(element => {
             const rect = element.screenRect();
             if (rect.width !== 0) {
diff --git a/src/client/views/Main.scss b/src/client/views/Main.scss
index 13cadb10d..4373534b2 100644
--- a/src/client/views/Main.scss
+++ b/src/client/views/Main.scss
@@ -168,24 +168,6 @@ button:hover {
     left:0;
     overflow: scroll;
 }
-.mainDiv-textInput {
-    background-color: rgba(248, 6, 6, 0.001);
-    width: 200px;
-    height: 200px;
-    position:absolute;
-    overflow: visible;
-    top: 0;
-    left: 0;
-    z-index: $mainTextInput-zindex;
-    .formattedTextBox-cont {
-        background-color: rgba(248, 6, 6, 0.001);
-        width: 100%;
-        height: 100%;
-        position:absolute;
-        top: 0;
-        left: 0;
-    }
-}
 #mainContent-div {
     width:100%;
     height:100%;
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index f8c6ec0e2..51c076b14 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -15,20 +15,19 @@ import { ListField } from '../../fields/ListField';
 import { WorkspacesMenu } from '../../server/authentication/controllers/WorkspacesMenu';
 import { CurrentUserUtils } from '../../server/authentication/models/current_user_utils';
 import { MessageStore } from '../../server/Message';
-import { Utils, returnTrue, emptyFunction, emptyDocFunction } from '../../Utils';
-import * as rp from 'request-promise';
 import { RouteStore } from '../../server/RouteStore';
 import { ServerUtils } from '../../server/ServerUtil';
+import { emptyDocFunction, emptyFunction, returnTrue, Utils } from '../../Utils';
 import { Documents } from '../documents/Documents';
 import { ColumnAttributeModel } from '../northstar/core/attribute/AttributeModel';
 import { AttributeTransformationModel } from '../northstar/core/attribute/AttributeTransformationModel';
-import { Gateway, Settings } from '../northstar/manager/Gateway';
-import { AggregateFunction, Catalog, Point } from '../northstar/model/idea/idea';
+import { Gateway, NorthstarSettings } from '../northstar/manager/Gateway';
+import { AggregateFunction, Catalog } from '../northstar/model/idea/idea';
 import '../northstar/model/ModelExtensions';
 import { HistogramOperation } from '../northstar/operations/HistogramOperation';
 import '../northstar/utils/Extensions';
 import { Server } from '../Server';
-import { SetupDrag, DragManager } from '../util/DragManager';
+import { SetupDrag } from '../util/DragManager';
 import { Transform } from '../util/Transform';
 import { UndoManager } from '../util/UndoManager';
 import { CollectionDockingView } from './collections/CollectionDockingView';
@@ -36,41 +35,28 @@ import { ContextMenu } from './ContextMenu';
 import { DocumentDecorations } from './DocumentDecorations';
 import { InkingControl } from './InkingControl';
 import "./Main.scss";
+import { MainOverlayTextBox } from './MainOverlayTextBox';
 import { DocumentView } from './nodes/DocumentView';
-import { FormattedTextBox } from './nodes/FormattedTextBox';
-import { REPLCommand } from 'repl';
-import { Key } from '../../fields/Key';
 import { PreviewCursor } from './PreviewCursor';
 
 
 @observer
 export class Main extends React.Component {
-    // dummy initializations keep the compiler happy
-    @observable private mainfreeform?: Document;
+    public static Instance: Main;
+    @observable private _workspacesShown: boolean = false;
     @observable public pwidth: number = 0;
     @observable public pheight: number = 0;
-    private _northstarSchemas: Document[] = [];
 
     @computed private get mainContainer(): Document | undefined {
-        let doc = this.userDocument.GetT(KeyStore.ActiveWorkspace, Document);
+        let doc = CurrentUserUtils.UserDocument.GetT(KeyStore.ActiveWorkspace, Document);
         return doc === FieldWaiting ? undefined : doc;
     }
-
     private set mainContainer(doc: Document | undefined) {
-        if (doc) {
-            this.userDocument.Set(KeyStore.ActiveWorkspace, doc);
-        }
+        doc && CurrentUserUtils.UserDocument.Set(KeyStore.ActiveWorkspace, doc);
     }
 
-    private get userDocument(): Document {
-        return CurrentUserUtils.UserDocument;
-    }
-
-    public static Instance: Main;
-
     constructor(props: Readonly<{}>) {
         super(props);
-        this._textProxyDiv = React.createRef();
         Main.Instance = this;
         // causes errors to be generated when modifying an observable outside of an action
         configure({ enforceActions: "observed" });
@@ -102,6 +88,10 @@ export class Main extends React.Component {
         this.initializeNorthstar();
     }
 
+    componentDidMount() { window.onpopstate = this.onHistory; }
+
+    componentWillUnmount() { window.onpopstate = null; }
+
     onHistory = () => {
         if (window.location.pathname !== RouteStore.home) {
             let pathname = window.location.pathname.split("/");
@@ -114,14 +104,6 @@ export class Main extends React.Component {
         }
     }
 
-    componentDidMount() {
-        window.onpopstate = this.onHistory;
-    }
-
-    componentWillUnmount() {
-        window.onpopstate = null;
-    }
-
     initEventListeners = () => {
         // window.addEventListener("pointermove", (e) => this.reportLocation(e))
         window.addEventListener("drop", (e) => e.preventDefault(), false); // drop event handler
@@ -137,7 +119,7 @@ export class Main extends React.Component {
     initAuthenticationRouters = () => {
         // Load the user's active workspace, or create a new one if initial session after signup
         if (!CurrentUserUtils.MainDocId) {
-            this.userDocument.GetTAsync(KeyStore.ActiveWorkspace, Document).then(doc => {
+            CurrentUserUtils.UserDocument.GetTAsync(KeyStore.ActiveWorkspace, Document).then(doc => {
                 if (doc) {
                     CurrentUserUtils.MainDocId = doc.Id;
                     this.openWorkspace(doc);
@@ -158,7 +140,7 @@ export class Main extends React.Component {
 
     @action
     createNewWorkspace = (id?: string): void => {
-        this.userDocument.GetTAsync<ListField<Document>>(KeyStore.Workspaces, ListField).then(action((list: Opt<ListField<Document>>) => {
+        CurrentUserUtils.UserDocument.GetTAsync<ListField<Document>>(KeyStore.Workspaces, ListField).then(action((list: Opt<ListField<Document>>) => {
             if (list) {
                 let freeformDoc = Documents.FreeformDocument([], { x: 0, y: 400, title: "mini collection" });
                 var dockingLayout = { content: [{ type: 'row', content: [CollectionDockingView.makeDocumentConfig(freeformDoc)] }] };
@@ -179,7 +161,7 @@ export class Main extends React.Component {
     openWorkspace = (doc: Document, fromHistory = false): void => {
         this.mainContainer = doc;
         fromHistory || window.history.pushState(null, doc.Title, "/doc/" + doc.Id);
-        this.userDocument.GetTAsync(KeyStore.OptionalRightCollection, Document).then(col => {
+        CurrentUserUtils.UserDocument.GetTAsync(KeyStore.OptionalRightCollection, Document).then(col => {
             // if there is a pending doc, and it has new data, show it (syip: we use a timeout to prevent collection docking view from being uninitialized)
             setTimeout(() => {
                 if (col) {
@@ -193,119 +175,33 @@ export class Main extends React.Component {
         });
     }
 
-    @observable
-    workspacesShown: boolean = false;
-
-    areWorkspacesShown = () => this.workspacesShown;
-    @action
-    toggleWorkspaces = () => {
-        this.workspacesShown = !this.workspacesShown;
-    }
-
-    pwidthFunc = () => this.pwidth;
-    pheightFunc = () => this.pheight;
-    noScaling = () => 1;
-
-    @observable _textDoc?: Document = undefined;
-    _textRect: any;
-    _textXf: Transform = Transform.Identity();
-    _textScroll: number = 0;
-    _textFieldKey: Key = KeyStore.Data;
-    _textColor: string | null = null;
-    _textTargetDiv: HTMLDivElement | undefined;
-    _textProxyDiv: React.RefObject<HTMLDivElement>;
-    @action
-    SetTextDoc(textDoc?: Document, textFieldKey?: Key, div?: HTMLDivElement, tx?: Transform) {
-        if (this._textTargetDiv) {
-            this._textTargetDiv.style.color = this._textColor;
-        }
-
-        this._textDoc = undefined;
-        this._textDoc = textDoc;
-        this._textFieldKey = textFieldKey!;
-        this._textXf = tx ? tx : Transform.Identity();
-        this._textTargetDiv = div;
-        if (div) {
-            this._textColor = div.style.color;
-            div.style.color = "transparent";
-            this._textRect = div.getBoundingClientRect();
-            this._textScroll = div.scrollTop;
-        }
-    }
-
-    @action
-    textScroll = (e: React.UIEvent) => {
-        if (this._textProxyDiv.current && this._textTargetDiv) {
-            this._textTargetDiv.scrollTop = this._textScroll = this._textProxyDiv.current.children[0].scrollTop;
-        }
-    }
-
-    textBoxDown = (e: React.PointerEvent) => {
-        if (e.button !== 0 || e.metaKey || e.altKey) {
-            document.addEventListener("pointermove", this.textBoxMove);
-            document.addEventListener('pointerup', this.textBoxUp);
-        }
-    }
-    textBoxMove = (e: PointerEvent) => {
-        if (e.movementX > 1 || e.movementY > 1) {
-            document.removeEventListener("pointermove", this.textBoxMove);
-            document.removeEventListener('pointerup', this.textBoxUp);
-            let dragData = new DragManager.DocumentDragData([this._textDoc!]);
-            const [left, top] = this._textXf
-                .inverse()
-                .transformPoint(0, 0);
-            dragData.xOffset = e.clientX - left;
-            dragData.yOffset = e.clientY - top;
-            DragManager.StartDocumentDrag([this._textTargetDiv!], dragData, e.clientX, e.clientY, {
-                handlers: {
-                    dragComplete: action(emptyFunction),
-                },
-                hideSource: false
-            });
-        }
-    }
-    textBoxUp = (e: PointerEvent) => {
-        document.removeEventListener("pointermove", this.textBoxMove);
-        document.removeEventListener('pointerup', this.textBoxUp);
-    }
-
-    @computed
-    get activeTextBox() {
-        if (this._textDoc) {
-            let x: number = this._textRect.x;
-            let y: number = this._textRect.y;
-            let w: number = this._textRect.width;
-            let h: number = this._textRect.height;
-            let t = this._textXf.transformPoint(0, 0);
-            let s = this._textXf.transformPoint(1, 0);
-            s[0] = Math.sqrt((s[0] - t[0]) * (s[0] - t[0]) + (s[1] - t[1]) * (s[1] - t[1]));
-            return <div className="mainDiv-textInput" style={{ pointerEvents: "none", transform: `translate(${x}px, ${y}px) scale(${1 / s[0]},${1 / s[0]})`, width: "auto", height: "auto" }} >
-                <div className="mainDiv-textInput" onPointerDown={this.textBoxDown} ref={this._textProxyDiv} onScroll={this.textScroll} style={{ pointerEvents: "none", transform: `scale(${1}, ${1})`, width: `${w * s[0]}px`, height: `${h * s[0]}px` }}>
-                    <FormattedTextBox fieldKey={this._textFieldKey!} isOverlay={true} Document={this._textDoc} isSelected={returnTrue} select={emptyFunction} isTopMost={true}
-                        selectOnLoad={true} ContainingCollectionView={undefined} onActiveChanged={emptyFunction} active={returnTrue} ScreenToLocalTransform={() => this._textXf} focus={emptyDocFunction} />
-                </div>
-            </ div>;
-        }
-        else return (null);
-    }
-
     @computed
     get mainContent() {
-        return !this.mainContainer ? (null) :
-            <DocumentView Document={this.mainContainer}
-                addDocument={undefined}
-                removeDocument={undefined}
-                opacity={1}
-                ScreenToLocalTransform={Transform.Identity}
-                ContentScaling={this.noScaling}
-                PanelWidth={this.pwidthFunc}
-                PanelHeight={this.pheightFunc}
-                isTopMost={true}
-                selectOnLoad={false}
-                focus={emptyDocFunction}
-                parentActive={returnTrue}
-                onActiveChanged={emptyFunction}
-                ContainingCollectionView={undefined} />;
+        trace();
+        let pwidthFunc = () => this.pwidth;
+        let pheightFunc = () => this.pheight;
+        let noScaling = () => 1;
+        return <Measure onResize={action((r: any) => { this.pwidth = r.entry.width; this.pheight = r.entry.height; })}>
+            {({ measureRef }) =>
+                <div ref={measureRef} id="mainContent-div">
+                    {!this.mainContainer ? (null) :
+                        <DocumentView Document={this.mainContainer}
+                            addDocument={undefined}
+                            removeDocument={undefined}
+                            opacity={1}
+                            ScreenToLocalTransform={Transform.Identity}
+                            ContentScaling={noScaling}
+                            PanelWidth={pwidthFunc}
+                            PanelHeight={pheightFunc}
+                            isTopMost={true}
+                            selectOnLoad={false}
+                            focus={emptyDocFunction}
+                            parentActive={returnTrue}
+                            onActiveChanged={emptyFunction}
+                            ContainingCollectionView={undefined} />}
+                </div>
+            }
+        </Measure>;
     }
 
     /* for the expandable add nodes menu. Not included with the miscbuttons because once it expands it expands the whole div with it, making canvas interactions limited. */
@@ -361,6 +257,7 @@ export class Main extends React.Component {
     get miscButtons() {
         let workspacesRef = React.createRef<HTMLDivElement>();
         let logoutRef = React.createRef<HTMLDivElement>();
+        let toggleWorkspaces = () => runInAction(() => { this._workspacesShown = !this._workspacesShown; });
 
         let clearDatabase = action(() => Utils.Emit(Server.Socket, MessageStore.DeleteAll, {}));
         return [
@@ -371,55 +268,50 @@ export class Main extends React.Component {
                 <button className="toolbar-button round-button" title="Ink" onClick={() => InkingControl.Instance.toggleDisplay()}><FontAwesomeIcon icon="pen-nib" size="sm" /></button>
             </div >,
             <div className="main-buttonDiv" key="workspaces" style={{ top: '34px', left: '2px', position: 'absolute' }} ref={workspacesRef}>
-                <button onClick={this.toggleWorkspaces}>Workspaces</button></div>,
+                <button onClick={toggleWorkspaces}>Workspaces</button></div>,
             <div className="main-buttonDiv" key="logout" style={{ top: '34px', right: '1px', position: 'absolute' }} ref={logoutRef}>
                 <button onClick={() => request.get(ServerUtils.prepend(RouteStore.logout), emptyFunction)}>Log Out</button></div>
         ];
     }
 
+    @computed
+    get workspaceMenu() {
+        let areWorkspacesShown = () => this._workspacesShown;
+        let toggleWorkspaces = () => runInAction(() => { this._workspacesShown = !this._workspacesShown; });
+        let workspaces = CurrentUserUtils.UserDocument.GetT<ListField<Document>>(KeyStore.Workspaces, ListField);
+        return (!workspaces || workspaces === FieldWaiting) ? (null) :
+            <WorkspacesMenu active={this.mainContainer} open={this.openWorkspace}
+                new={this.createNewWorkspace} allWorkspaces={workspaces.Data}
+                isShown={areWorkspacesShown} toggle={toggleWorkspaces} />;
+    }
+
     render() {
-        let workspaceMenu: any = null;
-        let workspaces = this.userDocument.GetT<ListField<Document>>(KeyStore.Workspaces, ListField);
-        if (workspaces && workspaces !== FieldWaiting) {
-            workspaceMenu = <WorkspacesMenu active={this.mainContainer} open={this.openWorkspace} new={this.createNewWorkspace} allWorkspaces={workspaces.Data}
-                isShown={this.areWorkspacesShown} toggle={this.toggleWorkspaces} />;
-        }
         return (
-            <>
-                <div id="main-div">
-                    <DocumentDecorations />
-                    <Measure onResize={(r: any) => runInAction(() => {
-                        this.pwidth = r.entry.width;
-                        this.pheight = r.entry.height;
-                    })}>
-                        {({ measureRef }) =>
-                            <div ref={measureRef} id="mainContent-div">
-                                {this.mainContent}
-                                <PreviewCursor />
-                            </div>
-                        }
-                    </Measure>
-                    <ContextMenu />
-                    {this.nodesMenu}
-                    {this.miscButtons}
-                    {workspaceMenu}
-                    <InkingControl />
-                </div>
-                {this.activeTextBox}
-            </>
+            <div id="main-div">
+                <DocumentDecorations />
+                {this.mainContent}
+                <PreviewCursor />
+                <ContextMenu />
+                {this.nodesMenu}
+                {this.miscButtons}
+                {this.workspaceMenu}
+                <InkingControl />
+                <MainOverlayTextBox />
+            </div>
         );
     }
 
     // --------------- Northstar hooks ------------- /
+    private _northstarSchemas: Document[] = [];
 
-    @action AddToNorthstarCatalog(ctlog: Catalog) {
-        CurrentUserUtils.NorthstarDBCatalog = CurrentUserUtils.NorthstarDBCatalog ? CurrentUserUtils.NorthstarDBCatalog : ctlog;
+    @action SetNorthstarCatalog(ctlog: Catalog) {
+        CurrentUserUtils.NorthstarDBCatalog = ctlog;
         if (ctlog && ctlog.schemas) {
             ctlog.schemas.map(schema => {
-                let promises: Promise<void>[] = [];
                 let schemaDocuments: Document[] = [];
-                CurrentUserUtils.GetAllNorthstarColumnAttributes(schema).map(attr => {
-                    let prom = Server.GetField(attr.displayName! + ".alias").then(action((field: Opt<Field>) => {
+                let attributesToBecomeDocs = CurrentUserUtils.GetAllNorthstarColumnAttributes(schema);
+                Promise.all(attributesToBecomeDocs.reduce((promises, attr) => {
+                    promises.push(Server.GetField(attr.displayName! + ".alias").then(action((field: Opt<Field>) => {
                         if (field instanceof Document) {
                             schemaDocuments.push(field);
                         } else {
@@ -430,32 +322,17 @@ export class Main extends React.Component {
                                 new AttributeTransformationModel(atmod, AggregateFunction.Count));
                             schemaDocuments.push(Documents.HistogramDocument(histoOp, { width: 200, height: 200, title: attr.displayName! }, undefined, attr.displayName! + ".alias"));
                         }
-                    }));
-                    promises.push(prom);
-                });
-                Promise.all(promises).finally(() => {
-                    let schemaDoc = Documents.TreeDocument(schemaDocuments, { width: 50, height: 100, title: schema.displayName! });
-                    this._northstarSchemas.push(schemaDoc);
-                });
+                    })));
+                    return promises;
+                }, [] as Promise<void>[])).finally(() =>
+                    this._northstarSchemas.push(Documents.TreeDocument(schemaDocuments, { width: 50, height: 100, title: schema.displayName! })));
             });
         }
     }
     async initializeNorthstar(): Promise<void> {
-        let envPath = "/assets/env.json";
-        const response = await fetch(envPath, {
-            redirect: "follow",
-            method: "GET",
-            credentials: "include"
-        });
-        const env = await response.json();
-        Settings.Instance.Update(env);
-        let cat = Gateway.Instance.ClearCatalog();
-        cat.then(async () => {
-            this.AddToNorthstarCatalog(await Gateway.Instance.GetCatalog());
-            // if (!CurrentUserUtils.GetNorthstarSchema("Book1"))
-            //     this.AddToNorthstarCatalog(await Gateway.Instance.GetSchema("http://www.cs.brown.edu/~bcz/Book1.csv", "Book1"));
-        });
-
+        const getEnvironment = await fetch("/assets/env.json", { redirect: "follow", method: "GET", credentials: "include" });
+        NorthstarSettings.Instance.UpdateEnvironment(await getEnvironment.json());
+        Gateway.Instance.ClearCatalog().then(async () => this.SetNorthstarCatalog(await Gateway.Instance.GetCatalog()));
     }
 }
 
diff --git a/src/client/views/MainOverlayTextBox.scss b/src/client/views/MainOverlayTextBox.scss
new file mode 100644
index 000000000..697d68c8c
--- /dev/null
+++ b/src/client/views/MainOverlayTextBox.scss
@@ -0,0 +1,19 @@
+@import "globalCssVariables";
+.mainOverlayTextBox-textInput {
+    background-color: rgba(248, 6, 6, 0.001);
+    width: 200px;
+    height: 200px;
+    position:absolute;
+    overflow: visible;
+    top: 0;
+    left: 0;
+    z-index: $mainTextInput-zindex;
+    .formattedTextBox-cont {
+        background-color: rgba(248, 6, 6, 0.001);
+        width: 100%;
+        height: 100%;
+        position:absolute;
+        top: 0;
+        left: 0;
+    }
+}
\ No newline at end of file
diff --git a/src/client/views/MainOverlayTextBox.tsx b/src/client/views/MainOverlayTextBox.tsx
new file mode 100644
index 000000000..df1addbc7
--- /dev/null
+++ b/src/client/views/MainOverlayTextBox.tsx
@@ -0,0 +1,110 @@
+import { action, observable, trace } from 'mobx';
+import { observer } from 'mobx-react';
+import "normalize.css";
+import * as React from 'react';
+import { Document } from '../../fields/Document';
+import { Key } from '../../fields/Key';
+import { KeyStore } from '../../fields/KeyStore';
+import { emptyDocFunction, emptyFunction, returnTrue } from '../../Utils';
+import '../northstar/model/ModelExtensions';
+import '../northstar/utils/Extensions';
+import { DragManager } from '../util/DragManager';
+import { Transform } from '../util/Transform';
+import "./MainOverlayTextBox.scss";
+import { FormattedTextBox } from './nodes/FormattedTextBox';
+
+interface MainOverlayTextBoxProps {
+}
+
+@observer
+export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps> {
+    public static Instance: MainOverlayTextBox;
+    @observable public TextDoc?: Document = undefined;
+    public TextScroll: number = 0;
+    private _textRect: any;
+    private _textXf: Transform = Transform.Identity();
+    private _textFieldKey: Key = KeyStore.Data;
+    private _textColor: string | null = null;
+    private _textTargetDiv: HTMLDivElement | undefined;
+    private _textProxyDiv: React.RefObject<HTMLDivElement>;
+
+    constructor(props: MainOverlayTextBoxProps) {
+        super(props);
+        this._textProxyDiv = React.createRef();
+        MainOverlayTextBox.Instance = this;
+    }
+
+    @action
+    SetTextDoc(textDoc?: Document, textFieldKey?: Key, div?: HTMLDivElement, tx?: Transform) {
+        if (this._textTargetDiv) {
+            this._textTargetDiv.style.color = this._textColor;
+        }
+
+        this.TextDoc = undefined;
+        this.TextDoc = textDoc;
+        this._textFieldKey = textFieldKey!;
+        this._textXf = tx ? tx : Transform.Identity();
+        this._textTargetDiv = div;
+        if (div) {
+            this._textColor = div.style.color;
+            div.style.color = "transparent";
+            this._textRect = div.getBoundingClientRect();
+            this.TextScroll = div.scrollTop;
+        }
+    }
+
+    @action
+    textScroll = (e: React.UIEvent) => {
+        if (this._textProxyDiv.current && this._textTargetDiv) {
+            this._textTargetDiv.scrollTop = this.TextScroll = this._textProxyDiv.current.children[0].scrollTop;
+        }
+    }
+
+    textBoxDown = (e: React.PointerEvent) => {
+        if (e.button !== 0 || e.metaKey || e.altKey) {
+            document.addEventListener("pointermove", this.textBoxMove);
+            document.addEventListener('pointerup', this.textBoxUp);
+        }
+    }
+    textBoxMove = (e: PointerEvent) => {
+        if (e.movementX > 1 || e.movementY > 1) {
+            document.removeEventListener("pointermove", this.textBoxMove);
+            document.removeEventListener('pointerup', this.textBoxUp);
+            let dragData = new DragManager.DocumentDragData([this.TextDoc!]);
+            const [left, top] = this._textXf
+                .inverse()
+                .transformPoint(0, 0);
+            dragData.xOffset = e.clientX - left;
+            dragData.yOffset = e.clientY - top;
+            DragManager.StartDocumentDrag([this._textTargetDiv!], dragData, e.clientX, e.clientY, {
+                handlers: {
+                    dragComplete: action(emptyFunction),
+                },
+                hideSource: false
+            });
+        }
+    }
+    textBoxUp = (e: PointerEvent) => {
+        document.removeEventListener("pointermove", this.textBoxMove);
+        document.removeEventListener('pointerup', this.textBoxUp);
+    }
+
+    render() {
+        if (this.TextDoc) {
+            let x: number = this._textRect.x;
+            let y: number = this._textRect.y;
+            let w: number = this._textRect.width;
+            let h: number = this._textRect.height;
+            let t = this._textXf.transformPoint(0, 0);
+            let s = this._textXf.transformPoint(1, 0);
+            s[0] = Math.sqrt((s[0] - t[0]) * (s[0] - t[0]) + (s[1] - t[1]) * (s[1] - t[1]));
+            return <div className="mainOverlayTextBox-textInput" style={{ pointerEvents: "none", transform: `translate(${x}px, ${y}px) scale(${1 / s[0]},${1 / s[0]})`, width: "auto", height: "auto" }} >
+                <div className="mainOverlayTextBox-textInput" onPointerDown={this.textBoxDown} ref={this._textProxyDiv} onScroll={this.textScroll} style={{ pointerEvents: "none", transform: `scale(${1}, ${1})`, width: `${w * s[0]}px`, height: `${h * s[0]}px` }}>
+                    <FormattedTextBox fieldKey={this._textFieldKey!} isOverlay={true} Document={this.TextDoc} isSelected={returnTrue} select={emptyFunction} isTopMost={true}
+                        selectOnLoad={true} ContainingCollectionView={undefined} onActiveChanged={emptyFunction} active={returnTrue} ScreenToLocalTransform={() => this._textXf} focus={emptyDocFunction} />
+                </div>
+            </ div>;
+        }
+        else return (null);
+    }
+}
\ No newline at end of file
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 6c382a353..cb4e8fb2e 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -24,6 +24,7 @@ import "./CollectionFreeFormView.scss";
 import { MarqueeView } from "./MarqueeView";
 import React = require("react");
 import v5 = require("uuid/v5");
+import { MainOverlayTextBox } from "../../MainOverlayTextBox";
 
 @observer
 export class CollectionFreeFormView extends CollectionSubView {
@@ -205,7 +206,7 @@ export class CollectionFreeFormView extends CollectionSubView {
 
     @action
     private SetPan(panX: number, panY: number) {
-        Main.Instance.SetTextDoc();
+        MainOverlayTextBox.Instance.SetTextDoc();
         var x1 = this.getLocalTransform().inverse().Scale;
         const newPanX = Math.min((1 - 1 / x1) * this.nativeWidth, Math.max(0, panX));
         const newPanY = Math.min((1 - 1 / x1) * this.nativeHeight, Math.max(0, panY));
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 2e6272836..87c1bcb1e 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -16,6 +16,7 @@ import "./FormattedTextBox.scss";
 import React = require("react");
 import { TextField } from "../../../fields/TextField";
 import { KeyStore } from "../../../fields/KeyStore";
+import { MainOverlayTextBox } from "../MainOverlayTextBox";
 const { buildMenuItems } = require("prosemirror-example-setup");
 const { menuBar } = require("prosemirror-menu");
 
@@ -92,7 +93,7 @@ export class FormattedTextBox extends React.Component<(FieldViewProps & Formatte
         };
 
         if (this.props.isOverlay) {
-            this._inputReactionDisposer = reaction(() => Main.Instance._textDoc && Main.Instance._textDoc.Id,
+            this._inputReactionDisposer = reaction(() => MainOverlayTextBox.Instance.TextDoc && MainOverlayTextBox.Instance.TextDoc.Id,
                 () => {
                     if (this._editorView) {
                         this._editorView.destroy();
@@ -103,7 +104,7 @@ export class FormattedTextBox extends React.Component<(FieldViewProps & Formatte
             );
         } else {
             this._proxyReactionDisposer = reaction(() => this.props.isSelected(),
-                () => this.props.isSelected() && Main.Instance.SetTextDoc(this.props.Document, this.props.fieldKey, this._ref.current!, this.props.ScreenToLocalTransform()));
+                () => this.props.isSelected() && MainOverlayTextBox.Instance.SetTextDoc(this.props.Document, this.props.fieldKey, this._ref.current!, this.props.ScreenToLocalTransform()));
         }
 
         this._reactionDisposer = reaction(
@@ -184,10 +185,10 @@ export class FormattedTextBox extends React.Component<(FieldViewProps & Formatte
 
     onFocused = (e: React.FocusEvent): void => {
         if (!this.props.isOverlay) {
-            Main.Instance.SetTextDoc(this.props.Document, this.props.fieldKey, this._ref.current!, this.props.ScreenToLocalTransform());
+            MainOverlayTextBox.Instance.SetTextDoc(this.props.Document, this.props.fieldKey, this._ref.current!, this.props.ScreenToLocalTransform());
         } else {
             if (this._ref.current) {
-                this._ref.current.scrollTop = Main.Instance._textScroll;
+                this._ref.current.scrollTop = MainOverlayTextBox.Instance.TextScroll;
             }
         }
     }
diff --git a/src/fields/KeyStore.ts b/src/fields/KeyStore.ts
index 19431bbe3..16a909eb8 100644
--- a/src/fields/KeyStore.ts
+++ b/src/fields/KeyStore.ts
@@ -49,7 +49,7 @@ export namespace KeyStore {
     export const CopyDraggedItems = new Key("CopyDraggedItems");
 
     export const KeyList: Key[] = [Prototype, X, Y, Page, Title, Author, PanX, PanY, Scale, NativeWidth, NativeHeight,
-        Width, Height, ZIndex, Data, Annotations, ViewType, Layout, BackgroundColor, BackgroundLayout, OverlayLayout, LayoutKeys,
+        Width, Height, ZIndex, Zoom, Data, Annotations, ViewType, Layout, BackgroundColor, BackgroundLayout, OverlayLayout, LayoutKeys,
         LayoutFields, ColumnsKey, SchemaSplitPercentage, Caption, ActiveWorkspace, DocumentText, BrushingDocs, LinkedToDocs, LinkedFromDocs,
         LinkDescription, LinkTags, Thumbnail, ThumbnailPage, CurPage, AnnotationOn, NumPages, Ink, Cursors, OptionalRightCollection,
         Archives, Workspaces, Minimized, CopyDraggedItems
diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts
index 13eddafbf..34454eda0 100644
--- a/src/server/authentication/models/current_user_utils.ts
+++ b/src/server/authentication/models/current_user_utils.ts
@@ -1,15 +1,14 @@
-import { DashUserModel } from "./user_model";
+import { computed, observable, action } from "mobx";
 import * as rp from 'request-promise';
-import { RouteStore } from "../../RouteStore";
-import { ServerUtils } from "../../ServerUtil";
+import { Documents } from "../../../client/documents/Documents";
+import { Attribute, AttributeGroup, Catalog, Schema } from "../../../client/northstar/model/idea/idea";
+import { ArrayUtil } from "../../../client/northstar/utils/ArrayUtil";
 import { Server } from "../../../client/Server";
 import { Document } from "../../../fields/Document";
 import { KeyStore } from "../../../fields/KeyStore";
 import { ListField } from "../../../fields/ListField";
-import { Documents } from "../../../client/documents/Documents";
-import { Schema, Attribute, AttributeGroup, Catalog } from "../../../client/northstar/model/idea/idea";
-import { observable, computed, action } from "mobx";
-import { ArrayUtil } from "../../../client/northstar/utils/ArrayUtil";
+import { RouteStore } from "../../RouteStore";
+import { ServerUtils } from "../../ServerUtil";
 
 export class CurrentUserUtils {
     private static curr_email: string;
@@ -17,65 +16,22 @@ export class CurrentUserUtils {
     private static user_document: Document;
     //TODO tfs: these should be temporary...
     private static mainDocId: string | undefined;
-    @observable private static catalog?: Catalog;
-
-    public static get email(): string {
-        return this.curr_email;
-    }
-
-    public static get id(): string {
-        return this.curr_id;
-    }
-
-    public static get UserDocument(): Document {
-        return this.user_document;
-    }
 
-    public static get MainDocId(): string | undefined {
-        return this.mainDocId;
-    }
-
-    public static set MainDocId(id: string | undefined) {
-        this.mainDocId = id;
-    }
-
-    @computed public static get NorthstarDBCatalog(): Catalog | undefined {
-        return this.catalog;
-    }
-    public static set NorthstarDBCatalog(ctlog: Catalog | undefined) {
-        this.catalog = ctlog;
-    }
-    public static GetNorthstarSchema(name: string): Schema | undefined {
-        return !this.catalog || !this.catalog.schemas ? undefined :
-            ArrayUtil.FirstOrDefault<Schema>(this.catalog.schemas, (s: Schema) => s.displayName === name);
-    }
-    public static GetAllNorthstarColumnAttributes(schema: Schema) {
-        if (!schema || !schema.rootAttributeGroup) {
-            return [];
-        }
-        const recurs = (attrs: Attribute[], g: AttributeGroup) => {
-            if (g.attributes) {
-                attrs.push.apply(attrs, g.attributes);
-                if (g.attributeGroups) {
-                    g.attributeGroups.forEach(ng => recurs(attrs, ng));
-                }
-            }
-        };
-        const allAttributes: Attribute[] = new Array<Attribute>();
-        recurs(allAttributes, schema.rootAttributeGroup);
-        return allAttributes;
-    }
+    public static get email() { return this.curr_email; }
+    public static get id() { return this.curr_id; }
+    public static get UserDocument() { return this.user_document; }
+    public static get MainDocId() { return this.mainDocId; }
+    public static set MainDocId(id: string | undefined) { this.mainDocId = id; }
 
     private static createUserDocument(id: string): Document {
         let doc = new Document(id);
-
         doc.Set(KeyStore.Workspaces, new ListField<Document>());
         doc.Set(KeyStore.OptionalRightCollection, Documents.SchemaDocument([], { title: "Pending documents" }));
         return doc;
     }
 
     public static loadCurrentUser(): Promise<any> {
-        let userPromise = rp.get(ServerUtils.prepend(RouteStore.getCurrUser)).then((response) => {
+        let userPromise = rp.get(ServerUtils.prepend(RouteStore.getCurrUser)).then(response => {
             if (response) {
                 let obj = JSON.parse(response);
                 CurrentUserUtils.curr_id = obj.id as string;
@@ -86,17 +42,34 @@ export class CurrentUserUtils {
         });
         let userDocPromise = rp.get(ServerUtils.prepend(RouteStore.getUserDocumentId)).then(id => {
             if (id) {
-                return Server.GetField(id).then(field => {
-                    if (field instanceof Document) {
-                        this.user_document = field;
-                    } else {
-                        this.user_document = this.createUserDocument(id);
-                    }
-                });
+                return Server.GetField(id).then(field =>
+                    this.user_document = field instanceof Document ? field : this.createUserDocument(id));
             } else {
                 throw new Error("There should be a user id! Why does Dash think there isn't one?");
             }
         });
         return Promise.all([userPromise, userDocPromise]);
     }
+
+    /* Northstar catalog ... really just for testing so this should eventually go away */
+    @observable private static _northstarCatalog?: Catalog;
+    @computed public static get NorthstarDBCatalog() { return this._northstarCatalog; }
+    public static set NorthstarDBCatalog(ctlog: Catalog | undefined) { this._northstarCatalog = ctlog; }
+
+    public static GetNorthstarSchema(name: string): Schema | undefined {
+        return !this._northstarCatalog || !this._northstarCatalog.schemas ? undefined :
+            ArrayUtil.FirstOrDefault<Schema>(this._northstarCatalog.schemas, (s: Schema) => s.displayName === name);
+    }
+    public static GetAllNorthstarColumnAttributes(schema: Schema) {
+        const recurs = (attrs: Attribute[], g?: AttributeGroup) => {
+            if (g && g.attributes) {
+                attrs.push.apply(attrs, g.attributes);
+                if (g.attributeGroups) {
+                    g.attributeGroups.forEach(ng => recurs(attrs, ng));
+                }
+            }
+            return attrs;
+        };
+        return recurs([] as Attribute[], schema ? schema.rootAttributeGroup : undefined);
+    }
 }
\ No newline at end of file
diff --git a/src/server/authentication/models/user_model.ts b/src/server/authentication/models/user_model.ts
index 1c6926517..d5c84c311 100644
--- a/src/server/authentication/models/user_model.ts
+++ b/src/server/authentication/models/user_model.ts
@@ -18,8 +18,8 @@ mongoose.connection.on('disconnected', function () {
 export type DashUserModel = mongoose.Document & {
     email: string,
     password: string,
-    passwordResetToken: string | undefined,
-    passwordResetExpires: Date | undefined,
+    passwordResetToken?: string,
+    passwordResetExpires?: Date,
 
     userDocumentId: string;
 
@@ -67,11 +67,17 @@ const userSchema = new mongoose.Schema({
  */
 userSchema.pre("save", function save(next) {
     const user = this as DashUserModel;
-    if (!user.isModified("password")) { return next(); }
+    if (!user.isModified("password")) {
+        return next();
+    }
     bcrypt.genSalt(10, (err, salt) => {
-        if (err) { return next(err); }
+        if (err) {
+            return next(err);
+        }
         bcrypt.hash(user.password, salt, () => void {}, (err: mongoose.Error, hash) => {
-            if (err) { return next(err); }
+            if (err) {
+                return next(err);
+            }
             user.password = hash;
             next();
         });
@@ -79,9 +85,8 @@ userSchema.pre("save", function save(next) {
 });
 
 const comparePassword: comparePasswordFunction = function (this: DashUserModel, candidatePassword, cb) {
-    bcrypt.compare(candidatePassword, this.password, (err: mongoose.Error, isMatch: boolean) => {
-        cb(err, isMatch);
-    });
+    bcrypt.compare(candidatePassword, this.password, (err: mongoose.Error, isMatch: boolean) =>
+        cb(err, isMatch));
 };
 
 userSchema.methods.comparePassword = comparePassword;
-- 
cgit v1.2.3-70-g09d2


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

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

(limited to 'src/client/views/nodes/FormattedTextBox.tsx')

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


From 166c37942ba536f024cbeba6f151da0ed6a3f671 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Mon, 15 Apr 2019 16:04:16 -0400
Subject: fixing up small interaction issues with web pages, collections, etc.

---
 src/client/views/EditableView.scss                 |  1 -
 src/client/views/Main.tsx                          |  6 +-
 .../views/collections/CollectionBaseView.tsx       |  2 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 89 +++++++++++++---------
 .../collections/collectionFreeForm/MarqueeView.tsx |  1 -
 src/client/views/nodes/DocumentView.scss           |  5 +-
 src/client/views/nodes/DocumentView.tsx            |  4 +-
 src/client/views/nodes/FormattedTextBox.tsx        |  1 -
 src/client/views/nodes/KeyValueBox.tsx             |  6 --
 src/client/views/nodes/WebBox.scss                 |  6 ++
 src/client/views/nodes/WebBox.tsx                  | 35 +++++++--
 11 files changed, 99 insertions(+), 57 deletions(-)

(limited to 'src/client/views/nodes/FormattedTextBox.tsx')

diff --git a/src/client/views/EditableView.scss b/src/client/views/EditableView.scss
index be3c5069a..ea401eaf9 100644
--- a/src/client/views/EditableView.scss
+++ b/src/client/views/EditableView.scss
@@ -2,5 +2,4 @@
     overflow-wrap: break-word;
     word-wrap: break-word;
     hyphens: auto;
-    max-width: 300px;
 }
\ No newline at end of file
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index 98ef329c7..6c18e9ad5 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -84,7 +84,11 @@ export class Main extends React.Component {
         this.initEventListeners();
         this.initAuthenticationRouters();
 
-        this.initializeNorthstar();
+        try {
+            this.initializeNorthstar();
+        } catch (e) {
+
+        }
     }
 
     componentDidMount() { window.onpopstate = this.onHistory; }
diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx
index bff56e8c3..444f6fc26 100644
--- a/src/client/views/collections/CollectionBaseView.tsx
+++ b/src/client/views/collections/CollectionBaseView.tsx
@@ -88,7 +88,7 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
         var curPage = props.Document.GetNumber(KeyStore.CurPage, -1);
         doc.SetOnPrototype(KeyStore.Page, new NumberField(curPage));
         if (this.isAnnotationOverlay) {
-            doc.SetOnPrototype(KeyStore.Zoom, new NumberField(this.props.Document.GetNumber(KeyStore.Scale, 1)));
+            doc.SetNumber(KeyStore.Zoom, this.props.Document.GetNumber(KeyStore.Scale, 1));
         }
         if (curPage >= 0) {
             doc.SetOnPrototype(KeyStore.AnnotationOn, props.Document);
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 4db3bf81b..03426cb27 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -111,7 +111,11 @@ export class CollectionFreeFormView extends CollectionSubView {
 
     @action
     onPointerDown = (e: React.PointerEvent): void => {
-        if (((e.button === 2 && (!this.isAnnotationOverlay || this.zoomScaling !== 1)) || e.button === 0) && this.props.active()) {
+        let childSelected = this.props.Document.GetList(this.props.fieldKey, [] as Document[]).filter(doc => doc).reduce((childSelected, doc) => {
+            var dv = DocumentManager.Instance.getDocumentView(doc);
+            return childSelected || (dv && SelectionManager.IsSelected(dv) ? true : false);
+        }, false);
+        if (((e.button === 2 && (!this.isAnnotationOverlay || this.zoomScaling !== 1)) || (e.button === 0 && e.altKey)) && (childSelected || this.props.active())) {
             document.removeEventListener("pointermove", this.onPointerMove);
             document.addEventListener("pointermove", this.onPointerMove);
             document.removeEventListener("pointerup", this.onPointerUp);
@@ -130,38 +134,36 @@ export class CollectionFreeFormView extends CollectionSubView {
 
     @action
     onPointerMove = (e: PointerEvent): void => {
-        if (!e.cancelBubble && this.props.active()) {
-            if ((!this.isAnnotationOverlay || this.zoomScaling !== 1) && !e.shiftKey) {
-                let x = this.props.Document.GetNumber(KeyStore.PanX, 0);
-                let y = this.props.Document.GetNumber(KeyStore.PanY, 0);
-                let docs = this.props.Document.GetList(this.props.fieldKey, [] as Document[]);
-                let [dx, dy] = this.getTransform().transformDirection(e.clientX - this._lastX, e.clientY - this._lastY);
-                if (!this.isAnnotationOverlay) {
-                    let minx = docs.length ? docs[0].GetNumber(KeyStore.X, 0) : 0;
-                    let maxx = docs.length ? docs[0].GetNumber(KeyStore.Width, 0) + minx : minx;
-                    let miny = docs.length ? docs[0].GetNumber(KeyStore.Y, 0) : 0;
-                    let maxy = docs.length ? docs[0].GetNumber(KeyStore.Height, 0) + miny : miny;
-                    let ranges = docs.filter(doc => doc).reduce((range, doc) => {
-                        let x = doc.GetNumber(KeyStore.X, 0);
-                        let xe = x + doc.GetNumber(KeyStore.Width, 0);
-                        let y = doc.GetNumber(KeyStore.Y, 0);
-                        let ye = y + doc.GetNumber(KeyStore.Height, 0);
-                        return [[range[0][0] > x ? x : range[0][0], range[0][1] < xe ? xe : range[0][1]],
-                        [range[1][0] > y ? y : range[1][0], range[1][1] < ye ? ye : range[1][1]]];
-                    }, [[minx, maxx], [miny, maxy]]);
-                    let panelwidth = this._pwidth / this.scale / 2;
-                    let panelheight = this._pheight / this.scale / 2;
-                    if (x - dx < ranges[0][0] - panelwidth) x = ranges[0][1] + panelwidth + dx;
-                    if (x - dx > ranges[0][1] + panelwidth) x = ranges[0][0] - panelwidth + dx;
-                    if (y - dy < ranges[1][0] - panelheight) y = ranges[1][1] + panelheight + dy;
-                    if (y - dy > ranges[1][1] + panelheight) y = ranges[1][0] - panelheight + dy;
-                }
-                this.SetPan(x - dx, y - dy);
-                this._lastX = e.pageX;
-                this._lastY = e.pageY;
-                e.stopPropagation();
-                e.preventDefault();
+        if (!e.cancelBubble) {
+            let x = this.props.Document.GetNumber(KeyStore.PanX, 0);
+            let y = this.props.Document.GetNumber(KeyStore.PanY, 0);
+            let docs = this.props.Document.GetList(this.props.fieldKey, [] as Document[]);
+            let [dx, dy] = this.getTransform().transformDirection(e.clientX - this._lastX, e.clientY - this._lastY);
+            if (!this.isAnnotationOverlay) {
+                let minx = docs.length ? docs[0].GetNumber(KeyStore.X, 0) : 0;
+                let maxx = docs.length ? docs[0].GetNumber(KeyStore.Width, 0) + minx : minx;
+                let miny = docs.length ? docs[0].GetNumber(KeyStore.Y, 0) : 0;
+                let maxy = docs.length ? docs[0].GetNumber(KeyStore.Height, 0) + miny : miny;
+                let ranges = docs.filter(doc => doc).reduce((range, doc) => {
+                    let x = doc.GetNumber(KeyStore.X, 0);
+                    let xe = x + doc.GetNumber(KeyStore.Width, 0);
+                    let y = doc.GetNumber(KeyStore.Y, 0);
+                    let ye = y + doc.GetNumber(KeyStore.Height, 0);
+                    return [[range[0][0] > x ? x : range[0][0], range[0][1] < xe ? xe : range[0][1]],
+                    [range[1][0] > y ? y : range[1][0], range[1][1] < ye ? ye : range[1][1]]];
+                }, [[minx, maxx], [miny, maxy]]);
+                let panelwidth = this._pwidth / this.scale / 2;
+                let panelheight = this._pheight / this.scale / 2;
+                if (x - dx < ranges[0][0] - panelwidth) x = ranges[0][1] + panelwidth + dx;
+                if (x - dx > ranges[0][1] + panelwidth) x = ranges[0][0] - panelwidth + dx;
+                if (y - dy < ranges[1][0] - panelheight) y = ranges[1][1] + panelheight + dy;
+                if (y - dy > ranges[1][1] + panelheight) y = ranges[1][0] - panelheight + dy;
             }
+            this.SetPan(x - dx, y - dy);
+            this._lastX = e.pageX;
+            this._lastY = e.pageY;
+            e.stopPropagation();
+            e.preventDefault();
         }
     }
 
@@ -170,6 +172,13 @@ export class CollectionFreeFormView extends CollectionSubView {
         // if (!this.props.active()) {
         //     return;
         // }
+        let childSelected = this.props.Document.GetList(this.props.fieldKey, [] as Document[]).filter(doc => doc).reduce((childSelected, doc) => {
+            var dv = DocumentManager.Instance.getDocumentView(doc);
+            return childSelected || (dv && SelectionManager.IsSelected(dv) ? true : false);
+        }, false);
+        if (!this.props.isSelected() && !childSelected && !this.props.isTopMost) {
+            return;
+        }
         e.stopPropagation();
         let coefficient = 1000;
 
@@ -281,13 +290,23 @@ export class CollectionFreeFormView extends CollectionSubView {
 
     @computed
     get views() {
+        let pw = this.props.CollectionView.props
         var curPage = this.props.Document.GetNumber(KeyStore.CurPage, -1);
         let docviews = this.props.Document.GetList(this.props.fieldKey, [] as Document[]).filter(doc => doc).reduce((prev, doc) => {
             var page = doc.GetNumber(KeyStore.Page, -1);
             var zoom = doc.GetNumber(KeyStore.Zoom, 1);
-            var dv = DocumentManager.Instance.getDocumentView(doc);
-            let zoomFade = !this.isAnnotationOverlay || (dv && SelectionManager.IsSelected(dv)) ? 1 :
-                Math.max(0, 2 - (zoom < this.scale ? this.scale / zoom : zoom / this.scale));
+            let zoomFade = 1;
+            var documentView = DocumentManager.Instance.getDocumentView(doc);
+            if (documentView) {
+                let transform = (documentView.props.ScreenToLocalTransform().scale(documentView.props.ContentScaling())).inverse();
+                var [sptX, sptY] = transform.transformPoint(0, 0);
+                let [bptX, bptY] = transform.transformPoint(documentView.props.PanelWidth(), documentView.props.PanelHeight());
+                let w = bptX - sptX;
+                //zoomFade = area < 100 || area > 800 ? Math.max(0, Math.min(1, 2 - 5 * (zoom < this.scale ? this.scale / zoom : zoom / this.scale))) : 1;
+                let fadeUp = .75 * 1800;
+                let fadeDown = .075 * 1800;
+                zoomFade = w < fadeDown || w > fadeUp ? Math.max(0, Math.min(1, 2 - (w < fadeDown ? fadeDown / w : w / fadeUp))) : 1;
+            }
             if (page === curPage || page === -1) {
                 prev.push(<CollectionFreeFormDocumentView key={doc.Id} {...this.getDocumentViewProps(doc)} zoomFade={zoomFade} />);
             }
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index dbab790d4..783470286 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -160,7 +160,6 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
                 pany: 0,
                 width: bounds.width,
                 height: bounds.height,
-                backgroundColor: "Transparent",
                 ink: inkData ? this.marqueeInkSelect(inkData) : undefined,
                 title: "a nested collection"
             });
diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss
index a946ac1a8..5071c9440 100644
--- a/src/client/views/nodes/DocumentView.scss
+++ b/src/client/views/nodes/DocumentView.scss
@@ -1,6 +1,6 @@
 @import "../globalCssVariables";
 
-.documentView-node {
+.documentView-node .documentView-node-topMost {
   position: inherit;
   top: 0;
   left:0;
@@ -28,6 +28,9 @@
     height: calc(100% - 20px);
   }
 }
+.documentView-node-topMost {
+    background: white;
+}
 
 .minimized-box {
   height: 10px;
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index bcd746024..20592894f 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -212,7 +212,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         }
     }
     fullScreenClicked = (e: React.MouseEvent): void => {
-        CollectionDockingView.Instance.OpenFullScreen(this.props.Document);
+        CollectionDockingView.Instance.OpenFullScreen((this.props.Document.GetPrototype() as Document).MakeDelegate());
         ContextMenu.Instance.clearItems();
         ContextMenu.Instance.addItem({
             description: "Close Full Screen",
@@ -425,7 +425,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
             );
             return (
                 <div
-                    className="documentView-node"
+                    className={`documentView-node${this.props.isTopMost ? "-topmost" : ""}`}
                     ref={this._mainCont}
                     style={{
                         background: backgroundcolor,
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index ad1ed5df0..8963484b6 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -42,7 +42,6 @@ export interface FormattedTextBoxOverlay {
     isOverlay?: boolean;
 }
 
-@observer
 export class FormattedTextBox extends React.Component<(FieldViewProps & FormattedTextBoxOverlay)> {
     public static LayoutString(fieldStr: string = "DataKey") {
         return FieldView.LayoutString(FormattedTextBox, fieldStr);
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index 29e4af160..ddbec014b 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -26,12 +26,6 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
         super(props);
     }
 
-
-
-    shouldComponentUpdate() {
-        return false;
-    }
-
     @action
     onEnterKey = (e: React.KeyboardEvent): void => {
         if (e.key === 'Enter') {
diff --git a/src/client/views/nodes/WebBox.scss b/src/client/views/nodes/WebBox.scss
index c73bc0c47..2ad1129a4 100644
--- a/src/client/views/nodes/WebBox.scss
+++ b/src/client/views/nodes/WebBox.scss
@@ -9,6 +9,12 @@
     overflow: scroll;
 }
 
+#webBox-htmlSpan {
+    position: absolute;
+    top:0;
+    left:0;
+}
+
 .webBox-button {
     padding : 0vw;
     border: none;
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 90ce72c41..1edb4d826 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -18,21 +18,40 @@ export class WebBox extends React.Component<FieldViewProps> {
 
     @computed get html(): string { return this.props.Document.GetHtml(KeyStore.Data, ""); }
 
+    _ignore = 0;
+    onPreWheel = (e: React.WheelEvent) => {
+        this._ignore = e.timeStamp;
+    }
+    onPrePointer = (e: React.PointerEvent) => {
+        this._ignore = e.timeStamp;
+    }
+    onPostPointer = (e: React.PointerEvent) => {
+        if (this._ignore !== e.timeStamp) {
+            e.stopPropagation();
+        }
+    }
+    onPostWheel = (e: React.WheelEvent) => {
+        if (this._ignore !== e.timeStamp) {
+            e.stopPropagation();
+        }
+    }
     render() {
         let field = this.props.Document.Get(this.props.fieldKey);
         let path = field === FieldWaiting ? "https://image.flaticon.com/icons/svg/66/66163.svg" :
             field instanceof WebField ? field.Data.href : "https://crossorigin.me/" + "https://cs.brown.edu";
 
-        let content = this.html ?
-            <span dangerouslySetInnerHTML={{ __html: this.html }}></span> :
-            <div style={{ width: "100%", height: "100%", position: "absolute" }}>
-                <iframe src={path} style={{ position: "absolute", width: "100%", height: "100%" }}></iframe>
-                {this.props.isSelected() ? (null) : <div style={{ width: "100%", height: "100%", position: "absolute" }} />}
+        let content =
+            <div style={{ width: "100%", height: "100%", position: "absolute" }} onWheel={this.onPostWheel} onPointerDown={this.onPostPointer} onPointerMove={this.onPostPointer} onPointerUp={this.onPostPointer}>
+                {this.html ? <span id="webBox-htmlSpan" dangerouslySetInnerHTML={{ __html: this.html }} /> :
+                    <iframe src={path} style={{ position: "absolute", width: "100%", height: "100%" }} />}
             </div>;
 
         return (
-            <div className="webBox-cont"  >
-                {content}
-            </div>);
+            <>
+                <div className="webBox-cont"  >
+                    {content}
+                </div>
+                {this.props.isSelected() ? (null) : <div onWheel={this.onPreWheel} onPointerDown={this.onPrePointer} onPointerMove={this.onPrePointer} onPointerUp={this.onPrePointer} style={{ width: "100%", height: "100%", position: "absolute" }} />}
+            </>);
     }
 }
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2


From e981d9d228507fc573febc997eb691a5428126e8 Mon Sep 17 00:00:00 2001
From: laurawilsonri <laura_wilson@brown.edu>
Date: Mon, 15 Apr 2019 17:40:38 -0400
Subject: fixed small pointer event bug with tooltip menu

---
 src/.DS_Store                               | Bin 6148 -> 6148 bytes
 src/client/util/TooltipTextMenu.scss        |   3 ++-
 src/client/util/TooltipTextMenu.tsx         |   2 +-
 src/client/views/nodes/FormattedTextBox.tsx |   1 -
 4 files changed, 3 insertions(+), 3 deletions(-)

(limited to 'src/client/views/nodes/FormattedTextBox.tsx')

diff --git a/src/.DS_Store b/src/.DS_Store
index 90213270f..d70e95c0a 100644
Binary files a/src/.DS_Store and b/src/.DS_Store differ
diff --git a/src/client/util/TooltipTextMenu.scss b/src/client/util/TooltipTextMenu.scss
index 8fddbd3cf..a9a98c426 100644
--- a/src/client/util/TooltipTextMenu.scss
+++ b/src/client/util/TooltipTextMenu.scss
@@ -58,7 +58,7 @@
     position: absolute;
     background: $dark-color;
     color:white;
-    border: 1px solid #aaa;
+    border: 1px solid rgb(223, 223, 223);
     padding: 2px;
   }
   
@@ -246,6 +246,7 @@
     margin-bottom: 7px;
     -webkit-transform: translateX(-50%);
     transform: translateX(-50%);
+    pointer-events: all;
 }
 
 .tooltipMenu:before {
diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx
index 3869db41a..a28a8eaeb 100644
--- a/src/client/util/TooltipTextMenu.tsx
+++ b/src/client/util/TooltipTextMenu.tsx
@@ -38,7 +38,7 @@ export class TooltipTextMenu {
         this.tooltip.className = "tooltipMenu";
 
         //add the div which is the tooltip
-        view.dom.parentNode!.appendChild(this.tooltip);
+        view.dom.parentNode!.parentNode!.appendChild(this.tooltip);
 
         //add additional icons
         library.add(faListUl);
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 19ed65272..be530416e 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -249,7 +249,6 @@ export class FormattedTextBox extends React.Component<(FieldViewProps & Formatte
         return (
             <div
                 className={`formattedTextBox-cont`}
-                style={{ overflow: "visible" }}
                 onKeyDown={this.onKeyPress}
                 onKeyPress={this.onKeyPress}
                 onFocus={this.onFocused}
-- 
cgit v1.2.3-70-g09d2


From 47ff32717a209e706b39265fbdc857f398094fe8 Mon Sep 17 00:00:00 2001
From: Sam Wilkins <abdullah_ahmed@brown.edu>
Date: Mon, 15 Apr 2019 18:53:25 -0400
Subject: updated list making and (un)indenting key commands) in prosemirror
 mapping

---
 src/client/util/ProsemirrorKeymap.ts        | 8 ++++----
 src/client/views/nodes/FormattedTextBox.tsx | 1 +
 2 files changed, 5 insertions(+), 4 deletions(-)

(limited to 'src/client/views/nodes/FormattedTextBox.tsx')

diff --git a/src/client/util/ProsemirrorKeymap.ts b/src/client/util/ProsemirrorKeymap.ts
index 7718b3c06..00d086b97 100644
--- a/src/client/util/ProsemirrorKeymap.ts
+++ b/src/client/util/ProsemirrorKeymap.ts
@@ -50,10 +50,10 @@ export default function buildKeymap<S extends Schema<any>>(schema: S, mapKeys?:
     }
 
     if (type = schema.nodes.bullet_list) {
-        bind("Shift-Ctrl-8", wrapInList(type));
+        bind("Ctrl-b", wrapInList(type));
     }
     if (type = schema.nodes.ordered_list) {
-        bind("Shift-Ctrl-9", wrapInList(type));
+        bind("Ctrl-n", wrapInList(type));
     }
     if (type = schema.nodes.blockquote) {
         bind("Ctrl->", wrapIn(type));
@@ -74,8 +74,8 @@ export default function buildKeymap<S extends Schema<any>>(schema: S, mapKeys?:
     }
     if (type = schema.nodes.list_item) {
         bind("Enter", splitListItem(type));
-        bind("Mod-[", liftListItem(type));
-        bind("Mod-]", sinkListItem(type));
+        bind("Shift-Tab", liftListItem(type));
+        bind("Tab", sinkListItem(type));
     }
     if (type = schema.nodes.paragraph) {
         bind("Shift-Ctrl-0", setBlockType(type));
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index be530416e..7e72c6398 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -241,6 +241,7 @@ export class FormattedTextBox extends React.Component<(FieldViewProps & Formatte
     }
     onKeyPress(e: React.KeyboardEvent) {
         e.stopPropagation();
+        if (e.keyCode === 9) e.preventDefault();
         // stop propagation doesn't seem to stop propagation of native keyboard events.
         // so we set a flag on the native event that marks that the event's been handled.
         // (e.nativeEvent as any).DASHFormattedTextBoxHandled = true;
-- 
cgit v1.2.3-70-g09d2


From 1439ae372fb6a3cc136dffc0a9e9da3d839dfcff Mon Sep 17 00:00:00 2001
From: Sam Wilkins <abdullah_ahmed@brown.edu>
Date: Mon, 15 Apr 2019 20:07:07 -0400
Subject: beginning linking tooltip

---
 src/client/util/TooltipLinkingMenu.tsx      | 86 +++++++++++++++++++++++++++++
 src/client/util/TooltipTextMenu.scss        |  4 ++
 src/client/views/nodes/FormattedTextBox.tsx | 18 +++++-
 3 files changed, 105 insertions(+), 3 deletions(-)
 create mode 100644 src/client/util/TooltipLinkingMenu.tsx

(limited to 'src/client/views/nodes/FormattedTextBox.tsx')

diff --git a/src/client/util/TooltipLinkingMenu.tsx b/src/client/util/TooltipLinkingMenu.tsx
new file mode 100644
index 000000000..55e0eb909
--- /dev/null
+++ b/src/client/util/TooltipLinkingMenu.tsx
@@ -0,0 +1,86 @@
+import { action, IReactionDisposer, reaction } from "mobx";
+import { Dropdown, DropdownSubmenu, MenuItem, MenuItemSpec, renderGrouped, icons, } from "prosemirror-menu"; //no import css
+import { baseKeymap, lift } from "prosemirror-commands";
+import { history, redo, undo } from "prosemirror-history";
+import { keymap } from "prosemirror-keymap";
+import { EditorState, Transaction, NodeSelection, TextSelection } from "prosemirror-state";
+import { EditorView } from "prosemirror-view";
+import { schema } from "./RichTextSchema";
+import { Schema, NodeType, MarkType } from "prosemirror-model";
+import React = require("react");
+import "./TooltipTextMenu.scss";
+const { toggleMark, setBlockType, wrapIn } = require("prosemirror-commands");
+import { library } from '@fortawesome/fontawesome-svg-core';
+import { wrapInList, bulletList, liftListItem, listItem } from 'prosemirror-schema-list';
+import {
+    faListUl,
+} from '@fortawesome/free-solid-svg-icons';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { FieldViewProps } from "../views/nodes/FieldView";
+import { throwStatement } from "babel-types";
+
+const SVG = "http://www.w3.org/2000/svg";
+
+//appears above a selection of text in a RichTextBox to give user options such as Bold, Italics, etc.
+export class TooltipLinkingMenu {
+
+    private tooltip: HTMLElement;
+    private view: EditorView;
+    private editorProps: FieldViewProps;
+
+    constructor(view: EditorView, editorProps: FieldViewProps) {
+        this.view = view;
+        this.editorProps = editorProps;
+        this.tooltip = document.createElement("div");
+        this.tooltip.className = "tooltipMenu linking";
+
+        //add the div which is the tooltip
+        view.dom.parentNode!.parentNode!.appendChild(this.tooltip);
+
+        let target = "https://www.google.com";
+
+        let link = document.createElement("a");
+        link.href = target;
+        link.textContent = target;
+        link.target = "_blank";
+        link.style.color = "white";
+        this.tooltip.appendChild(link);
+
+        this.update(view, undefined);
+    }
+
+    //updates the tooltip menu when the selection changes
+    update(view: EditorView, lastState: EditorState | undefined) {
+        let state = view.state;
+        // Don't do anything if the document/selection didn't change
+        if (lastState && lastState.doc.eq(state.doc) &&
+            lastState.selection.eq(state.selection)) return;
+
+        // Hide the tooltip if the selection is empty
+        if (state.selection.empty) {
+            this.tooltip.style.display = "none";
+            return;
+        }
+
+        console.log("STORED:");
+        console.log(state.doc.content.firstChild!.content);
+
+        // Otherwise, reposition it and update its content
+        this.tooltip.style.display = "";
+        let { from, to } = state.selection;
+        let start = view.coordsAtPos(from), end = view.coordsAtPos(to);
+        // The box in which the tooltip is positioned, to use as base
+        let box = this.tooltip.offsetParent!.getBoundingClientRect();
+        // Find a center-ish x position from the selection endpoints (when
+        // crossing lines, end may be more to the left)
+        let left = Math.max((start.left + end.left) / 2, start.left + 3);
+        this.tooltip.style.left = (left - box.left) * this.editorProps.ScreenToLocalTransform().Scale + "px";
+        let width = Math.abs(start.left - end.left) / 2 * this.editorProps.ScreenToLocalTransform().Scale;
+        let mid = Math.min(start.left, end.left) + width;
+
+        this.tooltip.style.width = "auto";
+        this.tooltip.style.bottom = (box.bottom - start.top) * this.editorProps.ScreenToLocalTransform().Scale + "px";
+    }
+
+    destroy() { this.tooltip.remove(); }
+}
diff --git a/src/client/util/TooltipTextMenu.scss b/src/client/util/TooltipTextMenu.scss
index a9a98c426..3ca3431c0 100644
--- a/src/client/util/TooltipTextMenu.scss
+++ b/src/client/util/TooltipTextMenu.scss
@@ -67,6 +67,10 @@
     min-width: 6em;
   }
   
+  .linking {
+      text-align: center;
+  }
+
   .ProseMirror-menu-dropdown-item {
     cursor: pointer;
     padding: 2px 8px 2px 4px;
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 7e72c6398..6eedf7561 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -19,6 +19,7 @@ import { undoItem } from "prosemirror-menu";
 import buildKeymap from "../../util/ProsemirrorKeymap";
 import { TextField } from "../../../fields/TextField";
 import { KeyStore } from "../../../fields/KeyStore";
+import { TooltipLinkingMenu } from "../../util/TooltipLinkingMenu";
 const { buildMenuItems } = require("prosemirror-example-setup");
 const { menuBar } = require("prosemirror-menu");
 
@@ -86,7 +87,8 @@ export class FormattedTextBox extends React.Component<(FieldViewProps & Formatte
                 history(),
                 keymap(buildKeymap(schema)),
                 keymap(baseKeymap),
-                this.tooltipMenuPlugin(),
+                this.tooltipTextMenuPlugin(),
+                // this.tooltipLinkingMenuPlugin(),
                 new Plugin({
                     props: {
                         attributes: { class: "ProseMirror-example-setup-style" }
@@ -94,7 +96,7 @@ export class FormattedTextBox extends React.Component<(FieldViewProps & Formatte
                 })
             ] : [
                     history(),
-                    keymap({ "Mod-z": undo, "Mod-y": redo }),
+                    keymap(buildKeymap(schema)),
                     keymap(baseKeymap),
                 ]
         };
@@ -231,7 +233,7 @@ export class FormattedTextBox extends React.Component<(FieldViewProps & Formatte
         e.stopPropagation();
     }
 
-    tooltipMenuPlugin() {
+    tooltipTextMenuPlugin() {
         let myprops = this.props;
         return new Plugin({
             view(_editorView) {
@@ -239,6 +241,16 @@ export class FormattedTextBox extends React.Component<(FieldViewProps & Formatte
             }
         });
     }
+
+    tooltipLinkingMenuPlugin() {
+        let myprops = this.props;
+        return new Plugin({
+            view(_editorView) {
+                return new TooltipLinkingMenu(_editorView, myprops);
+            }
+        });
+    }
+
     onKeyPress(e: React.KeyboardEvent) {
         e.stopPropagation();
         if (e.keyCode === 9) e.preventDefault();
-- 
cgit v1.2.3-70-g09d2


From c93d46a0d0692569d3a3a2a30c909d9ecbc40830 Mon Sep 17 00:00:00 2001
From: laurawilsonri <laura_wilson@brown.edu>
Date: Mon, 15 Apr 2019 20:07:37 -0400
Subject: added a font size indicator

---
 src/client/util/RichTextSchema.tsx          |   7 ++
 src/client/util/TooltipTextMenu.scss        |   6 +-
 src/client/util/TooltipTextMenu.tsx         | 126 +++++++++++++++++++---------
 src/client/views/nodes/FormattedTextBox.tsx |   1 -
 4 files changed, 100 insertions(+), 40 deletions(-)

(limited to 'src/client/views/nodes/FormattedTextBox.tsx')

diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx
index 42bcf2ae2..9ef71e305 100644
--- a/src/client/util/RichTextSchema.tsx
+++ b/src/client/util/RichTextSchema.tsx
@@ -267,6 +267,13 @@ export const marks: { [index: string]: MarkSpec } = {
         }]
     },
 
+    crimson: {
+        parseDOM: [{ style: 'font-family: "Crimson Text", sans-serif;' }],
+        toDOM: () => ['span', {
+            style: 'font-family: "Crimson Text", sans-serif;'
+        }]
+    },
+
     /** FONT SIZES */
 
     p10: {
diff --git a/src/client/util/TooltipTextMenu.scss b/src/client/util/TooltipTextMenu.scss
index a9a98c426..7e6659a52 100644
--- a/src/client/util/TooltipTextMenu.scss
+++ b/src/client/util/TooltipTextMenu.scss
@@ -289,4 +289,8 @@
   .underline {text-decoration: underline}
   .superscript {vertical-align:super}
   .subscript { vertical-align:sub }
-  .strikethrough {text-decoration-line:line-through}
\ No newline at end of file
+  .strikethrough {text-decoration-line:line-through}
+  .font-size-indicator {
+      font-size: 12px;
+      padding-right: 0px;
+  }
diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx
index a28a8eaeb..a92cbd263 100644
--- a/src/client/util/TooltipTextMenu.tsx
+++ b/src/client/util/TooltipTextMenu.tsx
@@ -30,9 +30,14 @@ export class TooltipTextMenu {
     private fontStyles: MarkType[];
     private fontSizes: MarkType[];
     private editorProps: FieldViewProps;
+    private state: EditorState;
+    private fontSizeToNum: Map<MarkType, number>;
+    private fontStylesToName: Map<MarkType, string>;
+    private fontSizeIndicator: HTMLSpanElement = document.createElement("span");
 
     constructor(view: EditorView, editorProps: FieldViewProps) {
         this.view = view;
+        this.state = view.state;
         this.editorProps = editorProps;
         this.tooltip = document.createElement("div");
         this.tooltip.className = "tooltipMenu";
@@ -66,25 +71,28 @@ export class TooltipTextMenu {
 
         });
 
-        //dropdowns
-        //list of font btns to add
-        this.fontStyles = [
-            schema.marks.timesNewRoman,
-            schema.marks.arial,
-            schema.marks.georgia,
-            schema.marks.comicSans,
-            schema.marks.tahoma,
-            schema.marks.impact,
-        ];
-        this.fontSizes = [
-            schema.marks.p10,
-            schema.marks.p12,
-            schema.marks.p16,
-            schema.marks.p24,
-            schema.marks.p32,
-            schema.marks.p48,
-            schema.marks.p72,
-        ];
+        //list of font styles
+        this.fontStylesToName = new Map();
+        this.fontStylesToName.set(schema.marks.timesNewRoman, "Times New Roman");
+        this.fontStylesToName.set(schema.marks.arial, "Arial");
+        this.fontStylesToName.set(schema.marks.georgia, "Georgia");
+        this.fontStylesToName.set(schema.marks.comicSans, "Comic Sans");
+        this.fontStylesToName.set(schema.marks.tahoma, "Tahoma");
+        this.fontStylesToName.set(schema.marks.impact, "Impact");
+        this.fontStylesToName.set(schema.marks.crimson, "Crimson Text");
+        this.fontStyles = Array.from(this.fontStylesToName.keys());
+
+        //font sizes
+        this.fontSizeToNum = new Map();
+        this.fontSizeToNum.set(schema.marks.p10, 10);
+        this.fontSizeToNum.set(schema.marks.p12, 12);
+        this.fontSizeToNum.set(schema.marks.p16, 16);
+        this.fontSizeToNum.set(schema.marks.p24, 24);
+        this.fontSizeToNum.set(schema.marks.p32, 32);
+        this.fontSizeToNum.set(schema.marks.p48, 48);
+        this.fontSizeToNum.set(schema.marks.p72, 72);
+        this.fontSizes = Array.from(this.fontSizeToNum.keys());
+
         this.addFontDropdowns();
 
         this.update(view, undefined);
@@ -94,30 +102,29 @@ export class TooltipTextMenu {
     addFontDropdowns() {
         //filtering function - might be unecessary
         let cut = (arr: MenuItem[]) => arr.filter(x => x);
-        let fontBtns = [
-            this.dropdownBtn("Times New Roman", "font-family: Times New Roman, Times, serif; width: 120px;", schema.marks.timesNewRoman, this.view, this.changeToMarkInGroup, this.fontStyles),
-            this.dropdownBtn("Arial", "font-family: Arial, Helvetica, sans-serif; width: 120px;", schema.marks.arial, this.view, this.changeToMarkInGroup, this.fontStyles),
-            this.dropdownBtn("Georgia", "font-family: Georgia, serif; width: 120px; width: 120px;", schema.marks.georgia, this.view, this.changeToMarkInGroup, this.fontStyles),
-            this.dropdownBtn("ComicSans", "font-family: Comic Sans MS, cursive, sans-serif; width: 120px;", schema.marks.comicSans, this.view, this.changeToMarkInGroup, this.fontStyles),
-            this.dropdownBtn("Tahoma", "font-family: Tahoma, Geneva, sans-serif; width: 120px;", schema.marks.tahoma, this.view, this.changeToMarkInGroup, this.fontStyles),
-            this.dropdownBtn("Impact", "font-family: Impact, Charcoal, sans-serif; width: 120px;", schema.marks.impact, this.view, this.changeToMarkInGroup, this.fontStyles),
-        ];
+        //font STYLES
+        let fontBtns: MenuItem[] = [];
+        this.fontStylesToName.forEach((name, mark) => {
+            fontBtns.push(this.dropdownBtn(name, "font-family: " + name + ", sans-serif; width: 120px;", mark, this.view, this.changeToMarkInGroup, this.fontStyles));
+        });
 
-        let fontSizeBtns = [
-            this.dropdownBtn("10", "width: 50px;", schema.marks.p10, this.view, this.changeToMarkInGroup, this.fontSizes),
-            this.dropdownBtn("12", "width: 50px;", schema.marks.p12, this.view, this.changeToMarkInGroup, this.fontSizes),
-            this.dropdownBtn("16", "width: 50px;", schema.marks.p16, this.view, this.changeToMarkInGroup, this.fontSizes),
-            this.dropdownBtn("24", "width: 50px;", schema.marks.p24, this.view, this.changeToMarkInGroup, this.fontSizes),
-            this.dropdownBtn("32", "width: 50px;", schema.marks.p32, this.view, this.changeToMarkInGroup, this.fontSizes),
-            this.dropdownBtn("48", "width: 50px;", schema.marks.p48, this.view, this.changeToMarkInGroup, this.fontSizes),
-            this.dropdownBtn("72", "width: 50px;", schema.marks.p72, this.view, this.changeToMarkInGroup, this.fontSizes),
-        ];
+        //font size indicator
+        this.fontSizeIndicator = this.icon("12", "font-size-indicator");
+
+        //font SIZES
+        let fontSizeBtns: MenuItem[] = [];
+        this.fontSizeToNum.forEach((number, mark) => {
+            fontSizeBtns.push(this.dropdownBtn(String(number), "width: 50px;", mark, this.view, this.changeToMarkInGroup, this.fontSizes));
+        });
 
         //dropdown to hold font btns
         let dd_fontStyle = new Dropdown(cut(fontBtns), { label: "Font Style", css: "color:white;" }) as MenuItem;
-        let dd_fontSize = new Dropdown(cut(fontSizeBtns), { label: "Font Size", css: "color:white;" }) as MenuItem;
+        let dd_fontSize = new Dropdown(cut(fontSizeBtns), { label: "Font Size", css: "color:white; margin-left: -6px;" }) as MenuItem;
         this.tooltip.appendChild(dd_fontStyle.render(this.view).dom);
+        this.tooltip.appendChild(this.fontSizeIndicator);
         this.tooltip.appendChild(dd_fontSize.render(this.view).dom);
+        dd_fontStyle.render(this.view).dom.nodeValue = "TEST";
+        console.log(dd_fontStyle.render(this.view).dom.nodeValue);
     }
 
     //for a specific grouping of marks (passed in), remove all and apply the passed-in one to the selected text
@@ -169,7 +176,7 @@ export class TooltipTextMenu {
     // Helper function to create menu icons
     icon(text: string, name: string) {
         let span = document.createElement("span");
-        span.className = "menuicon " + name;
+        span.className = name + " menuicon";
         span.title = name;
         span.textContent = text;
         span.style.color = "white";
@@ -241,6 +248,49 @@ export class TooltipTextMenu {
 
         this.tooltip.style.width = 220 + "px";
         this.tooltip.style.bottom = (box.bottom - start.top) * this.editorProps.ScreenToLocalTransform().Scale + "px";
+
+        let activeStyles = this.activeMarksOnSelection(this.fontStyles);
+        if (activeStyles.length === 1) {
+            // if we want to update something somewhere with active font name
+            let fontName = this.fontStylesToName.get(activeStyles[0]);
+        } else if (activeStyles.length === 0) {
+            //crimson on default
+        }
+
+        //update font size indicator
+        let activeSizes = this.activeMarksOnSelection(this.fontSizes);
+        if (activeSizes.length === 1) { //if there's only one active font size
+            let size = this.fontSizeToNum.get(activeSizes[0]);
+            if (size) {
+                this.fontSizeIndicator.innerHTML = String(size);
+            }
+            //should be 14 on default
+        } else if (activeSizes.length === 0) {
+            this.fontSizeIndicator.innerHTML = "14";
+            //multiple font sizes selected
+        } else {
+            this.fontSizeIndicator.innerHTML = "";
+        }
+    }
+
+    //finds all active marks on selection
+    activeMarksOnSelection(markGroup: MarkType[]) {
+        //current selection
+        let { empty, $cursor, ranges } = this.view.state.selection as TextSelection;
+        let state = this.view.state;
+        let dispatch = this.view.dispatch;
+
+        let activeMarks = markGroup.filter(mark => {
+            if (dispatch) {
+                let has = false, tr = state.tr;
+                for (let i = 0; !has && i < ranges.length; i++) {
+                    let { $from, $to } = ranges[i];
+                    return state.doc.rangeHasMark($from.pos, $to.pos, mark);
+                }
+            }
+            return false;
+        });
+        return activeMarks;
     }
 
     destroy() { this.tooltip.remove(); }
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index be530416e..631139d32 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -177,7 +177,6 @@ export class FormattedTextBox extends React.Component<(FieldViewProps & Formatte
         // doc.SetData(fieldKey, e.target.value, RichTextField);
     }
     onPointerDown = (e: React.PointerEvent): void => {
-        console.log("pointer down");
         if (e.button === 1 && this.props.isSelected() && !e.altKey && !e.ctrlKey && !e.metaKey) {
             console.log("first");
             e.stopPropagation();
-- 
cgit v1.2.3-70-g09d2


From feae8f4d314ef389cc544fd3ad0792a6bb04832c Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Tue, 16 Apr 2019 12:38:42 -0400
Subject: fixed some text focus issues. fixed contextmenu a bit.  removed
 upload files.

---
 package.json                                       |   2 +-
 src/client/util/SelectionManager.ts                |  14 ++++++
 src/client/views/MainOverlayTextBox.tsx            |   1 -
 src/client/views/_global_variables.ts              |   8 ---
 .../collectionFreeForm/CollectionFreeFormView.tsx  |   2 +-
 src/client/views/nodes/DocumentView.tsx            |   3 +-
 src/client/views/nodes/FormattedTextBox.scss       |   6 ++-
 src/client/views/nodes/FormattedTextBox.tsx        |  55 ++++++++++-----------
 .../upload_1b4818f39ea324b5a687bb1ade3dca6c.jpg    | Bin 26946 -> 0 bytes
 .../upload_1f1c6cfef33e5992fa860802e8c466a7.jpg    | Bin 45309 -> 0 bytes
 .../upload_2045f363aa9cf281407703ca242aad1a.jpg    | Bin 9009 -> 0 bytes
 .../upload_25bffd90c080c27f5ac822984406b958.jpg    | Bin 43534 -> 0 bytes
 .../upload_261f11dc39ad568212b5c7e39d1e6d13.jpg    | Bin 27259 -> 0 bytes
 .../upload_26bcc62639141ba64e603daebb5bf5d3.png    | Bin 2757327 -> 0 bytes
 .../upload_2d77d0773612e4723b78118ac50a2929.jpg    | Bin 1805512 -> 0 bytes
 .../upload_2de9ad4dc687c53760c39f724c9a08a5.jpg    | Bin 77462 -> 0 bytes
 .../upload_4abb568aa7cce9d291532c3d0da97102.jpg    | Bin 22445 -> 0 bytes
 .../upload_54c34aaca5a7bf510cebad461ec39512.png    | Bin 2757327 -> 0 bytes
 .../upload_562b1e527300df8b350eeab094b3e1f1.jpg    | Bin 15988 -> 0 bytes
 .../upload_6a26d3f7008a8c79ee5fc8054ba69996.jpg    | Bin 45025 -> 0 bytes
 .../upload_70fa5e0c3f393504349d5865e28f4cac.jpg    | Bin 18041 -> 0 bytes
 .../upload_8155b5b0f57da107bb07083c04e78943.jpg    | Bin 31103 -> 0 bytes
 .../upload_88f588574e0efc415186af935114af9a.jpg    | Bin 40249 -> 0 bytes
 .../upload_8d1c253f93f77c69c0c04ae3efb7d714.png    | Bin 2757327 -> 0 bytes
 .../upload_9ef80158609f5ff739087aecad367b9d.jpg    | Bin 28523 -> 0 bytes
 .../upload_c39a7e0d7e8d35bb18461a5a0aa063bf.jpg    | Bin 13811 -> 0 bytes
 .../upload_c6b81ab4eb70465a7e9b45d5c8f3ecaa.jpg    | Bin 28566 -> 0 bytes
 .../upload_c99ec7a8a2df0b2f90479fde7d70c2eb.jpg    | Bin 21995 -> 0 bytes
 .../upload_cec1cfcc67cfe5889de4098a49fec45e.jpg    | Bin 22125 -> 0 bytes
 .../upload_f27688fe92dc7de398e957e5d96e1a22.jpg    | Bin 18964 -> 0 bytes
 30 files changed, 48 insertions(+), 43 deletions(-)
 delete mode 100644 src/client/views/_global_variables.ts
 delete mode 100644 src/server/public/files/upload_1b4818f39ea324b5a687bb1ade3dca6c.jpg
 delete mode 100644 src/server/public/files/upload_1f1c6cfef33e5992fa860802e8c466a7.jpg
 delete mode 100644 src/server/public/files/upload_2045f363aa9cf281407703ca242aad1a.jpg
 delete mode 100644 src/server/public/files/upload_25bffd90c080c27f5ac822984406b958.jpg
 delete mode 100644 src/server/public/files/upload_261f11dc39ad568212b5c7e39d1e6d13.jpg
 delete mode 100644 src/server/public/files/upload_26bcc62639141ba64e603daebb5bf5d3.png
 delete mode 100644 src/server/public/files/upload_2d77d0773612e4723b78118ac50a2929.jpg
 delete mode 100644 src/server/public/files/upload_2de9ad4dc687c53760c39f724c9a08a5.jpg
 delete mode 100644 src/server/public/files/upload_4abb568aa7cce9d291532c3d0da97102.jpg
 delete mode 100644 src/server/public/files/upload_54c34aaca5a7bf510cebad461ec39512.png
 delete mode 100644 src/server/public/files/upload_562b1e527300df8b350eeab094b3e1f1.jpg
 delete mode 100644 src/server/public/files/upload_6a26d3f7008a8c79ee5fc8054ba69996.jpg
 delete mode 100644 src/server/public/files/upload_70fa5e0c3f393504349d5865e28f4cac.jpg
 delete mode 100644 src/server/public/files/upload_8155b5b0f57da107bb07083c04e78943.jpg
 delete mode 100644 src/server/public/files/upload_88f588574e0efc415186af935114af9a.jpg
 delete mode 100644 src/server/public/files/upload_8d1c253f93f77c69c0c04ae3efb7d714.png
 delete mode 100644 src/server/public/files/upload_9ef80158609f5ff739087aecad367b9d.jpg
 delete mode 100644 src/server/public/files/upload_c39a7e0d7e8d35bb18461a5a0aa063bf.jpg
 delete mode 100644 src/server/public/files/upload_c6b81ab4eb70465a7e9b45d5c8f3ecaa.jpg
 delete mode 100644 src/server/public/files/upload_c99ec7a8a2df0b2f90479fde7d70c2eb.jpg
 delete mode 100644 src/server/public/files/upload_cec1cfcc67cfe5889de4098a49fec45e.jpg
 delete mode 100644 src/server/public/files/upload_f27688fe92dc7de398e957e5d96e1a22.jpg

(limited to 'src/client/views/nodes/FormattedTextBox.tsx')

diff --git a/package.json b/package.json
index 489bce7e1..2463afa74 100644
--- a/package.json
+++ b/package.json
@@ -170,4 +170,4 @@
         "uuid": "^3.3.2",
         "xoauth2": "^1.2.0"
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index c56f6a4ff..320553952 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -28,6 +28,16 @@ export namespace SelectionManager {
             manager.SelectedDocuments = [];
             MainOverlayTextBox.Instance.SetTextDoc();
         }
+        @action
+        ReselectAll() {
+            let sdocs = manager.SelectedDocuments.map(d => d);
+            manager.SelectedDocuments = [];
+            return sdocs;
+        }
+        @action
+        ReselectAll2(sdocs: DocumentView[]) {
+            sdocs.map(s => SelectionManager.SelectDoc(s, false));
+        }
     }
 
     const manager = new Manager();
@@ -52,6 +62,10 @@ export namespace SelectionManager {
         if (found) manager.SelectDoc(found, false);
     }
 
+    export function ReselectAll() {
+        let sdocs = manager.ReselectAll();
+        manager.ReselectAll2(sdocs);
+    }
     export function SelectedDocuments(): Array<DocumentView> {
         return manager.SelectedDocuments;
     }
diff --git a/src/client/views/MainOverlayTextBox.tsx b/src/client/views/MainOverlayTextBox.tsx
index 422a45d59..141b3ad74 100644
--- a/src/client/views/MainOverlayTextBox.tsx
+++ b/src/client/views/MainOverlayTextBox.tsx
@@ -40,7 +40,6 @@ export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps>
             this._textTargetDiv.style.color = this._textColor;
         }
 
-        this.TextDoc = undefined;
         this.TextDoc = textDoc;
         this._textFieldKey = textFieldKey!;
         this._textXf = tx ? tx : Transform.Identity();
diff --git a/src/client/views/_global_variables.ts b/src/client/views/_global_variables.ts
deleted file mode 100644
index 10482bc8d..000000000
--- a/src/client/views/_global_variables.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-import * as globalStyleVariables from "../views/globalCssVariables.scss"
-
-export interface I_globalScss {
-    contextMenuZindex: string;  // context menu shows up over everything
-}
-let globalStyles = globalStyleVariables as any as I_globalScss;
-
-export default globalStyles;
\ No newline at end of file
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index fa3017258..50f0a6164 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -81,7 +81,6 @@ export class CollectionFreeFormView extends CollectionSubView {
                 de.data.droppedDocuments.map(d => {
                     d.SetNumber(KeyStore.X, x + (d.GetNumber(KeyStore.X, 0) - dropX));
                     d.SetNumber(KeyStore.Y, y + (d.GetNumber(KeyStore.Y, 0) - dropY));
-                    console.log("x = " + d.GetNumber(KeyStore.X, 0) + " y = " + d.GetNumber(KeyStore.X, 0));
                     if (!d.GetNumber(KeyStore.Width, 0)) {
                         d.SetNumber(KeyStore.Width, 300);
                     }
@@ -90,6 +89,7 @@ export class CollectionFreeFormView extends CollectionSubView {
                     }
                     this.bringToFront(d);
                 });
+                SelectionManager.ReselectAll();
             }
             return true;
         }
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 0d974c97e..1cbb24223 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -277,11 +277,12 @@ export class DocumentView extends React.Component<DocumentViewProps> {
     @action
     onContextMenu = (e: React.MouseEvent): void => {
         e.stopPropagation();
-        e.preventDefault();
         if (Math.abs(this._downX - e.clientX) > 3 || Math.abs(this._downY - e.clientY) > 3 ||
             e.isDefaultPrevented()) {
+            e.preventDefault();
             return;
         }
+        e.preventDefault();
 
         !this.isMinimized() && ContextMenu.Instance.addItem({ description: "Minimize", event: this.minimize });
         ContextMenu.Instance.addItem({ description: "Full Screen", event: this.fullScreenClicked });
diff --git a/src/client/views/nodes/FormattedTextBox.scss b/src/client/views/nodes/FormattedTextBox.scss
index 3978c3d38..5eb2bf7ce 100644
--- a/src/client/views/nodes/FormattedTextBox.scss
+++ b/src/client/views/nodes/FormattedTextBox.scss
@@ -10,7 +10,7 @@
   outline: none !important;
 }
 
-.formattedTextBox-cont {
+.formattedTextBox-cont-scroll, .formattedTextBox-cont-hidden {
   background: $light-color-secondary;
   padding: 0.9em;
   border-width: 0px;
@@ -24,6 +24,10 @@
   height: 100%;
   pointer-events: all;
 }
+.formattedTextBox-cont-hidden {
+    overflow: hidden;
+    pointer-events: none;
+}
 
 .menuicon {
   display: inline-block;
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index c380ef650..bff8ca7a4 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -1,27 +1,24 @@
-import { action, IReactionDisposer, reaction, trace, computed } from "mobx";
+import { action, IReactionDisposer, reaction } from "mobx";
 import { baseKeymap } from "prosemirror-commands";
-import { history, redo, undo } from "prosemirror-history";
+import { history } from "prosemirror-history";
 import { keymap } from "prosemirror-keymap";
 import { EditorState, Plugin, Transaction } from "prosemirror-state";
 import { EditorView } from "prosemirror-view";
 import { FieldWaiting, Opt } from "../../../fields/Field";
+import { KeyStore } from "../../../fields/KeyStore";
 import { RichTextField } from "../../../fields/RichTextField";
+import { TextField } from "../../../fields/TextField";
+import { Document } from "../../../fields/Document";
+import buildKeymap from "../../util/ProsemirrorKeymap";
 import { inpRules } from "../../util/RichTextRules";
-import { Schema } from "prosemirror-model";
 import { schema } from "../../util/RichTextSchema";
+import { TooltipLinkingMenu } from "../../util/TooltipLinkingMenu";
 import { TooltipTextMenu } from "../../util/TooltipTextMenu";
 import { ContextMenu } from "../../views/ContextMenu";
-import { Main } from "../Main";
+import { MainOverlayTextBox } from "../MainOverlayTextBox";
 import { FieldView, FieldViewProps } from "./FieldView";
 import "./FormattedTextBox.scss";
 import React = require("react");
-import { undoItem } from "prosemirror-menu";
-import buildKeymap from "../../util/ProsemirrorKeymap";
-import { TextField } from "../../../fields/TextField";
-import { KeyStore } from "../../../fields/KeyStore";
-import { TooltipLinkingMenu } from "../../util/TooltipLinkingMenu";
-import { MainOverlayTextBox } from "../MainOverlayTextBox";
-import { observer } from "mobx-react";
 const { buildMenuItems } = require("prosemirror-example-setup");
 const { menuBar } = require("prosemirror-menu");
 
@@ -52,6 +49,7 @@ export class FormattedTextBox extends React.Component<(FieldViewProps & Formatte
     }
     private _ref: React.RefObject<HTMLDivElement>;
     private _editorView: Opt<EditorView>;
+    private _gotDown: boolean = false;
     private _reactionDisposer: Opt<IReactionDisposer>;
     private _inputReactionDisposer: Opt<IReactionDisposer>;
     private _proxyReactionDisposer: Opt<IReactionDisposer>;
@@ -109,8 +107,7 @@ export class FormattedTextBox extends React.Component<(FieldViewProps & Formatte
                     if (this._editorView) {
                         this._editorView.destroy();
                     }
-
-                    this.setupEditor(config);
+                    this.setupEditor(config, MainOverlayTextBox.Instance.TextDoc); // bcz: not sure why, but the order of events is such that this.props.Document hasn't updated yet, so without forcing the editor to the MainOverlayTextBox, it will display the previously focused textbox
                 }
             );
         } else {
@@ -131,20 +128,18 @@ export class FormattedTextBox extends React.Component<(FieldViewProps & Formatte
                 }
             }
         );
-        this.setupEditor(config);
+        this.setupEditor(config, this.props.Document);
     }
 
-    private setupEditor(config: any) {
-        let state: EditorState;
-        let field = this.props.Document ? this.props.Document.GetT(this.props.fieldKey, RichTextField) : undefined;
-        if (field && field !== FieldWaiting && field.Data) {
-            state = EditorState.fromJSON(config, JSON.parse(field.Data));
-        } else {
-            state = EditorState.create(config);
-        }
+    shouldComponentUpdate() {
+        return false;
+    }
+
+    private setupEditor(config: any, doc?: Document) {
+        let field = doc ? doc.GetT(this.props.fieldKey, RichTextField) : undefined;
         if (this._ref.current) {
             this._editorView = new EditorView(this._ref.current, {
-                state,
+                state: field && field.Data ? EditorState.fromJSON(config, JSON.parse(field.Data)) : EditorState.create(config),
                 dispatchTransaction: this.dispatchTransaction
             });
         }
@@ -170,10 +165,6 @@ export class FormattedTextBox extends React.Component<(FieldViewProps & Formatte
         }
     }
 
-    shouldComponentUpdate() {
-        return false;
-    }
-
     @action
     onChange(e: React.ChangeEvent<HTMLInputElement>) {
         const { fieldKey, Document } = this.props;
@@ -186,6 +177,7 @@ export class FormattedTextBox extends React.Component<(FieldViewProps & Formatte
             e.stopPropagation();
         }
         if (e.button === 2) {
+            this._gotDown = true;
             console.log("second");
             e.preventDefault();
         }
@@ -211,6 +203,10 @@ export class FormattedTextBox extends React.Component<(FieldViewProps & Formatte
     textCapability = (e: React.MouseEvent): void => { };
 
     specificContextMenu = (e: React.MouseEvent): void => {
+        if (!this._gotDown) {
+            e.preventDefault();
+            return;
+        }
         ContextMenu.Instance.addItem({
             description: "Text Capability",
             event: this.textCapability
@@ -262,10 +258,9 @@ export class FormattedTextBox extends React.Component<(FieldViewProps & Formatte
         // (e.nativeEvent as any).DASHFormattedTextBoxHandled = true;
     }
     render() {
+        let style = this.props.isSelected() || this.props.isOverlay ? "scroll" : "hidden";
         return (
-            <div
-                style={{ overflowY: this.props.isSelected() || this.props.isOverlay ? "scroll" : "hidden" }}
-                className={`formattedTextBox-cont`}
+            <div className={`formattedTextBox-cont-${style}`}
                 onKeyDown={this.onKeyPress}
                 onKeyPress={this.onKeyPress}
                 onFocus={this.onFocused}
diff --git a/src/server/public/files/upload_1b4818f39ea324b5a687bb1ade3dca6c.jpg b/src/server/public/files/upload_1b4818f39ea324b5a687bb1ade3dca6c.jpg
deleted file mode 100644
index aeb10c4b0..000000000
Binary files a/src/server/public/files/upload_1b4818f39ea324b5a687bb1ade3dca6c.jpg and /dev/null differ
diff --git a/src/server/public/files/upload_1f1c6cfef33e5992fa860802e8c466a7.jpg b/src/server/public/files/upload_1f1c6cfef33e5992fa860802e8c466a7.jpg
deleted file mode 100644
index a2c1d8a46..000000000
Binary files a/src/server/public/files/upload_1f1c6cfef33e5992fa860802e8c466a7.jpg and /dev/null differ
diff --git a/src/server/public/files/upload_2045f363aa9cf281407703ca242aad1a.jpg b/src/server/public/files/upload_2045f363aa9cf281407703ca242aad1a.jpg
deleted file mode 100644
index c19b31a38..000000000
Binary files a/src/server/public/files/upload_2045f363aa9cf281407703ca242aad1a.jpg and /dev/null differ
diff --git a/src/server/public/files/upload_25bffd90c080c27f5ac822984406b958.jpg b/src/server/public/files/upload_25bffd90c080c27f5ac822984406b958.jpg
deleted file mode 100644
index 3614b42eb..000000000
Binary files a/src/server/public/files/upload_25bffd90c080c27f5ac822984406b958.jpg and /dev/null differ
diff --git a/src/server/public/files/upload_261f11dc39ad568212b5c7e39d1e6d13.jpg b/src/server/public/files/upload_261f11dc39ad568212b5c7e39d1e6d13.jpg
deleted file mode 100644
index ecd12d9cb..000000000
Binary files a/src/server/public/files/upload_261f11dc39ad568212b5c7e39d1e6d13.jpg and /dev/null differ
diff --git a/src/server/public/files/upload_26bcc62639141ba64e603daebb5bf5d3.png b/src/server/public/files/upload_26bcc62639141ba64e603daebb5bf5d3.png
deleted file mode 100644
index e2297cb3c..000000000
Binary files a/src/server/public/files/upload_26bcc62639141ba64e603daebb5bf5d3.png and /dev/null differ
diff --git a/src/server/public/files/upload_2d77d0773612e4723b78118ac50a2929.jpg b/src/server/public/files/upload_2d77d0773612e4723b78118ac50a2929.jpg
deleted file mode 100644
index 261a0ceff..000000000
Binary files a/src/server/public/files/upload_2d77d0773612e4723b78118ac50a2929.jpg and /dev/null differ
diff --git a/src/server/public/files/upload_2de9ad4dc687c53760c39f724c9a08a5.jpg b/src/server/public/files/upload_2de9ad4dc687c53760c39f724c9a08a5.jpg
deleted file mode 100644
index 6b6ec3c3f..000000000
Binary files a/src/server/public/files/upload_2de9ad4dc687c53760c39f724c9a08a5.jpg and /dev/null differ
diff --git a/src/server/public/files/upload_4abb568aa7cce9d291532c3d0da97102.jpg b/src/server/public/files/upload_4abb568aa7cce9d291532c3d0da97102.jpg
deleted file mode 100644
index f6332670c..000000000
Binary files a/src/server/public/files/upload_4abb568aa7cce9d291532c3d0da97102.jpg and /dev/null differ
diff --git a/src/server/public/files/upload_54c34aaca5a7bf510cebad461ec39512.png b/src/server/public/files/upload_54c34aaca5a7bf510cebad461ec39512.png
deleted file mode 100644
index e2297cb3c..000000000
Binary files a/src/server/public/files/upload_54c34aaca5a7bf510cebad461ec39512.png and /dev/null differ
diff --git a/src/server/public/files/upload_562b1e527300df8b350eeab094b3e1f1.jpg b/src/server/public/files/upload_562b1e527300df8b350eeab094b3e1f1.jpg
deleted file mode 100644
index db40705dd..000000000
Binary files a/src/server/public/files/upload_562b1e527300df8b350eeab094b3e1f1.jpg and /dev/null differ
diff --git a/src/server/public/files/upload_6a26d3f7008a8c79ee5fc8054ba69996.jpg b/src/server/public/files/upload_6a26d3f7008a8c79ee5fc8054ba69996.jpg
deleted file mode 100644
index f0417a752..000000000
Binary files a/src/server/public/files/upload_6a26d3f7008a8c79ee5fc8054ba69996.jpg and /dev/null differ
diff --git a/src/server/public/files/upload_70fa5e0c3f393504349d5865e28f4cac.jpg b/src/server/public/files/upload_70fa5e0c3f393504349d5865e28f4cac.jpg
deleted file mode 100644
index 395f8ec21..000000000
Binary files a/src/server/public/files/upload_70fa5e0c3f393504349d5865e28f4cac.jpg and /dev/null differ
diff --git a/src/server/public/files/upload_8155b5b0f57da107bb07083c04e78943.jpg b/src/server/public/files/upload_8155b5b0f57da107bb07083c04e78943.jpg
deleted file mode 100644
index 53d9315a9..000000000
Binary files a/src/server/public/files/upload_8155b5b0f57da107bb07083c04e78943.jpg and /dev/null differ
diff --git a/src/server/public/files/upload_88f588574e0efc415186af935114af9a.jpg b/src/server/public/files/upload_88f588574e0efc415186af935114af9a.jpg
deleted file mode 100644
index b72dbc482..000000000
Binary files a/src/server/public/files/upload_88f588574e0efc415186af935114af9a.jpg and /dev/null differ
diff --git a/src/server/public/files/upload_8d1c253f93f77c69c0c04ae3efb7d714.png b/src/server/public/files/upload_8d1c253f93f77c69c0c04ae3efb7d714.png
deleted file mode 100644
index e2297cb3c..000000000
Binary files a/src/server/public/files/upload_8d1c253f93f77c69c0c04ae3efb7d714.png and /dev/null differ
diff --git a/src/server/public/files/upload_9ef80158609f5ff739087aecad367b9d.jpg b/src/server/public/files/upload_9ef80158609f5ff739087aecad367b9d.jpg
deleted file mode 100644
index 84423538c..000000000
Binary files a/src/server/public/files/upload_9ef80158609f5ff739087aecad367b9d.jpg and /dev/null differ
diff --git a/src/server/public/files/upload_c39a7e0d7e8d35bb18461a5a0aa063bf.jpg b/src/server/public/files/upload_c39a7e0d7e8d35bb18461a5a0aa063bf.jpg
deleted file mode 100644
index dc7ec2f33..000000000
Binary files a/src/server/public/files/upload_c39a7e0d7e8d35bb18461a5a0aa063bf.jpg and /dev/null differ
diff --git a/src/server/public/files/upload_c6b81ab4eb70465a7e9b45d5c8f3ecaa.jpg b/src/server/public/files/upload_c6b81ab4eb70465a7e9b45d5c8f3ecaa.jpg
deleted file mode 100644
index 4422124a1..000000000
Binary files a/src/server/public/files/upload_c6b81ab4eb70465a7e9b45d5c8f3ecaa.jpg and /dev/null differ
diff --git a/src/server/public/files/upload_c99ec7a8a2df0b2f90479fde7d70c2eb.jpg b/src/server/public/files/upload_c99ec7a8a2df0b2f90479fde7d70c2eb.jpg
deleted file mode 100644
index 3747ca985..000000000
Binary files a/src/server/public/files/upload_c99ec7a8a2df0b2f90479fde7d70c2eb.jpg and /dev/null differ
diff --git a/src/server/public/files/upload_cec1cfcc67cfe5889de4098a49fec45e.jpg b/src/server/public/files/upload_cec1cfcc67cfe5889de4098a49fec45e.jpg
deleted file mode 100644
index 95053d772..000000000
Binary files a/src/server/public/files/upload_cec1cfcc67cfe5889de4098a49fec45e.jpg and /dev/null differ
diff --git a/src/server/public/files/upload_f27688fe92dc7de398e957e5d96e1a22.jpg b/src/server/public/files/upload_f27688fe92dc7de398e957e5d96e1a22.jpg
deleted file mode 100644
index 9841bad3f..000000000
Binary files a/src/server/public/files/upload_f27688fe92dc7de398e957e5d96e1a22.jpg and /dev/null differ
-- 
cgit v1.2.3-70-g09d2


From 0bd545674070fe90836912c7171642cf09e82696 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Thu, 18 Apr 2019 11:21:58 -0400
Subject: reenabled zoon fading.  added Esc.

---
 src/client/util/DragManager.ts                       |  6 ++++--
 src/client/util/SelectionManager.ts                  |  1 +
 src/client/views/Main.tsx                            | 17 ++++++++++++-----
 src/client/views/collections/CollectionBaseView.tsx  |  2 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx    | 11 +++++------
 .../views/nodes/CollectionFreeFormDocumentView.tsx   | 20 ++++++++++----------
 src/client/views/nodes/FormattedTextBox.tsx          |  4 ++++
 7 files changed, 37 insertions(+), 24 deletions(-)

(limited to 'src/client/views/nodes/FormattedTextBox.tsx')

diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 4bd654e15..64ea4342e 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -269,20 +269,22 @@ export namespace DragManager {
             );
         };
 
-        const abortDrag = () => {
+        AbortDrag = () => {
             document.removeEventListener("pointermove", moveHandler, true);
             document.removeEventListener("pointerup", upHandler);
             dragElements.map(dragElement => dragDiv.removeChild(dragElement));
             eles.map(ele => (ele.hidden = false));
         };
         const upHandler = (e: PointerEvent) => {
-            abortDrag();
+            AbortDrag();
             FinishDrag(eles, e, dragData, options, finishDrag);
         };
         document.addEventListener("pointermove", moveHandler, true);
         document.addEventListener("pointerup", upHandler);
     }
 
+    export let AbortDrag: () => void = emptyFunction;
+
     function FinishDrag(dragEles: HTMLElement[], e: PointerEvent, dragData: { [index: string]: any }, options?: DragOptions, finishDrag?: (dragData: { [index: string]: any }) => void) {
         let removed = dragEles.map(dragEle => {
             let parent = dragEle.parentElement;
diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index b15a93d9f..20319dff9 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -3,6 +3,7 @@ import { DocumentView } from "../views/nodes/DocumentView";
 import { Document } from "../../fields/Document";
 import { Main } from "../views/Main";
 import { MainOverlayTextBox } from "../views/MainOverlayTextBox";
+import { DragManager } from "./DragManager";
 
 export namespace SelectionManager {
     class Manager {
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index 5cae4fdaf..503a11b35 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -27,7 +27,7 @@ import '../northstar/model/ModelExtensions';
 import { HistogramOperation } from '../northstar/operations/HistogramOperation';
 import '../northstar/utils/Extensions';
 import { Server } from '../Server';
-import { SetupDrag } from '../util/DragManager';
+import { SetupDrag, DragManager } from '../util/DragManager';
 import { Transform } from '../util/Transform';
 import { UndoManager } from '../util/UndoManager';
 import { CollectionDockingView } from './collections/CollectionDockingView';
@@ -38,6 +38,7 @@ import "./Main.scss";
 import { MainOverlayTextBox } from './MainOverlayTextBox';
 import { DocumentView } from './nodes/DocumentView';
 import { PreviewCursor } from './PreviewCursor';
+import { SelectionManager } from '../util/SelectionManager';
 
 
 @observer
@@ -84,11 +85,11 @@ export class Main extends React.Component {
         this.initEventListeners();
         this.initAuthenticationRouters();
 
-        try {
-            this.initializeNorthstar();
-        } catch (e) {
+        // try {
+        //     this.initializeNorthstar();
+        // } catch (e) {
 
-        }
+        // }
     }
 
     componentDidMount() { window.onpopstate = this.onHistory; }
@@ -111,6 +112,12 @@ export class Main extends React.Component {
         // window.addEventListener("pointermove", (e) => this.reportLocation(e))
         window.addEventListener("drop", (e) => e.preventDefault(), false); // drop event handler
         window.addEventListener("dragover", (e) => e.preventDefault(), false); // drag event handler
+        window.addEventListener("keydown", (e) => {
+            if (e.key == "Escape") {
+                DragManager.AbortDrag();
+                SelectionManager.DeselectAll()
+            }
+        }, false); // drag event handler
         // click interactions for the context menu
         document.addEventListener("pointerdown", action(function (e: PointerEvent) {
             if (!ContextMenu.Instance.intersects(e.pageX, e.pageY)) {
diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx
index a8b061b04..4755b2d57 100644
--- a/src/client/views/collections/CollectionBaseView.tsx
+++ b/src/client/views/collections/CollectionBaseView.tsx
@@ -90,7 +90,7 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
         let props = this.props;
         var curPage = props.Document.GetNumber(KeyStore.CurPage, -1);
         doc.SetOnPrototype(KeyStore.Page, new NumberField(curPage));
-        if (this.isAnnotationOverlay) {
+        if (true || this.isAnnotationOverlay) {
             doc.SetNumber(KeyStore.Zoom, this.props.Document.GetNumber(KeyStore.Scale, 1));
         }
         if (curPage >= 0) {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 228fc937e..159db6279 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1,6 +1,5 @@
 import { action, computed, observable, trace } from "mobx";
 import { observer } from "mobx-react";
-import Measure from "react-measure";
 import { Document } from "../../../../fields/Document";
 import { KeyStore } from "../../../../fields/KeyStore";
 import { emptyFunction, returnFalse, returnOne } from "../../../../Utils";
@@ -49,9 +48,7 @@ export class CollectionFreeFormView extends CollectionSubView {
         this.addDocument(newBox, false);
     }
     private addDocument = (newBox: Document, allowDuplicates: boolean) => {
-        if (this.isAnnotationOverlay) {
-            newBox.SetNumber(KeyStore.Zoom, this.props.Document.GetNumber(KeyStore.Scale, 1));
-        }
+        newBox.SetNumber(KeyStore.Zoom, this.props.Document.GetNumber(KeyStore.Scale, 1));
         return this.props.addDocument(this.bringToFront(newBox), false);
     }
     private selectDocuments = (docs: Document[]) => {
@@ -187,14 +184,16 @@ export class CollectionFreeFormView extends CollectionSubView {
             // if (modes[e.deltaMode] === 'pixels') coefficient = 50;
             // else if (modes[e.deltaMode] === 'lines') coefficient = 1000; // This should correspond to line-height??
             let deltaScale = (1 - (e.deltaY / coefficient));
+            if (deltaScale < 0) deltaScale = -deltaScale;
             if (deltaScale * this.zoomScaling() < 1 && this.isAnnotationOverlay) {
                 deltaScale = 1 / this.zoomScaling();
             }
             let [x, y] = this.getTransform().transformPoint(e.clientX, e.clientY);
             let localTransform = this.getLocalTransform().inverse().scaleAbout(deltaScale, x, y);
 
-            this.props.Document.SetNumber(KeyStore.Scale, localTransform.Scale);
-            this.setPan(-localTransform.TranslateX / localTransform.Scale, -localTransform.TranslateY / localTransform.Scale);
+            let safeScale = Math.abs(localTransform.Scale);
+            this.props.Document.SetNumber(KeyStore.Scale, Math.abs(safeScale));
+            this.setPan(-localTransform.TranslateX / safeScale, -localTransform.TranslateY / safeScale);
             e.stopPropagation();
         }
     }
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 8cf7a0dd2..97d53a47e 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -66,22 +66,22 @@ export class CollectionFreeFormDocumentView extends React.Component<CollectionFr
 
     render() {
         let zoomFade = 1;
-        // //var zoom = doc.GetNumber(KeyStore.Zoom, 1);
-        // let transform = (this.props.ScreenToLocalTransform().scale(this.props.ContentScaling())).inverse();
-        // var [sptX, sptY] = transform.transformPoint(0, 0);
-        // let [bptX, bptY] = transform.transformPoint(this.props.PanelWidth(), this.props.PanelHeight());
-        // let w = bptX - sptX;
-        // //zoomFade = area < 100 || area > 800 ? Math.max(0, Math.min(1, 2 - 5 * (zoom < this.scale ? this.scale / zoom : zoom / this.scale))) : 1;
-        // let fadeUp = .75 * 1800;
-        // let fadeDown = .075 * 1800;
-        // zoomFade = w < fadeDown || w > fadeUp ? Math.max(0, Math.min(1, 2 - (w < fadeDown ? fadeDown / w : w / fadeUp))) : 1;
+        //var zoom = doc.GetNumber(KeyStore.Zoom, 1);
+        let transform = this.getTransform().scale(this.contentScaling()).inverse();
+        var [sptX, sptY] = transform.transformPoint(0, 0);
+        let [bptX, bptY] = transform.transformPoint(this.props.PanelWidth(), this.props.PanelHeight());
+        let w = bptX - sptX;
+        //zoomFade = area < 100 || area > 800 ? Math.max(0, Math.min(1, 2 - 5 * (zoom < this.scale ? this.scale / zoom : zoom / this.scale))) : 1;
+        let fadeUp = .75 * 1800;
+        let fadeDown = .075 * 1800;
+        zoomFade = w < fadeDown  /* || w > fadeUp */ ? Math.max(0, Math.min(1, 2 - (w < fadeDown ? fadeDown / w : w / fadeUp))) : 1;
 
         return (
             <div className="collectionFreeFormDocumentView-container" ref={this._mainCont} style={{
                 opacity: zoomFade,
                 transformOrigin: "left top",
                 transform: this.transform,
-                pointerEvents: "all",
+                pointerEvents: (zoomFade < 0.09 ? "none" : "all"),
                 width: this.width,
                 height: this.height,
                 position: "absolute",
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index bff8ca7a4..639dae30a 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -19,6 +19,7 @@ import { MainOverlayTextBox } from "../MainOverlayTextBox";
 import { FieldView, FieldViewProps } from "./FieldView";
 import "./FormattedTextBox.scss";
 import React = require("react");
+import { SelectionManager } from "../../util/SelectionManager";
 const { buildMenuItems } = require("prosemirror-example-setup");
 const { menuBar } = require("prosemirror-menu");
 
@@ -251,6 +252,9 @@ export class FormattedTextBox extends React.Component<(FieldViewProps & Formatte
     }
 
     onKeyPress(e: React.KeyboardEvent) {
+        if (e.key == "Escape") {
+            SelectionManager.DeselectAll();
+        }
         e.stopPropagation();
         if (e.keyCode === 9) e.preventDefault();
         // stop propagation doesn't seem to stop propagation of native keyboard events.
-- 
cgit v1.2.3-70-g09d2


From e47656cdc18aa1fd801a3853fa0f819140a68646 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Sat, 20 Apr 2019 14:26:10 -0400
Subject: fixed issues with FormattedTextBox updates.  Changed brushes to
 operate only on Histograms for efficiency.

---
 src/client/views/MainOverlayTextBox.scss           |  1 +
 src/client/views/MainOverlayTextBox.tsx            |  9 +++++---
 .../CollectionFreeFormLinksView.tsx                |  2 +-
 src/client/views/nodes/FormattedTextBox.tsx        | 25 ++++++++--------------
 4 files changed, 17 insertions(+), 20 deletions(-)

(limited to 'src/client/views/nodes/FormattedTextBox.tsx')

diff --git a/src/client/views/MainOverlayTextBox.scss b/src/client/views/MainOverlayTextBox.scss
index 697d68c8c..f6a746e63 100644
--- a/src/client/views/MainOverlayTextBox.scss
+++ b/src/client/views/MainOverlayTextBox.scss
@@ -7,6 +7,7 @@
     overflow: visible;
     top: 0;
     left: 0;
+    pointer-events: none;   
     z-index: $mainTextInput-zindex;
     .formattedTextBox-cont {
         background-color: rgba(248, 6, 6, 0.001);
diff --git a/src/client/views/MainOverlayTextBox.tsx b/src/client/views/MainOverlayTextBox.tsx
index 8cb01117c..a6c7eabf7 100644
--- a/src/client/views/MainOverlayTextBox.tsx
+++ b/src/client/views/MainOverlayTextBox.tsx
@@ -89,6 +89,8 @@ export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps>
         document.removeEventListener('pointerup', this.textBoxUp);
     }
 
+    textXf = () => this._textXf;
+
     render() {
         if (this.TextDoc) {
             let x: number = this._textRect.x;
@@ -98,10 +100,11 @@ export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps>
             let t = this._textXf.transformPoint(0, 0);
             let s = this._textXf.transformPoint(1, 0);
             s[0] = Math.sqrt((s[0] - t[0]) * (s[0] - t[0]) + (s[1] - t[1]) * (s[1] - t[1]));
-            return <div className="mainOverlayTextBox-textInput" style={{ pointerEvents: "none", transform: `translate(${x}px, ${y}px) scale(${1 / s[0]},${1 / s[0]})`, width: "auto", height: "auto" }} >
-                <div className="mainOverlayTextBox-textInput" onPointerDown={this.textBoxDown} ref={this._textProxyDiv} onScroll={this.textScroll} style={{ pointerEvents: "none", transform: `scale(${1}, ${1})`, width: `${w * s[0]}px`, height: `${h * s[0]}px` }}>
+            return <div className="mainOverlayTextBox-textInput" style={{ transform: `translate(${x}px, ${y}px) scale(${1 / s[0]},${1 / s[0]})`, width: "auto", height: "auto" }} >
+                <div className="mainOverlayTextBox-textInput" onPointerDown={this.textBoxDown} ref={this._textProxyDiv} onScroll={this.textScroll} style={{ transform: `scale(${1}, ${1})`, width: `${w * s[0]}px`, height: `${h * s[0]}px` }}>
                     <FormattedTextBox fieldKey={this._textFieldKey} isOverlay={true} Document={this.TextDoc} isSelected={returnTrue} select={emptyFunction} isTopMost={true}
-                        selectOnLoad={true} ContainingCollectionView={undefined} whenActiveChanged={emptyFunction} active={returnTrue} ScreenToLocalTransform={() => this._textXf} focus={emptyDocFunction} />
+                        selectOnLoad={true} ContainingCollectionView={undefined} whenActiveChanged={emptyFunction} active={returnTrue}
+                        ScreenToLocalTransform={this.textXf} focus={emptyDocFunction} />
                 </div>
             </ div>;
         }
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
index cd74d3a84..ebdb0c75c 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
@@ -18,7 +18,7 @@ export class CollectionFreeFormLinksView extends React.Component<CollectionViewP
     componentDidMount() {
         this._brushReactionDisposer = reaction(() => this.props.Document.GetList(this.props.fieldKey, [] as Document[]).map(doc => doc.GetNumber(KeyStore.X, 0)),
             () => {
-                let views = this.props.Document.GetList(this.props.fieldKey, [] as Document[]);
+                let views = this.props.Document.GetList(this.props.fieldKey, [] as Document[]).filter(doc => doc.GetText(KeyStore.BackgroundLayout, "").indexOf("istogram") !== -1);
                 for (let i = 0; i < views.length; i++) {
                     for (let j = 0; j < views.length; j++) {
                         let srcDoc = views[j];
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 639dae30a..ae05c2dad 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -1,4 +1,4 @@
-import { action, IReactionDisposer, reaction } from "mobx";
+import { action, IReactionDisposer, reaction, trace, computed } from "mobx";
 import { baseKeymap } from "prosemirror-commands";
 import { history } from "prosemirror-history";
 import { keymap } from "prosemirror-keymap";
@@ -20,6 +20,7 @@ import { FieldView, FieldViewProps } from "./FieldView";
 import "./FormattedTextBox.scss";
 import React = require("react");
 import { SelectionManager } from "../../util/SelectionManager";
+import { observer } from "mobx-react";
 const { buildMenuItems } = require("prosemirror-example-setup");
 const { menuBar } = require("prosemirror-menu");
 
@@ -44,6 +45,7 @@ export interface FormattedTextBoxOverlay {
     isOverlay?: boolean;
 }
 
+@observer
 export class FormattedTextBox extends React.Component<(FieldViewProps & FormattedTextBoxOverlay)> {
     public static LayoutString(fieldStr: string = "DataKey") {
         return FieldView.LayoutString(FormattedTextBox, fieldStr);
@@ -108,7 +110,7 @@ export class FormattedTextBox extends React.Component<(FieldViewProps & Formatte
                     if (this._editorView) {
                         this._editorView.destroy();
                     }
-                    this.setupEditor(config, MainOverlayTextBox.Instance.TextDoc); // bcz: not sure why, but the order of events is such that this.props.Document hasn't updated yet, so without forcing the editor to the MainOverlayTextBox, it will display the previously focused textbox
+                    this.setupEditor(config, this.props.Document);// MainOverlayTextBox.Instance.TextDoc); // bcz: not sure why, but the order of events is such that this.props.Document hasn't updated yet, so without forcing the editor to the MainOverlayTextBox, it will display the previously focused textbox
                 }
             );
         } else {
@@ -116,26 +118,18 @@ export class FormattedTextBox extends React.Component<(FieldViewProps & Formatte
                 () => this.props.isSelected() && MainOverlayTextBox.Instance.SetTextDoc(this.props.Document, this.props.fieldKey, this._ref.current!, this.props.ScreenToLocalTransform()));
         }
 
+
         this._reactionDisposer = reaction(
             () => {
                 const field = this.props.Document ? this.props.Document.GetT(this.props.fieldKey, RichTextField) : undefined;
                 return field && field !== FieldWaiting ? field.Data : undefined;
             },
-            field => {
-                if (field && this._editorView && !this._applyingChange) {
-                    this._editorView.updateState(
-                        EditorState.fromJSON(config, JSON.parse(field))
-                    );
-                }
-            }
+            field => field && this._editorView && !this._applyingChange &&
+                this._editorView.updateState(EditorState.fromJSON(config, JSON.parse(field)))
         );
         this.setupEditor(config, this.props.Document);
     }
 
-    shouldComponentUpdate() {
-        return false;
-    }
-
     private setupEditor(config: any, doc?: Document) {
         let field = doc ? doc.GetT(this.props.fieldKey, RichTextField) : undefined;
         if (this._ref.current) {
@@ -262,7 +256,7 @@ export class FormattedTextBox extends React.Component<(FieldViewProps & Formatte
         // (e.nativeEvent as any).DASHFormattedTextBoxHandled = true;
     }
     render() {
-        let style = this.props.isSelected() || this.props.isOverlay ? "scroll" : "hidden";
+        let style = this.props.isOverlay ? "scroll" : "hidden";
         return (
             <div className={`formattedTextBox-cont-${style}`}
                 onKeyDown={this.onKeyPress}
@@ -273,8 +267,7 @@ export class FormattedTextBox extends React.Component<(FieldViewProps & Formatte
                 onContextMenu={this.specificContextMenu}
                 // tfs: do we need this event handler
                 onWheel={this.onPointerWheel}
-                ref={this._ref}
-            />
+                ref={this._ref} />
         );
     }
 }
-- 
cgit v1.2.3-70-g09d2

