Source: webcomponents.js

'use strict'

import {helpers} from "./helpers";

/**
 * @module webcomponents
 * @deprecated
 */
export const webcomponents = {};

/**
 * A skeleton to ease the creation of web components.
 *
 * @memberOf module:webcomponents
 * @type {webcomponents.WebComponent}
 * @extends {HTMLElement}
 */
webcomponents.WebComponent = class extends HTMLElement {

  /**
   * @constructor
   */
  constructor() {
    super();
    this.attachShadow({mode: 'open'});
  }

  /**
   * Called every time the element is inserted into the DOM.
   *
   * @name connectedCallback
   * @function
   * @protected
   * @override
   */
  connectedCallback() {

    const styles = this.externalStyles();
    const scripts = this.externalScripts();
    const template = this.template();

    const wrapper = document.createElement('div');
    wrapper.id = 'wcw'; // wcw = Web Component Wrapper
    this.shadowRoot.appendChild(wrapper);

    if (styles && styles.length > 0 && scripts && scripts.length > 0) {
      helpers.injectStyles(wrapper, styles).then(() => {
        helpers.injectScripts(wrapper, scripts).then(() => {
          if (template !== '') {
            wrapper.insertAdjacentHTML('beforeend', template);
          }
          this.renderedCallback();
        });
      });
    } else if ((!styles || styles.length === 0) && scripts && scripts.length > 0) {
      helpers.injectScripts(wrapper, scripts).then(() => {
        if (template !== '') {
          wrapper.insertAdjacentHTML('beforeend', template);
        }
        this.renderedCallback();
      });
    } else if (styles && styles.length > 0 && (!scripts || scripts.length === 0)) {
      helpers.injectStyles(wrapper, styles).then(() => {
        if (template !== '') {
          wrapper.insertAdjacentHTML('beforeend', template);
        }
        this.renderedCallback();
      });
    } else {
      if (template !== '') {
        wrapper.insertAdjacentHTML('beforeend', template);
      }
      this.renderedCallback();
    }
  }

  /**
   * Called every time the element is removed from the DOM.
   *
   * @name disconnectedCallback
   * @function
   * @protected
   * @override
   */
  disconnectedCallback() {
  }

  /**
   * Called after the `template` has been added to the DOM.
   *
   * @name renderedCallback
   * @function
   * @protected
   * @override
   */
  renderedCallback() {
  }

  /**
   * A list of stylesheets URL.
   *
   * @return {Array<string>} an array of URL.
   * @name externalStyles
   * @function
   * @protected
   * @override
   */
  externalStyles() {
    return [];
  }

  /**
   * A list of scripts URL.
   *
   * @return {Array<string>} an array of URL.
   * @name externalScripts
   * @function
   * @protected
   * @override
   */
  externalScripts() {
    return [];
  }

  /**
   * Returns the component HTML template.
   *
   * @return {string} the HTML.
   * @name template
   * @function
   * @protected
   */
  template() {
    return ``;
  }

  /**
   * Emit a custom event.
   *
   * @param {string} type the event type.
   * @param {Object|null} data any data structure to pass along with the event.
   * @param {Node|null} elem the element to attach the event to.
   * @return {boolean} returns true if either event's cancelable attribute value is false or its preventDefault() method was not invoked, and false otherwise.
   * @name emit
   * @function
   * @protected
   */
  emit(type, data = {}, elem = document) {
    const event = new CustomEvent(type, {
      bubbles: true, cancelable: true, detail: {
        component: this, data: data
      }
    });
    return (elem ? elem : document).dispatchEvent(event);
  }

  /**
   * Returns the component attribute value or a default value if none was found.
   *
   * @param {string} attr the attribute to get.
   * @param {string|null} defaultValue the default value.
   * @return {string|null} the attribute value if any, defaultValue otherwise.
   * @name getAttributeOrDefault
   * @function
   * @protected
   */
  getAttributeOrDefault(attr, defaultValue) {
    return this.hasAttribute(attr) ? this.getAttribute(attr) : defaultValue;
  }

  /**
   * Returns the first element with a given identifier or class name.
   *
   * @param {string} idOrClassName the identifier or class name to match.
   * @return {Element|null} an HTML element.
   * @name getElement
   * @function
   * @protected
   */
  getElement(idOrClassName) {
    return this.shadowRoot.querySelector(idOrClassName);
  }

  /**
   * Get the first page element with a given identifier or class name.
   *
   * @param {string} idOrClassName the identifier or class name to match.
   * @return {Element|null} an HTML element.
   * @name getPageElement
   * @function
   * @protected
   */
  getPageElement(idOrClassName) {
    return document.querySelector(idOrClassName);
  }

  /**
   * Add a given class to a given element.
   *
   * @param {string} idOrClassName the identifier or class name to match.
   * @param {string} className the class name to add.
   * @name addCssClass
   * @function
   * @protected
   */
  addCssClass(idOrClassName, className) {
    const el = this.getElement(idOrClassName);
    if (el) {
      el.classList.add(className);
    }
  }

  /**
   * Remove a given class from a given element.
   *
   * @param {string} idOrClassName the identifier or class name to match.
   * @param {string} className the class name to remove.
   * @name removeCssClass
   * @function
   * @protected
   */
  removeCssClass(idOrClassName, className) {
    const el = this.getElement(idOrClassName);
    if (el) {
      el.classList.remove(className);
    }
  }

  /**
   * Add a class if it does not already exist on a given element, remove it otherwise.
   *
   * @param {string} idOrClassName the identifier or class name to match.
   * @param {string} className the class name to toggle.
   * @name toggleCssClass
   * @function
   * @protected
   */
  toggleCssClass(idOrClassName, className) {
    const el = this.getElement(idOrClassName);
    if (el) {
      el.classList.toggle(className);
    }
  }

  /**
   * Check if a given element has a given class.
   *
   * @param {string} idOrClassName the identifier or class name to match.
   * @param {string} className the class name to search.
   * @return true if the element contains the given class, false otherwise.
   * @name includesCssClass
   * @function
   * @protected
   */
  includesCssClass(idOrClassName, className) {
    const el = this.getElement(idOrClassName);
    if (el) {
      return el.classList.contains(className);
    }
    return false;
  }

  /**
   * Get all classes associated to a given element.
   *
   * @param {string} idOrClassName the identifier or class name to match.
   * @return {Array<string>} the class names.
   * @name getAllCssClasses
   * @function
   * @protected
   */
  getAllCssClasses(idOrClassName) {
    const el = this.getElement(idOrClassName);
    if (el) {
      return el.className.split(' ').map(clazz => clazz.trim());
    }
    return [];
  }

  /**
   * Replace all classes associated to a given element.
   *
   * @param {string} idOrClassName the identifier or class name to match.
   * @param {Array<string>|string} classes the class names.
   * @name replaceAllCssClasses
   * @function
   * @protected
   */
  replaceAllCssClasses(idOrClassName, classes) {
    const el = this.getElement(idOrClassName);
    if (el) {
      if (Array.isArray(classes)) {
        el.className = classes.join(' ');
      } else {
        el.className = classes;
      }
    }
  }

  /**
   * Get the style associated to a given element.
   *
   * @param {string} idOrClassName the identifier or class name to match.
   * @return {CSSStyleDeclaration|null} the element style.
   * @name getStyle
   * @function
   * @protected
   */
  getStyle(idOrClassName) {
    const el = this.getElement(idOrClassName);
    if (el) {
      return el.style;
    }
    return null;
  }

  /**
   * Get the actual computed style associated to a given element.
   *
   * @param {string} idOrClassName the identifier or class name to match.
   * @return {CSSStyleDeclaration|null} the element computed style.
   * @name getComputedStyle
   * @function
   * @protected
   */
  getComputedStyle(idOrClassName) {
    const el = this.getElement(idOrClassName);
    if (el) {
      return window.getComputedStyle(el);
    }
    return null;
  }

  /**
   * Set the text associated to a given element.
   *
   * @param {string} idOrClassName the identifier or class name to match.
   * @param {string} text the text to set.
   * @name setText
   * @function
   * @protected
   */
  setText(idOrClassName, text) {
    const el = this.getElement(idOrClassName);
    if (el) {
      el.textContent = text;
    }
  }

  /**
   * Set the HTML associated to a given element.
   *
   * @param {string} idOrClassName the identifier or class name to match.
   * @param {string} html the html to set.
   * @name setHtml
   * @function
   * @protected
   */
  setHtml(idOrClassName, html) {
    const el = this.getElement(idOrClassName);
    if (el) {
      el.innerHTML = html;
    }
  }

  /**
   * Append an element to a given element.
   *
   * @param {string} idOrClassName the identifier or class name to match.
   * @param {Element} element the element to append.
   * @name replaceContent
   * @function
   * @protected
   */
  replaceContent(idOrClassName, element) {
    const el = this.getElement(idOrClassName);
    if (el) {
      el.innerHTML = '';
      el.appendChild(element);
    }
  }
}