From d0025200b23f57ec25d3452d425de8725a032c6a Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 22 Jan 2021 21:14:37 -0500
Subject: fixed selection bounds for video box annotations, especially when in
 full screen view.

---
 src/client/views/collections/TabDocView.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

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

diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 5ca069fb9..e5f05c407 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -358,7 +358,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
     }
     active = () => this._isActive;
     ScreenToLocalTransform = () => {
-        const { translateX, translateY } = Utils.GetScreenTransform(this._mainCont?.children?.[0]?.firstChild as HTMLElement);
+        const { translateX, translateY } = Utils.GetScreenTransform(this._mainCont?.children?.[0] as HTMLElement);
         return CollectionDockingView.Instance?.props.ScreenToLocalTransform().translate(-translateX, -translateY);
     }
     PanelWidth = () => this._panelWidth;
-- 
cgit v1.2.3-70-g09d2


From 20add09510fb02b144d421910a56d3f3896b1f90 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 26 Jan 2021 01:38:55 -0500
Subject: preparing to unify VideoBox timeline with AudioBox timeline.  changed
 names from videoStart/audioStart,End to anchorStart,EndTime and
 _displayTimecode to _timecodeToShow etc

---
 src/client/documents/Documents.ts                  |  14 +-
 src/client/util/DocumentManager.ts                 |   4 +-
 src/client/views/DocComponent.tsx                  |   3 +-
 src/client/views/DocumentDecorations.tsx           |   2 +-
 src/client/views/StyleProvider.tsx                 |   4 +-
 src/client/views/collections/CollectionView.tsx    |   4 +-
 src/client/views/collections/TabDocView.tsx        |   5 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |   8 +-
 .../collections/collectionFreeForm/MarqueeView.tsx |   3 +-
 src/client/views/nodes/AudioBox.scss               |   3 +-
 src/client/views/nodes/AudioBox.tsx                | 217 +++++++++++----------
 src/client/views/nodes/PresBox.tsx                 |  23 +--
 src/client/views/nodes/VideoBox.tsx                | 207 ++++++++++----------
 .../views/nodes/formattedText/FormattedTextBox.tsx |   2 +-
 src/fields/documentSchemas.ts                      |   6 +-
 15 files changed, 270 insertions(+), 235 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 286b7afa9..080270321 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -151,8 +151,10 @@ export interface DocumentOptions {
     _curPage?: number;
     _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
+    _timecodeToShow?: number; // the time that a document should be displayed (e.g., when an annotation shows up as a video plays)
+    _timecodeToHide?: number; // the time that a document should be hidden
+    anchorStartTime?: number; // the time when an annotation starts on a timeline (e.g., when an audio anchor starts on an audio/video timeline)
+    anchorEndTime?: number;   // the time when an annotaiton ends on a timeline 
     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,8 +229,6 @@ export interface DocumentOptions {
     linearViewIsExpanded?: boolean; // is linear view expanded
     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   
     border?: string; //for searchbox
     hovercolor?: string;
 }
@@ -722,8 +722,8 @@ export namespace Docs {
             linkDocProto.treeViewOpen = true;// setting this in the instance creator would set it on the view document. 
             linkDocProto.anchor1 = source.doc;
             linkDocProto.anchor2 = target.doc;
-            linkDocProto.anchor1_timecode = source.doc._currentTimecode || source.doc.displayTimecode;
-            linkDocProto.anchor2_timecode = target.doc._currentTimecode || target.doc.displayTimecode;
+            linkDocProto.anchor1_timecode = source.doc._currentTimecode || source.doc._timecodeToShow;
+            linkDocProto.anchor2_timecode = target.doc._currentTimecode || target.doc._timecodeToShow;
 
             if (linkDocProto.linkBoxExcludedKeys === undefined) {
                 Cast(linkDocProto.proto, Doc, null).linkBoxExcludedKeys = new List(["treeViewExpandedView", "aliases", "treeViewHideTitle", "removeDropProperties", "linkBoxExcludedKeys", "treeViewOpen", "aliasNumber", "isPrototype", "creationDate", "author"]);
@@ -1252,7 +1252,7 @@ export namespace DocUtils {
             docList.forEach((d, i) => {
                 d.x = Math.cos(Math.PI * 2 * i / docList.length) * 10 - w / 2;
                 d.y = Math.sin(Math.PI * 2 * i / docList.length) * 10 - h / 2;
-                d.displayTimecode = undefined;  // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
+                d._timecodeToShow = undefined;  // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
             });
         });
         if (x !== undefined && y !== undefined) {
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 1f2dd350b..41c7a1409 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -191,8 +191,8 @@ export class DocumentManager {
                     targetDocContextView.props.focus(targetDocContextView.props.Document, willZoom);
 
                     // now find the target document within the context
-                    if (targetDoc.displayTimecode) {  // if the target has a timecode, it should show up once the (presumed) video context scrubs to the display timecode;
-                        targetDocContext._currentTimecode = targetDoc.displayTimecode;
+                    if (targetDoc._timecodeToShow) {  // if the target has a timecode, it should show up once the (presumed) video context scrubs to the display timecode;
+                        targetDocContext._currentTimecode = targetDoc.anchorTimecodeToShow;
                         finished?.();
                     } else { // no timecode means we need to find the context view and focus on our target
                         const findView = (delay: number) => {
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index db0b626a1..8d545c61b 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -81,6 +81,8 @@ export interface ViewBoxAnnotatableProps {
 }
 export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps, T>(schemaCtor: (doc: Doc) => T) {
     class Component extends Touchable<P> {
+        _annotationKey: string = "annotations";
+
         @observable _isChildActive = false;
         //TODO This might be pretty inefficient if doc isn't observed, because computed doesn't cache then
         @computed get Document(): T { return schemaCtor(this.props.Document); }
@@ -120,7 +122,6 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps, T
 
         protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer;
 
-        _annotationKey: string = "annotations";
         public get annotationKey() { return this.fieldKey + "-" + this._annotationKey; }
 
         @action.bound
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index e1b9fe8aa..fb85684fb 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -613,7 +613,7 @@ export class DocumentDecorations extends React.Component<{ boundsLeft: number, b
                 }}>
                     {closeIcon}
                     {bounds.r - bounds.x < 100 ? null : titleArea}
-                    {(seldoc.rootDoc.annotationOn as Doc)?.type === DocumentType.AUDIO ? (null) :
+                    {seldoc.rootDoc.anchorStartTime !== undefined ? (null) :
                         <>
                             {SelectionManager.Views().length !== 1 || seldoc.Document.type === DocumentType.INK ? (null) :
                                 <Tooltip key="i" title={<div className="dash-tooltip">{`${seldoc.finalLayoutKey.includes("icon") ? "De" : ""}Iconify Document`}</div>} placement="top">
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index 95a28b33e..8628be014 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -108,7 +108,7 @@ 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 || doc?.displayTimecode !== undefined ? "rgba(128, 128, 128, 0.18)" : undefined); break;
+                case DocumentType.LABEL: docColor = docColor || (doc.audioStart !== undefined || doc?.anchorStartTime !== undefined || doc?._timecodeToShow !== 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;
@@ -141,7 +141,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps |
                             `${darkScheme() ? "rgb(30, 32, 31) " : "#9c9396 "} ${StrCast(doc.boxShadow, "0.2vw 0.2vw 0.8vw")}`);
 
                 case DocumentType.LABEL:
-                    if (doc?.audioStart !== undefined) return "black 2px 2px 1px";
+                    if (doc?.anchorStartTime !== undefined) return "black 2px 2px 1px";
                 default:
                     return doc.z ? `#9c9396  ${StrCast(doc?.boxShadow, "10px 10px 0.9vw")}` :  // if it's a floating doc, give it a big shadow
                         props?.ContainingCollectionDoc?._useClusters && doc.type !== DocumentType.INK ? (`${backgroundCol()} ${StrCast(doc.boxShadow, `0vw 0vw ${(isBackground() ? 100 : 50) / (docProps?.ContentScaling?.() || 1)}px`)}`) :  // if it's just in a cluster, make the shadown roughly match the cluster border extent
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 83c639871..6b9b1a3c0 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -158,14 +158,14 @@ export class CollectionView extends Touchable<CollectionViewProps> {
                             const pushpin = Docs.Create.FontIconDocument({
                                 title: "pushpin", label: "",
                                 icon: "map-pin", x: Cast(doc.x, "number", null), y: Cast(doc.y, "number", null), _backgroundColor: "#0000003d", color: "#ACCEF7",
-                                _width: 15, _height: 15, _xPadding: 0, isLinkButton: true, displayTimecode: Cast(doc.displayTimecode, "number", null)
+                                _width: 15, _height: 15, _xPadding: 0, isLinkButton: true, _timecodeToShow: Cast(doc._timecodeToShow, "number", null)
                             });
                             pushpin.isPushpin = true;
                             Doc.GetProto(pushpin).annotationOn = doc.annotationOn;
                             Doc.SetInPlace(doc, "annotationOn", undefined, true);
                             Doc.AddDocToList(context, Doc.LayoutFieldKey(context) + "-annotations", pushpin);
                             const pushpinLink = DocUtils.MakeLink({ doc: pushpin }, { doc: doc }, "pushpin", "");
-                            doc.displayTimecode = undefined;
+                            doc._timecodeToShow = undefined;
                         }
                         doc._stayInCollection = undefined;
                         doc.context = this.props.Document;
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index e5f05c407..c66734556 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -182,12 +182,13 @@ export class TabDocView extends React.Component<TabDocViewProps> {
             const presArray: Doc[] = PresBox.Instance?.sortArray();
             const size: number = PresBox.Instance?._selectedArray.size;
             const presSelected: Doc | undefined = presArray && size ? presArray[size - 1] : undefined;
+            const duration = NumCast(doc[`${Doc.LayoutFieldKey(pinDoc)}-duration`], null);
             Doc.AddDocToList(curPres, "data", pinDoc, presSelected);
-            if (!pinProps?.audioRange && (pinDoc.type === DocumentType.AUDIO || pinDoc.type === DocumentType.VID)) {
+            if (!pinProps?.audioRange && duration !== undefined) {
                 pinDoc.mediaStart = "manual";
                 pinDoc.mediaStop = "manual";
                 pinDoc.presStartTime = 0;
-                pinDoc.presEndTime = pinDoc.type === DocumentType.AUDIO ? doc.duration : NumCast(doc["data-duration"]);
+                pinDoc.presEndTime = duration;
             }
             //save position
             if (pinProps?.setPosition || pinDoc.isInkMask) {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 013472a04..6619205af 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -54,7 +54,7 @@ export const panZoomSchema = createSchema({
     _panX: "number",
     _panY: "number",
     _currentTimecode: "number",
-    displayTimecode: "number",
+    _timecodeToShow: "number",
     _currentFrame: "number",
     arrangeInit: ScriptField,
     _useClusters: "boolean",
@@ -197,10 +197,10 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         docs.map(doc => DocumentManager.Instance.getDocumentView(doc, this.props.CollectionView)).map(dv => dv && SelectionManager.SelectView(dv, true));
     }
     public isCurrent(doc: Doc) {
-        const dispTime = NumCast(doc.displayTimecode, -1);
-        const endTime = NumCast(doc.undisplayTimecode, dispTime + 1.5);
+        const dispTime = NumCast(doc._timecodeToShow, -1);
+        const endTime = NumCast(doc._timecodeToHide, dispTime + 1.5);
         const curTime = NumCast(this.Document._currentTimecode, -1);
-        return dispTime === -1 || ((curTime - dispTime) >= -0.1 && curTime <= endTime);
+        return dispTime === -1 || ((curTime - dispTime) >= -1e-4 && curTime <= endTime);
     }
 
     public getActiveDocuments = () => {
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 9ef37ecc2..0edbfe7a5 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -426,7 +426,8 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
                 delete d.x;
                 delete d.y;
                 delete d.activeFrame;
-                delete d.displayTimecode;  // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
+                delete d._timecodeToShow;  // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
+                delete d._timecodeToHide;  // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
                 d.x = dx - this.Bounds.left - this.Bounds.width / 2;
                 d.y = dy - this.Bounds.top - this.Bounds.height / 2;
                 return d;
diff --git a/src/client/views/nodes/AudioBox.scss b/src/client/views/nodes/AudioBox.scss
index 93cdbbb09..4a1cee721 100644
--- a/src/client/views/nodes/AudioBox.scss
+++ b/src/client/views/nodes/AudioBox.scss
@@ -159,9 +159,8 @@
             }
 
             .audiobox-timeline {
-                position: relative;
+                position: absolute;
                 width: 100%;
-                background: white;
                 border: gray solid 1px;
                 border-radius: 3px;
                 z-index: 1000;
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index a481317c3..0faac44a7 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -13,11 +13,10 @@ import { createSchema, listSpec, makeInterface } from "../../../fields/Schema";
 import { ComputedField, ScriptField } from "../../../fields/ScriptField";
 import { Cast, NumCast } from "../../../fields/Types";
 import { AudioField, nullAudio } from "../../../fields/URLField";
-import { emptyFunction, formatTime, numberRange, returnFalse, setupMoveUpEvents, Utils } from "../../../Utils";
+import { emptyFunction, formatTime, numberRange, returnFalse, setupMoveUpEvents, Utils, OmitKeys } from "../../../Utils";
 import { Docs, DocUtils } from "../../documents/Documents";
 import { Networking } from "../../Network";
 import { CurrentUserUtils } from "../../util/CurrentUserUtils";
-import { DocumentManager } from "../../util/DocumentManager";
 import { Scripting } from "../../util/Scripting";
 import { SelectionManager } from "../../util/SelectionManager";
 import { SnappingManager } from "../../util/SnappingManager";
@@ -30,6 +29,7 @@ import { FormattedTextBoxComment } from "./formattedText/FormattedTextBoxComment
 import { LinkDocPreview } from "./LinkDocPreview";
 import "./AudioBox.scss";
 import { Id } from "../../../fields/FieldSymbols";
+import { LabelBox } from "./LabelBox";
 
 declare class MediaRecorder {
     // whatever MediaRecorder has
@@ -69,7 +69,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
     _audioRef = React.createRef<HTMLDivElement>();
     _timeline: Opt<HTMLDivElement>;
     _markerStart: number = 0;
-    _currMarker: any;
+    _currAnchor: Opt<Doc>;
 
     @observable static SelectingRegion: AudioBox | undefined = undefined;
     @observable static _scrubTime = 0;
@@ -81,8 +81,8 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
     set audioState(value) { this.dataDoc.audioState = value; }
     public static SetScrubTime = action((timeInMillisFrom1970: number) => { AudioBox._scrubTime = 0; AudioBox._scrubTime = timeInMillisFrom1970; });
     @computed get recordingStart() { return Cast(this.dataDoc[this.props.fieldKey + "-recordingStart"], DateField)?.date.getTime(); }
-    @computed get audioDuration() { return NumCast(this.dataDoc.duration); }
-    @computed get markerDocs() { return DocListCast(this.dataDoc[this.annotationKey]); }
+    @computed get duration() { return NumCast(this.dataDoc[`${this.fieldKey}-duration`]); }
+    @computed get anchorDocs() { return DocListCast(this.dataDoc[this.annotationKey]); }
     @computed get links() { return DocListCast(this.dataDoc.links); }
     @computed get pauseTime() { return this._pauseEnd - this._pauseStart; } // total time paused to update the correct time
 
@@ -90,17 +90,26 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
         super(props);
         AudioBox.Instance = this;
 
+        if (this.duration === undefined) {
+            runInAction(() => {
+                this.Document[this.fieldKey + "-duration"] = this.Document.duration;
+            })
+        }
+
         // onClick play scripts
-        AudioBox.RangeScript = AudioBox.RangeScript || ScriptField.MakeFunction(`scriptContext.clickMarker(self, this.audioStart, this.audioEnd)`, { self: Doc.name, scriptContext: "any" })!;
-        AudioBox.LabelScript = AudioBox.LabelScript || ScriptField.MakeFunction(`scriptContext.clickMarker(self, this.audioStart)`, { self: Doc.name, scriptContext: "any" })!;
-        AudioBox.RangePlayScript = AudioBox.RangePlayScript || ScriptField.MakeFunction(`scriptContext.playOnClick(self, this.audioStart, this.audioEnd)`, { self: Doc.name, scriptContext: "any" })!;
-        AudioBox.LabelPlayScript = AudioBox.LabelPlayScript || ScriptField.MakeFunction(`scriptContext.playOnClick(self, this.audioStart)`, { self: Doc.name, scriptContext: "any" })!;
+        AudioBox.RangeScript = AudioBox.RangeScript || ScriptField.MakeFunction(`scriptContext.clickAnchor(this)`, { self: Doc.name, scriptContext: "any" })!;
+        AudioBox.LabelScript = AudioBox.LabelScript || ScriptField.MakeFunction(`scriptContext.clickAnchor(this)`, { self: Doc.name, scriptContext: "any" })!;
+        AudioBox.RangePlayScript = AudioBox.RangePlayScript || ScriptField.MakeFunction(`scriptContext.playOnClick(this)`, { self: Doc.name, scriptContext: "any" })!;
+        AudioBox.LabelPlayScript = AudioBox.LabelPlayScript || ScriptField.MakeFunction(`scriptContext.playOnClick(this)`, { self: Doc.name, scriptContext: "any" })!;
     }
 
+    anchorStart = (anchor: Doc) => NumCast(anchor.anchorStartTime, NumCast(anchor.audioStart))
+    anchorEnd = (anchor: Doc, defaultVal: any = null) => NumCast(anchor.anchorEndTime, NumCast(anchor.audioEnd, defaultVal))
+
     getLinkData(l: Doc) {
         let la1 = l.anchor1 as Doc;
         let la2 = l.anchor2 as Doc;
-        const linkTime = NumCast(la2.audioStart, NumCast(la1.audioStart));
+        const linkTime = NumCast(la2.anchorStartTime, NumCast(la1.anchorStartTime));
         if (Doc.AreProtosEqual(la1, this.dataDoc)) {
             la1 = l.anchor2 as Doc;
             la2 = l.anchor1 as Doc;
@@ -109,7 +118,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
     }
 
     getAnchor = () => {
-        return this.createMarker(this._ele?.currentTime || Cast(this.props.Document._currentTimecode, "number", null) || (this.audioState === "recording" ? (Date.now() - (this.recordingStart || 0)) / 1000 : undefined));
+        return this.createAnchor(this._ele?.currentTime || Cast(this.props.Document._currentTimecode, "number", null) || (this.audioState === "recording" ? (Date.now() - (this.recordingStart || 0)) / 1000 : undefined));
     }
 
     componentWillUnmount() {
@@ -126,13 +135,13 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
 
         this._disposers.scrubbing = reaction(() => AudioBox._scrubTime, (time) => this.layoutDoc.playOnSelect && this.playFromTime(AudioBox._scrubTime));
 
-        this._disposers.audioStart = reaction(
-            () => !LinkDocPreview.TargetDoc && !FormattedTextBoxComment.linkDoc && this.props.renderDepth !== -1 ? Cast(this.Document._audioStart, "number", null) : undefined,
-            audioStart => audioStart !== undefined && setTimeout(() => {
-                this._audioRef.current && this.playFrom(audioStart);
+        this._disposers.triggerAudio = reaction(
+            () => !LinkDocPreview.TargetDoc && !FormattedTextBoxComment.linkDoc && this.props.renderDepth !== -1 ? NumCast(this.Document._triggerAudio, null) : undefined,
+            start => start !== undefined && setTimeout(() => {
+                this._audioRef.current && this.playFrom(start);
                 setTimeout(() => {
-                    this.Document._currentTimecode = audioStart;
-                    this.Document._audioStart = undefined;
+                    this.Document._currentTimecode = start;
+                    this.Document._triggerAudio = undefined;
                 }, 10);
             }, this._audioRef.current ? 0 : 250), // wait for mainCont and try again to play
             { fireImmediately: true }
@@ -148,23 +157,11 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
         );
     }
 
-    playLink = (doc: Doc) => {
-        this.links.filter(l => l.anchor1 === doc || l.anchor2 === doc).forEach(l => {
-            const { la1, la2 } = this.getLinkData(l);
-            const startTime = NumCast(la1.audioStart, NumCast(la2.audioStart, null));
-            const endTime = NumCast(la1.audioEnd, NumCast(la2.audioEnd, null));
-            if (startTime !== undefined) {
-                this.layoutDoc.playOnSelect && (endTime ? this.playFrom(startTime, endTime) : this.playFrom(startTime));
-            }
-        });
-        doc.annotationOn === this.rootDoc && this.playFrom(NumCast(doc.audioStart), Cast(doc.audioEnd, "number", null));
-    }
-
     // for updating the timecode
     timecodeChanged = () => {
         const htmlEle = this._ele;
         if (this.audioState !== "recording" && htmlEle) {
-            htmlEle.duration && htmlEle.duration !== Infinity && runInAction(() => this.dataDoc.duration = htmlEle.duration);
+            htmlEle.duration && htmlEle.duration !== Infinity && runInAction(() => this.dataDoc[this.fieldKey + "-duration"] = htmlEle.duration);
             this.links.map(l => {
                 const { la1, linkTime } = this.getLinkData(l);
                 if (linkTime > NumCast(this.layoutDoc._currentTimecode) && linkTime < htmlEle.currentTime) {
@@ -188,21 +185,21 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
 
     // play back the audio from time
     @action
-    playOnClick = (anchorDoc: Doc, seekTimeInSeconds: number, endTime: number = this.audioDuration) => {
-        this.playFrom(seekTimeInSeconds, endTime);
+    playOnClick = (anchorDoc: Doc) => {
+        this.playFrom(this.anchorStart(anchorDoc), this.anchorEnd(anchorDoc, this.duration));
         return true;
     }
 
     // play back the audio from time
     @action
-    clickMarker = (anchorDoc: Doc, seekTimeInSeconds: number, endTime: number = this.audioDuration) => {
-        if (this.layoutDoc.autoPlay) return this.playOnClick(anchorDoc, seekTimeInSeconds, endTime);
-        this._ele && (this._ele.currentTime = this.layoutDoc._currentTimecode = seekTimeInSeconds);
+    clickAnchor = (anchorDoc: Doc) => {
+        if (this.layoutDoc.autoPlay) return this.playOnClick(anchorDoc);
+        this._ele && (this._ele.currentTime = this.layoutDoc._currentTimecode = this.anchorStart(anchorDoc));
         return true;
     }
     // play back the audio from time
     @action
-    playFrom = (seekTimeInSeconds: number, endTime: number = this.audioDuration) => {
+    playFrom = (seekTimeInSeconds: number, endTime: number = this.duration) => {
         clearTimeout(this._play);
         if (Number.isNaN(this._ele?.duration)) {
             setTimeout(() => this.playFrom(seekTimeInSeconds, endTime), 500);
@@ -217,7 +214,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
                 this._ele.currentTime = seekTimeInSeconds;
                 this._ele.play();
                 runInAction(() => this.audioState = "playing");
-                if (endTime !== this.audioDuration) {
+                if (endTime !== this.duration) {
                     this._play = setTimeout(() => this.pause(), (endTime - seekTimeInSeconds) * 1000); // use setTimeout to play a specific duration
                 }
             } else {
@@ -260,9 +257,9 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
     // context menu
     specificContextMenu = (e: React.MouseEvent): void => {
         const funcs: ContextMenuProps[] = [];
+        funcs.push({ description: (this.layoutDoc.hideAnchors ? "Don't hide" : "Hide") + " anchors", event: () => this.layoutDoc.hideAnchors = !this.layoutDoc.hideAnchors, icon: "expand-arrows-alt" });
         funcs.push({ description: (this.layoutDoc.playOnSelect ? "Don't play" : "Play") + " when link is selected", event: () => this.layoutDoc.playOnSelect = !this.layoutDoc.playOnSelect, icon: "expand-arrows-alt" });
-        funcs.push({ description: (this.layoutDoc.hideMarkers ? "Don't hide" : "Hide") + " range markers", event: () => this.layoutDoc.hideMarkers = !this.layoutDoc.hideMarkers, icon: "expand-arrows-alt" });
-        funcs.push({ description: (this.layoutDoc.autoPlay ? "Don't auto play" : "Auto play") + " markers onClick", event: () => this.layoutDoc.autoPlay = !this.layoutDoc.autoPlay, icon: "expand-arrows-alt" });
+        funcs.push({ description: (this.layoutDoc.autoPlay ? "Don't auto play" : "Auto play") + " anchors onClick", event: () => this.layoutDoc.autoPlay = !this.layoutDoc.autoPlay, icon: "expand-arrows-alt" });
         ContextMenu.Instance?.addItem({ description: "Options...", subitems: funcs, icon: "asterisk" });
     }
 
@@ -270,7 +267,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
     stopRecording = action(() => {
         this._recorder.stop();
         this._recorder = undefined;
-        this.dataDoc.duration = (new Date().getTime() - this._recordStart - this.pauseTime) / 1000;
+        this.dataDoc[this.fieldKey + "-duration"] = (new Date().getTime() - this._recordStart - this.pauseTime) / 1000;
         this.audioState = "paused";
         this._stream?.getAudioTracks()[0].stop();
         const ind = DocUtils.ActiveRecordings.indexOf(this);
@@ -348,16 +345,16 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
         e.stopPropagation();
     }
 
-    // starting the drag event for marker resizing
+    // starting the drag event for anchor resizing
     @action
     onPointerDownTimeline = (e: React.PointerEvent): void => {
         const rect = this._timeline?.getBoundingClientRect();// (e.target as any).getBoundingClientRect();
         if (rect && e.target !== this._audioRef.current && this.active()) {
             const wasPaused = this.audioState === "paused";
-            this._ele!.currentTime = this.layoutDoc._currentTimecode = (e.clientX - rect.x) / rect.width * this.audioDuration;
+            this._ele!.currentTime = this.layoutDoc._currentTimecode = (e.clientX - rect.x) / rect.width * this.duration;
             wasPaused && this.pause();
 
-            const toTimeline = (screen_delta: number) => screen_delta / rect.width * this.audioDuration;
+            const toTimeline = (screen_delta: number) => screen_delta / rect.width * this.duration;
             this._markerStart = this._markerEnd = toTimeline(e.clientX - rect.x);
             AudioBox.SelectingRegion = this;
             setupMoveUpEvents(this, e,
@@ -372,46 +369,46 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
                         this._markerStart = this._markerEnd;
                         this._markerEnd = tmp;
                     }
-                    AudioBox.SelectingRegion === this && (Math.abs(movement[0]) > 15) && this.createMarker(this._markerStart, this._markerEnd);
+                    AudioBox.SelectingRegion === this && (Math.abs(movement[0]) > 15) && this.createAnchor(this._markerStart, this._markerEnd);
                     AudioBox.SelectingRegion = undefined;
                 }),
                 e => {
                     this.props.select(false);
-                    e.shiftKey && this.createMarker(this._ele!.currentTime);
+                    e.shiftKey && this.createAnchor(this._ele!.currentTime);
                 }
                 , this.props.isSelected(true) || this._isChildActive);
         }
     }
 
     @action
-    createMarker(audioStart?: number, audioEnd?: number) {
-        if (audioStart === undefined) return this.rootDoc;
-        const marker = Docs.Create.LabelDocument({
-            title: ComputedField.MakeFunction(`"#" + formatToTime(self.audioStart) + "-" + formatToTime(self.audioEnd)`) as any,
+    createAnchor(anchorStartTime?: number, anchorEndTime?: number) {
+        if (anchorStartTime === undefined) return this.rootDoc;
+        const anchor = Docs.Create.LabelDocument({
+            title: ComputedField.MakeFunction(`"#" + formatToTime(self.anchorStartTime) + "-" + formatToTime(self.anchorEndTime)`) as any,
             useLinkSmallAnchor: true,
             hideLinkButton: true,
-            audioStart,
-            audioEnd,
+            anchorStartTime,
+            anchorEndTime,
             annotationOn: this.props.Document
         });
         if (this.dataDoc[this.annotationKey]) {
-            this.dataDoc[this.annotationKey].push(marker);
+            this.dataDoc[this.annotationKey].push(anchor);
         } else {
-            this.dataDoc[this.annotationKey] = new List<Doc>([marker]);
+            this.dataDoc[this.annotationKey] = new List<Doc>([anchor]);
         }
-        return marker;
+        return anchor;
     }
 
-    // starting the drag event for marker resizing
-    onPointerDown = (e: React.PointerEvent, m: any, left: boolean): void => {
-        this._currMarker = m;
+    // starting the drag event for anchor resizing
+    onPointerDown = (e: React.PointerEvent, m: Doc, left: boolean): void => {
+        this._currAnchor = m;
         this._left = left;
         this._timeline?.setPointerCapture(e.pointerId);
-        const toTimeline = (screen_delta: number, width: number) => Math.max(0, Math.min(this.audioDuration, screen_delta / width * this.audioDuration));
+        const toTimeline = (screen_delta: number, width: number) => Math.max(0, Math.min(this.duration, screen_delta / width * this.duration));
         setupMoveUpEvents(this, e,
             (e) => {
                 const rect = (e.target as any).getBoundingClientRect();
-                this.changeMarker(this._currMarker, toTimeline(e.clientX - rect.x, rect.width));
+                this.changeAnchor(this._currAnchor, toTimeline(e.clientX - rect.x, rect.width));
                 return false;
             },
             (e) => {
@@ -422,27 +419,26 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
             emptyFunction);
     }
 
-    // updates the marker with the new time
+    // updates the anchor with the new time
     @action
-    changeMarker = (m: any, time: any) => {
-        this.markerDocs.filter(marker => this.isSame(marker, m)).forEach(marker =>
-            this._left ? marker.audioStart = time : marker.audioEnd = time);
+    changeAnchor = (anchor: Opt<Doc>, time: number) => {
+        anchor && (this._left ? anchor.anchorStartTime = time : anchor.anchorEndTime = time);
     }
 
-    // checks if the two markers are the same with start and end time
+    // checks if the two anchors are the same with start and end time
     isSame = (m1: any, m2: any) => {
-        return m1.audioStart === m2.audioStart && m1.audioEnd === m2.audioEnd;
+        return this.anchorStart(m1) === this.anchorStart(m2) && this.anchorEnd(m1) === this.anchorEnd(m2);
     }
 
-    // makes sure no markers overlaps each other by setting the correct position and width
-    getLevel = (m: any, placed: { audioStart: number, audioEnd: number, level: number }[]) => {
+    // makes sure no anchors overlaps each other by setting the correct position and width
+    getLevel = (m: Doc, placed: { anchorStartTime: number, anchorEndTime: number, level: number }[]) => {
         const timelineContentWidth = this.props.PanelWidth() - AudioBox.playheadWidth;
-        const x1 = m.audioStart;
-        const x2 = m.audioEnd === undefined ? m.audioStart + 10 / timelineContentWidth * this.audioDuration : m.audioEnd;
+        const x1 = this.anchorStart(m);
+        const x2 = this.anchorEnd(m, x1 + 10 / timelineContentWidth * this.duration);
         let max = 0;
         const overlappedLevels = new Set(placed.map(p => {
-            const y1 = p.audioStart;
-            const y2 = p.audioEnd;
+            const y1 = p.anchorStartTime;
+            const y2 = p.anchorEndTime;
             if ((x1 >= y1 && x1 <= y2) || (x2 >= y1 && x2 <= y2) ||
                 (y1 >= x1 && y1 <= x2) || (y2 >= x1 && y2 <= x2)) {
                 max = Math.max(max, p.level);
@@ -452,14 +448,14 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
         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({ anchorStartTime: x1, anchorEndTime: x2, level });
         return level;
     }
 
     @computed get selectionContainer() {
         return AudioBox.SelectingRegion !== this ? (null) : <div className="audiobox-container" style={{
-            left: `${Math.min(NumCast(this._markerStart), NumCast(this._markerEnd)) / this.audioDuration * 100}%`,
-            width: `${Math.abs(this._markerStart - this._markerEnd) / this.audioDuration * 100}%`, height: "100%", top: "0%"
+            left: `${Math.min(NumCast(this._markerStart), NumCast(this._markerEnd)) / this.duration * 100}%`,
+            width: `${Math.abs(this._markerStart - this._markerEnd) / this.duration * 100}%`, height: "100%", top: "0%"
         }} />;
     }
 
@@ -471,8 +467,8 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
             color={"darkblue"}
             height={this._waveHeight}
             barWidth={0.1}
-            pos={this.audioDuration}
-            duration={this.audioDuration}
+            pos={this.duration}
+            duration={this.duration}
             peaks={audioBuckets.length === AudioBox.NUMBER_OF_BUCKETS ? audioBuckets : undefined}
             progressColor={"blue"} />;
     }
@@ -496,17 +492,36 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
     labelClickScript = () => AudioBox.LabelScript;
     rangePlayScript = () => AudioBox.RangePlayScript;
     labelPlayScript = () => AudioBox.LabelPlayScript;
+
+    playLink = (link: Doc) => {
+        if (link.annotationOn === this.rootDoc) {
+            if (this.layoutDoc.playOnSelect) this.playFrom(this.anchorStart(link), this.anchorEnd(link));
+            else this._ele!.currentTime = this.layoutDoc._currentTimecode = this.anchorStart(link);
+        }
+        else this.links.filter(l => l.anchor1 === link || l.anchor2 === link).forEach(l => {
+            const { la1, la2 } = this.getLinkData(l);
+            const startTime = NumCast(la1.anchorStartTime, NumCast(la2.anchorStartTime, null));
+            const endTime = NumCast(la1.anchorEndTime, NumCast(la2.anchorEndTime, null));
+            if (startTime !== undefined) {
+                if (this.layoutDoc.playOnSelect) endTime ? this.playFrom(startTime, endTime) : this.playFrom(startTime);
+                else this._ele!.currentTime = this.layoutDoc._currentTimecode = startTime;
+            }
+        });
+    }
+
     renderInner = computedFn(function (this: AudioBox, mark: Doc, script: undefined | (() => ScriptField), doublescript: undefined | (() => ScriptField), x: number, y: number, width: number, height: number) {
-        const marker = observable({ view: undefined as any });
+        const anchor = observable({ view: undefined as any });
         return {
-            marker, view: <DocumentView key="view" {...this.props} ref={action((r: DocumentView | null) => marker.view = r)}
+            anchor, view: <DocumentView key="view"  {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit} ref={action((r: DocumentView | null) => anchor.view = r)}
                 Document={mark}
+                DataDoc={undefined}
                 PanelWidth={() => width}
                 PanelHeight={() => height}
                 renderDepth={this.props.renderDepth + 1}
                 focus={() => this.playLink(mark)}
                 rootSelected={returnFalse}
                 LayoutTemplate={undefined}
+                LayoutTemplateString={LabelBox.LayoutString("data")}
                 ContainingCollectionDoc={this.props.Document}
                 removeDocument={this.removeDocument}
                 ScreenToLocalTransform={() => this.props.ScreenToLocalTransform().translate(-x - 4, -y - 3)}
@@ -519,11 +534,11 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
                 scriptContext={this} />
         };
     });
-    renderMarker = computedFn(function (this: AudioBox, mark: Doc, script: undefined | (() => ScriptField), doublescript: undefined | (() => ScriptField), x: number, y: number, width: number, height: number) {
+    renderAnchor = computedFn(function (this: AudioBox, 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);
         return <>
             {inner.view}
-            {!inner.marker.view || !SelectionManager.IsSelected(inner.marker.view) ? (null) :
+            {!inner.anchor.view || !SelectionManager.IsSelected(inner.anchor.view) ? (null) :
                 <>
                     <div key="left" className="left-resizer" onPointerDown={e => this.onPointerDown(e, mark, true)} />
                     <div key="right" className="resizer" onPointerDown={e => this.onPointerDown(e, mark, false)} />
@@ -535,8 +550,8 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
         const interactive = SnappingManager.GetIsDragging() || this.active() ? "-interactive" : "";
         const timelineContentWidth = this.props.PanelWidth() - AudioBox.playheadWidth;
         const timelineContentHeight = (this.props.PanelHeight() * AudioBox.heightPercent / 100) * AudioBox.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 overlaps: { anchorStartTime: number, anchorEndTime: number, level: number }[] = [];
+        const drawAnchors = this.anchorDocs.map(anchor => ({ level: this.getLevel(anchor, overlaps), anchor }));
         const maxLevel = overlaps.reduce((m, o) => Math.max(m, o.level), 0) + 2;
         return <div className="audiobox-container"
             onContextMenu={this.specificContextMenu}
@@ -566,37 +581,43 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
                     <div className="audiobox-dictation" />
                     <div className="audiobox-player" style={{ height: `${AudioBox.heightPercent}%` }} >
                         <div className="audiobox-playhead" style={{ width: AudioBox.playheadWidth }} title={this.audioState === "paused" ? "play" : "pause"} onClick={this.onPlay}> <FontAwesomeIcon style={{ width: "100%", position: "absolute", left: "0px", top: "5px", borderWidth: "thin", borderColor: "white" }} icon={this.audioState === "paused" ? "play" : "pause"} size={"1x"} /></div>
-                        <div className="audiobox-timeline" style={{ height: `${AudioBox.heightPercent}%` }} ref={this.timelineRef}
-                            onClick={e => { e.stopPropagation(); e.preventDefault(); }}
-                            onPointerDown={e => e.button === 0 && !e.ctrlKey && this.onPointerDownTimeline(e)}>
-                            <div className="waveform">
+                        <div className="audiobox-timeline" style={{ height: `${AudioBox.heightPercent}%`, left: AudioBox.playheadWidth, width: `calc(100% - ${AudioBox.playheadWidth}px)`, background: "white" }}>
+                            <div className="waveform" >
                                 {this.waveform}
                             </div>
-                            {drawMarkers.map(d => {
-                                const m = d.marker;
-                                const left = NumCast(m.audioStart) / this.audioDuration * timelineContentWidth;
+                        </div>
+                        <div className="audiobox-timeline" style={{ height: `${AudioBox.heightPercent}%`, left: AudioBox.playheadWidth, width: `calc(100% - ${AudioBox.playheadWidth}px)` }} ref={this.timelineRef}
+                            onClick={e => { e.stopPropagation(); e.preventDefault(); }}
+                            onPointerDown={e => e.button === 0 && !e.ctrlKey && this.onPointerDownTimeline(e)}>
+                            {drawAnchors.map(d => {
+                                const m = d.anchor;
+                                const start = this.anchorStart(m);
+                                const end = this.anchorEnd(m, start + 10 / timelineContentWidth * this.duration);
+                                const left = start / this.duration * 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) :
+                                const timespan = end - start;
+                                return this.layoutDoc.hideAnchors ? (null) :
                                     <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,
+                                        style={{ left, top, width: `${timespan / this.duration * 100}%`, height: `${1 / maxLevel * 100}%` }}
+                                        onClick={e => { this.playFrom(start, this.anchorEnd(m)); e.stopPropagation(); }} >
+                                        {this.renderAnchor(m, this.rangeClickScript, this.rangePlayScript,
                                             left + AudioBox.playheadWidth,
                                             (1 - AudioBox.heightPercent / 100) / 2 * this.props.PanelHeight() + top,
-                                            timelineContentWidth * timespan / this.audioDuration,
+                                            timelineContentWidth * timespan / this.duration,
                                             timelineContentHeight / maxLevel)}
                                     </div>;
                             })}
                             {this.selectionContainer}
-                            <div className="audiobox-current" ref={this._audioRef} onClick={e => { e.stopPropagation(); e.preventDefault(); }} style={{ left: `${NumCast(this.layoutDoc._currentTimecode) / this.audioDuration * 100}%`, pointerEvents: "none" }} />
-                            {this.audio}
+                            <div className="audiobox-current" ref={this._audioRef} onClick={e => { e.stopPropagation(); e.preventDefault(); }}
+                                style={{ left: `${NumCast(this.layoutDoc._currentTimecode) / this.duration * 100}%`, pointerEvents: "none" }}
+                            />
                         </div>
+                        {this.audio}
                         <div className="current-time">
                             {formatTime(Math.round(NumCast(this.layoutDoc._currentTimecode)))}
                         </div>
                         <div className="total-time">
-                            {formatTime(Math.round(this.audioDuration))}
+                            {formatTime(Math.round(this.duration))}
                         </div>
                     </div>
                 </div>
diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx
index aef776563..62e497e18 100644
--- a/src/client/views/nodes/PresBox.tsx
+++ b/src/client/views/nodes/PresBox.tsx
@@ -232,14 +232,14 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
         const duration: number = NumCast(activeItem.presEndTime) - NumCast(activeItem.presStartTime);
         if (targetDoc.type === DocumentType.AUDIO) {
             if (this._mediaTimer && this._mediaTimer[1] === targetDoc) clearTimeout(this._mediaTimer[0]);
-            targetDoc._audioStart = NumCast(activeItem.presStartTime);
+            targetDoc._triggerAudio = NumCast(activeItem.presStartTime);
             this._mediaTimer = [setTimeout(() => targetDoc._audioStop = true, duration * 1000), targetDoc];
         } else if (targetDoc.type === DocumentType.VID) {
             if (this._mediaTimer && this._mediaTimer[1] === targetDoc) clearTimeout(this._mediaTimer[0]);
-            targetDoc._videoStop = true;
+            targetDoc._triggerVideoStop = true;
             setTimeout(() => targetDoc._currentTimecode = NumCast(activeItem.presStartTime), 10);
-            setTimeout(() => targetDoc._videoStart = true, 20);
-            this._mediaTimer = [setTimeout(() => targetDoc._videoStop = true, (duration * 1000) + 20), targetDoc];
+            setTimeout(() => targetDoc._triggerVideo = true, 20);
+            this._mediaTimer = [setTimeout(() => targetDoc._triggerVideoStop = true, (duration * 1000) + 20), targetDoc];
         }
     }
 
@@ -249,7 +249,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
             targetDoc._audioStop = true;
         } else if (targetDoc.type === DocumentType.VID) {
             if (this._mediaTimer && this._mediaTimer[1] === targetDoc) clearTimeout(this._mediaTimer[0]);
-            targetDoc._videoStop = true;
+            targetDoc._triggerVideoStop = true;
         }
     }
 
@@ -714,9 +714,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
                 if (audio) {
                     audio.mediaStart = "manual";
                     audio.mediaStop = "manual";
-                    audio.presStartTime = NumCast(doc.audioStart);
-                    audio.presEndTime = NumCast(doc.audioEnd);
-                    audio.presDuration = NumCast(doc.audioEnd) - NumCast(doc.audioStart);
+                    audio.presStartTime = NumCast(doc.anchorStartTime);
+                    audio.presEndTime = NumCast(doc.anchorEndTime);
+                    audio.presDuration = NumCast(doc.anchorEndTime) - NumCast(doc.anchorStartTime);
                     TabDocView.PinDoc(audio, { audioRange: true });
                     setTimeout(() => this.removeDocument(doc), 0);
                     return false;
@@ -1477,6 +1477,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
     @computed get mediaOptionsDropdown() {
         const activeItem: Doc = this.activeItem;
         const targetDoc: Doc = this.targetDoc;
+        const duration = Math.round(NumCast(activeItem[`${Doc.LayoutFieldKey(activeItem)}-duration`]) * 10);
         const mediaStopDocInd: number = NumCast(activeItem.mediaStopDoc);
         const mediaStopDocStr: string = mediaStopDocInd ? mediaStopDocInd + ". " + this.childDocs[mediaStopDocInd - 1].title : "";
         if (activeItem && targetDoc) {
@@ -1521,7 +1522,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
                                     </div>
                                 </div>
                                 <div className="multiThumb-slider">
-                                    <input type="range" step="0.1" min="0" max={activeItem.type === DocumentType.AUDIO ? Math.round(NumCast(activeItem.duration) * 10) / 10 : Math.round(NumCast(activeItem["data-duration"]) * 10) / 10} value={NumCast(activeItem.presEndTime)}
+                                    <input type="range" step="0.1" min="0" max={duration / 10} value={NumCast(activeItem.presEndTime)}
                                         style={{ gridColumn: 1, gridRow: 1 }}
                                         className={`toolbar-slider ${"end"}`}
                                         id="toolbar-slider"
@@ -1545,7 +1546,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
                                             e.stopPropagation();
                                             activeItem.presEndTime = Number(e.target.value);
                                         }} />
-                                    <input type="range" step="0.1" min="0" max={activeItem.type === DocumentType.AUDIO ? Math.round(NumCast(activeItem.duration) * 10) / 10 : Math.round(NumCast(activeItem["data-duration"]) * 10) / 10} value={NumCast(activeItem.presStartTime)}
+                                    <input type="range" step="0.1" min="0" max={duration / 10} value={NumCast(activeItem.presStartTime)}
                                         style={{ gridColumn: 1, gridRow: 1 }}
                                         className={`toolbar-slider ${"start"}`}
                                         id="toolbar-slider"
@@ -1573,7 +1574,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
                                 <div className={`slider-headers ${activeItem.presMovement === PresMovement.Pan || activeItem.presMovement === PresMovement.Zoom ? "" : "none"}`}>
                                     <div className="slider-text">0 s</div>
                                     <div className="slider-text"></div>
-                                    <div className="slider-text">{activeItem.type === DocumentType.AUDIO ? Math.round(NumCast(activeItem.duration) * 10) / 10 : Math.round(NumCast(activeItem["data-duration"]) * 10) / 10} s</div>
+                                    <div className="slider-text">{duration / 10} s</div>
                                 </div>
                             </div>
                             <div className="ribbon-final-box">
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 12822b64a..988dd47d8 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -42,6 +42,7 @@ const VideoDocument = makeInterface(documentSchema, timeSchema);
 
 @observer
 export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoDocument>(VideoDocument) {
+    public static LayoutString(fieldKey: string) { return FieldView.LayoutString(VideoBox, fieldKey); }
     static _youtubeIframeCounter: number = 0;
     static Instance: VideoBox;
     static RangeScript: ScriptField;
@@ -58,20 +59,19 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
     private _isResetClick = 0;
     private _mainCont: React.RefObject<HTMLDivElement> = React.createRef();
     private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef();
-    _play: any = null;
-    _timeline: Opt<HTMLDivElement>;
-    _audioRef = React.createRef<HTMLDivElement>();
-    _markerStart: number = 0;
-    _left: boolean = false;
-    _count: Array<any> = [];
-    _duration = 0;
-    _start: boolean = true;
-    _currMarker: any;
+    private _play: any = null;
+    private _timeline: Opt<HTMLDivElement>;
+    private _audioRef = React.createRef<HTMLDivElement>();
+    private _markerStart: number = 0;
+    private _left: boolean = false;
+    private _duration = 0;
+    private _start: boolean = true;
+    private _currAnchor: Doc | undefined;
+    @observable static _showControls: boolean;
+    @observable static SelectingRegion: VideoBox | undefined = undefined;
     @observable _marqueeing: number[] | undefined;
     @observable _savedAnnotations: Dictionary<number, HTMLDivElement[]> = new Dictionary<number, HTMLDivElement[]>();
     @observable _screenCapture = false;
-    @observable static _showControls: boolean;
-    @observable static SelectingRegion: VideoBox | undefined = undefined;
     @observable _visible: boolean = false;
     @observable _markerEnd: number = 0;
     @observable _forceCreateYouTubeIFrame = false;
@@ -79,11 +79,9 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
     @observable _fullScreen = false;
     @observable _playing = false;
     @computed get links() { return DocListCast(this.dataDoc.links); }
-    @computed get heightPercent() { return this.layoutDoc._showTimeline ? NumCast(this.layoutDoc._videoTimelineHeightPercent, VideoBox.heightPercent) : 100; }
-    @computed get videoDuration() { return NumCast(this.dataDoc[this.fieldKey + "-duration"]); }
-    @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); }
+    @computed get heightPercent() { return this.layoutDoc._timelineShow ? NumCast(this.layoutDoc._videoTimelineHeightPercent, VideoBox.heightPercent) : 100; }
+    @computed get duration() { return NumCast(this.dataDoc[this.fieldKey + "-duration"]); }
+    @computed get anchorDocs() { return DocListCast(this.dataDoc[this.annotationKey + "-timeline"]).concat(DocListCast(this.dataDoc[this.annotationKey])); }
 
     public get player(): HTMLVideoElement | null { return this._videoRef; }
 
@@ -92,14 +90,17 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
         VideoBox.Instance = this;
 
         // onClick play scripts
-        VideoBox.RangeScript = VideoBox.RangeScript || ScriptField.MakeFunction(`scriptContext.clickMarker(self, clientX, this.displayTimecode, this.undisplayTimecode)`, { self: Doc.name, clientX: "number", scriptContext: "any" })!;
-        VideoBox.LabelScript = VideoBox.LabelScript || ScriptField.MakeFunction(`scriptContext.clickMarker(self, clientX, this.displayTimecode)`, { self: Doc.name, clientX: "number", scriptContext: "any" })!;
-        VideoBox.RangePlayScript = VideoBox.RangePlayScript || ScriptField.MakeFunction(`scriptContext.playOnClick(self, clientX, this.displayTimecode, this.undisplayTimecode)`, { self: Doc.name, clientX: "number", scriptContext: "any" })!;
-        VideoBox.LabelPlayScript = VideoBox.LabelPlayScript || ScriptField.MakeFunction(`scriptContext.playOnClick(self, clientX, this.displayTimecode)`, { self: Doc.name, clientX: "number", scriptContext: "any" })!;
+        VideoBox.RangeScript = VideoBox.RangeScript || ScriptField.MakeFunction(`scriptContext.clickAnchor(this, clientX)`, { this: Doc.name, clientX: "number", scriptContext: "any" })!;
+        VideoBox.LabelScript = VideoBox.LabelScript || ScriptField.MakeFunction(`scriptContext.clickAnchor(this, clientX)`, { this: Doc.name, clientX: "number", scriptContext: "any" })!;
+        VideoBox.RangePlayScript = VideoBox.RangePlayScript || ScriptField.MakeFunction(`scriptContext.playOnClick(this, clientX)`, { self: Doc.name, clientX: "number", scriptContext: "any" })!;
+        VideoBox.LabelPlayScript = VideoBox.LabelPlayScript || ScriptField.MakeFunction(`scriptContext.playOnClick(this, clientX)`, { self: Doc.name, clientX: "number", scriptContext: "any" })!;
     }
 
+    anchorStart = (anchor: Doc) => NumCast(anchor.anchorStartTime, NumCast(anchor._timecodeToShow, NumCast(anchor.videoStart)))
+    anchorEnd = (anchor: Doc, defaultVal: any = null) => NumCast(anchor.anchorEndTime, NumCast(anchor._timecodeToHide, NumCast(anchor.videoEnd, defaultVal)))
+
     getAnchor = () => {
-        return this.createMarker(Cast(this.layoutDoc._currentTimecode, "number", null));
+        return this.createAnchor(Cast(this.layoutDoc._currentTimecode, "number", null));
     }
 
     choosePath(url: string) {
@@ -241,19 +242,19 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
                 }
             },
             { fireImmediately: true });
-        this._disposers.videoStart = reaction(
-            () => !LinkDocPreview.TargetDoc && !FormattedTextBoxComment.linkDoc && this.props.renderDepth !== -1 ? Cast(this.Document._videoStart, "number", null) : undefined,
-            videoStart => videoStart !== undefined && setTimeout(() => {
+        this._disposers.triggerVideo = reaction(
+            () => !LinkDocPreview.TargetDoc && !FormattedTextBoxComment.linkDoc && this.props.renderDepth !== -1 ? NumCast(this.Document._triggerVideo, null) : undefined,
+            time => time !== undefined && setTimeout(() => {
                 this.player && this.Play();
-                setTimeout(() => this.Document._videoStart = undefined, 10);
+                setTimeout(() => this.Document._triggerVideo = undefined, 10);
             }, this.player ? 0 : 250), // wait for mainCont and try again to play
             { fireImmediately: true }
         );
-        this._disposers.videoStop = reaction(
-            () => this.props.renderDepth !== -1 && !LinkDocPreview.TargetDoc && !FormattedTextBoxComment.linkDoc ? Cast(this.Document._videoStop, "number", null) : undefined,
-            videoStop => videoStop !== undefined && setTimeout(() => {
+        this._disposers.triggerStop = reaction(
+            () => this.props.renderDepth !== -1 && !LinkDocPreview.TargetDoc && !FormattedTextBoxComment.linkDoc ? NumCast(this.Document._triggerVideoStop, null) : undefined,
+            stop => stop !== undefined && setTimeout(() => {
                 this.player && this.Pause();
-                setTimeout(() => this.Document._videoStop = undefined, 10);
+                setTimeout(() => this.Document._triggerVideoStop = undefined, 10);
             }, this.player ? 0 : 250), // wait for mainCont and try again to play
             { fireImmediately: true }
         );
@@ -321,7 +322,8 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
                     this._videoRef!.srcObject = !this._screenCapture ? undefined : await (navigator.mediaDevices as any).getDisplayMedia({ video: true });
                 }), icon: "expand-arrows-alt"
             });
-            subitems.push({ description: (this.layoutDoc.autoPlay ? "Don't auto play" : "Auto play") + " markers onClick", event: () => this.layoutDoc.autoPlay = !this.layoutDoc.autoPlay, icon: "expand-arrows-alt" });
+            subitems.push({ description: (this.layoutDoc.playOnSelect ? "Don't play" : "Play") + " when link is selected", event: () => this.layoutDoc.playOnSelect = !this.layoutDoc.playOnSelect, icon: "expand-arrows-alt" });
+            subitems.push({ description: (this.layoutDoc.autoPlay ? "Don't auto play" : "Auto play") + " anchors onClick", event: () => this.layoutDoc.autoPlay = !this.layoutDoc.autoPlay, icon: "expand-arrows-alt" });
             ContextMenu.Instance.addItem({ description: "Options...", subitems: subitems, icon: "video" });
         }
     }
@@ -408,7 +410,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
             right: this.scaling() * 10 - 10,
             bottom: this.scaling() * 10 - 10
         }}>
-            <FontAwesomeIcon icon={this.layoutDoc._showTimeline ? "eye-slash" : "eye"} style={{ width: "100%" }} />
+            <FontAwesomeIcon icon={this.layoutDoc._timelineShow ? "eye-slash" : "eye"} style={{ width: "100%" }} />
         </div>,
         VideoBox._showControls ? (null) : [
             // <div className="control-background">
@@ -470,16 +472,16 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
     }
 
     @action.bound
-    addDocumentWithTimestamp(doc: Doc | Doc[]): boolean {
+    addDocWithTimecode(doc: Doc | Doc[]): boolean {
         const docs = doc instanceof Doc ? [doc] : doc;
         const curTime = NumCast(this.layoutDoc._currentTimecode);
-        docs.forEach(doc => doc.displayTimecode = curTime);
+        docs.forEach(doc => doc._timecodeToShow = curTime);
         return this.addDocument(doc);
     }
 
     // play back the video from time
     @action
-    playFrom = (seekTimeInSeconds: number, endTime: number = this.videoDuration) => {
+    playFrom = (seekTimeInSeconds: number, endTime: number = this.duration) => {
         clearTimeout(this._play);
         this._duration = endTime - seekTimeInSeconds;
         if (Number.isNaN(this.player?.duration)) {
@@ -495,7 +497,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
                 this.player.currentTime = seekTimeInSeconds;
                 this.player.play();
                 runInAction(() => this._playing = true);
-                if (endTime !== this.videoDuration) {
+                if (endTime !== this.duration) {
                     this._play = setTimeout(() => this.Pause(), (this._duration) * 1000); // use setTimeout to play a specific duration
                 }
             } else {
@@ -505,12 +507,12 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
     }
 
     @action
-    toggleTimeline = (e: React.PointerEvent) => this.layoutDoc._showTimeline = !this.layoutDoc._showTimeline
+    toggleTimeline = (e: React.PointerEvent) => this.layoutDoc._timelineShow = !this.layoutDoc._timelineShow
 
     // ref for timeline
-    timelineRef = (timeline: HTMLDivElement) => { this._timeline = timeline; };
+    timelineRef = (timeline: HTMLDivElement) => this._timeline = timeline
 
-    // starting the drag event creating a range marker
+    // starting the drag event creating a range mark
     @action
     onPointerDownTimeline = (e: React.PointerEvent): void => {
         const rect = this._timeline?.getBoundingClientRect();
@@ -520,7 +522,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
             else if (!this._doubleTime) {
                 this._doubleTime = setTimeout(() => {
                     this._doubleTime = undefined;
-                    this.player!.currentTime = this.layoutDoc._currentTimecode = (e.clientX - rect.x) / rect.width * this.videoDuration;
+                    this.player!.currentTime = this.layoutDoc._currentTimecode = (e.clientX - rect.x) / rect.width * this.duration;
                 }, 300);
             }
 
@@ -537,12 +539,12 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
                         this._markerStart = this._markerEnd;
                         this._markerEnd = tmp;
                     }
-                    VideoBox.SelectingRegion === this && (Math.abs(movement[0]) > 15) && this.createMarker(this._markerStart, this._markerEnd);
+                    VideoBox.SelectingRegion === this && (Math.abs(movement[0]) > 15) && this.createAnchor(this._markerStart, this._markerEnd);
                     VideoBox.SelectingRegion = undefined;
                 }),
                 (e, doubleTap) => {
                     this.props.select(false);
-                    e.shiftKey && this.createMarker(this.player!.currentTime);
+                    e.shiftKey && this.createAnchor(this.player!.currentTime);
                     !wasPlaying && doubleTap && this.Play();
                 }
                 , this.props.isSelected(true) || this._isChildActive);
@@ -550,25 +552,27 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
     }
 
     @action
-    createMarker(displayTimecode: number, undisplayTimecode?: number) {
-        const marker = Docs.Create.LabelDocument({
-            title: ComputedField.MakeFunction(`"#" + formatToTime(self.displayTimecode) + "-" + formatToTime(self.undisplayTimecode)`) as any,
+    createAnchor(anchorStartTime: number, anchorEndTime?: number) {
+        const anchor = Docs.Create.LabelDocument({
+            title: ComputedField.MakeFunction(`"#" + formatToTime(self.anchorStartTime) + "-" + formatToTime(self.anchorEndTime)`) 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,
+            anchorStartTime,
+            anchorEndTime,
             annotationOn: this.props.Document
         });
         if (this.dataDoc[this.annotationKey + "-timeline"]) {
-            this.dataDoc[this.annotationKey + "-timeline"].push(marker);
+            this.dataDoc[this.annotationKey + "-timeline"].push(anchor);
         } else {
-            this.dataDoc[this.annotationKey + "-timeline"] = new List<Doc>([marker]);
+            this.dataDoc[this.annotationKey + "-timeline"] = new List<Doc>([anchor]);
         }
-        return marker;
+        return anchor;
     }
 
     @action
-    playOnClick = (anchorDoc: Doc, clientX: number, seekTimeInSeconds: number, endTime: number = this.videoDuration) => {
+    playOnClick = (anchorDoc: Doc, clientX: number) => {
+        const seekTimeInSeconds = this.anchorStart(anchorDoc);
+        const endTime = this.anchorEnd(anchorDoc);
         if (this.layoutDoc.autoPlay) {
             if (this._playing) this.Pause();
             else this.playFrom(seekTimeInSeconds, endTime);
@@ -587,7 +591,9 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
     }
 
     @action
-    clickMarker = (anchorDoc: Doc, clientX: number, seekTimeInSeconds: number, endTime: number = this.videoDuration) => {
+    clickAnchor = (anchorDoc: Doc, clientX: number) => {
+        const seekTimeInSeconds = this.anchorStart(anchorDoc);
+        const endTime = this.anchorEnd(anchorDoc);
         if (seekTimeInSeconds < NumCast(this.layoutDoc._currentTimecode) + 1e-4 && endTime > NumCast(this.layoutDoc._currentTimecode) - 1e-4) {
             if (this._playing) this.Pause();
             else if (this.layoutDoc.autoPlay) this.Play();
@@ -602,16 +608,16 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
         return { select: true };
     }
 
-    toTimeline = (screen_delta: number, width: number) => Math.max(0, Math.min(this.videoDuration, screen_delta / width * this.videoDuration));
-    // starting the drag event for marker resizing
-    onPointerDown = (e: React.PointerEvent, m: any, left: boolean): void => {
-        this._currMarker = m;
+    toTimeline = (screen_delta: number, width: number) => Math.max(0, Math.min(this.duration, screen_delta / width * this.duration));
+    // starting the drag event for anchor resizing
+    onPointerDown = (e: React.PointerEvent, m: Doc, left: boolean): void => {
+        this._currAnchor = m;
         this._left = left;
         this._timeline?.setPointerCapture(e.pointerId);
         setupMoveUpEvents(this, e,
             (e: PointerEvent) => {
                 const rect = (e.target as any).getBoundingClientRect();
-                this.changeMarker(this._currMarker, this.toTimeline(e.clientX - rect.x, rect.width));
+                this.changeAnchor(this._currAnchor, this.toTimeline(e.clientX - rect.x, rect.width));
                 return false;
             },
             (e: PointerEvent) => {
@@ -622,14 +628,14 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
             emptyFunction);
     }
 
-    // makes sure no markers overlaps each other by setting the correct position and width
-    getLevel = (m: any, placed: { videoStart: number, videoEnd: number, level: number }[]) => {
+    // makes sure no anchors overlaps each other by setting the correct position and width
+    getLevel = (m: any, placed: { anchorStartTime: number, videoEnd: number, level: number }[]) => {
         const timelineContentWidth = this.props.PanelWidth();
-        const x1 = m.displayTimecode;
-        const x2 = m.undisplayTimecode === undefined ? m.displayTimecode + 10 / timelineContentWidth * this.videoDuration : m.undisplayTimecode;
+        const x1 = this.anchorStart(m);
+        const x2 = this.anchorEnd(m, x1 + 10 / timelineContentWidth * this.duration);
         let max = 0;
         const overlappedLevels = new Set(placed.map(p => {
-            const y1 = p.videoStart;
+            const y1 = p.anchorStartTime;
             const y2 = p.videoEnd;
             if ((x1 >= y1 && x1 <= y2) || (x2 >= y1 && x2 <= y2) ||
                 (y1 >= x1 && y1 <= x2) || (y2 >= x1 && y2 <= x2)) {
@@ -640,28 +646,29 @@ 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({ videoStart: x1, videoEnd: x2, level });
+        placed.push({ anchorStartTime: x1, videoEnd: x2, level });
         return level;
     }
 
     playLink = (doc: Doc) => {
-        const startTime = NumCast(doc.displayTimecode);
-        const endTime = NumCast(doc.undisplayTimecode, null);
+        const startTime = NumCast(doc.anchorStartTime, NumCast(doc._timecodeToShow));
+        const endTime = NumCast(doc.anchorEndTime, NumCast(doc._timecodeToHide, null));
         if (startTime !== undefined) {
-            this.layoutDoc.playOnSelect && (endTime ? this.playFrom(startTime, endTime) : this.playFrom(startTime));
+            if (this.layoutDoc.playOnSelect) endTime ? this.playFrom(startTime, endTime) : this.playFrom(startTime);
+            else this.Seek(startTime);
         }
     }
-    // renders the markers as a document
+    // renders the anchors as a document
     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 });
+        const anchor = observable({ view: undefined as any });
         return {
-            marker, view: <DocumentView key="view" {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit} ref={action((r: DocumentView | null) => marker.view = r)}
+            anchor, view: <DocumentView key="view" {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit} ref={action((r: DocumentView | null) => anchor.view = r)}
                 Document={mark}
                 DataDoc={undefined}
-                focus={() => this.playLink(mark)}
                 PanelWidth={() => width}
                 PanelHeight={() => height}
                 renderDepth={this.props.renderDepth + 1}
+                focus={() => this.playLink(mark)}
                 rootSelected={returnFalse}
                 LayoutTemplate={undefined}
                 LayoutTemplateString={LabelBox.LayoutString("data")}
@@ -678,11 +685,11 @@ 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, annotationKey: string) {
+    renderAnchor = 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) :
+            {!inner.anchor.view || !SelectionManager.IsSelected(inner.anchor.view) ? (null) :
                 <>
                     <div key="left" className="left-resizer" onPointerDown={e => this.onPointerDown(e, mark, true)} />
                     <div key="right" className="resizer" onPointerDown={e => this.onPointerDown(e, mark, false)} />
@@ -694,10 +701,10 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
     @computed get renderTimeline() {
         const timelineContentWidth = this.props.PanelWidth();
         const timelineContentHeight = this.props.PanelHeight() * (100 - this.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 overlaps: { anchorStartTime: number, videoEnd: number, level: number }[] = [];
+        const drawAnchors: { level: number, anchor: Doc }[] = this.anchorDocs.map(anchor => ({ level: this.getLevel(anchor, overlaps), anchor }));
         const maxLevel = overlaps.reduce((m, o) => Math.max(m, o.level), 0) + 2;
-        return !this.layoutDoc._showTimeline ? (null) :
+        return !this.layoutDoc._timelineShow ? (null) :
             <div className="audiobox-timeline" ref={this.timelineRef} style={{ height: `${100 - this.heightPercent}%` }}
                 onClick={e => {
                     if (this._isChildActive || this.props.isSelected()) {
@@ -709,47 +716,51 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
                         e.button === 0 && !e.ctrlKey && this.onPointerDownTimeline(e);
                     }
                 }}>
-                {drawMarkers.map(d => {
-                    const m = d.marker;
-                    const start = NumCast(m.displayTimecode, NumCast(m.displayTimecode, null));
-                    const left = start / this.videoDuration * timelineContentWidth;
+                {drawAnchors.map(d => {
+                    const m = d.anchor;
+                    const start = this.anchorStart(m);
+                    const end = this.anchorEnd(m, start + 10 / timelineContentWidth * this.duration);
+                    const left = start / this.duration * timelineContentWidth;
                     const top = d.level / maxLevel * timelineContentHeight;
-                    const timespan = m.undisplayTimecode === undefined ? 10 / timelineContentWidth * this.videoDuration : NumCast(m.undisplayTimecode) - NumCast(m.displayTimecode);
-                    return this.layoutDoc.hideMarkers ? (null) :
+                    const timespan = end - start;
+                    return this.layoutDoc.hideAnchors ? (null) :
                         <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,
+                            style={{ left, top, width: `${timespan / this.duration * 100}%`, height: `${1 / maxLevel * 100}%` }}
+                            onClick={e => { this.playFrom(start, this.anchorEnd(m)); e.stopPropagation(); }} >
+                            {this.renderAnchor(m, this.rangeClickScript, this.rangePlayScript,
                                 left,
                                 top + (this.props.PanelHeight() - timelineContentHeight),
-                                timelineContentWidth * timespan / this.videoDuration,
-                                timelineContentHeight / maxLevel, this.annotationKey + (m.useLinkSmallAnchor ? "-timeline" : ""))}
+                                timelineContentWidth * timespan / this.duration,
+                                timelineContentHeight / maxLevel, this.annotationKey + (m.anchorStartTime !== undefined ? "-timeline" : ""))}
                         </div>;
                 })}
                 {this.selectionContainer}
                 <div className="audiobox-current" ref={this._audioRef} onClick={e => { e.stopPropagation(); e.preventDefault(); }}
-                    style={{ left: `${NumCast(this.layoutDoc._currentTimecode) / this.videoDuration * 100}%`, pointerEvents: "none" }}
+                    style={{ left: `${NumCast(this.layoutDoc._currentTimecode) / this.duration * 100}%`, pointerEvents: "none" }}
                 />
             </div>;
     }
 
-    // updates the marker with the new time
+    // updates the anchor with the new time
     @action
-    changeMarker = (m: any, time: any) => {
-        this.markerDocs.filter(marker => this.isSame(marker, m)).forEach(marker =>
-            this._left ? marker.displayTimecode = time : marker.undisplayTimecode = time);
+    changeAnchor = (anchor: Opt<Doc>, time: number) => {
+        if (anchor) {
+            const timelineOnly = Cast(anchor.anchorStartTime, "number", null) !== undefined;
+            if (timelineOnly) this._left ? anchor.anchorStartTime = time : anchor.anchorEndTime = time;
+            else this._left ? anchor._timecodeToShow = time : anchor._timecodeToHide = time;
+        }
     }
 
-    // checks if the two markers are the same with start and end time
+    // checks if the two anchors are the same with start and end time
     isSame = (m1: any, m2: any) => {
-        return m1.displayTimecode === m2.displayTimecode && m1.undisplayTimecode === m2.undisplayTimecode;
+        return m1.anchorStartTime === m2.anchorStartTime && m1.anchorEndTime === m2.anchorEndTime && m1._timecodeToShow === m2._timecodeToShow && m1._timecodeToHide === m2._timecodeToHide;
     }
 
     // returns the blue container when dragging
     @computed get selectionContainer() {
         return VideoBox.SelectingRegion !== this ? (null) : <div className="audiobox-container" style={{
-            left: `${Math.min(NumCast(this._markerStart), NumCast(this._markerEnd)) / this.videoDuration * 100}%`,
-            width: `${Math.abs(this._markerStart - this._markerEnd) / this.videoDuration * 100}%`, height: "100%", top: "0%"
+            left: `${Math.min(NumCast(this._markerStart), NumCast(this._markerEnd)) / this.duration * 100}%`,
+            width: `${Math.abs(this._markerStart - this._markerEnd) / this.duration * 100}%`, height: "100%", top: "0%"
         }} />;
     }
 
@@ -757,7 +768,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
         VideoBox.Instance.keyEvents(e);
     }
 
-    // for creating key markers with key events
+    // for creating key anchors with key events
     @action
     keyEvents = (e: KeyboardEvent) => {
         if (e.target instanceof HTMLInputElement) return;
@@ -770,7 +781,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
                     this._start = false;
                     this._visible = true;
                 } else {
-                    this.createMarker(this._markerStart, currTime);
+                    this.createAnchor(this._markerStart, currTime);
                     this._start = true;
                     this._visible = false;
                 }
@@ -828,7 +839,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
                         whenActiveChanged={this.whenActiveChanged}
                         removeDocument={this.removeDocument}
                         moveDocument={this.moveDocument}
-                        addDocument={this.addDocumentWithTimestamp}
+                        addDocument={this.addDocWithTimecode}
                         CollectionView={undefined}
                         renderDepth={this.props.renderDepth + 1}>
                         {this.contentFunc}
@@ -838,7 +849,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
                 {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} />}
+                    <MarqueeAnnotator rootDoc={this.rootDoc} down={this._marqueeing} scaling={this.props.scaling} addDocument={this.addDocWithTimecode} finishMarquee={this.finishMarquee} savedAnnotations={this._savedAnnotations} annotationLayer={this._annotationLayer.current} mainCont={this._mainCont.current} />}
             </div>
         </div >);
     }
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 7348ebdd2..e06a324d2 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -361,7 +361,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             DocListCast(this.dataDoc.links).map((l, i) => {
                 let la1 = l.anchor1 as Doc;
                 let la2 = l.anchor2 as Doc;
-                this._linkTime = NumCast(la1.audioStart, NumCast(la2.audioStart));
+                this._linkTime = NumCast(la1.anchorStartTime, NumCast(la2.anchorStartTime));
                 audioState = la2.audioState;
                 if (Doc.AreProtosEqual(la2, this.dataDoc)) {
                     la1 = l.anchor2 as Doc;
diff --git a/src/fields/documentSchemas.ts b/src/fields/documentSchemas.ts
index 67df56015..d52f3a928 100644
--- a/src/fields/documentSchemas.ts
+++ b/src/fields/documentSchemas.ts
@@ -19,10 +19,10 @@ export const documentSchema = createSchema({
     lastFrame: "number",        // last frame of a frame based collection (e.g., a progressive slide)
     activeFrame: "number",      // the active frame of a frame based animated document 
     _currentTimecode: "number", // current play back time of a temporal document (video / audio)
-    displayTimecode: "number",  // the time that a document should be displayed (e.g., time an annotation should be displayed on a video)
+    _timecodeToShow: "number",  // the time that a document should be displayed (e.g., time an annotation should be displayed on a video)
     isLabel: "boolean",         // whether the document is a label or not (video / audio)
-    audioStart: "number",       // the time frame where the audio should begin playing
-    audioEnd: "number",         // the time frame where the audio should stop playing
+    anchorStartTime: "number",    // the time code where a document activates (eg in Audio or video timelines)
+    anchorEndTime: "number",      // the time code where a document deactivates
     markers: listSpec(Doc),     // list of markers for audio / video
     x: "number",                // x coordinate when in a freeform view 
     y: "number",                // y coordinate when in a freeform view 
-- 
cgit v1.2.3-70-g09d2

