// extensions of Prototype
// NOTE: this file is based on Prototype version 1.4
// require prototype.js version 1.4

// Element extensions ===============================================
Object.extend(Element, {

	getStyle: function(element, style) {
		element = $(element);

		if (!element || !element.style) return null;

		var value = element.style[style.camelize()];
		if (!value) {
			if (document.defaultView && document.defaultView.getComputedStyle) {
				var css = document.defaultView.getComputedStyle(element, null);
				value = css ? css.getPropertyValue(style) : null;
			} else if (element.currentStyle) {
				value = element.currentStyle[style.camelize()];
			}
		}

		if (window.opera && ['left', 'top', 'right', 'bottom'].include(style))
			if (Element.getStyle(element, 'position') == 'static') value = 'auto';

		return value == 'auto' ? null : value;
	}
});

// Array extensions ======================================================
Object.extend(Array.prototype, {
	remove: function(idx) {
		if( isNaN(idx) || idx < 0 || idx > this.length )
			return false;
		for( var i = 0, n = 0; i < this.length; i++ )
			if( i != idx )
				this[n++] = this[i];
		this.length -= 1;
	},
	
	removeItem: function(item) {
		this.remove(this.indexOf(item));
	}
});

Array.prototype.contains = Array.prototype.include;

// Position extensions ====================================================
Object.extend(Position, {

	getViewportWidth: function() {
		var w = 0;

		if(window.innerWidth){
			w = window.innerWidth;
		}

		if(document.documentElement && document.documentElement.clientWidth){
			// IE6 Strict
			var w2 = document.documentElement.clientWidth;
			// this lets us account for scrollbars
			if(!w || w2 && w2 < w) {
				w = w2;
			}
			return w;
		}

		if(document.body){
			// IE
			return document.body.clientWidth;
		}

		return 0;
	},
	
	getViewportHeight: function() {
		if (window.innerHeight){
			return window.innerHeight;
		}

		if (document.documentElement && document.documentElement.clientHeight){
			// IE6 Strict
			return document.documentElement.clientHeight;
		}

		if (document.body){
			// IE
			return document.body.clientHeight;
		}

		return 0;
	},

	getViewportSize: function() {
		var ret = [Position.getViewportWidth(), Position.getViewportHeight()];
		ret.w = ret[0];
		ret.h = ret[1];
		return ret;
	},
	
	// in: coordinate array [x,y,w,h] or dom node
	// return: coordinate array
	toCoordinateArray: function(coords, includeScroll) {
		if(coords instanceof Array || typeof coords == "array"){
			// coords is already an array (of format [x,y,w,h]), just return it
			while ( coords.length < 4 ) { coords.push(0); }
			while ( coords.length > 4 ) { coords.pop(); }
			var ret = coords;
		} else {
			// coords is an dom object (or dom object id); return it's coordinates
			var node = $(coords);
			var ret = [
				this.getAbsoluteX(node, includeScroll),
				this.getAbsoluteY(node, includeScroll),
				node.offsetWidth,
				node.offsetHeight
			];
		}
		ret.x = ret[0];
		ret.y = ret[1];
		ret.w = ret[2];
		ret.h = ret[3];
		return ret;
	},
	
	getAbsoluteX: function(node, includeScroll) {
		return this.getTotalOffset(node, "left", includeScroll);
	},
	
	getAbsoluteY: function(node, includeScroll) {
		return this.getTotalOffset(node, "top", includeScroll);
	},
	
	getTotalOffset: function (node, type, includeScroll){
		var typeStr = (type=="top") ? "offsetTop" : "offsetLeft";
		var typeScroll = (type=="top") ? "scrollTop" : "scrollLeft";
	
		var alt = (type=="top") ? "y" : "x";
		var ret = 0;
		if(node["offsetParent"]){
		
			if(includeScroll && node.parentNode != document.body) {
				ret -= this.sumAncestorProperties(node, typeScroll);
			}
			// FIXME: this is known not to work sometimes on IE 5.x since nodes
			// soemtimes need to be "tickled" before they will display their
			// offset correctly
			do {
				ret += node[typeStr];
				node = node.offsetParent;
			} while (node != document.getElementsByTagName("body")[0].parentNode && node != null);
			
		} else if(node[alt]){
			ret += node[alt];
		}
		return ret;
	},

	sumAncestorProperties: function (node, prop) {
		if (!node) { return 0; } // FIXME: throw an error?
	
		var retVal = 0;
		while (node) {
			var val = node[prop];
			if (val) {
				retVal += val - 0;
			}
			node = node.parentNode;
		}
		return retVal;
	},
	
	getAbsolutePosition: function(node, includeScroll) {
		var position = [
			this.getAbsoluteX(node, includeScroll),
			this.getAbsoluteY(node, includeScroll)
		];
		position.x = position[0];
		position.y = position[1];
		return position;
	},
	
	// from rico 
	getViewportPosition: function(element) {
		return this._toAbsolute(element, true);
		//return this.getAbsolutePosition(element, true);
	},

	getDocumentPosition: function(element) {
		return this._toAbsolute(element, false);
		//return this.getAbsolutePosition(element, false);
	},
	
	// TODO: method below could be removed.

	/**
	 *  Compute the elements position in terms of the window viewport
	 *  so that it can be compared to the position of the mouse (dnd)
	 *  This is additions of all the offsetTop,offsetLeft values up the
	 *  offsetParent hierarchy, ...taking into account any scrollTop,
	 *  scrollLeft values along the way...
	 *
	 * IE has a bug reporting a correct offsetLeft of elements within a
	 * a relatively positioned parent!!!
	 **/
	_toAbsolute: function(element, accountForDocScroll) {

		if ( navigator.userAgent.toLowerCase().indexOf("msie") == -1 )
			return this._toAbsoluteMozilla(element, accountForDocScroll);

		var x = 0;
		var y = 0;
		var parent = element;
		while ( parent ) {

			var borderXOffset = 0;
			var borderYOffset = 0;
			if ( parent != element ) {
				var borderXOffset = parseInt(Element.getStyle(parent, "borderLeftWidth" ));
				var borderYOffset = parseInt(Element.getStyle(parent, "borderTopWidth" ));
				borderXOffset = isNaN(borderXOffset) ? 0 : borderXOffset;
				borderYOffset = isNaN(borderYOffset) ? 0 : borderYOffset;
			}

			//~ zola edited:
			// x += parent.offsetLeft - parent.scrollLeft + borderXOffset;
			// y += parent.offsetTop - parent.scrollTop + borderYOffset;
	
			x += parent.offsetLeft + borderXOffset;
			y += parent.offsetTop + borderYOffset;
         
			if ( parent &&
				parent != document.body &&
				parent != document.documentElement ) {
				if (parent.scrollLeft)
					x -= parent.scrollLeft;
				if (parent.scrollTop)
					y -= parent.scrollTop;
			}
			//~ zola end;
			
			parent = parent.offsetParent;
		}

		if ( accountForDocScroll ) {
			x -= this.docScrollLeft();
			y -= this.docScrollTop();
		}

		return { x:x, y:y };
	},

	/**
	 *  Mozilla did not report all of the parents up the hierarchy via the
	 *  offsetParent property that IE did.  So for the calculation of the
	 *  offsets we use the offsetParent property, but for the calculation of
	 *  the scrollTop/scrollLeft adjustments we navigate up via the parentNode
	 *  property instead so as to get the scroll offsets...
	 *
	 **/
	_toAbsoluteMozilla: function(element, accountForDocScroll) {
		var x = 0;
		var y = 0;
		var parent = element;
		while ( parent ) {
			x += parent.offsetLeft;
			y += parent.offsetTop;
			parent = parent.offsetParent;
		}

		parent = element;
		while ( parent &&
				parent != document.body &&
				parent != document.documentElement ) {
			if ( parent.scrollLeft  )
				x -= parent.scrollLeft;
			if ( parent.scrollTop )
				y -= parent.scrollTop;
			parent = parent.parentNode;
		}

		if ( accountForDocScroll ) {
			x -= this.docScrollLeft();
			y -= this.docScrollTop();
		}

		return { x:x, y:y };
	},

	docScrollLeft: function() {
		if ( window.pageXOffset )
			return window.pageXOffset;
		else if ( document.documentElement && document.documentElement.scrollLeft )
			return document.documentElement.scrollLeft;
		else if ( document.body )
			return document.body.scrollLeft;
		else
			return 0;
	},

	docScrollTop: function() {
		if ( window.pageYOffset )
			return window.pageYOffset;
		else if ( document.documentElement && document.documentElement.scrollTop )
			return document.documentElement.scrollTop;
		else if ( document.body )
			return document.body.scrollTop;
		else
			return 0;
	},
	
	getScrollOffset: function() {
		var ret = [0, 0];

		if (window.pageYOffset) {
			ret = [window.pageXOffset, window.pageYOffset];
		} else if(document.documentElement && document.documentElement.scrollTop) {
			ret = [document.documentElement.scrollLeft, document.documentElement.scrollTop];
		} else if(document.body) {
			ret = [document.body.scrollLeft, document.body.scrollTop];
		}

		ret.x = ret[0];
		ret.y = ret[1];
		return ret;
	}
});

// Color extension from Rico(http://openrico.org/) ============================
if (!window.Color) {
	var Color = Class.create();
}

Color.prototype = {
	initialize: function(red, green, blue) {
		this.rgb = { r: red, g : green, b : blue };
	},

	setRed: function(r) {
		this.rgb.r = r;
	},

	setGreen: function(g) {
		this.rgb.g = g;
	},

	setBlue: function(b) {
		this.rgb.b = b;
	},

	setHue: function(h) {
		// get an HSB model, and set the new hue...
		var hsb = this.asHSB();
		hsb.h = h;

		// convert back to RGB...
		this.rgb = Color.HSBtoRGB(hsb.h, hsb.s, hsb.b);
	},

	setSaturation: function(s) {
		// get an HSB model, and set the new hue...
		var hsb = this.asHSB();
		hsb.s = s;

		// convert back to RGB and set values...
		this.rgb = Color.HSBtoRGB(hsb.h, hsb.s, hsb.b);
	},

	setBrightness: function(b) {
		// get an HSB model, and set the new hue...
		var hsb = this.asHSB();
		hsb.b = b;

		// convert back to RGB and set values...
		this.rgb = Color.HSBtoRGB( hsb.h, hsb.s, hsb.b );
	},

	darken: function(percent) {
		var hsb  = this.asHSB();
		this.rgb = Color.HSBtoRGB(hsb.h, hsb.s, Math.max(hsb.b - percent,0));
	},

	brighten: function(percent) {
		var hsb  = this.asHSB();
		this.rgb = Color.HSBtoRGB(hsb.h, hsb.s, Math.min(hsb.b + percent,1));
	},

	blend: function(other) {
		this.rgb.r = Math.floor((this.rgb.r + other.rgb.r)/2);
		this.rgb.g = Math.floor((this.rgb.g + other.rgb.g)/2);
		this.rgb.b = Math.floor((this.rgb.b + other.rgb.b)/2);
	},

	isBright: function() {
		var hsb = this.asHSB();
		return this.asHSB().b > 0.5;
	},

	isDark: function() {
		return ! this.isBright();
	},

	asRGB: function() {
		return "rgb(" + this.rgb.r + "," + this.rgb.g + "," + this.rgb.b + ")";
	},

	asHex: function() {
		return "#" + this.rgb.r.toColorPart() + this.rgb.g.toColorPart() + this.rgb.b.toColorPart();
	},

	asHSB: function() {
		return Color.RGBtoHSB(this.rgb.r, this.rgb.g, this.rgb.b);
	},

	toString: function() {
		return this.asHex();
	}

};

Color.createFromHex = function(hexCode) {
	if( hexCode.length == 4 ) {
		var shortHexCode = hexCode; 
		var hexCode = '#';
		for(var i = 1; i < 4; i++) 
			hexCode += (shortHexCode.charAt(i) + shortHexCode.charAt(i));
	}
	if ( hexCode.indexOf('#') == 0 )
		hexCode = hexCode.substring(1);
	var red   = hexCode.substring(0,2);
	var green = hexCode.substring(2,4);
	var blue  = hexCode.substring(4,6);
	return new Color( parseInt(red,16), parseInt(green,16), parseInt(blue,16) );
}

Color.createColorFromBackground = function(elem) {
	var actualColor = Element.getStyle($(elem), "background-color");
	
	if ( actualColor == "transparent" && elem.parentNode )
		return Color.createColorFromBackground(elem.parentNode);

	if ( actualColor == null )
		return new Color(255,255,255);

	if ( actualColor.indexOf("rgb(") == 0 ) {
		var colors = actualColor.substring(4, actualColor.length - 1 );
		var colorArray = colors.split(",");
		return new Color( parseInt( colorArray[0] ),
                            parseInt( colorArray[1] ),
                            parseInt( colorArray[2] )  );

	}
	else if ( actualColor.indexOf("#") == 0 ) {
		return Color.createFromHex(actualColor);
	}
	else
		return new Color(255,255,255);
}

Color.HSBtoRGB = function(hue, saturation, brightness) {
	var red   = 0;
	var green = 0;
	var blue  = 0;

	if (saturation == 0) {
      red = parseInt(brightness * 255.0 + 0.5);
	   green = red;
	   blue = red;
	}
	else {
		var h = (hue - Math.floor(hue)) * 6.0;
		var f = h - Math.floor(h);
		var p = brightness * (1.0 - saturation);
		var q = brightness * (1.0 - saturation * f);
		var t = brightness * (1.0 - (saturation * (1.0 - f)));

		switch (parseInt(h)) {
			case 0:
				red   = (brightness * 255.0 + 0.5);
				green = (t * 255.0 + 0.5);
				blue  = (p * 255.0 + 0.5);
				break;
			case 1:
				red   = (q * 255.0 + 0.5);
				green = (brightness * 255.0 + 0.5);
				blue  = (p * 255.0 + 0.5);
				break;
			case 2:
				red   = (p * 255.0 + 0.5);
				green = (brightness * 255.0 + 0.5);
				blue  = (t * 255.0 + 0.5);
				break;
			case 3:
				red   = (p * 255.0 + 0.5);
				green = (q * 255.0 + 0.5);
				blue  = (brightness * 255.0 + 0.5);
				break;
			case 4:
				red   = (t * 255.0 + 0.5);
				green = (p * 255.0 + 0.5);
				blue  = (brightness * 255.0 + 0.5);
				break;
			case 5:
				red   = (brightness * 255.0 + 0.5);
				green = (p * 255.0 + 0.5);
				blue  = (q * 255.0 + 0.5);
				break;
		}
	}

	return { r : parseInt(red), g : parseInt(green) , b : parseInt(blue) };
}

Color.RGBtoHSB = function(r, g, b) {

	var hue;
	var saturation;
	var brightness;

	var cmax = (r > g) ? r : g;
	if (b > cmax)
		cmax = b;

	var cmin = (r < g) ? r : g;
	if (b < cmin)
		cmin = b;

	brightness = cmax / 255.0;
	if (cmax != 0)
		saturation = (cmax - cmin)/cmax;
	else
		saturation = 0;

	if (saturation == 0)
		hue = 0;
	else {
		var redc   = (cmax - r)/(cmax - cmin);
		var greenc = (cmax - g)/(cmax - cmin);
		var bluec  = (cmax - b)/(cmax - cmin);

		if (r == cmax)
			hue = bluec - greenc;
		else if (g == cmax)
			hue = 2.0 + redc - bluec;
		else
			hue = 4.0 + greenc - redc;

		hue = hue / 6.0;
		if (hue < 0)
		hue = hue + 1.0;
	}

	return { h : hue, s : saturation, b : brightness };
}
