var H5P = H5P || {};

/**
 * Constructor.
 *
 * @param {Object} params Options for this library.
 * @param {Number} id Content identifier
 * @returns {undefined}
 */
(function ($) {
  H5P.Image = function (params, id, extras) {
    H5P.EventDispatcher.call(this);
    this.extras = extras;

    if (params.file === undefined || !(params.file instanceof Object)) {
      this.placeholder = true;
    }
    else {
      this.source = H5P.getPath(params.file.path, id);
      this.width = params.file.width;
      this.height = params.file.height;
    }

    this.alt = (!params.decorative && params.alt !== undefined) ?
      this.stripHTML(this.htmlDecode(params.alt)) :
      '';

    if (params.title !== undefined) {
      this.title = this.stripHTML(this.htmlDecode(params.title));
    }
  };

  H5P.Image.prototype = Object.create(H5P.EventDispatcher.prototype);
  H5P.Image.prototype.constructor = H5P.Image;

  /**
   * Wipe out the content of the wrapper and put our HTML in it.
   *
   * @param {jQuery} $wrapper
   * @returns {undefined}
   */
  H5P.Image.prototype.attach = function ($wrapper) {
    var self = this;
    var source = this.source;

    if (self.$img === undefined) {
      if(self.placeholder) {
        self.$img = $('<div>', {
          width: '100%',
          height: '100%',
          class: 'h5p-placeholder',
          title: this.title === undefined ? '' : this.title,
          on: {
            load: function () {
              self.trigger('loaded');
            }
          }
        });
      } else {
        self.$img = $('<img>', {
          width: '100%',
          height: '100%',
          src: source,
          alt: this.alt,
          title: this.title === undefined ? '' : this.title,
          on: {
            load: function () {
              self.trigger('loaded');
            }
          }
        });
      }
    }

    $wrapper.addClass('h5p-image').html(self.$img);
  };

  /**
   * Retrieve decoded HTML encoded string.
   *
   * @param {string} input HTML encoded string.
   * @returns {string} Decoded string.
   */
  H5P.Image.prototype.htmlDecode = function (input) {
    const dparser = new DOMParser().parseFromString(input, 'text/html');
    return dparser.documentElement.textContent;
  };

  /**
   * Retrieve string without HTML tags.
   *
   * @param {string} input Input string.
   * @returns {string} Output string.
   */
  H5P.Image.prototype.stripHTML = function (html) {
    const div = document.createElement('div');
    div.innerHTML = html;
    return div.textContent || div.innerText || '';
  };

  return H5P.Image;
}(H5P.jQuery));
;/** @namespace H5P */
H5P.VideoVimeo = (function ($) {

  let numInstances = 0;

  /**
   * Vimeo 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 VimeoPlayer(sources, options, l10n) {
    const self = this;

    let player;

    // Since all the methods of the Vimeo Player SDK are promise-based, we keep
    // track of all relevant state variables so that we can implement the
    // H5P.Video API where all methods return synchronously.
    let buffered = 0;
    let currentQuality;
    let currentTextTrack;
    let currentTime = 0;
    let duration = 0;
    let isMuted = 0;
    let volume = 0;
    let playbackRate = 1;
    let qualities = [];
    let loadingFailedTimeout;
    let failedLoading = false;
    let ratio = 9/16;
    let isLoaded = false;

    const LOADING_TIMEOUT_IN_SECONDS = 8;

    const id = `h5p-vimeo-${++numInstances}`;
    const $wrapper = $('<div/>');
    const $placeholder = $('<div/>', {
      id: id,
      html: `<div class="h5p-video-loading" style="height: 100%; min-height: 200px; display: block; z-index: 100;" aria-label="${l10n.loading}"></div>`
    }).appendTo($wrapper);

    /**
     * Create a new player with the Vimeo Player SDK.
     *
     * @private
     */
    const createVimeoPlayer = async () => {
      if (!$placeholder.is(':visible') || player !== undefined) {
        return;
      }

      // Since the SDK is loaded asynchronously below, explicitly set player to
      // null (unlike undefined) which indicates that creation has begun. This
      // allows the guard statement above to be hit if this function is called
      // more than once.
      player = null;

      const Vimeo = await loadVimeoPlayerSDK();

      const MIN_WIDTH = 200;
      const width = Math.max($wrapper.width(), MIN_WIDTH);

      const canHasControls = options.controls || self.pressToPlay;
      const embedOptions = {
        url: sources[0].path,
        controls: canHasControls,
        responsive: true,
        dnt: true,
        // Hardcoded autoplay to false to avoid playing videos on init
        autoplay: false,
        loop: options.loop ? true : false,
        playsinline: true,
        quality: 'auto',
        width: width,
        muted: false,
        keyboard: canHasControls,
      };

      // Create a new player
      player = new Vimeo.Player(id, embedOptions);

      registerVimeoPlayerEventListeneners(player);

      // Failsafe timeout to handle failed loading of videos.
      // This seems to happen for private videos even though the SDK docs
      // suggests to catch PrivacyError when attempting play()
      loadingFailedTimeout = setTimeout(() => {
        failedLoading = true;
        removeLoadingIndicator();
        $wrapper.html(`<p class="vimeo-failed-loading">${l10n.vimeoLoadingError}</p>`);
        $wrapper.css({
          width: null,
          height: null
        });
        self.trigger('resize');
        self.trigger('error', l10n.vimeoLoadingError);
      }, LOADING_TIMEOUT_IN_SECONDS * 1000);
    }

    const removeLoadingIndicator = () => {
      $placeholder.find('div.h5p-video-loading').remove();
    };

    /**
     * Register event listeners on the given Vimeo player.
     *
     * @private
     * @param {Vimeo.Player} player
     */
    const registerVimeoPlayerEventListeneners = (player) => {
      let isFirstPlay, tracks;
      player.on('loaded', async () => {
        isFirstPlay = true;
        isLoaded = true;
        clearTimeout(loadingFailedTimeout);

        const videoDetails = await getVimeoVideoMetadata(player);
        tracks = videoDetails.tracks.options;
        currentTextTrack = tracks.current;
        duration = videoDetails.duration;
        qualities = videoDetails.qualities;
        currentQuality = 'auto';
        try {
          ratio = videoDetails.dimensions.height / videoDetails.dimensions.width;
        }
        catch (e) { /* Intentionally ignore this, and fallback on the default ratio */ }

        removeLoadingIndicator();

        if (options.startAt) {
          // Vimeo.Player doesn't have an option for setting start time upon
          // instantiation, so we instead perform an initial seek here.
          currentTime = await self.seek(options.startAt);
        }

        self.trigger('ready');
        self.trigger('loaded');
        self.trigger('qualityChange', currentQuality);
        self.trigger('resize');
      });

      player.on('play', () => {
        if (isFirstPlay) {
          isFirstPlay = false;
          if (tracks.length) {
            self.trigger('captions', tracks);
          }
        }
      });

      // Handle playback state changes.
      player.on('playing', () => self.trigger('stateChange', H5P.Video.PLAYING));
      player.on('pause', () => self.trigger('stateChange', H5P.Video.PAUSED));
      player.on('ended', () => self.trigger('stateChange', H5P.Video.ENDED));

      // Track the percentage of video that has finished loading (buffered).
      player.on('progress', (data) => {
        buffered = data.percent * 100;
      });

      // Track the current time. The update frequency may be browser-dependent,
      // according to the official docs:
      // https://developer.vimeo.com/player/sdk/reference#timeupdate
      player.on('timeupdate', (time) => {
        currentTime = time.seconds;
      });
    };

    /**
     * Get metadata about the video loaded in the given Vimeo player.
     *
     * Example resolved value:
     *
     * ```
     * {
     *   "duration": 39,
     *   "qualities": [
     *     {
     *       "name": "auto",
     *       "label": "Auto"
     *     },
     *     {
     *       "name": "1080p",
     *       "label": "1080p"
     *     },
     *     {
     *       "name": "720p",
     *       "label": "720p"
     *     }
     *   ],
     *   "dimensions": {
     *     "width": 1920,
     *     "height": 1080
     *   },
     *   "tracks": {
     *     "current": {
     *       "label": "English",
     *       "value": "en"
     *     },
     *     "options": [
     *       {
     *         "label": "English",
     *         "value": "en"
     *       },
     *       {
     *         "label": "Norsk bokmål",
     *         "value": "nb"
     *       }
     *     ]
     *   }
     * }
     * ```
     *
     * @private
     * @param {Vimeo.Player} player
     * @returns {Promise}
     */
    const getVimeoVideoMetadata = (player) => {
      // Create an object for easy lookup of relevant metadata
      const massageVideoMetadata = (data) => {
        const duration = data[0];
        const qualities = data[1].map(q => ({
          name: q.id,
          label: q.label
        }));
        const tracks = data[2].reduce((tracks, current) => {
          const h5pVideoTrack = new H5P.Video.LabelValue(current.label, current.language);
          tracks.options.push(h5pVideoTrack);
          if (current.mode === 'showing') {
            tracks.current = h5pVideoTrack;
          }
          return tracks;
        }, { current: undefined, options: [] });
        const dimensions = { width: data[3], height: data[4] };

        return {
          duration,
          qualities,
          tracks,
          dimensions
        };
      };

      return Promise.all([
        player.getDuration(),
        player.getQualities(),
        player.getTextTracks(),
        player.getVideoWidth(),
        player.getVideoHeight(),
      ]).then(data => massageVideoMetadata(data));
    }

    try {
      if (document.featurePolicy.allowsFeature('autoplay') === false) {
        self.pressToPlay = true;
      }
    }
    catch (err) {}

    /**
     * Appends the video player to the DOM.
     *
     * @public
     * @param {jQuery} $container
     */
    self.appendTo = ($container) => {
      $container.addClass('h5p-vimeo').append($wrapper);
      createVimeoPlayer();
    };

    /**
     * Get list of available qualities.
     *
     * @public
     * @returns {Array}
     */
    self.getQualities = () => {
      return qualities;
    };

    /**
     * Get the current quality.
     *
     * @returns {String} Current quality identifier
     */
    self.getQuality = () => {
      return currentQuality;
    };

    /**
     * Set the playback quality.
     *
     * @public
     * @param {String} quality
     */
    self.setQuality = async (quality) => {
      currentQuality = await player.setQuality(quality);
      self.trigger('qualityChange', currentQuality);
    };

    /**
     * Start the video.
     *
     * @public
     */
    self.play = async () => {
      if (!player) {
        self.on('ready', self.play);
        return;
      }

      try {
        await player.play();
      }
      catch (error) {
        switch (error.name) {
          case 'PasswordError': // The video is password-protected
            self.trigger('error', l10n.vimeoPasswordError);
            break;

          case 'PrivacyError': // The video is private
            self.trigger('error', l10n.vimeoPrivacyError);
            break;

          default:
            self.trigger('error', l10n.unknownError);
            break;
        }
      }
    };

    /**
     * Pause the video.
     *
     * @public
     */
    self.pause = () => {
      if (player) {
        player.pause();
      }
    };

    /**
     * Seek video to given time.
     *
     * @public
     * @param {Number} time
     */
    self.seek = async (time) => {
      if (!player) {
        return;
      }

      currentTime = time;
      await player.setCurrentTime(time);
    };

    /**
     * @public
     * @returns {Number} Seconds elapsed since beginning of video
     */
    self.getCurrentTime = () => {
      return currentTime;
    };

    /**
     * @public
     * @returns {Number} Video duration in seconds
     */
    self.getDuration = () => {
      return duration;
    };

    /**
     * Get percentage of video that is buffered.
     *
     * @public
     * @returns {Number} Between 0 and 100
     */
    self.getBuffered = () => {
      return buffered;
    };

    /**
     * Mute the video.
     *
     * @public
     */
    self.mute = async () => {
      isMuted = await player.setMuted(true);
    };

    /**
     * Unmute the video.
     *
     * @public
     */
    self.unMute = async () => {
      isMuted = await player.setMuted(false);
    };

    /**
     * Whether the video is muted.
     *
     * @public
     * @returns {Boolean} True if the video is muted, false otherwise
     */
    self.isMuted = () => {
      return isMuted;
    };

    /**
     * Whether the video is loaded.
     *
     * @public
     * @returns {Boolean} True if the video is muted, false otherwise
     */
    self.isLoaded = () => {
      return isLoaded;
    };

    /**
     * Get the video player's current sound volume.
     *
     * @public
     * @returns {Number} Between 0 and 100.
     */
    self.getVolume = () => {
      return volume;
    };

    /**
     * Set the video player's sound volume.
     *
     * @public
     * @param {Number} level
     */
    self.setVolume = async (level) => {
      volume = await player.setVolume(level);
    };

    /**
     * Get list of available playback rates.
     *
     * @public
     * @returns {Array} Available playback rates
     */
    self.getPlaybackRates = () => {
      return [0.5, 1, 1.5, 2];
    };

    /**
     * Get the current playback rate.
     *
     * @public
     * @returns {Number} e.g. 0.5, 1, 1.5 or 2
     */
    self.getPlaybackRate = () => {
      return playbackRate;
    };

    /**
     * Set the current playback rate.
     *
     * @public
     * @param {Number} rate Must be one of available rates from getPlaybackRates
     */
    self.setPlaybackRate = async (rate) => {
      playbackRate = await player.setPlaybackRate(rate);
      self.trigger('playbackRateChange', rate);
    };

    /**
     * Set current captions track.
     *
     * @public
     * @param {H5P.Video.LabelValue} track Captions to display
     */
    self.setCaptionsTrack = (track) => {
      if (!track) {
        return player.disableTextTrack().then(() => {
          currentTextTrack = null;
        });
      }

      player.enableTextTrack(track.value).then(() => {
        currentTextTrack = track;
      });
    };

    /**
     * Get current captions track.
     *
     * @public
     * @returns {H5P.Video.LabelValue}
     */
    self.getCaptionsTrack = () => {
      return currentTextTrack;
    };

    self.on('resize', () => {
      if (failedLoading || !$wrapper.is(':visible')) {
        return;
      }

      if (player === undefined) {
        // Player isn't created yet. Try again.
        createVimeoPlayer();
        return;
      }

      // Use as much space as possible
      $wrapper.css({
        width: '100%',
        height: 'auto'
      });

      const width = $wrapper[0].clientWidth;
      const height = options.fit ? $wrapper[0].clientHeight : (width * (ratio));

      // Validate height before setting
      if (height > 0) {
        // Set size
        $wrapper.css({
          width: width + 'px',
          height: height + 'px'
        });
      }
    });
  }

  /**
   * Check to see if we can play any of the given sources.
   *
   * @public
   * @static
   * @param {Array} sources
   * @returns {Boolean}
   */
  VimeoPlayer.canPlay = (sources) => {
    return getId(sources[0].path);
  };

  /**
   * Find id of Vimeo video from given URL.
   *
   * @private
   * @param {String} url
   * @returns {String} Vimeo video ID
   */
  const getId = (url) => {
    // https://stackoverflow.com/a/11660798
    const matches = url.match(/^.*(vimeo\.com\/)((channels\/[A-z]+\/)|(groups\/[A-z]+\/videos\/))?([0-9]+)/);
    if (matches && matches[5]) {
      return matches[5];
    }
  };

  /**
   * Load the Vimeo Player SDK asynchronously.
   *
   * @private
   * @returns {Promise} Vimeo Player SDK object
   */
  const loadVimeoPlayerSDK = async () => {
    if (window.Vimeo) {
      return await Promise.resolve(window.Vimeo);
    }

    return await new Promise((resolve, reject) => {
      const tag = document.createElement('script');
      tag.src = 'https://player.vimeo.com/api/player.js';
      tag.onload = () => resolve(window.Vimeo);
      tag.onerror = reject;
      document.querySelector('script').before(tag);
    });
  };

  return VimeoPlayer;
})(H5P.jQuery);

// Register video handler
H5P.videoHandlers = H5P.videoHandlers || [];
H5P.videoHandlers.push(H5P.VideoVimeo);
;/** @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 ratio = 9/16;
    var $wrapper = $('<div/>');
    var $placeholder = $('<div />', {
      text: l10n.loading,
      html: `<div style="position: relative; padding: ${ratio*100}% 0 0 0;"><div id="${id}"></div></div>`
    }).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,
          // Hardcoded autoplay to false to avoid playing videos on init
          autoplay: 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: Math.floor(options.startAt),
          playsinline: 1
        },
        events: {
          onReady: function () {
            self.trigger('ready');
            self.trigger('loaded');

            if (!options.autoplay) {
              self.toPause = true;
            }
            
            if (options.deactivateSound) {
              self.mute();
            }
          },
          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) {
              if (self.toPause) {
                // if video buffering, was likely paused already - skip
                if (state.data === H5P.Video.BUFFERING) {
                  delete self.toPause;
                }
                else {
                  self.pause();
                }
              }

              // 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);
          }
        }
      });
      player.g.style = "position:absolute;top:0;left:0;width:100%;height:100%;";
    };

    /**
     * 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
     */
    if (navigator.userAgent.match(/iPad/i)) {
      self.pressToPlay = true;
    }
    else {
      try {
        if (document.featurePolicy.allowsFeature('autoplay') === false) {
          self.pressToPlay = true;
        }
      }
      catch (err) {}
    }

    /**
    * 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 () {
      delete self.toPause;
      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);
    };

    /**
     * Recreate player with initial time
     *
     * @public
     * @param {Number} time
     */
    self.resetPlayback = function (time) {
      options.startAt = time;

      if (player) {
        if (player.getPlayerState() === H5P.Video.PLAYING) {
          player.pauseVideo();
          self.trigger('stateChange', H5P.Video.PAUSED);
        }
        player.destroy();
        player = undefined;
      }

      create();
    }
    /**
     * 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();
    };

    /**
     * Check if video is loaded and ready to play.
     *
     * @public
     * @returns {Boolean}
     */
    self.isLoaded = function () {
      if (!player || !player.getPlayerState) {
        return;
      }

      return player.getPlayerState() === 5;
    };

    /**
     * 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: 'auto'
      });

      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;

    self.volume = 100;
    self.toSeek = undefined;

    var player;
    var playbackRate = 1;
    let canHasAutoplay;
    var id = 'h5p-panopto-' + numInstances;
    numInstances++;
    let isLoaded = false;
    let isPlayerReady = false;

    var $wrapper = $('<div/>');
    var $placeholder = $('<div/>', {
      id: id,
      html: '<div>' + l10n.loading + '</div>'
    }).appendTo($wrapper);

    // Determine autoplay/play.
    try {
      if (document.featurePolicy.allowsFeature('autoplay') !== false) {
        canHasAutoplay = true;
      }
    }
    catch (err) {}

    /**
     * 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: false,
          showbrand: false,
          start: 0,
          hideoverlay: !options.controls,
        },
        events: {
          onIframeReady: function () {
            isPlayerReady = true;
            $placeholder.children(0).text('');
            if (options.autoplay && canHasAutoplay) {
              player.loadVideo();
              isLoaded = true;
            }

            self.trigger('containerLoaded');
            self.trigger('resize'); // Avoid black iframe if loading is slow
          },
          onReady: function () {
            self.videoLoaded = true;
            self.trigger('loaded');

            if (typeof self.oldTime === 'number') {
              self.seek(self.oldTime);
            }
            else if (typeof self.startAt === 'number' && self.startAt > 0) {
              self.seek(self.startAt);
            }
            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);
            }
          },
          onStateChange: function (state) {
            if ([H5P.Video.PLAYING, H5P.Video.PAUSED].includes(state) && typeof self.seekToTime === 'number') {
              player.seekTo(self.seekToTime);
              delete self.seekToTime;
            }
            // since panopto has different load sequence in IV, need additional condition here
            if (self.WAS_RESET) {
              self.WAS_RESET = false;
            }

            // TODO: Playback rate fix for IE11?
            if (state > -1 && state < 4) {
              self.trigger('stateChange', state);
            }
          },
          onPlaybackRateChange: function () {
            self.trigger('playbackRateChange', self.getPlaybackRate());
          },
          onError: function (error) {
            if (error === ApiError.PlayWithSoundNotAllowed) {
              // pause and allow user to handle playing
              self.pause();

              self.unMute(); // because player is automuted on this error
            }
            else {
              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 || !isPlayerReady) {
        return;
      }
      if (isLoaded || self.videoLoaded) {
        player.playVideo();
      }
      else {
        player.loadVideo(); // Loads and starts playing
        isLoaded = true;
      }
    };

    /**
     * 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 || !self.videoLoaded) {
        return;
      }
      if (!player.isReady) {
        self.seekToTime = time;
        return;
      }

      player.seekTo(time);

      if (self.WAS_RESET) {
        // need to check just to be sure, since state === 1 is unusable
        delete self.seekToTime;
        self.WAS_RESET = false;
      }
    };

    /**
     * Recreate player with initial time
     *
     * @public
     * @param {Number} time
     */
    self.resetPlayback = function (time) {
      if (player && player.isReady && self.videoLoaded) {
        self.seek(time);
        self.pause();
      }
      else {
        self.seekToTime = 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();

      // The volume is set to 0 when the browser prevents autoplay,
      // causing there to be no sound despite unmuting
      self.setVolume(self.volume);
    };

    /**
     * Check if video sound is turned on or off.
     *
     * @public
     * @returns {Boolean}
     */
    self.isMuted = function () {
      if (!player || !player.isMuted) {
        return;
      }

      return player.isMuted();
    };

    /**
     * Check video is loaded and ready to play
     *
     * @public
     * @returns {Boolean}
     */
    self.isLoaded = function () {
      return isPlayerReady;
    };

    /**
     * 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);
      self.volume = level;
    };

    /**
     * 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) {
        player.disableCaptions();
        currentTrack = null;
      }
      else {
        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 Echo */
H5P.VideoEchoVideo = (() => {

  let numInstances = 0;

  const CONTROLS_HEIGHT = 100;

  /**
   * EchoVideo 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 EchoPlayer(sources, options, l10n) {
    // State variables for the Player.
    let player = undefined;
    let buffered = 0;
    let currentQuality;
    let trackOptions = [];
    let currentTime = 0;
    let duration = 0;
    let isMuted = false;
    let loadingComplete = false;
    let volume = 1;
    let playbackRate = 1;
    let qualities = [];
    let loadingFailedTimeout;
    let failedLoading = false;
    let ratio = 9 / 16;
    let currentState = H5P.Video.VIDEO_CUED;
    // Echo360 server doesn't sync seek time with regular play time fast enough
    let timelineUpdatesToSkip = 0;
    let timeUpdateTimeout;

    /*
     * Echo360 player does send time updates ~ 0.25 seconds by default and
     * ends playing the video without sending a final time update or an
     * Video Ended event. We take care of determining reaching the video end
     * ourselves.
     */
    const echoMinUncertaintyCompensationS = 0.3;
    const timelineUpdateDeltaSlackMS = 50;
    let echoUncertaintyCompensationS = echoMinUncertaintyCompensationS;
    let previousTickMS;

    // Player specific immutable variables.
    const LOADING_TIMEOUT_IN_SECONDS = 30;
    const id = `h5p-echo-${++numInstances}`;
    const instanceId = H5P.createUUID();
    const wrapperElement = document.createElement('div');

    const placeholderElement = document.createElement('div');
    placeholderElement.classList.add('h5p-video-loading');
    placeholderElement.setAttribute('style', 'height: 100%; min-height: 200px; display: block; z-index: 100; border: none;');
    placeholderElement.setAttribute('aria-label', l10n.loading);

    wrapperElement.setAttribute('id', id);
    wrapperElement.append(placeholderElement);

    /**
     * Remove all elements from the placeholder dom element.
     *
     * @private
     */
    const removeLoadingIndicator = () => {
      placeholderElement.replaceChildren();
    };

    /**
     * Generate an array of objects for use in a dropdown from the list of resolutions.
     * @private
     * @param {Array} qualityLevels - list of objects with supported qualities for the media
     * @returns {Array} list of objects with label and name properties
     */
    const mapQualityLevels = (qualityLevels) => {
      const qualities = qualityLevels.map((quality) => {
        return { label: quality.label.toLowerCase(), name: quality.value };
      });
      return qualities;
    };

    /**
     * Register event listeners on the given Echo player.
     *
     * @private
     * @param {HTMLElement} player
     */
    const registerEchoPlayerEventListeneners = (player) => {
      player.resolveLoading = null;
      player.loadingPromise = new Promise((resolve) => {
        player.resolveLoading = resolve;
      });
      player.onload = async () => {
        clearTimeout(loadingFailedTimeout);
        player.loadingPromise.then(async () => {
          this.trigger('ready');
          this.trigger('loaded');
          this.loadingComplete = true;
          this.trigger('resize');
          if (trackOptions.length) {
            this.trigger('captions', trackOptions);
          }

          const autoplayIsAllowed = !window.H5PEditor &&
            await H5P.Video.isAutoplayAllowed();
          if (options.autoplay && autoplayIsAllowed) {
            this.play();
          }

          return true;
        });
      };
      window.addEventListener('message', (event) => {
        let message = '';

        try {
          message = JSON.parse(event.data);
        }
        catch (e) {
          return;
        }
        if (
          message.context !== 'Echo360' || message.instanceId !== instanceId
        ) {
          return;
        }

        if (message.event === 'init') {
          // Set ratio if width and height is received from Echo360
          if (message.data.width && message.data.height) {
            // If controls are displayed we have to add a magic height to make it visible :(
            ratio = ((message.data.height + (options.controls ? CONTROLS_HEIGHT : 0)) / message.data.width);
          }

          duration = message.data.duration;
          this.setCurrentTime(message.data.currentTime ?? 0);

          textTracks = message.data.textTracks ?? [];
          if (message.data.captions) {
            trackOptions = textTracks.map((track) =>
              new H5P.Video.LabelValue(track.label, track.value)
            );
          }
          player.resolveLoading();

          // Player sends `init` event after rebuffering, unfortunately.
          if (!this.wasInitialized) {
            qualities = mapQualityLevels(message.data.qualityOptions);
            currentQuality = qualities[0].name;
            this.trigger('qualityChange', currentQuality);
          }

          this.trigger('resize');
          if (message.data.playing) {
            changeState(H5P.Video.PLAYING);
          }

          this.wasInitialized = true;
        }
        else if (message.event === 'timeline') {
          updateUncertaintyCompensation();

          duration = message.data.duration ?? this.getDuration();

          if (timelineUpdatesToSkip === 0) {
            this.setCurrentTime(message.data.currentTime ?? 0);
          }
          else {
            timelineUpdatesToSkip--;
          }

          /*
           * Should work, but it was better if the player itself clearly sent
           * the state (playing, paused, ended) instead of us having to infer.
           */
          const compensatedTime = this.getCurrentTime() +
            echoUncertaintyCompensationS * this.getPlaybackRate()

          if (
            currentState === H5P.Video.PLAYING &&
              Math.ceil(compensatedTime) >= duration
          ) {
            changeState(H5P.Video.ENDED);

            if (options.loop) {
              this.seek(0);
              this.play();
            }
            return;
          }

          if (message.data.playing) {
            timeUpdate(currentTime);
            changeState(H5P.Video.PLAYING);
          }
          else if (currentState === H5P.Video.PLAYING) {
            // Condition prevents video to be paused on startup
            changeState(H5P.Video.PAUSED);
            window.clearTimeout(timeUpdateTimeout);
          }
        }
      });
    };

    /**
     * Update the uncertainty compensation value.
     * Computes the delta time between the last two timeline events sent by the
     * Echo360 player and updates the compensation value.
     */
    const updateUncertaintyCompensation = () => {
      if (currentState === H5P.Video.PLAYING) {
        const time = Date.now();

        if (previousTickMS) {
          echoUncertaintyCompensationS = Math.max(
            echoMinUncertaintyCompensationS,
            (time - previousTickMS + timelineUpdateDeltaSlackMS) /
              1000
          )
        } else {
          echoUncertaintyCompensationS = echoMinUncertaintyCompensationS;
        }

        previousTickMS = time;
      }
      else {
        delete previousTickMS;
      }
    }

    /**
     * Change state of the player.
     * @param {number} state State id (H5P.Video[statename]).
     */
    const changeState = (state) => {
      if (state !== currentState) {
        currentState = state;
        this.trigger('stateChange', state);
      }
    };

    /**
     * Determine if the element is visible by computing the styles.
     *
     * @private
     * @param {HTMLElement} node - the element to check.
     * @returns {Boolean} true if it is visible.
     */
    const isNodeVisible = (node) => {
      let style = window.getComputedStyle(node);

      if (node.offsetWidth === 0) {
        return false;
      }

      return ((style.display !== 'none') && (style.visibility !== 'hidden'));
    };

    const timeUpdate = (time) => {
      window.clearTimeout(timeUpdateTimeout);

      this.lastTimeUpdate = Date.now();

      timeUpdateTimeout = window.setTimeout(() => {
        if (currentState !== H5P.Video.PLAYING) {
          return;
        }

        const delta = (Date.now() - this.lastTimeUpdate) * this.getPlaybackRate();
        this.setCurrentTime(currentTime + delta / 1000);
        timeUpdate(currentTime);
      }, 40); // 25 fps
    }

    /**
     * Create a new player by embedding an iframe.
     *
     * @private
     * @returns {Promise}
     */
    const createEchoPlayer = async () => {
      if (!isNodeVisible(placeholderElement) || player !== undefined) {
        return;
      }
      // Since the SDK is loaded asynchronously below, explicitly set player to
      // null (unlike undefined) which indicates that creation has begun. This
      // allows the guard statement above to be hit if this function is called
      // more than once.
      player = null;
      let queryString = '?';

      queryString += `instanceId=${instanceId}&`;

      if (options.controls) {
        queryString += 'controls=true&';
      }
      if (options.disableFullscreen) {
        queryString += 'disableFullscreen=true&';
      }
      if (options.deactivateSound) {
        queryString += 'deactivateSound=true&';
      }
      if (options.startAt) {
        queryString += `startTimeMillis=${Math.round(options.startAt * 1000)}&`;
      }

      wrapperElement.innerHTML = `<iframe src="${sources[0].path}${queryString}" style="display: inline-block; width: 100%; height: 100%;" allow="autoplay; fullscreen" frameborder="0" scrolling="no"></iframe>`;
      player = wrapperElement.firstChild;
      // Create a new player
      registerEchoPlayerEventListeneners(player);
      loadingFailedTimeout = setTimeout(() => {
        failedLoading = true;
        removeLoadingIndicator();
        wrapperElement.innerHTML = `<p class="echo-failed-loading">${l10n.unknownError}</p>`;
        wrapperElement.style.cssText = 'width: null; height: null;';
        this.trigger('resize');
        this.trigger('error', l10n.unknownError);
      }, LOADING_TIMEOUT_IN_SECONDS * 1000);
    };

    /**
     * Appends the video player to the DOM.
     *
     * @public
     * @param {jQuery} $container
     */
    this.appendTo = ($container) => {
      $container.addClass('h5p-echo').append(wrapperElement);
      createEchoPlayer();
    };

    /**
     * Determine if the video has loaded.
     *
     * @public
     * @returns {Boolean}
     */
    this.isLoaded = () => {
      return loadingComplete;
    };

    /**
     * Get list of available qualities.
     *
     * @public
     * @returns {Array}
     */
    this.getQualities = () => {
      return qualities;
    };

    /**
     * Get the current quality.
     *
     * @public
     * @returns {String} Current quality identifier
     */
    this.getQuality = () => {
      return currentQuality;
    };

    /**
     * Set the playback quality.
     *
     * @public
     * @param {String} quality
     */
    this.setQuality = async (quality) => {
      this.post('quality', quality);
      currentQuality = quality;
      this.trigger('qualityChange', currentQuality);
    };

    /**
     * Start the video.
     *
     * @public
     */
    this.play = () => {
      if (!player) {
        this.on('ready', this.play);
        return;
      }
      this.post('play', 0);
    };

    /**
     * Pause the video.
     *
     * @public
     */
    this.pause = () => {
      // Compensate for Echo360's delayed time updates
      timelineUpdatesToSkip = 1;
      this.post('pause', 0);
    };

    /**
     * Seek video to given time.
     *
     * @public
     * @param {Number} time
     */
    this.seek = (time) => {
      this.post('seek', time);
      this.setCurrentTime(time);
      // Compensate for Echo360's delayed time updates
      timelineUpdatesToSkip = 1;
    };

    /**
     * Post a window message to the iframe.
     *
     * @public
     * @param event
     * @param data
     */
    this.post = (event, data) => {
      player?.contentWindow?.postMessage(
        JSON.stringify({
          event: event,
          context: 'Echo360',
          instanceId: instanceId,
          data: data
        }),
        '*'
      );
    };

    /**
     * Return the current play position.
     *
     * @public
     * @returns {Number} Seconds elapsed since beginning of video
     */
    this.getCurrentTime = () => {
      return currentTime;
    };

    /**
     * Set current time.
     * @param {number} timeS Time in seconds.
     */
    this.setCurrentTime = (timeS) => {
      currentTime = timeS;
    }

    /**
     * Return the video duration.
     *
     * @public
     * @returns {?Number} Video duration in seconds
     */
    this.getDuration = () => {
      if (duration > 0) {
        return duration;
      }
      return null;
    };

    /**
     * Get percentage of video that is buffered.
     *
     * @public
     * @returns {Number} Between 0 and 100
     */
    this.getBuffered = () => {
      return buffered;
    };

    /**
     * Mute the video.
     *
     * @public
     */
    this.mute = () => {
      this.post('mute', 0);
      isMuted = true;
    };

    /**
     * Unmute the video.
     *
     * @public
     */
    this.unMute = () => {
      this.post('unmute', 0);
      isMuted = false;
    };

    /**
     * Whether the video is muted.
     *
     * @public
     * @returns {Boolean} True if the video is muted, false otherwise
     */
    this.isMuted = () => {
      return isMuted;
    };

    /**
     * Get the video player's current sound volume.
     *
     * @public
     * @returns {Number} Between 0 and 100.
     */
    this.getVolume = () => {
      return volume;
    };

    /**
     * Set the video player's sound volume.
     *
     * @public
     * @param {Number} level
     */
    this.setVolume = (level) => {
      this.post('volume', level);
      volume = level;
    };

    /**
     * Get list of available playback rates.
     *
     * @public
     * @returns {Array} Available playback rates
     */
    this.getPlaybackRates = () => {
      return [0.25, 0.5, 0.75, 1, 1.25, 1.5, 1.75, 2];
    };

    /**
     * Get the current playback rate.
     *
     * @public
     * @returns {Number} e.g. 0.5, 1, 1.5 or 2
     */
    this.getPlaybackRate = () => {
      return playbackRate;
    };

    /**
     * Set the current playback rate.
     *
     * @public
     * @param {Number} rate Must be one of available rates from getPlaybackRates
     */
    this.setPlaybackRate = async (rate) => {
      const echoRate = parseFloat(rate);
      this.post('playbackrate', echoRate);
      playbackRate = rate;
      this.trigger('playbackRateChange', rate);
    };

    /**
     * Set current captions track.
     *
     * @public
     * @param {H5P.Video.LabelValue} track Captions to display
     */
    this.setCaptionsTrack = (track) => {
      const echoCaption = trackOptions.find(
        (trackItem) => track?.value === trackItem.value
      );

      trackOptions.forEach(trackItem => {
        trackItem.mode = (trackItem === echoCaption) ? 'showing' : 'disabled';
      });

      this.post('captions', echoCaption ? echoCaption.value : 'off');
    };

    /**
     * Get current captions track.
     *
     * @public
     * @returns {H5P.Video.LabelValue|null} Current captions track.
     */
    this.getCaptionsTrack = () => {
      return trackOptions.find(
        (trackItem) => trackItem.mode === 'showing'
      ) ?? null;
    };

    this.on('resize', () => {
      if (failedLoading || !isNodeVisible(wrapperElement)) {
        return;
      }
      if (player === undefined) {
        // Player isn't created yet. Try again.
        createEchoPlayer();
        return;
      }
      // Use as much space as possible
      wrapperElement.style.cssText = 'width: 100%; height: 100%;';
      const width = wrapperElement.clientWidth;
      const height = options.fit ? wrapperElement.clientHeight : (width * (ratio));
      // Validate height before setting
      if (height > 0) {
        // Set size
        wrapperElement.style.cssText = 'width: ' + width + 'px; height: ' + height + 'px;';
      }
    });
  }

  /**
   * Find id of video from given URL.
   *
   * @private
   * @param {String} url
   * @returns {String} Echo video identifier
   */
  const getId = (url) => {
    const matches = url.match(/^[^/]+:\/\/(echo360[^/]+)\/media\/([^/]+)\/h5p.*$/i);
    if (matches && matches.length === 3) {
      return [matches[2], matches[2]];
    }
  };

  /**
   * Check to see if we can play any of the given sources.
   *
   * @public
   * @static
   * @param {Array} sources
   * @returns {Boolean}
   */
  EchoPlayer.canPlay = (sources) => {
    return getId(sources[0].path);
  };
  return EchoPlayer;
})(H5P.jQuery);

// Register video handler
H5P.videoHandlers = H5P.videoHandlers || [];
H5P.videoHandlers.push(H5P.VideoEchoVideo);
;/** @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);
    // Hardcoded autoplay to false to avoid playing videos on init
    video.autoplay = 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 '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;
            }

            if (video.poster) {
              $(video).one('play', function () {
                self.seek(self.getCurrentTime() || options.startAt);
              });
            }
            else {
              self.seek(options.startAt);
            }

            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();

        video.addEventListener('loadeddata', function() {
          video.play();
        }, false);
      }
      else {
        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) {
      video.currentTime = time;
      // Use canplaythrough for IOs devices
      // Use loadedmetadata for all other devices.
      const eventName = navigator.userAgent.match(/iPad|iPod|iPhone/i) ? "canplaythrough" : "loadedmetadata";
      function seekTo() {
        video.currentTime = time;
        video.removeEventListener(eventName, seekTo);
      };

      if (video.readyState === 4) {
        seekTo();
      }
      else {
        video.addEventListener(eventName, seekTo);
      }
    };

    /**
     * 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 video is loaded and ready play.
     *
     * @public
     * @param {Boolean}
     */
    self.isLoaded = function () {
      return isLoaded;
    };
  }

  /**
   * 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.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
   * @param {Object} [extras] Extra parameters.
   */
  function Video(parameters, id, extras = {}) {
    var self = this;
    self.oldTime = extras.previousState?.time;
    self.contentId = id;
    self.WAS_RESET = false;
    self.startAt = parameters.startAt || 0;

    // 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.',
        vimeoPasswordError: 'Password-protected Vimeo videos are not supported.',
        vimeoPrivacyError: 'The Vimeo video cannot be used due to its privacy settings.',
        vimeoLoadingError: 'The Vimeo video could not be loaded.',
        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);
      }
    });

    /**
     * Handle autoplay. If autoplay is disabled, it will still autopause when
     * video is not visible.
     *
     * @param {*} $container
     */
    const handleAutoPlayPause = function ($container) {
      // Keep the current state
      let state;
      self.on('stateChange', function(event) {
        state = event.data;
      });

      // Keep record of autopauses.
      // I.e: we don't wanna autoplay if the user has excplicitly paused.
      self.autoPaused = !self.pressToPlay;

      new IntersectionObserver(function (entries) {
        const entry = entries[0];

        // This video element became visible
        if (entry.isIntersecting) {
          // Autoplay if autoplay is enabled and it was not explicitly
          // paused by a user
          if (parameters.playback.autoplay && self.autoPaused) {
            self.autoPaused = false;
            self.play();
          }
        }
        else if (state !== Video.PAUSED && state !== Video.ENDED) {
          self.autoPaused = true;
          self.pause();
        }
      }, {
        root: null,
        threshold: [0, 1] // Get events when it is shown and hidden
      }).observe($container.get(0));
    };

    /**
     * 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);

        // Avoid autoplaying in authoring tool
        if (window.H5PEditor === undefined) {
          handleAutoPlayPause($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;
    };

    /**
    * @public
    * Get current state for resume support.
    *
    * @returns {object} Current state.
    */
    self.getCurrentState = function () {
      if (self.getCurrentTime) {
        return {
          time: self.getCurrentTime() || self.oldTime,
        };
      }
    };

    /**
     * The two functions below needs to be defined in this base class,
     * since it is used in this class even if no handler was found.
     */
    self.seek = () => {};
    self.pause = () => {};

    /**
    * @public
    * Reset current state (time).
    *
    */
    self.resetTask = function () {
      delete self.oldTime;
      self.resetPlayback(parameters.startAt || 0);
    };

    /**
     * Default implementation of resetPlayback. May be overridden by sub classes.
     *
     * @param {*} startAt
     */
    self.resetPlayback = startAt => {
      self.seek(startAt);
      self.pause();
      self.WAS_RESET = true;
    };

    // Resize the video when we know its aspect ratio
    self.on('loaded', function () {
      self.trigger('resize');

      // reset time if wasn't done immediately
      if (self.WAS_RESET) {
        self.seek(parameters.startAt || 0);
        if (!parameters.playback.autoplay) {
          self.pause();
        }
        self.WAS_RESET = false;
      }

    });

    // 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,
        tracks: tracks,
        disableRemotePlayback: parameters.visuals.disableRemotePlayback === true,
        disableFullscreen: parameters.visuals.disableFullscreen === true,
        deactivateSound: parameters.playback.deactivateSound,
      }
      if (!self.WAS_RESET) {
        options.startAt = self.oldTime !== undefined ? self.oldTime : (parameters.startAt || 0);
      }

      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;
  };

 /**
  * Determine whether video can be autoplayed.
  * @returns {Promise<boolean>} Whether autoplay is allowed.
  */
  Video.isAutoplayAllowed = async () => {
   if (document.featurePolicy?.allowsFeature('autoplay')) {
     return true; // Browser supports `featurePolicy` and can tell directly
   }

   const video = document.createElement('video');

   /*
    * Without a video source, the play Promise will be rejected with an error
    * if it cannot be autoplayed, but not resolve at all if it can be
    * autoplayed. Using a timeout to detect the latter case here.
    */
   const timeoutMs = 50; // If play promise rejects, then within few ms

   const timeoutPromise = new Promise((resolve) => {
     window.setTimeout(() => {
       resolve(true); // Timeout reached, autoplay is allowed
     }, timeoutMs);
   });

   let result;
   try {
     result = (await Promise.race([video.play(), timeoutPromise])) ?? true;
   } catch (error) {
     result = false;
   }

   return result;
 };

  /** @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 || []);
;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);
;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') {
        // 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',
    'H5P.MultiMediaChoice'
  ];

  /**
   * 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);
;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.Tooltip = H5P.Tooltip || function() {};

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', 'audio', '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;
          // Set tooltip (needed by H5P.Tooltip)
          let buttonText = $button.text();
          $button.attr('data-tooltip', buttonText);

          // Use button text as aria label if a specific one isn't provided
          if (!button.ariaLabel) {
            $button.attr('aria-label', buttonText);
          }
          // Remove label
          $button.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);

          // Remove tooltip (used by H5P.Tooltip)
          button.$element.removeAttr('data-tooltip');

          // Remove aria-label if a specific one isn't provided
          if (!button.ariaLabel) {
            button.$element.removeAttr('aria-label');
          }

          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;
    };

    /**
     * An audio player to display above the task.
     *
     * @param {object} params
     */
    self.setAudio = function (params) {
      params.params = params.params || {};

      sections.audio = {
        $element: $('<div/>', {
          'class': 'h5p-question-audio',
        })
      };

      if (disableAutoPlay) {
        params.params.autoplay = false;
      }
      else if (params.params.playerMode === 'transparent') {
        params.params.autoplay = true; // false doesn't make sense for transparent audio
      }

      sections.audio.instance = H5P.newRunnable(params, self.contentId, sections.audio.$element, true);
      // The height value that is set by H5P.Audio is counter-productive here.
      if (sections.audio.instance.audio) {
        sections.audio.instance.audio.style.height = '';
      }

      return self;
    };

    /**
     * Will stop any playback going on in the task.
     */
    self.pause = function () {
      if (sections.video && sections.video.isVisible) {
        sections.video.instance.pause();
      }
      if (sections.audio && sections.audio.isVisible) {
        sections.audio.instance.pause();
      }
    };

    /**
     * Start playback of video
     */
    self.play = function () {
      if (sections.video && sections.video.isVisible) {
        sections.video.instance.play();
      }
      if (sections.audio && sections.audio.isVisible) {
        sections.audio.instance.play();
      }
    };

    /**
     * Disable auto play, useful in editors.
     */
    self.disableAutoPlay = function () {
      disableAutoPlay = true;
    };

    /**
     * Process HTML escaped string for use as attribute value,
     * e.g. for alt text or title attributes.
     *
     * @param {string} value
     * @return {string} WARNING! Do NOT use for innerHTML.
     */
    self.massageAttributeOutput = function (value) {
      const dparser = new DOMParser().parseFromString(value, 'text/html');
      const div = document.createElement('div');
      div.innerHTML = dparser.documentElement.textContent;;
      return div.textContent || div.innerText || '';
    };

    /**
     * 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
     * @param {string} [options.expandImage] Localization strings
     * @param {string} [options.minimizeImage] Localization string

     */
    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 ? '' : self.massageAttributeOutput(options.alt)),
        title: (options.title === undefined ? '' : self.massageAttributeOutput(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;
      }

      const setAriaLabel = () => {
        const ariaLabel = $imgWrap.attr('aria-expanded') === 'true'
          ? options.minimizeImage 
          : options.expandImage;
          
          $imgWrap.attr('aria-label', `${ariaLabel} ${options.alt}`);
        };

      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
              setAriaLabel();
            }
          }).on('keypress', function (event) {
            if (event.which === 32) {
              event.preventDefault(); // Prevent default behaviour; page scroll down
              scaleImage.apply(this); // Space bar pressed
              setAriaLabel();
            }
          });

        setAriaLabel();

        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
     * @param {Object} [extras.contentData] Content data
     * @params {string} [extras.textIfSubmitting] Text to display if submitting
     */
    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();
        }
      };

      const isSubmitting = extras.contentData && extras.contentData.standalone
        && (extras.contentData.isScoringEnabled || extras.contentData.isReportingEnabled);

      if (isSubmitting && extras.textIfSubmitting) {
        text = extras.textIfSubmitting;
      }

      buttons[id] = {
        isTruncated: false,
        text: text,
        isVisible: false,
        ariaLabel: options['aria-label']
      };

      // 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,
        on: {
          click: function (event) {
            handleButtonClick();
            if (isAnchorTag) {
              event.preventDefault();
            }
          }
        }
      }, options));
      buttonOrder.push(id);

      H5P.Tooltip($e.get(0), {tooltipSource: 'data-tooltip'});

      // 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) {
        const parentElement = options.$parentElement.get(0);
        let dialogParent;
        // If using h5p-content, dialog will not appear on embedded fullscreen
        if (parentElement.classList.contains('h5p-content')) {
          dialogParent = parentElement.querySelector('.h5p-container');
        }

        confirmationDialog.appendTo(dialogParent ?? parentElement);
      }
      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);
;H5P.TrueFalse = (function ($, Question) {
  'use strict';

  // Maximum score for True False
  var MAX_SCORE = 1;

  /**
   * Enum containing the different states this content type can exist in
   *
   * @enum
   */
  var State = Object.freeze({
    ONGOING: 1,
    FINISHED_WRONG: 2,
    FINISHED_CORRECT: 3,
    INTERNAL_SOLUTION: 4,
    EXTERNAL_SOLUTION: 5
  });

  /**
   * Button IDs
   */
  var Button = Object.freeze({
    CHECK: 'check-answer',
    TRYAGAIN: 'try-again',
    SHOW_SOLUTION: 'show-solution'
  });

  /**
   * Initialize module.
   *
   * @class H5P.TrueFalse
   * @extends H5P.Question
   * @param {Object} options
   * @param {number} id Content identification
   * @param {Object} contentData Task specific content data
   */
  function TrueFalse(options, id, contentData) {
    var self = this;

    // Inheritance
    Question.call(self, 'true-false');

    var params = $.extend(true, {
      question: 'No question text provided',
      correct: 'true',
      l10n: {
        trueText: 'True',
        falseText: 'False',
        score: 'You got @score of @total points',
        checkAnswer: 'Check',
        submitAnswer: 'Submit',
        showSolutionButton: 'Show solution',
        tryAgain: 'Retry',
        wrongAnswerMessage: 'Wrong answer',
        correctAnswerMessage: 'Correct answer',
        scoreBarLabel: 'You got :num out of :total points',
        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,
        confirmCheckDialog: false,
        confirmRetryDialog: false,
        autoCheck: false
      }
    }, options);

    // Counter used to create unique id for this question
    TrueFalse.counter = (TrueFalse.counter === undefined ? 0 : TrueFalse.counter + 1);

    // A unique ID is needed for aria label
    var domId = 'h5p-tfq' + H5P.TrueFalse.counter;

    // saves the content id
    this.contentId = id;
    this.contentData = contentData;

    // The radio group
    var answerGroup = new H5P.TrueFalse.AnswerGroup(domId, params.correct, params.l10n);
    if (contentData.previousState !== undefined && contentData.previousState.answer !== undefined) {
      answerGroup.check(contentData.previousState.answer);
    }
    answerGroup.on('selected', function () {
      self.triggerXAPI('interacted');

      if (params.behaviour.autoCheck) {
        checkAnswer();
        triggerXAPIAnswered();
      }
    });


    /**
     * Create the answers
     *
     * @method createAnswers
     * @private
     * @return {H5P.jQuery}
     */
    var createAnswers = function () {
      return answerGroup.getDomElement();
    };

    /**
     * Register buttons
     *
     * @method registerButtons
     * @private
     */
    var registerButtons = 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
      if (params.behaviour.enableSolutionsButton === true) {
        self.addButton(Button.SHOW_SOLUTION, params.l10n.showSolutionButton, function () {
          self.showSolutions(true);
        }, false, {
          'aria-label': params.l10n.a11yShowSolution,
        });
      }

      // Check button
      if (!params.behaviour.autoCheck && params.behaviour.enableCheckButton) {
        self.addButton(Button.CHECK, params.l10n.checkAnswer, function () {
          checkAnswer();
          triggerXAPIAnswered();
        }, true, {
          'aria-label': params.l10n.a11yCheck
        }, {
          confirmationDialog: {
            enable: params.behaviour.confirmCheckDialog,
            l10n: params.confirmCheck,
            instance: self,
            $parentElement: $container
          },
          contentData: self.contentData,
          textIfSubmitting: params.l10n.submitAnswer,
        });
      }

      // Try again button
      if (params.behaviour.enableRetry === true) {
        self.addButton(Button.TRYAGAIN, params.l10n.tryAgain, function () {
          self.resetTask();
        }, true, {
          'aria-label': params.l10n.a11yRetry,
        }, {
          confirmationDialog: {
            enable: params.behaviour.confirmRetryDialog,
            l10n: params.confirmRetry,
            instance: self,
            $parentElement: $container
          }
        });
      }

      toggleButtonState(State.ONGOING);
    };

    /**
     * Creates and triggers the xAPI answered event
     *
     * @method triggerXAPIAnswered
     * @private
     * @fires xAPIEvent
     */
    var triggerXAPIAnswered = function () {
      var xAPIEvent = self.createXAPIEventTemplate('answered');
      addQuestionToXAPI(xAPIEvent);
      addResponseToXAPI(xAPIEvent);
      self.trigger(xAPIEvent);
    };

    /**
     * Add the question itself to the definition part of an xAPIEvent
     *
     * @method addQuestionToXAPI
     * @param {XAPIEvent} xAPIEvent
     * @private
     */
    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 = 'true-false';
      definition.correctResponsesPattern = [getCorrectAnswer()];
    };

    /**
     * Returns the correct answer
     *
     * @method getCorrectAnswer
     * @private
     * @return {String}
     */
    var getCorrectAnswer = function () {
      return (params.correct === 'true' ? 'true' : 'false');
    };

    /**
     * Returns the wrong answer
     *
     * @method getWrongAnswer
     * @private
     * @return {String}
     */
    var getWrongAnswer = function () {
      return (params.correct === 'false' ? 'true' : 'false');
    };

    /**
     * Add the response part to an xAPI event
     *
     * @method addResponseToXAPI
     * @private
     * @param {H5P.XAPIEvent} xAPIEvent
     *  The xAPI event we will add a response to
     */
    var addResponseToXAPI = function(xAPIEvent) {
      var isCorrect = answerGroup.isCorrect();
      xAPIEvent.setScoredResult(isCorrect ? MAX_SCORE : 0, MAX_SCORE, self, true, isCorrect);
      xAPIEvent.data.statement.result.response = (isCorrect ? getCorrectAnswer() : getWrongAnswer());
    };

    /**
     * Toggles btton visibility dependent of current state
     *
     * @method toggleButtonVisibility
     * @private
     * @param  {String}    buttonId
     * @param  {Boolean}   visible
     */
    var toggleButtonVisibility = function (buttonId, visible) {
      if (visible === true) {
        self.showButton(buttonId);
      }
      else {
        self.hideButton(buttonId);
      }
    };

    /**
     * Toggles buttons state
     *
     * @method toggleButtonState
     * @private
     * @param  {String}          state
     */
    var toggleButtonState = function (state) {
      toggleButtonVisibility(Button.SHOW_SOLUTION, state === State.FINISHED_WRONG);
      toggleButtonVisibility(Button.CHECK, state === State.ONGOING);
      toggleButtonVisibility(Button.TRYAGAIN, state === State.FINISHED_WRONG || state === State.INTERNAL_SOLUTION);
    };

    /**
     * Check if answer is correct or wrong, and update visuals accordingly
     *
     * @method checkAnswer
     * @private
     */
    var checkAnswer = function () {
      // Create feedback widget
      var score = self.getScore();
      var scoreText;

      toggleButtonState(score === MAX_SCORE ? State.FINISHED_CORRECT : State.FINISHED_WRONG);

      if (score === MAX_SCORE && params.behaviour.feedbackOnCorrect) {
        scoreText = params.behaviour.feedbackOnCorrect;
      }
      else if (score === 0 && params.behaviour.feedbackOnWrong) {
        scoreText = params.behaviour.feedbackOnWrong;
      }
      else {
        scoreText = params.l10n.score;
      }
      // Replace relevant variables:
      scoreText = scoreText.replace('@score', score).replace('@total', MAX_SCORE);
      self.setFeedback(scoreText, score, MAX_SCORE, params.l10n.scoreBarLabel);
      answerGroup.reveal();
    };

    /**
     * Registers this question type's DOM elements before they are attached.
     * Called from H5P.Question.
     *
     * @method registerDomElements
     * @private
     */
    self.registerDomElements = function () {
      var self = this;

      // Check for task media
      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,
              expandImage: media.params.expandImage,
              minimizeImage: media.params.minimizeImage
            });
          }
        }
        else if (type === 'H5P.Video') {
          if (media.params.sources) {
            // Register task video
            self.setVideo(media);
          }
        }
        else if (type === 'H5P.Audio') {
          if (media.params.files) {
            // Register task audio
            self.setAudio(media);
          }
        }
      }

      // Add task question text
      self.setIntroduction('<div id="' + domId + '">' + params.question + '</div>');

      // Register task content area
      self.$content = createAnswers();
      self.setContent(self.$content);

      // ... and buttons
      registerButtons();
    };

    /**
     * Implements resume (save content state)
     *
     * @method getCurrentState
     * @public
     * @returns {object} object containing answer
     */
    self.getCurrentState = function () {
      return {answer: answerGroup.getAnswer()};
    };

    /**
     * Used for contracts.
     * Checks if the parent program can proceed. Always true.
     *
     * @method getAnswerGiven
     * @public
     * @returns {Boolean} true
     */
    self.getAnswerGiven = function () {
      return answerGroup.hasAnswered();
    };

    /**
     * Used for contracts.
     * Checks the current score for this task.
     *
     * @method getScore
     * @public
     * @returns {Number} The current score.
     */
    self.getScore = function () {
      return answerGroup.isCorrect() ? MAX_SCORE : 0;
    };

    /**
     * Used for contracts.
     * Checks the maximum score for this task.
     *
     * @method getMaxScore
     * @public
     * @returns {Number} The maximum score.
     */
    self.getMaxScore = function () {
      return MAX_SCORE;
    };

    /**
     * Get title of task
     *
     * @method getTitle
     * @public
     * @returns {string} title
     */
    self.getTitle = function () {
      return H5P.createTitle((self.contentData && self.contentData.metadata && self.contentData.metadata.title) ? self.contentData.metadata.title : 'True-False');
    };

    /**
     * Used for contracts.
     * Show the solution.
     *
     * @method showSolutions
     * @public
     */
    self.showSolutions = function (internal) {
      checkAnswer();
      answerGroup.showSolution();
      toggleButtonState(internal ? State.INTERNAL_SOLUTION : State.EXTERNAL_SOLUTION);
    };

    /**
     * Used for contracts.
     * Resets the complete task back to its' initial state.
     *
     * @method resetTask
     * @public
     */
    self.resetTask = function () {
      answerGroup.reset();
      self.removeFeedback();
      toggleButtonState(State.ONGOING);
    };

    /**
     * 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 = this.createXAPIEventTemplate('answered');
      this.addQuestionToXAPI(xAPIEvent);
      this.addResponseToXAPI(xAPIEvent);
      return {
        statement: xAPIEvent.data.statement
      };
    };

    /**
     * Add the question itself to the definition part of an xAPIEvent
     */
    self.addQuestionToXAPI = function(xAPIEvent) {
      var definition = xAPIEvent.getVerifiedStatementValue(['object', 'definition']);
      $.extend(definition, this.getxAPIDefinition());
    };

    /**
     * Generate xAPI object definition used in xAPI statements.
     * @return {Object}
     */
    self.getxAPIDefinition = function () {
      var definition = {};
      definition.interactionType = 'true-false';
      definition.type = 'http://adlnet.gov/expapi/activities/cmi.interaction';
      definition.description = {
        'en-US': $('<div>' + params.question + '</div>').text()
      };
      definition.correctResponsesPattern = [getCorrectAnswer()];

      return definition;
    };

    /**
     * Add the response part to an xAPI event
     *
     * @param {H5P.XAPIEvent} xAPIEvent
     *  The xAPI event we will add a response to
     */
    self.addResponseToXAPI = function (xAPIEvent) {
      var isCorrect = answerGroup.isCorrect();
      var rawUserScore = isCorrect ? MAX_SCORE : 0;
      var currentResponse = '';

      xAPIEvent.setScoredResult(rawUserScore, MAX_SCORE, self, true, isCorrect);

      if(self.getCurrentState().answer !== undefined) {
        currentResponse += answerGroup.isCorrect() ? getCorrectAnswer() : getWrongAnswer();
      }
      xAPIEvent.data.statement.result.response = currentResponse;
    };
   }

  // Inheritance
  TrueFalse.prototype = Object.create(Question.prototype);
  TrueFalse.prototype.constructor = TrueFalse;

  return TrueFalse;
})(H5P.jQuery, H5P.Question);
;H5P.TrueFalse.AnswerGroup = (function ($, EventDispatcher) {
  'use strict';

  /**
   * Initialize module.
   *
   * @class H5P.TrueFalse.AnswerGroup
   * @extends H5P.EventDispatcher
   * @param {String} domId Id for label
   * @param {String} correctOption Correct option ('true' or 'false')
   * @param {Object} l10n Object containing all interface translations
   */
  function AnswerGroup(domId, correctOption, l10n) {
    var self = this;

    EventDispatcher.call(self);

    var $answers = $('<div>', {
      'class': 'h5p-true-false-answers',
      role: 'radiogroup',
      'aria-labelledby': domId
    });

    var answer;
    var trueAnswer = new H5P.TrueFalse.Answer(l10n.trueText, l10n.correctAnswerMessage, l10n.wrongAnswerMessage);
    var falseAnswer = new H5P.TrueFalse.Answer(l10n.falseText, l10n.correctAnswerMessage, l10n.wrongAnswerMessage);
    var correctAnswer = (correctOption === 'true' ? trueAnswer : falseAnswer);
    var wrongAnswer = (correctOption === 'false' ? trueAnswer : falseAnswer);

    // Handle checked
    var handleChecked = function (newAnswer, other) {
      return function () {
        answer = newAnswer;
        other.uncheck();
        self.trigger('selected');
      };
    };
    trueAnswer.on('checked', handleChecked(true, falseAnswer));
    falseAnswer.on('checked', handleChecked(false, trueAnswer));

    // Handle switches (using arrow keys)
    var handleInvert = function (newAnswer, other) {
      return function () {
        answer = newAnswer;
        other.check();
        self.trigger('selected');
      };
    };
    trueAnswer.on('invert', handleInvert(false, falseAnswer));
    falseAnswer.on('invert', handleInvert(true, trueAnswer));

    // Handle tabbing
    var handleTabable = function(other, tabable) {
      return function () {
        // If one of them are checked, that one should get tabfocus
        if (!tabable || !self.hasAnswered() || other.isChecked()) {
          other.tabable(tabable);
        }
      };
    };
    // Need to remove tabIndex on the other alternative on focus
    trueAnswer.on('focus', handleTabable(falseAnswer, false));
    falseAnswer.on('focus', handleTabable(trueAnswer, false));
    // Need to make both alternatives tabable on blur:
    trueAnswer.on('blur', handleTabable(falseAnswer, true));
    falseAnswer.on('blur', handleTabable(trueAnswer, true));

    $answers.append(trueAnswer.getDomElement());
    $answers.append(falseAnswer.getDomElement());

    /**
     * Get hold of the DOM element representing this thingy
     * @method getDomElement
     * @return {jQuery}
     */
    self.getDomElement = function () {
      return $answers;
    };

    /**
     * Programatic check
     * @method check
     * @param  {[type]} answer [description]
     */
    self.check = function (answer) {
      if (answer) {
        trueAnswer.check();
      }
      else {
        falseAnswer.check();
      }
    };

    /**
     * Return current answer
     * @method getAnswer
     * @return {Boolean} undefined if no answer if given
     */
    self.getAnswer = function () {
      return answer;
    };

    /**
     * Check if user has answered question yet
     * @method hasAnswered
     * @return {Boolean}
     */
    self.hasAnswered = function () {
      return answer !== undefined;
    };

    /**
     * Is answer correct?
     * @method isCorrect
     * @return {Boolean}
     */
    self.isCorrect = function () {
      return correctAnswer.isChecked();
    };

    /**
     * Enable user input
     *
     * @method enable
     */
    self.enable = function () {
      trueAnswer.enable().tabable(true);
      falseAnswer.enable();
    };

    /**
     * Disable user input
     *
     * @method disable
     */
    self.disable = function () {
      trueAnswer.disable();
      falseAnswer.disable();
    };

    /**
     * Reveal correct/wrong answer
     *
     * @method reveal
     */
    self.reveal = function () {
      if (self.hasAnswered()) {
        if (self.isCorrect()) {
          correctAnswer.markCorrect();
        }
        else {
          wrongAnswer.markWrong();
        }
      }

      self.disable();
    };

    /**
     * Reset task
     * @method reset
     */
    self.reset = function () {
      trueAnswer.reset();
      falseAnswer.reset();
      self.enable();
      answer = undefined;
    };

    /**
     * Show the solution
     * @method showSolution
     * @return {[type]}
     */
    self.showSolution = function () {
      correctAnswer.markCorrect();
      wrongAnswer.unmark();
    };
  }

  // Inheritance
  AnswerGroup.prototype = Object.create(EventDispatcher.prototype);
  AnswerGroup.prototype.constructor = AnswerGroup;

  return AnswerGroup;
})(H5P.jQuery, H5P.EventDispatcher);
;H5P.TrueFalse.Answer = (function ($, EventDispatcher) {
  'use strict';

  var Keys = {
    ENTER: 13,
    SPACE: 32,
    LEFT_ARROW: 37,
    UP_ARROW: 38,
    RIGHT_ARROW: 39,
    DOWN_ARROW: 40
  };

  /**
   * Initialize module.
   *
   * @class H5P.TrueFalse.Answer
   * @extends H5P.EventDispatcher
   * @param {String} text Label
   * @param {String} correctMessage Message read by readspeaker when correct alternative is chosen
   * @param {String} wrongMessage Message read by readspeaker when wrong alternative is chosen
   */
  function Answer (text, correctMessage, wrongMessage) {
    var self = this;

    EventDispatcher.call(self);

    var checked = false;
    var enabled = true;

    var $answer = $('<div>', {
      'class': 'h5p-true-false-answer',
      role: 'radio',
      'aria-checked': false,
      html: text + '<span class="aria-label"></span>',
      tabindex: 0, // Tabable by default
      click: function (event) {
        // Handle left mouse (or tap on touch devices)
        if (event.which === 1) {
          self.check();
        }
      },
      keydown: function (event) {
        if (!enabled) {
          return;
        }
        if ([Keys.SPACE, Keys.ENTER].indexOf(event.keyCode) !== -1) {
          event.preventDefault();
          self.check();
        }
        else if ([Keys.LEFT_ARROW, Keys.UP_ARROW, Keys.RIGHT_ARROW, Keys.DOWN_ARROW].indexOf(event.keyCode) !== -1) {
          event.preventDefault();
          self.uncheck();
          self.trigger('invert');
        }
      },
      focus: function () {
        self.trigger('focus');
      },
      blur: function () {
        self.trigger('blur');
      }
    });

    var $ariaLabel = $answer.find('.aria-label');

    // A bug in Chrome 54 makes the :after icons (V and X) not beeing rendered.
    // Doing this in a timeout solves this
    // Might be removed when Chrome 56 is out
    var chromeBugFixer = function (callback) {
      setTimeout(function () {
        callback();
      }, 0);
    };

    /**
     * Return the dom element representing the alternative
     *
     * @public
     * @method getDomElement
     * @return {H5P.jQuery}
     */
    self.getDomElement = function () {
      return $answer;
    };

    /**
     * Unchecks the alternative
     *
     * @public
     * @method uncheck
     * @return {H5P.TrueFalse.Answer}
     */
    self.uncheck = function () {
      if (enabled) {
        $answer.blur();
        checked = false;
        chromeBugFixer(function () {
          $answer.attr('aria-checked', checked);
        });
      }
      return self;
    };

    /**
     * Set tabable or not
     * @method tabable
     * @param  {Boolean} enabled
     * @return {H5P.TrueFalse.Answer}
     */
    self.tabable = function (enabled) {
      $answer.attr('tabIndex', enabled ? 0 : null);
      return self;
    };

    /**
     * Checks the alternative
     *
     * @method check
     * @return {H5P.TrueFalse.Answer}
     */
    self.check = function () {
      if (enabled) {
        checked = true;
        chromeBugFixer(function () {
          $answer.attr('aria-checked', checked);
        });
        self.trigger('checked');
        $answer.focus();
      }
      return self;
    };

    /**
     * Is this alternative checked?
     *
     * @method isChecked
     * @return {boolean}
     */
    self.isChecked = function () {
      return checked;
    };

    /**
     * Enable alternative
     *
     * @method enable
     * @return {H5P.TrueFalse.Answer}
     */
    self.enable = function () {
      $answer.attr({
        'aria-disabled': '',
        tabIndex: 0
      });
      enabled = true;

      return self;
    };

    /**
     * Disables alternative
     *
     * @method disable
     * @return {H5P.TrueFalse.Answer}
     */
    self.disable = function () {
      $answer.attr({
        'aria-disabled': true,
        tabIndex: null
      });
      enabled = false;

      return self;
    };

    /**
     * Reset alternative
     *
     * @method reset
     * @return {H5P.TrueFalse.Answer}
     */
    self.reset = function () {
      self.enable();
      self.uncheck();
      self.unmark();
      $ariaLabel.html('');

      return self;
    };

    /**
     * Marks this alternative as the wrong one
     *
     * @method markWrong
     * @return {H5P.TrueFalse.Answer}
     */
    self.markWrong = function () {
      chromeBugFixer(function () {
        $answer.addClass('wrong');
      });
      $ariaLabel.html('.' + wrongMessage);

      return self;
    };

    /**
     * Marks this alternative as the wrong one
     *
     * @method markCorrect
     * @return {H5P.TrueFalse.Answer}
     */
    self.markCorrect = function () {
      chromeBugFixer(function () {
        $answer.addClass('correct');
      });
      $ariaLabel.html('.' + correctMessage);

      return self;
    };

    self.unmark = function () {
      chromeBugFixer(function () {
        $answer.removeClass('wrong correct');
      });

      return self;
    };
  }

  // Inheritance
  Answer.prototype = Object.create(EventDispatcher.prototype);
  Answer.prototype.constructor = Answer;

  return Answer;

})(H5P.jQuery, H5P.EventDispatcher);
;/**
 * @class
 * @classdesc Keyboard navigation for accessibility support
 * @extends H5P.EventDispatcher
 */
H5P.KeyboardNav = (function (EventDispatcher) {
  /**
   * Construct a new KeyboardNav
   * @constructor
   */
  function KeyboardNav() {
    EventDispatcher.call(this);

    /** @member {boolean} */
    this.selectability = true;

    /** @member {HTMLElement[]|EventTarget[]} */
    this.elements = [];
  }

  KeyboardNav.prototype = Object.create(EventDispatcher.prototype);
  KeyboardNav.prototype.constructor = KeyboardNav;

  /**
   * Adds a new element to navigation
   *
   * @param {HTMLElement} el The element
   * @public
   */
  KeyboardNav.prototype.addElement = function(el){
    const keyDown = this.handleKeyDown.bind(this);
    const onClick = this.onClick.bind(this);
    el.addEventListener('keydown', keyDown);
    el.addEventListener('click', onClick);

    // add to array to navigate over
    this.elements.push({
      el: el,
      keyDown: keyDown,
      onClick: onClick,
    });

    if(this.elements.length === 1){ // if first
      this.setTabbableAt(0);
    }
  };

  /**
   * Select the previous element in the list. Select the last element,
   * if the current element is the first element in the list.
   *
   * @param {Number} index The index of currently selected element
   * @public
   * @fires KeyboardNav#previousOption
   */
  KeyboardNav.prototype.previousOption = function (index) {
    var isFirstElement = index === 0;
    if (isFirstElement) {
      return;
    }

    this.focusOnElementAt(isFirstElement ? (this.elements.length - 1) : (index - 1));

    /**
     * Previous option event
     *
     * @event KeyboardNav#previousOption
     * @type KeyboardNavigationEventData
     */
    this.trigger('previousOption', this.createEventPayload(index));
  };


  /**
   * Select the next element in the list. Select the first element,
   * if the current element is the first element in the list.
   *
   * @param {Number} index The index of the currently selected element
   * @public
   * @fires KeyboardNav#previousOption
   */
  KeyboardNav.prototype.nextOption = function (index) {
    var isLastElement = index === this.elements.length - 1;
    if (isLastElement) {
      return;
    }

    this.focusOnElementAt(isLastElement ? 0 : (index + 1));

    /**
     * Previous option event
     *
     * @event KeyboardNav#nextOption
     * @type KeyboardNavigationEventData
     */
    this.trigger('nextOption', this.createEventPayload(index));
  };

  /**
   * Focus on an element by index
   *
   * @param {Number} index The index of the element to focus on
   * @public
   */
  KeyboardNav.prototype.focusOnElementAt = function (index) {
    this.setTabbableAt(index);
    this.getElements()[index].focus();
  };

  /**
   * Disable possibility to select a word trough click and space or enter
   *
   * @public
   */
  KeyboardNav.prototype.disableSelectability = function () {
    this.elements.forEach(function (el) {
      el.el.removeEventListener('keydown', el.keyDown);
      el.el.removeEventListener('click', el.onClick);
    }.bind(this));
    this.selectability = false;
  };

  /**
   * Enable possibility to select a word trough click and space or enter
   *
   * @public
   */
  KeyboardNav.prototype.enableSelectability = function () {
    this.elements.forEach(function (el) {
      el.el.addEventListener('keydown', el.keyDown);
      el.el.addEventListener('click', el.onClick);
    }.bind(this));
    this.selectability = true;
  };

  /**
   * Sets tabbable on a single element in the list, by index
   * Also removes tabbable from all other elements in the list
   *
   * @param {Number} index The index of the element to set tabbale on
   * @public
   */
  KeyboardNav.prototype.setTabbableAt = function (index) {
    this.removeAllTabbable();
    this.getElements()[index].setAttribute('tabindex', '0');
  };

  /**
   * Remove tabbable from all entries
   *
   * @public
   */
  KeyboardNav.prototype.removeAllTabbable = function () {
    this.elements.forEach(function(el){
      el.el.removeAttribute('tabindex');
    });
  };

  /**
   * Toggles 'aria-selected' on an element, if selectability == true
   *
   * @param {EventTarget|HTMLElement} el The element to select/unselect
   * @private
   * @fires KeyboardNav#select
   */
  KeyboardNav.prototype.toggleSelect = function(el){
    if(this.selectability) {

      // toggle selection
      el.setAttribute('aria-selected', !isElementSelected(el));

      // focus current
      el.setAttribute('tabindex', '0');
      el.focus();

      var index = this.getElements().indexOf(el);

      /**
       * Previous option event
       *
       * @event KeyboardNav#select
       * @type KeyboardNavigationEventData
       */
      this.trigger('select', this.createEventPayload(index));
    }
  };

  /**
   * Handles key down
   *
   * @param {KeyboardEvent} event Keyboard event
   * @private
   */
  KeyboardNav.prototype.handleKeyDown = function(event){
    var index;

    switch (event.which) {
      case 13: // Enter
      case 32: // Space
        // Select
        this.toggleSelect(event.target);
        event.preventDefault();
        break;

      case 37: // Left Arrow
      case 38: // Up Arrow
        // Go to previous Option
        index = this.getElements().indexOf(event.currentTarget);
        this.previousOption(index);
        event.preventDefault();
        break;

      case 39: // Right Arrow
      case 40: // Down Arrow
        // Go to next Option
        index = this.getElements().indexOf(event.currentTarget);
        this.nextOption(index);
        event.preventDefault();
        break;
    }
  };

  /**
   * Get only elements from elements array
   * @returns {Array}
   */
  KeyboardNav.prototype.getElements = function () {
    return this.elements.map(function (el) {
      return el.el;
    });
  };

  /**
   * Handles element click. Toggles 'aria-selected' on element
   *
   * @param {MouseEvent} event Mouse click event
   * @private
   */
  KeyboardNav.prototype.onClick = function(event){
    this.toggleSelect(event.currentTarget);
  };

  /**
   * Creates a paylod for event that is fired
   *
   * @param {Number} index
   * @return {KeyboardNavigationEventData}
   */
  KeyboardNav.prototype.createEventPayload = function(index){
    /**
     * Data that is passed along as the event parameter
     *
     * @typedef {Object} KeyboardNavigationEventData
     * @property {HTMLElement} element
     * @property {number} index
     * @property {boolean} selected
     */
    return {
      element: this.getElements()[index],
      index: index,
      selected: isElementSelected(this.getElements()[index])
    };
  };

  /**
   * Sets aria-selected="true" on an element
   *
   * @param {HTMLElement} el The element to set selected
   * @return {boolean}
   */
  var isElementSelected = function(el){
    return el.getAttribute('aria-selected') === 'true';
  };

  return KeyboardNav;
})(H5P.EventDispatcher);
;H5P.MarkTheWords = H5P.MarkTheWords || {};

/**
 * Mark the words XapiGenerator
 */
H5P.MarkTheWords.XapiGenerator = (function ($) {

  /**
   * Xapi statements Generator
   * @param {H5P.MarkTheWords} markTheWords
   * @constructor
   */
  function XapiGenerator(markTheWords) {

    /**
     * Generate answered event
     * @return {H5P.XAPIEvent}
     */
    this.generateAnsweredEvent = function () {
      var xAPIEvent = markTheWords.createXAPIEventTemplate('answered');

      // Extend definition
      var objectDefinition = createDefinition(markTheWords);
      $.extend(true, xAPIEvent.getVerifiedStatementValue(['object', 'definition']), objectDefinition);

      // Set score
      xAPIEvent.setScoredResult(markTheWords.getScore(),
        markTheWords.getMaxScore(),
        markTheWords,
        true,
        markTheWords.getScore() === markTheWords.getMaxScore()
      );

      // Extend user result
      var userResult = {
        response: getUserSelections(markTheWords)
      };

      $.extend(xAPIEvent.getVerifiedStatementValue(['result']), userResult);

      return xAPIEvent;
    };
  }

  /**
   * Create object definition for question
   *
   * @param {H5P.MarkTheWords} markTheWords
   * @return {Object} Object definition
   */
  function createDefinition(markTheWords) {
    var definition = {};
    definition.description = {
      'en-US': markTheWords.params.taskDescription,
    };
    definition.type = 'http://adlnet.gov/expapi/activities/cmi.interaction';
    definition.interactionType = 'choice';
    definition.correctResponsesPattern = [getCorrectResponsesPattern(markTheWords)];
    definition.choices = getChoices(markTheWords);
    definition.extensions = {
      'https://h5p.org/x-api/line-breaks': markTheWords.getIndexesOfLineBreaks()
    };
    return definition;
  }

  /**
   * Get all choices that it is possible to choose between
   *
   * @param {H5P.MarkTheWords} markTheWords
   * @return {Array}
   */
  function getChoices(markTheWords) {
    return markTheWords.selectableWords.map(function (word, index) {
      var text = word.getText();
      if (text.charAt(0) === '*' && text.charAt(text.length - 1) === '*') {
        text = text.substr(1, text.length - 2);
      }

      return {
        id: index.toString(),
        description: {
          'en-US': $('<div>' + text + '</div>').text()
        }
      };
    });
  }

  /**
   * Get selected words as a user response pattern
   *
   * @param {H5P.MarkTheWords} markTheWords
   * @return {string}
   */
  function getUserSelections(markTheWords) {
    return markTheWords.selectableWords
      .reduce(function (prev, word, index) {
        if (word.isSelected()) {
          prev.push(index);
        }
        return prev;
      }, []).join('[,]');
  }

  /**
   * Get correct response pattern from correct words
   *
   * @param {H5P.MarkTheWords} markTheWords
   * @return {string}
   */
  function getCorrectResponsesPattern(markTheWords) {
    return markTheWords.selectableWords
      .reduce(function (prev, word, index) {
        if (word.isAnswer()) {
          prev.push(index);
        }
        return prev;
      }, []).join('[,]');
  }

  return XapiGenerator;
})(H5P.jQuery);
;H5P.MarkTheWords = H5P.MarkTheWords || {};
H5P.MarkTheWords.Word = (function () {
  /**
   * @constant
   *
   * @type {string}
  */
  Word.ID_MARK_MISSED = "h5p-description-missed";
  /**
   * @constant
   *
   * @type {string}
   */
  Word.ID_MARK_CORRECT = "h5p-description-correct";
  /**
   * @constant
   *
   * @type {string}
   */
  Word.ID_MARK_INCORRECT = "h5p-description-incorrect";

  /**
   * Class for keeping track of selectable words.
   *
   * @class
   * @param {jQuery} $word
   */
  function Word($word, params) {
    var self = this;
    self.params = params;
    H5P.EventDispatcher.call(self);

    var input = $word.text();
    var handledInput = input;

    // Check if word is an answer
    var isAnswer = checkForAnswer();

    // Remove single asterisk and escape double asterisks.
    handleAsterisks();

    if (isAnswer) {
      $word.text(handledInput);
    }

    const ariaText = document.createElement('span');
    ariaText.classList.add('hidden-but-read');
    $word[0].appendChild(ariaText);

    /**
     * Checks if the word is an answer by checking the first, second to last and last character of the word.
     *
     * @private
     * @return {Boolean} Returns true if the word is an answer.
     */
    function checkForAnswer() {
      // Check last and next to last character, in case of punctuations.
      var wordString = removeDoubleAsterisks(input);
      if (wordString.charAt(0) === ('*') && wordString.length > 2) {
        if (wordString.charAt(wordString.length - 1) === ('*')) {
          handledInput = input.slice(1, input.length - 1);
          return true;
        }
        // If punctuation, add the punctuation to the end of the word.
        else if(wordString.charAt(wordString.length - 2) === ('*')) {
          handledInput = input.slice(1, input.length - 2);
          return true;
        }
        return false;
      }
      return false;
    }

    /**
     * Removes double asterisks from string, used to handle input.
     *
     * @private
     * @param {String} wordString The string which will be handled.
     * @return {String} Returns a string without double asterisks.
     */
    function removeDoubleAsterisks(wordString) {
      var asteriskIndex = wordString.indexOf('*');
      var slicedWord = wordString;

      while (asteriskIndex !== -1) {
        if (wordString.indexOf('*', asteriskIndex + 1) === asteriskIndex + 1) {
          slicedWord = wordString.slice(0, asteriskIndex) + wordString.slice(asteriskIndex + 2, input.length);
        }
        asteriskIndex = wordString.indexOf('*', asteriskIndex + 1);
      }

      return slicedWord;
    }

    /**
     * Escape double asterisks ** = *, and remove single asterisk.
     *
     * @private
     */
    function handleAsterisks() {
      var asteriskIndex = handledInput.indexOf('*');

      while (asteriskIndex !== -1) {
        handledInput = handledInput.slice(0, asteriskIndex) + handledInput.slice(asteriskIndex + 1, handledInput.length);
        asteriskIndex = handledInput.indexOf('*', asteriskIndex + 1);
      }
    }

    /**
     * Removes any score points added to the marked word.
     */
    self.clearScorePoint = function () {
      const scorePoint = $word[0].querySelector('div');
      if (scorePoint) {
        scorePoint.parentNode.removeChild(scorePoint);
      }
    };

    /**
     * Get Word as a string
     *
     * @return {string} Word as text
     */
    this.getText = function () {
      return input;
    };

    /**
     * Clears all marks from the word.
     *
     * @public
     */
    this.markClear = function () {
      $word
        .attr('aria-selected', false)
        .removeAttr('aria-describedby');

      ariaText.innerHTML = '';
      this.clearScorePoint();
    };

    /**
     * Check if the word is correctly marked and style it accordingly.
     * Reveal result
     *
     * @public
     * @param {H5P.Question.ScorePoints} scorePoints
     */
    this.markCheck = function (scorePoints) {
      if (this.isSelected()) {
        $word.attr('aria-describedby', isAnswer ? Word.ID_MARK_CORRECT : Word.ID_MARK_INCORRECT);
        ariaText.innerHTML = isAnswer
          ? self.params.correctAnswer
          : self.params.incorrectAnswer;

        if (scorePoints) {
          $word[0].appendChild(scorePoints.getElement(isAnswer));
        }
      }
      else if (isAnswer) {
        $word.attr('aria-describedby', Word.ID_MARK_MISSED);
        ariaText.innerHTML = self.params.missedAnswer;
      }
    };

    /**
     * Checks if the word is marked correctly.
     *
     * @public
     * @returns {Boolean} True if the marking is correct.
     */
    this.isCorrect = function () {
      return (isAnswer && this.isSelected());
    };

    /**
     * Checks if the word is marked wrong.
     *
     * @public
     * @returns {Boolean} True if the marking is wrong.
     */
    this.isWrong = function () {
      return (!isAnswer && this.isSelected());
    };

    /**
     * Checks if the word is correct, but has not been marked.
     *
     * @public
     * @returns {Boolean} True if the marking is missed.
     */
    this.isMissed = function () {
      return (isAnswer && !this.isSelected());
    };

    /**
     * Checks if the word is an answer.
     *
     * @public
     * @returns {Boolean} True if the word is an answer.
     */
    this.isAnswer = function () {
      return isAnswer;
    };

    /**
     * Checks if the word is selected.
     *
     * @public
     * @returns {Boolean} True if the word is selected.
     */
    this.isSelected = function () {
      return $word.attr('aria-selected') === 'true';
    };

    /**
     * Sets that the Word is selected
     *
     * @public
     */
    this.setSelected = function () {
      $word.attr('aria-selected', 'true');
    };
  }
  Word.prototype = Object.create(H5P.EventDispatcher.prototype);
  Word.prototype.constructor = Word;

  return Word;
})();
;/*global H5P*/

/**
 * Mark The Words module
 * @external {jQuery} $ H5P.jQuery
 */
H5P.MarkTheWords = (function ($, Question, Word, KeyboardNav, XapiGenerator) {
  /**
   * Initialize module.
   *
   * @class H5P.MarkTheWords
   * @extends H5P.Question
   * @param {Object} params Behavior settings
   * @param {Number} contentId Content identification
   * @param {Object} contentData Object containing task specific content data
   *
   * @returns {Object} MarkTheWords Mark the words instance
   */
  function MarkTheWords(params, contentId, contentData) {
    this.contentId = contentId;
    this.contentData = contentData;
    this.introductionId = 'mark-the-words-introduction-' + contentId;

    Question.call(this, 'mark-the-words');

    // Set default behavior.
    this.params = $.extend(true, {
      media: {},
      taskDescription: "",
      textField: "This is a *nice*, *flexible* content type.",
      overallFeedback: [],
      behaviour: {
        enableRetry: true,
        enableSolutionsButton: true,
        enableCheckButton: true,
        showScorePoints: true
      },
      checkAnswerButton: "Check",
      submitAnswerButton: "Submit",
      tryAgainButton: "Retry",
      showSolutionButton: "Show solution",
      correctAnswer: "Correct!",
      incorrectAnswer: "Incorrect!",
      missedAnswer: "Answer not found!",
      displaySolutionDescription:  "Task is updated to contain the solution.",
      scoreBarLabel: 'You got :num out of :total points',
      a11yFullTextLabel: 'Full readable text',
      a11yClickableTextLabel: 'Full text where words can be marked',
      a11ySolutionModeHeader: 'Solution mode',
      a11yCheckingHeader: 'Checking mode',
      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.',
    }, params);

    this.contentData = contentData;
    if (this.contentData !== undefined && this.contentData.previousState !== undefined) {
      this.previousState = this.contentData.previousState;
    }

    this.keyboardNavigators = [];

    this.initMarkTheWords();
    this.XapiGenerator = new XapiGenerator(this);
  }

  MarkTheWords.prototype = Object.create(H5P.EventDispatcher.prototype);
  MarkTheWords.prototype.constructor = MarkTheWords;

  /**
   * Initialize Mark The Words task
   */
  MarkTheWords.prototype.initMarkTheWords = function () {
    this.$inner = $('<div class="h5p-word-inner"></div>');

    this.addTaskTo(this.$inner);

    // Set user state
    this.setH5PUserState();
  };

  /**
   * Recursive function that creates html for the words
   * @method createHtmlForWords
   * @param  {Array}           nodes Array of dom nodes
   * @return {string}
   */
  MarkTheWords.prototype.createHtmlForWords = function (nodes) {
    var self = this;
    var html = '';
    for (var i = 0; i < nodes.length; i++) {
      var node = nodes[i];

      if (node instanceof Text) {
        var text = $(node).text();
        var selectableStrings = text.replace(/(&nbsp;|\r\n|\n|\r)/g, ' ')
          .match(/ \*[^\* ]+\* |[^\s]+/g);

        if (selectableStrings) {
          selectableStrings.forEach(function (entry) {
            entry = entry.trim();

            // Words
            if (html) {
              // Add space before
              html += ' ';
            }

            // Remove prefix punctuations from word
            var prefix = entry.match(/^[\[\({⟨¿¡“"«„]+/);
            var start = 0;
            if (prefix !== null) {
              start = prefix[0].length;
              html += prefix;
            }

            // Remove suffix punctuations from word
            var suffix = entry.match(/[",….:;?!\]\)}⟩»”]+$/);
            var end = entry.length - start;
            if (suffix !== null) {
              end -= suffix[0].length;
            }

            // Word
            entry = entry.substr(start, end);
            if (entry.length) {
              html += '<span role="option" aria-selected="false">' + self.escapeHTML(entry) + '</span>';
            }

            if (suffix !== null) {
              html += suffix;
            }
          });
        }
        else if ((selectableStrings !== null) && text.length) {
          html += '<span role="option" aria-selected="false">' + this.escapeHTML(text) + '</span>';
        }
      }
      else {
        if (node.nodeName === 'BR') {
          html += '<br/>';
        }
        else {
          var attributes = ' ';
          for (var j = 0; j < node.attributes.length; j++) {
            attributes +=node.attributes[j].name + '="' + node.attributes[j].nodeValue + '" ';
          }
          html += '<' + node.nodeName +  attributes + '>';
          html += self.createHtmlForWords(node.childNodes);
          html += '</' + node.nodeName + '>';
        }
      }
    }

    return html;
  };

  /**
   * Escapes HTML
   *
   * @param html
   * @returns {jQuery}
   */
  MarkTheWords.prototype.escapeHTML = function (html) {
    return $('<div>').text(html).html();
  };

  /**
   * Search for the last children in every paragraph and
   * return their indexes in an array
   *
   * @returns {Array}
   */
  MarkTheWords.prototype.getIndexesOfLineBreaks = function () {

    var indexes = [];
    var selectables = this.$wordContainer.find('span.h5p-word-selectable');

    selectables.each(function (index, selectable) {
      if ($(selectable).next().is('br')) {
        indexes.push(index);
      }

      if ($(selectable).parent('p') && !$(selectable).parent().is(':last-child') && $(selectable).is(':last-child')) {
        indexes.push(index);
      }
    });

    return indexes;
  };

  /**
   * Handle task and add it to container.
   * @param {jQuery} $container The object which our task will attach to.
   */
  MarkTheWords.prototype.addTaskTo = function ($container) {
    var self = this;
    self.selectableWords = [];
    self.answers = 0;

    // Wrapper
    var $wordContainer = $('<div/>', {
      'class': 'h5p-word-selectable-words',
      'aria-labelledby': self.introductionId,
      'aria-multiselectable': 'true',
      'role': 'listbox',
      html: self.createHtmlForWords($.parseHTML(self.params.textField))
    });

    let isNewParagraph = true;
    $wordContainer.find('[role="option"], br').each(function () {
      if ($(this).is('br')) {
        isNewParagraph = true;
        return;
      }

      if (isNewParagraph) {
        // Add keyboard navigation helper
        self.currentKeyboardNavigator = new KeyboardNav();

        // on word clicked
        self.currentKeyboardNavigator.on('select', function () {
          self.isAnswered = true;
          self.triggerXAPI('interacted');
        });

        self.keyboardNavigators.push(self.currentKeyboardNavigator);
        isNewParagraph = false;
      }
      self.currentKeyboardNavigator.addElement(this);

      // Add keyboard navigation to this element
      var selectableWord = new Word($(this), self.params);
      if (selectableWord.isAnswer()) {
        self.isAnswered = true;
        self.answers += 1;
      }
      self.selectableWords.push(selectableWord);
    });

    self.blankIsCorrect = (self.answers === 0);
    if (self.blankIsCorrect) {
      self.answers = 1;
    }

    // A11y full readable text
    const $ariaTextWrapper = $('<div>', {
      'class': 'hidden-but-read',
    }).appendTo($container);
    $('<div>', {
      html: self.params.a11yFullTextLabel,
    }).appendTo($ariaTextWrapper);

    // Add space after each paragraph to read the sentences better
    const ariaText = $('<div>', {
      'html': $wordContainer.html().replace('</p>', ' </p>'),
    }).text();

    $('<div>', {
      text: ariaText,
    }).appendTo($ariaTextWrapper);

    // A11y clickable list label
    this.$a11yClickableTextLabel = $('<div>', {
      'class': 'hidden-but-read',
      html: self.params.a11yClickableTextLabel,
      tabIndex: '-1',
    }).appendTo($container);

    $wordContainer.appendTo($container);
    self.$wordContainer = $wordContainer;
  };

  /**
   * Add check solution and retry buttons.
   */
  MarkTheWords.prototype.addButtons = function () {
    var self = this;
    self.$buttonContainer = $('<div/>', {
      'class': 'h5p-button-bar'
    });

    if (this.params.behaviour.enableCheckButton) {
      this.addButton('check-answer', this.params.checkAnswerButton, function () {
        self.isAnswered = true;
        var answers = self.calculateScore();
        self.feedbackSelectedWords();

        if (!self.showEvaluation(answers)) {
          // Only show if a correct answer was not found.
          if (self.params.behaviour.enableSolutionsButton && (answers.correct < self.answers)) {
            self.showButton('show-solution');
          }
          if (self.params.behaviour.enableRetry) {
            self.showButton('try-again');
          }
        }
        // Set focus to start of text
        self.$a11yClickableTextLabel.html(self.params.a11yCheckingHeader + ' - ' + self.params.a11yClickableTextLabel);
        self.$a11yClickableTextLabel.focus();

        self.hideButton('check-answer');
        self.trigger(self.XapiGenerator.generateAnsweredEvent());
        self.toggleSelectable(true);
      }, true, {
        'aria-label': this.params.a11yCheck,
      }, {
        contentData: this.contentData,
        textIfSubmitting: this.params.submitAnswerButton,
      });
    }

    this.addButton('try-again', this.params.tryAgainButton, this.resetTask.bind(this), false, {
      'aria-label': this.params.a11yRetry,
    });

    this.addButton('show-solution', this.params.showSolutionButton, function () {
      self.setAllMarks();

      self.$a11yClickableTextLabel.html(self.params.a11ySolutionModeHeader + ' - ' + self.params.a11yClickableTextLabel);
      self.$a11yClickableTextLabel.focus();

      if (self.params.behaviour.enableRetry) {
        self.showButton('try-again');
      }
      self.hideButton('check-answer');
      self.hideButton('show-solution');

      self.read(self.params.displaySolutionDescription);
      self.toggleSelectable(true);
    }, false, {
      'aria-label': this.params.a11yShowSolution,
    });
  };

  /**
   * Toggle whether words can be selected
   * @param {Boolean} disable
   */
  MarkTheWords.prototype.toggleSelectable = function (disable) {
    this.keyboardNavigators.forEach(function (navigator) {
      if (disable) {
        navigator.disableSelectability();
        navigator.removeAllTabbable();
      }
      else {
        navigator.enableSelectability();
        navigator.setTabbableAt((0));
      }
    });

    if (disable) {
      this.$wordContainer.removeAttr('aria-multiselectable').removeAttr('role');
    }
    else {
      this.$wordContainer.attr('aria-multiselectable', 'true')
        .attr('role', 'listbox');
    }
  };

  /**
   * Get Xapi Data.
   *
   * @see used in contracts {@link https://h5p.org/documentation/developers/contracts#guides-header-6}
   * @return {Object}
   */
  MarkTheWords.prototype.getXAPIData = function () {
    return {
      statement: this.XapiGenerator.generateAnsweredEvent().data.statement
    };
  };

  /**
   * Mark the words as correct, wrong or missed.
   *
   * @fires MarkTheWords#resize
   */
  MarkTheWords.prototype.setAllMarks = function () {
    this.selectableWords.forEach(function (entry) {
      entry.markCheck();
      entry.clearScorePoint();
    });

    /**
     * Resize event
     *
     * @event MarkTheWords#resize
     */
    this.trigger('resize');
  };

  /**
   * Mark the selected words as correct or wrong.
   *
   * @fires MarkTheWords#resize
   */
  MarkTheWords.prototype.feedbackSelectedWords = function () {
    var self = this;

    var scorePoints;
    if (self.params.behaviour.showScorePoints) {
      scorePoints = new H5P.Question.ScorePoints();
    }

    this.selectableWords.forEach(function (entry) {
      if (entry.isSelected()) {
        entry.markCheck(scorePoints);
      }
    });

    this.$wordContainer.addClass('h5p-disable-hover');
    this.trigger('resize');
  };

  /**
   * Evaluate task and display score text for word markings.
   *
   * @fires MarkTheWords#resize
   * @return {Boolean} Returns true if maxScore was achieved.
   */
  MarkTheWords.prototype.showEvaluation = function (answers) {
    this.hideEvaluation();
    var score = answers.score;

    //replace editor variables with values, uses regexp to replace all instances.
    var scoreText = H5P.Question.determineOverallFeedback(this.params.overallFeedback, score / this.answers).replace(/@score/g, score.toString())
      .replace(/@total/g, this.answers.toString())
      .replace(/@correct/g, answers.correct.toString())
      .replace(/@wrong/g, answers.wrong.toString())
      .replace(/@missed/g, answers.missed.toString());

    this.setFeedback(scoreText, score, this.answers, this.params.scoreBarLabel);

    this.trigger('resize');
    return score === this.answers;
  };

  /**
   * Clear the evaluation text.
   *
   * @fires MarkTheWords#resize
   */
  MarkTheWords.prototype.hideEvaluation = function () {
    this.removeFeedback();
    this.trigger('resize');
  };

  /**
   * Calculate the score.
   *
   * @return {Answers}
   */
  MarkTheWords.prototype.calculateScore = function () {
    var self = this;

    /**
     * @typedef {Object} Answers
     * @property {number} correct The number of correct answers
     * @property {number} wrong The number of wrong answers
     * @property {number} missed The number of answers the user missed
     * @property {number} score The calculated score
     */
    var initial = {
      correct: 0,
      wrong: 0,
      missed: 0,
      score: 0
    };

    // iterate over words, and calculate score
    var answers = self.selectableWords.reduce(function (result, word) {
      if (word.isCorrect()) {
        result.correct++;
      }
      else if (word.isWrong()) {
        result.wrong++;
      }
      else if (word.isMissed()) {
        result.missed++;
      }

      return result;
    }, initial);

    // if no wrong answers, and black is correct
    if (answers.wrong === 0 && self.blankIsCorrect) {
      answers.correct = 1;
    }

    // no negative score
    answers.score = Math.max(answers.correct - answers.wrong, 0);

    return answers;
  };

  /**
   * Clear styling on marked words.
   *
   * @fires MarkTheWords#resize
   */
  MarkTheWords.prototype.clearAllMarks = function () {
    this.selectableWords.forEach(function (entry) {
      entry.markClear();
    });

    this.$wordContainer.removeClass('h5p-disable-hover');
    this.trigger('resize');
  };

  /**
   * Returns true if task is checked or a word has been clicked
   *
   * @see {@link https://h5p.org/documentation/developers/contracts|Needed for contracts.}
   * @returns {Boolean} Always returns true.
   */
  MarkTheWords.prototype.getAnswerGiven = function () {
    return this.blankIsCorrect ? true : this.isAnswered;
  };

  /**
   * Counts the score, which is correct answers subtracted by wrong answers.
   *
   * @see {@link https://h5p.org/documentation/developers/contracts|Needed for contracts.}
   * @returns {Number} score The amount of points achieved.
   */
  MarkTheWords.prototype.getScore = function () {
    return this.calculateScore().score;
  };

  /**
   * Gets max score for this task.
   *
   * @see {@link https://h5p.org/documentation/developers/contracts|Needed for contracts.}
   * @returns {Number} maxScore The maximum amount of points achievable.
   */
  MarkTheWords.prototype.getMaxScore = function () {
    return this.answers;
  };

  /**
   * Get title
   * @returns {string}
   */
  MarkTheWords.prototype.getTitle = function () {
    return H5P.createTitle((this.contentData && this.contentData.metadata && this.contentData.metadata.title) ? this.contentData.metadata.title : 'Mark the Words');
  };

  /**
   * Display the evaluation of the task, with proper markings.
   *
   * @fires MarkTheWords#resize
   * @see {@link https://h5p.org/documentation/developers/contracts|Needed for contracts.}
   */
  MarkTheWords.prototype.showSolutions = function () {
    var answers = this.calculateScore();
    this.showEvaluation(answers);
    this.setAllMarks();
    this.read(this.params.displaySolutionDescription);
    this.hideButton('try-again');
    this.hideButton('show-solution');
    this.hideButton('check-answer');
    this.$a11yClickableTextLabel.html(this.params.a11ySolutionModeHeader + ' - ' + this.params.a11yClickableTextLabel);

    this.toggleSelectable(true);
    this.trigger('resize');
  };

  /**
   * Resets the task back to its' initial state.
   *
   * @fires MarkTheWords#resize
   * @see {@link https://h5p.org/documentation/developers/contracts|Needed for contracts.}
   */
  MarkTheWords.prototype.resetTask = function () {
    this.isAnswered = false;
    this.clearAllMarks();
    this.hideEvaluation();
    this.hideButton('try-again');
    this.hideButton('show-solution');
    this.showButton('check-answer');
    this.$a11yClickableTextLabel.html(this.params.a11yClickableTextLabel);

    this.toggleSelectable(false);
    this.trigger('resize');
  };

  /**
   * Returns an object containing the selected words
   *
   * @public
   * @returns {object} containing indexes of selected words
   */
  MarkTheWords.prototype.getCurrentState = function () {
    var selectedWordsIndexes = [];
    if (this.selectableWords === undefined) {
      return undefined;
    }

    this.selectableWords.forEach(function (selectableWord, swIndex) {
      if (selectableWord.isSelected()) {
        selectedWordsIndexes.push(swIndex);
      }
    });
    return selectedWordsIndexes;
  };

  /**
   * Sets answers to current user state
   */
  MarkTheWords.prototype.setH5PUserState = function () {
    var self = this;

    // Do nothing if user state is undefined
    if (this.previousState === undefined || this.previousState.length === undefined) {
      return;
    }

    // Select words from user state
    this.previousState.forEach(function (answeredWordIndex) {
      if (isNaN(answeredWordIndex) || answeredWordIndex >= self.selectableWords.length || answeredWordIndex < 0) {
        throw new Error('Stored user state is invalid');
      }

      self.isAnswered = true;
      self.selectableWords[answeredWordIndex].setSelected();
    });
  };

  /**
   * Register dom elements
   *
   * @see {@link https://github.com/h5p/h5p-question/blob/1558b6144333a431dd71e61c7021d0126b18e252/scripts/question.js#L1236|Called from H5P.Question}
   */
  MarkTheWords.prototype.registerDomElements = function () {
    // Register optional media
    let media = this.params.media;
    if (media && media.type && media.type.library) {
      media = media.type;
      const type = media.library.split(' ')[0];
      if (type === 'H5P.Image') {
        if (media.params.file) {
          // Register task image
          this.setImage(media.params.file.path, {
            disableImageZooming: this.params.media.disableImageZooming || false,
            alt: media.params.alt,
            title: media.params.title,
            expandImage: media.params.expandImage,
            minimizeImage: media.params.minimizeImage
          });
        }
      }
      else if (type === 'H5P.Video') {
        if (media.params.sources) {
          // Register task video
          this.setVideo(media);
        }
      }
      else if (type === 'H5P.Audio') {
        if (media.params.files) {
          // Register task audio
          this.setAudio(media);
        }
      }
    }

    // wrap introduction in div with id
    var introduction = '<div id="' + this.introductionId + '">' + this.params.taskDescription + '</div>';

    // Register description
    this.setIntroduction(introduction);

    // creates aria descriptions for correct/incorrect/missed
    this.createDescriptionsDom().appendTo(this.$inner);

    // Register content
    this.setContent(this.$inner, {
      'class': 'h5p-word'
    });

    // Register buttons
    this.addButtons();
  };

  /**
   * Creates dom with description to be used with aria-describedby
   * @return {jQuery}
   */
  MarkTheWords.prototype.createDescriptionsDom = function () {
    var self = this;
    var $el = $('<div class="h5p-mark-the-words-descriptions"></div>');

    $('<div id="' + Word.ID_MARK_CORRECT + '">' + self.params.correctAnswer + '</div>').appendTo($el);
    $('<div id="' + Word.ID_MARK_INCORRECT + '">' + self.params.incorrectAnswer + '</div>').appendTo($el);
    $('<div id="' + Word.ID_MARK_MISSED + '">' + self.params.missedAnswer + '</div>').appendTo($el);

    return $el;
  };

  return MarkTheWords;
}(H5P.jQuery, H5P.Question, H5P.MarkTheWords.Word, H5P.KeyboardNav, H5P.MarkTheWords.XapiGenerator));

/**
 * Static utility method for parsing H5P.MarkTheWords content item questions
 * into format useful for generating reports.
 *
 * Example input: "<p lang=\"en\">I like *pizza* and *burgers*.</p>"
 *
 * Produces the following:
 * [
 *   {
 *     type: 'text',
 *     content: 'I like '
 *   },
 *   {
 *     type: 'answer',
 *     correct: 'pizza',
 *   },
 *   {
 *     type: 'text',
 *     content: ' and ',
 *   },
 *   {
 *     type: 'answer',
 *     correct: 'burgers'
 *   },
 *   {
 *     type: 'text',
 *     content: '.'
 *   }
 * ]
 *
 * @param {string} question MarkTheWords textField (html)
 */
H5P.MarkTheWords.parseText = function (question) {

  /**
   * Separate all words surrounded by a space and an asterisk and any other
   * sequence of non-whitespace characters from str into an array.
   *
   * @param {string} str
   * @returns {string[]} array of all words in the given string
   */
  function getWords(str) {
    return str.match(/ \*[^\*]+\* |[^\s]+/g);
  }

  /**
   * Replace each HTML tag in str with the provided value and return the resulting string
   *
   * Regexp expression explained:
   *   <     - first character is '<'
   *   [^>]* - followed by zero or more occurences of any character except '>'
   *   >     - last character is '>'
   **/
  function replaceHtmlTags(str, value) {
    return str.replace(/<[^>]*>/g, value);
  }

  function startsAndEndsWith(char, str) {
    return str.startsWith(char) && str.endsWith(char);
  }

  function removeLeadingPunctuation(str) {
    return str.replace(/^[\[\({⟨¿¡“"«„]+/, '');
  }

  function removeTrailingPunctuation(str) {
    return str.replace(/[",….:;?!\]\)}⟩»”]+$/, '');
  }

  /**
   * Escape double asterisks ** = *, and remove single asterisk.
   * @param {string} str
   */
  function handleAsterisks(str) {
    var asteriskIndex = str.indexOf('*');

    while (asteriskIndex !== -1) {
      str = str.slice(0, asteriskIndex) + str.slice(asteriskIndex + 1, str.length);
      asteriskIndex = str.indexOf('*', asteriskIndex + 1);
    }
    return str;
  }

  /**
   * Decode HTML entities (e.g. &nbsp;) from the given string using the DOM API
   * @param {string} str
   */
  function decodeHtmlEntities(str) {
    const el = document.createElement('textarea');
    el.innerHTML = str;
    return el.value;
  }

  const wordsWithAsterisksNotRemovedYet = getWords(replaceHtmlTags(decodeHtmlEntities(question), ' '))
    .map(function (w) {
      return w.trim();
    })
    .map(function (w) {
      return removeLeadingPunctuation(w);
    })
    .map(function (w) {
      return removeTrailingPunctuation(w);
    });

  const allSelectableWords = wordsWithAsterisksNotRemovedYet.map(function (w) {
    return handleAsterisks(w);
  });

  const correctWordIndexes = [];

  const correctWords = wordsWithAsterisksNotRemovedYet
    .filter(function (w, i) {
      if (startsAndEndsWith('*', w)) {
        correctWordIndexes.push(i);
        return true;
      }
      return false;
    })
    .map(function (w) {
      return handleAsterisks(w);
    });

  const printableQuestion = replaceHtmlTags(decodeHtmlEntities(question), ' ')
    .replace('\xa0', '\x20');

  return {
    alternatives: allSelectableWords,
    correctWords: correctWords,
    correctWordIndexes: correctWordIndexes,
    textWithPlaceholders: printableQuestion.match(/[^\s]+/g)
      .reduce(function (textWithPlaceholders, word, index) {
        word = removeTrailingPunctuation(
          removeLeadingPunctuation(word));

        return textWithPlaceholders.replace(word, '%' + index);
      }, printableQuestion)
  };
};
;/*! For license information please see h5p-interactive-book.js.LICENSE.txt */
(()=>{var t={2880:t=>{"use strict";t.exports={aliceblue:[240,248,255],antiquewhite:[250,235,215],aqua:[0,255,255],aquamarine:[127,255,212],azure:[240,255,255],beige:[245,245,220],bisque:[255,228,196],black:[0,0,0],blanchedalmond:[255,235,205],blue:[0,0,255],blueviolet:[138,43,226],brown:[165,42,42],burlywood:[222,184,135],cadetblue:[95,158,160],chartreuse:[127,255,0],chocolate:[210,105,30],coral:[255,127,80],cornflowerblue:[100,149,237],cornsilk:[255,248,220],crimson:[220,20,60],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgoldenrod:[184,134,11],darkgray:[169,169,169],darkgreen:[0,100,0],darkgrey:[169,169,169],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkseagreen:[143,188,143],darkslateblue:[72,61,139],darkslategray:[47,79,79],darkslategrey:[47,79,79],darkturquoise:[0,206,209],darkviolet:[148,0,211],deeppink:[255,20,147],deepskyblue:[0,191,255],dimgray:[105,105,105],dimgrey:[105,105,105],dodgerblue:[30,144,255],firebrick:[178,34,34],floralwhite:[255,250,240],forestgreen:[34,139,34],fuchsia:[255,0,255],gainsboro:[220,220,220],ghostwhite:[248,248,255],gold:[255,215,0],goldenrod:[218,165,32],gray:[128,128,128],green:[0,128,0],greenyellow:[173,255,47],grey:[128,128,128],honeydew:[240,255,240],hotpink:[255,105,180],indianred:[205,92,92],indigo:[75,0,130],ivory:[255,255,240],khaki:[240,230,140],lavender:[230,230,250],lavenderblush:[255,240,245],lawngreen:[124,252,0],lemonchiffon:[255,250,205],lightblue:[173,216,230],lightcoral:[240,128,128],lightcyan:[224,255,255],lightgoldenrodyellow:[250,250,210],lightgray:[211,211,211],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightsalmon:[255,160,122],lightseagreen:[32,178,170],lightskyblue:[135,206,250],lightslategray:[119,136,153],lightslategrey:[119,136,153],lightsteelblue:[176,196,222],lightyellow:[255,255,224],lime:[0,255,0],limegreen:[50,205,50],linen:[250,240,230],magenta:[255,0,255],maroon:[128,0,0],mediumaquamarine:[102,205,170],mediumblue:[0,0,205],mediumorchid:[186,85,211],mediumpurple:[147,112,219],mediumseagreen:[60,179,113],mediumslateblue:[123,104,238],mediumspringgreen:[0,250,154],mediumturquoise:[72,209,204],mediumvioletred:[199,21,133],midnightblue:[25,25,112],mintcream:[245,255,250],mistyrose:[255,228,225],moccasin:[255,228,181],navajowhite:[255,222,173],navy:[0,0,128],oldlace:[253,245,230],olive:[128,128,0],olivedrab:[107,142,35],orange:[255,165,0],orangered:[255,69,0],orchid:[218,112,214],palegoldenrod:[238,232,170],palegreen:[152,251,152],paleturquoise:[175,238,238],palevioletred:[219,112,147],papayawhip:[255,239,213],peachpuff:[255,218,185],peru:[205,133,63],pink:[255,192,203],plum:[221,160,221],powderblue:[176,224,230],purple:[128,0,128],rebeccapurple:[102,51,153],red:[255,0,0],rosybrown:[188,143,143],royalblue:[65,105,225],saddlebrown:[139,69,19],salmon:[250,128,114],sandybrown:[244,164,96],seagreen:[46,139,87],seashell:[255,245,238],sienna:[160,82,45],silver:[192,192,192],skyblue:[135,206,235],slateblue:[106,90,205],slategray:[112,128,144],slategrey:[112,128,144],snow:[255,250,250],springgreen:[0,255,127],steelblue:[70,130,180],tan:[210,180,140],teal:[0,128,128],thistle:[216,191,216],tomato:[255,99,71],turquoise:[64,224,208],violet:[238,130,238],wheat:[245,222,179],white:[255,255,255],whitesmoke:[245,245,245],yellow:[255,255,0],yellowgreen:[154,205,50]}},6250:(t,e,r)=>{var n=r(2880),a=r(6852),o=Object.hasOwnProperty,i=Object.create(null);for(var s in n)o.call(n,s)&&(i[n[s]]=s);var c=t.exports={to:{},get:{}};function u(t,e,r){return Math.min(Math.max(e,t),r)}function l(t){var e=Math.round(t).toString(16).toUpperCase();return e.length<2?"0"+e:e}c.get=function(t){var e,r;switch(t.substring(0,3).toLowerCase()){case"hsl":e=c.get.hsl(t),r="hsl";break;case"hwb":e=c.get.hwb(t),r="hwb";break;default:e=c.get.rgb(t),r="rgb"}return e?{model:r,value:e}:null},c.get.rgb=function(t){if(!t)return null;var e,r,a,i=[0,0,0,1];if(e=t.match(/^#([a-f0-9]{6})([a-f0-9]{2})?$/i)){for(a=e[2],e=e[1],r=0;r<3;r++){var s=2*r;i[r]=parseInt(e.slice(s,s+2),16)}a&&(i[3]=parseInt(a,16)/255)}else if(e=t.match(/^#([a-f0-9]{3,4})$/i)){for(a=(e=e[1])[3],r=0;r<3;r++)i[r]=parseInt(e[r]+e[r],16);a&&(i[3]=parseInt(a+a,16)/255)}else if(e=t.match(/^rgba?\(\s*([+-]?\d+)(?=[\s,])\s*(?:,\s*)?([+-]?\d+)(?=[\s,])\s*(?:,\s*)?([+-]?\d+)\s*(?:[,|\/]\s*([+-]?[\d\.]+)(%?)\s*)?\)$/)){for(r=0;r<3;r++)i[r]=parseInt(e[r+1],0);e[4]&&(e[5]?i[3]=.01*parseFloat(e[4]):i[3]=parseFloat(e[4]))}else{if(!(e=t.match(/^rgba?\(\s*([+-]?[\d\.]+)\%\s*,?\s*([+-]?[\d\.]+)\%\s*,?\s*([+-]?[\d\.]+)\%\s*(?:[,|\/]\s*([+-]?[\d\.]+)(%?)\s*)?\)$/)))return(e=t.match(/^(\w+)$/))?"transparent"===e[1]?[0,0,0,0]:o.call(n,e[1])?((i=n[e[1]])[3]=1,i):null:null;for(r=0;r<3;r++)i[r]=Math.round(2.55*parseFloat(e[r+1]));e[4]&&(e[5]?i[3]=.01*parseFloat(e[4]):i[3]=parseFloat(e[4]))}for(r=0;r<3;r++)i[r]=u(i[r],0,255);return i[3]=u(i[3],0,1),i},c.get.hsl=function(t){if(!t)return null;var e=t.match(/^hsla?\(\s*([+-]?(?:\d{0,3}\.)?\d+)(?:deg)?\s*,?\s*([+-]?[\d\.]+)%\s*,?\s*([+-]?[\d\.]+)%\s*(?:[,|\/]\s*([+-]?(?=\.\d|\d)(?:0|[1-9]\d*)?(?:\.\d*)?(?:[eE][+-]?\d+)?)\s*)?\)$/);if(e){var r=parseFloat(e[4]);return[(parseFloat(e[1])%360+360)%360,u(parseFloat(e[2]),0,100),u(parseFloat(e[3]),0,100),u(isNaN(r)?1:r,0,1)]}return null},c.get.hwb=function(t){if(!t)return null;var e=t.match(/^hwb\(\s*([+-]?\d{0,3}(?:\.\d+)?)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?(?=\.\d|\d)(?:0|[1-9]\d*)?(?:\.\d*)?(?:[eE][+-]?\d+)?)\s*)?\)$/);if(e){var r=parseFloat(e[4]);return[(parseFloat(e[1])%360+360)%360,u(parseFloat(e[2]),0,100),u(parseFloat(e[3]),0,100),u(isNaN(r)?1:r,0,1)]}return null},c.to.hex=function(){var t=a(arguments);return"#"+l(t[0])+l(t[1])+l(t[2])+(t[3]<1?l(Math.round(255*t[3])):"")},c.to.rgb=function(){var t=a(arguments);return t.length<4||1===t[3]?"rgb("+Math.round(t[0])+", "+Math.round(t[1])+", "+Math.round(t[2])+")":"rgba("+Math.round(t[0])+", "+Math.round(t[1])+", "+Math.round(t[2])+", "+t[3]+")"},c.to.rgb.percent=function(){var t=a(arguments),e=Math.round(t[0]/255*100),r=Math.round(t[1]/255*100),n=Math.round(t[2]/255*100);return t.length<4||1===t[3]?"rgb("+e+"%, "+r+"%, "+n+"%)":"rgba("+e+"%, "+r+"%, "+n+"%, "+t[3]+")"},c.to.hsl=function(){var t=a(arguments);return t.length<4||1===t[3]?"hsl("+t[0]+", "+t[1]+"%, "+t[2]+"%)":"hsla("+t[0]+", "+t[1]+"%, "+t[2]+"%, "+t[3]+")"},c.to.hwb=function(){var t=a(arguments),e="";return t.length>=4&&1!==t[3]&&(e=", "+t[3]),"hwb("+t[0]+", "+t[1]+"%, "+t[2]+"%"+e+")"},c.to.keyword=function(t){return i[t.slice(0,3)]}},4308:(t,e,r)=>{const n=r(6250),a=r(8349),o=["keyword","gray","hex"],i={};for(const t of Object.keys(a))i[[...a[t].labels].sort().join("")]=t;const s={};function c(t,e){if(!(this instanceof c))return new c(t,e);if(e&&e in o&&(e=null),e&&!(e in a))throw new Error("Unknown model: "+e);let r,u;if(null==t)this.model="rgb",this.color=[0,0,0],this.valpha=1;else if(t instanceof c)this.model=t.model,this.color=[...t.color],this.valpha=t.valpha;else if("string"==typeof t){const e=n.get(t);if(null===e)throw new Error("Unable to parse color from string: "+t);this.model=e.model,u=a[this.model].channels,this.color=e.value.slice(0,u),this.valpha="number"==typeof e.value[u]?e.value[u]:1}else if(t.length>0){this.model=e||"rgb",u=a[this.model].channels;const r=Array.prototype.slice.call(t,0,u);this.color=h(r,u),this.valpha="number"==typeof t[u]?t[u]:1}else if("number"==typeof t)this.model="rgb",this.color=[t>>16&255,t>>8&255,255&t],this.valpha=1;else{this.valpha=1;const e=Object.keys(t);"alpha"in t&&(e.splice(e.indexOf("alpha"),1),this.valpha="number"==typeof t.alpha?t.alpha:0);const n=e.sort().join("");if(!(n in i))throw new Error("Unable to parse color from object: "+JSON.stringify(t));this.model=i[n];const{labels:o}=a[this.model],s=[];for(r=0;r<o.length;r++)s.push(t[o[r]]);this.color=h(s)}if(s[this.model])for(u=a[this.model].channels,r=0;r<u;r++){const t=s[this.model][r];t&&(this.color[r]=t(this.color[r]))}this.valpha=Math.max(0,Math.min(1,this.valpha)),Object.freeze&&Object.freeze(this)}c.prototype={toString(){return this.string()},toJSON(){return this[this.model]()},string(t){let e=this.model in n.to?this:this.rgb();e=e.round("number"==typeof t?t:1);const r=1===e.valpha?e.color:[...e.color,this.valpha];return n.to[e.model](r)},percentString(t){const e=this.rgb().round("number"==typeof t?t:1),r=1===e.valpha?e.color:[...e.color,this.valpha];return n.to.rgb.percent(r)},array(){return 1===this.valpha?[...this.color]:[...this.color,this.valpha]},object(){const t={},{channels:e}=a[this.model],{labels:r}=a[this.model];for(let n=0;n<e;n++)t[r[n]]=this.color[n];return 1!==this.valpha&&(t.alpha=this.valpha),t},unitArray(){const t=this.rgb().color;return t[0]/=255,t[1]/=255,t[2]/=255,1!==this.valpha&&t.push(this.valpha),t},unitObject(){const t=this.rgb().object();return t.r/=255,t.g/=255,t.b/=255,1!==this.valpha&&(t.alpha=this.valpha),t},round(t){return t=Math.max(t||0,0),new c([...this.color.map(u(t)),this.valpha],this.model)},alpha(t){return void 0!==t?new c([...this.color,Math.max(0,Math.min(1,t))],this.model):this.valpha},red:l("rgb",0,p(255)),green:l("rgb",1,p(255)),blue:l("rgb",2,p(255)),hue:l(["hsl","hsv","hsl","hwb","hcg"],0,(t=>(t%360+360)%360)),saturationl:l("hsl",1,p(100)),lightness:l("hsl",2,p(100)),saturationv:l("hsv",1,p(100)),value:l("hsv",2,p(100)),chroma:l("hcg",1,p(100)),gray:l("hcg",2,p(100)),white:l("hwb",1,p(100)),wblack:l("hwb",2,p(100)),cyan:l("cmyk",0,p(100)),magenta:l("cmyk",1,p(100)),yellow:l("cmyk",2,p(100)),black:l("cmyk",3,p(100)),x:l("xyz",0,p(95.047)),y:l("xyz",1,p(100)),z:l("xyz",2,p(108.833)),l:l("lab",0,p(100)),a:l("lab",1),b:l("lab",2),keyword(t){return void 0!==t?new c(t):a[this.model].keyword(this.color)},hex(t){return void 0!==t?new c(t):n.to.hex(this.rgb().round().color)},hexa(t){if(void 0!==t)return new c(t);const e=this.rgb().round().color;let r=Math.round(255*this.valpha).toString(16).toUpperCase();return 1===r.length&&(r="0"+r),n.to.hex(e)+r},rgbNumber(){const t=this.rgb().color;return(255&t[0])<<16|(255&t[1])<<8|255&t[2]},luminosity(){const t=this.rgb().color,e=[];for(const[r,n]of t.entries()){const t=n/255;e[r]=t<=.04045?t/12.92:((t+.055)/1.055)**2.4}return.2126*e[0]+.7152*e[1]+.0722*e[2]},contrast(t){const e=this.luminosity(),r=t.luminosity();return e>r?(e+.05)/(r+.05):(r+.05)/(e+.05)},level(t){const e=this.contrast(t);return e>=7?"AAA":e>=4.5?"AA":""},isDark(){const t=this.rgb().color;return(2126*t[0]+7152*t[1]+722*t[2])/1e4<128},isLight(){return!this.isDark()},negate(){const t=this.rgb();for(let e=0;e<3;e++)t.color[e]=255-t.color[e];return t},lighten(t){const e=this.hsl();return e.color[2]+=e.color[2]*t,e},darken(t){const e=this.hsl();return e.color[2]-=e.color[2]*t,e},saturate(t){const e=this.hsl();return e.color[1]+=e.color[1]*t,e},desaturate(t){const e=this.hsl();return e.color[1]-=e.color[1]*t,e},whiten(t){const e=this.hwb();return e.color[1]+=e.color[1]*t,e},blacken(t){const e=this.hwb();return e.color[2]+=e.color[2]*t,e},grayscale(){const t=this.rgb().color,e=.3*t[0]+.59*t[1]+.11*t[2];return c.rgb(e,e,e)},fade(t){return this.alpha(this.valpha-this.valpha*t)},opaquer(t){return this.alpha(this.valpha+this.valpha*t)},rotate(t){const e=this.hsl();let r=e.color[0];return r=(r+t)%360,r=r<0?360+r:r,e.color[0]=r,e},mix(t,e){if(!t||!t.rgb)throw new Error('Argument to "mix" was not a Color instance, but rather an instance of '+typeof t);const r=t.rgb(),n=this.rgb(),a=void 0===e?.5:e,o=2*a-1,i=r.alpha()-n.alpha(),s=((o*i==-1?o:(o+i)/(1+o*i))+1)/2,u=1-s;return c.rgb(s*r.red()+u*n.red(),s*r.green()+u*n.green(),s*r.blue()+u*n.blue(),r.alpha()*a+n.alpha()*(1-a))}};for(const t of Object.keys(a)){if(o.includes(t))continue;const{channels:e}=a[t];c.prototype[t]=function(...e){return this.model===t?new c(this):e.length>0?new c(e,t):new c([...(r=a[this.model][t].raw(this.color),Array.isArray(r)?r:[r]),this.valpha],t);var r},c[t]=function(...r){let n=r[0];return"number"==typeof n&&(n=h(r,e)),new c(n,t)}}function u(t){return function(e){return function(t,e){return Number(t.toFixed(e))}(e,t)}}function l(t,e,r){t=Array.isArray(t)?t:[t];for(const n of t)(s[n]||(s[n]=[]))[e]=r;return t=t[0],function(n){let a;return void 0!==n?(r&&(n=r(n)),a=this[t](),a.color[e]=n,a):(a=this[t]().color[e],r&&(a=r(a)),a)}}function p(t){return function(e){return Math.max(0,Math.min(t,e))}}function h(t,e){for(let r=0;r<e;r++)"number"!=typeof t[r]&&(t[r]=0);return t}t.exports=c},7252:(t,e,r)=>{const n=r(2037),a={};for(const t of Object.keys(n))a[n[t]]=t;const o={rgb:{channels:3,labels:"rgb"},hsl:{channels:3,labels:"hsl"},hsv:{channels:3,labels:"hsv"},hwb:{channels:3,labels:"hwb"},cmyk:{channels:4,labels:"cmyk"},xyz:{channels:3,labels:"xyz"},lab:{channels:3,labels:"lab"},lch:{channels:3,labels:"lch"},hex:{channels:1,labels:["hex"]},keyword:{channels:1,labels:["keyword"]},ansi16:{channels:1,labels:["ansi16"]},ansi256:{channels:1,labels:["ansi256"]},hcg:{channels:3,labels:["h","c","g"]},apple:{channels:3,labels:["r16","g16","b16"]},gray:{channels:1,labels:["gray"]}};t.exports=o;for(const t of Object.keys(o)){if(!("channels"in o[t]))throw new Error("missing channels property: "+t);if(!("labels"in o[t]))throw new Error("missing channel labels property: "+t);if(o[t].labels.length!==o[t].channels)throw new Error("channel and label counts mismatch: "+t);const{channels:e,labels:r}=o[t];delete o[t].channels,delete o[t].labels,Object.defineProperty(o[t],"channels",{value:e}),Object.defineProperty(o[t],"labels",{value:r})}o.rgb.hsl=function(t){const e=t[0]/255,r=t[1]/255,n=t[2]/255,a=Math.min(e,r,n),o=Math.max(e,r,n),i=o-a;let s,c;o===a?s=0:e===o?s=(r-n)/i:r===o?s=2+(n-e)/i:n===o&&(s=4+(e-r)/i),s=Math.min(60*s,360),s<0&&(s+=360);const u=(a+o)/2;return c=o===a?0:u<=.5?i/(o+a):i/(2-o-a),[s,100*c,100*u]},o.rgb.hsv=function(t){let e,r,n,a,o;const i=t[0]/255,s=t[1]/255,c=t[2]/255,u=Math.max(i,s,c),l=u-Math.min(i,s,c),p=function(t){return(u-t)/6/l+.5};return 0===l?(a=0,o=0):(o=l/u,e=p(i),r=p(s),n=p(c),i===u?a=n-r:s===u?a=1/3+e-n:c===u&&(a=2/3+r-e),a<0?a+=1:a>1&&(a-=1)),[360*a,100*o,100*u]},o.rgb.hwb=function(t){const e=t[0],r=t[1];let n=t[2];const a=o.rgb.hsl(t)[0],i=1/255*Math.min(e,Math.min(r,n));return n=1-1/255*Math.max(e,Math.max(r,n)),[a,100*i,100*n]},o.rgb.cmyk=function(t){const e=t[0]/255,r=t[1]/255,n=t[2]/255,a=Math.min(1-e,1-r,1-n);return[100*((1-e-a)/(1-a)||0),100*((1-r-a)/(1-a)||0),100*((1-n-a)/(1-a)||0),100*a]},o.rgb.keyword=function(t){const e=a[t];if(e)return e;let r,o=1/0;for(const e of Object.keys(n)){const a=n[e],c=(s=a,((i=t)[0]-s[0])**2+(i[1]-s[1])**2+(i[2]-s[2])**2);c<o&&(o=c,r=e)}var i,s;return r},o.keyword.rgb=function(t){return n[t]},o.rgb.xyz=function(t){let e=t[0]/255,r=t[1]/255,n=t[2]/255;e=e>.04045?((e+.055)/1.055)**2.4:e/12.92,r=r>.04045?((r+.055)/1.055)**2.4:r/12.92,n=n>.04045?((n+.055)/1.055)**2.4:n/12.92;return[100*(.4124*e+.3576*r+.1805*n),100*(.2126*e+.7152*r+.0722*n),100*(.0193*e+.1192*r+.9505*n)]},o.rgb.lab=function(t){const e=o.rgb.xyz(t);let r=e[0],n=e[1],a=e[2];r/=95.047,n/=100,a/=108.883,r=r>.008856?r**(1/3):7.787*r+16/116,n=n>.008856?n**(1/3):7.787*n+16/116,a=a>.008856?a**(1/3):7.787*a+16/116;return[116*n-16,500*(r-n),200*(n-a)]},o.hsl.rgb=function(t){const e=t[0]/360,r=t[1]/100,n=t[2]/100;let a,o,i;if(0===r)return i=255*n,[i,i,i];a=n<.5?n*(1+r):n+r-n*r;const s=2*n-a,c=[0,0,0];for(let t=0;t<3;t++)o=e+1/3*-(t-1),o<0&&o++,o>1&&o--,i=6*o<1?s+6*(a-s)*o:2*o<1?a:3*o<2?s+(a-s)*(2/3-o)*6:s,c[t]=255*i;return c},o.hsl.hsv=function(t){const e=t[0];let r=t[1]/100,n=t[2]/100,a=r;const o=Math.max(n,.01);n*=2,r*=n<=1?n:2-n,a*=o<=1?o:2-o;return[e,100*(0===n?2*a/(o+a):2*r/(n+r)),100*((n+r)/2)]},o.hsv.rgb=function(t){const e=t[0]/60,r=t[1]/100;let n=t[2]/100;const a=Math.floor(e)%6,o=e-Math.floor(e),i=255*n*(1-r),s=255*n*(1-r*o),c=255*n*(1-r*(1-o));switch(n*=255,a){case 0:return[n,c,i];case 1:return[s,n,i];case 2:return[i,n,c];case 3:return[i,s,n];case 4:return[c,i,n];case 5:return[n,i,s]}},o.hsv.hsl=function(t){const e=t[0],r=t[1]/100,n=t[2]/100,a=Math.max(n,.01);let o,i;i=(2-r)*n;const s=(2-r)*a;return o=r*a,o/=s<=1?s:2-s,o=o||0,i/=2,[e,100*o,100*i]},o.hwb.rgb=function(t){const e=t[0]/360;let r=t[1]/100,n=t[2]/100;const a=r+n;let o;a>1&&(r/=a,n/=a);const i=Math.floor(6*e),s=1-n;o=6*e-i,1&i&&(o=1-o);const c=r+o*(s-r);let u,l,p;switch(i){default:case 6:case 0:u=s,l=c,p=r;break;case 1:u=c,l=s,p=r;break;case 2:u=r,l=s,p=c;break;case 3:u=r,l=c,p=s;break;case 4:u=c,l=r,p=s;break;case 5:u=s,l=r,p=c}return[255*u,255*l,255*p]},o.cmyk.rgb=function(t){const e=t[0]/100,r=t[1]/100,n=t[2]/100,a=t[3]/100;return[255*(1-Math.min(1,e*(1-a)+a)),255*(1-Math.min(1,r*(1-a)+a)),255*(1-Math.min(1,n*(1-a)+a))]},o.xyz.rgb=function(t){const e=t[0]/100,r=t[1]/100,n=t[2]/100;let a,o,i;return a=3.2406*e+-1.5372*r+-.4986*n,o=-.9689*e+1.8758*r+.0415*n,i=.0557*e+-.204*r+1.057*n,a=a>.0031308?1.055*a**(1/2.4)-.055:12.92*a,o=o>.0031308?1.055*o**(1/2.4)-.055:12.92*o,i=i>.0031308?1.055*i**(1/2.4)-.055:12.92*i,a=Math.min(Math.max(0,a),1),o=Math.min(Math.max(0,o),1),i=Math.min(Math.max(0,i),1),[255*a,255*o,255*i]},o.xyz.lab=function(t){let e=t[0],r=t[1],n=t[2];e/=95.047,r/=100,n/=108.883,e=e>.008856?e**(1/3):7.787*e+16/116,r=r>.008856?r**(1/3):7.787*r+16/116,n=n>.008856?n**(1/3):7.787*n+16/116;return[116*r-16,500*(e-r),200*(r-n)]},o.lab.xyz=function(t){let e,r,n;r=(t[0]+16)/116,e=t[1]/500+r,n=r-t[2]/200;const a=r**3,o=e**3,i=n**3;return r=a>.008856?a:(r-16/116)/7.787,e=o>.008856?o:(e-16/116)/7.787,n=i>.008856?i:(n-16/116)/7.787,e*=95.047,r*=100,n*=108.883,[e,r,n]},o.lab.lch=function(t){const e=t[0],r=t[1],n=t[2];let a;a=360*Math.atan2(n,r)/2/Math.PI,a<0&&(a+=360);return[e,Math.sqrt(r*r+n*n),a]},o.lch.lab=function(t){const e=t[0],r=t[1],n=t[2]/360*2*Math.PI;return[e,r*Math.cos(n),r*Math.sin(n)]},o.rgb.ansi16=function(t,e=null){const[r,n,a]=t;let i=null===e?o.rgb.hsv(t)[2]:e;if(i=Math.round(i/50),0===i)return 30;let s=30+(Math.round(a/255)<<2|Math.round(n/255)<<1|Math.round(r/255));return 2===i&&(s+=60),s},o.hsv.ansi16=function(t){return o.rgb.ansi16(o.hsv.rgb(t),t[2])},o.rgb.ansi256=function(t){const e=t[0],r=t[1],n=t[2];if(e===r&&r===n)return e<8?16:e>248?231:Math.round((e-8)/247*24)+232;return 16+36*Math.round(e/255*5)+6*Math.round(r/255*5)+Math.round(n/255*5)},o.ansi16.rgb=function(t){let e=t%10;if(0===e||7===e)return t>50&&(e+=3.5),e=e/10.5*255,[e,e,e];const r=.5*(1+~~(t>50));return[(1&e)*r*255,(e>>1&1)*r*255,(e>>2&1)*r*255]},o.ansi256.rgb=function(t){if(t>=232){const e=10*(t-232)+8;return[e,e,e]}let e;t-=16;return[Math.floor(t/36)/5*255,Math.floor((e=t%36)/6)/5*255,e%6/5*255]},o.rgb.hex=function(t){const e=(((255&Math.round(t[0]))<<16)+((255&Math.round(t[1]))<<8)+(255&Math.round(t[2]))).toString(16).toUpperCase();return"000000".substring(e.length)+e},o.hex.rgb=function(t){const e=t.toString(16).match(/[a-f0-9]{6}|[a-f0-9]{3}/i);if(!e)return[0,0,0];let r=e[0];3===e[0].length&&(r=r.split("").map((t=>t+t)).join(""));const n=parseInt(r,16);return[n>>16&255,n>>8&255,255&n]},o.rgb.hcg=function(t){const e=t[0]/255,r=t[1]/255,n=t[2]/255,a=Math.max(Math.max(e,r),n),o=Math.min(Math.min(e,r),n),i=a-o;let s,c;return s=i<1?o/(1-i):0,c=i<=0?0:a===e?(r-n)/i%6:a===r?2+(n-e)/i:4+(e-r)/i,c/=6,c%=1,[360*c,100*i,100*s]},o.hsl.hcg=function(t){const e=t[1]/100,r=t[2]/100,n=r<.5?2*e*r:2*e*(1-r);let a=0;return n<1&&(a=(r-.5*n)/(1-n)),[t[0],100*n,100*a]},o.hsv.hcg=function(t){const e=t[1]/100,r=t[2]/100,n=e*r;let a=0;return n<1&&(a=(r-n)/(1-n)),[t[0],100*n,100*a]},o.hcg.rgb=function(t){const e=t[0]/360,r=t[1]/100,n=t[2]/100;if(0===r)return[255*n,255*n,255*n];const a=[0,0,0],o=e%1*6,i=o%1,s=1-i;let c=0;switch(Math.floor(o)){case 0:a[0]=1,a[1]=i,a[2]=0;break;case 1:a[0]=s,a[1]=1,a[2]=0;break;case 2:a[0]=0,a[1]=1,a[2]=i;break;case 3:a[0]=0,a[1]=s,a[2]=1;break;case 4:a[0]=i,a[1]=0,a[2]=1;break;default:a[0]=1,a[1]=0,a[2]=s}return c=(1-r)*n,[255*(r*a[0]+c),255*(r*a[1]+c),255*(r*a[2]+c)]},o.hcg.hsv=function(t){const e=t[1]/100,r=e+t[2]/100*(1-e);let n=0;return r>0&&(n=e/r),[t[0],100*n,100*r]},o.hcg.hsl=function(t){const e=t[1]/100,r=t[2]/100*(1-e)+.5*e;let n=0;return r>0&&r<.5?n=e/(2*r):r>=.5&&r<1&&(n=e/(2*(1-r))),[t[0],100*n,100*r]},o.hcg.hwb=function(t){const e=t[1]/100,r=e+t[2]/100*(1-e);return[t[0],100*(r-e),100*(1-r)]},o.hwb.hcg=function(t){const e=t[1]/100,r=1-t[2]/100,n=r-e;let a=0;return n<1&&(a=(r-n)/(1-n)),[t[0],100*n,100*a]},o.apple.rgb=function(t){return[t[0]/65535*255,t[1]/65535*255,t[2]/65535*255]},o.rgb.apple=function(t){return[t[0]/255*65535,t[1]/255*65535,t[2]/255*65535]},o.gray.rgb=function(t){return[t[0]/100*255,t[0]/100*255,t[0]/100*255]},o.gray.hsl=function(t){return[0,0,t[0]]},o.gray.hsv=o.gray.hsl,o.gray.hwb=function(t){return[0,100,t[0]]},o.gray.cmyk=function(t){return[0,0,0,t[0]]},o.gray.lab=function(t){return[t[0],0,0]},o.gray.hex=function(t){const e=255&Math.round(t[0]/100*255),r=((e<<16)+(e<<8)+e).toString(16).toUpperCase();return"000000".substring(r.length)+r},o.rgb.gray=function(t){return[(t[0]+t[1]+t[2])/3/255*100]}},8349:(t,e,r)=>{const n=r(7252),a=r(4196),o={};Object.keys(n).forEach((t=>{o[t]={},Object.defineProperty(o[t],"channels",{value:n[t].channels}),Object.defineProperty(o[t],"labels",{value:n[t].labels});const e=a(t);Object.keys(e).forEach((r=>{const n=e[r];o[t][r]=function(t){const e=function(...e){const r=e[0];if(null==r)return r;r.length>1&&(e=r);const n=t(e);if("object"==typeof n)for(let t=n.length,e=0;e<t;e++)n[e]=Math.round(n[e]);return n};return"conversion"in t&&(e.conversion=t.conversion),e}(n),o[t][r].raw=function(t){const e=function(...e){const r=e[0];return null==r?r:(r.length>1&&(e=r),t(e))};return"conversion"in t&&(e.conversion=t.conversion),e}(n)}))})),t.exports=o},4196:(t,e,r)=>{const n=r(7252);function a(t){const e=function(){const t={},e=Object.keys(n);for(let r=e.length,n=0;n<r;n++)t[e[n]]={distance:-1,parent:null};return t}(),r=[t];for(e[t].distance=0;r.length;){const t=r.pop(),a=Object.keys(n[t]);for(let n=a.length,o=0;o<n;o++){const n=a[o],i=e[n];-1===i.distance&&(i.distance=e[t].distance+1,i.parent=t,r.unshift(n))}}return e}function o(t,e){return function(r){return e(t(r))}}function i(t,e){const r=[e[t].parent,t];let a=n[e[t].parent][t],i=e[t].parent;for(;e[i].parent;)r.unshift(e[i].parent),a=o(n[e[i].parent][i],a),i=e[i].parent;return a.conversion=r,a}t.exports=function(t){const e=a(t),r={},n=Object.keys(e);for(let t=n.length,a=0;a<t;a++){const t=n[a];null!==e[t].parent&&(r[t]=i(t,e))}return r}},2037:t=>{"use strict";t.exports={aliceblue:[240,248,255],antiquewhite:[250,235,215],aqua:[0,255,255],aquamarine:[127,255,212],azure:[240,255,255],beige:[245,245,220],bisque:[255,228,196],black:[0,0,0],blanchedalmond:[255,235,205],blue:[0,0,255],blueviolet:[138,43,226],brown:[165,42,42],burlywood:[222,184,135],cadetblue:[95,158,160],chartreuse:[127,255,0],chocolate:[210,105,30],coral:[255,127,80],cornflowerblue:[100,149,237],cornsilk:[255,248,220],crimson:[220,20,60],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgoldenrod:[184,134,11],darkgray:[169,169,169],darkgreen:[0,100,0],darkgrey:[169,169,169],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkseagreen:[143,188,143],darkslateblue:[72,61,139],darkslategray:[47,79,79],darkslategrey:[47,79,79],darkturquoise:[0,206,209],darkviolet:[148,0,211],deeppink:[255,20,147],deepskyblue:[0,191,255],dimgray:[105,105,105],dimgrey:[105,105,105],dodgerblue:[30,144,255],firebrick:[178,34,34],floralwhite:[255,250,240],forestgreen:[34,139,34],fuchsia:[255,0,255],gainsboro:[220,220,220],ghostwhite:[248,248,255],gold:[255,215,0],goldenrod:[218,165,32],gray:[128,128,128],green:[0,128,0],greenyellow:[173,255,47],grey:[128,128,128],honeydew:[240,255,240],hotpink:[255,105,180],indianred:[205,92,92],indigo:[75,0,130],ivory:[255,255,240],khaki:[240,230,140],lavender:[230,230,250],lavenderblush:[255,240,245],lawngreen:[124,252,0],lemonchiffon:[255,250,205],lightblue:[173,216,230],lightcoral:[240,128,128],lightcyan:[224,255,255],lightgoldenrodyellow:[250,250,210],lightgray:[211,211,211],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightsalmon:[255,160,122],lightseagreen:[32,178,170],lightskyblue:[135,206,250],lightslategray:[119,136,153],lightslategrey:[119,136,153],lightsteelblue:[176,196,222],lightyellow:[255,255,224],lime:[0,255,0],limegreen:[50,205,50],linen:[250,240,230],magenta:[255,0,255],maroon:[128,0,0],mediumaquamarine:[102,205,170],mediumblue:[0,0,205],mediumorchid:[186,85,211],mediumpurple:[147,112,219],mediumseagreen:[60,179,113],mediumslateblue:[123,104,238],mediumspringgreen:[0,250,154],mediumturquoise:[72,209,204],mediumvioletred:[199,21,133],midnightblue:[25,25,112],mintcream:[245,255,250],mistyrose:[255,228,225],moccasin:[255,228,181],navajowhite:[255,222,173],navy:[0,0,128],oldlace:[253,245,230],olive:[128,128,0],olivedrab:[107,142,35],orange:[255,165,0],orangered:[255,69,0],orchid:[218,112,214],palegoldenrod:[238,232,170],palegreen:[152,251,152],paleturquoise:[175,238,238],palevioletred:[219,112,147],papayawhip:[255,239,213],peachpuff:[255,218,185],peru:[205,133,63],pink:[255,192,203],plum:[221,160,221],powderblue:[176,224,230],purple:[128,0,128],rebeccapurple:[102,51,153],red:[255,0,0],rosybrown:[188,143,143],royalblue:[65,105,225],saddlebrown:[139,69,19],salmon:[250,128,114],sandybrown:[244,164,96],seagreen:[46,139,87],seashell:[255,245,238],sienna:[160,82,45],silver:[192,192,192],skyblue:[135,206,235],slateblue:[106,90,205],slategray:[112,128,144],slategrey:[112,128,144],snow:[255,250,250],springgreen:[0,255,127],steelblue:[70,130,180],tan:[210,180,140],teal:[0,128,128],thistle:[216,191,216],tomato:[255,99,71],turquoise:[64,224,208],violet:[238,130,238],wheat:[245,222,179],white:[255,255,255],whitesmoke:[245,245,245],yellow:[255,255,0],yellowgreen:[154,205,50]}},7853:(t,e,r)=>{var n=r(7424),a=r(2882),o=TypeError;t.exports=function(t){if(n(t))return t;throw o(a(t)+" is not a function")}},2197:(t,e,r)=>{var n=r(7086),a=r(2882),o=TypeError;t.exports=function(t){if(n(t))return t;throw o(a(t)+" is not a constructor")}},6637:(t,e,r)=>{var n=r(7424),a=String,o=TypeError;t.exports=function(t){if("object"==typeof t||n(t))return t;throw o("Can't set "+a(t)+" as a prototype")}},990:(t,e,r)=>{var n=r(7166),a=r(3981),o=r(7614).f,i=n("unscopables"),s=Array.prototype;null==s[i]&&o(s,i,{configurable:!0,value:a(null)}),t.exports=function(t){s[i][t]=!0}},2577:(t,e,r)=>{"use strict";var n=r(520).charAt;t.exports=function(t,e,r){return e+(r?n(t,e).length:1)}},1326:(t,e,r)=>{var n=r(6827),a=String,o=TypeError;t.exports=function(t){if(n(t))return t;throw o(a(t)+" is not an object")}},3321:(t,e,r)=>{var n=r(8462);t.exports=n((function(){if("function"==typeof ArrayBuffer){var t=new ArrayBuffer(8);Object.isExtensible(t)&&Object.defineProperty(t,"a",{value:8})}}))},9352:(t,e,r)=>{"use strict";var n=r(5860).forEach,a=r(8809)("forEach");t.exports=a?[].forEach:function(t){return n(this,t,arguments.length>1?arguments[1]:void 0)}},1871:(t,e,r)=>{"use strict";var n=r(8273),a=r(5244),o=r(4440),i=r(9448),s=r(9254),c=r(7086),u=r(7825),l=r(1861),p=r(898),h=r(3054),d=Array;t.exports=function(t){var e=o(t),r=c(this),f=arguments.length,v=f>1?arguments[1]:void 0,m=void 0!==v;m&&(v=n(v,f>2?arguments[2]:void 0));var b,g,y,k,w,C,S=h(e),x=0;if(!S||this===d&&s(S))for(b=u(e),g=r?new this(b):d(b);b>x;x++)C=m?v(e[x],x):e[x],l(g,x,C);else for(w=(k=p(e,S)).next,g=r?new this:[];!(y=a(w,k)).done;x++)C=m?i(k,v,[y.value,x],!0):y.value,l(g,x,C);return g.length=x,g}},5254:(t,e,r)=>{var n=r(3016),a=r(847),o=r(7825),i=function(t){return function(e,r,i){var s,c=n(e),u=o(c),l=a(i,u);if(t&&r!=r){for(;u>l;)if((s=c[l++])!=s)return!0}else for(;u>l;l++)if((t||l in c)&&c[l]===r)return t||l||0;return!t&&-1}};t.exports={includes:i(!0),indexOf:i(!1)}},5860:(t,e,r)=>{var n=r(8273),a=r(9709),o=r(8188),i=r(4440),s=r(7825),c=r(5309),u=a([].push),l=function(t){var e=1==t,r=2==t,a=3==t,l=4==t,p=6==t,h=7==t,d=5==t||p;return function(f,v,m,b){for(var g,y,k=i(f),w=o(k),C=n(v,m),S=s(w),x=0,O=b||c,E=e?O(f,S):r||h?O(f,0):void 0;S>x;x++)if((d||x in w)&&(y=C(g=w[x],x,k),t))if(e)E[x]=y;else if(y)switch(t){case 3:return!0;case 5:return g;case 6:return x;case 2:u(E,g)}else switch(t){case 4:return!1;case 7:u(E,g)}return p?-1:a||l?l:E}};t.exports={forEach:l(0),map:l(1),filter:l(2),some:l(3),every:l(4),find:l(5),findIndex:l(6),filterReject:l(7)}},6894:(t,e,r)=>{var n=r(8462),a=r(7166),o=r(5649),i=a("species");t.exports=function(t){return o>=51||!n((function(){var e=[];return(e.constructor={})[i]=function(){return{foo:1}},1!==e[t](Boolean).foo}))}},8809:(t,e,r)=>{"use strict";var n=r(8462);t.exports=function(t,e){var r=[][t];return!!r&&n((function(){r.call(null,e||function(){return 1},1)}))}},9292:(t,e,r)=>{var n=r(847),a=r(7825),o=r(1861),i=Array,s=Math.max;t.exports=function(t,e,r){for(var c=a(t),u=n(e,c),l=n(void 0===r?c:r,c),p=i(s(l-u,0)),h=0;u<l;u++,h++)o(p,h,t[u]);return p.length=h,p}},3653:(t,e,r)=>{var n=r(9709);t.exports=n([].slice)},5384:(t,e,r)=>{var n=r(3115),a=r(7086),o=r(6827),i=r(7166)("species"),s=Array;t.exports=function(t){var e;return n(t)&&(e=t.constructor,(a(e)&&(e===s||n(e.prototype))||o(e)&&null===(e=e[i]))&&(e=void 0)),void 0===e?s:e}},5309:(t,e,r)=>{var n=r(5384);t.exports=function(t,e){return new(n(t))(0===e?0:e)}},9448:(t,e,r)=>{var n=r(1326),a=r(872);t.exports=function(t,e,r,o){try{return o?e(n(r)[0],r[1]):e(r)}catch(e){a(t,"throw",e)}}},5239:(t,e,r)=>{var n=r(7166)("iterator"),a=!1;try{var o=0,i={next:function(){return{done:!!o++}},return:function(){a=!0}};i[n]=function(){return this},Array.from(i,(function(){throw 2}))}catch(t){}t.exports=function(t,e){if(!e&&!a)return!1;var r=!1;try{var o={};o[n]=function(){return{next:function(){return{done:r=!0}}}},t(o)}catch(t){}return r}},661:(t,e,r)=>{var n=r(9709),a=n({}.toString),o=n("".slice);t.exports=function(t){return o(a(t),8,-1)}},9794:(t,e,r)=>{var n=r(3557),a=r(7424),o=r(661),i=r(7166)("toStringTag"),s=Object,c="Arguments"==o(function(){return arguments}());t.exports=n?o:function(t){var e,r,n;return void 0===t?"Undefined":null===t?"Null":"string"==typeof(r=function(t,e){try{return t[e]}catch(t){}}(e=s(t),i))?r:c?o(e):"Object"==(n=o(e))&&a(e.callee)?"Arguments":n}},9621:(t,e,r)=>{var n=r(5110),a=r(1804),o=r(172),i=r(7614);t.exports=function(t,e,r){for(var s=a(e),c=i.f,u=o.f,l=0;l<s.length;l++){var p=s[l];n(t,p)||r&&n(r,p)||c(t,p,u(e,p))}}},6036:(t,e,r)=>{var n=r(8462);t.exports=!n((function(){function t(){}return t.prototype.constructor=null,Object.getPrototypeOf(new t)!==t.prototype}))},3087:(t,e,r)=>{"use strict";var n=r(3774).IteratorPrototype,a=r(3981),o=r(9367),i=r(5142),s=r(7240),c=function(){return this};t.exports=function(t,e,r,u){var l=e+" Iterator";return t.prototype=a(n,{next:o(+!u,r)}),i(t,l,!1,!0),s[l]=c,t}},4100:(t,e,r)=>{var n=r(6953),a=r(7614),o=r(9367);t.exports=n?function(t,e,r){return a.f(t,e,o(1,r))}:function(t,e,r){return t[e]=r,t}},9367:t=>{t.exports=function(t,e){return{enumerable:!(1&t),configurable:!(2&t),writable:!(4&t),value:e}}},1861:(t,e,r)=>{"use strict";var n=r(8756),a=r(7614),o=r(9367);t.exports=function(t,e,r){var i=n(e);i in t?a.f(t,i,o(0,r)):t[i]=r}},2297:(t,e,r)=>{var n=r(7424),a=r(7614),o=r(5390),i=r(6530);t.exports=function(t,e,r,s){s||(s={});var c=s.enumerable,u=void 0!==s.name?s.name:e;if(n(r)&&o(r,u,s),s.global)c?t[e]=r:i(e,r);else{try{s.unsafe?t[e]&&(c=!0):delete t[e]}catch(t){}c?t[e]=r:a.f(t,e,{value:r,enumerable:!1,configurable:!s.nonConfigurable,writable:!s.nonWritable})}return t}},6530:(t,e,r)=>{var n=r(2756),a=Object.defineProperty;t.exports=function(t,e){try{a(n,t,{value:e,configurable:!0,writable:!0})}catch(r){n[t]=e}return e}},2527:(t,e,r)=>{"use strict";var n=r(5689),a=r(5244),o=r(7690),i=r(7707),s=r(7424),c=r(3087),u=r(6166),l=r(4322),p=r(5142),h=r(4100),d=r(2297),f=r(7166),v=r(7240),m=r(3774),b=i.PROPER,g=i.CONFIGURABLE,y=m.IteratorPrototype,k=m.BUGGY_SAFARI_ITERATORS,w=f("iterator"),C="keys",S="values",x="entries",O=function(){return this};t.exports=function(t,e,r,i,f,m,E){c(r,e,i);var L,P,A,T=function(t){if(t===f&&B)return B;if(!k&&t in M)return M[t];switch(t){case C:case S:case x:return function(){return new r(this,t)}}return function(){return new r(this)}},j=e+" Iterator",I=!1,M=t.prototype,F=M[w]||M["@@iterator"]||f&&M[f],B=!k&&F||T(f),R="Array"==e&&M.entries||F;if(R&&(L=u(R.call(new t)))!==Object.prototype&&L.next&&(o||u(L)===y||(l?l(L,y):s(L[w])||d(L,w,O)),p(L,j,!0,!0),o&&(v[j]=O)),b&&f==S&&F&&F.name!==S&&(!o&&g?h(M,"name",S):(I=!0,B=function(){return a(F,this)})),f)if(P={values:T(S),keys:m?B:T(C),entries:T(x)},E)for(A in P)(k||I||!(A in M))&&d(M,A,P[A]);else n({target:e,proto:!0,forced:k||I},P);return o&&!E||M[w]===B||d(M,w,B,{name:f}),v[e]=B,P}},4300:(t,e,r)=>{var n=r(8844),a=r(5110),o=r(766),i=r(7614).f;t.exports=function(t){var e=n.Symbol||(n.Symbol={});a(e,t)||i(e,t,{value:o.f(t)})}},6953:(t,e,r)=>{var n=r(8462);t.exports=!n((function(){return 7!=Object.defineProperty({},1,{get:function(){return 7}})[1]}))},3018:(t,e,r)=>{var n=r(2756),a=r(6827),o=n.document,i=a(o)&&a(o.createElement);t.exports=function(t){return i?o.createElement(t):{}}},4958:t=>{var e=TypeError;t.exports=function(t){if(t>9007199254740991)throw e("Maximum allowed index exceeded");return t}},8993:t=>{t.exports={CSSRuleList:0,CSSStyleDeclaration:0,CSSValueList:0,ClientRectList:0,DOMRectList:0,DOMStringList:0,DOMTokenList:1,DataTransferItemList:0,FileList:0,HTMLAllCollection:0,HTMLCollection:0,HTMLFormElement:0,HTMLSelectElement:0,MediaList:0,MimeTypeArray:0,NamedNodeMap:0,NodeList:1,PaintRequestList:0,Plugin:0,PluginArray:0,SVGLengthList:0,SVGNumberList:0,SVGPathSegList:0,SVGPointList:0,SVGStringList:0,SVGTransformList:0,SourceBufferList:0,StyleSheetList:0,TextTrackCueList:0,TextTrackList:0,TouchList:0}},7891:(t,e,r)=>{var n=r(3018)("span").classList,a=n&&n.constructor&&n.constructor.prototype;t.exports=a===Object.prototype?void 0:a},5441:(t,e,r)=>{var n=r(5072);t.exports=n("navigator","userAgent")||""},5649:(t,e,r)=>{var n,a,o=r(2756),i=r(5441),s=o.process,c=o.Deno,u=s&&s.versions||c&&c.version,l=u&&u.v8;l&&(a=(n=l.split("."))[0]>0&&n[0]<4?1:+(n[0]+n[1])),!a&&i&&(!(n=i.match(/Edge\/(\d+)/))||n[1]>=74)&&(n=i.match(/Chrome\/(\d+)/))&&(a=+n[1]),t.exports=a},4798:t=>{t.exports=["constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","valueOf"]},5689:(t,e,r)=>{var n=r(2756),a=r(172).f,o=r(4100),i=r(2297),s=r(6530),c=r(9621),u=r(5149);t.exports=function(t,e){var r,l,p,h,d,f=t.target,v=t.global,m=t.stat;if(r=v?n:m?n[f]||s(f,{}):(n[f]||{}).prototype)for(l in e){if(h=e[l],p=t.dontCallGetSet?(d=a(r,l))&&d.value:r[l],!u(v?l:f+(m?".":"#")+l,t.forced)&&void 0!==p){if(typeof h==typeof p)continue;c(h,p)}(t.sham||p&&p.sham)&&o(h,"sham",!0),i(r,l,h,t)}}},8462:t=>{t.exports=function(t){try{return!!t()}catch(t){return!0}}},6031:(t,e,r)=>{"use strict";r(3188);var n=r(9709),a=r(2297),o=r(5866),i=r(8462),s=r(7166),c=r(4100),u=s("species"),l=RegExp.prototype;t.exports=function(t,e,r,p){var h=s(t),d=!i((function(){var e={};return e[h]=function(){return 7},7!=""[t](e)})),f=d&&!i((function(){var e=!1,r=/a/;return"split"===t&&((r={}).constructor={},r.constructor[u]=function(){return r},r.flags="",r[h]=/./[h]),r.exec=function(){return e=!0,null},r[h](""),!e}));if(!d||!f||r){var v=n(/./[h]),m=e(h,""[t],(function(t,e,r,a,i){var s=n(t),c=e.exec;return c===o||c===l.exec?d&&!i?{done:!0,value:v(e,r,a)}:{done:!0,value:s(r,e,a)}:{done:!1}}));a(String.prototype,t,m[0]),a(l,h,m[1])}p&&c(l[h],"sham",!0)}},9947:(t,e,r)=>{var n=r(8462);t.exports=!n((function(){return Object.isExtensible(Object.preventExtensions({}))}))},7322:(t,e,r)=>{var n=r(323),a=Function.prototype,o=a.apply,i=a.call;t.exports="object"==typeof Reflect&&Reflect.apply||(n?i.bind(o):function(){return i.apply(o,arguments)})},8273:(t,e,r)=>{var n=r(9709),a=r(7853),o=r(323),i=n(n.bind);t.exports=function(t,e){return a(t),void 0===e?t:o?i(t,e):function(){return t.apply(e,arguments)}}},323:(t,e,r)=>{var n=r(8462);t.exports=!n((function(){var t=function(){}.bind();return"function"!=typeof t||t.hasOwnProperty("prototype")}))},8631:(t,e,r)=>{"use strict";var n=r(9709),a=r(7853),o=r(6827),i=r(5110),s=r(3653),c=r(323),u=Function,l=n([].concat),p=n([].join),h={};t.exports=c?u.bind:function(t){var e=a(this),r=e.prototype,n=s(arguments,1),c=function(){var r=l(n,s(arguments));return this instanceof c?function(t,e,r){if(!i(h,e)){for(var n=[],a=0;a<e;a++)n[a]="a["+a+"]";h[e]=u("C,a","return new C("+p(n,",")+")")}return h[e](t,r)}(e,r.length,r):e.apply(t,r)};return o(r)&&(c.prototype=r),c}},5244:(t,e,r)=>{var n=r(323),a=Function.prototype.call;t.exports=n?a.bind(a):function(){return a.apply(a,arguments)}},7707:(t,e,r)=>{var n=r(6953),a=r(5110),o=Function.prototype,i=n&&Object.getOwnPropertyDescriptor,s=a(o,"name"),c=s&&"something"===function(){}.name,u=s&&(!n||n&&i(o,"name").configurable);t.exports={EXISTS:s,PROPER:c,CONFIGURABLE:u}},9709:(t,e,r)=>{var n=r(323),a=Function.prototype,o=a.bind,i=a.call,s=n&&o.bind(i,i);t.exports=n?function(t){return t&&s(t)}:function(t){return t&&function(){return i.apply(t,arguments)}}},5072:(t,e,r)=>{var n=r(2756),a=r(7424);t.exports=function(t,e){return arguments.length<2?(r=n[t],a(r)?r:void 0):n[t]&&n[t][e];var r}},3054:(t,e,r)=>{var n=r(9794),a=r(125),o=r(7240),i=r(7166)("iterator");t.exports=function(t){if(null!=t)return a(t,i)||a(t,"@@iterator")||o[n(t)]}},898:(t,e,r)=>{var n=r(5244),a=r(7853),o=r(1326),i=r(2882),s=r(3054),c=TypeError;t.exports=function(t,e){var r=arguments.length<2?s(t):e;if(a(r))return o(n(r,t));throw c(i(t)+" is not iterable")}},125:(t,e,r)=>{var n=r(7853);t.exports=function(t,e){var r=t[e];return null==r?void 0:n(r)}},5285:(t,e,r)=>{var n=r(9709),a=r(4440),o=Math.floor,i=n("".charAt),s=n("".replace),c=n("".slice),u=/\$([$&'`]|\d{1,2}|<[^>]*>)/g,l=/\$([$&'`]|\d{1,2})/g;t.exports=function(t,e,r,n,p,h){var d=r+t.length,f=n.length,v=l;return void 0!==p&&(p=a(p),v=u),s(h,v,(function(a,s){var u;switch(i(s,0)){case"$":return"$";case"&":return t;case"`":return c(e,0,r);case"'":return c(e,d);case"<":u=p[c(s,1,-1)];break;default:var l=+s;if(0===l)return a;if(l>f){var h=o(l/10);return 0===h?a:h<=f?void 0===n[h-1]?i(s,1):n[h-1]+i(s,1):a}u=n[l-1]}return void 0===u?"":u}))}},2756:(t,e,r)=>{var n=function(t){return t&&t.Math==Math&&t};t.exports=n("object"==typeof globalThis&&globalThis)||n("object"==typeof window&&window)||n("object"==typeof self&&self)||n("object"==typeof r.g&&r.g)||function(){return this}()||Function("return this")()},5110:(t,e,r)=>{var n=r(9709),a=r(4440),o=n({}.hasOwnProperty);t.exports=Object.hasOwn||function(t,e){return o(a(t),e)}},3472:t=>{t.exports={}},1198:(t,e,r)=>{var n=r(5072);t.exports=n("document","documentElement")},7106:(t,e,r)=>{var n=r(6953),a=r(8462),o=r(3018);t.exports=!n&&!a((function(){return 7!=Object.defineProperty(o("div"),"a",{get:function(){return 7}}).a}))},8188:(t,e,r)=>{var n=r(9709),a=r(8462),o=r(661),i=Object,s=n("".split);t.exports=a((function(){return!i("z").propertyIsEnumerable(0)}))?function(t){return"String"==o(t)?s(t,""):i(t)}:i},1633:(t,e,r)=>{var n=r(9709),a=r(7424),o=r(410),i=n(Function.toString);a(o.inspectSource)||(o.inspectSource=function(t){return i(t)}),t.exports=o.inspectSource},2634:(t,e,r)=>{var n=r(5689),a=r(9709),o=r(3472),i=r(6827),s=r(5110),c=r(7614).f,u=r(4161),l=r(4113),p=r(5395),h=r(637),d=r(9947),f=!1,v=h("meta"),m=0,b=function(t){c(t,v,{value:{objectID:"O"+m++,weakData:{}}})},g=t.exports={enable:function(){g.enable=function(){},f=!0;var t=u.f,e=a([].splice),r={};r[v]=1,t(r).length&&(u.f=function(r){for(var n=t(r),a=0,o=n.length;a<o;a++)if(n[a]===v){e(n,a,1);break}return n},n({target:"Object",stat:!0,forced:!0},{getOwnPropertyNames:l.f}))},fastKey:function(t,e){if(!i(t))return"symbol"==typeof t?t:("string"==typeof t?"S":"P")+t;if(!s(t,v)){if(!p(t))return"F";if(!e)return"E";b(t)}return t[v].objectID},getWeakData:function(t,e){if(!s(t,v)){if(!p(t))return!0;if(!e)return!1;b(t)}return t[v].weakData},onFreeze:function(t){return d&&f&&p(t)&&!s(t,v)&&b(t),t}};o[v]=!0},3962:(t,e,r)=>{var n,a,o,i=r(1602),s=r(2756),c=r(9709),u=r(6827),l=r(4100),p=r(5110),h=r(410),d=r(5868),f=r(3472),v="Object already initialized",m=s.TypeError,b=s.WeakMap;if(i||h.state){var g=h.state||(h.state=new b),y=c(g.get),k=c(g.has),w=c(g.set);n=function(t,e){if(k(g,t))throw new m(v);return e.facade=t,w(g,t,e),e},a=function(t){return y(g,t)||{}},o=function(t){return k(g,t)}}else{var C=d("state");f[C]=!0,n=function(t,e){if(p(t,C))throw new m(v);return e.facade=t,l(t,C,e),e},a=function(t){return p(t,C)?t[C]:{}},o=function(t){return p(t,C)}}t.exports={set:n,get:a,has:o,enforce:function(t){return o(t)?a(t):n(t,{})},getterFor:function(t){return function(e){var r;if(!u(e)||(r=a(e)).type!==t)throw m("Incompatible receiver, "+t+" required");return r}}}},9254:(t,e,r)=>{var n=r(7166),a=r(7240),o=n("iterator"),i=Array.prototype;t.exports=function(t){return void 0!==t&&(a.Array===t||i[o]===t)}},3115:(t,e,r)=>{var n=r(661);t.exports=Array.isArray||function(t){return"Array"==n(t)}},7424:t=>{t.exports=function(t){return"function"==typeof t}},7086:(t,e,r)=>{var n=r(9709),a=r(8462),o=r(7424),i=r(9794),s=r(5072),c=r(1633),u=function(){},l=[],p=s("Reflect","construct"),h=/^\s*(?:class|function)\b/,d=n(h.exec),f=!h.exec(u),v=function(t){if(!o(t))return!1;try{return p(u,l,t),!0}catch(t){return!1}},m=function(t){if(!o(t))return!1;switch(i(t)){case"AsyncFunction":case"GeneratorFunction":case"AsyncGeneratorFunction":return!1}try{return f||!!d(h,c(t))}catch(t){return!0}};m.sham=!0,t.exports=!p||a((function(){var t;return v(v.call)||!v(Object)||!v((function(){t=!0}))||t}))?m:v},5149:(t,e,r)=>{var n=r(8462),a=r(7424),o=/#|\.prototype\./,i=function(t,e){var r=c[s(t)];return r==l||r!=u&&(a(e)?n(e):!!e)},s=i.normalize=function(t){return String(t).replace(o,".").toLowerCase()},c=i.data={},u=i.NATIVE="N",l=i.POLYFILL="P";t.exports=i},6827:(t,e,r)=>{var n=r(7424);t.exports=function(t){return"object"==typeof t?null!==t:n(t)}},7690:t=>{t.exports=!1},5237:(t,e,r)=>{var n=r(6827),a=r(661),o=r(7166)("match");t.exports=function(t){var e;return n(t)&&(void 0!==(e=t[o])?!!e:"RegExp"==a(t))}},6396:(t,e,r)=>{var n=r(5072),a=r(7424),o=r(6830),i=r(4717),s=Object;t.exports=i?function(t){return"symbol"==typeof t}:function(t){var e=n("Symbol");return a(e)&&o(e.prototype,s(t))}},872:(t,e,r)=>{var n=r(5244),a=r(1326),o=r(125);t.exports=function(t,e,r){var i,s;a(t);try{if(!(i=o(t,"return"))){if("throw"===e)throw r;return r}i=n(i,t)}catch(t){s=!0,i=t}if("throw"===e)throw r;if(s)throw i;return a(i),r}},3774:(t,e,r)=>{"use strict";var n,a,o,i=r(8462),s=r(7424),c=r(3981),u=r(6166),l=r(2297),p=r(7166),h=r(7690),d=p("iterator"),f=!1;[].keys&&("next"in(o=[].keys())?(a=u(u(o)))!==Object.prototype&&(n=a):f=!0),null==n||i((function(){var t={};return n[d].call(t)!==t}))?n={}:h&&(n=c(n)),s(n[d])||l(n,d,(function(){return this})),t.exports={IteratorPrototype:n,BUGGY_SAFARI_ITERATORS:f}},7240:t=>{t.exports={}},7825:(t,e,r)=>{var n=r(9223);t.exports=function(t){return n(t.length)}},5390:(t,e,r)=>{var n=r(8462),a=r(7424),o=r(5110),i=r(6953),s=r(7707).CONFIGURABLE,c=r(1633),u=r(3962),l=u.enforce,p=u.get,h=Object.defineProperty,d=i&&!n((function(){return 8!==h((function(){}),"length",{value:8}).length})),f=String(String).split("String"),v=t.exports=function(t,e,r){"Symbol("===String(e).slice(0,7)&&(e="["+String(e).replace(/^Symbol\(([^)]*)\)/,"$1")+"]"),r&&r.getter&&(e="get "+e),r&&r.setter&&(e="set "+e),(!o(t,"name")||s&&t.name!==e)&&(i?h(t,"name",{value:e,configurable:!0}):t.name=e),d&&r&&o(r,"arity")&&t.length!==r.arity&&h(t,"length",{value:r.arity});try{r&&o(r,"constructor")&&r.constructor?i&&h(t,"prototype",{writable:!1}):t.prototype&&(t.prototype=void 0)}catch(t){}var n=l(t);return o(n,"source")||(n.source=f.join("string"==typeof e?e:"")),t};Function.prototype.toString=v((function(){return a(this)&&p(this).source||c(this)}),"toString")},1298:t=>{var e=Math.ceil,r=Math.floor;t.exports=Math.trunc||function(t){var n=+t;return(n>0?r:e)(n)}},3025:(t,e,r)=>{var n=r(3165);t.exports=n&&!!Symbol.for&&!!Symbol.keyFor},3165:(t,e,r)=>{var n=r(5649),a=r(8462);t.exports=!!Object.getOwnPropertySymbols&&!a((function(){var t=Symbol();return!String(t)||!(Object(t)instanceof Symbol)||!Symbol.sham&&n&&n<41}))},1602:(t,e,r)=>{var n=r(2756),a=r(7424),o=r(1633),i=n.WeakMap;t.exports=a(i)&&/native code/.test(o(i))},9484:(t,e,r)=>{"use strict";var n=r(6953),a=r(9709),o=r(5244),i=r(8462),s=r(4565),c=r(864),u=r(6492),l=r(4440),p=r(8188),h=Object.assign,d=Object.defineProperty,f=a([].concat);t.exports=!h||i((function(){if(n&&1!==h({b:1},h(d({},"a",{enumerable:!0,get:function(){d(this,"b",{value:3,enumerable:!1})}}),{b:2})).b)return!0;var t={},e={},r=Symbol(),a="abcdefghijklmnopqrst";return t[r]=7,a.split("").forEach((function(t){e[t]=t})),7!=h({},t)[r]||s(h({},e)).join("")!=a}))?function(t,e){for(var r=l(t),a=arguments.length,i=1,h=c.f,d=u.f;a>i;)for(var v,m=p(arguments[i++]),b=h?f(s(m),h(m)):s(m),g=b.length,y=0;g>y;)v=b[y++],n&&!o(d,m,v)||(r[v]=m[v]);return r}:h},3981:(t,e,r)=>{var n,a=r(1326),o=r(6434),i=r(4798),s=r(3472),c=r(1198),u=r(3018),l=r(5868),p="prototype",h="script",d=l("IE_PROTO"),f=function(){},v=function(t){return"<"+h+">"+t+"</"+h+">"},m=function(t){t.write(v("")),t.close();var e=t.parentWindow.Object;return t=null,e},b=function(){try{n=new ActiveXObject("htmlfile")}catch(t){}var t,e,r;b="undefined"!=typeof document?document.domain&&n?m(n):(e=u("iframe"),r="java"+h+":",e.style.display="none",c.appendChild(e),e.src=String(r),(t=e.contentWindow.document).open(),t.write(v("document.F=Object")),t.close(),t.F):m(n);for(var a=i.length;a--;)delete b[p][i[a]];return b()};s[d]=!0,t.exports=Object.create||function(t,e){var r;return null!==t?(f[p]=a(t),r=new f,f[p]=null,r[d]=t):r=b(),void 0===e?r:o.f(r,e)}},6434:(t,e,r)=>{var n=r(6953),a=r(5679),o=r(7614),i=r(1326),s=r(3016),c=r(4565);e.f=n&&!a?Object.defineProperties:function(t,e){i(t);for(var r,n=s(e),a=c(e),u=a.length,l=0;u>l;)o.f(t,r=a[l++],n[r]);return t}},7614:(t,e,r)=>{var n=r(6953),a=r(7106),o=r(5679),i=r(1326),s=r(8756),c=TypeError,u=Object.defineProperty,l=Object.getOwnPropertyDescriptor,p="enumerable",h="configurable",d="writable";e.f=n?o?function(t,e,r){if(i(t),e=s(e),i(r),"function"==typeof t&&"prototype"===e&&"value"in r&&d in r&&!r[d]){var n=l(t,e);n&&n[d]&&(t[e]=r.value,r={configurable:h in r?r[h]:n[h],enumerable:p in r?r[p]:n[p],writable:!1})}return u(t,e,r)}:u:function(t,e,r){if(i(t),e=s(e),i(r),a)try{return u(t,e,r)}catch(t){}if("get"in r||"set"in r)throw c("Accessors not supported");return"value"in r&&(t[e]=r.value),t}},172:(t,e,r)=>{var n=r(6953),a=r(5244),o=r(6492),i=r(9367),s=r(3016),c=r(8756),u=r(5110),l=r(7106),p=Object.getOwnPropertyDescriptor;e.f=n?p:function(t,e){if(t=s(t),e=c(e),l)try{return p(t,e)}catch(t){}if(u(t,e))return i(!a(o.f,t,e),t[e])}},4113:(t,e,r)=>{var n=r(661),a=r(3016),o=r(4161).f,i=r(9292),s="object"==typeof window&&window&&Object.getOwnPropertyNames?Object.getOwnPropertyNames(window):[];t.exports.f=function(t){return s&&"Window"==n(t)?function(t){try{return o(t)}catch(t){return i(s)}}(t):o(a(t))}},4161:(t,e,r)=>{var n=r(9583),a=r(4798).concat("length","prototype");e.f=Object.getOwnPropertyNames||function(t){return n(t,a)}},864:(t,e)=>{e.f=Object.getOwnPropertySymbols},6166:(t,e,r)=>{var n=r(5110),a=r(7424),o=r(4440),i=r(5868),s=r(6036),c=i("IE_PROTO"),u=Object,l=u.prototype;t.exports=s?u.getPrototypeOf:function(t){var e=o(t);if(n(e,c))return e[c];var r=e.constructor;return a(r)&&e instanceof r?r.prototype:e instanceof u?l:null}},5395:(t,e,r)=>{var n=r(8462),a=r(6827),o=r(661),i=r(3321),s=Object.isExtensible,c=n((function(){s(1)}));t.exports=c||i?function(t){return!!a(t)&&((!i||"ArrayBuffer"!=o(t))&&(!s||s(t)))}:s},6830:(t,e,r)=>{var n=r(9709);t.exports=n({}.isPrototypeOf)},9583:(t,e,r)=>{var n=r(9709),a=r(5110),o=r(3016),i=r(5254).indexOf,s=r(3472),c=n([].push);t.exports=function(t,e){var r,n=o(t),u=0,l=[];for(r in n)!a(s,r)&&a(n,r)&&c(l,r);for(;e.length>u;)a(n,r=e[u++])&&(~i(l,r)||c(l,r));return l}},4565:(t,e,r)=>{var n=r(9583),a=r(4798);t.exports=Object.keys||function(t){return n(t,a)}},6492:(t,e)=>{"use strict";var r={}.propertyIsEnumerable,n=Object.getOwnPropertyDescriptor,a=n&&!r.call({1:2},1);e.f=a?function(t){var e=n(this,t);return!!e&&e.enumerable}:r},4322:(t,e,r)=>{var n=r(9709),a=r(1326),o=r(6637);t.exports=Object.setPrototypeOf||("__proto__"in{}?function(){var t,e=!1,r={};try{(t=n(Object.getOwnPropertyDescriptor(Object.prototype,"__proto__").set))(r,[]),e=r instanceof Array}catch(t){}return function(r,n){return a(r),o(n),e?t(r,n):r.__proto__=n,r}}():void 0)},4536:(t,e,r)=>{"use strict";var n=r(3557),a=r(9794);t.exports=n?{}.toString:function(){return"[object "+a(this)+"]"}},4259:(t,e,r)=>{var n=r(5244),a=r(7424),o=r(6827),i=TypeError;t.exports=function(t,e){var r,s;if("string"===e&&a(r=t.toString)&&!o(s=n(r,t)))return s;if(a(r=t.valueOf)&&!o(s=n(r,t)))return s;if("string"!==e&&a(r=t.toString)&&!o(s=n(r,t)))return s;throw i("Can't convert object to primitive value")}},1804:(t,e,r)=>{var n=r(5072),a=r(9709),o=r(4161),i=r(864),s=r(1326),c=a([].concat);t.exports=n("Reflect","ownKeys")||function(t){var e=o.f(s(t)),r=i.f;return r?c(e,r(t)):e}},8844:(t,e,r)=>{var n=r(2756);t.exports=n},5805:(t,e,r)=>{var n=r(5244),a=r(1326),o=r(7424),i=r(661),s=r(5866),c=TypeError;t.exports=function(t,e){var r=t.exec;if(o(r)){var u=n(r,t,e);return null!==u&&a(u),u}if("RegExp"===i(t))return n(s,t,e);throw c("RegExp#exec called on incompatible receiver")}},5866:(t,e,r)=>{"use strict";var n,a,o=r(5244),i=r(9709),s=r(1670),c=r(4768),u=r(4124),l=r(3662),p=r(3981),h=r(3962).get,d=r(5976),f=r(6741),v=l("native-string-replace",String.prototype.replace),m=RegExp.prototype.exec,b=m,g=i("".charAt),y=i("".indexOf),k=i("".replace),w=i("".slice),C=(a=/b*/g,o(m,n=/a/,"a"),o(m,a,"a"),0!==n.lastIndex||0!==a.lastIndex),S=u.BROKEN_CARET,x=void 0!==/()??/.exec("")[1];(C||x||S||d||f)&&(b=function(t){var e,r,n,a,i,u,l,d=this,f=h(d),O=s(t),E=f.raw;if(E)return E.lastIndex=d.lastIndex,e=o(b,E,O),d.lastIndex=E.lastIndex,e;var L=f.groups,P=S&&d.sticky,A=o(c,d),T=d.source,j=0,I=O;if(P&&(A=k(A,"y",""),-1===y(A,"g")&&(A+="g"),I=w(O,d.lastIndex),d.lastIndex>0&&(!d.multiline||d.multiline&&"\n"!==g(O,d.lastIndex-1))&&(T="(?: "+T+")",I=" "+I,j++),r=new RegExp("^(?:"+T+")",A)),x&&(r=new RegExp("^"+T+"$(?!\\s)",A)),C&&(n=d.lastIndex),a=o(m,P?r:d,I),P?a?(a.input=w(a.input,j),a[0]=w(a[0],j),a.index=d.lastIndex,d.lastIndex+=a[0].length):d.lastIndex=0:C&&a&&(d.lastIndex=d.global?a.index+a[0].length:n),x&&a&&a.length>1&&o(v,a[0],r,(function(){for(i=1;i<arguments.length-2;i++)void 0===arguments[i]&&(a[i]=void 0)})),a&&L)for(a.groups=u=p(null),i=0;i<L.length;i++)u[(l=L[i])[0]]=a[l[1]];return a}),t.exports=b},4768:(t,e,r)=>{"use strict";var n=r(1326);t.exports=function(){var t=n(this),e="";return t.hasIndices&&(e+="d"),t.global&&(e+="g"),t.ignoreCase&&(e+="i"),t.multiline&&(e+="m"),t.dotAll&&(e+="s"),t.unicode&&(e+="u"),t.unicodeSets&&(e+="v"),t.sticky&&(e+="y"),e}},101:(t,e,r)=>{var n=r(5244),a=r(5110),o=r(6830),i=r(4768),s=RegExp.prototype;t.exports=function(t){var e=t.flags;return void 0!==e||"flags"in s||a(t,"flags")||!o(s,t)?e:n(i,t)}},4124:(t,e,r)=>{var n=r(8462),a=r(2756).RegExp,o=n((function(){var t=a("a","y");return t.lastIndex=2,null!=t.exec("abcd")})),i=o||n((function(){return!a("a","y").sticky})),s=o||n((function(){var t=a("^r","gy");return t.lastIndex=2,null!=t.exec("str")}));t.exports={BROKEN_CARET:s,MISSED_STICKY:i,UNSUPPORTED_Y:o}},5976:(t,e,r)=>{var n=r(8462),a=r(2756).RegExp;t.exports=n((function(){var t=a(".","s");return!(t.dotAll&&t.exec("\n")&&"s"===t.flags)}))},6741:(t,e,r)=>{var n=r(8462),a=r(2756).RegExp;t.exports=n((function(){var t=a("(?<a>b)","g");return"b"!==t.exec("b").groups.a||"bc"!=="b".replace(t,"$<a>c")}))},5065:t=>{var e=TypeError;t.exports=function(t){if(null==t)throw e("Can't call method on "+t);return t}},5142:(t,e,r)=>{var n=r(7614).f,a=r(5110),o=r(7166)("toStringTag");t.exports=function(t,e,r){t&&!r&&(t=t.prototype),t&&!a(t,o)&&n(t,o,{configurable:!0,value:e})}},5868:(t,e,r)=>{var n=r(3662),a=r(637),o=n("keys");t.exports=function(t){return o[t]||(o[t]=a(t))}},410:(t,e,r)=>{var n=r(2756),a=r(6530),o="__core-js_shared__",i=n[o]||a(o,{});t.exports=i},3662:(t,e,r)=>{var n=r(7690),a=r(410);(t.exports=function(t,e){return a[t]||(a[t]=void 0!==e?e:{})})("versions",[]).push({version:"3.23.3",mode:n?"pure":"global",copyright:"© 2014-2022 Denis Pushkarev (zloirock.ru)",license:"https://github.com/zloirock/core-js/blob/v3.23.3/LICENSE",source:"https://github.com/zloirock/core-js"})},8576:(t,e,r)=>{var n=r(1326),a=r(2197),o=r(7166)("species");t.exports=function(t,e){var r,i=n(t).constructor;return void 0===i||null==(r=n(i)[o])?e:a(r)}},520:(t,e,r)=>{var n=r(9709),a=r(6372),o=r(1670),i=r(5065),s=n("".charAt),c=n("".charCodeAt),u=n("".slice),l=function(t){return function(e,r){var n,l,p=o(i(e)),h=a(r),d=p.length;return h<0||h>=d?t?"":void 0:(n=c(p,h))<55296||n>56319||h+1===d||(l=c(p,h+1))<56320||l>57343?t?s(p,h):n:t?u(p,h,h+2):l-56320+(n-55296<<10)+65536}};t.exports={codeAt:l(!1),charAt:l(!0)}},8961:(t,e,r)=>{var n=r(5244),a=r(5072),o=r(7166),i=r(2297);t.exports=function(){var t=a("Symbol"),e=t&&t.prototype,r=e&&e.valueOf,s=o("toPrimitive");e&&!e[s]&&i(e,s,(function(t){return n(r,this)}),{arity:1})}},847:(t,e,r)=>{var n=r(6372),a=Math.max,o=Math.min;t.exports=function(t,e){var r=n(t);return r<0?a(r+e,0):o(r,e)}},3016:(t,e,r)=>{var n=r(8188),a=r(5065);t.exports=function(t){return n(a(t))}},6372:(t,e,r)=>{var n=r(1298);t.exports=function(t){var e=+t;return e!=e||0===e?0:n(e)}},9223:(t,e,r)=>{var n=r(6372),a=Math.min;t.exports=function(t){return t>0?a(n(t),9007199254740991):0}},4440:(t,e,r)=>{var n=r(5065),a=Object;t.exports=function(t){return a(n(t))}},7806:(t,e,r)=>{var n=r(5244),a=r(6827),o=r(6396),i=r(125),s=r(4259),c=r(7166),u=TypeError,l=c("toPrimitive");t.exports=function(t,e){if(!a(t)||o(t))return t;var r,c=i(t,l);if(c){if(void 0===e&&(e="default"),r=n(c,t,e),!a(r)||o(r))return r;throw u("Can't convert object to primitive value")}return void 0===e&&(e="number"),s(t,e)}},8756:(t,e,r)=>{var n=r(7806),a=r(6396);t.exports=function(t){var e=n(t,"string");return a(e)?e:e+""}},3557:(t,e,r)=>{var n={};n[r(7166)("toStringTag")]="z",t.exports="[object z]"===String(n)},1670:(t,e,r)=>{var n=r(9794),a=String;t.exports=function(t){if("Symbol"===n(t))throw TypeError("Cannot convert a Symbol value to a string");return a(t)}},2882:t=>{var e=String;t.exports=function(t){try{return e(t)}catch(t){return"Object"}}},637:(t,e,r)=>{var n=r(9709),a=0,o=Math.random(),i=n(1..toString);t.exports=function(t){return"Symbol("+(void 0===t?"":t)+")_"+i(++a+o,36)}},4717:(t,e,r)=>{var n=r(3165);t.exports=n&&!Symbol.sham&&"symbol"==typeof Symbol.iterator},5679:(t,e,r)=>{var n=r(6953),a=r(8462);t.exports=n&&a((function(){return 42!=Object.defineProperty((function(){}),"prototype",{value:42,writable:!1}).prototype}))},766:(t,e,r)=>{var n=r(7166);e.f=n},7166:(t,e,r)=>{var n=r(2756),a=r(3662),o=r(5110),i=r(637),s=r(3165),c=r(4717),u=a("wks"),l=n.Symbol,p=l&&l.for,h=c?l:l&&l.withoutSetter||i;t.exports=function(t){if(!o(u,t)||!s&&"string"!=typeof u[t]){var e="Symbol."+t;s&&o(l,t)?u[t]=l[t]:u[t]=c&&p?p(e):h(e)}return u[t]}},5423:(t,e,r)=>{"use strict";var n=r(5689),a=r(8462),o=r(3115),i=r(6827),s=r(4440),c=r(7825),u=r(4958),l=r(1861),p=r(5309),h=r(6894),d=r(7166),f=r(5649),v=d("isConcatSpreadable"),m=f>=51||!a((function(){var t=[];return t[v]=!1,t.concat()[0]!==t})),b=h("concat"),g=function(t){if(!i(t))return!1;var e=t[v];return void 0!==e?!!e:o(t)};n({target:"Array",proto:!0,arity:1,forced:!m||!b},{concat:function(t){var e,r,n,a,o,i=s(this),h=p(i,0),d=0;for(e=-1,n=arguments.length;e<n;e++)if(g(o=-1===e?i:arguments[e]))for(a=c(o),u(d+a),r=0;r<a;r++,d++)r in o&&l(h,d,o[r]);else u(d+1),l(h,d++,o);return h.length=d,h}})},8837:(t,e,r)=>{"use strict";var n=r(5689),a=r(5860).filter;n({target:"Array",proto:!0,forced:!r(6894)("filter")},{filter:function(t){return a(this,t,arguments.length>1?arguments[1]:void 0)}})},5831:(t,e,r)=>{var n=r(5689),a=r(1871);n({target:"Array",stat:!0,forced:!r(5239)((function(t){Array.from(t)}))},{from:a})},2493:(t,e,r)=>{"use strict";var n=r(3016),a=r(990),o=r(7240),i=r(3962),s=r(7614).f,c=r(2527),u=r(7690),l=r(6953),p="Array Iterator",h=i.set,d=i.getterFor(p);t.exports=c(Array,"Array",(function(t,e){h(this,{type:p,target:n(t),index:0,kind:e})}),(function(){var t=d(this),e=t.target,r=t.kind,n=t.index++;return!e||n>=e.length?(t.target=void 0,{value:void 0,done:!0}):"keys"==r?{value:n,done:!1}:"values"==r?{value:e[n],done:!1}:{value:[n,e[n]],done:!1}}),"values");var f=o.Arguments=o.Array;if(a("keys"),a("values"),a("entries"),!u&&l&&"values"!==f.name)try{s(f,"name",{value:"values"})}catch(t){}},1055:(t,e,r)=>{"use strict";var n=r(5689),a=r(9709),o=r(8188),i=r(3016),s=r(8809),c=a([].join),u=o!=Object,l=s("join",",");n({target:"Array",proto:!0,forced:u||!l},{join:function(t){return c(i(this),void 0===t?",":t)}})},2417:(t,e,r)=>{"use strict";var n=r(5689),a=r(5860).map;n({target:"Array",proto:!0,forced:!r(6894)("map")},{map:function(t){return a(this,t,arguments.length>1?arguments[1]:void 0)}})},653:(t,e,r)=>{"use strict";var n=r(5689),a=r(3115),o=r(7086),i=r(6827),s=r(847),c=r(7825),u=r(3016),l=r(1861),p=r(7166),h=r(6894),d=r(3653),f=h("slice"),v=p("species"),m=Array,b=Math.max;n({target:"Array",proto:!0,forced:!f},{slice:function(t,e){var r,n,p,h=u(this),f=c(h),g=s(t,f),y=s(void 0===e?f:e,f);if(a(h)&&(r=h.constructor,(o(r)&&(r===m||a(r.prototype))||i(r)&&null===(r=r[v]))&&(r=void 0),r===m||void 0===r))return d(h,g,y);for(n=new(void 0===r?m:r)(b(y-g,0)),p=0;g<y;g++,p++)g in h&&l(n,p,h[g]);return n.length=p,n}})},3145:(t,e,r)=>{var n=r(6953),a=r(7707).EXISTS,o=r(9709),i=r(7614).f,s=Function.prototype,c=o(s.toString),u=/function\b(?:\s|\/\*[\S\s]*?\*\/|\/\/[^\n\r]*[\n\r]+)*([^\s(/]*)/,l=o(u.exec);n&&!a&&i(s,"name",{configurable:!0,get:function(){try{return l(u,c(this))[1]}catch(t){return""}}})},6787:(t,e,r)=>{var n=r(5689),a=r(5072),o=r(7322),i=r(5244),s=r(9709),c=r(8462),u=r(3115),l=r(7424),p=r(6827),h=r(6396),d=r(3653),f=r(3165),v=a("JSON","stringify"),m=s(/./.exec),b=s("".charAt),g=s("".charCodeAt),y=s("".replace),k=s(1..toString),w=/[\uD800-\uDFFF]/g,C=/^[\uD800-\uDBFF]$/,S=/^[\uDC00-\uDFFF]$/,x=!f||c((function(){var t=a("Symbol")();return"[null]"!=v([t])||"{}"!=v({a:t})||"{}"!=v(Object(t))})),O=c((function(){return'"\\udf06\\ud834"'!==v("\udf06\ud834")||'"\\udead"'!==v("\udead")})),E=function(t,e){var r=d(arguments),n=e;if((p(e)||void 0!==t)&&!h(t))return u(e)||(e=function(t,e){if(l(n)&&(e=i(n,this,t,e)),!h(e))return e}),r[1]=e,o(v,null,r)},L=function(t,e,r){var n=b(r,e-1),a=b(r,e+1);return m(C,t)&&!m(S,a)||m(S,t)&&!m(C,n)?"\\u"+k(g(t,0),16):t};v&&n({target:"JSON",stat:!0,arity:3,forced:x||O},{stringify:function(t,e,r){var n=d(arguments),a=o(x?E:v,null,n);return O&&"string"==typeof a?y(a,w,L):a}})},9206:(t,e,r)=>{var n=r(5689),a=r(9484);n({target:"Object",stat:!0,arity:2,forced:Object.assign!==a},{assign:a})},6988:(t,e,r)=>{var n=r(5689),a=r(9947),o=r(8462),i=r(6827),s=r(2634).onFreeze,c=Object.freeze;n({target:"Object",stat:!0,forced:o((function(){c(1)})),sham:!a},{freeze:function(t){return c&&i(t)?c(s(t)):t}})},2002:(t,e,r)=>{var n=r(5689),a=r(8462),o=r(3016),i=r(172).f,s=r(6953),c=a((function(){i(1)}));n({target:"Object",stat:!0,forced:!s||c,sham:!s},{getOwnPropertyDescriptor:function(t,e){return i(o(t),e)}})},9185:(t,e,r)=>{var n=r(5689),a=r(6953),o=r(1804),i=r(3016),s=r(172),c=r(1861);n({target:"Object",stat:!0,sham:!a},{getOwnPropertyDescriptors:function(t){for(var e,r,n=i(t),a=s.f,u=o(n),l={},p=0;u.length>p;)void 0!==(r=a(n,e=u[p++]))&&c(l,e,r);return l}})},3134:(t,e,r)=>{var n=r(5689),a=r(3165),o=r(8462),i=r(864),s=r(4440);n({target:"Object",stat:!0,forced:!a||o((function(){i.f(1)}))},{getOwnPropertySymbols:function(t){var e=i.f;return e?e(s(t)):[]}})},1240:(t,e,r)=>{var n=r(5689),a=r(8462),o=r(4440),i=r(6166),s=r(6036);n({target:"Object",stat:!0,forced:a((function(){i(1)})),sham:!s},{getPrototypeOf:function(t){return i(o(t))}})},8471:(t,e,r)=>{var n=r(5689),a=r(4440),o=r(4565);n({target:"Object",stat:!0,forced:r(8462)((function(){o(1)}))},{keys:function(t){return o(a(t))}})},570:(t,e,r)=>{var n=r(3557),a=r(2297),o=r(4536);n||a(Object.prototype,"toString",o,{unsafe:!0})},9834:(t,e,r)=>{var n=r(5689),a=r(5072),o=r(7322),i=r(8631),s=r(2197),c=r(1326),u=r(6827),l=r(3981),p=r(8462),h=a("Reflect","construct"),d=Object.prototype,f=[].push,v=p((function(){function t(){}return!(h((function(){}),[],t)instanceof t)})),m=!p((function(){h((function(){}))})),b=v||m;n({target:"Reflect",stat:!0,forced:b,sham:b},{construct:function(t,e){s(t),c(e);var r=arguments.length<3?t:s(arguments[2]);if(m&&!v)return h(t,e,r);if(t==r){switch(e.length){case 0:return new t;case 1:return new t(e[0]);case 2:return new t(e[0],e[1]);case 3:return new t(e[0],e[1],e[2]);case 4:return new t(e[0],e[1],e[2],e[3])}var n=[null];return o(f,n,e),new(o(i,t,n))}var a=r.prototype,p=l(u(a)?a:d),b=o(t,p,e);return u(b)?b:p}})},3188:(t,e,r)=>{"use strict";var n=r(5689),a=r(5866);n({target:"RegExp",proto:!0,forced:/./.exec!==a},{exec:a})},844:(t,e,r)=>{"use strict";var n=r(7707).PROPER,a=r(2297),o=r(1326),i=r(1670),s=r(8462),c=r(101),u="toString",l=RegExp.prototype[u],p=s((function(){return"/a/b"!=l.call({source:"a",flags:"b"})})),h=n&&l.name!=u;(p||h)&&a(RegExp.prototype,u,(function(){var t=o(this);return"/"+i(t.source)+"/"+i(c(t))}),{unsafe:!0})},3343:(t,e,r)=>{"use strict";var n=r(520).charAt,a=r(1670),o=r(3962),i=r(2527),s="String Iterator",c=o.set,u=o.getterFor(s);i(String,"String",(function(t){c(this,{type:s,string:a(t),index:0})}),(function(){var t,e=u(this),r=e.string,a=e.index;return a>=r.length?{value:void 0,done:!0}:(t=n(r,a),e.index+=t.length,{value:t,done:!1})}))},9297:(t,e,r)=>{"use strict";var n=r(7322),a=r(5244),o=r(9709),i=r(6031),s=r(8462),c=r(1326),u=r(7424),l=r(6372),p=r(9223),h=r(1670),d=r(5065),f=r(2577),v=r(125),m=r(5285),b=r(5805),g=r(7166)("replace"),y=Math.max,k=Math.min,w=o([].concat),C=o([].push),S=o("".indexOf),x=o("".slice),O="$0"==="a".replace(/./,"$0"),E=!!/./[g]&&""===/./[g]("a","$0");i("replace",(function(t,e,r){var o=E?"$":"$0";return[function(t,r){var n=d(this),o=null==t?void 0:v(t,g);return o?a(o,t,n,r):a(e,h(n),t,r)},function(t,a){var i=c(this),s=h(t);if("string"==typeof a&&-1===S(a,o)&&-1===S(a,"$<")){var d=r(e,i,s,a);if(d.done)return d.value}var v=u(a);v||(a=h(a));var g=i.global;if(g){var O=i.unicode;i.lastIndex=0}for(var E=[];;){var L=b(i,s);if(null===L)break;if(C(E,L),!g)break;""===h(L[0])&&(i.lastIndex=f(s,p(i.lastIndex),O))}for(var P,A="",T=0,j=0;j<E.length;j++){for(var I=h((L=E[j])[0]),M=y(k(l(L.index),s.length),0),F=[],B=1;B<L.length;B++)C(F,void 0===(P=L[B])?P:String(P));var R=L.groups;if(v){var N=w([I],F,M,s);void 0!==R&&C(N,R);var D=h(n(a,void 0,N))}else D=m(I,s,M,F,R,a);M>=T&&(A+=x(s,T,M)+D,T=M+I.length)}return A+x(s,T)}]}),!!s((function(){var t=/./;return t.exec=function(){var t=[];return t.groups={a:"7"},t},"7"!=="".replace(t,"$<a>")}))||!O||E)},2773:(t,e,r)=>{"use strict";var n=r(7322),a=r(5244),o=r(9709),i=r(6031),s=r(5237),c=r(1326),u=r(5065),l=r(8576),p=r(2577),h=r(9223),d=r(1670),f=r(125),v=r(9292),m=r(5805),b=r(5866),g=r(4124),y=r(8462),k=g.UNSUPPORTED_Y,w=4294967295,C=Math.min,S=[].push,x=o(/./.exec),O=o(S),E=o("".slice);i("split",(function(t,e,r){var o;return o="c"=="abbc".split(/(b)*/)[1]||4!="test".split(/(?:)/,-1).length||2!="ab".split(/(?:ab)*/).length||4!=".".split(/(.?)(.?)/).length||".".split(/()()/).length>1||"".split(/.?/).length?function(t,r){var o=d(u(this)),i=void 0===r?w:r>>>0;if(0===i)return[];if(void 0===t)return[o];if(!s(t))return a(e,o,t,i);for(var c,l,p,h=[],f=(t.ignoreCase?"i":"")+(t.multiline?"m":"")+(t.unicode?"u":"")+(t.sticky?"y":""),m=0,g=new RegExp(t.source,f+"g");(c=a(b,g,o))&&!((l=g.lastIndex)>m&&(O(h,E(o,m,c.index)),c.length>1&&c.index<o.length&&n(S,h,v(c,1)),p=c[0].length,m=l,h.length>=i));)g.lastIndex===c.index&&g.lastIndex++;return m===o.length?!p&&x(g,"")||O(h,""):O(h,E(o,m)),h.length>i?v(h,0,i):h}:"0".split(void 0,0).length?function(t,r){return void 0===t&&0===r?[]:a(e,this,t,r)}:e,[function(e,r){var n=u(this),i=null==e?void 0:f(e,t);return i?a(i,e,n,r):a(o,d(n),e,r)},function(t,n){var a=c(this),i=d(t),s=r(o,a,i,n,o!==e);if(s.done)return s.value;var u=l(a,RegExp),f=a.unicode,v=(a.ignoreCase?"i":"")+(a.multiline?"m":"")+(a.unicode?"u":"")+(k?"g":"y"),b=new u(k?"^(?:"+a.source+")":a,v),g=void 0===n?w:n>>>0;if(0===g)return[];if(0===i.length)return null===m(b,i)?[i]:[];for(var y=0,S=0,x=[];S<i.length;){b.lastIndex=k?0:S;var L,P=m(b,k?E(i,S):i);if(null===P||(L=C(h(b.lastIndex+(k?S:0)),i.length))===y)S=p(i,S,f);else{if(O(x,E(i,y,S)),x.length===g)return x;for(var A=1;A<=P.length-1;A++)if(O(x,P[A]),x.length===g)return x;S=y=L}}return O(x,E(i,y)),x}]}),!!y((function(){var t=/(?:)/,e=t.exec;t.exec=function(){return e.apply(this,arguments)};var r="ab".split(t);return 2!==r.length||"a"!==r[0]||"b"!==r[1]})),k)},420:(t,e,r)=>{"use strict";var n=r(5689),a=r(2756),o=r(5244),i=r(9709),s=r(7690),c=r(6953),u=r(3165),l=r(8462),p=r(5110),h=r(6830),d=r(1326),f=r(3016),v=r(8756),m=r(1670),b=r(9367),g=r(3981),y=r(4565),k=r(4161),w=r(4113),C=r(864),S=r(172),x=r(7614),O=r(6434),E=r(6492),L=r(2297),P=r(3662),A=r(5868),T=r(3472),j=r(637),I=r(7166),M=r(766),F=r(4300),B=r(8961),R=r(5142),N=r(3962),D=r(5860).forEach,H=A("hidden"),_="Symbol",z="prototype",U=N.set,q=N.getterFor(_),W=Object[z],V=a.Symbol,$=V&&V[z],G=a.TypeError,X=a.QObject,J=S.f,Y=x.f,K=w.f,Q=E.f,Z=i([].push),tt=P("symbols"),et=P("op-symbols"),rt=P("wks"),nt=!X||!X[z]||!X[z].findChild,at=c&&l((function(){return 7!=g(Y({},"a",{get:function(){return Y(this,"a",{value:7}).a}})).a}))?function(t,e,r){var n=J(W,e);n&&delete W[e],Y(t,e,r),n&&t!==W&&Y(W,e,n)}:Y,ot=function(t,e){var r=tt[t]=g($);return U(r,{type:_,tag:t,description:e}),c||(r.description=e),r},it=function(t,e,r){t===W&&it(et,e,r),d(t);var n=v(e);return d(r),p(tt,n)?(r.enumerable?(p(t,H)&&t[H][n]&&(t[H][n]=!1),r=g(r,{enumerable:b(0,!1)})):(p(t,H)||Y(t,H,b(1,{})),t[H][n]=!0),at(t,n,r)):Y(t,n,r)},st=function(t,e){d(t);var r=f(e),n=y(r).concat(pt(r));return D(n,(function(e){c&&!o(ct,r,e)||it(t,e,r[e])})),t},ct=function(t){var e=v(t),r=o(Q,this,e);return!(this===W&&p(tt,e)&&!p(et,e))&&(!(r||!p(this,e)||!p(tt,e)||p(this,H)&&this[H][e])||r)},ut=function(t,e){var r=f(t),n=v(e);if(r!==W||!p(tt,n)||p(et,n)){var a=J(r,n);return!a||!p(tt,n)||p(r,H)&&r[H][n]||(a.enumerable=!0),a}},lt=function(t){var e=K(f(t)),r=[];return D(e,(function(t){p(tt,t)||p(T,t)||Z(r,t)})),r},pt=function(t){var e=t===W,r=K(e?et:f(t)),n=[];return D(r,(function(t){!p(tt,t)||e&&!p(W,t)||Z(n,tt[t])})),n};u||(L($=(V=function(){if(h($,this))throw G("Symbol is not a constructor");var t=arguments.length&&void 0!==arguments[0]?m(arguments[0]):void 0,e=j(t),r=function(t){this===W&&o(r,et,t),p(this,H)&&p(this[H],e)&&(this[H][e]=!1),at(this,e,b(1,t))};return c&&nt&&at(W,e,{configurable:!0,set:r}),ot(e,t)})[z],"toString",(function(){return q(this).tag})),L(V,"withoutSetter",(function(t){return ot(j(t),t)})),E.f=ct,x.f=it,O.f=st,S.f=ut,k.f=w.f=lt,C.f=pt,M.f=function(t){return ot(I(t),t)},c&&(Y($,"description",{configurable:!0,get:function(){return q(this).description}}),s||L(W,"propertyIsEnumerable",ct,{unsafe:!0}))),n({global:!0,constructor:!0,wrap:!0,forced:!u,sham:!u},{Symbol:V}),D(y(rt),(function(t){F(t)})),n({target:_,stat:!0,forced:!u},{useSetter:function(){nt=!0},useSimple:function(){nt=!1}}),n({target:"Object",stat:!0,forced:!u,sham:!c},{create:function(t,e){return void 0===e?g(t):st(g(t),e)},defineProperty:it,defineProperties:st,getOwnPropertyDescriptor:ut}),n({target:"Object",stat:!0,forced:!u},{getOwnPropertyNames:lt}),B(),R(V,_),T[H]=!0},4290:(t,e,r)=>{"use strict";var n=r(5689),a=r(6953),o=r(2756),i=r(9709),s=r(5110),c=r(7424),u=r(6830),l=r(1670),p=r(7614).f,h=r(9621),d=o.Symbol,f=d&&d.prototype;if(a&&c(d)&&(!("description"in f)||void 0!==d().description)){var v={},m=function(){var t=arguments.length<1||void 0===arguments[0]?void 0:l(arguments[0]),e=u(f,this)?new d(t):void 0===t?d():d(t);return""===t&&(v[e]=!0),e};h(m,d),m.prototype=f,f.constructor=m;var b="Symbol(test)"==String(d("test")),g=i(f.toString),y=i(f.valueOf),k=/^Symbol\((.*)\)[^)]+$/,w=i("".replace),C=i("".slice);p(f,"description",{configurable:!0,get:function(){var t=y(this),e=g(t);if(s(v,t))return"";var r=b?C(e,7,-1):w(e,k,"$1");return""===r?void 0:r}}),n({global:!0,constructor:!0,forced:!0},{Symbol:m})}},8727:(t,e,r)=>{var n=r(5689),a=r(5072),o=r(5110),i=r(1670),s=r(3662),c=r(3025),u=s("string-to-symbol-registry"),l=s("symbol-to-string-registry");n({target:"Symbol",stat:!0,forced:!c},{for:function(t){var e=i(t);if(o(u,e))return u[e];var r=a("Symbol")(e);return u[e]=r,l[r]=e,r}})},9068:(t,e,r)=>{r(4300)("iterator")},4162:(t,e,r)=>{r(420),r(8727),r(1721),r(6787),r(3134)},1721:(t,e,r)=>{var n=r(5689),a=r(5110),o=r(6396),i=r(2882),s=r(3662),c=r(3025),u=s("symbol-to-string-registry");n({target:"Symbol",stat:!0,forced:!c},{keyFor:function(t){if(!o(t))throw TypeError(i(t)+" is not a symbol");if(a(u,t))return u[t]}})},4439:(t,e,r)=>{var n=r(2756),a=r(8993),o=r(7891),i=r(9352),s=r(4100),c=function(t){if(t&&t.forEach!==i)try{s(t,"forEach",i)}catch(e){t.forEach=i}};for(var u in a)a[u]&&c(n[u]&&n[u].prototype);c(o)},7046:(t,e,r)=>{var n=r(2756),a=r(8993),o=r(7891),i=r(2493),s=r(4100),c=r(7166),u=c("iterator"),l=c("toStringTag"),p=i.values,h=function(t,e){if(t){if(t[u]!==p)try{s(t,u,p)}catch(e){t[u]=p}if(t[l]||s(t,l,e),a[e])for(var r in i)if(t[r]!==i[r])try{s(t,r,i[r])}catch(e){t[r]=i[r]}}};for(var d in a)h(n[d]&&n[d].prototype,d);h(o,"DOMTokenList")},4373:()=>{var t;t=function(t){return t=+t,isNaN(t)||t==1/0||t==-1/0?0:t},Element.prototype.scroll||(Element.prototype.scroll=function(){var e,r,n=arguments.length,a=this.ownerDocument,o=a.defaultView,i="BackCompat"==a.compatMode,s=document.getElementsByTagName("BODY")[0],c={};if(a==window.document&&o&&0!==n){if(1===n){var u=arguments[0];if("object"!=typeof u)throw"Failed to execute 'scrollBy' on 'Element': parameter 1 ('options') is not an object.";"left"in u&&(c.left=t(u.left)),"top"in u&&(c.top=t(u.top)),e="left"in c?c.left:this.scrollLeft,r="top"in c?c.top:this.scrollTop}else c.left=e=t(arguments[0]),c.top=r=t(arguments[1]);if(this!=document.documentElement)this!=s||!i||function(t){t=t||document.getElementsByTagName("BODY")[0];var e=window.getComputedStyle(t),r=window.getComputedStyle(t.parent),n=e.overflowX,a=e.overflowY,o=r.overflowX,i=r.overflowY;return("table-column"==e.display||"table-column-group"==e.display)&&"visible"!=o&&"clip"!=o&&"visible"!=i&&"clip"!=i&&"visible"!=n&&"clip"!=n&&"visible"!=a&&"clip"!=a}(s)?(this.scrollLeft=e,this.scrollTop=r):o.scroll(c.left,c.top);else{if(i)return;o.scroll("scrollX"in o?o.scrollX:"pageXOffset"in o?o.pageXOffset:this.scrollLeft,r)}}}),Element.prototype.scrollTo||(Element.prototype.scrollTo=Element.prototype.scroll),Element.prototype.scrollBy||(Element.prototype.scrollBy=function(){var e=arguments.length,r={};if(0!==e){if(1===e){var n=arguments[0];if("object"!=typeof n)throw"Failed to execute 'scrollBy' on 'Element': parameter 1 ('options') is not an object.";"left"in n&&(r.left=t(n.left)),"top"in n&&(r.top=t(n.top))}else r.left=t(arguments[0]),r.top=t(arguments[1]);r.left="left"in r?r.left+this.scrollLeft:this.scrollLeft,r.top="top"in r?r.top+this.scrollTop:this.scrollTop,this.scroll(r)}})},5199:t=>{t.exports=function(t){return!(!t||"string"==typeof t)&&(t instanceof Array||Array.isArray(t)||t.length>=0&&(t.splice instanceof Function||Object.getOwnPropertyDescriptor(t,t.length-1)&&"String"!==t.constructor.name))}},2683:(t,e,r)=>{var n,a,o;a=[r(648)],void 0===(o="function"==typeof(n=function(t){function e(t){this.init(t)}e.prototype={value:0,size:100,startAngle:-Math.PI,thickness:"auto",fill:{gradient:["#3aeabb","#fdd250"]},emptyFill:"rgba(0, 0, 0, .1)",animation:{duration:1200,easing:"circleProgressEasing"},animationStartValue:0,reverse:!1,lineCap:"butt",insertMode:"prepend",constructor:e,el:null,canvas:null,ctx:null,radius:0,arcFill:null,lastFrameValue:0,init:function(e){t.extend(this,e),this.radius=this.size/2,this.initWidget(),this.initFill(),this.draw(),this.el.trigger("circle-inited")},initWidget:function(){this.canvas||(this.canvas=t("<canvas>")["prepend"==this.insertMode?"prependTo":"appendTo"](this.el)[0]);var e=this.canvas;if(e.width=this.size,e.height=this.size,this.ctx=e.getContext("2d"),window.devicePixelRatio>1){var r=window.devicePixelRatio;e.style.width=e.style.height=this.size+"px",e.width=e.height=this.size*r,this.ctx.scale(r,r)}},initFill:function(){var e,r=this,n=this.fill,a=this.ctx,o=this.size;if(!n)throw Error("The fill is not specified!");if("string"==typeof n&&(n={color:n}),n.color&&(this.arcFill=n.color),n.gradient){var i=n.gradient;if(1==i.length)this.arcFill=i[0];else if(i.length>1){for(var s=n.gradientAngle||0,c=n.gradientDirection||[o/2*(1-Math.cos(s)),o/2*(1+Math.sin(s)),o/2*(1+Math.cos(s)),o/2*(1-Math.sin(s))],u=a.createLinearGradient.apply(a,c),l=0;l<i.length;l++){var p=i[l],h=l/(i.length-1);t.isArray(p)&&(h=p[1],p=p[0]),u.addColorStop(h,p)}this.arcFill=u}}function d(){var n=t("<canvas>")[0];n.width=r.size,n.height=r.size,n.getContext("2d").drawImage(e,0,0,o,o),r.arcFill=r.ctx.createPattern(n,"no-repeat"),r.drawFrame(r.lastFrameValue)}n.image&&(n.image instanceof Image?e=n.image:(e=new Image).src=n.image,e.complete?d():e.onload=d)},draw:function(){this.animation?this.drawAnimated(this.value):this.drawFrame(this.value)},drawFrame:function(t){this.lastFrameValue=t,this.ctx.clearRect(0,0,this.size,this.size),this.drawEmptyArc(t),this.drawArc(t)},drawArc:function(t){if(0!==t){var e=this.ctx,r=this.radius,n=this.getThickness(),a=this.startAngle;e.save(),e.beginPath(),this.reverse?e.arc(r,r,r-n/2,a-2*Math.PI*t,a):e.arc(r,r,r-n/2,a,a+2*Math.PI*t),e.lineWidth=n,e.lineCap=this.lineCap,e.strokeStyle=this.arcFill,e.stroke(),e.restore()}},drawEmptyArc:function(t){var e=this.ctx,r=this.radius,n=this.getThickness(),a=this.startAngle;t<1&&(e.save(),e.beginPath(),t<=0?e.arc(r,r,r-n/2,0,2*Math.PI):this.reverse?e.arc(r,r,r-n/2,a,a-2*Math.PI*t):e.arc(r,r,r-n/2,a+2*Math.PI*t,a),e.lineWidth=n,e.strokeStyle=this.emptyFill,e.stroke(),e.restore())},drawAnimated:function(e){var r=this,n=this.el,a=t(this.canvas);a.stop(!0,!1),n.trigger("circle-animation-start"),a.css({animationProgress:0}).animate({animationProgress:1},t.extend({},this.animation,{step:function(t){var a=r.animationStartValue*(1-t)+e*t;r.drawFrame(a),n.trigger("circle-animation-progress",[t,a])}})).promise().always((function(){n.trigger("circle-animation-end")}))},getThickness:function(){return t.isNumeric(this.thickness)?this.thickness:this.size/14},getValue:function(){return this.value},setValue:function(t){this.animation&&(this.animationStartValue=this.lastFrameValue),this.value=t,this.draw()}},t.circleProgress={defaults:e.prototype},t.easing.circleProgressEasing=function(t){return t<.5?.5*(t*=2)*t*t:1-.5*(t=2-2*t)*t*t},t.fn.circleProgress=function(r,n){var a="circle-progress",o=this.data(a);if("widget"==r){if(!o)throw Error('Calling "widget" method on not initialized instance is forbidden');return o.canvas}if("value"==r){if(!o)throw Error('Calling "value" method on not initialized instance is forbidden');if(void 0===n)return o.getValue();var i=arguments[1];return this.each((function(){t(this).data(a).setValue(i)}))}return this.each((function(){var n=t(this),o=n.data(a),i=t.isPlainObject(r)?r:{};if(o)o.init(i);else{var s=t.extend({},n.data());"string"==typeof s.fill&&(s.fill=JSON.parse(s.fill)),"string"==typeof s.animation&&(s.animation=JSON.parse(s.animation)),(i=t.extend(s,i)).el=n,o=new e(i),n.data(a,o)}}))}})?n.apply(e,a):n)||(t.exports=o)},6852:(t,e,r)=>{"use strict";var n=r(5199),a=Array.prototype.concat,o=Array.prototype.slice,i=t.exports=function(t){for(var e=[],r=0,i=t.length;r<i;r++){var s=t[r];n(s)?e=a.call(e,o.call(s)):e.push(s)}return e};i.wrap=function(t){return function(){return t(i(arguments))}}},648:t=>{"use strict";t.exports=H5P.jQuery}},e={};function r(n){var a=e[n];if(void 0!==a)return a.exports;var o=e[n]={exports:{}};return t[n](o,o.exports,r),o.exports}r.n=t=>{var e=t&&t.__esModule?()=>t.default:()=>t;return r.d(e,{a:e}),e},r.d=(t,e)=>{for(var n in e)r.o(e,n)&&!r.o(t,n)&&Object.defineProperty(t,n,{enumerable:!0,get:e[n]})},r.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(t){if("object"==typeof window)return window}}(),r.o=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),(()=>{"use strict";r(9834),r(9206),r(8471),r(4162),r(2002),r(9185),r(4290),r(9068),r(2493),r(3343),r(7046),r(570),r(4439),r(2417),r(8837),r(5423),r(653),r(3188),r(9297),r(1240),r(2773),r(1055),r(844);function t(t,e){for(var r=0;r<e.length;r++){var n=e[r];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(t,n.key,n)}}const e=function(){function e(){!function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,e)}var r,n,a;return r=e,a=[{key:"extractFragmentsFromURL",value:function(t,e){if(!e.location.hash)return{};var r={};return e.location.hash.replace("#","").split("&").forEach((function(t){if(-1!==t.indexOf("=")){var e=t.split("=");r[e[0]]=e[1]}})),"function"!=typeof t||t(r)?r:{}}},{key:"createFragmentsString",value:function(t){var e=[];for(var r in t)e.push("".concat(r,"=").concat(t[r]));return"#".concat(e.join("&"))}},{key:"areFragmentsEqual",value:function(t,e){var r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:[];for(var n in t)if(t.hasOwnProperty(n)){if(r.length>0&&-1===r.indexOf(n))continue;if(!e[n]||t[n].toString()!==e[n].toString())return!1}return!0}}],(n=null)&&t(r.prototype,n),a&&t(r,a),Object.defineProperty(r,"prototype",{writable:!1}),e}();r(6988),r(3145),r(5831);function n(t){return n="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},n(t)}function a(t,e){var r="undefined"!=typeof Symbol&&t[Symbol.iterator]||t["@@iterator"];if(!r){if(Array.isArray(t)||(r=function(t,e){if(!t)return;if("string"==typeof t)return o(t,e);var r=Object.prototype.toString.call(t).slice(8,-1);"Object"===r&&t.constructor&&(r=t.constructor.name);if("Map"===r||"Set"===r)return Array.from(t);if("Arguments"===r||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r))return o(t,e)}(t))||e&&t&&"number"==typeof t.length){r&&(t=r);var n=0,a=function(){};return{s:a,n:function(){return n>=t.length?{done:!0}:{done:!1,value:t[n++]}},e:function(t){throw t},f:a}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var i,s=!0,c=!1;return{s:function(){r=r.call(t)},n:function(){var t=r.next();return s=t.done,t},e:function(t){c=!0,i=t},f:function(){try{s||null==r.return||r.return()}finally{if(c)throw i}}}}function o(t,e){(null==e||e>t.length)&&(e=t.length);for(var r=0,n=new Array(e);r<e;r++)n[r]=t[r];return n}function i(t,e){for(var r=0;r<e.length;r++){var n=e[r];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(t,n.key,n)}}function s(t,e){return s=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(t,e){return t.__proto__=e,t},s(t,e)}function c(t){var e=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(t){return!1}}();return function(){var r,a=u(t);if(e){var o=u(this).constructor;r=Reflect.construct(a,arguments,o)}else r=a.apply(this,arguments);return function(t,e){if(e&&("object"===n(e)||"function"==typeof e))return e;if(void 0!==e)throw new TypeError("Derived constructors may only return object or undefined");return function(t){if(void 0===t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return t}(t)}(this,r)}}function u(t){return u=Object.setPrototypeOf?Object.getPrototypeOf.bind():function(t){return t.__proto__||Object.getPrototypeOf(t)},u(t)}const l=function(){!function(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function");t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,writable:!0,configurable:!0}}),Object.defineProperty(t,"prototype",{writable:!1}),e&&s(t,e)}(o,H5P.EventDispatcher);var t,e,r,n=c(o);function o(t,e,r,a){var i;return function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,o),(i=n.call(this)).id=e,i.parent=a,i.behaviour=t.behaviour,i.content=document.createElement("ul"),i.content.classList.add("navigation-list"),i.container=i.addSideBar(),i.l10n=t.l10n,i.chapters=i.findAllChapters(t.chapters),i.chapterNodes=i.getChapterNodes(),r&&(i.titleElem=i.addMainTitle(r),i.container.appendChild(i.titleElem)),i.chapterNodes.forEach((function(t){i.content.appendChild(t)})),i.chapters.length>20&&i.content.classList.add("large-navigation-list"),i.container.appendChild(i.content),i.addTransformListener(),i.initializeNavigationControls(),i}return t=o,e=[{key:"initializeNavigationControls",value:function(){var t=this,e=Object.freeze({UP:38,DOWN:40});this.chapterNodes.forEach((function(r,n){r.querySelector(".h5p-interactive-book-navigation-chapter-button").addEventListener("keydown",(function(r){switch(r.keyCode){case e.UP:t.setFocusToChapterItem(n,-1),r.preventDefault();break;case e.DOWN:t.setFocusToChapterItem(n,1),r.preventDefault()}}));for(var a=r.querySelectorAll(".h5p-interactive-book-navigation-section"),o=function(r){a[r].querySelector(".section-button").addEventListener("keydown",(function(a){switch(a.keyCode){case e.UP:t.setFocusToSectionItem(n,r,-1),a.preventDefault();break;case e.DOWN:t.setFocusToSectionItem(n,r,1),a.preventDefault()}}))},i=0;i<a.length;i++)o(i)}))}},{key:"focus",value:function(){this.content.querySelector("button").focus()}},{key:"setFocusToChapterItem",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,r=t+e;if(r<0?r=this.chapterNodes.length-1:r>this.chapterNodes.length-1&&(r=0),e){var n=e>0?t:r,a=this.chapterNodes[n];if(!a.classList.contains("h5p-interactive-book-navigation-closed")){var o=a.querySelectorAll(".h5p-interactive-book-navigation-section");if(o.length){var i=e>0?0:o.length-1;return void this.setFocusToSectionItem(n,i)}}}var s=this.chapterNodes[r].querySelector(".h5p-interactive-book-navigation-chapter-button");this.setFocusToItem(s,r)}},{key:"setFocusToSectionItem",value:function(t,e){var r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:0,n=this.chapterNodes[t].querySelectorAll(".h5p-interactive-book-navigation-section"),a=e+r;if(a>n.length-1)this.setFocusToChapterItem(t+1);else if(a<0)this.setFocusToChapterItem(t);else{var o=n[a].querySelector(".section-button");this.setFocusToItem(o,t)}}},{key:"setFocusToItem",value:function(t,e){var r=arguments.length>2&&void 0!==arguments[2]&&arguments[2];this.chapterNodes.forEach((function(t,r){var n=t.querySelector(".h5p-interactive-book-navigation-chapter-button");r===e?n.classList.add("h5p-interactive-book-navigation-current"):n.classList.remove("h5p-interactive-book-navigation-current"),n.setAttribute("tabindex","-1");for(var a=t.querySelectorAll(".h5p-interactive-book-navigation-section"),o=0;o<a.length;o++)a[o].querySelector(".section-button").setAttribute("tabindex","-1")})),t.setAttribute("tabindex","0"),this.focusedChapter=e,r||t.focus()}},{key:"addSideBar",value:function(){var t=document.createElement("div");return t.id="h5p-interactive-book-navigation-menu",t.classList.add("h5p-interactive-book-navigation"),t}},{key:"addMainTitle",value:function(t){var e=document.createElement("h2");e.classList.add("navigation-title"),e.innerHTML=t,e.setAttribute("title",t);var r=document.createElement("div");return r.classList.add("h5p-interactive-book-navigation-maintitle"),r.appendChild(e),r}},{key:"findSectionsInChapter",value:function(t){for(var e=[],r=t.params.content,n=0;n<r.length;n++){var a=r[n].content,o="";o="H5P.Link"===a.library.split(" ")[0]?a.params.title?a.params.title:"New link":a.metadata.title,e.push({title:o,id:a.subContentId?"h5p-interactive-book-section-".concat(a.subContentId):void 0})}return e}},{key:"findAllChapters",value:function(t){for(var e=[],r=0;r<t.length;r++){var n=this.findSectionsInChapter(t[r]),a=t[r].metadata.title,o="h5p-interactive-book-chapter-".concat(t[r].subContentId);e.push({sections:n,title:a,id:o,isSummary:!1})}return this.parent.hasSummary()&&e.push({sections:[],title:this.l10n.summaryHeader,id:"h5p-interactive-book-chapter-summary",isSummary:!0}),e}},{key:"toggleChapter",value:function(t,e){e=void 0!==e?e:!t.classList.contains("h5p-interactive-book-navigation-closed");var r=t.querySelector(".h5p-interactive-book-navigation-sectionlist"),n=t.getElementsByClassName("h5p-interactive-book-navigation-chapter-accordion")[0];t.querySelector(".h5p-interactive-book-navigation-chapter-button").setAttribute("aria-expanded",(!e).toString()),!0===e?(t.classList.add("h5p-interactive-book-navigation-closed"),n&&(n.classList.remove("icon-expanded"),n.classList.add("icon-collapsed"),r&&(r.setAttribute("aria-hidden",!0),r.setAttribute("tabindex","-1")))):(t.classList.remove("h5p-interactive-book-navigation-closed"),n&&(n.classList.remove("icon-collapsed"),n.classList.add("icon-expanded"),r&&(r.removeAttribute("aria-hidden"),r.removeAttribute("tabindex"))))}},{key:"redirectHandler",value:function(t){var e=this;if(this.chapterNodes.forEach((function(r,n){e.toggleChapter(r,n!==t)})),this.parent.trigger("resize"),t!==this.focusedChapter){var r=this.chapterNodes[t].querySelector(".h5p-interactive-book-navigation-chapter-button");this.setFocusToItem(r,t,!0)}}},{key:"resetIndicators",value:function(){var t=this;this.chapterNodes.forEach((function(e,r){t.updateChapterProgressIndicator(r,"BLANK");var n,o=a(e.getElementsByClassName("h5p-interactive-book-navigation-section"));try{for(o.s();!(n=o.n()).done;){var i=n.value.querySelector(".h5p-interactive-book-navigation-section-icon");i&&(i.classList.remove("icon-question-answered"),i.classList.add("icon-chapter-blank"))}}catch(t){o.e(t)}finally{o.f()}}))}},{key:"updateChapterProgressIndicator",value:function(t,e){if(this.behaviour.progressIndicators&&!this.chapters[t].isSummary){var r=this.chapterNodes[t].getElementsByClassName("h5p-interactive-book-navigation-chapter-progress")[0];"BLANK"===e?(r.classList.remove("icon-chapter-started"),r.classList.remove("icon-chapter-done"),r.classList.add("icon-chapter-blank")):"DONE"===e?(r.classList.remove("icon-chapter-blank"),r.classList.remove("icon-chapter-started"),r.classList.add("icon-chapter-done")):"STARTED"===e&&(r.classList.remove("icon-chapter-blank"),r.classList.remove("icon-chapter-done"),r.classList.add("icon-chapter-started"))}}},{key:"setSectionMarker",value:function(t,e){var r=this.chapterNodes[t].querySelector(".h5p-interactive-book-navigation-section-"+e+" .h5p-interactive-book-navigation-section-icon");r&&(r.classList.remove("icon-chapter-blank"),r.classList.add("icon-question-answered"))}},{key:"getNodesFromChapter",value:function(t,e){var r=this,n=document.createElement("li"),a="h5p-interactive-book-sectionlist-"+e;if(n.classList.add("h5p-interactive-book-navigation-chapter"),t.isSummary){n.classList.add("h5p-interactive-book-navigation-summary-button");var o=this.parent.chapters[e].instance.summaryMenuButton;return o.classList.add("h5p-interactive-book-navigation-chapter-button"),n.appendChild(o),n}var i=document.createElement("div");i.classList.add("h5p-interactive-book-navigation-chapter-accordion");var s=document.createElement("div");s.classList.add("h5p-interactive-book-navigation-chapter-title-text"),s.innerHTML=t.title,s.setAttribute("title",t.title);var c=document.createElement("div");this.behaviour.progressIndicators&&(c.classList.add("icon-chapter-blank"),c.classList.add("h5p-interactive-book-navigation-chapter-progress"));var u=document.createElement("button");u.setAttribute("tabindex",0===e?"0":"-1"),u.classList.add("h5p-interactive-book-navigation-chapter-button"),this.parent.activeChapter!==e?(i.classList.add("icon-collapsed"),u.setAttribute("aria-expanded","false")):(i.classList.add("icon-expanded"),u.setAttribute("aria-expanded","true")),u.setAttribute("aria-controls",a),u.onclick=function(t){var n=!t.currentTarget.querySelector(".h5p-interactive-book-navigation-chapter-accordion").classList.contains("hidden"),a="true"===t.currentTarget.getAttribute("aria-expanded");if(r.isOpenOnMobile()&&r.parent.trigger("toggleMenu"),r.isOpenOnMobile()||!n||!a){var o={h5pbookid:r.parent.contentId,chapter:r.chapters[e].id,section:0};r.parent.trigger("newChapter",o)}n&&(r.toggleChapter(t.currentTarget.parentElement),r.parent.trigger("resize"))},u.appendChild(i),u.appendChild(s),u.appendChild(c),n.appendChild(u),this.parent.activeChapter===e?n.querySelector(".h5p-interactive-book-navigation-chapter-button").classList.add("h5p-interactive-book-navigation-current"):this.toggleChapter(n,!0);var l=document.createElement("ul");l.classList.add("h5p-interactive-book-navigation-sectionlist"),l.id=a;for(var p=[],h=0;h<this.chapters[e].sections.length;h++)if(this.parent.chapters[e].sections[h].isTask){var d=this.createSectionLink(e,h);p.push(d),l.appendChild(d)}else{var f=this.parent.params.chapters[e].params.content[h].content;if("H5P.AdvancedText"===f.library.split(" ")[0]){var v=document.createElement("div");v.innerHTML=f.params.text;for(var m=v.querySelectorAll("h2, h3"),b=0;b<m.length;b++){var g=m[b],y=this.createSectionLink(e,h,g.textContent,b);p.push(y),l.appendChild(y)}}}if(t.tasksLeft&&(t.maxTasks=t.tasksLeft),0===p.length){var k=n.querySelector(".h5p-interactive-book-navigation-chapter-accordion");k&&k.classList.add("hidden")}return n.appendChild(l),n}},{key:"createSectionLink",value:function(t,e){var r=this,n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null,a=arguments.length>3&&void 0!==arguments[3]?arguments[3]:null,o=this.chapters[t].sections[e],i=document.createElement("div");i.innerHTML=n||o.title,i.setAttribute("title",n||o.title),i.classList.add("h5p-interactive-book-navigation-section-title");var s=document.createElement("div");s.classList.add("h5p-interactive-book-navigation-section-icon"),s.classList.add("icon-chapter-blank"),this.parent.chapters[t].sections[e].isTask&&s.classList.add("h5p-interactive-book-navigation-section-task");var c=document.createElement("button");c.classList.add("section-button"),c.setAttribute("tabindex","-1"),c.onclick=function(e){var n={h5pbookid:r.parent.contentId,chapter:r.chapters[t].id,section:o.id};null!==a&&(n.headerNumber=a),r.parent.trigger("newChapter",n),r.isOpenOnMobile()&&r.parent.trigger("toggleMenu"),e.preventDefault()},c.appendChild(s),c.appendChild(i);var u=document.createElement("li");return u.classList.add("h5p-interactive-book-navigation-section"),u.classList.add("h5p-interactive-book-navigation-section-"+e),u.appendChild(c),u}},{key:"getChapterNodes",value:function(){var t=this;return this.chapters.map((function(e,r){return t.getNodesFromChapter(e,r)}))}},{key:"isOpenOnMobile",value:function(){return this.parent.isMenuOpen()&&this.parent.isSmallSurface()}},{key:"addTransformListener",value:function(){var t=this;this.container.addEventListener("transitionend",(function(e){"flex-basis"===e.propertyName&&t.parent.trigger("resize")}))}}],e&&i(t.prototype,e),r&&i(t,r),Object.defineProperty(t,"prototype",{writable:!1}),o}();function p(t){return p="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},p(t)}function h(){return h=Object.assign?Object.assign.bind():function(t){for(var e=1;e<arguments.length;e++){var r=arguments[e];for(var n in r)Object.prototype.hasOwnProperty.call(r,n)&&(t[n]=r[n])}return t},h.apply(this,arguments)}function d(t,e){for(var r=0;r<e.length;r++){var n=e[r];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(t,n.key,n)}}function f(t,e){return f=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(t,e){return t.__proto__=e,t},f(t,e)}function v(t){var e=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(t){return!1}}();return function(){var r,n=m(t);if(e){var a=m(this).constructor;r=Reflect.construct(n,arguments,a)}else r=n.apply(this,arguments);return function(t,e){if(e&&("object"===p(e)||"function"==typeof e))return e;if(void 0!==e)throw new TypeError("Derived constructors may only return object or undefined");return function(t){if(void 0===t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return t}(t)}(this,r)}}function m(t){return m=Object.setPrototypeOf?Object.getPrototypeOf.bind():function(t){return t.__proto__||Object.getPrototypeOf(t)},m(t)}const b=function(){!function(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function");t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,writable:!0,configurable:!0}}),Object.defineProperty(t,"prototype",{writable:!1}),e&&f(t,e)}(a,H5P.EventDispatcher);var t,e,r,n=v(a);function a(t,e,r,o,i){var s;!function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,a),(s=n.call(this)).id=t,s.parent=r,s.params=o||{},s.params.l10n=o.l10n,s.params.a11y=h({progress:"Page @page of @total",menu:"Toggle navigation menu"},s.params.a11y||{}),s.totalChapters=e,s.arrows=s.addArrows(),s.progressBar=s.createProgressBar(),s.progressIndicator=s.createProgressIndicator(),s.chapterTitle=s.createChapterTitle(),s.menuToggleButton=s.createMenuToggleButton();var c=document.createElement("div");return c.classList.add("h5p-interactive-book-status"),s.params.displayToTopButton&&c.appendChild(s.createToTopButton()),s.params.displayFullScreenButton&&H5P.fullscreenSupported&&c.appendChild(s.createFullScreenButton()),c.appendChild(s.arrows.buttonWrapperNext),c.appendChild(s.arrows.buttonWrapperPrevious),s.params.displayMenuToggleButton&&c.appendChild(s.menuToggleButton),c.appendChild(s.progressIndicator.wrapper),c.appendChild(s.chapterTitle.wrapper),s.wrapper=document.createElement("div"),s.wrapper.classList.add(i),s.wrapper.setAttribute("tabindex","-1"),s.wrapper.appendChild(s.progressBar.wrapper),s.wrapper.appendChild(c),s.on("updateStatusBar",s.updateStatusBar),s.on("seqChapter",(function(t){var e={h5pbookid:s.parent.contentId};t.data.toTop&&(e.section="top"),"next"===t.data.direction?s.parent.activeChapter+1<s.parent.chapters.length?e.chapter="h5p-interactive-book-chapter-".concat(s.parent.chapters[s.parent.activeChapter+1].instance.subContentId):s.parent.hasSummary()&&s.parent.activeChapter+1===s.parent.chapters.length&&s.parent.trigger("viewSummary",e):"prev"===t.data.direction&&s.parent.activeChapter>0&&(e.chapter="h5p-interactive-book-chapter-".concat(s.parent.chapters[s.parent.activeChapter-1].instance.subContentId)),e.chapter&&s.parent.trigger("newChapter",e)})),s}return t=a,(e=[{key:"updateProgressBar",value:function(t){var e="".concat(t/this.totalChapters*100,"%");this.progressBar.progress.style.width=e;var r=this.params.a11y.progress.replace("@page",t).replace("@total",this.totalChapters);this.progressBar.progress.title=r}},{key:"updateA11yProgress",value:function(t){this.progressIndicator.hiddenButRead.innerHTML=this.params.a11y.progress.replace("@page",t).replace("@total",this.totalChapters)}},{key:"updateStatusBar",value:function(){var t=this.parent.getActiveChapter()+1,e=this.parent.chapters[t-1].title;this.progressIndicator.current.innerHTML=t,this.updateA11yProgress(t),this.updateProgressBar(t),this.chapterTitle.text.innerHTML=e,this.chapterTitle.text.setAttribute("title",e),this.parent.activeChapter<=0?this.setButtonStatus("Previous",!0):this.setButtonStatus("Previous",!1),this.parent.activeChapter+1>=this.totalChapters?this.setButtonStatus("Next",!0):this.setButtonStatus("Next",!1)}},{key:"addArrows",value:function(){var t=this,e={};return e.buttonPrevious=document.createElement("div"),e.buttonPrevious.classList.add("navigation-button","icon-previous"),e.buttonPrevious.setAttribute("title",this.params.l10n.previousPage),e.buttonWrapperPrevious=document.createElement("button"),e.buttonWrapperPrevious.classList.add("h5p-interactive-book-status-arrow","h5p-interactive-book-status-button","previous"),e.buttonWrapperPrevious.setAttribute("aria-label",this.params.l10n.previousPage),e.buttonWrapperPrevious.onclick=function(){t.trigger("seqChapter",{direction:"prev",toTop:!0})},e.buttonWrapperPrevious.appendChild(e.buttonPrevious),e.buttonNext=document.createElement("div"),e.buttonNext.classList.add("navigation-button","icon-next"),e.buttonNext.setAttribute("title",this.params.l10n.nextPage),e.buttonWrapperNext=document.createElement("button"),e.buttonWrapperNext.classList.add("h5p-interactive-book-status-arrow","h5p-interactive-book-status-button","next"),e.buttonWrapperNext.setAttribute("aria-label",this.params.l10n.nextPage),e.buttonWrapperNext.onclick=function(){t.trigger("seqChapter",{direction:"next",toTop:!0})},e.buttonWrapperNext.appendChild(e.buttonNext),e}},{key:"createMenuToggleButton",value:function(){var t=this,e=document.createElement("a");e.classList.add("icon-menu");var r=document.createElement("button");return r.classList.add("h5p-interactive-book-status-menu"),r.classList.add("h5p-interactive-book-status-button"),r.setAttribute("aria-label",this.params.a11y.menu),r.setAttribute("aria-expanded","false"),r.setAttribute("aria-controls","h5p-interactive-book-navigation-menu"),r.onclick=function(){t.parent.trigger("toggleMenu")},r.appendChild(e),r}},{key:"isMenuOpen",value:function(){return this.menuToggleButton.classList.contains("h5p-interactive-book-status-menu-active")}},{key:"createProgressBar",value:function(){var t=document.createElement("div");t.classList.add("h5p-interactive-book-status-progressbar-front"),t.setAttribute("tabindex","-1");var e=document.createElement("div");return e.classList.add("h5p-interactive-book-status-progressbar-back"),e.appendChild(t),{wrapper:e,progress:t}}},{key:"createChapterTitle",value:function(){var t=document.createElement("h1");t.classList.add("title");var e=document.createElement("div");return e.classList.add("h5p-interactive-book-status-chapter"),e.appendChild(t),{wrapper:e,text:t}}},{key:"createToTopButton",value:function(){var t=this,e=document.createElement("div");e.classList.add("icon-up"),e.classList.add("navigation-button");var r=document.createElement("button");return r.classList.add("h5p-interactive-book-status-to-top"),r.classList.add("h5p-interactive-book-status-button"),r.classList.add("h5p-interactive-book-status-arrow"),r.setAttribute("aria-label",this.params.l10n.navigateToTop),r.addEventListener("click",(function(){t.parent.trigger("scrollToTop"),document.querySelector(".h5p-interactive-book-status-menu").focus()})),r.appendChild(e),r}},{key:"setVisibility",value:function(t){t?this.wrapper.classList.add("footer-hidden"):this.wrapper.classList.remove("footer-hidden")}},{key:"createProgressIndicator",value:function(){var t=document.createElement("span");t.classList.add("h5p-interactive-book-status-progress-number"),t.setAttribute("aria-hidden","true");var e=document.createElement("span");e.classList.add("h5p-interactive-book-status-progress-divider"),e.innerHTML=" / ",e.setAttribute("aria-hidden","true");var r=document.createElement("span");r.classList.add("h5p-interactive-book-status-progress-number"),r.innerHTML=this.totalChapters,r.setAttribute("aria-hidden","true");var n=document.createElement("p");n.classList.add("hidden-but-read");var a=document.createElement("p");a.classList.add("h5p-interactive-book-status-progress"),a.appendChild(t),a.appendChild(e),a.appendChild(r),a.appendChild(n);var o=document.createElement("div");return o.classList.add("h5p-interactive-book-status-progress-wrapper"),o.appendChild(a),{wrapper:o,current:t,total:r,divider:e,progressText:a,hiddenButRead:n}}},{key:"setButtonStatus",value:function(t,e){e?(this.arrows["buttonWrapper"+t].setAttribute("disabled","disabled"),this.arrows["button"+t].classList.add("disabled")):(this.arrows["buttonWrapper"+t].removeAttribute("disabled"),this.arrows["button"+t].classList.remove("disabled"))}},{key:"createFullScreenButton",value:function(){var t=this,e=function(){!0===H5P.isFullscreen?H5P.exitFullScreen():H5P.fullScreen(t.parent.mainWrapper,t.parent)},r=document.createElement("button");return r.classList.add("h5p-interactive-book-status-fullscreen"),r.classList.add("h5p-interactive-book-status-button"),r.classList.add("h5p-interactive-book-enter-fullscreen"),r.setAttribute("title",this.params.l10n.fullscreen),r.setAttribute("aria-label",this.params.l10n.fullscreen),r.addEventListener("click",e),r.addEventListener("keyPress",(function(t){13!==t.which&&32!==t.which||(e(),t.preventDefault())})),this.parent.on("enterFullScreen",(function(){t.parent.isFullscreen=!0,r.classList.remove("h5p-interactive-book-enter-fullscreen"),r.classList.add("h5p-interactive-book-exit-fullscreen"),r.setAttribute("title",t.params.l10n.exitFullscreen),r.setAttribute("aria-label",t.params.l10n.exitFullScreen),t.parent.pageContent.updateFooter()})),this.parent.on("exitFullScreen",(function(){t.parent.isFullscreen=!1,r.classList.remove("h5p-interactive-book-exit-fullscreen"),r.classList.add("h5p-interactive-book-enter-fullscreen"),r.setAttribute("title",t.params.l10n.fullscreen),r.setAttribute("aria-label",t.params.l10n.fullscreen),t.parent.pageContent.updateFooter()})),r}}])&&d(t.prototype,e),r&&d(t,r),Object.defineProperty(t,"prototype",{writable:!1}),a}();function g(t){return g="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},g(t)}function y(t,e){for(var r=0;r<e.length;r++){var n=e[r];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(t,n.key,n)}}function k(t,e){return k=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(t,e){return t.__proto__=e,t},k(t,e)}function w(t){var e=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(t){return!1}}();return function(){var r,n=C(t);if(e){var a=C(this).constructor;r=Reflect.construct(n,arguments,a)}else r=n.apply(this,arguments);return function(t,e){if(e&&("object"===g(e)||"function"==typeof e))return e;if(void 0!==e)throw new TypeError("Derived constructors may only return object or undefined");return function(t){if(void 0===t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return t}(t)}(this,r)}}function C(t){return C=Object.setPrototypeOf?Object.getPrototypeOf.bind():function(t){return t.__proto__||Object.getPrototypeOf(t)},C(t)}const S=function(){!function(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function");t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,writable:!0,configurable:!0}}),Object.defineProperty(t,"prototype",{writable:!1}),e&&k(t,e)}(a,H5P.EventDispatcher);var t,e,r,n=w(a);function a(t,e,r,o,i){var s;return function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,a),(s=n.call(this)).parent=i,s.params=t,s.contentId=o,s.container=s.createContainer(),t.coverMedium?(s.visuals=s.createVisualsElement(t.coverMedium),s.visuals&&s.container.appendChild(s.visuals)):s.container.classList.add("h5p-cover-nographics"),s.container.appendChild(s.createTitleElement(e)),t.coverDescription&&s.container.appendChild(s.createDescriptionElement(t.coverDescription)),s.container.appendChild(s.createReadButton(r)),s}return t=a,(e=[{key:"createContainer",value:function(){var t=document.createElement("div");return t.classList.add("h5p-interactive-book-cover"),t}},{key:"createVisualsElement",value:function(t){if(!t||!t.params)return null;var e=document.createElement("div");return e.classList.add("h5p-interactive-book-cover-graphics"),e}},{key:"initMedia",value:function(){if(this.visuals&&this.params.coverMedium){var t=this.params.coverMedium;"H5P.Video"===(t.library||"").split(" ")[0]&&(t.params.visuals.fit=!1);var e=H5P.newRunnable(t,this.contentId,H5P.jQuery(this.visuals),!1,{metadata:t.medatata});if(this.bubbleUp(e,"resize",this.parent),this.bubbleDown(this.parent,"resize",[e]),"H5P.Image"===(t.library||"").split(" ")[0]){var r=this.visuals.querySelector("img")||this.visuals.querySelector(".h5p-placeholder");r.style.height="auto",r.style.width="auto"}this.visuals.appendChild(this.createCoverBar())}}},{key:"bubbleUp",value:function(t,e,r){t.on(e,(function(t){r.bubblingUpwards=!0,r.trigger(e,t),r.bubblingUpwards=!1}))}},{key:"bubbleDown",value:function(t,e,r){t.on(e,(function(n){t.bubblingUpwards||r.forEach((function(t){t.trigger(e,n)}))}))}},{key:"createImage",value:function(t,e,r){var n=document.createElement("img");return n.classList.add("h5p-interactive-book-cover-image"),n.src=H5P.getPath(t,e),n.setAttribute("draggable","false"),r&&(n.alt=r),n}},{key:"createCoverBar",value:function(){var t=document.createElement("div");return t.classList.add("h5p-interactive-book-cover-bar"),t}},{key:"createTitleElement",value:function(t){var e=document.createElement("p");e.innerHTML=t;var r=document.createElement("div");return r.classList.add("h5p-interactive-book-cover-title"),r.appendChild(e),r}},{key:"createDescriptionElement",value:function(t){if(!t)return null;var e=document.createElement("div");return e.classList.add("h5p-interactive-book-cover-description"),e.innerHTML=t,e}},{key:"createReadButton",value:function(t){var e=this,r=document.createElement("button");r.innerHTML=t,r.onclick=function(){e.removeCover()};var n=document.createElement("div");return n.classList.add("h5p-interactive-book-cover-readbutton"),n.appendChild(r),n}},{key:"removeCover",value:function(){this.container.parentElement&&(this.container.parentElement.classList.remove("covered"),this.container.parentElement.removeChild(this.container)),this.hidden=!0,this.parent.trigger("coverRemoved")}}])&&y(t.prototype,e),r&&y(t,r),Object.defineProperty(t,"prototype",{writable:!1}),a}();r(2683);var x=r(4308),O=r.n(x);function E(t,e){for(var r=0;r<e.length;r++){var n=e[r];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(t,n.key,n)}}var L=function(){function t(){!function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,t)}var e,r,n;return e=t,n=[{key:"setBase",value:function(e){e&&(t.colorBase=O()(e),t.colorText=[t.DEFAULT_COLOR_BG,t.computeContrastColor(t.colorBase),t.computeContrastColor(t.colorBase,t.DEFAULT_COLOR_BG)].map((function(e){return{color:e,contrast:t.colorBase.contrast(e)}})).reduce((function(t,e){return e.contrast>t.contrast?e:t}),{contrast:0}).color)}},{key:"getColor",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};if("string"==typeof e.opacity&&/^([0-9]|[1-8][0-9]|9[0-9]|100)(\.\d+)?\s?%$/.test(e.opacity)&&(e.opacity=parseInt(e.opacity)/100),"number"!=typeof e.opacity||e.opacity<0||e.opacity>1)return t;var r=O()("#ffffff").rgb().array();return O().rgb(t.rgb().array().map((function(t,n){return e.opacity*t+(1-e.opacity)*r[n]})))}},{key:"isBaseColor",value:function(e){return O()(e).hex()===t.colorBase.hex()}},{key:"computeContrastColor",value:function(e,r){for(var n,a=(r=r||e).luminosity(),o=function(o){if((n=O().rgb(e.rgb().array().map((function(t){return t*(a>.5?1-o:1+o)})))).contrast(r)>=t.MINIMUM_ACCEPTABLE_CONTRAST)return"break"},i=0;i<=1&&"break"!==o(i);i+=.05);return n}},{key:"getContentTypeCSS",value:function(e){return t.COLOR_OVERRIDES[e]?t.COLOR_OVERRIDES[e].getCSS():""}},{key:"getCSS",value:function(){return":root{\n      --color-base: ".concat(t.colorBase,";\n      --color-base-5: ").concat(t.getColor(t.colorBase,{opacity:.05}),";\n      --color-base-10: ").concat(t.getColor(t.colorBase,{opacity:.1}),";\n      --color-base-20: ").concat(t.getColor(t.colorBase,{opacity:.2}),";\n      --color-base-75: ").concat(t.getColor(t.colorBase,{opacity:.75}),";\n      --color-base-80: ").concat(t.getColor(t.colorBase,{opacity:.8}),";\n      --color-base-85: ").concat(t.getColor(t.colorBase,{opacity:.85}),";\n      --color-base-90: ").concat(t.getColor(t.colorBase,{opacity:.9}),";\n      --color-base-95: ").concat(t.getColor(t.colorBase,{opacity:.95}),";\n      --color-text: ").concat(t.colorText,";\n      --color-contrast: ").concat(t.computeContrastColor(t.colorBase,t.DEFAULT_COLOR_BG),";\n    }")}}],(r=null)&&E(e.prototype,r),n&&E(e,n),Object.defineProperty(e,"prototype",{writable:!1}),t}();function P(t){return P="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},P(t)}function A(t,e){var r="undefined"!=typeof Symbol&&t[Symbol.iterator]||t["@@iterator"];if(!r){if(Array.isArray(t)||(r=function(t,e){if(!t)return;if("string"==typeof t)return T(t,e);var r=Object.prototype.toString.call(t).slice(8,-1);"Object"===r&&t.constructor&&(r=t.constructor.name);if("Map"===r||"Set"===r)return Array.from(t);if("Arguments"===r||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r))return T(t,e)}(t))||e&&t&&"number"==typeof t.length){r&&(t=r);var n=0,a=function(){};return{s:a,n:function(){return n>=t.length?{done:!0}:{done:!1,value:t[n++]}},e:function(t){throw t},f:a}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var o,i=!0,s=!1;return{s:function(){r=r.call(t)},n:function(){var t=r.next();return i=t.done,t},e:function(t){s=!0,o=t},f:function(){try{i||null==r.return||r.return()}finally{if(s)throw o}}}}function T(t,e){(null==e||e>t.length)&&(e=t.length);for(var r=0,n=new Array(e);r<e;r++)n[r]=t[r];return n}function j(t,e){for(var r=0;r<e.length;r++){var n=e[r];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(t,n.key,n)}}function I(t,e){return I=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(t,e){return t.__proto__=e,t},I(t,e)}function M(t){var e=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(t){return!1}}();return function(){var r,n=F(t);if(e){var a=F(this).constructor;r=Reflect.construct(n,arguments,a)}else r=n.apply(this,arguments);return function(t,e){if(e&&("object"===P(e)||"function"==typeof e))return e;if(void 0!==e)throw new TypeError("Derived constructors may only return object or undefined");return function(t){if(void 0===t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return t}(t)}(this,r)}}function F(t){return F=Object.setPrototypeOf?Object.getPrototypeOf.bind():function(t){return t.__proto__||Object.getPrototypeOf(t)},F(t)}L.DEFAULT_COLOR_BASE=O()("#1768c4"),L.DEFAULT_COLOR_BG=O()("#ffffff"),L.MINIMUM_ACCEPTABLE_CONTRAST=4.5,L.colorBase=L.DEFAULT_COLOR_BASE,L.colorText=L.DEFAULT_COLOR_BG;const B=function(){!function(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function");t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,writable:!0,configurable:!0}}),Object.defineProperty(t,"prototype",{writable:!1}),e&&I(t,e)}(a,H5P.EventDispatcher);var t,e,r,n=M(a);function a(t,e,r){var o;return function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,a),(o=n.call(this)).parent=e,o.behaviour=t.behaviour,o.l10n=t.l10n,o.chapters=r||[],o.subContentId="summary",o.wrapper=null,o.summaryMenuButton=o.createSummaryButton(),o.filterActionAll="all",o.filterActionUnanswered="unanswered",o.bookCompleted=!1,o.tempState=JSON.stringify(o.parent.previousState&&o.parent.previousState.chapters?o.parent.previousState.chapters:o.getChapterStats()),e.on("bookCompleted",(function(t){return o.setBookComplete(t.data.completed)})),e.on("toggleMenu",(function(){var t=document.querySelector(".h5p-interactive-book-summary-footer");t&&o.bookCompleted&&(o.parent.isMenuOpen()?t.classList.add("menu-open"):t.classList.remove("menu-open"))})),o}return t=a,e=[{key:"setBookComplete",value:function(t){var e=this.parent.mainWrapper?this.parent.mainWrapper[0].querySelector(".h5p-interactive-book-summary-footer"):null;!e&&this.parent.isSmallSurface()&&((e=document.createElement("div")).classList.add("h5p-interactive-book-summary-footer"),e.appendChild(this.createSummaryButton()),this.parent.mainWrapper.append(e)),e&&t&&setTimeout((function(){return e.classList.add("show-footer")}),0),this.bookCompleted=t,Array.from(document.querySelectorAll(".h5p-interactive-book-summary-menu-button")).forEach((function(e){return e.setAttribute("data-book-completed",t.toString())}))}},{key:"setChapters",value:function(t){this.chapters=Array.isArray(t)?t:[]}},{key:"setSummaryMenuButtonDisabled",value:function(){var t=!(arguments.length>0&&void 0!==arguments[0])||arguments[0];this.summaryMenuButton.disabled=t}},{key:"setFilter",value:function(t){var e=this,r=this.wrapper.querySelector(".h5p-interactive-book-summary-overview-list"),n=Array.from(r.querySelectorAll(".h5p-interactive-book-summary-overview-section"));n.forEach((function(t){t.classList.remove("h5p-interactive-book-summary-top-section"),t.classList.remove("h5p-interactive-book-summary-bottom-section")}));var a=r.querySelector(".h5p-interactive-book-summary-overview-list-empty");if(a.style.display="none",t===this.filterActionUnanswered){r.classList.add("h5p-interactive-book-summary-overview-list-only-unanswered");var o=n.filter((function(t){return!t.classList.contains("h5p-interactive-book-summary-no-interactions")}));o.length?(o[0].classList.add("h5p-interactive-book-summary-top-section"),o[o.length-1].classList.add("h5p-interactive-book-summary-bottom-section")):a.style.display="block"}else t===this.filterActionAll&&r.classList.remove("h5p-interactive-book-summary-overview-list-only-unanswered");setTimeout((function(){return e.trigger("resize")}),1)}},{key:"createSummaryButton",value:function(){var t=this,e=document.createElement("button");e.classList.add("h5p-interactive-book-summary-menu-button"),e.onclick=function(){var e={h5pbookid:t.parent.contentId,chapter:"h5p-interactive-book-chapter-summary",section:"top"};t.parent.trigger("newChapter",e),t.parent.isMenuOpen()&&t.parent.isSmallSurface()&&t.parent.trigger("toggleMenu")};var r=document.createElement("span");r.classList.add("h5p-interactive-book-summary-icon"),r.classList.add("icon-paper"),r.setAttribute("aria-hidden","true");var n=document.createElement("span");n.classList.add("h5p-interactive-book-summary-text"),n.innerHTML=this.l10n.summaryAndSubmit;var a=document.createElement("span");return a.classList.add("h5p-interactive-book-summary-menu-button-arrow"),a.classList.add("icon-up"),a.setAttribute("aria-hidden","true"),e.appendChild(r),e.appendChild(n),e.appendChild(a),e}},{key:"createCircle",value:function(t){var e=L.computeContrastColor(L.colorBase,L.DEFAULT_COLOR_BG),r=document.createElement("div");return r.classList.add("h5p-interactive-book-summary-progress-circle"),r.setAttribute("data-value",t),r.setAttribute("data-start-angle",-Math.PI/3),r.setAttribute("data-thickness",13),r.setAttribute("data-empty-fill","rgba(".concat(e.rgb().array().join(", "),", .1)")),r.setAttribute("data-fill",JSON.stringify({color:e.hex()})),r}},{key:"createProgress",value:function(t,e,r,n){var a=arguments.length>4&&void 0!==arguments[4]&&arguments[4],o=arguments.length>5?arguments[5]:void 0,i=arguments.length>6?arguments[6]:void 0,s=document.createElement("div"),c=document.createElement("h3");c.innerHTML=t;var u=100*r/n;void 0===o&&(o=r),void 0===i&&(i=n);var l=document.createElement("p");if(l.classList.add("h5p-interactive-book-summary-progressbox-bigtext"),l.innerHTML=Math.round(u)+"%",a){var p=document.createElement("span");p.classList.add("absolute-value"),p.innerHTML=r;var h=document.createElement("span");h.classList.add("separator"),h.innerHTML="/";var d=document.createElement("span");d.classList.add("absolute-value"),d.innerHTML=n,l.innerHTML="",l.appendChild(p),l.appendChild(h),l.appendChild(d)}var f=document.createElement("span");f.classList.add("h5p-interactive-book-summary-progressbox-smalltext"),f.innerHTML=e.replace("@count",o).replace("@total",i),s.appendChild(c),s.appendChild(l),s.appendChild(f);var v=document.createElement("div");return v.appendChild(s),v.appendChild(this.createCircle(r/n)),v}},{key:"addScoreProgress",value:function(){var t,e=0,r=0,n=A(this.chapters);try{for(n.s();!(t=n.n()).done;){var a=t.value;e+=a.maxTasks,r+=a.tasksLeft}}catch(t){n.e(t)}finally{n.f()}var o=this.createProgress(this.l10n.totalScoreLabel,this.l10n.interactionsProgressSubtext,this.parent.getScore(),this.parent.getMaxScore(),!0,Math.max(e-r,0),e);o.classList.add("h5p-interactive-book-summary-progress-container"),o.classList.add("h5p-interactive-book-summary-score-progress");var i=o.querySelector(".h5p-interactive-book-summary-progress-circle");return i.setAttribute("data-empty-fill","rgb(198, 220, 212)"),i.setAttribute("data-fill",JSON.stringify({color:"#0e7c57"})),o}},{key:"addBookProgress",value:function(){var t=this.createProgress(this.l10n.bookProgress,this.l10n.bookProgressSubtext,this.chapters.filter((function(t){return t.completed})).length,this.chapters.length);return t.classList.add("h5p-interactive-book-summary-progress-container"),t.classList.add("h5p-interactive-book-summary-book-progress"),t}},{key:"addInteractionsProgress",value:function(){var t,e=0,r=0,n=A(this.chapters);try{for(n.s();!(t=n.n()).done;){var a=t.value;e+=a.maxTasks,r+=a.tasksLeft}}catch(t){n.e(t)}finally{n.f()}var o=this.createProgress(this.l10n.interactionsProgress,this.l10n.interactionsProgressSubtext,Math.max(e-r,0),e);return o.classList.add("h5p-interactive-book-summary-progress-container"),o.classList.add("h5p-interactive-book-summary-interactions-progress"),o}},{key:"addProgressIndicators",value:function(){if(this.behaviour.progressIndicators){var t=document.createElement("div");t.classList.add("h5p-interactive-box-summary-progress"),t.appendChild(this.addScoreProgress()),t.appendChild(this.addBookProgress()),t.appendChild(this.addInteractionsProgress()),setTimeout((function(){return H5P.jQuery(".h5p-interactive-book-summary-progress-circle").circleProgress()}),100),this.wrapper.appendChild(t)}}},{key:"addActionButtons",value:function(){var t=this,e=document.createElement("div");if(e.classList.add("h5p-interactive-book-summary-buttons"),this.checkTheAnswerIsUpdated(),this.parent.isSubmitButtonEnabled&&this.parent.isAnswerUpdated){var r=this.addButton("icon-paper-pencil",this.l10n.submitReport);r.classList.add("h5p-interactive-book-summary-submit"),r.onclick=function(){t.trigger("submitted"),t.parent.triggerXAPIScored(t.parent.getScore(),t.parent.getMaxScore(),"completed"),e.classList.add("submitted"),e.querySelector(".answers-submitted").focus(),t.tempState=JSON.stringify(t.getChapterStats()),t.parent.isAnswerUpdated=!1},e.appendChild(r)}this.behaviour.enableRetry&&e.appendChild(this.createRestartButton()),e.appendChild(this.createSubmittedConfirmation()),this.wrapper.appendChild(e)}},{key:"createRestartButton",value:function(){var t=this,e=this.addButton("icon-restart",this.l10n.restartLabel);return e.classList.add("h5p-interactive-book-summary-restart"),e.onclick=function(){return t.parent.resetTask()},e}},{key:"createSubmittedConfirmation",value:function(){var t=document.createElement("div");t.classList.add("h5p-interactive-book-summary-submitted");var e=document.createElement("span");e.classList.add("icon-chapter-done"),e.classList.add("icon-check-mark"),t.appendChild(e);var r=document.createElement("p");return r.innerHTML=this.l10n.yourAnswersAreSubmittedForReview,r.tabIndex=-1,r.classList.add("answers-submitted"),t.appendChild(r),this.behaviour.enableRetry&&t.appendChild(this.createRestartButton()),t}},{key:"addButton",value:function(t,e){var r=document.createElement("button");r.type="button",r.classList.add("h5p-interactive-book-summary-button"),r.innerHTML=e;var n=document.createElement("span");return n.classList.add(t),n.setAttribute("aria-hidden","true"),r.appendChild(n),r}},{key:"createSectionList",value:function(t,e){var r,n=this,a=[],o=!1,i=A(t);try{var s=function(){var t=r.value,i=document.createElement("li");if(i.classList.add("h5p-interactive-book-summary-overview-section-details"),n.behaviour.progressIndicators){var s=document.createElement("span");s.classList.add("h5p-interactive-book-summary-section-icon"),s.classList.add(t.taskDone?"icon-chapter-done":"icon-chapter-blank"),i.appendChild(s)}var c=document.createElement("button");c.type="button",c.classList.add("h5p-interactive-book-summary-section-title"),c.onclick=function(){var r={h5pbookid:n.parent.contentId,chapter:"h5p-interactive-book-chapter-".concat(e),section:"h5p-interactive-book-section-".concat(t.instance.subContentId)};n.parent.trigger("newChapter",r)};var u=t.instance.contentData&&t.instance.contentData.metadata&&t.instance.contentData.metadata.title,l=t.content&&t.content.metadata&&t.content.metadata.title;c.innerHTML=u||l||"Untitled";var p=document.createElement("div");p.classList.add("h5p-interactive-book-summary-section-score"),p.innerHTML="-","function"==typeof t.instance.getScore&&(p.innerHTML=n.l10n.scoreText.replace("@score",t.instance.getScore()).replace("@maxscore",t.instance.getMaxScore())),t.taskDone?i.classList.add("h5p-interactive-book-summary-overview-section-details-task-done"):o=!0,i.appendChild(c),i.appendChild(p),a.push(i)};for(i.s();!(r=i.n()).done;)s()}catch(t){i.e(t)}finally{i.f()}if(a.length){var c=document.createElement("div");c.classList.add("h5p-interactive-book-summary-overview-section-score-header");var u=document.createElement("div");u.innerHTML=this.l10n.score,c.appendChild(u),a.unshift(c)}return{hasUnansweredInteractions:o,sectionElements:a}}},{key:"createChapterOverview",value:function(t){var e=this,r=document.createElement("li");r.classList.add("h5p-interactive-book-summary-overview-section");var n=document.createElement("h4");n.onclick=function(){var r={h5pbookid:e.parent.contentId,chapter:"h5p-interactive-book-chapter-".concat(t.instance.subContentId),section:"top"};e.parent.trigger("newChapter",r)};var a=document.createElement("span");if(a.innerHTML=t.title,n.appendChild(a),this.behaviour.progressIndicators){var o=document.createElement("span"),i=this.parent.getChapterStatus(t);o.classList.add("icon-chapter-".concat(i.toLowerCase())),n.appendChild(o)}r.appendChild(n);var s=this.createSectionList(t.sections.filter((function(t){return t.isTask})),t.instance.subContentId),c=s.sectionElements;!1===s.hasUnansweredInteractions&&r.classList.add("h5p-interactive-book-summary-no-interactions");var u=document.createElement("div");u.classList.add("h5p-interactive-book-summary-chapter-subheader"),t.maxTasks?u.innerHTML=this.l10n.leftOutOfTotalCompleted.replace("@left",Math.max(t.maxTasks-t.tasksLeft,0)).replace("@max",t.maxTasks):u.innerHTML=this.l10n.noInteractions,r.appendChild(u);var l=document.createElement("ul");return c.length&&c.map((function(t){return l.appendChild(t)})),r.appendChild(l),r}},{key:"createFilterDropdown",value:function(){var t=this,e=function(e,o){var i=document.createElement("li");i.role="menuitem";var s=document.createElement("button");return s.textContent=e,s.type="button",s.onclick=function(e){t.setFilter(o),r.removeAttribute("active"),n.setAttribute("aria-expanded","false"),a.textContent=e.currentTarget.innerHTML},i.appendChild(s),i},r=document.createElement("div");r.classList.add("h5p-interactive-book-summary-dropdown");var n=document.createElement("button");n.setAttribute("aria-haspopup","true"),n.setAttribute("aria-expanded","false"),n.type="button",n.onclick=function(){r.hasAttribute("active")?(r.removeAttribute("active"),n.setAttribute("aria-expanded","false")):(r.setAttribute("active",""),n.setAttribute("aria-expanded","true"),n.focus())};var a=document.createElement("span");a.textContent=this.l10n.allInteractions,n.appendChild(a);var o=document.createElement("span");o.classList.add("h5p-interactive-book-summary-dropdown-icon"),o.classList.add("icon-expanded"),o.setAttribute("aria-hidden","true"),n.appendChild(o);var i=document.createElement("ul");i.role="menu",i.classList.add("h5p-interactive-book-summary-dropdown-menu");var s=e(this.l10n.allInteractions,this.filterActionAll),c=e(this.l10n.unansweredInteractions,this.filterActionUnanswered);return i.appendChild(s),i.appendChild(c),r.appendChild(n),r.appendChild(i),r}},{key:"addSummaryOverview",value:function(){var t=document.createElement("ul");t.classList.add("h5p-interactive-book-summary-list");var e=document.createElement("li");e.classList.add("h5p-interactive-book-summary-overview-header");var r=document.createElement("h3");r.innerHTML=this.l10n.summaryHeader,e.appendChild(r),e.appendChild(this.createFilterDropdown()),t.appendChild(e);var n=document.createElement("ol");n.classList.add("h5p-interactive-book-summary-overview-list");var a,o=A(this.chapters);try{for(o.s();!(a=o.n()).done;){var i=a.value;n.appendChild(this.createChapterOverview(i))}}catch(t){o.e(t)}finally{o.f()}var s=document.createElement("p");s.classList.add("h5p-interactive-book-summary-overview-list-empty"),s.classList.add("h5p-interactive-book-summary-top-section"),s.classList.add("h5p-interactive-book-summary-bottom-section"),s.innerHTML=this.l10n.noInteractions,n.appendChild(s),t.appendChild(n),this.wrapper.appendChild(t)}},{key:"addScoreBar",value:function(){var t=document.createElement("div");t.classList.add("h5p-interactive-book-summary-score-bar");var e=H5P.JoubelUI.createScoreBar(this.parent.getMaxScore());e.setScore(this.parent.getScore()),e.appendTo(t),this.wrapper.appendChild(t)}},{key:"noChapterInteractions",value:function(){var t=document.createElement("div");t.classList.add("h5p-interactive-book-summary-no-chapter-interactions");var e=document.createElement("p");e.innerHTML=this.l10n.noChapterInteractionBoldText;var r=document.createElement("p");r.classList.add("h5p-interactive-book-summary-no-initialized-chapters"),r.innerHTML=this.l10n.noChapterInteractionText,t.appendChild(e),t.appendChild(r),this.wrapper.appendChild(t)}},{key:"addSummaryPage",value:function(t){if(this.wrapper=document.createElement("div"),this.wrapper.classList.add("h5p-interactive-book-summary-page"),this.chapters.filter((function(t){return t.isInitialized})).length>0||this.chapters.some((function(t){return t.sections.some((function(t){return t.taskDone}))}))){if(this.parent.pageContent&&this.parent.chapters[this.parent.getChapterId(this.parent.pageContent.targetPage.chapter)].isSummary||0===this.parent.chapters.length){if(this.parent.chapters.length>0)for(var e in this.chapters)this.parent.pageContent.initializeChapter(e);this.addProgressIndicators(),this.addActionButtons(),this.addSummaryOverview(),this.addScoreBar()}}else this.noChapterInteractions();return Array.from(document.querySelectorAll(".h5p-interactive-book-summary-footer")).forEach((function(t){return t.remove()})),t.append(this.wrapper),t}},{key:"checkTheAnswerIsUpdated",value:function(){var t,e=this.getChapterStats(),r=JSON.parse(this.tempState),n=A(e.keys());try{for(n.s();!(t=n.n()).done;){var a,o=t.value,i=r[o].state.instances,s=e[o].state.instances,c=e[o].sections,u=A(i.keys());try{for(u.s();!(a=u.n()).done;){var l=a.value;null!==i[l]&&void 0!==i[l]&&(Array.isArray(i[l])&&!this.compareStates(i[l],s[l])&&c[l].taskDone&&(this.parent.isAnswerUpdated=!0),"object"===P(i[l])&&!Array.isArray(i[l])&&JSON.stringify(i[l])!==JSON.stringify(s[l])&&c[l].taskDone&&(this.parent.isAnswerUpdated=!0))}}catch(t){u.e(t)}finally{u.f()}if(this.parent.isAnswerUpdated)break}}catch(t){n.e(t)}finally{n.f()}}},{key:"getChapterStats",value:function(){return this.chapters.filter((function(t){return!t.isSummary})).map((function(t){return{sections:t.sections.map((function(t){return{taskDone:t.taskDone}})),state:t.instance.getCurrentState()}}))}},{key:"compareStates",value:function(t,e){return Array.isArray(t)&&Array.isArray(e)&&t.length===e.length&&t.every((function(t,r){return t===e[r]||""===e[r]}))}}],e&&j(t.prototype,e),r&&j(t,r),Object.defineProperty(t,"prototype",{writable:!1}),a}();function R(t){return R="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},R(t)}function N(t,e){var r=Object.keys(t);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(t);e&&(n=n.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),r.push.apply(r,n)}return r}function D(t){for(var e=1;e<arguments.length;e++){var r=null!=arguments[e]?arguments[e]:{};e%2?N(Object(r),!0).forEach((function(e){H(t,e,r[e])})):Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(r)):N(Object(r)).forEach((function(e){Object.defineProperty(t,e,Object.getOwnPropertyDescriptor(r,e))}))}return t}function H(t,e,r){return e in t?Object.defineProperty(t,e,{value:r,enumerable:!0,configurable:!0,writable:!0}):t[e]=r,t}function _(){return _=Object.assign?Object.assign.bind():function(t){for(var e=1;e<arguments.length;e++){var r=arguments[e];for(var n in r)Object.prototype.hasOwnProperty.call(r,n)&&(t[n]=r[n])}return t},_.apply(this,arguments)}function z(t,e){for(var r=0;r<e.length;r++){var n=e[r];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(t,n.key,n)}}function U(t,e){return U=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(t,e){return t.__proto__=e,t},U(t,e)}function q(t){var e=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(t){return!1}}();return function(){var r,n=W(t);if(e){var a=W(this).constructor;r=Reflect.construct(n,arguments,a)}else r=n.apply(this,arguments);return function(t,e){if(e&&("object"===R(e)||"function"==typeof e))return e;if(void 0!==e)throw new TypeError("Derived constructors may only return object or undefined");return function(t){if(void 0===t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return t}(t)}(this,r)}}function W(t){return W=Object.setPrototypeOf?Object.getPrototypeOf.bind():function(t){return t.__proto__||Object.getPrototypeOf(t)},W(t)}const V=function(){!function(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function");t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,writable:!0,configurable:!0}}),Object.defineProperty(t,"prototype",{writable:!1}),e&&U(t,e)}(o,H5P.EventDispatcher);var t,r,n,a=q(o);function o(t,e,r,n,i){var s;if(function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,o),(s=a.call(this)).parent=n,s.behaviour=t.behaviour,s.params=i,s.targetPage={},s.targetPage.redirectFromComponent=!1,s.columnNodes=[],s.chapters=[],s.l10n=t.l10n,s.sidebarIsOpen=!1,s.previousState=r.previousState&&Object.keys(r.previousState).length>0?r.previousState:null,n.hasValidChapters()){var c=s.createColumns(t,e,r);s.preloadChapter(c)}return s.content=s.createPageContent(),s.container=document.createElement("div"),s.container.classList.add("h5p-interactive-book-main","h5p-interactive-book-navigation-hidden"),s.container.appendChild(s.content),s}return t=o,r=[{key:"getChapters",value:function(){var t=!(arguments.length>0&&void 0!==arguments[0])||arguments[0];return this.chapters.filter((function(e){return!e.isSummary||e.isSummary&&!!t}))}},{key:"resetChapters",value:function(){this.behaviour.progressIndicators&&!this.behaviour.progressAuto&&this.columnNodes.forEach((function(t){Array.from(t.querySelectorAll(".h5p-interactive-book-status-progress-marker > input[type=checkbox]")).forEach((function(t){return t.checked=!1}))}))}},{key:"createPageContent",value:function(){var t=document.createElement("div");return t.classList.add("h5p-interactive-book-content"),this.columnNodes.forEach((function(e){t.appendChild(e)})),this.setChapterOrder(this.parent.getActiveChapter()),t}},{key:"setChapterOrder",value:function(t){t<0||t>this.columnNodes.length-1||this.columnNodes.forEach((function(e,r){e.classList.remove("h5p-interactive-book-previous"),e.classList.remove("h5p-interactive-book-current"),e.classList.remove("h5p-interactive-book-next"),r===t-1||r===t&&e.classList.add("h5p-interactive-book-current")}))}},{key:"createChapterReadCheckbox",value:function(t){var e=this,r=document.createElement("input");r.setAttribute("type","checkbox"),r.checked=t,r.onclick=function(t){e.parent.setChapterRead(void 0,t.target.checked)};var n=document.createElement("p");n.innerHTML=this.params.l10n.markAsFinished;var a=document.createElement("label");return a.classList.add("h5p-interactive-book-status-progress-marker"),a.appendChild(r),a.appendChild(n),a}},{key:"injectSectionId",value:function(t,e){for(var r=e.getElementsByClassName("h5p-column-content"),n=0;n<t.length;n++)r[n].id="h5p-interactive-book-section-".concat(t[n].instance.subContentId)}},{key:"preloadChapter",value:function(t){this.initializeChapter(t),this.initializeChapter(t+1)}},{key:"initializeChapter",value:function(t){if(!(t<0||t>this.chapters.length-1)){var e=this.chapters[t];if(e.isSummary){var r=this.columnNodes[t];return e.isInitialized&&(e.instance.setChapters(this.getChapters(!1)),r.innerHTML=""),e.instance.addSummaryPage(H5P.jQuery(r)),void(e.isInitialized=!0)}if(!e.isInitialized){var n,a,o=this.columnNodes[t];e.instance.attach(H5P.jQuery(o)),this.injectSectionId(e.sections,o),this.behaviour.progressIndicators&&!this.behaviour.progressAuto&&o.appendChild(this.createChapterReadCheckbox(!(null===(n=this.previousState)||void 0===n||null===(a=n.chapters)||void 0===a||!a[t].completed))),e.isInitialized=!0}}}},{key:"createColumns",value:function(t,r,n){var a=this,o=(n=_({},n)).previousState&&Object.keys(n.previousState).length>0?n.previousState:null,i=e.extractFragmentsFromURL(this.parent.validateFragments,this.parent.hashWindow);0===Object.keys(i).length&&n&&o&&o.urlFragments&&(i=o.urlFragments);var s=[];this.chapters=s;for(var c=function(e){var i=document.createElement("div"),c=D(D({},n),{},{metadata:D({},n.metadata),previousState:o?o.chapters[e].state:{}}),u=H5P.newRunnable(t.chapters[e],r,void 0,void 0,c);a.parent.bubbleUp(u,"resize",a.parent);var l={isInitialized:!1,instance:u,title:t.chapters[e].metadata.title,completed:!!o&&o.chapters[e].completed,tasksLeft:o?o.chapters[e].tasksLeft:0,isSummary:!1,sections:u.getInstances().map((function(r,n){return{content:t.chapters[e].params.content[n].content,instance:r,isTask:!1}}))};i.classList.add("h5p-interactive-book-chapter"),i.id="h5p-interactive-book-chapter-".concat(u.subContentId),l.maxTasks=0,l.tasksLeft=0,l.sections.forEach((function(t,r){H5P.Column.isTask(t.instance)&&(t.isTask=!0,l.maxTasks++,l.tasksLeft++,a.behaviour.progressIndicators&&(t.taskDone=!!o&&o.chapters[e].sections[r].taskDone,t.taskDone&&l.tasksLeft--))})),s.push(l),a.columnNodes.push(i)},u=0;u<t.chapters.length;u++)c(u);if(this.parent.hasSummary(s)){var l=document.createElement("div"),p=new B(D({},t),this.parent,this.getChapters(!1));this.parent.bubbleUp(p,"resize",this.parent);var h={isInitialized:!1,instance:p,title:this.l10n.summaryHeader,isSummary:!0,sections:[]};l.classList.add("h5p-interactive-book-chapter"),l.id="h5p-interactive-book-chapter-summary",h.maxTasks=h.tasksLeft,s.push(h),this.columnNodes.push(l)}if(i.chapter&&i.h5pbookid==this.parent.contentId){var d=this.findChapterIndex(i.chapter);if(-1===d)return 0;if(this.parent.setActiveChapter(d),i.section){var f=i.headerNumber;window.requestAnimationFrame((function(){a.redirectSection(i.section,f),a.parent.hasCover()&&a.parent.cover.removeCover()}))}return d}return 0}},{key:"redirectSection",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;if("top"===t)this.parent.trigger("scrollToTop");else{var r=document.getElementById(t);if(r){if(null!==e){var n=r.querySelectorAll("h2, h3");n[e]&&(r=n[e])}var a=document.createElement("div");a.setAttribute("tabindex","-1"),r.parentNode.insertBefore(a,r),a.focus(),a.addEventListener("blur",(function(){a.parentNode.removeChild(a)})),this.targetPage.redirectFromComponent=!1,setTimeout((function(){r.scrollIntoView(!0)}),100)}}}},{key:"findChapterIndex",value:function(t){var e=-1;return this.columnNodes.forEach((function(r,n){-1===e&&r.id===t&&(e=n)})),e}},{key:"changeChapter",value:function(t,e){var r=this;if(!this.columnNodes[this.parent.getActiveChapter()].classList.contains("h5p-interactive-book-animate")){this.targetPage=e;var n=this.parent.getActiveChapter(),a=this.parent.getChapterId(this.targetPage.chapter),o=n!==a;if(t||this.parent.updateChapterProgress(n,o),this.preloadChapter(a),a<this.columnNodes.length){var i=this.columnNodes[n],s=this.columnNodes[a];if(o&&!t){this.parent.setActiveChapter(a);var c=n<a?"next":"previous";s.classList.add("h5p-interactive-book-".concat(c)),s.classList.add("h5p-interactive-book-animate"),i.classList.add("h5p-interactive-book-animate"),setTimeout((function(){"previous"===c?i.classList.add("h5p-interactive-book-next"):(i.classList.remove("h5p-interactive-book-current"),i.classList.add("h5p-interactive-book-previous")),s.classList.remove("h5p-interactive-book-".concat(c))}),1),setTimeout((function(){i.classList.remove("h5p-interactive-book-next"),i.classList.remove("h5p-interactive-book-previous"),i.classList.remove("h5p-interactive-book-current"),s.classList.add("h5p-interactive-book-current"),s.classList.remove("h5p-interactive-book-animate"),i.classList.remove("h5p-interactive-book-animate"),r.redirectSection(r.targetPage.section,r.targetPage.headerNumber),r.parent.trigger("resize")}),250)}else this.parent.cover&&!this.parent.cover.hidden?this.parent.on("coverRemoved",(function(){r.redirectSection(r.targetPage.section,r.targetPage.headerNumber)})):this.redirectSection(this.targetPage.section,this.targetPage.headerNumber);this.parent.sideBar.redirectHandler(a)}}}},{key:"updateFooter",value:function(){if(0!==this.chapters.length){var t=this.parent.getActiveChapter(),e=this.columnNodes[t],r=this.parent.shouldFooterBeHidden(e.clientHeight),n=this.parent.statusBarFooter.wrapper.parentNode;r?n!==this.content&&this.content.appendChild(this.parent.statusBarFooter.wrapper):n!==this.parent.$wrapper&&this.parent.$wrapper.append(this.parent.statusBarFooter.wrapper)}}},{key:"toggleNavigationMenu",value:function(){var t=this;this.sidebarIsOpen?(H5P.Transition.onTransitionEnd(H5P.jQuery(this.container),(function(){t.container.classList.add("h5p-interactive-book-navigation-hidden")}),500),this.container.classList.remove("h5p-interactive-book-navigation-open")):(this.container.classList.remove("h5p-interactive-book-navigation-hidden"),setTimeout((function(){t.container.classList.add("h5p-interactive-book-navigation-open")}),1)),this.sidebarIsOpen=!this.sidebarIsOpen}}],r&&z(t.prototype,r),n&&z(t,n),Object.defineProperty(t,"prototype",{writable:!1}),o}();r(4373);function $(t){return $="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},$(t)}var G=["read","displayTOC","hideTOC","nextPage","previousPage","chapterCompleted","partCompleted","incompleteChapter","navigateToTop","markAsFinished","fullscreen","exitFullscreen","bookProgressSubtext","interactionsProgressSubtext","submitReport","restartLabel","summaryHeader","allInteractions","unansweredInteractions","scoreText","leftOutOfTotalCompleted","noInteractions","score","summaryAndSubmit","noChapterInteractionBoldText","noChapterInteractionText","yourAnswersAreSubmittedForReview","bookProgress","interactionsProgress","totalScoreLabel"];function X(t,e){if(null==t)return{};var r,n,a=function(t,e){if(null==t)return{};var r,n,a={},o=Object.keys(t);for(n=0;n<o.length;n++)r=o[n],e.indexOf(r)>=0||(a[r]=t[r]);return a}(t,e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(t);for(n=0;n<o.length;n++)r=o[n],e.indexOf(r)>=0||Object.prototype.propertyIsEnumerable.call(t,r)&&(a[r]=t[r])}return a}function J(t,e){var r=Object.keys(t);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(t);e&&(n=n.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),r.push.apply(r,n)}return r}function Y(t){for(var e=1;e<arguments.length;e++){var r=null!=arguments[e]?arguments[e]:{};e%2?J(Object(r),!0).forEach((function(e){K(t,e,r[e])})):Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(r)):J(Object(r)).forEach((function(e){Object.defineProperty(t,e,Object.getOwnPropertyDescriptor(r,e))}))}return t}function K(t,e,r){return e in t?Object.defineProperty(t,e,{value:r,enumerable:!0,configurable:!0,writable:!0}):t[e]=r,t}function Q(){return Q=Object.assign?Object.assign.bind():function(t){for(var e=1;e<arguments.length;e++){var r=arguments[e];for(var n in r)Object.prototype.hasOwnProperty.call(r,n)&&(t[n]=r[n])}return t},Q.apply(this,arguments)}function Z(t,e){for(var r=0;r<e.length;r++){var n=e[r];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(t,n.key,n)}}function tt(t,e){return tt=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(t,e){return t.__proto__=e,t},tt(t,e)}function et(t){var e=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(t){return!1}}();return function(){var r,n=nt(t);if(e){var a=nt(this).constructor;r=Reflect.construct(n,arguments,a)}else r=n.apply(this,arguments);return function(t,e){if(e&&("object"===$(e)||"function"==typeof e))return e;if(void 0!==e)throw new TypeError("Derived constructors may only return object or undefined");return rt(t)}(this,r)}}function rt(t){if(void 0===t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return t}function nt(t){return nt=Object.setPrototypeOf?Object.getPrototypeOf.bind():function(t){return t.__proto__||Object.getPrototypeOf(t)},nt(t)}var at=function(){!function(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function");t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,writable:!0,configurable:!0}}),Object.defineProperty(t,"prototype",{writable:!1}),e&&tt(t,e)}(o,H5P.EventDispatcher);var t,r,n,a=et(o);function o(t,r){var n,i,s,c,u=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};!function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,o);var p=rt(c=a.call(this));if(c.contentId=r,c.previousState=u.previousState,t&&t.behaviour&&t.behaviour.baseColor&&!L.isBaseColor(t.behaviour.baseColor)){L.setBase(t.behaviour.baseColor);var h=document.createElement("style");h.styleSheet?h.styleSheet.cssText=L.getCSS():h.appendChild(document.createTextNode(L.getCSS())),document.head.appendChild(h)}c.activeChapter=0,c.newHandler={},c.completed=!1,c.params=o.sanitizeConfig(t),c.l10n=c.params.l10n,c.params.behaviour=c.params.behaviour||{},c.mainWrapper=null,c.currentRatio=null,c.smallSurface="h5p-interactive-book-small",c.mediumSurface="h5p-interactive-book-medium",c.largeSurface="h5p-interactive-book-large",c.chapters=[],c.isSubmitButtonEnabled=!1,c.isAnswerUpdated=!0,void 0!==u.isScoringEnabled||void 0!==u.isReportingEnabled?c.isSubmitButtonEnabled=u.isScoringEnabled||u.isReportingEnabled:void 0!==H5PIntegration.reportingIsEnabled&&(c.isSubmitButtonEnabled=H5PIntegration.reportingIsEnabled),c.params.behaviour.enableSolutionsButton=!1,c.params.behaviour.enableRetry=null===(n=c.params.behaviour.enableRetry)||void 0===n||n,c.getAnswerGiven=function(){return c.chapters.reduce((function(t,e){return"function"==typeof e.instance.getAnswerGiven?t&&e.instance.getAnswerGiven():t}),!0)},c.getScore=function(){return c.chapters.length>0?c.chapters.reduce((function(t,e){return"function"==typeof e.instance.getScore?t+e.instance.getScore():t}),0):c.previousState&&c.previousState.score||0},c.getMaxScore=function(){return c.chapters.length>0?c.chapters.reduce((function(t,e){return"function"==typeof e.instance.getMaxScore?t+e.instance.getMaxScore():t}),0):c.previousState&&c.previousState.maxScore||0},c.showSolutions=function(){c.chapters.forEach((function(t){"function"==typeof t.instance.toggleReadSpeaker&&t.instance.toggleReadSpeaker(!0),"function"==typeof t.instance.showSolutions&&t.instance.showSolutions(),"function"==typeof t.instance.toggleReadSpeaker&&t.instance.toggleReadSpeaker(!1)}))},c.resetTask=function(){if(c.hasValidChapters()){for(var t in c.chapters.forEach((function(t,e){"function"==typeof t.instance.resetTask&&t.instance.resetTask(),t.completed=!1,t.tasksLeft=t.maxTasks,t.sections.forEach((function(t){return t.taskDone=!1})),c.setChapterRead(e,!1)})),c.previousState)delete c.previousState[t];c.hashWindow.location.hash="";var e=c.getActiveChapter();c.redirectChapter({h5pbookid:c.contentId,chapter:c.pageContent.columnNodes[0].id,section:"top"}),c.chapters[e].completed=!1,c.hasCover()&&c.displayCover(c.mainWrapper),c.isAnswerUpdated=!1,c.setActivityStarted(!0),c.pageContent.resetChapters(),c.sideBar.resetIndicators()}},c.getXAPIData=function(){var t=c.createXAPIEventTemplate("answered");return c.addQuestionToXAPI(t),t.setScoredResult(c.getScore(),c.getMaxScore(),rt(c),!0,c.getScore()===c.getMaxScore()),{statement:t.data.statement,children:c.getXAPIDataFromChildren(c.chapters.map((function(t){return t.instance})))}},c.getXAPIDataFromChildren=function(t){return t.map((function(t){if("function"==typeof t.getXAPIData)return t.getXAPIData()})).filter((function(t){return!!t}))},c.addQuestionToXAPI=function(t){Q(t.getVerifiedStatementValue(["object","definition"]),c.getxAPIDefinition())},c.getxAPIDefinition=function(){return{interactionType:"compound",type:"http://adlnet.gov/expapi/activities/cmi.interaction",description:{"en-US":""}}},c.getCurrentState=function(){var t,r=c.chapters.filter((function(t){return!t.isSummary})).map((function(t){return{completed:t.completed||null,sections:t.sections.map((function(t){return{taskDone:t.taskDone||null}})),state:t.instance.getCurrentState()||null}})),n={urlFragments:!c.hashWindow.location.hash&&null!==(t=c.previousState)&&void 0!==t&&t.urlFragments?c.previousState.urlFragments:e.extractFragmentsFromURL(c.validateFragments,c.hashWindow),chapters:r};return c.activeChapter>0&&(n.score=c.getScore(),n.maxScore=c.getMaxScore()),n},c.getContext=function(){return c.cover&&!c.cover.hidden?{}:{type:"page",value:c.activeChapter+1}},c.hasCover=function(){return c.cover&&c.cover.container},c.hasSummary=function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:c.chapters;return c.hasChaptersTasks(t)&&c.params.behaviour.displaySummary&&!0===c.params.behaviour.displaySummary},c.hasChaptersTasks=function(t){return t.filter((function(t){return t.sections.filter((function(t){return!0===t.isTask})).length>0})).length>0},c.hasValidChapters=function(){return c.params.chapters.length>0},c.getActiveChapter=function(){return arguments.length>0&&void 0!==arguments[0]&&arguments[0]?c.chapters[c.activeChapter]:c.activeChapter},c.setActiveChapter=function(t){t=parseInt(t),isNaN(t)||(c.activeChapter=t)},c.validateFragments=function(t){return void 0!==t.chapter&&String(t.h5pbookid)===String(p.contentId)},c.bubbleUp=function(t,e,r){t.on(e,(function(t){r.bubblingUpwards=!0,r.trigger(e,t),r.bubblingUpwards=!1}))},c.isMenuOpen=function(){return c.statusBarHeader.isMenuOpen()},c.isSmallSurface=function(){return c.mainWrapper&&c.mainWrapper.hasClass(c.smallSurface)},c.getRatio=function(){return c.mainWrapper.width()/parseFloat(c.mainWrapper.css("font-size"))},c.setWrapperClassFromRatio=function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:c.getRatio();e!==c.currentRatio&&(c.breakpoints().forEach((function(t){t.shouldAdd(e)?c.mainWrapper.addClass(t.className):c.mainWrapper.removeClass(t.className)})),c.currentRatio=e)},c.resize=function(){if(c.pageContent&&c.hasValidChapters()&&c.mainWrapper){c.setWrapperClassFromRatio(c.mainWrapper);var t=c.getActiveChapter(),e=c.pageContent.columnNodes[t];null!==e.offsetParent&&(c.bubblingUpwards||c.pageContent.chapters[t].instance.trigger("resize"),c.pageContent.content.style.height==="".concat(e.offsetHeight,"px")||e.classList.contains("h5p-interactive-book-animate")||(c.pageContent.content.style.height="".concat(e.offsetHeight,"px"),c.pageContent.updateFooter(),setTimeout((function(){c.trigger("resize")}),10),window.clearTimeout(c.extraResizeTimeout),c.extraResizeTimeout=window.setTimeout((function(){c.trigger("resize")}),200)))}},c.on("resize",c.resize,rt(c)),c.on("toggleMenu",(function(t){var e;c.sideBar.container.addEventListener("transitionend",(function(){c.trigger("resize")}));var r=!(null!==(e=t.data)&&void 0!==e&&e.shouldNotFocusNav);c.pageContent.toggleNavigationMenu(),c.statusBarHeader.menuToggleButton.setAttribute("aria-expanded",c.statusBarHeader.menuToggleButton.classList.toggle("h5p-interactive-book-status-menu-active")?"true":"false"),c.pageContent.sidebarIsOpen&&r&&c.sideBar.focus()})),c.on("scrollToTop",(function(){if(!0===H5P.isFullscreen){var t=c.pageContent.container;t.scrollBy(0,-t.scrollHeight)}else"lti"!==H5PIntegration.context&&c.statusBarHeader.wrapper.scrollIntoView(!0)})),c.on("newChapter",(function(t){if(!c.pageContent.columnNodes[c.getActiveChapter()].classList.contains("h5p-interactive-book-animate")){if(c.newHandler=t.data,t.data.newHash=e.createFragmentsString(c.newHandler),c.newHandler.redirectFromComponent=!0,c.getChapterId(t.data.chapter)===c.activeChapter)if(e.areFragmentsEqual(t.data,e.extractFragmentsFromURL(c.validateFragments,c.hashWindow),["h5pbookid","chapter","section","headerNumber"]))return void c.pageContent.changeChapter(!1,t.data);if(c.params.behaviour.progressAuto){var r=c.getChapterId(c.newHandler.chapter);c.isFinalChapterWithoutTask(r)&&c.setChapterRead(r)}H5P.trigger(rt(c),"changeHash",t.data),H5P.trigger(rt(c),"scrollToTop")}})),c.isCurrentChapterRead=function(){return c.isChapterRead(c.chapters[c.activeChapter],c.params.behaviour.progressAuto)},c.isChapterRead=function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:c.params.behaviour.progressAuto;return t.completed||e&&0===t.tasksLeft},c.isFinalChapterWithoutTask=function(t){return 0===c.chapters[t].maxTasks&&c.chapters.slice(0,t).concat(c.chapters.slice(t+1)).every((function(t){return 0===t.tasksLeft}))},c.setChapterRead=function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:c.activeChapter,e=!(arguments.length>1&&void 0!==arguments[1])||arguments[1];c.handleChapterCompletion(t,e),c.sideBar.updateChapterProgressIndicator(t,e?"DONE":c.hasChapterStartedTasks(c.chapters[t])?"STARTED":"BLANK")},c.hasChapterStartedTasks=function(t){return t.sections.filter((function(t){return t.taskDone})).length>0},c.getChapterStatus=function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:c.params.behaviour.progressAuto,r="BLANK";return c.isChapterRead(t,e)?r="DONE":c.hasChapterStartedTasks(t)&&(r="STARTED"),r},c.updateChapterProgress=function(t){var e=arguments.length>1&&void 0!==arguments[1]&&arguments[1];if(c.params.behaviour.progressIndicators){var r,n=c.chapters[t];"DONE"===(r=n.maxTasks?c.getChapterStatus(n):c.isChapterRead(n)&&e?"DONE":"BLANK")&&c.handleChapterCompletion(t),c.sideBar.updateChapterProgressIndicator(t,r)}},c.getChapterId=function(t){t=t.replace("h5p-interactive-book-chapter-","");var e=c.chapters.map((function(t){return t.instance.subContentId})).indexOf(t);return-1===e?0:e},c.handleChapterCompletion=function(t){var e=!(arguments.length>1&&void 0!==arguments[1])||arguments[1],r=c.chapters[t];if(!0!==r.isSummary){if(!e)return r.completed=!1,c.completed=!1,void c.trigger("bookCompleted",{completed:c.completed});r.completed||(r.completed=!0,r.instance.triggerXAPIScored(r.instance.getScore(),r.instance.getMaxScore(),"completed")),!c.completed&&c.chapters.filter((function(t){return!t.isSummary})).every((function(t){return t.completed}))&&(c.completed=!0,c.trigger("bookCompleted",{completed:c.completed}))}},c.shouldFooterBeHidden=function(){return c.isFullscreen},c.getContainerWidth=function(){return c.pageContent&&c.pageContent.container?c.pageContent.container.offsetWidth:0},c.changeChapter=function(t){c.pageContent.changeChapter(t,c.newHandler),c.statusBarHeader.updateStatusBar(),c.statusBarFooter.updateStatusBar(),c.newHandler.redirectFromComponent=!1},c.breakpoints=function(){return[{className:c.smallSurface,shouldAdd:function(t){return t<43}},{className:c.mediumSurface,shouldAdd:function(t){return t>=43&&t<60}},{className:c.largeSurface,shouldAdd:function(t){return t>=60}}]},H5P.on(rt(c),"respondChangeHash",(function(){var t=e.extractFragmentsFromURL(p.validateFragments,c.hashWindow);t.h5pbookid&&String(t.h5pbookid)===String(p.contentId)&&c.redirectChapter(t)})),H5P.on(rt(c),"changeHash",(function(t){String(t.data.h5pbookid)===String(c.contentId)&&(c.hashWindow.location.hash=t.data.newHash)})),H5P.externalDispatcher.on("xAPI",(function(t){var e=["answered","completed","interacted","attempted"].indexOf(t.getVerb())>-1,r=p.chapters.length;if(p!==this&&e&&r){var n,a=this.subContentId||(null===(n=this.contentData)||void 0===n?void 0:n.subContentId);if(!a)return;p.setSectionStatusByID(a,p.activeChapter)}})),c.redirectChapter=function(t){c.newHandler.redirectFromComponent||(t.h5pbookid&&String(t.h5pbookid)===String(p.contentId)?p.newHandler=t:p.newHandler={chapter:"h5p-interactive-book-chapter-".concat(p.chapters[0].instance.subContentId),h5pbookid:p.h5pbookid}),p.changeChapter(!1)},c.setSectionStatusByID=function(t,e){c.chapters[e].sections.forEach((function(r,n){var a=r.instance;a.subContentId!==t||r.taskDone||(r.taskDone=!a.getAnswerGiven||a.getAnswerGiven(),c.sideBar.setSectionMarker(e,n),r.taskDone&&(c.chapters[e].tasksLeft-=1),c.updateChapterProgress(e))}))},c.addHashListener=function(t){t.addEventListener("hashchange",(function(t){H5P.trigger(rt(c),"respondChangeHash",t)})),c.hashWindow=t};try{c.addHashListener(top)}catch(t){if(!(t instanceof DOMException))throw t;c.addHashListener(window)}c.displayCover=function(t){c.hideAllElements(!0),t.append(c.cover.container),t.addClass("covered"),c.cover.initMedia()},c.attach=function(t){c.mainWrapper=t,t.addClass("h5p-interactive-book h5p-scrollable-fullscreen"),c.isEdge18orEarlier()&&t.addClass("edge-18"),c.setWrapperClassFromRatio(c.mainWrapper),c.cover&&c.shouldShowCover&&c.displayCover(t),t.append(c.statusBarHeader.wrapper);var e=c.pageContent.container.firstChild;e&&c.pageContent.container.insertBefore(c.sideBar.container,e),t.append(c.pageContent.container),t.append(c.statusBarFooter.wrapper),c.$wrapper=t,c.params.behaviour.defaultTableOfContents&&!c.isSmallSurface()&&c.trigger("toggleMenu",{shouldNotFocusNav:!0}),c.pageContent.updateFooter()},c.isEdge18orEarlier=function(){var t=window.navigator.userAgent,e=t.indexOf("Edge/");if(e<0)return!1;var r=t.substring(e+5,t.indexOf(".",e));return parseInt(r)<=18},c.hideAllElements=function(t){var e=[this.statusBarHeader.wrapper,this.statusBarFooter.wrapper,this.pageContent.container];t?e.forEach((function(t){t.classList.add("h5p-content-hidden"),t.classList.add("h5p-interactive-book-cover-present")})):e.forEach((function(t){t.classList.remove("h5p-content-hidden"),t.classList.remove("h5p-interactive-book-cover-present")}))};var d=e.extractFragmentsFromURL(c.validateFragments,c.hashWindow);c.shouldShowCover=c.params.showCoverPage&&!(d.chapter||null!==(i=u.previousState)&&void 0!==i&&null!==(s=i.urlFragments)&&void 0!==s&&s.chapter),c.params.showCoverPage&&(c.cover=new S(c.params.bookCover,u.metadata.title,c.l10n.read,r,rt(c)));var f=Y(Y({},u),{},{parent:rt(c)});return c.pageContent=new V(c.params,r,f,rt(c),{l10n:{markAsFinished:c.l10n.markAsFinished},behaviour:c.params.behaviour}),c.chapters=c.pageContent.getChapters(),c.sideBar=new l(c.params,r,u.metadata.title,rt(c)),c.chapters.forEach((function(t,e){c.setChapterRead(e,t.completed)})),c.statusBarHeader=new b(r,c.chapters.length,rt(c),{l10n:c.l10n,a11y:c.params.a11y,behaviour:c.params.behaviour,displayFullScreenButton:!0,displayMenuToggleButton:!0},"h5p-interactive-book-status-header"),c.statusBarFooter=new b(r,c.chapters.length,rt(c),{l10n:c.l10n,a11y:c.params.a11y,behaviour:c.params.behaviour,displayToTopButton:!0},"h5p-interactive-book-status-footer"),c.hasCover()?(c.hideAllElements(!0),c.on("coverRemoved",(function(){var t;c.hideAllElements(!1),null!==(t=c.params.chapters[c.activeChapter])&&void 0!==t&&t.subContentId&&c.trigger("newChapter",{h5pbookid:c.contentId,chapter:"h5p-interactive-book-chapter-".concat(c.params.chapters[c.activeChapter].subContentId),section:0}),c.trigger("resize"),c.setActivityStarted(),"lti"!==H5PIntegration.context&&c.statusBarHeader.progressBar.progress.focus()}))):c.setActivityStarted(),c.hasValidChapters()&&(c.statusBarHeader.updateStatusBar(),c.statusBarFooter.updateStatusBar()),c}return t=o,n=[{key:"sanitizeConfig",value:function(t){var e=t.read,r=void 0===e?"Read":e,n=t.displayTOC,a=void 0===n?"Display &#039;Table of contents&#039;":n,o=t.hideTOC,i=void 0===o?"Hide &#039;Table of contents&#039;":o,s=t.nextPage,c=void 0===s?"Next page":s,u=t.previousPage,l=void 0===u?"Previous page":u,p=t.chapterCompleted,h=void 0===p?"Page completed!":p,d=t.partCompleted,f=void 0===d?"@pages of @total completed":d,v=t.incompleteChapter,m=void 0===v?"Incomplete page":v,b=t.navigateToTop,g=void 0===b?"Navigate to the top":b,y=t.markAsFinished,k=void 0===y?"I have finished this page":y,w=t.fullscreen,C=void 0===w?"Fullscreen":w,S=t.exitFullscreen,x=void 0===S?"Exit fullscreen":S,O=t.bookProgressSubtext,E=void 0===O?"@count of @total pages":O,L=t.interactionsProgressSubtext,P=void 0===L?"@count of @total interactions":L,A=t.submitReport,T=void 0===A?"Submit Report":A,j=t.restartLabel,I=void 0===j?"Restart":j,M=t.summaryHeader,F=void 0===M?"Summary":M,B=t.allInteractions,R=void 0===B?"All interactions":B,N=t.unansweredInteractions,D=void 0===N?"Unanswered interactions":N,H=t.scoreText,_=void 0===H?"@score / @maxscore":H,z=t.leftOutOfTotalCompleted,U=void 0===z?"@left of @max interactinos completed":z,q=t.noInteractions,W=void 0===q?"No interactions":q,V=t.score,$=void 0===V?"Score":V,J=t.summaryAndSubmit,Y=void 0===J?"Summary & submit":J,K=t.noChapterInteractionBoldText,Q=void 0===K?"You have not interacted with any pages.":K,Z=t.noChapterInteractionText,tt=void 0===Z?"You have to interact with at least one page before you can see the summary.":Z,et=t.yourAnswersAreSubmittedForReview,rt=void 0===et?"Your answers are submitted for review!":et,nt=t.bookProgress,at=void 0===nt?"Book progress":nt,ot=t.interactionsProgress,it=void 0===ot?"Interactions progress":ot,st=t.totalScoreLabel,ct=void 0===st?"Total score":st,ut=X(t,G);return ut.chapters=ut.chapters.map((function(t){return t.params.content=t.params.content.filter((function(t){return t.content})),t})).filter((function(t){return t.params.content&&t.params.content.length>0})),ut.behaviour.displaySummary=void 0===ut.behaviour.displaySummary||ut.behaviour.displaySummary,ut.l10n={read:r,displayTOC:a,hideTOC:i,nextPage:c,previousPage:l,chapterCompleted:h,partCompleted:f,incompleteChapter:m,navigateToTop:g,markAsFinished:k,fullscreen:C,exitFullscreen:x,bookProgressSubtext:E,interactionsProgressSubtext:P,submitReport:T,restartLabel:I,summaryHeader:F,allInteractions:R,unansweredInteractions:D,scoreText:_,leftOutOfTotalCompleted:U,noInteractions:W,score:$,summaryAndSubmit:Y,noChapterInteractionBoldText:Q,noChapterInteractionText:tt,yourAnswersAreSubmittedForReview:rt,bookProgress:at,interactionsProgress:it,totalScoreLabel:ct},ut}}],(r=null)&&Z(t.prototype,r),n&&Z(t,n),Object.defineProperty(t,"prototype",{writable:!1}),o}();H5P=H5P||{},H5P.InteractiveBook=at})()})();;// 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;

  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',
      submitAnswerButton: 'Submit',
      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 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 ? '&#xe603;' : '&#xe600;';
    }
    else {
      icon = selected ? '&#xe601;' : '&#xe602;';
    }
    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">' + 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,
            expandImage: media.params.expandImage,
            minimizeImage: media.params.minimizeImage
          });
        }
      }
      else if (type === 'H5P.Video') {
        if (media.params.sources) {
          // Register task video
          self.setVideo(media);
        }
      }
      else if (type === 'H5P.Audio') {
        if (media.params.files) {
          // Register task audio
          self.setAudio(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.labelId + '">' + params.question + '</div>');

    // Register task content area
    $myDom = $('<ul>', {
      'class': 'h5p-answers',
      role: params.role,
      'aria-labelledby': params.labelId
    });

    for (let i = 0; i < params.answers.length; i++) {
      const answer = params.answers[i];
      answer.text = answer.text ?? '<div></div>';
      $('<li>', {
        'class': 'h5p-answer',
        role: answer.role,
        tabindex: answer.tabindex,
        'aria-checked': answer.checked,
        'data-id': i,
        html: '<div class="h5p-alternative-container"><span class="h5p-alternative-inner">' + answer.text + '</span></div>',
        appendTo: $myDom
      });
    }

    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(/&nbsp;/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 = [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') {
          const pos = params.userAnswers.indexOf(num);
          if (pos !== -1) {
            params.userAnswers.splice(pos, 1);
          }

          // 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 {
          params.userAnswers.push(num);
          $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];
      const className = 'h5p-solution-icon-' + (params.behaviour.singleAnswer ? 'radio' : 'checkbox');

      if (a.correct) {
        $e.addClass('h5p-should').append($('<span/>', {
          'class': className,
          html: params.UI.shouldCheck + '.'
        }));
      }
      else {
        $e.addClass('h5p-should-not').append($('<span/>', {
          'class': className,
          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 alternative so that the user becomes
    // aware that the solution is being shown.
    $myDom.find('.h5p-answer:first-child').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-radio, ' +
        '.h5p-solution-icon-checkbox, ' +
        '.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
   * @param {boolean} moveFocus True to move the focus to first option
   * This prevents loss of focus if reset from within content
   */
  this.resetTask = function (moveFocus = false) {
    for (let i = 0; i < params.answers.length; i++) {
      if (params.answers[i].checked) {
        delete params.answers[i].checked;
      }
    }
    self.answered = false;
    self.hideSolutions();
    params.userAnswers = [];
    removeSelections(moveFocus);
    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);
  };

  /**
   * 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 && !self.getAnswerGiven(true)) {
        // 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 button
    if (params.behaviour.enableCheckButton && (!params.behaviour.autoCheck || !params.behaviour.singleAnswer)) {
      self.addButton('check-answer', params.UI.checkAnswerButton,
        function () {
          self.answered = true;
          checkAnswer();
          $myDom.find('.h5p-answer:first-child').focus();
        },
        true,
        {
          'aria-label': params.UI.a11yCheck,
        },
        {
          confirmationDialog: {
            enable: params.behaviour.confirmCheckDialog,
            l10n: params.confirmCheck,
            instance: self,
            $parentElement: $container
          },
          contentData: self.contentData,
          textIfSubmitting: params.UI.submitAnswerButton,
        }
      );
    }

    // Try Again button
    self.addButton('try-again', params.UI.tryAgainButton, function () {
      self.resetTask(true);

      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
      }
    });
  };

  /**
   * 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 !== '') {
          addFeedback($e, a.tipsAndFeedback.chosenFeedback);
        }
        else if (!chosen && a.tipsAndFeedback.notChosenFeedback !== undefined && a.tipsAndFeedback.notChosenFeedback !== '') {
          addFeedback($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'
    }).removeAttr('role')
      .removeAttr('aria-checked');

    $('.h5p-answers').removeAttr('role');
  };

  /**
   * Enables new input.
   */
  var enableInput = function () {
    $('.h5p-answer', $myDom)
      .attr({
        'aria-disabled': 'false',
        'role': params.behaviour.singleAnswer ? 'radio' : 'checkbox',
      });

    $('.h5p-answers').attr('role', params.role);
  };

  var calcScore = function () {
    score = 0;
    for (const answer of params.userAnswers) {
      const choice = params.answers[answer];
      const weight = (choice.weight !== undefined ? choice.weight : 1);
      if (choice.correct) {
        score += weight;
      }
      else {
        score -= weight;
      }
    }
    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 (moveFocus) {
    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
    if (moveFocus || self.isRoot()) {
      $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 = {
      'en-US': params.question,
    };
    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);
            }
          }
        }
      }
      calcScore();
    }
  }

  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.labelId = '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;
;		(function(xopen, fetch, dataurls) {

			switch(location.protocol.split(':')[0]) {
				case 'http':
				case 'https':
					return;
			};

			const url2data	= function(oldurl) {
					switch(oldurl.split(':')[0]) {
						case 'blob':
							console.log('blob', oldurl);
						case 'data':
							return oldurl;
					}
					let urltoks	 = oldurl.replace(H5PIntegration.url, '.').split('/');
					if(urltoks[0] == '.') {
						urltoks.shift();
						const url	= urltoks.join('/');
						if(typeof dataurls[url]!=='undefined') {
							if(dataurls[url][0]=='application/json') {
								return 'data:application/json;text,' + dataurls[url][1];
							}
							return 'data:' + dataurls[url].join(';base64,');
						}
					}
					console.error('url not found:', oldurl);
					return "data:;text,";
			};
			window.fetch = function() {
				arguments[0]	= url2data(arguments[0]);
				return fetch.apply(this, arguments);
			};
			XMLHttpRequest.prototype.open = function() {
				arguments[1]	= url2data(arguments[1]);
				return xopen.apply(this, arguments);
			};
		})(XMLHttpRequest.prototype.open, window.fetch, {"libraries\/H5P.Image-1.1\/library.json":["application\/json","{\"title\":\"Image\",\"contentType\":\"Media\",\"description\":\"Simple library that displays an image.\",\"majorVersion\":1,\"minorVersion\":1,\"patchVersion\":22,\"runnable\":0,\"machineName\":\"H5P.Image\",\"author\":\"Joubel\",\"coreApi\":{\"majorVersion\":1,\"minorVersion\":19},\"preloadedJs\":[{\"path\":\"image.js\"}],\"preloadedCss\":[{\"path\":\"image.css\"}],\"metadataSettings\":{\"disable\":0,\"disableExtraTitleField\":1},\"editorDependencies\":[{\"machineName\":\"H5PEditor.ShowWhen\",\"majorVersion\":1,\"minorVersion\":0}]}"],"libraries\/H5PEditor.ShowWhen-1.0\/library.json":["application\/json","{\"machineName\":\"H5PEditor.ShowWhen\",\"title\":\"Toggle visibility of a field based on rules\",\"license\":\"MIT\",\"author\":\"fnoks\",\"majorVersion\":1,\"minorVersion\":0,\"patchVersion\":9,\"runnable\":0,\"preloadedJs\":[{\"path\":\"h5p-show-when.js\"}],\"preloadedCss\":[{\"path\":\"h5p-show-when.css\"}]}"],"libraries\/H5P.Video-1.6\/library.json":["application\/json","{\"machineName\":\"H5P.Video\",\"title\":\"Video\",\"contentType\":\"Media\",\"description\":\"Plays video from multiple sources.\",\"license\":\"MIT\",\"author\":\"Joubel\",\"majorVersion\":1,\"minorVersion\":6,\"patchVersion\":64,\"runnable\":0,\"coreApi\":{\"majorVersion\":1,\"minorVersion\":19},\"preloadedJs\":[{\"path\":\"scripts\\\/vimeo.js\"},{\"path\":\"scripts\\\/youtube.js\"},{\"path\":\"scripts\\\/panopto.js\"},{\"path\":\"scripts\\\/echo360.js\"},{\"path\":\"scripts\\\/html5.js\"},{\"path\":\"scripts\\\/video.js\"}],\"preloadedCss\":[{\"path\":\"styles\\\/video.css\"}]}"],"libraries\/H5P.MultiChoice-1.16\/library.json":["application\/json","{\"title\":\"Multiple Choice\",\"contentType\":\"Question\",\"majorVersion\":1,\"minorVersion\":16,\"patchVersion\":14,\"runnable\":1,\"embedTypes\":[\"iframe\"],\"machineName\":\"H5P.MultiChoice\",\"author\":\"Joubel\",\"license\":\"MIT\",\"coreApi\":{\"majorVersion\":1,\"minorVersion\":27},\"preloadedJs\":[{\"path\":\"js\\\/multichoice.js\"}],\"preloadedCss\":[{\"path\":\"css\\\/multichoice.css\"}],\"preloadedDependencies\":[{\"machineName\":\"FontAwesome\",\"majorVersion\":4,\"minorVersion\":5},{\"machineName\":\"H5P.JoubelUI\",\"majorVersion\":1,\"minorVersion\":3},{\"machineName\":\"H5P.Question\",\"majorVersion\":1,\"minorVersion\":5}],\"editorDependencies\":[{\"machineName\":\"H5PEditor.RangeList\",\"majorVersion\":1,\"minorVersion\":0},{\"machineName\":\"H5PEditor.ShowWhen\",\"majorVersion\":1,\"minorVersion\":0}]}"],"libraries\/FontAwesome-4.5\/library.json":["application\/json","{\"title\":\"Font Awesome\",\"contentType\":\"Font\",\"majorVersion\":4,\"minorVersion\":5,\"patchVersion\":4,\"runnable\":0,\"machineName\":\"FontAwesome\",\"license\":\"MIT\",\"author\":\"Dave Gandy\",\"preloadedCss\":[{\"path\":\"h5p-font-awesome.min.css\"}]}"],"libraries\/H5P.JoubelUI-1.3\/library.json":["application\/json","{\"title\":\"Joubel UI\",\"contentType\":\"Utility\",\"description\":\"UI utility library\",\"majorVersion\":1,\"minorVersion\":3,\"patchVersion\":19,\"runnable\":0,\"coreApi\":{\"majorVersion\":1,\"minorVersion\":3},\"machineName\":\"H5P.JoubelUI\",\"author\":\"Joubel\",\"preloadedJs\":[{\"path\":\"js\\\/joubel-help-dialog.js\"},{\"path\":\"js\\\/joubel-message-dialog.js\"},{\"path\":\"js\\\/joubel-progress-circle.js\"},{\"path\":\"js\\\/joubel-simple-rounded-button.js\"},{\"path\":\"js\\\/joubel-speech-bubble.js\"},{\"path\":\"js\\\/joubel-throbber.js\"},{\"path\":\"js\\\/joubel-tip.js\"},{\"path\":\"js\\\/joubel-slider.js\"},{\"path\":\"js\\\/joubel-score-bar.js\"},{\"path\":\"js\\\/joubel-progressbar.js\"},{\"path\":\"js\\\/joubel-ui.js\"}],\"preloadedCss\":[{\"path\":\"css\\\/joubel-help-dialog.css\"},{\"path\":\"css\\\/joubel-message-dialog.css\"},{\"path\":\"css\\\/joubel-progress-circle.css\"},{\"path\":\"css\\\/joubel-simple-rounded-button.css\"},{\"path\":\"css\\\/joubel-speech-bubble.css\"},{\"path\":\"css\\\/joubel-tip.css\"},{\"path\":\"css\\\/joubel-slider.css\"},{\"path\":\"css\\\/joubel-score-bar.css\"},{\"path\":\"css\\\/joubel-progressbar.css\"},{\"path\":\"css\\\/joubel-ui.css\"},{\"path\":\"css\\\/joubel-icon.css\"}],\"preloadedDependencies\":[{\"machineName\":\"FontAwesome\",\"majorVersion\":4,\"minorVersion\":5},{\"machineName\":\"H5P.Transition\",\"majorVersion\":1,\"minorVersion\":0},{\"machineName\":\"H5P.FontIcons\",\"majorVersion\":1,\"minorVersion\":0}]}"],"libraries\/H5P.Transition-1.0\/library.json":["application\/json","{\"machineName\":\"H5P.Transition\",\"title\":\"Transition\",\"description\":\"Contains helper function relevant for transitioning\",\"license\":\"MIT\",\"author\":\"Joubel\",\"majorVersion\":1,\"minorVersion\":0,\"patchVersion\":4,\"runnable\":0,\"preloadedJs\":[{\"path\":\"transition.js\"}]}"],"libraries\/H5P.FontIcons-1.0\/library.json":["application\/json","{\"title\":\"H5P.FontIcons\",\"majorVersion\":1,\"minorVersion\":0,\"patchVersion\":11,\"runnable\":0,\"machineName\":\"H5P.FontIcons\",\"author\":\"Joubel\",\"preloadedCss\":[{\"path\":\"styles\\\/h5p-font-icons.css\"}]}"],"libraries\/H5P.Question-1.5\/library.json":["application\/json","{\"title\":\"Question\",\"machineName\":\"H5P.Question\",\"majorVersion\":1,\"minorVersion\":5,\"patchVersion\":15,\"runnable\":0,\"license\":\"MIT\",\"author\":\"Joubel\",\"coreApi\":{\"majorVersion\":1,\"minorVersion\":7},\"preloadedCss\":[{\"path\":\"styles\\\/question.css\"},{\"path\":\"styles\\\/explainer.css\"}],\"preloadedJs\":[{\"path\":\"scripts\\\/question.js\"},{\"path\":\"scripts\\\/explainer.js\"},{\"path\":\"scripts\\\/score-points.js\"}],\"preloadedDependencies\":[{\"machineName\":\"FontAwesome\",\"majorVersion\":4,\"minorVersion\":5},{\"machineName\":\"H5P.JoubelUI\",\"majorVersion\":1,\"minorVersion\":3}]}"],"libraries\/H5PEditor.RangeList-1.0\/library.json":["application\/json","{\"machineName\":\"H5PEditor.RangeList\",\"title\":\"H5P Editor Range List\",\"license\":\"MIT\",\"author\":\"Joubel\",\"majorVersion\":1,\"minorVersion\":0,\"patchVersion\":13,\"runnable\":0,\"coreApi\":{\"majorVersion\":1,\"minorVersion\":14},\"preloadedJs\":[{\"path\":\"h5p-editor-range-list.js\"}],\"preloadedCss\":[{\"path\":\"h5p-editor-range-list.css\"}],\"preloadedDependencies\":[{\"machineName\":\"H5PEditor.TableList\",\"majorVersion\":1,\"minorVersion\":0}]}"],"libraries\/H5PEditor.TableList-1.0\/library.json":["application\/json","{\"machineName\":\"H5PEditor.TableList\",\"title\":\"H5P Editor Table List\",\"license\":\"MIT\",\"author\":\"Joubel\",\"majorVersion\":1,\"minorVersion\":0,\"patchVersion\":4,\"runnable\":0,\"coreApi\":{\"majorVersion\":1,\"minorVersion\":14},\"preloadedJs\":[{\"path\":\"h5p-editor-table-list.js\"}],\"preloadedCss\":[{\"path\":\"h5p-editor-table-list.css\"}]}"],"libraries\/H5P.TrueFalse-1.8\/library.json":["application\/json","{\"title\":\"True\\\/False Question\",\"description\":\"Test your users with 'True or False' type questions\",\"machineName\":\"H5P.TrueFalse\",\"majorVersion\":1,\"minorVersion\":8,\"patchVersion\":8,\"runnable\":1,\"license\":\"MIT\",\"author\":\"Joubel\",\"embedTypes\":[\"iframe\"],\"coreApi\":{\"majorVersion\":1,\"minorVersion\":19},\"preloadedCss\":[{\"path\":\"styles\\\/h5p-true-false.css\"}],\"preloadedJs\":[{\"path\":\"scripts\\\/h5p-true-false.js\"},{\"path\":\"scripts\\\/h5p-true-false-answer-group.js\"},{\"path\":\"scripts\\\/h5p-true-false-answer.js\"}],\"preloadedDependencies\":[{\"machineName\":\"FontAwesome\",\"majorVersion\":4,\"minorVersion\":5},{\"machineName\":\"H5P.Question\",\"majorVersion\":1,\"minorVersion\":5},{\"machineName\":\"H5P.JoubelUI\",\"majorVersion\":1,\"minorVersion\":3},{\"machineName\":\"H5P.FontIcons\",\"majorVersion\":1,\"minorVersion\":0}],\"editorDependencies\":[{\"machineName\":\"H5PEditor.RadioGroup\",\"majorVersion\":1,\"minorVersion\":1},{\"machineName\":\"H5PEditor.ShowWhen\",\"majorVersion\":1,\"minorVersion\":0}]}"],"libraries\/H5PEditor.RadioGroup-1.1\/library.json":["application\/json","{\"title\":\"H5PEditor.RadioGroup\",\"majorVersion\":1,\"minorVersion\":1,\"patchVersion\":4,\"runnable\":0,\"machineName\":\"H5PEditor.RadioGroup\",\"coreApi\":{\"majorVersion\":1,\"minorVersion\":12},\"author\":\"Joubel\",\"preloadedJs\":[{\"path\":\"radio-group.js\"}],\"preloadedCss\":[{\"path\":\"radio-group.css\"}]}"],"libraries\/H5P.MarkTheWords-1.11\/library.json":["application\/json","{\"title\":\"Mark the Words\",\"description\":\"Test your users by making them select the correct words from a text.\",\"majorVersion\":1,\"minorVersion\":11,\"patchVersion\":8,\"coreApi\":{\"majorVersion\":1,\"minorVersion\":27},\"runnable\":1,\"embedTypes\":[\"iframe\"],\"author\":\"Joubel\",\"license\":\"MIT\",\"machineName\":\"H5P.MarkTheWords\",\"preloadedCss\":[{\"path\":\"styles\\\/mark-the-words.css\"}],\"preloadedJs\":[{\"path\":\"scripts\\\/keyboard-nav.js\"},{\"path\":\"scripts\\\/xAPI-generator.js\"},{\"path\":\"scripts\\\/word.js\"},{\"path\":\"scripts\\\/mark-the-words.js\"}],\"preloadedDependencies\":[{\"machineName\":\"FontAwesome\",\"majorVersion\":4,\"minorVersion\":5},{\"machineName\":\"H5P.JoubelUI\",\"majorVersion\":1,\"minorVersion\":3},{\"machineName\":\"H5P.Question\",\"majorVersion\":1,\"minorVersion\":5}],\"editorDependencies\":[{\"machineName\":\"H5PEditor.RangeList\",\"majorVersion\":1,\"minorVersion\":0},{\"machineName\":\"H5PEditor.ShowWhen\",\"majorVersion\":1,\"minorVersion\":0}]}"],"libraries\/H5P.Column-1.18\/library.json":["application\/json","{\"machineName\":\"H5P.Column\",\"title\":\"Column\",\"description\":\"Content holder displaying content in a column.\",\"contentType\":\"instructional\",\"license\":\"MIT\",\"author\":\"Joubel\",\"majorVersion\":1,\"minorVersion\":18,\"patchVersion\":1,\"runnable\":1,\"fullscreen\":0,\"embedTypes\":[\"iframe\"],\"coreApi\":{\"majorVersion\":1,\"minorVersion\":27},\"preloadedJs\":[{\"path\":\"scripts\\\/h5p-column.js\"}],\"preloadedCss\":[{\"path\":\"styles\\\/h5p-column.css\"}]}"],"libraries\/H5P.InteractiveBook-1.11\/library.json":["application\/json","{\"title\":\"Interactive Book\",\"description\":\"An interactive book which displays content in pages and sections\",\"majorVersion\":1,\"minorVersion\":11,\"patchVersion\":2,\"runnable\":1,\"fullscreen\":1,\"coreApi\":{\"majorVersion\":1,\"minorVersion\":27},\"author\":\"Joubel\",\"license\":\"MIT\",\"machineName\":\"H5P.InteractiveBook\",\"embedTypes\":[\"iframe\"],\"preloadedJs\":[{\"path\":\"dist\\\/h5p-interactive-book.js\"}],\"preloadedCss\":[{\"path\":\"dist\\\/h5p-interactive-book.css\"}],\"preloadedDependencies\":[{\"machineName\":\"FontAwesome\",\"majorVersion\":4,\"minorVersion\":5},{\"machineName\":\"H5P.JoubelUI\",\"majorVersion\":1,\"minorVersion\":3}],\"editorDependencies\":[{\"machineName\":\"H5PEditor.VerticalTabs\",\"majorVersion\":1,\"minorVersion\":3},{\"machineName\":\"H5PEditor.ShowWhen\",\"majorVersion\":1,\"minorVersion\":0},{\"machineName\":\"H5PEditor.ColorSelector\",\"majorVersion\":1,\"minorVersion\":3}]}"],"libraries\/H5PEditor.VerticalTabs-1.3\/library.json":["application\/json","{\"machineName\":\"H5PEditor.VerticalTabs\",\"title\":\"H5P Editor Vertical Tabs\",\"license\":\"MIT\",\"author\":\"Joubel\",\"majorVersion\":1,\"minorVersion\":3,\"patchVersion\":9,\"runnable\":0,\"coreApi\":{\"majorVersion\":1,\"minorVersion\":24},\"preloadedJs\":[{\"path\":\"vertical-tabs.js\"}],\"preloadedCss\":[{\"path\":\"styles\\\/css\\\/vertical-tabs.css\"}],\"preloadedDependencies\":[{\"machineName\":\"FontAwesome\",\"majorVersion\":4,\"minorVersion\":5}]}"],"libraries\/H5PEditor.ColorSelector-1.3\/library.json":["application\/json","{\"title\":\"H5PEditor.ColorSelector\",\"majorVersion\":1,\"minorVersion\":3,\"patchVersion\":1,\"runnable\":0,\"machineName\":\"H5PEditor.ColorSelector\",\"coreApi\":{\"majorVersion\":1,\"minorVersion\":24},\"author\":\"Joubel\",\"preloadedJs\":[{\"path\":\"scripts\\\/spectrum.js\"},{\"path\":\"scripts\\\/color-selector.js\"}],\"preloadedCss\":[{\"path\":\"styles\\\/spectrum.css\"},{\"path\":\"styles\\\/color-selector.css\"}]}"]});		H5PIntegration	= (function(x){
			let url	= window.location.href.split('/');
			url.pop();
			x.url	= url.join('/');
			return x;
		})({"baseUrl":"","url":"","siteUrl":"","l10n":{"H5P":{"fullscreen":"Vollbild","disableFullscreen":"Kein Vollbild","download":"Download","copyrights":"Nutzungsrechte","embed":"Einbetten","size":"Size","showAdvanced":"Show advanced","hideAdvanced":"Hide advanced","advancedHelp":"Include this script on your website if you want dynamic sizing of the embedded content:","copyrightInformation":"Nutzungsrechte","close":"Schlie\u00dfen","title":"Titel","author":"Autor","year":"Jahr","source":"Quelle","license":"Lizenz","thumbnail":"Thumbnail","noCopyrights":"Keine Copyright-Informationen vorhanden","reuse":"Wiederverwenden","reuseContent":"Verwende Inhalt","reuseDescription":"Verwende Inhalt.","downloadDescription":"Lade den Inhalt als H5P-Datei herunter.","copyrightsDescription":"Zeige Urheberinformationen an.","embedDescription":"Zeige den Code f\u00fcr die Einbettung an.","h5pDescription":"Visit H5P.org to check out more cool content.","contentChanged":"Dieser Inhalt hat sich seit Ihrer letzten Nutzung ver\u00e4ndert.","startingOver":"Sie beginnen von vorne.","by":"von","showMore":"Zeige mehr","showLess":"Zeige weniger","subLevel":"Sublevel","confirmDialogHeader":"Best\u00e4tige Aktion","confirmDialogBody":"Please confirm that you wish to proceed. This action is not reversible.","cancelLabel":"Abbrechen","confirmLabel":"Best\u00e4tigen","licenseU":"Undisclosed","licenseCCBY":"Attribution","licenseCCBYSA":"Attribution-ShareAlike","licenseCCBYND":"Attribution-NoDerivs","licenseCCBYNC":"Attribution-NonCommercial","licenseCCBYNCSA":"Attribution-NonCommercial-ShareAlike","licenseCCBYNCND":"Attribution-NonCommercial-NoDerivs","licenseCC40":"4.0 International","licenseCC30":"3.0 Unported","licenseCC25":"2.5 Generic","licenseCC20":"2.0 Generic","licenseCC10":"1.0 Generic","licenseGPL":"General Public License","licenseV3":"Version 3","licenseV2":"Version 2","licenseV1":"Version 1","licensePD":"Public Domain","licenseCC010":"CC0 1.0 Universal (CC0 1.0) Public Domain Dedication","licensePDM":"Public Domain Mark","licenseC":"Copyright","contentType":"Inhaltstyp","licenseExtras":"License Extras","changes":"Changelog","contentCopied":"Inhalt wurde ins Clipboard kopiert","connectionLost":"Connection lost. Results will be stored and sent when you regain connection.","connectionReestablished":"Connection reestablished.","resubmitScores":"Attempting to submit stored results.","offlineDialogHeader":"Your connection to the server was lost","offlineDialogBody":"We were unable to send information about your completion of this task. Please check your internet connection.","offlineDialogRetryMessage":"Versuche es wieder in :num....","offlineDialogRetryButtonLabel":"Jetzt nochmal probieren","offlineSuccessfulSubmit":"Erfolgreich Ergebnisse gesendet."}},"hubIsEnabled":false,"reportingIsEnabled":false,"libraryConfig":null,"crossorigin":null,"crossoriginCacheBuster":null,"pluginCacheBuster":"","libraryUrl":".\/libraries\/h5pcore\/js","contents":{"cid-1":{"displayOptions":{"copy":false,"copyright":false,"embed":false,"export":false,"frame":false,"icon":false},"embedCode":"","exportUrl":false,"fullScreen":false,"contentUserData":[],"metadata":{"title":"\u039f \u03b5\u03b8\u03b5\u03bb\u03bf\u03bd\u03c4\u03b9\u03c3\u03bc\u03cc\u03c2 \u03c3\u03c4\u03b7 \u03b6\u03c9\u03ae \u03bc\u03b1\u03c2 - \u0391\u0384\u03bc\u03ad\u03c1\u03bf\u03c2","license":null},"library":"H5P.InteractiveBook 1.11","jsonContent":"{\"showCoverPage\":true,\"bookCover\":{\"coverDescription\":\"<p style=\\\"text-align: center;\\\"><\\\/p>\",\"coverMedium\":{\"params\":{\"decorative\":false,\"contentName\":\"\\u0395\\u03b9\\u03ba\\u03cc\\u03bd\\u03b1\",\"expandImage\":\"Expand Image\",\"minimizeImage\":\"Minimize Image\",\"file\":{\"path\":\"images\\\/file-674b2ba0328db.png\",\"mime\":\"image\\\/png\",\"copyright\":{\"license\":\"U\"},\"width\":1280,\"height\":720}},\"library\":\"H5P.Image 1.1\",\"metadata\":{\"contentType\":\"Image\",\"license\":\"U\",\"title\":\"Untitled Image\"},\"subContentId\":\"ca84fa6b-cdbe-49b1-aec3-fe5366950c66\"}},\"chapters\":[{\"params\":{\"content\":[{\"content\":{\"params\":{\"visuals\":{\"fit\":true,\"controls\":true},\"playback\":{\"autoplay\":true,\"loop\":true},\"l10n\":{\"name\":\"\\u0392\\u03af\\u03bd\\u03c4\\u03b5\\u03bf\",\"loading\":\"\\u03a6\\u03cc\\u03c1\\u03c4\\u03c9\\u03c3\\u03b7 \\u03c0\\u03c1\\u03bf\\u03b3\\u03c1\\u03ac\\u03bc\\u03bc\\u03b1\\u03c4\\u03bf\\u03c2 \\u03b1\\u03bd\\u03b1\\u03c0\\u03b1\\u03c1\\u03b1\\u03b3\\u03c9\\u03b3\\u03ae\\u03c2 \\u03b2\\u03af\\u03bd\\u03c4\\u03b5\\u03bf...\",\"noPlayers\":\"\\u0394\\u03b5\\u03bd \\u03b2\\u03c1\\u03ad\\u03b8\\u03b7\\u03ba\\u03b1\\u03bd \\u03c0\\u03c1\\u03bf\\u03b3\\u03c1\\u03ac\\u03bc\\u03bc\\u03b1\\u03c4\\u03b1 \\u03b1\\u03bd\\u03b1\\u03c0\\u03b1\\u03c1\\u03b1\\u03b3\\u03c9\\u03b3\\u03ae\\u03c2 \\u03b2\\u03af\\u03bd\\u03c4\\u03b5\\u03bf \\u03c0\\u03bf\\u03c5 \\u03bd\\u03b1 \\u03c5\\u03c0\\u03bf\\u03c3\\u03c4\\u03b7\\u03c1\\u03af\\u03b6\\u03bf\\u03c5\\u03bd \\u03c4\\u03b7 \\u03c3\\u03c5\\u03b3\\u03ba\\u03b5\\u03ba\\u03c1\\u03b9\\u03bc\\u03ad\\u03bd\\u03b7 \\u03bc\\u03bf\\u03c1\\u03c6\\u03ae \\u03b2\\u03af\\u03bd\\u03c4\\u03b5\\u03bf.\",\"noSources\":\"\\u0394\\u03b5\\u03bd \\u03c5\\u03c0\\u03ac\\u03c1\\u03c7\\u03bf\\u03c5\\u03bd \\u03b1\\u03c1\\u03c7\\u03b5\\u03af\\u03b1 \\u03c0\\u03c1\\u03bf\\u03ad\\u03bb\\u03b5\\u03c5\\u03c3\\u03b7\\u03c2 \\u03c4\\u03bf\\u03c5 \\u03b2\\u03af\\u03bd\\u03c4\\u03b5\\u03bf.\",\"aborted\":\"\\u0394\\u03b9\\u03b1\\u03ba\\u03bf\\u03c0\\u03ae \\u03b1\\u03bd\\u03b1\\u03c0\\u03b1\\u03c1\\u03b1\\u03b3\\u03c9\\u03b3\\u03ae\\u03c2 \\u03b2\\u03af\\u03bd\\u03c4\\u03b5\\u03bf.\",\"networkFailure\":\"\\u0391\\u03c0\\u03bf\\u03c4\\u03c5\\u03c7\\u03af\\u03b1 \\u03b4\\u03b9\\u03ba\\u03c4\\u03cd\\u03bf\\u03c5.\",\"cannotDecode\":\"\\u0394\\u03b5\\u03bd \\u03b5\\u03af\\u03bd\\u03b1\\u03b9 \\u03b4\\u03c5\\u03bd\\u03b1\\u03c4\\u03ae \\u03b7 \\u03b1\\u03c0\\u03bf\\u03ba\\u03c9\\u03b4\\u03b9\\u03ba\\u03bf\\u03c0\\u03bf\\u03af\\u03b7\\u03c3\\u03b7 \\u03c4\\u03bf\\u03c5 \\u03bc\\u03ad\\u03c3\\u03bf\\u03c5.\",\"formatNotSupported\":\"\\u0397 \\u03bc\\u03bf\\u03c1\\u03c6\\u03ae \\u03c4\\u03bf\\u03c5 \\u03b2\\u03af\\u03bd\\u03c4\\u03b5\\u03bf \\u03b4\\u03b5\\u03bd \\u03c5\\u03c0\\u03bf\\u03c3\\u03c4\\u03b7\\u03c1\\u03af\\u03b6\\u03b5\\u03c4\\u03b1\\u03b9.\",\"mediaEncrypted\":\"\\u039a\\u03c1\\u03c5\\u03c0\\u03c4\\u03bf\\u03b3\\u03c1\\u03b1\\u03c6\\u03b7\\u03bc\\u03ad\\u03bd\\u03bf \\u03b2\\u03af\\u03bd\\u03c4\\u03b5\\u03bf.\",\"unknownError\":\"\\u0386\\u03b3\\u03bd\\u03c9\\u03c3\\u03c4\\u03bf \\u03c3\\u03c6\\u03ac\\u03bb\\u03bc\\u03b1.\",\"invalidYtId\":\"\\u03a4\\u03bf YouTube ID \\u03b4\\u03b5\\u03bd \\u03b5\\u03af\\u03bd\\u03b1\\u03b9 \\u03ad\\u03b3\\u03ba\\u03c5\\u03c1\\u03bf.\",\"unknownYtId\":\"\\u0394\\u03b5\\u03bd \\u03b5\\u03af\\u03bd\\u03b1\\u03b9 \\u03b4\\u03c5\\u03bd\\u03b1\\u03c4\\u03ae \\u03b7 \\u03b5\\u03cd\\u03c1\\u03b5\\u03c3\\u03b7 \\u03b2\\u03af\\u03bd\\u03c4\\u03b5\\u03bf \\u03bc\\u03b5 \\u03c4\\u03bf \\u03c3\\u03c5\\u03b3\\u03ba\\u03b5\\u03ba\\u03c1\\u03b9\\u03bc\\u03ad\\u03bd\\u03bf YouTube ID.\",\"restrictedYt\":\"\\u039f \\u03ba\\u03ac\\u03c4\\u03bf\\u03c7\\u03bf\\u03c2 \\u03b1\\u03c5\\u03c4\\u03bf\\u03cd \\u03c4\\u03bf\\u03c5 \\u03b2\\u03af\\u03bd\\u03c4\\u03b5\\u03bf \\u03b4\\u03b5\\u03bd \\u03b5\\u03c0\\u03b9\\u03c4\\u03c1\\u03ad\\u03c0\\u03b5\\u03b9 \\u03c4\\u03b7\\u03bd \\u03b5\\u03bd\\u03c3\\u03c9\\u03bc\\u03ac\\u03c4\\u03c9\\u03c3\\u03ae \\u03c4\\u03bf\\u03c5.\"},\"sources\":[{\"path\":\"videos\\\/sources-674b2a3aac7e4.mp4\",\"mime\":\"video\\\/mp4\",\"copyright\":{\"license\":\"U\"}}]},\"library\":\"H5P.Video 1.6\",\"metadata\":{\"contentType\":\"Video\",\"license\":\"U\",\"title\":\"\\u0392\\u03af\\u03bd\\u03c4\\u03b5\\u03bf\"},\"subContentId\":\"80c84c36-2517-4864-98f3-4dabf4a28f88\"},\"useSeparator\":\"auto\"},{\"content\":{\"params\":{\"media\":{\"disableImageZooming\":false},\"answers\":[{\"correct\":false,\"tipsAndFeedback\":{\"tip\":\"\",\"chosenFeedback\":\"\",\"notChosenFeedback\":\"\"},\"text\":\"<div>\\u039f\\u03bc\\u03ac\\u03b4\\u03b1 \\u03a0\\u03c1\\u03bf\\u03c3\\u03c4\\u03b1\\u03c3\\u03af\\u03b1\\u03c2 \\u0391\\u03b4\\u03ad\\u03c3\\u03c0\\u03bf\\u03c4\\u03c9\\u03bd \\u039a\\u03b1\\u03c3\\u03c4\\u03bf\\u03c1\\u03b9\\u03ac\\u03c2<\\\/div>\\n\"},{\"correct\":true,\"tipsAndFeedback\":{\"tip\":\"\",\"chosenFeedback\":\"\",\"notChosenFeedback\":\"\"},\"text\":\"<div>\\u0395\\u03b8\\u03b5\\u03bb\\u03bf\\u03bd\\u03c4\\u03b9\\u03ba\\u03ae \\u03b1\\u03b9\\u03bc\\u03bf\\u03b4\\u03bf\\u03c3\\u03af\\u03b1: \\u03c0\\u03c1\\u03ac\\u03be\\u03b7 \\u03b1\\u03bd\\u03b8\\u03c1\\u03c9\\u03c0\\u03b9\\u03ac\\u03c2 \\u03ba\\u03b1\\u03b9 \\u03b1\\u03bb\\u03bb\\u03b7\\u03bb\\u03b5\\u03b3\\u03b3\\u03cd\\u03b7\\u03c2<\\\/div>\\n\"}],\"overallFeedback\":[{\"from\":0,\"to\":100}],\"behaviour\":{\"enableRetry\":true,\"enableSolutionsButton\":true,\"enableCheckButton\":true,\"type\":\"auto\",\"singlePoint\":false,\"randomAnswers\":true,\"showSolutionsRequiresInput\":true,\"confirmCheckDialog\":false,\"confirmRetryDialog\":false,\"autoCheck\":false,\"passPercentage\":100,\"showScorePoints\":true},\"UI\":{\"checkAnswerButton\":\"\\u0388\\u03bb\\u03b5\\u03b3\\u03c7\\u03bf\\u03c2\",\"submitAnswerButton\":\"\\u03a5\\u03c0\\u03bf\\u03b2\\u03bf\\u03bb\\u03ae\",\"showSolutionButton\":\"\\u039b\\u03cd\\u03c3\\u03b7\",\"tryAgainButton\":\"\\u0395\\u03c0\\u03b1\\u03bd\\u03ac\\u03bb\\u03b7\\u03c8\\u03b7\",\"tipsLabel\":\"\\u0395\\u03bc\\u03c6\\u03ac\\u03bd\\u03b9\\u03c3\\u03b7 \\u03b5\\u03c0\\u03b5\\u03be\\u03ae\\u03b3\\u03b7\\u03c3\\u03b7\\u03c2\",\"scoreBarLabel\":\"\\u0388\\u03c7\\u03b5\\u03b9\\u03c2 :num \\u03b1\\u03c0\\u03cc :total \\u03c0\\u03cc\\u03bd\\u03c4\\u03bf\\u03c5\\u03c2\",\"tipAvailable\":\"\\u0395\\u03c0\\u03b5\\u03be\\u03ae\\u03b3\\u03b7\\u03c3\\u03b7 \\u03b4\\u03b9\\u03b1\\u03b8\\u03ad\\u03c3\\u03b9\\u03bc\\u03b7\",\"feedbackAvailable\":\"\\u0391\\u03bd\\u03b1\\u03c4\\u03c1\\u03bf\\u03c6\\u03bf\\u03b4\\u03cc\\u03c4\\u03b7\\u03c3\\u03b7 \\u03b4\\u03b9\\u03b1\\u03b8\\u03ad\\u03c3\\u03b9\\u03bc\\u03b7\",\"readFeedback\":\"\\u0391\\u03bd\\u03ac\\u03b3\\u03bd\\u03c9\\u03c3\\u03b7 \\u03b1\\u03bd\\u03b1\\u03c4\\u03c1\\u03bf\\u03c6\\u03bf\\u03b4\\u03cc\\u03c4\\u03b7\\u03c3\\u03b7\\u03c2\",\"wrongAnswer\":\"\\u039b\\u03ac\\u03b8\\u03bf\\u03c2 \\u03b1\\u03c0\\u03ac\\u03bd\\u03c4\\u03b7\\u03c3\\u03b7\",\"correctAnswer\":\"\\u03a3\\u03c9\\u03c3\\u03c4\\u03ae \\u03b1\\u03c0\\u03ac\\u03bd\\u03c4\\u03b7\\u03c3\\u03b7\",\"shouldCheck\":\"\\u0398\\u03b1 \\u03c0\\u03c1\\u03ad\\u03c0\\u03b5\\u03b9 \\u03bd\\u03b1 \\u03ad\\u03c7\\u03b5\\u03b9 \\u03b5\\u03c0\\u03b9\\u03bb\\u03b5\\u03b3\\u03b5\\u03af\",\"shouldNotCheck\":\"\\u0394\\u03b5\\u03bd \\u03b8\\u03b1 \\u03c0\\u03c1\\u03ad\\u03c0\\u03b5\\u03b9 \\u03bd\\u03b1 \\u03ad\\u03c7\\u03b5\\u03b9 \\u03b5\\u03c0\\u03b9\\u03bb\\u03b5\\u03b3\\u03b5\\u03af\",\"noInput\":\"\\u03a0\\u03b1\\u03c1\\u03b1\\u03ba\\u03b1\\u03bb\\u03ce \\u03b1\\u03c0\\u03ac\\u03bd\\u03c4\\u03b7\\u03c3\\u03b5 \\u03c0\\u03c1\\u03bf\\u03c4\\u03bf\\u03cd \\u03b4\\u03b5\\u03b9\\u03c2 \\u03c4\\u03b7 \\u03bb\\u03cd\\u03c3\\u03b7\",\"a11yCheck\":\"\\u0388\\u03bb\\u03b5\\u03b3\\u03c7\\u03bf\\u03c2 \\u03b1\\u03c0\\u03b1\\u03bd\\u03c4\\u03ae\\u03c3\\u03b5\\u03c9\\u03bd. \\u039f\\u03b9 \\u03b1\\u03c0\\u03b1\\u03bd\\u03c4\\u03ae\\u03c3\\u03b5\\u03b9\\u03c2 \\u03b8\\u03b1 \\u03bc\\u03b1\\u03c1\\u03ba\\u03b1\\u03c1\\u03b9\\u03c3\\u03c4\\u03bf\\u03cd\\u03bd \\u03c9\\u03c2 \\u03c3\\u03c9\\u03c3\\u03c4\\u03ad\\u03c2, \\u03bb\\u03ac\\u03b8\\u03bf\\u03c2 \\u03ba\\u03b1\\u03b9 \\u03bc\\u03b7 \\u03b1\\u03c0\\u03b1\\u03bd\\u03c4\\u03b7\\u03bc\\u03ad\\u03bd\\u03b5\\u03c2.\",\"a11yShowSolution\":\"\\u03a0\\u03c1\\u03bf\\u03b2\\u03bf\\u03bb\\u03ae \\u03c4\\u03b7\\u03c2 \\u03bb\\u03cd\\u03c3\\u03b7\\u03c2. \\u0397 \\u03ac\\u03c3\\u03ba\\u03b7\\u03c3\\u03b7 \\u03b8\\u03b1 \\u03b5\\u03c0\\u03b9\\u03c3\\u03b7\\u03bc\\u03b1\\u03bd\\u03b8\\u03b5\\u03af \\u03bc\\u03b5 \\u03c4\\u03b7\\u03bd \\u03c3\\u03c9\\u03c3\\u03c4\\u03ae \\u03b1\\u03c0\\u03ac\\u03bd\\u03c4\\u03c3\\u03b7.\",\"a11yRetry\":\"\\u0395\\u03c0\\u03b1\\u03bd\\u03ac\\u03bb\\u03b7\\u03c8\\u03b7 \\u03c4\\u03b7\\u03c2 \\u03b5\\u03c1\\u03b3\\u03b1\\u03c3\\u03af\\u03b1\\u03c2. \\u0395\\u03c0\\u03b1\\u03bd\\u03b1\\u03c6\\u03bf\\u03c1\\u03ac \\u03cc\\u03bb\\u03c9\\u03bd \\u03c4\\u03c9\\u03bd \\u03b1\\u03c0\\u03b1\\u03bd\\u03c4\\u03ae\\u03c3\\u03b5\\u03c9\\u03bd \\u03ba\\u03b1\\u03b9 \\u03ad\\u03bd\\u03b1\\u03c1\\u03be\\u03b7 \\u03b1\\u03c0\\u03cc \\u03c4\\u03b7\\u03bd \\u03b1\\u03c1\\u03c7\\u03ae.\"},\"confirmCheck\":{\"header\":\"\\u039f\\u03bb\\u03bf\\u03ba\\u03bb\\u03ae\\u03c1\\u03c9\\u03c3\\u03b7 \\u03ac\\u03c3\\u03ba\\u03b7\\u03c3\\u03b7\\u03c2\",\"body\":\"\\u0395\\u03af\\u03c3\\u03b1\\u03b9 \\u03c3\\u03af\\u03b3\\u03bf\\u03c5\\u03c1\\u03bf\\u03c2 \\u03cc\\u03c4\\u03b9 \\u03b8\\u03ad\\u03bb\\u03b5\\u03b9\\u03c2 \\u03bd\\u03b1 \\u03bf\\u03bb\\u03bf\\u03ba\\u03bb\\u03b7\\u03c1\\u03ce\\u03c3\\u03b5\\u03b9\\u03c2 \\u03c4\\u03b7\\u03bd \\u03ac\\u03c3\\u03ba\\u03b7\\u03c3\\u03b7;\",\"cancelLabel\":\"\\u038c\\u03c7\\u03b9\",\"confirmLabel\":\"\\u039d\\u03b1\\u03b9\"},\"confirmRetry\":{\"header\":\"\\u0395\\u03c0\\u03b1\\u03bd\\u03ac\\u03bb\\u03b7\\u03c8\\u03b7\",\"body\":\"\\u0395\\u03af\\u03c3\\u03b1\\u03b9 \\u03c3\\u03af\\u03b3\\u03bf\\u03c5\\u03c1\\u03bf\\u03c2 \\u03cc\\u03c4\\u03b9 \\u03b8\\u03ad\\u03bb\\u03b5\\u03b9\\u03c2 \\u03bd\\u03b1 \\u03c0\\u03c1\\u03bf\\u03c3\\u03c0\\u03b1\\u03b8\\u03ae\\u03c3\\u03b5\\u03b9\\u03c2 \\u03be\\u03b1\\u03bd\\u03ac;\",\"cancelLabel\":\"\\u038c\\u03c7\\u03b9\",\"confirmLabel\":\"\\u039d\\u03b1\\u03b9\"},\"question\":\"<p>\\u039c\\u03b5 \\u03c0\\u03bf\\u03b9\\u03bf \\u03ba\\u03b5\\u03af\\u03bc\\u03b5\\u03bd\\u03bf \\u03ad\\u03c7\\u03b5\\u03b9 \\u03c3\\u03c7\\u03ad\\u03c3\\u03b7 \\u03c4\\u03bf \\u03b2\\u03af\\u03bd\\u03c4\\u03b5\\u03bf \\u03c0\\u03bf\\u03c5 \\u03c0\\u03b1\\u03c1\\u03b1\\u03ba\\u03bf\\u03bb\\u03bf\\u03cd\\u03b8\\u03b7\\u03c3\\u03b5\\u03c2;&nbsp;<\\\/p>\\n\"},\"library\":\"H5P.MultiChoice 1.16\",\"metadata\":{\"contentType\":\"Multiple Choice\",\"license\":\"U\",\"title\":\"\\u0395\\u03c0\\u03af\\u03bb\\u03b5\\u03be\\u03b5 \\u03c4\\u03bf \\u03c3\\u03c9\\u03c3\\u03c4\\u03cc.\"},\"subContentId\":\"7c525af2-eb7e-41de-98fa-44bb092cd325\"},\"useSeparator\":\"auto\"},{\"content\":{\"params\":{\"media\":{\"disableImageZooming\":false},\"correct\":\"true\",\"behaviour\":{\"enableRetry\":true,\"enableSolutionsButton\":true,\"enableCheckButton\":true,\"confirmCheckDialog\":false,\"confirmRetryDialog\":false,\"autoCheck\":false},\"l10n\":{\"trueText\":\"\\u03a3\\u03c9\\u03c3\\u03c4\\u03cc\",\"falseText\":\"\\u039b\\u03ac\\u03b8\\u03bf\\u03c2\",\"score\":\"\\u03a3\\u03c5\\u03b3\\u03ba\\u03ad\\u03bd\\u03c4\\u03c1\\u03c9\\u03c3\\u03b5\\u03c2 @score \\u03b1\\u03c0\\u03cc @total \\u03b2\\u03b1\\u03b8\\u03bc\\u03bf\\u03cd\\u03c2\",\"checkAnswer\":\"\\u0388\\u03bb\\u03b5\\u03b3\\u03c7\\u03bf\\u03c2\",\"submitAnswer\":\"\\u03a5\\u03c0\\u03bf\\u03b2\\u03bf\\u03bb\\u03ae\",\"showSolutionButton\":\"\\u039b\\u03cd\\u03c3\\u03b7\",\"tryAgain\":\"\\u0395\\u03c0\\u03b1\\u03bd\\u03ac\\u03bb\\u03b7\\u03c8\\u03b7\",\"wrongAnswerMessage\":\"\\u039b\\u03ac\\u03b8\\u03bf\\u03c2 \\u03b1\\u03c0\\u03ac\\u03bd\\u03c4\\u03b7\\u03c3\\u03b7\",\"correctAnswerMessage\":\"\\u03a3\\u03c9\\u03c3\\u03c4\\u03ae \\u03b1\\u03c0\\u03ac\\u03bd\\u03c4\\u03b7\\u03c3\\u03b7\",\"scoreBarLabel\":\"\\u03a3\\u03c5\\u03b3\\u03ba\\u03b5\\u03bd\\u03c4\\u03c1\\u03ce\\u03c3\\u03b1\\u03c4\\u03b5 :num \\u03b1\\u03c0\\u03cc :total \\u03b2\\u03b1\\u03b8\\u03bc\\u03bf\\u03cd\\u03c2\",\"a11yCheck\":\"\\u0388\\u03bb\\u03b5\\u03b3\\u03c7\\u03bf\\u03c2 \\u03b1\\u03c0\\u03b1\\u03bd\\u03c4\\u03ae\\u03c3\\u03b5\\u03c9\\u03bd. \\u039f\\u03b9 \\u03b1\\u03c0\\u03b1\\u03bd\\u03c4\\u03ae\\u03c3\\u03b5\\u03b9\\u03c2 \\u03b8\\u03b1 \\u03bc\\u03b1\\u03c1\\u03ba\\u03b1\\u03c1\\u03b9\\u03c3\\u03c4\\u03bf\\u03cd\\u03bd \\u03c9\\u03c2 \\u03c3\\u03c9\\u03c3\\u03c4\\u03ad\\u03c2, \\u03bb\\u03ac\\u03b8\\u03bf\\u03c2 \\u03ba\\u03b1\\u03b9 \\u03bc\\u03b7 \\u03b1\\u03c0\\u03b1\\u03bd\\u03c4\\u03b7\\u03bc\\u03ad\\u03bd\\u03b5\\u03c2.\",\"a11yShowSolution\":\"\\u03a0\\u03c1\\u03bf\\u03b2\\u03bf\\u03bb\\u03ae \\u03c4\\u03b7\\u03c2 \\u03bb\\u03cd\\u03c3\\u03b7\\u03c2. \\u0397 \\u03ac\\u03c3\\u03ba\\u03b7\\u03c3\\u03b7 \\u03b8\\u03b1 \\u03b5\\u03c0\\u03b9\\u03c3\\u03b7\\u03bc\\u03b1\\u03bd\\u03b8\\u03b5\\u03af \\u03bc\\u03b5 \\u03c4\\u03b7\\u03bd \\u03c3\\u03c9\\u03c3\\u03c4\\u03ae \\u03b1\\u03c0\\u03ac\\u03bd\\u03c4\\u03c3\\u03b7.\",\"a11yRetry\":\"\\u0395\\u03c0\\u03b1\\u03bd\\u03ac\\u03bb\\u03b7\\u03c8\\u03b7 \\u03c4\\u03b7\\u03c2 \\u03b5\\u03c1\\u03b3\\u03b1\\u03c3\\u03af\\u03b1\\u03c2. \\u0395\\u03c0\\u03b1\\u03bd\\u03b1\\u03c6\\u03bf\\u03c1\\u03ac \\u03cc\\u03bb\\u03c9\\u03bd \\u03c4\\u03c9\\u03bd \\u03b1\\u03c0\\u03b1\\u03bd\\u03c4\\u03ae\\u03c3\\u03b5\\u03c9\\u03bd \\u03ba\\u03b1\\u03b9 \\u03ad\\u03bd\\u03b1\\u03c1\\u03be\\u03b7 \\u03b1\\u03c0\\u03cc \\u03c4\\u03b7\\u03bd \\u03b1\\u03c1\\u03c7\\u03ae.\"},\"confirmCheck\":{\"header\":\"\\u039f\\u03bb\\u03bf\\u03ba\\u03bb\\u03ae\\u03c1\\u03c9\\u03c3\\u03b7 \\u03ac\\u03c3\\u03ba\\u03b7\\u03c3\\u03b7\\u03c2\",\"body\":\"\\u0395\\u03af\\u03c3\\u03b1\\u03b9 \\u03c3\\u03af\\u03b3\\u03bf\\u03c5\\u03c1\\u03bf\\u03c2 \\u03cc\\u03c4\\u03b9 \\u03b8\\u03ad\\u03bb\\u03b5\\u03b9\\u03c2 \\u03bd\\u03b1 \\u03bf\\u03bb\\u03bf\\u03ba\\u03bb\\u03b7\\u03c1\\u03ce\\u03c3\\u03b5\\u03b9\\u03c2 \\u03c4\\u03b7\\u03bd \\u03ac\\u03c3\\u03ba\\u03b7\\u03c3\\u03b7;\",\"cancelLabel\":\"\\u038c\\u03c7\\u03b9\",\"confirmLabel\":\"\\u039d\\u03b1\\u03b9\"},\"confirmRetry\":{\"header\":\"\\u0395\\u03c0\\u03b1\\u03bd\\u03ac\\u03bb\\u03b7\\u03c8\\u03b7\",\"body\":\"\\u0395\\u03af\\u03c3\\u03b1\\u03b9 \\u03c3\\u03af\\u03b3\\u03bf\\u03c5\\u03c1\\u03bf\\u03c2\\\/\\u03b7 \\u03cc\\u03c4\\u03b9 \\u03b8\\u03ad\\u03bb\\u03b5\\u03b9\\u03c2 \\u03bd\\u03b1 \\u03c0\\u03c1\\u03bf\\u03c3\\u03c0\\u03b1\\u03b8\\u03ae\\u03c3\\u03b5\\u03b9\\u03c2 \\u03be\\u03b1\\u03bd\\u03ac;\",\"cancelLabel\":\"\\u038c\\u03c7\\u03b9\",\"confirmLabel\":\"\\u039d\\u03b1\\u03b9\"},\"question\":\"<p>\\u0397 \\u03bb\\u03ad\\u03be\\u03b7 \\\"\\u03b1\\u03b9\\u03bc\\u03bf\\u03b4\\u03bf\\u03c3\\u03af\\u03b1\\\" \\u03b5\\u03af\\u03bd\\u03b1\\u03b9 \\u03c3\\u03cd\\u03bd\\u03b8\\u03b5\\u03c4\\u03b7 \\u03bb\\u03ad\\u03be\\u03b7.<\\\/p>\\n\"},\"library\":\"H5P.TrueFalse 1.8\",\"metadata\":{\"contentType\":\"True\\\/False Question\",\"license\":\"U\",\"title\":\"\\u03a3\\u03c9\\u03c3\\u03c4\\u03cc \\u03ae \\u039b\\u03ac\\u03b8\\u03bf\\u03c2\"},\"subContentId\":\"468cb549-f54a-4f5e-9002-c180041589cb\"},\"useSeparator\":\"auto\"},{\"content\":{\"params\":{\"media\":{\"disableImageZooming\":false},\"overallFeedback\":[{\"from\":0,\"to\":100}],\"checkAnswerButton\":\"\\u0388\\u03bb\\u03b5\\u03b3\\u03c7\\u03bf\\u03c2\",\"submitAnswerButton\":\"\\u03a5\\u03c0\\u03bf\\u03b2\\u03bf\\u03bb\\u03ae\",\"tryAgainButton\":\"\\u0395\\u03c0\\u03b1\\u03bd\\u03ac\\u03bb\\u03b7\\u03c8\\u03b7\",\"showSolutionButton\":\"\\u039b\\u03cd\\u03c3\\u03b7\",\"behaviour\":{\"enableRetry\":true,\"enableSolutionsButton\":true,\"enableCheckButton\":true,\"showScorePoints\":true},\"correctAnswer\":\"\\u03a3\\u03c9\\u03c3\\u03c4\\u03cc!\",\"incorrectAnswer\":\"\\u039b\\u03ac\\u03b8\\u03bf\\u03c2!\",\"missedAnswer\":\"\\u0394\\u03b5\\u03bd \\u03b1\\u03c0\\u03b1\\u03bd\\u03c4\\u03ae\\u03b8\\u03b7\\u03ba\\u03b5!\",\"displaySolutionDescription\":\"\\u0397 \\u03ac\\u03c3\\u03ba\\u03b7\\u03c3\\u03b7 \\u03b5\\u03bd\\u03b7\\u03bc\\u03b5\\u03c1\\u03ce\\u03bd\\u03b5\\u03c4\\u03b1\\u03b9 \\u03ce\\u03c3\\u03c4\\u03b5 \\u03bd\\u03b1 \\u03c0\\u03b5\\u03c1\\u03b9\\u03ad\\u03c7\\u03b5\\u03b9 \\u03c4\\u03b7 \\u03bb\\u03cd\\u03c3\\u03b7.\",\"scoreBarLabel\":\"\\u0388\\u03c7\\u03b5\\u03b9\\u03c2 :num \\u03b1\\u03c0\\u03cc :total \\u03b2\\u03b1\\u03b8\\u03bc\\u03bf\\u03cd\\u03c2\",\"a11yFullTextLabel\":\"\\u03a0\\u03bb\\u03ae\\u03c1\\u03c9\\u03c2 \\u03b1\\u03bd\\u03b1\\u03b3\\u03bd\\u03ce\\u03c3\\u03b9\\u03bc\\u03bf \\u03ba\\u03b5\\u03af\\u03bc\\u03b5\\u03bd\\u03bf\",\"a11yClickableTextLabel\":\"\\u03a0\\u03bb\\u03ae\\u03c1\\u03b5\\u03c2 \\u03ba\\u03b5\\u03af\\u03bc\\u03b5\\u03bd\\u03bf \\u03bc\\u03b5 \\u03bb\\u03ad\\u03be\\u03b5\\u03b9\\u03c2 \\u03c0\\u03bf\\u03c5 \\u03bc\\u03c0\\u03bf\\u03c1\\u03bf\\u03cd\\u03bd \\u03bd\\u03b1 \\u03c3\\u03b7\\u03bc\\u03b5\\u03b9\\u03c9\\u03b8\\u03bf\\u03cd\\u03bd\",\"a11ySolutionModeHeader\":\"\\u039a\\u03b1\\u03c4\\u03ac\\u03c3\\u03c4\\u03b1\\u03c3\\u03b7 \\u03bb\\u03cd\\u03c3\\u03b7\\u03c2\",\"a11yCheckingHeader\":\"\\u039a\\u03b1\\u03c4\\u03ac\\u03c3\\u03c4\\u03b1\\u03c3\\u03b7 \\u03b5\\u03bb\\u03ad\\u03b3\\u03c7\\u03bf\\u03c5\",\"a11yCheck\":\"\\u0388\\u03bb\\u03b5\\u03b3\\u03c7\\u03bf\\u03c2 \\u03b1\\u03c0\\u03b1\\u03bd\\u03c4\\u03ae\\u03c3\\u03b5\\u03c9\\u03bd. \\u039f\\u03b9 \\u03b1\\u03c0\\u03b1\\u03bd\\u03c4\\u03ae\\u03c3\\u03b5\\u03b9\\u03c2 \\u03b8\\u03b1 \\u03bc\\u03b1\\u03c1\\u03ba\\u03b1\\u03c1\\u03b9\\u03c3\\u03c4\\u03bf\\u03cd\\u03bd \\u03c9\\u03c2 \\u03c3\\u03c9\\u03c3\\u03c4\\u03ad\\u03c2, \\u03bb\\u03ac\\u03b8\\u03bf\\u03c2 \\u03ba\\u03b1\\u03b9 \\u03bc\\u03b7 \\u03b1\\u03c0\\u03b1\\u03bd\\u03c4\\u03b7\\u03bc\\u03ad\\u03bd\\u03b5\\u03c2.\",\"a11yShowSolution\":\"\\u03a0\\u03c1\\u03bf\\u03b2\\u03bf\\u03bb\\u03ae \\u03c4\\u03b7\\u03c2 \\u03bb\\u03cd\\u03c3\\u03b7\\u03c2. \\u0397 \\u03ac\\u03c3\\u03ba\\u03b7\\u03c3\\u03b7 \\u03b8\\u03b1 \\u03b5\\u03c0\\u03b9\\u03c3\\u03b7\\u03bc\\u03b1\\u03bd\\u03b8\\u03b5\\u03af \\u03bc\\u03b5 \\u03c4\\u03b7\\u03bd \\u03c3\\u03c9\\u03c3\\u03c4\\u03ae \\u03b1\\u03c0\\u03ac\\u03bd\\u03c4\\u03c3\\u03b7.\",\"a11yRetry\":\"\\u0395\\u03c0\\u03b1\\u03bd\\u03ac\\u03bb\\u03b7\\u03c8\\u03b7 \\u03c4\\u03b7\\u03c2 \\u03b5\\u03c1\\u03b3\\u03b1\\u03c3\\u03af\\u03b1\\u03c2. \\u0395\\u03c0\\u03b1\\u03bd\\u03b1\\u03c6\\u03bf\\u03c1\\u03ac \\u03cc\\u03bb\\u03c9\\u03bd \\u03c4\\u03c9\\u03bd \\u03b1\\u03c0\\u03b1\\u03bd\\u03c4\\u03ae\\u03c3\\u03b5\\u03c9\\u03bd \\u03ba\\u03b1\\u03b9 \\u03ad\\u03bd\\u03b1\\u03c1\\u03be\\u03b7 \\u03b1\\u03c0\\u03cc \\u03c4\\u03b7\\u03bd \\u03b1\\u03c1\\u03c7\\u03ae.\",\"taskDescription\":\"<p>\\u0395\\u03c0\\u03b9\\u03bb\\u03ad\\u03b3\\u03c9 \\u03c4\\u03b9\\u03c2 \\u03bb\\u03ad\\u03be\\u03b5\\u03b9\\u03c2 \\u03c0\\u03bf\\u03c5 \\u03b1\\u03bd\\u03ae\\u03ba\\u03bf\\u03c5\\u03bd \\u03c3\\u03c4\\u03b7\\u03bd \\u03af\\u03b4\\u03b9\\u03b1 \\u03bf\\u03b9\\u03ba\\u03bf\\u03b3\\u03ad\\u03bd\\u03b5\\u03b9\\u03b1 \\u03bc\\u03b5 \\u03c4\\u03b7 \\u03bb\\u03ad\\u03be\\u03b7: \\\"\\u03b1\\u03b9\\u03bc\\u03bf\\u03b4\\u03bf\\u03c3\\u03af\\u03b1\\\".<\\\/p>\\n\",\"textField\":\"<p>*\\u03b1\\u03b9\\u03bc\\u03bf\\u03b4\\u03cc\\u03c4\\u03b7\\u03c2*, *\\u03b1\\u03b9\\u03bc\\u03bf\\u03b4\\u03cc\\u03c4\\u03c1\\u03b9\\u03b1*, *\\u03b1\\u03b9\\u03bc\\u03bf\\u03b4\\u03bf\\u03c4\\u03b9\\u03ba\\u03cc\\u03c2*, \\u03b1\\u03bd\\u03b8\\u03c1\\u03ce\\u03c0\\u03b9\\u03bd\\u03bf\\u03c2, \\u03b1\\u03bb\\u03bb\\u03b7\\u03bb\\u03b5\\u03b3\\u03b3\\u03cd\\u03b7<\\\/p>\\n\"},\"library\":\"H5P.MarkTheWords 1.11\",\"metadata\":{\"contentType\":\"Mark the Words\",\"license\":\"U\",\"title\":\"\\u0395\\u03c0\\u03b9\\u03bb\\u03ad\\u03b3\\u03c9 \\u03c4\\u03b9\\u03c2 \\u03bb\\u03ad\\u03be\\u03b5\\u03b9\\u03c2 \\u03c0\\u03bf\\u03c5 \\u03b1\\u03bd\\u03ae\\u03ba\\u03bf\\u03c5\\u03bd \\u03c3\\u03c4\\u03b7\\u03bd \\u03af\\u03b4\\u03b9\\u03b1 \\u03bf\\u03b9\\u03ba\\u03bf\\u03b3\\u03ad\\u03bd\\u03b5\\u03b9\\u03b1 \\u03bc\\u03b5 \\u03c4\\u03b7 \\u03bb\\u03ad\\u03be\\u03b7: &quot;\\u03b1\\u03b9\\u03bc\\u03bf\\u03b4\\u03bf\\u03c3\\u03af\\u03b1&quot;.\"},\"subContentId\":\"6ec69eff-180f-4990-9a92-6af16b31652b\"},\"useSeparator\":\"auto\"}]},\"library\":\"H5P.Column 1.18\",\"subContentId\":\"d308ac38-37a1-405c-8be4-77723edc67d7\",\"metadata\":{\"contentType\":\"Column\",\"license\":\"U\",\"title\":\"\\u03a0\\u03b1\\u03c1\\u03b1\\u03ba\\u03bf\\u03bb\\u03bf\\u03cd\\u03b8\\u03b7\\u03c3\\u03b7 \\u03b2\\u03af\\u03bd\\u03c4\\u03b5\\u03bf\"}}],\"behaviour\":{\"baseColor\":\"#1768c4\",\"defaultTableOfContents\":true,\"progressIndicators\":true,\"progressAuto\":true,\"displaySummary\":true,\"enableRetry\":true},\"read\":\"\\u0391\\u03bd\\u03ac\\u03b3\\u03bd\\u03c9\\u03c3\\u03b7\",\"displayTOC\":\"\\u03a0\\u03c1\\u03bf\\u03b2\\u03bf\\u03bb\\u03ae &#039;\\u03a0\\u03af\\u03bd\\u03b1\\u03ba\\u03b1 \\u03a0\\u03b5\\u03c1\\u03b9\\u03b5\\u03c7\\u03bf\\u03bc\\u03ad\\u03bd\\u03c9\\u03bd&#039;\",\"hideTOC\":\"\\u0391\\u03c0\\u03cc\\u03ba\\u03c1\\u03c5\\u03c8\\u03b7 &#039;\\u03a0\\u03af\\u03bd\\u03b1\\u03ba\\u03b1 \\u03a0\\u03b5\\u03c1\\u03b9\\u03b5\\u03c7\\u03bf\\u03bc\\u03ad\\u03bd\\u03c9\\u03bd&#039;\",\"nextPage\":\"\\u0395\\u03c0\\u03cc\\u03bc\\u03b5\\u03bd\\u03b7 \\u03c3\\u03b5\\u03bb\\u03af\\u03b4\\u03b1\",\"previousPage\":\"\\u03a0\\u03c1\\u03bf\\u03b7\\u03b3\\u03bf\\u03cd\\u03bc\\u03b5\\u03bd\\u03b7 \\u03c3\\u03b5\\u03bb\\u03af\\u03b4\\u03b1\",\"chapterCompleted\":\"\\u039f\\u03bb\\u03bf\\u03ba\\u03bb\\u03b7\\u03c1\\u03c9\\u03bc\\u03ad\\u03bd\\u03b7 \\u03c3\\u03b5\\u03bb\\u03af\\u03b4\\u03b1!\",\"partCompleted\":\"@pages \\u03b1\\u03c0\\u03cc @total \\u03ad\\u03c7\\u03bf\\u03c5\\u03bd \\u03bf\\u03bb\\u03bf\\u03ba\\u03bb\\u03b7\\u03c1\\u03c9\\u03b8\\u03b5\\u03af\",\"incompleteChapter\":\"\\u039c\\u03b7 \\u03bf\\u03bb\\u03bf\\u03ba\\u03bb\\u03b7\\u03c1\\u03c9\\u03bc\\u03ad\\u03bd\\u03b7 \\u03c3\\u03b5\\u03bb\\u03af\\u03b4\\u03b1\",\"navigateToTop\":\"\\u039c\\u03b5\\u03c4\\u03ac\\u03b2\\u03b1\\u03c3\\u03b7 \\u03c3\\u03c4\\u03b7\\u03bd \\u03ba\\u03bf\\u03c1\\u03c5\\u03c6\\u03ae\",\"markAsFinished\":\"\\u0388\\u03c7\\u03c9 \\u03bf\\u03bb\\u03bf\\u03ba\\u03bb\\u03b7\\u03c1\\u03ce\\u03c3\\u03b5\\u03b9 \\u03b1\\u03c5\\u03c4\\u03ae\\u03bd \\u03c4\\u03b7 \\u03c3\\u03b5\\u03bb\\u03af\\u03b4\\u03b1\",\"fullscreen\":\"\\u03a0\\u03bb\\u03ae\\u03c1\\u03b7\\u03c2 \\u03bf\\u03b8\\u03cc\\u03bd\\u03b7\",\"exitFullscreen\":\"\\u0388\\u03be\\u03bf\\u03b4\\u03bf\\u03c2 \\u03b1\\u03c0\\u03cc \\u03c0\\u03c1\\u03cc\\u03b2\\u03bf\\u03bb\\u03ae \\u03c0\\u03bb\\u03ae\\u03c1\\u03bf\\u03c5\\u03c2 \\u03bf\\u03b8\\u03cc\\u03bd\\u03b7\\u03c2\",\"bookProgressSubtext\":\"@count \\u03b1\\u03c0\\u03cc @total \\u03c3\\u03b5\\u03bb\\u03af\\u03b4\\u03b5\\u03c2\",\"interactionsProgressSubtext\":\"@count \\u03b1\\u03c0\\u03cc @total \\u03b4\\u03c1\\u03b1\\u03c3\\u03c4\\u03b7\\u03c1\\u03b9\\u03bf\\u03c4\\u03ae\\u03c4\\u03c9\\u03bd\",\"submitReport\":\"\\u03a5\\u03c0\\u03bf\\u03b2\\u03bf\\u03bb\\u03ae \\u0391\\u03bd\\u03b1\\u03c6\\u03bf\\u03c1\\u03ac\\u03c2\",\"restartLabel\":\"\\u0395\\u03c0\\u03b1\\u03bd\\u03b5\\u03ba\\u03ba\\u03af\\u03bd\\u03b7\\u03c3\\u03b7\",\"summaryHeader\":\"\\u03a0\\u03b5\\u03c1\\u03af\\u03bb\\u03b7\\u03c8\\u03b7\",\"allInteractions\":\"\\u038c\\u03bb\\u03b5\\u03c2 \\u03bf\\u03b9 \\u03b4\\u03b9\\u03b1\\u03b4\\u03c1\\u03ac\\u03c3\\u03b5\\u03b9\\u03c2\",\"unansweredInteractions\":\"\\u0394\\u03b9\\u03b1\\u03b4\\u03c1\\u03ac\\u03c3\\u03b5\\u03b9\\u03c2 \\u03c0\\u03bf\\u03c5 \\u03b4\\u03b5\\u03bd \\u03b1\\u03c0\\u03b1\\u03bd\\u03c4\\u03ae\\u03b8\\u03b7\\u03ba\\u03b1\\u03bd\",\"scoreText\":\"@score \\\/ @maxscore\",\"leftOutOfTotalCompleted\":\"@left \\u03b1\\u03c0\\u03cc @max \\u03b4\\u03b9\\u03b1\\u03b4\\u03c1\\u03ac\\u03c3\\u03b5\\u03b9\\u03c2 \\u03ad\\u03c7\\u03bf\\u03c5\\u03bd \\u03bf\\u03bb\\u03bf\\u03ba\\u03bb\\u03b7\\u03c1\\u03c9\\u03b8\\u03b5\\u03af\",\"noInteractions\":\"\\u0394\\u03b5\\u03bd \\u03c5\\u03c0\\u03ac\\u03c1\\u03c7\\u03bf\\u03c5\\u03bd \\u03b4\\u03b9\\u03b1\\u03b4\\u03c1\\u03ac\\u03c3\\u03b5\\u03b9\\u03c2\",\"score\":\"\\u0392\\u03b1\\u03b8\\u03bc\\u03bf\\u03bb\\u03bf\\u03b3\\u03af\\u03b1\",\"summaryAndSubmit\":\"\\u03a0\\u03b5\\u03c1\\u03af\\u03bb\\u03b7\\u03c8\\u03b7 &amp; \\u03c5\\u03c0\\u03bf\\u03b2\\u03bf\\u03bb\\u03ae\",\"noChapterInteractionBoldText\":\"\\u0394\\u03b5\\u03bd \\u03ad\\u03c7\\u03b5\\u03c4\\u03b5 \\u03bf\\u03bb\\u03bf\\u03ba\\u03bb\\u03b7\\u03c1\\u03ce\\u03c3\\u03b5\\u03b9 \\u03ba\\u03b1\\u03bc\\u03af\\u03b1 \\u03b4\\u03b9\\u03ac\\u03b4\\u03c1\\u03b1\\u03c3\\u03b7 \\u03c3\\u03b5 \\u03ba\\u03b1\\u03bc\\u03af\\u03b1 \\u03b1\\u03c0\\u03cc \\u03c4\\u03b9\\u03c2 \\u03c3\\u03b5\\u03bb\\u03af\\u03b4\\u03b5\\u03c2.\",\"noChapterInteractionText\":\"\\u03a0\\u03c1\\u03ad\\u03c0\\u03b5\\u03b9 \\u03bd\\u03b1 \\u03ad\\u03c7\\u03b5\\u03c4\\u03b5 \\u03bf\\u03bb\\u03bf\\u03ba\\u03bb\\u03b7\\u03c1\\u03ce\\u03c3\\u03b5\\u03b9 \\u03c4\\u03bf\\u03c5\\u03bb\\u03ac\\u03c7\\u03b9\\u03c3\\u03c4\\u03bf\\u03bd \\u03bc\\u03b9\\u03b1 \\u03b4\\u03b9\\u03ac\\u03b4\\u03c1\\u03b1\\u03c3\\u03b7 \\u03c3\\u03b5 \\u03c4\\u03bf\\u03c5\\u03bb\\u03ac\\u03c7\\u03b9\\u03c3\\u03c4\\u03bf\\u03bd \\u03bc\\u03b9\\u03b1 \\u03c3\\u03b5\\u03bb\\u03af\\u03b4\\u03b1 \\u03b3\\u03b9\\u03b1 \\u03bd\\u03b1 \\u03bc\\u03c0\\u03bf\\u03c1\\u03ad\\u03c3\\u03b5\\u03c4\\u03b5 \\u03bd\\u03b1 \\u03c0\\u03c1\\u03bf\\u03b2\\u03ac\\u03bb\\u03b5\\u03c4\\u03b5 \\u03c4\\u03b7\\u03bd \\u03c0\\u03b5\\u03c1\\u03af\\u03bb\\u03b7\\u03c8\\u03b7.\",\"yourAnswersAreSubmittedForReview\":\"\\u039f\\u03b9 \\u03b1\\u03c0\\u03b1\\u03bd\\u03c4\\u03ae\\u03c3\\u03b5\\u03b9\\u03c2 \\u03c3\\u03b1\\u03c2 \\u03ad\\u03c7\\u03bf\\u03c5\\u03bd \\u03c5\\u03c0\\u03bf\\u03b2\\u03bb\\u03b7\\u03b8\\u03b5\\u03af \\u03b3\\u03b9\\u03b1 \\u03ad\\u03bb\\u03b5\\u03b3\\u03c7\\u03bf!\",\"bookProgress\":\"\\u03a0\\u03c1\\u03cc\\u03bf\\u03b4\\u03bf\\u03c2 \\u03c3\\u03c4\\u03bf \\u03b2\\u03b9\\u03b2\\u03bb\\u03af\\u03bf\",\"interactionsProgress\":\"\\u03a0\\u03c1\\u03cc\\u03bf\\u03b4\\u03bf\\u03c2 \\u03b4\\u03b9\\u03b1\\u03b4\\u03c1\\u03ac\\u03c3\\u03b5\\u03c9\\u03bd\",\"totalScoreLabel\":\"\\u03a3\\u03c5\\u03bd\\u03bf\\u03bb\\u03b9\\u03ba\\u03ae \\u03b2\\u03b1\\u03b8\\u03bc\\u03bf\\u03bb\\u03bf\\u03b3\\u03af\\u03b1\",\"a11y\":{\"progress\":\"\\u03a3\\u03b5\\u03bb\\u03af\\u03b4\\u03b1 @page \\u03b1\\u03c0\\u03cc @total.\",\"menu\":\"\\u0395\\u03bd\\u03b1\\u03bb\\u03bb\\u03b1\\u03b3\\u03ae \\u03bc\\u03b5\\u03bd\\u03bf\\u03cd \\u03c0\\u03bb\\u03bf\\u03ae\\u03b3\\u03b7\\u03c3\\u03b7\\u03c2\"}}"}}});