//MooCanvasShadow, Mootools Canvas Dropshadow
/*
Script: MooCanvasShadow.js
Contains the Canvas class.

Dependencies:
MooTools, <http://mootools.net/>
Element, and its dependencies
Element.Dimensions
MooCanvas, <http://ibolmo.com/projects/moocanvas/>
Canvas,
Paths

Author:
Arian Stolwijk, <http://www.aryweb.nl/>
Enrique Erne, <http://mild.ch/>

License:
MIT License, <http://en.wikipedia.org/wiki/MIT_License>
*/


var mooCanvasShadow = new Class({
	
	/**
	* This function creates a new canvas element of MooCanvas
	* The elements will be placed at the right position, so the 
	* canvas element is right behind the to-shadow element
	* 
	* @param {Object} shadowDiv this div will get a shadow
	* @param {Object} options the options:
	* 		size: The size of the shadow
	* 		radius: the radius of the shadow corners
	* 		opacity: The opacity of the shadow
	* 		color: The shadow color, this should be like #FF9900, 
	* 			or an array with the rgb colors [255,0,255]
	* 		overwrite: if the canvas element already exists, 
	* 			it wil dispose that element and create a new one
	*/	
	initialize: function(shadowDiv, options){
		this.shadowDiv = shadowDiv;
		// Get the coordinates of the element relative to the parent 
		this.position = shadowDiv.getCoordinates(this.shadowDiv.getParent());


		/* @param {Object} color
		* @param {Object} horizontal offset
		* @param {Object} vertical offset
		* @param {Object} blur radius*/
		
		
		this.shadow = this.textShadow(options.shadow); //options.shadow.split(" ");
		this.shadow.blur = this.shadow.blur.toInt() * 3;
		// Set some options
		this.radius = this.shadow.blur.toInt().limit(0, this.position.width/2).limit(0, this.position.height/2); // options.radius;

		
		/*if($(shadowDiv.get('id')) == false){
		this.giveId(shadowDiv);
		}
		
		// Dispose the already existing element, if needed
		if ($type(options.overwrite) == 'boolean' && options.overwrite == true) {
		if ($type($(shadowDiv.get('id') + '_mooShadow')) == 'element') {
		$(shadowDiv.get('id') + '_mooShadow').dispose();
		}
		}*/
		
		// Create a new Canvas object, of MooCanvas
		this.canvas = new Canvas({
			'width': this.position.width + this.radius,
			'height': this.position.height + this.radius
		});
		
		// Create a div and put the canvas element in it to set it at the right position
		this.createCanvasDiv();
		
		// Create the context
		var ctx = this.canvas.getContext("2d");
		
		// Create the shadow color
	//	if(this.shadow.color.test(/#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})/)) this.shadow.color = this.shadow.color.hexToRgb();
		if(!this.shadow.color.test(/rgb/)) this.shadow.color = this.shadow.color.hexToRgb();
	
		this.shadow.color = this.shadow.color.match(/\d+(\.\d+){0,3}/g);
		if(this.shadow.color.length < 4) this.shadow.color.push('0.5');

		
		var color = this.shadow.color[0] + ',' + this.shadow.color[1] + ',' + this.shadow.color[2];	

		// Create the retangles/ the actual shadow
		for (var i = (this.radius); i >= 0; i--) {
			this.roundedRect(ctx,
				i,
				i,
				this.position.width - (i * 2) + this.radius,
				this.position.height - (i * 2) + this.radius,
				this.radius - i
			);
			//ctx.fillStyle = "rgba(" + color + ", " + (opacity / (this.radius - i)) + ")";
			// var lOpacity = (this.shadow.color[3] / (this.radius - i)).limit(0, 1);
		//	var lOpacity = Math.pow((1 / (this.radius - i)), 1.3).limit(0, 1) * this.shadow.color[3];
			
/*dev*/	
			function transition(p){
				//return p; // Linear
				//return Math.pow(p, 2); // Pow
				//return Math.pow(2, 6 * (p - 1)); // Expo
				return 1 - Math.sin((1 - p) * Math.PI / 2.1); // Sine should be / 2
			}
			var lOpacity = transition(i / this.radius) * this.shadow.color[3];
		

			ctx.fillStyle = "rgba(" + color + ", " + lOpacity + ")";
			ctx.fill();
		}
	
		window.addEvent('resize', function(){
			//	if(this.shadowDiv.getLeft() != this.shadowDiv.getParent().getLeft()) console.log('a');
		});
	
	},
	
	/**
	* This function creates a div where the canvas element is in inserted
	* This element will position behind the to-shadow div
	*/
	createCanvasDiv: function(){
		var zIndex = this.shadowDiv.getComputedStyle('z-index');
		if(zIndex === 'auto') zIndex = 0;
		var porps = {
			'width': this.position.width + this.radius,
			'height': this.position.height + this.radius,
			'z-index': zIndex - 1,
			'position': 'absolute',
			'left': this.position.left + this.shadow.offX.toInt() - (this.radius * 0.5),
			'top':  this.position.top + this.shadow.offY.toInt() - (this.radius * 0.5)
		}
		this.canvasDiv = new Element('div', {
			styles: porps
		}).adopt(this.canvas).inject(this.shadowDiv.getParent());
	},
	
	/**
	* This method creates a rounded rectangle
	* @param {Object} ctx the canvas context
	* @param {Object} x the upper left x-axis position
	* @param {Object} y the upper left y-axis positoin
	* @param {Object} width the retangle width
	* @param {Object} height the retangle height
	* @param {Object} radius the corner radius
	*/
	roundedRect: function(ctx, x, y, width, height, radius){
		ctx.beginPath();
		ctx.moveTo(x, y + radius);
		ctx.lineTo(x, y + height - radius);
		ctx.quadraticCurveTo(x, y + height, x + radius, y + height);
		ctx.lineTo(x + width - radius, y + height);
		ctx.quadraticCurveTo(x + width, y + height, x + width, y + height - radius);
		ctx.lineTo(x + width, y + radius);
		ctx.quadraticCurveTo(x + width, y, x + width - radius, y);
		ctx.lineTo(x + radius, y);
		ctx.quadraticCurveTo(x, y, x, y + radius);
	},
	
	/*function cached(fun) {
		var cache = {};
		return function(key) {
			if (!hasOwnProperty(cache, key)) cache[key] = fun.apply(null, arguments);
			return cache[key];
		};
	},*/
	
	
	/* Copyright (c) 2009 Simo Kinnunen. Licensed under the MIT license */
	textShadow: function(value) {
		if (value == 'none') return null;
		var shadows = [], currentShadow = {}, result, offCount = 0;
		var re = /(#[a-f0-9]+|[a-z]+\(.*?\)|[a-z]+)|(-?[\d.]+[a-z%]*)|,/ig;
		while (result = re.exec(value)) {
			if (result[0] == ',') {
				shadows.push(currentShadow);
				currentShadow = {};
				offCount = 0;
			} else if (result[1]) {
				currentShadow.color = result[1];
			} else {
				currentShadow[['offX', 'offY', 'blur'][offCount++]] = result[2];
			}
		}
		//shadows.push(currentShadow);
		return currentShadow;
	}

});

/**
* 
* @param {Object} options: the same options as the mooCanvasShadow() class
*/
Element.implement({
	mooCanvasShadow: function (options){
		new mooCanvasShadow(this,options);		
		return this;
	}
});

