var createElement = function(tagName,attrs,style) {
	var obj = new Element(tagName,attrs);
	if (!isElement(obj)) return undefined;
	if (Object.isString(style)) obj.addClassName(style);
	else obj.setStyle(style);
	return obj;
};
var removeNode = function(node) {
	if (!isElement(node) || !node.parentNode) return undefined;
	return node.parentNode.removeChild(node);
};
/* CLONE */var isUndefined = function(obj) {
	return typeof obj == 'undefined';
};
/* CLONE */var randName = function() {
	return 'r'+Math.random().toString().substr(2,8);
};
/* CLONE */var execArray = function(fnc) {
	if (!fnc) return;
	var array = new Array;
	if (Object.isFunction(fnc)) return fnc();
	if (!(fnc = $A(fnc)).length) return;
	for (var i = 0; i < fnc.length; array.push('fnc['+(i++)+']'));
	if (array.length) with (array)
		return new Function('fnc','return '+shift()+'('+join(',')+')')(fnc);
};
/* CLONE */var toBoolean = function(obj) {
	return new Boolean(obj).valueOf();
};
/* CLONE */var getRegExp = function() {
	return new RegExp($w($A(arguments).join(' ')).join('/s*?').replace(/\//g,'\\'),'i');
};
var extractDate = function(date) {
	var re = getRegExp(new String('^ p (/.|/057) p /2 ((?:/d/d)?/d/d)(?: p : p : p)? $').replace(/p/ig,'(/d?/d)'));
	if (new String(date).match(re)) return new Date(date).getTime();
	else if (date instanceof Date) return date.getTime();
	else return NaN;
};
window.$D = function(start,end) {
	var current = start = new Date(extractDate(start));
	if (isUndefined(end)) return $A([start]);
	var result = [],end = new Date(extractDate(end));
	while (current <= end && result.push(current)) with (current)
		current = new Date(getFullYear(),getMonth(),getDate()+1);
	return result;
};
var toNumber = function(value) {
	return isNaN(value = extractDate(value))?NaN:parseFloat(value);
};
var isElement = function(node) {
	return Object.isElement(node);
};
var setParent = function(parent,node) {
	if (isElement(node) && isElement(parent)) {
		if (isElement(node.parentNode)) node.remove();
		return parent.appendChild(node);
	} else return null;
};

var AxisPin = Class.create({
	initialize:function(tagName,attrs,style,fields,value) {
		var myself = this,sys = {fields:[],name:randName()};
		var getName = function(name) {
			return [sys.name,new String(name)].join();
		};
		this.getElement = function() {
			return sys.obj;
		};
		this.setField = function(name,node,fnc) {
			var pos,obj = {name:name,node:null};
			obj.node = (fnc = Object.isFunction(fnc)?fnc:Prototype.K)(node);
			if (!isElement(obj.node))
				obj.node = $(document.createTextNode(new String(obj.node)));
			if (!Object.isNumber(pos = sys.fields[name = getName(name)])) {
				sys.fields[name] = sys.fields.push(obj)-1;
				return void(sys.obj.appendChild(obj.node)) || myself;
			} (node = sys.fields[pos].node).parentNode.replaceChild(obj.node,node);
			return void(Object.extend(sys.fields[pos],obj)) || myself;
		};
		this.dropField = function(name) {
			var pos = sys.fields[name = getName(name)];
			if (!Object.isNumber(pos)) return myself;
			var node = sys.fields[pos].node;
			delete sys.fields[name];
			removeNode(node);
			sys.fields.splice(pos,1);
			sys.fields.each(function(obj,i) {
				sys.fields[getName(obj.name)] = i;
			});
			return myself;
		};
		this.getField = function(name) {
			var pos = sys.fields[getName(name)];
			if (!Object.isNumber(pos)) return;
			return sys.fields[pos].node;
		};
		this.getValue = function() {
			return value;
		};
		this.setValue = function(newValue) {
			return void(value = newValue) || myself;
		};
		this.eachField = function(fnc) {
			if (Object.isFunction(fnc))
				sys.fields.each(function(field,i) {
					return fnc(field.node,field.name,i);
				});
			return myself;
		};
		this.invoke = function(method) {
		    var args = $A(arguments).slice(1);
		    sys.fields.collect(function(field) {
				if (Object.isElement(field.node)) return field.node;
			}).map(function(value) {
		      return value[method].apply(value,args);
		    });
			return myself;
		};
		sys.obj = createElement(tagName,attrs,style);
		if (isUndefined(sys.obj)) throw new Error;
		$H(fields).keys().each(function(key) {
			myself.setField(key,$H(fields).get(key));
		});
	}
});
var AxisPane = Class.create({
	initialize:function(tagName,attrs,style,vertical,reversed) {
		var myself = this,sys = {pins:$A([])};
		var update = function(argument) {
			sys.pins = sys.pins.findAll(function(pin) {
				return pin instanceof AxisPin;
			});
			if (isElement(sys.obj.parentNode)) {
				var dim = sys.obj.getDimensions();
				$A(sys.obj.childNodes).each(function(node) {
					if (sys.pins.collect(function(pin) {
						return pin.getElement();
					}).indexOf(node) < 0) removeNode(node);
				});
				var last = sys.pins.length-1;
				vertical = isUndefined(vertical)?dim.height > dim.width:toBoolean(vertical);
				sys.pins.each(function(pin,i) {
					var parent = (pin = pin.getElement()).parentNode;
					if (isElement(parent))
						if (parent == sys.obj) return;
						else removeNode(pin);
					if (i == sys.obj.childNodes.length) sys.obj.appendChild(pin);
					else sys.obj.childElements()[i].insert({before:pin});
					var shift = vertical?pin.getHeight():pin.getWidth();
					shift = shift*(reversed?last-i:i)*100/((vertical?dim.height:dim.width)*last);
					shift = ((last == i)?100:Math.abs((last?i*100/last-shift*(reversed?-1:+1):0)-(reversed?100:0)))+'%';
					pin.setStyle(vertical?{top:shift}:{left:shift});
				});
			} return argument;
		};
		this.each = function(fnc) {
			return sys.pins.each(fnc);
		};
		this.collect = function(fnc) {
			return sys.pins.collect(fnc);
		};
		this.getElement = function() {
			return sys.obj;
		};
		this.shift = function() {
			return update(sys.pins.shift());
		};
		this.unshift = function(pin) {
			return update(sys.pins.unshift(pin));
		};
		this.pop = function() {
			return update(sys.pins.pop());
		};
		this.push = function(pin) {
			return update(sys.pins.push(pin));
		};
		this.slice = function() {
			return update(execArray([sys.pins.slice].concat($A(arguments))));
		};
		this.splice = function() {
			return update(execArray([sys.pins.splice].concat($A(arguments))));
		};
		this.setParent = function(node) {
			update(setParent(node,sys.obj));
			return myself;
		};
		this.byIndex = function(index) {
			return sys.pins[index];
		};
		this.count = function() {
			return sys.pins.length;
		};
		this.isVertical = function() {
			return vertical;
		};
		this.getReversed = function() {
			return toBoolean(reversed);
		};
		this.setReversed = function(value) {
			$A(sys.obj.childNodes).each(function(node) {
				removeNode(node);
			});
			update(reversed = toBoolean(value));
			return myself;
		};
		sys.obj = createElement(tagName,attrs,style);
		if (isUndefined(sys.obj)) throw new Error;
	}
});

var StackBar = Class.create(AxisPin,{
	initialize:function($super,tagName,attrs,style,fields,start,end) {
		var myself = this,sys = {};
		$super(tagName,attrs,style,fields);
		delete this.setValue;
		delete this.getValue;
		var update = function() {
			if (end < start) {
				with ([start,end])
					start = last();
					end = first();
			} return myself;
		};
		this.getStart = function() {
			return sys.start;
		};
		this.setStart = function(start) {
			return update(sys.start = toNumber(start));
		};
		this.getEnd = function() {
			return sys.end;
		};
		this.setEnd = function(end) {
			return update(sys.end = toNumber(end));
		};
		myself.setStart(start);
		myself.setEnd(end);
	}
});
var StackPane = Class.create({
	initialize:function(tagName,attrs,style,start,end,bars) {
		var sys = {bars:$A([]),map:$A([]),start:toNumber(start),end:toNumber(end)};
		var myself = this;
		var intersect = function(target,source) {
			with (target) var ts = getStart(),te = getEnd();
			with (source) var ss = getStart(),se = getEnd();
			if ((ss < te && ss > ts) || (se < te && se > ts) || (ts < se && ts > ss) || (te < se && te > ss) || (te == se && ts == ss)) return true;
			return false;
		};
		this.getElement = function() {
			return sys.obj;
		};
		var update = function(argument) {
			sys.map.clear();
			(sys.bars = sys.bars.findAll(function(bar) {
				if (bar instanceof StackBar) {
					//if (bar.getStart() < sys.start) 
					//if (bar.getStart() < sys.start) bar.setStart(sys.start);
					//if (bar.getEnd() > sys.end) bar.setEnd(sys.end);
					
					//alert("END:"+bar.getEnd()+"\nSTART:"+bar.getStart());
					
					return true;
				} else return false;
			})).each(function(bar) {
				with (sys) level:for (var i = 0; i < map.length; i++) {
					for (var j = 0; j < map[i].length; j++)
						if (intersect(map[i][j],bar)) continue level;
					return map[i].push(bar);
				} sys.map.push([bar]);
			});
			if (isElement(sys.obj.parentNode)) {
				$A(sys.obj.childNodes).each(function(node) {
					if (sys.bars.collect(function(bar) {
						return bar.getElement();
					}).indexOf(node) < 0) removeNode(node);
				});
				var dim = sys.obj.getDimensions();
				var length = sys.end-sys.start;
				sys.map.each(function(bars,i) {
					var barTop = 100*(sys.map.length-i-1)/sys.map.length+'%';
					//bars.each(function(bar) {
						
					//	alert("END:"+bar.getEnd()+"\nSTART:"+bar.getStart());
					//})
					
					
					//return
					for (var j = 0; j < bars.length; j++) {
						barLeft = 100*(bars[j].getStart()-sys.start)/length+'%';
						barWidth = 100*(bars[j].getEnd()-bars[j].getStart())/length+'%';
						
						
						//alert("top:"+barTop+"\nleft:"+barLeft+"\nwidth:"+barWidth+"\n"+
						//"END:"+bars[j].getEnd()+"\nSTART:"+bars[j].getStart()+"\nleng:"+length)
						
						
						bars[j].getElement().setStyle({top:barTop,left:barLeft,width:barWidth});
						setParent(sys.obj,bars[j].getElement());
					};
				});
			} return argument;
		};
		this.each = function(fnc) {
			return sys.bars.each(fnc);
		};
		this.setParent = function(node) {
			update(setParent(node,sys.obj));
			return myself;
		};
		this.push = function(bar) {
			return update(sys.bars.push(bar));
		};
		sys.obj = createElement(tagName,attrs,style);
		if (isUndefined(sys.obj)) throw new Error;
		$A(bars).each(function(bar) {
			myself.push(bar);
		});
	}
});