From 18cc66aaa90c5054268eac9520a963867c0c2f8e Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Thu, 29 Aug 2019 01:14:18 -0400
Subject: fixed expand/collapse of text.  added user tooltip

---
 src/client/views/nodes/FormattedTextBoxComment.tsx | 78 ++++++++++++++++++++++
 1 file changed, 78 insertions(+)
 create mode 100644 src/client/views/nodes/FormattedTextBoxComment.tsx

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

diff --git a/src/client/views/nodes/FormattedTextBoxComment.tsx b/src/client/views/nodes/FormattedTextBoxComment.tsx
new file mode 100644
index 000000000..e88c85a86
--- /dev/null
+++ b/src/client/views/nodes/FormattedTextBoxComment.tsx
@@ -0,0 +1,78 @@
+import { Plugin, EditorState, TextSelection } from "prosemirror-state"
+import './FormattedTextBoxComment.scss'
+import { DragManager } from "../../util/DragManager";
+import { ResolvedPos, Mark } from "prosemirror-model";
+import { EditorView } from "prosemirror-view";
+import { Doc } from "../../../new_fields/Doc";
+
+export let selectionSizePlugin = new Plugin({
+    view(editorView) { return new SelectionSizeTooltip(editorView); }
+})
+export function findOtherUserMark(marks: Mark[]): Mark | undefined {
+    return marks.find(m => m.attrs.userid && m.attrs.userid !== Doc.CurrentUserEmail);
+}
+export function findUserMark(marks: Mark[]): Mark | undefined {
+    return marks.find(m => m.attrs.userid);
+}
+export function findStartOfMark(rpos: ResolvedPos, view: EditorView, finder: (marks: Mark[]) => Mark | undefined) {
+    let before = 0;
+    let nbef = rpos.nodeBefore;
+    while (nbef && finder(nbef.marks)) {
+        before += nbef.nodeSize;
+        rpos = view.state.doc.resolve(rpos.pos - nbef.nodeSize);
+        rpos && (nbef = rpos.nodeBefore);
+    }
+    return before;
+}
+export function findEndOfMark(rpos: ResolvedPos, view: EditorView, finder: (marks: Mark[]) => Mark | undefined) {
+    let after = 0;
+    let naft = rpos.nodeAfter;
+    while (naft && finder(naft.marks)) {
+        after += naft.nodeSize;
+        rpos = view.state.doc.resolve(rpos.pos + naft.nodeSize);
+        rpos && (naft = rpos.nodeAfter);
+    }
+    return after;
+}
+
+export class SelectionSizeTooltip {
+    static tooltip: any;
+    constructor(view: any) {
+        if (!SelectionSizeTooltip.tooltip) {
+            SelectionSizeTooltip.tooltip = document.createElement("div");
+            SelectionSizeTooltip.tooltip.className = "FormattedTextBox-tooltip";
+            DragManager.Root().appendChild(SelectionSizeTooltip.tooltip);
+        }
+
+        this.update(view, undefined);
+    }
+
+
+    update(view: EditorView, lastState?: EditorState) {
+        let state = view.state;
+        // Don't do anything if the document/selection didn't change
+        if (lastState && lastState.doc.eq(state.doc) &&
+            lastState.selection.eq(state.selection)) return;
+
+        if (state.selection.$from) {
+            let nbef = findStartOfMark(state.selection.$from, view, findOtherUserMark);
+            let naft = findEndOfMark(state.selection.$from, view, findOtherUserMark);
+            let child = state.selection.$from.nodeBefore;
+            let mark = child && findOtherUserMark(child.marks);
+            if (mark && child && nbef && naft && mark.attrs.opened && SelectionSizeTooltip.tooltip.offsetParent) {
+                SelectionSizeTooltip.tooltip.textContent = mark.attrs.userid;
+                // These are in screen coordinates
+                let start = view.coordsAtPos(state.selection.from), end = view.coordsAtPos(state.selection.to);
+                // The box in which the tooltip is positioned, to use as base
+                let box = SelectionSizeTooltip.tooltip.offsetParent.getBoundingClientRect();
+                // Find a center-ish x position from the selection endpoints (when
+                // crossing lines, end may be more to the left)
+                let left = Math.max((start.left + end.left) / 2, start.left + 3);
+                SelectionSizeTooltip.tooltip.style.left = (left - box.left) + "px";
+                SelectionSizeTooltip.tooltip.style.bottom = (box.bottom - start.top) + "px";
+            }
+        }
+    }
+
+    destroy() { SelectionSizeTooltip.tooltip.style.display = "none" }
+}
-- 
cgit v1.2.3-70-g09d2


From 1079d90c5d6752a0a2c06a25d42c1192cb433ed3 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Thu, 29 Aug 2019 10:09:03 -0400
Subject: made text author tooltip interactive.

---
 src/client/util/RichTextSchema.tsx                 |  2 +-
 src/client/views/nodes/FormattedTextBox.tsx        | 21 ++++++-----
 src/client/views/nodes/FormattedTextBoxComment.tsx | 41 +++++++++++++++++-----
 3 files changed, 47 insertions(+), 17 deletions(-)

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

diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx
index ee0c0870a..f8da98f17 100644
--- a/src/client/util/RichTextSchema.tsx
+++ b/src/client/util/RichTextSchema.tsx
@@ -335,7 +335,7 @@ export const marks: { [index: string]: MarkSpec } = {
         attrs: {
             userid: { default: "" },
             hide_users: { default: [] },
-            opened: { default: false }
+            opened: { default: true }
         },
         group: "inline",
         inclusive: false,
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 2485760df..c23b85e83 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -714,27 +714,32 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
         const pos = view.posAtCoords({ left: e.clientX, top: e.clientY });
         const rpos = pos && view.state.doc.resolve(pos.pos);
         let noselection = view.state.selection.$from === view.state.selection.$to;
+        let set = false;
         if (pos && rpos) {
             let nbef = findStartOfMark(rpos, view, findOtherUserMark);
             let naft = findEndOfMark(rpos, view, findOtherUserMark);
-            const spos = view.state.doc.resolve(pos.pos - nbef);
-            const epos = view.state.doc.resolve(pos.pos + naft);
-            let ts = new TextSelection(spos, epos);
+            const spos = pos.pos - nbef;
+            const epos = pos.pos + naft;
             let child = rpos.nodeBefore || rpos.nodeAfter;
             let mark = child && findOtherUserMark(child.marks);
             if (mark && child && (nbef || naft) && (!mark.attrs.opened || noselection)) {
-                let opened = e.button === 2 ? false : !mark.attrs.opened;
-                SelectionSizeTooltip.tooltip.style.display = opened ? "" : "none";
-                let mid = opened ? epos : view.state.doc.resolve((spos.pos + epos.pos) / 2);
-                let nmark = view.state.schema.marks.user_mark.create({ ...mark.attrs, userid: e.button === 2 ? Doc.CurrentUserEmail : mark.attrs.userid, opened: opened });
-                view.dispatch(view.state.tr.addMark(ts.from, ts.to, nmark).setSelection(new TextSelection(mid, mid)));
+                SelectionSizeTooltip.SetState(this, mark.attrs.opened, spos, epos, mark);
+                set = true;
             }
         }
+        !set && SelectionSizeTooltip.Hide();
         if (e.buttons === 1 && this.props.isSelected() && !e.altKey) {
             e.stopPropagation();
         }
     }
 
+    setAnnotation = (start: number, end: number, mark: Mark, opened: boolean, keep: boolean = false) => {
+        let view = this._editorView!;
+        let mid = view.state.doc.resolve(Math.round((start + end) / 2));
+        let nmark = view.state.schema.marks.user_mark.create({ ...mark.attrs, userid: keep ? Doc.CurrentUserEmail : mark.attrs.userid, opened: opened });
+        view.dispatch(view.state.tr.removeMark(start, end, nmark).addMark(start, end, nmark).setSelection(new TextSelection(mid, mid)));
+    }
+
     @action
     onFocused = (e: React.FocusEvent): void => {
         document.removeEventListener("keypress", this.recordKeyHandler);
diff --git a/src/client/views/nodes/FormattedTextBoxComment.tsx b/src/client/views/nodes/FormattedTextBoxComment.tsx
index e88c85a86..31eb06427 100644
--- a/src/client/views/nodes/FormattedTextBoxComment.tsx
+++ b/src/client/views/nodes/FormattedTextBoxComment.tsx
@@ -1,6 +1,5 @@
-import { Plugin, EditorState, TextSelection } from "prosemirror-state"
+import { Plugin, EditorState } from "prosemirror-state"
 import './FormattedTextBoxComment.scss'
-import { DragManager } from "../../util/DragManager";
 import { ResolvedPos, Mark } from "prosemirror-model";
 import { EditorView } from "prosemirror-view";
 import { Doc } from "../../../new_fields/Doc";
@@ -35,18 +34,43 @@ export function findEndOfMark(rpos: ResolvedPos, view: EditorView, finder: (mark
     return after;
 }
 
+
 export class SelectionSizeTooltip {
-    static tooltip: any;
+    static tooltip: HTMLElement;
+    static start: number;
+    static end: number;
+    static mark: Mark;
+    static opened: boolean;
+    static textBox: any;
     constructor(view: any) {
         if (!SelectionSizeTooltip.tooltip) {
+            const root = document.getElementById("root");
             SelectionSizeTooltip.tooltip = document.createElement("div");
             SelectionSizeTooltip.tooltip.className = "FormattedTextBox-tooltip";
-            DragManager.Root().appendChild(SelectionSizeTooltip.tooltip);
+            SelectionSizeTooltip.tooltip.style.pointerEvents = "all";
+            SelectionSizeTooltip.tooltip.onpointerdown = (e: PointerEvent) => {
+                SelectionSizeTooltip.opened = !SelectionSizeTooltip.opened;
+                SelectionSizeTooltip.textBox.setAnnotation(
+                    SelectionSizeTooltip.start, SelectionSizeTooltip.end, SelectionSizeTooltip.mark,
+                    SelectionSizeTooltip.opened, e.button == 2);
+            };
+            root && root.appendChild(SelectionSizeTooltip.tooltip);
         }
-
         this.update(view, undefined);
     }
 
+    public static Hide() {
+        SelectionSizeTooltip.textBox = undefined;
+        SelectionSizeTooltip.tooltip && (SelectionSizeTooltip.tooltip.style.display = "none");
+    }
+    public static SetState(textBox: any, opened: boolean, start: number, end: number, mark: Mark) {
+        SelectionSizeTooltip.textBox = textBox;
+        SelectionSizeTooltip.start = start;
+        SelectionSizeTooltip.end = end;
+        SelectionSizeTooltip.mark = mark;
+        SelectionSizeTooltip.opened = opened;
+        SelectionSizeTooltip.textBox && SelectionSizeTooltip.tooltip && (SelectionSizeTooltip.tooltip.style.display = "");
+    }
 
     update(view: EditorView, lastState?: EditorState) {
         let state = view.state;
@@ -59,12 +83,13 @@ export class SelectionSizeTooltip {
             let naft = findEndOfMark(state.selection.$from, view, findOtherUserMark);
             let child = state.selection.$from.nodeBefore;
             let mark = child && findOtherUserMark(child.marks);
-            if (mark && child && nbef && naft && mark.attrs.opened && SelectionSizeTooltip.tooltip.offsetParent) {
+            if (mark && child && nbef && naft) {
                 SelectionSizeTooltip.tooltip.textContent = mark.attrs.userid;
                 // These are in screen coordinates
-                let start = view.coordsAtPos(state.selection.from), end = view.coordsAtPos(state.selection.to);
+                // let start = view.coordsAtPos(state.selection.from), end = view.coordsAtPos(state.selection.to);
+                let start = view.coordsAtPos(state.selection.from - nbef), end = view.coordsAtPos(state.selection.from - nbef);
                 // The box in which the tooltip is positioned, to use as base
-                let box = SelectionSizeTooltip.tooltip.offsetParent.getBoundingClientRect();
+                let box = (document.getElementById("main-div") as any).getBoundingClientRect();
                 // Find a center-ish x position from the selection endpoints (when
                 // crossing lines, end may be more to the left)
                 let left = Math.max((start.left + end.left) / 2, start.left + 3);
-- 
cgit v1.2.3-70-g09d2


From a46ae95ed6570342f03b7590ff70d6249e56f059 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Thu, 29 Aug 2019 10:34:08 -0400
Subject: added a timestamp for edit marks.

---
 src/client/util/RichTextSchema.tsx                 |  3 ++-
 src/client/views/nodes/FormattedTextBox.tsx        | 18 +++++++++++++++++-
 src/client/views/nodes/FormattedTextBoxComment.tsx |  2 +-
 3 files changed, 20 insertions(+), 3 deletions(-)

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

diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx
index f8da98f17..a642ee46c 100644
--- a/src/client/util/RichTextSchema.tsx
+++ b/src/client/util/RichTextSchema.tsx
@@ -335,7 +335,8 @@ export const marks: { [index: string]: MarkSpec } = {
         attrs: {
             userid: { default: "" },
             hide_users: { default: [] },
-            opened: { default: true }
+            opened: { default: true },
+            modified: { default: "when?" }
         },
         group: "inline",
         inclusive: false,
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index c23b85e83..0d4376d8d 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -38,6 +38,7 @@ import { DictationManager } from '../../util/DictationManager';
 import { ReplaceStep } from 'prosemirror-transform';
 import { DocumentType } from '../../documents/DocumentTypes';
 import { selectionSizePlugin, findStartOfMark, findUserMark, findEndOfMark, findOtherUserMark, SelectionSizeTooltip } from './FormattedTextBoxComment';
+import { date } from 'serializr';
 
 library.add(faEdit);
 library.add(faSmile, faTextHeight, faUpload);
@@ -811,8 +812,23 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
                 SelectionManager.SelectDoc(DocumentManager.Instance.getDocumentView(this.props.Document, this.props.ContainingCollectionView)!, false);
             }, 0);
         }
+        function timenow() {
+            var now = new Date();
+            let ampm = 'am';
+            let h = now.getHours();
+            let m: any = now.getMinutes();
+            let s: any = now.getSeconds();
+            if (h >= 12) {
+                if (h > 12) h -= 12;
+                ampm = 'pm';
+            }
+
+            if (m < 10) m = '0' + m;
+            if (s < 10) s = '0' + s;
+            return now.toLocaleDateString() + ' ' + h + ':' + m + ':' + s + ' ' + ampm;
+        }
         var markerss = this._editorView!.state.storedMarks || (this._editorView!.state.selection.$to.parentOffset && this._editorView!.state.selection.$from.marks());
-        let newMarks = [...(markerss ? markerss.filter(m => m.type !== schema.marks.user_mark) : []), schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail })];
+        let newMarks = [...(markerss ? markerss.filter(m => m.type !== schema.marks.user_mark) : []), schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: timenow() })];
         this._editorView!.state.storedMarks = newMarks;
 
         // stop propagation doesn't seem to stop propagation of native keyboard events.
diff --git a/src/client/views/nodes/FormattedTextBoxComment.tsx b/src/client/views/nodes/FormattedTextBoxComment.tsx
index 31eb06427..4ec0d6064 100644
--- a/src/client/views/nodes/FormattedTextBoxComment.tsx
+++ b/src/client/views/nodes/FormattedTextBoxComment.tsx
@@ -84,7 +84,7 @@ export class SelectionSizeTooltip {
             let child = state.selection.$from.nodeBefore;
             let mark = child && findOtherUserMark(child.marks);
             if (mark && child && nbef && naft) {
-                SelectionSizeTooltip.tooltip.textContent = mark.attrs.userid;
+                SelectionSizeTooltip.tooltip.textContent = mark.attrs.userid + " " + mark.attrs.modified;
                 // These are in screen coordinates
                 // let start = view.coordsAtPos(state.selection.from), end = view.coordsAtPos(state.selection.to);
                 let start = view.coordsAtPos(state.selection.from - nbef), end = view.coordsAtPos(state.selection.from - nbef);
-- 
cgit v1.2.3-70-g09d2


From 22a5999626b11cf75cafbcd421601e668438f6ad Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Thu, 29 Aug 2019 10:46:42 -0400
Subject: added keep checkbox

---
 src/client/views/nodes/FormattedTextBoxComment.tsx | 13 ++++++++++---
 1 file changed, 10 insertions(+), 3 deletions(-)

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

diff --git a/src/client/views/nodes/FormattedTextBoxComment.tsx b/src/client/views/nodes/FormattedTextBoxComment.tsx
index 4ec0d6064..9123f8aed 100644
--- a/src/client/views/nodes/FormattedTextBoxComment.tsx
+++ b/src/client/views/nodes/FormattedTextBoxComment.tsx
@@ -37,6 +37,7 @@ export function findEndOfMark(rpos: ResolvedPos, view: EditorView, finder: (mark
 
 export class SelectionSizeTooltip {
     static tooltip: HTMLElement;
+    static tooltipText: HTMLElement;
     static start: number;
     static end: number;
     static mark: Mark;
@@ -45,14 +46,20 @@ export class SelectionSizeTooltip {
     constructor(view: any) {
         if (!SelectionSizeTooltip.tooltip) {
             const root = document.getElementById("root");
+            let input = document.createElement("input");
+            input.type = "checkbox";
             SelectionSizeTooltip.tooltip = document.createElement("div");
+            SelectionSizeTooltip.tooltipText = document.createElement("div");
+            SelectionSizeTooltip.tooltip.appendChild(SelectionSizeTooltip.tooltipText);
             SelectionSizeTooltip.tooltip.className = "FormattedTextBox-tooltip";
             SelectionSizeTooltip.tooltip.style.pointerEvents = "all";
+            SelectionSizeTooltip.tooltip.appendChild(input);
             SelectionSizeTooltip.tooltip.onpointerdown = (e: PointerEvent) => {
-                SelectionSizeTooltip.opened = !SelectionSizeTooltip.opened;
+                let keep = e.target && (e.target as any).type === "checkbox";
+                SelectionSizeTooltip.opened = keep || !SelectionSizeTooltip.opened;
                 SelectionSizeTooltip.textBox.setAnnotation(
                     SelectionSizeTooltip.start, SelectionSizeTooltip.end, SelectionSizeTooltip.mark,
-                    SelectionSizeTooltip.opened, e.button == 2);
+                    SelectionSizeTooltip.opened, keep);
             };
             root && root.appendChild(SelectionSizeTooltip.tooltip);
         }
@@ -84,7 +91,7 @@ export class SelectionSizeTooltip {
             let child = state.selection.$from.nodeBefore;
             let mark = child && findOtherUserMark(child.marks);
             if (mark && child && nbef && naft) {
-                SelectionSizeTooltip.tooltip.textContent = mark.attrs.userid + " " + mark.attrs.modified;
+                SelectionSizeTooltip.tooltipText.textContent = mark.attrs.userid + " " + mark.attrs.modified;
                 // These are in screen coordinates
                 // let start = view.coordsAtPos(state.selection.from), end = view.coordsAtPos(state.selection.to);
                 let start = view.coordsAtPos(state.selection.from - nbef), end = view.coordsAtPos(state.selection.from - nbef);
-- 
cgit v1.2.3-70-g09d2


From f6c15087d30f27c15b3b6304af58c93c65536131 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Thu, 29 Aug 2019 14:50:22 -0400
Subject: changed linkFollowBox creation approach.  fixed target contexts to
 add link default.

---
 src/client/views/MainView.tsx                      |   8 +-
 src/client/views/OverlayView.tsx                   | 108 +++++++++++----------
 src/client/views/linking/LinkFollowBox.tsx         |  20 ++--
 src/client/views/linking/LinkMenuItem.tsx          |  18 ++--
 .../views/nodes/CollectionFreeFormDocumentView.tsx |   1 -
 src/client/views/nodes/FormattedTextBoxComment.tsx |   8 +-
 .../authentication/models/current_user_utils.ts    |   3 +
 7 files changed, 78 insertions(+), 88 deletions(-)

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

diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 1fc8d1397..b64986084 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -438,13 +438,7 @@ export class MainView extends React.Component {
         if (LinkFollowBox.Instance) {
             let dvs = DocumentManager.Instance.getDocumentViews(LinkFollowBox.Instance.props.Document);
             // if it already exisits, close it
-            if (dvs.length > 0 && shouldClose) LinkFollowBox.Instance.close();
-            // open it if not
-            else Doc.AddDocToList(Cast(CurrentUserUtils.UserDocument.overlays, Doc) as Doc, "data", LinkFollowBox.Instance.props.Document);
-        }
-        else {
-            let doc = Docs.Create.LinkFollowBoxDocument({ x: this.flyoutWidth, y: 20, width: 500, height: 370, title: "Link Follower" });
-            Doc.AddDocToList(Cast(CurrentUserUtils.UserDocument.overlays, Doc) as Doc, "data", doc);
+            LinkFollowBox.Instance.props.Document.isMinimized = (dvs.length > 0 && shouldClose);
         }
     }
 
diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx
index a1afe651c..54ab8696e 100644
--- a/src/client/views/OverlayView.tsx
+++ b/src/client/views/OverlayView.tsx
@@ -1,6 +1,6 @@
 import * as React from "react";
 import { observer } from "mobx-react";
-import { observable, action } from "mobx";
+import { observable, action, trace, computed } from "mobx";
 import { Utils, emptyFunction, returnOne, returnTrue, returnEmptyString, returnZero, returnFalse } from "../../Utils";
 
 import './OverlayView.scss';
@@ -140,61 +140,65 @@ export class OverlayView extends React.Component {
         return remove;
     }
 
+    @computed get overlayDocs() {
+        return CurrentUserUtils.UserDocument.overlays instanceof Doc && DocListCast(CurrentUserUtils.UserDocument.overlays.data).map(d => {
+            let offsetx = 0, offsety = 0;
+            let onPointerMove = action((e: PointerEvent) => {
+                if (e.buttons === 1) {
+                    d.x = e.clientX + offsetx;
+                    d.y = e.clientY + offsety;
+                    e.stopPropagation();
+                    e.preventDefault();
+                }
+            });
+            let onPointerUp = action((e: PointerEvent) => {
+                document.removeEventListener("pointermove", onPointerMove);
+                document.removeEventListener("pointerup", onPointerUp);
+                e.stopPropagation();
+                e.preventDefault();
+            });
+
+            let onPointerDown = (e: React.PointerEvent) => {
+                offsetx = NumCast(d.x) - e.clientX;
+                offsety = NumCast(d.y) - e.clientY;
+                e.stopPropagation();
+                e.preventDefault();
+                document.addEventListener("pointermove", onPointerMove);
+                document.addEventListener("pointerup", onPointerUp);
+            };
+            return <div className="overlayView-doc" key={d[Id]} onPointerDown={onPointerDown} style={{ transform: `translate(${d.x}px, ${d.y}px)`, display: d.isMinimized ? "none" : "" }}>
+                <DocumentContentsView
+                    Document={d}
+                    ChromeHeight={returnZero}
+                    isSelected={returnFalse}
+                    select={emptyFunction}
+                    layoutKey={"layout"}
+                    bringToFront={emptyFunction}
+                    addDocument={undefined}
+                    removeDocument={undefined}
+                    ContentScaling={returnOne}
+                    PanelWidth={returnOne}
+                    PanelHeight={returnOne}
+                    ScreenToLocalTransform={Transform.Identity}
+                    renderDepth={1}
+                    parentActive={returnTrue}
+                    whenActiveChanged={emptyFunction}
+                    focus={emptyFunction}
+                    backgroundColor={returnEmptyString}
+                    addDocTab={emptyFunction}
+                    pinToPres={emptyFunction}
+                    ContainingCollectionView={undefined}
+                    zoomToScale={emptyFunction}
+                    getScale={returnOne} />
+            </div>;
+        })
+    }
+
     render() {
         return (
             <div className="overlayView" id="overlayView">
                 {this._elements}
-                {CurrentUserUtils.UserDocument.overlays instanceof Doc && DocListCast(CurrentUserUtils.UserDocument.overlays.data).map(d => {
-                    let offsetx = 0, offsety = 0;
-                    let onPointerMove = action((e: PointerEvent) => {
-                        if (e.buttons === 1) {
-                            d.x = e.clientX + offsetx;
-                            d.y = e.clientY + offsety;
-                            e.stopPropagation();
-                            e.preventDefault();
-                        }
-                    });
-                    let onPointerUp = action((e: PointerEvent) => {
-                        document.removeEventListener("pointermove", onPointerMove);
-                        document.removeEventListener("pointerup", onPointerUp);
-                        e.stopPropagation();
-                        e.preventDefault();
-                    });
-
-                    let onPointerDown = (e: React.PointerEvent) => {
-                        offsetx = NumCast(d.x) - e.clientX;
-                        offsety = NumCast(d.y) - e.clientY;
-                        e.stopPropagation();
-                        e.preventDefault();
-                        document.addEventListener("pointermove", onPointerMove);
-                        document.addEventListener("pointerup", onPointerUp);
-                    }
-                    return <div className="overlayView-doc" key={d[Id]} onPointerDown={onPointerDown} style={{ transform: `translate(${d.x}px, ${d.y}px)` }}>
-                        <DocumentContentsView
-                            Document={d}
-                            ChromeHeight={returnZero}
-                            isSelected={returnFalse}
-                            select={emptyFunction}
-                            layoutKey={"layout"}
-                            bringToFront={emptyFunction}
-                            addDocument={undefined}
-                            removeDocument={undefined}
-                            ContentScaling={returnOne}
-                            PanelWidth={returnOne}
-                            PanelHeight={returnOne}
-                            ScreenToLocalTransform={Transform.Identity}
-                            renderDepth={1}
-                            parentActive={returnTrue}
-                            whenActiveChanged={emptyFunction}
-                            focus={emptyFunction}
-                            backgroundColor={returnEmptyString}
-                            addDocTab={emptyFunction}
-                            pinToPres={emptyFunction}
-                            ContainingCollectionView={undefined}
-                            zoomToScale={emptyFunction}
-                            getScale={returnOne} />
-                    </div>
-                })}
+                {this.overlayDocs}
             </div>
         );
     }
diff --git a/src/client/views/linking/LinkFollowBox.tsx b/src/client/views/linking/LinkFollowBox.tsx
index 07f07f72a..74663f9af 100644
--- a/src/client/views/linking/LinkFollowBox.tsx
+++ b/src/client/views/linking/LinkFollowBox.tsx
@@ -1,4 +1,4 @@
-import { observable, computed, action, trace, ObservableMap, runInAction, reaction, IReactionDisposer } from "mobx";
+import { observable, computed, action, runInAction, reaction, IReactionDisposer } from "mobx";
 import React = require("react");
 import { observer } from "mobx-react";
 import { FieldViewProps, FieldView } from "../nodes/FieldView";
@@ -15,8 +15,6 @@ import { SearchUtil } from "../../util/SearchUtil";
 import { Id } from "../../../new_fields/FieldSymbols";
 import { listSpec } from "../../../new_fields/Schema";
 import { DocServer } from "../../DocServer";
-import { RefField } from "../../../new_fields/RefField";
-import { Docs } from "../../documents/Documents";
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { faTimes } from '@fortawesome/free-solid-svg-icons';
 import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils";
@@ -126,9 +124,11 @@ export class LinkFollowBox extends React.Component<FieldViewProps> {
             const allDocs = await Promise.all(aliases.map(doc => SearchUtil.Search("", true, { fq: `data_l:"${doc[Id]}"` }).then(result => result.docs)));
             allDocs.forEach((docs, index) => docs.forEach(doc => map.set(doc, aliases[index])));
             docs.forEach(doc => map.delete(doc));
-            runInAction(() => {
+            runInAction(async () => {
                 this._docs = docs.filter(doc => !Doc.AreProtosEqual(doc, CollectionDockingView.Instance.props.Document)).map(doc => ({ col: doc, target: dest }));
                 this._otherDocs = Array.from(map.entries()).filter(entry => !Doc.AreProtosEqual(entry[0], CollectionDockingView.Instance.props.Document)).map(([col, target]) => ({ col, target }));
+                let tcontext = LinkFollowBox.linkDoc && (await Cast(LinkFollowBox.linkDoc.targetContext, Doc)) as Doc;
+                runInAction(() => tcontext && this._docs.splice(0, 0, { col: tcontext, target: dest }));
             });
         }
     }
@@ -268,7 +268,7 @@ export class LinkFollowBox extends React.Component<FieldViewProps> {
             let fullScreenAlias = Doc.MakeAlias(LinkFollowBox.destinationDoc);
             // THIS IS EMPTY FUNCTION
             this.props.addDocTab(fullScreenAlias, undefined, "inTab");
-            console.log(this.props.addDocTab)
+            console.log(this.props.addDocTab);
 
             this.highlightDoc();
             SelectionManager.DeselectAll();
@@ -348,7 +348,7 @@ export class LinkFollowBox extends React.Component<FieldViewProps> {
             }
             else if (this.selectedMode === FollowModes.OPENTAB) {
                 if (notOpenInContext) this.openLinkTab();
-                else this.selectedContext && this.openLinkColTab({ context: this.selectedContext, shouldZoom: shouldZoom })
+                else this.selectedContext && this.openLinkColTab({ context: this.selectedContext, shouldZoom: shouldZoom });
             }
             else if (this.selectedMode === FollowModes.PAN) {
                 this.jumpToLink({ shouldZoom: shouldZoom });
@@ -555,19 +555,13 @@ export class LinkFollowBox extends React.Component<FieldViewProps> {
         return null;
     }
 
-    async close() {
-        let res = await DocListCastAsync((CurrentUserUtils.UserDocument.overlays as Doc).data);
-        if (res) res.splice(res.indexOf(LinkFollowBox.Instance!.props.Document), 1);
-        LinkFollowBox.Instance = undefined;
-    }
-
     render() {
         return (
             <div className="linkFollowBox-main" style={{ height: NumCast(this.props.Document.height), width: NumCast(this.props.Document.width) }}>
                 <div className="linkFollowBox-header">
                     <div className="topHeader">
                         {LinkFollowBox.linkDoc ? "Link Title: " + StrCast(LinkFollowBox.linkDoc.title) : "No Link Selected"}
-                        <div onClick={this.close} className="closeDocument"><FontAwesomeIcon icon={faTimes} size="lg" /></div>
+                        <div onClick={() => this.props.Document.isMinimized = true} className="closeDocument"><FontAwesomeIcon icon={faTimes} size="lg" /></div>
                     </div>
                     <div className=" direction-indicator">{LinkFollowBox.linkDoc ?
                         LinkFollowBox.sourceDoc && LinkFollowBox.destinationDoc ? "Source: " + StrCast(LinkFollowBox.sourceDoc.title) + ", Destination: " + StrCast(LinkFollowBox.destinationDoc.title)
diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx
index 6c10d4fb6..780f161d3 100644
--- a/src/client/views/linking/LinkMenuItem.tsx
+++ b/src/client/views/linking/LinkMenuItem.tsx
@@ -97,24 +97,20 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
 
     @action.bound
     async followDefault() {
-        if (LinkFollowBox.Instance === undefined) {
-            let doc = await Docs.Create.LinkFollowBoxDocument({ x: MainView.Instance.flyoutWidth, y: 20, width: 500, height: 370, title: "Link Follower" });
-            await Doc.AddDocToList(Cast(CurrentUserUtils.UserDocument.overlays, Doc) as Doc, "data", doc);
-        } else {
-            LinkFollowBox.Instance!.setLinkDocs(this.props.linkDoc, this.props.sourceDoc, this.props.destinationDoc);
-            LinkFollowBox.Instance!.defaultLinkBehavior();
+        if (LinkFollowBox.Instance !== undefined) {
+            LinkFollowBox.Instance.props.Document.isMinimized = false;
+            LinkFollowBox.Instance.setLinkDocs(this.props.linkDoc, this.props.sourceDoc, this.props.destinationDoc);
+            LinkFollowBox.Instance.defaultLinkBehavior();
         }
     }
 
     @action.bound
     async openLinkFollower() {
-        if (LinkFollowBox.Instance === undefined) {
-            let doc = await Docs.Create.LinkFollowBoxDocument({ x: MainView.Instance.flyoutWidth, y: 20, width: 500, height: 370, title: "Link Follower" });
-            await Doc.AddDocToList(Cast(CurrentUserUtils.UserDocument.overlays, Doc) as Doc, "data", doc);
-        } else {
+        if (LinkFollowBox.Instance !== undefined) {
+            LinkFollowBox.Instance.props.Document.isMinimized = false;
             MainView.Instance.toggleLinkFollowBox(false);
+            LinkFollowBox.Instance.setLinkDocs(this.props.linkDoc, this.props.sourceDoc, this.props.destinationDoc);
         }
-        LinkFollowBox.Instance!.setLinkDocs(this.props.linkDoc, this.props.sourceDoc, this.props.destinationDoc);
     }
 
     render() {
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 7631ecc6c..c9c394960 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -8,7 +8,6 @@ import { DocumentView, DocumentViewProps, positionSchema } from "./DocumentView"
 import "./DocumentView.scss";
 import React = require("react");
 import { Doc } from "../../../new_fields/Doc";
-import { returnEmptyString } from "../../../Utils";
 
 export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps {
     x?: number;
diff --git a/src/client/views/nodes/FormattedTextBoxComment.tsx b/src/client/views/nodes/FormattedTextBoxComment.tsx
index 9123f8aed..0287d93a9 100644
--- a/src/client/views/nodes/FormattedTextBoxComment.tsx
+++ b/src/client/views/nodes/FormattedTextBoxComment.tsx
@@ -1,12 +1,12 @@
-import { Plugin, EditorState } from "prosemirror-state"
-import './FormattedTextBoxComment.scss'
+import { Plugin, EditorState } from "prosemirror-state";
+import './FormattedTextBoxComment.scss';
 import { ResolvedPos, Mark } from "prosemirror-model";
 import { EditorView } from "prosemirror-view";
 import { Doc } from "../../../new_fields/Doc";
 
 export let selectionSizePlugin = new Plugin({
     view(editorView) { return new SelectionSizeTooltip(editorView); }
-})
+});
 export function findOtherUserMark(marks: Mark[]): Mark | undefined {
     return marks.find(m => m.attrs.userid && m.attrs.userid !== Doc.CurrentUserEmail);
 }
@@ -106,5 +106,5 @@ export class SelectionSizeTooltip {
         }
     }
 
-    destroy() { SelectionSizeTooltip.tooltip.style.display = "none" }
+    destroy() { SelectionSizeTooltip.tooltip.style.display = "none"; }
 }
diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts
index 83e45d3ce..9866e22eb 100644
--- a/src/server/authentication/models/current_user_utils.ts
+++ b/src/server/authentication/models/current_user_utils.ts
@@ -79,6 +79,9 @@ export class CurrentUserUtils {
             Doc.GetProto(overlays).backgroundColor = "#aca3a6";
             doc.overlays = overlays;
         }
+        if (doc.linkFollowBox === undefined) {
+            PromiseValue(Cast(doc.overlays, Doc)).then(overlays => overlays && Doc.AddDocToList(overlays, "data", doc.linkFollowBox = Docs.Create.LinkFollowBoxDocument({ x: 250, y: 20, width: 500, height: 370, title: "Link Follower" })));
+        }
         StrCast(doc.title).indexOf("@") !== -1 && (doc.title = StrCast(doc.title).split("@")[0] + "'s Library");
         doc.width = 100;
         doc.preventTreeViewOpen = true;
-- 
cgit v1.2.3-70-g09d2


From dc7990e969bff201fefe050ac321b2d2d1d58059 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Sat, 31 Aug 2019 13:27:17 -0400
Subject: added text tooltip for links

---
 src/client/views/nodes/FormattedTextBox.tsx        | 19 +--------
 src/client/views/nodes/FormattedTextBoxComment.tsx | 46 +++++++++++++++++++++-
 2 files changed, 45 insertions(+), 20 deletions(-)

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

diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 0f349780e..93ac2f06f 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -713,24 +713,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
     }
 
     onPointerUp = (e: React.PointerEvent): void => {
-        let view = this._editorView!;
-        const pos = view.posAtCoords({ left: e.clientX, top: e.clientY });
-        const rpos = pos && view.state.doc.resolve(pos.pos);
-        let noselection = view.state.selection.$from === view.state.selection.$to;
-        let set = false;
-        if (pos && rpos) {
-            let nbef = findStartOfMark(rpos, view, findOtherUserMark);
-            let naft = findEndOfMark(rpos, view, findOtherUserMark);
-            const spos = pos.pos - nbef;
-            const epos = pos.pos + naft;
-            let child = rpos.nodeBefore || rpos.nodeAfter;
-            let mark = child && findOtherUserMark(child.marks);
-            if (mark && child && (nbef || naft) && (!mark.attrs.opened || noselection)) {
-                SelectionSizeTooltip.SetState(this, mark.attrs.opened, spos, epos, mark);
-                set = true;
-            }
-        }
-        !set && SelectionSizeTooltip.Hide();
+        SelectionSizeTooltip.textBox = this;
         if (e.buttons === 1 && this.props.isSelected() && !e.altKey) {
             e.stopPropagation();
         }
diff --git a/src/client/views/nodes/FormattedTextBoxComment.tsx b/src/client/views/nodes/FormattedTextBoxComment.tsx
index 0287d93a9..c368a3f34 100644
--- a/src/client/views/nodes/FormattedTextBoxComment.tsx
+++ b/src/client/views/nodes/FormattedTextBoxComment.tsx
@@ -3,6 +3,10 @@ import './FormattedTextBoxComment.scss';
 import { ResolvedPos, Mark } from "prosemirror-model";
 import { EditorView } from "prosemirror-view";
 import { Doc } from "../../../new_fields/Doc";
+import { schema } from "../../util/RichTextSchema";
+import { DocServer } from "../../DocServer";
+import { Utils } from "../../../Utils";
+import { StrCast } from "../../../new_fields/Types";
 
 export let selectionSizePlugin = new Plugin({
     view(editorView) { return new SelectionSizeTooltip(editorView); }
@@ -13,6 +17,9 @@ export function findOtherUserMark(marks: Mark[]): Mark | undefined {
 export function findUserMark(marks: Mark[]): Mark | undefined {
     return marks.find(m => m.attrs.userid);
 }
+export function findLinkMark(marks: Mark[]): Mark | undefined {
+    return marks.find(m => m.type === schema.marks.link);
+}
 export function findStartOfMark(rpos: ResolvedPos, view: EditorView, finder: (marks: Mark[]) => Mark | undefined) {
     let before = 0;
     let nbef = rpos.nodeBefore;
@@ -57,7 +64,7 @@ export class SelectionSizeTooltip {
             SelectionSizeTooltip.tooltip.onpointerdown = (e: PointerEvent) => {
                 let keep = e.target && (e.target as any).type === "checkbox";
                 SelectionSizeTooltip.opened = keep || !SelectionSizeTooltip.opened;
-                SelectionSizeTooltip.textBox.setAnnotation(
+                SelectionSizeTooltip.textBox && SelectionSizeTooltip.textBox.setAnnotation(
                     SelectionSizeTooltip.start, SelectionSizeTooltip.end, SelectionSizeTooltip.mark,
                     SelectionSizeTooltip.opened, keep);
             };
@@ -76,7 +83,7 @@ export class SelectionSizeTooltip {
         SelectionSizeTooltip.end = end;
         SelectionSizeTooltip.mark = mark;
         SelectionSizeTooltip.opened = opened;
-        SelectionSizeTooltip.textBox && SelectionSizeTooltip.tooltip && (SelectionSizeTooltip.tooltip.style.display = "");
+        SelectionSizeTooltip.tooltip && (SelectionSizeTooltip.tooltip.style.display = "");
     }
 
     update(view: EditorView, lastState?: EditorState) {
@@ -85,11 +92,18 @@ export class SelectionSizeTooltip {
         if (lastState && lastState.doc.eq(state.doc) &&
             lastState.selection.eq(state.selection)) return;
 
+        let set = "none"
         if (state.selection.$from) {
             let nbef = findStartOfMark(state.selection.$from, view, findOtherUserMark);
             let naft = findEndOfMark(state.selection.$from, view, findOtherUserMark);
+            const spos = state.selection.$from.pos - nbef;
+            const epos = state.selection.$from.pos + naft;
             let child = state.selection.$from.nodeBefore;
             let mark = child && findOtherUserMark(child.marks);
+            let noselection = view.state.selection.$from === view.state.selection.$to;
+            if (mark && child && (nbef || naft) && (!mark.attrs.opened || noselection)) {
+                SelectionSizeTooltip.SetState(this, mark.attrs.opened, spos, epos, mark);
+            }
             if (mark && child && nbef && naft) {
                 SelectionSizeTooltip.tooltipText.textContent = mark.attrs.userid + " " + mark.attrs.modified;
                 // These are in screen coordinates
@@ -102,8 +116,36 @@ export class SelectionSizeTooltip {
                 let left = Math.max((start.left + end.left) / 2, start.left + 3);
                 SelectionSizeTooltip.tooltip.style.left = (left - box.left) + "px";
                 SelectionSizeTooltip.tooltip.style.bottom = (box.bottom - start.top) + "px";
+                set = "";
+            }
+        }
+        if (set === "none" && state.selection.$from) {
+            SelectionSizeTooltip.textBox = undefined;
+            let nbef = findStartOfMark(state.selection.$from, view, findLinkMark);
+            let naft = findEndOfMark(state.selection.$from, view, findLinkMark);
+            let child = state.selection.$from.nodeBefore;
+            let mark = child && findLinkMark(child.marks);
+            if (mark && child && nbef && naft) {
+                SelectionSizeTooltip.tooltipText.textContent = "link : " + (mark.attrs.title || mark.attrs.href);
+                if (mark.attrs.href.indexOf(Utils.prepend("/doc/")) === 0) {
+                    let docTarget = mark.attrs.href.replace(Utils.prepend("/doc/"), "").split("?")[0];
+                    docTarget && DocServer.GetRefField(docTarget).then(linkDoc =>
+                        (linkDoc as Doc) && (SelectionSizeTooltip.tooltipText.textContent = "link :" + StrCast((linkDoc as Doc)!.title)));
+                }
+                // These are in screen coordinates
+                // let start = view.coordsAtPos(state.selection.from), end = view.coordsAtPos(state.selection.to);
+                let start = view.coordsAtPos(state.selection.from - nbef), end = view.coordsAtPos(state.selection.from - nbef);
+                // The box in which the tooltip is positioned, to use as base
+                let box = (document.getElementById("main-div") as any).getBoundingClientRect();
+                // Find a center-ish x position from the selection endpoints (when
+                // crossing lines, end may be more to the left)
+                let left = Math.max((start.left + end.left) / 2, start.left + 3);
+                SelectionSizeTooltip.tooltip.style.left = (left - box.left) + "px";
+                SelectionSizeTooltip.tooltip.style.bottom = (box.bottom - start.top) + "px";
+                set = "";
             }
         }
+        SelectionSizeTooltip.tooltip && (SelectionSizeTooltip.tooltip.style.display = set);
     }
 
     destroy() { SelectionSizeTooltip.tooltip.style.display = "none"; }
-- 
cgit v1.2.3-70-g09d2


From 4a1d94325d5d1b5641cd280e89c442c114074e8d Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Tue, 3 Sep 2019 08:03:43 -0400
Subject: cleanup names for formattedTextBoxComment stuff.  fixed brushing bug
 with user_mark.

---
 src/client/util/RichTextSchema.tsx                 |  2 +-
 src/client/util/TooltipTextMenu.tsx                |  6 +-
 src/client/views/nodes/FormattedTextBox.tsx        | 12 ++--
 src/client/views/nodes/FormattedTextBoxComment.tsx | 70 +++++++++++-----------
 4 files changed, 41 insertions(+), 49 deletions(-)

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

diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx
index 3161467b8..7911cf629 100644
--- a/src/client/util/RichTextSchema.tsx
+++ b/src/client/util/RichTextSchema.tsx
@@ -346,7 +346,7 @@ export const marks: { [index: string]: MarkSpec } = {
             return hidden ?
                 (node.attrs.opened ?
                     ['span', { class: "userMarkOpen" }, 0] :
-                    ['span', { class: "userMark" }, ['span', { style: "font-size:2" }, 0]]
+                    ['span', { class: "userMark" }, ['span', 0]]
                 ) :
                 ['span', 0];
         }
diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx
index 3b4b7f05a..ce7b04e31 100644
--- a/src/client/util/TooltipTextMenu.tsx
+++ b/src/client/util/TooltipTextMenu.tsx
@@ -60,8 +60,6 @@ export class TooltipTextMenu {
     @observable
     private _storedMarks: Mark<any>[] | null | undefined;
 
-    public HackToFixTextSelectionGlitch: boolean = false;
-
 
     constructor(view: EditorView, editorProps: FieldViewProps & FormattedTextBoxProps) {
         this.view = view;
@@ -629,7 +627,7 @@ export class TooltipTextMenu {
                 if (!this.view.state.selection.empty && $from && $from.nodeAfter) {
                     if (this._brushMarks && to - from > 0) {
                         this.view.dispatch(this.view.state.tr.removeMark(from, to));
-                        this._brushMarks.forEach((mark: Mark) => {
+                        Array.from(this._brushMarks).filter(m => m.type !== schema.marks.user_mark).forEach((mark: Mark) => {
                             const markType = mark.type;
                             this.changeToMarkInGroup(markType, this.view, []);
 
@@ -876,8 +874,6 @@ export class TooltipTextMenu {
                 this.updateFontSizeDropdown("Various");
             }
         }
-        !this.HackToFixTextSelectionGlitch &&
-            this.view.dispatch(this.view.state.tr.setStoredMarks(this._activeMarks)); // bcz: what's the purpose of this line?  It messes up text selection without the Hack.
 
         this.update_mark_doms();
     }
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 93ac2f06f..6232dd3ab 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -37,8 +37,7 @@ import { DocumentDecorations } from '../DocumentDecorations';
 import { DictationManager } from '../../util/DictationManager';
 import { ReplaceStep } from 'prosemirror-transform';
 import { DocumentType } from '../../documents/DocumentTypes';
-import { selectionSizePlugin, findStartOfMark, findUserMark, findEndOfMark, findOtherUserMark, SelectionSizeTooltip } from './FormattedTextBoxComment';
-import { date } from 'serializr';
+import { formattedTextBoxCommentPlugin, FormattedTextBoxComment } from './FormattedTextBoxComment';
 
 library.add(faEdit);
 library.add(faSmile, faTextHeight, faUpload);
@@ -172,9 +171,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
     dispatchTransaction = (tx: Transaction) => {
         if (this._editorView) {
             const state = this._editorView.state.apply(tx);
-            FormattedTextBox._toolTipTextMenu && (FormattedTextBox._toolTipTextMenu.HackToFixTextSelectionGlitch = true);
             this._editorView.updateState(state);
-            FormattedTextBox._toolTipTextMenu && (FormattedTextBox._toolTipTextMenu.HackToFixTextSelectionGlitch = false);
             if (state.selection.empty && FormattedTextBox._toolTipTextMenu && tx.storedMarks) {
                 FormattedTextBox._toolTipTextMenu.mark_key_pressed(tx.storedMarks);
             }
@@ -327,7 +324,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
                         attributes: { class: "ProseMirror-example-setup-style" }
                     }
                 }),
-                selectionSizePlugin
+                formattedTextBoxCommentPlugin
             ] : [
                     history(),
                     keymap(buildKeymap(schema)),
@@ -713,7 +710,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
     }
 
     onPointerUp = (e: React.PointerEvent): void => {
-        SelectionSizeTooltip.textBox = this;
+        FormattedTextBoxComment.textBox = this;
         if (e.buttons === 1 && this.props.isSelected() && !e.altKey) {
             e.stopPropagation();
         }
@@ -805,8 +802,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
             }
 
             if (m < 10) m = '0' + m;
-            if (s < 10) s = '0' + s;
-            return now.toLocaleDateString() + ' ' + h + ':' + m + ':' + s + ' ' + ampm;
+            return now.toLocaleDateString() + ' ' + h + ':' + m + ' ' + ampm;
         }
         var markerss = this._editorView!.state.storedMarks || (this._editorView!.state.selection.$to.parentOffset && this._editorView!.state.selection.$from.marks());
         let newMarks = [...(markerss ? markerss.filter(m => m.type !== schema.marks.user_mark) : []), schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: timenow() })];
diff --git a/src/client/views/nodes/FormattedTextBoxComment.tsx b/src/client/views/nodes/FormattedTextBoxComment.tsx
index c368a3f34..255000936 100644
--- a/src/client/views/nodes/FormattedTextBoxComment.tsx
+++ b/src/client/views/nodes/FormattedTextBoxComment.tsx
@@ -8,8 +8,8 @@ import { DocServer } from "../../DocServer";
 import { Utils } from "../../../Utils";
 import { StrCast } from "../../../new_fields/Types";
 
-export let selectionSizePlugin = new Plugin({
-    view(editorView) { return new SelectionSizeTooltip(editorView); }
+export let formattedTextBoxCommentPlugin = new Plugin({
+    view(editorView) { return new FormattedTextBoxComment(editorView); }
 });
 export function findOtherUserMark(marks: Mark[]): Mark | undefined {
     return marks.find(m => m.attrs.userid && m.attrs.userid !== Doc.CurrentUserEmail);
@@ -42,7 +42,7 @@ export function findEndOfMark(rpos: ResolvedPos, view: EditorView, finder: (mark
 }
 
 
-export class SelectionSizeTooltip {
+export class FormattedTextBoxComment {
     static tooltip: HTMLElement;
     static tooltipText: HTMLElement;
     static start: number;
@@ -51,39 +51,39 @@ export class SelectionSizeTooltip {
     static opened: boolean;
     static textBox: any;
     constructor(view: any) {
-        if (!SelectionSizeTooltip.tooltip) {
+        if (!FormattedTextBoxComment.tooltip) {
             const root = document.getElementById("root");
             let input = document.createElement("input");
             input.type = "checkbox";
-            SelectionSizeTooltip.tooltip = document.createElement("div");
-            SelectionSizeTooltip.tooltipText = document.createElement("div");
-            SelectionSizeTooltip.tooltip.appendChild(SelectionSizeTooltip.tooltipText);
-            SelectionSizeTooltip.tooltip.className = "FormattedTextBox-tooltip";
-            SelectionSizeTooltip.tooltip.style.pointerEvents = "all";
-            SelectionSizeTooltip.tooltip.appendChild(input);
-            SelectionSizeTooltip.tooltip.onpointerdown = (e: PointerEvent) => {
+            FormattedTextBoxComment.tooltip = document.createElement("div");
+            FormattedTextBoxComment.tooltipText = document.createElement("div");
+            FormattedTextBoxComment.tooltip.appendChild(FormattedTextBoxComment.tooltipText);
+            FormattedTextBoxComment.tooltip.className = "FormattedTextBox-tooltip";
+            FormattedTextBoxComment.tooltip.style.pointerEvents = "all";
+            FormattedTextBoxComment.tooltip.appendChild(input);
+            FormattedTextBoxComment.tooltip.onpointerdown = (e: PointerEvent) => {
                 let keep = e.target && (e.target as any).type === "checkbox";
-                SelectionSizeTooltip.opened = keep || !SelectionSizeTooltip.opened;
-                SelectionSizeTooltip.textBox && SelectionSizeTooltip.textBox.setAnnotation(
-                    SelectionSizeTooltip.start, SelectionSizeTooltip.end, SelectionSizeTooltip.mark,
-                    SelectionSizeTooltip.opened, keep);
+                FormattedTextBoxComment.opened = keep || !FormattedTextBoxComment.opened;
+                FormattedTextBoxComment.textBox && FormattedTextBoxComment.textBox.setAnnotation(
+                    FormattedTextBoxComment.start, FormattedTextBoxComment.end, FormattedTextBoxComment.mark,
+                    FormattedTextBoxComment.opened, keep);
             };
-            root && root.appendChild(SelectionSizeTooltip.tooltip);
+            root && root.appendChild(FormattedTextBoxComment.tooltip);
         }
         this.update(view, undefined);
     }
 
     public static Hide() {
-        SelectionSizeTooltip.textBox = undefined;
-        SelectionSizeTooltip.tooltip && (SelectionSizeTooltip.tooltip.style.display = "none");
+        FormattedTextBoxComment.textBox = undefined;
+        FormattedTextBoxComment.tooltip && (FormattedTextBoxComment.tooltip.style.display = "none");
     }
     public static SetState(textBox: any, opened: boolean, start: number, end: number, mark: Mark) {
-        SelectionSizeTooltip.textBox = textBox;
-        SelectionSizeTooltip.start = start;
-        SelectionSizeTooltip.end = end;
-        SelectionSizeTooltip.mark = mark;
-        SelectionSizeTooltip.opened = opened;
-        SelectionSizeTooltip.tooltip && (SelectionSizeTooltip.tooltip.style.display = "");
+        FormattedTextBoxComment.textBox = textBox;
+        FormattedTextBoxComment.start = start;
+        FormattedTextBoxComment.end = end;
+        FormattedTextBoxComment.mark = mark;
+        FormattedTextBoxComment.opened = opened;
+        FormattedTextBoxComment.tooltip && (FormattedTextBoxComment.tooltip.style.display = "");
     }
 
     update(view: EditorView, lastState?: EditorState) {
@@ -102,10 +102,10 @@ export class SelectionSizeTooltip {
             let mark = child && findOtherUserMark(child.marks);
             let noselection = view.state.selection.$from === view.state.selection.$to;
             if (mark && child && (nbef || naft) && (!mark.attrs.opened || noselection)) {
-                SelectionSizeTooltip.SetState(this, mark.attrs.opened, spos, epos, mark);
+                FormattedTextBoxComment.SetState(this, mark.attrs.opened, spos, epos, mark);
             }
             if (mark && child && nbef && naft) {
-                SelectionSizeTooltip.tooltipText.textContent = mark.attrs.userid + " " + mark.attrs.modified;
+                FormattedTextBoxComment.tooltipText.textContent = mark.attrs.userid + " " + mark.attrs.modified;
                 // These are in screen coordinates
                 // let start = view.coordsAtPos(state.selection.from), end = view.coordsAtPos(state.selection.to);
                 let start = view.coordsAtPos(state.selection.from - nbef), end = view.coordsAtPos(state.selection.from - nbef);
@@ -114,23 +114,23 @@ export class SelectionSizeTooltip {
                 // 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);
-                SelectionSizeTooltip.tooltip.style.left = (left - box.left) + "px";
-                SelectionSizeTooltip.tooltip.style.bottom = (box.bottom - start.top) + "px";
+                FormattedTextBoxComment.tooltip.style.left = (left - box.left) + "px";
+                FormattedTextBoxComment.tooltip.style.bottom = (box.bottom - start.top) + "px";
                 set = "";
             }
         }
         if (set === "none" && state.selection.$from) {
-            SelectionSizeTooltip.textBox = undefined;
+            FormattedTextBoxComment.textBox = undefined;
             let nbef = findStartOfMark(state.selection.$from, view, findLinkMark);
             let naft = findEndOfMark(state.selection.$from, view, findLinkMark);
             let child = state.selection.$from.nodeBefore;
             let mark = child && findLinkMark(child.marks);
             if (mark && child && nbef && naft) {
-                SelectionSizeTooltip.tooltipText.textContent = "link : " + (mark.attrs.title || mark.attrs.href);
+                FormattedTextBoxComment.tooltipText.textContent = "link : " + (mark.attrs.title || mark.attrs.href);
                 if (mark.attrs.href.indexOf(Utils.prepend("/doc/")) === 0) {
                     let docTarget = mark.attrs.href.replace(Utils.prepend("/doc/"), "").split("?")[0];
                     docTarget && DocServer.GetRefField(docTarget).then(linkDoc =>
-                        (linkDoc as Doc) && (SelectionSizeTooltip.tooltipText.textContent = "link :" + StrCast((linkDoc as Doc)!.title)));
+                        (linkDoc as Doc) && (FormattedTextBoxComment.tooltipText.textContent = "link :" + StrCast((linkDoc as Doc)!.title)));
                 }
                 // These are in screen coordinates
                 // let start = view.coordsAtPos(state.selection.from), end = view.coordsAtPos(state.selection.to);
@@ -140,13 +140,13 @@ export class SelectionSizeTooltip {
                 // 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);
-                SelectionSizeTooltip.tooltip.style.left = (left - box.left) + "px";
-                SelectionSizeTooltip.tooltip.style.bottom = (box.bottom - start.top) + "px";
+                FormattedTextBoxComment.tooltip.style.left = (left - box.left) + "px";
+                FormattedTextBoxComment.tooltip.style.bottom = (box.bottom - start.top) + "px";
                 set = "";
             }
         }
-        SelectionSizeTooltip.tooltip && (SelectionSizeTooltip.tooltip.style.display = set);
+        FormattedTextBoxComment.tooltip && (FormattedTextBoxComment.tooltip.style.display = set);
     }
 
-    destroy() { SelectionSizeTooltip.tooltip.style.display = "none"; }
+    destroy() { FormattedTextBoxComment.tooltip.style.display = "none"; }
 }
-- 
cgit v1.2.3-70-g09d2

