From 3e6f24e4ac4a4b64620d8c9f614f214f8f1c7b94 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Tue, 17 Sep 2019 19:47:05 -0400
Subject: cleanup for animating document icon collapse/expand - still need to
 fix for docs without native dimensions.   lots of lint fixes.

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

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

diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 7829bd7f1..9a2105467 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -439,7 +439,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
             let heading = NumCast(dv.props.Document.heading);
             ruleProvider && heading && (Doc.GetProto(ruleProvider)["ruleRounding_" + heading] = `${Math.min(100, dist)}%`);
             usingRule = usingRule || (ruleProvider && heading ? true : false);
-        })
+        });
         !usingRule && SelectionManager.SelectedDocuments().map(dv => dv.props.Document.layout instanceof Doc ? dv.props.Document.layout : dv.props.Document.isTemplate ? dv.props.Document : Doc.GetProto(dv.props.Document)).
             map(d => d.borderRounding = `${Math.min(100, dist)}%`);
         e.stopPropagation();
-- 
cgit v1.2.3-70-g09d2


From b93a9b48a00571e07859f5b6c6ade1cdf764e8ea Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Wed, 18 Sep 2019 00:20:58 -0400
Subject: cleaned up custom template layouts

---
 src/client/util/DragManager.ts                     |   2 +-
 src/client/views/DocumentDecorations.tsx           |  27 ++--
 src/client/views/TemplateMenu.tsx                  |   2 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |   4 +-
 src/client/views/nodes/ButtonBox.tsx               |   4 +-
 src/client/views/nodes/DocumentContentsView.tsx    |   9 +-
 src/client/views/nodes/DocumentView.tsx            | 166 +++++++--------------
 src/new_fields/Doc.ts                              |  16 +-
 8 files changed, 80 insertions(+), 150 deletions(-)

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

diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 252accefa..cb55e1bc6 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -250,7 +250,7 @@ export namespace DragManager {
             });
     }
 
-    export function StartButtonDrag(eles: HTMLElement[], script: string, title: string, vars: { [name: string]: Field }, params: string[], initialize?: (button: Doc) => void, downX: number, downY: number, options?: DragOptions) {
+    export function StartButtonDrag(eles: HTMLElement[], script: string, title: string, vars: { [name: string]: Field }, params: string[], initialize: (button: Doc) => void, downX: number, downY: number, options?: DragOptions) {
         let dragData = new DragManager.DocumentDragData([]);
         runInAction(() => StartDragFunctions.map(func => func()));
         StartDrag(eles, dragData, downX, downY, options, options && options.finishDrag ? options.finishDrag :
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 9a2105467..2826f4422 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -1,40 +1,37 @@
-import { library, IconProp } from '@fortawesome/fontawesome-svg-core';
-import { faLink, faTag, faTimes, faArrowAltCircleDown, faArrowAltCircleUp, faCheckCircle, faStopCircle, faCloudUploadAlt, faSyncAlt, faShare } from '@fortawesome/free-solid-svg-icons';
+import { IconProp, library } from '@fortawesome/fontawesome-svg-core';
+import { faArrowAltCircleDown, faArrowAltCircleUp, faCheckCircle, faCloudUploadAlt, faLink, faShare, faStopCircle, faSyncAlt, faTag, faTimes } from '@fortawesome/free-solid-svg-icons';
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action, computed, observable, reaction, runInAction, trace } from "mobx";
+import { action, computed, observable, reaction, runInAction } from "mobx";
 import { observer } from "mobx-react";
 import { Doc, DocListCastAsync } from "../../new_fields/Doc";
 import { List } from "../../new_fields/List";
+import { ObjectField } from '../../new_fields/ObjectField';
+import { RichTextField } from '../../new_fields/RichTextField';
 import { BoolCast, Cast, NumCast, StrCast } from "../../new_fields/Types";
 import { URLField } from '../../new_fields/URLField';
+import { CurrentUserUtils } from '../../server/authentication/models/current_user_utils';
 import { emptyFunction, Utils } from "../../Utils";
+import { Pulls, Pushes } from '../apis/google_docs/GoogleApiClientUtils';
 import { Docs, DocUtils } from "../documents/Documents";
 import { DocumentManager } from "../util/DocumentManager";
 import { DragLinksAsDocuments, DragManager } from "../util/DragManager";
+import { LinkManager } from '../util/LinkManager';
 import { SelectionManager } from "../util/SelectionManager";
 import { undoBatch, UndoManager } from "../util/UndoManager";
 import { MINIMIZED_ICON_SIZE } from "../views/globalCssVariables.scss";
 import { CollectionView } from "./collections/CollectionView";
 import './DocumentDecorations.scss';
+import { LinkMenu } from "./linking/LinkMenu";
+import { MetadataEntryMenu } from './MetadataEntryMenu';
+import { PositionDocument } from './nodes/CollectionFreeFormDocumentView';
 import { DocumentView } from "./nodes/DocumentView";
 import { FieldView } from "./nodes/FieldView";
 import { FormattedTextBox, GoogleRef } from "./nodes/FormattedTextBox";
 import { IconBox } from "./nodes/IconBox";
-import { LinkMenu } from "./linking/LinkMenu";
+import { ImageBox } from './nodes/ImageBox';
 import { TemplateMenu } from "./TemplateMenu";
 import { Template, Templates } from "./Templates";
 import React = require("react");
-import { RichTextField } from '../../new_fields/RichTextField';
-import { LinkManager } from '../util/LinkManager';
-import { MetadataEntryMenu } from './MetadataEntryMenu';
-import { ImageBox } from './nodes/ImageBox';
-import { CurrentUserUtils } from '../../server/authentication/models/current_user_utils';
-import { Pulls, Pushes } from '../apis/google_docs/GoogleApiClientUtils';
-import { ObjectField } from '../../new_fields/ObjectField';
-import { DocServer } from '../DocServer';
-import { CompileScript } from '../util/Scripting';
-import { ComputedField } from '../../new_fields/ScriptField';
-import { PositionDocument } from './nodes/CollectionFreeFormDocumentView';
 const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx
index d57a72dad..397bb14a4 100644
--- a/src/client/views/TemplateMenu.tsx
+++ b/src/client/views/TemplateMenu.tsx
@@ -123,7 +123,7 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
                 <div title="Template Options" className="templating-button" onClick={() => this.toggleTemplateActivity()}>+</div>
                 <ul id="template-list" ref={this.dragRef} style={{ display: this._hidden ? "none" : "block" }}>
                     {templateMenu}
-                    <button onClick={this.toggleCustom}>{this.props.docs[0].Document.nativeLayout ? "Native" : "Custom"}</button>
+                    <button onClick={this.toggleCustom}>{typeof this.props.docs[0].Document.layout === "string" ? "Custom" : "Native"}</button>
                     <button onClick={this.toggleFloat}>Float</button>
                     {/* <button onClick={this.clearTemplates}>Clear</button> */}
                 </ul>
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 8d392d764..0c559d83d 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -903,9 +903,9 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
             description: "Add Note ...",
             subitems: DocListCast((CurrentUserUtils.UserDocument.noteTypes as Doc).data).map((note, i) => ({
                 description: (i + 1) + ": " + StrCast(note.title),
-                event: ({ x, y }) => this.addLiveTextBox(Docs.Create.TextDocument({ width: 200, height: 100, x: this.getTransform().transformPoint(x, y)[0], y: this.getTransform().transformPoint(x, y)[1], autoHeight: true, layout: note, title: StrCast(note.title) })),
+                event: (args: { x: number, y: number }) => this.addLiveTextBox(Docs.Create.TextDocument({ width: 200, height: 100, x: this.getTransform().transformPoint(args.x, args.y)[0], y: this.getTransform().transformPoint(args.x, args.y)[1], autoHeight: true, layout: note, title: StrCast(note.title) })),
                 icon: "eye"
-            })),
+            })) as ContextMenuProps[],
             icon: "eye"
         });
         ContextMenu.Instance.addItem({ description: "Freeform Options ...", subitems: layoutItems, icon: "eye" });
diff --git a/src/client/views/nodes/ButtonBox.tsx b/src/client/views/nodes/ButtonBox.tsx
index eebf6c167..f08ea4891 100644
--- a/src/client/views/nodes/ButtonBox.tsx
+++ b/src/client/views/nodes/ButtonBox.tsx
@@ -3,7 +3,7 @@ import { faEdit } from '@fortawesome/free-regular-svg-icons';
 import { action, computed } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
-import { Doc, DocListCastAsync } from '../../../new_fields/Doc';
+import { Doc, DocListCastAsync, DocListCast } from '../../../new_fields/Doc';
 import { List } from '../../../new_fields/List';
 import { createSchema, makeInterface, listSpec } from '../../../new_fields/Schema';
 import { ScriptField } from '../../../new_fields/ScriptField';
@@ -68,7 +68,7 @@ export class ButtonBox extends DocComponent<FieldViewProps, ButtonDocument>(Butt
     render() {
         let params = Cast(this.props.Document.buttonParams, listSpec("string"));
         let missingParams = params && params.filter(p => this.props.Document[p] === undefined);
-        params && params.map(async p => await DocListCastAsync(this.props.Document[p])); // bcz: really hacky form of prefetching ... 
+        params && params.map(p => DocListCast(this.props.Document[p])); // bcz: really hacky form of prefetching ... 
         return (
             <div className="buttonBox-outerDiv" ref={this.createDropTarget} onContextMenu={this.specificContextMenu}>
                 <div className="buttonBox-mainButton" style={{ background: StrCast(this.props.Document.backgroundColor), color: StrCast(this.props.Document.color, "black") }} >
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index d0e117fe4..3c3cc0d91 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -93,13 +93,6 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
         return { props: list };
     }
 
-    @computed get templates(): List<string> {
-        let field = this.props.Document.templates;
-        if (field && field instanceof List) {
-            return field;
-        }
-        return new List<string>();
-    }
     @computed get finalLayout() {
         return this.props.layoutKey === "overlayLayout" ? "<div/>" : this.layout;
     }
@@ -107,7 +100,7 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
     render() {
         let self = this;
         if (this.props.renderDepth > 7) return (null);
-        if (!this.layout && (this.props.layoutKey !== "overlayLayout" || !this.templates.length)) return (null);
+        if (!this.layout && this.props.layoutKey !== "overlayLayout") return (null);
         return <ObserverJsxParser
             blacklistedAttrs={[]}
             components={{ FormattedTextBox, ImageBox, IconBox, DirectoryImportBox, DragBox, ButtonBox, FieldView, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, CollectionPDFView, CollectionVideoView, WebBox, KeyValueBox, PDFBox, VideoBox, AudioBox, HistogramBox, PresBox, YoutubeBox, LinkFollowBox }}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 2c7df334f..152dc3ddd 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -377,56 +377,31 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     deleteClicked = (): void => { SelectionManager.DeselectAll(); this.props.removeDocument && this.props.removeDocument(this.props.Document); }
 
     @undoBatch
-    makeNativeViewClicked = (): void => {
-        makeNativeView(this.props.Document);
-    }
+    makeNativeViewClicked = (): void => { swapViews(this.props.Document, "layoutNative", "layoutCustom"); }
+
     @undoBatch
-    makeCustomViewClicked = (): void => {
-        this.props.Document.nativeLayout = this.Document.layout;
-        this.props.Document.nativeType = this.Document.type;
-        this.props.Document.nonCustomAutoHeight = this.Document.autoHeight;
-        this.props.Document.nonCustomWidth = this.Document.width;
-        this.props.Document.nonCustomHeight = this.Document.height;
-        this.props.Document.nonCustomNativeWidth = this.Document.nativeWidth;
-        this.props.Document.nonCustomNativeHeight = this.Document.nativeHeight;
-        this.props.Document.nonCustomIgnoreAspect = this.Document.ignoreAspect;
-        PromiseValue(Cast(this.props.Document.customLayout, Doc)).then(custom => {
-            if (custom) {
-                this.Document.type = DocumentType.TEMPLATE;
-                this.props.Document.layout = custom;
-                !custom.nativeWidth && (this.Document.nativeWidth = 0);
-                !custom.nativeHeight && (this.Document.nativeHeight = 0);
-                !custom.nativeWidth && (this.Document.ignoreAspect = true);
-                this.Document.autoHeight = BoolCast(this.Document.customAutoHeight);
-                this.Document.width = NumCast(this.props.Document.customWidth);
-                this.Document.height = NumCast(this.props.Document.customHeight);
-                this.Document.nativeWidth = NumCast(this.props.Document.customNativeWidth);
-                this.Document.nativeHeight = NumCast(this.props.Document.customNativeHeight);
-                this.Document.ignoreAspect = BoolCast(this.Document.customIgnoreAspect);
-                this.props.Document.customAutoHeight = undefined;
-                this.props.Document.customWidth = undefined;
-                this.props.Document.customHeight = undefined;
-                this.props.Document.customNativeWidth = undefined;
-                this.props.Document.customNativeHeight = undefined;
-                this.props.Document.customIgnoreAspect = undefined;
-            } else {
-                let options = { title: "data", width: (this.Document.width || 0), x: -(this.Document.width || 0) / 2, y: - (this.Document.height || 0) / 2, };
-                let fieldTemplate = this.Document.type === DocumentType.TEXT ? Docs.Create.TextDocument(options) :
-                    this.Document.type === DocumentType.VID ? Docs.Create.VideoDocument("http://www.cs.brown.edu", options) :
-                        Docs.Create.ImageDocument("http://www.cs.brown.edu", options);
+    makeCustomViewClicked = async () => {
+        if (this.props.Document.layoutCustom === undefined) {
+            Doc.GetProto(this.dataDoc || this.props.Document).layoutNative = Doc.MakeTitled("layoutNative");
+            await swapViews(this.props.Document, "", "layoutNative");
 
-                fieldTemplate.backgroundColor = this.Document.backgroundColor;
-                fieldTemplate.heading = 1;
-                fieldTemplate.autoHeight = true;
+            let options = { title: "data", width: (this.Document.width || 0), x: -(this.Document.width || 0) / 2, y: - (this.Document.height || 0) / 2, };
+            let fieldTemplate = this.Document.type === DocumentType.TEXT ? Docs.Create.TextDocument(options) :
+                this.Document.type === DocumentType.VID ? Docs.Create.VideoDocument("http://www.cs.brown.edu", options) :
+                    Docs.Create.ImageDocument("http://www.cs.brown.edu", options);
 
-                let docTemplate = Docs.Create.FreeformDocument([fieldTemplate], { title: this.Document.title + "layout", width: (this.Document.width || 0) + 20, height: Math.max(100, (this.Document.height || 0) + 45) });
-                let proto = Doc.GetProto(docTemplate);
-                Doc.MakeMetadataFieldTemplate(fieldTemplate, proto, true);
+            fieldTemplate.backgroundColor = this.Document.backgroundColor;
+            fieldTemplate.heading = 1;
+            fieldTemplate.autoHeight = true;
 
-                Doc.ApplyTemplateTo(docTemplate, this.props.Document, undefined, false);
-                Doc.GetProto(this.dataDoc || this.props.Document).customLayout = this.Document.layout;
-            }
-        });
+            let docTemplate = Docs.Create.FreeformDocument([fieldTemplate], { title: this.Document.title + "_layout", width: (this.Document.width || 0) + 20, height: Math.max(100, (this.Document.height || 0) + 45) });
+
+            Doc.MakeMetadataFieldTemplate(fieldTemplate, Doc.GetProto(docTemplate), true);
+            Doc.ApplyTemplateTo(docTemplate, this.props.Document, undefined);
+            Doc.GetProto(this.dataDoc || this.props.Document).layoutCustom = Doc.MakeTitled("layoutCustom");
+        } else {
+            swapViews(this.props.Document, "layoutCustom", "layoutNative");
+        }
     }
 
     @undoBatch
@@ -487,9 +462,9 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     onDrop = (e: React.DragEvent) => {
         let text = e.dataTransfer.getData("text/plain");
         if (!e.isDefaultPrevented() && text && text.startsWith("<div")) {
-            let oldLayout = FieldValue(this.Document.layout) || "";
+            let oldLayout = StrCast(this.props.Document.layout);
             let layout = text.replace("{layout}", oldLayout);
-            this.Document.layout = layout;
+            this.props.Document.layout = layout;
             e.stopPropagation();
             e.preventDefault();
         }
@@ -527,9 +502,9 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         if (this.props.ContainingCollectionView && this.props.ContainingCollectionView.props.DataDoc) {
             Doc.MakeMetadataFieldTemplate(this.props.Document, this.props.ContainingCollectionView.props.DataDoc);
         } else {
-            if (this.Document.type !== DocumentType.COL && this.Document.type !== DocumentType.TEMPLATE) {
+            if (typeof this.props.Document.layout === "string") {
                 this.makeCustomViewClicked();
-            } else if (this.Document.nativeLayout) {
+            } else {
                 this.makeNativeViewClicked();
             }
         }
@@ -624,7 +599,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         }
         if (this.Document.type !== DocumentType.COL && this.Document.type !== DocumentType.TEMPLATE) {
             layoutItems.push({ description: "Use Custom Layout", event: this.makeCustomViewClicked, icon: "concierge-bell" });
-        } else if (this.props.Document.nativeLayout) {
+        } else if (this.props.Document.layoutNative) {
             layoutItems.push({ description: "Use Native Layout", event: this.makeNativeViewClicked, icon: "concierge-bell" });
         }
         !existing && cm.addItem({ description: "Layout...", subitems: layoutItems, icon: "compass" });
@@ -741,12 +716,6 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     chromeHeight = () => {
         let showOverlays = this.props.showOverlays ? this.props.showOverlays(this.layoutDoc) : undefined;
         let showTitle = showOverlays && "title" in showOverlays ? showOverlays.title : StrCast(this.layoutDoc.showTitle);
-        let templates = Cast(this.layoutDoc.templates, listSpec("string"));
-        if (!showOverlays && templates instanceof List) {
-            templates.map(str => {
-                if (!showTitle && str.indexOf("{props.Document.title}") !== -1) showTitle = "title";
-            });
-        }
         return (showTitle ? 25 : 0) + 1;// bcz: why 8??
     }
 
@@ -843,66 +812,33 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     }
 }
 
+let swapViews = async (doc: any, newLayoutField: any, oldLayoutField: any) => {
+    let oldLayoutExt = await Cast(doc[oldLayoutField], Doc);
+    if (oldLayoutExt) {
+        oldLayoutExt.autoHeight = doc.autoHeight;
+        oldLayoutExt.width = doc.width;
+        oldLayoutExt.height = doc.height;
+        oldLayoutExt.nativeWidth = doc.nativeWidth;
+        oldLayoutExt.nativeHeight = doc.nativeHeight;
+        oldLayoutExt.ignoreAspect = doc.ignoreAspect;
+        oldLayoutExt.type = doc.type;
+        oldLayoutExt.layout = doc.layout;
+    }
 
-let makeNativeView = (doc: any): void => {
-    doc.layout = doc.nativeLayout;
-    doc.nativeLayout = undefined;
-    doc.type = doc.nativeType;
-
-    doc.customAutoHeight = doc.autoHeight;
-    doc.customWidth = doc.width;
-    doc.customHeight = doc.height;
-    doc.customNativeWidth = doc.nativeWidth;
-    doc.customNativeHeight = doc.nativeHeight;
-    doc.customIgnoreAspect = doc.ignoreAspect;
-
-    doc.autoHeight = doc.nonCustomAutoHeight;
-    doc.width = doc.nonCustomWidth;
-    doc.height = doc.nonCustomHeight;
-    doc.nativeWidth = doc.nonCustomNativeWidth;
-    doc.nativeHeight = doc.nonCustomNativeHeight;
-    doc.ignoreAspect = doc.nonCustomIgnoreAspect;
-    doc.nonCustomAutoHeight = undefined;
-    doc.nonCustomWidth = undefined;
-    doc.nonCustomHeight = undefined;
-    doc.nonCustomNativeWidth = undefined;
-    doc.nonCustomNativeHeight = undefined;
-    doc.nonCustomIgnoreAspect = undefined;
-};
-let makeCustomView = (doc: any): void => {
-    doc.nativeLayout = doc.layout;
-    doc.nativeType = doc.type;
-    doc.nonCustomAutoHeight = doc.autoHeight;
-    doc.nonCustomWidth = doc.nativeWidth;
-    doc.nonCustomHeight = doc.nativeHeight;
-    doc.nonCustomNativeWidth = doc.nativeWidth;
-    doc.nonCustomNativeHeight = doc.nativeHeight;
-    doc.nonCustomIgnoreAspect = doc.ignoreAspect;
-    let custom = doc.customLayout as Doc;
-    if (custom instanceof Doc) {
-        doc.type = DocumentType.TEMPLATE;
-        doc.layout = custom;
-        !custom.nativeWidth && (doc.nativeWidth = 0);
-        !custom.nativeHeight && (doc.nativeHeight = 0);
-        !custom.nativeWidth && (doc.ignoreAspect = true);
-        doc.autoHeight = doc.autoHeight;
-        doc.width = doc.customWidth;
-        doc.height = doc.customHeight;
-        doc.nativeWidth = doc.customNativeWidth;
-        doc.nativeHeight = doc.customNativeHeight;
-        doc.ignoreAspect = doc.ignoreAspect;
-        doc.customAutoHeight = undefined;
-        doc.customWidth = undefined;
-        doc.customHeight = undefined;
-        doc.customNativeWidth = undefined;
-        doc.customNativeHeight = undefined;
-        doc.customIgnoreAspect = undefined;
+    let newLayoutExt = newLayoutField && await Cast(doc[newLayoutField], Doc);
+    if (newLayoutExt) {
+        doc.autoHeight = newLayoutExt.autoHeight;
+        doc.width = newLayoutExt.width;
+        doc.height = newLayoutExt.height;
+        doc.nativeWidth = newLayoutExt.nativeWidth;
+        doc.nativeHeight = newLayoutExt.nativeHeight;
+        doc.ignoreAspect = newLayoutExt.ignoreAspect;
+        doc.type = newLayoutExt.type;
+        doc.layout = await newLayoutExt.layout;
     }
 };
+
 Scripting.addGlobal(function toggleDetail(doc: any) {
-    if (doc.type !== DocumentType.COL && doc.type !== DocumentType.TEMPLATE) {
-        makeCustomView(doc);
-    } else if (doc.nativeLayout) {
-        makeNativeView(doc);
-    }
+    let native = typeof doc.layout === "string";
+    swapViews(doc, native ? "layoutCustom" : "layoutNative", native ? "layoutNative" : "layoutCustom");
 });
\ No newline at end of file
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index 0cf1208d7..ffa9a4e98 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -405,17 +405,21 @@ export namespace Doc {
         return docExtensionForField;
     }
 
-    export function UpdateDocumentExtensionForField(doc: Doc, fieldKey: string) {
+    export function UpdateDocumentExtensionForField(doc: Doc, fieldKey: string, immediate: boolean = false) {
         let docExtensionForField = doc[fieldKey + "_ext"] as Doc;
         if (docExtensionForField === undefined) {
-            setTimeout(() => {
-                CreateDocumentExtensionForField(doc, fieldKey);
-            }, 0);
+            if (immediate) CreateDocumentExtensionForField(doc, fieldKey);
+            else setTimeout(() => CreateDocumentExtensionForField(doc, fieldKey), 0);
         } else if (doc instanceof Doc) { // backward compatibility -- add fields for docs that don't have them already
             docExtensionForField.extendsDoc === undefined && setTimeout(() => docExtensionForField.extendsDoc = doc, 0);
             docExtensionForField.type === undefined && setTimeout(() => docExtensionForField.type = DocumentType.EXTENSION, 0);
         }
     }
+    export function MakeTitled(title: string) {
+        let doc = new Doc();
+        doc.title = title;
+        return doc;
+    }
     export function MakeAlias(doc: Doc) {
         let alias = !GetT(doc, "isPrototype", "boolean", true) ? Doc.MakeCopy(doc) : Doc.MakeDelegate(doc);
         if (alias.layout instanceof Doc) {
@@ -560,7 +564,7 @@ export namespace Doc {
         !templateDoc.nativeWidth && (otherdoc.ignoreAspect = true);
         return otherdoc;
     }
-    export function ApplyTemplateTo(templateDoc: Doc, target: Doc, targetData?: Doc, useTemplateDoc?: boolean) {
+    export function ApplyTemplateTo(templateDoc: Doc, target: Doc, targetData?: Doc) {
         if (!templateDoc) {
             target.layout = undefined;
             target.nativeWidth = undefined;
@@ -569,7 +573,7 @@ export namespace Doc {
             target.type = undefined;
             return;
         }
-        let temp = useTemplateDoc ? templateDoc : Doc.MakeDelegate(templateDoc);
+        let temp = Doc.MakeDelegate(templateDoc);
         target.nativeWidth = Doc.GetProto(target).nativeWidth = undefined;
         target.nativeHeight = Doc.GetProto(target).nativeHeight = undefined;
         !templateDoc.nativeWidth && (target.nativeWidth = 0);
-- 
cgit v1.2.3-70-g09d2


From 90e36ff4532bfc23831ad0b481c88c00cfac6ffe Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Wed, 18 Sep 2019 15:00:59 -0400
Subject: cleaned up a lot more stuff in DocumentView and related

---
 src/client/util/DictationManager.ts                |   2 +-
 src/client/util/DragManager.ts                     |  11 +-
 src/client/views/DocumentDecorations.tsx           |  16 +-
 src/client/views/MainOverlayTextBox.tsx            |   3 +-
 src/client/views/TemplateMenu.tsx                  |   8 +-
 .../views/collections/CollectionBaseView.tsx       |   2 +-
 .../views/collections/CollectionDockingView.tsx    |  10 +-
 .../views/collections/CollectionTreeView.tsx       |   5 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  12 +-
 src/client/views/nodes/DocumentView.tsx            | 181 ++++++++-------------
 src/client/views/nodes/DragBox.tsx                 |   2 +-
 src/client/views/nodes/FormattedTextBox.tsx        |   3 +-
 src/new_fields/Doc.ts                              |   2 +-
 .../authentication/models/current_user_utils.ts    |  40 +++--
 14 files changed, 129 insertions(+), 168 deletions(-)

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

diff --git a/src/client/util/DictationManager.ts b/src/client/util/DictationManager.ts
index fb3c15cea..c4016d2a5 100644
--- a/src/client/util/DictationManager.ts
+++ b/src/client/util/DictationManager.ts
@@ -313,7 +313,7 @@ export namespace DictationManager {
             ["open fields", {
                 action: (target: DocumentView) => {
                     let kvp = Docs.Create.KVPDocument(target.props.Document, { width: 300, height: 300 });
-                    target.props.addDocTab(kvp, target.dataDoc, "onRight");
+                    target.props.addDocTab(kvp, target.props.DataDoc, "onRight");
                 }
             }],
 
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index bd7051993..e3cdb3f39 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -204,13 +204,11 @@ export namespace DragManager {
         constructor(dragDoc: Doc[]) {
             this.draggedDocuments = dragDoc;
             this.droppedDocuments = dragDoc;
-            this.xOffset = 0;
-            this.yOffset = 0;
+            this.offset = [0, 0];
         }
         draggedDocuments: Doc[];
         droppedDocuments: Doc[];
-        xOffset: number;
-        yOffset: number;
+        offset: number[];
         dropAction: dropActionType;
         userDropAction: dropActionType;
         moveDocument?: MoveFunction;
@@ -223,14 +221,13 @@ export namespace DragManager {
             this.dragDocument = dragDoc;
             this.dropDocument = dropDoc;
             this.annotationDocument = annotationDoc;
-            this.xOffset = this.yOffset = 0;
+            this.offset = [0, 0];
         }
         targetContext: Doc | undefined;
         dragDocument: Doc;
         annotationDocument: Doc;
         dropDocument: Doc;
-        xOffset: number;
-        yOffset: number;
+        offset: number[];
         dropAction: dropActionType;
         userDropAction: dropActionType;
     }
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 2826f4422..12bd84684 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -226,7 +226,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
             }
             let transform = (documentView.props.ScreenToLocalTransform().scale(documentView.props.ContentScaling())).inverse();
             if (transform.TranslateX === 0 && transform.TranslateY === 0) {
-                setTimeout(action(() => this._forceUpdate++), 0); // bcz: fix CollectionStackingView's getTransform() somehow...
+                setTimeout(action(() => this._forceUpdate++), 0); // bcz: fix CollectionStackingView's getTransform() somehow...without this, resizing things in the library view, for instance, show the wrong bounds
                 return this._lastBox;
             }
 
@@ -251,11 +251,9 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
     @action
     onBackgroundMove = (e: PointerEvent): void => {
         let dragDocView = SelectionManager.SelectedDocuments()[0];
-        const [left, top] = dragDocView.props.ScreenToLocalTransform().scale(dragDocView.props.ContentScaling()).inverse().transformPoint(0, 0);
-        const [xoff, yoff] = dragDocView.props.ScreenToLocalTransform().scale(dragDocView.props.ContentScaling()).transformDirection(e.x - left, e.y - top);
         let dragData = new DragManager.DocumentDragData(SelectionManager.SelectedDocuments().map(dv => dv.props.Document));
-        dragData.xOffset = xoff;
-        dragData.yOffset = yoff;
+        const [left, top] = dragDocView.props.ScreenToLocalTransform().scale(dragDocView.props.ContentScaling()).inverse().transformPoint(0, 0);
+        dragData.offset = dragDocView.props.ScreenToLocalTransform().scale(dragDocView.props.ContentScaling()).transformDirection(e.x - left, e.y - top);
         dragData.moveDocument = SelectionManager.SelectedDocuments()[0].props.moveDocument;
         this.Interacting = true;
         this._hidden = true;
@@ -846,11 +844,9 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
         }
 
         let templates: Map<Template, boolean> = new Map();
-        Array.from(Object.values(Templates.TemplateList)).map(template => {
-            let checked = false;
-            SelectionManager.SelectedDocuments().map(doc => checked = checked || (doc.layoutDoc["show" + template.Name] !== undefined));
-            templates.set(template, checked);
-        });
+        Array.from(Object.values(Templates.TemplateList)).map(template =>
+            templates.set(template, SelectionManager.SelectedDocuments().reduce((checked, doc) => checked || (doc.
+                Document["show" + template.Name] ? true : false), false)));
 
         bounds.x = Math.max(0, bounds.x - this._resizeBorderWidth / 2) + this._resizeBorderWidth / 2;
         bounds.y = Math.max(0, bounds.y - this._resizeBorderWidth / 2 - this._titleHeight) + this._resizeBorderWidth / 2 + this._titleHeight;
diff --git a/src/client/views/MainOverlayTextBox.tsx b/src/client/views/MainOverlayTextBox.tsx
index e926fb1ad..8e72d236c 100644
--- a/src/client/views/MainOverlayTextBox.tsx
+++ b/src/client/views/MainOverlayTextBox.tsx
@@ -104,8 +104,7 @@ export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps>
             document.removeEventListener('pointerup', this.textBoxUp);
             let dragData = new DragManager.DocumentDragData([FormattedTextBox.InputBoxOverlay.props.Document]);
             const [left, top] = this._textXf().inverse().transformPoint(0, 0);
-            dragData.xOffset = e.clientX - left;
-            dragData.yOffset = e.clientY - top;
+            dragData.offset = [e.clientX - left, e.clientY - top];
             DragManager.StartDocumentDrag([this._textTargetDiv!], dragData, e.clientX, e.clientY, {
                 handlers: {
                     dragComplete: action(emptyFunction),
diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx
index 397bb14a4..34876cc79 100644
--- a/src/client/views/TemplateMenu.tsx
+++ b/src/client/views/TemplateMenu.tsx
@@ -90,16 +90,16 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
     @action
     toggleTemplate = (event: React.ChangeEvent<HTMLInputElement>, template: Template): void => {
         if (event.target.checked) {
-            this.props.docs.map(d => Doc.GetProto(d.layoutDoc)["show" + template.Name] = template.Name.toLowerCase());
+            this.props.docs.map(d => d.Document["show" + template.Name] = template.Name.toLowerCase());
         } else {
-            this.props.docs.map(d => Doc.GetProto(d.layoutDoc)["show" + template.Name] = undefined);
+            this.props.docs.map(d => d.Document["show" + template.Name] = "");
         }
     }
 
     @undoBatch
     @action
     clearTemplates = (event: React.MouseEvent) => {
-        Templates.TemplateList.map(template => this.props.docs.map(d => d.layoutDoc["show" + template.Name] = false));
+        Templates.TemplateList.map(template => this.props.docs.map(d => d.Document["show" + template.Name] = undefined));
     }
 
     @action
@@ -110,7 +110,7 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
     @undoBatch
     @action
     toggleChrome = (): void => {
-        this.props.docs.map(dv => dv.layoutDoc.chromeStatus = (dv.layoutDoc.chromeStatus !== "disabled" ? "disabled" : "enabled"));
+        this.props.docs.map(dv => dv.Document.chromeStatus = (dv.Document.chromeStatus !== "disabled" ? "disabled" : "enabled"));
     }
 
     render() {
diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx
index a54718e9e..e4e798608 100644
--- a/src/client/views/collections/CollectionBaseView.tsx
+++ b/src/client/views/collections/CollectionBaseView.tsx
@@ -85,7 +85,7 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
 
     active = (): boolean => {
         var isSelected = this.props.isSelected();
-        return isSelected || BoolCast(this.props.Document.forceActive) || this._isChildActive || this.props.renderDepth === 0 || BoolCast(this.props.Document.excludeFromLibrary);
+        return isSelected || BoolCast(this.props.Document.forceActive) || this._isChildActive || this.props.renderDepth === 0;
     }
 
     //TODO should this be observable?
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index a350cfcc5..55ba3a314 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -44,7 +44,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
             width: width,
             props: {
                 documentId: document[Id],
-                dataDocumentId: dataDoc ? dataDoc[Id] : ""
+                dataDocumentId: dataDoc && dataDoc[Id] !== document[Id] ? dataDoc[Id] : ""
                 //collectionDockingView: CollectionDockingView.Instance
             }
         };
@@ -90,7 +90,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
     @action
     public OpenFullScreen(docView: DocumentView) {
         let document = Doc.MakeAlias(docView.props.Document);
-        let dataDoc = docView.dataDoc;
+        let dataDoc = docView.props.DataDoc;
         let newItemStackConfig = {
             type: 'stack',
             content: [CollectionDockingView.makeDocumentConfig(document, dataDoc)]
@@ -528,11 +528,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
     @observable private _isActive: boolean = false;
 
     get _stack(): any {
-        let parent = (this.props as any).glContainer.parent.parent;
-        if (this._document && this._document.excludeFromLibrary && parent.parent && parent.parent.contentItems.length > 1) {
-            return parent.parent.contentItems[1];
-        }
-        return parent;
+        return (this.props as any).glContainer.parent.parent;
     }
     constructor(props: any) {
         super(props);
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 40bea2f9d..fed833261 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -405,7 +405,7 @@ class TreeView extends React.Component<TreeViewProps> {
         </div>;
     }
     public static GetChildElements(
-        docList: Doc[],
+        docs: Doc[],
         treeViewId: string,
         containingCollection: Doc,
         dataDoc: Doc | undefined,
@@ -425,7 +425,6 @@ class TreeView extends React.Component<TreeViewProps> {
         preventTreeViewOpen: boolean,
         renderedIds: string[]
     ) {
-        let docs = docList.filter(child => !child.excludeFromLibrary && child.opacity !== 0);
         let viewSpecScript = Cast(containingCollection.viewSpecScript, ScriptField);
         if (viewSpecScript) {
             let script = viewSpecScript.script;
@@ -548,7 +547,7 @@ export class CollectionTreeView extends CollectionSubView(Document) {
     }
     onContextMenu = (e: React.MouseEvent): void => {
         // need to test if propagation has stopped because GoldenLayout forces a parallel react hierarchy to be created for its top-level layout
-        if (!e.isPropagationStopped() && this.props.Document.workspaceLibrary) { // excludeFromLibrary means this is the user document
+        if (!e.isPropagationStopped() && this.props.Document.workspaceLibrary) {
             ContextMenu.Instance.addItem({ description: "Create Workspace", event: () => MainView.Instance.createNewWorkspace(), icon: "plus" });
             ContextMenu.Instance.addItem({ description: "Delete Workspace", event: () => this.remove(this.props.Document), icon: "minus" });
             e.stopPropagation();
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 0c559d83d..359731bda 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -323,8 +323,8 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
             if (de.data instanceof DragManager.DocumentDragData) {
                 if (de.data.droppedDocuments.length) {
                     let z = NumCast(de.data.droppedDocuments[0].z);
-                    let x = (z ? xpo : xp) - de.data.xOffset;
-                    let y = (z ? ypo : yp) - de.data.yOffset;
+                    let x = (z ? xpo : xp) - de.data.offset[0];
+                    let y = (z ? ypo : yp) - de.data.offset[1];
                     let dropX = NumCast(de.data.droppedDocuments[0].x);
                     let dropY = NumCast(de.data.droppedDocuments[0].y);
                     de.data.droppedDocuments.forEach(d => {
@@ -347,8 +347,8 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
             else if (de.data instanceof DragManager.AnnotationDragData) {
                 if (de.data.dropDocument) {
                     let dragDoc = de.data.dropDocument;
-                    let x = xp - de.data.xOffset;
-                    let y = yp - de.data.yOffset;
+                    let x = xp - de.data.offset[0];
+                    let y = yp - de.data.offset[1];
                     let dropX = NumCast(de.data.dropDocument.x);
                     let dropY = NumCast(de.data.dropDocument.y);
                     dragDoc.x = x + NumCast(dragDoc.x) - dropX;
@@ -387,10 +387,8 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
             let de = new DragManager.DocumentDragData(eles);
             de.moveDocument = this.props.moveDocument;
             const [left, top] = clusterDocs[0].props.ScreenToLocalTransform().scale(clusterDocs[0].props.ContentScaling()).inverse().transformPoint(0, 0);
-            const [xoff, yoff] = this.getTransform().transformDirection(e.x - left, e.y - top);
+            de.offset = this.getTransform().transformDirection(e.x - left, e.y - top);
             de.dropAction = e.ctrlKey || e.altKey ? "alias" : undefined;
-            de.xOffset = xoff;
-            de.yOffset = yoff;
             DragManager.StartDocumentDrag(clusterDocs.map(v => v.ContentDiv!), de, e.clientX, e.clientY, {
                 handlers: { dragComplete: action(emptyFunction) },
                 hideSource: !de.dropAction
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 16f8275c7..11924f86a 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -65,17 +65,6 @@ library.add(fa.faUnlock);
 library.add(fa.faLock);
 library.add(fa.faLaptopCode, fa.faMale, fa.faCopy, fa.faHandPointRight, fa.faCompass, fa.faSnowflake, fa.faMicrophone);
 
-// const linkSchema = createSchema({
-//     title: "string",
-//     linkDescription: "string",
-//     linkTags: "string",
-//     linkedTo: Doc,
-//     linkedFrom: Doc
-// });
-
-// type LinkDoc = makeInterface<[typeof linkSchema]>;
-// const LinkDoc = makeInterface(linkSchema);
-
 export interface DocumentViewProps {
     ContainingCollectionView: Opt<CollectionView | CollectionPDFView | CollectionVideoView>;
     Document: Doc;
@@ -107,34 +96,32 @@ export interface DocumentViewProps {
 }
 
 export const documentSchema = createSchema({
-    layout: "string", // should also allow Doc but that can't be expressed in the schema
-    title: "string",
-    nativeWidth: "number",
-    nativeHeight: "number",
-    backgroundColor: "string",
-    opacity: "number",
-    hidden: "boolean",
-    onClick: ScriptField,
-    ignoreAspect: "boolean",
-    autoHeight: "boolean",
-    isTemplate: "boolean",
-    isButton: "boolean",
-    isBackground: "boolean",
-    ignoreClick: "boolean",
-    type: "string",
-    maximizeLocation: "string",
-    lockedPosition: "boolean",
-    excludeFromLibrary: "boolean",
-    width: "number",
-    height: "number",
-    borderRounding: "string",
-    fitToBox: "boolean",
-    searchFields: "string",
-    heading: "number",
-    showCaption: "string",
-    showTitle: "string"
+    // layout: "string", // this should be a "string" or Doc, but can't do that in schemas, so best to leave it out
+    title: "string",            // document title (can be on either data document or layout)
+    nativeWidth: "number",      // native width of document which determines how much document contents are scaled when the document's width is set
+    nativeHeight: "number",     // "
+    width: "number",            // width of document in its container's coordinate system
+    height: "number",           // "
+    backgroundColor: "string",  // background color of document
+    opacity: "number",          // opacity of document
+    onClick: ScriptField,       // script to run when document is clicked (can be overriden by an onClick prop)
+    ignoreAspect: "boolean",    // whether aspect ratio should be ignored when laying out or manipulating the document
+    autoHeight: "boolean",      // whether the height of the document should be computed automatically based on its contents
+    isTemplate: "boolean",      // whether this document acts as a template layout for describing how other documents should be displayed
+    isBackground: "boolean",    // whether document is a background element and ignores input events (can only selet with marquee)
+    type: "string",             // enumerated type of document
+    maximizeLocation: "string", // flag for where to place content when following a click interaction (e.g., onRight, inPlace, inTab) 
+    lockedPosition: "boolean",  // whether the document can be spatially manipulated
+    borderRounding: "string",   // border radius rounding of document
+    searchFields: "string",     // the search fields to display when this document matches a search in its metadata
+    heading: "number",          // the logical layout 'heading' of this document (used by rule provider to stylize h1 header elements, from h2, etc)
+    showCaption: "string",      // whether editable caption text is overlayed at the bottom of the document 
+    showTitle: "string",        // whether an editable title banner is displayed at tht top of the document
+    isButton: "boolean",        // whether document functions as a button (overiding native interactions of its content)      
+    ignoreClick: "boolean",     // whether documents ignores input clicks (but does not ignore manipulation and other events) 
 });
 
+
 type Document = makeInterface<[typeof documentSchema]>;
 const Document = makeInterface(documentSchema);
 
@@ -154,47 +141,28 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
 
     @action
     componentDidMount() {
-        if (this._mainCont.current) {
-            this._dropDisposer = DragManager.MakeDropTarget(this._mainCont.current, {
-                handlers: { drop: this.drop.bind(this) }
-            });
-        }
+        this._mainCont.current && (this._dropDisposer = DragManager.MakeDropTarget(this._mainCont.current, { handlers: { drop: this.drop.bind(this) } }));
         DocumentManager.Instance.DocumentViews.push(this);
     }
 
     @action
     componentDidUpdate() {
         this._dropDisposer && this._dropDisposer();
-        if (this._mainCont.current) {
-            this._dropDisposer = DragManager.MakeDropTarget(this._mainCont.current, {
-                handlers: { drop: this.drop.bind(this) }
-            });
-        }
+        this._mainCont.current && (this._dropDisposer = DragManager.MakeDropTarget(this._mainCont.current, { handlers: { drop: this.drop.bind(this) } }));
     }
+
     @action
     componentWillUnmount() {
         this._dropDisposer && this._dropDisposer();
         DocumentManager.Instance.DocumentViews.splice(DocumentManager.Instance.DocumentViews.indexOf(this), 1);
     }
 
-    get dataDoc() {
-        // bcz: don't think we need this, but left it in in case strange behavior pops up.  DocumentContentsView has this functionality
-        // if (this.props.DataDoc === undefined && (this.props.Document.layout instanceof Doc || this.props.Document instanceof Promise)) {
-        //     // if there is no dataDoc (ie, we're not rendering a temlplate layout), but this document
-        //     // has a template layout document, then we will render the template layout but use 
-        //     // this document as the data document for the layout.
-        //     return this.props.Document;
-        // }
-        return this.props.DataDoc !== this.props.Document ? this.props.DataDoc : undefined;
-    }
     startDragging(x: number, y: number, dropAction: dropActionType, applyAsTemplate?: boolean) {
         if (this._mainCont.current) {
-            const [left, top] = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).inverse().transformPoint(0, 0);
             let dragData = new DragManager.DocumentDragData([this.props.Document]);
-            const [xoff, yoff] = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).transformDirection(x - left, y - top);
+            const [left, top] = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).inverse().transformPoint(0, 0);
+            dragData.offset = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).transformDirection(x - left, y - top);
             dragData.dropAction = dropAction;
-            dragData.xOffset = xoff;
-            dragData.yOffset = yoff;
             dragData.moveDocument = this.props.moveDocument;
             dragData.applyAsTemplate = applyAsTemplate;
             DragManager.StartDocumentDrag([this._mainCont.current], dragData, x, y, {
@@ -251,7 +219,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             let fullScreenAlias = Doc.MakeAlias(this.props.Document);
             Doc.UseDetailLayout(fullScreenAlias);
             fullScreenAlias.showCaption = "caption";
-            this.props.addDocTab(fullScreenAlias, this.dataDoc, "inTab");
+            this.props.addDocTab(fullScreenAlias, this.props.DataDoc, "inTab");
             SelectionManager.DeselectAll();
             Doc.UnBrushDoc(this.props.Document);
         }
@@ -306,7 +274,6 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
 
                     // @TODO: shouldn't always follow target context
                     let linkedFwdContextDocs = [first.length ? await (first[0].targetContext) as Doc : undefined, undefined];
-
                     let linkedFwdPage = [first.length ? NumCast(first[0].anchor2Page, undefined) : undefined, undefined];
 
                     if (!linkedFwdDocs.some(l => l instanceof Promise)) {
@@ -355,7 +322,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             document.removeEventListener("pointermove", this.onPointerMove);
         }
         else if (!e.cancelBubble && this.active) {
-            if (!this.Document.excludeFromLibrary && (Math.abs(this._downX - e.clientX) > 3 || Math.abs(this._downY - e.clientY) > 3)) {
+            if (Math.abs(this._downX - e.clientX) > 3 || Math.abs(this._downY - e.clientY) > 3) {
                 if (!e.altKey && !this.topMost && e.buttons === 1 && !BoolCast(this.Document.lockedPosition)) {
                     document.removeEventListener("pointermove", this.onPointerMove);
                     document.removeEventListener("pointerup", this.onPointerUp);
@@ -382,7 +349,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     @undoBatch
     makeCustomViewClicked = async () => {
         if (this.props.Document.layoutCustom === undefined) {
-            Doc.GetProto(this.dataDoc || this.props.Document).layoutNative = Doc.MakeTitled("layoutNative");
+            Doc.GetProto(this.props.DataDoc || this.props.Document).layoutNative = Doc.MakeTitled("layoutNative");
             await swapViews(this.props.Document, "", "layoutNative");
 
             let options = { title: "data", width: (this.Document.width || 0), x: -(this.Document.width || 0) / 2, y: - (this.Document.height || 0) / 2, };
@@ -398,7 +365,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
 
             Doc.MakeMetadataFieldTemplate(fieldTemplate, Doc.GetProto(docTemplate), true);
             Doc.ApplyTemplateTo(docTemplate, this.props.Document, undefined);
-            Doc.GetProto(this.dataDoc || this.props.Document).layoutCustom = Doc.MakeTitled("layoutCustom");
+            Doc.GetProto(this.props.DataDoc || this.props.Document).layoutCustom = Doc.MakeTitled("layoutCustom");
         } else {
             swapViews(this.props.Document, "layoutCustom", "layoutNative");
         }
@@ -406,12 +373,12 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
 
     @undoBatch
     makeBtnClicked = (): void => {
-        let doc = Doc.GetProto(this.props.Document);
-        if (doc.isButton || doc.onClick) {
-            doc.isButton = false;
-            doc.onClick = undefined;
+        if (this.Document.isButton || this.Document.onClick || this.Document.ignoreClick) {
+            this.Document.isButton = false;
+            this.Document.ignoreClick = false;
+            this.Document.onClick = undefined;
         } else {
-            doc.isButton = true;
+            this.Document.isButton = true;
         }
     }
 
@@ -492,7 +459,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             DocServer.GetRefField(portalID).then(existingPortal => {
                 let portal = existingPortal instanceof Doc ? existingPortal : Docs.Create.FreeformDocument([], { width: (this.Document.width || 0) + 10, height: this.Document.height || 0, title: portalID });
                 DocUtils.MakeLink(this.props.Document, portal, undefined, portalID);
-                Doc.GetProto(this.props.Document).isButton = true;
+                this.Document.isButton = true;
             });
         }
     }
@@ -513,14 +480,14 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     @undoBatch
     @action
     makeBackground = (): void => {
-        this.layoutDoc.isBackground = !this.layoutDoc.isBackground;
-        this.layoutDoc.isBackground && this.props.bringToFront(this.layoutDoc, true);
+        this.Document.isBackground = !this.Document.isBackground;
+        this.Document.isBackground && this.props.bringToFront(this.Document, true);
     }
 
     @undoBatch
     @action
     toggleLockPosition = (): void => {
-        this.layoutDoc.lockedPosition = BoolCast(this.layoutDoc.lockedPosition) ? undefined : true;
+        this.Document.lockedPosition = this.Document.lockedPosition ? undefined : true;
     }
 
     listen = async () => {
@@ -549,10 +516,10 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         const cm = ContextMenu.Instance;
         let subitems: ContextMenuProps[] = [];
         subitems.push({ description: "Open Full Screen", event: () => CollectionDockingView.Instance && CollectionDockingView.Instance.OpenFullScreen(this), icon: "desktop" });
-        subitems.push({ description: "Open Tab        ", event: () => this.props.addDocTab(this.props.Document, this.dataDoc, "inTab"), icon: "folder" });
-        subitems.push({ description: "Open Right      ", event: () => this.props.addDocTab(this.props.Document, this.dataDoc, "onRight"), icon: "caret-square-right" });
-        subitems.push({ description: "Open Alias Tab  ", event: () => this.props.addDocTab(Doc.MakeAlias(this.props.Document), this.dataDoc, "inTab"), icon: "folder" });
-        subitems.push({ description: "Open Alias Right", event: () => this.props.addDocTab(Doc.MakeAlias(this.props.Document), this.dataDoc, "onRight"), icon: "caret-square-right" });
+        subitems.push({ description: "Open Tab        ", event: () => this.props.addDocTab(this.props.Document, this.props.DataDoc, "inTab"), icon: "folder" });
+        subitems.push({ description: "Open Right      ", event: () => this.props.addDocTab(this.props.Document, this.props.DataDoc, "onRight"), icon: "caret-square-right" });
+        subitems.push({ description: "Open Alias Tab  ", event: () => this.props.addDocTab(Doc.MakeAlias(this.props.Document), this.props.DataDoc, "inTab"), icon: "folder" });
+        subitems.push({ description: "Open Alias Right", event: () => this.props.addDocTab(Doc.MakeAlias(this.props.Document), this.props.DataDoc, "onRight"), icon: "caret-square-right" });
         subitems.push({ description: "Open Fields     ", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { width: 300, height: 300 }), undefined, "onRight"), icon: "layer-group" });
         cm.addItem({ description: "Open...", subitems: subitems, icon: "external-link-alt" });
 
@@ -560,7 +527,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         let onClicks: ContextMenuProps[] = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : [];
         onClicks.push({ description: "Enter Portal", event: this.makeIntoPortal, icon: "window-restore" });
         onClicks.push({ description: "Toggle Detail", event: () => this.Document.onClick = ScriptField.MakeScript("toggleDetail(this)"), icon: "window-restore" });
-        onClicks.push({ description: this.layoutDoc.ignoreClick ? "Select" : "Do Nothing", event: () => this.layoutDoc.ignoreClick = !this.layoutDoc.ignoreClick, icon: this.layoutDoc.ignoreClick ? "unlock" : "lock" });
+        onClicks.push({ description: this.Document.ignoreClick ? "Select" : "Do Nothing", event: () => this.Document.ignoreClick = !this.Document.ignoreClick, icon: this.Document.ignoreClick ? "unlock" : "lock" });
         onClicks.push({ description: this.Document.isButton || this.Document.onClick ? "Remove Click Behavior" : "Follow Link", event: this.makeBtnClicked, icon: "concierge-bell" });
         onClicks.push({ description: "Edit onClick Script", icon: "edit", event: (obj: any) => ScriptBox.EditButtonScript("On Button Clicked ...", this.props.Document, "onClick", obj.x, obj.y) });
         onClicks.push({
@@ -577,10 +544,10 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         if (this.props.DataDoc) {
             layoutItems.push({ description: "Make View of Metadata Field", event: () => Doc.MakeMetadataFieldTemplate(this.props.Document, this.props.DataDoc!), icon: "concierge-bell" });
         }
-        layoutItems.push({ description: `${this.layoutDoc.chromeStatus !== "disabled" ? "Hide" : "Show"} Chrome`, event: () => this.layoutDoc.chromeStatus = (this.layoutDoc.chromeStatus !== "disabled" ? "disabled" : "enabled"), icon: "project-diagram" });
-        layoutItems.push({ description: `${this.layoutDoc.autoHeight ? "Variable Height" : "Auto Height"}`, event: () => this.layoutDoc.autoHeight = !this.layoutDoc.autoHeight, icon: "plus" });
+        layoutItems.push({ description: `${this.Document.chromeStatus !== "disabled" ? "Hide" : "Show"} Chrome`, event: () => this.Document.chromeStatus = (this.Document.chromeStatus !== "disabled" ? "disabled" : "enabled"), icon: "project-diagram" });
+        layoutItems.push({ description: `${this.Document.autoHeight ? "Variable Height" : "Auto Height"}`, event: () => this.Document.autoHeight = !this.Document.autoHeight, icon: "plus" });
         layoutItems.push({ description: this.Document.ignoreAspect || !this.Document.nativeWidth || !this.Document.nativeHeight ? "Freeze" : "Unfreeze", event: this.freezeNativeDimensions, icon: "snowflake" });
-        layoutItems.push({ description: this.layoutDoc.lockedPosition ? "Unlock Position" : "Lock Position", event: this.toggleLockPosition, icon: BoolCast(this.layoutDoc.lockedPosition) ? "unlock" : "lock" });
+        layoutItems.push({ description: this.Document.lockedPosition ? "Unlock Position" : "Lock Position", event: this.toggleLockPosition, icon: BoolCast(this.Document.lockedPosition) ? "unlock" : "lock" });
         layoutItems.push({ description: "Center View", event: () => this.props.focus(this.props.Document, false), icon: "crosshairs" });
         layoutItems.push({ description: "Zoom to Document", event: () => this.props.focus(this.props.Document, true), icon: "search" });
         if (this.props.Document.detailedLayout && !this.Document.isTemplate) {
@@ -683,9 +650,6 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         });
     }
 
-    onPointerEnter = (e: React.PointerEvent): void => { Doc.BrushDoc(this.props.Document); };
-    onPointerLeave = (e: React.PointerEvent): void => { Doc.UnBrushDoc(this.props.Document); };
-
     isSelected = () => SelectionManager.IsSelected(this);
     @action select = (ctrlPressed: boolean) => { SelectionManager.SelectDoc(this, ctrlPressed); };
     @computed get nativeWidth() { return this.Document.nativeWidth || 0; }
@@ -698,37 +662,30 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             select={this.select}
             onClick={this.onClickHandler}
             layoutKey={"layout"}
-            fitToBox={this.Document.fitToBox ? true : this.props.fitToBox}
-            DataDoc={this.dataDoc} />);
+            DataDoc={this.props.DataDoc} />);
     }
 
     chromeHeight = () => {
-        let showOverlays = this.props.showOverlays ? this.props.showOverlays(this.layoutDoc) : undefined;
-        let showTitle = showOverlays && "title" in showOverlays ? showOverlays.title : StrCast(this.layoutDoc.showTitle);
+        let showOverlays = this.props.showOverlays ? this.props.showOverlays(this.Document) : undefined;
+        let showTitle = showOverlays && "title" in showOverlays ? showOverlays.title : StrCast(this.Document.showTitle);
         return (showTitle ? 25 : 0) + 1;// bcz: why 8??
     }
 
-    get layoutDoc(): Document {
-        // if this document's layout field contains a document (ie, a rendering template), then we will use that
-        // to determine the render JSX string, otherwise the layout field should directly contain a JSX layout string.
-        return Document(this.props.Document.layout instanceof Doc ? this.props.Document.layout : this.props.Document);
-    }
-
     render() {
         const ruleColor = this.props.ruleProvider ? StrCast(this.props.ruleProvider["ruleColor_" + this.Document.heading]) : undefined;
         const ruleRounding = this.props.ruleProvider ? StrCast(this.props.ruleProvider["ruleRounding_" + this.Document.heading]) : undefined;
-        const colorSet = this.layoutDoc.backgroundColor !== this.layoutDoc.defaultBackgroundColor;
+        const colorSet = this.Document.backgroundColor !== this.Document.defaultBackgroundColor;
         const clusterCol = this.props.ContainingCollectionView && this.props.ContainingCollectionView.props.Document.clusterOverridesDefaultBackground;
-        const backgroundColor = this.layoutDoc.isBackground || (clusterCol && !colorSet) ?
-            this.props.backgroundColor(this.layoutDoc) || StrCast(this.layoutDoc.backgroundColor) :
-            ruleColor && !colorSet ? ruleColor : StrCast(this.layoutDoc.backgroundColor) || this.props.backgroundColor(this.layoutDoc);
+        const backgroundColor = this.Document.isBackground || (clusterCol && !colorSet) ?
+            this.props.backgroundColor(this.Document) || StrCast(this.Document.backgroundColor) :
+            ruleColor && !colorSet ? ruleColor : StrCast(this.Document.backgroundColor) || this.props.backgroundColor(this.Document);
 
         const nativeWidth = this.nativeWidth > 0 && !this.Document.ignoreAspect ? `${this.nativeWidth}px` : "100%";
         const nativeHeight = this.Document.ignoreAspect ? this.props.PanelHeight() / this.props.ContentScaling() : this.nativeHeight > 0 ? `${this.nativeHeight}px` : "100%";
-        const showOverlays = this.props.showOverlays ? this.props.showOverlays(this.layoutDoc) : undefined;
-        const showTitle = showOverlays && "title" in showOverlays ? showOverlays.title : this.layoutDoc.showTitle;
-        const showCaption = showOverlays && "caption" in showOverlays ? showOverlays.caption : this.layoutDoc.showCaption;
-        const showTextTitle = showTitle && StrCast(this.layoutDoc.layout).indexOf("FormattedTextBox") !== -1 ? showTitle : undefined;
+        const showOverlays = this.props.showOverlays ? this.props.showOverlays(this.Document) : undefined;
+        const showTitle = showOverlays && "title" in showOverlays ? showOverlays.title : this.Document.showTitle;
+        const showCaption = showOverlays && "caption" in showOverlays ? showOverlays.caption : this.Document.showCaption;
+        const showTextTitle = showTitle && StrCast(this.Document.layout).indexOf("FormattedTextBox") !== -1 ? showTitle : undefined;
         const fullDegree = Doc.isBrushedHighlightedDegree(this.props.Document);
         const borderRounding = this.Document.borderRounding || ruleRounding;
         const localScale = this.props.ScreenToLocalTransform().Scale * fullDegree;
@@ -739,7 +696,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         const captionView = (!showCaption ? (null) :
             <div className="documentView-captionWrapper" style={{ width: `${100 * this.props.ContentScaling()}%`, transform: `scale(${1 / this.props.ContentScaling()})` }}>
                 <FormattedTextBox {...this.props}
-                    onClick={this.onClickHandler} DataDoc={this.dataDoc} active={returnTrue}
+                    onClick={this.onClickHandler} DataDoc={this.props.DataDoc} active={returnTrue}
                     isSelected={this.isSelected} focus={emptyFunction} select={this.select}
                     fieldExt={""} hideOnLeave={true} fieldKey={showCaption}
                 />
@@ -752,19 +709,19 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 transform: `scale(${1 / this.props.ContentScaling()})`
             }}>
                 <EditableView
-                    contents={(this.layoutDoc.isTemplate || !this.dataDoc ? this.layoutDoc : this.dataDoc)[showTitle]}
+                    contents={this.Document[showTitle]}
                     display={"block"} height={72} fontSize={12}
-                    GetValue={() => StrCast((this.layoutDoc.isTemplate || !this.dataDoc ? this.layoutDoc : this.dataDoc)[showTitle])}
-                    SetValue={(value: string) => ((this.layoutDoc.isTemplate ? this.layoutDoc : Doc.GetProto(this.layoutDoc))[showTitle] = value) ? true : true}
+                    GetValue={() => StrCast(this.Document[showTitle])}
+                    SetValue={(value: string) => (Doc.GetProto(this.Document)[showTitle] = value) ? true : true}
                 />
             </div>);
         return (
             <div className={`documentView-node${this.topMost ? "-topmost" : ""}`}
                 ref={this._mainCont}
                 style={{
-                    transition: this.props.Document.isAnimating !== undefined ? ".5s linear" : StrCast(this.layoutDoc.transition),
-                    pointerEvents: this.layoutDoc.isBackground && !this.isSelected() ? "none" : "all",
-                    color: StrCast(this.layoutDoc.color),
+                    transition: this.props.Document.isAnimating !== undefined ? ".5s linear" : StrCast(this.Document.transition),
+                    pointerEvents: this.Document.isBackground && !this.isSelected() ? "none" : "all",
+                    color: StrCast(this.Document.color),
                     outlineColor: ["transparent", "maroon", "maroon", "yellow"][fullDegree],
                     outlineStyle: ["none", "dashed", "solid", "solid"][fullDegree],
                     outlineWidth: fullDegree && !borderRounding ? `${localScale}px` : "0px",
@@ -776,7 +733,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                     opacity: this.Document.opacity
                 }}
                 onDrop={this.onDrop} onContextMenu={this.onContextMenu} onPointerDown={this.onPointerDown} onClick={this.onClick}
-                onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}
+                onPointerEnter={() => Doc.BrushDoc(this.props.Document)} onPointerLeave={() => Doc.UnBrushDoc(this.props.Document)}
             >
                 {!showTitle && !showCaption ?
                     this.Document.searchFields ?
diff --git a/src/client/views/nodes/DragBox.tsx b/src/client/views/nodes/DragBox.tsx
index 067d47de4..2d1a98df2 100644
--- a/src/client/views/nodes/DragBox.tsx
+++ b/src/client/views/nodes/DragBox.tsx
@@ -45,7 +45,7 @@ export class DragBox extends DocComponent<FieldViewProps, DragDocument>(DragDocu
     }
 
     onDragMove = (e: MouseEvent) => {
-        if (!e.cancelBubble && !this.props.Document.excludeFromLibrary && (Math.abs(this._downX - e.clientX) > 5 || Math.abs(this._downY - e.clientY) > 5)) {
+        if (!e.cancelBubble && (Math.abs(this._downX - e.clientX) > 5 || Math.abs(this._downY - e.clientY) > 5)) {
             document.removeEventListener("pointermove", this.onDragMove);
             document.removeEventListener("pointerup", this.onDragUp);
             const onDragStart = this.Document.onDragStart;
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 157309cf5..3883886e8 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -921,7 +921,6 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
         let style = this.props.isOverlay ? "scroll" : "hidden";
         let rounded = StrCast(this.props.Document.borderRounding) === "100%" ? "-rounded" : "";
         let interactive: "all" | "none" = InkingControl.Instance.selectedTool || this.props.Document.isBackground
-            //||  (this.props.Document.isButton && !this.props.isSelected()) 
             ? "none" : "all";
         Doc.UpdateDocumentExtensionForField(this.dataDoc, this.props.fieldKey);
         return (
@@ -947,7 +946,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
                 onPointerEnter={action(() => this._entered = true)}
                 onPointerLeave={action(() => this._entered = false)}
             >
-                <div className={`formattedTextBox-inner${rounded}`} style={{ whiteSpace: "pre-wrap", pointerEvents: (this.props.Document.isButton && !this.props.isSelected()) ? "none" : undefined }} ref={this.createDropTarget} />
+                <div className={`formattedTextBox-inner${rounded}`} style={{ whiteSpace: "pre-wrap", pointerEvents: ((this.props.Document.isButton || this.props.onClick) && !this.props.isSelected()) ? "none" : undefined }} ref={this.createDropTarget} />
             </div>
         );
     }
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index 98b3d824e..af7775d94 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -721,7 +721,7 @@ export namespace Doc {
         manager.BrushedDoc.clear();
     }
 }
-Scripting.addGlobal(function renameAlias(doc: any, n: any) { return StrCast(doc.title).replace(/\([0-9]*\)/, "") + `(${n})`; });
+Scripting.addGlobal(function renameAlias(doc: any, n: any) { return StrCast(Doc.GetProto(doc).title).replace(/\([0-9]*\)/, "") + `(${n})`; });
 Scripting.addGlobal(function getProto(doc: any) { return Doc.GetProto(doc); });
 Scripting.addGlobal(function copyField(field: any) { return ObjectField.MakeCopy(field); });
 Scripting.addGlobal(function aliasDocs(field: any) { return new List<Doc>(field.map((d: any) => Doc.MakeAlias(d))); });
diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts
index 8cac8550c..1af36fccd 100644
--- a/src/server/authentication/models/current_user_utils.ts
+++ b/src/server/authentication/models/current_user_utils.ts
@@ -36,46 +36,62 @@ export class CurrentUserUtils {
         doc.xMargin = 5;
         doc.yMargin = 5;
         doc.boxShadow = "0 0";
-        doc.excludeFromLibrary = true;
         doc.optionalRightCollection = Docs.Create.StackingDocument([], { title: "New mobile uploads" });
         return doc;
     }
 
     static updateUserDocument(doc: Doc) {
+
+        // setup workspaces library item
         if (doc.workspaces === undefined) {
             const workspaces = Docs.Create.TreeDocument([], { title: "Workspaces", height: 100 });
-            workspaces.excludeFromLibrary = true;
-            workspaces.workspaceLibrary = true;
             workspaces.boxShadow = "0 0";
             doc.workspaces = workspaces;
         }
-        PromiseValue(Cast(doc.workspaces, Doc)).then(workspaces => workspaces && (workspaces.preventTreeViewOpen = true));
+        PromiseValue(Cast(doc.workspaces, Doc)).then(workspaces => {
+            if (workspaces) {
+                workspaces.preventTreeViewOpen = true;
+                workspaces.forceActive = true;
+                workspaces.lockedPosition = true;
+            }
+        });
+
+        // setup notes list
         if (doc.noteTypes === undefined) {
             let notes = [Docs.Create.TextDocument({ title: "Note", backgroundColor: "yellow", isTemplate: true }),
             Docs.Create.TextDocument({ title: "Idea", backgroundColor: "pink", isTemplate: true }),
             Docs.Create.TextDocument({ title: "Topic", backgroundColor: "lightBlue", isTemplate: true }),
             Docs.Create.TextDocument({ title: "Person", backgroundColor: "lightGreen", isTemplate: true })];
             const noteTypes = Docs.Create.TreeDocument(notes, { title: "Note Types", height: 75 });
-            noteTypes.excludeFromLibrary = true;
             doc.noteTypes = noteTypes;
         }
         PromiseValue(Cast(doc.noteTypes, Doc)).then(noteTypes => noteTypes && PromiseValue(noteTypes.data).then(DocListCast));
+
+        // setup Recently Closed library item
         if (doc.recentlyClosed === undefined) {
             const recentlyClosed = Docs.Create.TreeDocument([], { title: "Recently Closed", height: 75 });
-            recentlyClosed.excludeFromLibrary = true;
             recentlyClosed.boxShadow = "0 0";
             doc.recentlyClosed = recentlyClosed;
         }
-        PromiseValue(Cast(doc.recentlyClosed, Doc)).then(recent => recent && (recent.preventTreeViewOpen = true));
+        PromiseValue(Cast(doc.recentlyClosed, Doc)).then(recent => {
+            if (recent) {
+                recent.preventTreeViewOpen = true;
+                recent.forceActive = true;
+                recent.lockedPosition = true;
+            }
+        });
+
+
         if (doc.curPresentation === undefined) {
             const curPresentation = Docs.Create.PresDocument(new List<Doc>(), { title: "Presentation" });
-            curPresentation.excludeFromLibrary = true;
             curPresentation.boxShadow = "0 0";
             doc.curPresentation = curPresentation;
         }
+
         if (doc.sidebar === undefined) {
             const sidebar = Docs.Create.StackingDocument([doc.workspaces as Doc, doc, doc.recentlyClosed as Doc], { title: "Sidebar" });
-            sidebar.excludeFromLibrary = true;
+            sidebar.forceActive = true;
+            sidebar.lockedPosition = true;
             sidebar.gridGap = 5;
             sidebar.xMargin = 5;
             sidebar.yMargin = 5;
@@ -83,18 +99,22 @@ export class CurrentUserUtils {
             sidebar.boxShadow = "1 1 3";
             doc.sidebar = sidebar;
         }
+
         if (doc.overlays === undefined) {
             const overlays = Docs.Create.FreeformDocument([], { title: "Overlays" });
-            overlays.excludeFromLibrary = true;
             Doc.GetProto(overlays).backgroundColor = "#aca3a6";
             doc.overlays = overlays;
         }
+
         if (doc.linkFollowBox === undefined) {
             PromiseValue(Cast(doc.overlays, Doc)).then(overlays => overlays && Doc.AddDocToList(overlays, "data", doc.linkFollowBox = Docs.Create.LinkFollowBoxDocument({ x: 250, y: 20, width: 500, height: 370, title: "Link Follower" })));
         }
+
         StrCast(doc.title).indexOf("@") !== -1 && (doc.title = StrCast(doc.title).split("@")[0] + "'s Library");
         doc.width = 100;
         doc.preventTreeViewOpen = true;
+        doc.forceActive = true;
+        doc.lockedPosition = true;
     }
 
     public static loadCurrentUser() {
-- 
cgit v1.2.3-70-g09d2


From 25ea85f7cf2c7f6109eb740ffc111325a39fb745 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Wed, 18 Sep 2019 22:20:12 -0400
Subject: a bunch more template cleanup.  no more miniLayout and
 detailedLayout.  just layoutNative and layoutCustom

---
 src/client/views/DocumentDecorations.tsx           | 15 +++--
 src/client/views/GlobalKeyHandler.ts               |  3 -
 .../views/collections/CollectionBaseView.tsx       |  1 -
 .../views/collections/CollectionSchemaView.tsx     |  6 +-
 .../views/collections/CollectionStackingView.tsx   |  6 --
 src/client/views/collections/CollectionSubView.tsx | 16 ++++-
 .../views/collections/CollectionTreeView.tsx       |  4 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 26 --------
 src/client/views/nodes/DocumentView.tsx            | 19 +++---
 src/new_fields/Doc.ts                              | 74 ++++++----------------
 10 files changed, 56 insertions(+), 114 deletions(-)

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

diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 12bd84684..b730f88a4 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -24,7 +24,7 @@ import './DocumentDecorations.scss';
 import { LinkMenu } from "./linking/LinkMenu";
 import { MetadataEntryMenu } from './MetadataEntryMenu';
 import { PositionDocument } from './nodes/CollectionFreeFormDocumentView';
-import { DocumentView } from "./nodes/DocumentView";
+import { DocumentView, swapViews } from "./nodes/DocumentView";
 import { FieldView } from "./nodes/FieldView";
 import { FormattedTextBox, GoogleRef } from "./nodes/FormattedTextBox";
 import { IconBox } from "./nodes/IconBox";
@@ -155,16 +155,18 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
                 if (containerView) {
                     let docTemplate = containerView.props.Document;
                     let metaKey = text.startsWith(">>") ? text.slice(2, text.length) : text.slice(1, text.length);
-                    let proto = Doc.GetProto(docTemplate);
                     if (metaKey !== containerView.props.fieldKey && containerView.props.DataDoc) {
                         const fd = fieldTemplate.data;
                         fd instanceof ObjectField && (Doc.GetProto(containerView.props.DataDoc)[metaKey] = ObjectField.MakeCopy(fd));
                     }
                     fieldTemplate.title = metaKey;
-                    Doc.MakeMetadataFieldTemplate(fieldTemplate, proto);
+                    Doc.MakeMetadataFieldTemplate(fieldTemplate, Doc.GetProto(docTemplate));
                     if (text.startsWith(">>")) {
-                        proto.detailedLayout = proto.layout;
-                        proto.miniLayout = ImageBox.LayoutString(metaKey);
+                        let layoutNative = Doc.MakeTitled("layoutNative");
+                        Doc.GetProto(docTemplate).layoutNative = layoutNative;
+                        swapViews(fieldTemplate, "", "layoutNative", layoutNative);
+                        layoutNative.layout = StrCast(fieldTemplateView.props.Document.layout).replace(/fieldKey={"[^"]*"}/, `fieldKey={"${metaKey}"}`);
+                        layoutNative.backgroundLayout = StrCast(fieldTemplateView.props.Document.backgroundLayout).replace(/fieldKey={"[^"]*"}/, `fieldKey={"${metaKey}"}`);
                     }
                 }
             }
@@ -845,8 +847,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
 
         let templates: Map<Template, boolean> = new Map();
         Array.from(Object.values(Templates.TemplateList)).map(template =>
-            templates.set(template, SelectionManager.SelectedDocuments().reduce((checked, doc) => checked || (doc.
-                Document["show" + template.Name] ? true : false), false)));
+            templates.set(template, SelectionManager.SelectedDocuments().reduce((checked, doc) => checked || (doc.props.Document["show" + template.Name] ? true : false), false)));
 
         bounds.x = Math.max(0, bounds.x - this._resizeBorderWidth / 2) + this._resizeBorderWidth / 2;
         bounds.y = Math.max(0, bounds.y - this._resizeBorderWidth / 2 - this._titleHeight) + this._resizeBorderWidth / 2 + this._titleHeight;
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index 59229418d..ef5d76ce8 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -88,9 +88,6 @@ export default class KeyManager {
                     });
                 }, "delete");
                 break;
-            case "enter":
-                SelectionManager.SelectedDocuments().map(selected => Doc.ToggleDetailLayout(selected.props.Document));
-                break;
         }
 
         return {
diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx
index e4e798608..fdb2f0dc9 100644
--- a/src/client/views/collections/CollectionBaseView.tsx
+++ b/src/client/views/collections/CollectionBaseView.tsx
@@ -12,7 +12,6 @@ import { ContextMenu } from '../ContextMenu';
 import { FieldViewProps } from '../nodes/FieldView';
 import './CollectionBaseView.scss';
 import { DateField } from '../../../new_fields/DateField';
-import { DocumentType } from '../../documents/DocumentTypes';
 
 export enum CollectionViewType {
     Invalid,
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 5d09ade32..4d2b8e1c1 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -948,8 +948,10 @@ export class CollectionSchemaPreview extends React.Component<CollectionSchemaPre
         if (de.data instanceof DragManager.DocumentDragData) {
             this.props.childDocs && this.props.childDocs.map(otherdoc => {
                 let target = Doc.GetProto(otherdoc);
-                target.layout = target.detailedLayout = Doc.MakeDelegate(de.data.draggedDocuments[0]);
-                target.miniLayout = ComputedField.MakeFunction("this.image_data[0]");
+                let layoutNative = Doc.MakeTitled("layoutNative");
+                layoutNative.layout = ComputedField.MakeFunction("this.image_data[0]");
+                target.layoutNative = layoutNative;
+                target.layoutCUstom = target.layout = Doc.MakeDelegate(de.data.draggedDocuments[0]);
             });
             e.stopPropagation();
         }
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 14a9dc9d9..ccf131797 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -29,7 +29,6 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
     _masonryGridRef: HTMLDivElement | null = null;
     _draggerRef = React.createRef<HTMLDivElement>();
     _heightDisposer?: IReactionDisposer;
-    _childLayoutDisposer?: IReactionDisposer;
     _sectionFilterDisposer?: IReactionDisposer;
     _docXfs: any[] = [];
     _columnStart: number = 0;
@@ -87,10 +86,6 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
     }
 
     componentDidMount() {
-        this._childLayoutDisposer = reaction(() => [this.childDocs, Cast(this.props.Document.childLayout, Doc)],
-            async (args) => args[1] instanceof Doc &&
-                this.childDocs.map(async doc => !Doc.AreProtosEqual(args[1] as Doc, (await doc).layout as Doc) && Doc.ApplyTemplateTo(args[1] as Doc, (await doc), undefined)));
-
         // is there any reason this needs to exist? -syip.  yes, it handles autoHeight for stacking views (masonry isn't yet supported).
         this._heightDisposer = reaction(() => {
             if (this.isStackingView && BoolCast(this.props.Document.autoHeight)) {
@@ -115,7 +110,6 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
         );
     }
     componentWillUnmount() {
-        this._childLayoutDisposer && this._childLayoutDisposer();
         this._heightDisposer && this._heightDisposer();
         this._sectionFilterDisposer && this._sectionFilterDisposer();
     }
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index d13c69ecf..001560167 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -1,4 +1,4 @@
-import { action, computed } from "mobx";
+import { action, computed, IReactionDisposer, reaction } from "mobx";
 import * as rp from 'request-promise';
 import CursorField from "../../../new_fields/CursorField";
 import { Doc, DocListCast } from "../../../new_fields/Doc";
@@ -50,6 +50,18 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
             this.createDropTarget(ele);
         }
 
+        _childLayoutDisposer?: IReactionDisposer;
+
+        componentDidMount() {
+            this._childLayoutDisposer = reaction(() => [this.childDocs, Cast(this.props.Document.childLayout, Doc)],
+                async (args) => args[1] instanceof Doc &&
+                    this.childDocs.map(async doc => !Doc.AreProtosEqual(args[1] as Doc, (await doc).layout as Doc) && Doc.ApplyTemplateTo(args[1] as Doc, (await doc))));
+
+        }
+        componentWillUnmount() {
+            this._childLayoutDisposer && this._childLayoutDisposer();
+        }
+
         @computed get extensionDoc() { return Doc.resolvedFieldDataDoc(BoolCast(this.props.Document.isTemplate) && this.props.DataDoc ? this.props.DataDoc : this.props.Document, this.props.fieldKey, this.props.fieldExt); }
 
 
@@ -119,7 +131,7 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
             if (de.data instanceof DragManager.DocumentDragData && !de.data.applyAsTemplate) {
                 if (de.mods === "AltKey" && de.data.draggedDocuments.length) {
                     this.childDocs.map(doc =>
-                        Doc.ApplyTemplateTo(de.data.draggedDocuments[0], doc, undefined)
+                        Doc.ApplyTemplateTo(de.data.draggedDocuments[0], doc)
                     );
                     e.stopPropagation();
                     return true;
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index fed833261..b4026a810 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -178,7 +178,7 @@ class TreeView extends React.Component<TreeViewProps> {
         SetValue={undoBatch((value: string) => (Doc.GetProto(this.dataDoc)[key] = value) ? true : true)}
         OnFillDown={undoBatch((value: string) => {
             Doc.GetProto(this.dataDoc)[key] = value;
-            let doc = this.props.document.detailedLayout instanceof Doc ? Doc.ApplyTemplate(Doc.GetProto(this.props.document.detailedLayout)) : undefined;
+            let doc = this.props.document.layoutCustom instanceof Doc ? Doc.ApplyTemplate(Doc.GetProto(this.props.document.layoutCustom)) : undefined;
             if (!doc) doc = Docs.Create.FreeformDocument([], { title: "", x: 0, y: 0, width: 100, height: 25, templates: new List<string>([Templates.Title.Layout]) });
             TreeView.loadId = doc[Id];
             return this.props.addDocument(doc);
@@ -613,7 +613,7 @@ export class CollectionTreeView extends CollectionSubView(Document) {
                     SetValue={undoBatch((value: string) => (Doc.GetProto(this.resolvedDataDoc).title = value) ? true : true)}
                     OnFillDown={undoBatch((value: string) => {
                         Doc.GetProto(this.props.Document).title = value;
-                        let doc = this.props.Document.detailedLayout instanceof Doc ? Doc.ApplyTemplate(Doc.GetProto(this.props.Document.detailedLayout)) : undefined;
+                        let doc = this.props.Document.layoutCustom instanceof Doc ? Doc.ApplyTemplate(Doc.GetProto(this.props.Document.layoutCustom)) : undefined;
                         if (!doc) doc = Docs.Create.FreeformDocument([], { title: "", x: 0, y: 0, width: 100, height: 25, templates: new List<string>([Templates.Title.Layout]) });
                         TreeView.loadId = doc[Id];
                         Doc.AddDocToList(this.props.Document, this.props.fieldKey, doc, this.childDocs.length ? this.childDocs[0] : undefined, true, false, false, false);
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index baf907634..c9e78cee6 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -186,32 +186,6 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
     private _inkKey = "ink"; // the document key used to store ink annotation strokes
     private get _pwidth() { return this.props.PanelWidth(); }
     private get _pheight() { return this.props.PanelHeight(); }
-    private _childLayoutDisposer?: IReactionDisposer;
-    private _childDisposer?: IReactionDisposer;
-
-    componentDidMount() {
-        this._childDisposer = reaction(() => this.childDocs,
-            async (childDocs) => {
-                let childLayout = Cast(this.props.Document.childLayout, Doc) as Doc;
-                childLayout && childDocs.map(async doc => {
-                    if (!Doc.AreProtosEqual(childLayout, (await doc).layout as Doc)) {
-                        Doc.ApplyTemplateTo(childLayout, doc, undefined);
-                    }
-                });
-            });
-        this._childLayoutDisposer = reaction(() => Cast(this.props.Document.childLayout, Doc),
-            async (childLayout) => {
-                this.childDocs.map(async doc => {
-                    if (!Doc.AreProtosEqual(childLayout as Doc, (await doc).layout as Doc)) {
-                        Doc.ApplyTemplateTo(childLayout as Doc, doc, undefined);
-                    }
-                });
-            });
-    }
-    componentWillUnmount() {
-        this._childDisposer && this._childDisposer();
-        this._childLayoutDisposer && this._childLayoutDisposer();
-    }
 
     get parentScaling() {
         return (this.props as any).ContentScaling && this.fitToBox && !this.isAnnotationOverlay ? (this.props as any).ContentScaling() : 1;
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 90dbe4c86..d4e396752 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -220,9 +220,11 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         if (this._doubleTap && this.props.renderDepth) {
             e.stopPropagation();
             let fullScreenAlias = Doc.MakeAlias(this.props.Document);
-            Doc.UseDetailLayout(fullScreenAlias);
-            fullScreenAlias.showCaption = "caption";
-            this.props.addDocTab(fullScreenAlias, this.props.DataDoc, "inTab");
+            let layoutNative = await PromiseValue(Cast(this.props.Document.layoutNative, Doc));
+            if (layoutNative && fullScreenAlias.layout === layoutNative.layout) {
+                await swapViews(fullScreenAlias, "layoutCustom", "layoutNative");
+            }
+            this.props.addDocTab(fullScreenAlias, undefined, "inTab");
             SelectionManager.DeselectAll();
             Doc.UnBrushDoc(this.props.Document);
         }
@@ -392,7 +394,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             DocUtils.MakeLink(sourceDoc, targetDoc, this.props.ContainingCollectionView!.props.Document, `Link from ${StrCast(sourceDoc.title)}`);
         }
         if (de.data instanceof DragManager.DocumentDragData && de.data.applyAsTemplate) {
-            Doc.ApplyTemplateTo(de.data.draggedDocuments[0], this.props.Document, this.props.DataDoc);
+            Doc.ApplyTemplateTo(de.data.draggedDocuments[0], this.props.Document);
             e.stopPropagation();
         }
         if (de.data instanceof DragManager.LinkDragData) {
@@ -528,9 +530,6 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         layoutItems.push({ description: this.Document.lockedPosition ? "Unlock Position" : "Lock Position", event: this.toggleLockPosition, icon: BoolCast(this.Document.lockedPosition) ? "unlock" : "lock" });
         layoutItems.push({ description: "Center View", event: () => this.props.focus(this.props.Document, false), icon: "crosshairs" });
         layoutItems.push({ description: "Zoom to Document", event: () => this.props.focus(this.props.Document, true), icon: "search" });
-        if (this.props.Document.detailedLayout && !this.Document.isTemplate) {
-            layoutItems.push({ description: "Toggle detail", event: () => Doc.ToggleDetailLayout(this.props.Document), icon: "image" });
-        }
         if (this.Document.type !== DocumentType.COL && this.Document.type !== DocumentType.TEMPLATE) {
             layoutItems.push({ description: "Use Custom Layout", event: this.makeCustomViewClicked, icon: "concierge-bell" });
         } else if (this.props.Document.layoutNative) {
@@ -728,8 +727,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     }
 }
 
-let swapViews = async (doc: any, newLayoutField: any, oldLayoutField: any) => {
-    let oldLayoutExt = await Cast(doc[oldLayoutField], Doc);
+export async function swapViews(doc: Doc, newLayoutField: string, oldLayoutField: string, oldLayout?: Doc) {
+    let oldLayoutExt = oldLayout || await Cast(doc[oldLayoutField], Doc);
     if (oldLayoutExt) {
         oldLayoutExt.autoHeight = doc.autoHeight;
         oldLayoutExt.width = doc.width;
@@ -737,6 +736,7 @@ let swapViews = async (doc: any, newLayoutField: any, oldLayoutField: any) => {
         oldLayoutExt.nativeWidth = doc.nativeWidth;
         oldLayoutExt.nativeHeight = doc.nativeHeight;
         oldLayoutExt.ignoreAspect = doc.ignoreAspect;
+        oldLayoutExt.backgroundLayout = doc.backgroundLayout;
         oldLayoutExt.type = doc.type;
         oldLayoutExt.layout = doc.layout;
     }
@@ -749,6 +749,7 @@ let swapViews = async (doc: any, newLayoutField: any, oldLayoutField: any) => {
         doc.nativeWidth = newLayoutExt.nativeWidth;
         doc.nativeHeight = newLayoutExt.nativeHeight;
         doc.ignoreAspect = newLayoutExt.ignoreAspect;
+        doc.backgroundLayout = newLayoutExt.backgroundLayout;
         doc.type = newLayoutExt.type;
         doc.layout = await newLayoutExt.layout;
     }
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index af7775d94..0859cf41a 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -544,22 +544,9 @@ export namespace Doc {
 
     let _applyCount: number = 0;
     export function ApplyTemplate(templateDoc: Doc) {
-        if (!templateDoc) return undefined;
-        let datadoc = new Doc();
-        let otherdoc = Doc.MakeDelegate(datadoc);
-        otherdoc.width = templateDoc[WidthSym]();
-        otherdoc.height = templateDoc[HeightSym]();
-        otherdoc.title = templateDoc.title + "(..." + _applyCount++ + ")";
-        otherdoc.layout = Doc.MakeDelegate(templateDoc);
-        otherdoc.miniLayout = StrCast(templateDoc.miniLayout);
-        otherdoc.detailedLayout = otherdoc.layout;
-        otherdoc.type = DocumentType.TEMPLATE;
-        !templateDoc.nativeWidth && (otherdoc.nativeWidth = 0);
-        !templateDoc.nativeHeight && (otherdoc.nativeHeight = 0);
-        !templateDoc.nativeWidth && (otherdoc.ignoreAspect = true);
-        return otherdoc;
-    }
-    export function ApplyTemplateTo(templateDoc: Doc, target: Doc, targetData?: Doc) {
+        return !templateDoc ? undefined : ApplyTemplateTo(templateDoc, Doc.MakeDelegate(new Doc()), templateDoc.title + "(..." + _applyCount++ + ")");
+    }
+    export function ApplyTemplateTo(templateDoc: Doc, target: Doc, titleTarget: string | undefined = undefined) {
         if (!templateDoc) {
             target.layout = undefined;
             target.nativeWidth = undefined;
@@ -568,25 +555,24 @@ export namespace Doc {
             target.type = undefined;
             return;
         }
-        let temp = Doc.MakeDelegate(templateDoc);
-        target.nativeWidth = Doc.GetProto(target).nativeWidth = undefined;
-        target.nativeHeight = Doc.GetProto(target).nativeHeight = undefined;
-        !templateDoc.nativeWidth && (target.nativeWidth = 0);
-        !templateDoc.nativeHeight && (target.nativeHeight = 0);
-        !templateDoc.nativeHeight && (target.ignoreAspect = true);
+
+        let layoutCustom = Doc.MakeTitled("layoutCustom");
+        let layoutCustomLayout = Doc.MakeDelegate(templateDoc);
+
+        titleTarget && (target.title = titleTarget);
+        target.type = DocumentType.TEMPLATE;
         target.width = templateDoc.width;
         target.height = templateDoc.height;
+        target.nativeWidth = templateDoc.nativeWidth ? templateDoc.nativeWidth : 0;
+        target.nativeHeight = templateDoc.nativeHeight ? templateDoc.nativeHeight : 0;
+        target.ignoreAspect = templateDoc.nativeWidth ? true : false;
         target.onClick = templateDoc.onClick instanceof ObjectField && templateDoc.onClick[Copy]();
-        target.type = DocumentType.TEMPLATE;
-        if (targetData && targetData.layout === target) {
-            targetData.layout = temp;
-            targetData.miniLayout = StrCast(templateDoc.miniLayout);
-            targetData.detailedLayout = targetData.layout;
-        } else {
-            target.layout = temp;
-            target.miniLayout = StrCast(templateDoc.miniLayout);
-            target.detailedLayout = target.layout;
-        }
+        target.layout = layoutCustomLayout;
+        target.backgroundLayout = layoutCustomLayout.backgroundLayout;
+
+        target.layoutNative = Cast(templateDoc.layoutNative, Doc) as Doc;
+        target.layoutCustom = layoutCustom;
+        return target;
     }
 
     export function MakeMetadataFieldTemplate(fieldTemplate: Doc, templateDataDoc: Doc, suppressTitle: boolean = false) {
@@ -628,30 +614,6 @@ export namespace Doc {
         }), 0);
     }
 
-    export function ToggleDetailLayout(d: Doc) {
-        runInAction(async () => {
-            let miniLayout = await PromiseValue(d.miniLayout);
-            let detailLayout = await PromiseValue(d.detailedLayout);
-            d.layout !== miniLayout ? miniLayout && (d.layout = d.miniLayout) : detailLayout && (d.layout = detailLayout);
-            if (d.layout === detailLayout) d.nativeWidth = d.nativeHeight = 0;
-            if (StrCast(d.layout) !== "") d.nativeWidth = d.nativeHeight = undefined;
-        });
-    }
-    export function UseDetailLayout(d: Doc) {
-        runInAction(async () => {
-            let detailLayout = await d.detailedLayout;
-            if (detailLayout) {
-                d.layout = detailLayout;
-                d.nativeWidth = d.nativeHeight = undefined;
-                if (detailLayout instanceof Doc) {
-                    let delegDetailLayout = Doc.MakeDelegate(detailLayout);
-                    d.layout = delegDetailLayout;
-                    delegDetailLayout.layout = await delegDetailLayout.detailedLayout;
-                }
-            }
-        });
-    }
-
     export function isBrushedHighlightedDegree(doc: Doc) {
         if (Doc.IsHighlighted(doc)) {
             return 3;
-- 
cgit v1.2.3-70-g09d2


From be5456616a7813572b9fdc9637d4009e7283a46a Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Thu, 19 Sep 2019 00:11:23 -0400
Subject: added ContainingCollectionDoc to props that get passed around.

---
 src/client/documents/Documents.ts                  |   2 +-
 .../util/Import & Export/DirectoryImportBox.tsx    |   5 +-
 src/client/util/TooltipTextMenu.tsx                |   4 +-
 src/client/views/DocumentDecorations.tsx           |  11 +-
 src/client/views/InkingControl.tsx                 |  20 +--
 src/client/views/MainOverlayTextBox.tsx            |   6 +-
 src/client/views/MainView.tsx                      |   2 +
 src/client/views/OverlayView.tsx                   |   1 +
 .../views/collections/CollectionDockingView.tsx    |   1 +
 .../views/collections/CollectionSchemaCells.tsx    |   1 +
 .../CollectionSchemaMovableTableHOC.tsx            |   8 +-
 .../views/collections/CollectionSchemaView.tsx     |   3 +
 .../CollectionFreeFormLinksView.tsx                |   6 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |   2 +
 src/client/views/linking/LinkFollowBox.tsx         |  30 ++--
 src/client/views/nodes/DocumentView.tsx            | 164 ++++++++++-----------
 src/client/views/nodes/FieldView.tsx               |   1 +
 src/client/views/nodes/KeyValuePair.tsx            |   1 +
 src/client/views/nodes/PDFBox.tsx                  |   2 +-
 .../views/presentationview/PresentationElement.tsx |   1 +
 src/client/views/search/FilterBox.tsx              |   6 +-
 src/client/views/search/SearchItem.tsx             |   1 +
 22 files changed, 136 insertions(+), 142 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 99707db19..2fa0d2dcb 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -640,7 +640,7 @@ export namespace DocUtils {
     export function MakeLink(source: Doc, target: Doc, targetContext?: Doc, title: string = "", description: string = "", sourceContext?: Doc, id?: string, anchored1?: boolean) {
         if (LinkManager.Instance.doesLinkExist(source, target)) return undefined;
         let sv = DocumentManager.Instance.getDocumentView(source);
-        if (sv && sv.props.ContainingCollectionView && sv.props.ContainingCollectionView.props.Document === target) return;
+        if (sv && sv.props.ContainingCollectionDoc === target) return;
         if (target === CurrentUserUtils.UserDocument) return undefined;
 
         let linkDocProto = new Doc(id, true);
diff --git a/src/client/util/Import & Export/DirectoryImportBox.tsx b/src/client/util/Import & Export/DirectoryImportBox.tsx
index 75b0b52a7..dc6a0cb7a 100644
--- a/src/client/util/Import & Export/DirectoryImportBox.tsx	
+++ b/src/client/util/Import & Export/DirectoryImportBox.tsx	
@@ -134,11 +134,10 @@ export default class DirectoryImportBox extends React.Component<FieldViewProps>
             x: NumCast(doc.x),
             y: NumCast(doc.y) + offset
         };
-        let parent = this.props.ContainingCollectionView;
-        if (parent) {
+        if (this.props.ContainingCollectionDoc) {
             let importContainer = Docs.Create.StackingDocument(docs, options);
             importContainer.singleColumn = false;
-            Doc.AddDocToList(Doc.GetProto(parent.props.Document), "data", importContainer);
+            Doc.AddDocToList(Doc.GetProto(this.props.ContainingCollectionDoc), "data", importContainer);
             !this.persistent && this.props.removeDocument && this.props.removeDocument(doc);
             DocumentManager.Instance.jumpToDocument(importContainer, true);
 
diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx
index 5764af282..aa096dd64 100644
--- a/src/client/util/TooltipTextMenu.tsx
+++ b/src/client/util/TooltipTextMenu.tsx
@@ -310,8 +310,8 @@ export class TooltipTextMenu {
                             dragComplete: action(() => {
                                 let linkDoc = dragData.linkDocument;
                                 let proto = Doc.GetProto(linkDoc);
-                                if (proto && docView && docView.props.ContainingCollectionView) {
-                                    proto.sourceContext = docView.props.ContainingCollectionView.props.Document;
+                                if (proto && docView) {
+                                    proto.sourceContext = docView.props.ContainingCollectionDoc;
                                 }
                                 linkDoc instanceof Doc && this.makeLink(Utils.prepend("/doc/" + linkDoc[Id]), ctrlKey ? "onRight" : "inTab");
                             }),
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index b730f88a4..ad38d16aa 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -152,8 +152,8 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
                 SelectionManager.DeselectAll();
                 let fieldTemplate = fieldTemplateView.props.Document;
                 let containerView = fieldTemplateView.props.ContainingCollectionView;
-                if (containerView) {
-                    let docTemplate = containerView.props.Document;
+                let docTemplate = fieldTemplateView.props.ContainingCollectionDoc;
+                if (containerView && docTemplate) {
                     let metaKey = text.startsWith(">>") ? text.slice(2, text.length) : text.slice(1, text.length);
                     if (metaKey !== containerView.props.fieldKey && containerView.props.DataDoc) {
                         const fd = fieldTemplate.data;
@@ -431,8 +431,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
         dist = dist < 3 ? 0 : dist;
         let usingRule = false;
         SelectionManager.SelectedDocuments().map(dv => {
-            let cv = dv.props.ContainingCollectionView;
-            let ruleProvider = cv && cv.props.ruleProvider;
+            let ruleProvider = dv.props.ruleProvider;
             let heading = NumCast(dv.props.Document.heading);
             ruleProvider && heading && (Doc.GetProto(ruleProvider)["ruleRounding_" + heading] = `${Math.min(100, dist)}%`);
             usingRule = usingRule || (ruleProvider && heading ? true : false);
@@ -502,7 +501,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
             document.removeEventListener("pointermove", this.onLinkerButtonMoved);
             document.removeEventListener("pointerup", this.onLinkerButtonUp);
             let selDoc = SelectionManager.SelectedDocuments()[0];
-            let container = selDoc.props.ContainingCollectionView ? selDoc.props.ContainingCollectionView.props.Document.proto : undefined;
+            let container = selDoc.props.ContainingCollectionDoc ? selDoc.props.ContainingCollectionDoc.proto : undefined;
             let dragData = new DragManager.LinkDragData(selDoc.props.Document, container ? [container] : []);
             FormattedTextBox.InputBoxOverlay = undefined;
             this._linkDrag = UndoManager.StartBatch("Drag Link");
@@ -847,7 +846,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
 
         let templates: Map<Template, boolean> = new Map();
         Array.from(Object.values(Templates.TemplateList)).map(template =>
-            templates.set(template, SelectionManager.SelectedDocuments().reduce((checked, doc) => checked || (doc.props.Document["show" + template.Name] ? true : false), false)));
+            templates.set(template, SelectionManager.SelectedDocuments().reduce((checked, doc) => checked || (doc.props.Document["show" + template.Name] ? true : false), false as boolean)));
 
         bounds.x = Math.max(0, bounds.x - this._resizeBorderWidth / 2) + this._resizeBorderWidth / 2;
         bounds.y = Math.max(0, bounds.y - this._resizeBorderWidth / 2 - this._titleHeight) + this._resizeBorderWidth / 2 + this._titleHeight;
diff --git a/src/client/views/InkingControl.tsx b/src/client/views/InkingControl.tsx
index 57dad5e6b..a10df0e75 100644
--- a/src/client/views/InkingControl.tsx
+++ b/src/client/views/InkingControl.tsx
@@ -52,16 +52,16 @@ export class InkingControl extends React.Component {
                 let targetDoc = view.props.Document.layout instanceof Doc ? view.props.Document.layout : view.props.Document.isTemplate ? view.props.Document : Doc.GetProto(view.props.Document);
                 let oldColor = StrCast(targetDoc.backgroundColor);
                 let matchedColor = this._selectedColor;
-                const cv = view.props.ContainingCollectionView;
-                let ruleProvider: Doc | undefined;
-                if (cv) {
-                    if (!cv.props.Document.colorPalette) {
-                        let defaultPalette = ["rg14,229,239)", "rgb(255,246,209)", "rgb(255,188,156)", "rgb(247,220,96)", "rgb(122,176,238)",
+                const cvd = view.props.ContainingCollectionDoc;
+                let ruleProvider = view.props.ruleProvider;
+                if (cvd) {
+                    if (!cvd.colorPalette) {
+                        let defaultPalette = ["rg(114,229,239)", "rgb(255,246,209)", "rgb(255,188,156)", "rgb(247,220,96)", "rgb(122,176,238)",
                             "rgb(209,150,226)", "rgb(127,235,144)", "rgb(252,188,189)", "rgb(247,175,81)",];
-                        let colorPalette = Cast(cv.props.Document.colorPalette, listSpec("string"));
-                        if (!colorPalette) cv.props.Document.colorPalette = new List<string>(defaultPalette);
+                        let colorPalette = Cast(cvd.colorPalette, listSpec("string"));
+                        if (!colorPalette) cvd.colorPalette = new List<string>(defaultPalette);
                     }
-                    let cp = Cast(cv.props.Document.colorPalette, listSpec("string")) as string[];
+                    let cp = Cast(cvd.colorPalette, listSpec("string")) as string[];
                     let closest = 0;
                     let dist = 10000000;
                     let ccol = Utils.fromRGBAstr(StrCast(targetDoc.backgroundColor));
@@ -74,9 +74,9 @@ export class InkingControl extends React.Component {
                         }
                     }
                     cp[closest] = "rgba(" + color.rgb.r + "," + color.rgb.g + "," + color.rgb.b + "," + color.rgb.a + ")";
-                    cv.props.Document.colorPalette = new List(cp);
+                    cvd.colorPalette = new List(cp);
                     matchedColor = cp[closest];
-                    ruleProvider = (view.props.Document.heading && cv && cv.props.ruleProvider) ? cv.props.ruleProvider : undefined;
+                    ruleProvider = (view.props.Document.heading && ruleProvider) ? ruleProvider : undefined;
                     ruleProvider && ((Doc.GetProto(ruleProvider)["ruleColor_" + NumCast(view.props.Document.heading)] = Utils.toRGBAstr(color.rgb)));
                 }
                 !ruleProvider && (targetDoc.backgroundColor = matchedColor);
diff --git a/src/client/views/MainOverlayTextBox.tsx b/src/client/views/MainOverlayTextBox.tsx
index 8e72d236c..ac1b437af 100644
--- a/src/client/views/MainOverlayTextBox.tsx
+++ b/src/client/views/MainOverlayTextBox.tsx
@@ -142,9 +142,9 @@ export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps>
                                 DataDoc={FormattedTextBox.InputBoxOverlay.props.DataDoc}
                                 onClick={undefined}
                                 ruleProvider={this._textBox ? this._textBox.props.ruleProvider : undefined}
-                                ChromeHeight={this.ChromeHeight}
-                                isSelected={returnTrue} select={emptyFunction} renderDepth={0}
-                                ContainingCollectionView={undefined} whenActiveChanged={emptyFunction} active={returnTrue} ContentScaling={returnOne}
+                                ChromeHeight={this.ChromeHeight} isSelected={returnTrue} select={emptyFunction} renderDepth={0}
+                                ContainingCollectionDoc={undefined} ContainingCollectionView={undefined}
+                                whenActiveChanged={emptyFunction} active={returnTrue} ContentScaling={returnOne}
                                 ScreenToLocalTransform={this._textXf} PanelWidth={returnZero} PanelHeight={returnZero} focus={emptyFunction}
                                 pinToPres={returnZero} addDocTab={this.addDocTab} outer_div={(tooltip: HTMLElement) => { this._tooltip = tooltip; this.updateTooltip(); }} />
                         </div>
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 2cec1c052..8b0caf9a6 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -335,6 +335,7 @@ export class MainView extends React.Component {
                             whenActiveChanged={emptyFunction}
                             bringToFront={emptyFunction}
                             ContainingCollectionView={undefined}
+                            ContainingCollectionDoc={undefined}
                             zoomToScale={emptyFunction}
                             getScale={returnOne}
                         />}
@@ -399,6 +400,7 @@ export class MainView extends React.Component {
             whenActiveChanged={emptyFunction}
             bringToFront={emptyFunction}
             ContainingCollectionView={undefined}
+            ContainingCollectionDoc={undefined}
             zoomToScale={emptyFunction}
             getScale={returnOne}>
         </DocumentView>;
diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx
index 15faea3cd..8124ce653 100644
--- a/src/client/views/OverlayView.tsx
+++ b/src/client/views/OverlayView.tsx
@@ -189,6 +189,7 @@ export class OverlayView extends React.Component {
                     addDocTab={emptyFunction}
                     pinToPres={emptyFunction}
                     ContainingCollectionView={undefined}
+                    ContainingCollectionDoc={undefined}
                     zoomToScale={emptyFunction}
                     getScale={returnOne} />
             </div>;
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 55ba3a314..cab085d9b 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -639,6 +639,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
             addDocTab={this.addDocTab}
             pinToPres={this.PinDoc}
             ContainingCollectionView={undefined}
+            ContainingCollectionDoc={undefined}
             zoomToScale={emptyFunction}
             getScale={returnOne} />;
     }
diff --git a/src/client/views/collections/CollectionSchemaCells.tsx b/src/client/views/collections/CollectionSchemaCells.tsx
index 3452e8702..059967a7d 100644
--- a/src/client/views/collections/CollectionSchemaCells.tsx
+++ b/src/client/views/collections/CollectionSchemaCells.tsx
@@ -151,6 +151,7 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
             fieldExt: "",
             ruleProvider: undefined,
             ContainingCollectionView: this.props.CollectionView,
+            ContainingCollectionDoc: this.props.CollectionView.props.Document,
             isSelected: returnFalse,
             select: emptyFunction,
             renderDepth: this.props.renderDepth + 1,
diff --git a/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx b/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx
index ec40043cc..39abc41ec 100644
--- a/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx
+++ b/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx
@@ -201,12 +201,8 @@ export class MovableRow extends React.Component<MovableRowProps> {
     @action
     move: DragManager.MoveFunction = (doc: Doc, target: Doc, addDoc) => {
         let targetView = DocumentManager.Instance.getDocumentView(target);
-        if (targetView) {
-            let targetContainingColl = targetView.props.ContainingCollectionView; //.props.ContainingCollectionView.props.Document;
-            if (targetContainingColl) {
-                let targetContCollDoc = targetContainingColl.props.Document;
-                return doc !== target && doc !== targetContCollDoc && this.props.removeDoc(doc) && addDoc(doc);
-            }
+        if (targetView && targetView.props.ContainingCollectionDoc) {
+            return doc !== target && doc !== targetView.props.ContainingCollectionDoc && this.props.removeDoc(doc) && addDoc(doc);
         }
         return doc !== target && this.props.removeDoc(doc) && addDoc(doc);
     }
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 4d2b8e1c1..a742054d0 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -196,6 +196,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
                 childDocs={this.childDocs}
                 CollectionView={this.props.CollectionView}
                 ContainingCollectionView={this.props.ContainingCollectionView}
+                ContainingCollectionDoc={this.props.ContainingCollectionDoc}
                 fieldKey={this.props.fieldKey}
                 renderDepth={this.props.renderDepth}
                 moveDocument={this.props.moveDocument}
@@ -247,6 +248,7 @@ export interface SchemaTableProps {
     childDocs?: Doc[];
     CollectionView: CollectionView | CollectionPDFView | CollectionVideoView;
     ContainingCollectionView: Opt<CollectionView | CollectionPDFView | CollectionVideoView>;
+    ContainingCollectionDoc: Opt<Doc>;
     fieldKey: string;
     renderDepth: number;
     deleteDocument: (document: Doc) => boolean;
@@ -1004,6 +1006,7 @@ export class CollectionSchemaPreview extends React.Component<CollectionSchemaPre
                         moveDocument={this.props.moveDocument}
                         whenActiveChanged={this.props.whenActiveChanged}
                         ContainingCollectionView={this.props.CollectionView}
+                        ContainingCollectionDoc={this.props.CollectionView && this.props.CollectionView.props.Document}
                         addDocTab={this.props.addDocTab}
                         pinToPres={this.props.pinToPres}
                         parentActive={this.props.active}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
index a25627dd1..a81f5315a 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
@@ -79,15 +79,15 @@ export class CollectionFreeFormLinksView extends React.Component<CollectionViewP
         if (containerDoc) {
             equalViews = DocumentManager.Instance.getDocumentViews(containerDoc.proto!);
         }
-        if (view.props.ContainingCollectionView) {
-            let collid = view.props.ContainingCollectionView.props.Document[Id];
+        if (view.props.ContainingCollectionDoc) {
+            let collid = view.props.ContainingCollectionDoc[Id];
             DocListCast(this.props.Document[this.props.fieldKey]).
                 filter(child =>
                     child[Id] === collid).map(view =>
                         DocumentManager.Instance.getDocumentViews(view).map(view =>
                             equalViews.push(view)));
         }
-        return equalViews.filter(sv => sv.props.ContainingCollectionView && sv.props.ContainingCollectionView.props.Document === this.props.Document);
+        return equalViews.filter(sv => sv.props.ContainingCollectionDoc === this.props.Document);
     }
 
     @computed
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index c9e78cee6..ad77e0428 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -659,6 +659,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
             PanelHeight: childLayout[HeightSym],
             ContentScaling: returnOne,
             ContainingCollectionView: this.props.CollectionView,
+            ContainingCollectionDoc: this.props.CollectionView.props.Document,
             focus: this.focusDocument,
             backgroundColor: this.getClusterColor,
             parentActive: this.props.active,
@@ -685,6 +686,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
             PanelHeight: layoutDoc[HeightSym],
             ContentScaling: returnOne,
             ContainingCollectionView: this.props.CollectionView,
+            ContainingCollectionDoc: this.props.CollectionView.props.Document,
             focus: this.focusDocument,
             backgroundColor: returnEmptyString,
             parentActive: this.props.active,
diff --git a/src/client/views/linking/LinkFollowBox.tsx b/src/client/views/linking/LinkFollowBox.tsx
index b1c6c367f..d7cb2585e 100644
--- a/src/client/views/linking/LinkFollowBox.tsx
+++ b/src/client/views/linking/LinkFollowBox.tsx
@@ -85,11 +85,10 @@ export class LinkFollowBox extends React.Component<FieldViewProps> {
     }
 
     async resetPan() {
-        if (LinkFollowBox.destinationDoc && this.sourceView && this.sourceView.props.ContainingCollectionView) {
-            let colDoc = this.sourceView.props.ContainingCollectionView.props.Document;
-            runInAction(() => { this.canPan = false; });
-            if (colDoc.viewType && colDoc.viewType === CollectionViewType.Freeform) {
-                let docs = Cast(colDoc.data, listSpec(Doc), []);
+        if (LinkFollowBox.destinationDoc && this.sourceView && this.sourceView.props.ContainingCollectionDoc) {
+            runInAction(() => this.canPan = false);
+            if (this.sourceView.props.ContainingCollectionDoc.viewType === CollectionViewType.Freeform) {
+                let docs = Cast(this.sourceView.props.ContainingCollectionDoc.data, listSpec(Doc), []);
                 let aliases = await SearchUtil.GetViewsOfDocument(Doc.GetProto(LinkFollowBox.destinationDoc));
 
                 aliases.forEach(alias => {
@@ -371,9 +370,9 @@ export class LinkFollowBox extends React.Component<FieldViewProps> {
         this.shouldUseOnlyParentContext = (this.selectedMode === FollowModes.INPLACE || this.selectedMode === FollowModes.PAN);
 
         if (this.shouldUseOnlyParentContext) {
-            if (this.sourceView && this.sourceView.props.ContainingCollectionView) {
-                this.selectedContext = this.sourceView.props.ContainingCollectionView.props.Document;
-                this.selectedContextString = (StrCast(this.sourceView.props.ContainingCollectionView.props.Document.title));
+            if (this.sourceView && this.sourceView.props.ContainingCollectionDoc) {
+                this.selectedContext = this.sourceView.props.ContainingCollectionDoc;
+                this.selectedContextString = (StrCast(this.sourceView.props.ContainingCollectionDoc.title));
             }
         }
     }
@@ -394,9 +393,8 @@ export class LinkFollowBox extends React.Component<FieldViewProps> {
 
     @computed
     get canOpenInPlace() {
-        if (this.sourceView && this.sourceView.props.ContainingCollectionView) {
-            let colView = this.sourceView.props.ContainingCollectionView;
-            let colDoc = colView.props.Document;
+        if (this.sourceView && this.sourceView.props.ContainingCollectionDoc) {
+            let colDoc = this.sourceView.props.ContainingCollectionDoc;
             if (colDoc.viewType && colDoc.viewType === CollectionViewType.Freeform) return true;
         }
         return false;
@@ -457,17 +455,15 @@ export class LinkFollowBox extends React.Component<FieldViewProps> {
 
     @computed
     get parentName() {
-        if (this.sourceView && this.sourceView.props.ContainingCollectionView) {
-            let colView = this.sourceView.props.ContainingCollectionView;
-            return colView.props.Document.title;
+        if (this.sourceView && this.sourceView.props.ContainingCollectionDoc) {
+            return this.sourceView.props.ContainingCollectionDoc.title;
         }
     }
 
     @computed
     get parentID(): string {
-        if (this.sourceView && this.sourceView.props.ContainingCollectionView) {
-            let colView = this.sourceView.props.ContainingCollectionView;
-            return StrCast(colView.props.Document[Id]);
+        if (this.sourceView && this.sourceView.props.ContainingCollectionDoc) {
+            return StrCast(this.sourceView.props.ContainingCollectionDoc[Id]);
         }
         return "col";
     }
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index d4e396752..39bba2eb2 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -67,6 +67,7 @@ library.add(fa.faLaptopCode, fa.faMale, fa.faCopy, fa.faHandPointRight, fa.faCom
 
 export interface DocumentViewProps {
     ContainingCollectionView: Opt<CollectionView | CollectionPDFView | CollectionVideoView>;
+    ContainingCollectionDoc: Opt<Doc>;
     Document: Doc;
     DataDoc?: Doc;
     fitToBox?: boolean;
@@ -208,88 +209,83 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     }
 
     onClick = async (e: React.MouseEvent) => {
-        if (e.nativeEvent.cancelBubble) return; // || SelectionManager.IsSelected(this)) -- bcz: needed because EditableView may stopPropagation which won't apparently stop this event from firing.
-        if (this.onClickHandler && this.onClickHandler.script) {
+        if (!e.nativeEvent.cancelBubble && !this.Document.ignoreClick && CurrentUserUtils.MainDocId !== this.props.Document[Id] &&
+            (Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD && Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD)) {
             e.stopPropagation();
-            this.onClickHandler.script.run({ this: this.Document.isTemplate && this.props.DataDoc ? this.props.DataDoc : this.props.Document });
             e.preventDefault();
-            return;
+            if (this._doubleTap && this.props.renderDepth) {
+                let fullScreenAlias = Doc.MakeAlias(this.props.Document);
+                let layoutNative = await PromiseValue(Cast(this.props.Document.layoutNative, Doc));
+                if (layoutNative && fullScreenAlias.layout === layoutNative.layout) {
+                    await swapViews(fullScreenAlias, "layoutCustom", "layoutNative");
+                }
+                this.props.addDocTab(fullScreenAlias, undefined, "inTab");
+                SelectionManager.DeselectAll();
+                Doc.UnBrushDoc(this.props.Document);
+            } else if (this.onClickHandler && this.onClickHandler.script) {
+                this.onClickHandler.script.run({ this: this.Document.isTemplate && this.props.DataDoc ? this.props.DataDoc : this.props.Document });
+            } else if (this.Document.isButton) {
+                SelectionManager.SelectDoc(this, e.ctrlKey); // don't think this should happen if a button action is actually triggered.
+                this.buttonClick(e.altKey, e.ctrlKey);
+            } else SelectionManager.SelectDoc(this, e.ctrlKey);
         }
-        let altKey = e.altKey;
-        let ctrlKey = e.ctrlKey;
-        if (this._doubleTap && this.props.renderDepth) {
-            e.stopPropagation();
-            let fullScreenAlias = Doc.MakeAlias(this.props.Document);
-            let layoutNative = await PromiseValue(Cast(this.props.Document.layoutNative, Doc));
-            if (layoutNative && fullScreenAlias.layout === layoutNative.layout) {
-                await swapViews(fullScreenAlias, "layoutCustom", "layoutNative");
-            }
-            this.props.addDocTab(fullScreenAlias, undefined, "inTab");
+    }
+
+    buttonClick = async (altKey: boolean, ctrlKey: boolean) => {
+        let maximizedDocs = await DocListCastAsync(this.props.Document.maximizedDocs);
+        let summarizedDocs = await DocListCastAsync(this.props.Document.summarizedDocs);
+        let linkedDocs = LinkManager.Instance.getAllRelatedLinks(this.props.Document);
+        let expandedDocs: Doc[] = [];
+        expandedDocs = maximizedDocs ? [...maximizedDocs, ...expandedDocs] : expandedDocs;
+        expandedDocs = summarizedDocs ? [...summarizedDocs, ...expandedDocs] : expandedDocs;
+        // let expandedDocs = [ ...(maximizedDocs ? maximizedDocs : []), ...(summarizedDocs ? summarizedDocs : []),];
+        if (expandedDocs.length) {
             SelectionManager.DeselectAll();
-            Doc.UnBrushDoc(this.props.Document);
-        }
-        else if (!this.Document.ignoreClick && CurrentUserUtils.MainDocId !== this.props.Document[Id] &&
-            (Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD &&
-                Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD)) {
-            e.stopPropagation();
-            SelectionManager.SelectDoc(this, e.ctrlKey);
-            if (this.Document.isButton || this.Document.type === DocumentType.BUTTON) {
-                let maximizedDocs = await DocListCastAsync(this.props.Document.maximizedDocs);
-                let summarizedDocs = await DocListCastAsync(this.props.Document.summarizedDocs);
-                let linkedDocs = LinkManager.Instance.getAllRelatedLinks(this.props.Document);
-                let expandedDocs: Doc[] = [];
-                expandedDocs = maximizedDocs ? [...maximizedDocs, ...expandedDocs] : expandedDocs;
-                expandedDocs = summarizedDocs ? [...summarizedDocs, ...expandedDocs] : expandedDocs;
-                // let expandedDocs = [ ...(maximizedDocs ? maximizedDocs : []), ...(summarizedDocs ? summarizedDocs : []),];
-                if (expandedDocs.length) {
-                    SelectionManager.DeselectAll();
-                    let maxLocation = StrCast(this.Document.maximizeLocation, "inPlace");
-                    let getDispDoc = (target: Doc) => Object.getOwnPropertyNames(target).indexOf("isPrototype") === -1 ? target : Doc.MakeDelegate(target);
-                    if (altKey || ctrlKey) {
-                        maxLocation = this.Document.maximizeLocation = (ctrlKey ? maxLocation : (maxLocation === "inPlace" || !maxLocation ? "inTab" : "inPlace"));
-                        if (!maxLocation || maxLocation === "inPlace") {
-                            let hadView = expandedDocs.length === 1 && DocumentManager.Instance.getDocumentView(expandedDocs[0], this.props.ContainingCollectionView);
-                            let wasMinimized = !hadView && expandedDocs.reduce((min, d) => !min && !d.isMinimized, false);
-                            expandedDocs.forEach(maxDoc => Doc.GetProto(maxDoc).isMinimized = false);
-                            let hasView = expandedDocs.length === 1 && DocumentManager.Instance.getDocumentView(expandedDocs[0], this.props.ContainingCollectionView);
-                            if (!hasView) {
-                                this.props.addDocument && expandedDocs.forEach(async maxDoc => this.props.addDocument!(getDispDoc(maxDoc), false));
-                            }
-                            expandedDocs.forEach(maxDoc => maxDoc.isMinimized = wasMinimized);
-                        }
-                    }
-                    if (maxLocation && maxLocation !== "inPlace" && CollectionDockingView.Instance) {
-                        let dataDocs = DocListCast(CollectionDockingView.Instance.props.Document.data);
-                        if (dataDocs) {
-                            expandedDocs.forEach(maxDoc =>
-                                (!CollectionDockingView.Instance.CloseRightSplit(Doc.GetProto(maxDoc)) &&
-                                    this.props.addDocTab(getDispDoc(maxDoc), undefined, maxLocation)));
-                        }
-                    } else {
-                        let scrpt = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).inverse().transformPoint(NumCast(this.Document.width) / 2, NumCast(this.Document.height) / 2);
-                        this.collapseTargetsToPoint(scrpt, expandedDocs);
+            let maxLocation = StrCast(this.Document.maximizeLocation, "inPlace");
+            let getDispDoc = (target: Doc) => Object.getOwnPropertyNames(target).indexOf("isPrototype") === -1 ? target : Doc.MakeDelegate(target);
+            if (altKey || ctrlKey) {
+                maxLocation = this.Document.maximizeLocation = (ctrlKey ? maxLocation : (maxLocation === "inPlace" || !maxLocation ? "inTab" : "inPlace"));
+                if (!maxLocation || maxLocation === "inPlace") {
+                    let hadView = expandedDocs.length === 1 && DocumentManager.Instance.getDocumentView(expandedDocs[0], this.props.ContainingCollectionView);
+                    let wasMinimized = !hadView && expandedDocs.reduce((min, d) => !min && !d.isMinimized, false);
+                    expandedDocs.forEach(maxDoc => Doc.GetProto(maxDoc).isMinimized = false);
+                    let hasView = expandedDocs.length === 1 && DocumentManager.Instance.getDocumentView(expandedDocs[0], this.props.ContainingCollectionView);
+                    if (!hasView) {
+                        this.props.addDocument && expandedDocs.forEach(async maxDoc => this.props.addDocument!(getDispDoc(maxDoc), false));
                     }
+                    expandedDocs.forEach(maxDoc => maxDoc.isMinimized = wasMinimized);
                 }
-                else if (linkedDocs.length) {
-                    SelectionManager.DeselectAll();
-                    let first = linkedDocs.filter(d => Doc.AreProtosEqual(d.anchor1 as Doc, this.props.Document) && !d.anchor1anchored);
-                    let firstUnshown = first.filter(d => DocumentManager.Instance.getDocumentViews(d.anchor2 as Doc).length === 0);
-                    if (firstUnshown.length) first = [firstUnshown[0]];
-                    let linkedFwdDocs = first.length ? [first[0].anchor2 as Doc, first[0].anchor1 as Doc] : [expandedDocs[0], expandedDocs[0]];
-
-                    // @TODO: shouldn't always follow target context
-                    let linkedFwdContextDocs = [first.length ? await (first[0].targetContext) as Doc : undefined, undefined];
-                    let linkedFwdPage = [first.length ? NumCast(first[0].anchor2Page, undefined) : undefined, undefined];
-
-                    if (!linkedFwdDocs.some(l => l instanceof Promise)) {
-                        let maxLocation = StrCast(linkedFwdDocs[0].maximizeLocation, "inTab");
-                        let targetContext = !Doc.AreProtosEqual(linkedFwdContextDocs[altKey ? 1 : 0], this.props.ContainingCollectionView && this.props.ContainingCollectionView.props.Document) ? linkedFwdContextDocs[altKey ? 1 : 0] : undefined;
-                        DocumentManager.Instance.jumpToDocument(linkedFwdDocs[altKey ? 1 : 0], ctrlKey, false,
-                            // open up target if it's not already in view ... by zooming into the button document first and setting flag to reset zoom afterwards
-                            doc => this.props.focus(this.props.Document, true, 1, () => { this.props.addDocTab(doc, undefined, maxLocation); return true; }),
-                            linkedFwdPage[altKey ? 1 : 0], targetContext);
-                    }
+            }
+            if (maxLocation && maxLocation !== "inPlace" && CollectionDockingView.Instance) {
+                let dataDocs = DocListCast(CollectionDockingView.Instance.props.Document.data);
+                if (dataDocs) {
+                    expandedDocs.forEach(maxDoc =>
+                        (!CollectionDockingView.Instance.CloseRightSplit(Doc.GetProto(maxDoc)) &&
+                            this.props.addDocTab(getDispDoc(maxDoc), undefined, maxLocation)));
                 }
+            } else {
+                let scrpt = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).inverse().transformPoint(NumCast(this.Document.width) / 2, NumCast(this.Document.height) / 2);
+                this.collapseTargetsToPoint(scrpt, expandedDocs);
+            }
+        }
+        else if (linkedDocs.length) {
+            SelectionManager.DeselectAll();
+            let first = linkedDocs.filter(d => Doc.AreProtosEqual(d.anchor1 as Doc, this.props.Document) && !d.anchor1anchored);
+            let firstUnshown = first.filter(d => DocumentManager.Instance.getDocumentViews(d.anchor2 as Doc).length === 0);
+            if (firstUnshown.length) first = [firstUnshown[0]];
+            let linkedFwdDocs = first.length ? [first[0].anchor2 as Doc, first[0].anchor1 as Doc] : [expandedDocs[0], expandedDocs[0]];
+
+            // @TODO: shouldn't always follow target context
+            let linkedFwdContextDocs = [first.length ? await (first[0].targetContext) as Doc : undefined, undefined];
+            let linkedFwdPage = [first.length ? NumCast(first[0].anchor2Page, undefined) : undefined, undefined];
+
+            if (!linkedFwdDocs.some(l => l instanceof Promise)) {
+                let maxLocation = StrCast(linkedFwdDocs[0].maximizeLocation, "inTab");
+                let targetContext = !Doc.AreProtosEqual(linkedFwdContextDocs[altKey ? 1 : 0], this.props.ContainingCollectionDoc) ? linkedFwdContextDocs[altKey ? 1 : 0] : undefined;
+                DocumentManager.Instance.jumpToDocument(linkedFwdDocs[altKey ? 1 : 0], ctrlKey, false,
+                    // open up target if it's not already in view ... by zooming into the button document first and setting flag to reset zoom afterwards
+                    doc => this.props.focus(this.props.Document, true, 1, () => { this.props.addDocTab(doc, undefined, maxLocation); return true; }),
+                    linkedFwdPage[altKey ? 1 : 0], targetContext);
             }
         }
     }
@@ -387,25 +383,21 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             let targetDoc = this.props.Document;
             let annotations = await DocListCastAsync(sourceDoc.annotations);
             sourceDoc.linkedToDoc = true;
-            de.data.targetContext = this.props.ContainingCollectionView!.props.Document;
+            de.data.targetContext = this.props.ContainingCollectionDoc;
             targetDoc.targetContext = de.data.targetContext;
             annotations && annotations.forEach(anno => anno.target = targetDoc);
 
-            DocUtils.MakeLink(sourceDoc, targetDoc, this.props.ContainingCollectionView!.props.Document, `Link from ${StrCast(sourceDoc.title)}`);
+            DocUtils.MakeLink(sourceDoc, targetDoc, this.props.ContainingCollectionDoc, `Link from ${StrCast(sourceDoc.title)}`);
         }
         if (de.data instanceof DragManager.DocumentDragData && de.data.applyAsTemplate) {
             Doc.ApplyTemplateTo(de.data.draggedDocuments[0], this.props.Document);
             e.stopPropagation();
         }
         if (de.data instanceof DragManager.LinkDragData) {
-            let sourceDoc = de.data.linkSourceDocument;
-            let destDoc = this.props.Document;
-
             e.stopPropagation();
             // const docs = await SearchUtil.Search(`data_l:"${destDoc[Id]}"`, true);
             // const views = docs.map(d => DocumentManager.Instance.getDocumentView(d)).filter(d => d).map(d => d as DocumentView);
-            de.data.droppedDocuments.push(destDoc);
-            de.data.linkDocument = DocUtils.MakeLink(sourceDoc, destDoc, this.props.ContainingCollectionView && this.props.ContainingCollectionView.props.Document);
+            de.data.linkDocument = DocUtils.MakeLink(de.data.linkSourceDocument, this.props.Document, this.props.ContainingCollectionDoc);
         }
     }
 
@@ -436,7 +428,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     @undoBatch
     @action
     makeIntoPortal = async () => {
-        let anchors = await Promise.all(DocListCast(this.props.Document.links).map(async (d: Doc) => await Cast(d.anchor2, Doc)));
+        let anchors = await Promise.all(DocListCast(this.props.Document.links).map(async (d: Doc) => Cast(d.anchor2, Doc)));
         if (!anchors.find(anchor2 => anchor2 && anchor2.title === this.Document.title + ".portal" ? true : false)) {
             let portalID = (this.Document.title + ".portal").replace(/^-/, "").replace(/\([0-9]*\)$/, "");
             DocServer.GetRefField(portalID).then(existingPortal => {
@@ -512,7 +504,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         onClicks.push({ description: "Edit onClick Script", icon: "edit", event: (obj: any) => ScriptBox.EditButtonScript("On Button Clicked ...", this.props.Document, "onClick", obj.x, obj.y) });
         onClicks.push({
             description: "Edit onClick Foreach Doc Script", icon: "edit", event: (obj: any) => {
-                this.props.Document.collectionContext = this.props.ContainingCollectionView && this.props.ContainingCollectionView.props.Document;
+                this.props.Document.collectionContext = this.props.ContainingCollectionDoc;
                 ScriptBox.EditButtonScript("Foreach Collection Doc (d) => ", this.props.Document, "onClick", obj.x, obj.y, "docList(this.collectionContext.data).map(d => {", "});\n");
             }
         });
@@ -637,7 +629,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         const ruleColor = this.props.ruleProvider ? StrCast(this.props.ruleProvider["ruleColor_" + this.Document.heading]) : undefined;
         const ruleRounding = this.props.ruleProvider ? StrCast(this.props.ruleProvider["ruleRounding_" + this.Document.heading]) : undefined;
         const colorSet = this.Document.backgroundColor !== this.Document.defaultBackgroundColor;
-        const clusterCol = this.props.ContainingCollectionView && this.props.ContainingCollectionView.props.Document.clusterOverridesDefaultBackground;
+        const clusterCol = this.props.ContainingCollectionDoc && this.props.ContainingCollectionDoc.clusterOverridesDefaultBackground;
         const backgroundColor = this.Document.isBackground || (clusterCol && !colorSet) ?
             this.props.backgroundColor(this.Document) || StrCast(this.Document.backgroundColor) :
             ruleColor && !colorSet ? ruleColor : StrCast(this.Document.backgroundColor) || this.props.backgroundColor(this.Document);
@@ -753,7 +745,7 @@ export async function swapViews(doc: Doc, newLayoutField: string, oldLayoutField
         doc.type = newLayoutExt.type;
         doc.layout = await newLayoutExt.layout;
     }
-};
+}
 
 Scripting.addGlobal(function toggleDetail(doc: any) {
     let native = typeof doc.layout === "string";
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 943d181d6..faf11e9be 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -30,6 +30,7 @@ export interface FieldViewProps {
     leaveNativeSize?: boolean;
     fitToBox?: boolean;
     ContainingCollectionView: Opt<CollectionView | CollectionPDFView | CollectionVideoView>;
+    ContainingCollectionDoc: Opt<Doc>;
     ruleProvider: Doc | undefined;
     Document: Doc;
     DataDoc?: Doc;
diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx
index 7e0f3735d..c7e0f51d7 100644
--- a/src/client/views/nodes/KeyValuePair.tsx
+++ b/src/client/views/nodes/KeyValuePair.tsx
@@ -55,6 +55,7 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
             Document: this.props.doc,
             DataDoc: this.props.doc,
             ContainingCollectionView: undefined,
+            ContainingCollectionDoc: undefined,
             ruleProvider: undefined,
             fieldKey: this.props.keyName,
             fieldExt: "",
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 64b84ba55..c8534ed0d 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -31,7 +31,7 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
     @observable private _alt = false;
     @observable private _pdf: Opt<Pdfjs.PDFDocumentProxy>;
 
-    @computed get containingCollectionDocument() { return this.props.ContainingCollectionView && this.props.ContainingCollectionView.props.Document; }
+    @computed get containingCollectionDocument() { return this.props.ContainingCollectionDoc; }
     @computed get dataDoc() { return this.props.DataDoc && (BoolCast(this.props.Document.isTemplate) || BoolCast(this.props.DataDoc.isTemplate) || this.props.DataDoc.layout === this.props.Document) ? this.props.DataDoc : Doc.GetProto(this.props.Document); }
 
 
diff --git a/src/client/views/presentationview/PresentationElement.tsx b/src/client/views/presentationview/PresentationElement.tsx
index 7be44faf6..126d62c52 100644
--- a/src/client/views/presentationview/PresentationElement.tsx
+++ b/src/client/views/presentationview/PresentationElement.tsx
@@ -366,6 +366,7 @@ export default class PresentationElement extends React.Component<PresentationEle
                     zoomToScale={emptyFunction}
                     getScale={returnOne}
                     ContainingCollectionView={undefined}
+                    ContainingCollectionDoc={undefined}
                     ContentScaling={scale}
                 />
                 <div style={{
diff --git a/src/client/views/search/FilterBox.tsx b/src/client/views/search/FilterBox.tsx
index c13d1d276..da733d64b 100644
--- a/src/client/views/search/FilterBox.tsx
+++ b/src/client/views/search/FilterBox.tsx
@@ -195,11 +195,9 @@ export class FilterBox extends React.Component {
                     collections.push(element.props.Document);
                 }
             }
-            //gets the selected doc's containing view
-            let containingView = element.props.ContainingCollectionView;
             //makes sure collections aren't added more than once
-            if (containingView && !collections.includes(containingView.props.Document)) {
-                collections.push(containingView.props.Document);
+            if (element.props.ContainingCollectionDoc && !collections.includes(element.props.ContainingCollectionDoc)) {
+                collections.push(element.props.ContainingCollectionDoc);
             }
         });
 
diff --git a/src/client/views/search/SearchItem.tsx b/src/client/views/search/SearchItem.tsx
index 510672788..b2e886d95 100644
--- a/src/client/views/search/SearchItem.tsx
+++ b/src/client/views/search/SearchItem.tsx
@@ -180,6 +180,7 @@ export class SearchItem extends React.Component<SearchItemProps> {
                     zoomToScale={emptyFunction}
                     getScale={returnOne}
                     ContainingCollectionView={undefined}
+                    ContainingCollectionDoc={undefined}
                     ContentScaling={scale}
                 />
             </div>;
-- 
cgit v1.2.3-70-g09d2


From bf4f4cb2e2997cb0ff6c86eef68b3d6b0310f319 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Thu, 19 Sep 2019 00:41:11 -0400
Subject: more cleanup of document view

---
 src/client/views/DocumentDecorations.tsx |  1 -
 src/client/views/nodes/DocumentView.tsx  | 21 +++++++--------------
 2 files changed, 7 insertions(+), 15 deletions(-)

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

diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index ad38d16aa..4582c5b0c 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -381,7 +381,6 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
 
         IconBox.AutomaticTitle(iconDoc);
         //iconDoc.proto![this._fieldKey] = selected.length > 1 ? "collection" : undefined;
-        iconDoc.proto!.isMinimized = false;
         iconDoc.width = Number(MINIMIZED_ICON_SIZE);
         iconDoc.height = Number(MINIMIZED_ICON_SIZE);
         iconDoc.x = NumCast(doc.x);
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 39bba2eb2..4a37457c0 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -242,27 +242,20 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         if (expandedDocs.length) {
             SelectionManager.DeselectAll();
             let maxLocation = StrCast(this.Document.maximizeLocation, "inPlace");
-            let getDispDoc = (target: Doc) => Object.getOwnPropertyNames(target).indexOf("isPrototype") === -1 ? target : Doc.MakeDelegate(target);
             if (altKey || ctrlKey) {
-                maxLocation = this.Document.maximizeLocation = (ctrlKey ? maxLocation : (maxLocation === "inPlace" || !maxLocation ? "inTab" : "inPlace"));
-                if (!maxLocation || maxLocation === "inPlace") {
+                maxLocation = this.Document.maximizeLocation = (ctrlKey ? maxLocation : (maxLocation === "inPlace" ? "inTab" : "inPlace"));
+                if (maxLocation === "inPlace") {
                     let hadView = expandedDocs.length === 1 && DocumentManager.Instance.getDocumentView(expandedDocs[0], this.props.ContainingCollectionView);
                     let wasMinimized = !hadView && expandedDocs.reduce((min, d) => !min && !d.isMinimized, false);
-                    expandedDocs.forEach(maxDoc => Doc.GetProto(maxDoc).isMinimized = false);
+                    expandedDocs.forEach(maxDoc => maxDoc.isMinimized = false);
                     let hasView = expandedDocs.length === 1 && DocumentManager.Instance.getDocumentView(expandedDocs[0], this.props.ContainingCollectionView);
-                    if (!hasView) {
-                        this.props.addDocument && expandedDocs.forEach(async maxDoc => this.props.addDocument!(getDispDoc(maxDoc), false));
-                    }
+                    !hasView && expandedDocs.forEach(async maxDoc => this.props.addDocument && this.props.addDocument(maxDoc, false));
                     expandedDocs.forEach(maxDoc => maxDoc.isMinimized = wasMinimized);
                 }
             }
-            if (maxLocation && maxLocation !== "inPlace" && CollectionDockingView.Instance) {
-                let dataDocs = DocListCast(CollectionDockingView.Instance.props.Document.data);
-                if (dataDocs) {
-                    expandedDocs.forEach(maxDoc =>
-                        (!CollectionDockingView.Instance.CloseRightSplit(Doc.GetProto(maxDoc)) &&
-                            this.props.addDocTab(getDispDoc(maxDoc), undefined, maxLocation)));
-                }
+            if (maxLocation !== "inPlace" && CollectionDockingView.Instance) {
+                expandedDocs.forEach(maxDoc =>
+                    (!CollectionDockingView.Instance.CloseRightSplit(maxDoc) && this.props.addDocTab(maxDoc, undefined, maxLocation)));
             } else {
                 let scrpt = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).inverse().transformPoint(NumCast(this.Document.width) / 2, NumCast(this.Document.height) / 2);
                 this.collapseTargetsToPoint(scrpt, expandedDocs);
-- 
cgit v1.2.3-70-g09d2


From 4c0bdf9c38a134a7e669d8c113e10e9cfac6e1a6 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Thu, 19 Sep 2019 11:49:38 -0400
Subject: fixed text location when editing a resized document. made
 animatebetweenPoint from documentview's collapsetoTargetPoint

---
 src/client/util/DocumentManager.ts                 | 31 +++++++++++++++++++-
 src/client/views/DocumentDecorations.tsx           |  3 +-
 src/client/views/MainOverlayTextBox.tsx            |  2 +-
 src/client/views/collections/CollectionSubView.tsx |  4 +++
 src/client/views/nodes/DocumentView.tsx            | 33 +---------------------
 5 files changed, 38 insertions(+), 35 deletions(-)

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

diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 65ab32539..a3c7429b9 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -10,7 +10,7 @@ import { DocumentView } from '../views/nodes/DocumentView';
 import { LinkManager } from './LinkManager';
 import { undoBatch, UndoManager } from './UndoManager';
 import { Scripting } from './Scripting';
-import { emptyFunction } from '../../Utils';
+import { List } from '../../new_fields/List';
 
 
 export class DocumentManager {
@@ -208,5 +208,34 @@ export class DocumentManager {
             return 1;
         }
     }
+
+    @action
+    animateBetweenPoint = (scrpt: number[], expandedDocs: Doc[] | undefined): void => {
+        expandedDocs && expandedDocs.map(expDoc => {
+            if (expDoc.isMinimized || expDoc.isAnimating === "min") { // MAXIMIZE DOC
+                if (expDoc.isMinimized) {  // docs are never actaully at the minimized location.  so when we unminimize one, we have to set our overrides to make it look like it was at the minimize location
+                    expDoc.isMinimized = false;
+                    expDoc.animateToPos = new List<number>([...scrpt, 0]);
+                    expDoc.animateToDimensions = new List<number>([0, 0]);
+                }
+                setTimeout(() => {
+                    expDoc.isAnimating = "max";
+                    expDoc.animateToPos = new List<number>([0, 0, 1]);
+                    expDoc.animateToDimensions = new List<number>([NumCast(expDoc.width), NumCast(expDoc.height)]);
+                    setTimeout(() => expDoc.isAnimating === "max" && (expDoc.isAnimating = expDoc.animateToPos = expDoc.animateToDimensions = undefined), 600);
+                }, 0);
+            } else {  // MINIMIZE DOC
+                expDoc.isAnimating = "min";
+                expDoc.animateToPos = new List<number>([...scrpt, 0]);
+                expDoc.animateToDimensions = new List<number>([0, 0]);
+                setTimeout(() => {
+                    if (expDoc.isAnimating === "min") {
+                        expDoc.isMinimized = true;
+                        expDoc.isAnimating = expDoc.animateToPos = expDoc.animateToDimensions = undefined;
+                    }
+                }, 600);
+            }
+        });
+    }
 }
 Scripting.addGlobal(function focus(doc: any) { DocumentManager.Instance.getDocumentViews(Doc.GetProto(doc)).map(view => view.props.focus(doc, true)); });
\ No newline at end of file
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 4582c5b0c..2b121b32b 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -364,7 +364,8 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
                     if (minimizedDoc) {
                         let scrpt = selectedDocs[0].props.ScreenToLocalTransform().scale(selectedDocs[0].props.ContentScaling()).inverse().transformPoint(
                             NumCast(minimizedDoc.x) - NumCast(selectedDocs[0].Document.x), NumCast(minimizedDoc.y) - NumCast(selectedDocs[0].Document.y));
-                        selectedDocs[0].collapseTargetsToPoint(scrpt, await DocListCastAsync(minimizedDoc.maximizedDocs));
+                        SelectionManager.DeselectAll();
+                        DocumentManager.Instance.animateBetweenPoint(scrpt, await DocListCastAsync(minimizedDoc.maximizedDocs));
                     }
                 });
             }
diff --git a/src/client/views/MainOverlayTextBox.tsx b/src/client/views/MainOverlayTextBox.tsx
index f32fb584a..115ab6cd5 100644
--- a/src/client/views/MainOverlayTextBox.tsx
+++ b/src/client/views/MainOverlayTextBox.tsx
@@ -78,7 +78,7 @@ export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps>
         this._textTargetDiv = div;
         this._textHideOnLeave = FormattedTextBox.InputBoxOverlay && FormattedTextBox.InputBoxOverlay.props.hideOnLeave;
         if (div) {
-            this._textBottom = div.parentElement && getComputedStyle(div.parentElement).bottom ? true : false;
+            this._textBottom = div.parentElement && getComputedStyle(div.parentElement).top !== "0px" ? true : false;
             this._textColor = (getComputedStyle(div) as any).color;
             div.style.color = "transparent";
         }
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 001560167..804bfa2b2 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -277,6 +277,10 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
                     promises.push(prom);
                 }
             }
+            if (text) {
+                this.props.addDocument(Docs.Create.TextDocument({ ...options, documentText: "@@@" + text, width: 400, height: 315 }));
+                return;
+            }
 
             if (promises.length) {
                 Promise.all(promises).finally(() => { completed && completed(); batch.end(); });
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 50691fd38..c27e7f4a1 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -88,7 +88,6 @@ export interface DocumentViewProps {
     bringToFront: (doc: Doc, sendToBack?: boolean) => void;
     addDocTab: (doc: Doc, dataDoc: Doc | undefined, where: string) => boolean;
     pinToPres: (document: Doc) => void;
-    collapseToPoint?: (scrpt: number[], expandedDocs: Doc[] | undefined) => void;
     zoomToScale: (scale: number) => void;
     backgroundColor: (doc: Doc) => string | undefined;
     getScale: () => number;
@@ -178,36 +177,6 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         }
     }
 
-    @action
-    public collapseTargetsToPoint = (scrpt: number[], expandedDocs: Doc[] | undefined): void => {
-        SelectionManager.DeselectAll();
-        expandedDocs && expandedDocs.map(expDoc => {
-            if (expDoc.isMinimized || expDoc.isAnimating === "min") { // MAXIMIZE DOC
-                if (expDoc.isMinimized) {  // docs are never actaully at the minimized location.  so when we unminimize one, we have to set our overrides to make it look like it was at the minimize location
-                    expDoc.isMinimized = false;
-                    expDoc.animateToPos = new List<number>([...scrpt, 0]);
-                    expDoc.animateToDimensions = new List<number>([0, 0]);
-                }
-                setTimeout(() => {
-                    expDoc.isAnimating = "max";
-                    expDoc.animateToPos = new List<number>([0, 0, 1]);
-                    expDoc.animateToDimensions = new List<number>([NumCast(expDoc.width), NumCast(expDoc.height)]);
-                    setTimeout(() => expDoc.isAnimating === "max" && (expDoc.isAnimating = expDoc.animateToPos = expDoc.animateToDimensions = undefined), 600);
-                }, 0);
-            } else {  // MINIMIZE DOC
-                expDoc.isAnimating = "min";
-                expDoc.animateToPos = new List<number>([...scrpt, 0]);
-                expDoc.animateToDimensions = new List<number>([0, 0]);
-                setTimeout(() => {
-                    if (expDoc.isAnimating === "min") {
-                        expDoc.isMinimized = true;
-                        expDoc.isAnimating = expDoc.animateToPos = expDoc.animateToDimensions = undefined;
-                    }
-                }, 600);
-            }
-        });
-    }
-
     onClick = async (e: React.MouseEvent) => {
         if (!e.nativeEvent.cancelBubble && !this.Document.ignoreClick && CurrentUserUtils.MainDocId !== this.props.Document[Id] &&
             (Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD && Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD)) {
@@ -246,7 +215,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             if (maxLocation === "inPlace") {
                 expandedDocs.forEach(maxDoc => this.props.addDocument && this.props.addDocument(maxDoc, false));
                 let scrpt = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).inverse().transformPoint(NumCast(this.Document.width) / 2, NumCast(this.Document.height) / 2);
-                this.collapseTargetsToPoint(scrpt, expandedDocs);
+                DocumentManager.Instance.animateBetweenPoint(scrpt, expandedDocs);
             } else {
                 expandedDocs.forEach(maxDoc => (!this.props.addDocTab(maxDoc, undefined, "close") && this.props.addDocTab(maxDoc, undefined, maxLocation)));
             }
-- 
cgit v1.2.3-70-g09d2


From 13e3611f87a941fe29ff0256890cdf3c74351bab Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Thu, 19 Sep 2019 16:38:37 -0400
Subject: fixes to button bar

---
 src/client/util/DragManager.ts                     |   5 +-
 src/client/views/DocumentButtonBar.scss            | 129 ++++++++
 src/client/views/DocumentButtonBar.tsx             | 368 +++++++++++++++++++++
 src/client/views/DocumentDecorations.tsx           | 296 +----------------
 .../views/collections/CollectionDockingView.tsx    |  18 +-
 .../views/collections/ParentDocumentSelector.scss  |  11 +-
 .../views/collections/ParentDocumentSelector.tsx   |  46 ++-
 src/client/views/nodes/DocumentView.tsx            |   3 +-
 src/client/views/nodes/FormattedTextBox.tsx        |  15 +-
 9 files changed, 585 insertions(+), 306 deletions(-)
 create mode 100644 src/client/views/DocumentButtonBar.scss
 create mode 100644 src/client/views/DocumentButtonBar.tsx

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

diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index e3cdb3f39..56496c99b 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -66,7 +66,7 @@ export function SetupDrag(
 
 function moveLinkedDocument(doc: Doc, targetCollection: Doc, addDocument: (doc: Doc) => boolean): boolean {
     const document = SelectionManager.SelectedDocuments()[0];
-    document.props.removeDocument && document.props.removeDocument(doc);
+    document && document.props.removeDocument && document.props.removeDocument(doc);
     addDocument(doc);
     return true;
 }
@@ -270,7 +270,8 @@ export namespace DragManager {
                 let droppedDocuments: Doc[] = dragData.draggedDocuments.reduce((droppedDocs: Doc[], d) => {
                     let dvs = DocumentManager.Instance.getDocumentViews(d);
                     if (dvs.length) {
-                        let inContext = dvs.filter(dv => dv.props.ContainingCollectionView === SelectionManager.SelectedDocuments()[0].props.ContainingCollectionView);
+                        let containingView = SelectionManager.SelectedDocuments()[0] ? SelectionManager.SelectedDocuments()[0].props.ContainingCollectionView : undefined;
+                        let inContext = dvs.filter(dv => dv.props.ContainingCollectionView === containingView);
                         if (inContext.length) {
                             inContext.forEach(dv => droppedDocs.push(dv.props.Document));
                         } else {
diff --git a/src/client/views/DocumentButtonBar.scss b/src/client/views/DocumentButtonBar.scss
new file mode 100644
index 000000000..8cd419bbe
--- /dev/null
+++ b/src/client/views/DocumentButtonBar.scss
@@ -0,0 +1,129 @@
+@import "globalCssVariables";
+
+$linkGap : 3px;
+
+.linkFlyout {
+    grid-column: 2/4;
+}
+
+.linkButton-empty:hover {
+    background: $main-accent;
+    transform: scale(1.05);
+    cursor: pointer;
+}
+
+.linkButton-nonempty:hover {
+    background: $main-accent;
+    transform: scale(1.05);
+    cursor: pointer;
+}
+
+.documentButtonBar {
+    margin-top: $linkGap;
+    grid-column: 1/4;
+    width: max-content;
+    height: auto;
+    display: flex;
+    flex-direction: row;
+}
+
+.linkButtonWrapper {
+    pointer-events: auto;
+    padding-right: 5px;
+    width: 25px;
+}
+
+.linkButton-linker {
+    height: 20px;
+    width: 20px;
+    text-align: center;
+    border-radius: 50%;
+    pointer-events: auto;
+    color: $dark-color;
+    border: $dark-color 1px solid;
+}
+
+.linkButton-linker:hover {
+    cursor: pointer;
+    transform: scale(1.05);
+}
+
+.linkButton-empty,
+.linkButton-nonempty {
+    height: 20px;
+    width: 20px;
+    border-radius: 50%;
+    opacity: 0.9;
+    pointer-events: auto;
+    background-color: $dark-color;
+    color: $light-color;
+    text-transform: uppercase;
+    letter-spacing: 2px;
+    font-size: 75%;
+    transition: transform 0.2s;
+    text-align: center;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+
+    &:hover {
+        background: $main-accent;
+        transform: scale(1.05);
+        cursor: pointer;
+    }
+}
+
+.templating-menu {
+    position: absolute;
+    pointer-events: auto;
+    text-transform: uppercase;
+    letter-spacing: 2px;
+    font-size: 75%;
+    transition: transform 0.2s;
+    text-align: center;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+}
+
+.templating-button,
+.docDecs-tagButton {
+    width: 20px;
+    height: 20px;
+    border-radius: 50%;
+    opacity: 0.9;
+    font-size: 14;
+    background-color: $dark-color;
+    color: $light-color;
+    text-align: center;
+    cursor: pointer;
+
+    &:hover {
+        background: $main-accent;
+        transform: scale(1.05);
+    }
+}
+
+#template-list {
+    position: absolute;
+    top: 25px;
+    left: 0px;
+    width: max-content;
+    font-family: $sans-serif;
+    font-size: 12px;
+    background-color: $light-color-secondary;
+    padding: 2px 12px;
+    list-style: none;
+
+    .templateToggle, .chromeToggle {
+        text-align: left;
+    }
+
+    input {
+        margin-right: 10px;
+    }
+}
+
+@-moz-keyframes spin { 100% { -moz-transform: rotate(360deg); } }
+@-webkit-keyframes spin { 100% { -webkit-transform: rotate(360deg); } }
+@keyframes spin { 100% { -webkit-transform: rotate(360deg); transform: rotate(360deg); } }
\ No newline at end of file
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
new file mode 100644
index 000000000..6c29a2dc7
--- /dev/null
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -0,0 +1,368 @@
+import { IconProp, library } from '@fortawesome/fontawesome-svg-core';
+import { faArrowAltCircleDown, faArrowAltCircleUp, faCheckCircle, faCloudUploadAlt, faLink, faShare, faStopCircle, faSyncAlt, faTag, faTimes } from '@fortawesome/free-solid-svg-icons';
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { action, observable, runInAction } from "mobx";
+import { observer } from "mobx-react";
+import { Doc } from "../../new_fields/Doc";
+import { RichTextField } from '../../new_fields/RichTextField';
+import { NumCast } from "../../new_fields/Types";
+import { URLField } from '../../new_fields/URLField';
+import { emptyFunction } from "../../Utils";
+import { Pulls, Pushes } from '../apis/google_docs/GoogleApiClientUtils';
+import { DragLinksAsDocuments, DragManager } from "../util/DragManager";
+import { LinkManager } from '../util/LinkManager';
+import { UndoManager } from "../util/UndoManager";
+import './DocumentButtonBar.scss';
+import './collections/ParentDocumentSelector.scss';
+import { LinkMenu } from "./linking/LinkMenu";
+import { MetadataEntryMenu } from './MetadataEntryMenu';
+import { FormattedTextBox, GoogleRef } from "./nodes/FormattedTextBox";
+import { TemplateMenu } from "./TemplateMenu";
+import { Template, Templates } from "./Templates";
+import React = require("react");
+import { DocumentView } from './nodes/DocumentView';
+import { ParentDocSelector } from './collections/ParentDocumentSelector';
+import { CollectionDockingView } from './collections/CollectionDockingView';
+const higflyout = require("@hig/flyout");
+export const { anchorPoints } = higflyout;
+export const Flyout = higflyout.default;
+
+library.add(faLink);
+library.add(faTag);
+library.add(faTimes);
+library.add(faArrowAltCircleDown);
+library.add(faArrowAltCircleUp);
+library.add(faStopCircle);
+library.add(faCheckCircle);
+library.add(faCloudUploadAlt);
+library.add(faSyncAlt);
+library.add(faShare);
+
+const cloud: IconProp = "cloud-upload-alt";
+const fetch: IconProp = "sync-alt";
+
+@observer
+export class DocumentButtonBar extends React.Component<{ views: DocumentView[], stack?: any }, {}> {
+    private _linkButton = React.createRef<HTMLDivElement>();
+    private _linkerButton = React.createRef<HTMLDivElement>();
+    private _embedButton = React.createRef<HTMLDivElement>();
+    private _tooltipoff = React.createRef<HTMLDivElement>();
+    private _textDoc?: Doc;
+    private _linkDrag?: UndoManager.Batch;
+    public static Instance: DocumentButtonBar;
+
+    constructor(props: { views: DocumentView[] }) {
+        super(props);
+        DocumentButtonBar.Instance = this;
+    }
+
+    @observable public pushIcon: IconProp = "arrow-alt-circle-up";
+    @observable public pullIcon: IconProp = "arrow-alt-circle-down";
+    @observable public pullColor: string = "white";
+    @observable public isAnimatingFetch = false;
+    @observable public openHover = false;
+    public pullColorAnimating = false;
+
+    private pullAnimating = false;
+    private pushAnimating = false;
+
+    public startPullOutcome = action((success: boolean) => {
+        if (!this.pullAnimating) {
+            this.pullAnimating = true;
+            this.pullIcon = success ? "check-circle" : "stop-circle";
+            setTimeout(() => runInAction(() => {
+                this.pullIcon = "arrow-alt-circle-down";
+                this.pullAnimating = false;
+            }), 1000);
+        }
+    });
+
+    public startPushOutcome = action((success: boolean) => {
+        if (!this.pushAnimating) {
+            this.pushAnimating = true;
+            this.pushIcon = success ? "check-circle" : "stop-circle";
+            setTimeout(() => runInAction(() => {
+                this.pushIcon = "arrow-alt-circle-up";
+                this.pushAnimating = false;
+            }), 1000);
+        }
+    });
+
+    public setPullState = action((unchanged: boolean) => {
+        this.isAnimatingFetch = false;
+        if (!this.pullColorAnimating) {
+            this.pullColorAnimating = true;
+            this.pullColor = unchanged ? "lawngreen" : "red";
+            setTimeout(this.clearPullColor, 1000);
+        }
+    });
+
+    private clearPullColor = action(() => {
+        this.pullColor = "white";
+        this.pullColorAnimating = false;
+    });
+
+    onLinkerButtonDown = (e: React.PointerEvent): void => {
+        e.stopPropagation();
+        e.preventDefault();
+        document.removeEventListener("pointermove", this.onLinkerButtonMoved);
+        document.addEventListener("pointermove", this.onLinkerButtonMoved);
+        document.removeEventListener("pointerup", this.onLinkerButtonUp);
+        document.addEventListener("pointerup", this.onLinkerButtonUp);
+    }
+
+    onEmbedButtonDown = (e: React.PointerEvent): void => {
+        e.stopPropagation();
+        e.preventDefault();
+        document.removeEventListener("pointermove", this.onEmbedButtonMoved);
+        document.addEventListener("pointermove", this.onEmbedButtonMoved);
+        document.removeEventListener("pointerup", this.onEmbedButtonUp);
+        document.addEventListener("pointerup", this.onEmbedButtonUp);
+    }
+
+    onLinkerButtonUp = (e: PointerEvent): void => {
+        document.removeEventListener("pointermove", this.onLinkerButtonMoved);
+        document.removeEventListener("pointerup", this.onLinkerButtonUp);
+        e.stopPropagation();
+    }
+
+    onEmbedButtonUp = (e: PointerEvent): void => {
+        document.removeEventListener("pointermove", this.onEmbedButtonMoved);
+        document.removeEventListener("pointerup", this.onEmbedButtonUp);
+        e.stopPropagation();
+    }
+
+    @action
+    onLinkerButtonMoved = (e: PointerEvent): void => {
+        if (this._linkerButton.current !== null) {
+            document.removeEventListener("pointermove", this.onLinkerButtonMoved);
+            document.removeEventListener("pointerup", this.onLinkerButtonUp);
+            let selDoc = this.props.views[0];
+            let container = selDoc.props.ContainingCollectionDoc ? selDoc.props.ContainingCollectionDoc.proto : undefined;
+            let dragData = new DragManager.LinkDragData(selDoc.props.Document, container ? [container] : []);
+            FormattedTextBox.InputBoxOverlay = undefined;
+            this._linkDrag = UndoManager.StartBatch("Drag Link");
+            DragManager.StartLinkDrag(this._linkerButton.current, dragData, e.pageX, e.pageY, {
+                handlers: {
+                    dragComplete: () => {
+                        if (this._linkDrag) {
+                            this._linkDrag.end();
+                            this._linkDrag = undefined;
+                        }
+                    },
+                },
+                hideSource: false
+            });
+        }
+        e.stopPropagation();
+    }
+
+    @action
+    onEmbedButtonMoved = (e: PointerEvent): void => {
+        if (this._embedButton.current !== null) {
+            document.removeEventListener("pointermove", this.onEmbedButtonMoved);
+            document.removeEventListener("pointerup", this.onEmbedButtonUp);
+
+            let dragDocView = this.props.views[0];
+            let dragData = new DragManager.EmbedDragData(dragDocView.props.Document);
+
+            DragManager.StartEmbedDrag(dragDocView.ContentDiv!, dragData, e.x, e.y, {
+                handlers: {
+                    dragComplete: action(emptyFunction),
+                },
+                hideSource: false
+            });
+        }
+        e.stopPropagation();
+    }
+
+    onLinkButtonDown = (e: React.PointerEvent): void => {
+        e.stopPropagation();
+        e.preventDefault();
+        document.removeEventListener("pointermove", this.onLinkButtonMoved);
+        document.addEventListener("pointermove", this.onLinkButtonMoved);
+        document.removeEventListener("pointerup", this.onLinkButtonUp);
+        document.addEventListener("pointerup", this.onLinkButtonUp);
+    }
+
+    onLinkButtonUp = (e: PointerEvent): void => {
+        document.removeEventListener("pointermove", this.onLinkButtonMoved);
+        document.removeEventListener("pointerup", this.onLinkButtonUp);
+        e.stopPropagation();
+    }
+
+    onLinkButtonMoved = async (e: PointerEvent) => {
+        if (this._linkButton.current !== null && (e.movementX > 1 || e.movementY > 1)) {
+            document.removeEventListener("pointermove", this.onLinkButtonMoved);
+            document.removeEventListener("pointerup", this.onLinkButtonUp);
+            DragLinksAsDocuments(this._linkButton.current, e.x, e.y, this.props.views[0].props.Document);
+        }
+        e.stopPropagation();
+    }
+
+    considerEmbed = () => {
+        let thisDoc = this.props.views[0].props.Document;
+        let canEmbed = thisDoc.data && thisDoc.data instanceof URLField;
+        if (!canEmbed) return (null);
+        return (
+            <div className="linkButtonWrapper">
+                <div title="Drag Embed" className="linkButton-linker" ref={this._embedButton} onPointerDown={this.onEmbedButtonDown}>
+                    <FontAwesomeIcon className="documentdecorations-icon" icon="image" size="sm" />
+                </div>
+            </div>
+        );
+    }
+
+    private get targetDoc() {
+        return this.props.views[0].props.Document;
+    }
+
+    considerGoogleDocsPush = () => {
+        let canPush = this.targetDoc.data && this.targetDoc.data instanceof RichTextField;
+        if (!canPush) return (null);
+        let published = Doc.GetProto(this.targetDoc)[GoogleRef] !== undefined;
+        let icon: IconProp = published ? (this.pushIcon as any) : cloud;
+        return (
+            <div className={"linkButtonWrapper"}>
+                <div title={`${published ? "Push" : "Publish"} to Google Docs`} className="linkButton-linker" onClick={() => {
+                    DocumentDecorations.hasPushedHack = false;
+                    this.targetDoc[Pushes] = NumCast(this.targetDoc[Pushes]) + 1;
+                }}>
+                    <FontAwesomeIcon className="documentdecorations-icon" icon={icon} size={published ? "sm" : "xs"} />
+                </div>
+            </div>
+        );
+    }
+
+    considerGoogleDocsPull = () => {
+        let canPull = this.targetDoc.data && this.targetDoc.data instanceof RichTextField;
+        let dataDoc = Doc.GetProto(this.targetDoc);
+        if (!canPull || !dataDoc[GoogleRef]) return (null);
+        let icon = dataDoc.unchanged === false ? (this.pullIcon as any) : fetch;
+        icon = this.openHover ? "share" : icon;
+        let animation = this.isAnimatingFetch ? "spin 0.5s linear infinite" : "none";
+        let title = `${!dataDoc.unchanged ? "Pull from" : "Fetch"} Google Docs`;
+        return (
+            <div className={"linkButtonWrapper"}>
+                <div
+                    title={title}
+                    className="linkButton-linker"
+                    style={{
+                        backgroundColor: this.pullColor,
+                        transition: "0.2s ease all"
+                    }}
+                    onPointerEnter={e => e.altKey && runInAction(() => this.openHover = true)}
+                    onPointerLeave={() => runInAction(() => this.openHover = false)}
+                    onClick={e => {
+                        if (e.altKey) {
+                            e.preventDefault();
+                            window.open(`https://docs.google.com/document/d/${dataDoc[GoogleRef]}/edit`);
+                        } else {
+                            this.clearPullColor();
+                            DocumentDecorations.hasPulledHack = false;
+                            this.targetDoc[Pulls] = NumCast(this.targetDoc[Pulls]) + 1;
+                            dataDoc.unchanged && runInAction(() => this.isAnimatingFetch = true);
+                        }
+                    }}>
+                    <FontAwesomeIcon
+                        style={{
+                            WebkitAnimation: animation,
+                            MozAnimation: animation
+                        }}
+                        className="documentdecorations-icon"
+                        icon={icon}
+                        size="sm"
+                    />
+                </div>
+            </div>
+        );
+    }
+
+    public static hasPushedHack = false;
+    public static hasPulledHack = false;
+
+    considerTooltip = () => {
+        let thisDoc = this.props.views[0].props.Document;
+        let isTextDoc = thisDoc.data && thisDoc.data instanceof RichTextField;
+        if (!isTextDoc) return null;
+        this._textDoc = thisDoc;
+        return (
+            <div className="tooltipwrapper">
+                <div title="Hide Tooltip" className="linkButton-linker" ref={this._tooltipoff} onPointerDown={this.onTooltipOff}>
+                    {/* <FontAwesomeIcon className="fa-image" icon="image" size="sm" /> */}
+                </div>
+            </div>
+
+        );
+    }
+
+    onTooltipOff = (e: React.PointerEvent): void => {
+        e.stopPropagation();
+        if (this._textDoc) {
+            if (this._tooltipoff.current) {
+                if (this._tooltipoff.current.title === "Hide Tooltip") {
+                    this._tooltipoff.current.title = "Show Tooltip";
+                    this._textDoc.tooltip = "hi";
+                }
+                else {
+                    this._tooltipoff.current.title = "Hide Tooltip";
+                }
+            }
+        }
+    }
+
+    get metadataMenu() {
+        return (
+            <div className="linkButtonWrapper">
+                <Flyout anchorPoint={anchorPoints.TOP_LEFT}
+                    content={<MetadataEntryMenu docs={() => this.props.views.map(dv => dv.props.Document)} suggestWithFunction />}>{/* tfs: @bcz This might need to be the data document? */}
+                    <div className="docDecs-tagButton" title="Add fields"><FontAwesomeIcon className="documentdecorations-icon" icon="tag" size="sm" /></div>
+                </Flyout>
+            </div>
+        );
+    }
+
+    render() {
+        let linkButton = null;
+        if (this.props.views.length > 0) {
+            let selFirst = this.props.views[0];
+
+            let linkCount = LinkManager.Instance.getAllRelatedLinks(selFirst.props.Document).length;
+            linkButton = (<Flyout
+                anchorPoint={anchorPoints.RIGHT_TOP}
+                content={<LinkMenu docView={selFirst}
+                    addDocTab={selFirst.props.addDocTab}
+                    changeFlyout={emptyFunction} />}>
+                <div className={"linkButton-" + (linkCount ? "nonempty" : "empty")} onPointerDown={this.onLinkButtonDown} >{linkCount}</div>
+            </Flyout >);
+        }
+
+        let templates: Map<Template, boolean> = new Map();
+        Array.from(Object.values(Templates.TemplateList)).map(template =>
+            templates.set(template, this.props.views.reduce((checked, doc) => checked || (doc.props.Document["show" + template.Name] ? true : false), false as boolean)));
+
+        return (<div className="documentButtonBar">
+            <div className="linkButtonWrapper">
+                <div title="View Links" className="linkFlyout" ref={this._linkButton}> {linkButton}  </div>
+            </div>
+            <div className="linkButtonWrapper">
+                <div title="Drag Link" className="linkButton-linker" ref={this._linkerButton} onPointerDown={this.onLinkerButtonDown}>
+                    <FontAwesomeIcon className="documentdecorations-icon" icon="link" size="sm" />
+                </div>
+            </div>
+            <div className="linkButtonWrapper">
+                <TemplateMenu docs={this.props.views} templates={templates} />
+            </div>
+            {this.metadataMenu}
+            {this.considerEmbed()}
+            {this.considerGoogleDocsPush()}
+            {this.considerGoogleDocsPull()}
+            <ParentDocSelector Document={this.props.views[0].props.Document} addDocTab={(doc, data, where) => {
+                where === "onRight" ? CollectionDockingView.AddRightSplit(doc, data) : this.props.stack ? CollectionDockingView.Instance.AddTab(this.props.stack, doc, data) : this.props.views[0].props.addDocTab(doc, data, "onRight");
+                return true;
+            }} />
+            {/* {this.considerTooltip()} */}
+        </div>
+        );
+    }
+}
\ No newline at end of file
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 2b121b32b..cbf812311 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -6,31 +6,23 @@ import { observer } from "mobx-react";
 import { Doc, DocListCastAsync } from "../../new_fields/Doc";
 import { List } from "../../new_fields/List";
 import { ObjectField } from '../../new_fields/ObjectField';
-import { RichTextField } from '../../new_fields/RichTextField';
 import { BoolCast, Cast, NumCast, StrCast } from "../../new_fields/Types";
-import { URLField } from '../../new_fields/URLField';
 import { CurrentUserUtils } from '../../server/authentication/models/current_user_utils';
 import { emptyFunction, Utils } from "../../Utils";
-import { Pulls, Pushes } from '../apis/google_docs/GoogleApiClientUtils';
 import { Docs, DocUtils } from "../documents/Documents";
 import { DocumentManager } from "../util/DocumentManager";
-import { DragLinksAsDocuments, DragManager } from "../util/DragManager";
-import { LinkManager } from '../util/LinkManager';
+import { DragManager } from "../util/DragManager";
 import { SelectionManager } from "../util/SelectionManager";
 import { undoBatch, UndoManager } from "../util/UndoManager";
 import { MINIMIZED_ICON_SIZE } from "../views/globalCssVariables.scss";
 import { CollectionView } from "./collections/CollectionView";
+import { DocumentButtonBar } from './DocumentButtonBar';
 import './DocumentDecorations.scss';
-import { LinkMenu } from "./linking/LinkMenu";
-import { MetadataEntryMenu } from './MetadataEntryMenu';
 import { PositionDocument } from './nodes/CollectionFreeFormDocumentView';
 import { DocumentView, swapViews } from "./nodes/DocumentView";
 import { FieldView } from "./nodes/FieldView";
-import { FormattedTextBox, GoogleRef } from "./nodes/FormattedTextBox";
+import { FormattedTextBox } from "./nodes/FormattedTextBox";
 import { IconBox } from "./nodes/IconBox";
-import { ImageBox } from './nodes/ImageBox';
-import { TemplateMenu } from "./TemplateMenu";
-import { Template, Templates } from "./Templates";
 import React = require("react");
 const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
@@ -47,28 +39,21 @@ library.add(faCloudUploadAlt);
 library.add(faSyncAlt);
 library.add(faShare);
 
-const cloud: IconProp = "cloud-upload-alt";
-const fetch: IconProp = "sync-alt";
-
 @observer
 export class DocumentDecorations extends React.Component<{}, { value: string }> {
     static Instance: DocumentDecorations;
     private _isPointerDown = false;
     private _resizing = "";
-    private keyinput: React.RefObject<HTMLInputElement>;
+    private _keyinput: React.RefObject<HTMLInputElement>;
     private _resizeBorderWidth = 16;
     private _linkBoxHeight = 20 + 3; // link button height + margin
     private _titleHeight = 20;
-    private _linkButton = React.createRef<HTMLDivElement>();
-    private _linkerButton = React.createRef<HTMLDivElement>();
     private _embedButton = React.createRef<HTMLDivElement>();
-    private _tooltipoff = React.createRef<HTMLDivElement>();
-    private _textDoc?: Doc;
     private _downX = 0;
     private _downY = 0;
     private _iconDoc?: Doc = undefined;
     private _resizeUndo?: UndoManager.Batch;
-    private _linkDrag?: UndoManager.Batch;
+    private _radiusDown = [0, 0];
     @observable private _minimizedX = 0;
     @observable private _minimizedY = 0;
     @observable private _title: string = "";
@@ -78,58 +63,17 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
     @observable private _opacity = 1;
     @observable private _removeIcon = false;
     @observable public Interacting = false;
-    @observable private _isMoving = false;
 
     @observable public pushIcon: IconProp = "arrow-alt-circle-up";
     @observable public pullIcon: IconProp = "arrow-alt-circle-down";
     @observable public pullColor: string = "white";
     @observable public isAnimatingFetch = false;
     @observable public openHover = false;
-    public pullColorAnimating = false;
-
-    private pullAnimating = false;
-    private pushAnimating = false;
-
-    public startPullOutcome = action((success: boolean) => {
-        if (!this.pullAnimating) {
-            this.pullAnimating = true;
-            this.pullIcon = success ? "check-circle" : "stop-circle";
-            setTimeout(() => runInAction(() => {
-                this.pullIcon = "arrow-alt-circle-down";
-                this.pullAnimating = false;
-            }), 1000);
-        }
-    });
-
-    public startPushOutcome = action((success: boolean) => {
-        if (!this.pushAnimating) {
-            this.pushAnimating = true;
-            this.pushIcon = success ? "check-circle" : "stop-circle";
-            setTimeout(() => runInAction(() => {
-                this.pushIcon = "arrow-alt-circle-up";
-                this.pushAnimating = false;
-            }), 1000);
-        }
-    });
-
-    public setPullState = action((unchanged: boolean) => {
-        this.isAnimatingFetch = false;
-        if (!this.pullColorAnimating) {
-            this.pullColorAnimating = true;
-            this.pullColor = unchanged ? "lawngreen" : "red";
-            setTimeout(this.clearPullColor, 1000);
-        }
-    });
-
-    private clearPullColor = action(() => {
-        this.pullColor = "white";
-        this.pullColorAnimating = false;
-    });
 
     constructor(props: Readonly<{}>) {
         super(props);
         DocumentDecorations.Instance = this;
-        this.keyinput = React.createRef();
+        this._keyinput = React.createRef();
         reaction(() => SelectionManager.SelectedDocuments().slice(), docs => this._edtingTitle = false);
     }
 
@@ -410,7 +354,6 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
         iconDoc.y = where[1] + NumCast(selView.props.Document.y);
     }
 
-    _radiusDown = [0, 0];
     @action
     onRadiusDown = (e: React.PointerEvent): void => {
         e.stopPropagation();
@@ -426,7 +369,6 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
     }
 
     onRadiusMove = (e: PointerEvent): void => {
-        this._isMoving = true;
         let dist = Math.sqrt((e.clientX - this._radiusDown[0]) * (e.clientX - this._radiusDown[0]) + (e.clientY - this._radiusDown[1]) * (e.clientY - this._radiusDown[1]));
         dist = dist < 3 ? 0 : dist;
         let usingRule = false;
@@ -447,7 +389,6 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
         e.preventDefault();
         this._isPointerDown = false;
         this._resizeUndo && this._resizeUndo.end();
-        this._isMoving = false;
         document.removeEventListener("pointermove", this.onRadiusMove);
         document.removeEventListener("pointerup", this.onRadiusUp);
     }
@@ -467,13 +408,6 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
         }
     }
 
-    onLinkerButtonDown = (e: React.PointerEvent): void => {
-        e.stopPropagation();
-        document.removeEventListener("pointermove", this.onLinkerButtonMoved);
-        document.addEventListener("pointermove", this.onLinkerButtonMoved);
-        document.removeEventListener("pointerup", this.onLinkerButtonUp);
-        document.addEventListener("pointerup", this.onLinkerButtonUp);
-    }
 
     onEmbedButtonDown = (e: React.PointerEvent): void => {
         e.stopPropagation();
@@ -483,11 +417,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
         document.addEventListener("pointerup", this.onEmbedButtonUp);
     }
 
-    onLinkerButtonUp = (e: PointerEvent): void => {
-        document.removeEventListener("pointermove", this.onLinkerButtonMoved);
-        document.removeEventListener("pointerup", this.onLinkerButtonUp);
-        e.stopPropagation();
-    }
+
 
     onEmbedButtonUp = (e: PointerEvent): void => {
         document.removeEventListener("pointermove", this.onEmbedButtonMoved);
@@ -495,31 +425,6 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
         e.stopPropagation();
     }
 
-    @action
-    onLinkerButtonMoved = (e: PointerEvent): void => {
-        if (this._linkerButton.current !== null) {
-            document.removeEventListener("pointermove", this.onLinkerButtonMoved);
-            document.removeEventListener("pointerup", this.onLinkerButtonUp);
-            let selDoc = SelectionManager.SelectedDocuments()[0];
-            let container = selDoc.props.ContainingCollectionDoc ? selDoc.props.ContainingCollectionDoc.proto : undefined;
-            let dragData = new DragManager.LinkDragData(selDoc.props.Document, container ? [container] : []);
-            FormattedTextBox.InputBoxOverlay = undefined;
-            this._linkDrag = UndoManager.StartBatch("Drag Link");
-            DragManager.StartLinkDrag(this._linkerButton.current, dragData, e.pageX, e.pageY, {
-                handlers: {
-                    dragComplete: () => {
-                        if (this._linkDrag) {
-                            this._linkDrag.end();
-                            this._linkDrag = undefined;
-                        }
-                    },
-                },
-                hideSource: false
-            });
-        }
-        e.stopPropagation();
-    }
-
     @action
     onEmbedButtonMoved = (e: PointerEvent): void => {
         if (this._embedButton.current !== null) {
@@ -539,29 +444,6 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
         e.stopPropagation();
     }
 
-    onLinkButtonDown = (e: React.PointerEvent): void => {
-        e.stopPropagation();
-        document.removeEventListener("pointermove", this.onLinkButtonMoved);
-        document.addEventListener("pointermove", this.onLinkButtonMoved);
-        document.removeEventListener("pointerup", this.onLinkButtonUp);
-        document.addEventListener("pointerup", this.onLinkButtonUp);
-    }
-
-    onLinkButtonUp = (e: PointerEvent): void => {
-        document.removeEventListener("pointermove", this.onLinkButtonMoved);
-        document.removeEventListener("pointerup", this.onLinkButtonUp);
-        e.stopPropagation();
-    }
-
-    onLinkButtonMoved = async (e: PointerEvent) => {
-        if (this._linkButton.current !== null && (e.movementX > 1 || e.movementY > 1)) {
-            document.removeEventListener("pointermove", this.onLinkButtonMoved);
-            document.removeEventListener("pointerup", this.onLinkButtonUp);
-            DragLinksAsDocuments(this._linkButton.current, e.x, e.y, SelectionManager.SelectedDocuments()[0].props.Document);
-        }
-        e.stopPropagation();
-    }
-
     onPointerMove = (e: PointerEvent): void => {
         e.stopPropagation();
         e.preventDefault();
@@ -690,134 +572,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
         return "-unset-";
     }
 
-    changeFlyoutContent = (): void => {
 
-    }
-    // buttonOnPointerUp = (e: React.PointerEvent): void => {
-    //     e.stopPropagation();
-    // }
-
-    considerEmbed = () => {
-        let thisDoc = SelectionManager.SelectedDocuments()[0].props.Document;
-        let canEmbed = thisDoc.data && thisDoc.data instanceof URLField;
-        if (!canEmbed) return (null);
-        return (
-            <div className="linkButtonWrapper">
-                <div title="Drag Embed" className="linkButton-linker" ref={this._embedButton} onPointerDown={this.onEmbedButtonDown}>
-                    <FontAwesomeIcon className="documentdecorations-icon" icon="image" size="sm" />
-                </div>
-            </div>
-        );
-    }
-
-    private get targetDoc() {
-        return SelectionManager.SelectedDocuments()[0].props.Document;
-    }
-
-    considerGoogleDocsPush = () => {
-        let canPush = this.targetDoc.data && this.targetDoc.data instanceof RichTextField;
-        if (!canPush) return (null);
-        let published = Doc.GetProto(this.targetDoc)[GoogleRef] !== undefined;
-        let icon: IconProp = published ? (this.pushIcon as any) : cloud;
-        return (
-            <div className={"linkButtonWrapper"}>
-                <div title={`${published ? "Push" : "Publish"} to Google Docs`} className="linkButton-linker" onClick={() => {
-                    DocumentDecorations.hasPushedHack = false;
-                    this.targetDoc[Pushes] = NumCast(this.targetDoc[Pushes]) + 1;
-                }}>
-                    <FontAwesomeIcon className="documentdecorations-icon" icon={icon} size={published ? "sm" : "xs"} />
-                </div>
-            </div>
-        );
-    }
-
-    considerGoogleDocsPull = () => {
-        let canPull = this.targetDoc.data && this.targetDoc.data instanceof RichTextField;
-        let dataDoc = Doc.GetProto(this.targetDoc);
-        if (!canPull || !dataDoc[GoogleRef]) return (null);
-        let icon = dataDoc.unchanged === false ? (this.pullIcon as any) : fetch;
-        icon = this.openHover ? "share" : icon;
-        let animation = this.isAnimatingFetch ? "spin 0.5s linear infinite" : "none";
-        let title = `${!dataDoc.unchanged ? "Pull from" : "Fetch"} Google Docs`;
-        return (
-            <div className={"linkButtonWrapper"}>
-                <div
-                    title={title}
-                    className="linkButton-linker"
-                    style={{
-                        backgroundColor: this.pullColor,
-                        transition: "0.2s ease all"
-                    }}
-                    onPointerEnter={e => e.altKey && runInAction(() => this.openHover = true)}
-                    onPointerLeave={() => runInAction(() => this.openHover = false)}
-                    onClick={e => {
-                        if (e.altKey) {
-                            e.preventDefault();
-                            window.open(`https://docs.google.com/document/d/${dataDoc[GoogleRef]}/edit`);
-                        } else {
-                            this.clearPullColor();
-                            DocumentDecorations.hasPulledHack = false;
-                            this.targetDoc[Pulls] = NumCast(this.targetDoc[Pulls]) + 1;
-                            dataDoc.unchanged && runInAction(() => this.isAnimatingFetch = true);
-                        }
-                    }}>
-                    <FontAwesomeIcon
-                        style={{
-                            WebkitAnimation: animation,
-                            MozAnimation: animation
-                        }}
-                        className="documentdecorations-icon"
-                        icon={icon}
-                        size="sm"
-                    />
-                </div>
-            </div>
-        );
-    }
-
-    public static hasPushedHack = false;
-    public static hasPulledHack = false;
-
-    considerTooltip = () => {
-        let thisDoc = SelectionManager.SelectedDocuments()[0].props.Document;
-        let isTextDoc = thisDoc.data && thisDoc.data instanceof RichTextField;
-        if (!isTextDoc) return null;
-        this._textDoc = thisDoc;
-        return (
-            <div className="tooltipwrapper">
-                <div title="Hide Tooltip" className="linkButton-linker" ref={this._tooltipoff} onPointerDown={this.onTooltipOff}>
-                    {/* <FontAwesomeIcon className="fa-image" icon="image" size="sm" /> */}
-                </div>
-            </div>
-
-        );
-    }
-
-    onTooltipOff = (e: React.PointerEvent): void => {
-        e.stopPropagation();
-        if (this._textDoc) {
-            if (this._tooltipoff.current) {
-                if (this._tooltipoff.current.title === "Hide Tooltip") {
-                    this._tooltipoff.current.title = "Show Tooltip";
-                    this._textDoc.tooltip = "hi";
-                }
-                else {
-                    this._tooltipoff.current.title = "Hide Tooltip";
-                }
-            }
-        }
-    }
-
-    get metadataMenu() {
-        return (
-            <div className="linkButtonWrapper">
-                <Flyout anchorPoint={anchorPoints.TOP_LEFT}
-                    content={<MetadataEntryMenu docs={() => SelectionManager.SelectedDocuments().map(dv => dv.props.Document)} suggestWithFunction />}>{/* tfs: @bcz This might need to be the data document? */}
-                    <div className="docDecs-tagButton" title="Add fields"><FontAwesomeIcon className="documentdecorations-icon" icon="tag" size="sm" /></div>
-                </Flyout>
-            </div>
-        );
-    }
 
     render() {
         var bounds = this.Bounds;
@@ -830,24 +585,6 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
                 {SelectionManager.SelectedDocuments().length === 1 ? IconBox.DocumentIcon(StrCast(SelectionManager.SelectedDocuments()[0].props.Document.layout, "...")) : "..."}
             </div>);
 
-        let linkButton = null;
-        if (SelectionManager.SelectedDocuments().length > 0) {
-            let selFirst = SelectionManager.SelectedDocuments()[0];
-
-            let linkCount = LinkManager.Instance.getAllRelatedLinks(selFirst.props.Document).length;
-            linkButton = (<Flyout
-                anchorPoint={anchorPoints.RIGHT_TOP}
-                content={<LinkMenu docView={selFirst}
-                    addDocTab={selFirst.props.addDocTab}
-                    changeFlyout={this.changeFlyoutContent} />}>
-                <div className={"linkButton-" + (linkCount ? "nonempty" : "empty")} onPointerDown={this.onLinkButtonDown} >{linkCount}</div>
-            </Flyout >);
-        }
-
-        let templates: Map<Template, boolean> = new Map();
-        Array.from(Object.values(Templates.TemplateList)).map(template =>
-            templates.set(template, SelectionManager.SelectedDocuments().reduce((checked, doc) => checked || (doc.props.Document["show" + template.Name] ? true : false), false as boolean)));
-
         bounds.x = Math.max(0, bounds.x - this._resizeBorderWidth / 2) + this._resizeBorderWidth / 2;
         bounds.y = Math.max(0, bounds.y - this._resizeBorderWidth / 2 - this._titleHeight) + this._resizeBorderWidth / 2 + this._titleHeight;
         const borderRadiusDraggerWidth = 15;
@@ -879,7 +616,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
                 {minimizeIcon}
 
                 {this._edtingTitle ?
-                    <input ref={this.keyinput} className="title" type="text" name="dynbox" value={this._title} onBlur={this.titleBlur} onChange={this.titleChanged} onKeyPress={this.titleEntered} /> :
+                    <input ref={this._keyinput} className="title" type="text" name="dynbox" value={this._title} onBlur={this.titleBlur} onChange={this.titleChanged} onKeyPress={this.titleEntered} /> :
                     <div className="title" onPointerDown={this.onTitleDown} ><span>{`${this.selectionTitle}`}</span></div>}
                 <div className="documentDecorations-closeButton" title="Close Document" onPointerDown={this.onCloseDown}>
                     <FontAwesomeIcon className="documentdecorations-times" icon={faTimes} size="lg" />
@@ -895,22 +632,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
                 <div id="documentDecorations-bottomRightResizer" className="documentDecorations-resizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
                 <div id="documentDecorations-borderRadius" className="documentDecorations-radius" onPointerDown={this.onRadiusDown} onContextMenu={(e) => e.preventDefault()}><span className="borderRadiusTooltip" title="Drag Corner Radius"></span></div>
                 <div className="link-button-container">
-                    <div className="linkButtonWrapper">
-                        <div title="View Links" className="linkFlyout" ref={this._linkButton}> {linkButton}  </div>
-                    </div>
-                    <div className="linkButtonWrapper">
-                        <div title="Drag Link" className="linkButton-linker" ref={this._linkerButton} onPointerDown={this.onLinkerButtonDown}>
-                            <FontAwesomeIcon className="documentdecorations-icon" icon="link" size="sm" />
-                        </div>
-                    </div>
-                    <div className="linkButtonWrapper">
-                        <TemplateMenu docs={SelectionManager.ViewsSortedVertically()} templates={templates} />
-                    </div>
-                    {this.metadataMenu}
-                    {this.considerEmbed()}
-                    {this.considerGoogleDocsPush()}
-                    {this.considerGoogleDocsPull()}
-                    {/* {this.considerTooltip()} */}
+                    <DocumentButtonBar views={SelectionManager.SelectedDocuments()} />
                 </div>
             </div >
         </div>
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 277fa0066..5ace1048d 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -28,8 +28,8 @@ import { MainView } from '../MainView';
 import { DocumentView } from "../nodes/DocumentView";
 import "./CollectionDockingView.scss";
 import { SubCollectionViewProps } from "./CollectionSubView";
-import { ParentDocSelector } from './ParentDocumentSelector';
 import React = require("react");
+import { ButtonSelector } from './ParentDocumentSelector';
 library.add(faFile);
 
 @observer
@@ -401,6 +401,10 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
                 dragSpan.style.bottom = "6px";
                 dragSpan.style.paddingLeft = "4px";
                 dragSpan.style.paddingRight = "2px";
+                let gearSpan = document.createElement("span");
+                gearSpan.style.position = "relative";
+                gearSpan.style.paddingLeft = "0px";
+                gearSpan.style.paddingRight = "12px";
                 let upDiv = document.createElement("span");
                 const stack = tab.contentItem.parent;
                 // shifts the focus to this tab when another tab is dragged over it
@@ -421,12 +425,14 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
                             hideSource: false
                         });
                     }}><FontAwesomeIcon icon="file" size="lg" /></span>, dragSpan);
-                ReactDOM.render(<ParentDocSelector Document={doc} addDocTab={(doc, data, where) => {
-                    where === "onRight" ? CollectionDockingView.AddRightSplit(doc, dataDoc) : CollectionDockingView.Instance.AddTab(stack, doc, dataDoc);
-                    return true;
-                }} />, upDiv);
-                tab.reactComponents = [dragSpan, upDiv];
+                ReactDOM.render(<ButtonSelector Document={doc} Stack={stack} />, gearSpan);
+                // ReactDOM.render(<ParentDocSelector Document={doc} addDocTab={(doc, data, where) => {
+                //     where === "onRight" ? CollectionDockingView.AddRightSplit(doc, dataDoc) : CollectionDockingView.Instance.AddTab(stack, doc, dataDoc);
+                //     return true;
+                // }} />, upDiv);
+                tab.reactComponents = [dragSpan, gearSpan, upDiv];
                 tab.element.append(dragSpan);
+                tab.element.append(gearSpan);
                 tab.element.append(upDiv);
                 tab.reactionDisposer = reaction(() => [doc.title, Doc.IsBrushedDegree(doc)], () => {
                     tab.titleElement[0].textContent = doc.title, { fireImmediately: true };
diff --git a/src/client/views/collections/ParentDocumentSelector.scss b/src/client/views/collections/ParentDocumentSelector.scss
index 2dd3e49f2..6f71bcc79 100644
--- a/src/client/views/collections/ParentDocumentSelector.scss
+++ b/src/client/views/collections/ParentDocumentSelector.scss
@@ -1,5 +1,5 @@
 .PDS-flyout {
-    position: absolute;
+    position: relative;
     z-index: 9999;
     background-color: #eeeeee;
     box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
@@ -19,4 +19,13 @@
         border-right: 0px;
         border-left: 0px;
     }
+}
+.parentDocumentSelector-button {
+    pointer-events: all;
+}
+.buttonSelector {
+    position: absolute; 
+    display: inline-block; 
+    padding-left: 5px; 
+    padding-right: 5px;
 }
\ No newline at end of file
diff --git a/src/client/views/collections/ParentDocumentSelector.tsx b/src/client/views/collections/ParentDocumentSelector.tsx
index d8475a467..7f2913214 100644
--- a/src/client/views/collections/ParentDocumentSelector.tsx
+++ b/src/client/views/collections/ParentDocumentSelector.tsx
@@ -8,8 +8,15 @@ import { SearchUtil } from "../../util/SearchUtil";
 import { CollectionDockingView } from "./CollectionDockingView";
 import { NumCast } from "../../../new_fields/Types";
 import { CollectionViewType } from "./CollectionBaseView";
+import { DocumentButtonBar } from "../DocumentButtonBar";
+import { DocumentManager } from "../../util/DocumentManager";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { faEdit } from "@fortawesome/free-solid-svg-icons";
+import { library } from "@fortawesome/fontawesome-svg-core";
 
-type SelectorProps = { Document: Doc, addDocTab(doc: Doc, dataDoc: Doc | undefined, location: string): void };
+library.add(faEdit);
+
+type SelectorProps = { Document: Doc, Stack?: any, addDocTab(doc: Doc, dataDoc: Doc | undefined, location: string): void };
 @observer
 export class SelectorContextMenu extends React.Component<SelectorProps> {
     @observable private _docs: { col: Doc, target: Doc }[] = [];
@@ -83,7 +90,7 @@ export class ParentDocSelector extends React.Component<SelectorProps> {
             );
         }
         return (
-            <span style={{ position: "relative", display: "inline-block", paddingLeft: "5px", paddingRight: "5px" }}
+            <span className="parentDocumentSelector-button" style={{ position: "relative", display: "inline-block", paddingLeft: "5px", paddingRight: "5px" }}
                 onMouseEnter={this.onMouseEnter}
                 onMouseLeave={this.onMouseLeave}>
                 <p>^</p>
@@ -92,3 +99,38 @@ export class ParentDocSelector extends React.Component<SelectorProps> {
         );
     }
 }
+
+@observer
+export class ButtonSelector extends React.Component<{ Document: Doc, Stack: any }> {
+    @observable hover = false;
+
+    @action
+    onMouseLeave = () => {
+        this.hover = false;
+    }
+
+    @action
+    onMouseEnter = () => {
+        this.hover = true;
+    }
+
+    render() {
+        let flyout;
+        if (this.hover) {
+            let view = DocumentManager.Instance.getDocumentView(this.props.Document);
+            flyout = !view ? (null) : (
+                <div className="PDS-flyout" title=" " onMouseLeave={this.onMouseLeave}>
+                    <DocumentButtonBar views={[view]} stack={this.props.Stack} />
+                </div>
+            );
+        }
+        return (
+            <span className="buttonSelector"
+                onMouseEnter={this.onMouseEnter}
+                onMouseLeave={this.onMouseLeave}>
+                {this.hover ? (null) : <FontAwesomeIcon icon={faEdit} size={"sm"} />}
+                {flyout}
+            </span>
+        );
+    }
+}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 7f3ebe026..584b5d024 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -349,7 +349,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             e.stopPropagation();
             // const docs = await SearchUtil.Search(`data_l:"${destDoc[Id]}"`, true);
             // const views = docs.map(d => DocumentManager.Instance.getDocumentView(d)).filter(d => d).map(d => d as DocumentView);
-            de.data.linkDocument = DocUtils.MakeLink(de.data.linkSourceDocument, this.props.Document, this.props.ContainingCollectionDoc);
+            de.data.linkSourceDocument !== this.props.Document &&
+                (de.data.linkDocument = DocUtils.MakeLink(de.data.linkSourceDocument, this.props.Document, this.props.ContainingCollectionDoc));
         }
     }
 
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index ab0412c37..031929bbd 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -39,6 +39,7 @@ import { ReplaceStep } from 'prosemirror-transform';
 import { DocumentType } from '../../documents/DocumentTypes';
 import { formattedTextBoxCommentPlugin, FormattedTextBoxComment } from './FormattedTextBoxComment';
 import { inputRules } from 'prosemirror-inputrules';
+import { DocumentButtonBar } from '../DocumentButtonBar';
 
 library.add(faEdit);
 library.add(faSmile, faTextHeight, faUpload);
@@ -429,8 +430,8 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
         this._pullReactionDisposer = reaction(
             () => this.props.Document[Pulls],
             () => {
-                if (!DocumentDecorations.hasPulledHack) {
-                    DocumentDecorations.hasPulledHack = true;
+                if (!DocumentButtonBar.hasPulledHack) {
+                    DocumentButtonBar.hasPulledHack = true;
                     let unchanged = this.dataDoc.unchanged;
                     this.pullFromGoogleDoc(unchanged ? this.checkState : this.updateState);
                 }
@@ -440,8 +441,8 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
         this._pushReactionDisposer = reaction(
             () => this.props.Document[Pushes],
             () => {
-                if (!DocumentDecorations.hasPushedHack) {
-                    DocumentDecorations.hasPushedHack = true;
+                if (!DocumentButtonBar.hasPushedHack) {
+                    DocumentButtonBar.hasPushedHack = true;
                     this.pushToGoogleDoc();
                 }
             }
@@ -534,7 +535,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
                     response && (this.dataDoc[GoogleRef] = response.documentId);
                     let pushSuccess = response !== undefined && !("errors" in response);
                     dataDoc.unchanged = pushSuccess;
-                    DocumentDecorations.Instance.startPushOutcome(pushSuccess);
+                    DocumentButtonBar.Instance.startPushOutcome(pushSuccess);
                 }
             };
             let undo = () => {
@@ -579,7 +580,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
         } else {
             delete dataDoc[GoogleRef];
         }
-        DocumentDecorations.Instance.startPullOutcome(pullSuccess);
+        DocumentButtonBar.Instance.startPullOutcome(pullSuccess);
     }
 
     checkState = (exportState: GoogleApiClientUtils.ReadResult, dataDoc: Doc) => {
@@ -592,7 +593,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
                 let receivedTitle = exportState.title;
                 let unchanged = storedPlainText === receivedPlainText && storedTitle === receivedTitle;
                 dataDoc.unchanged = unchanged;
-                DocumentDecorations.Instance.setPullState(unchanged);
+                DocumentButtonBar.Instance.setPullState(unchanged);
             }
         }
     }
-- 
cgit v1.2.3-70-g09d2


From 1d5dc3eb4095cea017412de9519b8eaee979c16c Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Sat, 21 Sep 2019 00:57:40 -0400
Subject: ?? what's up with pick correlation .. this fixes it, but why?

---
 src/client/views/DocumentDecorations.tsx | 33 ++++++++++++++++++++------------
 1 file changed, 21 insertions(+), 12 deletions(-)

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

diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index cbf812311..7ec316bf9 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -393,10 +393,14 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
         document.removeEventListener("pointerup", this.onRadiusUp);
     }
 
+    _lastX = 0;
+    _lastY = 0;
     @action
     onPointerDown = (e: React.PointerEvent): void => {
         e.stopPropagation();
         if (e.button === 0) {
+            this._lastX = e.clientX;
+            this._lastY = e.clientY;
             this._isPointerDown = true;
             this._resizing = e.currentTarget.id;
             this.Interacting = true;
@@ -453,42 +457,47 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
 
         let dX = 0, dY = 0, dW = 0, dH = 0;
 
+        let moveX = e.clientX - this._lastX; // e.movementX;
+        let moveY = e.clientY - this._lastY; // e.movementY;
+        this._lastX = e.clientX;
+        this._lastY = e.clientY;
+
         switch (this._resizing) {
             case "":
                 break;
             case "documentDecorations-topLeftResizer":
                 dX = -1;
                 dY = -1;
-                dW = -(e.movementX);
-                dH = -(e.movementY);
+                dW = -moveX;
+                dH = -moveY;
                 break;
             case "documentDecorations-topRightResizer":
-                dW = e.movementX;
+                dW = moveX;
                 dY = -1;
-                dH = -(e.movementY);
+                dH = -moveY;
                 break;
             case "documentDecorations-topResizer":
                 dY = -1;
-                dH = -(e.movementY);
+                dH = -moveY;
                 break;
             case "documentDecorations-bottomLeftResizer":
                 dX = -1;
-                dW = -(e.movementX);
-                dH = e.movementY;
+                dW = -moveX;
+                dH = moveY;
                 break;
             case "documentDecorations-bottomRightResizer":
-                dW = e.movementX;
-                dH = e.movementY;
+                dW = moveX;
+                dH = moveY;
                 break;
             case "documentDecorations-bottomResizer":
-                dH = e.movementY;
+                dH = moveY;
                 break;
             case "documentDecorations-leftResizer":
                 dX = -1;
-                dW = -(e.movementX);
+                dW = -moveX;
                 break;
             case "documentDecorations-rightResizer":
-                dW = e.movementX;
+                dW = moveX;
                 break;
         }
 
-- 
cgit v1.2.3-70-g09d2

