var H5P = H5P || {}; /** * Transition contains helper function relevant for transitioning */ H5P.Transition = (function ($) { /** * @class * @namespace H5P */ Transition = {}; /** * @private */ Transition.transitionEndEventNames = { 'WebkitTransition': 'webkitTransitionEnd', 'transition': 'transitionend', 'MozTransition': 'transitionend', 'OTransition': 'oTransitionEnd', 'msTransition': 'MSTransitionEnd' }; /** * @private */ Transition.cache = []; /** * Get the vendor property name for an event * * @function H5P.Transition.getVendorPropertyName * @static * @private * @param {string} prop Generic property name * @return {string} Vendor specific property name */ Transition.getVendorPropertyName = function (prop) { if (Transition.cache[prop] !== undefined) { return Transition.cache[prop]; } var div = document.createElement('div'); // Handle unprefixed versions (FF16+, for example) if (prop in div.style) { Transition.cache[prop] = prop; } else { var prefixes = ['Moz', 'Webkit', 'O', 'ms']; var prop_ = prop.charAt(0).toUpperCase() + prop.substr(1); if (prop in div.style) { Transition.cache[prop] = prop; } else { for (var i = 0; i < prefixes.length; ++i) { var vendorProp = prefixes[i] + prop_; if (vendorProp in div.style) { Transition.cache[prop] = vendorProp; break; } } } } return Transition.cache[prop]; }; /** * Get the name of the transition end event * * @static * @private * @return {string} description */ Transition.getTransitionEndEventName = function () { return Transition.transitionEndEventNames[Transition.getVendorPropertyName('transition')] || undefined; }; /** * Helper function for listening on transition end events * * @function H5P.Transition.onTransitionEnd * @static * @param {domElement} $element The element which is transitioned * @param {function} callback The callback to be invoked when transition is finished * @param {number} timeout Timeout in milliseconds. Fallback if transition event is never fired */ Transition.onTransitionEnd = function ($element, callback, timeout) { // Fallback on 1 second if transition event is not supported/triggered timeout = timeout || 1000; Transition.transitionEndEventName = Transition.transitionEndEventName || Transition.getTransitionEndEventName(); var callbackCalled = false; var doCallback = function () { if (callbackCalled) { return; } $element.off(Transition.transitionEndEventName, callback); callbackCalled = true; clearTimeout(timer); callback(); }; var timer = setTimeout(function () { doCallback(); }, timeout); $element.on(Transition.transitionEndEventName, function () { doCallback(); }); }; /** * Wait for a transition - when finished, invokes next in line * * @private * * @param {Object[]} transitions Array of transitions * @param {H5P.jQuery} transitions[].$element Dom element transition is performed on * @param {number=} transitions[].timeout Timeout fallback if transition end never is triggered * @param {bool=} transitions[].break If true, sequence breaks after this transition * @param {number} index The index for current transition */ var runSequence = function (transitions, index) { if (index >= transitions.length) { return; } var transition = transitions[index]; H5P.Transition.onTransitionEnd(transition.$element, function () { if (transition.end) { transition.end(); } if (transition.break !== true) { runSequence(transitions, index+1); } }, transition.timeout || undefined); }; /** * Run a sequence of transitions * * @function H5P.Transition.sequence * @static * @param {Object[]} transitions Array of transitions * @param {H5P.jQuery} transitions[].$element Dom element transition is performed on * @param {number=} transitions[].timeout Timeout fallback if transition end never is triggered * @param {bool=} transitions[].break If true, sequence breaks after this transition */ Transition.sequence = function (transitions) { runSequence(transitions, 0); }; return Transition; })(H5P.jQuery); ; var H5P = H5P || {}; /** * Class responsible for creating a help text dialog */ H5P.JoubelHelpTextDialog = (function ($) { var numInstances = 0; /** * Display a pop-up containing a message. * * @param {H5P.jQuery} $container The container which message dialog will be appended to * @param {string} message The message * @param {string} closeButtonTitle The title for the close button * @return {H5P.jQuery} */ function JoubelHelpTextDialog(header, message, closeButtonTitle) { H5P.EventDispatcher.call(this); var self = this; numInstances++; var headerId = 'joubel-help-text-header-' + numInstances; var helpTextId = 'joubel-help-text-body-' + numInstances; var $helpTextDialogBox = $('<div>', { 'class': 'joubel-help-text-dialog-box', 'role': 'dialog', 'aria-labelledby': headerId, 'aria-describedby': helpTextId }); $('<div>', { 'class': 'joubel-help-text-dialog-background' }).appendTo($helpTextDialogBox); var $helpTextDialogContainer = $('<div>', { 'class': 'joubel-help-text-dialog-container' }).appendTo($helpTextDialogBox); $('<div>', { 'class': 'joubel-help-text-header', 'id': headerId, 'role': 'header', 'html': header }).appendTo($helpTextDialogContainer); $('<div>', { 'class': 'joubel-help-text-body', 'id': helpTextId, 'html': message, 'role': 'document', 'tabindex': 0 }).appendTo($helpTextDialogContainer); var handleClose = function () { $helpTextDialogBox.remove(); self.trigger('closed'); }; var $closeButton = $('<div>', { 'class': 'joubel-help-text-remove', 'role': 'button', 'title': closeButtonTitle, 'tabindex': 1, 'click': handleClose, 'keydown': function (event) { // 32 - space, 13 - enter if ([32, 13].indexOf(event.which) !== -1) { event.preventDefault(); handleClose(); } } }).appendTo($helpTextDialogContainer); /** * Get the DOM element * @return {HTMLElement} */ self.getElement = function () { return $helpTextDialogBox; }; self.focus = function () { $closeButton.focus(); }; } JoubelHelpTextDialog.prototype = Object.create(H5P.EventDispatcher.prototype); JoubelHelpTextDialog.prototype.constructor = JoubelHelpTextDialog; return JoubelHelpTextDialog; }(H5P.jQuery)); ; var H5P = H5P || {}; /** * Class responsible for creating auto-disappearing dialogs */ H5P.JoubelMessageDialog = (function ($) { /** * Display a pop-up containing a message. * * @param {H5P.jQuery} $container The container which message dialog will be appended to * @param {string} message The message * @return {H5P.jQuery} */ function JoubelMessageDialog ($container, message) { var timeout; var removeDialog = function () { $warning.remove(); clearTimeout(timeout); $container.off('click.messageDialog'); }; // Create warning popup: var $warning = $('<div/>', { 'class': 'joubel-message-dialog', text: message }).appendTo($container); // Remove after 3 seconds or if user clicks anywhere in $container: timeout = setTimeout(removeDialog, 3000); $container.on('click.messageDialog', removeDialog); return $warning; } return JoubelMessageDialog; })(H5P.jQuery); ; var H5P = H5P || {}; /** * Class responsible for creating a circular progress bar */ H5P.JoubelProgressCircle = (function ($) { /** * Constructor for the Progress Circle * * @param {Number} number The amount of progress to display * @param {string} progressColor Color for the progress meter * @param {string} backgroundColor Color behind the progress meter */ function ProgressCircle(number, progressColor, fillColor, backgroundColor) { progressColor = progressColor || '#1a73d9'; fillColor = fillColor || '#f0f0f0'; backgroundColor = backgroundColor || '#ffffff'; var progressColorRGB = this.hexToRgb(progressColor); //Verify number try { number = Number(number); if (number === '') { throw 'is empty'; } if (isNaN(number)) { throw 'is not a number'; } } catch (e) { number = 'err'; } //Draw circle if (number > 100) { number = 100; } // We can not use rgba, since they will stack on top of each other. // Instead we create the equivalent of the rgba color // and applies this to the activeborder and background color. var progressColorString = 'rgb(' + parseInt(progressColorRGB.r, 10) + ',' + parseInt(progressColorRGB.g, 10) + ',' + parseInt(progressColorRGB.b, 10) + ')'; // Circle wrapper var $wrapper = $('<div/>', { 'class': "joubel-progress-circle-wrapper" }); //Active border indicates progress var $activeBorder = $('<div/>', { 'class': "joubel-progress-circle-active-border" }).appendTo($wrapper); //Background circle var $backgroundCircle = $('<div/>', { 'class': "joubel-progress-circle-circle" }).appendTo($activeBorder); //Progress text/number $('<span/>', { 'text': number + '%', 'class': "joubel-progress-circle-percentage" }).appendTo($backgroundCircle); var deg = number * 3.6; if (deg <= 180) { $activeBorder.css('background-image', 'linear-gradient(' + (90 + deg) + 'deg, transparent 50%, ' + fillColor + ' 50%),' + 'linear-gradient(90deg, ' + fillColor + ' 50%, transparent 50%)') .css('border', '2px solid' + backgroundColor) .css('background-color', progressColorString); } else { $activeBorder.css('background-image', 'linear-gradient(' + (deg - 90) + 'deg, transparent 50%, ' + progressColorString + ' 50%),' + 'linear-gradient(90deg, ' + fillColor + ' 50%, transparent 50%)') .css('border', '2px solid' + backgroundColor) .css('background-color', progressColorString); } this.$activeBorder = $activeBorder; this.$backgroundCircle = $backgroundCircle; this.$wrapper = $wrapper; this.initResizeFunctionality(); return $wrapper; } /** * Initializes resize functionality for the progress circle */ ProgressCircle.prototype.initResizeFunctionality = function () { var self = this; $(window).resize(function () { // Queue resize setTimeout(function () { self.resize(); }); }); // First resize setTimeout(function () { self.resize(); }, 0); }; /** * Resize function makes progress circle grow or shrink relative to parent container */ ProgressCircle.prototype.resize = function () { var $parent = this.$wrapper.parent(); if ($parent !== undefined && $parent) { // Measurements var fontSize = parseInt($parent.css('font-size'), 10); // Static sizes var fontSizeMultiplum = 3.75; var progressCircleWidthPx = parseInt((fontSize / 4.5), 10) % 2 === 0 ? parseInt((fontSize / 4.5), 10) + 4 : parseInt((fontSize / 4.5), 10) + 5; var progressCircleOffset = progressCircleWidthPx / 2; var width = fontSize * fontSizeMultiplum; var height = fontSize * fontSizeMultiplum; this.$activeBorder.css({ 'width': width, 'height': height }); this.$backgroundCircle.css({ 'width': width - progressCircleWidthPx, 'height': height - progressCircleWidthPx, 'top': progressCircleOffset, 'left': progressCircleOffset }); } }; /** * Hex to RGB conversion * @param hex * @returns {{r: Number, g: Number, b: Number}} */ ProgressCircle.prototype.hexToRgb = function (hex) { var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); return result ? { r: parseInt(result[1], 16), g: parseInt(result[2], 16), b: parseInt(result[3], 16) } : null; }; return ProgressCircle; }(H5P.jQuery)); ; var H5P = H5P || {}; H5P.SimpleRoundedButton = (function ($) { /** * Creates a new tip */ function SimpleRoundedButton(text) { var $simpleRoundedButton = $('<div>', { 'class': 'joubel-simple-rounded-button', 'title': text, 'role': 'button', 'tabindex': '0' }).keydown(function (e) { // 32 - space, 13 - enter if ([32, 13].indexOf(e.which) !== -1) { $(this).click(); e.preventDefault(); } }); $('<span>', { 'class': 'joubel-simple-rounded-button-text', 'html': text }).appendTo($simpleRoundedButton); return $simpleRoundedButton; } return SimpleRoundedButton; }(H5P.jQuery)); ; var H5P = H5P || {}; /** * Class responsible for creating speech bubbles */ H5P.JoubelSpeechBubble = (function ($) { var $currentSpeechBubble; var $currentContainer; var $tail; var $innerTail; var removeSpeechBubbleTimeout; var currentMaxWidth; var DEFAULT_MAX_WIDTH = 400; var iDevice = navigator.userAgent.match(/iPod|iPhone|iPad/g) ? true : false; /** * Creates a new speech bubble * * @param {H5P.jQuery} $container The speaking object * @param {string} text The text to display * @param {number} maxWidth The maximum width of the bubble * @return {H5P.JoubelSpeechBubble} */ function JoubelSpeechBubble($container, text, maxWidth) { maxWidth = maxWidth || DEFAULT_MAX_WIDTH; currentMaxWidth = maxWidth; $currentContainer = $container; this.isCurrent = function ($tip) { return $tip.is($currentContainer); }; this.remove = function () { remove(); }; var fadeOutSpeechBubble = function ($speechBubble) { if (!$speechBubble) { return; } // Stop removing bubble clearTimeout(removeSpeechBubbleTimeout); $speechBubble.removeClass('show'); setTimeout(function () { if ($speechBubble) { $speechBubble.remove(); $speechBubble = undefined; } }, 500); }; if ($currentSpeechBubble !== undefined) { remove(); } var $h5pContainer = getH5PContainer($container); // Make sure we fade out old speech bubble fadeOutSpeechBubble($currentSpeechBubble); // Create bubble $tail = $('<div class="joubel-speech-bubble-tail"></div>'); $innerTail = $('<div class="joubel-speech-bubble-inner-tail"></div>'); var $innerBubble = $( '<div class="joubel-speech-bubble-inner">' + '<div class="joubel-speech-bubble-text">' + text + '</div>' + '</div>' ).prepend($innerTail); $currentSpeechBubble = $( '<div class="joubel-speech-bubble" aria-live="assertive">' ).append([$tail, $innerBubble]) .appendTo($h5pContainer); // Show speech bubble with transition setTimeout(function () { $currentSpeechBubble.addClass('show'); }, 0); position($currentSpeechBubble, $currentContainer, maxWidth, $tail, $innerTail); // Handle click to close H5P.$body.on('mousedown.speechBubble', handleOutsideClick); // Handle window resizing H5P.$window.on('resize', '', handleResize); // Handle clicks when inside IV which blocks bubbling. $container.parents('.h5p-dialog') .on('mousedown.speechBubble', handleOutsideClick); if (iDevice) { H5P.$body.css('cursor', 'pointer'); } return this; } // Remove speechbubble if it belongs to a dom element that is about to be hidden H5P.externalDispatcher.on('domHidden', function (event) { if ($currentSpeechBubble !== undefined && event.data.$dom.find($currentContainer).length !== 0) { remove(); } }); /** * Returns the closest h5p container for the given DOM element. * * @param {object} $container jquery element * @return {object} the h5p container (jquery element) */ function getH5PContainer($container) { var $h5pContainer = $container.closest('.h5p-frame'); // Check closest h5p frame first, then check for container in case there is no frame. if (!$h5pContainer.length) { $h5pContainer = $container.closest('.h5p-container'); } return $h5pContainer; } /** * Event handler that is called when the window is resized. */ function handleResize() { position($currentSpeechBubble, $currentContainer, currentMaxWidth, $tail, $innerTail); } /** * Repositions the speech bubble according to the position of the container. * * @param {object} $currentSpeechbubble the speech bubble that should be positioned * @param {object} $container the container to which the speech bubble should point * @param {number} maxWidth the maximum width of the speech bubble * @param {object} $tail the tail (the triangle that points to the referenced container) * @param {object} $innerTail the inner tail (the triangle that points to the referenced container) */ function position($currentSpeechBubble, $container, maxWidth, $tail, $innerTail) { var $h5pContainer = getH5PContainer($container); // Calculate offset between the button and the h5p frame var offset = getOffsetBetween($h5pContainer, $container); var direction = (offset.bottom > offset.top ? 'bottom' : 'top'); var tipWidth = offset.outerWidth * 0.9; // Var needs to be renamed to make sense var bubbleWidth = tipWidth > maxWidth ? maxWidth : tipWidth; var bubblePosition = getBubblePosition(bubbleWidth, offset); var tailPosition = getTailPosition(bubbleWidth, bubblePosition, offset, $container.width()); // Need to set font-size, since element is appended to body. // Using same font-size as parent. In that way it will grow accordingly // when resizing var fontSize = 16;//parseFloat($parent.css('font-size')); // Set width and position of speech bubble $currentSpeechBubble.css(bubbleCSS( direction, bubbleWidth, bubblePosition, fontSize )); var preparedTailCSS = tailCSS(direction, tailPosition); $tail.css(preparedTailCSS); $innerTail.css(preparedTailCSS); } /** * Static function for removing the speechbubble */ var remove = function () { H5P.$body.off('mousedown.speechBubble'); H5P.$window.off('resize', '', handleResize); $currentContainer.parents('.h5p-dialog').off('mousedown.speechBubble'); if (iDevice) { H5P.$body.css('cursor', ''); } if ($currentSpeechBubble !== undefined) { // Apply transition, then remove speech bubble $currentSpeechBubble.removeClass('show'); // Make sure we remove any old timeout before reassignment clearTimeout(removeSpeechBubbleTimeout); removeSpeechBubbleTimeout = setTimeout(function () { $currentSpeechBubble.remove(); $currentSpeechBubble = undefined; }, 500); } // Don't return false here. If the user e.g. clicks a button when the bubble is visible, // we want the bubble to disapear AND the button to receive the event }; /** * Remove the speech bubble and container reference */ function handleOutsideClick(event) { if (event.target === $currentContainer[0]) { return; // Button clicks are not outside clicks } remove(); // There is no current container when a container isn't clicked $currentContainer = undefined; } /** * Calculate position for speech bubble * * @param {number} bubbleWidth The width of the speech bubble * @param {object} offset * @return {object} Return position for the speech bubble */ function getBubblePosition(bubbleWidth, offset) { var bubblePosition = {}; var tailOffset = 9; var widthOffset = bubbleWidth / 2; // Calculate top position bubblePosition.top = offset.top + offset.innerHeight; // Calculate bottom position bubblePosition.bottom = offset.bottom + offset.innerHeight + tailOffset; // Calculate left position if (offset.left < widthOffset) { bubblePosition.left = 3; } else if ((offset.left + widthOffset) > offset.outerWidth) { bubblePosition.left = offset.outerWidth - bubbleWidth - 3; } else { bubblePosition.left = offset.left - widthOffset + (offset.innerWidth / 2); } return bubblePosition; } /** * Calculate position for speech bubble tail * * @param {number} bubbleWidth The width of the speech bubble * @param {object} bubblePosition Speech bubble position * @param {object} offset * @param {number} iconWidth The width of the tip icon * @return {object} Return position for the tail */ function getTailPosition(bubbleWidth, bubblePosition, offset, iconWidth) { var tailPosition = {}; // Magic numbers. Tuned by hand so that the tail fits visually within // the bounds of the speech bubble. var leftBoundary = 9; var rightBoundary = bubbleWidth - 20; tailPosition.left = offset.left - bubblePosition.left + (iconWidth / 2) - 6; if (tailPosition.left < leftBoundary) { tailPosition.left = leftBoundary; } if (tailPosition.left > rightBoundary) { tailPosition.left = rightBoundary; } tailPosition.top = -6; tailPosition.bottom = -6; return tailPosition; } /** * Return bubble CSS for the desired growth direction * * @param {string} direction The direction the speech bubble will grow * @param {number} width The width of the speech bubble * @param {object} position Speech bubble position * @param {number} fontSize The size of the bubbles font * @return {object} Return CSS */ function bubbleCSS(direction, width, position, fontSize) { if (direction === 'top') { return { width: width + 'px', bottom: position.bottom + 'px', left: position.left + 'px', fontSize: fontSize + 'px', top: '' }; } else { return { width: width + 'px', top: position.top + 'px', left: position.left + 'px', fontSize: fontSize + 'px', bottom: '' }; } } /** * Return tail CSS for the desired growth direction * * @param {string} direction The direction the speech bubble will grow * @param {object} position Tail position * @return {object} Return CSS */ function tailCSS(direction, position) { if (direction === 'top') { return { bottom: position.bottom + 'px', left: position.left + 'px', top: '' }; } else { return { top: position.top + 'px', left: position.left + 'px', bottom: '' }; } } /** * Calculates the offset between an element inside a container and the * container. Only works if all the edges of the inner element are inside the * outer element. * Width/height of the elements is included as a convenience. * * @param {H5P.jQuery} $outer * @param {H5P.jQuery} $inner * @return {object} Position offset */ function getOffsetBetween($outer, $inner) { var outer = $outer[0].getBoundingClientRect(); var inner = $inner[0].getBoundingClientRect(); return { top: inner.top - outer.top, right: outer.right - inner.right, bottom: outer.bottom - inner.bottom, left: inner.left - outer.left, innerWidth: inner.width, innerHeight: inner.height, outerWidth: outer.width, outerHeight: outer.height }; } return JoubelSpeechBubble; })(H5P.jQuery); ; var H5P = H5P || {}; H5P.JoubelThrobber = (function ($) { /** * Creates a new tip */ function JoubelThrobber() { // h5p-throbber css is described in core var $throbber = $('<div/>', { 'class': 'h5p-throbber' }); return $throbber; } return JoubelThrobber; }(H5P.jQuery)); ; H5P.JoubelTip = (function ($) { var $conv = $('<div/>'); /** * Creates a new tip element. * * NOTE that this may look like a class but it doesn't behave like one. * It returns a jQuery object. * * @param {string} tipHtml The text to display in the popup * @param {Object} [behaviour] Options * @param {string} [behaviour.tipLabel] Set to use a custom label for the tip button (you want this for good A11Y) * @param {boolean} [behaviour.helpIcon] Set to 'true' to Add help-icon classname to Tip button (changes the icon) * @param {boolean} [behaviour.showSpeechBubble] Set to 'false' to disable functionality (you may this in the editor) * @param {boolean} [behaviour.tabcontrol] Set to 'true' if you plan on controlling the tabindex in the parent (tabindex="-1") * @return {H5P.jQuery|undefined} Tip button jQuery element or 'undefined' if invalid tip */ function JoubelTip(tipHtml, behaviour) { // Keep track of the popup that appears when you click the Tip button var speechBubble; // Parse tip html to determine text var tipText = $conv.html(tipHtml).text().trim(); if (tipText === '') { return; // The tip has no textual content, i.e. it's invalid. } // Set default behaviour behaviour = $.extend({ tipLabel: tipText, helpIcon: false, showSpeechBubble: true, tabcontrol: false }, behaviour); // Create Tip button var $tipButton = $('<div/>', { class: 'joubel-tip-container' + (behaviour.showSpeechBubble ? '' : ' be-quiet'), 'aria-label': behaviour.tipLabel, 'aria-expanded': false, role: 'button', tabindex: (behaviour.tabcontrol ? -1 : 0), click: function (event) { // Toggle show/hide popup toggleSpeechBubble(); event.preventDefault(); }, keydown: function (event) { if (event.which === 32 || event.which === 13) { // Space & enter key // Toggle show/hide popup toggleSpeechBubble(); event.stopPropagation(); event.preventDefault(); } else { // Any other key // Toggle hide popup toggleSpeechBubble(false); } }, // Add markup to render icon html: '<span class="joubel-icon-tip-normal ' + (behaviour.helpIcon ? ' help-icon': '') + '">' + '<span class="h5p-icon-shadow"></span>' + '<span class="h5p-icon-speech-bubble"></span>' + '<span class="h5p-icon-info"></span>' + '</span>' // IMPORTANT: All of the markup elements must have 'pointer-events: none;' }); const $tipAnnouncer = $('<div>', { 'class': 'hidden-but-read', 'aria-live': 'polite', appendTo: $tipButton, }); /** * Tip button interaction handler. * Toggle show or hide the speech bubble popup when interacting with the * Tip button. * * @private * @param {boolean} [force] 'true' shows and 'false' hides. */ var toggleSpeechBubble = function (force) { if (speechBubble !== undefined && speechBubble.isCurrent($tipButton)) { // Hide current popup speechBubble.remove(); speechBubble = undefined; $tipButton.attr('aria-expanded', false); $tipAnnouncer.html(''); } else if (force !== false && behaviour.showSpeechBubble) { // Create and show new popup speechBubble = H5P.JoubelSpeechBubble($tipButton, tipHtml); $tipButton.attr('aria-expanded', true); $tipAnnouncer.html(tipHtml); } }; return $tipButton; } return JoubelTip; })(H5P.jQuery); ; var H5P = H5P || {}; H5P.JoubelSlider = (function ($) { /** * Creates a new Slider * * @param {object} [params] Additional parameters */ function JoubelSlider(params) { H5P.EventDispatcher.call(this); this.$slider = $('<div>', $.extend({ 'class': 'h5p-joubel-ui-slider' }, params)); this.$slides = []; this.currentIndex = 0; this.numSlides = 0; } JoubelSlider.prototype = Object.create(H5P.EventDispatcher.prototype); JoubelSlider.prototype.constructor = JoubelSlider; JoubelSlider.prototype.addSlide = function ($content) { $content.addClass('h5p-joubel-ui-slide').css({ 'left': (this.numSlides*100) + '%' }); this.$slider.append($content); this.$slides.push($content); this.numSlides++; if(this.numSlides === 1) { $content.addClass('current'); } }; JoubelSlider.prototype.attach = function ($container) { $container.append(this.$slider); }; JoubelSlider.prototype.move = function (index) { var self = this; if(index === 0) { self.trigger('first-slide'); } if(index+1 === self.numSlides) { self.trigger('last-slide'); } self.trigger('move'); var $previousSlide = self.$slides[this.currentIndex]; H5P.Transition.onTransitionEnd(this.$slider, function () { $previousSlide.removeClass('current'); self.trigger('moved'); }); this.$slides[index].addClass('current'); var translateX = 'translateX(' + (-index*100) + '%)'; this.$slider.css({ '-webkit-transform': translateX, '-moz-transform': translateX, '-ms-transform': translateX, 'transform': translateX }); this.currentIndex = index; }; JoubelSlider.prototype.remove = function () { this.$slider.remove(); }; JoubelSlider.prototype.next = function () { if(this.currentIndex+1 >= this.numSlides) { return; } this.move(this.currentIndex+1); }; JoubelSlider.prototype.previous = function () { this.move(this.currentIndex-1); }; JoubelSlider.prototype.first = function () { this.move(0); }; JoubelSlider.prototype.last = function () { this.move(this.numSlides-1); }; return JoubelSlider; })(H5P.jQuery); ; var H5P = H5P || {}; /** * @module */ H5P.JoubelScoreBar = (function ($) { /* Need to use an id for the star SVG since that is the only way to reference SVG filters */ var idCounter = 0; /** * Creates a score bar * @class H5P.JoubelScoreBar * @param {number} maxScore Maximum score * @param {string} [label] Makes it easier for readspeakers to identify the scorebar * @param {string} [helpText] Score explanation * @param {string} [scoreExplanationButtonLabel] Label for score explanation button */ function JoubelScoreBar(maxScore, label, helpText, scoreExplanationButtonLabel) { var self = this; self.maxScore = maxScore; self.score = 0; idCounter++; /** * @const {string} */ self.STAR_MARKUP = '<svg tabindex="-1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 63.77 53.87" aria-hidden="true" focusable="false">' + '<title>star</title>' + '<filter tabindex="-1" id="h5p-joubelui-score-bar-star-inner-shadow-' + idCounter + '" x0="-50%" y0="-50%" width="200%" height="200%">' + '<feGaussianBlur in="SourceAlpha" stdDeviation="3" result="blur"></feGaussianBlur>' + '<feOffset dy="2" dx="4"></feOffset>' + '<feComposite in2="SourceAlpha" operator="arithmetic" k2="-1" k3="1" result="shadowDiff"></feComposite>' + '<feFlood flood-color="#ffe95c" flood-opacity="1"></feFlood>' + '<feComposite in2="shadowDiff" operator="in"></feComposite>' + '<feComposite in2="SourceGraphic" operator="over" result="firstfilter"></feComposite>' + '<feGaussianBlur in="firstfilter" stdDeviation="3" result="blur2"></feGaussianBlur>' + '<feOffset dy="-2" dx="-4"></feOffset>' + '<feComposite in2="firstfilter" operator="arithmetic" k2="-1" k3="1" result="shadowDiff"></feComposite>' + '<feFlood flood-color="#ffe95c" flood-opacity="1"></feFlood>' + '<feComposite in2="shadowDiff" operator="in"></feComposite>' + '<feComposite in2="firstfilter" operator="over"></feComposite>' + '</filter>' + '<path tabindex="-1" class="h5p-joubelui-score-bar-star-shadow" d="M35.08,43.41V9.16H20.91v0L9.51,10.85,9,10.93C2.8,12.18,0,17,0,21.25a11.22,11.22,0,0,0,3,7.48l8.73,8.53-1.07,6.16Z"/>' + '<g tabindex="-1">' + '<path tabindex="-1" class="h5p-joubelui-score-bar-star-border" d="M61.36,22.8,49.72,34.11l2.78,16a2.6,2.6,0,0,1,.05.64c0,.85-.37,1.6-1.33,1.6A2.74,2.74,0,0,1,49.94,52L35.58,44.41,21.22,52a2.93,2.93,0,0,1-1.28.37c-.91,0-1.33-.75-1.33-1.6,0-.21.05-.43.05-.64l2.78-16L9.8,22.8A2.57,2.57,0,0,1,9,21.25c0-1,1-1.33,1.81-1.49l16.07-2.35L34.09,2.83c.27-.59.85-1.33,1.55-1.33s1.28.69,1.55,1.33l7.21,14.57,16.07,2.35c.75.11,1.81.53,1.81,1.49A3.07,3.07,0,0,1,61.36,22.8Z"/>' + '<path tabindex="-1" class="h5p-joubelui-score-bar-star-fill" d="M61.36,22.8,49.72,34.11l2.78,16a2.6,2.6,0,0,1,.05.64c0,.85-.37,1.6-1.33,1.6A2.74,2.74,0,0,1,49.94,52L35.58,44.41,21.22,52a2.93,2.93,0,0,1-1.28.37c-.91,0-1.33-.75-1.33-1.6,0-.21.05-.43.05-.64l2.78-16L9.8,22.8A2.57,2.57,0,0,1,9,21.25c0-1,1-1.33,1.81-1.49l16.07-2.35L34.09,2.83c.27-.59.85-1.33,1.55-1.33s1.28.69,1.55,1.33l7.21,14.57,16.07,2.35c.75.11,1.81.53,1.81,1.49A3.07,3.07,0,0,1,61.36,22.8Z"/>' + '<path tabindex="-1" filter="url(#h5p-joubelui-score-bar-star-inner-shadow-' + idCounter + ')" class="h5p-joubelui-score-bar-star-fill-full-score" d="M61.36,22.8,49.72,34.11l2.78,16a2.6,2.6,0,0,1,.05.64c0,.85-.37,1.6-1.33,1.6A2.74,2.74,0,0,1,49.94,52L35.58,44.41,21.22,52a2.93,2.93,0,0,1-1.28.37c-.91,0-1.33-.75-1.33-1.6,0-.21.05-.43.05-.64l2.78-16L9.8,22.8A2.57,2.57,0,0,1,9,21.25c0-1,1-1.33,1.81-1.49l16.07-2.35L34.09,2.83c.27-.59.85-1.33,1.55-1.33s1.28.69,1.55,1.33l7.21,14.57,16.07,2.35c.75.11,1.81.53,1.81,1.49A3.07,3.07,0,0,1,61.36,22.8Z"/>' + '</g>' + '</svg>'; /** * @function appendTo * @memberOf H5P.JoubelScoreBar# * @param {H5P.jQuery} $wrapper Dom container */ self.appendTo = function ($wrapper) { self.$scoreBar.appendTo($wrapper); }; /** * Create the text representation of the scorebar . * * @private * @return {string} */ var createLabel = function (score) { if (!label) { return ''; } return label.replace(':num', score).replace(':total', self.maxScore); }; /** * Creates the html for this widget * * @method createHtml * @private */ var createHtml = function () { // Container div self.$scoreBar = $('<div>', { 'class': 'h5p-joubelui-score-bar', }); var $visuals = $('<div>', { 'class': 'h5p-joubelui-score-bar-visuals', appendTo: self.$scoreBar }); // The progress bar wrapper self.$progressWrapper = $('<div>', { 'class': 'h5p-joubelui-score-bar-progress-wrapper', appendTo: $visuals }); self.$progress = $('<div>', { 'class': 'h5p-joubelui-score-bar-progress', 'html': createLabel(self.score), appendTo: self.$progressWrapper }); // The star $('<div>', { 'class': 'h5p-joubelui-score-bar-star', html: self.STAR_MARKUP }).appendTo($visuals); // The score container var $numerics = $('<div>', { 'class': 'h5p-joubelui-score-numeric', appendTo: self.$scoreBar, 'aria-hidden': true }); // The current score self.$scoreCounter = $('<span>', { 'class': 'h5p-joubelui-score-number h5p-joubelui-score-number-counter', text: 0, appendTo: $numerics }); // The separator $('<span>', { 'class': 'h5p-joubelui-score-number-separator', text: '/', appendTo: $numerics }); // Max score self.$maxScore = $('<span>', { 'class': 'h5p-joubelui-score-number h5p-joubelui-score-max', text: self.maxScore, appendTo: $numerics }); if (helpText) { H5P.JoubelUI.createTip(helpText, { tipLabel: scoreExplanationButtonLabel ? scoreExplanationButtonLabel : helpText, helpIcon: true }).appendTo(self.$scoreBar); self.$scoreBar.addClass('h5p-score-bar-has-help'); } }; /** * Set the current score * @method setScore * @memberOf H5P.JoubelScoreBar# * @param {number} score */ self.setScore = function (score) { // Do nothing if score hasn't changed if (score === self.score) { return; } self.score = score > self.maxScore ? self.maxScore : score; self.updateVisuals(); }; /** * Increment score * @method incrementScore * @memberOf H5P.JoubelScoreBar# * @param {number=} incrementBy Optional parameter, defaults to 1 */ self.incrementScore = function (incrementBy) { self.setScore(self.score + (incrementBy || 1)); }; /** * Set the max score * @method setMaxScore * @memberOf H5P.JoubelScoreBar# * @param {number} maxScore The max score */ self.setMaxScore = function (maxScore) { self.maxScore = maxScore; }; /** * Updates the progressbar visuals * @memberOf H5P.JoubelScoreBar# * @method updateVisuals */ self.updateVisuals = function () { self.$progress.html(createLabel(self.score)); self.$scoreCounter.text(self.score); self.$maxScore.text(self.maxScore); setTimeout(function () { // Start the progressbar animation self.$progress.css({ width: ((self.score / self.maxScore) * 100) + '%' }); H5P.Transition.onTransitionEnd(self.$progress, function () { // If fullscore fill the star and start the animation self.$scoreBar.toggleClass('h5p-joubelui-score-bar-full-score', self.score === self.maxScore); self.$scoreBar.toggleClass('h5p-joubelui-score-bar-animation-active', self.score === self.maxScore); // Only allow the star animation to run once self.$scoreBar.one("animationend", function() { self.$scoreBar.removeClass("h5p-joubelui-score-bar-animation-active"); }); }, 600); }, 300); }; /** * Removes all classes * @method reset */ self.reset = function () { self.$scoreBar.removeClass('h5p-joubelui-score-bar-full-score'); }; createHtml(); } return JoubelScoreBar; })(H5P.jQuery); ; var H5P = H5P || {}; H5P.JoubelProgressbar = (function ($) { /** * Joubel progressbar class * @method JoubelProgressbar * @constructor * @param {number} steps Number of steps * @param {Object} [options] Additional options * @param {boolean} [options.disableAria] Disable readspeaker assistance * @param {string} [options.progressText] A progress text for describing * current progress out of total progress for readspeakers. * e.g. "Slide :num of :total" */ function JoubelProgressbar(steps, options) { H5P.EventDispatcher.call(this); var self = this; this.options = $.extend({ progressText: 'Slide :num of :total' }, options); this.currentStep = 0; this.steps = steps; this.$progressbar = $('<div>', { 'class': 'h5p-joubelui-progressbar' }); this.$background = $('<div>', { 'class': 'h5p-joubelui-progressbar-background' }).appendTo(this.$progressbar); } JoubelProgressbar.prototype = Object.create(H5P.EventDispatcher.prototype); JoubelProgressbar.prototype.constructor = JoubelProgressbar; JoubelProgressbar.prototype.updateAria = function () { var self = this; if (this.options.disableAria) { return; } if (!this.$currentStatus) { this.$currentStatus = $('<div>', { 'class': 'h5p-joubelui-progressbar-slide-status-text', 'aria-live': 'assertive' }).appendTo(this.$progressbar); } var interpolatedProgressText = self.options.progressText .replace(':num', self.currentStep) .replace(':total', self.steps); this.$currentStatus.html(interpolatedProgressText); }; /** * Appends to a container * @method appendTo * @param {H5P.jquery} $container */ JoubelProgressbar.prototype.appendTo = function ($container) { this.$progressbar.appendTo($container); }; /** * Update progress * @method setProgress * @param {number} step */ JoubelProgressbar.prototype.setProgress = function (step) { // Check for valid value: if (step > this.steps || step < 0) { return; } this.currentStep = step; this.$background.css({ width: ((this.currentStep/this.steps)*100) + '%' }); this.updateAria(); }; /** * Increment progress with 1 * @method next */ JoubelProgressbar.prototype.next = function () { this.setProgress(this.currentStep+1); }; /** * Reset progressbar * @method reset */ JoubelProgressbar.prototype.reset = function () { this.setProgress(0); }; /** * Check if last step is reached * @method isLastStep * @return {Boolean} */ JoubelProgressbar.prototype.isLastStep = function () { return this.steps === this.currentStep; }; return JoubelProgressbar; })(H5P.jQuery); ; var H5P = H5P || {}; /** * H5P Joubel UI library. * * This is a utility library, which does not implement attach. I.e, it has to bee actively used by * other libraries * @module */ H5P.JoubelUI = (function ($) { /** * The internal object to return * @class H5P.JoubelUI * @static */ function JoubelUI() {} /* Public static functions */ /** * Create a tip icon * @method H5P.JoubelUI.createTip * @param {string} text The textual tip * @param {Object} params Parameters * @return {H5P.JoubelTip} */ JoubelUI.createTip = function (text, params) { return new H5P.JoubelTip(text, params); }; /** * Create message dialog * @method H5P.JoubelUI.createMessageDialog * @param {H5P.jQuery} $container The dom container * @param {string} message The message * @return {H5P.JoubelMessageDialog} */ JoubelUI.createMessageDialog = function ($container, message) { return new H5P.JoubelMessageDialog($container, message); }; /** * Create help text dialog * @method H5P.JoubelUI.createHelpTextDialog * @param {string} header The textual header * @param {string} message The textual message * @param {string} closeButtonTitle The title for the close button * @return {H5P.JoubelHelpTextDialog} */ JoubelUI.createHelpTextDialog = function (header, message, closeButtonTitle) { return new H5P.JoubelHelpTextDialog(header, message, closeButtonTitle); }; /** * Create progress circle * @method H5P.JoubelUI.createProgressCircle * @param {number} number The progress (0 to 100) * @param {string} progressColor The progress color in hex value * @param {string} fillColor The fill color in hex value * @param {string} backgroundColor The background color in hex value * @return {H5P.JoubelProgressCircle} */ JoubelUI.createProgressCircle = function (number, progressColor, fillColor, backgroundColor) { return new H5P.JoubelProgressCircle(number, progressColor, fillColor, backgroundColor); }; /** * Create throbber for loading * @method H5P.JoubelUI.createThrobber * @return {H5P.JoubelThrobber} */ JoubelUI.createThrobber = function () { return new H5P.JoubelThrobber(); }; /** * Create simple rounded button * @method H5P.JoubelUI.createSimpleRoundedButton * @param {string} text The button label * @return {H5P.SimpleRoundedButton} */ JoubelUI.createSimpleRoundedButton = function (text) { return new H5P.SimpleRoundedButton(text); }; /** * Create Slider * @method H5P.JoubelUI.createSlider * @param {Object} [params] Parameters * @return {H5P.JoubelSlider} */ JoubelUI.createSlider = function (params) { return new H5P.JoubelSlider(params); }; /** * Create Score Bar * @method H5P.JoubelUI.createScoreBar * @param {number=} maxScore The maximum score * @param {string} [label] Makes it easier for readspeakers to identify the scorebar * @return {H5P.JoubelScoreBar} */ JoubelUI.createScoreBar = function (maxScore, label, helpText, scoreExplanationButtonLabel) { return new H5P.JoubelScoreBar(maxScore, label, helpText, scoreExplanationButtonLabel); }; /** * Create Progressbar * @method H5P.JoubelUI.createProgressbar * @param {number=} numSteps The total numer of steps * @param {Object} [options] Additional options * @param {boolean} [options.disableAria] Disable readspeaker assistance * @param {string} [options.progressText] A progress text for describing * current progress out of total progress for readspeakers. * e.g. "Slide :num of :total" * @return {H5P.JoubelProgressbar} */ JoubelUI.createProgressbar = function (numSteps, options) { return new H5P.JoubelProgressbar(numSteps, options); }; /** * Create standard Joubel button * * @method H5P.JoubelUI.createButton * @param {object} params * May hold any properties allowed by jQuery. If href is set, an A tag * is used, if not a button tag is used. * @return {H5P.jQuery} The jquery element created */ JoubelUI.createButton = function(params) { var type = 'button'; if (params.href) { type = 'a'; } else { params.type = 'button'; } if (params.class) { params.class += ' h5p-joubelui-button'; } else { params.class = 'h5p-joubelui-button'; } return $('<' + type + '/>', params); }; /** * Fix for iframe scoll bug in IOS. When focusing an element that doesn't have * focus support by default the iframe will scroll the parent frame so that * the focused element is out of view. This varies dependening on the elements * of the parent frame. */ if (H5P.isFramed && !H5P.hasiOSiframeScrollFix && /iPad|iPhone|iPod/.test(navigator.userAgent)) { H5P.hasiOSiframeScrollFix = true; // Keep track of original focus function var focus = HTMLElement.prototype.focus; // Override the original focus HTMLElement.prototype.focus = function () { // Only focus the element if it supports it natively if ( (this instanceof HTMLAnchorElement || this instanceof HTMLInputElement || this instanceof HTMLSelectElement || this instanceof HTMLTextAreaElement || this instanceof HTMLButtonElement || this instanceof HTMLIFrameElement || this instanceof HTMLAreaElement) && // HTMLAreaElement isn't supported by Safari yet. !this.getAttribute('role')) { // Focus breaks if a different role has been set // In theory this.isContentEditable should be able to recieve focus, // but it didn't work when tested. // Trigger the original focus with the proper context focus.call(this); } }; } return JoubelUI; })(H5P.jQuery); ; H5P.Question = (function ($, EventDispatcher, JoubelUI) { /** * Extending this class make it alot easier to create tasks for other * content types. * * @class H5P.Question * @extends H5P.EventDispatcher * @param {string} type */ function Question(type) { var self = this; // Inheritance EventDispatcher.call(self); // Register default section order self.order = ['video', 'image', 'introduction', 'content', 'explanation', 'feedback', 'scorebar', 'buttons', 'read']; // Keep track of registered sections var sections = {}; // Buttons var buttons = {}; var buttonOrder = []; // Wrapper when attached var $wrapper; // Click element var clickElement; // ScoreBar var scoreBar; // Keep track of the feedback's visual status. var showFeedback; // Keep track of which buttons are scheduled for hiding. var buttonsToHide = []; // Keep track of which buttons are scheduled for showing. var buttonsToShow = []; // Keep track of the hiding and showing of buttons. var toggleButtonsTimer; var toggleButtonsTransitionTimer; var buttonTruncationTimer; // Keeps track of initialization of question var initialized = false; /** * @type {Object} behaviour Behaviour of Question * @property {Boolean} behaviour.disableFeedback Set to true to disable feedback section */ var behaviour = { disableFeedback: false, disableReadSpeaker: false }; // Keeps track of thumb state var imageThumb = true; // Keeps track of image transitions var imageTransitionTimer; // Keep track of whether sections is transitioning. var sectionsIsTransitioning = false; // Keep track of auto play state var disableAutoPlay = false; // Feedback transition timer var feedbackTransitionTimer; // Used when reading messages to the user var $read, readText; /** * Register section with given content. * * @private * @param {string} section ID of the section * @param {(string|H5P.jQuery)} [content] */ var register = function (section, content) { sections[section] = {}; var $e = sections[section].$element = $('<div/>', { 'class': 'h5p-question-' + section, }); if (content) { $e[content instanceof $ ? 'append' : 'html'](content); } }; /** * Update registered section with content. * * @private * @param {string} section ID of the section * @param {(string|H5P.jQuery)} content */ var update = function (section, content) { if (content instanceof $) { sections[section].$element.html('').append(content); } else { sections[section].$element.html(content); } }; /** * Insert element with given ID into the DOM. * * @private * @param {array|Array|string[]} order * List with ordered element IDs * @param {string} id * ID of the element to be inserted * @param {Object} elements * Maps ID to the elements * @param {H5P.jQuery} $container * Parent container of the elements */ var insert = function (order, id, elements, $container) { // Try to find an element id should be after for (var i = 0; i < order.length; i++) { if (order[i] === id) { // Found our pos while (i > 0 && (elements[order[i - 1]] === undefined || !elements[order[i - 1]].isVisible)) { i--; } if (i === 0) { // We are on top. elements[id].$element.prependTo($container); } else { // Add after element elements[id].$element.insertAfter(elements[order[i - 1]].$element); } elements[id].isVisible = true; break; } } }; /** * Make feedback into a popup and position relative to click. * * @private * @param {string} [closeText] Text for the close button */ var makeFeedbackPopup = function (closeText) { var $element = sections.feedback.$element; var $parent = sections.content.$element; var $click = (clickElement != null ? clickElement.$element : null); $element.appendTo($parent).addClass('h5p-question-popup'); if (sections.scorebar) { sections.scorebar.$element.appendTo($element); } $parent.addClass('h5p-has-question-popup'); // Draw the tail var $tail = $('<div/>', { 'class': 'h5p-question-feedback-tail' }).hide() .appendTo($parent); // Draw the close button var $close = $('<div/>', { 'class': 'h5p-question-feedback-close', 'tabindex': 0, 'title': closeText, on: { click: function (event) { $element.remove(); $tail.remove(); event.preventDefault(); }, keydown: function (event) { switch (event.which) { case 13: // Enter case 32: // Space $element.remove(); $tail.remove(); event.preventDefault(); } } } }).hide().appendTo($element); if ($click != null) { if ($click.hasClass('correct')) { $element.addClass('h5p-question-feedback-correct'); $close.show(); sections.buttons.$element.hide(); } else { sections.buttons.$element.appendTo(sections.feedback.$element); } } positionFeedbackPopup($element, $click); }; /** * Position the feedback popup. * * @private * @param {H5P.jQuery} $element Feedback div * @param {H5P.jQuery} $click Visual click div */ var positionFeedbackPopup = function ($element, $click) { var $container = $element.parent(); var $tail = $element.siblings('.h5p-question-feedback-tail'); var popupWidth = $element.outerWidth(); var popupHeight = setElementHeight($element); var space = 15; var disableTail = false; var positionY = $container.height() / 2 - popupHeight / 2; var positionX = $container.width() / 2 - popupWidth / 2; var tailX = 0; var tailY = 0; var tailRotation = 0; if ($click != null) { // Edge detection for click, takes space into account var clickNearTop = ($click[0].offsetTop < space); var clickNearBottom = ($click[0].offsetTop + $click.height() > $container.height() - space); var clickNearLeft = ($click[0].offsetLeft < space); var clickNearRight = ($click[0].offsetLeft + $click.width() > $container.width() - space); // Click is not in a corner or close to edge, calculate position normally positionX = $click[0].offsetLeft - popupWidth / 2 + $click.width() / 2; positionY = $click[0].offsetTop - popupHeight - space; tailX = positionX + popupWidth / 2 - $tail.width() / 2; tailY = positionY + popupHeight - ($tail.height() / 2); tailRotation = 225; // If popup is outside top edge, position under click instead if (popupHeight + space > $click[0].offsetTop) { positionY = $click[0].offsetTop + $click.height() + space; tailY = positionY - $tail.height() / 2 ; tailRotation = 45; } // If popup is outside left edge, position left if (positionX < 0) { positionX = 0; } // If popup is outside right edge, position right if (positionX + popupWidth > $container.width()) { positionX = $container.width() - popupWidth; } // Special cases such as corner clicks, or close to an edge, they override X and Y positions if met if (clickNearTop && (clickNearLeft || clickNearRight)) { positionX = $click[0].offsetLeft + (clickNearLeft ? $click.width() : -popupWidth); positionY = $click[0].offsetTop + $click.height(); disableTail = true; } else if (clickNearBottom && (clickNearLeft || clickNearRight)) { positionX = $click[0].offsetLeft + (clickNearLeft ? $click.width() : -popupWidth); positionY = $click[0].offsetTop - popupHeight; disableTail = true; } else if (!clickNearTop && !clickNearBottom) { if (clickNearLeft || clickNearRight) { positionY = $click[0].offsetTop - popupHeight / 2 + $click.width() / 2; positionX = $click[0].offsetLeft + (clickNearLeft ? $click.width() + space : -popupWidth + -space); // Make sure this does not position the popup off screen if (positionX < 0) { positionX = 0; disableTail = true; } else { tailX = positionX + (clickNearLeft ? - $tail.width() / 2 : popupWidth - $tail.width() / 2); tailY = positionY + popupHeight / 2 - $tail.height() / 2; tailRotation = (clickNearLeft ? 315 : 135); } } } // Contain popup from overflowing bottom edge if (positionY + popupHeight > $container.height()) { positionY = $container.height() - popupHeight; if (popupHeight > $container.height() - ($click[0].offsetTop + $click.height() + space)) { disableTail = true; } } } else { disableTail = true; } // Contain popup from ovreflowing top edge if (positionY < 0) { positionY = 0; } $element.css({top: positionY, left: positionX}); $tail.css({top: tailY, left: tailX}); if (!disableTail) { $tail.css({ 'left': tailX, 'top': tailY, 'transform': 'rotate(' + tailRotation + 'deg)' }).show(); } else { $tail.hide(); } }; /** * Set element max height, used for animations. * * @param {H5P.jQuery} $element */ var setElementHeight = function ($element) { if (!$element.is(':visible')) { // No animation $element.css('max-height', 'none'); return; } // If this element is shown in the popup, we can't set width to 100%, // since it already has a width set in CSS var isFeedbackPopup = $element.hasClass('h5p-question-popup'); // Get natural element height var $tmp = $element.clone() .css({ 'position': 'absolute', 'max-height': 'none', 'width': isFeedbackPopup ? '' : '100%' }) .appendTo($element.parent()); // Need to take margins into account when calculating available space var sideMargins = parseFloat($element.css('margin-left')) + parseFloat($element.css('margin-right')); var tmpElWidth = $tmp.css('width') ? $tmp.css('width') : '100%'; $tmp.css('width', 'calc(' + tmpElWidth + ' - ' + sideMargins + 'px)'); // Apply height to element var h = Math.round($tmp.get(0).getBoundingClientRect().height); var fontSize = parseFloat($element.css('fontSize')); var relativeH = h / fontSize; $element.css('max-height', relativeH + 'em'); $tmp.remove(); if (h > 0 && sections.buttons && sections.buttons.$element === $element) { // Make sure buttons section is visible showSection(sections.buttons); // Resize buttons after resizing button section setTimeout(resizeButtons, 150); } return h; }; /** * Does the actual job of hiding the buttons scheduled for hiding. * * @private * @param {boolean} [relocateFocus] Find a new button to focus */ var hideButtons = function (relocateFocus) { for (var i = 0; i < buttonsToHide.length; i++) { hideButton(buttonsToHide[i].id); } buttonsToHide = []; if (relocateFocus) { self.focusButton(); } }; /** * Does the actual hiding. * @private * @param {string} buttonId */ var hideButton = function (buttonId) { // Using detach() vs hide() makes it harder to cheat. buttons[buttonId].$element.detach(); buttons[buttonId].isVisible = false; }; /** * Shows the buttons on the next tick. This is to avoid buttons flickering * If they're both added and removed on the same tick. * * @private */ var toggleButtons = function () { // If no buttons section, return if (sections.buttons === undefined) { return; } // Clear transition timer, reevaluate if buttons will be detached clearTimeout(toggleButtonsTransitionTimer); // Show buttons for (var i = 0; i < buttonsToShow.length; i++) { insert(buttonOrder, buttonsToShow[i].id, buttons, sections.buttons.$element); buttons[buttonsToShow[i].id].isVisible = true; } buttonsToShow = []; // Hide buttons var numToHide = 0; var relocateFocus = false; for (var j = 0; j < buttonsToHide.length; j++) { var button = buttons[buttonsToHide[j].id]; if (button.isVisible) { numToHide += 1; } if (button.$element.is(':focus')) { // Move focus to the first visible button. relocateFocus = true; } } var animationTimer = 150; if (sections.feedback && sections.feedback.$element.hasClass('h5p-question-popup')) { animationTimer = 0; } if (numToHide === sections.buttons.$element.children().length) { // All buttons are going to be hidden. Hide container using transition. hideSection(sections.buttons); // Detach buttons hideButtons(relocateFocus); } else { hideButtons(relocateFocus); // Show button section if (!sections.buttons.$element.is(':empty')) { showSection(sections.buttons); setElementHeight(sections.buttons.$element); // Trigger resize after animation toggleButtonsTransitionTimer = setTimeout(function () { self.trigger('resize'); }, animationTimer); } // Resize buttons to fit container resizeButtons(); } toggleButtonsTimer = undefined; }; /** * Allows for scaling of the question image. */ var scaleImage = function () { var $imgSection = sections.image.$element; clearTimeout(imageTransitionTimer); // Add this here to avoid initial transition of the image making // content overflow. Alternatively we need to trigger a resize. $imgSection.addClass('animatable'); if (imageThumb) { // Expand image $(this).attr('aria-expanded', true); $imgSection.addClass('h5p-question-image-fill-width'); imageThumb = false; imageTransitionTimer = setTimeout(function () { self.trigger('resize'); }, 600); } else { // Scale down image $(this).attr('aria-expanded', false); $imgSection.removeClass('h5p-question-image-fill-width'); imageThumb = true; imageTransitionTimer = setTimeout(function () { self.trigger('resize'); }, 600); } }; /** * Get scrollable ancestor of element * * @private * @param {H5P.jQuery} $element * @param {Number} [currDepth=0] Current recursive calls to ancestor, stop at maxDepth * @param {Number} [maxDepth=5] Maximum depth for finding ancestor. * @returns {H5P.jQuery} Parent element that is scrollable */ var findScrollableAncestor = function ($element, currDepth, maxDepth) { if (!currDepth) { currDepth = 0; } if (!maxDepth) { maxDepth = 5; } // Check validation of element or if we have reached document root if (!$element || !($element instanceof $) || document === $element.get(0) || currDepth >= maxDepth) { return; } if ($element.css('overflow-y') === 'auto') { return $element; } else { return findScrollableAncestor($element.parent(), currDepth + 1, maxDepth); } }; /** * Scroll to bottom of Question. * * @private */ var scrollToBottom = function () { if (!$wrapper || ($wrapper.hasClass('h5p-standalone') && !H5P.isFullscreen)) { return; // No scroll } var scrollableAncestor = findScrollableAncestor($wrapper); // Scroll to bottom of scrollable ancestor if (scrollableAncestor) { scrollableAncestor.animate({ scrollTop: $wrapper.css('height') }, "slow"); } }; /** * Resize buttons to fit container width * * @private */ var resizeButtons = function () { if (!buttons || !sections.buttons) { return; } var go = function () { // Don't do anything if button elements are not visible yet if (!sections.buttons.$element.is(':visible')) { return; } // Width of all buttons var buttonsWidth = { max: 0, min: 0, current: 0 }; for (var i in buttons) { var button = buttons[i]; if (button.isVisible) { setButtonWidth(buttons[i]); buttonsWidth.max += button.width.max; buttonsWidth.min += button.width.min; buttonsWidth.current += button.isTruncated ? button.width.min : button.width.max; } } var makeButtonsFit = function (availableWidth) { if (buttonsWidth.max < availableWidth) { // It is room for everyone on the right side of the score bar (without truncating) if (buttonsWidth.max !== buttonsWidth.current) { // Need to make everyone big restoreButtonLabels(buttonsWidth.current, availableWidth); } return true; } else if (buttonsWidth.min < availableWidth) { // Is it room for everyone on the right side of the score bar with truncating? if (buttonsWidth.current > availableWidth) { removeButtonLabels(buttonsWidth.current, availableWidth); } else { restoreButtonLabels(buttonsWidth.current, availableWidth); } return true; } return false; }; toggleFullWidthScorebar(false); var buttonSectionWidth = Math.floor(sections.buttons.$element.width()) - 1; if (!makeButtonsFit(buttonSectionWidth)) { // If we get here we need to wrap: toggleFullWidthScorebar(true); buttonSectionWidth = Math.floor(sections.buttons.$element.width()) - 1; makeButtonsFit(buttonSectionWidth); } }; // If visible, resize right away if (sections.buttons.$element.is(':visible')) { go(); } else { // If not visible, try on the next tick // Clear button truncation timer if within a button truncation function if (buttonTruncationTimer) { clearTimeout(buttonTruncationTimer); } buttonTruncationTimer = setTimeout(function () { buttonTruncationTimer = undefined; go(); }, 0); } }; var toggleFullWidthScorebar = function (enabled) { if (sections.scorebar && sections.scorebar.$element && sections.scorebar.$element.hasClass('h5p-question-visible')) { sections.buttons.$element.addClass('has-scorebar'); sections.buttons.$element.toggleClass('wrap', enabled); sections.scorebar.$element.toggleClass('full-width', enabled); } else { sections.buttons.$element.removeClass('has-scorebar'); } }; /** * Remove button labels until they use less than max width. * * @private * @param {Number} buttonsWidth Total width of all buttons * @param {Number} maxButtonsWidth Max width allowed for buttons */ var removeButtonLabels = function (buttonsWidth, maxButtonsWidth) { // Reverse traversal for (var i = buttonOrder.length - 1; i >= 0; i--) { var buttonId = buttonOrder[i]; var button = buttons[buttonId]; if (!button.isTruncated && button.isVisible) { var $button = button.$element; buttonsWidth -= button.width.max - button.width.min; // Remove label button.$element.attr('aria-label', $button.text()).html('').addClass('truncated'); button.isTruncated = true; if (buttonsWidth <= maxButtonsWidth) { // Buttons are small enough. return; } } } }; /** * Restore button labels until it fills maximum possible width without exceeding the max width. * * @private * @param {Number} buttonsWidth Total width of all buttons * @param {Number} maxButtonsWidth Max width allowed for buttons */ var restoreButtonLabels = function (buttonsWidth, maxButtonsWidth) { for (var i = 0; i < buttonOrder.length; i++) { var buttonId = buttonOrder[i]; var button = buttons[buttonId]; if (button.isTruncated && button.isVisible) { // Calculate new total width of buttons with a static pixel for consistency cross-browser buttonsWidth += button.width.max - button.width.min + 1; if (buttonsWidth > maxButtonsWidth) { return; } // Restore label button.$element.html(button.text); button.$element.removeClass('truncated'); button.isTruncated = false; } } }; /** * Helper function for finding index of keyValue in array * * @param {String} keyValue Value to be found * @param {String} key In key * @param {Array} array In array * @returns {number} */ var existsInArray = function (keyValue, key, array) { var i; for (i = 0; i < array.length; i++) { if (array[i][key] === keyValue) { return i; } } return -1; }; /** * Show a section * @param {Object} section */ var showSection = function (section) { section.$element.addClass('h5p-question-visible'); section.isVisible = true; }; /** * Hide a section * @param {Object} section */ var hideSection = function (section) { section.$element.css('max-height', ''); section.isVisible = false; setTimeout(function () { // Only hide if section hasn't been set to visible in the meantime if (!section.isVisible) { section.$element.removeClass('h5p-question-visible'); } }, 150); }; /** * Set behaviour for question. * * @param {Object} options An object containing behaviour that will be extended by Question */ self.setBehaviour = function (options) { $.extend(behaviour, options); }; /** * A video to display above the task. * * @param {object} params */ self.setVideo = function (params) { sections.video = { $element: $('<div/>', { 'class': 'h5p-question-video' }) }; if (disableAutoPlay && params.params.playback) { params.params.playback.autoplay = false; } // Never fit to wrapper if (!params.params.visuals) { params.params.visuals = {}; } params.params.visuals.fit = false; sections.video.instance = H5P.newRunnable(params, self.contentId, sections.video.$element, true); var fromVideo = false; // Hack to avoid never ending loop sections.video.instance.on('resize', function () { fromVideo = true; self.trigger('resize'); fromVideo = false; }); self.on('resize', function () { if (!fromVideo) { sections.video.instance.trigger('resize'); } }); return self; }; /** * Will stop any playback going on in the task. */ self.pause = function () { if (sections.video && sections.video.isVisible) { sections.video.instance.pause(); } }; /** * Start playback of video */ self.play = function () { if (sections.video && sections.video.isVisible) { sections.video.instance.play(); } }; /** * Disable auto play, useful in editors. */ self.disableAutoPlay = function () { disableAutoPlay = true; }; /** * Add task image. * * @param {string} path Relative * @param {Object} [options] Options object * @param {string} [options.alt] Text representation * @param {string} [options.title] Hover text * @param {Boolean} [options.disableImageZooming] Set as true to disable image zooming */ self.setImage = function (path, options) { options = options ? options : {}; sections.image = {}; // Image container sections.image.$element = $('<div/>', { 'class': 'h5p-question-image h5p-question-image-fill-width' }); // Inner wrap var $imgWrap = $('<div/>', { 'class': 'h5p-question-image-wrap', appendTo: sections.image.$element }); // Image element var $img = $('<img/>', { src: H5P.getPath(path, self.contentId), alt: (options.alt === undefined ? '' : options.alt), title: (options.title === undefined ? '' : options.title), on: { load: function () { self.trigger('imageLoaded', this); self.trigger('resize'); } }, appendTo: $imgWrap }); // Disable image zooming if (options.disableImageZooming) { $img.css('maxHeight', 'none'); // Make sure we are using the correct amount of width at all times var determineImgWidth = function () { // Remove margins if natural image width is bigger than section width var imageSectionWidth = sections.image.$element.get(0).getBoundingClientRect().width; // Do not transition, for instant measurements $imgWrap.css({ '-webkit-transition': 'none', 'transition': 'none' }); // Margin as translateX on both sides of image. var diffX = 2 * ($imgWrap.get(0).getBoundingClientRect().left - sections.image.$element.get(0).getBoundingClientRect().left); if ($img.get(0).naturalWidth >= imageSectionWidth - diffX) { sections.image.$element.addClass('h5p-question-image-fill-width'); } else { // Use margin for small res images sections.image.$element.removeClass('h5p-question-image-fill-width'); } // Reset transition rules $imgWrap.css({ '-webkit-transition': '', 'transition': '' }); }; // Determine image width if ($img.is(':visible')) { determineImgWidth(); } else { $img.on('load', determineImgWidth); } // Skip adding zoom functionality return; } var sizeDetermined = false; var determineSize = function () { if (sizeDetermined || !$img.is(':visible')) { return; // Try again next time. } $imgWrap.addClass('h5p-question-image-scalable') .attr('aria-expanded', false) .attr('role', 'button') .attr('tabIndex', '0') .on('click', function (event) { if (event.which === 1) { scaleImage.apply(this); // Left mouse button click } }).on('keypress', function (event) { if (event.which === 32) { event.preventDefault(); // Prevent default behaviour; page scroll down scaleImage.apply(this); // Space bar pressed } }); sections.image.$element.removeClass('h5p-question-image-fill-width'); sizeDetermined = true; // Prevent any futher events }; self.on('resize', determineSize); return self; }; /** * Add the introduction section. * * @param {(string|H5P.jQuery)} content */ self.setIntroduction = function (content) { register('introduction', content); return self; }; /** * Add the content section. * * @param {(string|H5P.jQuery)} content * @param {Object} [options] * @param {string} [options.class] */ self.setContent = function (content, options) { register('content', content); if (options && options.class) { sections.content.$element.addClass(options.class); } return self; }; /** * Force readspeaker to read text. Useful when you have to use * setTimeout for animations. */ self.read = function (content) { if (!$read) { return; // Not ready yet } if (readText) { // Combine texts if called multiple times readText += (readText.substr(-1, 1) === '.' ? ' ' : '. ') + content; } else { readText = content; } // Set text $read.html(readText); setTimeout(function () { // Stop combining when done reading readText = null; $read.html(''); }, 100); }; /** * Read feedback */ self.readFeedback = function () { var invalidFeedback = behaviour.disableReadSpeaker || !showFeedback || !sections.feedback || !sections.feedback.$element; if (invalidFeedback) { return; } var $feedbackText = $('.h5p-question-feedback-content-text', sections.feedback.$element); if ($feedbackText && $feedbackText.html() && $feedbackText.html().length) { self.read($feedbackText.html()); } }; /** * Remove feedback * * @return {H5P.Question} */ self.removeFeedback = function () { clearTimeout(feedbackTransitionTimer); if (sections.feedback && showFeedback) { showFeedback = false; // Hide feedback & scorebar hideSection(sections.scorebar); hideSection(sections.feedback); sectionsIsTransitioning = true; // Detach after transition feedbackTransitionTimer = setTimeout(function () { // Avoiding Transition.onTransitionEnd since it will register multiple events, and there's no way to cancel it if the transition changes back to "show" while the animation is happening. if (!showFeedback) { sections.feedback.$element.children().detach(); sections.scorebar.$element.children().detach(); // Trigger resize after animation self.trigger('resize'); } sectionsIsTransitioning = false; scoreBar.setScore(0); }, 150); if ($wrapper) { $wrapper.find('.h5p-question-feedback-tail').remove(); } } return self; }; /** * Set feedback message. * * @param {string} [content] * @param {number} score The score * @param {number} maxScore The maximum score for this question * @param {string} [scoreBarLabel] Makes it easier for readspeakers to identify the scorebar * @param {string} [helpText] Help text that describes the score inside a tip icon * @param {object} [popupSettings] Extra settings for popup feedback * @param {boolean} [popupSettings.showAsPopup] Should the feedback display as popup? * @param {string} [popupSettings.closeText] Translation for close button text * @param {object} [popupSettings.click] Element representing where user clicked on screen */ self.setFeedback = function (content, score, maxScore, scoreBarLabel, helpText, popupSettings, scoreExplanationButtonLabel) { // Feedback is disabled if (behaviour.disableFeedback) { return self; } // Need to toggle buttons right away to avoid flickering/blinking // Note: This means content types should invoke hide/showButton before setFeedback toggleButtons(); clickElement = (popupSettings != null && popupSettings.click != null ? popupSettings.click : null); clearTimeout(feedbackTransitionTimer); var $feedback = $('<div>', { 'class': 'h5p-question-feedback-container' }); var $feedbackContent = $('<div>', { 'class': 'h5p-question-feedback-content' }).appendTo($feedback); // Feedback text $('<div>', { 'class': 'h5p-question-feedback-content-text', 'html': content }).appendTo($feedbackContent); var $scorebar = $('<div>', { 'class': 'h5p-question-scorebar-container' }); if (scoreBar === undefined) { scoreBar = JoubelUI.createScoreBar(maxScore, scoreBarLabel, helpText, scoreExplanationButtonLabel); } scoreBar.appendTo($scorebar); $feedbackContent.toggleClass('has-content', content !== undefined && content.length > 0); // Feedback for readspeakers if (!behaviour.disableReadSpeaker && scoreBarLabel) { self.read(scoreBarLabel.replace(':num', score).replace(':total', maxScore) + '. ' + (content ? content : '')); } showFeedback = true; if (sections.feedback) { // Update section update('feedback', $feedback); update('scorebar', $scorebar); } else { // Create section register('feedback', $feedback); register('scorebar', $scorebar); if (initialized && $wrapper) { insert(self.order, 'feedback', sections, $wrapper); insert(self.order, 'scorebar', sections, $wrapper); } } showSection(sections.feedback); showSection(sections.scorebar); resizeButtons(); if (popupSettings != null && popupSettings.showAsPopup == true) { makeFeedbackPopup(popupSettings.closeText); scoreBar.setScore(score); } else { // Show feedback section feedbackTransitionTimer = setTimeout(function () { setElementHeight(sections.feedback.$element); setElementHeight(sections.scorebar.$element); sectionsIsTransitioning = true; // Scroll to bottom after showing feedback scrollToBottom(); // Trigger resize after animation feedbackTransitionTimer = setTimeout(function () { sectionsIsTransitioning = false; self.trigger('resize'); scoreBar.setScore(score); }, 150); }, 0); } return self; }; /** * Set feedback content (no animation). * * @param {string} content * @param {boolean} [extendContent] True will extend content, instead of replacing it */ self.updateFeedbackContent = function (content, extendContent) { if (sections.feedback && sections.feedback.$element) { if (extendContent) { content = $('.h5p-question-feedback-content', sections.feedback.$element).html() + ' ' + content; } // Update feedback content html $('.h5p-question-feedback-content', sections.feedback.$element).html(content).addClass('has-content'); // Make sure the height is correct setElementHeight(sections.feedback.$element); // Need to trigger resize when feedback has finished transitioning setTimeout(self.trigger.bind(self, 'resize'), 150); } return self; }; /** * Set the content of the explanation / feedback panel * * @param {Object} data * @param {string} data.correct * @param {string} data.wrong * @param {string} data.text * @param {string} title Title for explanation panel * * @return {H5P.Question} */ self.setExplanation = function (data, title) { if (data) { var explainer = new H5P.Question.Explainer(title, data); if (sections.explanation) { // Update section update('explanation', explainer.getElement()); } else { register('explanation', explainer.getElement()); if (initialized && $wrapper) { insert(self.order, 'explanation', sections, $wrapper); } } } else if (sections.explanation) { // Hide explanation section sections.explanation.$element.children().detach(); } return self; }; /** * Checks to see if button is registered. * * @param {string} id * @returns {boolean} */ self.hasButton = function (id) { return (buttons[id] !== undefined); }; /** * @typedef {Object} ConfirmationDialog * @property {boolean} [enable] Must be true to show confirmation dialog * @property {Object} [instance] Instance that uses confirmation dialog * @property {jQuery} [$parentElement] Append to this element. * @property {Object} [l10n] Translatable fields * @property {string} [l10n.header] Header text * @property {string} [l10n.body] Body text * @property {string} [l10n.cancelLabel] * @property {string} [l10n.confirmLabel] */ /** * Register buttons for the task. * * @param {string} id * @param {string} text label * @param {function} clicked * @param {boolean} [visible=true] * @param {Object} [options] Options for button * @param {Object} [extras] Extra options * @param {ConfirmationDialog} [extras.confirmationDialog] Confirmation dialog */ self.addButton = function (id, text, clicked, visible, options, extras) { if (buttons[id]) { return self; // Already registered } if (sections.buttons === undefined) { // We have buttons, register wrapper register('buttons'); if (initialized) { insert(self.order, 'buttons', sections, $wrapper); } } extras = extras || {}; extras.confirmationDialog = extras.confirmationDialog || {}; options = options || {}; var confirmationDialog = self.addConfirmationDialogToButton(extras.confirmationDialog, clicked); /** * Handle button clicks through both mouse and keyboard * @private */ var handleButtonClick = function () { if (extras.confirmationDialog.enable && confirmationDialog) { // Show popups section if used if (!extras.confirmationDialog.$parentElement) { sections.popups.$element.removeClass('hidden'); } confirmationDialog.show($e.position().top); } else { clicked(); } }; buttons[id] = { isTruncated: false, text: text, isVisible: false }; // The button might be <button> or <a> // (dependent on options.href set or not) var isAnchorTag = (options.href !== undefined); var $e = buttons[id].$element = JoubelUI.createButton($.extend({ 'class': 'h5p-question-' + id, html: text, title: text, on: { click: function (event) { handleButtonClick(); if (isAnchorTag) { event.preventDefault(); } } } }, options)); buttonOrder.push(id); // The button might be <button> or <a>. If <a>, the space key is not // triggering the click event, must therefore handle this here: if (isAnchorTag) { $e.on('keypress', function (event) { if (event.which === 32) { // Space handleButtonClick(); event.preventDefault(); } }); } if (visible === undefined || visible) { // Button should be visible $e.appendTo(sections.buttons.$element); buttons[id].isVisible = true; showSection(sections.buttons); } return self; }; var setButtonWidth = function (button) { var $button = button.$element; var $tmp = $button.clone() .css({ 'position': 'absolute', 'white-space': 'nowrap', 'max-width': 'none' }).removeClass('truncated') .html(button.text) .appendTo($button.parent()); // Calculate max width (button including text) button.width = { max: Math.ceil($tmp.outerWidth() + parseFloat($tmp.css('margin-left')) + parseFloat($tmp.css('margin-right'))) }; // Calculate min width (truncated, icon only) $tmp.html('').addClass('truncated'); button.width.min = Math.ceil($tmp.outerWidth() + parseFloat($tmp.css('margin-left')) + parseFloat($tmp.css('margin-right'))); $tmp.remove(); }; /** * Add confirmation dialog to button * @param {ConfirmationDialog} options * A confirmation dialog that will be shown before click handler of button * is triggered * @param {function} clicked * Click handler of button * @return {H5P.ConfirmationDialog|undefined} * Confirmation dialog if enabled */ self.addConfirmationDialogToButton = function (options, clicked) { options = options || {}; if (!options.enable) { return; } // Confirmation dialog var confirmationDialog = new H5P.ConfirmationDialog({ instance: options.instance, headerText: options.l10n.header, dialogText: options.l10n.body, cancelText: options.l10n.cancelLabel, confirmText: options.l10n.confirmLabel }); // Determine parent element if (options.$parentElement) { confirmationDialog.appendTo(options.$parentElement.get(0)); } else { // Create popup section and append to that if (sections.popups === undefined) { register('popups'); if (initialized) { insert(self.order, 'popups', sections, $wrapper); } sections.popups.$element.addClass('hidden'); self.order.push('popups'); } confirmationDialog.appendTo(sections.popups.$element.get(0)); } // Add event listeners confirmationDialog.on('confirmed', function () { if (!options.$parentElement) { sections.popups.$element.addClass('hidden'); } clicked(); // Trigger to content type self.trigger('confirmed'); }); confirmationDialog.on('canceled', function () { if (!options.$parentElement) { sections.popups.$element.addClass('hidden'); } // Trigger to content type self.trigger('canceled'); }); return confirmationDialog; }; /** * Show registered button with given identifier. * * @param {string} id * @param {Number} [priority] */ self.showButton = function (id, priority) { var aboutToBeHidden = existsInArray(id, 'id', buttonsToHide) !== -1; if (buttons[id] === undefined || (buttons[id].isVisible === true && !aboutToBeHidden)) { return self; } priority = priority || 0; // Skip if already being shown var indexToShow = existsInArray(id, 'id', buttonsToShow); if (indexToShow !== -1) { // Update priority if (buttonsToShow[indexToShow].priority < priority) { buttonsToShow[indexToShow].priority = priority; } return self; } // Check if button is going to be hidden on next tick var exists = existsInArray(id, 'id', buttonsToHide); if (exists !== -1) { // Skip hiding if higher priority if (buttonsToHide[exists].priority <= priority) { buttonsToHide.splice(exists, 1); buttonsToShow.push({id: id, priority: priority}); } } // If button is not shown else if (!buttons[id].$element.is(':visible')) { // Show button on next tick buttonsToShow.push({id: id, priority: priority}); } if (!toggleButtonsTimer) { toggleButtonsTimer = setTimeout(toggleButtons, 0); } return self; }; /** * Hide registered button with given identifier. * * @param {string} id * @param {number} [priority] */ self.hideButton = function (id, priority) { var aboutToBeShown = existsInArray(id, 'id', buttonsToShow) !== -1; if (buttons[id] === undefined || (buttons[id].isVisible === false && !aboutToBeShown)) { return self; } priority = priority || 0; // Skip if already being hidden var indexToHide = existsInArray(id, 'id', buttonsToHide); if (indexToHide !== -1) { // Update priority if (buttonsToHide[indexToHide].priority < priority) { buttonsToHide[indexToHide].priority = priority; } return self; } // Check if buttons is going to be shown on next tick var exists = existsInArray(id, 'id', buttonsToShow); if (exists !== -1) { // Skip showing if higher priority if (buttonsToShow[exists].priority <= priority) { buttonsToShow.splice(exists, 1); buttonsToHide.push({id: id, priority: priority}); } } else if (!buttons[id].$element.is(':visible')) { // Make sure it is detached in case the container is hidden. hideButton(id); } else { // Hide button on next tick. buttonsToHide.push({id: id, priority: priority}); } if (!toggleButtonsTimer) { toggleButtonsTimer = setTimeout(toggleButtons, 0); } return self; }; /** * Set focus to the given button. If no button is given the first visible * button gets focused. This is useful if you lose focus. * * @param {string} [id] */ self.focusButton = function (id) { if (id === undefined) { // Find first button that is visible. for (var i = 0; i < buttonOrder.length; i++) { var button = buttons[buttonOrder[i]]; if (button && button.isVisible) { // Give that button focus button.$element.focus(); break; } } } else if (buttons[id] && buttons[id].$element.is(':visible')) { // Set focus to requested button buttons[id].$element.focus(); } return self; }; /** * Toggle readspeaker functionality * @param {boolean} [disable] True to disable, false to enable. */ self.toggleReadSpeaker = function (disable) { behaviour.disableReadSpeaker = disable || !behaviour.disableReadSpeaker; }; /** * Set new element for section. * * @param {String} id * @param {H5P.jQuery} $element */ self.insertSectionAtElement = function (id, $element) { if (sections[id] === undefined) { register(id); } sections[id].parent = $element; // Insert section if question is not initialized if (!initialized) { insert([id], id, sections, $element); } return self; }; /** * Attach content to given container. * * @param {H5P.jQuery} $container */ self.attach = function ($container) { if (self.isRoot()) { self.setActivityStarted(); } // The first time we attach we also create our DOM elements. if ($wrapper === undefined) { if (self.registerDomElements !== undefined && (self.registerDomElements instanceof Function || typeof self.registerDomElements === 'function')) { // Give the question type a chance to register before attaching self.registerDomElements(); } // Create section for reading messages $read = $('<div/>', { 'aria-live': 'polite', 'class': 'h5p-hidden-read' }); register('read', $read); self.trigger('registerDomElements'); } // Prepare container $wrapper = $container; $container.html('') .addClass('h5p-question h5p-' + type); // Add sections in given order var $sections = []; for (var i = 0; i < self.order.length; i++) { var section = self.order[i]; if (sections[section]) { if (sections[section].parent) { // Section has a different parent sections[section].$element.appendTo(sections[section].parent); } else { $sections.push(sections[section].$element); } sections[section].isVisible = true; } } // Only append once to DOM for optimal performance $container.append($sections); // Let others react to dom changes self.trigger('domChanged', { '$target': $container, 'library': self.libraryInfo.machineName, 'contentId': self.contentId, 'key': 'newLibrary' }, {'bubbles': true, 'external': true}); // ?? initialized = true; return self; }; /** * Detach all sections from their parents */ self.detachSections = function () { // Deinit Question initialized = false; // Detach sections for (var section in sections) { sections[section].$element.detach(); } return self; }; // Listen for resize self.on('resize', function () { // Allow elements to attach and set their height before resizing if (!sectionsIsTransitioning && sections.feedback && showFeedback) { // Resize feedback to fit setElementHeight(sections.feedback.$element); } // Re-position feedback popup if in use var $element = sections.feedback; var $click = clickElement; if ($element != null && $element.$element != null && $click != null && $click.$element != null) { setTimeout(function () { positionFeedbackPopup($element.$element, $click.$element); }, 10); } resizeButtons(); }); } // Inheritance Question.prototype = Object.create(EventDispatcher.prototype); Question.prototype.constructor = Question; /** * Determine the overall feedback to display for the question. * Returns empty string if no matching range is found. * * @param {Object[]} feedbacks * @param {number} scoreRatio * @return {string} */ Question.determineOverallFeedback = function (feedbacks, scoreRatio) { scoreRatio = Math.floor(scoreRatio * 100); for (var i = 0; i < feedbacks.length; i++) { var feedback = feedbacks[i]; var hasFeedback = (feedback.feedback !== undefined && feedback.feedback.trim().length !== 0); if (feedback.from <= scoreRatio && feedback.to >= scoreRatio && hasFeedback) { return feedback.feedback; } } return ''; }; return Question; })(H5P.jQuery, H5P.EventDispatcher, H5P.JoubelUI); ; H5P.Question.Explainer = (function ($) { /** * Constructor * * @class * @param {string} title * @param {array} explanations */ function Explainer(title, explanations) { var self = this; /** * Create the DOM structure */ var createHTML = function () { self.$explanation = $('<div>', { 'class': 'h5p-question-explanation-container' }); // Add title: $('<div>', { 'class': 'h5p-question-explanation-title', role: 'heading', html: title, appendTo: self.$explanation }); var $explanationList = $('<ul>', { 'class': 'h5p-question-explanation-list', appendTo: self.$explanation }); for (var i = 0; i < explanations.length; i++) { var feedback = explanations[i]; var $explanationItem = $('<li>', { 'class': 'h5p-question-explanation-item', appendTo: $explanationList }); var $content = $('<div>', { 'class': 'h5p-question-explanation-status' }); if (feedback.correct) { $('<span>', { 'class': 'h5p-question-explanation-correct', html: feedback.correct, appendTo: $content }); } if (feedback.wrong) { $('<span>', { 'class': 'h5p-question-explanation-wrong', html: feedback.wrong, appendTo: $content }); } $content.appendTo($explanationItem); if (feedback.text) { $('<div>', { 'class': 'h5p-question-explanation-text', html: feedback.text, appendTo: $explanationItem }); } } }; createHTML(); /** * Return the container HTMLElement * * @return {HTMLElement} */ self.getElement = function () { return self.$explanation; }; } return Explainer; })(H5P.jQuery); ; (function (Question) { /** * Makes it easy to add animated score points for your question type. * * @class H5P.Question.ScorePoints */ Question.ScorePoints = function () { var self = this; var elements = []; var showElementsTimer; /** * Create the element that displays the score point element for questions. * * @param {boolean} isCorrect * @return {HTMLElement} */ self.getElement = function (isCorrect) { var element = document.createElement('div'); element.classList.add(isCorrect ? 'h5p-question-plus-one' : 'h5p-question-minus-one'); element.classList.add('h5p-question-hidden-one'); elements.push(element); // Schedule display animation of all added elements if (showElementsTimer) { clearTimeout(showElementsTimer); } showElementsTimer = setTimeout(showElements, 0); return element; }; /** * @private */ var showElements = function () { // Determine delay between triggering animations var delay = 0; var increment = 150; var maxTime = 1000; if (elements.length && elements.length > Math.ceil(maxTime / increment)) { // Animations will run for more than ~1 second, reduce it. increment = maxTime / elements.length; } for (var i = 0; i < elements.length; i++) { // Use timer to trigger show setTimeout(showElement(elements[i]), delay); // Increse delay for next element delay += increment; } }; /** * Trigger transition animation for the given element * * @private * @param {HTMLElement} element * @return {function} */ var showElement = function (element) { return function () { element.classList.remove('h5p-question-hidden-one'); }; }; }; })(H5P.Question); ; (function(){var rsplit=function(string,regex){var result=regex.exec(string),retArr=new Array(),first_idx,last_idx,first_bit;while(result!=null){first_idx=result.index;last_idx=regex.lastIndex;if((first_idx)!=0){first_bit=string.substring(0,first_idx);retArr.push(string.substring(0,first_idx));string=string.slice(first_idx)}retArr.push(result[0]);string=string.slice(result[0].length);result=regex.exec(string)}if(!string==""){retArr.push(string)}return retArr},chop=function(string){return string.substr(0,string.length-1)},extend=function(d,s){for(var n in s){if(s.hasOwnProperty(n)){d[n]=s[n]}}};EJS=function(options){options=typeof options=="string"?{view:options}:options;this.set_options(options);if(options.precompiled){this.template={};this.template.process=options.precompiled;EJS.update(this.name,this);return }if(options.element){if(typeof options.element=="string"){var name=options.element;options.element=document.getElementById(options.element);if(options.element==null){throw name+"does not exist!"}}if(options.element.value){this.text=options.element.value}else{this.text=options.element.innerHTML}this.name=options.element.id;this.type="["}else{if(options.url){options.url=EJS.endExt(options.url,this.extMatch);this.name=this.name?this.name:options.url;var url=options.url;var template=EJS.get(this.name,this.cache);if(template){return template}if(template==EJS.INVALID_PATH){return null}try{this.text=EJS.request(url+(this.cache?"":"?"+Math.random()))}catch(e){}if(this.text==null){throw ({type:"EJS",message:"There is no template at "+url})}}}var template=new EJS.Compiler(this.text,this.type);template.compile(options,this.name);EJS.update(this.name,this);this.template=template};EJS.prototype={render:function(object,extra_helpers){object=object||{};this._extra_helpers=extra_helpers;var v=new EJS.Helpers(object,extra_helpers||{});return this.template.process.call(object,object,v)},update:function(element,options){if(typeof element=="string"){element=document.getElementById(element)}if(options==null){_template=this;return function(object){EJS.prototype.update.call(_template,element,object)}}if(typeof options=="string"){params={};params.url=options;_template=this;params.onComplete=function(request){var object=eval(request.responseText);EJS.prototype.update.call(_template,element,object)};EJS.ajax_request(params)}else{element.innerHTML=this.render(options)}},out:function(){return this.template.out},set_options:function(options){this.type=options.type||EJS.type;this.cache=options.cache!=null?options.cache:EJS.cache;this.text=options.text||null;this.name=options.name||null;this.ext=options.ext||EJS.ext;this.extMatch=new RegExp(this.ext.replace(/\./,"."))}};EJS.endExt=function(path,match){if(!path){return null}match.lastIndex=0;return path+(match.test(path)?"":this.ext)};EJS.Scanner=function(source,left,right){extend(this,{left_delimiter:left+"%",right_delimiter:"%"+right,double_left:left+"%%",double_right:"%%"+right,left_equal:left+"%=",left_comment:left+"%#"});this.SplitRegexp=left=="["?/(\[%%)|(%%\])|(\[%=)|(\[%#)|(\[%)|(%\]\n)|(%\])|(\n)/:new RegExp("("+this.double_left+")|(%%"+this.double_right+")|("+this.left_equal+")|("+this.left_comment+")|("+this.left_delimiter+")|("+this.right_delimiter+"\n)|("+this.right_delimiter+")|(\n)");this.source=source;this.stag=null;this.lines=0};EJS.Scanner.to_text=function(input){if(input==null||input===undefined){return""}if(input instanceof Date){return input.toDateString()}if(input.toString){return input.toString()}return""};EJS.Scanner.prototype={scan:function(block){scanline=this.scanline;regex=this.SplitRegexp;if(!this.source==""){var source_split=rsplit(this.source,/\n/);for(var i=0;i<source_split.length;i++){var item=source_split[i];this.scanline(item,regex,block)}}},scanline:function(line,regex,block){this.lines++;var line_split=rsplit(line,regex);for(var i=0;i<line_split.length;i++){var token=line_split[i];if(token!=null){try{block(token,this)}catch(e){throw {type:"EJS.Scanner",line:this.lines}}}}}};EJS.Buffer=function(pre_cmd,post_cmd){this.line=new Array();this.script="";this.pre_cmd=pre_cmd;this.post_cmd=post_cmd;for(var i=0;i<this.pre_cmd.length;i++){this.push(pre_cmd[i])}};EJS.Buffer.prototype={push:function(cmd){this.line.push(cmd)},cr:function(){this.script=this.script+this.line.join("; ");this.line=new Array();this.script=this.script+"\n"},close:function(){if(this.line.length>0){for(var i=0;i<this.post_cmd.length;i++){this.push(pre_cmd[i])}this.script=this.script+this.line.join("; ");line=null}}};EJS.Compiler=function(source,left){this.pre_cmd=["var ___ViewO = [];"];this.post_cmd=new Array();this.source=" ";if(source!=null){if(typeof source=="string"){source=source.replace(/\r\n/g,"\n");source=source.replace(/\r/g,"\n");this.source=source}else{if(source.innerHTML){this.source=source.innerHTML}}if(typeof this.source!="string"){this.source=""}}left=left||"<";var right=">";switch(left){case"[":right="]";break;case"<":break;default:throw left+" is not a supported deliminator";break}this.scanner=new EJS.Scanner(this.source,left,right);this.out=""};EJS.Compiler.prototype={compile:function(options,name){options=options||{};this.out="";var put_cmd="___ViewO.push(";var insert_cmd=put_cmd;var buff=new EJS.Buffer(this.pre_cmd,this.post_cmd);var content="";var clean=function(content){content=content.replace(/\\/g,"\\\\");content=content.replace(/\n/g,"\\n");content=content.replace(/"/g,'\\"');return content};this.scanner.scan(function(token,scanner){if(scanner.stag==null){switch(token){case"\n":content=content+"\n";buff.push(put_cmd+'"'+clean(content)+'");');buff.cr();content="";break;case scanner.left_delimiter:case scanner.left_equal:case scanner.left_comment:scanner.stag=token;if(content.length>0){buff.push(put_cmd+'"'+clean(content)+'")')}content="";break;case scanner.double_left:content=content+scanner.left_delimiter;break;default:content=content+token;break}}else{switch(token){case scanner.right_delimiter:switch(scanner.stag){case scanner.left_delimiter:if(content[content.length-1]=="\n"){content=chop(content);buff.push(content);buff.cr()}else{buff.push(content)}break;case scanner.left_equal:buff.push(insert_cmd+"(EJS.Scanner.to_text("+content+")))");break}scanner.stag=null;content="";break;case scanner.double_right:content=content+scanner.right_delimiter;break;default:content=content+token;break}}});if(content.length>0){buff.push(put_cmd+'"'+clean(content)+'")')}buff.close();this.out=buff.script+";";var to_be_evaled="/*"+name+"*/this.process = function(_CONTEXT,_VIEW) { try { with(_VIEW) { with (_CONTEXT) {"+this.out+" return ___ViewO.join('');}}}catch(e){e.lineNumber=null;throw e;}};";try{eval(to_be_evaled)}catch(e){if(typeof JSLINT!="undefined"){JSLINT(this.out);for(var i=0;i<JSLINT.errors.length;i++){var error=JSLINT.errors[i];if(error.reason!="Unnecessary semicolon."){error.line++;var e=new Error();e.lineNumber=error.line;e.message=error.reason;if(options.view){e.fileName=options.view}throw e}}}else{throw e}}}};EJS.config=function(options){EJS.cache=options.cache!=null?options.cache:EJS.cache;EJS.type=options.type!=null?options.type:EJS.type;EJS.ext=options.ext!=null?options.ext:EJS.ext;var templates_directory=EJS.templates_directory||{};EJS.templates_directory=templates_directory;EJS.get=function(path,cache){if(cache==false){return null}if(templates_directory[path]){return templates_directory[path]}return null};EJS.update=function(path,template){if(path==null){return }templates_directory[path]=template};EJS.INVALID_PATH=-1};EJS.config({cache:true,type:"<",ext:".ejs"});EJS.Helpers=function(data,extras){this._data=data;this._extras=extras;extend(this,extras)};EJS.Helpers.prototype={view:function(options,data,helpers){if(!helpers){helpers=this._extras}if(!data){data=this._data}return new EJS(options).render(data,helpers)},to_text:function(input,null_text){if(input==null||input===undefined){return null_text||""}if(input instanceof Date){return input.toDateString()}if(input.toString){return input.toString().replace(/\n/g,"<br />").replace(/''/g,"'")}return""}};EJS.newRequest=function(){var factories=[function(){return new ActiveXObject("Msxml2.XMLHTTP")},function(){return new XMLHttpRequest()},function(){return new ActiveXObject("Microsoft.XMLHTTP")}];for(var i=0;i<factories.length;i++){try{var request=factories[i]();if(request!=null){return request}}catch(e){continue}}};EJS.request=function(path){var request=new EJS.newRequest();request.open("GET",path,false);try{request.send(null)}catch(e){return null}if(request.status==404||request.status==2||(request.status==0&&request.responseText=="")){return null}return request.responseText};EJS.ajax_request=function(params){params.method=(params.method?params.method:"GET");var request=new EJS.newRequest();request.onreadystatechange=function(){if(request.readyState==4){if(request.status==200){params.onComplete(request)}else{params.onComplete(request)}}};request.open(params.method,params.url);request.send(null)}})();EJS.Helpers.prototype.date_tag=function(C,O,A){if(!(O instanceof Date)){O=new Date()}var B=["January","February","March","April","May","June","July","August","September","October","November","December"];var G=[],D=[],P=[];var J=O.getFullYear();var H=O.getMonth();var N=O.getDate();for(var M=J-15;M<J+15;M++){G.push({value:M,text:M})}for(var E=0;E<12;E++){D.push({value:(E),text:B[E]})}for(var I=0;I<31;I++){P.push({value:(I+1),text:(I+1)})}var L=this.select_tag(C+"[year]",J,G,{id:C+"[year]"});var F=this.select_tag(C+"[month]",H,D,{id:C+"[month]"});var K=this.select_tag(C+"[day]",N,P,{id:C+"[day]"});return L+F+K};EJS.Helpers.prototype.form_tag=function(B,A){A=A||{};A.action=B;if(A.multipart==true){A.method="post";A.enctype="multipart/form-data"}return this.start_tag_for("form",A)};EJS.Helpers.prototype.form_tag_end=function(){return this.tag_end("form")};EJS.Helpers.prototype.hidden_field_tag=function(A,C,B){return this.input_field_tag(A,C,"hidden",B)};EJS.Helpers.prototype.input_field_tag=function(A,D,C,B){B=B||{};B.id=B.id||A;B.value=D||"";B.type=C||"text";B.name=A;return this.single_tag_for("input",B)};EJS.Helpers.prototype.is_current_page=function(A){return(window.location.href==A||window.location.pathname==A?true:false)};EJS.Helpers.prototype.link_to=function(B,A,C){if(!B){var B="null"}if(!C){var C={}}if(C.confirm){C.onclick=' var ret_confirm = confirm("'+C.confirm+'"); if(!ret_confirm){ return false;} ';C.confirm=null}C.href=A;return this.start_tag_for("a",C)+B+this.tag_end("a")};EJS.Helpers.prototype.submit_link_to=function(B,A,C){if(!B){var B="null"}if(!C){var C={}}C.onclick=C.onclick||"";if(C.confirm){C.onclick=' var ret_confirm = confirm("'+C.confirm+'"); if(!ret_confirm){ return false;} ';C.confirm=null}C.value=B;C.type="submit";C.onclick=C.onclick+(A?this.url_for(A):"")+"return false;";return this.start_tag_for("input",C)};EJS.Helpers.prototype.link_to_if=function(F,B,A,D,C,E){return this.link_to_unless((F==false),B,A,D,C,E)};EJS.Helpers.prototype.link_to_unless=function(E,B,A,C,D){C=C||{};if(E){if(D&&typeof D=="function"){return D(B,A,C,D)}else{return B}}else{return this.link_to(B,A,C)}};EJS.Helpers.prototype.link_to_unless_current=function(B,A,C,D){C=C||{};return this.link_to_unless(this.is_current_page(A),B,A,C,D)};EJS.Helpers.prototype.password_field_tag=function(A,C,B){return this.input_field_tag(A,C,"password",B)};EJS.Helpers.prototype.select_tag=function(D,G,H,F){F=F||{};F.id=F.id||D;F.value=G;F.name=D;var B="";B+=this.start_tag_for("select",F);for(var E=0;E<H.length;E++){var C=H[E];var A={value:C.value};if(C.value==G){A.selected="selected"}B+=this.start_tag_for("option",A)+C.text+this.tag_end("option")}B+=this.tag_end("select");return B};EJS.Helpers.prototype.single_tag_for=function(A,B){return this.tag(A,B,"/>")};EJS.Helpers.prototype.start_tag_for=function(A,B){return this.tag(A,B)};EJS.Helpers.prototype.submit_tag=function(A,B){B=B||{};B.type=B.type||"submit";B.value=A||"Submit";return this.single_tag_for("input",B)};EJS.Helpers.prototype.tag=function(C,E,D){if(!D){var D=">"}var B=" ";for(var A in E){if(E[A]!=null){var F=E[A].toString()}else{var F=""}if(A=="Class"){A="class"}if(F.indexOf("'")!=-1){B+=A+'="'+F+'" '}else{B+=A+"='"+F+"' "}}return"<"+C+B+D};EJS.Helpers.prototype.tag_end=function(A){return"</"+A+">"};EJS.Helpers.prototype.text_area_tag=function(A,C,B){B=B||{};B.id=B.id||A;B.name=B.name||A;C=C||"";if(B.size){B.cols=B.size.split("x")[0];B.rows=B.size.split("x")[1];delete B.size}B.cols=B.cols||50;B.rows=B.rows||4;return this.start_tag_for("textarea",B)+C+this.tag_end("textarea")};EJS.Helpers.prototype.text_tag=EJS.Helpers.prototype.text_area_tag;EJS.Helpers.prototype.text_field_tag=function(A,C,B){return this.input_field_tag(A,C,"text",B)};EJS.Helpers.prototype.url_for=function(A){return'window.location="'+A+'";'};EJS.Helpers.prototype.img_tag=function(B,C,A){A=A||{};A.src=B;A.alt=C;return this.single_tag_for("img",A)}; EJS.Helpers.prototype.date_tag = function(name, value , html_options) { if(! (value instanceof Date)) value = new Date() var month_names = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]; var years = [], months = [], days =[]; var year = value.getFullYear(); var month = value.getMonth(); var day = value.getDate(); for(var y = year - 15; y < year+15 ; y++) { years.push({value: y, text: y}) } for(var m = 0; m < 12; m++) { months.push({value: (m), text: month_names[m]}) } for(var d = 0; d < 31; d++) { days.push({value: (d+1), text: (d+1)}) } var year_select = this.select_tag(name+'[year]', year, years, {id: name+'[year]'} ) var month_select = this.select_tag(name+'[month]', month, months, {id: name+'[month]'}) var day_select = this.select_tag(name+'[day]', day, days, {id: name+'[day]'}) return year_select+month_select+day_select; } EJS.Helpers.prototype.form_tag = function(action, html_options) { html_options = html_options || {}; html_options.action = action if(html_options.multipart == true) { html_options.method = 'post'; html_options.enctype = 'multipart/form-data'; } return this.start_tag_for('form', html_options) } EJS.Helpers.prototype.form_tag_end = function() { return this.tag_end('form'); } EJS.Helpers.prototype.hidden_field_tag = function(name, value, html_options) { return this.input_field_tag(name, value, 'hidden', html_options); } EJS.Helpers.prototype.input_field_tag = function(name, value , inputType, html_options) { html_options = html_options || {}; html_options.id = html_options.id || name; html_options.value = value || ''; html_options.type = inputType || 'text'; html_options.name = name; return this.single_tag_for('input', html_options) } EJS.Helpers.prototype.is_current_page = function(url) { return (window.location.href == url || window.location.pathname == url ? true : false); } EJS.Helpers.prototype.link_to = function(name, url, html_options) { if(!name) var name = 'null'; if(!html_options) var html_options = {} if(html_options.confirm){ html_options.onclick = " var ret_confirm = confirm(\""+html_options.confirm+"\"); if(!ret_confirm){ return false;} " html_options.confirm = null; } html_options.href=url return this.start_tag_for('a', html_options)+name+ this.tag_end('a'); } EJS.Helpers.prototype.submit_link_to = function(name, url, html_options){ if(!name) var name = 'null'; if(!html_options) var html_options = {} html_options.onclick = html_options.onclick || '' ; if(html_options.confirm){ html_options.onclick = " var ret_confirm = confirm(\""+html_options.confirm+"\"); if(!ret_confirm){ return false;} " html_options.confirm = null; } html_options.value = name; html_options.type = 'submit' html_options.onclick=html_options.onclick+ (url ? this.url_for(url) : '')+'return false;'; //html_options.href='#'+(options ? Routes.url_for(options) : '') return this.start_tag_for('input', html_options) } EJS.Helpers.prototype.link_to_if = function(condition, name, url, html_options, post, block) { return this.link_to_unless((condition == false), name, url, html_options, post, block); } EJS.Helpers.prototype.link_to_unless = function(condition, name, url, html_options, block) { html_options = html_options || {}; if(condition) { if(block && typeof block == 'function') { return block(name, url, html_options, block); } else { return name; } } else return this.link_to(name, url, html_options); } EJS.Helpers.prototype.link_to_unless_current = function(name, url, html_options, block) { html_options = html_options || {}; return this.link_to_unless(this.is_current_page(url), name, url, html_options, block) } EJS.Helpers.prototype.password_field_tag = function(name, value, html_options) { return this.input_field_tag(name, value, 'password', html_options); } EJS.Helpers.prototype.select_tag = function(name, value, choices, html_options) { html_options = html_options || {}; html_options.id = html_options.id || name; html_options.value = value; html_options.name = name; var txt = '' txt += this.start_tag_for('select', html_options) for(var i = 0; i < choices.length; i++) { var choice = choices[i]; var optionOptions = {value: choice.value} if(choice.value == value) optionOptions.selected ='selected' txt += this.start_tag_for('option', optionOptions )+choice.text+this.tag_end('option') } txt += this.tag_end('select'); return txt; } EJS.Helpers.prototype.single_tag_for = function(tag, html_options) { return this.tag(tag, html_options, '/>');} EJS.Helpers.prototype.start_tag_for = function(tag, html_options) { return this.tag(tag, html_options); } EJS.Helpers.prototype.submit_tag = function(name, html_options) { html_options = html_options || {}; //html_options.name = html_options.id || 'commit'; html_options.type = html_options.type || 'submit'; html_options.value = name || 'Submit'; return this.single_tag_for('input', html_options); } EJS.Helpers.prototype.tag = function(tag, html_options, end) { if(!end) var end = '>' var txt = ' ' for(var attr in html_options) { if(html_options[attr] != null) var value = html_options[attr].toString(); else var value='' if(attr == "Class") // special case because "class" is a reserved word in IE attr = "class"; if( value.indexOf("'") != -1 ) txt += attr+'=\"'+value+'\" ' else txt += attr+"='"+value+"' " } return '<'+tag+txt+end; } EJS.Helpers.prototype.tag_end = function(tag) { return '</'+tag+'>'; } EJS.Helpers.prototype.text_area_tag = function(name, value, html_options) { html_options = html_options || {}; html_options.id = html_options.id || name; html_options.name = html_options.name || name; value = value || '' if(html_options.size) { html_options.cols = html_options.size.split('x')[0] html_options.rows = html_options.size.split('x')[1]; delete html_options.size } html_options.cols = html_options.cols || 50; html_options.rows = html_options.rows || 4; return this.start_tag_for('textarea', html_options)+value+this.tag_end('textarea') } EJS.Helpers.prototype.text_tag = EJS.Helpers.prototype.text_area_tag EJS.Helpers.prototype.text_field_tag = function(name, value, html_options) { return this.input_field_tag(name, value, 'text', html_options); } EJS.Helpers.prototype.url_for = function(url) { return 'window.location="'+url+'";' } EJS.Helpers.prototype.img_tag = function(image_location, alt, options){ options = options || {}; options.src = image_location options.alt = alt return this.single_tag_for('img', options) } ; /*global EJS*/ // Will render a Question with multiple choices for answers. // Options format: // { // title: "Optional title for question box", // question: "Question text", // answers: [{text: "Answer text", correct: false}, ...], // singleAnswer: true, // or false, will change rendered output slightly. // singlePoint: true, // True if question give a single point score only // // if all are correct, false to give 1 point per // // correct answer. (Only for singleAnswer=false) // randomAnswers: false // Whether to randomize the order of answers. // } // // Events provided: // - h5pQuestionAnswered: Triggered when a question has been answered. var H5P = H5P || {}; /** * @typedef {Object} Options * Options for multiple choice * * @property {Object} behaviour * @property {boolean} behaviour.confirmCheckDialog * @property {boolean} behaviour.confirmRetryDialog * * @property {Object} UI * @property {string} UI.tipsLabel * * @property {Object} [confirmRetry] * @property {string} [confirmRetry.header] * @property {string} [confirmRetry.body] * @property {string} [confirmRetry.cancelLabel] * @property {string} [confirmRetry.confirmLabel] * * @property {Object} [confirmCheck] * @property {string} [confirmCheck.header] * @property {string} [confirmCheck.body] * @property {string} [confirmCheck.cancelLabel] * @property {string} [confirmCheck.confirmLabel] */ /** * Module for creating a multiple choice question * * @param {Options} options * @param {number} contentId * @param {Object} contentData * @returns {H5P.MultiChoice} * @constructor */ H5P.MultiChoice = function (options, contentId, contentData) { if (!(this instanceof H5P.MultiChoice)) return new H5P.MultiChoice(options, contentId, contentData); var self = this; this.contentId = contentId; this.contentData = contentData; H5P.Question.call(self, 'multichoice'); var $ = H5P.jQuery; // checkbox or radiobutton var texttemplate = '<ul class="h5p-answers" role="<%= role %>" aria-labelledby="<%= label %>">' + ' <% for (var i=0; i < answers.length; i++) { %>' + ' <li class="h5p-answer" role="<%= answers[i].role %>" tabindex="<%= answers[i].tabindex %>" aria-checked="<%= answers[i].checked %>" data-id="<%= i %>">' + ' <div class="h5p-alternative-container">' + ' <span class="h5p-alternative-inner"><%= answers[i].text %></span>' + ' </div>' + ' <div class="h5p-clearfix"></div>' + ' </li>' + ' <% } %>' + '</ul>'; var defaults = { image: null, question: "No question text provided", answers: [ { tipsAndFeedback: { tip: '', chosenFeedback: '', notChosenFeedback: '' }, text: "Answer 1", correct: true } ], overallFeedback: [], weight: 1, userAnswers: [], UI: { checkAnswerButton: 'Check', showSolutionButton: 'Show solution', tryAgainButton: 'Try again', scoreBarLabel: 'You got :num out of :total points', tipAvailable: "Tip available", feedbackAvailable: "Feedback available", readFeedback: 'Read feedback', shouldCheck: "Should have been checked", shouldNotCheck: "Should not have been checked", noInput: 'Input is required before viewing the solution', a11yCheck: 'Check the answers. The responses will be marked as correct, incorrect, or unanswered.', a11yShowSolution: 'Show the solution. The task will be marked with its correct solution.', a11yRetry: 'Retry the task. Reset all responses and start the task over again.', }, behaviour: { enableRetry: true, enableSolutionsButton: true, enableCheckButton: true, type: 'auto', singlePoint: true, randomAnswers: false, showSolutionsRequiresInput: true, autoCheck: false, passPercentage: 100, showScorePoints: true } }; var template = new EJS({text: texttemplate}); var params = $.extend(true, defaults, options); // Keep track of number of correct choices var numCorrect = 0; // Loop through choices for (var i = 0; i < params.answers.length; i++) { var answer = params.answers[i]; // Make sure tips and feedback exists answer.tipsAndFeedback = answer.tipsAndFeedback || {}; if (params.answers[i].correct) { // Update number of correct choices numCorrect++; } } // Determine if no choices is the correct var blankIsCorrect = (numCorrect === 0); // Determine task type if (params.behaviour.type === 'auto') { // Use single choice if only one choice is correct params.behaviour.singleAnswer = (numCorrect === 1); } else { params.behaviour.singleAnswer = (params.behaviour.type === 'single'); } var getCheckboxOrRadioIcon = function (radio, selected) { var icon; if (radio) { icon = selected ? '' : ''; } else { icon = selected ? '' : ''; } return icon; }; // Initialize buttons and elements. var $myDom; var $feedbackDialog; /** * Remove all feedback dialogs */ var removeFeedbackDialog = function () { // Remove the open feedback dialogs. $myDom.unbind('click', removeFeedbackDialog); $myDom.find('.h5p-feedback-button, .h5p-feedback-dialog').remove(); $myDom.find('.h5p-has-feedback').removeClass('h5p-has-feedback'); if ($feedbackDialog) { $feedbackDialog.remove(); } }; var score = 0; var solutionsVisible = false; /** * Add feedback to element * @param {jQuery} $element Element that feedback will be added to * @param {string} feedback Feedback string */ var addFeedback = function ($element, feedback) { $feedbackDialog = $('' + '<div class="h5p-feedback-dialog">' + '<div class="h5p-feedback-inner">' + '<div class="h5p-feedback-text" aria-hidden="true">' + feedback + '</div>' + '</div>' + '</div>'); //make sure feedback is only added once if (!$element.find($('.h5p-feedback-dialog')).length) { $feedbackDialog.appendTo($element.addClass('h5p-has-feedback')); } }; /** * Register the different parts of the task with the H5P.Question structure. */ self.registerDomElements = function () { var media = params.media; if (media && media.type && media.type.library) { media = media.type; var type = media.library.split(' ')[0]; if (type === 'H5P.Image') { if (media.params.file) { // Register task image self.setImage(media.params.file.path, { disableImageZooming: params.media.disableImageZooming || false, alt: media.params.alt, title: media.params.title }); } } else if (type === 'H5P.Video') { if (media.params.sources) { // Register task video self.setVideo(media); } } } // Determine if we're using checkboxes or radio buttons for (var i = 0; i < params.answers.length; i++) { params.answers[i].checkboxOrRadioIcon = getCheckboxOrRadioIcon(params.behaviour.singleAnswer, params.userAnswers.indexOf(i) > -1); } // Register Introduction self.setIntroduction('<div id="' + params.label + '">' + params.question + '</div>'); // Register task content area $myDom = $(template.render(params)); self.setContent($myDom, { 'class': params.behaviour.singleAnswer ? 'h5p-radio' : 'h5p-check' }); // Create tips: var $answers = $('.h5p-answer', $myDom).each(function (i) { var tip = params.answers[i].tipsAndFeedback.tip; if (tip === undefined) { return; // No tip } tip = tip.trim(); var tipContent = tip .replace(/ /g, '') .replace(/<p>/g, '') .replace(/<\/p>/g, '') .trim(); if (!tipContent.length) { return; // Empty tip } else { $(this).addClass('h5p-has-tip'); } // Add tip var $wrap = $('<div/>', { 'class': 'h5p-multichoice-tipwrap', 'aria-label': params.UI.tipAvailable + '.' }); var $multichoiceTip = $('<div>', { 'role': 'button', 'tabindex': 0, 'title': params.UI.tipsLabel, 'aria-label': params.UI.tipsLabel, 'aria-expanded': false, 'class': 'multichoice-tip', appendTo: $wrap }); var tipIconHtml = '<span class="joubel-icon-tip-normal">' + '<span class="h5p-icon-shadow"></span>' + '<span class="h5p-icon-speech-bubble"></span>' + '<span class="h5p-icon-info"></span>' + '</span>'; $multichoiceTip.append(tipIconHtml); $multichoiceTip.click(function () { var $tipContainer = $multichoiceTip.parents('.h5p-answer'); var openFeedback = !$tipContainer.children('.h5p-feedback-dialog').is($feedbackDialog); removeFeedbackDialog(); // Do not open feedback if it was open if (openFeedback) { $multichoiceTip.attr('aria-expanded', true); // Add tip dialog addFeedback($tipContainer, tip); $feedbackDialog.addClass('h5p-has-tip'); // Tip for readspeaker self.read(tip); } else { $multichoiceTip.attr('aria-expanded', false); } self.trigger('resize'); // Remove tip dialog on dom click setTimeout(function () { $myDom.click(removeFeedbackDialog); }, 100); // Do not propagate return false; }).keydown(function (e) { if (e.which === 32) { $(this).click(); return false; } }); $('.h5p-alternative-container', this).append($wrap); }); // Set event listeners. var toggleCheck = function ($ans) { if ($ans.attr('aria-disabled') === 'true') { return; } self.answered = true; var num = parseInt($ans.data('id')); if (params.behaviour.singleAnswer) { // Store answer params.userAnswers[0] = num; // Calculate score score = (params.answers[num].correct ? 1 : 0); // De-select previous answer $answers.not($ans).removeClass('h5p-selected').attr('tabindex', '-1').attr('aria-checked', 'false'); // Select new answer $ans.addClass('h5p-selected').attr('tabindex', '0').attr('aria-checked', 'true'); } else { if ($ans.attr('aria-checked') === 'true') { // Do not allow un-checking when retry disabled and auto check if (params.behaviour.autoCheck && !params.behaviour.enableRetry) { return; } // Remove check $ans.removeClass('h5p-selected').attr('aria-checked', 'false'); } else { $ans.addClass('h5p-selected').attr('aria-checked', 'true'); } // Calculate score calcScore(); } self.triggerXAPI('interacted'); hideSolution($ans); if (params.userAnswers.length) { self.showButton('check-answer'); self.hideButton('try-again'); self.hideButton('show-solution'); if (params.behaviour.autoCheck) { if (params.behaviour.singleAnswer) { // Only a single answer allowed checkAnswer(); } else { // Show feedback for selected alternatives self.showCheckSolution(true); // Always finish task if it was completed successfully if (score === self.getMaxScore()) { checkAnswer(); } } } } }; $answers.click(function () { toggleCheck($(this)); }).keydown(function (e) { if (e.keyCode === 32) { // Space bar // Select current item toggleCheck($(this)); return false; } if (params.behaviour.singleAnswer) { switch (e.keyCode) { case 38: // Up case 37: { // Left // Try to select previous item var $prev = $(this).prev(); if ($prev.length) { toggleCheck($prev.focus()); } return false; } case 40: // Down case 39: { // Right // Try to select next item var $next = $(this).next(); if ($next.length) { toggleCheck($next.focus()); } return false; } } } }); if (params.behaviour.singleAnswer) { // Special focus handler for radio buttons $answers.focus(function () { if ($(this).attr('aria-disabled') !== 'true') { $answers.not(this).attr('tabindex', '-1'); } }).blur(function () { if (!$answers.filter('.h5p-selected').length) { $answers.first().add($answers.last()).attr('tabindex', '0'); } }); } // Adds check and retry button addButtons(); if (!params.behaviour.singleAnswer) { calcScore(); } else { if (params.userAnswers.length && params.answers[params.userAnswers[0]].correct) { score = 1; } else { score = 0; } } // Has answered through auto-check in a previous session if (hasCheckedAnswer && params.behaviour.autoCheck) { // Check answers if answer has been given or max score reached if (params.behaviour.singleAnswer || score === self.getMaxScore()) { checkAnswer(); } else { // Show feedback for checked checkboxes self.showCheckSolution(true); } } }; this.showAllSolutions = function () { if (solutionsVisible) { return; } solutionsVisible = true; $myDom.find('.h5p-answer').each(function (i, e) { var $e = $(e); var a = params.answers[i]; if (a.correct) { $e.addClass('h5p-should').append($('<span/>', { 'class': 'h5p-solution-icon', html: params.UI.shouldCheck + '.' })); } else { $e.addClass('h5p-should-not').append($('<span/>', { 'class': 'h5p-solution-icon', html: params.UI.shouldNotCheck + '.' })); } }).find('.h5p-question-plus-one, .h5p-question-minus-one').remove(); // Make sure input is disabled in solution mode disableInput(); // Move focus back to the first correct alternative so that the user becomes // aware that the solution is being shown. $myDom.find('.h5p-answer.h5p-should').first().focus(); //Hide buttons and retry depending on settings. self.hideButton('check-answer'); self.hideButton('show-solution'); if (params.behaviour.enableRetry) { self.showButton('try-again'); } self.trigger('resize'); }; /** * Used in contracts. * Shows the solution for the task and hides all buttons. */ this.showSolutions = function () { removeFeedbackDialog(); self.showCheckSolution(); self.showAllSolutions(); disableInput(); self.hideButton('try-again'); }; /** * Hide solution for the given answer(s) * * @private * @param {H5P.jQuery} $answer */ var hideSolution = function ($answer) { $answer .removeClass('h5p-correct') .removeClass('h5p-wrong') .removeClass('h5p-should') .removeClass('h5p-should-not') .removeClass('h5p-has-feedback') .find('.h5p-question-plus-one, .h5p-question-minus-one, .h5p-answer-icon, .h5p-solution-icon, .h5p-feedback-dialog').remove(); }; /** * */ this.hideSolutions = function () { solutionsVisible = false; hideSolution($('.h5p-answer', $myDom)); this.removeFeedback(); // Reset feedback self.trigger('resize'); }; /** * Resets the whole task. * Used in contracts with integrated content. * @private */ this.resetTask = function () { self.answered = false; self.hideSolutions(); params.userAnswers = []; removeSelections(); self.showButton('check-answer'); self.hideButton('try-again'); self.hideButton('show-solution'); enableInput(); $myDom.find('.h5p-feedback-available').remove(); }; var calculateMaxScore = function () { if (blankIsCorrect) { return params.weight; } var maxScore = 0; for (var i = 0; i < params.answers.length; i++) { var choice = params.answers[i]; if (choice.correct) { maxScore += (choice.weight !== undefined ? choice.weight : 1); } } return maxScore; }; this.getMaxScore = function () { return (!params.behaviour.singleAnswer && !params.behaviour.singlePoint ? calculateMaxScore() : params.weight); }; /** * Check answer */ var checkAnswer = function () { // Unbind removal of feedback dialogs on click $myDom.unbind('click', removeFeedbackDialog); // Remove all tip dialogs removeFeedbackDialog(); if (params.behaviour.enableSolutionsButton) { self.showButton('show-solution'); } if (params.behaviour.enableRetry) { self.showButton('try-again'); } self.hideButton('check-answer'); self.showCheckSolution(); disableInput(); var xAPIEvent = self.createXAPIEventTemplate('answered'); addQuestionToXAPI(xAPIEvent); addResponseToXAPI(xAPIEvent); self.trigger(xAPIEvent); }; /** * Determine if any of the radios or checkboxes have been checked. * * @return {boolean} */ var isAnswerSelected = function () { return !!$('.h5p-answer[aria-checked="true"]', $myDom).length; }; /** * Adds the ui buttons. * @private */ var addButtons = function () { var $content = $('[data-content-id="' + self.contentId + '"].h5p-content'); var $containerParents = $content.parents('.h5p-container'); // select find container to attach dialogs to var $container; if($containerParents.length !== 0) { // use parent highest up if any $container = $containerParents.last(); } else if($content.length !== 0){ $container = $content; } else { $container = $(document.body); } // Show solution button self.addButton('show-solution', params.UI.showSolutionButton, function () { if (params.behaviour.showSolutionsRequiresInput && !isAnswerSelected()) { // Require answer before solution can be viewed self.updateFeedbackContent(params.UI.noInput); self.read(params.UI.noInput); } else { calcScore(); self.showAllSolutions(); } }, false, { 'aria-label': params.UI.a11yShowSolution, }); // Check solution button if (params.behaviour.enableCheckButton && (!params.behaviour.autoCheck || !params.behaviour.singleAnswer)) { self.addButton('check-answer', params.UI.checkAnswerButton, function () { self.answered = true; checkAnswer(); }, true, { 'aria-label': params.UI.a11yCheck, }, { confirmationDialog: { enable: params.behaviour.confirmCheckDialog, l10n: params.confirmCheck, instance: self, $parentElement: $container } } ); } // Try Again button self.addButton('try-again', params.UI.tryAgainButton, function () { self.showButton('check-answer'); self.hideButton('try-again'); self.hideButton('show-solution'); self.hideSolutions(); removeSelections(); enableInput(); $myDom.find('.h5p-feedback-available').remove(); self.answered = false; if (params.behaviour.randomAnswers) { // reshuffle answers var oldIdMap = idMap; idMap = getShuffleMap(); var answersDisplayed = $myDom.find('.h5p-answer'); // remember tips var tip = []; for (i = 0; i < answersDisplayed.length; i++) { tip[i] = $(answersDisplayed[i]).find('.h5p-multichoice-tipwrap'); } // Those two loops cannot be merged or you'll screw up your tips for (i = 0; i < answersDisplayed.length; i++) { // move tips and answers on display $(answersDisplayed[i]).find('.h5p-alternative-inner').html(params.answers[i].text); $(tip[i]).detach().appendTo($(answersDisplayed[idMap.indexOf(oldIdMap[i])]).find('.h5p-alternative-container')); } } }, false, { 'aria-label': params.UI.a11yRetry, }, { confirmationDialog: { enable: params.behaviour.confirmRetryDialog, l10n: params.confirmRetry, instance: self, $parentElement: $container } }); }; /** * @private */ var insertFeedback = function ($e, feedback) { // Add visuals addFeedback($e, feedback); // Add button for readspeakers var $wrap = $('<div/>', { 'class': 'h5p-hidden-read h5p-feedback-available', 'aria-label': params.UI.feedbackAvailable + '.' }); $('<div/>', { 'role': 'button', 'tabindex': 0, 'aria-label': params.UI.readFeedback + '.', appendTo: $wrap, on: { keydown: function (e) { if (e.which === 32) { // Space self.read(feedback); return false; } } } }); $wrap.appendTo($e); }; /** * Determine which feedback text to display * * @param {number} score * @param {number} max * @return {string} */ var getFeedbackText = function (score, max) { var ratio = (score / max); var feedback = H5P.Question.determineOverallFeedback(params.overallFeedback, ratio); return feedback.replace('@score', score).replace('@total', max); }; /** * Shows feedback on the selected fields. * @public * @param {boolean} [skipFeedback] Skip showing feedback if true */ this.showCheckSolution = function (skipFeedback) { var scorePoints; if (!(params.behaviour.singleAnswer || params.behaviour.singlePoint || !params.behaviour.showScorePoints)) { scorePoints = new H5P.Question.ScorePoints(); } $myDom.find('.h5p-answer').each(function (i, e) { var $e = $(e); var a = params.answers[i]; var chosen = ($e.attr('aria-checked') === 'true'); if (chosen) { if (a.correct) { // May already have been applied by instant feedback if (!$e.hasClass('h5p-correct')) { $e.addClass('h5p-correct').append($('<span/>', { 'class': 'h5p-answer-icon', html: params.UI.correctAnswer + '.' })); } } else { if (!$e.hasClass('h5p-wrong')) { $e.addClass('h5p-wrong').append($('<span/>', { 'class': 'h5p-answer-icon', html: params.UI.wrongAnswer + '.' })); } } if (scorePoints) { var alternativeContainer = $e[0].querySelector('.h5p-alternative-container'); if (!params.behaviour.autoCheck || alternativeContainer.querySelector('.h5p-question-plus-one, .h5p-question-minus-one') === null) { alternativeContainer.appendChild(scorePoints.getElement(a.correct)); } } } if (!skipFeedback) { if (chosen && a.tipsAndFeedback.chosenFeedback !== undefined && a.tipsAndFeedback.chosenFeedback !== '') { insertFeedback($e, a.tipsAndFeedback.chosenFeedback); } else if (!chosen && a.tipsAndFeedback.notChosenFeedback !== undefined && a.tipsAndFeedback.notChosenFeedback !== '') { insertFeedback($e, a.tipsAndFeedback.notChosenFeedback); } } }); // Determine feedback var max = self.getMaxScore(); // Disable task if maxscore is achieved var fullScore = (score === max); if (fullScore) { self.hideButton('check-answer'); self.hideButton('try-again'); self.hideButton('show-solution'); } // Show feedback if (!skipFeedback) { this.setFeedback(getFeedbackText(score, max), score, max, params.UI.scoreBarLabel); } self.trigger('resize'); }; /** * Disables choosing new input. */ var disableInput = function () { $('.h5p-answer', $myDom).attr({ 'aria-disabled': 'true', 'tabindex': '-1' }); }; /** * Enables new input. */ var enableInput = function () { $('.h5p-answer', $myDom).attr('aria-disabled', 'false'); }; var calcScore = function () { score = 0; params.userAnswers = []; $('.h5p-answer', $myDom).each(function (idx, el) { var $el = $(el); if ($el.attr('aria-checked') === 'true') { var choice = params.answers[idx]; var weight = (choice.weight !== undefined ? choice.weight : 1); if (choice.correct) { score += weight; } else { score -= weight; } var num = parseInt($(el).data('id')); params.userAnswers.push(num); } }); if (score < 0) { score = 0; } if (!params.userAnswers.length && blankIsCorrect) { score = params.weight; } if (params.behaviour.singlePoint) { score = (100 * score / calculateMaxScore()) >= params.behaviour.passPercentage ? params.weight : 0; } }; /** * Removes selections from task. */ var removeSelections = function () { var $answers = $('.h5p-answer', $myDom) .removeClass('h5p-selected') .attr('aria-checked', 'false'); if (!params.behaviour.singleAnswer) { $answers.attr('tabindex', '0'); } else { $answers.first().attr('tabindex', '0'); } // Set focus to first option $answers.first().focus(); calcScore(); }; /** * Get xAPI data. * Contract used by report rendering engine. * * @see contract at {@link https://h5p.org/documentation/developers/contracts#guides-header-6} */ this.getXAPIData = function(){ var xAPIEvent = this.createXAPIEventTemplate('answered'); addQuestionToXAPI(xAPIEvent); addResponseToXAPI(xAPIEvent); return { statement: xAPIEvent.data.statement }; }; /** * Add the question itself to the definition part of an xAPIEvent */ var addQuestionToXAPI = function (xAPIEvent) { var definition = xAPIEvent.getVerifiedStatementValue(['object', 'definition']); definition.description = { // Remove tags, must wrap in div tag because jQuery 1.9 will crash if the string isn't wrapped in a tag. 'en-US': $('<div>' + params.question + '</div>').text() }; definition.type = 'http://adlnet.gov/expapi/activities/cmi.interaction'; definition.interactionType = 'choice'; definition.correctResponsesPattern = []; definition.choices = []; for (var i = 0; i < params.answers.length; i++) { definition.choices[i] = { 'id': params.answers[i].originalOrder + '', 'description': { // Remove tags, must wrap in div tag because jQuery 1.9 will crash if the string isn't wrapped in a tag. 'en-US': $('<div>' + params.answers[i].text + '</div>').text() } }; if (params.answers[i].correct) { if (!params.singleAnswer) { if (definition.correctResponsesPattern.length) { definition.correctResponsesPattern[0] += '[,]'; // This looks insane, but it's how you separate multiple answers // that must all be chosen to achieve perfect score... } else { definition.correctResponsesPattern.push(''); } definition.correctResponsesPattern[0] += params.answers[i].originalOrder; } else { definition.correctResponsesPattern.push('' + params.answers[i].originalOrder); } } } }; /** * Add the response part to an xAPI event * * @param {H5P.XAPIEvent} xAPIEvent * The xAPI event we will add a response to */ var addResponseToXAPI = function (xAPIEvent) { var maxScore = self.getMaxScore(); var success = (100 * score / maxScore) >= params.behaviour.passPercentage; xAPIEvent.setScoredResult(score, maxScore, self, true, success); if (params.userAnswers === undefined) { calcScore(); } // Add the response var response = ''; for (var i = 0; i < params.userAnswers.length; i++) { if (response !== '') { response += '[,]'; } response += idMap === undefined ? params.userAnswers[i] : idMap[params.userAnswers[i]]; } xAPIEvent.data.statement.result.response = response; }; /** * Create a map pointing from original answers to shuffled answers * * @return {number[]} map pointing from original answers to shuffled answers */ var getShuffleMap = function() { params.answers = H5P.shuffleArray(params.answers); // Create a map from the new id to the old one var idMap = []; for (i = 0; i < params.answers.length; i++) { idMap[i] = params.answers[i].originalOrder; } return idMap; }; // Initialization code // Randomize order, if requested var idMap; // Store original order in answers for (i = 0; i < params.answers.length; i++) { params.answers[i].originalOrder = i; } if (params.behaviour.randomAnswers) { idMap = getShuffleMap(); } // Start with an empty set of user answers. params.userAnswers = []; // Restore previous state if (contentData && contentData.previousState !== undefined) { // Restore answers if (contentData.previousState.answers) { if (!idMap) { params.userAnswers = contentData.previousState.answers; } else { // The answers have been shuffled, and we must use the id mapping. for (i = 0; i < contentData.previousState.answers.length; i++) { for (var k = 0; k < idMap.length; k++) { if (idMap[k] === contentData.previousState.answers[i]) { params.userAnswers.push(k); } } } } } } var hasCheckedAnswer = false; // Loop through choices for (var j = 0; j < params.answers.length; j++) { var ans = params.answers[j]; if (!params.behaviour.singleAnswer) { // Set role ans.role = 'checkbox'; ans.tabindex = '0'; if (params.userAnswers.indexOf(j) !== -1) { ans.checked = 'true'; hasCheckedAnswer = true; } } else { // Set role ans.role = 'radio'; // Determine tabindex, checked and extra classes if (params.userAnswers.length === 0) { // No correct answers if (i === 0 || i === params.answers.length) { ans.tabindex = '0'; } } else if (params.userAnswers.indexOf(j) !== -1) { // This is the correct choice ans.tabindex = '0'; ans.checked = 'true'; hasCheckedAnswer = true; } } // Set default if (ans.tabindex === undefined) { ans.tabindex = '-1'; } if (ans.checked === undefined) { ans.checked = 'false'; } } H5P.MultiChoice.counter = (H5P.MultiChoice.counter === undefined ? 0 : H5P.MultiChoice.counter + 1); params.role = (params.behaviour.singleAnswer ? 'radiogroup' : 'group'); params.label = 'h5p-mcq' + H5P.MultiChoice.counter; /** * Pack the current state of the interactivity into a object that can be * serialized. * * @public */ this.getCurrentState = function () { var state = {}; if (!idMap) { state.answers = params.userAnswers; } else { // The answers have been shuffled and must be mapped back to their // original ID. state.answers = []; for (var i = 0; i < params.userAnswers.length; i++) { state.answers.push(idMap[params.userAnswers[i]]); } } return state; }; /** * Check if user has given an answer. * * @param {boolean} [ignoreCheck] Ignore returning true from pressing "check-answer" button. * @return {boolean} True if answer is given */ this.getAnswerGiven = function (ignoreCheck) { var answered = ignoreCheck ? false : this.answered; return answered || params.userAnswers.length > 0 || blankIsCorrect; }; this.getScore = function () { return score; }; this.getTitle = function () { return H5P.createTitle((this.contentData && this.contentData.metadata && this.contentData.metadata.title) ? this.contentData.metadata.title : 'Multiple Choice'); }; }; H5P.MultiChoice.prototype = Object.create(H5P.Question.prototype); H5P.MultiChoice.prototype.constructor = H5P.MultiChoice; ; /* * flowplayer.js 3.2.12. The Flowplayer API * * Copyright 2009-2011 Flowplayer Oy * * This file is part of Flowplayer. * * Flowplayer is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Flowplayer is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Flowplayer. If not, see <http://www.gnu.org/licenses/>. * * Date: ${date} * Revision: ${revision} */ !function(){function h(p){console.log("$f.fireEvent",[].slice.call(p))}function l(r){if(!r||typeof r!="object"){return r}var p=new r.constructor();for(var q in r){if(r.hasOwnProperty(q)){p[q]=l(r[q])}}return p}function n(u,r){if(!u){return}var p,q=0,s=u.length;if(s===undefined){for(p in u){if(r.call(u[p],p,u[p])===false){break}}}else{for(var t=u[0];q<s&&r.call(t,q,t)!==false;t=u[++q]){}}return u}function c(p){return document.getElementById(p)}function j(r,q,p){if(typeof q!="object"){return r}if(r&&q){n(q,function(s,t){if(!p||typeof t!="function"){r[s]=t}})}return r}function o(t){var r=t.indexOf(".");if(r!=-1){var q=t.slice(0,r)||"*";var p=t.slice(r+1,t.length);var s=[];n(document.getElementsByTagName(q),function(){if(this.className&&this.className.indexOf(p)!=-1){s.push(this)}});return s}}function g(p){p=p||window.event;if(p.preventDefault){p.stopPropagation();p.preventDefault()}else{p.returnValue=false;p.cancelBubble=true}return false}function k(r,p,q){r[p]=r[p]||[];r[p].push(q)}function e(p){return p.replace(/&/g,"%26").replace(/&/g,"%26").replace(/=/g,"%3D")}function f(){return"_"+(""+Math.random()).slice(2,10)}var i=function(u,s,t){var r=this,q={},v={};r.index=s;if(typeof u=="string"){u={url:u}}j(this,u,true);n(("Begin*,Start,Pause*,Resume*,Seek*,Stop*,Finish*,LastSecond,Update,BufferFull,BufferEmpty,BufferStop").split(","),function(){var w="on"+this;if(w.indexOf("*")!=-1){w=w.slice(0,w.length-1);var x="onBefore"+w.slice(2);r[x]=function(y){k(v,x,y);return r}}r[w]=function(y){k(v,w,y);return r};if(s==-1){if(r[x]){t[x]=r[x]}if(r[w]){t[w]=r[w]}}});j(this,{onCuepoint:function(y,x){if(arguments.length==1){q.embedded=[null,y];return r}if(typeof y=="number"){y=[y]}var w=f();q[w]=[y,x];if(t.isLoaded()){t._api().fp_addCuepoints(y,s,w)}return r},update:function(x){j(r,x);if(t.isLoaded()){t._api().fp_updateClip(x,s)}var w=t.getConfig();var y=(s==-1)?w.clip:w.playlist[s];j(y,x,true)},_fireEvent:function(w,z,x,B){if(w=="onLoad"){n(q,function(C,D){if(D[0]){t._api().fp_addCuepoints(D[0],s,C)}});return false}B=B||r;if(w=="onCuepoint"){var A=q[z];if(A){return A[1].call(t,B,x)}}if(z&&"onBeforeBegin,onMetaData,onStart,onUpdate,onResume".indexOf(w)!=-1){j(B,z);if(z.metaData){if(!B.duration){B.duration=z.metaData.duration}else{B.fullDuration=z.metaData.duration}}}var y=true;n(v[w],function(){y=this.call(t,B,z,x)});return y}});if(u.onCuepoint){var p=u.onCuepoint;r.onCuepoint.apply(r,typeof p=="function"?[p]:p);delete u.onCuepoint}n(u,function(w,x){if(typeof x=="function"){k(v,w,x);delete u[w]}});if(s==-1){t.onCuepoint=this.onCuepoint}};var m=function(q,s,r,u){var p=this,t={},v=false;if(u){j(t,u)}n(s,function(w,x){if(typeof x=="function"){t[w]=x;delete s[w]}});j(this,{animate:function(z,A,y){if(!z){return p}if(typeof A=="function"){y=A;A=500}if(typeof z=="string"){var x=z;z={};z[x]=A;A=500}if(y){var w=f();t[w]=y}if(A===undefined){A=500}s=r._api().fp_animate(q,z,A,w);return p},css:function(x,y){if(y!==undefined){var w={};w[x]=y;x=w}s=r._api().fp_css(q,x);j(p,s);return p},show:function(){this.display="block";r._api().fp_showPlugin(q);return p},hide:function(){this.display="none";r._api().fp_hidePlugin(q);return p},toggle:function(){this.display=r._api().fp_togglePlugin(q);return p},fadeTo:function(z,y,x){if(typeof y=="function"){x=y;y=500}if(x){var w=f();t[w]=x}this.display=r._api().fp_fadeTo(q,z,y,w);this.opacity=z;return p},fadeIn:function(x,w){return p.fadeTo(1,x,w)},fadeOut:function(x,w){return p.fadeTo(0,x,w)},getName:function(){return q},getPlayer:function(){return r},_fireEvent:function(x,w,y){if(x=="onUpdate"){var A=r._api().fp_getPlugin(q);if(!A){return}j(p,A);delete p.methods;if(!v){n(A.methods,function(){var C=""+this;p[C]=function(){var D=[].slice.call(arguments);var E=r._api().fp_invoke(q,C,D);return E==="undefined"||E===undefined?p:E}});v=true}}var B=t[x];if(B){var z=B.apply(p,w);if(x.slice(0,1)=="_"){delete t[x]}return z}return p}})};function b(r,H,u){var x=this,w=null,E=false,v,t,G=[],z={},y={},F,s,q,D,p,B;j(x,{id:function(){return F},isLoaded:function(){return(w!==null&&w.fp_play!==undefined&&!E)},getParent:function(){return r},hide:function(I){if(I){r.style.height="0px"}if(x.isLoaded()){w.style.height="0px"}return x},show:function(){r.style.height=B+"px";if(x.isLoaded()){w.style.height=p+"px"}return x},isHidden:function(){return x.isLoaded()&&parseInt(w.style.height,10)===0},load:function(K){if(!x.isLoaded()&&x._fireEvent("onBeforeLoad")!==false){var I=function(){if(v&&!flashembed.isSupported(H.version)){r.innerHTML=""}if(K){K.cached=true;k(y,"onLoad",K)}flashembed(r,H,{config:u})};var J=0;n(a,function(){this.unload(function(L){if(++J==a.length){I()}})})}return x},unload:function(K){if(v.replace(/\s/g,"")!==""){if(x._fireEvent("onBeforeUnload")===false){if(K){K(false)}return x}E=true;try{if(w){if(w.fp_isFullscreen()){w.fp_toggleFullscreen()}w.fp_close();x._fireEvent("onUnload")}}catch(I){}var J=function(){w=null;r.innerHTML=v;E=false;if(K){K(true)}};if(/WebKit/i.test(navigator.userAgent)&&!/Chrome/i.test(navigator.userAgent)){setTimeout(J,0)}else{J()}}else{if(K){K(false)}}return x},getClip:function(I){if(I===undefined){I=D}return G[I]},getCommonClip:function(){return t},getPlaylist:function(){return G},getPlugin:function(I){var K=z[I];if(!K&&x.isLoaded()){var J=x._api().fp_getPlugin(I);if(J){K=new m(I,J,x);z[I]=K}}return K},getScreen:function(){return x.getPlugin("screen")},getControls:function(){return x.getPlugin("controls")._fireEvent("onUpdate")},getLogo:function(){try{return x.getPlugin("logo")._fireEvent("onUpdate")}catch(I){}},getPlay:function(){return x.getPlugin("play")._fireEvent("onUpdate")},getConfig:function(I){return I?l(u):u},getFlashParams:function(){return H},loadPlugin:function(L,K,N,M){if(typeof N=="function"){M=N;N={}}var J=M?f():"_";x._api().fp_loadPlugin(L,K,N,J);var I={};I[J]=M;var O=new m(L,null,x,I);z[L]=O;return O},getState:function(){return x.isLoaded()?w.fp_getState():-1},play:function(J,I){var K=function(){if(J!==undefined){x._api().fp_play(J,I)}else{x._api().fp_play()}};if(x.isLoaded()){K()}else{if(E){setTimeout(function(){x.play(J,I)},50)}else{x.load(function(){K()})}}return x},getVersion:function(){var J="flowplayer.js 3.2.12";if(x.isLoaded()){var I=w.fp_getVersion();I.push(J);return I}return J},_api:function(){if(!x.isLoaded()){throw"Flowplayer "+x.id()+" not loaded when calling an API method"}return w},setClip:function(I){n(I,function(J,K){if(typeof K=="function"){k(y,J,K);delete I[J]}else{if(J=="onCuepoint"){$f(r).getCommonClip().onCuepoint(I[J][0],I[J][1])}}});x.setPlaylist([I]);return x},getIndex:function(){return q},bufferAnimate:function(I){w.fp_bufferAnimate(I===undefined||I);return x},_swfHeight:function(){return w.clientHeight}});n(("Click*,Load*,Unload*,Keypress*,Volume*,Mute*,Unmute*,PlaylistReplace,ClipAdd,Fullscreen*,FullscreenExit,Error,MouseOver,MouseOut").split(","),function(){var I="on"+this;if(I.indexOf("*")!=-1){I=I.slice(0,I.length-1);var J="onBefore"+I.slice(2);x[J]=function(K){k(y,J,K);return x}}x[I]=function(K){k(y,I,K);return x}});n(("pause,resume,mute,unmute,stop,toggle,seek,getStatus,getVolume,setVolume,getTime,isPaused,isPlaying,startBuffering,stopBuffering,isFullscreen,toggleFullscreen,reset,close,setPlaylist,addClip,playFeed,setKeyboardShortcutsEnabled,isKeyboardShortcutsEnabled").split(","),function(){var I=this;x[I]=function(K,J){if(!x.isLoaded()){return x}var L=null;if(K!==undefined&&J!==undefined){L=w["fp_"+I](K,J)}else{L=(K===undefined)?w["fp_"+I]():w["fp_"+I](K)}return L==="undefined"||L===undefined?x:L}});x._fireEvent=function(R){if(typeof R=="string"){R=[R]}var S=R[0],P=R[1],N=R[2],M=R[3],L=0;if(u.debug){h(R)}if(!x.isLoaded()&&S=="onLoad"&&P=="player"){w=w||c(s);p=x._swfHeight();n(G,function(){this._fireEvent("onLoad")});n(z,function(T,U){U._fireEvent("onUpdate")});t._fireEvent("onLoad")}if(S=="onLoad"&&P!="player"){return}if(S=="onError"){if(typeof P=="string"||(typeof P=="number"&&typeof N=="number")){P=N;N=M}}if(S=="onContextMenu"){n(u.contextMenu[P],function(T,U){U.call(x)});return}if(S=="onPluginEvent"||S=="onBeforePluginEvent"){var I=P.name||P;var J=z[I];if(J){J._fireEvent("onUpdate",P);return J._fireEvent(N,R.slice(3))}return}if(S=="onPlaylistReplace"){G=[];var O=0;n(P,function(){G.push(new i(this,O++,x))})}if(S=="onClipAdd"){if(P.isInStream){return}P=new i(P,N,x);G.splice(N,0,P);for(L=N+1;L<G.length;L++){G[L].index++}}var Q=true;if(typeof P=="number"&&P<G.length){D=P;var K=G[P];if(K){Q=K._fireEvent(S,N,M)}if(!K||Q!==false){Q=t._fireEvent(S,N,M,K)}}n(y[S],function(){Q=this.call(x,P,N);if(this.cached){y[S].splice(L,1)}if(Q===false){return false}L++});return Q};function C(){if($f(r)){$f(r).getParent().innerHTML="";q=$f(r).getIndex();a[q]=x}else{a.push(x);q=a.length-1}B=parseInt(r.style.height,10)||r.clientHeight;F=r.id||"fp"+f();s=H.id||F+"_api";H.id=s;v=r.innerHTML;if(typeof u=="string"){u={clip:{url:u}}}u.playerId=F;u.clip=u.clip||{};if(r.getAttribute("href",2)&&!u.clip.url){u.clip.url=r.getAttribute("href",2)}if(u.clip.url){u.clip.url=e(u.clip.url)}t=new i(u.clip,-1,x);u.playlist=u.playlist||[u.clip];var J=0;n(u.playlist,function(){var M=this;if(typeof M=="object"&&M.length){M={url:""+M}}if(M.url){M.url=e(M.url)}n(u.clip,function(N,O){if(O!==undefined&&M[N]===undefined&&typeof O!="function"){M[N]=O}});u.playlist[J]=M;M=new i(M,J,x);G.push(M);J++});n(u,function(M,N){if(typeof N=="function"){if(t[M]){t[M](N)}else{k(y,M,N)}delete u[M]}});n(u.plugins,function(M,N){if(N){z[M]=new m(M,N,x)}});if(!u.plugins||u.plugins.controls===undefined){z.controls=new m("controls",null,x)}z.canvas=new m("canvas",null,x);v=r.innerHTML;function L(M){if(/iPad|iPhone|iPod/i.test(navigator.userAgent)&&!/.flv$/i.test(G[0].url)&&!K()){return true}if(!x.isLoaded()&&x._fireEvent("onBeforeClick")!==false){x.load()}return g(M)}function K(){return x.hasiPadSupport&&x.hasiPadSupport()}function I(){if(v.replace(/\s/g,"")!==""){if(r.addEventListener){r.addEventListener("click",L,false)}else{if(r.attachEvent){r.attachEvent("onclick",L)}}}else{if(r.addEventListener&&!K()){r.addEventListener("click",g,false)}x.load()}}setTimeout(I,0)}if(typeof r=="string"){var A=c(r);if(!A){throw"Flowplayer cannot access element: "+r}r=A;C()}else{C()}}var a=[];function d(p){this.length=p.length;this.each=function(r){n(p,r)};this.size=function(){return p.length};var q=this;for(name in b.prototype){q[name]=function(){var r=arguments;q.each(function(){this[name].apply(this,r)})}}}window.flowplayer=window.$f=function(){var q=null;var p=arguments[0];if(!arguments.length){n(a,function(){if(this.isLoaded()){q=this;return false}});return q||a[0]}if(arguments.length==1){if(typeof p=="number"){return a[p]}else{if(p=="*"){return new d(a)}n(a,function(){if(this.id()==p.id||this.id()==p||this.getParent()==p){q=this;return false}});return q}}if(arguments.length>1){var u=arguments[1],r=(arguments.length==3)?arguments[2]:{};if(typeof u=="string"){u={src:u}}u=j({bgcolor:"#000000",version:[10,1],expressInstall:"http://releases.flowplayer.org/swf/expressinstall.swf",cachebusting:false},u);if(typeof p=="string"){if(p.indexOf(".")!=-1){var t=[];n(o(p),function(){t.push(new b(this,l(u),l(r)))});return new d(t)}else{var s=c(p);return new b(s!==null?s:l(p),l(u),l(r))}}else{if(p){return new b(p,l(u),l(r))}}}return null};j(window.$f,{fireEvent:function(){var q=[].slice.call(arguments);var r=$f(q[0]);return r?r._fireEvent(q.slice(1)):null},addPlugin:function(p,q){b.prototype[p]=q;return $f},each:n,extend:j});if(typeof jQuery=="function"){jQuery.fn.flowplayer=function(r,q){if(!arguments.length||typeof arguments[0]=="number"){var p=[];this.each(function(){var s=$f(this);if(s){p.push(s)}});return arguments.length?p[arguments[0]]:new d(p)}return this.each(function(){$f(this,l(r),q?l(q):{})})}}}();!function(){var h=document.all,j="http://get.adobe.com/flashplayer",c=typeof jQuery=="function",e=/(\d+)[^\d]+(\d+)[^\d]*(\d*)/,b={width:"100%",height:"100%",id:"_"+(""+Math.random()).slice(9),allowfullscreen:true,allowscriptaccess:"always",quality:"high",version:[3,0],onFail:null,expressInstall:null,w3c:false,cachebusting:false};if(window.attachEvent){window.attachEvent("onbeforeunload",function(){__flash_unloadHandler=function(){};__flash_savedUnloadHandler=function(){}})}function i(m,l){if(l){for(var f in l){if(l.hasOwnProperty(f)){m[f]=l[f]}}}return m}function a(f,n){var m=[];for(var l in f){if(f.hasOwnProperty(l)){m[l]=n(f[l])}}return m}window.flashembed=function(f,m,l){if(typeof f=="string"){f=document.getElementById(f.replace("#",""))}if(!f){return}if(typeof m=="string"){m={src:m}}return new d(f,i(i({},b),m),l)};var g=i(window.flashembed,{conf:b,getVersion:function(){var m,f;try{f=navigator.plugins["Shockwave Flash"].description.slice(16)}catch(o){try{m=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.7");f=m&&m.GetVariable("$version")}catch(n){try{m=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.6");f=m&&m.GetVariable("$version")}catch(l){}}}f=e.exec(f);return f?[1*f[1],1*f[(f[1]*1>9?2:3)]*1]:[0,0]},asString:function(l){if(l===null||l===undefined){return null}var f=typeof l;if(f=="object"&&l.push){f="array"}switch(f){case"string":l=l.replace(new RegExp('(["\\\\])',"g"),"\\$1");l=l.replace(/^\s?(\d+\.?\d*)%/,"$1pct");return'"'+l+'"';case"array":return"["+a(l,function(o){return g.asString(o)}).join(",")+"]";case"function":return'"function()"';case"object":var m=[];for(var n in l){if(l.hasOwnProperty(n)){m.push('"'+n+'":'+g.asString(l[n]))}}return"{"+m.join(",")+"}"}return String(l).replace(/\s/g," ").replace(/\'/g,'"')},getHTML:function(o,l){o=i({},o);var n='<object width="'+o.width+'" height="'+o.height+'" id="'+o.id+'" name="'+o.id+'"';if(o.cachebusting){o.src+=((o.src.indexOf("?")!=-1?"&":"?")+Math.random())}if(o.w3c||!h){n+=' data="'+o.src+'" type="application/x-shockwave-flash"'}else{n+=' classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"'}n+=">";if(o.w3c||h){n+='<param name="movie" value="'+o.src+'" />'}o.width=o.height=o.id=o.w3c=o.src=null;o.onFail=o.version=o.expressInstall=null;for(var m in o){if(o[m]){n+='<param name="'+m+'" value="'+o[m]+'" />'}}var p="";if(l){for(var f in l){if(l[f]){var q=l[f];p+=f+"="+(/function|object/.test(typeof q)?g.asString(q):q)+"&"}}p=p.slice(0,-1);n+='<param name="flashvars" value=\''+p+"' />"}n+="</object>";return n},isSupported:function(f){return k[0]>f[0]||k[0]==f[0]&&k[1]>=f[1]}});var k=g.getVersion();function d(f,n,m){if(g.isSupported(n.version)){f.innerHTML=g.getHTML(n,m)}else{if(n.expressInstall&&g.isSupported([6,65])){f.innerHTML=g.getHTML(i(n,{src:n.expressInstall}),{MMredirectURL:encodeURIComponent(location.href),MMplayerType:"PlugIn",MMdoctitle:document.title})}else{if(!f.innerHTML.replace(/\s/g,"")){f.innerHTML="<h2>Flash version "+n.version+" or greater is required</h2><h3>"+(k[0]>0?"Your version is "+k:"You have no flash plugin installed")+"</h3>"+(f.tagName=="A"?"<p>Click here to download latest version</p>":"<p>Download latest version from <a href='"+j+"'>here</a></p>");if(f.tagName=="A"||f.tagName=="DIV"){f.onclick=function(){location.href=j}}}if(n.onFail){var l=n.onFail.call(this);if(typeof l=="string"){f.innerHTML=l}}}}if(h){window[n.id]=document.getElementById(n.id)}i(this,{getRoot:function(){return f},getOptions:function(){return n},getConf:function(){return m},getApi:function(){return f.firstChild}})}if(c){jQuery.tools=jQuery.tools||{version:"3.2.12"};jQuery.tools.flashembed={conf:b};jQuery.fn.flashembed=function(l,f){return this.each(function(){$(this).data("flashembed",flashembed(this,l,f))})}}}();; /** @namespace H5P */ H5P.VideoYouTube = (function ($) { /** * YouTube video player for H5P. * * @class * @param {Array} sources Video files to use * @param {Object} options Settings for the player * @param {Object} l10n Localization strings */ function YouTube(sources, options, l10n) { var self = this; var player; var playbackRate = 1; var id = 'h5p-youtube-' + numInstances; numInstances++; var $wrapper = $('<div/>'); var $placeholder = $('<div/>', { id: id, text: l10n.loading }).appendTo($wrapper); // Optional placeholder // var $placeholder = $('<iframe id="' + id + '" type="text/html" width="640" height="360" src="https://www.youtube.com/embed/' + getId(sources[0].path) + '?enablejsapi=1&origin=' + encodeURIComponent(ORIGIN) + '&autoplay=' + (options.autoplay ? 1 : 0) + '&controls=' + (options.controls ? 1 : 0) + '&disabledkb=' + (options.controls ? 0 : 1) + '&fs=0&loop=' + (options.loop ? 1 : 0) + '&rel=0&showinfo=0&iv_load_policy=3" frameborder="0"></iframe>').appendTo($wrapper); /** * Use the YouTube API to create a new player * * @private */ var create = function () { if (!$placeholder.is(':visible') || player !== undefined) { return; } if (window.YT === undefined) { // Load API first loadAPI(create); return; } if (YT.Player === undefined) { return; } var width = $wrapper.width(); if (width < 200) { width = 200; } var loadCaptionsModule = true; var videoId = getId(sources[0].path); player = new YT.Player(id, { width: width, height: width * (9/16), videoId: videoId, playerVars: { origin: ORIGIN, autoplay: options.autoplay ? 1 : 0, controls: options.controls ? 1 : 0, disablekb: options.controls ? 0 : 1, fs: 0, loop: options.loop ? 1 : 0, playlist: options.loop ? videoId : undefined, rel: 0, showinfo: 0, iv_load_policy: 3, wmode: "opaque", start: options.startAt, playsinline: 1 }, events: { onReady: function () { self.trigger('ready'); self.trigger('loaded'); }, onApiChange: function () { if (loadCaptionsModule) { loadCaptionsModule = false; // Always load captions player.loadModule('captions'); } var trackList; try { // Grab tracklist from player trackList = player.getOption('captions', 'tracklist'); } catch (err) {} if (trackList && trackList.length) { // Format track list into valid track options var trackOptions = []; for (var i = 0; i < trackList.length; i++) { trackOptions.push(new H5P.Video.LabelValue(trackList[i].displayName, trackList[i].languageCode)); } // Captions are ready for loading self.trigger('captions', trackOptions); } }, onStateChange: function (state) { if (state.data > -1 && state.data < 4) { // Fix for keeping playback rate in IE11 if (H5P.Video.IE11_PLAYBACK_RATE_FIX && state.data === H5P.Video.PLAYING && playbackRate !== 1) { // YT doesn't know that IE11 changed the rate so it must be reset before it's set to the correct value player.setPlaybackRate(1); player.setPlaybackRate(playbackRate); } // End IE11 fix self.trigger('stateChange', state.data); } }, onPlaybackQualityChange: function (quality) { self.trigger('qualityChange', quality.data); }, onPlaybackRateChange: function (playbackRate) { self.trigger('playbackRateChange', playbackRate.data); }, onError: function (error) { var message; switch (error.data) { case 2: message = l10n.invalidYtId; break; case 100: message = l10n.unknownYtId; break; case 101: case 150: message = l10n.restrictedYt; break; default: message = l10n.unknownError + ' ' + error.data; break; } self.trigger('error', message); } } }); }; /** * Indicates if the video must be clicked for it to start playing. * For instance YouTube videos on iPad must be pressed to start playing. * * @public */ self.pressToPlay = navigator.userAgent.match(/iPad/i) ? true : false; /** * Appends the video player to the DOM. * * @public * @param {jQuery} $container */ self.appendTo = function ($container) { $container.addClass('h5p-youtube').append($wrapper); create(); }; /** * Get list of available qualities. Not available until after play. * * @public * @returns {Array} */ self.getQualities = function () { if (!player || !player.getAvailableQualityLevels) { return; } var qualities = player.getAvailableQualityLevels(); if (!qualities.length) { return; // No qualities } // Add labels for (var i = 0; i < qualities.length; i++) { var quality = qualities[i]; var label = (LABELS[quality] !== undefined ? LABELS[quality] : 'Unknown'); // TODO: l10n qualities[i] = { name: quality, label: LABELS[quality] }; } return qualities; }; /** * Get current playback quality. Not available until after play. * * @public * @returns {String} */ self.getQuality = function () { if (!player || !player.getPlaybackQuality) { return; } var quality = player.getPlaybackQuality(); return quality === 'unknown' ? undefined : quality; }; /** * Set current playback quality. Not available until after play. * Listen to event "qualityChange" to check if successful. * * @public * @params {String} [quality] */ self.setQuality = function (quality) { if (!player || !player.setPlaybackQuality) { return; } player.setPlaybackQuality(quality); }; /** * Start the video. * * @public */ self.play = function () { if (!player || !player.playVideo) { self.on('ready', self.play); return; } player.playVideo(); }; /** * Pause the video. * * @public */ self.pause = function () { self.off('ready', self.play); if (!player || !player.pauseVideo) { return; } player.pauseVideo(); }; /** * Seek video to given time. * * @public * @param {Number} time */ self.seek = function (time) { if (!player || !player.seekTo) { return; } player.seekTo(time, true); }; /** * Get elapsed time since video beginning. * * @public * @returns {Number} */ self.getCurrentTime = function () { if (!player || !player.getCurrentTime) { return; } return player.getCurrentTime(); }; /** * Get total video duration time. * * @public * @returns {Number} */ self.getDuration = function () { if (!player || !player.getDuration) { return; } return player.getDuration(); }; /** * Get percentage of video that is buffered. * * @public * @returns {Number} Between 0 and 100 */ self.getBuffered = function () { if (!player || !player.getVideoLoadedFraction) { return; } return player.getVideoLoadedFraction() * 100; }; /** * Turn off video sound. * * @public */ self.mute = function () { if (!player || !player.mute) { return; } player.mute(); }; /** * Turn on video sound. * * @public */ self.unMute = function () { if (!player || !player.unMute) { return; } player.unMute(); }; /** * Check if video sound is turned on or off. * * @public * @returns {Boolean} */ self.isMuted = function () { if (!player || !player.isMuted) { return; } return player.isMuted(); }; /** * Return the video sound level. * * @public * @returns {Number} Between 0 and 100. */ self.getVolume = function () { if (!player || !player.getVolume) { return; } return player.getVolume(); }; /** * Set video sound level. * * @public * @param {Number} level Between 0 and 100. */ self.setVolume = function (level) { if (!player || !player.setVolume) { return; } player.setVolume(level); }; /** * Get list of available playback rates. * * @public * @returns {Array} available playback rates */ self.getPlaybackRates = function () { if (!player || !player.getAvailablePlaybackRates) { return; } var playbackRates = player.getAvailablePlaybackRates(); if (!playbackRates.length) { return; // No rates, but the array should contain at least 1 } return playbackRates; }; /** * Get current playback rate. * * @public * @returns {Number} such as 0.25, 0.5, 1, 1.25, 1.5 and 2 */ self.getPlaybackRate = function () { if (!player || !player.getPlaybackRate) { return; } return player.getPlaybackRate(); }; /** * Set current playback rate. * Listen to event "playbackRateChange" to check if successful. * * @public * @params {Number} suggested rate that may be rounded to supported values */ self.setPlaybackRate = function (newPlaybackRate) { if (!player || !player.setPlaybackRate) { return; } playbackRate = Number(newPlaybackRate); player.setPlaybackRate(playbackRate); }; /** * Set current captions track. * * @param {H5P.Video.LabelValue} Captions track to show during playback */ self.setCaptionsTrack = function (track) { player.setOption('captions', 'track', track ? {languageCode: track.value} : {}); }; /** * Figure out which captions track is currently used. * * @return {H5P.Video.LabelValue} Captions track */ self.getCaptionsTrack = function () { var track = player.getOption('captions', 'track'); return (track.languageCode ? new H5P.Video.LabelValue(track.displayName, track.languageCode) : null); }; // Respond to resize events by setting the YT player size. self.on('resize', function () { if (!$wrapper.is(':visible')) { return; } if (!player) { // Player isn't created yet. Try again. create(); return; } // Use as much space as possible $wrapper.css({ width: '100%', height: '100%' }); var width = $wrapper[0].clientWidth; var height = options.fit ? $wrapper[0].clientHeight : (width * (9/16)); // Validate height before setting if (height > 0) { // Set size $wrapper.css({ width: width + 'px', height: height + 'px' }); player.setSize(width, height); } }); } /** * Check to see if we can play any of the given sources. * * @public * @static * @param {Array} sources * @returns {Boolean} */ YouTube.canPlay = function (sources) { return getId(sources[0].path); }; /** * Find id of YouTube video from given URL. * * @private * @param {String} url * @returns {String} YouTube video identifier */ var getId = function (url) { // Has some false positives, but should cover all regular URLs that people can find var matches = url.match(/(?:(?:youtube.com\/(?:attribution_link\?(?:\S+))?(?:v\/|embed\/|watch\/|(?:user\/(?:\S+)\/)?watch(?:\S+)v\=))|(?:youtu.be\/|y2u.be\/))([A-Za-z0-9_-]{11})/i); if (matches && matches[1]) { return matches[1]; } }; /** * Load the IFrame Player API asynchronously. */ var loadAPI = function (loaded) { if (window.onYouTubeIframeAPIReady !== undefined) { // Someone else is loading, hook in var original = window.onYouTubeIframeAPIReady; window.onYouTubeIframeAPIReady = function (id) { loaded(id); original(id); }; } else { // Load the API our self var tag = document.createElement('script'); tag.src = "https://www.youtube.com/iframe_api"; var firstScriptTag = document.getElementsByTagName('script')[0]; firstScriptTag.parentNode.insertBefore(tag, firstScriptTag); window.onYouTubeIframeAPIReady = loaded; } }; /** @constant {Object} */ var LABELS = { highres: '2160p', // Old API support hd2160: '2160p', // (New API) hd1440: '1440p', hd1080: '1080p', hd720: '720p', large: '480p', medium: '360p', small: '240p', tiny: '144p', auto: 'Auto' }; /** @private */ var numInstances = 0; // Extract the current origin (used for security) var ORIGIN = window.location.href.match(/http[s]?:\/\/[^\/]+/); ORIGIN = !ORIGIN || ORIGIN[0] === undefined ? undefined : ORIGIN[0]; // ORIGIN = undefined is needed to support fetching file from device local storage return YouTube; })(H5P.jQuery); // Register video handler H5P.videoHandlers = H5P.videoHandlers || []; H5P.videoHandlers.push(H5P.VideoYouTube); ; /** @namespace H5P */ H5P.VideoPanopto = (function ($) { /** * Panopto video player for H5P. * * @class * @param {Array} sources Video files to use * @param {Object} options Settings for the player * @param {Object} l10n Localization strings */ function Panopto(sources, options, l10n) { var self = this; var player; var playbackRate = 1; var id = 'h5p-panopto-' + numInstances; numInstances++; var $wrapper = $('<div/>'); var $placeholder = $('<div/>', { id: id, html: '<div>' + l10n.loading + '</div>' }).appendTo($wrapper); /** * Use the Panopto API to create a new player * * @private */ var create = function () { if (!$placeholder.is(':visible') || player !== undefined) { return; } if (window.EmbedApi === undefined) { // Load API first loadAPI(create); return; } var width = $wrapper.width(); if (width < 200) { width = 200; } const videoId = getId(sources[0].path); player = new EmbedApi(id, { width: width, height: width * (9/16), serverName: videoId[0], sessionId: videoId[1], videoParams: { // Optional interactivity: 'none', showtitle: false, autohide: true, offerviewer: false, autoplay: !!options.autoplay, showbrand: false, start: 0, hideoverlay: !options.controls, }, events: { onIframeReady: function () { $placeholder.children(0).text(''); player.loadVideo(); }, onReady: function () { self.trigger('loaded'); if (player.hasCaptions()) { const captions = []; const captionTracks = player.getCaptionTracks(); for (trackIndex in captionTracks) { captions.push(new H5P.Video.LabelValue(captionTracks[trackIndex], trackIndex)); } // Select active track currentTrack = player.getSelectedCaptionTrack(); currentTrack = captions[currentTrack] ? captions[currentTrack] : null; self.trigger('captions', captions); } self.pause(); }, onStateChange: function (state) { // TODO: Playback rate fix for IE11? if (state > -1 && state < 4) { self.trigger('stateChange', state); } }, onPlaybackRateChange: function () { self.trigger('playbackRateChange', self.getPlaybackRate()); }, onError: function () { self.trigger('error', l10n.unknownError); }, onLoginShown: function () { $placeholder.children().first().remove(); // Remove loading message self.trigger('loaded'); // Resize parent } } }); }; /** * Indicates if the video must be clicked for it to start playing. * This is always true for Panopto since all videos auto play. * * @public */ self.pressToPlay = true; /** * Appends the video player to the DOM. * * @public * @param {jQuery} $container */ self.appendTo = function ($container) { $container.addClass('h5p-panopto').append($wrapper); create(); }; /** * Get list of available qualities. Not available until after play. * * @public * @returns {Array} */ self.getQualities = function () { // Not available for Panopto }; /** * Get current playback quality. Not available until after play. * * @public * @returns {String} */ self.getQuality = function () { // Not available for Panopto }; /** * Set current playback quality. Not available until after play. * Listen to event "qualityChange" to check if successful. * * @public * @params {String} [quality] */ self.setQuality = function (quality) { // Not available for Panopto }; /** * Start the video. * * @public */ self.play = function () { if (!player || !player.playVideo) { return; } player.playVideo(); }; /** * Pause the video. * * @public */ self.pause = function () { if (!player || !player.pauseVideo) { return; } try { player.pauseVideo(); } catch (err) { // Swallow Panopto throwing an error. This has been seen in the authoring // tool if Panopto has been used inside Iv inside CP } }; /** * Seek video to given time. * * @public * @param {Number} time */ self.seek = function (time) { if (!player || !player.seekTo) { return; } player.seekTo(time); }; /** * Get elapsed time since video beginning. * * @public * @returns {Number} */ self.getCurrentTime = function () { if (!player || !player.getCurrentTime) { return; } return player.getCurrentTime(); }; /** * Get total video duration time. * * @public * @returns {Number} */ self.getDuration = function () { if (!player || !player.getDuration) { return; } return player.getDuration(); }; /** * Get percentage of video that is buffered. * * @public * @returns {Number} Between 0 and 100 */ self.getBuffered = function () { // Not available for Panopto }; /** * Turn off video sound. * * @public */ self.mute = function () { if (!player || !player.muteVideo) { return; } player.muteVideo(); }; /** * Turn on video sound. * * @public */ self.unMute = function () { if (!player || !player.unmuteVideo) { return; } player.unmuteVideo(); }; /** * Check if video sound is turned on or off. * * @public * @returns {Boolean} */ self.isMuted = function () { if (!player || !player.isMuted) { return; } return player.isMuted(); }; /** * Return the video sound level. * * @public * @returns {Number} Between 0 and 100. */ self.getVolume = function () { if (!player || !player.getVolume) { return; } return player.getVolume() * 100; }; /** * Set video sound level. * * @public * @param {Number} level Between 0 and 100. */ self.setVolume = function (level) { if (!player || !player.setVolume) { return; } player.setVolume(level/100); }; /** * Get list of available playback rates. * * @public * @returns {Array} available playback rates */ self.getPlaybackRates = function () { return [0.25, 0.5, 1, 1.25, 1.5, 2]; }; /** * Get current playback rate. * * @public * @returns {Number} such as 0.25, 0.5, 1, 1.25, 1.5 and 2 */ self.getPlaybackRate = function () { if (!player || !player.getPlaybackRate) { return; } return player.getPlaybackRate(); }; /** * Set current playback rate. * Listen to event "playbackRateChange" to check if successful. * * @public * @params {Number} suggested rate that may be rounded to supported values */ self.setPlaybackRate = function (newPlaybackRate) { if (!player || !player.setPlaybackRate) { return; } player.setPlaybackRate(newPlaybackRate); }; /** * Set current captions track. * * @param {H5P.Video.LabelValue} Captions track to show during playback */ self.setCaptionsTrack = function (track) { if (!track) { console.log('Disable captions'); player.disableCaptions(); currentTrack = null; } else { console.log('Set captions', track.value); player.enableCaptions(track.value + ''); currentTrack = track; } }; /** * Figure out which captions track is currently used. * * @return {H5P.Video.LabelValue} Captions track */ self.getCaptionsTrack = function () { return currentTrack; // No function for getting active caption track? }; // Respond to resize events by setting the player size. self.on('resize', function () { if (!$wrapper.is(':visible')) { return; } if (!player) { // Player isn't created yet. Try again. create(); return; } // Use as much space as possible $wrapper.css({ width: '100%', height: '100%' }); var width = $wrapper[0].clientWidth; var height = options.fit ? $wrapper[0].clientHeight : (width * (9/16)); // Set size $wrapper.css({ width: width + 'px', height: height + 'px' }); const $iframe = $placeholder.children('iframe'); if ($iframe.length) { $iframe.attr('width', width); $iframe.attr('height', height); } }); let currentTrack; } /** * Check to see if we can play any of the given sources. * * @public * @static * @param {Array} sources * @returns {Boolean} */ Panopto.canPlay = function (sources) { return getId(sources[0].path); }; /** * Find id of YouTube video from given URL. * * @private * @param {String} url * @returns {String} Panopto video identifier */ var getId = function (url) { const matches = url.match(/^[^\/]+:\/\/([^\/]*panopto\.[^\/]+)\/Panopto\/.+\?id=(.+)$/); if (matches && matches.length === 3) { return [matches[1], matches[2]]; } }; /** * Load the IFrame Player API asynchronously. */ var loadAPI = function (loaded) { if (window.onPanoptoEmbedApiReady !== undefined) { // Someone else is loading, hook in var original = window.onPanoptoEmbedApiReady; window.onPanoptoEmbedApiReady = function (id) { loaded(id); original(id); }; } else { // Load the API our self var tag = document.createElement('script'); tag.src = 'https://developers.panopto.com/scripts/embedapi.min.js'; var firstScriptTag = document.getElementsByTagName('script')[0]; firstScriptTag.parentNode.insertBefore(tag, firstScriptTag); window.onPanoptoEmbedApiReady = loaded; } }; /** @private */ var numInstances = 0; return Panopto; })(H5P.jQuery); // Register video handler H5P.videoHandlers = H5P.videoHandlers || []; H5P.videoHandlers.push(H5P.VideoPanopto); ; /** @namespace H5P */ H5P.VideoHtml5 = (function ($) { /** * HTML5 video player for H5P. * * @class * @param {Array} sources Video files to use * @param {Object} options Settings for the player * @param {Object} l10n Localization strings */ function Html5(sources, options, l10n) { var self = this; /** * Small helper to ensure all video sources get the same cache buster. * * @private * @param {Object} source * @return {string} */ const getCrossOriginPath = function (source) { let path = H5P.getPath(source.path, self.contentId); if (video.crossOrigin !== null && H5P.addQueryParameter && H5PIntegration.crossoriginCacheBuster) { path = H5P.addQueryParameter(path, H5PIntegration.crossoriginCacheBuster); } return path }; /** * Register track to video * * @param {Object} trackData Track object * @param {string} trackData.kind Kind of track * @param {Object} trackData.track Source path * @param {string} [trackData.label] Label of track * @param {string} [trackData.srcLang] Language code */ const addTrack = function (trackData) { // Skip invalid tracks if (!trackData.kind || !trackData.track.path) { return; } var track = document.createElement('track'); track.kind = trackData.kind; track.src = getCrossOriginPath(trackData.track); // Uses same crossOrigin as parent. You cannot mix. if (trackData.label) { track.label = trackData.label; } if (trackData.srcLang) { track.srcLang = trackData.srcLang; } return track; }; /** * Small helper to set the inital video source. * Useful if some of the loading happens asynchronously. * NOTE: Setting the crossOrigin must happen before any of the * sources(poster, tracks etc.) are loaded * * @private */ const setInitialSource = function () { if (qualities[currentQuality] === undefined) { return; } if (H5P.setSource !== undefined) { H5P.setSource(video, qualities[currentQuality].source, self.contentId) } else { // Backwards compatibility (H5P < v1.22) const srcPath = H5P.getPath(qualities[currentQuality].source.path, self.contentId); if (H5P.getCrossOrigin !== undefined) { var crossOrigin = H5P.getCrossOrigin(srcPath); video.setAttribute('crossorigin', crossOrigin !== null ? crossOrigin : 'anonymous'); } video.src = srcPath; } // Add poster if provided if (options.poster) { video.poster = getCrossOriginPath(options.poster); // Uses same crossOrigin as parent. You cannot mix. } // Register tracks options.tracks.forEach(function (track, i) { var trackElement = addTrack(track); if (i === 0) { trackElement.default = true; } if (trackElement) { video.appendChild(trackElement); } }); }; /** * Displayed when the video is buffering * @private */ var $throbber = $('<div/>', { 'class': 'h5p-video-loading' }); /** * Used to display error messages * @private */ var $error = $('<div/>', { 'class': 'h5p-video-error' }); /** * Keep track of current state when changing quality. * @private */ var stateBeforeChangingQuality; var currentTimeBeforeChangingQuality; /** * Avoids firing the same event twice. * @private */ var lastState; /** * Keeps track whether or not the video has been loaded. * @private */ var isLoaded = false; /** * * @private */ var playbackRate = 1; var skipRateChange = false; // Create player var video = document.createElement('video'); // Sort sources into qualities var qualities = getQualities(sources, video); var currentQuality; numQualities = 0; for (let quality in qualities) { numQualities++; } if (numQualities > 1 && H5P.VideoHtml5.getExternalQuality !== undefined) { H5P.VideoHtml5.getExternalQuality(sources, function (chosenQuality) { if (qualities[chosenQuality] !== undefined) { currentQuality = chosenQuality; } setInitialSource(); }); } else { // Select quality and source currentQuality = getPreferredQuality(); if (currentQuality === undefined || qualities[currentQuality] === undefined) { // No preferred quality, pick the first. for (currentQuality in qualities) { if (qualities.hasOwnProperty(currentQuality)) { break; } } } setInitialSource(); } // Setting webkit-playsinline, which makes iOS 10 beeing able to play video // inside browser. video.setAttribute('webkit-playsinline', ''); video.setAttribute('playsinline', ''); video.setAttribute('preload', 'metadata'); // Remove buttons in Chrome's video player: let controlsList = 'nodownload'; if (options.disableFullscreen) { controlsList += ' nofullscreen'; } if (options.disableRemotePlayback) { controlsList += ' noremoteplayback'; } video.setAttribute('controlsList', controlsList); // Remove picture in picture as it interfers with other video players video.disablePictureInPicture = true; // Set options video.disableRemotePlayback = (options.disableRemotePlayback ? true : false); video.controls = (options.controls ? true : false); video.autoplay = (options.autoplay ? true : false); video.loop = (options.loop ? true : false); video.className = 'h5p-video'; video.style.display = 'block'; if (options.fit) { // Style is used since attributes with relative sizes aren't supported by IE9. video.style.width = '100%'; video.style.height = '100%'; } /** * Helps registering events. * * @private * @param {String} native Event name * @param {String} h5p Event name * @param {String} [arg] Optional argument */ var mapEvent = function (native, h5p, arg) { video.addEventListener(native, function () { switch (h5p) { case 'stateChange': if (lastState === arg) { return; // Avoid firing event twice. } var validStartTime = options.startAt && options.startAt > 0; if (arg === H5P.Video.PLAYING && validStartTime) { video.currentTime = options.startAt; delete options.startAt; } break; case 'loaded': isLoaded = true; if (stateBeforeChangingQuality !== undefined) { return; // Avoid loaded event when changing quality. } // Remove any errors if ($error.is(':visible')) { $error.remove(); } if (OLD_ANDROID_FIX) { var andLoaded = function () { video.removeEventListener('durationchange', andLoaded, false); // On Android seeking isn't ready until after play. self.trigger(h5p); }; video.addEventListener('durationchange', andLoaded, false); return; } break; case 'error': // Handle error and get message. arg = error(arguments[0], arguments[1]); break; case 'playbackRateChange': // Fix for keeping playback rate in IE11 if (skipRateChange) { skipRateChange = false; return; // Avoid firing event when changing back } if (H5P.Video.IE11_PLAYBACK_RATE_FIX && playbackRate != video.playbackRate) { // Intentional // Prevent change in playback rate not triggered by the user video.playbackRate = playbackRate; skipRateChange = true; return; } // End IE11 fix arg = self.getPlaybackRate(); break; } self.trigger(h5p, arg); }, false); }; /** * Handle errors from the video player. * * @private * @param {Object} code Error * @param {String} [message] * @returns {String} Human readable error message. */ var error = function (code, message) { if (code instanceof Event) { // No error code if (!code.target.error) { return ''; } switch (code.target.error.code) { case MediaError.MEDIA_ERR_ABORTED: message = l10n.aborted; break; case MediaError.MEDIA_ERR_NETWORK: message = l10n.networkFailure; break; case MediaError.MEDIA_ERR_DECODE: message = l10n.cannotDecode; break; case MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED: message = l10n.formatNotSupported; break; case MediaError.MEDIA_ERR_ENCRYPTED: message = l10n.mediaEncrypted; break; } } if (!message) { message = l10n.unknownError; } // Hide throbber $throbber.remove(); // Display error message to user $error.text(message).insertAfter(video); // Pass message to our error event return message; }; /** * Appends the video player to the DOM. * * @public * @param {jQuery} $container */ self.appendTo = function ($container) { $container.append(video); }; /** * Get list of available qualities. Not available until after play. * * @public * @returns {Array} */ self.getQualities = function () { // Create reverse list var options = []; for (var q in qualities) { if (qualities.hasOwnProperty(q)) { options.splice(0, 0, { name: q, label: qualities[q].label }); } } if (options.length < 2) { // Do not return if only one quality. return; } return options; }; /** * Get current playback quality. Not available until after play. * * @public * @returns {String} */ self.getQuality = function () { return currentQuality; }; /** * Set current playback quality. Not available until after play. * Listen to event "qualityChange" to check if successful. * * @public * @params {String} [quality] */ self.setQuality = function (quality) { if (qualities[quality] === undefined || quality === currentQuality) { return; // Invalid quality } // Keep track of last choice setPreferredQuality(quality); // Avoid multiple loaded events if changing quality multiple times. if (!stateBeforeChangingQuality) { // Keep track of last state stateBeforeChangingQuality = lastState; // Keep track of current time currentTimeBeforeChangingQuality = video.currentTime; // Seek and start video again after loading. var loaded = function () { video.removeEventListener('loadedmetadata', loaded, false); if (OLD_ANDROID_FIX) { var andLoaded = function () { video.removeEventListener('durationchange', andLoaded, false); // On Android seeking isn't ready until after play. self.seek(currentTimeBeforeChangingQuality); }; video.addEventListener('durationchange', andLoaded, false); } else { // Seek to current time. self.seek(currentTimeBeforeChangingQuality); } // Always play to get image. video.play(); if (stateBeforeChangingQuality !== H5P.Video.PLAYING) { // Do not resume playing video.pause(); } // Done changing quality stateBeforeChangingQuality = undefined; // Remove any errors if ($error.is(':visible')) { $error.remove(); } }; video.addEventListener('loadedmetadata', loaded, false); } // Keep track of current quality currentQuality = quality; self.trigger('qualityChange', currentQuality); // Display throbber self.trigger('stateChange', H5P.Video.BUFFERING); // Change source video.src = getCrossOriginPath(qualities[quality].source); // (iPad does not support #t=). // Note: Optional tracks use same crossOrigin as the original. You cannot mix. // Remove poster so it will not show during quality change video.removeAttribute('poster'); }; /** * Starts the video. * * @public * @return {Promise|undefined} May return a Promise that resolves when * play has been processed. */ self.play = function () { if ($error.is(':visible')) { return; } if (!isLoaded) { // Make sure video is loaded before playing video.load(); } return video.play(); }; /** * Pauses the video. * * @public */ self.pause = function () { video.pause(); }; /** * Seek video to given time. * * @public * @param {Number} time */ self.seek = function (time) { if (lastState === undefined) { // Make sure we always play before we seek to get an image. // If not iOS devices will reset currentTime when pressing play. video.play(); video.pause(); } video.currentTime = time; }; /** * Get elapsed time since video beginning. * * @public * @returns {Number} */ self.getCurrentTime = function () { return video.currentTime; }; /** * Get total video duration time. * * @public * @returns {Number} */ self.getDuration = function () { if (isNaN(video.duration)) { return; } return video.duration; }; /** * Get percentage of video that is buffered. * * @public * @returns {Number} Between 0 and 100 */ self.getBuffered = function () { // Find buffer currently playing from var buffered = 0; for (var i = 0; i < video.buffered.length; i++) { var from = video.buffered.start(i); var to = video.buffered.end(i); if (video.currentTime > from && video.currentTime < to) { buffered = to; break; } } // To percentage return buffered ? (buffered / video.duration) * 100 : 0; }; /** * Turn off video sound. * * @public */ self.mute = function () { video.muted = true; }; /** * Turn on video sound. * * @public */ self.unMute = function () { video.muted = false; }; /** * Check if video sound is turned on or off. * * @public * @returns {Boolean} */ self.isMuted = function () { return video.muted; }; /** * Returns the video sound level. * * @public * @returns {Number} Between 0 and 100. */ self.getVolume = function () { return video.volume * 100; }; /** * Set video sound level. * * @public * @param {Number} level Between 0 and 100. */ self.setVolume = function (level) { video.volume = level / 100; }; /** * Get list of available playback rates. * * @public * @returns {Array} available playback rates */ self.getPlaybackRates = function () { /* * not sure if there's a common rule about determining good speeds * using Google's standard options via a constant for setting */ var playbackRates = PLAYBACK_RATES; return playbackRates; }; /** * Get current playback rate. * * @public * @returns {Number} such as 0.25, 0.5, 1, 1.25, 1.5 and 2 */ self.getPlaybackRate = function () { return video.playbackRate; }; /** * Set current playback rate. * Listen to event "playbackRateChange" to check if successful. * * @public * @params {Number} suggested rate that may be rounded to supported values */ self.setPlaybackRate = function (newPlaybackRate) { playbackRate = newPlaybackRate; video.playbackRate = newPlaybackRate; }; /** * Set current captions track. * * @param {H5P.Video.LabelValue} Captions track to show during playback */ self.setCaptionsTrack = function (track) { for (var i = 0; i < video.textTracks.length; i++) { video.textTracks[i].mode = (track && track.value === i ? 'showing' : 'disabled'); } }; /** * Figure out which captions track is currently used. * * @return {H5P.Video.LabelValue} Captions track */ self.getCaptionsTrack = function () { for (var i = 0; i < video.textTracks.length; i++) { if (video.textTracks[i].mode === 'showing') { return new H5P.Video.LabelValue(video.textTracks[i].label, i); } } return null; }; // Register event listeners mapEvent('ended', 'stateChange', H5P.Video.ENDED); mapEvent('playing', 'stateChange', H5P.Video.PLAYING); mapEvent('pause', 'stateChange', H5P.Video.PAUSED); mapEvent('waiting', 'stateChange', H5P.Video.BUFFERING); mapEvent('loadedmetadata', 'loaded'); mapEvent('canplay', 'canplay'); mapEvent('error', 'error'); mapEvent('ratechange', 'playbackRateChange'); if (!video.controls) { // Disable context menu(right click) to prevent controls. video.addEventListener('contextmenu', function (event) { event.preventDefault(); }, false); } // Display throbber when buffering/loading video. self.on('stateChange', function (event) { var state = event.data; lastState = state; if (state === H5P.Video.BUFFERING) { $throbber.insertAfter(video); } else { $throbber.remove(); } }); // Load captions after the video is loaded self.on('loaded', function () { nextTick(function () { var textTracks = []; for (var i = 0; i < video.textTracks.length; i++) { textTracks.push(new H5P.Video.LabelValue(video.textTracks[i].label, i)); } if (textTracks.length) { self.trigger('captions', textTracks); } }); }); // Alternative to 'canplay' event /*self.on('resize', function () { if (video.offsetParent === null) { return; } video.style.width = '100%'; video.style.height = '100%'; var width = video.clientWidth; var height = options.fit ? video.clientHeight : (width * (video.videoHeight / video.videoWidth)); video.style.width = width + 'px'; video.style.height = height + 'px'; });*/ // Video controls are ready nextTick(function () { self.trigger('ready'); }); } /** * Check to see if we can play any of the given sources. * * @public * @static * @param {Array} sources * @returns {Boolean} */ Html5.canPlay = function (sources) { var video = document.createElement('video'); if (video.canPlayType === undefined) { return false; // Not supported } // Cycle through sources for (var i = 0; i < sources.length; i++) { var type = getType(sources[i]); if (type && video.canPlayType(type) !== '') { // We should be able to play this return true; } } return false; }; /** * Find source type. * * @private * @param {Object} source * @returns {String} */ var getType = function (source) { var type = source.mime; if (!type) { // Try to get type from URL var matches = source.path.match(/\.(\w+)$/); if (matches && matches[1]) { type = 'video/' + matches[1]; } } if (type && source.codecs) { // Add codecs type += '; codecs="' + source.codecs + '"'; } return type; }; /** * Sort sources into qualities. * * @private * @static * @param {Array} sources * @param {Object} video * @returns {Object} Quality mapping */ var getQualities = function (sources, video) { var qualities = {}; var qualityIndex = 1; var lastQuality; // Cycle through sources for (var i = 0; i < sources.length; i++) { var source = sources[i]; // Find and update type. var type = source.type = getType(source); // Check if we support this type var isPlayable = type && (type === 'video/unknown' || video.canPlayType(type) !== ''); if (!isPlayable) { continue; // We cannot play this source } if (source.quality === undefined) { /** * No quality metadata. Create a quality tag to separate multiple sources of the same type, * e.g. if two mp4 files with different quality has been uploaded */ if (lastQuality === undefined || qualities[lastQuality].source.type === type) { // Create a new quality tag source.quality = { name: 'q' + qualityIndex, label: (source.metadata && source.metadata.qualityName) ? source.metadata.qualityName : 'Quality ' + qualityIndex // TODO: l10n }; qualityIndex++; } else { /** * Assumes quality already exists in a different format. * Uses existing label for this quality. */ source.quality = qualities[lastQuality].source.quality; } } // Log last quality lastQuality = source.quality.name; // Look to see if quality exists var quality = qualities[lastQuality]; if (quality) { // We have a source with this quality. Check if we have a better format. if (source.mime.split('/')[1] === PREFERRED_FORMAT) { quality.source = source; } } else { // Add new source with quality. qualities[source.quality.name] = { label: source.quality.label, source: source }; } } return qualities; }; /** * Set preferred video quality. * * @private * @static * @param {String} quality Index of preferred quality */ var setPreferredQuality = function (quality) { try { localStorage.setItem('h5pVideoQuality', quality); } catch (err) { console.warn('Unable to set preferred video quality, localStorage is not available.'); } }; /** * Get preferred video quality. * * @private * @static * @returns {String} Index of preferred quality */ var getPreferredQuality = function () { // First check localStorage let quality; try { quality = localStorage.getItem('h5pVideoQuality'); } catch (err) { console.warn('Unable to retrieve preferred video quality from localStorage.'); } if (!quality) { try { // The fallback to old cookie solution var settings = document.cookie.split(';'); for (var i = 0; i < settings.length; i++) { var setting = settings[i].split('='); if (setting[0] === 'H5PVideoQuality') { quality = setting[1]; break; } } } catch (err) { console.warn('Unable to retrieve preferred video quality from cookie.'); } } return quality; }; /** * Helps schedule a task for the next tick. * @param {function} task */ var nextTick = function (task) { setTimeout(task, 0); }; /** @constant {Boolean} */ var OLD_ANDROID_FIX = false; /** @constant {Boolean} */ var PREFERRED_FORMAT = 'mp4'; /** @constant {Object} */ var PLAYBACK_RATES = [0.25, 0.5, 1, 1.25, 1.5, 2]; if (navigator.userAgent.indexOf('Android') !== -1) { // We have Android, check version. var version = navigator.userAgent.match(/AppleWebKit\/(\d+\.?\d*)/); if (version && version[1] && Number(version[1]) <= 534.30) { // Include fix for devices running the native Android browser. // (We don't know when video was fixed, so the number is just the lastest // native android browser we found.) OLD_ANDROID_FIX = true; } } else { if (navigator.userAgent.indexOf('Chrome') !== -1) { // If we're using chrome on a device that isn't Android, prefer the webm // format. This is because Chrome has trouble with some mp4 codecs. PREFERRED_FORMAT = 'webm'; } } return Html5; })(H5P.jQuery); // Register video handler H5P.videoHandlers = H5P.videoHandlers || []; H5P.videoHandlers.push(H5P.VideoHtml5); ; /** @namespace H5P */ H5P.VideoFlash = (function ($) { /** * Flash video player for H5P. * * @class * @param {Array} sources Video files to use * @param {Object} options Settings for the player */ function Flash(sources, options) { var self = this; // Player wrapper var $wrapper = $('<div/>', { 'class': 'h5p-video-flash', css: { width: '100%', height: '100%' } }); /** * Used to display error messages * @private */ var $error = $('<div/>', { 'class': 'h5p-video-error' }); /** * Keep track of current state when changing quality. * @private */ var stateBeforeChangingQuality; var currentTimeBeforeChangingQuality; // Sort sources into qualities //var qualities = getQualities(sources); var currentQuality; // Create player options var playerOptions = { buffering: true, clip: { url: sources[0].path, // getPreferredQuality(), autoPlay: options.autoplay, autoBuffering: true, scaling: 'fit', onSeek: function () { if (stateBeforeChangingQuality) { // ???? } }, onMetaData: function () { setTimeout(function () { if (stateBeforeChangingQuality !== undefined) { fp.seek(currentTimeBeforeChangingQuality); if (stateBeforeChangingQuality === H5P.Video.PLAYING) { // Resume play fp.play(); } // Done changing quality stateBeforeChangingQuality = undefined; // Remove any errors if ($error.is(':visible')) { $error.remove(); } } else { self.trigger('ready'); self.trigger('loaded'); } }, 0); // Run on next tick }, onBegin: function () { self.trigger('stateChange', H5P.Video.PLAYING); }, onResume: function () { self.trigger('stateChange', H5P.Video.PLAYING); }, onPause: function () { self.trigger('stateChange', H5P.Video.PAUSED); }, onFinish: function () { self.trigger('stateChange', H5P.Video.ENDED); }, onError: function (code, message) { console.log('ERROR', code, message); // TODO self.trigger('error', message); } }, plugins: { controls: null }, play: null, // Disable overlay controls onPlaylistReplace: function () { that.playlistReplaced(); } }; if (options.controls) { playerOptions.plugins.controls = {}; delete playerOptions.play; } var fp = flowplayer($wrapper[0], { src: "http://releases.flowplayer.org/swf/flowplayer-3.2.16.swf", wmode: "opaque" }, playerOptions); /** * Appends the video player to the DOM. * * @public * @param {jQuery} $container */ self.appendTo = function ($container) { $wrapper.appendTo($container); }; /** * Get list of available qualities. Not available until after play. * * @public * @returns {Array} */ self.getQualities = function () { return; }; /** * Get current playback quality. Not available until after play. * * @public * @returns {String} */ self.getQuality = function () { return currentQuality; }; /** * Set current playback quality. Not available until after play. * Listen to event "qualityChange" to check if successful. * * @public * @params {String} [quality] */ self.setQuality = function (quality) { if (qualities[quality] === undefined || quality === currentQuality) { return; // Invalid quality } // Keep track of last choice setPreferredQuality(quality); // Avoid multiple loaded events if changing quality multiple times. if (!stateBeforeChangingQuality) { // Keep track of last state stateBeforeChangingQuality = lastState; // Keep track of current time currentTimeBeforeChangingQuality = video.currentTime; } // Keep track of current quality currentQuality = quality; self.trigger('qualityChange', currentQuality); // Display throbber self.trigger('stateChange', H5P.Video.BUFFERING); // Change source fp.setClip(qualities[quality].source.path); fp.startBuffering(); }; /** * Starts the video. * * @public */ self.play = function () { if ($error.is(':visible')) { return; } fp.play(); }; /** * Pauses the video. * * @public */ self.pause = function () { fp.pause(); }; /** * Seek video to given time. * * @public * @param {Number} time */ self.seek = function (time) { fp.seek(time); }; /** * Get elapsed time since video beginning. * * @public * @returns {Number} */ self.getCurrentTime = function () { return fp.getTime(); }; /** * Get total video duration time. * * @public * @returns {Number} */ self.getDuration = function () { return fp.getClip().metaData.duration; }; /** * Get percentage of video that is buffered. * * @public * @returns {Number} Between 0 and 100 */ self.getBuffered = function () { return fp.getClip().buffer; }; /** * Turn off video sound. * * @public */ self.mute = function () { fp.mute(); }; /** * Turn on video sound. * * @public */ self.unMute = function () { fp.unmute(); }; /** * Check if video sound is turned on or off. * * @public * @returns {Boolean} */ self.isMuted = function () { return fp.muted; }; /** * Returns the video sound level. * * @public * @returns {Number} Between 0 and 100. */ self.getVolume = function () { return fp.volumeLevel * 100; }; /** * Set video sound level. * * @public * @param {Number} volume Between 0 and 100. */ self.setVolume = function (level) { fp.volume(level / 100); }; // Handle resize events self.on('resize', function () { var $object = H5P.jQuery(fp.getParent()).children('object'); var clip = fp.getClip(); if (clip !== undefined) { $object.css('height', $object.width() * (clip.metaData.height / clip.metaData.width)); } }); } /** * Check to see if we can play any of the given sources. * * @public * @static * @param {Array} sources * @returns {Boolean} */ Flash.canPlay = function (sources) { // Cycle through sources for (var i = 0; i < sources.length; i++) { if (sources[i].mime === 'video/mp4' || /\.mp4$/.test(sources[i].mime)) { return true; // We only play mp4 } } }; return Flash; })(H5P.jQuery); // Register video handler H5P.videoHandlers = H5P.videoHandlers || []; H5P.videoHandlers.push(H5P.VideoFlash); ; /** @namespace H5P */ H5P.Video = (function ($, ContentCopyrights, MediaCopyright, handlers) { /** * The ultimate H5P video player! * * @class * @param {Object} parameters Options for this library. * @param {Object} parameters.visuals Visual options * @param {Object} parameters.playback Playback options * @param {Object} parameters.a11y Accessibility options * @param {Boolean} [parameters.startAt] Start time of video * @param {Number} id Content identifier */ function Video(parameters, id) { var self = this; self.contentId = id; // Ref youtube.js - ipad & youtube - issue self.pressToPlay = false; // Reference to the handler var handlerName = ''; // Initialize event inheritance H5P.EventDispatcher.call(self); // Default language localization parameters = $.extend(true, parameters, { l10n: { name: 'Video', loading: 'Video player loading...', noPlayers: 'Found no video players that supports the given video format.', noSources: 'Video source is missing.', aborted: 'Media playback has been aborted.', networkFailure: 'Network failure.', cannotDecode: 'Unable to decode media.', formatNotSupported: 'Video format not supported.', mediaEncrypted: 'Media encrypted.', unknownError: 'Unknown error.', invalidYtId: 'Invalid YouTube ID.', unknownYtId: 'Unable to find video with the given YouTube ID.', restrictedYt: 'The owner of this video does not allow it to be embedded.' } }); parameters.a11y = parameters.a11y || []; parameters.playback = parameters.playback || {}; parameters.visuals = $.extend(true, parameters.visuals, { disableFullscreen: false }); /** @private */ var sources = []; if (parameters.sources) { for (var i = 0; i < parameters.sources.length; i++) { // Clone to avoid changing of parameters. var source = $.extend(true, {}, parameters.sources[i]); // Create working URL without html entities. source.path = $cleaner.html(source.path).text(); sources.push(source); } } /** @private */ var tracks = []; parameters.a11y.forEach(function (track) { // Clone to avoid changing of parameters. var clone = $.extend(true, {}, track); // Create working URL without html entities if (clone.track && clone.track.path) { clone.track.path = $cleaner.html(clone.track.path).text(); tracks.push(clone); } }); /** * Attaches the video handler to the given container. * Inserts text if no handler is found. * * @public * @param {jQuery} $container */ self.attach = function ($container) { $container.addClass('h5p-video').html(''); if (self.appendTo !== undefined) { self.appendTo($container); } else { if (sources.length) { $container.text(parameters.l10n.noPlayers); } else { $container.text(parameters.l10n.noSources); } } }; /** * Get name of the video handler * * @public * @returns {string} */ self.getHandlerName = function() { return handlerName; }; // Resize the video when we know its aspect ratio self.on('loaded', function () { self.trigger('resize'); }); // Find player for video sources if (sources.length) { const options = { controls: parameters.visuals.controls, autoplay: parameters.playback.autoplay, loop: parameters.playback.loop, fit: parameters.visuals.fit, poster: parameters.visuals.poster === undefined ? undefined : parameters.visuals.poster, startAt: parameters.startAt || 0, tracks: tracks, disableRemotePlayback: parameters.visuals.disableRemotePlayback === true, disableFullscreen: parameters.visuals.disableFullscreen === true } var html5Handler; for (var i = 0; i < handlers.length; i++) { var handler = handlers[i]; if (handler.canPlay !== undefined && handler.canPlay(sources)) { handler.call(self, sources, options, parameters.l10n); handlerName = handler.name; return; } if (handler === H5P.VideoHtml5) { html5Handler = handler; handlerName = handler.name; } } // Fallback to trying HTML5 player if (html5Handler) { html5Handler.call(self, sources, options, parameters.l10n); } } } // Extends the event dispatcher Video.prototype = Object.create(H5P.EventDispatcher.prototype); Video.prototype.constructor = Video; // Player states /** @constant {Number} */ Video.ENDED = 0; /** @constant {Number} */ Video.PLAYING = 1; /** @constant {Number} */ Video.PAUSED = 2; /** @constant {Number} */ Video.BUFFERING = 3; /** * When video is queued to start * @constant {Number} */ Video.VIDEO_CUED = 5; // Used to convert between html and text, since URLs have html entities. var $cleaner = H5P.jQuery('<div/>'); /** * Help keep track of key value pairs used by the UI. * * @class * @param {string} label * @param {string} value */ Video.LabelValue = function (label, value) { this.label = label; this.value = value; }; /** @constant {Boolean} */ Video.IE11_PLAYBACK_RATE_FIX = (navigator.userAgent.match(/Trident.*rv[ :]*11\./) ? true : false); return Video; })(H5P.jQuery, H5P.ContentCopyrights, H5P.MediaCopyright, H5P.videoHandlers || []); ; H5P = H5P || {}; /** * Will render a Question with multiple choices for answers. * * Events provided: * - h5pQuestionSetFinished: Triggered when a question is finished. (User presses Finish-button) * * @param {Array} options * @param {int} contentId * @param {Object} contentData * @returns {H5P.QuestionSet} Instance */ H5P.QuestionSet = function (options, contentId, contentData) { if (!(this instanceof H5P.QuestionSet)) { return new H5P.QuestionSet(options, contentId, contentData); } H5P.EventDispatcher.call(this); var $ = H5P.jQuery; var self = this; this.contentId = contentId; var defaults = { initialQuestion: 0, progressType: 'dots', passPercentage: 50, questions: [], introPage: { showIntroPage: false, title: '', introduction: '', startButtonText: 'Start' }, texts: { prevButton: 'Previous question', nextButton: 'Next question', finishButton: 'Finish', textualProgress: 'Question: @current of @total questions', jumpToQuestion: 'Question %d of %total', questionLabel: 'Question', readSpeakerProgress: 'Question @current of @total', unansweredText: 'Unanswered', answeredText: 'Answered', currentQuestionText: 'Current question' }, endGame: { showResultPage: true, noResultMessage: 'Finished', message: 'Your result:', oldFeedback: { successGreeting: '', successComment: '', failGreeting: '', failComment: '' }, overallFeedback: [], finishButtonText: 'Finish', solutionButtonText: 'Show solution', retryButtonText: 'Retry', showAnimations: false, skipButtonText: 'Skip video', showSolutionButton: true, showRetryButton: true }, override: {}, disableBackwardsNavigation: false }; var params = $.extend(true, {}, defaults, options); var texttemplate = '<% if (introPage.showIntroPage) { %>' + '<div class="intro-page">' + ' <% if (introPage.title) { %>' + ' <div class="title"><span><%= introPage.title %></span></div>' + ' <% } %>' + ' <% if (introPage.introduction) { %>' + ' <div class="introduction"><%= introPage.introduction %></div>' + ' <% } %>' + ' <div class="buttons"><a href="#" class="qs-startbutton h5p-joubelui-button h5p-button"><%= introPage.startButtonText %></a></div>' + '</div>' + '<% } %>' + '<div tabindex="-1" class="qs-progress-announcer"></div>' + '<div class="questionset<% if (introPage.showIntroPage) { %> hidden<% } %>">' + ' <% for (var i=0; i<questions.length; i++) { %>' + ' <div class="question-container"></div>' + ' <% } %>' + ' <div class="qs-footer">' + ' <div class="qs-progress">' + ' <% if (progressType == "dots") { %>' + ' <ul class="dots-container" role="navigation">' + ' <% for (var i=0; i<questions.length; i++) { %>' + ' <li class="progress-item">' + ' <a href="#" ' + ' class="progress-dot unanswered<%' + ' if (disableBackwardsNavigation) { %> disabled <% } %>"' + ' aria-label="<%=' + ' texts.jumpToQuestion.replace("%d", i + 1).replace("%total", questions.length)' + ' + ", " + texts.unansweredText %>" tabindex="-1" ' + ' <% if (disableBackwardsNavigation) { %> aria-disabled="true" <% } %>' + ' ></a>' + ' </li>' + ' <% } %>' + ' </div>' + ' <% } else if (progressType == "textual") { %>' + ' <span class="progress-text"></span>' + ' <% } %>' + ' </div>' + ' </div>' + '</div>'; var solutionButtonTemplate = params.endGame.showSolutionButton ? ' <button type="button" class="h5p-joubelui-button h5p-button qs-solutionbutton"><%= solutionButtonText %></button>': ''; const retryButtonTemplate = params.endGame.showRetryButton ? ' <button type="button" class="h5p-joubelui-button h5p-button qs-retrybutton"><%= retryButtonText %></button>': ''; var resulttemplate = '<div class="questionset-results">' + ' <div class="greeting"><%= message %></div>' + ' <div class="feedback-section">' + ' <div class="feedback-scorebar"></div>' + ' <div class="feedback-text"></div>' + ' </div>' + ' <% if (comment) { %>' + ' <div class="result-header"><%= comment %></div>' + ' <% } %>' + ' <% if (resulttext) { %>' + ' <div class="result-text"><%= resulttext %></div>' + ' <% } %>' + ' <div class="buttons">' + solutionButtonTemplate + retryButtonTemplate + ' </div>' + '</div>'; var template = new EJS({text: texttemplate}); var endTemplate = new EJS({text: resulttemplate}); var initialParams = $.extend(true, {}, defaults, options); var poolOrder; // Order of questions in a pool var currentQuestion = 0; var questionInstances = []; var questionOrder; //Stores order of questions to allow resuming of question set var $myDom; var scoreBar; var up; var renderSolutions = false; var showingSolutions = false; contentData = contentData || {}; // Bring question set up to date when resuming if (contentData.previousState) { if (contentData.previousState.progress) { currentQuestion = contentData.previousState.progress; } questionOrder = contentData.previousState.order; } /** * Randomizes questions in an array and updates an array containing their order * @param {array} questions * @return {Object.<array, array>} questionOrdering */ var randomizeQuestionOrdering = function (questions) { // Save the original order of the questions in a multidimensional array [[question0,0],[question1,1]... var questionOrdering = questions.map(function (questionInstance, index) { return [questionInstance, index]; }); // Shuffle the multidimensional array questionOrdering = H5P.shuffleArray(questionOrdering); // Retrieve question objects from the first index questions = []; for (var i = 0; i < questionOrdering.length; i++) { questions[i] = questionOrdering[i][0]; } // Retrieve the new shuffled order from the second index var newOrder = []; for (var j = 0; j < questionOrdering.length; j++) { // Use a previous order if it exists if (contentData.previousState && contentData.previousState.questionOrder) { newOrder[j] = questionOrder[questionOrdering[j][1]]; } else { newOrder[j] = questionOrdering[j][1]; } } // Return the questions in their new order *with* their new indexes return { questions: questions, questionOrder: newOrder }; }; // Create a pool (a subset) of questions if necessary if (params.poolSize > 0) { // If a previous pool exists, recreate it if (contentData.previousState && contentData.previousState.poolOrder) { poolOrder = contentData.previousState.poolOrder; // Recreate the pool from the saved data var pool = []; for (var i = 0; i < poolOrder.length; i++) { pool[i] = params.questions[poolOrder[i]]; } // Replace original questions with just the ones in the pool params.questions = pool; } else { // Otherwise create a new pool // Randomize and get the results var poolResult = randomizeQuestionOrdering(params.questions); var poolQuestions = poolResult.questions; poolOrder = poolResult.questionOrder; // Discard extra questions poolQuestions = poolQuestions.slice(0, params.poolSize); poolOrder = poolOrder.slice(0, params.poolSize); // Replace original questions with just the ones in the pool params.questions = poolQuestions; } } // Create the html template for the question container var $template = $(template.render(params)); // Set overrides for questions var override; if (params.override.showSolutionButton || params.override.retryButton || params.override.checkButton === false) { override = {}; if (params.override.showSolutionButton) { // Force "Show solution" button to be on or off for all interactions override.enableSolutionsButton = (params.override.showSolutionButton === 'on' ? true : false); } if (params.override.retryButton) { // Force "Retry" button to be on or off for all interactions override.enableRetry = (params.override.retryButton === 'on' ? true : false); } if (params.override.checkButton === false) { // Force "Check" button to be on or off for all interactions override.enableCheckButton = params.override.checkButton; } } /** * Generates question instances from H5P objects * * @param {object} questions H5P content types to be created as instances * @return {array} Array of questions instances */ var createQuestionInstancesFromQuestions = function (questions) { var result = []; // Create question instances from questions // Instantiate question instances for (var i = 0; i < questions.length; i++) { var question; // If a previous order exists, use it if (questionOrder !== undefined) { question = questions[questionOrder[i]]; } else { // Use a generic order when initialzing for the first time question = questions[i]; } if (override) { // Extend subcontent with the overrided settings. $.extend(question.params.behaviour, override); } question.params = question.params || {}; var hasAnswers = contentData.previousState && contentData.previousState.answers; var questionInstance = H5P.newRunnable(question, contentId, undefined, undefined, { previousState: hasAnswers ? contentData.previousState.answers[i] : undefined, parent: self }); questionInstance.on('resize', function () { up = true; self.trigger('resize'); }); result.push(questionInstance); } return result; }; // Create question instances from questions given by params questionInstances = createQuestionInstancesFromQuestions(params.questions); // Randomize questions only on instantiation if (params.randomQuestions && contentData.previousState === undefined) { var result = randomizeQuestionOrdering(questionInstances); questionInstances = result.questions; questionOrder = result.questionOrder; } // Resize all interactions on resize self.on('resize', function () { if (up) { // Prevent resizing the question again. up = false; return; } for (var i = 0; i < questionInstances.length; i++) { questionInstances[i].trigger('resize'); } }); // Update button state. var _updateButtons = function () { // Verify that current question is answered when backward nav is disabled if (params.disableBackwardsNavigation) { if (questionInstances[currentQuestion].getAnswerGiven() && questionInstances.length-1 !== currentQuestion) { questionInstances[currentQuestion].showButton('next'); } else { questionInstances[currentQuestion].hideButton('next'); } } var answered = true; for (var i = questionInstances.length - 1; i >= 0; i--) { answered = answered && (questionInstances[i]).getAnswerGiven(); } if (currentQuestion === (params.questions.length - 1) && questionInstances[currentQuestion]) { if (answered) { questionInstances[currentQuestion].showButton('finish'); } else { questionInstances[currentQuestion].hideButton('finish'); } } }; var _stopQuestion = function (questionNumber) { if (questionInstances[questionNumber]) { pauseMedia(questionInstances[questionNumber]); } }; var _showQuestion = function (questionNumber, preventAnnouncement) { // Sanitize input. if (questionNumber < 0) { questionNumber = 0; } if (questionNumber >= params.questions.length) { questionNumber = params.questions.length - 1; } currentQuestion = questionNumber; handleAutoPlay(currentQuestion); // Hide all questions $('.question-container', $myDom).hide().eq(questionNumber).show(); if (questionInstances[questionNumber]) { // Trigger resize on question in case the size of the QS has changed. var instance = questionInstances[questionNumber]; instance.setActivityStarted(); if (instance.$ !== undefined) { instance.trigger('resize'); } } // Update progress indicator // Test if current has been answered. if (params.progressType === 'textual') { $('.progress-text', $myDom).text(params.texts.textualProgress.replace("@current", questionNumber+1).replace("@total", params.questions.length)); } else { // Set currentNess var previousQuestion = $('.progress-dot.current', $myDom).parent().index(); if (previousQuestion >= 0) { toggleCurrentDot(previousQuestion, false); toggleAnsweredDot(previousQuestion, questionInstances[previousQuestion].getAnswerGiven()); } toggleCurrentDot(questionNumber, true); } if (!preventAnnouncement) { // Announce question number of total, must use timeout because of buttons logic setTimeout(function () { var humanizedProgress = params.texts.readSpeakerProgress .replace('@current', (currentQuestion + 1).toString()) .replace('@total', questionInstances.length.toString()); $('.qs-progress-announcer', $myDom) .html(humanizedProgress) .show().focus(); if (instance && instance.readFeedback) { instance.readFeedback(); } }, 0); } // Remember where we are _updateButtons(); self.trigger('resize'); return currentQuestion; }; /** * Handle autoplays, limit to one at a time * * @param {number} currentQuestionIndex */ var handleAutoPlay = function (currentQuestionIndex) { for (var i = 0; i < questionInstances.length; i++) { questionInstances[i].pause(); } var currentQuestion = params.questions[currentQuestionIndex]; var hasAutoPlay = currentQuestion && currentQuestion.params.media && currentQuestion.params.media.params && currentQuestion.params.media.params.playback && currentQuestion.params.media.params.playback.autoplay; if (hasAutoPlay && typeof questionInstances[currentQuestionIndex].play === 'function') { questionInstances[currentQuestionIndex].play(); } }; /** * Show solutions for subcontent, and hide subcontent buttons. * Used for contracts with integrated content. * @public */ var showSolutions = function () { showingSolutions = true; for (var i = 0; i < questionInstances.length; i++) { // Enable back and forth navigation in solution mode toggleDotsNavigation(true); if (i < questionInstances.length - 1) { questionInstances[i].showButton('next'); } if (i > 0) { questionInstances[i].showButton('prev'); } try { // Do not read answers questionInstances[i].toggleReadSpeaker(true); questionInstances[i].showSolutions(); questionInstances[i].toggleReadSpeaker(false); } catch (error) { H5P.error("subcontent does not contain a valid showSolutions function"); H5P.error(error); } } }; /** * Toggles whether dots are enabled for navigation */ var toggleDotsNavigation = function (enable) { $('.progress-dot', $myDom).each(function () { $(this).toggleClass('disabled', !enable); $(this).attr('aria-disabled', enable ? 'false' : 'true'); // Remove tabindex if (!enable) { $(this).attr('tabindex', '-1'); } }); }; /** * Resets the task and every subcontent task. * Used for contracts with integrated content. * @public */ var resetTask = function () { // Clear previous state to ensure questions are created cleanly contentData.previousState = []; showingSolutions = false; for (var i = 0; i < questionInstances.length; i++) { try { questionInstances[i].resetTask(); // Hide back and forth navigation in normal mode if (params.disableBackwardsNavigation) { toggleDotsNavigation(false); // Check if first question is answered by default if (i === 0 && questionInstances[i].getAnswerGiven()) { questionInstances[i].showButton('next'); } else { questionInstances[i].hideButton('next'); } questionInstances[i].hideButton('prev'); } } catch (error) { H5P.error("subcontent does not contain a valid resetTask function"); H5P.error(error); } } // Hide finish button questionInstances[questionInstances.length - 1].hideButton('finish'); // Mark all tasks as unanswered: $('.progress-dot').each(function (idx) { toggleAnsweredDot(idx, false); }); //Force the last page to be reRendered rendered = false; if (params.poolSize > 0) { // Make new pool from params.questions // Randomize and get the results var poolResult = randomizeQuestionOrdering(initialParams.questions); var poolQuestions = poolResult.questions; poolOrder = poolResult.questionOrder; // Discard extra questions poolQuestions = poolQuestions.slice(0, params.poolSize); poolOrder = poolOrder.slice(0, params.poolSize); // Replace original questions with just the ones in the pool params.questions = poolQuestions; // Recreate the question instances questionInstances = createQuestionInstancesFromQuestions(params.questions); // Update buttons initializeQuestion(); } else if (params.randomQuestions) { randomizeQuestions(); } }; var rendered = false; this.reRender = function () { rendered = false; }; /** * Randomizes question instances */ var randomizeQuestions = function () { var result = randomizeQuestionOrdering(questionInstances); questionInstances = result.questions; questionOrder = result.questionOrder; replaceQuestionsInDOM(questionInstances); }; /** * Empty the DOM of all questions, attach new questions and update buttons * * @param {type} questionInstances Array of questions to be attached to the DOM */ var replaceQuestionsInDOM = function (questionInstances) { // Find all question containers and detach questions from them $('.question-container', $myDom).each(function () { $(this).children().detach(); }); // Reattach questions and their buttons in the new order for (var i = 0; i < questionInstances.length; i++) { var question = questionInstances[i]; // Make sure styles are not being added twice $('.question-container:eq(' + i + ')', $myDom).attr('class', 'question-container'); question.attach($('.question-container:eq(' + i + ')', $myDom)); //Show buttons if necessary if (questionInstances[questionInstances.length -1] === question && question.hasButton('finish')) { question.showButton('finish'); } if (questionInstances[questionInstances.length -1] !== question && question.hasButton('next')) { question.showButton('next'); } if (questionInstances[0] !== question && question.hasButton('prev') && !params.disableBackwardsNavigation) { question.showButton('prev'); } // Hide relevant buttons since the order has changed if (questionInstances[0] === question) { question.hideButton('prev'); } if (questionInstances[questionInstances.length-1] === question) { question.hideButton('next'); } if (questionInstances[questionInstances.length-1] !== question) { question.hideButton('finish'); } } }; var moveQuestion = function (direction) { if (params.disableBackwardsNavigation && !questionInstances[currentQuestion].getAnswerGiven()) { questionInstances[currentQuestion].hideButton('next'); questionInstances[currentQuestion].hideButton('finish'); return; } _stopQuestion(currentQuestion); if (currentQuestion + direction >= questionInstances.length) { _displayEndGame(); } else { // Allow movement if backward navigation enabled or answer given _showQuestion(currentQuestion + direction); } }; /** * Toggle answered state of dot at given index * @param {number} dotIndex Index of dot * @param {boolean} isAnswered True if is answered, False if not answered */ var toggleAnsweredDot = function (dotIndex, isAnswered) { var $el = $('.progress-dot:eq(' + dotIndex +')', $myDom); // Skip current button if ($el.hasClass('current')) { return; } // Ensure boolean isAnswered = !!isAnswered; var label = params.texts.jumpToQuestion .replace('%d', (dotIndex + 1).toString()) .replace('%total', $('.progress-dot', $myDom).length) + ', ' + (isAnswered ? params.texts.answeredText : params.texts.unansweredText); $el.toggleClass('unanswered', !isAnswered) .toggleClass('answered', isAnswered) .attr('aria-label', label); }; /** * Toggle current state of dot at given index * @param dotIndex * @param isCurrent */ var toggleCurrentDot = function (dotIndex, isCurrent) { var $el = $('.progress-dot:eq(' + dotIndex +')', $myDom); var texts = params.texts; var label = texts.jumpToQuestion .replace('%d', (dotIndex + 1).toString()) .replace('%total', $('.progress-dot', $myDom).length); if (!isCurrent) { var isAnswered = $el.hasClass('answered'); label += ', ' + (isAnswered ? texts.answeredText : texts.unansweredText); } else { label += ', ' + texts.currentQuestionText; } var disabledTabindex = params.disableBackwardsNavigation && !showingSolutions; $el.toggleClass('current', isCurrent) .attr('aria-label', label) .attr('tabindex', isCurrent && !disabledTabindex ? 0 : -1); }; var _displayEndGame = function () { $('.progress-dot.current', $myDom).removeClass('current'); if (rendered) { $myDom.children().hide().filter('.questionset-results').show(); self.trigger('resize'); return; } //Remove old score screen. $myDom.children().hide().filter('.questionset-results').remove(); rendered = true; // Get total score. var finals = self.getScore(); var totals = self.getMaxScore(); var scoreString = H5P.Question.determineOverallFeedback(params.endGame.overallFeedback, finals / totals).replace('@score', finals).replace('@total', totals); var success = ((100 * finals / totals) >= params.passPercentage); /** * Makes our buttons behave like other buttons. * * @private * @param {string} classSelector * @param {function} handler */ var hookUpButton = function (classSelector, handler) { $(classSelector, $myDom).click(handler).keypress(function (e) { if (e.which === 32) { handler(); e.preventDefault(); } }); }; var displayResults = function () { self.triggerXAPICompleted(self.getScore(), self.getMaxScore(), success); var eparams = { message: params.endGame.showResultPage ? params.endGame.message : params.endGame.noResultMessage, comment: params.endGame.showResultPage ? (success ? params.endGame.oldFeedback.successGreeting : params.endGame.oldFeedback.failGreeting) : undefined, resulttext: params.endGame.showResultPage ? (success ? params.endGame.oldFeedback.successComment : params.endGame.oldFeedback.failComment) : undefined, finishButtonText: params.endGame.finishButtonText, solutionButtonText: params.endGame.solutionButtonText, retryButtonText: params.endGame.retryButtonText }; // Show result page. $myDom.children().hide(); $myDom.append(endTemplate.render(eparams)); if (params.endGame.showResultPage) { hookUpButton('.qs-solutionbutton', function () { showSolutions(); $myDom.children().hide().filter('.questionset').show(); _showQuestion(params.initialQuestion); }); hookUpButton('.qs-retrybutton', function () { resetTask(); $myDom.children().hide(); var $intro = $('.intro-page', $myDom); if ($intro.length) { // Show intro $('.intro-page', $myDom).show(); $('.qs-startbutton', $myDom).focus(); } else { // Show first question $('.questionset', $myDom).show(); _showQuestion(params.initialQuestion); } }); if (scoreBar === undefined) { scoreBar = H5P.JoubelUI.createScoreBar(totals); } scoreBar.appendTo($('.feedback-scorebar', $myDom)); $('.feedback-text', $myDom).html(scoreString); // Announce that the question set is complete setTimeout(function () { $('.qs-progress-announcer', $myDom) .html(eparams.message + '.' + scoreString + '.' + eparams.comment + '.' + eparams.resulttext) .show().focus(); scoreBar.setMaxScore(totals); scoreBar.setScore(finals); }, 0); } else { // Remove buttons and feedback section $('.qs-solutionbutton, .qs-retrybutton, .feedback-section', $myDom).remove(); } self.trigger('resize'); }; if (params.endGame.showAnimations) { var videoData = success ? params.endGame.successVideo : params.endGame.failVideo; if (videoData) { $myDom.children().hide(); var $videoContainer = $('<div class="video-container"></div>').appendTo($myDom); var video = new H5P.Video({ sources: videoData, fitToWrapper: true, controls: false, autoplay: false }, contentId); video.on('stateChange', function (event) { if (event.data === H5P.Video.ENDED) { displayResults(); $videoContainer.hide(); } }); video.attach($videoContainer); // Resize on video loaded video.on('loaded', function () { self.trigger('resize'); }); video.play(); if (params.endGame.skippable) { $('<a class="h5p-joubelui-button h5p-button skip">' + params.endGame.skipButtonText + '</a>').click(function () { video.pause(); $videoContainer.hide(); displayResults(); }).appendTo($videoContainer); } return; } } // Trigger finished event. displayResults(); self.trigger('resize'); }; var registerImageLoadedListener = function (question) { H5P.on(question, 'imageLoaded', function () { self.trigger('resize'); }); }; /** * Initialize a question and attach it to the DOM * */ function initializeQuestion() { // Attach questions for (var i = 0; i < questionInstances.length; i++) { var question = questionInstances[i]; // Make sure styles are not being added twice $('.question-container:eq(' + i + ')', $myDom).attr('class', 'question-container'); question.attach($('.question-container:eq(' + i + ')', $myDom)); // Listen for image resize registerImageLoadedListener(question); // Add finish button question.addButton('finish', params.texts.finishButton, moveQuestion.bind(this, 1), false); // Add next button question.addButton('next', '', moveQuestion.bind(this, 1), !params.disableBackwardsNavigation || !!question.getAnswerGiven(), { href: '#', // Use href since this is a navigation button 'aria-label': params.texts.nextButton }); // Add previous button question.addButton('prev', '', moveQuestion.bind(this, -1), !(questionInstances[0] === question || params.disableBackwardsNavigation), { href: '#', // Use href since this is a navigation button 'aria-label': params.texts.prevButton }); // Hide next button if it is the last question if (questionInstances[questionInstances.length -1] === question) { question.hideButton('next'); } question.on('xAPI', function (event) { var shortVerb = event.getVerb(); if (shortVerb === 'interacted' || shortVerb === 'answered' || shortVerb === 'attempted') { toggleAnsweredDot(currentQuestion, questionInstances[currentQuestion].getAnswerGiven()); _updateButtons(); } if (shortVerb === 'completed') { // An activity within this activity is not allowed to send completed events event.setVerb('answered'); } if (event.data.statement.context.extensions === undefined) { event.data.statement.context.extensions = {}; } event.data.statement.context.extensions['http://id.tincanapi.com/extension/ending-point'] = currentQuestion + 1; }); // Mark question if answered toggleAnsweredDot(i, question.getAnswerGiven()); } } this.attach = function (target) { if (this.isRoot()) { this.setActivityStarted(); } if (typeof(target) === "string") { $myDom = $('#' + target); } else { $myDom = $(target); } // Render own DOM into target. $myDom.children().remove(); $myDom.append($template); if (params.backgroundImage !== undefined) { $myDom.css({ overflow: 'hidden', background: '#fff url("' + H5P.getPath(params.backgroundImage.path, contentId) + '") no-repeat 50% 50%', backgroundSize: '100% auto' }); } if (params.introPage.backgroundImage !== undefined) { var $intro = $myDom.find('.intro-page'); if ($intro.length) { var bgImg = params.introPage.backgroundImage; var bgImgRatio = (bgImg.height / bgImg.width); $intro.css({ background: '#fff url("' + H5P.getPath(bgImg.path, contentId) + '") no-repeat 50% 50%', backgroundSize: 'auto 100%', minHeight: bgImgRatio * +window.getComputedStyle($intro[0]).width.replace('px','') }); } } initializeQuestion(); // Allow other libraries to add transitions after the questions have been inited $('.questionset', $myDom).addClass('started'); $('.qs-startbutton', $myDom) .click(function () { $(this).parents('.intro-page').hide(); $('.questionset', $myDom).show(); _showQuestion(params.initialQuestion); event.preventDefault(); }) .keydown(function (event) { switch (event.which) { case 13: // Enter case 32: // Space $(this).parents('.intro-page').hide(); $('.questionset', $myDom).show(); _showQuestion(params.initialQuestion); event.preventDefault(); } }); /** * Triggers changing the current question. * * @private * @param {Object} [event] */ var handleProgressDotClick = function (event) { // Disable dots when backward nav disabled event.preventDefault(); if (params.disableBackwardsNavigation && !showingSolutions) { return; } _stopQuestion(currentQuestion); _showQuestion($(this).parent().index()); }; // Set event listeners. $('.progress-dot', $myDom).click(handleProgressDotClick).keydown(function (event) { var $this = $(this); switch (event.which) { case 13: // Enter case 32: // Space handleProgressDotClick.call(this, event); break; case 37: // Left Arrow case 38: // Up Arrow // Go to previous dot var $prev = $this.parent().prev(); if ($prev.length) { $prev.children('a').attr('tabindex', '0').focus(); $this.attr('tabindex', '-1'); } break; case 39: // Right Arrow case 40: // Down Arrow // Go to next dot var $next = $this.parent().next(); if ($next.length) { $next.children('a').attr('tabindex', '0').focus(); $this.attr('tabindex', '-1'); } break; } }); // Hide all but current question _showQuestion(currentQuestion, true); if (renderSolutions) { showSolutions(); } // Update buttons in case they have changed (restored user state) _updateButtons(); this.trigger('resize'); return this; }; // Get current score for questionset. this.getScore = function () { var score = 0; for (var i = questionInstances.length - 1; i >= 0; i--) { score += questionInstances[i].getScore(); } return score; }; // Get total score possible for questionset. this.getMaxScore = function () { var score = 0; for (var i = questionInstances.length - 1; i >= 0; i--) { score += questionInstances[i].getMaxScore(); } return score; }; /** * @deprecated since version 1.9.2 * @returns {number} */ this.totalScore = function () { return this.getMaxScore(); }; /** * Gather copyright information for the current content. * * @returns {H5P.ContentCopyrights} */ this.getCopyrights = function () { var info = new H5P.ContentCopyrights(); // IntroPage Background if (params.introPage !== undefined && params.introPage.backgroundImage !== undefined && params.introPage.backgroundImage.copyright !== undefined) { var introBackground = new H5P.MediaCopyright(params.introPage.backgroundImage.copyright); introBackground.setThumbnail(new H5P.Thumbnail(H5P.getPath(params.introPage.backgroundImage.path, contentId), params.introPage.backgroundImage.width, params.introPage.backgroundImage.height)); info.addMedia(introBackground); } // Background if (params.backgroundImage !== undefined && params.backgroundImage.copyright !== undefined) { var background = new H5P.MediaCopyright(params.backgroundImage.copyright); background.setThumbnail(new H5P.Thumbnail(H5P.getPath(params.backgroundImage.path, contentId), params.backgroundImage.width, params.backgroundImage.height)); info.addMedia(background); } // Questions var questionCopyrights; for (var i = 0; i < questionInstances.length; i++) { var instance = questionInstances[i]; var instanceParams = params.questions[i].params; questionCopyrights = undefined; if (instance.getCopyrights !== undefined) { // Use the instance's own copyright generator questionCopyrights = instance.getCopyrights(); } if (questionCopyrights === undefined) { // Create a generic flat copyright list questionCopyrights = new H5P.ContentCopyrights(); H5P.findCopyrights(questionCopyrights, instanceParams.params, contentId,{ metadata: instanceParams.metadata, machineName: instanceParams.library.split(' ')[0] }); } // Determine label var label = (params.texts.questionLabel + ' ' + (i + 1)); if (instanceParams.params.contentName !== undefined) { label += ': ' + instanceParams.params.contentName; } else if (instance.getTitle !== undefined) { label += ': ' + instance.getTitle(); } questionCopyrights.setLabel(label); // Add info info.addContent(questionCopyrights); } // Success video var video; if (params.endGame.successVideo !== undefined && params.endGame.successVideo.length > 0) { video = params.endGame.successVideo[0]; if (video.copyright !== undefined) { info.addMedia(new H5P.MediaCopyright(video.copyright)); } } // Fail video if (params.endGame.failVideo !== undefined && params.endGame.failVideo.length > 0) { video = params.endGame.failVideo[0]; if (video.copyright !== undefined) { info.addMedia(new H5P.MediaCopyright(video.copyright)); } } return info; }; this.getQuestions = function () { return questionInstances; }; this.showSolutions = function () { renderSolutions = true; }; /** * Stop the given element's playback if any. * * @param {object} instance */ var pauseMedia = function (instance) { try { if (instance.pause !== undefined && (instance.pause instanceof Function || typeof instance.pause === 'function')) { instance.pause(); } } catch (err) { // Prevent crashing, log error. H5P.error(err); } }; /** * Returns the complete state of question set and sub-content * * @returns {Object} current state */ this.getCurrentState = function () { return { progress: showingSolutions ? questionInstances.length - 1 : currentQuestion, answers: questionInstances.map(function (qi) { return qi.getCurrentState(); }), order: questionOrder, poolOrder: poolOrder }; }; /** * Generate xAPI object definition used in xAPI statements. * @return {Object} */ var getxAPIDefinition = function () { var definition = {}; definition.interactionType = 'compound'; definition.type = 'http://adlnet.gov/expapi/activities/cmi.interaction'; definition.description = { 'en-US': '' }; return definition; }; /** * Add the question itself to the definition part of an xAPIEvent */ var addQuestionToXAPI = function (xAPIEvent) { var definition = xAPIEvent.getVerifiedStatementValue(['object', 'definition']); $.extend(definition, getxAPIDefinition()); }; /** * Get xAPI data from sub content types * * @param {Object} metaContentType * @returns {array} */ var getXAPIDataFromChildren = function (metaContentType) { return metaContentType.getQuestions().map(function (question) { return question.getXAPIData(); }); }; /** * Get xAPI data. * Contract used by report rendering engine. * * @see contract at {@link https://h5p.org/documentation/developers/contracts#guides-header-6} */ this.getXAPIData = function () { var xAPIEvent = this.createXAPIEventTemplate('answered'); addQuestionToXAPI(xAPIEvent); xAPIEvent.setScoredResult(this.getScore(), this.getMaxScore(), this, true, this.getScore() === this.getMaxScore() ); return { statement: xAPIEvent.data.statement, children: getXAPIDataFromChildren(this) }; }; /** * Get context data. * Contract used for confusion report. */ this.getContext = function () { // Get question index and add 1, count starts from 0 return { type: 'question', value: (currentQuestion + 1) }; }; }; H5P.QuestionSet.prototype = Object.create(H5P.EventDispatcher.prototype); H5P.QuestionSet.prototype.constructor = H5P.QuestionSet; ; (()=>{var e={452:(e,t,n)=>{"use strict";function r(e,t){const n=Object.create(null),r=e.split(",");for(let e=0;e<r.length;e++)n[r[e]]=!0;return t?e=>!!n[e.toLowerCase()]:e=>!!n[e]}n.d(t,{Z:()=>qi});const o=r("itemscope,allowfullscreen,formnovalidate,ismap,nomodule,novalidate,readonly");function i(e){return!!e||""===e}function s(e){if(E(e)){const t={};for(let n=0;n<e.length;n++){const r=e[n],o=T(r)?l(r):s(r);if(o)for(const e in o)t[e]=o[e]}return t}return T(e)||P(e)?e:void 0}const a=/;(?![^(]*\))/g,c=/:(.+)/;function l(e){const t={};return e.split(a).forEach((e=>{if(e){const n=e.split(c);n.length>1&&(t[n[0].trim()]=n[1].trim())}})),t}function u(e){let t="";if(T(e))t=e;else if(E(e))for(let n=0;n<e.length;n++){const r=u(e[n]);r&&(t+=r+" ")}else if(P(e))for(const n in e)e[n]&&(t+=n+" ");return t.trim()}const p=e=>T(e)?e:null==e?"":E(e)||P(e)&&(e.toString===N||!j(e.toString))?JSON.stringify(e,f,2):String(e),f=(e,t)=>t&&t.__v_isRef?f(e,t.value):A(t)?{[`Map(${t.size})`]:[...t.entries()].reduce(((e,[t,n])=>(e[`${t} =>`]=n,e)),{})}:x(t)?{[`Set(${t.size})`]:[...t.values()]}:!P(t)||E(t)||I(t)?t:String(t),d={},h=[],y=()=>{},m=()=>!1,g=/^on[^a-z]/,v=e=>g.test(e),b=e=>e.startsWith("onUpdate:"),_=Object.assign,w=(e,t)=>{const n=e.indexOf(t);n>-1&&e.splice(n,1)},S=Object.prototype.hasOwnProperty,O=(e,t)=>S.call(e,t),E=Array.isArray,A=e=>"[object Map]"===L(e),x=e=>"[object Set]"===L(e),j=e=>"function"==typeof e,T=e=>"string"==typeof e,M=e=>"symbol"==typeof e,P=e=>null!==e&&"object"==typeof e,C=e=>P(e)&&j(e.then)&&j(e.catch),N=Object.prototype.toString,L=e=>N.call(e),I=e=>"[object Object]"===L(e),D=e=>T(e)&&"NaN"!==e&&"-"!==e[0]&&""+parseInt(e,10)===e,R=r(",key,ref,ref_for,ref_key,onVnodeBeforeMount,onVnodeMounted,onVnodeBeforeUpdate,onVnodeUpdated,onVnodeBeforeUnmount,onVnodeUnmounted"),k=e=>{const t=Object.create(null);return n=>t[n]||(t[n]=e(n))},U=/-(\w)/g,F=k((e=>e.replace(U,((e,t)=>t?t.toUpperCase():"")))),B=/\B([A-Z])/g,z=k((e=>e.replace(B,"-$1").toLowerCase())),V=k((e=>e.charAt(0).toUpperCase()+e.slice(1))),G=k((e=>e?`on${V(e)}`:"")),H=(e,t)=>!Object.is(e,t),$=(e,t)=>{for(let n=0;n<e.length;n++)e[n](t)},W=(e,t,n)=>{Object.defineProperty(e,t,{configurable:!0,enumerable:!1,value:n})},J=e=>{const t=parseFloat(e);return isNaN(t)?e:t};let X;const Z=()=>X||(X="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:"undefined"!=typeof window?window:void 0!==n.g?n.g:{});let q;class Q{constructor(e=!1){this.active=!0,this.effects=[],this.cleanups=[],!e&&q&&(this.parent=q,this.index=(q.scopes||(q.scopes=[])).push(this)-1)}run(e){if(this.active){const t=q;try{return q=this,e()}finally{q=t}}}on(){q=this}off(){q=this.parent}stop(e){if(this.active){let t,n;for(t=0,n=this.effects.length;t<n;t++)this.effects[t].stop();for(t=0,n=this.cleanups.length;t<n;t++)this.cleanups[t]();if(this.scopes)for(t=0,n=this.scopes.length;t<n;t++)this.scopes[t].stop(!0);if(this.parent&&!e){const e=this.parent.scopes.pop();e&&e!==this&&(this.parent.scopes[this.index]=e,e.index=this.index)}this.active=!1}}}const K=e=>{const t=new Set(e);return t.w=0,t.n=0,t},Y=e=>(e.w&oe)>0,ee=e=>(e.n&oe)>0,te=new WeakMap;let ne,re=0,oe=1;const ie=Symbol(""),se=Symbol("");class ae{constructor(e,t=null,n){this.fn=e,this.scheduler=t,this.active=!0,this.deps=[],this.parent=void 0,function(e,t=q){t&&t.active&&t.effects.push(e)}(this,n)}run(){if(!this.active)return this.fn();let e=ne,t=le;for(;e;){if(e===this)return;e=e.parent}try{return this.parent=ne,ne=this,le=!0,oe=1<<++re,re<=30?(({deps:e})=>{if(e.length)for(let t=0;t<e.length;t++)e[t].w|=oe})(this):ce(this),this.fn()}finally{re<=30&&(e=>{const{deps:t}=e;if(t.length){let n=0;for(let r=0;r<t.length;r++){const o=t[r];Y(o)&&!ee(o)?o.delete(e):t[n++]=o,o.w&=~oe,o.n&=~oe}t.length=n}})(this),oe=1<<--re,ne=this.parent,le=t,this.parent=void 0,this.deferStop&&this.stop()}}stop(){ne===this?this.deferStop=!0:this.active&&(ce(this),this.onStop&&this.onStop(),this.active=!1)}}function ce(e){const{deps:t}=e;if(t.length){for(let n=0;n<t.length;n++)t[n].delete(e);t.length=0}}let le=!0;const ue=[];function pe(){ue.push(le),le=!1}function fe(){const e=ue.pop();le=void 0===e||e}function de(e,t,n){if(le&&ne){let t=te.get(e);t||te.set(e,t=new Map);let r=t.get(n);r||t.set(n,r=K()),he(r)}}function he(e,t){let n=!1;re<=30?ee(e)||(e.n|=oe,n=!Y(e)):n=!e.has(ne),n&&(e.add(ne),ne.deps.push(e))}function ye(e,t,n,r,o,i){const s=te.get(e);if(!s)return;let a=[];if("clear"===t)a=[...s.values()];else if("length"===n&&E(e))s.forEach(((e,t)=>{("length"===t||t>=r)&&a.push(e)}));else switch(void 0!==n&&a.push(s.get(n)),t){case"add":E(e)?D(n)&&a.push(s.get("length")):(a.push(s.get(ie)),A(e)&&a.push(s.get(se)));break;case"delete":E(e)||(a.push(s.get(ie)),A(e)&&a.push(s.get(se)));break;case"set":A(e)&&a.push(s.get(ie))}if(1===a.length)a[0]&&me(a[0]);else{const e=[];for(const t of a)t&&e.push(...t);me(K(e))}}function me(e,t){const n=E(e)?e:[...e];for(const e of n)e.computed&&ge(e);for(const e of n)e.computed||ge(e)}function ge(e,t){(e!==ne||e.allowRecurse)&&(e.scheduler?e.scheduler():e.run())}const ve=r("__proto__,__v_isRef,__isVue"),be=new Set(Object.getOwnPropertyNames(Symbol).filter((e=>"arguments"!==e&&"caller"!==e)).map((e=>Symbol[e])).filter(M)),_e=Ae(),we=Ae(!1,!0),Se=Ae(!0),Oe=Ee();function Ee(){const e={};return["includes","indexOf","lastIndexOf"].forEach((t=>{e[t]=function(...e){const n=ct(this);for(let e=0,t=this.length;e<t;e++)de(n,0,e+"");const r=n[t](...e);return-1===r||!1===r?n[t](...e.map(ct)):r}})),["push","pop","shift","unshift","splice"].forEach((t=>{e[t]=function(...e){pe();const n=ct(this)[t].apply(this,e);return fe(),n}})),e}function Ae(e=!1,t=!1){return function(n,r,o){if("__v_isReactive"===r)return!e;if("__v_isReadonly"===r)return e;if("__v_isShallow"===r)return t;if("__v_raw"===r&&o===(e?t?et:Ye:t?Ke:Qe).get(n))return n;const i=E(n);if(!e&&i&&O(Oe,r))return Reflect.get(Oe,r,o);const s=Reflect.get(n,r,o);return(M(r)?be.has(r):ve(r))?s:(e||de(n,0,r),t?s:ft(s)?i&&D(r)?s:s.value:P(s)?e?nt(s):tt(s):s)}}function xe(e=!1){return function(t,n,r,o){let i=t[n];if(it(i)&&ft(i)&&!ft(r))return!1;if(!e&&!it(r)&&(st(r)||(r=ct(r),i=ct(i)),!E(t)&&ft(i)&&!ft(r)))return i.value=r,!0;const s=E(t)&&D(n)?Number(n)<t.length:O(t,n),a=Reflect.set(t,n,r,o);return t===ct(o)&&(s?H(r,i)&&ye(t,"set",n,r):ye(t,"add",n,r)),a}}const je={get:_e,set:xe(),deleteProperty:function(e,t){const n=O(e,t),r=(e[t],Reflect.deleteProperty(e,t));return r&&n&&ye(e,"delete",t,void 0),r},has:function(e,t){const n=Reflect.has(e,t);return M(t)&&be.has(t)||de(e,0,t),n},ownKeys:function(e){return de(e,0,E(e)?"length":ie),Reflect.ownKeys(e)}},Te={get:Se,set:(e,t)=>!0,deleteProperty:(e,t)=>!0},Me=_({},je,{get:we,set:xe(!0)}),Pe=e=>e,Ce=e=>Reflect.getPrototypeOf(e);function Ne(e,t,n=!1,r=!1){const o=ct(e=e.__v_raw),i=ct(t);n||(t!==i&&de(o,0,t),de(o,0,i));const{has:s}=Ce(o),a=r?Pe:n?pt:ut;return s.call(o,t)?a(e.get(t)):s.call(o,i)?a(e.get(i)):void(e!==o&&e.get(t))}function Le(e,t=!1){const n=this.__v_raw,r=ct(n),o=ct(e);return t||(e!==o&&de(r,0,e),de(r,0,o)),e===o?n.has(e):n.has(e)||n.has(o)}function Ie(e,t=!1){return e=e.__v_raw,!t&&de(ct(e),0,ie),Reflect.get(e,"size",e)}function De(e){e=ct(e);const t=ct(this);return Ce(t).has.call(t,e)||(t.add(e),ye(t,"add",e,e)),this}function Re(e,t){t=ct(t);const n=ct(this),{has:r,get:o}=Ce(n);let i=r.call(n,e);i||(e=ct(e),i=r.call(n,e));const s=o.call(n,e);return n.set(e,t),i?H(t,s)&&ye(n,"set",e,t):ye(n,"add",e,t),this}function ke(e){const t=ct(this),{has:n,get:r}=Ce(t);let o=n.call(t,e);o||(e=ct(e),o=n.call(t,e)),r&&r.call(t,e);const i=t.delete(e);return o&&ye(t,"delete",e,void 0),i}function Ue(){const e=ct(this),t=0!==e.size,n=e.clear();return t&&ye(e,"clear",void 0,void 0),n}function Fe(e,t){return function(n,r){const o=this,i=o.__v_raw,s=ct(i),a=t?Pe:e?pt:ut;return!e&&de(s,0,ie),i.forEach(((e,t)=>n.call(r,a(e),a(t),o)))}}function Be(e,t,n){return function(...r){const o=this.__v_raw,i=ct(o),s=A(i),a="entries"===e||e===Symbol.iterator&&s,c="keys"===e&&s,l=o[e](...r),u=n?Pe:t?pt:ut;return!t&&de(i,0,c?se:ie),{next(){const{value:e,done:t}=l.next();return t?{value:e,done:t}:{value:a?[u(e[0]),u(e[1])]:u(e),done:t}},[Symbol.iterator](){return this}}}}function ze(e){return function(...t){return"delete"!==e&&this}}function Ve(){const e={get(e){return Ne(this,e)},get size(){return Ie(this)},has:Le,add:De,set:Re,delete:ke,clear:Ue,forEach:Fe(!1,!1)},t={get(e){return Ne(this,e,!1,!0)},get size(){return Ie(this)},has:Le,add:De,set:Re,delete:ke,clear:Ue,forEach:Fe(!1,!0)},n={get(e){return Ne(this,e,!0)},get size(){return Ie(this,!0)},has(e){return Le.call(this,e,!0)},add:ze("add"),set:ze("set"),delete:ze("delete"),clear:ze("clear"),forEach:Fe(!0,!1)},r={get(e){return Ne(this,e,!0,!0)},get size(){return Ie(this,!0)},has(e){return Le.call(this,e,!0)},add:ze("add"),set:ze("set"),delete:ze("delete"),clear:ze("clear"),forEach:Fe(!0,!0)};return["keys","values","entries",Symbol.iterator].forEach((o=>{e[o]=Be(o,!1,!1),n[o]=Be(o,!0,!1),t[o]=Be(o,!1,!0),r[o]=Be(o,!0,!0)})),[e,n,t,r]}const[Ge,He,$e,We]=Ve();function Je(e,t){const n=t?e?We:$e:e?He:Ge;return(t,r,o)=>"__v_isReactive"===r?!e:"__v_isReadonly"===r?e:"__v_raw"===r?t:Reflect.get(O(n,r)&&r in t?n:t,r,o)}const Xe={get:Je(!1,!1)},Ze={get:Je(!1,!0)},qe={get:Je(!0,!1)},Qe=new WeakMap,Ke=new WeakMap,Ye=new WeakMap,et=new WeakMap;function tt(e){return it(e)?e:rt(e,!1,je,Xe,Qe)}function nt(e){return rt(e,!0,Te,qe,Ye)}function rt(e,t,n,r,o){if(!P(e))return e;if(e.__v_raw&&(!t||!e.__v_isReactive))return e;const i=o.get(e);if(i)return i;const s=(a=e).__v_skip||!Object.isExtensible(a)?0:function(e){switch(e){case"Object":case"Array":return 1;case"Map":case"Set":case"WeakMap":case"WeakSet":return 2;default:return 0}}((e=>L(e).slice(8,-1))(a));var a;if(0===s)return e;const c=new Proxy(e,2===s?r:n);return o.set(e,c),c}function ot(e){return it(e)?ot(e.__v_raw):!(!e||!e.__v_isReactive)}function it(e){return!(!e||!e.__v_isReadonly)}function st(e){return!(!e||!e.__v_isShallow)}function at(e){return ot(e)||it(e)}function ct(e){const t=e&&e.__v_raw;return t?ct(t):e}function lt(e){return W(e,"__v_skip",!0),e}const ut=e=>P(e)?tt(e):e,pt=e=>P(e)?nt(e):e;function ft(e){return!(!e||!0!==e.__v_isRef)}const dt={get:(e,t,n)=>{return ft(r=Reflect.get(e,t,n))?r.value:r;var r},set:(e,t,n,r)=>{const o=e[t];return ft(o)&&!ft(n)?(o.value=n,!0):Reflect.set(e,t,n,r)}};function ht(e){return ot(e)?e:new Proxy(e,dt)}class yt{constructor(e,t,n,r){this._setter=t,this.dep=void 0,this.__v_isRef=!0,this._dirty=!0,this.effect=new ae(e,(()=>{var e;this._dirty||(this._dirty=!0,(e=ct(e=this)).dep&&me(e.dep))})),this.effect.computed=this,this.effect.active=this._cacheable=!r,this.__v_isReadonly=n}get value(){const e=ct(this);return t=e,le&&ne&&he((t=ct(t)).dep||(t.dep=K())),!e._dirty&&e._cacheable||(e._dirty=!1,e._value=e.effect.run()),e._value;var t}set value(e){this._setter(e)}}function mt(e,t,n,r){let o;try{o=r?e(...r):e()}catch(e){vt(e,t,n)}return o}function gt(e,t,n,r){if(j(e)){const o=mt(e,t,n,r);return o&&C(o)&&o.catch((e=>{vt(e,t,n)})),o}const o=[];for(let i=0;i<e.length;i++)o.push(gt(e[i],t,n,r));return o}function vt(e,t,n,r=!0){if(t&&t.vnode,t){let r=t.parent;const o=t.proxy,i=n;for(;r;){const t=r.ec;if(t)for(let n=0;n<t.length;n++)if(!1===t[n](e,o,i))return;r=r.parent}const s=t.appContext.config.errorHandler;if(s)return void mt(s,null,10,[e,o,i])}!function(e,t,n,r=!0){console.error(e)}(e,0,0,r)}let bt=!1,_t=!1;const wt=[];let St=0;const Ot=[];let Et=null,At=0;const xt=[];let jt=null,Tt=0;const Mt=Promise.resolve();let Pt=null,Ct=null;function Nt(e){const t=Pt||Mt;return e?t.then(this?e.bind(this):e):t}function Lt(e){wt.length&&wt.includes(e,bt&&e.allowRecurse?St+1:St)||e===Ct||(null==e.id?wt.push(e):wt.splice(function(e){let t=St+1,n=wt.length;for(;t<n;){const r=t+n>>>1;Ut(wt[r])<e?t=r+1:n=r}return t}(e.id),0,e),It())}function It(){bt||_t||(_t=!0,Pt=Mt.then(Ft))}function Dt(e,t,n,r){E(e)?n.push(...e):t&&t.includes(e,e.allowRecurse?r+1:r)||n.push(e),It()}function Rt(e,t=null){if(Ot.length){for(Ct=t,Et=[...new Set(Ot)],Ot.length=0,At=0;At<Et.length;At++)Et[At]();Et=null,At=0,Ct=null,Rt(e,t)}}function kt(e){if(Rt(),xt.length){const e=[...new Set(xt)];if(xt.length=0,jt)return void jt.push(...e);for(jt=e,jt.sort(((e,t)=>Ut(e)-Ut(t))),Tt=0;Tt<jt.length;Tt++)jt[Tt]();jt=null,Tt=0}}const Ut=e=>null==e.id?1/0:e.id;function Ft(e){_t=!1,bt=!0,Rt(e),wt.sort(((e,t)=>Ut(e)-Ut(t)));try{for(St=0;St<wt.length;St++){const e=wt[St];e&&!1!==e.active&&mt(e,null,14)}}finally{St=0,wt.length=0,kt(),bt=!1,Pt=null,(wt.length||Ot.length||xt.length)&&Ft(e)}}new Set,new Map;let Bt,zt=[],Vt=!1;function Gt(e,...t){Bt?Bt.emit(e,...t):Vt||zt.push({event:e,args:t})}function Ht(e,t){var n,r;Bt=e,Bt?(Bt.enabled=!0,zt.forEach((({event:e,args:t})=>Bt.emit(e,...t))),zt=[]):"undefined"!=typeof window&&window.HTMLElement&&!(null===(r=null===(n=window.navigator)||void 0===n?void 0:n.userAgent)||void 0===r?void 0:r.includes("jsdom"))?((t.__VUE_DEVTOOLS_HOOK_REPLAY__=t.__VUE_DEVTOOLS_HOOK_REPLAY__||[]).push((e=>{Ht(e,t)})),setTimeout((()=>{Bt||(t.__VUE_DEVTOOLS_HOOK_REPLAY__=null,Vt=!0,zt=[])}),3e3)):(Vt=!0,zt=[])}const $t=Xt("component:added"),Wt=Xt("component:updated"),Jt=Xt("component:removed");function Xt(e){return t=>{Gt(e,t.appContext.app,t.uid,t.parent?t.parent.uid:void 0,t)}}function Zt(e,t,...n){if(e.isUnmounted)return;const r=e.vnode.props||d;let o=n;const i=t.startsWith("update:"),s=i&&t.slice(7);if(s&&s in r){const e=`${"modelValue"===s?"model":s}Modifiers`,{number:t,trim:i}=r[e]||d;i&&(o=n.map((e=>e.trim()))),t&&(o=n.map(J))}let a;__VUE_PROD_DEVTOOLS__&&function(e,t,n){Gt("component:emit",e.appContext.app,e,t,n)}(e,t,o);let c=r[a=G(t)]||r[a=G(F(t))];!c&&i&&(c=r[a=G(z(t))]),c&>(c,e,6,o);const l=r[a+"Once"];if(l){if(e.emitted){if(e.emitted[a])return}else e.emitted={};e.emitted[a]=!0,gt(l,e,6,o)}}function qt(e,t,n=!1){const r=t.emitsCache,o=r.get(e);if(void 0!==o)return o;const i=e.emits;let s={},a=!1;if(__VUE_OPTIONS_API__&&!j(e)){const r=e=>{const n=qt(e,t,!0);n&&(a=!0,_(s,n))};!n&&t.mixins.length&&t.mixins.forEach(r),e.extends&&r(e.extends),e.mixins&&e.mixins.forEach(r)}return i||a?(E(i)?i.forEach((e=>s[e]=null)):_(s,i),r.set(e,s),s):(r.set(e,null),null)}function Qt(e,t){return!(!e||!v(t))&&(t=t.slice(2).replace(/Once$/,""),O(e,t[0].toLowerCase()+t.slice(1))||O(e,z(t))||O(e,t))}let Kt=null,Yt=null;function en(e){const t=Kt;return Kt=e,Yt=e&&e.type.__scopeId||null,t}function tn(e){const{type:t,vnode:n,proxy:r,withProxy:o,props:i,propsOptions:[s],slots:a,attrs:c,emit:l,render:u,renderCache:p,data:f,setupState:d,ctx:h,inheritAttrs:y}=e;let m,g;const v=en(e);try{if(4&n.shapeFlag){const e=o||r;m=$r(u.call(e,e,p,i,d,f,h)),g=c}else{const e=t;m=$r(e.length>1?e(i,{attrs:c,slots:a,emit:l}):e(i,null)),g=t.props?c:nn(c)}}catch(t){jr.length=0,vt(t,e,1),m=zr(Ar)}let _=m;if(g&&!1!==y){const e=Object.keys(g),{shapeFlag:t}=_;e.length&&7&t&&(s&&e.some(b)&&(g=rn(g,s)),_=Vr(_,g))}return n.dirs&&(_=Vr(_),_.dirs=_.dirs?_.dirs.concat(n.dirs):n.dirs),n.transition&&(_.transition=n.transition),m=_,en(v),m}const nn=e=>{let t;for(const n in e)("class"===n||"style"===n||v(n))&&((t||(t={}))[n]=e[n]);return t},rn=(e,t)=>{const n={};for(const r in e)b(r)&&r.slice(9)in t||(n[r]=e[r]);return n};function on(e,t,n){const r=Object.keys(t);if(r.length!==Object.keys(e).length)return!0;for(let o=0;o<r.length;o++){const i=r[o];if(t[i]!==e[i]&&!Qt(n,i))return!0}return!1}function sn(e,t,n=!1){const r=Qr||Kt;if(r){const o=null==r.parent?r.vnode.appContext&&r.vnode.appContext.provides:r.parent.provides;if(o&&e in o)return o[e];if(arguments.length>1)return n&&j(t)?t.call(r.proxy):t}}const an={};function cn(e,t,n){return ln(e,t,n)}function ln(e,t,{immediate:n,deep:r,flush:o,onTrack:i,onTrigger:s}=d){const a=Qr;let c,l,u=!1,p=!1;if(ft(e)?(c=()=>e.value,u=st(e)):ot(e)?(c=()=>e,r=!0):E(e)?(p=!0,u=e.some((e=>ot(e)||st(e))),c=()=>e.map((e=>ft(e)?e.value:ot(e)?fn(e):j(e)?mt(e,a,2):void 0))):c=j(e)?t?()=>mt(e,a,2):()=>{if(!a||!a.isUnmounted)return l&&l(),gt(e,a,3,[f])}:y,t&&r){const e=c;c=()=>fn(e())}let f=e=>{l=v.onStop=()=>{mt(e,a,4)}};if(no)return f=y,t?n&>(t,a,3,[c(),p?[]:void 0,f]):c(),y;let h=p?[]:an;const m=()=>{if(v.active)if(t){const e=v.run();(r||u||(p?e.some(((e,t)=>H(e,h[t]))):H(e,h)))&&(l&&l(),gt(t,a,3,[e,h===an?void 0:h,f]),h=e)}else v.run()};let g;m.allowRecurse=!!t,g="sync"===o?m:"post"===o?()=>br(m,a&&a.suspense):()=>function(e){Dt(e,Et,Ot,At)}(m);const v=new ae(c,g);return t?n?m():h=v.run():"post"===o?br(v.run.bind(v),a&&a.suspense):v.run(),()=>{v.stop(),a&&a.scope&&w(a.scope.effects,v)}}function un(e,t,n){const r=this.proxy,o=T(e)?e.includes(".")?pn(r,e):()=>r[e]:e.bind(r,r);let i;j(t)?i=t:(i=t.handler,n=t);const s=Qr;Yr(this);const a=ln(o,i.bind(r),n);return s?Yr(s):eo(),a}function pn(e,t){const n=t.split(".");return()=>{let t=e;for(let e=0;e<n.length&&t;e++)t=t[n[e]];return t}}function fn(e,t){if(!P(e)||e.__v_skip)return e;if((t=t||new Set).has(e))return e;if(t.add(e),ft(e))fn(e.value,t);else if(E(e))for(let n=0;n<e.length;n++)fn(e[n],t);else if(x(e)||A(e))e.forEach((e=>{fn(e,t)}));else if(I(e))for(const n in e)fn(e[n],t);return e}const dn=[Function,Array],hn={name:"BaseTransition",props:{mode:String,appear:Boolean,persisted:Boolean,onBeforeEnter:dn,onEnter:dn,onAfterEnter:dn,onEnterCancelled:dn,onBeforeLeave:dn,onLeave:dn,onAfterLeave:dn,onLeaveCancelled:dn,onBeforeAppear:dn,onAppear:dn,onAfterAppear:dn,onAppearCancelled:dn},setup(e,{slots:t}){const n=Kr(),r=function(){const e={isMounted:!1,isLeaving:!1,isUnmounting:!1,leavingVNodes:new Map};return Pn((()=>{e.isMounted=!0})),Ln((()=>{e.isUnmounting=!0})),e}();let o;return()=>{const i=t.default&&_n(t.default(),!0);if(!i||!i.length)return;let s=i[0];if(i.length>1){let e=!1;for(const t of i)if(t.type!==Ar){s=t,e=!0;break}}const a=ct(e),{mode:c}=a;if(r.isLeaving)return gn(s);const l=vn(s);if(!l)return gn(s);const u=mn(l,a,r,n);bn(l,u);const p=n.subTree,f=p&&vn(p);let d=!1;const{getTransitionKey:h}=l.type;if(h){const e=h();void 0===o?o=e:e!==o&&(o=e,d=!0)}if(f&&f.type!==Ar&&(!Rr(l,f)||d)){const e=mn(f,a,r,n);if(bn(f,e),"out-in"===c)return r.isLeaving=!0,e.afterLeave=()=>{r.isLeaving=!1,n.update()},gn(s);"in-out"===c&&l.type!==Ar&&(e.delayLeave=(e,t,n)=>{yn(r,f)[String(f.key)]=f,e._leaveCb=()=>{t(),e._leaveCb=void 0,delete u.delayedLeave},u.delayedLeave=n})}return s}}};function yn(e,t){const{leavingVNodes:n}=e;let r=n.get(t.type);return r||(r=Object.create(null),n.set(t.type,r)),r}function mn(e,t,n,r){const{appear:o,mode:i,persisted:s=!1,onBeforeEnter:a,onEnter:c,onAfterEnter:l,onEnterCancelled:u,onBeforeLeave:p,onLeave:f,onAfterLeave:d,onLeaveCancelled:h,onBeforeAppear:y,onAppear:m,onAfterAppear:g,onAppearCancelled:v}=t,b=String(e.key),_=yn(n,e),w=(e,t)=>{e&>(e,r,9,t)},S=(e,t)=>{const n=t[1];w(e,t),E(e)?e.every((e=>e.length<=1))&&n():e.length<=1&&n()},O={mode:i,persisted:s,beforeEnter(t){let r=a;if(!n.isMounted){if(!o)return;r=y||a}t._leaveCb&&t._leaveCb(!0);const i=_[b];i&&Rr(e,i)&&i.el._leaveCb&&i.el._leaveCb(),w(r,[t])},enter(e){let t=c,r=l,i=u;if(!n.isMounted){if(!o)return;t=m||c,r=g||l,i=v||u}let s=!1;const a=e._enterCb=t=>{s||(s=!0,w(t?i:r,[e]),O.delayedLeave&&O.delayedLeave(),e._enterCb=void 0)};t?S(t,[e,a]):a()},leave(t,r){const o=String(e.key);if(t._enterCb&&t._enterCb(!0),n.isUnmounting)return r();w(p,[t]);let i=!1;const s=t._leaveCb=n=>{i||(i=!0,r(),w(n?h:d,[t]),t._leaveCb=void 0,_[o]===e&&delete _[o])};_[o]=e,f?S(f,[t,s]):s()},clone:e=>mn(e,t,n,r)};return O}function gn(e){if(Sn(e))return(e=Vr(e)).children=null,e}function vn(e){return Sn(e)?e.children?e.children[0]:void 0:e}function bn(e,t){6&e.shapeFlag&&e.component?bn(e.component.subTree,t):128&e.shapeFlag?(e.ssContent.transition=t.clone(e.ssContent),e.ssFallback.transition=t.clone(e.ssFallback)):e.transition=t}function _n(e,t=!1,n){let r=[],o=0;for(let i=0;i<e.length;i++){let s=e[i];const a=null==n?s.key:String(n)+String(null!=s.key?s.key:i);s.type===Or?(128&s.patchFlag&&o++,r=r.concat(_n(s.children,t,a))):(t||s.type!==Ar)&&r.push(null!=a?Vr(s,{key:a}):s)}if(o>1)for(let e=0;e<r.length;e++)r[e].patchFlag=-2;return r}const wn=e=>!!e.type.__asyncLoader,Sn=e=>e.type.__isKeepAlive;function On(e,t){An(e,"a",t)}function En(e,t){An(e,"da",t)}function An(e,t,n=Qr){const r=e.__wdc||(e.__wdc=()=>{let t=n;for(;t;){if(t.isDeactivated)return;t=t.parent}return e()});if(jn(t,r,n),n){let e=n.parent;for(;e&&e.parent;)Sn(e.parent.vnode)&&xn(r,t,n,e),e=e.parent}}function xn(e,t,n,r){const o=jn(t,e,r,!0);In((()=>{w(r[t],o)}),n)}function jn(e,t,n=Qr,r=!1){if(n){const o=n[e]||(n[e]=[]),i=t.__weh||(t.__weh=(...r)=>{if(n.isUnmounted)return;pe(),Yr(n);const o=gt(t,n,e,r);return eo(),fe(),o});return r?o.unshift(i):o.push(i),i}}RegExp,RegExp;const Tn=e=>(t,n=Qr)=>(!no||"sp"===e)&&jn(e,t,n),Mn=Tn("bm"),Pn=Tn("m"),Cn=Tn("bu"),Nn=Tn("u"),Ln=Tn("bum"),In=Tn("um"),Dn=Tn("sp"),Rn=Tn("rtg"),kn=Tn("rtc");function Un(e,t=Qr){jn("ec",e,t)}function Fn(e,t,n,r){const o=e.dirs,i=t&&t.dirs;for(let s=0;s<o.length;s++){const a=o[s];i&&(a.oldValue=i[s].value);let c=a.dir[r];c&&(pe(),gt(c,n,8,[e.el,a,e,t]),fe())}}const Bn="components";function zn(e,t){return function(e,t,n=!0,r=!1){const o=Kt||Qr;if(o){const n=o.type;if(e===Bn){const e=function(e,t=!0){return j(e)?e.displayName||e.name:e.name||t&&e.__name}(n,!1);if(e&&(e===t||e===F(t)||e===V(F(t))))return n}const i=Gn(o[e]||n[e],t)||Gn(o.appContext[e],t);return!i&&r?n:i}}(Bn,e,!0,t)||e}const Vn=Symbol();function Gn(e,t){return e&&(e[t]||e[F(t)]||e[V(F(t))])}const Hn=e=>e?to(e)?io(e)||e.proxy:Hn(e.parent):null,$n=_(Object.create(null),{$:e=>e,$el:e=>e.vnode.el,$data:e=>e.data,$props:e=>e.props,$attrs:e=>e.attrs,$slots:e=>e.slots,$refs:e=>e.refs,$parent:e=>Hn(e.parent),$root:e=>Hn(e.root),$emit:e=>e.emit,$options:e=>__VUE_OPTIONS_API__?qn(e):e.type,$forceUpdate:e=>e.f||(e.f=()=>Lt(e.update)),$nextTick:e=>e.n||(e.n=Nt.bind(e.proxy)),$watch:e=>__VUE_OPTIONS_API__?un.bind(e):y}),Wn={get({_:e},t){const{ctx:n,setupState:r,data:o,props:i,accessCache:s,type:a,appContext:c}=e;let l;if("$"!==t[0]){const a=s[t];if(void 0!==a)switch(a){case 1:return r[t];case 2:return o[t];case 4:return n[t];case 3:return i[t]}else{if(r!==d&&O(r,t))return s[t]=1,r[t];if(o!==d&&O(o,t))return s[t]=2,o[t];if((l=e.propsOptions[0])&&O(l,t))return s[t]=3,i[t];if(n!==d&&O(n,t))return s[t]=4,n[t];__VUE_OPTIONS_API__&&!Jn||(s[t]=0)}}const u=$n[t];let p,f;return u?("$attrs"===t&&de(e,0,t),u(e)):(p=a.__cssModules)&&(p=p[t])?p:n!==d&&O(n,t)?(s[t]=4,n[t]):(f=c.config.globalProperties,O(f,t)?f[t]:void 0)},set({_:e},t,n){const{data:r,setupState:o,ctx:i}=e;return o!==d&&O(o,t)?(o[t]=n,!0):r!==d&&O(r,t)?(r[t]=n,!0):!(O(e.props,t)||"$"===t[0]&&t.slice(1)in e||(i[t]=n,0))},has({_:{data:e,setupState:t,accessCache:n,ctx:r,appContext:o,propsOptions:i}},s){let a;return!!n[s]||e!==d&&O(e,s)||t!==d&&O(t,s)||(a=i[0])&&O(a,s)||O(r,s)||O($n,s)||O(o.config.globalProperties,s)},defineProperty(e,t,n){return null!=n.get?e._.accessCache[t]=0:O(n,"value")&&this.set(e,t,n.value,null),Reflect.defineProperty(e,t,n)}};let Jn=!0;function Xn(e,t,n){gt(E(e)?e.map((e=>e.bind(t.proxy))):e.bind(t.proxy),t,n)}function Zn(e,t,n,r){const o=r.includes(".")?pn(n,r):()=>n[r];if(T(e)){const n=t[e];j(n)&&cn(o,n)}else if(j(e))cn(o,e.bind(n));else if(P(e))if(E(e))e.forEach((e=>Zn(e,t,n,r)));else{const r=j(e.handler)?e.handler.bind(n):t[e.handler];j(r)&&cn(o,r,e)}}function qn(e){const t=e.type,{mixins:n,extends:r}=t,{mixins:o,optionsCache:i,config:{optionMergeStrategies:s}}=e.appContext,a=i.get(t);let c;return a?c=a:o.length||n||r?(c={},o.length&&o.forEach((e=>Qn(c,e,s,!0))),Qn(c,t,s)):c=t,i.set(t,c),c}function Qn(e,t,n,r=!1){const{mixins:o,extends:i}=t;i&&Qn(e,i,n,!0),o&&o.forEach((t=>Qn(e,t,n,!0)));for(const o in t)if(r&&"expose"===o);else{const r=Kn[o]||n&&n[o];e[o]=r?r(e[o],t[o]):t[o]}return e}const Kn={data:Yn,props:nr,emits:nr,methods:nr,computed:nr,beforeCreate:tr,created:tr,beforeMount:tr,mounted:tr,beforeUpdate:tr,updated:tr,beforeDestroy:tr,beforeUnmount:tr,destroyed:tr,unmounted:tr,activated:tr,deactivated:tr,errorCaptured:tr,serverPrefetch:tr,components:nr,directives:nr,watch:function(e,t){if(!e)return t;if(!t)return e;const n=_(Object.create(null),e);for(const r in t)n[r]=tr(e[r],t[r]);return n},provide:Yn,inject:function(e,t){return nr(er(e),er(t))}};function Yn(e,t){return t?e?function(){return _(j(e)?e.call(this,this):e,j(t)?t.call(this,this):t)}:t:e}function er(e){if(E(e)){const t={};for(let n=0;n<e.length;n++)t[e[n]]=e[n];return t}return e}function tr(e,t){return e?[...new Set([].concat(e,t))]:t}function nr(e,t){return e?_(_(Object.create(null),e),t):t}function rr(e,t,n,r){const[o,i]=e.propsOptions;let s,a=!1;if(t)for(let c in t){if(R(c))continue;const l=t[c];let u;o&&O(o,u=F(c))?i&&i.includes(u)?(s||(s={}))[u]=l:n[u]=l:Qt(e.emitsOptions,c)||c in r&&l===r[c]||(r[c]=l,a=!0)}if(i){const t=ct(n),r=s||d;for(let s=0;s<i.length;s++){const a=i[s];n[a]=or(o,t,a,r[a],e,!O(r,a))}}return a}function or(e,t,n,r,o,i){const s=e[n];if(null!=s){const e=O(s,"default");if(e&&void 0===r){const e=s.default;if(s.type!==Function&&j(e)){const{propsDefaults:i}=o;n in i?r=i[n]:(Yr(o),r=i[n]=e.call(null,t),eo())}else r=e}s[0]&&(i&&!e?r=!1:!s[1]||""!==r&&r!==z(n)||(r=!0))}return r}function ir(e,t,n=!1){const r=t.propsCache,o=r.get(e);if(o)return o;const i=e.props,s={},a=[];let c=!1;if(__VUE_OPTIONS_API__&&!j(e)){const r=e=>{c=!0;const[n,r]=ir(e,t,!0);_(s,n),r&&a.push(...r)};!n&&t.mixins.length&&t.mixins.forEach(r),e.extends&&r(e.extends),e.mixins&&e.mixins.forEach(r)}if(!i&&!c)return r.set(e,h),h;if(E(i))for(let e=0;e<i.length;e++){const t=F(i[e]);sr(t)&&(s[t]=d)}else if(i)for(const e in i){const t=F(e);if(sr(t)){const n=i[e],r=s[t]=E(n)||j(n)?{type:n}:n;if(r){const e=lr(Boolean,r.type),n=lr(String,r.type);r[0]=e>-1,r[1]=n<0||e<n,(e>-1||O(r,"default"))&&a.push(t)}}}const l=[s,a];return r.set(e,l),l}function sr(e){return"$"!==e[0]}function ar(e){const t=e&&e.toString().match(/^\s*function (\w+)/);return t?t[1]:null===e?"null":""}function cr(e,t){return ar(e)===ar(t)}function lr(e,t){return E(t)?t.findIndex((t=>cr(t,e))):j(t)&&cr(t,e)?0:-1}const ur=e=>"_"===e[0]||"$stable"===e,pr=e=>E(e)?e.map($r):[$r(e)],fr=(e,t,n)=>{if(t._n)return t;const r=function(e,t=Kt,n){if(!t)return e;if(e._n)return e;const r=(...n)=>{r._d&&Cr(-1);const o=en(t),i=e(...n);return en(o),r._d&&Cr(1),__VUE_PROD_DEVTOOLS__&&Wt(t),i};return r._n=!0,r._c=!0,r._d=!0,r}(((...e)=>pr(t(...e))),n);return r._c=!1,r},dr=(e,t,n)=>{const r=e._ctx;for(const n in e){if(ur(n))continue;const o=e[n];if(j(o))t[n]=fr(0,o,r);else if(null!=o){const e=pr(o);t[n]=()=>e}}},hr=(e,t)=>{const n=pr(t);e.slots.default=()=>n};function yr(){return{app:null,config:{isNativeTag:m,performance:!1,globalProperties:{},optionMergeStrategies:{},errorHandler:void 0,warnHandler:void 0,compilerOptions:{}},mixins:[],components:{},directives:{},provides:Object.create(null),optionsCache:new WeakMap,propsCache:new WeakMap,emitsCache:new WeakMap}}let mr=0;function gr(e,t){return function(n,r=null){j(n)||(n=Object.assign({},n)),null==r||P(r)||(r=null);const o=yr(),i=new Set;let s=!1;const a=o.app={_uid:mr++,_component:n,_props:r,_container:null,_context:o,_instance:null,version:ao,get config(){return o.config},set config(e){},use:(e,...t)=>(i.has(e)||(e&&j(e.install)?(i.add(e),e.install(a,...t)):j(e)&&(i.add(e),e(a,...t))),a),mixin:e=>(__VUE_OPTIONS_API__&&(o.mixins.includes(e)||o.mixins.push(e)),a),component:(e,t)=>t?(o.components[e]=t,a):o.components[e],directive:(e,t)=>t?(o.directives[e]=t,a):o.directives[e],mount(i,c,l){if(!s){const u=zr(n,r);return u.appContext=o,c&&t?t(u,i):e(u,i,l),s=!0,a._container=i,i.__vue_app__=a,__VUE_PROD_DEVTOOLS__&&(a._instance=u.component,function(e,t){Gt("app:init",e,t,{Fragment:Or,Text:Er,Comment:Ar,Static:xr})}(a,ao)),io(u.component)||u.component.proxy}},unmount(){s&&(e(null,a._container),__VUE_PROD_DEVTOOLS__&&(a._instance=null,function(e){Gt("app:unmount",e)}(a)),delete a._container.__vue_app__)},provide:(e,t)=>(o.provides[e]=t,a)};return a}}function vr(e,t,n,r,o=!1){if(E(e))return void e.forEach(((e,i)=>vr(e,t&&(E(t)?t[i]:t),n,r,o)));if(wn(r)&&!o)return;const i=4&r.shapeFlag?io(r.component)||r.component.proxy:r.el,s=o?null:i,{i:a,r:c}=e,l=t&&t.r,u=a.refs===d?a.refs={}:a.refs,p=a.setupState;if(null!=l&&l!==c&&(T(l)?(u[l]=null,O(p,l)&&(p[l]=null)):ft(l)&&(l.value=null)),j(c))mt(c,a,12,[s,u]);else{const t=T(c),r=ft(c);if(t||r){const a=()=>{if(e.f){const n=t?u[c]:c.value;o?E(n)&&w(n,i):E(n)?n.includes(i)||n.push(i):t?(u[c]=[i],O(p,c)&&(p[c]=u[c])):(c.value=[i],e.k&&(u[e.k]=c.value))}else t?(u[c]=s,O(p,c)&&(p[c]=s)):r&&(c.value=s,e.k&&(u[e.k]=s))};s?(a.id=-1,br(a,n)):a()}}}const br=function(e,t){t&&t.pendingBranch?E(e)?t.effects.push(...e):t.effects.push(e):Dt(e,jt,xt,Tt)};function _r(e,t){"boolean"!=typeof __VUE_OPTIONS_API__&&(Z().__VUE_OPTIONS_API__=!0),"boolean"!=typeof __VUE_PROD_DEVTOOLS__&&(Z().__VUE_PROD_DEVTOOLS__=!1);const n=Z();n.__VUE__=!0,__VUE_PROD_DEVTOOLS__&&Ht(n.__VUE_DEVTOOLS_GLOBAL_HOOK__,n);const{insert:r,remove:o,patchProp:i,createElement:s,createText:a,createComment:c,setText:l,setElementText:u,parentNode:p,nextSibling:f,setScopeId:m=y,cloneNode:g,insertStaticContent:v}=e,b=(e,t,n,r=null,o=null,i=null,s=!1,a=null,c=!!t.dynamicChildren)=>{if(e===t)return;e&&!Rr(e,t)&&(r=te(e),X(e,o,i,!0),e=null),-2===t.patchFlag&&(c=!1,t.dynamicChildren=null);const{type:l,ref:u,shapeFlag:p}=t;switch(l){case Er:w(e,t,n,r);break;case Ar:S(e,t,n,r);break;case xr:null==e&&E(t,n,r,s);break;case Or:L(e,t,n,r,o,i,s,a,c);break;default:1&p?A(e,t,n,r,o,i,s,a,c):6&p?I(e,t,n,r,o,i,s,a,c):(64&p||128&p)&&l.process(e,t,n,r,o,i,s,a,c,re)}null!=u&&o&&vr(u,e&&e.ref,i,t||e,!t)},w=(e,t,n,o)=>{if(null==e)r(t.el=a(t.children),n,o);else{const n=t.el=e.el;t.children!==e.children&&l(n,t.children)}},S=(e,t,n,o)=>{null==e?r(t.el=c(t.children||""),n,o):t.el=e.el},E=(e,t,n,r)=>{[e.el,e.anchor]=v(e.children,t,n,r,e.el,e.anchor)},A=(e,t,n,r,o,i,s,a,c)=>{s=s||"svg"===t.type,null==e?x(t,n,r,o,i,s,a,c):M(e,t,o,i,s,a,c)},x=(e,t,n,o,a,c,l,p)=>{let f,d;const{type:h,props:y,shapeFlag:m,transition:v,patchFlag:b,dirs:_}=e;if(e.el&&void 0!==g&&-1===b)f=e.el=g(e.el);else{if(f=e.el=s(e.type,c,y&&y.is,y),8&m?u(f,e.children):16&m&&T(e.children,f,null,o,a,c&&"foreignObject"!==h,l,p),_&&Fn(e,null,o,"created"),y){for(const t in y)"value"===t||R(t)||i(f,t,null,y[t],c,e.children,o,a,ee);"value"in y&&i(f,"value",null,y.value),(d=y.onVnodeBeforeMount)&&Xr(d,o,e)}j(f,e,e.scopeId,l,o)}__VUE_PROD_DEVTOOLS__&&(Object.defineProperty(f,"__vnode",{value:e,enumerable:!1}),Object.defineProperty(f,"__vueParentComponent",{value:o,enumerable:!1})),_&&Fn(e,null,o,"beforeMount");const w=(!a||a&&!a.pendingBranch)&&v&&!v.persisted;w&&v.beforeEnter(f),r(f,t,n),((d=y&&y.onVnodeMounted)||w||_)&&br((()=>{d&&Xr(d,o,e),w&&v.enter(f),_&&Fn(e,null,o,"mounted")}),a)},j=(e,t,n,r,o)=>{if(n&&m(e,n),r)for(let t=0;t<r.length;t++)m(e,r[t]);if(o&&t===o.subTree){const t=o.vnode;j(e,t,t.scopeId,t.slotScopeIds,o.parent)}},T=(e,t,n,r,o,i,s,a,c=0)=>{for(let l=c;l<e.length;l++){const c=e[l]=a?Wr(e[l]):$r(e[l]);b(null,c,t,n,r,o,i,s,a)}},M=(e,t,n,r,o,s,a)=>{const c=t.el=e.el;let{patchFlag:l,dynamicChildren:p,dirs:f}=t;l|=16&e.patchFlag;const h=e.props||d,y=t.props||d;let m;n&&wr(n,!1),(m=y.onVnodeBeforeUpdate)&&Xr(m,n,t,e),f&&Fn(t,e,n,"beforeUpdate"),n&&wr(n,!0);const g=o&&"foreignObject"!==t.type;if(p?P(e.dynamicChildren,p,c,n,r,g,s):a||V(e,t,c,null,n,r,g,s,!1),l>0){if(16&l)N(c,t,h,y,n,r,o);else if(2&l&&h.class!==y.class&&i(c,"class",null,y.class,o),4&l&&i(c,"style",h.style,y.style,o),8&l){const s=t.dynamicProps;for(let t=0;t<s.length;t++){const a=s[t],l=h[a],u=y[a];u===l&&"value"!==a||i(c,a,l,u,o,e.children,n,r,ee)}}1&l&&e.children!==t.children&&u(c,t.children)}else a||null!=p||N(c,t,h,y,n,r,o);((m=y.onVnodeUpdated)||f)&&br((()=>{m&&Xr(m,n,t,e),f&&Fn(t,e,n,"updated")}),r)},P=(e,t,n,r,o,i,s)=>{for(let a=0;a<t.length;a++){const c=e[a],l=t[a],u=c.el&&(c.type===Or||!Rr(c,l)||70&c.shapeFlag)?p(c.el):n;b(c,l,u,null,r,o,i,s,!0)}},N=(e,t,n,r,o,s,a)=>{if(n!==r){for(const c in r){if(R(c))continue;const l=r[c],u=n[c];l!==u&&"value"!==c&&i(e,c,u,l,a,t.children,o,s,ee)}if(n!==d)for(const c in n)R(c)||c in r||i(e,c,n[c],null,a,t.children,o,s,ee);"value"in r&&i(e,"value",n.value,r.value)}},L=(e,t,n,o,i,s,c,l,u)=>{const p=t.el=e?e.el:a(""),f=t.anchor=e?e.anchor:a("");let{patchFlag:d,dynamicChildren:h,slotScopeIds:y}=t;y&&(l=l?l.concat(y):y),null==e?(r(p,n,o),r(f,n,o),T(t.children,n,f,i,s,c,l,u)):d>0&&64&d&&h&&e.dynamicChildren?(P(e.dynamicChildren,h,n,i,s,c,l),(null!=t.key||i&&t===i.subTree)&&Sr(e,t,!0)):V(e,t,n,f,i,s,c,l,u)},I=(e,t,n,r,o,i,s,a,c)=>{t.slotScopeIds=a,null==e?512&t.shapeFlag?o.ctx.activate(t,n,r,s,c):D(t,n,r,o,i,s,c):k(e,t,c)},D=(e,t,n,r,o,i,s)=>{const a=e.component=function(e,t,n){const r=e.type,o=(t?t.appContext:e.appContext)||Zr,i={uid:qr++,vnode:e,type:r,parent:t,appContext:o,root:null,next:null,subTree:null,effect:null,update:null,scope:new Q(!0),render:null,proxy:null,exposed:null,exposeProxy:null,withProxy:null,provides:t?t.provides:Object.create(o.provides),accessCache:null,renderCache:[],components:null,directives:null,propsOptions:ir(r,o),emitsOptions:qt(r,o),emit:null,emitted:null,propsDefaults:d,inheritAttrs:r.inheritAttrs,ctx:d,data:d,props:d,attrs:d,slots:d,refs:d,setupState:d,setupContext:null,suspense:n,suspenseId:n?n.pendingId:0,asyncDep:null,asyncResolved:!1,isMounted:!1,isUnmounted:!1,isDeactivated:!1,bc:null,c:null,bm:null,m:null,bu:null,u:null,um:null,bum:null,da:null,a:null,rtg:null,rtc:null,ec:null,sp:null};return i.ctx={_:i},i.root=t?t.root:i,i.emit=Zt.bind(null,i),e.ce&&e.ce(i),i}(e,r,o);if(Sn(e)&&(a.ctx.renderer=re),function(e,t=!1){no=t;const{props:n,children:r}=e.vnode,o=to(e);(function(e,t,n,r=!1){const o={},i={};W(i,kr,1),e.propsDefaults=Object.create(null),rr(e,t,o,i);for(const t in e.propsOptions[0])t in o||(o[t]=void 0);n?e.props=r?o:rt(o,!1,Me,Ze,Ke):e.type.props?e.props=o:e.props=i,e.attrs=i})(e,n,o,t),((e,t)=>{if(32&e.vnode.shapeFlag){const n=t._;n?(e.slots=ct(t),W(t,"_",n)):dr(t,e.slots={})}else e.slots={},t&&hr(e,t);W(e.slots,kr,1)})(e,r);const i=o?function(e,t){const n=e.type;e.accessCache=Object.create(null),e.proxy=lt(new Proxy(e.ctx,Wn));const{setup:r}=n;if(r){const n=e.setupContext=r.length>1?function(e){const t=t=>{e.exposed=t||{}};let n;return{get attrs(){return n||(n=function(e){return new Proxy(e.attrs,{get:(t,n)=>(de(e,0,"$attrs"),t[n])})}(e))},slots:e.slots,emit:e.emit,expose:t}}(e):null;Yr(e),pe();const o=mt(r,e,0,[e.props,n]);if(fe(),eo(),C(o)){if(o.then(eo,eo),t)return o.then((n=>{ro(e,n,t)})).catch((t=>{vt(t,e,0)}));e.asyncDep=o}else ro(e,o,t)}else oo(e,t)}(e,t):void 0;no=!1}(a),a.asyncDep){if(o&&o.registerDep(a,U),!e.el){const e=a.subTree=zr(Ar);S(null,e,t,n)}}else U(a,e,t,n,o,i,s)},k=(e,t,n)=>{const r=t.component=e.component;if(function(e,t,n){const{props:r,children:o,component:i}=e,{props:s,children:a,patchFlag:c}=t,l=i.emitsOptions;if(t.dirs||t.transition)return!0;if(!(n&&c>=0))return!(!o&&!a||a&&a.$stable)||r!==s&&(r?!s||on(r,s,l):!!s);if(1024&c)return!0;if(16&c)return r?on(r,s,l):!!s;if(8&c){const e=t.dynamicProps;for(let t=0;t<e.length;t++){const n=e[t];if(s[n]!==r[n]&&!Qt(l,n))return!0}}return!1}(e,t,n)){if(r.asyncDep&&!r.asyncResolved)return void B(r,t,n);r.next=t,function(e){const t=wt.indexOf(e);t>St&&wt.splice(t,1)}(r.update),r.update()}else t.el=e.el,r.vnode=t},U=(e,t,n,r,o,i,s)=>{const a=e.effect=new ae((()=>{if(e.isMounted){let t,{next:n,bu:r,u:a,parent:c,vnode:l}=e,u=n;wr(e,!1),n?(n.el=l.el,B(e,n,s)):n=l,r&&$(r),(t=n.props&&n.props.onVnodeBeforeUpdate)&&Xr(t,c,n,l),wr(e,!0);const f=tn(e),d=e.subTree;e.subTree=f,b(d,f,p(d.el),te(d),e,o,i),n.el=f.el,null===u&&function({vnode:e,parent:t},n){for(;t&&t.subTree===e;)(e=t.vnode).el=n,t=t.parent}(e,f.el),a&&br(a,o),(t=n.props&&n.props.onVnodeUpdated)&&br((()=>Xr(t,c,n,l)),o),__VUE_PROD_DEVTOOLS__&&Wt(e)}else{let s;const{el:a,props:c}=t,{bm:l,m:u,parent:p}=e,f=wn(t);if(wr(e,!1),l&&$(l),!f&&(s=c&&c.onVnodeBeforeMount)&&Xr(s,p,t),wr(e,!0),a&&ie){const n=()=>{e.subTree=tn(e),ie(a,e.subTree,e,o,null)};f?t.type.__asyncLoader().then((()=>!e.isUnmounted&&n())):n()}else{const s=e.subTree=tn(e);b(null,s,n,r,e,o,i),t.el=s.el}if(u&&br(u,o),!f&&(s=c&&c.onVnodeMounted)){const e=t;br((()=>Xr(s,p,e)),o)}(256&t.shapeFlag||p&&wn(p.vnode)&&256&p.vnode.shapeFlag)&&e.a&&br(e.a,o),e.isMounted=!0,__VUE_PROD_DEVTOOLS__&&$t(e),t=n=r=null}}),(()=>Lt(c)),e.scope),c=e.update=()=>a.run();c.id=e.uid,wr(e,!0),c()},B=(e,t,n)=>{t.component=e;const r=e.vnode.props;e.vnode=t,e.next=null,function(e,t,n,r){const{props:o,attrs:i,vnode:{patchFlag:s}}=e,a=ct(o),[c]=e.propsOptions;let l=!1;if(!(r||s>0)||16&s){let r;rr(e,t,o,i)&&(l=!0);for(const i in a)t&&(O(t,i)||(r=z(i))!==i&&O(t,r))||(c?!n||void 0===n[i]&&void 0===n[r]||(o[i]=or(c,a,i,void 0,e,!0)):delete o[i]);if(i!==a)for(const e in i)t&&O(t,e)||(delete i[e],l=!0)}else if(8&s){const n=e.vnode.dynamicProps;for(let r=0;r<n.length;r++){let s=n[r];if(Qt(e.emitsOptions,s))continue;const u=t[s];if(c)if(O(i,s))u!==i[s]&&(i[s]=u,l=!0);else{const t=F(s);o[t]=or(c,a,t,u,e,!1)}else u!==i[s]&&(i[s]=u,l=!0)}}l&&ye(e,"set","$attrs")}(e,t.props,r,n),((e,t,n)=>{const{vnode:r,slots:o}=e;let i=!0,s=d;if(32&r.shapeFlag){const e=t._;e?n&&1===e?i=!1:(_(o,t),n||1!==e||delete o._):(i=!t.$stable,dr(t,o)),s=t}else t&&(hr(e,t),s={default:1});if(i)for(const e in o)ur(e)||e in s||delete o[e]})(e,t.children,n),pe(),Rt(void 0,e.update),fe()},V=(e,t,n,r,o,i,s,a,c=!1)=>{const l=e&&e.children,p=e?e.shapeFlag:0,f=t.children,{patchFlag:d,shapeFlag:h}=t;if(d>0){if(128&d)return void H(l,f,n,r,o,i,s,a,c);if(256&d)return void G(l,f,n,r,o,i,s,a,c)}8&h?(16&p&&ee(l,o,i),f!==l&&u(n,f)):16&p?16&h?H(l,f,n,r,o,i,s,a,c):ee(l,o,i,!0):(8&p&&u(n,""),16&h&&T(f,n,r,o,i,s,a,c))},G=(e,t,n,r,o,i,s,a,c)=>{t=t||h;const l=(e=e||h).length,u=t.length,p=Math.min(l,u);let f;for(f=0;f<p;f++){const r=t[f]=c?Wr(t[f]):$r(t[f]);b(e[f],r,n,null,o,i,s,a,c)}l>u?ee(e,o,i,!0,!1,p):T(t,n,r,o,i,s,a,c,p)},H=(e,t,n,r,o,i,s,a,c)=>{let l=0;const u=t.length;let p=e.length-1,f=u-1;for(;l<=p&&l<=f;){const r=e[l],u=t[l]=c?Wr(t[l]):$r(t[l]);if(!Rr(r,u))break;b(r,u,n,null,o,i,s,a,c),l++}for(;l<=p&&l<=f;){const r=e[p],l=t[f]=c?Wr(t[f]):$r(t[f]);if(!Rr(r,l))break;b(r,l,n,null,o,i,s,a,c),p--,f--}if(l>p){if(l<=f){const e=f+1,p=e<u?t[e].el:r;for(;l<=f;)b(null,t[l]=c?Wr(t[l]):$r(t[l]),n,p,o,i,s,a,c),l++}}else if(l>f)for(;l<=p;)X(e[l],o,i,!0),l++;else{const d=l,y=l,m=new Map;for(l=y;l<=f;l++){const e=t[l]=c?Wr(t[l]):$r(t[l]);null!=e.key&&m.set(e.key,l)}let g,v=0;const _=f-y+1;let w=!1,S=0;const O=new Array(_);for(l=0;l<_;l++)O[l]=0;for(l=d;l<=p;l++){const r=e[l];if(v>=_){X(r,o,i,!0);continue}let u;if(null!=r.key)u=m.get(r.key);else for(g=y;g<=f;g++)if(0===O[g-y]&&Rr(r,t[g])){u=g;break}void 0===u?X(r,o,i,!0):(O[u-y]=l+1,u>=S?S=u:w=!0,b(r,t[u],n,null,o,i,s,a,c),v++)}const E=w?function(e){const t=e.slice(),n=[0];let r,o,i,s,a;const c=e.length;for(r=0;r<c;r++){const c=e[r];if(0!==c){if(o=n[n.length-1],e[o]<c){t[r]=o,n.push(r);continue}for(i=0,s=n.length-1;i<s;)a=i+s>>1,e[n[a]]<c?i=a+1:s=a;c<e[n[i]]&&(i>0&&(t[r]=n[i-1]),n[i]=r)}}for(i=n.length,s=n[i-1];i-- >0;)n[i]=s,s=t[s];return n}(O):h;for(g=E.length-1,l=_-1;l>=0;l--){const e=y+l,p=t[e],f=e+1<u?t[e+1].el:r;0===O[l]?b(null,p,n,f,o,i,s,a,c):w&&(g<0||l!==E[g]?J(p,n,f,2):g--)}}},J=(e,t,n,o,i=null)=>{const{el:s,type:a,transition:c,children:l,shapeFlag:u}=e;if(6&u)J(e.component.subTree,t,n,o);else if(128&u)e.suspense.move(t,n,o);else if(64&u)a.move(e,t,n,re);else if(a!==Or)if(a!==xr)if(2!==o&&1&u&&c)if(0===o)c.beforeEnter(s),r(s,t,n),br((()=>c.enter(s)),i);else{const{leave:e,delayLeave:o,afterLeave:i}=c,a=()=>r(s,t,n),l=()=>{e(s,(()=>{a(),i&&i()}))};o?o(s,a,l):l()}else r(s,t,n);else(({el:e,anchor:t},n,o)=>{let i;for(;e&&e!==t;)i=f(e),r(e,n,o),e=i;r(t,n,o)})(e,t,n);else{r(s,t,n);for(let e=0;e<l.length;e++)J(l[e],t,n,o);r(e.anchor,t,n)}},X=(e,t,n,r=!1,o=!1)=>{const{type:i,props:s,ref:a,children:c,dynamicChildren:l,shapeFlag:u,patchFlag:p,dirs:f}=e;if(null!=a&&vr(a,null,n,e,!0),256&u)return void t.ctx.deactivate(e);const d=1&u&&f,h=!wn(e);let y;if(h&&(y=s&&s.onVnodeBeforeUnmount)&&Xr(y,t,e),6&u)Y(e.component,n,r);else{if(128&u)return void e.suspense.unmount(n,r);d&&Fn(e,null,t,"beforeUnmount"),64&u?e.type.remove(e,t,n,o,re,r):l&&(i!==Or||p>0&&64&p)?ee(l,t,n,!1,!0):(i===Or&&384&p||!o&&16&u)&&ee(c,t,n),r&&q(e)}(h&&(y=s&&s.onVnodeUnmounted)||d)&&br((()=>{y&&Xr(y,t,e),d&&Fn(e,null,t,"unmounted")}),n)},q=e=>{const{type:t,el:n,anchor:r,transition:i}=e;if(t===Or)return void K(n,r);if(t===xr)return void(({el:e,anchor:t})=>{let n;for(;e&&e!==t;)n=f(e),o(e),e=n;o(t)})(e);const s=()=>{o(n),i&&!i.persisted&&i.afterLeave&&i.afterLeave()};if(1&e.shapeFlag&&i&&!i.persisted){const{leave:t,delayLeave:r}=i,o=()=>t(n,s);r?r(e.el,s,o):o()}else s()},K=(e,t)=>{let n;for(;e!==t;)n=f(e),o(e),e=n;o(t)},Y=(e,t,n)=>{const{bum:r,scope:o,update:i,subTree:s,um:a}=e;r&&$(r),o.stop(),i&&(i.active=!1,X(s,e,t,n)),a&&br(a,t),br((()=>{e.isUnmounted=!0}),t),t&&t.pendingBranch&&!t.isUnmounted&&e.asyncDep&&!e.asyncResolved&&e.suspenseId===t.pendingId&&(t.deps--,0===t.deps&&t.resolve()),__VUE_PROD_DEVTOOLS__&&Jt(e)},ee=(e,t,n,r=!1,o=!1,i=0)=>{for(let s=i;s<e.length;s++)X(e[s],t,n,r,o)},te=e=>6&e.shapeFlag?te(e.component.subTree):128&e.shapeFlag?e.suspense.next():f(e.anchor||e.el),ne=(e,t,n)=>{null==e?t._vnode&&X(t._vnode,null,null,!0):b(t._vnode||null,e,t,null,null,null,n),kt(),t._vnode=e},re={p:b,um:X,m:J,r:q,mt:D,mc:T,pc:V,pbc:P,n:te,o:e};let oe,ie;return t&&([oe,ie]=t(re)),{render:ne,hydrate:oe,createApp:gr(ne,oe)}}function wr({effect:e,update:t},n){e.allowRecurse=t.allowRecurse=n}function Sr(e,t,n=!1){const r=e.children,o=t.children;if(E(r)&&E(o))for(let e=0;e<r.length;e++){const t=r[e];let i=o[e];1&i.shapeFlag&&!i.dynamicChildren&&((i.patchFlag<=0||32===i.patchFlag)&&(i=o[e]=Wr(o[e]),i.el=t.el),n||Sr(t,i))}}const Or=Symbol(void 0),Er=Symbol(void 0),Ar=Symbol(void 0),xr=Symbol(void 0),jr=[];let Tr=null;function Mr(e=!1){jr.push(Tr=e?null:[])}let Pr=1;function Cr(e){Pr+=e}function Nr(e){return e.dynamicChildren=Pr>0?Tr||h:null,jr.pop(),Tr=jr[jr.length-1]||null,Pr>0&&Tr&&Tr.push(e),e}function Lr(e,t,n,r,o,i){return Nr(Br(e,t,n,r,o,i,!0))}function Ir(e,t,n,r,o){return Nr(zr(e,t,n,r,o,!0))}function Dr(e){return!!e&&!0===e.__v_isVNode}function Rr(e,t){return e.type===t.type&&e.key===t.key}const kr="__vInternal",Ur=({key:e})=>null!=e?e:null,Fr=({ref:e,ref_key:t,ref_for:n})=>null!=e?T(e)||ft(e)||j(e)?{i:Kt,r:e,k:t,f:!!n}:e:null;function Br(e,t=null,n=null,r=0,o=null,i=(e===Or?0:1),s=!1,a=!1){const c={__v_isVNode:!0,__v_skip:!0,type:e,props:t,key:t&&Ur(t),ref:t&&Fr(t),scopeId:Yt,slotScopeIds:null,children:n,component:null,suspense:null,ssContent:null,ssFallback:null,dirs:null,transition:null,el:null,anchor:null,target:null,targetAnchor:null,staticCount:0,shapeFlag:i,patchFlag:r,dynamicProps:o,dynamicChildren:null,appContext:null};return a?(Jr(c,n),128&i&&e.normalize(c)):n&&(c.shapeFlag|=T(n)?8:16),Pr>0&&!s&&Tr&&(c.patchFlag>0||6&i)&&32!==c.patchFlag&&Tr.push(c),c}const zr=function(e,t=null,n=null,r=0,o=null,i=!1){if(e&&e!==Vn||(e=Ar),Dr(e)){const r=Vr(e,t,!0);return n&&Jr(r,n),Pr>0&&!i&&Tr&&(6&r.shapeFlag?Tr[Tr.indexOf(e)]=r:Tr.push(r)),r.patchFlag|=-2,r}if(a=e,j(a)&&"__vccOpts"in a&&(e=e.__vccOpts),t){t=function(e){return e?at(e)||kr in e?_({},e):e:null}(t);let{class:e,style:n}=t;e&&!T(e)&&(t.class=u(e)),P(n)&&(at(n)&&!E(n)&&(n=_({},n)),t.style=s(n))}var a;return Br(e,t,n,r,o,T(e)?1:(e=>e.__isSuspense)(e)?128:(e=>e.__isTeleport)(e)?64:P(e)?4:j(e)?2:0,i,!0)};function Vr(e,t,n=!1){const{props:r,ref:o,patchFlag:i,children:a}=e,c=t?function(...e){const t={};for(let n=0;n<e.length;n++){const r=e[n];for(const e in r)if("class"===e)t.class!==r.class&&(t.class=u([t.class,r.class]));else if("style"===e)t.style=s([t.style,r.style]);else if(v(e)){const n=t[e],o=r[e];!o||n===o||E(n)&&n.includes(o)||(t[e]=n?[].concat(n,o):o)}else""!==e&&(t[e]=r[e])}return t}(r||{},t):r;return{__v_isVNode:!0,__v_skip:!0,type:e.type,props:c,key:c&&Ur(c),ref:t&&t.ref?n&&o?E(o)?o.concat(Fr(t)):[o,Fr(t)]:Fr(t):o,scopeId:e.scopeId,slotScopeIds:e.slotScopeIds,children:a,target:e.target,targetAnchor:e.targetAnchor,staticCount:e.staticCount,shapeFlag:e.shapeFlag,patchFlag:t&&e.type!==Or?-1===i?16:16|i:i,dynamicProps:e.dynamicProps,dynamicChildren:e.dynamicChildren,appContext:e.appContext,dirs:e.dirs,transition:e.transition,component:e.component,suspense:e.suspense,ssContent:e.ssContent&&Vr(e.ssContent),ssFallback:e.ssFallback&&Vr(e.ssFallback),el:e.el,anchor:e.anchor}}function Gr(e=" ",t=0){return zr(Er,null,e,t)}function Hr(e="",t=!1){return t?(Mr(),Ir(Ar,null,e)):zr(Ar,null,e)}function $r(e){return null==e||"boolean"==typeof e?zr(Ar):E(e)?zr(Or,null,e.slice()):"object"==typeof e?Wr(e):zr(Er,null,String(e))}function Wr(e){return null===e.el||e.memo?e:Vr(e)}function Jr(e,t){let n=0;const{shapeFlag:r}=e;if(null==t)t=null;else if(E(t))n=16;else if("object"==typeof t){if(65&r){const n=t.default;return void(n&&(n._c&&(n._d=!1),Jr(e,n()),n._c&&(n._d=!0)))}{n=32;const r=t._;r||kr in t?3===r&&Kt&&(1===Kt.slots._?t._=1:(t._=2,e.patchFlag|=1024)):t._ctx=Kt}}else j(t)?(t={default:t,_ctx:Kt},n=32):(t=String(t),64&r?(n=16,t=[Gr(t)]):n=8);e.children=t,e.shapeFlag|=n}function Xr(e,t,n,r=null){gt(e,t,7,[n,r])}const Zr=yr();let qr=0;let Qr=null;const Kr=()=>Qr||Kt,Yr=e=>{Qr=e,e.scope.on()},eo=()=>{Qr&&Qr.scope.off(),Qr=null};function to(e){return 4&e.vnode.shapeFlag}let no=!1;function ro(e,t,n){j(t)?e.type.__ssrInlineRender?e.ssrRender=t:e.render=t:P(t)&&(__VUE_PROD_DEVTOOLS__&&(e.devtoolsRawSetupState=t),e.setupState=ht(t)),oo(e,n)}function oo(e,t,n){const r=e.type;e.render||(e.render=r.render||y),__VUE_OPTIONS_API__&&(Yr(e),pe(),function(e){const t=qn(e),n=e.proxy,r=e.ctx;Jn=!1,t.beforeCreate&&Xn(t.beforeCreate,e,"bc");const{data:o,computed:i,methods:s,watch:a,provide:c,inject:l,created:u,beforeMount:p,mounted:f,beforeUpdate:d,updated:h,activated:m,deactivated:g,beforeDestroy:v,beforeUnmount:b,destroyed:_,unmounted:w,render:S,renderTracked:O,renderTriggered:A,errorCaptured:x,serverPrefetch:T,expose:M,inheritAttrs:C,components:N,directives:L,filters:I}=t;if(l&&function(e,t,n=y,r=!1){E(e)&&(e=er(e));for(const n in e){const o=e[n];let i;i=P(o)?"default"in o?sn(o.from||n,o.default,!0):sn(o.from||n):sn(o),ft(i)&&r?Object.defineProperty(t,n,{enumerable:!0,configurable:!0,get:()=>i.value,set:e=>i.value=e}):t[n]=i}}(l,r,null,e.appContext.config.unwrapInjectedRef),s)for(const e in s){const t=s[e];j(t)&&(r[e]=t.bind(n))}if(o){const t=o.call(n,n);P(t)&&(e.data=tt(t))}if(Jn=!0,i)for(const e in i){const t=i[e],o=j(t)?t.bind(n,n):j(t.get)?t.get.bind(n,n):y,s=!j(t)&&j(t.set)?t.set.bind(n):y,a=so({get:o,set:s});Object.defineProperty(r,e,{enumerable:!0,configurable:!0,get:()=>a.value,set:e=>a.value=e})}if(a)for(const e in a)Zn(a[e],r,n,e);if(c){const e=j(c)?c.call(n):c;Reflect.ownKeys(e).forEach((t=>{!function(e,t){if(Qr){let n=Qr.provides;const r=Qr.parent&&Qr.parent.provides;r===n&&(n=Qr.provides=Object.create(r)),n[e]=t}}(t,e[t])}))}function D(e,t){E(t)?t.forEach((t=>e(t.bind(n)))):t&&e(t.bind(n))}if(u&&Xn(u,e,"c"),D(Mn,p),D(Pn,f),D(Cn,d),D(Nn,h),D(On,m),D(En,g),D(Un,x),D(kn,O),D(Rn,A),D(Ln,b),D(In,w),D(Dn,T),E(M))if(M.length){const t=e.exposed||(e.exposed={});M.forEach((e=>{Object.defineProperty(t,e,{get:()=>n[e],set:t=>n[e]=t})}))}else e.exposed||(e.exposed={});S&&e.render===y&&(e.render=S),null!=C&&(e.inheritAttrs=C),N&&(e.components=N),L&&(e.directives=L)}(e),fe(),eo())}function io(e){if(e.exposed)return e.exposeProxy||(e.exposeProxy=new Proxy(ht(lt(e.exposed)),{get:(t,n)=>n in t?t[n]:n in $n?$n[n](e):void 0}))}const so=(e,t)=>function(e,t,n=!1){let r,o;const i=j(e);return i?(r=e,o=y):(r=e.get,o=e.set),new yt(r,o,i||!o,n)}(e,0,no);Symbol("");const ao="3.2.37",co="undefined"!=typeof document?document:null,lo=co&&co.createElement("template"),uo={insert:(e,t,n)=>{t.insertBefore(e,n||null)},remove:e=>{const t=e.parentNode;t&&t.removeChild(e)},createElement:(e,t,n,r)=>{const o=t?co.createElementNS("http://www.w3.org/2000/svg",e):co.createElement(e,n?{is:n}:void 0);return"select"===e&&r&&null!=r.multiple&&o.setAttribute("multiple",r.multiple),o},createText:e=>co.createTextNode(e),createComment:e=>co.createComment(e),setText:(e,t)=>{e.nodeValue=t},setElementText:(e,t)=>{e.textContent=t},parentNode:e=>e.parentNode,nextSibling:e=>e.nextSibling,querySelector:e=>co.querySelector(e),setScopeId(e,t){e.setAttribute(t,"")},cloneNode(e){const t=e.cloneNode(!0);return"_value"in e&&(t._value=e._value),t},insertStaticContent(e,t,n,r,o,i){const s=n?n.previousSibling:t.lastChild;if(o&&(o===i||o.nextSibling))for(;t.insertBefore(o.cloneNode(!0),n),o!==i&&(o=o.nextSibling););else{lo.innerHTML=r?`<svg>${e}</svg>`:e;const o=lo.content;if(r){const e=o.firstChild;for(;e.firstChild;)o.appendChild(e.firstChild);o.removeChild(e)}t.insertBefore(o,n)}return[s?s.nextSibling:t.firstChild,n?n.previousSibling:t.lastChild]}},po=/\s*!important$/;function fo(e,t,n){if(E(n))n.forEach((n=>fo(e,t,n)));else if(null==n&&(n=""),t.startsWith("--"))e.setProperty(t,n);else{const r=function(e,t){const n=yo[t];if(n)return n;let r=F(t);if("filter"!==r&&r in e)return yo[t]=r;r=V(r);for(let n=0;n<ho.length;n++){const o=ho[n]+r;if(o in e)return yo[t]=o}return t}(e,t);po.test(n)?e.setProperty(z(r),n.replace(po,""),"important"):e[r]=n}}const ho=["Webkit","Moz","ms"],yo={},mo="http://www.w3.org/1999/xlink",[go,vo]=(()=>{let e=Date.now,t=!1;if("undefined"!=typeof window){Date.now()>document.createEvent("Event").timeStamp&&(e=performance.now.bind(performance));const n=navigator.userAgent.match(/firefox\/(\d+)/i);t=!!(n&&Number(n[1])<=53)}return[e,t]})();let bo=0;const _o=Promise.resolve(),wo=()=>{bo=0};const So=/(?:Once|Passive|Capture)$/,Oo=/^on[a-z]/;"undefined"!=typeof HTMLElement&&HTMLElement;const Eo="transition",Ao="animation",xo=(e,{slots:t})=>function(e,t,n){const r=arguments.length;return 2===r?P(t)&&!E(t)?Dr(t)?zr(e,null,[t]):zr(e,t):zr(e,null,t):(r>3?n=Array.prototype.slice.call(arguments,2):3===r&&Dr(n)&&(n=[n]),zr(e,t,n))}(hn,function(e){const t={};for(const n in e)n in jo||(t[n]=e[n]);if(!1===e.css)return t;const{name:n="v",type:r,duration:o,enterFromClass:i=`${n}-enter-from`,enterActiveClass:s=`${n}-enter-active`,enterToClass:a=`${n}-enter-to`,appearFromClass:c=i,appearActiveClass:l=s,appearToClass:u=a,leaveFromClass:p=`${n}-leave-from`,leaveActiveClass:f=`${n}-leave-active`,leaveToClass:d=`${n}-leave-to`}=e,h=function(e){if(null==e)return null;if(P(e))return[Po(e.enter),Po(e.leave)];{const t=Po(e);return[t,t]}}(o),y=h&&h[0],m=h&&h[1],{onBeforeEnter:g,onEnter:v,onEnterCancelled:b,onLeave:w,onLeaveCancelled:S,onBeforeAppear:O=g,onAppear:E=v,onAppearCancelled:A=b}=t,x=(e,t,n)=>{No(e,t?u:a),No(e,t?l:s),n&&n()},j=(e,t)=>{e._isLeaving=!1,No(e,p),No(e,d),No(e,f),t&&t()},T=e=>(t,n)=>{const o=e?E:v,s=()=>x(t,e,n);To(o,[t,s]),Lo((()=>{No(t,e?c:i),Co(t,e?u:a),Mo(o)||Do(t,r,y,s)}))};return _(t,{onBeforeEnter(e){To(g,[e]),Co(e,i),Co(e,s)},onBeforeAppear(e){To(O,[e]),Co(e,c),Co(e,l)},onEnter:T(!1),onAppear:T(!0),onLeave(e,t){e._isLeaving=!0;const n=()=>j(e,t);Co(e,p),document.body.offsetHeight,Co(e,f),Lo((()=>{e._isLeaving&&(No(e,p),Co(e,d),Mo(w)||Do(e,r,m,n))})),To(w,[e,n])},onEnterCancelled(e){x(e,!1),To(b,[e])},onAppearCancelled(e){x(e,!0),To(A,[e])},onLeaveCancelled(e){j(e),To(S,[e])}})}(e),t);xo.displayName="Transition";const jo={name:String,type:String,css:{type:Boolean,default:!0},duration:[String,Number,Object],enterFromClass:String,enterActiveClass:String,enterToClass:String,appearFromClass:String,appearActiveClass:String,appearToClass:String,leaveFromClass:String,leaveActiveClass:String,leaveToClass:String},To=(xo.props=_({},hn.props,jo),(e,t=[])=>{E(e)?e.forEach((e=>e(...t))):e&&e(...t)}),Mo=e=>!!e&&(E(e)?e.some((e=>e.length>1)):e.length>1);function Po(e){return J(e)}function Co(e,t){t.split(/\s+/).forEach((t=>t&&e.classList.add(t))),(e._vtc||(e._vtc=new Set)).add(t)}function No(e,t){t.split(/\s+/).forEach((t=>t&&e.classList.remove(t)));const{_vtc:n}=e;n&&(n.delete(t),n.size||(e._vtc=void 0))}function Lo(e){requestAnimationFrame((()=>{requestAnimationFrame(e)}))}let Io=0;function Do(e,t,n,r){const o=e._endId=++Io,i=()=>{o===e._endId&&r()};if(n)return setTimeout(i,n);const{type:s,timeout:a,propCount:c}=function(e,t){const n=window.getComputedStyle(e),r=e=>(n[e]||"").split(", "),o=r("transitionDelay"),i=r("transitionDuration"),s=Ro(o,i),a=r("animationDelay"),c=r("animationDuration"),l=Ro(a,c);let u=null,p=0,f=0;return t===Eo?s>0&&(u=Eo,p=s,f=i.length):t===Ao?l>0&&(u=Ao,p=l,f=c.length):(p=Math.max(s,l),u=p>0?s>l?Eo:Ao:null,f=u?u===Eo?i.length:c.length:0),{type:u,timeout:p,propCount:f,hasTransform:u===Eo&&/\b(transform|all)(,|$)/.test(n.transitionProperty)}}(e,t);if(!s)return r();const l=s+"end";let u=0;const p=()=>{e.removeEventListener(l,f),i()},f=t=>{t.target===e&&++u>=c&&p()};setTimeout((()=>{u<c&&p()}),a+1),e.addEventListener(l,f)}function Ro(e,t){for(;e.length<t.length;)e=e.concat(e);return Math.max(...t.map(((t,n)=>ko(t)+ko(e[n]))))}function ko(e){return 1e3*Number(e.slice(0,-1).replace(",","."))}new WeakMap,new WeakMap;const Uo=_({patchProp:(e,t,n,r,s=!1,a,c,l,u)=>{"class"===t?function(e,t,n){const r=e._vtc;r&&(t=(t?[t,...r]:[...r]).join(" ")),null==t?e.removeAttribute("class"):n?e.setAttribute("class",t):e.className=t}(e,r,s):"style"===t?function(e,t,n){const r=e.style,o=T(n);if(n&&!o){for(const e in n)fo(r,e,n[e]);if(t&&!T(t))for(const e in t)null==n[e]&&fo(r,e,"")}else{const i=r.display;o?t!==n&&(r.cssText=n):t&&e.removeAttribute("style"),"_vod"in e&&(r.display=i)}}(e,n,r):v(t)?b(t)||function(e,t,n,r,o=null){const i=e._vei||(e._vei={}),s=i[t];if(r&&s)s.value=r;else{const[n,a]=function(e){let t;if(So.test(e)){let n;for(t={};n=e.match(So);)e=e.slice(0,e.length-n[0].length),t[n[0].toLowerCase()]=!0}return[z(e.slice(2)),t]}(t);if(r){const s=i[t]=function(e,t){const n=e=>{const r=e.timeStamp||go();(vo||r>=n.attached-1)&>(function(e,t){if(E(t)){const n=e.stopImmediatePropagation;return e.stopImmediatePropagation=()=>{n.call(e),e._stopped=!0},t.map((e=>t=>!t._stopped&&e&&e(t)))}return t}(e,n.value),t,5,[e])};return n.value=e,n.attached=bo||(_o.then(wo),bo=go()),n}(r,o);!function(e,t,n,r){e.addEventListener(t,n,r)}(e,n,s,a)}else s&&(function(e,t,n,r){e.removeEventListener(t,n,r)}(e,n,s,a),i[t]=void 0)}}(e,t,0,r,c):("."===t[0]?(t=t.slice(1),1):"^"===t[0]?(t=t.slice(1),0):function(e,t,n,r){return r?"innerHTML"===t||"textContent"===t||!!(t in e&&Oo.test(t)&&j(n)):"spellcheck"!==t&&"draggable"!==t&&"translate"!==t&&("form"!==t&&(("list"!==t||"INPUT"!==e.tagName)&&(("type"!==t||"TEXTAREA"!==e.tagName)&&((!Oo.test(t)||!T(n))&&t in e))))}(e,t,r,s))?function(e,t,n,r,o,s,a){if("innerHTML"===t||"textContent"===t)return r&&a(r,o,s),void(e[t]=null==n?"":n);if("value"===t&&"PROGRESS"!==e.tagName&&!e.tagName.includes("-")){e._value=n;const r=null==n?"":n;return e.value===r&&"OPTION"!==e.tagName||(e.value=r),void(null==n&&e.removeAttribute(t))}let c=!1;if(""===n||null==n){const r=typeof e[t];"boolean"===r?n=i(n):null==n&&"string"===r?(n="",c=!0):"number"===r&&(n=0,c=!0)}try{e[t]=n}catch(e){}c&&e.removeAttribute(t)}(e,t,r,a,c,l,u):("true-value"===t?e._trueValue=r:"false-value"===t&&(e._falseValue=r),function(e,t,n,r,s){if(r&&t.startsWith("xlink:"))null==n?e.removeAttributeNS(mo,t.slice(6,t.length)):e.setAttributeNS(mo,t,n);else{const r=o(t);null==n||r&&!i(n)?e.removeAttribute(t):e.setAttribute(t,r?"":n)}}(e,t,r,s))}},uo);let Fo;const Bo=(...e)=>{const t=(Fo||(Fo=_r(Uo))).createApp(...e),{mount:n}=t;return t.mount=e=>{const r=function(e){if(T(e))return document.querySelector(e);return e}(e);if(!r)return;const o=t._component;j(o)||o.render||o.template||(o.template=r.innerHTML),r.innerHTML="";const i=n(r,!1,r instanceof SVGElement);return r instanceof Element&&(r.removeAttribute("v-cloak"),r.setAttribute("data-v-app","")),i},t};var zo={class:"h5p-audio-recorder-view"},Vo=["innerHTML"],Go=["innerHTML"],Ho={key:1,class:"h5p-audio-recorder-player"},$o={controls:"controls"},Wo=Gr(" Your browser does not support the "),Jo=Br("code",null,"audio",-1),Xo=Gr(" element. "),Zo=["src"],qo={key:3,class:"h5p-audio-recorder-download"},Qo={class:"button-row"},Ko={class:"button-row-double"},Yo=Br("span",{class:"fa-circle"},null,-1),ei=Br("span",{class:"fa-undo"},null,-1),ti={class:"label"},ni=Br("span",{class:"fa-pause"},null,-1),ri={class:"label"},oi=Br("span",{class:"fa-circle"},null,-1),ii={class:"label"},si=Br("span",{class:"fa-play-circle"},null,-1),ai={class:"label"},ci={class:"button-row-left"},li=["href","download"],ui=Br("span",{class:"icon-download"},null,-1),pi={class:"button-row-right"},fi=Br("span",{class:"fa-undo"},null,-1),di={class:"label"};const hi="unsupported",yi="blocked",mi="ready",gi="recording",vi="paused",bi="done",_i="insecure-not-allowed",wi="cant-create-audio-file";var Si={};Si[mi]="button-record",Si[gi]="button-pause",Si[vi]="button-continue",Si[bi]="button-download";const Oi={methods:{resize:function(){this.$el&&(this.viewState=this.$el.offsetWidth<=576?"small":"large")},record:function(){this.$emit(gi)},pause:function(){this.state=vi,this.$emit(this.state)},done:function(){this.state=bi,this.$emit(bi)},retry:function(){var e=this.$el;this.isSubcontent&&(e=function e(t){return t?-1!==t.className.indexOf("h5p-content")?t:e(t.parentNode):null}(this.$el)||this.$el);var t=new H5P.ConfirmationDialog({headerText:this.l10n.retryDialogHeaderText,dialogText:this.l10n.retryDialogBodyText,cancelText:this.l10n.retryDialogCancelText,confirmText:this.l10n.retryDialogConfirmText});t.appendTo(e),t.show();var n=this;t.on("confirmed",(function(){n.state=mi,n.$refs.timer&&n.$refs.timer.reset(),n.$emit("retry")}))}},computed:{unEscape:function(){return this.statusMessages[this.state].replace(/'/g,"'")}},watch:{state:function(e){var t=this;Si[e]&&this.$nextTick((function(){return t.$refs[Si[e]].focus()})),this.$emit("resize")}}};n(338);var Ei=n(959);const Ai=(0,Ei.Z)(Oi,[["render",function(e,t,n,r,o,i){var s=zn("vuMeter"),a=zn("timer");return Mr(),Lr("div",zo,[zr(s,{avgMicFrequency:e.avgMicFrequency,enablePulse:"recording"===e.state},null,8,["avgMicFrequency","enablePulse"]),"done"!==e.state&&e.title?(Mr(),Lr("div",{key:0,class:"title",innerHTML:e.title},null,8,Vo)):Hr("",!0),Br("div",{role:"status",class:u(e.state),innerHTML:e.statusMessages[e.state]},null,10,Go),"done"===e.state&&""!==e.audioSrc?(Mr(),Lr("div",Ho,[Br("audio",$o,[Wo,Jo,Xo,Br("source",{src:e.audioSrc},null,8,Zo)])])):Hr("",!0),"unsupported"!==e.state&&"done"!==e.state&&"insecure-not-allowed"!==e.state?(Mr(),Ir(a,{key:2,ref:"timer",stopped:"recording"!==e.state},null,8,["stopped"])):Hr("",!0),"blocked"!==e.state&&"unsupported"!==e.state&&"done"===e.state?(Mr(),Lr("div",qo,p(e.l10n.downloadRecording),1)):Hr("",!0),Br("div",Qo,[Br("div",Ko,["ready"===e.state||"blocked"===e.state?(Mr(),Lr("button",{key:0,class:"button record",ref:"button-record",onClick:t[0]||(t[0]=function(){return i.record&&i.record.apply(i,arguments)})},[Yo,Gr(" "+p(e.l10n.recordAnswer),1)],512)):Hr("",!0),"recording"===e.state||"paused"===e.state?(Mr(),Lr("button",{key:1,class:u(["button retry small",{"small-screen":"small"===this.viewState}]),onClick:t[1]||(t[1]=function(){return i.retry&&i.retry.apply(i,arguments)})},[ei,Br("span",ti,p(e.l10n.retry),1)],2)):Hr("",!0),"recording"===e.state?(Mr(),Lr("button",{key:2,class:u(["button pause",{"small-screen":"small"===this.viewState}]),ref:"button-pause",onClick:t[2]||(t[2]=function(){return i.pause&&i.pause.apply(i,arguments)})},[ni,Br("span",ri,p(e.l10n.pause),1)],2)):Hr("",!0),"paused"===e.state?(Mr(),Lr("button",{key:3,class:u(["button record",{"small-screen":"small"===this.viewState}]),ref:"button-continue",onClick:t[3]||(t[3]=function(){return i.record&&i.record.apply(i,arguments)})},[oi,Br("span",ii,p(e.l10n.continue),1)],2)):Hr("",!0),"recording"===e.state||"paused"===e.state?(Mr(),Lr("button",{key:4,class:u(["button done small",{"small-screen":"small"===this.viewState}]),onClick:t[4]||(t[4]=function(){return i.done&&i.done.apply(i,arguments)})},[si,Br("span",ai,p(e.l10n.done),1)],2)):Hr("",!0)]),Br("span",ci,["done"===e.state?(Mr(),Lr("a",{key:0,class:"button download",ref:"button-download",href:e.audioSrc,download:e.audioFilename},[ui,Gr(" "+p(e.l10n.download),1)],8,li)):Hr("",!0)]),Br("span",pi,["done"===e.state||"cant-create-audio-file"===e.state?(Mr(),Lr("button",{key:0,class:"button retry",onClick:t[5]||(t[5]=function(){return i.retry&&i.retry.apply(i,arguments)})},[fi,Br("span",di,p(e.l10n.retry),1)])):Hr("",!0)])])])}]]);var xi={class:"recording-indicator-wrapper"},ji=Br("div",{class:"fa-microphone"},null,-1);const Ti={props:["avgMicFrequency","enablePulse"],computed:{pulseScale:function(){return function(e){e>30?e=30:e<4&&(e=4);var t=(e-4)/30*(1.3-.7)+.7;return.005*Math.round(t/.005)}(this.avgMicFrequency)}}};n(656);const Mi=(0,Ei.Z)(Ti,[["render",function(e,t,n,r,o,i){return Mr(),Lr("div",xi,[Br("div",{style:s({transform:"scale(".concat(i.pulseScale,")")}),class:u([[{hidden:!n.enablePulse}],"h5p-audio-recorder-vu-meter"])},null,6),ji])}]]);var Pi={role:"timer",class:"audio-recorder-timer"},Ci=n(277);const Ni={props:["stopped"],mounted:function(){var e=this;setInterval((function(){e.secondsPassed=e.timer.time()/1e3}),200)},data:function(){return{timer:new Ci,secondsPassed:0}},methods:{reset:function(){this.secondsPassed=0,this.timer=new Ci}},computed:{formatTime:function(){var e=function(e){return e<10?"0".concat(e):"".concat(e)},t=parseInt(this.secondsPassed,10),n=e(Math.floor(t/60)),r=e(t-60*n);return"".concat(n,":").concat(r)}},watch:{stopped:function(e){this.timer[e?"stop":"start"]()}}};n(680);const Li=(0,Ei.Z)(Ni,[["render",function(e,t,n,r,o,i){return Mr(),Lr("div",Pi,p(i.formatTime),1)}]]);function Ii(e){return Ii="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},Ii(e)}function Di(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}function Ri(e,t){return Ri=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(e,t){return e.__proto__=t,e},Ri(e,t)}function ki(e,t){if(t&&("object"===Ii(t)||"function"==typeof t))return t;if(void 0!==t)throw new TypeError("Derived constructors may only return object or undefined");return function(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}(e)}function Ui(e){return Ui=Object.setPrototypeOf?Object.getPrototypeOf.bind():function(e){return e.__proto__||Object.getPrototypeOf(e)},Ui(e)}var Fi="inactive",Bi="recording",zi=function(e){!function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),Object.defineProperty(e,"prototype",{writable:!1}),t&&Ri(e,t)}(s,H5P.EventDispatcher);var t,n,r,o,i=(r=s,o=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}(),function(){var e,t=Ui(r);if(o){var n=Ui(this).constructor;e=Reflect.construct(t,arguments,n)}else e=t.apply(this,arguments);return ki(this,e)});function s(){var e;!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,s),(e=i.call(this)).config={bufferLength:4096,numChannels:1},e.state=Fi;var t=new Blob(["/**\n * This is a web worker responsible for recording/buffering the sound and\n * encoding it as wav.\n */\n\nvar recLength = 0;\nvar recBuffers = [];\nvar sampleRate;\nvar numChannels;\n\n// Listen to incoming messages\nthis.onmessage = function(e) {\n switch(e.data.command){\n case 'init':\n init(e.data.config);\n break;\n case 'record':\n record(e.data.buffer);\n break;\n case 'export-wav':\n exportWAV();\n break;\n case 'clear':\n clear();\n break;\n }\n};\n\n/**\n * Initialization\n *\n * @param {Object} config\n */\nfunction init(config) {\n sampleRate = config.sampleRate;\n numChannels = config.numChannels;\n initBuffers();\n}\n\n/**\n * Storing the data buffer\n *\n * @param {Float32Array} inputBuffer\n */\nfunction record(inputBuffer) {\n for (var channel = 0; channel < numChannels; channel++){\n recBuffers[channel].push(inputBuffer[channel]);\n }\n recLength += inputBuffer[0].length;\n}\n\n/**\n * Export buffered data as a wav encoded blob\n */\nfunction exportWAV() {\n var buffers = [];\n for (var channel = 0; channel < numChannels; channel++){\n buffers.push(mergeBuffers(recBuffers[channel], recLength));\n }\n if (numChannels === 2){\n var interleaved = interleave(buffers[0], buffers[1]);\n } else {\n var interleaved = buffers[0];\n }\n var dataview = encodeWAV(interleaved);\n var audioBlob = new Blob([dataview], { type: 'audio/wav' });\n\n this.postMessage({\n command: 'wav-delivered',\n blob: audioBlob\n });\n}\n\n/**\n * Clear the buffers\n */\nfunction clear() {\n recLength = 0;\n recBuffers = [];\n initBuffers();\n}\n\n/**\n * Initialize the buffers\n */\nfunction initBuffers() {\n for (var channel = 0; channel < numChannels; channel++){\n recBuffers[channel] = [];\n }\n}\n\n/**\n * Merge buffers\n *\n * @param {Array} recBuffers\n * @param {[type]} recLength\n * @return {Float32Array}\n */\nfunction mergeBuffers(recBuffers, recLength){\n var result = new Float32Array(recLength);\n var offset = 0;\n for (var i = 0; i < recBuffers.length; i++){\n result.set(recBuffers[i], offset);\n offset += recBuffers[i].length;\n }\n return result;\n}\n\n/**\n * Interleave two channels\n *\n * @param {Array} inputL\n * @param {Array} inputR\n * @return {Float32Array}\n */\nfunction interleave(inputL, inputR){\n var length = inputL.length + inputR.length;\n var result = new Float32Array(length);\n\n var index = 0,\n inputIndex = 0;\n\n while (index < length){\n result[index++] = inputL[inputIndex];\n result[index++] = inputR[inputIndex];\n inputIndex++;\n }\n return result;\n}\n\n/**\n * Convert floats to 16 bit PCMs\n *\n * @param {DataView} output\n * @param {number} offset\n * @param {Array} input\n */\nfunction floatTo16BitPCM(output, offset, input) {\n for (var i = 0; i < input.length; i++, offset+=2){\n var s = Math.max(-1, Math.min(1, input[i]));\n output.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7FFF, true);\n }\n}\n\n/**\n * Write string to wav header\n *\n * @param {DataView} view\n * @param {number} offset\n * @param {string} string\n */\nfunction writeString(view, offset, string) {\n for (var i = 0; i < string.length; i++){\n view.setUint8(offset + i, string.charCodeAt(i));\n }\n}\n\n/**\n * Encode as wav\n *\n * @param {Array} samples\n * @return {DataView}\n */\nfunction encodeWAV(samples) {\n var buffer = new ArrayBuffer(44 + samples.length * 2);\n var view = new DataView(buffer);\n\n /* RIFF identifier */\n writeString(view, 0, 'RIFF');\n /* RIFF chunk length */\n view.setUint32(4, 36 + samples.length * 2, true);\n /* RIFF type */\n writeString(view, 8, 'WAVE');\n /* format chunk identifier */\n writeString(view, 12, 'fmt ');\n /* format chunk length */\n view.setUint32(16, 16, true);\n /* sample format (raw) */\n view.setUint16(20, 1, true);\n /* channel count */\n view.setUint16(22, numChannels, true);\n /* sample rate */\n view.setUint32(24, sampleRate, true);\n /* byte rate (sample rate * block align) */\n view.setUint32(28, sampleRate * 2 * numChannels, true);\n /* block align (channel count * bytes per sample) */\n view.setUint16(32, numChannels * 2, true);\n /* bits per sample */\n view.setUint16(34, 16, true);\n /* data chunk identifier */\n writeString(view, 36, 'data');\n /* data chunk length */\n view.setUint32(40, samples.length * 2, true);\n\n floatTo16BitPCM(view, 44, samples);\n\n return view;\n}\n"],{type:"text/javascript"});return e.worker=new Worker(URL.createObjectURL(t)),e.worker.onmessage=function(t){e.trigger(t.data.command,t.data.blob)},e.worker.onerror=function(t){e.trigger("worker-error",t)},e}return t=s,n=[{key:"getWavURL",value:function(){var e=this;this.stop();var t=new Promise((function(t,n){e.once("wav-delivered",(function(e){t(URL.createObjectURL(e.data))})),e.once("worker-error",(function(e){n(e)}))}));return this.worker.postMessage({command:"export-wav"}),t}},{key:"_setupAudioProcessing",value:function(e){var t=this;this.stream=e;var n=window.AudioContext||window.webkitAudioContext;this.audioContext=new n,this.scriptProcessorNode=this.audioContext.createScriptProcessor(this.config.bufferLength,this.config.numChannels,this.config.numChannels),this.scriptProcessorNode.onaudioprocess=function(e){t.state===Bi&&t.worker.postMessage({command:"record",buffer:[e.inputBuffer.getChannelData(0)]})};var r=this.audioContext.createAnalyser();r.minDecibels=-90,r.maxDecibels=-10,r.smoothingTimeConstant=.85,r.fftSize=256,this.freqBufferLength=r.frequencyBinCount,this.freqDataArray=new Uint8Array(this.freqBufferLength),this.freqAnalyser=r,this.sourceNode=this.audioContext.createMediaStreamSource(e),this.sourceNode.connect(this.freqAnalyser),this.freqAnalyser.connect(this.scriptProcessorNode),this.scriptProcessorNode.connect(this.audioContext.destination)}},{key:"getAverageMicFrequency",value:function(){return this.freqAnalyser.getByteFrequencyData(this.freqDataArray),this.freqDataArray.reduce((function(e,t){return e+t}),0)/this.freqBufferLength}},{key:"grabMic",value:function(){var e=this;return this.supported()?(void 0===this.userMedia&&(this.userMedia=navigator.mediaDevices.getUserMedia({audio:!0}).then((function(t){e._setupAudioProcessing(t),e.worker.postMessage({command:"init",config:{sampleRate:e.sourceNode.context.sampleRate,numChannels:e.config.numChannels}})})).catch((function(t){var n="blocked";return t.name&&-1!==["NotSupportedError","NotSupportedError","NotAllowedError"].indexOf(t.name)&&(n="insecure-not-allowed"),delete e.userMedia,Promise.reject(n)}))),this.userMedia):Promise.reject()}},{key:"start",value:function(){var e=this;this.grabMic().then((function(){e._setState(Bi)})).catch((function(t){e.trigger(t)}))}},{key:"stop",value:function(){this._setState(Fi)}},{key:"supported",value:function(){return(void 0!==window.AudioContext||void 0!==window.webkitAudioContext)&&navigator.mediaDevices&&navigator.mediaDevices.getUserMedia}},{key:"releaseMic",value:function(){this._setState(Fi),this.worker.postMessage({command:"clear"}),this.stream.getAudioTracks().forEach((function(e){return e.stop()})),this.sourceNode.disconnect(),this.scriptProcessorNode.disconnect(),"closed"!==this.audioContext.state&&this.audioContext.close(),delete this.userMedia}},{key:"_setState",value:function(e){this.state=e,this.trigger(this.state)}}],n&&Di(t.prototype,n),Object.defineProperty(t,"prototype",{writable:!1}),s}();function Vi(e){return Vi="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},Vi(e)}function Gi(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}const Hi=function(){function e(){!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e)}var t,n;return t=e,n=[{key:"extend",value:function(){for(var e=1;e<arguments.length;e++)for(var t in arguments[e])arguments[e].hasOwnProperty(t)&&("object"===Vi(arguments[0][t])&&"object"===Vi(arguments[e][t])?this.extend(arguments[0][t],arguments[e][t]):arguments[0][t]=arguments[e][t]);return arguments[0]}}],null&&Gi(t.prototype,null),n&&Gi(t,n),Object.defineProperty(t,"prototype",{writable:!1}),e}();function $i(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function Wi(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?$i(Object(n),!0).forEach((function(t){Ji(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):$i(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function Ji(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function Xi(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}function Zi(e,t,n){return t&&Xi(e.prototype,t),n&&Xi(e,n),Object.defineProperty(e,"prototype",{writable:!1}),e}var qi=Zi((function e(t,n){var r=this;(function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")})(this,e),H5P.EventDispatcher.call(this),t=Hi.extend({l10n:{recordAnswer:"Record",pause:"Pause",continue:"Continue",download:"Download",done:"Done",retry:"Retry",microphoneNotSupported:"Microphone not supported. Make sure you are using a browser that allows microphone recording.",microphoneInaccessible:"Microphone is not accessible. Make sure that the browser microphone is enabled.",insecureNotAllowed:"Access to microphone is not allowed in your browser since this page is not served using HTTPS. Please contact the author, and ask him to make this available using HTTPS",statusReadyToRecord:"Press a button below to record your answer.",statusRecording:"Recording...",statusPaused:"Recording paused. Press a button to continue recording.",statusFinishedRecording:"You have successfully recorded your answer! Listen to the recording below.",downloadRecording:"Download this recording or retry.",retryDialogHeaderText:"Retry recording?",retryDialogBodyText:'By pressing "Retry" you will lose your current recording.',retryDialogConfirmText:"Retry",retryDialogCancelText:"Cancel",statusCantCreateTheAudioFile:"Can't create the audio file."}},t);var o=document.createElement("div");o.classList.add("h5p-audio-recorder");var i,s=this.recorder=new zi,a={};a[hi]=t.l10n.microphoneNotSupported,a[yi]=t.l10n.microphoneInaccessible,a[mi]=t.l10n.statusReadyToRecord,a[gi]=t.l10n.statusRecording,a[vi]=t.l10n.statusPaused,a[bi]=t.l10n.statusFinishedRecording,a[_i]=t.l10n.insecureNotAllowed,a[wi]=t.l10n.statusCantCreateTheAudioFile;var c=this;Ai.data=function(){return{title:t.title,state:s.supported()?mi:hi,statusMessages:a,l10n:t.l10n,audioSrc:"",audioFilename:"",avgMicFrequency:0,isSubcontent:!c.hasOwnProperty("isRoot")||!c.isRoot()}};var l=Bo(Wi(Wi({},Ai),{},{components:{timer:Li,vuMeter:Mi}}),{onRecording:function(){s.start()},onDone:function(){s.stop(),s.getWavURL().then((function(e){if(s.releaseMic(),i.$data.audioSrc=e,t.title&&t.title.length>0){var n=t.title.substr(0,20);i.$data.audioFilename=n.toLowerCase().replace(/ /g,"-")+".wav"}c.trigger("resize")})).catch((function(e){i.$data.state=wi,console.error(t.l10n.statusCantCreateTheAudioFile,e)}))},onRetry:function(){s.releaseMic(),i.$data.audioSrc=""},onPaused:function(){s.stop()},onResize:function(){c.trigger("resize")}});this.on("resize",(function(){i.resize()})),s.on("recording",(function(){i.$data.state=gi,r.updateMicFrequency()})),s.on("blocked",(function(){i.$data.state=yi})),s.on("insecure-not-allowed",(function(){i.$data.state=_i})),this.updateMicFrequency=function(){var e=this;i.$data.state===gi?(i.$data.avgMicFrequency=s.getAverageMicFrequency(),setTimeout((function(){e.animateVUMeter=window.requestAnimationFrame((function(){e.updateMicFrequency()}))}),10)):window.cancelAnimationFrame(this.animateVUMeter)},this.attach=function(e){e.get(0).appendChild(o),i=l.mount(o)}}))},737:(e,t,n)=>{"use strict";var r=n(750),o=n(573),i=o(r("String.prototype.indexOf"));e.exports=function(e,t){var n=r(e,!!t);return"function"==typeof n&&i(e,".prototype.")>-1?o(n):n}},573:(e,t,n)=>{"use strict";var r=n(132),o=n(750),i=o("%Function.prototype.apply%"),s=o("%Function.prototype.call%"),a=o("%Reflect.apply%",!0)||r.call(s,i),c=o("%Object.getOwnPropertyDescriptor%",!0),l=o("%Object.defineProperty%",!0),u=o("%Math.max%");if(l)try{l({},"a",{value:1})}catch(e){l=null}e.exports=function(e){var t=a(r,s,arguments);if(c&&l){var n=c(t,"length");n.configurable&&l(t,"length",{value:1+u(0,e.length-(arguments.length-1))})}return t};var p=function(){return a(r,i,arguments)};l?l(e.exports,"apply",{value:p}):e.exports.apply=p},833:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>a});var r=n(933),o=n.n(r),i=n(476),s=n.n(i)()(o());s.push([e.id,'.h5p-content:not(.using-mouse) .h5p-audio-recorder-view .button:focus{outline:0;box-shadow:.06em 0 .6em .1em #7bc1f9}.h5p-audio-recorder-view{font-size:1em;padding:.9em;text-align:center;font-family:Arial,"Open Sans",sans-serif}.h5p-audio-recorder-view [class^=fa-]{font-family:"H5PFontAwesome4"}.h5p-audio-recorder-view .fa-microphone{width:60%;height:60%;left:50%;top:50%;transform:translate(-50%, -50%);position:absolute;font-size:2.5em;border-radius:50%;background-color:#fff;line-height:2.5em}.h5p-audio-recorder-view .h5p-audio-recorder-player{box-sizing:border-box;margin:1.25em 1em 0 1em}.h5p-audio-recorder-view .h5p-audio-recorder-player audio{width:100%}.h5p-audio-recorder-view .title{color:#000;font-size:1.25em;margin-bottom:1em;line-height:1.5em}.h5p-audio-recorder-view .icon-download:before{font-family:"H5PFontIcons";content:"ī¤"}.h5p-audio-recorder-view [role=status]{background-color:#f8f8f8;color:#777;padding:.6em}.h5p-audio-recorder-view [role=status].recording{background-color:#f9e5e6;color:#da5254}.h5p-audio-recorder-view [role=status].done{background-color:#e0f9e3;color:#20603d}.h5p-audio-recorder-view [role=status].blocked,.h5p-audio-recorder-view [role=status].unsupported,.h5p-audio-recorder-view [role=status].insecure-not-allowed,.h5p-audio-recorder-view [role=status].cant-create-audio-file{background-color:#db8b8b;color:#000}.h5p-audio-recorder-view .h5p-audio-recorder-download{font-size:1.2em;padding:2em}.h5p-audio-recorder-view .h5p-confirmation-dialog-popup{top:5em;width:35em;max-width:100%;min-width:0}.h5p-audio-recorder-view .button-row{margin-bottom:1em}.h5p-audio-recorder-view .button-row .button-row-double{width:100%}.h5p-audio-recorder-view .button-row .button-row-left{text-align:right;flex:1}.h5p-audio-recorder-view .button-row .button-row-right{text-align:left;flex:1}.h5p-audio-recorder-view .button{font-size:1.042em;font-family:"Open Sans",sans-serif;padding:.708em 1.25em;border-radius:2em;margin:0 .5em;border:0;display:inline-block;cursor:pointer;text-decoration:none;font-weight:600;white-space:nowrap}.h5p-audio-recorder-view .button [class^=fa-]{font-weight:400}.h5p-audio-recorder-view .button.small{font-size:.85em}.h5p-audio-recorder-view .button.done{background-color:#fff;color:#1f824c;border:2px solid #1f824c;box-sizing:border-box}.h5p-audio-recorder-view .button.done:hover{color:#29ab64;border-color:#29ab64}.h5p-audio-recorder-view .button.done:active{color:#155934;border-color:#155934}.h5p-audio-recorder-view .button.done[disabled],.h5p-audio-recorder-view .button.done[aria-disabled]{color:#8ae3b2;border-color:#8ae3b2}.h5p-audio-recorder-view .button.retry{background-color:#5e5e5e;color:#fff;border-color:#5e5e5e;border:2px solid #5e5e5e;box-sizing:border-box}.h5p-audio-recorder-view .button.retry:hover{background-color:#515151;border-color:#515151}.h5p-audio-recorder-view .button.retry:active{background-color:#454545;border-color:#454545}.h5p-audio-recorder-view .button.retry[disabled]{background-color:#c4c4c4;border-color:#c4c4c4}.h5p-audio-recorder-view .button.record{background-color:#d95354;color:#fff;border-color:#d95354;border:2px solid #d95354;box-sizing:border-box}.h5p-audio-recorder-view .button.record:hover{background-color:#d43e3f;border-color:#d43e3f}.h5p-audio-recorder-view .button.record:active{background-color:#cc2d2e;border-color:#cc2d2e}.h5p-audio-recorder-view .button.record[disabled]{background-color:#fefafa;border-color:#fefafa}.h5p-audio-recorder-view .button.download{background-color:#1f824c;color:#fff;border-color:#1f824c;border:2px solid #1f824c;box-sizing:border-box}.h5p-audio-recorder-view .button.download:hover{background-color:#1a6d40;border-color:#1a6d40}.h5p-audio-recorder-view .button.download:active{background-color:#155934;border-color:#155934}.h5p-audio-recorder-view .button.download[disabled]{background-color:#8ae3b2;border-color:#8ae3b2}.h5p-audio-recorder-view .button.pause{background-color:#fff;color:#d95354;border:2px solid #d95354;box-sizing:border-box}.h5p-audio-recorder-view .button.pause:hover{color:#e27d7e;border-color:#e27d7e}.h5p-audio-recorder-view .button.pause:active{color:#cc2d2e;border-color:#cc2d2e}.h5p-audio-recorder-view .button.pause[disabled],.h5p-audio-recorder-view .button.pause[aria-disabled]{color:#fefafa;border-color:#fefafa}.h5p-audio-recorder-view .button.small-screen .label{display:none}.h5p-audio-recorder-view .button:not(.small-screen) [class^=fa-]{margin-right:.4em}.h5p-audio-recorder-view .button:not(.small-screen).record,.h5p-audio-recorder-view .button:not(.small-screen).pause{min-width:8.2em}',""]);const a=s},903:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>a});var r=n(933),o=n.n(r),i=n(476),s=n.n(i)()(o());s.push([e.id,'.audio-recorder-timer{font-family:"Open Sans",sans-serif;font-size:2.5em;font-weight:600;color:#8f8f8f;margin:1em 0}',""]);const a=s},867:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>f});var r=n(933),o=n.n(r),i=n(476),s=n.n(i),a=n(678),c=n.n(a),l=new URL(n(945),n.b),u=s()(o()),p=c()(l);u.push([e.id,".recording-indicator-wrapper{height:9.375em;width:9.375em;margin-left:auto;margin-right:auto;line-height:9.375em;color:#8e8e8e;position:relative;margin-bottom:1em}.h5p-audio-recorder-vu-meter{height:100%;width:100%;background-image:url("+p+");position:absolute;transform:scale(0.8)}.h5p-audio-recorder-vu-meter.hidden{display:none}",""]);const f=u},476:e=>{"use strict";e.exports=function(e){var t=[];return t.toString=function(){return this.map((function(t){var n="",r=void 0!==t[5];return t[4]&&(n+="@supports (".concat(t[4],") {")),t[2]&&(n+="@media ".concat(t[2]," {")),r&&(n+="@layer".concat(t[5].length>0?" ".concat(t[5]):""," {")),n+=e(t),r&&(n+="}"),t[2]&&(n+="}"),t[4]&&(n+="}"),n})).join("")},t.i=function(e,n,r,o,i){"string"==typeof e&&(e=[[null,e,void 0]]);var s={};if(r)for(var a=0;a<this.length;a++){var c=this[a][0];null!=c&&(s[c]=!0)}for(var l=0;l<e.length;l++){var u=[].concat(e[l]);r&&s[u[0]]||(void 0!==i&&(void 0===u[5]||(u[1]="@layer".concat(u[5].length>0?" ".concat(u[5]):""," {").concat(u[1],"}")),u[5]=i),n&&(u[2]?(u[1]="@media ".concat(u[2]," {").concat(u[1],"}"),u[2]=n):u[2]=n),o&&(u[4]?(u[1]="@supports (".concat(u[4],") {").concat(u[1],"}"),u[4]=o):u[4]="".concat(o)),t.push(u))}},t}},678:e=>{"use strict";e.exports=function(e,t){return t||(t={}),e?(e=String(e.__esModule?e.default:e),/^['"].*['"]$/.test(e)&&(e=e.slice(1,-1)),t.hash&&(e+=t.hash),/["'() \t\n]|(%20)/.test(e)||t.needQuotes?'"'.concat(e.replace(/"/g,'\\"').replace(/\n/g,"\\n"),'"'):e):e}},933:e=>{"use strict";e.exports=function(e){return e[1]}},343:e=>{"use strict";var t,n="object"==typeof Reflect?Reflect:null,r=n&&"function"==typeof n.apply?n.apply:function(e,t,n){return Function.prototype.apply.call(e,t,n)};t=n&&"function"==typeof n.ownKeys?n.ownKeys:Object.getOwnPropertySymbols?function(e){return Object.getOwnPropertyNames(e).concat(Object.getOwnPropertySymbols(e))}:function(e){return Object.getOwnPropertyNames(e)};var o=Number.isNaN||function(e){return e!=e};function i(){i.init.call(this)}e.exports=i,e.exports.once=function(e,t){return new Promise((function(n,r){function o(n){e.removeListener(t,i),r(n)}function i(){"function"==typeof e.removeListener&&e.removeListener("error",o),n([].slice.call(arguments))}y(e,t,i,{once:!0}),"error"!==t&&function(e,t,n){"function"==typeof e.on&&y(e,"error",t,{once:!0})}(e,o)}))},i.EventEmitter=i,i.prototype._events=void 0,i.prototype._eventsCount=0,i.prototype._maxListeners=void 0;var s=10;function a(e){if("function"!=typeof e)throw new TypeError('The "listener" argument must be of type Function. Received type '+typeof e)}function c(e){return void 0===e._maxListeners?i.defaultMaxListeners:e._maxListeners}function l(e,t,n,r){var o,i,s,l;if(a(n),void 0===(i=e._events)?(i=e._events=Object.create(null),e._eventsCount=0):(void 0!==i.newListener&&(e.emit("newListener",t,n.listener?n.listener:n),i=e._events),s=i[t]),void 0===s)s=i[t]=n,++e._eventsCount;else if("function"==typeof s?s=i[t]=r?[n,s]:[s,n]:r?s.unshift(n):s.push(n),(o=c(e))>0&&s.length>o&&!s.warned){s.warned=!0;var u=new Error("Possible EventEmitter memory leak detected. "+s.length+" "+String(t)+" listeners added. Use emitter.setMaxListeners() to increase limit");u.name="MaxListenersExceededWarning",u.emitter=e,u.type=t,u.count=s.length,l=u,console&&console.warn&&console.warn(l)}return e}function u(){if(!this.fired)return this.target.removeListener(this.type,this.wrapFn),this.fired=!0,0===arguments.length?this.listener.call(this.target):this.listener.apply(this.target,arguments)}function p(e,t,n){var r={fired:!1,wrapFn:void 0,target:e,type:t,listener:n},o=u.bind(r);return o.listener=n,r.wrapFn=o,o}function f(e,t,n){var r=e._events;if(void 0===r)return[];var o=r[t];return void 0===o?[]:"function"==typeof o?n?[o.listener||o]:[o]:n?function(e){for(var t=new Array(e.length),n=0;n<t.length;++n)t[n]=e[n].listener||e[n];return t}(o):h(o,o.length)}function d(e){var t=this._events;if(void 0!==t){var n=t[e];if("function"==typeof n)return 1;if(void 0!==n)return n.length}return 0}function h(e,t){for(var n=new Array(t),r=0;r<t;++r)n[r]=e[r];return n}function y(e,t,n,r){if("function"==typeof e.on)r.once?e.once(t,n):e.on(t,n);else{if("function"!=typeof e.addEventListener)throw new TypeError('The "emitter" argument must be of type EventEmitter. Received type '+typeof e);e.addEventListener(t,(function o(i){r.once&&e.removeEventListener(t,o),n(i)}))}}Object.defineProperty(i,"defaultMaxListeners",{enumerable:!0,get:function(){return s},set:function(e){if("number"!=typeof e||e<0||o(e))throw new RangeError('The value of "defaultMaxListeners" is out of range. It must be a non-negative number. Received '+e+".");s=e}}),i.init=function(){void 0!==this._events&&this._events!==Object.getPrototypeOf(this)._events||(this._events=Object.create(null),this._eventsCount=0),this._maxListeners=this._maxListeners||void 0},i.prototype.setMaxListeners=function(e){if("number"!=typeof e||e<0||o(e))throw new RangeError('The value of "n" is out of range. It must be a non-negative number. Received '+e+".");return this._maxListeners=e,this},i.prototype.getMaxListeners=function(){return c(this)},i.prototype.emit=function(e){for(var t=[],n=1;n<arguments.length;n++)t.push(arguments[n]);var o="error"===e,i=this._events;if(void 0!==i)o=o&&void 0===i.error;else if(!o)return!1;if(o){var s;if(t.length>0&&(s=t[0]),s instanceof Error)throw s;var a=new Error("Unhandled error."+(s?" ("+s.message+")":""));throw a.context=s,a}var c=i[e];if(void 0===c)return!1;if("function"==typeof c)r(c,this,t);else{var l=c.length,u=h(c,l);for(n=0;n<l;++n)r(u[n],this,t)}return!0},i.prototype.addListener=function(e,t){return l(this,e,t,!1)},i.prototype.on=i.prototype.addListener,i.prototype.prependListener=function(e,t){return l(this,e,t,!0)},i.prototype.once=function(e,t){return a(t),this.on(e,p(this,e,t)),this},i.prototype.prependOnceListener=function(e,t){return a(t),this.prependListener(e,p(this,e,t)),this},i.prototype.removeListener=function(e,t){var n,r,o,i,s;if(a(t),void 0===(r=this._events))return this;if(void 0===(n=r[e]))return this;if(n===t||n.listener===t)0==--this._eventsCount?this._events=Object.create(null):(delete r[e],r.removeListener&&this.emit("removeListener",e,n.listener||t));else if("function"!=typeof n){for(o=-1,i=n.length-1;i>=0;i--)if(n[i]===t||n[i].listener===t){s=n[i].listener,o=i;break}if(o<0)return this;0===o?n.shift():function(e,t){for(;t+1<e.length;t++)e[t]=e[t+1];e.pop()}(n,o),1===n.length&&(r[e]=n[0]),void 0!==r.removeListener&&this.emit("removeListener",e,s||t)}return this},i.prototype.off=i.prototype.removeListener,i.prototype.removeAllListeners=function(e){var t,n,r;if(void 0===(n=this._events))return this;if(void 0===n.removeListener)return 0===arguments.length?(this._events=Object.create(null),this._eventsCount=0):void 0!==n[e]&&(0==--this._eventsCount?this._events=Object.create(null):delete n[e]),this;if(0===arguments.length){var o,i=Object.keys(n);for(r=0;r<i.length;++r)"removeListener"!==(o=i[r])&&this.removeAllListeners(o);return this.removeAllListeners("removeListener"),this._events=Object.create(null),this._eventsCount=0,this}if("function"==typeof(t=n[e]))this.removeListener(e,t);else if(void 0!==t)for(r=t.length-1;r>=0;r--)this.removeListener(e,t[r]);return this},i.prototype.listeners=function(e){return f(this,e,!0)},i.prototype.rawListeners=function(e){return f(this,e,!1)},i.listenerCount=function(e,t){return"function"==typeof e.listenerCount?e.listenerCount(t):d.call(e,t)},i.prototype.listenerCount=d,i.prototype.eventNames=function(){return this._eventsCount>0?t(this._events):[]}},278:(e,t,n)=>{"use strict";var r=n(922),o=Object.prototype.toString,i=Object.prototype.hasOwnProperty,s=function(e,t,n){for(var r=0,o=e.length;r<o;r++)i.call(e,r)&&(null==n?t(e[r],r,e):t.call(n,e[r],r,e))},a=function(e,t,n){for(var r=0,o=e.length;r<o;r++)null==n?t(e.charAt(r),r,e):t.call(n,e.charAt(r),r,e)},c=function(e,t,n){for(var r in e)i.call(e,r)&&(null==n?t(e[r],r,e):t.call(n,e[r],r,e))};e.exports=function(e,t,n){if(!r(t))throw new TypeError("iterator must be a function");var i;arguments.length>=3&&(i=n),"[object Array]"===o.call(e)?s(e,t,i):"string"==typeof e?a(e,t,i):c(e,t,i)}},458:e=>{"use strict";var t="Function.prototype.bind called on incompatible ",n=Array.prototype.slice,r=Object.prototype.toString,o="[object Function]";e.exports=function(e){var i=this;if("function"!=typeof i||r.call(i)!==o)throw new TypeError(t+i);for(var s,a=n.call(arguments,1),c=function(){if(this instanceof s){var t=i.apply(this,a.concat(n.call(arguments)));return Object(t)===t?t:this}return i.apply(e,a.concat(n.call(arguments)))},l=Math.max(0,i.length-a.length),u=[],p=0;p<l;p++)u.push("$"+p);if(s=Function("binder","return function ("+u.join(",")+"){ return binder.apply(this,arguments); }")(c),i.prototype){var f=function(){};f.prototype=i.prototype,s.prototype=new f,f.prototype=null}return s}},132:(e,t,n)=>{"use strict";var r=n(458);e.exports=Function.prototype.bind||r},750:(e,t,n)=>{"use strict";var r,o=SyntaxError,i=Function,s=TypeError,a=function(e){try{return i('"use strict"; return ('+e+").constructor;")()}catch(e){}},c=Object.getOwnPropertyDescriptor;if(c)try{c({},"")}catch(e){c=null}var l=function(){throw new s},u=c?function(){try{return l}catch(e){try{return c(arguments,"callee").get}catch(e){return l}}}():l,p=n(679)(),f=Object.getPrototypeOf||function(e){return e.__proto__},d={},h="undefined"==typeof Uint8Array?r:f(Uint8Array),y={"%AggregateError%":"undefined"==typeof AggregateError?r:AggregateError,"%Array%":Array,"%ArrayBuffer%":"undefined"==typeof ArrayBuffer?r:ArrayBuffer,"%ArrayIteratorPrototype%":p?f([][Symbol.iterator]()):r,"%AsyncFromSyncIteratorPrototype%":r,"%AsyncFunction%":d,"%AsyncGenerator%":d,"%AsyncGeneratorFunction%":d,"%AsyncIteratorPrototype%":d,"%Atomics%":"undefined"==typeof Atomics?r:Atomics,"%BigInt%":"undefined"==typeof BigInt?r:BigInt,"%Boolean%":Boolean,"%DataView%":"undefined"==typeof DataView?r:DataView,"%Date%":Date,"%decodeURI%":decodeURI,"%decodeURIComponent%":decodeURIComponent,"%encodeURI%":encodeURI,"%encodeURIComponent%":encodeURIComponent,"%Error%":Error,"%eval%":eval,"%EvalError%":EvalError,"%Float32Array%":"undefined"==typeof Float32Array?r:Float32Array,"%Float64Array%":"undefined"==typeof Float64Array?r:Float64Array,"%FinalizationRegistry%":"undefined"==typeof FinalizationRegistry?r:FinalizationRegistry,"%Function%":i,"%GeneratorFunction%":d,"%Int8Array%":"undefined"==typeof Int8Array?r:Int8Array,"%Int16Array%":"undefined"==typeof Int16Array?r:Int16Array,"%Int32Array%":"undefined"==typeof Int32Array?r:Int32Array,"%isFinite%":isFinite,"%isNaN%":isNaN,"%IteratorPrototype%":p?f(f([][Symbol.iterator]())):r,"%JSON%":"object"==typeof JSON?JSON:r,"%Map%":"undefined"==typeof Map?r:Map,"%MapIteratorPrototype%":"undefined"!=typeof Map&&p?f((new Map)[Symbol.iterator]()):r,"%Math%":Math,"%Number%":Number,"%Object%":Object,"%parseFloat%":parseFloat,"%parseInt%":parseInt,"%Promise%":"undefined"==typeof Promise?r:Promise,"%Proxy%":"undefined"==typeof Proxy?r:Proxy,"%RangeError%":RangeError,"%ReferenceError%":ReferenceError,"%Reflect%":"undefined"==typeof Reflect?r:Reflect,"%RegExp%":RegExp,"%Set%":"undefined"==typeof Set?r:Set,"%SetIteratorPrototype%":"undefined"!=typeof Set&&p?f((new Set)[Symbol.iterator]()):r,"%SharedArrayBuffer%":"undefined"==typeof SharedArrayBuffer?r:SharedArrayBuffer,"%String%":String,"%StringIteratorPrototype%":p?f(""[Symbol.iterator]()):r,"%Symbol%":p?Symbol:r,"%SyntaxError%":o,"%ThrowTypeError%":u,"%TypedArray%":h,"%TypeError%":s,"%Uint8Array%":"undefined"==typeof Uint8Array?r:Uint8Array,"%Uint8ClampedArray%":"undefined"==typeof Uint8ClampedArray?r:Uint8ClampedArray,"%Uint16Array%":"undefined"==typeof Uint16Array?r:Uint16Array,"%Uint32Array%":"undefined"==typeof Uint32Array?r:Uint32Array,"%URIError%":URIError,"%WeakMap%":"undefined"==typeof WeakMap?r:WeakMap,"%WeakRef%":"undefined"==typeof WeakRef?r:WeakRef,"%WeakSet%":"undefined"==typeof WeakSet?r:WeakSet},m=function e(t){var n;if("%AsyncFunction%"===t)n=a("async function () {}");else if("%GeneratorFunction%"===t)n=a("function* () {}");else if("%AsyncGeneratorFunction%"===t)n=a("async function* () {}");else if("%AsyncGenerator%"===t){var r=e("%AsyncGeneratorFunction%");r&&(n=r.prototype)}else if("%AsyncIteratorPrototype%"===t){var o=e("%AsyncGenerator%");o&&(n=f(o.prototype))}return y[t]=n,n},g={"%ArrayBufferPrototype%":["ArrayBuffer","prototype"],"%ArrayPrototype%":["Array","prototype"],"%ArrayProto_entries%":["Array","prototype","entries"],"%ArrayProto_forEach%":["Array","prototype","forEach"],"%ArrayProto_keys%":["Array","prototype","keys"],"%ArrayProto_values%":["Array","prototype","values"],"%AsyncFunctionPrototype%":["AsyncFunction","prototype"],"%AsyncGenerator%":["AsyncGeneratorFunction","prototype"],"%AsyncGeneratorPrototype%":["AsyncGeneratorFunction","prototype","prototype"],"%BooleanPrototype%":["Boolean","prototype"],"%DataViewPrototype%":["DataView","prototype"],"%DatePrototype%":["Date","prototype"],"%ErrorPrototype%":["Error","prototype"],"%EvalErrorPrototype%":["EvalError","prototype"],"%Float32ArrayPrototype%":["Float32Array","prototype"],"%Float64ArrayPrototype%":["Float64Array","prototype"],"%FunctionPrototype%":["Function","prototype"],"%Generator%":["GeneratorFunction","prototype"],"%GeneratorPrototype%":["GeneratorFunction","prototype","prototype"],"%Int8ArrayPrototype%":["Int8Array","prototype"],"%Int16ArrayPrototype%":["Int16Array","prototype"],"%Int32ArrayPrototype%":["Int32Array","prototype"],"%JSONParse%":["JSON","parse"],"%JSONStringify%":["JSON","stringify"],"%MapPrototype%":["Map","prototype"],"%NumberPrototype%":["Number","prototype"],"%ObjectPrototype%":["Object","prototype"],"%ObjProto_toString%":["Object","prototype","toString"],"%ObjProto_valueOf%":["Object","prototype","valueOf"],"%PromisePrototype%":["Promise","prototype"],"%PromiseProto_then%":["Promise","prototype","then"],"%Promise_all%":["Promise","all"],"%Promise_reject%":["Promise","reject"],"%Promise_resolve%":["Promise","resolve"],"%RangeErrorPrototype%":["RangeError","prototype"],"%ReferenceErrorPrototype%":["ReferenceError","prototype"],"%RegExpPrototype%":["RegExp","prototype"],"%SetPrototype%":["Set","prototype"],"%SharedArrayBufferPrototype%":["SharedArrayBuffer","prototype"],"%StringPrototype%":["String","prototype"],"%SymbolPrototype%":["Symbol","prototype"],"%SyntaxErrorPrototype%":["SyntaxError","prototype"],"%TypedArrayPrototype%":["TypedArray","prototype"],"%TypeErrorPrototype%":["TypeError","prototype"],"%Uint8ArrayPrototype%":["Uint8Array","prototype"],"%Uint8ClampedArrayPrototype%":["Uint8ClampedArray","prototype"],"%Uint16ArrayPrototype%":["Uint16Array","prototype"],"%Uint32ArrayPrototype%":["Uint32Array","prototype"],"%URIErrorPrototype%":["URIError","prototype"],"%WeakMapPrototype%":["WeakMap","prototype"],"%WeakSetPrototype%":["WeakSet","prototype"]},v=n(132),b=n(492),_=v.call(Function.call,Array.prototype.concat),w=v.call(Function.apply,Array.prototype.splice),S=v.call(Function.call,String.prototype.replace),O=v.call(Function.call,String.prototype.slice),E=v.call(Function.call,RegExp.prototype.exec),A=/[^%.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|%$))/g,x=/\\(\\)?/g,j=function(e){var t=O(e,0,1),n=O(e,-1);if("%"===t&&"%"!==n)throw new o("invalid intrinsic syntax, expected closing `%`");if("%"===n&&"%"!==t)throw new o("invalid intrinsic syntax, expected opening `%`");var r=[];return S(e,A,(function(e,t,n,o){r[r.length]=n?S(o,x,"$1"):t||e})),r},T=function(e,t){var n,r=e;if(b(g,r)&&(r="%"+(n=g[r])[0]+"%"),b(y,r)){var i=y[r];if(i===d&&(i=m(r)),void 0===i&&!t)throw new s("intrinsic "+e+" exists, but is not available. Please file an issue!");return{alias:n,name:r,value:i}}throw new o("intrinsic "+e+" does not exist!")};e.exports=function(e,t){if("string"!=typeof e||0===e.length)throw new s("intrinsic name must be a non-empty string");if(arguments.length>1&&"boolean"!=typeof t)throw new s('"allowMissing" argument must be a boolean');if(null===E(/^%?[^%]*%?$/g,e))throw new o("`%` may not be present anywhere but at the beginning and end of the intrinsic name");var n=j(e),r=n.length>0?n[0]:"",i=T("%"+r+"%",t),a=i.name,l=i.value,u=!1,p=i.alias;p&&(r=p[0],w(n,_([0,1],p)));for(var f=1,d=!0;f<n.length;f+=1){var h=n[f],m=O(h,0,1),g=O(h,-1);if(('"'===m||"'"===m||"`"===m||'"'===g||"'"===g||"`"===g)&&m!==g)throw new o("property names with quotes must have matching quotes");if("constructor"!==h&&d||(u=!0),b(y,a="%"+(r+="."+h)+"%"))l=y[a];else if(null!=l){if(!(h in l)){if(!t)throw new s("base intrinsic for "+e+" exists, but the property is not available.");return}if(c&&f+1>=n.length){var v=c(l,h);l=(d=!!v)&&"get"in v&&!("originalValue"in v.get)?v.get:l[h]}else d=b(l,h),l=l[h];d&&!u&&(y[a]=l)}}return l}},679:(e,t,n)=>{"use strict";var r="undefined"!=typeof Symbol&&Symbol,o=n(186);e.exports=function(){return"function"==typeof r&&"function"==typeof Symbol&&"symbol"==typeof r("foo")&&"symbol"==typeof Symbol("bar")&&o()}},186:e=>{"use strict";e.exports=function(){if("function"!=typeof Symbol||"function"!=typeof Object.getOwnPropertySymbols)return!1;if("symbol"==typeof Symbol.iterator)return!0;var e={},t=Symbol("test"),n=Object(t);if("string"==typeof t)return!1;if("[object Symbol]"!==Object.prototype.toString.call(t))return!1;if("[object Symbol]"!==Object.prototype.toString.call(n))return!1;for(t in e[t]=42,e)return!1;if("function"==typeof Object.keys&&0!==Object.keys(e).length)return!1;if("function"==typeof Object.getOwnPropertyNames&&0!==Object.getOwnPropertyNames(e).length)return!1;var r=Object.getOwnPropertySymbols(e);if(1!==r.length||r[0]!==t)return!1;if(!Object.prototype.propertyIsEnumerable.call(e,t))return!1;if("function"==typeof Object.getOwnPropertyDescriptor){var o=Object.getOwnPropertyDescriptor(e,t);if(42!==o.value||!0!==o.enumerable)return!1}return!0}},698:(e,t,n)=>{"use strict";var r=n(186);e.exports=function(){return r()&&!!Symbol.toStringTag}},492:(e,t,n)=>{"use strict";var r=n(132);e.exports=r.call(Function.call,Object.prototype.hasOwnProperty)},87:e=>{"function"==typeof Object.create?e.exports=function(e,t){t&&(e.super_=t,e.prototype=Object.create(t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}))}:e.exports=function(e,t){if(t){e.super_=t;var n=function(){};n.prototype=t.prototype,e.prototype=new n,e.prototype.constructor=e}}},740:(e,t,n)=>{"use strict";var r=n(698)(),o=n(737)("Object.prototype.toString"),i=function(e){return!(r&&e&&"object"==typeof e&&Symbol.toStringTag in e)&&"[object Arguments]"===o(e)},s=function(e){return!!i(e)||null!==e&&"object"==typeof e&&"number"==typeof e.length&&e.length>=0&&"[object Array]"!==o(e)&&"[object Function]"===o(e.callee)},a=function(){return i(arguments)}();i.isLegacyArguments=s,e.exports=a?i:s},922:e=>{"use strict";var t,n,r=Function.prototype.toString,o="object"==typeof Reflect&&null!==Reflect&&Reflect.apply;if("function"==typeof o&&"function"==typeof Object.defineProperty)try{t=Object.defineProperty({},"length",{get:function(){throw n}}),n={},o((function(){throw 42}),null,t)}catch(e){e!==n&&(o=null)}else o=null;var i=/^\s*class\b/,s=function(e){try{var t=r.call(e);return i.test(t)}catch(e){return!1}},a=Object.prototype.toString,c="function"==typeof Symbol&&!!Symbol.toStringTag,l="object"==typeof document&&void 0===document.all&&void 0!==document.all?document.all:{};e.exports=o?function(e){if(e===l)return!0;if(!e)return!1;if("function"!=typeof e&&"object"!=typeof e)return!1;if("function"==typeof e&&!e.prototype)return!0;try{o(e,null,t)}catch(e){if(e!==n)return!1}return!s(e)}:function(e){if(e===l)return!0;if(!e)return!1;if("function"!=typeof e&&"object"!=typeof e)return!1;if("function"==typeof e&&!e.prototype)return!0;if(c)return function(e){try{return!s(e)&&(r.call(e),!0)}catch(e){return!1}}(e);if(s(e))return!1;var t=a.call(e);return"[object Function]"===t||"[object GeneratorFunction]"===t}},265:(e,t,n)=>{"use strict";var r,o=Object.prototype.toString,i=Function.prototype.toString,s=/^\s*(?:function)?\*/,a=n(698)(),c=Object.getPrototypeOf;e.exports=function(e){if("function"!=typeof e)return!1;if(s.test(i.call(e)))return!0;if(!a)return"[object GeneratorFunction]"===o.call(e);if(!c)return!1;if(void 0===r){var t=function(){if(!a)return!1;try{return Function("return function*() {}")()}catch(e){}}();r=!!t&&c(t)}return c(e)===r}},387:(e,t,n)=>{"use strict";var r=n(278),o=n(973),i=n(737),s=i("Object.prototype.toString"),a=n(698)(),c="undefined"==typeof globalThis?n.g:globalThis,l=o(),u=i("Array.prototype.indexOf",!0)||function(e,t){for(var n=0;n<e.length;n+=1)if(e[n]===t)return n;return-1},p=i("String.prototype.slice"),f={},d=n(828),h=Object.getPrototypeOf;a&&d&&h&&r(l,(function(e){var t=new c[e];if(Symbol.toStringTag in t){var n=h(t),r=d(n,Symbol.toStringTag);if(!r){var o=h(n);r=d(o,Symbol.toStringTag)}f[e]=r.get}})),e.exports=function(e){if(!e||"object"!=typeof e)return!1;if(!a||!(Symbol.toStringTag in e)){var t=p(s(e),8,-1);return u(l,t)>-1}return!!d&&function(e){var t=!1;return r(f,(function(n,r){if(!t)try{t=n.call(e)===r}catch(e){}})),t}(e)}},277:(e,t,n)=>{var r=n(343).EventEmitter,o=n(323).inherits;e.exports=s;var i={};function s(e){r.call(this),this._total=0,this._start=null,this._startCount=0,e&&this.start()}function a(){return(new Date).getTime()}o(s,r),s.get=function(e){return i[e]||(i[e]=new s),i[e]},s.destroy=function(e){return!!i[e]&&delete i[e]},s.prototype.time=function(){return this._total+this.timeFromStart()},s.prototype.emitTime=function(){var e=this.time();return this.emit("time",e),e},s.prototype.timeFromStart=function(){return this.isStarted()?a()-this._start:0},s.prototype.isStarted=function(){return!this.isStopped()},s.prototype.isStopped=function(){return null===this._start},s.prototype.start=function(){return++this._startCount,!!this.isStopped()&&(this._start=a(),this.emit("start"),!0)},s.prototype.stop=function(){return!!this.isStarted()&&(this._total+=this.timeFromStart(),this._start=null,this._stopCount=0,this.emit("stop"),!0)},s.prototype.stopParallel=function(){return!(!this.isStarted()||(--this._startCount,0!==this._startCount))&&this.stop()},s.prototype.toggle=function(){return this.start()||this.stop()},s.prototype.toString=function(){return this.time()+"ms"},s.prototype.valueOf=function(){return this.time()}},579:e=>{e.exports=function(e){return e&&"object"==typeof e&&"function"==typeof e.copy&&"function"==typeof e.fill&&"function"==typeof e.readUInt8}},673:(e,t,n)=>{"use strict";var r=n(740),o=n(265),i=n(505),s=n(387);function a(e){return e.call.bind(e)}var c="undefined"!=typeof BigInt,l="undefined"!=typeof Symbol,u=a(Object.prototype.toString),p=a(Number.prototype.valueOf),f=a(String.prototype.valueOf),d=a(Boolean.prototype.valueOf);if(c)var h=a(BigInt.prototype.valueOf);if(l)var y=a(Symbol.prototype.valueOf);function m(e,t){if("object"!=typeof e)return!1;try{return t(e),!0}catch(e){return!1}}function g(e){return"[object Map]"===u(e)}function v(e){return"[object Set]"===u(e)}function b(e){return"[object WeakMap]"===u(e)}function _(e){return"[object WeakSet]"===u(e)}function w(e){return"[object ArrayBuffer]"===u(e)}function S(e){return"undefined"!=typeof ArrayBuffer&&(w.working?w(e):e instanceof ArrayBuffer)}function O(e){return"[object DataView]"===u(e)}function E(e){return"undefined"!=typeof DataView&&(O.working?O(e):e instanceof DataView)}t.isArgumentsObject=r,t.isGeneratorFunction=o,t.isTypedArray=s,t.isPromise=function(e){return"undefined"!=typeof Promise&&e instanceof Promise||null!==e&&"object"==typeof e&&"function"==typeof e.then&&"function"==typeof e.catch},t.isArrayBufferView=function(e){return"undefined"!=typeof ArrayBuffer&&ArrayBuffer.isView?ArrayBuffer.isView(e):s(e)||E(e)},t.isUint8Array=function(e){return"Uint8Array"===i(e)},t.isUint8ClampedArray=function(e){return"Uint8ClampedArray"===i(e)},t.isUint16Array=function(e){return"Uint16Array"===i(e)},t.isUint32Array=function(e){return"Uint32Array"===i(e)},t.isInt8Array=function(e){return"Int8Array"===i(e)},t.isInt16Array=function(e){return"Int16Array"===i(e)},t.isInt32Array=function(e){return"Int32Array"===i(e)},t.isFloat32Array=function(e){return"Float32Array"===i(e)},t.isFloat64Array=function(e){return"Float64Array"===i(e)},t.isBigInt64Array=function(e){return"BigInt64Array"===i(e)},t.isBigUint64Array=function(e){return"BigUint64Array"===i(e)},g.working="undefined"!=typeof Map&&g(new Map),t.isMap=function(e){return"undefined"!=typeof Map&&(g.working?g(e):e instanceof Map)},v.working="undefined"!=typeof Set&&v(new Set),t.isSet=function(e){return"undefined"!=typeof Set&&(v.working?v(e):e instanceof Set)},b.working="undefined"!=typeof WeakMap&&b(new WeakMap),t.isWeakMap=function(e){return"undefined"!=typeof WeakMap&&(b.working?b(e):e instanceof WeakMap)},_.working="undefined"!=typeof WeakSet&&_(new WeakSet),t.isWeakSet=function(e){return _(e)},w.working="undefined"!=typeof ArrayBuffer&&w(new ArrayBuffer),t.isArrayBuffer=S,O.working="undefined"!=typeof ArrayBuffer&&"undefined"!=typeof DataView&&O(new DataView(new ArrayBuffer(1),0,1)),t.isDataView=E;var A="undefined"!=typeof SharedArrayBuffer?SharedArrayBuffer:void 0;function x(e){return"[object SharedArrayBuffer]"===u(e)}function j(e){return void 0!==A&&(void 0===x.working&&(x.working=x(new A)),x.working?x(e):e instanceof A)}function T(e){return m(e,p)}function M(e){return m(e,f)}function P(e){return m(e,d)}function C(e){return c&&m(e,h)}function N(e){return l&&m(e,y)}t.isSharedArrayBuffer=j,t.isAsyncFunction=function(e){return"[object AsyncFunction]"===u(e)},t.isMapIterator=function(e){return"[object Map Iterator]"===u(e)},t.isSetIterator=function(e){return"[object Set Iterator]"===u(e)},t.isGeneratorObject=function(e){return"[object Generator]"===u(e)},t.isWebAssemblyCompiledModule=function(e){return"[object WebAssembly.Module]"===u(e)},t.isNumberObject=T,t.isStringObject=M,t.isBooleanObject=P,t.isBigIntObject=C,t.isSymbolObject=N,t.isBoxedPrimitive=function(e){return T(e)||M(e)||P(e)||C(e)||N(e)},t.isAnyArrayBuffer=function(e){return"undefined"!=typeof Uint8Array&&(S(e)||j(e))},["isProxy","isExternal","isModuleNamespaceObject"].forEach((function(e){Object.defineProperty(t,e,{enumerable:!1,value:function(){throw new Error(e+" is not supported in userland")}})}))},323:(e,t,n)=>{var r=Object.getOwnPropertyDescriptors||function(e){for(var t=Object.keys(e),n={},r=0;r<t.length;r++)n[t[r]]=Object.getOwnPropertyDescriptor(e,t[r]);return n},o=/%[sdj%]/g;t.format=function(e){if(!v(e)){for(var t=[],n=0;n<arguments.length;n++)t.push(c(arguments[n]));return t.join(" ")}n=1;for(var r=arguments,i=r.length,s=String(e).replace(o,(function(e){if("%%"===e)return"%";if(n>=i)return e;switch(e){case"%s":return String(r[n++]);case"%d":return Number(r[n++]);case"%j":try{return JSON.stringify(r[n++])}catch(e){return"[Circular]"}default:return e}})),a=r[n];n<i;a=r[++n])m(a)||!w(a)?s+=" "+a:s+=" "+c(a);return s},t.deprecate=function(e,n){if("undefined"!=typeof process&&!0===process.noDeprecation)return e;if("undefined"==typeof process)return function(){return t.deprecate(e,n).apply(this,arguments)};var r=!1;return function(){if(!r){if(process.throwDeprecation)throw new Error(n);process.traceDeprecation?console.trace(n):console.error(n),r=!0}return e.apply(this,arguments)}};var i={},s=/^$/;if({GJS_DEBUG_TOPICS:"JS ERROR;JS LOG",LESSOPEN:"| /usr/bin/lesspipe %s",J2SDKDIR:"/usr/lib/jvm/java-8-oracle",USER:"paal",LC_TIME:"nb_NO.UTF-8",npm_config_user_agent:"npm/8.15.0 node/v16.17.0 linux x64 workspaces/false",TEXTDOMAIN:"im-config",XDG_SEAT:"seat0",SSH_AGENT_PID:"4774",XDG_SESSION_TYPE:"x11",npm_node_execpath:"/home/paal/.nvm/versions/node/v16.17.0/bin/node",J2REDIR:"/usr/lib/jvm/java-8-oracle/jre",SHLVL:"2",npm_config_noproxy:"",OLDPWD:"/home/paal/h5p-libs/joubel",QT4_IM_MODULE:"xim",HOME:"/home/paal",H5P_IGNORE_PATTERN:"^\\.|~$|LICENSE|\\.md|node_modules|gulpfile.js|package-lock.json|.*language\\/source.*$",DESKTOP_SESSION:"ubuntu",NVM_BIN:"/home/paal/.nvm/versions/node/v16.17.0/bin",NVM_NODEJS_ORG_MIRROR:"https://nodejs.org/dist",npm_package_json:"/home/paal/projects/h5p/libs/joubel/h5p-audio-recorder/package.json",GNOME_SHELL_SESSION_MODE:"ubuntu",GTK_MODULES:"gail:atk-bridge",LC_MONETARY:"nb_NO.UTF-8",npm_config_userconfig:"/home/paal/.npmrc",npm_config_local_prefix:"/home/paal/projects/h5p/libs/joubel/h5p-audio-recorder",DBUS_SESSION_BUS_ADDRESS:"unix:path=/run/user/1000/bus",npm_config_engine_strict:"true",COLORTERM:"truecolor",COLOR:"1",NVM_DIR:"/home/paal/.nvm",H5P_IGNORE_REPOS:"h5p-personality-quiz,h5p-editor-mini-course,h5p-editor-conditional,h5p-fullscreen-scroller",npm_config_metrics_registry:"https://registry.npmjs.org/",MANDATORY_PATH:"/usr/share/gconf/ubuntu.mandatory.path",QT_QPA_PLATFORMTHEME:"appmenu-qt5",IM_CONFIG_PHASE:"2",LOGNAME:"paal",GTK_IM_MODULE:"ibus",_:"/home/paal/.nvm/versions/node/v16.17.0/bin/npm",npm_config_prefix:"/home/paal/.nvm/versions/node/v16.17.0",DEFAULTS_PATH:"/usr/share/gconf/ubuntu.default.path",USERNAME:"paal",XDG_SESSION_ID:"5",TERM:"xterm-256color",npm_config_cache:"/home/paal/.npm",GNOME_DESKTOP_SESSION_ID:"this-is-deprecated",GTK2_MODULES:"overlay-scrollbar",WINDOWPATH:"2",npm_config_node_gyp:"/home/paal/.nvm/versions/node/v16.17.0/lib/node_modules/npm/node_modules/node-gyp/bin/node-gyp.js",PATH:"/home/paal/projects/h5p/libs/joubel/h5p-audio-recorder/node_modules/.bin:/home/paal/projects/h5p/libs/joubel/node_modules/.bin:/home/paal/projects/h5p/libs/node_modules/.bin:/home/paal/projects/h5p/node_modules/.bin:/home/paal/projects/node_modules/.bin:/home/paal/node_modules/.bin:/home/node_modules/.bin:/node_modules/.bin:/home/paal/.nvm/versions/node/v16.17.0/lib/node_modules/npm/node_modules/@npmcli/run-script/lib/node-gyp-bin:/home/paal/.nvm/versions/node/v16.17.0/bin:/home/paal/.linuxbrew/bin:/home/paal/.composer/vendor/bin:~/.local/bin:/usr/local/go/bin:/home/paal/projects/h5pcom/h5pcom-tools:/home/paal/bin:/usr/bin:/home/paal/.config/composer/vendor/bin:/home/paal/.symfony/bin:/home/paal/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/usr/lib/jvm/java-8-oracle/bin:/usr/lib/jvm/java-8-oracle/db/bin:/usr/lib/jvm/java-8-oracle/jre/bin:/home/paal/.gem/ruby/2.5.0/bin:/var/lib/gems/2.5.0/bin:/usr/lib/x86_64-linux-gnu/rubygems-integration/2.5.0/bin:/usr/share/rubygems-integration/2.5.0/bin:/usr/share/rubygems-integration/all/bin:/usr/local/aws-cli/v2/current/bin/",H5P_IGNORE_MODIFIERS:"ig",DERBY_HOME:"/usr/lib/jvm/java-8-oracle/db",NVM_IOJS_ORG_MIRROR:"https://iojs.org/dist",PAPERSIZE:"a4",SESSION_MANAGER:"local/big:@/tmp/.ICE-unix/4684,unix/big:/tmp/.ICE-unix/4684",NODE:"/home/paal/.nvm/versions/node/v16.17.0/bin/node",npm_package_name:"h5p-audio-recorder",XDG_MENU_PREFIX:"gnome-",S_COLORS:"auto",GNOME_TERMINAL_SCREEN:"/org/gnome/Terminal/screen/062b8e11_67a7_4525_ab8b_4a95d6c737a9",LC_ADDRESS:"nb_NO.UTF-8",XDG_RUNTIME_DIR:"/run/user/1000",DISPLAY:":1",LANG:"en_US.UTF-8",XDG_CURRENT_DESKTOP:"ubuntu:GNOME",LC_TELEPHONE:"nb_NO.UTF-8",LS_COLORS:"rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=00:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.zst=01;31:*.tzst=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:*.wim=01;31:*.swm=01;31:*.dwm=01;31:*.esd=01;31:*.jpg=01;35:*.jpeg=01;35:*.mjpg=01;35:*.mjpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:*.m4a=00;36:*.mid=00;36:*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:*.ra=00;36:*.wav=00;36:*.oga=00;36:*.opus=00;36:*.spx=00;36:*.xspf=00;36:",XDG_SESSION_DESKTOP:"ubuntu",XMODIFIERS:"@im=ibus",GNOME_TERMINAL_SERVICE:":1.88",XAUTHORITY:"/run/user/1000/gdm/Xauthority",npm_lifecycle_script:"cross-env NODE_ENV='production' webpack",SSH_AUTH_SOCK:"/run/user/1000/keyring/ssh",LC_NAME:"nb_NO.UTF-8",SHELL:"/bin/bash",npm_package_version:"1.0.0",npm_lifecycle_event:"build",QT_ACCESSIBILITY:"1",GDMSESSION:"ubuntu",LESSCLOSE:"/usr/bin/lesspipe %s %s",LC_MEASUREMENT:"nb_NO.UTF-8",TEXTDOMAINDIR:"/usr/share/locale/",GJS_DEBUG_OUTPUT:"stderr",GPG_AGENT_INFO:"/run/user/1000/gnupg/S.gpg-agent:0:1",LC_IDENTIFICATION:"nb_NO.UTF-8",XDG_VTNR:"2",QT_IM_MODULE:"ibus",npm_config_globalconfig:"/home/paal/.nvm/versions/node/v16.17.0/etc/npmrc",npm_config_init_module:"/home/paal/.npm-init.js",JAVA_HOME:"/usr/lib/jvm/java-8-oracle",PWD:"/home/paal/h5p-libs/joubel/h5p-audio-recorder",npm_execpath:"/home/paal/.nvm/versions/node/v16.17.0/lib/node_modules/npm/bin/npm-cli.js",CLUTTER_IM_MODULE:"xim",NVM_CD_FLAGS:"",XDG_DATA_DIRS:"/usr/share/ubuntu:/usr/local/share/:/usr/share/:/var/lib/snapd/desktop",XDG_CONFIG_DIRS:"/etc/xdg/xdg-ubuntu:/etc/xdg",npm_config_global_prefix:"/home/paal/.nvm/versions/node/v16.17.0",LC_NUMERIC:"nb_NO.UTF-8",npm_command:"run-script",LC_PAPER:"nb_NO.UTF-8",VTE_VERSION:"5202",MANPATH:"/home/paal/.nvm/versions/node/v16.17.0/share/man:/home/paal/.linuxbrew/share/man:/usr/share/man:/usr/local/man:/usr/local/share/man:/usr/lib/jvm/java-8-oracle/man",INIT_CWD:"/home/paal/projects/h5p/libs/joubel/h5p-audio-recorder",EDITOR:"vi",NODE_ENV:"production"}.NODE_DEBUG){var a={GJS_DEBUG_TOPICS:"JS ERROR;JS LOG",LESSOPEN:"| /usr/bin/lesspipe %s",J2SDKDIR:"/usr/lib/jvm/java-8-oracle",USER:"paal",LC_TIME:"nb_NO.UTF-8",npm_config_user_agent:"npm/8.15.0 node/v16.17.0 linux x64 workspaces/false",TEXTDOMAIN:"im-config",XDG_SEAT:"seat0",SSH_AGENT_PID:"4774",XDG_SESSION_TYPE:"x11",npm_node_execpath:"/home/paal/.nvm/versions/node/v16.17.0/bin/node",J2REDIR:"/usr/lib/jvm/java-8-oracle/jre",SHLVL:"2",npm_config_noproxy:"",OLDPWD:"/home/paal/h5p-libs/joubel",QT4_IM_MODULE:"xim",HOME:"/home/paal",H5P_IGNORE_PATTERN:"^\\.|~$|LICENSE|\\.md|node_modules|gulpfile.js|package-lock.json|.*language\\/source.*$",DESKTOP_SESSION:"ubuntu",NVM_BIN:"/home/paal/.nvm/versions/node/v16.17.0/bin",NVM_NODEJS_ORG_MIRROR:"https://nodejs.org/dist",npm_package_json:"/home/paal/projects/h5p/libs/joubel/h5p-audio-recorder/package.json",GNOME_SHELL_SESSION_MODE:"ubuntu",GTK_MODULES:"gail:atk-bridge",LC_MONETARY:"nb_NO.UTF-8",npm_config_userconfig:"/home/paal/.npmrc",npm_config_local_prefix:"/home/paal/projects/h5p/libs/joubel/h5p-audio-recorder",DBUS_SESSION_BUS_ADDRESS:"unix:path=/run/user/1000/bus",npm_config_engine_strict:"true",COLORTERM:"truecolor",COLOR:"1",NVM_DIR:"/home/paal/.nvm",H5P_IGNORE_REPOS:"h5p-personality-quiz,h5p-editor-mini-course,h5p-editor-conditional,h5p-fullscreen-scroller",npm_config_metrics_registry:"https://registry.npmjs.org/",MANDATORY_PATH:"/usr/share/gconf/ubuntu.mandatory.path",QT_QPA_PLATFORMTHEME:"appmenu-qt5",IM_CONFIG_PHASE:"2",LOGNAME:"paal",GTK_IM_MODULE:"ibus",_:"/home/paal/.nvm/versions/node/v16.17.0/bin/npm",npm_config_prefix:"/home/paal/.nvm/versions/node/v16.17.0",DEFAULTS_PATH:"/usr/share/gconf/ubuntu.default.path",USERNAME:"paal",XDG_SESSION_ID:"5",TERM:"xterm-256color",npm_config_cache:"/home/paal/.npm",GNOME_DESKTOP_SESSION_ID:"this-is-deprecated",GTK2_MODULES:"overlay-scrollbar",WINDOWPATH:"2",npm_config_node_gyp:"/home/paal/.nvm/versions/node/v16.17.0/lib/node_modules/npm/node_modules/node-gyp/bin/node-gyp.js",PATH:"/home/paal/projects/h5p/libs/joubel/h5p-audio-recorder/node_modules/.bin:/home/paal/projects/h5p/libs/joubel/node_modules/.bin:/home/paal/projects/h5p/libs/node_modules/.bin:/home/paal/projects/h5p/node_modules/.bin:/home/paal/projects/node_modules/.bin:/home/paal/node_modules/.bin:/home/node_modules/.bin:/node_modules/.bin:/home/paal/.nvm/versions/node/v16.17.0/lib/node_modules/npm/node_modules/@npmcli/run-script/lib/node-gyp-bin:/home/paal/.nvm/versions/node/v16.17.0/bin:/home/paal/.linuxbrew/bin:/home/paal/.composer/vendor/bin:~/.local/bin:/usr/local/go/bin:/home/paal/projects/h5pcom/h5pcom-tools:/home/paal/bin:/usr/bin:/home/paal/.config/composer/vendor/bin:/home/paal/.symfony/bin:/home/paal/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/usr/lib/jvm/java-8-oracle/bin:/usr/lib/jvm/java-8-oracle/db/bin:/usr/lib/jvm/java-8-oracle/jre/bin:/home/paal/.gem/ruby/2.5.0/bin:/var/lib/gems/2.5.0/bin:/usr/lib/x86_64-linux-gnu/rubygems-integration/2.5.0/bin:/usr/share/rubygems-integration/2.5.0/bin:/usr/share/rubygems-integration/all/bin:/usr/local/aws-cli/v2/current/bin/",H5P_IGNORE_MODIFIERS:"ig",DERBY_HOME:"/usr/lib/jvm/java-8-oracle/db",NVM_IOJS_ORG_MIRROR:"https://iojs.org/dist",PAPERSIZE:"a4",SESSION_MANAGER:"local/big:@/tmp/.ICE-unix/4684,unix/big:/tmp/.ICE-unix/4684",NODE:"/home/paal/.nvm/versions/node/v16.17.0/bin/node",npm_package_name:"h5p-audio-recorder",XDG_MENU_PREFIX:"gnome-",S_COLORS:"auto",GNOME_TERMINAL_SCREEN:"/org/gnome/Terminal/screen/062b8e11_67a7_4525_ab8b_4a95d6c737a9",LC_ADDRESS:"nb_NO.UTF-8",XDG_RUNTIME_DIR:"/run/user/1000",DISPLAY:":1",LANG:"en_US.UTF-8",XDG_CURRENT_DESKTOP:"ubuntu:GNOME",LC_TELEPHONE:"nb_NO.UTF-8",LS_COLORS:"rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=00:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.zst=01;31:*.tzst=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:*.wim=01;31:*.swm=01;31:*.dwm=01;31:*.esd=01;31:*.jpg=01;35:*.jpeg=01;35:*.mjpg=01;35:*.mjpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:*.m4a=00;36:*.mid=00;36:*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:*.ra=00;36:*.wav=00;36:*.oga=00;36:*.opus=00;36:*.spx=00;36:*.xspf=00;36:",XDG_SESSION_DESKTOP:"ubuntu",XMODIFIERS:"@im=ibus",GNOME_TERMINAL_SERVICE:":1.88",XAUTHORITY:"/run/user/1000/gdm/Xauthority",npm_lifecycle_script:"cross-env NODE_ENV='production' webpack",SSH_AUTH_SOCK:"/run/user/1000/keyring/ssh",LC_NAME:"nb_NO.UTF-8",SHELL:"/bin/bash",npm_package_version:"1.0.0",npm_lifecycle_event:"build",QT_ACCESSIBILITY:"1",GDMSESSION:"ubuntu",LESSCLOSE:"/usr/bin/lesspipe %s %s",LC_MEASUREMENT:"nb_NO.UTF-8",TEXTDOMAINDIR:"/usr/share/locale/",GJS_DEBUG_OUTPUT:"stderr",GPG_AGENT_INFO:"/run/user/1000/gnupg/S.gpg-agent:0:1",LC_IDENTIFICATION:"nb_NO.UTF-8",XDG_VTNR:"2",QT_IM_MODULE:"ibus",npm_config_globalconfig:"/home/paal/.nvm/versions/node/v16.17.0/etc/npmrc",npm_config_init_module:"/home/paal/.npm-init.js",JAVA_HOME:"/usr/lib/jvm/java-8-oracle",PWD:"/home/paal/h5p-libs/joubel/h5p-audio-recorder",npm_execpath:"/home/paal/.nvm/versions/node/v16.17.0/lib/node_modules/npm/bin/npm-cli.js",CLUTTER_IM_MODULE:"xim",NVM_CD_FLAGS:"",XDG_DATA_DIRS:"/usr/share/ubuntu:/usr/local/share/:/usr/share/:/var/lib/snapd/desktop",XDG_CONFIG_DIRS:"/etc/xdg/xdg-ubuntu:/etc/xdg",npm_config_global_prefix:"/home/paal/.nvm/versions/node/v16.17.0",LC_NUMERIC:"nb_NO.UTF-8",npm_command:"run-script",LC_PAPER:"nb_NO.UTF-8",VTE_VERSION:"5202",MANPATH:"/home/paal/.nvm/versions/node/v16.17.0/share/man:/home/paal/.linuxbrew/share/man:/usr/share/man:/usr/local/man:/usr/local/share/man:/usr/lib/jvm/java-8-oracle/man",INIT_CWD:"/home/paal/projects/h5p/libs/joubel/h5p-audio-recorder",EDITOR:"vi",NODE_ENV:"production"}.NODE_DEBUG;a=a.replace(/[|\\{}()[\]^$+?.]/g,"\\$&").replace(/\*/g,".*").replace(/,/g,"$|^").toUpperCase(),s=new RegExp("^"+a+"$","i")}function c(e,n){var r={seen:[],stylize:u};return arguments.length>=3&&(r.depth=arguments[2]),arguments.length>=4&&(r.colors=arguments[3]),y(n)?r.showHidden=n:n&&t._extend(r,n),b(r.showHidden)&&(r.showHidden=!1),b(r.depth)&&(r.depth=2),b(r.colors)&&(r.colors=!1),b(r.customInspect)&&(r.customInspect=!0),r.colors&&(r.stylize=l),p(r,e,r.depth)}function l(e,t){var n=c.styles[t];return n?"["+c.colors[n][0]+"m"+e+"["+c.colors[n][1]+"m":e}function u(e,t){return e}function p(e,n,r){if(e.customInspect&&n&&E(n.inspect)&&n.inspect!==t.inspect&&(!n.constructor||n.constructor.prototype!==n)){var o=n.inspect(r,e);return v(o)||(o=p(e,o,r)),o}var i=function(e,t){if(b(t))return e.stylize("undefined","undefined");if(v(t)){var n="'"+JSON.stringify(t).replace(/^"|"$/g,"").replace(/'/g,"\\'").replace(/\\"/g,'"')+"'";return e.stylize(n,"string")}return g(t)?e.stylize(""+t,"number"):y(t)?e.stylize(""+t,"boolean"):m(t)?e.stylize("null","null"):void 0}(e,n);if(i)return i;var s=Object.keys(n),a=function(e){var t={};return e.forEach((function(e,n){t[e]=!0})),t}(s);if(e.showHidden&&(s=Object.getOwnPropertyNames(n)),O(n)&&(s.indexOf("message")>=0||s.indexOf("description")>=0))return f(n);if(0===s.length){if(E(n)){var c=n.name?": "+n.name:"";return e.stylize("[Function"+c+"]","special")}if(_(n))return e.stylize(RegExp.prototype.toString.call(n),"regexp");if(S(n))return e.stylize(Date.prototype.toString.call(n),"date");if(O(n))return f(n)}var l,u="",w=!1,A=["{","}"];return h(n)&&(w=!0,A=["[","]"]),E(n)&&(u=" [Function"+(n.name?": "+n.name:"")+"]"),_(n)&&(u=" "+RegExp.prototype.toString.call(n)),S(n)&&(u=" "+Date.prototype.toUTCString.call(n)),O(n)&&(u=" "+f(n)),0!==s.length||w&&0!=n.length?r<0?_(n)?e.stylize(RegExp.prototype.toString.call(n),"regexp"):e.stylize("[Object]","special"):(e.seen.push(n),l=w?function(e,t,n,r,o){for(var i=[],s=0,a=t.length;s<a;++s)M(t,String(s))?i.push(d(e,t,n,r,String(s),!0)):i.push("");return o.forEach((function(o){o.match(/^\d+$/)||i.push(d(e,t,n,r,o,!0))})),i}(e,n,r,a,s):s.map((function(t){return d(e,n,r,a,t,w)})),e.seen.pop(),function(e,t,n){return e.reduce((function(e,t){return t.indexOf("\n"),e+t.replace(/\u001b\[\d\d?m/g,"").length+1}),0)>60?n[0]+(""===t?"":t+"\n ")+" "+e.join(",\n ")+" "+n[1]:n[0]+t+" "+e.join(", ")+" "+n[1]}(l,u,A)):A[0]+u+A[1]}function f(e){return"["+Error.prototype.toString.call(e)+"]"}function d(e,t,n,r,o,i){var s,a,c;if((c=Object.getOwnPropertyDescriptor(t,o)||{value:t[o]}).get?a=c.set?e.stylize("[Getter/Setter]","special"):e.stylize("[Getter]","special"):c.set&&(a=e.stylize("[Setter]","special")),M(r,o)||(s="["+o+"]"),a||(e.seen.indexOf(c.value)<0?(a=m(n)?p(e,c.value,null):p(e,c.value,n-1)).indexOf("\n")>-1&&(a=i?a.split("\n").map((function(e){return" "+e})).join("\n").substr(2):"\n"+a.split("\n").map((function(e){return" "+e})).join("\n")):a=e.stylize("[Circular]","special")),b(s)){if(i&&o.match(/^\d+$/))return a;(s=JSON.stringify(""+o)).match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)?(s=s.substr(1,s.length-2),s=e.stylize(s,"name")):(s=s.replace(/'/g,"\\'").replace(/\\"/g,'"').replace(/(^"|"$)/g,"'"),s=e.stylize(s,"string"))}return s+": "+a}function h(e){return Array.isArray(e)}function y(e){return"boolean"==typeof e}function m(e){return null===e}function g(e){return"number"==typeof e}function v(e){return"string"==typeof e}function b(e){return void 0===e}function _(e){return w(e)&&"[object RegExp]"===A(e)}function w(e){return"object"==typeof e&&null!==e}function S(e){return w(e)&&"[object Date]"===A(e)}function O(e){return w(e)&&("[object Error]"===A(e)||e instanceof Error)}function E(e){return"function"==typeof e}function A(e){return Object.prototype.toString.call(e)}function x(e){return e<10?"0"+e.toString(10):e.toString(10)}t.debuglog=function(e){if(e=e.toUpperCase(),!i[e])if(s.test(e)){var n=process.pid;i[e]=function(){var r=t.format.apply(t,arguments);console.error("%s %d: %s",e,n,r)}}else i[e]=function(){};return i[e]},t.inspect=c,c.colors={bold:[1,22],italic:[3,23],underline:[4,24],inverse:[7,27],white:[37,39],grey:[90,39],black:[30,39],blue:[34,39],cyan:[36,39],green:[32,39],magenta:[35,39],red:[31,39],yellow:[33,39]},c.styles={special:"cyan",number:"yellow",boolean:"yellow",undefined:"grey",null:"bold",string:"green",date:"magenta",regexp:"red"},t.types=n(673),t.isArray=h,t.isBoolean=y,t.isNull=m,t.isNullOrUndefined=function(e){return null==e},t.isNumber=g,t.isString=v,t.isSymbol=function(e){return"symbol"==typeof e},t.isUndefined=b,t.isRegExp=_,t.types.isRegExp=_,t.isObject=w,t.isDate=S,t.types.isDate=S,t.isError=O,t.types.isNativeError=O,t.isFunction=E,t.isPrimitive=function(e){return null===e||"boolean"==typeof e||"number"==typeof e||"string"==typeof e||"symbol"==typeof e||void 0===e},t.isBuffer=n(579);var j=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];function T(){var e=new Date,t=[x(e.getHours()),x(e.getMinutes()),x(e.getSeconds())].join(":");return[e.getDate(),j[e.getMonth()],t].join(" ")}function M(e,t){return Object.prototype.hasOwnProperty.call(e,t)}t.log=function(){console.log("%s - %s",T(),t.format.apply(t,arguments))},t.inherits=n(87),t._extend=function(e,t){if(!t||!w(t))return e;for(var n=Object.keys(t),r=n.length;r--;)e[n[r]]=t[n[r]];return e};var P="undefined"!=typeof Symbol?Symbol("util.promisify.custom"):void 0;function C(e,t){if(!e){var n=new Error("Promise was rejected with a falsy value");n.reason=e,e=n}return t(e)}t.promisify=function(e){if("function"!=typeof e)throw new TypeError('The "original" argument must be of type Function');if(P&&e[P]){var t;if("function"!=typeof(t=e[P]))throw new TypeError('The "util.promisify.custom" argument must be of type Function');return Object.defineProperty(t,P,{value:t,enumerable:!1,writable:!1,configurable:!0}),t}function t(){for(var t,n,r=new Promise((function(e,r){t=e,n=r})),o=[],i=0;i<arguments.length;i++)o.push(arguments[i]);o.push((function(e,r){e?n(e):t(r)}));try{e.apply(this,o)}catch(e){n(e)}return r}return Object.setPrototypeOf(t,Object.getPrototypeOf(e)),P&&Object.defineProperty(t,P,{value:t,enumerable:!1,writable:!1,configurable:!0}),Object.defineProperties(t,r(e))},t.promisify.custom=P,t.callbackify=function(e){if("function"!=typeof e)throw new TypeError('The "original" argument must be of type Function');function t(){for(var t=[],n=0;n<arguments.length;n++)t.push(arguments[n]);var r=t.pop();if("function"!=typeof r)throw new TypeError("The last argument must be of type Function");var o=this,i=function(){return r.apply(o,arguments)};e.apply(this,t).then((function(e){process.nextTick(i.bind(null,null,e))}),(function(e){process.nextTick(C.bind(null,e,i))}))}return Object.setPrototypeOf(t,Object.getPrototypeOf(e)),Object.defineProperties(t,r(e)),t}},959:(e,t)=>{"use strict";t.Z=(e,t)=>{const n=e.__vccOpts||e;for(const[e,r]of t)n[e]=r;return n}},338:(e,t,n)=>{var r=n(833);r.__esModule&&(r=r.default),"string"==typeof r&&(r=[[e.id,r,""]]),r.locals&&(e.exports=r.locals),(0,n(441).Z)("5dcc9be9",r,!0,{})},680:(e,t,n)=>{var r=n(903);r.__esModule&&(r=r.default),"string"==typeof r&&(r=[[e.id,r,""]]),r.locals&&(e.exports=r.locals),(0,n(441).Z)("28c669e3",r,!0,{})},656:(e,t,n)=>{var r=n(867);r.__esModule&&(r=r.default),"string"==typeof r&&(r=[[e.id,r,""]]),r.locals&&(e.exports=r.locals),(0,n(441).Z)("4ca8745e",r,!0,{})},441:(e,t,n)=>{"use strict";function r(e,t){for(var n=[],r={},o=0;o<t.length;o++){var i=t[o],s=i[0],a={id:e+":"+o,css:i[1],media:i[2],sourceMap:i[3]};r[s]?r[s].parts.push(a):n.push(r[s]={id:s,parts:[a]})}return n}n.d(t,{Z:()=>h});var o="undefined"!=typeof document;if("undefined"!=typeof DEBUG&&DEBUG&&!o)throw new Error("vue-style-loader cannot be used in a non-browser environment. Use { target: 'node' } in your Webpack config to indicate a server-rendering environment.");var i={},s=o&&(document.head||document.getElementsByTagName("head")[0]),a=null,c=0,l=!1,u=function(){},p=null,f="data-vue-ssr-id",d="undefined"!=typeof navigator&&/msie [6-9]\b/.test(navigator.userAgent.toLowerCase());function h(e,t,n,o){l=n,p=o||{};var s=r(e,t);return y(s),function(t){for(var n=[],o=0;o<s.length;o++){var a=s[o];(c=i[a.id]).refs--,n.push(c)}for(t?y(s=r(e,t)):s=[],o=0;o<n.length;o++){var c;if(0===(c=n[o]).refs){for(var l=0;l<c.parts.length;l++)c.parts[l]();delete i[c.id]}}}}function y(e){for(var t=0;t<e.length;t++){var n=e[t],r=i[n.id];if(r){r.refs++;for(var o=0;o<r.parts.length;o++)r.parts[o](n.parts[o]);for(;o<n.parts.length;o++)r.parts.push(g(n.parts[o]));r.parts.length>n.parts.length&&(r.parts.length=n.parts.length)}else{var s=[];for(o=0;o<n.parts.length;o++)s.push(g(n.parts[o]));i[n.id]={id:n.id,refs:1,parts:s}}}}function m(){var e=document.createElement("style");return e.type="text/css",s.appendChild(e),e}function g(e){var t,n,r=document.querySelector("style["+f+'~="'+e.id+'"]');if(r){if(l)return u;r.parentNode.removeChild(r)}if(d){var o=c++;r=a||(a=m()),t=_.bind(null,r,o,!1),n=_.bind(null,r,o,!0)}else r=m(),t=w.bind(null,r),n=function(){r.parentNode.removeChild(r)};return t(e),function(r){if(r){if(r.css===e.css&&r.media===e.media&&r.sourceMap===e.sourceMap)return;t(e=r)}else n()}}var v,b=(v=[],function(e,t){return v[e]=t,v.filter(Boolean).join("\n")});function _(e,t,n,r){var o=n?"":r.css;if(e.styleSheet)e.styleSheet.cssText=b(t,o);else{var i=document.createTextNode(o),s=e.childNodes;s[t]&&e.removeChild(s[t]),s.length?e.insertBefore(i,s[t]):e.appendChild(i)}}function w(e,t){var n=t.css,r=t.media,o=t.sourceMap;if(r&&e.setAttribute("media",r),p.ssrId&&e.setAttribute(f,t.id),o&&(n+="\n/*# sourceURL="+o.sources[0]+" */",n+="\n/*# sourceMappingURL=data:application/json;base64,"+btoa(unescape(encodeURIComponent(JSON.stringify(o))))+" */"),e.styleSheet)e.styleSheet.cssText=n;else{for(;e.firstChild;)e.removeChild(e.firstChild);e.appendChild(document.createTextNode(n))}}},505:(e,t,n)=>{"use strict";var r=n(278),o=n(973),i=n(737),s=i("Object.prototype.toString"),a=n(698)(),c="undefined"==typeof globalThis?n.g:globalThis,l=o(),u=i("String.prototype.slice"),p={},f=n(828),d=Object.getPrototypeOf;a&&f&&d&&r(l,(function(e){if("function"==typeof c[e]){var t=new c[e];if(Symbol.toStringTag in t){var n=d(t),r=f(n,Symbol.toStringTag);if(!r){var o=d(n);r=f(o,Symbol.toStringTag)}p[e]=r.get}}}));var h=n(387);e.exports=function(e){return!!h(e)&&(a&&Symbol.toStringTag in e?function(e){var t=!1;return r(p,(function(n,r){if(!t)try{var o=n.call(e);o===r&&(t=o)}catch(e){}})),t}(e):u(s(e),8,-1))}},945:e=>{"use strict";e.exports="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAyMC4xLjAsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjxzdmcgdmVyc2lvbj0iMS4xIiBpZD0iTGF5ZXJfMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeD0iMHB4IiB5PSIwcHgiDQoJIHZpZXdCb3g9IjAgMCAxMDUuNCAxMDUuNCIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgMTA1LjQgMTA1LjQ7IiB4bWw6c3BhY2U9InByZXNlcnZlIj4NCjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+DQoJLnN0MHtmaWxsOiNFREFCQjA7fQ0KPC9zdHlsZT4NCjx0aXRsZT5BcnRib2FyZCAxPC90aXRsZT4NCjxnPg0KCTxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik01Mi43LDBDMjMuNiwwLDAsMjMuNiwwLDUyLjdzMjMuNiw1Mi43LDUyLjcsNTIuN2MyOS4xLDAsNTIuNy0yMy42LDUyLjctNTIuN0MxMDUuNCwyMy42LDgxLjgsMCw1Mi43LDB6DQoJCSBNNTIuNywxMDUuMmMtMjksMC01Mi41LTIzLjUtNTIuNS01Mi41UzIzLjcsMC4yLDUyLjcsMC4yczUyLjUsMjMuNSw1Mi41LDUyLjVDMTA1LjIsODEuNyw4MS43LDEwNS4yLDUyLjcsMTA1LjJ6Ii8+DQoJPHBhdGggY2xhc3M9InN0MCIgZD0iTTUyLjcsMTUuMkMzMiwxNS4yLDE1LjIsMzIsMTUuMiw1Mi43UzMyLDkwLjIsNTIuNyw5MC4yczM3LjUtMTYuOCwzNy41LTM3LjVjMCwwLDAsMCwwLDANCgkJQzkwLjEsMzIsNzMuNCwxNS4yLDUyLjcsMTUuMnoiLz4NCgk8cGF0aCBjbGFzcz0ic3QwIiBkPSJNNTIuNyw3LjhDMjcuOSw3LjgsNy44LDI3LjksNy44LDUyLjdzMjAuMSw0NC45LDQ0LjksNDQuOWMyNC44LDAsNDQuOS0yMC4xLDQ0LjktNDQuOQ0KCQlDOTcuNiwyNy45LDc3LjUsNy44LDUyLjcsNy44eiBNNTIuNyw5Ni42Yy0yNC4yLDAtNDMuOS0xOS43LTQzLjktNDMuOWMwLTI0LjIsMTkuNy00My45LDQzLjktNDMuOWMyNC4yLDAsNDMuOSwxOS42LDQzLjksNDMuOQ0KCQlDOTYuNiw3Ni45LDc2LjksOTYuNiw1Mi43LDk2LjZ6Ii8+DQo8L2c+DQo8L3N2Zz4NCg=="},973:(e,t,n)=>{"use strict";var r=["BigInt64Array","BigUint64Array","Float32Array","Float64Array","Int16Array","Int32Array","Int8Array","Uint16Array","Uint32Array","Uint8Array","Uint8ClampedArray"],o="undefined"==typeof globalThis?n.g:globalThis;e.exports=function(){for(var e=[],t=0;t<r.length;t++)"function"==typeof o[r[t]]&&(e[e.length]=r[t]);return e}},828:(e,t,n)=>{"use strict";var r=n(750)("%Object.getOwnPropertyDescriptor%",!0);if(r)try{r([],"length")}catch(e){r=null}e.exports=r}},t={};function n(r){var o=t[r];if(void 0!==o)return o.exports;var i=t[r]={id:r,exports:{}};return e[r](i,i.exports,n),i.exports}n.m=e,n.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return n.d(t,{a:t}),t},n.d=(e,t)=>{for(var r in t)n.o(t,r)&&!n.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},n.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),n.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),n.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.b=document.baseURI||self.location.href,H5P=H5P||{},H5P.AudioRecorder=n(452).Z})();; H5P.Column = (function (EventDispatcher) { /** * Column Constructor * * @class * @param {Object} params Describes task behavior * @param {number} id Content identifier * @param {Object} data User specific data to adapt behavior */ function Column(params, id, data) { /** @alias H5P.Column# */ var self = this; // We support events by extending this class EventDispatcher.call(self); // Add defaults params = params || {}; if (params.useSeparators === undefined) { params.useSeparators = true; } this.contentData = data; // Column wrapper element var wrapper; // H5P content in the column var instances = []; var instanceContainers = []; // Number of tasks among instances var numTasks = 0; // Number of tasks that has been completed var numTasksCompleted = 0; // Keep track of result for each task var tasksResultEvent = []; // Keep track of last content's margin state var previousHasMargin; /** * Calculate score and trigger completed event. * * @private */ var completed = function () { // Sum all scores var raw = 0; var max = 0; for (var i = 0; i < tasksResultEvent.length; i++) { var event = tasksResultEvent[i]; raw += event.getScore(); max += event.getMaxScore(); } self.triggerXAPIScored(raw, max, 'completed'); }; /** * Generates an event handler for the given task index. * * @private * @param {number} taskIndex * @return {function} xAPI event handler */ var trackScoring = function (taskIndex) { return function (event) { if (event.getScore() === null) { return; // Skip, not relevant } if (tasksResultEvent[taskIndex] === undefined) { // Update number of completed tasks numTasksCompleted++; } // Keep track of latest event with result tasksResultEvent[taskIndex] = event; // Track progress var progressed = self.createXAPIEventTemplate('progressed'); progressed.data.statement.object.definition.extensions['http://id.tincanapi.com/extension/ending-point'] = taskIndex + 1; self.trigger(progressed); // Check to see if we're done if (numTasksCompleted === numTasks) { // Run this after the current event is sent setTimeout(function () { completed(); // Done }, 0); } }; }; /** * Creates a new ontent instance from the given content parameters and * then attaches it the wrapper. Sets up event listeners. * * @private * @param {Object} content Parameters * @param {Object} [contentData] Content Data */ var addRunnable = function (content, contentData) { // Create container for content var container = document.createElement('div'); container.classList.add('h5p-column-content'); // Content overrides var library = content.library.split(' ')[0]; if (library === 'H5P.Video') { // Prevent video from growing endlessly since height is unlimited. content.params.visuals.fit = false; } // Create content instance var instance = H5P.newRunnable(content, id, undefined, true, contentData); // Bubble resize events bubbleUp(instance, 'resize', self); // Check if instance is a task if (Column.isTask(instance)) { // Tasks requires completion instance.on('xAPI', trackScoring(numTasks)); numTasks++; } if (library === 'H5P.Image' || library === 'H5P.TwitterUserFeed') { // Resize when images are loaded instance.on('loaded', function () { self.trigger('resize'); }); } // Keep track of all instances instances.push(instance); instanceContainers.push({ hasAttached: false, container: container, instanceIndex: instances.length - 1, }); // Add to DOM wrapper wrapper.appendChild(container); }; /** * Help get data for content at given index * * @private * @param {number} index * @returns {Object} Data object with previous state */ var grabContentData = function (index) { var contentData = { parent: self }; if (data.previousState && data.previousState.instances && data.previousState.instances[index]) { contentData.previousState = data.previousState.instances[index]; } return contentData; }; /** * Adds separator before the next content. * * @private * @param {string} libraryName Name of the next content type * @param {string} useSeparator */ var addSeparator = function (libraryName, useSeparator) { // Determine separator spacing var thisHasMargin = (hasMargins.indexOf(libraryName) !== -1); // Only add if previous content exists if (previousHasMargin !== undefined) { // Create separator element var separator = document.createElement('div'); //separator.classList.add('h5p-column-ruler'); // If no margins, check for top margin only if (!thisHasMargin && (hasTopMargins.indexOf(libraryName) === -1)) { if (!previousHasMargin) { // None of them have margin // Only add separator if forced if (useSeparator === 'enabled') { // Add ruler separator.classList.add('h5p-column-ruler'); // Add space both before and after the ruler separator.classList.add('h5p-column-space-before-n-after'); } else { // Default is to separte using a single space, no ruler separator.classList.add('h5p-column-space-before'); } } else { // We don't have any margin but the previous content does // Only add separator if forced if (useSeparator === 'enabled') { // Add ruler separator.classList.add('h5p-column-ruler'); // Add space after the ruler separator.classList.add('h5p-column-space-after'); } } } else if (!previousHasMargin) { // We have margin but not the previous content doesn't // Only add separator if forced if (useSeparator === 'enabled') { // Add ruler separator.classList.add('h5p-column-ruler'); // Add space after the ruler separator.classList.add('h5p-column-space-before'); } } else { // Both already have margin if (useSeparator !== 'disabled') { // Default is to add ruler unless its disabled separator.classList.add('h5p-column-ruler'); } } // Insert into DOM wrapper.appendChild(separator); } // Keep track of spacing for next separator previousHasMargin = thisHasMargin || (hasBottomMargins.indexOf(libraryName) !== -1); }; /** * Creates a wrapper and the column content the first time the column * is attached to the DOM. * * @private */ var createHTML = function () { // Create wrapper wrapper = document.createElement('div'); // Go though all contents for (var i = 0; i < params.content.length; i++) { var content = params.content[i]; // In case the author has created an element without selecting any // library if (content.content === undefined) { continue; } if (params.useSeparators) { // (check for global override) // Add separator between contents addSeparator(content.content.library.split(' ')[0], content.useSeparator); } // Add content addRunnable(content.content, grabContentData(i)); } }; /** * Attach the column to the given container * * @param {H5P.jQuery} $container */ self.attach = function ($container) { if (wrapper === undefined) { // Create wrapper and content createHTML(); } // Attach instances that have not been attached instanceContainers.filter(function (container) { return !container.hasAttached }) .forEach(function (container) { instances[container.instanceIndex] .attach(H5P.jQuery(container.container)); // Remove any fullscreen buttons disableFullscreen(instances[container.instanceIndex]); }); // Add to DOM $container.addClass('h5p-column').html('').append(wrapper); }; /** * Create object containing information about the current state * of this content. * * @return {Object} */ self.getCurrentState = function () { // Get previous state object or create new state object var state = (data.previousState ? data.previousState : {}); if (!state.instances) { state.instances = []; } // Grab the current state for each instance for (var i = 0; i < instances.length; i++) { var instance = instances[i]; if (instance.getCurrentState instanceof Function || typeof instance.getCurrentState === 'function') { state.instances[i] = instance.getCurrentState(); } } // Done return state; }; /** * Get xAPI data. * Contract used by report rendering engine. * * @see contract at {@link https://h5p.org/documentation/developers/contracts#guides-header-6} */ self.getXAPIData = function () { var xAPIEvent = self.createXAPIEventTemplate('answered'); addQuestionToXAPI(xAPIEvent); xAPIEvent.setScoredResult(self.getScore(), self.getMaxScore(), self, true, self.getScore() === self.getMaxScore() ); return { statement: xAPIEvent.data.statement, children: getXAPIDataFromChildren(instances) }; }; /** * Get score for all children * Contract used for getting the complete score of task. * * @return {number} Score for questions */ self.getScore = function () { return instances.reduce(function (prev, instance) { return prev + (instance.getScore ? instance.getScore() : 0); }, 0); }; /** * Get maximum score possible for all children instances * Contract. * * @return {number} Maximum score for questions */ self.getMaxScore = function () { return instances.reduce(function (prev, instance) { return prev + (instance.getMaxScore ? instance.getMaxScore() : 0); }, 0); }; /** * Get answer given * Contract. * * @return {boolean} True, if all answers have been given. */ self.getAnswerGiven = function () { return instances.reduce(function (prev, instance) { return prev && (instance.getAnswerGiven ? instance.getAnswerGiven() : prev); }, true); }; /** * Show solutions. * Contract. */ self.showSolutions = function () { instances.forEach(function (instance) { if (instance.toggleReadSpeaker) { instance.toggleReadSpeaker(true); } if (instance.showSolutions) { instance.showSolutions(); } if (instance.toggleReadSpeaker) { instance.toggleReadSpeaker(false); } }); }; /** * Reset task. * Contract. */ self.resetTask = function () { instances.forEach(function (instance) { if (instance.resetTask) { instance.resetTask(); } }); }; /** * Get instances for all children * TODO: This is not a good interface, we should provide handling needed * handling of the tasks instead of repeating them for each parent... * * @return {Object[]} array of instances */ self.getInstances = function () { return instances; }; /** * Get title, e.g. for xAPI when Column is subcontent. * * @return {string} Title. */ self.getTitle = function () { return H5P.createTitle((self.contentData && self.contentData.metadata && self.contentData.metadata.title) ? self.contentData.metadata.title : 'Column'); }; /** * Add the question itself to the definition part of an xAPIEvent */ var addQuestionToXAPI = function (xAPIEvent) { var definition = xAPIEvent.getVerifiedStatementValue(['object', 'definition']); H5P.jQuery.extend(definition, getxAPIDefinition()); }; /** * Generate xAPI object definition used in xAPI statements. * @return {Object} */ var getxAPIDefinition = function () { var definition = {}; definition.interactionType = 'compound'; definition.type = 'http://adlnet.gov/expapi/activities/cmi.interaction'; definition.description = { 'en-US': '' }; return definition; }; /** * Get xAPI data from sub content types * * @param {Array} of H5P instances * @returns {Array} of xAPI data objects used to build a report */ var getXAPIDataFromChildren = function (children) { return children.map(function (child) { if (typeof child.getXAPIData == 'function') { return child.getXAPIData(); } }).filter(function (data) { return !!data; }); }; // Resize children to fit inside parent bubbleDown(self, 'resize', instances); if (wrapper === undefined) { // Create wrapper and content createHTML(); } self.setActivityStarted(); } Column.prototype = Object.create(EventDispatcher.prototype); Column.prototype.constructor = Column; /** * Makes it easy to bubble events from parent to children * * @private * @param {Object} origin Origin of the Event * @param {string} eventName Name of the Event * @param {Array} targets Targets to trigger event on */ function bubbleDown(origin, eventName, targets) { origin.on(eventName, function (event) { if (origin.bubblingUpwards) { return; // Prevent send event back down. } for (var i = 0; i < targets.length; i++) { targets[i].trigger(eventName, event); } }); } /** * Makes it easy to bubble events from child to parent * * @private * @param {Object} origin Origin of the Event * @param {string} eventName Name of the Event * @param {Object} target Target to trigger event on */ function bubbleUp(origin, eventName, target) { origin.on(eventName, function (event) { // Prevent target from sending event back down target.bubblingUpwards = true; // Trigger event target.trigger(eventName, event); // Reset target.bubblingUpwards = false; }); } /** * Definition of which content types are tasks */ var isTasks = [ 'H5P.ImageHotspotQuestion', 'H5P.Blanks', 'H5P.Essay', 'H5P.SingleChoiceSet', 'H5P.MultiChoice', 'H5P.TrueFalse', 'H5P.DragQuestion', 'H5P.Summary', 'H5P.DragText', 'H5P.MarkTheWords', 'H5P.MemoryGame', 'H5P.QuestionSet', 'H5P.InteractiveVideo', 'H5P.CoursePresentation', 'H5P.DocumentationTool' ]; /** * Check if the given content instance is a task (will give a score) * * @param {Object} instance * @return {boolean} */ Column.isTask = function (instance) { if (instance.isTask !== undefined) { return instance.isTask; // Content will determine self if it's a task } // Go through the valid task names for (var i = 0; i < isTasks.length; i++) { // Check against library info. (instanceof is broken in H5P.newRunnable) if (instance.libraryInfo.machineName === isTasks[i]) { return true; } } return false; } /** * Definition of which content type have margins */ var hasMargins = [ 'H5P.AdvancedText', 'H5P.AudioRecorder', 'H5P.Essay', 'H5P.Link', 'H5P.Accordion', 'H5P.Table', 'H5P.GuessTheAnswer', 'H5P.Blanks', 'H5P.MultiChoice', 'H5P.TrueFalse', 'H5P.DragQuestion', 'H5P.Summary', 'H5P.DragText', 'H5P.MarkTheWords', 'H5P.ImageHotspotQuestion', 'H5P.MemoryGame', 'H5P.Dialogcards', 'H5P.QuestionSet', 'H5P.DocumentationTool' ]; /** * Definition of which content type have top margins */ var hasTopMargins = [ 'H5P.SingleChoiceSet' ]; /** * Definition of which content type have bottom margins */ var hasBottomMargins = [ 'H5P.CoursePresentation', 'H5P.Dialogcards', 'H5P.GuessTheAnswer', 'H5P.ImageSlider' ]; /** * Remove custom fullscreen buttons from sub content. * (A bit of a hack, there should have been some sort of overrideā¦) * * @param {Object} instance */ function disableFullscreen(instance) { switch (instance.libraryInfo.machineName) { case 'H5P.CoursePresentation': if (instance.$fullScreenButton) { instance.$fullScreenButton.remove(); } break; case 'H5P.InteractiveVideo': instance.on('controls', function () { if (instance.controls.$fullscreen) { instance.controls.$fullscreen.remove(); } }); break; } } return Column; })(H5P.EventDispatcher); ;