<?xml version="1.0" encoding="UTF-8" standalone="no"?><addon xmlns="http://icplayer.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" id="Audio" xsi:schemaLocation="http://icplayer.com ../../tools/addon.xsd">
	<model>
		<property name="mp3" nameLabel="Audio_property_mp3" type="audio"/>
		<property name="ogg" nameLabel="Audio_property_ogg" type="audio"/>
		<property name="defaultControls" nameLabel="Audio_property_default_controls" type="boolean"/>
        <property name="useBrowserControls" nameLabel="Audio_property_use_browser_controls" type="boolean"/>
		<property name="displayTime" nameLabel="Audio_property_display_time" type="boolean"/>
		<property name="enableLoop" nameLabel="Audio_property_enable_loop" type="boolean"/>
		<property isLocalized="true" name="Narration" nameLabel="Audio_property_narration" type="narration"/>
		<property name="onEnd" nameLabel="Audio_property_on_end" type="event"/>
		<property name="forceLoadAudio" nameLabel="Audio_property_force_load_audio" type="boolean"/>
		<property name="enablePlaybackSpeedControls" nameLabel="Audio_property_enable_playback_speed_controls" type="boolean"/>
	</model>
<css>.wrapper-addon-audio audio {
    bottom: 0;
    position: absolute;
}

.wrapper-addon-audio {
    height: 40px;
    position: relative;
}

.audioplayer {
    background-color: #EBEFF0;
    border-radius: 20px;
    width: 340px;
    height: 40px;
    display: table;
    padding-right: 2px;
    position: relative;
}

.audioplayer.playback-speed-control-enabled {
    width: 390px;
}

.play-pause-btn {
    width: 38px;
    height: 40px;
    float:left;
    border: solid 1px #444;
    border-radius: 30px;
    background-position: center center;
    background-repeat: no-repeat;
    cursor: pointer;
}

.audio-play-btn {
    background-image: url('resources/audio-play-btn.png');
}

.audio-pause-btn {
    background-image: url('resources/audio-pause-btn.png');
}

.audio-stop-btn {
    width: 40px;
    height: 40px;
    background: url('resources/audio-stop-btn.png') no-repeat center center;
    float:left;
    cursor: pointer;
}

.audio-volume-btn {
    width: 40px;
    height: 40px;
    background: url('resources/audio-volume-btn.png') no-repeat center center;
    float:left;
    cursor: pointer;
}

.audio-volume0 {
    background-image: url('resources/volume-0.png');
}

.audio-volume1 {
    background-image: url('resources/volume-1.png');
}

.audio-volume2 {
    background-image: url('resources/volume-2.png');
}

.audio-volume3 {
    background-image: url('resources/volume-3.png');
}

.player-time {
    vertical-align: middle;
    display: table-cell;
    height: 40px;
    line-height: 16px;
    padding-right: 10px;
    font-size: 14px;
    font-family: Arial, "Helvetica Neue", Helvetica, sans-serif;
    color: grey;
}

.audio-playback-rate {
    vertical-align: middle;
    display: table-cell;
    height: 40px;
    width: 60px;
    line-height: 16px;
    padding-right: 10px;
    background-color:inherit;
}

.audio-playback-rate select {
    width: 100%;
    font-size: 14px;
    font-family: Arial, "Helvetica Neue", Helvetica, sans-serif;
    color: grey;
    border: 0px;
    background-color:inherit;
}

.audio-progress-bar {
    -ms-touch-action: none;
    touch-action: none;
    width: 100px;
    float:left;
    padding-left: 0px;
    margin-top: 15px;
    margin-left: 5px;
    height: 10px;
    background-color: #FFF;
    border-radius: 5px;
    position: relative;
}

.audio-slider-btn {
    width: 20px;
    height: 20px;
    background: url('resources/audio-slider-btn.png') no-repeat;
    float:left;
    margin-left: -10px;
    margin-top: -5px;
    position: absolute;
    cursor: pointer;
}

.audio-bar {
    background-color: #BDC2C7;
    float: left;
    height: 10px;
    margin-left: 0px;
    border-bottom-left-radius: 5px;
    border-top-left-radius: 5px;
}

.audio-volume-layer {
    width: 100px;
    height: 40px;
    position: absolute;
    right: 15px;
    cursor: pointer;
}

.volume-control-background {
    position: absolute;
    width: 100%;
    height: 50%;
    border-bottom: solid 1px #808080;
}

.audio-volume-control {
    position: absolute;
    width: 20px;
    height: 40px;
    background: url('resources/audio-slider-btn.png') no-repeat center;
    margin-left: -10px;
}
</css><view>&lt;div class="wrapper-addon-audio"&gt;

&lt;/div&gt;
</view><preview>&lt;div class="wrapper-addon-audio"&gt;

&lt;/div&gt;
</preview><presenter>function AddonAudio_create(){
    var presenter = function(){};
    var AUDIO_FILES_MISSING = "This addon needs at least 1 audio file.";
    var mp3File;
    var oggFile;
    var eventBus;
    var currentTimeAlreadySent;
    var deferredSyncQueue = window.DecoratorUtils.DeferredSyncQueue(deferredQueueDecoratorChecker);
    var audioIsLoaded = false;
    var fetchedAudioData;
    var isReadyToReplay = true;
    var wasPlayed = false;

    presenter.playbackRate = 1.0;
    var playbackRateList = [0.25, 0.5, 0.75, 1.0, 1.25, 1.5, 1.75, 2.0];

    function deferredQueueDecoratorChecker() {
        if (!presenter.configuration.forceLoadAudio) {
            return true;
        }

        return audioIsLoaded;
    }

    presenter.audio = {
        readyState : 0
    };
    presenter.playerController = null;
    presenter.addonID = null;
    presenter.type = 'audio';

    presenter.mouseData = {};

    presenter.onEventReceived = function AddonAudio_onEventReceived (eventName, eventData) {
        if (eventData.value == 'dropdownClicked' &amp;&amp; !presenter.audio.playing &amp;&amp; !isTemporarilyPaused()) {
            presenter.audio.load();
        }
    };

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

    presenter.setPlayerController = function AddonAudio_setPlayerController (controller) {
        presenter.playerController = controller;
    };

    presenter.setEventBus = function AddonAudio_setEventBus (eventBusWrapper) {
        eventBus = eventBusWrapper;
    };

    presenter.upgradeModel = function AddonAudio_upgradeModel (model) {
        var upgradedModel = presenter.upgradeEnableLoop(model);
        upgradedModel = presenter.upgradeForceLoadAudio(upgradedModel);
        upgradedModel = presenter.upgradeEnablePlaybackSpeedControls(upgradedModel);
        return upgradedModel;
    };

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

        if (!upgradedModel["forceLoadAudio"]) {
            upgradedModel["forceLoadAudio"] = "False";
        }

        return upgradedModel;
    };

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

        if (!upgradedModel["enableLoop"]) {
            upgradedModel["enableLoop"] = "";
        }

        return upgradedModel;
    };

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

        if (!upgradedModel["enablePlaybackSpeedControls"]) {
            upgradedModel["enablePlaybackSpeedControls"] = "False";
        }

        return upgradedModel;
    };

    presenter.createTimeUpdateEventData = function AddonAudio_createTimeUpdateEventData (data) {
        return {
            source : presenter.addonID,
            item : '',
            value : '' + data.currentTime,
            score : ''
        };
    };

    presenter.createOnEndEventData = function AddonAudio_createOnEndEventData () {
        return {
            source : presenter.addonID,
            item : 'end',
            value : '',
            score : ''
        };
    };

    presenter.getAudioCurrentTime = function AddonAudio_getAudioCurrentTime () {
        return this.audio.currentTime;
    };

    function addonAudio_formatTime (seconds) {
        return StringUtils.timeFormat(seconds);
    }

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

    presenter.AddonAudio_onLoadedMetadataCallback = function () {
        var duration = parseInt(presenter.audio.duration, 10);
        duration = isNaN(duration) ? 0 : duration;
        if (presenter.configuration.displayTime) {
            AddonAudio_displayTimer(0, duration);
        }
        if (presenter.configuration.isHtmlPlayer){
            presenter.$playerTime.html('0:00 / ' + addonAudio_formatTime(duration))
        }


        deferredSyncQueue.resolve();
    };

    presenter.sendEventAndSetCurrentTimeAlreadySent = function AddonAudio_sendEventAndSetCurrentTimeAlreadySent (eventData, currentTime) {
        eventBus.sendEvent('ValueChanged', eventData);
        currentTimeAlreadySent = currentTime;
    };

    presenter.sendOnEndEvent = function AddonAudio_sendOnEndEvent () {
        var eventData = presenter.createOnEndEventData();
        eventBus.sendEvent('ValueChanged', eventData);
    };

    presenter.onTimeUpdateSendEventCallback = function AddonAudio_onTimeUpdateSendEventCallback () {
        var currentTime = addonAudio_formatTime(presenter.getAudioCurrentTime());
        if (currentTime !== currentTimeAlreadySent) { // to prevent duplicated value
            var eventData = presenter.createTimeUpdateEventData({'currentTime' : currentTime});
            presenter.sendEventAndSetCurrentTimeAlreadySent(eventData, currentTime);
        }
    };

    function AddonAudio_onTimeUpdateCallback() {
        var bar_width, duration = presenter.audio.duration;
        duration = isNaN(duration) ? 0 : duration;
        var currentTime = presenter.audio.currentTime;
        if (presenter.configuration.displayTime) {
            AddonAudio_displayTimer(currentTime, duration);
        }
        if (presenter.configuration.isHtmlPlayer){
            presenter.$playerTime.html(addonAudio_formatTime(currentTime) + ' / ' + addonAudio_formatTime(duration));
            bar_width = presenter.$progressWrapper.width() * currentTime / duration;
            presenter.$progressBar.width(Math.round(bar_width));
            presenter.$progressSlider.css('left', Math.round(bar_width));
        }
    }

    function AddonAudio_change_volume_class(volume_class) {
        if (presenter.$volumeBtn.hasClass(volume_class)) {
            return;
        }
        for (var i=0; i&lt;=3; i++) {
            if (presenter.$volumeBtn.hasClass('audio-volume'+i)) {
                presenter.$volumeBtn.removeClass('audio-volume'+i);
            }
        }
        presenter.$volumeBtn.addClass(volume_class);
    }

    function AddonAudio_onVolumeChanged() {
        if (presenter.configuration.isHtmlPlayer){
            var volume = presenter.audio.volume,
                volume_class = '';
            presenter.$volumeControl.css('left', volume * presenter.$volumeLayer.width());
            if (volume &lt; 0.1) {
                volume_class = 'audio-volume0';
                if (volume &gt; 0) {
                    presenter.audio.volume = 0;
                }
            } else if (volume &lt; 0.4) {
                volume_class = 'audio-volume1';
            } else if (volume &lt; 0.7) {
                volume_class = 'audio-volume2';
            } else {
                volume_class = 'audio-volume3';
            }
            AddonAudio_change_volume_class(volume_class);
        }
    }

    function AddonAudio_playPauseCallback () {
        if (presenter.$playPauseBtn.hasClass('audio-pause-btn')) {
            presenter.pause();
        }
        else {
            presenter.play();
        }
    }

    function AddonAudio_displayTimer(current, duration) {
        presenter.$view.find('#currentTime').html(addonAudio_formatTime(current) + ' / ');
        presenter.$view.find('#durationTime').html(addonAudio_formatTime(duration));
    }

    function AddonAudio_progressMouseDownCallback(event) {
        if ($(event.target).hasClass('audio-slider-btn')) {
            presenter.mouseData.oldPosition = event.pageX;
            presenter.mouseData.isMouseDragged = true;
            presenter.mouseData.playedBeforeDragging = !presenter.audio.paused;
            if (!presenter.audio.paused) {
                presenter.pause();
            }
        }
    }

    function AddonAudio_progressMouseUpCallback() {
        if (presenter.mouseData.isMouseDragged) {
            var duration = presenter.audio.duration;
            duration = isNaN(duration) ? 0 : duration;
            presenter.audio.currentTime = duration * presenter.$progressBar.width() / presenter.$progressWrapper.width();
            presenter.mouseData.isMouseDragged = false;
            presenter.mouseData.oldPosition = 0;
            if (presenter.mouseData.playedBeforeDragging) {
                presenter.play();
            }
        }
    }

    function AddonAudio_progressMouseMoveCallback(event) {
        if (presenter.mouseData.isMouseDragged){
            var relativeDistance = event.pageX - presenter.mouseData.oldPosition,
                bar_width = 0,
                oldWidth = presenter.$progressBar.width();
            if (oldWidth + relativeDistance &lt; presenter.$progressWrapper.width()) {
                bar_width = oldWidth + relativeDistance;
            }
            else {
                bar_width = presenter.$progressWrapper.width();
            }
            presenter.$progressBar.width(bar_width);
            presenter.$progressSlider.css('left',Math.round(bar_width));
            presenter.mouseData.oldPosition = event.pageX;
        }
    }

    function addonAudio_isMoreThanOneFingerGesture(event) {
        var touchPoints = (typeof event.changedTouches != 'undefined') ? event.changedTouches : [event];
        if (event.hasOwnProperty('touches'))
            touchPoints = event.touches;
        return touchPoints.length&gt; 1;
    }

    function AddonAudio_progressTouchStartCallback(event) {
        var touch, touchPoints;
        if (addonAudio_isMoreThanOneFingerGesture(event)) return;

        touchPoints = (typeof event.changedTouches != 'undefined') ? event.changedTouches : [event];

        if (event.hasOwnProperty('touches'))
            touch = event.touches[0];
        else
            touch = touchPoints[0];
        AddonAudio_progressMouseDownCallback(touch);
    }


    function AddonAudio_progressTouchEndCallback() {
        AddonAudio_progressMouseUpCallback();
    }


    function AddonAudio_progressTouchMoveCallback(event) {
        if (addonAudio_isMoreThanOneFingerGesture(event)) return;

        var touch;
        var touchPoints = (typeof event.changedTouches != 'undefined') ? event.changedTouches : [event];

        if (event.hasOwnProperty('touches'))
            touch = event.touches[0];
        else
            touch = touchPoints[0];
        AddonAudio_progressMouseMoveCallback(touch);
    }

    function AddonAudio_attachProgressListeners() {
        presenter.isMobileDevice = MobileUtils.isMobileUserAgent(navigator.userAgent) || MobileUtils.isEventSupported('touchend');
        if (MobileUtils.isWindowsMobile(window.navigator)) {
            presenter.$progressWrapper[0].addEventListener("MSPointerDown", AddonAudio_progressTouchStartCallback, false);
            presenter.$progressWrapper[0].addEventListener("MSPointerUp", AddonAudio_progressTouchEndCallback, false);
            presenter.$progressWrapper[0].addEventListener("MSPointerMove", AddonAudio_progressTouchMoveCallback, false);
        }
        else if (presenter.isMobileDevice) {
            presenter.$progressWrapper[0].ontouchstart=AddonAudio_progressTouchStartCallback;
            presenter.$customPlayer[0].ontouchend=AddonAudio_progressTouchEndCallback;
            presenter.$progressWrapper[0].ontouchmove=AddonAudio_progressTouchMoveCallback;
        }
        else {
            presenter.$progressWrapper.on('mousedown', AddonAudio_progressMouseDownCallback);
            presenter.$progressWrapper.on('mouseup', AddonAudio_progressMouseUpCallback);
            presenter.$progressWrapper.on('mousemove', AddonAudio_progressMouseMoveCallback);
        }
    }

    function AddonAudio_toogleVolumeLayer(){
        AddonAudio_onVolumeChanged();
        presenter.$volumeLayer.toggle();
        presenter.$playerTime.toggle();
    }

    function AddonAudio_createHtmlPlayer() {
        presenter.$customPlayer = $('&lt;div&gt;').
            addClass('audioplayer');
        if (presenter.configuration.enablePlaybackSpeedControls) {
            presenter.$customPlayer.addClass('playback-speed-control-enabled');
        }

        presenter.$playPauseBtn = $('&lt;div&gt;').
            addClass('play-pause-btn').
            addClass('audio-play-btn').
            on('click', AddonAudio_playPauseCallback);

        presenter.$customPlayer.append(presenter.$playPauseBtn);

        presenter.$stopBtn = $('&lt;div&gt;').
            addClass('audio-stop-btn').
            on('click', presenter.stop);

        presenter.$customPlayer.append(presenter.$stopBtn);

        presenter.$progressWrapper = $('&lt;div&gt;').
            addClass('audio-progress-bar');

        presenter.$progressBar = $('&lt;div&gt;').
            addClass('audio-bar');

        presenter.$progressSlider = $('&lt;div&gt;').
            addClass('audio-slider-btn');

        AddonAudio_attachProgressListeners();

        presenter.$progressWrapper.
            append(presenter.$progressBar).
            append(presenter.$progressSlider);

        presenter.$customPlayer.append(presenter.$progressWrapper);

        if (!MobileUtils.isSafariMobile(navigator.userAgent)) {
            presenter.$volumeBtn = $('&lt;div&gt;').
                addClass('audio-volume-btn').
                on('click', AddonAudio_toogleVolumeLayer);

            presenter.$customPlayer.append(presenter.$volumeBtn);

            presenter.$volumeControlBackground = $('&lt;div&gt;').addClass('volume-control-background');
            presenter.$volumeControl = $('&lt;div&gt;').addClass('audio-volume-control');

            presenter.$volumeLayer = $('&lt;div&gt;').
                addClass('audio-volume-layer').
                append(presenter.$volumeControlBackground).
                append(presenter.$volumeControl).
                on('click', function (e) {
                presenter.audio.volume = e.offsetX / $(this).width();
            });

            presenter.$volumeLayer.hide();

            presenter.$customPlayer.append(presenter.$volumeLayer);
        }

        presenter.$playbackRateControls = null;
        if (presenter.configuration.enablePlaybackSpeedControls) {
            presenter.$playbackRateControls = $('&lt;div&gt;');
            presenter.$playbackRateControls.addClass('audio-playback-rate');
            presenter.$playbackRateControls.append(createPlaybackRateSelectElement());
            displayPlaybackRate();
            presenter.$customPlayer.append(presenter.$playbackRateControls);
        }

        presenter.$playerTime = $('&lt;div&gt;').
            addClass('player-time').
            text('00:00 / --:--');
        presenter.$customPlayer.append(presenter.$playerTime);

        presenter.$customPlayer.on('click mousedown mouseup', function(event){
            event.stopPropagation();
        });
        presenter.$audioWrapper.append(presenter.$customPlayer);
    }

    function createPlaybackRateSelectElement () {
        var $select = $('&lt;select&gt;');
        for (var i = 0; i &lt; playbackRateList.length; i++) {
            var $option = $('&lt;option&gt;');
            $option.text(playbackRateList[i]);
            $option.attr('value', playbackRateList[i]);
            if (playbackRateList[i] == 1) {
                $option.attr('selected', 'selected');
            }
            $select.append($option);
        }

        $select.on('change', function(){
            presenter.setPlaybackRate($select.val());
        })

        return $select;

    }

    function displayPlaybackRate () {
        if (presenter.$playbackRateControls != null) {
            var $select = presenter.$playbackRateControls.find('select');
            if ($select.val() == presenter.playbackRate) {
                if (playbackRateList.indexOf(presenter.playbackRate) != -1) {
                    $select.find('.custom-option').remove();
                }
                return;
            }
            $select.find('.custom-option').remove();
            if (playbackRateList.indexOf(presenter.playbackRate) != -1) {
                $select.val(presenter.playbackRate);
            } else {
                var $customOption = $('&lt;option&gt;');
                $customOption.text(presenter.playbackRate);
                $customOption.attr('value', presenter.playbackRate);
                $customOption.addClass('custom-option');
                $select.append($customOption);
                $select.val(presenter.playbackRate);

            }
        }
    }

    function AddonAudio_createView(view, model, isPreview){
        presenter.$audioWrapper = presenter.$view.find(".wrapper-addon-audio");

        mp3File = model.mp3;
        oggFile = model.ogg;

        if (!oggFile &amp;&amp; !mp3File) {
            $(view).html(AUDIO_FILES_MISSING);
        }

        var audio = document.createElement("audio");

        if (presenter.configuration.defaultControls){
            $(audio).attr("preload", "auto");
            if (presenter.configuration.isHtmlPlayer){
                AddonAudio_createHtmlPlayer();
            }
            else {
                $(audio).attr("controls", "controls");
            }

        }

        var currentTime = document.createElement("span");
        var durationTime = document.createElement("span");
        $(currentTime).attr("id", "currentTime");
        $(durationTime).attr("id", "durationTime");

        presenter.$audioWrapper.append(audio);
        if (presenter.configuration.displayTime) {
            presenter.$audioWrapper.append(currentTime).append(durationTime);
        }
        if (!isPreview) {
            audio.addEventListener('timeupdate', presenter.onTimeUpdateSendEventCallback, false);
        }

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

        presenter.audio = audio;
    }

    presenter.sendOnPLayingEvent = function () {
        const eventData = {
            'source': presenter.configuration.addonID,
            'item': '',
            'value': 'playing',
            'score': ''
        };

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

    presenter.sendOnPauseEvent = function () {
        const eventData = {
            'source': presenter.configuration.addonID,
            'item': '',
            'value': 'pause',
            'score': ''
        };

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

    presenter.sendNotStartedEvent = function () {
        const eventData = {
            'source': presenter.configuration.addonID,
            'item': '',
            'value': 'not-started',
        };

        eventBus.sendEvent('PreDestroyed', eventData);
    };

    presenter.sendReplayedInLoopEvent = function () {
        const eventData = {
            'source': presenter.configuration.addonID,
            'item': '',
            'value': 'replayed-in-loop',
            'score': ''
        };

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

    function AddonAudio_onAudioPlaying () {
        presenter._setPlayed(true);
        presenter.sendOnPLayingEvent();
    }

    function AddonAudio_onAudioPause () {
        presenter.sendOnPauseEvent();
    }

    function AddonAudio_attachEventListeners(audio) {
        audio.addEventListener('loadeddata', presenter.AddonAudio_onLoadedMetadataCallback, false);
        audio.addEventListener('timeupdate', AddonAudio_onTimeUpdateCallback, false);
        audio.addEventListener('volumechange', AddonAudio_onVolumeChanged, false);
        audio.addEventListener('ended', AddonAudio_onAudioEnded , false);
        audio.addEventListener('click', AddonAudio_onAudioClick, false);
        audio.addEventListener('playing', AddonAudio_onAudioPlaying, false);
        audio.addEventListener('pause', AddonAudio_onAudioPause, false);

        if (isMobileIOS()) {
            AddonAudio_attachEventListenersForIOS(audio);
        }
    }

    function AddonAudio_attachEventListenersForIOS(audio) {
        if (presenter.configuration.forceLoadAudio) {
            audio.addEventListener('progress', presenter.AddonAudio_onProgressedCallback, { once: true });
        }
    }

    function AddonAudio_removeEventListeners(audio) {
        presenter.audio.removeEventListener('loadeddata', presenter.AddonAudio_onLoadedMetadataCallback, false);
        presenter.audio.removeEventListener('timeupdate', presenter.onTimeUpdateSendEventCallback, false);
        presenter.audio.removeEventListener('timeupdate', AddonAudio_onTimeUpdateCallback, false);
        presenter.audio.removeEventListener('volumechange', AddonAudio_onVolumeChanged, false);
        presenter.audio.removeEventListener('ended', AddonAudio_onAudioEnded , false);
        presenter.audio.removeEventListener('click', AddonAudio_onAudioClick, false);
        presenter.audio.removeEventListener('playing', AddonAudio_onAudioPlaying, false);
        presenter.audio.removeEventListener('pause', AddonAudio_onAudioPause, false);

        if (isMobileIOS()) {
            AddonAudio_removeEventListenersForIOS(audio);
        }
    }

    function AddonAudio_removeEventListenersForIOS(audio) {
        if (presenter.configuration.forceLoadAudio) {
            audio.removeEventListener('progress', presenter.AddonAudio_onProgressedCallback, { once: true });
        }
    }

    function isMobileIOS() {
        return window.MobileUtils.isSafariMobile(navigator.userAgent);
    }

    function AddonAudio_onAudioEnded () {
        if (presenter.configuration.enableLoop) {
            presenter.sendReplayedInLoopEvent();
            this.currentTime = 0;
            this.play();
        } else {
            presenter.executeOnEndEvent();
            presenter.sendOnEndEvent();
            presenter.stop();
        }
    }

    function AddonAudio_onAudioClick (e) {
        e.stopPropagation();
    }

    presenter.fetchAudioFromServer = function (src) {
        var req = new XMLHttpRequest();
        req.open('GET', src, true);
        req.responseType = 'blob';
        req.addEventListener("load", presenter.loadAudioDataFromRequest);

        req.send();
    };

    presenter.loadAudioDataFromRequest = function (event) {
        if (event.currentTarget.status == 200 || (isMobileIOS() &amp;&amp; event.currentTarget.status == 0)) {
            fetchedAudioData = event.currentTarget.response;
            presenter.audio.src = URL.createObjectURL(fetchedAudioData);
            audioIsLoaded = true;
        }
    };

    function AddonAudio_loadFiles(){
        var canPlayMp3 = false;
        var canPlayOgg = false;
        var audio = presenter.audio;

        AddonAudio_attachEventListeners(audio);

        if(audio.canPlayType) {
            canPlayMp3 = audio.canPlayType &amp;&amp; "" != audio.canPlayType('audio/mpeg');
            canPlayOgg = audio.canPlayType &amp;&amp; "" != audio.canPlayType('audio/ogg; codecs="vorbis"');
            var audioSrc = "";

            if(canPlayMp3){
                audioSrc = mp3File;
            } else if (canPlayOgg) {
                audioSrc = oggFile;
            }

            if (presenter.configuration.forceLoadAudio) {
                presenter.fetchAudioFromServer(audioSrc);
            } else {
                $(audio).attr("src", audioSrc);
            }

        } else {
            $(audio).append("Your browser doesn't support audio.");
        }

        $(audio).load();
    }

    presenter.run = function AddonAudio_run (view, model){
        presenter.initialize(view, model, false);
        presenter.addonID = model.ID;
        eventBus.addEventListener('ValueChanged', this);
    };

    presenter.createPreview = function AddonAudio_createPreview (view, model){
        presenter.initialize(view, model, true);
    };

    presenter.initialize = function AddonAudio_initialize (view, model, isPreview) {
        var upgradedModel = presenter.upgradeModel(model);

        presenter.view = view;
        presenter.$view = $(view);
        presenter.configuration = presenter.validateModel(upgradedModel);

        AddonAudio_createView(view, upgradedModel, isPreview);
        
        if (!isPreview) {
        	AddonAudio_loadFiles();
            presenter.$view.bind('click', function (event) {
                event.stopPropagation();
            });
        }
    };

    presenter.onDestroy = function () {
        presenter.audio.pause();

        presenter.playerController = null;

        AddonAudio_removeEventListeners(presenter.audio);
        if (presenter.configuration.forceLoadAudio &amp;&amp; audioIsLoaded) {
            URL.revokeObjectURL(presenter.audio.src);
        }
        presenter.audio.setAttribute('src', '');
        presenter.audio.load();
        presenter.audio = null;

        if (presenter.$playPauseBtn) {
            presenter.$playPauseBtn.off();
            presenter.$playPauseBtn = null;
        }
        if (presenter.$stopBtn) {
            presenter.$stopBtn.off();
            presenter.$stopBtn = null;
        }
        if (presenter.$progressWrapper) {
            presenter.$progressWrapper[0].removeEventListener("MSPointerDown", AddonAudio_progressTouchStartCallback, false);
            presenter.$progressWrapper[0].removeEventListener("MSPointerUp", AddonAudio_progressTouchEndCallback, false);
            presenter.$progressWrapper[0].removeEventListener("MSPointerMove", AddonAudio_progressTouchMoveCallback, false);
            presenter.$progressWrapper[0].ontouchstart=null;
            presenter.$progressWrapper[0].ontouchmove=null;
            presenter.$progressWrapper.off();
            presenter.$progressWrapper = null;
        }
        if (presenter.$customPlayer) {
            presenter.$customPlayer.off();
            presenter.$customPlayer[0].ontouchend=null;
            presenter.$customPlayer = null;
        }
        if (presenter.$volumeBtn) {
            presenter.$volumeBtn.off();
            presenter.$volumeBtn = null;
        }
        if (presenter.$volumeLayer) {
            presenter.$volumeLayer.off();
            presenter.$volumeLayer = null;
        }

        presenter.$volumeControlBackground = null;
        presenter.$volumeControl = null;
        presenter.$audioWrapper = null;
        presenter.$playerTime = null;
        presenter.$progressBar = null;
        presenter.$progressSlider = null;

        presenter.mouseData = null;
        presenter.configuration = null;
        presenter.$view.unbind();
        presenter.$view = null;
        presenter.view = null;

        deferredSyncQueue = null;
    };

    presenter.validateModel = function AddonAudio_validateModel (model) {
        var isVisible = ModelValidationUtils.validateBoolean(model["Is Visible"]),
            defaultControls = ModelValidationUtils.validateBoolean(model.defaultControls),
            useBrowserControls = ModelValidationUtils.validateBoolean(model.useBrowserControls);

        return {
            isVisible: isVisible,
            isVisibleByDefault: isVisible,
            onEndEventCode: model.onEnd,
            enableLoop: ModelValidationUtils.validateBoolean(model.enableLoop),
            displayTime: ModelValidationUtils.validateBoolean(model.displayTime),
            defaultControls: defaultControls,
            useBrowserControls: useBrowserControls,
            isHtmlPlayer: defaultControls &amp;&amp; !useBrowserControls,
            addonID: model.ID,
            forceLoadAudio: ModelValidationUtils.validateBoolean(model.forceLoadAudio),
            narration: model.Narration,
            enablePlaybackSpeedControls: ModelValidationUtils.validateBoolean(model.enablePlaybackSpeedControls)
        };
    };

    presenter.executeCommand = function AddonAudio_executeCommand (name, params) {
        var commands = {
            'play': presenter.play,
            'stop': presenter.stop,
            'show': presenter.show,
            'hide': presenter.hide,
            'pause': presenter.pause,
            'getNarration': presenter.getNarration,
            'setPlaybackRate': presenter.setPlaybackRate
        };

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

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

    presenter.play = deferredSyncQueue.decorate(function() {
        if (!presenter.audio) return;
        if (presenter.audio.src &amp;&amp; presenter.audio.paused) {
            if (!isReadyToReplay) {
                prepareToReplay();
            }
            presenter.audio.play();
            if (presenter.configuration.isHtmlPlayer) {
                presenter.$playPauseBtn.
                    removeClass('audio-play-btn').
                    addClass('audio-pause-btn');
            }
        }
    });

    presenter.pause = deferredSyncQueue.decorate(function AddonAudio_pause () {
        if (!presenter.audio) return;
        if(presenter.audio.readyState &gt; 0) {
            if (!presenter.audio.paused) {
                presenter.audio.pause();
            }
            if (presenter.configuration.isHtmlPlayer) {
                presenter.$playPauseBtn.
                    removeClass('audio-pause-btn').
                    addClass('audio-play-btn');
            }
        }
    });

    function prepareToReplay() {
        if (isMobileIOS()) {
            prepareToReplayOnMobileIOS()
        }
        isReadyToReplay = true;
    }

    function prepareToReplayOnMobileIOS() {
        if (presenter.configuration.forceLoadAudio) {
            URL.revokeObjectURL(presenter.audio.src);
            presenter.audio.src = URL.createObjectURL(fetchedAudioData);
        }
    }

    presenter.stop = deferredSyncQueue.decorate(function AddonAudio_stop () {
        if (!presenter.audio) return;
        if(presenter.audio.readyState &gt; 0) {
            isReadyToReplay = false;
            presenter.pause();
            presenter.audio.currentTime = 0;
        }
    });

    function increasePlaybackRate() {
        const playbackIndex = playbackRateList.indexOf(presenter.playbackRate);
        const nextPlaybackIndex = Math.min(playbackIndex+1, playbackRateList.length-1);
        presenter.setPlaybackRate(("" + playbackRateList[nextPlaybackIndex]));
    }

    function decreasePlaybackRate() {
        const playbackIndex = playbackRateList.indexOf(presenter.playbackRate);
        const nextPlaybackIndex = Math.max(playbackIndex-1, 0);
        presenter.setPlaybackRate(("" + playbackRateList[nextPlaybackIndex]));
    }

    presenter.setPlaybackRate = function (value) {
        if (!presenter.audio) return;
        if (isNaN(value)) return;
        var parsedValue = parseFloat(value);
        presenter.playbackRate = parsedValue;
        presenter.audio.playbackRate = parsedValue;
        displayPlaybackRate();
    };

    presenter.show = function AddonAudio_show () {
        this.setVisibility(true);
        this.configuration.isVisible = true;
    };

    presenter.hideAddon = function AddonAudio_hideAddon () {
        this.setVisibility(false);
        this.configuration.isVisible = false;
    };

    presenter.hide = function AddonAudio_hide () {
        this.stop();
        this.hideAddon();
    };

    presenter.reset = function AddonAudio_reset () {
        if (!presenter.audio) return;

        presenter.stop();

        presenter.configuration.isVisible = presenter.configuration.isVisibleByDefault;
        if (presenter.configuration.isVisible) {
            presenter.show();
        } else {
            presenter.hideAddon();
        }
    };

    presenter.getState = function AddonAudio_getState () {
        return JSON.stringify({
            isVisible : presenter.configuration.isVisible,
            playbackRate: presenter.playbackRate,
            wasPlayed: wasPlayed
        });
    };

    presenter.setState = function addonAudio_setState (stateString) {
        if (ModelValidationUtils.isStringEmpty(stateString)) {
            return false;
        }

        /*
        * The If statement resolves an issue on Mobile Safari, where presenter.stop call from setState would be resolved
        * right after the first presenter.play call. The problem occurs because the deferred queue is resolved
        * on loadedmetadata event and Mobile Safari only downloads the audio/video src file on the first interaction
        * with the tag, rather than when the page itself is loaded.
        * */
        if (audioIsLoaded) {
            presenter.stop();
        };

        var parsedJson = JSON.parse(stateString);
        if (parsedJson.isVisible) {
            this.show();
        } else {
            this.hideAddon();
        }
        if (parsedJson['playbackRate'] != undefined) {
            presenter.setPlaybackRate(parsedJson['playbackRate']);
        }
        if (parsedJson.wasPlayed !== undefined) {
            wasPlayed = parsedJson.wasPlayed;
        }

        return false;
    };

    presenter.executeOnEndEvent = function AddonAudio_executeOnEndEvent () {
        if (presenter.configuration.onEndEventCode) {
            presenter.playerController.getCommands().executeEventCode(presenter.configuration.onEndEventCode);
        }
    };

    presenter.getNarration = function AddonAudio_getNarration() {
        return presenter.configuration.narration;
    };

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

    function forward() {
        presenter.audio.currentTime += 5;
    }

    function backward() {
        presenter.audio.currentTime -= 5;
    }

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

    function increaseVolume() {
         var volume = presenter.audio.volume;
        volume += 0.1;
        if (volume &gt; 1.0) volume = 1.0;
        presenter.audio.volume = volume;
    }

    function decreaseVolume() {
        var volume = presenter.audio.volume;
        volume -= 0.1;
        if (volume &lt; 0.0) volume = 0.0;
        presenter.audio.volume = volume;
    }

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

        if (keysDownCodes === undefined) {
            keysDownCodes = [];
        }
        const letterSKeyCode = 83;
        switch (keycode) {
            case window.KeyboardControllerKeys.TAB:
                if (isShift) {
                    backward();
                } else {
                    forward();
                }
                break;
            case window.KeyboardControllerKeys.ENTER:
                if (isShift) {
                    presenter.stop();
                }
                break;
            case window.KeyboardControllerKeys.SPACE:
                playPause();
                break;
            case window.KeyboardControllerKeys.ARROW_UP:
                if (keysDownCodes.includes(letterSKeyCode)) {
                    increasePlaybackRate();
                } else {
                    increaseVolume();
                }
                break;
            case window.KeyboardControllerKeys.ARROW_DOWN:
                if (keysDownCodes.includes(letterSKeyCode)) {
                    decreasePlaybackRate();
                } else {
                    decreaseVolume();
                }
                break;
            case window.KeyboardControllerKeys.ARROW_LEFT:
                backward();
                break;
            case window.KeyboardControllerKeys.ARROW_RIGHT:
                forward();
                break;
            case window.KeyboardControllerKeys.ESCAPE:
                presenter.stop();
                break;
        }
    };

    presenter._setPlayed = function (_wasPlayed) {
        wasPlayed = _wasPlayed;
    };

    presenter.preDestroy = function() {
        if (!wasPlayed) {
            presenter.sendNotStartedEvent();
        }
    };

    return presenter;
}

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