// Version 0.5 
var undefined;

function Xar(item){
	if(arguments.length === 0) { this.item = []; }
	
	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 = [];
		
		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(value){
		var newArray = [];
		
		for(var key in this.item){
			if(key != value) { newArray[key] = this.item[key]; }
		}
		
		return newArray;
	},
	
	keys: function(){
		var newArray = [];
		
		for(var key in this.item) { newArray.push(key); }
		
		return newArray;
	},
	
	values: function(){
		var newArray = [];
		
		for(var key in this.item) { newArray.push(this.item[key]); }
		
		return newArray;
	}
};

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      = [];
		this.item.Xu_events    = [];
		this.item.Xu_listeners = [];
	}
	
	this.addTagSpecificFunctions();
	delete this.addTagSpecificFunctions;
}

Xu.prototype = {
	getById: function(id){
		return document.getElementById(id);
	},
	
	getByTag: function(tag, num){
		var tmp = this.item.getElementsByTagName(tag);
		
		var results = [];
		for(var i = 0; i < tmp.length; ++i) { results.push(tmp[i]); }
		
		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 = [];
		var curClassName = null;
		var re = new RegExp('\\b' + className + '\\b');
		for(var i = 0; i < tmp.length; ++i){
			curClassName = tmp[i].className;
			if(curClassName === className || (curClassName.indexOf(' ') !== -1 && curClassName.match(re))) { 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] = []; }
		
		this.item.Xu_listeners[event].push([listener, funcName]);
		
		if(!this.item.Xu_events[event]){
			var emptyFunc = function(){ return true; };
			this.addEvent(event, emptyFunc);
		}
		
		return this;
	},
	
	removeListener: function(listener, event){
		if(!event){
			for(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 = []; }
		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] = [];
			
			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 x = 0; x < eventsLength; ++x) { propagate = this.Xu_events[type][x](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(func);
			}
		}
		
		this.item.Xu_events[event] = new Xar(this.item.Xu_events[event]).remove(func);
		
		return this;
	},
	
	purgeEvents: function(event){
		if(!event) { this.item.Xu_events = []; }
		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 this.item.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 = 0;
		var 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 [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){
			var tmp = [];
			for(var i = 0; i < this.item.childNodes.length; ++i) { tmp.push(this.item.childNodes[i]); }
			return tmp;
		}
		else if(pos == 0) { return this.item.firstChild; }
		else if(pos == -1) { return this.item.lastChild; }
		else{
			var tmp = [];
			for(var i = 0; i < this.item.childNodes.length; ++i) { tmp.push(this.item.childNodes[i]); }
			
			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;
	},
	
	//tag specific functions
	addTagSpecificFunctions: function(){
		if(this.item == null) { return; }
		
		switch(this.item.nodeType){
			case 1:
			switch(this.item.tagName.toLowerCase()){
				case 'select':
				this.setComboOptions = function(options, selected){
					for(var i = 0; i < options.length; i++) { this.item.options[i] = new Option(options[i].text, options[i].value); }
					this.item.options.length = options.length;
					this.item.selectedIndex = (selected == undefined || selected < 0 || selected > options.length ? 0 : selected);
					return this;
				}
				
				this.getComboValue = function(what){
					if(what == undefined) { return this.item.selectedIndex; }
					switch(what){
						case 'text': return this.item.options[this.item.selectedIndex].text;
						case 'value': return this.item.options[this.item.selectedIndex].value;
						case 'object':
						case 'option':
						case 'both': return this.item.options[this.item.selectedIndex];
						default: return null;
					}
				}
				break;
			}
			break;
		}
	}
}



function Xf(items){
	this.source = null;
	this.items  = null;
	
	if(items !== null && items !== undefined){
		if(items instanceof Array || (typeof NodeList != 'undefined' && items instanceof NodeList)){
			this.items  = [];
			this.source = [];
			var tmp = null;
			
			for(var i = 0; i < items.length; ++i){
				if(items[i].nodeType != 1) { continue; }
				tmp = new Xu(items[i]);
				this.items.push(tmp);
				this.source.push(tmp);
			}
		}
		else if(items.nodeType == 1){
			this.items  = [];
			this.source = [];
			items       = new Xu(items);
			
			this.items.push(items);
			this.source.push(items);
		}
	}
}

Xf.prototype = {
	reset: function(){
		if(this.source === null) { this.items = null; }
		else{
			this.items = [];
			for(var i = 0; i < this.source.length; ++i) { this.items.push(this.source[i]); }
		}
		
		return this;
	},
	
	consolidate: function(){
		if(this.items === null) { this.source = null; }
		else{
			this.source = [];
			for(var i = 0; i < this.items.length; ++i) this.source.push(this.items[i]);
		}
		
		return this;
	},
	
	getXu: function(pos){
		if(this.items === null) { return null; }
		
		if(pos === undefined) { return this.items; }
		else{
			if(pos >= 0) { return (pos < this.items.length ? this.items[pos] : null); }
			else         { return (this.items.length + pos >= 0 ? this.items[this.items.length + pos] : null); }
		}
	},
	
	get: function(pos){
		var items = this.getXu(pos);
		
		if(items === null) { return null; }
		else if(items instanceof Array) {
			for(var i = 0; i < items.length; ++i) { items[i] = items[i].get(); }
			return items;
		}
		else return new Xu(items);
	},
	
	filterByVar: function(varName, varValue){
		if(this.items === null) return;
		var tmp = [];
		for(var i = 0; i < this.items.length; ++i){
			if(this.items[i].getVar(varName) !== varValue) continue;
			tmp.push(this.items[i]);
		}
		this.items = tmp;
		
		return this;
	},
	
	filterByTag: function(tagName){
		if(this.items === null) return;
		
		tagName = tagName.toLowerCase();
		
		var tmp = [];
		for(var i = 0; i < this.items.length; ++i){
			if(this.items[i].getVar('tagName').toLowerCase() !== tagName) continue;
			tmp.push(this.items[i]);
		}
		this.items = tmp;
		
		return this;
	},
	
	filterByClass: function(className, multiclass){
		if(this.items === null) return;
		var tmp = [];
		if(!multiclass){
			for(var i = 0; i < this.items.length; ++i){
				if(this.items[i].getVar('className') !== tagName) continue;
				tmp.push(this.items[i]);
			}
		}
		else{
			var curClassName = null;
			var classes      = null;
			var found        = null;
			for(var i = 0; i < this.items.length; ++i){
				curClassName = this.items[i].getVar('className');
				if(curClassName.match(' ')){
					classes = curClassName.split(' ');
					found = false;
					for(var x = 0; x < classes.length; ++x){
						if(classes[x] !== className) continue;
						found = true;
						break;
					}
					if(!found) continue;
				}
				else if(curClassName !== tagName) continue;
				
				tmp.push(this.items[i]);
			}
		}
		this.items = tmp;
		
		return this;
	}
}

function Xi(obj, method, func, interval, parameters){
	this.obj        = obj;
	this.method     = method;
	this.func       = func;
	this.parameters = (parameters ? parameters : []);
	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 = [];
			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(); }
	}
}
