import React = require('react');
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Tooltip } from '@material-ui/core';
import { action, computed, IReactionDisposer, observable, ObservableSet, observe, reaction, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import { ColorState, SketchPicker } from 'react-color';
import { AnimationSym, Doc, DocListCast, FieldResult, HighlightSym, Opt, StrListCast } from '../../../../fields/Doc';
import { Copy, Id } from '../../../../fields/FieldSymbols';
import { InkTool } from '../../../../fields/InkField';
import { List } from '../../../../fields/List';
import { ObjectField } from '../../../../fields/ObjectField';
import { listSpec } from '../../../../fields/Schema';
import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../../../fields/Types';
import { AudioField } from '../../../../fields/URLField';
import { emptyFunction, emptyPath, returnFalse, returnOne, setupMoveUpEvents, StopEvent } from '../../../../Utils';
import { DocServer } from '../../../DocServer';
import { Docs } from '../../../documents/Documents';
import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes';
import { DocumentManager } from '../../../util/DocumentManager';
import { ScriptingGlobals } from '../../../util/ScriptingGlobals';
import { SelectionManager } from '../../../util/SelectionManager';
import { SettingsManager } from '../../../util/SettingsManager';
import { undoBatch, UndoManager } from '../../../util/UndoManager';
import { CollectionDockingView } from '../../collections/CollectionDockingView';
import { CollectionFreeFormView, computeTimelineLayout, MarqueeViewBounds } from '../../collections/collectionFreeForm';
import { CollectionView } from '../../collections/CollectionView';
import { TabDocView } from '../../collections/TabDocView';
import { ViewBoxBaseComponent } from '../../DocComponent';
import { Colors } from '../../global/globalEnums';
import { LightboxView } from '../../LightboxView';
import { CollectionFreeFormDocumentView } from '../CollectionFreeFormDocumentView';
import { DocFocusOptions, DocumentView, OpenWhere, OpenWhereMod } from '../DocumentView';
import { FieldView, FieldViewProps } from '../FieldView';
import { ScriptingBox } from '../ScriptingBox';
import './PresBox.scss';
import { PresEffect, PresEffectDirection, PresMovement, PresStatus } from './PresEnums';
import { CollectionStackedTimeline } from '../../collections/CollectionStackedTimeline';
const { Howl } = require('howler');

export interface PinProps {
    audioRange?: boolean;
    activeFrame?: number;
    currentFrame?: number;
    hidePresBox?: boolean;
    pinViewport?: MarqueeViewBounds; // pin a specific viewport on a freeform view (use MarqueeView.CurViewBounds to compute if no region has been selected)
    pinDocLayout?: boolean; // pin layout info (width/height/x/y)
    pinDocContent?: boolean; // pin data info (scroll/pan/zoom/text)
    pinAudioPlay?: boolean; // pin audio annotation
    pinData?: {
        scrollable?: boolean | undefined;
        pannable?: boolean | undefined;
        temporal?: boolean | undefined;
        clippable?: boolean | undefined;
        dataview?: boolean | undefined;
        textview?: boolean | undefined;
        poslayoutview?: boolean | undefined;
        dataannos?: boolean | undefined;
    };
}

@observer
export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
    public static LayoutString(fieldKey: string) {
        return FieldView.LayoutString(PresBox, fieldKey);
    }

    private _disposers: { [name: string]: IReactionDisposer } = {};
    public selectedArray = new ObservableSet<Doc>();

    @observable public static Instance: PresBox;

    @observable _isChildActive = false;
    @observable _moveOnFromAudio: boolean = true;
    @observable _presTimer!: NodeJS.Timeout;

    @observable _eleArray: HTMLElement[] = [];
    @observable _dragArray: HTMLElement[] = [];
    @observable _pathBoolean: boolean = false;
    @observable _expandBoolean: boolean = false;
    @observable _transitionTools: boolean = false;
    @observable _newDocumentTools: boolean = false;
    @observable _openMovementDropdown: boolean = false;
    @observable _openEffectDropdown: boolean = false;
    @observable _presentTools: boolean = false;
    @observable _treeViewMap: Map<Doc, number> = new Map();
    @observable _presKeyEvents: boolean = false;
    @observable _forceKeyEvents: boolean = false;
    @computed get isTreeOrStack() {
        return [CollectionViewType.Tree, CollectionViewType.Stacking].includes(StrCast(this.layoutDoc._viewType) as any);
    }
    @computed get isTree() {
        return this.layoutDoc._viewType === CollectionViewType.Tree;
    }
    @computed get presFieldKey() {
        return StrCast(this.layoutDoc.presFieldKey, 'data');
    }
    @computed get childDocs() {
        return DocListCast(this.rootDoc[this.presFieldKey]);
    }
    @computed get tagDocs() {
        return this.childDocs.map(doc => Cast(doc.presentationTargetDoc, Doc, null));
    }
    @computed get itemIndex() {
        return NumCast(this.rootDoc._itemIndex);
    }
    @computed get activeItem() {
        return DocCast(this.childDocs[NumCast(this.rootDoc._itemIndex)]);
    }
    @computed get targetDoc() {
        return Cast(this.activeItem?.presentationTargetDoc, Doc, null);
    }
    @computed get scrollable() {
        if ([DocumentType.PDF, DocumentType.WEB, DocumentType.RTF].includes(this.targetDoc.type as DocumentType) || this.targetDoc._viewType === CollectionViewType.Stacking) return true;
        return false;
    }
    @computed get panable() {
        if ((this.targetDoc.type === DocumentType.COL && this.targetDoc._viewType === CollectionViewType.Freeform) || this.targetDoc.type === DocumentType.IMG) return true;
        return false;
    }
    @computed get selectedDocumentView() {
        if (SelectionManager.Views().length) return SelectionManager.Views()[0];
        if (this.selectedArray.size) return DocumentManager.Instance.getDocumentView(this.rootDoc);
    }
    @computed get isPres() {
        return this.selectedDoc === this.rootDoc;
    }
    @computed get selectedDoc() {
        return this.selectedDocumentView?.rootDoc;
    }
    isActiveItemTarget = (layoutDoc: Doc) => this.activeItem?.presentationTargetDoc === layoutDoc;
    clearSelectedArray = () => this.selectedArray.clear();
    addToSelectedArray = action((doc: Doc) => this.selectedArray.add(doc));
    removeFromSelectedArray = action((doc: Doc) => this.selectedArray.delete(doc));

    _unmounting = false;
    @action
    componentWillUnmount() {
        this._unmounting = true;
        if (this._presTimer) clearTimeout(this._presTimer);
        document.removeEventListener('keydown', PresBox.keyEventsWrapper, true);
        this.resetPresentation();
        // Turn of progressivize editors
        this.turnOffEdit(true);
        Object.values(this._disposers).forEach(disposer => disposer?.());
    }

    componentDidMount() {
        this._disposers.keyboard = reaction(
            () => this.selectedDoc,
            selected => {
                document.removeEventListener('keydown', PresBox.keyEventsWrapper, true);
                (this._presKeyEvents = selected === this.rootDoc) && document.addEventListener('keydown', PresBox.keyEventsWrapper, true);
            },
            { fireImmediately: true }
        );
        this._disposers.forcekeyboard = reaction(
            () => this._forceKeyEvents,
            force => {
                if (force) {
                    document.removeEventListener('keydown', PresBox.keyEventsWrapper, true);
                    document.addEventListener('keydown', PresBox.keyEventsWrapper, true);
                    this._presKeyEvents = true;
                }
                this._forceKeyEvents = false;
            },
            { fireImmediately: true }
        );
        this.props.setContentView?.(this);
        this._unmounting = false;
        if (this.props.renderDepth > 0) {
            runInAction(() => {
                this.rootDoc._forceRenderEngine = computeTimelineLayout.name;
                this.layoutDoc._gridGap = 0;
                this.layoutDoc._yMargin = 0;
            });
        }
        this.turnOffEdit(true);
        this._disposers.selection = reaction(
            () => SelectionManager.Views(),
            views => views.some(view => view.props.Document === this.rootDoc) && this.updateCurrentPresentation()
        );
    }

    @action
    updateCurrentPresentation = (pres?: Doc) => {
        Doc.ActivePresentation = pres ?? this.rootDoc;
        PresBox.Instance = this;
    };

    _mediaTimer!: [NodeJS.Timeout, Doc];
    // 'Play on next' for audio or video therefore first navigate to the audio/video before it should be played
    startTempMedia = (targetDoc: Doc, activeItem: Doc) => {
        const duration: number = NumCast(activeItem.presEndTime) - NumCast(activeItem.presStartTime);
        if ([DocumentType.VID, DocumentType.AUDIO].includes(targetDoc.type as any)) {
            const targMedia = DocumentManager.Instance.getDocumentView(targetDoc);
            targMedia?.ComponentView?.playFrom?.(NumCast(activeItem.presStartTime), NumCast(activeItem.presStartTime) + duration);
        }
    };

    stopTempMedia = (targetDocField: FieldResult) => {
        const targetDoc = Cast(targetDocField, Doc, null);
        if ([DocumentType.VID, DocumentType.AUDIO].includes(targetDoc.type as any)) {
            const targMedia = DocumentManager.Instance.getDocumentView(targetDoc);
            targMedia?.ComponentView?.Pause?.();
        }
    };

    //TODO: al: it seems currently that tempMedia doesn't stop onslidechange after clicking the button; the time the tempmedia stop depends on the start & end time
    // TODO: to handle child slides (entering into subtrail and exiting), also the next() and back() functions
    // No more frames in current doc and next slide is defined, therefore move to next slide
    nextSlide = (slideNum?: number) => {
        const nextSlideInd = slideNum ?? this.itemIndex + 1;
        let curSlideInd = nextSlideInd;
        CollectionStackedTimeline.CurrentlyPlaying?.map((clip, i) => DocumentManager.Instance.getDocumentView(clip)?.ComponentView?.Pause?.());
        this.clearSelectedArray();
        const doGroupWithUp =
            (nextSelected: number, force = false) =>
            () => {
                if (nextSelected < this.childDocs.length) {
                    if (force || this.childDocs[nextSelected].groupWithUp) {
                        this.addToSelectedArray(this.childDocs[nextSelected]);
                        const serial = nextSelected + 1 < this.childDocs.length && NumCast(this.childDocs[nextSelected + 1].groupWithUp) > 1;
                        if (serial) {
                            this.gotoDocument(nextSelected, this.activeItem, true, async () => {
                                const waitTime = NumCast(this.activeItem.presDuration) - NumCast(this.activeItem.presTransition);
                                await new Promise<void>(res => setTimeout(() => res(), Math.max(0, waitTime)));
                                doGroupWithUp(nextSelected + 1)();
                            });
                        } else {
                            this.gotoDocument(nextSelected, this.activeItem, true);
                            curSlideInd = this.itemIndex;
                            doGroupWithUp(nextSelected + 1)();
                        }
                    }
                }
            };
        doGroupWithUp(curSlideInd, true)();
    };

    // Called when the user activates 'next' - to move to the next part of the pres. trail
    @action
    next = () => {
        if (this.childDocs[this.itemIndex + 1] !== undefined) {
            // Case 1: No more frames in current doc and next slide is defined, therefore move to next slide
            const slides = DocListCast(this.rootDoc[StrCast(this.presFieldKey, 'data')]);
            const curLast = this.selectedArray.size ? Math.max(...Array.from(this.selectedArray).map(d => slides.indexOf(DocCast(d)))) : this.itemIndex;
            this.nextSlide(curLast + 1 === this.childDocs.length ? (this.layoutDoc.presLoop ? 0 : curLast) : curLast + 1);
        } else {
            if (this.childDocs[this.itemIndex + 1] === undefined && (this.layoutDoc.presLoop || this.layoutDoc.presStatus === PresStatus.Edit)) {
                // Case 2: Last slide and presLoop is toggled ON or it is in Edit mode
                this.nextSlide(0);
            }
            return 0;
        }
        return this.itemIndex;
    };

    // Called when the user activates 'back' - to move to the previous part of the pres. trail
    @action
    back = () => {
        const activeItem: Doc = this.activeItem;
        const prevItem = Cast(this.childDocs[Math.max(0, this.itemIndex - 1)], Doc, null);
        let prevSelected = this.itemIndex;
        // Functionality for group with up
        let didZoom = activeItem.presMovement;
        for (; prevSelected > 0 && this.childDocs[Math.max(0, prevSelected - 1)].groupWithUp; prevSelected--) {
            didZoom = didZoom === 'none' ? this.childDocs[prevSelected].presMovement : didZoom;
        }
        if (activeItem && this.childDocs[this.itemIndex - 1] !== undefined) {
            // Case 2: There are no other frames so it should go to the previous slide
            prevSelected = Math.max(0, prevSelected - 1);
            this.nextSlide(prevSelected);
            this.rootDoc._itemIndex = prevSelected;
        } else if (this.childDocs[this.itemIndex - 1] === undefined && this.layoutDoc.presLoop) {
            // Case 3: Pres loop is on so it should go to the last slide
            this.nextSlide(this.childDocs.length - 1);
        }
        return this.itemIndex;
    };

    //The function that is called when a document is clicked or reached through next or back.
    //it'll also execute the necessary actions if presentation is playing.
    @undoBatch
    public gotoDocument = action((index: number, from?: Doc, group?: boolean, finished?: () => void) => {
        Doc.UnBrushAllDocs();
        if (index >= 0 && index < this.childDocs.length) {
            this.rootDoc._itemIndex = index;
            const activeItem: Doc = this.activeItem;
            const targetDoc: Doc = this.targetDoc;
            const activeFrame = activeItem.presActiveFrame ?? activeItem.presCurrentFrame;
            if (activeFrame !== undefined) {
                const transTime = NumCast(activeItem.presTransition, 500);
                const acontext = activeItem.presActiveFrame !== undefined ? DocCast(DocCast(activeItem.presentationTargetDoc).context) : DocCast(activeItem.presentationTargetDoc);
                const context = DocCast(acontext)?.annotationOn ? DocCast(DocCast(acontext).annotationOn) : acontext;
                if (context) {
                    const ffview = DocumentManager.Instance.getFirstDocumentView(context)?.ComponentView as CollectionFreeFormView;
                    if (ffview?.childDocs) {
                        this._keyTimer = CollectionFreeFormDocumentView.gotoKeyframe(this._keyTimer, ffview.childDocs.slice(), transTime);
                        context._currentFrame = NumCast(activeFrame);
                    }
                }
            }
            if (from?.mediaStopTriggerList && this.layoutDoc.presStatus !== PresStatus.Edit) {
                DocListCast(from.mediaStopTriggerList).forEach(this.stopTempMedia);
            }
            if (from?.mediaStop === 'auto' && this.layoutDoc.presStatus !== PresStatus.Edit) {
                this.stopTempMedia(from.presentationTargetDoc);
            }
            // If next slide is audio / video 'Play automatically' then the next slide should be played
            if (this.layoutDoc.presStatus !== PresStatus.Edit && (targetDoc.type === DocumentType.AUDIO || targetDoc.type === DocumentType.VID) && activeItem.mediaStart === 'auto') {
                this.startTempMedia(targetDoc, activeItem);
            }
            if (!group) this.clearSelectedArray();
            this.childDocs[index] && this.addToSelectedArray(this.childDocs[index]); //Update selected array
            this.turnOffEdit();
            this.navigateToActiveItem(finished); //Handles movement to element only when presTrail is list
            this.onHideDocument(); //Handles hide after/before
        }
    });
    static pinDataTypes(target?: Doc): { scrollable?: boolean; pannable?: boolean; temporal?: boolean; clippable?: boolean; dataview?: boolean; textview?: boolean; poslayoutview?: boolean; dataannos?: boolean } {
        const targetType = target?.type as any;
        const scrollable = [DocumentType.PDF, DocumentType.RTF, DocumentType.WEB].includes(targetType) || target?._viewType === CollectionViewType.Stacking;
        const pannable = [DocumentType.IMG, DocumentType.PDF].includes(targetType) || (targetType === DocumentType.COL && target?._viewType === CollectionViewType.Freeform);
        const temporal = [DocumentType.AUDIO, DocumentType.VID].includes(targetType);
        const clippable = [DocumentType.COMPARISON].includes(targetType);
        const dataview = [DocumentType.INK, DocumentType.COL, DocumentType.IMG].includes(targetType) && target?.activeFrame === undefined;
        const poslayoutview = [DocumentType.COL].includes(targetType) && target?.activeFrame === undefined;
        const textview = [DocumentType.RTF].includes(targetType) && target?.activeFrame === undefined;
        const dataannos = false;
        return { scrollable, pannable, temporal, clippable, dataview, textview, poslayoutview, dataannos };
    }

    @action
    playAnnotation = (anno: AudioField) => {};
    @action
    static restoreTargetDocView(bestTargetView: Opt<DocumentView>, pinProps: PinProps | undefined, activeItem: Doc, transTime: number, pinDataTypes = this.pinDataTypes(bestTargetView?.rootDoc)) {
        if (!bestTargetView) return;
        const bestTarget = bestTargetView.rootDoc;
        let changed = false;
        if (pinProps?.pinDocLayout) {
            if (
                bestTarget.x !== NumCast(activeItem.presX, NumCast(bestTarget.x)) ||
                bestTarget.y !== NumCast(activeItem.presY, NumCast(bestTarget.y)) ||
                bestTarget.rotation !== NumCast(activeItem.presRot, NumCast(bestTarget.rotation)) ||
                bestTarget.width !== NumCast(activeItem.presWidth, NumCast(bestTarget.width)) ||
                bestTarget.height !== NumCast(activeItem.presHeight, NumCast(bestTarget.height))
            ) {
                bestTarget._dataTransition = `all ${transTime}ms`;
                bestTarget.x = NumCast(activeItem.presX, NumCast(bestTarget.x));
                bestTarget.y = NumCast(activeItem.presY, NumCast(bestTarget.y));
                bestTarget.rotation = NumCast(activeItem.presRot, NumCast(bestTarget.rotation));
                bestTarget.width = NumCast(activeItem.presWidth, NumCast(bestTarget.width));
                bestTarget.height = NumCast(activeItem.presHeight, NumCast(bestTarget.height));
                setTimeout(() => (bestTarget._dataTransition = undefined), transTime + 10);
                changed = true;
            }
        }
        if (pinDataTypes.clippable) {
            if (bestTarget._clipWidth !== activeItem.presPinClipWidth) {
                bestTarget._clipWidth = activeItem.presPinClipWidth;
                changed = true;
            }
        }
        if (pinDataTypes.temporal) {
            if (bestTarget._currentTimecode !== activeItem.presStartTime) {
                bestTarget._currentTimecode = activeItem.presStartTime;
                changed = true;
            }
        }
        if (pinDataTypes.scrollable) {
            if (bestTarget._scrollTop !== activeItem.presPinViewScroll) {
                bestTarget._scrollTop = activeItem.presPinViewScroll;
                changed = true;
                const contentBounds = Cast(activeItem.presPinViewBounds, listSpec('number'));
                if (contentBounds) {
                    const dv = DocumentManager.Instance.getDocumentView(bestTarget)?.ComponentView;
                    dv?.brushView?.({ panX: (contentBounds[0] + contentBounds[2]) / 2, panY: (contentBounds[1] + contentBounds[3]) / 2, width: contentBounds[2] - contentBounds[0], height: contentBounds[3] - contentBounds[1] });
                }
            }
        }
        if (pinDataTypes.dataannos) {
            const fkey = Doc.LayoutFieldKey(bestTarget);
            Doc.GetProto(bestTarget)[fkey + '-annotations'] = new List<Doc>([...DocListCast(bestTarget[fkey + '-annotations']).filter(doc => doc.unrendered), ...DocListCast(activeItem.presAnnotations)]);
        }
        if (pinDataTypes.dataview && activeItem.presData !== undefined) {
            const fkey = Doc.LayoutFieldKey(bestTarget);
            Doc.GetProto(bestTarget)[fkey] = activeItem.presData instanceof ObjectField ? activeItem.presData[Copy]() : activeItem.presData;
            bestTarget[fkey + '-useAlt'] = activeItem.presUseAlt;
        }
        if (pinDataTypes.textview && activeItem.presData !== undefined) Doc.GetProto(bestTarget)[Doc.LayoutFieldKey(bestTarget)] = activeItem.presData instanceof ObjectField ? activeItem.presData[Copy]() : activeItem.presData;
        if (pinDataTypes.poslayoutview) {
            changed = true;
            StrListCast(activeItem.presPinLayoutData)
                .map(str => JSON.parse(str) as { id: string; x: number; y: number; w: number; h: number })
                .forEach(data => {
                    const doc = DocServer.GetCachedRefField(data.id) as Doc;
                    doc._dataTransition = `all ${transTime}ms`;
                    doc.x = data.x;
                    doc.y = data.y;
                    doc._width = data.w;
                    doc._height = data.h;
                });
            setTimeout(
                () =>
                    StrListCast(activeItem.presPinLayoutData)
                        .map(str => JSON.parse(str) as { id: string; x: number; y: number; w: number; h: number })
                        .forEach(action(data => ((DocServer.GetCachedRefField(data.id) as Doc)._dataTransition = undefined))),
                transTime + 10
            );
        }
        if (pinDataTypes.pannable) {
            const contentBounds = Cast(activeItem.presPinViewBounds, listSpec('number'));
            if (contentBounds) {
                const viewport = { panX: (contentBounds[0] + contentBounds[2]) / 2, panY: (contentBounds[1] + contentBounds[3]) / 2, width: contentBounds[2] - contentBounds[0], height: contentBounds[3] - contentBounds[1] };
                bestTarget._panX = viewport.panX;
                bestTarget._panY = viewport.panY;
                const dv = DocumentManager.Instance.getDocumentView(bestTarget);
                if (dv) {
                    const computedScale = NumCast(activeItem.presZoom, 1) * Math.min(dv.props.PanelWidth() / viewport.width, dv.props.PanelHeight() / viewport.height);
                    activeItem.presMovement === PresMovement.Zoom && (bestTarget._viewScale = computedScale);
                    dv.ComponentView?.brushView?.(viewport);
                }
            } else {
                if (bestTarget._panX !== activeItem.presPinViewX || bestTarget._panY !== activeItem.presPinViewY || bestTarget._viewScale !== activeItem.presPinViewScale) {
                    bestTarget._panX = activeItem.presPinViewX;
                    bestTarget._panY = activeItem.presPinViewY;
                    bestTarget._viewScale = activeItem.presPinViewScale;
                    changed = true;
                }
            }
        }
        if (changed) {
            return bestTargetView.setViewTransition('all', transTime);
        }
    }

    /// copies values from the targetDoc (which is the prototype of the pinDoc) to
    /// reserved fields on the pinDoc so that those values can be restored to the
    /// target doc when navigating to it.
    @action
    static pinDocView(pinDoc: Doc, pinProps: PinProps, targetDoc: Doc) {
        if (pinProps.pinDocLayout) {
            pinDoc.presPinLayout = true;
            pinDoc.presX = NumCast(targetDoc.x);
            pinDoc.presY = NumCast(targetDoc.y);
            pinDoc.presRot = NumCast(targetDoc.rotation);
            pinDoc.presWidth = NumCast(targetDoc.width);
            pinDoc.presHeight = NumCast(targetDoc.height);
        }
        if (pinProps.pinAudioPlay) pinDoc.presPlayAudio = true;
        if (pinProps.pinData) {
            pinDoc.presPinData =
                pinProps.pinData.scrollable ||
                pinProps.pinData.temporal ||
                pinProps.pinData.pannable ||
                pinProps.pinData.clippable ||
                pinProps.pinData.dataview ||
                pinProps.pinData.textview ||
                pinProps.pinData.poslayoutview ||
                pinProps?.activeFrame !== undefined;
            if (pinProps.pinData.dataview) {
                const fkey = Doc.LayoutFieldKey(targetDoc);
                pinDoc.presUseAlt = targetDoc[fkey + '-useAlt'];
                pinDoc.presData = targetDoc[fkey] instanceof ObjectField ? (targetDoc[fkey] as ObjectField)[Copy]() : targetDoc.data;
            }
            if (pinProps.pinData.dataannos) {
                const fkey = Doc.LayoutFieldKey(targetDoc);
                pinDoc.presAnnotations = new List<Doc>(DocListCast(Doc.GetProto(targetDoc)[fkey + '-annotations']).filter(doc => !doc.unrendered));
            }
            if (pinProps.pinData.textview) pinDoc.presData = targetDoc[Doc.LayoutFieldKey(targetDoc)] instanceof ObjectField ? (targetDoc[Doc.LayoutFieldKey(targetDoc)] as ObjectField)[Copy]() : targetDoc.text;
            if (pinProps.pinData.scrollable) pinDoc.presPinViewScroll = targetDoc._scrollTop;
            if (pinProps.pinData.clippable) pinDoc.presPinClipWidth = targetDoc._clipWidth;
            if (pinProps.pinData.poslayoutview) pinDoc.presPinLayoutData = new List<string>(DocListCast(targetDoc.presData).map(d => JSON.stringify({ id: d[Id], x: NumCast(d.x), y: NumCast(d.y), w: NumCast(d._width), h: NumCast(d._height) })));
            if (pinProps.pinData.pannable) {
                pinDoc.presPinViewX = NumCast(targetDoc._panX);
                pinDoc.presPinViewY = NumCast(targetDoc._panY);
                pinDoc.presPinViewScale = NumCast(targetDoc._viewScale, 1);
            }
            if (pinProps.pinData.temporal) {
                pinDoc.presStartTime = targetDoc._currentTimecode;
                const duration = NumCast(pinDoc[`${Doc.LayoutFieldKey(pinDoc)}-duration`], NumCast(targetDoc.presStartTime) + 0.1);
                pinDoc.presEndTime = NumCast(targetDoc.clipEnd, duration);
            }
        }
        if (pinProps?.pinViewport) {
            // If pinWithView option set then update scale and x / y props of slide
            const bounds = pinProps.pinViewport;
            pinDoc.presPinView = true;
            pinDoc.presPinViewScale = NumCast(targetDoc._viewScale, 1);
            pinDoc.presPinViewX = bounds.left + bounds.width / 2;
            pinDoc.presPinViewY = bounds.top + bounds.height / 2;
            pinDoc.presPinViewBounds = new List<number>([bounds.left, bounds.top, bounds.left + bounds.width, bounds.top + bounds.height]);
        }
    }
    /**
     * This method makes sure that cursor navigates to the element that
     * has the option open and last in the group.
     * Design choice: If the next document is not in presCollection or
     * presCollection itself then if there is a presCollection it will add
     * a new tab. If presCollection is undefined it will open the document
     * on the right.
     */
    navigateToActiveItem = (afterNav?: () => void) => {
        const activeItem: Doc = this.activeItem;
        const targetDoc: Doc = this.targetDoc;
        const finished = () => {
            afterNav?.();
            console.log('Finish Slide Nav: ' + targetDoc.title);
            targetDoc[AnimationSym] = undefined;
        };
        const selViewCache = Array.from(this.selectedArray);
        const dragViewCache = Array.from(this._dragArray);
        const eleViewCache = Array.from(this._eleArray);
        const resetSelection = action(() => {
            if (!this.props.isSelected()) {
                const presDocView = DocumentManager.Instance.getDocumentView(this.rootDoc);
                if (presDocView) SelectionManager.SelectView(presDocView, false);
                this.clearSelectedArray();
                selViewCache.forEach(doc => this.addToSelectedArray(doc));
                this._dragArray.splice(0, this._dragArray.length, ...dragViewCache);
                this._eleArray.splice(0, this._eleArray.length, ...eleViewCache);
            }
            finished();
        });
        const createDocView = (doc: Doc, finished?: () => void) => {
            DocumentManager.Instance.AddViewRenderedCb(doc, () => finished?.());
            LightboxView.AddDocTab(doc, OpenWhere.lightbox);
        };
        PresBox.NavigateToTarget(targetDoc, activeItem, createDocView, resetSelection);
    };

    static NavigateToTarget(targetDoc: Doc, activeItem: Doc, createDocView: any, finished?: () => void) {
        if (activeItem.presMovement === PresMovement.None && targetDoc.type === DocumentType.SCRIPTING) {
            (DocumentManager.Instance.getFirstDocumentView(targetDoc)?.ComponentView as ScriptingBox)?.onRun?.();
            return;
        }
        const options: DocFocusOptions = {
            willPan: activeItem.presMovement !== PresMovement.None,
            willPanZoom: activeItem.presMovement === PresMovement.Zoom || activeItem.presMovement === PresMovement.Jump || activeItem.presMovement === PresMovement.Center,
            zoomScale: activeItem.presMovement === PresMovement.Center ? 0 : NumCast(activeItem.presZoom, 1),
            zoomTime: activeItem.presMovement === PresMovement.Jump ? 0 : NumCast(activeItem.presTransition, 500),
            effect: activeItem,
            noSelect: true,
            originatingDoc: activeItem,
            easeFunc: StrCast(activeItem.presEaseFunc, 'ease') as any,
            zoomTextSelections: BoolCast(activeItem.presZoomText),
            playAudio: BoolCast(activeItem.presPlayAudio),
        };
        const restoreLayout = () => {
            // After navigating to the document, if it is added as a presPinView then it will
            // adjust the pan and scale to that of the pinView when it was added.
            const pinDocLayout = (BoolCast(activeItem.presPinLayout) || BoolCast(activeItem.presPinView)) && DocCast(targetDoc.context)?._currentFrame === undefined;
            if (activeItem.presPinData || activeItem.presPinView || pinDocLayout) {
                // targetDoc may or may not be displayed.  so get the first available document (or alias) view that matches targetDoc and use it
                PresBox.restoreTargetDocView(DocumentManager.Instance.getFirstDocumentView(targetDoc), { pinDocLayout }, activeItem, NumCast(activeItem.presTransition, 500));
            }
        };
        const finishAndRestoreLayout = () => {
            finished?.();
            restoreLayout();
        };
        const containerDocContext = DocumentManager.GetContextPath(targetDoc);

        let context = containerDocContext.length ? containerDocContext[0] : targetDoc;
        if (activeItem.presOpenInLightbox) {
            if (!DocumentManager.Instance.getLightboxDocumentView(DocCast(DocCast(targetDoc.annotationOn) ?? targetDoc))) {
                context = DocCast(targetDoc.annotationOn) ?? targetDoc;
                LightboxView.SetLightboxDoc(context); // openInTab(targetDoc);
            }
        }
        if (targetDoc) {
            if (activeItem.presentationTargetDoc instanceof Doc) activeItem.presentationTargetDoc[AnimationSym] = undefined;

            DocumentManager.Instance.AddViewRenderedCb(LightboxView.LightboxDoc, dv => {
                if (!DocumentManager.Instance.getLightboxDocumentView(DocCast(context.annotationOn) ?? context)) {
                    LightboxView.SetLightboxDoc(undefined);
                }
                DocumentManager.Instance.jumpToDocument(targetDoc, options, createDocView, containerDocContext, finishAndRestoreLayout);
            });
        } else finishAndRestoreLayout();
    }

    /**
     * For 'Hide Before' and 'Hide After' buttons making sure that
     * they are hidden each time the presentation is updated.
     */
    @action
    onHideDocument = () => {
        this.childDocs.forEach((doc, index) => {
            const curDoc = Cast(doc, Doc, null);
            const tagDoc = Cast(curDoc.presentationTargetDoc, Doc, null);
            const itemIndexes: number[] = this.getAllIndexes(this.tagDocs, tagDoc);
            if (curDoc.presHide) {
                if (index !== this.itemIndex) {
                    tagDoc.opacity = 1;
                }
            }
            const hidingIndBef = itemIndexes.find(item => item >= this.itemIndex);
            if (curDoc.presHideBefore && index === hidingIndBef) {
                if (index > this.itemIndex) {
                    tagDoc.opacity = 0;
                } else if (index === this.itemIndex || !curDoc.presHideAfter) {
                    tagDoc.opacity = 1;
                }
            }
            const hidingIndAft = itemIndexes
                .slice()
                .reverse()
                .find(item => item < this.itemIndex);
            if (curDoc.presHideAfter && index === hidingIndAft) {
                if (index < this.itemIndex) {
                    tagDoc.opacity = 0;
                } else if (index === this.itemIndex || !curDoc.presHideBefore) {
                    tagDoc.opacity = 1;
                }
            }
            const hidingInd = itemIndexes.find(item => item === this.itemIndex);
            if (curDoc.presHide && index === hidingInd) {
                if (index === this.itemIndex) {
                    tagDoc.opacity = 0;
                }
            }
        });
    };

    _exitTrail: Opt<() => void>;
    PlayTrail = (docs: Doc[]) => {
        const savedStates = docs.map(doc => (doc._viewType !== CollectionViewType.Freeform ? undefined : { c: doc, x: NumCast(doc.panX), y: NumCast(doc.panY), s: NumCast(doc.viewScale) }));
        this.startPresentation(0);
        this._exitTrail = () => {
            savedStates
                .filter(savedState => savedState)
                .map(savedState => {
                    const { x, y, s, c } = savedState!;
                    c._panX = x;
                    c._panY = y;
                    c._viewScale = s;
                });
            LightboxView.SetLightboxDoc(undefined);
            Doc.RemoveDocFromList(Doc.MyOverlayDocs, undefined, this.rootDoc);
            return PresStatus.Edit;
        };
    };

    // The function pauses the auto presentation
    @action
    pauseAutoPres = () => {
        if (this.layoutDoc.presStatus === PresStatus.Autoplay) {
            if (this._presTimer) clearTimeout(this._presTimer);
            this.layoutDoc.presStatus = PresStatus.Manual;
            this.childDocs.forEach(this.stopTempMedia);
        }
    };

    //The function that resets the presentation by removing every action done by it. It also
    //stops the presentaton.
    resetPresentation = () => {
        this.childDocs
            .map(doc => Cast(doc.presentationTargetDoc, Doc, null))
            .filter(doc => doc instanceof Doc)
            .forEach(doc => {
                try {
                    doc.opacity = 1;
                } catch (e) {
                    console.log('Reset presentation error: ', e);
                }
            });
    };

    // The function allows for viewing the pres path on toggle
    @action togglePath = (off?: boolean) => {
        this._pathBoolean = off ? false : !this._pathBoolean;
        CollectionFreeFormView.ShowPresPaths = this._pathBoolean;
    };

    // The function allows for expanding the view of pres on toggle
    @action toggleExpandMode = () => {
        runInAction(() => (this._expandBoolean = !this._expandBoolean));
        this.rootDoc.expandBoolean = this._expandBoolean;
        this.childDocs.forEach(doc => {
            doc.presExpandInlineButton = this._expandBoolean;
        });
    };

    /**
     * The function that starts the presentation at the given index, also checking if actions should be applied
     * directly at start.
     * @param startIndex: index that the presentation will start at
     */
    startPresentation = (startIndex: number) => {
        clearTimeout(this._presTimer);
        if (this.childDocs.length) {
            this.layoutDoc.presStatus = PresStatus.Autoplay;
            this.childDocs.forEach(doc => {
                const tagDoc = doc.presentationTargetDoc as Doc;
                if (doc.presHideBefore && this.childDocs.indexOf(doc) > startIndex) tagDoc.opacity = 0;
                if (doc.presHideAfter && this.childDocs.indexOf(doc) < startIndex) tagDoc.opacity = 0;
                // if (doc.presHide && this.childDocs.indexOf(doc) === startIndex)  tagDoc.opacity = 0;
            });
            const func = () => {
                const delay = NumCast(this.activeItem.presDuration, this.activeItem.type === DocumentType.SCRIPTING ? 0 : 2500) + NumCast(this.activeItem.presTransition);
                this._presTimer = setTimeout(() => {
                    if (!this.next()) this.layoutDoc.presStatus = this._exitTrail?.() ?? PresStatus.Manual;
                    this.layoutDoc.presStatus === PresStatus.Autoplay && func();
                }, delay);
            };
            this.gotoDocument(startIndex, this.activeItem, undefined, func);
        }
    };

    /**
     * The method called to open the presentation as a minimized view
     */
    @action
    enterMinimize = () => {
        clearTimeout(this._presTimer);
        const pt = this.props.ScreenToLocalTransform().inverse().transformPoint(0, 0);
        this.props.removeDocument?.(this.layoutDoc);
        return PresBox.OpenPresMinimized(this.rootDoc, [pt[0] + (this.props.PanelWidth() - 250), pt[1] + 10]);
    };
    exitMinimize = () => {
        if (DocListCast(Doc.MyOverlayDocs?.data).includes(this.layoutDoc)) {
            Doc.RemoveDocFromList(Doc.MyOverlayDocs, undefined, this.rootDoc);
            CollectionDockingView.AddSplit(this.rootDoc, OpenWhereMod.right);
        }
        return PresStatus.Edit;
    };

    public static minimizedWidth = 198;
    public static OpenPresMinimized(doc: Doc, pt: number[]) {
        doc.overlayX = pt[0];
        doc.overlayY = pt[1];
        doc._height = 30;
        doc._width = PresBox.minimizedWidth;
        Doc.AddDocToList(Doc.MyOverlayDocs, undefined, doc);
        return (doc.presStatus = PresStatus.Manual);
    }

    /**
     * Called when the user changes the view type
     * Either 'List' (stacking) or 'Slides' (carousel)
     */
    @undoBatch
    viewChanged = action((e: React.ChangeEvent) => {
        //@ts-ignore
        const viewType = e.target.selectedOptions[0].value as CollectionViewType;
        this.layoutDoc.presFieldKey = this.fieldKey + (viewType === CollectionViewType.Tree ? '-linearized' : '');
        // pivot field may be set by the user in timeline view (or some other way) -- need to reset it here
        [CollectionViewType.Tree || CollectionViewType.Stacking].includes(viewType) && (this.rootDoc._pivotField = undefined);
        this.rootDoc._viewType = viewType;
        if (this.isTreeOrStack) {
            this.layoutDoc._gridGap = 0;
        }
    });

    /**
     * Called when the user changes the view type
     * Either 'List' (stacking) or 'Slides' (carousel)
     */
    // @undoBatch
    mediaStopChanged = action((e: React.ChangeEvent) => {
        const activeItem: Doc = this.activeItem;
        //@ts-ignore
        const stopDoc = e.target.selectedOptions[0].value as string;
        const stopDocIndex: number = Number(stopDoc[0]);
        activeItem.mediaStopDoc = stopDocIndex;
        if (this.childDocs[stopDocIndex - 1].mediaStopTriggerList) {
            const list = DocListCast(this.childDocs[stopDocIndex - 1].mediaStopTriggerList);
            list.push(activeItem);
            // this.childDocs[stopDocIndex - 1].mediaStopTriggerList = list;\
        } else {
            this.childDocs[stopDocIndex - 1].mediaStopTriggerList = new List<Doc>();
            const list = DocListCast(this.childDocs[stopDocIndex - 1].mediaStopTriggerList);
            list.push(activeItem);
            // this.childDocs[stopDocIndex - 1].mediaStopTriggerList = list;
        }
    });

    movementName = action((activeItem: Doc) => {
        if (![PresMovement.Zoom, PresMovement.Pan, PresMovement.Center, PresMovement.Jump, PresMovement.None].includes(StrCast(activeItem.presMovement) as any)) {
            return PresMovement.Zoom;
        }
        return StrCast(activeItem.presMovement);
    });

    whenChildContentsActiveChanged = action((isActive: boolean) => this.props.whenChildContentsActiveChanged((this._isChildActive = isActive)));
    // For dragging documents into the presentation trail
    addDocumentFilter = (docs: Doc[]) => {
        docs.forEach((doc, i) => {
            if (doc.presentationTargetDoc) return true;
            if (doc.type === DocumentType.LABEL) {
                const audio = Cast(doc.annotationOn, Doc, null);
                if (audio) {
                    audio.mediaStart = 'manual';
                    audio.mediaStop = 'manual';
                    audio.presStartTime = NumCast(doc._timecodeToShow /* audioStart */, NumCast(doc._timecodeToShow /* videoStart */));
                    audio.presEndTime = NumCast(doc._timecodeToHide /* audioEnd */, NumCast(doc._timecodeToHide /* videoEnd */));
                    audio.presDuration = audio.presStartTime - audio.presEndTime;
                    TabDocView.PinDoc(audio, { audioRange: true });
                    setTimeout(() => this.removeDocument(doc), 0);
                    return false;
                }
            } else {
                if (!doc.aliasOf) {
                    const original = Doc.MakeAlias(doc);
                    TabDocView.PinDoc(original, {});
                    setTimeout(() => this.removeDocument(doc), 0);
                    return false;
                } else {
                    if (!doc.presentationTargetDoc) doc.title = doc.title + ' - Slide';
                    doc.aliasOf instanceof Doc && (doc.presentationTargetDoc = doc.aliasOf);
                    doc.presMovement = PresMovement.Zoom;
                    if (this._expandBoolean) doc.presExpandInlineButton = true;
                }
            }
        });
        return true;
    };
    childLayoutTemplate = () => (!this.isTreeOrStack ? undefined : DocCast(Doc.UserDoc().presElement));
    removeDocument = (doc: Doc) => Doc.RemoveDocFromList(this.rootDoc, this.fieldKey, doc);
    getTransform = () => this.props.ScreenToLocalTransform().translate(-5, -65); // listBox padding-left and pres-box-cont minHeight
    panelHeight = () => this.props.PanelHeight() - 40;
    isContentActive = (outsideReaction?: boolean) =>
        Doc.ActiveTool === InkTool.None && !this.layoutDoc._lockedPosition && (this.layoutDoc.forceActive || this.props.isSelected(outsideReaction) || this._isChildActive || this.props.renderDepth === 0) ? true : false;

    /**
     * For sorting the array so that the order is maintained when it is dropped.
     */
    sortArray = () => this.childDocs.filter(doc => this.selectedArray.has(doc));

    /**
     * Method to get the list of selected items in the order in which they have been selected
     */
    @computed get listOfSelected() {
        return Array.from(this.selectedArray).map((doc: Doc, index: any) => {
            const curDoc = Cast(doc, Doc, null);
            const tagDoc = Cast(curDoc.presentationTargetDoc, Doc, null);
            if (curDoc && curDoc === this.activeItem)
                return (
                    <div key={index} className="selectedList-items">
                        <b>
                            {index + 1}. {curDoc.title}
                        </b>
                    </div>
                );
            else if (tagDoc)
                return (
                    <div key={index} className="selectedList-items">
                        {index + 1}. {curDoc.title}
                    </div>
                );
            else if (curDoc)
                return (
                    <div key={index} className="selectedList-items">
                        {index + 1}. {curDoc.title}
                    </div>
                );
        });
    }

    @action
    selectPres = () => {
        const presDocView = DocumentManager.Instance.getDocumentView(this.rootDoc);
        presDocView && SelectionManager.SelectView(presDocView, false);
    };

    //Regular click
    @action
    selectElement = async (doc: Doc) => {
        CollectionStackedTimeline.CurrentlyPlaying?.map((clip, i) => DocumentManager.Instance.getDocumentView(clip)?.ComponentView?.Pause?.());
        this.gotoDocument(this.childDocs.indexOf(doc), this.activeItem);
        // if (doc.presPinView) setTimeout(() => this.updateCurrentPresentation(DocCast(doc.context)), 0);
        // else
        this.updateCurrentPresentation(DocCast(doc.context));
    };

    //Command click
    @action
    multiSelect = (doc: Doc, ref: HTMLElement, drag: HTMLElement) => {
        if (!this.selectedArray.has(doc)) {
            this.addToSelectedArray(doc);
            this._eleArray.push(ref);
            this._dragArray.push(drag);
        } else {
            this.removeFromSelectedArray(doc);
            this._eleArray.splice(this._eleArray.indexOf(ref));
            this._dragArray.splice(this._dragArray.indexOf(drag));
        }
        this.selectPres();
    };

    //Shift click
    @action
    shiftSelect = (doc: Doc, ref: HTMLElement, drag: HTMLElement) => {
        this.clearSelectedArray();
        // const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
        if (this.activeItem) {
            for (let i = Math.min(this.itemIndex, this.childDocs.indexOf(doc)); i <= Math.max(this.itemIndex, this.childDocs.indexOf(doc)); i++) {
                this.addToSelectedArray(this.childDocs[i]);
                this._eleArray.push(ref);
                this._dragArray.push(drag);
            }
        }
        this.selectPres();
    };

    //regular click
    @action
    regularSelect = (doc: Doc, ref: HTMLElement, drag: HTMLElement, focus: boolean, selectPres = true) => {
        this.clearSelectedArray();
        this.addToSelectedArray(doc);
        this._eleArray.splice(0, this._eleArray.length, ref);
        this._dragArray.splice(0, this._dragArray.length, drag);
        focus && this.selectElement(doc);
        selectPres && this.selectPres();
    };

    modifierSelect = (doc: Doc, ref: HTMLElement, drag: HTMLElement, focus: boolean, cmdClick: boolean, shiftClick: boolean) => {
        if (cmdClick) this.multiSelect(doc, ref, drag);
        else if (shiftClick) this.shiftSelect(doc, ref, drag);
        else this.regularSelect(doc, ref, drag, focus);
    };

    static keyEventsWrapper = (e: KeyboardEvent) => PresBox.Instance?.keyEvents(e);

    // Key for when the presentaiton is active
    @action
    keyEvents = (e: KeyboardEvent) => {
        if (e.target instanceof HTMLInputElement) return;
        let handled = false;
        const anchorNode = document.activeElement as HTMLDivElement;
        if (anchorNode && anchorNode.className?.includes('lm_title')) return;
        switch (e.key) {
            case 'Backspace':
                if (this.layoutDoc.presStatus === 'edit') {
                    undoBatch(
                        action(() => {
                            for (const doc of this.selectedArray) {
                                this.removeDocument(doc);
                            }
                            this.clearSelectedArray();
                            this._eleArray.length = 0;
                            this._dragArray.length = 0;
                        })
                    )();
                    handled = true;
                }
                break;
            case 'Escape':
                if (DocListCast(Doc.MyOverlayDocs?.data).includes(this.layoutDoc)) {
                    this.exitClicked();
                } else if (this.layoutDoc.presStatus === PresStatus.Edit) {
                    this.clearSelectedArray();
                    this._eleArray.length = this._dragArray.length = 0;
                } else {
                    this.layoutDoc.presStatus = PresStatus.Edit;
                }
                if (this._presTimer) clearTimeout(this._presTimer);
                handled = true;
                break;
            case 'Down':
            case 'ArrowDown':
            case 'Right':
            case 'ArrowRight':
                if (e.shiftKey && this.itemIndex < this.childDocs.length - 1) {
                    // TODO: update to work properly
                    this.rootDoc._itemIndex = NumCast(this.rootDoc._itemIndex) + 1;
                    this.addToSelectedArray(this.childDocs[this.rootDoc._itemIndex]);
                } else {
                    this.next();
                    if (this._presTimer) {
                        clearTimeout(this._presTimer);
                        this.layoutDoc.presStatus = PresStatus.Manual;
                    }
                }
                handled = true;
                break;
            case 'Up':
            case 'ArrowUp':
            case 'Left':
            case 'ArrowLeft':
                if (e.shiftKey && this.itemIndex !== 0) {
                    // TODO: update to work properly
                    this.rootDoc._itemIndex = NumCast(this.rootDoc._itemIndex) - 1;
                    this.addToSelectedArray(this.childDocs[this.rootDoc._itemIndex]);
                } else {
                    this.back();
                    if (this._presTimer) {
                        clearTimeout(this._presTimer);
                        this.layoutDoc.presStatus = PresStatus.Manual;
                    }
                }
                handled = true;
                break;
            case 'Spacebar':
            case ' ':
                if (this.layoutDoc.presStatus === PresStatus.Manual) this.startOrPause(true);
                else if (this.layoutDoc.presStatus === PresStatus.Autoplay) if (this._presTimer) clearTimeout(this._presTimer);
                handled = true;
                break;
            case 'a':
                if ((e.metaKey || e.altKey) && this.layoutDoc.presStatus === 'edit') {
                    this.clearSelectedArray();
                    this.childDocs.forEach(doc => this.addToSelectedArray(doc));
                    handled = true;
                }
            default:
                break;
        }
        if (handled) {
            e.stopPropagation();
            e.preventDefault();
        }
    };

    getAllIndexes = (arr: Doc[], val: Doc) => arr.map((doc, i) => (doc === val ? i : -1)).filter(i => i !== -1);

    // Adds the index in the pres path graphically
    @computed get order() {
        const order: JSX.Element[] = [];
        const docs: Doc[] = [];
        const presCollection = DocumentManager.GetContextPath(this.activeItem).reverse().lastElement();
        const dv = DocumentManager.Instance.getDocumentView(presCollection);
        this.childDocs
            .filter(doc => Cast(doc.presentationTargetDoc, Doc, null))
            .forEach((doc, index) => {
                const tagDoc = Cast(doc.presentationTargetDoc, Doc, null);
                const srcContext = Cast(tagDoc.context, Doc, null);
                const width = NumCast(tagDoc._width) / 10;
                const height = Math.max(NumCast(tagDoc._height) / 10, 15);
                const edge = Math.max(width, height);
                const fontSize = edge * 0.8;
                const gap = 2;
                if (presCollection === srcContext) {
                    // Case A: Document is contained within the collection
                    if (docs.includes(tagDoc)) {
                        const prevOccurances: number = this.getAllIndexes(docs, tagDoc).length;
                        docs.push(tagDoc);
                        order.push(
                            <div
                                className="pathOrder"
                                key={tagDoc.id + 'pres' + index}
                                style={{ top: NumCast(tagDoc.y) + (prevOccurances * (edge + gap) - edge / 2), left: NumCast(tagDoc.x) - edge / 2, width: edge, height: edge, fontSize: fontSize }}
                                onClick={() => this.selectElement(doc)}>
                                <div className="pathOrder-frame">{index + 1}</div>
                            </div>
                        );
                    } else {
                        docs.push(tagDoc);
                        order.push(
                            <div
                                className="pathOrder"
                                key={tagDoc.id + 'pres' + index}
                                style={{ top: NumCast(tagDoc.y) - edge / 2, left: NumCast(tagDoc.x) - edge / 2, width: edge, height: edge, fontSize: fontSize }}
                                onClick={() => this.selectElement(doc)}>
                                <div className="pathOrder-frame">{index + 1}</div>
                            </div>
                        );
                    }
                } else if (doc.presPinView && presCollection === tagDoc && dv) {
                    // Case B: Document is presPinView and is presCollection
                    const scale: number = 1 / NumCast(doc.presPinViewScale);
                    const height: number = dv.props.PanelHeight() * scale;
                    const width: number = dv.props.PanelWidth() * scale;
                    const indWidth = width / 10;
                    const indHeight = Math.max(height / 10, 15);
                    const indEdge = Math.max(indWidth, indHeight);
                    const indFontSize = indEdge * 0.8;
                    const xLoc: number = NumCast(doc.presPinViewX) - width / 2;
                    const yLoc: number = NumCast(doc.presPinViewY) - height / 2;
                    docs.push(tagDoc);
                    order.push(
                        <>
                            <div className="pathOrder" key={tagDoc.id + 'pres' + index} style={{ top: yLoc - indEdge / 2, left: xLoc - indEdge / 2, width: indEdge, height: indEdge, fontSize: indFontSize }} onClick={() => this.selectElement(doc)}>
                                <div className="pathOrder-frame">{index + 1}</div>
                            </div>
                            <div className="pathOrder-presPinView" style={{ top: yLoc, left: xLoc, width: width, height: height, borderWidth: indEdge / 10 }}></div>
                        </>
                    );
                }
            });
        return order;
    }

    /**
     * Method called for viewing paths which adds a single line with
     * points at the center of each document added.
     * Design choice: When this is called it sets _fitContentsToBox as true so the
     * user can have an overview of all of the documents in the collection.
     * (Design needed for when documents in presentation trail are in another
     * collection)
     */
    @computed get paths() {
        let pathPoints = '';
        this.childDocs.forEach((doc, index) => {
            const tagDoc = Cast(doc.presentationTargetDoc, Doc, null);
            if (tagDoc) {
                const n1x = NumCast(tagDoc.x) + NumCast(tagDoc._width) / 2;
                const n1y = NumCast(tagDoc.y) + NumCast(tagDoc._height) / 2;
                if ((index = 0)) pathPoints = n1x + ',' + n1y;
                else pathPoints = pathPoints + ' ' + n1x + ',' + n1y;
            } else if (doc.presPinView) {
                const n1x = NumCast(doc.presPinViewX);
                const n1y = NumCast(doc.presPinViewY);
                if ((index = 0)) pathPoints = n1x + ',' + n1y;
                else pathPoints = pathPoints + ' ' + n1x + ',' + n1y;
            }
        });
        return (
            <polyline
                points={pathPoints}
                style={{
                    opacity: 1,
                    stroke: '#69a6db',
                    strokeWidth: 5,
                    strokeDasharray: '10 5',
                    boxShadow: '0px 4px 4px rgba(0, 0, 0, 0.25)',
                }}
                fill="none"
                markerStart="url(#markerArrow)"
                markerMid="url(#markerSquare)"
                markerEnd="url(#markerSquareFilled)"
            />
        );
    }
    getPaths = (collection: Doc) => this.paths; // needs to be smarter and figure out the paths to draw for this specific collection.  or better yet, draw everything in an overlay layer instad of within a collection

    // Converts seconds to ms and updates presTransition
    public static SetTransitionTime = (number: String, setter: (timeInMS: number) => void, change?: number) => {
        let timeInMS = Number(number) * 1000;
        if (change) timeInMS += change;
        if (timeInMS < 100) timeInMS = 100;
        if (timeInMS > 100000) timeInMS = 100000;
        setter(timeInMS);
    };
    setTransitionTime = (number: String, change?: number) => {
        PresBox.SetTransitionTime(number, (timeInMS: number) => this.selectedArray.forEach(doc => (doc.presTransition = timeInMS)), change);
    };

    // Converts seconds to ms and updates presTransition
    setZoom = (number: String, change?: number) => {
        let scale = Number(number) / 100;
        if (change) scale += change;
        if (scale < 0.01) scale = 0.01;
        if (scale > 1) scale = 1;
        this.selectedArray.forEach(doc => (doc.presZoom = scale));
    };

    // Converts seconds to ms and updates presDuration
    setDurationTime = (number: String, change?: number) => {
        let timeInMS = Number(number) * 1000;
        if (change) timeInMS += change;
        if (timeInMS < 100) timeInMS = 100;
        if (timeInMS > 20000) timeInMS = 20000;
        this.selectedArray.forEach(doc => (doc.presDuration = timeInMS));
    };

    /**
     * When the movement dropdown is changes
     */
    @undoBatch
    updateMovement = action((movement: PresMovement, all?: boolean) => (all ? this.childDocs : this.selectedArray).forEach(doc => (doc.presMovement = movement)));

    @undoBatch
    @action
    updateHideBefore = (activeItem: Doc) => {
        activeItem.presHideBefore = !activeItem.presHideBefore;
        this.selectedArray.forEach(doc => (doc.presHideBefore = activeItem.presHideBefore));
    };

    @undoBatch
    @action
    updateHide = (activeItem: Doc) => {
        activeItem.presHide = !activeItem.presHide;
        this.selectedArray.forEach(doc => (doc.presHide = activeItem.presHide));
    };

    @undoBatch
    @action
    updateHideAfter = (activeItem: Doc) => {
        activeItem.presHideAfter = !activeItem.presHideAfter;
        this.selectedArray.forEach(doc => (doc.presHideAfter = activeItem.presHideAfter));
    };

    @undoBatch
    @action
    updateOpenDoc = (activeItem: Doc) => {
        activeItem.presOpenInLightbox = !activeItem.presOpenInLightbox;
        this.selectedArray.forEach(doc => (doc.presOpenInLightbox = activeItem.presOpenInLightbox));
    };
    @undoBatch
    @action
    updateEaseFunc = (activeItem: Doc) => {
        activeItem.presEaseFunc = activeItem.presEaseFunc === 'linear' ? 'ease' : 'linear';
        this.selectedArray.forEach(doc => (doc.presEaseFunc = activeItem.presEaseFunc));
    };

    @undoBatch
    @action
    updateEffectDirection = (effect: PresEffectDirection, all?: boolean) => (all ? this.childDocs : this.selectedArray).forEach(doc => (doc.presEffectDirection = effect));

    @undoBatch
    @action
    updateEffect = (effect: PresEffect, all?: boolean) => (all ? this.childDocs : this.selectedArray).forEach(doc => (doc.presEffect = effect));

    _batch: UndoManager.Batch | undefined = undefined;

    public static inputter = (min: string, step: string, max: string, value: number, active: boolean, change: (val: string) => void, hmargin?: number) => {
        let batch: any;
        return (
            <input
                type="range"
                step={step}
                min={min}
                max={max}
                value={value}
                style={{ marginLeft: hmargin, marginRight: hmargin, width: `calc(100% - ${2 * (hmargin ?? 0)}px)` }}
                className={`toolbar-slider ${active ? '' : 'none'}`}
                onPointerDown={e => {
                    batch = UndoManager.StartBatch('pres slider');
                    e.stopPropagation();
                }}
                onPointerUp={() => batch?.end()}
                onChange={e => {
                    e.stopPropagation();
                    change(e.target.value);
                }}
            />
        );
    };
    @computed get transitionDropdown() {
        const activeItem: Doc = this.activeItem;
        const targetDoc: Doc = this.targetDoc;
        const presEffect = (effect: PresEffect) => (
            <div className={`presBox-dropdownOption ${activeItem.presEffect === effect || (effect === PresEffect.None && !activeItem.presEffect) ? 'active' : ''}`} onPointerDown={StopEvent} onClick={() => this.updateEffect(effect)}>
                {effect}
            </div>
        );
        const presMovement = (movement: PresMovement) => (
            <div className={`presBox-dropdownOption ${activeItem.presMovement === movement ? 'active' : ''}`} onPointerDown={StopEvent} onClick={() => this.updateMovement(movement)}>
                {movement}
            </div>
        );
        const presDirection = (direction: PresEffectDirection, icon: string, gridColumn: number, gridRow: number, opts: object) => {
            const color = activeItem.presEffectDirection === direction || (direction === PresEffectDirection.Center && !activeItem.presEffectDirection) ? Colors.LIGHT_BLUE : 'black';
            return (
                <Tooltip title={<div className="dash-tooltip">{direction}</div>}>
                    <div
                        style={{ ...opts, border: direction === PresEffectDirection.Center ? `solid 2px ${color}` : undefined, borderRadius: '100%', cursor: 'pointer', gridColumn, gridRow, justifySelf: 'center', color }}
                        onClick={() => this.updateEffectDirection(direction)}>
                        {icon ? <FontAwesomeIcon icon={icon as any} /> : null}
                    </div>
                </Tooltip>
            );
        };
        if (activeItem && targetDoc) {
            const type = targetDoc.type;
            const transitionSpeed = activeItem.presTransition ? NumCast(activeItem.presTransition) / 1000 : 0.5;
            const zoom = NumCast(activeItem.presZoom, 1) * 100;
            let duration = activeItem.presDuration ? NumCast(activeItem.presDuration) / 1000 : 0;
            if (activeItem.type === DocumentType.AUDIO) duration = NumCast(activeItem.duration);
            const effect = activeItem.presEffect ? activeItem.presEffect : PresMovement.None;
            // activeItem.presMovement = activeItem.presMovement ? activeItem.presMovement : PresMovement.Zoom;
            return (
                <div
                    className={`presBox-ribbon ${this._transitionTools && this.layoutDoc.presStatus === PresStatus.Edit ? 'active' : ''}`}
                    onPointerDown={StopEvent}
                    onPointerUp={StopEvent}
                    onClick={action(e => {
                        e.stopPropagation();
                        this._openMovementDropdown = false;
                        this._openEffectDropdown = false;
                    })}>
                    <div className="ribbon-box">
                        Movement
                        <div
                            className="presBox-dropdown"
                            onClick={action(e => {
                                e.stopPropagation();
                                this._openMovementDropdown = !this._openMovementDropdown;
                            })}
                            style={{ borderBottomLeftRadius: this._openMovementDropdown ? 0 : 5, border: this._openMovementDropdown ? `solid 2px ${Colors.MEDIUM_BLUE}` : 'solid 1px black' }}>
                            {this.movementName(activeItem)}
                            <FontAwesomeIcon className="presBox-dropdownIcon" style={{ gridColumn: 2, color: this._openMovementDropdown ? Colors.MEDIUM_BLUE : 'black' }} icon={'angle-down'} />
                            <div className={'presBox-dropdownOptions'} id={'presBoxMovementDropdown'} onPointerDown={StopEvent} style={{ display: this._openMovementDropdown ? 'grid' : 'none' }}>
                                {presMovement(PresMovement.None)}
                                {presMovement(PresMovement.Center)}
                                {presMovement(PresMovement.Zoom)}
                                {presMovement(PresMovement.Pan)}
                                {presMovement(PresMovement.Jump)}
                            </div>
                        </div>
                        <div className="ribbon-doubleButton" style={{ display: activeItem.presMovement === PresMovement.Zoom ? 'inline-flex' : 'none' }}>
                            <div className="presBox-subheading">Zoom (% screen filled)</div>
                            <div className="ribbon-property">
                                <input className="presBox-input" type="number" value={zoom} onChange={action(e => this.setZoom(e.target.value))} />%
                            </div>
                            <div className="ribbon-propertyUpDown">
                                <div className="ribbon-propertyUpDownItem" onClick={undoBatch(() => this.setZoom(String(zoom), 0.1))}>
                                    <FontAwesomeIcon icon={'caret-up'} />
                                </div>
                                <div className="ribbon-propertyUpDownItem" onClick={undoBatch(() => this.setZoom(String(zoom), -0.1))}>
                                    <FontAwesomeIcon icon={'caret-down'} />
                                </div>
                            </div>
                        </div>
                        {PresBox.inputter('0', '1', '100', zoom, activeItem.presMovement === PresMovement.Zoom, this.setZoom)}
                        <div className="ribbon-doubleButton" style={{ display: 'inline-flex' }}>
                            <div className="presBox-subheading">Transition Speed</div>
                            <div className="ribbon-property">
                                <input className="presBox-input" type="number" value={transitionSpeed} onKeyDown={e => e.stopPropagation()} onChange={action(e => this.setTransitionTime(e.target.value))} /> s
                            </div>
                            <div className="ribbon-propertyUpDown">
                                <div className="ribbon-propertyUpDownItem" onClick={undoBatch(() => this.setTransitionTime(String(transitionSpeed), 1000))}>
                                    <FontAwesomeIcon icon={'caret-up'} />
                                </div>
                                <div className="ribbon-propertyUpDownItem" onClick={undoBatch(() => this.setTransitionTime(String(transitionSpeed), -1000))}>
                                    <FontAwesomeIcon icon={'caret-down'} />
                                </div>
                            </div>
                        </div>
                        {PresBox.inputter('0.1', '0.1', '100', transitionSpeed, true, this.setTransitionTime)}
                        <div className={'slider-headers'}>
                            <div className="slider-text">Fast</div>
                            <div className="slider-text">Medium</div>
                            <div className="slider-text">Slow</div>
                        </div>
                    </div>
                    <div className="ribbon-box">
                        Visibility {'&'} Duration
                        <div className="ribbon-doubleButton">
                            <Tooltip title={<div className="dash-tooltip">{'Hide before presented'}</div>}>
                                <div className={`ribbon-toggle ${activeItem.presHideBefore ? 'active' : ''}`} onClick={() => this.updateHideBefore(activeItem)}>
                                    Hide before
                                </div>
                            </Tooltip>
                            <Tooltip title={<div className="dash-tooltip">{'Hide while presented'}</div>}>
                                <div className={`ribbon-toggle ${activeItem.presHide ? 'active' : ''}`} onClick={() => this.updateHide(activeItem)}>
                                    Hide
                                </div>
                            </Tooltip>

                            <Tooltip title={<div className="dash-tooltip">{'Hide after presented'}</div>}>
                                <div className={`ribbon-toggle ${activeItem.presHideAfter ? 'active' : ''}`} onClick={() => this.updateHideAfter(activeItem)}>
                                    Hide after
                                </div>
                            </Tooltip>

                            <Tooltip title={<div className="dash-tooltip">{'Open in lightbox view'}</div>}>
                                <div className="ribbon-toggle" style={{ backgroundColor: activeItem.presOpenInLightbox ? Colors.LIGHT_BLUE : '' }} onClick={() => this.updateOpenDoc(activeItem)}>
                                    Lightbox
                                </div>
                            </Tooltip>
                            <Tooltip title={<div className="dash-tooltip">{'Transition movement style'}</div>}>
                                <div className="ribbon-toggle" onClick={() => this.updateEaseFunc(activeItem)}>
                                    {`${StrCast(activeItem.presEaseFunc, 'ease')}`}
                                </div>
                            </Tooltip>
                        </div>
                        {type === DocumentType.AUDIO || type === DocumentType.VID ? null : (
                            <>
                                <div className="ribbon-doubleButton">
                                    <div className="presBox-subheading">Slide Duration</div>
                                    <div className="ribbon-property">
                                        <input className="presBox-input" type="number" value={duration} onKeyDown={e => e.stopPropagation()} onChange={action(e => this.setDurationTime(e.target.value))} /> s
                                    </div>
                                    <div className="ribbon-propertyUpDown">
                                        <div className="ribbon-propertyUpDownItem" onClick={undoBatch(() => this.setDurationTime(String(duration), 1000))}>
                                            <FontAwesomeIcon icon={'caret-up'} />
                                        </div>
                                        <div className="ribbon-propertyUpDownItem" onClick={undoBatch(() => this.setDurationTime(String(duration), -1000))}>
                                            <FontAwesomeIcon icon={'caret-down'} />
                                        </div>
                                    </div>
                                </div>
                                {PresBox.inputter('0.1', '0.1', '20', duration, targetDoc.type !== DocumentType.AUDIO, this.setDurationTime)}
                                <div className={'slider-headers'} style={{ display: targetDoc.type === DocumentType.AUDIO ? 'none' : 'grid' }}>
                                    <div className="slider-text">Short</div>
                                    <div className="slider-text">Medium</div>
                                    <div className="slider-text">Long</div>
                                </div>
                            </>
                        )}
                    </div>
                    <div className="ribbon-box">
                        Effects
                        <div className="ribbon-doubleButton" style={{ display: 'inline-flex' }}>
                            <div className="presBox-subheading">Play Audio Annotation</div>
                            <input className="presBox-checkbox" style={{ margin: 10 }} type="checkbox" onChange={() => (activeItem.presPlayAudio = !BoolCast(activeItem.presPlayAudio))} checked={BoolCast(activeItem.presPlayAudio)} />
                        </div>
                        <div className="ribbon-doubleButton" style={{ display: 'inline-flex' }}>
                            <div className="presBox-subheading">Zoom Text Selections</div>
                            <input className="presBox-checkbox" style={{ margin: 10 }} type="checkbox" onChange={() => (activeItem.presZoomText = !BoolCast(activeItem.presZoomText))} checked={BoolCast(activeItem.presZoomText)} />
                        </div>
                        <div
                            className="presBox-dropdown"
                            onClick={action(e => {
                                e.stopPropagation();
                                this._openEffectDropdown = !this._openEffectDropdown;
                            })}
                            style={{ borderBottomLeftRadius: this._openEffectDropdown ? 0 : 5, border: this._openEffectDropdown ? `solid 2px ${Colors.MEDIUM_BLUE}` : 'solid 1px black' }}>
                            {effect?.toString()}
                            <FontAwesomeIcon className="presBox-dropdownIcon" style={{ gridColumn: 2, color: this._openEffectDropdown ? Colors.MEDIUM_BLUE : 'black' }} icon={'angle-down'} />
                            <div className={'presBox-dropdownOptions'} id={'presBoxMovementDropdown'} style={{ display: this._openEffectDropdown ? 'grid' : 'none' }} onPointerDown={e => e.stopPropagation()}>
                                {presEffect(PresEffect.None)}
                                {presEffect(PresEffect.Fade)}
                                {presEffect(PresEffect.Flip)}
                                {presEffect(PresEffect.Rotate)}
                                {presEffect(PresEffect.Bounce)}
                                {presEffect(PresEffect.Roll)}
                            </div>
                        </div>
                        <div className="ribbon-doubleButton" style={{ display: effect === PresEffectDirection.None ? 'none' : 'inline-flex' }}>
                            <div className="presBox-subheading">Effect direction</div>
                            <div className="ribbon-property">{StrCast(this.activeItem.presEffectDirection)}</div>
                        </div>
                        <div className="effectDirection" style={{ display: effect === PresEffectDirection.None ? 'none' : 'grid', width: 40 }}>
                            {presDirection(PresEffectDirection.Left, 'angle-right', 1, 2, {})}
                            {presDirection(PresEffectDirection.Right, 'angle-left', 3, 2, {})}
                            {presDirection(PresEffectDirection.Top, 'angle-down', 2, 1, {})}
                            {presDirection(PresEffectDirection.Bottom, 'angle-up', 2, 3, {})}
                            {presDirection(PresEffectDirection.Center, '', 2, 2, { width: 10, height: 10, alignSelf: 'center' })}
                        </div>
                    </div>
                    <div className="ribbon-final-box">
                        <div className="ribbon-final-button-hidden" onClick={() => this.applyTo(this.childDocs)}>
                            Apply to all
                        </div>
                    </div>
                </div>
            );
        }
    }

    @undoBatch
    @action
    applyTo = (array: Doc[]) => {
        this.updateMovement(this.activeItem.presMovement as PresMovement, true);
        this.updateEffect(this.activeItem.presEffect as PresEffect, true);
        this.updateEffectDirection(this.activeItem.presEffectDirection as PresEffectDirection, true);
        const { presTransition, presDuration, presHideBefore, presHideAfter } = this.activeItem;
        array.forEach(curDoc => {
            curDoc.presTransition = presTransition;
            curDoc.presDuration = presDuration;
            curDoc.presHideBefore = presHideBefore;
            curDoc.presHideAfter = presHideAfter;
        });
    };

    @computed get mediaOptionsDropdown() {
        const activeItem: Doc = this.activeItem;
        const targetDoc: Doc = this.targetDoc;
        const clipStart: number = NumCast(activeItem.clipStart);
        const clipEnd: number = NumCast(activeItem.clipEnd, NumCast(activeItem[Doc.LayoutFieldKey(activeItem) + '-duration']));
        const mediaStopDocInd: number = NumCast(activeItem.mediaStopDoc);
        if (activeItem && targetDoc) {
            return (
                <div>
                    <div className={'presBox-ribbon'} onClick={e => e.stopPropagation()} onPointerUp={e => e.stopPropagation()} onPointerDown={e => e.stopPropagation()}>
                        <div>
                            <div className="ribbon-box">
                                Start {'&'} End Time
                                <div className={'slider-headers'}>
                                    <div className="slider-block">
                                        <div className="slider-text" style={{ fontWeight: 500 }}>
                                            Start time (s)
                                        </div>
                                        <div id={'startTime'} className="slider-number" style={{ backgroundColor: Colors.LIGHT_GRAY }}>
                                            <input
                                                className="presBox-input"
                                                style={{ textAlign: 'center', width: '100%', height: 15, fontSize: 10 }}
                                                type="number"
                                                value={NumCast(activeItem.presStartTime).toFixed(2)}
                                                onKeyDown={e => e.stopPropagation()}
                                                onChange={action((e: React.ChangeEvent<HTMLInputElement>) => {
                                                    activeItem.presStartTime = Number(e.target.value);
                                                })}
                                            />
                                        </div>
                                    </div>
                                    <div className="slider-block">
                                        <div className="slider-text" style={{ fontWeight: 500 }}>
                                            Duration (s)
                                        </div>
                                        <div className="slider-number" style={{ backgroundColor: Colors.LIGHT_BLUE }}>
                                            {Math.round((NumCast(activeItem.presEndTime) - NumCast(activeItem.presStartTime)) * 10) / 10}
                                        </div>
                                    </div>
                                    <div className="slider-block">
                                        <div className="slider-text" style={{ fontWeight: 500 }}>
                                            End time (s)
                                        </div>
                                        <div id={'endTime'} className="slider-number" style={{ backgroundColor: Colors.LIGHT_GRAY }}>
                                            <input
                                                className="presBox-input"
                                                onKeyDown={e => e.stopPropagation()}
                                                style={{ textAlign: 'center', width: '100%', height: 15, fontSize: 10 }}
                                                type="number"
                                                value={NumCast(activeItem.presEndTime).toFixed(2)}
                                                onChange={action((e: React.ChangeEvent<HTMLInputElement>) => {
                                                    activeItem.presEndTime = Number(e.target.value);
                                                })}
                                            />
                                        </div>
                                    </div>
                                </div>
                                <div className="multiThumb-slider">
                                    <input
                                        type="range"
                                        step="0.1"
                                        min={clipStart}
                                        max={clipEnd}
                                        value={NumCast(activeItem.presEndTime)}
                                        style={{ gridColumn: 1, gridRow: 1 }}
                                        className={`toolbar-slider ${'end'}`}
                                        id="toolbar-slider"
                                        onPointerDown={e => {
                                            this._batch = UndoManager.StartBatch('presEndTime');
                                            const endBlock = document.getElementById('endTime');
                                            if (endBlock) {
                                                endBlock.style.color = Colors.LIGHT_GRAY;
                                                endBlock.style.backgroundColor = Colors.MEDIUM_BLUE;
                                            }
                                            e.stopPropagation();
                                        }}
                                        onPointerUp={() => {
                                            this._batch?.end();
                                            const endBlock = document.getElementById('endTime');
                                            if (endBlock) {
                                                endBlock.style.color = Colors.BLACK;
                                                endBlock.style.backgroundColor = Colors.LIGHT_GRAY;
                                            }
                                        }}
                                        onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
                                            e.stopPropagation();
                                            activeItem.presEndTime = Number(e.target.value);
                                        }}
                                    />
                                    <input
                                        type="range"
                                        step="0.1"
                                        min={clipStart}
                                        max={clipEnd}
                                        value={NumCast(activeItem.presStartTime)}
                                        style={{ gridColumn: 1, gridRow: 1 }}
                                        className={`toolbar-slider ${'start'}`}
                                        id="toolbar-slider"
                                        onPointerDown={e => {
                                            this._batch = UndoManager.StartBatch('presStartTime');
                                            const startBlock = document.getElementById('startTime');
                                            if (startBlock) {
                                                startBlock.style.color = Colors.LIGHT_GRAY;
                                                startBlock.style.backgroundColor = Colors.MEDIUM_BLUE;
                                            }
                                            e.stopPropagation();
                                        }}
                                        onPointerUp={() => {
                                            this._batch?.end();
                                            const startBlock = document.getElementById('startTime');
                                            if (startBlock) {
                                                startBlock.style.color = Colors.BLACK;
                                                startBlock.style.backgroundColor = Colors.LIGHT_GRAY;
                                            }
                                        }}
                                        onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
                                            e.stopPropagation();
                                            activeItem.presStartTime = Number(e.target.value);
                                        }}
                                    />
                                </div>
                                <div className="slider-headers">
                                    <div className="slider-text">{clipStart.toFixed(2)} s</div>
                                    <div className="slider-text"></div>
                                    <div className="slider-text">{clipEnd.toFixed(2)} s</div>
                                </div>
                            </div>
                            <div className="ribbon-final-box">
                                Playback
                                <div className="presBox-subheading">Start playing:</div>
                                <div className="presBox-radioButtons">
                                    <div className="checkbox-container">
                                        <input className="presBox-checkbox" type="checkbox" onChange={() => (activeItem.mediaStart = 'manual')} checked={activeItem.mediaStart === 'manual'} />
                                        <div>On click</div>
                                    </div>
                                    <div className="checkbox-container">
                                        <input className="presBox-checkbox" type="checkbox" onChange={() => (activeItem.mediaStart = 'auto')} checked={activeItem.mediaStart === 'auto'} />
                                        <div>Automatically</div>
                                    </div>
                                </div>
                                <div className="presBox-subheading">Stop playing:</div>
                                <div className="presBox-radioButtons">
                                    <div className="checkbox-container">
                                        <input className="presBox-checkbox" type="checkbox" onChange={() => (activeItem.mediaStop = 'manual')} checked={activeItem.mediaStop === 'manual'} />
                                        <div>At audio end time</div>
                                    </div>
                                    <div className="checkbox-container">
                                        <input className="presBox-checkbox" type="checkbox" onChange={() => (activeItem.mediaStop = 'auto')} checked={activeItem.mediaStop === 'auto'} />
                                        <div>On slide change</div>
                                    </div>
                                    {/* <div className="checkbox-container">
                                    <input className="presBox-checkbox"
                                        type="checkbox"
                                        onChange={() => activeItem.mediaStop = "afterSlide"}
                                        checked={activeItem.mediaStop === "afterSlide"}
                                    />
                                    <div className="checkbox-dropdown">
                                        After chosen slide
                                        <select className="presBox-viewPicker"
                                            style={{ opacity: activeItem.mediaStop === "afterSlide" && this.itemIndex !== this.childDocs.length - 1 ? 1 : 0.3 }}
                                            onPointerDown={e => e.stopPropagation()}
                                            onChange={this.mediaStopChanged}
                                            value={mediaStopDocStr}>
                                            {this.mediaStopSlides}
                                        </select>
                                    </div>
                                </div> */}
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            );
        }
    }

    @computed get newDocumentToolbarDropdown() {
        return (
            <div
                className="presBox-toolbar-dropdown"
                style={{ display: this._newDocumentTools && this.layoutDoc.presStatus === 'edit' ? 'inline-flex' : 'none' }}
                onClick={e => e.stopPropagation()}
                onPointerUp={e => e.stopPropagation()}
                onPointerDown={e => e.stopPropagation()}>
                <div className="layout-container" style={{ height: 'max-content' }}>
                    <div
                        className="layout"
                        style={{ border: this.layout === 'blank' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }}
                        onClick={action(() => {
                            this.layout = 'blank';
                            this.createNewSlide(this.layout);
                        })}
                    />
                    <div
                        className="layout"
                        style={{ border: this.layout === 'title' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }}
                        onClick={action(() => {
                            this.layout = 'title';
                            this.createNewSlide(this.layout);
                        })}>
                        <div className="title">Title</div>
                        <div className="subtitle">Subtitle</div>
                    </div>
                    <div
                        className="layout"
                        style={{ border: this.layout === 'header' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }}
                        onClick={action(() => {
                            this.layout = 'header';
                            this.createNewSlide(this.layout);
                        })}>
                        <div className="title" style={{ alignSelf: 'center', fontSize: 10 }}>
                            Section header
                        </div>
                    </div>
                    <div
                        className="layout"
                        style={{ border: this.layout === 'content' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }}
                        onClick={action(() => {
                            this.layout = 'content';
                            this.createNewSlide(this.layout);
                        })}>
                        <div className="title" style={{ alignSelf: 'center' }}>
                            Title
                        </div>
                        <div className="content">Text goes here</div>
                    </div>
                </div>
            </div>
        );
    }

    @observable openLayouts: boolean = false;
    @observable addFreeform: boolean = true;
    @observable layout: string = '';
    @observable title: string = '';

    @computed get newDocumentDropdown() {
        return (
            <div className={'presBox-ribbon'} onClick={e => e.stopPropagation()} onPointerDown={e => e.stopPropagation()}>
                <div className="ribbon-box">
                    Slide Title: <br></br>
                    <input
                        className="ribbon-textInput"
                        placeholder="..."
                        type="text"
                        name="fname"
                        onChange={e => {
                            e.stopPropagation();
                            e.preventDefault();
                            runInAction(() => (this.title = e.target.value));
                        }}></input>
                </div>
                <div className="ribbon-box">
                    Choose type:
                    <div className="ribbon-doubleButton">
                        <div title="Text" className={'ribbon-toggle'} style={{ background: this.addFreeform ? '' : Colors.LIGHT_BLUE }} onClick={action(() => (this.addFreeform = !this.addFreeform))}>
                            Text
                        </div>
                        <div title="Freeform" className={'ribbon-toggle'} style={{ background: this.addFreeform ? Colors.LIGHT_BLUE : '' }} onClick={action(() => (this.addFreeform = !this.addFreeform))}>
                            Freeform
                        </div>
                    </div>
                </div>
                <div className="ribbon-box" style={{ display: this.addFreeform ? 'grid' : 'none' }}>
                    Preset layouts:
                    <div className="layout-container" style={{ height: this.openLayouts ? 'max-content' : '75px' }}>
                        <div className="layout" style={{ border: this.layout === 'blank' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }} onClick={action(() => (this.layout = 'blank'))} />
                        <div className="layout" style={{ border: this.layout === 'title' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }} onClick={action(() => (this.layout = 'title'))}>
                            <div className="title">Title</div>
                            <div className="subtitle">Subtitle</div>
                        </div>
                        <div className="layout" style={{ border: this.layout === 'header' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }} onClick={action(() => (this.layout = 'header'))}>
                            <div className="title" style={{ alignSelf: 'center', fontSize: 10 }}>
                                Section header
                            </div>
                        </div>
                        <div className="layout" style={{ border: this.layout === 'content' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }} onClick={action(() => (this.layout = 'content'))}>
                            <div className="title" style={{ alignSelf: 'center' }}>
                                Title
                            </div>
                            <div className="content">Text goes here</div>
                        </div>
                        <div className="layout" style={{ border: this.layout === 'twoColumns' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }} onClick={action(() => (this.layout = 'twoColumns'))}>
                            <div className="title" style={{ alignSelf: 'center', gridColumn: '1/3' }}>
                                Title
                            </div>
                            <div className="content" style={{ gridColumn: 1, gridRow: 2 }}>
                                Column one text
                            </div>
                            <div className="content" style={{ gridColumn: 2, gridRow: 2 }}>
                                Column two text
                            </div>
                        </div>
                    </div>
                    <div className="open-layout" onClick={action(() => (this.openLayouts = !this.openLayouts))}>
                        <FontAwesomeIcon style={{ transition: 'all 0.3s', transform: this.openLayouts ? 'rotate(180deg)' : 'rotate(0deg)' }} icon={'caret-down'} size={'lg'} />
                    </div>
                </div>
                <div className="ribbon-final-box">
                    <div
                        className={this.title !== '' && ((this.addFreeform && this.layout !== '') || !this.addFreeform) ? 'ribbon-final-button-hidden' : 'ribbon-final-button'}
                        onClick={() => this.createNewSlide(this.layout, this.title, this.addFreeform)}>
                        Create New Slide
                    </div>
                </div>
            </div>
        );
    }

    createNewSlide = (layout?: string, title?: string, freeform?: boolean) => {
        let doc = undefined;
        if (layout) doc = this.createTemplate(layout);
        if (freeform && layout) doc = this.createTemplate(layout, title);
        if (!freeform && !layout) doc = Docs.Create.TextDocument('', { _nativeWidth: 400, _width: 225, title: title });
        if (doc) {
            const tabMap = CollectionDockingView.Instance?.tabMap;
            const tab = tabMap && Array.from(tabMap).find(tab => tab.DashDoc.type === DocumentType.COL)?.DashDoc;
            const presCollection = DocumentManager.GetContextPath(this.activeItem).reverse().lastElement().presentationTargetDoc ?? tab;
            const data = Cast(presCollection?.data, listSpec(Doc));
            const presData = Cast(this.rootDoc.data, listSpec(Doc));
            if (data && presData) {
                data.push(doc);
                TabDocView.PinDoc(doc, {});
                this.gotoDocument(this.childDocs.length, this.activeItem);
            } else {
                this.props.addDocTab(doc, OpenWhere.addRight);
            }
        }
    };

    createTemplate = (layout: string, input?: string) => {
        const x = this.activeItem && this.targetDoc ? NumCast(this.targetDoc.x) : 0;
        const y = this.activeItem && this.targetDoc ? NumCast(this.targetDoc.y) + NumCast(this.targetDoc._height) + 20 : 0;
        const title = () => Docs.Create.TextDocument('Click to change title', { title: 'Slide title', _width: 380, _height: 60, x: 10, y: 58, _fontSize: '24pt' });
        const subtitle = () => Docs.Create.TextDocument('Click to change subtitle', { title: 'Slide subtitle', _width: 380, _height: 50, x: 10, y: 118, _fontSize: '16pt' });
        const header = () => Docs.Create.TextDocument('Click to change header', { title: 'Slide header', _width: 380, _height: 65, x: 10, y: 80, _fontSize: '20pt' });
        const contentTitle = () => Docs.Create.TextDocument('Click to change title', { title: 'Slide title', _width: 380, _height: 60, x: 10, y: 10, _fontSize: '24pt' });
        const content = () => Docs.Create.TextDocument('Click to change text', { title: 'Slide text', _width: 380, _height: 145, x: 10, y: 70, _fontSize: '14pt' });
        const content1 = () => Docs.Create.TextDocument('Click to change text', { title: 'Column 1', _width: 185, _height: 140, x: 10, y: 80, _fontSize: '14pt' });
        const content2 = () => Docs.Create.TextDocument('Click to change text', { title: 'Column 2', _width: 185, _height: 140, x: 205, y: 80, _fontSize: '14pt' });
        // prettier-ignore
        switch (layout) {
            case 'blank': return Docs.Create.FreeformDocument([], { title: input ? input : 'Blank slide', _width: 400, _height: 225, x, y });
            case 'title': return Docs.Create.FreeformDocument([title(), subtitle()], { title: input ? input : 'Title slide', _width: 400, _height: 225, _fitContentsToBox: true, x, y });
            case 'header': return Docs.Create.FreeformDocument([header()], { title: input ? input : 'Section header', _width: 400, _height: 225, _fitContentsToBox: true, x, y });
            case 'content': return Docs.Create.FreeformDocument([contentTitle(), content()], { title: input ? input : 'Title and content', _width: 400, _height: 225, _fitContentsToBox: true, x, y });
            case 'twoColumns': return Docs.Create.FreeformDocument([contentTitle(), content1(), content2()], { title: input ? input : 'Title and two columns', _width: 400, _height: 225, _fitContentsToBox: true, x, y })
        }
    };

    // Dropdown that appears when the user wants to begin presenting (either minimize or sidebar view)
    @computed get presentDropdown() {
        return (
            <div className={`dropdown-play ${this._presentTools ? 'active' : ''}`} onClick={e => e.stopPropagation()} onPointerUp={e => e.stopPropagation()} onPointerDown={e => e.stopPropagation()}>
                <div
                    className="dropdown-play-button"
                    onClick={undoBatch(
                        action(() => {
                            this.enterMinimize();
                            this.turnOffEdit(true);
                            this.gotoDocument(this.itemIndex, this.activeItem);
                        })
                    )}>
                    Mini-player
                </div>
                <div
                    className="dropdown-play-button"
                    onClick={undoBatch(
                        action(() => {
                            this.layoutDoc.presStatus = 'manual';
                            this.turnOffEdit(true);
                            this.gotoDocument(this.itemIndex, this.activeItem);
                        })
                    )}>
                    Sidebar player
                </div>
            </div>
        );
    }

    scrollFocus = () => {
        // this.gotoDocument(0);
        // this.startOrPause(false);
        return undefined;
    };

    _keyTimer: NodeJS.Timeout | undefined;

    /**
     * Returns the collection type as a string for headers
     */
    @computed get stringType() {
        if (this.activeItem) {
            // prettier-ignore
            switch (this.targetDoc.type) {
                case DocumentType.PDF: return 'PDF';
                case DocumentType.RTF: return 'Text node';
                case DocumentType.COL: return 'Collection';
                case DocumentType.AUDIO: return 'Audio';
                case DocumentType.VID: return 'Video';
                case DocumentType.IMG: return 'Image';
                case DocumentType.WEB: return 'Web page';
                case DocumentType.MAP: return 'Map';
                default: return 'Other node';
            }
        }
        return '';
    }

    @observable private openActiveColorPicker: boolean = false;
    @observable private openViewedColorPicker: boolean = false;

    @undoBatch
    @action
    switchActive = (color: ColorState) => {
        this.targetDoc['pres-text-color'] = String(color.hex);
        return true;
    };
    @undoBatch
    @action
    switchPresented = (color: ColorState) => {
        this.targetDoc['pres-text-viewed-color'] = String(color.hex);
        return true;
    };

    @computed get activeColorPicker() {
        return !this.openActiveColorPicker ? null : (
            <SketchPicker
                onChange={this.switchActive}
                presetColors={['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505', '#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B', '#FFFFFF', '#f1efeb', 'transparent']}
                color={StrCast(this.targetDoc['pres-text-color'])}
            />
        );
    }

    @computed get viewedColorPicker() {
        return !this.openViewedColorPicker ? null : (
            <SketchPicker
                onChange={this.switchPresented}
                presetColors={['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505', '#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B', '#FFFFFF', '#f1efeb', 'transparent']}
                color={StrCast(this.targetDoc['pres-text-viewed-color'])}
            />
        );
    }

    @action
    turnOffEdit = (paths?: boolean) => paths && this.togglePath(true); // Turn off paths

    @computed
    get toolbarWidth(): number {
        return this.props.PanelWidth();
    }

    @action
    toggleProperties = () => (SettingsManager.propertiesWidth = SettingsManager.propertiesWidth > 0 ? 0 : 250);

    @computed get toolbar() {
        const propIcon = SettingsManager.propertiesWidth > 0 ? 'angle-double-right' : 'angle-double-left';
        const propTitle = SettingsManager.propertiesWidth > 0 ? 'Close Presentation Panel' : 'Open Presentation Panel';
        const mode = StrCast(this.rootDoc._viewType) as CollectionViewType;
        const isMini: boolean = this.toolbarWidth <= 100;
        const activeColor = Colors.LIGHT_BLUE;
        const inactiveColor = Colors.WHITE;
        return mode === CollectionViewType.Carousel3D ? null : (
            <div id="toolbarContainer" className={'presBox-toolbar'}>
                {/* <Tooltip title={<><div className="dash-tooltip">{"Add new slide"}</div></>}><div className={`toolbar-button ${this.newDocumentTools ? "active" : ""}`} onClick={action(() => this.newDocumentTools = !this.newDocumentTools)}>
                    <FontAwesomeIcon icon={"plus"} />
                    <FontAwesomeIcon className={`dropdown ${this.newDocumentTools ? "active" : ""}`} icon={"angle-down"} />
                </div></Tooltip> */}
                <Tooltip title={<div className="dash-tooltip">View paths</div>}>
                    <div
                        style={{ opacity: this.childDocs.length > 1 ? 1 : 0.3, color: this._pathBoolean ? Colors.MEDIUM_BLUE : 'white', width: isMini ? '100%' : undefined }}
                        className={'toolbar-button'}
                        onClick={this.childDocs.length > 1 ? () => this.togglePath() : undefined}>
                        <FontAwesomeIcon icon={'exchange-alt'} />
                    </div>
                </Tooltip>
                {isMini ? null : (
                    <>
                        <div className="toolbar-divider" />
                        {/* <Tooltip title={<><div className="dash-tooltip">{this._expandBoolean ? "Minimize all" : "Expand all"}</div></>}>
                            <div className={"toolbar-button"}
                                style={{ color: this._expandBoolean ? Colors.MEDIUM_BLUE : 'white' }}
                                onClick={this.toggleExpandMode}>
                                <FontAwesomeIcon icon={"eye"} />
                            </div>
                        </Tooltip>
                        <div className="toolbar-divider" /> */}
                        <Tooltip title={<div className="dash-tooltip">{this._presKeyEvents ? 'Keys are active' : 'Keys are not active - click anywhere on the presentation trail to activate keys'}</div>}>
                            <div className="toolbar-button" style={{ cursor: this._presKeyEvents ? 'default' : 'pointer', position: 'absolute', right: 30, fontSize: 16 }}>
                                <FontAwesomeIcon className={'toolbar-thumbtack'} icon={'keyboard'} style={{ color: this._presKeyEvents ? activeColor : inactiveColor }} />
                            </div>
                        </Tooltip>
                        <Tooltip title={<div className="dash-tooltip">{propTitle}</div>}>
                            <div className="toolbar-button" style={{ position: 'absolute', right: 4, fontSize: 16 }} onClick={this.toggleProperties}>
                                <FontAwesomeIcon className={'toolbar-thumbtack'} icon={propIcon} style={{ color: SettingsManager.propertiesWidth > 0 ? activeColor : inactiveColor }} />
                            </div>
                        </Tooltip>
                    </>
                )}
            </div>
        );
    }

    /**
     * Top panel containes:
     * viewPicker: The option to choose between List and Slides view for the presentaiton trail
     * presentPanel: The button to start the presentation / open minimized view of the presentation
     */
    @computed get topPanel() {
        const mode = StrCast(this.rootDoc._viewType) as CollectionViewType;
        const isMini: boolean = this.toolbarWidth <= 100;
        const inOverlay = DocListCast(Doc.MyOverlayDocs?.data).includes(this.layoutDoc);
        return (
            <div className={`presBox-buttons${inOverlay ? ' inOverlay' : ''}`} style={{ background: Doc.ActivePresentation === this.rootDoc ? Colors.LIGHT_BLUE : undefined, display: !this.rootDoc._chromeHidden ? 'none' : undefined }}>
                {isMini ? null : (
                    <select className="presBox-viewPicker" style={{ display: this.layoutDoc.presStatus === 'edit' ? 'block' : 'none' }} onPointerDown={e => e.stopPropagation()} onChange={this.viewChanged} value={mode}>
                        <option onPointerDown={StopEvent} value={CollectionViewType.Stacking}>
                            List
                        </option>
                        <option onPointerDown={StopEvent} value={CollectionViewType.Tree}>
                            Tree
                        </option>
                        {Doc.noviceMode ? null : (
                            <option onPointerDown={StopEvent} value={CollectionViewType.Carousel3D}>
                                3D Carousel
                            </option>
                        )}
                    </select>
                )}
                <div className="presBox-presentPanel" style={{ opacity: this.childDocs.length ? 1 : 0.3 }}>
                    <span className={`presBox-button ${this.layoutDoc.presStatus === PresStatus.Edit ? 'present' : ''}`}>
                        <div
                            className="presBox-button-left"
                            onClick={undoBatch(() => {
                                if (this.childDocs.length) {
                                    this.layoutDoc.presStatus = 'manual';
                                    this.gotoDocument(this.itemIndex, this.activeItem);
                                }
                            })}>
                            <FontAwesomeIcon icon={'play-circle'} />
                            <div style={{ display: this.props.PanelWidth() > 200 ? 'inline-flex' : 'none' }}>&nbsp; Present</div>
                        </div>
                        {mode === CollectionViewType.Carousel3D || isMini ? null : (
                            <div
                                className={`presBox-button-right ${this._presentTools ? 'active' : ''}`}
                                onClick={action(() => {
                                    if (this.childDocs.length) this._presentTools = !this._presentTools;
                                })}>
                                <FontAwesomeIcon className="dropdown" style={{ margin: 0, transform: this._presentTools ? 'rotate(180deg)' : 'rotate(0deg)' }} icon={'angle-down'} />
                                {this.presentDropdown}
                            </div>
                        )}
                    </span>
                    {this.playButtons}
                </div>
            </div>
        );
    }

    @computed get playButtons() {
        const presEnd: boolean = !this.layoutDoc.presLoop && this.itemIndex === this.childDocs.length - 1;
        const presStart: boolean = !this.layoutDoc.presLoop && this.itemIndex === 0;
        const inOverlay = DocListCast(Doc.MyOverlayDocs?.data).includes(this.layoutDoc);
        // Case 1: There are still other frames and should go through all frames before going to next slide
        return (
            <div className="presPanelOverlay" style={{ display: this.layoutDoc.presStatus !== 'edit' ? 'inline-flex' : 'none' }}>
                <Tooltip title={<div className="dash-tooltip">{'Loop'}</div>}>
                    <div
                        className="presPanel-button"
                        style={{ color: this.layoutDoc.presLoop ? Colors.MEDIUM_BLUE : 'white' }}
                        onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => (this.layoutDoc.presLoop = !this.layoutDoc.presLoop), false, false)}>
                        <FontAwesomeIcon icon={'redo-alt'} />
                    </div>
                </Tooltip>
                <div className="presPanel-divider" />
                <div
                    className="presPanel-button"
                    style={{ opacity: presStart ? 0.4 : 1 }}
                    onPointerDown={e =>
                        setupMoveUpEvents(
                            this,
                            e,
                            returnFalse,
                            emptyFunction,
                            () => {
                                this.back();
                                if (this._presTimer) {
                                    clearTimeout(this._presTimer);
                                    this.layoutDoc.presStatus = PresStatus.Manual;
                                }
                                e.stopPropagation();
                            },
                            false,
                            false
                        )
                    }>
                    <FontAwesomeIcon icon={'arrow-left'} />
                </div>
                <Tooltip title={<div className="dash-tooltip">{this.layoutDoc.presStatus === PresStatus.Autoplay ? 'Pause' : 'Autoplay'}</div>}>
                    <div className="presPanel-button" onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => this.startOrPause(true), false, false)}>
                        <FontAwesomeIcon icon={this.layoutDoc.presStatus === PresStatus.Autoplay ? 'pause' : 'play'} />
                    </div>
                </Tooltip>
                <div
                    className="presPanel-button"
                    style={{ opacity: presEnd ? 0.4 : 1 }}
                    onPointerDown={e =>
                        setupMoveUpEvents(
                            this,
                            e,
                            returnFalse,
                            emptyFunction,
                            () => {
                                this.next();
                                if (this._presTimer) {
                                    clearTimeout(this._presTimer);
                                    this.layoutDoc.presStatus = PresStatus.Manual;
                                }
                                e.stopPropagation();
                            },
                            false,
                            false
                        )
                    }>
                    <FontAwesomeIcon icon={'arrow-right'} />
                </div>
                <div className="presPanel-divider"></div>
                <Tooltip title={<div className="dash-tooltip">{'Click to return to 1st slide'}</div>}>
                    <div
                        className="presPanel-button"
                        style={{ border: 'solid 1px white' }}
                        onPointerDown={e =>
                            setupMoveUpEvents(
                                this,
                                e,
                                returnFalse,
                                emptyFunction,
                                () => {
                                    this.nextSlide(0);
                                },
                                false,
                                false
                            )
                        }>
                        <b>1</b>
                    </div>
                </Tooltip>
                <div className="presPanel-button-text" onClick={() => this.gotoDocument(0, this.activeItem)} style={{ display: inOverlay || this.props.PanelWidth() > 250 ? 'inline-flex' : 'none' }}>
                    {`${inOverlay ? '' : 'Slide'} ${this.itemIndex + 1} / ${this.childDocs.length}`}
                </div>
                <div className="presPanel-divider"></div>
                {this.props.PanelWidth() > 250 ? (
                    <div
                        className="presPanel-button-text"
                        onClick={undoBatch(
                            action(() => {
                                this.layoutDoc.presStatus = PresStatus.Edit;
                                clearTimeout(this._presTimer);
                            })
                        )}>
                        EXIT
                    </div>
                ) : (
                    <div className="presPanel-button" onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, this.exitClicked, false, false)}>
                        <FontAwesomeIcon icon={'times'} />
                    </div>
                )}
            </div>
        );
    }

    @action
    startOrPause = (makeActive = true) => {
        makeActive && this.updateCurrentPresentation();
        if (this.layoutDoc.presStatus === PresStatus.Manual || this.layoutDoc.presStatus === PresStatus.Edit) this.startPresentation(this.itemIndex);
        else this.pauseAutoPres();
    };

    @action
    prevClicked = (e: PointerEvent) => {
        this.back();
        if (this._presTimer) {
            clearTimeout(this._presTimer);
            this.layoutDoc.presStatus = PresStatus.Manual;
        }
    };

    @action
    nextClicked = (e: PointerEvent) => {
        this.next();
        if (this._presTimer) {
            clearTimeout(this._presTimer);
            this.layoutDoc.presStatus = PresStatus.Manual;
        }
    };
    @undoBatch
    @action
    exitClicked = () => {
        this.layoutDoc.presStatus = this._exitTrail?.() ?? this.exitMinimize();
        clearTimeout(this._presTimer);
    };

    AddToMap = (treeViewDoc: Doc, index: number[]): Doc[] => {
        var indexNum = 0;
        for (let i = 0; i < index.length; i++) {
            indexNum += index[i] * 10 ** -i;
        }
        if (this._treeViewMap.get(treeViewDoc) !== indexNum) {
            this._treeViewMap.set(treeViewDoc, indexNum);
            const sorted = this.sort(this._treeViewMap);
            const curList = DocListCast(this.dataDoc[this.presFieldKey]);
            if (sorted.length !== curList.length || sorted.some((doc, ind) => doc !== curList[ind])) {
                this.dataDoc[this.presFieldKey] = new List<Doc>(sorted); // this is a flat array of Docs
            }
        }
        return this.childDocs;
    };

    RemFromMap = (treeViewDoc: Doc, index: number[]): Doc[] => {
        if (!this._unmounting && this.isTree) {
            this._treeViewMap.delete(treeViewDoc);
            this.dataDoc[this.presFieldKey] = new List<Doc>(this.sort(this._treeViewMap));
        }
        return this.childDocs;
    };

    // TODO: [AL] implement sort function for an array of numbers (e.g. arr[1,2,4] v arr[1,2,1])
    sort = (treeViewMap: Map<Doc, number>) => [...treeViewMap.entries()].sort((a: [Doc, number], b: [Doc, number]) => (a[1] > b[1] ? 1 : a[1] < b[1] ? -1 : 0)).map(kv => kv[0]);

    render() {
        // needed to ensure that the childDocs are loaded for looking up fields
        this.childDocs.slice();
        const mode = StrCast(this.rootDoc._viewType) as CollectionViewType;
        const presEnd = !this.layoutDoc.presLoop && this.itemIndex === this.childDocs.length - 1;
        const presStart = !this.layoutDoc.presLoop && this.itemIndex === 0;
        const inOverlay = DocListCast(Doc.MyOverlayDocs?.data).includes(this.layoutDoc);
        return this.props.addDocTab === returnFalse ? ( // bcz: hack!! - addDocTab === returnFalse only when this is being rendered by the OverlayView which means the doc is a mini player
            <div className="miniPres" onClick={e => e.stopPropagation()} onPointerEnter={action(e => (this._forceKeyEvents = true))}>
                <div
                    className="presPanelOverlay"
                    style={{ display: 'inline-flex', height: 30, background: Doc.ActivePresentation === this.rootDoc ? 'green' : '#323232', top: 0, zIndex: 3000000, boxShadow: this._presKeyEvents ? '0 0 0px 3px ' + Colors.MEDIUM_BLUE : undefined }}>
                    <Tooltip title={<div className="dash-tooltip">{'Loop'}</div>}>
                        <div
                            className="presPanel-button"
                            style={{ color: this.layoutDoc.presLoop ? Colors.MEDIUM_BLUE : undefined }}
                            onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, returnFalse, () => (this.layoutDoc.presLoop = !this.layoutDoc.presLoop), false, false)}>
                            <FontAwesomeIcon icon={'redo-alt'} />
                        </div>
                    </Tooltip>
                    <div className="presPanel-divider"></div>
                    <div className="presPanel-button" style={{ opacity: presStart ? 0.4 : 1 }} onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, returnFalse, this.prevClicked, false, false)}>
                        <FontAwesomeIcon icon={'arrow-left'} />
                    </div>
                    <Tooltip title={<div className="dash-tooltip">{this.layoutDoc.presStatus === PresStatus.Autoplay ? 'Pause' : 'Autoplay'}</div>}>
                        <div className="presPanel-button" onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, returnFalse, () => this.startOrPause(true), false, false)}>
                            <FontAwesomeIcon icon={this.layoutDoc.presStatus === 'auto' ? 'pause' : 'play'} />
                        </div>
                    </Tooltip>
                    <div className="presPanel-button" style={{ opacity: presEnd ? 0.4 : 1 }} onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, returnFalse, this.nextClicked, false, false)}>
                        <FontAwesomeIcon icon={'arrow-right'} />
                    </div>
                    <div className="presPanel-divider"></div>
                    <Tooltip title={<div className="dash-tooltip">{'Click to return to 1st slide'}</div>}>
                        <div className="presPanel-button" style={{ border: 'solid 1px white' }} onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, returnFalse, () => this.gotoDocument(0, this.activeItem), false, false)}>
                            <b>1</b>
                        </div>
                    </Tooltip>
                    <div className="presPanel-button-text">
                        Slide {this.itemIndex + 1} / {this.childDocs.length}
                    </div>
                    <div className="presPanel-divider" />
                    <div className="presPanel-button-text" onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, returnFalse, this.exitClicked, false, false)}>
                        EXIT
                    </div>
                </div>
            </div>
        ) : (
            <div className="presBox-cont" style={{ minWidth: inOverlay ? PresBox.minimizedWidth : undefined }}>
                {this.topPanel}
                {this.toolbar}
                {this.newDocumentToolbarDropdown}
                <div className="presBox-listCont">
                    <div className="Slide">
                        {mode !== CollectionViewType.Invalid ? (
                            <CollectionView
                                {...this.props}
                                ContainingCollectionDoc={this.props.Document}
                                PanelWidth={this.props.PanelWidth}
                                PanelHeight={this.panelHeight}
                                childIgnoreNativeSize={true}
                                moveDocument={returnFalse}
                                ignoreUnrendered={true}
                                //childFitWidth={returnTrue}
                                childOpacity={returnOne}
                                childLayoutTemplate={this.childLayoutTemplate}
                                childXPadding={Doc.IsComicStyle(this.rootDoc) ? 20 : undefined}
                                filterAddDocument={this.addDocumentFilter}
                                removeDocument={returnFalse}
                                dontRegisterView={true}
                                focus={this.selectElement}
                                scriptContext={this}
                                ScreenToLocalTransform={this.getTransform}
                                AddToMap={this.AddToMap}
                                RemFromMap={this.RemFromMap}
                                hierarchyIndex={emptyPath}
                            />
                        ) : null}
                    </div>

                    {/* {
                        // if the document type is a presentation, then the collection stacking view has a "+ new slide" button at the bottom of the stack
                        <Tooltip title={<div className="dash-tooltip">{'Click on document to pin to presentaiton or make a marquee selection to pin your desired view'}</div>}>
                            <button className="add-slide-button" onPointerDown={this.startMarqueeCreateSlide}>
                                + NEW SLIDE
                            </button>
                        </Tooltip>
                    } */}
                </div>
            </div>
        );
    }
    static NavigateToDoc(bestTarget: Doc, activeItem: Doc) {
        const openInTab = (doc: Doc, finished?: () => void) => {
            CollectionDockingView.AddSplit(doc, OpenWhereMod.right);
            finished?.();
        };
        PresBox.NavigateToTarget(bestTarget, activeItem, openInTab);
    }
}

ScriptingGlobals.add(function navigateToDoc(bestTarget: Doc, activeItem: Doc) {
    PresBox.NavigateToDoc(bestTarget, activeItem);
});
