From 83d282a6b5e607e3c7dae4b5e5d37a8b458f81cc Mon Sep 17 00:00:00 2001
From: usodhi <61431818+usodhi@users.noreply.github.com>
Date: Sun, 24 May 2020 10:36:31 +0530
Subject: Added chrome to grid view and modified grid mechanism

---
 src/client/views/nodes/DocumentView.tsx | 1 +
 1 file changed, 1 insertion(+)

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

diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 340fa06a8..95af52f13 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -96,6 +96,7 @@ export interface DocumentViewProps {
     dontRegisterView?: boolean;
     layoutKey?: string;
     radialMenu?: String[];
+    display?: string;
 }
 
 @observer
-- 
cgit v1.2.3-70-g09d2


From ae552bf33e197bf18989a821f726f2d5670f6647 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Wed, 27 May 2020 13:53:18 -0400
Subject: fixed ineracting with documents selected in GridView

---
 src/client/views/collections/collectionGrid/CollectionGridView.tsx | 3 ++-
 src/client/views/collections/collectionGrid/Grid.tsx               | 3 +++
 src/client/views/nodes/DocumentView.tsx                            | 3 ++-
 3 files changed, 7 insertions(+), 2 deletions(-)

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

diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.tsx b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
index bc554e2c2..a55d5a8ac 100644
--- a/src/client/views/collections/collectionGrid/CollectionGridView.tsx
+++ b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
@@ -240,7 +240,7 @@ export class CollectionGridView extends CollectionSubView(GridSchema) {
                 style={{
                     marginLeft: NumCast(this.props.Document._xMargin), marginRight: NumCast(this.props.Document._xMargin),
                     marginTop: NumCast(this.props.Document._yMargin), marginBottom: NumCast(this.props.Document._yMargin),
-                    pointerEvents: !this.props.isSelected() && !this.props.ContainingCollectionView?._isChildActive && !SnappingManager.GetIsDragging() ? "none" : undefined
+                    pointerEvents: !this.props.isSelected() && this.props.renderDepth !== 0 && !this.props.ContainingCollectionView?._isChildActive && !SnappingManager.GetIsDragging() ? "none" : undefined
                 }}
                 ref={this.createDashEventsTarget}
                 onPointerDown={e => e.stopPropagation()}
@@ -249,6 +249,7 @@ export class CollectionGridView extends CollectionSubView(GridSchema) {
                     width={this.props.PanelWidth()}
                     nodeList={contents}
                     layout={layout}
+                    transformScale={this.props.ScreenToLocalTransform().Scale}
                     numCols={this.props.Document.numCols as number}
                     rowHeight={this.props.Document.rowHeight as number}
                     setLayout={(layout: Layout[]) => this.setLayout(layout)}
diff --git a/src/client/views/collections/collectionGrid/Grid.tsx b/src/client/views/collections/collectionGrid/Grid.tsx
index a5f5c724a..9c66d1e33 100644
--- a/src/client/views/collections/collectionGrid/Grid.tsx
+++ b/src/client/views/collections/collectionGrid/Grid.tsx
@@ -18,6 +18,7 @@ interface GridProps {
     numCols: number;
     rowHeight: number;
     setLayout: Function;
+    transformScale: number;
 }
 
 /**
@@ -35,6 +36,7 @@ export default class Grid extends React.Component<GridProps> {
         this.props.setLayout(layout);
     }
 
+    Scale = 2
     render() {
         console.log("In grid layout prop received value= " + this.props.layout?.[0]?.w);
         return (
@@ -45,6 +47,7 @@ export default class Grid extends React.Component<GridProps> {
                 width={this.props.width}
                 compactType={null}
                 isDroppable={true}
+                useCSSTransforms={true}
                 margin={[10, 10]}
                 onLayoutChange={layout => this.onLayoutChange(layout)}
             >
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index b5de0af12..79b560118 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -517,6 +517,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         if (!(InteractionUtils.IsType(e, InteractionUtils.MOUSETYPE) || InkingControl.Instance.selectedTool === InkTool.Highlighter || InkingControl.Instance.selectedTool === InkTool.Pen)) {
             if (!InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) {
                 e.stopPropagation();
+                e.preventDefault();
                 // TODO: check here for panning/inking
             }
             return;
@@ -531,7 +532,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 (e.button === 0 || InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) &&
                 !this.Document.inOverlay) {
                 e.stopPropagation(); // events stop at the lowest document that is active.  if right dragging, we let it go through though to allow for context menu clicks. PointerMove callbacks should remove themselves if the move event gets stopPropagated by a lower-level handler (e.g, marquee drag);
-
+                e.preventDefault();
             }
             document.removeEventListener("pointermove", this.onPointerMove);
             document.removeEventListener("pointerup", this.onPointerUp);
-- 
cgit v1.2.3-70-g09d2


From efaa44cacf9cf8506209c9bf1437e36828132eea Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Wed, 27 May 2020 15:17:01 -0400
Subject: fixed layout and interactions of grid view

---
 .../collectionGrid/CollectionGridView.scss         | 13 ++++----
 .../collectionGrid/CollectionGridView.tsx          | 37 ++++++++++------------
 src/client/views/nodes/DocumentView.tsx            |  6 ++--
 3 files changed, 26 insertions(+), 30 deletions(-)

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

diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.scss b/src/client/views/collections/collectionGrid/CollectionGridView.scss
index 8f12c1a24..c0a2cbc22 100644
--- a/src/client/views/collections/collectionGrid/CollectionGridView.scss
+++ b/src/client/views/collections/collectionGrid/CollectionGridView.scss
@@ -4,11 +4,10 @@
     width: 100%;
     height: 100%;
     flex-direction: column;
-
-    .document-wrapper {
-        display: flex;
-        flex-direction: column;
-        width: 100%;
-        height: 100%;
-    }
+}
+.collectionGridView_contents .document-wrapper {
+    display: flex;
+    flex-direction: column;
+    width: 100%;
+    height: 100%;
 }
\ No newline at end of file
diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.tsx b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
index a55d5a8ac..03ba9d004 100644
--- a/src/client/views/collections/collectionGrid/CollectionGridView.tsx
+++ b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
@@ -94,25 +94,22 @@ export class CollectionGridView extends CollectionSubView(GridSchema) {
     }
 
     getDisplayDoc(layout: Doc, dxf: () => Transform, width: () => number, height: () => number) {
-        return <div style={{ display: "flex" }}>
-            <ContentFittingDocumentView
-                {...this.props}
-                Document={layout}
-                DataDoc={layout.resolvedDataDoc as Doc}
-                NativeHeight={returnZero}
-                NativeWidth={returnZero}
-                addDocTab={this.addDocTab}
-                backgroundColor={this.props.backgroundColor}
-                ContainingCollectionDoc={this.props.Document}
-                PanelWidth={width}
-                PanelHeight={height}
-                ScreenToLocalTransform={dxf}
-                onClick={this.onChildClickHandler}
-                renderDepth={this.props.renderDepth + 1}
-                parentActive={this.props.active}
-                display={"contents"}
-            />
-        </div>;
+        return <ContentFittingDocumentView
+            {...this.props}
+            Document={layout}
+            DataDoc={layout.resolvedDataDoc as Doc}
+            NativeHeight={returnZero}
+            NativeWidth={returnZero}
+            addDocTab={this.addDocTab}
+            backgroundColor={this.props.backgroundColor}
+            ContainingCollectionDoc={this.props.Document}
+            PanelWidth={width}
+            PanelHeight={height}
+            ScreenToLocalTransform={dxf}
+            onClick={this.onChildClickHandler}
+            renderDepth={this.props.renderDepth + 1}
+            parentActive={this.props.active}
+        />;
     }
 
 
@@ -243,7 +240,7 @@ export class CollectionGridView extends CollectionSubView(GridSchema) {
                     pointerEvents: !this.props.isSelected() && this.props.renderDepth !== 0 && !this.props.ContainingCollectionView?._isChildActive && !SnappingManager.GetIsDragging() ? "none" : undefined
                 }}
                 ref={this.createDashEventsTarget}
-                onPointerDown={e => e.stopPropagation()}
+                onPointerDown={e => { ((e.target as any)?.className.includes("react-resizable-handle")) && e.stopPropagation(); }}
             >
                 <Grid
                     width={this.props.PanelWidth()}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 79b560118..6034a3cb7 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -517,7 +517,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         if (!(InteractionUtils.IsType(e, InteractionUtils.MOUSETYPE) || InkingControl.Instance.selectedTool === InkTool.Highlighter || InkingControl.Instance.selectedTool === InkTool.Pen)) {
             if (!InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) {
                 e.stopPropagation();
-                e.preventDefault();
+                if (SelectionManager.IsSelected(this, true) && this.props.Document._viewType !== CollectionViewType.Docking) e.preventDefault(); // goldenlayout needs to be able to move its tabs, so can't preventDefault for it
                 // TODO: check here for panning/inking
             }
             return;
@@ -531,8 +531,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 !e.ctrlKey &&
                 (e.button === 0 || InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) &&
                 !this.Document.inOverlay) {
-                e.stopPropagation(); // events stop at the lowest document that is active.  if right dragging, we let it go through though to allow for context menu clicks. PointerMove callbacks should remove themselves if the move event gets stopPropagated by a lower-level handler (e.g, marquee drag);
-                e.preventDefault();
+                e.stopPropagation();
+                if (SelectionManager.IsSelected(this, true) && this.props.Document._viewType !== CollectionViewType.Docking) e.preventDefault(); // goldenlayout needs to be able to move its tabs, so can't preventDefault for it
             }
             document.removeEventListener("pointermove", this.onPointerMove);
             document.removeEventListener("pointerup", this.onPointerUp);
-- 
cgit v1.2.3-70-g09d2


From 1daca67ea2a574b16c02b185d66f5724651ba235 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Wed, 10 Jun 2020 15:55:05 -0400
Subject: changed how ink is highlighted when selected.

---
 src/client/util/InteractionUtils.tsx               |  4 ++-
 src/client/views/GestureOverlay.tsx                |  2 ++
 src/client/views/InkingStroke.tsx                  | 35 ++++++++++++++++++----
 .../collections/collectionFreeForm/MarqueeView.tsx |  9 +++---
 .../views/nodes/CollectionFreeFormDocumentView.tsx |  3 +-
 src/client/views/nodes/DocumentView.tsx            |  2 +-
 6 files changed, 43 insertions(+), 12 deletions(-)

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

diff --git a/src/client/util/InteractionUtils.tsx b/src/client/util/InteractionUtils.tsx
index 3948611f0..81f9b9362 100644
--- a/src/client/util/InteractionUtils.tsx
+++ b/src/client/util/InteractionUtils.tsx
@@ -89,7 +89,7 @@ export namespace InteractionUtils {
         return myTouches;
     }
 
-    export function CreatePolyline(points: { X: number, Y: number }[], left: number, top: number, color: string, width: string, bezier: string, scalex: number, scaley: number, shape: string) {
+    export function CreatePolyline(points: { X: number, Y: number }[], left: number, top: number, color: string, width: string, bezier: string, scalex: number, scaley: number, shape: string, pevents: string, drawHalo: boolean) {
         var pts = "";
         if (shape) {
             //if any of the shape are true
@@ -120,7 +120,9 @@ export namespace InteractionUtils {
             <polyline
                 points={pts}
                 style={{
+                    filter: drawHalo ? "url(#dangerShine)" : undefined,
                     fill: "none",
+                    pointerEvents: pevents as any,
                     stroke: color ?? "rgb(0, 0, 0)",
                     strokeWidth: parseInt(width),
                     strokeLinejoin: "round",
diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx
index 3384b5fce..d239a1d6f 100644
--- a/src/client/views/GestureOverlay.tsx
+++ b/src/client/views/GestureOverlay.tsx
@@ -674,6 +674,8 @@ export default class GestureOverlay extends Touchable {
                     this._points = [];
                 }
             }
+        } else {
+            this._points = [];
         }
         document.removeEventListener("pointermove", this.onPointerMove);
         document.removeEventListener("pointerup", this.onPointerUp);
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index b545ede54..acbfde5a1 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -16,6 +16,7 @@ import { FieldView, FieldViewProps } from "./nodes/FieldView";
 import React = require("react");
 import { Scripting } from "../util/Scripting";
 import { Doc } from "../../fields/Doc";
+import { Id } from "../../fields/FieldSymbols";
 
 library.add(faPaintBrush);
 
@@ -44,15 +45,21 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocume
         const height = bottom - top;
         const scaleX = this.props.PanelWidth() / width;
         const scaleY = this.props.PanelHeight() / height;
+        const strokeWidth = Number(StrCast(this.layoutDoc.strokeWidth, ActiveInkWidth()));
+        const strokeColor = StrCast(this.layoutDoc.color, ActiveInkColor());
         const points = InteractionUtils.CreatePolyline(data, left, top,
-            StrCast(this.layoutDoc.color, ActiveInkColor()),
-            StrCast(this.layoutDoc.strokeWidth, ActiveInkWidth()),
-            StrCast(this.layoutDoc.strokeBezier, ActiveInkBezierApprox()), scaleX, scaleY, "");
+            strokeColor,
+            strokeWidth.toString(),
+            StrCast(this.layoutDoc.strokeBezier, ActiveInkBezierApprox()), scaleX, scaleY, "", "none", this.props.isSelected() && strokeWidth <= 5);
+        const hpoints = InteractionUtils.CreatePolyline(data, left, top,
+            this.props.isSelected() && strokeWidth > 5 ? strokeColor : "transparent",
+            (strokeWidth + 15).toString(),
+            StrCast(this.layoutDoc.strokeBezier, ActiveInkBezierApprox()), scaleX, scaleY, "", this.props.active() ? "visiblestroke" : "none", false);
         return (
             <svg className="inkingStroke"
                 width={width}
                 height={height}
-                style={{ mixBlendMode: this.layoutDoc.tool === InkTool.Highlighter ? "multiply" : "unset" }}
+                style={{ pointerEvents: "none", mixBlendMode: this.layoutDoc.tool === InkTool.Highlighter ? "multiply" : "unset" }}
                 onContextMenu={() => {
                     ContextMenu.Instance.addItem({
                         description: "Analyze Stroke",
@@ -60,7 +67,25 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocume
                         icon: "paint-brush"
                     });
                 }}
-            >
+            ><defs>
+                    <filter id="dangerShine">
+                        <feColorMatrix type="matrix"
+                            result="color"
+                            values="1 0 0 0 0
+                        0 0 0 0 0
+                        0 0 0 0 0
+                        0 0 0 1 0">
+                        </feColorMatrix>
+                        <feGaussianBlur in="color" stdDeviation="4" result="blur"></feGaussianBlur>
+                        <feOffset in="blur" dx="0" dy="0" result="offset"></feOffset>
+                        <feMerge>
+                            <feMergeNode in="bg"></feMergeNode>
+                            <feMergeNode in="offset"></feMergeNode>
+                            <feMergeNode in="SourceGraphic"></feMergeNode>
+                        </feMerge>
+                    </filter>
+                </defs>
+                {hpoints}
                 {points}
             </svg>
         );
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 97244ed06..b0461609a 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -1,7 +1,7 @@
 import { action, computed, observable } from "mobx";
 import { observer } from "mobx-react";
 import { Doc, Opt } from "../../../../fields/Doc";
-import { InkData, InkField } from "../../../../fields/InkField";
+import { InkData, InkField, InkTool } from "../../../../fields/InkField";
 import { List } from "../../../../fields/List";
 import { RichTextField } from "../../../../fields/RichTextField";
 import { SchemaHeaderField } from "../../../../fields/SchemaHeaderField";
@@ -271,10 +271,11 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
 
     @action
     onClick = (e: React.MouseEvent): void => {
-        if (
-            Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD &&
+        if (Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD &&
             Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD) {
-            !(e.nativeEvent as any).formattedHandled && this.setPreviewCursor(e.clientX, e.clientY, false);
+            if (Doc.GetSelectedTool() === InkTool.None) {
+                !(e.nativeEvent as any).formattedHandled && this.setPreviewCursor(e.clientX, e.clientY, false);
+            }
             // let the DocumentView stopPropagation of this event when it selects this document
         } else {  // why do we get a click event when the cursor have moved a big distance?
             // let's cut it off here so no one else has to deal with it.
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 910aa744d..509f78021 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -14,6 +14,7 @@ import { List } from "../../../fields/List";
 import { numberRange } from "../../../Utils";
 import { ComputedField } from "../../../fields/ScriptField";
 import { listSpec } from "../../../fields/Schema";
+import { DocumentType } from "../../documents/DocumentTypes";
 
 export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps {
     dataProvider?: (doc: Doc, replica: string) => { x: number, y: number, zIndex?: number, opacity?: number, highlight?: boolean, z: number, transition?: string } | undefined;
@@ -156,7 +157,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
                 zIndex: this.ZInd,
                 mixBlendMode: StrCast(this.layoutDoc.mixBlendMode) as any,
                 display: this.ZInd === -99 ? "none" : undefined,
-                pointerEvents: this.props.Document.isBackground || this.Opacity === 0 ? "none" : this.props.pointerEvents ? "all" : undefined
+                pointerEvents: this.props.Document.isBackground || this.Opacity === 0 || this.props.Document.type === DocumentType.INK ? "none" : this.props.pointerEvents ? "all" : undefined
             }} >
             {Doc.UserDoc().renderStyle !== "comic" ? (null) :
                 <div style={{ width: "100%", height: "100%", position: "absolute" }}>
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 98be1adc0..9f2b0124a 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1135,7 +1135,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             ["transparent", "#65350c", "#65350c", "yellow", "magenta", "cyan", "orange"] :
             ["transparent", "maroon", "maroon", "yellow", "magenta", "cyan", "orange"];
         const highlightStyles = ["solid", "dashed", "solid", "solid", "solid", "solid", "solid"];
-        let highlighting = fullDegree && this.layoutDoc.type !== DocumentType.FONTICON && this.layoutDoc._viewType !== CollectionViewType.Linear;
+        let highlighting = fullDegree && this.layoutDoc.type !== DocumentType.FONTICON && this.layoutDoc._viewType !== CollectionViewType.Linear && this.props.Document.type !== DocumentType.INK;
         highlighting = highlighting && this.props.focus !== emptyFunction;  // bcz: hack to turn off highlighting onsidebar panel documents.  need to flag a document as not highlightable in a more direct way
         return <div className={`documentView-node${this.topMost ? "-topmost" : ""}`}
             id={this.props.Document[Id]}
-- 
cgit v1.2.3-70-g09d2


From 279e71d098053dc2ca5b69e6e502a95dab230daa Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Mon, 15 Jun 2020 11:58:04 -0400
Subject: added a linkView and changed display of docFieldView to inline to
 allow text wrapping on the same line as the fieldLabel

---
 src/client/documents/Documents.ts                  |  4 +-
 src/client/util/CurrentUserUtils.ts                | 58 ++++++++++++++++++++--
 src/client/views/nodes/ComparisonBox.tsx           |  6 +--
 src/client/views/nodes/DocumentView.tsx            |  2 +-
 .../views/nodes/formattedText/DashFieldView.scss   |  2 +-
 .../views/nodes/formattedText/DashFieldView.tsx    | 19 ++++---
 6 files changed, 72 insertions(+), 19 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 2ecc8c8b7..002bfe6a2 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -606,7 +606,6 @@ export namespace Docs {
                 selection: { type: "text", anchor: 1, head: 1 },
                 storedMarks: []
             };
-
             const field = text ? new RichTextField(JSON.stringify(rtf), text) : undefined;
             return InstanceFromProto(Prototypes.get(DocumentType.RTF), field, options, undefined, fieldKey);
         }
@@ -823,7 +822,8 @@ export namespace DocUtils {
         if (sv && sv.props.ContainingCollectionDoc === target.doc) return;
         if (target.doc === Doc.UserDoc()) return undefined;
 
-        const linkDoc = Docs.Create.LinkDocument(source, target, { linkRelationship }, id);
+        const linkDoc = Docs.Create.LinkDocument(source, target, { linkRelationship, layoutKey: "layout_linkView" }, id);
+        linkDoc.layout_linkView = Cast(Cast(Doc.UserDoc()["template-button-link"], Doc, null).dragFactory, Doc, null);
         Doc.GetProto(linkDoc).title = ComputedField.MakeFunction('self.anchor1.title +" (" + (self.linkRelationship||"to") +") "  + self.anchor2.title');
 
         Doc.GetProto(source.doc).links = ComputedField.MakeFunction("links(self)");
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 0acfb72c6..47672f7c9 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -86,6 +86,52 @@ export class CurrentUserUtils {
             });
         }
 
+        if (doc["template-button-link"] === undefined) {
+            const linkTemplate = Docs.Create.TextDocument(" ", { title: "header", _height: 100 }, "header"); // text needs to be a space to allow templateText to be created
+            Doc.GetProto(linkTemplate).layout =
+                "<div>" +
+                "    <FormattedTextBox {...props} height='{this._headerHeight||75}px' background='{this._headerColor||`lightBlue`}' fieldKey={'header'}/>" +
+                "    <FormattedTextBox {...props} position='absolute' top='{(this._headerHeight||75)*scale}px' height='calc({100/scale}% - {this._headerHeight||75}px)' fieldKey={'text'}/>" +
+                "</div>";
+            linkTemplate.isTemplateDoc = makeTemplate(linkTemplate, true, "linkView");
+
+            const rtf2 = {
+                doc: {
+                    type: "doc", content: [
+                        {
+                            type: "paragraph",
+                            content: [{
+                                type: "dashField",
+                                attrs: {
+                                    fieldKey: "src",
+                                    hideKey: false
+                                }
+                            }]
+                        },
+                        { type: "paragraph" },
+                        {
+                            type: "paragraph",
+                            content: [{
+                                type: "dashField",
+                                attrs: {
+                                    fieldKey: "dst",
+                                    hideKey: false
+                                }
+                            }]
+                        }]
+                },
+                selection: { type: "text", anchor: 1, head: 1 },
+                storedMarks: []
+            };
+            linkTemplate.header = new RichTextField(JSON.stringify(rtf2), "");
+
+            doc["template-button-link"] = CurrentUserUtils.ficon({
+                onDragStart: ScriptField.MakeFunction('makeDelegate(this.dragFactory)'),
+                dragFactory: new PrefetchProxy(linkTemplate) as any as Doc,
+                removeDropProperties: new List<string>(["dropAction"]), title: "link view", icon: "window-maximize"
+            });
+        }
+
         if (doc["template-button-switch"] === undefined) {
             const { FreeformDocument, MulticolumnDocument, TextDocument } = Docs.Create;
 
@@ -168,17 +214,21 @@ export class CurrentUserUtils {
             });
         }
 
+        const requiredTypes = [
+            doc["template-button-slides"] as Doc,
+            doc["template-button-description"] as Doc,
+            doc["template-button-query"] as Doc,
+            doc["template-button-detail"] as Doc,
+            doc["template-button-link"] as Doc,
+            doc["template-button-switch"] as Doc];
         if (doc["template-buttons"] === undefined) {
-            doc["template-buttons"] = new PrefetchProxy(Docs.Create.MasonryDocument([doc["template-button-slides"] as Doc, doc["template-button-description"] as Doc,
-            doc["template-button-query"] as Doc, doc["template-button-detail"] as Doc, doc["template-button-switch"] as Doc], {
+            doc["template-buttons"] = new PrefetchProxy(Docs.Create.MasonryDocument(requiredTypes, {
                 title: "Advanced Item Prototypes", _xMargin: 0, _showTitle: "title",
                 _autoHeight: true, _width: 500, columnWidth: 35, ignoreClick: true, lockedPosition: true, _chromeStatus: "disabled",
                 dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }),
             }));
         } else {
             const curButnTypes = Cast(doc["template-buttons"], Doc, null);
-            const requiredTypes = [doc["template-button-slides"] as Doc, doc["template-button-description"] as Doc,
-            doc["template-button-query"] as Doc, doc["template-button-detail"] as Doc, doc["template-button-switch"] as Doc];
             DocListCastAsync(curButnTypes.data).then(async curBtns => {
                 await Promise.all(curBtns!);
                 requiredTypes.map(btype => Doc.AddDocToList(curButnTypes, "data", btype));
diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx
index cce60628d..14a2c25bf 100644
--- a/src/client/views/nodes/ComparisonBox.tsx
+++ b/src/client/views/nodes/ComparisonBox.tsx
@@ -76,12 +76,12 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps, C
         const clearButton = (which: string) => {
             return <div className={`clear-button ${which}`}
                 onPointerDown={e => e.stopPropagation()} // prevent triggering slider movement in registerSliding 
-                onClick={e => this.clearDoc(e, `${which}Doc`)}>
+                onClick={e => this.clearDoc(e, `compareBox-${which}`)}>
                 <FontAwesomeIcon className={`clear-button ${which}`} icon={"times"} size="sm" />
             </div>;
         };
         const displayDoc = (which: string) => {
-            const whichDoc = Cast(this.dataDoc[`${which}Doc`], Doc, null);
+            const whichDoc = Cast(this.dataDoc[`compareBox-${which}`], Doc, null);
             return whichDoc ? <>
                 <ContentFittingDocumentView {...childProps} Document={whichDoc} />
                 {clearButton(which)}
@@ -93,7 +93,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps, C
         const displayBox = (which: string, index: number, cover: number) => {
             return <div className={`${which}Box-cont`} key={which} style={{ width: this.props.PanelWidth() }}
                 onPointerDown={e => this.registerSliding(e, cover)}
-                ref={ele => this.createDropTarget(ele, `${which}Doc`, index)} >
+                ref={ele => this.createDropTarget(ele, `compareBox-${which}`, index)} >
                 {displayDoc(which)}
             </div>;
         };
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index e4e4dfdd6..35bf5344e 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -335,7 +335,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 this.props.addDocTab(alias, "onRight");
                 // UndoManager.RunInBatch(() => Doc.makeCustomViewClicked(this.props.Document, undefined, "onClick"), "edit onClick");
                 //ScriptBox.EditButtonScript("On Button Clicked ...", this.props.Document, "onClick", e.clientX, e.clientY), "on button click");
-            } else if (this.Document.isLinkButton && !e.shiftKey && !e.ctrlKey) {
+            } else if (this.props.Document.links && this.Document.isLinkButton && !e.shiftKey && !e.ctrlKey) {
                 DocListCast(this.props.Document.links).length && this.followLinkClick(e.altKey, e.ctrlKey, e.shiftKey);
             } else {
                 if ((this.props.Document.onDragStart || (this.props.Document.rootDocument)) && !(e.ctrlKey || e.button > 0)) {  // onDragStart implies a button doc that we don't want to select when clicking.   RootDocument & isTEmplaetForField implies we're clicking on part of a template instance and we want to select the whole template, not the part
diff --git a/src/client/views/nodes/formattedText/DashFieldView.scss b/src/client/views/nodes/formattedText/DashFieldView.scss
index 35ff9c1e6..23cf1e79b 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.scss
+++ b/src/client/views/nodes/formattedText/DashFieldView.scss
@@ -25,7 +25,7 @@
         margin-left: 2px;
         margin-right: 5px;
         position: relative;
-        display: inline-block;
+        display: inline;
         background-color: rgba(155, 155, 155, 0.24);
         span {
             min-width: 100%;
diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx
index 9a1b909c1..60a9c2a27 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.tsx
+++ b/src/client/views/nodes/formattedText/DashFieldView.tsx
@@ -103,11 +103,14 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
                 // bcz: this is unfortunate, but since this React component is nested within a non-React text box (prosemirror), we can't
                 // use React events.  Essentially, React events occur after native events have been processed, so corresponding React events
                 // will never fire because Prosemirror has handled the native events.  So we add listeners for native events here.
-                return <span contentEditable={true} suppressContentEditableWarning={true} defaultValue={strVal} ref={r => {
-                    r?.addEventListener("keydown", e => this.fieldSpanKeyDown(e, r));
-                    r?.addEventListener("blur", e => r && this.updateText(r.textContent!, false));
-                    r?.addEventListener("pointerdown", action((e) => this._showEnumerables = true));
-                }} >
+                return <span className="dashFieldView-fieldSpan" contentEditable={true}
+                    style={{ display: strVal.length < 2 ? "inline-block" : undefined }}
+                    suppressContentEditableWarning={true} defaultValue={strVal}
+                    ref={r => {
+                        r?.addEventListener("keydown", e => this.fieldSpanKeyDown(e, r));
+                        r?.addEventListener("blur", e => r && this.updateText(r.textContent!, false));
+                        r?.addEventListener("pointerdown", action((e) => this._showEnumerables = true));
+                    }} >
                     {strVal}
                 </span>;
             }
@@ -205,9 +208,9 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
                     {this._fieldKey}
                 </span>}
 
-            <div className="dashFieldView-fieldSpan">
-                {this.fieldValueContent}
-            </div>
+            {/* <div className="dashFieldView-fieldSpan"> */}
+            {this.fieldValueContent}
+            {/* </div> */}
 
             {!this._showEnumerables ? (null) : <div className="dashFieldView-enumerables" onPointerDown={this.onPointerDownEnumerables} />}
 
-- 
cgit v1.2.3-70-g09d2


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

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

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

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


From 163e1f579d5a6b111645953b12e665839694585c Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Mon, 15 Jun 2020 18:22:14 -0400
Subject: fixed testAcl.   added testReadonly

---
 src/client/views/nodes/DocumentView.tsx | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

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

diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 8a067b806..d1877c3a0 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -700,9 +700,15 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         this.dataDoc.author = this.props.Document.author = "ADMIN";
         this.dataDoc.ACL = this.props.Document.ACL = acl;
         DocListCast(this.dataDoc[Doc.LayoutFieldKey(this.dataDoc)]).map(d => {
-            if (d.author === Doc.CurrentUserEmail) d.ACL = acl;
+            if (d.author === Doc.CurrentUserEmail) {
+                d.author = "ADMIN";
+                d.ACL = acl;
+            }
             const data = d[DataSym];
-            if (data && data.author === Doc.CurrentUserEmail) data.ACL = acl;
+            if (data && data.author === Doc.CurrentUserEmail) {
+                data.author = "ADMIN";
+                data.ACL = acl;
+            }
         });
     }
 
@@ -770,6 +776,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         moreItems.push({ description: "Make Read Only", event: () => this.setAcl("readOnly"), icon: "concierge-bell" });
         moreItems.push({ description: "Make Private", event: () => this.setAcl("ownerOnly"), icon: "concierge-bell" });
         moreItems.push({ description: "Test Private", event: () => this.testAcl("ownerOnly"), icon: "concierge-bell" });
+        moreItems.push({ description: "Test Readonly", event: () => this.testAcl("readOnly"), icon: "concierge-bell" });
         moreItems.push({ description: "Make View of Metadata Field", event: () => Doc.MakeMetadataFieldTemplate(this.props.Document, this.props.DataDoc), icon: "concierge-bell" });
         moreItems.push({ description: `${this.Document._chromeStatus !== "disabled" ? "Hide" : "Show"} Chrome`, event: () => this.Document._chromeStatus = (this.Document._chromeStatus !== "disabled" ? "disabled" : "enabled"), icon: "project-diagram" });
         moreItems.push({ description: this.Document.lockedPosition ? "Unlock Position" : "Lock Position", event: this.toggleLockPosition, icon: BoolCast(this.Document.lockedPosition) ? "unlock" : "lock" });
-- 
cgit v1.2.3-70-g09d2

