Source: components/messages/_messages.js

'use strict';
/**
 * Messages component.
 * @namespace messages
 * @memberof UCASDesignFramework
 * @param {object} global - UCASDesignFramework object.
 * @param {object} _u - UCASUtilities object.
 */
(function (global, _u) {
  var alertsContainer
  var messages = {}

  /**
   * Message.
   * @class
   * @memberof! UCASDesignFramework.messages
   */
  var Message = {
    init: function (params, messagesList) {
      // If any more params are needed then a polyfill for Object.assign might be a good idea to support IE.
      this.type = params.type || 'info'
      this.message = params.message
      this.closeButton = params.closeButton === undefined ? true : params.closeButton
      this.deleteAfterClose = params.deleteAfterClose === undefined ? true : params.deleteAfterClose
      this.id = params.id === undefined ? 'message' + _u.unique.genId() : params.id
      this.static = params.static === undefined ? false : params.static
      this.duration = params.duration || 5000
      // Create the generic message container.
      this.containerDiv = document.createElement('div')
      this.containerDiv.classList.add('message')
      // Create the generic message content.
      this.contentDiv = document.createElement('div')
      this.contentDiv.classList.add('message--content')
      this.contentDiv.textContent = this.message
      this.contentDiv.setAttribute('role', 'alert')
      // Put them together.
      this.containerDiv.appendChild(this.contentDiv)

      // Deal with optional parameters.
      if (this.closeButton) { addCloseButton(this) }
      if (this.static) { this.containerDiv.classList.add('message-static') } else {
        this.timeout = window.setTimeout(function () {
          destroyMessage(this.id)
        }.bind(this), this.duration)
      }
      switch (this.type) {
        case 'info':
          this.containerDiv.classList.add('message--information')
          break
        case 'warning':
          this.containerDiv.classList.add('message--warning')
          break
        case 'success':
          this.containerDiv.classList.add('message--success')
          break
        case 'error':
          this.containerDiv.classList.add('message--error')
          break
      }
      // Add to the messages list.
      messagesList[this.id] = this
    },
    destroy: function (messagesList) {
      if (this.containerDiv instanceof Node) {
        this.containerDiv.classList.add('message--disappearing')
        window.setTimeout(function () { alertsContainer.removeChild(this.containerDiv) }.bind(this), 550)
      }
      if (this.timeout) {
        window.clearTimeout(this.timeout)
      }
      // Remove from messages list.
      delete messagesList[this.id]
    }
  }

  /**
   * Initialise the messages plugin.
   * @function init
   * @memberof! UCASDesignFramework.messages
   * @public
   */
  function init () {
    // Handle any existing messages in the markup.
    document.querySelectorAll('.message:not(.message--static)').forEach(
      function (div) {
        // This is to support legacy behaviour. Not creating a full object, here.
        addCloseButton({ containerDiv: div, deleteAfterClose: false })
      }
    )
  }

  /**
   * Add the close button to a message.
   * @param {Message} message - The message object.
   */
  function addCloseButton (message) {
    var button = document.createElement('span')
    var closeMessage = function () {
      if (message.deleteAfterClose) {
        destroyMessage(message.id)
      } else {
        // I can't remember why this happens, but it's the legacy behaviour so I'm leaving it for now.
        message.containerDiv.classList.add('message__closed')
      }
    }
    button.classList.add('message__close-button')
    message.containerDiv.appendChild(button)
    button.addEventListener('click', closeMessage)
  }

  /**
   * Create a message or update the message if it already exists.
   * @public
   * @param {Object} params - message params.
   * @param {String} params.message - the message text.
   * @param {('error'|'warning'|'success'|'info')} [params.type='info'] - the message type.
   * @param {Boolean} [params.closeButton=true] - true to add a close button.
   * @param {Boolean} [params.deleteAfterClose=true] - true to delete the message after close instead of just hiding it.
   * @param {Boolean} [params.static=false] - true to keep the message on the screen.
   * @param {Number} [params.duration=5000] - number of milliseconds to keep the messsage on the screen.
   * @return {String} the id of the message object.
   */
  function createMessage (params) {
    if (params === null || typeof params !== 'object' || !params.message) {
      _u.log.warn('message params needs to be an object')
      return
    }

    var message

    // Check if message already exists.
    if (params.id && messages[params.id]) {
      message = messages[params.id]
      message.contentDiv.innerText = params.message
    } else {
      message = Object.create(Message)
      message.init(params, messages)
      alertsContainer || createAlertsContainer()
      alertsContainer.appendChild(message.containerDiv)
    }

    return message.id
  }

  /**
   * Destroy a message.
   * @public
   * @param {String} id - the id of the message
   */
  function destroyMessage (id) {
    if (messages[id]) {
      messages[id].destroy(messages)
    }
  }

  /**
   * Destroy all messages.
   * @public
   */
  function destroyAllMessages () {
    Object.keys(messages).forEach(function (id) {
      destroyMessage(id)
    })
  }

  /**
   * Create the alerts container.
   */
  function createAlertsContainer () {
    alertsContainer = document.createElement('div')
    alertsContainer.id = 'alerts-container'
    alertsContainer.classList.add('wrapper')
    document.body.appendChild(alertsContainer)
  }

  // Expose public methods and properties.
  global.messages = {
    addSubscriptions: true,
    init: init,
    create: createMessage,
    destroy: destroyMessage,
    destroyAll: destroyAllMessages
  }
})(UCASDesignFramework, UCASUtilities)