From 3a4a7ba1f35eeba7d48c954f80a92c6904e5d065 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Fri, 12 Jul 2019 11:35:25 -0400
Subject: playing with videos

---
 src/client/views/nodes/WebBox.tsx | 35 ++++++++++++++++++++++++++++++++++-
 1 file changed, 34 insertions(+), 1 deletion(-)

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

diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 98c57fc75..96b972a1c 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -6,12 +6,45 @@ import { InkingControl } from "../InkingControl";
 import { FieldView, FieldViewProps } from './FieldView';
 import "./WebBox.scss";
 import React = require("react");
+import { InkTool } from "../../../new_fields/InkField";
+import { Cast, FieldValue, NumCast } from "../../../new_fields/Types";
 
+export function onYouTubeIframeAPIReady() {
+    console.log("player");
+    return;
+    let player = new YT.Player('player', {
+        events: {
+            'onReady': onPlayerReady
+        }
+    });
+}
+// must cast as any to set property on window
+const _global = (window /* browser */ || global /* node */) as any
+_global.onYouTubeIframeAPIReady = onYouTubeIframeAPIReady;
+
+function onPlayerReady(event: any) {
+    event.target.playVideo();
+}
 @observer
 export class WebBox extends React.Component<FieldViewProps> {
 
     public static LayoutString() { return FieldView.LayoutString(WebBox); }
 
+    componentWillMount() {
+
+        let field = Cast(this.props.Document[this.props.fieldKey], WebField);
+        if (field && field.url.href.indexOf("youtube") !== -1) {
+            let youtubeaspect = 400 / 315;
+            var nativeWidth = NumCast(this.props.Document.nativeWidth, 0);
+            var nativeHeight = NumCast(this.props.Document.nativeHeight, 0);
+            if (!nativeWidth || !nativeHeight || Math.abs(nativeWidth / nativeHeight - youtubeaspect) > 0.05) {
+                if (!nativeWidth) this.props.Document.nativeWidth = 600;
+                this.props.Document.nativeHeight = NumCast(this.props.Document.nativeWidth) / youtubeaspect;
+                this.props.Document.height = NumCast(this.props.Document.width) / youtubeaspect;
+            }
+        }
+    }
+
     _ignore = 0;
     onPreWheel = (e: React.WheelEvent) => {
         this._ignore = e.timeStamp;
@@ -46,7 +79,7 @@ export class WebBox extends React.Component<FieldViewProps> {
 
         let frozen = !this.props.isSelected() || DocumentDecorations.Instance.Interacting;
 
-        let classname = "webBox-cont" + (this.props.isSelected() && !InkingControl.Instance.selectedTool && !DocumentDecorations.Instance.Interacting ? "-interactive" : "");
+        let classname = "webBox-cont" + (this.props.isSelected() && InkingControl.Instance.selectedTool === InkTool.None && !DocumentDecorations.Instance.Interacting ? "-interactive" : "");
         return (
             <>
                 <div className={classname}  >
-- 
cgit v1.2.3-70-g09d2


From 533c48010ca668a944962bd61cd65115fc5a6fa6 Mon Sep 17 00:00:00 2001
From: Sam Wilkins <samuel_wilkins@brown.edu>
Date: Tue, 16 Jul 2019 12:04:21 -0400
Subject: linter and error fixes

---
 src/client/documents/Documents.ts                      | 2 +-
 src/client/views/InkingControl.tsx                     | 4 ++--
 src/client/views/Main.tsx                              | 2 +-
 src/client/views/collections/CollectionDockingView.tsx | 4 ++--
 src/client/views/collections/CollectionTreeView.tsx    | 2 +-
 src/client/views/nodes/ImageBox.tsx                    | 2 +-
 src/client/views/nodes/VideoBox.tsx                    | 4 ++--
 src/client/views/nodes/WebBox.tsx                      | 2 +-
 src/client/views/search/SearchBox.tsx                  | 2 +-
 9 files changed, 12 insertions(+), 12 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index ada9f3610..177810444 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -98,7 +98,7 @@ export namespace Docs {
 
         type LayoutSource = { LayoutString: () => string };
         type CollectionLayoutSource = { LayoutString: (fieldStr: string, fieldExt?: string) => string };
-        type CollectionViewType = [CollectionLayoutSource, string, string?]
+        type CollectionViewType = [CollectionLayoutSource, string, string?];
         type PrototypeTemplate = {
             layout: {
                 view: LayoutSource,
diff --git a/src/client/views/InkingControl.tsx b/src/client/views/InkingControl.tsx
index c7f7bdb66..1910e409b 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 { ColorResult } from 'react-color';
+import { ColorState } 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: ColorResult): void => {
+    switchColor = action((color: ColorState): 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/Main.tsx b/src/client/views/Main.tsx
index 80399e24b..86578af3e 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -26,7 +26,7 @@ let swapDocs = async () => {
         }
         CurrentUserUtils.UserDocument.linkManagerDoc = undefined;
     }
-}
+};
 
 (async () => {
     const info = await CurrentUserUtils.loadCurrentUser();
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 1069ebbdb..a193ff677 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -301,7 +301,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
             this._flush = false;
             setTimeout(() => {
                 CollectionDockingView.Instance._ignoreStateChange = JSON.stringify(CollectionDockingView.Instance._goldenLayout.toConfig());
-                this.stateChanged()
+                this.stateChanged();
             }, 10);
         }
     }
@@ -606,7 +606,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
             addDocTab={this.addDocTab}
             ContainingCollectionView={undefined}
             zoomToScale={emptyFunction}
-            getScale={returnOne} />
+            getScale={returnOne} />;
     }
 
     @computed get content() {
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 0196fecff..c212cc97c 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -153,7 +153,7 @@ class TreeView extends React.Component<TreeViewProps> {
         let docList = Cast(this.resolvedDataDoc[this.fieldKey], listSpec(Doc));
         let doc = Cast(this.resolvedDataDoc[this.fieldKey], Doc);
         let isDoc = doc instanceof Doc || docList;
-        let c
+        let c;
         return <div className="bullet" onClick={action(() => this._collapsed = !this._collapsed)} style={{ color: StrCast(this.props.document.color, "black"), opacity: 0.4 }}>
             {<FontAwesomeIcon icon={this._collapsed ? (isDoc ? "caret-square-right" : "caret-right") : (isDoc ? "caret-square-down" : "caret-down")} />}
         </div>;
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 73ae8955d..1df955f1f 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -33,7 +33,7 @@ export const pageSchema = createSchema({
     curPage: "number",
 });
 
-interface window {
+interface Window {
     MediaRecorder: MediaRecorder;
 }
 
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 66844cdd6..e90efb31c 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -108,7 +108,7 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
             this._youtubeReactionDisposer = reaction(() => [this.props.isSelected(), DocumentDecorations.Instance.Interacting, InkingControl.Instance.selectedTool], () => {
                 let interactive = InkingControl.Instance.selectedTool === InkTool.None && this.props.isSelected() && !DocumentDecorations.Instance.Interacting;
                 this._youtubePlayer.getIframe().style.pointerEvents = interactive ? "all" : "none";
-            }, { fireImmediately: true })
+            }, { fireImmediately: true });
             // let iframe = $(document.getElementById(`${videoid}-player`)!);
             // iframe.on("load", function () {
             //     iframe.contents().find("head")
@@ -120,7 +120,7 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
     @action
     onYoutubePlayerStateChange = (event: any) => {
         console.log("event.data = " + event.data);
-        this.Playing = event.data == YT.PlayerState.PLAYING;
+        this.Playing = event.data === YT.PlayerState.PLAYING;
         if (this._youtubeSeekTo && this.Playing) {
             this._youtubePlayer.pauseVideo();
             this._youtubeSeekTo = false;
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 96b972a1c..f0a9ec6d8 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -19,7 +19,7 @@ export function onYouTubeIframeAPIReady() {
     });
 }
 // must cast as any to set property on window
-const _global = (window /* browser */ || global /* node */) as any
+const _global = (window /* browser */ || global /* node */) as any;
 _global.onYouTubeIframeAPIReady = onYouTubeIframeAPIReady;
 
 function onPlayerReady(event: any) {
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index 3830a6d16..ec778b346 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -282,7 +282,7 @@ export class SearchBox extends React.Component {
 
     @computed
     get resFull() {
-        console.log(this._numTotalResults)
+        console.log(this._numTotalResults);
         return this._numTotalResults <= 8;
     }
 
-- 
cgit v1.2.3-70-g09d2


From 3442776999d1d7b7bced9bbed601ead637a43cc7 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Mon, 22 Jul 2019 10:24:37 -0400
Subject: merged stacking sections branch.

---
 src/client/views/DocumentDecorations.tsx           |  10 +-
 .../views/collections/CollectionBaseView.tsx       |   6 +-
 .../views/collections/CollectionStackingView.scss  |  13 +-
 .../views/collections/CollectionStackingView.tsx   | 247 ---------------------
 src/client/views/collections/CollectionView.tsx    |  28 ++-
 src/client/views/nodes/WebBox.tsx                  |  16 --
 src/new_fields/Doc.ts                              |  33 ++-
 7 files changed, 64 insertions(+), 289 deletions(-)
 delete mode 100644 src/client/views/collections/CollectionStackingView.tsx

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

diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index fb5104915..2f7bea365 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -28,6 +28,7 @@ import { RichTextField } from '../../new_fields/RichTextField';
 import { LinkManager } from '../util/LinkManager';
 import { ObjectField } from '../../new_fields/ObjectField';
 import { MetadataEntryMenu } from './MetadataEntryMenu';
+import { ImageBox } from './nodes/ImageBox';
 const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
@@ -85,8 +86,13 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
                 SelectionManager.DeselectAll();
                 let fieldTemplate = fieldTemplateView.props.Document;
                 let docTemplate = fieldTemplateView.props.ContainingCollectionView!.props.Document;
-                let metaKey = text.slice(1, text.length);
-                Doc.MakeTemplate(fieldTemplate, metaKey, Doc.GetProto(docTemplate));
+                let metaKey = text.startsWith(">>") ? text.slice(2, text.length) : text.slice(1, text.length);
+                let proto = Doc.GetProto(docTemplate);
+                Doc.MakeTemplate(fieldTemplate, metaKey, proto);
+                if (text.startsWith(">>")) {
+                    proto.detailedLayout = proto.layout;
+                    proto.miniLayout = ImageBox.LayoutString(metaKey);
+                }
             }
             else {
                 if (SelectionManager.SelectedDocuments().length > 0) {
diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx
index eba69b448..72faf52c4 100644
--- a/src/client/views/collections/CollectionBaseView.tsx
+++ b/src/client/views/collections/CollectionBaseView.tsx
@@ -18,7 +18,8 @@ export enum CollectionViewType {
     Schema,
     Docking,
     Tree,
-    Stacking
+    Stacking,
+    Masonry
 }
 
 export interface CollectionRenderProps {
@@ -78,7 +79,6 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
 
     @action.bound
     addDocument(doc: Doc, allowDuplicates: boolean = false): boolean {
-        let self = this;
         var curPage = NumCast(this.props.Document.curPage, -1);
         Doc.GetProto(doc).page = curPage;
         if (curPage >= 0) {
@@ -146,7 +146,7 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
         const viewtype = this.collectionViewType;
         return (
             <div id="collectionBaseView"
-                style={{ boxShadow: `#9c9396 ${StrCast(this.props.Document.boxShadow, "0.2vw 0.2vw 0.8vw")}` }}
+                style={{ overflow: "auto", boxShadow: `#9c9396 ${StrCast(this.props.Document.boxShadow, "0.2vw 0.2vw 0.8vw")}` }}
                 className={this.props.className || "collectionView-cont"}
                 onContextMenu={this.props.onContextMenu} ref={this.props.contentRef}>
                 {viewtype !== undefined ? this.props.children(viewtype, props) : (null)}
diff --git a/src/client/views/collections/CollectionStackingView.scss b/src/client/views/collections/CollectionStackingView.scss
index 7e886304d..7ebf5f77c 100644
--- a/src/client/views/collections/CollectionStackingView.scss
+++ b/src/client/views/collections/CollectionStackingView.scss
@@ -1,7 +1,9 @@
 @import "../globalCssVariables";
 .collectionStackingView {
+    height: 100%;
+    width: 100%;
+    position: absolute;
     overflow-y: auto;
-
     .collectionStackingView-docView-container {
         width: 45%;
         margin: 5% 2.5%;
@@ -71,4 +73,13 @@
         grid-column-end: span 1;
         height: 100%;
     }
+    .collectionStackingView-sectionHeader {    
+        width: 90%;
+        background: gray;
+        text-align: center;
+        margin-left: 5%;
+        margin-right: 5%;
+        color: white;
+        margin-top: 10px;
+    }
 }
\ No newline at end of file
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
deleted file mode 100644
index 6d9e942c9..000000000
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ /dev/null
@@ -1,247 +0,0 @@
-import React = require("react");
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action, computed, IReactionDisposer, reaction, untracked, observable, runInAction } from "mobx";
-import { observer } from "mobx-react";
-import { Doc, HeightSym, WidthSym } from "../../../new_fields/Doc";
-import { Id } from "../../../new_fields/FieldSymbols";
-import { BoolCast, NumCast, Cast } from "../../../new_fields/Types";
-import { emptyFunction, Utils } from "../../../Utils";
-import { ContextMenu } from "../ContextMenu";
-import { CollectionSchemaPreview } from "./CollectionSchemaView";
-import "./CollectionStackingView.scss";
-import { CollectionSubView } from "./CollectionSubView";
-import { undoBatch } from "../../util/UndoManager";
-import { DragManager } from "../../util/DragManager";
-import { DocumentType } from "../../documents/Documents";
-import { Transform } from "../../util/Transform";
-import { CursorProperty } from "csstype";
-
-@observer
-export class CollectionStackingView extends CollectionSubView(doc => doc) {
-    _masonryGridRef: HTMLDivElement | null = null;
-    _draggerRef = React.createRef<HTMLDivElement>();
-    _heightDisposer?: IReactionDisposer;
-    _gridSize = 1;
-    _docXfs: any[] = [];
-    @observable private cursor: CursorProperty = "grab";
-    @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); }
-
-    componentDidMount() {
-        this._heightDisposer = reaction(() => [this.yMargin, this.gridGap, this.columnWidth, this.childDocs.map(d => [d.height, d.width, d.zoomBasis, d.nativeHeight, d.nativeWidth, d.isMinimized])],
-            () => this.singleColumn &&
-                (this.props.Document.height = this.filteredChildren.reduce((height, d, i) =>
-                    height + this.getDocHeight(d) + (i === this.filteredChildren.length - 1 ? this.yMargin : this.gridGap), this.yMargin))
-            , { fireImmediately: true });
-    }
-    componentWillUnmount() {
-        if (this._heightDisposer) this._heightDisposer();
-    }
-
-    @action
-    moveDocument = (doc: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean): boolean => {
-        return this.props.removeDocument(doc) && addDocument(doc);
-    }
-    createRef = (ele: HTMLDivElement | null) => {
-        this._masonryGridRef = ele;
-        this.createDropTarget(ele!);
-    }
-
-    overlays = (doc: Doc) => {
-        return doc.type === DocumentType.IMG || doc.type === DocumentType.VID ? { title: "title", caption: "caption" } : {};
-    }
-
-    getDisplayDoc(layoutDoc: Doc, d: Doc, dxf: () => Transform) {
-        let resolvedDataDoc = !this.props.Document.isTemplate && this.props.DataDoc !== this.props.Document ? this.props.DataDoc : undefined;
-        let width = () => d.nativeWidth ? Math.min(layoutDoc[WidthSym](), this.columnWidth) : this.columnWidth;
-        let height = () => this.getDocHeight(layoutDoc);
-        let finalDxf = () => dxf().scale(this.columnWidth / layoutDoc[WidthSym]());
-        return <CollectionSchemaPreview
-            Document={layoutDoc}
-            DataDocument={resolvedDataDoc}
-            showOverlays={this.overlays}
-            renderDepth={this.props.renderDepth}
-            width={width}
-            height={height}
-            getTransform={finalDxf}
-            CollectionView={this.props.CollectionView}
-            addDocument={this.props.addDocument}
-            moveDocument={this.props.moveDocument}
-            removeDocument={this.props.removeDocument}
-            active={this.props.active}
-            whenActiveChanged={this.props.whenActiveChanged}
-            addDocTab={this.props.addDocTab}
-            setPreviewScript={emptyFunction}
-            previewScript={undefined}>
-        </CollectionSchemaPreview>;
-    }
-    getDocHeight(d: Doc) {
-        let nw = NumCast(d.nativeWidth);
-        let nh = NumCast(d.nativeHeight);
-        let aspect = nw && nh ? nh / nw : 1;
-        let wid = Math.min(d[WidthSym](), this.columnWidth);
-        return (nw && nh) ? wid * aspect : d[HeightSym]();
-    }
-
-
-    offsetTransform(doc: Doc, translateX: number, translateY: number) {
-        let outerXf = Utils.GetScreenTransform(this._masonryGridRef!);
-        let offset = this.props.ScreenToLocalTransform().transformDirection(outerXf.translateX - translateX, outerXf.translateY - translateY);
-        return this.props.ScreenToLocalTransform().translate(offset[0], offset[1]).scale(NumCast(doc.width, 1) / this.columnWidth);
-    }
-    getDocTransform(doc: Doc, dref: HTMLDivElement) {
-        let { scale, translateX, translateY } = Utils.GetScreenTransform(dref);
-        return this.offsetTransform(doc, translateX, translateY);
-    }
-    getSingleDocTransform(doc: Doc, ind: number, width: number) {
-        let localY = this.filteredChildren.reduce((height, d, i) =>
-            height + (i < ind ? this.getDocHeight(Doc.expandTemplateLayout(d, this.props.DataDoc)) + this.gridGap : 0), this.yMargin);
-        let translate = this.props.ScreenToLocalTransform().inverse().transformPoint((this.props.PanelWidth() - width) / 2, localY);
-        return this.offsetTransform(doc, translate[0], translate[1]);
-    }
-
-    @computed
-    get children() {
-        this._docXfs.length = 0;
-        return this.filteredChildren.map((d, i) => {
-            let layoutDoc = Doc.expandTemplateLayout(d, this.props.DataDoc);
-            let width = () => d.nativeWidth ? Math.min(layoutDoc[WidthSym](), this.columnWidth) : this.columnWidth;
-            let height = () => this.getDocHeight(layoutDoc);
-            if (this.singleColumn) {
-                let dxf = () => this.getSingleDocTransform(layoutDoc, i, width());
-                let rowHgtPcnt = height() / (this.props.Document[HeightSym]() - 2 * this.yMargin) * 100;
-                this._docXfs.push({ dxf: dxf, width: width, height: height });
-                return <div className="collectionStackingView-columnDoc" key={d[Id]} style={{ width: width(), marginTop: i === 0 ? 0 : this.gridGap, height: `${rowHgtPcnt}%` }} >
-                    {this.getDisplayDoc(layoutDoc, d, dxf)}
-                </div>;
-            } else {
-                let dref = React.createRef<HTMLDivElement>();
-                let dxf = () => this.getDocTransform(layoutDoc, dref.current!);
-                let rowSpan = Math.ceil((height() + this.gridGap) / (this._gridSize + this.gridGap));
-                this._docXfs.push({ dxf: dxf, width: width, height: height });
-                return <div className="collectionStackingView-masonryDoc" key={d[Id]} ref={dref} style={{ gridRowEnd: `span ${rowSpan}` }} >
-                    {this.getDisplayDoc(layoutDoc, d, dxf)}
-                </div>;
-            }
-        });
-    }
-
-    _columnStart: number = 0;
-    columnDividerDown = (e: React.PointerEvent) => {
-        e.stopPropagation();
-        e.preventDefault();
-        runInAction(() => this.cursor = "grabbing");
-        document.addEventListener("pointermove", this.onDividerMove);
-        document.addEventListener('pointerup', this.onDividerUp);
-        this._columnStart = this.props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY)[0];
-    }
-    @action
-    onDividerMove = (e: PointerEvent): void => {
-        let dragPos = this.props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY)[0];
-        let delta = dragPos - this._columnStart;
-        this._columnStart = dragPos;
-
-        this.props.Document.columnWidth = this.columnWidth + delta;
-    }
-
-    @action
-    onDividerUp = (e: PointerEvent): void => {
-        runInAction(() => this.cursor = "grab");
-        document.removeEventListener("pointermove", this.onDividerMove);
-        document.removeEventListener('pointerup', this.onDividerUp);
-    }
-
-    @computed get columnDragger() {
-        return <div className="collectionStackingView-columnDragger" onPointerDown={this.columnDividerDown} ref={this._draggerRef} style={{ cursor: this.cursor, left: `${this.columnWidth + this.xMargin}px` }} >
-            <FontAwesomeIcon icon={"arrows-alt-h"} />
-        </div>;
-    }
-    onContextMenu = (e: React.MouseEvent): void => {
-        if (!e.isPropagationStopped() && this.props.Document[Id] !== "mainDoc") { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7
-            ContextMenu.Instance.addItem({
-                description: "Toggle multi-column",
-                event: () => this.props.Document.singleColumn = !BoolCast(this.props.Document.singleColumn, true), icon: "file-pdf"
-            });
-        }
-    }
-
-    @undoBatch
-    @action
-    drop = (e: Event, de: DragManager.DropEvent) => {
-        let targInd = -1;
-        let where = [de.x, de.y];
-        if (de.data instanceof DragManager.DocumentDragData) {
-            this._docXfs.map((cd, i) => {
-                let pos = cd.dxf().inverse().transformPoint(-2 * this.gridGap, -2 * this.gridGap);
-                let pos1 = cd.dxf().inverse().transformPoint(cd.width(), cd.height());
-                if (where[0] > pos[0] && where[0] < pos1[0] && where[1] > pos[1] && where[1] < pos1[1]) {
-                    targInd = i;
-                }
-            });
-        }
-        if (super.drop(e, de)) {
-            let newDoc = de.data.droppedDocuments[0];
-            let docs = this.childDocList;
-            if (docs) {
-                if (targInd === -1) targInd = docs.length;
-                else targInd = docs.indexOf(this.filteredChildren[targInd]);
-                let srcInd = docs.indexOf(newDoc);
-                docs.splice(srcInd, 1);
-                docs.splice(targInd > srcInd ? targInd - 1 : targInd, 0, newDoc);
-            }
-        }
-        return false;
-    }
-    @undoBatch
-    @action
-    onDrop = (e: React.DragEvent): void => {
-        let where = [e.clientX, e.clientY];
-        let targInd = -1;
-        this._docXfs.map((cd, i) => {
-            let pos = cd.dxf().inverse().transformPoint(-2 * this.gridGap, -2 * this.gridGap);
-            let pos1 = cd.dxf().inverse().transformPoint(cd.width(), cd.height());
-            if (where[0] > pos[0] && where[0] < pos1[0] && where[1] > pos[1] && where[1] < pos1[1]) {
-                targInd = i;
-            }
-        });
-        super.onDrop(e, {}, () => {
-            if (targInd !== -1) {
-                let newDoc = this.childDocs[this.childDocs.length - 1];
-                let docs = this.childDocList;
-                if (docs) {
-                    docs.splice(docs.length - 1, 1);
-                    docs.splice(targInd, 0, newDoc);
-                }
-            }
-        });
-    }
-    render() {
-        let cols = this.singleColumn ? 1 : Math.max(1, Math.min(this.filteredChildren.length,
-            Math.floor((this.props.PanelWidth() - 2 * this.xMargin) / (this.columnWidth + this.gridGap))));
-        let templatecols = "";
-        for (let i = 0; i < cols; i++) templatecols += `${this.columnWidth}px `;
-        return (
-            <div className="collectionStackingView" ref={this.createRef} onDrop={this.onDrop.bind(this)} onContextMenu={this.onContextMenu} onWheel={(e: React.WheelEvent) => e.stopPropagation()} >
-                <div className={`collectionStackingView-masonry${this.singleColumn ? "Single" : "Grid"}`}
-                    style={{
-                        padding: this.singleColumn ? `${this.yMargin}px ${this.xMargin}px ${this.yMargin}px ${this.xMargin}px` : `${this.yMargin}px ${this.xMargin}px`,
-                        margin: "auto",
-                        width: this.singleColumn ? undefined : `${cols * (this.columnWidth + this.gridGap) + 2 * this.xMargin - this.gridGap}px`,
-                        height: "100%",
-                        position: "relative",
-                        gridGap: this.gridGap,
-                        gridTemplateColumns: this.singleColumn ? undefined : templatecols,
-                        gridAutoRows: this.singleColumn ? undefined : `${this._gridSize}px`
-                    }}
-                >
-                    {this.children}
-                    {this.singleColumn ? (null) : this.columnDragger}
-                </div>
-            </div>
-        );
-    }
-}
\ No newline at end of file
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 56750668d..045c8531e 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -1,11 +1,10 @@
 import { library } from '@fortawesome/fontawesome-svg-core';
-import { faProjectDiagram, faSignature, faSquare, faTh, faThList, faTree } from '@fortawesome/free-solid-svg-icons';
+import { faProjectDiagram, faSignature, faColumns, faSquare, faTh, faImage, faThList, faTree, faEllipsisV } from '@fortawesome/free-solid-svg-icons';
 import { observer } from "mobx-react";
 import * as React from 'react';
-import { Doc } from '../../../new_fields/Doc';
+import { Doc, DocListCast, WidthSym, HeightSym } from '../../../new_fields/Doc';
 import { Id } from '../../../new_fields/FieldSymbols';
 import { CurrentUserUtils } from '../../../server/authentication/models/current_user_utils';
-import { Docs } from '../../documents/Documents';
 import { undoBatch } from '../../util/UndoManager';
 import { ContextMenu } from "../ContextMenu";
 import { ContextMenuProps } from '../ContextMenuItem';
@@ -16,6 +15,8 @@ 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';
 export const COLLECTION_BORDER_WIDTH = 2;
 
 library.add(faTh);
@@ -24,6 +25,9 @@ library.add(faSquare);
 library.add(faProjectDiagram);
 library.add(faSignature);
 library.add(faThList);
+library.add(faColumns);
+library.add(faEllipsisV);
+library.add(faImage);
 
 @observer
 export class CollectionView extends React.Component<FieldViewProps> {
@@ -35,7 +39,8 @@ export class CollectionView extends React.Component<FieldViewProps> {
             case CollectionViewType.Schema: return (<CollectionSchemaView {...props} CollectionView={this} />);
             case CollectionViewType.Docking: return (<CollectionDockingView {...props} CollectionView={this} />);
             case CollectionViewType.Tree: return (<CollectionTreeView {...props} CollectionView={this} />);
-            case CollectionViewType.Stacking: return (<CollectionStackingView {...props} CollectionView={this} />);
+            case CollectionViewType.Stacking: { this.props.Document.singleColumn = true; return (<CollectionStackingView {...props} CollectionView={this} />); }
+            case CollectionViewType.Masonry: { this.props.Document.singleColumn = false; return (<CollectionStackingView {...props} CollectionView={this} />); }
             case CollectionViewType.Freeform:
             default:
                 return (<CollectionFreeFormView {...props} CollectionView={this} />);
@@ -45,6 +50,7 @@ export class CollectionView extends React.Component<FieldViewProps> {
 
     get isAnnotationOverlay() { return this.props.fieldExt ? true : false; }
 
+    static _applyCount: number = 0;
     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[] = [];
@@ -54,15 +60,19 @@ export class CollectionView extends React.Component<FieldViewProps> {
             }
             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: "th-list" });
+            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" });
             ContextMenu.Instance.addItem({ description: "View Modes...", subitems: subItems });
             ContextMenu.Instance.addItem({
                 description: "Apply Template", event: undoBatch(() => {
                     let otherdoc = new Doc();
-                    otherdoc.width = 100;
-                    otherdoc.height = 50;
-                    Doc.GetProto(otherdoc).title = "applied(" + this.props.Document.title + ")";
-                    Doc.GetProto(otherdoc).layout = Doc.MakeDelegate(this.props.Document);
+                    otherdoc.width = this.props.Document[WidthSym]();
+                    otherdoc.height = this.props.Document[HeightSym]();
+                    otherdoc.title = this.props.Document.title + "(..." + CollectionView._applyCount++ + ")"; // previously "applied"
+                    otherdoc.layout = Doc.MakeDelegate(this.props.Document);
+                    otherdoc.miniLayout = StrCast(this.props.Document.miniLayout);
+                    otherdoc.detailedLayout = otherdoc.layout;
+                    otherdoc.type = DocumentType.TEMPLATE;
                     this.props.addDocTab && this.props.addDocTab(otherdoc, undefined, "onRight");
                 }), icon: "project-diagram"
             });
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index f0a9ec6d8..162ac1d98 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -9,22 +9,6 @@ import React = require("react");
 import { InkTool } from "../../../new_fields/InkField";
 import { Cast, FieldValue, NumCast } from "../../../new_fields/Types";
 
-export function onYouTubeIframeAPIReady() {
-    console.log("player");
-    return;
-    let player = new YT.Player('player', {
-        events: {
-            'onReady': onPlayerReady
-        }
-    });
-}
-// must cast as any to set property on window
-const _global = (window /* browser */ || global /* node */) as any;
-_global.onYouTubeIframeAPIReady = onYouTubeIframeAPIReady;
-
-function onPlayerReady(event: any) {
-    event.target.playVideo();
-}
 @observer
 export class WebBox extends React.Component<FieldViewProps> {
 
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index 59e61023f..2ad6ae5f0 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -10,6 +10,7 @@ import { RefField, FieldId } from "./RefField";
 import { ToScriptString, SelfProxy, Parent, OnUpdate, Self, HandleUpdate, Update, Id } from "./FieldSymbols";
 import { scriptingGlobal } from "../client/util/Scripting";
 import { List } from "./List";
+import { DocumentType } from "../client/documents/Documents";
 import { ComputedField } from "./ScriptField";
 
 export namespace Field {
@@ -317,7 +318,7 @@ export namespace Doc {
         if (extensionDoc === undefined) {
             setTimeout(() => {
                 let docExtensionForField = new Doc(doc[Id] + fieldKey, true);
-                docExtensionForField.title = "Extension of " + doc.title + "'s field:" + fieldKey;
+                docExtensionForField.title = doc.title + ":" + fieldKey + ".ext";
                 docExtensionForField.extendsDoc = doc;
                 let proto: Doc | undefined = doc;
                 while (proto && !Doc.IsPrototype(proto)) {
@@ -345,20 +346,23 @@ export namespace Doc {
         // ... which means we change the layout to be an expanded view of the template layout.  
         // This allows the view override the template's properties and be referenceable as its own document.
 
-        let expandedTemplateLayout = templateLayoutDoc["_expanded_" + dataDoc[Id]];
+        let expandedTemplateLayout = dataDoc[templateLayoutDoc[Id]];
         if (expandedTemplateLayout instanceof Doc) {
             return expandedTemplateLayout;
         }
         if (expandedTemplateLayout === undefined && BoolCast(templateLayoutDoc.isTemplate)) {
             setTimeout(() => {
-                templateLayoutDoc["_expanded_" + dataDoc[Id]] = Doc.MakeDelegate(templateLayoutDoc);
-                (templateLayoutDoc["_expanded_" + dataDoc[Id]] as Doc).title = templateLayoutDoc.title + " applied to " + dataDoc.title;
-                (templateLayoutDoc["_expanded_" + dataDoc[Id]] as Doc).isExpandedTemplate = templateLayoutDoc;
+                let expandedDoc = Doc.MakeDelegate(templateLayoutDoc);
+                expandedDoc.title = templateLayoutDoc.title + "[" + StrCast(dataDoc.title).match(/\.\.\.[0-9]*/) + "]";
+                expandedDoc.isExpandedTemplate = templateLayoutDoc;
+                dataDoc[templateLayoutDoc[Id]] = expandedDoc;
             }, 0);
         }
-        return templateLayoutDoc;
+        return templateLayoutDoc; // use the templateLayout when it's not a template or the expandedTemplate is pending.
     }
 
+    let _pendingExpansions: Map<string, boolean> = new Map();
+
     export function MakeCopy(doc: Doc, copyProto: boolean = false): Doc {
         const copy = new Doc;
         Object.keys(doc).forEach(key => {
@@ -386,12 +390,12 @@ export namespace Doc {
     export function MakeDelegate(doc: Doc, id?: string): Doc;
     export function MakeDelegate(doc: Opt<Doc>, id?: string): Opt<Doc>;
     export function MakeDelegate(doc: Opt<Doc>, id?: string): Opt<Doc> {
-        if (!doc) {
-            return undefined;
+        if (doc) {
+            const delegate = new Doc(id, true);
+            delegate.proto = doc;
+            return delegate;
         }
-        const delegate = new Doc(id, true);
-        delegate.proto = doc;
-        return delegate;
+        return undefined;
     }
 
     export function MakeTemplate(fieldTemplate: Doc, metaKey: string, proto: Doc) {
@@ -421,4 +425,11 @@ export namespace Doc {
         fieldTemplate.showTitle = "title";
         setTimeout(() => fieldTemplate.proto = proto);
     }
+
+    export async function ToggleDetailLayout(d: Doc) {
+        let miniLayout = await PromiseValue(d.miniLayout);
+        let detailLayout = await PromiseValue(d.detailedLayout);
+        d.layout !== miniLayout ? miniLayout && (d.layout = d.miniLayout) : detailLayout && (d.layout = detailLayout);
+        if (d.layout === detailLayout) Doc.GetProto(d).nativeWidth = Doc.GetProto(d).nativeHeight = undefined;
+    }
 }
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2

