Source: scripts/helper/_size.js

'use strict'

/**
 * Window size global handler.
 * @namespace size
 * @memberof UCASDesignFramework
 */

UCASDesignFramework.size = {

  initOnLoad: true,
  resizeTimeout: 0,
  scrollTimeout: 0,
  clientWidth: 0,
  dfResize: {},
  position: null,
  positionLocked: false,

  /**
   * Get breakpoint, based on pixels.
   * @deprecated
   * @type {string}
   * @memberof! UCASDesignFramework.size
   * @public
   */
  get breakpoint () {
    return this.currentBreakpoint(document.body.clientWidth);
  },

  /**
   * Get cssBreakpoint, based on em.
   * @type {string}
   * @memberof! UCASDesignFramework.size
   * @public
   */
  get cssBreakpoint () {
    return window.getComputedStyle(document.querySelector('body'), ':before').getPropertyValue('content').replace(/\"/g, '');
  },

  /**
   * Initialise plugin.
   * @function init
   * @memberof! UCASDesignFramework.size
   * @public
   */
  init: function () {
    document.body.setAttribute('data-breakpoint', this.cssBreakpoint)
    var event = document.createEvent('Event')

    // @event dfpAdvertResize
    event.initEvent('dfpAdvertResize', true, true)

    window.addEventListener('resize', function () {
      // If the screen has switched to a new breakpoint and we have ad placements
      // refresh the adverts by dispatching advertResize event.
      if (document.body.getAttribute('data-breakpoint') !== UCASDesignFramework.size.cssBreakpoint) {
        var placements = document.querySelectorAll('[data-google-slot-id]')

        if (placements.length > 0) {
          document.dispatchEvent(event)
        }
      }

      document.body.setAttribute('data-breakpoint', UCASDesignFramework.size.cssBreakpoint)
    }, false)

    // Add the custom resize event.
    UCASDesignFramework.size.clientWidth = document.body.clientWidth
    UCASDesignFramework.size.dfResize = document.createEvent('Event')
    UCASDesignFramework.size.dfResize.initEvent('dfResize', true, true)
    window.addEventListener('resize', UCASDesignFramework.size.resizeThrottler, false)
    // Add a throttled scroll event.
    UCASDesignFramework.size.dfScroll = document.createEvent('Event')
    UCASDesignFramework.size.dfScroll.initEvent('dfScroll', true, true)
    window.addEventListener('scroll', UCASDesignFramework.size.scrollThrottler, false)
  },

  breakpoints: { small: 720, medium: 992, large: 1200 },

  /**
   * Calculate current breakpoint, based on pixel width.
   * @function currentBreakpoint
   * @memberof! UCASDesignFramework.size
   * @public
   * @deprecated
   * @param  {number} width - browser width in pixels
   * @return {string} - the keyword for the breakpoint
   */
  currentBreakpoint: function (width) {
    var breakpoints = UCASDesignFramework.size.breakpoints

    if (width < breakpoints.small) {
      return 'small'
    }
    if (width > breakpoints.large) {
      return 'xlarge'
    }
    if (width > breakpoints.small && width < breakpoints.medium) {
      return 'medium'
    }

    return 'large'
  },

  /**
   * Calculate if a breakpoint, based on pixel width, is greater than the
   * current browser width.
   * @function greaterThan
   * @memberof! UCASDesignFramework.size
   * @public
   * @deprecated
   * @param  {String} width - a breakpoint keyword
   * @return {Boolean} - true if the breakpoint is greater than the current browser width
   */
  greaterThan: function (width) {
    return document.body.clientWidth > UCASDesignFramework.size.breakpoints[width]
  },

  /**
   * Slow down the resize handler so we don't repaint too much.
   * Triggers a custom event which can be used instead of window.resize.
   * @function resizeThrottler
   * @memberof! UCASDesignFramework.size
   * @public
   */
  resizeThrottler: function () {
    if (!UCASDesignFramework.size.resizeTimeout) {
      UCASDesignFramework.size.resizeTimeout = setTimeout(function () {
        UCASDesignFramework.size.resizeTimeout = null

        // Check width as triggering the keyboard on Android changes page size.
        // We're only interested if the width changes.
        if (UCASDesignFramework.size.clientWidth !== document.body.clientWidth) {
          UCASDesignFramework.size.clientWidth = document.body.clientWidth
          window.dispatchEvent(UCASDesignFramework.size.dfResize)
        }
      }, 100)
    }
  },

  /**
   * Slow down the scroll handler so we don't repaint too much.
   * Triggers a custom event which can be used instead of window.scroll.
   * @function scrollThrottler
   * @memberof! UCASDesignFramework.size
   * @public
   */
  scrollThrottler: function () {
    if (!UCASDesignFramework.size.scrollTimeout) {
      UCASDesignFramework.size.scrollTimeout = setTimeout(function () {
        UCASDesignFramework.size.scrollTimeout = null
        window.dispatchEvent(UCASDesignFramework.size.dfScroll)
      }, 100)
    }
  },

  /**
   * Resize a text input field by setting the size attribute
   * to the length of the value.
   * @function resizeTextFieldToFitContent
   * @memberof! UCASDesignFramework.size
   * @public
   * @param {Node} field - an input field
   * @param {Number} [max=50] - the maximum size
   */
  resizeTextFieldToFitContent: function (field, max) {
    max = max || 50
    if (field.nodeName === 'INPUT' && field.value.length) {
      var length = field.value.length > 50 ? 50 : field.value.length
      var size = Math.ceil(length * 0.95) // This is a best guess at not allowing too big a size.
      field.setAttribute('size', size)
    }
  },

  /**
   * Resize an element by setting its width to that of its inner text plus any extra that might be needed.
   * @function resizeElementToContentWidth
   * @memberof! UCASDesignFramework.size
   * @public
   * @param {Node} element - an element
   * @param {Number} padding - any extra padding to be added to the width
   */
  resizeElementToContentWidth: function (element, padding) {
    var textElement = element

    // Assign a specific textElement as required by the element type.
    if (element.nodeName === 'SELECT') {
      textElement = element[element.selectedIndex]
    }

    // Work out the padding if it's not set.
    if (padding === undefined) {
      padding = parseFloat(getComputedStyle(element).paddingLeft) + parseFloat(getComputedStyle(element).paddingRight)
      padding += parseFloat(getComputedStyle(element).borderLeftWidth) + parseFloat(getComputedStyle(element).borderRightWidth)
      if (element !== textElement) {
        padding += parseFloat(getComputedStyle(textElement).paddingLeft) + parseFloat(getComputedStyle(textElement).paddingRight)
        padding += parseFloat(getComputedStyle(textElement).borderLeftWidth) + parseFloat(getComputedStyle(textElement).borderRightWidth)
      }
    }

    if (textElement.innerText) {
      element.style.width = UCASDesignFramework.size.getWidthOfText(textElement) + padding + 'px'
    } else {
      console.warn('Element type ' + element.nodeName + ' is not currently supported by resizeElementToContentWidth(). Please raise a ticket.')
    }
  },

  /**
   * Work out the width of some text in pixels
   * by recreating it in a temporary span.
   * @function getWidthOfText
   * @memberof! UCASDesignFramework.size
   * @public
   * @param {Node} element - an element that contains text
   * @return {Number} width in px
   */
  getWidthOfText: function (element) {
    var text = element.innerText
    var span = document.createElement('span')
    var width

    span.innerText = text
    // Make sure we have all the right styles on the text.
    span.style.fontFamily = getComputedStyle(element).fontFamily
    span.style.fontSize = getComputedStyle(element).fontSize
    span.style.fontWeight = getComputedStyle(element).fontWeight
    span.style.zIndex = -1
    span.style.position = 'absolute'

    document.body.appendChild(span)
    width = span.offsetWidth
    document.body.removeChild(span)
    return width
  },

  /**
   * Lock the page position.
   * @function lockPosition
   * @public
   * @param {Boolean} hide - hide the main content.
   */
  lockPosition: function (hide) {
    if (!this.positionLocked) {
      UCASDesignFramework.size.position = window.pageYOffset
      var wrapper = document.documentElement
      wrapper.style.top = -UCASDesignFramework.size.position + 'px'
      wrapper.style.overflow = 'hidden'

      // We lock position when we display something on top of the main content, such as a modal.
      // To cope with the HTML not being in the correct order for z-index, we set the z-index
      // of the header to something low.
      var header = document.getElementById('section--upper-header')
      header.style.zIndex = '0'

      if (hide) {
        var mainContent = document.getElementById('wrapper')
        if (mainContent) {
          mainContent.style.opacity = '0'
        }
      }
      this.positionLocked = true
    }
  },

  /**
   * Unlock the page position.
   * @function unlockPosition
   * @public
   */
  unlockPosition: function () {
    if (this.positionLocked) {
      var mainContent = document.getElementById('wrapper')
      if (mainContent) {
        mainContent.style.opacity = ''
      }

      // Reset the z-index of the header.
      var header = document.getElementById('section--upper-header')
      header.style.zIndex = ''

      var wrapper = document.documentElement
      wrapper.style.top = null
      wrapper.style.overflow = null
      window.scrollTo(0, UCASDesignFramework.size.position)
      this.positionLocked = false
    }
  },

  // Add possible subscriptions to the DF bus.
  addSubscriptions: true
}