From ae62e415d18b83fb1ef6b4a8f438b1d7e8d3157a Mon Sep 17 00:00:00 2001
From: HJF Bulterman <henri_bulterman@brown.edu>
Date: Fri, 9 Aug 2019 11:31:57 -0400
Subject: initial commit - individual presentation document view showing but
 with errors

---
 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 f101222ae..0ca303dde 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -553,7 +553,7 @@ 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" });
         cm.addItem({ description: BoolCast(this.props.Document.ignoreAspect, false) || !this.props.Document.nativeWidth || !this.props.Document.nativeHeight ? "Freeze" : "Unfreeze", event: this.freezeNativeDimensions, icon: "snowflake" });
-        cm.addItem({ description: "Pin to Presentation", event: () => PresentationView.Instance.PinDoc(this.props.Document), icon: "map-pin" });
+        cm.addItem({ description: "Pin to Presentation", event: () => PresentationView.Instance.PinDoc(this.props.Document), icon: "map-pin" }); //change this to
         cm.addItem({ description: BoolCast(this.props.Document.lockedPosition) ? "Unlock Position" : "Lock Position", event: this.toggleLockPosition, icon: BoolCast(this.props.Document.lockedPosition) ? "unlock" : "lock" });
         let makes: ContextMenuProps[] = [];
         makes.push({ description: "Make Background", event: this.makeBackground, icon: BoolCast(this.props.Document.lockedPosition) ? "unlock" : "lock" });
-- 
cgit v1.2.3-70-g09d2


From 0af800d2edf120ab2a20842ed67ea7d7616996b9 Mon Sep 17 00:00:00 2001
From: HJF Bulterman <henri_bulterman@brown.edu>
Date: Fri, 9 Aug 2019 15:37:49 -0400
Subject: working for about 85%

---
 src/client/views/MainView.tsx                      | 27 +++++++++++++++++-----
 .../views/collections/CollectionDockingView.tsx    |  9 ++++++++
 src/client/views/nodes/DocumentView.tsx            |  3 ++-
 src/client/views/nodes/PresBox.tsx                 |  7 +++---
 4 files changed, 36 insertions(+), 10 deletions(-)

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

diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 14cfc6792..cb81f9aad 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -247,7 +247,6 @@ export class MainView extends React.Component {
     @computed get dockingContent() {
         let flyoutWidth = this.flyoutWidth;
         let mainCont = this.mainContainer;
-        let castRes = mainCont ? FieldValue(Cast(mainCont.presentationView, listSpec(Doc))) : undefined;
         return <Measure offset onResize={this.onResize}>
             {({ measureRef }) =>
                 <div ref={measureRef} id="mainContent-div" style={{ width: `calc(100% - ${flyoutWidth}px`, transform: `translate(${flyoutWidth}px, 0px)` }} onDrop={this.onDrop}>
@@ -271,7 +270,7 @@ export class MainView extends React.Component {
                             zoomToScale={emptyFunction}
                             getScale={returnOne}
                         />}
-                    {castRes ? <PresentationView Documents={castRes} key="presentation" /> : null}
+                    {/* {presentationDoc ? <PresentationView Documents={Cast(presentationDoc.data, listSpec(Doc))!} key="presentation" /> : null} */}
                 </div>
             }
         </Measure>;
@@ -385,13 +384,11 @@ export class MainView extends React.Component {
         let addButtonDocument = action(() => Docs.Create.ButtonDocument({ width: 150, height: 50, title: "Button" }));
         let addImportCollectionNode = action(() => Docs.Create.DirectoryImportDocument({ title: "Directory Import", width: 400, height: 400 }));
 
-        let addPresentationNode = action(() => Docs.Create.PresDocument(new List<Doc>([Docs.Create.TreeDocument([], { title: "Presentation" })])));
-
         let btns: [React.RefObject<HTMLDivElement>, IconName, string, () => Doc][] = [
             [React.createRef<HTMLDivElement>(), "object-group", "Add Collection", addColNode],
             [React.createRef<HTMLDivElement>(), "bolt", "Add Button", addButtonDocument],
             // [React.createRef<HTMLDivElement>(), "clone", "Add Docking Frame", addDockingNode],
-            [React.createRef<HTMLDivElement>(), "cloud-upload-alt", "Import Directory", addPresentationNode], //remove at some point in favor of addImportCollectionNode
+            [React.createRef<HTMLDivElement>(), "cloud-upload-alt", "Import Directory", addImportCollectionNode], //remove at some point in favor of addImportCollectionNode
         ];
         if (!ClientUtils.RELEASE) btns.unshift([React.createRef<HTMLDivElement>(), "cat", "Add Cat Image", addImageNode]);
 
@@ -402,7 +399,7 @@ export class MainView extends React.Component {
             <div id="add-options-content">
                 <ul id="add-options-list">
                     <li key="search"><button className="add-button round-button" title="Search" onClick={this.toggleSearch}><FontAwesomeIcon icon="search" size="sm" /></button></li>
-                    <li key="presentation"><button className="add-button round-button" title="Open Presentation View" onClick={() => PresentationView.Instance.toggle(undefined)}><FontAwesomeIcon icon="table" size="sm" /></button></li>
+                    <li key="presentation"><button className="add-button round-button" title="Open Presentation View" onClick={this.togglePresentationView}><FontAwesomeIcon icon="table" size="sm" /></button></li>
                     <li key="undo"><button className="add-button round-button" title="Undo" style={{ opacity: UndoManager.CanUndo() ? 1 : 0.5, transition: "0.4s ease all" }} onClick={() => UndoManager.Undo()}><FontAwesomeIcon icon="undo-alt" size="sm" /></button></li>
                     <li key="redo"><button className="add-button round-button" title="Redo" style={{ opacity: UndoManager.CanRedo() ? 1 : 0.5, transition: "0.4s ease all" }} onClick={() => UndoManager.Redo()}><FontAwesomeIcon icon="redo-alt" size="sm" /></button></li>
                     {btns.map(btn =>
@@ -453,6 +450,24 @@ export class MainView extends React.Component {
         this.isSearchVisible = !this.isSearchVisible;
     }
 
+    togglePresentationView = () => {
+        let presDoc = this.presentationDoc;
+        if (!presDoc) {
+            return;
+        }
+        let isOpen = CollectionDockingView.Instance.Has(presDoc);
+        if (isOpen) {
+            CollectionDockingView.Instance.CloseRightSplit(presDoc);
+        } else {
+            CollectionDockingView.Instance.AddRightSplit(presDoc, undefined);
+        }
+    }
+
+    private get presentationDoc() {
+        let mainCont = this.mainContainer;
+        return mainCont ? FieldValue(Cast(mainCont.presentationView, Doc)) : undefined;
+    }
+
     render() {
         return (
             <div id="main-div">
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 1859ebee7..bd83a46a3 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -162,6 +162,14 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
         this.stateChanged();
     }
 
+    public Has = (document: Doc) => {
+        let docs = Cast(this.props.Document.data, listSpec(Doc));
+        if (!docs) {
+            return false;
+        }
+        return docs.includes(document);
+    }
+
     //
     //  Creates a vertical split on the right side of the docking view, and then adds the Document to that split
     //
@@ -525,6 +533,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
         this.props.glContainer.layoutManager.on("activeContentItemChanged", this.onActiveContentItemChanged);
         this.props.glContainer.on("tab", this.onActiveContentItemChanged);
         this.onActiveContentItemChanged();
+        // setTimeout(() => MainView.Instance.openPresentationView(), 2000);
     }
 
     componentWillUnmount() {
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 0ca303dde..6d4c18050 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -41,6 +41,7 @@ import { ClientUtils } from '../../util/ClientUtils';
 import { EditableView } from '../EditableView';
 import { faHandPointer, faHandPointRight } from '@fortawesome/free-regular-svg-icons';
 import { DocumentDecorations } from '../DocumentDecorations';
+import { PresBox } from './PresBox';
 const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this?
 
 library.add(fa.faTrash);
@@ -553,7 +554,7 @@ 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" });
         cm.addItem({ description: BoolCast(this.props.Document.ignoreAspect, false) || !this.props.Document.nativeWidth || !this.props.Document.nativeHeight ? "Freeze" : "Unfreeze", event: this.freezeNativeDimensions, icon: "snowflake" });
-        cm.addItem({ description: "Pin to Presentation", event: () => PresentationView.Instance.PinDoc(this.props.Document), icon: "map-pin" }); //change this to
+        cm.addItem({ description: "Pin to Presentation", event: () => PresBox.Instance.PinDoc(this.props.Document), icon: "map-pin" }); //this should work, and it does! A miracle!
         cm.addItem({ description: BoolCast(this.props.Document.lockedPosition) ? "Unlock Position" : "Lock Position", event: this.toggleLockPosition, icon: BoolCast(this.props.Document.lockedPosition) ? "unlock" : "lock" });
         let makes: ContextMenuProps[] = [];
         makes.push({ description: "Make Background", event: this.makeBackground, icon: BoolCast(this.props.Document.lockedPosition) ? "unlock" : "lock" });
diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx
index 2feb32693..8316c4469 100644
--- a/src/client/views/nodes/PresBox.tsx
+++ b/src/client/views/nodes/PresBox.tsx
@@ -32,7 +32,7 @@ export interface PresViewProps {
     Documents: List<Doc>;
 }
 
-const expandedWidth = 400;
+const expandedWidth = 450;
 
 @observer
 export class PresBox extends React.Component<FieldViewProps> { //FieldViewProps?
@@ -825,10 +825,11 @@ export class PresBox extends React.Component<FieldViewProps> { //FieldViewProps?
 
     render() {
 
-        let width = NumCast(this.curPresentation.width);
+        let width = "100%"; //NumCast(this.curPresentation.width)
+        console.log("The width is: " + width);
 
         return (
-            <div className="presentationView-cont" onPointerEnter={action(() => !this.persistOpacity && (this.opacity = 1))} onPointerLeave={action(() => !this.persistOpacity && (this.opacity = 0.4))} style={{ width: width, overflow: "hidden", opacity: this.opacity, transition: "0.7s opacity ease" }}>
+            <div className="presentationView-cont" onPointerEnter={action(() => !this.persistOpacity && (this.opacity = 1))} onPointerLeave={action(() => !this.persistOpacity && (this.opacity = 0.4))} style={{ width: width, overflow: "hidden", opacity: this.opacity, transition: "0.7s opacity ease", pointerEvents: "all" }}>
                 <div className="presentationView-heading">
                     {this.renderSelectOrPresSelection()}
                     <button title="Close Presentation" className='presentation-icon' onClick={this.closePresentation}><FontAwesomeIcon icon={"times"} /></button> {/**this.closePresentation  CLICK does not work?! Also without the*/}
-- 
cgit v1.2.3-70-g09d2


From 07482c3bf435748140addfd4fd338fc668657798 Mon Sep 17 00:00:00 2001
From: HJF Bulterman <henri_bulterman@brown.edu>
Date: Fri, 16 Aug 2019 14:50:11 -0400
Subject: initial commit, mostly functional pending circular import issue

---
 src/client/views/MainView.tsx                         |   8 ++++++--
 src/client/views/nodes/DocumentView.tsx               |   2 +-
 src/client/views/nodes/PresBox.tsx                    |   1 -
 src/new_fields/ListSpec.ts                            |   0
 .../buxton/source/.Bill_Notes_NewO.docx.icloud        | Bin 0 -> 172 bytes
 .../buxton/source/.Bill_Notes_OLPC.docx.icloud        | Bin 0 -> 172 bytes
 src/scraping/buxton/source/Bill_Notes_NewO.docx       | Bin 2150143 -> 0 bytes
 src/scraping/buxton/source/Bill_Notes_OLPC.docx       | Bin 6721592 -> 0 bytes
 8 files changed, 7 insertions(+), 4 deletions(-)
 create mode 100644 src/new_fields/ListSpec.ts
 create mode 100644 src/scraping/buxton/source/.Bill_Notes_NewO.docx.icloud
 create mode 100644 src/scraping/buxton/source/.Bill_Notes_OLPC.docx.icloud
 delete mode 100644 src/scraping/buxton/source/Bill_Notes_NewO.docx
 delete mode 100644 src/scraping/buxton/source/Bill_Notes_OLPC.docx

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

diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index cb81f9aad..de312318b 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -8,10 +8,10 @@ import * as React from 'react';
 import { SketchPicker } from 'react-color';
 import Measure from 'react-measure';
 import { Doc, DocListCast, Opt, HeightSym } from '../../new_fields/Doc';
+import { listSpec } from "../../new_fields/Schema";
 import { Id } from '../../new_fields/FieldSymbols';
 import { InkTool } from '../../new_fields/InkField';
 import { List } from '../../new_fields/List';
-import { listSpec } from '../../new_fields/Schema';
 import { Cast, FieldValue, NumCast, BoolCast, StrCast } from '../../new_fields/Types';
 import { CurrentUserUtils } from '../../server/authentication/models/current_user_utils';
 import { RouteStore } from '../../server/RouteStore';
@@ -93,6 +93,7 @@ export class MainView extends React.Component {
 
     componentWillUnMount() {
         window.removeEventListener("keydown", KeyManager.Instance.handle);
+        //close presentation 
     }
 
     constructor(props: Readonly<{}>) {
@@ -457,7 +458,10 @@ export class MainView extends React.Component {
         }
         let isOpen = CollectionDockingView.Instance.Has(presDoc);
         if (isOpen) {
-            CollectionDockingView.Instance.CloseRightSplit(presDoc);
+            return;
+            // CollectionDockingView.Instance.CloseRightSplit(presDoc);
+            //why?? It's throwing an error that seems impossible to fix.
+            //ToDo: 
         } else {
             CollectionDockingView.Instance.AddRightSplit(presDoc, undefined);
         }
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 6d4c18050..228efdc87 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -554,7 +554,7 @@ 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" });
         cm.addItem({ description: BoolCast(this.props.Document.ignoreAspect, false) || !this.props.Document.nativeWidth || !this.props.Document.nativeHeight ? "Freeze" : "Unfreeze", event: this.freezeNativeDimensions, icon: "snowflake" });
-        cm.addItem({ description: "Pin to Presentation", event: () => PresBox.Instance.PinDoc(this.props.Document), icon: "map-pin" }); //this should work, and it does! A miracle!
+        cm.addItem({ description: "Pin to Presentation", event: () => PresBox.Instance.PinDoc(this.props.Document), icon: "map-pin" }); //I think this should work... and it does! A miracle!
         cm.addItem({ description: BoolCast(this.props.Document.lockedPosition) ? "Unlock Position" : "Lock Position", event: this.toggleLockPosition, icon: BoolCast(this.props.Document.lockedPosition) ? "unlock" : "lock" });
         let makes: ContextMenuProps[] = [];
         makes.push({ description: "Make Background", event: this.makeBackground, icon: BoolCast(this.props.Document.lockedPosition) ? "unlock" : "lock" });
diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx
index 8316c4469..1bd2ad3c9 100644
--- a/src/client/views/nodes/PresBox.tsx
+++ b/src/client/views/nodes/PresBox.tsx
@@ -826,7 +826,6 @@ export class PresBox extends React.Component<FieldViewProps> { //FieldViewProps?
     render() {
 
         let width = "100%"; //NumCast(this.curPresentation.width)
-        console.log("The width is: " + width);
 
         return (
             <div className="presentationView-cont" onPointerEnter={action(() => !this.persistOpacity && (this.opacity = 1))} onPointerLeave={action(() => !this.persistOpacity && (this.opacity = 0.4))} style={{ width: width, overflow: "hidden", opacity: this.opacity, transition: "0.7s opacity ease", pointerEvents: "all" }}>
diff --git a/src/new_fields/ListSpec.ts b/src/new_fields/ListSpec.ts
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/scraping/buxton/source/.Bill_Notes_NewO.docx.icloud b/src/scraping/buxton/source/.Bill_Notes_NewO.docx.icloud
new file mode 100644
index 000000000..f71886d8c
Binary files /dev/null and b/src/scraping/buxton/source/.Bill_Notes_NewO.docx.icloud differ
diff --git a/src/scraping/buxton/source/.Bill_Notes_OLPC.docx.icloud b/src/scraping/buxton/source/.Bill_Notes_OLPC.docx.icloud
new file mode 100644
index 000000000..30ddb3091
Binary files /dev/null and b/src/scraping/buxton/source/.Bill_Notes_OLPC.docx.icloud differ
diff --git a/src/scraping/buxton/source/Bill_Notes_NewO.docx b/src/scraping/buxton/source/Bill_Notes_NewO.docx
deleted file mode 100644
index 2f4a04e81..000000000
Binary files a/src/scraping/buxton/source/Bill_Notes_NewO.docx and /dev/null differ
diff --git a/src/scraping/buxton/source/Bill_Notes_OLPC.docx b/src/scraping/buxton/source/Bill_Notes_OLPC.docx
deleted file mode 100644
index 7a636e2d6..000000000
Binary files a/src/scraping/buxton/source/Bill_Notes_OLPC.docx and /dev/null differ
-- 
cgit v1.2.3-70-g09d2


From 0e4729a8d634c67a3575761784b840a28694ba7a Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Mon, 19 Aug 2019 15:55:36 -0400
Subject: fixed treeview dragging.  got rid of extra stuff from
 presentationview

---
 src/client/documents/Documents.ts                  |   2 +-
 src/client/views/MainView.tsx                      |  62 +-
 .../views/collections/CollectionDockingView.tsx    |  27 +-
 .../views/collections/CollectionSchemaCells.tsx    |   2 +
 .../views/collections/CollectionSchemaView.tsx     |   4 +
 src/client/views/collections/CollectionSubView.tsx |   2 +-
 .../views/collections/CollectionTreeView.tsx       |  18 +-
 .../CollectionFreeFormRemoteCursors.tsx            |   2 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |   2 +
 src/client/views/nodes/DocumentView.tsx            |   6 +-
 src/client/views/nodes/FieldView.tsx               |   1 +
 src/client/views/nodes/KeyValuePair.tsx            |   1 +
 src/client/views/nodes/LinkMenu.tsx                |   3 -
 src/client/views/nodes/PresBox.tsx                 | 249 +-----
 src/client/views/pdf/Annotation.tsx                |   6 +-
 src/client/views/pdf/PDFViewer.tsx                 |   1 +
 .../views/presentationview/PresentationElement.tsx |   3 +-
 .../views/presentationview/PresentationList.tsx    |   5 +-
 .../views/presentationview/PresentationView.scss   |   4 +-
 .../views/presentationview/PresentationView.tsx    | 994 ---------------------
 src/client/views/search/SearchItem.tsx             |   1 +
 src/new_fields/Doc.ts                              |  55 +-
 src/new_fields/util.ts                             |   3 +-
 .../authentication/models/current_user_utils.ts    |  23 +-
 24 files changed, 172 insertions(+), 1304 deletions(-)
 delete mode 100644 src/client/views/presentationview/PresentationView.tsx

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index c551fd2a3..47df17329 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -290,7 +290,7 @@ export namespace Docs {
             const { omit: protoProps, extract: delegateProps } = OmitKeys(options, delegateKeys);
 
             if (!("author" in protoProps)) {
-                protoProps.author = CurrentUserUtils.email;
+                protoProps.author = Doc.CurrentUserEmail;
             }
 
             if (!("creationDate" in protoProps)) {
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 7119a4fc3..7b7a5542d 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -12,7 +12,6 @@ import { List } from '../../new_fields/List';
 import { Id } from '../../new_fields/FieldSymbols';
 import { InkTool } from '../../new_fields/InkField';
 import { listSpec } from '../../new_fields/Schema';
-import { SchemaHeaderField } from '../../new_fields/SchemaHeaderField';
 import { BoolCast, Cast, FieldValue, StrCast } from '../../new_fields/Types';
 import { CurrentUserUtils } from '../../server/authentication/models/current_user_utils';
 import { RouteStore } from '../../server/RouteStore';
@@ -24,7 +23,7 @@ import { DictationManager } from '../util/DictationManager';
 import { SetupDrag } from '../util/DragManager';
 import { HistoryUtil } from '../util/History';
 import { Transform } from '../util/Transform';
-import { UndoManager } from '../util/UndoManager';
+import { UndoManager, undoBatch } from '../util/UndoManager';
 import { CollectionBaseView } from './collections/CollectionBaseView';
 import { CollectionDockingView } from './collections/CollectionDockingView';
 import { CollectionTreeView } from './collections/CollectionTreeView';
@@ -37,15 +36,16 @@ import { MainOverlayTextBox } from './MainOverlayTextBox';
 import { DocumentView } from './nodes/DocumentView';
 import { OverlayView } from './OverlayView';
 import PDFMenu from './pdf/PDFMenu';
-import { PresentationView } from './presentationview/PresentationView';
 import { PreviewCursor } from './PreviewCursor';
 import { FilterBox } from './search/FilterBox';
+import { DocumentManager } from '../util/DocumentManager';
+import PresModeMenu from './presentationview/PresentationModeMenu';
+import { PresBox } from './nodes/PresBox';
 
 @observer
 export class MainView extends React.Component {
     public static Instance: MainView;
     @observable addMenuToggle = React.createRef<HTMLInputElement>();
-    @observable private _workspacesShown: boolean = false;
     @observable public pwidth: number = 0;
     @observable public pheight: number = 0;
 
@@ -82,10 +82,6 @@ export class MainView extends React.Component {
     public isPointerDown = false;
     private set mainContainer(doc: Opt<Doc>) {
         if (doc) {
-            if (!("presentationView" in doc)) {
-                let initialDoc = Docs.Create.TreeDocument([], { title: "Presentation" });
-                doc.presentationView = Docs.Create.PresDocument(new List<Doc>([initialDoc]));
-            }
             CurrentUserUtils.UserDocument.activeWorkspace = doc;
         }
     }
@@ -322,6 +318,7 @@ export class MainView extends React.Component {
                             DataDoc={undefined}
                             addDocument={undefined}
                             addDocTab={emptyFunction}
+                            pinToPres={emptyFunction}
                             onClick={undefined}
                             removeDocument={undefined}
                             ScreenToLocalTransform={Transform.Identity}
@@ -339,7 +336,6 @@ export class MainView extends React.Component {
                             zoomToScale={emptyFunction}
                             getScale={returnOne}
                         />}
-                    {/* {presentationDoc ? <PresentationView Documents={Cast(presentationDoc.data, listSpec(Doc))!} key="presentation" /> : null} */}
                 </div>
             }
         </Measure>;
@@ -386,6 +382,7 @@ export class MainView extends React.Component {
             DataDoc={undefined}
             addDocument={undefined}
             addDocTab={this.addDocTabFunc}
+            pinToPres={emptyFunction}
             removeDocument={undefined}
             onClick={undefined}
             ScreenToLocalTransform={Transform.Identity}
@@ -443,26 +440,24 @@ export class MainView extends React.Component {
 
         let imgurl = "https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg";
 
-        // let addDockingNode = action(() => Docs.Create.StandardCollectionDockingDocument([{ doc: addColNode(), initialWidth: 200 }], { width: 200, height: 200, title: "a nested docking freeform collection" }));
-        let addSchemaNode = action(() => Docs.Create.SchemaDocument([new SchemaHeaderField("title", "#f1efeb")], [], { width: 200, height: 200, title: "a schema collection" }));
-        //let addTreeNode = action(() => Docs.TreeDocument([CurrentUserUtils.UserDocument], { width: 250, height: 400, title: "Library:" + CurrentUserUtils.email, dropAction: "alias" }));
-        // let addTreeNode = action(() => Docs.TreeDocument(this._northstarSchemas, { width: 250, height: 400, title: "northstar schemas", dropAction: "copy"  }));
         let addColNode = action(() => Docs.Create.FreeformDocument([], { width: this.pwidth * .7, height: this.pheight, title: "a freeform collection" }));
+        let addPresNode = action(() => Doc.UserDoc().curPresentation = Docs.Create.PresDocument(new List<Doc>(), { width: 200, height: 500, title: "a presentation trail" }));
         let addWebNode = action(() => Docs.Create.WebDocument("https://en.wikipedia.org/wiki/Hedgehog", { width: 300, height: 300, title: "New Webpage" }));
         let addDragboxNode = action(() => Docs.Create.DragboxDocument({ width: 40, height: 40, title: "drag collection" }));
         let addImageNode = action(() => Docs.Create.ImageDocument(imgurl, { width: 200, title: "an image of a cat" }));
         let addButtonDocument = action(() => Docs.Create.ButtonDocument({ width: 150, height: 50, title: "Button" }));
         let addImportCollectionNode = action(() => Docs.Create.DirectoryImportDocument({ title: "Directory Import", width: 400, height: 400 }));
-        let youtubeurl = "https://www.youtube.com/embed/TqcApsGRzWw";
-        let addYoutubeSearcher = action(() => Docs.Create.YoutubeDocument(youtubeurl, { width: 600, height: 600, title: "youtube search" }));
+        // let youtubeurl = "https://www.youtube.com/embed/TqcApsGRzWw";
+        // let addYoutubeSearcher = action(() => Docs.Create.YoutubeDocument(youtubeurl, { width: 600, height: 600, title: "youtube search" }));
 
         let btns: [React.RefObject<HTMLDivElement>, IconName, string, () => Doc][] = [
             [React.createRef<HTMLDivElement>(), "object-group", "Add Collection", addColNode],
+            [React.createRef<HTMLDivElement>(), "table", "Add Presentation Trail", addPresNode],
             [React.createRef<HTMLDivElement>(), "globe-asia", "Add Website", addWebNode],
             [React.createRef<HTMLDivElement>(), "bolt", "Add Button", addButtonDocument],
             // [React.createRef<HTMLDivElement>(), "clone", "Add Docking Frame", addDockingNode],
             [React.createRef<HTMLDivElement>(), "cloud-upload-alt", "Import Directory", addImportCollectionNode], //remove at some point in favor of addImportCollectionNode
-            [React.createRef<HTMLDivElement>(), "play", "Add Youtube Searcher", addYoutubeSearcher],
+            //[React.createRef<HTMLDivElement>(), "play", "Add Youtube Searcher", addYoutubeSearcher],
             [React.createRef<HTMLDivElement>(), "file", "Add Document Dragger", addDragboxNode]
         ];
         if (!ClientUtils.RELEASE) btns.unshift([React.createRef<HTMLDivElement>(), "cat", "Add Cat Image", addImageNode]);
@@ -489,7 +484,7 @@ export class MainView extends React.Component {
             <div id="add-options-content">
                 <ul id="add-options-list">
                     <li key="search"><button className="add-button round-button" title="Search" onClick={this.toggleSearch}><FontAwesomeIcon icon="search" size="sm" /></button></li>
-                    <li key="presentation"><button className="add-button round-button" title="Open Presentation View" onClick={this.togglePresentationView}><FontAwesomeIcon icon="table" size="sm" /></button></li>
+                    <li key="presentation"><button className="add-button round-button" title="Show Current Presentation Trail" onClick={this.togglePresentationView}><FontAwesomeIcon icon="table" size="sm" /></button></li>
                     <li key="undo"><button className="add-button round-button" title="Undo" style={{ opacity: UndoManager.CanUndo() ? 1 : 0.5, transition: "0.4s ease all" }} onClick={() => UndoManager.Undo()}><FontAwesomeIcon icon="undo-alt" size="sm" /></button></li>
                     <li key="redo"><button className="add-button round-button" title="Redo" style={{ opacity: UndoManager.CanRedo() ? 1 : 0.5, transition: "0.4s ease all" }} onClick={() => UndoManager.Redo()}><FontAwesomeIcon icon="redo-alt" size="sm" /></button></li>
                     {btns.map(btn =>
@@ -543,30 +538,20 @@ export class MainView extends React.Component {
     @observable isSearchVisible = false;
     @action.bound
     toggleSearch = () => {
-        // console.log("search toggling")
         this.isSearchVisible = !this.isSearchVisible;
     }
 
     togglePresentationView = () => {
-        let presDoc = this.presentationDoc;
-        if (!presDoc) {
-            return;
-        }
-        let isOpen = CollectionDockingView.Instance.Has(presDoc);
-        if (isOpen) {
-            return;
-            // CollectionDockingView.Instance.CloseRightSplit(presDoc);
-            //why?? It's throwing an error that seems impossible to fix.
-            //ToDo: 
-        } else {
-            CollectionDockingView.Instance.AddRightSplit(presDoc, undefined);
+        if (CurrentUserUtils.UserDocument.curPresentation) {
+            let isOpen = DocumentManager.Instance.getDocumentView(CurrentUserUtils.UserDocument.curPresentation as Doc);
+            if (isOpen) {
+                CollectionDockingView.Instance.CloseRightSplit(CurrentUserUtils.UserDocument.curPresentation as Doc);
+            } else {
+                CollectionDockingView.Instance.AddRightSplit(CurrentUserUtils.UserDocument.curPresentation as Doc, undefined);
+            }
         }
     }
 
-    private get presentationDoc() {
-        let mainCont = this.mainContainer;
-        return mainCont ? FieldValue(Cast(mainCont.presentationView, Doc)) : undefined;
-    }
     private get dictationOverlay() {
         let display = this.dictationOverlayVisible;
         let success = this.dictationSuccess;
@@ -592,12 +577,21 @@ export class MainView extends React.Component {
         );
     }
 
+    @computed get miniPresentation() {
+        let next = () => PresBox.CurrentPresentation.next();
+        let back = () => PresBox.CurrentPresentation.back();
+        let startOrResetPres = () => PresBox.CurrentPresentation.startOrResetPres();
+        let closePresMode = action(() => {PresBox.CurrentPresentation.presMode = false; this.addDocTabFunc(PresBox.CurrentPresentation.props.Document)});
+        return !PresBox.CurrentPresentation || !PresBox.CurrentPresentation.presMode ? (null) : <PresModeMenu next={next} back={back} presStatus={PresBox.CurrentPresentation.presStatus} startOrResetPres={startOrResetPres} closePresMode={closePresMode} > </PresModeMenu>
+    }
+
     render() {
         return (
             <div id="main-div">
                 {this.dictationOverlay}
                 <DocumentDecorations />
                 {this.mainContent}
+                {this.miniPresentation}
                 <PreviewCursor />
                 <ContextMenu />
                 {this.nodesMenu()}
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 3fcc61b76..47dfeb169 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -29,6 +29,8 @@ import { faFile, faUnlockAlt } from '@fortawesome/free-solid-svg-icons';
 import { CurrentUserUtils } from '../../../server/authentication/models/current_user_utils';
 import { Docs } from '../../documents/Documents';
 import { DateField } from '../../../new_fields/DateField';
+import { List } from '../../../new_fields/List';
+import { DocumentType } from '../../documents/DocumentTypes';
 library.add(faFile);
 
 @observer
@@ -543,11 +545,31 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
         }));
     }
 
+    /**
+     * Adds a document to the presentation view
+     **/
+    @undoBatch
+    @action
+    public PinDoc(doc: Doc) {
+        if (doc.type === DocumentType.PRES) {
+            MainView.Instance.toggleMiniPresentation()
+        }
+        //add this new doc to props.Document
+        let curPres = Cast(CurrentUserUtils.UserDocument.curPresentation, Doc) as Doc;
+        if (curPres) {
+            const data = Cast(curPres.data, listSpec(Doc));
+            if (data) {
+                data.push(doc);
+            } else {
+                curPres.data = new List([doc]);
+            }
+        }
+    }
+
     componentDidMount() {
         this.props.glContainer.layoutManager.on("activeContentItemChanged", this.onActiveContentItemChanged);
         this.props.glContainer.on("tab", this.onActiveContentItemChanged);
         this.onActiveContentItemChanged();
-        // setTimeout(() => MainView.Instance.openPresentationView(), 2000);
     }
 
     componentWillUnmount() {
@@ -592,6 +614,8 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
             MainView.Instance.openWorkspace(doc);
         } else if (location === "onRight") {
             CollectionDockingView.Instance.AddRightSplit(doc, dataDoc);
+        } else if (location === "close") {
+            CollectionDockingView.Instance.CloseRightSplit(doc);
         } else {
             CollectionDockingView.Instance.AddTab(this._stack, doc, dataDoc);
         }
@@ -618,6 +642,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
             focus={emptyFunction}
             backgroundColor={returnEmptyString}
             addDocTab={this.addDocTab}
+            pinToPres={this.PinDoc}
             ContainingCollectionView={undefined}
             zoomToScale={emptyFunction}
             getScale={returnOne} />;
diff --git a/src/client/views/collections/CollectionSchemaCells.tsx b/src/client/views/collections/CollectionSchemaCells.tsx
index 7e3061354..551b485e7 100644
--- a/src/client/views/collections/CollectionSchemaCells.tsx
+++ b/src/client/views/collections/CollectionSchemaCells.tsx
@@ -40,6 +40,7 @@ export interface CellProps {
     fieldKey: string;
     renderDepth: number;
     addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => void;
+    pinToPres: (document: Doc) => void;
     moveDocument: (document: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => boolean;
     isFocused: boolean;
     changeFocusedCellByIndex: (row: number, col: number) => void;
@@ -160,6 +161,7 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
             PanelHeight: returnZero,
             PanelWidth: returnZero,
             addDocTab: this.props.addDocTab,
+            pinToPres: this.props.pinToPres,
             ContentScaling: returnOne
         };
 
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 4537dcc85..221908dd2 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -251,6 +251,7 @@ export interface SchemaTableProps {
     active: () => boolean;
     onDrop: (e: React.DragEvent<Element>, options: DocumentOptions, completed?: (() => void) | undefined) => void;
     addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => void;
+    pinToPres: (document: Doc) => void;
     isSelected: () => boolean;
     isFocused: (document: Doc) => boolean;
     setFocused: (document: Doc) => void;
@@ -377,6 +378,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
                         fieldKey: this.props.fieldKey,
                         renderDepth: this.props.renderDepth,
                         addDocTab: this.props.addDocTab,
+                        pinToPres: this.props.pinToPres,
                         moveDocument: this.props.moveDocument,
                         setIsEditing: this.setCellIsEditing,
                         isEditable: isEditable,
@@ -907,6 +909,7 @@ interface CollectionSchemaPreviewProps {
     active: () => boolean;
     whenActiveChanged: (isActive: boolean) => void;
     addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => void;
+    pinToPres: (document: Doc) => void;
     setPreviewScript: (script: string) => void;
     previewScript?: string;
 }
@@ -997,6 +1000,7 @@ export class CollectionSchemaPreview extends React.Component<CollectionSchemaPre
                         whenActiveChanged={this.props.whenActiveChanged}
                         ContainingCollectionView={this.props.CollectionView}
                         addDocTab={this.props.addDocTab}
+                        pinToPres={this.props.pinToPres}
                         parentActive={this.props.active}
                         ScreenToLocalTransform={this.getTransform}
                         renderDepth={this.props.renderDepth + 1}
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index d192721a7..cfe3fefb3 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -83,7 +83,7 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
             let ind;
             let doc = this.props.Document;
             let id = CurrentUserUtils.id;
-            let email = CurrentUserUtils.email;
+            let email = Doc.CurrentUserEmail;
             let pos = { x: position[0], y: position[1] };
             if (id && email) {
                 const proto = Doc.GetProto(doc);
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 7424cc186..6b9cd57b3 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -39,6 +39,7 @@ export interface TreeViewProps {
     moveDocument: DragManager.MoveFunction;
     dropAction: "alias" | "copy" | undefined;
     addDocTab: (doc: Doc, dataDoc: Doc | undefined, where: string) => void;
+    pinToPres: (document: Doc) => void;
     panelWidth: () => number;
     panelHeight: () => number;
     addDocument: (doc: Doc, relativeTo?: Doc, before?: boolean) => boolean;
@@ -259,10 +260,10 @@ class TreeView extends React.Component<TreeViewProps> {
 
             if (contents instanceof Doc || Cast(contents, listSpec(Doc))) {
                 let remDoc = (doc: Doc) => this.remove(doc, key);
-                let addDoc = (doc: Doc, addBefore?: Doc, before?: boolean) => Doc.AddDocToList(this.dataDoc, key, doc, addBefore, before);
+                let addDoc = (doc: Doc, addBefore?: Doc, before?: boolean) => Doc.AddDocToList(this.dataDoc, key, doc, addBefore, before, !BoolCast(this.props.document.stackingHeadersSortDescending));
                 contentElement = TreeView.GetChildElements(contents instanceof Doc ? [contents] :
                     DocListCast(contents), this.props.treeViewId, doc, undefined, key, addDoc, remDoc, this.move,
-                    this.props.dropAction, this.props.addDocTab, this.props.ScreenToLocalTransform, this.props.outerXf, this.props.active, this.props.panelWidth, this.props.renderDepth);
+                    this.props.dropAction, this.props.addDocTab, this.props.pinToPres, this.props.ScreenToLocalTransform, this.props.outerXf, this.props.active, this.props.panelWidth, this.props.renderDepth);
             } else {
                 contentElement = <EditableView
                     key="editableView"
@@ -287,13 +288,13 @@ class TreeView extends React.Component<TreeViewProps> {
         const expandKey = this.treeViewExpandedView === this.fieldKey ? this.fieldKey : this.treeViewExpandedView === "links" ? "links" : undefined;
         if (expandKey !== undefined) {
             let remDoc = (doc: Doc) => this.remove(doc, expandKey);
-            let addDoc = (doc: Doc, addBefore?: Doc, before?: boolean) => Doc.AddDocToList(this.dataDoc, expandKey, doc, addBefore, before);
+            let addDoc = (doc: Doc, addBefore?: Doc, before?: boolean) => Doc.AddDocToList(this.dataDoc, expandKey, doc, addBefore, before, !BoolCast(this.props.document.stackingHeadersSortDescending));
             let docs = expandKey === "links" ? this.childLinks : this.childDocs;
             return <ul key={expandKey + "more"}>
                 {!docs ? (null) :
                     TreeView.GetChildElements(docs as Doc[], this.props.treeViewId, this.props.document.layout as Doc,
                         this.resolvedDataDoc, expandKey, addDoc, remDoc, this.move,
-                        this.props.dropAction, this.props.addDocTab, this.props.ScreenToLocalTransform,
+                        this.props.dropAction, this.props.addDocTab, this.props.pinToPres, this.props.ScreenToLocalTransform,
                         this.props.outerXf, this.props.active, this.props.panelWidth, this.props.renderDepth)}
             </ul >;
         } else if (this.treeViewExpandedView === "fields") {
@@ -319,6 +320,7 @@ class TreeView extends React.Component<TreeViewProps> {
                     active={this.props.active}
                     whenActiveChanged={emptyFunction as any}
                     addDocTab={this.props.addDocTab}
+                    pinToPres={this.props.pinToPres}
                     setPreviewScript={emptyFunction}>
                 </CollectionSchemaPreview>
             </div>;
@@ -395,6 +397,7 @@ class TreeView extends React.Component<TreeViewProps> {
         move: DragManager.MoveFunction,
         dropAction: dropActionType,
         addDocTab: (doc: Doc, dataDoc: Doc | undefined, where: string) => void,
+        pinToPres: (document: Doc) => void,
         screenToLocalXf: () => Transform,
         outerXf: () => { translateX: number, translateY: number },
         active: () => boolean,
@@ -471,6 +474,7 @@ class TreeView extends React.Component<TreeViewProps> {
                 moveDocument={move}
                 dropAction={dropAction}
                 addDocTab={addDocTab}
+                pinToPres={pinToPres}
                 ScreenToLocalTransform={screenToLocalXf}
                 outerXf={outerXf}
                 parentKey={key}
@@ -554,7 +558,7 @@ export class CollectionTreeView extends CollectionSubView(Document) {
     render() {
         Doc.UpdateDocumentExtensionForField(this.props.DataDoc ? this.props.DataDoc : this.props.Document, this.props.fieldKey);
         let dropAction = StrCast(this.props.Document.dropAction) as dropActionType;
-        let addDoc = (doc: Doc, relativeTo?: Doc, before?: boolean) => Doc.AddDocToList(this.props.Document, this.props.fieldKey, doc, relativeTo, before);
+        let addDoc = (doc: Doc, relativeTo?: Doc, before?: boolean) => Doc.AddDocToList(this.props.Document, this.props.fieldKey, doc, relativeTo, before, !BoolCast(this.props.Document.stackingHeadersSortDescending));
         let moveDoc = (d: Doc, target: Doc, addDoc: (doc: Doc) => boolean) => this.props.moveDocument(d, target, addDoc);
         return !this.childDocs ? (null) : (
             <div id="body" className="collectionTreeView-dropTarget"
@@ -574,14 +578,14 @@ export class CollectionTreeView extends CollectionSubView(Document) {
                         let doc = this.props.Document.detailedLayout instanceof Doc ? Doc.ApplyTemplate(Doc.GetProto(this.props.Document.detailedLayout)) : undefined;
                         if (!doc) doc = Docs.Create.FreeformDocument([], { title: "", x: 0, y: 0, width: 100, height: 25, templates: new List<string>([Templates.Title.Layout]) });
                         TreeView.loadId = doc[Id];
-                        Doc.AddDocToList(this.props.Document, this.props.fieldKey, doc, this.childDocs.length ? this.childDocs[0] : undefined, true);
+                        Doc.AddDocToList(this.props.Document, this.props.fieldKey, doc, this.childDocs.length ? this.childDocs[0] : undefined, true, !BoolCast(this.props.Document.stackingHeadersSortDescending));
                     }} />
                 {this.props.Document.workspaceLibrary ? this.renderNotifsButton : (null)}
                 {this.props.Document.allowClear ? this.renderClearButton : (null)}
                 <ul className="no-indent" style={{ width: "max-content" }} >
                     {
                         TreeView.GetChildElements(this.childDocs, this.props.Document[Id], this.props.Document, this.props.DataDoc, this.props.fieldKey, addDoc, this.remove,
-                            moveDoc, dropAction, this.props.addDocTab, this.props.ScreenToLocalTransform, this.outerXf, this.props.active, this.props.PanelWidth, this.props.renderDepth)
+                            moveDoc, dropAction, this.props.addDocTab, this.props.pinToPres, this.props.ScreenToLocalTransform, this.outerXf, this.props.active, this.props.PanelWidth, this.props.renderDepth)
                     }
                 </ul>
             </div >
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
index 3193f5624..b8148852d 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
@@ -55,7 +55,7 @@ export class CollectionFreeFormRemoteCursors extends React.Component<CollectionV
                 ctx.stroke();
 
                 // ctx.font = "10px Arial";
-                // ctx.fillText(CurrentUserUtils.email[0].toUpperCase(), 10, 10);
+                // ctx.fillText(Doc.CurrentUserEmail[0].toUpperCase(), 10, 10);
             }
         }
     }
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index dbe5272dc..d69ece421 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -644,6 +644,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
             whenActiveChanged: this.props.whenActiveChanged,
             bringToFront: this.bringToFront,
             addDocTab: this.props.addDocTab,
+            pinToPres: this.props.pinToPres,
             zoomToScale: this.zoomToScale,
             getScale: this.getScale
         };
@@ -669,6 +670,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
             whenActiveChanged: this.props.whenActiveChanged,
             bringToFront: this.bringToFront,
             addDocTab: this.props.addDocTab,
+            pinToPres: this.props.pinToPres,
             zoomToScale: this.zoomToScale,
             getScale: this.getScale
         };
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 08d17e559..4378a51fb 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -33,7 +33,6 @@ import { DocComponent } from "../DocComponent";
 import { EditableView } from '../EditableView';
 import { MainView } from '../MainView';
 import { OverlayView } from '../OverlayView';
-import { PresentationView } from "../presentationview/PresentationView";
 import { ScriptBox } from '../ScriptBox';
 import { ScriptingRepl } from '../ScriptingRepl';
 import { Template } from "./../Templates";
@@ -98,6 +97,7 @@ export interface DocumentViewProps {
     whenActiveChanged: (isActive: boolean) => void;
     bringToFront: (doc: Doc, sendToBack?: boolean) => void;
     addDocTab: (doc: Doc, dataDoc: Doc | undefined, where: string) => void;
+    pinToPres: (document: Doc) => void;
     collapseToPoint?: (scrpt: number[], expandedDocs: Doc[] | undefined) => void;
     zoomToScale: (scale: number) => void;
     backgroundColor: (doc: Doc) => string | undefined;
@@ -614,7 +614,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         let analyzers: ContextMenuProps[] = existingAnalyze && "subitems" in existingAnalyze ? existingAnalyze.subitems : [];
         analyzers.push({ description: "Transcribe Speech", event: this.listen, icon: "microphone" });
         !existingAnalyze && cm.addItem({ description: "Analyzers...", subitems: analyzers, icon: "hand-point-right" });
-        cm.addItem({ description: "Pin to Presentation", event: () => PresBox.Instance.PinDoc(this.props.Document), icon: "map-pin" }); //I think this should work... and it does! A miracle!
+        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: "Download document", icon: "download", event: () => {
@@ -661,7 +661,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         try {
             let stuff = await rp.get(Utils.prepend(RouteStore.getUsers));
             const users: User[] = JSON.parse(stuff);
-            usersMenu = users.filter(({ email }) => email !== CurrentUserUtils.email).map(({ email, userDocumentId }) => ({
+            usersMenu = users.filter(({ email }) => email !== Doc.CurrentUserEmail).map(({ email, userDocumentId }) => ({
                 description: email, event: async () => {
                     const userDocument = await Cast(DocServer.GetRefField(userDocumentId), Doc);
                     if (!userDocument) {
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index cae975f30..f0f1b3b73 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -39,6 +39,7 @@ export interface FieldViewProps {
     selectOnLoad: boolean;
     addDocument?: (document: Doc, allowDuplicates?: boolean) => boolean;
     addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => void;
+    pinToPres: (document: Doc) => void;
     removeDocument?: (document: Doc) => boolean;
     moveDocument?: (document: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => boolean;
     ScreenToLocalTransform: () => Transform;
diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx
index 534a42efc..8001b24a7 100644
--- a/src/client/views/nodes/KeyValuePair.tsx
+++ b/src/client/views/nodes/KeyValuePair.tsx
@@ -68,6 +68,7 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
             PanelWidth: returnZero,
             PanelHeight: returnZero,
             addDocTab: returnZero,
+            pinToPres: returnZero,
             ContentScaling: returnOne
         };
         let contents = <FieldView {...props} />;
diff --git a/src/client/views/nodes/LinkMenu.tsx b/src/client/views/nodes/LinkMenu.tsx
index 1a4af04f8..1908889e9 100644
--- a/src/client/views/nodes/LinkMenu.tsx
+++ b/src/client/views/nodes/LinkMenu.tsx
@@ -12,9 +12,6 @@ import { library } from "@fortawesome/fontawesome-svg-core";
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 
 library.add(faTrash);
-import { Cast, FieldValue, StrCast } from "../../../new_fields/Types";
-import { Id } from "../../../new_fields/FieldSymbols";
-import { DocumentType } from "../../documents/Documents";
 
 interface Props {
     docView: DocumentView;
diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx
index 91c141540..cc042e008 100644
--- a/src/client/views/nodes/PresBox.tsx
+++ b/src/client/views/nodes/PresBox.tsx
@@ -1,22 +1,22 @@
 import React = require("react");
-import { FieldViewProps, FieldView } from './FieldView';
+import { library } from '@fortawesome/fontawesome-svg-core';
+import { faArrowLeft, faArrowRight, faEdit, faMinus, faPlay, faPlus, faStop, 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 { observable, action, runInAction, reaction, autorun, computed } from "mobx";
-import "../presentationview/PresentationView.scss";
-import { DocumentManager } from "../../util/DocumentManager";
-import { Utils } from "../../../Utils";
 import { Doc, DocListCast, DocListCastAsync } from "../../../new_fields/Doc";
-import { listSpec } from "../../../new_fields/Schema";
-import { Cast, NumCast, FieldValue, PromiseValue, StrCast, BoolCast } from "../../../new_fields/Types";
 import { Id } from "../../../new_fields/FieldSymbols";
 import { List } from "../../../new_fields/List";
+import { listSpec } from "../../../new_fields/Schema";
+import { BoolCast, Cast, FieldValue, NumCast, StrCast } from "../../../new_fields/Types";
+import { Utils } from "../../../Utils";
+import { DocumentManager } from "../../util/DocumentManager";
+import { undoBatch } from "../../util/UndoManager";
 import PresentationElement, { buttonIndex } from "../presentationview/PresentationElement";
-import { library } from '@fortawesome/fontawesome-svg-core';
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { faArrowRight, faArrowLeft, faPlay, faStop, faPlus, faTimes, faMinus, faEdit } from '@fortawesome/free-solid-svg-icons';
-import { Docs } from "../../documents/Documents";
-import { undoBatch, UndoManager } from "../../util/UndoManager";
 import PresentationViewList from "../presentationview/PresentationList";
+import "../presentationview/PresentationView.scss";
+import { FieldView, FieldViewProps } from './FieldView';
+import { ContextMenu } from "../ContextMenu";
 
 library.add(faArrowLeft);
 library.add(faArrowRight);
@@ -37,17 +37,14 @@ const expandedWidth = 450;
 @observer
 export class PresBox extends React.Component<FieldViewProps> { //FieldViewProps?
 
-    @computed
-    private get presentationDocs() {
-        let source = Doc.GetProto(this.props.Document);
-        return DocListCast(source.data);
-    }
 
     public static LayoutString(fieldKey?: string) { return FieldView.LayoutString(PresBox, fieldKey); }
 
-    //public static Instance: PresentationView;
     public static Instance: PresBox;
 
+    //Keeping track of the doc for the current presentation -- bcz: keeping a list of current presentations shouldn't be needed.  Let users create them, store them, as they see fit.
+    @computed get curPresentation() { return this.props.Document; }
+
     //Mapping from presentation ids to a list of doc that represent a group
     @observable groupMappings: Map<String, Doc[]> = new Map();
     //mapping from docs to their rendered component
@@ -59,9 +56,6 @@ export class PresBox extends React.Component<FieldViewProps> { //FieldViewProps?
     //back-up so that presentation stays the way it's when refreshed
     @observable presGroupBackUp: Doc = new Doc();
     @observable presButtonBackUp: Doc = new Doc();
-
-    //Keeping track of the doc for the current presentation
-    @observable curPresentation: Doc = new Doc();
     //Mapping of guids to presentations.
     @observable presentationsMapping: Map<String, Doc> = new Map();
     //Mapping of presentations to guid, so that select option values can be given.
@@ -77,12 +71,14 @@ export class PresBox extends React.Component<FieldViewProps> { //FieldViewProps?
     @observable opacity = 1;
     @observable persistOpacity = true;
     @observable labelOpacity = 0;
+    @observable presMode = false;
+
+    @observable public static CurrentPresentation: PresBox;
 
     //initilize class variables
-    constructor(props: FieldViewProps) { //FieldViewProps?
+    constructor(props: FieldViewProps) {
         super(props);
-        //PresentationView.Instance = this;
-        PresBox.Instance = this;
+        runInAction(() => PresBox.CurrentPresentation = this);
     }
 
     @action
@@ -94,32 +90,10 @@ export class PresBox extends React.Component<FieldViewProps> { //FieldViewProps?
         }
     }
 
-    //The first lifecycle function that gets called to set up the current presentation.
-    async componentWillMount() {
-        this.presentationDocs.forEach(async (doc, index: number) => {
-
-            //For each presentation received from mainContainer, a mapping is created.
-            let curDoc: Doc = await doc;
-            let newGuid = Utils.GenerateGuid();
-            this.presentationsKeyMapping.set(curDoc, newGuid);
-            this.presentationsMapping.set(newGuid, curDoc);
-
-            //The Presentation at first index gets set as default start presentation
-            if (index === 0) {
-                runInAction(() => this.currentSelectedPresValue = newGuid);
-                runInAction(() => this.curPresentation = curDoc);
-            }
-        });
-    }
-
-    //Second lifecycle function that gets called when component mounts. It makes sure to
+    //Second lifecycle function that gets called when component mounts. It makes sure toS
     //get the back-up information from previous session for the current presentation.
     async componentDidMount() {
-        let docAtZero = await this.presentationDocs[0];
-        runInAction(() => this.curPresentation = docAtZero);
-
         this.setPresentationBackUps();
-
     }
 
 
@@ -212,7 +186,6 @@ export class PresBox extends React.Component<FieldViewProps> { //FieldViewProps?
     //observable means render is re-called every time variable is changed
     @observable
     collapsed: boolean = false;
-    closePresentation = action(() => this.curPresentation.width = 0);
     next = async () => {
         const current = NumCast(this.curPresentation.selectedDoc);
         //asking to get document at current index
@@ -556,23 +529,6 @@ export class PresBox extends React.Component<FieldViewProps> { //FieldViewProps?
         runInAction(() => this.groupMappings = new Map());
     }
 
-    /**
-     * Adds a document to the presentation view
-     **/
-    @undoBatch
-    @action
-    public PinDoc(doc: Doc) {
-        //add this new doc to props.Document
-        const data = Cast(this.curPresentation.data, listSpec(Doc));
-        if (data) {
-            data.push(doc);
-        } else {
-            this.curPresentation.data = new List([doc]);
-        }
-
-        this.toggle(true);
-    }
-
     //Function that sets the store of the children docs.
     @action
     setChildrenDocs = (docList: Doc[]) => {
@@ -648,72 +604,15 @@ export class PresBox extends React.Component<FieldViewProps> { //FieldViewProps?
 
     }
 
-    /**
-     * The function that is called to add a new presentation to the presentationView.
-     * It sets up te mappings and local copies of it. Resets the groupings and presentation.
-     * Makes the new presentation current selected, and retrieve the back-Ups if present.
-     */
-    @action
-    addNewPresentation = (presTitle: string) => {
-        //creating a new presentation doc
-        let newPresentationDoc = Docs.Create.TreeDocument([], { title: presTitle });
-        let presDocs = Cast(Doc.GetProto(this.props.Document).data, listSpec(Doc));
-        presDocs && presDocs.push(newPresentationDoc);
-
-        //setting that new doc as current
-        this.curPresentation = newPresentationDoc;
-
-        //storing the doc in local copies for easier access
-        let newGuid = Utils.GenerateGuid();
-        this.presentationsMapping.set(newGuid, newPresentationDoc);
-        this.presentationsKeyMapping.set(newPresentationDoc, newGuid);
-
-        //resetting the previous presentation's actions so that new presentation can be loaded.
-        this.resetGroupIds();
-        this.resetPresentation();
-        this.presElementsMappings = new Map();
-        this.currentSelectedPresValue = newGuid;
-        this.setPresentationBackUps();
-
-    }
-
-    /**
-     * The function that is called to change the current selected presentation.
-     * Changes the presentation, also resetting groupings and presentation in process.
-     * Plus retrieving the backUps for the newly selected presentation.
-     */
-    @action
-    getSelectedPresentation = (e: React.ChangeEvent<HTMLSelectElement>) => {
-        //get the guid of the selected presentation
-        let selectedGuid = e.target.value;
-        //set that as current presentation
-        this.curPresentation = this.presentationsMapping.get(selectedGuid)!;
-
-        //reset current Presentations local things so that new one can be loaded
-        this.resetGroupIds();
-        this.resetPresentation();
-        this.presElementsMappings = new Map();
-        this.currentSelectedPresValue = selectedGuid;
-        this.setPresentationBackUps();
-
-
-    }
 
     /**
      * The function that is called to render either select for presentations, or title inputting.
      */
     renderSelectOrPresSelection = () => {
-        let presentationList = this.presentationDocs;
         if (this.PresTitleInputOpen || this.PresTitleChangeOpen) {
             return <input ref={(e) => this.titleInputElement = e!} type="text" className="presentationView-title" placeholder="Enter Name!" onKeyDown={this.submitPresentationTitle} />;
         } else {
-            return <select value={this.currentSelectedPresValue} id="pres_selector" className="presentationView-title" onChange={this.getSelectedPresentation}>
-                {presentationList.map((doc: Doc, index: number) => {
-                    let mappedGuid = this.presentationsKeyMapping.get(doc);
-                    let docGuid: string = mappedGuid ? mappedGuid.toString() : "";
-                    return <option key={docGuid} value={docGuid}>{StrCast(doc.title)}</option>;
-                })}
-            </select>;
+            return (null);
         }
     }
 
@@ -726,87 +625,12 @@ export class PresBox extends React.Component<FieldViewProps> { //FieldViewProps?
         if (e.keyCode === 13) {
             let presTitle = this.titleInputElement!.value;
             this.titleInputElement!.value = "";
-            if (this.PresTitleInputOpen) {
-                if (presTitle === "") {
-                    presTitle = "Presentation";
-                }
-                this.PresTitleInputOpen = false;
-                this.addNewPresentation(presTitle);
-            } else if (this.PresTitleChangeOpen) {
+            if (this.PresTitleChangeOpen) {
                 this.PresTitleChangeOpen = false;
                 this.changePresentationTitle(presTitle);
             }
         }
     }
-
-    /**
-     * The function that is called to remove a presentation from all its copies, and the main Container's
-     * list. Sets up the next presentation as current.
-     */
-    @action
-    removePresentation = async () => {
-        if (this.presentationsMapping.size !== 1) {
-            let presentationList = this.presentationDocs;
-            let batch = UndoManager.StartBatch("presRemoval");
-
-            //getting the presentation that will be removed
-            let removedDoc = this.presentationsMapping.get(this.currentSelectedPresValue!);
-            //that presentation is removed
-            presentationList!.splice(presentationList.indexOf(removedDoc!), 1);
-
-            //its mappings are removed from local copies
-            this.presentationsKeyMapping.delete(removedDoc!);
-            this.presentationsMapping.delete(this.currentSelectedPresValue!);
-
-            //the next presentation is set as current
-            let remainingPresentations = this.presentationsMapping.values();
-            let nextDoc = remainingPresentations.next().value;
-            this.curPresentation = nextDoc;
-
-
-            //Storing these for being able to undo changes
-            let curGuid = this.currentSelectedPresValue!;
-            let curPresStatus = this.presStatus;
-
-            //resetting the groups and presentation actions so that next presentation gets loaded
-            this.resetGroupIds();
-            this.resetPresentation();
-            this.currentSelectedPresValue = this.presentationsKeyMapping.get(nextDoc)!.toString();
-            this.setPresentationBackUps();
-
-            //Storing for undo
-            let currentGroups = this.groupMappings;
-            let curPresElemMapping = this.presElementsMappings;
-
-            //Event to undo actions that are not related to doc directly, aka. local things
-            UndoManager.AddEvent({
-                undo: action(() => {
-                    this.curPresentation = removedDoc!;
-                    this.presentationsMapping.set(curGuid, removedDoc!);
-                    this.presentationsKeyMapping.set(removedDoc!, curGuid);
-                    this.currentSelectedPresValue = curGuid;
-
-                    this.presStatus = curPresStatus;
-                    this.groupMappings = currentGroups;
-                    this.presElementsMappings = curPresElemMapping;
-                    this.setPresentationBackUps();
-
-                }),
-                redo: action(() => {
-                    this.curPresentation = nextDoc;
-                    this.presStatus = false;
-                    this.presentationsKeyMapping.delete(removedDoc!);
-                    this.presentationsMapping.delete(curGuid);
-                    this.currentSelectedPresValue = this.presentationsKeyMapping.get(nextDoc)!.toString();
-                    this.setPresentationBackUps();
-
-                }),
-            });
-
-            batch.end();
-        }
-    }
-
     /**
      * The function that is called to change title of presentation to what user entered.
      */
@@ -822,26 +646,23 @@ export class PresBox extends React.Component<FieldViewProps> { //FieldViewProps?
         this.presElementsMappings.set(keyDoc, elem);
     }
 
+    specificContextMenu = (e: React.MouseEvent): void => {
+        ContextMenu.Instance.addItem({ description: "Make Current Presentation", event: action(() => Doc.UserDoc().curPresentation = this.props.Document), icon: "asterisk" });
+        ContextMenu.Instance.addItem({
+            description: "Toggle Minimized Mode", event: action(() => {
+                this.presMode = !this.presMode;
+                if (this.presMode) this.props.addDocTab && this.props.addDocTab(this.props.Document, this.props.DataDoc, "close");
+            }), icon: "asterisk"
+        });
+    }
 
     render() {
 
         let width = "100%"; //NumCast(this.curPresentation.width)
-
         return (
-            <div className="presentationView-cont" onPointerEnter={action(() => !this.persistOpacity && (this.opacity = 1))} onPointerLeave={action(() => !this.persistOpacity && (this.opacity = 0.4))} style={{ width: width, overflow: "hidden", opacity: this.opacity, transition: "0.7s opacity ease", pointerEvents: "all" }}>
-                <div className="presentationView-heading">
-                    {this.renderSelectOrPresSelection()}
-                    <button title="Close Presentation" className='presentation-icon' onClick={this.closePresentation}><FontAwesomeIcon icon={"times"} /></button> {/**this.closePresentation  CLICK does not work?! Also without the*/}
-                    <button title="Add Presentation" className="presentation-icon" style={{ marginRight: 10 }} onClick={() => {
-                        runInAction(() => { if (this.PresTitleChangeOpen) { this.PresTitleChangeOpen = false; } });
-                        runInAction(() => this.PresTitleInputOpen ? this.PresTitleInputOpen = false : this.PresTitleInputOpen = true);
-                    }}><FontAwesomeIcon icon={"plus"} /></button>
-                    <button title="Remove Presentation" className='presentation-icon' style={{ marginRight: 10 }} onClick={this.removePresentation}><FontAwesomeIcon icon={"minus"} /></button>
-                    <button title="Change Presentation Title" className="presentation-icon" style={{ marginRight: 10 }} onClick={() => {
-                        runInAction(() => { if (this.PresTitleInputOpen) { this.PresTitleInputOpen = false; } });
-                        runInAction(() => this.PresTitleChangeOpen ? this.PresTitleChangeOpen = false : this.PresTitleChangeOpen = true);
-                    }}><FontAwesomeIcon icon={"edit"} /></button>
-                </div>
+            <div className="presentationView-cont" onPointerEnter={action(() => !this.persistOpacity && (this.opacity = 1))} onContextMenu={this.specificContextMenu}
+                onPointerLeave={action(() => !this.persistOpacity && (this.opacity = 0.4))}
+                style={{ width: width, opacity: this.opacity, }}>
                 <div className="presentation-buttons">
                     <button title="Back" className="presentation-button" onClick={this.back}><FontAwesomeIcon icon={"arrow-left"} /></button>
                     {this.renderPlayPauseButton()}
diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx
index 7ba7b6d14..6f77a0a5b 100644
--- a/src/client/views/pdf/Annotation.tsx
+++ b/src/client/views/pdf/Annotation.tsx
@@ -6,10 +6,10 @@ import { Id } from "../../../new_fields/FieldSymbols";
 import { List } from "../../../new_fields/List";
 import { Cast, FieldValue, NumCast, StrCast } from "../../../new_fields/Types";
 import { DocumentManager } from "../../util/DocumentManager";
-import { PresentationView } from "../presentationview/PresentationView";
 import PDFMenu from "./PDFMenu";
 import "./Annotation.scss";
 import { scale } from "./PDFViewer";
+import { PresBox } from "../nodes/PresBox";
 
 interface IAnnotationProps {
     anno: Doc;
@@ -18,6 +18,7 @@ interface IAnnotationProps {
     fieldExtensionDoc: Doc;
     scrollTo?: (n: number) => void;
     addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => void;
+    pinToPres: (document: Doc) => void;
 }
 
 export default class Annotation extends React.Component<IAnnotationProps> {
@@ -37,6 +38,7 @@ interface IRegionAnnotationProps {
     fieldExtensionDoc: Doc;
     scrollTo?: (n: number) => void;
     addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => void;
+    pinToPres: (document: Doc) => void;
     document: Doc;
 }
 
@@ -81,7 +83,7 @@ class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
 
     pinToPres = () => {
         let group = FieldValue(Cast(this.props.document.group, Doc));
-        group && PresentationView.Instance.PinDoc(group);
+        group && this.props.pinToPres(group);
     }
 
     @action
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 08674720d..258e218f0 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -36,6 +36,7 @@ interface IViewerProps {
     active: () => boolean;
     setPanY?: (n: number) => void;
     addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => void;
+    pinToPres: (document: Doc) => void;
     addDocument?: (doc: Doc, allowDuplicates?: boolean) => boolean;
 }
 
diff --git a/src/client/views/presentationview/PresentationElement.tsx b/src/client/views/presentationview/PresentationElement.tsx
index d98b66324..912970a50 100644
--- a/src/client/views/presentationview/PresentationElement.tsx
+++ b/src/client/views/presentationview/PresentationElement.tsx
@@ -15,7 +15,7 @@ import { SelectionManager } from "../../util/SelectionManager";
 import { ContextMenu } from "../ContextMenu";
 import { Transform } from "../../util/Transform";
 import { DocumentView } from "../nodes/DocumentView";
-import { DocumentType } from "../../documents/Documents";
+import { DocumentType } from "../../documents/DocumentTypes";
 import React = require("react");
 
 
@@ -839,6 +839,7 @@ export default class PresentationElement extends React.Component<PresentationEle
                     removeDocument={returnFalse}
                     ScreenToLocalTransform={Transform.Identity}
                     addDocTab={returnFalse}
+                    pinToPres={returnFalse}
                     renderDepth={1}
                     PanelWidth={() => 350}
                     PanelHeight={() => 90}
diff --git a/src/client/views/presentationview/PresentationList.tsx b/src/client/views/presentationview/PresentationList.tsx
index e853c4070..288ade042 100644
--- a/src/client/views/presentationview/PresentationList.tsx
+++ b/src/client/views/presentationview/PresentationList.tsx
@@ -6,10 +6,7 @@ import { Utils } from "../../../Utils";
 import { Doc, DocListCast, DocListCastAsync } from "../../../new_fields/Doc";
 import { NumCast, StrCast } from "../../../new_fields/Types";
 import { Id } from "../../../new_fields/FieldSymbols";
-import PresentationElement, { buttonIndex } from "./PresentationElement";
-import "../../../new_fields/Doc";
-
-
+import PresentationElement from "./PresentationElement";
 
 
 interface PresListProps {
diff --git a/src/client/views/presentationview/PresentationView.scss b/src/client/views/presentationview/PresentationView.scss
index 65b09c833..4f5858f20 100644
--- a/src/client/views/presentationview/PresentationView.scss
+++ b/src/client/views/presentationview/PresentationView.scss
@@ -7,7 +7,9 @@
     top: 0;
     bottom: 0;
     letter-spacing: 2px;
-    
+    overflow: hidden;
+    transition: 0.7s opacity ease;
+    pointer-events: all;
 }
 
 .presentationView-item {
diff --git a/src/client/views/presentationview/PresentationView.tsx b/src/client/views/presentationview/PresentationView.tsx
deleted file mode 100644
index bea70f00b..000000000
--- a/src/client/views/presentationview/PresentationView.tsx
+++ /dev/null
@@ -1,994 +0,0 @@
-import { observer } from "mobx-react";
-import React = require("react");
-import { observable, action, runInAction, reaction, autorun } from "mobx";
-import "./PresentationView.scss";
-import { DocumentManager } from "../../util/DocumentManager";
-import { Utils } from "../../../Utils";
-import { Doc, DocListCast, DocListCastAsync, WidthSym } from "../../../new_fields/Doc";
-import { listSpec } from "../../../new_fields/Schema";
-import { Cast, NumCast, FieldValue, PromiseValue, StrCast, BoolCast } from "../../../new_fields/Types";
-import { Id } from "../../../new_fields/FieldSymbols";
-import { List } from "../../../new_fields/List";
-import PresentationElement, { buttonIndex } from "./PresentationElement";
-import { library } from '@fortawesome/fontawesome-svg-core';
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { faArrowRight, faArrowLeft, faPlay, faStop, faPlus, faTimes, faMinus, faEdit, faEye } from '@fortawesome/free-solid-svg-icons';
-import { Docs } from "../../documents/Documents";
-import { undoBatch, UndoManager } from "../../util/UndoManager";
-import PresentationViewList from "./PresentationList";
-import PresModeMenu from "./PresentationModeMenu";
-import { CollectionDockingView } from "../collections/CollectionDockingView";
-
-library.add(faArrowLeft);
-library.add(faArrowRight);
-library.add(faPlay);
-library.add(faStop);
-library.add(faPlus);
-library.add(faTimes);
-library.add(faMinus);
-library.add(faEdit);
-library.add(faEye);
-
-
-export interface PresViewProps {
-    Documents: List<Doc>;
-}
-
-const expandedWidth = 400;
-const presMinWidth = 300;
-
-@observer
-export class PresentationView extends React.Component<PresViewProps>  {
-    public static Instance: PresentationView;
-
-    //Mapping from presentation ids to a list of doc that represent a group
-    @observable groupMappings: Map<String, Doc[]> = new Map();
-    //mapping from docs to their rendered component
-    @observable presElementsMappings: Map<Doc, PresentationElement> = new Map();
-    //variable that holds all the docs in the presentation
-    @observable childrenDocs: Doc[] = [];
-    //variable to hold if presentation is started
-    @observable presStatus: boolean = false;
-    //back-up so that presentation stays the way it's when refreshed
-    @observable presGroupBackUp: Doc = new Doc();
-    @observable presButtonBackUp: Doc = new Doc();
-
-    //Keeping track of the doc for the current presentation
-    @observable curPresentation: Doc = new Doc();
-    //Mapping of guids to presentations.
-    @observable presentationsMapping: Map<String, Doc> = new Map();
-    //Mapping of presentations to guid, so that select option values can be given.
-    @observable presentationsKeyMapping: Map<Doc, String> = new Map();
-    //Variable to keep track of guid of the current presentation
-    @observable currentSelectedPresValue: string | undefined;
-    //A flag to keep track if title input is open, which is used in rendering.
-    @observable PresTitleInputOpen: boolean = false;
-    //Variable that holds reference to title input, so that new presentations get titles assigned.
-    @observable titleInputElement: HTMLInputElement | undefined;
-    @observable PresTitleChangeOpen: boolean = false;
-    @observable presMode: boolean = false;
-
-
-    @observable opacity = 1;
-    @observable persistOpacity = true;
-    @observable labelOpacity = 0;
-
-    //initilize class variables
-    constructor(props: PresViewProps) {
-        super(props);
-        PresentationView.Instance = this;
-    }
-
-    @action
-    toggle = (forcedValue: boolean | undefined) => {
-        if (forcedValue !== undefined) {
-            this.curPresentation.width = forcedValue ? expandedWidth : 0;
-        } else {
-            this.curPresentation.width = this.curPresentation.width === expandedWidth ? 0 : expandedWidth;
-        }
-    }
-
-    //The first lifecycle function that gets called to set up the current presentation.
-    async componentWillMount() {
-
-        this.props.Documents.forEach(async (doc, index: number) => {
-
-            //For each presentation received from mainContainer, a mapping is created.
-            let curDoc: Doc = await doc;
-            let newGuid = Utils.GenerateGuid();
-            this.presentationsKeyMapping.set(curDoc, newGuid);
-            this.presentationsMapping.set(newGuid, curDoc);
-
-            //The Presentation at first index gets set as default start presentation
-            if (index === 0) {
-                runInAction(() => this.currentSelectedPresValue = newGuid);
-                runInAction(() => this.curPresentation = curDoc);
-            }
-        });
-    }
-
-    //Second lifecycle function that gets called when component mounts. It makes sure to
-    //get the back-up information from previous session for the current presentation.
-    async componentDidMount() {
-        let docAtZero = await this.props.Documents[0];
-        runInAction(() => this.curPresentation = docAtZero);
-
-        this.setPresentationBackUps();
-
-    }
-
-
-    /**
-     * The function that retrieves the backUps for the current Presentation if present,
-     * otherwise initializes.
-     */
-    setPresentationBackUps = async () => {
-        //getting both backUp documents
-
-        let castedGroupBackUp = Cast(this.curPresentation.presGroupBackUp, Doc);
-        let castedButtonBackUp = Cast(this.curPresentation.presButtonBackUp, Doc);
-        //if instantiated before 
-        if (castedGroupBackUp instanceof Promise) {
-            castedGroupBackUp.then(doc => {
-                let toAssign = doc ? doc : new Doc();
-                this.curPresentation.presGroupBackUp = toAssign;
-                runInAction(() => this.presGroupBackUp = toAssign);
-                if (doc) {
-                    if (toAssign[Id] === doc[Id]) {
-                        this.retrieveGroupMappings();
-                    }
-                }
-            });
-
-            //if never instantiated a store doc yet
-        } else if (castedGroupBackUp instanceof Doc) {
-            let castedDoc: Doc = await castedGroupBackUp;
-            runInAction(() => this.presGroupBackUp = castedDoc);
-            this.retrieveGroupMappings();
-        } else {
-            runInAction(() => {
-                let toAssign = new Doc();
-                this.presGroupBackUp = toAssign;
-                this.curPresentation.presGroupBackUp = toAssign;
-
-            });
-
-        }
-        //if instantiated before 
-        if (castedButtonBackUp instanceof Promise) {
-            castedButtonBackUp.then(doc => {
-                let toAssign = doc ? doc : new Doc();
-                this.curPresentation.presButtonBackUp = toAssign;
-                runInAction(() => this.presButtonBackUp = toAssign);
-            });
-
-            //if never instantiated a store doc yet
-        } else if (castedButtonBackUp instanceof Doc) {
-            let castedDoc: Doc = await castedButtonBackUp;
-            runInAction(() => this.presButtonBackUp = castedDoc);
-
-        } else {
-            runInAction(() => {
-                let toAssign = new Doc();
-                this.presButtonBackUp = toAssign;
-                this.curPresentation.presButtonBackUp = toAssign;
-            });
-
-        }
-
-
-        //storing the presentation status,ie. whether it was stopped or playing
-        let presStatusBackUp = BoolCast(this.curPresentation.presStatus);
-        runInAction(() => this.presStatus = presStatusBackUp);
-    }
-
-    /**
-     * This is the function that is called to retrieve the groups that have been stored and
-     * push them to the groupMappings.
-     */
-    retrieveGroupMappings = async () => {
-        let castedGroupDocs = await DocListCastAsync(this.presGroupBackUp.groupDocs);
-        if (castedGroupDocs !== undefined) {
-            castedGroupDocs.forEach(async (groupDoc: Doc, index: number) => {
-                let castedGrouping = await DocListCastAsync(groupDoc.grouping);
-                let castedKey = StrCast(groupDoc.presentIdStore, null);
-                if (castedGrouping) {
-                    castedGrouping.forEach((doc: Doc) => {
-                        doc.presentId = castedKey;
-                    });
-                }
-                if (castedGrouping !== undefined && castedKey !== undefined) {
-                    this.groupMappings.set(castedKey, castedGrouping);
-                }
-            });
-        }
-    }
-
-    //observable means render is re-called every time variable is changed
-    @observable
-    collapsed: boolean = false;
-    closePresentation = action(() => this.curPresentation.width = 0);
-    next = async () => {
-        const current = NumCast(this.curPresentation.selectedDoc);
-        //asking to get document at current index
-        let docAtCurrentNext = await this.getDocAtIndex(current + 1);
-        if (docAtCurrentNext === undefined) {
-            return;
-        }
-        //asking for it's presentation id
-        let curNextPresId = StrCast(docAtCurrentNext.presentId);
-        let nextSelected = current + 1;
-
-        //if curDoc is in a group, selection slides until last one, if not it's next one
-        if (this.groupMappings.has(curNextPresId)) {
-            let currentsArray = this.groupMappings.get(StrCast(docAtCurrentNext.presentId))!;
-            nextSelected = current + currentsArray.length - currentsArray.indexOf(docAtCurrentNext);
-
-            //end of grup so go beyond
-            if (nextSelected === current) nextSelected = current + 1;
-        }
-
-        this.gotoDocument(nextSelected, current);
-
-    }
-    back = async () => {
-        const current = NumCast(this.curPresentation.selectedDoc);
-        //requesting for the doc at current index
-        let docAtCurrent = await this.getDocAtIndex(current);
-        if (docAtCurrent === undefined) {
-            return;
-        }
-
-        //asking for its presentation id.
-        let curPresId = StrCast(docAtCurrent.presentId);
-        let prevSelected = current - 1;
-        let zoomOut: boolean = false;
-
-        //checking if this presentation id is mapped to a group, if so chosing the first element in group
-        if (this.groupMappings.has(curPresId)) {
-            let currentsArray = this.groupMappings.get(StrCast(docAtCurrent.presentId))!;
-            prevSelected = current - currentsArray.length + (currentsArray.length - currentsArray.indexOf(docAtCurrent)) - 1;
-            //end of grup so go beyond
-            if (prevSelected === current) prevSelected = current - 1;
-
-            //checking if any of the group members had used zooming in
-            currentsArray.forEach((doc: Doc) => {
-                //let presElem: PresentationElement | undefined = this.presElementsMappings.get(doc);
-                if (this.presElementsMappings.get(doc)!.selected[buttonIndex.Show]) {
-                    zoomOut = true;
-                    return;
-                }
-            });
-
-        }
-
-        // if a group set that flag to zero or a single element
-        //If so making sure to zoom out, which goes back to state before zooming action
-        if (current > 0) {
-            if (zoomOut || this.presElementsMappings.get(docAtCurrent)!.selected[buttonIndex.Show]) {
-                let prevScale = NumCast(this.childrenDocs[prevSelected].viewScale, null);
-                let curScale = DocumentManager.Instance.getScaleOfDocView(this.childrenDocs[current]);
-                if (prevScale !== undefined) {
-                    if (prevScale !== curScale) {
-                        DocumentManager.Instance.zoomIntoScale(docAtCurrent, prevScale);
-                    }
-                }
-            }
-        }
-        this.gotoDocument(prevSelected, current);
-
-    }
-
-    /**
-     * This is the method that checks for the actions that need to be performed
-     * after the document has been presented, which involves 3 button options:
-     * Hide Until Presented, Hide After Presented, Fade After Presented
-     */
-    showAfterPresented = (index: number) => {
-        this.presElementsMappings.forEach((presElem: PresentationElement, key: Doc) => {
-            let selectedButtons: boolean[] = presElem.selected;
-            //the order of cases is aligned based on priority
-            if (selectedButtons[buttonIndex.HideTillPressed]) {
-                if (this.childrenDocs.indexOf(key) <= index) {
-                    key.opacity = 1;
-                }
-            }
-            if (selectedButtons[buttonIndex.HideAfter]) {
-                if (this.childrenDocs.indexOf(key) < index) {
-                    key.opacity = 0;
-                }
-            }
-            if (selectedButtons[buttonIndex.FadeAfter]) {
-                if (this.childrenDocs.indexOf(key) < index) {
-                    key.opacity = 0.5;
-                }
-            }
-        });
-    }
-
-    /**
-     * This is the method that checks for the actions that need to be performed
-     * before the document has been presented, which involves 3 button options:
-     * Hide Until Presented, Hide After Presented, Fade After Presented
-     */
-    hideIfNotPresented = (index: number) => {
-        this.presElementsMappings.forEach((presElem: PresentationElement, key: Doc) => {
-            let selectedButtons: boolean[] = presElem.selected;
-
-            //the order of cases is aligned based on priority
-
-            if (selectedButtons[buttonIndex.HideAfter]) {
-                if (this.childrenDocs.indexOf(key) >= index) {
-                    key.opacity = 1;
-                }
-            }
-            if (selectedButtons[buttonIndex.FadeAfter]) {
-                if (this.childrenDocs.indexOf(key) >= index) {
-                    key.opacity = 1;
-                }
-            }
-            if (selectedButtons[buttonIndex.HideTillPressed]) {
-                if (this.childrenDocs.indexOf(key) > index) {
-                    key.opacity = 0;
-                }
-            }
-        });
-    }
-
-    /**
-     * This method makes sure that cursor navigates to the element that
-     * has the option open and last in the group. If not in the group, and it has
-     * te option open, navigates to that element.
-     */
-    navigateToElement = async (curDoc: Doc, fromDoc: number) => {
-        let docToJump: Doc = curDoc;
-        let curDocPresId = StrCast(curDoc.presentId, null);
-        let willZoom: boolean = false;
-
-        //checking if in group
-        if (curDocPresId !== undefined) {
-            if (this.groupMappings.has(curDocPresId)) {
-                let currentDocGroup = this.groupMappings.get(curDocPresId)!;
-                currentDocGroup.forEach((doc: Doc, index: number) => {
-                    let selectedButtons: boolean[] = this.presElementsMappings.get(doc)!.selected;
-                    if (selectedButtons[buttonIndex.Navigate]) {
-                        docToJump = doc;
-                        willZoom = false;
-                    }
-                    if (selectedButtons[buttonIndex.Show]) {
-                        docToJump = doc;
-                        willZoom = true;
-                    }
-                });
-            }
-
-        }
-        //docToJump stayed same meaning, it was not in the group or was the last element in the group
-        if (docToJump === curDoc) {
-            //checking if curDoc has navigation open
-            let curDocButtons = this.presElementsMappings.get(curDoc)!.selected;
-            if (curDocButtons[buttonIndex.Navigate]) {
-                this.jumpToTabOrRight(curDocButtons, curDoc);
-            } else if (curDocButtons[buttonIndex.Show]) {
-                let curScale = DocumentManager.Instance.getScaleOfDocView(this.childrenDocs[fromDoc]);
-                if (curDocButtons[buttonIndex.OpenRight]) {
-                    //awaiting jump so that new scale can be found, since jumping is async
-                    await DocumentManager.Instance.jumpToDocument(curDoc, true);
-                } else {
-                    await DocumentManager.Instance.jumpToDocument(curDoc, true, undefined, doc => CollectionDockingView.Instance.AddTab(undefined, doc, undefined));
-                }
-
-                let newScale = DocumentManager.Instance.getScaleOfDocView(curDoc);
-                curDoc.viewScale = newScale;
-
-                //saving the scale user was on before zooming in
-                if (curScale !== 1) {
-                    this.childrenDocs[fromDoc].viewScale = curScale;
-                }
-
-            }
-            return;
-        }
-        let curScale = DocumentManager.Instance.getScaleOfDocView(this.childrenDocs[fromDoc]);
-        let curDocButtons = this.presElementsMappings.get(docToJump)!.selected;
-
-
-        if (curDocButtons[buttonIndex.OpenRight]) {
-            //awaiting jump so that new scale can be found, since jumping is async
-            await DocumentManager.Instance.jumpToDocument(docToJump, willZoom);
-        } else {
-            await DocumentManager.Instance.jumpToDocument(docToJump, willZoom, undefined, doc => CollectionDockingView.Instance.AddTab(undefined, doc, undefined));
-        }
-        let newScale = DocumentManager.Instance.getScaleOfDocView(curDoc);
-        curDoc.viewScale = newScale;
-        //saving the scale that user was on
-        if (curScale !== 1) {
-            this.childrenDocs[fromDoc].viewScale = curScale;
-        }
-
-    }
-
-    /**
-     * This function  checks if right option is clicked on a presentation element, if not it does open it as a tab
-     * with help of CollectionDockingView.
-     */
-    jumpToTabOrRight = (curDocButtons: boolean[], curDoc: Doc) => {
-        if (curDocButtons[buttonIndex.OpenRight]) {
-            DocumentManager.Instance.jumpToDocument(curDoc, false);
-        } else {
-            DocumentManager.Instance.jumpToDocument(curDoc, false, undefined, doc => CollectionDockingView.Instance.AddTab(undefined, doc, undefined));
-        }
-    }
-
-    /**
-     * Async function that supposedly return the doc that is located at given index.
-     */
-    getDocAtIndex = async (index: number) => {
-        const list = FieldValue(Cast(this.curPresentation.data, listSpec(Doc)));
-        if (!list) {
-            return undefined;
-        }
-        if (index < 0 || index >= list.length) {
-            return undefined;
-        }
-
-        this.curPresentation.selectedDoc = index;
-        //awaiting async call to finish to get Doc instance
-        const doc = await list[index];
-        return doc;
-    }
-
-    /**
-     * The function that removes a doc from a presentation. It also makes sure to
-     * do necessary updates to backUps and mappings stored locally.
-     */
-    @action
-    public RemoveDoc = async (index: number) => {
-        const value = FieldValue(Cast(this.curPresentation.data, listSpec(Doc)));
-        if (value) {
-            let removedDoc = await value.splice(index, 1)[0];
-
-            //removing the Presentation Element stored for it
-            this.presElementsMappings.delete(removedDoc);
-
-            let removedDocPresentId = StrCast(removedDoc.presentId);
-
-            //Removing it from local mapping of the groups
-            if (this.groupMappings.has(removedDocPresentId)) {
-                let removedDocsGroup = this.groupMappings.get(removedDocPresentId);
-                if (removedDocsGroup) {
-                    removedDocsGroup.splice(removedDocsGroup.indexOf(removedDoc), 1);
-                    if (removedDocsGroup.length === 0) {
-                        this.groupMappings.delete(removedDocPresentId);
-                    }
-                }
-            }
-
-
-            let castedList = Cast(this.presButtonBackUp.selectedButtonDocs, listSpec(Doc));
-            if (castedList) {
-                for (let doc of castedList) {
-                    let curDoc = await doc;
-                    let curDocId = StrCast(curDoc.docId);
-                    if (curDocId === removedDoc[Id]) {
-                        castedList.splice(castedList.indexOf(curDoc), 1);
-                        break;
-
-                    }
-                }
-            }
-
-            //removing it from the backup of groups
-            let castedGroupDocs = await DocListCastAsync(this.presGroupBackUp.groupDocs);
-            if (castedGroupDocs) {
-                castedGroupDocs.forEach(async (groupDoc: Doc, index: number) => {
-                    let castedKey = StrCast(groupDoc.presentIdStore, null);
-                    if (castedKey === removedDocPresentId) {
-                        let castedGrouping = await DocListCastAsync(groupDoc.grouping);
-                        if (castedGrouping) {
-                            castedGrouping.splice(castedGrouping.indexOf(removedDoc), 1);
-                            if (castedGrouping.length === 0) {
-                                castedGroupDocs!.splice(castedGroupDocs!.indexOf(groupDoc), 1);
-                            }
-                        }
-                    }
-
-                });
-
-            }
-
-
-        }
-    }
-
-    /**
-     * An alternative remove method that removes a doc from presentation by its actual
-     * reference.
-     */
-    public removeDocByRef = (doc: Doc) => {
-        let indexOfDoc = this.childrenDocs.indexOf(doc);
-        const value = FieldValue(Cast(this.curPresentation.data, listSpec(Doc)));
-        if (value) {
-            value.splice(indexOfDoc, 1)[0];
-        }
-        if (indexOfDoc !== - 1) {
-            return true;
-        }
-        return false;
-    }
-
-    //The function that is called when a document is clicked or reached through next or back.
-    //it'll also execute the necessary actions if presentation is playing.
-    @action
-    public gotoDocument = async (index: number, fromDoc: number) => {
-        const list = FieldValue(Cast(this.curPresentation.data, listSpec(Doc)));
-        if (!list) {
-            return;
-        }
-        if (index < 0 || index >= list.length) {
-            return;
-        }
-        this.curPresentation.selectedDoc = index;
-
-        if (!this.presStatus) {
-            this.presStatus = true;
-            this.startPresentation(index);
-        }
-
-        const doc = await list[index];
-        if (this.presStatus) {
-            this.navigateToElement(doc, fromDoc);
-            this.hideIfNotPresented(index);
-            this.showAfterPresented(index);
-        }
-
-    }
-
-    //Function that is called to resetGroupIds, so that documents get new groupIds at
-    //first load, when presentation is changed.
-    resetGroupIds = async () => {
-        let castedGroupDocs = await DocListCastAsync(this.presGroupBackUp.groupDocs);
-        if (castedGroupDocs !== undefined) {
-            castedGroupDocs.forEach(async (groupDoc: Doc, index: number) => {
-                let castedGrouping = await DocListCastAsync(groupDoc.grouping);
-                if (castedGrouping) {
-                    castedGrouping.forEach((doc: Doc) => {
-                        doc.presentId = Utils.GenerateGuid();
-                    });
-                }
-            });
-        }
-        runInAction(() => this.groupMappings = new Map());
-    }
-
-    /**
-     * Adds a document to the presentation view
-     **/
-    @undoBatch
-    @action
-    public PinDoc(doc: Doc) {
-        //add this new doc to props.Document
-        const data = Cast(this.curPresentation.data, listSpec(Doc));
-        if (data) {
-            data.push(doc);
-        } else {
-            this.curPresentation.data = new List([doc]);
-        }
-
-        this.toggle(true);
-    }
-
-    //Function that sets the store of the children docs.
-    @action
-    setChildrenDocs = (docList: Doc[]) => {
-        this.childrenDocs = docList;
-    }
-
-    //The function that is called to render the play or pause button depending on
-    //if presentation is running or not.
-    renderPlayPauseButton = () => {
-        if (this.presStatus) {
-            return <button title="Reset Presentation" className="presentation-button" onClick={this.startOrResetPres}><FontAwesomeIcon icon="stop" /></button>;
-        } else {
-            return <button title="Start Presentation From Start" className="presentation-button" onClick={this.startOrResetPres}><FontAwesomeIcon icon="play" /></button>;
-        }
-    }
-
-    //The function that starts or resets presentaton functionally, depending on status flag.
-    @action
-    startOrResetPres = async () => {
-        if (this.presStatus) {
-            this.resetPresentation();
-        } else {
-            this.presStatus = true;
-            let startIndex = await this.findStartDocument();
-            this.startPresentation(startIndex);
-            const current = NumCast(this.curPresentation.selectedDoc);
-            this.gotoDocument(startIndex, current);
-        }
-        this.curPresentation.presStatus = this.presStatus;
-    }
-
-    /**
-     * This method is called to find the start document of presentation. So
-     * that when user presses on play, the correct presentation element will be
-     * selected.
-     */
-    findStartDocument = async () => {
-        let docAtZero = await this.getDocAtIndex(0);
-        if (docAtZero === undefined) {
-            return 0;
-        }
-        let docAtZeroPresId = StrCast(docAtZero.presentId);
-
-        if (this.groupMappings.has(docAtZeroPresId)) {
-            let group = this.groupMappings.get(docAtZeroPresId)!;
-            let lastDoc = group[group.length - 1];
-            return this.childrenDocs.indexOf(lastDoc);
-        } else {
-            return 0;
-        }
-    }
-
-    //The function that resets the presentation by removing every action done by it. It also
-    //stops the presentaton.
-    @action
-    resetPresentation = () => {
-        this.childrenDocs.forEach((doc: Doc) => {
-            doc.opacity = 1;
-            doc.viewScale = 1;
-        });
-        this.curPresentation.selectedDoc = 0;
-        this.presStatus = false;
-        this.curPresentation.presStatus = this.presStatus;
-        if (this.childrenDocs.length === 0) {
-            return;
-        }
-        DocumentManager.Instance.zoomIntoScale(this.childrenDocs[0], 1);
-    }
-
-
-    //The function that starts the presentation, also checking if actions should be applied
-    //directly at start.
-    startPresentation = (startIndex: number) => {
-        let selectedButtons: boolean[];
-        this.presElementsMappings.forEach((component: PresentationElement, doc: Doc) => {
-            selectedButtons = component.selected;
-            if (selectedButtons[buttonIndex.HideTillPressed]) {
-                if (this.childrenDocs.indexOf(doc) > startIndex) {
-                    doc.opacity = 0;
-                }
-
-            }
-            if (selectedButtons[buttonIndex.HideAfter]) {
-                if (this.childrenDocs.indexOf(doc) < startIndex) {
-                    doc.opacity = 0;
-                }
-            }
-            if (selectedButtons[buttonIndex.FadeAfter]) {
-                if (this.childrenDocs.indexOf(doc) < startIndex) {
-                    doc.opacity = 0.5;
-                }
-            }
-
-        });
-
-    }
-
-    /**
-     * The function that is called to add a new presentation to the presentationView.
-     * It sets up te mappings and local copies of it. Resets the groupings and presentation.
-     * Makes the new presentation current selected, and retrieve the back-Ups if present.
-     */
-    @action
-    addNewPresentation = (presTitle: string) => {
-        //creating a new presentation doc
-        let newPresentationDoc = Docs.Create.TreeDocument([], { title: presTitle });
-        this.props.Documents.push(newPresentationDoc);
-
-        //setting that new doc as current
-        this.curPresentation = newPresentationDoc;
-
-        //storing the doc in local copies for easier access
-        let newGuid = Utils.GenerateGuid();
-        this.presentationsMapping.set(newGuid, newPresentationDoc);
-        this.presentationsKeyMapping.set(newPresentationDoc, newGuid);
-
-        //resetting the previous presentation's actions so that new presentation can be loaded.
-        this.resetGroupIds();
-        this.resetPresentation();
-        this.presElementsMappings = new Map();
-        this.currentSelectedPresValue = newGuid;
-        this.setPresentationBackUps();
-
-    }
-
-    /**
-     * The function that is called to change the current selected presentation.
-     * Changes the presentation, also resetting groupings and presentation in process.
-     * Plus retrieving the backUps for the newly selected presentation.
-     */
-    @action
-    getSelectedPresentation = (e: React.ChangeEvent<HTMLSelectElement>) => {
-        //get the guid of the selected presentation
-        let selectedGuid = e.target.value;
-        //set that as current presentation
-        this.curPresentation = this.presentationsMapping.get(selectedGuid)!;
-
-        //reset current Presentations local things so that new one can be loaded
-        this.resetGroupIds();
-        this.resetPresentation();
-        this.presElementsMappings = new Map();
-        this.currentSelectedPresValue = selectedGuid;
-        this.setPresentationBackUps();
-
-
-    }
-
-    /**
-     * The function that is called to render either select for presentations, or title inputting.
-     */
-    renderSelectOrPresSelection = () => {
-        let presentationList = DocListCast(this.props.Documents);
-        if (this.PresTitleInputOpen || this.PresTitleChangeOpen) {
-            return <input ref={(e) => this.titleInputElement = e!} type="text" className="presentationView-title" placeholder="Enter Name!" onKeyDown={this.submitPresentationTitle} />;
-        } else {
-            return <select value={this.currentSelectedPresValue} id="pres_selector" className="presentationView-title" onChange={this.getSelectedPresentation}>
-                {presentationList.map((doc: Doc, index: number) => {
-                    let mappedGuid = this.presentationsKeyMapping.get(doc);
-                    let docGuid: string = mappedGuid ? mappedGuid.toString() : "";
-                    return <option key={docGuid} value={docGuid}>{StrCast(doc.title)}</option>;
-                })}
-            </select>;
-        }
-    }
-
-    /**
-     * The function that is called on enter press of title input. It gives the
-     * new presentation the title user entered. If nothing is entered, gives a default title.
-     */
-    @action
-    submitPresentationTitle = (e: React.KeyboardEvent) => {
-        if (e.keyCode === 13) {
-            let presTitle = this.titleInputElement!.value;
-            this.titleInputElement!.value = "";
-            if (this.PresTitleInputOpen) {
-                if (presTitle === "") {
-                    presTitle = "Presentation";
-                }
-                this.PresTitleInputOpen = false;
-                this.addNewPresentation(presTitle);
-            } else if (this.PresTitleChangeOpen) {
-                this.PresTitleChangeOpen = false;
-                this.changePresentationTitle(presTitle);
-            }
-        }
-    }
-
-    /**
-     * The function that is called to remove a presentation from all its copies, and the main Container's
-     * list. Sets up the next presentation as current.
-     */
-    @action
-    removePresentation = async () => {
-        if (this.presentationsMapping.size !== 1) {
-            let presentationList = Cast(this.props.Documents, listSpec(Doc));
-            let batch = UndoManager.StartBatch("presRemoval");
-
-            //getting the presentation that will be removed
-            let removedDoc = this.presentationsMapping.get(this.currentSelectedPresValue!);
-            //that presentation is removed
-            presentationList!.splice(presentationList!.indexOf(removedDoc!), 1);
-
-            //its mappings are removed from local copies
-            this.presentationsKeyMapping.delete(removedDoc!);
-            this.presentationsMapping.delete(this.currentSelectedPresValue!);
-
-            //the next presentation is set as current
-            let remainingPresentations = this.presentationsMapping.values();
-            let nextDoc = remainingPresentations.next().value;
-            this.curPresentation = nextDoc;
-
-
-            //Storing these for being able to undo changes
-            let curGuid = this.currentSelectedPresValue!;
-            let curPresStatus = this.presStatus;
-
-            //resetting the groups and presentation actions so that next presentation gets loaded
-            this.resetGroupIds();
-            this.resetPresentation();
-            this.currentSelectedPresValue = this.presentationsKeyMapping.get(nextDoc)!.toString();
-            this.setPresentationBackUps();
-
-            //Storing for undo
-            let currentGroups = this.groupMappings;
-            let curPresElemMapping = this.presElementsMappings;
-
-            //Event to undo actions that are not related to doc directly, aka. local things
-            UndoManager.AddEvent({
-                undo: action(() => {
-                    this.curPresentation = removedDoc!;
-                    this.presentationsMapping.set(curGuid, removedDoc!);
-                    this.presentationsKeyMapping.set(removedDoc!, curGuid);
-                    this.currentSelectedPresValue = curGuid;
-
-                    this.presStatus = curPresStatus;
-                    this.groupMappings = currentGroups;
-                    this.presElementsMappings = curPresElemMapping;
-                    this.setPresentationBackUps();
-
-                }),
-                redo: action(() => {
-                    this.curPresentation = nextDoc;
-                    this.presStatus = false;
-                    this.presentationsKeyMapping.delete(removedDoc!);
-                    this.presentationsMapping.delete(curGuid);
-                    this.currentSelectedPresValue = this.presentationsKeyMapping.get(nextDoc)!.toString();
-                    this.setPresentationBackUps();
-
-                }),
-            });
-
-            batch.end();
-        }
-    }
-
-    /**
-     * The function that is called to change title of presentation to what user entered.
-     */
-    @undoBatch
-    changePresentationTitle = (newTitle: string) => {
-        if (newTitle === "") {
-            return;
-        }
-        this.curPresentation.title = newTitle;
-    }
-
-    /**
-     * On pointer down element that is catched on resizer of te
-     * presentation view. Sets up the event listeners to change the size with
-     * mouse move.
-     */
-    _downsize = 0;
-    onPointerDown = (e: React.PointerEvent) => {
-        this._downsize = e.clientX;
-        document.removeEventListener("pointermove", this.onPointerMove);
-        document.removeEventListener("pointerup", this.onPointerUp);
-        document.addEventListener("pointermove", this.onPointerMove);
-        document.addEventListener("pointerup", this.onPointerUp);
-        e.stopPropagation();
-        e.preventDefault();
-    }
-    /**
-     * Changes the size of the presentation view, with mouse move.
-     * Minimum size is set to 300, so that every button is visible.
-     */
-    @action
-    onPointerMove = (e: PointerEvent) => {
-
-        this.curPresentation.width = Math.max(window.innerWidth - e.clientX, presMinWidth);
-    }
-
-    /**
-     * The method that is called on pointer up event. It checks if the button is just
-     * clicked so that presentation view will be closed. The way it's done is to check 
-     * for minimal pixel change like 4, and accept it as it's just a click on top of the dragger.
-     */
-    @action
-    onPointerUp = (e: PointerEvent) => {
-        if (Math.abs(e.clientX - this._downsize) < 4) {
-            let presWidth = NumCast(this.curPresentation.width);
-            if (presWidth - presMinWidth !== 0) {
-                this.curPresentation.width = 0;
-            }
-            if (presWidth === 0) {
-                this.curPresentation.width = presMinWidth;
-            }
-        }
-        document.removeEventListener("pointermove", this.onPointerMove);
-        document.removeEventListener("pointerup", this.onPointerUp);
-    }
-
-    /**
-     * This function is a setter that opens up the 
-     * presentation mode, by setting it's render flag
-     * to true. It also closes the presentation view.
-     */
-    @action
-    openPresMode = () => {
-        if (!this.presMode) {
-            this.curPresentation.width = 0;
-            this.presMode = true;
-        }
-    }
-
-    /**
-     * This function closes the presentation mode by setting its
-     * render flag to false. It also opens up the presentation view.
-     * By setting it to it's minimum size.
-     */
-    @action
-    closePresMode = () => {
-        if (this.presMode) {
-            this.presMode = false;
-            this.curPresentation.width = presMinWidth;
-        }
-
-    }
-
-    /**
-     * Function that is called to render the presentation mode, depending on its flag.
-     */
-    renderPresMode = () => {
-        if (this.presMode) {
-            return <PresModeMenu next={this.next} back={this.back} startOrResetPres={this.startOrResetPres} presStatus={this.presStatus} closePresMode={this.closePresMode} />;
-        } else {
-            return (null);
-        }
-
-    }
-
-    render() {
-
-        let width = NumCast(this.curPresentation.width);
-
-        return (
-            <div>
-                <div className="presentationView-cont" onPointerEnter={action(() => !this.persistOpacity && (this.opacity = 1))} onPointerLeave={action(() => !this.persistOpacity && (this.opacity = 0.4))} style={{ width: width, overflowY: "scroll", overflowX: "hidden", opacity: this.opacity, transition: "0.7s opacity ease" }}>
-                    <div className="presentationView-heading">
-                        {this.renderSelectOrPresSelection()}
-                        <button title="Close Presentation" className='presentation-icon' onClick={this.closePresentation}><FontAwesomeIcon icon={"times"} /></button>
-                        <button title="Open Presentation Mode" className="presentation-icon" style={{ marginRight: 10 }} onClick={this.openPresMode}><FontAwesomeIcon icon={"eye"} /></button>
-                        <button title="Add Presentation" className="presentation-icon" style={{ marginRight: 10 }} onClick={() => {
-                            runInAction(() => { if (this.PresTitleChangeOpen) { this.PresTitleChangeOpen = false; } });
-                            runInAction(() => this.PresTitleInputOpen ? this.PresTitleInputOpen = false : this.PresTitleInputOpen = true);
-                        }}><FontAwesomeIcon icon={"plus"} /></button>
-                        <button title="Remove Presentation" className='presentation-icon' style={{ marginRight: 10 }} onClick={this.removePresentation}><FontAwesomeIcon icon={"minus"} /></button>
-                        <button title="Change Presentation Title" className="presentation-icon" style={{ marginRight: 10 }} onClick={() => {
-                            runInAction(() => { if (this.PresTitleInputOpen) { this.PresTitleInputOpen = false; } });
-                            runInAction(() => this.PresTitleChangeOpen ? this.PresTitleChangeOpen = false : this.PresTitleChangeOpen = true);
-                        }}><FontAwesomeIcon icon={"edit"} /></button>
-                    </div>
-                    <div className="presentation-buttons">
-                        <button title="Back" className="presentation-button" onClick={this.back}><FontAwesomeIcon icon={"arrow-left"} /></button>
-                        {this.renderPlayPauseButton()}
-                        <button title="Next" className="presentation-button" onClick={this.next}><FontAwesomeIcon icon={"arrow-right"} /></button>
-                    </div>
-
-                    <PresentationViewList
-                        mainDocument={this.curPresentation}
-                        deleteDocument={this.RemoveDoc}
-                        gotoDocument={this.gotoDocument}
-                        groupMappings={this.groupMappings}
-                        PresElementsMappings={this.presElementsMappings}
-                        setChildrenDocs={this.setChildrenDocs}
-                        presStatus={this.presStatus}
-                        presButtonBackUp={this.presButtonBackUp}
-                        presGroupBackUp={this.presGroupBackUp}
-                        removeDocByRef={this.removeDocByRef}
-                        clearElemMap={() => this.presElementsMappings.clear()}
-                    />
-                    <input
-                        type="checkbox"
-                        onChange={action((e: React.ChangeEvent<HTMLInputElement>) => {
-                            this.persistOpacity = e.target.checked;
-                            this.opacity = this.persistOpacity ? 1 : 0.4;
-                        })}
-                        checked={this.persistOpacity}
-                        style={{ position: "absolute", bottom: 5, left: 5 }}
-                        onPointerEnter={action(() => this.labelOpacity = 1)}
-                        onPointerLeave={action(() => this.labelOpacity = 0)}
-                    />
-                    <p style={{ position: "absolute", bottom: 1, left: 22, opacity: this.labelOpacity, transition: "0.7s opacity ease" }}>opacity {this.persistOpacity ? "persistent" : "on focus"}</p>
-                </div>
-                <div className="mainView-libraryHandle"
-                    style={{ cursor: "ew-resize", right: `${width - 10}px`, backgroundColor: "white", opacity: this.opacity, transition: "0.7s opacity ease" }}
-                    onPointerDown={this.onPointerDown}>
-                    <span title="library View Dragger" style={{ width: "100%", height: "100%", position: "absolute" }} />
-                </div>
-                {this.renderPresMode()}
-
-            </div>
-        );
-    }
-}
diff --git a/src/client/views/search/SearchItem.tsx b/src/client/views/search/SearchItem.tsx
index 4bb2ed55b..41fc49c2e 100644
--- a/src/client/views/search/SearchItem.tsx
+++ b/src/client/views/search/SearchItem.tsx
@@ -203,6 +203,7 @@ export class SearchItem extends React.Component<SearchItemProps> {
                     removeDocument={returnFalse}
                     ScreenToLocalTransform={Transform.Identity}
                     addDocTab={returnFalse}
+                    pinToPres={returnFalse}
                     renderDepth={1}
                     PanelWidth={returnXDimension}
                     PanelHeight={returnYDimension}
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index a8b616565..31f1f7a12 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -1,19 +1,18 @@
-import { observable, action, runInAction, ObservableMap } from "mobx";
-import { serializable, primitive, map, alias, list, PropSchema, custom } from "serializr";
-import { autoObject, SerializationHelper, Deserializable, afterDocDeserialize } from "../client/util/SerializationHelper";
+import { observable, ObservableMap, runInAction } from "mobx";
+import { alias, map, serializable } from "serializr";
 import { DocServer } from "../client/DocServer";
-import { setter, getter, getField, updateFunction, deleteProperty, makeEditable, makeReadOnly } from "./util";
-import { Cast, ToConstructor, PromiseValue, FieldValue, NumCast, BoolCast, StrCast } from "./Types";
-import { listSpec } from "./Schema";
-import { ObjectField } from "./ObjectField";
-import { RefField, FieldId } from "./RefField";
-import { ToScriptString, SelfProxy, Parent, OnUpdate, Self, HandleUpdate, Update, Id, Copy } from "./FieldSymbols";
-import { scriptingGlobal, CompileScript, Scripting } from "../client/util/Scripting";
-import { List } from "./List";
 import { DocumentType } from "../client/documents/DocumentTypes";
-import { ComputedField, ScriptField } from "./ScriptField";
+import { CompileScript, Scripting, scriptingGlobal } from "../client/util/Scripting";
+import { afterDocDeserialize, autoObject, Deserializable, SerializationHelper } from "../client/util/SerializationHelper";
+import { Copy, HandleUpdate, Id, OnUpdate, Parent, Self, SelfProxy, ToScriptString, Update } from "./FieldSymbols";
+import { List } from "./List";
+import { ObjectField } from "./ObjectField";
 import { PrefetchProxy, ProxyField } from "./Proxy";
-//import { CurrentUserUtils } from "../server/authentication/models/current_user_utils";
+import { FieldId, RefField } from "./RefField";
+import { listSpec } from "./Schema";
+import { ComputedField } from "./ScriptField";
+import { BoolCast, Cast, FieldValue, NumCast, PromiseValue, StrCast, ToConstructor } from "./Types";
+import { deleteProperty, getField, getter, makeEditable, makeReadOnly, setter, updateFunction } from "./util";
 
 export namespace Field {
     export function toKeyValueString(doc: Doc, key: string): string {
@@ -72,6 +71,7 @@ export const HeightSym = Symbol("Height");
 export const UpdatingFromServer = Symbol("UpdatingFromServer");
 const CachedUpdates = Symbol("Cached updates");
 
+
 function fetchProto(doc: Doc) {
     const proto = doc.proto;
     if (proto instanceof Promise) {
@@ -151,10 +151,10 @@ export class Doc extends RefField {
     }
 
     private [CachedUpdates]: { [key: string]: () => void | Promise<any> } = {};
-
+    public static CurrentUserEmail: string = "";
     public async [HandleUpdate](diff: any) {
         const set = diff.$set;
-        const sameAuthor = this.author === "foo@bar.com";//CurrentUserUtils.email;
+        const sameAuthor = this.author === Doc.CurrentUserEmail;
         if (set) {
             for (const key in set) {
                 if (!key.startsWith("fields.")) {
@@ -327,14 +327,12 @@ export namespace Doc {
         return Array.from(results);
     }
 
-    export function AddDocToList(target: Doc, key: string, doc: Doc, relativeTo?: Doc, before?: boolean, first?: boolean, allowDuplicates?: boolean) {
+    export function AddDocToList(target: Doc, key: string, doc: Doc, relativeTo?: Doc, before?: boolean, first?: boolean, allowDuplicates?: boolean, reversed?: boolean) {
         if (target[key] === undefined) {
-            console.log("target key undefined");
             Doc.GetProto(target)[key] = new List<Doc>();
         }
         let list = Cast(target[key], listSpec(Doc));
         if (list) {
-            console.log("has list");
             if (allowDuplicates !== true) {
                 let pind = list.reduce((l, d, i) => d instanceof Doc && Doc.AreProtosEqual(d, doc) ? i : l, -1);
                 if (pind !== -1) {
@@ -342,15 +340,18 @@ export namespace Doc {
                 }
             }
             if (first) {
-                console.log("is first");
                 list.splice(0, 0, doc);
             }
             else {
-                console.log("not first");
                 let ind = relativeTo ? list.indexOf(relativeTo) : -1;
-                if (ind === -1) list.push(doc);
-                else list.splice(before ? ind : ind + 1, 0, doc);
-                console.log("index", ind);
+                if (ind === -1) {
+                    if (reversed) list.splice(0, 0, doc);
+                    else list.push(doc);
+                }
+                else {
+                    if (reversed) list.splice(before ? (list.length - ind) + 1 : list.length - ind, 0, doc);
+                    else list.splice(before ? ind : ind + 1, 0, doc);
+                }
             }
         }
         return true;
@@ -595,10 +596,14 @@ export namespace Doc {
         });
     }
 
-    export class DocBrush {
+
+    export class DocData {
+        @observable _user_doc: Doc = undefined!;
         @observable BrushedDoc: ObservableMap<Doc, boolean> = new ObservableMap();
     }
-    const manager = new DocBrush();
+    const manager = new DocData();
+    export function UserDoc(): Doc { return manager._user_doc; }
+    export function SetUserDoc(doc: Doc) { manager._user_doc = doc; }
     export function IsBrushed(doc: Doc) {
         return manager.BrushedDoc.has(doc) || manager.BrushedDoc.has(Doc.GetDataDoc(doc));
     }
diff --git a/src/new_fields/util.ts b/src/new_fields/util.ts
index c546e2aac..04194509c 100644
--- a/src/new_fields/util.ts
+++ b/src/new_fields/util.ts
@@ -7,7 +7,6 @@ import { ObjectField } from "./ObjectField";
 import { action } from "mobx";
 import { Parent, OnUpdate, Update, Id, SelfProxy, Self } from "./FieldSymbols";
 import { DocServer } from "../client/DocServer";
-import { CurrentUserUtils } from "../server/authentication/models/current_user_utils";
 
 function _readOnlySetter(): never {
     throw new Error("Documents can't be modified in read-only mode");
@@ -61,7 +60,7 @@ const _setterImpl = action(function (target: any, prop: string | symbol | number
     }
     const writeMode = DocServer.getFieldWriteMode(prop as string);
     const fromServer = target[UpdatingFromServer];
-    const sameAuthor = fromServer || (receiver.author === CurrentUserUtils.email);
+    const sameAuthor = fromServer || (receiver.author === Doc.CurrentUserEmail);
     const writeToDoc = sameAuthor || (writeMode !== DocServer.WriteMode.LiveReadonly);
     const writeToServer = sameAuthor || (writeMode === DocServer.WriteMode.Default);
     if (writeToDoc) {
diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts
index f36f5b73d..508655605 100644
--- a/src/server/authentication/models/current_user_utils.ts
+++ b/src/server/authentication/models/current_user_utils.ts
@@ -10,20 +10,17 @@ import { CollectionView } from "../../../client/views/collections/CollectionView
 import { Doc } from "../../../new_fields/Doc";
 import { List } from "../../../new_fields/List";
 import { listSpec } from "../../../new_fields/Schema";
-import { Cast, FieldValue, StrCast } from "../../../new_fields/Types";
-import { RouteStore } from "../../RouteStore";
+import { Cast, StrCast } from "../../../new_fields/Types";
 import { Utils } from "../../../Utils";
+import { RouteStore } from "../../RouteStore";
 
 export class CurrentUserUtils {
-    private static curr_email: string;
     private static curr_id: string;
-    @observable private static user_document: Doc;
     //TODO tfs: these should be temporary...
     private static mainDocId: string | undefined;
 
-    public static get email() { return this.curr_email; }
     public static get id() { return this.curr_id; }
-    @computed public static get UserDocument() { return this.user_document; }
+    @computed public static get UserDocument() { return Doc.UserDoc(); }
     public static get MainDocId() { return this.mainDocId; }
     public static set MainDocId(id: string | undefined) { this.mainDocId = id; }
 
@@ -32,7 +29,7 @@ export class CurrentUserUtils {
         doc.viewType = CollectionViewType.Tree;
         doc.dropAction = "alias";
         doc.layout = CollectionView.LayoutString();
-        doc.title = this.email;
+        doc.title = Doc.CurrentUserEmail
         this.updateUserDocument(doc);
         doc.data = new List<Doc>();
         doc.gridGap = 5;
@@ -58,6 +55,12 @@ export class CurrentUserUtils {
             recentlyClosed.boxShadow = "0 0";
             doc.recentlyClosed = recentlyClosed;
         }
+        if (doc.curPresentation === undefined) {
+            const curPresentation = Docs.Create.PresDocument(new List<Doc>(), { title: "Presentation" });
+            curPresentation.excludeFromLibrary = true;
+            curPresentation.boxShadow = "0 0";
+            doc.curPresentation = curPresentation;
+        }
         if (doc.sidebar === undefined) {
             const sidebar = Docs.Create.StackingDocument([doc.workspaces as Doc, doc, doc.recentlyClosed as Doc], { title: "Sidebar" });
             sidebar.excludeFromLibrary = true;
@@ -85,15 +88,15 @@ export class CurrentUserUtils {
 
     public static async loadUserDocument({ id, email }: { id: string, email: string }) {
         this.curr_id = id;
-        this.curr_email = email;
+        Doc.CurrentUserEmail = email;
         await rp.get(Utils.prepend(RouteStore.getUserDocumentId)).then(id => {
             if (id) {
                 return DocServer.GetRefField(id).then(async field => {
                     if (field instanceof Doc) {
                         await this.updateUserDocument(field);
-                        runInAction(() => this.user_document = field);
+                        runInAction(() => Doc.SetUserDoc(field));
                     } else {
-                        runInAction(() => this.user_document = this.createUserDocument(id));
+                        runInAction(() => Doc.SetUserDoc(this.createUserDocument(id)));
                     }
                 });
             } else {
-- 
cgit v1.2.3-70-g09d2


From 8225f3d2f6647b4163f7fe056af73c86f85c28d2 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Mon, 19 Aug 2019 17:00:59 -0400
Subject: cleaned up some buttons

---
 src/client/DocServer.ts                            |  2 +
 src/client/views/MainView.tsx                      | 45 +++-------------------
 .../views/collections/CollectionTreeView.tsx       |  4 +-
 src/client/views/collections/CollectionView.tsx    |  6 ++-
 src/client/views/nodes/DocumentView.tsx            | 40 +++++++++++++------
 5 files changed, 43 insertions(+), 54 deletions(-)

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

diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts
index bf5168c22..2cec1046b 100644
--- a/src/client/DocServer.ts
+++ b/src/client/DocServer.ts
@@ -33,6 +33,8 @@ export namespace DocServer {
         LivePlayground = 3,
     }
 
+    export let AclsMode = WriteMode.Default;
+
     const fieldWriteModes: { [field: string]: WriteMode } = {};
     const docsWithUpdates: { [field: string]: Set<Doc> } = {};
 
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 7b7a5542d..b27b91c12 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -1,5 +1,5 @@
 import { IconName, library } from '@fortawesome/fontawesome-svg-core';
-import { faArrowDown, faArrowUp, faBolt, faCaretUp, faCat, faCheck, faClone, faCloudUploadAlt, faCommentAlt, faCut, faExclamation, faFilePdf, faFilm, faFont, faGlobeAsia, faLongArrowAltRight, faMusic, faObjectGroup, faPause, faPenNib, faPlay, faPortrait, faRedoAlt, faTable, faThumbtack, faTree, faUndoAlt } from '@fortawesome/free-solid-svg-icons';
+import { faArrowDown, faArrowUp, faBolt, faCaretUp, faCat, faCheck, faClone, faCloudUploadAlt, faCommentAlt, faCut, faExclamation, faFilePdf, faFilm, faFont, faGlobeAsia, faLongArrowAltRight, faMusic, faObjectGroup, faPause, faPenNib, faPlay, faPortrait, faRedoAlt, faThumbtack, faTree, faUndoAlt, faTv } from '@fortawesome/free-solid-svg-icons';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { action, computed, configure, observable, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
@@ -38,7 +38,6 @@ import { OverlayView } from './OverlayView';
 import PDFMenu from './pdf/PDFMenu';
 import { PreviewCursor } from './PreviewCursor';
 import { FilterBox } from './search/FilterBox';
-import { DocumentManager } from '../util/DocumentManager';
 import PresModeMenu from './presentationview/PresentationModeMenu';
 import { PresBox } from './nodes/PresBox';
 
@@ -172,7 +171,7 @@ export class MainView extends React.Component {
         library.add(faCat);
         library.add(faFilePdf);
         library.add(faObjectGroup);
-        library.add(faTable);
+        library.add(faTv);
         library.add(faGlobeAsia);
         library.add(faUndoAlt);
         library.add(faRedoAlt);
@@ -452,30 +451,15 @@ export class MainView extends React.Component {
 
         let btns: [React.RefObject<HTMLDivElement>, IconName, string, () => Doc][] = [
             [React.createRef<HTMLDivElement>(), "object-group", "Add Collection", addColNode],
-            [React.createRef<HTMLDivElement>(), "table", "Add Presentation Trail", addPresNode],
+            [React.createRef<HTMLDivElement>(), "tv", "Add Presentation Trail", addPresNode],
             [React.createRef<HTMLDivElement>(), "globe-asia", "Add Website", addWebNode],
             [React.createRef<HTMLDivElement>(), "bolt", "Add Button", addButtonDocument],
-            // [React.createRef<HTMLDivElement>(), "clone", "Add Docking Frame", addDockingNode],
+            [React.createRef<HTMLDivElement>(), "file", "Add Document Dragger", addDragboxNode],
             [React.createRef<HTMLDivElement>(), "cloud-upload-alt", "Import Directory", addImportCollectionNode], //remove at some point in favor of addImportCollectionNode
             //[React.createRef<HTMLDivElement>(), "play", "Add Youtube Searcher", addYoutubeSearcher],
-            [React.createRef<HTMLDivElement>(), "file", "Add Document Dragger", addDragboxNode]
         ];
         if (!ClientUtils.RELEASE) btns.unshift([React.createRef<HTMLDivElement>(), "cat", "Add Cat Image", addImageNode]);
 
-        const setWriteMode = (mode: DocServer.WriteMode) => {
-            console.log(DocServer.WriteMode[mode]);
-            const mode1 = mode;
-            const mode2 = mode === DocServer.WriteMode.Default ? mode : DocServer.WriteMode.Playground;
-            DocServer.setFieldWriteMode("x", mode1);
-            DocServer.setFieldWriteMode("y", mode1);
-            DocServer.setFieldWriteMode("width", mode1);
-            DocServer.setFieldWriteMode("height", mode1);
-
-            DocServer.setFieldWriteMode("panX", mode2);
-            DocServer.setFieldWriteMode("panY", mode2);
-            DocServer.setFieldWriteMode("scale", mode2);
-            DocServer.setFieldWriteMode("viewType", mode2);
-        };
         return < div id="add-nodes-menu" style={{ left: this.flyoutWidth + 20, bottom: 20 }} >
 
             <input type="checkbox" id="add-menu-toggle" ref={this.addMenuToggle} />
@@ -484,7 +468,6 @@ export class MainView extends React.Component {
             <div id="add-options-content">
                 <ul id="add-options-list">
                     <li key="search"><button className="add-button round-button" title="Search" onClick={this.toggleSearch}><FontAwesomeIcon icon="search" size="sm" /></button></li>
-                    <li key="presentation"><button className="add-button round-button" title="Show Current Presentation Trail" onClick={this.togglePresentationView}><FontAwesomeIcon icon="table" size="sm" /></button></li>
                     <li key="undo"><button className="add-button round-button" title="Undo" style={{ opacity: UndoManager.CanUndo() ? 1 : 0.5, transition: "0.4s ease all" }} onClick={() => UndoManager.Undo()}><FontAwesomeIcon icon="undo-alt" size="sm" /></button></li>
                     <li key="redo"><button className="add-button round-button" title="Redo" style={{ opacity: UndoManager.CanRedo() ? 1 : 0.5, transition: "0.4s ease all" }} onClick={() => UndoManager.Redo()}><FontAwesomeIcon icon="redo-alt" size="sm" /></button></li>
                     {btns.map(btn =>
@@ -493,13 +476,6 @@ export class MainView extends React.Component {
                                 <FontAwesomeIcon icon={btn[1]} size="sm" />
                             </button>
                         </div></li>)}
-                    <li key="undoTest"><button className="add-button round-button" title="Click if undo isn't working" onClick={() => UndoManager.TraceOpenBatches()}><FontAwesomeIcon icon="exclamation" size="sm" /></button></li>
-                    {ClientUtils.RELEASE ? [] : [
-                        <li key="test"><button className="add-button round-button" title="Default" onClick={() => setWriteMode(DocServer.WriteMode.Default)}><FontAwesomeIcon icon="exclamation" size="sm" /></button></li>,
-                        <li key="test1"><button className="add-button round-button" title="Playground" onClick={() => setWriteMode(DocServer.WriteMode.Playground)}><FontAwesomeIcon icon="exclamation" size="sm" /></button></li>,
-                        <li key="test2"><button className="add-button round-button" title="Live Playground" onClick={() => setWriteMode(DocServer.WriteMode.LivePlayground)}><FontAwesomeIcon icon="exclamation" size="sm" /></button></li>,
-                        <li key="test3"><button className="add-button round-button" title="Live Readonly" onClick={() => setWriteMode(DocServer.WriteMode.LiveReadonly)}><FontAwesomeIcon icon="exclamation" size="sm" /></button></li>
-                    ]}
                     <li key="color"><button className="add-button round-button" title="Select Color" style={{ zIndex: 1000 }} onClick={() => this.toggleColorPicker()}><div className="toolbar-color-button" style={{ backgroundColor: InkingControl.Instance.selectedColor }} >
                         <div className="toolbar-color-picker" onClick={this.onColorClick} style={this._colorPickerDisplay ? { color: "black", display: "block" } : { color: "black", display: "none" }}>
                             <SketchPicker color={InkingControl.Instance.selectedColor} onChange={InkingControl.Instance.switchColor} />
@@ -541,17 +517,6 @@ export class MainView extends React.Component {
         this.isSearchVisible = !this.isSearchVisible;
     }
 
-    togglePresentationView = () => {
-        if (CurrentUserUtils.UserDocument.curPresentation) {
-            let isOpen = DocumentManager.Instance.getDocumentView(CurrentUserUtils.UserDocument.curPresentation as Doc);
-            if (isOpen) {
-                CollectionDockingView.Instance.CloseRightSplit(CurrentUserUtils.UserDocument.curPresentation as Doc);
-            } else {
-                CollectionDockingView.Instance.AddRightSplit(CurrentUserUtils.UserDocument.curPresentation as Doc, undefined);
-            }
-        }
-    }
-
     private get dictationOverlay() {
         let display = this.dictationOverlayVisible;
         let success = this.dictationSuccess;
@@ -581,7 +546,7 @@ export class MainView extends React.Component {
         let next = () => PresBox.CurrentPresentation.next();
         let back = () => PresBox.CurrentPresentation.back();
         let startOrResetPres = () => PresBox.CurrentPresentation.startOrResetPres();
-        let closePresMode = action(() => {PresBox.CurrentPresentation.presMode = false; this.addDocTabFunc(PresBox.CurrentPresentation.props.Document)});
+        let closePresMode = action(() => { PresBox.CurrentPresentation.presMode = false; this.addDocTabFunc(PresBox.CurrentPresentation.props.Document) });
         return !PresBox.CurrentPresentation || !PresBox.CurrentPresentation.presMode ? (null) : <PresModeMenu next={next} back={back} presStatus={PresBox.CurrentPresentation.presStatus} startOrResetPres={startOrResetPres} closePresMode={closePresMode} > </PresModeMenu>
     }
 
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 6b9cd57b3..b6a14238e 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -254,7 +254,7 @@ class TreeView extends React.Component<TreeViewProps> {
         doc && Object.keys(doc).forEach(key => !(key in ids) && doc[key] !== ComputedField.undefined && (ids[key] = key));
 
         let rows: JSX.Element[] = [];
-        for (let key of Object.keys(ids).sort()) {
+        for (let key of Object.keys(ids).slice().sort()) {
             let contents = doc[key];
             let contentElement: JSX.Element[] | JSX.Element = [];
 
@@ -419,7 +419,7 @@ class TreeView extends React.Component<TreeViewProps> {
         }
 
         let descending = BoolCast(containingCollection.stackingHeadersSortDescending);
-        docs.sort(function (a, b): 1 | -1 {
+        docs.slice().sort(function (a, b): 1 | -1 {
             let descA = descending ? b : a;
             let descB = descending ? a : b;
             let first = descA[String(containingCollection.sectionFilter)];
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 7e1adaa19..3a8253720 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -108,9 +108,13 @@ export class CollectionView extends React.Component<FieldViewProps> {
             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: "Create Layout Instance", event: () => this.props.addDocTab && this.props.addDocTab(Doc.ApplyTemplate(this.props.Document)!, undefined, "onRight"), icon: "project-diagram" });
             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/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 4378a51fb..9e71094b0 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -573,21 +573,14 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         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" : "Make Background", event: this.makeBackground, icon: BoolCast(this.props.Document.lockedPosition) ? "unlock" : "lock" });
-        makes.push({ description: this.props.Document.isButton ? "Remove Button" : "Make Button", event: this.makeBtnClicked, icon: "concierge-bell" });
-        makes.push({ description: "Edit OnClick script", icon: "edit", event: () => ScriptBox.EditClickScript(this.props.Document, "onClick") });
+        makes.push({ description: this.props.Document.isBackground ? "Remove Background" : "Into Background", event: this.makeBackground, icon: BoolCast(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 script", icon: "edit", event: () => ScriptBox.EditClickScript(this.props.Document, "onClick") });
         makes.push({
-            description: "Make Portal", event: () => {
+            description: "Into Portal", event: () => {
                 let portal = Docs.Create.FreeformDocument([], { width: this.props.Document[WidthSym]() + 10, height: this.props.Document[HeightSym](), title: this.props.Document.title + ".portal" });
-                //Doc.GetProto(this.props.Document).subBulletDocs = new List<Doc>([portal]);
-                //summary.proto!.maximizeLocation = "inTab";  // or "inPlace", or "onRight"
-                //Doc.GetProto(this.props.Document).templates = new List<string>([Templates.Bullet.Layout]);
-                //let coll = Docs.Create.StackingDocument([this.props.Document, portal], { x: NumCast(this.props.Document.x), y: NumCast(this.props.Document.y), width: this.props.Document[WidthSym]() + 10, height: this.props.Document[HeightSym](), title: this.props.Document.title + ".cont" });
-                //this.props.addDocument && this.props.addDocument(coll);
-                //this.props.removeDocument && this.props.removeDocument(this.props.Document);
                 DocUtils.MakeLink(this.props.Document, portal, undefined, this.props.Document.title + ".portal");
                 this.makeBtnClicked();
-
             }, icon: "window-restore"
         });
         !existingMake && cm.addItem({ description: "Make...", subitems: makes, icon: "hand-point-right" });
@@ -684,6 +677,31 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         }
         runInAction(() => {
             cm.addItem({ description: "Share...", subitems: usersMenu, icon: "share" });
+            if (!ClientUtils.RELEASE) {
+                let setWriteMode = (mode: DocServer.WriteMode) => {
+                    console.log(DocServer.WriteMode[mode]);
+                    DocServer.AclsMode = mode;
+                    const mode1 = mode;
+                    const mode2 = mode === DocServer.WriteMode.Default ? mode : DocServer.WriteMode.Playground;
+                    DocServer.setFieldWriteMode("x", mode1);
+                    DocServer.setFieldWriteMode("y", mode1);
+                    DocServer.setFieldWriteMode("width", mode1);
+                    DocServer.setFieldWriteMode("height", mode1);
+
+                    DocServer.setFieldWriteMode("panX", mode2);
+                    DocServer.setFieldWriteMode("panY", mode2);
+                    DocServer.setFieldWriteMode("scale", mode2);
+                    DocServer.setFieldWriteMode("viewType", mode2);
+                };
+                let aclsMenu: ContextMenuProps[] = [];
+                aclsMenu.push({ description: "Default (write/read all)", event: () => setWriteMode(DocServer.WriteMode.Default), icon: DocServer.AclsMode === DocServer.WriteMode.Default ? "check" : "exclamation" });
+                aclsMenu.push({ description: "Playground (write own/no read)", event: () => setWriteMode(DocServer.WriteMode.Playground), icon: DocServer.AclsMode === DocServer.WriteMode.Playground ? "check" : "exclamation" });
+                aclsMenu.push({ description: "Live Playground (write own/read others)", event: () => setWriteMode(DocServer.WriteMode.LivePlayground), icon: DocServer.AclsMode === DocServer.WriteMode.LivePlayground ? "check" : "exclamation" });
+                aclsMenu.push({ description: "Live Readonly (no write/read others)", event: () => setWriteMode(DocServer.WriteMode.LiveReadonly), icon: DocServer.AclsMode === DocServer.WriteMode.LiveReadonly ? "check" : "exclamation" });
+                cm.addItem({ description: "Collaboration ACLs...", subitems: aclsMenu, icon: "share" });
+                cm.addItem({ description: "Undo Debug Test", event: () => UndoManager.TraceOpenBatches(), icon: "exclamation" });
+            }
+
             if (!this.topMost) {
                 // DocumentViews should stop propagation of this event
                 e.stopPropagation();
-- 
cgit v1.2.3-70-g09d2


From ffb4da00970f4146d7ce2c5022dabba193e763a3 Mon Sep 17 00:00:00 2001
From: monikahedman <monika_hedman@brown.edu>
Date: Mon, 19 Aug 2019 22:44:05 -0400
Subject: things won't highlight more than once

---
 src/client/views/nodes/DocumentView.tsx |  12 ++--
 src/client/views/nodes/LinkMenuItem.tsx | 106 ++++++++++++++++++++------------
 src/new_fields/Doc.ts                   |  67 ++++++++++++++++----
 3 files changed, 129 insertions(+), 56 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 6f5235c4a..900cd266e 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -747,18 +747,20 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         }
         let showTextTitle = showTitle && StrCast(this.layoutDoc.layout).startsWith("<FormattedTextBox") ? showTitle : undefined;
         let brushDegree = Doc.IsBrushedDegree(this.props.Document);
+        let fullDegree = Doc.isBrushedHighlightedDegree(this.props.Document);
+        // console.log(fullDegree)
         let borderRounding = StrCast(Doc.GetProto(this.props.Document).borderRounding);
-        let localScale = this.props.ScreenToLocalTransform().Scale * brushDegree;
+        let localScale = this.props.ScreenToLocalTransform().Scale * fullDegree;
         return (
             <div className={`documentView-node${this.topMost ? "-topmost" : ""}`}
                 ref={this._mainCont}
                 style={{
                     pointerEvents: this.layoutDoc.isBackground && !this.isSelected() ? "none" : "all",
                     color: foregroundColor,
-                    outlineColor: ["transparent", "maroon", "maroon"][brushDegree],
-                    outlineStyle: ["none", "dashed", "solid"][brushDegree],
-                    outlineWidth: brushDegree && !borderRounding ? `${localScale}px` : "0px",
-                    border: brushDegree && borderRounding ? `${["none", "dashed", "solid"][brushDegree]} ${["transparent", "maroon", "maroon"][brushDegree]} ${localScale}px` : undefined,
+                    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,
diff --git a/src/client/views/nodes/LinkMenuItem.tsx b/src/client/views/nodes/LinkMenuItem.tsx
index caae88943..c3d9d033f 100644
--- a/src/client/views/nodes/LinkMenuItem.tsx
+++ b/src/client/views/nodes/LinkMenuItem.tsx
@@ -32,45 +32,30 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
     private _drag = React.createRef<HTMLDivElement>();
     @observable private _showMore: boolean = false;
     @action toggleShowMore() { this._showMore = !this._showMore; }
+    @observable shouldUnhighlight: boolean = false;
 
-    @undoBatch
-    onFollowLink = async (e: React.PointerEvent): Promise<void> => {
-        e.stopPropagation();
-        e.persist();
-        let jumpToDoc = this.props.destinationDoc;
-        let pdfDoc = FieldValue(Cast(this.props.destinationDoc, Doc));
-        if (pdfDoc) {
-            jumpToDoc = pdfDoc;
-        }
-        let proto = Doc.GetProto(this.props.linkDoc);
-        let targetContext = await Cast(proto.targetContext, Doc);
-        let sourceContext = await Cast(proto.sourceContext, Doc);
-        let self = this;
+    componentDidMount = () => {
+        // document.addEventListener("pointerdown", this.unhighlight);
+    }
 
+    unhighlight = () => {
+        // if (this.shouldUnhighlight)
+        // Doc.UnhighlightAll();
+        Doc.UnHighlightDoc(this.props.destinationDoc);
+    }
 
-        let dockingFunc = (document: Doc) => { this.props.addDocTab(document, undefined, "inTab"); SelectionManager.DeselectAll(); };
-        if (e.ctrlKey) {
-            dockingFunc = (document: Doc) => CollectionDockingView.Instance.AddRightSplit(document, undefined);
-        }
+    @action
+    highlightDoc = () => {
+        // this.shouldUnhighlight = false;
+        document.removeEventListener("pointerdown", this.unhighlight);
 
-        if (this.props.destinationDoc === self.props.linkDoc.anchor2 && targetContext) {
-            DocumentManager.Instance.jumpToDocument(jumpToDoc, e.altKey, false, async document => dockingFunc(document), undefined, targetContext!);
-            console.log("1")
-        }
-        else if (this.props.destinationDoc === self.props.linkDoc.anchor1 && sourceContext) {
-            DocumentManager.Instance.jumpToDocument(jumpToDoc, e.altKey, false, document => dockingFunc(sourceContext!));
-            console.log("2")
-        }
-        else if (DocumentManager.Instance.getDocumentView(jumpToDoc)) {
-            DocumentManager.Instance.jumpToDocument(jumpToDoc, e.altKey, undefined, undefined, NumCast((this.props.destinationDoc === self.props.linkDoc.anchor2 ? self.props.linkDoc.anchor2Page : self.props.linkDoc.anchor1Page)));
-            console.log("3")
+        Doc.HighlightDoc(this.props.destinationDoc);
 
-        }
-        else {
-            DocumentManager.Instance.jumpToDocument(jumpToDoc, e.altKey, false, dockingFunc);
-            console.log("4")
+        window.setTimeout(() => {
+            // this.shouldUnhighlight = true;
+            document.addEventListener("pointerdown", this.unhighlight);
 
-        }
+        }, 3000);
     }
 
     // NOT DONE?
@@ -92,17 +77,18 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
     // this opens the linked doc in a right split, NOT in its collection
     @undoBatch
     openLinkRight = () => {
+        this.highlightDoc();
         let alias = Doc.MakeAlias(this.props.destinationDoc);
         CollectionDockingView.Instance.AddRightSplit(alias, undefined);
         SelectionManager.DeselectAll();
-
     }
 
-    // NOT DONE
+    // DONE
     // this is the standard "follow link" (jump to document)
     // taken from follow link
     @undoBatch
     jumpToLink = async (shouldZoom: boolean = false) => {
+        this.highlightDoc();
         let jumpToDoc = this.props.destinationDoc;
         let pdfDoc = FieldValue(Cast(this.props.destinationDoc, Doc));
         if (pdfDoc) {
@@ -113,7 +99,6 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
         let sourceContext = await Cast(proto.sourceContext, Doc);
         let self = this;
 
-
         let dockingFunc = (document: Doc) => { this.props.addDocTab(document, undefined, "inTab"); SelectionManager.DeselectAll(); };
 
         if (this.props.destinationDoc === self.props.linkDoc.anchor2 && targetContext) {
@@ -136,6 +121,7 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
     // this opens it full screen, do we need a separate full screen option?
     @undoBatch
     openLinkTab = () => {
+        this.highlightDoc();
         let fullScreenAlias = Doc.MakeAlias(this.props.destinationDoc);
         this.props.addDocTab(fullScreenAlias, undefined, "inTab");
         SelectionManager.DeselectAll();
@@ -146,12 +132,14 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
     // target = the document to center on
     @undoBatch
     openLinkColTab = ({ col, target }: { col: Doc, target: Doc }) => {
-
+        this.highlightDoc();
     }
 
     // this will open a link next to the source doc
     @undoBatch
     openLinkInPlace = () => {
+        this.highlightDoc();
+
         let alias = Doc.MakeAlias(this.props.destinationDoc);
         let y = this.props.sourceDoc.y;
         let x = this.props.sourceDoc.x;
@@ -160,7 +148,7 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
     }
 
     //set this to be the default link behavior, can be any of the above
-    private defaultLinkBehavior: any = this.openLinkInPlace;
+    private defaultLinkBehavior: any = this.openLinkRight;
 
     onEdit = (e: React.PointerEvent): void => {
         e.stopPropagation();
@@ -237,4 +225,44 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
             </div >
         );
     }
-}
\ No newline at end of file
+}
+
+    // @undoBatch
+    // onFollowLink = async (e: React.PointerEvent): Promise<void> => {
+    //     e.stopPropagation();
+    //     e.persist();
+    //     let jumpToDoc = this.props.destinationDoc;
+    //     let pdfDoc = FieldValue(Cast(this.props.destinationDoc, Doc));
+    //     if (pdfDoc) {
+    //         jumpToDoc = pdfDoc;
+    //     }
+    //     let proto = Doc.GetProto(this.props.linkDoc);
+    //     let targetContext = await Cast(proto.targetContext, Doc);
+    //     let sourceContext = await Cast(proto.sourceContext, Doc);
+    //     let self = this;
+
+
+    //     let dockingFunc = (document: Doc) => { this.props.addDocTab(document, undefined, "inTab"); SelectionManager.DeselectAll(); };
+    //     if (e.ctrlKey) {
+    //         dockingFunc = (document: Doc) => CollectionDockingView.Instance.AddRightSplit(document, undefined);
+    //     }
+
+    //     if (this.props.destinationDoc === self.props.linkDoc.anchor2 && targetContext) {
+    //         DocumentManager.Instance.jumpToDocument(jumpToDoc, e.altKey, false, async document => dockingFunc(document), undefined, targetContext!);
+    //         console.log("1")
+    //     }
+    //     else if (this.props.destinationDoc === self.props.linkDoc.anchor1 && sourceContext) {
+    //         DocumentManager.Instance.jumpToDocument(jumpToDoc, e.altKey, false, document => dockingFunc(sourceContext!));
+    //         console.log("2")
+    //     }
+    //     else if (DocumentManager.Instance.getDocumentView(jumpToDoc)) {
+    //         DocumentManager.Instance.jumpToDocument(jumpToDoc, e.altKey, undefined, undefined, NumCast((this.props.destinationDoc === self.props.linkDoc.anchor2 ? self.props.linkDoc.anchor2Page : self.props.linkDoc.anchor1Page)));
+    //         console.log("3")
+
+    //     }
+    //     else {
+    //         DocumentManager.Instance.jumpToDocument(jumpToDoc, e.altKey, false, dockingFunc);
+    //         console.log("4")
+
+    //     }
+    // }
\ No newline at end of file
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index d634cf57f..425d532c0 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -329,12 +329,12 @@ export namespace Doc {
 
     export function AddDocToList(target: Doc, key: string, doc: Doc, relativeTo?: Doc, before?: boolean, first?: boolean, allowDuplicates?: boolean) {
         if (target[key] === undefined) {
-            console.log("target key undefined");
+            // console.log("target key undefined");
             Doc.GetProto(target)[key] = new List<Doc>();
         }
         let list = Cast(target[key], listSpec(Doc));
         if (list) {
-            console.log("has list");
+            // console.log("has list");
             if (allowDuplicates !== true) {
                 let pind = list.reduce((l, d, i) => d instanceof Doc && Doc.AreProtosEqual(d, doc) ? i : l, -1);
                 if (pind !== -1) {
@@ -342,15 +342,15 @@ export namespace Doc {
                 }
             }
             if (first) {
-                console.log("is first");
+                // console.log("is first");
                 list.splice(0, 0, doc);
             }
             else {
-                console.log("not first");
+                // console.log("not first");
                 let ind = relativeTo ? list.indexOf(relativeTo) : -1;
                 if (ind === -1) list.push(doc);
                 else list.splice(before ? ind : ind + 1, 0, doc);
-                console.log("index", ind);
+                // console.log("index", ind);
             }
         }
         return true;
@@ -595,23 +595,66 @@ export namespace Doc {
         });
     }
 
+    export function isBrushedHighlightedDegree(doc: Doc) {
+        if (Doc.IsHighlighted(doc)) {
+            return 3;
+        }
+        else {
+            return Doc.IsBrushedDegree(doc);
+        }
+    }
+
     export class DocBrush {
         @observable BrushedDoc: ObservableMap<Doc, boolean> = new ObservableMap();
     }
-    const manager = new DocBrush();
+    const brushManager = new DocBrush();
     export function IsBrushed(doc: Doc) {
-        return manager.BrushedDoc.has(doc) || manager.BrushedDoc.has(Doc.GetDataDoc(doc));
+        return brushManager.BrushedDoc.has(doc) || brushManager.BrushedDoc.has(Doc.GetDataDoc(doc));
     }
     export function IsBrushedDegree(doc: Doc) {
-        return manager.BrushedDoc.has(Doc.GetDataDoc(doc)) ? 2 : manager.BrushedDoc.has(doc) ? 1 : 0;
+        return brushManager.BrushedDoc.has(Doc.GetDataDoc(doc)) ? 2 : brushManager.BrushedDoc.has(doc) ? 1 : 0;
     }
     export function BrushDoc(doc: Doc) {
-        manager.BrushedDoc.set(doc, true);
-        manager.BrushedDoc.set(Doc.GetDataDoc(doc), true);
+        brushManager.BrushedDoc.set(doc, true);
+        brushManager.BrushedDoc.set(Doc.GetDataDoc(doc), true);
     }
     export function UnBrushDoc(doc: Doc) {
-        manager.BrushedDoc.delete(doc);
-        manager.BrushedDoc.delete(Doc.GetDataDoc(doc));
+        brushManager.BrushedDoc.delete(doc);
+        brushManager.BrushedDoc.delete(Doc.GetDataDoc(doc));
+    }
+
+    export class HighlightBrush {
+        @observable HighlightedDoc: ObservableMap<Doc, boolean> = new ObservableMap();
+    }
+    const highlightManager = new HighlightBrush();
+    export function IsHighlighted(doc: Doc) {
+        // return highlightManager.HighlightedDoc.has(doc) || highlightManager.HighlightedDoc.has(Doc.GetDataDoc(doc));
+        return highlightManager.HighlightedDoc.get(doc) || highlightManager.HighlightedDoc.get(Doc.GetDataDoc(doc));
+    }
+    export function HighlightDoc(doc: Doc) {
+        console.log("is highlighting")
+        runInAction(() => {
+            highlightManager.HighlightedDoc.set(doc, true);
+            highlightManager.HighlightedDoc.set(Doc.GetDataDoc(doc), true);
+        });
+    }
+    export function UnHighlightDoc(doc: Doc) {
+        // highlightManager.HighlightedDoc.delete(doc);
+        // highlightManager.HighlightedDoc.delete(Doc.GetDataDoc(doc));
+        runInAction(() => {
+            highlightManager.HighlightedDoc.set(doc, false);
+            highlightManager.HighlightedDoc.set(Doc.GetDataDoc(doc), false);
+        })
+    }
+    export function UnhighlightAll() {
+        // highlightManager.HighlightedDoc.clear();
+        let docs = highlightManager.HighlightedDoc.keys();
+        let doc = docs.next();
+        while (docs.next !== null) {
+            Doc.UnHighlightDoc(doc.value);
+            doc = docs.next();
+        }
+
     }
 }
 Scripting.addGlobal(function renameAlias(doc: any, n: any) { return StrCast(doc.title).replace(/\([0-9]*\)/, "") + `(${n})`; });
-- 
cgit v1.2.3-70-g09d2


From 700dfc5add1ecd9c2b1ecafcdc593ff821b7a6a6 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Tue, 20 Aug 2019 17:20:27 -0400
Subject: UI enhancements and other backend fixes for templates.

---
 src/client/util/DragManager.ts                     |  3 +-
 src/client/views/MetadataEntryMenu.scss            | 12 +++++
 src/client/views/MetadataEntryMenu.tsx             | 52 +++++++++++---------
 .../views/collections/CollectionStackingView.tsx   |  3 +-
 .../CollectionStackingViewFieldColumn.tsx          |  9 ++--
 src/client/views/collections/CollectionSubView.tsx |  2 +-
 .../views/collections/CollectionTreeView.tsx       |  3 ++
 .../views/collections/CollectionViewChromes.scss   |  1 -
 .../views/collections/CollectionViewChromes.tsx    | 57 +++++++++++-----------
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 30 ++++++------
 src/client/views/nodes/DocumentContentsView.tsx    |  2 +-
 src/client/views/nodes/DocumentView.tsx            | 25 +++++++---
 src/client/views/nodes/FormattedTextBox.tsx        | 12 ++---
 src/client/views/nodes/ImageBox.tsx                |  7 +--
 src/new_fields/Doc.ts                              | 11 +++--
 15 files changed, 131 insertions(+), 98 deletions(-)

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

diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 0b6d9b5e5..894b366ef 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -9,8 +9,6 @@ import { DocumentManager } from "./DocumentManager";
 import { LinkManager } from "./LinkManager";
 import { SelectionManager } from "./SelectionManager";
 import { SchemaHeaderField } from "../../new_fields/SchemaHeaderField";
-import { DocumentDecorations } from "../views/DocumentDecorations";
-import { NumberLiteralType } from "typescript";
 
 export type dropActionType = "alias" | "copy" | undefined;
 export function SetupDrag(
@@ -211,6 +209,7 @@ export namespace DragManager {
         dropAction: dropActionType;
         userDropAction: dropActionType;
         moveDocument?: MoveFunction;
+        applyAsTemplate?: boolean;
         [id: string]: any;
     }
 
diff --git a/src/client/views/MetadataEntryMenu.scss b/src/client/views/MetadataEntryMenu.scss
index bcfc9a82d..e28c4d0e0 100644
--- a/src/client/views/MetadataEntryMenu.scss
+++ b/src/client/views/MetadataEntryMenu.scss
@@ -1,8 +1,20 @@
 .metadataEntry-outerDiv {
     display: flex;
+    flex-direction: column;
     width: 300px;
 }
 
+.metadataEntry-keys {
+    max-height: 80;
+    overflow-y: auto; 
+    display: flex;
+    flex-direction: column;
+}
+.metadataEntry-inputArea {
+    display:flex;
+    flex-direction: row;
+}
+
 .react-autosuggest__container {
     position: relative;
 }
diff --git a/src/client/views/MetadataEntryMenu.tsx b/src/client/views/MetadataEntryMenu.tsx
index 36c240dd8..4a45eede9 100644
--- a/src/client/views/MetadataEntryMenu.tsx
+++ b/src/client/views/MetadataEntryMenu.tsx
@@ -1,11 +1,12 @@
 import * as React from 'react';
 import "./MetadataEntryMenu.scss";
 import { observer } from 'mobx-react';
-import { observable, action, runInAction, trace } from 'mobx';
+import { observable, action, runInAction, trace, computed, IReactionDisposer, reaction } from 'mobx';
 import { KeyValueBox } from './nodes/KeyValueBox';
 import { Doc, Field } from '../../new_fields/Doc';
 import * as Autosuggest from 'react-autosuggest';
 import { undoBatch } from '../util/UndoManager';
+import { emptyFunction } from '../../Utils';
 
 export type DocLike = Doc | Doc[] | Promise<Doc> | Promise<Doc[]>;
 export interface MetadataEntryProps {
@@ -18,7 +19,8 @@ export interface MetadataEntryProps {
 export class MetadataEntryMenu extends React.Component<MetadataEntryProps>{
     @observable private _currentKey: string = "";
     @observable private _currentValue: string = "";
-    @observable private suggestions: string[] = [];
+    @observable _allSuggestions: string[] = [];
+    _suggestionDispser: IReactionDisposer | undefined;
     private userModified = false;
 
     private autosuggestRef = React.createRef<Autosuggest>();
@@ -140,35 +142,39 @@ export class MetadataEntryMenu extends React.Component<MetadataEntryProps>{
     getSuggestionValue = (suggestion: string) => suggestion;
 
     renderSuggestion = (suggestion: string) => {
-        return <p>{suggestion}</p>;
+        return (null);
     }
+    componentDidMount() {
 
-    onSuggestionFetch = async ({ value }: { value: string }) => {
-        const sugg = await this.getKeySuggestions(value);
-        runInAction(() => {
-            this.suggestions = sugg;
-        });
+        this._suggestionDispser = reaction(() => this._currentKey,
+            () => this.getKeySuggestions(this._currentKey).then(action((s: string[]) => this._allSuggestions = s)),
+            { fireImmediately: true });
     }
-
-    @action
-    onSuggestionClear = () => {
-        this.suggestions = [];
+    componentWillUnmount() {
+        this._suggestionDispser && this._suggestionDispser();
     }
 
     render() {
         return (
             <div className="metadataEntry-outerDiv">
-                Key:
-                <Autosuggest inputProps={{ value: this._currentKey, onChange: this.onKeyChange }}
-                    getSuggestionValue={this.getSuggestionValue}
-                    suggestions={this.suggestions}
-                    alwaysRenderSuggestions
-                    renderSuggestion={this.renderSuggestion}
-                    onSuggestionsFetchRequested={this.onSuggestionFetch}
-                    onSuggestionsClearRequested={this.onSuggestionClear}
-                    ref={this.autosuggestRef} />
-                Value:
-                <input className="metadataEntry-input" value={this._currentValue} onChange={this.onValueChange} onKeyDown={this.onValueKeyDown} />
+                <div className="metadataEntry-inputArea">
+                    Key:
+                    <Autosuggest inputProps={{ value: this._currentKey, onChange: this.onKeyChange }}
+                        getSuggestionValue={this.getSuggestionValue}
+                        suggestions={[]}
+                        alwaysRenderSuggestions={false}
+                        renderSuggestion={this.renderSuggestion}
+                        onSuggestionsFetchRequested={emptyFunction}
+                        onSuggestionsClearRequested={emptyFunction}
+                        ref={this.autosuggestRef} />
+                    Value:
+                    <input className="metadataEntry-input" value={this._currentValue} onChange={this.onValueChange} onKeyDown={this.onValueKeyDown} />
+                </div>
+                <div className="metadataEntry-keys" >
+                    <ul>
+                        {this._allSuggestions.map(s => <li key={s} onClick={action(() => { this._currentKey = s; this.previewValue(); })} >{s}</li>)}
+                    </ul>
+                </div>
             </div>
         );
     }
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 1f7ef0689..c74c60d8f 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -160,7 +160,8 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
             previewScript={undefined}>
         </CollectionSchemaPreview>;
     }
-    getDocHeight(d: Doc) {
+    getDocHeight(d?: Doc) {
+        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/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index 74c7ef305..cc8476548 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -78,11 +78,14 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
         let parent = this.props.parent;
         parent._docXfs.length = 0;
         return docs.map((d, i) => {
-            let pair = Doc.GetLayoutDataDocPair(parent.props.Document, parent.props.DataDoc, parent.props.fieldKey, d);
+            const pair = Doc.GetLayoutDataDocPair(parent.props.Document, parent.props.DataDoc, parent.props.fieldKey, d);
+            if (!pair.layout || pair.data instanceof Promise) {
+                return (null);
+            }
             let width = () => Math.min(d.nativeWidth && !d.ignoreAspect && !parent.props.Document.fillColumn ? d[WidthSym]() : Number.MAX_VALUE, parent.columnWidth / parent.numGroupColumns);
-            let height = () => parent.getDocHeight(pair.layout);
+            let height = () => parent.getDocHeight(pair!.layout);
             let dref = React.createRef<HTMLDivElement>();
-            let dxf = () => this.getDocTransform(pair.layout, dref.current!);
+            let dxf = () => this.getDocTransform(pair!.layout, dref.current!);
             this.props.parent._docXfs.push({ dxf: dxf, width: width, height: height });
             let rowSpan = Math.ceil((height() + parent.gridGap) / parent.gridGap);
             let style = parent.isStackingView ? { width: width(), margin: "auto", marginTop: i === 0 ? 0 : parent.gridGap, height: height() } : { gridRowEnd: `span ${rowSpan}` };
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index cfe3fefb3..818e76619 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -113,7 +113,7 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
         @undoBatch
         @action
         protected drop(e: Event, de: DragManager.DropEvent): boolean {
-            if (de.data instanceof DragManager.DocumentDragData) {
+            if (de.data instanceof DragManager.DocumentDragData && !de.data.applyAsTemplate) {
                 if (de.mods === "AltKey" && de.data.draggedDocuments.length) {
                     this.childDocs.map(doc =>
                         Doc.ApplyTemplateTo(de.data.draggedDocuments[0], doc, undefined)
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index b6a14238e..ebd385743 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -443,6 +443,9 @@ class TreeView extends React.Component<TreeViewProps> {
         let rowWidth = () => panelWidth() - 20;
         return docs.map((child, i) => {
             let pair = Doc.GetLayoutDataDocPair(containingCollection, dataDoc, key, child);
+            if (!pair.layout || pair.data instanceof Promise) {
+                return (null);
+            }
 
             let indent = i === 0 ? undefined : () => {
                 if (StrCast(docs[i - 1].layout).indexOf("CollectionView") !== -1) {
diff --git a/src/client/views/collections/CollectionViewChromes.scss b/src/client/views/collections/CollectionViewChromes.scss
index 595f079d1..f39bd877a 100644
--- a/src/client/views/collections/CollectionViewChromes.scss
+++ b/src/client/views/collections/CollectionViewChromes.scss
@@ -7,7 +7,6 @@
     z-index: 9001;
     transition: top .5s;
     background: lightgrey;
-    padding: 10px;
 
     .collectionViewChrome {
         display: grid;
diff --git a/src/client/views/collections/CollectionViewChromes.tsx b/src/client/views/collections/CollectionViewChromes.tsx
index b92bcc7cb..25b152d4e 100644
--- a/src/client/views/collections/CollectionViewChromes.tsx
+++ b/src/client/views/collections/CollectionViewChromes.tsx
@@ -1,30 +1,25 @@
-import * as React from "react";
-import { CollectionView } from "./CollectionView";
-import "./CollectionViewChromes.scss";
-import { CollectionViewType } from "./CollectionBaseView";
-import { undoBatch } from "../../util/UndoManager";
-import { action, observable, runInAction, computed, IObservable, IObservableValue, reaction, autorun } from "mobx";
-import { observer } from "mobx-react";
-import { Doc, DocListCast, FieldResult } from "../../../new_fields/Doc";
-import { DocLike } from "../MetadataEntryMenu";
-import * as Autosuggest from 'react-autosuggest';
-import { EditableView } from "../EditableView";
-import { StrCast, NumCast, BoolCast, Cast } from "../../../new_fields/Types";
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { action, computed, observable, runInAction } from "mobx";
+import { observer } from "mobx-react";
+import * as React from "react";
+import { Doc, DocListCast } from "../../../new_fields/Doc";
+import { Id } from "../../../new_fields/FieldSymbols";
+import { List } from "../../../new_fields/List";
+import { listSpec } from "../../../new_fields/Schema";
+import { ScriptField } from "../../../new_fields/ScriptField";
+import { BoolCast, Cast, NumCast, StrCast } from "../../../new_fields/Types";
 import { Utils } from "../../../Utils";
-import KeyRestrictionRow from "./KeyRestrictionRow";
+import { DragManager } from "../../util/DragManager";
 import { CompileScript } from "../../util/Scripting";
-import { ScriptField } from "../../../new_fields/ScriptField";
-import { CollectionSchemaView } from "./CollectionSchemaView";
+import { undoBatch } from "../../util/UndoManager";
+import { EditableView } from "../EditableView";
 import { COLLECTION_BORDER_WIDTH } from "../globalCssVariables.scss";
-import { listSpec } from "../../../new_fields/Schema";
-import { List } from "../../../new_fields/List";
-import { Id } from "../../../new_fields/FieldSymbols";
-import { threadId } from "worker_threads";
-import { DragManager } from "../../util/DragManager";
+import { DocLike } from "../MetadataEntryMenu";
+import { CollectionViewType } from "./CollectionBaseView";
+import { CollectionView } from "./CollectionView";
+import "./CollectionViewChromes.scss";
+import KeyRestrictionRow from "./KeyRestrictionRow";
 const datepicker = require('js-datepicker');
-import * as $ from 'jquery';
-import { firebasedynamiclinks } from "googleapis/build/src/apis/firebasedynamiclinks";
 
 interface CollectionViewChromeProps {
     CollectionView: CollectionView;
@@ -51,7 +46,6 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
     @computed private get filterValue() { return Cast(this.props.CollectionView.props.Document.viewSpecScript, ScriptField); }
 
     private _picker: any;
-    private _datePickerElGuid = Utils.GenerateGuid();
 
     getFilters = (script: string) => {
         let re: any = /(!)?\(\(\(doc\.(\w+)\s+&&\s+\(doc\.\w+\s+as\s+\w+\)\.includes\(\"(\w+)\"\)/g;
@@ -91,11 +85,6 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
     }
 
     componentDidMount = () => {
-        setTimeout(() => this._picker = datepicker("#" + this._datePickerElGuid, {
-            disabler: (date: Date) => date > new Date(),
-            onSelect: (instance: any, date: Date) => runInAction(() => this._dateValue = date),
-            dateSelected: new Date()
-        }), 1000);
 
         let fields: Filter[] = [];
         if (this.filterValue) {
@@ -306,6 +295,15 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
         return true;
     }
 
+    datePickerRef = (node: HTMLInputElement) => {
+        if (node) {
+            this._picker = datepicker("#" + node.id, {
+                disabler: (date: Date) => date > new Date(),
+                onSelect: (instance: any, date: Date) => runInAction(() => this._dateValue = date),
+                dateSelected: new Date()
+            });
+        }
+    }
     render() {
         let collapsed = this.props.CollectionView.props.Document.chromeStatus !== "enabled";
         return (
@@ -366,7 +364,8 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
                                         <option value="1y">1 year of</option>
                                     </select>
                                     <input className="collectionViewBaseChrome-viewSpecsMenu-rowRight"
-                                        id={this._datePickerElGuid}
+                                        id={Utils.GenerateGuid()}
+                                        ref={this.datePickerRef}
                                         value={this._dateValue instanceof Date ? this._dateValue.toLocaleDateString() : this._dateValue}
                                         onChange={(e) => runInAction(() => this._dateValue = e.target.value)}
                                         onPointerDown={this.openDatePicker}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index d69ece421..3be6aa3d3 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -622,20 +622,19 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
 
     getScale = () => this.Document.scale ? this.Document.scale : 1;
 
-    getChildDocumentViewProps(childDocLayout: Doc): DocumentViewProps {
-        let pair = Doc.GetLayoutDataDocPair(this.props.Document, this.props.DataDoc, this.props.fieldKey, childDocLayout);
+    getChildDocumentViewProps(childLayout: Doc, childData?: Doc): DocumentViewProps {
         return {
-            DataDoc: pair.data,
-            Document: pair.layout,
+            DataDoc: childData,
+            Document: childLayout,
             addDocument: this.props.addDocument,
             removeDocument: this.props.removeDocument,
             moveDocument: this.props.moveDocument,
             onClick: this.props.onClick,
-            ScreenToLocalTransform: pair.layout.z ? this.getTransformOverlay : this.getTransform,
+            ScreenToLocalTransform: childLayout.z ? this.getTransformOverlay : this.getTransform,
             renderDepth: this.props.renderDepth + 1,
-            selectOnLoad: pair.layout[Id] === this._selectOnLoaded,
-            PanelWidth: pair.layout[WidthSym],
-            PanelHeight: pair.layout[HeightSym],
+            selectOnLoad: childLayout[Id] === this._selectOnLoaded,
+            PanelWidth: childLayout[WidthSym],
+            PanelHeight: childLayout[HeightSym],
             ContentScaling: returnOne,
             ContainingCollectionView: this.props.CollectionView,
             focus: this.focusDocument,
@@ -745,12 +744,15 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
                     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") };
                     state = pos.state === undefined ? state : pos.state;
-                    prev.push({
-                        ele: <CollectionFreeFormDocumentView key={doc[Id]}
-                            x={script ? pos.x : undefined} y={script ? pos.y : undefined}
-                            width={script ? pos.width : undefined} height={script ? pos.height : undefined} {...this.getChildDocumentViewProps(doc)} />,
-                        bounds: (pos.x !== undefined && pos.y !== undefined) ? { x: pos.x, y: pos.y, z: pos.z, width: NumCast(pos.width), height: NumCast(pos.height) } : undefined
-                    });
+                    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]}
+                                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)} />,
+                            bounds: (pos.x !== undefined && pos.y !== undefined) ? { x: pos.x, y: pos.y, z: pos.z, width: NumCast(pos.width), height: NumCast(pos.height) } : undefined
+                        });
+                    }
                 }
             }
             return prev;
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index b901bdcfb..d77662355 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -68,7 +68,7 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
     }
 
     get dataDoc() {
-        if (this.props.DataDoc === undefined && this.props.Document.layout instanceof Doc) {
+        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 template 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.
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 9e71094b0..fd53262d7 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -139,6 +139,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     private _lastTap: number = 0;
     private _doubleTap = false;
     private _hitExpander = false;
+    private _hitTemplateDrag = false;
     private _mainCont = React.createRef<HTMLDivElement>();
     private _dropDisposer?: DragManager.DragDropDisposer;
     _animateToIconDisposer?: IReactionDisposer;
@@ -228,7 +229,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     }
 
     get dataDoc() {
-        if (this.props.DataDoc === undefined && this.props.Document.layout instanceof Doc) {
+        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.
@@ -236,7 +237,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         }
         return this.props.DataDoc !== this.props.Document ? this.props.DataDoc : undefined;
     }
-    startDragging(x: number, y: number, dropAction: dropActionType, dragSubBullets: boolean) {
+    startDragging(x: number, y: number, dropAction: dropActionType, dragSubBullets: boolean, 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) : [])];
@@ -247,6 +248,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             dragData.xOffset = xoff;
             dragData.yOffset = yoff;
             dragData.moveDocument = this.props.moveDocument;
+            dragData.applyAsTemplate = applyAsTemplate;
             DragManager.StartDocumentDrag([this._mainCont.current], dragData, x, y, {
                 handlers: {
                     dragComplete: action(emptyFunction)
@@ -377,15 +379,19 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             }
         }
     }
+
+
     onPointerDown = (e: React.PointerEvent): void => {
         if (e.nativeEvent.cancelBubble) return;
         this._downX = e.clientX;
         this._downY = e.clientY;
         this._hitExpander = DocListCast(this.props.Document.subBulletDocs).length > 0;
-        // if (e.shiftKey && e.buttons === 1 && CollectionDockingView.Instance) {
-        // CollectionDockingView.Instance.StartOtherDrag(e, [Doc.MakeAlias(this.props.Document)], [this.dataDoc]);
-        // e.stopPropagation();
-        // } else {
+        this._hitTemplateDrag = false;
+        for (let element = (e.target as any); element && !this._hitTemplateDrag; element = element.parentElement) {
+            if (element.className && element.className.toString() === "collectionViewBaseChrome-collapse") {
+                this._hitTemplateDrag = true;
+            }
+        }
         if (this.active) e.stopPropagation(); // events stop at the lowest document that is active.  
         document.removeEventListener("pointermove", this.onPointerMove);
         document.addEventListener("pointermove", this.onPointerMove);
@@ -399,7 +405,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.startDragging(this._downX, this._downY, e.ctrlKey || e.altKey ? "alias" : undefined, this._hitExpander, 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
@@ -457,6 +463,10 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
 
             DocUtils.MakeLink(annotationDoc, targetDoc, this.props.ContainingCollectionView!.props.Document, `Link from ${StrCast(annotationDoc.title)}`);
         }
+        if (de.data instanceof DragManager.DocumentDragData && de.data.applyAsTemplate) {
+            Doc.ApplyTemplateTo(de.data.draggedDocuments[0], this.props.Document, this.props.DataDoc);
+            e.stopPropagation();
+        }
         if (de.data instanceof DragManager.LinkDragData) {
             let sourceDoc = de.data.linkSourceDocument;
             let destDoc = this.props.Document;
@@ -679,7 +689,6 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             cm.addItem({ description: "Share...", subitems: usersMenu, icon: "share" });
             if (!ClientUtils.RELEASE) {
                 let setWriteMode = (mode: DocServer.WriteMode) => {
-                    console.log(DocServer.WriteMode[mode]);
                     DocServer.AclsMode = mode;
                     const mode1 = mode;
                     const mode2 = mode === DocServer.WriteMode.Default ? mode : DocServer.WriteMode.Playground;
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index f7890e5a6..0e347ca67 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -167,7 +167,6 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
             FormattedTextBox._toolTipTextMenu && (FormattedTextBox._toolTipTextMenu.HackToFixTextSelectionGlitch = false);
             if (state.selection.empty && FormattedTextBox._toolTipTextMenu) {
                 const marks = tx.storedMarks;
-                console.log(marks);
                 if (marks) { FormattedTextBox._toolTipTextMenu.mark_key_pressed(marks); }
             }
             this._applyingChange = true;
@@ -296,10 +295,10 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
                 this._editorView.updateState(EditorState.fromJSON(config, JSON.parse(field2)))
         );
 
-        this.props.isOverlay && (this._heightReactionDisposer = reaction(
+        this._heightReactionDisposer = reaction(
             () => this.props.Document[WidthSym](),
             () => this.tryUpdateHeight()
-        ));
+        );
 
         this._textReactionDisposer = reaction(
             () => this.extensionDoc,
@@ -448,8 +447,8 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
         if (this.props.selectOnLoad) {
             if (!this.props.isOverlay) this.props.select(false);
             else this._editorView!.focus();
-            this.tryUpdateHeight();
         }
+        this.tryUpdateHeight();
     }
 
     componentWillUnmount() {
@@ -610,9 +609,10 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
 
     @action
     tryUpdateHeight() {
-        if (this.props.Document.autoHeight && this.props.isOverlay) {
+        if (this.props.Document.autoHeight && this._ref.current!.scrollHeight !== 0) {
+            console.log("DT = " + this.props.Document.title + " " + this._ref.current!.clientHeight + " " + this._ref.current!.scrollHeight + " " + this._ref.current!.textContent)
             let xf = this._ref.current!.getBoundingClientRect();
-            let scrBounds = this.props.ScreenToLocalTransform().transformBounds(0, 0, xf.width, xf.height);
+            let scrBounds = this.props.ScreenToLocalTransform().transformBounds(0, 0, xf.width, this._ref.current!.textContent === "" ? 35 : this._ref.current!.scrollHeight);
             let nh = this.props.Document.isTemplate ? 0 : NumCast(this.dataDoc.nativeHeight, 0);
             let dh = NumCast(this.props.Document.height, 0);
             let sh = scrBounds.height;
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 708e00576..6fc94a140 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -89,10 +89,7 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
     drop = (e: Event, de: DragManager.DropEvent) => {
         if (de.data instanceof DragManager.DocumentDragData) {
             de.data.droppedDocuments.forEach(action((drop: Doc) => {
-                if (de.mods === "CtrlKey") {
-                    Doc.ApplyTemplateTo(drop, this.props.Document, this.props.DataDoc);
-                    e.stopPropagation();
-                } else if (de.mods === "AltKey" && /*this.dataDoc !== this.props.Document &&*/ drop.data instanceof ImageField) {
+                if (de.mods === "AltKey" && /*this.dataDoc !== this.props.Document &&*/ drop.data instanceof ImageField) {
                     Doc.GetProto(this.dataDoc)[this.props.fieldKey] = new ImageField(drop.data.url);
                     e.stopPropagation();
                 } else if (de.mods === "MetaKey") {
@@ -321,7 +318,7 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
                 let rotation = NumCast(this.dataDoc.rotation) % 180;
                 let realsize = rotation === 90 || rotation === 270 ? { height: size.width, width: size.height } : size;
                 let aspect = realsize.height / realsize.width;
-                if (Math.abs(NumCast(layoutdoc.height) - realsize.height) > 1 || Math.abs(NumCast(layoutdoc.width) - realsize.width) > 1) {
+                if (layoutdoc.nativeHeight !== 0 && layoutdoc.nativeWidth !== 0 && (Math.abs(NumCast(layoutdoc.height) - realsize.height) > 1 || Math.abs(NumCast(layoutdoc.width) - realsize.width) > 1)) {
                     setTimeout(action(() => {
                         layoutdoc.height = layoutdoc[WidthSym]() * aspect;
                         layoutdoc.nativeHeight = realsize.height;
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index 31f1f7a12..ca05dfa45 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -446,20 +446,23 @@ export namespace Doc {
         if (expandedTemplateLayout instanceof Doc) {
             return expandedTemplateLayout;
         }
+        if (expandedTemplateLayout instanceof Promise) {
+            return undefined;
+        }
         let expandedLayoutFieldKey = "Layout[" + templateLayoutDoc[Id] + "]";
         expandedTemplateLayout = dataDoc[expandedLayoutFieldKey];
         if (expandedTemplateLayout instanceof Doc) {
             return expandedTemplateLayout;
         }
         if (expandedTemplateLayout === undefined) {
-            setTimeout(() =>
-                dataDoc[expandedLayoutFieldKey] = Doc.MakeDelegate(templateLayoutDoc, undefined, "[" + templateLayoutDoc.title + "]"), 0);
+            setTimeout(() => dataDoc[expandedLayoutFieldKey] === undefined &&
+                (dataDoc[expandedLayoutFieldKey] = Doc.MakeDelegate(templateLayoutDoc, undefined, "[" + templateLayoutDoc.title + "]")), 0);
         }
-        return templateLayoutDoc; // use the templateLayout when it's not a template or the expandedTemplate is pending.
+        return undefined; // use the templateLayout when it's not a template or the expandedTemplate is pending.
     }
 
     export function GetLayoutDataDocPair(doc: Doc, dataDoc: Doc | undefined, fieldKey: string, childDocLayout: Doc) {
-        let layoutDoc = childDocLayout;
+        let layoutDoc: Doc | undefined = childDocLayout;
         let resolvedDataDoc = !doc.isTemplate && dataDoc !== doc && dataDoc ? Doc.GetDataDoc(dataDoc) : undefined;
         if (resolvedDataDoc && Doc.WillExpandTemplateLayout(childDocLayout, resolvedDataDoc)) {
             Doc.UpdateDocumentExtensionForField(resolvedDataDoc, fieldKey);
-- 
cgit v1.2.3-70-g09d2


From 5bd664e113434ec08fd56a34bb8f6a3e2ce136e8 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Wed, 21 Aug 2019 17:25:13 -0400
Subject: fixed upload of docs zip

---
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 46 ++++++++++++++++++----
 src/client/views/nodes/DocumentView.tsx            | 30 --------------
 src/server/index.ts                                | 22 ++++++-----
 3 files changed, 51 insertions(+), 47 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 3be6aa3d3..cbc123c61 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1,17 +1,18 @@
 import { library } from "@fortawesome/fontawesome-svg-core";
 import { faEye } from "@fortawesome/free-regular-svg-icons";
-import { faCompass, faCompressArrowsAlt, faExpandArrowsAlt, faPaintBrush, faTable, faUpload, faChalkboard, faBraille } from "@fortawesome/free-solid-svg-icons";
-import { action, computed, observable, IReactionDisposer, reaction } from "mobx";
+import { faBraille, faChalkboard, faCompass, faCompressArrowsAlt, faExpandArrowsAlt, faPaintBrush, faTable, faUpload } from "@fortawesome/free-solid-svg-icons";
+import { action, computed, IReactionDisposer, observable, reaction } from "mobx";
 import { observer } from "mobx-react";
-import { Doc, DocListCastAsync, HeightSym, WidthSym, DocListCast, FieldResult, Field, Opt } from "../../../../new_fields/Doc";
+import { Doc, DocListCastAsync, Field, FieldResult, HeightSym, Opt, WidthSym } from "../../../../new_fields/Doc";
 import { Id } from "../../../../new_fields/FieldSymbols";
 import { InkField, StrokeData } from "../../../../new_fields/InkField";
 import { createSchema, makeInterface } from "../../../../new_fields/Schema";
 import { ScriptField } from "../../../../new_fields/ScriptField";
 import { BoolCast, Cast, FieldValue, NumCast, StrCast } from "../../../../new_fields/Types";
-import { emptyFunction, returnOne, Utils, returnFalse, returnEmptyString } from "../../../../Utils";
+import { emptyFunction, returnEmptyString, returnOne, Utils } from "../../../../Utils";
 import { CognitiveServices } from "../../../cognitive_services/CognitiveServices";
-import { DocServer } from "../../../DocServer";
+import { Docs } from "../../../documents/Documents";
+import { DocumentType } from "../../../documents/DocumentTypes";
 import { DocumentManager } from "../../../util/DocumentManager";
 import { DragManager } from "../../../util/DragManager";
 import { HistoryUtil } from "../../../util/History";
@@ -29,15 +30,14 @@ import { DocumentViewProps, positionSchema } from "../../nodes/DocumentView";
 import { pageSchema } from "../../nodes/ImageBox";
 import { OverlayElementOptions, OverlayView } from "../../OverlayView";
 import PDFMenu from "../../pdf/PDFMenu";
-import { CollectionSubView } from "../CollectionSubView";
 import { ScriptBox } from "../../ScriptBox";
+import { CollectionSubView } from "../CollectionSubView";
 import { CollectionFreeFormLinksView } from "./CollectionFreeFormLinksView";
 import { CollectionFreeFormRemoteCursors } from "./CollectionFreeFormRemoteCursors";
 import "./CollectionFreeFormView.scss";
 import { MarqueeView } from "./MarqueeView";
 import React = require("react");
-import { Docs } from "../../../documents/Documents";
-import { DocumentType } from "../../../documents/DocumentTypes";
+import { DocServer } from "../../../DocServer";
 
 library.add(faEye as any, faTable, faPaintBrush, faExpandArrowsAlt, faCompressArrowsAlt, faCompass, faUpload, faBraille, faChalkboard);
 
@@ -841,6 +841,36 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
         let analyzers: ContextMenuProps[] = existingAnalyze && "subitems" in existingAnalyze ? existingAnalyze.subitems : [];
         analyzers.push({ description: "Analyze Strokes", event: this.analyzeStrokes, icon: "paint-brush" });
         !existingAnalyze && ContextMenu.Instance.addItem({ description: "Analyzers...", subitems: analyzers, icon: "hand-point-right" });
+        ContextMenu.Instance.addItem({
+            description: "Import document", icon: "upload", event: () => {
+                const input = document.createElement("input");
+                input.type = "file";
+                input.accept = ".zip";
+                input.onchange = async _e => {
+                    const files = input.files;
+                    if (!files) return;
+                    const file = files[0];
+                    let formData = new FormData();
+                    formData.append('file', file);
+                    formData.append('remap', "true");
+                    const upload = Utils.prepend("/uploadDoc");
+                    const response = await fetch(upload, { method: "POST", body: formData });
+                    const json = await response.json();
+                    if (json === "error") {
+                        return;
+                    }
+                    const doc = await DocServer.GetRefField(json);
+                    if (!doc || !(doc instanceof Doc)) {
+                        return;
+                    }
+                    const [x, y] = this.props.ScreenToLocalTransform().transformPoint(e.pageX, e.pageY);
+                    doc.x = x, doc.y = y;
+                    this.props.addDocument &&
+                        this.props.addDocument(doc, false);
+                };
+                input.click();
+            }
+        });
     }
 
 
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index fd53262d7..8dadf2bef 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -40,7 +40,6 @@ import { DocumentContentsView } from "./DocumentContentsView";
 import "./DocumentView.scss";
 import { FormattedTextBox } from './FormattedTextBox';
 import React = require("react");
-import { PresBox } from './PresBox';
 const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this?
 
 library.add(fa.faTrash);
@@ -629,35 +628,6 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             }
         });
 
-        cm.addItem({
-            description: "Import document", icon: "upload", event: () => {
-                const input = document.createElement("input");
-                input.type = "file";
-                input.accept = ".zip";
-                input.onchange = async _e => {
-                    const files = input.files;
-                    if (!files) return;
-                    const file = files[0];
-                    let formData = new FormData();
-                    formData.append('file', file);
-                    formData.append('remap', "true");
-                    const upload = Utils.prepend("/uploadDoc");
-                    const response = await fetch(upload, { method: "POST", body: formData });
-                    const json = await response.json();
-                    if (json === "error") {
-                        return;
-                    }
-                    const doc = await DocServer.GetRefField(json);
-                    if (!doc || !(doc instanceof Doc)) {
-                        return;
-                    }
-                    const [x, y] = this.props.ScreenToLocalTransform().transformPoint(e.pageX, e.pageY);
-                    doc.x = x, doc.y = y;
-                    this.props.addDocument && this.props.addDocument(doc, false);
-                };
-                input.click();
-            }
-        });
         cm.addItem({ description: "Delete", event: this.deleteClicked, icon: "trash" });
         type User = { email: string, userDocumentId: string };
         let usersMenu: ContextMenuProps[] = [];
diff --git a/src/server/index.ts b/src/server/index.ts
index ef1829f30..51c7d8a38 100644
--- a/src/server/index.ts
+++ b/src/server/index.ts
@@ -38,7 +38,7 @@ import flash = require('connect-flash');
 import { Search } from './Search';
 import _ = require('lodash');
 import * as Archiver from 'archiver';
-import AdmZip from 'adm-zip';
+var AdmZip = require('adm-zip');
 import * as YoutubeApi from "./apis/youtube/youtubeApiSample";
 import { Response } from 'express-serve-static-core';
 import { GoogleApiServerUtils } from "./apis/google/GoogleApiServerUtils";
@@ -358,7 +358,7 @@ app.post("/uploadDoc", (req, res) => {
             for (const name in files) {
                 const path_2 = files[name].path;
                 const zip = new AdmZip(path_2);
-                zip.getEntries().forEach(entry => {
+                zip.getEntries().forEach((entry: any) => {
                     if (!entry.entryName.startsWith("files/")) return;
                     let dirname = path.dirname(entry.entryName) + "/";
                     let extname = path.extname(entry.entryName);
@@ -367,13 +367,17 @@ app.post("/uploadDoc", (req, res) => {
                     // zip.extractEntryTo(dirname + basename + "_s" + extname, __dirname + RouteStore.public, true, false);
                     // zip.extractEntryTo(dirname + basename + "_m" + extname, __dirname + RouteStore.public, true, false);
                     // zip.extractEntryTo(dirname + basename + "_l" + extname, __dirname + RouteStore.public, true, false);
-                    zip.extractEntryTo(entry.entryName, __dirname + RouteStore.public, true, false);
-                    dirname = "/" + dirname;
-
-                    fs.createReadStream(__dirname + RouteStore.public + dirname + basename + extname).pipe(fs.createWriteStream(__dirname + RouteStore.public + dirname + basename + "_o" + extname));
-                    fs.createReadStream(__dirname + RouteStore.public + dirname + basename + extname).pipe(fs.createWriteStream(__dirname + RouteStore.public + dirname + basename + "_s" + extname));
-                    fs.createReadStream(__dirname + RouteStore.public + dirname + basename + extname).pipe(fs.createWriteStream(__dirname + RouteStore.public + dirname + basename + "_m" + extname));
-                    fs.createReadStream(__dirname + RouteStore.public + dirname + basename + extname).pipe(fs.createWriteStream(__dirname + RouteStore.public + dirname + basename + "_l" + extname));
+                    try {
+                        zip.extractEntryTo(entry.entryName, __dirname + RouteStore.public, true, false);
+                        dirname = "/" + dirname;
+
+                        fs.createReadStream(__dirname + RouteStore.public + dirname + basename + extname).pipe(fs.createWriteStream(__dirname + RouteStore.public + dirname + basename + "_o" + extname));
+                        fs.createReadStream(__dirname + RouteStore.public + dirname + basename + extname).pipe(fs.createWriteStream(__dirname + RouteStore.public + dirname + basename + "_s" + extname));
+                        fs.createReadStream(__dirname + RouteStore.public + dirname + basename + extname).pipe(fs.createWriteStream(__dirname + RouteStore.public + dirname + basename + "_m" + extname));
+                        fs.createReadStream(__dirname + RouteStore.public + dirname + basename + extname).pipe(fs.createWriteStream(__dirname + RouteStore.public + dirname + basename + "_l" + extname));
+                    } catch (e) {
+                        console.log(e);
+                    }
                 });
                 const json = zip.getEntry("doc.json");
                 let docs: any;
-- 
cgit v1.2.3-70-g09d2


From 8cffe031fe364e19897cfb638cffa9e4056c4343 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Thu, 22 Aug 2019 11:07:11 -0400
Subject: fixed textheight with titles. fixed marquee with floating docs. other
 tweaks.

---
 src/client/util/DocumentManager.ts                   |  4 +++-
 src/client/views/MainOverlayTextBox.tsx              |  1 +
 .../views/collections/CollectionStackingView.tsx     | 13 ++++++++-----
 .../views/collections/CollectionViewChromes.tsx      | 14 +++++++++-----
 .../collections/collectionFreeForm/MarqueeView.tsx   | 20 ++++++++++++++++++--
 src/client/views/nodes/DocumentView.tsx              | 10 ++++++++--
 src/client/views/nodes/FormattedTextBox.tsx          |  6 ++++--
 7 files changed, 51 insertions(+), 17 deletions(-)

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

diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 7f526b247..124faf266 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -9,6 +9,7 @@ import { CollectionView } from '../views/collections/CollectionView';
 import { DocumentView } from '../views/nodes/DocumentView';
 import { LinkManager } from './LinkManager';
 import { undoBatch, UndoManager } from './UndoManager';
+import { Scripting } from './Scripting';
 
 
 export class DocumentManager {
@@ -202,4 +203,5 @@ export class DocumentManager {
             return 1;
         }
     }
-}
\ No newline at end of file
+}
+Scripting.addGlobal(function focus(doc: any) { DocumentManager.Instance.getDocumentViews(Doc.GetProto(doc)).map(view => view.props.focus(doc, true)) })
\ No newline at end of file
diff --git a/src/client/views/MainOverlayTextBox.tsx b/src/client/views/MainOverlayTextBox.tsx
index 9fe435bc5..0839e1114 100644
--- a/src/client/views/MainOverlayTextBox.tsx
+++ b/src/client/views/MainOverlayTextBox.tsx
@@ -144,6 +144,7 @@ export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps>
                                 Document={FormattedTextBox.InputBoxOverlay.props.Document}
                                 DataDoc={FormattedTextBox.InputBoxOverlay.props.DataDoc}
                                 onClick={undefined}
+                                ChromeHeight={this.ChromeHeight}
                                 isSelected={returnTrue} select={emptyFunction} renderDepth={0} selectOnLoad={true}
                                 ContainingCollectionView={undefined} whenActiveChanged={emptyFunction} active={returnTrue} ContentScaling={returnOne}
                                 ScreenToLocalTransform={this._textXf} PanelWidth={returnZero} PanelHeight={returnZero} focus={emptyFunction}
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index be6ee1756..4ab656744 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -287,15 +287,18 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
     masonryChildren(docs: Doc[]) {
         this._docXfs.length = 0;
         return docs.map((d, i) => {
+            const pair = Doc.GetLayoutDataDocPair(this.props.Document, this.props.DataDoc, this.props.fieldKey, d);
+            if (!pair.layout || pair.data instanceof Promise) {
+                return (null);
+            }
             let dref = React.createRef<HTMLDivElement>();
-            let layoutDoc = Doc.expandTemplateLayout(d, this.props.DataDoc);
             let width = () => (d.nativeWidth && !d.ignoreAspect && !this.props.Document.fillColumn ? Math.min(d[WidthSym](), this.columnWidth) : this.columnWidth);/// (uniqueHeadings.length + 1);
-            let height = () => this.getDocHeight(layoutDoc);
-            let dxf = () => this.getDocTransform(layoutDoc!, dref.current!);
+            let height = () => this.getDocHeight(pair.layout);
+            let dxf = () => this.getDocTransform(pair.layout!, dref.current!);
             let rowSpan = Math.ceil((height() + this.gridGap) / this.gridGap);
             this._docXfs.push({ dxf: dxf, width: width, height: height });
-            return !layoutDoc ? (null) : <div className="collectionStackingView-masonryDoc" key={d[Id]} ref={dref} style={{ gridRowEnd: `span ${rowSpan}` }} >
-                {this.getDisplayDoc(layoutDoc, d, dxf, width)}
+            return !pair.layout ? (null) : <div className="collectionStackingView-masonryDoc" key={d[Id]} ref={dref} style={{ gridRowEnd: `span ${rowSpan}` }} >
+                {this.getDisplayDoc(pair.layout, pair.data, dxf, width)}
             </div>;
         });
     }
diff --git a/src/client/views/collections/CollectionViewChromes.tsx b/src/client/views/collections/CollectionViewChromes.tsx
index 25b152d4e..74e57611d 100644
--- a/src/client/views/collections/CollectionViewChromes.tsx
+++ b/src/client/views/collections/CollectionViewChromes.tsx
@@ -297,11 +297,15 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
 
     datePickerRef = (node: HTMLInputElement) => {
         if (node) {
-            this._picker = datepicker("#" + node.id, {
-                disabler: (date: Date) => date > new Date(),
-                onSelect: (instance: any, date: Date) => runInAction(() => this._dateValue = date),
-                dateSelected: new Date()
-            });
+            try {
+                this._picker = datepicker("#" + node.id, {
+                    disabler: (date: Date) => date > new Date(),
+                    onSelect: (instance: any, date: Date) => runInAction(() => this._dateValue = date),
+                    dateSelected: new Date()
+                });
+            } catch (e) {
+                console.log("date picker exception:" + e);
+            }
         }
     }
     render() {
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index aad26efa0..221237365 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -373,7 +373,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
     marqueeSelect(selectBackgrounds: boolean = true) {
         let selRect = this.Bounds;
         let selection: Doc[] = [];
-        this.props.activeDocuments().filter(doc => !doc.isBackground).map(doc => {
+        this.props.activeDocuments().filter(doc => !doc.isBackground && doc.z === undefined).map(doc => {
             var x = NumCast(doc.x);
             var y = NumCast(doc.y);
             var w = NumCast(doc.width);
@@ -383,7 +383,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
             }
         });
         if (!selection.length && selectBackgrounds) {
-            this.props.activeDocuments().map(doc => {
+            this.props.activeDocuments().filter(doc => doc.z === undefined).map(doc => {
                 var x = NumCast(doc.x);
                 var y = NumCast(doc.y);
                 var w = NumCast(doc.width);
@@ -393,6 +393,22 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
                 }
             });
         }
+        if (!selection.length) {
+            let left = this._downX < this._lastX ? this._downX : this._lastX;
+            let top = this._downY < this._lastY ? this._downY : this._lastY;
+            let topLeft = this.props.getContainerTransform().transformPoint(left, top);
+            let size = this.props.getContainerTransform().transformDirection(this._lastX - this._downX, this._lastY - this._downY);
+            let otherBounds = { left: topLeft[0], top: topLeft[1], width: Math.abs(size[0]), height: Math.abs(size[1]) };
+            this.props.activeDocuments().filter(doc => doc.z !== undefined).map(doc => {
+                var x = NumCast(doc.x);
+                var y = NumCast(doc.y);
+                var w = NumCast(doc.width);
+                var h = NumCast(doc.height);
+                if (this.intersectRect({ left: x, top: y, width: w, height: h }, otherBounds)) {
+                    selection.push(doc);
+                }
+            });
+        }
         return selection;
     }
 
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 8dadf2bef..0c577af86 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -715,7 +715,13 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     chromeHeight = () => {
         let showOverlays = this.props.showOverlays ? this.props.showOverlays(this.layoutDoc) : undefined;
         let showTitle = showOverlays && "title" in showOverlays ? showOverlays.title : StrCast(this.layoutDoc.showTitle);
-        return showTitle ? 25 : 0;
+        let templates = Cast(this.layoutDoc.templates, listSpec("string"));
+        if (!showOverlays && templates instanceof List) {
+            templates.map(str => {
+                if (!showTitle && str.indexOf("{props.Document.title}") !== -1) showTitle = "title";
+            });
+        }
+        return (showTitle ? 25 : 0) + 1;// bcz: why 8??
     }
 
     get layoutDoc() {
@@ -769,7 +775,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 {!showTitle && !showCaption ? this.contents :
                     <div style={{ position: "absolute", display: "inline-block", width: "100%", height: "100%", pointerEvents: "none" }}>
 
-                        <div style={{ width: "100%", height: showTextTitle ? "calc(100% - 33px)" : "100%", display: "inline-block", position: "absolute", top: showTextTitle ? "29px" : undefined }}>
+                        <div style={{ width: "100%", height: showTextTitle ? "calc(100% - 29px)" : "100%", display: "inline-block", position: "absolute", top: showTextTitle ? "29px" : undefined }}>
                             {this.contents}
                         </div>
                         {!showTitle ? (null) :
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 849f942b6..94d9f2f66 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -374,6 +374,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
                 this.unhighlightSearchTerms();
             }
         }, { fireImmediately: true });
+        this.tryUpdateHeight();
     }
 
     pushToGoogleDoc = async () => {
@@ -735,11 +736,12 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
 
     @action
     tryUpdateHeight() {
+        const ChromeHeight = this.props.ChromeHeight;
         let sh = this._ref.current ? this._ref.current.scrollHeight : 0;
+        console.log(this.props.Document.title + " " + sh + " " + (ChromeHeight && ChromeHeight()));
         if (this.props.Document.autoHeight && sh !== 0) {
             let nh = this.props.Document.isTemplate ? 0 : NumCast(this.dataDoc.nativeHeight, 0);
             let dh = NumCast(this.props.Document.height, 0);
-            const ChromeHeight = MainOverlayTextBox.Instance.ChromeHeight;
             this.props.Document.height = Math.max(10, (nh ? dh / nh * sh : sh) + (ChromeHeight ? ChromeHeight() : 0));
             this.dataDoc.nativeHeight = nh ? sh : undefined;
         }
@@ -775,7 +777,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
             <div className={`formattedTextBox-cont-${style}`} ref={this._ref}
                 style={{
                     overflowY: this.props.Document.autoHeight ? "hidden" : "auto",
-                    height: this.props.height ? this.props.height : undefined,
+                    height: this.props.Document.autoHeight ? "max-content" : this.props.height ? this.props.height : undefined,
                     background: this.props.hideOnLeave ? "rgba(0,0,0 ,0.4)" : undefined,
                     opacity: this.props.hideOnLeave ? (this._entered || this.props.isSelected() || Doc.IsBrushed(this.props.Document) ? 1 : 0.1) : 1,
                     color: this.props.color ? this.props.color : this.props.hideOnLeave ? "white" : "inherit",
-- 
cgit v1.2.3-70-g09d2


From 224d58da7c8bf8a7eb27cb616100473afcad4f7b Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Thu, 22 Aug 2019 12:22:54 -0400
Subject: cleaned up more context menu stuff.  fixed buttons to follow links by
 default.

---
 src/client/util/SelectionManager.ts                |  1 -
 .../views/collections/CollectionTreeView.tsx       |  5 +++
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 47 +++++++++++-----------
 src/client/views/nodes/DocumentView.tsx            | 24 +++++++----
 4 files changed, 46 insertions(+), 31 deletions(-)

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

diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index ee623d082..9efef888d 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -4,7 +4,6 @@ import { DocumentView } from "../views/nodes/DocumentView";
 import { FormattedTextBox } from "../views/nodes/FormattedTextBox";
 import { NumCast, StrCast } from "../../new_fields/Types";
 import { InkingControl } from "../views/InkingControl";
-import { CurrentUserUtils } from "../../server/authentication/models/current_user_utils";
 
 export namespace SelectionManager {
 
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index b61894b29..7e1aacd5d 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -28,6 +28,7 @@ import "./CollectionTreeView.scss";
 import React = require("react");
 import { ComputedField, ScriptField } from '../../../new_fields/ScriptField';
 import { KeyValueBox } from '../nodes/KeyValueBox';
+import { ContextMenuProps } from '../ContextMenuItem';
 
 
 export interface TreeViewProps {
@@ -541,6 +542,10 @@ export class CollectionTreeView extends CollectionSubView(Document) {
             e.stopPropagation();
             e.preventDefault();
             ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15);
+        } else {
+            let layoutItems: ContextMenuProps[] = [];
+            layoutItems.push({ description: this.props.Document.preventTreeViewOpen ? "Persist Treeview State" : "Abandon Treeview State", event: () => this.props.Document.preventTreeViewOpen = !this.props.Document.preventTreeViewOpen, icon: "paint-brush" });
+            ContextMenu.Instance.addItem({ description: "Treeview Options ...", subitems: layoutItems, icon: "eye" });
         }
     }
     outerXf = () => Utils.GetScreenTransform(this._mainEle!);
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index cbc123c61..224e8047d 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -345,9 +345,14 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
         }, -1);
         if (cluster !== -1) {
             let eles = this.childDocs.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();
             this.selectDocuments(eles);
             let clusterDocs = SelectionManager.SelectedDocuments();
             SelectionManager.DeselectAll();
+            prevSelected.map(dv => SelectionManager.SelectDoc(dv, true));
+
             let de = new DragManager.DocumentDragData(eles, eles.map(d => undefined));
             de.moveDocument = this.props.moveDocument;
             const [left, top] = clusterDocs[0].props.ScreenToLocalTransform().scale(clusterDocs[0].props.ContentScaling()).inverse().transformPoint(0, 0);
@@ -818,30 +823,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
 
     onContextMenu = (e: React.MouseEvent) => {
         let layoutItems: ContextMenuProps[] = [];
-        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;
-            },
-            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" });
-        ContextMenu.Instance.addItem({ description: "Layout...", subitems: layoutItems, icon: "compass" });
-
-        let existingAnalyze = ContextMenu.Instance.findByDescription("Analyzers...");
-        let analyzers: ContextMenuProps[] = existingAnalyze && "subitems" in existingAnalyze ? existingAnalyze.subitems : [];
-        analyzers.push({ description: "Analyze Strokes", event: this.analyzeStrokes, icon: "paint-brush" });
-        !existingAnalyze && ContextMenu.Instance.addItem({ description: "Analyzers...", subitems: analyzers, icon: "hand-point-right" });
-        ContextMenu.Instance.addItem({
             description: "Import document", icon: "upload", event: () => {
                 const input = document.createElement("input");
                 input.type = "file";
@@ -871,6 +853,25 @@ 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;
+            },
+            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" });
+        ContextMenu.Instance.addItem({ description: "Freeform Options ...", subitems: layoutItems, icon: "eye" });
     }
 
 
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 0c577af86..6c944c6b2 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 { 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);
@@ -294,8 +295,8 @@ 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.
-        e.stopPropagation();
         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 });
             e.preventDefault();
             return;
@@ -303,6 +304,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         let altKey = e.altKey;
         let ctrlKey = e.ctrlKey;
         if (this._doubleTap && this.props.renderDepth) {
+            e.stopPropagation();
             let fullScreenAlias = Doc.MakeAlias(this.props.Document);
             fullScreenAlias.templates = new List<string>();
             Doc.UseDetailLayout(fullScreenAlias);
@@ -314,10 +316,13 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         else if (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) || isExpander) {
-                SelectionManager.DeselectAll();
+            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);
@@ -328,6 +333,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 expandedDocs = summarizedDocs ? [...summarizedDocs, ...expandedDocs] : expandedDocs;
                 // let expandedDocs = [...(subBulletDocs ? subBulletDocs : []), ...(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");
                     let getDispDoc = (target: Doc) => Object.getOwnPropertyNames(target).indexOf("isPrototype") === -1 ? target : Doc.MakeDelegate(target);
                     if (altKey || ctrlKey) {
@@ -356,6 +362,7 @@ 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 linkedFwdDocs = first.length ? [first[0].anchor2 as Doc, first[0].anchor1 as Doc] : [expandedDocs[0], expandedDocs[0]];
 
@@ -393,13 +400,15 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         }
         if (this.active) e.stopPropagation(); // events stop at the lowest document that is active.  
         document.removeEventListener("pointermove", this.onPointerMove);
-        document.addEventListener("pointermove", this.onPointerMove);
         document.removeEventListener("pointerup", this.onPointerUp);
+        document.addEventListener("pointermove", this.onPointerMove);
         document.addEventListener("pointerup", this.onPointerUp);
-        // }
     }
     onPointerMove = (e: PointerEvent): void => {
-        if (!e.cancelBubble && this.active) {
+        if (e.cancelBubble && this.active) {
+            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)) {
                     document.removeEventListener("pointermove", this.onPointerMove);
@@ -582,7 +591,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         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: BoolCast(this.props.Document.lockedPosition) ? "unlock" : "lock" });
+        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 script", icon: "edit", event: () => ScriptBox.EditClickScript(this.props.Document, "onClick") });
         makes.push({
@@ -592,6 +601,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 this.makeBtnClicked();
             }, 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" })
         !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 : [];
-- 
cgit v1.2.3-70-g09d2


From da192a9f580174e3b452079026e70d5c08d8a90f Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Fri, 23 Aug 2019 16:11:23 -0400
Subject: added start of an overlay for documents.

---
 src/client/views/MainView.tsx                      |  2 +-
 src/client/views/OverlayView.tsx                   | 31 ++++++++++++++++++++--
 src/client/views/nodes/DocumentView.tsx            |  1 +
 .../authentication/models/current_user_utils.ts    |  6 +++++
 4 files changed, 37 insertions(+), 3 deletions(-)

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

diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index f28844009..a02214deb 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -404,7 +404,7 @@ export class MainView extends React.Component {
     get mainContent() {
         let sidebar = CurrentUserUtils.UserDocument.sidebar;
         if (!(sidebar instanceof Doc)) return (null);
-        return <div>
+        return <div className="mainContent" style={{ width: "100%", height: "100%", position: "absolute" }}>
             <div className="mainView-libraryHandle"
                 style={{ cursor: "ew-resize", left: `${this.flyoutWidth - 10}px`, backgroundColor: `${StrCast(sidebar.backgroundColor, "lightGray")}` }}
                 onPointerDown={this.onPointerDown}>
diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx
index 2f2579057..a60dc591c 100644
--- a/src/client/views/OverlayView.tsx
+++ b/src/client/views/OverlayView.tsx
@@ -1,9 +1,15 @@
 import * as React from "react";
 import { observer } from "mobx-react";
 import { observable, action } from "mobx";
-import { Utils } from "../../Utils";
+import { Utils, emptyFunction, returnOne, returnTrue, returnEmptyString } from "../../Utils";
 
 import './OverlayView.scss';
+import { CurrentUserUtils } from "../../server/authentication/models/current_user_utils";
+import { DocListCast, Doc } from "../../new_fields/Doc";
+import { Id } from "../../new_fields/FieldSymbols";
+import { DocumentView } from "./nodes/DocumentView";
+import { Transform } from "../util/Transform";
+import { CollectionFreeFormDocumentView } from "./nodes/CollectionFreeFormDocumentView";
 
 export type OverlayDisposer = () => void;
 
@@ -134,8 +140,29 @@ export class OverlayView extends React.Component {
 
     render() {
         return (
-            <div>
+            <div className="overlayView">
                 {this._elements}
+                {CurrentUserUtils.UserDocument.overlays instanceof Doc && DocListCast(CurrentUserUtils.UserDocument.overlays.data).map(d => (
+                    <CollectionFreeFormDocumentView key={d[Id]}
+                        Document={d}
+                        bringToFront={emptyFunction}
+                        addDocument={undefined}
+                        removeDocument={undefined}
+                        ContentScaling={returnOne}
+                        PanelWidth={returnOne}
+                        PanelHeight={returnOne}
+                        ScreenToLocalTransform={Transform.Identity}
+                        renderDepth={1}
+                        selectOnLoad={false}
+                        parentActive={returnTrue}
+                        whenActiveChanged={emptyFunction}
+                        focus={emptyFunction}
+                        backgroundColor={returnEmptyString}
+                        addDocTab={emptyFunction}
+                        pinToPres={emptyFunction}
+                        ContainingCollectionView={undefined}
+                        zoomToScale={emptyFunction}
+                        getScale={returnOne} />))}
             </div>
         );
     }
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 6c944c6b2..3cf86b6f9 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -628,6 +628,7 @@ 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: () => {
                 const a = document.createElement("a");
diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts
index f7ce24967..83e45d3ce 100644
--- a/src/server/authentication/models/current_user_utils.ts
+++ b/src/server/authentication/models/current_user_utils.ts
@@ -73,6 +73,12 @@ export class CurrentUserUtils {
             sidebar.boxShadow = "1 1 3";
             doc.sidebar = sidebar;
         }
+        if (doc.overlays === undefined) {
+            const overlays = Docs.Create.FreeformDocument([], { title: "Overlays" });
+            overlays.excludeFromLibrary = true;
+            Doc.GetProto(overlays).backgroundColor = "#aca3a6";
+            doc.overlays = overlays;
+        }
         StrCast(doc.title).indexOf("@") !== -1 && (doc.title = StrCast(doc.title).split("@")[0] + "'s Library");
         doc.width = 100;
         doc.preventTreeViewOpen = true;
-- 
cgit v1.2.3-70-g09d2


From 34e0b1cf34474ec6765e212b6a35defefbfb49c9 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Tue, 27 Aug 2019 15:46:03 -0400
Subject: removed selectOnLoad prop.  fixed textboxes from deselecting on
 carriage return.

---
 src/client/views/MainOverlayTextBox.tsx                    |  2 +-
 src/client/views/MainView.tsx                              |  2 --
 src/client/views/OverlayView.tsx                           |  1 -
 src/client/views/collections/CollectionDockingView.tsx     |  1 -
 src/client/views/collections/CollectionSchemaCells.tsx     |  1 -
 src/client/views/collections/CollectionSchemaView.tsx      |  1 -
 .../collectionFreeForm/CollectionFreeFormView.tsx          | 14 +++-----------
 src/client/views/nodes/DocumentView.tsx                    |  4 +---
 src/client/views/nodes/FieldView.tsx                       |  2 --
 src/client/views/nodes/FormattedTextBox.tsx                |  9 ++++++---
 src/client/views/nodes/KeyValuePair.tsx                    |  1 -
 src/client/views/presentationview/PresentationElement.tsx  |  1 -
 src/client/views/search/SearchItem.tsx                     |  1 -
 13 files changed, 11 insertions(+), 29 deletions(-)

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

diff --git a/src/client/views/MainOverlayTextBox.tsx b/src/client/views/MainOverlayTextBox.tsx
index 65a291d99..f15b60347 100644
--- a/src/client/views/MainOverlayTextBox.tsx
+++ b/src/client/views/MainOverlayTextBox.tsx
@@ -156,7 +156,7 @@ export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps>
                                 DataDoc={FormattedTextBox.InputBoxOverlay.props.DataDoc}
                                 onClick={undefined}
                                 ChromeHeight={this.ChromeHeight}
-                                isSelected={returnTrue} select={emptyFunction} renderDepth={0} selectOnLoad={true}
+                                isSelected={returnTrue} select={emptyFunction} renderDepth={0}
                                 ContainingCollectionView={undefined} whenActiveChanged={emptyFunction} active={returnTrue} ContentScaling={returnOne}
                                 ScreenToLocalTransform={this._textXf} PanelWidth={returnZero} PanelHeight={returnZero} focus={emptyFunction}
                                 pinToPres={returnZero} addDocTab={this.addDocTab} outer_div={(tooltip: HTMLElement) => { this._tooltip = tooltip; this.updateTooltip(); }} />
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index a02214deb..6b856443b 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -326,7 +326,6 @@ export class MainView extends React.Component {
                             PanelHeight={this.getPHeight}
                             renderDepth={0}
                             backgroundColor={returnEmptyString}
-                            selectOnLoad={false}
                             focus={emptyFunction}
                             parentActive={returnTrue}
                             whenActiveChanged={emptyFunction}
@@ -389,7 +388,6 @@ export class MainView extends React.Component {
             PanelWidth={this.flyoutWidthFunc}
             PanelHeight={this.getPHeight}
             renderDepth={0}
-            selectOnLoad={false}
             focus={emptyFunction}
             backgroundColor={returnEmptyString}
             parentActive={returnTrue}
diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx
index a60dc591c..538614089 100644
--- a/src/client/views/OverlayView.tsx
+++ b/src/client/views/OverlayView.tsx
@@ -153,7 +153,6 @@ export class OverlayView extends React.Component {
                         PanelHeight={returnOne}
                         ScreenToLocalTransform={Transform.Identity}
                         renderDepth={1}
-                        selectOnLoad={false}
                         parentActive={returnTrue}
                         whenActiveChanged={emptyFunction}
                         focus={emptyFunction}
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index dc0cbda0b..95f94875c 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -636,7 +636,6 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
             PanelHeight={this.panelHeight}
             ScreenToLocalTransform={this.ScreenToLocalTransform}
             renderDepth={0}
-            selectOnLoad={false}
             parentActive={returnTrue}
             whenActiveChanged={emptyFunction}
             focus={emptyFunction}
diff --git a/src/client/views/collections/CollectionSchemaCells.tsx b/src/client/views/collections/CollectionSchemaCells.tsx
index 551b485e7..9c26a08f0 100644
--- a/src/client/views/collections/CollectionSchemaCells.tsx
+++ b/src/client/views/collections/CollectionSchemaCells.tsx
@@ -153,7 +153,6 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
             isSelected: returnFalse,
             select: emptyFunction,
             renderDepth: this.props.renderDepth + 1,
-            selectOnLoad: false,
             ScreenToLocalTransform: Transform.Identity,
             focus: emptyFunction,
             active: returnFalse,
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 4008dea51..9d83aa6c1 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -1006,7 +1006,6 @@ export class CollectionSchemaPreview extends React.Component<CollectionSchemaPre
                         parentActive={this.props.active}
                         ScreenToLocalTransform={this.getTransform}
                         renderDepth={this.props.renderDepth + 1}
-                        selectOnLoad={false}
                         ContentScaling={this.contentScaling}
                         PanelWidth={this.PanelWidth}
                         PanelHeight={this.PanelHeight}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index f185222ce..62f848a2b 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1,7 +1,7 @@
 import { library } from "@fortawesome/fontawesome-svg-core";
 import { faEye } from "@fortawesome/free-regular-svg-icons";
 import { faBraille, faChalkboard, faCompass, faCompressArrowsAlt, faExpandArrowsAlt, faPaintBrush, faTable, faUpload } from "@fortawesome/free-solid-svg-icons";
-import { action, computed, IReactionDisposer, observable, reaction } from "mobx";
+import { action, computed, IReactionDisposer, observable, reaction, trace } from "mobx";
 import { observer } from "mobx-react";
 import { Doc, DocListCastAsync, Field, FieldResult, HeightSym, Opt, WidthSym } from "../../../../new_fields/Doc";
 import { Id } from "../../../../new_fields/FieldSymbols";
@@ -38,6 +38,7 @@ import "./CollectionFreeFormView.scss";
 import { MarqueeView } from "./MarqueeView";
 import React = require("react");
 import { DocServer } from "../../../DocServer";
+import { FormattedTextBox } from "../../nodes/FormattedTextBox";
 
 library.add(faEye as any, faTable, faPaintBrush, faExpandArrowsAlt, faCompressArrowsAlt, faCompass, faUpload, faBraille, faChalkboard);
 
@@ -165,8 +166,6 @@ export namespace PivotView {
             return prev;
         }, elements);
 
-        target.resetSelectOnLoaded();
-
         return docViews;
     };
 
@@ -177,7 +176,6 @@ const PanZoomDocument = makeInterface(panZoomSchema, positionSchema, pageSchema)
 
 @observer
 export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
-    private _selectOnLoaded: string = ""; // id of document that should be selected once it's loaded (used for click-to-type)
     private _lastX: number = 0;
     private _lastY: number = 0;
     private get _pwidth() { return this.props.PanelWidth(); }
@@ -241,7 +239,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
     private getContainerTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-this.borderWidth, -this.borderWidth);
     private getLocalTransform = (): Transform => Transform.Identity().scale(1 / this.zoomScaling()).translate(this.panX(), this.panY());
     private addLiveTextBox = (newBox: Doc) => {
-        this._selectOnLoaded = newBox[Id];// track the new text box so we can give it a prop that tells it to focus itself when it's displayed
+        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
         this.addDocument(newBox, false);
     }
     private addDocument = (newBox: Doc, allowDuplicates: boolean) => {
@@ -642,7 +640,6 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
             onClick: this.props.onClick,
             ScreenToLocalTransform: childLayout.z ? this.getTransformOverlay : this.getTransform,
             renderDepth: this.props.renderDepth + 1,
-            selectOnLoad: childLayout[Id] === this._selectOnLoaded,
             PanelWidth: childLayout[WidthSym],
             PanelHeight: childLayout[HeightSym],
             ContentScaling: returnOne,
@@ -668,7 +665,6 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
             onClick: this.props.onClick,
             ScreenToLocalTransform: this.getTransform,
             renderDepth: this.props.renderDepth,
-            selectOnLoad: layoutDoc[Id] === this._selectOnLoaded,
             PanelWidth: layoutDoc[WidthSym],
             PanelHeight: layoutDoc[HeightSym],
             ContentScaling: returnOne,
@@ -768,13 +764,9 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
             return prev;
         }, elements);
 
-        this.resetSelectOnLoaded();
-
         return docviews;
     }
 
-    resetSelectOnLoaded = () => setTimeout(() => this._selectOnLoaded = "", 600);// bcz: surely there must be a better way ....
-
     @computed.struct
     get views() {
         let source = this.elements;
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 3cf86b6f9..f7ebfb75a 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -92,7 +92,6 @@ export interface DocumentViewProps {
     PanelWidth: () => number;
     PanelHeight: () => number;
     focus: (doc: Doc, willZoom: boolean, scale?: number) => void;
-    selectOnLoad: boolean;
     parentActive: () => boolean;
     whenActiveChanged: (isActive: boolean) => void;
     bringToFront: (doc: Doc, sendToBack?: boolean) => void;
@@ -717,7 +716,6 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             isSelected={this.isSelected}
             select={this.select}
             onClick={this.onClickHandler}
-            selectOnLoad={this.props.selectOnLoad}
             layoutKey={"layout"}
             fitToBox={BoolCast(this.props.Document.fitToBox) ? true : this.props.fitToBox}
             DataDoc={this.dataDoc} />);
@@ -808,7 +806,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                         }
                         {!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} selectOnLoad={this.props.selectOnLoad} fieldExt={""} hideOnLeave={true} fieldKey={showCaption} />
+                                <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>
                         }
                     </div>
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index f0f1b3b73..d9774303b 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -36,7 +36,6 @@ export interface FieldViewProps {
     isSelected: () => boolean;
     select: (isCtrlPressed: boolean) => void;
     renderDepth: number;
-    selectOnLoad: boolean;
     addDocument?: (document: Doc, allowDuplicates?: boolean) => boolean;
     addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => void;
     pinToPres: (document: Doc) => void;
@@ -108,7 +107,6 @@ export class FieldView extends React.Component<FieldViewProps> {
             //         PanelWidth={returnHundred}
             //         PanelHeight={returnHundred}
             //         renderDepth={0} //TODO Why is renderDepth reset?
-            //         selectOnLoad={false}
             //         focus={emptyFunction}
             //         isSelected={this.props.isSelected}
             //         select={returnFalse}
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index acfd2a3b8..c28bf1821 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -622,12 +622,15 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
             }
         }
 
-        if (this.props.selectOnLoad) {
-            if (!this.props.isOverlay) this.props.select(false);
-            else this._editorView!.focus();
+        if (this.props.Document[Id] == FormattedTextBox.SelectOnLoad) {
+            FormattedTextBox.SelectOnLoad = "";
+            this.props.select(false);
         }
+        else if (this.props.isOverlay) this._editorView!.focus();
     }
 
+    public static SelectOnLoad = "";
+
     componentWillUnmount() {
         this._editorView && this._editorView.destroy();
         this._reactionDisposer && this._reactionDisposer();
diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx
index 8001b24a7..5afd4d834 100644
--- a/src/client/views/nodes/KeyValuePair.tsx
+++ b/src/client/views/nodes/KeyValuePair.tsx
@@ -60,7 +60,6 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
             isSelected: returnFalse,
             select: emptyFunction,
             renderDepth: 1,
-            selectOnLoad: false,
             active: returnFalse,
             whenActiveChanged: emptyFunction,
             ScreenToLocalTransform: Transform.Identity,
diff --git a/src/client/views/presentationview/PresentationElement.tsx b/src/client/views/presentationview/PresentationElement.tsx
index 83413814f..80aa25f48 100644
--- a/src/client/views/presentationview/PresentationElement.tsx
+++ b/src/client/views/presentationview/PresentationElement.tsx
@@ -359,7 +359,6 @@ export default class PresentationElement extends React.Component<PresentationEle
                     PanelHeight={() => 90}
                     focus={emptyFunction}
                     backgroundColor={returnEmptyString}
-                    selectOnLoad={false}
                     parentActive={returnFalse}
                     whenActiveChanged={returnFalse}
                     bringToFront={emptyFunction}
diff --git a/src/client/views/search/SearchItem.tsx b/src/client/views/search/SearchItem.tsx
index 41fc49c2e..672892fdf 100644
--- a/src/client/views/search/SearchItem.tsx
+++ b/src/client/views/search/SearchItem.tsx
@@ -209,7 +209,6 @@ export class SearchItem extends React.Component<SearchItemProps> {
                     PanelHeight={returnYDimension}
                     focus={emptyFunction}
                     backgroundColor={returnEmptyString}
-                    selectOnLoad={false}
                     parentActive={returnFalse}
                     whenActiveChanged={returnFalse}
                     bringToFront={emptyFunction}
-- 
cgit v1.2.3-70-g09d2


From 656a08a130124870c9f652f7b1529b2b496cdac7 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Tue, 27 Aug 2019 15:52:25 -0400
Subject: lint fixes.

---
 src/client/views/PreviewCursor.tsx                     | 2 +-
 src/client/views/collections/CollectionViewChromes.tsx | 2 ++
 src/client/views/nodes/DocumentView.tsx                | 2 +-
 src/client/views/nodes/FormattedTextBox.tsx            | 2 +-
 src/client/views/nodes/WebBox.tsx                      | 2 +-
 src/client/views/pdf/PDFViewer.tsx                     | 2 +-
 6 files changed, 7 insertions(+), 5 deletions(-)

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

diff --git a/src/client/views/PreviewCursor.tsx b/src/client/views/PreviewCursor.tsx
index d8e161ab6..1329dc02c 100644
--- a/src/client/views/PreviewCursor.tsx
+++ b/src/client/views/PreviewCursor.tsx
@@ -51,7 +51,7 @@ export class PreviewCursor extends React.Component<{}> {
                     // tests for URL and makes web document
                     let re: any = /^https?:\/\//g;
                     if (re.test(e.clipboardData.getData("text/plain"))) {
-                        const url = e.clipboardData.getData("text/plain")
+                        const url = e.clipboardData.getData("text/plain");
                         PreviewCursor._addDocument(Docs.Create.WebDocument(url, {
                             title: url, width: 300, height: 300,
                             // nativeWidth: 300, nativeHeight: 472.5,
diff --git a/src/client/views/collections/CollectionViewChromes.tsx b/src/client/views/collections/CollectionViewChromes.tsx
index b2df0e747..4b3f7c87e 100644
--- a/src/client/views/collections/CollectionViewChromes.tsx
+++ b/src/client/views/collections/CollectionViewChromes.tsx
@@ -42,11 +42,13 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
 
     _templateCommand = {
         title: "set template", script: "this.target.childLayout = this.source ? this.source[0] : undefined", params: ["target", "source"],
+        initialize: emptyFunction,
         immediate: (draggedDocs: Doc[]) => this.props.CollectionView.props.Document.childLayout = draggedDocs.length ? draggedDocs[0] : undefined
     };
     _contentCommand = {
         // title: "set content", script: "getProto(this.target).data = aliasDocs(this.source.map(async p => await p));", params: ["target", "source"],  // bcz: doesn't look like we can do async stuff in scripting...
         title: "set content", script: "getProto(this.target).data = aliasDocs(this.source);", params: ["target", "source"],
+        initialize: emptyFunction,
         immediate: (draggedDocs: Doc[]) => Doc.GetProto(this.props.CollectionView.props.Document).data = new List<Doc>(draggedDocs.map((d: any) => Doc.MakeAlias(d)))
     };
     _viewCommand = {
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index f7ebfb75a..4d5307c88 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -600,7 +600,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 this.makeBtnClicked();
             }, 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: this.props.Document.ignoreClick ? "Selectable" : "Unselectable", event: () => this.props.Document.ignoreClick = !this.props.Document.ignoreClick, icon: this.props.Document.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 : [];
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index c28bf1821..49189c5f3 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -622,7 +622,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
             }
         }
 
-        if (this.props.Document[Id] == FormattedTextBox.SelectOnLoad) {
+        if (this.props.Document[Id] === FormattedTextBox.SelectOnLoad) {
             FormattedTextBox.SelectOnLoad = "";
             this.props.select(false);
         }
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index f0140d04b..642f58daf 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -18,7 +18,7 @@ import { Docs } from "../../documents/Documents";
 import { faStickyNote } from "@fortawesome/free-solid-svg-icons";
 import { library } from "@fortawesome/fontawesome-svg-core";
 
-library.add(faStickyNote)
+library.add(faStickyNote);
 
 @observer
 export class WebBox extends React.Component<FieldViewProps> {
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index e3bfea237..e5917fefc 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -163,7 +163,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
                         <div key={`${this.props.url}-placeholder-${i + 1}`} className="pdfviewer-placeholder"
                             style={{ width: this._pageSizes[i].width, height: this._pageSizes[i].height }}>
                             "PAGE IS LOADING... "
-                </div>)
+                </div>);
                     this.getPlaceholderPage(i);
                 }))));
             this.props.loaded(Math.max(...this._pageSizes.map(i => i.width)), this._pageSizes[0].height, this.props.pdf.numPages);
-- 
cgit v1.2.3-70-g09d2


From 3865ccd688015d92c8c551bf78ee2a9b6ece5500 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Mon, 9 Sep 2019 15:40:49 -0400
Subject: added local overlay for free form views.   added start of minimap.

---
 src/client/views/OverlayView.tsx                   |  4 +++-
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 16 ++++++++++++---
 .../views/nodes/CollectionFreeFormDocumentView.tsx | 23 ++++++++++++++++++----
 src/client/views/nodes/DocumentView.tsx            |  2 --
 src/server/index.ts                                |  2 +-
 5 files changed, 36 insertions(+), 11 deletions(-)

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

diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx
index fe06e4440..da4b71e5c 100644
--- a/src/client/views/OverlayView.tsx
+++ b/src/client/views/OverlayView.tsx
@@ -197,7 +197,9 @@ export class OverlayView extends React.Component {
     render() {
         return (
             <div className="overlayView" id="overlayView">
-                {this._elements}
+                <div>
+                    {this._elements}
+                </div>
                 {this.overlayDocs}
             </div>
         );
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 2df2a3464..2ec2b0671 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -3,7 +3,7 @@ import { faEye } from "@fortawesome/free-regular-svg-icons";
 import { faBraille, faChalkboard, faCompass, faCompressArrowsAlt, faExpandArrowsAlt, faPaintBrush, faTable, faUpload } from "@fortawesome/free-solid-svg-icons";
 import { action, computed, IReactionDisposer, observable, reaction, trace } from "mobx";
 import { observer } from "mobx-react";
-import { Doc, DocListCastAsync, Field, FieldResult, HeightSym, Opt, WidthSym } from "../../../../new_fields/Doc";
+import { Doc, DocListCastAsync, Field, FieldResult, HeightSym, Opt, WidthSym, DocListCast } from "../../../../new_fields/Doc";
 import { Id } from "../../../../new_fields/FieldSymbols";
 import { InkField, StrokeData } from "../../../../new_fields/InkField";
 import { createSchema, makeInterface } from "../../../../new_fields/Schema";
@@ -225,8 +225,12 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
         return bounds;
     }
 
+    @computed get actualContentBounds() {
+        return this.fitToBox && !this.isAnnotationOverlay ? this.ComputeContentBounds(this.elements.filter(e => e.bounds && !e.bounds.z).map(e => e.bounds!)) : undefined;
+    }
+
     @computed get contentBounds() {
-        let bounds = this.fitToBox && !this.isAnnotationOverlay ? this.ComputeContentBounds(this.elements.filter(e => e.bounds && !e.bounds.z).map(e => e.bounds!)) : undefined;
+        let bounds = this.actualContentBounds;
         let res = {
             panX: bounds ? (bounds.x + bounds.r) / 2 : this.Document.panX || 0,
             panY: bounds ? (bounds.y + bounds.b) / 2 : this.Document.panY || 0,
@@ -775,7 +779,9 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
         const initScript = this.Document.arrangeInit;
         const script = this.Document.arrangeScript;
         let state: any = undefined;
-        const docs = this.childDocs;
+        let docs = this.childDocs;
+        let overlayDocs = DocListCast(this.props.Document.localOverlays);
+        overlayDocs && docs.push(...overlayDocs);
         let elements: ViewDefResult[] = [];
         if (initScript) {
             const initResult = initScript.script.run({ docs, collection: this.Document });
@@ -967,6 +973,10 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
     }
 
     render() {
+        this.props.Document.fitX = this.actualContentBounds && this.actualContentBounds.x;
+        this.props.Document.fitY = this.actualContentBounds && this.actualContentBounds.y;
+        this.props.Document.fitW = this.actualContentBounds && (this.actualContentBounds.r - this.actualContentBounds.x);
+        this.props.Document.fitH = this.actualContentBounds && (this.actualContentBounds.b - this.actualContentBounds.y);
         const easing = () => this.props.Document.panTransformType === "Ease";
         Doc.UpdateDocumentExtensionForField(this.props.DataDoc ? this.props.DataDoc : this.props.Document, this.props.fieldKey);
         return (
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index f07584b4f..c059ff50d 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -1,7 +1,7 @@
 import { computed } from "mobx";
 import { observer } from "mobx-react";
 import { createSchema, makeInterface } from "../../../new_fields/Schema";
-import { BoolCast, FieldValue, NumCast, StrCast } from "../../../new_fields/Types";
+import { BoolCast, FieldValue, NumCast, StrCast, Cast } from "../../../new_fields/Types";
 import { Transform } from "../../util/Transform";
 import { DocComponent } from "../DocComponent";
 import { DocumentView, DocumentViewProps, positionSchema } from "./DocumentView";
@@ -77,6 +77,21 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
     clusterColorFunc = (doc: Doc) => this.clusterColor;
 
     render() {
+        let txf = this.transform;
+        let w = this.width;
+        let h = this.height;
+        let renderScript = this.Document.renderScript;
+        if (renderScript) {
+            let someView = Cast(this.Document.someView, Doc);
+            let minimap = Cast(this.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;
+                w = NumCast(someView.width) / NumCast(someView.scale) / NumCast(minimap.fitW) * NumCast(minimap.width);
+                h = NumCast(someView.height) / NumCast(someView.scale) / NumCast(minimap.fitH) * NumCast(minimap.height);
+                txf = `translate(${x}px,${y}px)`;
+            }
+        }
         const hasPosition = this.props.x !== undefined || this.props.y !== undefined;
         return (
             <div className="collectionFreeFormDocumentView-container"
@@ -89,10 +104,10 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
                             this.props.Document.isBackground ? `0px 0px 50px 50px ${this.clusterColor}` :
                                 `${this.clusterColor} ${StrCast(this.props.Document.boxShadow, `0vw 0vw ${50 / this.props.ContentScaling()}px`)}`) : undefined,
                     borderRadius: this.borderRounding(),
-                    transform: this.transform,
+                    transform: txf,
                     transition: hasPosition ? "transform 1s" : StrCast(this.props.Document.transition),
-                    width: this.width,
-                    height: this.height,
+                    width: w,
+                    height: h,
                     zIndex: this.Document.zIndex || 0,
                 }} >
                 <DocumentView {...this.props}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index b60730a6b..1763c664e 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -758,9 +758,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             });
         }
         let showTextTitle = showTitle && StrCast(this.layoutDoc.layout).startsWith("<FormattedTextBox") ? showTitle : undefined;
-        let brushDegree = Doc.IsBrushedDegree(this.props.Document);
         let fullDegree = Doc.isBrushedHighlightedDegree(this.props.Document);
-        // console.log(fullDegree)
         let borderRounding = StrCast(Doc.GetProto(this.props.Document).borderRounding);
         let localScale = this.props.ScreenToLocalTransform().Scale * fullDegree;
         return (
diff --git a/src/server/index.ts b/src/server/index.ts
index d2c6584cc..6472ffb93 100644
--- a/src/server/index.ts
+++ b/src/server/index.ts
@@ -447,7 +447,7 @@ function LoadPage(file: string, pageNumber: number, res: Response) {
             console.log(pageNumber);
             pdf.getPage(pageNumber).then((page: Pdfjs.PDFPageProxy) => {
                 console.log("reading " + page);
-                let viewport = page.getViewport({scale: 1});
+                let viewport = page.getViewport(1 as any);
                 let canvasAndContext = factory.create(viewport.width, viewport.height);
                 let renderContext = {
                     canvasContext: canvasAndContext.context,
-- 
cgit v1.2.3-70-g09d2


From 5941e6f9904b0285fa983248535bead284aa16eb Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Mon, 9 Sep 2019 15:51:01 -0400
Subject: fixed portal navigation to restore collection pan/zoom

---
 src/client/views/nodes/DocumentView.tsx | 11 ++++++++++-
 1 file changed, 10 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 1763c664e..a65f16e1c 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -375,8 +375,17 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                         let targetContext = !Doc.AreProtosEqual(linkedFwdContextDocs[altKey ? 1 : 0], this.props.ContainingCollectionView && this.props.ContainingCollectionView.props.Document) ? linkedFwdContextDocs[altKey ? 1 : 0] : undefined;
                         DocumentManager.Instance.jumpToDocument(linkedFwdDocs[altKey ? 1 : 0], ctrlKey, false,
                             document => {  // open up target if it's not already in view ...
+                                let cv = this.props.ContainingCollectionView;  // bcz: ugh --- maybe need to have a props.unfocus() method so that we leave things in the state we found them??
+                                let px = cv && cv.props.Document.panX;
+                                let py = cv && cv.props.Document.panY;
+                                let s = cv && cv.props.Document.scale;
                                 this.props.focus(this.props.Document, true, 1);  // by zooming into the button document first
-                                setTimeout(() => this.props.addDocTab(document, undefined, maxLocation), 1000); // then after the 1sec animation, open up the target in a new tab
+                                setTimeout(() => {
+                                    this.props.addDocTab(document, undefined, maxLocation);
+                                    cv && (cv.props.Document.panX = px);
+                                    cv && (cv.props.Document.panY = py);
+                                    cv && (cv.props.Document.scale = s);
+                                }, 1000); // then after the 1sec animation, open up the target in a new tab
                             },
                             linkedFwdPage[altKey ? 1 : 0], targetContext);
                     }
-- 
cgit v1.2.3-70-g09d2


From 9608245db4ba8cca6054a0641f2eb2bd2032eba6 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Tue, 10 Sep 2019 15:55:51 -0400
Subject: fixed up some stuff with portals and added a ButtonBox  menu item for
 running scripts over a collection

---
 src/client/views/ContextMenuItem.tsx                      |  2 +-
 src/client/views/InkingControl.tsx                        |  2 +-
 src/client/views/ScriptBox.tsx                            | 15 +++++++++++++--
 src/client/views/collections/CollectionBaseView.tsx       |  4 ++++
 src/client/views/nodes/CollectionFreeFormDocumentView.tsx | 11 +++++++----
 src/client/views/nodes/DocumentView.tsx                   | 12 +++++++++---
 src/new_fields/Doc.ts                                     |  3 ++-
 7 files changed, 37 insertions(+), 12 deletions(-)

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

diff --git a/src/client/views/ContextMenuItem.tsx b/src/client/views/ContextMenuItem.tsx
index 1a0839060..0366a6a30 100644
--- a/src/client/views/ContextMenuItem.tsx
+++ b/src/client/views/ContextMenuItem.tsx
@@ -89,7 +89,7 @@ export class ContextMenuItem extends React.Component<ContextMenuProps & { select
             );
         } else if ("subitems" in this.props) {
             let submenu = !this.overItem ? (null) :
-                <div className="contextMenu-subMenu-cont" style={{ marginLeft: "10.5%", left: "0px" }}>
+                <div className="contextMenu-subMenu-cont" style={{ marginLeft: "25%", left: "0px" }}>
                     {this._items.map(prop => <ContextMenuItem {...prop} key={prop.description} closeMenu={this.props.closeMenu} />)}
                 </div>;
             return (
diff --git a/src/client/views/InkingControl.tsx b/src/client/views/InkingControl.tsx
index 519792308..aa573f16b 100644
--- a/src/client/views/InkingControl.tsx
+++ b/src/client/views/InkingControl.tsx
@@ -64,7 +64,7 @@ export class InkingControl extends React.Component {
                             closest = i;
                         }
                     }
-                    cp[closest] = "rgb(" + color.rgb.r + "," + color.rgb.g + "," + color.rgb.b + ")";
+                    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
diff --git a/src/client/views/ScriptBox.tsx b/src/client/views/ScriptBox.tsx
index 2b862a81e..7afba5e01 100644
--- a/src/client/views/ScriptBox.tsx
+++ b/src/client/views/ScriptBox.tsx
@@ -66,13 +66,24 @@ export class ScriptBox extends React.Component<ScriptBoxProps> {
             </div>
         );
     }
-    public static EditClickScript(doc: Doc, fieldKey: string) {
+    public static EditClickScript(doc: Doc, fieldKey: string, prewrapper?: string, postwrapper?: string) {
         let overlayDisposer: () => void = emptyFunction;
         const script = ScriptCast(doc[fieldKey]);
         let originalText: string | undefined = undefined;
-        if (script) originalText = script.script.originalScript;
+        if (script) {
+            originalText = script.script.originalScript;
+            if (prewrapper && originalText.startsWith(prewrapper)) {
+                originalText = originalText.substr(prewrapper.length);
+            }
+            if (postwrapper && originalText.endsWith(postwrapper)) {
+                originalText = originalText.substr(0, originalText.length - postwrapper.length);
+            }
+        }
         // 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 : "");
+            }
             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 b6ed6aaa0..bd8d56851 100644
--- a/src/client/views/collections/CollectionBaseView.tsx
+++ b/src/client/views/collections/CollectionBaseView.tsx
@@ -12,6 +12,7 @@ import { ContextMenu } from '../ContextMenu';
 import { FieldViewProps } from '../nodes/FieldView';
 import './CollectionBaseView.scss';
 import { DateField } from '../../../new_fields/DateField';
+import { DocumentType } from '../../documents/DocumentTypes';
 
 export enum CollectionViewType {
     Invalid,
@@ -103,6 +104,9 @@ 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/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index c059ff50d..9692dd8a9 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -99,10 +99,13 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
                     transformOrigin: "left top",
                     position: "absolute",
                     backgroundColor: "transparent",
-                    boxShadow: this.props.Document.opacity === 0 ? undefined : this.props.Document.z ? `#9c9396  ${StrCast(this.props.Document.boxShadow, "10px 10px 0.9vw")}` :
-                        this.clusterColor ? (
-                            this.props.Document.isBackground ? `0px 0px 50px 50px ${this.clusterColor}` :
-                                `${this.clusterColor} ${StrCast(this.props.Document.boxShadow, `0vw 0vw ${50 / this.props.ContentScaling()}px`)}`) : undefined,
+                    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,
                     borderRadius: this.borderRounding(),
                     transform: txf,
                     transition: hasPosition ? "transform 1s" : StrCast(this.props.Document.transition),
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index a65f16e1c..633455f63 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -602,11 +602,17 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         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 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: () => {
-                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");
-                this.makeBtnClicked();
+                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" });
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index e3d7cc9ed..ccb8f4aa2 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -685,4 +685,5 @@ export namespace Doc {
 Scripting.addGlobal(function renameAlias(doc: any, n: any) { return StrCast(doc.title).replace(/\([0-9]*\)/, "") + `(${n})`; });
 Scripting.addGlobal(function getProto(doc: any) { return Doc.GetProto(doc); });
 Scripting.addGlobal(function copyField(field: any) { return ObjectField.MakeCopy(field); });
-Scripting.addGlobal(function aliasDocs(field: any) { return new List<Doc>(field.map((d: any) => Doc.MakeAlias(d))); });
\ No newline at end of file
+Scripting.addGlobal(function aliasDocs(field: any) { return new List<Doc>(field.map((d: any) => Doc.MakeAlias(d))); });
+Scripting.addGlobal(function docList(field: any) { return DocListCast(field); });
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2


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.

---
 .../collections/collectionFreeForm/MarqueeView.tsx |  5 +-
 src/client/views/nodes/DocumentView.tsx            | 16 ++++---
 src/client/views/search/SearchBox.tsx              |  6 +--
 src/client/views/search/SearchItem.tsx             | 56 ++--------------------
 src/server/Search.ts                               |  3 ++
 src/server/index.ts                                |  3 ++
 6 files changed, 27 insertions(+), 62 deletions(-)

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

diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 56d8127e2..0c4860be1 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -288,8 +288,11 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
                     else usedPaletted.set(bg, 1);
                 }
             });
+            usedPaletted.delete("#f1efeb");
+            usedPaletted.delete("white");
+            usedPaletted.delete("rgba(255,255,255,1)");
             let usedSequnce = Array.from(usedPaletted.keys()).sort((a, b) => usedPaletted.get(a)! < usedPaletted.get(b)! ? -1 : usedPaletted.get(a)! > usedPaletted.get(b)! ? 1 : 0);
-            let chosenColor = usedPaletted.get("white") || usedPaletted.get("rgb(255,255,255)") && usedPaletted.size === 1 ? "white" : palette.length ? palette[0] : usedSequnce[0];
+            let chosenColor = (usedPaletted.size === 0) ? "white" : palette.length ? palette[0] : usedSequnce[0];
             let inkData = this.ink ? this.ink.inkData : undefined;
             let newCollection = Docs.Create.FreeformDocument(selected, {
                 x: bounds.left,
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();
             }
         });
 
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index 2ad69daca..2e29838e6 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -141,7 +141,7 @@ export class SearchBox extends React.Component {
     private get filterQuery() {
         const types = FilterBox.Instance.filterTypes;
         const includeDeleted = FilterBox.Instance.getDataStatus();
-        return "NOT baseProto_b:true" + (includeDeleted ? "" : " AND NOT deleted:true") + (types ? ` AND (${types.map(type => `({!join from=id to=proto_i}type_t:"${type}" AND NOT type_t:*) OR type_t:"${type}"`).join(" ")})` : "");
+        return "NOT baseProto_b:true" + (includeDeleted ? "" : " AND NOT deleted_b:true") + (types ? ` AND (${types.map(type => `({!join from=id to=proto_i}type_t:"${type}" AND NOT type_t:*) OR type_t:"${type}"`).join(" ")})` : "");
     }
 
 
@@ -304,14 +304,14 @@ 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]} key={result[0][Id]} highlighting={result[1]} />;
+                            this._visibleElements[i] = <SearchItem doc={result[0]} query={this._searchString} key={result[0][Id]} highlighting={result[1]} />;
                             this._isSearch[i] = "search";
                         }
                     }
                     else {
                         result = this._results[i];
                         if (result) {
-                            this._visibleElements[i] = <SearchItem doc={result[0]} key={result[0][Id]} highlighting={result[1]} />;
+                            this._visibleElements[i] = <SearchItem doc={result[0]} query={this._searchString} key={result[0][Id]} highlighting={result[1]} />;
                             this._isSearch[i] = "search";
                         }
                     }
diff --git a/src/client/views/search/SearchItem.tsx b/src/client/views/search/SearchItem.tsx
index 386b5fe74..0b722c086 100644
--- a/src/client/views/search/SearchItem.tsx
+++ b/src/client/views/search/SearchItem.tsx
@@ -28,7 +28,7 @@ import "./SelectorContextMenu.scss";
 
 export interface SearchItemProps {
     doc: Doc;
-    query?: string;
+    query: string;
     highlighting: string[];
 }
 
@@ -128,68 +128,26 @@ export class LinkContextMenu extends React.Component<LinkMenuProps> {
 export class SearchItem extends React.Component<SearchItemProps> {
 
     @observable _selected: boolean = false;
-    private _previewDoc?: Doc;
 
     onClick = () => {
         // I dont think this is the best functionality because clicking the name of the collection does that. Change it back if you'd like
         DocumentManager.Instance.jumpToDocument(this.props.doc, false);
-        if (this.props.doc.data instanceof RichTextField) {
-            this.highlightTextBox(this.props.doc);
-        }
-        // CollectionDockingView.Instance.AddRightSplit(this.props.doc, undefined);
     }
     @observable _useIcons = true;
     @observable _displayDim = 50;
 
-    highlightTextBox = (doc: Doc) => {
-        if (this.props.query) {
-            const fieldkey = 'search_string';
-            if (Object.keys(doc).indexOf(fieldkey) === -1) {
-                doc.search_string = this.props.query;
-            }
-            else {
-                doc.search_string = undefined;
-            }
-
-        }
-    }
-
-    fitToBox = () => {
-        let bounds = Doc.ComputeContentBounds([this.props.doc]);
-        return [(bounds.x + bounds.r) / 2, (bounds.y + bounds.b) / 2, Number(SEARCH_THUMBNAIL_SIZE) / Math.max((bounds.b - bounds.y), (bounds.r - bounds.x)), this._displayDim];
-    }
-
     componentWillUnmount() {
-        if (this._previewDoc) {
-            DocServer.DeleteDocument(this._previewDoc[Id]);
-        }
+        this.props.doc.search_string = undefined;
     }
 
-
     //@computed
     @action
     public DocumentIcon() {
         let layoutresult = StrCast(this.props.doc.type);
         if (!this._useIcons) {
-            let renderDoc = this.props.doc;
-            //let box: number[] = [];
-            if (layoutresult.indexOf(DocumentType.COL) !== -1) {
-                renderDoc = Doc.MakeDelegate(renderDoc);
-                let bounds = DocListCast(renderDoc.data).reduce((bounds, doc) => {
-                    var [sptX, sptY] = [NumCast(doc.x), NumCast(doc.y)];
-                    let [bptX, bptY] = [sptX + doc[WidthSym](), sptY + doc[HeightSym]()];
-                    return {
-                        x: Math.min(sptX, bounds.x), y: Math.min(sptY, bounds.y),
-                        r: Math.max(bptX, bounds.r), b: Math.max(bptY, bounds.b)
-                    };
-                }, { x: Number.MAX_VALUE, y: Number.MAX_VALUE, r: Number.MIN_VALUE, b: Number.MIN_VALUE });
-                let box = () => [(bounds.x + bounds.r) / 2, (bounds.y + bounds.b) / 2, Number(SEARCH_THUMBNAIL_SIZE) / (bounds.r - bounds.x), this._displayDim];
-            }
             let returnXDimension = () => this._useIcons ? 50 : Number(SEARCH_THUMBNAIL_SIZE);
             let returnYDimension = () => this._displayDim;
-            let scale = () => returnXDimension() / NumCast(renderDoc.nativeWidth, returnXDimension());
-            let newRenderDoc = Doc.MakeDelegate(renderDoc); ///   newRenderDoc -> renderDoc -> render"data"Doc -> TextProt
-            this._previewDoc = newRenderDoc;
+            let scale = () => returnXDimension() / NumCast(this.props.doc.nativeWidth, returnXDimension());
             const docview = <div
                 onPointerDown={action(() => {
                     this._useIcons = !this._useIcons;
@@ -219,15 +177,9 @@ export class SearchItem extends React.Component<SearchItemProps> {
                     ContentScaling={scale}
                 />
             </div>;
-            const data = renderDoc.data;
-            if (data instanceof ObjectField) newRenderDoc.data = ObjectField.MakeCopy(data);
-            newRenderDoc.preview = true;
-            newRenderDoc.search_string = this.props.query;
+            this.props.doc.search_string = this.props.query;
             return docview;
         }
-        if (this._previewDoc) {
-            DocServer.DeleteDocument(this._previewDoc[Id]);
-        }
         let button = layoutresult.indexOf(DocumentType.PDF) !== -1 ? faFilePdf :
             layoutresult.indexOf(DocumentType.IMG) !== -1 ? faImage :
                 layoutresult.indexOf(DocumentType.TEXT) !== -1 ? faStickyNote :
diff --git a/src/server/Search.ts b/src/server/Search.ts
index 723dc101b..4a408405a 100644
--- a/src/server/Search.ts
+++ b/src/server/Search.ts
@@ -32,10 +32,13 @@ 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 50ce2b14e..149bacf0f 100644
--- a/src/server/index.ts
+++ b/src/server/index.ts
@@ -198,12 +198,15 @@ 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 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

