
(function(root, factory) {
	if (typeof define === 'function' && define.amd) {
		define(factory);
	} else if (typeof exports === 'object') {
		module.exports = factory(require, exports, module);
	} else {
		root.CountUp = factory();
	}
}(this, function(require, exports, module) {

	/*

	 countUp.js
	 by @inorganik

	 */

// target = id of html element or var of previously selected html element where counting occurs
// startVal = the value you want to begin at
// endVal = the value you want to arrive at
// decimals = number of decimal places, default 0
// duration = duration of animation in seconds, default 2
// options = optional object of options (see below)

	var CountUp = function(target, startVal, endVal, decimals, duration, options) {

		// make sure requestAnimationFrame and cancelAnimationFrame are defined
		// polyfill for browsers without native support
		// by Opera engineer Erik Möller
		var lastTime = 0;
		var vendors = ['webkit', 'moz', 'ms', 'o'];
		for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
			window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];
			window.cancelAnimationFrame =
				window[vendors[x]+'CancelAnimationFrame'] || window[vendors[x]+'CancelRequestAnimationFrame'];
		}
		if (!window.requestAnimationFrame) {
			window.requestAnimationFrame = function(callback, element) {
				var currTime = new Date().getTime();
				var timeToCall = Math.max(0, 16 - (currTime - lastTime));
				var id = window.setTimeout(function() { callback(currTime + timeToCall); },
					timeToCall);
				lastTime = currTime + timeToCall;
				return id;
			};
		}
		if (!window.cancelAnimationFrame) {
			window.cancelAnimationFrame = function(id) {
				clearTimeout(id);
			};
		}

		var self = this;
		self.version = function () { return '1.8.5'; };

		function formatNumber(num) {
			num = num.toFixed(self.decimals);
			num += '';
			var x, x1, x2, rgx;
			x = num.split('.');
			x1 = x[0];
			x2 = x.length > 1 ? self.options.decimal + x[1] : '';
			rgx = /(\d+)(\d{3})/;
			if (self.options.useGrouping) {
				while (rgx.test(x1)) {
					x1 = x1.replace(rgx, '$1' + self.options.separator + '$2');
				}
			}
			return self.options.prefix + x1 + x2 + self.options.suffix;
		}
		// Robert Penner's easeOutExpo
		function easeOutExpo(t, b, c, d) {
			return c * (-Math.pow(2, -10 * t / d) + 1) * 1024 / 1023 + b;
		}
		function ensureNumber(n) {
			return (typeof n === 'number' && !isNaN(n));
		}

		// default options
		self.options = {
			useEasing: true, // toggle easing
			useGrouping: true, // 1,000,000 vs 1000000
			separator: ',', // character to use as a separator
			decimal: '.', // character to use as a decimal
			easingFn: easeOutExpo, // optional custom easing function, default is Robert Penner's easeOutExpo
			formattingFn: formatNumber, // optional custom formatting function, default is formatNumber above
			prefix: '', // optional text before the result
			suffix: '' // optional text after the result
		};

		// extend default options with passed options object
		if (options && typeof options === 'object') {
			for (var key in self.options) {
				if (options.hasOwnProperty(key) && options[key] !== null) {
					self.options[key] = options[key];
				}
			}
		}

		if (self.options.separator === '') self.options.useGrouping = false;

		self.initialize = function() {
			if (self.initialized) return true;
			self.d = (typeof target === 'string') ? document.getElementById(target) : target;
			if (!self.d) {
				console.error('[CountUp] target is null or undefined', self.d);
				return false;
			}
			self.startVal = Number(startVal);
			self.endVal = Number(endVal);
			// error checks
			if (ensureNumber(self.startVal) && ensureNumber(self.endVal)) {
				self.decimals = Math.max(0, decimals || 0);
				self.dec = Math.pow(10, self.decimals);
				self.duration = Number(duration) * 1000 || 2000;
				self.countDown = (self.startVal > self.endVal);
				self.frameVal = self.startVal;
				self.initialized = true;
				return true;
			}
			else {
				console.error('[CountUp] startVal or endVal is not a number', self.startVal, self.endVal);
				return false;
			}
		};

		// Print value to target
		self.printValue = function(value) {
			var result = self.options.formattingFn(value);

			if (self.d.tagName === 'INPUT') {
				this.d.value = result;
			}
			else if (self.d.tagName === 'text' || self.d.tagName === 'tspan') {
				this.d.textContent = result;
			}
			else {
				this.d.innerHTML = result;
			}
		};

		self.count = function(timestamp) {

			if (!self.startTime) { self.startTime = timestamp; }

			self.timestamp = timestamp;
			var progress = timestamp - self.startTime;
			self.remaining = self.duration - progress;

			// to ease or not to ease
			if (self.options.useEasing) {
				if (self.countDown) {
					self.frameVal = self.startVal - self.options.easingFn(progress, 0, self.startVal - self.endVal, self.duration);
				} else {
					self.frameVal = self.options.easingFn(progress, self.startVal, self.endVal - self.startVal, self.duration);
				}
			} else {
				if (self.countDown) {
					self.frameVal = self.startVal - ((self.startVal - self.endVal) * (progress / self.duration));
				} else {
					self.frameVal = self.startVal + (self.endVal - self.startVal) * (progress / self.duration);
				}
			}

			// don't go past endVal since progress can exceed duration in the last frame
			if (self.countDown) {
				self.frameVal = (self.frameVal < self.endVal) ? self.endVal : self.frameVal;
			} else {
				self.frameVal = (self.frameVal > self.endVal) ? self.endVal : self.frameVal;
			}

			// decimal
			self.frameVal = Math.round(self.frameVal*self.dec)/self.dec;

			// format and print value
			self.printValue(self.frameVal);

			// whether to continue
			if (progress < self.duration) {
				self.rAF = requestAnimationFrame(self.count);
			} else {
				if (self.callback) self.callback();
			}
		};
		// start your animation
		self.start = function(callback) {
			if (!self.initialize()) return;
			self.callback = callback;
			self.rAF = requestAnimationFrame(self.count);
		};
		// toggles pause/resume animation
		self.pauseResume = function() {
			if (!self.paused) {
				self.paused = true;
				cancelAnimationFrame(self.rAF);
			} else {
				self.paused = false;
				delete self.startTime;
				self.duration = self.remaining;
				self.startVal = self.frameVal;
				requestAnimationFrame(self.count);
			}
		};
		// reset to startVal so animation can be run again
		self.reset = function() {
			self.paused = false;
			delete self.startTime;
			self.initialized = false;
			if (self.initialize()) {
				cancelAnimationFrame(self.rAF);
				self.printValue(self.startVal);
			}
		};
		// pass a new endVal and start animation
		self.update = function (newEndVal) {
			if (!self.initialize()) return;
			newEndVal = Number(newEndVal);
			if (!ensureNumber(newEndVal)) {
				console.error('[CountUp] update() - new endVal is not a number', newEndVal);
				return;
			}
			if (newEndVal === self.frameVal) return;
			cancelAnimationFrame(self.rAF);
			self.paused = false;
			delete self.startTime;
			self.startVal = self.frameVal;
			self.endVal = newEndVal;
			self.countDown = (self.startVal > self.endVal);
			self.rAF = requestAnimationFrame(self.count);
		};

		// format startVal on initialization
		if (self.initialize()) self.printValue(self.startVal);
	};

	return CountUp;

}));

(function($) {

	$.fn.countup = function(params) {
		// make sure dependency is present
		if (typeof CountUp !== 'function') {
			console.error('countUp.js is a required dependency of countUp-jquery.js.');
			return;
		}

		var defaults = {
			startVal: 0,
			decimals: 0,
			duration: 2
		};

		if (typeof params === 'number') {
			defaults.endVal = params;
		}
		else if (typeof params === 'object') {
			$.extend(defaults, params);
		}
		else {
			console.error('countUp-jquery requires its argument to be either an object or number');
			return;
		}

		this.each(function(i, elem) {
			var countUp = new CountUp(elem, defaults.startVal, defaults.endVal, defaults.decimals, defaults.duration, defaults.options);
			countUp.start();
		});

		return this;

	};

}(jQuery));