From 26e9e0a554ee5aff001e2bc10b6802a4e830b63c Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 26 Jul 2021 12:35:25 -0400
Subject: cleaned up pile view a bit so that it doesn't scale up huge when
 there's one document.  made the pile be round and drop-shadowed.   made the
 pile go away immediately if there are no documents.  made it work with undo.

---
 src/client/views/collections/CollectionSubView.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'src/client/views/collections/CollectionSubView.tsx')

diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index ca45536f4..f39443ae2 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -453,7 +453,7 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
                     generatedDocuments.length > 1 ? generatedDocuments.map(d => { DocUtils.iconify(d); return d; }) : [];
                 if (completed) completed(set);
                 else {
-                    if (isFreeformView) {
+                    if (isFreeformView && generatedDocuments.length > 1) {
                         addDocument(DocUtils.pileup(generatedDocuments, options.x!, options.y!)!);
                     } else {
                         generatedDocuments.forEach(addDocument);
-- 
cgit v1.2.3-70-g09d2


From 8717e90d12c8caa16984f5a55eb8f442dcf5cbab Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 27 Jul 2021 14:31:30 -0400
Subject: fixe MakeClone to handle links properly.  fixed cloning richtext to
 update rich text references to documents properly.  fixed dragging to call
 MakeClone properly.

---
 src/client/util/DragManager.ts                     | 12 +++----
 src/client/util/LinkManager.ts                     | 38 ++++++++--------------
 src/client/util/Scripting.ts                       |  2 +-
 src/client/views/StyleProvider.tsx                 |  2 +-
 src/client/views/collections/CollectionSubView.tsx |  2 +-
 .../collections/collectionFreeForm/MarqueeView.tsx |  4 +--
 .../collectionSchema/CollectionSchemaCells.tsx     | 20 ++++++------
 src/fields/Doc.ts                                  | 33 ++++++++++++-------
 8 files changed, 55 insertions(+), 58 deletions(-)

(limited to 'src/client/views/collections/CollectionSubView.tsx')

diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index ab58f25e9..dd50727dd 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -210,16 +210,16 @@ export namespace DragManager {
             dropDoc instanceof Doc && DocUtils.MakeLinkToActiveAudio(() => dropDoc);
             return dropDoc;
         };
-        const finishDrag = (e: DragCompleteEvent) => {
+        const finishDrag = async (e: DragCompleteEvent) => {
             const docDragData = e.docDragData;
             dropEvent?.(); // glr: optional additional function to be called - in this case with presentation trails
             if (docDragData && !docDragData.droppedDocuments.length) {
                 docDragData.dropAction = dragData.userDropAction || dragData.dropAction;
                 docDragData.droppedDocuments =
-                    dragData.draggedDocuments.map(d => !dragData.isSelectionMove && !dragData.userDropAction && ScriptCast(d.onDragStart) ? addAudioTag(ScriptCast(d.onDragStart).script.run({ this: d }).result) :
+                    await Promise.all(dragData.draggedDocuments.map(async d => !dragData.isSelectionMove && !dragData.userDropAction && ScriptCast(d.onDragStart) ? addAudioTag(ScriptCast(d.onDragStart).script.run({ this: d }).result) :
                         docDragData.dropAction === "alias" ? Doc.MakeAlias(d) :
                             docDragData.dropAction === "proto" ? Doc.GetProto(d) :
-                                docDragData.dropAction === "copy" ? Doc.MakeClone(d) : d);
+                                docDragData.dropAction === "copy" ? (await Doc.MakeClone(d)).clone : d));
                 !["same", "proto"].includes(docDragData.dropAction as any) && docDragData.droppedDocuments.forEach((drop: Doc, i: number) => {
                     const dragProps = Cast(dragData.draggedDocuments[i].removeDropProperties, listSpec("string"), []);
                     const remProps = (dragData?.removeDropProperties || []).concat(Array.from(dragProps));
@@ -509,7 +509,7 @@ export namespace DragManager {
                 `translate(${(xs[i] += moveVec.x) + (options?.offsetX || 0)}px, ${(ys[i] += moveVec.y) + (options?.offsetY || 0)}px)  scale(${scaleXs[i]}, ${scaleYs[i]})`)
             );
         };
-        const upHandler = (e: PointerEvent) => {
+        const upHandler = async (e: PointerEvent) => {
             dispatchDrag(document.elementFromPoint(e.x, e.y) || document.body, e, new DragCompleteEvent(false, dragData), snapDrag(e, xFromLeft, yFromTop, xFromRight, yFromBottom), finishDrag, options);
             endDrag();
         };
@@ -517,7 +517,7 @@ export namespace DragManager {
         document.addEventListener("pointerup", upHandler);
     }
 
-    function dispatchDrag(target: Element, e: PointerEvent, complete: DragCompleteEvent, pos: { x: number, y: number }, finishDrag?: (e: DragCompleteEvent) => void, options?: DragOptions) {
+    async function dispatchDrag(target: Element, e: PointerEvent, complete: DragCompleteEvent, pos: { x: number, y: number }, finishDrag?: (e: DragCompleteEvent) => void, options?: DragOptions) {
         const dropArgs = {
             bubbles: true,
             detail: {
@@ -531,7 +531,7 @@ export namespace DragManager {
             }
         };
         target.dispatchEvent(new CustomEvent<DropEvent>("dashPreDrop", dropArgs));
-        finishDrag?.(complete);
+        await finishDrag?.(complete);
         target.dispatchEvent(new CustomEvent<DropEvent>("dashOnDrop", dropArgs));
         options?.dragComplete?.(complete);
     }
diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts
index 3c3d5c3b8..08f4ac9b7 100644
--- a/src/client/util/LinkManager.ts
+++ b/src/client/util/LinkManager.ts
@@ -1,15 +1,14 @@
+import { observable, observe, action } from "mobx";
 import { computedFn } from "mobx-utils";
-import { Doc, DocListCast, Opt, DirectLinksSym, Field } from "../../fields/Doc";
-import { BoolCast, Cast, StrCast, PromiseValue } from "../../fields/Types";
+import { DirectLinksSym, Doc, DocListCast, Field, Opt } from "../../fields/Doc";
+import { List } from "../../fields/List";
+import { ProxyField } from "../../fields/Proxy";
+import { BoolCast, Cast, PromiseValue, StrCast } from "../../fields/Types";
 import { LightboxView } from "../views/LightboxView";
 import { DocumentViewSharedProps, ViewAdjustment } from "../views/nodes/DocumentView";
 import { DocumentManager } from "./DocumentManager";
 import { SharingManager } from "./SharingManager";
 import { UndoManager } from "./UndoManager";
-import { observe, observable, reaction } from "mobx";
-import { listSpec } from "../../fields/Schema";
-import { List } from "../../fields/List";
-import { ProxyField } from "../../fields/Proxy";
 
 type CreateViewFunc = (doc: Doc, followLinkLocation: string, finished?: () => void) => void;
 /* 
@@ -34,7 +33,7 @@ export class LinkManager {
         LinkManager._instance = this;
         setTimeout(() => {
             LinkManager.userDocs = [Doc.LinkDBDoc().data as Doc, ...SharingManager.Instance.users.map(user => user.linkDatabase)];
-            const addLinkToDoc = (link: Doc): any => {
+            const addLinkToDoc = action((link: Doc): any => {
                 const a1 = link?.anchor1;
                 const a2 = link?.anchor2;
                 if (a1 instanceof Promise || a2 instanceof Promise) return PromiseValue(a1).then(a1 => PromiseValue(a2).then(a2 => addLinkToDoc(link)));
@@ -43,8 +42,8 @@ export class LinkManager {
                     Doc.GetProto(a2)[DirectLinksSym].add(link);
                     Doc.GetProto(link)[DirectLinksSym].add(link);
                 }
-            };
-            const remLinkFromDoc = (link: Doc): any => {
+            });
+            const remLinkFromDoc = action((link: Doc): any => {
                 const a1 = link?.anchor1;
                 const a2 = link?.anchor2;
                 if (a1 instanceof Promise || a2 instanceof Promise) return PromiseValue(a1).then(a1 => PromiseValue(a2).then(a2 => remLinkFromDoc(link)));
@@ -53,7 +52,7 @@ export class LinkManager {
                     Doc.GetProto(a2)[DirectLinksSym].delete(link);
                     Doc.GetProto(link)[DirectLinksSym].delete(link);
                 }
-            };
+            });
             const watchUserLinks = (userLinks: List<Doc>) => {
                 const toRealField = (field: Field) => field instanceof ProxyField ? field.value() : field;  // see List.ts.  data structure is not a simple list of Docs, but a list of ProxyField/Fields
                 observe(userLinks, change => {
@@ -75,8 +74,10 @@ export class LinkManager {
         });
     }
 
-    public addLink(linkDoc: Doc) {
-        return Doc.AddDocToList(Doc.LinkDBDoc(), "data", linkDoc);
+    public addLink(linkDoc: Doc, checkExists = false) {
+        if (!checkExists || !DocListCast(Doc.LinkDBDoc().data).includes(linkDoc)) {
+            Doc.AddDocToList(Doc.LinkDBDoc(), "data", linkDoc);
+        }
     }
     public deleteLink(linkDoc: Doc) { return Doc.RemoveDocFromList(Doc.LinkDBDoc(), "data", linkDoc); }
     public deleteAllLinksOnAnchor(anchor: Doc) { LinkManager.Instance.relatedLinker(anchor).forEach(linkDoc => LinkManager.Instance.deleteLink(linkDoc)); }
@@ -85,19 +86,6 @@ export class LinkManager {
     public getAllDirectLinks(anchor: Doc): Doc[] {
         return Array.from(Doc.GetProto(anchor)[DirectLinksSym]);
     } // finds all links that contain the given anchor
-    public getAllLinks(): Doc[] { return []; }//this.allLinks(); }
-
-    // allLinks = computedFn(function allLinks(this: any): Doc[] {
-    //     const linkData = Doc.LinkDBDoc().data;
-    //     const lset = new Set<Doc>(DocListCast(linkData));
-    //     SharingManager.Instance.users.forEach(user => DocListCast(user.linkDatabase?.data).forEach(doc => lset.add(doc)));
-    //     LinkManager.Instance.allLinks().filter(link => {
-    //         const a1 = Cast(link?.anchor1, Doc, null);
-    //         const a2 = Cast(link?.anchor2, Doc, null);
-    //         return link && ((a1?.author !== undefined && a2?.author !== undefined) || link.author === Doc.CurrentUserEmail) && (Doc.AreProtosEqual(anchor, a1) || Doc.AreProtosEqual(anchor, a2) || Doc.AreProtosEqual(link, anchor));
-    //     });
-    //     return Array.from(lset);
-    // }, true);
 
     relatedLinker = computedFn(function relatedLinker(this: any, anchor: Doc): Doc[] {
         const lfield = Doc.LayoutFieldKey(anchor);
diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts
index c3c3083be..f981f84cd 100644
--- a/src/client/util/Scripting.ts
+++ b/src/client/util/Scripting.ts
@@ -181,7 +181,7 @@ function Run(script: string | undefined, customParams: string[], diagnostics: an
             if (batch) {
                 batch.end();
             }
-            onError?.(error);
+            onError?.(script + " " + error);
             return { success: false, error, result: errorVal };
         }
     };
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index 6b94539c9..32ddc140c 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -101,7 +101,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
             const backColor = backgroundCol();
             if (!backColor) return undefined;
             const nonAlphaColor = backColor.startsWith("#") ? (backColor as string).substring(0, 7) :
-                backColor.startsWith("rgba") ? backColor.replace(/,.[^,]*\)/, ")").replace("rgba", "rgb") : backColor
+                backColor.startsWith("rgba") ? backColor.replace(/,.[^,]*\)/, ")").replace("rgba", "rgb") : backColor;
             const col = Color(nonAlphaColor).rgb();
             const colsum = (col.red() + col.green() + col.blue());
             if (colsum / col.alpha() > 400 || col.alpha() < 0.25) return Colors.DARK_GRAY;
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index f39443ae2..a5d27f038 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -454,7 +454,7 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
                 if (completed) completed(set);
                 else {
                     if (isFreeformView && generatedDocuments.length > 1) {
-                        addDocument(DocUtils.pileup(generatedDocuments, options.x!, options.y!)!);
+                        addDocument(DocUtils.pileup(generatedDocuments, options.x!, options.y!));
                     } else {
                         generatedDocuments.forEach(addDocument);
                     }
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index b1f2750c3..1f4fcb2a5 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -368,8 +368,8 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
         SelectionManager.DeselectAll();
         selected.forEach(d => this.props.removeDocument?.(d));
         const newCollection = DocUtils.pileup(selected, this.Bounds.left + this.Bounds.width / 2, this.Bounds.top + this.Bounds.height / 2);
-        this.props.addDocument?.(newCollection!);
-        this.props.selectDocuments([newCollection!]);
+        this.props.addDocument?.(newCollection);
+        this.props.selectDocuments([newCollection]);
         MarqueeOptionsMenu.Instance.fadeOut(true);
         this.hideMarquee();
     }
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx
index 90f64f163..0c434eae5 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx
@@ -246,13 +246,13 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
                                     } else {
                                         // check if the input is a number
                                         let inputIsNum = true;
-                                        for (let s of value) {
-                                            if (isNaN(parseInt(s)) && !(s == ".") && !(s == ",")) {
+                                        for (const s of value) {
+                                            if (isNaN(parseInt(s)) && !(s === ".") && !(s === ",")) {
                                                 inputIsNum = false;
                                             }
                                         }
                                         // check if the input is a boolean
-                                        let inputIsBool: boolean = value == "false" || value == "true";
+                                        const inputIsBool: boolean = value === "false" || value === "true";
                                         // what to do in the case 
                                         if (!inputIsNum && !inputIsBool && !value.startsWith("=")) {
                                             // if it's not a number, it's a string, and should be processed as such
@@ -263,12 +263,12 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
                                                 const vsqLength = valueSansQuotes.length;
                                                 // get rid of outer quotes
                                                 valueSansQuotes = valueSansQuotes.substring(value.startsWith("\"") ? 1 : 0,
-                                                    valueSansQuotes.charAt(vsqLength - 1) == "\"" ? vsqLength - 1 : vsqLength);
+                                                    valueSansQuotes.charAt(vsqLength - 1) === "\"" ? vsqLength - 1 : vsqLength);
                                             }
                                             let inputAsString = '"';
                                             // escape any quotes in the string
                                             for (const i of valueSansQuotes) {
-                                                if (i == '"') {
+                                                if (i === '"') {
                                                     inputAsString += '\\"';
                                                 } else {
                                                     inputAsString += i;
@@ -278,7 +278,7 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
                                             inputAsString += '"';
                                             //two options here: we can strip off outer quotes or we can figure out what's going on with the script
                                             const script = CompileScript(inputAsString, { requiredType: type, typecheck: false, editable: true, addReturn: true, params: { this: Doc.name, $r: "number", $c: "number", $: "any" } });
-                                            const changeMade = inputAsString.length !== value.length || inputAsString.length - 2 !== value.length
+                                            const changeMade = inputAsString.length !== value.length || inputAsString.length - 2 !== value.length;
                                             script.compiled && (retVal = this.applyToDoc(changeMade ? this._rowDoc : this._rowDataDoc, this.props.row, this.props.col, script.run));
                                             // handle numbers and expressions
                                         } else if (inputIsNum || value.startsWith("=")) {
@@ -286,18 +286,18 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
                                             const inputscript = value.substring(value.startsWith("=") ? 1 : 0);
                                             // if commas are not stripped, the parser only considers the numbers after the last comma
                                             let inputSansCommas = "";
-                                            for (let s of inputscript) {
-                                                if (!(s == ",")) {
+                                            for (const s of inputscript) {
+                                                if (!(s === ",")) {
                                                     inputSansCommas += s;
                                                 }
                                             }
                                             const script = CompileScript(inputSansCommas, { requiredType: type, typecheck: false, editable: true, addReturn: true, params: { this: Doc.name, $r: "number", $c: "number", $: "any" } });
-                                            const changeMade = value.length !== value.length || value.length - 2 !== value.length
+                                            const changeMade = value.length !== value.length || value.length - 2 !== value.length;
                                             script.compiled && (retVal = this.applyToDoc(changeMade ? this._rowDoc : this._rowDataDoc, this.props.row, this.props.col, script.run));
                                             // handle booleans
                                         } else if (inputIsBool) {
                                             const script = CompileScript(value, { requiredType: type, typecheck: false, editable: true, addReturn: true, params: { this: Doc.name, $r: "number", $c: "number", $: "any" } });
-                                            const changeMade = value.length !== value.length || value.length - 2 !== value.length
+                                            const changeMade = value.length !== value.length || value.length - 2 !== value.length;
                                             script.compiled && (retVal = this.applyToDoc(changeMade ? this._rowDoc : this._rowDataDoc, this.props.row, this.props.col, script.run));
                                         }
                                     }
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 464a8ad05..7993af149 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -516,30 +516,30 @@ export namespace Doc {
         return alias;
     }
 
-    export async function makeClone(doc: Doc, cloneMap: Map<string, Doc>, rtfs: { copy: Doc, key: string, field: RichTextField }[], exclusions: string[], dontCreate: boolean, asBranch: boolean): Promise<Doc> {
+    export async function makeClone(doc: Doc, cloneMap: Map<string, Doc>, linkMap: Map<Doc, Doc>, rtfs: { copy: Doc, key: string, field: RichTextField }[], exclusions: string[], dontCreate: boolean, asBranch: boolean): Promise<Doc> {
         if (Doc.IsBaseProto(doc)) return doc;
         if (cloneMap.get(doc[Id])) return cloneMap.get(doc[Id])!;
         const copy = dontCreate ? asBranch ? (Cast(doc.branchMaster, Doc, null) || doc) : doc : new Doc(undefined, true);
         cloneMap.set(doc[Id], copy);
-        if (LinkManager.Instance.getAllLinks().includes(doc) && LinkManager.Instance.getAllLinks().indexOf(copy) === -1) LinkManager.Instance.addLink(copy);
-        const filter = [...exclusions, ...Cast(doc.cloneFieldFilter, listSpec("string"), [])];
-        await Promise.all([...Object.keys(doc), "links"].map(async key => {
+        const fieldExclusions = (doc.type === DocumentType.TEXTANCHOR) ? exclusions.filter(ex => ex !== "annotationOn") : exclusions;
+        const filter = [...fieldExclusions, ...Cast(doc.cloneFieldFilter, listSpec("string"), [])];
+        await Promise.all(Object.keys(doc).map(async key => {
             if (filter.includes(key)) return;
             const assignKey = (val: any) => !dontCreate && (copy[key] = val);
             const cfield = ComputedField.WithoutComputed(() => FieldValue(doc[key]));
-            const field = key === "links" && Doc.IsPrototype(doc) ? doc[key] : ProxyField.WithoutProxy(() => doc[key]);
+            const field = ProxyField.WithoutProxy(() => doc[key]);
             const copyObjectField = async (field: ObjectField) => {
                 const list = Cast(doc[key], listSpec(Doc));
                 const docs = list && (await DocListCastAsync(list))?.filter(d => d instanceof Doc);
                 if (docs !== undefined && docs.length) {
-                    const clones = await Promise.all(docs.map(async d => Doc.makeClone(d, cloneMap, rtfs, exclusions, dontCreate, asBranch)));
+                    const clones = await Promise.all(docs.map(async d => Doc.makeClone(d, cloneMap, linkMap, rtfs, exclusions, dontCreate, asBranch)));
                     !dontCreate && assignKey(new List<Doc>(clones));
                 } else if (doc[key] instanceof Doc) {
-                    assignKey(key.includes("layout[") ? undefined : key.startsWith("layout") ? doc[key] as Doc : await Doc.makeClone(doc[key] as Doc, cloneMap, rtfs, exclusions, dontCreate, asBranch)); // reference documents except copy documents that are expanded teplate fields 
+                    assignKey(key.includes("layout[") ? undefined : key.startsWith("layout") ? doc[key] as Doc : await Doc.makeClone(doc[key] as Doc, cloneMap, linkMap, rtfs, exclusions, dontCreate, asBranch)); // reference documents except copy documents that are expanded teplate fields 
                 } else {
                     !dontCreate && assignKey(ObjectField.MakeCopy(field));
                     if (field instanceof RichTextField) {
-                        if (field.Data.includes('"docid":') || field.Data.includes('"targetId":') || field.Data.includes('"linkId":')) {
+                        if (field.Data.includes('"audioId":') || field.Data.includes('"textId":') || field.Data.includes('"anchorId":')) {
                             rtfs.push({ copy, key, field });
                         }
                     }
@@ -547,14 +547,17 @@ export namespace Doc {
             };
             if (key === "proto") {
                 if (doc[key] instanceof Doc) {
-                    assignKey(await Doc.makeClone(doc[key]!, cloneMap, rtfs, exclusions, dontCreate, asBranch));
+                    assignKey(await Doc.makeClone(doc[key] as Doc, cloneMap, linkMap, rtfs, exclusions, dontCreate, asBranch));
+                }
+            } else if (key === "anchor1" || key === "anchor2") {
+                if (doc[key] instanceof Doc) {
+                    assignKey(await Doc.makeClone(doc[key] as Doc, cloneMap, linkMap, rtfs, exclusions, true, asBranch));
                 }
             } else {
                 if (field instanceof RefField) {
                     assignKey(field);
                 } else if (cfield instanceof ComputedField) {
                     !dontCreate && assignKey(ComputedField.MakeFunction(cfield.script.originalScript));
-                    (key === "links" && field instanceof ObjectField) && await copyObjectField(field);
                 } else if (field instanceof ObjectField) {
                     await copyObjectField(field);
                 } else if (field instanceof Promise) {
@@ -564,6 +567,10 @@ export namespace Doc {
                 }
             }
         }));
+        for (const link of Array.from(doc[DirectLinksSym])) {
+            const linkClone = await Doc.makeClone(link, cloneMap, linkMap, rtfs, exclusions, dontCreate, asBranch);
+            linkMap.set(link, linkClone);
+        }
         if (!dontCreate) {
             Doc.SetInPlace(copy, "title", (asBranch ? "BRANCH: " : "CLONE: ") + doc.title, true);
             asBranch ? (copy.branchOf = doc) : (copy.cloneOf = doc);
@@ -576,8 +583,10 @@ export namespace Doc {
     }
     export async function MakeClone(doc: Doc, dontCreate: boolean = false, asBranch = false) {
         const cloneMap = new Map<string, Doc>();
+        const linkMap = new Map<Doc, Doc>();
         const rtfMap: { copy: Doc, key: string, field: RichTextField }[] = [];
-        const copy = await Doc.makeClone(doc, cloneMap, rtfMap, ["context", "annotationOn", "cloneOf", "branches", "branchOf"], dontCreate, asBranch);
+        const copy = await Doc.makeClone(doc, cloneMap, linkMap, rtfMap, ["context", "annotationOn", "cloneOf", "branches", "branchOf"], dontCreate, asBranch);
+        Array.from(linkMap.entries()).map((links: Doc[]) => LinkManager.Instance.addLink(links[1], true));
         rtfMap.map(({ copy, key, field }) => {
             const replacer = (match: any, attr: string, id: string, offset: any, string: any) => {
                 const mapped = cloneMap.get(id);
@@ -589,7 +598,7 @@ export namespace Doc {
             };
             const regex = `(${Utils.prepend("/doc/")})([^"]*)`;
             const re = new RegExp(regex, "g");
-            copy[key] = new RichTextField(field.Data.replace(/("docid":|"targetId":|"linkId":)"([^"]+)"/g, replacer).replace(re, replacer2), field.Text);
+            copy[key] = new RichTextField(field.Data.replace(/("textId":|"audioId":|"anchorId":)"([^"]+)"/g, replacer).replace(re, replacer2), field.Text);
         });
         return { clone: copy, map: cloneMap };
     }
-- 
cgit v1.2.3-70-g09d2

