/// <reference path="jquery.intellisense.js"/>  
/**
 * Cobalt Elements for jQuery
 * color - this manages the color picker.
 *
 * These methods are build on the jQuery and interface foundation to provider
 * client functionality to the Cobalt CMS system.
 *
 * Copyright (c) 2007 Scorpion Design, Inc.
 *
 */
(function($) {

	// Create the cobalt namespace if it has not already been done.
	$.cobalt = $.cobalt || {};
	$.cobalt.color = {};

	// Assign a global shortcut to this class.
	if (!window.$cp) { window.$cp = $.cobalt.color; }

	// Initialize these static properties/methods.
	$.extend($.cobalt.color, {

		html : '\
<div id="cp_main" style="position:absolute;display:none;">\
	<div id="cp_color">\
		<div id="cp_colorind"></div>\
	</div>\
	<div id="cp_hue">\
		<div id="cp_hueind"></div>\
	</div>\
	<div id="cp_alpha">\
		<div id="cp_alphaind"></div>\
	</div>\
	<div id="cp_selected">\
		<div id="cp_current"></div>\
		<div id="cp_old"></div>\
	</div>\
	<div id="cp_values">\
		<table cellpadding=0 cellspacing=2>\
			<tr>\
				<td>R:</td>\
				<td><input type=text size=2 id="cp_rgbR" value="255"></td>\
			</tr>\
			<tr>\
				<td>G:</td>\
				<td><input type=text size=2 id="cp_rgbG" value="255"></td>\
			</tr>\
			<tr>\
				<td>B:</td>\
				<td><input type=text size=2 id="cp_rgbB" value="255"></td>\
			</tr>\
			<tr id="cp_alphavalue">\
				<td>A:</td>\
				<td><input type=text size=2 id="cp_rgbA" value="255"></td>\
			</tr>\
			<tr>\
				<td>#</td>\
				<td><input type=text size=5 id="cp_hex" value="FFFFFF"></td>\
			</tr>\
			<tr>\
				<td colspan=2><input id="cp_save" type="button" value="Save"></td>\
			</tr>\
		</table>\
	</div>\
</div>',
		main			: null,
		color			: null,
		colorInd		: null,
		hue				: null,
		hueInd			: null,
		alpha			: null,
		alphaInd		: null,
		alphaValue		: null,
		curColor		: null,
		oldColor		: null,
		rBox			: null,
		gBox			: null,
		bBox			: null,
		aBox			: null,
		hBox			: null,
		baseColor		: {r:255,g:0,b:0},
		currentColor	: {r:255,g:255,b:255,a:255},
		lastPosition	: [0,0],

		// Properties.
		element : null,
		useAlpha : false,
		isover : false,
		onsave : null,

		// Slider range.
		colorX : 0,
		colorY : 0,
		hueY : 0,
		alphaY : 0,

		// Activate the color picker
		activate : function(el,o)
			{
				// Initialize the color picker.
				o = $.extend({},o||{});
				$cp.options = o;
				$cp.init();
				el = $cp.element = el ? (el.jquery ? el : $(el)) : null;
				$cp.onsave = o.onsave;
				$cp.oncancel = o.oncancel;
				$cp.onclose = o.onclose;

				// Show or hide the alpha components
				$cp.useAlpha = (o.alpha==true);
				if ($cp.useAlpha)
				{
					$cp.alpha.show();
					$cp.alphaValue.show();
				}
				else
				{
					$cp.alpha.hide();
					$cp.alphaValue.hide();
				}

				// Get the color.
				var color = o.color || el && el.val();
				$cp.currentColor = $cp.cleanColor(color);

				// Show the color picker.
				$cp.main.relativePos(el,o.pos||{left:true,top:false});

				// Set the slider ranges (now that the color picker is visible).
				$cp.colorX = $cp.color[0].offsetWidth - $cp.colorInd[0].offsetWidth;
				$cp.colorY = $cp.color[0].offsetHeight - $cp.colorInd[0].offsetHeight;
				$cp.hueY = $cp.hue[0].offsetHeight - $cp.hueInd[0].offsetHeight;
				$cp.alphaY = $cp.alpha[0].offsetHeight - $cp.alphaInd[0].offsetHeight;

				// Set the indicator positions to the current color.
				$cp.calculateColor();
				var c = $cp.currentColor;
				$cp.oldColor[0].style.backgroundColor = 'rgb('+c.r+','+c.g+','+c.b+')';
				$cp.oldColor.css({opacity:c.a/255});

				// When someone hits escape or clicks elsewhere on the page, cancel the color picker.
				$(document).escape($cp.cancel);
				$(document).bind('mousedown',$cp.checkHide);

				// The color picker is now open.
				$cp.isopen = true;
			},

		// Parse a supplied color string into a four-part color representation.
		cleanColor : function(color)
			{
				if (!color)
					return $cp.namedColors.black;
				else
				{
					color = color.replace('#','');
					if (/[0-9A-F]{6,8}/i.test(color))
					{
						var r = color.substring(0,2);
						var g = color.substring(2,4);
						var b = color.substring(4,6);
						var a = color.length<8 ? 'ff' : color.substring(6,8);
						return {r:parseInt(r,16),g:parseInt(g,16),b:parseInt(b,16),a:parseInt(a,16)};
					}
					else
						return $cp.namedColors[color.toLowerCase()] || $cp.namedColors.black;
				}
			},

		// Format the color as a string.
		toHex : function(c,alpha)
			{
				if (!c) return alpha ? "000000FF" : "000000";
				try
				{
					var hex = 
						(c.r<16?'0':'')+c.r.toString(16)+
						(c.g<16?'0':'')+c.g.toString(16)+
						(c.b<16?'0':'')+c.b.toString(16)+ 
						(alpha ? (c.a<16?'0':'')+c.a.toString(16) : '');
					return hex.toUpperCase();
				}
				catch(ex)
				{
					return alpha ? "000000FF" : "000000";
				}
			},

		// A list of named colors.
		namedColors :
			{
				aqua:{r:0,g:255,b:255,a:255},
				azure:{r:240,g:255,b:255,a:255},
				beige:{r:245,g:245,b:220,a:255},
				black:{r:0,g:0,b:0,a:255},
				blue:{r:0,g:0,b:255,a:255},
				brown:{r:165,g:42,b:42,a:255},
				cyan:{r:0,g:255,b:255,a:255},
				darkblue:{r:0,g:0,b:139,a:255},
				darkcyan:{r:0,g:139,b:139,a:255},
				darkgrey:{r:169,g:169,b:169,a:255},
				darkgreen:{r:0,g:100,b:0,a:255},
				darkkhaki:{r:189,g:183,b:107,a:255},
				darkmagenta:{r:139,g:0,b:139,a:255},
				darkolivegreen:{r:85,g:107,b:47,a:255},
				darkorange:{r:255,g:140,b:0,a:255},
				darkorchid:{r:153,g:50,b:204,a:255},
				darkred:{r:139,g:0,b:0,a:255},
				darksalmon:{r:233,g:150,b:122,a:255},
				darkviolet:{r:148,g:0,b:211,a:255},
				fuchsia:{r:255,g:0,b:255,a:255},
				gold:{r:255,g:215,b:0,a:255},
				green:{r:0,g:128,b:0,a:255},
				indigo:{r:75,g:0,b:130,a:255},
				khaki:{r:240,g:230,b:140,a:255},
				lightblue:{r:173,g:216,b:230,a:255},
				lightcyan:{r:224,g:255,b:255,a:255},
				lightgreen:{r:144,g:238,b:144,a:255},
				lightgrey:{r:211,g:211,b:211,a:255},
				lightpink:{r:255,g:182,b:193,a:255},
				lightyellow:{r:255,g:255,b:224,a:255},
				lime:{r:0,g:255,b:0,a:255},
				magenta:{r:255,g:0,b:255,a:255},
				maroon:{r:128,g:0,b:0,a:255},
				navy:{r:0,g:0,b:128,a:255},
				olive:{r:128,g:128,b:0,a:255},
				orange:{r:255,g:165,b:0,a:255},
				pink:{r:255,g:192,b:203,a:255},
				purple:{r:128,g:0,b:128,a:255},
				red:{r:255,g:0,b:0,a:255},
				silver:{r:192,g:192,b:192,a:255},
				white:{r:255,g:255,b:255,a:255},
				yellow:{r:255,g:255,b:0,a:255}
			},

		// Adjust the hue/color/alpha sliders according to the current color in the system.
		calculateColor : function()
			{
				var c = $cp.currentColor;
				var hsv = $cp.RGBtoHSV(c);
				var b = $cp.HSVtoRGB({h:hsv.h,s:1,v:1});

				// Move the hue indicator, then set the base color.
				var hue = hsv.h==0 ? 0 : (360 - hsv.h) / 360;
				$cp.hueInd.css({top:parseInt(hue*$cp.hueY)});
				$cp.baseColor = b;
				$cp.color[0].style.backgroundColor = 'rgb('+b.r+','+b.g+','+b.b+')';

				// Move the color indicator.
				var left = hsv.s;
				var top = (1-hsv.v);
				$cp.lastPosition = [parseInt(left*255),parseInt(top*255)];
				$cp.colorInd.css({left:parseInt(left*$cp.colorX),top:parseInt(top*$cp.colorY)});

				// Set the alpha channel.
				var alpha = c.a/255;
				$cp.alphaInd.css({top:$cp.alphaY-parseInt(alpha*$cp.alphaY)});
				$cp.curColor.css({opacity:alpha});

				// Update the values based on the current color.
				$cp.updateColorValues();
			},

		// When the color slider is moved.
		onColorSlide : function(e,ui)
			{
				var x = parseInt(ui.position.left / $cp.colorX * 255);
				var y = parseInt(ui.position.top / $cp.colorY * 255);
				$cp.lastPosition = [x,y];
				$cp.setGradientColor(x,y);
			},

		// When the hue slider is moved.
		onHueSlide : function(e,ui)
			{
				var hue = parseInt(ui.position.top / $cp.hueY * 255);
				$cp.setVertColor(hue);
			},

		// When the alpha slider is moved.
		onAlphaSlide : function(e,ui)
			{
				// Get the alpha value and assign it to the current color.
				var alpha = ($cp.alphaY - ui.position.top) / $cp.alphaY;
				$cp.setAlphaChannel(alpha);
			},

		// When clicking directly on the color panel, reposition the slider and fire the change event.
		positionColorInd : function(e)
			{
				// Cancel a bubbled mousedown.
				if (e.target!=$cp.color[0]) return false;

				// Get the offset and calculate the indicator position.
				var o = $cp.color.offset();
				var css = {left:e.clientX-o.left-7,top:e.clientY-o.top-7};
				if (css.left<0) css.left = 0;
				if (css.left>$cp.colorX) css.left = $cp.colorX;
				if (css.top<0) css.top = 0;
				if (css.top>$cp.colorY) css.top = $cp.colorY;

				// Move the indicator.
				$cp.colorInd.css(css);

				// Update the color.
				var x = parseInt(css.left / $cp.colorX * 255);
				var y = parseInt(css.top  / $cp.colorY * 255);
				$cp.lastPosition = [x,y];
				$cp.setGradientColor(x,y);

				// Fire the mousedown trigger on it (so the user can keep dragging the element)
				$cp.colorInd.trigger(e);
			},

		// When clicking directly on the hue bar, reposition the slider and fire the change event.
		positionHueInd : function(e)
			{
				// Cancel a bubbled mousedown.
				if (e.target!=$cp.hue[0]) return false;

				// Get the offset and calculate the indicator position.
				var o = $cp.hue.offset();
				var css = {top:e.clientY-o.top-3};
				if (css.top<0) css.top = 0;
				if (css.top>$cp.hueY) css.top = $cp.hueY;

				// Update the hue.
				var hue = parseInt(css.top / $cp.hueY * 255);
				$cp.setVertColor(hue);

				// Move the indicator and fire the mousedown trigger on it (so the user can keep dragging the element).
				$cp.hueInd.css(css);
				$cp.hueInd.trigger(e);
			},

		// When clicking directly on the alpha bar, reposition the slider and fire the change event.
		positionAlphaInd : function(e)
			{
				// Cancel a bubbled mousedown.
				if (e.target!=$cp.alphaInd[0]) return false;

				// Get the offset and calculate the indicator position.
				var o = $cp.alpha.offset();
				var css = {top:e.clientY-o.top-3};
				if (css.top<0) css.top = 0;
				if (css.top>$cp.alphaY) css.top = $cp.alphaY;

				// Update the hue.
				var alpha = ($cp.alphaY - css.top) / $cp.alphaY;
				$cp.setAlphaChannel(alpha);

				// Move the indicator and fire the mousedown trigger on it (so the user can keep dragging the element).
				$cp.alphaInd.css(css);
				$cp.alphaInd.trigger(e);
			},

		// Set the gradient color.
		setGradientColor : function(x,y)
			{
				// Get the base color and adjust the brightness/saturation based on the current coordinates.
				var base = $cp.baseColor;
				var r = Math.round((1-(1-(base.r/255))*(x/255))*(255-y));
				var g = Math.round((1-(1-(base.g/255))*(x/255))*(255-y));
				var b = Math.round((1-(1-(base.b/255))*(x/255))*(255-y));
				var a = $cp.currentColor.a;

				// Save the new color and update the values.
				$cp.currentColor = {r:r,g:g,b:b,a:a};
				$cp.updateColorValues();
			},

		// Change the color based on the hue slider.
		setVertColor : function(hue)
			{
				// Declare the values.
				var n=256/6, j=256/n, C=hue, c=C%n;

				// Calculate the base color based on the hue slider.
				var b = $cp.baseColor = {
					r : parseInt(C<n?255:C<n*2?255-c*j:C<n*4?0:C<n*5?c*j:255),
					g : parseInt(C<n*2?0:C<n*3?c*j:C<n*5?255:255-c*j),
					b : parseInt(C<n?c*j:C<n*3?255:C<n*4?255-c*j:0)
				};
				$cp.color[0].style.backgroundColor = 'rgb('+b.r+','+b.g+','+b.b+')';

				var pos = $cp.lastPosition;
				$cp.setGradientColor(pos[0],pos[1]);
			},

		// Hue values from 0-359 degrees, Saturation and value from 0 - 100%.
		RGBtoHSV : function(rgb)
			{
				// Get the current color.
				var red = rgb.r;
				var green = rgb.g;
				var blue = rgb.b;

				// Analyze the min/max.
				var minRGB = Math.min(Math.min(red,green),blue);
				var maxRGB = Math.max(Math.max(red,green),blue);
				var delta = maxRGB-minRGB;

				// Declare the HSV variables.
				var h = 0.0;
				var v = maxRGB / 255;
				var s = maxRGB==0 ? 0 : delta / maxRGB;

				// Calculate the hue.
				if (s!=0)
				{
					if (maxRGB == red)
						h = (green-blue)/delta;
					else if (maxRGB == green)
						h = 2 + (blue-red)/delta;
					else
						h = 4 + (red-green)/delta;
				}
				else
					h = 0;

				// Convert the hue to degrees.
				h = h * 60;
				if (h<0) h += 360;
				
				return { h:h,s:s,v:v };
			},

		HSVtoRGB : function(hsv)
			{
				var hue = hsv.h;
				var saturation = hsv.s;
				var value = hsv.v;

				if (saturation==0)
				{
					// Greyscale.
					value = parseInt(value*255);
					return { r:value, g:value, b:value};
				}
				else
				{
					// Divide the hue into 6 segments of the circle.
					hue = hue/60;
					var i = Math.floor(hue);

					// Calculate other color values.
					var f = hue-i;
					var p = value * ( 1 - saturation );
					var q = value * ( 1 - saturation * f);
					var t = value * ( 1 - saturation * ( 1 - f ) );

					// Calculate RGB.
					var r,g,b;
					switch (i)
					{
						case 0:
							r = value;
							g = t;
							b = p;
							break;
						case 1:
							r = q;
							g = value;
							b = p;
							break;
						case 2:
							r = p;
							g = value;
							b = t;
							break;
						case 3:
							r = p;
							g = q;
							b = value;
							break;
						case 4:
							r = t;
							g = p;
							b = value;
							break;
						default:
							r = value;
							g = p;
							b = q;
							break;
					}
					
					// Return RGB as a byte triplet.
					return { r:parseInt(r*255), g:parseInt(g*255), b:parseInt(b*255) };
				}
			},

		// Set the alpha channel.
		setAlphaChannel : function(alpha)
			{
				// Update the current color.
				$cp.currentColor.a = parseInt(alpha * 255);

				// Set the opacity of the current color box and update the hex value.
				$cp.curColor.css({opacity:alpha});
				$cp.aBox.val($cp.currentColor.a);
				$cp.hBox.val($cp.toHex($cp.currentColor,$cp.useAlpha));
			},

		// Update the color values based on the current color.
		updateColorValues : function()
			{
				var c = $cp.currentColor;
				$cp.rBox.val(c.r);
				$cp.gBox.val(c.g);
				$cp.bBox.val(c.b);
				$cp.aBox.val(c.a);
				$cp.hBox.val($cp.toHex(c,$cp.useAlpha));
				$cp.curColor[0].style.backgroundColor = 'rgb('+c.r+','+c.g+','+c.b+')';
				$cp.alpha[0].style.backgroundColor = 'rgb('+c.r+','+c.g+','+c.b+')';
			},

		// Save the color.
		saveColor : function(e)
			{
				// Fire the onsave event and hide the selector.
				var color = ($cp.options.nohash?'':'#')+$cp.toHex($cp.currentColor,$cp.useAlpha);
				if (!$.isFunction($cp.onsave) || $cp.onsave($cp.element,color)!=false)
				{
					if ($cp.element)
						$cp.element.val(color).trigger('change');
				}
				$cp.close();
			},

		// Mouse over/out of the color picker.
		colorOver : function(e) { $cp.isover = true; },
		colorOut : function(e) { $cp.isover = false; },

		cancel : function(e)
			{
				// Fire any oncancel event.
				if ($.isFunction($cp.oncancel))
					$cp.oncancel($cp,$cp.element);

				// Then close the box.
				$cp.close();
			},

		// Close the color picker.
		close : function(e)
			{
				// Hide the box.
				if ($cp.main) $cp.main.hide();

				// Fire any onclose event.
				if ($.isFunction($cp.onclose))
					$cp.onclose($cp,$cp.element);

				// The color picker is not open anymore.
				$cp.isopen = false;

				// Unbind the global handlers.
				$(document).unescape($cp.cancel);
				$(document).unbind('mousedown',$cp.checkHide);
			},

		// A mouseclick on the page that isn't over the color picker should close it.
		checkHide : function(e)
			{
				if (!$cp.isover) $cp.close();
			},

		// Initialize the color picker control.
		init : function()
			{
				if ($cp.main) return;

				// Get the components.
				$cp.main = $($cp.html).appendTo(document.body).hover($cp.colorOver,$cp.colorOut);
				$cp.color = $('#cp_color');
				$cp.colorInd = $('#cp_colorind');
				$cp.hue = $('#cp_hue');
				$cp.hueInd = $('#cp_hueind');
				$cp.alpha = $('#cp_alpha');
				$cp.alphaInd = $('#cp_alphaind');
				$cp.alphaValue = $('#cp_alphavalue');
				$cp.curColor = $('#cp_current');
				$cp.oldColor = $('#cp_old');
				$cp.rBox = $('#cp_rgbR').bind('change',function(){$cp.currentColor.r=$.toByte(this.value);$cp.calculateColor();});
				$cp.gBox = $('#cp_rgbG').bind('change',function(){$cp.currentColor.g=$.toByte(this.value);$cp.calculateColor();});
				$cp.bBox = $('#cp_rgbB').bind('change',function(){$cp.currentColor.b=$.toByte(this.value);$cp.calculateColor();});
				$cp.aBox = $('#cp_rgbA').bind('change',function(){$cp.currentColor.a=$.toByte(this.value);$cp.calculateColor();});
				$cp.hBox = $('#cp_hex').bind('change',function(){$cp.currentColor=$cp.cleanColor(this.value);$cp.calculateColor();});

				// Adjust for IE PNG Transparency in IE6.
				if (jQuery.browser.msie6) { p.color.addClass('ie6_png'); };
				
				// Set up the events and slider.
				$('#cp_save').click($cp.saveColor);
				$cp.colorInd.draggable({containment:'parent',cursorAt:{top:5,left:5},drag:$cp.onColorSlide});;
				$cp.hueInd.draggable({containment:'parent',cursorAt:{top:3,left:0},drag:$cp.onHueSlide});
				$cp.alphaInd.draggable({containment:'parent',cursorAt:{top:3,left:0},drag:$cp.onAlphaSlide});

				// Set the sliders to move to the appropriate position when the base is clicked.
				$cp.color.bind('mousedown',$cp.positionColorInd);
				$cp.hue.bind('mousedown',$cp.positionHueInd);
				$cp.alpha.bind('mousedown',$cp.positionAlphaInd);
			}
	});


	// Set up a textbox as a color editor.
	$.fn.colorpicker = function(options)
		{
			return this.each(function(i)
				{
					if (this.$color) return;
					
					var box = this;
					var fn = function(e){$cp.activate(box,options);};
					var el = $(this).css({verticalAlign:'middle'});
					var btn = $('<img src="Shared/images/admin/ChooseColor.gif" style="width:16px;height:16px;vertical-align:middle;">').insertAfter(el).bind('click',fn);
				});
		};	

})(jQuery);
