From 27cafb6eebd1c6229c3377187a5c0043db25ba0a Mon Sep 17 00:00:00 2001
From: Sam Wilkins <samwilkins333@gmail.com>
Date: Sun, 28 Jul 2019 22:58:32 -0400
Subject: templating fixes, context menu undo streamlining

---
 src/client/views/collections/CollectionView.tsx | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

(limited to 'src/client/views/collections/CollectionView.tsx')

diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index b4f29755c..e3f5d1eb8 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -53,14 +53,14 @@ 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[] = [];
-            subItems.push({ description: "Freeform", event: undoBatch(() => this.props.Document.viewType = CollectionViewType.Freeform), icon: "signature" });
+            subItems.push({ description: "Freeform", event: () => this.props.Document.viewType = CollectionViewType.Freeform, icon: "signature" });
             if (CollectionBaseView.InSafeMode()) {
-                ContextMenu.Instance.addItem({ description: "Test Freeform", event: undoBatch(() => this.props.Document.viewType = CollectionViewType.Invalid), icon: "project-diagram" });
+                ContextMenu.Instance.addItem({ description: "Test Freeform", event: () => this.props.Document.viewType = CollectionViewType.Invalid, icon: "project-diagram" });
             }
-            subItems.push({ description: "Schema", event: undoBatch(() => this.props.Document.viewType = CollectionViewType.Schema), icon: "th-list" });
-            subItems.push({ description: "Treeview", event: undoBatch(() => this.props.Document.viewType = CollectionViewType.Tree), icon: "tree" });
-            subItems.push({ description: "Stacking", event: undoBatch(() => this.props.Document.viewType = CollectionViewType.Stacking), icon: "ellipsis-v" });
-            subItems.push({ description: "Masonry", event: undoBatch(() => this.props.Document.viewType = CollectionViewType.Masonry), icon: "columns" });
+            subItems.push({ description: "Schema", event: () => this.props.Document.viewType = CollectionViewType.Schema, icon: "th-list" });
+            subItems.push({ description: "Treeview", event: () => this.props.Document.viewType = CollectionViewType.Tree, icon: "tree" });
+            subItems.push({ description: "Stacking", event: () => this.props.Document.viewType = CollectionViewType.Stacking, icon: "ellipsis-v" });
+            subItems.push({ description: "Masonry", event: () => this.props.Document.viewType = CollectionViewType.Masonry, icon: "columns" });
             switch (this.props.Document.viewType) {
                 case CollectionViewType.Freeform: {
                     subItems.push({ description: "Custom", icon: "fingerprint", event: CollectionFreeFormView.AddCustomLayout(this.props.Document, this.props.fieldKey) });
@@ -68,7 +68,7 @@ export class CollectionView extends React.Component<FieldViewProps> {
                 }
             }
             ContextMenu.Instance.addItem({ description: "View Modes...", subitems: subItems, icon: "eye" });
-            ContextMenu.Instance.addItem({ description: "Apply Template", event: undoBatch(() => this.props.addDocTab && this.props.addDocTab(Doc.ApplyTemplate(this.props.Document)!, undefined, "onRight")), icon: "project-diagram" });
+            ContextMenu.Instance.addItem({ description: "Apply Template", event: () => this.props.addDocTab && this.props.addDocTab(Doc.ApplyTemplate(this.props.Document)!, undefined, "onRight"), icon: "project-diagram" });
         }
     }
 
-- 
cgit v1.2.3-70-g09d2


From 1c5c84c91742ff7822194519f536338bf96e6453 Mon Sep 17 00:00:00 2001
From: yipstanley <stanley_yip@brown.edu>
Date: Mon, 29 Jul 2019 13:58:36 -0400
Subject: fixed things to chrome and stacking view styling

---
 .../views/collections/CollectionSchemaView.scss    | 27 +++++++++++++---------
 .../views/collections/CollectionSchemaView.tsx     |  1 -
 .../views/collections/CollectionStackingView.scss  |  1 +
 .../views/collections/CollectionStackingView.tsx   |  2 +-
 .../CollectionStackingViewFieldColumn.tsx          |  2 +-
 src/client/views/collections/CollectionView.tsx    | 21 ++++++++++++-----
 .../views/collections/CollectionViewChromes.scss   |  5 ++--
 7 files changed, 37 insertions(+), 22 deletions(-)

(limited to 'src/client/views/collections/CollectionView.tsx')

diff --git a/src/client/views/collections/CollectionSchemaView.scss b/src/client/views/collections/CollectionSchemaView.scss
index e0de76247..2697b482f 100644
--- a/src/client/views/collections/CollectionSchemaView.scss
+++ b/src/client/views/collections/CollectionSchemaView.scss
@@ -6,14 +6,14 @@
     border-style: solid;
     border-radius: $border-radius;
     box-sizing: border-box;
-    // position: absolute;
+    position: absolute;
+    top: 0;
     width: 100%;
     height: calc(100% - 50px);
     // overflow: hidden;
     // overflow-x: scroll;
     // border: none;
     overflow: hidden;
-    transition: top 0.5s;
 
     // .collectionSchemaView-cellContents {
     //     height: $MAX_ROW_HEIGHT;
@@ -78,7 +78,7 @@
         overflow-x: auto;
         height: 100%;
         display: -webkit-inline-box;
-        direction: ltr; 
+        direction: ltr;
     }
 
     .rt-thead {
@@ -122,7 +122,7 @@
         font-size: 13px;
         text-align: center;
         background-color: $light-color-secondary;
-        
+
         &:last-child {
             overflow: visible;
         }
@@ -147,7 +147,7 @@
         // &:nth-child(even) {
         //     background-color: $light-color;
         // }
-    
+
         // &:nth-child(odd) {
         //     background-color: $light-color-secondary;
         // }
@@ -175,7 +175,7 @@
         padding: 0;
         font-size: 13px;
         text-align: center;
-        
+
         // white-space: normal;
 
         .imageBox-cont {
@@ -207,18 +207,19 @@
     background: $light-color;
 }
 
-.collectionSchema-col{
+.collectionSchema-col {
     height: 100%;
 
     .collectionSchema-col-wrapper {
         &.col-before {
             border-left: 2px solid red;
         }
+
         &.col-after {
             border-right: 2px solid red;
         }
     }
-}  
+}
 
 
 .collectionSchemaView-header {
@@ -285,7 +286,7 @@ button.add-column {
                 background-color: $light-color;
                 border: 1px solid $light-color-secondary;
                 padding: 2px 3px;
-                
+
                 &:not(:last-child) {
                     border-top: 0;
                 }
@@ -313,7 +314,7 @@ button.add-column {
     background-color: white;
 
     &.row-focused {
-        background-color: rgb(255, 246, 246);//$light-color-secondary;
+        background-color: rgb(255, 246, 246); //$light-color-secondary;
     }
 
     &.row-wrapped {
@@ -358,9 +359,11 @@ button.add-column {
         &.row-above {
             border-top: 1px solid red;
         }
+
         &.row-below {
             border-bottom: 1px solid red;
         }
+
         &.row-inside {
             border: 1px solid red;
         }
@@ -461,7 +464,9 @@ button.add-column {
     .rt-table {
         overflow-x: hidden; // todo; this shouldnt be like this :((
         overflow-y: visible;
-    } // TODO fix
+    }
+
+    // TODO fix
 
     .row-dragger {
         background-color: rgb(252, 252, 252);
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 8436b22a4..8fe66a949 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -72,7 +72,6 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
     @observable private _node: HTMLDivElement | null = null;
     @observable private _focusedTable: Doc = this.props.Document;
 
-    @computed get chromeCollapsed() { return this.props.chromeCollapsed; }
     @computed get previewWidth() { return () => NumCast(this.props.Document.schemaPreviewWidth); }
     @computed get previewHeight() { return () => this.props.PanelHeight() - 2 * this.borderWidth; }
     @computed get tableWidth() { return this.props.PanelWidth() - 2 * this.borderWidth - this.DIVIDER_WIDTH - this.previewWidth(); }
diff --git a/src/client/views/collections/CollectionStackingView.scss b/src/client/views/collections/CollectionStackingView.scss
index 9dbe4ccb8..004b57eff 100644
--- a/src/client/views/collections/CollectionStackingView.scss
+++ b/src/client/views/collections/CollectionStackingView.scss
@@ -5,6 +5,7 @@
     width: 100%;
     position: absolute;
     display: flex;
+    top: 0;
     overflow-y: auto;
     flex-wrap: wrap;
     transition: top .5s;
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index f647da8f0..287c8a461 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -278,7 +278,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
         };
         // let uniqueHeadings = headings.map((i, idx) => headings.indexOf(i) === idx);
         return (
-            <div className="collectionStackingView" style={{ top: this.chromeCollapsed ? 0 : 100 }}
+            <div className="collectionStackingView"
                 ref={this.createRef} onDrop={this.onDrop.bind(this)} onWheel={(e: React.WheelEvent) => e.stopPropagation()} >
                 {/* {sectionFilter as boolean ? [
                     ["width > height", this.filteredChildren.filter(f => f[WidthSym]() >= 1 + f[HeightSym]())],
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index 387e189e7..c45b0c60a 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -266,7 +266,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
                 {headingView}
                 <div key={`${heading}-stack`} className={`collectionStackingView-masonry${singleColumn ? "Single" : "Grid"}`}
                     style={{
-                        padding: singleColumn ? `${style.yMargin}px ${0}px ${style.yMargin}px ${0}px` : `${style.yMargin}px ${0}px`,
+                        padding: singleColumn ? `${style.yMargin}px ${5}px ${style.yMargin}px ${5}px` : `${style.yMargin}px ${0}px`,
                         margin: "auto",
                         width: "max-content", //singleColumn ? undefined : `${cols * (style.columnWidth + style.gridGap) + 2 * style.xMargin - style.gridGap}px`,
                         height: 'max-content',
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 81c84852a..462e86aa2 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -18,7 +18,7 @@ import { CollectionTreeView } from "./CollectionTreeView";
 import { StrCast, PromiseValue } from '../../../new_fields/Types';
 import { DocumentType } from '../../documents/Documents';
 import { CollectionStackingViewChrome, CollectionViewBaseChrome } from './CollectionViewChromes';
-import { observable, action, runInAction } from 'mobx';
+import { observable, action, runInAction, IReactionDisposer, reaction } from 'mobx';
 import { faEye } from '@fortawesome/free-regular-svg-icons';
 export const COLLECTION_BORDER_WIDTH = 2;
 
@@ -37,14 +37,23 @@ library.add(faImage, faEye);
 export class CollectionView extends React.Component<FieldViewProps> {
     @observable private _collapsed = false;
 
+    private _reactionDisposer: IReactionDisposer | undefined;
+
     public static LayoutString(fieldStr: string = "data", fieldExt: string = "") { return FieldView.LayoutString(CollectionView, fieldStr, fieldExt); }
 
     componentDidMount = () => {
-        // chrome status is one of disabled, collapsed, or visible. this determines initial state from document
-        let chromeStatus = this.props.Document.chromeStatus;
-        if (chromeStatus && (chromeStatus === "disabled" || chromeStatus === "collapsed")) {
-            runInAction(() => this._collapsed = true);
-        }
+        this._reactionDisposer = reaction(() => StrCast(this.props.Document.chromeStatus),
+            () => {
+                // chrome status is one of disabled, collapsed, or visible. this determines initial state from document
+                let chromeStatus = this.props.Document.chromeStatus;
+                if (chromeStatus && (chromeStatus === "disabled" || chromeStatus === "collapsed")) {
+                    runInAction(() => this._collapsed = true);
+                }
+            });
+    }
+
+    componentWillUnmount = () => {
+        this._reactionDisposer && this._reactionDisposer();
     }
 
     private SubViewHelper = (type: CollectionViewType, renderProps: CollectionRenderProps) => {
diff --git a/src/client/views/collections/CollectionViewChromes.scss b/src/client/views/collections/CollectionViewChromes.scss
index 6525f3b07..731333ff5 100644
--- a/src/client/views/collections/CollectionViewChromes.scss
+++ b/src/client/views/collections/CollectionViewChromes.scss
@@ -3,16 +3,17 @@
 
 .collectionViewChrome-cont {
     position: relative;
+    opacity: 0.9;
     z-index: 9001;
     transition: top .5s;
-    background: lightslategray;
+    background: lightgrey;
     padding: 10px;
 
     .collectionViewChrome {
         display: grid;
         grid-template-columns: 1fr auto;
         padding-bottom: 10px;
-        border-bottom: .5px solid lightgrey;
+        border-bottom: .5px solid rgb(180, 180, 180);
 
         .collectionViewBaseChrome {
             display: flex;
-- 
cgit v1.2.3-70-g09d2


From d87020047c1953b212257236a25036df3903dd47 Mon Sep 17 00:00:00 2001
From: yipstanley <stanley_yip@brown.edu>
Date: Mon, 29 Jul 2019 16:20:20 -0400
Subject: chrome default to collapsed

---
 src/client/documents/Documents.ts               | 11 ++++++-----
 src/client/views/collections/CollectionView.tsx |  2 +-
 2 files changed, 7 insertions(+), 6 deletions(-)

(limited to 'src/client/views/collections/CollectionView.tsx')

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 18709499a..d1b3071ed 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -82,6 +82,7 @@ export interface DocumentOptions {
     backgroundColor?: string;
     dropAction?: dropActionType;
     backgroundLayout?: string;
+    chromeStatus?: string;
     curPage?: number;
     documentText?: string;
     borderRounding?: string;
@@ -403,23 +404,23 @@ export namespace Docs {
         }
 
         export function FreeformDocument(documents: Array<Doc>, options: DocumentOptions) {
-            return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { schemaColumns: new List([new SchemaHeaderField("title")]), ...options, viewType: CollectionViewType.Freeform });
+            return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { chromeStatus: "collapsed", schemaColumns: new List([new SchemaHeaderField("title")]), ...options, viewType: CollectionViewType.Freeform });
         }
 
         export function SchemaDocument(schemaColumns: SchemaHeaderField[], documents: Array<Doc>, options: DocumentOptions) {
-            return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { schemaColumns: new List(schemaColumns), ...options, viewType: CollectionViewType.Schema });
+            return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { chromeStatus: "collapsed", schemaColumns: new List(schemaColumns), ...options, viewType: CollectionViewType.Schema });
         }
 
         export function TreeDocument(documents: Array<Doc>, options: DocumentOptions) {
-            return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { schemaColumns: new List([new SchemaHeaderField("title")]), ...options, viewType: CollectionViewType.Tree });
+            return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { chromeStatus: "collapsed", schemaColumns: new List([new SchemaHeaderField("title")]), ...options, viewType: CollectionViewType.Tree });
         }
 
         export function StackingDocument(documents: Array<Doc>, options: DocumentOptions) {
-            return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { schemaColumns: new List([new SchemaHeaderField("title")]), ...options, viewType: CollectionViewType.Stacking });
+            return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { chromeStatus: "collapsed", schemaColumns: new List([new SchemaHeaderField("title")]), ...options, viewType: CollectionViewType.Stacking });
         }
 
         export function MasonryDocument(documents: Array<Doc>, options: DocumentOptions) {
-            return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { schemaColumns: new List([new SchemaHeaderField("title")]), ...options, viewType: CollectionViewType.Masonry });
+            return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { chromeStatus: "collapsed", schemaColumns: new List([new SchemaHeaderField("title")]), ...options, viewType: CollectionViewType.Masonry });
         }
 
         export function ButtonDocument(options?: DocumentOptions) {
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 55e014c66..b7ac8768f 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -35,7 +35,7 @@ library.add(faImage, faEye);
 
 @observer
 export class CollectionView extends React.Component<FieldViewProps> {
-    @observable private _collapsed = false;
+    @observable private _collapsed = true;
 
     private _reactionDisposer: IReactionDisposer | undefined;
 
-- 
cgit v1.2.3-70-g09d2


From 5b455e2aaf119c7db1fe9ef22d71a3accf55a8e2 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Tue, 30 Jul 2019 10:27:42 -0400
Subject: tweaks.

---
 src/client/apis/youtube/YoutubeBox.tsx             | 22 +++++++++-------------
 src/client/views/collections/CollectionView.tsx    |  2 +-
 .../views/collections/CollectionViewChromes.tsx    |  1 +
 3 files changed, 11 insertions(+), 14 deletions(-)

(limited to 'src/client/views/collections/CollectionView.tsx')

diff --git a/src/client/apis/youtube/YoutubeBox.tsx b/src/client/apis/youtube/YoutubeBox.tsx
index 019d191bc..7f9a3ad70 100644
--- a/src/client/apis/youtube/YoutubeBox.tsx
+++ b/src/client/apis/youtube/YoutubeBox.tsx
@@ -1,20 +1,16 @@
-import "../../views/nodes/WebBox.scss";
-import React = require("react");
-import { FieldViewProps, FieldView } from "../../views/nodes/FieldView";
-import { HtmlField } from "../../../new_fields/HtmlField";
-import { WebField } from "../../../new_fields/URLField";
+import { action, observable, runInAction } from 'mobx';
 import { observer } from "mobx-react";
-import { computed, reaction, IReactionDisposer, observable, action, runInAction } from 'mobx';
-import { DocumentDecorations } from "../../views/DocumentDecorations";
-import { InkingControl } from "../../views/InkingControl";
+import { Doc, DocListCastAsync } from "../../../new_fields/Doc";
+import { Cast, NumCast, StrCast } from "../../../new_fields/Types";
 import { Utils } from "../../../Utils";
 import { DocServer } from "../../DocServer";
-import { NumCast, Cast, StrCast } from "../../../new_fields/Types";
-import "./YoutubeBox.scss";
 import { Docs } from "../../documents/Documents";
-import { Doc, DocListCastAsync } from "../../../new_fields/Doc";
-import { listSpec } from "../../../new_fields/Schema";
-import { List } from "../../../new_fields/List";
+import { DocumentDecorations } from "../../views/DocumentDecorations";
+import { InkingControl } from "../../views/InkingControl";
+import { FieldView, FieldViewProps } from "../../views/nodes/FieldView";
+import "../../views/nodes/WebBox.scss";
+import "./YoutubeBox.scss";
+import React = require("react");
 
 interface VideoTemplate {
     thumbnailUrl: string;
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index b7ac8768f..212cc5477 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -85,7 +85,7 @@ export class CollectionView extends React.Component<FieldViewProps> {
         }
         else {
             return [
-                (<CollectionViewBaseChrome CollectionView={this} type={type} collapse={this.collapse} />),
+                (<CollectionViewBaseChrome CollectionView={this} key="chrome" type={type} collapse={this.collapse} />),
                 this.SubViewHelper(type, renderProps)
             ];
         }
diff --git a/src/client/views/collections/CollectionViewChromes.tsx b/src/client/views/collections/CollectionViewChromes.tsx
index 2bffe3cc0..38aafd3cc 100644
--- a/src/client/views/collections/CollectionViewChromes.tsx
+++ b/src/client/views/collections/CollectionViewChromes.tsx
@@ -208,6 +208,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
                             <input className="collectionViewBaseChrome-viewSpecsInput"
                                 placeholder="FILTER DOCUMENTS"
                                 value={this.filterValue ? this.filterValue.script.originalScript : ""}
+                                onChange={(e) => { }}
                                 onPointerDown={this.openViewSpecs} />
                             <div className="collectionViewBaseChrome-viewSpecsMenu"
                                 onPointerDown={this.openViewSpecs}
-- 
cgit v1.2.3-70-g09d2


From c3dcf252e53584b097e3bb2261c72c78786ab16c Mon Sep 17 00:00:00 2001
From: kimdahey <claire_kim1@brown.edu>
Date: Tue, 30 Jul 2019 13:32:24 -0400
Subject: added description of view-mode for chrome status in collectionview

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

(limited to 'src/client/views/collections/CollectionView.tsx')

diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index b7ac8768f..186b612d0 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -45,6 +45,7 @@ export class CollectionView extends React.Component<FieldViewProps> {
         this._reactionDisposer = reaction(() => StrCast(this.props.Document.chromeStatus),
             () => {
                 // chrome status is one of disabled, collapsed, or visible. this determines initial state from document
+                // chrome status may also be view-mode, in reference to stacking view's toggle mode. it is essentially disabled mode, but prevents the toggle button from showing up on the left sidebar.
                 let chromeStatus = this.props.Document.chromeStatus;
                 if (chromeStatus && (chromeStatus === "disabled" || chromeStatus === "collapsed")) {
                     runInAction(() => this._collapsed = true);
-- 
cgit v1.2.3-70-g09d2


From f953ab92c8e0d4bf54f90a6b7f22a86cf5985c38 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Tue, 30 Jul 2019 22:39:52 -0400
Subject: removed chrome (scrollbar) of collectionChrome when collapsed.  fixed
 make background.

---
 src/client/views/collections/CollectionView.tsx          | 14 ++++++--------
 src/client/views/collections/CollectionViewChromes.scss  |  1 +
 src/client/views/collections/CollectionViewChromes.tsx   |  2 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx        |  6 +++++-
 .../views/collections/collectionFreeForm/MarqueeView.tsx | 16 +++++++++++++---
 src/client/views/nodes/DocumentView.tsx                  |  9 +++++----
 6 files changed, 31 insertions(+), 17 deletions(-)

(limited to 'src/client/views/collections/CollectionView.tsx')

diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 376aaf53f..34adc5840 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -1,11 +1,13 @@
 import { library } from '@fortawesome/fontawesome-svg-core';
-import { faProjectDiagram, faSignature, faColumns, faSquare, faTh, faImage, faThList, faTree, faEllipsisV, faFingerprint, faLaptopCode } from '@fortawesome/free-solid-svg-icons';
+import { faEye } from '@fortawesome/free-regular-svg-icons';
+import { faColumns, faEllipsisV, faFingerprint, faImage, faProjectDiagram, faSignature, faSquare, faTh, faThList, faTree } from '@fortawesome/free-solid-svg-icons';
+import { action, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
 import { observer } from "mobx-react";
 import * as React from 'react';
-import { Doc, DocListCast, WidthSym, HeightSym } from '../../../new_fields/Doc';
+import { Doc } from '../../../new_fields/Doc';
 import { Id } from '../../../new_fields/FieldSymbols';
+import { StrCast } from '../../../new_fields/Types';
 import { CurrentUserUtils } from '../../../server/authentication/models/current_user_utils';
-import { undoBatch } from '../../util/UndoManager';
 import { ContextMenu } from "../ContextMenu";
 import { ContextMenuProps } from '../ContextMenuItem';
 import { FieldView, FieldViewProps } from '../nodes/FieldView';
@@ -15,11 +17,7 @@ import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormV
 import { CollectionSchemaView } from "./CollectionSchemaView";
 import { CollectionStackingView } from './CollectionStackingView';
 import { CollectionTreeView } from "./CollectionTreeView";
-import { StrCast, PromiseValue } from '../../../new_fields/Types';
-import { DocumentType } from '../../documents/Documents';
-import { CollectionStackingViewChrome, CollectionViewBaseChrome } from './CollectionViewChromes';
-import { observable, action, runInAction, IReactionDisposer, reaction } from 'mobx';
-import { faEye } from '@fortawesome/free-regular-svg-icons';
+import { CollectionViewBaseChrome } from './CollectionViewChromes';
 export const COLLECTION_BORDER_WIDTH = 2;
 
 library.add(faTh);
diff --git a/src/client/views/collections/CollectionViewChromes.scss b/src/client/views/collections/CollectionViewChromes.scss
index 0c2665b1c..793cb7a8b 100644
--- a/src/client/views/collections/CollectionViewChromes.scss
+++ b/src/client/views/collections/CollectionViewChromes.scss
@@ -14,6 +14,7 @@
         grid-template-columns: 1fr auto;
         padding-bottom: 10px;
         border-bottom: .5px solid rgb(180, 180, 180);
+        overflow: hidden;
 
         .collectionViewBaseChrome {
             display: flex;
diff --git a/src/client/views/collections/CollectionViewChromes.tsx b/src/client/views/collections/CollectionViewChromes.tsx
index ecf78b8c1..1b2561953 100644
--- a/src/client/views/collections/CollectionViewChromes.tsx
+++ b/src/client/views/collections/CollectionViewChromes.tsx
@@ -213,7 +213,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
                             <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="5">Stacking View</option>
                             <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="6">Masonry View</option>
                         </select>
-                        <div className="collectionViewBaseChrome-viewSpecs">
+                        <div className="collectionViewBaseChrome-viewSpecs" style={{ display: this._collapsed ? "none" : "grid" }}>
                             <input className="collectionViewBaseChrome-viewSpecsInput"
                                 placeholder="FILTER DOCUMENTS"
                                 value={this.filterValue ? this.filterValue.script.originalScript : ""}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index cbab14976..3cead34e6 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -310,7 +310,11 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
     onDragOver = (): void => {
     }
 
-    bringToFront = (doc: Doc) => {
+    bringToFront = (doc: Doc, sendToBack?: boolean) => {
+        if (sendToBack) {
+            doc.zIndex = 0;
+            return;
+        }
         const docs = this.childDocs;
         docs.slice().sort((doc1, doc2) => {
             if (doc1 === doc) return 1;
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 07d06d053..b9ee588dd 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -370,15 +370,25 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
         let selRect = this.Bounds;
         let selection: Doc[] = [];
         this.props.activeDocuments().filter(doc => !doc.isBackground).map(doc => {
-            var z = NumCast(doc.zoomBasis, 1);
             var x = NumCast(doc.x);
             var y = NumCast(doc.y);
-            var w = NumCast(doc.width) / z;
-            var h = NumCast(doc.height) / z;
+            var w = NumCast(doc.width);
+            var h = NumCast(doc.height);
             if (this.intersectRect({ left: x, top: y, width: w, height: h }, selRect)) {
                 selection.push(doc);
             }
         });
+        if (!selection.length) {
+            this.props.activeDocuments().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 }, selRect)) {
+                    selection.push(doc);
+                }
+            });
+        }
         return selection;
     }
 
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 51662274d..14f0b5a5a 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -96,7 +96,7 @@ export interface DocumentViewProps {
     selectOnLoad: boolean;
     parentActive: () => boolean;
     whenActiveChanged: (isActive: boolean) => void;
-    bringToFront: (doc: Doc) => void;
+    bringToFront: (doc: Doc, sendToBack?: boolean) => void;
     addDocTab: (doc: Doc, dataDoc: Doc | undefined, where: string) => void;
     collapseToPoint?: (scrpt: number[], expandedDocs: Doc[] | undefined) => void;
     zoomToScale: (scale: number) => void;
@@ -529,7 +529,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     @undoBatch
     @action
     makeBackground = (): void => {
-        this.props.Document.isBackground = true;
+        this.props.Document.isBackground = !this.props.Document.isBackground;
+        this.props.Document.isBackground && this.props.bringToFront(this.props.Document, true);
     }
 
     @undoBatch
@@ -568,7 +569,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         cm.addItem({ description: BoolCast(this.props.Document.lockedPosition) ? "Unlock Position" : "Lock Position", event: this.toggleLockPosition, icon: BoolCast(this.props.Document.lockedPosition) ? "unlock" : "lock" });
         cm.addItem({ description: "Transcribe Speech", event: this.listen, icon: "microphone" });
         let makes: ContextMenuProps[] = [];
-        makes.push({ description: "Make Background", event: this.makeBackground, icon: BoolCast(this.props.Document.lockedPosition) ? "unlock" : "lock" });
+        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: "Make Portal", event: () => {
@@ -708,7 +709,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             <div className={`documentView-node${this.topMost ? "-topmost" : ""}`}
                 ref={this._mainCont}
                 style={{
-                    pointerEvents: this.layoutDoc.isBackground ? "none" : "all",
+                    pointerEvents: this.layoutDoc.isBackground && !this.isSelected() ? "none" : "all",
                     color: foregroundColor,
                     outlineColor: "maroon",
                     outlineStyle: "dashed",
-- 
cgit v1.2.3-70-g09d2


From a75f6fb17a4cbb4643cff0d347eb812138957c95 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Wed, 31 Jul 2019 11:48:29 -0400
Subject: fixed some css and creating text links.

---
 src/client/views/collections/CollectionSchemaView.scss | 8 ++++----
 src/client/views/collections/CollectionSchemaView.tsx  | 1 +
 src/client/views/collections/CollectionView.tsx        | 3 +++
 src/new_fields/Doc.ts                                  | 8 ++++++--
 4 files changed, 14 insertions(+), 6 deletions(-)

(limited to 'src/client/views/collections/CollectionView.tsx')

diff --git a/src/client/views/collections/CollectionSchemaView.scss b/src/client/views/collections/CollectionSchemaView.scss
index 3c4279eea..01744fb34 100644
--- a/src/client/views/collections/CollectionSchemaView.scss
+++ b/src/client/views/collections/CollectionSchemaView.scss
@@ -56,6 +56,10 @@
         background: gray;
         cursor: col-resize;
     }
+
+    .documentView-node:first-child {
+        background: $light-color;
+    }
 }
 
 .ReactTable {
@@ -171,10 +175,6 @@
     display: inline-block;
 }
 
-.documentView-node:first-child {
-    background: $light-color;
-}
-
 .collectionSchema-col {
     height: 100%;
 
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 4d6bf437f..9efd0d3ec 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -980,6 +980,7 @@ export class CollectionSchemaPreview extends React.Component<CollectionSchemaPre
                     style={{
                         transform: `translate(${this.centeringOffset}px, 0px)`,
                         borderRadius: this.borderRounding,
+                        display: "inline",
                         height: "100%"
                     }}>
                     <DocumentView
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 34adc5840..57dc5879b 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -111,6 +111,9 @@ export class CollectionView extends React.Component<FieldViewProps> {
             }
             ContextMenu.Instance.addItem({ description: "View Modes...", subitems: subItems, icon: "eye" });
             ContextMenu.Instance.addItem({ description: "Apply Template", event: () => this.props.addDocTab && this.props.addDocTab(Doc.ApplyTemplate(this.props.Document)!, undefined, "onRight"), icon: "project-diagram" });
+            ContextMenu.Instance.addItem({
+                description: this.props.Document.chromeStatus !== "disabled" ? "Hide Chrome" : "Show Chrome", event: () => this.props.Document.chromeStatus = (this.props.Document.chromeStatus !== "disabled" ? "disabled" : "enabled"), icon: "project-diagram"
+            });
         }
     }
 
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index 5f9df786e..84b8589dd 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -197,8 +197,12 @@ export namespace Doc {
     }
 
     export function Get(doc: Doc, key: string, ignoreProto: boolean = false): FieldResult {
-        const self = doc[Self];
-        return getField(self, key, ignoreProto);
+        try {
+            const self = doc[Self];
+            return getField(self, key, ignoreProto);
+        } catch  {
+            return doc;
+        }
     }
     export function GetT<T extends Field>(doc: Doc, key: string, ctor: ToConstructor<T>, ignoreProto: boolean = false): FieldResult<T> {
         return Cast(Get(doc, key, ignoreProto), ctor) as FieldResult<T>;
-- 
cgit v1.2.3-70-g09d2


From 02346eabdefd428ca23d6a3fbefdcd51ef62b738 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Sun, 4 Aug 2019 01:17:29 -0400
Subject: fixed errors/warnings

---
 src/client/apis/youtube/YoutubeBox.tsx             |   8 +-
 src/client/views/InkingControl.tsx                 |   4 +-
 src/client/views/TemplateMenu.tsx                  |   2 +-
 .../views/collections/CollectionSchemaCells.tsx    |   4 +-
 .../views/collections/CollectionSchemaHeaders.tsx  |   2 +-
 .../views/collections/CollectionSchemaView.tsx     |   5 +-
 .../views/collections/CollectionStackingView.tsx   | 116 +++++++++------------
 src/client/views/collections/CollectionView.tsx    |  11 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  23 ++--
 src/client/views/nodes/ButtonBox.tsx               |   2 +-
 src/client/views/nodes/DocumentView.tsx            |   2 +-
 src/client/views/nodes/PDFBox.tsx                  |   4 +-
 src/client/views/search/FilterBox.tsx              |   2 +-
 src/client/views/search/SearchItem.tsx             |   2 +-
 src/client/views/search/ToggleBar.tsx              |   2 +-
 src/new_fields/Doc.ts                              |   4 +-
 16 files changed, 82 insertions(+), 111 deletions(-)

(limited to 'src/client/views/collections/CollectionView.tsx')

diff --git a/src/client/apis/youtube/YoutubeBox.tsx b/src/client/apis/youtube/YoutubeBox.tsx
index dc142802c..d73988bb8 100644
--- a/src/client/apis/youtube/YoutubeBox.tsx
+++ b/src/client/apis/youtube/YoutubeBox.tsx
@@ -57,7 +57,7 @@ export class YoutubeBox extends React.Component<FieldViewProps> {
         if (awaitedBackUp) {
 
 
-            let jsonList = await DocListCastAsync(awaitedBackUp!.json);
+            let jsonList = await DocListCastAsync(awaitedBackUp.json);
             let jsonDetailList = await DocListCastAsync(awaitedDetails!.json);
 
             if (jsonList!.length !== 0) {
@@ -76,8 +76,8 @@ export class YoutubeBox extends React.Component<FieldViewProps> {
                     let videoDescription = StrCast(snippet!.description);
                     let pusblishDate = (this.roundPublishTime(StrCast(snippet!.publishedAt)))!;
                     let channelTitle = StrCast(snippet!.channelTitle);
-                    let duration: string;
-                    let viewCount: string;
+                    let duration: string = "";
+                    let viewCount: string = "";
                     if (jsonDetailList!.length !== 0) {
                         let contentDetails = await Cast(jsonDetailList![index].contentDetails, Doc);
                         let statistics = await Cast(jsonDetailList![index].statistics, Doc);
@@ -85,7 +85,7 @@ export class YoutubeBox extends React.Component<FieldViewProps> {
                         viewCount = this.abbreviateViewCount(parseInt(StrCast(statistics!.viewCount)))!;
                     }
                     index = index + 1;
-                    let newTemplate: VideoTemplate = { videoId: id, videoTitle: videoTitle, thumbnailUrl: thumbnailUrl, publishDate: pusblishDate, channelTitle: channelTitle, videoDescription: videoDescription, duration: duration!, viewCount: viewCount! };
+                    let newTemplate: VideoTemplate = { videoId: id, videoTitle: videoTitle, thumbnailUrl: thumbnailUrl, publishDate: pusblishDate, channelTitle: channelTitle, videoDescription: videoDescription, duration: duration, viewCount: viewCount };
                     runInAction(() => this.curVideoTemplates.push(newTemplate));
                 }
             }
diff --git a/src/client/views/InkingControl.tsx b/src/client/views/InkingControl.tsx
index 58c83915b..3f40642b5 100644
--- a/src/client/views/InkingControl.tsx
+++ b/src/client/views/InkingControl.tsx
@@ -1,5 +1,5 @@
 import { observable, action, computed, runInAction } from "mobx";
-import { ColorState } from 'react-color';
+import { ColorResult } from 'react-color';
 import React = require("react");
 import { observer } from "mobx-react";
 import "./InkingControl.scss";
@@ -41,7 +41,7 @@ export class InkingControl extends React.Component {
     }
 
     @undoBatch
-    switchColor = action((color: ColorState): void => {
+    switchColor = action((color: ColorResult): void => {
         this._selectedColor = color.hex + (color.rgb.a !== undefined ? this.decimalToHexString(Math.round(color.rgb.a * 255)) : "ff");
         if (InkingControl.Instance.selectedTool === InkTool.None) {
             if (MainOverlayTextBox.Instance.SetColor(color.hex)) return;
diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx
index 6dd908445..393e97a7e 100644
--- a/src/client/views/TemplateMenu.tsx
+++ b/src/client/views/TemplateMenu.tsx
@@ -68,7 +68,7 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
                 }
             }, 10);
         } else if (topDocView.props.ContainingCollectionView) {
-            let collView = topDocView.props.ContainingCollectionView!;
+            let collView = topDocView.props.ContainingCollectionView;
             let [sx, sy] = xf.inverse().transformPoint(0, 0);
             let [x, y] = collView.props.ScreenToLocalTransform().transformPoint(sx, sy);
             topDoc.x = x;
diff --git a/src/client/views/collections/CollectionSchemaCells.tsx b/src/client/views/collections/CollectionSchemaCells.tsx
index 4ff65b277..7e3061354 100644
--- a/src/client/views/collections/CollectionSchemaCells.tsx
+++ b/src/client/views/collections/CollectionSchemaCells.tsx
@@ -175,11 +175,11 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
         };
         let onPointerEnter = (e: React.PointerEvent): void => {
             if (e.buttons === 1 && SelectionManager.GetIsDragging() && (type === "document" || type === undefined)) {
-                dragRef!.current!.className = "collectionSchemaView-cellContainer doc-drag-over";
+                dragRef.current!.className = "collectionSchemaView-cellContainer doc-drag-over";
             }
         };
         let onPointerLeave = (e: React.PointerEvent): void => {
-            dragRef!.current!.className = "collectionSchemaView-cellContainer";
+            dragRef.current!.className = "collectionSchemaView-cellContainer";
         };
 
         let contents: any = "incorrect type";
diff --git a/src/client/views/collections/CollectionSchemaHeaders.tsx b/src/client/views/collections/CollectionSchemaHeaders.tsx
index dfd65770e..d24f63fbb 100644
--- a/src/client/views/collections/CollectionSchemaHeaders.tsx
+++ b/src/client/views/collections/CollectionSchemaHeaders.tsx
@@ -13,7 +13,7 @@ import { faFile } from "@fortawesome/free-regular-svg-icons";
 import { SchemaHeaderField, RandomPastel, PastelSchemaPalette } from "../../../new_fields/SchemaHeaderField";
 import { undoBatch } from "../../util/UndoManager";
 
-library.add(faPlus, faFont, faHashtag, faAlignJustify, faCheckSquare, faToggleOn, faFile, faSortAmountDown, faSortAmountUp, faTimes);
+library.add(faPlus, faFont, faHashtag, faAlignJustify, faCheckSquare, faToggleOn, faFile as any, faSortAmountDown, faSortAmountUp, faTimes);
 
 export interface HeaderProps {
     keyValue: SchemaHeaderField;
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 2d4c88b94..75787c0a8 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -303,14 +303,13 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
             return resized;
         }, [] as { "id": string, "value": number }[]);
     }
-
-    @computed get sorted(): { "id": string, "desc": boolean }[] {
+    @computed get sorted(): { "id": string, "desc"?: true }[] {
         return this.columns.reduce((sorted, shf) => {
             if (shf.desc) {
                 sorted.push({ "id": shf.heading, "desc": shf.desc });
             }
             return sorted;
-        }, [] as { "id": string, "desc": boolean }[]);
+        }, [] as { "id": string, "desc"?: true }[]);
     }
 
     @computed get borderWidth() { return Number(COLLECTION_BORDER_WIDTH); }
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index d7c3ac3b8..4a751c84c 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -1,26 +1,25 @@
 import React = require("react");
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action, computed, IReactionDisposer, reaction, untracked, observable, runInAction } from "mobx";
+import { CursorProperty } from "csstype";
+import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx";
 import { observer } from "mobx-react";
-import { Doc, HeightSym, WidthSym, DocListCast } from "../../../new_fields/Doc";
+import Switch from 'rc-switch';
+import { Doc, HeightSym, WidthSym } from "../../../new_fields/Doc";
 import { Id } from "../../../new_fields/FieldSymbols";
-import { BoolCast, NumCast, Cast, StrCast } from "../../../new_fields/Types";
-import { emptyFunction, Utils, returnTrue } from "../../../Utils";
-import { CollectionSchemaPreview } from "./CollectionSchemaView";
-import "./CollectionStackingView.scss";
-import { CollectionSubView, SubCollectionViewProps } from "./CollectionSubView";
-import { undoBatch } from "../../util/UndoManager";
-import { DragManager } from "../../util/DragManager";
+import { List } from "../../../new_fields/List";
+import { listSpec } from "../../../new_fields/Schema";
+import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField";
+import { BoolCast, Cast, NumCast, StrCast } from "../../../new_fields/Types";
+import { emptyFunction } from "../../../Utils";
 import { DocumentType } from "../../documents/Documents";
+import { DragManager } from "../../util/DragManager";
 import { Transform } from "../../util/Transform";
-import { CursorProperty } from "csstype";
-import { CollectionStackingViewFieldColumn } from "./CollectionStackingViewFieldColumn";
-import { listSpec } from "../../../new_fields/Schema";
-import { SchemaHeaderField, RandomPastel } from "../../../new_fields/SchemaHeaderField";
-import { List } from "../../../new_fields/List";
+import { undoBatch } from "../../util/UndoManager";
 import { EditableView } from "../EditableView";
-import { CollectionViewProps } from "./CollectionBaseView";
-import Switch from 'rc-switch';
+import { CollectionSchemaPreview } from "./CollectionSchemaView";
+import "./CollectionStackingView.scss";
+import { CollectionStackingViewFieldColumn } from "./CollectionStackingViewFieldColumn";
+import { CollectionSubView } from "./CollectionSubView";
 
 @observer
 export class CollectionStackingView extends CollectionSubView(doc => doc) {
@@ -32,13 +31,13 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
     _columnStart: number = 0;
     @observable private cursor: CursorProperty = "grab";
     get sectionHeaders() { return Cast(this.props.Document.sectionHeaders, listSpec(SchemaHeaderField)); }
-    @computed get chromeCollapsed() { return this.props.chromeCollapsed; }
     @computed get xMargin() { return NumCast(this.props.Document.xMargin, 2 * this.gridGap); }
     @computed get yMargin() { return NumCast(this.props.Document.yMargin, 2 * this.gridGap); }
     @computed get gridGap() { return NumCast(this.props.Document.gridGap, 10); }
     @computed get singleColumn() { return BoolCast(this.props.Document.singleColumn, true); }
     @computed get columnWidth() { return this.singleColumn ? (this.props.PanelWidth() / (this.props as any).ContentScaling() - 2 * this.xMargin) : Math.min(this.props.PanelWidth() - 2 * this.xMargin, NumCast(this.props.Document.columnWidth, 250)); }
     @computed get filteredChildren() { return this.childDocs.filter(d => !d.isMinimized); }
+    @computed get sectionFilter() { return this.singleColumn ? StrCast(this.props.Document.sectionFilter) : ""; }
 
     get layoutDoc() {
         // if this document's layout field contains a document (ie, a rendering template), then we will use that
@@ -46,37 +45,32 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
         return this.props.Document.layout instanceof Doc ? this.props.Document.layout : this.props.Document;
     }
 
-    @computed
-    get SectionFilter() { return this.singleColumn ? StrCast(this.props.Document.sectionFilter) : "" }
 
     get Sections() {
-        let sectionHeaders = this.sectionHeaders;
-        if (!sectionHeaders) {
-            this.props.Document.sectionHeaders = sectionHeaders = new List();
+        if (!this.sectionFilter) return new Map<SchemaHeaderField, Doc[]>();
+
+        if (this.sectionHeaders === undefined) {
+            this.props.Document.sectionHeaders = new List<SchemaHeaderField>();
         }
-        let fields = new Map<SchemaHeaderField, Doc[]>(sectionHeaders.map(sh => [sh, []]));
-        if (this.SectionFilter) {
-            this.filteredChildren.map(d => {
-                let sectionValue = (d[this.SectionFilter] ? d[this.SectionFilter] : `NO ${this.SectionFilter.toUpperCase()} VALUE`) as object;
-                // the next five lines ensures that floating point rounding errors don't create more than one section -syip
-                let parsed = parseInt(sectionValue.toString());
-                let castedSectionValue: any = sectionValue;
-                if (!isNaN(parsed)) {
-                    castedSectionValue = parsed;
-                }
+        const sectionHeaders = this.sectionHeaders!;
+        let fields = new Map<SchemaHeaderField, Doc[]>(sectionHeaders.map(sh => [sh, []] as [SchemaHeaderField, []]));
+        this.filteredChildren.map(d => {
+            let sectionValue = (d[this.sectionFilter] ? d[this.sectionFilter] : `NO ${this.sectionFilter.toUpperCase()} VALUE`) as object;
+            // the next five lines ensures that floating point rounding errors don't create more than one section -syip
+            let parsed = parseInt(sectionValue.toString());
+            let castedSectionValue = !isNaN(parsed) ? parsed : sectionValue;
 
-                // look for if header exists already
-                let existingHeader = sectionHeaders!.find(sh => sh.heading === (castedSectionValue ? castedSectionValue.toString() : `NO ${this.SectionFilter.toUpperCase()} VALUE`));
-                if (existingHeader) {
-                    fields.get(existingHeader)!.push(d);
-                }
-                else {
-                    let newSchemaHeader = new SchemaHeaderField(castedSectionValue ? castedSectionValue.toString() : `NO ${this.SectionFilter.toUpperCase()} VALUE`);
-                    fields.set(newSchemaHeader, [d]);
-                    sectionHeaders!.push(newSchemaHeader);
-                }
-            });
-        } else fields.clear();
+            // look for if header exists already
+            let existingHeader = sectionHeaders.find(sh => sh.heading === (castedSectionValue ? castedSectionValue.toString() : `NO ${this.sectionFilter.toUpperCase()} VALUE`));
+            if (existingHeader) {
+                fields.get(existingHeader)!.push(d);
+            }
+            else {
+                let newSchemaHeader = new SchemaHeaderField(castedSectionValue ? castedSectionValue.toString() : `NO ${this.sectionFilter.toUpperCase()} VALUE`);
+                fields.set(newSchemaHeader, [d]);
+                sectionHeaders.push(newSchemaHeader);
+            }
+        });
         return fields;
     }
 
@@ -96,10 +90,8 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
 
         // reset section headers when a new filter is inputted
         this._sectionFilterDisposer = reaction(
-            () => this.SectionFilter,
-            () => {
-                this.props.Document.sectionHeaders = new List();
-            }
+            () => this.sectionFilter,
+            () => this.props.Document.sectionHeaders = new List()
         );
     }
     componentWillUnmount() {
@@ -186,8 +178,8 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
     @undoBatch
     @action
     drop = (e: Event, de: DragManager.DropEvent) => {
-        let targInd = -1;
         let where = [de.x, de.y];
+        let targInd = -1;
         if (de.data instanceof DragManager.DocumentDragData) {
             this._docXfs.map((cd, i) => {
                 let pos = cd.dxf().inverse().transformPoint(-2 * this.gridGap, -2 * this.gridGap);
@@ -233,8 +225,9 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
             }
         });
     }
+    headings = () => Array.from(this.Sections.keys());
     section = (heading: SchemaHeaderField | undefined, docList: Doc[]) => {
-        let key = this.SectionFilter;
+        let key = this.sectionFilter;
         let type: "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" | undefined = undefined;
         let types = docList.length ? docList.map(d => typeof d[key]) : this.childDocs.map(d => typeof d[key]);
         if (types.map((i, idx) => types.indexOf(i) === idx).length === 1) {
@@ -245,7 +238,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
         return <CollectionStackingViewFieldColumn
             key={heading ? heading.heading : ""}
             cols={cols}
-            headings={() => Array.from(this.Sections.keys())}
+            headings={this.headings}
             heading={heading ? heading.heading : ""}
             headingObject={heading}
             docList={docList}
@@ -254,16 +247,13 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
             createDropTarget={this.createDropTarget}
             screenToLocalTransform={this.props.ScreenToLocalTransform}
         />;
-
     }
 
     @action
     addGroup = (value: string) => {
-        if (value) {
-            if (this.sectionHeaders) {
-                this.sectionHeaders.push(new SchemaHeaderField(value));
-                return true;
-            }
+        if (value && this.sectionHeaders) {
+            this.sectionHeaders.push(new SchemaHeaderField(value));
+            return true;
         }
         return false;
     }
@@ -276,11 +266,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
     }
 
     onToggle = (checked: Boolean) => {
-        if (checked) {
-            this.props.CollectionView.props.Document.chromeStatus = 'collapsed';
-        } else {
-            this.props.CollectionView.props.Document.chromeStatus = 'view-mode';
-        }
+        this.props.CollectionView.props.Document.chromeSatus = checked ? "collapsed" : "view-mode";
     }
 
     render() {
@@ -296,10 +282,10 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
         return (
             <div className="collectionStackingView"
                 ref={this.createRef} onDrop={this.onDrop.bind(this)} onWheel={(e: React.WheelEvent) => e.stopPropagation()} >
-                {this.SectionFilter ? Array.from(this.Sections.entries()).sort(this.sortFunc).
-                    map(section => this.section(section[0], section[1])) :
+                {this.sectionFilter ? Array.from(this.Sections.entries()).sort(this.sortFunc).
+                    map((section: [SchemaHeaderField, Doc[]]) => this.section(section[0], section[1])) :
                     this.section(undefined, this.filteredChildren)}
-                {(this.SectionFilter && (this.props.CollectionView.props.Document.chromeStatus !== 'view-mode' && this.props.CollectionView.props.Document.chromeStatus !== 'disabled')) ?
+                {(this.sectionFilter && (this.props.CollectionView.props.Document.chromeStatus !== 'view-mode' && this.props.CollectionView.props.Document.chromeStatus !== 'disabled')) ?
                     <div key={`${this.props.Document[Id]}-addGroup`} className="collectionStackingView-addGroupButton"
                         style={{ width: (this.columnWidth / (headings.length + ((this.props.CollectionView.props.Document.chromeStatus !== 'view-mode' && this.props.CollectionView.props.Document.chromeStatus !== 'disabled') ? 1 : 0))) - 10, marginTop: 10 }}>
                         <EditableView {...editableViewProps} />
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 57dc5879b..f59fee985 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -20,16 +20,7 @@ import { CollectionTreeView } from "./CollectionTreeView";
 import { CollectionViewBaseChrome } from './CollectionViewChromes';
 export const COLLECTION_BORDER_WIDTH = 2;
 
-library.add(faTh);
-library.add(faTree);
-library.add(faSquare);
-library.add(faProjectDiagram);
-library.add(faSignature);
-library.add(faThList);
-library.add(faFingerprint);
-library.add(faColumns);
-library.add(faEllipsisV);
-library.add(faImage, faEye);
+library.add(faTh, faTree, faSquare, faProjectDiagram, faSignature, faThList, faFingerprint, faColumns, faEllipsisV, faImage, faEye as any);
 
 @observer
 export class CollectionView extends React.Component<FieldViewProps> {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index b4d065d26..c943fac74 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -40,7 +40,7 @@ import v5 = require("uuid/v5");
 import { setScheduler } from "bluebird";
 import { DocumentType, Docs } from "../../../documents/Documents";
 
-library.add(faEye, faTable, faPaintBrush, faExpandArrowsAlt, faCompressArrowsAlt, faCompass, faUpload);
+library.add(faEye as any, faTable, faPaintBrush, faExpandArrowsAlt, faCompressArrowsAlt, faCompass, faUpload);
 
 export const panZoomSchema = createSchema({
     panX: "number",
@@ -206,8 +206,9 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
             let cy = NumCast(cd.y) - this._clusterDistance;
             let cw = NumCast(cd.width) + 2 * this._clusterDistance;
             let ch = NumCast(cd.height) + 2 * this._clusterDistance;
-            if (!cd.z && this.intersectRect({ left: cx, top: cy, width: cw, height: ch }, { left: probe[0], top: probe[1], width: 1, height: 1 }))
+            if (!cd.z && this.intersectRect({ left: cx, top: cy, width: cw, height: ch }, { left: probe[0], top: probe[1], width: 1, height: 1 })) {
                 return NumCast(cd.cluster);
+            }
             return cluster;
         }, -1);
         if (cluster !== -1) {
@@ -236,20 +237,20 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
     updateClusters() {
         this.sets.length = 0;
         this.childDocs.map(c => {
-            let included = []
+            let included = [];
             for (let i = 0; i < this.sets.length; i++) {
-                for (let j = 0; j < this.sets[i].length; j++) {
-                    if (this.boundsOverlap(c, this.sets[i][j])) {
+                for (let member of this.sets[i]) {
+                    if (this.boundsOverlap(c, member)) {
                         included.push(i);
                         break;
                     }
                 }
             }
-            if (included.length === 0)
+            if (included.length === 0) {
                 this.sets.push([c]);
-            else if (included.length === 1)
+            } else if (included.length === 1) {
                 this.sets[included[0]].push(c);
-            else {
+            } else {
                 this.sets[included[0]].push(c);
                 for (let s = 1; s < included.length; s++) {
                     this.sets[included[0]].push(...this.sets[included[s]]);
@@ -257,11 +258,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
                 }
             }
         });
-        for (let s = 0; s < this.sets.length; s++) {
-            for (let i = 0; i < this.sets[s].length; i++) {
-                this.sets[s][i].cluster = s;
-            }
-        }
+        this.sets.map((set, i) => set.map(member => member.cluster = i));
     }
 
     getClusterColor = (doc: Doc) => {
diff --git a/src/client/views/nodes/ButtonBox.tsx b/src/client/views/nodes/ButtonBox.tsx
index e2c559c9a..640795789 100644
--- a/src/client/views/nodes/ButtonBox.tsx
+++ b/src/client/views/nodes/ButtonBox.tsx
@@ -16,7 +16,7 @@ import './ButtonBox.scss';
 import { observer } from 'mobx-react';
 import { DocumentIconContainer } from './DocumentIcon';
 
-library.add(faEdit);
+library.add(faEdit as any);
 
 const ButtonSchema = createSchema({
     onClick: ScriptField,
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 0347fc9b2..4528cf94d 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -95,7 +95,7 @@ export interface DocumentViewProps {
     addDocTab: (doc: Doc, dataDoc: Doc | undefined, where: string) => void;
     collapseToPoint?: (scrpt: number[], expandedDocs: Doc[] | undefined) => void;
     zoomToScale: (scale: number) => void;
-    backgroundColor: (doc: Doc) => string;
+    backgroundColor: (doc: Doc) => string | undefined;
     getScale: () => number;
     animateBetweenIcon?: (iconPos: number[], startTime: number, maximizing: boolean) => void;
     ChromeHeight?: () => number;
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index fa072aecf..a49709e83 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -152,9 +152,7 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
     }
 
     scrollTo(y: number) {
-        if (this._mainCont.current) {
-            this._mainCont.current.scrollTo({ top: Math.max(y - (this._mainCont.current!.offsetHeight / 2), 0), behavior: "auto" });
-        }
+        this._mainCont.current && this._mainCont.current.scrollTo({ top: Math.max(y - (this._mainCont.current.offsetHeight / 2), 0), behavior: "auto" });
     }
 
     settingsPanel() {
diff --git a/src/client/views/search/FilterBox.tsx b/src/client/views/search/FilterBox.tsx
index 995ddd5c3..3e8582d61 100644
--- a/src/client/views/search/FilterBox.tsx
+++ b/src/client/views/search/FilterBox.tsx
@@ -384,7 +384,7 @@ export class FilterBox extends React.Component {
                     <div className="active-icon description">Collection Filters Active</div>
                 </div> : undefined}
             </div>
-        )
+        );
     }
 
     // Useful queries:
diff --git a/src/client/views/search/SearchItem.tsx b/src/client/views/search/SearchItem.tsx
index 1b9bba5c6..48eb87251 100644
--- a/src/client/views/search/SearchItem.tsx
+++ b/src/client/views/search/SearchItem.tsx
@@ -134,7 +134,7 @@ export class LinkContextMenu extends React.Component<LinkMenuProps> {
                 <div className="collection"><a onMouseEnter={this.highlightDoc(this.props.doc1)} onMouseLeave={this.unHighlightDoc(this.props.doc1)} onClick={this.getOnClick(this.props.doc1)}>Doc 1: {this.props.doc2.title}</a></div>
                 <div><a onMouseEnter={this.highlightDoc(this.props.doc2)} onMouseLeave={this.unHighlightDoc(this.props.doc2)} onClick={this.getOnClick(this.props.doc2)}>Doc 2: {this.props.doc1.title}</a></div>
             </div>
-        )
+        );
     }
 
 }
diff --git a/src/client/views/search/ToggleBar.tsx b/src/client/views/search/ToggleBar.tsx
index a30104089..ed5ecd3ba 100644
--- a/src/client/views/search/ToggleBar.tsx
+++ b/src/client/views/search/ToggleBar.tsx
@@ -59,7 +59,7 @@ export class ToggleBar extends React.Component<ToggleBarProps>{
         this._forwardTimeline.play();
         this._forwardTimeline.reverse();
         this.props.handleChange();
-        console.log(this.props.getStatus())
+        console.log(this.props.getStatus());
     }
 
     @action.bound
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index b081588a3..c01f4e8cf 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -358,7 +358,7 @@ export namespace Doc {
         let docExtensionForField = doc[fieldKey + "_ext"] as Doc;
         if (docExtensionForField === undefined) {
             setTimeout(() => {
-                CreateDocumentExtensionForField(doc, field);
+                CreateDocumentExtensionForField(doc, fieldKey);
             }, 0);
         } else if (doc instanceof Doc) { // backward compatibility -- add fields for docs that don't have them already
             docExtensionForField.extendsDoc === undefined && setTimeout(() => docExtensionForField.extendsDoc = doc, 0);
@@ -535,7 +535,7 @@ export namespace Doc {
                 d.layout = detailLayout;
                 d.nativeWidth = d.nativeHeight = undefined;
                 if (detailLayout instanceof Doc) {
-                    let delegDetailLayout = Doc.MakeDelegate(detailLayout) as Doc;
+                    let delegDetailLayout = Doc.MakeDelegate(detailLayout);
                     d.layout = delegDetailLayout;
                     delegDetailLayout.layout = await delegDetailLayout.detailedLayout;
                 }
-- 
cgit v1.2.3-70-g09d2


From d8c065935a0cb5a89b6d76407b7729f8584424eb Mon Sep 17 00:00:00 2001
From: Sam Wilkins <samwilkins333@gmail.com>
Date: Tue, 6 Aug 2019 22:03:59 -0400
Subject: pivot view

---
 src/client/views/Main.tsx                          |  2 +
 src/client/views/collections/CollectionView.tsx    |  1 +
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 32 +++++++++++++++
 src/scraping/buxton/scraper.py                     | 15 +++----
 src/scraping/buxton/scripts/initialization.txt     | 46 ++++++++++++++++++++++
 src/scraping/buxton/scripts/layout.txt             |  1 +
 src/server/index.ts                                | 29 +++++++++-----
 src/server/remapUrl.ts                             |  2 +
 8 files changed, 111 insertions(+), 17 deletions(-)
 create mode 100644 src/scraping/buxton/scripts/initialization.txt
 create mode 100644 src/scraping/buxton/scripts/layout.txt

(limited to 'src/client/views/collections/CollectionView.tsx')

diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index 1cf13aa74..daa778ed3 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -7,6 +7,7 @@ import { Cast } from "../../new_fields/Types";
 import { Doc, DocListCastAsync } from "../../new_fields/Doc";
 import { List } from "../../new_fields/List";
 import { DocServer } from "../DocServer";
+import { PivotView } from "./collections/collectionFreeForm/CollectionFreeFormView";
 
 let swapDocs = async () => {
     let oldDoc = await Cast(CurrentUserUtils.UserDocument.linkManagerDoc, Doc);
@@ -33,6 +34,7 @@ let swapDocs = async () => {
     DocServer.init(window.location.protocol, window.location.hostname, 4321, info.email);
     await Docs.Prototypes.initialize();
     await CurrentUserUtils.loadUserDocument(info);
+    await PivotView.loadLayouts();
     // updates old user documents to prevent chrome on tree view.
     (await Cast(CurrentUserUtils.UserDocument.workspaces, Doc))!.chromeStatus = "disabled";
     (await Cast(CurrentUserUtils.UserDocument.recentlyClosed, Doc))!.chromeStatus = "disabled";
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index f59fee985..59572b7e7 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -97,6 +97,7 @@ export class CollectionView extends React.Component<FieldViewProps> {
             switch (this.props.Document.viewType) {
                 case CollectionViewType.Freeform: {
                     subItems.push({ description: "Custom", icon: "fingerprint", event: CollectionFreeFormView.AddCustomLayout(this.props.Document, this.props.fieldKey) });
+                    subItems.push({ description: "Pivot", icon: "fingerprint", event: () => CollectionFreeFormView.SetPivotLayout(this.props.Document) });
                     break;
                 }
             }
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 7ba13e961..17c4e83b0 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -37,6 +37,7 @@ import "./CollectionFreeFormView.scss";
 import { MarqueeView } from "./MarqueeView";
 import React = require("react");
 import { DocumentType, Docs } from "../../../documents/Documents";
+import { RouteStore } from "../../../../server/RouteStore";
 
 library.add(faEye as any, faTable, faPaintBrush, faExpandArrowsAlt, faCompressArrowsAlt, faCompass, faUpload, faBraille, faChalkboard);
 
@@ -48,6 +49,20 @@ export const panZoomSchema = createSchema({
     arrangeInit: ScriptField,
 });
 
+export namespace PivotView {
+
+    export let arrangeInit: string;
+    export let arrangeScript: string;
+
+    export async function loadLayouts() {
+        let response = await fetch(Utils.prepend("/layoutscripts"));
+        let scripts = JSON.parse(await response.text());
+        arrangeInit = scripts[0];
+        arrangeScript = scripts[1];
+    }
+
+}
+
 type PanZoomDocument = makeInterface<[typeof panZoomSchema, typeof positionSchema, typeof pageSchema]>;
 const PanZoomDocument = makeInterface(panZoomSchema, positionSchema, pageSchema);
 
@@ -787,6 +802,23 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
         };
     }
 
+    public static SetPivotLayout = (target: Doc) => {
+        let setSpecifiedLayoutField = (originalText: string, key: string, params: Record<string, string>, requiredType?: string) => {
+            const script = CompileScript(originalText, {
+                params,
+                requiredType,
+                typecheck: false
+            });
+            if (!script.compiled) {
+                console.log(script.errors.map(error => error.messageText).join("\n"));
+                return;
+            }
+            target[key] = new ScriptField(script);
+        };
+        setSpecifiedLayoutField(PivotView.arrangeInit, "arrangeInit", { collection: "Doc", docs: "Doc[]" }, undefined);
+        setSpecifiedLayoutField(PivotView.arrangeScript, "arrangeScript", { doc: "Doc", index: "number", collection: "Doc", state: "any", docs: "Doc[]" }, "{x: number, y: number, width?: number, height?: number}");
+    }
+
     render() {
         const easing = () => this.props.Document.panTransformType === "Ease";
         Doc.UpdateDocumentExtensionForField(this.props.DataDoc ? this.props.DataDoc : this.props.Document, this.props.fieldKey);
diff --git a/src/scraping/buxton/scraper.py b/src/scraping/buxton/scraper.py
index f0f45d8f9..1c19118fd 100644
--- a/src/scraping/buxton/scraper.py
+++ b/src/scraping/buxton/scraper.py
@@ -17,6 +17,7 @@ dist = "../../server/public/files"
 
 db = MongoClient("localhost", 27017)["Dash"]
 target_collection = db.newDocuments
+target_doc_title = "Workspace 1"
 schema_guids = []
 common_proto_id = ""
 
@@ -69,7 +70,7 @@ def text_doc_map(string_list):
     return listify(proxify_guids(list(map(guid_map, string_list))))
 
 
-def write_schema(parse_results, display_fields, storage_key):
+def write_collection(parse_results, display_fields, storage_key, viewType=2):
     view_guids = parse_results["child_guids"]
 
     data_doc = parse_results["schema"]
@@ -90,7 +91,7 @@ def write_schema(parse_results, display_fields, storage_key):
             "zoomBasis": 1,
             "zIndex": 2,
             "libraryBrush": False,
-            "viewType": 2
+            "viewType": viewType
         },
         "__type": "Doc"
     }
@@ -237,7 +238,7 @@ def parse_document(file_name: str):
         copyfile(dir_path + "/" + image, dir_path +
                  "/" + image.replace(".", "_o.", 1))
         copyfile(dir_path + "/" + image, dir_path +
-                  "/" + image.replace(".", "_m.", 1))
+                 "/" + image.replace(".", "_m.", 1))
     print(f"extracted {count} images...")
 
     def sanitize(line): return re.sub("[\n\t]+", "", line).replace(u"\u00A0", " ").replace(
@@ -381,22 +382,22 @@ candidates = 0
 for file_name in os.listdir(source):
     if file_name.endswith('.docx'):
         candidates += 1
-        schema_guids.append(write_schema(
+        schema_guids.append(write_collection(
             parse_document(file_name), ["title", "data"], "image_data"))
 
 print("writing parent schema...")
-parent_guid = write_schema({
+parent_guid = write_collection({
     "schema": {
         "_id": guid(),
         "fields": {},
         "__type": "Doc"
     },
     "child_guids": schema_guids
-}, ["title", "short_description", "original_price"], "data")
+}, ["title", "short_description", "original_price"], "data", 1)
 
 print("appending parent schema to main workspace...\n")
 target_collection.update_one(
-    {"fields.title": "WS collection 1"},
+    {"fields.title": target_doc_title},
     {"$push": {"fields.data.fields": {"fieldId": parent_guid, "__type": "proxy"}}}
 )
 
diff --git a/src/scraping/buxton/scripts/initialization.txt b/src/scraping/buxton/scripts/initialization.txt
new file mode 100644
index 000000000..53f3f0d53
--- /dev/null
+++ b/src/scraping/buxton/scripts/initialization.txt
@@ -0,0 +1,46 @@
+const field = collection.pivotField || "title";
+const width = collection.pivotWidth || 200;
+
+const groups = new Map;
+
+for (const doc of docs) {
+    const val = doc[field];
+    if (val === undefined) continue;
+
+    const l = groups.get(val);
+    if (l) {
+        l.push(doc);
+    } else {
+        groups.set(val, [doc]);
+    }
+
+}
+
+let minSize = Infinity;
+
+groups.forEach((val, key) => {
+    minSize = Math.min(minSize, val.length);
+});
+
+const numCols = collection.pivotNumColumns || Math.ceil(Math.sqrt(minSize));
+
+const docMap = new Map;
+const groupNames = [];
+
+let x = 0;
+groups.forEach((val, key) => {
+    let y = 0;
+    let xCount = 0;
+    groupNames.push({type:"text", text:String(key), x, y:width + 50, width: width * 1.25 * numCols, height:100, fontSize:collection.pivotFontSize});
+    for (const doc of val) {
+        docMap.set(doc, {x: x + xCount * width * 1.25, y:-y, width, height:width});
+        xCount++;
+        if (xCount >= numCols) {
+            xCount = 0;
+            y += width * 1.25;
+        }
+    }
+    x += width * 1.25 * (numCols + 1);
+});
+
+return {state:{ map: docMap}, views:groupNames };
\ No newline at end of file
diff --git a/src/scraping/buxton/scripts/layout.txt b/src/scraping/buxton/scripts/layout.txt
new file mode 100644
index 000000000..46b6dbaac
--- /dev/null
+++ b/src/scraping/buxton/scripts/layout.txt
@@ -0,0 +1 @@
+return state.map.get(doc)
\ No newline at end of file
diff --git a/src/server/index.ts b/src/server/index.ts
index 1e811c1b2..d735a6d8c 100644
--- a/src/server/index.ts
+++ b/src/server/index.ts
@@ -148,24 +148,33 @@ app.get("/pull", (req, res) =>
         res.redirect("/");
     }));
 
-app.get("/buxton/:clear", (req, res) => {
-    if (req.params.clear === "true") {
-        deleteFields().then(() => upload_buxton_docs(res));
-    } else {
-        upload_buxton_docs(res);
-    }
-});
-
-let upload_buxton_docs = (res: Response) => {
+app.get("/buxton", (req, res) => {
     let buxton_scraping = path.join(__dirname, '../scraping/buxton');
     exec('python scraper.py', { cwd: buxton_scraping }, (err, stdout, sterr) => {
         if (err) {
             res.send(err.message);
             return;
         }
+        console.log(stdout);
         res.redirect("/");
     });
-};
+});
+
+app.get('/layoutscripts', (req, res) => {
+    let scripts: string[] = [];
+    let handler = (err: NodeJS.ErrnoException | null, data: Buffer) => {
+        if (err) {
+            console.log(err.message);
+            return;
+        }
+        scripts.push(data.toString());
+        if (scripts.length === 2) {
+            res.send(JSON.stringify(scripts));
+        }
+    };
+    fs.readFile(path.join(__dirname, '../scraping/buxton/scripts/initialization.txt'), handler);
+    fs.readFile(path.join(__dirname, '../scraping/buxton/scripts/layout.txt'), handler);
+});
 
 app.get("/version", (req, res) => {
     exec('"C:\\Program Files\\Git\\bin\\git.exe" rev-parse HEAD', (err, stdout, stderr) => {
diff --git a/src/server/remapUrl.ts b/src/server/remapUrl.ts
index 69c766d56..5218a239a 100644
--- a/src/server/remapUrl.ts
+++ b/src/server/remapUrl.ts
@@ -2,6 +2,8 @@ import { Database } from "./database";
 import { Search } from "./Search";
 import * as path from 'path';
 
+//npx ts-node src/server/remapUrl.ts
+
 const suffixMap: { [type: string]: true } = {
     "video": true,
     "pdf": true,
-- 
cgit v1.2.3-70-g09d2


From 4d3962cf221cb2421835c7016562433077e7b200 Mon Sep 17 00:00:00 2001
From: Sam Wilkins <samwilkins333@gmail.com>
Date: Wed, 7 Aug 2019 01:48:15 -0400
Subject: pivot view script retrieval and context menu behavior improvements

---
 src/client/views/collections/CollectionView.tsx    |  14 ++++--
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  12 ++---
 src/scraping/buxton/source/Silverlight.dmg         | Bin 0 -> 15097394 bytes
 src/server/index.ts                                |  52 ++++++++++++---------
 4 files changed, 44 insertions(+), 34 deletions(-)
 create mode 100644 src/scraping/buxton/source/Silverlight.dmg

(limited to 'src/client/views/collections/CollectionView.tsx')

diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 59572b7e7..60ede17e7 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -1,6 +1,6 @@
 import { library } from '@fortawesome/fontawesome-svg-core';
 import { faEye } from '@fortawesome/free-regular-svg-icons';
-import { faColumns, faEllipsisV, faFingerprint, faImage, faProjectDiagram, faSignature, faSquare, faTh, faThList, faTree } from '@fortawesome/free-solid-svg-icons';
+import { faColumns, faEllipsisV, faFingerprint, faImage, faProjectDiagram, faSignature, faSquare, faTh, faThList, faTree, faCopy } from '@fortawesome/free-solid-svg-icons';
 import { action, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
 import { observer } from "mobx-react";
 import * as React from 'react';
@@ -20,7 +20,7 @@ import { CollectionTreeView } from "./CollectionTreeView";
 import { CollectionViewBaseChrome } from './CollectionViewChromes';
 export const COLLECTION_BORDER_WIDTH = 2;
 
-library.add(faTh, faTree, faSquare, faProjectDiagram, faSignature, faThList, faFingerprint, faColumns, faEllipsisV, faImage, faEye as any);
+library.add(faTh, faTree, faSquare, faProjectDiagram, faSignature, faThList, faFingerprint, faColumns, faEllipsisV, faImage, faEye as any, faCopy);
 
 @observer
 export class CollectionView extends React.Component<FieldViewProps> {
@@ -86,7 +86,13 @@ 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[] = [];
-            subItems.push({ description: "Freeform", event: () => this.props.Document.viewType = CollectionViewType.Freeform, icon: "signature" });
+            subItems.push({
+                description: "Freeform", event: () => {
+                    this.props.Document.viewType = CollectionViewType.Freeform;
+                    delete this.props.Document.arrangeInit;
+                    delete this.props.Document.arrangeScript;
+                }, icon: "signature"
+            });
             if (CollectionBaseView.InSafeMode()) {
                 ContextMenu.Instance.addItem({ description: "Test Freeform", event: () => this.props.Document.viewType = CollectionViewType.Invalid, icon: "project-diagram" });
             }
@@ -97,7 +103,7 @@ export class CollectionView extends React.Component<FieldViewProps> {
             switch (this.props.Document.viewType) {
                 case CollectionViewType.Freeform: {
                     subItems.push({ description: "Custom", icon: "fingerprint", event: CollectionFreeFormView.AddCustomLayout(this.props.Document, this.props.fieldKey) });
-                    subItems.push({ description: "Pivot", icon: "fingerprint", event: () => CollectionFreeFormView.SetPivotLayout(this.props.Document) });
+                    subItems.push({ description: "Pivot", icon: "copy", event: () => CollectionFreeFormView.SetPivotLayout(this.props.Document) });
                     break;
                 }
             }
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 17c4e83b0..110ac2f25 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -51,14 +51,10 @@ export const panZoomSchema = createSchema({
 
 export namespace PivotView {
 
-    export let arrangeInit: string;
-    export let arrangeScript: string;
+    export let scripts: { arrangeInit: string, arrangeScript: string };
 
     export async function loadLayouts() {
-        let response = await fetch(Utils.prepend("/layoutscripts"));
-        let scripts = JSON.parse(await response.text());
-        arrangeInit = scripts[0];
-        arrangeScript = scripts[1];
+        scripts = JSON.parse(await (await fetch(Utils.prepend("/layoutscripts"))).text());
     }
 
 }
@@ -815,8 +811,8 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
             }
             target[key] = new ScriptField(script);
         };
-        setSpecifiedLayoutField(PivotView.arrangeInit, "arrangeInit", { collection: "Doc", docs: "Doc[]" }, undefined);
-        setSpecifiedLayoutField(PivotView.arrangeScript, "arrangeScript", { doc: "Doc", index: "number", collection: "Doc", state: "any", docs: "Doc[]" }, "{x: number, y: number, width?: number, height?: number}");
+        setSpecifiedLayoutField(PivotView.scripts.arrangeInit, "arrangeInit", { collection: "Doc", docs: "Doc[]" }, undefined);
+        setSpecifiedLayoutField(PivotView.scripts.arrangeScript, "arrangeScript", { doc: "Doc", index: "number", collection: "Doc", state: "any", docs: "Doc[]" }, "{x: number, y: number, width?: number, height?: number}");
     }
 
     render() {
diff --git a/src/scraping/buxton/source/Silverlight.dmg b/src/scraping/buxton/source/Silverlight.dmg
new file mode 100644
index 000000000..d88290362
Binary files /dev/null and b/src/scraping/buxton/source/Silverlight.dmg differ
diff --git a/src/server/index.ts b/src/server/index.ts
index d735a6d8c..a0101e3f8 100644
--- a/src/server/index.ts
+++ b/src/server/index.ts
@@ -1,6 +1,6 @@
 require('dotenv').config();
 import * as bodyParser from 'body-parser';
-import { exec } from 'child_process';
+import { exec, ExecOptions } from 'child_process';
 import * as cookieParser from 'cookie-parser';
 import * as express from 'express';
 import * as session from 'express-session';
@@ -149,31 +149,39 @@ app.get("/pull", (req, res) =>
     }));
 
 app.get("/buxton", (req, res) => {
-    let buxton_scraping = path.join(__dirname, '../scraping/buxton');
-    exec('python scraper.py', { cwd: buxton_scraping }, (err, stdout, sterr) => {
-        if (err) {
-            res.send(err.message);
-            return;
+    let cwd = '../scraping/buxton';
+
+    let onResolved = (stdout: string) => { console.log(stdout); res.redirect("/"); };
+    let onRejected = (err: any) => { console.error(err.message); res.send(err); };
+    let tryPython3 = () => command_line('python3 scraper.py', cwd).then(onResolved, onRejected);
+
+    command_line('python scraper.py', cwd).then(onResolved, tryPython3);
+});
+
+const command_line = (command: string, fromDirectory?: string) => {
+    return new Promise<string>((resolve, reject) => {
+        let options: ExecOptions = {};
+        if (fromDirectory) {
+            options.cwd = path.join(__dirname, fromDirectory);
         }
-        console.log(stdout);
-        res.redirect("/");
+        exec(command, options, (err, stdout) => err ? reject(err) : resolve(stdout));
     });
-});
+};
+
+const read_text_file = (relativePath: string) => {
+    let target = path.join(__dirname, relativePath);
+    return new Promise<string>((resolve, reject) => {
+        fs.readFile(target, (err, data) => err ? reject(err) : resolve(data.toString()));
+    });
+};
 
 app.get('/layoutscripts', (req, res) => {
-    let scripts: string[] = [];
-    let handler = (err: NodeJS.ErrnoException | null, data: Buffer) => {
-        if (err) {
-            console.log(err.message);
-            return;
-        }
-        scripts.push(data.toString());
-        if (scripts.length === 2) {
-            res.send(JSON.stringify(scripts));
-        }
-    };
-    fs.readFile(path.join(__dirname, '../scraping/buxton/scripts/initialization.txt'), handler);
-    fs.readFile(path.join(__dirname, '../scraping/buxton/scripts/layout.txt'), handler);
+    let prefix = '../scraping/buxton/scripts/';
+    read_text_file(prefix + 'initialization.txt').then(arrangeInit => {
+        read_text_file(prefix + 'layout.txt').then(arrangeScript => {
+            res.send(JSON.stringify({ arrangeInit, arrangeScript }));
+        });
+    });
 });
 
 app.get("/version", (req, res) => {
-- 
cgit v1.2.3-70-g09d2


From 50d7ffb3303cf06ced84b9788b95578fff2e92da Mon Sep 17 00:00:00 2001
From: Sam Wilkins <samwilkins333@gmail.com>
Date: Wed, 7 Aug 2019 20:14:53 -0400
Subject: pivot viewer native code

---
 src/client/views/Main.tsx                          |   2 +-
 src/client/views/collections/CollectionView.tsx    |   5 +-
 .../views/collections/CollectionViewChromes.tsx    |  33 +++-
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 203 +++++++++++++++++----
 4 files changed, 205 insertions(+), 38 deletions(-)

(limited to 'src/client/views/collections/CollectionView.tsx')

diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index daa778ed3..8be633f80 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -34,7 +34,7 @@ let swapDocs = async () => {
     DocServer.init(window.location.protocol, window.location.hostname, 4321, info.email);
     await Docs.Prototypes.initialize();
     await CurrentUserUtils.loadUserDocument(info);
-    await PivotView.loadLayouts();
+    // await PivotView.loadLayouts();
     // updates old user documents to prevent chrome on tree view.
     (await Cast(CurrentUserUtils.UserDocument.workspaces, Doc))!.chromeStatus = "disabled";
     (await Cast(CurrentUserUtils.UserDocument.recentlyClosed, Doc))!.chromeStatus = "disabled";
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 60ede17e7..7a402798e 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -89,8 +89,7 @@ export class CollectionView extends React.Component<FieldViewProps> {
             subItems.push({
                 description: "Freeform", event: () => {
                     this.props.Document.viewType = CollectionViewType.Freeform;
-                    delete this.props.Document.arrangeInit;
-                    delete this.props.Document.arrangeScript;
+                    delete this.props.Document.usePivotLayout;
                 }, icon: "signature"
             });
             if (CollectionBaseView.InSafeMode()) {
@@ -103,7 +102,7 @@ export class CollectionView extends React.Component<FieldViewProps> {
             switch (this.props.Document.viewType) {
                 case CollectionViewType.Freeform: {
                     subItems.push({ description: "Custom", icon: "fingerprint", event: CollectionFreeFormView.AddCustomLayout(this.props.Document, this.props.fieldKey) });
-                    subItems.push({ description: "Pivot", icon: "copy", event: () => CollectionFreeFormView.SetPivotLayout(this.props.Document) });
+                    subItems.push({ description: "Pivot", icon: "copy", event: () => this.props.Document.usePivotLayout = true });
                     break;
                 }
             }
diff --git a/src/client/views/collections/CollectionViewChromes.tsx b/src/client/views/collections/CollectionViewChromes.tsx
index 1b2561953..e1ac79da6 100644
--- a/src/client/views/collections/CollectionViewChromes.tsx
+++ b/src/client/views/collections/CollectionViewChromes.tsx
@@ -3,7 +3,7 @@ import { CollectionView } from "./CollectionView";
 import "./CollectionViewChromes.scss";
 import { CollectionViewType } from "./CollectionBaseView";
 import { undoBatch } from "../../util/UndoManager";
-import { action, observable, runInAction, computed, IObservable, IObservableValue } from "mobx";
+import { action, observable, runInAction, computed, IObservable, IObservableValue, reaction, autorun } from "mobx";
 import { observer } from "mobx-react";
 import { Doc, DocListCast } from "../../../new_fields/Doc";
 import { DocLike } from "../MetadataEntryMenu";
@@ -187,6 +187,36 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
         }
     }
 
+    private get document() {
+        return this.props.CollectionView.props.Document;
+    }
+
+    private get pivotKey() {
+        return StrCast(this.document.pivotField);
+    }
+
+    private set pivotKey(value: string) {
+        this.document.pivotField = value;
+    }
+
+    @observable private pivotKeyDisplay = "";
+    getPivotInput = () => {
+        if (!this.document.usePivotLayout) {
+            return (null);
+        }
+        return (<input className="collectionViewBaseChrome-viewSpecsInput"
+            placeholder="PIVOT ON..."
+            value={this.pivotKeyDisplay}
+            onChange={action((e: React.ChangeEvent<HTMLInputElement>) => this.pivotKeyDisplay = e.currentTarget.value)}
+            onKeyPress={action((e: React.KeyboardEvent<HTMLInputElement>) => {
+                let value = e.currentTarget.value;
+                if (e.which === 13) {
+                    this.pivotKey = value;
+                    this.pivotKeyDisplay = "";
+                }
+            })} />);
+    }
+
     render() {
         return (
             <div className="collectionViewChrome-cont" style={{ top: this._collapsed ? -70 : 0 }}>
@@ -219,6 +249,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
                                 value={this.filterValue ? this.filterValue.script.originalScript : ""}
                                 onChange={(e) => { }}
                                 onPointerDown={this.openViewSpecs} />
+                            {this.getPivotInput()}
                             <div className="collectionViewBaseChrome-viewSpecsMenu"
                                 onPointerDown={this.openViewSpecs}
                                 style={{
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 110ac2f25..7d3c0569d 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 { faCompass, faCompressArrowsAlt, faExpandArrowsAlt, faPaintBrush, faTable, faUpload, faChalkboard, faBraille } from "@fortawesome/free-solid-svg-icons";
 import { action, computed, observable } from "mobx";
 import { observer } from "mobx-react";
-import { Doc, DocListCastAsync, HeightSym, WidthSym } from "../../../../new_fields/Doc";
+import { Doc, DocListCastAsync, HeightSym, WidthSym, DocListCast, FieldResult, Field, Opt } from "../../../../new_fields/Doc";
 import { Id } from "../../../../new_fields/FieldSymbols";
 import { InkField, StrokeData } from "../../../../new_fields/InkField";
 import { createSchema, makeInterface } from "../../../../new_fields/Schema";
@@ -38,6 +38,7 @@ import { MarqueeView } from "./MarqueeView";
 import React = require("react");
 import { DocumentType, Docs } from "../../../documents/Documents";
 import { RouteStore } from "../../../../server/RouteStore";
+import { string, number, elementType } from "prop-types";
 
 library.add(faEye as any, faTable, faPaintBrush, faExpandArrowsAlt, faCompressArrowsAlt, faCompass, faUpload, faBraille, faChalkboard);
 
@@ -49,14 +50,143 @@ export const panZoomSchema = createSchema({
     arrangeInit: ScriptField,
 });
 
+export interface ViewDefBounds {
+    x: number;
+    y: number;
+    z?: number;
+    width: number;
+    height: number;
+}
+
+export interface ViewDefResult {
+    ele: JSX.Element;
+    bounds?: ViewDefBounds;
+}
+
 export namespace PivotView {
 
-    export let scripts: { arrangeInit: string, arrangeScript: string };
+    // export let scripts: { arrangeInit: string, arrangeScript: string };
 
-    export async function loadLayouts() {
-        scripts = JSON.parse(await (await fetch(Utils.prepend("/layoutscripts"))).text());
+    // export async function loadLayouts() {
+    //     scripts = JSON.parse(await (await fetch(Utils.prepend("/layoutscripts"))).text());
+    // }
+
+    export interface PivotData {
+        type: string;
+        text: string;
+        x: number;
+        y: number;
+        width: number;
+        height: number;
+        fontSize: number;
     }
 
+    export const elements = (target: CollectionFreeFormView) => {
+        let collection = target.Document;
+        const field = StrCast(collection.pivotField) || "title";
+        const width = NumCast(collection.pivotWidth) || 200;
+
+        const groups = new Map<FieldResult<Field>, Doc[]>();
+
+        for (const doc of target.childDocs) {
+            const val = doc[field];
+            if (val === undefined) continue;
+
+            const l = groups.get(val);
+            if (l) {
+                l.push(doc);
+            } else {
+                groups.set(val, [doc]);
+            }
+
+        }
+
+        let minSize = Infinity;
+
+        groups.forEach((val, key) => {
+            minSize = Math.min(minSize, val.length);
+        });
+
+        const numCols = NumCast(collection.pivotNumColumns) || Math.ceil(Math.sqrt(minSize));
+        const fontSize = NumCast(collection.pivotFontSize);
+
+        const docMap = new Map<Doc, ViewDefBounds>();
+        const groupNames: PivotData[] = [];
+
+        let x = 0;
+        groups.forEach((val, key) => {
+            let y = 0;
+            let xCount = 0;
+            groupNames.push({
+                type: "text",
+                text: String(key),
+                x,
+                y: width + 50,
+                width: width * 1.25 * numCols,
+                height: 100, fontSize: fontSize
+            });
+            for (const doc of val) {
+                docMap.set(doc, {
+                    x: x + xCount * width * 1.25,
+                    y: -y,
+                    width,
+                    height: width
+                });
+                xCount++;
+                if (xCount >= numCols) {
+                    xCount = 0;
+                    y += width * 1.25;
+                }
+            }
+            x += width * 1.25 * (numCols + 1);
+        });
+
+        let elements = target.viewDefsToJSX(groupNames);
+        let curPage = FieldValue(target.Document.curPage, -1);
+
+        let docViews = target.childDocs.filter(doc => doc instanceof Doc).reduce((prev, doc) => {
+            var page = NumCast(doc.page, -1);
+            if ((Math.abs(Math.round(page) - Math.round(curPage)) < 3) || page === -1) {
+                let minim = BoolCast(doc.isMinimized);
+                if (minim === undefined || !minim) {
+                    let defaultPosition = (): ViewDefBounds => {
+                        return {
+                            x: NumCast(doc.x),
+                            y: NumCast(doc.y),
+                            z: NumCast(doc.z),
+                            width: NumCast(doc.width),
+                            height: NumCast(doc.height)
+                        };
+                    };
+                    const pos = docMap.get(doc) || defaultPosition();
+                    prev.push({
+                        ele: (
+                            <CollectionFreeFormDocumentView
+                                key={doc[Id]}
+                                x={pos.x}
+                                y={pos.y}
+                                width={pos.width}
+                                height={pos.height}
+                                {...target.getChildDocumentViewProps(doc)}
+                            />),
+                        bounds: {
+                            x: pos.x,
+                            y: pos.y,
+                            z: pos.z,
+                            width: NumCast(pos.width),
+                            height: NumCast(pos.height)
+                        }
+                    });
+                }
+            }
+            return prev;
+        }, elements);
+
+        target.resetSelectOnLoaded();
+
+        return docViews;
+    };
+
 }
 
 type PanZoomDocument = makeInterface<[typeof panZoomSchema, typeof positionSchema, typeof pageSchema]>;
@@ -508,9 +638,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
         return 1;
     }
 
-
     getChildDocumentViewProps(childDocLayout: Doc): DocumentViewProps {
-        let self = this;
         let pair = Doc.GetLayoutDataDocPair(this.props.Document, this.props.DataDoc, this.props.fieldKey, childDocLayout);
         return {
             DataDoc: pair.data,
@@ -569,7 +697,19 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
         return result.result === undefined ? { 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") } : result.result;
     }
 
-    private viewDefToJSX(viewDef: any): { ele: JSX.Element, bounds?: { x: number, y: number, z?: number, width: number, height: number } } | undefined {
+    viewDefsToJSX = (views: any[]) => {
+        let elements: ViewDefResult[] = [];
+        if (Array.isArray(views)) {
+            elements = views.reduce<typeof elements>((prev, ele) => {
+                const jsx = this.viewDefToJSX(ele);
+                jsx && prev.push(jsx);
+                return prev;
+            }, elements);
+        }
+        return elements;
+    }
+
+    private viewDefToJSX(viewDef: any): Opt<ViewDefResult> {
         if (viewDef.type === "text") {
             const text = Cast(viewDef.text, "string");
             const x = Cast(viewDef.x, "number");
@@ -598,20 +738,14 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
         const script = this.Document.arrangeScript;
         let state: any = undefined;
         const docs = this.childDocs;
-        let elements: { ele: JSX.Element, bounds?: { x: number, y: number, z?: number, width: number, height: number } }[] = [];
+        let elements: ViewDefResult[] = [];
         if (initScript) {
             const initResult = initScript.script.run({ docs, collection: this.Document });
             if (initResult.success) {
                 const result = initResult.result;
                 const { state: scriptState, views } = result;
                 state = scriptState;
-                if (Array.isArray(views)) {
-                    elements = views.reduce<typeof elements>((prev, ele) => {
-                        const jsx = this.viewDefToJSX(ele);
-                        jsx && prev.push(jsx);
-                        return prev;
-                    }, elements);
-                }
+                elements = this.viewDefsToJSX(views);
             }
         }
         let docviews = docs.filter(doc => doc instanceof Doc).reduce((prev, doc) => {
@@ -633,14 +767,17 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
             return prev;
         }, elements);
 
-        setTimeout(() => this._selectOnLoaded = "", 600);// bcz: surely there must be a better way ....
+        this.resetSelectOnLoaded();
 
         return docviews;
     }
 
+    resetSelectOnLoaded = () => setTimeout(() => this._selectOnLoaded = "", 600);// bcz: surely there must be a better way ....
+
     @computed.struct
     get views() {
-        return this.elements.filter(ele => ele.bounds && !ele.bounds.z).map(ele => ele.ele);
+        let source = this.Document.usePivotLayout === true ? PivotView.elements(this) : this.elements;
+        return source.filter(ele => ele.bounds && !ele.bounds.z).map(ele => ele.ele);
     }
     @computed.struct
     get overlayViews() {
@@ -798,22 +935,22 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
         };
     }
 
-    public static SetPivotLayout = (target: Doc) => {
-        let setSpecifiedLayoutField = (originalText: string, key: string, params: Record<string, string>, requiredType?: string) => {
-            const script = CompileScript(originalText, {
-                params,
-                requiredType,
-                typecheck: false
-            });
-            if (!script.compiled) {
-                console.log(script.errors.map(error => error.messageText).join("\n"));
-                return;
-            }
-            target[key] = new ScriptField(script);
-        };
-        setSpecifiedLayoutField(PivotView.scripts.arrangeInit, "arrangeInit", { collection: "Doc", docs: "Doc[]" }, undefined);
-        setSpecifiedLayoutField(PivotView.scripts.arrangeScript, "arrangeScript", { doc: "Doc", index: "number", collection: "Doc", state: "any", docs: "Doc[]" }, "{x: number, y: number, width?: number, height?: number}");
-    }
+    // public static SetPivotLayout = (target: Doc) => {
+    //     let setSpecifiedLayoutField = (originalText: string, key: string, params: Record<string, string>, requiredType?: string) => {
+    //         const script = CompileScript(originalText, {
+    //             params,
+    //             requiredType,
+    //             typecheck: false
+    //         });
+    //         if (!script.compiled) {
+    //             console.log(script.errors.map(error => error.messageText).join("\n"));
+    //             return;
+    //         }
+    //         target[key] = new ScriptField(script);
+    //     };
+    //     setSpecifiedLayoutField(PivotView.scripts.arrangeInit, "arrangeInit", { collection: "Doc", docs: "Doc[]" }, undefined);
+    //     setSpecifiedLayoutField(PivotView.scripts.arrangeScript, "arrangeScript", { doc: "Doc", index: "number", collection: "Doc", state: "any", docs: "Doc[]" }, "{x: number, y: number, width?: number, height?: number}");
+    // }
 
     render() {
         const easing = () => this.props.Document.panTransformType === "Ease";
-- 
cgit v1.2.3-70-g09d2


From e291730c407932506a80f96457a84dce1820521d Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Fri, 9 Aug 2019 17:42:11 -0400
Subject: made onClick a prop

---
 src/client/views/MainOverlayTextBox.tsx                  |  1 +
 src/client/views/MainView.tsx                            |  2 ++
 src/client/views/collections/CollectionSchemaView.tsx    | 16 +++++++++-------
 src/client/views/collections/CollectionStackingView.tsx  |  1 +
 src/client/views/collections/CollectionView.tsx          |  4 ++++
 .../collectionFreeForm/CollectionFreeFormView.tsx        |  8 ++++++--
 src/client/views/nodes/DocumentContentsView.tsx          | 13 +++++++++++--
 src/client/views/nodes/DocumentView.tsx                  | 16 +++++++++-------
 src/client/views/nodes/FieldView.tsx                     |  3 ++-
 9 files changed, 45 insertions(+), 19 deletions(-)

(limited to 'src/client/views/collections/CollectionView.tsx')

diff --git a/src/client/views/MainOverlayTextBox.tsx b/src/client/views/MainOverlayTextBox.tsx
index fccbeb16c..0f20dc3a8 100644
--- a/src/client/views/MainOverlayTextBox.tsx
+++ b/src/client/views/MainOverlayTextBox.tsx
@@ -142,6 +142,7 @@ export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps>
                             <FormattedTextBox color={`${this._textColor}`} fieldKey={this.TextFieldKey} fieldExt="" hideOnLeave={this._textHideOnLeave} isOverlay={true}
                                 Document={FormattedTextBox.InputBoxOverlay.props.Document}
                                 DataDoc={FormattedTextBox.InputBoxOverlay.props.DataDoc}
+                                onClick={emptyFunction}
                                 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} addDocTab={this.addDocTab} />
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 0d8ade247..bb0048982 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -321,6 +321,7 @@ export class MainView extends React.Component {
                             DataDoc={undefined}
                             addDocument={undefined}
                             addDocTab={emptyFunction}
+                            onClick={emptyFunction}
                             removeDocument={undefined}
                             ScreenToLocalTransform={Transform.Identity}
                             ContentScaling={returnOne}
@@ -385,6 +386,7 @@ export class MainView extends React.Component {
             addDocument={undefined}
             addDocTab={this.addDocTabFunc}
             removeDocument={undefined}
+            onClick={emptyFunction}
             ScreenToLocalTransform={Transform.Identity}
             ContentScaling={returnOne}
             PanelWidth={this.flyoutWidthFunc}
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index ebfa737be..67b8b4a8d 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -30,7 +30,7 @@ import { undoBatch } from "../../util/UndoManager";
 import { CollectionSchemaHeader, CollectionSchemaAddColumnHeader } from "./CollectionSchemaHeaders";
 import { CellProps, CollectionSchemaCell, CollectionSchemaNumberCell, CollectionSchemaStringCell, CollectionSchemaBooleanCell, CollectionSchemaCheckboxCell, CollectionSchemaDocCell } from "./CollectionSchemaCells";
 import { MovableColumn, MovableRow } from "./CollectionSchemaMovableTableHOC";
-import { ComputedField } from "../../../new_fields/ScriptField";
+import { ComputedField, ScriptField } from "../../../new_fields/ScriptField";
 import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField";
 
 
@@ -899,6 +899,7 @@ interface CollectionSchemaPreviewProps {
     height: () => number;
     showOverlays?: (doc: Doc) => { title?: string, caption?: string };
     CollectionView?: CollectionView | CollectionPDFView | CollectionVideoView;
+    onClick?: () => void | ScriptField;
     getTransform: () => Transform;
     addDocument: (document: Doc, allowDuplicates?: boolean) => boolean;
     moveDocument: (document: Doc, target: Doc, addDoc: ((doc: Doc) => boolean)) => boolean;
@@ -988,23 +989,24 @@ export class CollectionSchemaPreview extends React.Component<CollectionSchemaPre
                         DataDoc={this.props.DataDocument}
                         Document={this.props.Document}
                         fitToBox={this.props.fitToBox}
-                        renderDepth={this.props.renderDepth + 1}
-                        selectOnLoad={false}
+                        onClick={this.props.onClick}
                         showOverlays={this.props.showOverlays}
                         addDocument={this.props.addDocument}
                         removeDocument={this.props.removeDocument}
                         moveDocument={this.props.moveDocument}
+                        whenActiveChanged={this.props.whenActiveChanged}
+                        ContainingCollectionView={this.props.CollectionView}
+                        addDocTab={this.props.addDocTab}
+                        parentActive={this.props.active}
                         ScreenToLocalTransform={this.getTransform}
+                        renderDepth={this.props.renderDepth + 1}
+                        selectOnLoad={false}
                         ContentScaling={this.contentScaling}
                         PanelWidth={this.PanelWidth}
                         PanelHeight={this.PanelHeight}
-                        ContainingCollectionView={this.props.CollectionView}
                         focus={emptyFunction}
                         backgroundColor={returnEmptyString}
-                        parentActive={this.props.active}
-                        whenActiveChanged={this.props.whenActiveChanged}
                         bringToFront={emptyFunction}
-                        addDocTab={this.props.addDocTab}
                         zoomToScale={emptyFunction}
                         getScale={returnOne}
                     />
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 112d64e3d..22af98c4d 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -121,6 +121,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
             showOverlays={this.overlays}
             renderDepth={this.props.renderDepth}
             fitToBox={this.props.fitToBox}
+            onClick={this.props.onClick}
             width={width}
             height={height}
             getTransform={finalDxf}
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 7a402798e..8b939259c 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -30,6 +30,10 @@ export class CollectionView extends React.Component<FieldViewProps> {
 
     public static LayoutString(fieldStr: string = "data", fieldExt: string = "") { return FieldView.LayoutString(CollectionView, fieldStr, fieldExt); }
 
+    constructor(props:any) {
+        super(props);
+    }
+
     componentDidMount = () => {
         this._reactionDisposer = reaction(() => StrCast(this.props.Document.chromeStatus),
             () => {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index ba8dcff98..30010e826 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -37,8 +37,6 @@ import "./CollectionFreeFormView.scss";
 import { MarqueeView } from "./MarqueeView";
 import React = require("react");
 import { DocumentType, Docs } from "../../../documents/Documents";
-import { RouteStore } from "../../../../server/RouteStore";
-import { string, number, elementType } from "prop-types";
 
 library.add(faEye as any, faTable, faPaintBrush, faExpandArrowsAlt, faCompressArrowsAlt, faCompass, faUpload, faBraille, faChalkboard);
 
@@ -195,6 +193,10 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
     private get _pheight() { return this.props.PanelHeight(); }
     private inkKey = "ink";
 
+    constructor(props: any) {
+        super(props);
+    }
+
     get parentScaling() {
         return (this.props as any).ContentScaling && this.fitToBox && !this.isAnnotationOverlay ? (this.props as any).ContentScaling() : 1;
     }
@@ -631,6 +633,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
             addDocument: this.props.addDocument,
             removeDocument: this.props.removeDocument,
             moveDocument: this.props.moveDocument,
+            onClick: this.props.onClick,
             ScreenToLocalTransform: pair.layout.z ? this.getTransformOverlay : this.getTransform,
             renderDepth: this.props.renderDepth + 1,
             selectOnLoad: pair.layout[Id] === this._selectOnLoaded,
@@ -655,6 +658,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
             addDocument: this.props.addDocument,
             removeDocument: this.props.removeDocument,
             moveDocument: this.props.moveDocument,
+            onClick: this.props.onClick,
             ScreenToLocalTransform: this.getTransform,
             renderDepth: this.props.renderDepth,
             selectOnLoad: layoutDoc[Id] === this._selectOnLoaded,
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 6b7b239f0..2466f13f6 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -28,7 +28,7 @@ import { Cast, StrCast, NumCast } from "../../../new_fields/Types";
 import { List } from "../../../new_fields/List";
 import { Doc } from "../../../new_fields/Doc";
 import DirectoryImportBox from "../../util/Import & Export/DirectoryImportBox";
-import { CollectionViewType } from "../collections/CollectionBaseView";
+import { ScriptField } from "../../../new_fields/ScriptField";
 const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this?
 
 type BindingProps = Without<FieldViewProps, 'fieldKey'>;
@@ -49,6 +49,7 @@ const ObserverJsxParser: typeof JsxParser = ObserverJsxParser1 as any;
 export class DocumentContentsView extends React.Component<DocumentViewProps & {
     isSelected: () => boolean,
     select: (ctrl: boolean) => void,
+    onClick?: ScriptField,
     layoutKey: string,
     hideOnLeave?: boolean
 }> {
@@ -81,7 +82,13 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
     }
 
     CreateBindings(): JsxBindings {
-        return { props: { ...OmitKeys(this.props, ['parentActive'], (obj: any) => obj.active = this.props.parentActive).omit, Document: this.layoutDoc, DataDoc: this.dataDoc } };
+        let list = {
+            ...OmitKeys(this.props, ['parentActive'], (obj: any) => obj.active = this.props.parentActive).omit,
+            Document: this.layoutDoc,
+            DataDoc: this.dataDoc,
+            onClick: this.props.onClick
+        };
+        return { props: list };
     }
 
     @computed get templates(): List<string> {
@@ -100,10 +107,12 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
         if (this.props.renderDepth > 7) return (null);
         if (!this.layout && (this.props.layoutKey !== "overlayLayout" || !this.templates.length)) return (null);
         return <ObserverJsxParser
+            blacklistedAttrs={[]}
             components={{ FormattedTextBox, ImageBox, IconBox, DirectoryImportBox, DragBox, ButtonBox, FieldView, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, CollectionPDFView, CollectionVideoView, WebBox, KeyValueBox, PDFBox, VideoBox, AudioBox, HistogramBox, YoutubeBox }}
             bindings={this.CreateBindings()}
             jsx={this.finalLayout}
             showWarnings={true}
+
             onError={(test: any) => { console.log(test); }}
         />;
     }
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index cf16db203..b8e2eb436 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -39,7 +39,6 @@ import { FormattedTextBox } from './FormattedTextBox';
 import React = require("react");
 import { DictationManager } from '../../util/DictationManager';
 import { MainView } from '../MainView';
-import requestPromise = require('request-promise');
 import { ScriptBox } from '../ScriptBox';
 import { CompileScript } from '../../util/Scripting';
 import { DocumentIconContainer } from './DocumentIcon';
@@ -84,6 +83,7 @@ export interface DocumentViewProps {
     Document: Doc;
     DataDoc?: Doc;
     fitToBox?: boolean;
+    onClick?: ScriptField;
     addDocument?: (doc: Doc, allowDuplicates?: boolean) => boolean;
     removeDocument?: (doc: Doc) => boolean;
     moveDocument?: (doc: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => boolean;
@@ -297,8 +297,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.Document.onClick) {
-            this.Document.onClick.script.run({ this: this.props.Document });
+        if (this.onClickHandler) {
+            this.onClickHandler.script.run({ this: this.props.Document });
             e.preventDefault();
             return;
         }
@@ -687,14 +687,16 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     onPointerLeave = (e: React.PointerEvent): void => { Doc.UnBrushDoc(this.props.Document); };
 
     isSelected = () => SelectionManager.IsSelected(this);
-    @action select = (ctrlPressed: boolean) => { SelectionManager.SelectDoc(this, ctrlPressed); };
-
+    @action select = (ctrlPressed: boolean) => { SelectionManager.SelectDoc(this, ctrlPressed); }
     @computed get nativeWidth() { return this.Document.nativeWidth || 0; }
     @computed get nativeHeight() { return this.Document.nativeHeight || 0; }
+    @computed get onClickHandler() { return this.props.onClick ? this.props.onClick : this.Document.onClick; }
     @computed get contents() {
         return (<DocumentContentsView {...this.props}
             ChromeHeight={this.chromeHeight}
-            isSelected={this.isSelected} select={this.select}
+            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}
@@ -783,7 +785,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} 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} selectOnLoad={this.props.selectOnLoad} fieldExt={""} hideOnLeave={true} fieldKey={showCaption} />
                             </div>
                         }
                     </div>
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index da54ecc3a..3287949e2 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -17,7 +17,7 @@ import { IconBox } from "./IconBox";
 import { ImageBox } from "./ImageBox";
 import { PDFBox } from "./PDFBox";
 import { VideoBox } from "./VideoBox";
-import { Id } from "../../../new_fields/FieldSymbols";
+import { ScriptField } from "../../../new_fields/ScriptField";
 
 //
 // these properties get assigned through the render() method of the DocumentView when it creates this node.
@@ -32,6 +32,7 @@ export interface FieldViewProps {
     ContainingCollectionView: Opt<CollectionView | CollectionPDFView | CollectionVideoView>;
     Document: Doc;
     DataDoc?: Doc;
+    onClick?: ScriptField;
     isSelected: () => boolean;
     select: (isCtrlPressed: boolean) => void;
     renderDepth: number;
-- 
cgit v1.2.3-70-g09d2


From afec8d91ec6de13de33e2a31c987727b4cc7101d Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Tue, 13 Aug 2019 15:42:05 -0400
Subject: changes to fix stacking layouts used with templates. rearrangement of
 menu options.

---
 src/client/util/TooltipTextMenu.tsx                |   2 +-
 src/client/views/DocumentDecorations.tsx           |   4 +-
 src/client/views/InkingCanvas.tsx                  |  20 ++--
 src/client/views/ScriptBox.tsx                     |  30 +++++-
 .../views/collections/CollectionDockingView.tsx    |   4 +-
 .../views/collections/CollectionStackingView.tsx   |  57 +++++++----
 .../CollectionStackingViewFieldColumn.tsx          |   2 +-
 src/client/views/collections/CollectionView.tsx    |  19 ++--
 .../views/collections/CollectionViewChromes.tsx    |  19 ++--
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  64 ++-----------
 src/client/views/nodes/DocumentContentsView.tsx    |   3 +-
 src/client/views/nodes/DocumentView.tsx            | 105 ++++++++++++---------
 src/client/views/nodes/FormattedTextBox.tsx        |  15 +--
 src/new_fields/Doc.ts                              |   7 ++
 src/new_fields/Types.ts                            |   4 +
 15 files changed, 193 insertions(+), 162 deletions(-)

(limited to 'src/client/views/collections/CollectionView.tsx')

diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx
index 8dc5cdb2a..48f02d38a 100644
--- a/src/client/util/TooltipTextMenu.tsx
+++ b/src/client/util/TooltipTextMenu.tsx
@@ -963,7 +963,7 @@ export class TooltipTextMenu {
                 });
             }
         }
-        if (!ref_node.isLeaf) {
+        if (!ref_node.isLeaf && ref_node.childCount > 0) {
             ref_node = ref_node.child(0);
         }
         return ref_node;
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 6616d5d58..d537e2dac 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -537,12 +537,12 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
                 doc.x = (doc.x || 0) + dX * (actualdW - width);
                 doc.y = (doc.y || 0) + dY * (actualdH - height);
                 let proto = doc.isTemplate ? doc : Doc.GetProto(element.props.Document); // bcz: 'doc' didn't work here...
-                let fixedAspect = e.ctrlKey || (!BoolCast(proto.ignoreAspect) && nwidth && nheight);
+                let fixedAspect = e.ctrlKey || (!BoolCast(doc.ignoreAspect) && nwidth && nheight);
                 if (fixedAspect && (!nwidth || !nheight)) {
                     proto.nativeWidth = nwidth = doc.width || 0;
                     proto.nativeHeight = nheight = doc.height || 0;
                 }
-                if (nwidth > 0 && nheight > 0 && !BoolCast(proto.ignoreAspect)) {
+                if (nwidth > 0 && nheight > 0 && !BoolCast(doc.ignoreAspect)) {
                     if (Math.abs(dW) > Math.abs(dH)) {
                         if (!fixedAspect) {
                             Doc.SetInPlace(element.props.Document, "nativeWidth", actualdW / (doc.width || 1) * (doc.nativeWidth || 0), true);
diff --git a/src/client/views/InkingCanvas.tsx b/src/client/views/InkingCanvas.tsx
index 1c221e3df..b08133d80 100644
--- a/src/client/views/InkingCanvas.tsx
+++ b/src/client/views/InkingCanvas.tsx
@@ -165,14 +165,18 @@ export class InkingCanvas extends React.Component<InkCanvasProps> {
             }
             return paths;
         }, [] as JSX.Element[]);
-        return [<svg className={`inkingCanvas-paths-ink`} key="Pens"
-            style={{ left: `${this.inkMidX - this.maxCanvasDim}px`, top: `${this.inkMidY - this.maxCanvasDim}px` }} >
-            {paths.filter(path => path.props.tool !== InkTool.Highlighter)}
-        </svg>,
-        <svg className={`inkingCanvas-paths-markers`} key="Markers"
-            style={{ left: `${this.inkMidX - this.maxCanvasDim}px`, top: `${this.inkMidY - this.maxCanvasDim}px` }}>
-            {paths.filter(path => path.props.tool === InkTool.Highlighter)}
-        </svg>];
+        let markerPaths = paths.filter(path => path.props.tool === InkTool.Highlighter);
+        let penPaths = paths.filter(path => path.props.tool !== InkTool.Highlighter);
+        return [!penPaths.length ? (null) :
+            <svg className={`inkingCanvas-paths-ink`} key="Pens"
+                style={{ left: `${this.inkMidX - this.maxCanvasDim}px`, top: `${this.inkMidY - this.maxCanvasDim}px` }} >
+                {}
+            </svg>,
+        !markerPaths.length ? (null) :
+            <svg className={`inkingCanvas-paths-markers`} key="Markers"
+                style={{ left: `${this.inkMidX - this.maxCanvasDim}px`, top: `${this.inkMidY - this.maxCanvasDim}px` }}>
+                {}
+            </svg>];
     }
 
     render() {
diff --git a/src/client/views/ScriptBox.tsx b/src/client/views/ScriptBox.tsx
index d073945e5..2b862a81e 100644
--- a/src/client/views/ScriptBox.tsx
+++ b/src/client/views/ScriptBox.tsx
@@ -5,7 +5,11 @@ import { observable, action } from "mobx";
 import "./ScriptBox.scss";
 import { OverlayView } from "./OverlayView";
 import { DocumentIconContainer } from "./nodes/DocumentIcon";
-import { Opt } from "../../new_fields/Doc";
+import { Opt, Doc } from "../../new_fields/Doc";
+import { emptyFunction } from "../../Utils";
+import { ScriptCast } from "../../new_fields/Types";
+import { CompileScript } from "../util/Scripting";
+import { ScriptField } from "../../new_fields/ScriptField";
 
 export interface ScriptBoxProps {
     onSave: (text: string, onError: (error: string) => void) => void;
@@ -62,4 +66,26 @@ export class ScriptBox extends React.Component<ScriptBoxProps> {
             </div>
         );
     }
-}
\ No newline at end of file
+    public static EditClickScript(doc: Doc, fieldKey: string) {
+        let overlayDisposer: () => void = emptyFunction;
+        const script = ScriptCast(doc[fieldKey]);
+        let originalText: string | undefined = undefined;
+        if (script) originalText = 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;
+            }
+            doc[fieldKey] = new ScriptField(script);
+            overlayDisposer();
+        }} showDocumentIcons />;
+        overlayDisposer = OverlayView.Instance.addWindow(scriptingBox, { x: 400, y: 200, width: 500, height: 400, title: `${doc.title || ""} OnClick` });
+    }
+}
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index c0c82a44e..13bb34037 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -555,8 +555,8 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
     panelWidth = () => this._document!.ignoreAspect ? this._panelWidth : Math.min(this._panelWidth, Math.max(NumCast(this._document!.width), this.nativeWidth()));
     panelHeight = () => this._document!.ignoreAspect ? this._panelHeight : Math.min(this._panelHeight, Math.max(NumCast(this._document!.height), NumCast(this._document!.nativeHeight, this._panelHeight)));
 
-    nativeWidth = () => !BoolCast(this._document!.ignoreAspect) ? NumCast(this._document!.nativeWidth, this._panelWidth) : 0;
-    nativeHeight = () => !BoolCast(this._document!.ignoreAspect) ? NumCast(this._document!.nativeHeight, this._panelHeight) : 0;
+    nativeWidth = () => !this._document!.ignoreAspect ? NumCast(this._document!.nativeWidth) || this._panelWidth : 0;
+    nativeHeight = () => !this._document!.ignoreAspect ? NumCast(this._document!.nativeHeight) || this._panelHeight : 0;
 
     contentScaling = () => {
         const nativeH = this.nativeHeight();
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 9d2671356..ba3d8e6a7 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -9,7 +9,7 @@ import { Id } from "../../../new_fields/FieldSymbols";
 import { List } from "../../../new_fields/List";
 import { listSpec } from "../../../new_fields/Schema";
 import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField";
-import { BoolCast, Cast, NumCast, StrCast } from "../../../new_fields/Types";
+import { BoolCast, Cast, NumCast, StrCast, ScriptCast } from "../../../new_fields/Types";
 import { emptyFunction } from "../../../Utils";
 import { DocumentType } from "../../documents/Documents";
 import { DragManager } from "../../util/DragManager";
@@ -20,6 +20,9 @@ import { CollectionSchemaPreview } from "./CollectionSchemaView";
 import "./CollectionStackingView.scss";
 import { CollectionStackingViewFieldColumn } from "./CollectionStackingViewFieldColumn";
 import { CollectionSubView } from "./CollectionSubView";
+import { ContextMenu } from "../ContextMenu";
+import { ContextMenuProps } from "../ContextMenuItem";
+import { ScriptBox } from "../ScriptBox";
 
 @observer
 export class CollectionStackingView extends CollectionSubView(doc => doc) {
@@ -76,17 +79,24 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
 
     componentDidMount() {
         // is there any reason this needs to exist? -syip
-        this._heightDisposer = reaction(() => [this.props.Document.autoHeight, this.yMargin, this.props.Document[WidthSym](), this.gridGap, this.columnWidth, this.childDocs.map(d => [d.height, d.width, d.zoomBasis, d.nativeHeight, d.nativeWidth, d.isMinimized])],
-            () => {
-                if (this.singleColumn && BoolCast(this.props.Document.autoHeight)) {
-                    let hgt = this.Sections.size * 50 + this.filteredChildren.reduce((height, d, i) => {
-                        let pair = Doc.GetLayoutDataDocPair(this.props.Document, this.props.DataDoc, this.props.fieldKey, d);
-                        return height + this.getDocHeight(pair.layout) + (i === this.filteredChildren.length - 1 ? this.yMargin : this.gridGap);
-                    }, this.yMargin);
-                    (this.props.DataDoc && this.props.DataDoc.layout === this.layoutDoc ? this.props.DataDoc : this.layoutDoc)
-                        .height = hgt * (this.props as any).ContentScaling();
+        this._heightDisposer = reaction(() => {
+            if (this.singleColumn && BoolCast(this.props.Document.autoHeight)) {
+                let hgt = this.Sections.size * 50 + this.filteredChildren.reduce((height, d, i) => {
+                    let pair = Doc.GetLayoutDataDocPair(this.props.Document, this.props.DataDoc, this.props.fieldKey, d);
+                    return height + this.getDocHeight(pair.layout) + (i === this.filteredChildren.length - 1 ? this.yMargin : this.gridGap);
+                }, this.yMargin);
+                return hgt * this.props.ContentScaling();
+            }
+            return -1;
+        },
+            (hgt: number) => {
+                if (hgt !== -1) {
+                    let doc = this.props.DataDoc && this.props.DataDoc.layout === this.layoutDoc ? this.props.DataDoc : this.layoutDoc;
+                    doc.height = hgt;
                 }
-            }, { fireImmediately: true });
+            },
+            { fireImmediately: true }
+        );
 
         // reset section headers when a new filter is inputted
         this._sectionFilterDisposer = reaction(
@@ -109,9 +119,12 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
     }
 
     overlays = (doc: Doc) => {
-        return doc.type === DocumentType.IMG || doc.type === DocumentType.VID ? { title: "title", caption: "caption" } : {};
+        return doc.type === DocumentType.IMG || doc.type === DocumentType.VID ? { title: StrCast(this.props.Document.showTitles), caption: StrCast(this.props.Document.showCaptions) } : {};
     }
 
+    @computed get onChildClickHandler() { return ScriptCast(this.Document.onChildClick); }
+    @computed get onClickHandler() { return this.props.onClick ? this.props.onClick : ScriptCast(this.Document.onChildClick); }
+
     getDisplayDoc(layoutDoc: Doc, dataDoc: Doc | undefined, dxf: () => Transform, width: () => number) {
         let height = () => this.getDocHeight(layoutDoc);
         let finalDxf = () => dxf().scale(this.columnWidth / layoutDoc[WidthSym]());
@@ -121,7 +134,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
             showOverlays={this.overlays}
             renderDepth={this.props.renderDepth}
             fitToBox={this.props.fitToBox}
-            onClick={this.props.onClick}
+            onClick={layoutDoc.isTemplate ? this.onClickHandler : this.onChildClickHandler}
             width={width}
             height={height}
             getTransform={finalDxf}
@@ -141,7 +154,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
         let nh = NumCast(d.nativeHeight);
         if (!d.ignoreAspect && nw && nh) {
             let aspect = nw && nh ? nh / nw : 1;
-            let wid = this.props.Document.fillColumn ? this.columnWidth / columnScale : Math.min(d[WidthSym](), this.columnWidth / columnScale);
+            let wid = this.props.Document.fillColumn ? this.columnWidth / columnScale : Math.min(Math.min(d[WidthSym](), NumCast(d.nativeWidth)), this.columnWidth / columnScale);
             return wid * aspect;
         }
         return d[HeightSym]();
@@ -267,7 +280,19 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
     }
 
     onToggle = (checked: Boolean) => {
-        this.props.CollectionView.props.Document.chromeSatus = checked ? "collapsed" : "view-mode";
+        this.props.CollectionView.props.Document.chromeStatus = checked ? "collapsed" : "view-mode";
+    }
+
+    onContextMenu = (e: React.MouseEvent): void => {
+        // need to test if propagation has stopped because GoldenLayout forces a parallel react hierarchy to be created for its top-level layout
+        if (!e.isPropagationStopped()) {
+            let subItems: ContextMenuProps[] = [];
+            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" });
+        }
     }
 
     render() {
@@ -282,7 +307,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
         // let uniqueHeadings = headings.map((i, idx) => headings.indexOf(i) === idx);
         return (
             <div className="collectionStackingView"
-                ref={this.createRef} onDrop={this.onDrop.bind(this)} onWheel={(e: React.WheelEvent) => e.stopPropagation()} >
+                ref={this.createRef} onDrop={this.onDrop.bind(this)} onContextMenu={this.onContextMenu} onWheel={(e: React.WheelEvent) => e.stopPropagation()} >
                 {this.sectionFilter ? Array.from(this.Sections.entries()).sort(this.sortFunc).
                     map((section: [SchemaHeaderField, Doc[]]) => this.section(section[0], section[1])) :
                     this.section(undefined, this.filteredChildren)}
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index 817f9f4cb..9824a501b 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -84,7 +84,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
             let headings = this.props.headings();
             let uniqueHeadings = headings.map((i, idx) => headings.indexOf(i) === idx);
             let pair = Doc.GetLayoutDataDocPair(parent.props.Document, parent.props.DataDoc, parent.props.fieldKey, d);
-            let width = () => (d.nativeWidth && !d.ignoreAspect && !parent.props.Document.fillColumn ? Math.min(NumCast(d.nativeWidth), parent.columnWidth / (uniqueHeadings.length + 1)) : parent.columnWidth / (uniqueHeadings.length + 1));/// (uniqueHeadings.length + 1);
+            let width = () => (d.nativeWidth && !d.ignoreAspect && !parent.props.Document.fillColumn ? Math.min(Math.min(d[WidthSym](), NumCast(d.nativeWidth)), parent.columnWidth / (uniqueHeadings.length + 1)) : parent.columnWidth / (uniqueHeadings.length + 1));/// (uniqueHeadings.length + 1);
             let height = () => parent.getDocHeight(pair.layout, uniqueHeadings.length + 1);// / (d.nativeWidth && !BoolCast(d.ignoreAspect) ? uniqueHeadings.length + 1 : 1);
             let dref = React.createRef<HTMLDivElement>();
             // if (uniqueHeadings.length > 0) {
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 8b939259c..aaaa623fb 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -30,7 +30,7 @@ export class CollectionView extends React.Component<FieldViewProps> {
 
     public static LayoutString(fieldStr: string = "data", fieldExt: string = "") { return FieldView.LayoutString(CollectionView, fieldStr, fieldExt); }
 
-    constructor(props:any) {
+    constructor(props: any) {
         super(props);
     }
 
@@ -69,7 +69,7 @@ export class CollectionView extends React.Component<FieldViewProps> {
     @action
     private collapse = (value: boolean) => {
         this._collapsed = value;
-        this.props.Document.chromeStatus = value ? "collapsed" : "visible";
+        this.props.Document.chromeStatus = value ? "collapsed" : "enabled";
     }
 
     private SubView = (type: CollectionViewType, renderProps: CollectionRenderProps) => {
@@ -90,12 +90,7 @@ 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[] = [];
-            subItems.push({
-                description: "Freeform", event: () => {
-                    this.props.Document.viewType = CollectionViewType.Freeform;
-                    delete this.props.Document.usePivotLayout;
-                }, icon: "signature"
-            });
+            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" });
             }
@@ -111,10 +106,10 @@ export class CollectionView extends React.Component<FieldViewProps> {
                 }
             }
             ContextMenu.Instance.addItem({ description: "View Modes...", subitems: subItems, icon: "eye" });
-            ContextMenu.Instance.addItem({ description: "Apply Template", event: () => this.props.addDocTab && this.props.addDocTab(Doc.ApplyTemplate(this.props.Document)!, undefined, "onRight"), icon: "project-diagram" });
-            ContextMenu.Instance.addItem({
-                description: this.props.Document.chromeStatus !== "disabled" ? "Hide Chrome" : "Show Chrome", event: () => this.props.Document.chromeStatus = (this.props.Document.chromeStatus !== "disabled" ? "disabled" : "enabled"), icon: "project-diagram"
-            });
+            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" });
+            !existing && ContextMenu.Instance.addItem({ description: "Layout...", subitems: layoutItems, icon: "hand-point-right" });
         }
     }
 
diff --git a/src/client/views/collections/CollectionViewChromes.tsx b/src/client/views/collections/CollectionViewChromes.tsx
index 52c47e7e8..bccc0a5b2 100644
--- a/src/client/views/collections/CollectionViewChromes.tsx
+++ b/src/client/views/collections/CollectionViewChromes.tsx
@@ -37,7 +37,6 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
     @observable private _dateWithinValue: string = "";
     @observable private _dateValue: Date | string = "";
     @observable private _keyRestrictions: [JSX.Element, string][] = [];
-    @observable private _collapsed: boolean = false;
     @computed private get filterValue() { return Cast(this.props.CollectionView.props.Document.viewSpecScript, ScriptField); }
 
     private _picker: any;
@@ -61,7 +60,6 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
                     throw new Error("how did you get here, if chrome status is 'disabled' on a collection, a chrome shouldn't even be instantiated!");
                 }
                 else if (chromeStatus === "collapsed") {
-                    this._collapsed = true;
                     if (this.props.collapse) {
                         this.props.collapse(true);
                     }
@@ -163,9 +161,9 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
 
     @action
     toggleCollapse = () => {
-        this._collapsed = !this._collapsed;
+        this.props.CollectionView.props.Document.chromeStatus = this.props.CollectionView.props.Document.chromeStatus === "enabled" ? "collapsed" : "enabled";
         if (this.props.collapse) {
-            this.props.collapse(this._collapsed);
+            this.props.collapse(this.props.CollectionView.props.Document.chromeStatus !== "enabled");
         }
     }
 
@@ -218,16 +216,17 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
     }
 
     render() {
+        let collapsed = this.props.CollectionView.props.Document.chromeStatus !== "enabled";
         return (
-            <div className="collectionViewChrome-cont" style={{ top: this._collapsed ? -70 : 0 }}>
+            <div className="collectionViewChrome-cont" style={{ top: collapsed ? -70 : 0 }}>
                 <div className="collectionViewChrome">
                     <div className="collectionViewBaseChrome">
                         <button className="collectionViewBaseChrome-collapse"
                             style={{
-                                top: this._collapsed ? 70 : 10,
-                                transform: `rotate(${this._collapsed ? 180 : 0}deg) scale(${this._collapsed ? 0.5 : 1}) translate(${this._collapsed ? "-100%, -100%" : "0, 0"})`,
-                                opacity: (this._collapsed && !this.props.CollectionView.props.isSelected()) ? 0 : 0.9,
-                                left: (this._collapsed ? 0 : "unset"),
+                                top: collapsed ? 70 : 10,
+                                transform: `rotate(${collapsed ? 180 : 0}deg) scale(${collapsed ? 0.5 : 1}) translate(${collapsed ? "-100%, -100%" : "0, 0"})`,
+                                opacity: (collapsed && !this.props.CollectionView.props.isSelected()) ? 0 : 0.9,
+                                left: (collapsed ? 0 : "unset"),
                             }}
                             title="Collapse collection chrome" onClick={this.toggleCollapse}>
                             <FontAwesomeIcon icon="caret-up" size="2x" />
@@ -243,7 +242,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
                             <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="5">Stacking View</option>
                             <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="6">Masonry View</option>
                         </select>
-                        <div className="collectionViewBaseChrome-viewSpecs" style={{ display: this._collapsed ? "none" : "grid" }}>
+                        <div className="collectionViewBaseChrome-viewSpecs" style={{ display: collapsed ? "none" : "grid" }}>
                             <input className="collectionViewBaseChrome-viewSpecsInput"
                                 placeholder="FILTER DOCUMENTS"
                                 value={this.filterValue ? this.filterValue.script.originalScript : ""}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 30010e826..0501bf929 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -816,17 +816,8 @@ 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.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 () => {
@@ -841,50 +832,13 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
             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"
-        });
-        ContextMenu.Instance.addItem({
-            description: "Analyze Strokes",
-            event: this.analyzeStrokes,
-            icon: "paint-brush"
-        });
-        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.addDocument(doc, false);
-                };
-                input.click();
-            }
-        });
+        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" });
     }
 
 
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 2466f13f6..c73e960d8 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -85,8 +85,7 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
         let list = {
             ...OmitKeys(this.props, ['parentActive'], (obj: any) => obj.active = this.props.parentActive).omit,
             Document: this.layoutDoc,
-            DataDoc: this.dataDoc,
-            onClick: this.props.onClick
+            DataDoc: this.dataDoc
         };
         return { props: list };
     }
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index b8e2eb436..a702ab80e 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -8,13 +8,15 @@ import { Copy, Id } from '../../../new_fields/FieldSymbols';
 import { List } from "../../../new_fields/List";
 import { ObjectField } from "../../../new_fields/ObjectField";
 import { createSchema, listSpec, makeInterface } from "../../../new_fields/Schema";
-import { BoolCast, Cast, FieldValue, NumCast, StrCast, PromiseValue } from "../../../new_fields/Types";
+import { ScriptField } from '../../../new_fields/ScriptField';
+import { BoolCast, Cast, FieldValue, NumCast, 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 { ClientUtils } from '../../util/ClientUtils';
+import { DictationManager } from '../../util/DictationManager';
 import { DocumentManager } from "../../util/DocumentManager";
 import { DragManager, dropActionType } from "../../util/DragManager";
 import { LinkManager } from '../../util/LinkManager';
@@ -29,20 +31,16 @@ import { ContextMenu } from "../ContextMenu";
 import { ContextMenuProps } from '../ContextMenuItem';
 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";
 import { DocumentContentsView } from "./DocumentContentsView";
 import "./DocumentView.scss";
 import { FormattedTextBox } from './FormattedTextBox';
 import React = require("react");
-import { DictationManager } from '../../util/DictationManager';
-import { MainView } from '../MainView';
-import { ScriptBox } from '../ScriptBox';
-import { CompileScript } from '../../util/Scripting';
-import { DocumentIconContainer } from './DocumentIcon';
-import { ScriptField } from '../../../new_fields/ScriptField';
 const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this?
 
 library.add(fa.faTrash);
@@ -297,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.run({ this: this.props.Document });
+        if (this.onClickHandler && this.onClickHandler.script) {
+            this.onClickHandler.script.run({ this: this.props.Document.isTemplate && this.props.DataDoc ? this.props.DataDoc : this.props.Document });
             e.preventDefault();
             return;
         }
@@ -573,37 +571,11 @@ 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" });
-        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: BoolCast(this.props.Document.lockedPosition) ? "Unlock Position" : "Lock Position", event: this.toggleLockPosition, icon: BoolCast(this.props.Document.lockedPosition) ? "unlock" : "lock" });
-        cm.addItem({ description: "Transcribe Speech", event: this.listen, icon: "microphone" });
-        cm.addItem({
-            description: "Edit OnClick script", icon: "edit", event: () => {
-                let overlayDisposer: () => void = emptyFunction;
-                const script = this.Document.onClick;
-                let originalText: string | undefined = undefined;
-                if (script) originalText = 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;
-                    }
-                    this.Document.onClick = new ScriptField(script);
-                    overlayDisposer();
-                }} showDocumentIcons />;
-                overlayDisposer = OverlayView.Instance.addWindow(scriptingBox, { x: 400, y: 200, width: 500, height: 400, title: `${this.Document.title || ""} OnClick` });
-            }
-        });
-        let makes: ContextMenuProps[] = [];
+        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: "Make Portal", event: () => {
                 let portal = Docs.Create.FreeformDocument([], { width: this.props.Document[WidthSym]() + 10, height: this.props.Document[HeightSym](), title: this.props.Document.title + ".portal" });
@@ -618,15 +590,19 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
 
             }, icon: "window-restore"
         });
-        cm.addItem({ description: "Make...", subitems: makes, icon: "hand-point-right" });
-        if (this.props.Document.detailedLayout && !this.props.Document.isTemplate) {
-            cm.addItem({ description: "Toggle detail", event: () => Doc.ToggleDetailLayout(this.props.Document), icon: "image" });
-        }
-        cm.addItem({ description: "Add Repl", icon: "laptop-code", event: () => OverlayView.Instance.addWindow(<ScriptingRepl />, { x: 300, y: 100, width: 200, height: 200, title: "Scripting REPL" }) });
+        !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: "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) {
+            layoutItems.push({ description: "Toggle detail", event: () => Doc.ToggleDetailLayout(this.props.Document), icon: "image" });
+        }
         !existing && cm.addItem({ description: "Layout...", subitems: layoutItems, icon: "compass" });
         if (!ClientUtils.RELEASE) {
             let copies: ContextMenuProps[] = [];
@@ -634,6 +610,12 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             copies.push({ description: "Copy ID", event: () => Utils.CopyText(this.props.Document[Id]), icon: "fingerprint" });
             cm.addItem({ description: "Copy...", subitems: copies, icon: "copy" });
         }
+        let existingAnalyze = ContextMenu.Instance.findByDescription("Analyzers...");
+        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: () => PresentationView.Instance.PinDoc(this.props.Document), icon: "map-pin" });
+        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: () => {
                 const a = document.createElement("a");
@@ -643,6 +625,36 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 a.click();
             }
         });
+
+        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[] = [];
@@ -715,7 +727,10 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         return this.props.Document.layout instanceof Doc ? this.props.Document.layout : this.props.Document;
     }
 
+    @computed get brushedDegree() { return Doc.IsBrushedDegree(this.layoutDoc); }
+
     render() {
+        trace();
         let backgroundColor = this.layoutDoc.isBackground || (this.props.ContainingCollectionView && this.props.ContainingCollectionView.props.Document.clusterOverridesDefaultBackground && this.layoutDoc.backgroundColor === this.layoutDoc.defaultBackgroundColor) ?
             this.props.backgroundColor(this.layoutDoc) || StrCast(this.layoutDoc.backgroundColor) :
             StrCast(this.layoutDoc.backgroundColor) || this.props.backgroundColor(this.layoutDoc);
@@ -733,7 +748,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.layoutDoc);
+        let brushDegree = 0;//this.brushedDegree;
         return (
             <div className={`documentView-node${this.topMost ? "-topmost" : ""}`}
                 ref={this._mainCont}
@@ -779,7 +794,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                                     height={72}
                                     fontSize={12}
                                     GetValue={() => StrCast((this.layoutDoc.isTemplate || !this.dataDoc ? this.layoutDoc : this.dataDoc)[showTitle!])}
-                                    SetValue={(value: string) => (Doc.GetProto(this.layoutDoc)[showTitle!] = value) ? true : true}
+                                    SetValue={(value: string) => ((this.layoutDoc.isTemplate ? this.layoutDoc : Doc.GetProto(this.layoutDoc))[showTitle!] = value) ? true : true}
                                 />
                             </div>
                         }
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index cf4f7f668..732576328 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -475,6 +475,9 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
     }
 
     onPointerDown = (e: React.PointerEvent): void => {
+        if (this.onClick && e.button === 0) {
+            e.preventDefault();
+        }
         if (e.button === 0 && this.props.isSelected() && !e.altKey && !e.ctrlKey && !e.metaKey) {
             e.stopPropagation();
             if (FormattedTextBox._toolTipTextMenu && FormattedTextBox._toolTipTextMenu.tooltip) {
@@ -643,12 +646,12 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
     }
 
     specificContextMenu = (e: React.MouseEvent): void => {
-        let subitems: ContextMenuProps[] = [];
-        subitems.push({
-            description: BoolCast(this.props.Document.autoHeight) ? "Manual Height" : "Auto Height",
-            event: action(() => Doc.GetProto(this.props.Document).autoHeight = !BoolCast(this.props.Document.autoHeight)), icon: "expand-arrows-alt"
-        });
-        ContextMenu.Instance.addItem({ description: "Text Funcs...", subitems: subitems, icon: "text-height" });
+        // let subitems: ContextMenuProps[] = [];
+        // subitems.push({
+        //     description: BoolCast(this.props.Document.autoHeight) ? "Manual Height" : "Auto Height",
+        //     event: action(() => Doc.GetProto(this.props.Document).autoHeight = !BoolCast(this.props.Document.autoHeight)), icon: "expand-arrows-alt"
+        // });
+        // ContextMenu.Instance.addItem({ description: "Text Funcs...", subitems: subitems, icon: "text-height" });
     }
     render() {
         let self = this;
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index 4cfd7929b..564446bb8 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -516,6 +516,8 @@ export namespace Doc {
         otherdoc.miniLayout = StrCast(templateDoc.miniLayout);
         otherdoc.detailedLayout = otherdoc.layout;
         otherdoc.type = DocumentType.TEMPLATE;
+        !templateDoc.nativeWidth && (otherdoc.nativeWidth = 0);
+        !templateDoc.nativeHeight && (otherdoc.nativeHeight = 0);
         return otherdoc;
     }
     export function ApplyTemplateTo(templateDoc: Doc, target: Doc, targetData?: Doc) {
@@ -534,6 +536,8 @@ export namespace Doc {
             target.miniLayout = StrCast(templateDoc.miniLayout);
             target.detailedLayout = target.layout;
         }
+        !templateDoc.nativeWidth && (target.nativeWidth = 0);
+        !templateDoc.nativeHeight && (target.nativeHeight = 0);
     }
 
     export function MakeTemplate(fieldTemplate: Doc, metaKey: string, templateDataDoc: Doc) {
@@ -610,4 +614,7 @@ 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);
 });
\ No newline at end of file
diff --git a/src/new_fields/Types.ts b/src/new_fields/Types.ts
index 09cbff25e..0ca35fab2 100644
--- a/src/new_fields/Types.ts
+++ b/src/new_fields/Types.ts
@@ -2,6 +2,7 @@ import { Field, Opt, FieldResult, Doc } from "./Doc";
 import { List } from "./List";
 import { RefField } from "./RefField";
 import { DateField } from "./DateField";
+import { ScriptField } from "./ScriptField";
 
 export type ToType<T extends InterfaceValue> =
     T extends "string" ? string :
@@ -86,6 +87,9 @@ export function BoolCast(field: FieldResult, defaultVal: boolean | null = false)
 export function DateCast(field: FieldResult) {
     return Cast(field, DateField, null);
 }
+export function ScriptCast(field: FieldResult) {
+    return Cast(field, ScriptField, null);
+}
 
 type WithoutList<T extends Field> = T extends List<infer R> ? (R extends RefField ? (R | Promise<R>)[] : R[]) : T;
 
-- 
cgit v1.2.3-70-g09d2


From f73b29df2e6d928a3c67a9028326250bb0aa6a93 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Tue, 13 Aug 2019 22:58:47 -0400
Subject: added forceActive flag for collections

---
 src/client/views/collections/CollectionBaseView.tsx | 2 +-
 src/client/views/collections/CollectionView.tsx     | 1 +
 2 files changed, 2 insertions(+), 1 deletion(-)

(limited to 'src/client/views/collections/CollectionView.tsx')

diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx
index cad87ebcc..4a296493a 100644
--- a/src/client/views/collections/CollectionBaseView.tsx
+++ b/src/client/views/collections/CollectionBaseView.tsx
@@ -83,7 +83,7 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
 
     active = (): boolean => {
         var isSelected = this.props.isSelected();
-        return isSelected || this._isChildActive || this.props.renderDepth === 0 || BoolCast(this.props.Document.excludeFromLibrary);
+        return isSelected || BoolCast(this.props.Document.forceActive) || this._isChildActive || this.props.renderDepth === 0 || BoolCast(this.props.Document.excludeFromLibrary);
     }
 
     //TODO should this be observable?
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index aaaa623fb..7e1adaa19 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -109,6 +109,7 @@ export class CollectionView extends React.Component<FieldViewProps> {
             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" });
         }
     }
-- 
cgit v1.2.3-70-g09d2

