From ad87140f4a52984d754c06fcb55e66a0cc935ce2 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Thu, 4 Jul 2019 23:18:13 -0400
Subject: fixed shift-dragging of documents

---
 src/client/views/nodes/VideoBox.tsx | 1 -
 1 file changed, 1 deletion(-)

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

diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 1239b498f..264d3c1f7 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -119,7 +119,6 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
         });
     }
     onPointerDown = (e: React.PointerEvent) => {
-        e.preventDefault();
         e.stopPropagation();
     }
 
-- 
cgit v1.2.3-70-g09d2


From 36992ca2388e2a6919ecf0b78f4731bc9aea8f86 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Thu, 11 Jul 2019 16:05:37 -0400
Subject: added video controls

---
 src/client/views/nodes/VideoBox.scss |  6 +++++-
 src/client/views/nodes/VideoBox.tsx  | 29 ++++++++++++++++++++++++-----
 2 files changed, 29 insertions(+), 6 deletions(-)

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

diff --git a/src/client/views/nodes/VideoBox.scss b/src/client/views/nodes/VideoBox.scss
index 35db64cf4..55f2fe7c5 100644
--- a/src/client/views/nodes/VideoBox.scss
+++ b/src/client/views/nodes/VideoBox.scss
@@ -1,8 +1,12 @@
-.videoBox-cont, .videoBox-cont-fullScreen{
+.videoBox-cont, .videoBox-cont-interactive, .videoBox-cont-fullScreen {
     width: 100%; 
     height: Auto; 
 }
 
+.videoBox-cont-interactive {
+    pointer-events: all;
+}
+
 .videoBox-cont-fullScreen {
     pointer-events: all;
 }
\ No newline at end of file
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 264d3c1f7..a5e145856 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -12,6 +12,9 @@ import { positionSchema } from "./DocumentView";
 import { FieldView, FieldViewProps } from './FieldView';
 import { pageSchema } from "./ImageBox";
 import "./VideoBox.scss";
+import { ContextMenu } from "../ContextMenu";
+import { ContextMenuProps } from "../ContextMenuItem";
+import { InkingControl } from "../InkingControl";
 
 type VideoDocument = makeInterface<[typeof positionSchema, typeof pageSchema]>;
 const VideoDocument = makeInterface(positionSchema, pageSchema);
@@ -119,7 +122,17 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
         });
     }
     onPointerDown = (e: React.PointerEvent) => {
-        e.stopPropagation();
+    }
+
+    @observable static _showControls: boolean = false;
+
+    specificContextMenu = (e: React.MouseEvent): void => {
+        let field = Cast(this.Document[this.props.fieldKey], VideoField);
+        if (field) {
+            let subitems: ContextMenuProps[] = [];
+            subitems.push({ description: "Toggle Show Controls", event: action(() => VideoBox._showControls = !VideoBox._showControls), icon: "expand-arrows-alt" });
+            ContextMenu.Instance.addItem({ description: "Video Funcs...", subitems: subitems });
+        }
     }
 
     render() {
@@ -132,11 +145,17 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
         // });
         // //
 
-        let style = "videoBox-cont" + (this._fullScreen ? "-fullScreen" : "");
+        let interactive = InkingControl.Instance.selectedTool ? "" : "-interactive";
+        let style = "videoBox-cont" + (this._fullScreen ? "-fullScreen" : interactive);
         return !field ? <div>Loading</div> :
-            <video className={`${style}`} ref={this.setVideoRef} onCanPlay={this.videoLoad} onPointerDown={this.onPointerDown}>
-                <source src={field.url.href} type="video/mp4" />
-                Not supported.
+            VideoBox._showControls ?
+                <video className={`${style}`} ref={this.setVideoRef} onCanPlay={this.videoLoad} onPointerDown={this.onPointerDown} onContextMenu={this.specificContextMenu} controls>
+                    <source src={field.url.href} type="video/mp4" />
+                    Not supported.
+            </video> :
+                <video className={`${style}`} ref={this.setVideoRef} onCanPlay={this.videoLoad} onPointerDown={this.onPointerDown} onContextMenu={this.specificContextMenu}>
+                    <source src={field.url.href} type="video/mp4" />
+                    Not supported.
             </video>;
     }
 }
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2


From aeb0521e8ad8f1efc8c3a39af9eb5443c5368453 Mon Sep 17 00:00:00 2001
From: Tyler Schicke <tyler_schicke@brown.edu>
Date: Thu, 11 Jul 2019 16:47:22 -0400
Subject: Removed rendering of collapsed tabs

---
 .../views/collections/CollectionDockingView.tsx    | 26 +++++++++++++++++++++-
 src/client/views/nodes/VideoBox.tsx                | 11 +++------
 2 files changed, 28 insertions(+), 9 deletions(-)

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

diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 57964442d..abc9bbeba 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -26,7 +26,7 @@ import React = require("react");
 import { MainView } from '../MainView';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { library } from '@fortawesome/fontawesome-svg-core';
-import { faFile } from '@fortawesome/free-solid-svg-icons';
+import { faFile, faUnlockAlt } from '@fortawesome/free-solid-svg-icons';
 import { CurrentUserUtils } from '../../../server/authentication/models/current_user_utils';
 library.add(faFile);
 
@@ -471,6 +471,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
 interface DockedFrameProps {
     documentId: FieldId;
     dataDocumentId: FieldId;
+    glContainer: any;
     //collectionDockingView: CollectionDockingView
 }
 @observer
@@ -480,6 +481,9 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
     @observable private _panelHeight = 0;
     @observable private _document: Opt<Doc>;
     @observable private _dataDoc: Opt<Doc>;
+
+    @observable private _isActive: boolean = false;
+
     get _stack(): any {
         let parent = (this.props as any).glContainer.parent.parent;
         if (this._document && this._document.excludeFromLibrary && parent.parent && parent.parent.contentItems.length > 1) {
@@ -497,6 +501,25 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
         }));
     }
 
+    componentDidMount() {
+        this.props.glContainer.layoutManager.on("activeContentItemChanged", this.onActiveContentItemChanged);
+        this.props.glContainer.on("tab", this.onActiveContentItemChanged);
+        this.onActiveContentItemChanged();
+    }
+
+    componentWillUnmount() {
+        this.props.glContainer.layoutManager.off("activeContentItemChanged", this.onActiveContentItemChanged);
+        this.props.glContainer.off("tab", this.onActiveContentItemChanged);
+    }
+
+    @action.bound
+    private onActiveContentItemChanged() {
+        if (this.props.glContainer.tab) {
+            this._isActive = this.props.glContainer.tab.isActive;
+        }
+    }
+
+
     nativeWidth = () => NumCast(this._document!.nativeWidth, this._panelWidth);
     nativeHeight = () => {
         let nh = NumCast(this._document!.nativeHeight, this._panelHeight);
@@ -571,6 +594,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
     }
 
     render() {
+        if (!this._isActive) return null;
         let theContent = this.content;
         return !this._document ? (null) :
             <Measure offset onResize={action((r: any) => { this._panelWidth = r.offset.width; this._panelHeight = r.offset.height; })}>
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index a5e145856..c65dfe0bd 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -148,14 +148,9 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
         let interactive = InkingControl.Instance.selectedTool ? "" : "-interactive";
         let style = "videoBox-cont" + (this._fullScreen ? "-fullScreen" : interactive);
         return !field ? <div>Loading</div> :
-            VideoBox._showControls ?
-                <video className={`${style}`} ref={this.setVideoRef} onCanPlay={this.videoLoad} onPointerDown={this.onPointerDown} onContextMenu={this.specificContextMenu} controls>
-                    <source src={field.url.href} type="video/mp4" />
-                    Not supported.
-            </video> :
-                <video className={`${style}`} ref={this.setVideoRef} onCanPlay={this.videoLoad} onPointerDown={this.onPointerDown} onContextMenu={this.specificContextMenu}>
-                    <source src={field.url.href} type="video/mp4" />
-                    Not supported.
+            <video className={`${style}`} ref={this.setVideoRef} onCanPlay={this.videoLoad} onPointerDown={this.onPointerDown} onContextMenu={this.specificContextMenu} controls={VideoBox._showControls}>
+                <source src={field.url.href} type="video/mp4" />
+                Not supported.
             </video>;
     }
 }
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2


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

---
 package.json                        |  4 +++-
 src/client/documents/Documents.ts   |  2 +-
 src/client/views/nodes/VideoBox.tsx | 27 ++++++++++++++++++++++-----
 src/client/views/nodes/WebBox.tsx   | 35 ++++++++++++++++++++++++++++++++++-
 4 files changed, 60 insertions(+), 8 deletions(-)

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

diff --git a/package.json b/package.json
index 85fc7f1be..6681c4c1c 100644
--- a/package.json
+++ b/package.json
@@ -103,6 +103,7 @@
         "@types/typescript": "^2.0.0",
         "@types/uuid": "^3.4.4",
         "@types/webpack": "^4.4.25",
+        "@types/youtube": "0.0.38",
         "async": "^2.6.2",
         "babel-runtime": "^6.26.0",
         "bcrypt-nodejs": "0.0.3",
@@ -195,6 +196,7 @@
         "typescript-collections": "^1.3.2",
         "url-loader": "^1.1.2",
         "uuid": "^3.3.2",
-        "xoauth2": "^1.2.0"
+        "xoauth2": "^1.2.0",
+        "youtube": "^0.1.0"
     }
 }
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 30e4637b0..2a3827782 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -118,7 +118,7 @@ export namespace Docs {
                 options: { nativeWidth: 600, curPage: 0 }
             }],
             [DocumentType.WEB, {
-                layout: { view: WebBox },
+                layout: { view: WebBox, collectionView: [CollectionView, data, anno] as CollectionViewType },
                 options: { height: 300 }
             }],
             [DocumentType.COL, {
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index c65dfe0bd..895d9a422 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -70,6 +70,18 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
 
     componentDidMount() {
         if (this.props.setVideoBox) this.props.setVideoBox(this);
+
+        let field = Cast(this.Document[this.props.fieldKey], VideoField);
+        if (field && field.url.href.indexOf("youtube") !== -1) {
+            let youtubeaspect = 400 / 315;
+            var nativeWidth = FieldValue(this.Document.nativeWidth, 0);
+            var nativeHeight = FieldValue(this.Document.nativeHeight, 0);
+            if (!nativeWidth || !nativeHeight || Math.abs(nativeWidth / nativeHeight - youtubeaspect) > 0.05) {
+                if (!this.Document.nativeWidth) this.Document.nativeWidth = 600;
+                this.Document.nativeHeight = this.Document.nativeWidth / youtubeaspect;
+                this.Document.height = FieldValue(this.Document.width, 0) / youtubeaspect;
+            }
+        }
     }
     componentWillUnmount() {
         this.Pause();
@@ -145,12 +157,17 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
         // });
         // //
 
-        let interactive = InkingControl.Instance.selectedTool ? "" : "-interactive";
+        let interactive = InkingControl.Instance.selectedTool || !this.props.isSelected() ? "" : "-interactive";
         let style = "videoBox-cont" + (this._fullScreen ? "-fullScreen" : interactive);
+        let videoid = field ? ((arr: string[]) => arr[arr.length - 1])(field.url.href.split("/")) : "";
         return !field ? <div>Loading</div> :
-            <video className={`${style}`} ref={this.setVideoRef} onCanPlay={this.videoLoad} onPointerDown={this.onPointerDown} onContextMenu={this.specificContextMenu} controls={VideoBox._showControls}>
-                <source src={field.url.href} type="video/mp4" />
-                Not supported.
-            </video>;
+            field.url.href.indexOf("youtube") !== -1 ?
+                <embed className={`${style}`} onLoad={this.videoLoad} id="video" onPointerDown={this.onPointerDown} onContextMenu={this.specificContextMenu}
+                    src={`https://www.youtube.com/v/${videoid}`} type="application/x-shockwave-flash" style={{ height: "100%" }} title="Adobe Flash Player">
+                </embed> :
+                <video className={`${style}`} ref={this.setVideoRef} onCanPlay={this.videoLoad} onPointerDown={this.onPointerDown} onContextMenu={this.specificContextMenu} controls={VideoBox._showControls}>
+                    <source src={field.url.href} type="video/mp4" />
+                    Not supported.
+                </video>;
     }
 }
\ No newline at end of file
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 98c57fc75..96b972a1c 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -6,12 +6,45 @@ import { InkingControl } from "../InkingControl";
 import { FieldView, FieldViewProps } from './FieldView';
 import "./WebBox.scss";
 import React = require("react");
+import { InkTool } from "../../../new_fields/InkField";
+import { Cast, FieldValue, NumCast } from "../../../new_fields/Types";
 
+export function onYouTubeIframeAPIReady() {
+    console.log("player");
+    return;
+    let player = new YT.Player('player', {
+        events: {
+            'onReady': onPlayerReady
+        }
+    });
+}
+// must cast as any to set property on window
+const _global = (window /* browser */ || global /* node */) as any
+_global.onYouTubeIframeAPIReady = onYouTubeIframeAPIReady;
+
+function onPlayerReady(event: any) {
+    event.target.playVideo();
+}
 @observer
 export class WebBox extends React.Component<FieldViewProps> {
 
     public static LayoutString() { return FieldView.LayoutString(WebBox); }
 
+    componentWillMount() {
+
+        let field = Cast(this.props.Document[this.props.fieldKey], WebField);
+        if (field && field.url.href.indexOf("youtube") !== -1) {
+            let youtubeaspect = 400 / 315;
+            var nativeWidth = NumCast(this.props.Document.nativeWidth, 0);
+            var nativeHeight = NumCast(this.props.Document.nativeHeight, 0);
+            if (!nativeWidth || !nativeHeight || Math.abs(nativeWidth / nativeHeight - youtubeaspect) > 0.05) {
+                if (!nativeWidth) this.props.Document.nativeWidth = 600;
+                this.props.Document.nativeHeight = NumCast(this.props.Document.nativeWidth) / youtubeaspect;
+                this.props.Document.height = NumCast(this.props.Document.width) / youtubeaspect;
+            }
+        }
+    }
+
     _ignore = 0;
     onPreWheel = (e: React.WheelEvent) => {
         this._ignore = e.timeStamp;
@@ -46,7 +79,7 @@ export class WebBox extends React.Component<FieldViewProps> {
 
         let frozen = !this.props.isSelected() || DocumentDecorations.Instance.Interacting;
 
-        let classname = "webBox-cont" + (this.props.isSelected() && !InkingControl.Instance.selectedTool && !DocumentDecorations.Instance.Interacting ? "-interactive" : "");
+        let classname = "webBox-cont" + (this.props.isSelected() && InkingControl.Instance.selectedTool === InkTool.None && !DocumentDecorations.Instance.Interacting ? "-interactive" : "");
         return (
             <>
                 <div className={classname}  >
-- 
cgit v1.2.3-70-g09d2


From 6a477918f2f16bdc023c76d6a145bb6435e918a6 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Fri, 12 Jul 2019 16:06:15 -0400
Subject: fixed annotations on video timecodes.  added start of youtube
 support.

---
 src/client/views/InkingCanvas.tsx                  |  7 +-
 src/client/views/MainView.tsx                      |  5 ++
 src/client/views/collections/CollectionSubView.tsx |  4 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  2 +-
 src/client/views/nodes/VideoBox.tsx                | 98 +++++++++++-----------
 5 files changed, 61 insertions(+), 55 deletions(-)

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

diff --git a/src/client/views/InkingCanvas.tsx b/src/client/views/InkingCanvas.tsx
index 2c54054a5..37a6bbab7 100644
--- a/src/client/views/InkingCanvas.tsx
+++ b/src/client/views/InkingCanvas.tsx
@@ -13,6 +13,7 @@ import { Cast, PromiseValue, NumCast } from "../../new_fields/Types";
 
 interface InkCanvasProps {
     getScreenTransform: () => Transform;
+    AnnotationDocument: Doc;
     Document: Doc;
     inkFieldKey: string;
     children: () => JSX.Element[];
@@ -41,7 +42,7 @@ export class InkingCanvas extends React.Component<InkCanvasProps> {
     }
 
     componentDidMount() {
-        PromiseValue(Cast(this.props.Document[this.props.inkFieldKey], InkField)).then(ink => runInAction(() => {
+        PromiseValue(Cast(this.props.AnnotationDocument[this.props.inkFieldKey], InkField)).then(ink => runInAction(() => {
             if (ink) {
                 let bounds = Array.from(ink.inkData).reduce(([mix, max, miy, may], [id, strokeData]) =>
                     strokeData.pathData.reduce(([mix, max, miy, may], p) =>
@@ -56,12 +57,12 @@ export class InkingCanvas extends React.Component<InkCanvasProps> {
 
     @computed
     get inkData(): Map<string, StrokeData> {
-        let map = Cast(this.props.Document[this.props.inkFieldKey], InkField);
+        let map = Cast(this.props.AnnotationDocument[this.props.inkFieldKey], InkField);
         return !map ? new Map : new Map(map.inkData);
     }
 
     set inkData(value: Map<string, StrokeData>) {
-        this.props.Document[this.props.inkFieldKey] = new InkField(value);
+        this.props.AnnotationDocument[this.props.inkFieldKey] = new InkField(value);
     }
 
     @action
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index ec5ec6e8b..f0e11a480 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -64,6 +64,11 @@ export class MainView extends React.Component {
     }
 
     componentWillMount() {
+        var tag = document.createElement('script');
+
+        tag.src = "https://www.youtube.com/iframe_api";
+        var firstScriptTag = document.getElementsByTagName('script')[0];
+        firstScriptTag.parentNode!.insertBefore(tag, firstScriptTag);
         window.removeEventListener("keydown", KeyManager.Instance.handle);
         window.addEventListener("keydown", KeyManager.Instance.handle);
 
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 7ac8aee4c..b2c3fb7d0 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -179,8 +179,8 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
                 }
             }
             if (text && text.indexOf("www.youtube.com/watch") !== -1) {
-                const url = text.replace("youtube.com/watch?v=", "youtube.com/embed/");
-                this.props.addDocument(Docs.Create.WebDocument(url, { ...options, width: 300, height: 300 }));
+                const url = text.replace("youtube.com/watch?v=", "youtube.com/embed/");// + "?enablejsapi=1";
+                this.props.addDocument(Docs.Create.VideoDocument(url, { ...options, width: 400, height: 315 }));
                 return;
             }
 
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index f4e5c4384..e35546fec 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -524,7 +524,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
                     <CollectionFreeFormViewPannableContents centeringShiftX={this.centeringShiftX} centeringShiftY={this.centeringShiftY}
                         easing={easing} zoomScaling={this.zoomScaling} panX={this.panX} panY={this.panY}>
                         <CollectionFreeFormLinksView {...this.props} key="freeformLinks">
-                            <InkingCanvas getScreenTransform={this.getTransform} Document={this.fieldExtensionDoc} inkFieldKey={"ink"} >
+                            <InkingCanvas getScreenTransform={this.getTransform} Document={this.props.Document} AnnotationDocument={this.fieldExtensionDoc} inkFieldKey={"ink"} >
                                 {this.childViews}
                             </InkingCanvas>
                         </CollectionFreeFormLinksView>
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 895d9a422..9ee4d7942 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -3,7 +3,7 @@ import { action, IReactionDisposer, observable, reaction } from "mobx";
 import { observer } from "mobx-react";
 import * as rp from "request-promise";
 import { makeInterface } from "../../../new_fields/Schema";
-import { Cast, FieldValue } from "../../../new_fields/Types";
+import { Cast, FieldValue, NumCast } from "../../../new_fields/Types";
 import { VideoField } from "../../../new_fields/URLField";
 import { RouteStore } from "../../../server/RouteStore";
 import { DocServer } from "../../DocServer";
@@ -15,6 +15,7 @@ import "./VideoBox.scss";
 import { ContextMenu } from "../ContextMenu";
 import { ContextMenuProps } from "../ContextMenuItem";
 import { InkingControl } from "../InkingControl";
+import * as $ from "jquery";
 
 type VideoDocument = makeInterface<[typeof positionSchema, typeof pageSchema]>;
 const VideoDocument = makeInterface(positionSchema, pageSchema);
@@ -22,6 +23,7 @@ const VideoDocument = makeInterface(positionSchema, pageSchema);
 @observer
 export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoDocument) {
     private _reactionDisposer?: IReactionDisposer;
+    private _youtubePlayer: any = undefined;
     private _videoRef: HTMLVideoElement | null = null;
     @observable _playTimer?: NodeJS.Timeout = undefined;
     @observable _fullScreen = false;
@@ -67,12 +69,16 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
     updateTimecode = () => {
         this.player && (this.props.Document.curPage = this.player.currentTime);
     }
-
+    @action
+    updateYoutubeTimecode = () => {
+        this._youtubePlayer && (this.props.Document.curPage = this._youtubePlayer.getCurrentTime());
+    }
     componentDidMount() {
         if (this.props.setVideoBox) this.props.setVideoBox(this);
 
         let field = Cast(this.Document[this.props.fieldKey], VideoField);
-        if (field && field.url.href.indexOf("youtube") !== -1) {
+        let videoid = field && field.url.href.indexOf("youtube") !== -1 ? ((arr: string[]) => arr[arr.length - 1])(field.url.href.split("/")) : "";
+        if (videoid) {
             let youtubeaspect = 400 / 315;
             var nativeWidth = FieldValue(this.Document.nativeWidth, 0);
             var nativeHeight = FieldValue(this.Document.nativeHeight, 0);
@@ -81,7 +87,42 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
                 this.Document.nativeHeight = this.Document.nativeWidth / youtubeaspect;
                 this.Document.height = FieldValue(this.Document.width, 0) / youtubeaspect;
             }
+            this._youtubePlayer = new YT.Player(`${videoid}-player`, {
+                height: `${NumCast(this.props.Document.height)}`,
+                width: `${NumCast(this.props.Document.width)}`,
+                videoId: videoid.toString(),
+                playerVars: { 'controls': 0 },
+                events: {
+                    //'onReady': this.onPlayerReady,
+                }
+            });
+            // let iframe = $(document.getElementById(`${videoid}-player`)!);
+            // iframe.on("load", function () {
+            //     iframe.contents().find("head")
+            //         .append($("<style type='text/css'>  .ytp-pause-overlay, .ytp-scroll-min { opacity : 0 !important; }  </style>"));
+            // });
+            reaction(() => this.props.isSelected(), (sel) => {
+                if (sel) {
+                    this._youtubePlayer.playVideo();
+                    if (!this._playTimer) this._playTimer = setInterval(this.updateYoutubeTimecode, 1000);
+                } else {
+                    let iframe = $(document.getElementById(`${videoid}-player`)!);
+                    //  .ytp-pause-overlay, .ytp-scroll-min { opacity : 0 !important; }
+                    //     $('iframe').load( function() {
+                    //     $('iframe').contents().find("head")
+                    //       .append($("<style type='text/css'>  .my-class{display:none;}  </style>"));
+                    // });
+                    //this._youtubePlayer.cueVideoById(videoid, this._youtubePlayer.getCurrentTime());
+                    this._youtubePlayer.pauseVideo();
+                    if (this._playTimer) {
+                        clearInterval(this._playTimer);
+                        this._playTimer = undefined;
+                    }
+                }
+            });
         }
+
+
     }
     componentWillUnmount() {
         this.Pause();
@@ -100,39 +141,7 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
         }
     }
 
-    getMp4ForVideo(videoId: string = "JN5beCVArMs") {
-        return new Promise(async (resolve, reject) => {
-            const videoInfoRequestConfig = {
-                headers: {
-                    connection: 'keep-alive',
-                    "user-agent": 'Mozilla/5.0 (Windows NT 10.0; WOW64; rv:43.0) Gecko/20100101 Firefox/46.0',
-                },
-            };
-            try {
-                let responseSchema: any = {};
-                const videoInfoResponse = await rp.get(DocServer.prepend(RouteStore.corsProxy + "/" + `https://www.youtube.com/watch?v=${videoId}`), videoInfoRequestConfig);
-                const dataHtml = videoInfoResponse;
-                const start = dataHtml.indexOf('ytplayer.config = ') + 18;
-                const end = dataHtml.indexOf(';ytplayer.load');
-                const subString = dataHtml.substring(start, end);
-                const subJson = JSON.parse(subString);
-                const stringSub = subJson.args.player_response;
-                const stringSubJson = JSON.parse(stringSub);
-                const adaptiveFormats = stringSubJson.streamingData.adaptiveFormats;
-                const videoDetails = stringSubJson.videoDetails;
-                responseSchema.adaptiveFormats = adaptiveFormats;
-                responseSchema.videoDetails = videoDetails;
-                resolve(responseSchema);
-            }
-            catch (err) {
-                console.log(`
-                --- Youtube ---
-                Function: getMp4ForVideo
-                Error: `, err);
-                reject(err);
-            }
-        });
-    }
+
     onPointerDown = (e: React.PointerEvent) => {
     }
 
@@ -149,22 +158,13 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
 
     render() {
         let field = Cast(this.Document[this.props.fieldKey], VideoField);
-
-        // this.getMp4ForVideo().then((mp4) => {
-        //     console.log(mp4);
-        // }).catch(e => {
-        //     console.log("")
-        // });
-        // //
-
         let interactive = InkingControl.Instance.selectedTool || !this.props.isSelected() ? "" : "-interactive";
         let style = "videoBox-cont" + (this._fullScreen ? "-fullScreen" : interactive);
-        let videoid = field ? ((arr: string[]) => arr[arr.length - 1])(field.url.href.split("/")) : "";
+        let videoid = field && field.url.href.indexOf("youtube") !== -1 ? ((arr: string[]) => arr[arr.length - 1])(field.url.href.split("/")) : "";
+
         return !field ? <div>Loading</div> :
-            field.url.href.indexOf("youtube") !== -1 ?
-                <embed className={`${style}`} onLoad={this.videoLoad} id="video" onPointerDown={this.onPointerDown} onContextMenu={this.specificContextMenu}
-                    src={`https://www.youtube.com/v/${videoid}`} type="application/x-shockwave-flash" style={{ height: "100%" }} title="Adobe Flash Player">
-                </embed> :
+            videoid ?
+                <div id={`${videoid}-player`} className={`${style}`} style={{ height: "100%" }} /> :
                 <video className={`${style}`} ref={this.setVideoRef} onCanPlay={this.videoLoad} onPointerDown={this.onPointerDown} onContextMenu={this.specificContextMenu} controls={VideoBox._showControls}>
                     <source src={field.url.href} type="video/mp4" />
                     Not supported.
-- 
cgit v1.2.3-70-g09d2


From 0c4fe96495ea73c16dcf189b09dbceb44943b8ba Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Fri, 12 Jul 2019 18:06:34 -0400
Subject: enabled youtube play controls.  fixed youtube video playing .

---
 src/client/views/collections/CollectionSubView.tsx |  4 +-
 .../views/collections/CollectionVideoView.tsx      |  2 +-
 src/client/views/nodes/VideoBox.tsx                | 65 +++++++++++-----------
 3 files changed, 34 insertions(+), 37 deletions(-)

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

diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index b2c3fb7d0..71f1908f0 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -197,7 +197,7 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
                         .then(result => {
                             let type = result["content-type"];
                             if (type) {
-                                Docs.Get.DocumentFromType(type, str, { ...options, width: 300, nativeWidth: 300 })
+                                Docs.Get.DocumentFromType(type, str, { ...options, width: 300, nativeWidth: type.indexOf("video") !== -1 ? 600 : 300 })
                                     .then(doc => doc && this.props.addDocument(doc, false));
                             }
                         });
@@ -218,7 +218,7 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
                         body: formData
                     }).then(async (res: Response) => {
                         (await res.json()).map(action((file: any) => {
-                            let full = { ...options, nativeWidth: 300, width: 300, title: dropFileName };
+                            let full = { ...options, nativeWidth: type.indexOf("video") !== -1 ? 600 : 300, width: 300, title: dropFileName };
                             let path = DocServer.prepend(file);
                             Docs.Get.DocumentFromType(type, path, full).then(doc => doc && this.props.addDocument(doc));
                         }));
diff --git a/src/client/views/collections/CollectionVideoView.tsx b/src/client/views/collections/CollectionVideoView.tsx
index 2b6c272aa..8d519ff2b 100644
--- a/src/client/views/collections/CollectionVideoView.tsx
+++ b/src/client/views/collections/CollectionVideoView.tsx
@@ -43,7 +43,7 @@ export class CollectionVideoView extends React.Component<FieldViewProps> {
 
     @action
     onPlayDown = () => {
-        if (this._videoBox && this._videoBox.player) {
+        if (this._videoBox) {
             if (this._videoBox.Playing) {
                 this._videoBox.Pause();
             } else {
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 9ee4d7942..1b9138cfd 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -47,16 +47,31 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
 
     @action public Play() {
         this.Playing = true;
-        if (this.player) this.player.play();
-        if (!this._playTimer) this._playTimer = setInterval(this.updateTimecode, 500);
+        if (this.player) {
+            this.player.play();
+            if (!this._playTimer) this._playTimer = setInterval(this.updateTimecode, 500);
+        } else if (this._youtubePlayer) {
+            this._youtubePlayer.playVideo();
+            if (!this._playTimer) this._playTimer = setInterval(this.updateYoutubeTimecode, 1000);
+        }
     }
 
     @action public Pause() {
         this.Playing = false;
-        if (this.player) this.player.pause();
-        if (this._playTimer) {
-            clearInterval(this._playTimer);
-            this._playTimer = undefined;
+        if (this.player) {
+            this.player.pause();
+            if (this._playTimer) {
+                clearInterval(this._playTimer);
+                this._playTimer = undefined;
+            }
+        } else if (this._youtubePlayer) {
+            // let interactive = InkingControl.Instance.selectedTool || !this.props.isSelected() ? "" : "-interactive";
+            // this._youtubePlayer.getIframe().style.pointerEvents = interactive ? "all" : "none";
+            this._youtubePlayer.pauseVideo();
+            if (this._playTimer) {
+                clearInterval(this._playTimer);
+                this._playTimer = undefined;
+            }
         }
     }
 
@@ -91,39 +106,24 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
                 height: `${NumCast(this.props.Document.height)}`,
                 width: `${NumCast(this.props.Document.width)}`,
                 videoId: videoid.toString(),
-                playerVars: { 'controls': 0 },
+                playerVars: { 'controls': VideoBox._showControls ? 1 : 0 },
                 events: {
-                    //'onReady': this.onPlayerReady,
+                    'onStateChange': this.onPlayerStateChange,
                 }
             });
             // let iframe = $(document.getElementById(`${videoid}-player`)!);
             // iframe.on("load", function () {
             //     iframe.contents().find("head")
             //         .append($("<style type='text/css'>  .ytp-pause-overlay, .ytp-scroll-min { opacity : 0 !important; }  </style>"));
-            // });
-            reaction(() => this.props.isSelected(), (sel) => {
-                if (sel) {
-                    this._youtubePlayer.playVideo();
-                    if (!this._playTimer) this._playTimer = setInterval(this.updateYoutubeTimecode, 1000);
-                } else {
-                    let iframe = $(document.getElementById(`${videoid}-player`)!);
-                    //  .ytp-pause-overlay, .ytp-scroll-min { opacity : 0 !important; }
-                    //     $('iframe').load( function() {
-                    //     $('iframe').contents().find("head")
-                    //       .append($("<style type='text/css'>  .my-class{display:none;}  </style>"));
-                    // });
-                    //this._youtubePlayer.cueVideoById(videoid, this._youtubePlayer.getCurrentTime());
-                    this._youtubePlayer.pauseVideo();
-                    if (this._playTimer) {
-                        clearInterval(this._playTimer);
-                        this._playTimer = undefined;
-                    }
-                }
-            });
+            // })
         }
+    }
 
-
+    @action
+    onPlayerStateChange = (event: any) => {
+        this.Playing = event.data == YT.PlayerState.PLAYING;
     }
+
     componentWillUnmount() {
         this.Pause();
         if (this._reactionDisposer) this._reactionDisposer();
@@ -141,10 +141,6 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
         }
     }
 
-
-    onPointerDown = (e: React.PointerEvent) => {
-    }
-
     @observable static _showControls: boolean = false;
 
     specificContextMenu = (e: React.MouseEvent): void => {
@@ -162,10 +158,11 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
         let style = "videoBox-cont" + (this._fullScreen ? "-fullScreen" : interactive);
         let videoid = field && field.url.href.indexOf("youtube") !== -1 ? ((arr: string[]) => arr[arr.length - 1])(field.url.href.split("/")) : "";
 
+        if (this._youtubePlayer) this._youtubePlayer.getIframe().style.pointerEvents = interactive ? "all" : "none";
         return !field ? <div>Loading</div> :
             videoid ?
                 <div id={`${videoid}-player`} className={`${style}`} style={{ height: "100%" }} /> :
-                <video className={`${style}`} ref={this.setVideoRef} onCanPlay={this.videoLoad} onPointerDown={this.onPointerDown} onContextMenu={this.specificContextMenu} controls={VideoBox._showControls}>
+                <video className={`${style}`} ref={this.setVideoRef} onCanPlay={this.videoLoad} onContextMenu={this.specificContextMenu} controls={VideoBox._showControls}>
                     <source src={field.url.href} type="video/mp4" />
                     Not supported.
                 </video>;
-- 
cgit v1.2.3-70-g09d2


From d75d951a863103568f93ab9b04259d07b9668b47 Mon Sep 17 00:00:00 2001
From: Tyler Schicke <tyler_schicke@brown.edu>
Date: Fri, 12 Jul 2019 19:25:20 -0400
Subject: Various small changes/fixes

---
 src/client/views/MainView.tsx                        | 8 +++++---
 src/client/views/collections/CollectionVideoView.tsx | 2 +-
 src/client/views/nodes/VideoBox.tsx                  | 6 ++++--
 src/client/views/search/SearchItem.tsx               | 2 +-
 4 files changed, 11 insertions(+), 7 deletions(-)

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

diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index f0e11a480..614b9cce7 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -1,5 +1,5 @@
 import { IconName, library } from '@fortawesome/fontawesome-svg-core';
-import { faArrowDown, faArrowUp, faClone, faCheck, faCommentAlt, faCut, faExclamation, faFilePdf, faFilm, faFont, faGlobeAsia, faPortrait, faMusic, faObjectGroup, faPenNib, faRedoAlt, faTable, faThumbtack, faTree, faUndoAlt } from '@fortawesome/free-solid-svg-icons';
+import { faArrowDown, faArrowUp, faClone, faCheck, faCommentAlt, faCut, faExclamation, faFilePdf, faFilm, faFont, faGlobeAsia, faPortrait, faMusic, faObjectGroup, faPenNib, faRedoAlt, faTable, faThumbtack, faTree, faUndoAlt, faCat } from '@fortawesome/free-solid-svg-icons';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { action, computed, configure, observable, runInAction, reaction, trace } from 'mobx';
 import { observer } from 'mobx-react';
@@ -38,6 +38,7 @@ import { PresentationView } from './presentationview/PresentationView';
 import { PreviewCursor } from './PreviewCursor';
 import { FilterBox } from './search/FilterBox';
 import { CollectionTreeView } from './collections/CollectionTreeView';
+import { ClientUtils } from '../util/ClientUtils';
 
 @observer
 export class MainView extends React.Component {
@@ -111,6 +112,7 @@ export class MainView extends React.Component {
         library.add(faFont);
         library.add(faExclamation);
         library.add(faPortrait);
+        library.add(faCat);
         library.add(faFilePdf);
         library.add(faObjectGroup);
         library.add(faTable);
@@ -378,11 +380,11 @@ export class MainView extends React.Component {
         let addImportCollectionNode = action(() => Docs.Create.DirectoryImportDocument({ title: "Directory Import", width: 400, height: 400 }));
 
         let btns: [React.RefObject<HTMLDivElement>, IconName, string, () => Doc][] = [
-            [React.createRef<HTMLDivElement>(), "portrait", "Add Cat Image", addImageNode],
             [React.createRef<HTMLDivElement>(), "object-group", "Add Collection", addColNode],
             // [React.createRef<HTMLDivElement>(), "clone", "Add Docking Frame", addDockingNode],
             [React.createRef<HTMLDivElement>(), "arrow-up", "Import Directory", addImportCollectionNode],
         ];
+        if (!ClientUtils.RELEASE) btns.unshift([React.createRef<HTMLDivElement>(), "cat", "Add Cat Image", addImageNode]);
 
         return < div id="add-nodes-menu" style={{ left: this.flyoutWidth + 5 }} >
             <input type="checkbox" id="add-menu-toggle" ref={this.addMenuToggle} />
@@ -399,7 +401,7 @@ export class MainView extends React.Component {
                                 <FontAwesomeIcon icon={btn[1]} size="sm" />
                             </button>
                         </div></li>)}
-                    <li key="undoTest"><button className="add-button round-button" onClick={() => UndoManager.PrintBatches()}><FontAwesomeIcon icon="exclamation" size="sm" /></button></li>
+                    <li key="undoTest"><button className="add-button round-button" title="Click if undo isn't working" onClick={() => UndoManager.TraceOpenBatches()}><FontAwesomeIcon icon="exclamation" size="sm" /></button></li>
                     <li key="color"><button className="add-button round-button" title="Select Color" onClick={() => this.toggleColorPicker()}><div className="toolbar-color-button" style={{ backgroundColor: InkingControl.Instance.selectedColor }} >
                         <div className="toolbar-color-picker" onClick={this.onColorClick} style={this._colorPickerDisplay ? { color: "black", display: "block" } : { color: "black", display: "none" }}>
                             <SketchPicker color={InkingControl.Instance.selectedColor} onChange={InkingControl.Instance.switchColor} />
diff --git a/src/client/views/collections/CollectionVideoView.tsx b/src/client/views/collections/CollectionVideoView.tsx
index 2b6c272aa..39fe3edd1 100644
--- a/src/client/views/collections/CollectionVideoView.tsx
+++ b/src/client/views/collections/CollectionVideoView.tsx
@@ -27,7 +27,7 @@ export class CollectionVideoView extends React.Component<FieldViewProps> {
     private get uIButtons() {
         let scaling = Math.min(1.8, this.props.ScreenToLocalTransform().Scale);
         let curTime = NumCast(this.props.Document.curPage);
-        return ([
+        return (VideoBox._showControls ? [] : [
             <div className="collectionVideoView-time" key="time" onPointerDown={this.onResetDown} style={{ transform: `scale(${scaling}, ${scaling})` }}>
                 <span>{"" + Math.round(curTime)}</span>
                 <span style={{ fontSize: 8 }}>{" " + Math.round((curTime - Math.trunc(curTime)) * 100)}</span>
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 9ee4d7942..fa1e4d273 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -145,7 +145,7 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
     onPointerDown = (e: React.PointerEvent) => {
     }
 
-    @observable static _showControls: boolean = false;
+    @observable static _showControls: boolean;
 
     specificContextMenu = (e: React.MouseEvent): void => {
         let field = Cast(this.Document[this.props.fieldKey], VideoField);
@@ -170,4 +170,6 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
                     Not supported.
                 </video>;
     }
-}
\ No newline at end of file
+}
+
+VideoBox._showControls = true;
\ No newline at end of file
diff --git a/src/client/views/search/SearchItem.tsx b/src/client/views/search/SearchItem.tsx
index f6f5b19f7..cd7e31b20 100644
--- a/src/client/views/search/SearchItem.tsx
+++ b/src/client/views/search/SearchItem.tsx
@@ -241,7 +241,7 @@ export class SearchItem extends React.Component<SearchItemProps> {
                     onClick={this.onClick} onPointerDown={this.pointerDown} >
                     <div className="main-search-info">
                         <div title="Drag as document" onPointerDown={this.onPointerDown}> <FontAwesomeIcon icon="file" size="lg" /> </div>
-                        <div className="search-title" id="result" >{this.props.doc.title}</div>
+                        <div className="search-title" id="result" >{StrCast(this.props.doc.title)}</div>
                         <div className="search-info" style={{ width: this._useIcons ? "15%" : "400px" }}>
                             <div className={`icon-${this._useIcons ? "icons" : "live"}`}>
                                 <div className="search-type" >{this.DocumentIcon}</div>
-- 
cgit v1.2.3-70-g09d2


From 4db507d08249ccddf664798ab59c3b729c3d1065 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Fri, 12 Jul 2019 22:19:03 -0400
Subject: fixed a few youtube video things.  changed title of library doc.

---
 .../views/collections/CollectionDockingView.tsx    |  55 ++++++----
 .../views/collections/CollectionStackingView.tsx   |   2 +-
 src/client/views/nodes/VideoBox.scss               |  15 ++-
 src/client/views/nodes/VideoBox.tsx                | 116 +++++++++++++--------
 .../authentication/models/current_user_utils.ts    |   3 +-
 5 files changed, 116 insertions(+), 75 deletions(-)

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

diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index e5cee188f..34b8eb8e1 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -1,6 +1,6 @@
 import 'golden-layout/src/css/goldenlayout-base.css';
 import 'golden-layout/src/css/goldenlayout-dark-theme.css';
-import { action, Lambda, observable, reaction } from "mobx";
+import { action, Lambda, observable, reaction, trace, computed } from "mobx";
 import { observer } from "mobx-react";
 import * as ReactDOM from 'react-dom';
 import Measure from "react-measure";
@@ -412,7 +412,9 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
                     }
                     SelectionManager.DeselectAll();
                 }
+                CollectionDockingView.Instance._ignoreStateChange = JSON.stringify(CollectionDockingView.Instance._goldenLayout.toConfig());
                 tab.contentItem.remove();
+                CollectionDockingView.Instance._ignoreStateChange = JSON.stringify(CollectionDockingView.Instance._goldenLayout.toConfig());
             });
     }
 
@@ -569,33 +571,40 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
             CollectionDockingView.Instance.AddTab(this._stack, doc, dataDoc);
         }
     }
-    get content() {
+    @computed get docView() {
         if (!this._document) {
             return (null);
         }
         let resolvedDataDoc = this._document.layout instanceof Doc ? this._document : this._dataDoc;
+        return <DocumentView key={this._document[Id]}
+            Document={this._document}
+            DataDoc={resolvedDataDoc}
+            bringToFront={emptyFunction}
+            addDocument={undefined}
+            removeDocument={undefined}
+            ContentScaling={this.contentScaling}
+            PanelWidth={this.nativeWidth}
+            PanelHeight={this.nativeHeight}
+            ScreenToLocalTransform={this.ScreenToLocalTransform}
+            renderDepth={0}
+            selectOnLoad={false}
+            parentActive={returnTrue}
+            whenActiveChanged={emptyFunction}
+            focus={emptyFunction}
+            addDocTab={this.addDocTab}
+            ContainingCollectionView={undefined}
+            zoomToScale={emptyFunction}
+            getScale={returnOne} />
+    }
+
+    @computed get content() {
+        if (!this._document) {
+            return (null);
+        }
         return (
             <div className="collectionDockingView-content" ref={this._mainCont}
                 style={{ transform: `translate(${this.previewPanelCenteringOffset}px, 0px) scale(${this.scaleToFitMultiplier})` }}>
-                <DocumentView key={this._document[Id]}
-                    Document={this._document}
-                    DataDoc={resolvedDataDoc}
-                    bringToFront={emptyFunction}
-                    addDocument={undefined}
-                    removeDocument={undefined}
-                    ContentScaling={this.contentScaling}
-                    PanelWidth={this.nativeWidth}
-                    PanelHeight={this.nativeHeight}
-                    ScreenToLocalTransform={this.ScreenToLocalTransform}
-                    renderDepth={0}
-                    selectOnLoad={false}
-                    parentActive={returnTrue}
-                    whenActiveChanged={emptyFunction}
-                    focus={emptyFunction}
-                    addDocTab={this.addDocTab}
-                    ContainingCollectionView={undefined}
-                    zoomToScale={emptyFunction}
-                    getScale={returnOne} />
+                {this.docView}
             </div >);
     }
 
@@ -604,7 +613,9 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
         let theContent = this.content;
         return !this._document ? (null) :
             <Measure offset onResize={action((r: any) => { this._panelWidth = r.offset.width; this._panelHeight = r.offset.height; })}>
-                {({ measureRef }) => <div ref={measureRef}>  {theContent} </div>}
+                {({ measureRef }) => <div ref={measureRef}>
+                    {theContent}
+                </div>}
             </Measure>;
     }
 }
\ No newline at end of file
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index a84fd9cfe..fe01103d6 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -50,7 +50,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
     }
 
     overlays = (doc: Doc) => {
-        return doc.type === DocumentType.IMG ? { title: "title", caption: "caption" } : {};
+        return doc.type === DocumentType.IMG || doc.type === DocumentType.VID ? { title: "title", caption: "caption" } : {};
     }
 
     getDisplayDoc(layoutDoc: Doc, d: Doc, dxf: () => Transform) {
diff --git a/src/client/views/nodes/VideoBox.scss b/src/client/views/nodes/VideoBox.scss
index 55f2fe7c5..d651a8621 100644
--- a/src/client/views/nodes/VideoBox.scss
+++ b/src/client/views/nodes/VideoBox.scss
@@ -1,12 +1,17 @@
-.videoBox-cont, .videoBox-cont-interactive, .videoBox-cont-fullScreen {
-    width: 100%; 
+.videoBox-content-YouTube, .videoBox-content-YouTube-fullScreen,
+.videoBox-content, .videoBox-content-interactive, .videoBox-cont-fullScreen {
+    width: 100%;
+}
+
+.videoBox-content, .videoBox-content-interactive, .videoBox-content-fullScreen {
     height: Auto; 
 }
 
-.videoBox-cont-interactive {
-    pointer-events: all;
+.videoBox-content-YouTube, .videoBox-content-YouTube-fullScreen  {
+    height: 100%;
 }
 
-.videoBox-cont-fullScreen {
+.videoBox-content-interactive, .videoBox-content-fullScreen,
+.videoBox-content-YouTube-fullScreen  {
     pointer-events: all;
 }
\ No newline at end of file
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 1b9138cfd..c9179db47 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -1,21 +1,18 @@
 import React = require("react");
-import { action, IReactionDisposer, observable, reaction } from "mobx";
+import { action, IReactionDisposer, observable, reaction, trace, computed } from "mobx";
 import { observer } from "mobx-react";
-import * as rp from "request-promise";
 import { makeInterface } from "../../../new_fields/Schema";
 import { Cast, FieldValue, NumCast } from "../../../new_fields/Types";
 import { VideoField } from "../../../new_fields/URLField";
-import { RouteStore } from "../../../server/RouteStore";
-import { DocServer } from "../../DocServer";
+import { ContextMenu } from "../ContextMenu";
+import { ContextMenuProps } from "../ContextMenuItem";
 import { DocComponent } from "../DocComponent";
+import { InkingControl } from "../InkingControl";
 import { positionSchema } from "./DocumentView";
 import { FieldView, FieldViewProps } from './FieldView';
 import { pageSchema } from "./ImageBox";
 import "./VideoBox.scss";
-import { ContextMenu } from "../ContextMenu";
-import { ContextMenuProps } from "../ContextMenuItem";
-import { InkingControl } from "../InkingControl";
-import * as $ from "jquery";
+import { InkTool } from "../../../new_fields/InkField";
 
 type VideoDocument = makeInterface<[typeof positionSchema, typeof pageSchema]>;
 const VideoDocument = makeInterface(positionSchema, pageSchema);
@@ -23,6 +20,7 @@ const VideoDocument = makeInterface(positionSchema, pageSchema);
 @observer
 export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoDocument) {
     private _reactionDisposer?: IReactionDisposer;
+    private _youtubeReactionDisposer?: IReactionDisposer;
     private _youtubePlayer: any = undefined;
     private _videoRef: HTMLVideoElement | null = null;
     @observable _playTimer?: NodeJS.Timeout = undefined;
@@ -47,45 +45,37 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
 
     @action public Play() {
         this.Playing = true;
-        if (this.player) {
-            this.player.play();
-            if (!this._playTimer) this._playTimer = setInterval(this.updateTimecode, 500);
-        } else if (this._youtubePlayer) {
-            this._youtubePlayer.playVideo();
-            if (!this._playTimer) this._playTimer = setInterval(this.updateYoutubeTimecode, 1000);
+        this.player && this.player.play();
+        this._youtubePlayer && this._youtubePlayer.playVideo();
+        !this._playTimer && (this._playTimer = setInterval(this.updateTimecode, 500));
+        this._youtubeSeekTo = false;
+    }
+
+    @action public Seek(time: number) {
+        if (this._youtubePlayer && !this.Playing) {
+            this._youtubeSeekTo = true;
+            this._youtubePlayer.seekTo(time);
         }
     }
 
     @action public Pause() {
         this.Playing = false;
-        if (this.player) {
-            this.player.pause();
-            if (this._playTimer) {
-                clearInterval(this._playTimer);
-                this._playTimer = undefined;
-            }
-        } else if (this._youtubePlayer) {
-            // let interactive = InkingControl.Instance.selectedTool || !this.props.isSelected() ? "" : "-interactive";
-            // this._youtubePlayer.getIframe().style.pointerEvents = interactive ? "all" : "none";
-            this._youtubePlayer.pauseVideo();
-            if (this._playTimer) {
-                clearInterval(this._playTimer);
-                this._playTimer = undefined;
-            }
-        }
+        this.player && this.player.pause();
+        this._youtubePlayer && this._youtubePlayer.pauseVideo();
+        this._playTimer && clearInterval(this._playTimer);
+        this._playTimer = undefined;
+        this._youtubeSeekTo = false;
     }
 
     @action public FullScreen() {
         this._fullScreen = true;
         this.player && this.player.requestFullscreen();
+        this._youtubePlayer && this.props.addDocTab(this.props.Document, this.props.DataDoc, "inTab");
     }
 
     @action
     updateTimecode = () => {
         this.player && (this.props.Document.curPage = this.player.currentTime);
-    }
-    @action
-    updateYoutubeTimecode = () => {
         this._youtubePlayer && (this.props.Document.curPage = this._youtubePlayer.getCurrentTime());
     }
     componentDidMount() {
@@ -108,9 +98,15 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
                 videoId: videoid.toString(),
                 playerVars: { 'controls': VideoBox._showControls ? 1 : 0 },
                 events: {
-                    'onStateChange': this.onPlayerStateChange,
+                    'onStateChange': this.onYoutubePlayerStateChange,
+                    'onReady': this.onYoutubePlayerReady,
                 }
             });
+            this._reactionDisposer = reaction(() => this.props.Document.curPage, () => this.Seek(this.Document.curPage || 0), { fireImmediately: true });
+            this._youtubeReactionDisposer = reaction(() => [this.props.isSelected(), InkingControl.Instance.selectedTool], () => {
+                let interactive = InkingControl.Instance.selectedTool === InkTool.None && this.props.isSelected();
+                this._youtubePlayer.getIframe().style.pointerEvents = interactive ? "all" : "none";
+            }, { fireImmediately: true })
             // let iframe = $(document.getElementById(`${videoid}-player`)!);
             // iframe.on("load", function () {
             //     iframe.contents().find("head")
@@ -120,13 +116,30 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
     }
 
     @action
-    onPlayerStateChange = (event: any) => {
+    onYoutubePlayerStateChange = (event: any) => {
+        console.log("event.data = " + event.data);
         this.Playing = event.data == YT.PlayerState.PLAYING;
+        if (this._youtubeSeekTo && this.Playing) {
+            this._youtubePlayer.pauseVideo();
+            this._youtubeSeekTo = false;
+        } else this.Playing && !this._playTimer && (this._playTimer = setInterval(this.updateTimecode, 500));
+        event.data === YT.PlayerState.PAUSED && this._playTimer && clearInterval(this._playTimer);
+    }
+    _youtubeSeekTo = false;
+    @action
+    onYoutubePlayerReady = (event: any) => {
+        this.Playing = false;
+        this._youtubePlayer && (this._youtubePlayer.getIframe().style.pointerEvents = "none");
+        if (this.Document.curPage) {
+            this.Seek(this.Document.curPage);
+            this._youtubeSeekTo = true;
+        }
     }
 
     componentWillUnmount() {
         this.Pause();
-        if (this._reactionDisposer) this._reactionDisposer();
+        this._reactionDisposer && this._reactionDisposer();
+        this._youtubeReactionDisposer && this._youtubeReactionDisposer();
     }
 
     @action
@@ -143,6 +156,11 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
 
     @observable static _showControls: boolean = false;
 
+    @computed get youtubeVideoId() {
+        let field = Cast(this.Document[this.props.fieldKey], VideoField);
+        return field && field.url.href.indexOf("youtube") !== -1 ? ((arr: string[]) => arr[arr.length - 1])(field.url.href.split("/")) : "";
+    }
+
     specificContextMenu = (e: React.MouseEvent): void => {
         let field = Cast(this.Document[this.props.fieldKey], VideoField);
         if (field) {
@@ -152,19 +170,25 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
         }
     }
 
-    render() {
+    @computed get content() {
         let field = Cast(this.Document[this.props.fieldKey], VideoField);
         let interactive = InkingControl.Instance.selectedTool || !this.props.isSelected() ? "" : "-interactive";
-        let style = "videoBox-cont" + (this._fullScreen ? "-fullScreen" : interactive);
-        let videoid = field && field.url.href.indexOf("youtube") !== -1 ? ((arr: string[]) => arr[arr.length - 1])(field.url.href.split("/")) : "";
-
-        if (this._youtubePlayer) this._youtubePlayer.getIframe().style.pointerEvents = interactive ? "all" : "none";
+        let style = "videoBox-content" + (this._fullScreen ? "-fullScreen" : "") + interactive;
         return !field ? <div>Loading</div> :
-            videoid ?
-                <div id={`${videoid}-player`} className={`${style}`} style={{ height: "100%" }} /> :
-                <video className={`${style}`} ref={this.setVideoRef} onCanPlay={this.videoLoad} onContextMenu={this.specificContextMenu} controls={VideoBox._showControls}>
-                    <source src={field.url.href} type="video/mp4" />
-                    Not supported.
-                </video>;
+            <video className={`${style}`} ref={this.setVideoRef} onCanPlay={this.videoLoad} controls={VideoBox._showControls}>
+                <source src={field.url.href} type="video/mp4" />
+                Not supported.
+            </video>;
+    }
+
+    @computed get youtubeContent() {
+        let style = "videoBox-content-YouTube" + (this._fullScreen ? "-fullScreen" : "");
+        return <div id={`${this.youtubeVideoId}-player`} className={`${style}`} />;
+    }
+
+    render() {
+        return <div style={{ pointerEvents: "all", width: "100%", height: "100%" }} onContextMenu={this.specificContextMenu}>
+            {this.youtubeVideoId ? this.youtubeContent : this.content}
+        </div>;
     }
 }
\ No newline at end of file
diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts
index 39a973e08..384c579de 100644
--- a/src/server/authentication/models/current_user_utils.ts
+++ b/src/server/authentication/models/current_user_utils.ts
@@ -10,7 +10,7 @@ import { CollectionView } from "../../../client/views/collections/CollectionView
 import { Doc } from "../../../new_fields/Doc";
 import { List } from "../../../new_fields/List";
 import { listSpec } from "../../../new_fields/Schema";
-import { Cast, FieldValue } from "../../../new_fields/Types";
+import { Cast, FieldValue, StrCast } from "../../../new_fields/Types";
 import { RouteStore } from "../../RouteStore";
 
 export class CurrentUserUtils {
@@ -69,6 +69,7 @@ export class CurrentUserUtils {
             sidebar.boxShadow = "1 1 3";
             doc.sidebar = sidebar;
         }
+        StrCast(doc.title).indexOf("@") !== -1 && (doc.title = StrCast(doc.title).split("@")[0] + "'s Library");
 
     }
 
-- 
cgit v1.2.3-70-g09d2


From 5ee47fa3533c769364c91561a0be5ee388f855d3 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Fri, 12 Jul 2019 23:49:37 -0400
Subject: fixed resizing youtube videos.  changed dockingview to not call
 setupGoldenLayout on  dropping or closing a tab.

---
 src/client/views/collections/CollectionDockingView.tsx | 7 ++++++-
 src/client/views/nodes/VideoBox.tsx                    | 5 +++--
 2 files changed, 9 insertions(+), 3 deletions(-)

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

diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 34b8eb8e1..fe8288b28 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -298,7 +298,10 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
     onPointerUp = (e: React.PointerEvent): void => {
         if (this._flush) {
             this._flush = false;
-            setTimeout(() => this.stateChanged(), 10);
+            setTimeout(() => {
+                CollectionDockingView.Instance._ignoreStateChange = JSON.stringify(CollectionDockingView.Instance._goldenLayout.toConfig());
+                this.stateChanged()
+            }, 10);
         }
     }
     @action
@@ -342,6 +345,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
     }
 
     itemDropped = () => {
+        CollectionDockingView.Instance._ignoreStateChange = JSON.stringify(CollectionDockingView.Instance._goldenLayout.toConfig());
         this.stateChanged();
     }
 
@@ -357,6 +361,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
             if (tab.contentItem.config.fixed) {
                 tab.contentItem.parent.config.fixed = true;
             }
+
             let doc = await DocServer.GetRefField(tab.contentItem.config.props.documentId) as Doc;
             let dataDoc = await DocServer.GetRefField(tab.contentItem.config.props.dataDocumentId) as Doc;
             if (doc instanceof Doc) {
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 861e91e41..e86348241 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -13,6 +13,7 @@ import { FieldView, FieldViewProps } from './FieldView';
 import { pageSchema } from "./ImageBox";
 import "./VideoBox.scss";
 import { InkTool } from "../../../new_fields/InkField";
+import { DocumentDecorations } from "../DocumentDecorations";
 
 type VideoDocument = makeInterface<[typeof positionSchema, typeof pageSchema]>;
 const VideoDocument = makeInterface(positionSchema, pageSchema);
@@ -103,8 +104,8 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
                 }
             });
             this._reactionDisposer = reaction(() => this.props.Document.curPage, () => this.Seek(this.Document.curPage || 0), { fireImmediately: true });
-            this._youtubeReactionDisposer = reaction(() => [this.props.isSelected(), InkingControl.Instance.selectedTool], () => {
-                let interactive = InkingControl.Instance.selectedTool === InkTool.None && this.props.isSelected();
+            this._youtubeReactionDisposer = reaction(() => [this.props.isSelected(), DocumentDecorations.Instance.Interacting, InkingControl.Instance.selectedTool], () => {
+                let interactive = InkingControl.Instance.selectedTool === InkTool.None && this.props.isSelected() && !DocumentDecorations.Instance.Interacting;
                 this._youtubePlayer.getIframe().style.pointerEvents = interactive ? "all" : "none";
             }, { fireImmediately: true })
             // let iframe = $(document.getElementById(`${videoid}-player`)!);
-- 
cgit v1.2.3-70-g09d2


From 27fc2cce30d990502f7836054eb15e24f51e709c Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Mon, 15 Jul 2019 10:41:31 -0400
Subject: fixed annotation & time code updating.

---
 src/client/views/nodes/VideoBox.tsx | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

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

diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index e86348241..66844cdd6 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -44,12 +44,13 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
         }
     }
 
-    @action public Play() {
+    @action public Play = () => {
         this.Playing = true;
         this.player && this.player.play();
         this._youtubePlayer && this._youtubePlayer.playVideo();
         !this._playTimer && (this._playTimer = setInterval(this.updateTimecode, 500));
         this._youtubeSeekTo = false;
+        this.updateTimecode();
     }
 
     @action public Seek(time: number) {
@@ -59,7 +60,7 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
         }
     }
 
-    @action public Pause() {
+    @action public Pause = () => {
         this.Playing = false;
         this.player && this.player.pause();
         this._youtubePlayer && this._youtubePlayer.pauseVideo();
@@ -176,7 +177,7 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
         let interactive = InkingControl.Instance.selectedTool || !this.props.isSelected() ? "" : "-interactive";
         let style = "videoBox-content" + (this._fullScreen ? "-fullScreen" : "") + interactive;
         return !field ? <div>Loading</div> :
-            <video className={`${style}`} ref={this.setVideoRef} onCanPlay={this.videoLoad} controls={VideoBox._showControls}>
+            <video className={`${style}`} ref={this.setVideoRef} onCanPlay={this.videoLoad} controls={VideoBox._showControls} onPlay={this.Play} onSeeked={this.updateTimecode} onPause={this.Pause}>
                 <source src={field.url.href} type="video/mp4" />
                 Not supported.
             </video>;
-- 
cgit v1.2.3-70-g09d2


From 98bd031db9c6f6ce1eb9688b19e898c67baca724 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Mon, 15 Jul 2019 18:17:45 -0400
Subject: better but not working.

---
 src/client/views/nodes/VideoBox.tsx | 104 ++++++++++++++++++++----------------
 1 file changed, 58 insertions(+), 46 deletions(-)

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

diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 66844cdd6..552d3f188 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -1,5 +1,5 @@
 import React = require("react");
-import { action, IReactionDisposer, observable, reaction, trace, computed } from "mobx";
+import { action, IReactionDisposer, observable, reaction, trace, computed, runInAction } from "mobx";
 import { observer } from "mobx-react";
 import { makeInterface } from "../../../new_fields/Schema";
 import { Cast, FieldValue, NumCast } from "../../../new_fields/Types";
@@ -44,29 +44,25 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
         }
     }
 
-    @action public Play = () => {
+    @action public Play = (update: boolean = true) => {
         this.Playing = true;
-        this.player && this.player.play();
-        this._youtubePlayer && this._youtubePlayer.playVideo();
+        update && this.player && this.player.play();
+        update && this._youtubePlayer && this._youtubePlayer.playVideo();
         !this._playTimer && (this._playTimer = setInterval(this.updateTimecode, 500));
-        this._youtubeSeekTo = false;
         this.updateTimecode();
     }
 
     @action public Seek(time: number) {
-        if (this._youtubePlayer && !this.Playing) {
-            this._youtubeSeekTo = true;
-            this._youtubePlayer.seekTo(time);
-        }
+        this._youtubePlayer && !this.Playing && this._youtubePlayer.seekTo(time);
     }
 
-    @action public Pause = () => {
+    @action public Pause = (update: boolean = true) => {
         this.Playing = false;
-        this.player && this.player.pause();
-        this._youtubePlayer && this._youtubePlayer.pauseVideo();
+        update && this.player && this.player.pause();
+        update && this._youtubePlayer && this._youtubePlayer.pauseVideo();
         this._playTimer && clearInterval(this._playTimer);
         this._playTimer = undefined;
-        this._youtubeSeekTo = false;
+        this.updateTimecode();
     }
 
     @action public FullScreen() {
@@ -77,15 +73,19 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
 
     @action
     updateTimecode = () => {
+        let ctime = this._youtubePlayer.getCurrentTime();
+        let otime = this.lastyoutube ? (this.lastyoutube as any).getCurrentTime() : -1;
+        console.log("Setting " + ctime + " " + otime);
         this.player && (this.props.Document.curPage = this.player.currentTime);
         this._youtubePlayer && (this.props.Document.curPage = this._youtubePlayer.getCurrentTime());
     }
+    static _staticCounter: number = 0;
+    counter: number = -1;
+    _startupTime: number = -1;
     componentDidMount() {
         if (this.props.setVideoBox) this.props.setVideoBox(this);
 
-        let field = Cast(this.Document[this.props.fieldKey], VideoField);
-        let videoid = field && field.url.href.indexOf("youtube") !== -1 ? ((arr: string[]) => arr[arr.length - 1])(field.url.href.split("/")) : "";
-        if (videoid) {
+        if (this.youtubeVideoId) {
             let youtubeaspect = 400 / 315;
             var nativeWidth = FieldValue(this.Document.nativeWidth, 0);
             var nativeHeight = FieldValue(this.Document.nativeHeight, 0);
@@ -94,47 +94,28 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
                 this.Document.nativeHeight = this.Document.nativeWidth / youtubeaspect;
                 this.Document.height = FieldValue(this.Document.width, 0) / youtubeaspect;
             }
-            this._youtubePlayer = new YT.Player(`${videoid}-player`, {
-                height: `${NumCast(this.props.Document.height)}`,
-                width: `${NumCast(this.props.Document.width)}`,
-                videoId: videoid.toString(),
-                playerVars: { 'controls': VideoBox._showControls ? 1 : 0 },
-                events: {
-                    'onStateChange': this.onYoutubePlayerStateChange,
-                    'onReady': this.onYoutubePlayerReady,
-                }
-            });
             this._reactionDisposer = reaction(() => this.props.Document.curPage, () => this.Seek(this.Document.curPage || 0), { fireImmediately: true });
             this._youtubeReactionDisposer = reaction(() => [this.props.isSelected(), DocumentDecorations.Instance.Interacting, InkingControl.Instance.selectedTool], () => {
                 let interactive = InkingControl.Instance.selectedTool === InkTool.None && this.props.isSelected() && !DocumentDecorations.Instance.Interacting;
                 this._youtubePlayer.getIframe().style.pointerEvents = interactive ? "all" : "none";
             }, { fireImmediately: true })
-            // let iframe = $(document.getElementById(`${videoid}-player`)!);
-            // iframe.on("load", function () {
-            //     iframe.contents().find("head")
-            //         .append($("<style type='text/css'>  .ytp-pause-overlay, .ytp-scroll-min { opacity : 0 !important; }  </style>"));
-            // })
         }
     }
 
-    @action
-    onYoutubePlayerStateChange = (event: any) => {
-        console.log("event.data = " + event.data);
-        this.Playing = event.data == YT.PlayerState.PLAYING;
-        if (this._youtubeSeekTo && this.Playing) {
-            this._youtubePlayer.pauseVideo();
-            this._youtubeSeekTo = false;
-        } else this.Playing && !this._playTimer && (this._playTimer = setInterval(this.updateTimecode, 500));
-        event.data === YT.PlayerState.PAUSED && this._playTimer && clearInterval(this._playTimer);
-    }
-    _youtubeSeekTo = false;
     @action
     onYoutubePlayerReady = (event: any) => {
-        this.Playing = false;
         this._youtubePlayer && (this._youtubePlayer.getIframe().style.pointerEvents = "none");
         if (this.Document.curPage) {
-            this.Seek(this.Document.curPage);
-            this._youtubeSeekTo = true;
+            this.Playing = false;
+            if (this._startupTime !== -1) {
+                this.Seek(this.Document.curPage);
+            }
+            else {
+                let cpage = this.Document.curPage;
+                this.Pause();
+                this._startupTime = cpage;
+                this._youtubePlayer.seekTo(cpage);
+            }
         }
     }
 
@@ -183,12 +164,43 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
             </video>;
     }
 
+    times = 0;
+    lastyoutube = undefined;
+    @action load = () => {
+        let x = ++this.times;
+        console.log("LOADED " + x + " times = " + this.times);
+        let onYoutubePlayerStateChange = (event: any) => runInAction(() => {
+            console.log("STATE + " + event.data + "  times = " + this.times + " " + x);
+            //if (this.times !== x) return;
+            if (event.data === YT.PlayerState.UNSTARTED || event.data === YT.PlayerState.BUFFERING)
+                return;
+            if (this._startupTime !== -1) {
+                this._startupTime = -1;
+                if (event.data === YT.PlayerState.PLAYING) this.Pause();
+            }
+            if (event.data == YT.PlayerState.PLAYING && !this.Playing) this.Play(false);
+            if (event.data == YT.PlayerState.PAUSED && this.Playing) this.Pause(false);
+        });
+        let cpage = NumCast(this.Document.curPage);
+        this._startupTime = cpage;
+        this.lastyoutube = this._youtubePlayer;
+        this._youtubePlayer = new YT.Player(`${this.youtubeVideoId + this.counter}-player`, {
+            events: {
+                'onReady': this.onYoutubePlayerReady,
+                'onStateChange': onYoutubePlayerStateChange
+            }
+        });
+    }
+
     @computed get youtubeContent() {
+        this.counter = VideoBox._staticCounter++;
         let style = "videoBox-content-YouTube" + (this._fullScreen ? "-fullScreen" : "");
-        return <div id={`${this.youtubeVideoId}-player`} className={`${style}`} />;
+        return <iframe id={`${this.youtubeVideoId + this.counter.toString()}-player`} onLoad={this.load} className={`${style}`} width="640" height="390"
+            src={`http://www.youtube.com/embed/${this.youtubeVideoId}?enablejsapi=1`} />
     }
 
     render() {
+        trace();
         return <div style={{ pointerEvents: "all", width: "100%", height: "100%" }} onContextMenu={this.specificContextMenu}>
             {this.youtubeVideoId ? this.youtubeContent : this.content}
         </div>;
-- 
cgit v1.2.3-70-g09d2


From 005367a60410af854cad60ee59be642bd411c37e Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Tue, 16 Jul 2019 00:53:58 -0400
Subject: fixed youtube except for automatic update on scrubbing and hiding of
 related videos.

---
 src/client/views/nodes/VideoBox.tsx | 116 +++++++++++++++++-------------------
 1 file changed, 56 insertions(+), 60 deletions(-)

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

diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 552d3f188..864db0bfc 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -24,6 +24,11 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
     private _youtubeReactionDisposer?: IReactionDisposer;
     private _youtubePlayer: any = undefined;
     private _videoRef: HTMLVideoElement | null = null;
+    private _youtubeIframeId: number = -1;
+    private _youtubeContentCreated = false;
+    static _youtubeIframeCounter: number = 0;
+    @observable _forceCreateYouTubeIFrame = false;
+    @observable static _showControls: boolean;
     @observable _playTimer?: NodeJS.Timeout = undefined;
     @observable _fullScreen = false;
     @observable public Playing: boolean = false;
@@ -53,6 +58,7 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
     }
 
     @action public Seek(time: number) {
+        //if (this._youtubePlayer && this._youtubePlayer.getPlayerState() === 5) return;
         this._youtubePlayer && !this.Playing && this._youtubePlayer.seekTo(time);
     }
 
@@ -73,15 +79,10 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
 
     @action
     updateTimecode = () => {
-        let ctime = this._youtubePlayer.getCurrentTime();
-        let otime = this.lastyoutube ? (this.lastyoutube as any).getCurrentTime() : -1;
-        console.log("Setting " + ctime + " " + otime);
         this.player && (this.props.Document.curPage = this.player.currentTime);
         this._youtubePlayer && (this.props.Document.curPage = this._youtubePlayer.getCurrentTime());
     }
-    static _staticCounter: number = 0;
-    counter: number = -1;
-    _startupTime: number = -1;
+
     componentDidMount() {
         if (this.props.setVideoBox) this.props.setVideoBox(this);
 
@@ -94,28 +95,6 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
                 this.Document.nativeHeight = this.Document.nativeWidth / youtubeaspect;
                 this.Document.height = FieldValue(this.Document.width, 0) / youtubeaspect;
             }
-            this._reactionDisposer = reaction(() => this.props.Document.curPage, () => this.Seek(this.Document.curPage || 0), { fireImmediately: true });
-            this._youtubeReactionDisposer = reaction(() => [this.props.isSelected(), DocumentDecorations.Instance.Interacting, InkingControl.Instance.selectedTool], () => {
-                let interactive = InkingControl.Instance.selectedTool === InkTool.None && this.props.isSelected() && !DocumentDecorations.Instance.Interacting;
-                this._youtubePlayer.getIframe().style.pointerEvents = interactive ? "all" : "none";
-            }, { fireImmediately: true })
-        }
-    }
-
-    @action
-    onYoutubePlayerReady = (event: any) => {
-        this._youtubePlayer && (this._youtubePlayer.getIframe().style.pointerEvents = "none");
-        if (this.Document.curPage) {
-            this.Playing = false;
-            if (this._startupTime !== -1) {
-                this.Seek(this.Document.curPage);
-            }
-            else {
-                let cpage = this.Document.curPage;
-                this.Pause();
-                this._startupTime = cpage;
-                this._youtubePlayer.seekTo(cpage);
-            }
         }
     }
 
@@ -137,13 +116,6 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
         }
     }
 
-    @observable static _showControls: boolean;
-
-    @computed get youtubeVideoId() {
-        let field = Cast(this.Document[this.props.fieldKey], VideoField);
-        return field && field.url.href.indexOf("youtube") !== -1 ? ((arr: string[]) => arr[arr.length - 1])(field.url.href.split("/")) : "";
-    }
-
     specificContextMenu = (e: React.MouseEvent): void => {
         let field = Cast(this.Document[this.props.fieldKey], VideoField);
         if (field) {
@@ -158,49 +130,73 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
         let interactive = InkingControl.Instance.selectedTool || !this.props.isSelected() ? "" : "-interactive";
         let style = "videoBox-content" + (this._fullScreen ? "-fullScreen" : "") + interactive;
         return !field ? <div>Loading</div> :
-            <video className={`${style}`} ref={this.setVideoRef} onCanPlay={this.videoLoad} controls={VideoBox._showControls} onPlay={this.Play} onSeeked={this.updateTimecode} onPause={this.Pause}>
+            <video className={`${style}`} ref={this.setVideoRef} onCanPlay={this.videoLoad} controls={VideoBox._showControls}
+                onPlay={() => this.Play()} onSeeked={this.updateTimecode} onPause={() => this.Pause()}>
                 <source src={field.url.href} type="video/mp4" />
                 Not supported.
             </video>;
     }
 
-    times = 0;
-    lastyoutube = undefined;
-    @action load = () => {
-        let x = ++this.times;
-        console.log("LOADED " + x + " times = " + this.times);
+    @computed get youtubeVideoId() {
+        let field = Cast(this.Document[this.props.fieldKey], VideoField);
+        return field && field.url.href.indexOf("youtube") !== -1 ? ((arr: string[]) => arr[arr.length - 1])(field.url.href.split("/")) : "";
+    }
+
+    @action youtubeIframeLoaded = (e: any) => {
+        if (!this._youtubeContentCreated) {
+            this._forceCreateYouTubeIFrame = !this._forceCreateYouTubeIFrame;
+            return;
+        }
+        else this._youtubeContentCreated = false;
+
+        let iframe = e.target;
+        let startupTimecode = NumCast(this.Document.curPage);
         let onYoutubePlayerStateChange = (event: any) => runInAction(() => {
-            console.log("STATE + " + event.data + "  times = " + this.times + " " + x);
-            //if (this.times !== x) return;
-            if (event.data === YT.PlayerState.UNSTARTED || event.data === YT.PlayerState.BUFFERING)
-                return;
-            if (this._startupTime !== -1) {
-                this._startupTime = -1;
-                if (event.data === YT.PlayerState.PLAYING) this.Pause();
+            if (event.data !== YT.PlayerState.UNSTARTED && event.data !== YT.PlayerState.BUFFERING) {
+                if (startupTimecode !== -1) {
+                    startupTimecode = -1;
+                    if (event.data === YT.PlayerState.PLAYING) this.Pause();
+                }
+                if (event.data == YT.PlayerState.PLAYING && !this.Playing) this.Play(false);
+                if (event.data == YT.PlayerState.PAUSED && this.Playing) this.Pause(false);
             }
-            if (event.data == YT.PlayerState.PLAYING && !this.Playing) this.Play(false);
-            if (event.data == YT.PlayerState.PAUSED && this.Playing) this.Pause(false);
         });
-        let cpage = NumCast(this.Document.curPage);
-        this._startupTime = cpage;
-        this.lastyoutube = this._youtubePlayer;
-        this._youtubePlayer = new YT.Player(`${this.youtubeVideoId + this.counter}-player`, {
+        let onYoutubePlayerReady = (event: any) => {
+            this._reactionDisposer && this._reactionDisposer();
+            this._youtubeReactionDisposer && this._youtubeReactionDisposer();
+            this._reactionDisposer = reaction(() => this.props.Document.curPage, () => this.Seek(this.Document.curPage || 0));
+            this._youtubeReactionDisposer = reaction(() => [this.props.isSelected(), DocumentDecorations.Instance.Interacting, InkingControl.Instance.selectedTool], () => {
+                let interactive = InkingControl.Instance.selectedTool === InkTool.None && this.props.isSelected() && !DocumentDecorations.Instance.Interacting;
+                iframe.style.pointerEvents = interactive ? "all" : "none";
+            }, { fireImmediately: true });
+            if (startupTimecode) {
+                this.Playing = false;
+                this.Seek(startupTimecode);
+            }
+        }
+        this._youtubePlayer = new YT.Player(`${this.youtubeVideoId + this._youtubeIframeId}-player`, {
+            playerVars: {
+                rel: 0,
+                start: NumCast(this.props.Document.curPage)
+            },
             events: {
-                'onReady': this.onYoutubePlayerReady,
-                'onStateChange': onYoutubePlayerStateChange
+                'onReady': onYoutubePlayerReady,
+                'onStateChange': onYoutubePlayerStateChange,
             }
         });
+
     }
 
     @computed get youtubeContent() {
-        this.counter = VideoBox._staticCounter++;
+        this._youtubeIframeId = VideoBox._youtubeIframeCounter++;
+        this._youtubeContentCreated = this._forceCreateYouTubeIFrame ? true : true;
         let style = "videoBox-content-YouTube" + (this._fullScreen ? "-fullScreen" : "");
-        return <iframe id={`${this.youtubeVideoId + this.counter.toString()}-player`} onLoad={this.load} className={`${style}`} width="640" height="390"
-            src={`http://www.youtube.com/embed/${this.youtubeVideoId}?enablejsapi=1`} />
+        return <iframe key={this._youtubeIframeId} id={`${this.youtubeVideoId + this._youtubeIframeId.toString()}-player`}
+            onLoad={this.youtubeIframeLoaded} className={`${style}`} width="640" height="390"
+            src={`https://www.youtube.com/embed/${this.youtubeVideoId}?enablejsapi=1`} />
     }
 
     render() {
-        trace();
         return <div style={{ pointerEvents: "all", width: "100%", height: "100%" }} onContextMenu={this.specificContextMenu}>
             {this.youtubeVideoId ? this.youtubeContent : this.content}
         </div>;
-- 
cgit v1.2.3-70-g09d2


From 162bad9f993d349f4baecd714ba51d0f4d0ba3a3 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Tue, 16 Jul 2019 08:50:12 -0400
Subject: trying to fix startup time

---
 src/client/views/nodes/VideoBox.tsx | 24 ++++++++++++++----------
 1 file changed, 14 insertions(+), 10 deletions(-)

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

diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 864db0bfc..85e56ca7b 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -14,6 +14,7 @@ import { pageSchema } from "./ImageBox";
 import "./VideoBox.scss";
 import { InkTool } from "../../../new_fields/InkField";
 import { DocumentDecorations } from "../DocumentDecorations";
+import { addHiddenFinalProp } from "mobx/lib/internal";
 
 type VideoDocument = makeInterface<[typeof positionSchema, typeof pageSchema]>;
 const VideoDocument = makeInterface(positionSchema, pageSchema);
@@ -153,10 +154,10 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
         let startupTimecode = NumCast(this.Document.curPage);
         let onYoutubePlayerStateChange = (event: any) => runInAction(() => {
             if (event.data !== YT.PlayerState.UNSTARTED && event.data !== YT.PlayerState.BUFFERING) {
-                if (startupTimecode !== -1) {
-                    startupTimecode = -1;
-                    if (event.data === YT.PlayerState.PLAYING) this.Pause();
-                }
+                // if (startupTimecode !== -1) {
+                //     startupTimecode = -1;
+                //     if (event.data === YT.PlayerState.PLAYING) this.Pause();
+                // }
                 if (event.data == YT.PlayerState.PLAYING && !this.Playing) this.Play(false);
                 if (event.data == YT.PlayerState.PAUSED && this.Playing) this.Pause(false);
             }
@@ -169,15 +170,18 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
                 let interactive = InkingControl.Instance.selectedTool === InkTool.None && this.props.isSelected() && !DocumentDecorations.Instance.Interacting;
                 iframe.style.pointerEvents = interactive ? "all" : "none";
             }, { fireImmediately: true });
-            if (startupTimecode) {
-                this.Playing = false;
-                this.Seek(startupTimecode);
-            }
+            // if (startupTimecode) {
+            //     this.Playing = false;
+            //     this.Seek(startupTimecode);
+            // }
         }
         this._youtubePlayer = new YT.Player(`${this.youtubeVideoId + this._youtubeIframeId}-player`, {
             playerVars: {
                 rel: 0,
-                start: NumCast(this.props.Document.curPage)
+                start: NumCast(this.props.Document.curPage),
+                fs: 1,
+                modestbranding: 1, // Modest
+                controls: VideoBox._showControls ? 1 : 0// YT.Controls.ShowLoadPlayer : YT.Controls.Hide
             },
             events: {
                 'onReady': onYoutubePlayerReady,
@@ -193,7 +197,7 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
         let style = "videoBox-content-YouTube" + (this._fullScreen ? "-fullScreen" : "");
         return <iframe key={this._youtubeIframeId} id={`${this.youtubeVideoId + this._youtubeIframeId.toString()}-player`}
             onLoad={this.youtubeIframeLoaded} className={`${style}`} width="640" height="390"
-            src={`https://www.youtube.com/embed/${this.youtubeVideoId}?enablejsapi=1`} />
+            src={`https://www.youtube.com/embed/${this.youtubeVideoId}?enablejsapi=1&fs=1&rel=0&start=32&controls=0`} />
     }
 
     render() {
-- 
cgit v1.2.3-70-g09d2


From bbfc935958b88c7ecaeb9a1e06a255cac958ec4c Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Tue, 16 Jul 2019 11:58:16 -0400
Subject: added extendsDoc to extension doc's  fixed folllowing links to video
 locations.  fixed aspects of youtube integration.

---
 .../views/collections/CollectionVideoView.tsx      |  4 +--
 .../collections/collectionFreeForm/MarqueeView.tsx |  2 +-
 src/client/views/nodes/LinkMenuItem.tsx            |  5 +--
 src/client/views/nodes/VideoBox.tsx                | 42 +++++++++-------------
 src/new_fields/Doc.ts                              |  6 +++-
 5 files changed, 27 insertions(+), 32 deletions(-)

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

diff --git a/src/client/views/collections/CollectionVideoView.tsx b/src/client/views/collections/CollectionVideoView.tsx
index f731c4cef..faf507496 100644
--- a/src/client/views/collections/CollectionVideoView.tsx
+++ b/src/client/views/collections/CollectionVideoView.tsx
@@ -88,14 +88,14 @@ export class CollectionVideoView extends React.Component<FieldViewProps> {
                 canvas.width = 640;
                 canvas.height = 640 * NumCast(this.props.Document.nativeHeight) / NumCast(this.props.Document.nativeWidth);
                 var ctx = canvas.getContext('2d');//draw image to canvas. scale to target dimensions
-                ctx && ctx.drawImage(this._videoBox!.player!, 0, 0, canvas.width, canvas.height);
+                this._videoBox!.player && ctx && ctx.drawImage(this._videoBox!.player!, 0, 0, canvas.width, canvas.height);
 
                 //convert to desired file format
                 var dataUrl = canvas.toDataURL('image/png'); // can also use 'image/png'
                 // if you want to preview the captured image,
 
                 let filename = encodeURIComponent("snapshot" + this.props.Document.title + "_" + this.props.Document.curPage).replace(/\./g, "");
-                SearchBox.convertDataUri(dataUrl, filename).then((returnedFilename) => {
+                SearchBox.convertDataUri(dataUrl, filename).then(returnedFilename => {
                     if (returnedFilename) {
                         let url = DocServer.prepend(returnedFilename);
                         let imageSummary = Docs.Create.ImageDocument(url, {
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index a4a6881f8..b765517a2 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -147,7 +147,6 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
         PreviewCursor.Visible = false;
         this.cleanupInteractions(true);
         if (e.button === 2 || (e.button === 0 && e.altKey)) {
-            if (!this.props.container.props.active()) this.props.selectDocuments([this.props.container.props.Document]);
             document.addEventListener("pointermove", this.onPointerMove, true);
             document.addEventListener("pointerup", this.onPointerUp, true);
             document.addEventListener("keydown", this.marqueeCommand, true);
@@ -181,6 +180,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
 
     @action
     onPointerUp = (e: PointerEvent): void => {
+        if (!this.props.container.props.active()) this.props.selectDocuments([this.props.container.props.Document]);
         // console.log("pointer up!");
         if (this._visible) {
             // console.log("visible");
diff --git a/src/client/views/nodes/LinkMenuItem.tsx b/src/client/views/nodes/LinkMenuItem.tsx
index 9728671c0..6a18a4e7b 100644
--- a/src/client/views/nodes/LinkMenuItem.tsx
+++ b/src/client/views/nodes/LinkMenuItem.tsx
@@ -7,7 +7,7 @@ import { undoBatch } from "../../util/UndoManager";
 import './LinkMenu.scss';
 import React = require("react");
 import { Doc } from '../../../new_fields/Doc';
-import { StrCast, Cast, BoolCast, FieldValue } from '../../../new_fields/Types';
+import { StrCast, Cast, BoolCast, FieldValue, NumCast } from '../../../new_fields/Types';
 import { observable, action } from 'mobx';
 import { LinkManager } from '../../util/LinkManager';
 import { DragLinkAsDocument } from '../../util/DragManager';
@@ -38,7 +38,8 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
             jumpToDoc = pdfDoc;
         }
         if (DocumentManager.Instance.getDocumentView(jumpToDoc)) {
-            DocumentManager.Instance.jumpToDocument(jumpToDoc, e.altKey);
+            let self = this;
+            DocumentManager.Instance.jumpToDocument(jumpToDoc, e.altKey, undefined, undefined, NumCast((this.props.destinationDoc === self.props.linkDoc.anchor2 ? self.props.linkDoc.anchor2Page : self.props.linkDoc.anchor1Page)));
         } else {
             CollectionDockingView.Instance.AddRightSplit(jumpToDoc, undefined);
         }
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 85e56ca7b..3ade3396e 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -1,5 +1,5 @@
 import React = require("react");
-import { action, IReactionDisposer, observable, reaction, trace, computed, runInAction } from "mobx";
+import { action, IReactionDisposer, observable, reaction, trace, computed, runInAction, untracked } from "mobx";
 import { observer } from "mobx-react";
 import { makeInterface } from "../../../new_fields/Schema";
 import { Cast, FieldValue, NumCast } from "../../../new_fields/Types";
@@ -14,7 +14,6 @@ import { pageSchema } from "./ImageBox";
 import "./VideoBox.scss";
 import { InkTool } from "../../../new_fields/InkField";
 import { DocumentDecorations } from "../DocumentDecorations";
-import { addHiddenFinalProp } from "mobx/lib/internal";
 
 type VideoDocument = makeInterface<[typeof positionSchema, typeof pageSchema]>;
 const VideoDocument = makeInterface(positionSchema, pageSchema);
@@ -53,18 +52,21 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
     @action public Play = (update: boolean = true) => {
         this.Playing = true;
         update && this.player && this.player.play();
+        console.log("PLAYING = " + update);
         update && this._youtubePlayer && this._youtubePlayer.playVideo();
         !this._playTimer && (this._playTimer = setInterval(this.updateTimecode, 500));
         this.updateTimecode();
     }
 
     @action public Seek(time: number) {
+        console.log("Seeking " + time);
         //if (this._youtubePlayer && this._youtubePlayer.getPlayerState() === 5) return;
-        this._youtubePlayer && !this.Playing && this._youtubePlayer.seekTo(time);
+        this._youtubePlayer && this._youtubePlayer.seekTo(Math.round(time), true);
     }
 
     @action public Pause = (update: boolean = true) => {
         this.Playing = false;
+        console.log("PAUSING = " + update);
         update && this.player && this.player.pause();
         update && this._youtubePlayer && this._youtubePlayer.pauseVideo();
         this._playTimer && clearInterval(this._playTimer);
@@ -122,6 +124,8 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
         if (field) {
             let subitems: ContextMenuProps[] = [];
             subitems.push({ description: "Toggle Show Controls", event: action(() => VideoBox._showControls = !VideoBox._showControls), icon: "expand-arrows-alt" });
+            subitems.push({ description: "GOTO 3", event: action(() => this.Seek(3)), icon: "expand-arrows-alt" });
+            subitems.push({ description: "PLAY", event: action(() => this.Play()), icon: "expand-arrows-alt" });
             ContextMenu.Instance.addItem({ description: "Video Funcs...", subitems: subitems });
         }
     }
@@ -151,38 +155,22 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
         else this._youtubeContentCreated = false;
 
         let iframe = e.target;
-        let startupTimecode = NumCast(this.Document.curPage);
         let onYoutubePlayerStateChange = (event: any) => runInAction(() => {
-            if (event.data !== YT.PlayerState.UNSTARTED && event.data !== YT.PlayerState.BUFFERING) {
-                // if (startupTimecode !== -1) {
-                //     startupTimecode = -1;
-                //     if (event.data === YT.PlayerState.PLAYING) this.Pause();
-                // }
-                if (event.data == YT.PlayerState.PLAYING && !this.Playing) this.Play(false);
-                if (event.data == YT.PlayerState.PAUSED && this.Playing) this.Pause(false);
-            }
+            console.log("Event  " + event.data);
+            if (event.data == YT.PlayerState.PLAYING && !this.Playing) this.Play(false);
+            if (event.data == YT.PlayerState.PAUSED && this.Playing) this.Pause(false);
         });
         let onYoutubePlayerReady = (event: any) => {
+            console.log("READY!");
             this._reactionDisposer && this._reactionDisposer();
             this._youtubeReactionDisposer && this._youtubeReactionDisposer();
-            this._reactionDisposer = reaction(() => this.props.Document.curPage, () => this.Seek(this.Document.curPage || 0));
+            this._reactionDisposer = reaction(() => this.props.Document.curPage, () => !this.Playing && this.Seek(this.Document.curPage || 0));
             this._youtubeReactionDisposer = reaction(() => [this.props.isSelected(), DocumentDecorations.Instance.Interacting, InkingControl.Instance.selectedTool], () => {
                 let interactive = InkingControl.Instance.selectedTool === InkTool.None && this.props.isSelected() && !DocumentDecorations.Instance.Interacting;
                 iframe.style.pointerEvents = interactive ? "all" : "none";
             }, { fireImmediately: true });
-            // if (startupTimecode) {
-            //     this.Playing = false;
-            //     this.Seek(startupTimecode);
-            // }
         }
         this._youtubePlayer = new YT.Player(`${this.youtubeVideoId + this._youtubeIframeId}-player`, {
-            playerVars: {
-                rel: 0,
-                start: NumCast(this.props.Document.curPage),
-                fs: 1,
-                modestbranding: 1, // Modest
-                controls: VideoBox._showControls ? 1 : 0// YT.Controls.ShowLoadPlayer : YT.Controls.Hide
-            },
             events: {
                 'onReady': onYoutubePlayerReady,
                 'onStateChange': onYoutubePlayerStateChange,
@@ -195,9 +183,11 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
         this._youtubeIframeId = VideoBox._youtubeIframeCounter++;
         this._youtubeContentCreated = this._forceCreateYouTubeIFrame ? true : true;
         let style = "videoBox-content-YouTube" + (this._fullScreen ? "-fullScreen" : "");
-        return <iframe key={this._youtubeIframeId} id={`${this.youtubeVideoId + this._youtubeIframeId.toString()}-player`}
+        let start = untracked(() => Math.round(NumCast(this.props.Document.curPage)));
+        return <iframe key={this._youtubeIframeId} id={`${this.youtubeVideoId + this._youtubeIframeId}-player`}
             onLoad={this.youtubeIframeLoaded} className={`${style}`} width="640" height="390"
-            src={`https://www.youtube.com/embed/${this.youtubeVideoId}?enablejsapi=1&fs=1&rel=0&start=32&controls=0`} />
+            src={`https://www.youtube.com/embed/${this.youtubeVideoId}?enablejsapi=1&rel=0&showinfo=1&autoplay=1&start=${start}&modestbranding=1&controls=${VideoBox._showControls ? 1 : 0}`}
+        ></iframe>;
     }
 
     render() {
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index c5f9e7adf..0d9fa540f 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -312,16 +312,20 @@ export namespace Doc {
     }
 
     export function UpdateDocumentExtensionForField(doc: Doc, fieldKey: string) {
-        if (doc[fieldKey + "_ext"] === undefined) {
+        let extensionDoc = doc[fieldKey + "_ext"];
+        if (extensionDoc === undefined) {
             setTimeout(() => {
                 let docExtensionForField = new Doc(doc[Id] + fieldKey, true);
                 docExtensionForField.title = "Extension of " + doc.title + "'s field:" + fieldKey;
+                docExtensionForField.extendsDoc = doc;
                 let proto: Doc | undefined = doc;
                 while (proto && !Doc.IsPrototype(proto)) {
                     proto = proto.proto;
                 }
                 (proto ? proto : doc)[fieldKey + "_ext"] = docExtensionForField;
             }, 0);
+        } else if (extensionDoc instanceof Doc && extensionDoc.extendsDoc === undefined) {
+            setTimeout(() => (extensionDoc as Doc).extendsDoc = doc, 0);
         }
     }
     export function MakeAlias(doc: Doc) {
-- 
cgit v1.2.3-70-g09d2


From b10e8e0c22bdc27e39c365cae1a1592bc714b547 Mon Sep 17 00:00:00 2001
From: Sam Wilkins <samuel_wilkins@brown.edu>
Date: Tue, 16 Jul 2019 12:23:10 -0400
Subject: linter

---
 src/client/views/nodes/VideoBox.tsx | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

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

diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 3ade3396e..972e6875d 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -157,8 +157,8 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
         let iframe = e.target;
         let onYoutubePlayerStateChange = (event: any) => runInAction(() => {
             console.log("Event  " + event.data);
-            if (event.data == YT.PlayerState.PLAYING && !this.Playing) this.Play(false);
-            if (event.data == YT.PlayerState.PAUSED && this.Playing) this.Pause(false);
+            if (event.data === YT.PlayerState.PLAYING && !this.Playing) this.Play(false);
+            if (event.data === YT.PlayerState.PAUSED && this.Playing) this.Pause(false);
         });
         let onYoutubePlayerReady = (event: any) => {
             console.log("READY!");
@@ -169,7 +169,7 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
                 let interactive = InkingControl.Instance.selectedTool === InkTool.None && this.props.isSelected() && !DocumentDecorations.Instance.Interacting;
                 iframe.style.pointerEvents = interactive ? "all" : "none";
             }, { fireImmediately: true });
-        }
+        };
         this._youtubePlayer = new YT.Player(`${this.youtubeVideoId + this._youtubeIframeId}-player`, {
             events: {
                 'onReady': onYoutubePlayerReady,
-- 
cgit v1.2.3-70-g09d2

