

(function(){





var tables		= {},
	all			= {},
	byColorId	= {},
	width		= 16,
	radius		= 3,
	length		= (width / 2) - radius,
	steps		= 12,
	full		= 2 * Math.PI,
	rotationAngle		= full / 16,
	rotationInterval	= 160,
	step		= full / steps,
	half		= width / 2,
	cnt			= 0,
	defaultHighColor	= '000000',
	defaultLowColor		= '333333';






var hexdec 	= function(hex) {
	return parseInt(hex, 16);
};

var hexrgb	= function(hex) {
	return {
		r: hexdec(hex.substr(0, 2)),
		g: hexdec(hex.substr(2, 2)),
		b: hexdec(hex.substr(4, 2))
	};
};

var dechex = function(val) {
	val	= parseInt(val, 10).toString(16);
	if (val.length == 1) val = '0'+val;
	return val;
};

var colorTable	= function(highColor, lowColor) {

	var colorSteps	= steps - 2,
		colorStep	= 100 / colorSteps,
		colors		= [lowColor],
		rgb			= hexrgb(highColor),
		curRgb		= hexrgb(lowColor),
		r			= curRgb.r,
		g			= curRgb.g,
		b			= curRgb.b,
		rStep		= (( rgb.r - r ) / 100) * colorStep,
		gStep		= (( rgb.g - g ) / 100) * colorStep,
		bStep		= (( rgb.b - b ) / 100) * colorStep;

	for (var i = 0; i < colorSteps; i++) {

		r += rStep;
		g += gStep;
		b += bStep;

		colors.push(dechex(r)+dechex(g)+dechex(b));
	}

	colors.push(highColor);

	return colors;
};




var draw = function(ctx, colors) {

	ctx.clearRect(-half,-half,width,width);

	for (var i = 0; i < steps; i++) {

		var x1		= radius * Math.cos(step * i),
			y1		= radius * Math.sin(step * i),
			x2		= (radius + length) * Math.cos(step * i),
			y2		= (radius + length) * Math.sin(step * i);

		ctx.beginPath();
		ctx.strokeStyle	= "#"+colors[i];
		ctx.moveTo(x1, y1);
		ctx.lineTo(x2, y2);
		ctx.stroke();
		ctx.closePath();
	}
};




var loadingIndicator	= function(highColor, lowColor) {

	var dom			= document.createElement('canvas'),
		colorId		= highColor+lowColor;

	if (!dom.getContext) {
		return;
	}

	this.colorId	= colorId;
	this.id			= 'i'+(cnt++);
	this.dom		= dom;
	this.canvas		= $(dom);
	this.ctx		= dom.getContext('2d');
	this.itv		= null;
	this.colors		= tables[colorId] ? tables[colorId] : (tables[colorId] = colorTable(highColor, lowColor));

	this.rotateDelegate	= (function(fn,scope) {
		return function() {
			fn.apply(scope, arguments);
		};
	}(this.rotate, this));

	dom.width 		= width;
	dom.height 		= width;
	this.ctx.lineWidth 	= 2;
	this.ctx.translate(width/2, width/2);

	all[this.id] = this;

	if (!byColorId[colorId]) {
		byColorId[colorId] = {
			length:	0
		};
	}

	byColorId[colorId][this.id] = this;
	byColorId[colorId].length	+= 1;

	dom.loadingIndicator = this;
};

loadingIndicator.prototype = {

	owned:			false,

	destroy: 		function() {
		this.dom.loadingIndicator	= null;
		this.stop();
		this.rotateDelegate = null;
		this.canvas 		= null;
		this.dom 			= null;
		this.ctx 			= null;
		this.colors			= null;
	},

	moveTo:			function(parentNode) {
		if (!this.id)
			return;
		this.stop();
		parentNode.appendChild(this.dom);
		this.start();
	},

	rotate: 		function() {
		if (!this.id)
			return;
		this.ctx.rotate(rotationAngle);
		draw(this.ctx, this.colors);
	},

	setOwned:		function(state) {
		if (!this.id)
			return;
		this.owned	= state;
	},

	start: 			function() {
		if (!this.id)
			return;
		if (!this.itv) {
			this.itv 	= setInterval(this.rotateDelegate, rotationInterval);
		}
	},

	stop:			function() {
		if (!this.id)
			return;

		var itv	= this.itv;

		if (itv === null)
			return;

		clearInterval(itv);
		this.itv = null;

		var dom	= this.dom;
		if (dom.parentNode) {
			dom.parentNode.removeChild(dom);
		}

		this.ctx.clearRect(-half,-half,width,width);
		setTimeout(window.loadingIndicator.removeUnused, 50);

	},

	busy: 		function() {
		return this.itv !== null && !this.owned;
	}
};

window.loadingIndicator = {

	get: function(highColor, lowColor) {

		highColor 	= highColor || defaultHighColor;
		lowColor 	= lowColor || defaultLowColor;

		var colorId	= highColor + lowColor,
			i,
			coll;

		if (byColorId[colorId]) {

			coll = byColorId[colorId];

			for (i in coll) {

				if (i == 'length')
					continue;

				if (!coll[i].busy()) {
					return coll[i];
				}
			}
		}

		return new loadingIndicator(highColor, lowColor);
	},

	removeUnused: function(colorId) {

		var coll	= byColorId[colorId],
			free	= 0,
			id;

		for (id in coll) {

			if (id == 'length')
				continue;

			if (!coll[id].busy()) {

				free++;

				if (free > 1) {
					window.loadingIndicator.destroy(coll[id]);
				}
			}
		}
	},

	destroy: function(indicator) {

		if (indicator instanceof loadingIndicator) {

			var id 		= indicator.id,
				colorId	= indicator.colorId;

			indicator.destroy();
			indicator 		= null;

			all[id] 	= null;
			delete all[id];

			byColorId[colorId][id] = null;
			delete byColorId[colorId][id];

			byColorId[colorId].length--;
		}
	}
};

})();




