var H5P = H5P || {}; /** * H5P audio module * * @external {jQuery} $ H5P.jQuery */ H5P.Audio = (function ($) { /** * @param {Object} params Options for this library. * @param {Number} id Content identifier. * @param {Object} extras Extras. * @returns {undefined} */ function C(params, id, extras) { H5P.EventDispatcher.call(this); this.contentId = id; this.params = params; this.extras = extras; this.toggleButtonEnabled = true; // Retrieve previous state if (extras && extras.previousState !== undefined) { this.oldTime = extras.previousState.currentTime; } this.params = $.extend({}, { playerMode: 'minimalistic', fitToWrapper: false, controls: true, autoplay: false, audioNotSupported: "Your browser does not support this audio", playAudio: "Play audio", pauseAudio: "Pause audio" }, params); // Required if e.g. used in CoursePresentation as area to click on if (this.params.playerMode === 'transparent') { this.params.fitToWrapper = true; } this.on('resize', this.resize, this); } C.prototype = Object.create(H5P.EventDispatcher.prototype); C.prototype.constructor = C; /** * Adds a minimalistic audio player with only "play" and "pause" functionality. * * @param {jQuery} $container Container for the player. * @param {boolean} transparentMode true: the player is only visible when hovering over it; false: player's UI always visible */ C.prototype.addMinimalAudioPlayer = function ($container, transparentMode) { var INNER_CONTAINER = 'h5p-audio-inner'; var AUDIO_BUTTON = 'h5p-audio-minimal-button'; var PLAY_BUTTON = 'h5p-audio-minimal-play'; var PLAY_BUTTON_PAUSED = 'h5p-audio-minimal-play-paused'; var PAUSE_BUTTON = 'h5p-audio-minimal-pause'; var self = this; this.$container = $container; self.$inner = $('
', { 'class': INNER_CONTAINER + (transparentMode ? ' h5p-audio-transparent' : '') }).appendTo($container); var audioButton = $('', { 'class': AUDIO_BUTTON + " " + PLAY_BUTTON, 'aria-label': this.params.playAudio }).appendTo(self.$inner) .click( function () { if (!self.isEnabledToggleButton()) { return; } // Prevent ARIA from playing over audio on click this.setAttribute('aria-hidden', 'true'); if (self.audio.paused) { self.play(); } else { self.pause(); } }) .on('focusout', function () { // Restore ARIA, required when playing longer audio and tabbing out and back in this.setAttribute('aria-hidden', 'false'); }); // Fit to wrapper if (this.params.fitToWrapper) { audioButton.css({ 'width': '100%', 'height': '100%' }); } //Event listeners that change the look of the player depending on events. self.audio.addEventListener('ended', function () { audioButton .attr('aria-hidden', false) .attr('aria-label', self.params.playAudio) .removeClass(PAUSE_BUTTON) .removeClass(PLAY_BUTTON_PAUSED) .addClass(PLAY_BUTTON); }); self.audio.addEventListener('play', function () { audioButton .attr('aria-label', self.params.pauseAudio) .removeClass(PLAY_BUTTON) .removeClass(PLAY_BUTTON_PAUSED) .addClass(PAUSE_BUTTON); }); self.audio.addEventListener('pause', function () { audioButton .attr('aria-hidden', false) .attr('aria-label', self.params.playAudio) .removeClass(PAUSE_BUTTON) .addClass(PLAY_BUTTON_PAUSED); }); this.$audioButton = audioButton; // Scale icon to container self.resize(); }; /** * Resizes the audio player icon when the wrapper is resized. */ C.prototype.resize = function () { // Find the smallest value of height and width, and use it to choose the font size. if (this.params.fitToWrapper && this.$container && this.$container.width()) { var w = this.$container.width(); var h = this.$container.height(); if (w < h) { this.$audioButton.css({'font-size': w / 2 + 'px'}); } else { this.$audioButton.css({'font-size': h / 2 + 'px'}); } } }; return C; })(H5P.jQuery); /** * Wipe out the content of the wrapper and put our HTML in it. * * @param {jQuery} $wrapper Our poor container. */ H5P.Audio.prototype.attach = function ($wrapper) { const self = this; $wrapper.addClass('h5p-audio-wrapper'); // Check if browser supports audio. var audio = document.createElement('audio'); if (audio.canPlayType === undefined) { this.attachNotSupportedMessage($wrapper); return; } // Add supported source files. if (this.params.files !== undefined && this.params.files instanceof Object) { for (var i = 0; i < this.params.files.length; i++) { var file = this.params.files[i]; if (audio.canPlayType(file.mime)) { var source = document.createElement('source'); source.src = H5P.getPath(file.path, this.contentId); source.type = file.mime; audio.appendChild(source); } } } if (!audio.children.length) { this.attachNotSupportedMessage($wrapper); return; } if (this.endedCallback !== undefined) { audio.addEventListener('ended', this.endedCallback, false); } audio.className = 'h5p-audio'; audio.controls = this.params.controls === undefined ? true : this.params.controls; // Menu removed, because it's cut off if audio is used as H5P.Question intro const controlsList = 'nodownload noplaybackrate'; audio.setAttribute('controlsList', controlsList); audio.preload = 'auto'; audio.style.display = 'block'; if (this.params.fitToWrapper === undefined || this.params.fitToWrapper) { audio.style.width = '100%'; if (!this.isRoot()) { // Only set height if this isn't a root audio.style.height = '100%'; } } this.audio = audio; if (this.params.playerMode === 'minimalistic') { audio.controls = false; this.addMinimalAudioPlayer($wrapper, false); } else if (this.params.playerMode === 'transparent') { audio.controls = false; this.addMinimalAudioPlayer($wrapper, true); } else { $wrapper.html(audio); } if (audio.controls) { $wrapper.addClass('h5p-audio-controls'); } // Set time to saved time from previous run if (this.oldTime) { this.seekTo(this.oldTime); } // Avoid autoplaying in authoring tool if (window.H5PEditor === undefined) { // Keep record of autopauses. // I.e: we don't wanna autoplay if the user has excplicitly paused. self.autoPaused = true; // Set up intersection observer new IntersectionObserver(function (entries) { const entry = entries[0]; if (entry.intersectionRatio == 0) { if (!self.audio.paused) { // Audio element is hidden, pause it self.autoPaused = true; self.audio.pause(); } } else if (self.params.autoplay && self.autoPaused) { // Audio element is visible. Autoplay if autoplay is enabled and it was // not explicitly paused by a user self.autoPaused = false; self.audio.play(); } }, { root: document.documentElement, threshold: [0, 1] // Get events when it is shown and hidden }).observe($wrapper.get(0)); } }; /** * Attaches not supported message. * * @param {jQuery} $wrapper Our dear container. */ H5P.Audio.prototype.attachNotSupportedMessage = function ($wrapper) { $wrapper.addClass('h5p-audio-not-supported'); $wrapper.html( '