text: 'use strict';
  
  /**
   * This module defines a collection of static general utility functions.
   *
   * @module util
   */
  (function () {
    /**
     * Adds the given class to the given element.
     *
     * @param {HTMLElement} element The element to add the class to.
     * @param {string} className The class to add.
     */
    function addClass(element, className) {
      element.className += ' ' + className;
    }
  
    /**
     * Removes the given class from the given element.
     *
     * @param {HTMLElement} element The element to remove the class from.
     * @param {string} className The class to remove.
     */
    function removeClass(element, className) {
      element.className = element.className.split(' ').filter(function (value) {
        return value !== className;
      }).join(' ');
    }
  
    /**
     * Sets the CSS transition-timing-function style of the given element with the given cubic-
     * bezier points.
     *
     * @param {HTMLElement} element The element.
     * @param {{p1x: Number, p1y: Number, p2x: Number, p2y: Number}} bezierPts The cubic-bezier
     * points to use for this timing function.
     */
    function setTransitionCubicBezierTimingFunction(element, bezierPts) {
      var value = 'cubic-bezier(' + bezierPts.p1x + ',' + bezierPts.p1y + ',' + bezierPts.p2x + ',' +
          bezierPts.p2y + ')';
      element.style.transitionTimingFunction = value;
      element.style.WebkitTransitionTimingFunction = value;
      element.style.MozTransitionTimingFunction = value;
      element.style.msTransitionTimingFunction = value;
      element.style.OTransitionTimingFunction = value;
    }
  
    // A collection of different types of easing functions.
    var easingFunctions = {
      linear: function (t) {
        return t;
      },
      easeInQuad: function (t) {
        return t * t;
      },
      easeOutQuad: function (t) {
        return t * (2 - t);
      },
      easeInOutQuad: function (t) {
        return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
      },
      easeInCubic: function (t) {
        return t * t * t;
      },
      easeOutCubic: function (t) {
        return 1 + --t * t * t;
      },
      easeInOutCubic: function (t) {
        return t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1;
      },
      easeInQuart: function (t) {
        return t * t * t * t;
      },
      easeOutQuart: function (t) {
        return 1 - --t * t * t * t;
      },
      easeInOutQuart: function (t) {
        return t < 0.5 ? 8 * t * t * t * t : 1 - 8 * --t * t * t * t;
      },
      easeInQuint: function (t) {
        return t * t * t * t * t;
      },
      easeOutQuint: function (t) {
        return 1 + --t * t * t * t * t;
      },
      easeInOutQuint: function (t) {
        return t < 0.5 ? 16 * t * t * t * t * t : 1 + 16 * --t * t * t * t * t;
      }
    };
  
    // A collection of the inverses of different types of easing functions.
    var inverseEasingFunctions = {
      linear: function (t) {
        return t;
      },
      easeInQuad: function (t) {
        return Math.sqrt(t);
      },
      easeOutQuad: function (t) {
        return 1 - Math.sqrt(1 - t);
      },
      easeInOutQuad: function (t) {
        return t < 0.5 ? Math.sqrt(t * 0.5) : 1 - 0.70710678 * Math.sqrt(1 - t);
      }
    };
  
    /**
     * Removes any children elements from the given parent that have the given class.
     *
     * @param {HTMLElement} parent The parent to remove children from.
     * @param {string} className The class to match.
     */
    function removeChildrenWithClass(parent, className) {
      var matchingChildren, i, count;
  
      matchingChildren = parent.querySelectorAll('.' + className);
  
      for (i = 0, count = matchingChildren.length; i < count; i++) {
        parent.removeChild(matchingChildren[i]);
      }
    }
  
    /**
     * A cross-browser compatible requestAnimationFrame. From
     * https://hacks.mozilla.org/2011/08/animating-with-javascript-from-setinterval-to-requestanimationframe/
     *
     * @type {Function}
     */
    var requestAnimationFrame =
        window.requestAnimationFrame || // the standard
        window.webkitRequestAnimationFrame || // chrome/safari
        window.mozRequestAnimationFrame || // firefox
        window.oRequestAnimationFrame || // opera
        window.msRequestAnimationFrame || // ie
        function (callback) { // default
          window.setTimeout(callback, 16); // 60fps
        };
  
    /**
     * Calculates the x and y coordinates represented by the given Bezier curve at the given
     * percentage.
     *
     * @param {number} percent Expressed as a number between 0 and 1.
     * @param {Array.<{x: number, y: number}>} controlPoints
     * @returns {{x: number, y: number}}
     */
    function getXYFromPercentWithBezier(percent, controlPoints) {
      var x, y, oneMinusPercent, tmp1, tmp2, tmp3, tmp4;
  
      oneMinusPercent = 1 - percent;
      tmp1 = oneMinusPercent * oneMinusPercent * oneMinusPercent;
      tmp2 = 3 * percent * oneMinusPercent * oneMinusPercent;
      tmp3 = 3 * percent * percent * oneMinusPercent;
      tmp4 = percent * percent * percent;
  
      x = controlPoints[0].x * tmp1 +
          controlPoints[1].x * tmp2 +
          controlPoints[2].x * tmp3 +
          controlPoints[3].x * tmp4;
      y = controlPoints[0].y * tmp1 +
          controlPoints[1].y * tmp2 +
          controlPoints[2].y * tmp3 +
          controlPoints[3].y * tmp4;
  
      return {x: x, y: y};
    }
  
    /**
     * Calculates the y coordinate of the given Bezier curve at the given x coordinate.
     *
     * This is helpful as an easing function for determining the value at a given time.
     *
     * @param {number} x
     * @param {Array.<{x: number, y: number}>} controlPoints
     * @returns {number}
     */
    function getYFromXWithBezier(x, controlPoints) {
      // TODO: use this bucket algorithm; this will require adding a bucket setup function in util, calling it for each of the easing functions in config, and storing these buckets with the config objects
  
  //  From: http://gamedev.stackexchange.com/questions/71845/cubic-bezier-for-easing
  //
  //    Other method:
  //
  //        Calculate the Y and X values for a 1000 values of t (in advance). Put the results into
  // buckets in an array of length N. x is between [0 1] so place all results between M/N and
  // (M + 1)/N in the list in index M in the bucket array. You can later use a (fast) lookup to find
  // the x,y pair you want (by radix/range). Binary search (and Newton Raphson) are you friends when
  // you want to quickly answer complex mathematical questions with the use of a computer. You can
  // still use the binary search to increase precision after the look up by searching between the
  // two closest values.
  //
  //        A quick recap: you store the x,y pairs in linked-lists within an array of buckets by
  // creating lists of x,y pairs where the values of x(s) that are between [m/n (m+1)/n] are all
  // stored in the mth index (i.e. in listarray[m]).
    }
  
    /**
     * Applies the given transform to the given element as a CSS style in a cross-browser compatible
     * manner.
     *
     * @param {HTMLElement} element
     * @param {string} transform
     */
    function applyTransform(element, transform) {
      element.style.webkitTransform = transform;
      element.style.MozTransform = transform;
      element.style.msTransform = transform;
      element.style.OTransform = transform;
      element.style.transform = transform;
    }
  
    // ------------------------------------------------------------------------------------------- //
    // Expose this module
  
    /**
     * Exposes the static util functions.
     *
     * @global
     */
    var util = {
      addClass: addClass,
      removeClass: removeClass,
      setTransitionCubicBezierTimingFunction: setTransitionCubicBezierTimingFunction,
      easingFunctions: easingFunctions,
      inverseEasingFunctions: inverseEasingFunctions,
      removeChildrenWithClass: removeChildrenWithClass,
      requestAnimationFrame: requestAnimationFrame,
      getXYFromPercentWithBezier: getXYFromPercentWithBezier,
      getYFromXWithBezier: getYFromXWithBezier,
      applyTransform: applyTransform
    };
  
    // Expose this module
    if (!window.ta) window.ta = {};
    window.ta.util = util;
  
    console.log('util module loaded');
  })();
  