/****************************************************************
@name: /htdocs/js/lib/oggetti/Binder.js
@desc: Classe helper per il binding di eventi
@authors: Marco Biondi
@lastauthor: Marco Biondi

INFO:
Classe statica che abilita le funzioni di binding automatico su un determinato oggetto.

UTILIZZO:
Invocare nel costruttore dell'oggetto Binder.initialize(this);

TODO: documentare completamente

HISTORY:
*****************************************************************/

var Binder = {
	initialize: function (obj) {
		obj.doBindings = Binder.doBindings;
		obj.bindFunction = Binder.bindFunction;
		obj.unbindFunction = Binder.unbindFunction;
		obj.bindMultiFunction = Binder.bindMultiFunction;
		obj.unbindMultiFunction = Binder.unbindMultiFunction;
		obj.unbindAll = Binder.unbindAll;
		obj.findBindings = Binder.findBindings;
		obj.defineBind = Binder.defineBind;
		obj.defineMultiBind = Binder.defineMultiBind;
		obj.getBindObject = Binder.getBindObject;
		obj.getMultiBindObjects = Binder.getMultiBindObjects;
		obj.getOtherBindObjects = Binder.getOtherBindObjects;
		obj.isBindDefined = Binder.isBindDefined;
		obj.isMultiBindDefined = Binder.isMultiBindDefined;
		obj.findBindObjectFromEvent = Binder.findBindObjectFromEvent;
		obj.__observers__ = new Object();
		obj.__bindTable__ = new Object();
		obj.__mbindTable__ = new Object();
		obj.__mobservers__ = new Object();
		obj.__otherTable__ = new Object();
	},

	_mapBind: function(obj, fname, elem) {
		if(!(obj.__otherTable__[fname] instanceof Array))
			obj.__otherTable__[fname] = [];
		obj.__otherTable__[fname].push(elem);
	},

	_realFname: function(fname) {
		if(fname && fname.charAt(0) == '#') fname = fname.substr(1);
		return fname;
	},

	isBindDefined: function(fname) {
		fname = Binder._realFname(fname);
		return(typeof(this.__bindTable__[fname]) == 'object');
	},

	isMultiBindDefined: function(fname) {
		fname = Binder._realFname(fname);
		return(typeof(this.__mbindTable__[fname]) == 'object');
	},

	defineBind: function(fname, event, funct, capturePh) {
		fname = Binder._realFname(fname);
		if(!this.isBindDefined(fname))
		{
			this.__bindTable__[fname] = { 'event': event, 'fname': funct, 'capture': capturePh };
		}
		else
		{
			console.debug("Bind already defined: %s",fname);
		}
	},

	defineMultiBind: function(fname, event, funct, capturePh) {
		fname = Binder._realFname(fname);
		if(!this.isMultiBindDefined(fname))
		{
			this.__mbindTable__[fname] = { 'event': event, 'fname': funct, 'capture': capturePh };
		}
		else
		{
			console.debug("MultiBind already defined: %s",fname);
		}
	},

	getBindObject: function(fname) {
		fname = Binder._realFname(fname);
		try
		{
			return(this.__observers__[fname]['el']);
		}
		catch(err)
		{
			return(null);
		}
	},

	getMultiBindObjects: function(fname) {
		fname = Binder._realFname(fname);
		try
		{
			return(this.__mobservers__[fname]['el']);
		}
		catch(err)
		{
			return(null);
		}
	},

	getOtherBindObjects: function(fname) {
		fname = Binder._realFname(fname);
		try
		{
			return(this.__otherTable__[fname]);
		}
		catch(err)
		{
			return(null);
		}
	},

	doBindings: function (elem) {
		var found = this.findBindings(elem);
		for(var i = 0; i<found.length; i++)
		{
			if(this.isBindDefined(found[i]['fn']))
			{
				this.bindFunction(found[i]['fn'], found[i]['el']);
			}
			else if(this.isMultiBindDefined(found[i]['fn']))
			{
				this.bindMultiFunction(found[i]['fn'], found[i]['el']);
			}
			else if(this._bindHandler)
			{
				if(this._bindHandler(Binder._realFname(found[i]['fn']), found[i]['el']))
				{
					Binder._mapBind(this, Binder._realFname(found[i]['fn']), found[i]['el']);
				}
			}
			else
			{
				Binder._mapBind(this, Binder._realFname(found[i]['fn']), found[i]['el']);
			}
		}
		delete(found);
	},

	bindFunction: function(fname, elem) {

		if(fname.charAt(0) == '#')
		{
			fname = fname.substr(1);
			elem.style.cursor = _IEhack ? 'hand' : 'pointer';
		}

		this.unbindFunction(fname);

		if(this.__bindTable__[fname])
		{
			this.__observers__[fname] = {el: elem, fn: this.__bindTable__[fname]['fname'].bindAsEventListener(this, fname)};
			Event.observe(this.__observers__[fname]['el'], this.__bindTable__[fname]['event'], this.__observers__[fname]['fn'], this.__bindTable__[fname]['capture']);
		}
		else
		{
			console.debug("Unknown bind: %s",fname);
		}
	},

	unbindFunction: function(fname) {
		fname = Binder._realFname(fname);
		if(this.__observers__[fname])
		{
			Event.stopObserving(this.__observers__[fname]['el'], this.__bindTable__[fname]['event'], this.__observers__[fname]['fn'], this.__bindTable__[fname]['capture']);

			if(this.__observers__[fname]['el'].style)
				this.__observers__[fname]['el'].style.cursor = '';

			this.__observers__[fname]['el'] = null;
			delete(this.__observers__[fname]['fn']);
			delete(this.__observers__[fname]);
		}
	},

	bindMultiFunction: function(fname, elem) {

		if(fname.charAt(0) == '#')
		{
			fname = fname.substr(1);
			elem.style.cursor = _IEhack ? 'hand' : 'pointer';
		}

		if(this.__mbindTable__[fname])
		{
			if(!this.__mobservers__[fname])
			{
				this.__mobservers__[fname] = {el: [], fn: this.__mbindTable__[fname]['fname'].bindAsEventListener(this, fname)};
			}
			this.__mobservers__[fname]['el'].push(elem);
			Event.observe(elem, this.__mbindTable__[fname]['event'], this.__mobservers__[fname]['fn'], this.__mbindTable__[fname]['capture']);
		}
		else
		{
			console.debug("Unknown multi bind: %s",fname);
		}
	},

	unbindMultiFunction: function(fname, elem) {
		fname = Binder._realFname(fname);
		if(this.__mobservers__[fname])
		{
			var arr = this.__mobservers__[fname]['el'];
			if(arguments.length == 2)
			{
				Event.stopObserving(elem, this.__mbindTable__[fname]['event'], this.__mobservers__[fname]['fn'], this.__mbindTable__[fname]['capture']);
				for(var i=0; i < arr.length; i++)
				{
					if(arr[i] == elem)
					{
						if(elem.style)
							elem.style.cursor = '';

						arr.splice(i,1);
						break;
					}
				}
			}
			else
			{
				while(arr.length)
				{
					elem = arr.pop();
					if(elem.style)
						elem.style.cursor = '';
					Event.stopObserving(elem, this.__mbindTable__[fname]['event'], this.__mobservers__[fname]['fn'], this.__mbindTable__[fname]['capture']);
				}
			}

			if(arr.length == 0)
			{
				this.__mobservers__[fname]['el'] = null;
				delete(this.__mobservers__[fname]['fn']);
				delete(this.__mobservers__[fname]);
			}
		}
	},

	unbindAll: function() {
		for(var foo in this.__observers__)
		{
			if(this.__observers__.hasOwnProperty(foo))
				this.unbindFunction(foo);
		}

		for(var foo in this.__mobservers__)
		{
			if(this.__mobservers__.hasOwnProperty(foo))
				this.unbindMultiFunction(foo);
		}

		this.__otherTable__ = new Object();
	},

	findBindings: function(elem) {
		var res = [];
		if(elem.getAttribute('bindto'))
			res.push({fn: elem.getAttribute('bindto'), el: elem});
		var foo = elem.firstChild;
		while(foo)
		{
			if(foo.nodeType == 1)
				res = res.concat(this.findBindings(foo));
			foo = foo.nextSibling;
		}
		return(res);
	},

	findBindObjectFromEvent: function(ev, fname) {
		fname = Binder._realFname(fname);
		var res = Event.element(ev);
		var cbind = Binder._realFname(res.getAttribute('bindto'));
		while(res.parentNode && cbind != fname)
		{
			res = res.parentNode;
			cbind = Binder._realFname(res.getAttribute('bindto'));
		}
		return((cbind != fname) ? null : res);
	}
}