Source: components/images/_lazy-images.js

'use strict';

/**
 * Lazy images component.
 * See lazy loading documentation under utilities on the index page
 * @namespace lazyImages
 * @memberof UCASDesignFramework
 * @param {object} global - UCASDesignFramework object.
 * @param {object} _u - UCASUtilities object.
 */
(function (global, _u) {
  var lazyImages

  /**
   * @function init
   * @memberof! UCASDesignFramework.lazyImages
   * @param {Node} [context=document] - context to limit the selector
   * @param {String} [selector=[data-lazy-image-background]:not([data-lazy-image-prepared])] - a valid CSS selector for the element to lazy load
   * @param {String} [image=http://example.com/image.jpg] - an image to load when the selector is passed
   * @returns {Boolean} - true for success
   * @example
   * UCASDesignFramework.lazyImages.init()
   */
  function init (context, selector, image) {
    context = context || document
    selector = selector || '[data-lazy-image-background]:not([data-lazy-image-prepared])'
    lazyImages = context.querySelectorAll(selector)

    if (!lazyImages) {
      _u.log.info('Failed to find any lazy images')
      return false
    }

    lazyImages.forEach(function (lazyImage) {
      if (image !== undefined) {
        lazyImage.setAttribute('data-lazy-image-background', image)
      }

      if (window['IntersectionObserver']) {
        createObserver(lazyImage)
      } else {
        loadImage(lazyImage)
      }
    })
  }

  /**
   * @function createObserver
   * @memberof! UCASDesignFramework.lazyImages
   * @param {Array} entries - list of lazyImages objects
   * @param {observer} observer - the calling observer
   * @private
   */
  function handleIntersect (entries, observer) {
    entries.forEach(function (entry) {
      if (entry.isIntersecting) {
        loadImage(entry.target)
        observer.unobserve(entry.target)
      }
    })
  }

  /**
   * @function createObserver
   * @memberof! UCASDesignFramework.lazyImages
   * @param {Node} lazyImage - a lazyImages node
   * @private
   */
  function createObserver (lazyImage) {
    const options = {
      root: null,
      threshold: '0'
    }
    const observer = new IntersectionObserver(handleIntersect, options)
    observer.observe(lazyImage)
  }

  /**
   * @function loadImage
   * @memberof! UCASDesignFramework.lazyImages
   * @param {Node} lazyImage - a lazyImages node
   * @private
   */
  function loadImage (lazyImage) {
    var imageElement = new Image()
    imageElement.src = lazyImage.getAttribute('data-lazy-image-background')
    var lazyImageTarget = lazyImage
    var transitionRemoved = false

    if (lazyImage.hasAttribute('data-lazy-image-child')) {
      var lazyBackground = document.createElement('div')
      var lazyChildClasses = lazyImage.getAttribute('data-lazy-image-child')
      var lazyChildData = lazyImage.getAttribute('data-lazy-image-data')

      // Done this way for IE support.
      lazyChildClasses.split(' ').forEach(
        function (classname) {
          lazyBackground.classList.add(classname)
        }
      )

      // Done this way for IE support.
      if (lazyChildData) {
        lazyChildData.split(' ').forEach(
          function (dataAttribute) {
            lazyBackground.setAttribute(dataAttribute, '')
          }
        )
      }

      lazyBackground.classList.add('lazy-image-child')

      // Done this way for IE support.
      lazyImage.insertBefore(lazyBackground, lazyImage.childNodes[0])

      lazyImageTarget = lazyBackground
    }

    lazyImageTarget.style.backgroundImage = 'url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAcIAAAGQCAQAAACXV6gyAAADO0lEQVR42u3TMQ0AAAzDsJU/6bFoHxtCpOSAqUgAJgQTAiYEEwImBBMCJgQTAiYEEwImBBMCJgQTAiYEEwImBBMCJgQTAiYEEwImBBMCJgQTAiYEEwImBBMCJgQTAiYEEwImBBMCJgQTAiYEEwImBBMCJgQTAiYEEwImBBMCJgQTAiYEEwImBBMCJgQTAiYEEwImBBMCJgQTAiYEE4IJAROCCQETggkBE4IJAROCCQETggkBE4IJAROCCQETggkBE4IJAROCCQETggkBE4IJAROCCQETggkBE4IJAROCCQETggkBE4IJAROCCQETggkBE4IJAROCCQETggkBE4IJAROCCQETggkBE4IJAROCCQETggkBE4IJAROCCQETggnBhIAJwYSACcGEgAnBhIAJwYSACcGEgAnBhIAJwYSACcGEgAnBhIAJwYSACcGEgAnBhIAJwYSACcGEgAnBhIAJwYSACcGEgAnBhIAJwYSACcGEgAnBhIAJwYSACcGEgAnBhIAJwYSACcGEgAnBhIAJwYSACcGEgAnBhIAJwYSACcGEYELAhGBCwIRgQsCEYELAhGBCwIRgQsCEYELAhGBCwIRgQsCEYELAhGBCwIRgQsCEYELAhGBCwIRgQsCEYELAhGBCwIRgQsCEYELAhGBCwIRgQsCEYELAhGBCwIRgQsCEYELAhGBCwIRgQsCEYELAhGBCwIRgQsCEYELAhGBCMKEEYEIwIWBCMCFgQjAhYEIwIWBCMCFgQjAhYEIwIWBCMCFgQjAhYEIwIWBCMCFgQjAhYEIwIWBCMCFgQjAhYEIwIWBCMCFgQjAhYEIwIWBCMCFgQjAhYEIwIWBCMCFgQjAhYEIwIWBCMCFgQjAhYEIwIWBCMCFgQjAhYEIwIWBCMCGYEDAhmBAwIZgQMCGYEDAhmBAwIZgQMCGYEDAhmBAwIZgQMCGYEDAhmBAwIZgQMCGYEDAhmBAwIZgQMCGYEDAhmBAwIZgQMCGYEDAhmBAwIZgQMCGYEDAhmBAwIZgQMCGYEDAhmBAwIZgQMCGYEDAhmBAwIZgQMCGYEDAhmBAwIZgQTAiYEEwImBBMCJgQTAiYEEwImBBMCLQ8WyQBkb6vglMAAAAASUVORK5CYII=)'
    lazyImageTarget.style.transition = 'background .5s linear'

    imageElement.addEventListener('load', function () {
      setTimeout(function () {
        renderLazyImage(lazyImage)
        // IE and Firefox don't support transitions on background images, so this timeout is a fallback to remove the transition.
        setTimeout(function () {
          if (!transitionRemoved) {
            lazyImageTarget.style.removeProperty('transition')
          }
        }, 700)
      }, 100)
    })
    imageElement.addEventListener('error', function () {
      _u.log.error('Unable to lazy load image.')
    })
    lazyImageTarget.addEventListener('transitionend', function (event) {
      if ((event.propertyName === 'background') || (event.propertyName === 'background-image')) {
        lazyImageTarget.style.removeProperty('transition')
        transitionRemoved = true
      }
    })
  }

  /**
   * @function renderLazyImage
   * @memberof! UCASDesignFramework.lazyImages
   * @param {Node} lazyImage - a lazyImages node
   * @private
   */
  function renderLazyImage (lazyImage) {
    var lazyImageTarget = (lazyImage.hasAttribute('data-lazy-image-child')) ? lazyImage.querySelector('.lazy-image-child') : lazyImage

    lazyImage.setAttribute('data-lazy-image-prepared', '')

    lazyImageTarget.style.backgroundImage = 'url(' + lazyImage.getAttribute('data-lazy-image-background') + ')'
  }

  /**
   * @function destroy
   * @memberof! UCASDesignFramework.lazyImages
   * @example
   * UCASDesignFramework.lazyImages.destroy()
   */
  function destroy () {

  }

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