// Version 0.5 
var undefined;

function Xu(item, create, textString){
	var createStructure = true;
	if(create) {
		if(item == 'text') item = document.createTextNode(textString);
		else item = document.createElement(item);
	}
	else{
		if(arguments.length == 0) item = document;
		else if(typeof item == 'string') item = document.getElementById(item);
		else if(typeof item == 'object' && item.isXu){
			item = item.get();
			createStructure = false;
		}
	}
	
	this.item = item;
	this.isXu = true;
	
	if(createStructure && this.item != undefined && this.item.nodeType != 3 && this.item.Xu_data == undefined){
		this.item.Xu_data      = new Array();
		this.item.Xu_events    = new Array();
		this.item.Xu_listeners = new Array();
	}
}

Xu.prototype = {
	getById: function(id){
		return document.getElementById(id);
	},
	
	getByTag: function(tag, num){
		var results = this.item.getElementsByTagName(tag);
		
		if(arguments.length == 2) return (results.length > num ? results[num] : null);
		
		return results;
	},
	
	getByClass: function(tag, className, num){
		var tmp = this.getByTag(tag);
		
		var results = new Array();
		for(var i = 0; i < tmp.length; ++i){
			if(tmp[i].className == className) results.push(tmp[i]);
		}
		
		if(arguments.length == 3) return (results.length > num ? results[num] : null);
		
		return results;
	},
	
	contains: function(item){
		return this.item == item;
	},
	
	get: function(){
		return this.item;
	},
	
	isNull: function(){
		return this.item == null;
	},
	
	setVar: function(label, value){
		if(arguments.length == 1){
			if(this.item[label] != undefined) delete this.item[label];
		}
		else this.item[label] = value;
		
		return this;
	},
	
	getVar: function(label){
		if(this.item[label] == undefined) return undefined;
		
		return this.item[label];
	},
	
	setData: function(label, value){
		if(arguments.length == 1){
			if(this.item.Xu_data[label] != undefined) delete this.item.Xu_data[label];
		}
		else this.item.Xu_data[label] = value;
		
		return this;
	},
	
	getData: function(label){
		if(this.item.Xu_data[label] == undefined) return undefined;
		
		return this.item.Xu_data[label];
	},
	
	addListener: function(listener, event, funcName){
		if(!funcName) funcName = event + 'Handler';
		if(!this.item.Xu_listeners[event]) this.item.Xu_listeners[event] = new Array();
		this.item.Xu_listeners[event].push(new Array(listener, funcName));
		
		return this;
	},
	
	removeListener: function(listener, event){
		if(!event){
			for(var event in this.item.Xu_listener){
				this.item.Xu_listener[event] = new Xar(this.item.Xu_listener[event]).remove(listener);
			}
		}
		
		this.item.Xu_listener[event] = new Xar(this.item.Xu_listener[event]).remove(listener);
		
		return this;
	},
	
	purgeListeners: function(event){
		if(!event) this.item.Xu_listeners = new Array();
		else this.item.Xu_listeners = new Xar(this.item.Xu_listeners).removeByKey(event);
	},
	
	getListeners: function(){
		return this.item.Xu_listeners;
	},
	
	addEvent: function(event, func){
		if(!this.item.Xu_events[event]){
			this.item.Xu_events[event] = new Array();
			
			this.item[event] = function(e){
				if (!e) e = window.event;
				
				var type = 'on' + e.type;
				
				var source = this;
				
				if(this.Xu_listeners[type]){
					var listenersLength = this.Xu_listeners[type].length;
					
					for(var i = 0; i < listenersLength; ++i) {
						this.Xu_listeners[type][i][0][this.Xu_listeners[type][i][1]](e, source, type);
					}
				}
				
				var eventsLength = this.Xu_events[type].length;
				
				var propagate = true;
				
				for(var i = 0; i < eventsLength; ++i){
					propagate = this.Xu_events[type][i](e, source, type);
				}
				
				if(propagate === false) return false;
			}
		}
		
		this.item.Xu_events[event].push(func);
		
		return this;
	},
	
	removeEvent: function(event, func){
		if(!event){
			for(event in this.item.Xu_events){
				this.item.Xu_events[event] = new Xar(this.item.Xu_events[event]).remove(listener);
			}
		}
		
		this.item.Xu_events[event] = new Xar(this.item.Xu_events[event]).remove(listener);
		
		return this;
	},
	
	purgeEvents: function(event){
		if(!event) this.item.Xu_events = new Array();
		else this.item.Xu_events = new Xar(this.item.Xu_events).removeByKey(event);
	},
	
	getEvents: function(){
		return this.item.Xu_events;
	},
	
	fireEvent: function(event){
		var e = {
			type: event.substring(2),
			srcElement: this.item,
			target: this.item
		}
		
		this.item[event](e);
	},
	
	getComputedStyle: function(property){
		if(this.item.currentStyle) return x.currentStyle[property];
		
		if(document.defaultView.getComputedStyle) return document.defaultView.getComputedStyle(this.item, '')[property];
		
		return null;
	},
	
	getJSStyle: function(property){
		return this.item.style[property];
	},
	
	getStyle: function(property){
		//TODO: migliorare come setstyle
		var res = this.getJSStyle(property);
		
		if(res != null) return res;
		
		return this.getComputedStyle(property);
	},
	
	setStyles: function(styleObj){
		for(var property in styleObj) this.setStyle(property, styleObj[property]);
		
		return this;
	},
	
	setStyle: function(property, value){
		switch(property){
			case 'float':
			if(!document.all || window.Opera) this.item.style.cssFloat   = value;
			else                              this.item.style.styleFloat = value;
			break;
			
			case 'opacity':
			this.item.style.opacity = (value / 100);
			this.item.style.filter  = 'alpha(opacity=' + value + ')';
			break;
			
			default:
			this.item.style[property] = value;
			break;
		}
		
		return this;
	},
	
	getAbsolutePosition: function(){
		var x = y = 0;
		var obj = this.item;
		if (obj.offsetParent) {
			x = obj.offsetLeft
			y = obj.offsetTop
			while (obj = obj.offsetParent) {
				x += obj.offsetLeft
				y += obj.offsetTop
			}
		}
		return new Array(x, y);
	},
	
	appendTo: function(item){
		new Xu(item).get().appendChild(this.item);
		
		return this;
	},
	
	append: function(item){
		this.item.appendChild(new Xu(item).get());
		
		return this;
	},
	
	insertBeforeItem: function(item){
		item = new Xu(item).get();
		
		item.parentNode.insertBefore(this.item, item);
		
		return this;
	},
	
	insertAfterItem: function(item){
		item = new Xu(item).get();
		
		var next = item.nextSibling;
		
		if(next) item.parentNode.insertBefore(this.item, next);
		else item.parentNode.appendChild(this.item);
		
		return this;
	},
	
	parentItem: function(level){
		if(level == undefined) level = 0;
		
		var tmp = this.item.parentNode;
		for(var i = level; i > 0; --i){
			if(tmp.parentNode == null) return null;
			tmp = tmp.parentNode;
		}
		
		return tmp;
	},
	
	childItem: function(pos){
		if(!this.item.hasChildNodes) return null;
		
		if(pos == undefined) return this.item.childNodes;
		else if(pos == 0) return this.item.firstChild;
		else if(pos == -1) return this.item.lastChild;
		else{
			var tmp = this.item.childNodes;
			
			if(pos > 0) return (pos < tmp.length ? tmp[pos] : null);
			else return (tmp.length + pos >= 0 ? tmp[tmp.length + pos] : null);
		}
	},
	
	removeItem: function(item){
		this.item.removeChild(new Xu(item).get());
	},
	
	removeAll: function(){
		while(this.item.hasChildNodes()) this.item.removeChild(this.item.lastChild);
		
		return this;
	},
	
	removeSelf: function(){
		return this.item.parentNode.removeChild(this.item);
	},
	
	swapVarWithItem: function(item, property){
		//TODO: un po' di controllo di errore
		item = new Xu(item);
		tmp  = item.getVar(property);
		item.setVar(property, this.item[property]);
		this.item[property] = tmp;
	}
}

function Xar(item){
	if(arguments.length === 0) this.item = new Array();
	
	this.item = item;
}

Xar.prototype = {
	find: function(value){
		var length = this.item.length;
		
		for(var i = 0; i < length; ++i){
			if(this.item[i] == value) return i;
		}
		
		return null;
	},
	
	remove: function(value){
		var length = this.item.length;
		
		var newArray = new Array();
		
		for(var i = 0; i < length; ++i){
			if(this.item[i] != value) newArray.push(this.item[i]);
		}
		
		return newArray;
	},
	
	removeByValue: function(value){
		return this.remove(value);
	},
	
	removeByKey: function(key){
		var newArray = new Array();
		
		for(var key in this.item){
			if(key != value) newArray[key] = this.item[key];
		}
		
		return newArray;
	},
	
	keys: function(){
		var newArray = new Array();
		
		for(var key in this.item) newArray.push(key);
		
		return newArray;
	},
	
	values: function(){
		var newArray = new Array();
		
		for(var key in this.item) newArray.push(this.item[key]);
		
		return newArray;
	}
}

function Xi(obj, method, func, interval, parameters){
	this.obj        = obj;
	this.method     = method;
	this.func       = func;
	this.parameters = (parameters ? parameters : new Array());
	this.interval   = interval;
	
	return this;
}

Xi.prototype = {
	start: function(){
		var selfObj = this;
		
		var vfunc = function(){ selfObj.proceed(); }
		
		if(this.method == 'interval') this.timerID = setInterval(vfunc, this.interval);
		else                          this.timerID = setTimeout(vfunc, this.interval);
		
		return this;
	},
	
	abort: function(){
		clearTimeout(this.timerID);
		this.timerID = null;
		
		return this;
	},
	
	reset: function(){
		this.abort();
		this.start();
		
		return this;
	},
	
	proceed: function(){
		this.func.apply(this.obj, this.parameters);
		
		return this;
	}
}

function Xt(startValue, endValue, duration, mode, func, timeStep){
	this.startValue = startValue;
	this.endValue   = endValue;
	this.duration   = duration;
	this.mode       = mode;
	this.func       = func;
	this.timeStep   = (timeStep ? timeStep : 100);
	
	this.path       = null;
	this.pos        = null;
	this.Xi         = null;
	
	this.prepare();
	
	return this;
}

Xt.prototype = {
	//TODO: in futuro mettere dei listener
	prepare: function(){
		switch(this.mode){
			//TODO: mettere altre modalità: logaritmica, doppia logaritmica ecc.
			default:
			case 'linear':
			var steps = Math.floor(this.duration / this.timeStep);
			var step  = Math.floor((this.endValue - this.startValue) / steps);
			
			this.path = new Array();
			for(var i = 1; i < steps; ++i) this.path.push(this.startValue + step * i);
			this.path.push(this.endValue);
			break;
		}
		
		return this;
	},
	
	start: function(){
		this.pos = 0;
		this.Xi = new Xi(this, 'interval', this.doTransition, this.timeStep);
		this.Xi.start();
		
		return this;
	},
	
	doTransition: function(){
		this.func(this.path[this.pos++]);
		if(this.pos == this.path.length) this.Xi.abort();
	}
}
