'use strict';

(function () {

  function setupRequestAnimationFrame () {
    var lastTime = 0;
    var vendors = ['webkit', 'moz', 'ms', 'o'];

    for (var i = 0; !window.requestAnimationFrame && i < vendors.length; i += 1) {
      var vendor = vendors[i];
      window.requestAnimationFrame = window[vendor + 'RequestAnimationFrame'];
      window.cancelAnimationFrame = window[vendor + 'CancelAnimationFrame'] ||
        window[vendor + 'CancelRequestAnimationFrame'];
    }

    if (!window.requestAnimationFrame) {
      window.requestAnimationFrame = function (callback) {
        var now = new Date().getTime();
        var nextTime = Math.max(0, 16 - (now - lastTime));

        var id = window.setTimeout(function () {
          callback(now + nextTime);
        }, nextTime);

        lastTime = now + nextTime;
        return id;
      };
    }

    if (!window.cancelAnimationFrame) {
      window.cancelAnimationFrame = function (id) {
        clearTimeout(id);
      };
    }
  }

  setupRequestAnimationFrame();

  function setupPerformanceNow () {
    if (!('performance' in window)) {
      window.performance = {};
    }

    if (!('now' in window.performance)) {
      var dateNow = Date.now || function () {
        return new Date().getTime();
      };

      var nowOffset = dateNow();

      if (performance.timing && performance.timing.navigationStart) {
        nowOffset = performance.timing.navigationStart;
      }

      window.performance.now = function now () {
        return dateNow() - nowOffset;
      };
    }
  }

  setupPerformanceNow();

  function getSlik (Immutable) {

    function multiply (fromValue, toValue, multiplier) {
      // NOTE: Below line removed to allow spring / bounce easing
      // multiplier = Math.min(Math.max(multiplier, 0), 1);
      return fromValue * multiplier + toValue * (1 - multiplier);
    }

    function Linear (fromValue, toValue, progress) {
      var multiplier = progress;
      return multiply(fromValue, toValue, multiplier);
    }

    function Ease (fromValue, toValue, progress) {
      var multiplier = (1 - Math.cos(progress * Math.PI)) / 2;
      return multiply(fromValue, toValue, multiplier);
    }

    function EaseIn (fromValue, toValue, progress) {
      var multiplier = (1 - Math.cos(progress * Math.PI / 2));
      return multiply(fromValue, toValue, multiplier);
    }

    function EaseOut (fromValue, toValue, progress) {
      var multiplier = Math.cos((1 - progress) * Math.PI / 2);
      return multiply(fromValue, toValue, multiplier);
    }

    function Animation (initial) {
      var self = this;

      var raf, startTime, pausedAfter;
      var fromValues = Immutable.fromJS(initial);
      var currentValues = fromValues;
      var toValues = Immutable.Map();
      var durationMillis = 500;
      var frameRate = 1000 / 30;
      var easing = Linear;
      var shouldLoop = false;

      var events = {
        all: [],
        start: [],
        stop: [],
        pause: [],
        end: [],
        update: [],
        loop: []
      };

      function easeValues () {
        cancelAnimationFrame(raf);

        var now = performance.now();
        var progress = (now - startTime) / durationMillis;

        function mapValues (value, key) {
          if (Immutable.Iterable.isIterable(value)) {
            return value.map(mapValues);
          }

          return easing(value, toValues.get(key), progress);
        }

        currentValues = currentValues.map(mapValues);

        raf = requestAnimationFrame(easeValues);
      }

      // Set the frameRate
      function fps (input) {
        frameRate = 1000 / input;
        return self;
      }

      // Set from values
      function from (input) {
        fromValues = Immutable.fromJS(input);
        return self;
      }

      // Set to values
      function to (input) {
        toValues = Immutable.fromJS(input);
        return self;
      }

      // Set animation duration
      function duration (input) {
        durationMillis = input;
        return self;
      }

      // Set easing
      function ease (input) {
        if (typeof input !== 'function') {
          throw new Error('Ease must be a function, instead got: ' + (typeof input));
        }

        easing = input;
        return self;
      }

      // Set whether the animation should loop
      function loop (input) {
        shouldLoop = input;
        return self;
      }

      // Invert animation values
      function invert () {
        return self;
      }

      // Play animation in reverse
      function reverse () {
        var temp = fromValues;
        fromValues = toValues;
        toValues = temp;
        return self;
      }

      // Start or resume animation
      function start () {
        cancelAnimationFrame(raf);

        if (typeof pausedAfter !== 'undefined') {
          startTime = performance.now() - pausedAfter;
        } else {
          startTime = performance.now();
        }

        pausedAfter = undefined;

        raf = requestAnimationFrame(easeValues);

        return self;
      }

      // Stop animation and resume from beginning
      function stop () {
        cancelAnimationFrame(raf);
        pausedAfter = undefined;
        startTime = undefined;
        return self;
      }

      // Pause animation and resume from this point
      function pause () {
        cancelAnimationFrame(raf);
        pausedAfter = startTime - performance.now();
        startTime = undefined;
        return self;
      }

      // Manually start the animation / step forward in time
      function update () {
        return self;
      }

      // Add event listener
      function bind (type, callback) {
        if (!(type in events)) {
          throw new Error('Unknown event: ' + type);
        }

        if (typeof callback !== 'function') {
          throw new Error('Callback must be a function, instead got: ' + (typeof callback));
        }

        var index = events[type].indexOf(callback);

        if (index < 0) {
          events[type].push(callback);
        }

        return self;
      }

      // Remove event listener
      function unbind (type, callback) {
        if (!(type in events)) {
          throw new Error('Unknown event: ' + type);
        }

        if (typeof callback !== 'function') {
          throw new Error('Callback must be a function, instead got: ' + (typeof callback));
        }

        var index = events[type].indexOf(callback);

        if (index >= 0) {
          events[type].splice(index, 1);
        }

        return self;
      }

      // Run on complete and automatically unbind
      function then (callback) {
        if (typeof callback !== 'function') {
          throw new Error('Callback must be a function, instead got: ' + (typeof callback));
        }

        function thenCallback () {
          callback();
          unbind('end', thenCallback);
        }

        bind('end', thenCallback);

        return self;
      }

      self.fps = self.frameRate = fps;
      self.from = from;
      self.to = to;
      self.duration = duration;
      self.ease = ease;
      self.loop = loop;
      self.invert = invert;
      self.reverse = reverse;
      self.start = start;
      self.stop = self.reset = stop;
      self.pause = pause;
      self.update = update;
      self.bind = self.on = bind;
      self.unbind = self.off = unbind;
      self.then = then;
    }

    return {
      Animation: Animation,
      Easing: {
        Linear: Linear,
        Ease: Ease,
        EaseIn: EaseIn,
        EaseOut: EaseOut
      }
    };

  }

  // Export for commonjs / browserify
  if (typeof exports === 'object' && typeof module !== 'undefined') {
    module.exports = getSlik(require('immutable')); // eslint-disable-line no-undef
  // Export for amd / require
  } else if (typeof define === 'function' && define.amd) { // eslint-disable-line no-undef
    define(['immutable'], function (Immutable) { // eslint-disable-line no-undef
      return getSlik(Immutable);
    });
  // Export globally
  } else {
    var root;

    if (typeof window !== 'undefined') {
      root = window;
    } else if (typeof global !== 'undefined') {
      root = global; // eslint-disable-line no-undef
    } else if (typeof self !== 'undefined') {
      root = self; // eslint-disable-line no-undef
    } else {
      root = this;
    }

    root.Slik = getSlik(root.Immutable);
  }

})();

@@ -5519,126 +5519,8 @@
  %7D%0A%0A
-      // Manually start the animation / step forward in time%0A      function update () %7B%0A        return self;%0A      %7D%0A%0A
     
@@ -7187,36 +7187,8 @@
 se;%0A
-      self.update = update;%0A
     

Remove manual update