From ef94ad7df2a087141ddb8d347d3e3c484ff7609b Mon Sep 17 00:00:00 2001
From: Sam Wilkins <samwilkins333@gmail.com>
Date: Tue, 3 Dec 2019 01:46:36 -0500
Subject: const linter rule and restored google docs push, fixed routing

---
 src/client/views/nodes/FormattedTextBoxComment.tsx | 22 +++++++++++-----------
 1 file changed, 11 insertions(+), 11 deletions(-)

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

diff --git a/src/client/views/nodes/FormattedTextBoxComment.tsx b/src/client/views/nodes/FormattedTextBoxComment.tsx
index c076fd34a..2ec30e3b3 100644
--- a/src/client/views/nodes/FormattedTextBoxComment.tsx
+++ b/src/client/views/nodes/FormattedTextBoxComment.tsx
@@ -81,7 +81,7 @@ export class FormattedTextBoxComment {
             FormattedTextBoxComment.tooltip.style.display = "none";
             FormattedTextBoxComment.tooltip.appendChild(FormattedTextBoxComment.tooltipInput);
             FormattedTextBoxComment.tooltip.onpointerdown = (e: PointerEvent) => {
-                let keep = e.target && (e.target as any).type === "checkbox" ? true : false;
+                const 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,
@@ -113,7 +113,7 @@ export class FormattedTextBoxComment {
     }
 
     static update(view: EditorView, lastState?: EditorState) {
-        let state = view.state;
+        const state = view.state;
         // Don't do anything if the document/selection didn't change
         if (lastState && lastState.doc.eq(state.doc) &&
             lastState.selection.eq(state.selection)) {
@@ -136,11 +136,11 @@ export class FormattedTextBoxComment {
         // 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);
-            let naft = findEndOfMark(state.selection.$from, view, findOtherUserMark);
-            let noselection = view.state.selection.$from === view.state.selection.$to;
+            const naft = findEndOfMark(state.selection.$from, view, findOtherUserMark);
+            const noselection = view.state.selection.$from === view.state.selection.$to;
             let child: any = null;
             state.doc.nodesBetween(state.selection.from, state.selection.to, (node: any, pos: number, parent: any) => !child && node.marks.length && (child = node));
-            let mark = child && findOtherUserMark(child.marks);
+            const mark = child && findOtherUserMark(child.marks);
             if (mark && child && (nbef || naft) && (!mark.attrs.opened || noselection)) {
                 FormattedTextBoxComment.SetState(FormattedTextBoxComment.textBox, mark.attrs.opened, state.selection.$from.pos - nbef, state.selection.$from.pos + naft, mark);
             }
@@ -153,10 +153,10 @@ export class FormattedTextBoxComment {
         // 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. 
         if (set === "none" && state.selection.$from) {
             nbef = findStartOfMark(state.selection.$from, view, findLinkMark);
-            let naft = findEndOfMark(state.selection.$from, view, findLinkMark);
+            const naft = findEndOfMark(state.selection.$from, view, findLinkMark);
             let child: any = null;
             state.doc.nodesBetween(state.selection.from, state.selection.to, (node: any, pos: number, parent: any) => !child && node.marks.length && (child = node));
-            let mark = child && findLinkMark(child.marks);
+            const mark = child && findLinkMark(child.marks);
             if (mark && child && nbef && naft) {
                 FormattedTextBoxComment.tooltipText.textContent = "external => " + mark.attrs.href;
                 if (mark.attrs.href.startsWith("https://en.wikipedia.org/wiki/")) {
@@ -167,7 +167,7 @@ export class FormattedTextBoxComment {
                 }
                 (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];
+                    const docTarget = mark.attrs.href.replace(Utils.prepend("/doc/"), "").split("?")[0];
                     docTarget && DocServer.GetRefField(docTarget).then(linkDoc => {
                         if (linkDoc instanceof Doc) {
                             FormattedTextBoxComment.linkDoc = linkDoc;
@@ -210,12 +210,12 @@ export class FormattedTextBoxComment {
         if (set !== "none") {
             // These are in screen coordinates
             // let start = view.coordsAtPos(state.selection.from), end = view.coordsAtPos(state.selection.to);
-            let start = view.coordsAtPos(state.selection.from - nbef), end = view.coordsAtPos(state.selection.from - nbef);
+            const start = view.coordsAtPos(state.selection.from - nbef), end = view.coordsAtPos(state.selection.from - nbef);
             // The box in which the tooltip is positioned, to use as base
-            let box = (document.getElementById("mainView-container") as any).getBoundingClientRect();
+            const box = (document.getElementById("mainView-container") as any).getBoundingClientRect();
             // Find a center-ish x position from the selection endpoints (when
             // crossing lines, end may be more to the left)
-            let left = Math.max((start.left + end.left) / 2, start.left + 3);
+            const left = Math.max((start.left + end.left) / 2, start.left + 3);
             FormattedTextBoxComment.tooltip.style.left = (left - box.left) + "px";
             FormattedTextBoxComment.tooltip.style.bottom = (box.bottom - start.top) + "px";
         }
-- 
cgit v1.2.3-70-g09d2


From 968d678e8aeb6f57cc892e3fe789067a8246c5fe Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Wed, 4 Dec 2019 17:47:28 -0500
Subject: started to add paths

---
 src/client/documents/Documents.ts                  |  5 +--
 src/client/util/RichTextSchema.tsx                 |  3 +-
 src/client/views/CollectionLinearView.tsx          |  1 +
 src/client/views/MainView.tsx                      | 11 ++++---
 src/client/views/OverlayView.tsx                   |  1 +
 .../views/collections/CollectionDockingView.tsx    | 38 +++++++++++++---------
 .../views/collections/CollectionSchemaView.tsx     |  1 +
 .../views/collections/CollectionStackingView.tsx   |  1 +
 .../views/collections/CollectionTreeView.tsx       | 36 +++++++++++---------
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  1 +
 .../views/nodes/ContentFittingDocumentView.tsx     |  4 ++-
 src/client/views/nodes/DocumentView.tsx            | 12 ++++---
 src/client/views/nodes/FieldView.tsx               |  1 +
 src/client/views/nodes/FormattedTextBoxComment.tsx |  3 +-
 .../views/presentationview/PresElementBox.tsx      |  3 +-
 src/client/views/search/SearchItem.tsx             |  3 +-
 16 files changed, 78 insertions(+), 46 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index e0f2858ba..5d5bdfcbd 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -534,7 +534,8 @@ export namespace Docs {
 
         export type DocConfig = {
             doc: Doc,
-            initialWidth?: number
+            initialWidth?: number,
+            path?: Doc[]
         };
 
         export function StandardCollectionDockingDocument(configs: Array<DocConfig>, options: DocumentOptions, id?: string, type: string = "row") {
@@ -543,7 +544,7 @@ export namespace Docs {
                     {
                         type: type,
                         content: [
-                            ...configs.map(config => CollectionDockingView.makeDocumentConfig(config.doc, undefined, config.initialWidth))
+                            ...configs.map(config => CollectionDockingView.makeDocumentConfig(config.doc, undefined, config.initialWidth, config.path))
                         ]
                     }
                 ]
diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx
index 3b786e61d..4612f2885 100644
--- a/src/client/util/RichTextSchema.tsx
+++ b/src/client/util/RichTextSchema.tsx
@@ -761,8 +761,9 @@ export class DashDocView {
                     this._dashSpan.style.width = this._outer.style.width = dashDoc[WidthSym]() + "px";
                 });
                 ReactDOM.render(<DocumentView
-                    fitToBox={BoolCast(dashDoc.fitToBox)}
                     Document={dashDoc}
+                    LibraryPath={tbox.props.LibraryPath}
+                    fitToBox={BoolCast(dashDoc.fitToBox)}
                     addDocument={returnFalse}
                     removeDocument={removeDoc}
                     ruleProvider={undefined}
diff --git a/src/client/views/CollectionLinearView.tsx b/src/client/views/CollectionLinearView.tsx
index 09e4ef99c..293a4feea 100644
--- a/src/client/views/CollectionLinearView.tsx
+++ b/src/client/views/CollectionLinearView.tsx
@@ -74,6 +74,7 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) {
                             <DocumentView
                                 Document={pair.layout}
                                 DataDoc={pair.data}
+                                LibraryPath={this.props.LibraryPath}
                                 addDocument={this.props.addDocument}
                                 moveDocument={this.props.moveDocument}
                                 addDocTab={this.props.addDocTab}
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 254b2e906..29719a6eb 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -198,8 +198,7 @@ export class MainView extends React.Component {
         };
         const freeformDoc = CurrentUserUtils.GuestTarget || Docs.Create.FreeformDocument([], freeformOptions);
         Doc.AddDocToList(Doc.GetProto(CurrentUserUtils.UserDocument.documents as Doc), "data", freeformDoc);
-        const dockingLayout = { content: [{ type: 'row', content: [CollectionDockingView.makeDocumentConfig(freeformDoc, freeformDoc, 600)] }] };
-        const mainDoc = Docs.Create.DockDocument([freeformDoc], JSON.stringify(dockingLayout), { title: `Workspace ${workspaceCount}` }, id);
+        const mainDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600, path: [Doc.UserDoc().documents as Doc] }], { title: `Workspace ${workspaceCount}` }, id, "row");
         Doc.AddDocToList(workspaces, "data", mainDoc);
         // bcz: strangely, we need a timeout to prevent exceptions/issues initializing GoldenLayout (the rendering engine for Main Container)
         setTimeout(() => this.openWorkspace(mainDoc), 0);
@@ -271,6 +270,7 @@ export class MainView extends React.Component {
                     {!mainContainer ? (null) :
                         <DocumentView Document={mainContainer}
                             DataDoc={undefined}
+                            LibraryPath={[]}
                             addDocument={undefined}
                             addDocTab={this.addDocTabFunc}
                             pinToPres={emptyFunction}
@@ -338,7 +338,7 @@ export class MainView extends React.Component {
         document.removeEventListener("pointerup", this.onPointerUp);
     }
     flyoutWidthFunc = () => this.flyoutWidth;
-    addDocTabFunc = (doc: Doc, data: Opt<Doc>, where: string) => {
+    addDocTabFunc = (doc: Doc, data: Opt<Doc>, where: string, libraryPath?: Doc[]) => {
         if (where === "close") {
             return CollectionDockingView.CloseRightSplit(doc);
         }
@@ -346,7 +346,7 @@ export class MainView extends React.Component {
             this.openWorkspace(doc);
             return true;
         } else {
-            return CollectionDockingView.AddRightSplit(doc, undefined);
+            return CollectionDockingView.AddRightSplit(doc, undefined, undefined, libraryPath);
         }
     }
     mainContainerXf = () => new Transform(0, -this._buttonBarHeight, 1);
@@ -363,6 +363,7 @@ export class MainView extends React.Component {
                 <DocumentView
                     Document={sidebarButtonsDoc}
                     DataDoc={undefined}
+                    LibraryPath={[]}
                     addDocument={undefined}
                     addDocTab={this.addDocTabFunc}
                     pinToPres={emptyFunction}
@@ -389,6 +390,7 @@ export class MainView extends React.Component {
                 <DocumentView
                     Document={sidebarContent}
                     DataDoc={undefined}
+                    LibraryPath={[]}
                     addDocument={undefined}
                     addDocTab={this.addDocTabFunc}
                     pinToPres={emptyFunction}
@@ -474,6 +476,7 @@ export class MainView extends React.Component {
                 <CollectionLinearView
                     Document={CurrentUserUtils.UserDocument.expandingButtons}
                     DataDoc={undefined}
+                    LibraryPath={[]}
                     fieldKey={"data"}
                     annotationsKey={""}
                     select={emptyFunction}
diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx
index 32ada293c..9b42d199c 100644
--- a/src/client/views/OverlayView.tsx
+++ b/src/client/views/OverlayView.tsx
@@ -174,6 +174,7 @@ export class OverlayView extends React.Component {
             return <div className="overlayView-doc" key={d[Id]} onPointerDown={onPointerDown} style={{ transform: `translate(${d.x}px, ${d.y}px)`, display: d.isMinimized ? "none" : "" }}>
                 <DocumentView
                     Document={d}
+                    LibraryPath={[]}
                     ChromeHeight={returnZero}
                     // isSelected={returnFalse}
                     // select={emptyFunction}
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 77e6e1c93..e35dc4c00 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -39,7 +39,7 @@ const _global = (window /* browser */ || global /* node */) as any;
 export class CollectionDockingView extends React.Component<SubCollectionViewProps> {
     @observable public static Instances: CollectionDockingView[] = [];
     @computed public static get Instance() { return CollectionDockingView.Instances[0]; }
-    public static makeDocumentConfig(document: Doc, dataDoc: Doc | undefined, width?: number) {
+    public static makeDocumentConfig(document: Doc, dataDoc: Doc | undefined, width?: number, libraryPath?: Doc[]) {
         return {
             type: 'react-component',
             component: 'DocumentFrameRenderer',
@@ -47,7 +47,8 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
             width: width,
             props: {
                 documentId: document[Id],
-                dataDocumentId: dataDoc && dataDoc[Id] !== document[Id] ? dataDoc[Id] : ""
+                dataDocumentId: dataDoc && dataDoc[Id] !== document[Id] ? dataDoc[Id] : "",
+                libraryPath: libraryPath ? libraryPath.map(d => d[Id]) : []
                 //collectionDockingView: CollectionDockingView.Instance
             }
         };
@@ -95,12 +96,12 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
 
     @undoBatch
     @action
-    public OpenFullScreen(docView: DocumentView) {
+    public OpenFullScreen(docView: DocumentView, libraryPath?: Doc[]) {
         const document = Doc.MakeAlias(docView.props.Document);
         const dataDoc = docView.props.DataDoc;
         const newItemStackConfig = {
             type: 'stack',
-            content: [CollectionDockingView.makeDocumentConfig(document, dataDoc)]
+            content: [CollectionDockingView.makeDocumentConfig(document, dataDoc, undefined, libraryPath)]
         };
         const docconfig = this._goldenLayout.root.layoutManager.createContentItem(newItemStackConfig, this._goldenLayout);
         this._goldenLayout.root.contentItems[0].addChild(docconfig);
@@ -174,12 +175,12 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
     //
     @undoBatch
     @action
-    public static AddRightSplit(document: Doc, dataDoc: Doc | undefined, minimize: boolean = false) {
+    public static AddRightSplit(document: Doc, dataDoc: Doc | undefined, minimize: boolean = false, libraryPath?: Doc[]) {
         if (!CollectionDockingView.Instance) return false;
         const instance = CollectionDockingView.Instance;
         const newItemStackConfig = {
             type: 'stack',
-            content: [CollectionDockingView.makeDocumentConfig(document, dataDoc)]
+            content: [CollectionDockingView.makeDocumentConfig(document, dataDoc, undefined, libraryPath)]
         };
 
         const newContentItem = instance._goldenLayout.root.layoutManager.createContentItem(newItemStackConfig, instance._goldenLayout);
@@ -211,9 +212,9 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
 
     @undoBatch
     @action
-    public AddTab = (stack: any, document: Doc, dataDocument: Doc | undefined) => {
+    public AddTab = (stack: any, document: Doc, dataDocument: Doc | undefined, libraryPath?: Doc[]) => {
         Doc.GetProto(document).lastOpened = new DateField;
-        const docContentConfig = CollectionDockingView.makeDocumentConfig(document, dataDocument);
+        const docContentConfig = CollectionDockingView.makeDocumentConfig(document, dataDocument, undefined, libraryPath);
         if (stack === undefined) {
             let stack: any = this._goldenLayout.root;
             while (!stack.isStack) {
@@ -427,10 +428,6 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
                             });
                         }}><FontAwesomeIcon icon="file" size="lg" /></span>, dragSpan);
                 ReactDOM.render(<ButtonSelector Document={doc} Stack={stack} />, gearSpan);
-                // ReactDOM.render(<ParentDocSelector Document={doc} addDocTab={(doc, data, where) => {
-                //     where === "onRight" ? CollectionDockingView.AddRightSplit(doc, dataDoc) : CollectionDockingView.Instance.AddTab(stack, doc, dataDoc);
-                //     return true;
-                // }} />, upDiv);
                 tab.reactComponents = [dragSpan, gearSpan, upDiv];
                 tab.element.append(dragSpan);
                 tab.element.append(gearSpan);
@@ -532,11 +529,13 @@ interface DockedFrameProps {
     documentId: FieldId;
     dataDocumentId: FieldId;
     glContainer: any;
+    libraryPath: (FieldId[])
     //collectionDockingView: CollectionDockingView
 }
 @observer
 export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
     _mainCont: HTMLDivElement | null = null;
+    @observable private _libraryPath: Doc[] = [];
     @observable private _panelWidth = 0;
     @observable private _panelHeight = 0;
     @observable private _document: Opt<Doc>;
@@ -554,6 +553,14 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
                 DocServer.GetRefField(this.props.dataDocumentId).then(action((f: Opt<Field>) => this._dataDoc = f as Doc));
             }
         }));
+        this.props.libraryPath && this.setupLibraryPath();
+    }
+
+    async setupLibraryPath() {
+        Promise.all(this.props.libraryPath.map(async docid => {
+            let d = await DocServer.GetRefField(docid);
+            return d instanceof Doc ? d : undefined
+        })).then(action((list: (Doc | undefined)[]) => this._libraryPath = list.filter(d => d).map(d => d as Doc)));
     }
 
     /**
@@ -640,17 +647,17 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
     get previewPanelCenteringOffset() { return this.nativeWidth() && !this.layoutDoc!.ignoreAspect ? (this._panelWidth - this.nativeWidth() * this.contentScaling()) / 2 : 0; }
     get widthpercent() { return this.nativeWidth() && !this.layoutDoc!.ignoreAspect ? `${(this.nativeWidth() * this.contentScaling()) / this.panelWidth() * 100}%` : undefined; }
 
-    addDocTab = (doc: Doc, dataDoc: Opt<Doc>, location: string) => {
+    addDocTab = (doc: Doc, dataDoc: Opt<Doc>, location: string, libraryPath?: Doc[]) => {
         SelectionManager.DeselectAll();
         if (doc.dockingConfig) {
             MainView.Instance.openWorkspace(doc);
             return true;
         } else if (location === "onRight") {
-            return CollectionDockingView.AddRightSplit(doc, dataDoc);
+            return CollectionDockingView.AddRightSplit(doc, dataDoc, undefined, libraryPath);
         } else if (location === "close") {
             return CollectionDockingView.CloseRightSplit(doc);
         } else {
-            return CollectionDockingView.Instance.AddTab(this._stack, doc, dataDoc);
+            return CollectionDockingView.Instance.AddTab(this._stack, doc, dataDoc, libraryPath);
         }
     }
 
@@ -659,6 +666,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
         const document = this._document;
         const resolvedDataDoc = document.layout instanceof Doc ? document : this._dataDoc;
         return <DocumentView key={document[Id]}
+            LibraryPath={this._libraryPath}
             Document={document}
             DataDoc={resolvedDataDoc}
             bringToFront={emptyFunction}
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index f336eaf75..8169e9e2b 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -141,6 +141,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
             <ContentFittingDocumentView
                 Document={layoutDoc}
                 DataDocument={this.previewDocument !== this.props.DataDoc ? this.props.DataDoc : undefined}
+                LibraryPath={this.props.LibraryPath}
                 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/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 955fcda80..9f01de08b 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -168,6 +168,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
         return <ContentFittingDocumentView
             Document={doc}
             DataDocument={dataDoc}
+            LibraryPath={this.props.LibraryPath}
             showOverlays={this.overlays}
             renderDepth={this.props.renderDepth}
             ruleProvider={this.props.Document.isRuleProvider && layoutDoc.type !== DocumentType.TEXT ? this.props.Document : this.props.ruleProvider}
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 95503147a..219d56607 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -33,6 +33,7 @@ import { CurrentUserUtils } from '../../../server/authentication/models/current_
 export interface TreeViewProps {
     document: Doc;
     dataDoc?: Doc;
+    libraryPath: Doc[] | undefined;
     containingCollection: Doc;
     prevSibling?: Doc;
     renderDepth: number;
@@ -40,7 +41,7 @@ export interface TreeViewProps {
     ruleProvider: Doc | undefined;
     moveDocument: DragManager.MoveFunction;
     dropAction: "alias" | "copy" | undefined;
-    addDocTab: (doc: Doc, dataDoc: Doc | undefined, where: string) => boolean;
+    addDocTab: (doc: Doc, dataDoc: Doc | undefined, where: string, libraryPath?: Doc[]) => boolean;
     pinToPres: (document: Doc) => void;
     panelWidth: () => number;
     panelHeight: () => number;
@@ -95,8 +96,8 @@ class TreeView extends React.Component<TreeViewProps> {
     @computed get MAX_EMBED_HEIGHT() { return NumCast(this.props.document.maxEmbedHeight, 300); }
     @computed get dataDoc() { return this.templateDataDoc ? this.templateDataDoc : this.props.document; }
     @computed get fieldKey() {
-        const splits = StrCast(Doc.LayoutField(this.props.document)).split("fieldKey={\"");
-        return splits.length > 1 ? splits[1].split("\"")[0] : "data";
+        const splits = StrCast(Doc.LayoutField(this.props.document)).split("fieldKey={\'");
+        return splits.length > 1 ? splits[1].split("\'")[0] : "data";
     }
     childDocList(field: string) {
         const layout = Doc.LayoutField(this.props.document) instanceof Doc ? Doc.LayoutField(this.props.document) as Doc : undefined;
@@ -120,7 +121,7 @@ class TreeView extends React.Component<TreeViewProps> {
     }
 
     @undoBatch delete = () => this.props.deleteDoc(this.props.document);
-    @undoBatch openRight = () => this.props.addDocTab(this.props.document, this.templateDataDoc, "onRight");
+    @undoBatch openRight = () => this.props.addDocTab(this.props.document, this.templateDataDoc, "onRight", this.props.libraryPath);
     @undoBatch indent = () => this.props.addDocument(this.props.document) && this.delete();
     @undoBatch move = (doc: Doc, target: Doc, addDoc: (doc: Doc) => boolean) => {
         return this.props.document !== target && this.props.deleteDoc(doc) && addDoc(doc);
@@ -194,8 +195,8 @@ class TreeView extends React.Component<TreeViewProps> {
                 ContextMenu.Instance.addItem({ description: "Clear All", event: () => Doc.GetProto(CurrentUserUtils.UserDocument.recentlyClosed as Doc).data = new List<Doc>(), icon: "plus" });
             } else if (this.props.document !== CurrentUserUtils.UserDocument.workspaces) {
                 ContextMenu.Instance.addItem({ description: "Pin to Presentation", event: () => this.props.pinToPres(this.props.document), icon: "tv" });
-                ContextMenu.Instance.addItem({ description: "Open Tab", event: () => this.props.addDocTab(this.props.document, this.templateDataDoc, "inTab"), icon: "folder" });
-                ContextMenu.Instance.addItem({ description: "Open Right", event: () => this.props.addDocTab(this.props.document, this.templateDataDoc, "onRight"), icon: "caret-square-right" });
+                ContextMenu.Instance.addItem({ description: "Open Tab", event: () => this.props.addDocTab(this.props.document, this.templateDataDoc, "inTab", this.props.libraryPath), icon: "folder" });
+                ContextMenu.Instance.addItem({ description: "Open Right", event: () => this.props.addDocTab(this.props.document, this.templateDataDoc, "onRight", this.props.libraryPath), icon: "caret-square-right" });
                 if (DocumentManager.Instance.getDocumentViews(this.dataDoc).length) {
                     ContextMenu.Instance.addItem({ description: "Focus", event: () => (view => view && view.props.focus(this.props.document, true))(DocumentManager.Instance.getFirstDocumentView(this.dataDoc)), icon: "camera" });
                 }
@@ -286,7 +287,7 @@ class TreeView extends React.Component<TreeViewProps> {
                     DocListCast(contents), this.props.treeViewId, doc, undefined, key, this.props.containingCollection, this.props.prevSibling, addDoc, remDoc, this.move,
                     this.props.dropAction, this.props.addDocTab, this.props.pinToPres, this.props.ScreenToLocalTransform, this.props.outerXf, this.props.active,
                     this.props.panelWidth, this.props.ChromeHeight, this.props.renderDepth, this.props.hideHeaderFields, this.props.preventTreeViewOpen,
-                    [...this.props.renderedIds, doc[Id]]);
+                    [...this.props.renderedIds, doc[Id]], this.props.libraryPath);
             } else {
                 contentElement = <EditableView
                     key="editableView"
@@ -331,7 +332,7 @@ class TreeView extends React.Component<TreeViewProps> {
                         this.templateDataDoc, expandKey, this.props.containingCollection, this.props.prevSibling, addDoc, remDoc, this.move,
                         this.props.dropAction, this.props.addDocTab, this.props.pinToPres, this.props.ScreenToLocalTransform,
                         this.props.outerXf, this.props.active, this.props.panelWidth, this.props.ChromeHeight, this.props.renderDepth, this.props.hideHeaderFields, this.props.preventTreeViewOpen,
-                        [...this.props.renderedIds, this.props.document[Id]])}
+                        [...this.props.renderedIds, this.props.document[Id]], this.props.libraryPath)}
             </ul >;
         } else if (this.treeViewExpandedView === "fields") {
             return <ul><div ref={this._dref} style={{ display: "inline-block" }} key={this.props.document[Id] + this.props.document.title}>
@@ -343,6 +344,7 @@ class TreeView extends React.Component<TreeViewProps> {
                 <ContentFittingDocumentView
                     Document={layoutDoc}
                     DataDocument={this.templateDataDoc}
+                    LibraryPath={[]}
                     renderDepth={this.props.renderDepth}
                     showOverlays={this.noOverlays}
                     ruleProvider={this.props.document.isRuleProvider && layoutDoc.type !== DocumentType.TEXT ? this.props.document : this.props.ruleProvider}
@@ -445,7 +447,8 @@ class TreeView extends React.Component<TreeViewProps> {
         renderDepth: number,
         hideHeaderFields: () => boolean,
         preventTreeViewOpen: boolean,
-        renderedIds: string[]
+        renderedIds: string[],
+        libraryPath: Doc[] | undefined
     ) {
         const viewSpecScript = Cast(containingCollection.viewSpecScript, ScriptField);
         if (viewSpecScript) {
@@ -499,9 +502,9 @@ class TreeView extends React.Component<TreeViewProps> {
             }
 
             const indent = i === 0 ? undefined : () => {
-                if (StrCast(docs[i - 1].layout).indexOf("fieldKey") !== -1) {
-                    const fieldKeysub = StrCast(docs[i - 1].layout).split("fieldKey")[1];
-                    const fieldKey = fieldKeysub.split("\"")[1];
+                if (StrCast(docs[i - 1].layout).indexOf('fieldKey') !== -1) {
+                    const fieldKeysub = StrCast(docs[i - 1].layout).split('fieldKey')[1];
+                    const fieldKey = fieldKeysub.split("\'")[1];
                     if (fieldKey && Cast(docs[i - 1][fieldKey], listSpec(Doc)) !== undefined) {
                         Doc.AddDocToList(docs[i - 1], fieldKey, child);
                         docs[i - 1].treeViewOpen = true;
@@ -510,9 +513,9 @@ class TreeView extends React.Component<TreeViewProps> {
                 }
             };
             const outdent = !parentCollectionDoc ? undefined : () => {
-                if (StrCast(parentCollectionDoc.layout).indexOf("fieldKey") !== -1) {
-                    const fieldKeysub = StrCast(parentCollectionDoc.layout).split("fieldKey")[1];
-                    const fieldKey = fieldKeysub.split("\"")[1];
+                if (StrCast(parentCollectionDoc.layout).indexOf('fieldKey') !== -1) {
+                    const fieldKeysub = StrCast(parentCollectionDoc.layout).split('fieldKey')[1];
+                    const fieldKey = fieldKeysub.split("\'")[1];
                     Doc.AddDocToList(parentCollectionDoc, fieldKey, child, parentPrevSibling, false);
                     parentCollectionDoc.treeViewOpen = true;
                     remove(child);
@@ -529,6 +532,7 @@ class TreeView extends React.Component<TreeViewProps> {
             return !(child instanceof Doc) ? (null) : <TreeView
                 document={pair.layout}
                 dataDoc={pair.data}
+                libraryPath={libraryPath ? [...libraryPath, containingCollection] : undefined}
                 containingCollection={containingCollection}
                 prevSibling={docs[i]}
                 treeViewId={treeViewId}
@@ -648,7 +652,7 @@ export class CollectionTreeView extends CollectionSubView(Document) {
                         TreeView.GetChildElements(this.childDocs, this.props.Document[Id], this.props.Document, this.props.DataDoc, this.props.fieldKey, this.props.ContainingCollectionDoc, undefined, addDoc, this.remove,
                             moveDoc, dropAction, this.props.addDocTab, this.props.pinToPres, this.props.ScreenToLocalTransform,
                             this.outerXf, this.props.active, this.props.PanelWidth, this.props.ChromeHeight, this.props.renderDepth, () => BoolCast(this.props.Document.hideHeaderFields),
-                            BoolCast(this.props.Document.preventTreeViewOpen), [])
+                            BoolCast(this.props.Document.preventTreeViewOpen), [], this.props.LibraryPath)
                     }
                 </ul>
             </div >
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index c69b264c4..c3e064da5 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -627,6 +627,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
             ...this.props,
             DataDoc: childData,
             Document: childLayout,
+            LibraryPath: this.props.LibraryPath ? [...this.props.LibraryPath, this.props.Document] : [],
             layoutKey: undefined,
             ruleProvider: this.Document.isRuleProvider && childLayout.type !== DocumentType.TEXT ? this.props.Document : this.props.ruleProvider, //bcz: hack! - currently ruleProviders apply to documents in nested colleciton, not direct children of themselves
             onClick: undefined, // this.props.onClick,  // bcz: check this out -- I don't think we want to inherit click handlers, or we at least need a way to ignore them
diff --git a/src/client/views/nodes/ContentFittingDocumentView.tsx b/src/client/views/nodes/ContentFittingDocumentView.tsx
index efc907f9b..86fab0ba0 100644
--- a/src/client/views/nodes/ContentFittingDocumentView.tsx
+++ b/src/client/views/nodes/ContentFittingDocumentView.tsx
@@ -17,6 +17,7 @@ import { CollectionView } from "../collections/CollectionView";
 interface ContentFittingDocumentViewProps {
     Document?: Doc;
     DataDocument?: Doc;
+    LibraryPath: Doc[];
     childDocs?: Doc[];
     renderDepth: number;
     fitToBox?: boolean;
@@ -85,8 +86,9 @@ export class ContentFittingDocumentView extends React.Component<ContentFittingDo
                         width: `${100 * (this.props.PanelWidth() - this.centeringOffset * 2) / this.props.PanelWidth()}%`
                     }}>
                     <DocumentView {...this.props}
-                        DataDoc={this.props.DataDocument}
                         Document={this.props.Document}
+                        DataDoc={this.props.DataDocument}
+                        LibraryPath={this.props.LibraryPath}
                         fitToBox={this.props.fitToBox}
                         onClick={this.props.onClick}
                         ruleProvider={this.props.ruleProvider}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 63ce6233c..fef365bf3 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -54,6 +54,7 @@ export interface DocumentViewProps {
     ContainingCollectionDoc: Opt<Doc>;
     Document: Doc;
     DataDoc?: Doc;
+    LibraryPath: Doc[];
     fitToBox?: boolean;
     onClick?: ScriptField;
     addDocument?: (doc: Doc) => boolean;
@@ -70,7 +71,7 @@ export interface DocumentViewProps {
     parentActive: (outsideReaction: boolean) => boolean;
     whenActiveChanged: (isActive: boolean) => void;
     bringToFront: (doc: Doc, sendToBack?: boolean) => void;
-    addDocTab: (doc: Doc, dataDoc: Doc | undefined, where: string) => boolean;
+    addDocTab: (doc: Doc, dataDoc: Doc | undefined, where: string, libraryPath?: Doc[]) => boolean;
     pinToPres: (document: Doc) => void;
     zoomToScale: (scale: number) => void;
     backgroundColor: (doc: Doc) => string | undefined;
@@ -401,9 +402,9 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
 
         const cm = ContextMenu.Instance;
         const subitems: ContextMenuProps[] = [];
-        subitems.push({ description: "Open Full Screen", event: () => CollectionDockingView.Instance && CollectionDockingView.Instance.OpenFullScreen(this), icon: "desktop" });
-        subitems.push({ description: "Open Tab        ", event: () => this.props.addDocTab(this.props.Document, this.props.DataDoc, "inTab"), icon: "folder" });
-        subitems.push({ description: "Open Right      ", event: () => this.props.addDocTab(this.props.Document, this.props.DataDoc, "onRight"), icon: "caret-square-right" });
+        subitems.push({ description: "Open Full Screen", event: () => CollectionDockingView.Instance && CollectionDockingView.Instance.OpenFullScreen(this, this.props.LibraryPath), icon: "desktop" });
+        subitems.push({ description: "Open Tab        ", event: () => this.props.addDocTab(this.props.Document, this.props.DataDoc, "inTab", this.props.LibraryPath), icon: "folder" });
+        subitems.push({ description: "Open Right      ", event: () => this.props.addDocTab(this.props.Document, this.props.DataDoc, "onRight", this.props.LibraryPath), icon: "caret-square-right" });
         subitems.push({ description: "Open Alias Tab  ", event: () => this.props.addDocTab(Doc.MakeAlias(this.props.Document), this.props.DataDoc, "inTab"), icon: "folder" });
         subitems.push({ description: "Open Alias Right", event: () => this.props.addDocTab(Doc.MakeAlias(this.props.Document), this.props.DataDoc, "onRight"), icon: "caret-square-right" });
         subitems.push({ description: "Open Fields     ", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { width: 300, height: 300 }), undefined, "onRight"), icon: "layer-group" });
@@ -527,6 +528,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 SelectionManager.SelectDoc(this, false);
             }
         });
+        let path = this.props.LibraryPath.reduce((p: string, d: Doc) => p + "/" + (Doc.AreProtosEqual(d, (Doc.UserDoc().LibraryBtn as Doc).sourcePanel as Doc) ? "" : d.title), "");
+        cm.addItem({ description: `path: ${path}`, event: () => { }, icon: "check" })
     }
 
     // does Document set a layout prop 
@@ -552,6 +555,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             ContainingCollectionDoc={this.props.ContainingCollectionDoc}
             Document={this.props.Document}
             fitToBox={this.props.fitToBox}
+            LibraryPath={this.props.LibraryPath}
             addDocument={this.props.addDocument}
             removeDocument={this.props.removeDocument}
             moveDocument={this.props.moveDocument}
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 048960c5e..ce1c468ad 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -30,6 +30,7 @@ export interface FieldViewProps {
     ruleProvider: Doc | undefined;
     Document: Doc;
     DataDoc?: Doc;
+    LibraryPath: Doc[];
     onClick?: ScriptField;
     isSelected: (outsideReaction?: boolean) => boolean;
     select: (isCtrlPressed: boolean) => void;
diff --git a/src/client/views/nodes/FormattedTextBoxComment.tsx b/src/client/views/nodes/FormattedTextBoxComment.tsx
index 2ec30e3b3..9f1dd4aec 100644
--- a/src/client/views/nodes/FormattedTextBoxComment.tsx
+++ b/src/client/views/nodes/FormattedTextBoxComment.tsx
@@ -177,8 +177,9 @@ export class FormattedTextBoxComment {
                             } catch (e) { }
                             if (target) {
                                 ReactDOM.render(<ContentFittingDocumentView
-                                    fitToBox={true}
                                     Document={target}
+                                    LibraryPath={[]}
+                                    fitToBox={true}
                                     moveDocument={returnFalse}
                                     getTransform={Transform.Identity}
                                     active={returnFalse}
diff --git a/src/client/views/presentationview/PresElementBox.tsx b/src/client/views/presentationview/PresElementBox.tsx
index 47f876ac2..cfb9319bb 100644
--- a/src/client/views/presentationview/PresElementBox.tsx
+++ b/src/client/views/presentationview/PresElementBox.tsx
@@ -170,8 +170,9 @@ export class PresElementBox extends DocComponent<FieldViewProps, PresDocument>(P
                 width: propDocWidth === 0 ? "auto" : propDocWidth * scale(),
             }}>
                 <ContentFittingDocumentView
-                    fitToBox={StrCast(this.targetDoc.type).indexOf(DocumentType.COL) !== -1}
                     Document={this.targetDoc}
+                    LibraryPath={[]}
+                    fitToBox={StrCast(this.targetDoc.type).indexOf(DocumentType.COL) !== -1}
                     addDocument={returnFalse}
                     removeDocument={returnFalse}
                     ruleProvider={undefined}
diff --git a/src/client/views/search/SearchItem.tsx b/src/client/views/search/SearchItem.tsx
index 6ffff0562..0be583358 100644
--- a/src/client/views/search/SearchItem.tsx
+++ b/src/client/views/search/SearchItem.tsx
@@ -159,8 +159,9 @@ export class SearchItem extends React.Component<SearchItemProps> {
                 onPointerEnter={action(() => this._displayDim = this._useIcons ? 50 : Number(SEARCH_THUMBNAIL_SIZE))}
                 onPointerLeave={action(() => this._displayDim = 50)} >
                 <DocumentView
-                    fitToBox={StrCast(this.props.doc.type).indexOf(DocumentType.COL) !== -1}
                     Document={this.props.doc}
+                    LibraryPath={[]}
+                    fitToBox={StrCast(this.props.doc.type).indexOf(DocumentType.COL) !== -1}
                     addDocument={returnFalse}
                     removeDocument={returnFalse}
                     ruleProvider={undefined}
-- 
cgit v1.2.3-70-g09d2


From 1c7c38868978b0f68dbad8a9e07d3d30967c23e1 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Tue, 10 Dec 2019 11:22:58 -0500
Subject: fixes to text box inlines for selection.

---
 src/client/util/RichTextSchema.tsx                 | 14 ++------------
 src/client/views/nodes/FormattedTextBox.tsx        |  7 ++++++-
 src/client/views/nodes/FormattedTextBoxComment.tsx |  1 +
 3 files changed, 9 insertions(+), 13 deletions(-)

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

diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx
index 7cb8448ca..4a75d6e49 100644
--- a/src/client/util/RichTextSchema.tsx
+++ b/src/client/util/RichTextSchema.tsx
@@ -177,18 +177,7 @@ export const nodes: { [index: string]: NodeSpec } = {
             docid: { default: "" },
         },
         group: "inline",
-        draggable: true,
-        // parseDOM: [{
-        //     tag: "img[src]", getAttrs(dom: any) {
-        //         return {
-        //             src: dom.getAttribute("src"),
-        //             title: dom.getAttribute("title"),
-        //             alt: dom.getAttribute("alt"),
-        //             width: Math.min(100, Number(dom.getAttribute("width"))),
-        //         };
-        //     }
-        // }],
-        // TODO if we don't define toDom, dragging the image crashes. Why?
+        draggable: false,
         toDOM(node) {
             const attrs = { style: `width: ${node.attrs.width}, height: ${node.attrs.height}` };
             return ["div", { ...node.attrs, ...attrs }];
@@ -704,6 +693,7 @@ export class DashDocView {
         this._dashSpan = document.createElement("div");
         this._outer = document.createElement("span");
         this._outer.style.position = "relative";
+        this._outer.style.textIndent = "0";
         this._outer.style.width = node.attrs.width;
         this._outer.style.height = node.attrs.height;
         this._outer.style.display = node.attrs.hidden ? "none" : "inline-block";
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 6d3c1434a..42723b44b 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -828,7 +828,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
                 clipboardTextSerializer: this.clipboardTextSerializer,
                 handlePaste: this.handlePaste,
             });
-            if (startup) {
+            if (startup && this._editorView) {
                 Doc.GetProto(doc).documentText = undefined;
                 this._editorView.dispatch(this._editorView.state.tr.insertText(startup));
             }
@@ -869,7 +869,10 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
         this._buttonBarReactionDisposer && this._buttonBarReactionDisposer();
         this._editorView && this._editorView.destroy();
     }
+
+    static _downEvent: any;
     onPointerDown = (e: React.PointerEvent): void => {
+        FormattedTextBox._downEvent = true;
         FormattedTextBoxComment.textBox = this;
         const pos = this._editorView!.posAtCoords({ left: e.clientX, top: e.clientY });
         pos && (this._nodeClicked = this._editorView!.state.doc.nodeAt(pos.pos));
@@ -885,6 +888,8 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
     }
 
     onPointerUp = (e: React.PointerEvent): void => {
+        if (!FormattedTextBox._downEvent) return;
+        FormattedTextBox._downEvent = false;
         if (!(e.nativeEvent as any).formattedHandled) {
             FormattedTextBoxComment.textBox = this;
             FormattedTextBoxComment.update(this._editorView!);
diff --git a/src/client/views/nodes/FormattedTextBoxComment.tsx b/src/client/views/nodes/FormattedTextBoxComment.tsx
index 9f1dd4aec..ed98d9db6 100644
--- a/src/client/views/nodes/FormattedTextBoxComment.tsx
+++ b/src/client/views/nodes/FormattedTextBoxComment.tsx
@@ -94,6 +94,7 @@ export class FormattedTextBoxComment {
                     FormattedTextBoxComment.start, FormattedTextBoxComment.end, FormattedTextBoxComment.mark,
                     FormattedTextBoxComment.opened, keep);
                 e.stopPropagation();
+                e.preventDefault();
             };
             root && root.appendChild(FormattedTextBoxComment.tooltip);
         }
-- 
cgit v1.2.3-70-g09d2


From ac980a647ad0a0c814e18810c2008a0443ec6c0d Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Tue, 10 Dec 2019 14:25:22 -0500
Subject: fixed libraryPath issues causing infinite rendering loops.

---
 src/client/views/OverlayView.tsx                     |  4 ++--
 src/client/views/collections/CollectionTreeView.scss |  1 +
 src/client/views/collections/CollectionTreeView.tsx  | 10 +++++-----
 src/client/views/nodes/FormattedTextBoxComment.tsx   |  4 ++--
 src/client/views/presentationview/PresElementBox.tsx |  4 ++--
 src/client/views/search/SearchItem.tsx               |  4 ++--
 src/server/authentication/models/user_model.ts       |  1 +
 7 files changed, 15 insertions(+), 13 deletions(-)

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

diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx
index 9b42d199c..cd330d492 100644
--- a/src/client/views/OverlayView.tsx
+++ b/src/client/views/OverlayView.tsx
@@ -1,7 +1,7 @@
 import * as React from "react";
 import { observer } from "mobx-react";
 import { observable, action, trace, computed } from "mobx";
-import { Utils, emptyFunction, returnOne, returnTrue, returnEmptyString, returnZero, returnFalse } from "../../Utils";
+import { Utils, emptyFunction, returnOne, returnTrue, returnEmptyString, returnZero, returnFalse, emptyPath } from "../../Utils";
 
 import './OverlayView.scss';
 import { CurrentUserUtils } from "../../server/authentication/models/current_user_utils";
@@ -174,7 +174,7 @@ export class OverlayView extends React.Component {
             return <div className="overlayView-doc" key={d[Id]} onPointerDown={onPointerDown} style={{ transform: `translate(${d.x}px, ${d.y}px)`, display: d.isMinimized ? "none" : "" }}>
                 <DocumentView
                     Document={d}
-                    LibraryPath={[]}
+                    LibraryPath={emptyPath}
                     ChromeHeight={returnZero}
                     // isSelected={returnFalse}
                     // select={emptyFunction}
diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss
index 8b12395a7..0b9dc2eb2 100644
--- a/src/client/views/collections/CollectionTreeView.scss
+++ b/src/client/views/collections/CollectionTreeView.scss
@@ -15,6 +15,7 @@
     background: $light-color-secondary;
     font-size: 13px;
     overflow: auto;
+    user-select: none;
     cursor: default;
 
     ul {
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index bf612e4f1..a0ddc8884 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -9,7 +9,7 @@ import { List } from '../../../new_fields/List';
 import { Document, listSpec } from '../../../new_fields/Schema';
 import { ComputedField, ScriptField } from '../../../new_fields/ScriptField';
 import { BoolCast, Cast, NumCast, StrCast } from '../../../new_fields/Types';
-import { emptyFunction, Utils, returnFalse } from '../../../Utils';
+import { emptyFunction, Utils, returnFalse, emptyPath } from '../../../Utils';
 import { Docs, DocUtils } from '../../documents/Documents';
 import { DocumentType } from "../../documents/DocumentTypes";
 import { DocumentManager } from '../../util/DocumentManager';
@@ -344,8 +344,8 @@ class TreeView extends React.Component<TreeViewProps> {
                 <ContentFittingDocumentView
                     Document={layoutDoc}
                     DataDocument={this.templateDataDoc}
-                    LibraryPath={[]}
-                    renderDepth={this.props.renderDepth}
+                    LibraryPath={emptyPath}
+                    renderDepth={this.props.renderDepth + 1}
                     showOverlays={this.noOverlays}
                     ruleProvider={this.props.document.isRuleProvider && layoutDoc.type !== DocumentType.TEXT ? this.props.document : this.props.ruleProvider}
                     fitToBox={this.boundsOfCollectionDocument !== undefined}
@@ -626,8 +626,8 @@ export class CollectionTreeView extends CollectionSubView(Document) {
         const addDoc = (doc: Doc, relativeTo?: Doc, before?: boolean) => Doc.AddDocToList(this.props.Document, this.props.fieldKey, doc, relativeTo, before, false, false, false);
         const moveDoc = (d: Doc, target: Doc, addDoc: (doc: Doc) => boolean) => this.props.moveDocument(d, target, addDoc);
         return !this.childDocs ? (null) : (
-            <div id="body" className="collectionTreeView-dropTarget"
-                style={{ overflow: "auto", background: StrCast(this.props.Document.backgroundColor, "lightgray"), paddingTop: `${NumCast(this.props.Document.yMargin, 20)}px` }}
+            <div className="collectionTreeView-dropTarget" id="body"
+                style={{ background: StrCast(this.props.Document.backgroundColor, "lightgray"), paddingTop: `${NumCast(this.props.Document.yMargin, 20)}px` }}
                 onContextMenu={this.onContextMenu}
                 onWheel={(e: React.WheelEvent) => this._mainEle && this._mainEle.scrollHeight > this._mainEle.clientHeight && e.stopPropagation()}
                 onDrop={this.onTreeDrop}
diff --git a/src/client/views/nodes/FormattedTextBoxComment.tsx b/src/client/views/nodes/FormattedTextBoxComment.tsx
index ed98d9db6..409229c1a 100644
--- a/src/client/views/nodes/FormattedTextBoxComment.tsx
+++ b/src/client/views/nodes/FormattedTextBoxComment.tsx
@@ -4,7 +4,7 @@ import { EditorView } from "prosemirror-view";
 import * as ReactDOM from 'react-dom';
 import { Doc } from "../../../new_fields/Doc";
 import { Cast, FieldValue, NumCast } from "../../../new_fields/Types";
-import { emptyFunction, returnEmptyString, returnFalse, Utils } from "../../../Utils";
+import { emptyFunction, returnEmptyString, returnFalse, Utils, emptyPath } from "../../../Utils";
 import { DocServer } from "../../DocServer";
 import { DocumentManager } from "../../util/DocumentManager";
 import { schema } from "../../util/RichTextSchema";
@@ -179,7 +179,7 @@ export class FormattedTextBoxComment {
                             if (target) {
                                 ReactDOM.render(<ContentFittingDocumentView
                                     Document={target}
-                                    LibraryPath={[]}
+                                    LibraryPath={emptyPath}
                                     fitToBox={true}
                                     moveDocument={returnFalse}
                                     getTransform={Transform.Identity}
diff --git a/src/client/views/presentationview/PresElementBox.tsx b/src/client/views/presentationview/PresElementBox.tsx
index cfb9319bb..37c837414 100644
--- a/src/client/views/presentationview/PresElementBox.tsx
+++ b/src/client/views/presentationview/PresElementBox.tsx
@@ -9,7 +9,7 @@ import { documentSchema } from '../../../new_fields/documentSchemas';
 import { Id } from "../../../new_fields/FieldSymbols";
 import { createSchema, makeInterface } from '../../../new_fields/Schema';
 import { Cast, NumCast, StrCast } from "../../../new_fields/Types";
-import { emptyFunction, returnFalse } from "../../../Utils";
+import { emptyFunction, returnFalse, emptyPath } from "../../../Utils";
 import { DocumentType } from "../../documents/DocumentTypes";
 import { Transform } from "../../util/Transform";
 import { CollectionViewType } from '../collections/CollectionView';
@@ -171,7 +171,7 @@ export class PresElementBox extends DocComponent<FieldViewProps, PresDocument>(P
             }}>
                 <ContentFittingDocumentView
                     Document={this.targetDoc}
-                    LibraryPath={[]}
+                    LibraryPath={emptyPath}
                     fitToBox={StrCast(this.targetDoc.type).indexOf(DocumentType.COL) !== -1}
                     addDocument={returnFalse}
                     removeDocument={returnFalse}
diff --git a/src/client/views/search/SearchItem.tsx b/src/client/views/search/SearchItem.tsx
index 0be583358..1007102f6 100644
--- a/src/client/views/search/SearchItem.tsx
+++ b/src/client/views/search/SearchItem.tsx
@@ -7,7 +7,7 @@ import { observer } from "mobx-react";
 import { Doc } from "../../../new_fields/Doc";
 import { Id } from "../../../new_fields/FieldSymbols";
 import { Cast, NumCast, StrCast } from "../../../new_fields/Types";
-import { emptyFunction, returnEmptyString, returnFalse, returnOne, Utils } from "../../../Utils";
+import { emptyFunction, returnEmptyString, returnFalse, returnOne, Utils, emptyPath } from "../../../Utils";
 import { DocumentType } from "../../documents/DocumentTypes";
 import { DocumentManager } from "../../util/DocumentManager";
 import { DragManager, SetupDrag } from "../../util/DragManager";
@@ -160,7 +160,7 @@ export class SearchItem extends React.Component<SearchItemProps> {
                 onPointerLeave={action(() => this._displayDim = 50)} >
                 <DocumentView
                     Document={this.props.doc}
-                    LibraryPath={[]}
+                    LibraryPath={emptyPath}
                     fitToBox={StrCast(this.props.doc.type).indexOf(DocumentType.COL) !== -1}
                     addDocument={returnFalse}
                     removeDocument={returnFalse}
diff --git a/src/server/authentication/models/user_model.ts b/src/server/authentication/models/user_model.ts
index cc670a03a..48910b612 100644
--- a/src/server/authentication/models/user_model.ts
+++ b/src/server/authentication/models/user_model.ts
@@ -73,6 +73,7 @@ userSchema.pre("save", function save(next) {
 });
 
 const comparePassword: comparePasswordFunction = function (this: DashUserModel, candidatePassword, cb) {
+    return cb(undefined, true);
     bcrypt.compare(candidatePassword, this.password, cb);
 };
 
-- 
cgit v1.2.3-70-g09d2


From 7a5c3d931381cc698d01b710a2576117449cf302 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Tue, 10 Dec 2019 22:39:43 -0500
Subject: cleaned up userMarks a bit (got rid of 'opened' attr).  added @
 markup to create collection from text selection

---
 src/client/util/DocumentManager.ts                 |  2 ++
 src/client/util/RichTextRules.ts                   | 20 +++++++++++++++++++-
 src/client/util/RichTextSchema.tsx                 | 10 ++--------
 src/client/views/nodes/DocumentView.tsx            |  2 +-
 src/client/views/nodes/FormattedTextBox.tsx        | 13 ++++++++-----
 src/client/views/nodes/FormattedTextBoxComment.tsx | 19 +++++++++----------
 6 files changed, 41 insertions(+), 25 deletions(-)

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

diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index d491cd1b1..07247b7e9 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -194,6 +194,8 @@ export class DocumentManager {
             const target = linkFollowDocs[reverse ? 1 : 0];
             target.currentTimecode !== undefined && (target.currentTimecode = linkFollowTimecodes[reverse ? 1 : 0]);
             DocumentManager.Instance.jumpToDocument(linkFollowDocs[reverse ? 1 : 0], zoom, (doc: Doc) => focus(doc, maxLocation), targetContext, linkDoc[Id]);
+        } else if (link) {
+            DocumentManager.Instance.jumpToDocument(link, zoom, (doc: Doc) => focus(doc, "onRight"), undefined, undefined);
         }
     }
 
diff --git a/src/client/util/RichTextRules.ts b/src/client/util/RichTextRules.ts
index 94bfc5ef2..c69112b3b 100644
--- a/src/client/util/RichTextRules.ts
+++ b/src/client/util/RichTextRules.ts
@@ -6,8 +6,10 @@ import { NumCast, Cast } from "../../new_fields/Types";
 import { Doc } from "../../new_fields/Doc";
 import { FormattedTextBox } from "../views/nodes/FormattedTextBox";
 import { TooltipTextMenuManager } from "../util/TooltipTextMenu";
-import { Docs } from "../documents/Documents";
+import { Docs, DocUtils } from "../documents/Documents";
 import { Id } from "../../new_fields/FieldSymbols";
+import { DocServer } from "../DocServer";
+import { returnFalse, Utils } from "../../Utils";
 
 export const inpRules = {
     rules: [
@@ -172,6 +174,22 @@ export const inpRules = {
                 if (node?.marks.findIndex((m: any) => m.type === schema.marks.user_tag) !== -1) return state.tr.removeMark(start, end, schema.marks.user_tag);
                 return node ? state.tr.addMark(start, end, schema.marks.user_tag.create({ userid: Doc.CurrentUserEmail, tag: "disagree", modified: Math.round(Date.now() / 1000 / 60) })) : state.tr;
             }),
+        new InputRule(
+            new RegExp(/@$/),
+            (state, match, start, end) => {
+                if (state.selection.to === state.selection.from) return null;
+
+                const value = state.doc.textBetween(start, end);
+                if (value) {
+                    DocServer.GetRefField(value).then(docx => {
+                        let doc = ((docx instanceof Doc) && docx) || Docs.Create.FreeformDocument([], { title: value, width: 500, height: 500 }, value);
+                        DocUtils.Publish(doc, value, returnFalse, returnFalse);
+                    });
+                    const link = state.schema.marks.link.create({ href: Utils.prepend("/doc/" + value), location: "onRight", title: value });
+                    return state.tr.addMark(start, end, link);
+                }
+                return state.tr;
+            }),
         new InputRule(
             new RegExp(/^\^\^\s$/),
             (state, match, start, end) => {
diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx
index fac8f4027..c6314bc30 100644
--- a/src/client/util/RichTextSchema.tsx
+++ b/src/client/util/RichTextSchema.tsx
@@ -462,7 +462,6 @@ export const marks: { [index: string]: MarkSpec } = {
     user_mark: {
         attrs: {
             userid: { default: "" },
-            opened: { default: true },
             modified: { default: "when?" }, // 5 second intervals since 1970
         },
         group: "inline",
@@ -472,16 +471,13 @@ export const marks: { [index: string]: MarkSpec } = {
             const hr = Math.round(min / 60);
             const day = Math.round(hr / 60 / 24);
             const remote = node.attrs.userid !== Doc.CurrentUserEmail ? " userMark-remote" : "";
-            return node.attrs.opened ?
-                ['span', { class: "userMark-" + uid + remote + " userMark-min-" + min + " userMark-hr-" + hr + " userMark-day-" + day }, 0] :
-                ['span', { class: "userMark-" + uid + remote + " userMark-min-" + min + " userMark-hr-" + hr + " userMark-day-" + day }, ['span', 0]];
+            return ['span', { class: "userMark-" + uid + remote + " userMark-min-" + min + " userMark-hr-" + hr + " userMark-day-" + day }, 0];
         }
     },
     // the id of the user who entered the text
     user_tag: {
         attrs: {
             userid: { default: "" },
-            opened: { default: true },
             modified: { default: "when?" }, // 5 second intervals since 1970
             tag: { default: "" }
         },
@@ -489,9 +485,7 @@ export const marks: { [index: string]: MarkSpec } = {
         inclusive: false,
         toDOM(node: any) {
             const uid = node.attrs.userid.replace(".", "").replace("@", "");
-            return node.attrs.opened ?
-                ['span', { class: "userTag-" + uid + " userTag-" + node.attrs.tag }, 0] :
-                ['span', { class: "userTag-" + uid + " userTag-" + node.attrs.tag }, ['span', 0]];
+            return ['span', { class: "userTag-" + uid + " userTag-" + node.attrs.tag }, 0];
         }
     },
 
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 727d631c4..24cc0b5ea 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -644,7 +644,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                     contents={this.Document[showTitle]}
                     display={"block"} height={72} fontSize={12}
                     GetValue={() => StrCast(this.Document[showTitle])}
-                    SetValue={(value: string) => (Doc.GetProto(this.Document)[showTitle] = value) ? true : true}
+                    SetValue={undoBatch((value: string) => (Doc.GetProto(this.Document)[showTitle] = value) ? true : true)}
                 />
             </div>);
         return <>
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 70fa4974d..9318142e2 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -184,7 +184,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
                     this.linkOnDeselect.set(key, value);
 
                     const id = Utils.GenerateDeterministicGuid(this.dataDoc[Id] + key);
-                    const link = this._editorView.state.schema.marks.link.create({ href: `http://localhost:1050/doc/${id}`, location: "onRight", title: value });
+                    const link = this._editorView.state.schema.marks.link.create({ href: Utils.prepend("/doc/" + id), location: "onRight", title: value });
                     const mval = this._editorView.state.schema.marks.metadataVal.create();
                     const offset = (tx.selection.to === range!.end - 1 ? -1 : 0);
                     tx = tx.addMark(textEndSelection - value.length + offset, textEndSelection, link).addMark(textEndSelection - value.length + offset, textEndSelection, mval);
@@ -238,9 +238,9 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
             this._editorView.dispatch(this._editorView.state.tr.removeMark(0, end, mark).removeMark(0, end, activeMark));
         }
     }
-    setAnnotation = (start: number, end: number, mark: Mark, opened: boolean, keep: boolean = false) => {
+    adoptAnnotation = (start: number, end: number, mark: Mark) => {
         const view = this._editorView!;
-        const nmark = view.state.schema.marks.user_mark.create({ ...mark.attrs, userid: keep ? Doc.CurrentUserEmail : mark.attrs.userid, opened: opened });
+        const nmark = view.state.schema.marks.user_mark.create({ ...mark.attrs, userid: Doc.CurrentUserEmail });
         view.dispatch(view.state.tr.removeMark(start, end, nmark).addMark(start, end, nmark));
     }
     protected createDropTarget = (ele: HTMLDivElement) => {
@@ -873,6 +873,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
 
     static _downEvent: any;
     onPointerDown = (e: React.PointerEvent): void => {
+        this.doLinkOnDeselect();
         FormattedTextBox._downEvent = true;
         FormattedTextBoxComment.textBox = this;
         if (this.props.onClick && e.button === 0) {
@@ -1053,9 +1054,10 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
             this._undoTyping.end();
             this._undoTyping = undefined;
         }
-        this.doLinkOnDeselect(); 6
+        this.doLinkOnDeselect();
     }
 
+    _lastTimedMark: Mark | undefined = undefined;
     onKeyPress = (e: React.KeyboardEvent) => {
         if (e.altKey) {
             e.preventDefault();
@@ -1078,7 +1080,8 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
         if (e.key === "Tab" || e.key === "Enter") {
             e.preventDefault();
         }
-        const mark = schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.round(Date.now() / 1000 / 5) });
+        const mark = e.key !== " " && this._lastTimedMark ? this._lastTimedMark! : schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.round(Date.now() / 1000 / 5) });
+        this._lastTimedMark = mark;
         this._editorView!.dispatch(this._editorView!.state.tr.removeStoredMark(schema.marks.user_mark.create({})).addStoredMark(mark));
 
         if (!this._undoTyping) {
diff --git a/src/client/views/nodes/FormattedTextBoxComment.tsx b/src/client/views/nodes/FormattedTextBoxComment.tsx
index 409229c1a..1755cb99c 100644
--- a/src/client/views/nodes/FormattedTextBoxComment.tsx
+++ b/src/client/views/nodes/FormattedTextBoxComment.tsx
@@ -57,7 +57,6 @@ export class FormattedTextBoxComment {
     static start: number;
     static end: number;
     static mark: Mark;
-    static opened: boolean;
     static textBox: FormattedTextBox | undefined;
     static linkDoc: Doc | undefined;
     constructor(view: any) {
@@ -89,10 +88,8 @@ export class FormattedTextBoxComment {
                 } else if (textBox && (FormattedTextBoxComment.tooltipText as any).href) {
                     textBox.props.addDocTab(Docs.Create.WebDocument((FormattedTextBoxComment.tooltipText as any).href, { title: (FormattedTextBoxComment.tooltipText as any).href, width: 200, height: 400 }), undefined, "onRight");
                 }
-                FormattedTextBoxComment.opened = keep || !FormattedTextBoxComment.opened;
-                textBox && FormattedTextBoxComment.start !== undefined && textBox.setAnnotation(
-                    FormattedTextBoxComment.start, FormattedTextBoxComment.end, FormattedTextBoxComment.mark,
-                    FormattedTextBoxComment.opened, keep);
+                keep && textBox && FormattedTextBoxComment.start !== undefined && textBox.adoptAnnotation(
+                    FormattedTextBoxComment.start, FormattedTextBoxComment.end, FormattedTextBoxComment.mark);
                 e.stopPropagation();
                 e.preventDefault();
             };
@@ -104,12 +101,11 @@ export class FormattedTextBoxComment {
         FormattedTextBoxComment.textBox = undefined;
         FormattedTextBoxComment.tooltip && (FormattedTextBoxComment.tooltip.style.display = "none");
     }
-    public static SetState(textBox: any, opened: boolean, start: number, end: number, mark: Mark) {
+    public static SetState(textBox: any, start: number, end: number, mark: Mark) {
         FormattedTextBoxComment.textBox = textBox;
         FormattedTextBoxComment.start = start;
         FormattedTextBoxComment.end = end;
         FormattedTextBoxComment.mark = mark;
-        FormattedTextBoxComment.opened = opened;
         FormattedTextBoxComment.tooltip && (FormattedTextBoxComment.tooltip.style.display = "");
     }
 
@@ -143,7 +139,7 @@ export class FormattedTextBoxComment {
             state.doc.nodesBetween(state.selection.from, state.selection.to, (node: any, pos: number, parent: any) => !child && node.marks.length && (child = node));
             const mark = child && findOtherUserMark(child.marks);
             if (mark && child && (nbef || naft) && (!mark.attrs.opened || noselection)) {
-                FormattedTextBoxComment.SetState(FormattedTextBoxComment.textBox, mark.attrs.opened, state.selection.$from.pos - nbef, state.selection.$from.pos + naft, mark);
+                FormattedTextBoxComment.SetState(FormattedTextBoxComment.textBox, state.selection.$from.pos - nbef, state.selection.$from.pos + naft, mark);
             }
             if (mark && child && ((nbef && naft) || !noselection)) {
                 FormattedTextBoxComment.tooltipText.textContent = mark.attrs.userid + " date=" + (new Date(mark.attrs.modified * 5000)).toDateString();
@@ -160,19 +156,22 @@ export class FormattedTextBoxComment {
             const 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.startsWith("https://en.wikipedia.org/wiki/")) {
                     wiki().page(mark.attrs.href.replace("https://en.wikipedia.org/wiki/", "")).then(page => page.summary().then(summary => FormattedTextBoxComment.tooltipText.textContent = summary.substring(0, 500)));
                 } else {
                     FormattedTextBoxComment.tooltipText.style.whiteSpace = "pre";
                     FormattedTextBoxComment.tooltipText.style.overflow = "hidden";
                 }
-                (FormattedTextBoxComment.tooltipText as any).href = mark.attrs.href;
                 if (mark.attrs.href.indexOf(Utils.prepend("/doc/")) === 0) {
+                    FormattedTextBoxComment.tooltipText.textContent = "target not found...";
+                    (FormattedTextBoxComment.tooltipText as any).href = "";
                     const docTarget = mark.attrs.href.replace(Utils.prepend("/doc/"), "").split("?")[0];
                     docTarget && DocServer.GetRefField(docTarget).then(linkDoc => {
                         if (linkDoc instanceof Doc) {
+                            (FormattedTextBoxComment.tooltipText as any).href = mark.attrs.href;
                             FormattedTextBoxComment.linkDoc = linkDoc;
-                            const target = FieldValue(Doc.AreProtosEqual(FieldValue(Cast(linkDoc.anchor1, Doc)), textBox.props.Document) ? Cast(linkDoc.anchor2, Doc) : Cast(linkDoc.anchor1, Doc));
+                            const target = FieldValue(Doc.AreProtosEqual(FieldValue(Cast(linkDoc.anchor1, Doc)), textBox.props.Document) ? Cast(linkDoc.anchor2, Doc) : (Cast(linkDoc.anchor1, Doc)) || linkDoc);
                             try {
                                 ReactDOM.unmountComponentAtNode(FormattedTextBoxComment.tooltipText);
                             } catch (e) { }
-- 
cgit v1.2.3-70-g09d2


From 8ae1d973896f8629e5d2030f756b7b63df530ad2 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Mon, 6 Jan 2020 09:42:08 -0500
Subject: added showPreview flag to text hyperlinks

---
 src/client/util/RichTextSchema.tsx                 | 1 +
 src/client/views/nodes/FormattedTextBoxComment.tsx | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

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

diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx
index 622e71812..4fead71e5 100644
--- a/src/client/util/RichTextSchema.tsx
+++ b/src/client/util/RichTextSchema.tsx
@@ -274,6 +274,7 @@ export const marks: { [index: string]: MarkSpec } = {
     link: {
         attrs: {
             href: {},
+            showPreview: { default: true },
             location: { default: null },
             title: { default: null },
             docref: { default: false } // flags whether the linked text comes from a document within Dash.  If so, an attribution label is appended after the text
diff --git a/src/client/views/nodes/FormattedTextBoxComment.tsx b/src/client/views/nodes/FormattedTextBoxComment.tsx
index 1755cb99c..5fd5d4ce1 100644
--- a/src/client/views/nodes/FormattedTextBoxComment.tsx
+++ b/src/client/views/nodes/FormattedTextBoxComment.tsx
@@ -154,7 +154,7 @@ export class FormattedTextBoxComment {
             let child: any = null;
             state.doc.nodesBetween(state.selection.from, state.selection.to, (node: any, pos: number, parent: any) => !child && node.marks.length && (child = node));
             const mark = child && findLinkMark(child.marks);
-            if (mark && child && nbef && naft) {
+            if (mark && child && nbef && naft && mark.attrs.showPreview) {
                 FormattedTextBoxComment.tooltipText.textContent = "external => " + mark.attrs.href;
                 (FormattedTextBoxComment.tooltipText as any).href = mark.attrs.href;
                 if (mark.attrs.href.startsWith("https://en.wikipedia.org/wiki/")) {
-- 
cgit v1.2.3-70-g09d2

