aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/client/documents/Documents.ts3
-rw-r--r--src/client/views/DocComponent.tsx6
-rw-r--r--src/client/views/MarqueeAnnotator.tsx4
-rw-r--r--src/client/views/StyleProvider.tsx3
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx7
-rw-r--r--src/client/views/nodes/AudioBox.tsx13
-rw-r--r--src/client/views/nodes/LabelBox.tsx2
-rw-r--r--src/client/views/nodes/VideoBox.scss24
-rw-r--r--src/client/views/nodes/VideoBox.tsx143
-rw-r--r--src/client/views/nodes/WebBox.tsx6
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx2
-rw-r--r--src/client/views/nodes/formattedText/RichTextMenu.tsx2
12 files changed, 119 insertions, 96 deletions
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index df08345f9..286b7afa9 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -152,6 +152,7 @@ export interface DocumentOptions {
_currentTimecode?: number; // the current timecode of a time-based document (e.g., current time of a video) value is in seconds
_currentFrame?: number; // the current frame of a frame-based collection (e.g., progressive slide)
displayTimecode?: number; // the time that a document should be displayed (e.g., time an annotation should be displayed on a video)
+ undisplayTimecode?: number; // the time that a document should be hidden
lastFrame?: number; // the last frame of a frame-based collection (e.g., progressive slide)
activeFrame?: number; // the active frame of a document in a frame base collection
appearFrame?: number; // the frame in which the document appears
@@ -227,7 +228,7 @@ export interface DocumentOptions {
isLabel?: boolean; // whether the document is a label or not (video / audio)
useLinkSmallAnchor?: boolean; // whether links to this document should use a miniature linkAnchorBox
audioStart?: number; // the time frame where the audio should begin playing
- audioEnd?: number; // the time frame where the audio should stop playing
+ audioEnd?: number; // the time frame where the audio should stop playing
border?: string; //for searchbox
hovercolor?: string;
}
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index 2c7d15ae0..db0b626a1 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -124,7 +124,7 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps, T
public get annotationKey() { return this.fieldKey + "-" + this._annotationKey; }
@action.bound
- removeDocument(doc: Doc | Doc[]): boolean {
+ removeDocument(doc: Doc | Doc[], annotationKey?: string): boolean {
const effectiveAcl = GetEffectiveAcl(this.dataDoc);
const indocs = doc instanceof Doc ? [doc] : doc;
const docs = indocs.filter(doc => effectiveAcl === AclEdit || effectiveAcl === AclAdmin || GetEffectiveAcl(doc) === AclAdmin);
@@ -132,13 +132,13 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps, T
const docs = doc instanceof Doc ? [doc] : doc;
docs.map(doc => doc.isPushpin = doc.annotationOn = undefined);
const targetDataDoc = this.dataDoc;
- const value = DocListCast(targetDataDoc[this.annotationKey]);
+ const value = DocListCast(targetDataDoc[annotationKey ?? this.annotationKey]);
const toRemove = value.filter(v => docs.includes(v));
if (toRemove.length !== 0) {
const recent = Cast(Doc.UserDoc().myRecentlyClosedDocs, Doc) as Doc;
toRemove.forEach(doc => {
- Doc.RemoveDocFromList(targetDataDoc, this.props.fieldKey + "-annotations", doc);
+ Doc.RemoveDocFromList(targetDataDoc, annotationKey ?? this.annotationKey, doc);
recent && Doc.AddDocToList(recent, "data", doc, undefined, true, true);
});
return true;
diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx
index 0ab2d1ecf..8ef69802b 100644
--- a/src/client/views/MarqueeAnnotator.tsx
+++ b/src/client/views/MarqueeAnnotator.tsx
@@ -163,13 +163,13 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
const target = CurrentUserUtils.GetNewTextDoc("Note linked to " + this.props.rootDoc.title, 0, 0, 100, 100);
FormattedTextBox.SelectOnLoad = target[Id];
return target;
- }
+ };
const anchorCreator = () => {
const annoDoc = this.highlight("rgba(173, 216, 230, 0.75)"); // hyperlink color
annoDoc.isLinkButton = true; // prevents link button from showing up --- maybe not a good thing?
this.props.addDocument(annoDoc);
return annoDoc;
- }
+ };
DragManager.StartAnchorAnnoDrag([ele], new DragManager.AnchorAnnoDragData(this.props.rootDoc, anchorCreator, targetCreator), e.pageX, e.pageY, {
dragComplete: e => {
if (!e.aborted && e.annoDragData && e.annoDragData.annotationDocument && e.annoDragData.dropDocument && !e.linkDocument) {
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index 1d822439a..95a28b33e 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -108,9 +108,10 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps |
case DocumentType.FILTER: docColor = docColor || (darkScheme() ? "#2d2d2d" : "rgba(105, 105, 105, 0.432)"); break;
case DocumentType.INK: docColor = doc?.isInkMask ? "rgba(0,0,0,0.7)" : undefined; break;
case DocumentType.SLIDER: break;
- case DocumentType.LABEL: docColor = docColor || (doc?.audioStart !== undefined ? "rgba(128, 128, 128, 0.18)" : undefined); break;
+ case DocumentType.LABEL: docColor = docColor || (doc?.audioStart !== undefined || doc?.displayTimecode !== undefined ? "rgba(128, 128, 128, 0.18)" : undefined); break;
case DocumentType.BUTTON: docColor = docColor || (darkScheme() ? "#2d2d2d" : "lightgray"); break;
case DocumentType.LINK: return "transparent";
+ case DocumentType.VID: docColor = docColor || (darkScheme() ? "#2d2d2d" : "lightgray"); break;
case DocumentType.COL:
if (StrCast(Doc.LayoutField(doc)).includes("SliderBox")) break;
docColor = docColor ? docColor :
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 2bc716928..bc86ecd19 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -196,7 +196,12 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
SelectionManager.DeselectAll();
docs.map(doc => DocumentManager.Instance.getDocumentView(doc, this.props.CollectionView)).map(dv => dv && SelectionManager.SelectView(dv, true));
}
- public isCurrent(doc: Doc) { return (Math.abs(NumCast(doc.displayTimecode, -1) - NumCast(this.Document._currentTimecode, -1)) < 1.5 || NumCast(doc.displayTimecode, -1) === -1); }
+ public isCurrent(doc: Doc) {
+ const dispTime = NumCast(doc.displayTimecode, -1);
+ const endTime = NumCast(doc.undisplayTimecode, dispTime + 1.5);
+ const curTime = NumCast(this.Document._currentTimecode, -1);
+ return dispTime === -1 || (curTime > dispTime && curTime < endTime);
+ }
public getActiveDocuments = () => {
return this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)).map(pair => pair.layout);
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index 77777ff76..4ddb0502b 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -29,6 +29,7 @@ import { FieldView, FieldViewProps } from './FieldView';
import { FormattedTextBoxComment } from "./formattedText/FormattedTextBoxComment";
import { LinkDocPreview } from "./LinkDocPreview";
import "./AudioBox.scss";
+import { Id } from "../../../fields/FieldSymbols";
declare class MediaRecorder {
// whatever MediaRecorder has
@@ -390,9 +391,11 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
if (audioStart === undefined) return this.rootDoc;
const marker = Docs.Create.LabelDocument({
title: ComputedField.MakeFunction(`"#" + formatToTime(self.audioStart) + "-" + formatToTime(self.audioEnd)`) as any,
- useLinkSmallAnchor: true, hideLinkButton: true, audioStart, audioEnd, _showSidebar: false,
- isLabel: audioEnd === undefined,
- _autoHeight: true, annotationOn: this.props.Document
+ useLinkSmallAnchor: true,
+ hideLinkButton: true,
+ audioStart,
+ audioEnd,
+ annotationOn: this.props.Document
});
if (this.dataDoc[this.annotationKey]) {
this.dataDoc[this.annotationKey].push(marker);
@@ -571,13 +574,13 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
<div className="waveform">
{this.waveform}
</div>
- {drawMarkers.map((d, i) => {
+ {drawMarkers.map(d => {
const m = d.marker;
const left = NumCast(m.audioStart) / this.audioDuration * timelineContentWidth;
const top = d.level / maxLevel * timelineContentHeight;
const timespan = m.audioEnd === undefined ? 10 / timelineContentWidth * this.audioDuration : NumCast(m.audioEnd) - NumCast(m.audioStart);
return this.layoutDoc.hideMarkers ? (null) :
- <div className={`audiobox-marker-${this.props.PanelHeight() < 32 ? "mini" : ""}timeline`} key={i}
+ <div className={`audiobox-marker-${this.props.PanelHeight() < 32 ? "mini" : ""}timeline`} key={m[Id]}
style={{ left, top, width: `${timespan / this.audioDuration * 100}%`, height: `${1 / maxLevel * 100}%` }}
onClick={e => { this.playFrom(NumCast(m.audioStart), Cast(m.audioEnd, "number", null)); e.stopPropagation(); }} >
{this.renderMarker(m, this.rangeClickScript, this.rangePlayScript,
diff --git a/src/client/views/nodes/LabelBox.tsx b/src/client/views/nodes/LabelBox.tsx
index 3448a4abd..87d5b07a2 100644
--- a/src/client/views/nodes/LabelBox.tsx
+++ b/src/client/views/nodes/LabelBox.tsx
@@ -67,7 +67,7 @@ export class LabelBox extends ViewBoxBaseComponent<FieldViewProps, LabelDocument
const params = Cast(this.paramsDoc["onClick-paramFieldKeys"], listSpec("string"), []);
const missingParams = params?.filter(p => !this.paramsDoc[p]);
params?.map(p => DocListCast(this.paramsDoc[p])); // bcz: really hacky form of prefetching ...
- const label = StrCast(this.rootDoc[this.fieldKey], StrCast(this.rootDoc.title));
+ const label = typeof this.rootDoc[this.fieldKey] === "string" ? StrCast(this.rootDoc[this.fieldKey]) : StrCast(this.rootDoc.title);
return (
<div className="labelBox-outerDiv"
onClick={action(() => this.clicked = !this.clicked)}
diff --git a/src/client/views/nodes/VideoBox.scss b/src/client/views/nodes/VideoBox.scss
index 76edda847..8bba5d1ff 100644
--- a/src/client/views/nodes/VideoBox.scss
+++ b/src/client/views/nodes/VideoBox.scss
@@ -13,15 +13,13 @@
.audiobox-timeline {
position: absolute;
- height: 20%;
width: 100%;
- bottom: 0px;
- background: white;
+ background: beige;
border: gray solid 1px;
border-radius: 3px;
z-index: 1000;
overflow: hidden;
- left: 0px;
+ bottom: 0;
.audiobox-current {
width: 1px;
@@ -46,18 +44,23 @@
border-width: 1px;
}
- .audiobox-marker-container,
+ .audiobox-marker-timeline,
.audiobox-marker-minicontainer {
position: absolute;
width: 10px;
height: 10px;
top: 2.5%;
- background: gray;
border-radius: 50%;
box-shadow: black 2px 2px 1px;
overflow: visible;
cursor: pointer;
+ .left-resizer {
+ background: dimgrey;
+ }
+ .resizer {
+ background: dimgrey;
+ }
.audiobox-marker {
position: relative;
height: 100%;
@@ -77,10 +80,8 @@
width: 10px;
height: 90%;
top: 2.5%;
- background: gray;
border-radius: 5px;
box-shadow: black 2px 2px 1px;
- opacity: 0.3;
.audiobox-marker {
position: relative;
@@ -94,10 +95,12 @@
.resizer {
position: absolute;
+ top: 0;
right: 0;
+ pointer-events: all;
cursor: ew-resize;
height: 100%;
- width: 2px;
+ width: 10px;
z-index: 100;
}
@@ -111,9 +114,10 @@
.left-resizer {
position: absolute;
left: 0;
+ top: 0;
cursor: ew-resize;
height: 100%;
- width: 2px;
+ width: 10px;
z-index: 100;
}
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 2ac105545..608d7daa3 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -26,10 +26,13 @@ import { List } from "../../../fields/List";
import { DocumentView } from "./DocumentView";
import { LinkDocPreview } from "./LinkDocPreview";
import { FormattedTextBoxComment } from "./formattedText/FormattedTextBoxComment";
-import { Transform } from "../../util/Transform";
import { StyleProp } from "../StyleProvider";
import { computedFn } from "mobx-utils";
import { DocumentManager } from "../../util/DocumentManager";
+import { Dictionary } from "typescript-collections";
+import { MarqueeAnnotator } from "../MarqueeAnnotator";
+import { Id } from "../../../fields/FieldSymbols";
+import { LabelBox } from "./LabelBox";
const path = require('path');
export const timeSchema = createSchema({
@@ -47,16 +50,16 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
static RangePlayScript: ScriptField;
static LabelPlayScript: ScriptField;
static heightPercent = 20; // height of timeline in percent of height of videoBox.
- private _reactionDisposer?: IReactionDisposer;
- private _youtubeReactionDisposer?: IReactionDisposer;
- // private _reactionDisposer?: IReactionDisposer;
- // private _youtubeReactionDisposer?: IReactionDisposer;
private _disposers: { [name: string]: IReactionDisposer } = {};
private _youtubePlayer: YT.Player | undefined = undefined;
private _videoRef: HTMLVideoElement | null = null;
private _youtubeIframeId: number = -1;
private _youtubeContentCreated = false;
private _isResetClick = 0;
+ private _mainCont: React.RefObject<HTMLDivElement> = React.createRef();
+ private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef();
+ @observable _marqueeing: number[] | undefined;
+ @observable _savedAnnotations: Dictionary<number, HTMLDivElement[]> = new Dictionary<number, HTMLDivElement[]>();
_play: any = null;
_timeline: Opt<HTMLDivElement>;
_audioRef = React.createRef<HTMLDivElement>();
@@ -75,7 +78,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
@observable _playing = false;
@observable static _showControls: boolean;
@computed get videoDuration() { return NumCast(this.dataDoc[this.fieldKey + "-duration"]); }
- @computed get markerDocs() { return DocListCast(this.dataDoc[this.annotationKey]); }
+ @computed get markerDocs() { return DocListCast(this.dataDoc[this.annotationKey + "-timeline"]).concat(DocListCast(this.dataDoc[this.annotationKey])); }
public static LayoutString(fieldKey: string) { return FieldView.LayoutString(VideoBox, fieldKey); }
public get player(): HTMLVideoElement | null {
@@ -87,10 +90,10 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
VideoBox.Instance = this;
// onClick play scripts
- VideoBox.RangeScript = VideoBox.RangeScript || ScriptField.MakeScript(`scriptContext.clickMarker(self, this.audioStart, this.audioEnd)`, { self: Doc.name, scriptContext: "any" })!;
- VideoBox.LabelScript = VideoBox.LabelScript || ScriptField.MakeScript(`scriptContext.clickMarker(self, this.audioStart)`, { self: Doc.name, scriptContext: "any" })!;
- VideoBox.RangePlayScript = VideoBox.RangePlayScript || ScriptField.MakeScript(`scriptContext.playOnClick(self, this.audioStart, this.audioEnd)`, { self: Doc.name, scriptContext: "any" })!;
- VideoBox.LabelPlayScript = VideoBox.LabelPlayScript || ScriptField.MakeScript(`scriptContext.playOnClick(self, this.audioStart)`, { self: Doc.name, scriptContext: "any" })!;
+ VideoBox.RangeScript = VideoBox.RangeScript || ScriptField.MakeScript(`scriptContext.clickMarker(self, this.displayTimecode, this.undisplayTimecode)`, { self: Doc.name, scriptContext: "any" })!;
+ VideoBox.LabelScript = VideoBox.LabelScript || ScriptField.MakeScript(`scriptContext.clickMarker(self, this.displayTimecode)`, { self: Doc.name, scriptContext: "any" })!;
+ VideoBox.RangePlayScript = VideoBox.RangePlayScript || ScriptField.MakeScript(`scriptContext.playOnClick(self, this.displayTimecode, this.undisplayTimecode)`, { self: Doc.name, scriptContext: "any" })!;
+ VideoBox.LabelPlayScript = VideoBox.LabelPlayScript || ScriptField.MakeScript(`scriptContext.playOnClick(self, this.displayTimecode)`, { self: Doc.name, scriptContext: "any" })!;
}
videoLoad = () => {
@@ -240,7 +243,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
if (this.props.renderDepth !== -1 && !LinkDocPreview.TargetDoc && !FormattedTextBoxComment.linkDoc) {
const delay = this.player ? 0 : 250; // wait for mainCont and try again to play
setTimeout(() => this.player && this.Play(), delay);
- setTimeout(() => { this.Document._videoStart = undefined; }, 10 + delay);
+ setTimeout(() => this.Document._videoStart = undefined, 10 + delay);
}
}
},
@@ -349,9 +352,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
<source src={field.url.href} type="video/mp4" />
Not supported.
</video>
- {this.uIButtons}
</div>
- {this.renderTimeline}
</div>;
}
@@ -412,9 +413,11 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
</div>,
<div className="timeline-button" key="timeline-button" onPointerDown={this.toggleTimeline} style={{
position: "absolute",
- bottom: "41px",
- right: this.layoutDoc._showTimeline ? "235px" : "155px",
- color: "lightgrey",
+ bottom: 0,
+ right: 0,
+ zIndex: 1001,
+ color: "white",
+ background: "dimgrey",
width: "20px"
}}>
<FontAwesomeIcon icon={this.layoutDoc._showTimeline ? "eye-slash" : "eye"} style={{ width: "100%" }} />
@@ -557,17 +560,19 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
}
@action
- createMarker(audioStart: number, audioEnd?: number) {
+ createMarker(displayTimecode: number, undisplayTimecode?: number) {
const marker = Docs.Create.LabelDocument({
- title: ComputedField.MakeFunction(`formatToTime(self.audioStart) + "-" + formatToTime(self.audioEnd)`) as any, isLabel: audioEnd === undefined,
- useLinkSmallAnchor: true, hideLinkButton: true, audioStart, audioEnd, _showSidebar: false,
- _autoHeight: true, annotationOn: this.props.Document
+ title: ComputedField.MakeFunction(`"#" + formatToTime(self.displayTimecode) + "-" + formatToTime(self.undisplayTimecode)`) as any,
+ useLinkSmallAnchor: true, // bcz: note this also flags that the annotation is not on the video itself, just the timeline
+ hideLinkButton: true,
+ displayTimecode,
+ undisplayTimecode,
+ annotationOn: this.props.Document
});
- marker.data = ""; // clears out the label's text so that only its border will display
- if (this.dataDoc[this.annotationKey]) {
- this.dataDoc[this.annotationKey].push(marker);
+ if (this.dataDoc[this.annotationKey + "-timeline"]) {
+ this.dataDoc[this.annotationKey + "-timeline"].push(marker);
} else {
- this.dataDoc[this.annotationKey] = new List<Doc>([marker]);
+ this.dataDoc[this.annotationKey + "-timeline"] = new List<Doc>([marker]);
}
}
@@ -609,14 +614,14 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
}
// makes sure no markers overlaps each other by setting the correct position and width
- getLevel = (m: any, placed: { audioStart: number, audioEnd: number, level: number }[]) => {
+ getLevel = (m: any, placed: { videoStart: number, videoEnd: number, level: number }[]) => {
const timelineContentWidth = this.props.PanelWidth();
- const x1 = m.audioStart;
- const x2 = m.audioEnd === undefined ? m.audioStart + 10 / timelineContentWidth * this.videoDuration : m.audioEnd;
+ const x1 = m.displayTimecode;
+ const x2 = m.undisplayTimecode === undefined ? m.displayTimecode + 10 / timelineContentWidth * this.videoDuration : m.undisplayTimecode;
let max = 0;
const overlappedLevels = new Set(placed.map(p => {
- const y1 = p.audioStart;
- const y2 = p.audioEnd;
+ const y1 = p.videoStart;
+ const y2 = p.videoEnd;
if ((x1 >= y1 && x1 <= y2) || (x2 >= y1 && x2 <= y2) ||
(y1 >= x1 && y1 <= x2) || (y2 >= x1 && y2 <= x2)) {
max = Math.max(max, p.level);
@@ -626,23 +631,25 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
let level = max + 1;
for (let j = max; j >= 0; j--) !overlappedLevels.has(j) && (level = j);
- placed.push({ audioStart: x1, audioEnd: x2, level });
+ placed.push({ videoStart: x1, videoEnd: x2, level });
return level;
}
// renders the markers as a document
- renderInner = computedFn(function (this: VideoBox, mark: Doc, script: undefined | (() => ScriptField), doublescript: undefined | (() => ScriptField), x: number, y: number, width: number, height: number) {
+ renderInner = computedFn(function (this: VideoBox, mark: Doc, script: undefined | (() => ScriptField), doublescript: undefined | (() => ScriptField), x: number, y: number, width: number, height: number, annotationKey: string) {
const marker = observable({ view: undefined as any });
return {
- marker, view: <DocumentView key="view" {...this.props} ref={action((r: DocumentView | null) => marker.view = r)}
+ marker, view: <DocumentView key="view" {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit} ref={action((r: DocumentView | null) => marker.view = r)}
Document={mark}
+ DataDoc={undefined}
PanelWidth={() => width}
PanelHeight={() => height}
rootSelected={returnFalse}
LayoutTemplate={undefined}
+ LayoutTemplateString={LabelBox.LayoutString("data")}
ContainingCollectionDoc={this.props.Document}
- removeDocument={this.removeDocument}
- ScreenToLocalTransform={() => this.props.ScreenToLocalTransform().translate(-x - 4, -y - 3)}
+ removeDocument={(doc: Doc | Doc[]) => this.removeDocument(doc, annotationKey)}
+ ScreenToLocalTransform={() => this.props.ScreenToLocalTransform().scale(this.props.scaling?.() || 1).translate(-x - 4, -y - 3)}
parentActive={(out) => this.props.isSelected(out) || this._isChildActive}
whenActiveChanged={action((isActive: boolean) => this.props.whenActiveChanged(this._isChildActive = isActive))}
onClick={script}
@@ -653,8 +660,8 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
};
});
- renderMarker = computedFn(function (this: VideoBox, mark: Doc, script: undefined | (() => ScriptField), doublescript: undefined | (() => ScriptField), x: number, y: number, width: number, height: number) {
- const inner = this.renderInner(mark, script, doublescript, x, y, width, height);
+ renderMarker = computedFn(function (this: VideoBox, mark: Doc, script: undefined | (() => ScriptField), doublescript: undefined | (() => ScriptField), x: number, y: number, width: number, height: number, annotationKey: string) {
+ const inner = this.renderInner(mark, script, doublescript, x, y, width, height, annotationKey);
return <>
{inner.view}
{!inner.marker.view || !SelectionManager.IsSelected(inner.marker.view) ? (null) :
@@ -667,32 +674,34 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
// returns the timeline
@computed get renderTimeline() {
- const rect = this._timeline?.getBoundingClientRect();
const timelineContentWidth = this.props.PanelWidth();
- //const timelineContentWidth = this.layoutDoc._showTimeline ? this.props.PanelWidth() * 1.25 : this.props.PanelWidth();
- //const timelineContentWidth = rect ? rect.width : this.props.PanelWidth();
- const timelineContentHeight = (this.props.PanelHeight() * VideoBox.heightPercent / 100); // panelHeight * heightPercent is player height. * heightPercent is timeline height (as per css inline)
- const overlaps: { audioStart: number, audioEnd: number, level: number }[] = [];
- const drawMarkers = this.markerDocs.map((m, i) => ({ level: this.getLevel(m, overlaps), marker: m }));
+ const timelineContentHeight = this.props.PanelHeight() * VideoBox.heightPercent / 100;
+ const overlaps: { videoStart: number, videoEnd: number, level: number }[] = [];
+ const drawMarkers: { level: number, marker: Doc }[] = this.markerDocs.map((m, i) => ({ level: this.getLevel(m, overlaps), marker: m }));
const maxLevel = overlaps.reduce((m, o) => Math.max(m, o.level), 0) + 2;
return !this.layoutDoc._showTimeline ? (null) :
- <div className="audiobox-timeline" ref={this.timelineRef} onClick={e => { e.stopPropagation(); e.preventDefault(); }} style={{ height: `${VideoBox.heightPercent}%` }}
- onPointerDown={e => e.button === 0 && !e.ctrlKey && this.onPointerDownTimeline(e)}>
- {drawMarkers.map((d, i) => {
+ <div className="audiobox-timeline" ref={this.timelineRef} style={{ height: `${VideoBox.heightPercent}%` }}
+ onClick={e => { if (this._isChildActive || this.props.isSelected()) { e.stopPropagation(); e.preventDefault(); } }}
+ onPointerDown={e => {
+ if (this._isChildActive || this.props.isSelected()) {
+ e.button === 0 && !e.ctrlKey && this.onPointerDownTimeline(e);
+ }
+ }}>
+ {drawMarkers.map(d => {
const m = d.marker;
- const left = NumCast(m.audioStart) / this.videoDuration;
- const l = `${NumCast(m.audioStart) / this.videoDuration * 100}%`;
+ const start = NumCast(m.displayTimecode, NumCast(m.displayTimecode, null));
+ const left = start / this.videoDuration * timelineContentWidth;
const top = d.level / maxLevel * timelineContentHeight;
- const timespan = m.audioEnd === undefined ? 10 / timelineContentWidth * this.videoDuration : NumCast(m.audioEnd) - NumCast(m.audioStart);
+ const timespan = m.undisplayTimecode === undefined ? 10 / timelineContentWidth * this.videoDuration : NumCast(m.undisplayTimecode) - NumCast(m.displayTimecode);
return this.layoutDoc.hideMarkers ? (null) :
- <div className={`audiobox-marker-${this.props.PanelHeight() < 32 ? "mini" : ""}timeline`} key={i}
- style={{ left: l, top, width: `${timespan / this.videoDuration * 100}%`, height: `${1 / maxLevel * 100}%` }}
- onClick={e => { this.playFrom(NumCast(m.audioStart), Cast(m.audioEnd, "number", null)); e.stopPropagation(); }} >
+ <div className={`audiobox-marker-${this.props.PanelHeight() < 32 ? "mini" : ""}timeline`} key={m[Id]}
+ style={{ left, top, width: `${timespan / this.videoDuration * 100}%`, height: `${1 / maxLevel * 100}%` }}
+ onClick={e => { this.playFrom(start, Cast(m.undisplayTimecode, "number", null)); e.stopPropagation(); }} >
{this.renderMarker(m, this.rangeClickScript, this.rangePlayScript,
left,
- top,
+ top + (this.props.PanelHeight() - timelineContentHeight),
timelineContentWidth * timespan / this.videoDuration,
- timelineContentHeight / maxLevel)}
+ timelineContentHeight / maxLevel, this.annotationKey + (m.useLinkSmallAnchor ? "-timeline" : ""))}
</div>;
})}
{this.selectionContainer}
@@ -703,13 +712,13 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
// updates the marker with the new time
@action
changeMarker = (m: any, time: any) => {
- DocListCast(this.dataDoc[this.annotationKey]).filter(marker => this.isSame(marker, m)).forEach(marker =>
- this._left ? marker.audioStart = time : marker.audioEnd = time);
+ this.markerDocs.filter(marker => this.isSame(marker, m)).forEach(marker =>
+ this._left ? marker.displayTimecode = time : marker.undisplayTimecode = time);
}
// checks if the two markers are the same with start and end time
isSame = (m1: any, m2: any) => {
- return m1.audioStart === m2.audioStart && m1.audioEnd === m2.audioEnd;
+ return m1.displayTimecode === m2.displayTimecode && m1.undisplayTimecode === m2.undisplayTimecode;
}
// returns the blue container when dragging
@@ -753,17 +762,17 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
contentFunc = () => [this.youtubeVideoId ? this.youtubeContent : this.content];
@computed get annotationLayer() {
- return <div className="imageBox-annotationLayer" style={{ height: Doc.NativeHeight(this.Document) || undefined }} ref={this._annotationLayer} />;
+ return <div className="imageBox-annotationLayer" style={{ height: "100%" }} ref={this._annotationLayer} />;
}
marqueeDown = action((e: React.PointerEvent) => {
if (!e.altKey && e.button === 0 && this.active(true)) this._marqueeing = [e.clientX, e.clientY];
- })
+ });
finishMarquee = action(() => {
this._marqueeing = undefined;
this.props.select(true);
- })
+ });
render() {
const borderRad = this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BorderRounding);
@@ -781,7 +790,9 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
select={emptyFunction}
active={this.annotationsActive}
scaling={returnOne}
- ScreenToLocalTransform={this.screenToLocalTransform}
+ PanelWidth={() => this.props.PanelWidth() * (this.layoutDoc._showTimeline ? .8 : 1)}
+ PanelHeight={() => this.props.PanelHeight() * (this.layoutDoc._showTimeline ? .8 : 1)}
+ ScreenToLocalTransform={() => this.screenToLocalTransform().scale(this.layoutDoc._showTimeline ? 1 / .8 : 1)}
whenActiveChanged={this.whenActiveChanged}
removeDocument={this.removeDocument}
moveDocument={this.moveDocument}
@@ -790,14 +801,12 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
renderDepth={this.props.renderDepth + 1}>
{this.contentFunc}
</CollectionFreeFormView>
+ {this.uIButtons}
+ {this.annotationLayer}
+ {this.renderTimeline}
+ {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? (null) :
+ <MarqueeAnnotator rootDoc={this.rootDoc} down={this._marqueeing} scaling={this.props.scaling} addDocument={this.addDocumentWithTimestamp} finishMarquee={this.finishMarquee} savedAnnotations={this._savedAnnotations} annotationLayer={this._annotationLayer.current} mainCont={this._mainCont.current} />}
</div>
-<<<<<<< HEAD
-=======
- {this.uIButtons}
- {this.annotationLayer}
- {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? (null) :
- <MarqueeAnnotator rootDoc={this.rootDoc} down={this._marqueeing} scaling={this.props.scaling} addDocument={this.addDocumentWithTimestamp} finishMarquee={this.finishMarquee} savedAnnotations={this._savedAnnotations} annotationLayer={this._annotationLayer.current} mainCont={this._mainCont.current} />}
->>>>>>> 10b759d2bd09af3a8e8a4effbc8fd2312dd873d2
</div >);
}
}
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 69f797880..37f268823 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -194,7 +194,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
this._iframe?.removeEventListener('wheel', this.iframeWheel);
}
- onUrlDragover = (e: React.DragEvent) => { e.preventDefault(); }
+ onUrlDragover = (e: React.DragEvent) => { e.preventDefault(); };
@undoBatch
@action
@@ -280,8 +280,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
}
_ignore = 0;
- onPreWheel = (e: React.WheelEvent) => { this._ignore = e.timeStamp; }
- onPrePointer = (e: React.PointerEvent) => { this._ignore = e.timeStamp; }
+ onPreWheel = (e: React.WheelEvent) => { this._ignore = e.timeStamp; };
+ onPrePointer = (e: React.PointerEvent) => { this._ignore = e.timeStamp; };
onPostPointer = (e: React.PointerEvent) => {
if (this._ignore !== e.timeStamp) e.stopPropagation();
}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index ac5ea66ff..7348ebdd2 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -230,7 +230,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
const target = CurrentUserUtils.GetNewTextDoc("Note linked to " + this.rootDoc.title, 0, 0, 100, 100);
FormattedTextBox.SelectOnLoad = target[Id];
return target;
- }
+ };
DragManager.StartAnchorAnnoDrag([ele], new DragManager.AnchorAnnoDragData(this.rootDoc, () => this.rootDoc, targetCreator), e.pageX, e.pageY, {
dragComplete: e => {
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index 992194e2b..dc630af74 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -604,7 +604,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
e.stopPropagation();
self.TextView.endUndoTypingBatch();
UndoManager.RunInBatch(() => self.view && self.fillBrush(self.view.state, self.view.dispatch), "rt brush");
- }
+ };
let label = "Stored marks: ";
if (this.brushMarks && this.brushMarks.size > 0) {