Source: picture/modules/picture.js

/**
 * @summary aofl-picture
 * @version 3.0.0
 * @since 3.0.0
 * @author Arian Khosravi <arian.khosravi@aofl.com>
 */
import {AoflElement} from '@aofl/element';
import style from './picture.css';

/**
 * <aofl-picture> serves as a container for zero or more <aofl-source> and one <aofl-img> elements
 * to provide versions of an image for different display sizes.
 *
 * @memberof module:@aofl/picture
 * @extends {AoflElement}
 */
class AoflPicture extends AoflElement {
  /**
   * Creates an instance of AoflPicture.
   */
  constructor() {
    super();
    this.img = null;
    this.defaultSrc = null;
    this.sources = [];
    this.updateImageSrc = () => {
      const matchedSource = AoflPicture.findMatchingSource(this.sources);
      this.setMediaSrc(matchedSource);
    };
  }
  /**
   *
   * @readonly
   * @type {String}
   */
  static get is() {
    return 'aofl-picture';
  }
  /**
   *
   * @readonly
   * @type {Object}
   */
  static get properties() {
    return {
      'disable-sources': {type: Boolean}
    };
  }
  /**
   *
   * @return {Object}
   */
  render() {
    return super.render((ctx, html) => html`<slot></slot>`, [style]);
  }
  /**
   * setImg should be invoked by a slotted <aofl-img> and sets the aofl-img element as the main img element of aofl-picture.
   *
   * @param {HTMLElement} img
   */
  setImg(img) {
    /* istanbul ignore next */
    if (this.img !== null) { return; }
    this.img = img;
    this.defaultSrc = {
      src: this.img.src,
      height: this.img.height,
      width: this.img.width
    };

    this.updateImageSrc();
  }

  /**
   * addSource should be invoked by a slotted <aofl-source> element and adds aofl-source elements as alternative
   * sources for image besed on media attribute of aofl-source
   *
   * @param {String} source
   */
  addSource(source) {
    if (this['disable-sources']) return;
    const mediaQuery = window.matchMedia(source.getAttribute('media'));
    const sourceAttributes = {
      src: source.getAttribute('srcset'),
      height: source.getAttribute('height'),
      width: source.getAttribute('width'),
      mediaQuery
    };

    this.sources.push(sourceAttributes);
    mediaQuery.addListener(this.updateImageSrc);

    this.updateImageSrc();
  }
  /**
   * Set's aofl-picture's image source to the matching aofl-source media query.
   *
   * @param {String} [source=this.defaultSrc]
   */
  setMediaSrc(source = this.defaultSrc) {
    if (!this.img) { return; }
    const imgSrc = this.img.src;

    if (imgSrc !== source.src) {
      this.img.setAttribute('src', source.src);

      /* istanbul ignore else */
      if (source.width) {
        this.img.setAttribute('width', source.width);
      }

      /* istanbul ignore else */
      if (source.height) {
        this.img.setAttribute('height', source.height);
      }
    }
  }
  /**
   * Iterates through an array of sources and returns the first matching source.
   *
   * @param {Array} [sources=[]]
   * @return {Object}
   */
  static findMatchingSource(/* istanbul ignore next */sources = []) {
    for (let i = 0; i < sources.length; i++) {
      if (sources[i].mediaQuery.matches === true) {
        return sources[i];
      }
    }
  }
  /**
   *
   */
  disconnectedCallback() {
    for (let i = 0; i < this.sources.length; i++) {
      this.sources[i].mediaQuery.removeListener(this.updateImageSrc);
    }
    super.disconnectedCallback();
  }
}

if (window.customElements.get(AoflPicture.is) === void 0) {
  window.customElements.define(AoflPicture.is, AoflPicture);
}

export default AoflPicture;