From 1b65063a3cce0123215c914af6328a8a81ece33f Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Sat, 13 Jun 2020 20:53:31 -0400
Subject: fixed text sidebar when text is scaled.  started to add multiple
 links to a text selection

---
 .../nodes/formattedText/FormattedTextBoxComment.tsx   | 19 ++++++++++---------
 1 file changed, 10 insertions(+), 9 deletions(-)

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

diff --git a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
index 59a6045ab..dde7cc0c1 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
@@ -115,7 +115,7 @@ export class FormattedTextBoxComment {
         FormattedTextBoxComment.tooltip && (FormattedTextBoxComment.tooltip.style.display = "");
     }
 
-    static update(view: EditorView, lastState?: EditorState) {
+    static update(view: EditorView, lastState?: EditorState, forceUrl: string = "") {
         const state = view.state;
         // Don't do anything if the document/selection didn't change
         if (lastState && lastState.doc.eq(state.doc) &&
@@ -160,25 +160,26 @@ 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 && 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/")) {
-                    wiki().page(mark.attrs.href.replace("https://en.wikipedia.org/wiki/", "")).then(page => page.summary().then(summary => FormattedTextBoxComment.tooltipText.textContent = summary.substring(0, 500)));
+            const href = mark?.attrs.href || forceUrl;
+            if (forceUrl || (href && child && nbef && naft && mark?.attrs.showPreview)) {
+                FormattedTextBoxComment.tooltipText.textContent = "external => " + href;
+                (FormattedTextBoxComment.tooltipText as any).href = href;
+                if (href.startsWith("https://en.wikipedia.org/wiki/")) {
+                    wiki().page(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";
                 }
-                if (mark.attrs.href.indexOf(Utils.prepend("/doc/")) === 0) {
+                if (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];
+                    const docTarget = href.replace(Utils.prepend("/doc/"), "").split("?")[0];
                     try {
                         ReactDOM.unmountComponentAtNode(FormattedTextBoxComment.tooltipText);
                     } catch (e) { }
                     docTarget && DocServer.GetRefField(docTarget).then(async linkDoc => {
                         if (linkDoc instanceof Doc) {
-                            (FormattedTextBoxComment.tooltipText as any).href = mark.attrs.href;
+                            (FormattedTextBoxComment.tooltipText as any).href = href;
                             FormattedTextBoxComment.linkDoc = linkDoc;
                             const anchor = FieldValue(Doc.AreProtosEqual(FieldValue(Cast(linkDoc.anchor1, Doc)), textBox.dataDoc) ? Cast(linkDoc.anchor2, Doc) : (Cast(linkDoc.anchor1, Doc)) || linkDoc);
                             const target = anchor?.annotationOn ? await DocCastAsync(anchor.annotationOn) : anchor;
-- 
cgit v1.2.3-70-g09d2


From e2423815ef608f35fdb9c1625d7de0bf5a5fe206 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Sat, 13 Jun 2020 23:19:27 -0400
Subject: fixes to make link lines work correctly with text anchors that have
 more than one link.  fixed problem making a text link anchor by adding dummy
 mark to split the selection first.

---
 .../CollectionFreeFormLinkView.tsx                 | 11 ++++++---
 .../views/nodes/formattedText/FormattedTextBox.tsx | 24 ++++++++++---------
 .../formattedText/FormattedTextBoxComment.tsx      |  2 +-
 .../formattedText/ProsemirrorExampleTransfer.ts    |  1 -
 .../views/nodes/formattedText/RichTextMenu.tsx     |  8 +++----
 src/client/views/nodes/formattedText/marks_rts.ts  | 27 ++++++++++++++--------
 src/fields/RichTextUtils.ts                        |  2 +-
 7 files changed, 45 insertions(+), 30 deletions(-)

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

diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
index f3fc04752..8ab00bb29 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
@@ -46,10 +46,15 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
                 const bfield = afield === "anchor1" ? "anchor2" : "anchor1";
 
                 // really hacky stuff to make the LinkAnchorBox display where we want it to:
-                //   if there's an element in the DOM with the id of the opposite anchor, then that DOM element is a hyperlink source for the current anchor and we want to place our link box at it's top right
+                //   if there's an element in the DOM with a classname containing the link's id and a targetids attribute containing the other end of the link, 
+                //   then that DOM element is a hyperlink source for the current anchor and we want to place our link box at it's top right
                 //   otherwise, we just use the computed nearest point on the document boundary to the target Document
-                const targetAhyperlink = window.document.getElementById(this.props.LinkDocs[0][Id] + (this.props.LinkDocs[0][afield] as Doc)[Id]);
-                const targetBhyperlink = window.document.getElementById(this.props.LinkDocs[0][Id] + (this.props.LinkDocs[0][bfield] as Doc)[Id]);
+                const linkId = this.props.LinkDocs[0][Id]; // this link's Id
+                const AanchorId = (this.props.LinkDocs[0][afield] as Doc)[Id]; // anchor a's id
+                const BanchorId = (this.props.LinkDocs[0][bfield] as Doc)[Id]; // anchor b's id
+                const linkEles = Array.from(window.document.getElementsByClassName(linkId))
+                const targetAhyperlink = linkEles.find((ele: any) => ele.getAttribute("targetids")?.includes(AanchorId));
+                const targetBhyperlink = linkEles.find((ele: any) => ele.getAttribute("targetids")?.includes(BanchorId));
                 if (!targetBhyperlink) {
                     this.props.A.props.Document[afield + "_x"] = (apt.point.x - abounds.left) / abounds.width * 100;
                     this.props.A.props.Document[afield + "_y"] = (apt.point.y - abounds.top) / abounds.height * 100;
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 95d36c994..ffab1d1e1 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -583,20 +583,22 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         };
     }
 
-    makeLinkToSelection(linkDocId: string, title: string, location: string, targetId: string) {
-        if (this._editorView) {
-            const href = Utils.prepend("/doc/" + linkDocId);
-            const sel = this._editorView.state.selection;
-            let tr = this._editorView!.state.tr;
-            sel.from !== sel.to && this._editorView.state.doc.nodesBetween(sel.from, sel.to, (node: any, pos: number, parent: any) => {
-                if (node.firstChild === null) {
-                    const allHrefs = [{ href, title }];
+    makeLinkToSelection(linkId: string, title: string, location: string, targetId: string) {
+        const state = this._editorView?.state;
+        if (state) {
+            const href = Utils.prepend("/doc/" + linkId);
+            const sel = state.selection;
+            const splitter = state.schema.marks.splitter.create({ id: Utils.GenerateGuid() });
+            let tr = state.tr.addMark(sel.from, sel.to, splitter);
+            sel.from !== sel.to && tr.doc.nodesBetween(sel.from, sel.to, (node: any, pos: number, parent: any) => {
+                if (node.firstChild === null && node.marks.find((m: Mark) => m.type.name === schema.marks.splitter.name)) {
+                    const allHrefs = [{ href, title, targetId, linkId }];
                     allHrefs.push(...(node.marks.find((m: Mark) => m.type.name === schema.marks.link.name)?.attrs.allHrefs ?? []));
-                    const link = this._editorView!.state.schema.marks.link.create({ href, allHrefs, title, location, linkId: linkDocId, targetId });
+                    const link = state.schema.marks.link.create({ href, allHrefs, title, location, linkId, targetId });
                     tr = tr.addMark(pos, pos + node.nodeSize, link);
                 }
             });
-            this._editorView.dispatch(tr);
+            this._editorView!.dispatch(tr.removeMark(sel.from, sel.to, splitter));
         }
     }
     componentDidMount() {
@@ -704,7 +706,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                     }
                     const marks = [...node.marks];
                     const linkIndex = marks.findIndex(mark => mark.type === editor.state.schema.marks.link);
-                    return linkIndex !== -1 && scrollToLinkID === marks[linkIndex].attrs.href.replace(/.*\/doc\//, "") ? node : undefined;
+                    return linkIndex !== -1 && marks[linkIndex].attrs.allRefs.find((item: { href: string }) => scrollToLinkID === item.href.replace(/.*\/doc\//, "")) ? node : undefined;
                 };
 
                 let start = 0;
diff --git a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
index dde7cc0c1..7513c881d 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
@@ -160,7 +160,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);
-            const href = mark?.attrs.href || forceUrl;
+            const href = mark?.attrs.allHrefs.find((item: { href: string }) => item.href)?.href || forceUrl;
             if (forceUrl || (href && child && nbef && naft && mark?.attrs.showPreview)) {
                 FormattedTextBoxComment.tooltipText.textContent = "external => " + href;
                 (FormattedTextBoxComment.tooltipText as any).href = href;
diff --git a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
index 0e3e7f91e..77b93b9d2 100644
--- a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
+++ b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
@@ -6,7 +6,6 @@ import { liftListItem, sinkListItem } from "./prosemirrorPatches.js";
 import { splitListItem, wrapInList, } from "prosemirror-schema-list";
 import { EditorState, Transaction, TextSelection } from "prosemirror-state";
 import { SelectionManager } from "../../../util/SelectionManager";
-import { Docs } from "../../../documents/Documents";
 import { NumCast, BoolCast, Cast, StrCast } from "../../../../fields/Types";
 import { Doc } from "../../../../fields/Doc";
 import { FormattedTextBox } from "./FormattedTextBox";
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index fd1b26208..03d393cde 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -631,7 +631,7 @@ export default class RichTextMenu extends AntimodeMenu {
         const node = this.view.state.selection.$from.nodeAfter;
         const link = node && node.marks.find(m => m.type.name === "link");
         if (link) {
-            const href = link.attrs.href;
+            const href = link.attrs.allHrefs.length > 0 ? link.attrs.allHrefs[0].href : undefined;
             if (href) {
                 if (href.indexOf(Utils.prepend("/doc/")) === 0) {
                     const linkclicked = href.replace(Utils.prepend("/doc/"), "").split("?")[0];
@@ -677,7 +677,7 @@ export default class RichTextMenu extends AntimodeMenu {
 
         const node = this.view.state.selection.$from.nodeAfter;
         const link = node && node.marks.find(m => m.type === this.view!.state.schema.marks.link);
-        const href = link!.attrs.href;
+        const href = link!.attrs.allHrefs.length > 0 ? link!.attrs.allHrefs[0].href : undefined;
         if (href) {
             if (href.indexOf(Utils.prepend("/doc/")) === 0) {
                 const linkclicked = href.replace(Utils.prepend("/doc/"), "").split("?")[0];
@@ -705,8 +705,8 @@ export default class RichTextMenu extends AntimodeMenu {
         let startIndex = $start.index();
         let endIndex = $start.indexAfter();
 
-        while (startIndex > 0 && $start.parent.child(startIndex - 1).marks.filter(m => m.type === mark && m.attrs.href === href).length) startIndex--;
-        while (endIndex < $start.parent.childCount && $start.parent.child(endIndex).marks.filter(m => m.type === mark && m.attrs.href === href).length) endIndex++;
+        while (startIndex > 0 && $start.parent.child(startIndex - 1).marks.filter(m => m.type === mark && m.attrs.allHrefs.find((item: { href: string }) => item.href === href)).length) startIndex--;
+        while (endIndex < $start.parent.childCount && $start.parent.child(endIndex).marks.filter(m => m.type === mark && m.attrs.allHrefs.find((item: { href: string }) => item.href === href)).length) endIndex++;
 
         let startPos = $start.start();
         let endPos = startPos;
diff --git a/src/client/views/nodes/formattedText/marks_rts.ts b/src/client/views/nodes/formattedText/marks_rts.ts
index b42fa6755..9c279a88f 100644
--- a/src/client/views/nodes/formattedText/marks_rts.ts
+++ b/src/client/views/nodes/formattedText/marks_rts.ts
@@ -9,15 +9,20 @@ const codeDOM: DOMOutputSpecArray = ["code", 0];
 
 // :: Object [Specs](#model.MarkSpec) for the marks in the schema.
 export const marks: { [index: string]: MarkSpec } = {
+    splitter: {
+        attrs: {
+            id: { default: "" }
+        },
+        toDOM(node: any) {
+            return ["div", { className: "dummy" }, 0];
+        }
+    },
     // :: MarkSpec A link. Has `href` and `title` attributes. `title`
     // defaults to the empty string. Rendered and parsed as an `<a>`
     // element.
     link: {
         attrs: {
-            href: {},
-            allHrefs: { default: [] as { href: string, title: string }[] },
-            targetId: { default: "" },
-            linkId: { default: "" },
+            allHrefs: { default: [] as { href: string, title: string, linkId: string, targetId: string }[] },
             showPreview: { default: true },
             location: { default: null },
             title: { default: null },
@@ -26,20 +31,24 @@ export const marks: { [index: string]: MarkSpec } = {
         inclusive: false,
         parseDOM: [{
             tag: "a[href]", getAttrs(dom: any) {
-                return { href: dom.getAttribute("href"), location: dom.getAttribute("location"), title: dom.getAttribute("title"), targetId: dom.getAttribute("id") };
+                return { allHrefs: [{ href: dom.getAttribute("href"), title: dom.getAttribute("title"), linkId: dom.getAttribute("linkids"), targetId: dom.getAttribute("targetids") }], location: dom.getAttribute("location"), };
             }
         }],
         toDOM(node: any) {
+            const targetids = node.attrs.allHrefs.reduce((p: string, item: { href: string, title: string, targetId: string, linkId: string }) => p + " " + item.targetId, "");
+            const linkids = node.attrs.allHrefs.reduce((p: string, item: { href: string, title: string, targetId: string, linkId: string }) => p + " " + item.linkId, "");
             return node.attrs.docref && node.attrs.title ?
                 ["div", ["span", `"`], ["span", 0], ["span", `"`], ["br"], ["a", { ...node.attrs, class: "prosemirror-attribution", title: `${node.attrs.title}` }, node.attrs.title], ["br"]] :
                 node.attrs.allHrefs.length === 1 ?
-                    ["a", { ...node.attrs, id: node.attrs.linkId + node.attrs.targetId, title: `${node.attrs.title}` }, 0] :
+                    ["a", { ...node.attrs, class: linkids, targetids, title: `${node.attrs.title}`, href: node.attrs.allHrefs[0].href }, 0] :
                     ["div", { class: "prosemirror-anchor" },
                         ["button", { class: "prosemirror-linkBtn" },
-                            ["a", { ...node.attrs, id: node.attrs.linkId + node.attrs.targetId, title: `${node.attrs.title}` }, 0],
-                            ["input", { class: "fa fa-caret-down prosemirror-hrefoptions" }],
+                            ["a", { ...node.attrs, class: linkids, targetids, title: `${node.attrs.title}` }, 0],
+                            ["input", { class: "prosemirror-hrefoptions" }],
                         ],
-                        ["div", { class: "prosemirror-links" }, ...node.attrs.allHrefs.map((item: { href: string, title: string }) => ["a", { class: "prosemirror-dropdownlink", href: item.href }, item.title])]
+                        ["div", { class: "prosemirror-links" }, ...node.attrs.allHrefs.map((item: { href: string, title: string }) =>
+                            ["a", { class: "prosemirror-dropdownlink", href: item.href }, item.title]
+                        )]
                     ]
         }
     },
diff --git a/src/fields/RichTextUtils.ts b/src/fields/RichTextUtils.ts
index f81ec8c6d..66959882d 100644
--- a/src/fields/RichTextUtils.ts
+++ b/src/fields/RichTextUtils.ts
@@ -392,7 +392,7 @@ export namespace RichTextUtils {
                     const { attrs } = mark;
                     switch (converted) {
                         case "link":
-                            let url = attrs.href;
+                            let url = attrs.allHrefs.length ? attrs.allHrefs[0].href : "";
                             const delimiter = "/doc/";
                             const alreadyShared = "?sharing=true";
                             if (new RegExp(window.location.origin + delimiter).test(url) && !url.endsWith(alreadyShared)) {
-- 
cgit v1.2.3-70-g09d2


From 182aef3ef47a3005e1b8a60bad8195769ce07733 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Mon, 15 Jun 2020 18:00:25 -0400
Subject: fixed acls for text hyperlinks (mostly) by not allowing links to be
 traversed

---
 src/client/DocServer.ts                            |  7 +++-
 src/client/views/nodes/DocumentView.tsx            | 14 ++++++-
 .../formattedText/FormattedTextBoxComment.tsx      | 46 +++++++++++++---------
 src/fields/Doc.ts                                  | 20 ++--------
 4 files changed, 49 insertions(+), 38 deletions(-)

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

diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts
index c6b3fa61f..c7dfb0b23 100644
--- a/src/client/DocServer.ts
+++ b/src/client/DocServer.ts
@@ -1,6 +1,6 @@
 import * as io from 'socket.io-client';
 import { MessageStore, YoutubeQueryTypes, GestureContent, MobileInkOverlayContent, UpdateMobileInkOverlayPositionContent, MobileDocumentUploadContent } from "./../server/Message";
-import { Opt, Doc } from '../fields/Doc';
+import { Opt, Doc, fetchProto } from '../fields/Doc';
 import { Utils, emptyFunction } from '../Utils';
 import { SerializationHelper } from './util/SerializationHelper';
 import { RefField } from '../fields/RefField';
@@ -244,7 +244,10 @@ export namespace DocServer {
             return cached;
         } else {
             // CACHED => great, let's just return the cached field we have
-            return Promise.resolve(cached);
+            return Promise.resolve(cached).then(field => {
+                (field instanceof Doc) && fetchProto(field);
+                return field;
+            });
         }
     };
     const _GetCachedRefFieldImpl = (id: string): Opt<RefField> => {
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 35bf5344e..8a067b806 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -687,7 +687,18 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     @undoBatch
     @action
     setAcl = (acl: "readOnly" | "addOnly" | "ownerOnly") => {
-        this.layoutDoc.ACL = this.dataDoc.ACL = acl;
+        this.dataDoc.ACL = this.props.Document.ACL = acl;
+        DocListCast(this.dataDoc[Doc.LayoutFieldKey(this.dataDoc)]).map(d => {
+            if (d.author === Doc.CurrentUserEmail) d.ACL = acl;
+            const data = d[DataSym];
+            if (data && data.author === Doc.CurrentUserEmail) data.ACL = acl;
+        });
+    }
+    @undoBatch
+    @action
+    testAcl = (acl: "readOnly" | "addOnly" | "ownerOnly") => {
+        this.dataDoc.author = this.props.Document.author = "ADMIN";
+        this.dataDoc.ACL = this.props.Document.ACL = acl;
         DocListCast(this.dataDoc[Doc.LayoutFieldKey(this.dataDoc)]).map(d => {
             if (d.author === Doc.CurrentUserEmail) d.ACL = acl;
             const data = d[DataSym];
@@ -758,6 +769,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         moreItems.push({ description: "Make Add Only", event: () => this.setAcl("addOnly"), icon: "concierge-bell" });
         moreItems.push({ description: "Make Read Only", event: () => this.setAcl("readOnly"), icon: "concierge-bell" });
         moreItems.push({ description: "Make Private", event: () => this.setAcl("ownerOnly"), icon: "concierge-bell" });
+        moreItems.push({ description: "Test Private", event: () => this.testAcl("ownerOnly"), icon: "concierge-bell" });
         moreItems.push({ description: "Make View of Metadata Field", event: () => Doc.MakeMetadataFieldTemplate(this.props.Document, this.props.DataDoc), icon: "concierge-bell" });
         moreItems.push({ description: `${this.Document._chromeStatus !== "disabled" ? "Hide" : "Show"} Chrome`, event: () => this.Document._chromeStatus = (this.Document._chromeStatus !== "disabled" ? "disabled" : "enabled"), icon: "project-diagram" });
         moreItems.push({ description: this.Document.lockedPosition ? "Unlock Position" : "Lock Position", event: this.toggleLockPosition, icon: BoolCast(this.Document.lockedPosition) ? "unlock" : "lock" });
diff --git a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
index 7513c881d..5ac173602 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
@@ -84,11 +84,13 @@ export class FormattedTextBoxComment {
                 const keep = e.target && (e.target as any).type === "checkbox" ? true : false;
                 const textBox = FormattedTextBoxComment.textBox;
                 if (FormattedTextBoxComment.linkDoc && !keep && textBox) {
-                    if (FormattedTextBoxComment.linkDoc.type !== DocumentType.LINK) {
-                        textBox.props.addDocTab(FormattedTextBoxComment.linkDoc, e.ctrlKey ? "inTab" : "onRight");
-                    } else {
-                        DocumentManager.Instance.FollowLink(FormattedTextBoxComment.linkDoc, textBox.props.Document,
-                            (doc: Doc, followLinkLocation: string) => textBox.props.addDocTab(doc, e.ctrlKey ? "inTab" : followLinkLocation));
+                    if (FormattedTextBoxComment.linkDoc.author) {
+                        if (FormattedTextBoxComment.linkDoc.type !== DocumentType.LINK) {
+                            textBox.props.addDocTab(FormattedTextBoxComment.linkDoc, e.ctrlKey ? "inTab" : "onRight");
+                        } else {
+                            DocumentManager.Instance.FollowLink(FormattedTextBoxComment.linkDoc, textBox.props.Document,
+                                (doc: Doc, followLinkLocation: string) => textBox.props.addDocTab(doc, e.ctrlKey ? "inTab" : followLinkLocation));
+                        }
                     }
                 } 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, UseCors: true }), "onRight");
@@ -115,6 +117,23 @@ export class FormattedTextBoxComment {
         FormattedTextBoxComment.tooltip && (FormattedTextBoxComment.tooltip.style.display = "");
     }
 
+    static showCommentbox(set: string, view: EditorView, nbef: number) {
+        const state = view.state;
+        if (set !== "none") {
+            // These are in screen coordinates
+            // let start = view.coordsAtPos(state.selection.from), end = view.coordsAtPos(state.selection.to);
+            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
+            const box = (document.getElementsByClassName("mainView-container") as any)[0].getBoundingClientRect();
+            // Find a center-ish x position from the selection endpoints (when
+            // crossing lines, end may be more to the left)
+            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";
+        }
+        FormattedTextBoxComment.tooltip && (FormattedTextBoxComment.tooltip.style.display = set);
+    }
+
     static update(view: EditorView, lastState?: EditorState, forceUrl: string = "") {
         const state = view.state;
         // Don't do anything if the document/selection didn't change
@@ -186,7 +205,8 @@ export class FormattedTextBoxComment {
                             if (anchor !== target && anchor && target) {
                                 target._scrollY = NumCast(anchor?.y);
                             }
-                            if (target) {
+                            if (target?.author) {
+                                FormattedTextBoxComment.showCommentbox("", view, nbef);
                                 ReactDOM.render(<ContentFittingDocumentView
                                     Document={target}
                                     LibraryPath={emptyPath}
@@ -224,19 +244,7 @@ export class FormattedTextBoxComment {
                 set = "";
             }
         }
-        if (set !== "none") {
-            // These are in screen coordinates
-            // let start = view.coordsAtPos(state.selection.from), end = view.coordsAtPos(state.selection.to);
-            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
-            const box = (document.getElementsByClassName("mainView-container") as any)[0].getBoundingClientRect();
-            // Find a center-ish x position from the selection endpoints (when
-            // crossing lines, end may be more to the left)
-            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";
-        }
-        FormattedTextBoxComment.tooltip && (FormattedTextBoxComment.tooltip.style.display = set);
+        FormattedTextBoxComment.showCommentbox(set, view, nbef);
     }
 
     destroy() { }
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index a72088d04..b205a4a10 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -100,7 +100,7 @@ export const UpdatingFromServer = Symbol("UpdatingFromServer");
 const CachedUpdates = Symbol("Cached updates");
 
 
-function fetchProto(doc: Doc) {
+export function fetchProto(doc: Doc) {
     if (doc.author !== Doc.CurrentUserEmail) {
         const acl = Doc.Get(doc, "ACL", true);
         switch (acl) {
@@ -116,21 +116,9 @@ function fetchProto(doc: Doc) {
         }
     }
 
-    const proto = doc.proto;
-    if (proto instanceof Promise) {
-        proto.then(proto => {
-            if (proto.author !== Doc.CurrentUserEmail) {
-                if (proto.ACL === "ownerOnly") {
-                    proto[AclSym] = doc[AclSym] = AclPrivate;
-                    return undefined;
-                } else if (proto.ACL === "readOnly") {
-                    proto[AclSym] = doc[AclSym] = AclReadonly;
-                } else if (proto.ACL === "addOnly") {
-                    proto[AclSym] = doc[AclSym] = AclAddonly;
-                }
-            }
-        });
-        return proto;
+    if (doc.proto instanceof Promise) {
+        doc.proto.then(proto => fetchProto(proto));
+        return doc.proto;
     }
 }
 
-- 
cgit v1.2.3-70-g09d2

