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/views/nodes/FormattedTextBox.tsx | 4 ++--
 1 file changed, 2 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 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());
         }
     }
 
-- 
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

