From 4f1f20a52a5c01b3046a56f37b6c7c029d3e10f0 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Wed, 13 Mar 2019 15:17:53 -0400
Subject: fixed a variety of things related to selection, dragging, marqueeing

---
 .../views/collections/CollectionVideoView.tsx      | 50 ++++++++++++++++++++++
 1 file changed, 50 insertions(+)
 create mode 100644 src/client/views/collections/CollectionVideoView.tsx

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

diff --git a/src/client/views/collections/CollectionVideoView.tsx b/src/client/views/collections/CollectionVideoView.tsx
new file mode 100644
index 000000000..a6471f53c
--- /dev/null
+++ b/src/client/views/collections/CollectionVideoView.tsx
@@ -0,0 +1,50 @@
+import { action, computed } from "mobx";
+import { observer } from "mobx-react";
+import { Document } from "../../../fields/Document";
+import { KeyStore } from "../../../fields/KeyStore";
+import { ContextMenu } from "../ContextMenu";
+import { CollectionView, CollectionViewType } from "./CollectionView";
+import { CollectionViewProps } from "./CollectionViewBase";
+import React = require("react");
+import { FieldId } from "../../../fields/Field";
+
+
+@observer
+export class CollectionVideoView extends React.Component<CollectionViewProps> {
+
+    public static LayoutString(fieldKey: string = "DataKey") {
+        return `<${CollectionVideoView.name} Document={Document}
+                    ScreenToLocalTransform={ScreenToLocalTransform} fieldKey={${fieldKey}} panelWidth={PanelWidth} panelHeight={PanelHeight} isSelected={isSelected} select={select} bindings={bindings}
+                    isTopMost={isTopMost} SelectOnLoad={selectOnLoad} BackgroundView={BackgroundView} focus={focus}/>`;
+    }
+
+    public SelectedDocs: FieldId[] = []
+    @action onPageBack = () => this.curPage > 1 ? this.props.Document.SetNumber(KeyStore.CurPage, this.curPage - 1) : 0;
+    @action onPageForward = () => this.curPage < this.numPages ? this.props.Document.SetNumber(KeyStore.CurPage, this.curPage + 1) : 0;
+
+    @computed private get curPage() { return this.props.Document.GetNumber(KeyStore.CurPage, 0); }
+    @computed private get numPages() { return this.props.Document.GetNumber(KeyStore.NumPages, 0); }
+    // "inherited" CollectionView API starts here...
+
+    public active: () => boolean = () => CollectionView.Active(this);
+
+    addDocument = (doc: Document): void => { CollectionView.AddDocument(this.props, doc); }
+    removeDocument = (doc: Document): boolean => { return CollectionView.RemoveDocument(this.props, doc); }
+
+    specificContextMenu = (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: "VideoOptions", event: () => { } });
+        }
+    }
+
+    get collectionViewType(): CollectionViewType { return CollectionViewType.Freeform; }
+    get subView(): any { return CollectionView.SubView(this); }
+
+    render() {
+        return (<div className="collectionView-cont" onContextMenu={this.specificContextMenu}>
+            {/* <video controls className="videobox-cont"> */}
+            {this.subView}
+            {/* </video> */}
+        </div>)
+    }
+}
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2


From 41f458fb4f383bfbf8d5b651189c2f4731262ff8 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Wed, 13 Mar 2019 20:33:51 -0400
Subject: added annotations for videos.  added custom play controls for videos.

---
 src/client/documents/Documents.ts                  |  5 +-
 src/client/views/InkingCanvas.tsx                  |  6 +-
 .../views/collections/CollectionFreeFormView.tsx   |  8 +--
 src/client/views/collections/CollectionPDFView.tsx | 13 ++--
 .../views/collections/CollectionVideoView.scss     | 35 +++++++++
 .../views/collections/CollectionVideoView.tsx      | 83 +++++++++++++++++++---
 src/client/views/collections/CollectionView.tsx    |  2 +-
 src/client/views/nodes/PDFBox.tsx                  |  2 +-
 src/client/views/nodes/VideoBox.tsx                |  2 +-
 src/fields/KeyStore.ts                             |  1 -
 10 files changed, 126 insertions(+), 31 deletions(-)
 create mode 100644 src/client/views/collections/CollectionVideoView.scss

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index f35d553b1..15e16a491 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -23,6 +23,7 @@ import { PDFField } from "../../fields/PDFField";
 import { PDFBox } from "../views/nodes/PDFBox";
 import { CollectionPDFView } from "../views/collections/CollectionPDFView";
 import { RichTextField } from "../../fields/RichTextField";
+import { CollectionVideoView } from "../views/collections/CollectionVideoView";
 
 export interface DocumentOptions {
     x?: number;
@@ -138,9 +139,9 @@ export namespace Documents {
     }
     function GetVideoPrototype(): Document {
         if (!videoProto) {
-            videoProto = setupPrototypeOptions(videoProtoId, "VIDEO_PROTO", CollectionView.LayoutString("AnnotationsKey"),
+            videoProto = setupPrototypeOptions(videoProtoId, "VIDEO_PROTO", CollectionVideoView.LayoutString("AnnotationsKey"),
                 { x: 0, y: 0, nativeWidth: 600, width: 300, layoutKeys: [KeyStore.Data, KeyStore.Annotations] });
-            videoProto.SetNumber(KeyStore.CurFrame, 1);
+            videoProto.SetNumber(KeyStore.CurPage, 0);
             videoProto.SetText(KeyStore.BackgroundLayout, VideoBox.LayoutString());
         }
         return videoProto;
diff --git a/src/client/views/InkingCanvas.tsx b/src/client/views/InkingCanvas.tsx
index 0d87c1239..064d1cba2 100644
--- a/src/client/views/InkingCanvas.tsx
+++ b/src/client/views/InkingCanvas.tsx
@@ -74,7 +74,7 @@ export class InkingCanvas extends React.Component<InkCanvasProps> {
                 color: InkingControl.Instance.selectedColor,
                 width: InkingControl.Instance.selectedWidth,
                 tool: InkingControl.Instance.selectedTool,
-                page: this.props.Document.GetNumber(KeyStore.CurPage, 0)
+                page: this.props.Document.GetNumber(KeyStore.CurPage, -1)
             });
         this.inkData = data;
         this._isDrawing = true;
@@ -145,11 +145,11 @@ export class InkingCanvas extends React.Component<InkCanvasProps> {
 
         // parse data from server
         let paths: Array<JSX.Element> = []
-        let curPage = this.props.Document.GetNumber(KeyStore.CurPage, 0)
+        let curPage = this.props.Document.GetNumber(KeyStore.CurPage, -1)
         Array.from(lines).map(item => {
             let id = item[0];
             let strokeData = item[1];
-            if (strokeData.page == 0 || strokeData.page == curPage)
+            if (strokeData.page == -1 || strokeData.page == curPage)
                 paths.push(<InkingStroke key={id} id={id}
                     line={strokeData.pathData}
                     color={strokeData.color}
diff --git a/src/client/views/collections/CollectionFreeFormView.tsx b/src/client/views/collections/CollectionFreeFormView.tsx
index 71f429e80..08f3a338b 100644
--- a/src/client/views/collections/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/CollectionFreeFormView.tsx
@@ -52,13 +52,13 @@ export class CollectionFreeFormView extends CollectionViewBase {
     }
 
     public getActiveDocuments = () => {
-        var curPage = this.props.Document.GetNumber(KeyStore.CurPage, 1);
+        var curPage = this.props.Document.GetNumber(KeyStore.CurPage, -1);
         const lvalue = this.props.Document.GetT<ListField<Document>>(this.props.fieldKey, ListField);
         let active: Document[] = [];
         if (lvalue && lvalue != FieldWaiting) {
             lvalue.Data.map(doc => {
-                var page = doc.GetNumber(KeyStore.Page, 0);
-                if (page == curPage || page == 0) {
+                var page = doc.GetNumber(KeyStore.Page, -1);
+                if (page == curPage || page == -1) {
                     active.push(doc);
                 }
             })
@@ -257,7 +257,7 @@ export class CollectionFreeFormView extends CollectionViewBase {
 
     @computed
     get views() {
-        var curPage = this.props.Document.GetNumber(KeyStore.CurPage, 1);
+        var curPage = this.props.Document.GetNumber(KeyStore.CurPage, -1);
         const lvalue = this.props.Document.GetT<ListField<Document>>(this.props.fieldKey, ListField);
         if (lvalue && lvalue != FieldWaiting) {
             return lvalue.Data.map(doc => {
diff --git a/src/client/views/collections/CollectionPDFView.tsx b/src/client/views/collections/CollectionPDFView.tsx
index f22c07060..91ffc9500 100644
--- a/src/client/views/collections/CollectionPDFView.tsx
+++ b/src/client/views/collections/CollectionPDFView.tsx
@@ -18,13 +18,12 @@ export class CollectionPDFView extends React.Component<CollectionViewProps> {
                     isTopMost={isTopMost} SelectOnLoad={selectOnLoad} BackgroundView={BackgroundView} focus={focus}/>`;
     }
 
-    public SelectedDocs: FieldId[] = []
-    @action onPageBack = () => this.curPage > 1 ? this.props.Document.SetNumber(KeyStore.CurPage, this.curPage - 1) : 0;
-    @action onPageForward = () => this.curPage < this.numPages ? this.props.Document.SetNumber(KeyStore.CurPage, this.curPage + 1) : 0;
+    private get curPage() { return this.props.Document.GetNumber(KeyStore.CurPage, -1); }
+    private get numPages() { return this.props.Document.GetNumber(KeyStore.NumPages, 0); }
+    @action onPageBack = () => this.curPage > 1 ? this.props.Document.SetNumber(KeyStore.CurPage, this.curPage - 1) : -1;
+    @action onPageForward = () => this.curPage < this.numPages ? this.props.Document.SetNumber(KeyStore.CurPage, this.curPage + 1) : -1;
 
-    @computed private get curPage() { return this.props.Document.GetNumber(KeyStore.CurPage, 0); }
-    @computed private get numPages() { return this.props.Document.GetNumber(KeyStore.NumPages, 0); }
-    @computed private get uIButtons() {
+    private get uIButtons() {
         return (
             <div className="pdfBox-buttonTray" key="tray">
                 <button className="pdfButton" onClick={this.onPageBack}>{"<"}</button>
@@ -33,7 +32,7 @@ export class CollectionPDFView extends React.Component<CollectionViewProps> {
     }
 
     // "inherited" CollectionView API starts here...
-
+    public SelectedDocs: FieldId[] = []
     public active: () => boolean = () => CollectionView.Active(this);
 
     addDocument = (doc: Document): void => { CollectionView.AddDocument(this.props, doc); }
diff --git a/src/client/views/collections/CollectionVideoView.scss b/src/client/views/collections/CollectionVideoView.scss
new file mode 100644
index 000000000..6c2f5a62a
--- /dev/null
+++ b/src/client/views/collections/CollectionVideoView.scss
@@ -0,0 +1,35 @@
+.collectionVideoView-controls{
+    width: 100%;
+    height: 100%;
+    position: absolute;
+}
+.collectionVideoView-time{
+    color : white;
+    top :25px;
+    left : 25px;
+    position: absolute;
+    background-color: rgba(50, 50, 50, 0.2);
+}
+.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;
+}
+.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;
+
+}
\ No newline at end of file
diff --git a/src/client/views/collections/CollectionVideoView.tsx b/src/client/views/collections/CollectionVideoView.tsx
index a6471f53c..058325b21 100644
--- a/src/client/views/collections/CollectionVideoView.tsx
+++ b/src/client/views/collections/CollectionVideoView.tsx
@@ -1,4 +1,4 @@
-import { action, computed } from "mobx";
+import { action, computed, observable } from "mobx";
 import { observer } from "mobx-react";
 import { Document } from "../../../fields/Document";
 import { KeyStore } from "../../../fields/KeyStore";
@@ -7,6 +7,8 @@ import { CollectionView, CollectionViewType } from "./CollectionView";
 import { CollectionViewProps } from "./CollectionViewBase";
 import React = require("react");
 import { FieldId } from "../../../fields/Field";
+import { ReplaceAroundStep } from "prosemirror-transform";
+import "./CollectionVideoView.scss"
 
 
 @observer
@@ -18,14 +20,10 @@ export class CollectionVideoView extends React.Component<CollectionViewProps> {
                     isTopMost={isTopMost} SelectOnLoad={selectOnLoad} BackgroundView={BackgroundView} focus={focus}/>`;
     }
 
-    public SelectedDocs: FieldId[] = []
-    @action onPageBack = () => this.curPage > 1 ? this.props.Document.SetNumber(KeyStore.CurPage, this.curPage - 1) : 0;
-    @action onPageForward = () => this.curPage < this.numPages ? this.props.Document.SetNumber(KeyStore.CurPage, this.curPage + 1) : 0;
-
-    @computed private get curPage() { return this.props.Document.GetNumber(KeyStore.CurPage, 0); }
-    @computed private get numPages() { return this.props.Document.GetNumber(KeyStore.NumPages, 0); }
+    private _mainCont = React.createRef<HTMLDivElement>();
     // "inherited" CollectionView API starts here...
 
+    public SelectedDocs: FieldId[] = []
     public active: () => boolean = () => CollectionView.Active(this);
 
     addDocument = (doc: Document): void => { CollectionView.AddDocument(this.props, doc); }
@@ -40,11 +38,74 @@ export class CollectionVideoView extends React.Component<CollectionViewProps> {
     get collectionViewType(): CollectionViewType { return CollectionViewType.Freeform; }
     get subView(): any { return CollectionView.SubView(this); }
 
+    componentDidMount() {
+        this.repete();
+    }
+
+    player = (): HTMLVideoElement => {
+        return this._mainCont.current!.getElementsByTagName("video")[0];
+    }
+
+    @action
+    repete = () => {
+        if (this.player()) {
+            this.ctime = this.player().currentTime;
+            this.props.Document.SetNumber(KeyStore.CurPage, Math.round(this.ctime));
+        }
+        setTimeout(() => this.repete(), 100)
+    }
+
+
+    @observable
+    ctime: number = 0
+    @observable
+    playing: boolean = false;
+
+    @action
+    onPlayDown = () => {
+        if (this.player()) {
+            if (this.player().paused) {
+                this.player().play();
+                this.playing = true;
+            } else {
+                this.player().pause();
+                this.playing = false;
+            }
+        }
+    }
+    @action
+    onFullDown = (e: React.PointerEvent) => {
+        if (this.player()) {
+            this.player().requestFullscreen();
+            e.stopPropagation();
+            e.preventDefault();
+        }
+    }
+
+    @action
+    onResetDown = () => {
+        if (this.player()) {
+            this.player().pause();
+            this.player().currentTime = 0;
+        }
+
+    }
+
     render() {
-        return (<div className="collectionView-cont" onContextMenu={this.specificContextMenu}>
-            {/* <video controls className="videobox-cont"> */}
-            {this.subView}
-            {/* </video> */}
+        return (<div className="collectionVideoView-cont" ref={this._mainCont} onContextMenu={this.specificContextMenu}>
+            <div className="collectionVideoView-controls" >
+                {this.subView}
+                <div className="collectionVideoView-time" onPointerDown={this.onResetDown}>
+                    <span>{"" + Math.round(this.ctime)}</span>
+                    <span style={{ fontSize: 8 }}>{" " + Math.round((this.ctime - Math.trunc(this.ctime)) * 100)}</span>
+                </div>
+                <div className="collectionVideoView-play" onPointerDown={this.onPlayDown}>
+                    {this.playing ? "\"" : ">"}
+                </div>
+                <div className="collectionVideoView-full" onPointerDown={this.onFullDown}>
+                    F
+                </div>
+            </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 17a0fbd23..bbb4a5d83 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -50,7 +50,7 @@ export class CollectionView extends React.Component<CollectionViewProps> {
 
     @action
     public static AddDocument(props: CollectionViewProps, doc: Document) {
-        doc.SetNumber(KeyStore.Page, props.Document.GetNumber(KeyStore.CurPage, 0));
+        doc.SetNumber(KeyStore.Page, props.Document.GetNumber(KeyStore.CurPage, -1));
         if (props.Document.Get(props.fieldKey) instanceof Field) {
             //TODO This won't create the field if it doesn't already exist
             const value = props.Document.GetData(props.fieldKey, ListField, new Array<Document>())
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 544af41a2..b0b5a63a4 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -86,7 +86,7 @@ export class PDFBox extends React.Component<FieldViewProps> {
     @observable private _interactive: boolean = false;
     @observable private _loaded: boolean = false;
 
-    @computed private get curPage() { return this.props.doc.GetNumber(KeyStore.CurPage, 0); }
+    @computed private get curPage() { return this.props.doc.GetNumber(KeyStore.CurPage, -1); }
 
     componentDidMount() {
         this._reactionDisposer = reaction(
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 9c60d4348..053d9dd56 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -51,7 +51,7 @@ export class VideoBox extends React.Component<FieldViewProps> {
             <div style={{ width: "100%", height: "Auto" }} >
                 <Measure onResize={this.setScaling}>
                     {({ measureRef }) =>
-                        <video controls className="videobox-cont" ref={measureRef}>
+                        <video className="videobox-cont" ref={measureRef}>
                             <source src={path} type="video/mp4" />
                             Not supported.
                         </video>
diff --git a/src/fields/KeyStore.ts b/src/fields/KeyStore.ts
index 6481e14fe..f93a68c85 100644
--- a/src/fields/KeyStore.ts
+++ b/src/fields/KeyStore.ts
@@ -33,7 +33,6 @@ export namespace KeyStore {
     export const LinkTags = new Key("LinkTag");
     export const Thumbnail = new Key("Thumbnail");
     export const CurPage = new Key("CurPage");
-    export const CurFrame = new Key("CurFrame");
     export const NumPages = new Key("NumPages");
     export const Ink = new Key("Ink");
 }
-- 
cgit v1.2.3-70-g09d2


From f905d60fecaf7301c1ffb716529d6e5b86130798 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Thu, 14 Mar 2019 02:11:39 -0400
Subject: fixed marquee

---
 src/client/views/collections/CollectionPDFView.tsx     | 3 ++-
 src/client/views/collections/CollectionSchemaView.scss | 5 +++++
 src/client/views/collections/CollectionVideoView.tsx   | 1 +
 src/client/views/collections/CollectionView.tsx        | 5 ++---
 4 files changed, 10 insertions(+), 4 deletions(-)

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

diff --git a/src/client/views/collections/CollectionPDFView.tsx b/src/client/views/collections/CollectionPDFView.tsx
index 91ffc9500..be3a281f2 100644
--- a/src/client/views/collections/CollectionPDFView.tsx
+++ b/src/client/views/collections/CollectionPDFView.tsx
@@ -1,4 +1,4 @@
-import { action, computed } from "mobx";
+import { action, computed, observable } from "mobx";
 import { observer } from "mobx-react";
 import { Document } from "../../../fields/Document";
 import { KeyStore } from "../../../fields/KeyStore";
@@ -32,6 +32,7 @@ export class CollectionPDFView extends React.Component<CollectionViewProps> {
     }
 
     // "inherited" CollectionView API starts here...
+    @observable
     public SelectedDocs: FieldId[] = []
     public active: () => boolean = () => CollectionView.Active(this);
 
diff --git a/src/client/views/collections/CollectionSchemaView.scss b/src/client/views/collections/CollectionSchemaView.scss
index d40e6d314..c4e6528e5 100644
--- a/src/client/views/collections/CollectionSchemaView.scss
+++ b/src/client/views/collections/CollectionSchemaView.scss
@@ -71,6 +71,11 @@
                 max-width: 100%;
                 height: 100%
             }
+            .videobox-cont  {
+                object-fit: contain;
+                max-width: 100%;
+                height: 100%
+            }
         }
         .rt-tr-group {
             border-width: 1;
diff --git a/src/client/views/collections/CollectionVideoView.tsx b/src/client/views/collections/CollectionVideoView.tsx
index 058325b21..9856a1284 100644
--- a/src/client/views/collections/CollectionVideoView.tsx
+++ b/src/client/views/collections/CollectionVideoView.tsx
@@ -23,6 +23,7 @@ export class CollectionVideoView extends React.Component<CollectionViewProps> {
     private _mainCont = React.createRef<HTMLDivElement>();
     // "inherited" CollectionView API starts here...
 
+    @observable
     public SelectedDocs: FieldId[] = []
     public active: () => boolean = () => CollectionView.Active(this);
 
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index bbb4a5d83..303379fc4 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -27,15 +27,14 @@ export const COLLECTION_BORDER_WIDTH = 2;
 @observer
 export class CollectionView extends React.Component<CollectionViewProps> {
 
-    @observable
-    public SelectedDocs: FieldId[] = [];
-
     public static LayoutString(fieldKey: string = "DataKey") {
         return `<${CollectionView.name} Document={Document}
                     ScreenToLocalTransform={ScreenToLocalTransform} fieldKey={${fieldKey}} panelWidth={PanelWidth} panelHeight={PanelHeight} isSelected={isSelected} select={select} bindings={bindings}
                     isTopMost={isTopMost} SelectOnLoad={selectOnLoad} BackgroundView={BackgroundView} focus={focus}/>`;
     }
 
+    @observable
+    public SelectedDocs: FieldId[] = [];
     public active: () => boolean = () => CollectionView.Active(this);
     addDocument = (doc: Document): void => { CollectionView.AddDocument(this.props, doc); }
     removeDocument = (doc: Document): boolean => { return CollectionView.RemoveDocument(this.props, doc); }
-- 
cgit v1.2.3-70-g09d2


From 7921253dfc1e115b9e8beaa845f952fa5c3eb226 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Thu, 14 Mar 2019 09:54:04 -0400
Subject: fixed zoom behavior of pdf / video buttons.

---
 .../views/collections/CollectionFreeFormView.tsx   |  2 +-
 .../views/collections/CollectionPDFView.scss       | 28 +++++++++
 src/client/views/collections/CollectionPDFView.tsx | 10 ++--
 .../views/collections/CollectionVideoView.scss     | 10 +++-
 .../views/collections/CollectionVideoView.tsx      | 67 ++++++++++++----------
 src/client/views/collections/PreviewCursor.tsx     |  8 ++-
 src/client/views/nodes/PDFBox.scss                 |  2 +-
 7 files changed, 87 insertions(+), 40 deletions(-)
 create mode 100644 src/client/views/collections/CollectionPDFView.scss

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

diff --git a/src/client/views/collections/CollectionFreeFormView.tsx b/src/client/views/collections/CollectionFreeFormView.tsx
index 08f3a338b..98fb23fa4 100644
--- a/src/client/views/collections/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/CollectionFreeFormView.tsx
@@ -335,7 +335,7 @@ export class CollectionFreeFormView extends CollectionViewBase {
                     ref={this._canvasRef}>
                     {this.backgroundView}
                     <InkingCanvas getScreenTransform={this.getTransform} Document={this.props.Document} />
-                    <PreviewCursor container={this} addLiveTextDocuemnt={this.addLiveTextBox} getTransform={this.getTransform} />
+                    <PreviewCursor container={this} addLiveTextDocument={this.addLiveTextBox} getTransform={this.getTransform} />
                     {this.views}
                 </div>
                 <MarqueeView container={this} activeDocuemnts={this.getActiveDocuments} selectDocuments={this.selectDocuments} addDocument={this.props.addDocument} removeDocument={this.props.removeDocument} getMarqueeTransform={this.getMarqueeTransform} getTransform={this.getTransform} />
diff --git a/src/client/views/collections/CollectionPDFView.scss b/src/client/views/collections/CollectionPDFView.scss
new file mode 100644
index 000000000..28878c6fa
--- /dev/null
+++ b/src/client/views/collections/CollectionPDFView.scss
@@ -0,0 +1,28 @@
+.collectionPdfView-buttonTray {
+    width: 100%;
+    height: 100%;
+    top : 25px;
+    left : 20px;
+    position: relative; 
+    transform-origin: left top;
+    position: absolute;
+}
+.collectionPdfView-cont{
+    width: 100%;
+    height: 100%;
+    position: absolute; 
+}
+.collectionPdfView-backward {
+    color : white;
+    top :0px;
+    left : 0px;
+    position: absolute;
+    background-color: rgba(50, 50, 50, 0.2);
+}
+.collectionPdfView-forward {
+    color : white;
+    top :0px;
+    left : 25px;
+    position: absolute;
+    background-color: rgba(50, 50, 50, 0.2);
+}
\ No newline at end of file
diff --git a/src/client/views/collections/CollectionPDFView.tsx b/src/client/views/collections/CollectionPDFView.tsx
index be3a281f2..124d82c8b 100644
--- a/src/client/views/collections/CollectionPDFView.tsx
+++ b/src/client/views/collections/CollectionPDFView.tsx
@@ -5,6 +5,7 @@ import { KeyStore } from "../../../fields/KeyStore";
 import { ContextMenu } from "../ContextMenu";
 import { CollectionView, CollectionViewType } from "./CollectionView";
 import { CollectionViewProps } from "./CollectionViewBase";
+import "./CollectionPDFView.scss"
 import React = require("react");
 import { FieldId } from "../../../fields/Field";
 
@@ -24,10 +25,11 @@ export class CollectionPDFView extends React.Component<CollectionViewProps> {
     @action onPageForward = () => this.curPage < this.numPages ? this.props.Document.SetNumber(KeyStore.CurPage, this.curPage + 1) : -1;
 
     private get uIButtons() {
+        let scaling = Math.min(1.8, this.props.ScreenToLocalTransform().transformDirection(1, 1)[0]);
         return (
-            <div className="pdfBox-buttonTray" key="tray">
-                <button className="pdfButton" onClick={this.onPageBack}>{"<"}</button>
-                <button className="pdfButton" onClick={this.onPageForward}>{">"}</button>
+            <div className="collectionPdfView-buttonTray" key="tray" style={{ transform: `scale(${scaling}, ${scaling})` }}>
+                <button className="collectionPdfView-backward" onClick={this.onPageBack}>{"<"}</button>
+                <button className="collectionPdfView-forward" onClick={this.onPageForward}>{">"}</button>
             </div>);
     }
 
@@ -49,7 +51,7 @@ export class CollectionPDFView extends React.Component<CollectionViewProps> {
     get subView(): any { return CollectionView.SubView(this); }
 
     render() {
-        return (<div className="collectionView-cont" onContextMenu={this.specificContextMenu}>
+        return (<div className="collectionPdfView-cont" onContextMenu={this.specificContextMenu}>
             {this.subView}
             {this.props.isSelected() ? this.uIButtons : (null)}
         </div>)
diff --git a/src/client/views/collections/CollectionVideoView.scss b/src/client/views/collections/CollectionVideoView.scss
index 6c2f5a62a..ec3c0327d 100644
--- a/src/client/views/collections/CollectionVideoView.scss
+++ b/src/client/views/collections/CollectionVideoView.scss
@@ -1,4 +1,9 @@
-.collectionVideoView-controls{
+.collectionVideoView-buttonTray{
+    width: 100%;
+    height: 100%;
+    position: absolute; 
+}
+.collectionVideoView-cont{
     width: 100%;
     height: 100%;
     position: absolute;
@@ -9,6 +14,7 @@
     left : 25px;
     position: absolute;
     background-color: rgba(50, 50, 50, 0.2);
+    transform-origin: left top;
 }
 .collectionVideoView-play {
     width: 25px;
@@ -20,6 +26,7 @@
     background-color: rgba(50, 50, 50, 0.2);
     border-radius: 4px;
     text-align: center;
+    transform-origin: left bottom;
 }
 .collectionVideoView-full {
     width: 25px;
@@ -31,5 +38,6 @@
     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
index 9856a1284..9b01c23c9 100644
--- a/src/client/views/collections/CollectionVideoView.tsx
+++ b/src/client/views/collections/CollectionVideoView.tsx
@@ -7,7 +7,6 @@ import { CollectionView, CollectionViewType } from "./CollectionView";
 import { CollectionViewProps } from "./CollectionViewBase";
 import React = require("react");
 import { FieldId } from "../../../fields/Field";
-import { ReplaceAroundStep } from "prosemirror-transform";
 import "./CollectionVideoView.scss"
 
 
@@ -21,6 +20,25 @@ export class CollectionVideoView extends React.Component<CollectionViewProps> {
     }
 
     private _mainCont = React.createRef<HTMLDivElement>();
+
+    private get uIButtons() {
+        let scaling = Math.min(1.8, this.props.ScreenToLocalTransform().transformDirection(1, 1)[0]);
+        return (
+            <div className="collectionVideoView-buttonTray" key="tray">
+                <div className="collectionVideoView-time" onPointerDown={this.onResetDown} style={{ transform: `scale(${scaling}, ${scaling})` }}>
+                    <span>{"" + Math.round(this.ctime)}</span>
+                    <span style={{ fontSize: 8 }}>{" " + Math.round((this.ctime - Math.trunc(this.ctime)) * 100)}</span>
+                </div>
+                <div className="collectionVideoView-play" onPointerDown={this.onPlayDown} style={{ transform: `scale(${scaling}, ${scaling})` }}>
+                    {this.playing ? "\"" : ">"}
+                </div>
+                <div className="collectionVideoView-full" onPointerDown={this.onFullDown} style={{ transform: `scale(${scaling}, ${scaling})` }}>
+                    F
+                </div>
+            </div>);
+    }
+
+
     // "inherited" CollectionView API starts here...
 
     @observable
@@ -40,20 +58,20 @@ export class CollectionVideoView extends React.Component<CollectionViewProps> {
     get subView(): any { return CollectionView.SubView(this); }
 
     componentDidMount() {
-        this.repete();
+        this.updateTimecode();
     }
 
-    player = (): HTMLVideoElement => {
-        return this._mainCont.current!.getElementsByTagName("video")[0];
+    get player(): HTMLVideoElement | undefined {
+        return this._mainCont.current ? this._mainCont.current.getElementsByTagName("video")[0] : undefined;
     }
 
     @action
-    repete = () => {
-        if (this.player()) {
-            this.ctime = this.player().currentTime;
+    updateTimecode = () => {
+        if (this.player) {
+            this.ctime = this.player.currentTime;
             this.props.Document.SetNumber(KeyStore.CurPage, Math.round(this.ctime));
         }
-        setTimeout(() => this.repete(), 100)
+        setTimeout(() => this.updateTimecode(), 100)
     }
 
 
@@ -64,20 +82,20 @@ export class CollectionVideoView extends React.Component<CollectionViewProps> {
 
     @action
     onPlayDown = () => {
-        if (this.player()) {
-            if (this.player().paused) {
-                this.player().play();
+        if (this.player) {
+            if (this.player.paused) {
+                this.player.play();
                 this.playing = true;
             } else {
-                this.player().pause();
+                this.player.pause();
                 this.playing = false;
             }
         }
     }
     @action
     onFullDown = (e: React.PointerEvent) => {
-        if (this.player()) {
-            this.player().requestFullscreen();
+        if (this.player) {
+            this.player.requestFullscreen();
             e.stopPropagation();
             e.preventDefault();
         }
@@ -85,28 +103,17 @@ export class CollectionVideoView extends React.Component<CollectionViewProps> {
 
     @action
     onResetDown = () => {
-        if (this.player()) {
-            this.player().pause();
-            this.player().currentTime = 0;
+        if (this.player) {
+            this.player.pause();
+            this.player.currentTime = 0;
         }
 
     }
 
     render() {
         return (<div className="collectionVideoView-cont" ref={this._mainCont} onContextMenu={this.specificContextMenu}>
-            <div className="collectionVideoView-controls" >
-                {this.subView}
-                <div className="collectionVideoView-time" onPointerDown={this.onResetDown}>
-                    <span>{"" + Math.round(this.ctime)}</span>
-                    <span style={{ fontSize: 8 }}>{" " + Math.round((this.ctime - Math.trunc(this.ctime)) * 100)}</span>
-                </div>
-                <div className="collectionVideoView-play" onPointerDown={this.onPlayDown}>
-                    {this.playing ? "\"" : ">"}
-                </div>
-                <div className="collectionVideoView-full" onPointerDown={this.onFullDown}>
-                    F
-                </div>
-            </div>
+            {this.subView}
+            {this.uIButtons}
         </div>)
     }
 }
\ No newline at end of file
diff --git a/src/client/views/collections/PreviewCursor.tsx b/src/client/views/collections/PreviewCursor.tsx
index ab68fbc46..a1411250a 100644
--- a/src/client/views/collections/PreviewCursor.tsx
+++ b/src/client/views/collections/PreviewCursor.tsx
@@ -16,7 +16,7 @@ import { CollectionFreeFormView } from "./CollectionFreeFormView";
 export interface PreviewCursorProps {
     getTransform: () => Transform;
     container: CollectionFreeFormView;
-    addLiveTextDocuemnt: (doc: Document) => void;
+    addLiveTextDocument: (doc: Document) => void;
 }
 
 @observer
@@ -57,11 +57,13 @@ export class PreviewCursor extends React.Component<PreviewCursorProps>  {
     @action
     onKeyPress = (e: KeyboardEvent) => {
         //if not these keys, make a textbox if preview cursor is active!
-        if (!e.ctrlKey && !e.altKey) {
+        if (!e.ctrlKey && !e.altKey && !e.defaultPrevented) {
             //make textbox and add it to this collection
             let [x, y] = this.props.getTransform().transformPoint(this._lastX, this._lastY);
             let newBox = Documents.TextDocument({ width: 200, height: 100, x: x, y: y, title: "new" });
-            this.props.addLiveTextDocuemnt(newBox);
+            this.props.addLiveTextDocument(newBox);
+            e.stopPropagation();
+            e.preventDefault();
         }
     }
 
diff --git a/src/client/views/nodes/PDFBox.scss b/src/client/views/nodes/PDFBox.scss
index 9f92410d4..ad947afd5 100644
--- a/src/client/views/nodes/PDFBox.scss
+++ b/src/client/views/nodes/PDFBox.scss
@@ -11,5 +11,5 @@
 }
 .pdfBox-contentContainer {
     position: absolute;
-    transform-origin: "left top";
+    transform-origin: left top;
 }
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2


From 05b23c6b94b47393ba4d34a393e5080f20aff099 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Thu, 14 Mar 2019 19:14:07 -0400
Subject: fixed scrollzooming on videos.

---
 .../views/collections/CollectionVideoView.scss     |  6 +-----
 .../views/collections/CollectionVideoView.tsx      | 23 +++++++++++-----------
 2 files changed, 12 insertions(+), 17 deletions(-)

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

diff --git a/src/client/views/collections/CollectionVideoView.scss b/src/client/views/collections/CollectionVideoView.scss
index ec3c0327d..ddcd74616 100644
--- a/src/client/views/collections/CollectionVideoView.scss
+++ b/src/client/views/collections/CollectionVideoView.scss
@@ -1,8 +1,4 @@
-.collectionVideoView-buttonTray{
-    width: 100%;
-    height: 100%;
-    position: absolute; 
-}
+
 .collectionVideoView-cont{
     width: 100%;
     height: 100%;
diff --git a/src/client/views/collections/CollectionVideoView.tsx b/src/client/views/collections/CollectionVideoView.tsx
index 9b01c23c9..0e2d250aa 100644
--- a/src/client/views/collections/CollectionVideoView.tsx
+++ b/src/client/views/collections/CollectionVideoView.tsx
@@ -23,19 +23,18 @@ export class CollectionVideoView extends React.Component<CollectionViewProps> {
 
     private get uIButtons() {
         let scaling = Math.min(1.8, this.props.ScreenToLocalTransform().transformDirection(1, 1)[0]);
-        return (
-            <div className="collectionVideoView-buttonTray" key="tray">
-                <div className="collectionVideoView-time" onPointerDown={this.onResetDown} style={{ transform: `scale(${scaling}, ${scaling})` }}>
-                    <span>{"" + Math.round(this.ctime)}</span>
-                    <span style={{ fontSize: 8 }}>{" " + Math.round((this.ctime - Math.trunc(this.ctime)) * 100)}</span>
+        return ([
+            <div className="collectionVideoView-time" onPointerDown={this.onResetDown} style={{ transform: `scale(${scaling}, ${scaling})` }}>
+                <span>{"" + Math.round(this.ctime)}</span>
+                <span style={{ fontSize: 8 }}>{" " + Math.round((this.ctime - Math.trunc(this.ctime)) * 100)}</span>
+            </div>,
+            <div className="collectionVideoView-play" onPointerDown={this.onPlayDown} style={{ transform: `scale(${scaling}, ${scaling})` }}>
+                {this.playing ? "\"" : ">"}
+            </div>,
+            <div className="collectionVideoView-full" onPointerDown={this.onFullDown} style={{ transform: `scale(${scaling}, ${scaling})` }}>
+                F
                 </div>
-                <div className="collectionVideoView-play" onPointerDown={this.onPlayDown} style={{ transform: `scale(${scaling}, ${scaling})` }}>
-                    {this.playing ? "\"" : ">"}
-                </div>
-                <div className="collectionVideoView-full" onPointerDown={this.onFullDown} style={{ transform: `scale(${scaling}, ${scaling})` }}>
-                    F
-                </div>
-            </div>);
+        ]);
     }
 
 
-- 
cgit v1.2.3-70-g09d2


From f089e10dfd42edc395729a6b71e42385745f364e Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Thu, 14 Mar 2019 22:21:16 -0400
Subject: marquees can now delete ink or move it to a new collection.

---
 src/client/documents/Documents.ts                  |  3 ++
 src/client/views/InkingCanvas.scss                 |  3 +-
 src/client/views/InkingCanvas.tsx                  | 22 +++++-----
 src/client/views/InkingStroke.tsx                  |  2 -
 src/client/views/Main.tsx                          |  2 +-
 .../views/collections/CollectionFreeFormView.tsx   |  4 +-
 .../views/collections/CollectionVideoView.tsx      |  6 +--
 src/client/views/collections/MarqueeView.tsx       | 47 +++++++++++++++++-----
 8 files changed, 63 insertions(+), 26 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 4e0eefcc6..8402118b8 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -24,10 +24,12 @@ import { PDFBox } from "../views/nodes/PDFBox";
 import { CollectionPDFView } from "../views/collections/CollectionPDFView";
 import { RichTextField } from "../../fields/RichTextField";
 import { CollectionVideoView } from "../views/collections/CollectionVideoView";
+import { StrokeData, InkField } from "../../fields/InkField";
 
 export interface DocumentOptions {
     x?: number;
     y?: number;
+    ink?: Map<string, StrokeData>;
     width?: number;
     height?: number;
     nativeWidth?: number;
@@ -87,6 +89,7 @@ export namespace Documents {
         if (options.backgroundColor !== undefined) { doc.SetText(KeyStore.BackgroundColor, options.backgroundColor); }
         if (options.layout !== undefined) { doc.SetText(KeyStore.Layout, options.layout); }
         if (options.layoutKeys !== undefined) { doc.Set(KeyStore.LayoutKeys, new ListField(options.layoutKeys)); }
+        if (options.ink !== undefined) { doc.Set(KeyStore.Ink, new InkField(options.ink)); }
         return doc;
     }
     function setupPrototypeOptions(protoId: string, title: string, layout: string, options: DocumentOptions): Document {
diff --git a/src/client/views/InkingCanvas.scss b/src/client/views/InkingCanvas.scss
index f654b194b..37fa3143d 100644
--- a/src/client/views/InkingCanvas.scss
+++ b/src/client/views/InkingCanvas.scss
@@ -1,8 +1,9 @@
 .inking-canvas {
-    position: fixed;
+    position: absolute;
     top: -50000px;
     left: -50000px; // z-index: 99; //overlays ink on top of everything
     svg {
+        position:absolute;
         width: 100000px;
         height: 100000px;
         .highlight {
diff --git a/src/client/views/InkingCanvas.tsx b/src/client/views/InkingCanvas.tsx
index 064d1cba2..84c47f616 100644
--- a/src/client/views/InkingCanvas.tsx
+++ b/src/client/views/InkingCanvas.tsx
@@ -1,4 +1,5 @@
 import { observer } from "mobx-react";
+import { observable } from "mobx";
 import { action, computed } from "mobx";
 import { InkingControl } from "./InkingControl";
 import React = require("react");
@@ -6,14 +7,10 @@ import { Transform } from "../util/Transform";
 import { Document } from "../../fields/Document";
 import { KeyStore } from "../../fields/KeyStore";
 import { InkField, InkTool, StrokeData, StrokeMap } from "../../fields/InkField";
-import { JsxArgs } from "./nodes/DocumentView";
 import { InkingStroke } from "./InkingStroke";
 import "./InkingCanvas.scss"
-import { CollectionDockingView } from "./collections/CollectionDockingView";
 import { Utils } from "../../Utils";
 import { FieldWaiting } from "../../fields/Field";
-import { getMapLikeKeys } from "mobx/lib/internal";
-
 
 interface InkCanvasProps {
     getScreenTransform: () => Transform;
@@ -22,7 +19,16 @@ interface InkCanvasProps {
 
 @observer
 export class InkingCanvas extends React.Component<InkCanvasProps> {
-
+    static InkOffset: number = 50000;
+    public static IntersectStrokeRect(stroke: StrokeData, selRect: { left: number, top: number, width: number, height: number }): boolean {
+        let inside = false;
+        stroke.pathData.map(val => {
+            if (selRect.left < val.x - InkingCanvas.InkOffset && selRect.left + selRect.width > val.x - InkingCanvas.InkOffset &&
+                selRect.top < val.y - InkingCanvas.InkOffset && selRect.top + selRect.height > val.y - InkingCanvas.InkOffset)
+                inside = true;
+        });
+        return inside
+    }
     private _isDrawing: boolean = false;
     private _idGenerator: string = "";
 
@@ -51,7 +57,6 @@ export class InkingCanvas extends React.Component<InkCanvasProps> {
         document.removeEventListener("mouseup", this.handleMouseUp);
     }
 
-
     @action
     handleMouseDown = (e: React.PointerEvent): void => {
         if (e.button != 0 ||
@@ -62,7 +67,6 @@ export class InkingCanvas extends React.Component<InkCanvasProps> {
         if (InkingControl.Instance.selectedTool === InkTool.Eraser) {
             return
         }
-        e.stopPropagation()
         const point = this.relativeCoordinatesForEvent(e);
 
         // start the new line, saves a uuid to represent the field of the stroke
@@ -110,8 +114,8 @@ export class InkingCanvas extends React.Component<InkCanvasProps> {
 
     relativeCoordinatesForEvent = (e: React.MouseEvent): { x: number, y: number } => {
         let [x, y] = this.props.getScreenTransform().transformPoint(e.clientX, e.clientY);
-        x += 50000
-        y += 50000
+        x += InkingCanvas.InkOffset;
+        y += InkingCanvas.InkOffset;
         return { x, y };
     }
 
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index d724421d3..87b5c43d8 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -21,8 +21,6 @@ export class InkingStroke extends React.Component<StrokeProps> {
     @observable private _strokeColor: string = this.props.color;
     @observable private _strokeWidth: string = this.props.width;
 
-    private _canvasColor: string = "#cdcdcd";
-
     deleteStroke = (e: React.MouseEvent): void => {
         if (InkingControl.Instance.selectedTool === InkTool.Eraser && e.buttons === 1) {
             this.props.deleteCallback(this.props.id);
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index 8ee028076..d73e9b8fe 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -58,7 +58,7 @@ Documents.initProtos(mainDocId, (res?: Document) => {
     let videourl = "http://techslides.com/demos/sample-videos/small.mp4";
     let clearDatabase = action(() => Utils.Emit(Server.Socket, MessageStore.DeleteAll, {}))
     let addTextNode = action(() => Documents.TextDocument({ width: 200, height: 200, title: "a text note" }))
-    let addColNode = action(() => Documents.FreeformDocument([], { width: 200, height: 200, backgroundColor: "Transparent", title: "a freeform collection" }));
+    let addColNode = action(() => Documents.FreeformDocument([], { width: 200, height: 200, title: "a freeform collection" }));
     let addSchemaNode = action(() => Documents.SchemaDocument([Documents.TextDocument()], { width: 200, height: 200, title: "a schema collection" }));
     let addVideoNode = action(() => Documents.VideoDocument(videourl, { width: 200, title: "video node" }));
     let addPDFNode = action(() => Documents.PdfDocument(pdfurl, { width: 200, title: "a schema collection" }));
diff --git a/src/client/views/collections/CollectionFreeFormView.tsx b/src/client/views/collections/CollectionFreeFormView.tsx
index 98fb23fa4..c8b745254 100644
--- a/src/client/views/collections/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/CollectionFreeFormView.tsx
@@ -338,7 +338,9 @@ export class CollectionFreeFormView extends CollectionViewBase {
                     <PreviewCursor container={this} addLiveTextDocument={this.addLiveTextBox} getTransform={this.getTransform} />
                     {this.views}
                 </div>
-                <MarqueeView container={this} activeDocuemnts={this.getActiveDocuments} selectDocuments={this.selectDocuments} addDocument={this.props.addDocument} removeDocument={this.props.removeDocument} getMarqueeTransform={this.getMarqueeTransform} getTransform={this.getTransform} />
+                <MarqueeView container={this} activeDocuments={this.getActiveDocuments} selectDocuments={this.selectDocuments}
+                    addDocument={this.props.addDocument} removeDocument={this.props.removeDocument}
+                    getMarqueeTransform={this.getMarqueeTransform} getTransform={this.getTransform} />
                 {this.overlayView}
             </div>
         );
diff --git a/src/client/views/collections/CollectionVideoView.tsx b/src/client/views/collections/CollectionVideoView.tsx
index 0e2d250aa..b64ef3c07 100644
--- a/src/client/views/collections/CollectionVideoView.tsx
+++ b/src/client/views/collections/CollectionVideoView.tsx
@@ -24,14 +24,14 @@ export class CollectionVideoView extends React.Component<CollectionViewProps> {
     private get uIButtons() {
         let scaling = Math.min(1.8, this.props.ScreenToLocalTransform().transformDirection(1, 1)[0]);
         return ([
-            <div className="collectionVideoView-time" onPointerDown={this.onResetDown} style={{ transform: `scale(${scaling}, ${scaling})` }}>
+            <div className="collectionVideoView-time" key="time" onPointerDown={this.onResetDown} style={{ transform: `scale(${scaling}, ${scaling})` }}>
                 <span>{"" + Math.round(this.ctime)}</span>
                 <span style={{ fontSize: 8 }}>{" " + Math.round((this.ctime - Math.trunc(this.ctime)) * 100)}</span>
             </div>,
-            <div className="collectionVideoView-play" onPointerDown={this.onPlayDown} style={{ transform: `scale(${scaling}, ${scaling})` }}>
+            <div className="collectionVideoView-play" key="play" onPointerDown={this.onPlayDown} style={{ transform: `scale(${scaling}, ${scaling})` }}>
                 {this.playing ? "\"" : ">"}
             </div>,
-            <div className="collectionVideoView-full" onPointerDown={this.onFullDown} style={{ transform: `scale(${scaling}, ${scaling})` }}>
+            <div className="collectionVideoView-full" key="full" onPointerDown={this.onFullDown} style={{ transform: `scale(${scaling}, ${scaling})` }}>
                 F
                 </div>
         ]);
diff --git a/src/client/views/collections/MarqueeView.tsx b/src/client/views/collections/MarqueeView.tsx
index d9e4df1e9..67716f99a 100644
--- a/src/client/views/collections/MarqueeView.tsx
+++ b/src/client/views/collections/MarqueeView.tsx
@@ -3,21 +3,22 @@ import { observer } from "mobx-react";
 import { Document } from "../../../fields/Document";
 import { FieldWaiting, Opt } from "../../../fields/Field";
 import { KeyStore } from "../../../fields/KeyStore";
-import { ListField } from "../../../fields/ListField";
 import { Documents } from "../../documents/Documents";
 import { SelectionManager } from "../../util/SelectionManager";
 import { Transform } from "../../util/Transform";
 import { CollectionFreeFormView } from "./CollectionFreeFormView";
 import "./MarqueeView.scss";
 import React = require("react");
-
+import { InkField, StrokeData } from "../../../fields/InkField";
+import { Utils } from "../../../Utils";
+import { InkingCanvas } from "../InkingCanvas";
 
 interface MarqueeViewProps {
     getMarqueeTransform: () => Transform;
     getTransform: () => Transform;
     container: CollectionFreeFormView;
     addDocument: (doc: Document) => void;
-    activeDocuemnts: () => Document[];
+    activeDocuments: () => Document[];
     selectDocuments: (docs: Document[]) => void;
     removeDocument: (doc: Document) => boolean;
 }
@@ -94,29 +95,57 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
     marqueeCommand = (e: KeyboardEvent) => {
         if (e.key == "Backspace") {
             this.marqueeSelect().map(d => this.props.removeDocument(d));
+            this.props.container.props.Document.SetData(KeyStore.Ink, this.marqueeInkSelect(false), InkField);
             this.cleanupInteractions();
         }
         if (e.key == "c") {
             let bounds = this.Bounds;
-            let selected = this.marqueeSelect().map(m => m);
-            this.marqueeSelect().map(d => this.props.removeDocument(d));
-            //setTimeout(() => {
-            this.props.addDocument(Documents.FreeformDocument(selected.map(d => {
+            let selected = this.marqueeSelect().map(d => {
+                this.props.removeDocument(d);
                 d.SetNumber(KeyStore.X, d.GetNumber(KeyStore.X, 0) - bounds.left - bounds.width / 2);
                 d.SetNumber(KeyStore.Y, d.GetNumber(KeyStore.Y, 0) - bounds.top - bounds.height / 2);
                 d.SetNumber(KeyStore.Page, 0);
                 d.SetText(KeyStore.Title, "" + d.GetNumber(KeyStore.Width, 0) + " " + d.GetNumber(KeyStore.Height, 0));
                 return d;
-            }), { x: bounds.left, y: bounds.top, panx: 0, pany: 0, width: bounds.width, height: bounds.height, title: "a nested collection" }));
+            });
+            let liftedInk = this.marqueeInkSelect(true);
+            this.props.container.props.Document.SetData(KeyStore.Ink, this.marqueeInkSelect(false), InkField);
+            //setTimeout(() => {
+            this.props.addDocument(Documents.FreeformDocument(selected, { x: bounds.left, y: bounds.top, panx: 0, pany: 0, width: bounds.width, backgroundColor: "Transparent", height: bounds.height, ink: liftedInk, title: "a nested collection" }));
             // }, 100);
             this.cleanupInteractions();
         }
     }
+    marqueeInkSelect(select: boolean) {
+        let selRect = this.Bounds;
+        let centerShiftX = 0 - (selRect.left + selRect.width / 2); // moves each point by the offset that shifts the selection's center to the origin.
+        let centerShiftY = 0 - (selRect.top + selRect.height / 2);
+        let ink = this.props.container.props.Document.GetT(KeyStore.Ink, InkField);
+        if (ink && ink != FieldWaiting) {
+            let idata = new Map();
+            ink.Data.forEach((value: StrokeData, key: string, map: any) => {
+                let inside = InkingCanvas.IntersectStrokeRect(value, selRect);
+                if (inside && select) {
+                    idata.set(key,
+                        {
+                            pathData: value.pathData.map(val => { return { x: val.x + centerShiftX, y: val.y + centerShiftY } }),
+                            color: value.color,
+                            width: value.width,
+                            tool: value.tool,
+                            page: -1
+                        });
+                } else if (!inside && !select) {
+                    idata.set(key, value);
+                }
+            })
+            return idata;
+        }
+    }
 
     marqueeSelect() {
         let selRect = this.Bounds;
         let selection: Document[] = [];
-        this.props.activeDocuemnts().map(doc => {
+        this.props.activeDocuments().map(doc => {
             var x = doc.GetNumber(KeyStore.X, 0);
             var y = doc.GetNumber(KeyStore.Y, 0);
             var w = doc.GetNumber(KeyStore.Width, 0);
-- 
cgit v1.2.3-70-g09d2


From 0b6aa6e7fb31ce0f8085bfee77130f4a276cda80 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Mon, 18 Mar 2019 09:35:54 -0400
Subject: tweaks

---
 src/client/views/collections/CollectionFreeFormView.scss | 4 ++--
 src/client/views/collections/CollectionFreeFormView.tsx  | 2 +-
 src/client/views/collections/CollectionVideoView.tsx     | 2 +-
 src/fields/ListField.ts                                  | 6 +++---
 4 files changed, 7 insertions(+), 7 deletions(-)

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

diff --git a/src/client/views/collections/CollectionFreeFormView.scss b/src/client/views/collections/CollectionFreeFormView.scss
index 9c7a03b52..79ca5fccd 100644
--- a/src/client/views/collections/CollectionFreeFormView.scss
+++ b/src/client/views/collections/CollectionFreeFormView.scss
@@ -8,11 +8,11 @@
   }
 
   //nested freeform views
-  .collectionfreeformview-container {
+  //.collectionfreeformview-container {
     // background-image: linear-gradient(to right, $light-color-secondary 1px, transparent 1px),
     //   linear-gradient(to bottom, $light-color-secondary 1px, transparent 1px);
     // background-size: 30px 30px;
-  }
+  //}
 
   border: 0px solid $light-color-secondary;
   border-radius: $border-radius;
diff --git a/src/client/views/collections/CollectionFreeFormView.tsx b/src/client/views/collections/CollectionFreeFormView.tsx
index 808a22a5d..728076ef8 100644
--- a/src/client/views/collections/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/CollectionFreeFormView.tsx
@@ -304,7 +304,7 @@ export class CollectionFreeFormView extends CollectionViewBase {
                 onDrop={this.onDrop.bind(this)}
                 onDragOver={this.onDragOver}
                 onBlur={this.onBlur}
-                style={{ borderWidth: `${COLLECTION_BORDER_WIDTH}px`, }}
+                style={{ borderWidth: `${COLLECTION_BORDER_WIDTH}px` }}// , zIndex: !this.props.isTopMost ? -1 : undefined }}
                 tabIndex={0}
                 ref={this.createDropTarget}>
                 <div className="collectionfreeformview"
diff --git a/src/client/views/collections/CollectionVideoView.tsx b/src/client/views/collections/CollectionVideoView.tsx
index b64ef3c07..8fc1a1b4d 100644
--- a/src/client/views/collections/CollectionVideoView.tsx
+++ b/src/client/views/collections/CollectionVideoView.tsx
@@ -112,7 +112,7 @@ export class CollectionVideoView extends React.Component<CollectionViewProps> {
     render() {
         return (<div className="collectionVideoView-cont" ref={this._mainCont} onContextMenu={this.specificContextMenu}>
             {this.subView}
-            {this.uIButtons}
+            {this.props.isSelected() ? this.uIButtons : (null)}
         </div>)
     }
 }
\ No newline at end of file
diff --git a/src/fields/ListField.ts b/src/fields/ListField.ts
index 77c1d6e14..4527ee548 100644
--- a/src/fields/ListField.ts
+++ b/src/fields/ListField.ts
@@ -20,6 +20,9 @@ export class ListField<T extends Field> extends BasicField<T[]> {
 
     private observeDisposer: Lambda | undefined;
     private observeList(): void {
+        if (this.observeDisposer) {
+            this.observeDisposer()
+        }
         this.observeDisposer = observe(this.Data as IObservableArray<T>, (change: IArrayChange<T> | IArraySplice<T>) => {
             this.updateProxies()
             if (change.type == "splice") {
@@ -39,9 +42,6 @@ export class ListField<T extends Field> extends BasicField<T[]> {
     }
 
     protected setData(value: T[]) {
-        if (this.observeDisposer) {
-            this.observeDisposer()
-        }
         this.data = observable(value);
         this.updateProxies();
         this.observeList();
-- 
cgit v1.2.3-70-g09d2


From 8418b842b16da68150243e1750aa8b40eb8a8792 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Mon, 18 Mar 2019 11:48:24 -0400
Subject: fixed removing from schemas.  fixed dragging link button to move
 quickly. cleaned up drag/drop code a little

---
 src/client/util/DragManager.ts                     | 12 +++-----
 src/client/views/DocumentDecorations.tsx           | 14 ++++-----
 src/client/views/InkingControl.tsx                 |  3 +-
 .../views/collections/CollectionFreeFormView.tsx   |  4 +--
 src/client/views/collections/CollectionPDFView.tsx |  2 +-
 .../views/collections/CollectionSchemaView.tsx     |  4 +--
 .../views/collections/CollectionVideoView.tsx      |  2 +-
 src/client/views/collections/CollectionView.tsx    |  7 +++--
 .../views/collections/CollectionViewBase.tsx       | 36 ++++++++--------------
 src/client/views/collections/MarqueeView.tsx       | 15 +++++++--
 src/client/views/nodes/DocumentView.tsx            | 16 +++++-----
 11 files changed, 57 insertions(+), 58 deletions(-)

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

diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index c0abec407..54784f2d2 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -2,18 +2,18 @@ import { DocumentDecorations } from "../views/DocumentDecorations";
 import { CollectionDockingView } from "../views/collections/CollectionDockingView";
 import { Document } from "../../fields/Document"
 import { action } from "mobx";
-import { DocumentView } from "../views/nodes/DocumentView";
 import { ImageField } from "../../fields/ImageField";
 import { KeyStore } from "../../fields/KeyStore";
+import { CollectionView } from "../views/collections/CollectionView";
 
-export function setupDrag(_reference: React.RefObject<HTMLDivElement>, docFunc: () => Document) {
+export function setupDrag(_reference: React.RefObject<HTMLDivElement>, docFunc: () => Document, removeFunc: (containingCollection: CollectionView) => void = () => { }) {
     let onRowMove = action((e: PointerEvent): void => {
         e.stopPropagation();
         e.preventDefault();
 
         document.removeEventListener("pointermove", onRowMove);
         document.removeEventListener('pointerup', onRowUp);
-        DragManager.StartDrag(_reference.current!, { document: docFunc() });
+        DragManager.StartDrag(_reference.current!, { draggedDocument: docFunc(), removeDocument: removeFunc });
     });
     let onRowUp = action((e: PointerEvent): void => {
         document.removeEventListener("pointermove", onRowMove);
@@ -122,9 +122,7 @@ export namespace DragManager {
         // bcz: PDFs don't show up if you clone them because they contain a canvas.
         //      however, PDF's have a thumbnail field that contains an image of their canvas.
         //      So we replace the pdf's canvas with the image thumbnail
-        const docView: DocumentView = dragData["documentView"];
-        const doc: Document = dragData["document"];
-
+        const doc: Document = dragData["draggedDocument"];
         if (doc) {
             var pdfBox = dragElement.getElementsByClassName("pdfBox-cont")[0] as HTMLElement;
             let thumbnail = doc.GetT(KeyStore.Thumbnail, ImageField);
@@ -138,7 +136,6 @@ export namespace DragManager {
             }
         }
 
-
         dragDiv.appendChild(dragElement);
 
         let hideSource = false;
@@ -191,6 +188,7 @@ export namespace DragManager {
         if (!target) {
             return;
         }
+        dragData["droppedDocument"] = dragData["aliasOnDrop"] ? (dragData["draggedDocument"] as Document).CreateAlias() : dragData["draggedDocument"];
         target.dispatchEvent(new CustomEvent<DropEvent>("dashOnDrop", {
             bubbles: true,
             detail: {
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 3bdb7d5b3..1145fbe7f 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -77,7 +77,6 @@ export class DocumentDecorations extends React.Component {
     }
 
     onLinkButtonUp = (e: PointerEvent): void => {
-        console.log("up");
         document.removeEventListener("pointermove", this.onLinkButtonMoved)
         document.removeEventListener("pointerup", this.onLinkButtonUp)
         e.stopPropagation();
@@ -85,10 +84,11 @@ export class DocumentDecorations extends React.Component {
 
 
     onLinkButtonMoved = (e: PointerEvent): void => {
-        console.log("moved");
-        let dragData: { [id: string]: any } = {};
-        dragData["linkSourceDoc"] = SelectionManager.SelectedDocuments()[0];
         if (this._linkButton.current != null) {
+            document.removeEventListener("pointermove", this.onLinkButtonMoved)
+            document.removeEventListener("pointerup", this.onLinkButtonUp)
+            let dragData: { [id: string]: any } = {};
+            dragData["linkSourceDoc"] = SelectionManager.SelectedDocuments()[0];
             DragManager.StartDrag(this._linkButton.current, dragData, {
                 handlers: {
                     dragComplete: action(() => { }),
@@ -96,8 +96,6 @@ export class DocumentDecorations extends React.Component {
                 hideSource: false
             })
         }
-        document.removeEventListener("pointermove", this.onLinkButtonMoved)
-        document.removeEventListener("pointerup", this.onLinkButtonUp)
         e.stopPropagation();
     }
 
@@ -217,7 +215,7 @@ export class DocumentDecorations extends React.Component {
                     <LinkMenu docView={selFirst} changeFlyout={this.changeFlyoutContent}>
                     </LinkMenu>
                 }>
-                <div className={"linkButton-" + (selFirst.props.Document.GetData(KeyStore.LinkedToDocs, ListField, []).length ? "nonempty" : "empty")} onPointerDown={this.onLinkButtonDown} ref={this._linkButton}>{linkCount}</div>
+                <div className={"linkButton-" + (selFirst.props.Document.GetData(KeyStore.LinkedToDocs, ListField, []).length ? "nonempty" : "empty")} onPointerDown={this.onLinkButtonDown} >{linkCount}</div>
             </Flyout>);
         }
         return (
@@ -237,7 +235,7 @@ export class DocumentDecorations extends React.Component {
                 <div id="documentDecorations-bottomResizer" className="documentDecorations-resizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
                 <div id="documentDecorations-bottomRightResizer" className="documentDecorations-resizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
 
-                <div title="View Links" className="linkFlyout">{linkButton}</div>
+                <div title="View Links" className="linkFlyout" ref={this._linkButton}>{linkButton}</div>
 
             </div >
         )
diff --git a/src/client/views/InkingControl.tsx b/src/client/views/InkingControl.tsx
index 6616f68d8..ad6bbd476 100644
--- a/src/client/views/InkingControl.tsx
+++ b/src/client/views/InkingControl.tsx
@@ -11,6 +11,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { faPen, faHighlighter, faEraser, faBan } from '@fortawesome/free-solid-svg-icons';
 import { SelectionManager } from "../util/SelectionManager";
 import { KeyStore } from "../../fields/KeyStore";
+import { TextField } from "../../fields/TextField";
 
 library.add(faPen, faHighlighter, faEraser, faBan);
 
@@ -39,7 +40,7 @@ export class InkingControl extends React.Component {
         if (SelectionManager.SelectedDocuments().length == 1) {
             var sdoc = SelectionManager.SelectedDocuments()[0];
             if (sdoc.props.ContainingCollectionView && sdoc.props.ContainingCollectionView) {
-                sdoc.props.Document.SetText(KeyStore.BackgroundColor, color.hex);
+                sdoc.props.Document.SetOnPrototype(KeyStore.BackgroundColor, new TextField(color.hex));
             }
         }
     }
diff --git a/src/client/views/collections/CollectionFreeFormView.tsx b/src/client/views/collections/CollectionFreeFormView.tsx
index 728076ef8..85ea2d121 100644
--- a/src/client/views/collections/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/CollectionFreeFormView.tsx
@@ -28,7 +28,7 @@ export class CollectionFreeFormView extends CollectionViewBase {
         // mark this collection so that when the text box is created we can send it the SelectOnLoad prop to focus itself
         this._selectOnLoaded = newBox.Id;
         //set text to be the typed key and get focus on text box
-        this.props.addDocument(newBox);
+        this.props.addDocument(newBox, false);
         //remove cursor from screen
         this.PreviewCursorVisible = false;
     }
@@ -79,7 +79,7 @@ export class CollectionFreeFormView extends CollectionViewBase {
         let screenX = de.x - (de.data["xOffset"] as number || 0);
         let screenY = de.y - (de.data["yOffset"] as number || 0);
         const [x, y] = this.getTransform().transformPoint(screenX, screenY);
-        let doc: Document = de.data["document"];
+        let doc: Document = de.data["droppedDocument"];
         if (doc) {
             doc.SetNumber(KeyStore.X, x);
             doc.SetNumber(KeyStore.Y, y);
diff --git a/src/client/views/collections/CollectionPDFView.tsx b/src/client/views/collections/CollectionPDFView.tsx
index 124d82c8b..e64b4c945 100644
--- a/src/client/views/collections/CollectionPDFView.tsx
+++ b/src/client/views/collections/CollectionPDFView.tsx
@@ -38,7 +38,7 @@ export class CollectionPDFView extends React.Component<CollectionViewProps> {
     public SelectedDocs: FieldId[] = []
     public active: () => boolean = () => CollectionView.Active(this);
 
-    addDocument = (doc: Document): void => { CollectionView.AddDocument(this.props, doc); }
+    addDocument = (doc: Document, allowDuplicates: boolean): void => { CollectionView.AddDocument(this.props, doc, allowDuplicates); }
     removeDocument = (doc: Document): boolean => { return CollectionView.RemoveDocument(this.props, doc); }
 
     specificContextMenu = (e: React.MouseEvent): void => {
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index f88a62a0f..ced08a14e 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -14,7 +14,7 @@ import { EditableView } from "../EditableView";
 import { DocumentView } from "../nodes/DocumentView";
 import { FieldView, FieldViewProps } from "../nodes/FieldView";
 import "./CollectionSchemaView.scss";
-import { COLLECTION_BORDER_WIDTH } from "./CollectionView";
+import { COLLECTION_BORDER_WIDTH, CollectionView } from "./CollectionView";
 import { CollectionViewBase } from "./CollectionViewBase";
 import { setupDrag } from "../../util/DragManager";
 
@@ -47,7 +47,7 @@ export class CollectionSchemaView extends CollectionViewBase {
             <FieldView {...props} />
         )
         let reference = React.createRef<HTMLDivElement>();
-        let onItemDown = setupDrag(reference, () => props.doc);
+        let onItemDown = setupDrag(reference, () => props.doc, (containingCollection: CollectionView) => this.props.removeDocument(props.doc));
         return (
             <div className="collectionSchemaView-cellContents" onPointerDown={onItemDown} style={{ height: "36px" }} key={props.doc.Id} ref={reference}>
                 <EditableView contents={contents}
diff --git a/src/client/views/collections/CollectionVideoView.tsx b/src/client/views/collections/CollectionVideoView.tsx
index 8fc1a1b4d..05f759967 100644
--- a/src/client/views/collections/CollectionVideoView.tsx
+++ b/src/client/views/collections/CollectionVideoView.tsx
@@ -44,7 +44,7 @@ export class CollectionVideoView extends React.Component<CollectionViewProps> {
     public SelectedDocs: FieldId[] = []
     public active: () => boolean = () => CollectionView.Active(this);
 
-    addDocument = (doc: Document): void => { CollectionView.AddDocument(this.props, doc); }
+    addDocument = (doc: Document, allowDuplicates: boolean): void => { CollectionView.AddDocument(this.props, doc, allowDuplicates); }
     removeDocument = (doc: Document): boolean => { return CollectionView.RemoveDocument(this.props, doc); }
 
     specificContextMenu = (e: React.MouseEvent): void => {
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 40acf466e..303099c16 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -36,7 +36,7 @@ export class CollectionView extends React.Component<CollectionViewProps> {
     @observable
     public SelectedDocs: FieldId[] = [];
     public active: () => boolean = () => CollectionView.Active(this);
-    addDocument = (doc: Document): void => { CollectionView.AddDocument(this.props, doc); }
+    addDocument = (doc: Document, allowDuplicates: boolean): void => { CollectionView.AddDocument(this.props, doc, allowDuplicates); }
     removeDocument = (doc: Document): boolean => { return CollectionView.RemoveDocument(this.props, doc); }
     get subView() { return CollectionView.SubView(this); }
 
@@ -48,12 +48,13 @@ export class CollectionView extends React.Component<CollectionViewProps> {
     }
 
     @action
-    public static AddDocument(props: CollectionViewProps, doc: Document) {
+    public static AddDocument(props: CollectionViewProps, doc: Document, allowDuplicates: boolean) {
         doc.SetNumber(KeyStore.Page, props.Document.GetNumber(KeyStore.CurPage, -1));
         if (props.Document.Get(props.fieldKey) instanceof Field) {
             //TODO This won't create the field if it doesn't already exist
             const value = props.Document.GetData(props.fieldKey, ListField, new Array<Document>())
-            value.push(doc);
+            if (!value.some(v => v.Id == doc.Id) || allowDuplicates)
+                value.push(doc);
         } else {
             props.Document.SetOnPrototype(props.fieldKey, new ListField([doc]));
         }
diff --git a/src/client/views/collections/CollectionViewBase.tsx b/src/client/views/collections/CollectionViewBase.tsx
index 3d1a76a07..0358b5907 100644
--- a/src/client/views/collections/CollectionViewBase.tsx
+++ b/src/client/views/collections/CollectionViewBase.tsx
@@ -27,7 +27,7 @@ export interface CollectionViewProps {
 }
 export interface SubCollectionViewProps extends CollectionViewProps {
     active: () => boolean;
-    addDocument: (doc: Document) => void;
+    addDocument: (doc: Document, allowDuplicates: boolean) => void;
     removeDocument: (doc: Document) => boolean;
     CollectionView: CollectionView;
 }
@@ -46,28 +46,16 @@ export class CollectionViewBase extends React.Component<SubCollectionViewProps>
     @undoBatch
     @action
     protected drop(e: Event, de: DragManager.DropEvent) {
-        let docToAlias = de.data["documentToAlias"];
-        let docView = de.data["documentView"];
-        let doc = docToAlias ? docToAlias.CreateAlias() : de.data["document"];
-        if (docToAlias) {
-            [KeyStore.Width, KeyStore.Height].map(key =>
-                docToAlias.GetTAsync(key, NumberField, (f: Opt<NumberField>) => {
-                    if (f) {
-                        doc.SetNumber(key, f.Data)
-                    }
-                })
-            );
-            de.data["document"] = doc;
-            this.props.addDocument(doc);
-        } else if (docView) {
-            if (doc && docView.props.RemoveDocument && docView.props.ContainingCollectionView !== this.props.CollectionView) {
-                docView.props.RemoveDocument(doc);
-                this.props.removeDocument(doc); // bcz: not good -- want to check if it's there and then add if it isn't
-                this.props.addDocument(doc);
+        let dropped = de.data["droppedDocument"];
+        if (dropped) {
+            if (de.data["aliasOnDrop"]) {
+                let dragged = de.data["draggedDocument"];
+                [KeyStore.Width, KeyStore.Height, KeyStore.CurPage].map(key =>
+                    dragged.GetTAsync(key, NumberField, (f: Opt<NumberField>) => f ? dropped.SetNumber(key, f.Data) : null));
+            } else if (de.data["removeDocument"]) {
+                de.data["removeDocument"](this.props.CollectionView);
             }
-        } else if (doc) {
-            this.props.removeDocument(doc); // bcz: not good -- want to check if it's there and then add if it isn't
-            this.props.addDocument(doc);
+            this.props.addDocument(dropped, false);
         }
         e.stopPropagation();
     }
@@ -89,7 +77,7 @@ export class CollectionViewBase extends React.Component<SubCollectionViewProps>
             console.log("not good");
             let htmlDoc = Documents.HtmlDocument(html, { ...options, width: 300, height: 300 });
             htmlDoc.SetText(KeyStore.DocumentText, text);
-            this.props.addDocument(htmlDoc);
+            this.props.addDocument(htmlDoc, false);
             return;
         }
 
@@ -99,7 +87,7 @@ export class CollectionViewBase extends React.Component<SubCollectionViewProps>
             const upload = window.location.origin + "/upload";
             let item = e.dataTransfer.items[i];
             if (item.kind === "string" && item.type.indexOf("uri") != -1) {
-                e.dataTransfer.items[i].getAsString(action((s: string) => this.props.addDocument(Documents.WebDocument(s, options))))
+                e.dataTransfer.items[i].getAsString(action((s: string) => this.props.addDocument(Documents.WebDocument(s, options), false)))
             }
             let type = item.type
             console.log(type)
diff --git a/src/client/views/collections/MarqueeView.tsx b/src/client/views/collections/MarqueeView.tsx
index f5c83a934..8c2f3443c 100644
--- a/src/client/views/collections/MarqueeView.tsx
+++ b/src/client/views/collections/MarqueeView.tsx
@@ -17,7 +17,7 @@ interface MarqueeViewProps {
     getMarqueeTransform: () => Transform;
     getTransform: () => Transform;
     container: CollectionFreeFormView;
-    addDocument: (doc: Document) => void;
+    addDocument: (doc: Document, allowDuplicates: false) => void;
     activeDocuments: () => Document[];
     selectDocuments: (docs: Document[]) => void;
     removeDocument: (doc: Document) => boolean;
@@ -111,7 +111,18 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
             let liftedInk = this.marqueeInkSelect(true);
             this.props.container.props.Document.SetData(KeyStore.Ink, this.marqueeInkSelect(false), InkField);
             //setTimeout(() => {
-            this.props.addDocument(Documents.FreeformDocument(selected, { x: bounds.left, y: bounds.top, panx: 0, pany: 0, width: bounds.width, backgroundColor: "Transparent", height: bounds.height, ink: liftedInk, title: "a nested collection" }));
+            let newCollection = Documents.FreeformDocument(selected, {
+                x: bounds.left,
+                y: bounds.top,
+                panx: 0,
+                pany: 0,
+                width: bounds.width,
+                height: bounds.height,
+                backgroundColor: "Transparent",
+                ink: liftedInk,
+                title: "a nested collection"
+            });
+            this.props.addDocument(newCollection, false);
             // }, 100);
             this.cleanupInteractions();
         }
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index fbd58ef7e..e98815fa6 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -23,7 +23,7 @@ import React = require("react");
 export interface DocumentViewProps {
     ContainingCollectionView: Opt<CollectionView>;
     Document: Document;
-    AddDocument?: (doc: Document) => void;
+    AddDocument?: (doc: Document, allowDuplicates: boolean) => void;
     RemoveDocument?: (doc: Document) => boolean;
     ScreenToLocalTransform: () => Transform;
     isTopMost: boolean;
@@ -164,12 +164,15 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         if (this._mainCont.current) {
             const [left, top] = this.props.ScreenToLocalTransform().inverse().transformPoint(0, 0);
             let dragData: { [id: string]: any } = {};
-            dragData["documentView"] = this;
-            if (dropAliasOfDraggedDoc)
-                dragData["documentToAlias"] = this.props.Document;
-            dragData["document"] = this.props.Document;
+            dragData["aliasOnDrop"] = dropAliasOfDraggedDoc;
+            dragData["draggedDocument"] = this.props.Document;
             dragData["xOffset"] = x - left;
             dragData["yOffset"] = y - top;
+            dragData["removeDocument"] = (dropCollectionView: CollectionView) => {
+                if (this.props.RemoveDocument && this.props.ContainingCollectionView !== dropCollectionView) {
+                    this.props.RemoveDocument(this.props.Document);
+                }
+            }
             DragManager.StartDrag(this._mainCont.current, dragData, {
                 handlers: {
                     dragComplete: action(() => { }),
@@ -213,7 +216,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
 
     fieldsClicked = (e: React.MouseEvent): void => {
         if (this.props.AddDocument) {
-            this.props.AddDocument(Documents.KVPDocument(this.props.Document, { width: 300, height: 300 }));
+            this.props.AddDocument(Documents.KVPDocument(this.props.Document, { width: 300, height: 300 }), false);
         }
     }
     fullScreenClicked = (e: React.MouseEvent): void => {
@@ -232,7 +235,6 @@ export class DocumentView extends React.Component<DocumentViewProps> {
 
     @action
     drop = (e: Event, de: DragManager.DropEvent) => {
-        console.log("drop");
         const sourceDocView: DocumentView = de.data["linkSourceDoc"];
         if (!sourceDocView) {
             return;
-- 
cgit v1.2.3-70-g09d2


From bc59ea805f32568f0835bd55d39575236c24a066 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Thu, 21 Mar 2019 22:22:25 -0400
Subject: added very basic cycle detection when adding to collections

---
 src/client/util/DragManager.ts                     |  4 +---
 .../views/collections/CollectionDockingView.tsx    |  2 +-
 .../views/collections/CollectionFreeFormView.tsx   | 21 ++++++++++++---------
 src/client/views/collections/CollectionPDFView.tsx |  2 +-
 .../views/collections/CollectionVideoView.tsx      |  2 +-
 src/client/views/collections/CollectionView.tsx    | 22 ++++++++++++++++++----
 .../views/collections/CollectionViewBase.tsx       | 12 ++++++++----
 src/client/views/collections/MarqueeView.tsx       |  2 +-
 src/client/views/nodes/DocumentView.tsx            |  2 +-
 9 files changed, 44 insertions(+), 25 deletions(-)

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

diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 753115f76..661fa4dc8 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -197,9 +197,7 @@ export namespace DragManager {
             document.removeEventListener("pointermove", moveHandler, true);
             document.removeEventListener("pointerup", upHandler);
             dragDiv.removeChild(dragElement);
-            if (hideSource && !wasHidden) {
-                ele.hidden = false;
-            }
+            ele.hidden = false;
         }
         const upHandler = (e: PointerEvent) => {
             abortDrag();
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 950df7261..39b284d8e 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -202,7 +202,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
                         handlers: {
                             dragComplete: action(() => { }),
                         },
-                        hideSource: true
+                        hideSource: false
                     }))
             );
         }
diff --git a/src/client/views/collections/CollectionFreeFormView.tsx b/src/client/views/collections/CollectionFreeFormView.tsx
index da9f7b392..8b9d178c9 100644
--- a/src/client/views/collections/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/CollectionFreeFormView.tsx
@@ -75,16 +75,19 @@ export class CollectionFreeFormView extends CollectionViewBase {
 
     @undoBatch
     @action
-    drop = (e: Event, de: DragManager.DropEvent) => {
-        super.drop(e, de);
-        if (de.data instanceof DragManager.DocumentDragData) {
-            let screenX = de.x - (de.data.xOffset as number || 0);
-            let screenY = de.y - (de.data.yOffset as number || 0);
-            const [x, y] = this.getTransform().transformPoint(screenX, screenY);
-            de.data.droppedDocument.SetNumber(KeyStore.X, x);
-            de.data.droppedDocument.SetNumber(KeyStore.Y, y);
-            this.bringToFront(de.data.droppedDocument);
+    drop = (e: Event, de: DragManager.DropEvent): boolean => {
+        if (super.drop(e, de)) {
+            if (de.data instanceof DragManager.DocumentDragData) {
+                let screenX = de.x - (de.data.xOffset as number || 0);
+                let screenY = de.y - (de.data.yOffset as number || 0);
+                const [x, y] = this.getTransform().transformPoint(screenX, screenY);
+                de.data.droppedDocument.SetNumber(KeyStore.X, x);
+                de.data.droppedDocument.SetNumber(KeyStore.Y, y);
+                this.bringToFront(de.data.droppedDocument);
+            }
+            return true;
         }
+        return false;
     }
 
 
diff --git a/src/client/views/collections/CollectionPDFView.tsx b/src/client/views/collections/CollectionPDFView.tsx
index e64b4c945..4d2daf149 100644
--- a/src/client/views/collections/CollectionPDFView.tsx
+++ b/src/client/views/collections/CollectionPDFView.tsx
@@ -38,7 +38,7 @@ export class CollectionPDFView extends React.Component<CollectionViewProps> {
     public SelectedDocs: FieldId[] = []
     public active: () => boolean = () => CollectionView.Active(this);
 
-    addDocument = (doc: Document, allowDuplicates: boolean): void => { CollectionView.AddDocument(this.props, doc, allowDuplicates); }
+    addDocument = (doc: Document, allowDuplicates: boolean): boolean => { return CollectionView.AddDocument(this.props, doc, allowDuplicates); }
     removeDocument = (doc: Document): boolean => { return CollectionView.RemoveDocument(this.props, doc); }
 
     specificContextMenu = (e: React.MouseEvent): void => {
diff --git a/src/client/views/collections/CollectionVideoView.tsx b/src/client/views/collections/CollectionVideoView.tsx
index 05f759967..a3921696f 100644
--- a/src/client/views/collections/CollectionVideoView.tsx
+++ b/src/client/views/collections/CollectionVideoView.tsx
@@ -44,7 +44,7 @@ export class CollectionVideoView extends React.Component<CollectionViewProps> {
     public SelectedDocs: FieldId[] = []
     public active: () => boolean = () => CollectionView.Active(this);
 
-    addDocument = (doc: Document, allowDuplicates: boolean): void => { CollectionView.AddDocument(this.props, doc, allowDuplicates); }
+    addDocument = (doc: Document, allowDuplicates: boolean): boolean => { return CollectionView.AddDocument(this.props, doc, allowDuplicates); }
     removeDocument = (doc: Document): boolean => { return CollectionView.RemoveDocument(this.props, doc); }
 
     specificContextMenu = (e: React.MouseEvent): void => {
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 7e1d31018..2e93e6bb8 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -37,7 +37,7 @@ export class CollectionView extends React.Component<CollectionViewProps> {
     @observable
     public SelectedDocs: FieldId[] = [];
     public active: () => boolean = () => CollectionView.Active(this);
-    addDocument = (doc: Document, allowDuplicates: boolean): void => { CollectionView.AddDocument(this.props, doc, allowDuplicates); }
+    addDocument = (doc: Document, allowDuplicates: boolean): boolean => { return CollectionView.AddDocument(this.props, doc, allowDuplicates); }
     removeDocument = (doc: Document): boolean => { return CollectionView.RemoveDocument(this.props, doc); }
     get subView() { return CollectionView.SubView(this); }
 
@@ -48,17 +48,31 @@ export class CollectionView extends React.Component<CollectionViewProps> {
         return isSelected || childSelected || topMost;
     }
 
+    static createsCycle(doc: Document, props: CollectionViewProps): boolean {
+        let ldata = doc.GetList<Document>(KeyStore.Data, []);
+        for (let i = 0; i < ldata.length; i++) {
+            if (CollectionView.createsCycle(ldata[i], props))
+                return true;
+        }
+        return doc.Id == props.Document.Id;
+    }
+
     @action
-    public static AddDocument(props: CollectionViewProps, doc: Document, allowDuplicates: boolean) {
+    public static AddDocument(props: CollectionViewProps, doc: Document, allowDuplicates: boolean): boolean {
         doc.SetNumber(KeyStore.Page, props.Document.GetNumber(KeyStore.CurPage, -1));
         if (props.Document.Get(props.fieldKey) instanceof Field) {
             //TODO This won't create the field if it doesn't already exist
             const value = props.Document.GetData(props.fieldKey, ListField, new Array<Document>())
-            if (!value.some(v => v.Id == doc.Id) || allowDuplicates)
-                value.push(doc);
+            if (!CollectionView.createsCycle(doc, props)) {
+                if (!value.some(v => v.Id == doc.Id) || allowDuplicates)
+                    value.push(doc);
+            }
+            else
+                return false;
         } else {
             props.Document.SetOnPrototype(props.fieldKey, new ListField([doc]));
         }
+        return true;
     }
 
     @action
diff --git a/src/client/views/collections/CollectionViewBase.tsx b/src/client/views/collections/CollectionViewBase.tsx
index f33007196..535a8a8d2 100644
--- a/src/client/views/collections/CollectionViewBase.tsx
+++ b/src/client/views/collections/CollectionViewBase.tsx
@@ -17,6 +17,7 @@ import { NumberField } from "../../../fields/NumberField";
 import { DocumentManager } from "../../util/DocumentManager";
 import request = require("request");
 import { ServerUtils } from "../../../server/ServerUtil";
+import { addHiddenFinalProp } from "mobx/lib/internal";
 
 export interface CollectionViewProps {
     fieldKey: Key;
@@ -33,7 +34,7 @@ export interface CollectionViewProps {
 
 export interface SubCollectionViewProps extends CollectionViewProps {
     active: () => boolean;
-    addDocument: (doc: Document, allowDuplicates: boolean) => void;
+    addDocument: (doc: Document, allowDuplicates: boolean) => boolean;
     removeDocument: (doc: Document) => boolean;
     CollectionView: CollectionView;
 }
@@ -83,17 +84,20 @@ export class CollectionViewBase extends React.Component<SubCollectionViewProps>
 
     @undoBatch
     @action
-    protected drop(e: Event, de: DragManager.DropEvent) {
+    protected drop(e: Event, de: DragManager.DropEvent): boolean {
         if (de.data instanceof DragManager.DocumentDragData) {
             if (de.data.aliasOnDrop) {
                 [KeyStore.Width, KeyStore.Height, KeyStore.CurPage].map(key =>
                     de.data.draggedDocument.GetTAsync(key, NumberField, (f: Opt<NumberField>) => f ? de.data.droppedDocument.SetNumber(key, f.Data) : null));
-            } else if (de.data.removeDocument) {
+            }
+            let added = this.props.addDocument(de.data.droppedDocument, false);
+            if (added && de.data.removeDocument && !de.data.aliasOnDrop) {
                 de.data.removeDocument(this.props.CollectionView);
             }
-            this.props.addDocument(de.data.droppedDocument, false);
             e.stopPropagation();
+            return added;
         }
+        return false;
     }
 
     protected getDocumentFromType(type: string, path: string, options: DocumentOptions): Opt<Document> {
diff --git a/src/client/views/collections/MarqueeView.tsx b/src/client/views/collections/MarqueeView.tsx
index 8c2f3443c..b48ad9c64 100644
--- a/src/client/views/collections/MarqueeView.tsx
+++ b/src/client/views/collections/MarqueeView.tsx
@@ -17,7 +17,7 @@ interface MarqueeViewProps {
     getMarqueeTransform: () => Transform;
     getTransform: () => Transform;
     container: CollectionFreeFormView;
-    addDocument: (doc: Document, allowDuplicates: false) => void;
+    addDocument: (doc: Document, allowDuplicates: false) => boolean;
     activeDocuments: () => Document[];
     selectDocuments: (docs: Document[]) => void;
     removeDocument: (doc: Document) => boolean;
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index fec451b09..383341e02 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -23,7 +23,7 @@ import React = require("react");
 export interface DocumentViewProps {
     ContainingCollectionView: Opt<CollectionView>;
     Document: Document;
-    AddDocument?: (doc: Document, allowDuplicates: boolean) => void;
+    AddDocument?: (doc: Document, allowDuplicates: boolean) => boolean;
     RemoveDocument?: (doc: Document) => boolean;
     ScreenToLocalTransform: () => Transform;
     isTopMost: boolean;
-- 
cgit v1.2.3-70-g09d2


From 2264fb874a09ee01540141986325c76650f32c21 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Mon, 25 Mar 2019 23:20:22 -0400
Subject: added 1-level navigation to region annotations

---
 .../views/collections/CollectionDockingView.tsx    |   2 +-
 .../views/collections/CollectionVideoView.tsx      | 108 ++++++++++++---------
 src/client/views/collections/CollectionView.tsx    |   6 +-
 src/client/views/nodes/LinkBox.tsx                 |  21 +++-
 src/client/views/nodes/PDFBox.tsx                  |  21 ++--
 src/client/views/nodes/VideoBox.tsx                |  29 ++++--
 src/fields/KeyStore.ts                             |   2 +
 7 files changed, 120 insertions(+), 69 deletions(-)

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

diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 39b284d8e..d4f510f5d 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -191,7 +191,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
     @action
     onPointerDown = (e: React.PointerEvent): void => {
         var className = (e.target as any).className;
-        if ((className == "lm_title" || className == "lm_tab lm_active") && e.ctrlKey) {
+        if ((className == "lm_title" || className == "lm_tab lm_active") && (e.ctrlKey || e.altKey)) {
             e.stopPropagation();
             e.preventDefault();
             let docid = (e.target as any).DashDocId;
diff --git a/src/client/views/collections/CollectionVideoView.tsx b/src/client/views/collections/CollectionVideoView.tsx
index a3921696f..470a853e3 100644
--- a/src/client/views/collections/CollectionVideoView.tsx
+++ b/src/client/views/collections/CollectionVideoView.tsx
@@ -1,4 +1,4 @@
-import { action, computed, observable } from "mobx";
+import { action, computed, observable, trace } from "mobx";
 import { observer } from "mobx-react";
 import { Document } from "../../../fields/Document";
 import { KeyStore } from "../../../fields/KeyStore";
@@ -12,24 +12,26 @@ import "./CollectionVideoView.scss"
 
 @observer
 export class CollectionVideoView extends React.Component<CollectionViewProps> {
+    private _intervalTimer: any = undefined;
+    private _player: HTMLVideoElement | undefined = undefined;
+
+    @observable _currentTimecode: number = 0;
+    @observable _isPlaying: boolean = false;
 
     public static LayoutString(fieldKey: string = "DataKey") {
         return `<${CollectionVideoView.name} Document={Document}
                     ScreenToLocalTransform={ScreenToLocalTransform} fieldKey={${fieldKey}} panelWidth={PanelWidth} panelHeight={PanelHeight} isSelected={isSelected} select={select} bindings={bindings}
                     isTopMost={isTopMost} SelectOnLoad={selectOnLoad} BackgroundView={BackgroundView} focus={focus}/>`;
     }
-
-    private _mainCont = React.createRef<HTMLDivElement>();
-
     private get uIButtons() {
         let scaling = Math.min(1.8, this.props.ScreenToLocalTransform().transformDirection(1, 1)[0]);
         return ([
             <div className="collectionVideoView-time" key="time" onPointerDown={this.onResetDown} style={{ transform: `scale(${scaling}, ${scaling})` }}>
-                <span>{"" + Math.round(this.ctime)}</span>
-                <span style={{ fontSize: 8 }}>{" " + Math.round((this.ctime - Math.trunc(this.ctime)) * 100)}</span>
+                <span>{"" + Math.round(this._currentTimecode)}</span>
+                <span style={{ fontSize: 8 }}>{" " + Math.round((this._currentTimecode - Math.trunc(this._currentTimecode)) * 100)}</span>
             </div>,
             <div className="collectionVideoView-play" key="play" onPointerDown={this.onPlayDown} style={{ transform: `scale(${scaling}, ${scaling})` }}>
-                {this.playing ? "\"" : ">"}
+                {this._isPlaying ? "\"" : ">"}
             </div>,
             <div className="collectionVideoView-full" key="full" onPointerDown={this.onFullDown} style={{ transform: `scale(${scaling}, ${scaling})` }}>
                 F
@@ -37,64 +39,54 @@ export class CollectionVideoView extends React.Component<CollectionViewProps> {
         ]);
     }
 
-
-    // "inherited" CollectionView API starts here...
-
-    @observable
-    public SelectedDocs: FieldId[] = []
-    public active: () => boolean = () => CollectionView.Active(this);
-
-    addDocument = (doc: Document, allowDuplicates: boolean): boolean => { return CollectionView.AddDocument(this.props, doc, allowDuplicates); }
-    removeDocument = (doc: Document): boolean => { return CollectionView.RemoveDocument(this.props, doc); }
-
-    specificContextMenu = (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: "VideoOptions", event: () => { } });
+    @action
+    mainCont = (ele: HTMLDivElement | null) => {
+        if (ele) {
+            this._player = ele!.getElementsByTagName("video")[0];
+            if (this.props.Document.GetNumber(KeyStore.CurPage, -1) >= 0) {
+                this._currentTimecode = this.props.Document.GetNumber(KeyStore.CurPage, -1);
+            }
         }
     }
 
-    get collectionViewType(): CollectionViewType { return CollectionViewType.Freeform; }
-    get subView(): any { return CollectionView.SubView(this); }
-
     componentDidMount() {
-        this.updateTimecode();
+        this._intervalTimer = setInterval(this.updateTimecode, 1000);
     }
 
-    get player(): HTMLVideoElement | undefined {
-        return this._mainCont.current ? this._mainCont.current.getElementsByTagName("video")[0] : undefined;
+    componentWillUnmount() {
+        clearInterval(this._intervalTimer);
     }
 
     @action
     updateTimecode = () => {
-        if (this.player) {
-            this.ctime = this.player.currentTime;
-            this.props.Document.SetNumber(KeyStore.CurPage, Math.round(this.ctime));
+        if (this._player) {
+            if ((this._player as any).AHackBecauseSomethingResetsTheVideoToZero != -1) {
+                this._player.currentTime = (this._player as any).AHackBecauseSomethingResetsTheVideoToZero;
+                (this._player as any).AHackBecauseSomethingResetsTheVideoToZero = -1;
+            } else {
+                this._currentTimecode = this._player.currentTime;
+                this.props.Document.SetNumber(KeyStore.CurPage, Math.round(this._currentTimecode));
+            }
         }
-        setTimeout(() => this.updateTimecode(), 100)
     }
 
-
-    @observable
-    ctime: number = 0
-    @observable
-    playing: boolean = false;
-
     @action
     onPlayDown = () => {
-        if (this.player) {
-            if (this.player.paused) {
-                this.player.play();
-                this.playing = true;
+        if (this._player) {
+            if (this._player.paused) {
+                this._player.play();
+                this._isPlaying = true;
             } else {
-                this.player.pause();
-                this.playing = false;
+                this._player.pause();
+                this._isPlaying = false;
             }
         }
     }
+
     @action
     onFullDown = (e: React.PointerEvent) => {
-        if (this.player) {
-            this.player.requestFullscreen();
+        if (this._player) {
+            this._player.requestFullscreen();
             e.stopPropagation();
             e.preventDefault();
         }
@@ -102,15 +94,35 @@ export class CollectionVideoView extends React.Component<CollectionViewProps> {
 
     @action
     onResetDown = () => {
-        if (this.player) {
-            this.player.pause();
-            this.player.currentTime = 0;
+        if (this._player) {
+            this._player.pause();
+            this._player.currentTime = 0;
         }
 
     }
 
+    // "inherited" CollectionView API starts here...
+
+    @observable
+    public SelectedDocs: FieldId[] = []
+    public active: () => boolean = () => CollectionView.Active(this);
+
+    addDocument = (doc: Document, allowDuplicates: boolean): boolean => { return CollectionView.AddDocument(this.props, doc, allowDuplicates); }
+    removeDocument = (doc: Document): boolean => { return CollectionView.RemoveDocument(this.props, doc); }
+
+    specificContextMenu = (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: "VideoOptions", event: () => { } });
+        }
+    }
+
+    get collectionViewType(): CollectionViewType { return CollectionViewType.Freeform; }
+    get subView(): any { return CollectionView.SubView(this); }
+
+
     render() {
-        return (<div className="collectionVideoView-cont" ref={this._mainCont} onContextMenu={this.specificContextMenu}>
+        trace();
+        return (<div className="collectionVideoView-cont" ref={this.mainCont} onContextMenu={this.specificContextMenu}>
             {this.subView}
             {this.props.isSelected() ? this.uIButtons : (null)}
         </div>)
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index a740865ad..2fa2c9086 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -68,7 +68,11 @@ export class CollectionView extends React.Component<CollectionViewProps> {
 
     @action
     public static AddDocument(props: CollectionViewProps, doc: Document, allowDuplicates: boolean): boolean {
-        doc.SetNumber(KeyStore.Page, props.Document.GetNumber(KeyStore.CurPage, -1));
+        var curPage = props.Document.GetNumber(KeyStore.CurPage, -1);
+        doc.SetNumber(KeyStore.Page, curPage);
+        if (curPage > 0) {
+            doc.Set(KeyStore.AnnotationOn, props.Document);
+        }
         if (props.Document.Get(props.fieldKey) instanceof Field) {
             //TODO This won't create the field if it doesn't already exist
             const value = props.Document.GetData(props.fieldKey, ListField, new Array<Document>())
diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx
index dd2f71b59..638d3b5a7 100644
--- a/src/client/views/nodes/LinkBox.tsx
+++ b/src/client/views/nodes/LinkBox.tsx
@@ -17,6 +17,8 @@ import { faEye } from '@fortawesome/free-solid-svg-icons';
 import { faEdit } from '@fortawesome/free-solid-svg-icons';
 import { faTimes } from '@fortawesome/free-solid-svg-icons';
 import { undoBatch } from "../../util/UndoManager";
+import { FieldWaiting } from "../../../fields/Field";
+import { NumberField } from "../../../fields/NumberField";
 
 
 library.add(faEye);
@@ -41,7 +43,24 @@ export class LinkBox extends React.Component<Props> {
         if (docView) {
             docView.props.focus(this.props.pairedDoc);
         } else {
-            CollectionDockingView.Instance.AddRightSplit(this.props.pairedDoc)
+            this.props.pairedDoc.GetAsync(KeyStore.AnnotationOn, (contextDoc: any) => {
+                if (!contextDoc) {
+                    CollectionDockingView.Instance.AddRightSplit(this.props.pairedDoc);
+                } else if (contextDoc instanceof Document) {
+                    this.props.pairedDoc.GetTAsync(KeyStore.Page, NumberField).then((pfield: any) => {
+                        contextDoc.GetTAsync(KeyStore.CurPage, NumberField).then((cfield: any) => {
+                            if (pfield != cfield)
+                                contextDoc.SetNumber(KeyStore.CurPage, pfield.Data);
+                            let contextView = DocumentManager.Instance.getDocumentView(contextDoc);
+                            if (contextView) {
+                                contextView.props.focus(contextDoc);
+                            } else {
+                                CollectionDockingView.Instance.AddRightSplit(contextDoc);
+                            }
+                        })
+                    });
+                }
+            });
         }
     }
 
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 3a0ef2d32..e273b0b4f 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -1,5 +1,5 @@
 import * as htmlToImage from "html-to-image";
-import { action, computed, observable, reaction, IReactionDisposer, trace } from 'mobx';
+import { action, computed, observable, reaction, IReactionDisposer, trace, keys } from 'mobx';
 import { observer } from "mobx-react";
 import 'react-image-lightbox/style.css';
 import Measure from "react-measure";
@@ -18,6 +18,7 @@ import "./PDFBox.scss";
 import { Sticky } from './Sticky'; //you should look at sticky and annotation, because they are used here
 import React = require("react")
 import { RouteStore } from "../../../server/RouteStore";
+import { NumberField } from "../../../fields/NumberField";
 
 /** ALSO LOOK AT: Annotation.tsx, Sticky.tsx
  * This method renders PDF and puts all kinds of functionalities such as annotation, highlighting, 
@@ -60,7 +61,6 @@ export class PDFBox extends React.Component<FieldViewProps> {
     //very useful for keeping track of X and y position throughout the PDF Canvas
     private initX: number = 0;
     private initY: number = 0;
-    private initPage: boolean = false;
 
     //checks if tool is on
     private _toolOn: boolean = false; //checks if tool is on
@@ -88,17 +88,15 @@ export class PDFBox extends React.Component<FieldViewProps> {
     @observable private _loaded: boolean = false;
 
     @computed private get curPage() { return this.props.doc.GetNumber(KeyStore.CurPage, -1); }
+    @computed private get thumbnailPage() { return this.props.doc.GetNumber(KeyStore.ThumbnailPage, -1); }
 
     componentDidMount() {
         this._reactionDisposer = reaction(
-            () => this.curPage,
+            () => [this.curPage, this.thumbnailPage],
             () => {
-                if (this.curPage && this.initPage) {
+                if (this.curPage > 0 && this.thumbnailPage > 0 && this.curPage != this.thumbnailPage) {
                     this.saveThumbnail();
                     this._interactive = true;
-                } else {
-                    if (this.curPage > 0)
-                        this.initPage = true;
                 }
             },
             { fireImmediately: true });
@@ -384,6 +382,7 @@ export class PDFBox extends React.Component<FieldViewProps> {
                 { width: me.props.doc.GetNumber(KeyStore.NativeWidth, 0), height: me.props.doc.GetNumber(KeyStore.NativeHeight, 0), quality: 0.5 })
                 .then(function (dataUrl: string) {
                     me.props.doc.SetData(KeyStore.Thumbnail, new URL(dataUrl), ImageField);
+                    me.props.doc.SetNumber(KeyStore.ThumbnailPage, me.props.doc.GetNumber(KeyStore.CurPage, -1));
                 })
                 .catch(function (error: any) {
                     console.error('oops, something went wrong!', error);
@@ -473,10 +472,10 @@ export class PDFBox extends React.Component<FieldViewProps> {
 
     @computed
     get imageProxyRenderer() {
-        let field = this.props.doc.Get(KeyStore.Thumbnail);
-        if (field) {
-            let path = field == FieldWaiting ? "https://image.flaticon.com/icons/svg/66/66163.svg" :
-                field instanceof ImageField ? field.Data.href : "http://cs.brown.edu/people/bcz/prairie.jpg";
+        let thumbField = this.props.doc.Get(KeyStore.Thumbnail);
+        if (thumbField) {
+            let path = thumbField == FieldWaiting || this.thumbnailPage != this.curPage ? "https://image.flaticon.com/icons/svg/66/66163.svg" :
+                thumbField instanceof ImageField ? thumbField.Data.href : "http://cs.brown.edu/people/bcz/prairie.jpg";
             return <img src={path} width="100%" />;
         }
         return (null);
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 09ae95183..7c0db83a8 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -1,23 +1,27 @@
 import React = require("react")
 import { observer } from "mobx-react";
-import { FieldWaiting } from '../../../fields/Field';
+import { FieldWaiting, Opt } from '../../../fields/Field';
 import { VideoField } from '../../../fields/VideoField';
 import { FieldView, FieldViewProps } from './FieldView';
 import "./VideoBox.scss";
 import Measure from "react-measure";
-import { action, trace, observable } from "mobx";
+import { action, trace, observable, IReactionDisposer, computed, reaction } from "mobx";
 import { KeyStore } from "../../../fields/KeyStore";
 import { number } from "prop-types";
 
 @observer
 export class VideoBox extends React.Component<FieldViewProps> {
 
+    private _reactionDisposer: Opt<IReactionDisposer>;
+    private _videoRef = React.createRef<HTMLVideoElement>()
     public static LayoutString() { return FieldView.LayoutString(VideoBox) }
 
     constructor(props: FieldViewProps) {
         super(props);
     }
 
+    @computed private get curPage() { return this.props.doc.GetNumber(KeyStore.CurPage, -1); }
+
 
     _loaded: boolean = false;
 
@@ -39,7 +43,17 @@ export class VideoBox extends React.Component<FieldViewProps> {
         }
     }
 
+    get player(): HTMLVideoElement | undefined {
+        return this._videoRef.current ? this._videoRef.current.getElementsByTagName("video")[0] : undefined;
+    }
 
+    @action
+    setVideoRef = (vref: HTMLVideoElement | null) => {
+        if (this.curPage >= 0 && vref) {
+            vref!.currentTime = this.curPage;
+            (vref! as any).AHackBecauseSomethingResetsTheVideoToZero = this.curPage;
+        }
+    }
 
     render() {
         let field = this.props.doc.GetT(this.props.fieldKey, VideoField);
@@ -47,15 +61,16 @@ export class VideoBox extends React.Component<FieldViewProps> {
             return <div>Loading</div>
         }
         let path = field.Data.href;
-
-        //setTimeout(action(() => this._loaded = true), 500);
+        trace();
         return (
             <Measure onResize={this.setScaling}>
                 {({ measureRef }) =>
-                    <video className="videobox-cont" onClick={() => { }} ref={measureRef}>
-                        <source src={path} type="video/mp4" />
-                        Not supported.
+                    <div style={{ width: "100%", height: "auto" }} ref={measureRef}>
+                        <video className="videobox-cont" onClick={() => { }} ref={this.setVideoRef}>
+                            <source src={path} type="video/mp4" />
+                            Not supported.
                         </video>
+                    </div>
                 }
             </Measure>
         )
diff --git a/src/fields/KeyStore.ts b/src/fields/KeyStore.ts
index 611e2951b..f9684b212 100644
--- a/src/fields/KeyStore.ts
+++ b/src/fields/KeyStore.ts
@@ -36,7 +36,9 @@ export namespace KeyStore {
     export const LinkDescription = new Key("LinkDescription");
     export const LinkTags = new Key("LinkTag");
     export const Thumbnail = new Key("Thumbnail");
+    export const ThumbnailPage = new Key("ThumbnailPage");
     export const CurPage = new Key("CurPage");
+    export const AnnotationOn = new Key("AnnotationOn");
     export const NumPages = new Key("NumPages");
     export const Ink = new Key("Ink");
     export const Cursors = new Key("Cursors");
-- 
cgit v1.2.3-70-g09d2

