From caebaeca6993ff48609ab16dcc390d4d0458b03a Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Wed, 16 Oct 2019 23:50:27 -0400
Subject: fixed applying templates to collections.  fixed templates containing
 collections

---
 src/client/views/InkingCanvas.tsx | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

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

diff --git a/src/client/views/InkingCanvas.tsx b/src/client/views/InkingCanvas.tsx
index 9ab320eab..920ebaedd 100644
--- a/src/client/views/InkingCanvas.tsx
+++ b/src/client/views/InkingCanvas.tsx
@@ -181,9 +181,10 @@ export class InkingCanvas extends React.Component<InkCanvasProps> {
 
     render() {
         let svgCanvasStyle = InkingControl.Instance.selectedTool !== InkTool.None && !this.props.Document.isBackground ? "canSelect" : "noSelect";
+        let cursor = svgCanvasStyle === "canSelect" ? (InkingControl.Instance.selectedTool === InkTool.Eraser ? "pointer" : "default") : undefined;
         return (
             <div className="inkingCanvas">
-                <div className={`inkingCanvas-${svgCanvasStyle}`} onPointerDown={this.onPointerDown} />
+                <div className={`inkingCanvas-${svgCanvasStyle}`} onPointerDown={this.onPointerDown} style={{ cursor: cursor }} />
                 {this.props.children()}
                 {this.drawnPaths}
             </div >
-- 
cgit v1.2.3-70-g09d2


From 77119dc1719af955e162248e5747bd9ef8921b4c Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Tue, 22 Oct 2019 20:43:24 -0400
Subject: added ink audio scrubbing

---
 src/client/documents/Documents.ts                   |  4 +---
 src/client/views/InkingCanvas.tsx                   | 11 +++++++----
 src/client/views/InkingControl.tsx                  |  1 +
 src/client/views/InkingStroke.tsx                   | 18 ++++++++++++------
 src/client/views/nodes/AudioBox.tsx                 | 21 +++++++++++++--------
 src/client/views/nodes/VideoBox.tsx                 |  2 --
 src/new_fields/InkField.ts                          |  4 +++-
 .../authentication/models/current_user_utils.ts     |  1 +
 8 files changed, 38 insertions(+), 24 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 316efe44c..5ae4ca82a 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -334,9 +334,7 @@ export namespace Docs {
             let dataDoc = MakeDataDelegate(proto, protoProps, data);
             let viewDoc = Doc.MakeDelegate(dataDoc, delegId);
 
-            AudioBox.ActiveRecordings.map(d => {
-                DocUtils.MakeLink({ doc: viewDoc }, { doc: d }, "audio link", "link to audio: " + d.title);
-            });
+            AudioBox.ActiveRecordings.map(d => DocUtils.MakeLink({ doc: viewDoc }, { doc: d }, "audio link", "link to audio: " + d.title));
 
             return Doc.assign(viewDoc, delegateProps);
         }
diff --git a/src/client/views/InkingCanvas.tsx b/src/client/views/InkingCanvas.tsx
index 920ebaedd..0037b95d0 100644
--- a/src/client/views/InkingCanvas.tsx
+++ b/src/client/views/InkingCanvas.tsx
@@ -78,7 +78,7 @@ export class InkingCanvas extends React.Component<InkCanvasProps> {
 
         this.previousState = new Map(this.inkData);
 
-        if (InkingControl.Instance.selectedTool !== InkTool.Eraser) {
+        if (InkingControl.Instance.selectedTool !== InkTool.Eraser && InkingControl.Instance.selectedTool !== InkTool.Scrubber) {
             // start the new line, saves a uuid to represent the field of the stroke
             this._currentStrokeId = Utils.GenerateGuid();
             const data = this.inkData;
@@ -87,7 +87,8 @@ export class InkingCanvas extends React.Component<InkCanvasProps> {
                 color: InkingControl.Instance.selectedColor,
                 width: InkingControl.Instance.selectedWidth,
                 tool: InkingControl.Instance.selectedTool,
-                displayTimecode: NumCast(this.props.Document.currentTimecode, -1)
+                displayTimecode: NumCast(this.props.Document.currentTimecode, -1),
+                creationTime: new Date().getTime()
             });
             this.inkData = data;
         }
@@ -120,7 +121,7 @@ export class InkingCanvas extends React.Component<InkCanvasProps> {
     onPointerMove = (e: PointerEvent): void => {
         e.stopPropagation();
         e.preventDefault();
-        if (InkingControl.Instance.selectedTool !== InkTool.Eraser) {
+        if (InkingControl.Instance.selectedTool !== InkTool.Eraser && InkingControl.Instance.selectedTool !== InkTool.Scrubber) {
             let data = this.inkData;  // add points to new line as it is being drawn
             let strokeData = data.get(this._currentStrokeId);
             if (strokeData) {
@@ -161,6 +162,7 @@ export class InkingCanvas extends React.Component<InkCanvasProps> {
                     color={strokeData.color}
                     width={strokeData.width}
                     tool={strokeData.tool}
+                    creationTime={strokeData.creationTime}
                     deleteCallback={this.removeLine} />);
             }
             return paths;
@@ -181,7 +183,8 @@ export class InkingCanvas extends React.Component<InkCanvasProps> {
 
     render() {
         let svgCanvasStyle = InkingControl.Instance.selectedTool !== InkTool.None && !this.props.Document.isBackground ? "canSelect" : "noSelect";
-        let cursor = svgCanvasStyle === "canSelect" ? (InkingControl.Instance.selectedTool === InkTool.Eraser ? "pointer" : "default") : undefined;
+        let cursor = svgCanvasStyle === "canSelect" ? (InkingControl.Instance.selectedTool === InkTool.Eraser ||
+            InkingControl.Instance.selectedTool === InkTool.Scrubber ? "pointer" : "default") : undefined;
         return (
             <div className="inkingCanvas">
                 <div className={`inkingCanvas-${svgCanvasStyle}`} onPointerDown={this.onPointerDown} style={{ cursor: cursor }} />
diff --git a/src/client/views/InkingControl.tsx b/src/client/views/InkingControl.tsx
index 105adc03d..75faa9641 100644
--- a/src/client/views/InkingControl.tsx
+++ b/src/client/views/InkingControl.tsx
@@ -126,6 +126,7 @@ export class InkingControl {
 Scripting.addGlobal(function activatePen(pen: any, width: any, color: any) { InkingControl.Instance.switchTool(pen ? InkTool.Pen : InkTool.None); InkingControl.Instance.switchWidth(width); InkingControl.Instance.updateSelectedColor(color); });
 Scripting.addGlobal(function activateBrush(pen: any, width: any, color: any) { InkingControl.Instance.switchTool(pen ? InkTool.Highlighter : InkTool.None); InkingControl.Instance.switchWidth(width); InkingControl.Instance.updateSelectedColor(color); });
 Scripting.addGlobal(function activateEraser(pen: any) { return InkingControl.Instance.switchTool(pen ? InkTool.Eraser : InkTool.None); });
+Scripting.addGlobal(function activateScrubber(pen: any) { return InkingControl.Instance.switchTool(pen ? InkTool.Scrubber : InkTool.None); });
 Scripting.addGlobal(function deactivateInk() { return InkingControl.Instance.switchTool(InkTool.None); });
 Scripting.addGlobal(function setInkWidth(width: any) { return InkingControl.Instance.switchWidth(width); });
 Scripting.addGlobal(function setInkColor(color: any) { return InkingControl.Instance.updateSelectedColor(color); });
\ No newline at end of file
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index b8d428d31..7bbf71482 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -1,9 +1,10 @@
 import { observer } from "mobx-react";
-import { observable, trace } from "mobx";
+import { observable, trace, runInAction } from "mobx";
 import { InkingControl } from "./InkingControl";
 import React = require("react");
 import { InkTool } from "../../new_fields/InkField";
 import "./InkingStroke.scss";
+import { AudioBox } from "./nodes/AudioBox";
 
 
 interface StrokeProps {
@@ -15,6 +16,7 @@ interface StrokeProps {
     color: string;
     width: string;
     tool: InkTool;
+    creationTime: number;
     deleteCallback: (index: string) => void;
 }
 
@@ -31,6 +33,11 @@ export class InkingStroke extends React.Component<StrokeProps> {
             e.stopPropagation();
             e.preventDefault();
         }
+        if (InkingControl.Instance.selectedTool === InkTool.Scrubber && e.buttons === 1) {
+            runInAction(() => AudioBox.ScrubTime = this.props.creationTime);
+            e.stopPropagation();
+            e.preventDefault();
+        }
     }
 
     parseData = (line: Array<{ x: number, y: number }>): string => {
@@ -55,10 +62,9 @@ export class InkingStroke extends React.Component<StrokeProps> {
         let pathlength = this.props.count; // bcz: this is needed to force reactions to the line's data changes
         let marker = this.props.tool === InkTool.Highlighter ? "-marker" : "";
 
-        let pointerEvents: any = InkingControl.Instance.selectedTool === InkTool.Eraser ? "all" : "none";
-        return (
-            <path className={`inkingStroke${marker}`} d={pathData} style={{ ...pathStyle, pointerEvents: pointerEvents }} strokeLinejoin="round" strokeLinecap="round"
-                onPointerOver={this.deleteStroke} onPointerDown={this.deleteStroke} />
-        );
+        let pointerEvents: any = InkingControl.Instance.selectedTool === InkTool.Eraser ||
+            InkingControl.Instance.selectedTool === InkTool.Scrubber ? "all" : "none";
+        return (<path className={`inkingStroke${marker}`} d={pathData} style={{ ...pathStyle, pointerEvents: pointerEvents }}
+            strokeLinejoin="round" strokeLinecap="round" onPointerOver={this.deleteStroke} onPointerDown={this.deleteStroke} />);
     }
 }
\ No newline at end of file
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index 55b472726..62ec683da 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -38,11 +38,13 @@ export class AudioBox extends DocExtendableComponent<FieldViewProps, AudioDocume
 
     _linkPlayDisposer: IReactionDisposer | undefined;
     _reactionDisposer: IReactionDisposer | undefined;
+    _scrubbingDisposer: IReactionDisposer | undefined;
     _ele: HTMLAudioElement | null = null;
     _recorder: any;
     _lastUpdate = 0;
 
     @observable private _audioState = 0;
+    @observable public static ScrubTime = 0;
     public static ActiveRecordings: Doc[] = [];
 
     componentDidMount() {
@@ -53,7 +55,9 @@ export class AudioBox extends DocExtendableComponent<FieldViewProps, AudioDocume
                     const la1 = l.anchor1 as Doc;
                     const la2 = l.anchor2 as Doc;
                     if (l[Id] === scrollLinkId && la1 && la2) {
-                        setTimeout(() => this.playFrom(Doc.AreProtosEqual(la1, this.dataDoc) ? la2 : la1), 250);
+                        let doc = Doc.AreProtosEqual(la1, this.dataDoc) ? la2 : la1;
+                        let seek = DateCast(la1.creationTime);
+                        setTimeout(() => this.playFrom(seek.date.getTime()), 250);
                     }
                 });
                 scrollLinkId && (this.layoutDoc.scrollLinkID = undefined);
@@ -61,8 +65,9 @@ export class AudioBox extends DocExtendableComponent<FieldViewProps, AudioDocume
         this._reactionDisposer = reaction(() => SelectionManager.SelectedDocuments(),
             selected => {
                 let sel = selected.length ? selected[0].props.Document : undefined;
-                this.Document.playOnSelect && sel && !Doc.AreProtosEqual(sel, this.props.Document) && this.playFrom(sel);
+                this.Document.playOnSelect && sel && !Doc.AreProtosEqual(sel, this.props.Document) && this.playFrom(DateCast(sel.creationTime).date.getTime());
             });
+        this._scrubbingDisposer = reaction(() => AudioBox.ScrubTime, time => this.Document.playOnSelect && this.playFrom(time));
     }
 
     updateHighlights = () => {
@@ -86,14 +91,13 @@ export class AudioBox extends DocExtendableComponent<FieldViewProps, AudioDocume
         }
     }
 
-    playFrom = (sel: Doc) => {
+    playFrom = (seek: number) => {
         const extensionDoc = this.extensionDoc;
         let start = extensionDoc && DateCast(extensionDoc.recordingStart);
-        let seek = sel && DateCast(sel.creationDate);
-        if (this._ele && start && seek) {
-            if (sel) {
-                let delta = (seek.date.getTime() - start.date.getTime()) / 1000;
-                if (start && seek && delta > 0 && delta < this._ele.duration) {
+        if (this._ele && start) {
+            if (seek) {
+                let delta = (seek - start.date.getTime()) / 1000;
+                if (start && delta > 0 && delta < this._ele.duration) {
                     this._ele.currentTime = delta;
                     this._ele.play();
                     this._lastUpdate = delta;
@@ -110,6 +114,7 @@ export class AudioBox extends DocExtendableComponent<FieldViewProps, AudioDocume
     componentWillUnmount() {
         this._reactionDisposer && this._reactionDisposer();
         this._linkPlayDisposer && this._linkPlayDisposer();
+        this._scrubbingDisposer && this._scrubbingDisposer();
     }
 
     recordAudioAnnotation = () => {
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 48a699e58..53baea4ae 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -301,7 +301,6 @@ export class VideoBox extends DocAnnotatableComponent<FieldViewProps, VideoDocum
         this._isResetClick = 0;
         document.addEventListener("pointermove", this.onResetMove, true);
         document.addEventListener("pointerup", this.onResetUp, true);
-        InkingControl.Instance.switchTool(InkTool.Eraser);
     }
 
     onResetMove = (e: PointerEvent) => {
@@ -314,7 +313,6 @@ export class VideoBox extends DocAnnotatableComponent<FieldViewProps, VideoDocum
     onResetUp = (e: PointerEvent) => {
         document.removeEventListener("pointermove", this.onResetMove, true);
         document.removeEventListener("pointerup", this.onResetUp, true);
-        InkingControl.Instance.switchTool(InkTool.None);
         this._isResetClick < 10 && (this.Document.currentTimecode = 0);
     }
 
diff --git a/src/new_fields/InkField.ts b/src/new_fields/InkField.ts
index e381d0218..d94834e91 100644
--- a/src/new_fields/InkField.ts
+++ b/src/new_fields/InkField.ts
@@ -8,7 +8,8 @@ export enum InkTool {
     None,
     Pen,
     Highlighter,
-    Eraser
+    Eraser,
+    Scrubber
 }
 
 export interface StrokeData {
@@ -17,6 +18,7 @@ export interface StrokeData {
     width: string;
     tool: InkTool;
     displayTimecode: number;
+    creationTime: number;
 }
 
 export type InkData = Map<string, StrokeData>;
diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts
index 477b36ea4..95ebe3cb6 100644
--- a/src/server/authentication/models/current_user_utils.ts
+++ b/src/server/authentication/models/current_user_utils.ts
@@ -58,6 +58,7 @@ export class CurrentUserUtils {
             { title: "use pen", icon: "pen-nib", click: 'activatePen(this.activePen.pen = sameDocs(this.activePen.pen, this) ? undefined : this,2, this.backgroundColor)', backgroundColor: "blue", unchecked: `!sameDocs(this.activePen.pen,  this)`, activePen: doc },
             { title: "use highlighter", icon: "highlighter", click: 'activateBrush(this.activePen.pen = sameDocs(this.activePen.pen, this) ? undefined : this,20,this.backgroundColor)', backgroundColor: "yellow", unchecked: `!sameDocs(this.activePen.pen, this)`, activePen: doc },
             { title: "use eraser", icon: "eraser", click: 'activateEraser(this.activePen.pen = sameDocs(this.activePen.pen, this) ? undefined : this);', unchecked: `!sameDocs(this.activePen.pen, this)`, backgroundColor: "pink", activePen: doc },
+            { title: "use scrubber", icon: "eraser", click: 'activateScrubber(this.activePen.pen = sameDocs(this.activePen.pen, this) ? undefined : this);', unchecked: `!sameDocs(this.activePen.pen, this)`, backgroundColor: "green", activePen: doc },
             { title: "use drag", icon: "mouse-pointer", click: 'deactivateInk();this.activePen.pen = this;', unchecked: `!sameDocs(this.activePen.pen, this) && this.activePen.pen !== undefined`, backgroundColor: "white", activePen: doc },
         ];
         return docProtoData.map(data => Docs.Create.FontIconDocument({
-- 
cgit v1.2.3-70-g09d2

