From e8095539c208169b1883e8e73686382d8d8c6336 Mon Sep 17 00:00:00 2001
From: Fawn <fangrui_tong@brown.edu>
Date: Sat, 12 Oct 2019 17:18:58 -0400
Subject: created color picker for text on rich text menu

---
 src/client/util/RichTextSchema.tsx | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

(limited to 'src/client/util/RichTextSchema.tsx')

diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx
index a5502577b..e943b71e8 100644
--- a/src/client/util/RichTextSchema.tsx
+++ b/src/client/util/RichTextSchema.tsx
@@ -308,6 +308,24 @@ export const marks: { [index: string]: MarkSpec } = {
         }
     },
 
+    // :: MarkSpec Coloring on text. Has `color` attribute that defined the color of the marked text.
+    color: {
+        attrs: {
+            color: { default: "#000" }
+        },
+        inclusive: false,
+        parseDOM: [{
+            tag: "span", getAttrs(dom: any) {
+                return { color: dom.getAttribute("color") };
+            }
+        }],
+        toDOM(node: any) {
+            return node.attrs.color ? ['span', { style: 'color:' + node.attrs.color }] : ['span', { style: 'color: black' }];
+            // ["div", ["span", `"`], ["span", 0], ["span", `"`], ["br"], ["a", { ...node.attrs, class: "prosemirror-attribution" }, node.attrs.title], ["br"]] :
+            // ["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: {
-- 
cgit v1.2.3-70-g09d2


From bd817ac118c53845c882b89c4b8a110dc8fe10c2 Mon Sep 17 00:00:00 2001
From: Fawn <fangrui_tong@brown.edu>
Date: Wed, 23 Oct 2019 22:22:34 -0400
Subject: added dropdown to highlighter tool to choose colors

---
 src/client/util/RichTextSchema.tsx  |  17 +++++-
 src/client/util/TooltipTextMenu.tsx | 113 ++++++++++++++++++++++++++++++++++--
 2 files changed, 124 insertions(+), 6 deletions(-)

(limited to 'src/client/util/RichTextSchema.tsx')

diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx
index e943b71e8..22756be24 100644
--- a/src/client/util/RichTextSchema.tsx
+++ b/src/client/util/RichTextSchema.tsx
@@ -321,8 +321,21 @@ export const marks: { [index: string]: MarkSpec } = {
         }],
         toDOM(node: any) {
             return node.attrs.color ? ['span', { style: 'color:' + node.attrs.color }] : ['span', { style: 'color: black' }];
-            // ["div", ["span", `"`], ["span", 0], ["span", `"`], ["br"], ["a", { ...node.attrs, class: "prosemirror-attribution" }, node.attrs.title], ["br"]] :
-            // ["a", { ...node.attrs }, 0];
+        }
+    },
+
+    highlight2: {
+        attrs: {
+            highlight: { default: "transparent" }
+        },
+        inclusive: false,
+        parseDOM: [{
+            tag: "span", getAttrs(dom: any) {
+                return { highlight: dom.getAttribute("backgroundColor") };
+            }
+        }],
+        toDOM(node: any) {
+            return node.attrs.highlight ? ['span', { style: 'background-color:' + node.attrs.highlight }] : ['span', { style: 'background-color: transparent' }];
         }
     },
 
diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx
index 92765b1e0..0626eb504 100644
--- a/src/client/util/TooltipTextMenu.tsx
+++ b/src/client/util/TooltipTextMenu.tsx
@@ -1,4 +1,4 @@
-import { library } from '@fortawesome/fontawesome-svg-core';
+import { library, dom } from '@fortawesome/fontawesome-svg-core';
 import { faListUl } from '@fortawesome/free-solid-svg-icons';
 import { action, observable } from "mobx";
 import { Dropdown, icons, MenuItem } from "prosemirror-menu"; //no import css
@@ -60,6 +60,8 @@ export class TooltipTextMenu {
     private listTypeBtnDom?: Node;
     private colorDom?: Node;
     private colorDropdownDom?: Node;
+    private highlightDom?: Node;
+    private highlightDropdownDom?: Node;
 
 
     // private _collapseBtn?: MenuItem;
@@ -136,6 +138,12 @@ export class TooltipTextMenu {
 
         });
 
+        // highlight menu
+        this.highlightDom = this.createHighlightTool().render(this.view).dom;
+        this.highlightDropdownDom = this.createHighlightDropdown().render(this.view).dom;
+        this.tooltip.appendChild(this.highlightDom);
+        this.tooltip.appendChild(this.highlightDropdownDom);
+
         // color menu
         this.colorDom = this.createColorTool().render(this.view).dom;
         this.colorDropdownDom = this.createColorDropdown().render(this.view).dom;
@@ -765,14 +773,111 @@ export class TooltipTextMenu {
     }
 
     createHighlightTool() {
+        return new MenuItem({
+            title: "Highlight",
+            css: "color:white;",
+            class: "menuicon",
+            execEvent: "",
+            render() {
+                let svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
+                svg.setAttribute("viewBox", "-100 -100 650 650");
+                let path = document.createElementNS('http://www.w3.org/2000/svg', "path");
+                path.setAttributeNS(null, "d", "M0 479.98L99.92 512l35.45-35.45-67.04-67.04L0 479.98zm124.61-240.01a36.592 36.592 0 0 0-10.79 38.1l13.05 42.83-50.93 50.94 96.23 96.23 50.86-50.86 42.74 13.08c13.73 4.2 28.65-.01 38.15-10.78l35.55-41.64-173.34-173.34-41.52 35.44zm403.31-160.7l-63.2-63.2c-20.49-20.49-53.38-21.52-75.12-2.35L190.55 183.68l169.77 169.78L530.27 154.4c19.18-21.74 18.15-54.63-2.35-75.13z");
+                svg.appendChild(path);
+
+                let color = document.createElement("div");
+                color.className = "buttonColor";
+                color.style.backgroundColor = TooltipTextMenuManager.Instance.highlight.toString() === "transparent" ? "#fff" : TooltipTextMenuManager.Instance.highlight.toString();
+
+                let wrapper = document.createElement("div");
+                wrapper.id = "colorPicker";
+                wrapper.appendChild(svg);
+                wrapper.appendChild(color);
+                return wrapper;
+            },
+            run: (state, dispatch) => {
+                TooltipTextMenu.insertHighlight(TooltipTextMenuManager.Instance.highlight, this.view.state, this.view.dispatch);
+            }
+        });
+    }
+
+    public static insertHighlight(color: String, state: EditorState<any>, dispatch: any) {
+        if (state.selection.empty) return false;
+
+        let highlightMark = state.schema.mark(state.schema.marks.highlight2, { highlight: color });
+        dispatch(state.tr.addMark(state.selection.from, state.selection.to, highlightMark));
+    }
+
+    createHighlightDropdown() {
+        // menu item for color picker
+        let self = this;
+        let colors = new MenuItem({
+            title: "",
+            execEvent: "",
+            class: "button-setting-disabled",
+            css: "",
+            render() {
+                let p = document.createElement("p");
+                p.textContent = "Change highlight:";
+
+                let colorsWrapper = document.createElement("div");
+                colorsWrapper.className = "colorPicker-wrapper";
+
+                let colors = [
+                    PastelSchemaPalette.get("pink2"),
+                    PastelSchemaPalette.get("purple4"),
+                    PastelSchemaPalette.get("bluegreen1"),
+                    PastelSchemaPalette.get("yellow4"),
+                    PastelSchemaPalette.get("red2"),
+                    PastelSchemaPalette.get("bluegreen7"),
+                    PastelSchemaPalette.get("bluegreen5"),
+                    PastelSchemaPalette.get("orange1"),
+                    // "#f1f0eb",
+                    "transparent"
+                ];
 
+                colors.forEach(color => {
+                    let button = document.createElement("button");
+                    button.className = color === TooltipTextMenuManager.Instance.highlight ? "colorPicker active" : "colorPicker";
+                    if (color) {
+                        button.style.backgroundColor = color === "transparent" ? "#fff" : color;
+                        button.style.color = "black";
+                        button.textContent = color === "transparent" ? "X" : "";
+                        button.onclick = e => {
+                            TooltipTextMenuManager.Instance.highlight = color;
+
+                            TooltipTextMenu.insertHighlight(TooltipTextMenuManager.Instance.highlight, self.view.state, self.view.dispatch);
+
+                            // update color menu
+                            let highlightDom = self.createHighlightTool().render(self.view).dom;
+                            let highlightDropdownDom = self.createHighlightDropdown().render(self.view).dom;
+                            self.highlightDom && self.tooltip.replaceChild(highlightDom, self.highlightDom);
+                            self.highlightDropdownDom && self.tooltip.replaceChild(highlightDropdownDom, self.highlightDropdownDom);
+                            self.highlightDom = highlightDom;
+                            self.highlightDropdownDom = highlightDropdownDom;
+                        };
+                    }
+                    colorsWrapper.appendChild(button);
+                });
+
+                let div = document.createElement("div");
+                div.appendChild(p);
+                div.appendChild(colorsWrapper);
+                return div;
+            },
+            enable() { return false; },
+            run(p1, p2, p3, event) {
+                event.stopPropagation();
+            }
+        });
+
+        let colorDropdown = new Dropdown([colors], { class: "buttonSettings-dropdown" }) as MenuItem;
+        return colorDropdown;
     }
 
     createColorTool() {
         return new MenuItem({
             title: "Color",
-            // label: "Color",
-            // icon: icon,
             css: "color:white;",
             class: "menuicon",
             execEvent: "",
@@ -796,7 +901,6 @@ export class TooltipTextMenu {
             run: (state, dispatch) => {
                 TooltipTextMenu.insertColor(TooltipTextMenuManager.Instance.color, this.view.state, this.view.dispatch);
             }
-
         });
     }
 
@@ -1406,6 +1510,7 @@ class TooltipTextMenuManager {
     public _brushIsEmpty: boolean = true;
 
     public color: String = "#000";
+    public highlight: String = "transparent";
 
     public activeMenu: TooltipTextMenu | undefined;
 
-- 
cgit v1.2.3-70-g09d2


From 76411ba009c4265751431d255ab3cc9a3cfab69f Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Tue, 12 Nov 2019 21:28:39 -0500
Subject: switched link preview to be a document view

---
 src/client/util/RichTextSchema.tsx                 |  1 -
 .../views/nodes/ContentFittingDocumentView.tsx     |  2 +
 src/client/views/nodes/DocumentView.tsx            |  6 ++-
 src/client/views/nodes/FormattedTextBoxComment.tsx | 62 +++++++++++++++++-----
 4 files changed, 54 insertions(+), 17 deletions(-)

(limited to 'src/client/util/RichTextSchema.tsx')

diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx
index 76b8aeaa1..1004cb3d4 100644
--- a/src/client/util/RichTextSchema.tsx
+++ b/src/client/util/RichTextSchema.tsx
@@ -18,7 +18,6 @@ import { Transform } from "./Transform";
 import React = require("react");
 import { BoolCast, NumCast } from "../../new_fields/Types";
 import { FormattedTextBox } from "../views/nodes/FormattedTextBox";
-import { CurrentUserUtils } from "../../server/authentication/models/current_user_utils";
 
 const pDOM: DOMOutputSpecArray = ["p", 0], blockquoteDOM: DOMOutputSpecArray = ["blockquote", 0], hrDOM: DOMOutputSpecArray = ["hr"],
     preDOM: DOMOutputSpecArray = ["pre", ["code", 0]], brDOM: DOMOutputSpecArray = ["br"], ulDOM: DOMOutputSpecArray = ["ul", 0];
diff --git a/src/client/views/nodes/ContentFittingDocumentView.tsx b/src/client/views/nodes/ContentFittingDocumentView.tsx
index f41c4fc91..751355403 100644
--- a/src/client/views/nodes/ContentFittingDocumentView.tsx
+++ b/src/client/views/nodes/ContentFittingDocumentView.tsx
@@ -37,6 +37,7 @@ interface ContentFittingDocumentViewProps {
     whenActiveChanged: (isActive: boolean) => void;
     addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => boolean;
     pinToPres: (document: Doc) => void;
+    dontRegisterView?: boolean;
     setPreviewScript: (script: string) => void;
     previewScript?: string;
 }
@@ -108,6 +109,7 @@ export class ContentFittingDocumentView extends React.Component<ContentFittingDo
                         focus={this.props.focus || emptyFunction}
                         backgroundColor={returnEmptyString}
                         bringToFront={emptyFunction}
+                        dontRegisterView={this.props.dontRegisterView}
                         zoomToScale={emptyFunction}
                         getScale={returnOne}
                     />
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 93052ea73..4fbb0516f 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -74,6 +74,7 @@ export interface DocumentViewProps {
     getScale: () => number;
     animateBetweenIcon?: (maximize: boolean, target: number[]) => void;
     ChromeHeight?: () => number;
+    dontRegisterView?: boolean;
     layoutKey?: string;
 }
 
@@ -98,7 +99,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     @action
     componentDidMount() {
         this._mainCont.current && (this._dropDisposer = DragManager.MakeDropTarget(this._mainCont.current, { handlers: { drop: this.drop.bind(this) } }));
-        DocumentManager.Instance.DocumentViews.push(this);
+
+        !this.props.dontRegisterView && DocumentManager.Instance.DocumentViews.push(this);
     }
 
     @action
@@ -111,7 +113,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     componentWillUnmount() {
         this._dropDisposer && this._dropDisposer();
         Doc.UnBrushDoc(this.props.Document);
-        DocumentManager.Instance.DocumentViews.splice(DocumentManager.Instance.DocumentViews.indexOf(this), 1);
+        !this.props.dontRegisterView && DocumentManager.Instance.DocumentViews.splice(DocumentManager.Instance.DocumentViews.indexOf(this), 1);
     }
 
     startDragging(x: number, y: number, dropAction: dropActionType, applyAsTemplate?: boolean) {
diff --git a/src/client/views/nodes/FormattedTextBoxComment.tsx b/src/client/views/nodes/FormattedTextBoxComment.tsx
index f35ca502f..98cd17972 100644
--- a/src/client/views/nodes/FormattedTextBoxComment.tsx
+++ b/src/client/views/nodes/FormattedTextBoxComment.tsx
@@ -2,16 +2,19 @@ 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";
+import { Doc, WidthSym } from "../../../new_fields/Doc";
 import { schema } from "../../util/RichTextSchema";
 import { DocServer } from "../../DocServer";
-import { Utils } from "../../../Utils";
-import { StrCast, Cast, FieldValue } from "../../../new_fields/Types";
+import { Utils, returnTrue, returnFalse, emptyFunction, returnEmptyString, returnOne } from "../../../Utils";
+import { StrCast, Cast, FieldValue, NumCast } from "../../../new_fields/Types";
 import { FormattedTextBox } from "./FormattedTextBox";
-import { DocUtils } from "../../documents/Documents";
-import { isBuffer } from "util";
 import { DocumentManager } from "../../util/DocumentManager";
 import { DocumentType } from "../../documents/DocumentTypes";
+import { DocumentView } from "./DocumentView";
+import React = require("react");
+import * as ReactDOM from 'react-dom';
+import { Transform } from "../../util/Transform";
+import { ContentFittingDocumentView } from "./ContentFittingDocumentView";
 
 export let formattedTextBoxCommentPlugin = new Plugin({
     view(editorView) { return new FormattedTextBoxComment(editorView); }
@@ -67,22 +70,28 @@ export class FormattedTextBoxComment {
             FormattedTextBoxComment.tooltip.className = "FormattedTextBox-tooltip";
             FormattedTextBoxComment.tooltip.style.pointerEvents = "all";
             FormattedTextBoxComment.tooltip.style.maxWidth = "350px";
+            FormattedTextBoxComment.tooltip.style.maxHeight = "250px";
+            FormattedTextBoxComment.tooltip.style.width = "100%";
+            FormattedTextBoxComment.tooltip.style.height = "100%";
+            FormattedTextBoxComment.tooltip.style.overflow = "hidden";
+            FormattedTextBoxComment.tooltip.style.display = "none";
             FormattedTextBoxComment.tooltip.appendChild(input);
             FormattedTextBoxComment.tooltip.onpointerdown = (e: PointerEvent) => {
                 let keep = e.target && (e.target as any).type === "checkbox" ? true : false;
-                if (FormattedTextBoxComment.linkDoc && !keep && FormattedTextBoxComment.textBox) {
-                    DocumentManager.Instance.FollowLink(FormattedTextBoxComment.linkDoc, FormattedTextBoxComment.textBox.props.Document,
-                        (doc: Doc, maxLocation: string) => FormattedTextBoxComment.textBox!.props.addDocTab(doc, undefined, e.ctrlKey ? "inTab" : "onRight"));
+                const textBox = FormattedTextBoxComment.textBox;
+                if (FormattedTextBoxComment.linkDoc && !keep && textBox) {
+                    DocumentManager.Instance.FollowLink(FormattedTextBoxComment.linkDoc, textBox.props.Document,
+                        (doc: Doc, maxLocation: string) => textBox.props.addDocTab(doc, undefined, e.ctrlKey ? "inTab" : "onRight"));
                 }
                 FormattedTextBoxComment.opened = keep || !FormattedTextBoxComment.opened;
-                FormattedTextBoxComment.textBox && FormattedTextBoxComment.start !== undefined && FormattedTextBoxComment.textBox.setAnnotation(
+                textBox && FormattedTextBoxComment.start !== undefined && textBox.setAnnotation(
                     FormattedTextBoxComment.start, FormattedTextBoxComment.end, FormattedTextBoxComment.mark,
                     FormattedTextBoxComment.opened, keep);
                 e.stopPropagation();
             };
             root && root.appendChild(FormattedTextBoxComment.tooltip);
         }
-        this.update(view, undefined);
+        //this.update(view, undefined);
     }
 
     public static Hide() {
@@ -144,9 +153,34 @@ export class FormattedTextBoxComment {
                         if (linkDoc instanceof Doc) {
                             FormattedTextBoxComment.linkDoc = linkDoc;
                             let target = FieldValue(Doc.AreProtosEqual(FieldValue(Cast(linkDoc.anchor1, Doc)), textBox.props.Document) ? Cast(linkDoc.anchor2, Doc) : Cast(linkDoc.anchor1, Doc));
-                            let ext = (target && target.type !== DocumentType.PDFANNO && Doc.fieldExtensionDoc(target, "data")) || target; // try guessing that the target doc's data is in the 'data' field.  probably need an 'overviewLayout' and then just display the target Document ....
-                            let text = ext && StrCast(ext.text);
-                            ext && (FormattedTextBoxComment.tooltipText.textContent = (target && target.type === DocumentType.PDFANNO ? "Quoted from " : "") + "=> " + (text || StrCast(ext.title)));
+                            try {
+                                ReactDOM.unmountComponentAtNode(FormattedTextBoxComment.tooltipText);
+                            } catch (e) {
+
+                            }
+                            target && ReactDOM.render(<ContentFittingDocumentView
+                                fitToBox={true}
+                                Document={target}
+                                fieldKey={"data"}
+                                moveDocument={returnFalse}
+                                getTransform={Transform.Identity}
+                                active={returnFalse}
+                                setPreviewScript={returnEmptyString}
+                                addDocument={returnFalse}
+                                removeDocument={returnFalse}
+                                ruleProvider={undefined}
+                                addDocTab={returnFalse}
+                                pinToPres={returnFalse}
+                                dontRegisterView={true}
+                                renderDepth={1}
+                                PanelWidth={() => 350}
+                                PanelHeight={() => 250}
+                                focus={emptyFunction}
+                                whenActiveChanged={returnFalse}
+                            />, FormattedTextBoxComment.tooltipText);
+                            // let ext = (target && target.type !== DocumentType.PDFANNO && Doc.fieldExtensionDoc(target, "data")) || target; // try guessing that the target doc's data is in the 'data' field.  probably need an 'overviewLayout' and then just display the target Document ....
+                            // let text = ext && StrCast(ext.text);
+                            // ext && (FormattedTextBoxComment.tooltipText.textContent = (target && target.type === DocumentType.PDFANNO ? "Quoted from " : "") + "=> " + (text || StrCast(ext.title)));
                         }
                     });
                 }
@@ -168,5 +202,5 @@ export class FormattedTextBoxComment {
         FormattedTextBoxComment.tooltip && (FormattedTextBoxComment.tooltip.style.display = set);
     }
 
-    destroy() { FormattedTextBoxComment.tooltip.style.display = "none"; }
+    destroy() { }//FormattedTextBoxComment.tooltip.style.display = "none"; }
 }
-- 
cgit v1.2.3-70-g09d2


From e87b4b99323875afce2d9847f3bddd4196b85b81 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Wed, 13 Nov 2019 14:48:57 -0500
Subject: added a lockedTransform field to lock pan/zoom.  fixed text
 scrollintoview to scroll only when necessary.

---
 src/client/documents/Documents.ts                                  | 3 ++-
 src/client/util/RichTextSchema.tsx                                 | 1 +
 .../collections/collectionFreeForm/CollectionFreeFormView.tsx      | 4 ++--
 src/client/views/nodes/DocumentView.tsx                            | 7 +++++++
 src/client/views/nodes/FormattedTextBox.tsx                        | 4 +++-
 src/new_fields/documentSchemas.ts                                  | 3 ++-
 6 files changed, 17 insertions(+), 5 deletions(-)

(limited to 'src/client/util/RichTextSchema.tsx')

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index ba9f87025..1a9d67d83 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -76,7 +76,8 @@ export interface DocumentOptions {
     viewType?: number;
     backgroundColor?: string;
     ignoreClick?: boolean;
-    lockedPosition?: boolean;
+    lockedPosition?: boolean; // lock the x,y coordinates of the document so that it can't be dragged
+    lockedTransform?: boolean; // lock the panx,pany and scale parameters of the document so that it be panned/zoomed
     opacity?: number;
     defaultBackgroundColor?: string;
     dropAction?: dropActionType;
diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx
index 1004cb3d4..0d1ae3841 100644
--- a/src/client/util/RichTextSchema.tsx
+++ b/src/client/util/RichTextSchema.tsx
@@ -679,6 +679,7 @@ export class DashDocView {
                     bringToFront={emptyFunction}
                     zoomToScale={emptyFunction}
                     getScale={returnOne}
+                    dontRegisterView={true}
                     ContainingCollectionView={undefined}
                     ContainingCollectionDoc={undefined}
                     ContentScaling={this.contentScaling}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 6e0f75bc1..0c5f4ec80 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -333,7 +333,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
 
     @action
     onPointerWheel = (e: React.WheelEvent): void => {
-        if (this.props.Document.lockedPosition || this.props.Document.inOverlay) return;
+        if (this.props.Document.lockedTransform || this.props.Document.inOverlay) return;
         if (!e.ctrlKey && this.props.Document.scrollHeight !== undefined) { // things that can scroll vertically should do that instead of zooming
             e.stopPropagation();
         }
@@ -355,7 +355,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
 
     @action
     setPan(panX: number, panY: number, panType: string = "None") {
-        if (!this.Document.lockedPosition || this.Document.inOverlay) {
+        if (!this.Document.lockedTransform || this.Document.inOverlay) {
             this.Document.panTransformType = panType;
             var scale = this.getLocalTransform().inverse().Scale;
             const newPanX = Math.min((1 - 1 / scale) * this.nativeWidth, Math.max(0, panX));
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 7e81cd673..98c610c68 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -372,6 +372,12 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         this.Document.lockedPosition = this.Document.lockedPosition ? undefined : true;
     }
 
+    @undoBatch
+    @action
+    toggleLockTransform = (): void => {
+        this.Document.lockedTransform = this.Document.lockedTransform ? undefined : true;
+    }
+
     listen = async () => {
         Doc.GetProto(this.props.Document).transcript = await DictationManager.Controls.listen({
             continuous: { indefinite: true },
@@ -444,6 +450,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         layoutItems.push({ description: `${this.Document.autoHeight ? "Variable Height" : "Auto Height"}`, event: () => this.layoutDoc.autoHeight = !this.layoutDoc.autoHeight, icon: "plus" });
         layoutItems.push({ description: this.Document.ignoreAspect || !this.Document.nativeWidth || !this.Document.nativeHeight ? "Freeze" : "Unfreeze", event: this.freezeNativeDimensions, icon: "snowflake" });
         layoutItems.push({ description: this.Document.lockedPosition ? "Unlock Position" : "Lock Position", event: this.toggleLockPosition, icon: BoolCast(this.Document.lockedPosition) ? "unlock" : "lock" });
+        layoutItems.push({ description: this.Document.lockedTransform ? "Unlock Transform" : "Lock Transform", event: this.toggleLockTransform, icon: BoolCast(this.Document.lockedTransform) ? "unlock" : "lock" });
         layoutItems.push({ description: "Center View", event: () => this.props.focus(this.props.Document, false), icon: "crosshairs" });
         layoutItems.push({ description: "Zoom to Document", event: () => this.props.focus(this.props.Document, true), icon: "search" });
         if (this.Document.type !== DocumentType.COL && this.Document.type !== DocumentType.TEMPLATE) {
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 8b1e65663..015a21fd2 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -774,7 +774,9 @@ export class FormattedTextBox extends DocExtendableComponent<(FieldViewProps & F
                     while (refNode && !("getBoundingClientRect" in refNode)) refNode = refNode.parentElement;
                     let r1 = refNode && refNode.getBoundingClientRect();
                     let r3 = self._ref.current!.getBoundingClientRect();
-                    r1 && (self._ref.current!.scrollTop += (r1.top - r3.top) * self.props.ScreenToLocalTransform().Scale);
+                    if (r1.top < r3.top || r1.top > r3.bottom) {
+                        r1 && (self._ref.current!.scrollTop += (r1.top - r3.top) * self.props.ScreenToLocalTransform().Scale);
+                    }
                     return true;
                 },
                 dispatchTransaction: this.dispatchTransaction,
diff --git a/src/new_fields/documentSchemas.ts b/src/new_fields/documentSchemas.ts
index e2730914f..fa47374f1 100644
--- a/src/new_fields/documentSchemas.ts
+++ b/src/new_fields/documentSchemas.ts
@@ -31,7 +31,8 @@ export const documentSchema = createSchema({
     summarizedDocs: listSpec(Doc), // documents that are summarized by this document (and which will typically be opened by clicking this document)
     maximizedDocs: listSpec(Doc), // documents to maximize when clicking this document (generally this document will be an icon)
     maximizeLocation: "string", // flag for where to place content when following a click interaction (e.g., onRight, inPlace, inTab) 
-    lockedPosition: "boolean",  // whether the document can be spatially manipulated
+    lockedPosition: "boolean",  // whether the document can be moved (dragged)
+    lockedTransform: "boolean", // whether the document can be panned/zoomed
     inOverlay: "boolean",       // whether the document is rendered in an OverlayView which handles selection/dragging differently
     borderRounding: "string",   // border radius rounding of document
     searchFields: "string",     // the search fields to display when this document matches a search in its metadata
-- 
cgit v1.2.3-70-g09d2


From c6b602e2ab00d3a38e56164ecc928d5db8c12c72 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Thu, 14 Nov 2019 11:35:33 -0500
Subject: fixed warning/errors.  cleaned up link following box a bit.

---
 src/client/util/RichTextSchema.tsx                 |  4 ++--
 src/client/util/TooltipTextMenu.tsx                | 20 ++++++++-----------
 .../views/collections/CollectionSchemaView.tsx     |  1 -
 src/client/views/nodes/DocumentView.tsx            |  2 +-
 src/client/views/nodes/FontIconBox.tsx             |  2 +-
 src/client/views/nodes/FormattedTextBox.tsx        |  1 +
 src/client/views/nodes/FormattedTextBoxComment.tsx | 23 ++++++++++++++++------
 src/client/views/search/FilterBox.tsx              |  4 ++--
 8 files changed, 32 insertions(+), 25 deletions(-)

(limited to 'src/client/util/RichTextSchema.tsx')

diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx
index 0d1ae3841..0fc526ca7 100644
--- a/src/client/util/RichTextSchema.tsx
+++ b/src/client/util/RichTextSchema.tsx
@@ -575,8 +575,8 @@ export class ImageResizeView {
         this._handle.onpointerdown = function (e: any) {
             e.preventDefault();
             e.stopPropagation();
-            let wid = Number(getComputedStyle(self._img).width!.replace(/px/, ""));
-            let hgt = Number(getComputedStyle(self._img).height!.replace(/px/, ""));
+            let wid = Number(getComputedStyle(self._img).width.replace(/px/, ""));
+            let hgt = Number(getComputedStyle(self._img).height.replace(/px/, ""));
             const startX = e.pageX;
             const startWidth = parseFloat(node.attrs.width);
             const onpointermove = (e: any) => {
diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx
index 38471a955..5e11ae653 100644
--- a/src/client/util/TooltipTextMenu.tsx
+++ b/src/client/util/TooltipTextMenu.tsx
@@ -1000,12 +1000,10 @@ export class TooltipTextMenu {
         let activeMarks: MarkType[];
         if (!empty) {
             activeMarks = markGroup.filter(mark => {
-                if (dispatch) {
-                    let has = false;
-                    for (let i = 0; !has && i < ranges.length; i++) {
-                        let { $from, $to } = ranges[i];
-                        return state.doc.rangeHasMark($from.pos, $to.pos, mark);
-                    }
+                let has = false;
+                for (let i = 0; !has && i < ranges.length; i++) {
+                    let { $from, $to } = ranges[i];
+                    return state.doc.rangeHasMark($from.pos, $to.pos, mark);
                 }
                 return false;
             });
@@ -1024,13 +1022,11 @@ export class TooltipTextMenu {
                 }
                 this._activeMarks = ref_node.marks;
                 activeMarks = markGroup.filter(mark_type => {
-                    if (dispatch) {
-                        if (mark_type === state.schema.marks.pFontSize) {
-                            return ref_node.marks.some(m => m.type.name === state.schema.marks.pFontSize.name);
-                        }
-                        let mark = state.schema.mark(mark_type);
-                        return ref_node.marks.includes(mark);
+                    if (mark_type === state.schema.marks.pFontSize) {
+                        return ref_node.marks.some(m => m.type.name === state.schema.marks.pFontSize.name);
                     }
+                    let mark = state.schema.mark(mark_type);
+                    return ref_node.marks.includes(mark);
                     return false;
                 });
             }
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 203c68463..ebd47fd19 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -141,7 +141,6 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
             <ContentFittingDocumentView
                 Document={layoutDoc}
                 DataDocument={this.previewDocument !== this.props.DataDoc ? this.props.DataDoc : undefined}
-                fieldKey={this.props.fieldKey}
                 childDocs={this.childDocs}
                 renderDepth={this.props.renderDepth}
                 ruleProvider={this.props.Document.isRuleProvider && layoutDoc && layoutDoc.type !== DocumentType.TEXT ? this.props.Document : this.props.ruleProvider}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 98c610c68..7132f181e 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -639,7 +639,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                     {searchHighlight}
                 </div>
             }
-        </>
+        </>;
     }
     render() {
         if (!this.props.Document) return (null);
diff --git a/src/client/views/nodes/FontIconBox.tsx b/src/client/views/nodes/FontIconBox.tsx
index ae9273639..83ecc4657 100644
--- a/src/client/views/nodes/FontIconBox.tsx
+++ b/src/client/views/nodes/FontIconBox.tsx
@@ -25,7 +25,7 @@ export class FontIconBox extends DocComponent<FieldViewProps, FontIconDocument>(
         this._backgroundReaction = reaction(() => this.props.Document.backgroundColor,
             () => {
                 if (this._ref && this._ref.current) {
-                    let col = Utils.fromRGBAstr(getComputedStyle(this._ref.current).backgroundColor!);
+                    let col = Utils.fromRGBAstr(getComputedStyle(this._ref.current).backgroundColor);
                     let colsum = (col.r + col.g + col.b);
                     if (colsum / col.a > 600 || col.a < 0.25) runInAction(() => this._foregroundColor = "black");
                     else if (colsum / col.a <= 600 || col.a >= .25) runInAction(() => this._foregroundColor = "white");
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 015a21fd2..6d60e8f77 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -288,6 +288,7 @@ export class FormattedTextBox extends DocExtendableComponent<(FieldViewProps & F
         if (context === node) return { from: offset, to: offset + node.nodeSize };
 
         if (node.isBlock) {
+            // tslint:disable-next-line: prefer-for-of
             for (let i = 0; i < (context.content as any).content.length; i++) {
                 let result = this.getNodeEndpoints((context.content as any).content[i], node);
                 if (result) {
diff --git a/src/client/views/nodes/FormattedTextBoxComment.tsx b/src/client/views/nodes/FormattedTextBoxComment.tsx
index 29b4b6383..32efb16cf 100644
--- a/src/client/views/nodes/FormattedTextBoxComment.tsx
+++ b/src/client/views/nodes/FormattedTextBoxComment.tsx
@@ -13,6 +13,7 @@ import { ContentFittingDocumentView } from "./ContentFittingDocumentView";
 import { FormattedTextBox } from "./FormattedTextBox";
 import './FormattedTextBoxComment.scss';
 import React = require("react");
+import { Docs } from "../../documents/Documents";
 
 export let formattedTextBoxCommentPlugin = new Plugin({
     view(editorView) { return new FormattedTextBoxComment(editorView); }
@@ -51,6 +52,7 @@ export function findEndOfMark(rpos: ResolvedPos, view: EditorView, finder: (mark
 export class FormattedTextBoxComment {
     static tooltip: HTMLElement;
     static tooltipText: HTMLElement;
+    static tooltipInput: HTMLInputElement;
     static start: number;
     static end: number;
     static mark: Mark;
@@ -60,10 +62,15 @@ export class FormattedTextBoxComment {
     constructor(view: any) {
         if (!FormattedTextBoxComment.tooltip) {
             const root = document.getElementById("root");
-            let input = document.createElement("input");
-            input.type = "checkbox";
+            FormattedTextBoxComment.tooltipInput = document.createElement("input");
+            FormattedTextBoxComment.tooltipInput.type = "checkbox";
             FormattedTextBoxComment.tooltip = document.createElement("div");
             FormattedTextBoxComment.tooltipText = document.createElement("div");
+            FormattedTextBoxComment.tooltipText.style.whiteSpace = "pre";
+            FormattedTextBoxComment.tooltipText.style.overflow = "hidden";
+            FormattedTextBoxComment.tooltipText.style.width = "100%";
+            FormattedTextBoxComment.tooltipText.style.height = "100%";
+            FormattedTextBoxComment.tooltipText.style.textOverflow = "ellipsis";
             FormattedTextBoxComment.tooltip.appendChild(FormattedTextBoxComment.tooltipText);
             FormattedTextBoxComment.tooltip.className = "FormattedTextBox-tooltip";
             FormattedTextBoxComment.tooltip.style.pointerEvents = "all";
@@ -73,13 +80,15 @@ export class FormattedTextBoxComment {
             FormattedTextBoxComment.tooltip.style.height = "100%";
             FormattedTextBoxComment.tooltip.style.overflow = "hidden";
             FormattedTextBoxComment.tooltip.style.display = "none";
-            FormattedTextBoxComment.tooltip.appendChild(input);
+            FormattedTextBoxComment.tooltip.appendChild(FormattedTextBoxComment.tooltipInput);
             FormattedTextBoxComment.tooltip.onpointerdown = (e: PointerEvent) => {
                 let keep = e.target && (e.target as any).type === "checkbox" ? true : false;
                 const textBox = FormattedTextBoxComment.textBox;
                 if (FormattedTextBoxComment.linkDoc && !keep && textBox) {
                     DocumentManager.Instance.FollowLink(FormattedTextBoxComment.linkDoc, textBox.props.Document,
                         (doc: Doc, maxLocation: string) => textBox.props.addDocTab(doc, undefined, e.ctrlKey ? "inTab" : "onRight"));
+                } else if (textBox && (FormattedTextBoxComment.tooltipText as any).href) {
+                    textBox.props.addDocTab(Docs.Create.WebDocument((FormattedTextBoxComment.tooltipText as any).href, { width: 200, height: 400 }), undefined, "onRight");
                 }
                 FormattedTextBoxComment.opened = keep || !FormattedTextBoxComment.opened;
                 textBox && FormattedTextBoxComment.start !== undefined && textBox.setAnnotation(
@@ -120,8 +129,10 @@ export class FormattedTextBoxComment {
         }
         let set = "none";
         let nbef = 0;
+        FormattedTextBoxComment.tooltipInput.style.display = "none";
         FormattedTextBoxComment.tooltip.style.width = "";
         FormattedTextBoxComment.tooltip.style.height = "";
+        (FormattedTextBoxComment.tooltipText as any).href = "";
         // this section checks to see if the insertion point is over text entered by a different user.  If so, it sets ths comment text to indicate the user and the modification date
         if (state.selection.$from) {
             nbef = findStartOfMark(state.selection.$from, view, findOtherUserMark);
@@ -136,6 +147,7 @@ export class FormattedTextBoxComment {
             if (mark && child && ((nbef && naft) || !noselection)) {
                 FormattedTextBoxComment.tooltipText.textContent = mark.attrs.userid + " date=" + (new Date(mark.attrs.modified * 5000)).toDateString();
                 set = "";
+                FormattedTextBoxComment.tooltipInput.style.display = "";
             }
         }
         // this checks if the selection is a hyperlink.  If so, it displays the target doc's text for internal links, and the url of the target for external links. 
@@ -147,6 +159,7 @@ export class FormattedTextBoxComment {
             let mark = child && findLinkMark(child.marks);
             if (mark && child && nbef && naft) {
                 FormattedTextBoxComment.tooltipText.textContent = "external => " + mark.attrs.href;
+                (FormattedTextBoxComment.tooltipText as any).href = 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 => {
@@ -155,9 +168,7 @@ export class FormattedTextBoxComment {
                             const target = FieldValue(Doc.AreProtosEqual(FieldValue(Cast(linkDoc.anchor1, Doc)), textBox.props.Document) ? Cast(linkDoc.anchor2, Doc) : Cast(linkDoc.anchor1, Doc));
                             try {
                                 ReactDOM.unmountComponentAtNode(FormattedTextBoxComment.tooltipText);
-                            } catch (e) {
-
-                            }
+                            } catch (e) { }
                             if (target) {
                                 ReactDOM.render(<ContentFittingDocumentView
                                     fitToBox={true}
diff --git a/src/client/views/search/FilterBox.tsx b/src/client/views/search/FilterBox.tsx
index 6e7e4ee1b..62f3aba4c 100644
--- a/src/client/views/search/FilterBox.tsx
+++ b/src/client/views/search/FilterBox.tsx
@@ -82,7 +82,7 @@ export class FilterBox extends React.Component {
                     var panel = this.nextElementSibling as HTMLElement;
                     if (panel.style.maxHeight) {
                         panel.style.overflow = "hidden";
-                        panel.style.maxHeight = null;
+                        panel.style.maxHeight = "";
                         panel.style.opacity = "0";
                     } else {
                         setTimeout(() => {
@@ -114,7 +114,7 @@ export class FilterBox extends React.Component {
                     acc[i].classList.toggle("active");
                     var panel = acc[i].nextElementSibling as HTMLElement;
                     panel.style.overflow = "hidden";
-                    panel.style.maxHeight = null;
+                    panel.style.maxHeight = "";
                 }
             }
         });
-- 
cgit v1.2.3-70-g09d2


From e3f06e390f98cc5b97d63fc287daff994d5fef6f Mon Sep 17 00:00:00 2001
From: Fawn <fangrui_tong@brown.edu>
Date: Thu, 14 Nov 2019 23:01:49 -0500
Subject: double cliking dragger collapses menu

---
 src/client/util/RichTextSchema.tsx   |   1 +
 src/client/util/TooltipTextMenu.scss | 399 ++---------------------
 src/client/util/TooltipTextMenu.tsx  | 610 +++++++++++++++--------------------
 3 files changed, 278 insertions(+), 732 deletions(-)

(limited to 'src/client/util/RichTextSchema.tsx')

diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx
index 1eea529d2..69f9a4c53 100644
--- a/src/client/util/RichTextSchema.tsx
+++ b/src/client/util/RichTextSchema.tsx
@@ -130,6 +130,7 @@ export const nodes: { [index: string]: NodeSpec } = {
         //     }
         // }]
     },
+
     // :: NodeSpec An inline image (`<img>`) node. Supports `src`,
     // `alt`, and `href` attributes. The latter two default to the empty
     // string.
diff --git a/src/client/util/TooltipTextMenu.scss b/src/client/util/TooltipTextMenu.scss
index 1f619b4f5..8310a0da6 100644
--- a/src/client/util/TooltipTextMenu.scss
+++ b/src/client/util/TooltipTextMenu.scss
@@ -1,6 +1,5 @@
 @import "../views/globalCssVariables";
 .ProseMirror-menu-dropdown-wrap {
-    // margin: 0 4px;
     display: inline-block;
     position: relative;
 }
@@ -39,9 +38,10 @@
     font-size: 12px;
     background: white;
     border: 1px solid rgb(223, 223, 223);
-    min-width: 50px;
+    min-width: 40px;
     z-index: 50000;
     position: absolute;
+    box-sizing: content-box;
 
     .ProseMirror-menu-dropdown-item {
         cursor: pointer;
@@ -91,20 +91,22 @@
 .wrapper {
     position: absolute;
     pointer-events: all;
-}
+    display: flex;
+    align-items: center;
+    transform: translateY(-85px);
 
-.tooltipMenu {
-    position: absolute;
-    z-index: 20000;
+    height: 35px;
     background: #323232;
     border-radius: 6px;
-    transform: translateY(-85px);
+    box-shadow: 3px 3px 3px rgba(0, 0, 0, 0.25);
+
+}
+
+.tooltipMenu, .basic-tools {
+    z-index: 20000;
     pointer-events: all;
-    height: 35px;
-    // width: 650px;
     padding: 3px;
     padding-bottom: 5px;
-    box-shadow: 3px 3px 3px rgba(0, 0, 0, 0.25);
     display: flex;
     align-items: center;
 
@@ -203,7 +205,7 @@
 }
 
 .underline svg {
-    margin-top: 15px;
+    margin-top: 13px;
 }
 
   .font-size-indicator {
@@ -232,21 +234,19 @@
     width: 1em;
     height: 1em;
     stroke-width: 3;
-    // stroke: greenyellow;
     fill: greenyellow;
     margin-right: 15px;
 }
 
-.dragger {
+.dragger-wrapper {
     color: #eee;
-    margin-left: 5px;
-    width: 16px;
     height: 22px;
-    display: inline-block;
+    padding: 0 5px;
+    box-sizing: content-box;
     cursor: grab;
 
-    .dragger-wrapper {
-        width: 100%;
+    .dragger {
+        width: 18px;
         height: 100%;
         display: flex;
         justify-content: space-evenly;
@@ -259,11 +259,6 @@
     }
 }
 
-// .menuicon:hover + .ProseMirror-menu-dropdown-wrap .buttonSettings-dropdown, 
-// .menuicon-active:hover + .ProseMirror-menu-dropdown-wrap .buttonSettings-dropdown {
-//     background-color: black;
-// }
-
 .button-dropdown-wrapper {
     display: flex;
     align-content: center;
@@ -374,360 +369,4 @@ button.colorPicker {
     &.active {
         border: 2px solid white !important;
     }
-}
-
-// @import "../views/globalCssVariables";
-
-// .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;
-//     z-index: 50000;
-//     position: relative;
-//   }
-  
-//   .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;
-//     margin: 3px;
-//     background: white;
-//     border-radius: 3px;
-//     text-align: center;
-//   }
-  
-//   .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 {
-//     background: $dark-color;
-//     color:white;
-//     border: 1px solid rgb(223, 223, 223);
-//     padding: 2px;
-//   }
-  
-//   .ProseMirror-menu-dropdown-menu {
-//     z-index: 50000;
-//     min-width: 6em;
-//     background: white;
-//     position: absolute;
-//   }
-  
-//   .linking {
-//       text-align: center;
-//   }
-
-//   .ProseMirror-menu-dropdown-item {
-//     cursor: pointer;
-//     padding: 2px 8px 2px 4px;
-//     width: auto;
-//     z-index: 100000;
-//   }
-  
-//   .ProseMirror-menu-dropdown-item:hover {
-//     background: white;
-//   }
-  
-//   .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: 10px 10px;
-//     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:white;
-//     height: 1em;
-//   }
-  
-//   .ProseMirror-icon span {
-//     vertical-align: text-top;
-//   }  
-  
-//   .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 white(255, 255, 255, 0.2);
-//   }
-  
-//   .ProseMirror-prompt h5 {
-//     margin: 0;
-//     font-weight: normal;
-//     font-size: 100%;
-//     color: #444;
-//   }
-  
-//   .ProseMirror-prompt input[type="text"],
-//   .ProseMirror-prompt textarea {
-//     background: white;
-//     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: 20000;
-//     background: #121721;
-//     border: 1px solid silver;
-//     border-radius: 15px;
-//     //height: 60px;
-//     //padding: 2px 10px;
-//     //margin-top: 100px;
-//     //-webkit-transform: translateX(-50%);
-//     //transform: translateX(-50%);
-//     transform: translateY(-85px);
-//     pointer-events: all;
-//     height: fit-content;
-//     width:550px;
-//     .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;
-//     }
-// }
-
-// .tooltipExtras {
-//     position: absolute;
-//     z-index: 20000;
-//     background: #121721;
-//     border: 1px solid silver;
-//     border-radius: 15px;
-//     //height: 60px;
-//     //padding: 2px 10px;
-//     //margin-top: 100px;
-//     //-webkit-transform: translateX(-50%);
-//     //transform: translateX(-50%);
-//     transform: translateY(-115px);
-//     pointer-events: all;
-//     height: 25px;
-//     width:fit-content;
-//     .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;
-//     }
-// }
-
-// .wrapper {
-//     position: absolute;
-//     pointer-events: all;
-// }
-
-//   .menuicon {
-//     display: inline-block;
-//     border-right: 1px solid white(0, 0, 0, 0.2);
-//     //color: rgb(19, 18, 18);
-//     color: rgb(226, 21, 21);
-//     line-height: 1;
-//     padding: 0px 2px;
-//     margin: 1px;
-//     cursor: pointer;
-//     text-align: center;
-//     min-width: 10px;
-    
-//   }
-//   .strong, .heading { font-weight: bold; }
-//   .em { font-style: italic; }
-//   .underline {text-decoration: underline}
-//   .superscript {vertical-align:super}
-//   .subscript { vertical-align:sub }
-//   .strikethrough {text-decoration-line:line-through}
-//   .font-size-indicator {
-//       font-size: 12px;
-//       padding-right: 0px;
-//   }
-//   .summarize{
-//       color: white;
-//       height: 20px;
-//       text-align: center;
-//   }
-
-//   .brush{
-//     display: inline-block;
-//     width: 1em;
-//     height: 1em;
-//     stroke-width: 0;
-//     stroke: currentColor;
-//     fill: currentColor;
-//     margin-right: 15px;
-//   }
-
-//   .brush-active{
-//     display: inline-block;
-//     width: 1em;
-//     height: 1em;
-//     stroke-width: 3;
-//     stroke: greenyellow;
-//     fill: greenyellow;
-//     margin-right: 15px;
-//   }
-
-//   .dragger{
-//       color: #eee;
-//       margin-left: 5px;
-//   }
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx
index 564d9f0df..6001f9840 100644
--- a/src/client/util/TooltipTextMenu.tsx
+++ b/src/client/util/TooltipTextMenu.tsx
@@ -30,9 +30,8 @@ export class TooltipTextMenu {
 
     public static Toolbar: HTMLDivElement | undefined;
 
-    // editor state
+    // editor state properties
     private view: EditorView;
-    // private editorProps: FieldViewProps & FormattedTextBoxProps | undefined;
     private fontStyles: MarkType[] = [];
     private fontSizes: MarkType[] = [];
     private listTypes: (NodeType | any)[] = [];
@@ -42,13 +41,16 @@ export class TooltipTextMenu {
     private _activeMarks: Mark[] = [];
     private _marksToDoms: Map<Mark, HTMLSpanElement> = new Map();
     private _collapsed: boolean = false;
-    //private link: HTMLAnchorElement;
 
     // editor doms
     public tooltip: HTMLElement = document.createElement("div");
     private wrapper: HTMLDivElement = document.createElement("div");
 
     // editor button doms
+    private colorDom?: Node;
+    private colorDropdownDom?: Node;
+    private highlightDom?: Node;
+    private highlightDropdownDom?: Node;
     private linkEditor?: HTMLDivElement;
     private linkText?: HTMLDivElement;
     private linkDrag?: HTMLImageElement;
@@ -58,42 +60,32 @@ export class TooltipTextMenu {
     private fontSizeDom?: Node;
     private fontStyleDom?: Node;
     private listTypeBtnDom?: Node;
-    private colorDom?: Node;
-    private colorDropdownDom?: Node;
-    private highlightDom?: Node;
-    private highlightDropdownDom?: Node;
-
-
-    // private _collapseBtn?: MenuItem;
-    // private _brushIsEmpty: boolean = true;
-
+    private basicTools?: HTMLElement;
 
 
     constructor(view: EditorView) {
         this.view = view;
 
-        // // replace old active menu with this
-        // if (TooltipTextMenuManager.Instance.activeMenu) {
-        //     TooltipTextMenuManager.Instance.activeMenu.wrapper.remove();
-        // }
-        // TooltipTextMenuManager.Instance.activeMenu = this;
-
-        // initialize the tooltip
-        this.createTooltip(view);
+        // initialize the tooltip -- sets this.tooltip
+        this.initTooltip(view);
 
         // initialize the wrapper
         this.wrapper = document.createElement("div");
         this.wrapper.className = "wrapper";
         this.wrapper.appendChild(this.tooltip);
 
-        // positioning?
+        // initialize the dragger -- appends it to the wrapper
+        this.createDragger();
+
         TooltipTextMenu.Toolbar = this.wrapper;
     }
 
-    private async createTooltip(view: EditorView) {
+    private async initTooltip(view: EditorView) {
         // initialize tooltip dom
         this.tooltip = document.createElement("div");
         this.tooltip.className = "tooltipMenu";
+        this.basicTools = document.createElement("div");
+        this.basicTools.className = "basic-tools";
 
         // init buttons to the tooltip -- paths to svgs are obtained from fontawesome
         let items = [
@@ -113,12 +105,15 @@ export class TooltipTextMenu {
             switch (dom.title) {
                 case "Bold":
                     this._marksToDoms.set(schema.mark(schema.marks.strong), dom);
+                    this.basicTools && this.basicTools.appendChild(dom.cloneNode(true));
                     break;
                 case "Italic":
                     this._marksToDoms.set(schema.mark(schema.marks.em), dom);
+                    this.basicTools && this.basicTools.appendChild(dom.cloneNode(true));
                     break;
                 case "Underline":
                     this._marksToDoms.set(schema.mark(schema.marks.underline), dom);
+                    this.basicTools && this.basicTools.appendChild(dom.cloneNode(true));
                     break;
             }
 
@@ -172,19 +167,12 @@ export class TooltipTextMenu {
         this.tooltip.appendChild(this._brushDropdownDom);
 
         // star
-        // this.tooltip.appendChild(this.createLink().render(this.view).dom);
         this.tooltip.appendChild(this.createStar().render(this.view).dom);
 
-        //
+        // list types dropdown
         this.updateListItemDropdown(":", this.listTypeBtnDom);
 
-        //
         await this.updateFromDash(view, undefined, undefined);
-        // TooltipTextMenu.Toolbar = this.wrapper;
-
-        // dragger
-        // TODO: onclick handler in drag handles collapsing
-        this.createDragger();
     }
 
     initFontStyles() {
@@ -224,13 +212,15 @@ export class TooltipTextMenu {
         this.listTypes = Array.from(this.listTypeToIcon.keys());
     }
 
+    // creates dragger element that allows dragging and collapsing (on double click) 
+    // of editor and appends it to the wrapper
     createDragger() {
-        const dragger = document.createElement("div");
-        dragger.className = "dragger";
-
         let draggerWrapper = document.createElement("div");
         draggerWrapper.className = "dragger-wrapper";
 
+        let dragger = document.createElement("div");
+        dragger.className = "dragger";
+
         let line1 = document.createElement("span");
         line1.className = "dragger-line";
         let line2 = document.createElement("span");
@@ -238,14 +228,72 @@ export class TooltipTextMenu {
         let line3 = document.createElement("span");
         line3.className = "dragger-line";
 
-        draggerWrapper.appendChild(line1);
-        draggerWrapper.appendChild(line2);
-        draggerWrapper.appendChild(line3);
+        dragger.appendChild(line1);
+        dragger.appendChild(line2);
+        dragger.appendChild(line3);
 
-        dragger.appendChild(draggerWrapper);
+        draggerWrapper.appendChild(dragger);
 
-        this.tooltip.appendChild(dragger);
-        this.dragElement(dragger);
+        this.wrapper.appendChild(draggerWrapper);
+        this.dragElement(draggerWrapper);
+    }
+
+    dragElement(elmnt: HTMLElement) {
+        var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
+        if (elmnt) {
+            // if present, the header is where you move the DIV from:
+            elmnt.onpointerdown = dragMouseDown;
+            elmnt.ondblclick = onClick;
+        }
+        const self = this;
+
+        function dragMouseDown(e: PointerEvent) {
+            e = e || window.event;
+            //e.preventDefault();
+            // get the mouse cursor position at startup:
+            pos3 = e.clientX;
+            pos4 = e.clientY;
+            document.onpointerup = closeDragElement;
+            // call a function whenever the cursor moves:
+            document.onpointermove = elementDrag;
+        }
+
+        function onClick(e: MouseEvent) {
+            self._collapsed = !self._collapsed;
+            const children = self.wrapper.childNodes;
+            if (self._collapsed && children.length > 0) {
+                self.wrapper.removeChild(self.tooltip);
+                self.basicTools && self.wrapper.prepend(self.basicTools);
+            }
+            else {
+                self.wrapper.prepend(self.tooltip);
+                self.basicTools && self.wrapper.removeChild(self.basicTools);
+            }
+        }
+
+        function elementDrag(e: PointerEvent) {
+            e = e || window.event;
+            //e.preventDefault();
+            // calculate the new cursor position:
+            pos1 = pos3 - e.clientX;
+            pos2 = pos4 - e.clientY;
+            pos3 = e.clientX;
+            pos4 = e.clientY;
+            // set the element's new position:
+            // elmnt.style.top = (elmnt.offsetTop - pos2) + "px";
+            // elmnt.style.left = (elmnt.offsetLeft - pos1) + "px";
+
+            self.wrapper.style.top = (self.wrapper.offsetTop - pos2) + "px";
+            self.wrapper.style.left = (self.wrapper.offsetLeft - pos1) + "px";
+        }
+
+        function closeDragElement() {
+            // stop moving when mouse button is released:
+            document.onpointerup = null;
+            document.onpointermove = null;
+            //self.highlightSearchTerms(self.state, ["hello"]);
+            //FormattedTextBox.Instance.unhighlightSearchTerms();
+        }
     }
 
     //label of dropdown will change to given label
@@ -261,7 +309,7 @@ export class TooltipTextMenu {
 
         let newfontSizeDom = (new Dropdown(cut(fontSizeBtns), {
             label: label,
-            css: "color:black; min-width: 60px; padding-left: 5px; margin-right: 0;"
+            css: "color:black; min-width: 60px;"
         }) as MenuItem).render(this.view).dom;
         if (this.fontSizeDom) { this.tooltip.replaceChild(newfontSizeDom, this.fontSizeDom); }
         else {
@@ -270,8 +318,6 @@ export class TooltipTextMenu {
         this.fontSizeDom = newfontSizeDom;
     }
 
-    // Make the DIV element draggable
-
     //label of dropdown will change to given label
     updateFontStyleDropdown(label: string) {
         //filtering function - might be unecessary
@@ -285,7 +331,7 @@ export class TooltipTextMenu {
 
         let newfontStyleDom = (new Dropdown(cut(fontBtns), {
             label: label,
-            css: "color:black; width: 125px; margin-left: -3px; padding-left: 2px;"
+            css: "color:black; width: 125px;"
         }) as MenuItem).render(this.view).dom;
         if (this.fontStyleDom) { this.tooltip.replaceChild(newfontStyleDom, this.fontStyleDom); }
         else {
@@ -299,11 +345,7 @@ export class TooltipTextMenu {
         if (!this.linkEditor || !this.linkText) {
             this.linkEditor = document.createElement("div");
             this.linkEditor.className = "ProseMirror-icon menuicon";
-            // this.linkEditor.style.color = "black";
             this.linkText = document.createElement("div");
-            // this.linkText.style.cssFloat = "left";
-            // this.linkText.style.marginRight = "5px";
-            // this.linkText.style.marginLeft = "5px";
             this.linkText.setAttribute("contenteditable", "true");
             this.linkText.style.whiteSpace = "nowrap";
             this.linkText.style.width = "150px";
@@ -391,13 +433,11 @@ export class TooltipTextMenu {
                 e.preventDefault();
             }
         };
-        // this.tooltip.appendChild(this.linkEditor);
     }
 
     async getTextLinkTargetTitle() {
         let node = this.view.state.selection.$from.nodeAfter;
         let link = node && node.marks.find(m => m.type.name === "link");
-        // let href = link!.attrs.href;
         if (link) {
             let href = link.attrs.href;
             if (href) {
@@ -445,7 +485,6 @@ export class TooltipTextMenu {
 
                 input.type = "text";
                 input.placeholder = "Enter URL";
-                console.log(targetTitle);
                 if (targetTitle) input.value = targetTitle;
                 input.onclick = (e: MouseEvent) => {
                     input.select();
@@ -514,62 +553,6 @@ export class TooltipTextMenu {
         return linkDropdown;
     }
 
-    dragElement(elmnt: HTMLElement) {
-        var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
-        if (elmnt) {
-            // if present, the header is where you move the DIV from:
-            elmnt.onpointerdown = dragMouseDown;
-            elmnt.ondblclick = onClick;
-        }
-        const self = this;
-
-        function dragMouseDown(e: PointerEvent) {
-            e = e || window.event;
-            //e.preventDefault();
-            // get the mouse cursor position at startup:
-            pos3 = e.clientX;
-            pos4 = e.clientY;
-            document.onpointerup = closeDragElement;
-            // call a function whenever the cursor moves:
-            document.onpointermove = elementDrag;
-        }
-
-        function onClick(e: MouseEvent) {
-            self._collapsed = !self._collapsed;
-            const children = self.wrapper.childNodes;
-            if (self._collapsed && children.length > 1) {
-                self.wrapper.removeChild(self.tooltip);
-            }
-            else {
-                self.wrapper.appendChild(self.tooltip);
-            }
-        }
-
-        function elementDrag(e: PointerEvent) {
-            e = e || window.event;
-            //e.preventDefault();
-            // calculate the new cursor position:
-            pos1 = pos3 - e.clientX;
-            pos2 = pos4 - e.clientY;
-            pos3 = e.clientX;
-            pos4 = e.clientY;
-            // set the element's new position:
-            // elmnt.style.top = (elmnt.offsetTop - pos2) + "px";
-            // elmnt.style.left = (elmnt.offsetLeft - pos1) + "px";
-
-            self.wrapper.style.top = (self.wrapper.offsetTop - pos2) + "px";
-            self.wrapper.style.left = (self.wrapper.offsetLeft - pos1) + "px";
-        }
-
-        function closeDragElement() {
-            // stop moving when mouse button is released:
-            document.onpointerup = null;
-            document.onpointermove = null;
-            //self.highlightSearchTerms(self.state, ["hello"]);
-            //FormattedTextBox.Instance.unhighlightSearchTerms();
-        }
-    }
-
     // makeLinkWithState = (state: EditorState, target: string, location: string) => {
     //     let link = state.schema.mark(state.schema.marks.link, { href: target, location: location });
     // }
@@ -616,10 +599,70 @@ export class TooltipTextMenu {
                 }
             }
         }
+    }
 
-
+    deleteLinkItem() {
+        const icon = {
+            height: 16, width: 16,
+            path: "M15.898,4.045c-0.271-0.272-0.713-0.272-0.986,0l-4.71,4.711L5.493,4.045c-0.272-0.272-0.714-0.272-0.986,0s-0.272,0.714,0,0.986l4.709,4.711l-4.71,4.711c-0.272,0.271-0.272,0.713,0,0.986c0.136,0.136,0.314,0.203,0.492,0.203c0.179,0,0.357-0.067,0.493-0.203l4.711-4.711l4.71,4.711c0.137,0.136,0.314,0.203,0.494,0.203c0.178,0,0.355-0.067,0.492-0.203c0.273-0.273,0.273-0.715,0-0.986l-4.711-4.711l4.711-4.711C16.172,4.759,16.172,4.317,15.898,4.045z"
+        };
+        return new MenuItem({
+            title: "Delete Link",
+            label: "X",
+            icon: icon,
+            css: "color: red",
+            class: "summarize",
+            execEvent: "",
+            run: (state, dispatch) => {
+                this.deleteLink();
+            }
+        });
     }
 
+    createLink() {
+        let markType = schema.marks.link;
+        return new MenuItem({
+            title: "Add or remove link",
+            label: "Add or remove link",
+            execEvent: "",
+            icon: icons.link,
+            css: "color:white;",
+            class: "menuicon",
+            enable(state) { return !state.selection.empty; },
+            run: (state, dispatch, view) => {
+                // to remove link
+                let curLink = "";
+                if (this.markActive(state, markType)) {
+
+                    let { from, $from, to, empty } = state.selection;
+                    let node = state.doc.nodeAt(from);
+                    node && node.marks.map(m => {
+                        m.type === markType && (curLink = m.attrs.href);
+                    });
+                    //toggleMark(markType)(state, dispatch);
+                    //return true;
+                }
+                // to create link
+                openPrompt({
+                    title: "Create a link",
+                    fields: {
+                        href: new TextField({
+                            value: curLink,
+                            label: "Link Target",
+                            required: true
+                        }),
+                        title: new TextField({ label: "Title" })
+                    },
+                    callback(attrs: any) {
+                        toggleMark(markType, attrs)(view.state, view.dispatch);
+                        view.focus();
+                    },
+                    flyout_top: 0,
+                    flyout_left: 0
+                });
+            }
+        });
+    }
 
     //will display a remove-list-type button if selection is in list, otherwise will show list type dropdown
     updateListItemDropdown(label: string, listTypeBtn: any) {
@@ -644,108 +687,6 @@ export class TooltipTextMenu {
         return listTypeBtn;
     }
 
-    //for a specific grouping of marks (passed in), remove all and apply the passed-in one to the selected text
-    changeToMarkInGroup = (markType: MarkType | undefined, view: EditorView, fontMarks: MarkType[]) => {
-        let { $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;
-                    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 of ranges) {
-                        if (has) {
-                            toggleMark(type)(view.state, view.dispatch, view);
-                        }
-                    }
-                }
-            }
-        });
-
-        if (markType) {
-            // fontsize
-            if (markType.name[0] === 'p') {
-                let size = this.fontSizeToNum.get(markType);
-                if (size) { this.updateFontSizeDropdown(String(size) + " pt"); }
-                if (this.editorProps) {
-                    let ruleProvider = this.editorProps.ruleProvider;
-                    let heading = NumCast(this.editorProps.Document.heading);
-                    if (ruleProvider && heading) {
-                        ruleProvider["ruleSize_" + heading] = size;
-                    }
-                }
-            }
-            else {
-                let fontName = this.fontStylesToName.get(markType);
-                if (fontName) { this.updateFontStyleDropdown(fontName); }
-                if (this.editorProps) {
-                    let ruleProvider = this.editorProps.ruleProvider;
-                    let heading = NumCast(this.editorProps.Document.heading);
-                    if (ruleProvider && heading) {
-                        ruleProvider["ruleFont_" + heading] = fontName;
-                    }
-                }
-            }
-            //actually apply font
-            if ((view.state.selection as any).node && (view.state.selection as any).node.type === view.state.schema.nodes.ordered_list) {
-                let status = updateBullets(view.state.tr.setNodeMarkup(view.state.selection.from, (view.state.selection as any).node.type,
-                    { ...(view.state.selection as NodeSelection).node.attrs, setFontFamily: markType.name, setFontSize: Number(markType.name.replace(/p/, "")) }), view.state.schema);
-                view.dispatch(status.setSelection(new NodeSelection(status.doc.resolve(view.state.selection.from))));
-            }
-            else toggleMark(markType)(view.state, view.dispatch, view);
-        }
-    }
-
-    //remove all node typeand apply the passed-in one to the selected text
-    changeToNodeType = (nodeType: NodeType | undefined, view: EditorView) => {
-        //remove oldif (nodeType) { //add new
-        if (nodeType === schema.nodes.bullet_list) {
-            wrapInList(nodeType)(view.state, view.dispatch);
-        } else {
-            var marks = view.state.storedMarks || (view.state.selection.$to.parentOffset && view.state.selection.$from.marks());
-            if (!wrapInList(schema.nodes.ordered_list)(view.state, (tx2: any) => {
-                let tx3 = updateBullets(tx2, schema, (nodeType as any).attrs.mapStyle);
-                marks && tx3.ensureMarks([...marks]);
-                marks && tx3.setStoredMarks([...marks]);
-
-                view.dispatch(tx2);
-            })) {
-                let tx2 = view.state.tr;
-                let tx3 = nodeType ? updateBullets(tx2, schema, (nodeType as any).attrs.mapStyle) : tx2;
-                marks && tx3.ensureMarks([...marks]);
-                marks && tx3.setStoredMarks([...marks]);
-
-                view.dispatch(tx3);
-            }
-        }
-    }
-
-    //makes a button for the drop down FOR MARKS
-    //css is the style you want applied to the button
-    dropdownMarkBtn(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() { return true; },
-            run() {
-                changeToMarkInGroup(markType, view, groupMarks);
-            }
-        });
-    }
-
     createStar() {
         return new MenuItem({
             title: "Summarize",
@@ -772,6 +713,17 @@ export class TooltipTextMenu {
         return true;
     }
 
+    public static insertComment(state: EditorState<any>, dispatch: any) {
+        if (state.selection.empty) return false;
+        let mark = state.schema.marks.highlight.create();
+        let tr = state.tr;
+        tr.addMark(state.selection.from, state.selection.to, mark);
+        let content = tr.selection.content();
+        let newNode = state.schema.nodes.star.create({ visibility: false, text: content, textslice: content.toJSON() });
+        dispatch && dispatch(tr.replaceSelectionWith(newNode).removeMark(tr.selection.from - 1, tr.selection.from, mark));
+        return true;
+    }
+
     createHighlightTool() {
         return new MenuItem({
             title: "Highlight",
@@ -975,24 +927,6 @@ export class TooltipTextMenu {
         return colorDropdown;
     }
 
-    deleteLinkItem() {
-        const icon = {
-            height: 16, width: 16,
-            path: "M15.898,4.045c-0.271-0.272-0.713-0.272-0.986,0l-4.71,4.711L5.493,4.045c-0.272-0.272-0.714-0.272-0.986,0s-0.272,0.714,0,0.986l4.709,4.711l-4.71,4.711c-0.272,0.271-0.272,0.713,0,0.986c0.136,0.136,0.314,0.203,0.492,0.203c0.179,0,0.357-0.067,0.493-0.203l4.711-4.711l4.71,4.711c0.137,0.136,0.314,0.203,0.494,0.203c0.178,0,0.355-0.067,0.492-0.203c0.273-0.273,0.273-0.715,0-0.986l-4.711-4.711l4.711-4.711C16.172,4.759,16.172,4.317,15.898,4.045z"
-        };
-        return new MenuItem({
-            title: "Delete Link",
-            label: "X",
-            icon: icon,
-            css: "color: red",
-            class: "summarize",
-            execEvent: "",
-            run: (state, dispatch) => {
-                this.deleteLink();
-            }
-        });
-    }
-
     createBrush(active: boolean = false) {
         const icon = {
             height: 32, width: 32,
@@ -1020,8 +954,6 @@ export class TooltipTextMenu {
         });
     }
 
-    // selectionchanged event handler
-
     brush_function(state: EditorState<any>, dispatch: any) {
         if (TooltipTextMenuManager.Instance._brushIsEmpty) {
             const selected_marks = this.getMarksInSelection(this.view.state);
@@ -1055,8 +987,6 @@ export class TooltipTextMenu {
                 }
             }
         }
-
-
     }
 
     createBrushDropdown(active: boolean = false) {
@@ -1120,88 +1050,106 @@ export class TooltipTextMenu {
         return brushDom;
     }
 
-    // createCollapse() {
-    //     this._collapseBtn = new MenuItem({
-    //         title: "Collapse",
-    //         //label: "Collapse",
-    //         icon: icons.join,
-    //         execEvent: "",
-    //         css: "color:white;",
-    //         class: "summarize",
-    //         run: () => {
-    //             this.collapseToolTip();
-    //         }
-    //     });
-    // }
 
-    // collapseToolTip() {
-    //     if (this._collapseBtn) {
-    //         if (this._collapseBtn.spec.title === "Collapse") {
-    //             // const newcollapseBtn = new MenuItem({
-    //             //     title: "Expand",
-    //             //     icon: icons.join,
-    //             //     execEvent: "",
-    //             //     css: "color:white;",
-    //             //     class: "summarize",
-    //             //     run: (state, dispatch, view) => {
-    //             //         this.collapseToolTip();
-    //             //     }
-    //             // });
-    //             // this.tooltip.replaceChild(newcollapseBtn.render(this.view).dom, this._collapseBtn.render(this.view).dom);
-    //             // this._collapseBtn = newcollapseBtn;
-    //             this.tooltip.style.width = "30px";
-    //             this._collapseBtn.spec.title = "Expand";
-    //             this._collapseBtn.render(this.view);
-    //         }
-    //         else {
-    //             this._collapseBtn.spec.title = "Collapse";
-    //             this.tooltip.style.width = "550px";
-    //             this._collapseBtn.render(this.view);
-    //         }
-    //     }
-    // }
 
-    createLink() {
-        let markType = schema.marks.link;
-        return new MenuItem({
-            title: "Add or remove link",
-            label: "Add or remove link",
-            execEvent: "",
-            icon: icons.link,
-            css: "color:white;",
-            class: "menuicon",
-            enable(state) { return !state.selection.empty; },
-            run: (state, dispatch, view) => {
-                // to remove link
-                let curLink = "";
-                if (this.markActive(state, markType)) {
+    //for a specific grouping of marks (passed in), remove all and apply the passed-in one to the selected text
+    changeToMarkInGroup = (markType: MarkType | undefined, view: EditorView, fontMarks: MarkType[]) => {
+        let { $cursor, ranges } = view.state.selection as TextSelection;
+        let state = view.state;
+        let dispatch = view.dispatch;
 
-                    let { from, $from, to, empty } = state.selection;
-                    let node = state.doc.nodeAt(from);
-                    node && node.marks.map(m => {
-                        m.type === markType && (curLink = m.attrs.href);
-                    });
-                    //toggleMark(markType)(state, dispatch);
-                    //return true;
+        //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;
+                    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 of ranges) {
+                        if (has) {
+                            toggleMark(type)(view.state, view.dispatch, view);
+                        }
+                    }
                 }
-                // to create link
-                openPrompt({
-                    title: "Create a link",
-                    fields: {
-                        href: new TextField({
-                            value: curLink,
-                            label: "Link Target",
-                            required: true
-                        }),
-                        title: new TextField({ label: "Title" })
-                    },
-                    callback(attrs: any) {
-                        toggleMark(markType, attrs)(view.state, view.dispatch);
-                        view.focus();
-                    },
-                    flyout_top: 0,
-                    flyout_left: 0
-                });
+            }
+        });
+
+        if (markType) {
+            // fontsize
+            if (markType.name[0] === 'p') {
+                let size = this.fontSizeToNum.get(markType);
+                if (size) { this.updateFontSizeDropdown(String(size) + " pt"); }
+                if (this.editorProps) {
+                    let ruleProvider = this.editorProps.ruleProvider;
+                    let heading = NumCast(this.editorProps.Document.heading);
+                    if (ruleProvider && heading) {
+                        ruleProvider["ruleSize_" + heading] = size;
+                    }
+                }
+            }
+            else {
+                let fontName = this.fontStylesToName.get(markType);
+                if (fontName) { this.updateFontStyleDropdown(fontName); }
+                if (this.editorProps) {
+                    let ruleProvider = this.editorProps.ruleProvider;
+                    let heading = NumCast(this.editorProps.Document.heading);
+                    if (ruleProvider && heading) {
+                        ruleProvider["ruleFont_" + heading] = fontName;
+                    }
+                }
+            }
+            //actually apply font
+            if ((view.state.selection as any).node && (view.state.selection as any).node.type === view.state.schema.nodes.ordered_list) {
+                let status = updateBullets(view.state.tr.setNodeMarkup(view.state.selection.from, (view.state.selection as any).node.type,
+                    { ...(view.state.selection as NodeSelection).node.attrs, setFontFamily: markType.name, setFontSize: Number(markType.name.replace(/p/, "")) }), view.state.schema);
+                view.dispatch(status.setSelection(new NodeSelection(status.doc.resolve(view.state.selection.from))));
+            }
+            else toggleMark(markType)(view.state, view.dispatch, view);
+        }
+    }
+
+    //remove all node typeand apply the passed-in one to the selected text
+    changeToNodeType = (nodeType: NodeType | undefined, view: EditorView) => {
+        //remove oldif (nodeType) { //add new
+        if (nodeType === schema.nodes.bullet_list) {
+            wrapInList(nodeType)(view.state, view.dispatch);
+        } else {
+            var marks = view.state.storedMarks || (view.state.selection.$to.parentOffset && view.state.selection.$from.marks());
+            if (!wrapInList(schema.nodes.ordered_list)(view.state, (tx2: any) => {
+                let tx3 = updateBullets(tx2, schema, (nodeType as any).attrs.mapStyle);
+                marks && tx3.ensureMarks([...marks]);
+                marks && tx3.setStoredMarks([...marks]);
+
+                view.dispatch(tx2);
+            })) {
+                let tx2 = view.state.tr;
+                let tx3 = nodeType ? updateBullets(tx2, schema, (nodeType as any).attrs.mapStyle) : tx2;
+                marks && tx3.ensureMarks([...marks]);
+                marks && tx3.setStoredMarks([...marks]);
+
+                view.dispatch(tx3);
+            }
+        }
+    }
+
+    //makes a button for the drop down FOR MARKS
+    //css is the style you want applied to the button
+    dropdownMarkBtn(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: "dropdown-item",
+            css: css,
+            enable() { return true; },
+            run() {
+                changeToMarkInGroup(markType, view, groupMarks);
             }
         });
     }
@@ -1213,7 +1161,7 @@ export class TooltipTextMenu {
             title: "",
             label: label,
             execEvent: "",
-            class: "menuicon",
+            class: "dropdown-item",
             css: css,
             enable() { return true; },
             run() {
@@ -1390,35 +1338,12 @@ export class TooltipTextMenu {
 
     update_mark_doms() {
         this.reset_mark_doms();
-        let foundlink = false;
-        // let children = this.extras.childNodes;
         this._activeMarks.forEach((mark) => {
             if (this._marksToDoms.has(mark)) {
                 let dom = this._marksToDoms.get(mark);
                 if (dom) dom.style.color = "greenyellow";
             }
-            // if (children.length > 1) {
-            //     foundlink = true;
-            // }
-            // if (mark.type.name === "link" && children.length === 1) {
-            //     // let del = document.createElement("button");
-            //     // del.textContent = "X";
-            //     // del.style.color = "red";
-            //     // del.style.height = "10px";
-            //     // del.style.width = "10px";
-            //     // del.style.marginLeft = "5px";
-            //     // del.onclick = this.deleteLink;
-            //     // this.extras.appendChild(del);
-            //     let del = this.deleteLinkItem().render(this.view).dom;
-            //     this.extras.appendChild(del);
-            //     foundlink = true;
-            // }
         });
-        // if (!foundlink) {
-        //     if (children.length > 1) {
-        //         this.extras.removeChild(children[1]);
-        //     }
-        // }
 
         // keeps brush tool highlighted if active when switching between textboxes
         if (!TooltipTextMenuManager.Instance._brushIsEmpty) {
@@ -1535,30 +1460,11 @@ class TooltipTextMenuManager {
         return TooltipTextMenuManager._instance;
     }
 
-    // private pinnedToUnpinned() {
-    //     let position = MainOverlayTextBox.Instance.position;
-
-    //     this.unpinnedX = this.pinnedX - position[0];
-    //     this.unpinnedY = this.pinnedY - position[1];
-    // }
-
-    // private unpinnedToPinned() {
-    //     let position = MainOverlayTextBox.Instance.position;
-
-    //     this.pinnedX = position[0] + this.unpinnedX;
-    //     this.pinnedY = position[1] + this.unpinnedY;
-    // }
-
     public get isPinned() {
         return this._isPinned;
     }
 
     public toggleIsPinned() {
-        // if (this._isPinned) {
-        //     this.pinnedToUnpinned();
-        // } else {
-        //     this.unpinnedToPinned();
-        // }
         this._isPinned = !this._isPinned;
     }
 }
-- 
cgit v1.2.3-70-g09d2


From 2ab22e64e9b95e6b76566c493ae90f5951475e72 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Fri, 15 Nov 2019 15:11:26 -0500
Subject: fixed naming of 'marker' mark

---
 src/client/util/RichTextSchema.tsx  | 2 +-
 src/client/util/TooltipTextMenu.tsx | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

(limited to 'src/client/util/RichTextSchema.tsx')

diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx
index 0e8c9f14c..0a717dff1 100644
--- a/src/client/util/RichTextSchema.tsx
+++ b/src/client/util/RichTextSchema.tsx
@@ -326,7 +326,7 @@ export const marks: { [index: string]: MarkSpec } = {
         }
     },
 
-    highlight2: {
+    marker: {
         attrs: {
             highlight: { default: "transparent" }
         },
diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx
index 14f8b2bd4..5136089b3 100644
--- a/src/client/util/TooltipTextMenu.tsx
+++ b/src/client/util/TooltipTextMenu.tsx
@@ -735,7 +735,7 @@ export class TooltipTextMenu {
     public static insertHighlight(color: String, state: EditorState<any>, dispatch: any) {
         if (state.selection.empty) return false;
 
-        let highlightMark = state.schema.mark(state.schema.marks.highlight2, { highlight: color });
+        let highlightMark = state.schema.mark(state.schema.marks.marker, { highlight: color });
         dispatch(state.tr.addMark(state.selection.from, state.selection.to, highlightMark));
     }
 
@@ -1223,7 +1223,7 @@ export class TooltipTextMenu {
         });
     }
 
-    markActive = function (state: EditorState<any>, type: MarkType<Schema<string, string>>) {
+    markActive = function(state: EditorState<any>, type: MarkType<Schema<string, string>>) {
         let { from, $from, to, empty } = state.selection;
         if (empty) return type.isInSet(state.storedMarks || $from.marks());
         else return state.doc.rangeHasMark(from, to, type);
-- 
cgit v1.2.3-70-g09d2

