From a709e21384cef80a85eac9220739c854a96d5313 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Tue, 10 Sep 2019 20:15:40 -0400
Subject: fixed several search issues with text boxes and highlighting.

---
 src/client/views/nodes/DocumentView.tsx | 16 ++++++++++------
 1 file changed, 10 insertions(+), 6 deletions(-)

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

diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 633455f63..1e755f121 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -644,12 +644,16 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         cm.addItem({ description: "Add Repl", icon: "laptop-code", event: () => OverlayView.Instance.addWindow(<ScriptingRepl />, { x: 300, y: 100, width: 200, height: 200, title: "Scripting REPL" }) });
         cm.addItem({ description: "Move To Overlay", icon: "laptop-code", event: () => ((o: Doc) => o && Doc.AddDocToList(o, "data", this.props.Document))(Cast(CurrentUserUtils.UserDocument.overlays, Doc) as Doc) });
         cm.addItem({
-            description: "Download document", icon: "download", event: () => {
-                const a = document.createElement("a");
-                const url = Utils.prepend(`/downloadId/${this.props.Document[Id]}`);
-                a.href = url;
-                a.download = `DocExport-${this.props.Document[Id]}.zip`;
-                a.click();
+            description: "Download document", icon: "download", event: async () => {
+                let y = JSON.parse(await rp.get(Utils.CorsProxy("http://localhost:8983/solr/dash/select"), {
+                    qs: { q: 'world', fq: 'NOT baseProto_b:true AND NOT deleted:true', start: '0', rows: '100', hl: true, 'hl.fl': '*' }
+                }));
+                console.log(y);
+                // const a = document.createElement("a");
+                // const url = Utils.prepend(`/downloadId/${this.props.Document[Id]}`);
+                // a.href = url;
+                // a.download = `DocExport-${this.props.Document[Id]}.zip`;
+                // a.click();
             }
         });
 
-- 
cgit v1.2.3-70-g09d2


From 4df85ecd5026127c27b147b34398822307715e54 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Tue, 10 Sep 2019 21:36:42 -0400
Subject: fixed search highlighting

---
 .../views/collections/CollectionTreeView.tsx       |  1 +
 src/client/views/nodes/DocumentView.tsx            | 13 ++++++--
 src/client/views/search/SearchBox.tsx              | 38 +++++++++++-----------
 src/client/views/search/SearchItem.tsx             | 12 ++++---
 src/server/Search.ts                               |  3 --
 src/server/index.ts                                |  3 --
 6 files changed, 38 insertions(+), 32 deletions(-)

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

diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 50f03005c..e31fa0b40 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -373,6 +373,7 @@ class TreeView extends React.Component<TreeViewProps> {
                 style={{
                     color: this.props.document.isMinimized ? "red" : "black",
                     background: Doc.IsBrushed(this.props.document) ? "#06121212" : "0",
+                    fontWeight: this.props.document.search_string ? "bold" : undefined,
                     outline: BoolCast(this.props.document.workspaceBrush) ? "dashed 1px #06123232" : undefined,
                     pointerEvents: this.props.active() || SelectionManager.GetIsDragging() ? "all" : "none"
                 }} >
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 1e755f121..73426b3dc 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -780,6 +780,10 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         let fullDegree = Doc.isBrushedHighlightedDegree(this.props.Document);
         let borderRounding = StrCast(Doc.GetProto(this.props.Document).borderRounding);
         let localScale = this.props.ScreenToLocalTransform().Scale * fullDegree;
+        let searchHighlight = (!this.props.Document.search_fields ? (null) :
+            <div key="search" style={{ position: "absolute", background: "yellow", bottom: "-20px", borderRadius: "5px", transformOrigin: "bottom left", width: `${100 * this.props.ContentScaling()}%`, transform: `scale(${1 / this.props.ContentScaling()})` }}>
+                {StrCast(this.props.Document.search_fields)}
+            </div>);
         return (
             <div className={`documentView-node${this.topMost ? "-topmost" : ""}`}
                 ref={this._mainCont}
@@ -800,9 +804,13 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 onDrop={this.onDrop} onContextMenu={this.onContextMenu} onPointerDown={this.onPointerDown} onClick={this.onClick}
                 onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}
             >
-                {!showTitle && !showCaption ? this.contents :
+                {!showTitle && !showCaption ?
+                    this.props.Document.search_fields ? <div>
+                        {this.contents}
+                        {searchHighlight}
+                    </div> :
+                        this.contents :
                     <div style={{ position: "absolute", display: "inline-block", width: "100%", height: "100%", pointerEvents: "none" }}>
-
                         <div style={{ width: "100%", height: showTextTitle ? "calc(100% - 29px)" : "100%", display: "inline-block", position: "absolute", top: showTextTitle ? "29px" : undefined }}>
                             {this.contents}
                         </div>
@@ -828,6 +836,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                                 <FormattedTextBox {...this.props} onClick={this.onClickHandler} DataDoc={this.dataDoc} active={returnTrue} isSelected={this.isSelected} focus={emptyFunction} select={this.select} fieldExt={""} hideOnLeave={true} fieldKey={showCaption} />
                             </div>
                         }
+                        {searchHighlight}
                     </div>
                 }
             </div>
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index 2e29838e6..b30fd83e8 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -1,25 +1,23 @@
-import * as React from 'react';
-import { observer } from 'mobx-react';
-import { observable, action, runInAction, flow, computed } from 'mobx';
-import "./SearchBox.scss";
-import "./FilterBox.scss";
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { faTimes } from '@fortawesome/free-solid-svg-icons';
 import { library } from '@fortawesome/fontawesome-svg-core';
-import { SetupDrag } from '../../util/DragManager';
-import { Docs } from '../../documents/Documents';
-import { NumCast, Cast } from '../../../new_fields/Types';
-import { Doc } from '../../../new_fields/Doc';
-import { SearchItem } from './SearchItem';
+import { faTimes } from '@fortawesome/free-solid-svg-icons';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { action, computed, observable, runInAction } from 'mobx';
+import { observer } from 'mobx-react';
+import * as React from 'react';
 import * as rp from 'request-promise';
+import { Doc } from '../../../new_fields/Doc';
 import { Id } from '../../../new_fields/FieldSymbols';
-import { SearchUtil } from '../../util/SearchUtil';
+import { Cast, NumCast } from '../../../new_fields/Types';
 import { RouteStore } from '../../../server/RouteStore';
-import { FilterBox } from './FilterBox';
-import { ReadStream } from 'fs';
-import * as $ from 'jquery';
-import { MainView } from '../MainView';
 import { Utils } from '../../../Utils';
+import { Docs } from '../../documents/Documents';
+import { SetupDrag } from '../../util/DragManager';
+import { SearchUtil } from '../../util/SearchUtil';
+import { MainView } from '../MainView';
+import { FilterBox } from './FilterBox';
+import "./FilterBox.scss";
+import "./SearchBox.scss";
+import { SearchItem } from './SearchItem';
 
 library.add(faTimes);
 
@@ -304,14 +302,16 @@ export class SearchBox extends React.Component {
                         this.getResults(this._searchString);
                         if (i < this._results.length) result = this._results[i];
                         if (result) {
-                            this._visibleElements[i] = <SearchItem doc={result[0]} query={this._searchString} key={result[0][Id]} highlighting={result[1]} />;
+                            let highlights = Array.from([...Array.from(new Set(result[1]).values())]).filter(v => v !== "search_string");
+                            this._visibleElements[i] = <SearchItem doc={result[0]} query={this._searchString} key={result[0][Id]} highlighting={highlights} />;
                             this._isSearch[i] = "search";
                         }
                     }
                     else {
                         result = this._results[i];
                         if (result) {
-                            this._visibleElements[i] = <SearchItem doc={result[0]} query={this._searchString} key={result[0][Id]} highlighting={result[1]} />;
+                            let highlights = Array.from([...Array.from(new Set(result[1]).values())]).filter(v => v !== "search_string");
+                            this._visibleElements[i] = <SearchItem doc={result[0]} query={this._searchString} key={result[0][Id]} highlighting={highlights} />;
                             this._isSearch[i] = "search";
                         }
                     }
diff --git a/src/client/views/search/SearchItem.tsx b/src/client/views/search/SearchItem.tsx
index 0b722c086..30e0454f3 100644
--- a/src/client/views/search/SearchItem.tsx
+++ b/src/client/views/search/SearchItem.tsx
@@ -136,8 +136,13 @@ export class SearchItem extends React.Component<SearchItemProps> {
     @observable _useIcons = true;
     @observable _displayDim = 50;
 
+    componentDidMount() {
+        this.props.doc.search_string = this.props.query;
+        this.props.doc.search_fields = this.props.highlighting.join(", ");
+    }
     componentWillUnmount() {
         this.props.doc.search_string = undefined;
+        this.props.doc.search_fields = undefined;
     }
 
     //@computed
@@ -177,7 +182,6 @@ export class SearchItem extends React.Component<SearchItemProps> {
                     ContentScaling={scale}
                 />
             </div>;
-            this.props.doc.search_string = this.props.query;
             return docview;
         }
         let button = layoutresult.indexOf(DocumentType.PDF) !== -1 ? faFilePdf :
@@ -231,8 +235,7 @@ export class SearchItem extends React.Component<SearchItemProps> {
                 Doc.BrushDoc(doc2);
             }
         } else {
-            DocumentManager.Instance.getAllDocumentViews(this.props.doc).forEach(element =>
-                Doc.BrushDoc(element.props.Document));
+            Doc.BrushDoc(this.props.doc);
         }
     }
 
@@ -246,8 +249,7 @@ export class SearchItem extends React.Component<SearchItemProps> {
                 Doc.UnBrushDoc(doc2);
             }
         } else {
-            DocumentManager.Instance.getAllDocumentViews(this.props.doc).
-                forEach(element => Doc.UnBrushDoc(element.props.Document));
+            Doc.UnBrushDoc(this.props.doc);
         }
     }
 
diff --git a/src/server/Search.ts b/src/server/Search.ts
index 4a408405a..723dc101b 100644
--- a/src/server/Search.ts
+++ b/src/server/Search.ts
@@ -32,13 +32,10 @@ export class Search {
 
     public async search(query: any) {
         try {
-            console.log("SEARCH " + query + " " + (this.url + "dash/select") + " " + query.q);
-            console.log(query);
             const searchResults = JSON.parse(await rp.get(this.url + "dash/select", {
                 qs: query
             }));
             const { docs, numFound } = searchResults.response;
-            console.log("RESULTS " + numFound);
             const ids = docs.map((field: any) => field.id);
             return { ids, numFound, highlighting: searchResults.highlighting };
         } catch {
diff --git a/src/server/index.ts b/src/server/index.ts
index 149bacf0f..50ce2b14e 100644
--- a/src/server/index.ts
+++ b/src/server/index.ts
@@ -198,15 +198,12 @@ const solrURL = "http://localhost:8983/solr/#/dash";
 
 app.get("/search", async (req, res) => {
     const solrQuery: any = {};
-    console.log("GOT SEARCH");
     ["q", "fq", "start", "rows", "hl", "hl.fl"].forEach(key => solrQuery[key] = req.query[key]);
     if (solrQuery.q === undefined) {
         res.send([]);
         return;
     }
-    console.log("CALLING SEARCH")
     let results = await Search.Instance.search(solrQuery);
-    console.log("RETURNING SEARCH")
     res.send(results);
 });
 
-- 
cgit v1.2.3-70-g09d2


From f4df8cedd38dbf09e282315604ecaa6cde8185e5 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Wed, 11 Sep 2019 00:56:26 -0400
Subject: added start of parameterized button maker

---
 src/client/views/ContextMenuItem.tsx    |  4 ++--
 src/client/views/EditableView.tsx       |  2 +-
 src/client/views/ScriptBox.tsx          | 41 +++++++++++++++++++++++++++++++++
 src/client/views/nodes/ButtonBox.tsx    |  4 ++--
 src/client/views/nodes/DocumentView.tsx |  1 +
 5 files changed, 47 insertions(+), 5 deletions(-)

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

diff --git a/src/client/views/ContextMenuItem.tsx b/src/client/views/ContextMenuItem.tsx
index 0366a6a30..ac055be5b 100644
--- a/src/client/views/ContextMenuItem.tsx
+++ b/src/client/views/ContextMenuItem.tsx
@@ -10,7 +10,7 @@ library.add(faAngleRight);
 
 export interface OriginalMenuProps {
     description: string;
-    event: () => void;
+    event: (stuff?: any) => void;
     undoable?: boolean;
     icon: IconProp; //maybe should be optional (icon?)
     closeMenu?: () => void;
@@ -44,7 +44,7 @@ export class ContextMenuItem extends React.Component<ContextMenuProps & { select
             if (this.props.undoable !== false) {
                 batch = UndoManager.StartBatch(`Context menu event: ${this.props.description}`);
             }
-            await this.props.event();
+            await this.props.event({ x: e.clientX, y: e.clientY });
             batch && batch.end();
         }
     }
diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx
index dd5395802..1bdb26b3d 100644
--- a/src/client/views/EditableView.tsx
+++ b/src/client/views/EditableView.tsx
@@ -145,7 +145,7 @@ export class EditableView extends React.Component<EditableProps> {
             if (this.props.autosuggestProps) this.props.autosuggestProps.resetValue();
             return (
                 <div className={`editableView-container-editing${this.props.oneLine ? "-oneLine" : ""}`}
-                    style={{ display: this.props.display, height: "auto", maxHeight: `${this.props.height}` }}
+                    style={{ display: this.props.display, minHeight: "20px", height: "auto", maxHeight: `${this.props.height}` }}
                     onClick={this.onClick}>
                     <span style={{ fontStyle: this.props.fontStyle, fontSize: this.props.fontSize }}>{this.props.contents}</span>
                 </div>
diff --git a/src/client/views/ScriptBox.tsx b/src/client/views/ScriptBox.tsx
index 7afba5e01..375a5cc93 100644
--- a/src/client/views/ScriptBox.tsx
+++ b/src/client/views/ScriptBox.tsx
@@ -10,6 +10,8 @@ import { emptyFunction } from "../../Utils";
 import { ScriptCast } from "../../new_fields/Types";
 import { CompileScript } from "../util/Scripting";
 import { ScriptField } from "../../new_fields/ScriptField";
+import { DragManager } from "../util/DragManager";
+import { EditableView } from "./EditableView";
 
 export interface ScriptBoxProps {
     onSave: (text: string, onError: (error: string) => void) => void;
@@ -66,6 +68,45 @@ export class ScriptBox extends React.Component<ScriptBoxProps> {
             </div>
         );
     }
+    //let l = docList(this.source.data).length; if (l) { let ind = this.target.index !== undefined ? (this.target.index+1) % l : 0;  this.target.index = ind;  this.target.proto = getProto(docList(this.source.data)[ind]);}
+    public static EditButtonScript(doc: Doc, fieldKey: string, content: any, clientX: number, clientY: number) {
+        let overlayDisposer: () => void = emptyFunction;
+        const script = ScriptCast(doc[fieldKey]);
+        let originalText = script && script.script.originalScript;
+        // tslint:disable-next-line: no-unnecessary-callback-wrapper
+        let scriptingBox = <ScriptBox initialText={originalText} onCancel={() => overlayDisposer()} onSave={(text, onError) => {
+            const script = CompileScript(text, {
+                params: { this: Doc.name },
+                typecheck: false,
+                editable: true,
+                transformer: DocumentIconContainer.getTransformer()
+            });
+            if (!script.compiled) {
+                onError(script.errors.map(error => error.messageText).join("\n"));
+                return;
+            }
+
+            DragManager.StartButtonDrag([], text, "a script",
+                {}, this._params, (button: Doc) => { }, clientX, clientY);
+
+            doc[fieldKey] = new ScriptField(script);
+            overlayDisposer();
+        }} showDocumentIcons />;
+        let params = <EditableView
+            contents={""}
+            display={"block"}
+            height={72}
+            fontSize={12}
+            GetValue={() => ""}
+            SetValue={(value: string) => (this._params = value.split(" ").filter(s => s !== " ")) ? true : true}
+        />;
+        let box = <div style={{ display: "flex", flexDirection: "column", height: "100%" }}>
+            {scriptingBox}
+            {params}
+        </div>
+        overlayDisposer = OverlayView.Instance.addWindow(box, { x: 400, y: 200, width: 500, height: 400, title: `${doc.title || ""} OnClick` });
+    }
+    static _params: string[] = [];
     public static EditClickScript(doc: Doc, fieldKey: string, prewrapper?: string, postwrapper?: string) {
         let overlayDisposer: () => void = emptyFunction;
         const script = ScriptCast(doc[fieldKey]);
diff --git a/src/client/views/nodes/ButtonBox.tsx b/src/client/views/nodes/ButtonBox.tsx
index 54848344b..db4bb7972 100644
--- a/src/client/views/nodes/ButtonBox.tsx
+++ b/src/client/views/nodes/ButtonBox.tsx
@@ -44,8 +44,8 @@ export class ButtonBox extends DocComponent<FieldViewProps, ButtonDocument>(Butt
     @undoBatch
     @action
     drop = (e: Event, de: DragManager.DropEvent) => {
-        if (de.data instanceof DragManager.DocumentDragData) {
-            Doc.GetProto(this.dataDoc).source = new List<Doc>(de.data.droppedDocuments);
+        if (de.data instanceof DragManager.DocumentDragData && e.target) {
+            Doc.GetProto(this.dataDoc)[(e.target as any).textContent] = new List<Doc>(de.data.droppedDocuments);
             e.stopPropagation();
         }
     }
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 73426b3dc..84169cc93 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -601,6 +601,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         let makes: ContextMenuProps[] = existingMake && "subitems" in existingMake ? existingMake.subitems : [];
         makes.push({ description: this.props.Document.isBackground ? "Remove Background" : "Into Background", event: this.makeBackground, icon: this.props.Document.lockedPosition ? "unlock" : "lock" });
         makes.push({ description: this.props.Document.isButton ? "Remove Button" : "Into Button", event: this.makeBtnClicked, icon: "concierge-bell" });
+        makes.push({ description: "OnClick Button script", icon: "edit", event: (obj: any) => ScriptBox.EditButtonScript(this.props.Document, "onClick", this._mainCont, obj.x, obj.y) });
         makes.push({ description: "OnClick script", icon: "edit", event: () => ScriptBox.EditClickScript(this.props.Document, "onClick") });
         makes.push({ description: "OnClick foreach doc", icon: "edit", event: () => ScriptBox.EditClickScript(this.props.Document, "onClick", "docList(this.collectionContext.data).map(d => {", "});\n") });
         makes.push({
-- 
cgit v1.2.3-70-g09d2


From 71594c0e64d2559f2a72bcbc5faee1db78eecfb8 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Wed, 11 Sep 2019 11:19:21 -0400
Subject: added makeCustomView to promote documents to a new layout.

---
 src/client/views/DocumentDecorations.tsx |  2 +-
 src/client/views/ScriptBox.tsx           |  2 +-
 src/client/views/nodes/DocumentView.tsx  | 57 +++++++++++++++++++++-----------
 src/new_fields/Doc.ts                    | 12 ++++---
 4 files changed, 47 insertions(+), 26 deletions(-)

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

diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 773ab8b9f..fe409d9a6 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -283,7 +283,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
     onCloseUp = async (e: PointerEvent) => {
         e.stopPropagation();
         if (e.button === 0) {
-            const recent = await Cast(CurrentUserUtils.UserDocument.recentlyClosed, Doc);
+            const recent = Cast(CurrentUserUtils.UserDocument.recentlyClosed, Doc) as Doc;
             SelectionManager.SelectedDocuments().map(dv => {
                 recent && Doc.AddDocToList(recent, "data", dv.props.Document, undefined, true, true);
                 dv.props.removeDocument && dv.props.removeDocument(dv.props.Document);
diff --git a/src/client/views/ScriptBox.tsx b/src/client/views/ScriptBox.tsx
index 375a5cc93..1f3673390 100644
--- a/src/client/views/ScriptBox.tsx
+++ b/src/client/views/ScriptBox.tsx
@@ -68,7 +68,7 @@ export class ScriptBox extends React.Component<ScriptBoxProps> {
             </div>
         );
     }
-    //let l = docList(this.source.data).length; if (l) { let ind = this.target.index !== undefined ? (this.target.index+1) % l : 0;  this.target.index = ind;  this.target.proto = getProto(docList(this.source.data)[ind]);}
+    //let l = docList(this.source[0].data).length; if (l) { let ind = this.target[0].index !== undefined ? (this.target[0].index+1) % l : 0;  this.target[0].index = ind;  this.target[0].proto = getProto(docList(this.source[0].data)[ind]);}
     public static EditButtonScript(doc: Doc, fieldKey: string, content: any, clientX: number, clientY: number) {
         let overlayDisposer: () => void = emptyFunction;
         const script = ScriptCast(doc[fieldKey]);
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 84169cc93..ca8fb573f 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -444,6 +444,19 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         this.props.addDocTab(kvp, this.dataDoc, "onRight");
     }
 
+    @undoBatch
+    makeCustomViewClicked = (): void => {
+        let options = { title: "data", width: NumCast(this.props.Document.width), height: NumCast(this.props.Document.height) };
+        let fieldTemplate = this.props.Document.type === DocumentType.TEXT ? Docs.Create.TextDocument(options) : Docs.Create.ImageDocument("http://www.cs.brown.edu", options);
+
+        let docTemplate = Docs.Create.FreeformDocument([fieldTemplate], { title: StrCast(this.Document.title) + "layout", width: NumCast(this.props.Document.width) + 20, height: NumCast(this.props.Document.height) + 20 });
+        let metaKey = "data";
+        let proto = Doc.GetProto(docTemplate);
+        Doc.MakeTemplate(fieldTemplate, metaKey, proto, true);
+
+        Doc.ApplyTemplateTo(docTemplate, this.props.Document, undefined, true);
+    }
+
     @undoBatch
     makeBtnClicked = (): void => {
         let doc = Doc.GetProto(this.props.Document);
@@ -552,17 +565,30 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             proto.nativeHeight = this.props.PanelHeight();
         }
     }
+    @undoBatch
+    @action
+    makeIntoPortal = (): void => {
+        if (!DocListCast(this.props.Document.links).find(doc => {
+            if (Cast(doc.anchor2, Doc) instanceof Doc && (Cast(doc.anchor2, Doc) as Doc)!.title === this.props.Document.title + ".portal") return true;
+            return false;
+        })) {
+            let portal = Docs.Create.FreeformDocument([], { width: this.props.Document[WidthSym]() + 10, height: this.props.Document[HeightSym](), title: this.props.Document.title + ".portal" });
+            DocUtils.MakeLink(this.props.Document, portal, undefined, this.props.Document.title + ".portal");
+            Doc.GetProto(this.props.Document).isButton = true;
+        }
+    }
+
     @undoBatch
     @action
     makeBackground = (): void => {
-        this.props.Document.isBackground = !this.props.Document.isBackground;
-        this.props.Document.isBackground && this.props.bringToFront(this.props.Document, true);
+        this.layoutDoc.isBackground = !this.layoutDoc.isBackground;
+        this.layoutDoc.isBackground && this.props.bringToFront(this.layoutDoc, true);
     }
 
     @undoBatch
     @action
     toggleLockPosition = (): void => {
-        this.props.Document.lockedPosition = BoolCast(this.props.Document.lockedPosition) ? undefined : true;
+        this.layoutDoc.lockedPosition = BoolCast(this.layoutDoc.lockedPosition) ? undefined : true;
     }
 
     listen = async () => {
@@ -601,30 +627,21 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         let makes: ContextMenuProps[] = existingMake && "subitems" in existingMake ? existingMake.subitems : [];
         makes.push({ description: this.props.Document.isBackground ? "Remove Background" : "Into Background", event: this.makeBackground, icon: this.props.Document.lockedPosition ? "unlock" : "lock" });
         makes.push({ description: this.props.Document.isButton ? "Remove Button" : "Into Button", event: this.makeBtnClicked, icon: "concierge-bell" });
+        makes.push({ description: "Custom View", event: this.makeCustomViewClicked, icon: "concierge-bell" });
+        makes.push({ description: "Custom Field", event: () => this.props.ContainingCollectionView && Doc.MakeTemplate(this.props.Document, StrCast(this.props.Document.title), this.props.ContainingCollectionView.props.Document, true), icon: "concierge-bell" })
         makes.push({ description: "OnClick Button script", icon: "edit", event: (obj: any) => ScriptBox.EditButtonScript(this.props.Document, "onClick", this._mainCont, obj.x, obj.y) });
         makes.push({ description: "OnClick script", icon: "edit", event: () => ScriptBox.EditClickScript(this.props.Document, "onClick") });
         makes.push({ description: "OnClick foreach doc", icon: "edit", event: () => ScriptBox.EditClickScript(this.props.Document, "onClick", "docList(this.collectionContext.data).map(d => {", "});\n") });
-        makes.push({
-            description: "Into Portal", event: () => {
-                if (!DocListCast(this.props.Document.links).find(doc => {
-                    if (Cast(doc.anchor2, Doc) instanceof Doc && (Cast(doc.anchor2, Doc) as Doc)!.title === this.props.Document.title + ".portal") return true;
-                    return false;
-                })) {
-                    let portal = Docs.Create.FreeformDocument([], { width: this.props.Document[WidthSym]() + 10, height: this.props.Document[HeightSym](), title: this.props.Document.title + ".portal" });
-                    DocUtils.MakeLink(this.props.Document, portal, undefined, this.props.Document.title + ".portal");
-                    Doc.GetProto(this.props.Document).isButton = true;
-                }
-            }, icon: "window-restore"
-        });
-        makes.push({ description: this.props.Document.ignoreClick ? "Selectable" : "Unselectable", event: () => this.props.Document.ignoreClick = !this.props.Document.ignoreClick, icon: this.props.Document.ignoreClick ? "unlock" : "lock" });
+        makes.push({ description: "Into Portal", event: this.makeIntoPortal, icon: "window-restore" });
+        makes.push({ description: this.layoutDoc.ignoreClick ? "Selectable" : "Unselectable", event: () => this.layoutDoc.ignoreClick = !this.layoutDoc.ignoreClick, icon: this.layoutDoc.ignoreClick ? "unlock" : "lock" });
         !existingMake && cm.addItem({ description: "Make...", subitems: makes, icon: "hand-point-right" });
         let existing = ContextMenu.Instance.findByDescription("Layout...");
         let layoutItems: ContextMenuProps[] = existing && "subitems" in existing ? existing.subitems : [];
 
-        layoutItems.push({ description: `${this.props.Document.chromeStatus !== "disabled" ? "Hide" : "Show"} Chrome`, event: () => this.props.Document.chromeStatus = (this.props.Document.chromeStatus !== "disabled" ? "disabled" : "enabled"), icon: "project-diagram" });
-        layoutItems.push({ description: `${this.props.Document.autoHeight ? "Variable Height" : "Auto Height"}`, event: () => this.props.Document.autoHeight = !this.props.Document.autoHeight, icon: "plus" });
-        layoutItems.push({ description: BoolCast(this.props.Document.ignoreAspect, false) || !this.props.Document.nativeWidth || !this.props.Document.nativeHeight ? "Freeze" : "Unfreeze", event: this.freezeNativeDimensions, icon: "snowflake" });
-        layoutItems.push({ description: BoolCast(this.props.Document.lockedPosition) ? "Unlock Position" : "Lock Position", event: this.toggleLockPosition, icon: BoolCast(this.props.Document.lockedPosition) ? "unlock" : "lock" });
+        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.props.Document.ignoreAspect || !this.props.Document.nativeWidth || !this.props.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: "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.props.Document.isTemplate) {
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index ccb8f4aa2..d4b784cac 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -459,7 +459,7 @@ export namespace Doc {
         }
         if (expandedTemplateLayout === undefined) {
             setTimeout(() => dataDoc[expandedLayoutFieldKey] === undefined &&
-                (dataDoc[expandedLayoutFieldKey] = Doc.MakeDelegate(templateLayoutDoc, undefined, "[" + templateLayoutDoc.title + "]")), 0);
+                (dataDoc[expandedLayoutFieldKey] = !BoolCast(templateLayoutDoc.suppressTemplateInstance) ? Doc.MakeDelegate(templateLayoutDoc, undefined, "[" + templateLayoutDoc.title + "]") : templateLayoutDoc), 0);
         }
         return undefined; // use the templateLayout when it's not a template or the expandedTemplate is pending.
     }
@@ -528,7 +528,7 @@ export namespace Doc {
         !templateDoc.nativeWidth && (otherdoc.ignoreAspect = true);
         return otherdoc;
     }
-    export function ApplyTemplateTo(templateDoc: Doc, target: Doc, targetData?: Doc) {
+    export function ApplyTemplateTo(templateDoc: Doc, target: Doc, targetData?: Doc, useTemplateDoc?: boolean) {
         if (!templateDoc) {
             target.layout = undefined;
             target.nativeWidth = undefined;
@@ -537,7 +537,7 @@ export namespace Doc {
             target.type = undefined;
             return;
         }
-        let temp = Doc.MakeDelegate(templateDoc);
+        let temp = useTemplateDoc ? templateDoc : Doc.MakeDelegate(templateDoc);
         target.nativeWidth = Doc.GetProto(target).nativeWidth = undefined;
         target.nativeHeight = Doc.GetProto(target).nativeHeight = undefined;
         !templateDoc.nativeWidth && (target.nativeWidth = 0);
@@ -558,7 +558,7 @@ export namespace Doc {
         }
     }
 
-    export function MakeTemplate(fieldTemplate: Doc, metaKey: string, templateDataDoc: Doc) {
+    export function MakeTemplate(fieldTemplate: Doc, metaKey: string, templateDataDoc: Doc, suppressTemplateFlag?: boolean) {
         // move data doc fields to layout doc as needed (nativeWidth/nativeHeight, data, ??)
         let backgroundLayout = StrCast(fieldTemplate.backgroundLayout);
         let fieldLayoutDoc = fieldTemplate;
@@ -576,6 +576,7 @@ export namespace Doc {
         fieldTemplate.templateField = metaKey;
         fieldTemplate.title = metaKey;
         fieldTemplate.isTemplate = true;
+        fieldTemplate.suppressTemplateInstance = suppressTemplateFlag;
         fieldTemplate.layout = layoutDelegate !== fieldTemplate ? layoutDelegate : layout;
         fieldTemplate.backgroundLayout = backgroundLayout;
         /* move certain layout properties from the original data doc to the template layout to avoid
@@ -585,6 +586,9 @@ export namespace Doc {
         fieldTemplate.singleColumn = BoolCast(fieldTemplate.singleColumn);
         fieldTemplate.nativeWidth = Cast(fieldTemplate.nativeWidth, "number");
         fieldTemplate.nativeHeight = Cast(fieldTemplate.nativeHeight, "number");
+        fieldTemplate.panX = 0;
+        fieldTemplate.panY = 0;
+        fieldTemplate.scale = 1;
         fieldTemplate.showTitle = "title";
         setTimeout(() => fieldTemplate.proto = templateDataDoc);
     }
-- 
cgit v1.2.3-70-g09d2


From d7d73856ed405cef01b4cf727ca56f4d9a31e894 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Wed, 11 Sep 2019 15:45:37 -0400
Subject: cleaned up ScriptBox EditScript's  restructured menus for dealing
 with onClicks...  exposed more layout paramters for EditableViews.

---
 src/client/views/ContextMenuItem.tsx               |  4 +-
 src/client/views/EditableView.tsx                  |  5 +-
 src/client/views/ScriptBox.tsx                     | 67 +++++++---------------
 .../views/collections/CollectionSchemaCells.tsx    |  3 +-
 .../views/collections/CollectionStackingView.tsx   |  6 +-
 .../views/collections/CollectionTreeView.tsx       |  5 +-
 src/client/views/nodes/ButtonBox.tsx               | 19 +++++-
 src/client/views/nodes/DocumentView.tsx            | 42 ++++++++------
 src/client/views/nodes/KeyValuePair.tsx            |  3 +-
 9 files changed, 82 insertions(+), 72 deletions(-)

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

diff --git a/src/client/views/ContextMenuItem.tsx b/src/client/views/ContextMenuItem.tsx
index ac055be5b..5f673b3f3 100644
--- a/src/client/views/ContextMenuItem.tsx
+++ b/src/client/views/ContextMenuItem.tsx
@@ -93,13 +93,13 @@ export class ContextMenuItem extends React.Component<ContextMenuProps & { select
                     {this._items.map(prop => <ContextMenuItem {...prop} key={prop.description} closeMenu={this.props.closeMenu} />)}
                 </div>;
             return (
-                <div className={"contextMenu-item" + (this.props.selected ? " contextMenu-itemSelected" : "")} onMouseEnter={this.onPointerEnter} onMouseLeave={this.onPointerLeave}>
+                <div className={"contextMenu-item" + (this.props.selected ? " contextMenu-itemSelected" : "")} onMouseLeave={this.onPointerLeave}>
                     {this.props.icon ? (
                         <span className="icon-background">
                             <FontAwesomeIcon icon={this.props.icon} size="sm" />
                         </span>
                     ) : null}
-                    <div className="contextMenu-description">
+                    <div className="contextMenu-description" onMouseEnter={this.onPointerEnter} >
                         {this.props.description}
                         <FontAwesomeIcon icon={faAngleRight} size="lg" style={{ position: "absolute", right: "10px" }} />
                     </div>
diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx
index 1bdb26b3d..e9db4b048 100644
--- a/src/client/views/EditableView.tsx
+++ b/src/client/views/EditableView.tsx
@@ -28,7 +28,8 @@ export interface EditableProps {
     contents: any;
     fontStyle?: string;
     fontSize?: number;
-    height?: number;
+    height?: number | "auto";
+    maxHeight?: number;
     display?: string;
     autosuggestProps?: {
         resetValue: () => void;
@@ -145,7 +146,7 @@ export class EditableView extends React.Component<EditableProps> {
             if (this.props.autosuggestProps) this.props.autosuggestProps.resetValue();
             return (
                 <div className={`editableView-container-editing${this.props.oneLine ? "-oneLine" : ""}`}
-                    style={{ display: this.props.display, minHeight: "20px", height: "auto", maxHeight: `${this.props.height}` }}
+                    style={{ display: this.props.display, minHeight: "20px", height: `${this.props.height ? this.props.height : "auto"}`, maxHeight: `${this.props.maxHeight}` }}
                     onClick={this.onClick}>
                     <span style={{ fontStyle: this.props.fontStyle, fontSize: this.props.fontSize }}>{this.props.contents}</span>
                 </div>
diff --git a/src/client/views/ScriptBox.tsx b/src/client/views/ScriptBox.tsx
index 1f3673390..8f08224c8 100644
--- a/src/client/views/ScriptBox.tsx
+++ b/src/client/views/ScriptBox.tsx
@@ -18,6 +18,7 @@ export interface ScriptBoxProps {
     onCancel?: () => void;
     initialText?: string;
     showDocumentIcons?: boolean;
+    setParams?: (p: string[]) => void;
 }
 
 @observer
@@ -58,56 +59,30 @@ export class ScriptBox extends React.Component<ScriptBoxProps> {
             onFocus = this.onFocus;
             onBlur = this.onBlur;
         }
+        let params = <EditableView
+            contents={""}
+            display={"block"}
+            maxHeight={72}
+            height={35}
+            fontSize={28}
+            GetValue={() => ""}
+            SetValue={(value: string) => this.props.setParams && this.props.setParams(value.split(" ").filter(s => s !== " ")) ? true : true}
+        />;
         return (
             <div className="scriptBox-outerDiv">
+                <div style={{ display: "flex", flexDirection: "column", height: "100%" }}>
+                    <textarea className="scriptBox-textarea" onChange={this.onChange} value={this._scriptText} onFocus={onFocus} onBlur={onBlur}></textarea>
+                    <div style={{ background: "beige" }} >{params}</div>
+                </div>
                 <div className="scriptBox-toolbar">
                     <button onClick={e => { this.props.onSave(this._scriptText, this.onError); e.stopPropagation(); }}>Save</button>
                     <button onClick={e => { this.props.onCancel && this.props.onCancel(); e.stopPropagation(); }}>Cancel</button>
                 </div>
-                <textarea className="scriptBox-textarea" onChange={this.onChange} value={this._scriptText} onFocus={onFocus} onBlur={onBlur}></textarea>
             </div>
         );
     }
     //let l = docList(this.source[0].data).length; if (l) { let ind = this.target[0].index !== undefined ? (this.target[0].index+1) % l : 0;  this.target[0].index = ind;  this.target[0].proto = getProto(docList(this.source[0].data)[ind]);}
-    public static EditButtonScript(doc: Doc, fieldKey: string, content: any, clientX: number, clientY: number) {
-        let overlayDisposer: () => void = emptyFunction;
-        const script = ScriptCast(doc[fieldKey]);
-        let originalText = script && script.script.originalScript;
-        // tslint:disable-next-line: no-unnecessary-callback-wrapper
-        let scriptingBox = <ScriptBox initialText={originalText} onCancel={() => overlayDisposer()} onSave={(text, onError) => {
-            const script = CompileScript(text, {
-                params: { this: Doc.name },
-                typecheck: false,
-                editable: true,
-                transformer: DocumentIconContainer.getTransformer()
-            });
-            if (!script.compiled) {
-                onError(script.errors.map(error => error.messageText).join("\n"));
-                return;
-            }
-
-            DragManager.StartButtonDrag([], text, "a script",
-                {}, this._params, (button: Doc) => { }, clientX, clientY);
-
-            doc[fieldKey] = new ScriptField(script);
-            overlayDisposer();
-        }} showDocumentIcons />;
-        let params = <EditableView
-            contents={""}
-            display={"block"}
-            height={72}
-            fontSize={12}
-            GetValue={() => ""}
-            SetValue={(value: string) => (this._params = value.split(" ").filter(s => s !== " ")) ? true : true}
-        />;
-        let box = <div style={{ display: "flex", flexDirection: "column", height: "100%" }}>
-            {scriptingBox}
-            {params}
-        </div>
-        overlayDisposer = OverlayView.Instance.addWindow(box, { x: 400, y: 200, width: 500, height: 400, title: `${doc.title || ""} OnClick` });
-    }
-    static _params: string[] = [];
-    public static EditClickScript(doc: Doc, fieldKey: string, prewrapper?: string, postwrapper?: string) {
+    public static EditButtonScript(title: string, doc: Doc, fieldKey: string, clientX: number, clientY: number, prewrapper?: string, postwrapper?: string) {
         let overlayDisposer: () => void = emptyFunction;
         const script = ScriptCast(doc[fieldKey]);
         let originalText: string | undefined = undefined;
@@ -121,10 +96,9 @@ export class ScriptBox extends React.Component<ScriptBoxProps> {
             }
         }
         // tslint:disable-next-line: no-unnecessary-callback-wrapper
-        let scriptingBox = <ScriptBox initialText={originalText} onCancel={() => overlayDisposer()} onSave={(text, onError) => {
-            if (prewrapper) {
-                text = prewrapper + text + (postwrapper ? postwrapper : "");
-            }
+        let params: string[] = [];
+        let setParams = (p: string[]) => params.splice(0, params.length, ...p);
+        let scriptingBox = <ScriptBox initialText={originalText} setParams={setParams} onCancel={() => overlayDisposer()} onSave={(text, onError) => {
             const script = CompileScript(text, {
                 params: { this: Doc.name },
                 typecheck: false,
@@ -135,9 +109,12 @@ export class ScriptBox extends React.Component<ScriptBoxProps> {
                 onError(script.errors.map(error => error.messageText).join("\n"));
                 return;
             }
+
+            params.length && DragManager.StartButtonDrag([], text, "a script", {}, params, (button: Doc) => { }, clientX, clientY);
+
             doc[fieldKey] = new ScriptField(script);
             overlayDisposer();
         }} showDocumentIcons />;
-        overlayDisposer = OverlayView.Instance.addWindow(scriptingBox, { x: 400, y: 200, width: 500, height: 400, title: `${doc.title || ""} OnClick` });
+        overlayDisposer = OverlayView.Instance.addWindow(scriptingBox, { x: 400, y: 200, width: 500, height: 400, title: title });
     }
 }
diff --git a/src/client/views/collections/CollectionSchemaCells.tsx b/src/client/views/collections/CollectionSchemaCells.tsx
index 9c26a08f0..c59107b53 100644
--- a/src/client/views/collections/CollectionSchemaCells.tsx
+++ b/src/client/views/collections/CollectionSchemaCells.tsx
@@ -214,7 +214,8 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
                             isEditingCallback={this.isEditingCallback}
                             display={"inline"}
                             contents={contents}
-                            height={Number(MAX_ROW_HEIGHT)}
+                            height={"auto"}
+                            maxHeight={Number(MAX_ROW_HEIGHT)}
                             GetValue={() => {
                                 let field = props.Document[props.fieldKey];
                                 if (Field.IsField(field)) {
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 654ff2279..91e10b0ac 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -362,8 +362,12 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
             subItems.push({ description: `${this.props.Document.fillColumn ? "Variable Size" : "Autosize"} Column`, event: () => this.props.Document.fillColumn = !this.props.Document.fillColumn, icon: "plus" });
             subItems.push({ description: `${this.props.Document.showTitles ? "Hide Titles" : "Show Titles"}`, event: () => this.props.Document.showTitles = !this.props.Document.showTitles ? "title" : "", icon: "plus" });
             subItems.push({ description: `${this.props.Document.showCaptions ? "Hide Captions" : "Show Captions"}`, event: () => this.props.Document.showCaptions = !this.props.Document.showCaptions ? "caption" : "", icon: "plus" });
-            subItems.push({ description: "Edit onChildClick script", icon: "edit", event: () => ScriptBox.EditClickScript(this.props.Document, "onChildClick") });
             ContextMenu.Instance.addItem({ description: "Stacking Options ...", subitems: subItems, icon: "eye" });
+
+            let existingOnClick = ContextMenu.Instance.findByDescription("OnClick...");
+            let onClicks: ContextMenuProps[] = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : [];
+            onClicks.push({ description: "Edit onChildClick script", icon: "edit", event: (obj: any) => ScriptBox.EditButtonScript("On Child Clicked...", this.props.Document, "onChildClick", obj.x, obj.y) });
+            !existingOnClick && ContextMenu.Instance.addItem({ description: "OnClick...", subitems: onClicks, icon: "hand-point-right" });
         }
     }
 
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index e31fa0b40..8539b3fcc 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -592,13 +592,14 @@ export class CollectionTreeView extends CollectionSubView(Document) {
             <div id="body" className="collectionTreeView-dropTarget"
                 style={{ overflow: "auto", background: StrCast(this.props.Document.backgroundColor, "lightgray") }}
                 onContextMenu={this.onContextMenu}
-                onWheel={(e: React.WheelEvent) => (e.target as any).scrollHeight > (e.target as any).clientHeight && e.stopPropagation()}
+                onWheel={(e: React.WheelEvent) => this._mainEle && this._mainEle.scrollHeight > this._mainEle.clientHeight && e.stopPropagation()}
                 onDrop={this.onTreeDrop}
                 ref={this.createTreeDropTarget}>
                 <EditableView
                     contents={this.resolvedDataDoc.title}
                     display={"block"}
-                    height={72}
+                    maxHeight={72}
+                    height={"auto"}
                     GetValue={() => StrCast(this.resolvedDataDoc.title)}
                     SetValue={undoBatch((value: string) => (Doc.GetProto(this.resolvedDataDoc).title = value) ? true : true)}
                     OnFillDown={undoBatch((value: string) => {
diff --git a/src/client/views/nodes/ButtonBox.tsx b/src/client/views/nodes/ButtonBox.tsx
index db4bb7972..68d3b8ae1 100644
--- a/src/client/views/nodes/ButtonBox.tsx
+++ b/src/client/views/nodes/ButtonBox.tsx
@@ -13,6 +13,8 @@ import { undoBatch } from '../../util/UndoManager';
 import { DocComponent } from '../DocComponent';
 import './ButtonBox.scss';
 import { FieldView, FieldViewProps } from './FieldView';
+import { ContextMenuProps } from '../ContextMenuItem';
+import { ContextMenu } from '../ContextMenu';
 
 
 library.add(faEdit as any);
@@ -41,11 +43,24 @@ export class ButtonBox extends DocComponent<FieldViewProps, ButtonDocument>(Butt
             this.dropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.drop.bind(this) } });
         }
     }
+
+    specificContextMenu = (e: React.MouseEvent): void => {
+        let funcs: ContextMenuProps[] = [];
+        funcs.push({
+            description: "Clear Script Params", event: () => {
+                let params = Cast(this.props.Document.buttonParams, listSpec("string"));
+                params && params.map(p => this.props.Document[p] = undefined)
+            }, icon: "trash"
+        });
+
+        ContextMenu.Instance.addItem({ description: "OnClick...", subitems: funcs, icon: "asterisk" });
+    }
+
     @undoBatch
     @action
     drop = (e: Event, de: DragManager.DropEvent) => {
         if (de.data instanceof DragManager.DocumentDragData && e.target) {
-            Doc.GetProto(this.dataDoc)[(e.target as any).textContent] = new List<Doc>(de.data.droppedDocuments);
+            this.props.Document[(e.target as any).textContent] = new List<Doc>(de.data.droppedDocuments);
             e.stopPropagation();
         }
     }
@@ -55,7 +70,7 @@ export class ButtonBox extends DocComponent<FieldViewProps, ButtonDocument>(Butt
         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 ... 
         return (
-            <div className="buttonBox-outerDiv" ref={this.createDropTarget} >
+            <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") }} >
                     <div className="buttonBox-mainButtonCenter">
                         {(this.Document.text || this.Document.title)}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index ca8fb573f..9c2cf5f01 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -446,10 +446,10 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
 
     @undoBatch
     makeCustomViewClicked = (): void => {
-        let options = { title: "data", width: NumCast(this.props.Document.width), height: NumCast(this.props.Document.height) };
+        let options = { title: "data", width: NumCast(this.props.Document.width), height: NumCast(this.props.Document.height) + 25, x: -NumCast(this.props.Document.width) / 2, y: -NumCast(this.props.Document.height) / 2, };
         let fieldTemplate = this.props.Document.type === DocumentType.TEXT ? Docs.Create.TextDocument(options) : Docs.Create.ImageDocument("http://www.cs.brown.edu", options);
 
-        let docTemplate = Docs.Create.FreeformDocument([fieldTemplate], { title: StrCast(this.Document.title) + "layout", width: NumCast(this.props.Document.width) + 20, height: NumCast(this.props.Document.height) + 20 });
+        let docTemplate = Docs.Create.FreeformDocument([fieldTemplate], { title: StrCast(this.Document.title) + "layout", width: NumCast(this.props.Document.width) + 20, height: Math.max(100, NumCast(this.props.Document.height) + 45) });
         let metaKey = "data";
         let proto = Doc.GetProto(docTemplate);
         Doc.MakeTemplate(fieldTemplate, metaKey, proto, true);
@@ -460,15 +460,21 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     @undoBatch
     makeBtnClicked = (): void => {
         let doc = Doc.GetProto(this.props.Document);
-        doc.isButton = !BoolCast(doc.isButton);
-        if (doc.isButton) {
-            if (!doc.nativeWidth) {
-                doc.nativeWidth = this.props.Document[WidthSym]();
-                doc.nativeHeight = this.props.Document[HeightSym]();
-            }
+        if (doc.isButton || doc.onClick) {
+            doc.isButton = false;
+            doc.onClick = undefined;
         } else {
-            doc.nativeWidth = doc.nativeHeight = undefined;
+            doc.isButton = true;
         }
+
+        // if (doc.isButton) {
+        //     if (!doc.nativeWidth) {
+        //         doc.nativeWidth = this.props.Document[WidthSym]();
+        //         doc.nativeHeight = this.props.Document[HeightSym]();
+        //     }
+        // } else {
+        //     doc.nativeWidth = doc.nativeHeight = undefined;
+        // }
     }
 
     @undoBatch
@@ -623,21 +629,25 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         subitems.push({ description: "Open Right Alias", event: () => this.props.addDocTab && this.props.addDocTab(Doc.MakeAlias(this.props.Document), this.dataDoc, "onRight"), icon: "caret-square-right" });
         subitems.push({ description: "Open Fields", event: this.fieldsClicked, icon: "layer-group" });
         cm.addItem({ description: "Open...", subitems: subitems, icon: "external-link-alt" });
+
         let existingMake = ContextMenu.Instance.findByDescription("Make...");
         let makes: ContextMenuProps[] = existingMake && "subitems" in existingMake ? existingMake.subitems : [];
         makes.push({ description: this.props.Document.isBackground ? "Remove Background" : "Into Background", event: this.makeBackground, icon: this.props.Document.lockedPosition ? "unlock" : "lock" });
-        makes.push({ description: this.props.Document.isButton ? "Remove Button" : "Into Button", event: this.makeBtnClicked, icon: "concierge-bell" });
-        makes.push({ description: "Custom View", event: this.makeCustomViewClicked, icon: "concierge-bell" });
-        makes.push({ description: "Custom Field", event: () => this.props.ContainingCollectionView && Doc.MakeTemplate(this.props.Document, StrCast(this.props.Document.title), this.props.ContainingCollectionView.props.Document, true), icon: "concierge-bell" })
-        makes.push({ description: "OnClick Button script", icon: "edit", event: (obj: any) => ScriptBox.EditButtonScript(this.props.Document, "onClick", this._mainCont, obj.x, obj.y) });
-        makes.push({ description: "OnClick script", icon: "edit", event: () => ScriptBox.EditClickScript(this.props.Document, "onClick") });
-        makes.push({ description: "OnClick foreach doc", icon: "edit", event: () => ScriptBox.EditClickScript(this.props.Document, "onClick", "docList(this.collectionContext.data).map(d => {", "});\n") });
+        makes.push({ description: "Custom Document View", event: this.makeCustomViewClicked, icon: "concierge-bell" });
+        makes.push({ description: "Metadata Field View", event: () => this.props.ContainingCollectionView && Doc.MakeTemplate(this.props.Document, StrCast(this.props.Document.title), this.props.ContainingCollectionView.props.Document, true), icon: "concierge-bell" })
         makes.push({ description: "Into Portal", event: this.makeIntoPortal, icon: "window-restore" });
         makes.push({ description: this.layoutDoc.ignoreClick ? "Selectable" : "Unselectable", event: () => this.layoutDoc.ignoreClick = !this.layoutDoc.ignoreClick, icon: this.layoutDoc.ignoreClick ? "unlock" : "lock" });
         !existingMake && cm.addItem({ description: "Make...", subitems: makes, icon: "hand-point-right" });
+
+        let existingOnClick = ContextMenu.Instance.findByDescription("OnClick...");
+        let onClicks: ContextMenuProps[] = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : [];
+        onClicks.push({ description: this.props.Document.isButton || this.props.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({ description: "Edit onClick Foreach Doc Script", icon: "edit", event: (obj: any) => ScriptBox.EditButtonScript("Foreach Collection Doc (d) => ", this.props.Document, "onClick", obj.x, obj.y, "docList(this.collectionContext.data).map(d => {", "});\n") });
+        !existingOnClick && cm.addItem({ description: "OnClick...", subitems: onClicks, icon: "hand-point-right" });
+
         let existing = ContextMenu.Instance.findByDescription("Layout...");
         let layoutItems: ContextMenuProps[] = existing && "subitems" in existing ? existing.subitems : [];
-
         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.props.Document.ignoreAspect || !this.props.Document.nativeWidth || !this.props.Document.nativeHeight ? "Freeze" : "Unfreeze", event: this.freezeNativeDimensions, icon: "snowflake" });
diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx
index 5afd4d834..a27dbd83d 100644
--- a/src/client/views/nodes/KeyValuePair.tsx
+++ b/src/client/views/nodes/KeyValuePair.tsx
@@ -112,7 +112,8 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
                     <div className="keyValuePair-td-value-container">
                         <EditableView
                             contents={contents}
-                            height={36}
+                            maxHeight={36}
+                            height={"auto"}
                             GetValue={() => {
                                 return Field.toKeyValueString(props.Document, props.fieldKey);
                             }}
-- 
cgit v1.2.3-70-g09d2


From 4722644d09a561e394bd72c92af5561a2020776e Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Wed, 11 Sep 2019 16:28:14 -0400
Subject: fixed collection iteration onClick script

---
 src/client/views/ScriptBox.tsx                      | 3 +++
 src/client/views/collections/CollectionBaseView.tsx | 3 ---
 src/client/views/nodes/DocumentView.tsx             | 7 ++++++-
 3 files changed, 9 insertions(+), 4 deletions(-)

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

diff --git a/src/client/views/ScriptBox.tsx b/src/client/views/ScriptBox.tsx
index 8f08224c8..8f06cf770 100644
--- a/src/client/views/ScriptBox.tsx
+++ b/src/client/views/ScriptBox.tsx
@@ -99,6 +99,9 @@ export class ScriptBox extends React.Component<ScriptBoxProps> {
         let params: string[] = [];
         let setParams = (p: string[]) => params.splice(0, params.length, ...p);
         let scriptingBox = <ScriptBox initialText={originalText} setParams={setParams} onCancel={() => overlayDisposer()} onSave={(text, onError) => {
+            if (prewrapper) {
+                text = prewrapper + text + (postwrapper ? postwrapper : "");
+            }
             const script = CompileScript(text, {
                 params: { this: Doc.name },
                 typecheck: false,
diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx
index bd8d56851..5829f0626 100644
--- a/src/client/views/collections/CollectionBaseView.tsx
+++ b/src/client/views/collections/CollectionBaseView.tsx
@@ -104,9 +104,6 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
         if (this.props.fieldExt) { // bcz: fieldExt !== undefined means this is an overlay layer
             Doc.GetProto(doc).annotationOn = this.props.Document;
         }
-        if (doc.type === DocumentType.BUTTON) {
-            doc.collectionContext = this.props.Document;  // used by docList() function in Doc.ts so that buttons can iterate over the documents in their collection
-        }
         allowDuplicates = true;
         let targetDataDoc = this.props.fieldExt || this.props.Document.isTemplate ? this.extensionDoc : this.props.Document;
         let targetField = (this.props.fieldExt || this.props.Document.isTemplate) && this.props.fieldExt ? this.props.fieldExt : this.props.fieldKey;
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 9c2cf5f01..940a66b36 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -643,7 +643,12 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         let onClicks: ContextMenuProps[] = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : [];
         onClicks.push({ description: this.props.Document.isButton || this.props.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({ description: "Edit onClick Foreach Doc Script", icon: "edit", event: (obj: any) => ScriptBox.EditButtonScript("Foreach Collection Doc (d) => ", this.props.Document, "onClick", obj.x, obj.y, "docList(this.collectionContext.data).map(d => {", "});\n") });
+        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;
+                ScriptBox.EditButtonScript("Foreach Collection Doc (d) => ", this.props.Document, "onClick", obj.x, obj.y, "docList(this.collectionContext.data).map(d => {", "});\n");
+            }
+        });
         !existingOnClick && cm.addItem({ description: "OnClick...", subitems: onClicks, icon: "hand-point-right" });
 
         let existing = ContextMenu.Instance.findByDescription("Layout...");
-- 
cgit v1.2.3-70-g09d2


From 186d7aed7b99b1373e99b51cfe0c88c8167c8290 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Wed, 11 Sep 2019 22:52:07 -0400
Subject: fixed some template issues specifically for self-templates.

---
 src/client/views/DocumentDecorations.tsx           | 22 +++++++++++++-------
 .../views/collections/CollectionBaseView.tsx       |  2 +-
 .../views/nodes/CollectionFreeFormDocumentView.tsx | 24 ++++++++++++++--------
 src/client/views/nodes/DocumentView.tsx            |  6 +++---
 src/client/views/nodes/FormattedTextBox.tsx        |  3 +--
 src/new_fields/Doc.ts                              |  5 ++---
 6 files changed, 37 insertions(+), 25 deletions(-)

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

diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index fe409d9a6..814d718be 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -30,6 +30,7 @@ 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';
 const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
@@ -145,13 +146,20 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
                 let fieldTemplateView = SelectionManager.SelectedDocuments()[0];
                 SelectionManager.DeselectAll();
                 let fieldTemplate = fieldTemplateView.props.Document;
-                let docTemplate = fieldTemplateView.props.ContainingCollectionView!.props.Document;
-                let metaKey = text.startsWith(">>") ? text.slice(2, text.length) : text.slice(1, text.length);
-                let proto = Doc.GetProto(docTemplate);
-                Doc.MakeTemplate(fieldTemplate, metaKey, proto);
-                if (text.startsWith(">>")) {
-                    proto.detailedLayout = proto.layout;
-                    proto.miniLayout = ImageBox.LayoutString(metaKey);
+                let containerView = fieldTemplateView.props.ContainingCollectionView;
+                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));
+                    }
+                    Doc.MakeTemplate(fieldTemplate, metaKey, proto);
+                    if (text.startsWith(">>")) {
+                        proto.detailedLayout = proto.layout;
+                        proto.miniLayout = ImageBox.LayoutString(metaKey);
+                    }
                 }
             }
             else {
diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx
index 5829f0626..b7036b3ff 100644
--- a/src/client/views/collections/CollectionBaseView.tsx
+++ b/src/client/views/collections/CollectionBaseView.tsx
@@ -127,7 +127,7 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
         let targetDataDoc = this.props.fieldExt || this.props.Document.isTemplate ? this.extensionDoc : this.props.Document;
         let targetField = (this.props.fieldExt || this.props.Document.isTemplate) && this.props.fieldExt ? this.props.fieldExt : this.props.fieldKey;
         let value = Cast(targetDataDoc[targetField], listSpec(Doc), []);
-        let index = value.reduce((p, v, i) => (v instanceof Doc && v[Id] === doc[Id]) ? i : p, -1);
+        let index = value.reduce((p, v, i) => (v instanceof Doc && Doc.AreProtosEqual(v, doc)) ? i : p, -1);
         PromiseValue(Cast(doc.annotationOn, Doc)).then(annotationOn =>
             annotationOn === this.dataDoc.Document && (doc.annotationOn = undefined));
 
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index eb7ab64f8..07dd1cae7 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -74,10 +74,10 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
     }
 
     borderRounding = () => {
-        let br = StrCast(this.props.Document.layout instanceof Doc ? this.props.Document.layout.borderRounding : this.props.Document.borderRounding);
+        let br = StrCast(this.layoutDoc.layout instanceof Doc ? this.layoutDoc.layout.borderRounding : this.props.Document.borderRounding);
         if (br.endsWith("%")) {
             let percent = Number(br.substr(0, br.length - 1)) / 100;
-            let nativeDim = Math.min(NumCast(this.props.Document.nativeWidth), NumCast(this.props.Document.nativeHeight));
+            let nativeDim = Math.min(NumCast(this.layoutDoc.nativeWidth), NumCast(this.layoutDoc.nativeHeight));
             let minDim = percent * (nativeDim ? nativeDim : Math.min(this.props.PanelWidth(), this.props.PanelHeight()));
             return minDim;
         }
@@ -89,6 +89,12 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
 
     clusterColorFunc = (doc: Doc) => this.clusterColor;
 
+    get layoutDoc() {
+        // 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 this.props.Document.layout instanceof Doc ? this.props.Document.layout : this.props.Document;
+    }
+
     render() {
         const hasPosition = this.props.x !== undefined || this.props.y !== undefined;
         return (
@@ -98,15 +104,15 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
                     position: "absolute",
                     backgroundColor: "transparent",
                     boxShadow:
-                        this.props.Document.opacity === 0 ? undefined :  // if it's not visible, then no shadow
-                            this.props.Document.z ? `#9c9396  ${StrCast(this.props.Document.boxShadow, "10px 10px 0.9vw")}` :  // if it's a floating doc, give it a big shadow
-                                this.clusterColor ? (
-                                    this.props.Document.isBackground ? `0px 0px 50px 50px ${this.clusterColor}` :  // if it's a background & has a cluster color, make the shadow spread really big
-                                        `${this.clusterColor} ${StrCast(this.props.Document.boxShadow, `0vw 0vw ${50 / this.props.ContentScaling()}px`)}`) :  // if it's just in a cluster, make the shadown roughly match the cluster border extent
-                                    undefined,
+                        this.layoutDoc.opacity === 0 ? undefined :  // if it's not visible, then no shadow
+                            this.layoutDoc.z ? `#9c9396  ${StrCast(this.layoutDoc.boxShadow, "10px 10px 0.9vw")}` :  // if it's a floating doc, give it a big shadow
+                                this.layoutDoc.isBackground ? `0px 0px 50px 50px ${this.clusterColor}` :  // if it's a background & has a cluster color, make the shadow spread really big
+                                    this.clusterColor ? (
+                                        `${this.clusterColor} ${StrCast(this.layoutDoc.boxShadow, `0vw 0vw ${50 / this.props.ContentScaling()}px`)}`) :  // if it's just in a cluster, make the shadown roughly match the cluster border extent
+                                        StrCast(this.layoutDoc.boxShadow, ""),
                     borderRadius: this.borderRounding(),
                     transform: this.transform,
-                    transition: hasPosition ? "transform 1s" : StrCast(this.props.Document.transition),
+                    transition: hasPosition ? "transform 1s" : StrCast(this.layoutDoc.transition),
                     width: this.width,
                     height: this.height,
                     zIndex: this.Document.zIndex || 0,
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 940a66b36..c2143bb82 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -452,9 +452,9 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         let docTemplate = Docs.Create.FreeformDocument([fieldTemplate], { title: StrCast(this.Document.title) + "layout", width: NumCast(this.props.Document.width) + 20, height: Math.max(100, NumCast(this.props.Document.height) + 45) });
         let metaKey = "data";
         let proto = Doc.GetProto(docTemplate);
-        Doc.MakeTemplate(fieldTemplate, metaKey, proto, true);
+        Doc.MakeTemplate(fieldTemplate, metaKey, proto);
 
-        Doc.ApplyTemplateTo(docTemplate, this.props.Document, undefined, true);
+        Doc.ApplyTemplateTo(docTemplate, this.props.Document, undefined, false);
     }
 
     @undoBatch
@@ -634,7 +634,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         let makes: ContextMenuProps[] = existingMake && "subitems" in existingMake ? existingMake.subitems : [];
         makes.push({ description: this.props.Document.isBackground ? "Remove Background" : "Into Background", event: this.makeBackground, icon: this.props.Document.lockedPosition ? "unlock" : "lock" });
         makes.push({ description: "Custom Document View", event: this.makeCustomViewClicked, icon: "concierge-bell" });
-        makes.push({ description: "Metadata Field View", event: () => this.props.ContainingCollectionView && Doc.MakeTemplate(this.props.Document, StrCast(this.props.Document.title), this.props.ContainingCollectionView.props.Document, true), icon: "concierge-bell" })
+        makes.push({ description: "Metadata Field View", event: () => this.props.ContainingCollectionView && Doc.MakeTemplate(this.props.Document, StrCast(this.props.Document.title), this.props.ContainingCollectionView.props.Document), icon: "concierge-bell" })
         makes.push({ description: "Into Portal", event: this.makeIntoPortal, icon: "window-restore" });
         makes.push({ description: this.layoutDoc.ignoreClick ? "Selectable" : "Unselectable", event: () => this.layoutDoc.ignoreClick = !this.layoutDoc.ignoreClick, icon: this.layoutDoc.ignoreClick ? "unlock" : "lock" });
         !existingMake && cm.addItem({ description: "Make...", subitems: makes, icon: "hand-point-right" });
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 194026a08..0ea36cdc2 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -289,8 +289,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
         } else if (de.data instanceof DragManager.DocumentDragData) {
             const draggedDoc = de.data.draggedDocuments.length && de.data.draggedDocuments[0];
             if (draggedDoc && draggedDoc.type === DocumentType.TEXT && StrCast(draggedDoc.layout) !== "") {
-                if (this.props.DataDoc) this.props.DataDoc.layout = draggedDoc;
-                else this.props.Document.layout = draggedDoc;
+                this.props.Document.layout = draggedDoc;
                 draggedDoc.isTemplate = true;
                 e.stopPropagation();
             }
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index d4b784cac..e94b9f1eb 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -459,7 +459,7 @@ export namespace Doc {
         }
         if (expandedTemplateLayout === undefined) {
             setTimeout(() => dataDoc[expandedLayoutFieldKey] === undefined &&
-                (dataDoc[expandedLayoutFieldKey] = !BoolCast(templateLayoutDoc.suppressTemplateInstance) ? Doc.MakeDelegate(templateLayoutDoc, undefined, "[" + templateLayoutDoc.title + "]") : templateLayoutDoc), 0);
+                (dataDoc[expandedLayoutFieldKey] = Doc.MakeDelegate(templateLayoutDoc, undefined, "[" + templateLayoutDoc.title + "]")), 0);
         }
         return undefined; // use the templateLayout when it's not a template or the expandedTemplate is pending.
     }
@@ -558,7 +558,7 @@ export namespace Doc {
         }
     }
 
-    export function MakeTemplate(fieldTemplate: Doc, metaKey: string, templateDataDoc: Doc, suppressTemplateFlag?: boolean) {
+    export function MakeTemplate(fieldTemplate: Doc, metaKey: string, templateDataDoc: Doc) {
         // move data doc fields to layout doc as needed (nativeWidth/nativeHeight, data, ??)
         let backgroundLayout = StrCast(fieldTemplate.backgroundLayout);
         let fieldLayoutDoc = fieldTemplate;
@@ -576,7 +576,6 @@ export namespace Doc {
         fieldTemplate.templateField = metaKey;
         fieldTemplate.title = metaKey;
         fieldTemplate.isTemplate = true;
-        fieldTemplate.suppressTemplateInstance = suppressTemplateFlag;
         fieldTemplate.layout = layoutDelegate !== fieldTemplate ? layoutDelegate : layout;
         fieldTemplate.backgroundLayout = backgroundLayout;
         /* move certain layout properties from the original data doc to the template layout to avoid
-- 
cgit v1.2.3-70-g09d2


From daeb624194c324600674198ebd9ec758383019d4 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Thu, 12 Sep 2019 08:46:33 -0400
Subject: added custom views for documents

---
 src/client/views/collections/CollectionView.tsx |  6 ++--
 src/client/views/nodes/DocumentView.tsx         | 45 +++++++++++++++++++------
 2 files changed, 38 insertions(+), 13 deletions(-)

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

diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 6182e82f4..a17899b8b 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -87,7 +87,8 @@ export class CollectionView extends React.Component<FieldViewProps> {
 
     onContextMenu = (e: React.MouseEvent): void => {
         if (!this.isAnnotationOverlay && !e.isPropagationStopped() && this.props.Document[Id] !== CurrentUserUtils.MainDocId) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7
-            let subItems: ContextMenuProps[] = [];
+            let existingVm = ContextMenu.Instance.findByDescription("View Modes...");
+            let subItems: ContextMenuProps[] = existingVm && "subitems" in existingVm ? existingVm.subitems : [];
             subItems.push({ description: "Freeform", event: () => { this.props.Document.viewType = CollectionViewType.Freeform; delete this.props.Document.usePivotLayout; }, icon: "signature" });
             if (CollectionBaseView.InSafeMode()) {
                 ContextMenu.Instance.addItem({ description: "Test Freeform", event: () => this.props.Document.viewType = CollectionViewType.Invalid, icon: "project-diagram" });
@@ -103,7 +104,8 @@ export class CollectionView extends React.Component<FieldViewProps> {
                     break;
                 }
             }
-            ContextMenu.Instance.addItem({ description: "View Modes...", subitems: subItems, icon: "eye" });
+            !existingVm && ContextMenu.Instance.addItem({ description: "View Modes...", subitems: subItems, icon: "eye" });
+
             let existing = ContextMenu.Instance.findByDescription("Layout...");
             let layoutItems: ContextMenuProps[] = existing && "subitems" in existing ? existing.subitems : [];
             layoutItems.push({ description: `${this.props.Document.forceActive ? "Select" : "Force"} Contents Active`, event: () => this.props.Document.forceActive = !this.props.Document.forceActive, icon: "project-diagram" });
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index c2143bb82..6960689b4 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -9,7 +9,7 @@ import { List } from "../../../new_fields/List";
 import { ObjectField } from "../../../new_fields/ObjectField";
 import { createSchema, listSpec, makeInterface } from "../../../new_fields/Schema";
 import { ScriptField } from '../../../new_fields/ScriptField';
-import { BoolCast, Cast, FieldValue, NumCast, StrCast } from "../../../new_fields/Types";
+import { BoolCast, Cast, FieldValue, NumCast, StrCast, PromiseValue } from "../../../new_fields/Types";
 import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils";
 import { RouteStore } from '../../../server/RouteStore';
 import { emptyFunction, returnTrue, Utils } from "../../../Utils";
@@ -444,17 +444,32 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         this.props.addDocTab(kvp, this.dataDoc, "onRight");
     }
 
+    @undoBatch
+    makeNativeViewClicked = (): void => {
+        this.props.Document.customLayout = this.props.Document.layout;
+        this.props.Document.layout = this.props.Document.nativeLayout;
+        this.props.Document.type = this.props.Document.nativeType;
+    }
     @undoBatch
     makeCustomViewClicked = (): void => {
-        let options = { title: "data", width: NumCast(this.props.Document.width), height: NumCast(this.props.Document.height) + 25, x: -NumCast(this.props.Document.width) / 2, y: -NumCast(this.props.Document.height) / 2, };
-        let fieldTemplate = this.props.Document.type === DocumentType.TEXT ? Docs.Create.TextDocument(options) : Docs.Create.ImageDocument("http://www.cs.brown.edu", options);
-
-        let docTemplate = Docs.Create.FreeformDocument([fieldTemplate], { title: StrCast(this.Document.title) + "layout", width: NumCast(this.props.Document.width) + 20, height: Math.max(100, NumCast(this.props.Document.height) + 45) });
-        let metaKey = "data";
-        let proto = Doc.GetProto(docTemplate);
-        Doc.MakeTemplate(fieldTemplate, metaKey, proto);
-
-        Doc.ApplyTemplateTo(docTemplate, this.props.Document, undefined, false);
+        this.props.Document.nativeLayout = this.props.Document.layout;
+        this.props.Document.nativeType = this.props.Document.type;
+        PromiseValue(this.props.Document.customLayout).then(custom => {
+            if (custom) {
+                this.props.Document.type = DocumentType.TEMPLATE;
+                this.props.Document.layout = custom;
+            } else {
+                let options = { title: "data", width: NumCast(this.props.Document.width), height: NumCast(this.props.Document.height) + 25, x: -NumCast(this.props.Document.width) / 2, y: -NumCast(this.props.Document.height) / 2, };
+                let fieldTemplate = this.props.Document.type === DocumentType.TEXT ? Docs.Create.TextDocument(options) : Docs.Create.ImageDocument("http://www.cs.brown.edu", options);
+
+                let docTemplate = Docs.Create.FreeformDocument([fieldTemplate], { title: StrCast(this.Document.title) + "layout", width: NumCast(this.props.Document.width) + 20, height: Math.max(100, NumCast(this.props.Document.height) + 45) });
+                let metaKey = "data";
+                let proto = Doc.GetProto(docTemplate);
+                Doc.MakeTemplate(fieldTemplate, metaKey, proto);
+
+                Doc.ApplyTemplateTo(docTemplate, this.props.Document, undefined, false);
+            }
+        });
     }
 
     @undoBatch
@@ -630,10 +645,18 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         subitems.push({ description: "Open Fields", event: this.fieldsClicked, icon: "layer-group" });
         cm.addItem({ description: "Open...", subitems: subitems, icon: "external-link-alt" });
 
+        let existingVm = ContextMenu.Instance.findByDescription("View Modes...");
+        let vms: ContextMenuProps[] = existingVm && "subitems" in existingVm ? existingVm.subitems : [];
+        if (this.props.Document.type !== DocumentType.COL && this.props.Document.type !== DocumentType.TEMPLATE) {
+            vms.push({ description: "Custom Document View", event: this.makeCustomViewClicked, icon: "concierge-bell" });
+        } else if (this.props.Document.nativeLayout) {
+            vms.push({ description: "Native Document View", event: this.makeNativeViewClicked, icon: "concierge-bell" });
+        }
+        !existingVm && cm.addItem({ description: "View Modes...", subitems: vms, icon: "eye" });
+
         let existingMake = ContextMenu.Instance.findByDescription("Make...");
         let makes: ContextMenuProps[] = existingMake && "subitems" in existingMake ? existingMake.subitems : [];
         makes.push({ description: this.props.Document.isBackground ? "Remove Background" : "Into Background", event: this.makeBackground, icon: this.props.Document.lockedPosition ? "unlock" : "lock" });
-        makes.push({ description: "Custom Document View", event: this.makeCustomViewClicked, icon: "concierge-bell" });
         makes.push({ description: "Metadata Field View", event: () => this.props.ContainingCollectionView && Doc.MakeTemplate(this.props.Document, StrCast(this.props.Document.title), this.props.ContainingCollectionView.props.Document), icon: "concierge-bell" })
         makes.push({ description: "Into Portal", event: this.makeIntoPortal, icon: "window-restore" });
         makes.push({ description: this.layoutDoc.ignoreClick ? "Selectable" : "Unselectable", event: () => this.layoutDoc.ignoreClick = !this.layoutDoc.ignoreClick, icon: this.layoutDoc.ignoreClick ? "unlock" : "lock" });
-- 
cgit v1.2.3-70-g09d2


From 91480dbd1b734795f514281ee0a2dac2442d6e84 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Thu, 12 Sep 2019 11:06:23 -0400
Subject: cleaned up menus.  simplified making custom views through template
 dropdown menu

---
 src/client/views/ContextMenu.scss                  |  4 +-
 src/client/views/ContextMenuItem.tsx               |  4 +-
 src/client/views/DocumentDecorations.scss          |  2 +-
 src/client/views/DocumentDecorations.tsx           | 18 +-----
 src/client/views/TemplateMenu.tsx                  | 70 ++++++++++++----------
 src/client/views/Templates.tsx                     | 38 +-----------
 src/client/views/collections/CollectionView.tsx    |  5 --
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 44 +++++++-------
 src/client/views/nodes/DocumentView.tsx            | 59 ++++--------------
 src/client/views/nodes/ImageBox.tsx                |  5 +-
 src/client/views/nodes/KeyValueBox.tsx             | 27 ++++-----
 11 files changed, 96 insertions(+), 180 deletions(-)

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

diff --git a/src/client/views/ContextMenu.scss b/src/client/views/ContextMenu.scss
index e2c0de8af..8f112de0c 100644
--- a/src/client/views/ContextMenu.scss
+++ b/src/client/views/ContextMenu.scss
@@ -124,7 +124,9 @@
 }
 
 .icon-background {
-    pointer-events: none;
+    pointer-events: all;
+    height:100%;
+    margin-top: 15px;
     background-color: transparent;
     width: 35px;
     text-align: center;
diff --git a/src/client/views/ContextMenuItem.tsx b/src/client/views/ContextMenuItem.tsx
index 5f673b3f3..330b94afa 100644
--- a/src/client/views/ContextMenuItem.tsx
+++ b/src/client/views/ContextMenuItem.tsx
@@ -93,9 +93,9 @@ export class ContextMenuItem extends React.Component<ContextMenuProps & { select
                     {this._items.map(prop => <ContextMenuItem {...prop} key={prop.description} closeMenu={this.props.closeMenu} />)}
                 </div>;
             return (
-                <div className={"contextMenu-item" + (this.props.selected ? " contextMenu-itemSelected" : "")} onMouseLeave={this.onPointerLeave}>
+                <div className={"contextMenu-item" + (this.props.selected ? " contextMenu-itemSelected" : "")} onMouseLeave={this.onPointerLeave} onMouseEnter={this.onPointerEnter}>
                     {this.props.icon ? (
-                        <span className="icon-background">
+                        <span className="icon-background" onMouseEnter={this.onPointerLeave}>
                             <FontAwesomeIcon icon={this.props.icon} size="sm" />
                         </span>
                     ) : null}
diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss
index ac8497bd0..4ab5d733f 100644
--- a/src/client/views/DocumentDecorations.scss
+++ b/src/client/views/DocumentDecorations.scss
@@ -257,7 +257,7 @@ $linkGap : 3px;
     padding: 2px 12px;
     list-style: none;
 
-    .templateToggle {
+    .templateToggle, .chromeToggle {
         text-align: left;
     }
 
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 814d718be..6451fdf5e 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -849,24 +849,8 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
 
         let templates: Map<Template, boolean> = new Map();
         Array.from(Object.values(Templates.TemplateList)).map(template => {
-            let sorted = SelectionManager.ViewsSortedVertically();
-            let docTemps = sorted.reduce((res: string[], doc: DocumentView, i) => {
-                let temps = doc.props.Document.templates;
-                if (temps instanceof List) {
-                    temps.map(temp => {
-                        if (temp !== Templates.Bullet.Layout || i === 0) {
-                            res.push(temp);
-                        }
-                    });
-                }
-                return res;
-            }, [] as string[]);
             let checked = false;
-            docTemps.forEach(temp => {
-                if (template.Layout === temp) {
-                    checked = true;
-                }
-            });
+            SelectionManager.SelectedDocuments().map(doc => checked = checked || (doc.props.Document["show" + template.Name] !== undefined));
             templates.set(template, checked);
         });
 
diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx
index 393e97a7e..0586b31e4 100644
--- a/src/client/views/TemplateMenu.tsx
+++ b/src/client/views/TemplateMenu.tsx
@@ -1,16 +1,14 @@
 import { action, observable } from "mobx";
 import { observer } from "mobx-react";
-import { Doc } from "../../new_fields/Doc";
-import { List } from "../../new_fields/List";
-import './DocumentDecorations.scss';
-import { DocumentView } from "./nodes/DocumentView";
-import { Template } from "./Templates";
-import React = require("react");
-import { undoBatch } from "../util/UndoManager";
+import { DocumentType } from "../documents/DocumentTypes";
 import { DocumentManager } from "../util/DocumentManager";
-import { NumCast } from "../../new_fields/Types";
 import { DragManager } from "../util/DragManager";
 import { SelectionManager } from "../util/SelectionManager";
+import { undoBatch } from "../util/UndoManager";
+import './DocumentDecorations.scss';
+import { DocumentView } from "./nodes/DocumentView";
+import { Template, Templates } from "./Templates";
+import React = require("react");
 const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
@@ -30,6 +28,17 @@ class TemplateToggle extends React.Component<{ template: Template, checked: bool
         }
     }
 }
+@observer
+class ChromeToggle extends React.Component<{ checked: boolean, toggle: (event: React.ChangeEvent<HTMLInputElement>) => void }> {
+    render() {
+        return (
+            <li className="chromeToggle">
+                <input type="checkbox" checked={this.props.checked} onChange={(event) => this.props.toggle(event)} />
+                Chrome
+            </li>
+        );
+    }
+}
 
 export interface TemplateMenuProps {
     docs: DocumentView[];
@@ -45,6 +54,16 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
         super(props);
     }
 
+    toggleCustom = (e: React.MouseEvent): void => {
+        this.props.docs.map(dv => {
+            if (dv.Document.type !== DocumentType.COL && dv.Document.type !== DocumentType.TEMPLATE) {
+                dv.makeCustomViewClicked();
+            } else if (dv.Document.nativeLayout) {
+                dv.makeNativeViewClicked();
+            }
+        });
+    }
+
     toggleFloat = (e: React.MouseEvent): void => {
         SelectionManager.DeselectAll();
         let topDocView = this.props.docs[0];
@@ -80,55 +99,42 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
     @action
     toggleTemplate = (event: React.ChangeEvent<HTMLInputElement>, template: Template): void => {
         if (event.target.checked) {
-            if (template.Name === "Bullet") {
-                let topDocView = this.props.docs[0];
-                topDocView.addTemplate(template);
-                topDocView.props.Document.subBulletDocs = new List<Doc>(this.props.docs.filter(v => v !== topDocView).map(v => v.props.Document));
-            } else {
-                this.props.docs.map(d => d.addTemplate(template));
-            }
-            this.props.templates.set(template, true);
+            this.props.docs.map(d => d.props.Document["show" + template.Name] = template.Name.toLowerCase());
         } else {
-            if (template.Name === "Bullet") {
-                let topDocView = this.props.docs[0];
-                topDocView.removeTemplate(template);
-                topDocView.props.Document.subBulletDocs = undefined;
-            } else {
-                this.props.docs.map(d => d.removeTemplate(template));
-            }
-            this.props.templates.set(template, false);
+            this.props.docs.map(d => d.props.Document["show" + template.Name] = undefined);
         }
     }
 
     @undoBatch
     @action
     clearTemplates = (event: React.MouseEvent) => {
-        this.props.docs.map(d => d.clearTemplates());
-        Array.from(this.props.templates.keys()).map(t => this.props.templates.set(t, false));
+        Templates.TemplateList.map(template => this.props.docs.map(d => d.props.Document["show" + template.Name] = false));
     }
 
     @action
-    componentWillReceiveProps(nextProps: TemplateMenuProps) {
-        // this._templates = nextProps.templates;
+    toggleTemplateActivity = (): void => {
+        this._hidden = !this._hidden;
     }
 
+    @undoBatch
     @action
-    toggleTemplateActivity = (): void => {
-        this._hidden = !this._hidden;
+    toggleChrome = (): void => {
+        this.props.docs.map(dv => dv.layoutDoc.chromeStatus = (dv.layoutDoc.chromeStatus !== "disabled" ? "disabled" : "enabled"));
     }
 
     render() {
         let templateMenu: Array<JSX.Element> = [];
         this.props.templates.forEach((checked, template) =>
             templateMenu.push(<TemplateToggle key={template.Name} template={template} checked={checked} toggle={this.toggleTemplate} />));
-
+        templateMenu.push(<ChromeToggle key={"chrome"} checked={this.props.docs[0].Document.chromeStatus !== "disabled"} toggle={this.toggleChrome} />);
         return (
             <div className="templating-menu" >
                 <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.toggleFloat}>Float</button>
-                    <button onClick={this.clearTemplates}>Clear</button>
+                    {/* <button onClick={this.clearTemplates}>Clear</button> */}
                 </ul>
             </div>
         );
diff --git a/src/client/views/Templates.tsx b/src/client/views/Templates.tsx
index 236704fa2..ef78b60d4 100644
--- a/src/client/views/Templates.tsx
+++ b/src/client/views/Templates.tsx
@@ -57,43 +57,7 @@ export namespace Templates {
             </div>
         </div>` );
 
-    export const Header = new Template("Header", TemplatePosition.InnerTop,
-        `<div style = "display:flex; flex-direction:column; height:100%;" >
-            <div style="width:100%; background-color: rgba(0, 0, 0, .4); color: white; ">
-                <FormattedTextBox {...props} height={"min-content"} color={"white"} fieldKey={"header"} />
-            </div>
-            <div style="width:100%;height:100%;overflow:auto;">{layout}</div>
-        </div > ` );
-
-    export const Bullet = new Template("Bullet", TemplatePosition.InnerTop,
-        `< div >
-        <div style="height:100%; width:100%;position:absolute;">{layout}</div>
-        <div id="isExpander" style="height:15px; width:15px; margin-left:-16px; pointer-events:all; position:absolute; top: 0; background-color: rgba(0, 0, 0, .4); color: white;">
-            <img id="isExpander" src="/assets/downarrow.png" width="15px" height="15px" />
-        </div>
-        </div > `
-    );
-
-    export function ImageOverlay(width: number, height: number, field: string = "thumbnail") {
-        return (`< div >
-        <div style="height:100%; width:100%; position:absolute;">{layout}</div>
-        <div style="height:auto; width:${width}px; bottom:0; right:0; background:rgba(0,0,0,0.25); position:absolute;overflow:hidden;">
-            <ImageBox id="isExpander" {...props} style="width:100%; height=auto;" PanelWidth={${width}} fieldKey={"${field}"} />
-                </div>
-            </div > `);
-    }
-
-    export function TitleBar(datastring: string) {
-        return (`<div>
-            <div style="height:25px; width:100%; background-color: rgba(0, 0, 0, .4); color: white; z-index: 100">
-                <span style="text-align:center;width:100%;font-size:20px;position:absolute;overflow:hidden;white-space:nowrap;text-overflow:ellipsis">${datastring}</span>
-            </div>
-            <div style="height:calc(100% - 25px);">
-                <div style="width:100%;overflow:auto">{layout}</div>
-            </div>
-        </div>` );
-    }
-    export const TemplateList: Template[] = [Title, Header, Caption, Bullet];
+    export const TemplateList: Template[] = [Title, Caption];
 
     export function sortTemplates(a: Template, b: Template) {
         if (a.Position < b.Position) { return -1; }
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index a17899b8b..9caa4ea37 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -110,11 +110,6 @@ export class CollectionView extends React.Component<FieldViewProps> {
             let layoutItems: ContextMenuProps[] = existing && "subitems" in existing ? existing.subitems : [];
             layoutItems.push({ description: `${this.props.Document.forceActive ? "Select" : "Force"} Contents Active`, event: () => this.props.Document.forceActive = !this.props.Document.forceActive, icon: "project-diagram" });
             !existing && ContextMenu.Instance.addItem({ description: "Layout...", subitems: layoutItems, icon: "hand-point-right" });
-
-            let makes = ContextMenu.Instance.findByDescription("Make...");
-            let makeItems: ContextMenuProps[] = makes && "subitems" in makes ? makes.subitems : [];
-            makeItems.push({ description: "Template Layout Instance", event: () => this.props.addDocTab && this.props.addDocTab(Doc.ApplyTemplate(this.props.Document)!, undefined, "onRight"), icon: "project-diagram" });
-            !makes && ContextMenu.Instance.addItem({ description: "Make...", subitems: makeItems, icon: "hand-point-right" });
         }
     }
 
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 2591bdd8d..24be5963f 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -894,6 +894,30 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
 
     onContextMenu = (e: React.MouseEvent) => {
         let layoutItems: ContextMenuProps[] = [];
+
+        if (this.childDocs.some(d => d.isTemplate)) {
+            layoutItems.push({ description: "Template Layout Instance", event: () => this.props.addDocTab && this.props.addDocTab(Doc.ApplyTemplate(this.props.Document)!, undefined, "onRight"), icon: "project-diagram" });
+        }
+        layoutItems.push({ description: "reset view", event: () => { this.props.Document.panX = this.props.Document.panY = 0; this.props.Document.scale = 1; }, icon: "compress-arrows-alt" });
+        layoutItems.push({ description: `${this.fitToBox ? "Unset" : "Set"} Fit To Container`, event: this.fitToContainer, icon: !this.fitToBox ? "expand-arrows-alt" : "compress-arrows-alt" });
+        layoutItems.push({
+            description: `${this.props.Document.useClusters ? "Uncluster" : "Use Clusters"}`,
+            event: async () => {
+                Docs.Prototypes.get(DocumentType.TEXT).defaultBackgroundColor = "#f1efeb"; // backward compatibility with databases that didn't have a default background color on prototypes
+                Docs.Prototypes.get(DocumentType.COL).defaultBackgroundColor = "white";
+                this.props.Document.useClusters = !this.props.Document.useClusters;
+                this.updateClusters();
+            },
+            icon: !this.props.Document.useClusters ? "braille" : "braille"
+        });
+        this.props.Document.useClusters && layoutItems.push({
+            description: `${this.props.Document.clusterOverridesDefaultBackground ? "Use Default Backgrounds" : "Clusters Override Defaults"}`,
+            event: async () => this.props.Document.clusterOverridesDefaultBackground = !this.props.Document.clusterOverridesDefaultBackground,
+            icon: !this.props.Document.useClusters ? "chalkboard" : "chalkboard"
+        });
+        layoutItems.push({ description: "Arrange contents in grid", event: this.arrangeContents, icon: "table" });
+        layoutItems.push({ description: "Analyze Strokes", event: this.analyzeStrokes, icon: "paint-brush" });
+        layoutItems.push({ description: "Jitter Rotation", event: action(() => this.props.Document.jitterRotation = 10), icon: "paint-brush" });
         layoutItems.push({
             description: "Import document", icon: "upload", event: () => {
                 const input = document.createElement("input");
@@ -924,26 +948,6 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
                 input.click();
             }
         });
-        layoutItems.push({ description: `${this.fitToBox ? "Unset" : "Set"} Fit To Container`, event: this.fitToContainer, icon: !this.fitToBox ? "expand-arrows-alt" : "compress-arrows-alt" });
-        layoutItems.push({ description: "reset view", event: () => { this.props.Document.panX = this.props.Document.panY = 0; this.props.Document.scale = 1; }, icon: "compress-arrows-alt" });
-        layoutItems.push({
-            description: `${this.props.Document.useClusters ? "Uncluster" : "Use Clusters"}`,
-            event: async () => {
-                Docs.Prototypes.get(DocumentType.TEXT).defaultBackgroundColor = "#f1efeb"; // backward compatibility with databases that didn't have a default background color on prototypes
-                Docs.Prototypes.get(DocumentType.COL).defaultBackgroundColor = "white";
-                this.props.Document.useClusters = !this.props.Document.useClusters;
-                this.updateClusters();
-            },
-            icon: !this.props.Document.useClusters ? "braille" : "braille"
-        });
-        layoutItems.push({
-            description: `${this.props.Document.clusterOverridesDefaultBackground ? "Use Default Backgrounds" : "Clusters Override Defaults"}`,
-            event: async () => this.props.Document.clusterOverridesDefaultBackground = !this.props.Document.clusterOverridesDefaultBackground,
-            icon: !this.props.Document.useClusters ? "chalkboard" : "chalkboard"
-        });
-        layoutItems.push({ description: "Arrange contents in grid", event: this.arrangeContents, icon: "table" });
-        layoutItems.push({ description: "Analyze Strokes", event: this.analyzeStrokes, icon: "paint-brush" });
-        layoutItems.push({ description: "Jitter Rotation", event: action(() => this.props.Document.jitterRotation = 10), icon: "paint-brush" });
 
         let noteItems: ContextMenuProps[] = [];
         let notes = DocListCast((CurrentUserUtils.UserDocument.noteTypes as Doc).data);
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 6960689b4..2d17c09e5 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -147,14 +147,6 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     public get ContentDiv() { return this._mainCont.current; }
     @computed get active(): boolean { return SelectionManager.IsSelected(this) || this.props.parentActive(); }
     @computed get topMost(): boolean { return this.props.renderDepth === 0; }
-    @computed get templates(): List<string> {
-        let field = this.props.Document.templates;
-        if (field && field instanceof List) {
-            return field;
-        }
-        return new List<string>();
-    }
-    set templates(templates: List<string>) { this.props.Document.templates = templates; }
     screenRect = (): ClientRect | DOMRect => this._mainCont.current ? this._mainCont.current.getBoundingClientRect() : new DOMRect();
 
     @action
@@ -449,6 +441,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         this.props.Document.customLayout = this.props.Document.layout;
         this.props.Document.layout = this.props.Document.nativeLayout;
         this.props.Document.type = this.props.Document.nativeType;
+        this.props.Document.nativeLayout = undefined;
     }
     @undoBatch
     makeCustomViewClicked = (): void => {
@@ -553,28 +546,6 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         }
     }
 
-    @action
-    addTemplate = (template: Template) => {
-        this.templates.push(template.Layout);
-        this.templates = this.templates;
-    }
-
-    @action
-    removeTemplate = (template: Template) => {
-        for (let i = 0; i < this.templates.length; i++) {
-            if (this.templates[i] === template.Layout) {
-                this.templates.splice(i, 1);
-                break;
-            }
-        }
-        this.templates = this.templates;
-    }
-    @action
-    clearTemplates = () => {
-        this.templates.length = 0;
-        this.templates = this.templates;
-    }
-
     @undoBatch
     @action
     freezeNativeDimensions = (): void => {
@@ -645,25 +616,11 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         subitems.push({ description: "Open Fields", event: this.fieldsClicked, icon: "layer-group" });
         cm.addItem({ description: "Open...", subitems: subitems, icon: "external-link-alt" });
 
-        let existingVm = ContextMenu.Instance.findByDescription("View Modes...");
-        let vms: ContextMenuProps[] = existingVm && "subitems" in existingVm ? existingVm.subitems : [];
-        if (this.props.Document.type !== DocumentType.COL && this.props.Document.type !== DocumentType.TEMPLATE) {
-            vms.push({ description: "Custom Document View", event: this.makeCustomViewClicked, icon: "concierge-bell" });
-        } else if (this.props.Document.nativeLayout) {
-            vms.push({ description: "Native Document View", event: this.makeNativeViewClicked, icon: "concierge-bell" });
-        }
-        !existingVm && cm.addItem({ description: "View Modes...", subitems: vms, icon: "eye" });
-
-        let existingMake = ContextMenu.Instance.findByDescription("Make...");
-        let makes: ContextMenuProps[] = existingMake && "subitems" in existingMake ? existingMake.subitems : [];
-        makes.push({ description: this.props.Document.isBackground ? "Remove Background" : "Into Background", event: this.makeBackground, icon: this.props.Document.lockedPosition ? "unlock" : "lock" });
-        makes.push({ description: "Metadata Field View", event: () => this.props.ContainingCollectionView && Doc.MakeTemplate(this.props.Document, StrCast(this.props.Document.title), this.props.ContainingCollectionView.props.Document), icon: "concierge-bell" })
-        makes.push({ description: "Into Portal", event: this.makeIntoPortal, icon: "window-restore" });
-        makes.push({ description: this.layoutDoc.ignoreClick ? "Selectable" : "Unselectable", event: () => this.layoutDoc.ignoreClick = !this.layoutDoc.ignoreClick, icon: this.layoutDoc.ignoreClick ? "unlock" : "lock" });
-        !existingMake && cm.addItem({ description: "Make...", subitems: makes, icon: "hand-point-right" });
 
         let existingOnClick = ContextMenu.Instance.findByDescription("OnClick...");
         let onClicks: ContextMenuProps[] = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : [];
+        onClicks.push({ description: "Enter Portal", event: this.makeIntoPortal, 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.props.Document.isButton || this.props.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({
@@ -676,6 +633,10 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
 
         let existing = ContextMenu.Instance.findByDescription("Layout...");
         let layoutItems: ContextMenuProps[] = existing && "subitems" in existing ? existing.subitems : [];
+        layoutItems.push({ description: this.props.Document.isBackground ? "As Foreground" : "As Background", event: this.makeBackground, icon: this.props.Document.lockedPosition ? "unlock" : "lock" });
+        if (this.props.ContainingCollectionView && this.props.ContainingCollectionView.props.Document.layout instanceof Doc) {
+            layoutItems.push({ description: "Make View of Metadata Field", event: () => this.props.ContainingCollectionView && Doc.MakeTemplate(this.props.Document, StrCast(this.props.Document.title), this.props.ContainingCollectionView.props.Document), 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.props.Document.ignoreAspect || !this.props.Document.nativeWidth || !this.props.Document.nativeHeight ? "Freeze" : "Unfreeze", event: this.freezeNativeDimensions, icon: "snowflake" });
@@ -685,6 +646,11 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         if (this.props.Document.detailedLayout && !this.props.Document.isTemplate) {
             layoutItems.push({ description: "Toggle detail", event: () => Doc.ToggleDetailLayout(this.props.Document), icon: "image" });
         }
+        if (this.props.Document.type !== DocumentType.COL && this.props.Document.type !== DocumentType.TEMPLATE) {
+            layoutItems.push({ description: "Use Custom Layout", event: this.makeCustomViewClicked, icon: "concierge-bell" });
+        } else if (this.props.Document.nativeLayout) {
+            layoutItems.push({ description: "Use Native Layout", event: this.makeNativeViewClicked, icon: "concierge-bell" });
+        }
         !existing && cm.addItem({ description: "Layout...", subitems: layoutItems, icon: "compass" });
         if (!ClientUtils.RELEASE) {
             let copies: ContextMenuProps[] = [];
@@ -698,7 +664,6 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         !existingAnalyze && cm.addItem({ description: "Analyzers...", subitems: analyzers, icon: "hand-point-right" });
         cm.addItem({ description: "Pin to Presentation", event: () => this.props.pinToPres(this.props.Document), icon: "map-pin" }); //I think this should work... and it does! A miracle!
         cm.addItem({ description: "Add Repl", icon: "laptop-code", event: () => OverlayView.Instance.addWindow(<ScriptingRepl />, { x: 300, y: 100, width: 200, height: 200, title: "Scripting REPL" }) });
-        cm.addItem({ description: "Move To Overlay", icon: "laptop-code", event: () => ((o: Doc) => o && Doc.AddDocToList(o, "data", this.props.Document))(Cast(CurrentUserUtils.UserDocument.overlays, Doc) as Doc) });
         cm.addItem({
             description: "Download document", icon: "download", event: async () => {
                 let y = JSON.parse(await rp.get(Utils.CorsProxy("http://localhost:8983/solr/dash/select"), {
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 6fc94a140..19788c21a 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -216,12 +216,13 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
             funcs.push({ description: "Record 1sec audio", event: this.recordAudioAnnotation, icon: "expand-arrows-alt" });
             funcs.push({ description: "Rotate", event: this.rotate, icon: "expand-arrows-alt" });
 
-            let modes: ContextMenuProps[] = [];
+            let existingAnalyze = ContextMenu.Instance.findByDescription("Analyzers...");
+            let modes: ContextMenuProps[] = existingAnalyze && "subitems" in existingAnalyze ? existingAnalyze.subitems : [];
             modes.push({ description: "Generate Tags", event: this.generateMetadata, icon: "tag" });
             modes.push({ description: "Find Faces", event: this.extractFaces, icon: "camera" });
+            !existingAnalyze && ContextMenu.Instance.addItem({ description: "Analyzers...", subitems: modes, icon: "hand-point-right" })
 
             ContextMenu.Instance.addItem({ description: "Image Funcs...", subitems: funcs, icon: "asterisk" });
-            ContextMenu.Instance.addItem({ description: "Analyze...", subitems: modes, icon: "eye" });
         }
     }
 
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index 653c5c27f..f80f414b1 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -2,26 +2,21 @@
 import { action, computed, observable } from "mobx";
 import { observer } from "mobx-react";
 import 'react-image-lightbox/style.css'; // This only needs to be imported once in your app
-import { CompileScript, ScriptOptions, CompiledScript } from "../../util/Scripting";
-import { FieldView, FieldViewProps } from './FieldView';
-import "./KeyValueBox.scss";
-import { KeyValuePair } from "./KeyValuePair";
-import React = require("react");
-import { NumCast, Cast, FieldValue, StrCast } from "../../../new_fields/Types";
-import { Doc, Field, FieldResult, DocListCastAsync } from "../../../new_fields/Doc";
-import { ComputedField, ScriptField } from "../../../new_fields/ScriptField";
-import { SetupDrag } from "../../util/DragManager";
-import { Docs } from "../../documents/Documents";
-import { RawDataOperationParameters } from "../../northstar/model/idea/idea";
-import { Templates } from "../Templates";
+import { Doc, Field, FieldResult } from "../../../new_fields/Doc";
 import { List } from "../../../new_fields/List";
-import { TextField } from "../../util/ProsemirrorCopy/prompt";
 import { RichTextField } from "../../../new_fields/RichTextField";
-import { ImageField } from "../../../new_fields/URLField";
-import { SelectionManager } from "../../util/SelectionManager";
 import { listSpec } from "../../../new_fields/Schema";
-import { CollectionViewType } from "../collections/CollectionBaseView";
+import { ComputedField, ScriptField } from "../../../new_fields/ScriptField";
+import { Cast, FieldValue, NumCast } from "../../../new_fields/Types";
+import { ImageField } from "../../../new_fields/URLField";
+import { Docs } from "../../documents/Documents";
+import { SetupDrag } from "../../util/DragManager";
+import { CompiledScript, CompileScript, ScriptOptions } from "../../util/Scripting";
 import { undoBatch } from "../../util/UndoManager";
+import { FieldView, FieldViewProps } from './FieldView';
+import "./KeyValueBox.scss";
+import { KeyValuePair } from "./KeyValuePair";
+import React = require("react");
 
 export type KVPScript = {
     script: CompiledScript;
-- 
cgit v1.2.3-70-g09d2


From 03579f16e2c18ef8af578f5e65da3939ee9860ee Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Thu, 12 Sep 2019 14:13:34 -0400
Subject: fixe template custom toggling.

---
 src/client/views/nodes/DocumentView.tsx | 14 +++++++++++++-
 1 file changed, 13 insertions(+), 1 deletion(-)

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

diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 2d17c09e5..4bb31ef1d 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -441,16 +441,28 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         this.props.Document.customLayout = this.props.Document.layout;
         this.props.Document.layout = this.props.Document.nativeLayout;
         this.props.Document.type = this.props.Document.nativeType;
+        this.props.Document.nativeWidth = this.props.Document.nativeNativeWidth;
+        this.props.Document.nativeHeight = this.props.Document.nativeNativeHeight;
+        this.props.Document.ignoreAspect = this.props.Document.nativeIgnoreAspect;
         this.props.Document.nativeLayout = undefined;
+        this.props.Document.nativeNativeWidth = undefined;
+        this.props.Document.nativeNativeHeight = undefined;
+        this.props.Document.nativeIgnoreAspect = undefined;
     }
     @undoBatch
     makeCustomViewClicked = (): void => {
         this.props.Document.nativeLayout = this.props.Document.layout;
         this.props.Document.nativeType = this.props.Document.type;
-        PromiseValue(this.props.Document.customLayout).then(custom => {
+        this.props.Document.nativeNativeWidth = this.props.Document.nativeWidth;
+        this.props.Document.nativeNativeHeight = this.props.Document.nativeHeight;
+        this.props.Document.nativeIgnoreAspect = this.props.Document.ignoreAspect;
+        PromiseValue(Cast(this.props.Document.customLayout, Doc)).then(custom => {
             if (custom) {
                 this.props.Document.type = DocumentType.TEMPLATE;
                 this.props.Document.layout = custom;
+                !custom.nativeWidth && (this.props.Document.nativeWidth = 0);
+                !custom.nativeHeight && (this.props.Document.nativeHeight = 0);
+                !custom.nativeWidth && (this.props.Document.ignoreAspect = true);
             } else {
                 let options = { title: "data", width: NumCast(this.props.Document.width), height: NumCast(this.props.Document.height) + 25, x: -NumCast(this.props.Document.width) / 2, y: -NumCast(this.props.Document.height) / 2, };
                 let fieldTemplate = this.props.Document.type === DocumentType.TEXT ? Docs.Create.TextDocument(options) : Docs.Create.ImageDocument("http://www.cs.brown.edu", options);
-- 
cgit v1.2.3-70-g09d2


From 612dbe02514f7f34c066aee3b5dd09c4a99b113a Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Thu, 12 Sep 2019 14:45:24 -0400
Subject: moved customLayout to data doc.  need to allow for multiple
 customLayouts now.

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

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

diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 4bb31ef1d..3f0b62511 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -438,7 +438,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
 
     @undoBatch
     makeNativeViewClicked = (): void => {
-        this.props.Document.customLayout = this.props.Document.layout;
+        (this.dataDoc || Doc.GetProto(this.props.Document)).customLayout = this.props.Document.layout;
         this.props.Document.layout = this.props.Document.nativeLayout;
         this.props.Document.type = this.props.Document.nativeType;
         this.props.Document.nativeWidth = this.props.Document.nativeNativeWidth;
-- 
cgit v1.2.3-70-g09d2


From 47ca25c49d7d9f1fee22b256f86e296dac42b47b Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Thu, 12 Sep 2019 21:42:07 -0400
Subject: cleaned up rulerProvider a bit and added menu item to turn it on.

---
 src/client/views/DocumentDecorations.tsx           | 13 +++++----
 src/client/views/InkingControl.tsx                 |  2 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 31 +++++++---------------
 .../collections/collectionFreeForm/MarqueeView.tsx |  2 +-
 .../views/nodes/CollectionFreeFormDocumentView.tsx |  3 +++
 src/client/views/nodes/DocumentView.tsx            | 12 ++++++---
 src/client/views/nodes/FormattedTextBox.tsx        |  4 +--
 7 files changed, 31 insertions(+), 36 deletions(-)

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

diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 6451fdf5e..4ab2ade8e 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -424,23 +424,22 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
             document.addEventListener("pointermove", this.onRadiusMove);
             document.addEventListener("pointerup", this.onRadiusUp);
         }
-        if (!this._isMoving) {
-            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 = "0%");
-        }
     }
 
     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]));
-        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)}%`);
+        dist = dist < 3 ? 0 : dist;
+        let usingRule = false;
         SelectionManager.SelectedDocuments().map(dv => {
             let cv = dv.props.ContainingCollectionView;
             let ruleProvider = cv && (Cast(cv.props.Document.ruleProvider, Doc) as Doc);
             let heading = NumCast(dv.props.Document.heading);
-            cv && ((ruleProvider ? ruleProvider : cv.props.Document)["ruleRounding_" + heading] = StrCast(dv.props.Document.borderRounding));
+            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();
         e.preventDefault();
     }
diff --git a/src/client/views/InkingControl.tsx b/src/client/views/InkingControl.tsx
index aa573f16b..867735c0b 100644
--- a/src/client/views/InkingControl.tsx
+++ b/src/client/views/InkingControl.tsx
@@ -73,7 +73,7 @@ export class InkingControl extends React.Component {
                     let cv = view.props.ContainingCollectionView;
                     let ruleProvider = cv && (Cast(cv.props.Document.ruleProvider, Doc) as Doc);
                     let parback = cv && StrCast(cv.props.Document.backgroundColor);
-                    cv && parback && ((ruleProvider ? ruleProvider : cv.props.Document)["ruleColor_" + NumCast(view.props.Document.heading)] = Utils.toRGBAstr(color.rgb));
+                    cv && parback && (Doc.GetProto(ruleProvider ? ruleProvider : cv.props.Document)["ruleColor_" + NumCast(view.props.Document.heading)] = Utils.toRGBAstr(color.rgb));
                     // if (parback && cv && parback.indexOf("rgb") !== -1) {
                     //     let parcol = Utils.fromRGBAstr(parback);
                     //     let hsl = Utils.RGBToHSL(parcol.r, parcol.g, parcol.b);
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 24f2a60a9..9a8ae3535 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -262,27 +262,14 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
         if (heading === 0) {
             let sorted = this.childDocs.filter(d => d.type === DocumentType.TEXT && d.data_ext instanceof Doc && d.data_ext.lastModified).sort((a, b) => DateCast((Cast(a.data_ext, Doc) as Doc).lastModified).date > DateCast((Cast(b.data_ext, Doc) as Doc).lastModified).date ? 1 :
                 DateCast((Cast(a.data_ext, Doc) as Doc).lastModified).date < DateCast((Cast(b.data_ext, Doc) as Doc).lastModified).date ? -1 : 0);
-            heading = NumCast(sorted[sorted.length - 1].heading) === 1 ? 2 : NumCast(sorted[sorted.length - 1].heading);
+            heading = !sorted.length ? 1 : NumCast(sorted[sorted.length - 1].heading) === 1 ? 2 : NumCast(sorted[sorted.length - 1].heading);
         }
         newBox.heading = heading;
 
-        PromiseValue(Cast(this.props.Document.ruleProvider, Doc)).then(ruleProvider => {
-            if (!ruleProvider) ruleProvider = this.props.Document;
-            // saturation shift
-            // let col = NumCast(ruleProvider["ruleColor_" + NumCast(newBox.heading)]);
-            // let back = Utils.fromRGBAstr(StrCast(this.props.Document.backgroundColor));
-            // let hsl = Utils.RGBToHSL(back.r, back.g, back.b);
-            // let newcol = { h: hsl.h, s: hsl.s + col, l: hsl.l };
-            // col && (Doc.GetProto(newBox).backgroundColor = Utils.toRGBAstr(Utils.HSLtoRGB(newcol.h, newcol.s, newcol.l)));
-            // OR transparency set
-            let col = StrCast(ruleProvider["ruleColor_" + NumCast(newBox.heading)]);
-            (newBox.backgroundColor === newBox.defaultBackgroundColor) && col && (Doc.GetProto(newBox).backgroundColor = col);
-
-            let round = StrCast(ruleProvider["ruleRounding_" + NumCast(newBox.heading)]);
-            round && (Doc.GetProto(newBox).borderRounding = round);
-            newBox.ruleProvider = ruleProvider;
-            this.addDocument(newBox, false);
-        });
+        if (Cast(this.props.Document.ruleProvider, Doc) as Doc) {
+            newBox.ruleProvider = Doc.GetProto(Cast(this.props.Document.ruleProvider, Doc) as Doc);
+        }
+        this.addDocument(newBox, false);
     }
     private addDocument = (newBox: Doc, allowDuplicates: boolean) => {
         this.props.addDocument(newBox, false);
@@ -898,7 +885,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
     onContextMenu = (e: React.MouseEvent) => {
         let layoutItems: ContextMenuProps[] = [];
 
-        if (this.childDocs.some(d => d.isTemplate)) {
+        if (this.childDocs.some(d => BoolCast(d.isTemplate))) {
             layoutItems.push({ description: "Template Layout Instance", event: () => this.props.addDocTab && this.props.addDocTab(Doc.ApplyTemplate(this.props.Document)!, undefined, "onRight"), icon: "project-diagram" });
         }
         layoutItems.push({ description: "reset view", event: () => { this.props.Document.panX = this.props.Document.panY = 0; this.props.Document.scale = 1; }, icon: "compress-arrows-alt" });
@@ -913,9 +900,9 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
             },
             icon: !this.props.Document.useClusters ? "braille" : "braille"
         });
-        this.props.Document.useClusters && layoutItems.push({
-            description: `${this.props.Document.clusterOverridesDefaultBackground ? "Use Default Backgrounds" : "Clusters Override Defaults"}`,
-            event: async () => this.props.Document.clusterOverridesDefaultBackground = !this.props.Document.clusterOverridesDefaultBackground,
+        layoutItems.push({
+            description: `${this.props.Document.isRuleProvider ? "Stop Auto Format" : "Auto Format"}`,
+            event: () => this.props.Document.isRuleProvider = !this.props.Document.isRuleProvider,
             icon: !this.props.Document.useClusters ? "chalkboard" : "chalkboard"
         });
         layoutItems.push({ description: "Arrange contents in grid", event: this.arrangeContents, icon: "table" });
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index fe48a3485..4308497a1 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -356,7 +356,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
                 this.props.addLiveTextDocument(summary);
             }
             else {
-                newCollection.ruleProvider = this.props.container.props.Document;
+                newCollection.ruleProvider = this.props.container.props.Document.isRuleProvider ? this.props.container.props.Document : this.props.container.props.Document.ruleProvider;
                 this.props.addDocument(newCollection, false);
                 this.props.selectDocuments([newCollection]);
             }
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 07dd1cae7..082e5c5e3 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -74,7 +74,10 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
     }
 
     borderRounding = () => {
+        let ruleProvider = this.props.Document.ruleProvider as Doc;
+        let ruleRounding = ruleProvider ? StrCast(Doc.GetProto(ruleProvider)["ruleRounding_" + NumCast(this.props.Document.heading)]) : undefined;
         let br = StrCast(this.layoutDoc.layout instanceof Doc ? this.layoutDoc.layout.borderRounding : this.props.Document.borderRounding);
+        br = !br && ruleRounding ? ruleRounding : br;
         if (br.endsWith("%")) {
             let percent = Number(br.substr(0, br.length - 1)) / 100;
             let nativeDim = Math.min(NumCast(this.layoutDoc.nativeWidth), NumCast(this.layoutDoc.nativeHeight));
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 3f0b62511..7b9ed12a7 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -793,9 +793,15 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
 
 
     render() {
-        let backgroundColor = this.layoutDoc.isBackground || (this.props.ContainingCollectionView && this.props.ContainingCollectionView.props.Document.clusterOverridesDefaultBackground && this.layoutDoc.backgroundColor === this.layoutDoc.defaultBackgroundColor) ?
+        let ruleProvider = this.props.Document.ruleProvider as Doc;
+        let ruleColor = ruleProvider ? StrCast(Doc.GetProto(ruleProvider)["ruleColor_" + NumCast(this.props.Document.heading)]) : undefined;
+        let ruleRounding = ruleProvider ? StrCast(Doc.GetProto(ruleProvider)["ruleRounding_" + NumCast(this.props.Document.heading)]) : undefined;
+        let colorSet = this.layoutDoc.backgroundColor !== this.layoutDoc.defaultBackgroundColor;
+        let clusterCol = this.props.ContainingCollectionView && this.props.ContainingCollectionView.props.Document.clusterOverridesDefaultBackground;
+
+        let backgroundColor = this.layoutDoc.isBackground || (clusterCol && !colorSet) ?
             this.props.backgroundColor(this.layoutDoc) || StrCast(this.layoutDoc.backgroundColor) :
-            StrCast(this.layoutDoc.backgroundColor) || this.props.backgroundColor(this.layoutDoc);
+            ruleColor && !colorSet ? ruleColor : StrCast(this.layoutDoc.backgroundColor) || this.props.backgroundColor(this.layoutDoc);
         let foregroundColor = StrCast(this.layoutDoc.color);
         var nativeWidth = this.nativeWidth > 0 && !BoolCast(this.props.Document.ignoreAspect) ? `${this.nativeWidth}px` : "100%";
         var nativeHeight = BoolCast(this.props.Document.ignoreAspect) ? this.props.PanelHeight() / this.props.ContentScaling() : this.nativeHeight > 0 ? `${this.nativeHeight}px` : "100%";
@@ -811,7 +817,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         }
         let showTextTitle = showTitle && StrCast(this.layoutDoc.layout).startsWith("<FormattedTextBox") ? showTitle : undefined;
         let fullDegree = Doc.isBrushedHighlightedDegree(this.props.Document);
-        let borderRounding = StrCast(Doc.GetProto(this.props.Document).borderRounding);
+        let borderRounding = StrCast(Doc.GetProto(this.props.Document).borderRounding, ruleRounding);
         let localScale = this.props.ScreenToLocalTransform().Scale * fullDegree;
         let searchHighlight = (!this.props.Document.search_fields ? (null) :
             <div key="search" style={{ position: "absolute", background: "yellow", bottom: "-20px", borderRadius: "5px", transformOrigin: "bottom left", width: `${100 * this.props.ContentScaling()}%`, transform: `scale(${1 / this.props.ContentScaling()})` }}>
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 444b91b28..658e2a04d 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -284,8 +284,8 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
             e.stopPropagation();
         } else if (de.data instanceof DragManager.DocumentDragData) {
             const draggedDoc = de.data.draggedDocuments.length && de.data.draggedDocuments[0];
-            if (draggedDoc && draggedDoc.type === DocumentType.TEXT && StrCast(draggedDoc.layout) !== "") {
-                this.props.Document.layout = draggedDoc;
+            if (draggedDoc && draggedDoc.type === DocumentType.TEXT) {
+                this.props.Document.layout = draggedDoc.layout instanceof Doc ? draggedDoc.layout : draggedDoc;
                 draggedDoc.isTemplate = true;
                 e.stopPropagation();
             }
-- 
cgit v1.2.3-70-g09d2


From e241e61d6521ff5d63de1292f2b4269493f5d7cc Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Fri, 13 Sep 2019 00:55:39 -0400
Subject: added "publish" option to convert a document to a recognizable id

---
 src/client/documents/Documents.ts           | 28 +++++++++++++++++++++++++++-
 src/client/views/DocumentDecorations.tsx    |  9 +++++++--
 src/client/views/nodes/DocumentView.tsx     |  3 ++-
 src/client/views/nodes/FormattedTextBox.tsx |  4 ++--
 src/new_fields/Doc.ts                       |  8 ++++----
 5 files changed, 42 insertions(+), 10 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 602a7f9ad..28e5e5f40 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -20,7 +20,7 @@ import { AttributeTransformationModel } from "../northstar/core/attribute/Attrib
 import { AggregateFunction } from "../northstar/model/idea/idea";
 import { MINIMIZED_ICON_SIZE } from "../views/globalCssVariables.scss";
 import { IconBox } from "../views/nodes/IconBox";
-import { Field, Doc, Opt } from "../../new_fields/Doc";
+import { Field, Doc, Opt, DocListCastAsync } from "../../new_fields/Doc";
 import { OmitKeys, JSONUtils } from "../../Utils";
 import { ImageField, VideoField, AudioField, PdfField, WebField, YoutubeField } from "../../new_fields/URLField";
 import { HtmlField } from "../../new_fields/HtmlField";
@@ -607,6 +607,32 @@ export namespace Docs {
 
 export namespace DocUtils {
 
+    export function Publish(promoteDoc: Doc, targetID: string, addDoc: any, remDoc: any) {
+        if (targetID.startsWith("-")) {
+            targetID = targetID.substr(1, targetID.length - 1);
+            Doc.GetProto(promoteDoc).title = targetID;
+        }
+        DocServer.GetRefField(targetID).then(doc => {
+            let copy = doc instanceof Doc ? doc : Doc.MakeCopy(promoteDoc, true, targetID);
+            !doc && (Doc.GetProto(copy).title = targetID);
+            addDoc && addDoc(copy);
+            !doc && remDoc && remDoc(promoteDoc);
+            if (!doc) {
+                DocListCastAsync(promoteDoc.links).then(links => {
+                    links && links.map(async link => {
+                        if (link) {
+                            let a1 = await Cast(link.anchor1, Doc);
+                            if (a1 && Doc.AreProtosEqual(a1, promoteDoc)) link.anchor1 = copy;
+                            let a2 = await Cast(link.anchor2, Doc);
+                            if (a2 && Doc.AreProtosEqual(a2, promoteDoc)) link.anchor2 = copy;
+                            LinkManager.Instance.deleteLink(link);
+                            LinkManager.Instance.addLink(link);
+                        }
+                    })
+                })
+            }
+        });
+    }
     export function MakeLink(source: Doc, target: Doc, targetContext?: Doc, title: string = "", description: string = "", sourceContext?: Doc, id?: string) {
         if (LinkManager.Instance.doesLinkExist(source, target)) return undefined;
         let sv = DocumentManager.Instance.getDocumentView(source);
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 4ab2ade8e..589d69264 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -3,12 +3,12 @@ import { faLink, faTag, faTimes, faArrowAltCircleDown, faArrowAltCircleUp, faChe
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { action, computed, observable, reaction, runInAction, trace } from "mobx";
 import { observer } from "mobx-react";
-import { Doc } from "../../new_fields/Doc";
+import { Doc, DocListCastAsync } from "../../new_fields/Doc";
 import { List } from "../../new_fields/List";
 import { BoolCast, Cast, NumCast, StrCast } from "../../new_fields/Types";
 import { URLField } from '../../new_fields/URLField';
 import { emptyFunction, Utils } from "../../Utils";
-import { Docs } from "../documents/Documents";
+import { Docs, DocUtils } from "../documents/Documents";
 import { DocumentManager } from "../util/DocumentManager";
 import { DragLinksAsDocuments, DragManager } from "../util/DragManager";
 import { SelectionManager } from "../util/SelectionManager";
@@ -31,6 +31,7 @@ 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';
 const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
@@ -142,6 +143,10 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
             if (text[0] === '#') {
                 this._fieldKey = text.slice(1, text.length);
                 this._title = this.selectionTitle;
+            } else if (text.startsWith("::")) {
+                let targetID = text.slice(2, text.length);
+                let promoteDoc = SelectionManager.SelectedDocuments()[0];
+                DocUtils.Publish(promoteDoc.props.Document, targetID, promoteDoc.props.addDocument, promoteDoc.props.removeDocument);
             } else if (text.startsWith(">")) {
                 let fieldTemplateView = SelectionManager.SelectedDocuments()[0];
                 SelectionManager.DeselectAll();
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 7b9ed12a7..44e9b3180 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -438,7 +438,6 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
 
     @undoBatch
     makeNativeViewClicked = (): void => {
-        (this.dataDoc || Doc.GetProto(this.props.Document)).customLayout = this.props.Document.layout;
         this.props.Document.layout = this.props.Document.nativeLayout;
         this.props.Document.type = this.props.Document.nativeType;
         this.props.Document.nativeWidth = this.props.Document.nativeNativeWidth;
@@ -473,6 +472,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 Doc.MakeTemplate(fieldTemplate, metaKey, proto);
 
                 Doc.ApplyTemplateTo(docTemplate, this.props.Document, undefined, false);
+                Doc.GetProto(this.dataDoc || this.props.Document).customLayout = this.props.Document.layout;
             }
         });
     }
@@ -690,6 +690,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             }
         });
 
+        cm.addItem({ description: "Publish", event: () => DocUtils.Publish(this.props.Document, StrCast(this.props.Document.title), this.props.addDocument, this.props.removeDocument), icon: "file" });
         cm.addItem({ description: "Delete", event: this.deleteClicked, icon: "trash" });
         type User = { email: string, userDocumentId: string };
         let usersMenu: ContextMenuProps[] = [];
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index c07461e13..04d24fe8c 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -293,7 +293,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
     }
 
     recordKeyHandler = (e: KeyboardEvent) => {
-        if (this.props.Document === SelectionManager.SelectedDocuments()[0].props.Document) {
+        if (SelectionManager.SelectedDocuments().length && this.props.Document === SelectionManager.SelectedDocuments()[0].props.Document) {
             if (e.key === "R" && e.altKey) {
                 e.stopPropagation();
                 e.preventDefault();
@@ -473,7 +473,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
                 this._fontFamily = rules.font;
                 this._fontSize = rules.size;
                 setTimeout(() => {
-                    if (this._editorView!.state.doc.childCount) {
+                    if (this._editorView!.state.doc.childCount && this._proseRef) {
                         let tr = this._editorView!.state.tr;
                         let n = new NodeSelection(this._editorView!.state.doc.resolve(0));
                         if (this._editorView!.state.doc.textContent === "") {
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index e94b9f1eb..29925feb8 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -475,13 +475,13 @@ export namespace Doc {
         return { layout: layoutDoc, data: resolvedDataDoc };
     }
 
-    export function MakeCopy(doc: Doc, copyProto: boolean = false): Doc {
-        const copy = new Doc;
+    export function MakeCopy(doc: Doc, copyProto: boolean = false, copyProtoId?: string): Doc {
+        const copy = new Doc(copyProtoId, true);
         Object.keys(doc).forEach(key => {
             const field = ProxyField.WithoutProxy(() => doc[key]);
             if (key === "proto" && copyProto) {
-                if (field instanceof Doc) {
-                    copy[key] = Doc.MakeCopy(field);
+                if (doc[key] instanceof Doc) {
+                    copy[key] = Doc.MakeCopy(doc[key]!, false);
                 }
             } else {
                 if (field instanceof RefField) {
-- 
cgit v1.2.3-70-g09d2


From 106d7ca39e36fc114f79fd5fef27998a68fd3d5b Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Fri, 13 Sep 2019 11:15:01 -0400
Subject: fixed video w/ templates.  changed headings with text boxes, tweaked
 MakeTemplate titling

---
 src/client/documents/Documents.ts       | 11 ++++-------
 src/client/util/RichTextRules.ts        |  3 ++-
 src/client/views/nodes/DocumentView.tsx | 17 +++++++++++------
 src/client/views/nodes/ImageBox.tsx     |  7 +------
 src/client/views/nodes/VideoBox.tsx     | 11 +++++++----
 src/new_fields/Doc.ts                   |  3 ++-
 6 files changed, 27 insertions(+), 25 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 28e5e5f40..9db2ac558 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -121,7 +121,7 @@ export namespace Docs {
             }],
             [DocumentType.IMG, {
                 layout: { view: ImageBox, collectionView: [CollectionView, data, anno] as CollectionViewType },
-                options: { nativeWidth: 600, curPage: 0 }
+                options: { curPage: 0 }
             }],
             [DocumentType.WEB, {
                 layout: { view: WebBox, collectionView: [CollectionView, data, anno] as CollectionViewType },
@@ -137,7 +137,7 @@ export namespace Docs {
             }],
             [DocumentType.VID, {
                 layout: { view: VideoBox, collectionView: [CollectionVideoView, data, anno] as CollectionViewType },
-                options: { nativeWidth: 600, curPage: 0 },
+                options: { curPage: 0 },
             }],
             [DocumentType.AUDIO, {
                 layout: { view: AudioBox },
@@ -608,13 +608,10 @@ export namespace Docs {
 export namespace DocUtils {
 
     export function Publish(promoteDoc: Doc, targetID: string, addDoc: any, remDoc: any) {
-        if (targetID.startsWith("-")) {
-            targetID = targetID.substr(1, targetID.length - 1);
-            Doc.GetProto(promoteDoc).title = targetID;
-        }
+        targetID = targetID.replace(/^-/, "").replace(/\([0-9]*\)$/, "");
         DocServer.GetRefField(targetID).then(doc => {
             let copy = doc instanceof Doc ? doc : Doc.MakeCopy(promoteDoc, true, targetID);
-            !doc && (Doc.GetProto(copy).title = targetID);
+            !doc && (copy.title = undefined) && (Doc.GetProto(copy).title = targetID);
             addDoc && addDoc(copy);
             !doc && remDoc && remDoc(promoteDoc);
             if (!doc) {
diff --git a/src/client/util/RichTextRules.ts b/src/client/util/RichTextRules.ts
index 8ceb56f2f..c0c62463a 100644
--- a/src/client/util/RichTextRules.ts
+++ b/src/client/util/RichTextRules.ts
@@ -64,7 +64,8 @@ export const inpRules = {
                 let ruleProvider = Cast(FormattedTextBox.InputBoxOverlay!.props.Document.ruleProvider, Doc) as Doc;
                 let heading = NumCast(FormattedTextBox.InputBoxOverlay!.props.Document.heading);
                 if (ruleProvider && heading) {
-                    ruleProvider["ruleSize_" + heading] = size;
+                    (Cast(FormattedTextBox.InputBoxOverlay!.props.Document, Doc) as Doc).heading = Number(match[1]);
+                    return state.tr.deleteRange(start, end);
                 }
                 return state.tr.deleteRange(start, end).addStoredMark(schema.marks.pFontSize.create({ fontSize: Number(match[1]) }))
             }),
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 44e9b3180..31f1c7583 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -464,7 +464,9 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 !custom.nativeWidth && (this.props.Document.ignoreAspect = true);
             } else {
                 let options = { title: "data", width: NumCast(this.props.Document.width), height: NumCast(this.props.Document.height) + 25, x: -NumCast(this.props.Document.width) / 2, y: -NumCast(this.props.Document.height) / 2, };
-                let fieldTemplate = this.props.Document.type === DocumentType.TEXT ? Docs.Create.TextDocument(options) : Docs.Create.ImageDocument("http://www.cs.brown.edu", options);
+                let fieldTemplate = this.props.Document.type === DocumentType.TEXT ? Docs.Create.TextDocument(options) :
+                    this.props.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: StrCast(this.Document.title) + "layout", width: NumCast(this.props.Document.width) + 20, height: Math.max(100, NumCast(this.props.Document.height) + 45) });
                 let metaKey = "data";
@@ -576,9 +578,12 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             if (Cast(doc.anchor2, Doc) instanceof Doc && (Cast(doc.anchor2, Doc) as Doc)!.title === this.props.Document.title + ".portal") return true;
             return false;
         })) {
-            let portal = Docs.Create.FreeformDocument([], { width: this.props.Document[WidthSym]() + 10, height: this.props.Document[HeightSym](), title: this.props.Document.title + ".portal" });
-            DocUtils.MakeLink(this.props.Document, portal, undefined, this.props.Document.title + ".portal");
-            Doc.GetProto(this.props.Document).isButton = true;
+            let portalID = (this.props.Document.title + ".portal").replace(/^-/, "").replace(/\([0-9]*\)$/, "");
+            DocServer.GetRefField(portalID).then(existingPortal => {
+                let portal = existingPortal instanceof Doc ? existingPortal : Docs.Create.FreeformDocument([], { width: this.props.Document[WidthSym]() + 10, height: this.props.Document[HeightSym](), title: portalID });
+                DocUtils.MakeLink(this.props.Document, portal, undefined, portalID);
+                Doc.GetProto(this.props.Document).isButton = true;
+            })
         }
     }
 
@@ -646,8 +651,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         let existing = ContextMenu.Instance.findByDescription("Layout...");
         let layoutItems: ContextMenuProps[] = existing && "subitems" in existing ? existing.subitems : [];
         layoutItems.push({ description: this.props.Document.isBackground ? "As Foreground" : "As Background", event: this.makeBackground, icon: this.props.Document.lockedPosition ? "unlock" : "lock" });
-        if (this.props.ContainingCollectionView && this.props.ContainingCollectionView.props.Document.layout instanceof Doc) {
-            layoutItems.push({ description: "Make View of Metadata Field", event: () => this.props.ContainingCollectionView && Doc.MakeTemplate(this.props.Document, StrCast(this.props.Document.title), this.props.ContainingCollectionView.props.Document), icon: "concierge-bell" })
+        if (this.props.DataDoc) {
+            layoutItems.push({ description: "Make View of Metadata Field", event: () => Doc.MakeTemplate(this.props.Document, StrCast(this.props.Document.title), 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" });
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 19788c21a..95f304641 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -262,13 +262,8 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
     onDotDown(index: number) {
         this.Document.curPage = index;
     }
-
-    @computed get fieldExtensionDoc() {
-        return Doc.resolvedFieldDataDoc(this.props.DataDoc ? this.props.DataDoc : this.props.Document, this.props.fieldKey, "true");
-    }
-
     @computed private get url() {
-        let data = Cast(Doc.GetProto(this.props.Document).data, ImageField);
+        let data = Cast(Doc.GetProto(this.props.Document)[this.props.fieldKey], ImageField);
         return data ? data.url.href : undefined;
     }
 
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 3f4ee8960..96f011eff 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -4,7 +4,7 @@ import { observer } from "mobx-react";
 import * as rp from 'request-promise';
 import { InkTool } from "../../../new_fields/InkField";
 import { makeInterface } from "../../../new_fields/Schema";
-import { Cast, FieldValue, NumCast } from "../../../new_fields/Types";
+import { Cast, FieldValue, NumCast, BoolCast } from "../../../new_fields/Types";
 import { VideoField } from "../../../new_fields/URLField";
 import { RouteStore } from "../../../server/RouteStore";
 import { Utils } from "../../../Utils";
@@ -204,7 +204,7 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
         }
     }
     specificContextMenu = (e: React.MouseEvent): void => {
-        let field = Cast(this.Document[this.props.fieldKey], VideoField);
+        let field = Cast(this.dataDoc[this.props.fieldKey], VideoField);
         if (field) {
             let url = field.url.href;
             let subitems: ContextMenuProps[] = [];
@@ -216,7 +216,7 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
     }
 
     @computed get content() {
-        let field = Cast(this.Document[this.props.fieldKey], VideoField);
+        let field = Cast(this.dataDoc[this.props.fieldKey], VideoField);
         let interactive = InkingControl.Instance.selectedTool || !this.props.isSelected() ? "" : "-interactive";
         let style = "videoBox-content" + (this._fullScreen ? "-fullScreen" : "") + interactive;
         return !field ? <div>Loading</div> :
@@ -228,7 +228,7 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
     }
 
     @computed get youtubeVideoId() {
-        let field = Cast(this.Document[this.props.fieldKey], VideoField);
+        let field = Cast(this.dataDoc[this.props.fieldKey], VideoField);
         return field && field.url.href.indexOf("youtube") !== -1 ? ((arr: string[]) => arr[arr.length - 1])(field.url.href.split("/")) : "";
     }
 
@@ -269,6 +269,8 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
 
     }
 
+    @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); }
+
     @computed get youtubeContent() {
         this._youtubeIframeId = VideoBox._youtubeIframeCounter++;
         this._youtubeContentCreated = this._forceCreateYouTubeIFrame ? true : true;
@@ -281,6 +283,7 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
     }
 
     render() {
+        Doc.UpdateDocumentExtensionForField(this.dataDoc, this.props.fieldKey);
         return <div style={{ pointerEvents: "all", width: "100%", height: "100%" }} onContextMenu={this.specificContextMenu}>
             {this.youtubeVideoId ? this.youtubeContent : this.content}
         </div>;
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index 29925feb8..6f7453bbe 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -558,7 +558,8 @@ export namespace Doc {
         }
     }
 
-    export function MakeTemplate(fieldTemplate: Doc, metaKey: string, templateDataDoc: Doc) {
+    export function MakeTemplate(fieldTemplate: Doc, metaKeyRaw: string, templateDataDoc: Doc) {
+        let metaKey = metaKeyRaw.replace(/^-/, "").replace(/\([0-9]*\)$/, "");
         // move data doc fields to layout doc as needed (nativeWidth/nativeHeight, data, ??)
         let backgroundLayout = StrCast(fieldTemplate.backgroundLayout);
         let fieldLayoutDoc = fieldTemplate;
-- 
cgit v1.2.3-70-g09d2


From 3665945fd4ef1b1dfc300f9188fd358df76e38b3 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Fri, 13 Sep 2019 11:43:03 -0400
Subject: preserved data from field being converted to metadata template field

---
 src/client/views/DocumentDecorations.tsx |  3 ++-
 src/client/views/nodes/DocumentView.tsx  |  5 ++---
 src/client/views/nodes/KeyValueBox.tsx   |  2 +-
 src/new_fields/Doc.ts                    | 14 ++++++++------
 4 files changed, 13 insertions(+), 11 deletions(-)

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

diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 589d69264..6d63e8f73 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -160,7 +160,8 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
                         const fd = fieldTemplate.data;
                         fd instanceof ObjectField && (Doc.GetProto(containerView.props.DataDoc)[metaKey] = ObjectField.MakeCopy(fd));
                     }
-                    Doc.MakeTemplate(fieldTemplate, metaKey, proto);
+                    fieldTemplate.title = metaKey;
+                    Doc.MakeMetadataFieldTemplate(fieldTemplate, proto);
                     if (text.startsWith(">>")) {
                         proto.detailedLayout = proto.layout;
                         proto.miniLayout = ImageBox.LayoutString(metaKey);
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 31f1c7583..0a1367b56 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -469,9 +469,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                         Docs.Create.ImageDocument("http://www.cs.brown.edu", options);
 
                 let docTemplate = Docs.Create.FreeformDocument([fieldTemplate], { title: StrCast(this.Document.title) + "layout", width: NumCast(this.props.Document.width) + 20, height: Math.max(100, NumCast(this.props.Document.height) + 45) });
-                let metaKey = "data";
                 let proto = Doc.GetProto(docTemplate);
-                Doc.MakeTemplate(fieldTemplate, metaKey, proto);
+                Doc.MakeMetadataFieldTemplate(fieldTemplate, proto);
 
                 Doc.ApplyTemplateTo(docTemplate, this.props.Document, undefined, false);
                 Doc.GetProto(this.dataDoc || this.props.Document).customLayout = this.props.Document.layout;
@@ -652,7 +651,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         let layoutItems: ContextMenuProps[] = existing && "subitems" in existing ? existing.subitems : [];
         layoutItems.push({ description: this.props.Document.isBackground ? "As Foreground" : "As Background", event: this.makeBackground, icon: this.props.Document.lockedPosition ? "unlock" : "lock" });
         if (this.props.DataDoc) {
-            layoutItems.push({ description: "Make View of Metadata Field", event: () => Doc.MakeTemplate(this.props.Document, StrCast(this.props.Document.title), this.props.DataDoc!), icon: "concierge-bell" })
+            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" });
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index f80f414b1..ee70942de 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -198,7 +198,7 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
             return;
         }
         let previousViewType = fieldTemplate.viewType;
-        Doc.MakeTemplate(fieldTemplate, metaKey, Doc.GetProto(parentStackingDoc));
+        Doc.MakeMetadataFieldTemplate(fieldTemplate, Doc.GetProto(parentStackingDoc));
         previousViewType && (fieldTemplate.viewType = previousViewType);
 
         Cast(parentStackingDoc.data, listSpec(Doc))!.push(fieldTemplate);
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index 6f7453bbe..1a3d689bb 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -558,24 +558,24 @@ export namespace Doc {
         }
     }
 
-    export function MakeTemplate(fieldTemplate: Doc, metaKeyRaw: string, templateDataDoc: Doc) {
-        let metaKey = metaKeyRaw.replace(/^-/, "").replace(/\([0-9]*\)$/, "");
+    export function MakeMetadataFieldTemplate(fieldTemplate: Doc, templateDataDoc: Doc) {
         // move data doc fields to layout doc as needed (nativeWidth/nativeHeight, data, ??)
+        let metadataFieldName = StrCast(fieldTemplate.title);
         let backgroundLayout = StrCast(fieldTemplate.backgroundLayout);
         let fieldLayoutDoc = fieldTemplate;
         if (fieldTemplate.layout instanceof Doc) {
             fieldLayoutDoc = Doc.MakeDelegate(fieldTemplate.layout);
         }
-        let layout = StrCast(fieldLayoutDoc.layout).replace(/fieldKey={"[^"]*"}/, `fieldKey={"${metaKey}"}`);
+        let layout = StrCast(fieldLayoutDoc.layout).replace(/fieldKey={"[^"]*"}/, `fieldKey={"${metadataFieldName}"}`);
         if (backgroundLayout) {
-            backgroundLayout = backgroundLayout.replace(/fieldKey={"[^"]*"}/, `fieldKey={"${metaKey}"}`);
+            backgroundLayout = backgroundLayout.replace(/fieldKey={"[^"]*"}/, `fieldKey={"${metadataFieldName}"}`);
         }
 
         let layoutDelegate = fieldTemplate.layout instanceof Doc ? fieldLayoutDoc : fieldTemplate;
         layoutDelegate.layout = layout;
 
-        fieldTemplate.templateField = metaKey;
-        fieldTemplate.title = metaKey;
+        fieldTemplate.templateField = metadataFieldName;
+        fieldTemplate.title = metadataFieldName;
         fieldTemplate.isTemplate = true;
         fieldTemplate.layout = layoutDelegate !== fieldTemplate ? layoutDelegate : layout;
         fieldTemplate.backgroundLayout = backgroundLayout;
@@ -590,6 +590,8 @@ export namespace Doc {
         fieldTemplate.panY = 0;
         fieldTemplate.scale = 1;
         fieldTemplate.showTitle = "title";
+        let data = fieldTemplate.data;
+        !templateDataDoc[metadataFieldName] && data instanceof ObjectField && (templateDataDoc[metadataFieldName] = ObjectField.MakeCopy(data));
         setTimeout(() => fieldTemplate.proto = templateDataDoc);
     }
 
-- 
cgit v1.2.3-70-g09d2


From f508d5987e91e8297258905d8e8c9dfc405c50e9 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Fri, 13 Sep 2019 12:30:20 -0400
Subject: changed link following to follow links that aren't shown that don't
 have an anchor first.  changed text pointerevents when its a button.

---
 src/client/documents/Documents.ts           | 3 ++-
 src/client/views/nodes/DocumentView.tsx     | 4 +++-
 src/client/views/nodes/FormattedTextBox.tsx | 9 +++++----
 3 files changed, 10 insertions(+), 6 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 9db2ac558..2eff73b87 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -630,7 +630,7 @@ export namespace DocUtils {
             }
         });
     }
-    export function MakeLink(source: Doc, target: Doc, targetContext?: Doc, title: string = "", description: string = "", sourceContext?: Doc, id?: string) {
+    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;
@@ -649,6 +649,7 @@ export namespace DocUtils {
             linkDocProto.anchor1 = source;
             linkDocProto.anchor1Page = source.curPage;
             linkDocProto.anchor1Groups = new List<Doc>([]);
+            linkDocProto.anchor1anchored = anchored1;
             linkDocProto.anchor2 = target;
             linkDocProto.anchor2Page = target.curPage;
             linkDocProto.anchor2Groups = new List<Doc>([]);
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 0a1367b56..0816cb813 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -354,7 +354,9 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 }
                 else if (linkedDocs.length) {
                     SelectionManager.DeselectAll();
-                    let first = linkedDocs.filter(d => Doc.AreProtosEqual(d.anchor1 as Doc, this.props.Document));
+                    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
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index b2d44f14b..d39291743 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -189,7 +189,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
                         DocServer.GetRefField(id).then(linkDoc => {
                             this.dataDoc[key] = doc || Docs.Create.FreeformDocument([], { title: value, width: 500, height: 500 }, value);
                             if (linkDoc) { (linkDoc as Doc).anchor2 = this.dataDoc[key] as Doc; }
-                            else DocUtils.MakeLink(this.dataDoc, this.dataDoc[key] as Doc, undefined, "Ref:" + value, undefined, undefined, id);
+                            else DocUtils.MakeLink(this.dataDoc, this.dataDoc[key] as Doc, undefined, "Ref:" + value, undefined, undefined, id, true);
                         })
                     });
                     const link = this._editorView!.state.schema.marks.link.create({ href: `http://localhost:1050/doc/${id}`, location: "onRight", title: value });
@@ -898,8 +898,9 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
     render() {
         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";
+        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 (
             <div className={`formattedTextBox-cont-${style}`} ref={this._ref}
@@ -924,7 +925,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
                 onPointerEnter={action(() => this._entered = true)}
                 onPointerLeave={action(() => this._entered = false)}
             >
-                <div className={`formattedTextBox-inner${rounded}`} ref={this.createDropTarget} style={{ whiteSpace: "pre-wrap" }} />
+                <div className={`formattedTextBox-inner${rounded}`} style={{ whiteSpace: "pre-wrap", pointerEvents: (this.props.Document.isButton && !this.props.isSelected()) ? "none" : undefined }} ref={this.createDropTarget} />
             </div>
         );
     }
-- 
cgit v1.2.3-70-g09d2


From 233893698083cbcfcf39ddad8b57049aeb1ba842 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Fri, 13 Sep 2019 14:18:55 -0400
Subject: refactored how ruleProvider's work.   overloaded custom template  for
 creating metadata fields

---
 src/client/util/RichTextRules.ts                   |  8 +++----
 src/client/util/TooltipTextMenu.tsx                |  4 ++--
 src/client/views/DocumentDecorations.tsx           | 11 ++-------
 src/client/views/InkingControl.tsx                 |  5 ++--
 src/client/views/MainOverlayTextBox.tsx            |  2 +-
 src/client/views/MainView.tsx                      |  2 ++
 src/client/views/TemplateMenu.tsx                  | 16 ++++++-------
 .../views/collections/CollectionDockingView.tsx    |  1 +
 .../views/collections/CollectionSchemaView.tsx     |  1 +
 src/client/views/collections/CollectionSubView.tsx |  1 +
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 27 +++++++++++-----------
 .../collections/collectionFreeForm/MarqueeView.tsx |  2 --
 .../views/nodes/CollectionFreeFormDocumentView.tsx |  2 +-
 src/client/views/nodes/DocumentView.tsx            | 13 +++++++----
 src/client/views/nodes/FieldView.tsx               |  1 +
 src/client/views/nodes/FormattedTextBox.tsx        |  2 +-
 .../views/presentationview/PresentationElement.tsx |  1 +
 src/client/views/search/SearchItem.tsx             |  1 +
 src/new_fields/Doc.ts                              |  2 +-
 19 files changed, 51 insertions(+), 51 deletions(-)

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

diff --git a/src/client/util/RichTextRules.ts b/src/client/util/RichTextRules.ts
index c0c62463a..c727eec73 100644
--- a/src/client/util/RichTextRules.ts
+++ b/src/client/util/RichTextRules.ts
@@ -61,7 +61,7 @@ export const inpRules = {
             new RegExp(/^#([0-9]+)\s$/),
             (state, match, start, end) => {
                 let size = Number(match[1]);
-                let ruleProvider = Cast(FormattedTextBox.InputBoxOverlay!.props.Document.ruleProvider, Doc) as Doc;
+                let ruleProvider = FormattedTextBox.InputBoxOverlay!.props.ruleProvider;
                 let heading = NumCast(FormattedTextBox.InputBoxOverlay!.props.Document.heading);
                 if (ruleProvider && heading) {
                     (Cast(FormattedTextBox.InputBoxOverlay!.props.Document, Doc) as Doc).heading = Number(match[1]);
@@ -74,7 +74,7 @@ export const inpRules = {
             (state, match, start, end) => {
                 let node = (state.doc.resolve(start) as any).nodeAfter;
                 let sm = state.storedMarks || undefined;
-                let ruleProvider = Cast(FormattedTextBox.InputBoxOverlay!.props.Document.ruleProvider, Doc) as Doc;
+                let ruleProvider = FormattedTextBox.InputBoxOverlay!.props.ruleProvider;
                 let heading = NumCast(FormattedTextBox.InputBoxOverlay!.props.Document.heading);
                 if (ruleProvider && heading) {
                     ruleProvider["ruleAlign_" + heading] = "center";
@@ -88,7 +88,7 @@ export const inpRules = {
             (state, match, start, end) => {
                 let node = (state.doc.resolve(start) as any).nodeAfter;
                 let sm = state.storedMarks || undefined;
-                let ruleProvider = Cast(FormattedTextBox.InputBoxOverlay!.props.Document.ruleProvider, Doc) as Doc;
+                let ruleProvider = FormattedTextBox.InputBoxOverlay!.props.ruleProvider;
                 let heading = NumCast(FormattedTextBox.InputBoxOverlay!.props.Document.heading);
                 if (ruleProvider && heading) {
                     ruleProvider["ruleAlign_" + heading] = "left";
@@ -100,7 +100,7 @@ export const inpRules = {
             (state, match, start, end) => {
                 let node = (state.doc.resolve(start) as any).nodeAfter;
                 let sm = state.storedMarks || undefined;
-                let ruleProvider = Cast(FormattedTextBox.InputBoxOverlay!.props.Document.ruleProvider, Doc) as Doc;
+                let ruleProvider = FormattedTextBox.InputBoxOverlay!.props.ruleProvider;
                 let heading = NumCast(FormattedTextBox.InputBoxOverlay!.props.Document.heading);
                 if (ruleProvider && heading) {
                     ruleProvider["ruleAlign_" + heading] = "right";
diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx
index c376b6f86..84d045e6f 100644
--- a/src/client/util/TooltipTextMenu.tsx
+++ b/src/client/util/TooltipTextMenu.tsx
@@ -496,7 +496,7 @@ export class TooltipTextMenu {
             if (markType.name[0] === 'p') {
                 let size = this.fontSizeToNum.get(markType);
                 if (size) { this.updateFontSizeDropdown(String(size) + " pt"); }
-                let ruleProvider = Cast(this.editorProps.Document.ruleProvider, Doc) as Doc;
+                let ruleProvider = this.editorProps.ruleProvider;
                 let heading = NumCast(this.editorProps.Document.heading);
                 if (ruleProvider && heading) {
                     ruleProvider["ruleSize_" + heading] = size;
@@ -505,7 +505,7 @@ export class TooltipTextMenu {
             else {
                 let fontName = this.fontStylesToName.get(markType);
                 if (fontName) { this.updateFontStyleDropdown(fontName); }
-                let ruleProvider = Cast(this.editorProps.Document.ruleProvider, Doc) as Doc;
+                let ruleProvider = this.editorProps.ruleProvider;
                 let heading = NumCast(this.editorProps.Document.heading);
                 if (ruleProvider && heading) {
                     ruleProvider["ruleFont_" + heading] = fontName;
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 6d63e8f73..ebdf2a749 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -365,14 +365,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
                     Math.abs(e.pageY - this._downY) < Utils.DRAG_THRESHOLD) {
                     let docViews = SelectionManager.ViewsSortedVertically();
                     let topDocView = docViews[0];
-                    let ind = topDocView.templates.indexOf(Templates.Bullet.Layout);
-                    if (ind !== -1) {
-                        topDocView.templates.splice(ind, 1);
-                        topDocView.props.Document.subBulletDocs = undefined;
-                    } else {
-                        topDocView.addTemplate(Templates.Bullet);
-                        topDocView.props.Document.subBulletDocs = new List<Doc>(docViews.filter(v => v !== topDocView).map(v => v.props.Document.proto!));
-                    }
+                    topDocView.props.Document.subBulletDocs = new List<Doc>(docViews.filter(v => v !== topDocView).map(v => v.props.Document.proto!));
                 }
             }
             this._removeIcon = false;
@@ -439,7 +432,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
         let usingRule = false;
         SelectionManager.SelectedDocuments().map(dv => {
             let cv = dv.props.ContainingCollectionView;
-            let ruleProvider = cv && (Cast(cv.props.Document.ruleProvider, Doc) as Doc);
+            let ruleProvider = cv && cv.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);
diff --git a/src/client/views/InkingControl.tsx b/src/client/views/InkingControl.tsx
index 867735c0b..86d0fc0be 100644
--- a/src/client/views/InkingControl.tsx
+++ b/src/client/views/InkingControl.tsx
@@ -71,9 +71,8 @@ export class InkingControl extends React.Component {
                     targetDoc.backgroundColor = this._selectedColor;
                 if (view.props.Document.heading) {
                     let cv = view.props.ContainingCollectionView;
-                    let ruleProvider = cv && (Cast(cv.props.Document.ruleProvider, Doc) as Doc);
-                    let parback = cv && StrCast(cv.props.Document.backgroundColor);
-                    cv && parback && (Doc.GetProto(ruleProvider ? ruleProvider : cv.props.Document)["ruleColor_" + NumCast(view.props.Document.heading)] = Utils.toRGBAstr(color.rgb));
+                    let ruleProvider = cv && (Cast(cv.props.ruleProvider, Doc) as Doc);
+                    cv && (Doc.GetProto(ruleProvider ? ruleProvider : cv.props.Document)["ruleColor_" + NumCast(view.props.Document.heading)] = Utils.toRGBAstr(color.rgb));
                     // if (parback && cv && parback.indexOf("rgb") !== -1) {
                     //     let parcol = Utils.fromRGBAstr(parback);
                     //     let hsl = Utils.RGBToHSL(parcol.r, parcol.g, parcol.b);
diff --git a/src/client/views/MainOverlayTextBox.tsx b/src/client/views/MainOverlayTextBox.tsx
index c3a2cb214..71fb2707d 100644
--- a/src/client/views/MainOverlayTextBox.tsx
+++ b/src/client/views/MainOverlayTextBox.tsx
@@ -72,7 +72,6 @@ export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps>
         if (this._textTargetDiv) {
             this._textTargetDiv.style.color = this._textColor;
         }
-        this._textAutoHeight = autoHeight;
         this.TextFieldKey = textFieldKey!;
         let txf = tx ? tx : () => Transform.Identity();
         this._textXf = txf;
@@ -143,6 +142,7 @@ export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps>
                                 Document={FormattedTextBox.InputBoxOverlay.props.Document}
                                 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}
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index b64986084..2cec1c052 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -322,6 +322,7 @@ export class MainView extends React.Component {
                             addDocTab={emptyFunction}
                             pinToPres={emptyFunction}
                             onClick={undefined}
+                            ruleProvider={undefined}
                             removeDocument={undefined}
                             ScreenToLocalTransform={Transform.Identity}
                             ContentScaling={returnOne}
@@ -385,6 +386,7 @@ export class MainView extends React.Component {
             addDocTab={this.addDocTabFunc}
             pinToPres={emptyFunction}
             removeDocument={undefined}
+            ruleProvider={undefined}
             onClick={undefined}
             ScreenToLocalTransform={Transform.Identity}
             ContentScaling={returnOne}
diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx
index 0ef1a137d..060191e29 100644
--- a/src/client/views/TemplateMenu.tsx
+++ b/src/client/views/TemplateMenu.tsx
@@ -51,16 +51,16 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
     @observable private _hidden: boolean = true;
     dragRef = React.createRef<HTMLUListElement>();
 
-    constructor(props: TemplateMenuProps) {
-        super(props);
-    }
-
     toggleCustom = (e: React.MouseEvent): void => {
         this.props.docs.map(dv => {
-            if (dv.Document.type !== DocumentType.COL && dv.Document.type !== DocumentType.TEMPLATE) {
-                dv.makeCustomViewClicked();
-            } else if (dv.Document.nativeLayout) {
-                dv.makeNativeViewClicked();
+            if (dv.props.ContainingCollectionView && dv.props.ContainingCollectionView.props.DataDoc) {
+                Doc.MakeMetadataFieldTemplate(dv.props.Document, dv.props.ContainingCollectionView.props.DataDoc)
+            } else {
+                if (dv.Document.type !== DocumentType.COL && dv.Document.type !== DocumentType.TEMPLATE) {
+                    dv.makeCustomViewClicked();
+                } else if (dv.Document.nativeLayout) {
+                    dv.makeNativeViewClicked();
+                }
             }
         });
     }
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index fb8b0c41b..166fa0811 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -631,6 +631,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
             bringToFront={emptyFunction}
             addDocument={undefined}
             removeDocument={undefined}
+            ruleProvider={undefined}
             ContentScaling={this.contentScaling}
             PanelWidth={this.panelWidth}
             PanelHeight={this.panelHeight}
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 9d83aa6c1..dca1d7c1d 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -995,6 +995,7 @@ export class CollectionSchemaPreview extends React.Component<CollectionSchemaPre
                         Document={this.props.Document}
                         fitToBox={this.props.fitToBox}
                         onClick={this.props.onClick}
+                        ruleProvider={undefined}
                         showOverlays={this.props.showOverlays}
                         addDocument={this.props.addDocument}
                         removeDocument={this.props.removeDocument}
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 99e5ab7b3..270916522 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -34,6 +34,7 @@ export interface CollectionViewProps extends FieldViewProps {
 
 export interface SubCollectionViewProps extends CollectionViewProps {
     CollectionView: CollectionView | CollectionPDFView | CollectionVideoView;
+    ruleProvider: Doc | undefined;
 }
 
 export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 9a8ae3535..4a3e5039a 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -257,18 +257,14 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
     private getLocalTransform = (): Transform => Transform.Identity().scale(1 / this.zoomScaling()).translate(this.panX(), this.panY());
     private addLiveTextBox = (newBox: Doc) => {
         FormattedTextBox.SelectOnLoad = newBox[Id];// track the new text box so we can give it a prop that tells it to focus itself when it's displayed
-        let heading = this.childDocs.reduce((maxHeading, doc) => NumCast(doc.heading) > maxHeading ? NumCast(doc.heading) : maxHeading, 0);
-        heading = heading === 0 || this.childDocs.length === 0 ? 1 : heading === 1 ? 2 : 0;
+        let maxHeading = this.childDocs.reduce((maxHeading, doc) => NumCast(doc.heading) > maxHeading ? NumCast(doc.heading) : maxHeading, 0);
+        let heading = maxHeading === 0 || this.childDocs.length === 0 ? 1 : maxHeading === 1 ? 2 : 0;
         if (heading === 0) {
             let sorted = this.childDocs.filter(d => d.type === DocumentType.TEXT && d.data_ext instanceof Doc && d.data_ext.lastModified).sort((a, b) => DateCast((Cast(a.data_ext, Doc) as Doc).lastModified).date > DateCast((Cast(b.data_ext, Doc) as Doc).lastModified).date ? 1 :
                 DateCast((Cast(a.data_ext, Doc) as Doc).lastModified).date < DateCast((Cast(b.data_ext, Doc) as Doc).lastModified).date ? -1 : 0);
-            heading = !sorted.length ? 1 : NumCast(sorted[sorted.length - 1].heading) === 1 ? 2 : NumCast(sorted[sorted.length - 1].heading);
-        }
-        newBox.heading = heading;
-
-        if (Cast(this.props.Document.ruleProvider, Doc) as Doc) {
-            newBox.ruleProvider = Doc.GetProto(Cast(this.props.Document.ruleProvider, Doc) as Doc);
+            heading = !sorted.length ? Math.max(1, maxHeading) : NumCast(sorted[sorted.length - 1].heading) === 1 ? 2 : NumCast(sorted[sorted.length - 1].heading);
         }
+        !this.props.Document.isRuleProvider && (newBox.heading = heading);
         this.addDocument(newBox, false);
     }
     private addDocument = (newBox: Doc, allowDuplicates: boolean) => {
@@ -698,6 +694,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
             addDocument: this.props.addDocument,
             removeDocument: this.props.removeDocument,
             moveDocument: this.props.moveDocument,
+            ruleProvider: this.props.Document.isRuleProvider && childLayout.type !== DocumentType.TEXT ? this.props.Document : this.props.ruleProvider,
             onClick: this.props.onClick,
             ScreenToLocalTransform: childLayout.z ? this.getTransformOverlay : this.getTransform,
             renderDepth: this.props.renderDepth + 1,
@@ -723,6 +720,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
             addDocument: this.props.addDocument,
             removeDocument: this.props.removeDocument,
             moveDocument: this.props.moveDocument,
+            ruleProvider: this.props.ruleProvider,
             onClick: this.props.onClick,
             ScreenToLocalTransform: this.getTransform,
             renderDepth: this.props.renderDepth,
@@ -817,6 +815,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
                     if (pair.layout && !(pair.data instanceof Promise)) {
                         prev.push({
                             ele: <CollectionFreeFormDocumentView key={doc[Id]}
+                                ruleProvider={this.props.Document.isRuleProvider ? this.props.Document : this.props.ruleProvider}
                                 jitterRotation={NumCast(this.props.Document.jitterRotation)}
                                 x={script ? pos.x : undefined} y={script ? pos.y : undefined}
                                 width={script ? pos.width : undefined} height={script ? pos.height : undefined} {...this.getChildDocumentViewProps(pair.layout, pair.data)} />,
@@ -873,6 +872,11 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
         }, "arrange contents");
     }
 
+    autoFormat = () => {
+        this.props.Document.isRuleProvider = !this.props.Document.isRuleProvider;
+        this.childDocs.map(child => child.heading = undefined);
+    }
+
     analyzeStrokes = async () => {
         let data = Cast(this.fieldExtensionDoc[this.inkKey], InkField);
         if (!data) {
@@ -900,11 +904,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
             },
             icon: !this.props.Document.useClusters ? "braille" : "braille"
         });
-        layoutItems.push({
-            description: `${this.props.Document.isRuleProvider ? "Stop Auto Format" : "Auto Format"}`,
-            event: () => this.props.Document.isRuleProvider = !this.props.Document.isRuleProvider,
-            icon: !this.props.Document.useClusters ? "chalkboard" : "chalkboard"
-        });
+        layoutItems.push({ description: `${this.props.Document.isRuleProvider ? "Stop Auto Format" : "Auto Format"}`, event: this.autoFormat, icon: !this.props.Document.isRuleProvider ? "chalkboard" : "chalkboard" });
         layoutItems.push({ description: "Arrange contents in grid", event: this.arrangeContents, icon: "table" });
         layoutItems.push({ description: "Analyze Strokes", event: this.analyzeStrokes, icon: "paint-brush" });
         layoutItems.push({ description: "Jitter Rotation", event: action(() => this.props.Document.jitterRotation = 10), icon: "paint-brush" });
@@ -1034,7 +1034,6 @@ class CollectionFreeFormOverlayView extends React.Component<DocumentViewProps &
 @observer
 class CollectionFreeFormBackgroundView extends React.Component<DocumentViewProps & { isSelected: () => boolean }> {
     @computed get backgroundView() {
-        let props = this.props;
         return (<DocumentContentsView {...this.props} layoutKey={"backgroundLayout"}
             renderDepth={this.props.renderDepth} isSelected={this.props.isSelected} select={emptyFunction} />);
     }
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 4308497a1..e46e8cb88 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -329,7 +329,6 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
                 selected = [newCollection];
                 newCollection.x = bounds.left + bounds.width;
                 summary.proto!.subBulletDocs = new List<Doc>(selected);
-                summary.templates = new List<string>([Templates.Bullet.Layout]);
                 let container = Docs.Create.FreeformDocument([summary, newCollection], { x: bounds.left, y: bounds.top, width: 300, height: 200, chromeStatus: "disabled", title: "-summary-" });
                 container.viewType = CollectionViewType.Stacking;
                 container.autoHeight = true;
@@ -356,7 +355,6 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
                 this.props.addLiveTextDocument(summary);
             }
             else {
-                newCollection.ruleProvider = this.props.container.props.Document.isRuleProvider ? this.props.container.props.Document : this.props.container.props.Document.ruleProvider;
                 this.props.addDocument(newCollection, false);
                 this.props.selectDocuments([newCollection]);
             }
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 082e5c5e3..4872a7aa1 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -74,7 +74,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
     }
 
     borderRounding = () => {
-        let ruleProvider = this.props.Document.ruleProvider as Doc;
+        let ruleProvider = this.props.ruleProvider;
         let ruleRounding = ruleProvider ? StrCast(Doc.GetProto(ruleProvider)["ruleRounding_" + NumCast(this.props.Document.heading)]) : undefined;
         let br = StrCast(this.layoutDoc.layout instanceof Doc ? this.layoutDoc.layout.borderRounding : this.props.Document.borderRounding);
         br = !br && ruleRounding ? ruleRounding : br;
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 0816cb813..cc04c5a9f 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1,6 +1,6 @@
 import { library } from '@fortawesome/fontawesome-svg-core';
 import * as fa from '@fortawesome/free-solid-svg-icons';
-import { action, computed, IReactionDisposer, reaction, runInAction, trace, observable } from "mobx";
+import { action, computed, IReactionDisposer, reaction, runInAction } from "mobx";
 import { observer } from "mobx-react";
 import * as rp from "request-promise";
 import { Doc, DocListCast, DocListCastAsync, HeightSym, Opt, WidthSym } from "../../../new_fields/Doc";
@@ -9,12 +9,13 @@ import { List } from "../../../new_fields/List";
 import { ObjectField } from "../../../new_fields/ObjectField";
 import { createSchema, listSpec, makeInterface } from "../../../new_fields/Schema";
 import { ScriptField } from '../../../new_fields/ScriptField';
-import { BoolCast, Cast, FieldValue, NumCast, StrCast, PromiseValue } from "../../../new_fields/Types";
+import { BoolCast, Cast, FieldValue, NumCast, PromiseValue, StrCast } from "../../../new_fields/Types";
 import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils";
 import { RouteStore } from '../../../server/RouteStore';
 import { emptyFunction, returnTrue, Utils } from "../../../Utils";
 import { DocServer } from "../../DocServer";
 import { Docs, DocUtils } from "../../documents/Documents";
+import { DocumentType } from '../../documents/DocumentTypes';
 import { ClientUtils } from '../../util/ClientUtils';
 import { DictationManager } from '../../util/DictationManager';
 import { DocumentManager } from "../../util/DocumentManager";
@@ -35,12 +36,10 @@ import { MainView } from '../MainView';
 import { OverlayView } from '../OverlayView';
 import { ScriptBox } from '../ScriptBox';
 import { ScriptingRepl } from '../ScriptingRepl';
-import { Template } from "./../Templates";
 import { DocumentContentsView } from "./DocumentContentsView";
 import "./DocumentView.scss";
 import { FormattedTextBox } from './FormattedTextBox';
 import React = require("react");
-import { DocumentType } from '../../documents/DocumentTypes';
 const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this?
 
 library.add(fa.faTrash);
@@ -89,6 +88,7 @@ export interface DocumentViewProps {
     renderDepth: number;
     showOverlays?: (doc: Doc) => { title?: string, caption?: string };
     ContentScaling: () => number;
+    ruleProvider: Doc | undefined;
     PanelWidth: () => number;
     PanelHeight: () => number;
     focus: (doc: Doc, willZoom: boolean, scale?: number) => void;
@@ -470,6 +470,9 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                     this.props.Document.type === DocumentType.VID ? Docs.Create.VideoDocument("http://www.cs.brown.edu", options) :
                         Docs.Create.ImageDocument("http://www.cs.brown.edu", options);
 
+                fieldTemplate.backgroundColor = StrCast(this.props.Document.backgroundColor);
+                fieldTemplate.heading = 1;
+
                 let docTemplate = Docs.Create.FreeformDocument([fieldTemplate], { title: StrCast(this.Document.title) + "layout", width: NumCast(this.props.Document.width) + 20, height: Math.max(100, NumCast(this.props.Document.height) + 45) });
                 let proto = Doc.GetProto(docTemplate);
                 Doc.MakeMetadataFieldTemplate(fieldTemplate, proto);
@@ -800,7 +803,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
 
 
     render() {
-        let ruleProvider = this.props.Document.ruleProvider as Doc;
+        let ruleProvider = this.props.ruleProvider;
         let ruleColor = ruleProvider ? StrCast(Doc.GetProto(ruleProvider)["ruleColor_" + NumCast(this.props.Document.heading)]) : undefined;
         let ruleRounding = ruleProvider ? StrCast(Doc.GetProto(ruleProvider)["ruleRounding_" + NumCast(this.props.Document.heading)]) : undefined;
         let colorSet = this.layoutDoc.backgroundColor !== this.layoutDoc.defaultBackgroundColor;
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index d9774303b..943d181d6 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>;
+    ruleProvider: Doc | undefined;
     Document: Doc;
     DataDoc?: Doc;
     onClick?: ScriptField;
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index d39291743..a0dc054cf 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -458,7 +458,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
 
 
         this._rulesReactionDisposer = reaction(() => {
-            let ruleProvider = Cast(this.props.Document.ruleProvider, Doc);
+            let ruleProvider = this.props.ruleProvider;
             let heading = NumCast(this.props.Document.heading);
             if (ruleProvider instanceof Doc) {
                 return {
diff --git a/src/client/views/presentationview/PresentationElement.tsx b/src/client/views/presentationview/PresentationElement.tsx
index 80aa25f48..7be44faf6 100644
--- a/src/client/views/presentationview/PresentationElement.tsx
+++ b/src/client/views/presentationview/PresentationElement.tsx
@@ -351,6 +351,7 @@ export default class PresentationElement extends React.Component<PresentationEle
                     Document={this.props.document}
                     addDocument={returnFalse}
                     removeDocument={returnFalse}
+                    ruleProvider={undefined}
                     ScreenToLocalTransform={Transform.Identity}
                     addDocTab={returnFalse}
                     pinToPres={returnFalse}
diff --git a/src/client/views/search/SearchItem.tsx b/src/client/views/search/SearchItem.tsx
index c56d093fa..cd3dd912c 100644
--- a/src/client/views/search/SearchItem.tsx
+++ b/src/client/views/search/SearchItem.tsx
@@ -165,6 +165,7 @@ export class SearchItem extends React.Component<SearchItemProps> {
                     Document={this.props.doc}
                     addDocument={returnFalse}
                     removeDocument={returnFalse}
+                    ruleProvider={undefined}
                     ScreenToLocalTransform={Transform.Identity}
                     addDocTab={returnFalse}
                     pinToPres={returnFalse}
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index 1a3d689bb..5b22a62a1 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -560,7 +560,7 @@ export namespace Doc {
 
     export function MakeMetadataFieldTemplate(fieldTemplate: Doc, templateDataDoc: Doc) {
         // move data doc fields to layout doc as needed (nativeWidth/nativeHeight, data, ??)
-        let metadataFieldName = StrCast(fieldTemplate.title);
+        let metadataFieldName = StrCast(fieldTemplate.title).replace(/^-/, "");
         let backgroundLayout = StrCast(fieldTemplate.backgroundLayout);
         let fieldLayoutDoc = fieldTemplate;
         if (fieldTemplate.layout instanceof Doc) {
-- 
cgit v1.2.3-70-g09d2


From dd7679295b84ceba49b8d581bb64f97cc1a86fbb Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Fri, 13 Sep 2019 17:30:02 -0400
Subject: more rule provider fixes

---
 src/client/views/DocumentDecorations.tsx                        | 2 +-
 src/client/views/InkingControl.tsx                              | 8 +++++++-
 src/client/views/collections/CollectionSchemaCells.tsx          | 1 +
 src/client/views/collections/CollectionSchemaView.tsx           | 5 ++++-
 src/client/views/collections/CollectionStackingView.tsx         | 1 +
 src/client/views/collections/CollectionTreeView.tsx             | 3 +++
 src/client/views/collections/collectionFreeForm/MarqueeView.tsx | 2 +-
 src/client/views/nodes/DocumentView.tsx                         | 2 +-
 src/client/views/nodes/FormattedTextBox.tsx                     | 2 ++
 src/client/views/nodes/KeyValuePair.tsx                         | 1 +
 10 files changed, 22 insertions(+), 5 deletions(-)

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

diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index ebdf2a749..ac103b2ea 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -848,7 +848,7 @@ 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.props.Document["show" + template.Name] !== undefined));
+            SelectionManager.SelectedDocuments().map(doc => checked = checked || (doc.layoutDoc["show" + template.Name] !== undefined));
             templates.set(template, checked);
         });
 
diff --git a/src/client/views/InkingControl.tsx b/src/client/views/InkingControl.tsx
index 86d0fc0be..94cc1f06c 100644
--- a/src/client/views/InkingControl.tsx
+++ b/src/client/views/InkingControl.tsx
@@ -51,7 +51,13 @@ export class InkingControl extends React.Component {
             let oldColors = selected.map(view => {
                 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);
-                if (view.props.ContainingCollectionView && view.props.ContainingCollectionView.props.Document.colorPalette) {
+                if (view.props.ContainingCollectionView) {
+                    if (!view.props.ContainingCollectionView.props.Document.colorPalette) {
+                        let defaultPalette = ["rg14,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(view.props.ContainingCollectionView.props.Document.colorPalette, listSpec("string"));
+                        if (!colorPalette) view.props.ContainingCollectionView.props.Document.colorPalette = new List<string>(defaultPalette);
+                    }
                     let cp = Cast(view.props.ContainingCollectionView.props.Document.colorPalette, listSpec("string")) as string[];
                     let closest = 0;
                     let dist = 10000000;
diff --git a/src/client/views/collections/CollectionSchemaCells.tsx b/src/client/views/collections/CollectionSchemaCells.tsx
index c59107b53..17a3f4f7c 100644
--- a/src/client/views/collections/CollectionSchemaCells.tsx
+++ b/src/client/views/collections/CollectionSchemaCells.tsx
@@ -149,6 +149,7 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
             DataDoc: this.props.rowProps.original,
             fieldKey: this.props.rowProps.column.id as string,
             fieldExt: "",
+            ruleProvider: undefined,
             ContainingCollectionView: this.props.CollectionView,
             isSelected: returnFalse,
             select: emptyFunction,
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index dca1d7c1d..1a84f94c8 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -32,6 +32,7 @@ import { CellProps, CollectionSchemaCell, CollectionSchemaNumberCell, Collection
 import { MovableColumn, MovableRow } from "./CollectionSchemaMovableTableHOC";
 import { ComputedField, ScriptField } from "../../../new_fields/ScriptField";
 import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField";
+import { DocumentType } from "../../documents/DocumentTypes";
 
 
 library.add(faCog, faPlus, faSortUp, faSortDown);
@@ -161,6 +162,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
                 DataDocument={this.previewDocument !== this.props.DataDoc ? this.props.DataDoc : undefined}
                 childDocs={this.childDocs}
                 renderDepth={this.props.renderDepth}
+                ruleProvider={this.props.Document.isRuleProvider && layoutDoc && layoutDoc.type !== DocumentType.TEXT ? this.props.Document : this.props.ruleProvider}
                 width={this.previewWidth}
                 height={this.previewHeight}
                 getTransform={this.getPreviewTransform}
@@ -901,6 +903,7 @@ interface CollectionSchemaPreviewProps {
     fitToBox?: boolean;
     width: () => number;
     height: () => number;
+    ruleProvider: Doc | undefined;
     showOverlays?: (doc: Doc) => { title?: string, caption?: string };
     CollectionView?: CollectionView | CollectionPDFView | CollectionVideoView;
     onClick?: ScriptField;
@@ -995,7 +998,7 @@ export class CollectionSchemaPreview extends React.Component<CollectionSchemaPre
                         Document={this.props.Document}
                         fitToBox={this.props.fitToBox}
                         onClick={this.props.onClick}
-                        ruleProvider={undefined}
+                        ruleProvider={this.props.ruleProvider}
                         showOverlays={this.props.showOverlays}
                         addDocument={this.props.addDocument}
                         removeDocument={this.props.removeDocument}
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 91e10b0ac..89cccedff 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -144,6 +144,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
             DataDocument={dataDoc}
             showOverlays={this.overlays}
             renderDepth={this.props.renderDepth}
+            ruleProvider={this.props.Document.isRuleProvider && layoutDoc.type !== DocumentType.TEXT ? this.props.Document : this.props.ruleProvider}
             fitToBox={this.props.fitToBox}
             onClick={layoutDoc.isTemplate ? this.onClickHandler : this.onChildClickHandler}
             width={width}
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index f5bb76966..b1e063997 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -37,6 +37,7 @@ export interface TreeViewProps {
     containingCollection: Doc;
     renderDepth: number;
     deleteDoc: (doc: Doc) => boolean;
+    ruleProvider: Doc | undefined;
     moveDocument: DragManager.MoveFunction;
     dropAction: "alias" | "copy" | undefined;
     addDocTab: (doc: Doc, dataDoc: Doc | undefined, where: string) => void;
@@ -324,6 +325,7 @@ class TreeView extends React.Component<TreeViewProps> {
                     DataDocument={this.resolvedDataDoc}
                     renderDepth={this.props.renderDepth}
                     showOverlays={this.noOverlays}
+                    ruleProvider={this.props.document.isRuleProvider && layoutDoc.type !== DocumentType.TEXT ? this.props.document : this.props.ruleProvider}
                     fitToBox={this.boundsOfCollectionDocument !== undefined}
                     width={this.docWidth}
                     height={this.docHeight}
@@ -491,6 +493,7 @@ class TreeView extends React.Component<TreeViewProps> {
                 dataDoc={pair.data}
                 containingCollection={containingCollection}
                 treeViewId={treeViewId}
+                ruleProvider={containingCollection.isRuleProvider && pair.layout.type !== DocumentType.TEXT ? containingCollection : containingCollection.ruleProvider as Doc}
                 key={child[Id]}
                 indentDocument={indent}
                 renderDepth={renderDepth}
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index e46e8cb88..cc5e887b2 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -287,7 +287,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
             let palette = Array.from(Cast(this.props.container.props.Document.colorPalette, listSpec("string")) as string[]);
             let usedPaletted = new Map<string, number>();
             [...this.props.activeDocuments(), this.props.container.props.Document].map(child => {
-                let bg = StrCast(child.backgroundColor);
+                let bg = StrCast(child.layout instanceof Doc ? child.layout.backgroundColor : child.backgroundColor);
                 if (palette.indexOf(bg) !== -1) {
                     palette.splice(palette.indexOf(bg), 1);
                     if (usedPaletted.get(bg)) usedPaletted.set(bg, usedPaletted.get(bg)! + 1);
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index cc04c5a9f..591a507eb 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -811,7 +811,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
 
         let 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);
+            ruleColor ? ruleColor : StrCast(this.layoutDoc.backgroundColor) || this.props.backgroundColor(this.layoutDoc);
         let foregroundColor = StrCast(this.layoutDoc.color);
         var nativeWidth = this.nativeWidth > 0 && !BoolCast(this.props.Document.ignoreAspect) ? `${this.nativeWidth}px` : "100%";
         var nativeHeight = BoolCast(this.props.Document.ignoreAspect) ? this.props.PanelHeight() / this.props.ContentScaling() : this.nativeHeight > 0 ? `${this.nativeHeight}px` : "100%";
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index a0dc054cf..ffb829825 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -285,6 +285,8 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
         } else if (de.data instanceof DragManager.DocumentDragData) {
             const draggedDoc = de.data.draggedDocuments.length && de.data.draggedDocuments[0];
             if (draggedDoc && draggedDoc.type === DocumentType.TEXT) {
+                // let m = Doc.MakeDelegate(draggedDoc);  // under construction
+                // m.layout = m.layout.replace(/fieldKey={})
                 this.props.Document.layout = draggedDoc.layout instanceof Doc ? draggedDoc.layout : draggedDoc;
                 draggedDoc.isTemplate = true;
                 e.stopPropagation();
diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx
index a27dbd83d..7e0f3735d 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,
+            ruleProvider: undefined,
             fieldKey: this.props.keyName,
             fieldExt: "",
             isSelected: returnFalse,
-- 
cgit v1.2.3-70-g09d2


From 8a4163eedcfd37a5e245c710ffc674c1d16088f8 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Fri, 13 Sep 2019 18:51:13 -0400
Subject: fixed applying template to template

---
 src/client/views/nodes/DocumentView.tsx     |  2 +-
 src/client/views/nodes/FormattedTextBox.tsx | 10 +++++++---
 src/new_fields/Doc.ts                       | 17 +++++++++--------
 3 files changed, 17 insertions(+), 12 deletions(-)

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

diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 591a507eb..3dd384c80 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -285,7 +285,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     }
 
     onClick = async (e: React.MouseEvent) => {
-        if (e.nativeEvent.cancelBubble) return; // needed because EditableView may stopPropagation which won't apparently stop this event from firing.
+        if (e.nativeEvent.cancelBubble || SelectionManager.IsSelected(this)) return; // needed because EditableView may stopPropagation which won't apparently stop this event from firing.
         if (this.onClickHandler && this.onClickHandler.script) {
             e.stopPropagation();
             this.onClickHandler.script.run({ this: this.props.Document.isTemplate && this.props.DataDoc ? this.props.DataDoc : this.props.Document });
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index ffb829825..3e8b01dfd 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -285,10 +285,14 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
         } else if (de.data instanceof DragManager.DocumentDragData) {
             const draggedDoc = de.data.draggedDocuments.length && de.data.draggedDocuments[0];
             if (draggedDoc && draggedDoc.type === DocumentType.TEXT) {
-                // let m = Doc.MakeDelegate(draggedDoc);  // under construction
-                // m.layout = m.layout.replace(/fieldKey={})
-                this.props.Document.layout = draggedDoc.layout instanceof Doc ? draggedDoc.layout : draggedDoc;
                 draggedDoc.isTemplate = true;
+                if (typeof (draggedDoc.layout) === "string") {
+                    let layoutDelegateToOverrideFieldKey = Doc.MakeDelegate(draggedDoc);
+                    layoutDelegateToOverrideFieldKey.layout = StrCast(layoutDelegateToOverrideFieldKey.layout).replace(/fieldKey={"[^"]*"}/, `fieldKey={"${this.props.fieldKey}"}`);
+                    this.props.Document.layout = layoutDelegateToOverrideFieldKey;
+                } else {
+                    this.props.Document.layout = draggedDoc.layout instanceof Doc ? draggedDoc.layout : draggedDoc;
+                }
                 e.stopPropagation();
             }
         }
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index 5b22a62a1..474644dba 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -1,4 +1,4 @@
-import { observable, ObservableMap, runInAction } from "mobx";
+import { observable, ObservableMap, runInAction, action } from "mobx";
 import { alias, map, serializable } from "serializr";
 import { DocServer } from "../client/DocServer";
 import { DocumentType } from "../client/documents/DocumentTypes";
@@ -566,18 +566,13 @@ export namespace Doc {
         if (fieldTemplate.layout instanceof Doc) {
             fieldLayoutDoc = Doc.MakeDelegate(fieldTemplate.layout);
         }
-        let layout = StrCast(fieldLayoutDoc.layout).replace(/fieldKey={"[^"]*"}/, `fieldKey={"${metadataFieldName}"}`);
         if (backgroundLayout) {
             backgroundLayout = backgroundLayout.replace(/fieldKey={"[^"]*"}/, `fieldKey={"${metadataFieldName}"}`);
         }
 
-        let layoutDelegate = fieldTemplate.layout instanceof Doc ? fieldLayoutDoc : fieldTemplate;
-        layoutDelegate.layout = layout;
-
         fieldTemplate.templateField = metadataFieldName;
         fieldTemplate.title = metadataFieldName;
         fieldTemplate.isTemplate = true;
-        fieldTemplate.layout = layoutDelegate !== fieldTemplate ? layoutDelegate : layout;
         fieldTemplate.backgroundLayout = backgroundLayout;
         /* move certain layout properties from the original data doc to the template layout to avoid
            inheriting them from the template's data doc which may also define these fields for its own use.
@@ -591,8 +586,14 @@ export namespace Doc {
         fieldTemplate.scale = 1;
         fieldTemplate.showTitle = "title";
         let data = fieldTemplate.data;
-        !templateDataDoc[metadataFieldName] && data instanceof ObjectField && (templateDataDoc[metadataFieldName] = ObjectField.MakeCopy(data));
-        setTimeout(() => fieldTemplate.proto = templateDataDoc);
+        setTimeout(action(() => {
+            !templateDataDoc[metadataFieldName] && data instanceof ObjectField && (Doc.GetProto(templateDataDoc)[metadataFieldName] = ObjectField.MakeCopy(data));
+            let layout = StrCast(fieldLayoutDoc.layout).replace(/fieldKey={"[^"]*"}/, `fieldKey={"${metadataFieldName}"}`);
+            let layoutDelegate = fieldTemplate.layout instanceof Doc ? fieldLayoutDoc : fieldTemplate;
+            layoutDelegate.layout = layout;
+            fieldTemplate.layout = layoutDelegate !== fieldTemplate ? layoutDelegate : layout;
+            fieldTemplate.proto = templateDataDoc;
+        }), 0);
     }
 
     export function ToggleDetailLayout(d: Doc) {
-- 
cgit v1.2.3-70-g09d2


From d9fa64c229b13f9c8121a40b76d180775be5f6c6 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Sat, 14 Sep 2019 00:33:18 -0400
Subject: fixed color assignments for rule providers.  no titles are shown when
 promoting to a custom layout

---
 src/client/views/InkingControl.tsx                 | 32 ++++++++++------------
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 16 +++++++++++
 src/client/views/nodes/DocumentView.tsx            |  4 +--
 src/client/views/nodes/FormattedTextBox.tsx        | 16 ++++++-----
 src/new_fields/Doc.ts                              |  5 ++--
 5 files changed, 44 insertions(+), 29 deletions(-)

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

diff --git a/src/client/views/InkingControl.tsx b/src/client/views/InkingControl.tsx
index 94cc1f06c..57dad5e6b 100644
--- a/src/client/views/InkingControl.tsx
+++ b/src/client/views/InkingControl.tsx
@@ -51,14 +51,17 @@ export class InkingControl extends React.Component {
             let oldColors = selected.map(view => {
                 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);
-                if (view.props.ContainingCollectionView) {
-                    if (!view.props.ContainingCollectionView.props.Document.colorPalette) {
+                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)",
                             "rgb(209,150,226)", "rgb(127,235,144)", "rgb(252,188,189)", "rgb(247,175,81)",];
-                        let colorPalette = Cast(view.props.ContainingCollectionView.props.Document.colorPalette, listSpec("string"));
-                        if (!colorPalette) view.props.ContainingCollectionView.props.Document.colorPalette = new List<string>(defaultPalette);
+                        let colorPalette = Cast(cv.props.Document.colorPalette, listSpec("string"));
+                        if (!colorPalette) cv.props.Document.colorPalette = new List<string>(defaultPalette);
                     }
-                    let cp = Cast(view.props.ContainingCollectionView.props.Document.colorPalette, listSpec("string")) as string[];
+                    let cp = Cast(cv.props.Document.colorPalette, listSpec("string")) as string[];
                     let closest = 0;
                     let dist = 10000000;
                     let ccol = Utils.fromRGBAstr(StrCast(targetDoc.backgroundColor));
@@ -71,20 +74,13 @@ export class InkingControl extends React.Component {
                         }
                     }
                     cp[closest] = "rgba(" + color.rgb.r + "," + color.rgb.g + "," + color.rgb.b + "," + color.rgb.a + ")";
-                    view.props.ContainingCollectionView.props.Document.colorPalette = new List(cp);
-                    targetDoc.backgroundColor = cp[closest];
-                } else
-                    targetDoc.backgroundColor = this._selectedColor;
-                if (view.props.Document.heading) {
-                    let cv = view.props.ContainingCollectionView;
-                    let ruleProvider = cv && (Cast(cv.props.ruleProvider, Doc) as Doc);
-                    cv && (Doc.GetProto(ruleProvider ? ruleProvider : cv.props.Document)["ruleColor_" + NumCast(view.props.Document.heading)] = Utils.toRGBAstr(color.rgb));
-                    // if (parback && cv && parback.indexOf("rgb") !== -1) {
-                    //     let parcol = Utils.fromRGBAstr(parback);
-                    //     let hsl = Utils.RGBToHSL(parcol.r, parcol.g, parcol.b);
-                    //     cv && ((ruleProvider ? ruleProvider : cv.props.Document)["ruleColor_" + NumCast(view.props.Document.heading)] = color.hsl.s - hsl.s);
-                    // }
+                    cv.props.Document.colorPalette = new List(cp);
+                    matchedColor = cp[closest];
+                    ruleProvider = (view.props.Document.heading && cv && cv.props.ruleProvider) ? cv.props.ruleProvider : undefined;
+                    ruleProvider && ((Doc.GetProto(ruleProvider)["ruleColor_" + NumCast(view.props.Document.heading)] = Utils.toRGBAstr(color.rgb)));
                 }
+                !ruleProvider && (targetDoc.backgroundColor = matchedColor);
+
                 return {
                     target: targetDoc,
                     previous: oldColor
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 4a3e5039a..ad91eb007 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -875,6 +875,22 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
     autoFormat = () => {
         this.props.Document.isRuleProvider = !this.props.Document.isRuleProvider;
         this.childDocs.map(child => child.heading = undefined);
+        this.childDocs.map(child => {
+            DocListCast(child.layout instanceof Doc ? child.layout.data : child.data).map(heading => {
+                let pair = Doc.GetLayoutDataDocPair(this.props.Document, this.props.DataDoc, this.props.fieldKey, heading);
+                let disp = (child.data_ext instanceof Doc) && pair.layout && (child.data_ext[`Layout[${pair.layout[Id]}]`] as Doc);
+                if (disp && NumCast(disp.heading) > 0) {
+                    if (disp.backgroundColor !== disp.defaultBackgroundColor) {
+                        Doc.GetProto(this.props.Document)["ruleColor_" + NumCast(disp.heading)] = disp.backgroundColor;
+                    }
+                }
+                if (pair.layout && NumCast(pair.layout.heading) > 0) {
+                    if (pair.layout.backgroundColor !== pair.layout.defaultBackgroundColor) {
+                        Doc.GetProto(this.props.Document)["ruleColor_" + NumCast(pair.layout.heading)] = pair.layout.backgroundColor;
+                    }
+                }
+            })
+        })
     }
 
     analyzeStrokes = async () => {
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 3dd384c80..d37a0ee59 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -475,7 +475,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
 
                 let docTemplate = Docs.Create.FreeformDocument([fieldTemplate], { title: StrCast(this.Document.title) + "layout", width: NumCast(this.props.Document.width) + 20, height: Math.max(100, NumCast(this.props.Document.height) + 45) });
                 let proto = Doc.GetProto(docTemplate);
-                Doc.MakeMetadataFieldTemplate(fieldTemplate, proto);
+                Doc.MakeMetadataFieldTemplate(fieldTemplate, proto, true);
 
                 Doc.ApplyTemplateTo(docTemplate, this.props.Document, undefined, false);
                 Doc.GetProto(this.dataDoc || this.props.Document).customLayout = this.props.Document.layout;
@@ -811,7 +811,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
 
         let backgroundColor = this.layoutDoc.isBackground || (clusterCol && !colorSet) ?
             this.props.backgroundColor(this.layoutDoc) || StrCast(this.layoutDoc.backgroundColor) :
-            ruleColor ? ruleColor : StrCast(this.layoutDoc.backgroundColor) || this.props.backgroundColor(this.layoutDoc);
+            ruleColor && !colorSet ? ruleColor : StrCast(this.layoutDoc.backgroundColor) || this.props.backgroundColor(this.layoutDoc);
         let foregroundColor = StrCast(this.layoutDoc.color);
         var nativeWidth = this.nativeWidth > 0 && !BoolCast(this.props.Document.ignoreAspect) ? `${this.nativeWidth}px` : "100%";
         var nativeHeight = BoolCast(this.props.Document.ignoreAspect) ? this.props.PanelHeight() / this.props.ContentScaling() : this.nativeHeight > 0 ? `${this.nativeHeight}px` : "100%";
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 3e8b01dfd..2e05268a6 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -285,13 +285,15 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
         } else if (de.data instanceof DragManager.DocumentDragData) {
             const draggedDoc = de.data.draggedDocuments.length && de.data.draggedDocuments[0];
             if (draggedDoc && draggedDoc.type === DocumentType.TEXT) {
-                draggedDoc.isTemplate = true;
-                if (typeof (draggedDoc.layout) === "string") {
-                    let layoutDelegateToOverrideFieldKey = Doc.MakeDelegate(draggedDoc);
-                    layoutDelegateToOverrideFieldKey.layout = StrCast(layoutDelegateToOverrideFieldKey.layout).replace(/fieldKey={"[^"]*"}/, `fieldKey={"${this.props.fieldKey}"}`);
-                    this.props.Document.layout = layoutDelegateToOverrideFieldKey;
-                } else {
-                    this.props.Document.layout = draggedDoc.layout instanceof Doc ? draggedDoc.layout : draggedDoc;
+                if (!Doc.AreProtosEqual(draggedDoc, this.props.Document)) {
+                    draggedDoc.isTemplate = true;
+                    if (typeof (draggedDoc.layout) === "string") {
+                        let layoutDelegateToOverrideFieldKey = Doc.MakeDelegate(draggedDoc);
+                        layoutDelegateToOverrideFieldKey.layout = StrCast(layoutDelegateToOverrideFieldKey.layout).replace(/fieldKey={"[^"]*"}/, `fieldKey={"${this.props.fieldKey}"}`);
+                        this.props.Document.layout = layoutDelegateToOverrideFieldKey;
+                    } else {
+                        this.props.Document.layout = draggedDoc.layout instanceof Doc ? draggedDoc.layout : draggedDoc;
+                    }
                 }
                 e.stopPropagation();
             }
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index 474644dba..b6b3bf73e 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -558,7 +558,7 @@ export namespace Doc {
         }
     }
 
-    export function MakeMetadataFieldTemplate(fieldTemplate: Doc, templateDataDoc: Doc) {
+    export function MakeMetadataFieldTemplate(fieldTemplate: Doc, templateDataDoc: Doc, suppressTitle: boolean = false) {
         // move data doc fields to layout doc as needed (nativeWidth/nativeHeight, data, ??)
         let metadataFieldName = StrCast(fieldTemplate.title).replace(/^-/, "");
         let backgroundLayout = StrCast(fieldTemplate.backgroundLayout);
@@ -584,7 +584,7 @@ export namespace Doc {
         fieldTemplate.panX = 0;
         fieldTemplate.panY = 0;
         fieldTemplate.scale = 1;
-        fieldTemplate.showTitle = "title";
+        fieldTemplate.showTitle = suppressTitle ? undefined : "title";
         let data = fieldTemplate.data;
         setTimeout(action(() => {
             !templateDataDoc[metadataFieldName] && data instanceof ObjectField && (Doc.GetProto(templateDataDoc)[metadataFieldName] = ObjectField.MakeCopy(data));
@@ -592,6 +592,7 @@ export namespace Doc {
             let layoutDelegate = fieldTemplate.layout instanceof Doc ? fieldLayoutDoc : fieldTemplate;
             layoutDelegate.layout = layout;
             fieldTemplate.layout = layoutDelegate !== fieldTemplate ? layoutDelegate : layout;
+            if (fieldTemplate.backgroundColor !== templateDataDoc.defaultBackgroundColor) fieldTemplate.defaultBackgroundColor = fieldTemplate.backgroundColor;
             fieldTemplate.proto = templateDataDoc;
         }), 0);
     }
-- 
cgit v1.2.3-70-g09d2


From 4ec15a9576a27b8290fb37b6959cb13ae76feeaa Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Sat, 14 Sep 2019 11:18:51 -0400
Subject: various fixes for templating and publishing document ids

---
 src/client/documents/Documents.ts                  | 39 ++++++++++-------
 .../CollectionStackingViewFieldColumn.tsx          |  3 ++
 .../views/collections/CollectionTreeView.tsx       |  1 +
 src/client/views/nodes/DocumentView.tsx            | 49 +++++++++++++++++-----
 src/client/views/nodes/FormattedTextBox.tsx        | 28 +++++++++----
 src/new_fields/Doc.ts                              | 23 ++++++++++
 6 files changed, 110 insertions(+), 33 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 2eff73b87..e7ac1e321 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -610,23 +610,30 @@ export namespace DocUtils {
     export function Publish(promoteDoc: Doc, targetID: string, addDoc: any, remDoc: any) {
         targetID = targetID.replace(/^-/, "").replace(/\([0-9]*\)$/, "");
         DocServer.GetRefField(targetID).then(doc => {
-            let copy = doc instanceof Doc ? doc : Doc.MakeCopy(promoteDoc, true, targetID);
-            !doc && (copy.title = undefined) && (Doc.GetProto(copy).title = targetID);
-            addDoc && addDoc(copy);
-            !doc && remDoc && remDoc(promoteDoc);
-            if (!doc) {
-                DocListCastAsync(promoteDoc.links).then(links => {
-                    links && links.map(async link => {
-                        if (link) {
-                            let a1 = await Cast(link.anchor1, Doc);
-                            if (a1 && Doc.AreProtosEqual(a1, promoteDoc)) link.anchor1 = copy;
-                            let a2 = await Cast(link.anchor2, Doc);
-                            if (a2 && Doc.AreProtosEqual(a2, promoteDoc)) link.anchor2 = copy;
-                            LinkManager.Instance.deleteLink(link);
-                            LinkManager.Instance.addLink(link);
-                        }
+            if (promoteDoc !== doc) {
+                let copy = doc as Doc;
+                if (copy) {
+                    Doc.Overwrite(promoteDoc, copy, true);
+                } else {
+                    copy = Doc.MakeCopy(promoteDoc, true, targetID);
+                }
+                !doc && (copy.title = undefined) && (Doc.GetProto(copy).title = targetID);
+                addDoc && addDoc(copy);
+                remDoc && remDoc(promoteDoc);
+                if (!doc) {
+                    DocListCastAsync(promoteDoc.links).then(links => {
+                        links && links.map(async link => {
+                            if (link) {
+                                let a1 = await Cast(link.anchor1, Doc);
+                                if (a1 && Doc.AreProtosEqual(a1, promoteDoc)) link.anchor1 = copy;
+                                let a2 = await Cast(link.anchor2, Doc);
+                                if (a2 && Doc.AreProtosEqual(a2, promoteDoc)) link.anchor2 = copy;
+                                LinkManager.Instance.deleteLink(link);
+                                LinkManager.Instance.addLink(link);
+                            }
+                        })
                     })
-                })
+                }
             }
         });
     }
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index bc4fe7dd7..185bec7a2 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -155,6 +155,9 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
         let key = StrCast(this.props.parent.props.Document.sectionFilter);
         let newDoc = Docs.Create.TextDocument({ height: 18, width: 200, documentText: "@@@" + value, title: value, autoHeight: true });
         newDoc[key] = this.getValue(this.props.heading);
+        let maxHeading = this.props.docList.reduce((maxHeading, doc) => NumCast(doc.heading) > maxHeading ? NumCast(doc.heading) : maxHeading, 0);
+        let heading = maxHeading === 0 || this.props.docList.length === 0 ? 1 : maxHeading === 1 ? 2 : 3;
+        newDoc.heading = heading;
         return this.props.parent.props.addDocument(newDoc);
     }
 
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index b1e063997..6217ef859 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -201,6 +201,7 @@ class TreeView extends React.Component<TreeViewProps> {
                 ContextMenu.Instance.addItem({ description: "Delete Workspace", event: () => this.props.deleteDoc(this.props.document), icon: "trash-alt" });
             }
             ContextMenu.Instance.addItem({ description: "Open Fields", event: () => { let kvp = Docs.Create.KVPDocument(this.props.document, { width: 300, height: 300 }); this.props.addDocTab(kvp, this.props.dataDoc ? this.props.dataDoc : kvp, "onRight"); }, icon: "layer-group" });
+            ContextMenu.Instance.addItem({ description: "Publish", event: () => DocUtils.Publish(this.props.document, StrCast(this.props.document.title), () => { }, () => { }), icon: "file" });
             ContextMenu.Instance.displayMenu(e.pageX > 156 ? e.pageX - 156 : 0, e.pageY - 15);
             e.stopPropagation();
             e.preventDefault();
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index d37a0ee59..d90224eae 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -441,22 +441,39 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     @undoBatch
     makeNativeViewClicked = (): void => {
         this.props.Document.layout = this.props.Document.nativeLayout;
-        this.props.Document.type = this.props.Document.nativeType;
-        this.props.Document.nativeWidth = this.props.Document.nativeNativeWidth;
-        this.props.Document.nativeHeight = this.props.Document.nativeNativeHeight;
-        this.props.Document.ignoreAspect = this.props.Document.nativeIgnoreAspect;
         this.props.Document.nativeLayout = undefined;
-        this.props.Document.nativeNativeWidth = undefined;
-        this.props.Document.nativeNativeHeight = undefined;
-        this.props.Document.nativeIgnoreAspect = undefined;
+        this.props.Document.type = this.props.Document.nativeType;
+
+        this.props.Document.customAutoHeight = this.props.Document.autoHeight;
+        this.props.Document.customWidth = this.props.Document.nativeWidth;
+        this.props.Document.customHeight = this.props.Document.nativeHeight;
+        this.props.Document.customNativeWidth = this.props.Document.nativeWidth;
+        this.props.Document.customNativeHeight = this.props.Document.nativeHeight;
+        this.props.Document.customIgnoreAspect = this.props.Document.ignoreAspect;
+
+        this.props.Document.autoHeight = this.props.Document.nonCustomAutoHeight;
+        this.props.Document.width = this.props.Document.nonCustomWidth;
+        this.props.Document.height = this.props.Document.nonCustomHeight;
+        this.props.Document.nativeWidth = this.props.Document.nonCustomNativeWidth;
+        this.props.Document.nativeHeight = this.props.Document.nonCustomNativeHeight;
+        this.props.Document.ignoreAspect = this.props.Document.nonCustomIgnoreAspect;
+        this.props.Document.nonCustomAutoHeight = undefined;
+        this.props.Document.nonCustomWidth = undefined;
+        this.props.Document.nonCustomHeight = undefined;
+        this.props.Document.nonCustomNativeWidth = undefined;
+        this.props.Document.nonCustomNativeHeight = undefined;
+        this.props.Document.nonCustomIgnoreAspect = undefined;
     }
     @undoBatch
     makeCustomViewClicked = (): void => {
         this.props.Document.nativeLayout = this.props.Document.layout;
         this.props.Document.nativeType = this.props.Document.type;
-        this.props.Document.nativeNativeWidth = this.props.Document.nativeWidth;
-        this.props.Document.nativeNativeHeight = this.props.Document.nativeHeight;
-        this.props.Document.nativeIgnoreAspect = this.props.Document.ignoreAspect;
+        this.props.Document.nonCustomAutoHeight = this.props.Document.autoHeight;
+        this.props.Document.nonCustomWidth = this.props.Document.nativeWidth;
+        this.props.Document.nonCustomHeight = this.props.Document.nativeHeight;
+        this.props.Document.nonCustomNativeWidth = this.props.Document.nativeWidth;
+        this.props.Document.nonCustomNativeHeight = this.props.Document.nativeHeight;
+        this.props.Document.nonCustomIgnoreAspect = this.props.Document.ignoreAspect;
         PromiseValue(Cast(this.props.Document.customLayout, Doc)).then(custom => {
             if (custom) {
                 this.props.Document.type = DocumentType.TEMPLATE;
@@ -464,6 +481,18 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 !custom.nativeWidth && (this.props.Document.nativeWidth = 0);
                 !custom.nativeHeight && (this.props.Document.nativeHeight = 0);
                 !custom.nativeWidth && (this.props.Document.ignoreAspect = true);
+                this.props.Document.autoHeight = this.props.Document.autoHeight;
+                this.props.Document.width = this.props.Document.customWidth;
+                this.props.Document.height = this.props.Document.customHeight;
+                this.props.Document.nativeWidth = this.props.Document.customNativeWidth;
+                this.props.Document.nativeHeight = this.props.Document.customNativeHeight;
+                this.props.Document.ignoreAspect = this.props.Document.ignoreAspect;
+                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: NumCast(this.props.Document.width), height: NumCast(this.props.Document.height) + 25, x: -NumCast(this.props.Document.width) / 2, y: -NumCast(this.props.Document.height) / 2, };
                 let fieldTemplate = this.props.Document.type === DocumentType.TEXT ? Docs.Create.TextDocument(options) :
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 2e05268a6..77e29632e 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -169,6 +169,25 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
         }
     }
 
+    linkOnDeselect: Map<string, string> = new Map();
+
+    doLinkOnDeselect() {
+        Array.from(this.linkOnDeselect.entries()).map(entry => {
+            let key = entry[0];
+            let value = entry[1];
+            let id = Utils.GenerateDeterministicGuid(this.dataDoc[Id] + key);
+            DocServer.GetRefField(value).then(doc => {
+                DocServer.GetRefField(id).then(linkDoc => {
+                    this.dataDoc[key] = doc || Docs.Create.FreeformDocument([], { title: value, width: 500, height: 500 }, value);
+                    DocUtils.Publish(this.dataDoc[key] as Doc, value, this.props.addDocument, this.props.removeDocument);
+                    if (linkDoc) { (linkDoc as Doc).anchor2 = this.dataDoc[key] as Doc; }
+                    else DocUtils.MakeLink(this.dataDoc, this.dataDoc[key] as Doc, undefined, "Ref:" + value, undefined, undefined, id, true);
+                })
+            });
+        })
+        this.linkOnDeselect.clear();
+    }
+
     dispatchTransaction = (tx: Transaction) => {
         if (this._editorView) {
             let metadata = tx.selection.$from.marks().find((m: Mark) => m.type === schema.marks.metadata);
@@ -183,15 +202,9 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
                 if (split.length > 1 && split[1]) {
                     let key = split[0];
                     let value = split[split.length - 1];
+                    this.linkOnDeselect.set(key, value);
 
                     let id = Utils.GenerateDeterministicGuid(this.dataDoc[Id] + key);
-                    DocServer.GetRefField(value).then(doc => {
-                        DocServer.GetRefField(id).then(linkDoc => {
-                            this.dataDoc[key] = doc || Docs.Create.FreeformDocument([], { title: value, width: 500, height: 500 }, value);
-                            if (linkDoc) { (linkDoc as Doc).anchor2 = this.dataDoc[key] as Doc; }
-                            else DocUtils.MakeLink(this.dataDoc, this.dataDoc[key] as Doc, undefined, "Ref:" + value, undefined, undefined, id, true);
-                        })
-                    });
                     const link = this._editorView!.state.schema.marks.link.create({ href: `http://localhost:1050/doc/${id}`, location: "onRight", title: value });
                     const mval = this._editorView!.state.schema.marks.metadataVal.create();
                     let offset = (tx.selection.to === range!.end - 1 ? -1 : 0);
@@ -875,6 +888,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
             this._undoTyping.end();
             this._undoTyping = undefined;
         }
+        this.doLinkOnDeselect();
     }
     onKeyPress = (e: React.KeyboardEvent) => {
         if (e.key === "Escape") {
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index b6b3bf73e..eef14ad25 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -475,6 +475,29 @@ export namespace Doc {
         return { layout: layoutDoc, data: resolvedDataDoc };
     }
 
+    export function Overwrite(doc: Doc, overwrite: Doc, copyProto: boolean = false): Doc {
+        Object.keys(doc).forEach(key => {
+            const field = ProxyField.WithoutProxy(() => doc[key]);
+            if (key === "proto" && copyProto) {
+                if (doc.proto instanceof Doc && overwrite.proto instanceof Doc) {
+                    overwrite[key] = Doc.Overwrite(doc[key]!, overwrite.proto);
+                }
+            } else {
+                if (field instanceof RefField) {
+                    overwrite[key] = field;
+                } else if (field instanceof ObjectField) {
+                    overwrite[key] = ObjectField.MakeCopy(field);
+                } else if (field instanceof Promise) {
+                    debugger; //This shouldn't happend...
+                } else {
+                    overwrite[key] = field;
+                }
+            }
+        });
+
+        return overwrite;
+    }
+
     export function MakeCopy(doc: Doc, copyProto: boolean = false, copyProtoId?: string): Doc {
         const copy = new Doc(copyProtoId, true);
         Object.keys(doc).forEach(key => {
-- 
cgit v1.2.3-70-g09d2


From e125267541968eff8b2bd463b23daf2f79a841b9 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Sat, 14 Sep 2019 11:26:46 -0400
Subject: typo

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

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

diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index d90224eae..a302a7a07 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -445,8 +445,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         this.props.Document.type = this.props.Document.nativeType;
 
         this.props.Document.customAutoHeight = this.props.Document.autoHeight;
-        this.props.Document.customWidth = this.props.Document.nativeWidth;
-        this.props.Document.customHeight = this.props.Document.nativeHeight;
+        this.props.Document.customWidth = this.props.Document.width;
+        this.props.Document.customHeight = this.props.Document.height;
         this.props.Document.customNativeWidth = this.props.Document.nativeWidth;
         this.props.Document.customNativeHeight = this.props.Document.nativeHeight;
         this.props.Document.customIgnoreAspect = this.props.Document.ignoreAspect;
-- 
cgit v1.2.3-70-g09d2


From 445b77d13e382542b262cf12e647ed20160dfeaf Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Sat, 14 Sep 2019 11:43:34 -0400
Subject: fixed autoHeight on custom view

---
 src/client/views/nodes/DocumentView.tsx | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

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

diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index a302a7a07..f7e4b3d21 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -494,13 +494,14 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 this.props.Document.customNativeHeight = undefined;
                 this.props.Document.customIgnoreAspect = undefined;
             } else {
-                let options = { title: "data", width: NumCast(this.props.Document.width), height: NumCast(this.props.Document.height) + 25, x: -NumCast(this.props.Document.width) / 2, y: -NumCast(this.props.Document.height) / 2, };
+                let options = { title: "data", width: NumCast(this.props.Document.width), x: -NumCast(this.props.Document.width) / 2, y: -NumCast(this.props.Document.height) / 2, };
                 let fieldTemplate = this.props.Document.type === DocumentType.TEXT ? Docs.Create.TextDocument(options) :
                     this.props.Document.type === DocumentType.VID ? Docs.Create.VideoDocument("http://www.cs.brown.edu", options) :
                         Docs.Create.ImageDocument("http://www.cs.brown.edu", options);
 
                 fieldTemplate.backgroundColor = StrCast(this.props.Document.backgroundColor);
                 fieldTemplate.heading = 1;
+                fieldTemplate.autoHeight = true;
 
                 let docTemplate = Docs.Create.FreeformDocument([fieldTemplate], { title: StrCast(this.Document.title) + "layout", width: NumCast(this.props.Document.width) + 20, height: Math.max(100, NumCast(this.props.Document.height) + 45) });
                 let proto = Doc.GetProto(docTemplate);
-- 
cgit v1.2.3-70-g09d2


From 2f1dbc8906d6428e8b3b9a973d6bab807eabd025 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Sat, 14 Sep 2019 12:58:45 -0400
Subject: fixed toggle custom on click behavior

---
 src/client/views/TemplateMenu.tsx       |  12 +---
 src/client/views/nodes/DocumentView.tsx | 116 +++++++++++++++++++++++++-------
 2 files changed, 93 insertions(+), 35 deletions(-)

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

diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx
index 060191e29..af3fcaa24 100644
--- a/src/client/views/TemplateMenu.tsx
+++ b/src/client/views/TemplateMenu.tsx
@@ -52,17 +52,7 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
     dragRef = React.createRef<HTMLUListElement>();
 
     toggleCustom = (e: React.MouseEvent): void => {
-        this.props.docs.map(dv => {
-            if (dv.props.ContainingCollectionView && dv.props.ContainingCollectionView.props.DataDoc) {
-                Doc.MakeMetadataFieldTemplate(dv.props.Document, dv.props.ContainingCollectionView.props.DataDoc)
-            } else {
-                if (dv.Document.type !== DocumentType.COL && dv.Document.type !== DocumentType.TEMPLATE) {
-                    dv.makeCustomViewClicked();
-                } else if (dv.Document.nativeLayout) {
-                    dv.makeNativeViewClicked();
-                }
-            }
-        });
+        this.props.docs.map(dv => dv.toggleCustomView());
     }
 
     toggleFloat = (e: React.MouseEvent): void => {
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index f7e4b3d21..e95f484a4 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -40,6 +40,7 @@ import { DocumentContentsView } from "./DocumentContentsView";
 import "./DocumentView.scss";
 import { FormattedTextBox } from './FormattedTextBox';
 import React = require("react");
+import { CompileScript, Scripting } from '../../util/Scripting';
 const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this?
 
 library.add(fa.faTrash);
@@ -440,29 +441,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
 
     @undoBatch
     makeNativeViewClicked = (): void => {
-        this.props.Document.layout = this.props.Document.nativeLayout;
-        this.props.Document.nativeLayout = undefined;
-        this.props.Document.type = this.props.Document.nativeType;
-
-        this.props.Document.customAutoHeight = this.props.Document.autoHeight;
-        this.props.Document.customWidth = this.props.Document.width;
-        this.props.Document.customHeight = this.props.Document.height;
-        this.props.Document.customNativeWidth = this.props.Document.nativeWidth;
-        this.props.Document.customNativeHeight = this.props.Document.nativeHeight;
-        this.props.Document.customIgnoreAspect = this.props.Document.ignoreAspect;
-
-        this.props.Document.autoHeight = this.props.Document.nonCustomAutoHeight;
-        this.props.Document.width = this.props.Document.nonCustomWidth;
-        this.props.Document.height = this.props.Document.nonCustomHeight;
-        this.props.Document.nativeWidth = this.props.Document.nonCustomNativeWidth;
-        this.props.Document.nativeHeight = this.props.Document.nonCustomNativeHeight;
-        this.props.Document.ignoreAspect = this.props.Document.nonCustomIgnoreAspect;
-        this.props.Document.nonCustomAutoHeight = undefined;
-        this.props.Document.nonCustomWidth = undefined;
-        this.props.Document.nonCustomHeight = undefined;
-        this.props.Document.nonCustomNativeWidth = undefined;
-        this.props.Document.nonCustomNativeHeight = undefined;
-        this.props.Document.nonCustomIgnoreAspect = undefined;
+        makeNativeView(this.props.Document);
     }
     @undoBatch
     makeCustomViewClicked = (): void => {
@@ -620,6 +599,19 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             })
         }
     }
+    @undoBatch
+    @action
+    toggleCustomView = (): void => {
+        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) {
+                this.makeCustomViewClicked();
+            } else if (this.Document.nativeLayout) {
+                this.makeNativeViewClicked();
+            }
+        }
+    }
 
     @undoBatch
     @action
@@ -671,6 +663,18 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         let existingOnClick = ContextMenu.Instance.findByDescription("OnClick...");
         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: () => {
+                let compiled = CompileScript("toggleDetail(this)", {
+                    params: { this: "Doc" },
+                    typecheck: false,
+                    editable: true,
+                });
+                if (compiled.compiled) {
+                    this.Document.onClick = new ScriptField(compiled);
+                }
+            }, 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.props.Document.isButton || this.props.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) });
@@ -921,4 +925,68 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             </div>
         );
     }
-}
\ No newline at end of file
+}
+
+
+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;
+    }
+}
+Scripting.addGlobal(function toggleDetail(doc: any) {
+    if (doc.type !== DocumentType.COL && doc.type !== DocumentType.TEMPLATE) {
+        makeCustomView(doc);
+    } else if (doc.nativeLayout) {
+        makeNativeView(doc);
+    }
+});
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2


From 827cee4d7ac4f1e761b141097d20671e7873bea8 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Sun, 15 Sep 2019 01:14:21 -0400
Subject: changed moveDocument to move the draggedDoc -- its up to the caller
 to add the droppedDoc if it wants to. cleaned up some old code related to
 passing dataDoc along with doc's to drag and other functinos

---
 src/client/util/DragManager.ts                     | 16 ++++-----
 src/client/views/DocumentDecorations.tsx           | 20 +++++------
 src/client/views/MainOverlayTextBox.tsx            |  2 +-
 src/client/views/TemplateMenu.tsx                  |  2 +-
 .../views/collections/CollectionBaseView.tsx       | 13 ++++---
 .../views/collections/CollectionDockingView.tsx    | 14 +++-----
 .../views/collections/CollectionSchemaCells.tsx    |  3 +-
 .../views/collections/CollectionSchemaView.tsx     |  2 +-
 .../CollectionStackingViewFieldColumn.tsx          |  2 +-
 src/client/views/collections/CollectionSubView.tsx |  8 +++--
 src/client/views/collections/CollectionView.tsx    |  3 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  2 +-
 .../collections/collectionFreeForm/MarqueeView.tsx | 40 +++++++---------------
 src/client/views/linking/LinkMenuGroup.tsx         |  2 +-
 src/client/views/nodes/DocumentView.tsx            | 35 ++++++-------------
 src/client/views/nodes/DragBox.tsx                 |  2 +-
 src/client/views/pdf/Page.tsx                      |  2 +-
 src/client/views/search/SearchItem.tsx             |  2 +-
 18 files changed, 67 insertions(+), 103 deletions(-)

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

diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 4c9c9c17c..252accefa 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -32,7 +32,7 @@ export function SetupDrag(
         document.removeEventListener("pointermove", onRowMove);
         document.removeEventListener('pointerup', onRowUp);
         let doc = await docFunc();
-        var dragData = new DragManager.DocumentDragData([doc], [undefined]);
+        var dragData = new DragManager.DocumentDragData([doc]);
         dragData.dropAction = dropAction;
         dragData.moveDocument = moveFunc;
         dragData.options = options;
@@ -76,7 +76,7 @@ export async function DragLinkAsDocument(dragEle: HTMLElement, x: number, y: num
     if (draggeddoc) {
         let moddrag = await Cast(draggeddoc.annotationOn, Doc);
         let dragdocs = moddrag ? [moddrag] : [draggeddoc];
-        let dragData = new DragManager.DocumentDragData(dragdocs, dragdocs);
+        let dragData = new DragManager.DocumentDragData(dragdocs);
         dragData.moveDocument = moveLinkedDocument;
         DragManager.StartLinkedDocumentDrag([dragEle], dragData, x, y, {
             handlers: {
@@ -107,7 +107,7 @@ export async function DragLinksAsDocuments(dragEle: HTMLElement, x: number, y: n
             if (doc) moddrag.push(doc);
         }
         let dragdocs = moddrag.length ? moddrag : draggedDocs;
-        let dragData = new DragManager.DocumentDragData(dragdocs, dragdocs);
+        let dragData = new DragManager.DocumentDragData(dragdocs);
         dragData.moveDocument = moveLinkedDocument;
         DragManager.StartLinkedDocumentDrag([dragEle], dragData, x, y, {
             handlers: {
@@ -201,15 +201,13 @@ export namespace DragManager {
 
     export type MoveFunction = (document: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => boolean;
     export class DocumentDragData {
-        constructor(dragDoc: Doc[], dragDataDocs: (Doc | undefined)[]) {
+        constructor(dragDoc: Doc[]) {
             this.draggedDocuments = dragDoc;
-            this.draggedDataDocs = dragDataDocs;
             this.droppedDocuments = dragDoc;
             this.xOffset = 0;
             this.yOffset = 0;
         }
         draggedDocuments: Doc[];
-        draggedDataDocs: (Doc | undefined)[];
         droppedDocuments: Doc[];
         xOffset: number;
         yOffset: number;
@@ -253,7 +251,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) {
-        let dragData = new DragManager.DocumentDragData([], [undefined]);
+        let dragData = new DragManager.DocumentDragData([]);
         runInAction(() => StartDragFunctions.map(func => func()));
         StartDrag(eles, dragData, downX, downY, options, options && options.finishDrag ? options.finishDrag :
             (dropData: { [id: string]: any }) => {
@@ -363,8 +361,6 @@ export namespace DragManager {
 
         const docs: Doc[] =
             dragData instanceof DocumentDragData ? dragData.draggedDocuments : dragData instanceof AnnotationDragData ? [dragData.dragDocument] : [];
-        const datadocs: (Doc | undefined)[] =
-            dragData instanceof DocumentDragData ? dragData.draggedDataDocs : dragData instanceof AnnotationDragData ? [dragData.dragDocument] : [];
         let dragElements = eles.map(ele => {
             const w = ele.offsetWidth,
                 h = ele.offsetHeight;
@@ -449,7 +445,7 @@ export namespace DragManager {
                     pageY: e.pageY,
                     preventDefault: emptyFunction,
                     button: 0
-                }, docs, datadocs);
+                }, docs);
             }
             //TODO: Why can't we use e.movementX and e.movementY?
             let moveX = e.pageX - lastX;
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index ac103b2ea..ac1f51688 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -253,7 +253,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
         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), SelectionManager.SelectedDocuments().map(dv => dv.props.DataDoc ? dv.props.DataDoc : dv.props.Document));
+        let dragData = new DragManager.DocumentDragData(SelectionManager.SelectedDocuments().map(dv => dv.props.Document));
         dragData.xOffset = xoff;
         dragData.yOffset = yoff;
         dragData.moveDocument = SelectionManager.SelectedDocuments()[0].props.moveDocument;
@@ -358,15 +358,15 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
             if (this._iconDoc && selectedDocs.length === 1 && this._removeIcon) {
                 selectedDocs[0].props.removeDocument && selectedDocs[0].props.removeDocument(this._iconDoc);
             }
-            if (!this._removeIcon) {
-                if (selectedDocs.length === 1) {
-                    this.getIconDoc(selectedDocs[0]).then(icon => selectedDocs[0].toggleMinimized());
-                } else if (Math.abs(e.pageX - this._downX) < Utils.DRAG_THRESHOLD &&
-                    Math.abs(e.pageY - this._downY) < Utils.DRAG_THRESHOLD) {
-                    let docViews = SelectionManager.ViewsSortedVertically();
-                    let topDocView = docViews[0];
-                    topDocView.props.Document.subBulletDocs = new List<Doc>(docViews.filter(v => v !== topDocView).map(v => v.props.Document.proto!));
-                }
+            if (!this._removeIcon && selectedDocs.length === 1) { // if you click on the top-left button when just 1 doc is selected, then collapse it.  not sure why we don't do it for multiple selections
+                this.getIconDoc(selectedDocs[0]).then(async icon => {
+                    let minimizedDoc = await Cast(selectedDocs[0].props.Document.minimizedDoc, Doc);
+                    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));
+                    }
+                });
             }
             this._removeIcon = false;
         }
diff --git a/src/client/views/MainOverlayTextBox.tsx b/src/client/views/MainOverlayTextBox.tsx
index 71fb2707d..cd8423a12 100644
--- a/src/client/views/MainOverlayTextBox.tsx
+++ b/src/client/views/MainOverlayTextBox.tsx
@@ -102,7 +102,7 @@ export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps>
         if ((e.movementX > 1 || e.movementY > 1) && FormattedTextBox.InputBoxOverlay) {
             document.removeEventListener("pointermove", this.textBoxMove);
             document.removeEventListener('pointerup', this.textBoxUp);
-            let dragData = new DragManager.DocumentDragData([FormattedTextBox.InputBoxOverlay.props.Document], [FormattedTextBox.InputBoxOverlay.props.DataDoc]);
+            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;
diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx
index af3fcaa24..d57a72dad 100644
--- a/src/client/views/TemplateMenu.tsx
+++ b/src/client/views/TemplateMenu.tsx
@@ -67,7 +67,7 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
             setTimeout(() => {
                 let newDocView = DocumentManager.Instance.getDocumentView(topDoc);
                 if (newDocView) {
-                    let de = new DragManager.DocumentDragData([topDoc], [undefined]);
+                    let de = new DragManager.DocumentDragData([topDoc]);
                     de.moveDocument = topDocView.props.moveDocument;
                     let xf = newDocView.ContentDiv!.getBoundingClientRect();
                     DragManager.StartDocumentDrag([newDocView.ContentDiv!], de, ex, ey, {
diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx
index b7036b3ff..a54718e9e 100644
--- a/src/client/views/collections/CollectionBaseView.tsx
+++ b/src/client/views/collections/CollectionBaseView.tsx
@@ -141,17 +141,16 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
         return false;
     }
 
+    // this is called with the document that was dragged and the collection to move it into.
+    // if the target collection is the same as this collection, then the move will be allowed.
+    // otherwise, the document being moved must be able to be removed from its container before
+    // moving it into the target.  
     @action.bound
     moveDocument(doc: Doc, targetCollection: Doc, addDocument: (doc: Doc) => boolean): boolean {
-        let self = this;
-        let targetDataDoc = this.props.Document;
-        if (Doc.AreProtosEqual(targetDataDoc, targetCollection)) {
+        if (Doc.AreProtosEqual(this.props.Document, targetCollection)) {
             return true;
         }
-        if (this.removeDocument(doc)) {
-            return addDocument(doc);
-        }
-        return false;
+        return this.removeDocument(doc) ? addDocument(doc) : false;
     }
 
     render() {
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 166fa0811..bf55dba31 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -66,15 +66,15 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
     }
     hack: boolean = false;
     undohack: any = null;
-    public StartOtherDrag(e: any, dragDocs: Doc[], dragDataDocs: (Doc | undefined)[] = []) {
+    public StartOtherDrag(e: any, dragDocs: Doc[]) {
         let config: any;
         if (dragDocs.length === 1) {
-            config = CollectionDockingView.makeDocumentConfig(dragDocs[0], dragDataDocs[0]);
+            config = CollectionDockingView.makeDocumentConfig(dragDocs[0], undefined);
         } else {
             config = {
                 type: 'row',
                 content: dragDocs.map((doc, i) => {
-                    CollectionDockingView.makeDocumentConfig(doc, dragDataDocs[i]);
+                    CollectionDockingView.makeDocumentConfig(doc, undefined);
                 })
             };
         }
@@ -84,12 +84,6 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
             dragSource.destroy();
         });
         dragSource._dragListener.onMouseDown(e);
-        // dragSource.destroy();
-        // this.hack = true;
-        // this.undohack = UndoManager.StartBatch("goldenDrag");
-        // dragDocs.map((dragDoc, i) =>
-        //     this.AddRightSplit(dragDoc, dragDataDocs[i], true).contentItems[0].tab._dragListener.
-        //         onMouseDown({ pageX: e.pageX, pageY: e.pageY, preventDefault: emptyFunction, button: 0 }));
     }
 
     @action
@@ -412,7 +406,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
                     e => {
                         e.preventDefault();
                         e.stopPropagation();
-                        DragManager.StartDocumentDrag([dragSpan], new DragManager.DocumentDragData([doc], [dataDoc]), e.clientX, e.clientY, {
+                        DragManager.StartDocumentDrag([dragSpan], new DragManager.DocumentDragData([doc]), e.clientX, e.clientY, {
                             handlers: { dragComplete: emptyFunction },
                             hideSource: false
                         });
diff --git a/src/client/views/collections/CollectionSchemaCells.tsx b/src/client/views/collections/CollectionSchemaCells.tsx
index 17a3f4f7c..3452e8702 100644
--- a/src/client/views/collections/CollectionSchemaCells.tsx
+++ b/src/client/views/collections/CollectionSchemaCells.tsx
@@ -172,7 +172,8 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
         let onItemDown = (e: React.PointerEvent) => {
             if (fieldIsDoc) {
                 SetupDrag(this._focusRef, () => this._document[props.fieldKey] instanceof Doc ? this._document[props.fieldKey] : this._document,
-                    this._document[props.fieldKey] instanceof Doc ? (doc: Doc, target: Doc, addDoc: (newDoc: Doc) => any) => addDoc(doc) : this.props.moveDocument, this._document[props.fieldKey] instanceof Doc ? "alias" : this.props.Document.schemaDoc ? "copy" : undefined)(e);
+                    this._document[props.fieldKey] instanceof Doc ? (doc: Doc, target: Doc, addDoc: (newDoc: Doc) => any) => addDoc(doc) : this.props.moveDocument,
+                    this._document[props.fieldKey] instanceof Doc ? "alias" : this.props.Document.schemaDoc ? "copy" : undefined)(e);
             }
         };
         let onPointerEnter = (e: React.PointerEvent): void => {
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 1a84f94c8..25d3bd128 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -14,7 +14,7 @@ import { listSpec } from "../../../new_fields/Schema";
 import { Docs, DocumentOptions } from "../../documents/Documents";
 import { Cast, FieldValue, NumCast, StrCast } from "../../../new_fields/Types";
 import { Gateway } from "../../northstar/manager/Gateway";
-import { SetupDrag, DragManager } from "../../util/DragManager";
+import { DragManager } from "../../util/DragManager";
 import { CompileScript, ts, Transformer } from "../../util/Scripting";
 import { Transform } from "../../util/Transform";
 import { COLLECTION_BORDER_WIDTH } from '../../views/globalCssVariables.scss';
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index 185bec7a2..34f2652ff 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -183,7 +183,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
             if (compiled.compiled) {
                 let scriptField = new ScriptField(compiled);
                 alias.viewSpecScript = scriptField;
-                let dragData = new DragManager.DocumentDragData([alias], [alias.proto]);
+                let dragData = new DragManager.DocumentDragData([alias]);
                 DragManager.StartDocumentDrag([this._headerRef.current!], dragData, e.clientX, e.clientY);
             }
 
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 270916522..bc994cffd 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -126,9 +126,11 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
                 if (de.data.dropAction || de.data.userDropAction) {
                     added = de.data.droppedDocuments.reduce((added: boolean, d) => this.props.addDocument(d) || added, false);
                 } else if (de.data.moveDocument) {
-                    let movedDocs = de.data.options === this.props.Document[Id] ? de.data.draggedDocuments : de.data.droppedDocuments;
-                    added = movedDocs.reduce((added: boolean, d) =>
-                        de.data.moveDocument(d, this.props.Document, this.props.addDocument) || added, false);
+                    let movedDocs = de.data.draggedDocuments;// de.data.options === this.props.Document[Id] ? de.data.draggedDocuments : de.data.droppedDocuments;
+                    // note that it's possible the drag function might create a drop document that's not the same as the
+                    // original dragged document.  So we explicitly call addDocument() with a droppedDocument and 
+                    added = movedDocs.reduce((added: boolean, d, i) =>
+                        de.data.moveDocument(d, this.props.Document, (doc: Doc) => this.props.addDocument(de.data.droppedDocuments[i])) || added, false);
                 } else {
                     added = de.data.droppedDocuments.reduce((added: boolean, d) => this.props.addDocument(d) || added, false);
                 }
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index bce4eb427..548f663ec 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -95,8 +95,9 @@ export class CollectionView extends React.Component<FieldViewProps> {
             }
             subItems.push({ description: "Schema", event: () => this.props.Document.viewType = CollectionViewType.Schema, icon: "th-list" });
             subItems.push({ description: "Treeview", event: () => this.props.Document.viewType = CollectionViewType.Tree, icon: "tree" });
+            subItems.push({ description: "Stacking", event: () => this.props.Document.viewType = CollectionViewType.Stacking, icon: "ellipsis-v" });
             subItems.push({
-                description: "Stacking", event: () => {
+                description: "Stacking (AutoHeight)", event: () => {
                     this.props.Document.viewType = CollectionViewType.Stacking;
                     this.props.Document.autoHeight = true
                 }, icon: "ellipsis-v"
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index ad91eb007..f2ca645a7 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -381,7 +381,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
             SelectionManager.DeselectAll();
             prevSelected.map(dv => SelectionManager.SelectDoc(dv, true));
 
-            let de = new DragManager.DocumentDragData(eles, eles.map(d => undefined));
+            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);
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index cc5e887b2..5cab6f8e0 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -315,7 +315,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
             dataExtensionField.ink = inkData ? new InkField(this.marqueeInkSelect(inkData)) : undefined;
             this.marqueeInkDelete(inkData);
 
-            if (e.key === "s") {
+            if (e.key === "s" || e.key === "S") {
                 selected.map(d => {
                     this.props.removeDocument(d);
                     d.x = NumCast(d.x) - bounds.left - bounds.width / 2;
@@ -324,35 +324,19 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
                     return d;
                 });
                 newCollection.chromeStatus = "disabled";
-                let summary = Docs.Create.TextDocument({ x: bounds.left, y: bounds.top, width: 300, height: 100, backgroundColor: "#e2ad32" /* yellow */, title: "-summary-" });
-                newCollection.proto!.summaryDoc = summary;
-                selected = [newCollection];
-                newCollection.x = bounds.left + bounds.width;
-                summary.proto!.subBulletDocs = new List<Doc>(selected);
-                let container = Docs.Create.FreeformDocument([summary, newCollection], { x: bounds.left, y: bounds.top, width: 300, height: 200, chromeStatus: "disabled", title: "-summary-" });
-                container.viewType = CollectionViewType.Stacking;
-                container.autoHeight = true;
-                this.props.addLiveTextDocument(container);
-                // });
-            } else if (e.key === "S") {
-                selected.map(d => {
-                    this.props.removeDocument(d);
-                    d.x = NumCast(d.x) - bounds.left - bounds.width / 2;
-                    d.y = NumCast(d.y) - bounds.top - bounds.height / 2;
-                    d.page = -1;
-                    return d;
-                });
-                newCollection.chromeStatus = "disabled";
-                let summary = Docs.Create.TextDocument({ x: bounds.left, y: bounds.top, width: 300, height: 100, backgroundColor: "#e2ad32" /* yellow */, title: "-summary-" });
+                let summary = Docs.Create.TextDocument({ x: bounds.left, y: bounds.top, width: 300, height: 100, autoHeight: true, backgroundColor: "#e2ad32" /* yellow */, title: "-summary-" });
+                summary.proto!.maximizeLocation = "inTab";  // or "inPlace", or "onRight"
                 newCollection.proto!.summaryDoc = summary;
-                selected = [newCollection];
                 newCollection.x = bounds.left + bounds.width;
-                //this.props.addDocument(newCollection, false);
-                summary.proto!.summarizedDocs = new List<Doc>(selected);
-                summary.proto!.maximizeLocation = "inTab";  // or "inPlace", or "onRight"
-                summary.autoHeight = true;
-
-                this.props.addLiveTextDocument(summary);
+                if (e.key === "s") { // summary is wrapped in an expand/collapse container that also contains the summarized documents in a free form view.
+                    let container = Docs.Create.FreeformDocument([summary, newCollection], { x: bounds.left, y: bounds.top, width: 300, height: 200, chromeStatus: "disabled", title: "-summary-" });
+                    container.viewType = CollectionViewType.Stacking;
+                    container.autoHeight = true;
+                    this.props.addLiveTextDocument(container);
+                } else if (e.key === "S") { // the summary stands alone, but is linked to a collection of the summarized documents - set the OnCLick behavior to link follow to access them
+                    summary.proto!.summarizedDocs = new List<Doc>([newCollection]);
+                    this.props.addLiveTextDocument(summary);
+                }
             }
             else {
                 this.props.addDocument(newCollection, false);
diff --git a/src/client/views/linking/LinkMenuGroup.tsx b/src/client/views/linking/LinkMenuGroup.tsx
index b6a24b0c8..d711ac284 100644
--- a/src/client/views/linking/LinkMenuGroup.tsx
+++ b/src/client/views/linking/LinkMenuGroup.tsx
@@ -57,7 +57,7 @@ export class LinkMenuGroup extends React.Component<LinkMenuGroupProps> {
                     let opp = LinkManager.Instance.getOppositeAnchor(linkDoc, this.props.sourceDoc);
                     if (opp) return opp;
                 }) as Doc[];
-                let dragData = new DragManager.DocumentDragData(draggedDocs, draggedDocs.map(d => undefined));
+                let dragData = new DragManager.DocumentDragData(draggedDocs);
 
                 DragManager.StartLinkedDocumentDrag([this._drag.current], dragData, e.x, e.y, {
                     handlers: {
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index e95f484a4..17bf02448 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -138,7 +138,6 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     private _downY: number = 0;
     private _lastTap: number = 0;
     private _doubleTap = false;
-    private _hitExpander = false;
     private _hitTemplateDrag = false;
     private _mainCont = React.createRef<HTMLDivElement>();
     private _dropDisposer?: DragManager.DragDropDisposer;
@@ -221,20 +220,19 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     }
 
     get dataDoc() {
-        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;
-        }
+        // 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, dragSubBullets: boolean, applyAsTemplate?: boolean) {
+    startDragging(x: number, y: number, dropAction: dropActionType, applyAsTemplate?: boolean) {
         if (this._mainCont.current) {
-            let allConnected = [this.props.Document, ...(dragSubBullets ? DocListCast(this.props.Document.subBulletDocs) : [])];
-            let alldataConnected = [this.dataDoc, ...(dragSubBullets ? DocListCast(this.props.Document.subBulletDocs) : [])];
             const [left, top] = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).inverse().transformPoint(0, 0);
-            let dragData = new DragManager.DocumentDragData(allConnected, alldataConnected);
+            let dragData = new DragManager.DocumentDragData([this.props.Document]);
             const [xoff, yoff] = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).transformDirection(x - left, y - top);
             dragData.dropAction = dropAction;
             dragData.xOffset = xoff;
@@ -249,14 +247,6 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             });
         }
     }
-    toggleMinimized = async () => {
-        let minimizedDoc = await Cast(this.props.Document.minimizedDoc, Doc);
-        if (minimizedDoc) {
-            let scrpt = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).inverse().transformPoint(
-                NumCast(minimizedDoc.x) - NumCast(this.Document.x), NumCast(minimizedDoc.y) - NumCast(this.Document.y));
-            this.collapseTargetsToPoint(scrpt, await DocListCastAsync(minimizedDoc.maximizedDocs));
-        }
-    }
 
     static _undoBatch?: UndoManager.Batch = undefined;
     @action
@@ -315,15 +305,13 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             SelectionManager.SelectDoc(this, e.ctrlKey);
             let isExpander = (e.target as any).id === "isExpander";
             if (BoolCast(this.props.Document.isButton) || this.props.Document.type === DocumentType.BUTTON || isExpander) {
-                let subBulletDocs = await DocListCastAsync(this.props.Document.subBulletDocs);
                 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 = subBulletDocs ? [...subBulletDocs, ...expandedDocs] : expandedDocs;
                 expandedDocs = maximizedDocs ? [...maximizedDocs, ...expandedDocs] : expandedDocs;
                 expandedDocs = summarizedDocs ? [...summarizedDocs, ...expandedDocs] : expandedDocs;
-                // let expandedDocs = [...(subBulletDocs ? subBulletDocs : []), ...(maximizedDocs ? maximizedDocs : []), ...(summarizedDocs ? summarizedDocs : []),];
+                // let expandedDocs = [ ...(maximizedDocs ? maximizedDocs : []), ...(summarizedDocs ? summarizedDocs : []),];
                 if (expandedDocs.length) {   // bcz: need a better way to associate behaviors with click events on widget-documents
                     SelectionManager.DeselectAll();
                     let maxLocation = StrCast(this.props.Document.maximizeLocation, "inPlace");
@@ -394,7 +382,6 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         if (e.nativeEvent.cancelBubble) return;
         this._downX = e.clientX;
         this._downY = e.clientY;
-        this._hitExpander = DocListCast(this.props.Document.subBulletDocs).length > 0;
         this._hitTemplateDrag = false;
         for (let element = (e.target as any); element && !this._hitTemplateDrag; element = element.parentElement) {
             if (element.className && element.className.toString() === "collectionViewBaseChrome-collapse") {
@@ -416,7 +403,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 if (!e.altKey && !this.topMost && e.buttons === 1 && !BoolCast(this.props.Document.lockedPosition)) {
                     document.removeEventListener("pointermove", this.onPointerMove);
                     document.removeEventListener("pointerup", this.onPointerUp);
-                    this.startDragging(this._downX, this._downY, e.ctrlKey || e.altKey ? "alias" : undefined, this._hitExpander, this._hitTemplateDrag);
+                    this.startDragging(this._downX, this._downY, e.ctrlKey || e.altKey ? "alias" : undefined, this._hitTemplateDrag);
                 }
             }
             e.stopPropagation(); // doesn't actually stop propagation since all our listeners are listening to events on 'document'  however it does mark the event as cancelBubble=true which we test for in the move event handlers
diff --git a/src/client/views/nodes/DragBox.tsx b/src/client/views/nodes/DragBox.tsx
index 1f2c88086..067d47de4 100644
--- a/src/client/views/nodes/DragBox.tsx
+++ b/src/client/views/nodes/DragBox.tsx
@@ -55,7 +55,7 @@ export class DragBox extends DocComponent<FieldViewProps, DragDocument>(DragDocu
             let doc = res !== undefined && res.success ?
                 res.result as Doc :
                 Docs.Create.FreeformDocument([], { nativeWidth: undefined, nativeHeight: undefined, width: 150, height: 100, title: "freeform" });
-            doc && DragManager.StartDocumentDrag([this._mainCont.current!], new DragManager.DocumentDragData([doc], [undefined]), e.clientX, e.clientY);
+            doc && DragManager.StartDocumentDrag([this._mainCont.current!], new DragManager.DocumentDragData([doc]), e.clientX, e.clientY);
         }
         e.stopPropagation();
         e.preventDefault();
diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx
index 856e883e7..533247170 100644
--- a/src/client/views/pdf/Page.tsx
+++ b/src/client/views/pdf/Page.tsx
@@ -137,7 +137,7 @@ export default class Page extends React.Component<IPageProps> {
         view.nativeWidth = this.props.Document.nativeWidth;
         view.startY = marquee.top + this.props.getScrollFromPage(this.props.page);
         view.width = this.props.Document[WidthSym]();
-        DragManager.StartDocumentDrag([], new DragManager.DocumentDragData([view], [undefined]), 0, 0);
+        DragManager.StartDocumentDrag([], new DragManager.DocumentDragData([view]), 0, 0);
     }
 
     @action
diff --git a/src/client/views/search/SearchItem.tsx b/src/client/views/search/SearchItem.tsx
index cd3dd912c..510672788 100644
--- a/src/client/views/search/SearchItem.tsx
+++ b/src/client/views/search/SearchItem.tsx
@@ -270,7 +270,7 @@ export class SearchItem extends React.Component<SearchItemProps> {
     onPointerDown = (e: React.PointerEvent<HTMLDivElement>) => {
         e.stopPropagation();
         const doc = Doc.IsPrototype(this.props.doc) ? Doc.MakeDelegate(this.props.doc) : this.props.doc;
-        DragManager.StartDocumentDrag([e.currentTarget], new DragManager.DocumentDragData([doc], []), e.clientX, e.clientY, {
+        DragManager.StartDocumentDrag([e.currentTarget], new DragManager.DocumentDragData([doc]), e.clientX, e.clientY, {
             handlers: { dragComplete: emptyFunction },
             hideSource: false,
         });
-- 
cgit v1.2.3-70-g09d2


From 5e3c12128f4d15d621b323e0f1d78bfff2452b92 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Mon, 16 Sep 2019 10:36:16 -0400
Subject: fixed templates to allow aliasing, clustering and other things.

---
 src/client/views/collections/CollectionSubView.tsx |  4 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 44 +++++++++++-----------
 src/client/views/nodes/DocumentView.tsx            |  4 +-
 src/new_fields/Doc.ts                              |  3 ++
 4 files changed, 29 insertions(+), 26 deletions(-)

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

diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index bc994cffd..d13c69ecf 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -53,8 +53,10 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
         @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); }
 
 
+        get childLayoutPairs() {
+            return this.childDocs.map(cd => Doc.GetLayoutDataDocPair(this.props.Document, this.props.DataDoc, this.props.fieldKey, cd)).filter(pair => pair.layout).map(pair => ({ layout: pair.layout!, data: pair.data! }));
+        }
         get childDocs() {
-            let self = this;
             //TODO tfs: This might not be what we want?
             //This linter error can't be fixed because of how js arguments work, so don't switch this to filter(FieldValue)
             let docs = DocListCast(this.extensionDoc[this.props.fieldExt ? this.props.fieldExt : this.props.fieldKey]);
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 5d5d9de9e..49640c040 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -280,10 +280,10 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
     }
     public getActiveDocuments = () => {
         const curPage = FieldValue(this.Document.curPage, -1);
-        return this.childDocs.filter(doc => {
-            var page = NumCast(doc.page, -1);
+        return this.childLayoutPairs.filter(pair => {
+            var page = NumCast(pair.layout!.page, -1);
             return page === curPage || page === -1;
-        }).map(doc => Doc.GetLayoutDataDocPair(this.props.Document, this.props.DataDoc, this.props.fieldKey, doc).layout);
+        }).map(pair => pair.layout);
     }
 
     @computed get fieldExtensionDoc() {
@@ -361,7 +361,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
 
     tryDragCluster(e: PointerEvent) {
         let probe = this.getTransform().transformPoint(e.clientX, e.clientY);
-        let cluster = this.childDocs.reduce((cluster, cd) => {
+        let cluster = this.childLayoutPairs.map(pair => pair.layout).reduce((cluster, cd) => {
             let cx = NumCast(cd.x) - this._clusterDistance;
             let cy = NumCast(cd.y) - this._clusterDistance;
             let cw = NumCast(cd.width) + 2 * this._clusterDistance;
@@ -372,7 +372,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
             return cluster;
         }, -1);
         if (cluster !== -1) {
-            let eles = this.childDocs.filter(cd => NumCast(cd.cluster) === cluster);
+            let eles = this.childLayoutPairs.map(pair => pair.layout).filter(cd => NumCast(cd.cluster) === cluster);
 
             // hacky way to get a list of DocumentViews in the current view given a list of Documents in the current view
             let prevSelected = SelectionManager.SelectedDocuments();
@@ -403,7 +403,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
     @action
     updateClusters() {
         this.sets.length = 0;
-        this.childDocs.map(c => {
+        this.childLayoutPairs.map(pair => pair.layout).map(c => {
             let included = [];
             for (let i = 0; i < this.sets.length; i++) {
                 for (let member of this.sets[i]) {
@@ -431,20 +431,21 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
     @undoBatch
     @action
     updateCluster(doc: Doc) {
+        let childLayouts = this.childLayoutPairs.map(pair => pair.layout);
         if (this.props.Document.useClusters) {
             this.sets.map(set => Doc.IndexOf(doc, set) !== -1 && set.splice(Doc.IndexOf(doc, set), 1));
             let preferredInd = NumCast(doc.cluster);
             doc.cluster = -1;
             this.sets.map((set, i) => set.map(member => {
-                if (doc.cluster === -1 && Doc.IndexOf(member, this.childDocs) !== -1 && this.boundsOverlap(doc, member)) {
+                if (doc.cluster === -1 && Doc.IndexOf(member, childLayouts) !== -1 && this.boundsOverlap(doc, member)) {
                     doc.cluster = i;
                 }
             }));
-            if (doc.cluster === -1 && preferredInd !== -1 && (!this.sets[preferredInd] || !this.sets[preferredInd].filter(member => Doc.IndexOf(member, this.childDocs) !== -1).length)) {
+            if (doc.cluster === -1 && preferredInd !== -1 && (!this.sets[preferredInd] || !this.sets[preferredInd].filter(member => Doc.IndexOf(member, childLayouts) !== -1).length)) {
                 doc.cluster = preferredInd;
             }
             this.sets.map((set, i) => {
-                if (doc.cluster === -1 && !set.filter(member => Doc.IndexOf(member, this.childDocs) !== -1).length) {
+                if (doc.cluster === -1 && !set.filter(member => Doc.IndexOf(member, childLayouts) !== -1).length) {
                     doc.cluster = i;
                 }
             });
@@ -506,7 +507,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
             }
             let x = this.Document.panX || 0;
             let y = this.Document.panY || 0;
-            let docs = this.childDocs || [];
+            let docs = this.childLayoutPairs.map(pair => pair.layout);
             let [dx, dy] = this.getTransform().transformDirection(e.clientX - this._lastX, e.clientY - this._lastY);
             if (!this.isAnnotationOverlay) {
                 PDFMenu.Instance.fadeOut(true);
@@ -556,7 +557,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
             return;
         }
 
-        let childSelected = this.childDocs.some(doc => {
+        let childSelected = this.childLayoutPairs.map(pair => pair.layout).some(doc => {
             var dv = DocumentManager.Instance.getDocumentView(doc);
             return dv && SelectionManager.IsSelected(dv) ? true : false;
         });
@@ -619,7 +620,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
             doc.zIndex = 0;
             return;
         }
-        const docs = this.childDocs;
+        const docs = this.childLayoutPairs.map(pair => pair.layout);
         docs.slice().sort((doc1, doc2) => {
             if (doc1 === doc) return 1;
             if (doc2 === doc) return -1;
@@ -790,12 +791,10 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
         const initScript = this.Document.arrangeInit;
         const script = this.Document.arrangeScript;
         let state: any = undefined;
-        let docs = this.childDocs;
-        let overlayDocs = DocListCast(this.props.Document.localOverlays);
-        overlayDocs && docs.push(...overlayDocs);
+        let pairs = this.childLayoutPairs;
         let elements: ViewDefResult[] = [];
         if (initScript) {
-            const initResult = initScript.script.run({ docs, collection: this.Document });
+            const initResult = initScript.script.run({ docs: pairs.map(pair => pair.layout), collection: this.Document });
             if (initResult.success) {
                 const result = initResult.result;
                 const { state: scriptState, views } = result;
@@ -803,18 +802,17 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
                 elements = this.viewDefsToJSX(views);
             }
         }
-        let docviews = docs.filter(doc => doc instanceof Doc).reduce((prev, doc) => {
-            var page = NumCast(doc.page, -1);
+        let docviews = pairs.reduce((prev, pair) => {
+            var page = NumCast(pair.layout.page, -1);
             if ((Math.abs(Math.round(page) - Math.round(curPage)) < 3) || page === -1) {
-                let minim = BoolCast(doc.isMinimized);
+                let minim = BoolCast(pair.layout.isMinimized);
                 if (minim === undefined || !minim) {
-                    const pos = script ? this.getCalculatedPositions(script, { doc, index: prev.length, collection: this.Document, docs, state }) :
-                        { x: Cast(doc.x, "number"), y: Cast(doc.y, "number"), z: Cast(doc.z, "number"), width: Cast(doc.width, "number"), height: Cast(doc.height, "number") };
+                    const pos = script ? this.getCalculatedPositions(script, { doc: pair.layout, index: prev.length, collection: this.Document, docs: pairs.map(pair => pair.layout), state }) :
+                        { x: Cast(pair.layout.x, "number"), y: Cast(pair.layout.y, "number"), z: Cast(pair.layout.z, "number"), width: Cast(pair.layout.width, "number"), height: Cast(pair.layout.height, "number") };
                     state = pos.state === undefined ? state : pos.state;
-                    let pair = Doc.GetLayoutDataDocPair(this.props.Document, this.props.DataDoc, this.props.fieldKey, doc);
                     if (pair.layout && !(pair.data instanceof Promise)) {
                         prev.push({
-                            ele: <CollectionFreeFormDocumentView key={doc[Id]}
+                            ele: <CollectionFreeFormDocumentView key={pair.layout[Id]}
                                 ruleProvider={this.props.Document.isRuleProvider ? this.props.Document : this.props.ruleProvider}
                                 jitterRotation={NumCast(this.props.Document.jitterRotation)}
                                 x={script ? pos.x : undefined} y={script ? pos.y : undefined}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 17bf02448..503d59195 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -435,8 +435,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         this.props.Document.nativeLayout = this.props.Document.layout;
         this.props.Document.nativeType = this.props.Document.type;
         this.props.Document.nonCustomAutoHeight = this.props.Document.autoHeight;
-        this.props.Document.nonCustomWidth = this.props.Document.nativeWidth;
-        this.props.Document.nonCustomHeight = this.props.Document.nativeHeight;
+        this.props.Document.nonCustomWidth = this.props.Document.width;
+        this.props.Document.nonCustomHeight = this.props.Document.height;
         this.props.Document.nonCustomNativeWidth = this.props.Document.nativeWidth;
         this.props.Document.nonCustomNativeHeight = this.props.Document.nativeHeight;
         this.props.Document.nonCustomIgnoreAspect = this.props.Document.ignoreAspect;
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index eef14ad25..2434a439a 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -412,6 +412,9 @@ export namespace Doc {
     }
     export function MakeAlias(doc: Doc) {
         let alias = !GetT(doc, "isPrototype", "boolean", true) ? Doc.MakeCopy(doc) : Doc.MakeDelegate(doc);
+        if (alias.layout instanceof Doc) {
+            alias.layout = Doc.MakeAlias(alias.layout as Doc);
+        }
         let aliasNumber = Doc.GetProto(doc).aliasNumber = NumCast(Doc.GetProto(doc).aliasNumber) + 1;
         let script = `return renameAlias(self, ${aliasNumber})`;
         //let script = "StrCast(self.title).replace(/\\([0-9]*\\)/, \"\") + `(${n})`";
-- 
cgit v1.2.3-70-g09d2


From 2f1741cf291c69ce55b87116bd24edd2f940141d Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Mon, 16 Sep 2019 16:09:23 -0400
Subject: fixed double-clicking to open docs.

---
 src/client/views/collections/CollectionTreeView.tsx | 2 +-
 src/client/views/nodes/DocumentView.tsx             | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

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

diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 6217ef859..40bea2f9d 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -347,7 +347,7 @@ class TreeView extends React.Component<TreeViewProps> {
 
     @computed
     get renderBullet() {
-        return <div className="bullet" title="view inline" onClick={action(() => this.treeViewOpen = !this.treeViewOpen)} style={{ color: StrCast(this.props.document.color, "black"), opacity: 0.4 }}>
+        return <div className="bullet" title="view inline" onClick={action((e: React.MouseEvent) => { this.treeViewOpen = !this.treeViewOpen; e.stopPropagation(); })} style={{ color: StrCast(this.props.document.color, "black"), opacity: 0.4 }}>
             {<FontAwesomeIcon icon={!this.treeViewOpen ? (this.childDocs ? "caret-square-right" : "caret-right") : (this.childDocs ? "caret-square-down" : "caret-down")} />}
         </div>;
     }
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 503d59195..760f4d584 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -276,7 +276,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     }
 
     onClick = async (e: React.MouseEvent) => {
-        if (e.nativeEvent.cancelBubble || SelectionManager.IsSelected(this)) return; // needed because EditableView may stopPropagation which won't apparently stop this event from firing.
+        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) {
             e.stopPropagation();
             this.onClickHandler.script.run({ this: this.props.Document.isTemplate && this.props.DataDoc ? this.props.DataDoc : this.props.Document });
-- 
cgit v1.2.3-70-g09d2


From 999cd7deb87ce5c6430a5f8e0e1721033736bbab Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Mon, 16 Sep 2019 17:13:16 -0400
Subject: cleaned up titling of icons and summaries.

---
 src/client/views/DocumentDecorations.tsx           |  6 ++-
 .../collections/collectionFreeForm/MarqueeView.tsx | 10 ++--
 src/client/views/nodes/DocumentView.tsx            | 22 +--------
 src/client/views/nodes/IconBox.tsx                 | 53 +++++++++++++---------
 4 files changed, 43 insertions(+), 48 deletions(-)

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

diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index ac1f51688..240abaf6b 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -32,6 +32,8 @@ import { CurrentUserUtils } from '../../server/authentication/models/current_use
 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';
 const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
@@ -378,8 +380,8 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
         let doc = selected[0].props.Document;
         let iconDoc = Docs.Create.IconDocument(layoutString);
         iconDoc.isButton = true;
-        iconDoc.proto!.title = selected.length > 1 ? "-multiple-.icon" : StrCast(doc.title) + ".icon";
-        iconDoc.labelField = selected.length > 1 ? undefined : this._fieldKey;
+
+        IconBox.AutomaticTitle(iconDoc);
         //iconDoc.proto![this._fieldKey] = selected.length > 1 ? "collection" : undefined;
         iconDoc.proto!.isMinimized = false;
         iconDoc.width = Number(MINIMIZED_ICON_SIZE);
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 5cab6f8e0..d74fbafb3 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -23,6 +23,8 @@ import { SchemaHeaderField, RandomPastel } from "../../../../new_fields/SchemaHe
 import { string } from "prop-types";
 import { listSpec } from "../../../../new_fields/Schema";
 import { CurrentUserUtils } from "../../../../server/authentication/models/current_user_utils";
+import { CompileScript } from "../../../util/Scripting";
+import { ComputedField } from "../../../../new_fields/ScriptField";
 
 interface MarqueeViewProps {
     getContainerTransform: () => Transform;
@@ -309,7 +311,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
                 defaultBackgroundColor: this.props.container.isAnnotationOverlay ? undefined : chosenColor,
                 width: bounds.width,
                 height: bounds.height,
-                title: e.key === "s" || e.key === "S" ? "-summary-" : "a nested collection",
+                title: "a nested collection",
             });
             let dataExtensionField = Doc.CreateDocumentExtensionForField(newCollection, "data");
             dataExtensionField.ink = inkData ? new InkField(this.marqueeInkSelect(inkData)) : undefined;
@@ -325,9 +327,11 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
                 });
                 newCollection.chromeStatus = "disabled";
                 let summary = Docs.Create.TextDocument({ x: bounds.left, y: bounds.top, width: 300, height: 100, autoHeight: true, backgroundColor: "#e2ad32" /* yellow */, title: "-summary-" });
-                summary.proto!.maximizeLocation = "inTab";  // or "inPlace", or "onRight"
-                newCollection.proto!.summaryDoc = summary;
+                Doc.GetProto(summary).maximizeLocation = "inTab";  // or "inPlace", or "onRight"
+                Doc.GetProto(newCollection).summaryDoc = summary;
                 newCollection.x = bounds.left + bounds.width;
+                let computed = CompileScript(`return summaryTitle(this);`, { params: { this: "Doc" }, typecheck: false });
+                computed.compiled && (Doc.GetProto(newCollection).title = new ComputedField(computed));
                 if (e.key === "s") { // summary is wrapped in an expand/collapse container that also contains the summarized documents in a free form view.
                     let container = Docs.Create.FreeformDocument([summary, newCollection], { x: bounds.left, y: bounds.top, width: 300, height: 200, chromeStatus: "disabled", title: "-summary-" });
                     container.viewType = CollectionViewType.Stacking;
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 760f4d584..50a9aa326 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -141,13 +141,11 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     private _hitTemplateDrag = false;
     private _mainCont = React.createRef<HTMLDivElement>();
     private _dropDisposer?: DragManager.DragDropDisposer;
-    _animateToIconDisposer?: IReactionDisposer;
-    _reactionDisposer?: IReactionDisposer;
+    private _animateToIconDisposer?: IReactionDisposer;
 
     public get ContentDiv() { return this._mainCont.current; }
     @computed get active(): boolean { return SelectionManager.IsSelected(this) || this.props.parentActive(); }
     @computed get topMost(): boolean { return this.props.renderDepth === 0; }
-    screenRect = (): ClientRect | DOMRect => this._mainCont.current ? this._mainCont.current.getBoundingClientRect() : new DOMRect();
 
     @action
     componentDidMount() {
@@ -156,19 +154,6 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 handlers: { drop: this.drop.bind(this) }
             });
         }
-        // bcz: kind of ugly .. setup a reaction to update the title of a summary document's target (maximizedDocs) whenver the summary doc's title changes
-        this._reactionDisposer = reaction(() => [DocListCast(this.props.Document.maximizedDocs).map(md => md.title),
-        this.props.Document.summaryDoc, this.props.Document.summaryDoc instanceof Doc ? this.props.Document.summaryDoc.title : ""],
-            () => {
-                let maxDoc = DocListCast(this.props.Document.maximizedDocs);
-                if (maxDoc.length === 1 && StrCast(this.props.Document.title).startsWith("-") && StrCast(this.props.Document.layout).indexOf("IconBox") !== -1) {
-                    this.props.Document.proto!.title = "-" + maxDoc[0].title + ".icon";
-                }
-                let sumDoc = Cast(this.props.Document.summaryDoc, Doc);
-                if (sumDoc instanceof Doc && StrCast(this.props.Document.title).startsWith("-")) {
-                    this.props.Document.proto!.title = "-" + sumDoc.title + ".expanded";
-                }
-            }, { fireImmediately: true });
         this._animateToIconDisposer = reaction(() => this.props.Document.isIconAnimating, (values) =>
             (values instanceof List) && this.animateBetweenIcon(values, values[2], values[3] ? true : false)
             , { fireImmediately: true });
@@ -209,16 +194,11 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     }
     @action
     componentWillUnmount() {
-        this._reactionDisposer && this._reactionDisposer();
         this._animateToIconDisposer && this._animateToIconDisposer();
         this._dropDisposer && this._dropDisposer();
         DocumentManager.Instance.DocumentViews.splice(DocumentManager.Instance.DocumentViews.indexOf(this), 1);
     }
 
-    stopPropagation = (e: React.SyntheticEvent) => {
-        e.stopPropagation();
-    }
-
     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)) {
diff --git a/src/client/views/nodes/IconBox.tsx b/src/client/views/nodes/IconBox.tsx
index 7e78ec684..ef9885bcf 100644
--- a/src/client/views/nodes/IconBox.tsx
+++ b/src/client/views/nodes/IconBox.tsx
@@ -12,6 +12,8 @@ import { IconField } from "../../../new_fields/IconField";
 import { ContextMenu } from "../ContextMenu";
 import Measure from "react-measure";
 import { MINIMIZED_ICON_SIZE } from "../../views/globalCssVariables.scss";
+import { Scripting, CompileScript } from "../../util/Scripting";
+import { ComputedField } from "../../../new_fields/ScriptField";
 
 
 library.add(faCaretUp);
@@ -27,6 +29,26 @@ export class IconBox extends React.Component<FieldViewProps> {
     @computed get layout(): string { const field = Cast(this.props.Document[this.props.fieldKey], IconField); return field ? field.icon : "<p>Error loading icon data</p>"; }
     @computed get minimizedIcon() { return IconBox.DocumentIcon(this.layout); }
 
+    public static summaryTitleScript(inputDoc: Doc) {
+        const sumDoc = Cast(inputDoc.summaryDoc, Doc) as Doc;
+        if (sumDoc && StrCast(sumDoc.title).startsWith("-")) {
+            return sumDoc.title + ".expanded";
+        }
+        return "???";
+    }
+    public static titleScript(inputDoc: Doc) {
+        const maxDoc = DocListCast(inputDoc.maximizedDocs);
+        if (maxDoc.length === 1 && StrCast(maxDoc[0].title).startsWith("-")) {
+            return maxDoc[0].title + ".icon";
+        }
+        return maxDoc.length > 1 ? "-multiple-.icon" : "???";
+    }
+
+    public static AutomaticTitle(doc: Doc) {
+        let computed = CompileScript(`return iconTitle(this);`, { params: { this: "Doc" }, typecheck: false });
+        computed.compiled && (Doc.GetProto(doc).title = new ComputedField(computed));
+    }
+
     public static DocumentIcon(layout: string) {
         let button = layout.indexOf("PDFBox") !== -1 ? faFilePdf :
             layout.indexOf("ImageBox") !== -1 ? faImage :
@@ -38,35 +60,20 @@ export class IconBox extends React.Component<FieldViewProps> {
     }
 
     setLabelField = (): void => {
-        this.props.Document.hideLabel = !BoolCast(this.props.Document.hideLabel);
-    }
-    setUseOwnTitleField = (): void => {
-        this.props.Document.useOwnTitle = !BoolCast(this.props.Document.useTargetTitle);
+        this.props.Document.hideLabel = !this.props.Document.hideLabel;
     }
 
     specificContextMenu = (): void => {
-        ContextMenu.Instance.addItem({
-            description: BoolCast(this.props.Document.hideLabel) ? "Show label with icon" : "Remove label from icon",
-            event: this.setLabelField,
-            icon: "tag"
-        });
-        let maxDocs = DocListCast(this.props.Document.maximizedDocs);
-        if (maxDocs.length === 1 && !BoolCast(this.props.Document.hideLabel)) {
-            ContextMenu.Instance.addItem({
-                description: BoolCast(this.props.Document.useOwnTitle) ? "Use target title for label" : "Use own title label",
-                event: this.setUseOwnTitleField,
-                icon: "text-height"
-            });
+        let cm = ContextMenu.Instance;
+        cm.addItem({ description: this.props.Document.hideLabel ? "Show label with icon" : "Remove label from icon", event: this.setLabelField, icon: "tag" });
+        if (!this.props.Document.hideLabel) {
+            cm.addItem({ description: "Use Target Title", event: () => IconBox.AutomaticTitle(this.props.Document), icon: "text-height" });
         }
     }
     @observable _panelWidth: number = 0;
     @observable _panelHeight: number = 0;
     render() {
-        let labelField = StrCast(this.props.Document.labelField);
-        let hideLabel = BoolCast(this.props.Document.hideLabel);
-        let maxDocs = DocListCast(this.props.Document.maximizedDocs);
-        let firstDoc = maxDocs.length ? maxDocs[0] : undefined;
-        let label = hideLabel ? "" : (firstDoc && labelField && !BoolCast(this.props.Document.useOwnTitle) ? firstDoc[labelField] : this.props.Document.title);
+        let label = this.props.Document.hideLabel ? "" : this.props.Document.title;
         return (
             <div className="iconBox-container" onContextMenu={this.specificContextMenu}>
                 {this.minimizedIcon}
@@ -82,4 +89,6 @@ export class IconBox extends React.Component<FieldViewProps> {
                 </Measure>
             </div>);
     }
-}
\ No newline at end of file
+}
+Scripting.addGlobal(function iconTitle(doc: any) { return IconBox.titleScript(doc); });
+Scripting.addGlobal(function summaryTitle(doc: any) { return IconBox.summaryTitleScript(doc); });
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2


From 624d44ad339d55df66a4ed8bf4b2cb91608efeef Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Mon, 16 Sep 2019 17:45:30 -0400
Subject: fixed maximizing of summarized documents and docs in stacking views.

---
 src/client/views/collections/CollectionStackingView.tsx         | 2 +-
 src/client/views/collections/collectionFreeForm/MarqueeView.tsx | 5 +++--
 src/client/views/nodes/DocumentView.tsx                         | 6 +++---
 3 files changed, 7 insertions(+), 6 deletions(-)

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

diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 14a9dc9d9..0a96676e3 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -163,7 +163,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
         </CollectionSchemaPreview>;
     }
     getDocHeight(d?: Doc) {
-        if (!d) return 0;
+        if (!d || d.willMaximize) return 0;
         let nw = NumCast(d.nativeWidth);
         let nh = NumCast(d.nativeHeight);
         if (!d.ignoreAspect && nw && nh) {
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index d74fbafb3..8decebe0d 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -327,8 +327,8 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
                 });
                 newCollection.chromeStatus = "disabled";
                 let summary = Docs.Create.TextDocument({ x: bounds.left, y: bounds.top, width: 300, height: 100, autoHeight: true, backgroundColor: "#e2ad32" /* yellow */, title: "-summary-" });
-                Doc.GetProto(summary).maximizeLocation = "inTab";  // or "inPlace", or "onRight"
                 Doc.GetProto(newCollection).summaryDoc = summary;
+                Doc.GetProto(summary).summarizedDocs = new List<Doc>([newCollection]);
                 newCollection.x = bounds.left + bounds.width;
                 let computed = CompileScript(`return summaryTitle(this);`, { params: { this: "Doc" }, typecheck: false });
                 computed.compiled && (Doc.GetProto(newCollection).title = new ComputedField(computed));
@@ -336,9 +336,10 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
                     let container = Docs.Create.FreeformDocument([summary, newCollection], { x: bounds.left, y: bounds.top, width: 300, height: 200, chromeStatus: "disabled", title: "-summary-" });
                     container.viewType = CollectionViewType.Stacking;
                     container.autoHeight = true;
+                    Doc.GetProto(summary).maximizeLocation = "inPlace";  // or "inPlace", or "onRight"
                     this.props.addLiveTextDocument(container);
                 } else if (e.key === "S") { // the summary stands alone, but is linked to a collection of the summarized documents - set the OnCLick behavior to link follow to access them
-                    summary.proto!.summarizedDocs = new List<Doc>([newCollection]);
+                    Doc.GetProto(summary).maximizeLocation = "inTab";  // or "inPlace", or "onRight"
                     this.props.addLiveTextDocument(summary);
                 }
             }
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 50a9aa326..c9bd60d35 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -300,7 +300,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                         maxLocation = this.props.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 && !BoolCast(d.IsMinimized), false);
+                            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) {
@@ -814,8 +814,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             this.props.backgroundColor(this.layoutDoc) || StrCast(this.layoutDoc.backgroundColor) :
             ruleColor && !colorSet ? ruleColor : StrCast(this.layoutDoc.backgroundColor) || this.props.backgroundColor(this.layoutDoc);
         let foregroundColor = StrCast(this.layoutDoc.color);
-        var nativeWidth = this.nativeWidth > 0 && !BoolCast(this.props.Document.ignoreAspect) ? `${this.nativeWidth}px` : "100%";
-        var nativeHeight = BoolCast(this.props.Document.ignoreAspect) ? this.props.PanelHeight() / this.props.ContentScaling() : this.nativeHeight > 0 ? `${this.nativeHeight}px` : "100%";
+        var nativeWidth = this.props.Document.willMaximize ? 0 : this.nativeWidth > 0 && !BoolCast(this.props.Document.ignoreAspect) ? `${this.nativeWidth}px` : "100%";
+        var nativeHeight = this.props.Document.willMaximize ? 0 : BoolCast(this.props.Document.ignoreAspect) ? this.props.PanelHeight() / this.props.ContentScaling() : this.nativeHeight > 0 ? `${this.nativeHeight}px` : "100%";
         let showOverlays = this.props.showOverlays ? this.props.showOverlays(this.layoutDoc) : undefined;
         let showTitle = showOverlays && "title" in showOverlays ? showOverlays.title : StrCast(this.layoutDoc.showTitle);
         let showCaption = showOverlays && "caption" in showOverlays ? showOverlays.caption : StrCast(this.layoutDoc.showCaption);
-- 
cgit v1.2.3-70-g09d2


From 5b09512cf4aa2319e498a233c16dba93ae83fbda Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Tue, 17 Sep 2019 11:26:54 -0400
Subject: lots of code cleanup in collectionFreeFormDocumentView and
 DocumentView

---
 src/Utils.ts                                       |   5 +
 src/client/views/DocumentDecorations.tsx           |   3 +-
 src/client/views/GlobalKeyHandler.ts               |   2 +-
 .../views/collections/CollectionDockingView.tsx    |   5 +
 .../collectionFreeForm/CollectionFreeFormView.tsx  |   2 +-
 .../document_templates/image_card/ImageCard.tsx    |   3 -
 src/client/views/linking/LinkFollowBox.tsx         |   2 -
 .../nodes/CollectionFreeFormDocumentView.scss      |   5 +
 .../views/nodes/CollectionFreeFormDocumentView.tsx |  48 ++--
 src/client/views/nodes/DocumentView.scss           |  42 +++
 src/client/views/nodes/DocumentView.tsx            | 286 ++++++++++-----------
 src/client/views/nodes/ImageBox.tsx                |   5 +-
 12 files changed, 215 insertions(+), 193 deletions(-)
 create mode 100644 src/client/views/nodes/CollectionFreeFormDocumentView.scss

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

diff --git a/src/Utils.ts b/src/Utils.ts
index 3921a49c3..415023ac4 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -37,6 +37,7 @@ export class Utils {
     public static prepend(extension: string): string {
         return window.location.origin + extension;
     }
+
     public static CorsProxy(url: string): string {
         return this.prepend(RouteStore.corsProxy + "/") + encodeURIComponent(url);
     }
@@ -239,6 +240,10 @@ export function timenow() {
     return now.toLocaleDateString() + ' ' + h + ':' + m + ' ' + ampm;
 }
 
+export function percent2frac(percent: string) {
+    return Number(percent.substr(0, percent.length - 1)) / 100;
+}
+
 export function numberRange(num: number) { return Array.from(Array(num)).map((v, i) => i); }
 
 export function returnTrue() { return true; }
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 240abaf6b..7829bd7f1 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -16,7 +16,7 @@ import { undoBatch, UndoManager } from "../util/UndoManager";
 import { MINIMIZED_ICON_SIZE } from "../views/globalCssVariables.scss";
 import { CollectionView } from "./collections/CollectionView";
 import './DocumentDecorations.scss';
-import { DocumentView, PositionDocument } from "./nodes/DocumentView";
+import { DocumentView } from "./nodes/DocumentView";
 import { FieldView } from "./nodes/FieldView";
 import { FormattedTextBox, GoogleRef } from "./nodes/FormattedTextBox";
 import { IconBox } from "./nodes/IconBox";
@@ -34,6 +34,7 @@ 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/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index f9ee22f61..ba125d6e5 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -166,7 +166,7 @@ export default class KeyManager {
                 break;
             case "o":
                 let target = SelectionManager.SelectedDocuments()[0];
-                target && target.fullScreenClicked();
+                target && CollectionDockingView.Instance && CollectionDockingView.Instance.OpenFullScreen(target)
                 break;
             case "r":
                 preventDefault = false;
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index bf55dba31..a350cfcc5 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -86,6 +86,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
         dragSource._dragListener.onMouseDown(e);
     }
 
+    @undoBatch
     @action
     public OpenFullScreen(docView: DocumentView) {
         let document = Doc.MakeAlias(docView.props.Document);
@@ -101,6 +102,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
         this._maximizedSrc = docView;
         this._ignoreStateChange = JSON.stringify(this._goldenLayout.toConfig());
         this.stateChanged();
+        SelectionManager.DeselectAll();
     }
 
     public CloseFullScreen = () => {
@@ -169,6 +171,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
     //
     //  Creates a vertical split on the right side of the docking view, and then adds the Document to that split
     //
+    @undoBatch
     @action
     public AddRightSplit = (document: Doc, dataDoc: Doc | undefined, minimize: boolean = false) => {
         let docs = Cast(this.props.Document.data, listSpec(Doc));
@@ -207,6 +210,8 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
 
         return newContentItem;
     }
+
+    @undoBatch
     @action
     public AddTab = (stack: any, document: Doc, dataDocument: Doc | undefined) => {
         Doc.GetProto(document).lastOpened = new DateField;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index e2d9bbb33..f32843d98 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -862,7 +862,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
         let layoutItems: ContextMenuProps[] = [];
 
         if (this.childDocs.some(d => BoolCast(d.isTemplate))) {
-            layoutItems.push({ description: "Template Layout Instance", event: () => this.props.addDocTab && this.props.addDocTab(Doc.ApplyTemplate(this.props.Document)!, undefined, "onRight"), icon: "project-diagram" });
+            layoutItems.push({ description: "Template Layout Instance", event: () => this.props.addDocTab(Doc.ApplyTemplate(this.props.Document)!, undefined, "onRight"), icon: "project-diagram" });
         }
         layoutItems.push({ description: "reset view", event: () => { this.props.Document.panX = this.props.Document.panY = 0; this.props.Document.scale = 1; }, icon: "compress-arrows-alt" });
         layoutItems.push({ description: `${this.fitToBox ? "Unset" : "Set"} Fit To Container`, event: async () => this.Document.fitToBox = !this.fitToBox, icon: !this.fitToBox ? "expand-arrows-alt" : "compress-arrows-alt" });
diff --git a/src/client/views/document_templates/image_card/ImageCard.tsx b/src/client/views/document_templates/image_card/ImageCard.tsx
index 9931515f3..868afc423 100644
--- a/src/client/views/document_templates/image_card/ImageCard.tsx
+++ b/src/client/views/document_templates/image_card/ImageCard.tsx
@@ -1,8 +1,5 @@
 import * as React from 'react';
-import { DocComponent } from '../../DocComponent';
 import { FieldViewProps } from '../../nodes/FieldView';
-import { createSchema, makeInterface } from '../../../../new_fields/Schema';
-import { createInterface } from 'readline';
 import { ImageBox } from '../../nodes/ImageBox';
 
 export default class ImageCard extends React.Component<FieldViewProps> {
diff --git a/src/client/views/linking/LinkFollowBox.tsx b/src/client/views/linking/LinkFollowBox.tsx
index f8807641b..b1c6c367f 100644
--- a/src/client/views/linking/LinkFollowBox.tsx
+++ b/src/client/views/linking/LinkFollowBox.tsx
@@ -172,7 +172,6 @@ export class LinkFollowBox extends React.Component<FieldViewProps> {
         if (LinkFollowBox.destinationDoc) {
             let view: DocumentView | null = DocumentManager.Instance.getDocumentView(LinkFollowBox.destinationDoc);
             view && CollectionDockingView.Instance && CollectionDockingView.Instance.OpenFullScreen(view);
-            SelectionManager.DeselectAll();
         }
     }
 
@@ -188,7 +187,6 @@ export class LinkFollowBox extends React.Component<FieldViewProps> {
             let view = DocumentManager.Instance.getDocumentView(options.context);
             view && CollectionDockingView.Instance && CollectionDockingView.Instance.OpenFullScreen(view);
             this.highlightDoc();
-            SelectionManager.DeselectAll();
         }
     }
 
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.scss b/src/client/views/nodes/CollectionFreeFormDocumentView.scss
new file mode 100644
index 000000000..c0d9e1267
--- /dev/null
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.scss
@@ -0,0 +1,5 @@
+.collectionFreeFormDocumentView-container {
+    transform-origin: left top;
+    position: absolute;
+    background-color: transparent;
+}
\ No newline at end of file
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 865eb27d5..9f0bd736d 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -1,11 +1,12 @@
 import { computed } from "mobx";
 import { observer } from "mobx-react";
 import { createSchema, makeInterface } from "../../../new_fields/Schema";
-import { BoolCast, FieldValue, NumCast, StrCast, Cast } from "../../../new_fields/Types";
+import { FieldValue, NumCast, StrCast, Cast } from "../../../new_fields/Types";
 import { Transform } from "../../util/Transform";
 import { DocComponent } from "../DocComponent";
-import { DocumentView, DocumentViewProps, positionSchema } from "./DocumentView";
-import "./DocumentView.scss";
+import { percent2frac } from "../../../Utils"
+import { DocumentView, DocumentViewProps, documentSchema } from "./DocumentView";
+import "./CollectionFreeFormDocumentView.scss";
 import React = require("react");
 import { Doc } from "../../../new_fields/Doc";
 import { random } from "animejs";
@@ -17,30 +18,31 @@ export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps {
     height?: number;
     jitterRotation: number;
 }
-
-const schema = createSchema({
+const positionSchema = createSchema({
     zIndex: "number",
+    x: "number",
+    y: "number",
+    z: "number",
 });
 
-//TODO Types: The import order is wrong, so positionSchema is undefined
-type FreeformDocument = makeInterface<[typeof schema, typeof positionSchema]>;
-const FreeformDocument = makeInterface(schema, positionSchema);
+export type PositionDocument = makeInterface<[typeof documentSchema, typeof positionSchema]>;
+export const PositionDocument = makeInterface(documentSchema, positionSchema);
 
 @observer
-export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeFormDocumentViewProps, FreeformDocument>(FreeformDocument) {
+export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeFormDocumentViewProps, PositionDocument>(PositionDocument) {
     @computed get transform() { return `scale(${this.props.ContentScaling()}) translate(${this.X}px, ${this.Y}px) rotate(${random(-1, 1) * this.props.jitterRotation}deg)`; }
     @computed get X() { return this.renderScriptDim ? this.renderScriptDim.x : this.props.x !== undefined ? this.props.x : this.Document.x || 0; }
     @computed get Y() { return this.renderScriptDim ? this.renderScriptDim.y : this.props.y !== undefined ? this.props.y : this.Document.y || 0; }
-    @computed get width(): number { return BoolCast(this.props.Document.willMaximize) ? 0 : this.renderScriptDim ? this.renderScriptDim.width : this.props.width !== undefined ? this.props.width : this.Document.width || 0; }
-    @computed get height(): number { return BoolCast(this.props.Document.willMaximize) ? 0 : this.renderScriptDim ? this.renderScriptDim.height : this.props.height !== undefined ? this.props.height : this.Document.height || 0; }
-    @computed get nativeWidth(): number { return FieldValue(this.Document.nativeWidth, 0); }
-    @computed get nativeHeight(): number { return FieldValue(this.Document.nativeHeight, 0); }
-    @computed get scaleToOverridingWidth() { return this.width / NumCast(this.props.Document.width, this.width); }
+    @computed get width() { return this.Document.willMaximize ? 0 : this.renderScriptDim ? this.renderScriptDim.width : this.props.width !== undefined ? this.props.width : this.Document.width || 0; }
+    @computed get height() { return this.Document.willMaximize ? 0 : this.renderScriptDim ? this.renderScriptDim.height : this.props.height !== undefined ? this.props.height : this.Document.height || 0; }
+    @computed get nativeWidth() { return FieldValue(this.Document.nativeWidth, 0); }
+    @computed get nativeHeight() { return FieldValue(this.Document.nativeHeight, 0); }
+    @computed get scaleToOverridingWidth() { return this.width / FieldValue(this.Document.width, this.width); }
 
     @computed get renderScriptDim() {
         if (this.Document.renderScript) {
-            let someView = Cast(this.Document.someView, Doc);
-            let minimap = Cast(this.Document.minimap, Doc);
+            let someView = Cast(this.props.Document.someView, Doc);
+            let minimap = Cast(this.props.Document.minimap, Doc);
             if (someView instanceof Doc && minimap instanceof Doc) {
                 let x = (NumCast(someView.panX) - NumCast(someView.width) / 2 / NumCast(someView.scale) - (NumCast(minimap.fitX) - NumCast(minimap.fitW) / 2)) / NumCast(minimap.fitW) * NumCast(minimap.width) - NumCast(minimap.width) / 2;
                 let y = (NumCast(someView.panY) - NumCast(someView.height) / 2 / NumCast(someView.scale) - (NumCast(minimap.fitY) - NumCast(minimap.fitH) / 2)) / NumCast(minimap.fitH) * NumCast(minimap.height) - NumCast(minimap.height) / 2;
@@ -52,7 +54,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
         return undefined;
     }
 
-    contentScaling = () => this.nativeWidth > 0 && !BoolCast(this.props.Document.ignoreAspect) ? this.width / this.nativeWidth : 1;
+    contentScaling = () => this.nativeWidth > 0 && !this.props.Document.ignoreAspect ? this.width / this.nativeWidth : 1;
     panelWidth = () => this.props.PanelWidth();
     panelHeight = () => this.props.PanelHeight();
     getTransform = (): Transform => this.props.ScreenToLocalTransform()
@@ -74,15 +76,12 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
     }
 
     borderRounding = () => {
-        let ruleProvider = this.props.ruleProvider;
-        let ruleRounding = ruleProvider ? StrCast(Doc.GetProto(ruleProvider)["ruleRounding_" + NumCast(this.props.Document.heading)]) : undefined;
-        let br = StrCast(this.layoutDoc.layout instanceof Doc ? this.layoutDoc.layout.borderRounding : this.props.Document.borderRounding);
+        let ruleRounding = this.props.ruleProvider ? StrCast(this.props.ruleProvider["ruleRounding_" + this.Document.heading]) : undefined;
+        let br = StrCast(((this.layoutDoc.layout as Doc) || this.Document).borderRounding);
         br = !br && ruleRounding ? ruleRounding : br;
         if (br.endsWith("%")) {
-            let percent = Number(br.substr(0, br.length - 1)) / 100;
             let nativeDim = Math.min(NumCast(this.layoutDoc.nativeWidth), NumCast(this.layoutDoc.nativeHeight));
-            let minDim = percent * (nativeDim ? nativeDim : Math.min(this.props.PanelWidth(), this.props.PanelHeight()));
-            return minDim;
+            return percent2frac(br) * (nativeDim ? nativeDim : Math.min(this.props.PanelWidth(), this.props.PanelHeight()));
         }
         return undefined;
     }
@@ -103,9 +102,6 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
         return (
             <div className="collectionFreeFormDocumentView-container"
                 style={{
-                    transformOrigin: "left top",
-                    position: "absolute",
-                    backgroundColor: "transparent",
                     boxShadow:
                         this.layoutDoc.opacity === 0 ? undefined :  // if it's not visible, then no shadow
                             this.layoutDoc.z ? `#9c9396  ${StrCast(this.layoutDoc.boxShadow, "10px 10px 0.9vw")}` :  // if it's a floating doc, give it a big shadow
diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss
index 7c72fb6e6..63011f271 100644
--- a/src/client/views/nodes/DocumentView.scss
+++ b/src/client/views/nodes/DocumentView.scss
@@ -4,6 +4,8 @@
   position: inherit;
   top: 0;
   left:0;
+  border-radius: inherit;
+                    
  // background: $light-color; //overflow: hidden;
   transform-origin: left top;
 
@@ -30,4 +32,44 @@
 }
 .documentView-node-topmost {
     background: white;
+}
+
+.documentView-styleWrapper {
+    position: absolute; 
+    display: inline-block;
+    width:100%;
+    height:100%;
+    pointer-events: none;
+
+    .documentView-styleContentWrapper {
+        width:100%;
+        display: inline-block;
+        position: absolute;
+    }
+    .documentView-titleWrapper {
+        overflow:hidden; 
+        color: white; 
+        transform-origin: top left; 
+        top: 0; 
+        height: 25;
+        background: rgba(0, 0, 0, .4);
+        padding: 4px; 
+        text-align: center; 
+        text-overflow: ellipsis; 
+        white-space: pre;
+    }
+
+    .documentView-searchHighlight {
+        position: absolute; 
+        background: yellow; 
+        bottom: -20px;
+        border-radius: 5px;
+        transform-origin: bottom left; 
+    }
+
+    .documentView-captionWrapper {
+        position: absolute; 
+        bottom: 0; 
+        transform-origin: bottom left;
+    }
 }
\ No newline at end of file
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index c9bd60d35..92acfffb3 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -106,31 +106,38 @@ export interface DocumentViewProps {
     ChromeHeight?: () => number;
 }
 
-const schema = createSchema({
-    layout: "string",
+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,
-});
-
-export const positionSchema = createSchema({
-    nativeWidth: "number",
-    nativeHeight: "number",
+    willMaximize: "boolean",
+    ignoreAspect: "boolean",
+    autoHeight: "boolean",
+    isTemplate: "boolean",
+    isButton: "boolean",
+    isBackground: "boolean",
+    ignoreClick: "boolean",
+    type: "string",
+    maximizeLocation: "string",
+    lockedPosition: "boolean",
+    excludeFromLibrary: "boolean",
     width: "number",
     height: "number",
-    x: "number",
-    y: "number",
-    z: "number",
+    borderRounding: "string",
+    fitToBox: "boolean",
+    searchFields: "string",
+    heading: "number",
+    showCaption: "string",
+    showTitle: "string"
 });
 
-export type PositionDocument = makeInterface<[typeof positionSchema]>;
-export const PositionDocument = makeInterface(positionSchema);
-
-type Document = makeInterface<[typeof schema]>;
-const Document = makeInterface(schema);
+type Document = makeInterface<[typeof documentSchema]>;
+const Document = makeInterface(documentSchema);
 
 @observer
 export class DocumentView extends DocComponent<DocumentViewProps, Document>(Document) {
@@ -259,7 +266,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         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) {
             e.stopPropagation();
-            this.onClickHandler.script.run({ this: this.props.Document.isTemplate && this.props.DataDoc ? this.props.DataDoc : this.props.Document });
+            this.onClickHandler.script.run({ this: this.Document.isTemplate && this.props.DataDoc ? this.props.DataDoc : this.props.Document });
             e.preventDefault();
             return;
         }
@@ -268,23 +275,18 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         if (this._doubleTap && this.props.renderDepth) {
             e.stopPropagation();
             let fullScreenAlias = Doc.MakeAlias(this.props.Document);
-            fullScreenAlias.templates = new List<string>();
             Doc.UseDetailLayout(fullScreenAlias);
-            fullScreenAlias.showCaption = true;
+            fullScreenAlias.showCaption = "caption";
             this.props.addDocTab(fullScreenAlias, this.dataDoc, "inTab");
             SelectionManager.DeselectAll();
             Doc.UnBrushDoc(this.props.Document);
         }
-        else if (CurrentUserUtils.MainDocId !== this.props.Document[Id] &&
+        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)) {
-            if (BoolCast(this.props.Document.ignoreClick)) {
-                return;
-            }
             e.stopPropagation();
             SelectionManager.SelectDoc(this, e.ctrlKey);
-            let isExpander = (e.target as any).id === "isExpander";
-            if (BoolCast(this.props.Document.isButton) || this.props.Document.type === DocumentType.BUTTON || isExpander) {
+            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);
@@ -292,12 +294,12 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 expandedDocs = maximizedDocs ? [...maximizedDocs, ...expandedDocs] : expandedDocs;
                 expandedDocs = summarizedDocs ? [...summarizedDocs, ...expandedDocs] : expandedDocs;
                 // let expandedDocs = [ ...(maximizedDocs ? maximizedDocs : []), ...(summarizedDocs ? summarizedDocs : []),];
-                if (expandedDocs.length) {   // bcz: need a better way to associate behaviors with click events on widget-documents
+                if (expandedDocs.length) {
                     SelectionManager.DeselectAll();
-                    let maxLocation = StrCast(this.props.Document.maximizeLocation, "inPlace");
+                    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.props.Document.maximizeLocation = (ctrlKey ? maxLocation : (maxLocation === "inPlace" || !maxLocation ? "inTab" : "inPlace"));
+                        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);
@@ -379,8 +381,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             document.removeEventListener("pointermove", this.onPointerMove);
         }
         else if (!e.cancelBubble && this.active) {
-            if (!this.props.Document.excludeFromLibrary && (Math.abs(this._downX - e.clientX) > 3 || Math.abs(this._downY - e.clientY) > 3)) {
-                if (!e.altKey && !this.topMost && e.buttons === 1 && !BoolCast(this.props.Document.lockedPosition)) {
+            if (!this.Document.excludeFromLibrary && (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);
                     this.startDragging(this._downX, this._downY, e.ctrlKey || e.altKey ? "alias" : undefined, this._hitTemplateDrag);
@@ -400,39 +402,33 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     @undoBatch
     deleteClicked = (): void => { SelectionManager.DeselectAll(); this.props.removeDocument && this.props.removeDocument(this.props.Document); }
 
-    @undoBatch
-    fieldsClicked = (): void => {
-        let kvp = Docs.Create.KVPDocument(this.props.Document, { width: 300, height: 300 });
-        this.props.addDocTab(kvp, this.dataDoc, "onRight");
-    }
-
     @undoBatch
     makeNativeViewClicked = (): void => {
         makeNativeView(this.props.Document);
     }
     @undoBatch
     makeCustomViewClicked = (): void => {
-        this.props.Document.nativeLayout = this.props.Document.layout;
-        this.props.Document.nativeType = this.props.Document.type;
-        this.props.Document.nonCustomAutoHeight = this.props.Document.autoHeight;
-        this.props.Document.nonCustomWidth = this.props.Document.width;
-        this.props.Document.nonCustomHeight = this.props.Document.height;
-        this.props.Document.nonCustomNativeWidth = this.props.Document.nativeWidth;
-        this.props.Document.nonCustomNativeHeight = this.props.Document.nativeHeight;
-        this.props.Document.nonCustomIgnoreAspect = this.props.Document.ignoreAspect;
+        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.props.Document.type = DocumentType.TEMPLATE;
+                this.Document.type = DocumentType.TEMPLATE;
                 this.props.Document.layout = custom;
-                !custom.nativeWidth && (this.props.Document.nativeWidth = 0);
-                !custom.nativeHeight && (this.props.Document.nativeHeight = 0);
-                !custom.nativeWidth && (this.props.Document.ignoreAspect = true);
-                this.props.Document.autoHeight = this.props.Document.autoHeight;
-                this.props.Document.width = this.props.Document.customWidth;
-                this.props.Document.height = this.props.Document.customHeight;
-                this.props.Document.nativeWidth = this.props.Document.customNativeWidth;
-                this.props.Document.nativeHeight = this.props.Document.customNativeHeight;
-                this.props.Document.ignoreAspect = this.props.Document.ignoreAspect;
+                !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;
@@ -440,21 +436,21 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 this.props.Document.customNativeHeight = undefined;
                 this.props.Document.customIgnoreAspect = undefined;
             } else {
-                let options = { title: "data", width: NumCast(this.props.Document.width), x: -NumCast(this.props.Document.width) / 2, y: -NumCast(this.props.Document.height) / 2, };
-                let fieldTemplate = this.props.Document.type === DocumentType.TEXT ? Docs.Create.TextDocument(options) :
-                    this.props.Document.type === DocumentType.VID ? Docs.Create.VideoDocument("http://www.cs.brown.edu", options) :
+                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);
 
-                fieldTemplate.backgroundColor = StrCast(this.props.Document.backgroundColor);
+                fieldTemplate.backgroundColor = this.Document.backgroundColor;
                 fieldTemplate.heading = 1;
                 fieldTemplate.autoHeight = true;
 
-                let docTemplate = Docs.Create.FreeformDocument([fieldTemplate], { title: StrCast(this.Document.title) + "layout", width: NumCast(this.props.Document.width) + 20, height: Math.max(100, NumCast(this.props.Document.height) + 45) });
+                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);
 
                 Doc.ApplyTemplateTo(docTemplate, this.props.Document, undefined, false);
-                Doc.GetProto(this.dataDoc || this.props.Document).customLayout = this.props.Document.layout;
+                Doc.GetProto(this.dataDoc || this.props.Document).customLayout = this.Document.layout;
             }
         });
     }
@@ -468,21 +464,6 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         } else {
             doc.isButton = true;
         }
-
-        // if (doc.isButton) {
-        //     if (!doc.nativeWidth) {
-        //         doc.nativeWidth = this.props.Document[WidthSym]();
-        //         doc.nativeHeight = this.props.Document[HeightSym]();
-        //     }
-        // } else {
-        //     doc.nativeWidth = doc.nativeHeight = undefined;
-        // }
-    }
-
-    @undoBatch
-    public fullScreenClicked = (): void => {
-        CollectionDockingView.Instance && CollectionDockingView.Instance.OpenFullScreen(this);
-        SelectionManager.DeselectAll();
     }
 
     @undoBatch
@@ -543,10 +524,10 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     @undoBatch
     @action
     freezeNativeDimensions = (): void => {
-        let proto = this.props.Document.isTemplate ? this.props.Document : Doc.GetProto(this.props.Document);
-        this.props.Document.autoHeight = proto.autoHeight = false;
-        proto.ignoreAspect = !BoolCast(proto.ignoreAspect);
-        if (!BoolCast(proto.ignoreAspect) && !proto.nativeWidth) {
+        let proto = this.Document.isTemplate ? this.props.Document : Doc.GetProto(this.props.Document);
+        proto.autoHeight = this.Document.autoHeight = false;
+        proto.ignoreAspect = !proto.ignoreAspect;
+        if (!proto.ignoreAspect && !proto.nativeWidth) {
             proto.nativeWidth = this.props.PanelWidth();
             proto.nativeHeight = this.props.PanelHeight();
         }
@@ -555,12 +536,12 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     @action
     makeIntoPortal = (): void => {
         if (!DocListCast(this.props.Document.links).find(doc => {
-            if (Cast(doc.anchor2, Doc) instanceof Doc && (Cast(doc.anchor2, Doc) as Doc)!.title === this.props.Document.title + ".portal") return true;
+            if (Cast(doc.anchor2, Doc) instanceof Doc && (Cast(doc.anchor2, Doc) as Doc)!.title === this.Document.title + ".portal") return true;
             return false;
         })) {
-            let portalID = (this.props.Document.title + ".portal").replace(/^-/, "").replace(/\([0-9]*\)$/, "");
+            let portalID = (this.Document.title + ".portal").replace(/^-/, "").replace(/\([0-9]*\)$/, "");
             DocServer.GetRefField(portalID).then(existingPortal => {
-                let portal = existingPortal instanceof Doc ? existingPortal : Docs.Create.FreeformDocument([], { width: this.props.Document[WidthSym]() + 10, height: this.props.Document[HeightSym](), title: portalID });
+                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;
             })
@@ -618,15 +599,14 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
 
         const cm = ContextMenu.Instance;
         let subitems: ContextMenuProps[] = [];
-        subitems.push({ description: "Open Full Screen", event: this.fullScreenClicked, icon: "desktop" });
-        subitems.push({ description: "Open Tab", event: () => this.props.addDocTab && this.props.addDocTab(this.props.Document, this.dataDoc, "inTab"), icon: "folder" });
-        subitems.push({ description: "Open Tab Alias", event: () => this.props.addDocTab && this.props.addDocTab(Doc.MakeAlias(this.props.Document), this.dataDoc, "inTab"), icon: "folder" });
-        subitems.push({ description: "Open Right", event: () => this.props.addDocTab && this.props.addDocTab(this.props.Document, this.dataDoc, "onRight"), icon: "caret-square-right" });
-        subitems.push({ description: "Open Right Alias", event: () => this.props.addDocTab && this.props.addDocTab(Doc.MakeAlias(this.props.Document), this.dataDoc, "onRight"), icon: "caret-square-right" });
-        subitems.push({ description: "Open Fields", event: this.fieldsClicked, icon: "layer-group" });
+        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 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" });
 
-
         let existingOnClick = ContextMenu.Instance.findByDescription("OnClick...");
         let onClicks: ContextMenuProps[] = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : [];
         onClicks.push({ description: "Enter Portal", event: this.makeIntoPortal, icon: "window-restore" });
@@ -643,7 +623,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             }, 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.props.Document.isButton || this.props.Document.onClick ? "Remove Click Behavior" : "Follow Link", event: this.makeBtnClicked, icon: "concierge-bell" });
+        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({
             description: "Edit onClick Foreach Doc Script", icon: "edit", event: (obj: any) => {
@@ -655,20 +635,20 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
 
         let existing = ContextMenu.Instance.findByDescription("Layout...");
         let layoutItems: ContextMenuProps[] = existing && "subitems" in existing ? existing.subitems : [];
-        layoutItems.push({ description: this.props.Document.isBackground ? "As Foreground" : "As Background", event: this.makeBackground, icon: this.props.Document.lockedPosition ? "unlock" : "lock" });
+        layoutItems.push({ description: this.Document.isBackground ? "As Foreground" : "As Background", event: this.makeBackground, icon: this.Document.lockedPosition ? "unlock" : "lock" });
         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.props.Document.ignoreAspect || !this.props.Document.nativeWidth || !this.props.Document.nativeHeight ? "Freeze" : "Unfreeze", event: this.freezeNativeDimensions, icon: "snowflake" });
+        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: "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.props.Document.isTemplate) {
+        if (this.props.Document.detailedLayout && !this.Document.isTemplate) {
             layoutItems.push({ description: "Toggle detail", event: () => Doc.ToggleDetailLayout(this.props.Document), icon: "image" });
         }
-        if (this.props.Document.type !== DocumentType.COL && this.props.Document.type !== DocumentType.TEMPLATE) {
+        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) {
             layoutItems.push({ description: "Use Native Layout", event: this.makeNativeViewClicked, icon: "concierge-bell" });
@@ -700,7 +680,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             }
         });
 
-        cm.addItem({ description: "Publish", event: () => DocUtils.Publish(this.props.Document, StrCast(this.props.Document.title), this.props.addDocument, this.props.removeDocument), icon: "file" });
+        cm.addItem({ description: "Publish", event: () => DocUtils.Publish(this.props.Document, this.Document.title || "", this.props.addDocument, this.props.removeDocument), icon: "file" });
         cm.addItem({ description: "Delete", event: this.deleteClicked, icon: "trash" });
         type User = { email: string, userDocumentId: string };
         let usersMenu: ContextMenuProps[] = [];
@@ -780,7 +760,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             select={this.select}
             onClick={this.onClickHandler}
             layoutKey={"layout"}
-            fitToBox={BoolCast(this.props.Document.fitToBox) ? true : this.props.fitToBox}
+            fitToBox={this.Document.fitToBox ? true : this.props.fitToBox}
             DataDoc={this.dataDoc} />);
     }
 
@@ -796,55 +776,66 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         return (showTitle ? 25 : 0) + 1;// bcz: why 8??
     }
 
-    get layoutDoc() {
+    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 this.props.Document.layout instanceof Doc ? this.props.Document.layout : this.props.Document;
+        return Document(this.props.Document.layout instanceof Doc ? this.props.Document.layout : this.props.Document);
     }
 
-
     render() {
-        let ruleProvider = this.props.ruleProvider;
-        let ruleColor = ruleProvider ? StrCast(Doc.GetProto(ruleProvider)["ruleColor_" + NumCast(this.props.Document.heading)]) : undefined;
-        let ruleRounding = ruleProvider ? StrCast(Doc.GetProto(ruleProvider)["ruleRounding_" + NumCast(this.props.Document.heading)]) : undefined;
-        let colorSet = this.layoutDoc.backgroundColor !== this.layoutDoc.defaultBackgroundColor;
-        let clusterCol = this.props.ContainingCollectionView && this.props.ContainingCollectionView.props.Document.clusterOverridesDefaultBackground;
-
-        let backgroundColor = this.layoutDoc.isBackground || (clusterCol && !colorSet) ?
+        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 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);
-        let foregroundColor = StrCast(this.layoutDoc.color);
-        var nativeWidth = this.props.Document.willMaximize ? 0 : this.nativeWidth > 0 && !BoolCast(this.props.Document.ignoreAspect) ? `${this.nativeWidth}px` : "100%";
-        var nativeHeight = this.props.Document.willMaximize ? 0 : BoolCast(this.props.Document.ignoreAspect) ? this.props.PanelHeight() / this.props.ContentScaling() : this.nativeHeight > 0 ? `${this.nativeHeight}px` : "100%";
-        let showOverlays = this.props.showOverlays ? this.props.showOverlays(this.layoutDoc) : undefined;
-        let showTitle = showOverlays && "title" in showOverlays ? showOverlays.title : StrCast(this.layoutDoc.showTitle);
-        let showCaption = showOverlays && "caption" in showOverlays ? showOverlays.caption : StrCast(this.layoutDoc.showCaption);
-        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";
-                if (!showCaption && str.indexOf("fieldKey={\"caption\"}") !== -1) showCaption = "caption";
-            });
-        }
-        let showTextTitle = showTitle && StrCast(this.layoutDoc.layout).startsWith("<FormattedTextBox") ? showTitle : undefined;
-        let fullDegree = Doc.isBrushedHighlightedDegree(this.props.Document);
-        let borderRounding = StrCast(Doc.GetProto(this.props.Document).borderRounding, ruleRounding);
-        let localScale = this.props.ScreenToLocalTransform().Scale * fullDegree;
-        let searchHighlight = (!this.props.Document.search_fields ? (null) :
-            <div key="search" style={{ position: "absolute", background: "yellow", bottom: "-20px", borderRadius: "5px", transformOrigin: "bottom left", width: `${100 * this.props.ContentScaling()}%`, transform: `scale(${1 / this.props.ContentScaling()})` }}>
-                {StrCast(this.props.Document.search_fields)}
+
+        const nativeWidth = this.Document.willMaximize ? 0 : this.nativeWidth > 0 && !this.Document.ignoreAspect ? `${this.nativeWidth}px` : "100%";
+        const nativeHeight = this.Document.willMaximize ? 0 : 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 fullDegree = Doc.isBrushedHighlightedDegree(this.props.Document);
+        const borderRounding = this.Document.borderRounding || ruleRounding;
+        const localScale = this.props.ScreenToLocalTransform().Scale * fullDegree;
+        const searchHighlight = (!this.Document.searchFields ? (null) :
+            <div className="documentView-searchHighlight" style={{ width: `${100 * this.props.ContentScaling()}%`, transform: `scale(${1 / this.props.ContentScaling()})` }}>
+                {this.Document.searchFields}
+            </div>);
+        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}
+                    isSelected={this.isSelected} focus={emptyFunction} select={this.select}
+                    fieldExt={""} hideOnLeave={true} fieldKey={showCaption}
+                />
+            </div>);
+        const titleView = (!showTitle ? (null) :
+            <div className="documentView-titleWrapper" style={{
+                position: showTextTitle ? "relative" : "absolute",
+                pointerEvents: SelectionManager.GetIsDragging() ? "none" : "all",
+                width: `${100 * this.props.ContentScaling()}%`,
+                transform: `scale(${1 / this.props.ContentScaling()})`
+            }}>
+                <EditableView
+                    contents={(this.layoutDoc.isTemplate || !this.dataDoc ? this.layoutDoc : this.dataDoc)[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}
+                />
             </div>);
         return (
             <div className={`documentView-node${this.topMost ? "-topmost" : ""}`}
                 ref={this._mainCont}
                 style={{
                     pointerEvents: this.layoutDoc.isBackground && !this.isSelected() ? "none" : "all",
-                    color: foregroundColor,
+                    color: StrCast(this.layoutDoc.color),
                     outlineColor: ["transparent", "maroon", "maroon", "yellow"][fullDegree],
                     outlineStyle: ["none", "dashed", "solid", "solid"][fullDegree],
                     outlineWidth: fullDegree && !borderRounding ? `${localScale}px` : "0px",
                     border: fullDegree && borderRounding ? `${["none", "dashed", "solid", "solid"][fullDegree]} ${["transparent", "maroon", "maroon", "yellow"][fullDegree]} ${localScale}px` : undefined,
-                    borderRadius: "inherit",
                     background: backgroundColor,
                     width: nativeWidth,
                     height: nativeHeight,
@@ -855,37 +846,20 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}
             >
                 {!showTitle && !showCaption ?
-                    this.props.Document.search_fields ? <div>
-                        {this.contents}
-                        {searchHighlight}
-                    </div> :
-                        this.contents :
-                    <div style={{ position: "absolute", display: "inline-block", width: "100%", height: "100%", pointerEvents: "none" }}>
-                        <div style={{ width: "100%", height: showTextTitle ? "calc(100% - 29px)" : "100%", display: "inline-block", position: "absolute", top: showTextTitle ? "29px" : undefined }}>
+                    this.Document.searchFields ?
+                        (<div className="documentView-searchWrapper">
+                            {this.contents}
+                            {searchHighlight}
+                        </div>)
+                        :
+                        this.contents
+                    :
+                    <div className="documentView-styleWrapper" >
+                        <div className="documentView-styleContentWrapper" style={{ height: showTextTitle ? "calc(100% - 29px)" : "100%", top: showTextTitle ? "29px" : undefined }}>
                             {this.contents}
                         </div>
-                        {!showTitle ? (null) :
-                            <div style={{
-                                position: showTextTitle ? "relative" : "absolute", top: 0, padding: "4px", textAlign: "center", textOverflow: "ellipsis", whiteSpace: "pre",
-                                pointerEvents: SelectionManager.GetIsDragging() ? "none" : "all",
-                                overflow: "hidden", width: `${100 * this.props.ContentScaling()}%`, height: 25, background: "rgba(0, 0, 0, .4)", color: "white",
-                                transformOrigin: "top left", transform: `scale(${1 / this.props.ContentScaling()})`
-                            }}>
-                                <EditableView
-                                    contents={(this.layoutDoc.isTemplate || !this.dataDoc ? this.layoutDoc : this.dataDoc)[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}
-                                />
-                            </div>
-                        }
-                        {!showCaption ? (null) :
-                            <div style={{ position: "absolute", bottom: 0, transformOrigin: "bottom left", width: `${100 * this.props.ContentScaling()}%`, transform: `scale(${1 / this.props.ContentScaling()})` }}>
-                                <FormattedTextBox {...this.props} onClick={this.onClickHandler} DataDoc={this.dataDoc} active={returnTrue} isSelected={this.isSelected} focus={emptyFunction} select={this.select} fieldExt={""} hideOnLeave={true} fieldKey={showCaption} />
-                            </div>
-                        }
+                        {titleView}
+                        {captionView}
                         {searchHighlight}
                     </div>
                 }
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 95f304641..beccce9dd 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -376,7 +376,6 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
         // let [bptX, bptY] = transform.transformPoint(pw, this.props.PanelHeight());
         // let w = bptX - sptX;
 
-        let id = (this.props as any).id; // bcz: used to set id = "isExpander" in templates.tsx
         let nativeWidth = FieldValue(this.Document.nativeWidth, pw);
         let nativeHeight = FieldValue(this.Document.nativeHeight, 0);
         let paths: string[] = [Utils.CorsProxy("http://www.cs.brown.edu/~bcz/noImage.png")];
@@ -402,11 +401,11 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
         if (!this.props.Document.ignoreAspect && !this.props.leaveNativeSize) this.resize(srcpath, this.props.Document);
 
         return (
-            <div id={id} className={`imageBox-cont${interactive}`} style={{ background: "transparent" }}
+            <div className={`imageBox-cont${interactive}`} style={{ background: "transparent" }}
                 onPointerDown={this.onPointerDown}
                 onDrop={this.onDrop} ref={this.createDropTarget} onContextMenu={this.specificContextMenu}>
                 <div id="cf">
-                    <img id={id}
+                    <img
                         key={this._smallRetryCount + (this._mediumRetryCount << 4) + (this._largeRetryCount << 8)} // force cache to update on retrys
                         src={srcpath}
                         style={{ transform: `translate(0px, ${shift}px) rotate(${rotation}deg) scale(${aspect})` }}
-- 
cgit v1.2.3-70-g09d2


From 9cd476b6cc7a3d72c6b2b96630506b04a5c6fb22 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Tue, 17 Sep 2019 13:03:10 -0400
Subject: minor icon tweaks.

---
 .../views/collections/collectionFreeForm/CollectionFreeFormView.tsx   | 2 +-
 src/client/views/nodes/DocumentView.tsx                               | 4 ++--
 src/client/views/nodes/IconBox.tsx                                    | 2 +-
 3 files changed, 4 insertions(+), 4 deletions(-)

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

diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index f32843d98..03ac012b4 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -668,7 +668,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
             addDocument: this.props.addDocument,
             removeDocument: this.props.removeDocument,
             moveDocument: this.props.moveDocument,
-            ruleProvider: this.Document.isRuleProvider && childLayout.type !== DocumentType.TEXT ? this.props.Document : this.props.ruleProvider,
+            ruleProvider: this.Document.isRuleProvider && childLayout.type !== DocumentType.TEXT ? this.props.Document : this.props.ruleProvider, //bcz: hack! - currently ruleProviders apply to documents in nested colleciton, not direct children of themselves
             onClick: undefined, // this.props.onClick,  // bcz: check this out -- I don't think we want to inherit click handlers, or we at least need a way to ignore them
             ScreenToLocalTransform: childLayout.z ? this.getTransformOverlay : this.getTransform,
             renderDepth: this.props.renderDepth + 1,
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 92acfffb3..f58587066 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -161,7 +161,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 handlers: { drop: this.drop.bind(this) }
             });
         }
-        this._animateToIconDisposer = reaction(() => this.props.Document.isIconAnimating, (values) =>
+        this._animateToIconDisposer = reaction(() => this.Document.isIconAnimating, (values) =>
             (values instanceof List) && this.animateBetweenIcon(values, values[2], values[3] ? true : false)
             , { fireImmediately: true });
         DocumentManager.Instance.DocumentViews.push(this);
@@ -169,7 +169,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
 
     animateBetweenIcon = (iconPos: number[], startTime: number, maximizing: boolean) => {
         this.props.animateBetweenIcon ? this.props.animateBetweenIcon(iconPos, startTime, maximizing) :
-            DocumentView.animateBetweenIconFunc(this.props.Document, this.Document[WidthSym](), this.Document[HeightSym](), startTime, maximizing);
+            DocumentView.animateBetweenIconFunc(this.props.Document, this.Document.width || 0, this.Document.height || 0, startTime, maximizing);
     }
 
     public static animateBetweenIconFunc = (doc: Doc, width: number, height: number, stime: number, maximizing: boolean, cb?: (progress: number) => void) => {
diff --git a/src/client/views/nodes/IconBox.tsx b/src/client/views/nodes/IconBox.tsx
index ef9885bcf..92cb5a9c9 100644
--- a/src/client/views/nodes/IconBox.tsx
+++ b/src/client/views/nodes/IconBox.tsx
@@ -38,7 +38,7 @@ export class IconBox extends React.Component<FieldViewProps> {
     }
     public static titleScript(inputDoc: Doc) {
         const maxDoc = DocListCast(inputDoc.maximizedDocs);
-        if (maxDoc.length === 1 && StrCast(maxDoc[0].title).startsWith("-")) {
+        if (maxDoc.length === 1) {
             return maxDoc[0].title + ".icon";
         }
         return maxDoc.length > 1 ? "-multiple-.icon" : "???";
-- 
cgit v1.2.3-70-g09d2


From 1310633790e3db50a31a1cc6d357306d7884a053 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Tue, 17 Sep 2019 15:30:02 -0400
Subject: cleaning up icon animations

---
 .../views/collections/CollectionStackingView.tsx   |  2 +-
 .../views/nodes/CollectionFreeFormDocumentView.tsx | 60 +++++++++-------
 src/client/views/nodes/DocumentView.tsx            | 82 +++++++++-------------
 src/new_fields/Doc.ts                              | 10 ++-
 4 files changed, 80 insertions(+), 74 deletions(-)

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

diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 0a96676e3..14a9dc9d9 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -163,7 +163,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
         </CollectionSchemaPreview>;
     }
     getDocHeight(d?: Doc) {
-        if (!d || d.willMaximize) return 0;
+        if (!d) return 0;
         let nw = NumCast(d.nativeWidth);
         let nh = NumCast(d.nativeHeight);
         if (!d.ignoreAspect && nw && nh) {
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 9f0bd736d..19d4a6784 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -1,6 +1,6 @@
-import { computed } from "mobx";
+import { computed, action, observable, reaction, IReactionDisposer } from "mobx";
 import { observer } from "mobx-react";
-import { createSchema, makeInterface } from "../../../new_fields/Schema";
+import { createSchema, makeInterface, listSpec } from "../../../new_fields/Schema";
 import { FieldValue, NumCast, StrCast, Cast } from "../../../new_fields/Types";
 import { Transform } from "../../util/Transform";
 import { DocComponent } from "../DocComponent";
@@ -8,7 +8,7 @@ import { percent2frac } from "../../../Utils"
 import { DocumentView, DocumentViewProps, documentSchema } from "./DocumentView";
 import "./CollectionFreeFormDocumentView.scss";
 import React = require("react");
-import { Doc } from "../../../new_fields/Doc";
+import { Doc, WidthSym, HeightSym } from "../../../new_fields/Doc";
 import { random } from "animejs";
 
 export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps {
@@ -30,11 +30,12 @@ export const PositionDocument = makeInterface(documentSchema, positionSchema);
 
 @observer
 export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeFormDocumentViewProps, PositionDocument>(PositionDocument) {
+    _disposer: IReactionDisposer | undefined = undefined;
     @computed get transform() { return `scale(${this.props.ContentScaling()}) translate(${this.X}px, ${this.Y}px) rotate(${random(-1, 1) * this.props.jitterRotation}deg)`; }
-    @computed get X() { return this.renderScriptDim ? this.renderScriptDim.x : this.props.x !== undefined ? this.props.x : this.Document.x || 0; }
-    @computed get Y() { return this.renderScriptDim ? this.renderScriptDim.y : this.props.y !== undefined ? this.props.y : this.Document.y || 0; }
-    @computed get width() { return this.Document.willMaximize ? 0 : this.renderScriptDim ? this.renderScriptDim.width : this.props.width !== undefined ? this.props.width : this.Document.width || 0; }
-    @computed get height() { return this.Document.willMaximize ? 0 : this.renderScriptDim ? this.renderScriptDim.height : this.props.height !== undefined ? this.props.height : this.Document.height || 0; }
+    @computed get X() { return this._animx !== undefined ? this._animx : this.renderScriptDim ? this.renderScriptDim.x : this.props.x !== undefined ? this.props.x : this.Document.x || 0; }
+    @computed get Y() { return this._animy !== undefined ? this._animy : this.renderScriptDim ? this.renderScriptDim.y : this.props.y !== undefined ? this.props.y : this.Document.y || 0; }
+    @computed get width() { return this.renderScriptDim ? this.renderScriptDim.width : this.props.width !== undefined ? this.props.width : this.props.Document[WidthSym](); }
+    @computed get height() { return this.renderScriptDim ? this.renderScriptDim.height : this.props.height !== undefined ? this.props.height : this.props.Document[HeightSym](); }
     @computed get nativeWidth() { return FieldValue(this.Document.nativeWidth, 0); }
     @computed get nativeHeight() { return FieldValue(this.Document.nativeHeight, 0); }
     @computed get scaleToOverridingWidth() { return this.width / FieldValue(this.Document.width, this.width); }
@@ -54,26 +55,35 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
         return undefined;
     }
 
+    componentWillUnmount() {
+        this._disposer && this._disposer();
+    }
+    componentDidMount() {
+        this._disposer = reaction(() => this.props.Document.iconTarget,
+            () => {
+                const icon = this.props.Document.iconTarget ? Array.from(Cast(this.props.Document.iconTarget, listSpec("number"))!) : undefined;
+                if (icon) {
+                    let target = this.props.ScreenToLocalTransform().transformPoint(icon[0], icon[1]);
+                    if (icon[2] === 1) {
+                        this._animx = target[0];
+                        this._animy = target[1];
+                    }
+                    setTimeout(action(() => {
+                        this._animx = icon[2] === 1 ? this.Document.x : target[0];
+                        this._animy = icon[2] === 1 ? this.Document.y : target[1];
+                    }), 25);
+                } else {
+                    this._animx = this._animy = undefined;
+                }
+            }, { fireImmediately: true });
+    }
+
     contentScaling = () => this.nativeWidth > 0 && !this.props.Document.ignoreAspect ? this.width / this.nativeWidth : 1;
     panelWidth = () => this.props.PanelWidth();
     panelHeight = () => this.props.PanelHeight();
     getTransform = (): Transform => this.props.ScreenToLocalTransform()
         .translate(-this.X, -this.Y)
-        .scale(1 / this.contentScaling()).scale(1 / this.scaleToOverridingWidth)
-
-    animateBetweenIcon = (icon: number[], stime: number, maximizing: boolean) => {
-        this.props.bringToFront(this.props.Document);
-        let targetPos = [this.Document.x || 0, this.Document.y || 0];
-        let iconPos = this.props.ScreenToLocalTransform().transformPoint(icon[0], icon[1]);
-        DocumentView.animateBetweenIconFunc(this.props.Document,
-            this.Document.width || 0, this.Document.height || 0, stime, maximizing, (progress: number) => {
-                let pval = maximizing ?
-                    [iconPos[0] + (targetPos[0] - iconPos[0]) * progress, iconPos[1] + (targetPos[1] - iconPos[1]) * progress] :
-                    [targetPos[0] + (iconPos[0] - targetPos[0]) * progress, targetPos[1] + (iconPos[1] - targetPos[1]) * progress];
-                this.Document.x = progress === 1 ? targetPos[0] : pval[0];
-                this.Document.y = progress === 1 ? targetPos[1] : pval[1];
-            });
-    }
+        .scale(1 / this.contentScaling()).scale(1 / this.scaleToOverridingWidth);
 
     borderRounding = () => {
         let ruleRounding = this.props.ruleProvider ? StrCast(this.props.ruleProvider["ruleRounding_" + this.Document.heading]) : undefined;
@@ -97,6 +107,9 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
         return this.props.Document.layout instanceof Doc ? this.props.Document.layout : this.props.Document;
     }
 
+    @observable _animx: number | undefined = undefined;
+    @observable _animy: number | undefined = undefined;
+
     render() {
         const hasPosition = this.props.x !== undefined || this.props.y !== undefined;
         return (
@@ -110,7 +123,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
                                         StrCast(this.layoutDoc.boxShadow, ""),
                     borderRadius: this.borderRounding(),
                     transform: this.transform,
-                    transition: hasPosition ? "transform 1s" : StrCast(this.layoutDoc.transition),
+                    transition: this.props.Document.isIconAnimating ? "transform .5s" : hasPosition ? "transform 1s" : StrCast(this.layoutDoc.transition),
                     width: this.width,
                     height: this.height,
                     zIndex: this.Document.zIndex || 0,
@@ -121,7 +134,6 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
                     backgroundColor={this.clusterColorFunc}
                     PanelWidth={this.panelWidth}
                     PanelHeight={this.panelHeight}
-                    animateBetweenIcon={this.animateBetweenIcon}
                 />
             </div>
         );
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index f58587066..37c38cc04 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -102,7 +102,7 @@ export interface DocumentViewProps {
     zoomToScale: (scale: number) => void;
     backgroundColor: (doc: Doc) => string | undefined;
     getScale: () => number;
-    animateBetweenIcon?: (iconPos: number[], startTime: number, maximizing: boolean) => void;
+    animateBetweenIcon?: (maximize: boolean, target: number[]) => void;
     ChromeHeight?: () => number;
 }
 
@@ -115,7 +115,6 @@ export const documentSchema = createSchema({
     opacity: "number",
     hidden: "boolean",
     onClick: ScriptField,
-    willMaximize: "boolean",
     ignoreAspect: "boolean",
     autoHeight: "boolean",
     isTemplate: "boolean",
@@ -148,7 +147,6 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     private _hitTemplateDrag = false;
     private _mainCont = React.createRef<HTMLDivElement>();
     private _dropDisposer?: DragManager.DragDropDisposer;
-    private _animateToIconDisposer?: IReactionDisposer;
 
     public get ContentDiv() { return this._mainCont.current; }
     @computed get active(): boolean { return SelectionManager.IsSelected(this) || this.props.parentActive(); }
@@ -161,35 +159,9 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 handlers: { drop: this.drop.bind(this) }
             });
         }
-        this._animateToIconDisposer = reaction(() => this.Document.isIconAnimating, (values) =>
-            (values instanceof List) && this.animateBetweenIcon(values, values[2], values[3] ? true : false)
-            , { fireImmediately: true });
         DocumentManager.Instance.DocumentViews.push(this);
     }
 
-    animateBetweenIcon = (iconPos: number[], startTime: number, maximizing: boolean) => {
-        this.props.animateBetweenIcon ? this.props.animateBetweenIcon(iconPos, startTime, maximizing) :
-            DocumentView.animateBetweenIconFunc(this.props.Document, this.Document.width || 0, this.Document.height || 0, startTime, maximizing);
-    }
-
-    public static animateBetweenIconFunc = (doc: Doc, width: number, height: number, stime: number, maximizing: boolean, cb?: (progress: number) => void) => {
-        setTimeout(() => {
-            let now = Date.now();
-            let progress = now < stime + 200 ? Math.min(1, (now - stime) / 200) : 1;
-            doc.width = progress === 1 ? width : maximizing ? 25 + (width - 25) * progress : width + (25 - width) * progress;
-            doc.height = progress === 1 ? height : maximizing ? 25 + (height - 25) * progress : height + (25 - height) * progress;
-            cb && cb(progress);
-            if (now < stime + 200) {
-                DocumentView.animateBetweenIconFunc(doc, width, height, stime, maximizing, cb);
-            }
-            else {
-                doc.isMinimized = !maximizing;
-                doc.isIconAnimating = undefined;
-            }
-            doc.willMaximize = false;
-        },
-            2);
-    }
     @action
     componentDidUpdate() {
         this._dropDisposer && this._dropDisposer();
@@ -201,7 +173,6 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     }
     @action
     componentWillUnmount() {
-        this._animateToIconDisposer && this._animateToIconDisposer();
         this._dropDisposer && this._dropDisposer();
         DocumentManager.Instance.DocumentViews.splice(DocumentManager.Instance.DocumentViews.indexOf(this), 1);
     }
@@ -235,30 +206,45 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         }
     }
 
-    static _undoBatch?: UndoManager.Batch = undefined;
     @action
     public collapseTargetsToPoint = (scrpt: number[], expandedDocs: Doc[] | undefined): void => {
         SelectionManager.DeselectAll();
         if (expandedDocs) {
-            if (!DocumentView._undoBatch) {
-                DocumentView._undoBatch = UndoManager.StartBatch("iconAnimating");
-            }
             let isMinimized: boolean | undefined;
             expandedDocs.map(maximizedDoc => {
-                let iconAnimating = Cast(maximizedDoc.isIconAnimating, List);
-                if (!iconAnimating || (Date.now() - iconAnimating[2] > 1000)) {
-                    if (isMinimized === undefined) {
-                        isMinimized = BoolCast(maximizedDoc.isMinimized);
+                if (isMinimized === undefined) {
+                    isMinimized = BoolCast(maximizedDoc.isMinimized);
+                }
+                let w = NumCast(maximizedDoc.width);
+                let h = NumCast(maximizedDoc.height);
+                let iconAnimating = maximizedDoc.isIconAnimating ? Array.from(Cast(maximizedDoc.isIconAnimating, listSpec("number"))!) : undefined;
+                if (isMinimized || (iconAnimating && iconAnimating.length && iconAnimating[0] === 0)) {
+                    // MAXIMIZE DOC
+                    if (maximizedDoc.isMinimized) {
+                        maximizedDoc.isIconAnimating = new List<number>([0, 0]);
+                        maximizedDoc.isMinimized = false;
                     }
-                    maximizedDoc.willMaximize = isMinimized;
-                    maximizedDoc.isMinimized = false;
-                    maximizedDoc.isIconAnimating = new List<number>([scrpt[0], scrpt[1], Date.now(), isMinimized ? 1 : 0]);
+                    maximizedDoc.iconTarget = new List<number>([...scrpt, 1]);
+                    setTimeout(() => {
+                        maximizedDoc.isIconAnimating = new List<number>([w, h]);
+                        setTimeout(() => {
+                            if (maximizedDoc.isIconAnimating && Array.from(Cast(maximizedDoc.isIconAnimating, listSpec("number"))!)[0] !== 0) {
+                                maximizedDoc.isIconAnimating = undefined;
+                            }
+                        }, 750);
+                    }, 0);
+                } else {
+                    maximizedDoc.iconTarget = new List<number>([...scrpt, 0]);
+                    // MINIMIZE DOC
+                    maximizedDoc.isIconAnimating = new List<number>([0, 0]);
+                    setTimeout(() => {
+                        if (maximizedDoc.isIconAnimating && Array.from(Cast(maximizedDoc.isIconAnimating, listSpec("number"))!)[0] === 0) {
+                            maximizedDoc.isMinimized = true;
+                            maximizedDoc.isIconAnimating = undefined;
+                        }
+                    }, 750);
                 }
             });
-            setTimeout(() => {
-                DocumentView._undoBatch && DocumentView._undoBatch.end();
-                DocumentView._undoBatch = undefined;
-            }, 500);
         }
     }
 
@@ -791,8 +777,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             this.props.backgroundColor(this.layoutDoc) || StrCast(this.layoutDoc.backgroundColor) :
             ruleColor && !colorSet ? ruleColor : StrCast(this.layoutDoc.backgroundColor) || this.props.backgroundColor(this.layoutDoc);
 
-        const nativeWidth = this.Document.willMaximize ? 0 : this.nativeWidth > 0 && !this.Document.ignoreAspect ? `${this.nativeWidth}px` : "100%";
-        const nativeHeight = this.Document.willMaximize ? 0 : this.Document.ignoreAspect ? this.props.PanelHeight() / this.props.ContentScaling() : this.nativeHeight > 0 ? `${this.nativeHeight}px` : "100%";
+        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;
@@ -800,6 +786,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         const fullDegree = Doc.isBrushedHighlightedDegree(this.props.Document);
         const borderRounding = this.Document.borderRounding || ruleRounding;
         const localScale = this.props.ScreenToLocalTransform().Scale * fullDegree;
+        const iconAnimating = this.Document.isIconAnimating ? Array.from(Cast(this.Document.isIconAnimating, listSpec("number"))!) : undefined;
         const searchHighlight = (!this.Document.searchFields ? (null) :
             <div className="documentView-searchHighlight" style={{ width: `${100 * this.props.ContentScaling()}%`, transform: `scale(${1 / this.props.ContentScaling()})` }}>
                 {this.Document.searchFields}
@@ -830,6 +817,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             <div className={`documentView-node${this.topMost ? "-topmost" : ""}`}
                 ref={this._mainCont}
                 style={{
+                    transition: iconAnimating ? "transform .5s" : StrCast(this.layoutDoc.transition),
                     pointerEvents: this.layoutDoc.isBackground && !this.isSelected() ? "none" : "all",
                     color: StrCast(this.layoutDoc.color),
                     outlineColor: ["transparent", "maroon", "maroon", "yellow"][fullDegree],
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index 2434a439a..614babd3c 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -143,8 +143,14 @@ export class Doc extends RefField {
 
     private [Self] = this;
     private [SelfProxy]: any;
-    public [WidthSym] = () => NumCast(this[SelfProxy].width);  // bcz: is this the right way to access width/height?   it didn't work with : this.width
-    public [HeightSym] = () => NumCast(this[SelfProxy].height);
+    public [WidthSym] = () => {
+        let iconAnimating = this[SelfProxy].isIconAnimating ? Array.from(Cast(this[SelfProxy].isIconAnimating, listSpec("number"))!) : undefined;
+        return iconAnimating ? iconAnimating[0] : NumCast(this[SelfProxy].width);
+    }
+    public [HeightSym] = () => {
+        let iconAnimating = this[SelfProxy].isIconAnimating ? Array.from(Cast(this[SelfProxy].isIconAnimating, listSpec("number"))!) : undefined;
+        return iconAnimating ? iconAnimating[1] : NumCast(this[SelfProxy].height);
+    }
 
     [ToScriptString]() {
         return "invalid";
-- 
cgit v1.2.3-70-g09d2

