(function($) {

	var useWindow = window;

	/* From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys */
	if (!Object.keys) {
		Object.keys = (function() {
			'use strict';
			var hasOwnProperty = Object.prototype.hasOwnProperty,
					hasDontEnumBug = !({toString: null}).propertyIsEnumerable('toString'),
					dontEnums = [
						'toString',
						'toLocaleString',
						'valueOf',
						'hasOwnProperty',
						'isPrototypeOf',
						'propertyIsEnumerable',
						'constructor'
					],
					dontEnumsLength = dontEnums.length;

			return function(obj) {
				if (typeof obj !== 'object' && (typeof obj !== 'function' || obj === null)) {
					throw new TypeError('Object.keys called on non-object');
				}

				var result = [], prop, i;

				for (prop in obj) {
					if (hasOwnProperty.call(obj, prop)) {
						result.push(prop);
					}
				}

				if (hasDontEnumBug) {
					for (i = 0; i < dontEnumsLength; i++) {
						if (hasOwnProperty.call(obj, dontEnums[i])) {
							result.push(dontEnums[i]);
						}
					}
				}
				return result;
			};
		}());
	}

	/* Used to disable some features on IE8 */
	var limited_mode = false;
	var tick_duration = 200; // in ms

	var debug = (location.hash === "#debug");
	function debug_log(msg) {
		if (debug) {
			console.log(msg);
		}
	}

	var allUnits = ["Days", "Hours", "Minutes", "Seconds"];
	var nextUnits = {
		Seconds: "Minutes",
		Minutes: "Hours",
		Hours: "Days",
		Days: "Years"
	};
	var secondsIn = {
		Seconds: 1,
		Minutes: 60,
		Hours: 3600,
		Days: 86400,
		Months: 2678400,
		Years: 31536000
	};

	/**
	 * Converts hex color code into object containing integer values for the r,g,b use
	 * This function (hexToRgb) originates from:
	 * http://stackoverflow.com/questions/5623838/rgb-to-hex-and-hex-to-rgb
	 * @param {string} hex color code
	 */
	function hexToRgb(hex) {
		// Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF")
		var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
		hex = hex.replace(shorthandRegex, function(m, r, g, b) {
			return r + r + g + g + b + b;
		});

		var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
		return result ? {
			r: parseInt(result[1], 16),
			g: parseInt(result[2], 16),
			b: parseInt(result[3], 16)
		} : null;
	}

	function isCanvasSupported() {
		var elem = document.createElement('canvas');
		return !!(elem.getContext && elem.getContext('2d'));
	}

	/**
	 * Function s4() and guid() originate from:
	 * http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript
	 */
	function s4() {
		return Math.floor((1 + Math.random()) * 0x10000)
				.toString(16)
				.substring(1);
	}

	/**
	 * Creates a unique id
	 * @returns {String}
	 */
	function guid() {
		return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
				s4() + '-' + s4() + s4() + s4();
	}

	/**
	 * Array.prototype.indexOf fallback for IE8
	 * @param {Mixed} mixed
	 * @returns {Number}
	 */
	if (!Array.prototype.indexOf) {
		Array.prototype.indexOf = function(elt /*, from*/)
		{
			var len = this.length >>> 0;

			var from = Number(arguments[1]) || 0;
			from = (from < 0)
					? Math.ceil(from)
					: Math.floor(from);
			if (from < 0)
				from += len;

			for (; from < len; from++)
			{
				if (from in this &&
						this[from] === elt)
					return from;
			}
			return -1;
		};
	}

	function parse_date(str) {
		var match = str.match(/^[0-9]{4}-[0-9]{2}-[0-9]{2}\s[0-9]{1,2}:[0-9]{2}:[0-9]{2}$/);
		if (match !== null && match.length > 0) {
			var parts = str.split(" ");
			var date = parts[0].split("-");
			var time = parts[1].split(":");
			return new Date(date[0], date[1] - 1, date[2], time[0], time[1], time[2]);
		}
		/* Fallback for different date formats */
		var d = Date.parse(str);
		if (!isNaN(d))
			return d;
		d = Date.parse(str.replace(/-/g, '/').replace('T', ' '));
		if (!isNaN(d))
			return d;
		/* Cant find anything */
		return new Date();
	}

	function parse_times(diff, old_diff, total_duration, units, floor) {
		var raw_time = {};
		var raw_old_time = {};
		var time = {};
		var pct = {};
		var old_pct = {};
		var old_time = {};

		var greater_unit = null;
		for(var i = 0; i < units.length; i++) {
			var unit = units[i];
			var maxUnits;

			if (greater_unit === null) {
				maxUnits = total_duration / secondsIn[unit];
			}
			else {
				maxUnits = secondsIn[greater_unit] / secondsIn[unit];
			}

			var curUnits = (diff / secondsIn[unit]);
			var oldUnits = (old_diff / secondsIn[unit]);

			if(floor) {
				if(curUnits > 0) curUnits = Math.floor(curUnits);
				else curUnits = Math.ceil(curUnits);
				if(oldUnits > 0) oldUnits = Math.floor(oldUnits);
				else oldUnits = Math.ceil(oldUnits);
			}

			if (unit !== "Days") {
				curUnits = curUnits % maxUnits;
				oldUnits = oldUnits % maxUnits;
			}

			raw_time[unit] = curUnits;
			time[unit] = Math.abs(curUnits);
			raw_old_time[unit] = oldUnits;
			old_time[unit] = Math.abs(oldUnits);
			pct[unit] = Math.abs(curUnits) / maxUnits;
			old_pct[unit] = Math.abs(oldUnits) / maxUnits;

			greater_unit = unit;
		}

		return {
			raw_time: raw_time,
			raw_old_time: raw_old_time,
			time: time,
			old_time: old_time,
			pct: pct,
			old_pct: old_pct
		};
	}

	var TC_Instance_List = {};
	function updateUsedWindow() {
		if(typeof useWindow.TC_Instance_List !== "undefined") {
			TC_Instance_List = useWindow.TC_Instance_List;
		}
		else {
			useWindow.TC_Instance_List = TC_Instance_List;
		}
		initializeAnimationFrameHandler(useWindow);
	};

	function initializeAnimationFrameHandler(w) {
		var vendors = ['webkit', 'moz'];
		for (var x = 0; x < vendors.length && !w.requestAnimationFrame; ++x) {
			w.requestAnimationFrame = w[vendors[x] + 'RequestAnimationFrame'];
			w.cancelAnimationFrame = w[vendors[x] + 'CancelAnimationFrame'];
		}

		if (!w.requestAnimationFrame || !w.cancelAnimationFrame) {
			w.requestAnimationFrame = function(callback, element, instance) {
				if (typeof instance === "undefined")
					instance = {data: {last_frame: 0}};
				var currTime = new Date().getTime();
				var timeToCall = Math.max(0, 16 - (currTime - instance.data.last_frame));
				var id = w.setTimeout(function() {
					callback(currTime + timeToCall);
				}, timeToCall);
				instance.data.last_frame = currTime + timeToCall;
				return id;
			};
			w.cancelAnimationFrame = function(id) {
				clearTimeout(id);
			};
		}
	};

	var TC_Instance = function(element, options) {
		this.element = element;
		this.container;
		this.listeners = null;
		this.data = {
			paused: false,
			last_frame: 0,
			animation_frame: null,
			interval_fallback: null,
			timer: false,
			total_duration: null,
			prev_time: null,
			drawn_units: [],
			text_elements: {
				Days: null,
				Hours: null,
				Minutes: null,
				Seconds: null
			},
			attributes: {
				canvas: null,
				context: null,
				item_size: null,
				line_width: null,
				radius: null,
				outer_radius: null
			},
			state: {
				fading: {
					Days: false,
					Hours: false,
					Minutes: false,
					Seconds: false
				}
			}
		};

		this.config = null;
		this.setOptions(options);
		this.initialize();
	};

	TC_Instance.prototype.clearListeners = function() {
		this.listeners = { all: [], visible: [] };
	};

	TC_Instance.prototype.addTime = function(seconds_to_add) {
		if(this.data.attributes.ref_date instanceof Date) {
			var d = this.data.attributes.ref_date;
			d.setSeconds(d.getSeconds() + seconds_to_add);
		}
		else if(!isNaN(this.data.attributes.ref_date)) {
			this.data.attributes.ref_date += (seconds_to_add * 1000);
		}
	};

	TC_Instance.prototype.initialize = function(clear_listeners) {
		/* Initialize drawn units */
		this.data.drawn_units = [];
		for(var i = 0; i < Object.keys(this.config.time).length; i++) {
			var unit = Object.keys(this.config.time)[i];
			if (this.config.time[unit].show) {
				this.data.drawn_units.push(unit);
			}
		}

		/* Avoid stacking */
		$(this.element).children('div.time_circles').remove();

		if (typeof clear_listeners === "undefined")
			clear_listeners = true;
		if (clear_listeners || this.listeners === null) {
			this.clearListeners();
		}
		this.container = $("<div>");
		this.container.addClass('time_circles');
		this.container.appendTo(this.element);

		/* Determine the needed width and height of TimeCircles */
		var height = this.element.offsetHeight;
		var width = this.element.offsetWidth;
		if (height === 0)
			height = $(this.element).height();
		if (width === 0)
			width = $(this.element).width();

		if (height === 0 && width > 0)
			height = width / this.data.drawn_units.length;
		else if (width === 0 && height > 0)
			width = height * this.data.drawn_units.length;

		/* Create our canvas and set it to the appropriate size */
		var canvasElement = document.createElement('canvas');
		canvasElement.width = width;
		canvasElement.height = height;

		/* Add canvas elements */
		this.data.attributes.canvas = $(canvasElement);
		this.data.attributes.canvas.appendTo(this.container);

		/* Check if the browser has browser support */
		var canvasSupported = isCanvasSupported();
		/* If the browser doesn't have browser support, check if explorer canvas is loaded */
		/* (A javascript library that adds canvas support to browsers that don't have it) */
		if(!canvasSupported && typeof G_vmlCanvasManager !== "undefined") {
			G_vmlCanvasManager.initElement(canvasElement);
			limited_mode = true;
			canvasSupported = true;
		}
		if(canvasSupported) {
			this.data.attributes.context = canvasElement.getContext('2d');
		}

		this.data.attributes.item_size = Math.min(width / this.data.drawn_units.length, height);
		this.data.attributes.line_width = this.data.attributes.item_size * this.config.fg_width;
		this.data.attributes.radius = ((this.data.attributes.item_size * 0.8) - this.data.attributes.line_width) / 2;
		this.data.attributes.outer_radius = this.data.attributes.radius + 0.5 * Math.max(this.data.attributes.line_width, this.data.attributes.line_width * this.config.bg_width);

		/* Prepare Time Elements */
		var i = 0;
		for (var key in this.data.text_elements) {
			if (!this.config.time[key].show)
				continue;

			var textElement = $("<div>");
			textElement.addClass('textDiv_' + key);
			textElement.css("top", Math.round(0.35 * this.data.attributes.item_size));
			textElement.css("left", Math.round(i++ * this.data.attributes.item_size));
			textElement.css("width", this.data.attributes.item_size);
			textElement.appendTo(this.container);

			var headerElement = $("<h4>");
			headerElement.text(this.config.time[key].text); /* Options */
			headerElement.css("font-size", Math.round(this.config.text_size * this.data.attributes.item_size));
			headerElement.css("line-height", Math.round(this.config.text_size * this.data.attributes.item_size) + "px");
			headerElement.appendTo(textElement);

			var numberElement = $("<span>");
			numberElement.css("font-size", Math.round(3 * this.config.text_size * this.data.attributes.item_size));
			numberElement.css("line-height", Math.round(this.config.text_size * this.data.attributes.item_size) + "px");
			numberElement.appendTo(textElement);

			this.data.text_elements[key] = numberElement;
		}

		this.start();
		if (!this.config.start) {
			this.data.paused = true;
		}

		/* Set up interval fallback */
		var _this = this;
		this.data.interval_fallback = useWindow.setInterval(function(){
			_this.update.call(_this, true);
		}, 100);
	};

	TC_Instance.prototype.update = function(nodraw) {
		if(typeof nodraw === "undefined") {
			nodraw = false;
		}
		else if(nodraw && this.data.paused) {
			return;
		}

		if(limited_mode) {
			/* Per unit clearing doesn't work in IE8 using explorer canvas, so do it in one time. The downside is that radial fade cant be used */
			this.data.attributes.context.clearRect(0, 0, this.data.attributes.canvas[0].width, this.data.attributes.canvas[0].hright);
		}
		var diff, old_diff;

		var prevDate = this.data.prev_time;
		var curDate = new Date();
		this.data.prev_time = curDate;

		if (prevDate === null)
			prevDate = curDate;

		/* If not counting past zero, and time < 0, then simply draw the zero point once, and call stop */
		if (!this.config.count_past_zero) {
			if (curDate > this.data.attributes.ref_date) {
				for(var i = 0; i < this.data.drawn_units.length; i++) {
					var key = this.data.drawn_units[i];

					/* Set the text value */
					this.data.text_elements[key].text("0");
					var x = (i * this.data.attributes.item_size) + (this.data.attributes.item_size / 2);
					var y = this.data.attributes.item_size / 2;
					var color = this.config.time[key].color;
					this.drawArc(x, y, color, 0);
				}
				this.stop();
				return;
			}
		}

		/* Compare current time with reference */
		diff = (this.data.attributes.ref_date - curDate) / 1000;
		old_diff = (this.data.attributes.ref_date - prevDate) / 1000;

		var floor = this.config.animation !== "smooth";

		var visible_times = parse_times(diff, old_diff, this.data.total_duration, this.data.drawn_units, floor);
		var all_times = parse_times(diff, old_diff, secondsIn["Years"], allUnits, floor);

		var i = 0;
		var j = 0;
		var lastKey = null;

		var cur_shown = this.data.drawn_units.slice();
		for (var i in allUnits) {
			var key = allUnits[i];

			/* Notify (all) listeners */
			if (Math.floor(all_times.raw_time[key]) !== Math.floor(all_times.raw_old_time[key])) {
				this.notifyListeners(key, Math.floor(all_times.time[key]), Math.floor(diff), "all");
			}

			if (cur_shown.indexOf(key) < 0)
				continue;

			/* Notify (visible) listeners */
			if (Math.floor(visible_times.raw_time[key]) !== Math.floor(visible_times.raw_old_time[key])) {
				this.notifyListeners(key, Math.floor(visible_times.time[key]), Math.floor(diff), "visible");
			}

			if(!nodraw) {
				/* Set the text value */
				this.data.text_elements[key].text(Math.floor(Math.abs(visible_times.time[key])));

				var x = (j * this.data.attributes.item_size) + (this.data.attributes.item_size / 2);
				var y = this.data.attributes.item_size / 2;
				var color = this.config.time[key].color;

				if (this.config.animation === "smooth") {
					if (lastKey !== null && !limited_mode) {
						if (Math.floor(visible_times.time[lastKey]) > Math.floor(visible_times.old_time[lastKey])) {
							this.radialFade(x, y, color, 1, key);
							this.data.state.fading[key] = true;
						}
						else if (Math.floor(visible_times.time[lastKey]) < Math.floor(visible_times.old_time[lastKey])) {
							this.radialFade(x, y, color, 0, key);
							this.data.state.fading[key] = true;
						}
					}
					if (!this.data.state.fading[key]) {
						this.drawArc(x, y, color, visible_times.pct[key]);
					}
				}
				else {
					this.animateArc(x, y, color, visible_times.pct[key], visible_times.old_pct[key], (new Date()).getTime() + tick_duration);
				}
			}
			lastKey = key;
			j++;
		}

		/* Dont request another update if we should be paused */
		if(this.data.paused || nodraw) {
			return;
		}

		/* We need this for our next frame either way */
		var _this = this;
		var update = function() {
			_this.update.call(_this);
		};

		/* Either call next update immediately, or in a second */
		if (this.config.animation === "smooth") {
			/* Smooth animation, Queue up the next frame */
			this.data.animation_frame = useWindow.requestAnimationFrame(update, _this.element, _this);
		}
		else {
			/* Tick animation, Don't queue until very slightly after the next second happens */
			var delay = (diff % 1) * 1000;
			if (delay < 0)
				delay = 1000 + delay;
			delay += 50;

			_this.data.animation_frame = useWindow.setTimeout(function() {
				_this.data.animation_frame = useWindow.requestAnimationFrame(update, _this.element, _this);
			}, delay);
		}
	};

	TC_Instance.prototype.animateArc = function(x, y, color, target_pct, cur_pct, animation_end) {
		if (this.data.attributes.context === null)
			return;

		var diff = cur_pct - target_pct;
		if (Math.abs(diff) > 0.5) {
			if (target_pct === 0) {
				this.radialFade(x, y, color, 1);
			}
			else {
				this.radialFade(x, y, color, 0);
			}
		}
		else {
			var progress = (tick_duration - (animation_end - (new Date()).getTime())) / tick_duration;
			if (progress > 1)
				progress = 1;

			var pct = (cur_pct * (1 - progress)) + (target_pct * progress);
			this.drawArc(x, y, color, pct);

			/*var show_pct = */
			if (progress >= 1)
				return;
			var _this = this;
			useWindow.requestAnimationFrame(function() {
				_this.animateArc(x, y, color, target_pct, cur_pct, animation_end);
			}, this.element);
		}
	};

	TC_Instance.prototype.drawArc = function(x, y, color, pct) {
		if (this.data.attributes.context === null)
			return;

		var clear_radius = Math.max(this.data.attributes.outer_radius, this.data.attributes.item_size / 2);
		if(!limited_mode) {
			this.data.attributes.context.clearRect(
					x - clear_radius,
					y - clear_radius,
					clear_radius * 2,
					clear_radius * 2
					);
		}

		if (this.config.use_background) {
			this.data.attributes.context.beginPath();
			this.data.attributes.context.arc(x, y, this.data.attributes.radius, 0, 2 * Math.PI, false);
			this.data.attributes.context.lineWidth = this.data.attributes.line_width * this.config.bg_width;

			/* line color */
			this.data.attributes.context.strokeStyle = this.config.circle_bg_color;
			this.data.attributes.context.stroke();
		}

		/* Direction */
		var startAngle, endAngle, counterClockwise;
		var defaultOffset = (-0.5 * Math.PI);
		var fullCircle = 2 * Math.PI;
		startAngle = defaultOffset + (this.config.start_angle / 360 * fullCircle);
		var offset = (2 * pct * Math.PI);

		if (this.config.direction === "Both") {
			counterClockwise = false;
			startAngle -= (offset / 2);
			endAngle = startAngle + offset;
		}
		else {
			if (this.config.direction === "Clockwise") {
				counterClockwise = false;
				endAngle = startAngle + offset;
			}
			else {
				counterClockwise = true;
				endAngle = startAngle - offset;
			}
		}

		this.data.attributes.context.beginPath();
		this.data.attributes.context.arc(x, y, this.data.attributes.radius, startAngle, endAngle, counterClockwise);
		this.data.attributes.context.lineWidth = this.data.attributes.line_width;

		/* line color */
		this.data.attributes.context.strokeStyle = color;
		this.data.attributes.context.stroke();
	};

	TC_Instance.prototype.radialFade = function(x, y, color, from, key) {
		/* TODO: Make fade_time option */
		var rgb = hexToRgb(color);
		var _this = this; /* We have a few inner scopes here that will need access to our instance */

		var step = 0.2 * ((from === 1) ? -1 : 1);
		var i;
		for (i = 0; from <= 1 && from >= 0; i++) {
			/* Create inner scope so our variables are not changed by the time the Timeout triggers */
			(function() {
				var delay = 50 * i;
				var rgba = "rgba(" + rgb.r + ", " + rgb.g + ", " + rgb.b + ", " + (Math.round(from * 10) / 10) + ")";
				useWindow.setTimeout(function() {
					_this.drawArc(x, y, rgba, 1);
				}, delay);
			}());
			from += step;
		}
		if (typeof key !== undefined) {
			useWindow.setTimeout(function() {
				_this.data.state.fading[key] = false;
			}, 50 * i);
		}
	};

	TC_Instance.prototype.timeLeft = function() {
		if (this.data.paused && typeof this.data.timer === "number") {
			return this.data.timer;
		}
		var now = new Date();
		return ((this.data.attributes.ref_date - now) / 1000);
	};

	TC_Instance.prototype.start = function() {
		useWindow.cancelAnimationFrame(this.data.animation_frame);
		useWindow.clearTimeout(this.data.animation_frame)

		/* Check if a date was passed in html attribute or jquery data */
		var attr_data_date = $(this.element).data('date');
		if (typeof attr_data_date === "undefined") {
			attr_data_date = $(this.element).attr('data-date');
		}
		if (typeof attr_data_date === "string") {
			this.data.attributes.ref_date = parse_date(attr_data_date);
		}
		/* Check if this is an unpause of a timer */
		else if (typeof this.data.timer === "number") {
			if (this.data.paused) {
				this.data.attributes.ref_date = (new Date()).getTime() + (this.data.timer * 1000);
			}
		}
		else {
			/* Try to get data-timer */
			var attr_data_timer = $(this.element).data('timer');
			if (typeof attr_data_timer === "undefined") {
				attr_data_timer = $(this.element).attr('data-timer');
			}
			if (typeof attr_data_timer === "string") {
				attr_data_timer = parseFloat(attr_data_timer);
			}
			if (typeof attr_data_timer === "number") {
				this.data.timer = attr_data_timer;
				this.data.attributes.ref_date = (new Date()).getTime() + (attr_data_timer * 1000);
			}
			else {
				/* data-timer and data-date were both not set
				use config date */
				this.data.attributes.ref_date = this.config.ref_date;
			}
		}

		/* Start running */
		this.data.paused = false;
		this.update.call(this);
	};

	TC_Instance.prototype.restart = function() {
		this.data.timer = false;
		this.start();
	};

	TC_Instance.prototype.stop = function() {
		if (typeof this.data.timer === "number") {
			this.data.timer = this.timeLeft(this);
		}
		/* Stop running */
		this.data.paused = true;
		useWindow.cancelAnimationFrame(this.data.animation_frame);
	};

	TC_Instance.prototype.destroy = function() {
		this.clearListeners();
		this.stop();
		useWindow.clearInterval(this.data.interval_fallback);
		this.data.interval_fallback = null;

		this.container.remove();
		$(this.element).removeAttr('data-tc-id');
		$(this.element).removeData('tc-id');
	};

	TC_Instance.prototype.setOptions = function(options) {
		if (this.config === null) {
			this.default_options.ref_date = new Date();
			this.config = $.extend(true, {}, this.default_options);
		}
		$.extend(true, this.config, options);

		/* Use window.top if use_top_frame is true */
		if(this.config.use_top_frame) {
			useWindow = window.top;
		}
		else {
			useWindow = window;
		}
		updateUsedWindow();

		this.data.total_duration = this.config.total_duration;
		if (typeof this.data.total_duration === "string") {
			if (typeof secondsIn[this.data.total_duration] !== "undefined") {
				/* If set to Years, Months, Days, Hours or Minutes, fetch the secondsIn value for that */
				this.data.total_duration = secondsIn[this.data.total_duration];
			}
			else if (this.data.total_duration === "Auto") {
				/* If set to auto, total_duration is the size of 1 unit, of the unit type bigger than the largest shown */
				for(var i = 0; i < Object.keys(this.config.time).length; i++) {
					var unit = Object.keys(this.config.time)[i];
					if (this.config.time[unit].show) {
						this.data.total_duration = secondsIn[nextUnits[unit]];
						break;
					}
				}
			}
			else {
				/* If it's a string, but neither of the above, user screwed up. */
				this.data.total_duration = secondsIn["Years"];
				console.error("Valid values for TimeCircles config.total_duration are either numeric, or (string) Years, Months, Days, Hours, Minutes, Auto");
			}
		}
	};

	TC_Instance.prototype.addListener = function(f, context, type) {
		if (typeof f !== "function")
			return;
		if (typeof type === "undefined")
			type = "visible";
		this.listeners[type].push({func: f, scope: context});
	};

	TC_Instance.prototype.notifyListeners = function(unit, value, total, type) {
		for (var i = 0; i < this.listeners[type].length; i++) {
			var listener = this.listeners[type][i];
			listener.func.apply(listener.scope, [unit, value, total]);
		}
	};

	TC_Instance.prototype.default_options = {
		ref_date: new Date(),
		start: true,
		animation: "smooth",
		count_past_zero: true,
		circle_bg_color: "#313332",
		use_background: true,
		fg_width: 0.1,
		bg_width: 1.2,
		text_size: 0.07,
		total_duration: "Auto",
		direction: "Clockwise",
		use_top_frame: false,
		start_angle: 0,
		time: {
			Days: {
				show: true,
				text: "Days",
				color: "#e3be32"
			},
			Hours: {
				show: true,
				text: "Hours",
				color: "#36b0e3"
			},
			Minutes: {
				show: true,
				text: "Minutes",
				color: "#75bf44"
			},
			Seconds: {
				show: true,
				text: "Seconds",
				color: "#66c5af"
			}
		}
	};

	/* Time circle class */
	var TC_Class = function(elements, options) {
		this.elements = elements;
		this.options = options;
		this.foreach();
	};

	TC_Class.prototype.getInstance = function(element) {
		var instance;

		var cur_id = $(element).data("tc-id");
		if (typeof cur_id === "undefined") {
			cur_id = guid();
			$(element).attr("data-tc-id", cur_id);
		}
		if (typeof TC_Instance_List[cur_id] === "undefined") {
			var options = this.options;
			var element_options = $(element).data('options');
			if (typeof element_options === "string") {
				element_options = JSON.parse(element_options);
			}
			if (typeof element_options === "object") {
				options = $.extend(true, {}, this.options, element_options);
			}
			instance = new TC_Instance(element, options);
			TC_Instance_List[cur_id] = instance;
		}
		else {
			instance = TC_Instance_List[cur_id];
			if (typeof this.options !== "undefined") {
				instance.setOptions(this.options);
			}
		}
		return instance;
	};

	TC_Class.prototype.addTime = function(seconds_to_add) {
		this.foreach(function(instance) {
			instance.addTime(seconds_to_add);
		});
	};

	TC_Class.prototype.foreach = function(callback) {
		var _this = this;
		this.elements.each(function() {
			var instance = _this.getInstance(this);
			if (typeof callback === "function") {
				callback(instance);
			}
		});
		return this;
	};

	TC_Class.prototype.start = function() {
		this.foreach(function(instance) {
			instance.start();
		});
		return this;
	};

	TC_Class.prototype.stop = function() {
		this.foreach(function(instance) {
			instance.stop();
		});
		return this;
	};

	TC_Class.prototype.restart = function() {
		this.foreach(function(instance) {
			instance.restart();
		});
		return this;
	};

	TC_Class.prototype.rebuild = function() {
		this.foreach(function(instance) {
			instance.initialize(false);
		});
		return this;
	};

	TC_Class.prototype.getTime = function() {
		return this.getInstance(this.elements[0]).timeLeft();
	};

	TC_Class.prototype.addListener = function(f, type) {
		if (typeof type === "undefined")
			type = "visible";
		var _this = this;
		this.foreach(function(instance) {
			instance.addListener(f, _this.elements, type);
		});
		return this;
	};

	TC_Class.prototype.destroy = function() {
		this.foreach(function(instance) {
			instance.destroy();
		});
		return this;
	};

	TC_Class.prototype.end = function() {
		return this.elements;
	};

	$.fn.TimeCircles = function(options) {
		return new TC_Class(this, options);
	};
}(jQuery));