Source: scripts/helper/_sticky.js

/* global jQuery */
/* global Stickyfill */

'use strict';
/**
 * Initialises polyfill for sticky funtionality
 * see https://github.com/wilddeer/stickyfill
 * @namespace sticky
 * @memberof UCASDesignFramework
 * @param {object} global - UCASDesignFramework object.
 * @param {object} $ - jQuery.
 * @param {object} context - context to limit the selectors.
 * @param {object} _u - UCASUtilities object.
 */
(function (global, $, context, _u) {
  // Create namespaced object for module
  global.sticky = {}
  var stickies = {}
  var conditionalStickies = {}

  /**
   * Initialise plugin.
   * @function init
   * @memberof! UCASDesignFramework.sticky
   * @public
   * @param  {Node} [context=document] - DOM element we're limiting selection to
   */
  function init (context) {
    context = context || document
    conditionalStickies = $('[data-sticky-conditional]', context)
    stickies = $('.sticky', context)
    // If the window is wide enough, enable stickiness.
    if (context.body.clientWidth >= 720) {
      stickies.each(function () {
        $(this).Stickyfill()
      })
      conditionalSticky(context)
    }
    // Add an event listener on scroll to check if we need the
    // content has resized and we need to remove the conditional sticky
    // and a MutationObserver to see if the content has changed.
    if (conditionalStickies.length > 0) {
      $(window).on('scroll.sticky', _u.throttle(rebuildStickyConditional, 1000))
      if (window.MutationObserver) {
        addMutationObserver()
      }
    }
    // Add an event listener on resize to check if we need to
    // remove sticky.
    $(window).on('resize.sticky', function () {
      if (context.body.clientWidth >= 720) {
        stickies.each(function () {
          $(this).Stickyfill()
        })
        conditionalSticky(context)
      }
      // Make sure we remove the sticky functionality at small sizes.
      if (document.body.clientWidth < 720) {
        Stickyfill.kill()
      }
      Stickyfill.rebuild()
    })
  }

  /**
   * Add a mutation observer to see if the conditional stickies change content.
   */
  function addMutationObserver () {
    var onLoadImage = function (event) {
      rebuildStickyConditional()
      event.target.removeEventListener('load', onLoadImage)
    }
    var observer = new MutationObserver(function (mutations) {
      rebuildStickyConditional()
      // Loop through the mutations and check for images and iframes,
      // which will need to rebuild the sticky when loaded.
      mutations.forEach(function (mutation) {
        _u.forEach(mutation.addedNodes, function (index, node) {
          if ((node.nodeName === 'IMG' || node.nodeName === 'IFRAME') && !node.complete) {
            node.addEventListener('load', onLoadImage)
          }
        })
      })
    })
    var observerConfig = {
      childList: true,
      subtree: true
    }
    conditionalStickies.each(function () {
      observer.observe(this, observerConfig)
    })
  }

  /**
   * Rebuild conditional sticky, checking its height.
   */
  function rebuildStickyConditional () {
    conditionalStickies.each(function () {
      if ($(this).outerHeight(true) > $(window).height()) {
        $(this).removeClass('sticky')
        Stickyfill.remove(this)
      }
    })
    Stickyfill.rebuild()
  }

  /**
   * Remove all Stickyfill bindings and added classes
   * @function destroy
   * @memberof! UCASDesignFramework.sticky
   * @public
   * @param {Node} context - DOM element we're limiting selection to
   */
  function destroy (context) {
    Stickyfill.kill()
    $(window).off('resize.sticky', context)
    $(window).off('scroll.sticky', context)
    conditionalStickies.removeClass('sticky')
  }

  /**
   * Conditionally prevent Stickyfill from affecting elements taller than viewport
   */
  function conditionalSticky () {
    conditionalStickies.each(function () {
      // If the element is taller than the viewport
      if ($(this).outerHeight(true) < $(window).height()) {
        // Add sticky class to element
        $(this).addClass('sticky')
        Stickyfill.add(this)
      } else {
        $(this).removeClass('sticky')
        Stickyfill.remove(this)
      }
    })
  }

  /**
   * Map local functions to global object
   */
  function exports () {
    global.sticky.init = init
    global.sticky.destroy = destroy
  }
  /**
   * Return public functions
   */
  return exports()
})(UCASDesignFramework, jQuery, document, UCASUtilities)