From 5fdd55e5e293835f6902b0f3c2cbcb054dc27747 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Fri, 17 Apr 2020 18:02:23 -0400
Subject: added more annotation ability to web pages.  need a mode switch to
 follow links/select text now.

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

(limited to 'src/client/views/pdf/PDFViewer.tsx')

diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index c49e6512a..948d2300d 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -419,7 +419,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
         addStyleSheetRule(PDFViewer._annotationStyle, "pdfAnnotation", { "pointer-events": "none" });
         if ((this.Document.scale || 1) !== 1) return;
         if ((e.button !== 0 || e.altKey) && this.active(true)) {
-            this._setPreviewCursor && this._setPreviewCursor(e.clientX, e.clientY, true);
+            this._setPreviewCursor?.(e.clientX, e.clientY, true);
             //e.stopPropagation();
         }
         this._marqueeing = false;
-- 
cgit v1.2.3-70-g09d2


From d3aebc8404685496673a8611a6d72ff7695ad191 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Sat, 18 Apr 2020 13:42:03 -0400
Subject: several pointerevent fixes broken by last change.  added scroll to
 target for webBox links.

---
 src/client/util/DocumentManager.ts                 |  13 ++-
 .../views/collections/CollectionTreeView.tsx       |   1 -
 .../collectionFreeForm/CollectionFreeFormView.tsx  |   6 +-
 .../views/nodes/CollectionFreeFormDocumentView.tsx |   1 -
 src/client/views/nodes/DocumentView.scss           |  10 --
 src/client/views/nodes/DocumentView.tsx            | 118 ++++++++++-----------
 src/client/views/nodes/FormattedTextBoxComment.tsx |   4 +-
 src/client/views/nodes/LinkAnchorBox.scss          |   1 +
 src/client/views/nodes/LinkAnchorBox.tsx           |   2 +
 src/client/views/nodes/LinkBox.tsx                 |   1 -
 src/client/views/nodes/WebBox.tsx                  |  22 ++--
 src/client/views/pdf/Annotation.tsx                |   4 +-
 src/client/views/pdf/PDFViewer.tsx                 |   8 +-
 13 files changed, 100 insertions(+), 91 deletions(-)

(limited to 'src/client/views/pdf/PDFViewer.tsx')

diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 2d6078cf3..e66e67723 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -156,7 +156,12 @@ export class DocumentManager {
         let annotatedDoc = await Cast(targetDoc.annotationOn, Doc);
         if (annotatedDoc) {
             const first = getFirstDocView(annotatedDoc);
-            if (first) annotatedDoc = first.props.Document;
+            if (first) {
+                annotatedDoc = first.props.Document;
+                if (docView) {
+                    docView.props.focus(annotatedDoc, false);
+                }
+            }
         }
         if (docView) {  // we have a docView already and aren't forced to create a new one ... just focus on the document.  TODO move into view if necessary otherwise just highlight?
             docView.props.focus(docView.props.Document, willZoom, undefined, focusAndFinish);
@@ -219,9 +224,9 @@ export class DocumentManager {
         if (linkDoc) {
             const target = (doc === linkDoc.anchor1 ? linkDoc.anchor2 : doc === linkDoc.anchor2 ? linkDoc.anchor1 :
                 (Doc.AreProtosEqual(doc, linkDoc.anchor1 as Doc) ? linkDoc.anchor2 : linkDoc.anchor1)) as Doc;
-            const targetTimecode = (doc === linkDoc.anchor1 ? Cast(linkDoc.anchor2_timecode, "number") : 
-                                    doc === linkDoc.anchor2 ? Cast(linkDoc.anchor1_timecode, "number"):
-                (Doc.AreProtosEqual(doc, linkDoc.anchor1 as Doc) ? Cast(linkDoc.anchor2_timecode, "number"):Cast(linkDoc.anchor1_timecode, "number")));
+            const targetTimecode = (doc === linkDoc.anchor1 ? Cast(linkDoc.anchor2_timecode, "number") :
+                doc === linkDoc.anchor2 ? Cast(linkDoc.anchor1_timecode, "number") :
+                    (Doc.AreProtosEqual(doc, linkDoc.anchor1 as Doc) ? Cast(linkDoc.anchor2_timecode, "number") : Cast(linkDoc.anchor1_timecode, "number")));
             if (target) {
                 const containerDoc = (await Cast(target.annotationOn, Doc)) || target;
                 containerDoc.currentTimecode = targetTimecode;
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 510c9924b..5ff8c11ee 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -145,7 +145,6 @@ class TreeView extends React.Component<TreeViewProps> {
         ele && (this._treedropDisposer = DragManager.MakeDropTarget(ele, this.treeDrop.bind(this)));
     }
 
-    onPointerDown = (e: React.PointerEvent) => e.stopPropagation();
     onPointerEnter = (e: React.PointerEvent): void => {
         this.props.active(true) && Doc.BrushDoc(this.dataDoc);
         if (e.buttons === 1 && SelectionManager.GetIsDragging()) {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 02f9bd487..b3409bd57 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -804,7 +804,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument, u
             if (!annotOn) {
                 this.props.focus(doc);
             } else {
-                const contextHgt = Doc.AreProtosEqual(annotOn, this.props.Document) && this.props.VisibleHeight ? this.props.VisibleHeight() : NumCast(annotOn.height);
+                const contextHgt = Doc.AreProtosEqual(annotOn, this.props.Document) && this.props.VisibleHeight ? this.props.VisibleHeight() : NumCast(annotOn._height);
                 const offset = annotOn && (contextHgt / 2 * 96 / 72);
                 this.props.Document.scrollY = NumCast(doc.y) - offset;
             }
@@ -820,7 +820,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument, u
 
             const savedState = { px: this.Document._panX, py: this.Document._panY, s: this.Document.scale, pt: this.Document.panTransformType };
 
-            if (!willZoom) {
+            if (!willZoom && DocumentView._focusHack.length) {
                 Doc.BrushDoc(this.props.Document);
                 !doc.z && this.scaleAtPt(DocumentView._focusHack, 1); // [NumCast(doc.x), NumCast(doc.y)], 1);
             } else {
@@ -995,7 +995,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument, u
                     {...this.getChildDocumentViewProps(pair.layout, pair.data)}
                     dataProvider={this.childDataProvider}
                     LayoutDoc={this.childLayoutDocFunc}
-                    pointerEvents={this.props.layoutEngine?.() !== undefined ? "none" : undefined}
+                    pointerEvents={this.props.layoutEngine?.() !== undefined ? false : undefined}
                     jitterRotation={NumCast(this.props.Document.jitterRotation)}
                     fitToBox={this.props.fitToBox || BoolCast(this.props.freezeChildDimensions)}
                     FreezeDimensions={BoolCast(this.props.freezeChildDimensions)}
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 05ad98c43..e325a70c0 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -22,7 +22,6 @@ export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps {
     width?: number;
     height?: number;
     jitterRotation: number;
-    pointerEvents?: "none";
     transition?: string;
     fitToBox?: boolean;
 }
diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss
index fc9ee1201..81ae36cc0 100644
--- a/src/client/views/nodes/DocumentView.scss
+++ b/src/client/views/nodes/DocumentView.scss
@@ -38,16 +38,6 @@
         display:flex;
         overflow: hidden;
     }
-    .documentView-linkAnchorBoxWrapper {
-        pointer-events: none;
-        position: absolute;
-        transform-origin: top left;
-        width: 100%;
-        height: 100%;
-        top:0;
-        left:0;
-        z-index: 1;
-    }
 
     .documentView-lock {
         width: 20; 
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 2f22488e4..2bae2fa96 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -80,6 +80,7 @@ export interface DocumentViewProps {
     ContentScaling: () => number;
     PanelWidth: () => number;
     PanelHeight: () => number;
+    pointerEvents?: boolean;
     focus: (doc: Doc, willZoom: boolean, scale?: number, afterFocus?: DocFocusFunc) => void;
     parentActive: (outsideReaction: boolean) => boolean;
     whenActiveChanged: (isActive: boolean) => void;
@@ -994,38 +995,42 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     screenToLocalTransform = () => this.props.ScreenToLocalTransform();
     @computed get contents() {
         TraceMobx();
-        return (<DocumentContentsView ContainingCollectionView={this.props.ContainingCollectionView}
-            ContainingCollectionDoc={this.props.ContainingCollectionDoc}
-            NativeWidth={this.NativeWidth}
-            NativeHeight={this.NativeHeight}
-            Document={this.props.Document}
-            DataDoc={this.props.DataDoc}
-            LayoutDoc={this.props.LayoutDoc}
-            makeLink={this.makeLink}
-            rootSelected={this.rootSelected}
-            dontRegisterView={this.props.dontRegisterView}
-            fitToBox={this.props.fitToBox}
-            LibraryPath={this.props.LibraryPath}
-            addDocument={this.props.addDocument}
-            removeDocument={this.props.removeDocument}
-            moveDocument={this.props.moveDocument}
-            ScreenToLocalTransform={this.screenToLocalTransform}
-            renderDepth={this.props.renderDepth}
-            PanelWidth={this.panelWidth}
-            PanelHeight={this.panelHeight}
-            focus={this.props.focus}
-            parentActive={this.props.parentActive}
-            whenActiveChanged={this.props.whenActiveChanged}
-            bringToFront={this.props.bringToFront}
-            addDocTab={this.props.addDocTab}
-            pinToPres={this.props.pinToPres}
-            backgroundColor={this.props.backgroundColor}
-            ContentScaling={this.childScaling}
-            ChromeHeight={this.chromeHeight}
-            isSelected={this.isSelected}
-            select={this.select}
-            onClick={this.onClickHandler}
-            layoutKey={this.finalLayoutKey} />);
+        return (<>
+            <DocumentContentsView key={1} ContainingCollectionView={this.props.ContainingCollectionView}
+                ContainingCollectionDoc={this.props.ContainingCollectionDoc}
+                NativeWidth={this.NativeWidth}
+                NativeHeight={this.NativeHeight}
+                Document={this.props.Document}
+                DataDoc={this.props.DataDoc}
+                LayoutDoc={this.props.LayoutDoc}
+                makeLink={this.makeLink}
+                rootSelected={this.rootSelected}
+                dontRegisterView={this.props.dontRegisterView}
+                fitToBox={this.props.fitToBox}
+                LibraryPath={this.props.LibraryPath}
+                addDocument={this.props.addDocument}
+                removeDocument={this.props.removeDocument}
+                moveDocument={this.props.moveDocument}
+                ScreenToLocalTransform={this.screenToLocalTransform}
+                renderDepth={this.props.renderDepth}
+                PanelWidth={this.panelWidth}
+                PanelHeight={this.panelHeight}
+                focus={this.props.focus}
+                parentActive={this.props.parentActive}
+                whenActiveChanged={this.props.whenActiveChanged}
+                bringToFront={this.props.bringToFront}
+                addDocTab={this.props.addDocTab}
+                pinToPres={this.props.pinToPres}
+                backgroundColor={this.props.backgroundColor}
+                ContentScaling={this.childScaling}
+                ChromeHeight={this.chromeHeight}
+                isSelected={this.isSelected}
+                select={this.select}
+                onClick={this.onClickHandler}
+                layoutKey={this.finalLayoutKey} />
+            {this.anchors}
+        </>
+        );
     }
     linkEndpoint = (linkDoc: Doc) => Doc.LinkEndpoint(linkDoc, this.props.Document);
 
@@ -1049,20 +1054,19 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     @computed get anchors() {
         TraceMobx();
         return this.layoutDoc.presBox ? (null) : DocListCast(this.Document.links).filter(d => !d.hidden && this.isNonTemporalLink).map((d, i) =>
-            <div className="documentView-linkAnchorBoxWrapper" key={d[Id]}>
-                <DocumentView {...this.props}
-                    Document={d}
-                    ContainingCollectionView={this.props.ContainingCollectionView}
-                    ContainingCollectionDoc={this.props.Document} // bcz: hack this.props.Document is not a collection  Need a better prop for passing the containing document to the LinkAnchorBox
-                    PanelWidth={this.anchorPanelWidth}
-                    PanelHeight={this.anchorPanelHeight}
-                    layoutKey={this.linkEndpoint(d)}
-                    ContentScaling={returnOne}
-                    backgroundColor={returnTransparent}
-                    removeDocument={this.hideLinkAnchor}
-                    LayoutDoc={undefined}
-                />
-            </div>);
+            <DocumentView {...this.props} key={i + 1}
+                Document={d}
+                ContainingCollectionView={this.props.ContainingCollectionView}
+                ContainingCollectionDoc={this.props.Document} // bcz: hack this.props.Document is not a collection  Need a better prop for passing the containing document to the LinkAnchorBox
+                PanelWidth={this.anchorPanelWidth}
+                PanelHeight={this.anchorPanelHeight}
+                layoutKey={this.linkEndpoint(d)}
+                ContentScaling={returnOne}
+                backgroundColor={returnTransparent}
+                removeDocument={this.hideLinkAnchor}
+                pointerEvents={false}
+                LayoutDoc={undefined}
+            />);
     }
     @computed get innards() {
         TraceMobx();
@@ -1101,22 +1105,18 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                     SetValue={undoBatch((value: string) => (Doc.GetProto(this.props.DataDoc || this.props.Document)[showTitle] = value) ? true : true)}
                 />
             </div>);
-        return <>
-            {this.anchors}
-            {!showTitle && !showCaption ?
-                this.contents :
-                <div className="documentView-styleWrapper" >
-                    <div className="documentView-styleContentWrapper" style={{ height: showTextTitle ? `calc(100% - ${this.chromeHeight()}px)` : "100%", top: showTextTitle ? this.chromeHeight() : undefined }}>
-                        {this.contents}
-                    </div>
-                    {titleView}
-                    {captionView}
+        return !showTitle && !showCaption ?
+            this.contents :
+            <div className="documentView-styleWrapper" >
+                <div className="documentView-styleContentWrapper" style={{ height: showTextTitle ? `calc(100% - ${this.chromeHeight()}px)` : "100%", top: showTextTitle ? this.chromeHeight() : undefined }}>
+                    {this.contents}
                 </div>
-            }
-        </>;
+                {titleView}
+                {captionView}
+            </div>;
     }
     @computed get ignorePointerEvents() {
-        return (this.Document.isBackground && !this.isSelected() && !SelectionManager.GetIsDragging()) || this.props.layoutKey?.includes("layout_key") || (this.Document.type === DocumentType.INK && InkingControl.Instance.selectedTool !== InkTool.None);
+        return this.props.pointerEvents === false || (this.Document.isBackground && !this.isSelected() && !SelectionManager.GetIsDragging()) || (this.Document.type === DocumentType.INK && InkingControl.Instance.selectedTool !== InkTool.None);
     }
 
     @observable _animate = 0;
diff --git a/src/client/views/nodes/FormattedTextBoxComment.tsx b/src/client/views/nodes/FormattedTextBoxComment.tsx
index 35304033f..41df5b3c1 100644
--- a/src/client/views/nodes/FormattedTextBoxComment.tsx
+++ b/src/client/views/nodes/FormattedTextBoxComment.tsx
@@ -16,6 +16,7 @@ import React = require("react");
 import { Docs } from "../../documents/Documents";
 import wiki from "wikijs";
 import { DocumentType } from "../../documents/DocumentTypes";
+import { DocumentView } from "./DocumentView";
 
 export let formattedTextBoxCommentPlugin = new Plugin({
     view(editorView) { return new FormattedTextBoxComment(editorView); }
@@ -85,8 +86,9 @@ export class FormattedTextBoxComment {
                 const textBox = FormattedTextBoxComment.textBox;
                 if (FormattedTextBoxComment.linkDoc && !keep && textBox) {
                     if (FormattedTextBoxComment.linkDoc.type !== DocumentType.LINK) {
-                        textBox.props.addDocTab(FormattedTextBoxComment.linkDoc, e.ctrlKey ? "inTab":"onRight");
+                        textBox.props.addDocTab(FormattedTextBoxComment.linkDoc, e.ctrlKey ? "inTab" : "onRight");
                     } else {
+                        DocumentView._focusHack = [];
                         DocumentManager.Instance.FollowLink(FormattedTextBoxComment.linkDoc, textBox.props.Document,
                             (doc: Doc, followLinkLocation: string) => textBox.props.addDocTab(doc, e.ctrlKey ? "inTab" : followLinkLocation));
                     }
diff --git a/src/client/views/nodes/LinkAnchorBox.scss b/src/client/views/nodes/LinkAnchorBox.scss
index 24f9c1ea0..710f2178b 100644
--- a/src/client/views/nodes/LinkAnchorBox.scss
+++ b/src/client/views/nodes/LinkAnchorBox.scss
@@ -5,6 +5,7 @@
     height: 15;
     border-radius: 20px;
     user-select: none;
+    pointer-events: all;
 
     .linkAnchorBox-linkCloser {
         position: absolute;
diff --git a/src/client/views/nodes/LinkAnchorBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx
index 13ffc6956..3b1ced815 100644
--- a/src/client/views/nodes/LinkAnchorBox.tsx
+++ b/src/client/views/nodes/LinkAnchorBox.tsx
@@ -17,6 +17,7 @@ import { LinkEditor } from "../linking/LinkEditor";
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { SelectionManager } from "../../util/SelectionManager";
 import { TraceMobx } from "../../../new_fields/util";
+import { DocumentView } from "./DocumentView";
 const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
@@ -74,6 +75,7 @@ export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps, LinkAnch
             anchorContainerDoc && this.props.bringToFront(anchorContainerDoc, false);
             if (anchorContainerDoc && !this.layoutDoc.onClick && !this._isOpen) {
                 this._timeout = setTimeout(action(() => {
+                    DocumentView._focusHack = [];
                     DocumentManager.Instance.FollowLink(this.rootDoc, anchorContainerDoc, document => this.props.addDocTab(document, StrCast(this.layoutDoc.linkOpenLocation, "inTab")), false);
                     this._editing = false;
                 }), 300 - (Date.now() - this._lastTap));
diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx
index af4bf420f..740f2ef04 100644
--- a/src/client/views/nodes/LinkBox.tsx
+++ b/src/client/views/nodes/LinkBox.tsx
@@ -17,7 +17,6 @@ export class LinkBox extends ViewBoxBaseComponent<FieldViewProps, LinkDocument>(
     public static LayoutString(fieldKey: string) { return FieldView.LayoutString(LinkBox, fieldKey); }
     render() {
         return <div className={`linkBox-container${this.active() ? "-interactive" : ""}`}
-            onPointerDown={e => e.button === 0 && !e.ctrlKey && e.stopPropagation()}
             style={{ background: this.props.backgroundColor?.(this.props.Document) }} >
 
             <CollectionTreeView {...this.props}
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 26e947f5b..510e6be0c 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -1,6 +1,6 @@
 import { library } from "@fortawesome/fontawesome-svg-core";
 import { faStickyNote, faLock, faUnlock } from '@fortawesome/free-solid-svg-icons';
-import { action, computed, observable, trace } from "mobx";
+import { action, computed, observable, trace, IReactionDisposer, reaction } from "mobx";
 import { observer } from "mobx-react";
 import { Doc, FieldResult } from "../../../new_fields/Doc";
 import { documentSchema } from "../../../new_fields/documentSchemas";
@@ -13,7 +13,6 @@ import { Utils, returnOne, emptyFunction, returnZero } from "../../../Utils";
 import { Docs } from "../../documents/Documents";
 import { DragManager } from "../../util/DragManager";
 import { ImageUtils } from "../../util/Import & Export/ImageUtils";
-import { SelectionManager } from "../../util/SelectionManager";
 import { ViewBoxAnnotatableComponent } from "../DocComponent";
 import { DocumentDecorations } from "../DocumentDecorations";
 import { InkingControl } from "../InkingControl";
@@ -37,15 +36,15 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
     public static LayoutString(fieldKey: string) { return FieldView.LayoutString(WebBox, fieldKey); }
     @observable private collapsed: boolean = true;
     @observable private url: string = "hello";
+    @observable private _pressX: number = 0;
+    @observable private _pressY: number = 0;
 
     private _longPressSecondsHack?: NodeJS.Timeout;
     private _outerRef = React.createRef<HTMLDivElement>();
     private _iframeRef = React.createRef<HTMLIFrameElement>();
     private _iframeIndicatorRef = React.createRef<HTMLDivElement>();
     private _iframeDragRef = React.createRef<HTMLDivElement>();
-    @observable private _pressX: number = 0;
-    @observable private _pressY: number = 0;
-    private _scrollTop = 0;
+    private _reactionDisposer?: IReactionDisposer;
     private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean) => void);
 
     iframeLoaded = action((e: any) => {
@@ -53,6 +52,16 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
         this._iframeRef.current!.contentDocument?.addEventListener('scroll', this.iframeScrolled, false);
         this.layoutDoc.scrollHeight = this._iframeRef.current!.contentDocument?.children?.[0].scrollHeight || 1000;
         this._iframeRef.current!.contentDocument!.children[0].scrollTop = NumCast(this.layoutDoc.scrollTop);
+        this._reactionDisposer?.();
+        this._reactionDisposer = reaction(() => this.layoutDoc.scrollY,
+            (scrollY) => {
+                if (scrollY !== undefined) {
+                    this._outerRef.current!.scrollTop = scrollY;
+                    this.layoutDoc.scrollY = undefined;
+                }
+            },
+            { fireImmediately: true }
+        );
     });
     setPreviewCursor = (func?: (x: number, y: number, drag: boolean) => void) => this._setPreviewCursor = func;
     iframedown = (e: PointerEvent) => {
@@ -60,7 +69,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
     }
     iframeScrolled = (e: any) => {
         const scroll = (e.target as any)?.children?.[0].scrollTop;
-        this.layoutDoc.scrollTop = this._outerRef.current!.scrollTop = this._scrollTop = scroll;
+        this.layoutDoc.scrollTop = this._outerRef.current!.scrollTop = scroll;
     }
     async componentDidMount() {
 
@@ -87,6 +96,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
     }
 
     componentWillUnmount() {
+        this._reactionDisposer?.();
         document.removeEventListener("pointerup", this.onLongPressUp);
         document.removeEventListener("pointermove", this.onLongPressMove);
         this._iframeRef.current!.contentDocument?.removeEventListener('pointerdown', this.iframedown);
diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx
index 71b19f3a6..eaf80d252 100644
--- a/src/client/views/pdf/Annotation.tsx
+++ b/src/client/views/pdf/Annotation.tsx
@@ -8,6 +8,7 @@ import { Cast, FieldValue, NumCast, StrCast } from "../../../new_fields/Types";
 import { DocumentManager } from "../../util/DocumentManager";
 import PDFMenu from "./PDFMenu";
 import "./Annotation.scss";
+import { DocumentView } from "../nodes/DocumentView";
 
 interface IAnnotationProps {
     anno: Doc;
@@ -97,7 +98,8 @@ class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
         else if (e.button === 0) {
             const annoGroup = await Cast(this.props.document.group, Doc);
             if (annoGroup) {
-                DocumentManager.Instance.FollowLink(undefined, annoGroup, (doc, followLinkLocation) => this.props.addDocTab(doc, e.ctrlKey ? "inTab" : followLinkLocation), false,  undefined);
+                DocumentView._focusHack = [];
+                DocumentManager.Instance.FollowLink(undefined, annoGroup, (doc, followLinkLocation) => this.props.addDocTab(doc, e.ctrlKey ? "inTab" : followLinkLocation), false, undefined);
                 e.stopPropagation();
             }
         }
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 948d2300d..75be08e9f 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -9,7 +9,7 @@ import { List } from "../../../new_fields/List";
 import { makeInterface, createSchema } from "../../../new_fields/Schema";
 import { ScriptField, ComputedField } from "../../../new_fields/ScriptField";
 import { Cast, NumCast, StrCast } from "../../../new_fields/Types";
-import { smoothScroll, Utils, emptyFunction, returnOne, intersectRect, addStyleSheet, addStyleSheetRule, clearStyleSheetRules, returnZero } from "../../../Utils";
+import { smoothScroll, Utils, emptyFunction, returnOne, intersectRect, addStyleSheet, addStyleSheetRule, clearStyleSheetRules, returnZero, emptyPath } from "../../../Utils";
 import { Docs, DocUtils } from "../../documents/Documents";
 import { DragManager } from "../../util/DragManager";
 import { CompiledScript, CompileScript } from "../../util/Scripting";
@@ -607,7 +607,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
 
 
     getCoverImage = () => {
-        if (!this.props.Document[HeightSym]() || !this.props.Document.nativeHeight) {
+        if (!this.props.Document[HeightSym]() || !this.props.Document._nativeHeight) {
             setTimeout((() => {
                 this.Document._height = this.Document[WidthSym]() * this._coverPath.height / this._coverPath.width;
                 this.Document._nativeHeight = (this.Document._nativeWidth || 0) * this._coverPath.height / this._coverPath.width;
@@ -632,7 +632,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
 
     @computed get annotationLayer() {
         TraceMobx();
-        return <div className="pdfViewer-annotationLayer" style={{ height: NumCast(this.Document.nativeHeight), transform: `scale(${this._zoomed})` }} ref={this._annotationLayer}>
+        return <div className="pdfViewer-annotationLayer" style={{ height: NumCast(this.Document._nativeHeight), transform: `scale(${this._zoomed})` }} ref={this._annotationLayer}>
             {this.nonDocAnnotations.sort((a, b) => NumCast(a.y) - NumCast(b.y)).map((anno, index) =>
                 <Annotation {...this.props} focus={this.props.focus} dataDoc={this.dataDoc} fieldKey={this.props.fieldKey} anno={anno} key={`${anno[Id]}-annotation`} />)}
         </div>;
@@ -643,7 +643,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
     @computed get overlayLayer() {
         return <div className={`pdfViewer-overlay${InkingControl.Instance.selectedTool !== InkTool.None ? "-inking" : ""}`} id="overlay" style={{ transform: `scale(${this._zoomed})` }}>
             <CollectionFreeFormView {...this.props}
-                LibraryPath={this.props.ContainingCollectionView?.props.LibraryPath ?? []}
+                LibraryPath={this.props.ContainingCollectionView?.props.LibraryPath ?? emptyPath}
                 annotationsKey={this.annotationKey}
                 setPreviewCursor={this.setPreviewCursor}
                 PanelHeight={this.panelWidth}
-- 
cgit v1.2.3-70-g09d2


From 9fcde485c294c1cfae84874df162c8f8da5fb1f5 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Sat, 18 Apr 2020 17:42:09 -0400
Subject: cleaned up webbox - fit width by default. native dimensions by
 default.  toggle buttons for annotate/interact.

---
 src/client/documents/Documents.ts                  |  4 +-
 .../views/collections/CollectionDockingView.tsx    | 30 ++++++---
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  2 +-
 src/client/views/nodes/PDFBox.scss                 |  3 -
 src/client/views/nodes/WebBox.scss                 | 21 +++---
 src/client/views/nodes/WebBox.tsx                  | 78 ++++++++++++----------
 src/client/views/pdf/PDFViewer.scss                |  4 --
 src/client/views/pdf/PDFViewer.tsx                 |  7 +-
 8 files changed, 77 insertions(+), 72 deletions(-)

(limited to 'src/client/views/pdf/PDFViewer.tsx')

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 968e3d4dc..baaee09ea 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -590,7 +590,7 @@ export namespace Docs {
         }
 
         export function WebDocument(url: string, options: DocumentOptions = {}) {
-            return InstanceFromProto(Prototypes.get(DocumentType.WEB), new WebField(new URL(url)), options);
+            return InstanceFromProto(Prototypes.get(DocumentType.WEB), new WebField(new URL(url)), { _fitWidth: true, ...options });
         }
 
         export function HtmlDocument(html: string, options: DocumentOptions = {}) {
@@ -914,7 +914,7 @@ export namespace Docs {
                     });
                 }
                 ctor = Docs.Create.WebDocument;
-                options = { _height: options._width, ...options, title: path, _nativeWidth: undefined };
+                options = { ...options, _nativeWidth: 850, _nativeHeight: 962, _width: 500, _height: 566, title: path, };
             }
             return ctor ? ctor(path, options) : undefined;
         }
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index d77ef812f..c74f5555b 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -739,19 +739,27 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
     nativeHeight = () => !this.layoutDoc!._fitWidth ? NumCast(this.layoutDoc!._nativeHeight) || this._panelHeight : 0;
 
     contentScaling = () => {
-        if (this.layoutDoc!.type === DocumentType.PDF) {
-            if ((this.layoutDoc && this.layoutDoc._fitWidth) ||
-                this._panelHeight / NumCast(this.layoutDoc!._nativeHeight) > this._panelWidth / NumCast(this.layoutDoc!._nativeWidth)) {
-                return this._panelWidth / NumCast(this.layoutDoc!._nativeWidth);
-            } else {
-                return this._panelHeight / NumCast(this.layoutDoc!._nativeHeight);
-            }
-        }
         const nativeH = this.nativeHeight();
         const nativeW = this.nativeWidth();
-        if (!nativeW || !nativeH) return 1;
-        const wscale = this.panelWidth() / nativeW;
-        return wscale * nativeH > this._panelHeight ? this._panelHeight / nativeH : wscale;
+        let scaling = 1;
+        if (!this.layoutDoc?._fitWidth && (!nativeW || !nativeH)) {
+            scaling = 1;
+        } else if ((this.layoutDoc?._fitWidth) ||
+            this._panelHeight / NumCast(this.layoutDoc!._nativeHeight) > this._panelWidth / NumCast(this.layoutDoc!._nativeWidth)) {
+            scaling = this._panelWidth / NumCast(this.layoutDoc!._nativeWidth);
+        } else {
+            // if (this.layoutDoc!.type === DocumentType.PDF || this.layoutDoc!.type === DocumentType.WEB) {
+            //     if ((this.layoutDoc?._fitWidth) ||
+            //         this._panelHeight / NumCast(this.layoutDoc!._nativeHeight) > this._panelWidth / NumCast(this.layoutDoc!._nativeWidth)) {
+            //         return this._panelWidth / NumCast(this.layoutDoc!._nativeWidth);
+            //     } else {
+            //         return this._panelHeight / NumCast(this.layoutDoc!._nativeHeight);
+            //     }
+            // }
+            const wscale = this.panelWidth() / nativeW;
+            scaling = wscale * nativeH > this._panelHeight ? this._panelHeight / nativeH : wscale;
+        }
+        return scaling;
     }
 
     ScreenToLocalTransform = () => {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index b3409bd57..87cf716e5 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -822,7 +822,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument, u
 
             if (!willZoom && DocumentView._focusHack.length) {
                 Doc.BrushDoc(this.props.Document);
-                !doc.z && this.scaleAtPt(DocumentView._focusHack, 1); // [NumCast(doc.x), NumCast(doc.y)], 1);
+                !doc.z && NumCast(this.layoutDoc.scale) < 1 && this.scaleAtPt(DocumentView._focusHack, 1); // [NumCast(doc.x), NumCast(doc.y)], 1);
             } else {
                 if (DocListCast(this.dataDoc[this.props.fieldKey]).includes(doc)) {
                     if (!doc.z) this.setPan(newPanX, newPanY, "Ease", true); // docs that are floating in their collection can't be panned to from their collection -- need to propagate the pan to a parent freeform somehow
diff --git a/src/client/views/nodes/PDFBox.scss b/src/client/views/nodes/PDFBox.scss
index 7a3d2e92b..bccf0f291 100644
--- a/src/client/views/nodes/PDFBox.scss
+++ b/src/client/views/nodes/PDFBox.scss
@@ -199,9 +199,6 @@
 
 .pdfBox {
     pointer-events: none;
-    .collectionFreeFormView-none {
-        pointer-events: none;
-    }
     .pdfViewer-text {
         .textLayer {
             span {
diff --git a/src/client/views/nodes/WebBox.scss b/src/client/views/nodes/WebBox.scss
index cdeac4bd1..af84a7d95 100644
--- a/src/client/views/nodes/WebBox.scss
+++ b/src/client/views/nodes/WebBox.scss
@@ -48,11 +48,17 @@
     position: absolute;
 }
 
-.webBox-button {
-    padding: 0vw;
-    border: none;
+.webBox-buttons {
+    margin-left: 44;
+    background:lightGray;
     width: 100%;
-    height: 100%;
+}
+.webBox-freeze {
+    display: flex;
+    align-items: center; 
+    justify-content: center;
+    margin-right: 5px;
+    width: 30px;
 }
 
 .webBox-urlEditor {
@@ -60,7 +66,6 @@
     opacity: 0.9;
     z-index: 9001;
     transition: top .5s;
-    background: lightgrey;
     padding: 10px;
 
 
@@ -70,12 +75,6 @@
         padding-bottom: 10px;
         overflow: hidden;
 
-        .webBox-freeze {
-            display: flex;
-            align-items: center; 
-            justify-content: center;
-        }
-
         .editorBase {
             display: flex;
 
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 510e6be0c..0a7772044 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -1,5 +1,5 @@
 import { library } from "@fortawesome/fontawesome-svg-core";
-import { faStickyNote, faLock, faUnlock } from '@fortawesome/free-solid-svg-icons';
+import { faStickyNote, faPen, faMousePointer } from '@fortawesome/free-solid-svg-icons';
 import { action, computed, observable, trace, IReactionDisposer, reaction } from "mobx";
 import { observer } from "mobx-react";
 import { Doc, FieldResult } from "../../../new_fields/Doc";
@@ -34,8 +34,8 @@ const WebDocument = makeInterface(documentSchema);
 export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocument>(WebDocument) {
 
     public static LayoutString(fieldKey: string) { return FieldView.LayoutString(WebBox, fieldKey); }
-    @observable private collapsed: boolean = true;
-    @observable private url: string = "hello";
+    @observable private _collapsed: boolean = true;
+    @observable private _url: string = "hello";
     @observable private _pressX: number = 0;
     @observable private _pressY: number = 0;
 
@@ -105,19 +105,19 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
 
     @action
     onURLChange = (e: React.ChangeEvent<HTMLInputElement>) => {
-        this.url = e.target.value;
+        this._url = e.target.value;
     }
 
     @action
     submitURL = () => {
-        this.dataDoc[this.props.fieldKey] = new WebField(new URL(this.url));
+        this.dataDoc[this.props.fieldKey] = new WebField(new URL(this._url));
     }
 
     @action
     setURL() {
         const urlField: FieldResult<WebField> = Cast(this.dataDoc[this.props.fieldKey], WebField);
-        if (urlField) this.url = urlField.url.toString();
-        else this.url = "";
+        if (urlField) this._url = urlField.url.toString();
+        else this._url = "";
     }
 
     onValueKeyDown = async (e: React.KeyboardEvent) => {
@@ -128,36 +128,44 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
     }
 
     toggleNativeDimensions = () => {
-        if (this.Document._nativeWidth || this.Document._nativeHeight) {
-            DocumentView.unfreezeNativeDimensions(this.layoutDoc);
+        if (!this.layoutDoc.isAnnotating) {
+            //DocumentView.unfreezeNativeDimensions(this.layoutDoc);
             this.layoutDoc.lockedTransform = false;
+            this.layoutDoc.isAnnotating = true;
         }
         else {
-            Doc.freezeNativeDimensions(this.layoutDoc, this.props.PanelWidth(), this.props.PanelHeight());
+            //Doc.freezeNativeDimensions(this.layoutDoc, this.props.PanelWidth(), this.props.PanelHeight());
             this.layoutDoc.lockedTransform = true;
+            this.layoutDoc.isAnnotating = false;
         }
     }
 
     urlEditor() {
-        const frozen = this.layoutDoc._nativeWidth && this.layoutDoc._nativeHeight;
+        const frozen = this.layoutDoc._nativeWidth && this.layoutDoc.isAnnotating;
         return (
-            <div className="webBox-urlEditor" style={{ top: this.collapsed ? -70 : 0 }}>
+            <div className="webBox-urlEditor" style={{ top: this._collapsed ? -70 : 0 }}>
                 <div className="urlEditor">
                     <div className="editorBase">
                         <button className="editor-collapse"
                             style={{
-                                top: this.collapsed ? 70 : 10,
-                                transform: `rotate(${this.collapsed ? 180 : 0}deg) scale(${this.collapsed ? 0.5 : 1}) translate(${this.collapsed ? "-100%, -100%" : "0, 0"})`,
-                                opacity: (this.collapsed && !this.props.isSelected()) ? 0 : 0.9,
-                                left: (this.collapsed ? 0 : "unset"),
+                                top: this._collapsed ? 70 : 10,
+                                transform: `rotate(${this._collapsed ? 180 : 0}deg) scale(${this._collapsed ? 0.5 : 1}) translate(${this._collapsed ? "-100%, -100%" : "0, 0"})`,
+                                opacity: (this._collapsed && !this.props.isSelected()) ? 0 : 0.9,
+                                left: (this._collapsed ? 0 : "unset"),
                             }}
                             title="Collapse Url Editor" onClick={this.toggleCollapse}>
                             <FontAwesomeIcon icon="caret-up" size="2x" />
                         </button>
-                        <div style={{ marginLeft: 54, width: "100%", display: this.collapsed ? "none" : "flex" }}>
+                        <div className="webBox-buttons" style={{ display: this._collapsed ? "none" : "flex" }}>
+                            <div className="webBox-freeze" title={"Annotate"} style={{ background: frozen ? "lightBlue" : "gray" }} onClick={this.toggleNativeDimensions} >
+                                <FontAwesomeIcon icon={faPen} size={"2x"} />
+                            </div>
+                            <div className="webBox-freeze" title={"Select"} style={{ background: !frozen ? "lightBlue" : "gray" }} onClick={this.toggleNativeDimensions} >
+                                <FontAwesomeIcon icon={faMousePointer} size={"2x"} />
+                            </div>
                             <input className="webpage-urlInput"
                                 placeholder="ENTER URL"
-                                value={this.url}
+                                value={this._url}
                                 onChange={this.onURLChange}
                                 onKeyDown={this.onValueKeyDown}
                             />
@@ -170,9 +178,6 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
                                 <button className="submitUrl" onClick={this.submitURL}>
                                     SUBMIT
                                 </button>
-                                <div className="webBox-freeze" title={frozen ? "Unfreeze" : "Freeze Dimensions"} onClick={this.toggleNativeDimensions} >
-                                    <FontAwesomeIcon icon={frozen ? faUnlock : faLock} size={"lg"} />
-                                </div>
                             </div>
                         </div>
                     </div>
@@ -183,7 +188,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
 
     @action
     toggleCollapse = () => {
-        this.collapsed = !this.collapsed;
+        this._collapsed = !this._collapsed;
     }
 
     _ignore = 0;
@@ -326,20 +331,19 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
 
         const frozen = !this.props.isSelected() || decInteracting;
 
-        return (
-            <>
-                <div className={"webBox-cont" + (this.props.isSelected() && InkingControl.Instance.selectedTool === InkTool.None && !decInteracting ? "-interactive" : "")}  >
-                    {content}
-                </div>
-                {!frozen ? (null) :
-                    <div className="webBox-overlay" style={{ pointerEvents: this.layoutDoc.isBackground ? undefined : "all" }}
-                        onWheel={this.onPreWheel} onPointerDown={this.onPrePointer} onPointerMove={this.onPrePointer} onPointerUp={this.onPrePointer}>
-                        <div className="touch-iframe-overlay" onPointerDown={this.onLongPressDown} >
-                            <div className="indicator" ref={this._iframeIndicatorRef}></div>
-                            <div className="dragger" ref={this._iframeDragRef}></div>
-                        </div>
-                    </div>}
-            </>);
+        return (<>
+            <div className={"webBox-cont" + (this.props.isSelected() && InkingControl.Instance.selectedTool === InkTool.None && !decInteracting ? "-interactive" : "")}  >
+                {content}
+            </div>
+            {!frozen ? (null) :
+                <div className="webBox-overlay" style={{ pointerEvents: this.layoutDoc.isBackground ? undefined : "all" }}
+                    onWheel={this.onPreWheel} onPointerDown={this.onPrePointer} onPointerMove={this.onPrePointer} onPointerUp={this.onPrePointer}>
+                    <div className="touch-iframe-overlay" onPointerDown={this.onLongPressDown} >
+                        <div className="indicator" ref={this._iframeIndicatorRef}></div>
+                        <div className="dragger" ref={this._iframeDragRef}></div>
+                    </div>
+                </div>}
+        </>);
     }
     scrollXf = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.props.Document.scrollTop))
     render() {
@@ -352,7 +356,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
             }} >
             {this.content}
             <div className={"webBox-outerContent"} ref={this._outerRef}
-                style={{ pointerEvents: this.layoutDoc._nativeHeight && !this.layoutDoc.isBackground ? "all" : "none" }}
+                style={{ pointerEvents: this.layoutDoc.isAnnotating && !this.layoutDoc.isBackground ? "all" : "none" }}
                 onWheel={e => e.stopPropagation()}
                 onScroll={e => {
                     if (this._iframeRef.current!.contentDocument!.children[0].scrollTop !== this._outerRef.current!.scrollTop) {
diff --git a/src/client/views/pdf/PDFViewer.scss b/src/client/views/pdf/PDFViewer.scss
index 5cd2c4fe4..26c31a80e 100644
--- a/src/client/views/pdf/PDFViewer.scss
+++ b/src/client/views/pdf/PDFViewer.scss
@@ -30,10 +30,6 @@
     .page {
         position: relative;
     }
-    .collectionfreeformview-container {
-        pointer-events: none;
-    }
-
     .pdfViewer-text-selected {
         .textLayer{
            pointer-events: all;
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 75be08e9f..d54390236 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -282,7 +282,6 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
             if (anno.style.height) annoDoc._height = parseInt(anno.style.height);
             if (anno.style.width) annoDoc._width = parseInt(anno.style.width);
             annoDoc.group = mainAnnoDoc;
-            annoDoc.isLinkButton = true;
             annoDocs.push(annoDoc);
             anno.remove();
             mainAnnoDoc = annoDoc;
@@ -554,7 +553,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
     highlight = (color: string) => {
         // creates annotation documents for current highlights
         const annotationDoc = this.makeAnnotationDocument(color);
-        annotationDoc && this.props.addDocument && this.props.addDocument(annotationDoc);
+        annotationDoc && this.props?.addDocument(annotationDoc);
         return annotationDoc;
     }
 
@@ -585,6 +584,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
                 dragComplete: e => {
                     if (!e.aborted && e.annoDragData && !e.annoDragData.linkedToDoc) {
                         const link = DocUtils.MakeLink({ doc: annotationDoc }, { doc: e.annoDragData.dropDocument }, "Annotation");
+                        annotationDoc.isLinkButton = true;
                         if (link) link.followLinkLocation = "onRight";
                     }
                 }
@@ -641,7 +641,8 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
     panelWidth = () => (this.Document.scrollHeight || this.Document._nativeHeight || 0);
     panelHeight = () => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : (this.Document._nativeWidth || 0);
     @computed get overlayLayer() {
-        return <div className={`pdfViewer-overlay${InkingControl.Instance.selectedTool !== InkTool.None ? "-inking" : ""}`} id="overlay" style={{ transform: `scale(${this._zoomed})` }}>
+        return <div className={`pdfViewer-overlay${InkingControl.Instance.selectedTool !== InkTool.None ? "-inking" : ""}`} id="overlay"
+            style={{ transform: `scale(${this._zoomed})`, pointerEvents: this.active(false) ? "all" : undefined }}>
             <CollectionFreeFormView {...this.props}
                 LibraryPath={this.props.ContainingCollectionView?.props.LibraryPath ?? emptyPath}
                 annotationsKey={this.annotationKey}
-- 
cgit v1.2.3-70-g09d2


From 9f8e76451b807ed8f51ef32aab9b2255dfccd525 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Sat, 18 Apr 2020 18:37:27 -0400
Subject: fixed pointer events for templated PDFs.  changed clicking on a
 document with a rootDocument to pass the event on up

---
 src/client/views/nodes/CollectionFreeFormDocumentView.tsx | 2 +-
 src/client/views/nodes/DocumentView.tsx                   | 7 +++++--
 src/client/views/pdf/PDFViewer.scss                       | 5 ++---
 src/client/views/pdf/PDFViewer.tsx                        | 4 +++-
 4 files changed, 11 insertions(+), 7 deletions(-)

(limited to 'src/client/views/pdf/PDFViewer.tsx')

diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index e325a70c0..3a7e005ac 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -92,7 +92,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
                 height: this.height,
                 zIndex: this.ZInd,
                 display: this.ZInd === -99 ? "none" : undefined,
-                pointerEvents: this.props.Document.isBackground ? "none" : this.props.pointerEvents
+                pointerEvents: this.props.Document.isBackground ? "none" : this.props.pointerEvents ? "all" : undefined
             }} >
 
             {!this.props.fitToBox ?
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 2bae2fa96..e3013698d 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -315,7 +315,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             } else if (this.Document.isLinkButton) {
                 DocListCast(this.props.Document.links).length && this.followLinkClick(e.altKey, e.ctrlKey, e.shiftKey);
             } else {
-                if ((this.props.Document.onDragStart || (this.props.Document.rootDocument && this.props.Document.isTemplateForField)) && !(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
+                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
                     stopPropagate = false; // don't stop propagation for field templates -- want the selection to propagate up to the root document of the template
                 } else {
                     DocumentView._focusHack = this.props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY) || [0, 0];
@@ -480,7 +480,9 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             }
             return;
         }
-        if (!e.nativeEvent.cancelBubble || this.onClickHandler || this.Document.onDragStart) {
+        if ((!e.nativeEvent.cancelBubble || this.onClickHandler || this.Document.onDragStart) &&
+            // if this is part of a template, let the event go up to the tempalte root unless right/ctrl clicking
+            !((this.props.Document.rootDocument) && !(e.ctrlKey || e.button > 0))) {
             this._downX = e.clientX;
             this._downY = e.clientY;
             if ((this.active || this.Document.onDragStart || this.onClickHandler) &&
@@ -489,6 +491,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 !this.Document.lockedPosition &&
                 !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);
+
             }
             document.removeEventListener("pointermove", this.onPointerMove);
             document.removeEventListener("pointerup", this.onPointerUp);
diff --git a/src/client/views/pdf/PDFViewer.scss b/src/client/views/pdf/PDFViewer.scss
index 26c31a80e..760f64a72 100644
--- a/src/client/views/pdf/PDFViewer.scss
+++ b/src/client/views/pdf/PDFViewer.scss
@@ -57,12 +57,11 @@
         display: inline-block;
         width:100%;
         pointer-events: none;
-    }
-    .pdfViewer-overlay-inking {
-        .collectionfreeformview-container {
+        .collectionFreeFormDocumentView-container {
             pointer-events: all;
         }
     }
+    
     .pdfViewer-annotationLayer {
         position: absolute;
         transform-origin: left top;
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index d54390236..10cfaa2f1 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -573,7 +573,9 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
         clipDoc._scrollTop = this.marqueeY();
         const targetDoc = Docs.Create.TextDocument("", { _width: 200, _height: 200, title: "Note linked to " + this.props.Document.title });
         Doc.GetProto(targetDoc).data = new List<Doc>([clipDoc]);
+        clipDoc.rootDocument = targetDoc;
         DocumentView.makeCustomViewClicked(targetDoc, Docs.Create.StackingDocument, "slideView", undefined);
+        targetDoc.layoutKey = "layout";
         // const targetDoc = Docs.Create.TextDocument("", { _width: 200, _height: 200, title: "Note linked to " + this.props.Document.title });
         // Doc.GetProto(targetDoc).snipped = this.dataDoc[this.props.fieldKey][Copy]();
         // const snipLayout = Docs.Create.PdfDocument("http://www.msn.com", { title: "snippetView", isTemplateDoc: true, isTemplateForField: "snipped", _fitWidth: true, _width: this.marqueeWidth(), _height: this.marqueeHeight(), _scrollTop: this.marqueeY() });
@@ -642,7 +644,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
     panelHeight = () => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : (this.Document._nativeWidth || 0);
     @computed get overlayLayer() {
         return <div className={`pdfViewer-overlay${InkingControl.Instance.selectedTool !== InkTool.None ? "-inking" : ""}`} id="overlay"
-            style={{ transform: `scale(${this._zoomed})`, pointerEvents: this.active(false) ? "all" : undefined }}>
+            style={{ transform: `scale(${this._zoomed})` }}>
             <CollectionFreeFormView {...this.props}
                 LibraryPath={this.props.ContainingCollectionView?.props.LibraryPath ?? emptyPath}
                 annotationsKey={this.annotationKey}
-- 
cgit v1.2.3-70-g09d2


From bdf4ac9601e54bf8e2a3a8f988c97274d84ae8a4 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Sun, 19 Apr 2020 13:30:20 -0400
Subject: fixed removing docs from tree view with menu.  restructured
 current_user_utils completely

---
 src/client/views/MainView.tsx                      |  11 +-
 .../views/collections/CollectionTreeView.tsx       |   6 +-
 src/client/views/nodes/AudioBox.tsx                |   5 +-
 src/client/views/nodes/DocumentView.tsx            |   6 +-
 src/client/views/nodes/WebBox.tsx                  |   4 +-
 src/client/views/pdf/PDFViewer.tsx                 |   2 +-
 .../authentication/models/current_user_utils.ts    | 562 +++++++++++++--------
 src/server/database.ts                             |   3 -
 8 files changed, 369 insertions(+), 230 deletions(-)

(limited to 'src/client/views/pdf/PDFViewer.tsx')

diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 40cabcf83..0877cc5f6 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -61,7 +61,7 @@ export class MainView extends React.Component {
     @computed private get userDoc() { return Doc.UserDoc(); }
     @computed private get mainContainer() { return this.userDoc ? FieldValue(Cast(this.userDoc.activeWorkspace, Doc)) : CurrentUserUtils.GuestWorkspace; }
     @computed public get mainFreeform(): Opt<Doc> { return (docs => (docs && docs.length > 1) ? docs[1] : undefined)(DocListCast(this.mainContainer!.data)); }
-    @computed public get sidebarButtonsDoc() { return Cast(CurrentUserUtils.UserDocument.sidebarButtons, Doc) as Doc; }
+    @computed public get sidebarButtonsDoc() { return Cast(this.userDoc["tabs-buttons"], Doc) as Doc; }
 
     public isPointerDown = false;
 
@@ -399,15 +399,14 @@ export class MainView extends React.Component {
     mainContainerXf = () => new Transform(0, -this._buttonBarHeight, 1);
 
     @computed get flyout() {
-        const sidebarContent = this.userDoc?.sidebarContainer;
+        const sidebarContent = this.userDoc?.["tabs-panelContainer"];
         if (!(sidebarContent instanceof Doc)) {
             return (null);
         }
-        const sidebarButtonsDoc = Cast(CurrentUserUtils.UserDocument.sidebarButtons, Doc) as Doc;
         return <div className="mainView-flyoutContainer" >
-            <div className="mainView-tabButtons" style={{ height: `${this._buttonBarHeight}px`, backgroundColor: StrCast(sidebarButtonsDoc.backgroundColor) }}>
+            <div className="mainView-tabButtons" style={{ height: `${this._buttonBarHeight}px`, backgroundColor: StrCast(this.sidebarButtonsDoc.backgroundColor) }}>
                 <DocumentView
-                    Document={sidebarButtonsDoc}
+                    Document={this.sidebarButtonsDoc}
                     DataDoc={undefined}
                     LibraryPath={emptyPath}
                     addDocument={undefined}
@@ -468,7 +467,7 @@ export class MainView extends React.Component {
     }
 
     @computed get mainContent() {
-        const sidebar = this.userDoc?.sidebarContainer;
+        const sidebar = this.userDoc?.["tabs-panelContainer"];
         return !this.userDoc || !(sidebar instanceof Doc) ? (null) : (
             <div className="mainView-mainContent" style={{ color: this.darkScheme ? "rgb(205,205,205)" : "black" }} >
                 <div className="mainView-flyoutContainer" onPointerLeave={this.pointerLeaveDragger} style={{ width: this.flyoutWidth }}>
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index cd1e23bbd..c243d8f29 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -139,6 +139,9 @@ class TreeView extends React.Component<TreeViewProps> {
     @undoBatch @action remove = (document: Document, key: string) => {
         return Doc.RemoveDocFromList(this.dataDoc, key, document);
     }
+    @undoBatch @action removeDoc = (document: Document) => {
+        return Doc.RemoveDocFromList(this.props.containingCollection, Doc.LayoutFieldKey(this.props.containingCollection), document);
+    }
 
     protected createTreeDropTarget = (ele: HTMLDivElement) => {
         this._treedropDisposer && this._treedropDisposer();
@@ -455,7 +458,7 @@ class TreeView extends React.Component<TreeViewProps> {
                         onClick={this.props.onChildClick || editTitle}
                         dropAction={this.props.dropAction}
                         moveDocument={this.props.moveDocument}
-                        removeDocument={undefined}
+                        removeDocument={this.removeDoc}
                         ScreenToLocalTransform={this.getTransform}
                         ContentScaling={returnOne}
                         PanelWidth={returnZero}
@@ -778,6 +781,7 @@ export class CollectionTreeView extends CollectionSubView(Document, undefined as
     }
 
     render() {
+        if (!(this.props.Document instanceof Doc)) return (null);
         const dropAction = StrCast(this.props.Document.childDropAction) as dropActionType;
         const addDoc = (doc: Doc, relativeTo?: Doc, before?: boolean) => this.addDoc(doc, relativeTo, before);
         const moveDoc = (d: Doc, target: Doc | undefined, addDoc: (doc: Doc) => boolean) => this.props.moveDocument(d, target, addDoc);
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index 7078cc01c..8f40ea2be 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -20,6 +20,7 @@ import { DocumentView } from "./DocumentView";
 import { Docs } from "../../documents/Documents";
 import { ComputedField } from "../../../new_fields/ScriptField";
 import { Networking } from "../../Network";
+import { Upload } from "../../../server/SharedMediaTypes";
 
 // testing testing 
 
@@ -146,7 +147,9 @@ export class AudioBox extends ViewBoxBaseComponent<FieldViewProps, AudioDocument
         AudioBox.ActiveRecordings.push(this.props.Document);
         this._recorder.ondataavailable = async (e: any) => {
             const [{ result }] = await Networking.UploadFilesToServer(e.data);
-            this.props.Document[this.props.fieldKey] = new AudioField(Utils.prepend(result.accessPaths.agnostic.client));
+            if (!(result instanceof Error)) {
+                this.props.Document[this.props.fieldKey] = new AudioField(Utils.prepend(result.accessPaths.agnostic.client));
+            }
         };
         this._recordStart = new Date().getTime();
         runInAction(() => this.audioState = "recording");
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 5c1326b1a..ca4ec7aff 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -560,14 +560,14 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     }
     static findTemplate(templateName: string, type: string, signature: string) {
         let docLayoutTemplate: Opt<Doc>;
-        const iconViews = DocListCast(Cast(Doc.UserDoc().iconViews, Doc, null)?.data);
+        const iconViews = DocListCast(Cast(Doc.UserDoc()["icon-view-all"], Doc, null)?.data);
         const templBtns = DocListCast(Cast(Doc.UserDoc().templateButtons, Doc, null)?.data);
         const noteTypes = DocListCast(Cast(Doc.UserDoc().noteTypes, Doc, null)?.data);
         const clickFuncs = DocListCast(Cast(Doc.UserDoc().clickFuncs, Doc, null)?.data);
         const allTemplates = iconViews.concat(templBtns).concat(noteTypes).concat(clickFuncs).map(btnDoc => (btnDoc.dragFactory as Doc) || btnDoc).filter(doc => doc.isTemplateDoc);
         // bcz: this is hacky -- want to have different templates be applied depending on the "type" of a document.  but type is not reliable and there could be other types of template searches so this should be generalized
         // first try to find a template that matches the specific document type (<typeName>_<templateName>).  otherwise, fallback to a general match on <templateName>
-        !docLayoutTemplate && allTemplates.forEach(tempDoc => StrCast(tempDoc.title) === type + "_" + templateName && (docLayoutTemplate = tempDoc));
+        !docLayoutTemplate && allTemplates.forEach(tempDoc => StrCast(tempDoc.title) === templateName + "_" + type && (docLayoutTemplate = tempDoc));
         !docLayoutTemplate && allTemplates.forEach(tempDoc => StrCast(tempDoc.title) === templateName && (docLayoutTemplate = tempDoc));
         return docLayoutTemplate;
     }
@@ -991,7 +991,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         return typeof fallback === "string" ? fallback : "layout";
     }
     rootSelected = (outsideReaction?: boolean) => {
-        return this.isSelected(outsideReaction) || (this.props.Document.rootDocument && this.props.rootSelected?.(outsideReaction));
+        return this.isSelected(outsideReaction) || (this.props.Document.rootDocument && this.props.rootSelected?.(outsideReaction)) || false;
     }
     childScaling = () => (this.layoutDoc._fitWidth ? this.props.PanelWidth() / this.nativeWidth : this.props.ContentScaling());
     panelWidth = () => this.props.PanelWidth();
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 36907c4a7..66ddf64c9 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -68,7 +68,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
         this._setPreviewCursor?.(e.screenX, e.screenY, false);
     }
     iframeScrolled = (e: any) => {
-        const scroll = (e.target as any)?.children?.[0].scrollTop;
+        const scroll = e.target?.children?.[0].scrollTop;
         this.layoutDoc.scrollTop = this._outerRef.current!.scrollTop = scroll;
     }
     async componentDidMount() {
@@ -345,7 +345,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
                 </div>}
         </>);
     }
-    scrollXf = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.props.Document.scrollTop))
+    scrollXf = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.props.Document.scrollTop));
     render() {
         return (<div className={`webBox-container`}
             style={{
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 10cfaa2f1..46b60b554 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -553,7 +553,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
     highlight = (color: string) => {
         // creates annotation documents for current highlights
         const annotationDoc = this.makeAnnotationDocument(color);
-        annotationDoc && this.props?.addDocument(annotationDoc);
+        annotationDoc && this.props.addDocument?.(annotationDoc);
         return annotationDoc;
     }
 
diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts
index 7e5f7257a..3d68d8425 100644
--- a/src/server/authentication/models/current_user_utils.ts
+++ b/src/server/authentication/models/current_user_utils.ts
@@ -19,6 +19,7 @@ import { RichTextField } from "../../../new_fields/RichTextField";
 import { PrefetchProxy } from "../../../new_fields/Proxy";
 import { FormattedTextBox } from "../../../client/views/nodes/FormattedTextBox";
 import { MainView } from "../../../client/views/MainView";
+import { DocumentType } from "../../../client/documents/DocumentTypes";
 
 export class CurrentUserUtils {
     private static curr_id: string;
@@ -35,60 +36,151 @@ export class CurrentUserUtils {
     @observable public static GuestWorkspace: Doc | undefined;
     @observable public static GuestMobile: Doc | undefined;
 
-    static setupDefaultDocTemplates(doc: Doc, buttons?: string[]) {
-        const taskStatusValues = [{ title: "todo", _backgroundColor: "blue", color: "white" },
-        { title: "in progress", _backgroundColor: "yellow", color: "black" },
-        { title: "completed", _backgroundColor: "green", color: "white" }
-        ];
-        const noteTemplates = [
-            Docs.Create.TextDocument("", { title: "text", style: "Note", isTemplateDoc: true, backgroundColor: "yellow" }),
-            Docs.Create.TextDocument("", { title: "text", style: "Idea", isTemplateDoc: true, backgroundColor: "pink" }),
-            Docs.Create.TextDocument("", { title: "text", style: "Topic", isTemplateDoc: true, backgroundColor: "lightBlue" }),
-            Docs.Create.TextDocument("", { title: "text", style: "Person", isTemplateDoc: true, backgroundColor: "lightGreen" }),
-            Docs.Create.TextDocument("", {
-                title: "text", style: "Todo", isTemplateDoc: true, backgroundColor: "orange", _autoHeight: false,
-                layout: FormattedTextBox.LayoutString("Todo"), _height: 100, _showCaption: "caption", caption: RichTextField.DashField("taskStatus")
-            })
-        ];
-        doc.fieldTypes = Docs.Create.TreeDocument([], { title: "field enumerations" });
-        Doc.addFieldEnumerations(Doc.GetProto(noteTemplates[4]), "taskStatus", taskStatusValues);
-        doc.noteTypes = new PrefetchProxy(Docs.Create.TreeDocument(noteTemplates.map(nt => makeTemplate(nt, true, StrCast(nt.style)) ? nt : nt), { title: "Note Layouts", _height: 75 }))
-        doc.templateButtons = Docs.Create.MasonryDocument(CurrentUserUtils.setupTemplateButtons(doc), {
-            title: "template buttons",
-            _pivotField: "author", _xMargin: 0,
-            _autoHeight: true, _width: 500, columnWidth: 35, ignoreClick: true, lockedPosition: true, _chromeStatus: "disabled",
-            dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }),
-        });
-        doc.templateDocs = new PrefetchProxy(Docs.Create.TreeDocument([doc.noteTypes as any as Doc, doc.templateButtons as Doc, doc.clickFuncs as Doc], {
-            title: "template layouts", _xPadding: 0,
-            dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name })
-        }));
+    // sets up the default User Templates - slideView, queryView, descriptionView
+    static setupUserTemplateButtons(doc: Doc) {
+        if (doc["template-button-query"] === undefined) {
+            const queryTemplate = Docs.Create.MulticolumnDocument(
+                [
+                    Docs.Create.QueryDocument({ title: "query", _height: 200 }),
+                    Docs.Create.FreeformDocument([], { title: "data", _height: 100, _LODdisable: true })
+                ],
+                { _width: 400, _height: 300, title: "queryView", _chromeStatus: "disabled", _xMargin: 3, _yMargin: 3, hideFilterView: true }
+            );
+            queryTemplate.isTemplateDoc = makeTemplate(queryTemplate);
+            doc["template-button-query"] = CurrentUserUtils.ficon({
+                onDragStart: ScriptField.MakeFunction('getCopy(this.dragFactory, true)'),
+                dragFactory: new PrefetchProxy(queryTemplate) as any as Doc,
+                removeDropProperties: new List<string>(["dropAction"]), title: "query view", icon: "question-circle"
+            });
+        }
+
+        if (doc["template-button-slides"] === undefined) {
+            const slideTemplate = Docs.Create.MultirowDocument(
+                [
+                    Docs.Create.MulticolumnDocument([], { title: "data", _height: 200 }),
+                    Docs.Create.TextDocument("", { title: "text", _height: 100 })
+                ],
+                { _width: 400, _height: 300, title: "slideView", _chromeStatus: "disabled", _xMargin: 3, _yMargin: 3, hideFilterView: true }
+            );
+            slideTemplate.isTemplateDoc = makeTemplate(slideTemplate);
+            doc["template-button-slides"] = CurrentUserUtils.ficon({
+                onDragStart: ScriptField.MakeFunction('getCopy(this.dragFactory, true)'),
+                dragFactory: new PrefetchProxy(slideTemplate) as any as Doc,
+                removeDropProperties: new List<string>(["dropAction"]), title: "presentation slide", icon: "address-card"
+            });
+        }
+
+        if (doc["template-button-description"] === undefined) {
+            const descriptionTemplate = Docs.Create.TextDocument("", { title: "text", _height: 100, _showTitle: "title" });
+            Doc.GetProto(descriptionTemplate).layout = FormattedTextBox.LayoutString("description");
+            descriptionTemplate.isTemplateDoc = makeTemplate(descriptionTemplate, true, "descriptionView");
+
+            doc["template-button-description"] = CurrentUserUtils.ficon({
+                onDragStart: ScriptField.MakeFunction('getCopy(this.dragFactory, true)'),
+                dragFactory: new PrefetchProxy(descriptionTemplate) as any as Doc,
+                removeDropProperties: new List<string>(["dropAction"]), title: "description view", icon: "window-maximize"
+            });
+        }
+
+        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], {
+                title: "template buttons", _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 }),
+            }));
+        }
+        return doc["template-buttons"] as Doc;
     }
-    static setupDefaultIconTypes(doc: Doc, buttons?: string[]) {
-        doc.iconView = new PrefetchProxy(Docs.Create.TextDocument("", { title: "icon", _width: 150, _height: 30, isTemplateDoc: true, onClick: ScriptField.MakeScript("deiconifyView(this)") }));
-        Doc.GetProto(doc.iconView as any as Doc).icon = new RichTextField('{"doc":{"type":"doc","content":[{"type":"paragraph","attrs":{"align":null,"color":null,"id":null,"indent":null,"inset":null,"lineSpacing":null,"paddingBottom":null,"paddingTop":null},"content":[{"type":"dashField","attrs":{"fieldKey":"title","docid":""}}]}]},"selection":{"type":"text","anchor":2,"head":2},"storedMarks":[]}', "");
-        doc.isTemplateDoc = makeTemplate(doc.iconView as any as Doc);
-        doc.iconImageView = new PrefetchProxy(Docs.Create.ImageDocument("http://www.cs.brown.edu/~bcz/face.gif", { title: "data", _width: 50, isTemplateDoc: true, onClick: ScriptField.MakeScript("deiconifyView(self)") }));
-        doc.isTemplateDoc = makeTemplate(doc.iconImageView as any as Doc, true, "image_icon");
-        doc.iconColView = new PrefetchProxy(Docs.Create.TreeDocument([], { title: "data", _width: 180, _height: 80, isTemplateDoc: true, onClick: ScriptField.MakeScript("deiconifyView(self)") }));
-        doc.isTemplateDoc = makeTemplate(doc.iconColView as any as Doc, true, "collection_icon");
-        doc.iconViews = Docs.Create.TreeDocument([doc.iconView as any as Doc, doc.iconImageView as any as Doc, doc.iconColView as any as Doc], { title: "icon types", _height: 75 });
+
+    // setup the different note type skins
+    static setupNoteTemplates(doc: Doc) {
+        if (doc.noteTypes === undefined) {
+            const taskStatusValues = [
+                { title: "todo", _backgroundColor: "blue", color: "white" },
+                { title: "in progress", _backgroundColor: "yellow", color: "black" },
+                { title: "completed", _backgroundColor: "green", color: "white" }
+            ];
+            const noteTemplates = [
+                Docs.Create.TextDocument("", { title: "text", style: "Note", isTemplateDoc: true, backgroundColor: "yellow" }),
+                Docs.Create.TextDocument("", { title: "text", style: "Idea", isTemplateDoc: true, backgroundColor: "pink" }),
+                Docs.Create.TextDocument("", { title: "text", style: "Topic", isTemplateDoc: true, backgroundColor: "lightBlue" }),
+                Docs.Create.TextDocument("", { title: "text", style: "Person", isTemplateDoc: true, backgroundColor: "lightGreen" }),
+                Docs.Create.TextDocument("", {
+                    title: "text", style: "Todo", isTemplateDoc: true, backgroundColor: "orange", _autoHeight: false, _height: 100, _showCaption: "caption",
+                    layout: FormattedTextBox.LayoutString("Todo"), caption: RichTextField.DashField("taskStatus")
+                })
+            ];
+            doc.fieldTypes = Docs.Create.TreeDocument([], { title: "field enumerations" });
+            Doc.addFieldEnumerations(Doc.GetProto(noteTemplates[4]), "taskStatus", taskStatusValues);
+            doc.noteTypes = new PrefetchProxy(Docs.Create.TreeDocument(noteTemplates.map(nt => makeTemplate(nt, true, StrCast(nt.style)) ? nt : nt),
+                { title: "Note Layouts", _height: 75 }));
+        }
+        // this is equivalent to using PrefetchProxies to make sure all the sidebarButtons and noteType internal Doc's have been retrieved.
+        PromiseValue(Cast(doc.noteTypes, Doc)).then(noteTypes => noteTypes && PromiseValue(noteTypes.data).then(DocListCast));
+        return doc.noteTypes as Doc;
     }
 
-    // setup the "creator" buttons for the sidebar-- eg. the default set of draggable document creation tools
-    static setupCreatorButtons(doc: Doc, alreadyCreatedButtons?: string[]) {
-        const emptyPresentation = Docs.Create.PresDocument(new List<Doc>(), { title: "Presentation", _viewType: CollectionViewType.Stacking, _LODdisable: true, _chromeStatus: "replaced", _showTitle: "title", boxShadow: "0 0" });
-        const emptyCollection = Docs.Create.FreeformDocument([], { _nativeWidth: undefined, _nativeHeight: undefined, _LODdisable: true, _width: 150, _height: 100, title: "freeform" });
-        doc.activePen = doc;
-        const docProtoData: { title: string, label: string, icon: string, drag?: string, ignoreClick?: boolean, click?: string, ischecked?: string, activePen?: Doc, backgroundColor?: string, dragFactory?: Doc }[] = [
-            { title: "Drag a collection", label: "Col", icon: "folder", click: 'openOnRight(getCopy(this.dragFactory, true))', drag: 'getCopy(this.dragFactory, true)', dragFactory: emptyCollection },
-            { title: "Drag a web page", label: "Web", icon: "globe-asia", ignoreClick: true, drag: 'Docs.Create.WebDocument("", {_width: 300, _height: 300, title: "New Webpage" })' },
+    // creates Note templates, and initial "user" templates
+    static setupDocTemplates(doc: Doc) {
+        const noteTemplates = CurrentUserUtils.setupNoteTemplates(doc);
+        const userTemplateBtns = CurrentUserUtils.setupUserTemplateButtons(doc);
+        const clickTemplates = CurrentUserUtils.setupClickEditorTemplates(doc);
+        if (doc.templateDocs === undefined) {
+            doc.templateDocs = new PrefetchProxy(Docs.Create.TreeDocument([noteTemplates, userTemplateBtns, clickTemplates], {
+                title: "template layouts", _xPadding: 0,
+                dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name })
+            }));
+        }
+    }
+
+    // setup templates for different document types when they are iconified from Document Decorations
+    static setupDefaultIconTemplates(doc: Doc) {
+        if (doc["icon-view"] === undefined) {
+            const iconView = Docs.Create.TextDocument("", {
+                title: "icon", _width: 150, _height: 30, isTemplateDoc: true,
+                onClick: ScriptField.MakeScript("deiconifyView(this)")
+            });
+            Doc.GetProto(iconView).icon = new RichTextField('{"doc":{"type":"doc","content":[{"type":"paragraph","attrs":{"align":null,"color":null,"id":null,"indent":null,"inset":null,"lineSpacing":null,"paddingBottom":null,"paddingTop":null},"content":[{"type":"dashField","attrs":{"fieldKey":"title","docid":""}}]}]},"selection":{"type":"text","anchor":2,"head":2},"storedMarks":[]}', "");
+            iconView.isTemplateDoc = makeTemplate(iconView);
+            doc["icon-view"] = new PrefetchProxy(iconView);
+        }
+        if (doc["icon-view-img"] === undefined) {
+            const iconImageView = Docs.Create.ImageDocument("http://www.cs.brown.edu/~bcz/face.gif", { title: "data", _width: 50, isTemplateDoc: true, onClick: ScriptField.MakeScript("deiconifyView(self)") });
+            iconImageView.isTemplateDoc = makeTemplate(iconImageView, true, "icon_" + DocumentType.IMG);
+            doc["icon-view-img"] = new PrefetchProxy(iconImageView);
+        }
+        if (doc["icon-view-col"] === undefined) {
+            const iconColView = Docs.Create.TreeDocument([], { title: "data", _width: 180, _height: 80, onClick: ScriptField.MakeScript("deiconifyView(self)") });
+            iconColView.isTemplateDoc = makeTemplate(iconColView, true, "icon_" + DocumentType.COL);
+            doc["icon-view-col"] = new PrefetchProxy(iconColView);
+        }
+        if (doc["icon-view-all"] === undefined) {
+            doc["icon-view-all"] = new PrefetchProxy(Docs.Create.TreeDocument([doc["icon-view"] as Doc, doc["icon-view-img"] as Doc, doc["icon-view-col"] as Doc], { title: "icon templates", _height: 75 }));
+        }
+        return doc["icon-view-all"] as Doc;
+    }
+
+    static creatorBtnDescriptors(doc: Doc): {
+        title: string, label: string, icon: string, drag?: string, ignoreClick?: boolean,
+        click?: string, ischecked?: string, activePen?: Doc, backgroundColor?: string, dragFactory?: Doc
+    }[] {
+        if (doc.emptyPresentation === undefined) {
+            doc.emptyPresentation = Docs.Create.PresDocument(new List<Doc>(),
+                { title: "Presentation", _viewType: CollectionViewType.Stacking, _LODdisable: true, _chromeStatus: "replaced", _showTitle: "title", boxShadow: "0 0" });
+        }
+        if (doc.emptyCollection === undefined) {
+            doc.emptyCollection = Docs.Create.FreeformDocument([],
+                { _nativeWidth: undefined, _nativeHeight: undefined, _LODdisable: true, _width: 150, _height: 100, title: "freeform" });
+        }
+        return [
+            { title: "Drag a collection", label: "Col", icon: "folder", click: 'openOnRight(getCopy(this.dragFactory, true))', drag: 'getCopy(this.dragFactory, true)', dragFactory: doc.emptyCollection as Doc },
+            { title: "Drag a web page", label: "Web", icon: "globe-asia", ignoreClick: true, drag: 'Docs.Create.WebDocument("", { title: "New Webpage" })' },
             { title: "Drag a cat image", label: "Img", icon: "cat", ignoreClick: true, drag: 'Docs.Create.ImageDocument("https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg", { _width: 250, _nativeWidth:250, title: "an image of a cat" })' },
             { title: "Drag a screenshot", label: "Grab", icon: "photo-video", ignoreClick: true, drag: 'Docs.Create.ScreenshotDocument("", { _width: 400, _height: 200, title: "screen snapshot" })' },
             { title: "Drag a webcam", label: "Cam", icon: "video", ignoreClick: true, drag: 'Docs.Create.WebCamDocument("", { _width: 400, _height: 400, title: "a test cam" })' },
             { title: "Drag a audio recorder", label: "Audio", icon: "microphone", ignoreClick: true, drag: `Docs.Create.AudioDocument("${nullAudio}", { _width: 200, title: "ready to record audio" })` },
             { title: "Drag a clickable button", label: "Btn", icon: "bolt", ignoreClick: true, drag: 'Docs.Create.ButtonDocument({ _width: 150, _height: 50, title: "Button" })' },
-            { title: "Drag a presentation view", label: "Prezi", icon: "tv", click: 'openOnRight(Doc.UserDoc().curPresentation = getCopy(this.dragFactory, true))', drag: `Doc.UserDoc().curPresentation = getCopy(this.dragFactory,true)`, dragFactory: emptyPresentation },
+            { title: "Drag a presentation view", label: "Prezi", icon: "tv", click: 'openOnRight(Doc.UserDoc().curPresentation = getCopy(this.dragFactory, true))', drag: `Doc.UserDoc().curPresentation = getCopy(this.dragFactory,true)`, dragFactory: doc.emptyPresentation as Doc },
             { title: "Drag a scripting box", label: "Script", icon: "terminal", ignoreClick: true, drag: 'Docs.Create.ScriptingDocument(undefined, { _width: 200, _height: 250 title: "untitled script" })' },
             { title: "Drag an import folder", label: "Load", icon: "cloud-upload-alt", ignoreClick: true, drag: 'Docs.Create.DirectoryImportDocument({ title: "Directory Import", _width: 400, _height: 400 })' },
             { title: "Drag a mobile view", label: "Phone", icon: "phone", ignoreClick: true, drag: 'Doc.UserDoc().activeMobile' },
@@ -101,59 +193,45 @@ export class CurrentUserUtils {
             { title: "Drag a document previewer", label: "Prev", icon: "expand", ignoreClick: true, drag: 'Docs.Create.DocumentDocument(ComputedField.MakeFunction("selectedDocs(this,this.excludeCollections,[_last_])?.[0]"), { _width: 250, _height: 250, title: "container" })' },
             // { title: "buxton", icon: "cloud-upload-alt", ignoreClick: true, drag: "Docs.Create.Buxton()" },
         ];
-        return docProtoData.filter(d => !alreadyCreatedButtons?.includes(d.title)).map(data => Docs.Create.FontIconDocument({
-            _nativeWidth: 100, _nativeHeight: 100, _width: 100, _height: 100,
-            icon: data.icon,
-            title: data.title,
-            label: data.label,
-            author: "Draggable Items",
-            ignoreClick: data.ignoreClick,
-            dropAction: data.click ? "copy" : undefined,
-            onDragStart: data.drag ? ScriptField.MakeFunction(data.drag) : undefined,
-            onClick: data.click ? ScriptField.MakeScript(data.click) : undefined,
-            ischecked: data.ischecked ? ComputedField.MakeFunction(data.ischecked) : undefined,
-            activePen: data.activePen,
-            backgroundColor: data.backgroundColor, removeDropProperties: new List<string>(["dropAction"]),
-            dragFactory: data.dragFactory,
-        }));
-    }
-
-    static setupTemplateButtons(doc: Doc) {
-        const queryTemplate = Docs.Create.MulticolumnDocument(
-            [
-                Docs.Create.QueryDocument({ title: "query", _height: 200, forceActive: true }),
-                Docs.Create.FreeformDocument([], { title: "data", _height: 100, _LODdisable: true, forceActive: true })
-            ],
-            { _width: 400, _height: 300, title: "queryView", _chromeStatus: "disabled", _xMargin: 3, _yMargin: 3, _autoHeight: false, forceActive: true, hideFilterView: true });
-        queryTemplate.isTemplateDoc = makeTemplate(queryTemplate);
-        const slideTemplate = Docs.Create.MultirowDocument(
-            [
-                Docs.Create.MulticolumnDocument([], { title: "data", _height: 200, forceActive: true }),
-                Docs.Create.TextDocument("", { title: "text", _height: 100, forceActive: true })
-            ],
-            { _width: 400, _height: 300, title: "slideView", _chromeStatus: "disabled", _xMargin: 3, _yMargin: 3, _autoHeight: false, forceActive: true, hideFilterView: true });
-        slideTemplate.isTemplateDoc = makeTemplate(slideTemplate);
-        const descriptionTemplate = Docs.Create.TextDocument("", { title: "text", _height: 100, _showTitle: "title" });
-        Doc.GetProto(descriptionTemplate).layout = FormattedTextBox.LayoutString("description");
-        descriptionTemplate.isTemplateDoc = makeTemplate(descriptionTemplate, true, "descriptionView");
-
-        doc.slidesBtn = CurrentUserUtils.ficon({ onDragStart: ScriptField.MakeFunction('getCopy(this.dragFactory, true)'), dragFactory: slideTemplate, author: "-Template Items", removeDropProperties: new List<string>(["dropAction"]), title: "presentation slide", icon: "address-card" });
-        doc.descriptionBtn = CurrentUserUtils.ficon({ onDragStart: ScriptField.MakeFunction('getCopy(this.dragFactory, true)'), dragFactory: descriptionTemplate, author: "-Template Items", removeDropProperties: new List<string>(["dropAction"]), title: "description view", icon: "window-maximize" });
-        doc.queryBtn = CurrentUserUtils.ficon({ onDragStart: ScriptField.MakeFunction('getCopy(this.dragFactory, true)'), dragFactory: queryTemplate, author: "-Template Items", removeDropProperties: new List<string>(["dropAction"]), title: "query view", icon: "question-circle" });
-
-        return [doc.slidesBtn as Doc, doc.descriptionBtn as Doc, doc.queryBtn as Doc];
     }
 
-
-    static async updateCreatorButtons(doc: Doc) {
-        const dragset = await Cast(doc.dragCreators, Doc);
-        if (dragset) {
-            const dragdocs = await Cast(dragset.data, listSpec(Doc));
-            if (dragdocs) {
-                const dragDocs = await Promise.all(dragdocs);
-                this.setupCreatorButtons(doc, dragDocs.map(d => StrCast(d.title))).map(nb => Doc.AddDocToList(dragset, "data", nb));
+    // setup the "creator" buttons for the sidebar-- eg. the default set of draggable document creation tools
+    static async setupCreatorButtons(doc: Doc) {
+        let alreadyCreatedButtons: string[] = [];
+        const dragCreatorSet = await Cast(doc.dragCreators, Doc, null);
+        if (dragCreatorSet) {
+            const dragCreators = await Cast(dragCreatorSet.data, listSpec(Doc));
+            if (dragCreators) {
+                const dragDocs = await Promise.all(dragCreators);
+                alreadyCreatedButtons = dragDocs.map(d => StrCast(d.title));
             }
         }
+        const creatorBtns = CurrentUserUtils.creatorBtnDescriptors(doc).filter(d => !alreadyCreatedButtons?.includes(d.title)).
+            map(data => Docs.Create.FontIconDocument({
+                _nativeWidth: 100, _nativeHeight: 100, _width: 100, _height: 100,
+                icon: data.icon,
+                title: data.title,
+                label: data.label,
+                ignoreClick: data.ignoreClick,
+                dropAction: data.click ? "copy" : undefined,
+                onDragStart: data.drag ? ScriptField.MakeFunction(data.drag) : undefined,
+                onClick: data.click ? ScriptField.MakeScript(data.click) : undefined,
+                ischecked: data.ischecked ? ComputedField.MakeFunction(data.ischecked) : undefined,
+                activePen: data.activePen,
+                backgroundColor: data.backgroundColor, removeDropProperties: new List<string>(["dropAction"]),
+                dragFactory: data.dragFactory,
+            }));
+
+        if (dragCreatorSet === undefined) {
+            doc.dragCreators = new PrefetchProxy(Docs.Create.MasonryDocument(creatorBtns, {
+                title: "Draggable Items", _showTitle: "title", _xMargin: 0,
+                _autoHeight: true, _width: 500, columnWidth: 35, ignoreClick: true, lockedPosition: true, _chromeStatus: "disabled",
+                dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }),
+            }));
+        } else {
+            creatorBtns.forEach(nb => Doc.AddDocToList(doc.dragCreators as Doc, "data", nb));
+        }
+        return doc.dragCreators as Doc;
     }
 
     static setupMobileButtons(doc: Doc, buttons?: string[]) {
@@ -231,96 +309,138 @@ export class CurrentUserUtils {
         });
     }
 
-    // setup the Creator button which will display the creator panel.  This panel will include the drag creators and the color picker.  when clicked, this panel will be displayed in the target container (ie, sidebarContainer)  
-    static setupToolsPanel(sidebarContainer: Doc, doc: Doc) {
+    // setup the Creator button which will display the creator panel.  This panel will include the drag creators and the color picker. 
+    // when clicked, this panel will be displayed in the target container (ie, sidebarContainer)  
+    static async setupToolsBtnPanel(doc: Doc, sidebarContainer: Doc) {
         // setup a masonry view of all he creators
-        const creatorBtns = CurrentUserUtils.setupCreatorButtons(doc);
-        doc.dragCreators = Docs.Create.MasonryDocument(creatorBtns, {
-            title: "drag Creators",
-            _pivotField: "author", _xMargin: 0,
-            _autoHeight: true, _width: 500, columnWidth: 35, ignoreClick: true, lockedPosition: true, _chromeStatus: "disabled",
-            dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }),
-        });
-        doc.allCreators = Docs.Create.StackingDocument([doc.dragCreators as any as Doc, doc.templateButtons as any as Doc], {
-            title: "all Creators", _yMargin: 0, _autoHeight: true, _xMargin: 0,
-            _width: 500, ignoreClick: true, lockedPosition: true, _chromeStatus: "disabled",
-        });
+        const creatorBtns = await CurrentUserUtils.setupCreatorButtons(doc);
+        const templateBtns = CurrentUserUtils.setupUserTemplateButtons(doc);
+
+        if (doc.allCreators === undefined) {
+            doc.allCreators = new PrefetchProxy(Docs.Create.StackingDocument([creatorBtns, templateBtns], {
+                title: "all Creators", _yMargin: 0, _autoHeight: true, _xMargin: 0,
+                _width: 500, ignoreClick: true, lockedPosition: true, _chromeStatus: "disabled",
+            }));
+        }
         // setup a color picker
-        const color = Docs.Create.ColorDocument({
-            title: "color picker", _width: 300, dropAction: "alias", forceActive: true, removeDropProperties: new List<string>(["dropAction", "forceActive"])
-        });
+        if (doc.colorPicker === undefined) {
+            const color = Docs.Create.ColorDocument({
+                title: "color picker", _width: 300, dropAction: "alias", forceActive: true, removeDropProperties: new List<string>(["dropAction", "forceActive"])
+            });
+            doc.colorPicker = new PrefetchProxy(color);
+        }
 
-        return Docs.Create.ButtonDocument({
-            _width: 35, _height: 25, title: "Tools", fontSize: 10, targetContainer: sidebarContainer,
-            letterSpacing: "0px", textTransform: "unset", borderRounding: "5px 5px 0px 0px", boxShadow: "3px 3px 0px rgb(34, 34, 34)",
-            sourcePanel: Docs.Create.StackingDocument([doc.allCreators as Doc, color], {
-                _width: 500, lockedPosition: true, _chromeStatus: "disabled", title: "tools stack", forceActive: true
-            }),
-            onClick: ScriptField.MakeScript("this.targetContainer.proto = this.sourcePanel"),
-        });
+        if (doc["tabs-button-tools"] === undefined) {
+            doc["tabs-button-tools"] = new PrefetchProxy(Docs.Create.ButtonDocument({
+                _width: 35, _height: 25, title: "Tools", fontSize: 10,
+                letterSpacing: "0px", textTransform: "unset", borderRounding: "5px 5px 0px 0px", boxShadow: "3px 3px 0px rgb(34, 34, 34)",
+                sourcePanel: new PrefetchProxy(Docs.Create.StackingDocument([doc.allCreators as Doc, doc.colorPicker as Doc], {
+                    _width: 500, lockedPosition: true, _chromeStatus: "disabled", title: "tools stack", forceActive: true
+                })) as any as Doc,
+                targetContainer: new PrefetchProxy(sidebarContainer) as any as Doc,
+                onClick: ScriptField.MakeScript("this.targetContainer.proto = this.sourcePanel"),
+            }));
+        }
+        (doc["tabs-button-tools"] as Doc).sourcePanel; // prefetch sourcePanel
+        return doc["tabs-button-tools"] as Doc;
     }
 
-    // setup the Library button which will display the library panel.  This panel includes a collection of workspaces, documents, and recently closed views
-    static setupLibraryPanel(sidebarContainer: Doc, doc: Doc) {
+    static setupWorkspaces(doc: Doc) {
         // setup workspaces library item
-        doc.workspaces = Docs.Create.TreeDocument([], {
-            title: "WORKSPACES", _height: 100, forceActive: true, boxShadow: "0 0", lockedPosition: true,
-        });
+        if (doc.worspaces === undefined) {
+            doc.workspaces = new PrefetchProxy(Docs.Create.TreeDocument([], {
+                title: "WORKSPACES", _height: 100, forceActive: true, boxShadow: "0 0", lockedPosition: true,
+            }));
+        }
         const newWorkspace = ScriptField.MakeScript(`createNewWorkspace()`);
         (doc.workspaces as Doc).contextMenuScripts = new List<ScriptField>([newWorkspace!]);
         (doc.workspaces as Doc).contextMenuLabels = new List<string>(["Create New Workspace"]);
 
-        doc.documents = Docs.Create.TreeDocument([], {
-            title: "DOCUMENTS", _height: 42, forceActive: true, boxShadow: "0 0", treeViewPreventOpen: true, lockedPosition: true,
-        });
-
+        return doc.workspaces as Doc;
+    }
+    static setupDocumentCollection(doc: Doc) {
+        if (doc.documents === undefined) {
+            doc.documents = new PrefetchProxy(Docs.Create.TreeDocument([], {
+                title: "DOCUMENTS", _height: 42, forceActive: true, boxShadow: "0 0", treeViewPreventOpen: true, lockedPosition: true,
+            }));
+        }
+        return doc.documents as Doc;
+    }
+    static setupRecentlyClosed(doc: Doc) {
         // setup Recently Closed library item
-        doc.recentlyClosed = Docs.Create.TreeDocument([], {
-            title: "RECENTLY CLOSED", _height: 75, forceActive: true, boxShadow: "0 0", treeViewPreventOpen: true, lockedPosition: true,
-        });
+        if (doc.recentlyClosed === undefined) {
+            doc.recentlyClosed = new PrefetchProxy(Docs.Create.TreeDocument([], {
+                title: "RECENTLY CLOSED", _height: 75, forceActive: true, boxShadow: "0 0", treeViewPreventOpen: true, lockedPosition: true,
+            }));
+        }
+        // this is equivalent to using PrefetchProxies to make sure the recentlyClosed doc is ready
+        PromiseValue(Cast(doc.recentlyClosed, Doc)).then(recent => recent && PromiseValue(recent.data).then(DocListCast));
         const clearAll = ScriptField.MakeScript(`self.data = new List([])`);
         (doc.recentlyClosed as Doc).contextMenuScripts = new List<ScriptField>([clearAll!]);
         (doc.recentlyClosed as Doc).contextMenuLabels = new List<string>(["Clear All"]);
 
-        return Docs.Create.ButtonDocument({
-            _width: 50, _height: 25, title: "Library", fontSize: 10,
-            letterSpacing: "0px", textTransform: "unset", borderRounding: "5px 5px 0px 0px", boxShadow: "3px 3px 0px rgb(34, 34, 34)",
-            sourcePanel: Docs.Create.TreeDocument([doc.workspaces as Doc, doc.documents as Doc, doc.recentlyClosed as Doc, doc], {
-                title: "Library", _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, childDropAction: "place", lockedPosition: true, boxShadow: "0 0", dontRegisterChildren: true
-            }),
-            targetContainer: sidebarContainer,
-            onClick: ScriptField.MakeScript("this.targetContainer.proto = this.sourcePanel;")
-        });
+        return doc.recentlyClosed as Doc;
+    }
+    // setup the Library button which will display the library panel.  This panel includes a collection of workspaces, documents, and recently closed views
+    static setupLibraryPanel(doc: Doc, sidebarContainer: Doc) {
+        const workspaces = CurrentUserUtils.setupWorkspaces(doc);
+        const documents = CurrentUserUtils.setupDocumentCollection(doc);
+        const recentlyClosed = CurrentUserUtils.setupRecentlyClosed(doc);
+
+        if (doc["tabs-button-library"] === undefined) {
+            doc["tabs-button-library"] = new PrefetchProxy(Docs.Create.ButtonDocument({
+                _width: 50, _height: 25, title: "Library", fontSize: 10,
+                letterSpacing: "0px", textTransform: "unset", borderRounding: "5px 5px 0px 0px", boxShadow: "3px 3px 0px rgb(34, 34, 34)",
+                sourcePanel: new PrefetchProxy(Docs.Create.TreeDocument([workspaces, documents, recentlyClosed, doc], {
+                    title: "Library", _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, childDropAction: "place", lockedPosition: true, boxShadow: "0 0", dontRegisterChildren: true
+                })) as any as Doc,
+                targetContainer: new PrefetchProxy(sidebarContainer) as any as Doc,
+                onClick: ScriptField.MakeScript("this.targetContainer.proto = this.sourcePanel;")
+            }));
+        }
+        return doc["tabs-button-library"] as Doc;
     }
 
     // setup the Search button which will display the search panel.  
-    static setupSearchPanel(sidebarContainer: Doc) {
-        return Docs.Create.ButtonDocument({
-            _width: 50, _height: 25, title: "Search", fontSize: 10,
-            letterSpacing: "0px", textTransform: "unset", borderRounding: "5px 5px 0px 0px", boxShadow: "3px 3px 0px rgb(34, 34, 34)",
-            sourcePanel: Docs.Create.QueryDocument({ title: "search stack", }),
-            targetContainer: sidebarContainer,
-            lockedPosition: true,
-            onClick: ScriptField.MakeScript("this.targetContainer.proto = this.sourcePanel")
-        });
+    static setupSearchBtnPanel(doc: Doc, sidebarContainer: Doc) {
+        if (doc["tabs-button-search"] === undefined) {
+            doc["tabs-button-search"] = new PrefetchProxy(Docs.Create.ButtonDocument({
+                _width: 50, _height: 25, title: "Search", fontSize: 10,
+                letterSpacing: "0px", textTransform: "unset", borderRounding: "5px 5px 0px 0px", boxShadow: "3px 3px 0px rgb(34, 34, 34)",
+                sourcePanel: new PrefetchProxy(Docs.Create.QueryDocument({ title: "search stack", })) as any as Doc,
+                targetContainer: new PrefetchProxy(sidebarContainer) as any as Doc,
+                lockedPosition: true,
+                onClick: ScriptField.MakeScript("this.targetContainer.proto = this.sourcePanel")
+            }));
+        }
+        return doc["tabs-button-search"] as Doc;
     }
 
-    // setup the list of sidebar mode buttons which determine what is displayed in the sidebar
-    static setupSidebarButtons(doc: Doc) {
-        const sidebarContainer = new Doc();
-        doc.sidebarContainer = new PrefetchProxy(sidebarContainer);
-        sidebarContainer._chromeStatus = "disabled";
-        sidebarContainer.onClick = ScriptField.MakeScript("freezeSidebar()");
+    static setupSidebarContainer(doc: Doc) {
+        if (doc["tabs-panelContainer"] === undefined) {
+            const sidebarContainer = new Doc();
+            sidebarContainer._chromeStatus = "disabled";
+            sidebarContainer.onClick = ScriptField.MakeScript("freezeSidebar()");
+            doc["tabs-panelContainer"] = new PrefetchProxy(sidebarContainer);
+        }
+        return doc["tabs-panelContainer"] as Doc;
+    }
 
-        doc.ToolsBtn = new PrefetchProxy(this.setupToolsPanel(sidebarContainer, doc));
-        doc.LibraryBtn = new PrefetchProxy(this.setupLibraryPanel(sidebarContainer, doc));
-        doc.SearchBtn = new PrefetchProxy(this.setupSearchPanel(sidebarContainer));
+    // setup the list of sidebar mode buttons which determine what is displayed in the sidebar
+    static async setupSidebarButtons(doc: Doc) {
+        const sidebarContainer = CurrentUserUtils.setupSidebarContainer(doc);
+        const toolsBtn = await CurrentUserUtils.setupToolsBtnPanel(doc, sidebarContainer);
+        const libraryBtn = CurrentUserUtils.setupLibraryPanel(doc, sidebarContainer);
+        const searchBtn = CurrentUserUtils.setupSearchBtnPanel(doc, sidebarContainer);
 
         // Finally, setup the list of buttons to display in the sidebar
-        doc.sidebarButtons = new PrefetchProxy(Docs.Create.StackingDocument([doc.SearchBtn as any as Doc, doc.LibraryBtn as any as Doc, doc.ToolsBtn as any as Doc], {
-            _width: 500, _height: 80, boxShadow: "0 0", _pivotField: "title", hideHeadings: true, ignoreClick: true, _chromeStatus: "view-mode",
-            title: "sidebar btn row stack", backgroundColor: "dimGray",
-        }));
+        if (doc["tabs-buttons"] === undefined) {
+            doc["tabs-buttons"] = new PrefetchProxy(Docs.Create.StackingDocument([searchBtn, libraryBtn, toolsBtn], {
+                _width: 500, _height: 80, boxShadow: "0 0", _pivotField: "title", hideHeadings: true, ignoreClick: true, _chromeStatus: "view-mode",
+                title: "sidebar btn row stack", backgroundColor: "dimGray",
+            }));
+            (toolsBtn.onClick as ScriptField).script.run({ this: toolsBtn });
+        }
     }
 
     static blist = (opts: DocumentOptions, docs: Doc[]) => new PrefetchProxy(Docs.Create.LinearDocument(docs, {
@@ -328,85 +448,101 @@ export class CurrentUserUtils {
         _gridGap: 5, _xMargin: 5, _yMargin: 5, _height: 42, _width: 100, boxShadow: "0 0", forceActive: true,
         dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }),
         backgroundColor: "black", treeViewPreventOpen: true, lockedPosition: true, _chromeStatus: "disabled", linearViewIsExpanded: true
-    })) as any as Doc;
+    })) as any as Doc
 
     static ficon = (opts: DocumentOptions) => new PrefetchProxy(Docs.Create.FontIconDocument({
         ...opts,
         dropAction: "alias", removeDropProperties: new List<string>(["dropAction"]), _nativeWidth: 100, _nativeHeight: 100, _width: 100, _height: 100
-    })) as any as Doc;
+    })) as any as Doc
 
     /// sets up the default list of buttons to be shown in the expanding button menu at the bottom of the Dash window
     static setupExpandingButtons(doc: Doc) {
-        doc.penBtn = CurrentUserUtils.ficon({
-            onClick: ScriptField.MakeScript("activatePen(this.activePen.pen = sameDocs(this.activePen.pen, this) ? undefined : this,2, this.backgroundColor)"),
-            author: "systemTemplates", title: "ink mode", icon: "pen-nib", ischecked: ComputedField.MakeFunction(`sameDocs(this.activePen.pen,  this)`), activePen: doc
-        })
-        doc.undoBtn = CurrentUserUtils.ficon({ onClick: ScriptField.MakeScript("undo()"), title: "undo button", icon: "undo-alt" });
-        doc.redoBtn = CurrentUserUtils.ficon({ onClick: ScriptField.MakeScript("redo()"), title: "redo button", icon: "redo-alt" });
-        doc.expandingButtons = CurrentUserUtils.blist({ title: "expanding buttons", ignoreClick: true }, [doc.undoBtn as Doc, doc.redoBtn as Doc, doc.penBtn as Doc]);
+        if (doc.penBtn === undefined) {
+            doc.penBtn = CurrentUserUtils.ficon({
+                onClick: ScriptField.MakeScript("activatePen(this.activePen.pen = sameDocs(this.activePen.pen, this) ? undefined : this,2, this.backgroundColor)"),
+                author: "systemTemplates", title: "ink mode", icon: "pen-nib", ischecked: ComputedField.MakeFunction(`sameDocs(this.activePen.pen,  this)`), activePen: doc
+            });
+        }
+        if (doc.undoBtn === undefined) {
+            doc.undoBtn = CurrentUserUtils.ficon({ onClick: ScriptField.MakeScript("undo()"), title: "undo button", icon: "undo-alt" });
+        }
+        if (doc.redoBtn === undefined) {
+            doc.redoBtn = CurrentUserUtils.ficon({ onClick: ScriptField.MakeScript("redo()"), title: "redo button", icon: "redo-alt" });
+        }
+        if (doc.expandingButtons === undefined) {
+            doc.expandingButtons = CurrentUserUtils.blist({ title: "expanding buttons", ignoreClick: true }, [doc.undoBtn as Doc, doc.redoBtn as Doc, doc.penBtn as Doc]);
+        }
     }
     // sets up the default set of documents to be shown in the Overlay layer
     static setupOverlays(doc: Doc) {
-        doc.overlays = new PrefetchProxy(Docs.Create.FreeformDocument([], { title: "Overlays", backgroundColor: "#aca3a6" }));
+        if (doc.overlays === undefined) {
+            doc.overlays = new PrefetchProxy(Docs.Create.FreeformDocument([], { title: "Overlays", backgroundColor: "#aca3a6" }));
+        }
     }
 
     // the initial presentation Doc to use
     static setupDefaultPresentation(doc: Doc) {
-        doc.presentationTemplate = new PrefetchProxy(Docs.Create.PresElementBoxDocument({ title: "pres element template", backgroundColor: "transparent", _xMargin: 5, _height: 46, isTemplateDoc: true, isTemplateForField: "data" }));
-        doc.curPresentation = Docs.Create.PresDocument(new List<Doc>(), { title: "Presentation", _viewType: CollectionViewType.Stacking, _LODdisable: true, _chromeStatus: "replaced", _showTitle: "title", boxShadow: "0 0" });
+        if (doc.presentationTemplate === undefined) {
+            doc.presentationTemplate = new PrefetchProxy(Docs.Create.PresElementBoxDocument({
+                title: "pres element template", backgroundColor: "transparent", _xMargin: 5, _height: 46, isTemplateDoc: true, isTemplateForField: "data"
+            }));
+        }
+        if (doc.curPresentation === undefined) {
+            doc.curPresentation = Docs.Create.PresDocument(new List<Doc>(), {
+                title: "Presentation", _viewType: CollectionViewType.Stacking,
+                _LODdisable: true, _chromeStatus: "replaced", _showTitle: "title", boxShadow: "0 0"
+            });
+        }
     }
 
     static setupMobileUploads(doc: Doc) {
-        doc.optionalRightCollection = new PrefetchProxy(Docs.Create.StackingDocument([], { title: "New mobile uploads" }));
+        if (doc.optionalRightCollection === undefined) {
+            doc.optionalRightCollection = new PrefetchProxy(Docs.Create.StackingDocument([], { title: "New mobile uploads" }));
+        }
     }
 
-    static setupChildClicks(doc: Doc) {
-        const openInTarget = Docs.Create.ScriptingDocument(ScriptField.MakeScript(
-            "docCast(thisContainer.target).then((target) => { target && docCast(this.source).then((source) => { target.proto.data = new List([source || this]); } ); } )",
-            { target: Doc.name }), { title: "On Child Clicked (open in target)", _width: 300, _height: 200 });
-        const onClick = Docs.Create.ScriptingDocument(undefined, { title: "onClick", "onClick-rawScript": "console.log('click')", isTemplateDoc: true, isTemplateForField: "onClick", _width: 300, _height: 200 }, "onClick");
-        const onCheckedClick = Docs.Create.ScriptingDocument(undefined,
-            { title: "onCheckedClick", "onCheckedClick-rawScript": "console.log(heading + checked + containingTreeView)", "onCheckedClick-params": new List<string>(["heading", "checked", "containingTreeView"]), isTemplateDoc: true, isTemplateForField: "onCheckedClick", _width: 300, _height: 200 }, "onCheckedClick");
-        doc.childClickFuncs = Docs.Create.TreeDocument([openInTarget], { title: "on Child Click function templates" });
-        doc.clickFuncs = Docs.Create.TreeDocument([onClick, onCheckedClick], { title: "onClick funcs" });
-    }
-
-    static updateUserDocument(doc: Doc) {
-        doc.title = Doc.CurrentUserEmail;
-        new InkingControl();
-        (doc.childClickFuncs === undefined) && CurrentUserUtils.setupChildClicks(doc);
-        (doc.iconTypes === undefined) && CurrentUserUtils.setupDefaultIconTypes(doc);
-        (doc.templateDocs === undefined) && CurrentUserUtils.setupDefaultDocTemplates(doc);
-        (doc.optionalRightCollection === undefined) && CurrentUserUtils.setupMobileUploads(doc);
-        (doc.overlays === undefined) && CurrentUserUtils.setupOverlays(doc);
-        (doc.expandingButtons === undefined) && CurrentUserUtils.setupExpandingButtons(doc);
-        (doc.curPresentation === undefined) && CurrentUserUtils.setupDefaultPresentation(doc);
-        (doc.sidebarButtons === undefined) && CurrentUserUtils.setupSidebarButtons(doc);
-        doc.linkDb = Docs.Prototypes.MainLinkDocument();
+    static setupClickEditorTemplates(doc: Doc) {
+        if (doc.childClickFuncs === undefined) {
+            const openInTarget = Docs.Create.ScriptingDocument(ScriptField.MakeScript(
+                "docCast(thisContainer.target).then((target) => { target && docCast(this.source).then((source) => { target.proto.data = new List([source || this]); } ); } )",
+                { target: Doc.name }), { title: "On Child Clicked (open in target)", _width: 300, _height: 200 });
 
+            doc.childClickFuncs = Docs.Create.TreeDocument([openInTarget], { title: "on Child Click function templates" });
+        }
         // this is equivalent to using PrefetchProxies to make sure all the childClickFuncs have been retrieved.
         PromiseValue(Cast(doc.childClickFuncs, Doc)).then(func => func && PromiseValue(func.data).then(DocListCast));
-        // this is equivalent to using PrefetchProxies to make sure the recentlyClosed doc is ready
-        PromiseValue(Cast(doc.recentlyClosed, Doc)).then(recent => recent && PromiseValue(recent.data).then(DocListCast));
-        // this is equivalent to using PrefetchProxies to make sure all the sidebarButtons and noteType internal Doc's have been retrieved.
-        PromiseValue(Cast(doc.noteTypes, Doc)).then(noteTypes => noteTypes && PromiseValue(noteTypes.data).then(DocListCast));
+
+        if (doc.clickFuncs === undefined) {
+            const onClick = Docs.Create.ScriptingDocument(undefined, {
+                title: "onClick", "onClick-rawScript": "console.log('click')",
+                isTemplateDoc: true, isTemplateForField: "onClick", _width: 300, _height: 200
+            }, "onClick");
+            const onCheckedClick = Docs.Create.ScriptingDocument(undefined, {
+                title: "onCheckedClick", "onCheckedClick-rawScript": "console.log(heading + checked + containingTreeView)", "onCheckedClick-params": new List<string>(["heading", "checked", "containingTreeView"]), isTemplateDoc: true, isTemplateForField: "onCheckedClick", _width: 300, _height: 200
+            }, "onCheckedClick");
+            doc.clickFuncs = Docs.Create.TreeDocument([onClick, onCheckedClick], { title: "onClick funcs" });
+        }
         PromiseValue(Cast(doc.clickFuncs, Doc)).then(func => func && PromiseValue(func.data).then(DocListCast));
-        PromiseValue(Cast(doc.childClickFuncs, Doc)).then(func => func && PromiseValue(func.data).then(DocListCast));
-        PromiseValue(Cast(doc.sidebarButtons, Doc)).then(stackingDoc => {
-            stackingDoc && PromiseValue(Cast(stackingDoc.data, listSpec(Doc))).then(sidebarButtons => {
-                sidebarButtons?.map((sidebarBtn, i) => {
-                    sidebarBtn && PromiseValue(Cast(sidebarBtn, Doc)).then(async btn => { // choose which item to display first in sidebar panel (i === 0,1, or 2)
-                        btn?.sourcePanel && btn.targetContainer && i === 2 && (btn.onClick as ScriptField).script.run({ this: btn });
-                    });
-                });
-            });
-        });
+
+        return doc.clickFuncs as Doc;
+    }
+
+    static async updateUserDocument(doc: Doc) {
+        new InkingControl();
+        doc.title = Doc.CurrentUserEmail;
+        doc.activePen = doc;
+        CurrentUserUtils.setupDefaultIconTemplates(doc);  // creates a set of icon templates triggered by the document deoration icon
+        CurrentUserUtils.setupMobileUploads(doc);  // sets up the optional right collection of documents uploaded from mobile
+        CurrentUserUtils.setupOverlays(doc);  // documents in overlay layer 
+        CurrentUserUtils.setupExpandingButtons(doc);  // the bottom bar of font icons
+        CurrentUserUtils.setupDefaultPresentation(doc); // presentation that's initially triggered
+        await CurrentUserUtils.setupSidebarButtons(doc); // the pop-out left sidebar of tools/panels
+        doc.linkDb = Docs.Prototypes.MainLinkDocument();
 
         // setup reactions to change the highlights on the undo/redo buttons -- would be better to encode this in the undo/redo buttons, but the undo/redo stacks are not wired up that way yet
         doc.undoBtn && reaction(() => UndoManager.undoStack.slice(), () => Doc.GetProto(doc.undoBtn as Doc).opacity = UndoManager.CanUndo() ? 1 : 0.4, { fireImmediately: true });
         doc.redoBtn && reaction(() => UndoManager.redoStack.slice(), () => Doc.GetProto(doc.redoBtn as Doc).opacity = UndoManager.CanRedo() ? 1 : 0.4, { fireImmediately: true });
 
-        this.updateCreatorButtons(doc);
         return doc;
     }
 
diff --git a/src/server/database.ts b/src/server/database.ts
index 1da31c5ff..d74bd7321 100644
--- a/src/server/database.ts
+++ b/src/server/database.ts
@@ -52,9 +52,6 @@ export namespace Database {
         private currentWrites: { [id: string]: Promise<void> } = {};
         private db?: mongodb.Db;
         private onConnect: (() => void)[] = [];
-        constructor() {
-        }
-
         doConnect() {
             console.error(`\nConnecting to Mongo with URL : ${url}\n`);
             this.MongoClient.connect(url, { connectTimeoutMS: 30000, socketTimeoutMS: 30000, useUnifiedTopology: true }, (_err, client) => {
-- 
cgit v1.2.3-70-g09d2

