From 460cec4d786e026dabdb8fb873284f6574799367 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Thu, 3 Oct 2019 17:52:35 -0400
Subject: start of embedding documents  in text notes.

---
 src/client/views/DocumentDecorations.tsx | 36 --------------------------------
 1 file changed, 36 deletions(-)

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

diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 9d42eb719..26ffaf3a6 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -48,7 +48,6 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
     private _resizeBorderWidth = 16;
     private _linkBoxHeight = 20 + 3; // link button height + margin
     private _titleHeight = 20;
-    private _embedButton = React.createRef<HTMLDivElement>();
     private _downX = 0;
     private _downY = 0;
     private _iconDoc?: Doc = undefined;
@@ -414,41 +413,6 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
     }
 
 
-    onEmbedButtonDown = (e: React.PointerEvent): void => {
-        e.stopPropagation();
-        document.removeEventListener("pointermove", this.onEmbedButtonMoved);
-        document.addEventListener("pointermove", this.onEmbedButtonMoved);
-        document.removeEventListener("pointerup", this.onEmbedButtonUp);
-        document.addEventListener("pointerup", this.onEmbedButtonUp);
-    }
-
-
-
-    onEmbedButtonUp = (e: PointerEvent): void => {
-        document.removeEventListener("pointermove", this.onEmbedButtonMoved);
-        document.removeEventListener("pointerup", this.onEmbedButtonUp);
-        e.stopPropagation();
-    }
-
-    @action
-    onEmbedButtonMoved = (e: PointerEvent): void => {
-        if (this._embedButton.current !== null) {
-            document.removeEventListener("pointermove", this.onEmbedButtonMoved);
-            document.removeEventListener("pointerup", this.onEmbedButtonUp);
-
-            let dragDocView = SelectionManager.SelectedDocuments()[0];
-            let dragData = new DragManager.EmbedDragData(dragDocView.props.Document);
-
-            DragManager.StartEmbedDrag(dragDocView.ContentDiv!, dragData, e.x, e.y, {
-                handlers: {
-                    dragComplete: action(emptyFunction),
-                },
-                hideSource: false
-            });
-        }
-        e.stopPropagation();
-    }
-
     onPointerMove = (e: PointerEvent): void => {
         e.stopPropagation();
         e.preventDefault();
-- 
cgit v1.2.3-70-g09d2


From 54f2067dbadb66e22249c1572bdc5d6d097f41d1 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Fri, 4 Oct 2019 17:01:09 -0400
Subject: restored tooltiptextmenu

---
 src/client/util/RichTextRules.ts            |  8 +++--
 src/client/util/RichTextSchema.tsx          |  1 -
 src/client/util/SelectionManager.ts         |  2 --
 src/client/util/TooltipTextMenu.tsx         | 45 +++++++++++++++--------------
 src/client/views/DocumentButtonBar.tsx      |  1 -
 src/client/views/DocumentDecorations.tsx    |  4 +--
 src/client/views/nodes/FormattedTextBox.tsx | 35 +++++-----------------
 7 files changed, 40 insertions(+), 56 deletions(-)

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

diff --git a/src/client/util/RichTextRules.ts b/src/client/util/RichTextRules.ts
index cd37ea0bb..2d5ec1743 100644
--- a/src/client/util/RichTextRules.ts
+++ b/src/client/util/RichTextRules.ts
@@ -92,8 +92,10 @@ export const inpRules = {
                 let heading = NumCast(FormattedTextBox.InputBoxOverlay!.props.Document.heading);
                 if (ruleProvider && heading) {
                     ruleProvider["ruleAlign_" + heading] = "left";
+                    return node ? state.tr.deleteRange(start, end).setStoredMarks([...node.marks, ...(sm ? sm : [])]) : state.tr;
                 }
-                return node ? state.tr.deleteRange(start, end).setStoredMarks([...node.marks, ...(sm ? sm : [])]) : state.tr;
+                return node ? state.tr.replaceRangeWith(start, end, schema.nodes.paragraph.create({ align: "left" })).setStoredMarks([...node.marks, ...(sm ? sm : [])]) :
+                    state.tr;
             }),
         new InputRule(
             new RegExp(/^\]\]\s$/),
@@ -104,8 +106,10 @@ export const inpRules = {
                 let heading = NumCast(FormattedTextBox.InputBoxOverlay!.props.Document.heading);
                 if (ruleProvider && heading) {
                     ruleProvider["ruleAlign_" + heading] = "right";
+                    return node ? state.tr.deleteRange(start, end).setStoredMarks([...node.marks, ...(sm ? sm : [])]) : state.tr;
                 }
-                return node ? state.tr.deleteRange(start, end).setStoredMarks([...node.marks, ...(sm ? sm : [])]) : state.tr;
+                return node ? state.tr.replaceRangeWith(start, end, schema.nodes.paragraph.create({ align: "right" })).setStoredMarks([...node.marks, ...(sm ? sm : [])]) :
+                    state.tr;
             }),
         new InputRule(
             new RegExp(/\^f\s$/),
diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx
index 53eaf9ce2..948a3c5bd 100644
--- a/src/client/util/RichTextSchema.tsx
+++ b/src/client/util/RichTextSchema.tsx
@@ -864,7 +864,6 @@ export class FootnoteView {
         if (this.innerView) this.close();
     }
     open() {
-        if (!this.outerView.isOverlay) return;
         // Append a tooltip to the outer node
         let tooltip = this.dom.appendChild(document.createElement("div"));
         tooltip.className = "footnote-tooltip";
diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index a02a270ee..df1b46b33 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -27,7 +27,6 @@ export namespace SelectionManager {
             } else if (!ctrlPressed && manager.SelectedDocuments.length > 1) {
                 manager.SelectedDocuments.map(dv => dv !== docView && dv.props.whenActiveChanged(false));
                 manager.SelectedDocuments = [docView];
-                FormattedTextBox.InputBoxOverlay = undefined;
             }
         }
         @action
@@ -42,7 +41,6 @@ export namespace SelectionManager {
         DeselectAll(): void {
             manager.SelectedDocuments.map(dv => dv.props.whenActiveChanged(false));
             manager.SelectedDocuments = [];
-            FormattedTextBox.InputBoxOverlay = undefined;
         }
     }
 
diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx
index a83a3949d..9116ef995 100644
--- a/src/client/util/TooltipTextMenu.tsx
+++ b/src/client/util/TooltipTextMenu.tsx
@@ -10,7 +10,6 @@ import { Doc, Field, Opt } from "../../new_fields/Doc";
 import { Id } from "../../new_fields/FieldSymbols";
 import { Utils } from "../../Utils";
 import { DocServer } from "../DocServer";
-import { CollectionDockingView } from "../views/collections/CollectionDockingView";
 import { FieldViewProps } from "../views/nodes/FieldView";
 import { FormattedTextBoxProps } from "../views/nodes/FormattedTextBox";
 import { DocumentManager } from "./DocumentManager";
@@ -28,10 +27,10 @@ export class TooltipTextMenu {
 
     public tooltip: HTMLElement;
     private view: EditorView;
+    private editorProps: FieldViewProps & FormattedTextBoxProps | undefined;
     private fontStyles: MarkType[];
     private fontSizes: MarkType[];
     private listTypes: (NodeType | any)[];
-    private editorProps: FieldViewProps & FormattedTextBoxProps;
     private fontSizeToNum: Map<MarkType, number>;
     private fontStylesToName: Map<MarkType, string>;
     private listTypeToIcon: Map<NodeType | any, string>;
@@ -59,9 +58,8 @@ export class TooltipTextMenu {
 
     private _collapsed: boolean = false;
 
-    constructor(view: EditorView, editorProps: FieldViewProps & FormattedTextBoxProps) {
+    constructor(view: EditorView) {
         this.view = view;
-        this.editorProps = editorProps;
 
         this.wrapper = document.createElement("div");
         this.tooltip = document.createElement("div");
@@ -120,10 +118,10 @@ export class TooltipTextMenu {
             //pointer down handler to activate button effects
             dom.addEventListener("pointerdown", e => {
                 e.preventDefault();
-                view.focus();
+                this.view.focus();
                 if (dom.contains(e.target as Node)) {
                     e.stopPropagation();
-                    command(view.state, view.dispatch, view);
+                    command(this.view.state, this.view.dispatch, this.view);
                     // if (this.view.state.selection.empty) {
                     //     if (dom.style.color === "white") { dom.style.color = "greenyellow"; }
                     //     else { dom.style.color = "white"; }
@@ -188,12 +186,10 @@ export class TooltipTextMenu {
 
         this.updateListItemDropdown(":", this.listTypeBtnDom);
 
-        this.update(view, undefined);
-
-        // add tooltip to outerdiv to circumvent scaling problem
-        const outer_div = this.editorProps.outer_div;
-        outer_div && outer_div(this.wrapper);
+        this.update(view, undefined, undefined);
+        TooltipTextMenu.Toolbar = this.wrapper;
     }
+    public static Toolbar: HTMLDivElement | undefined;
 
     //label of dropdown will change to given label
     updateFontSizeDropdown(label: string) {
@@ -275,7 +271,7 @@ export class TooltipTextMenu {
                                 if (DocumentManager.Instance.getDocumentView(f)) {
                                     DocumentManager.Instance.getDocumentView(f)!.props.focus(f, false);
                                 }
-                                else this.editorProps.addDocTab(f, undefined, "onRight");
+                                else this.editorProps && this.editorProps.addDocTab(f, undefined, "onRight");
                             }
                         }));
                     }
@@ -293,6 +289,7 @@ export class TooltipTextMenu {
             this.linkDrag.style.background = "black";
             this.linkDrag.style.cssFloat = "left";
             this.linkDrag.onpointerdown = (e: PointerEvent) => {
+                if (!this.editorProps) return;
                 let dragData = new DragManager.LinkDragData(this.editorProps.Document);
                 dragData.dontClearTextBox = true;
                 // hack to get source context -sy
@@ -503,19 +500,23 @@ export class TooltipTextMenu {
             if (markType.name[0] === 'p') {
                 let size = this.fontSizeToNum.get(markType);
                 if (size) { this.updateFontSizeDropdown(String(size) + " pt"); }
-                let ruleProvider = this.editorProps.ruleProvider;
-                let heading = NumCast(this.editorProps.Document.heading);
-                if (ruleProvider && heading) {
-                    ruleProvider["ruleSize_" + heading] = size;
+                if (this.editorProps) {
+                    let ruleProvider = this.editorProps.ruleProvider;
+                    let heading = NumCast(this.editorProps.Document.heading);
+                    if (ruleProvider && heading) {
+                        ruleProvider["ruleSize_" + heading] = size;
+                    }
                 }
             }
             else {
                 let fontName = this.fontStylesToName.get(markType);
                 if (fontName) { this.updateFontStyleDropdown(fontName); }
-                let ruleProvider = this.editorProps.ruleProvider;
-                let heading = NumCast(this.editorProps.Document.heading);
-                if (ruleProvider && heading) {
-                    ruleProvider["ruleFont_" + heading] = fontName;
+                if (this.editorProps) {
+                    let ruleProvider = this.editorProps.ruleProvider;
+                    let heading = NumCast(this.editorProps.Document.heading);
+                    if (ruleProvider && heading) {
+                        ruleProvider["ruleFont_" + heading] = fontName;
+                    }
                 }
             }
             //actually apply font
@@ -849,8 +850,10 @@ export class TooltipTextMenu {
     }
 
     //updates the tooltip menu when the selection changes
-    update(view: EditorView, lastState: EditorState | undefined) {
+    update(view: EditorView, lastState: EditorState | undefined, props: any) {
+        this.view = view;
         let state = view.state;
+        props && (this.editorProps = props);
         // Don't do anything if the document/selection didn't change
         if (lastState && lastState.doc.eq(state.doc) &&
             lastState.selection.eq(state.selection)) return;
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index e57745b86..9e2d41621 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -140,7 +140,6 @@ export class DocumentButtonBar extends React.Component<{ views: DocumentView[],
             let selDoc = this.props.views[0];
             let container = selDoc.props.ContainingCollectionDoc ? selDoc.props.ContainingCollectionDoc.proto : undefined;
             let dragData = new DragManager.LinkDragData(selDoc.props.Document, container ? [container] : []);
-            FormattedTextBox.InputBoxOverlay = undefined;
             this._linkDrag = UndoManager.StartBatch("Drag Link");
             DragManager.StartLinkDrag(this._linkerButton.current, dragData, e.pageX, e.pageY, {
                 handlers: {
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 26ffaf3a6..b7ec27957 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -24,6 +24,7 @@ import { FieldView } from "./nodes/FieldView";
 import { FormattedTextBox } from "./nodes/FormattedTextBox";
 import { IconBox } from "./nodes/IconBox";
 import React = require("react");
+import { TooltipTextMenu } from '../util/TooltipTextMenu';
 const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
@@ -466,7 +467,6 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
                 break;
         }
 
-        if (!this._resizing) runInAction(() => FormattedTextBox.InputBoxOverlay = undefined);
         SelectionManager.SelectedDocuments().forEach(element => {
             if (dX !== 0 || dY !== 0 || dW !== 0 || dH !== 0) {
                 let doc = PositionDocument(element.props.Document);
@@ -578,7 +578,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
                 zIndex: SelectionManager.SelectedDocuments().length > 1 ? 900 : 0,
             }} onPointerDown={this.onBackgroundDown} onContextMenu={(e: React.MouseEvent) => { e.preventDefault(); e.stopPropagation(); }} >
             </div>
-            <div className="documentDecorations-container" style={{
+            <div className="documentDecorations-container" ref={(r) => r && TooltipTextMenu.Toolbar && Array.from(r.childNodes).indexOf(TooltipTextMenu.Toolbar) === -1 && r.appendChild(TooltipTextMenu.Toolbar)} style={{
                 width: (bounds.r - bounds.x + this._resizeBorderWidth) + "px",
                 height: (bounds.b - bounds.y + this._resizeBorderWidth + this._linkBoxHeight + this._titleHeight + 3) + "px",
                 left: bounds.x - this._resizeBorderWidth / 2,
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 05904e1e7..cbdb0503b 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -116,8 +116,8 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
         return "";
     }
 
-    public static getToolTip() {
-        return this._toolTipTextMenu;
+    public static getToolTip(ev: EditorView) {
+        return this._toolTipTextMenu ? this._toolTipTextMenu : this._toolTipTextMenu = new TooltipTextMenu(ev, undefined);
     }
 
     @undoBatch
@@ -143,29 +143,6 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
 
     @computed get dataDoc() { return this.props.DataDoc && this.props.Document.isTemplate ? this.props.DataDoc : Doc.GetProto(this.props.Document); }
 
-    // this should be internal to prosemirror, but is needed
-    // here to make sure that footnote view nodes in the overlay editor
-    // get removed when they're not selected.
-
-    syncNodeSelection(view: any, sel: any) {
-        if (sel instanceof NodeSelection) {
-            var desc = view.docView.descAt(sel.from);
-            if (desc !== view.lastSelectedViewDesc) {
-                if (view.lastSelectedViewDesc) {
-                    view.lastSelectedViewDesc.deselectNode();
-                    view.lastSelectedViewDesc = null;
-                }
-                if (desc) { desc.selectNode(); }
-                view.lastSelectedViewDesc = desc;
-            }
-        } else {
-            if (view.lastSelectedViewDesc) {
-                view.lastSelectedViewDesc.deselectNode();
-                view.lastSelectedViewDesc = null;
-            }
-        }
-    }
-
     linkOnDeselect: Map<string, string> = new Map();
 
     doLinkOnDeselect() {
@@ -211,7 +188,6 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
             }
             const state = this._editorView.state.apply(tx);
             this._editorView.updateState(state);
-            this.syncNodeSelection(this._editorView, this._editorView.state.selection); // bcz: ugh -- shouldn't be needed but without this the overlay view's footnote popup doesn't get deselected
             if (state.selection.empty && FormattedTextBox._toolTipTextMenu && tx.storedMarks) {
                 FormattedTextBox._toolTipTextMenu.mark_key_pressed(tx.storedMarks);
             }
@@ -808,8 +784,10 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
         }
     }
 
+    static InputBoxOverlay: FormattedTextBox | undefined;
     @action
     onFocused = (e: React.FocusEvent): void => {
+        FormattedTextBox.InputBoxOverlay = this;
         document.removeEventListener("keypress", this.recordKeyHandler);
         document.addEventListener("keypress", this.recordKeyHandler);
         this.tryUpdateHeight();
@@ -901,7 +879,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
         let self = FormattedTextBox;
         return new Plugin({
             view(_editorView) {
-                return self._toolTipTextMenu = new TooltipTextMenu(_editorView, myprops);
+                return self._toolTipTextMenu = FormattedTextBox.getToolTip(_editorView);
             }
         });
     }
@@ -955,6 +933,9 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
         let interactive: "all" | "none" = InkingControl.Instance.selectedTool || this.props.Document.isBackground
             ? "none" : "all";
         Doc.UpdateDocumentExtensionForField(this.dataDoc, this.props.fieldKey);
+        if (this.props.isSelected()) {
+            FormattedTextBox._toolTipTextMenu!.update(this._editorView!, undefined, this.props);
+        }
         return (
             <div className={`formattedTextBox-cont-${style}`} ref={this._ref}
                 style={{
-- 
cgit v1.2.3-70-g09d2


From 2485a9f0f32968040a79f757118dfb6fad8930fd Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Mon, 7 Oct 2019 17:13:48 -0400
Subject: fixes for toolbar and text footnote/comments.

---
 src/client/util/RichTextSchema.tsx                 |  2 +-
 src/client/util/TooltipTextMenu.tsx                |  8 +++++---
 src/client/views/DocumentDecorations.tsx           |  9 ++++++++-
 src/client/views/nodes/FormattedTextBox.tsx        |  9 +++++----
 src/client/views/nodes/FormattedTextBoxComment.tsx | 10 ++++++----
 5 files changed, 25 insertions(+), 13 deletions(-)

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

diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx
index 432e0c2fb..06b97fa68 100644
--- a/src/client/util/RichTextSchema.tsx
+++ b/src/client/util/RichTextSchema.tsx
@@ -168,7 +168,7 @@ export const nodes: { [index: string]: NodeSpec } = {
             width: { default: 200 },
             height: { default: 100 },
             title: { default: null },
-            float: { default: "left" },
+            float: { default: "right" },
             location: { default: "onRight" },
             docid: { default: "" }
         },
diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx
index 55c6e6609..2732b708d 100644
--- a/src/client/util/TooltipTextMenu.tsx
+++ b/src/client/util/TooltipTextMenu.tsx
@@ -19,6 +19,7 @@ import { schema } from "./RichTextSchema";
 import "./TooltipTextMenu.scss";
 import { Cast, NumCast } from '../../new_fields/Types';
 import { updateBullets } from './ProsemirrorExampleTransfer';
+import { DocumentDecorations } from '../views/DocumentDecorations';
 const { toggleMark, setBlockType } = require("prosemirror-commands");
 const { openPrompt, TextField } = require("./ProsemirrorCopy/prompt.js");
 
@@ -186,7 +187,7 @@ export class TooltipTextMenu {
 
         this.updateListItemDropdown(":", this.listTypeBtnDom);
 
-        this.updateInternal(view, undefined, undefined);
+        this.updateFromDash(view, undefined, undefined);
         TooltipTextMenu.Toolbar = this.wrapper;
     }
     public static Toolbar: HTMLDivElement | undefined;
@@ -849,11 +850,12 @@ export class TooltipTextMenu {
         }
     }
 
-    update(view: EditorView, lastState: EditorState | undefined) { this.updateInternal(view, lastState, this.editorProps) }
+    update(view: EditorView, lastState: EditorState | undefined) { this.updateFromDash(view, lastState, this.editorProps) }
     //updates the tooltip menu when the selection changes
-    private updateInternal(view: EditorView, lastState: EditorState | undefined, props: any) {
+    public updateFromDash(view: EditorView, lastState: EditorState | undefined, props: any) {
         this.view = view;
         let state = view.state;
+        DocumentDecorations.Instance.setTextBar(DocumentDecorations.Instance.TextBar);
         props && (this.editorProps = props);
         // Don't do anything if the document/selection didn't change
         if (lastState && lastState.doc.eq(state.doc) &&
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index b7ec27957..4f9bdbe9c 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -546,6 +546,13 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
         return "-unset-";
     }
 
+    TextBar: HTMLDivElement | undefined;
+    setTextBar = (ele: HTMLDivElement) => {
+        if (ele) {
+            this.TextBar = ele;
+            TooltipTextMenu.Toolbar && Array.from(ele.childNodes).indexOf(TooltipTextMenu.Toolbar) === -1 && ele.appendChild(TooltipTextMenu.Toolbar);
+        }
+    }
     render() {
         var bounds = this.Bounds;
         let seldoc = SelectionManager.SelectedDocuments().length ? SelectionManager.SelectedDocuments()[0] : undefined;
@@ -578,7 +585,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
                 zIndex: SelectionManager.SelectedDocuments().length > 1 ? 900 : 0,
             }} onPointerDown={this.onBackgroundDown} onContextMenu={(e: React.MouseEvent) => { e.preventDefault(); e.stopPropagation(); }} >
             </div>
-            <div className="documentDecorations-container" ref={(r) => r && TooltipTextMenu.Toolbar && Array.from(r.childNodes).indexOf(TooltipTextMenu.Toolbar) === -1 && r.appendChild(TooltipTextMenu.Toolbar)} style={{
+            <div className="documentDecorations-container" ref={this.setTextBar} style={{
                 width: (bounds.r - bounds.x + this._resizeBorderWidth) + "px",
                 height: (bounds.b - bounds.y + this._resizeBorderWidth + this._linkBoxHeight + this._titleHeight + 3) + "px",
                 left: bounds.x - this._resizeBorderWidth / 2,
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 966f183b0..20edbdb23 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -267,7 +267,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
             let node = schema.nodes.dashDoc.create({
                 width: target[WidthSym](), height: target[HeightSym](),
                 title: "dashDoc", docid: alias[Id],
-                float: "none"
+                float: "right"
             });
             let pos = this._editorView!.posAtCoords({ left: de.x, top: de.y });
             link && this._editorView!.dispatch(this._editorView!.state.tr.insert(pos!.pos, node));
@@ -759,7 +759,9 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
     }
 
     onPointerUp = (e: React.PointerEvent): void => {
-        FormattedTextBoxComment.textBox = this;
+        if (!(e.nativeEvent as any).formattedHandled) { FormattedTextBoxComment.textBox = this; }
+        (e.nativeEvent as any).formattedHandled = true;
+
         if (e.buttons === 1 && this.props.isSelected() && !e.altKey) {
             e.stopPropagation();
         }
@@ -837,7 +839,6 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
                 }
             }
         }
-        this._editorView!.focus();
     }
     onMouseUp = (e: React.MouseEvent): void => {
         e.stopPropagation();
@@ -914,7 +915,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
             ? "none" : "all";
         Doc.UpdateDocumentExtensionForField(this.dataDoc, this.props.fieldKey);
         if (this.props.isSelected()) {
-            FormattedTextBox._toolTipTextMenu!.update(this._editorView!, undefined, this.props);
+            FormattedTextBox._toolTipTextMenu!.updateFromDash(this._editorView!, undefined, this.props);
         }
         return (
             <div className={`formattedTextBox-cont-${style}`} ref={this._ref}
diff --git a/src/client/views/nodes/FormattedTextBoxComment.tsx b/src/client/views/nodes/FormattedTextBoxComment.tsx
index 2d2fff06d..a75bfd373 100644
--- a/src/client/views/nodes/FormattedTextBoxComment.tsx
+++ b/src/client/views/nodes/FormattedTextBoxComment.tsx
@@ -7,6 +7,7 @@ import { schema } from "../../util/RichTextSchema";
 import { DocServer } from "../../DocServer";
 import { Utils } from "../../../Utils";
 import { StrCast } from "../../../new_fields/Types";
+import { FormattedTextBox } from "./FormattedTextBox";
 
 export let formattedTextBoxCommentPlugin = new Plugin({
     view(editorView) { return new FormattedTextBoxComment(editorView); }
@@ -49,7 +50,7 @@ export class FormattedTextBoxComment {
     static end: number;
     static mark: Mark;
     static opened: boolean;
-    static textBox: any;
+    static textBox: FormattedTextBox | undefined;
     constructor(view: any) {
         if (!FormattedTextBoxComment.tooltip) {
             const root = document.getElementById("root");
@@ -62,9 +63,9 @@ export class FormattedTextBoxComment {
             FormattedTextBoxComment.tooltip.style.pointerEvents = "all";
             FormattedTextBoxComment.tooltip.appendChild(input);
             FormattedTextBoxComment.tooltip.onpointerdown = (e: PointerEvent) => {
-                let keep = e.target && (e.target as any).type === "checkbox";
+                let keep = e.target && (e.target as any).type === "checkbox" ? true : false;
                 FormattedTextBoxComment.opened = keep || !FormattedTextBoxComment.opened;
-                FormattedTextBoxComment.textBox && FormattedTextBoxComment.textBox.setAnnotation(
+                FormattedTextBoxComment.textBox && FormattedTextBoxComment.start !== undefined && FormattedTextBoxComment.textBox.setAnnotation(
                     FormattedTextBoxComment.start, FormattedTextBoxComment.end, FormattedTextBoxComment.mark,
                     FormattedTextBoxComment.opened, keep);
             };
@@ -92,8 +93,9 @@ export class FormattedTextBoxComment {
         if (lastState && lastState.doc.eq(state.doc) &&
             lastState.selection.eq(state.selection)) return;
 
+        if (!FormattedTextBoxComment.textBox || !FormattedTextBoxComment.textBox.props.isSelected()) return;
         let set = "none";
-        if (state.selection.$from) {
+        if (FormattedTextBoxComment.textBox && state.selection.$from) {
             let nbef = findStartOfMark(state.selection.$from, view, findOtherUserMark);
             let naft = findEndOfMark(state.selection.$from, view, findOtherUserMark);
             const spos = state.selection.$from.pos - nbef;
-- 
cgit v1.2.3-70-g09d2


From 77d66d159d75442ff5635c4bf4843b6155883cc2 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Thu, 10 Oct 2019 14:04:22 -0400
Subject: removed CollectionVideoView

---
 src/client/documents/Documents.ts                  |   3 +-
 src/client/util/DocumentManager.ts                 |   5 +-
 src/client/util/SharingManager.tsx                 |   3 +-
 src/client/views/DocumentDecorations.tsx           |   2 +-
 src/client/views/PreviewCursor.tsx                 |  18 +--
 .../views/collections/CollectionBaseView.tsx       |  15 +-
 .../views/collections/CollectionSchemaCells.tsx    |   7 +-
 .../views/collections/CollectionSchemaView.tsx     |   9 +-
 src/client/views/collections/CollectionSubView.tsx |  17 +-
 .../views/collections/CollectionVideoView.scss     |  51 ------
 .../views/collections/CollectionVideoView.tsx      | 115 -------------
 .../CollectionFreeFormLinkView.tsx                 |   2 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |   8 +-
 .../collections/collectionFreeForm/MarqueeView.tsx |  14 +-
 src/client/views/linking/LinkFollowBox.tsx         |   2 +-
 src/client/views/nodes/DocumentContentsView.tsx    |   3 +-
 src/client/views/nodes/DocumentView.tsx            |   7 +-
 src/client/views/nodes/FieldView.tsx               |   5 +-
 src/client/views/nodes/PDFBox.tsx                  |   2 +-
 src/client/views/nodes/VideoBox.scss               |  57 ++++++-
 src/client/views/nodes/VideoBox.tsx                | 177 +++++++++++++++++++--
 src/client/views/nodes/WebBox.tsx                  |   2 +-
 src/client/views/pdf/PDFViewer.tsx                 |  18 ++-
 23 files changed, 284 insertions(+), 258 deletions(-)
 delete mode 100644 src/client/views/collections/CollectionVideoView.scss
 delete mode 100644 src/client/views/collections/CollectionVideoView.tsx

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 9d1a6ed3e..22aa74634 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -1,7 +1,6 @@
 import { HistogramField } from "../northstar/dash-fields/HistogramField";
 import { HistogramBox } from "../northstar/dash-nodes/HistogramBox";
 import { HistogramOperation } from "../northstar/operations/HistogramOperation";
-import { CollectionVideoView } from "../views/collections/CollectionVideoView";
 import { CollectionView } from "../views/collections/CollectionView";
 import { CollectionViewType } from "../views/collections/CollectionBaseView";
 import { AudioBox } from "../views/nodes/AudioBox";
@@ -137,7 +136,7 @@ export namespace Docs {
                 options: { height: 150 }
             }],
             [DocumentType.VID, {
-                layout: { view: VideoBox, collectionView: [CollectionVideoView, data, anno] as CollectionViewType },
+                layout: { view: VideoBox, ext: anno },
                 options: { currentTimecode: 0 },
             }],
             [DocumentType.AUDIO, {
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index c95d923cb..24285a70a 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -3,7 +3,6 @@ import { Doc, DocListCastAsync } from '../../new_fields/Doc';
 import { Id } from '../../new_fields/FieldSymbols';
 import { Cast, NumCast, StrCast } from '../../new_fields/Types';
 import { CollectionDockingView } from '../views/collections/CollectionDockingView';
-import { CollectionVideoView } from '../views/collections/CollectionVideoView';
 import { CollectionView } from '../views/collections/CollectionView';
 import { DocumentView } from '../views/nodes/DocumentView';
 import { LinkManager } from './LinkManager';
@@ -56,7 +55,7 @@ export class DocumentManager {
         return this.getDocumentViewsById(doc[Id]);
     }
 
-    public getDocumentViewById(id: string, preferredCollection?: CollectionView | CollectionVideoView): DocumentView | undefined {
+    public getDocumentViewById(id: string, preferredCollection?: CollectionView): DocumentView | undefined {
 
         let toReturn: DocumentView | undefined;
         let passes = preferredCollection ? [preferredCollection, undefined] : [undefined];
@@ -81,7 +80,7 @@ export class DocumentManager {
         return toReturn;
     }
 
-    public getDocumentView(toFind: Doc, preferredCollection?: CollectionView | CollectionVideoView): DocumentView | undefined {
+    public getDocumentView(toFind: Doc, preferredCollection?: CollectionView): DocumentView | undefined {
         return this.getDocumentViewById(toFind[Id], preferredCollection);
     }
 
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index c989b6c17..d37cd1b80 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -17,7 +17,6 @@ import * as fa from '@fortawesome/free-solid-svg-icons';
 import { DocumentView } from "../views/nodes/DocumentView";
 import { SelectionManager } from "./SelectionManager";
 import { DocumentManager } from "./DocumentManager";
-import { CollectionVideoView } from "../views/collections/CollectionVideoView";
 import { CollectionView } from "../views/collections/CollectionView";
 
 library.add(fa.faCopy);
@@ -185,7 +184,7 @@ export default class SharingManager extends React.Component<{}> {
                 className={"focus-span"}
                 title={title}
                 onClick={() => {
-                    let context: Opt<CollectionVideoView | CollectionView>;
+                    let context: Opt<CollectionView>;
                     if (this.targetDoc && this.targetDocView && (context = this.targetDocView.props.ContainingCollectionView)) {
                         DocumentManager.Instance.jumpToDocument(this.targetDoc, true, undefined, context.props.Document);
                     }
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 4f9bdbe9c..1d9f0c74b 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -333,7 +333,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
         iconDoc.y = NumCast(doc.y) - 24;
         iconDoc.maximizedDocs = new List<Doc>(selected.map(s => s.props.Document));
         selected.length === 1 && (doc.minimizedDoc = iconDoc);
-        selected[0].props.addDocument && selected[0].props.addDocument(iconDoc, false);
+        selected[0].props.addDocument && selected[0].props.addDocument(iconDoc);
         return iconDoc;
     }
     @action
diff --git a/src/client/views/PreviewCursor.tsx b/src/client/views/PreviewCursor.tsx
index 1aed51e64..eed2cc5da 100644
--- a/src/client/views/PreviewCursor.tsx
+++ b/src/client/views/PreviewCursor.tsx
@@ -14,7 +14,7 @@ export class PreviewCursor extends React.Component<{}> {
     static _onKeyPress?: (e: KeyboardEvent) => void;
     static _getTransform: () => Transform;
     static _addLiveTextDoc: (doc: Doc) => void;
-    static _addDocument: (doc: Doc, allowDuplicates: false) => boolean;
+    static _addDocument: (doc: Doc) => boolean;
     @observable static _clickPoint = [0, 0];
     @observable public static Visible = false;
     //when focus is lost, this will remove the preview cursor
@@ -44,7 +44,7 @@ export class PreviewCursor extends React.Component<{}> {
                             title: url, width: 400, height: 315,
                             nativeWidth: 600, nativeHeight: 472.5,
                             x: newPoint[0], y: newPoint[1]
-                        }), false);
+                        }));
                         return;
                     }
 
@@ -56,7 +56,7 @@ export class PreviewCursor extends React.Component<{}> {
                             title: url, width: 300, height: 300,
                             // nativeWidth: 300, nativeHeight: 472.5,
                             x: newPoint[0], y: newPoint[1]
-                        }), false);
+                        }));
                         return;
                     }
 
@@ -79,11 +79,11 @@ export class PreviewCursor extends React.Component<{}> {
 
                     let img: Doc = Docs.Create.ImageDocument(
                         arr[1], {
-                            width: 300, title: arr[1],
-                            x: newPoint[0],
-                            y: newPoint[1],
-                        });
-                    PreviewCursor._addDocument(img, false);
+                        width: 300, title: arr[1],
+                        x: newPoint[0],
+                        y: newPoint[1],
+                    });
+                    PreviewCursor._addDocument(img);
                     return;
                 }
 
@@ -113,7 +113,7 @@ export class PreviewCursor extends React.Component<{}> {
         onKeyPress: (e: KeyboardEvent) => void,
         addLiveText: (doc: Doc) => void,
         getTransform: () => Transform,
-        addDocument: (doc: Doc, allowDuplicates: false) => boolean) {
+        addDocument: (doc: Doc) => boolean) {
         this._clickPoint = [x, y];
         this._onKeyPress = onKeyPress;
         this._addLiveTextDoc = addLiveText;
diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx
index 62be1fc31..61919427a 100644
--- a/src/client/views/collections/CollectionBaseView.tsx
+++ b/src/client/views/collections/CollectionBaseView.tsx
@@ -45,7 +45,7 @@ export namespace CollectionViewType {
 }
 
 export interface CollectionRenderProps {
-    addDocument: (document: Doc, allowDuplicates?: boolean) => boolean;
+    addDocument: (document: Doc) => boolean;
     removeDocument: (document: Doc) => boolean;
     moveDocument: (document: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => boolean;
     active: () => boolean;
@@ -100,22 +100,13 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
     @computed get extensionDoc() { return Doc.fieldExtensionDoc(this.props.DataDoc ? this.props.DataDoc : this.props.Document, this.props.fieldKey, this.props.fieldExt); }
 
     @action.bound
-    addDocument(doc: Doc, allowDuplicates: boolean = false): boolean {
-        var curTime = NumCast(this.props.Document.currentTimecode, -1);
-        curTime !== -1 && (doc.displayTimecode = curTime);
+    addDocument(doc: Doc): boolean {
         if (this.props.fieldExt) { // bcz: fieldExt !== undefined means this is an overlay layer
             Doc.GetProto(doc).annotationOn = this.props.Document;
         }
         let targetDataDoc = this.props.fieldExt || this.props.Document.isTemplate ? this.extensionDoc : this.props.Document;
         let targetField = (this.props.fieldExt || this.props.Document.isTemplate) && this.props.fieldExt ? this.props.fieldExt : this.props.fieldKey;
-        const value = Cast(targetDataDoc[targetField], listSpec(Doc));
-        if (value !== undefined) {
-            if (allowDuplicates || !value.some(v => v instanceof Doc && v[Id] === doc[Id])) {
-                value.push(doc);
-            }
-        } else {
-            Doc.GetProto(targetDataDoc)[targetField] = new List([doc]);
-        }
+        Doc.AddDocToList(targetDataDoc, targetField, doc);
         Doc.GetProto(doc).lastOpened = new DateField;
         return true;
     }
diff --git a/src/client/views/collections/CollectionSchemaCells.tsx b/src/client/views/collections/CollectionSchemaCells.tsx
index fd1362848..79c032723 100644
--- a/src/client/views/collections/CollectionSchemaCells.tsx
+++ b/src/client/views/collections/CollectionSchemaCells.tsx
@@ -14,15 +14,12 @@ import '../DocumentDecorations.scss';
 import { EditableView } from "../EditableView";
 import { FieldView, FieldViewProps } from "../nodes/FieldView";
 import "./CollectionSchemaView.scss";
-import { CollectionVideoView } from "./CollectionVideoView";
 import { CollectionView } from "./CollectionView";
 import { NumCast, StrCast, BoolCast, FieldValue, Cast } from "../../../new_fields/Types";
 import { Docs } from "../../documents/Documents";
-import { DocumentContentsView } from "../nodes/DocumentContentsView";
 import { SelectionManager } from "../../util/SelectionManager";
 import { library } from '@fortawesome/fontawesome-svg-core';
 import { faExpand } from '@fortawesome/free-solid-svg-icons';
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField";
 import { KeyCodes } from "../../northstar/utils/KeyCodes";
 import { undoBatch } from "../../util/UndoManager";
@@ -33,8 +30,8 @@ export interface CellProps {
     row: number;
     col: number;
     rowProps: CellInfo;
-    CollectionView: Opt<CollectionView | CollectionVideoView>;
-    ContainingCollection: Opt<CollectionView | CollectionVideoView>;
+    CollectionView: Opt<CollectionView>;
+    ContainingCollection: Opt<CollectionView>;
     Document: Doc;
     fieldKey: string;
     renderDepth: number;
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 670c6bd43..3218f630a 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -23,7 +23,6 @@ import '../DocumentDecorations.scss';
 import { DocumentView } from "../nodes/DocumentView";
 import "./CollectionSchemaView.scss";
 import { CollectionSubView } from "./CollectionSubView";
-import { CollectionVideoView } from "./CollectionVideoView";
 import { CollectionView } from "./CollectionView";
 import { undoBatch } from "../../util/UndoManager";
 import { CollectionSchemaHeader, CollectionSchemaAddColumnHeader } from "./CollectionSchemaHeaders";
@@ -246,8 +245,8 @@ export interface SchemaTableProps {
     PanelHeight: () => number;
     PanelWidth: () => number;
     childDocs?: Doc[];
-    CollectionView: Opt<CollectionView | CollectionVideoView>;
-    ContainingCollectionView: Opt<CollectionView | CollectionVideoView>;
+    CollectionView: Opt<CollectionView>;
+    ContainingCollectionView: Opt<CollectionView>;
     ContainingCollectionDoc: Opt<Doc>;
     fieldKey: string;
     renderDepth: number;
@@ -904,11 +903,11 @@ interface CollectionSchemaPreviewProps {
     ruleProvider: Doc | undefined;
     focus?: (doc: Doc) => void;
     showOverlays?: (doc: Doc) => { title?: string, caption?: string };
-    CollectionView?: CollectionView | CollectionVideoView;
+    CollectionView?: CollectionView;
     CollectionDoc?: Doc;
     onClick?: ScriptField;
     getTransform: () => Transform;
-    addDocument: (document: Doc, allowDuplicates?: boolean) => boolean;
+    addDocument: (document: Doc) => boolean;
     moveDocument: (document: Doc, target: Doc, addDoc: ((doc: Doc) => boolean)) => boolean;
     removeDocument: (document: Doc) => boolean;
     active: () => boolean;
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 5e2b79278..689adc375 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -18,7 +18,6 @@ import { undoBatch, UndoManager } from "../../util/UndoManager";
 import { DocComponent } from "../DocComponent";
 import { FieldViewProps } from "../nodes/FieldView";
 import { FormattedTextBox, GoogleRef } from "../nodes/FormattedTextBox";
-import { CollectionVideoView } from "./CollectionVideoView";
 import { CollectionView } from "./CollectionView";
 import React = require("react");
 var path = require('path');
@@ -26,7 +25,7 @@ import { GooglePhotos } from "../../apis/google_docs/GooglePhotosClientUtils";
 import { ImageUtils } from "../../util/Import & Export/ImageUtils";
 
 export interface CollectionViewProps extends FieldViewProps {
-    addDocument: (document: Doc, allowDuplicates?: boolean) => boolean;
+    addDocument: (document: Doc) => boolean;
     removeDocument: (document: Doc) => boolean;
     moveDocument: (document: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => boolean;
     PanelWidth: () => number;
@@ -37,7 +36,7 @@ export interface CollectionViewProps extends FieldViewProps {
 }
 
 export interface SubCollectionViewProps extends CollectionViewProps {
-    CollectionView: Opt<CollectionView | CollectionVideoView>;
+    CollectionView: Opt<CollectionView>;
     ruleProvider: Doc | undefined;
 }
 
@@ -175,14 +174,14 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
                         DocServer.GetRefField(docid).then(f => {
                             if (f instanceof Doc) {
                                 if (options.x || options.y) { f.x = options.x; f.y = options.y; } // should be in CollectionFreeFormView
-                                (f instanceof Doc) && this.props.addDocument(f, false);
+                                (f instanceof Doc) && this.props.addDocument(f);
                             }
                         });
                     } else {
                         this.props.addDocument && this.props.addDocument(Docs.Create.WebDocument(href, options));
                     }
                 } else if (text) {
-                    this.props.addDocument && this.props.addDocument(Docs.Create.TextDocument({ ...options, width: 100, height: 25, documentText: "@@@" + text }), false);
+                    this.props.addDocument && this.props.addDocument(Docs.Create.TextDocument({ ...options, width: 100, height: 25, documentText: "@@@" + text }));
                 }
                 return;
             }
@@ -194,7 +193,7 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
                     let split = img.split("src=\"")[1].split("\"")[0];
                     let doc = Docs.Create.ImageDocument(split, { ...options, width: 300 });
                     ImageUtils.ExtractExif(doc);
-                    this.props.addDocument(doc, false);
+                    this.props.addDocument(doc);
                     return;
                 } else {
                     let path = window.location.origin + "/doc/";
@@ -203,12 +202,12 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
                         DocServer.GetRefField(docid).then(f => {
                             if (f instanceof Doc) {
                                 if (options.x || options.y) { f.x = options.x; f.y = options.y; } // should be in CollectionFreeFormView
-                                (f instanceof Doc) && this.props.addDocument(f, false);
+                                (f instanceof Doc) && this.props.addDocument(f);
                             }
                         });
                     } else {
                         let htmlDoc = Docs.Create.HtmlDocument(html, { ...options, width: 300, height: 300, documentText: text });
-                        this.props.addDocument(htmlDoc, false);
+                        this.props.addDocument(htmlDoc);
                     }
                     return;
                 }
@@ -252,7 +251,7 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
                             let type = result["content-type"];
                             if (type) {
                                 Docs.Get.DocumentFromType(type, str, { ...options, width: 300, nativeWidth: type.indexOf("video") !== -1 ? 600 : 300 })
-                                    .then(doc => doc && this.props.addDocument(doc, false));
+                                    .then(doc => doc && this.props.addDocument(doc));
                             }
                         });
                     promises.push(prom);
diff --git a/src/client/views/collections/CollectionVideoView.scss b/src/client/views/collections/CollectionVideoView.scss
deleted file mode 100644
index 509851ebb..000000000
--- a/src/client/views/collections/CollectionVideoView.scss
+++ /dev/null
@@ -1,51 +0,0 @@
-
-.collectionVideoView-cont{
-    width: 100%;
-    height: 100%;
-    position: inherit;
-    top: 0;
-    left:0;
-    z-index: -1;
-    display:inline-table;
-}
-.collectionVideoView-time{
-    color : white;
-    top :25px;
-    left : 25px;
-    position: absolute;
-    background-color: rgba(50, 50, 50, 0.2);
-    transform-origin: left top;
-}
-.collectionVideoView-snapshot{
-    color : white;
-    top :25px;
-    right : 25px;
-    position: absolute;
-    background-color: rgba(50, 50, 50, 0.2);
-    transform-origin: left top;
-}
-.collectionVideoView-play {
-    width: 25px;
-    height: 20px;
-    bottom: 25px;
-    left : 25px;
-    position: absolute;
-    color : white;
-    background-color: rgba(50, 50, 50, 0.2);
-    border-radius: 4px;
-    text-align: center;
-    transform-origin: left bottom;
-}
-.collectionVideoView-full {
-    width: 25px;
-    height: 20px;
-    bottom: 25px;
-    right : 25px;
-    position: absolute;
-    color : white;
-    background-color: rgba(50, 50, 50, 0.2);
-    border-radius: 4px;
-    text-align: center;
-    transform-origin: right bottom;
-
-}
\ No newline at end of file
diff --git a/src/client/views/collections/CollectionVideoView.tsx b/src/client/views/collections/CollectionVideoView.tsx
deleted file mode 100644
index 3d898b7de..000000000
--- a/src/client/views/collections/CollectionVideoView.tsx
+++ /dev/null
@@ -1,115 +0,0 @@
-import { action } from "mobx";
-import { observer } from "mobx-react";
-import { NumCast } from "../../../new_fields/Types";
-import { FieldView, FieldViewProps } from "../nodes/FieldView";
-import { VideoBox } from "../nodes/VideoBox";
-import { CollectionBaseView, CollectionRenderProps, CollectionViewType } from "./CollectionBaseView";
-import { CollectionFreeFormView } from "./collectionFreeForm/CollectionFreeFormView";
-import "./CollectionVideoView.scss";
-import React = require("react");
-import { InkingControl } from "../InkingControl";
-import { InkTool } from "../../../new_fields/InkField";
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-
-
-@observer
-export class CollectionVideoView extends React.Component<FieldViewProps> {
-    private _videoBox?: VideoBox;
-
-    public static LayoutString(fieldKey: string = "data", fieldExt: string = "annotations") {
-        return FieldView.LayoutString(CollectionVideoView, fieldKey, fieldExt);
-    }
-    private get uIButtons() {
-        let scaling = Math.min(1.8, this.props.ScreenToLocalTransform().Scale);
-        let curTime = NumCast(this.props.Document.currentTimecode);
-        return ([<div className="collectionVideoView-time" key="time" onPointerDown={this.onResetDown} style={{ transform: `scale(${scaling})` }}>
-            <span>{"" + Math.round(curTime)}</span>
-            <span style={{ fontSize: 8 }}>{" " + Math.round((curTime - Math.trunc(curTime)) * 100)}</span>
-        </div>,
-        <div className="collectionVideoView-snapshot" key="time" onPointerDown={this.onSnapshot} style={{ transform: `scale(${scaling})` }}>
-            <FontAwesomeIcon icon="camera" size="lg" />
-        </div>,
-        VideoBox._showControls ? (null) : [
-            <div className="collectionVideoView-play" key="play" onPointerDown={this.onPlayDown} style={{ transform: `scale(${scaling})` }}>
-                <FontAwesomeIcon icon={this._videoBox && this._videoBox.Playing ? "pause" : "play"} size="lg" />
-            </div>,
-            <div className="collectionVideoView-full" key="full" onPointerDown={this.onFullDown} style={{ transform: `scale(${scaling})` }}>
-                F
-            </div>
-        ]]);
-    }
-
-    @action
-    onPlayDown = () => {
-        if (this._videoBox) {
-            if (this._videoBox.Playing) {
-                this._videoBox.Pause();
-            } else {
-                this._videoBox.Play();
-            }
-        }
-    }
-
-    @action
-    onFullDown = (e: React.PointerEvent) => {
-        if (this._videoBox) {
-            this._videoBox.FullScreen();
-            e.stopPropagation();
-            e.preventDefault();
-        }
-    }
-
-    @action
-    onSnapshot = (e: React.PointerEvent) => {
-        if (this._videoBox) {
-            this._videoBox.Snapshot();
-            e.stopPropagation();
-            e.preventDefault();
-        }
-    }
-
-    _isclick = 0;
-    @action
-    onResetDown = (e: React.PointerEvent) => {
-        if (this._videoBox) {
-            this._videoBox.Pause();
-            e.stopPropagation();
-            this._isclick = 0;
-            document.addEventListener("pointermove", this.onPointerMove, true);
-            document.addEventListener("pointerup", this.onPointerUp, true);
-            InkingControl.Instance.switchTool(InkTool.Eraser);
-        }
-    }
-
-    @action
-    onPointerMove = (e: PointerEvent) => {
-        this._isclick += Math.abs(e.movementX) + Math.abs(e.movementY);
-        if (this._videoBox) {
-            this._videoBox.Seek(Math.max(0, NumCast(this.props.Document.currentTimecode, 0) + Math.sign(e.movementX) * 0.0333));
-        }
-        e.stopImmediatePropagation();
-    }
-    @action
-    onPointerUp = (e: PointerEvent) => {
-        document.removeEventListener("pointermove", this.onPointerMove, true);
-        document.removeEventListener("pointerup", this.onPointerUp, true);
-        InkingControl.Instance.switchTool(InkTool.None);
-        this._isclick < 10 && (this.props.Document.currentTimecode = 0);
-    }
-    setVideoBox = (videoBox: VideoBox) => { this._videoBox = videoBox; };
-
-    private subView = (_type: CollectionViewType, renderProps: CollectionRenderProps) => {
-        let props = { ...this.props, ...renderProps };
-        return (<>
-            <CollectionFreeFormView {...props} setVideoBox={this.setVideoBox} CollectionView={this} chromeCollapsed={true} />
-            {this.props.isSelected() ? this.uIButtons : (null)}
-        </>);
-    }
-
-    render() {
-        return (
-            <CollectionBaseView {...this.props} className="collectionVideoView-cont" >
-                {this.subView}
-            </CollectionBaseView>);
-    }
-}
\ No newline at end of file
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
index df089eb00..fe92eed10 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
@@ -10,7 +10,7 @@ export interface CollectionFreeFormLinkViewProps {
     A: Doc;
     B: Doc;
     LinkDocs: Doc[];
-    addDocument: (document: Doc, allowDuplicates?: boolean) => boolean;
+    addDocument: (document: Doc) => boolean;
     removeDocument: (document: Doc) => boolean;
 }
 
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 38488f033..d2644480c 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -93,10 +93,10 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
             heading = !sorted.length ? Math.max(1, maxHeading) : NumCast(sorted[sorted.length - 1].heading) === 1 ? 2 : NumCast(sorted[sorted.length - 1].heading);
         }
         !this.Document.isRuleProvider && (newBox.heading = heading);
-        this.addDocument(newBox, false);
+        this.addDocument(newBox);
     }
-    private addDocument = (newBox: Doc, allowDuplicates: boolean) => {
-        let added = this.props.addDocument(newBox, false);
+    private addDocument = (newBox: Doc) => {
+        let added = this.props.addDocument(newBox);
         added && this.bringToFront(newBox);
         added && this.updateCluster(newBox);
         return added;
@@ -659,7 +659,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
                             if (doc instanceof Doc) {
                                 const [xx, yy] = this.props.ScreenToLocalTransform().transformPoint(x, y);
                                 doc.x = xx, doc.y = yy;
-                                this.props.addDocument && this.props.addDocument(doc, false);
+                                this.props.addDocument && this.props.addDocument(doc);
                             }
                         }
                     }
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index eaf65b88c..bb787106c 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -24,7 +24,7 @@ interface MarqueeViewProps {
     getContainerTransform: () => Transform;
     getTransform: () => Transform;
     container: CollectionFreeFormView;
-    addDocument: (doc: Doc, allowDuplicates: false) => boolean;
+    addDocument: (doc: Doc) => boolean;
     activeDocuments: () => Doc[];
     selectDocuments: (docs: Doc[]) => void;
     removeDocument: (doc: Doc) => boolean;
@@ -83,7 +83,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
                 ns.map(line => {
                     let indent = line.search(/\S|$/);
                     let newBox = Docs.Create.TextDocument({ width: 200, height: 35, x: x + indent / 3 * 10, y: y, documentText: "@@@" + line, title: line });
-                    this.props.addDocument(newBox, false);
+                    this.props.addDocument(newBox);
                     y += 40 * this.props.getTransform().Scale;
                 });
             })();
@@ -92,7 +92,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
             navigator.clipboard.readText().then(text => {
                 let ns = text.split("\n").filter(t => t.trim() !== "\r" && t.trim() !== "");
                 if (ns.length === 1 && text.startsWith("http")) {
-                    this.props.addDocument(Docs.Create.ImageDocument(text, { nativeWidth: 300, width: 300, x: x, y: y }), false);// paste an image from its URL in the paste buffer
+                    this.props.addDocument(Docs.Create.ImageDocument(text, { nativeWidth: 300, width: 300, x: x, y: y }));// paste an image from its URL in the paste buffer
                 } else {
                     this.pasteTable(ns, x, y);
                 }
@@ -146,7 +146,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
             }
             let newCol = Docs.Create.SchemaDocument([...(groupAttr ? [new SchemaHeaderField("_group", "#f1efeb")] : []), ...columns.filter(c => c).map(c => new SchemaHeaderField(c, "#f1efeb"))], docList, { x: x, y: y, title: "droppedTable", width: 300, height: 100 });
 
-            this.props.addDocument(newCol, false);
+            this.props.addDocument(newCol);
         }
     }
     @action
@@ -202,7 +202,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
         }
     }
 
-    setPreviewCursor = (x: number, y: number, drag: boolean) => {
+    setPreviewCursor = action((x: number, y: number, drag: boolean) => {
         if (drag) {
             this._downX = this._lastX = x;
             this._downY = this._lastY = y;
@@ -217,7 +217,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
             this._downY = y;
             PreviewCursor.Show(x, y, this.onKeyPress, this.props.addLiveTextDocument, this.props.getTransform, this.props.addDocument);
         }
-    }
+    })
 
     @action
     onClick = (e: React.MouseEvent): void => {
@@ -350,7 +350,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
                 }
             }
             else {
-                this.props.addDocument(newCollection, false);
+                this.props.addDocument(newCollection);
                 this.props.selectDocuments([newCollection]);
             }
             this.cleanupInteractions(false);
diff --git a/src/client/views/linking/LinkFollowBox.tsx b/src/client/views/linking/LinkFollowBox.tsx
index b18aa5d63..32ebe7c61 100644
--- a/src/client/views/linking/LinkFollowBox.tsx
+++ b/src/client/views/linking/LinkFollowBox.tsx
@@ -278,7 +278,7 @@ export class LinkFollowBox extends React.Component<FieldViewProps> {
                     alias.width = width;
                     alias.height = height;
 
-                    this.sourceView.props.addDocument(alias, false);
+                    this.sourceView.props.addDocument(alias);
                 }
             }
 
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index a824ae9cc..e4b2ecffd 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -9,7 +9,6 @@ import DirectoryImportBox from "../../util/Import & Export/DirectoryImportBox";
 import { CollectionDockingView } from "../collections/CollectionDockingView";
 import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView";
 import { CollectionSchemaView } from "../collections/CollectionSchemaView";
-import { CollectionVideoView } from "../collections/CollectionVideoView";
 import { CollectionView } from "../collections/CollectionView";
 import { LinkFollowBox } from "../linking/LinkFollowBox";
 import { YoutubeBox } from "./../../apis/youtube/YoutubeBox";
@@ -100,7 +99,7 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
         if (!this.layout && this.props.layoutKey !== "overlayLayout") return (null);
         return <ObserverJsxParser
             blacklistedAttrs={[]}
-            components={{ FormattedTextBox, ImageBox, IconBox, DirectoryImportBox, DragBox, ButtonBox, FieldView, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, CollectionVideoView, WebBox, KeyValueBox, PDFBox, VideoBox, AudioBox, HistogramBox, PresBox, YoutubeBox, LinkFollowBox, PresElementBox }}
+            components={{ FormattedTextBox, ImageBox, IconBox, DirectoryImportBox, DragBox, ButtonBox, FieldView, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, WebBox, KeyValueBox, PDFBox, VideoBox, AudioBox, HistogramBox, PresBox, YoutubeBox, LinkFollowBox, PresElementBox }}
             bindings={this.CreateBindings()}
             jsx={this.finalLayout}
             showWarnings={true}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 0bf3b5c40..758222e52 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -21,7 +21,6 @@ import { SelectionManager } from "../../util/SelectionManager";
 import { Transform } from "../../util/Transform";
 import { undoBatch, UndoManager } from "../../util/UndoManager";
 import { CollectionDockingView } from "../collections/CollectionDockingView";
-import { CollectionVideoView } from "../collections/CollectionVideoView";
 import { CollectionView } from "../collections/CollectionView";
 import { ContextMenu } from "../ContextMenu";
 import { ContextMenuProps } from '../ContextMenuItem';
@@ -65,13 +64,13 @@ library.add(fa.faLock);
 library.add(fa.faLaptopCode, fa.faMale, fa.faCopy, fa.faHandPointRight, fa.faCompass, fa.faSnowflake, fa.faMicrophone);
 
 export interface DocumentViewProps {
-    ContainingCollectionView: Opt<CollectionView | CollectionVideoView>;
+    ContainingCollectionView: Opt<CollectionView>;
     ContainingCollectionDoc: Opt<Doc>;
     Document: Doc;
     DataDoc?: Doc;
     fitToBox?: boolean;
     onClick?: ScriptField;
-    addDocument?: (doc: Doc, allowDuplicates?: boolean) => boolean;
+    addDocument?: (doc: Doc) => boolean;
     removeDocument?: (doc: Doc) => boolean;
     moveDocument?: (doc: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => boolean;
     ScreenToLocalTransform: () => Transform;
@@ -217,7 +216,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             let maxLocation = StrCast(this.Document.maximizeLocation, "inPlace");
             maxLocation = this.Document.maximizeLocation = (!ctrlKey ? !altKey ? maxLocation : (maxLocation !== "inPlace" ? "inPlace" : "onRight") : (maxLocation !== "inPlace" ? "inPlace" : "inTab"));
             if (maxLocation === "inPlace") {
-                expandedDocs.forEach(maxDoc => this.props.addDocument && this.props.addDocument(maxDoc, false));
+                expandedDocs.forEach(maxDoc => this.props.addDocument && this.props.addDocument(maxDoc));
                 let scrpt = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).inverse().transformPoint(NumCast(this.Document.width) / 2, NumCast(this.Document.height) / 2);
                 DocumentManager.Instance.animateBetweenPoint(scrpt, expandedDocs);
             } else {
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index c17730f48..074efaf6c 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -8,7 +8,6 @@ import { List } from "../../../new_fields/List";
 import { RichTextField } from "../../../new_fields/RichTextField";
 import { AudioField, ImageField, VideoField } from "../../../new_fields/URLField";
 import { Transform } from "../../util/Transform";
-import { CollectionVideoView } from "../collections/CollectionVideoView";
 import { CollectionView } from "../collections/CollectionView";
 import { AudioBox } from "./AudioBox";
 import { FormattedTextBox } from "./FormattedTextBox";
@@ -28,7 +27,7 @@ export interface FieldViewProps {
     fieldExt: string;
     leaveNativeSize?: boolean;
     fitToBox?: boolean;
-    ContainingCollectionView: Opt<CollectionView | CollectionVideoView>;
+    ContainingCollectionView: Opt<CollectionView>;
     ContainingCollectionDoc: Opt<Doc>;
     ruleProvider: Doc | undefined;
     Document: Doc;
@@ -37,7 +36,7 @@ export interface FieldViewProps {
     isSelected: () => boolean;
     select: (isCtrlPressed: boolean) => void;
     renderDepth: number;
-    addDocument?: (document: Doc, allowDuplicates?: boolean) => boolean;
+    addDocument?: (document: Doc) => boolean;
     addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => boolean;
     pinToPres: (document: Doc) => void;
     removeDocument?: (document: Doc) => boolean;
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 1f3887608..57803be1f 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -174,7 +174,7 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
 
         ContextMenu.Instance.addItem({ description: "Pdf Funcs...", subitems: funcs, icon: "asterisk" });
     }
-    _initialScale: number | undefined;
+    _initialScale: number | undefined;  // the initial scale of the PDF when first rendered which determines whether the document will be live on startup or not.  Getting bigger after startup won't make it automatically be live....
     render() {
         const pdfUrl = Cast(this.dataDoc[this.props.fieldKey], PdfField);
         let classname = "pdfBox-cont" + (InkingControl.Instance.selectedTool || !this.active ? "" : "-interactive");
diff --git a/src/client/views/nodes/VideoBox.scss b/src/client/views/nodes/VideoBox.scss
index d651a8621..b3cd439aa 100644
--- a/src/client/views/nodes/VideoBox.scss
+++ b/src/client/views/nodes/VideoBox.scss
@@ -1,6 +1,14 @@
+// .videoBox-container {
+//     .collectionfreeformview-container {
+//         mix-blend-mode: multiply;
+//     }
+// }
+
 .videoBox-content-YouTube, .videoBox-content-YouTube-fullScreen,
 .videoBox-content, .videoBox-content-interactive, .videoBox-cont-fullScreen {
     width: 100%;
+    z-index: 0;  // logically this should be 0 (or unset) which would give us transparent brush strokes over videos.  However, this makes Chrome crawl to a halt
+    position: absolute;
 }
 
 .videoBox-content, .videoBox-content-interactive, .videoBox-content-fullScreen {
@@ -11,7 +19,52 @@
     height: 100%;
 }
 
-.videoBox-content-interactive, .videoBox-content-fullScreen,
-.videoBox-content-YouTube-fullScreen  {
+.videoBox-content-interactive, .videoBox-content-fullScreen, .videoBox-content-YouTube-fullScreen  {
     pointer-events: all;
+}
+
+.videoBox-time{
+    color : white;
+    top :25px;
+    left : 25px;
+    position: absolute;
+    background-color: rgba(50, 50, 50, 0.2);
+    transform-origin: left top;
+    pointer-events:all;
+}
+.videoBox-snapshot{
+    color : white;
+    top :25px;
+    right : 25px;
+    position: absolute;
+    background-color:rgba(50, 50, 50, 0.2);
+    transform-origin: left top;
+    pointer-events:all;
+}
+.videoBox-play {
+    width: 25px;
+    height: 20px;
+    bottom: 25px;
+    left : 25px;
+    position: absolute;
+    color : white;
+    background-color: rgba(50, 50, 50, 0.2);
+    border-radius: 4px;
+    text-align: center;
+    transform-origin: left bottom;
+    pointer-events:all;
+}
+.videoBox-full {
+    width: 25px;
+    height: 20px;
+    bottom: 25px;
+    right : 25px;
+    position: absolute;
+    color : white;
+    background-color: rgba(50, 50, 50, 0.2);
+    border-radius: 4px;
+    text-align: center;
+    transform-origin: right bottom;
+    pointer-events:all;
+
 }
\ No newline at end of file
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index e83aa8bea..feb067d8f 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -3,11 +3,11 @@ import { action, computed, IReactionDisposer, observable, reaction, runInAction,
 import { observer } from "mobx-react";
 import * as rp from 'request-promise';
 import { InkTool } from "../../../new_fields/InkField";
-import { makeInterface, createSchema } from "../../../new_fields/Schema";
-import { Cast, FieldValue, NumCast, BoolCast } from "../../../new_fields/Types";
+import { makeInterface, createSchema, listSpec } from "../../../new_fields/Schema";
+import { Cast, FieldValue, NumCast, BoolCast, StrCast } from "../../../new_fields/Types";
 import { VideoField } from "../../../new_fields/URLField";
 import { RouteStore } from "../../../server/RouteStore";
-import { Utils } from "../../../Utils";
+import { Utils, emptyFunction, returnOne } from "../../../Utils";
 import { Docs, DocUtils } from "../../documents/Documents";
 import { ContextMenu } from "../ContextMenu";
 import { ContextMenuProps } from "../ContextMenuItem";
@@ -22,6 +22,8 @@ import { faVideo } from "@fortawesome/free-solid-svg-icons";
 import { Doc } from "../../../new_fields/Doc";
 import { ScriptField } from "../../../new_fields/ScriptField";
 import { positionSchema } from "./CollectionFreeFormDocumentView";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView";
 var path = require('path');
 
 export const timeSchema = createSchema({
@@ -34,19 +36,24 @@ library.add(faVideo);
 
 @observer
 export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoDocument) {
+    static _youtubeIframeCounter: number = 0;
     private _reactionDisposer?: IReactionDisposer;
     private _youtubeReactionDisposer?: IReactionDisposer;
     private _youtubePlayer: YT.Player | undefined = undefined;
     private _videoRef: HTMLVideoElement | null = null;
     private _youtubeIframeId: number = -1;
     private _youtubeContentCreated = false;
-    static _youtubeIframeCounter: number = 0;
+    private _downX: number = 0;
+    private _downY: number = 0;
+    private _isResetClick = 0;
+    private _isChildActive = false;
+    private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean) => void);
     @observable _forceCreateYouTubeIFrame = false;
     @observable static _showControls: boolean;
     @observable _playTimer?: NodeJS.Timeout = undefined;
     @observable _fullScreen = false;
     @observable public Playing: boolean = false;
-    public static LayoutString() { return FieldView.LayoutString(VideoBox); }
+    public static LayoutString(fieldExt?: string) { return FieldView.LayoutString(VideoBox, "data", fieldExt); }
 
     public get player(): HTMLVideoElement | null {
         return this._videoRef;
@@ -123,8 +130,8 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
             //convert to desired file format
             var dataUrl = canvas.toDataURL('image/png'); // can also use 'image/png'
             // if you want to preview the captured image,
-            let filename = path.basename(encodeURIComponent("snapshot" + this.Document.title + "_" + (this.Document.currentTimecode || 0).toString()));
-            VideoBox.convertDataUri(dataUrl, filename.replace(/\..*$/, "")).then(returnedFilename => {
+            let filename = path.basename(encodeURIComponent("snapshot" + StrCast(this.Document.title).replace(/\..*$/, "") + "_" + (this.Document.currentTimecode || 0).toString().replace(/\./, "_")));
+            VideoBox.convertDataUri(dataUrl, filename).then(returnedFilename => {
                 if (returnedFilename) {
                     let url = this.choosePath(Utils.prepend(returnedFilename));
                     let imageSummary = Docs.Create.ImageDocument(url, {
@@ -132,7 +139,7 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
                         width: 150, height: height / width * 150, title: "--snapshot" + (this.Document.currentTimecode || 0) + " image-"
                     });
                     imageSummary.isButton = true;
-                    this.props.ContainingCollectionView && this.props.ContainingCollectionView.props.addDocument && this.props.ContainingCollectionView.props.addDocument(imageSummary, false);
+                    this.props.addDocument && this.props.addDocument(imageSummary);
                     DocUtils.MakeLink({ doc: imageSummary }, { doc: this.props.Document }, "snapshot from " + this.Document.title, "video frame snapshot");
                 }
             });
@@ -259,7 +266,73 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
         });
 
     }
+    private get uIButtons() {
+        let scaling = Math.min(1.8, this.props.ScreenToLocalTransform().Scale);
+        let curTime = NumCast(this.props.Document.currentTimecode);
+        return ([<div className="videoBox-time" key="time" onPointerDown={this.onResetDown} style={{ transform: `scale(${scaling})` }}>
+            <span>{"" + Math.round(curTime)}</span>
+            <span style={{ fontSize: 8 }}>{" " + Math.round((curTime - Math.trunc(curTime)) * 100)}</span>
+        </div>,
+        <div className="videoBox-snapshot" key="snap" onPointerDown={this.onSnapshot} style={{ transform: `scale(${scaling})` }}>
+            <FontAwesomeIcon icon="camera" size="lg" />
+        </div>,
+        VideoBox._showControls ? (null) : [
+            <div className="videoBox-play" key="play" onPointerDown={this.onPlayDown} style={{ transform: `scale(${scaling})` }}>
+                <FontAwesomeIcon icon={this.Playing ? "pause" : "play"} size="lg" />
+            </div>,
+            <div className="collecvideoBoxtionVideoView-full" key="full" onPointerDown={this.onFullDown} style={{ transform: `scale(${scaling})` }}>
+                F
+            </div>
+        ]]);
+    }
+
+    @action
+    onPlayDown = () => {
+        if (this.Playing) {
+            this.Pause();
+        } else {
+            this.Play();
+        }
+    }
 
+    @action
+    onFullDown = (e: React.PointerEvent) => {
+        this.FullScreen();
+        e.stopPropagation();
+        e.preventDefault();
+    }
+
+    @action
+    onSnapshot = (e: React.PointerEvent) => {
+        this.Snapshot();
+        e.stopPropagation();
+        e.preventDefault();
+    }
+
+    @action
+    onResetDown = (e: React.PointerEvent) => {
+        this.Pause();
+        e.stopPropagation();
+        this._isResetClick = 0;
+        document.addEventListener("pointermove", this.onResetMove, true);
+        document.addEventListener("pointerup", this.onResetUp, true);
+        InkingControl.Instance.switchTool(InkTool.Eraser);
+    }
+
+    @action
+    onResetMove = (e: PointerEvent) => {
+        this._isResetClick += Math.abs(e.movementX) + Math.abs(e.movementY);
+        this.Seek(Math.max(0, NumCast(this.props.Document.currentTimecode, 0) + Math.sign(e.movementX) * 0.0333));
+        e.stopImmediatePropagation();
+    }
+    @action
+    onResetUp = (e: PointerEvent) => {
+        document.removeEventListener("pointermove", this.onResetMove, true);
+        document.removeEventListener("pointerup", this.onResetUp, true);
+        InkingControl.Instance.switchTool(InkTool.None);
+        this._isResetClick < 10 && (this.props.Document.currentTimecode = 0);
+    }
+    @computed get fieldExtensionDoc() { return Doc.fieldExtensionDoc(this.dataDoc, this.props.fieldKey); }
     @computed get dataDoc() { return this.props.DataDoc && this.props.Document.isTemplate ? this.props.DataDoc : Doc.GetProto(this.props.Document); }
 
     @computed get youtubeContent() {
@@ -273,11 +346,95 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
         ></iframe>;
     }
 
+
+    setPreviewCursor = (func?: (x: number, y: number, drag: boolean) => void) => {
+        this._setPreviewCursor = func;
+    }
+
+    @action.bound
+    removeDocument(doc: Doc): boolean {
+        Doc.GetProto(doc).annotationOn = undefined;
+        //TODO This won't create the field if it doesn't already exist
+        let targetDataDoc = this.fieldExtensionDoc;
+        let targetField = this.props.fieldExt;
+        let value = Cast(targetDataDoc[targetField], listSpec(Doc), []);
+        let index = value.reduce((p, v, i) => (v instanceof Doc && v === doc) ? i : p, -1);
+        index = index !== -1 ? index : value.reduce((p, v, i) => (v instanceof Doc && Doc.AreProtosEqual(v, doc)) ? i : p, -1);
+        index !== -1 && value.splice(index, 1);
+        return true;
+    }
+    // this is called with the document that was dragged and the collection to move it into.
+    // if the target collection is the same as this collection, then the move will be allowed.
+    // otherwise, the document being moved must be able to be removed from its container before
+    // moving it into the target.  
+    @action.bound
+    moveDocument(doc: Doc, targetCollection: Doc, addDocument: (doc: Doc) => boolean): boolean {
+        if (Doc.AreProtosEqual(this.props.Document, targetCollection)) {
+            return true;
+        }
+        return this.removeDocument(doc) ? addDocument(doc) : false;
+    }
+
+    @action.bound
+    addDocument(doc: Doc): boolean {
+        Doc.GetProto(doc).annotationOn = this.props.Document;
+        var curTime = NumCast(this.props.Document.currentTimecode, -1);
+        curTime !== -1 && (doc.displayTimecode = curTime);
+        Doc.AddDocToList(this.fieldExtensionDoc, this.props.fieldExt, doc);
+        return true;
+    }
+    whenActiveChanged = (isActive: boolean) => {
+        this._isChildActive = isActive;
+        this.props.whenActiveChanged(isActive);
+    }
+    active = () => {
+        return this.props.isSelected() || this._isChildActive || this.props.renderDepth === 0;
+    }
+    onClick = (e: React.MouseEvent) => {
+        this._setPreviewCursor &&
+            e.button === 0 &&
+            Math.abs(e.clientX - this._downX) < 3 &&
+            Math.abs(e.clientY - this._downY) < 3 &&
+            this._setPreviewCursor(e.clientX, e.clientY, false);
+    }
+
+    onPointerDown = (e: React.PointerEvent): void => {
+        this._downX = e.clientX;
+        this._downY = e.clientY;
+        if ((e.button !== 0 || e.altKey) && this.active()) {
+            this._setPreviewCursor && this._setPreviewCursor(e.clientX, e.clientY, true);
+        }
+    }
+    @computed get annotationLayer() {
+        return <CollectionFreeFormView {...this.props}
+            setPreviewCursor={this.setPreviewCursor}
+            PanelHeight={this.props.PanelHeight}
+            PanelWidth={this.props.PanelWidth}
+            focus={this.props.focus}
+            isSelected={this.props.isSelected}
+            select={emptyFunction}
+            active={this.active}
+            ContentScaling={returnOne}
+            whenActiveChanged={this.whenActiveChanged}
+            removeDocument={this.removeDocument}
+            moveDocument={this.moveDocument}
+            addDocument={this.addDocument}
+            CollectionView={undefined}
+            ScreenToLocalTransform={this.props.ScreenToLocalTransform}
+            ruleProvider={undefined}
+            renderDepth={this.props.renderDepth + 1}
+            ContainingCollectionDoc={this.props.ContainingCollectionDoc}
+            chromeCollapsed={true}>
+        </CollectionFreeFormView>
+    }
+
     render() {
         Doc.UpdateDocumentExtensionForField(this.dataDoc, this.props.fieldKey);
-        return <div style={{ pointerEvents: "all", width: "100%", height: "100%" }} onContextMenu={this.specificContextMenu}>
+        return (<div className={"videoBox-container"} onClick={this.onClick} onPointerDown={this.onPointerDown} onContextMenu={this.specificContextMenu}>
             {this.youtubeVideoId ? this.youtubeContent : this.content}
-        </div>;
+            {this.annotationLayer}
+            {this.uIButtons}
+        </div >);
     }
 }
 
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 29eef27a0..bb4c5adc9 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -91,7 +91,7 @@ export class WebBox extends React.Component<FieldViewProps> {
         });
 
         SelectionManager.SelectedDocuments().map(dv => {
-            dv.props.addDocument && dv.props.addDocument(newBox, false);
+            dv.props.addDocument && dv.props.addDocument(newBox);
             dv.props.removeDocument && dv.props.removeDocument(dv.props.Document);
         });
 
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 65ca830d9..366861144 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -18,7 +18,6 @@ import PDFMenu from "./PDFMenu";
 import "./PDFViewer.scss";
 import React = require("react");
 import * as rp from "request-promise";
-import { CollectionVideoView } from "../collections/CollectionVideoView";
 import { CollectionView } from "../collections/CollectionView";
 import Annotation from "./Annotation";
 import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView";
@@ -50,10 +49,10 @@ interface IViewerProps {
     GoToPage?: (n: number) => void;
     addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => boolean;
     pinToPres: (document: Doc) => void;
-    addDocument?: (doc: Doc, allowDuplicates?: boolean) => boolean;
+    addDocument?: (doc: Doc) => boolean;
     setPdfViewer: (view: PDFViewer) => void;
     ScreenToLocalTransform: () => Transform;
-    ContainingCollectionView: Opt<CollectionView | CollectionVideoView>;
+    ContainingCollectionView: Opt<CollectionView>;
     whenActiveChanged: (isActive: boolean) => void;
 }
 
@@ -582,12 +581,15 @@ export class PDFViewer extends React.Component<IViewerProps> {
         }
         return this.removeDocument(doc) ? addDocument(doc) : false;
     }
-
-
+    @action.bound
+    addDocument(doc: Doc): boolean {
+        Doc.GetProto(doc).annotationOn = this.props.Document;
+        Doc.AddDocToList(this.props.fieldExtensionDoc, this.props.fieldExt, doc);
+        return true;
+    }
     @action.bound
     removeDocument(doc: Doc): boolean {
         Doc.GetProto(doc).annotationOn = undefined;
-        //TODO This won't create the field if it doesn't already exist
         let targetDataDoc = this.props.fieldExtensionDoc;
         let targetField = this.props.fieldExt;
         let value = Cast(targetDataDoc[targetField], listSpec(Doc), []);
@@ -659,8 +661,8 @@ export class PDFViewer extends React.Component<IViewerProps> {
                     whenActiveChanged={this.whenActiveChanged}
                     removeDocument={this.removeDocument}
                     moveDocument={this.moveDocument}
-                    addDocument={(doc: Doc, allow: boolean | undefined) => { Doc.GetProto(doc).annotationOn = this.props.Document; Doc.AddDocToList(this.props.fieldExtensionDoc, this.props.fieldExt, doc); return true; }}
-                    CollectionView={this.props.ContainingCollectionView}
+                    addDocument={this.addDocument}
+                    CollectionView={undefined}
                     ScreenToLocalTransform={this.scrollXf}
                     ruleProvider={undefined}
                     renderDepth={this.props.renderDepth + 1}
-- 
cgit v1.2.3-70-g09d2

