From 1a1fc27a66c95c947dc8d2a812484f37586133cd Mon Sep 17 00:00:00 2001
From: Ashley Cai <ashley.lcai@gmail.com>
Date: Thu, 15 Jul 2021 12:37:31 -0700
Subject: Starting Color consistency

---
 src/client/views/StyleProvider.tsx | 31 ++++++++++++++++---------------
 1 file changed, 16 insertions(+), 15 deletions(-)

(limited to 'src/client/views/StyleProvider.tsx')

diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index 9e61351c4..1bf47f6ac 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -1,4 +1,5 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { Colors } from './global/globalEnums';
 import { IconProp } from '@fortawesome/fontawesome-svg-core';
 import 'golden-layout/src/css/goldenlayout-base.css';
 import 'golden-layout/src/css/goldenlayout-dark-theme.css';
@@ -97,14 +98,14 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
         case StyleProp.Color:
             const docColor: Opt<string> = StrCast(doc?.[fieldKey + "color"], StrCast(doc?._color));
             if (docColor) return docColor;
-            const backColor = backgroundCol();// || (darkScheme() ? "black" : "white");
+            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
             const col = Color(nonAlphaColor).rgb();
             const colsum = (col.red() + col.green() + col.blue());
-            if (colsum / col.alpha() > 400 || col.alpha() < 0.25) return "black";
-            return "white";
+            if (colsum / col.alpha() > 400 || col.alpha() < 0.25) return Colors.DARK_GRAY;
+            return Colors.WHITE;
         case StyleProp.Hidden: return BoolCast(doc?._hidden);
         case StyleProp.BorderRounding: return StrCast(doc?.[fieldKey + "borderRounding"]);
         case StyleProp.TitleHeight: return 15;
@@ -114,30 +115,30 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
             doc?.type === DocumentType.RTF) && showTitle() && !StrCast(doc?.showTitle).includes(":hover") ? 15 : 0;
         case StyleProp.BackgroundColor: {
             let docColor: Opt<string> = StrCast(doc?.[fieldKey + "backgroundColor"], StrCast(doc?._backgroundColor, isCaption ? "rgba(0,0,0,0.4)" : ""));
-            if (MainView.Instance.LastButton === doc) return darkScheme() ? "dimgrey" : "lightgrey";
+            if (MainView.Instance.LastButton === doc) return darkScheme() ? Colors.MEDIUM_GRAY : Colors.LIGHT_GRAY;
             switch (doc?.type) {
                 case DocumentType.PRESELEMENT: docColor = docColor || (darkScheme() ? "" : ""); break;
-                case DocumentType.PRES: docColor = docColor || (darkScheme() ? "#3e3e3e" : "white"); break;
-                case DocumentType.FONTICON: docColor = docColor || "black"; break;
-                case DocumentType.RTF: docColor = docColor || (darkScheme() ? "#2d2d2d" : "#f1efeb"); break;
-                case DocumentType.FILTER: docColor = docColor || (darkScheme() ? "#2d2d2d" : "rgba(105, 105, 105, 0.432)"); break;
+                case DocumentType.PRES: docColor = docColor || (darkScheme() ? Colors.DARK_GRAY : Colors.WHITE); break;
+                case DocumentType.FONTICON: docColor = docColor || Colors.DARK_GRAY; break;
+                case DocumentType.RTF: docColor = docColor || (darkScheme() ? Colors.DARK_GRAY : Colors.LIGHT_GRAY); break;
+                case DocumentType.FILTER: docColor = docColor || (darkScheme() ? Colors.DARK_GRAY : "rgba(105, 105, 105, 0.432)"); break;
                 case DocumentType.INK: docColor = doc?.isInkMask ? "rgba(0,0,0,0.7)" : undefined; break;
                 case DocumentType.SLIDER: break;
                 case DocumentType.EQUATION: docColor = docColor || "transparent"; break;
                 case DocumentType.LABEL: docColor = docColor || (doc.annotationOn !== undefined ? "rgba(128, 128, 128, 0.18)" : undefined); break;
-                case DocumentType.BUTTON: docColor = docColor || (darkScheme() ? "#2d2d2d" : "lightgray"); break;
-                case DocumentType.LINKANCHOR: docColor = isAnchor ? "lightblue" : "transparent"; break;
+                case DocumentType.BUTTON: docColor = docColor || (darkScheme() ? Colors.DARK_GRAY : Colors.LIGHT_GRAY); break;
+                case DocumentType.LINKANCHOR: docColor = isAnchor ? Colors.LIGHT_BLUE : "transparent"; break;
                 case DocumentType.LINK: docColor = docColor || "transparent"; break;
                 case DocumentType.IMG:
                 case DocumentType.WEB:
                 case DocumentType.PDF:
                 case DocumentType.SCREENSHOT:
-                case DocumentType.VID: docColor = docColor || (darkScheme() ? "#2d2d2d" : "lightgray"); break;
+                case DocumentType.VID: docColor = docColor || (darkScheme() ? Colors.DARK_GRAY : Colors.LIGHT_GRAY); break;
                 case DocumentType.COL:
                     if (StrCast(Doc.LayoutField(doc)).includes("SliderBox")) break;
-                    docColor = docColor ? docColor :
+                    docColor = docColor ? Colors.DARK_GRAY :
                         doc?._isGroup ? "#00000004" : // very faint highlight to show bounds of group
-                            (Doc.IsSystem(doc) ? (darkScheme() ? "rgb(62,62,62)" : "lightgrey") : // system docs (seen in treeView) get a grayish background
+                            (Doc.IsSystem(doc) ? (darkScheme() ? Colors.DARK_GRAY : Colors.LIGHT_GRAY) : // system docs (seen in treeView) get a grayish background
                                 isBackground() ? "cyan" : // ?? is there a good default for a background collection
                                     doc.annotationOn ? "#00000015" : // faint interior for collections on PDFs, images, etc
                                         StrCast((props?.renderDepth || 0) > 0 ?
@@ -145,7 +146,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
                                             Doc.UserDoc().activeCollectionBackground));
                     break;
                 //if (doc._viewType !== CollectionViewType.Freeform && doc._viewType !== CollectionViewType.Time) return "rgb(62,62,62)";
-                default: docColor = docColor || (darkScheme() ? "black" : "white"); break;
+                default: docColor = docColor || (darkScheme() ? Colors.DARK_GRAY : Colors.WHITE); break;
             }
             if (docColor && (!doc || props?.layerProvider?.(doc) === false)) docColor = Color(docColor.toLowerCase()).fade(0.5).toString();
             return docColor;
@@ -159,7 +160,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
                 case DocumentType.COL:
                     return StrCast(doc?.boxShadow,
                         isBackground() || doc?._isGroup || docProps?.LayoutTemplateString ? undefined : // groups have no drop shadow -- they're supposed to be "invisible".  LayoutString's imply collection is being rendered as something else (e.g., title of a Slide)
-                            `${darkScheme() ? "rgb(30, 32, 31) " : "#9c9396 "} ${StrCast(doc.boxShadow, "0.2vw 0.2vw 0.8vw")}`);
+                            `${darkScheme() ? Colors.DARK_GRAY : Colors.MEDIUM_GRAY} ${StrCast(doc.boxShadow, "0.2vw 0.2vw 0.8vw")}`);
 
                 case DocumentType.LABEL:
                     if (doc?.annotationOn !== undefined) return "black 2px 2px 1px";
-- 
cgit v1.2.3-70-g09d2


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/documents/Documents.ts                         |  2 --
 src/client/views/StyleProvider.tsx                        | 15 ++++++++-------
 src/client/views/collections/CollectionPileView.tsx       | 15 +++++++++------
 src/client/views/collections/CollectionSubView.tsx        |  2 +-
 .../CollectionFreeFormLayoutEngines.tsx                   |  5 +++--
 5 files changed, 21 insertions(+), 18 deletions(-)

(limited to 'src/client/views/StyleProvider.tsx')

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index c2cdf7f72..f1db3e32c 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -1333,9 +1333,7 @@ export namespace DocUtils {
         newCollection.x = NumCast(newCollection.x) + NumCast(newCollection._width) / 2 - 55;
         newCollection.y = NumCast(newCollection.y) + NumCast(newCollection._height) / 2 - 55;
         newCollection._width = newCollection._height = 110;
-        //newCollection.borderRounding = "40px";
         newCollection._jitterRotation = 10;
-        newCollection._backgroundColor = "gray";
         return newCollection;
     }
 
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index 55f6b8e3c..6b94539c9 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -107,7 +107,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
             if (colsum / col.alpha() > 400 || col.alpha() < 0.25) return Colors.DARK_GRAY;
             return Colors.WHITE;
         case StyleProp.Hidden: return BoolCast(doc?._hidden);
-        case StyleProp.BorderRounding: return StrCast(doc?.[fieldKey + "borderRounding"]);
+        case StyleProp.BorderRounding: return StrCast(doc?.[fieldKey + "borderRounding"], doc?._viewType === CollectionViewType.Pile ? "50%" : "");
         case StyleProp.TitleHeight: return 15;
         case StyleProp.BorderPath: return comicStyle() && props?.renderDepth ? { path: wavyBorderPath(props?.PanelWidth?.() || 0, props?.PanelHeight?.() || 0), fill: wavyBorderPath(props?.PanelWidth?.() || 0, props?.PanelHeight?.() || 0, .08), width: 3 } : { path: undefined, width: 0 };
         case StyleProp.JitterRotation: return comicStyle() ? random(-1, 1, NumCast(doc?.x), NumCast(doc?.y)) * ((props?.PanelWidth() || 0) > (props?.PanelHeight() || 0) ? 5 : 10) : 0;
@@ -136,14 +136,14 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
                 case DocumentType.VID: docColor = docColor || (darkScheme() ? Colors.DARK_GRAY : Colors.LIGHT_GRAY); break;
                 case DocumentType.COL:
                     if (StrCast(Doc.LayoutField(doc)).includes("SliderBox")) break;
-                    docColor = docColor ? Colors.DARK_GRAY :
-                        doc?._isGroup ? "#00000004" : // very faint highlight to show bounds of group
-                            (Doc.IsSystem(doc) ? (darkScheme() ? Colors.DARK_GRAY : Colors.LIGHT_GRAY) : // system docs (seen in treeView) get a grayish background
+                    docColor = docColor ||
+                        (doc?._isGroup ? "#00000004" : // very faint highlight to show bounds of group
+                            (doc?._viewType === CollectionViewType.Pile || Doc.IsSystem(doc) ? (darkScheme() ? Colors.DARK_GRAY : Colors.LIGHT_GRAY) : // system docs (seen in treeView) get a grayish background
                                 isBackground() ? "cyan" : // ?? is there a good default for a background collection
                                     doc.annotationOn ? "#00000015" : // faint interior for collections on PDFs, images, etc
                                         StrCast((props?.renderDepth || 0) > 0 ?
                                             Doc.UserDoc().activeCollectionNestedBackground :
-                                            Doc.UserDoc().activeCollectionBackground));
+                                            Doc.UserDoc().activeCollectionBackground)));
                     break;
                 //if (doc._viewType !== CollectionViewType.Freeform && doc._viewType !== CollectionViewType.Time) return "rgb(62,62,62)";
                 default: docColor = docColor || (darkScheme() ? Colors.DARK_GRAY : Colors.WHITE); break;
@@ -159,8 +159,9 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
             switch (doc?.type) {
                 case DocumentType.COL:
                     return StrCast(doc?.boxShadow,
-                        isBackground() || doc?._isGroup || docProps?.LayoutTemplateString ? undefined : // groups have no drop shadow -- they're supposed to be "invisible".  LayoutString's imply collection is being rendered as something else (e.g., title of a Slide)
-                            `${darkScheme() ? Colors.DARK_GRAY : Colors.MEDIUM_GRAY} ${StrCast(doc.boxShadow, "0.2vw 0.2vw 0.8vw")}`);
+                        doc?._viewType === CollectionViewType.Pile ? "4px 4px 10px 2px" :
+                            isBackground() || doc?._isGroup || docProps?.LayoutTemplateString ? undefined : // groups have no drop shadow -- they're supposed to be "invisible".  LayoutString's imply collection is being rendered as something else (e.g., title of a Slide)
+                                `${darkScheme() ? Colors.DARK_GRAY : Colors.MEDIUM_GRAY} ${StrCast(doc.boxShadow, "0.2vw 0.2vw 0.8vw")}`);
 
                 case DocumentType.LABEL:
                     if (doc?.annotationOn !== undefined) return "black 2px 2px 1px";
diff --git a/src/client/views/collections/CollectionPileView.tsx b/src/client/views/collections/CollectionPileView.tsx
index 6baf633dd..bc1407c53 100644
--- a/src/client/views/collections/CollectionPileView.tsx
+++ b/src/client/views/collections/CollectionPileView.tsx
@@ -1,12 +1,12 @@
-import { action, computed } from "mobx";
+import { action, computed, IReactionDisposer, reaction } from "mobx";
 import { observer } from "mobx-react";
 import { Doc, HeightSym, WidthSym } from "../../../fields/Doc";
 import { NumCast, StrCast } from "../../../fields/Types";
-import { emptyFunction, setupMoveUpEvents, returnTrue } from "../../../Utils";
+import { emptyFunction, returnTrue, setupMoveUpEvents } from "../../../Utils";
 import { DocUtils } from "../../documents/Documents";
 import { SelectionManager } from "../../util/SelectionManager";
 import { SnappingManager } from "../../util/SnappingManager";
-import { UndoManager, undoBatch } from "../../util/UndoManager";
+import { undoBatch, UndoManager } from "../../util/UndoManager";
 import { CollectionFreeFormView } from "./collectionFreeForm/CollectionFreeFormView";
 import "./CollectionPileView.scss";
 import { CollectionSubView } from "./CollectionSubView";
@@ -15,6 +15,7 @@ import React = require("react");
 @observer
 export class CollectionPileView extends CollectionSubView(doc => doc) {
     _originalChrome: any = "";
+    _disposers: { [name: string]: IReactionDisposer } = {};
 
     componentDidMount() {
         if (this.layoutEngine() !== "pass" && this.layoutEngine() !== "starburst") {
@@ -22,9 +23,14 @@ export class CollectionPileView extends CollectionSubView(doc => doc) {
         }
         this._originalChrome = this.layoutDoc._chromeHidden;
         this.layoutDoc._chromeHidden = true;
+
+        // pileups are designed to go away when they are empty.  
+        this._disposers.selected = reaction(() => this.childDocs.length,
+            (num) => !num && this.props.ContainingCollectionView?.removeDocument(this.props.Document));
     }
     componentWillUnmount() {
         this.layoutDoc._chromeHidden = this._originalChrome;
+        Object.values(this._disposers).forEach(disposer => disposer?.());
     }
 
     layoutEngine = () => StrCast(this.Document._pileLayoutEngine);
@@ -107,9 +113,6 @@ export class CollectionPileView extends CollectionSubView(doc => doc) {
             this._undoBatch?.end();
             this._undoBatch = undefined;
             SnappingManager.SetIsDragging(false);
-            if (!this.childDocs.length) {
-                this.props.ContainingCollectionView?.removeDocument(this.props.Document);
-            }
         }, emptyFunction, e.shiftKey && this.layoutEngine() === "pass", this.layoutEngine() === "pass" && e.shiftKey); // this sets _doubleTap
     }
 
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);
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
index afc1babeb..37444a9dc 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
@@ -126,7 +126,8 @@ export function computerStarburstLayout(
             replica: ""
         });
     });
-    return normalizeResults(scaleDim, 12, docMap, poolData, viewDefsToJSX, [], 0, []);
+    const divider = { type: "div", color: "transparent", x: -burstRadius[0] / 3, y: 0, width: 15, height: 15, payload: undefined };
+    return normalizeResults(scaleDim, 12, docMap, poolData, viewDefsToJSX, [], 0, [divider]);
 }
 
 
@@ -399,7 +400,7 @@ function normalizeResults(
 ): ViewDefResult[] {
     const grpEles = groupNames.map(gn => ({ x: gn.x, y: gn.y, width: gn.width, height: gn.height }) as ViewDefBounds);
     const docEles = Array.from(docMap.entries()).map(ele => ele[1]);
-    const aggBounds = aggregateBounds(grpEles.concat(docEles.map(de => ({ ...de, type: "doc", payload: "" }))).filter(e => e.zIndex !== -99), 0, 0);
+    const aggBounds = aggregateBounds(extras.concat(grpEles.concat(docEles.map(de => ({ ...de, type: "doc", payload: "" })))).filter(e => e.zIndex !== -99), 0, 0);
     aggBounds.r = Math.max(minWidth, aggBounds.r - aggBounds.x);
     const wscale = panelDim[0] / (aggBounds.r - aggBounds.x);
     let scale = wscale * (aggBounds.b - aggBounds.y) > panelDim[1] ? (panelDim[1]) / (aggBounds.b - aggBounds.y) : wscale;
-- 
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/StyleProvider.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


From a5faca5f510e60ecce125ab59096d4e943352ea5 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 29 Jul 2021 17:49:14 -0400
Subject: fixed issues with following links to anchors in PDFs/etc and getting
 scroll position right.

---
 src/Utils.ts                                                  | 11 ++++++-----
 src/client/views/MarqueeAnnotator.tsx                         | 10 ++++++++--
 src/client/views/StyleProvider.tsx                            |  1 +
 .../collections/collectionFreeForm/CollectionFreeFormView.tsx | 10 +++++-----
 src/client/views/nodes/WebBox.tsx                             |  3 ++-
 src/client/views/pdf/PDFViewer.tsx                            |  3 ++-
 6 files changed, 24 insertions(+), 14 deletions(-)

(limited to 'src/client/views/StyleProvider.tsx')

diff --git a/src/Utils.ts b/src/Utils.ts
index ef9c51b8b..d87c3cc6b 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -191,11 +191,12 @@ export namespace Utils {
         return { h: h, s: s, l: l };
     }
 
-    export function scrollIntoView(targetY: number, targetHgt: number, scrollTop: number, contextHgt: number) {
-        if (scrollTop + contextHgt < targetY + targetHgt * 1.1) {
-            return Math.ceil(targetY + targetHgt * 1.1 - contextHgt);
-        } else if (scrollTop > targetY - targetHgt * .1) {
-            return Math.max(0, Math.floor(targetY - targetHgt * .1));
+    export function scrollIntoView(targetY: number, targetHgt: number, scrollTop: number, contextHgt: number, minSpacing: number) {
+        if (scrollTop + contextHgt < targetY + minSpacing + targetHgt) {
+            return Math.ceil(targetY + minSpacing + targetHgt - contextHgt);
+        }
+        if (scrollTop > targetY - minSpacing - targetHgt) {
+            return Math.max(0, Math.floor(targetY - minSpacing - targetHgt));
         }
     }
 
diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx
index 717bd0768..805cda95c 100644
--- a/src/client/views/MarqueeAnnotator.tsx
+++ b/src/client/views/MarqueeAnnotator.tsx
@@ -120,9 +120,11 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
             return marqueeAnno;
         }
 
-        const textRegionAnno = Docs.Create.HTMLAnchorDocument([], { annotationOn: this.props.rootDoc, title: "Selection on " + this.props.rootDoc.title, _width: 1, _height: 1 });
+        const textRegionAnno = Docs.Create.HTMLAnchorDocument([], { annotationOn: this.props.rootDoc, backgroundColor: "transparent", title: "Selection on " + this.props.rootDoc.title });
+        let minX = Number.MAX_VALUE;
         let maxX = -Number.MAX_VALUE;
         let minY = Number.MAX_VALUE;
+        let maxY = -Number.MIN_VALUE;
         const annoDocs: Doc[] = [];
         savedAnnoMap.forEach((value: HTMLDivElement[], key: number) => value.map(anno => {
             const textRegion = new Doc();
@@ -135,12 +137,16 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
             annoDocs.push(textRegion);
             anno.remove();
             minY = Math.min(NumCast(textRegion.y), minY);
+            minX = Math.min(NumCast(textRegion.x), minX);
+            maxY = Math.max(NumCast(textRegion.y) + NumCast(textRegion._height), maxY);
             maxX = Math.max(NumCast(textRegion.x) + NumCast(textRegion._width), maxX);
         }));
 
         const textRegionAnnoProto = Doc.GetProto(textRegionAnno);
         textRegionAnnoProto.y = Math.max(minY, 0);
-        textRegionAnnoProto.x = Math.max(maxX, 0);
+        textRegionAnnoProto.x = Math.max(minX, 0);
+        textRegionAnnoProto.height = Math.max(maxY, 0) - Math.max(minY, 0);
+        textRegionAnnoProto.width = Math.max(maxX, 0) - Math.max(minX, 0);
         // mainAnnoDocProto.text = this._selectionText;
         textRegionAnnoProto.textInlineAnnotations = new List<Doc>(annoDocs);
         savedAnnoMap.clear();
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index 32ddc140c..dc6ac0366 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -174,6 +174,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
             }
         }
         case StyleProp.PointerEvents:
+            if (doc?.type === DocumentType.HTMLANCHOR) return "none";
             if (props?.pointerEvents === "none") return "none";
             const layer = doc && props?.layerProvider?.(doc);
             if (opacity() === 0 || (doc?.type === DocumentType.INK && !docProps?.treeViewDoc) || doc?.isInkMask) return "none";
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index a4e310e6c..7fa5fd871 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -48,6 +48,7 @@ import { CollectionFreeFormRemoteCursors } from "./CollectionFreeFormRemoteCurso
 import "./CollectionFreeFormView.scss";
 import { MarqueeView } from "./MarqueeView";
 import React = require("react");
+import { DocumentType } from "../../../documents/DocumentTypes";
 
 export const panZoomSchema = createSchema({
     _panX: "number",
@@ -834,10 +835,9 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
                 map(doc => ({ ...this.childDataProvider(doc, ""), ...this.childSizeProvider(doc, "") }));
             if (measuredDocs.length) {
                 const ranges = measuredDocs.reduce(({ xrange, yrange }, { x, y, width, height }) =>  // computes range of content
-                ({
-                    xrange: { min: Math.min(xrange.min, x), max: Math.max(xrange.max, x + width) },
-                    yrange: { min: Math.min(yrange.min, y), max: Math.max(yrange.max, y + height) }
-                })
+                        xrange: { min: Math.min(xrange.min, x), max: Math.max(xrange.max, x + width) },
+                        yrange: { min: Math.min(yrange.min, y), max: Math.max(yrange.max, y + height) }
+                    })
                     , {
                         xrange: { min: Number.MAX_VALUE, max: -Number.MAX_VALUE },
                         yrange: { min: Number.MAX_VALUE, max: -Number.MAX_VALUE }
@@ -1486,7 +1486,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
             onDragOver={e => e.preventDefault()}
             onContextMenu={this.onContextMenu}
             style={{
-                pointerEvents: this.backgroundEvents ? "all" : this.props.pointerEvents as any,
+                pointerEvents: this.props.Document.type === DocumentType.HTMLANCHOR ? "none" : this.backgroundEvents ? "all" : this.props.pointerEvents as any,
                 transform: `scale(${this.contentScaling || 1})`,
                 width: `${100 / (this.contentScaling || 1)}%`,
                 height: this.isAnnotationOverlay && this.Document.scrollHeight ? this.Document.scrollHeight : `${100 / (this.contentScaling || 1)}%`// : this.isAnnotationOverlay ? (this.Document.scrollHeight ? this.Document.scrollHeight : "100%") : this.props.PanelHeight()
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 88e38712a..c60798c6d 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -162,7 +162,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     scrollFocus = (doc: Doc, smooth: boolean) => {
         if (this._sidebarRef?.current?.makeDocUnfiltered(doc)) return 1;
         if (doc !== this.rootDoc && this._outerRef.current) {
-            const scrollTo = doc.type === DocumentType.TEXTANCHOR ? NumCast(doc.y) : Utils.scrollIntoView(NumCast(doc.y), doc[HeightSym](), NumCast(this.layoutDoc._scrollTop), this.props.PanelHeight() / (this.props.scaling?.() || 1));
+            const windowHeight = this.props.PanelHeight() / (this.props.scaling?.() || 1);
+            const scrollTo = doc.type === DocumentType.TEXTANCHOR ? NumCast(doc.y) : Utils.scrollIntoView(NumCast(doc.y), doc[HeightSym](), NumCast(this.layoutDoc._scrollTop), windowHeifht, windowHeight * .1);
             if (scrollTo !== undefined) {
                 const focusSpeed = smooth ? 500 : 0;
                 this._initialScroll !== undefined && (this._initialScroll = scrollTo);
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 4a50dccf3..e8c7a4ab0 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -184,7 +184,8 @@ export class PDFViewer extends React.Component<IViewerProps> {
         const mainCont = this._mainCont.current;
         let focusSpeed: Opt<number>;
         if (doc !== this.props.rootDoc && mainCont && this._pdfViewer) {
-            const scrollTo = Utils.scrollIntoView(NumCast(doc.y), doc[HeightSym](), NumCast(this.props.layoutDoc._scrollTop), this.props.PanelHeight() / (this.props.scaling?.() || 1));
+            const windowHeight = this.props.PanelHeight() / (this.props.scaling?.() || 1);
+            const scrollTo = Utils.scrollIntoView(NumCast(doc.y), doc[HeightSym](), NumCast(this.props.layoutDoc._scrollTop), windowHeight, .1 * windowHeight);
             if (scrollTo !== undefined) {
                 focusSpeed = 500;
 
-- 
cgit v1.2.3-70-g09d2


From b6b2057cf28e8c0d3c22b9056074fe5155602d0a Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 29 Jul 2021 20:15:46 -0400
Subject: converted HTMLANCHOR and TEXTANCHOR to MARKER

---
 src/client/documents/DocumentTypes.ts                      |  4 +---
 src/client/documents/Documents.ts                          | 14 +++++---------
 src/client/util/DocumentManager.ts                         |  4 ++--
 src/client/util/SelectionManager.ts                        |  2 +-
 src/client/views/StyleProvider.tsx                         |  2 +-
 src/client/views/collections/CollectionTimeView.tsx        |  2 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx          |  2 +-
 src/client/views/nodes/WebBox.tsx                          |  2 +-
 src/fields/Doc.ts                                          |  2 +-
 9 files changed, 14 insertions(+), 20 deletions(-)

(limited to 'src/client/views/StyleProvider.tsx')

diff --git a/src/client/documents/DocumentTypes.ts b/src/client/documents/DocumentTypes.ts
index 8565784b4..dba7ff907 100644
--- a/src/client/documents/DocumentTypes.ts
+++ b/src/client/documents/DocumentTypes.ts
@@ -18,7 +18,7 @@ export enum DocumentType {
     LABEL = "label",            // simple text label
     BUTTON = "button",          // onClick button
     WEBCAM = "webcam",          // webcam
-    HTMLANCHOR = "htmlanchor",  // text selection anchor in PDF/Web
+    MARKER = "marker",          // generic marker document not intended to be viewed independently of its context (e.g., for text selections in PDF/Web/RTF)
     DATE = "date",              // calendar view of a date
     SCRIPTING = "script",       // script editor
     EQUATION = "equation",      // equation editor
@@ -40,6 +40,4 @@ export enum DocumentType {
     LINKDB = "linkdb",          // database of links  ??? why do we have this
     SCRIPTDB = "scriptdb",      // database of scripts
     GROUPDB = "groupdb",        // database of groups
-
-    TEXTANCHOR = "textanchor"   // selection of text in a text box
 }
\ No newline at end of file
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index f1db3e32c..e863b4198 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -427,7 +427,7 @@ export namespace Docs {
             [DocumentType.PRESELEMENT, {
                 layout: { view: PresElementBox, dataField: defaultDataKey }
             }],
-            [DocumentType.HTMLANCHOR, {
+            [DocumentType.MARKER, {
                 layout: { view: CollectionView, dataField: defaultDataKey },
                 options: { links: ComputedField.MakeFunction("links(self)") as any, hideLinkButton: true }
             }],
@@ -452,10 +452,6 @@ export namespace Docs {
                 layout: { view: EmptyBox, dataField: defaultDataKey },
                 options: { links: ComputedField.MakeFunction("links(self)") as any }
             }],
-            [DocumentType.TEXTANCHOR, {
-                layout: { view: EmptyBox, dataField: defaultDataKey },
-                options: { targetDropAction: "move", links: ComputedField.MakeFunction("links(self)") as any, hideLinkButton: true }
-            }]
         ]);
 
         const suffix = "Proto";
@@ -670,9 +666,9 @@ export namespace Docs {
             viewProps["acl-Override"] = "None";
             viewProps["acl-Public"] = Doc.UserDoc()?.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Add;
             const viewDoc = Doc.assign(Doc.MakeDelegate(dataDoc, delegId), viewProps, true, true);
-            ![DocumentType.LINK, DocumentType.TEXTANCHOR, DocumentType.LABEL].includes(viewDoc.type as any) && DocUtils.MakeLinkToActiveAudio(() => viewDoc);
+            ![DocumentType.LINK, DocumentType.MARKER, DocumentType.LABEL].includes(viewDoc.type as any) && DocUtils.MakeLinkToActiveAudio(() => viewDoc);
 
-            !Doc.IsSystem(dataDoc) && ![DocumentType.HTMLANCHOR, DocumentType.KVP, DocumentType.LINK, DocumentType.LINKANCHOR, DocumentType.TEXTANCHOR].includes(proto.type as any) &&
+            !Doc.IsSystem(dataDoc) && ![DocumentType.MARKER, DocumentType.KVP, DocumentType.LINK, DocumentType.LINKANCHOR].includes(proto.type as any) &&
                 !dataDoc.isFolder && !dataProps.annotationOn && Doc.AddDocToList(Cast(Doc.UserDoc().myFileOrphans, Doc, null), "data", dataDoc);
 
             return viewDoc;
@@ -802,7 +798,7 @@ export namespace Docs {
         }
 
         export function TextanchorDocument(options: DocumentOptions = {}, id?: string) {
-            return InstanceFromProto(Prototypes.get(DocumentType.TEXTANCHOR), undefined, options, id);
+            return InstanceFromProto(Prototypes.get(DocumentType.MARKER), undefined, options, id);
         }
 
         export function FreeformDocument(documents: Array<Doc>, options: DocumentOptions, id?: string) {
@@ -811,7 +807,7 @@ export namespace Docs {
             return inst;
         }
         export function HTMLAnchorDocument(documents: Array<Doc>, options: DocumentOptions, id?: string) {
-            return InstanceFromProto(Prototypes.get(DocumentType.HTMLANCHOR), new List(documents), options, id);
+            return InstanceFromProto(Prototypes.get(DocumentType.MARKER), new List(documents), options, id);
         }
 
         export function PileDocument(documents: Array<Doc>, options: DocumentOptions, id?: string) {
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 304215a8f..5b092258a 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -28,7 +28,7 @@ export class DocumentManager {
         DocListCast(view.rootDoc.links).forEach(link => {
             const whichOtherAnchor = view.props.LayoutTemplateString?.includes("anchor2") ? "anchor1" : "anchor2";
             const otherDoc = link && (link[whichOtherAnchor] as Doc);
-            const otherDocAnno = otherDoc?.type === DocumentType.TEXTANCHOR ? otherDoc.annotationOn as Doc : undefined;
+            const otherDocAnno = DocumentType.MARKER === otherDoc?.type ? otherDoc.annotationOn as Doc : undefined;
             otherDoc && DocumentManager.Instance.DocumentViews?.filter(dv => Doc.AreProtosEqual(dv.rootDoc, otherDoc) || Doc.AreProtosEqual(dv.rootDoc, otherDocAnno)).
                 forEach(otherView => {
                     if (otherView.rootDoc.type !== DocumentType.LINK || otherView.props.LayoutTemplateString !== view.props.LayoutTemplateString) {
@@ -162,7 +162,7 @@ export class DocumentManager {
         const contextDoc = contextDocs?.find(doc => Doc.AreProtosEqual(doc, targetDoc) || Doc.AreProtosEqual(doc, annotatedDoc)) ? docContext : undefined;
         const targetDocContext = contextDoc || annotatedDoc;
         const targetDocContextView = targetDocContext && getFirstDocView(targetDocContext);
-        const focusView = !docView && targetDoc.type === DocumentType.TEXTANCHOR && annoContainerView ? annoContainerView : docView;
+        const focusView = !docView && targetDoc.type === DocumentType.MARKER && annoContainerView ? annoContainerView : docView;
         if (!docView && annoContainerView && !focusView) {
             annoContainerView.focus(targetDoc);  // this allows something like a PDF view to remove its doc filters to expose the target so that it can be found in the retry code below
         }
diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index 7aeb19391..dbcc49f3d 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -23,7 +23,7 @@ export namespace SelectionManager {
         @action
         SelectView(docView: DocumentView, ctrlPressed: boolean): void {
             // if doc is not in SelectedDocuments, add it
-            if (!manager.SelectedViews.get(docView) && docView.props.Document.type !== DocumentType.HTMLANCHOR) {
+            if (!manager.SelectedViews.get(docView) && docView.props.Document.type !== DocumentType.MARKER) {
                 if (!ctrlPressed) {
                     this.DeselectAll();
                 }
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index dc6ac0366..c9e532745 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -174,7 +174,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
             }
         }
         case StyleProp.PointerEvents:
-            if (doc?.type === DocumentType.HTMLANCHOR) return "none";
+            if (doc?.type === DocumentType.MARKER) return "none";
             if (props?.pointerEvents === "none") return "none";
             const layer = doc && props?.layerProvider?.(doc);
             if (opacity() === 0 || (doc?.type === DocumentType.INK && !docProps?.treeViewDoc) || doc?.isInkMask) return "none";
diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx
index f41043179..339163510 100644
--- a/src/client/views/collections/CollectionTimeView.tsx
+++ b/src/client/views/collections/CollectionTimeView.tsx
@@ -37,7 +37,7 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) {
     @observable _focusRangeFilters: Opt<string[]>;
 
     getAnchor = () => {
-        const anchor = Docs.Create.TextanchorDocument({
+        const anchor = Docs.Create.HTMLAnchorDocument({
             title: ComputedField.MakeFunction(`"${this.pivotField}"])`) as any,
             annotationOn: this.rootDoc
         });
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 35da09af6..8ef0057bd 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1487,7 +1487,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
             onDragOver={e => e.preventDefault()}
             onContextMenu={this.onContextMenu}
             style={{
-                pointerEvents: this.props.Document.type === DocumentType.HTMLANCHOR ? "none" :  // bcz: ugh.. this is here to prevent htmlanchor's, which render as freeform views, from grabbing events -- need a better approach.
+                pointerEvents: this.props.Document.type === DocumentType.MARKER ? "none" :  // bcz: ugh.. this is here to prevent markers, which render as freeform views, from grabbing events -- need a better approach.
                     this.backgroundEvents ? "all" : this.props.pointerEvents as any,
                 transform: `scale(${this.contentScaling || 1})`,
                 width: `${100 / (this.contentScaling || 1)}%`,
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index abc3a7d7d..f5b1f96f2 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -163,7 +163,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         if (this._sidebarRef?.current?.makeDocUnfiltered(doc)) return 1;
         if (doc !== this.rootDoc && this._outerRef.current) {
             const windowHeight = this.props.PanelHeight() / (this.props.scaling?.() || 1);
-            const scrollTo = doc.type === DocumentType.TEXTANCHOR ? NumCast(doc.y) : Utils.scrollIntoView(NumCast(doc.y), doc[HeightSym](), NumCast(this.layoutDoc._scrollTop), windowHeight, windowHeight * .1);
+            const scrollTo = Utils.scrollIntoView(NumCast(doc.y), doc[HeightSym](), NumCast(this.layoutDoc._scrollTop), windowHeight, windowHeight * .1);
             if (scrollTo !== undefined) {
                 const focusSpeed = smooth ? 500 : 0;
                 this._initialScroll !== undefined && (this._initialScroll = scrollTo);
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 7993af149..111fd3f0d 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -521,7 +521,7 @@ export namespace 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);
-        const fieldExclusions = (doc.type === DocumentType.TEXTANCHOR) ? exclusions.filter(ex => ex !== "annotationOn") : exclusions;
+        const fieldExclusions = doc.type === DocumentType.MARKER ? 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;
-- 
cgit v1.2.3-70-g09d2

