Source: components/forms/_password-toggle.js

'use strict';

/**
 * Password toggle component.
 * @namespace passwordToggle
 * @memberof UCASDesignFramework
 * @param {object} global - UCASDesignFramework object.
 * @param {object} _u - UCASUtilities object.
 */
(function (global, _u) {
  var passwordToggles = {}

  /**
   * Initialises any form items it finds that have a data-password-toggle attribute
   * set to true to allow the password to toggle between password and text types.
   * @function init
   * @memberof! UCASDesignFramework.passwordToggle
   * @param {Node} [context=document] - context to limit the selector
   * @param {String} [selector=.form-item [data-password-toggle="true"]] - a valid CSS selector for the password toggle data attribute
   * @returns {Boolean} - true for success
   * @example
   * UCASDesignFramework.passwordToggle.init()
   */
  function init (context, selector) {
    context = context || document
    selector = selector || '.form-item [data-password-toggle="true"]'

    var formItems = document.querySelectorAll(selector)

    if (!formItems) {
      _u.log.info('Failed to find any passwords fields to toggle')
      return false
    } else {
      formItems.forEach(function (formItem) {
        if (!formItem.id) {
          _u.log.info('Form item must have an ID')
          return
        }
        if (passwordToggles[formItem.id]) {
          _u.log.info('Already initialised password toggle with ID ' + formItem.id)
          return
        }

        createPasswordToggle(formItem)
      })
    }
    return true
  }

  /**
   * PasswordToggle.
   * @class
   * @memberof! UCASDesignFramework.passwordToggle
   */
  var PasswordToggle = {
    init: function (formItem) {
      createPasswordToggleButton(formItem)
    },
    destroy: function (formItem) {
      removePasswordToggleButton(formItem)
      delete passwordToggles[formItem.id]
    }
  }

  /**
   * @function createPasswordToggleButton
   * @memberof! UCASDesignFramework.passwordToggle
   * @param {Node} formItem - the form element
   * @private
   */
  function createPasswordToggleButton (formItem) {
    // Create the password-toggle wrapper.
    passwordToggles[formItem.id].$toggleWrapper = document.createElement('div')
    passwordToggles[formItem.id].$toggleButton = document.createElement('a')

    var toggleWrapper = passwordToggles[formItem.id].$toggleWrapper
    var toggleButton = passwordToggles[formItem.id].$toggleButton
    // The node surrounding the wrapper.
    var formItemContainer = formItem.parentNode

    // Add the required classes, attributes and content to the wrapper and button.
    toggleWrapper.classList.add('password-toggle')
    toggleButton.classList.add('password-toggle__button')
    toggleButton.setAttribute('role', 'button')

    // Add the toggle click event to the toggle button.
    toggleButton.addEventListener('click', togglePasswordHandler.bind(this, formItem, false), false)

    // Insert the wrapper before the form item.
    formItemContainer.insertBefore(toggleWrapper, formItem)

    // Move the form item into the wrapper.
    toggleWrapper.appendChild(formItem)
    toggleWrapper.appendChild(toggleButton)

    // Call the toggle function with setup set to true to set the initial state of the toggle.
    togglePasswordHandler(formItem, true)
  }

  /**
   * @function removePasswordToggleButton
   * @memberof! UCASDesignFramework.passwordToggle
   * @param {Node} formItem - the form element
   * @private
   */
  function removePasswordToggleButton (formItem) {
    var toggleWrapper = passwordToggles[formItem.id].$toggleWrapper
    var toggleButton = passwordToggles[formItem.id].$toggleButton
    // The node surrounding the wrapper.
    var formItemContainer = toggleWrapper.parentNode

    // Set the password type back to password.
    formItem.setAttribute('type', 'password')

    // Remove the toggle click event from the toggle button.
    toggleButton.removeEventListener('click', togglePasswordHandler.bind(this, formItem, false), false)

    // Move all children out of the wrapper apart from the button.
    while (toggleWrapper.firstChild && toggleWrapper.firstChild !== toggleButton) formItemContainer.insertBefore(toggleWrapper.firstChild, toggleWrapper)

    // Remove the wrapper and button.
    formItemContainer.removeChild(toggleWrapper)
  }

  /**
   * Handler that toggles the password field between password and text types.
   * @function togglePasswordHandler
   * @memberof! UCASDesignFramework.passwordToggle
   * @param {Node} formItem - the form element
   * @param {Boolean} setup - if the toggle is being setup for the first time
   * @private
   */
  function togglePasswordHandler (formItem, setup) {
    var toggleButton = passwordToggles[formItem.id].$toggleButton
    var toggleButtonComputedStyle = window.getComputedStyle(toggleButton)

    if (!setup) formItem.setAttribute('type', formItem.getAttribute('type') === 'password' ? 'text' : 'password')
    toggleButton.innerHTML = formItem.getAttribute('type') === 'password' ? formItem.getAttribute('data-password-toggle-show-text') || 'Show' : formItem.getAttribute('data-password-toggle-hide-text') || 'Hide'
    toggleButton.setAttribute('title', formItem.getAttribute('type') === 'password' ? 'Show password' : 'Hide password')
    formItem.style.paddingRight = parseFloat(toggleButtonComputedStyle.getPropertyValue('width')) + (parseFloat(toggleButtonComputedStyle.getPropertyValue('right')) * 2) + 'px'
    if (!setup) formItem.focus()
  }

  /**
   * Creates and registers a passwordToggle.
   * @function create
   * @memberof! UCASDesignFramework.passwordToggle
   * @param {Node} formItem - the form element
   * @return {PasswordToggle} - the PasswordToggle object
   * @example
   * UCASDesignFramework.passwordToggle.create($myFormItem)
   */
  function createPasswordToggle (formItem) {
    if (passwordToggles[formItem.id] === undefined) {
      if (formItem.id) {
        passwordToggles[formItem.id] = Object.create(PasswordToggle)
        passwordToggles[formItem.id].init(formItem)
        return passwordToggles[formItem.id]
      } else {
        _u.log.info('Form item must have an ID')
      }
    }
  }

  /**
   * @function destroy
   * @memberof! UCASDesignFramework.passwordToggle
   * @param {Node} formItem - the form element
   * @example
   * UCASDesignFramework.passwordToggle.destroy($myFormItem)
   */
  function destroyPasswordToggle (formItem) {
    if (passwordToggles[formItem.id]) {
      passwordToggles[formItem.id].destroy(formItem)
    } else {
      _u.log.info('Form item must have an ID')
    }
  }

  // Expose public methods and properties.
  global.passwordToggle = {
    addSubscriptions: true,
    init: init,
    initOnLoad: true,
    create: createPasswordToggle,
    destroy: destroyPasswordToggle
  }
})(UCASDesignFramework, UCASUtilities)