<?xml version="1.0" encoding="UTF-8" standalone="no"?><addon xmlns="http://icplayer.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" id="video" xsi:schemaLocation="http://icplayer.com ../../tools/addon.xsd">
	<model>
		<property isDefault="true" name="Files" nameLabel="video_property_files" type="list">
			<property displayName="Ogv video" name="Ogg video" nameLabel="video_property_ogg_video" type="video"/>
			<property name="MP4 video" nameLabel="video_property_mp4_video" type="video"/>
			<property name="WebM video" nameLabel="video_property_webm_video" type="video"/>
			<property name="m3u8 video" nameLabel="video_property_m3u8_video" type="video"/>
			<property isLocalized="true" name="Subtitles" nameLabel="video_property_subtitles" type="text"/>
			<property name="Poster" nameLabel="video_property_poster" type="image"/>
			<property name="ID" nameLabel="video_property_id" type="string"/>
            <property name="Loop video" nameLabel="video_property_loop_video" type="boolean"/>
			<property name="AlternativeText" nameLabel="video_property_alternative_text" type="string"/>
			<property name="time_labels" nameLabel="video_property_time_labels" type="text"/>
			<property isLocalized="true" name="Audio Description" nameLabel="video_property_audiodescription" type="text"/>
		</property>
		<property isLocalized="true" name="Narration" nameLabel="video_property_narration" type="narration"/>
        <property name="Hide default controls" nameLabel="video_property_hide_default_controls" type="boolean"/>
        <property name="Hide subtitles" nameLabel="video_property_hide_subtitles" type="boolean"/>
		<property name="Show play button" nameLabel="video_property_show_play_button" type="boolean"/>
		<property name="speechTexts" nameLabel="feedback_property_speech_texts" type="staticlist">
            <property name="AudioDescriptionEnabled" nameLabel="video_property_description_enabled" type="staticrow">
                <property name="AudioDescriptionEnabled" nameLabel="video_property_description_enabled" type="string"/>
            </property>
            <property name="AudioDescriptionDisabled" nameLabel="video_property_description_disabled" type="staticrow">
                <property name="AudioDescriptionDisabled" nameLabel="video_property_description_disabled" type="string"/>
            </property>
        </property>
        <property name="offlineMessage" nameLabel="video_property_offline_message" type="text"/>
		<property name="enableVideoSpeedController" nameLabel="enable_video_speed_controller" type="boolean"/>
		<property name="Base width" nameLabel="video_property_base_width" type="string"/>
		<property name="Base height" nameLabel="video_property_base_height" type="string"/>
		<property name="{library}" nameLabel="video_property_hls_library" type="module-script" value="/file/serve/5350926942339072"/>
	</model>
<css>.video-container {
    background-color: #000;
    position: relative;
}

.captions {
    position: absolute;
    color: #fff;
}

.poster-wrapper {
    display: block;
    position: absolute;
    top:0;
    left:0;
    width: 100%;
    height: 100%;
    background-color: white;
}

.poster-wrapper img {
    position : absolute;
    top:0;
    left:0;
    width: 100%;
    height: 100%;
}

.video-poster-play {
    position : absolute;
    background: url('resources/video-poster-play.svg') no-repeat center;
    text-align: center;
    width: 80px;
    height: 80px;
    top: calc(50% - 40px);
    left: calc(50% - 40px);

}

.video-container-mask {
    position: absolute;
    width: 100%;
    height: 100%;
    top: 0px;
    left: 0px;
    color: transparent;
    overflow: hidden;
}

.video-container .CustomControlsBar-wrapper {
    bottom : 0px;
    left : 0px;
    background-color: #EBEFF0;
    height: 40px;
    width: 100%;
    position: absolute;
}

.video-container .CustomControlsBar-wrapper-controls-controlsWrapper {
    margin-top: 3px;
    position: absolute;
    height: 37px;
    width: 100%;
}

.video-container-video {
    display: block;
    pointer-events: none;
    position: absolute;
}

.video-container .CustomControlsBar-wrapper-controls-play {
    height: 100%;
    width: 38px;
    background: url("resources/video-play-btn.png") no-repeat center;
    box-sizing: border-box;
    cursor: pointer;
    float: left;

}

.video-container .CustomControlsBar-wrapper-controls-pause {
    height: 100%;
    width: 38px;
    background: url("resources/video-pause-btn.png") no-repeat center;
    box-sizing: border-box;
    cursor: pointer;
    float: left;

}

.video-container .CustomControlsBar-wrapper-controls-stop {
    height: 100%;
    width: 40px;
    background: url("resources/video-stop-btn.png") no-repeat center center;
    cursor: pointer;
    float: left;

}

.video-container .CustomControlsBar-wrapper-controls-progressBarWrapper {
    position:absolute;
    width: 100%;
    height: 3px;
    float: left;
}

.video-container .CustomControlsBar-wrapper-controls-progressBarWrapper:hover {
    height: 6px;
}

.video-container .CustomControlsBar-wrapper-controls-progressBarWrapper-redProgressBar {
    background-color: red;
    height: 100%;
    position:absolute;
}

.video-container .CustomControlsBar-wrapper-controls-progressBarWrapper-grayProgressBar {
    background-color: dimgrey;
    width: 100%;
    height: 100%;
    position:absolute;
}

.video-container .CustomControlsBar-wrapper-controls-volume {
    height: 100%;
    width: 40px;
    background: url("resources/video-volume-btn.png") no-repeat center center;
    cursor: pointer;
    float: left;

}

.video-container .CustomControlsBar-wrapper-controls-volumeBarWrapper {
    height: 100%;
    float: left;
    cursor: pointer;
}

.video-container .CustomControlsBar-wrapper-controls-volumeBarWrapper-volumeBackground {
    width: 100px;
    height: 14px;
    border-bottom: solid 9px white;
    float: left;
    cursor: pointer;
    position: absolute;
}

.video-container .CustomControlsBar-wrapper-controls-volumeBarWrapper-volumeBackgroundSelected {
    width: 100px;
    height: 14px;
    border-bottom: solid 9px lightgrey;
    float: left;
    cursor: pointer;
    position: absolute;
}

.video-container .CustomControlsBar-wrapper-controls-fullscreen {
    float: right;
    width: 40px;
    height: 100%;
    background: url("resources/video-fullscreen-btn.png") no-repeat center center;
    cursor: pointer;
}

.video-container .CustomControlsBar-wrapper-controls-closeFullscreen {
    float: right;
    width: 40px;
    height: 100%;
    background: url("resources/video-close-fullscreen-btn.png") no-repeat center center;
    cursor: pointer;
}

.video-container .CustomControlsBar-wrapper-controls-timer {
    height: 40px;
    color: gray;
    font-size: 14px;
    padding-right: 10px;
    display: flex;
    align-items: center;
    float: right;

}

.CustomControlsBar-wrapper-controls-burgersContainer-time_labels {
    height: 100%;
    width: 40px;
    background: url("resources/video-drop-down.png") no-repeat center;
    cursor: pointer;
    float: left;
}

.CustomControlsBar-wrapper-controls-burgersContainer {
    float: right;
    height: 100%;
}

.CustomControlsBar-wrapper-controls-burgersContainer-container-time_labels {
    background-color: #f9f9f9;
    min-width: 100px;
    box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
    position: absolute;
    display: none;
    z-index: 1;
    white-space: nowrap;
    word-break: keep-all;
}

.CustomControlsBar-wrapper-controls-burgersContainer-time_labels-element {
    padding: 5px;
}

.CustomControlsBar-wrapper-controls-burgersContainer-time_labels-element:hover {
    background-color: #f1f1f1;
}

.CustomControlsBar-wrapper-controls-videoSpeedController .video-playback-rate{
    float: left;
    margin-top: 10px;
    height: 30px;
    width: 60px;
    line-height: 16px;
    padding-right: 10px;
    background-color: inherit;
    cursor: pointer;
}

.CustomControlsBar-wrapper-controls-videoSpeedController .video-playback-rate select {
    width: 100%;
    font-size: 14px;
    font-family: Arial, "Helvetica Neue", Helvetica, sans-serif;
    color: grey;
    border: 0;
    background-color: inherit;
    cursor: pointer;
}

.CustomControlsBar-wrapper-controls-videoSpeedController .video-playback-rate select option{
    cursor: pointer;
}

.video-container .video-js {
    height: 0;
}

.video-container .video-js :not(video) {
    display: none;
    visibility: hidden;
}

.video-container .video-js video {
    position: absolute;
    top: 0px;
    left: 0px;
    width: 100%;
    height: 100%;
}
</css><view>&lt;div class="video-container"&gt;
    &lt;video class="video-container-video"&gt;
        Video playback is not supported on your browser.
    &lt;/video&gt;
    &lt;div class="captions-container"&gt;&lt;/div&gt;
    &lt;div class="video-container-mask"&gt;&lt;/div&gt;
    &lt;div class="poster-wrapper"&gt;
    &lt;/div&gt;
    &lt;div class="video-poster-play"&gt;&lt;/div&gt;
&lt;/div&gt;
</view><preview>&lt;div class="video-container"&gt;
    &lt;video preload="auto"&gt;
        Video playback is not supported on your browser.
    &lt;/video&gt;
&lt;/div&gt;
</preview><presenter>/**
 * @module VideoAddon
 * @class VideoAddon
 * @constructor
 */
function Addonvideo_create() {
    var presenter = function () {
    };

    var deferredSyncQueue = window.DecoratorUtils.DeferredSyncQueue(deferredQueueDecoratorChecker);
    var currentTime;

    var escapedSeparator = '&amp;&amp;separator&amp;&amp;';

    presenter.currentMovie = 0;
    presenter.videoContainer = null;
    presenter.$view = null;
    presenter.files = [];
    presenter.video = null;
    presenter.hlsPlayer = null;
    presenter.isHLSValue = null;
    presenter.controlBar = null;
    presenter.isVideoSpeedControllerAdded = false;
    presenter.isCurrentlyVisible = true;
    presenter.isVideoLoaded = false;
    presenter.metadadaLoaded = false;
    presenter.isPreview = false;
    presenter.captions = [];
    presenter.captionDivs = [];
    presenter.descriptions = [];
    presenter.descriptionsDivs = [];
    presenter.speechTexts = [];
    presenter.metadataQueue = [];
    presenter.areSubtitlesHidden = false;
    presenter.calledFullScreen = false;
    presenter.playTriggered = false;
    presenter.playerController = null;
    presenter.posterPlayButton = null;
    presenter.videoView = null;
    presenter.isAudioDescriptionEnabled = null;
    presenter.prevTime = -0.001;
    presenter.usedStop = false;
    presenter.stylesBeforeFullscreen = {
        changedStyles: false,
        style: null,
        moduleWidth: 0,
        moduleHeight: 0,
        actualTime: -1,
        className: ''
    };

    presenter.originalVideoSize = {
        width: 0,
        height: 0
    };

    presenter.captionsOffset = {
        left: 0,
        top: 0
    };

    presenter.captionsScale = {
        xScale: 1.0,
        yScale: 1.0
    };

    presenter.lastWidthAndHeightValues = {
        width: 0,
        height: 0
    };

    presenter.addedVideoURLS = {};

    presenter.configuration = {
        isValid: false,
        addonSize: {
            width: 0,
            height: 0
        },
        addonID: null,
        isVisibleByDefault: null,
        shouldHideSubtitles: null,
        defaultControls: null,
        files: [],
        height: 0,
        showPlayButton: false,
        offlineMessage: "",
        baseDimensions: {
            width: 0,
            height: 0
        }
    };

    presenter.lastSentCurrentTime = 0;

    function deferredQueueDecoratorChecker() {
        return presenter.isVideoLoaded;
    }

    presenter.metadataLoadedDecorator = function (fn) {
        return function () {
            if (presenter.metadadaLoaded) {
                return fn.apply(this, arguments);
            } else {
                presenter.pushToMetadataQueue(fn, arguments);
            }
        }
    };

    presenter.pushToMetadataQueue = function (fn, providedArguments) {
        presenter.metadataQueue.push({
            function: fn,
            arguments: providedArguments,
            self: this
        });
    };

    presenter.upgradeShowPlayButton = function (model) {
        if (!model['Show play button']) {
            model['Show play button'] = 'False';
        }

        return model;
    };

    presenter.upgradeTimeLabels = function (model) {
        var upgradedModel = {};
        $.extend(true, upgradedModel, model); // Deep copy

        for (var i = 0; i &lt; model.Files.length; i++) {
            if (!upgradedModel.Files[i].time_labels) {
                upgradedModel.Files[i].time_labels = "";
            }
        }

        return upgradedModel;
    };

    presenter.upgradeHLS = function (model) {
        var upgradedModel = {};
        $.extend(true, upgradedModel, model); // Deep copy

        for (var i = 0; i &lt; model.Files.length; i++) {
            if (!upgradedModel.Files[i]['m3u8 video']) {
                upgradedModel.Files[i]['m3u8 video'] = "";
            }
        }

        return upgradedModel;
    };

    presenter.upgradeModel = function (model) {
        var upgradedModel = presenter.upgradePoster(model);
        upgradedModel = presenter.upgradeTimeLabels(upgradedModel);
        upgradedModel = presenter.upgradeSpeechTexts(upgradedModel);
        upgradedModel = presenter.upgradeOfflineMessage(upgradedModel);
        upgradedModel = presenter.upgradeVideoSpeedController(upgradedModel);
        upgradedModel = presenter.upgradeShowPlayButton(upgradedModel);
        upgradedModel = presenter.upgradeBaseDimensions(upgradedModel);
        upgradedModel = presenter.removeShowVideoFromModel(upgradedModel);
        return presenter.upgradeHLS(upgradedModel);
    };

    presenter.upgradePoster = function (model) {
        var upgradedModel = {};
        $.extend(true, upgradedModel, model); // Deep copy of model object

        for (var i = 0; i &lt; model.Files.length; i++) {
            if (!upgradedModel.Files[i].Poster) {
                upgradedModel.Files[i].Poster = "";
            }
        }

        return upgradedModel;
    };

    presenter.upgradeSpeechTexts = function (model) {
        var upgradedModel = {};
        $.extend(true, upgradedModel, model); // Deep copy of model object

        for (var i = 0; i &lt; model.Files.length; i++) {
            if (!upgradedModel.Files[i]["Audio Description"]) {
                upgradedModel.Files[i]["Audio Description"] = "";
            }
        }

        if (!model.speechTexts) {
            upgradedModel.speechTexts = {
                AudioDescriptionEnabled: {AudioDescriptionEnabled: "Audio description enabled"},
                AudioDescriptionDisabled: {AudioDescriptionDisabled: "Audio description disabled"}
            }
        }

        return upgradedModel;
    };

    presenter.upgradeOfflineMessage = function (model) {
        var upgradedModel = {};
        $.extend(true, upgradedModel, model);

        if (!upgradedModel["offlineMessage"]) {
            upgradedModel["offlineMessage"] = "This video is not available offline. Please connect to the Internet to watch it."
        }

        return upgradedModel;
    };

    presenter.upgradeVideoSpeedController = function(model) {
        var upgradedModel = {};
        $.extend(true, upgradedModel, model);

        if (!upgradedModel.hasOwnProperty('enableVideoSpeedController')) {
            upgradedModel['enableVideoSpeedController'] = 'False';
        }

        return upgradedModel;
    }

    presenter.upgradeBaseDimensions = function(model) {
        var upgradedModel = {};
        $.extend(true, upgradedModel, model);

        if (!upgradedModel.hasOwnProperty('Base width')) {
            upgradedModel['Base width'] = '';
        }

        if (!upgradedModel.hasOwnProperty('Base height')) {
            upgradedModel['Base height'] = '';
        }

        return upgradedModel;
    }

    presenter.removeShowVideoFromModel = function(model) {
        const upgradedModel = {};
        $.extend(true, upgradedModel, model);

        if (upgradedModel.hasOwnProperty("Show video")) {
            delete upgradedModel["Show video"];
        }

        return upgradedModel;
    };

    presenter.callMetadataLoadedQueue = function () {
        for (var i = 0; i &lt; presenter.metadataQueue.length; i++) {
            var queueElement = presenter.metadataQueue[i];
            queueElement.function.apply(queueElement.self, queueElement.arguments);
        }

        presenter.metadataQueue = [];
    };

    presenter.ERROR_CODES = {
        'MEDIA_ERR_ABORTED': 1,
        'MEDIA_ERR_DECODE': 2,
        'MEDIA_ERR_NETWORK': 3,
        'MEDIA_ERR_SRC_NOT_SUPPORTED': [4, 'Ups ! Looks like your browser doesn\'t support this codecs. Go &lt;a href="https://tools.google.com/dlpage/webmmf/" &gt; -here- &lt;/a&gt; to download WebM plugin'],
        'NVT01': "Not valid data format in time labels property",
        'NVD01': "Base width and height must be either positive integers or empty"
    };

    presenter.getVideoErrorMessage = function (errorCode) {
        var errorMessage = 'We are terribly sorry, but an error has occurred: ';

        switch (errorCode) {
            case presenter.ERROR_CODES.MEDIA_ERR_ABORTED:
                errorMessage += 'you aborted the video playback.';
                break;
            case presenter.ERROR_CODES.MEDIA_ERR_NETWORK:
                errorMessage += 'a network error caused the video download to fail part-way.';
                break;
            case presenter.ERROR_CODES.MEDIA_ERR_DECODE:
                errorMessage += 'the video playback was aborted due to a corruption problem or because the video used features your browser did not support.';
                break;
            case presenter.ERROR_CODES.MEDIA_ERR_SRC_NOT_SUPPORTED:
                errorMessage += 'the video could not be loaded, either because the server or network failed or because the format is not supported.';
                break;
            default:
                errorMessage += 'unknown.';
                break;
        }

        return errorMessage + ' Please refresh page.';
    };

    presenter.videoTypes = [
        {name: 'MP4 video', type: 'video/mp4'},
        {name: 'Ogg video', type: 'video/ogg'},
        {name: 'WebM video', type: 'video/webm'},
        {name: 'm3u8 video', type: 'application/x-mpegURL'}
    ];

    presenter.VIDEO_STATE = {
        STOPPED: 0,
        PLAYING: 1,
        PAUSED: 2
    };

    function fullScreenChange() {
        if (presenter.configuration.isFullScreen) {
            $(presenter.videoContainer).css({
                width: "100%",
                height: "100%"
            });

            $(presenter.videoObject).css({
                width: "100%",
                height: "100%",
                position: 'fixed',
                left: '0px',
                top: '0px'
            });

            if (presenter.configuration.defaultControls) {
                $(presenter.controlBar.getMainElement()).css('position', "fixed");
            }

            presenter.$posterWrapper.hide();

        } else {
            $(presenter.videoContainer).css({
                width: presenter.configuration.dimensions.container.width + 'px',
                height: presenter.configuration.dimensions.container.height + 'px',
                position: presenter.isHLS() ? 'absolute' : 'relative'
            });
            $(presenter.videoObject).css({
                width: presenter.configuration.dimensions.video.width + 'px',
                height: presenter.configuration.dimensions.video.height + 'px',
                position: presenter.isHLS() ? 'absolute' : 'relative'
            });

            if (presenter.configuration.defaultControls) {
                $(presenter.controlBar.getMainElement()).css('position', "absolute");
            }

        }

        $(presenter.videoObject).on("loadedmetadata", function onLoadedMeta() {
            presenter.$view.find(".poster-wrapper").show();
            $(presenter.videoObject).off("loadedmetadata");
        });

        $(presenter.videoObject).on("canplay", function onCanPlay() {
            $(presenter.videoObject).off("canplay");
        });
    }

    presenter.registerHook = function () {
        presenter.mathJaxHook = MathJax.Hub.Register.MessageHook("End Process", function mathJaxResolve(message) {
            if ($(message[1]).hasClass('ic_page')) {
                presenter.mathJaxProcessEndedDeferred.resolve();
            }
        });
    };

    presenter.setPlayerController = function (controller) {
        var mathJaxDeferred = new jQuery.Deferred();
        presenter.mathJaxProcessEndedDeferred = mathJaxDeferred;
        presenter.mathJaxProcessEnded = mathJaxDeferred.promise();
        presenter.playerController = controller;
        presenter.registerHook();

        presenter.eventBus = controller.getEventBus();
        presenter.eventBus.addEventListener('PageLoaded', this);

        var pageLoadedDeferred = new jQuery.Deferred();
        presenter.pageLoadedDeferred = pageLoadedDeferred;
        presenter.pageLoaded = pageLoadedDeferred.promise();
    };

    presenter.onEventReceived = function (eventName, eventData) {
        presenter.pageLoadedDeferred.resolve();
        if (eventData.value === 'dropdownClicked' &amp;&amp; !presenter.videoObject.playing &amp;&amp; !isTemporarilyPaused()) {
            presenter.metadadaLoaded = false;
            presenter.videoObject.load();
        }
    };

    function isTemporarilyPaused() {
        return (presenter.videoObject.paused
            &amp;&amp; presenter.videoObject.readyState &gt; 2
            &amp;&amp; presenter.videoObject.currentTime &gt; 0
            &amp;&amp; !presenter.videoObject.ended
        );
    }

    presenter.createEndedEventData = function (currentVideo) {
        return {
            source: presenter.configuration.addonID,
            item: '' + (currentVideo + 1),
            value: 'ended'
        };
    };

    presenter.formatTime = function addonVideo_formatTime(seconds) {
        if (seconds &lt; 0 || isNaN(seconds)) {
            return "00:00";
        }
        var minutes = Math.floor(seconds / 60);
        minutes = (minutes &gt;= 10) ? minutes : "0" + minutes;
        seconds = Math.floor(seconds % 60);
        seconds = (seconds &gt;= 10) ? seconds : "0" + seconds;
        return minutes + ":" + seconds;
    };

    presenter.sendTimeUpdateEvent = function Video_sendTimeUpdate(formattedTime) {
        presenter.eventBus.sendEvent('ValueChanged', {
            source: presenter.configuration.addonID,
            item: (presenter.currentMovie + 1),
            value: formattedTime
        });
    };

    presenter.sendVideoEndedEvent = function () {
        var eventData = presenter.createEndedEventData(presenter.currentMovie);
        presenter.eventBus.sendEvent('ValueChanged', eventData);
    };

    presenter.stopPropagationOnClickEvent = function (e) {
        e.stopPropagation();
    };

    presenter.setMetaDataOnMetaDataLoadedEvent = function () {
        if (DevicesUtils.isFirefox()) {
            presenter.$view.find(".video-container").prepend(presenter.videoObject);
        }

        presenter.metadadaLoaded = true;
        presenter.originalVideoSize = presenter.getVideoSize(presenter.configuration.addonSize, presenter.videoObject);
        presenter.calculateCaptionsOffset(presenter.configuration.addonSize, true);

        if (presenter.configuration.defaultControls) {
            presenter.$view.find('.video-container').append(presenter.controlBar.getMainElement());
            presenter.controlBar.setMaxDurationTime(presenter.videoObject.duration);
            if (presenter.stylesBeforeFullscreen.actualTime !== -1) {
                presenter.videoObject.currentTime = presenter.stylesBeforeFullscreen.actualTime;
                presenter.play();
                presenter.stylesBeforeFullscreen.actualTime = -1;
            }
        }

        presenter.callMetadataLoadedQueue();
    };

    presenter.calculateCaptionsOffset = function (size, changeWidth) {
        var videoSize = presenter.getVideoSize(size, presenter.videoObject);

        presenter.captionsOffset.left = Math.abs(size.width - videoSize.width) / 2;
        presenter.captionsOffset.top = Math.abs(size.height - videoSize.height) / 2;

        presenter.$captionsContainer.css({
            top: presenter.captionsOffset.top,
            left: presenter.captionsOffset.left,
            position: "absolute"
        });

        if (changeWidth) {
            presenter.$captionsContainer.css({
                width: videoSize.width / presenter.captionsScale.xScale,
                height: videoSize.height / presenter.captionsScale.yScale
            });
        }
    };

    /**
     * @param  {{width: Number, height:Number}} size
     * @param  {HTMLVideoElement} video
     * @returns {{width: Number, height:Number}} calculated video size
     */
    presenter.getVideoSize = function (size, video) {
        //https://stackoverflow.com/questions/17056654/getting-the-real-html5-video-width-and-height
        var videoRatio = video.videoWidth / video.videoHeight;
        var width = size.width, height = size.height;
        var elementRatio = width / height;

        if (elementRatio &gt; videoRatio) {
            width = height * videoRatio;
        } else {
            height = width / videoRatio;
        }

        return {
            width: width,
            height: height
        };
    };

    function setVideoStateOnPlayEvent() {
        presenter.videoState = presenter.VIDEO_STATE.PLAYING;
        presenter.addClassToView('playing');
    }

    function setVideoStateOnPauseEvent() {
        if (!presenter.isHideExecuted) {
            presenter.videoState = presenter.VIDEO_STATE.PAUSED;
            presenter.removeClassFromView('playing');
        }

        delete presenter.isHideExecuted;
    }

    presenter.removeMathJaxHook = function () {
        MathJax.Hub.signal.hooks["End Process"].Remove(presenter.mathJaxHook);
    };

    presenter.onDestroy = function () {
        var view = document.getElementsByClassName('ic_page');

        if (presenter.hlsPlayer != null) {
            presenter.hlsPlayer.off('click');
            presenter.hlsPlayer.off('error');
            presenter.hlsPlayer.off('loadedmetadata');
            presenter.hlsPlayer.off('play');
            presenter.hlsPlayer.off('pause');
            presenter.hlsPlayer.off('playing');

            presenter.hlsPlayer.off('canplay');
            presenter.hlsPlayer.dispose();
            presenter.hlsPlayer = null;
        }

        if (presenter.configuration.defaultControls) {
            presenter.controlBar.destroy();
        }

        presenter.stop();

        presenter.videoObject.removeEventListener('click', presenter.stopPropagationOnClickEvent);
        presenter.videoObject.removeEventListener('loadedmetadata', presenter.setMetaDataOnMetaDataLoadedEvent);
        presenter.videoObject.removeEventListener('play', setVideoStateOnPlayEvent);
        presenter.videoObject.removeEventListener('pause', setVideoStateOnPauseEvent);
        presenter.videoObject.removeEventListener('stalled', presenter.onStalledEventHandler);
        presenter.videoObject.removeEventListener('webkitfullscreenchange', fullScreenChange);
        $(document).off('webkitfullscreenchange mozfullscreenchange fullscreenchange MSFullscreenChange');
        document.removeEventListener("mozfullscreenchange", fullScreenChange);

        presenter.$videoObject.unbind("ended");
        presenter.$videoObject.unbind("error");
        presenter.$videoObject.unbind("canplay");
        presenter.$videoObject.unbind('timeupdate');

        presenter.removeMathJaxHook();
        presenter.$view.off();

        presenter.videoObject.src = '';
        presenter.mathJaxHook = null;
        presenter.eventBus = null;
        presenter.view = null;
        presenter.viewObject = null;
        presenter.videoObject = null;

        $(view).off('click');
        $(window).off('click');
    };

    presenter.resizeVideoToWindow = function () {
        var body = document.getElementsByTagName('body')[0];

        var video = presenter.videoContainer.get(0);
        presenter.stylesBeforeFullscreen.actualTime = presenter.videoObject.currentTime;
        presenter.stylesBeforeFullscreen.style = {
            position: video.style.position,
            top: video.style.top,
            left: video.style.left,
            zIndex: video.style.zIndex,
            className: video.className
        };

        presenter.stylesBeforeFullscreen.changedStyles = true;
        video.style.position = "fixed";
        video.style.top = "0";
        video.style.left = "0";
        video.style.zIndex = 20000;
        video.className = video.className + " " + presenter.$view[0].className;
        body.appendChild(video);
        presenter.metadadaLoaded = false;
        presenter.videoObject.load();
        presenter.scalePosterToWindowSize();
        presenter.scaleCaptionsContainerToVideoNewVideoSize();
    };

    presenter.scalePosterToWindowSize = presenter.metadataLoadedDecorator(function () {
        var size = {
            width: $(presenter.videoObject).width(),
            height: $(presenter.videoObject).height()
        };

        presenter.calculatePosterSize(presenter.videoObject, size);
    });

    presenter.fullScreen = function () {
        var requestMethod = requestFullscreen(presenter.videoContainer);
        presenter.stylesBeforeFullscreen.moduleWidth = presenter.$view.width();
        presenter.stylesBeforeFullscreen.moduleHeight = presenter.$view.height();
        if (requestMethod === null) {
            presenter.resizeVideoToWindow();
        } else {
            presenter.scaleCaptionsContainerToScreenSize();

            var size = {
                width: screen.width,
                height: screen.height
            };
            presenter.calculatePosterSize(presenter.videoObject, size);
        }

        presenter.configuration.isFullScreen = true;
        presenter.playerController.setAbleChangeLayout(false);
        fullScreenChange();
    };

    presenter.closeFullscreen = function () {
        if (presenter.stylesBeforeFullscreen.changedStyles === true) {
            presenter.stylesBeforeFullscreen.actualTime = presenter.videoObject.currentTime;
            presenter.stylesBeforeFullscreen.changedStyles = false;
            var video = presenter.videoContainer.get(0);
            presenter.videoView.appendChild(video);
            presenter.metadadaLoaded = false;
            presenter.videoObject.load();
            video.style.position = presenter.stylesBeforeFullscreen.style.position;
            video.style.top = presenter.stylesBeforeFullscreen.style.top;
            video.style.left = presenter.stylesBeforeFullscreen.style.left;
            video.style.zIndex = presenter.stylesBeforeFullscreen.style.zIndex;
            video.className = presenter.stylesBeforeFullscreen.style.className;
        } else {
            exitFullscreen();
        }
        presenter.configuration.isFullScreen = false;
        presenter.removeScaleFromCaptionsContainer();
        fullScreenChange();

        presenter.calculatePosterSize(presenter.videoObject, presenter.configuration.addonSize);
        presenter.playerController.setAbleChangeLayout(true);
    };

    presenter.switchAudioDescriptionEnabled = function() {
        if (presenter.isAudioDescriptionEnabled == null) {
            setAudioDescriptionEnabled(false);
        } else {
            setAudioDescriptionEnabled(!presenter.isAudioDescriptionEnabled);
        }
    };

    function setAudioDescriptionEnabled(isEnabled) {
        presenter.isAudioDescriptionEnabled = isEnabled;
        if (presenter.isAudioDescriptionEnabled) {
            speak([window.TTSUtils.getTextVoiceObject(presenter.speechTexts.audioDescriptionEnabled)]);
        } else {
            speak([window.TTSUtils.getTextVoiceObject(presenter.speechTexts.audioDescriptionDisabled)]);
            setAudioDescriptionDisabled();
        }
    }

    function setAudioDescriptionDisabled(){
        if ('speechSynthesis' in window) {
            window.speechSynthesis.cancel();
        }
        if (window.responsiveVoice &amp;&amp; window.responsiveVoice.isPlaying()) {
            audioDescriptionEndedCallback();
            window.responsiveVoice.cancel();
        }
        for ( var i = 0; i &lt; presenter.descriptions.length; i++) {
            var description = presenter.descriptions[i];
            $(description.element).css('visibility', 'hidden');
            $(description.element).attr('visibility', 'hidden');
        }
    }

    presenter.showAudioDescription = function() {
        setAudioDescriptionEnabled(true);
    };

    presenter.hideAudioDescription = function() {
        setAudioDescriptionEnabled(false);
    };

    presenter.changePlayingSpeed = function(diff) {
            const playingSpeedOptions = [0.25, 0.5, 0.75, 1.0, 1.25, 1.5, 1.75, 2];
            let currentIndex = playingSpeedOptions.indexOf(presenter.videoObject.playbackRate);
            if (currentIndex == -1) return;
            currentIndex += diff;
            if (currentIndex &lt; 0) currentIndex = 0;
            if (currentIndex &gt;= playingSpeedOptions.length) currentIndex = playingSpeedOptions.length - 1;
            presenter.videoObject.playbackRate = playingSpeedOptions[currentIndex];
        }


    presenter.keyboardController = function (keycode, isShift, event, keysDownCodes) {
        event.preventDefault();

        function increasedVolume() {
            var val = Math.round((presenter.videoObject.volume + 0.1) * 10) / 10;

            return val &gt; 1 ? 1 : val;
        }

        function decreasedVolume() {
            var val = Math.round((presenter.videoObject.volume - 0.1) * 10) / 10;

            return val &lt; 0 ? 0 : val;
        }

        function forward() {
            presenter.videoObject.currentTime += 15;
        }

        function backward() {
            presenter.videoObject.currentTime -= 15;
        }

        function playPause() {
            if (presenter.videoObject.paused) {
                presenter.play();
            } else {
                presenter.pause();
            }
        }

        function nextTimeLabel() {
            var currentTime = presenter.videoObject.currentTime;
            var currentElement = presenter.configuration.files[presenter.currentMovie],
                /**
                 * @type {{title: String, time: Number}[]}
                 */
                timeLabels = currentElement.timeLabels;


            for (var i = 0; i &lt; timeLabels.length; i++) {
                var element = timeLabels[i];

                if (element.time &gt; currentTime) {
                    presenter.seek(element.time);
                    break;
                }
            }
        }

        function previousTimeLabel() {
            var currentTime = presenter.videoObject.currentTime - 2;
            var currentElement = presenter.configuration.files[presenter.currentMovie],
                /**
                 * @type {{title: String, time: Number}[]}
                 */
                timeLabels = currentElement.timeLabels;

            for (var i = timeLabels.length - 1; i &gt;= 0; i--) {
                var element = timeLabels[i];

                if (element.time &lt; currentTime) {
                    presenter.seek(element.time);
                    break;
                }
            }
        }

        function increaseSpeed() {
            presenter.changePlayingSpeed(1);
            presenter.updateVideoSpeedController();
        }

        function decreaseSpeed() {
            presenter.changePlayingSpeed(-1);
            presenter.updateVideoSpeedController();
        }

        switch (keycode) {
            case 32:
                playPause();
                break;
            case 38:
                if (keysDownCodes.indexOf(83) &gt; -1) { // Press S at the same time
                    increaseSpeed();
                } else {
                    presenter.videoObject.volume = increasedVolume();
                }
                break;
            case 40:
                if (keysDownCodes.indexOf(83) &gt; -1) { // Press S at the same time
                    decreaseSpeed();
                } else {
                    presenter.videoObject.volume = decreasedVolume();
                }
                break;
            case 37:
                if (!isShift) {
                    backward();
                } else {
                    previousTimeLabel();
                }
                break;
            case 39:
                if (!isShift) {
                    forward();
                } else {
                    nextTimeLabel();
                }
                break;
            case 27:
                presenter.pause();
                break;
            case 70:
                presenter.fullScreen();
                break;
            case 65: // A
                presenter.switchAudioDescriptionEnabled();
                break;
            case 83: // S
                if (keysDownCodes.indexOf(38) &gt; -1) { // Press UP at the same time
                    increaseSpeed();
                } else if (keysDownCodes.indexOf(40) &gt; -1) { // Press DOWN at the same time
                    decreaseSpeed();
                }
                break;
        }
    };

    /**
     *
     * @param {String} timeLabel
     */
    presenter.validateTimeLabel = function (timeLabel, index) {
        var title = timeLabel.split(' ').slice(1).join(' '),
            time = timeLabel.split(' ')[0],
            //[Sec, Min, Hour]
            timeMultiplication = [1, 60, 60 * 60],
            timeElements = time.split(':'),
            i;

        if (timeElements.length === 0 || timeElements.length &gt; 3) {
            return {
                isValid: false,
                errorCode: "NVT01"
            };
        }

        for (i = 0; i &lt; timeElements.length; i++) {
            if (!timeElements[i].match(/^[0-9]+$/g)) {
                return {
                    isValid: false,
                    errorCode: "NVT01"
                };
            }
        }

        if (title.trim() === '') {
            title = index + ". " + time;
        }

        var timeInSeconds = 0;

        timeElements = timeElements.reverse();
        for (i = timeElements.length - 1; i &gt;= 0; i--) {
            timeInSeconds += parseInt(timeElements[i], 10) * timeMultiplication[i];
        }

        if (isNaN(timeInSeconds)) {
            return {
                isValid: false,
                errorCode: "NVT01"
            };
        }

        return {
            isValid: true,
            title: title,
            time: timeInSeconds
        };
    };

    presenter.validateTimeLabels = function (file) {
        var timeLabelsText = file['time_labels'],
            timeLabels = timeLabelsText.match(/[^\r\n]+/g) || [],  //https://stackoverflow.com/questions/5034781/js-regex-to-split-by-line
            validatedTimeLabels = [];

        for (var i = 0; i &lt; timeLabels.length; i++) {
            var validatedTimeLabel = presenter.validateTimeLabel(timeLabels[i], i + 1);
            if (!validatedTimeLabel.isValid) {
                return validatedTimeLabel;
            }

            validatedTimeLabels.push(validatedTimeLabel);
        }

        validatedTimeLabels = validatedTimeLabels.sort(function (a, b) {
            return a.time - b.time;
        });

        return {
            isValid: true,
            value: validatedTimeLabels
        }
    };

    presenter.validateFile = function (file) {
        var validatedTimeLabels = presenter.validateTimeLabels(file);
        if (!validatedTimeLabels.isValid) {
            return validatedTimeLabels;
        }

        var fileToReturn = {
            "Ogg video": file['Ogg video'],
            "MP4 video": file['MP4 video'],
            "WebM video": file['WebM video'],
            "m3u8 video": file['m3u8 video'],
            "Subtitles": file['Subtitles'],
            "Audiodescription": file['Audio Description'],
            "Poster": file['Poster'],
            "ID": file['ID'],
            "AlternativeText": file['AlternativeText'],
            "Loop video": ModelValidationUtils.validateBoolean(file['Loop video']),
            timeLabels: validatedTimeLabels.value
        };

        return {
            isValid: true,
            file: fileToReturn
        };

    };

    presenter.validateFiles = function (model) {
        var modelFiles = model.Files;
        var files = [];

        for (var i = 0; i &lt; modelFiles.length; i++) {
            var validatedFile = presenter.validateFile(modelFiles[i]);
            if (!validatedFile.isValid) {
                return validatedFile;
            }

            files.push(validatedFile.file);
        }

        return {
            isValid: true,
            files: files
        };
    };

    function getSpeechTextProperty (rawValue, defaultValue) {
        var value = rawValue.trim();

        if (value === undefined || value === null || value === '') {
            return defaultValue;
        }

        return value;
    }

    function setSpeechTexts (speechTexts) {
        presenter.speechTexts = {
            audioDescriptionEnabled:  'Audio description enabled',
            audioDescriptionDisabled: 'Audio description disabled'
        };

        if (!speechTexts) {
            return;
        }

        presenter.speechTexts = {
            audioDescriptionEnabled: getSpeechTextProperty(speechTexts['AudioDescriptionEnabled']['AudioDescriptionEnabled'], presenter.speechTexts.audioDescriptionEnabled),
            audioDescriptionDisabled: getSpeechTextProperty(speechTexts['AudioDescriptionDisabled']['AudioDescriptionDisabled'], presenter.speechTexts.audioDescriptionDisabled)
        };
    }

    presenter.validateModel = function (model) {
        var validatedFiles = presenter.validateFiles(model);
        setSpeechTexts(model["speechTexts"]);
        if (!validatedFiles.isValid) {
            return validatedFiles;
        }

        var baseDimensions = {
            width: 0,
            height: 0
        };
        if (model['Base width'].trim().length !== 0) {
            var validatedBaseWidth = ModelValidationUtils.validatePositiveInteger(model['Base width']);
            if (validatedBaseWidth.isValid) {
                baseDimensions.width = validatedBaseWidth.value;
            } else {
                validatedBaseWidth.errorCode = 'NVD01';
                return validatedBaseWidth;
            }
        }
        if (model['Base height'].trim().length !== 0) {
            var validatedBaseHeight = ModelValidationUtils.validatePositiveInteger(model['Base height']);
            if (validatedBaseHeight.isValid) {
                baseDimensions.height = validatedBaseHeight.value;
            } else {
                validatedBaseHeight.errorCode = 'NVD01';
                return validatedBaseHeight;
            }
        }

        return {
            isValid: true,
            addonSize: {
                width: parseInt(model.Width, 10),
                height: parseInt(model.Height, 10)
            },
            addonID: model.ID,
            isVisibleByDefault: ModelValidationUtils.validateBoolean(model["Is Visible"]),
            shouldHideSubtitles: ModelValidationUtils.validateBoolean(model["Hide subtitles"]),
            defaultControls: !ModelValidationUtils.validateBoolean(model['Hide default controls']),
            files: validatedFiles.files,
            originalFiles: JSON.parse(JSON.stringify(validatedFiles.files)), //deep copy
            height: parseInt(model.Height, 10),
            showPlayButton: ModelValidationUtils.validateBoolean(model['Show play button']),
            isTabindexEnabled: ModelValidationUtils.validateBoolean(model["Is Tabindex Enabled"]),
            offlineMessage: model["offlineMessage"],
            enableVideoSpeedController: ModelValidationUtils.validateBoolean(model["enableVideoSpeedController"]),
            baseDimensions: baseDimensions
        }
    };

    presenter.checkPlayButtonVisibility = function () {
        if (!presenter.configuration.showPlayButton) {
            presenter.$view.find('.video-poster-play').hide();
        }
    };

    presenter.cachePosters = function () {
        for (var fileNumber = 0; fileNumber &lt; presenter.configuration.files.length; fileNumber++) {
            presenter.cachePoster(fileNumber);
        }
    };

    presenter.cachePoster = function (fileNumber) {
        var posterSource = presenter.configuration.files[fileNumber].Poster;
        if (typeof posterSource == 'object') return; //don't cache twice
        if (posterSource) {
            var image = new Image();
            image.src = posterSource;

            presenter.configuration.files[fileNumber].Poster = image;
        }
    };


    presenter.showPlayButton = function () {
        if (presenter.configuration.showPlayButton) {
            presenter.posterPlayButton.show();
        }
    };

    presenter.hidePlayButton = function () {
        if (presenter.configuration.showPlayButton) {
            presenter.posterPlayButton.hide();
        }
    };

    presenter.setBurgerMenu = function () {
        var BURGER_MENU = "time_labels";
        if (!presenter.configuration.defaultControls) {
            return;
        }

        presenter.controlBar.removeBurgerMenu(BURGER_MENU);

        var currentElement = presenter.configuration.files[presenter.currentMovie],
            /**
             * @type {{title: String, time: Number}[]}
             */
            labels = currentElement.timeLabels;

        if (labels.length === 0) {
            return;
        }

        var elementsForBurger = labels.map(function (value) {
            return {
                title: value.title,
                callback: function () {
                    presenter.seek(value.time);
                }
            };
        });

        presenter.controlBar.addBurgerMenu(BURGER_MENU, elementsForBurger);
    };

    presenter.addVideoSpeedController = function () {
        if (!presenter.configuration.defaultControls || !presenter.configuration.enableVideoSpeedController) {
            return;
        }

        presenter.isVideoSpeedControllerAdded = true;
        presenter.controlBar.addVideoSpeedController(presenter.setPlaybackRate);
    };

    presenter.resetVideoSpeedController = function () {
        if (!presenter.configuration.defaultControls || !presenter.configuration.enableVideoSpeedController) {
            return;
        }

        presenter.controlBar.resetPlaybackRateSelectValue();
        presenter.setPlaybackRate(1.0);
    };

    presenter.run = function (view, model) {
        var upgradedModel = presenter.upgradeModel(model);
        var validatedModel = presenter.validateModel(upgradedModel);
        if (!validatedModel.isValid) {
            DOMOperationsUtils.showErrorMessage(view, presenter.ERROR_CODES, validatedModel.errorCode);
            return;
        }

        presenter.configuration = $.extend(presenter.configuration, validatedModel);

        presenter.cachePosters();

        presenter.videoState = presenter.VIDEO_STATE.STOPPED;
        presenter.videoView = view;
        presenter.$view = $(view);

        presenter.posterPlayButton = $(view).find('.video-poster-play');
        presenter.videoContainer = $(view).find('.video-container:first');
        presenter.$captionsContainer = presenter.$view.find(".captions-container:first");
        presenter.$posterWrapper = presenter.$view.find('.poster-wrapper');
        presenter.$mask = presenter.$view.find('.video-container-mask');

        presenter.videoObject = presenter.videoContainer.find('video')[0];
        presenter.$videoObject = $(presenter.videoObject);

        Object.defineProperty(presenter.videoObject, 'playing', {
           get: function () {
               return !!(this.currentTime &gt; 0 &amp;&amp; !this.paused &amp;&amp; !this.ended &amp;&amp; this.readyState &gt; 2);
           }
        });

        presenter.setDimensions();

        presenter.checkPlayButtonVisibility();

        if (presenter.configuration.defaultControls) {
            presenter.buildControlsBars();
        } else {
            presenter.videoContainer.on("click", function () {
                presenter.videoObject = presenter.videoContainer.find('video')[0];
                if (presenter.videoObject.paused) {
                    presenter.play();
                } else {
                    presenter.pause();
                }
            });
        }

        presenter.addTabindex(presenter.configuration.isTabindexEnabled);

        if (presenter.isHLS()) presenter.loadHLSPlayer();

        presenter.connectHandlers();
        presenter.reload();

        if (!presenter.configuration.isVisibleByDefault) {
            presenter.hide();
        }

        presenter.eventBus.addEventListener('ValueChanged', this);

        if (presenter.configuration.shouldHideSubtitles) {
            presenter.hideSubtitles();
        } else {
            presenter.showSubtitles();
        }

        presenter.videoObject.setAttribute('webkit-playsinline', 'webkit-playsinline');
        presenter.videoObject.setAttribute('playsinline', 'playsinline');
    };

    presenter.connectHandlers = function () {
        if (!presenter.isHLS()) {
            presenter.videoObject.addEventListener('click', presenter.stopPropagationOnClickEvent);
            presenter.videoObject.addEventListener('error', function () {
                presenter.handleErrorCode(this.error);
            }, true);
            presenter.videoObject.addEventListener('loadedmetadata', presenter.setMetaDataOnMetaDataLoadedEvent);
            presenter.videoObject.addEventListener('play', setVideoStateOnPlayEvent);
            presenter.videoObject.addEventListener('pause', setVideoStateOnPauseEvent);
            presenter.videoObject.addEventListener('playing', presenter.onVideoPlaying, false);
        }
        $(document).on('webkitfullscreenchange mozfullscreenchange fullscreenchange MSFullscreenChange', presenter.fullscreenChangedEventReceived);

        presenter.addClickListener();
    };

    presenter.addClickListener = function () {
        var view = document.getElementsByClassName('ic_page');
        $(view[0]).on('click', function (event) {
            if (presenter.configuration.defaultControls &amp;&amp; presenter.controlBar.isSelectorOpen &amp;&amp; !event.target.localName.includes('select')) {
                presenter.controlBar.isSelectorOpen = false;
                presenter.controlBar.hideControls();
            }
            event.preventDefault();
            event.stopPropagation();
        });

        $(window).on('click', function (event) {
            if (presenter.configuration.defaultControls &amp;&amp; presenter.controlBar.isSelectorOpen) {
                presenter.controlBar.isSelectorOpen = false;
                presenter.controlBar.hideControls();
            }
            event.preventDefault();
            event.stopPropagation();
        });
    }

    presenter.isHLS = function() {
        if (window.videojs === undefined) return false;
        if (presenter.isHLSValue == null) {
            presenter.isHLSValue = false;
            for (var i = 0; i &lt; presenter.configuration.files.length; i++) {
                var videoFile = presenter.configuration.files[i];
                if (videoFile["m3u8 video"].trim().length &gt; 0) presenter.isHLSValue = true;
            }
        }
        return presenter.isHLSValue;
    }

    presenter.loadHLSPlayer = function() {
        presenter.$videoObject.addClass('video-js vjs-theme-sea vjs-big-play-centered');
        presenter.$videoObject.attr({
            preload: presenter.isHLS() ? 'metadata' : 'auto',
            fluid: "true",
            "data-setup": '{}'
        });
        var videoID = 'videojs-player-' + presenter.configuration.addonID;
        presenter.$videoObject.attr('id', videoID);
        presenter.hlsPlayer = window.videojs(videoID);
        presenter.hlsPlayer.ready(() =&gt; {
            presenter.hlsPlayer.on('click', presenter.stopPropagationOnClickEvent);
            presenter.hlsPlayer.on('error', function () {
                presenter.handleErrorCode(this.error);
            });
            presenter.hlsPlayer.on('loadedmetadata', presenter.setMetaDataOnMetaDataLoadedEvent);
            presenter.hlsPlayer.on('play', setVideoStateOnPlayEvent);
            presenter.hlsPlayer.on('pause', setVideoStateOnPauseEvent);
            presenter.hlsPlayer.on('playing', presenter.onVideoPlaying);

            presenter.hlsPlayer.on('loadedmetadata', function onLoadedMetadata() {
                presenter.isVideoLoaded = true;
                presenter.callTasksFromDeferredQueue();

                $(presenter.videoObject).unbind("canplay");

                if (presenter.areSubtitlesHidden) {
                    presenter.hideSubtitles();
                } else {
                    presenter.showSubtitles();
                }
                }
            );
        });
    }

    presenter.fullscreenChangedEventReceived = function () {
        if (!isVideoInFullscreen() &amp;&amp; presenter.configuration.isFullScreen) {
            presenter.configuration.isFullScreen = false;
            presenter.removeScaleFromCaptionsContainer();
            fullScreenChange();
            presenter.configuration.defaultControls &amp;&amp; presenter.controlBar.showFullscreenButton();

            presenter.calculatePosterSize(presenter.videoObject, presenter.configuration.addonSize);
            presenter.playerController.setAbleChangeLayout(true);
        }
    };

    presenter.checkAddonSize = function () {
        if (presenter.videoContainer.width() !== presenter.lastWidthAndHeightValues.width
            || presenter.videoContainer.height() !== presenter.lastWidthAndHeightValues.height) {

            presenter.lastWidthAndHeightValues.width = presenter.videoContainer.width();
            presenter.lastWidthAndHeightValues.height = presenter.videoContainer.height();

            presenter.calculateCaptionsOffset(presenter.lastWidthAndHeightValues, false);
            presenter.scaleCaptionsContainerToVideoNewVideoSize();
        }
    };

    presenter.buildControlsBars = function () {
        var config = {
            videoObject: presenter.videoObject,
            parentElement: presenter.videoContainer[0],
            isVolumeEnabled: !MobileUtils.isSafariMobile(navigator.userAgent)
        };

        var controls = new window.CustomControlsBar(config);

        controls.addPlayCallback(presenter.play);
        controls.addPauseCallback(presenter.pause);
        controls.addStopCallback(presenter.stop);
        controls.addFullscreenCallback(presenter.fullScreen);
        controls.addCloseFullscreenCallback(presenter.closeFullscreen);
        controls.addProgressChangedCallback(presenter.seekFromPercent);
        controls.addVolumeChangedCallback(presenter.setVolume);
        controls.addCallbackToBuildInTimer(presenter.checkAddonSize);

        presenter.$view.find('.video-container').append(controls.getMainElement());

        presenter.controlBar = controls;
    };

    presenter.scaleCaptionsContainerToVideoNewVideoSize = presenter.metadataLoadedDecorator(function () {
        var size = {
            width: $(presenter.videoObject).width(),
            height: $(presenter.videoObject).height()
        };

        var newVideoSize = presenter.getVideoSize(size, presenter.videoObject);

        var xScale = newVideoSize.width / presenter.originalVideoSize.width;
        var yScale = newVideoSize.height / presenter.originalVideoSize.height;

        if (presenter.configuration.baseDimensions.width != 0) {
            xScale = xScale * (presenter.configuration.addonSize.width/presenter.configuration.baseDimensions.width);
        }
        if (presenter.configuration.baseDimensions.height != 0) {
            yScale = yScale * (presenter.configuration.addonSize.height/presenter.configuration.baseDimensions.height);
        }

        presenter.$captionsContainer.css(generateTransformDict(xScale, yScale));
        presenter.captionsScale.xScale = xScale;
        presenter.captionsScale.yScale = yScale;

        presenter.calculateCaptionsOffset(size, true);
    });

    presenter.scaleCaptionsContainerToScreenSize = presenter.metadataLoadedDecorator(function () {
        var size = {
            width: screen.width,
            height: screen.height
        };

        var newVideoSize = presenter.getVideoSize(size, presenter.videoObject);

        var xScale = newVideoSize.width / presenter.originalVideoSize.width;
        var yScale = newVideoSize.height / presenter.originalVideoSize.height;


        presenter.$captionsContainer.css(generateTransformDict(xScale, yScale));
        presenter.captionsScale.xScale = xScale;
        presenter.captionsScale.yScale = yScale;

        presenter.calculateCaptionsOffset(size, false);
    });

    presenter.removeScaleFromCaptionsContainer = presenter.metadataLoadedDecorator(function () {
        presenter.$captionsContainer.css(generateTransformDict(1, 1));
        presenter.captionsScale.xScale = 1.0;
        presenter.captionsScale.yScale = 1.0;

        presenter.calculateCaptionsOffset(presenter.configuration.addonSize, false);
    });

    presenter.sendOnPlayingEvent = function () {
        var eventData = {
            'source': presenter.configuration.addonID,
            'item': (presenter.currentMovie + 1),
            'value': 'playing',
            'score': ''
        };

        presenter.eventBus.sendEvent('ValueChanged', eventData);
    };

    presenter.onVideoPlaying = function AddonVideo_onVideoPlaying() {
        presenter.sendOnPlayingEvent();

        if (presenter.videoObject.currentTime === 0) {
            presenter.sendTimeUpdateEvent(presenter.formatTime(presenter.videoObject.currentTime))
        }
    };

    presenter.convertTimeStringToNumber = function (timeString) {
        timeString = timeString.split(':');
        var minutes = parseInt(timeString[0] * 60, 10);
        var seconds = parseInt(timeString[1], 10);
        return {isCorrect: true, value: (minutes + seconds)};
    };

    presenter.handleErrorCode = function (error) {
        if (!error) return;

        presenter.$view.html(presenter.getVideoErrorMessage(error.code));
    };

    presenter.createPreview = function (view, model) {
        presenter.isPreview = true;

        var upgradedModel = presenter.upgradeModel(model);
        var validatedModel = presenter.validateModel(upgradedModel);
        if (!validatedModel.isValid) {
            DOMOperationsUtils.showErrorMessage(view, presenter.ERROR_CODES, validatedModel.errorCode);
            return;
        }

        presenter.configuration = $.extend(presenter.configuration, validatedModel);

        presenter.$view = $(view);
        presenter.videoContainer = $(view).find('.video-container:first');

        presenter.setVideo();
        presenter.setDimensions();
    };

    presenter.showCaptions = function (time) {
        if (!presenter.configuration.dimensions) return ; // No captions to show when video wasn't loaded properly
        for (var i = 0; i &lt; presenter.captions.length; i++) {
            var caption = presenter.captions[i];
            if (caption.start &lt;= time &amp;&amp; caption.end &gt;= time) {
                $(caption.element).attr('visibility', 'visible');
                $(caption.element).css('visibility', presenter.isCurrentlyVisible ? 'visible' : 'hidden');
            } else {
                $(caption.element).css('visibility', 'hidden');
                $(caption.element).attr('visibility', 'hidden');
            }
        }
    };

    function getAudioDescriptionEnabled() {
        if (presenter.isAudioDescriptionEnabled != null) {
            return presenter.isAudioDescriptionEnabled;
        }
        if (presenter.playerController &amp;&amp; presenter.playerController.isWCAGOn()) {
            return presenter.playerController.isWCAGOn();
        }
        return false;
    }

    function audioDescriptionEndedCallback() {
        if (presenter) {
            if(!presenter.usedStop) {
                presenter.play();
            }
        }
    }

    presenter.readAudioDescriptions = function (time) {
        if (!presenter.configuration.dimensions) return false;
        if (!presenter.playerController || !getAudioDescriptionEnabled()) return false;
        if ((time &lt; presenter.prevTime) || ((time - presenter.prevTime) &gt; 1.0)) {
            presenter.prevTime = time - 0.001;
            return false;
        }

        var isSpeaking = false;
        for ( var i = 0; i &lt; presenter.descriptions.length; i++) {
            var description = presenter.descriptions[i];
            if (presenter.prevTime &lt; description.start &amp;&amp; description.start &lt;= time) {
                isSpeaking = true;
                presenter.pause();
                $(description.element).attr('visibility', 'visible');
                $(description.element).css('visibility', presenter.isCurrentlyVisible ? 'visible' : 'hidden');
                speakWithCallback([window.TTSUtils.getTextVoiceObject(description.text,description.langTag)], audioDescriptionEndedCallback);
            } else {
                $(description.element).css('visibility', 'hidden');
                $(description.element).attr('visibility', 'hidden');
            }
        }

        presenter.prevTime = time;

        if (isSpeaking) {
            for (var i = 0; i &lt; presenter.captions.length; i++) {
                var caption = presenter.captions[i];
                $(caption.element).css('visibility', 'hidden');
                $(caption.element).attr('visibility', 'hidden');
            }
        }
        return isSpeaking;
    };

    presenter.reload = function () {
        presenter.cachePosters();
        presenter.showPlayButton();
        presenter.isVideoLoaded = false;
        $(presenter.videoContainer).find('.captions').remove();
        presenter.setVideo();
        presenter.loadSubtitles();
        presenter.loadAudioDescription();
        presenter.setBurgerMenu();
        if (presenter.isVideoSpeedControllerAdded) {
            presenter.resetVideoSpeedController();
        } else {
            presenter.addVideoSpeedController();
        }
        $(presenter.videoObject).unbind('timeupdate');
        $(presenter.videoObject).bind("timeupdate", function () {
            onTimeUpdate(this);
        });
        presenter.removeClassFromView('playing');
        presenter.posterPlayButton.removeClass('video-poster-pause');
    };

    presenter.sendTimeUpdate = function Video_sendTime() {
        var actualVideoTime = parseInt(presenter.videoObject.currentTime, 10);
        if (actualVideoTime !== presenter.lastSentCurrentTime) {
            var formattedTime = presenter.formatTime(actualVideoTime, 10);
            presenter.sendTimeUpdateEvent(formattedTime);
            presenter.lastSentCurrentTime = actualVideoTime;
        }
    };

    function onTimeUpdate(video) {
        if (!presenter.videoObject) return;
        if (!presenter.videoObject.paused) {
            var isSpeaking = presenter.readAudioDescriptions(presenter.videoObject.currentTime);
            if (!isSpeaking) {
                presenter.showCaptions(presenter.videoObject.currentTime);
            }
        }

        presenter.sendTimeUpdate();

        var currentTime = Math.round(video.currentTime * 10) / 10,
            videoDuration = Math.round(video.duration * 10) / 10,
            isFullScreen = document.mozFullScreen || document.webkitIsFullScreen;

        var shouldSetAbleChangeLayout = false;
        if (currentTime &gt;= videoDuration) {
            presenter.sendVideoEndedEvent();
            presenter.showWaterMark();
            presenter.prevTime = -0.001;
            if (document.exitFullscreen &amp;&amp; document.fullscreenElement) {
                document.exitFullscreen();
            } else if (document.webkitExitFullscreen) {
                document.webkitExitFullscreen();
            } else if (document.mozCancelFullScreen) {
                document.mozCancelFullScreen();
            } else if (document.msExitFullscreen) {
                document.msExitFullscreen();
            } else if (presenter.configuration.isFullScreen) {
                presenter.configuration.isFullScreen = false;
                presenter.removeScaleFromCaptionsContainer();
                presenter.configuration.defaultControls &amp;&amp; presenter.controlBar.showFullscreenButton();
                presenter.closeFullscreen();
                shouldSetAbleChangeLayout = true;

            }

            if (!presenter.configuration.defaultControls) {
                presenter.seek(0); // sets the current time to 0
                presenter.$posterWrapper.show();
                if (presenter.configuration.showPlayButton) {
                    presenter.posterPlayButton.show();
                }
                presenter.videoObject.pause();
            }

            $(presenter.videoObject).on("canplay", function onCanPlay() {
                currentTime = 0;
                presenter.videoObject.currentTime = currentTime;
                presenter.pause();
                $(presenter.videoObject).off("canplay");
            });

            presenter.lastSentCurrentTime = 0;

            if(shouldSetAbleChangeLayout) {
                presenter.playerController.setAbleChangeLayout(true);
            }
        }
    }

    presenter.getState = function () {
        var isPaused = presenter.videoObject.paused;
        return JSON.stringify({
            files: "deprecated",        //Removed from state.
            videoURLS: presenter.addedVideoURLS,
            currentTime: presenter.videoObject.currentTime,
            isCurrentlyVisible: presenter.isCurrentlyVisible,
            isPaused: isPaused,
            currentMovie: presenter.currentMovie,
            areSubtitlesHidden: presenter.areSubtitlesHidden,
            isAudioDescriptionEnabled: presenter.isAudioDescriptionEnabled
        });
    };

    presenter.setState = function (stateString) {
        if (ModelValidationUtils.isStringEmpty(stateString)) {
            return;
        }
        var state = JSON.parse(stateString);
        var currentTime = state.currentTime;

        if (state.videoURLS) {  //This was added later than rest of state
            for (var i in state.videoURLS) {
                if (state.videoURLS.hasOwnProperty(i)) {
                    var element = state.videoURLS[i];

                    if (element.url === undefined) {
                        console.warn(`Impact of execution addon' "setVideoURL" command was not properly restored for addon with ID: ${presenter.configuration.addonID}`)
                        continue;
                    }

                    presenter._setVideoURL(element.url, element.index);
                }
            }
        }

        presenter.isCurrentlyVisible = state.isCurrentlyVisible;

        if (presenter.isCurrentlyVisible !== (presenter.$view.css('visibility') !== 'hidden')) {
            presenter.setVisibility(presenter.isCurrentlyVisible);
        }

        presenter.currentMovie = state.currentMovie;
        presenter.reload();

        var eventType = presenter.isHLS() ? 'loadedmetadata' : 'canplay';
        $(presenter.videoObject).on(eventType, function onVideoCanPlay() {
            if (presenter.videoObject.currentTime &lt; currentTime) {
                presenter.currentTime = currentTime;
                presenter.videoObject.currentTime = currentTime;
                presenter.startTime = currentTime;
                presenter.videoState = presenter.VIDEO_STATE.PAUSED;
                $(this).off('canplay');
            }

            if (state.areSubtitlesHidden != undefined) {
                if (state.areSubtitlesHidden) {
                    presenter.hideSubtitles();
                } else {
                    presenter.showSubtitles();
                }
            }
        });

        presenter.isAudioDescriptionEnabled = state.isAudioDescriptionEnabled;
    };

    presenter.getIOSVersion = function (userAgent) {
        var match = /CPU OS ([\d_]+) like Mac OS X/.exec(userAgent);
        return match === null ? '' : match[1];
    };

    /**
     * Setting poster for video.
     *
     * Attribute poster is not used because safari wont reload poster while reloading video.
     * @param  {HTMLVideoElement} video
     * @param  {String} posterSource
     */
    presenter.addAttributePoster = presenter.metadataLoadedDecorator(function (video, poster) {
        presenter.$posterWrapper.find("img").remove();
        var $video = $(video);

        if (poster) {
            presenter.$posterWrapper.prepend(poster);

            presenter.calculatePosterSize(video, presenter.configuration.addonSize);

            presenter.$posterWrapper.show();
        } else {
            presenter.$posterWrapper.hide();
            $video.attr('poster', '');
        }
    });

    presenter.calculatePosterSize = presenter.metadataLoadedDecorator(function (video, toSize) {
        var $poster = presenter.$posterWrapper.find("img");

        var calculatedVideoSize = presenter.getVideoSize(toSize, video);

        var left = (toSize.width - calculatedVideoSize.width) / 2;
        var top = (toSize.height - calculatedVideoSize.height) / 2;

        $poster.width(calculatedVideoSize.width);
        $poster.height(calculatedVideoSize.height);
        $poster.css({
            left: left,
            top: top
        });
    });

    presenter.setAltText = function () {
        var files = presenter.configuration.files;
        presenter.$view.find('.video-container-mask').text(files[presenter.currentMovie].AlternativeText);
        presenter.$view.find('.video-container-video').text(files[presenter.currentMovie].AlternativeText);
    };

    presenter.isOnlineResourceOnly = function() {
        for (var i = 0; i &lt; presenter.configuration.files.length; i++) {
            var videoFile = presenter.configuration.files[i];
            var isMP4Local = videoFile["MP4 video"] &amp;&amp; videoFile["MP4 video"].trim().indexOf("file:/") == 0;
            var isOggLocal = videoFile["Ogg video"] &amp;&amp; videoFile["Ogg video"].trim().indexOf("file:/") == 0;
            var isWebMLocal = videoFile["WebM video"] &amp;&amp; videoFile["WebM video"].trim().indexOf("file:/") == 0;
            if (!isMP4Local &amp;&amp; !isOggLocal &amp;&amp; !isWebMLocal) {
                return true;
            }
            return false;
        }
    };

    presenter.setVideo = function () {
        if (!window.navigator.onLine) {
            isOnlineResourceOnly().then(function (responses){
                setVideo();
            }).catch(function(error){
                presenter.$view.html(presenter.configuration.offlineMessage);
            });
        } else {
            setVideo();
        }
    };

    function isOnlineResourceOnly() {
        let endpointsToFetch = [];
        for (let i = 0; i &lt; presenter.configuration.files.length; i++) {
            const videoFile = presenter.configuration.files[i];
            !!videoFile["MP4 video"] &amp;&amp; endpointsToFetch.push(videoFile["MP4 video"].trim());
            !!videoFile["Ogg video"] &amp;&amp; endpointsToFetch.push(videoFile["Ogg video"].trim());
            !!videoFile["WebM video"] &amp;&amp; endpointsToFetch.push(videoFile["WebM video"].trim());
        }
        return Promise.all(endpointsToFetch.map(function(fetchURL) {
            // method: "Head" - used to check if addon have a connection to video (not for download video)
            // method: "no-cache" - mLibro has a cache. Added to avoid the situation that once there is video and
            //      once there is not, when user is offline. Such behavior would be inconsistent with the documentation.
            return fetch(fetchURL, {method: "Head", cache: "no-cache"});
        }));
    }

    function setVideo() {
        if (presenter.videoObject) {
            $(presenter.videoObject).unbind("ended");
            $(presenter.videoObject).unbind("error");
            $(presenter.videoObject).unbind("canplay");

            presenter.videoObject.pause();
        }

        presenter.videoObject = presenter.videoContainer.find('video')[0];
        if (!presenter.videoObject) return;
        if (presenter.isHLS() &amp;&amp; !!presenter.controlBar &amp;&amp; !!presenter.controlBar.configuration) {
            presenter.controlBar.configuration.videoObject = presenter.videoObject;
        }
        if (!presenter.videoObject.hasOwnProperty('playing')) {
            Object.defineProperty(presenter.videoObject, 'playing', {
               get: function () {
                   return !!(this.currentTime &gt; 0 &amp;&amp; !this.paused &amp;&amp; !this.ended &amp;&amp; this.readyState &gt; 2);
               }
            });
        }

        presenter.videoState = presenter.VIDEO_STATE.STOPPED;
        var $video = $(presenter.videoObject);
        var files = presenter.configuration.files;

        presenter.videoContainer.find('source').remove();
        presenter.addAttributePoster($video[0], files[presenter.currentMovie].Poster);

        if (!presenter.isHLS()) presenter.setAltText();
        if (presenter.isPreview) {
            $video.attr('preload', 'none');
        } else {
            $video.attr('preload', presenter.isHLS() ? 'metadata' : 'auto');
            var isNewHLSSourceSet = false;
            for (var vtype in presenter.videoTypes) {
                if (files[presenter.currentMovie][presenter.videoTypes[vtype].name] &amp;&amp; (presenter.videoObject.canPlayType(presenter.videoTypes[vtype].type) || presenter.videoTypes[vtype].type == "application/x-mpegURL")) {
                    var source = $('&lt;source&gt;');
                    source.attr('type', presenter.videoTypes[vtype].type);
                    source.attr('src', files[presenter.currentMovie][presenter.videoTypes[vtype].name]);
                    $video.append(source);
                    if (presenter.hlsPlayer != null &amp;&amp; !isNewHLSSourceSet) {
                        isNewHLSSourceSet = true;
                        presenter.hlsPlayer.src({
                            'type': presenter.videoTypes[vtype].type,
                            'src': files[presenter.currentMovie][presenter.videoTypes[vtype].name]
                        });
                    }
                }
            }

            // "ended" event doesn't work on Safari
            $(presenter.videoObject).unbind('timeupdate');
            $(presenter.videoObject).bind("timeupdate", function () {
                onTimeUpdate(presenter);
            });

            $(presenter.videoObject).bind("error", function onError() {
                $(this).unbind("error");
                presenter.reload();
                if (presenter.configuration.isFullScreen) {
                    fullScreenChange();
                }
            });

            $(presenter.videoObject).bind("canplay", function onCanPlay() {
                presenter.isVideoLoaded = true;
                presenter.callTasksFromDeferredQueue();

                $(this).unbind("canplay");

                if (presenter.areSubtitlesHidden) {
                    presenter.hideSubtitles();
                } else {
                    presenter.showSubtitles();
                }
            });
            // Android devices have problem with loading content.
            presenter.videoObject.addEventListener("stalled", presenter.onStalledEventHandler, false);
            presenter.videoObject.load();
            presenter.metadadaLoaded = false;

            if (files[presenter.currentMovie]['Loop video']) {
                if (typeof presenter.videoObject.loop == 'boolean') {
                    presenter.videoObject.loop = true;
                } else {
                    $(presenter.videoObject).on('ended', function () {
                        presenter.currentTime = 0;
                        presenter.play();
                    }, false);
                }

                presenter.isAborted = false;

                $(presenter.videoObject).on('abort', function () {
                    presenter.isAborted = true;
                });

                $(presenter.videoObject).on('canplay', function () {
                    if (presenter.isAborted &amp;&amp; presenter.playTriggered) {
                        presenter.play();
                    }
                });
            } else {
                if (typeof presenter.videoObject.loop == 'boolean') {
                    presenter.videoObject.loop = false;
                } else {
                    $(presenter.videoObject).off('ended');
                }
            }
        }
    }

    /**
     * Creates DIV element containing caption text.
     *
     * @param caption - used text, top and left properties
     * @return reference do newly created element
     */
    function createCaptionElement(caption, isAudioDescription) {
        const captionElement = document.createElement('div');

        $(captionElement).addClass('captions');
        if(isAudioDescription) {
            $(captionElement).addClass('audio-description');
        }
        $(captionElement).addClass(caption.cssClass);
        const sanitizedText = window.xssUtils.sanitize(caption.text);
        $(captionElement).html(window.TTSUtils.parsePreviewAltText(sanitizedText));
        $(captionElement).css({
            top: caption.top,
            left: caption.left
        });

        $(captionElement).css('visibility', 'hidden');
        $(captionElement).attr('visibility', 'hidden');
        presenter.$captionsContainer.append(captionElement);

        return captionElement;
    }

    presenter.convertLinesToCaptions = function (lines) {
        presenter.captions = [];

        for (var i = 0; i &lt; lines.length; i++) {
            var parts = lines[i].split('|');
            if (parts.length == 6) {
                var caption = {
                    start: parts[0],
                    end: parts[1],
                    top: (StringUtils.endsWith(parts[2], 'px') ? parts[2] : parts[2] + 'px'),
                    left: (StringUtils.endsWith(parts[3], 'px') ? parts[3] : parts[3] + 'px'),
                    cssClass: parts[4],
                    text: parts[5]
                };

                caption.element = createCaptionElement(caption, false);
                presenter.captions.push(caption);

                presenter.captionDivs.push(caption.element);
            }
        }
    };

    presenter.loadSubtitles = function () {
        var subtitlesLoadedDeferred = new $.Deferred(),
            subtitles = presenter.configuration.files[presenter.currentMovie].Subtitles;

        if (subtitles) {
            if (StringUtils.startsWith(subtitles, "/file")) {
                $.get(subtitles, function (data) {
                    subtitlesLoadedDeferred.resolve(data);
                });
            } else {
                subtitlesLoadedDeferred.resolve(subtitles);
            }

            presenter.convertLinesToCaptions(Helpers.splitLines(subtitles));
            $.when(subtitlesLoadedDeferred.promise(), presenter.mathJaxProcessEnded, presenter.pageLoaded).then(function onSubtitlesLoaded(data) {
                presenter.convertLinesToCaptions(Helpers.splitLines(data));
                MathJax.Hub.Queue(["Typeset", MathJax.Hub, presenter.captionDivs])();
            });
        }
    };

    presenter.loadAudioDescription = function () {
        var descriptionsLoadedDeferred = new $.Deferred(),
            descriptions = presenter.configuration.files[presenter.currentMovie].Audiodescription;

        if (descriptions) {
            if (StringUtils.startsWith(descriptions, "/file")) {
                $.get(descriptions, function (data) {
                    descriptionsLoadedDeferred.resolve(data);
                });
            } else {
                descriptionsLoadedDeferred.resolve(descriptions);
            }

            presenter.convertLinesToAudioDescriptions(Helpers.splitLines(descriptions));
            $.when(descriptionsLoadedDeferred.promise(), presenter.mathJaxProcessEnded, presenter.pageLoaded).then(function onDescriptionsLoaded(data) {
                presenter.convertLinesToAudioDescriptions(Helpers.splitLines(data));
                MathJax.Hub.Queue(["Typeset", MathJax.Hub, presenter.descriptionsDivs])();
            });
        }
    };

    presenter.escapeAltText = function(text) {
        function replacer(match, p1, offset, string) {
          return '[' + p1.replace(/\|/g, escapedSeparator) + ']';
        }
        return text.replace(/\[(.*?)\]/g, replacer);
    };
    
    presenter.unescapeAndConvertAltText = function(text) {
        function replacer(match, p1, offset, string) {
          var parts = p1.split(escapedSeparator);
          if (parts.length === 2) {
              return '\\alt{' + parts[0] + '|' + parts[1] + '}';
          }
          if (parts.length === 3) {
              return '\\alt{' + parts[0] + '|' + parts[1] + '}[lang ' + parts[2] + ']';
          }
          return '[' + parts.join('|') + ']';
        }
        return text.replace(/\[(.*?)\]/g, replacer);
    };

    presenter.convertLinesToAudioDescriptions = function (lines) {
        presenter.descriptions = [];

        for (var i = 0; i &lt; lines.length; i++) {
            var line = presenter.escapeAltText(lines[i]);
            var parts = line.split('|');
            if (parts.length == 6) {
                var description = {
                    start: parts[0],
                    top: (StringUtils.endsWith(parts[2], 'px') ? parts[1] : parts[1] + 'px'),
                    left: (StringUtils.endsWith(parts[3], 'px') ? parts[2] : parts[2] + 'px'),
                    cssClass: parts[3],
                    langTag: parts[4],
                    text: presenter.unescapeAndConvertAltText(parts[5])
                };

                description.element = createCaptionElement(description, true);
                presenter.descriptions.push(description);

                presenter.descriptionsDivs.push(description.element);
            }
        }
    };

    presenter.calculateVideoContainerHeight = function ($container, moduleHeight) {
        var borderBottom = $container.css('border-bottom-width'),
            borderTop = $container.css('border-top-width'),
            marginTop = $container.css('margin-top'),
            marginBottom = $container.css('margin-bottom');

        if (ModelValidationUtils.isStringEmpty(borderTop)) borderTop = "0px";
        if (ModelValidationUtils.isStringEmpty(borderBottom)) borderBottom = "0px";
        if (ModelValidationUtils.isStringEmpty(marginTop)) marginTop = "0px";
        if (ModelValidationUtils.isStringEmpty(marginBottom)) marginBottom = "0px";

        return moduleHeight - parseInt(borderBottom, 10) -
            parseInt(borderTop, 10) -
            parseInt(marginTop, 10) -
            parseInt(marginBottom, 10);
    };

    presenter.setDimensions = function () {
        var video = presenter.getVideo();

        presenter.videoContainer.css('height', presenter.calculateVideoContainerHeight(presenter.videoContainer, presenter.configuration.height) + 'px');

        video.css("width", "100%")
            .attr('height', presenter.videoContainer.height());

        presenter.configuration.dimensions = {
            video: {
                width: $(video).width(),
                height: $(video).height()
            },
            container: {
                width: $(presenter.videoContainer).width(),
                height: $(presenter.videoContainer).height()
            }
        };
    };

    presenter.showSubtitles = function () {
        presenter.$view.find('.captions:not(.audio-description)').show();
        presenter.areSubtitlesHidden = false;
    };

    presenter.hideSubtitles = function () {
        presenter.$view.find('.captions:not(.audio-description)').hide();
        presenter.areSubtitlesHidden = true;
    };

    presenter.executeCommand = function (name, params) {
        var commands = {
            'show': presenter.show,
            'hide': presenter.hide,
            'next': presenter.next,
            'previous': presenter.previous,
            'jumpTo': presenter.jumpToCommand,
            'jumpToID': presenter.jumpToIDCommand,
            'seek': presenter.seekCommand,
            'play': presenter.play,
            'stop': presenter.stop,
            'pause': presenter.pause,
            'showSubtitles': presenter.showSubtitles,
            'hideSubtitles': presenter.hideSubtitles,
            'showAudioDescription': presenter.showAudioDescription,
            'hideAudioDescription': presenter.hideAudioDescription,
            'setVideoURL': presenter.setVideoURLCommand
        };

        Commands.dispatch(commands, name, params, presenter);
    };

    presenter.setVideoURLCommand = function (params) {
        presenter.setVideoURL(params[0], params[1]);
    };

    presenter._setVideoURL = function (url, index) {
        var key;
        var videoFile;
        var originalFile;
        var mapper = {
            "oggFormat": "Ogg video",
            "mp4Format": "MP4 video",
            "webMFormat": "WebM video",
            "poster": "Poster",
            "subtitles": "Subtitles",
            "id": "ID",
            "altText": "AlternativeText",
            "loop": "Loop video"
        };

        if (index &gt;= presenter.configuration.files.length) {
            return false;
        }

        videoFile = presenter.configuration.files[index];
        originalFile = presenter.configuration.originalFiles[index];

        for (key in mapper) {
            if (mapper.hasOwnProperty(key)) {
                if (url.hasOwnProperty(key) &amp;&amp; (url[key] == null || url[key] == "")) {
                    videoFile[mapper[key]]  = originalFile[mapper[key]];
                } else {
                    videoFile[mapper[key]] = url[key] || videoFile[mapper[key]];
                }
            }
        }

        if (presenter.addedVideoURLS[index] === undefined) {
            presenter.addedVideoURLS[index] = {
                url: url,
                index: index
            };
        } else {
            for (key in mapper) {
                if (url.hasOwnProperty(key)) {
                    presenter.addedVideoURLS[index].url[key] = url[key];
                }
            }
        }

        return true;
    };

    /*
        Set video url and jump to this video.
        index: video index counted from 0
        url: object {
            "oggFormat": "Ogg video",
            "mp4Format": "MP4 video",
            "webMFormat": "WebM video",
            "poster": "Poster",
            "subtitles": "Subtitles",
            "id": "ID",
            "altText": "AlternativeText",
            "loop": "Loop video"
        }
    */
    presenter.setVideoURL = function (url, index) {
        index = (index || 1) - 1;

        if (presenter._setVideoURL(url, index)) {
            presenter.jumpTo(index + 1);
        }
    };

    presenter.setVisibility = function (isVisible) {
        presenter.$view.css("visibility", isVisible ? "visible" : "hidden");

        var $captions = presenter.$view.find('.captions');
        if (!isVisible) {
            $captions.each(function hideVisibility() {
                $(this).css('visibility', 'hidden');
            });
        } else {
            $captions.each(function showVisibility() {
                if ($(this).attr('visibility') === 'visible') {
                    $(this).css('visibility', 'visible');
                }
            });
        }
    };

    presenter.seek = deferredSyncQueue.decorate(function (seconds) {
        presenter.videoObject.currentTime = seconds;
        if (seconds &gt; presenter.videoObject.duration) {
            presenter.posterPlayButton.removeClass('video-poster-pause');
        }
    });

    presenter.seekFromPercent = function (percent) {
        presenter.seek(presenter.videoObject.duration * (percent / 100));
    };

    presenter.seekCommand = function (params) {
        presenter.seek(params[0]);
    };

    presenter.show = function () {
        if (presenter.isCurrentlyVisible) return;
        if (presenter.VIDEO_STATE.PLAYING == presenter.videoState) {
            presenter.videoObject.play();
        }
        presenter.isCurrentlyVisible = true;
        presenter.setVisibility(true);
    };

    presenter.hide = function () {
        if (!presenter.isCurrentlyVisible) return;

        if (presenter.VIDEO_STATE.PLAYING == presenter.videoState) {
            presenter.videoObject.pause();
            presenter.videoState = presenter.VIDEO_STATE.PLAYING;
            presenter.isHideExecuted = true;
        }
        presenter.isCurrentlyVisible = false;
        presenter.setVisibility(false);
    };

    presenter.jumpTo = function (movieNumber) {
        var newMovie = parseInt(movieNumber, 10) - 1;
        if (0 &lt;= newMovie &amp;&amp; newMovie &lt; presenter.configuration.files.length) {
            presenter.currentMovie = newMovie;
            presenter.reload();
        }
    };

    presenter.jumpToCommand = function (params) {
        presenter.jumpTo(params[0]);
    };

    presenter.jumpToID = function (id) {
        for (var i = 0; i &lt; presenter.configuration.files.length; i++) {
            if (id === presenter.configuration.files[i].ID) {
                presenter.jumpTo(i + 1);  // Video numbers are counted from 1 to n
                break;
            }
        }
    };

    presenter.jumpToIDCommand = function (params) {
        presenter.jumpToID(params[0]);
    };

    presenter.onStalledEventHandler = function () {
        var video = this;

        if (video.readyState &gt;= 2) {
            presenter.isVideoLoaded = true;
            presenter.callTasksFromDeferredQueue();
        }
    };

    presenter.callTasksFromDeferredQueue = function () {
        deferredSyncQueue.resolve();
    };

    presenter.removeWaterMark = function () {
        presenter.$view.find('.poster-wrapper').hide();
    };

    presenter.showWaterMark = function () {
        presenter.$view.find(".poster-wrapper").show();
    };

    presenter.loadVideoAtPlayOnMobiles = function () {
        if (MobileUtils.isSafariMobile(navigator.userAgent)) {
            if (!presenter.isVideoLoaded) {
                presenter.videoObject.load();
                presenter.metadadaLoaded = false;
            }
        }
        if (!presenter.isVideoLoaded) {
            presenter.videoObject.load();
            presenter.metadadaLoaded = false;
        }
    };

    presenter.addClassToView = function (className) {
        presenter.$view.addClass(className);
    };

    presenter.removeClassFromView = function (className) {
        presenter.$view.removeClass(className);
    };

    presenter.play = deferredSyncQueue.decorate(function () {
        presenter.removeWaterMark();
        presenter.hidePlayButton();
        presenter.loadVideoAtPlayOnMobiles();
        presenter.videoObject = presenter.videoContainer.find('video')[0];
        if (presenter.videoObject.paused) {
            presenter.videoObject.play();
            presenter.addClassToView('playing');
        }
        presenter.usedStop = false;
        presenter.playTriggered = true;
    });

    presenter.stop = deferredSyncQueue.decorate(function () {
            presenter.showPlayButton();
            presenter.seek(0);
            presenter.prevTime = -0.001;
            presenter.videoObject.pause();
            presenter.usedStop = true;
            if(presenter.descriptions.length &gt; 0){
                setAudioDescriptionDisabled();
            }
            presenter.removeClassFromView('playing');
            presenter.posterPlayButton.removeClass('video-poster-pause');
    });

    presenter.pause = deferredSyncQueue.decorate(function () {
        if (!presenter.videoObject.paused) {
            presenter.posterPlayButton.addClass('video-poster-pause');
            presenter.showPlayButton();
            presenter.videoObject.pause();
            presenter.removeClassFromView('playing');
        }
        presenter.usedStop = false;
    });

    presenter.previous = function () {
        if (presenter.currentMovie &gt; 0) {
            presenter.currentMovie--;
            presenter.reload();
        }
    };

    presenter.next = function () {
        if (presenter.currentMovie &lt; presenter.configuration.files.length - 1) {
            presenter.currentMovie++;
            presenter.reload();
        }
    };

    presenter.setVolume = function (percent) {
        presenter.videoObject.volume = percent / 100;
    };

    presenter.reset = function () {
        presenter.configuration.isVisibleByDefault ? presenter.show() : presenter.hide();
        presenter.videoState = presenter.VIDEO_STATE.STOPPED;
        presenter.videoObject.currentTime = 0;
        presenter.currentMovie = 0;
        if (presenter.metadadaLoaded) {
            presenter.videoObject.pause();
        }
        presenter.configuration.files = JSON.parse(JSON.stringify(presenter.configuration.originalFiles));
        presenter.reload();

        if (presenter.configuration.shouldHideSubtitles) {
            presenter.hideSubtitles();
        } else {
            presenter.showSubtitles();
        }
        presenter.addedVideoURLS = {};
    };

    presenter.setPlaybackRate = function (playbackRate) {
        presenter.videoObject.playbackRate = parseFloat(playbackRate);
    }

    presenter.updateVideoSpeedController = function() {
        let $playebackRateSelect = presenter.$view.find('.video-playback-rate select');
        if ($playebackRateSelect.length == 0) return;
        $playebackRateSelect.val(presenter.videoObject.playbackRate);
    }

    presenter.getVideo = function () {
        return presenter.videoContainer.find('video:first');
    };

    function generateTransformDict(scaleX, scaleY) {
        var scale = "scale(" + scaleX + "," + scaleY + ")";
        return {
            'transform': scale,
            '-ms-transform': scale,
            '-webkit-transform': scale,
            '-o-transform': scale,
            '-moz-transform': scale,
            "-webkit-transform-origin": "top left",
            "-ms-transform-origin": "top left",
            "transform-origin": "top left"
        }
    }

    function requestFullscreen($element) {
        var DomElement = $element.get(0);

        var requestMethod = DomElement.requestFullscreen || DomElement.mozRequestFullScreen ||
            DomElement.msRequestFullscreen || DomElement.webkitRequestFullScreen ||
            DomElement.webkitEnterFullscreen || null;
        if (requestMethod) {
            requestMethod.call(DomElement);
        }
        return requestMethod;
    }

    function exitFullscreen() {
        var exitMethod = document.exitFullscreen || document.mozCancelFullScreen ||
            document.msExitFullscreen || document.webkitExitFullscreen || null;

        if (exitMethod) {
            exitMethod.call(document);
        }
    }

    function isVideoInFullscreen() {
        if (document.fullscreenElement
            || document.mozFullScreenElement
            || document.webkitFullscreenElement
            || document.msFullscreenElement
            || document.webkitCurrentFullScreenElement
            || document.fullscreen
            || document.webkitIsFullScreen
            || document.mozFullScreen) {
            return true;
        }

        return false;
    }

    presenter.addTabindex = function (isTabindexEnabled) {
        var value = isTabindexEnabled ? "0" : "-1";
        presenter.videoContainer.attr("tabindex", value);
    };

    presenter.getTextToSpeechOrNull = function (playerController) {
        if (playerController) {
            return playerController.getModule('Text_To_Speech1');
        }

        return null;
    };

    function speak (data) {
        var tts = presenter.getTextToSpeechOrNull(presenter.playerController);
        if (tts &amp;&amp; presenter.playerController.isWCAGOn()) {
            tts.speak(data);
        }
    }

    function speakWithCallback (data, callbackFunction) {
        var tts = presenter.getTextToSpeechOrNull(presenter.playerController);
        if (tts) {
            tts.speakWithCallback(data, callbackFunction);
        }
    }

    presenter.isWCAGOn = function(isWCAGOn) {
        //This method has been added to enable the addon's detection by the autofill option of TTS
    };

    return presenter;
}

Addonvideo_create.__supported_player_options__ = {
    interfaceVersion: 2
};
</presenter></addon>