//polyfill for Object.assign(). Taken from MDN.
if (typeof Object.assign != 'function') {
	Object.assign = function(target) {
		// We must check against these specific cases.
		if (target === undefined || target === null) {
			throw new TypeError('Cannot convert undefined or null to object');
		}
		var output = Object(target);
		for (var index = 1; index<arguments.length; index++) {
			var source = arguments[index];
			if (source !== undefined && source !== null) {
				for (var nextKey in source) {
					if (source.hasOwnProperty(nextKey)) {
						output[nextKey] = source[nextKey];
					}
				}
			}
		}
		return output;
	};
}

//polyfill for CustomEvent(). Taken from MDN.
if (typeof window.CustomEvent !== 'function') {
	CustomEvent = function(event, params) {
		params = params || {
				bubbles: false,
				cancelable: false,
				detail: undefined
			};
		var evt = document.createEvent('CustomEvent');
		evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);
		return evt;
	};

	CustomEvent.prototype = window.Event.prototype;
	window.CustomEvent = CustomEvent;
}

//partial polyfill for .bind(). Taken from MDN.
if (!Function.prototype.bind) {
	Function.prototype.bind = function (oThis) {
		if (typeof this !== 'function') {
			// closest thing possible to the ECMAScript 5
			// internal IsCallable function
			throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
		}
		var aArgs = Array.prototype.slice.call(arguments, 1),
			fToBind = this,
			fNOP = function () {
			},
			fBound = function () {
				return fToBind.apply(this instanceof fNOP ? this : oThis,
					aArgs.concat(Array.prototype.slice.call(arguments)));
			};
		if (this.prototype) {
			// Function.prototype don't have a prototype property
			fNOP.prototype = this.prototype;
		}
		fBound.prototype = new fNOP();
		return fBound;
	};
}

// http://paulirish.com/2011/requestanimationframe-for-smart-animating/
// http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating
// requestAnimationFrame polyfill by Erik Möller. fixes from Paul Irish and Tino Zijdel
// MIT license
(function () {
	var lastTime = 0;
	var vendors = ['ms', 'moz', 'webkit', '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);
		};
	}
}());

if (typeof window.console == 'undefined') {
	window.console = {
		log: function () {
		}
	};
}

if (!String.prototype.trim) {
	String.prototype.trim = function () {
		return this.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '');
	};
}

if (!Array.isArray) {
	Array.isArray = function (arg) {
		return Object.prototype.toString.call(arg) === '[object Array]';
	};
}

if (!Array.prototype.indexOf) {
	Array.prototype.indexOf = function (obj, start) {
		for (var i = (start || 0), j = this.length; i<j; i++) {
			if (this[i] === obj) {
				return i;
			}
		}
		return -1;
	};
}

if (!Event.prototype.preventDefault) {
	Event.prototype.preventDefault = function () {
		this.returnValue = false;
	};
}

if (!Event.prototype.stopPropagation) {
	Event.prototype.stopPropagation = function () {
		this.cancelBubble = true;
	};
}

if (!Element.prototype.addEventListener) {
	var eventListeners = [];
	var addEventListener = function (type, listener /*, useCapture (will be ignored) */) {
		var self = this;
		var wrapper = function (e) {
			e.which = e.keyCode || e.button;
			e.target = e.srcElement;
			e.currentTarget = self;
			if (listener.handleEvent) {
				listener.handleEvent(e);
			}
			else {
				listener.call(self, e);
			}
		};
		if (type == 'DOMContentLoaded') {
			var wrapper2 = function (e) {
				if (document.readyState == 'complete') {
					wrapper(e);
				}
			};
			document.attachEvent('onreadystatechange', wrapper2);
			eventListeners.push({
				object: this,
				type: type,
				listener: listener,
				wrapper: wrapper2
			});
			if (document.readyState == 'complete') {
				var e = new Event();
				e.srcElement = window;
				wrapper2(e);
			}
		}
		else {
			this.attachEvent('on' + type, wrapper);
			eventListeners.push({
				object: this,
				type: type,
				listener: listener,
				wrapper: wrapper
			});
		}
	};
	var removeEventListener = function (type, listener /*, useCapture (will be ignored) */) {
		var counter = 0;
		while (counter<eventListeners.length) {
			var eventListener = eventListeners[counter];
			if (eventListener.object == this && eventListener.type == type && eventListener.listener == listener) {
				if (type == 'DOMContentLoaded') {
					this.detachEvent('onreadystatechange', eventListener.wrapper);
				}
				else {
					this.detachEvent('on' + type, eventListener.wrapper);
				}
				eventListeners.splice(counter, 1);
				break;
			}
			++counter;
		}
	};
	Element.prototype.addEventListener = addEventListener;
	Element.prototype.removeEventListener = removeEventListener;
	if (HTMLDocument) {
		HTMLDocument.prototype.addEventListener = addEventListener;
		HTMLDocument.prototype.removeEventListener = removeEventListener;
	}
	if (Window) {
		Window.prototype.addEventListener = addEventListener;
		Window.prototype.removeEventListener = removeEventListener;
	}
}