H5P.Collage = (function ($, EventDispatcher) {

  /**
   * Create a new collage.
   *
   * @class H5P.Collage
   * @extends H5P.EventDispatcher
   * @param {Object} parameters
   * @param {number} contentId
   */
  function Collage(parameters, contentId) {
    var self = this;

    // Initialize event inheritance
    EventDispatcher.call(self);

    // Content defaults
    setDefaults(parameters, {
      collage: {
        template: '2-1',
        options: {
          heightRatio: 0.75,
          spacing: 0.5,
          frame: true
        },
        clips: []
      }
    });
    var content = parameters.collage;
    var $wrapper;

    // Create new template for adding clips to
    var template = new Collage.Template(content.options.spacing);

    // Add clips to columns
    template.on('columnAdded', function (event) {
      var $col = event.data;
      var clipIndex = self.clips.length;

      // Set default
      if (!content.clips[clipIndex]) {
        content.clips[clipIndex] = {};
      }

      // Add new clip
      var clip = new Collage.Clip($col, content.clips[clipIndex], contentId);
      self.clips.push(clip);

      self.trigger('clipAdded', clip);
      clip.load();
    });

    /**
     * Creates the HTML the first time the collage is attaced.
     *
     * @private
     */
    var createHtml = function () {
      // Create collage wrapper
      var wrapperOptions = {
        'class': 'h5p-collage-wrapper',
        css: {}
      };
      if (content.options.frame) {
        wrapperOptions.css.borderWidth = content.options.spacing + 'em';
      }
      $wrapper = $('<div/>', wrapperOptions);

      // Add template
      template.appendTo($wrapper);

      // Render template
      self.setLayout(content.template);
    };

    /**
     * Attach the collage to the given container.
     *
     * @param {H5P.jQuery} $container
     */
    self.attach = function ($container) {
      this.triggerConsumed();
      if ($wrapper === undefined) {
        createHtml();
        var $parent = $container.parent();
        if (!$parent.hasClass('h5p-frame')) {
          $parent.css('backgroundColor', 'transparent');
        }
      }

      // Add to DOM
      $container.addClass('h5p-collage').html('').append($wrapper);
    };

    /**
     * Trigger the 'consumed' xAPI event when this commences
     *
     * (Will be more sophisticated in future version)
     */
    self.triggerConsumed = function () {
      var xAPIEvent = this.createXAPIEventTemplate({
        id: 'http://activitystrea.ms/schema/1.0/consume',
        display: {
          'en-US': 'consumed'
        }
      }, {
        result: {
          completion: true
        }
      });
      this.trigger(xAPIEvent);
    };

    /**
     * Set a new collage layout.
     *
     * @param {string} newLayout
     */
    self.setLayout = function (newLayout) {
      self.clips = [];
      template.setLayout(newLayout);
    };

    /**
     * Set the spacing between the collage clips.
     *
     * @param {number} newSpacing
     */
    self.setSpacing = function (newSpacing) {
      template.setSpacing(newSpacing);
    };

    /**
     * Set the frame around the collage.
     *
     * @param {number} newFrameWidth
     */
    self.setFrame = function (newFrameWidth) {
      $wrapper.css('borderWidth', newFrameWidth + 'em');
    };

    /**
     * Set the height / aspect ratio of the collage.
     *
     * @param {number} newHeight
     */
    self.setHeight = function (newHeight) {
      // Update template
      var wrapperSize = $wrapper[0].getBoundingClientRect();
      $wrapper.css('height', (wrapperSize.width * newHeight) + 'px');
    };

    /**
     * Handle resize events
     */
    self.on('resize', function () {
      if ($wrapper === undefined) {
        return;
      }

      // Get outer width without rounding
      var width = $wrapper[0].getBoundingClientRect().width;
      $wrapper.css({
        fontSize: ((width / 480) * 16) + 'px',
        height: (content.options.heightRatio * width) + 'px'
      });

      // Position clips
      for (let i = 0; i < self.clips.length; i++) {
        if (!self.clips[i].isPositioned()) {
          self.clips[i].positionImage()
        }
      }
    });
  }

  // Extends the event dispatcher
  Collage.prototype = Object.create(EventDispatcher.prototype);
  Collage.prototype.constructor = Collage;

  /**
   * Simple recusive function the helps set default values without
   * destroying object references.
   *
   * @param {object} params values
   * @param {object} values default values
   */
  var setDefaults = function (params, values) {
    for (var prop in values) {
      if (values.hasOwnProperty(prop)) {
        if (params[prop] === undefined) {
          params[prop] = values[prop];
        }
        else if (params[prop] instanceof Object && !(params[prop] instanceof Array)) {
          setDefaults(params[prop], values[prop]);
        }
      }
    }
  };

  return Collage;
})(H5P.jQuery, H5P.EventDispatcher);
;
(function ($, EventDispatcher, Collage) {

  /**
   * Collage Template
   *
   * @class H5P.Collage.Template
   * @extends H5P.EventDispatcher
   * @param {number} spacing
   * @param {string} layout
   */
  Collage.Template = function (spacing, layout) {
    var self = this;

    // Initialize event inheritance
    EventDispatcher.call(self);

    // Create template wrapper
    var $wrapper;

    // Half the spacing
    spacing /= 2;

    // Keep track of our rows
    var table = [];

    /**
     * Add columns to row.
     *
     * @private
     * @param {H5P.jQuery} $row
     * @param {number} num
     */
    var addCols = function ($row, num) {
      var cols = [];

      for (var i = 0; i < num; i++) {
        // Add column to row
        var $col = $('<div/>', {
          'class': 'h5p-collage-col',
          css: {
            width: (100 / num) + '%',
            borderLeftWidth: (i === 0 ? 0 : spacing + 'em'),
            borderRightWidth: (i === num - 1 ? 0 : spacing + 'em')
          },
          appendTo: $row
        });

        self.trigger('columnAdded', $col);
        cols.push($col);
      }

      return cols;
    };

    /**
     * Add rows to wrapper.
     *
     * @private
     * @param {Array} rows
     */
    var addRows = function (rows) {
      for (var i = 0; i < rows.length; i++) {
        // Add row to wrapper
        var $row = $('<div/>', {
          'class': 'h5p-collage-row',
          css: {
            height: (100 / rows.length) + '%',
            borderTopWidth: (i === 0 ? 0 : spacing + 'em'),
            borderBottomWidth: (i === rows.length - 1 ? 0 : spacing + 'em')
          },
          appendTo: $wrapper
        });

        // Add row columns
        table.push({
          $row: $row,
          cols: addCols($row, Number(rows[i]))
        });
      }
    };

    /**
     * Append template to given container.
     *
     * @param {H5P.jQuery} $container
     */
    self.appendTo = function ($container) {
      // Create wrapper
      $wrapper = $('<div/>', {
        'class': 'h5p-collage-template'
      });

      // Initialize right away if we have a layout
      if (layout) {
        self.setLayout(layout);
      }

      // Insert our wrapper into the given container
      $wrapper.appendTo($container);
    };

    /**
     * Set a new layout for the template.
     *
     * @param {string} newLayout
     */
    self.setLayout = function (newLayout) {
      $wrapper.html('');
      addRows(newLayout.split('-'));
    };

    /**
     * Set the spacing between the clips.
     *
     * @param {number} newSpacing
     */
    self.setSpacing = function (newSpacing) {
      spacing = newSpacing / 2;

      // Update table styling
      for (var i = 0; i < table.length; i++) {
        var row = table[i];
        row.$row.css({
          borderTopWidth: (i === 0 ? 0 : spacing + 'em'),
          borderBottomWidth: (i === table.length - 1 ? 0 : spacing + 'em')
        });

        for (var j = 0; j < row.cols.length; j++) {
          row.cols[j].css({
            borderLeftWidth: (j === 0 ? 0 : spacing + 'em'),
            borderRightWidth: (j === row.cols.length - 1 ? 0 : spacing + 'em')
          });
        }
      }
    };
  };

  // Extends the event dispatcher
  Collage.Template.prototype = Object.create(EventDispatcher.prototype);
  Collage.Template.prototype.constructor = Collage.Template;

})(H5P.jQuery, H5P.EventDispatcher, H5P.Collage);
;
(function ($, Collage, EventDispatcher) {

  /**
   * Collage Clip
   *
   * @class H5P.Collage.Clip
   * @extends H5P.EventDispatcher
   * @param {H5P.jQuery} $container
   * @param {Object} content
   * @param {number} contentId
   */
  Collage.Clip = function ($container, content, contentId) {
    var self = this;

    // Initialize event inheritance
    EventDispatcher.call(self);

    // Photo wrapper
    self.$wrapper = $('<div/>', {
      'class': 'h5p-collage-photo',
      appendTo: $container
    });

    // Clip resource
    var $img;

    // Always available
    self.content = content;

    // Keep track of image has been positioned
    let isPositioned = false;

    /**
     * Position the clip image according to params.
     */
    self.positionImage = function () {
      if (self.$wrapper[0].offsetParent === null || isPositioned || !$img || !$img.length) {
        return; // Not visible, position will not be correct
      }

      // Determine image ratio
      const imageRatio = $img[0].width ? ($img[0].width / $img[0].height) : (content.image.width && content.image.height ? content.image.width / content.image.height : null);
      if (imageRatio === null) {
        return; // Skip
      }
      isPositioned = true;

      // Find container raioratios
      var containerSize = window.getComputedStyle(self.$wrapper[0]);
      var containerRatio = (parseFloat(containerSize.width) / parseFloat(containerSize.height));

      // Make sure image covers the whole container
      if (isNaN(containerRatio) || imageRatio > containerRatio) {
        self.prop = 'height';
      }
      else {
        self.prop = 'width';
      }
      $img.css(self.prop, (content.scale * 100) + '%');

      // Pan image
      $img.css('margin', content.offset.top + '% 0 0 ' + content.offset.left + '%');
    };
    /**
     * Decode html
     */ 
    self.decodeHTML = value => ($('<textarea/>').html(value).text());

    /**
     * Triggers the loading of the image.
     */
    self.load = function () {
      if (self.empty()) {
        self.$wrapper.addClass('h5p-collage-empty');
        return; // No image set
      }
      else {
        self.$wrapper.removeClass('h5p-collage-empty');
      }

      // Create image
      $img = $('<img/>', {
        'class': 'h5p-collage-image',
        alt: self.decodeHTML(content.alt),
        title: self.decodeHTML(content.title),
        src: H5P.getPath(content.image.path, contentId),
        prependTo: self.$wrapper,
        on: {
          load: function () {
            // Make sure it's in the correct position
            self.positionImage();
          }
        }
      });
      setTimeout(function () {
        // Wait for next tick to make sure everything is visible
        self.positionImage();
      }, 0);
      self.trigger('change', $img);
    };

    /**
     * Check if the current clip is empty or set.
     *
     * @returns {boolean}
     */
    self.empty = function () {
      return !content.image;
    };

    /**
     * Check if the current clip is positioned yet.
     *
     * @returns {boolean}
     */
    self.isPositioned = function () {
      return isPositioned;
    };
  };

  // Extends the event dispatcher
  Collage.Clip.prototype = Object.create(EventDispatcher.prototype);
  Collage.Clip.prototype.constructor = Collage.Clip;

})(H5P.jQuery, H5P.Collage, H5P.EventDispatcher);
;