/* eventManager.js, WAJAF, the WebAbility(r) Javascript Application Framework
 (c) 2008 Philippe Thomassigny
 */

function _eventManager() {
	var self = this;
	this.listenerid = 1;
	this.functionid = 1;
	this.events = {};
	this.flushs = [];
	this.keys = [];
	this.specialkeys = {
		'esc' :27,
		'escape' :27,
		'tab' :9,
		'space' :32,
		'return' :13,
		'enter' :13,
		'backspace' :8,
		'scrolllock' :145,
		'capslock' :20,
		'numlock' :144,
		'pause' :19,
		'break' :19,
		'insert' :45,
		'home' :36,
		'delete' :46,
		'end' :35,
		'pageup' :33,
		'pagedown' :34,
		'left' :37,
		'up' :38,
		'right' :39,
		'down' :40,
		'f1' :112,
		'f2' :113,
		'f3' :114,
		'f4' :115,
		'f5' :116,
		'f6' :117,
		'f7' :118,
		'f8' :119,
		'f9' :120,
		'f10' :121,
		'f11' :122,
		'f12' :123
	};

	// eventname: one of: mousedown, mouseup, click, dblclick, mousemove,
	// mouseover, mouseout, mousewheel,
	// keydown, keyup, keypress, load, unload, scroll,
	// focus, blur, change, submit, abort, error, reset, resize
	// eventnode: the id or the node itself
	// eventfunction: pointer to the function to execute when the event happens
	// eventcapture: true/false to notify if this event is greedy
	this.addListener = this.on = this.add = this.start = this.listen = this.attachEvent = this.registerEvent = addListener;
	function addListener(eventname, eventnode, eventfunction, eventcapture) {

		if (typeof eventnode == 'string')
			eventnode = $(eventnode);
		if (!eventnode) // no node found ?
			return false;

		// link the UID to the node
		if (eventnode.listeneruid == undefined)
			eventnode.listeneruid = self.listenerid++;
		if (eventfunction.functionuid == undefined)
			eventfunction.functionuid = self.functionid++;

		if (self.events[eventnode.listeneruid] == undefined)
			self.events[eventnode.listeneruid] = {};
		if (self.events[eventnode.listeneruid][eventname] == undefined)
			self.events[eventnode.listeneruid][eventname] = {};

		// get the context from the ID of the node if any
		eventnode.context = WA.context;

		thefunction = function() {
			var xid = oldcontext = null;
			if (this.id && this.id.indexOf('-') != -1) {
				xid = WA.parseID(this.id);
				oldcontext = WA.setContext(xid[0] + '-' + xid[1] + '-');
			}
			var ret = eventfunction.apply(this, arguments);
			if (xid)
				WA.setContext(oldcontext);
			return ret;
		}

		if (eventname == 'load' && browser.msie) // special incompatible IE
													// not firing onload event
		{
			thefunction = function() {
				if (this.readyState != 'complete'
						&& this.readyState != 'loaded')
					return null;
				var xid = oldcontext = null;
				if (this.id && this.id.indexOf('-') != -1) {
					xid = WA.parseID(this.id);
					oldcontext = WA.setContext(xid[0] + '-' + xid[1] + '-');
				}
				var ret = eventfunction.apply(this, arguments);
				if (xid)
					WA.setContext(oldcontext);
				return ret;
			};
			eventnode.onreadystatechange = thefunction;
		} else if (eventnode.addEventListener) {
			if (eventname == 'mousewheel') // special incompatible mousewheel
			{
				eventnode.addEventListener('DOMMouseScroll', thefunction,
						eventcapture);
			}
			eventnode.addEventListener(eventname, thefunction, eventcapture);
		} else if (eventnode.attachEvent) {
			eventnode.attachEvent('on' + eventname, thefunction);
		} else {
			eventnode['on' + eventname] = thefunction;
		}
		self.events[eventnode.listeneruid][eventname][eventfunction.functionuid] = thefunction;
		return true;
	}

	// must be the SAME PARAMETERS as addListener
	this.removeListener = this.off = this.remove = this.stop = this.detachEvent = removeListener;
	function removeListener(eventname, eventnode, eventfunction, eventcapture) {
		if (typeof eventnode == 'string')
			eventnode = $(eventnode);
		if (!eventnode) // no node found ?
			return;
		if (eventnode.listeneruid == undefined) // node not registered here
			return;
		if (eventfunction.functionuid == undefined) // function not registered
													// here
			return;
		if (self.events[eventnode.listeneruid] == undefined) // already
																// unregistered
																// ?
			return;
		if (self.events[eventnode.listeneruid][eventname] == undefined) // already
																		// unregistered
																		// ?
			return;
		if (self.events[eventnode.listeneruid][eventname][eventfunction.functionuid] == undefined) // already
																									// unregistered
																									// ?
			return;

		if (eventname == 'load' && browser.msie) // special incompatible IE
													// not firing onload event
		{
			eventnode.onreadystatechange = nothing;
		} else if (eventnode.removeEventListener) {
			if (eventname == 'mousewheel') {
				eventnode
						.removeEventListener(
								'DOMMouseScroll',
								self.events[eventnode.listeneruid][eventname][eventfunction.functionuid],
								eventcapture);
			}
			eventnode
					.removeEventListener(
							eventname,
							self.events[eventnode.listeneruid][eventname][eventfunction.functionuid],
							eventcapture);
		} else if (eventnode.detachEvent) {
			eventnode
					.detachEvent(
							'on' + eventname,
							self.events[eventnode.listeneruid][eventname][eventfunction.functionuid]);
		} else {
			eventnode['on' + eventname] = null;
		}
		delete self.events[eventnode.listeneruid][eventname][eventfunction.functionuid];
		// *********************************************
		// do we clean the 3 levels of the array ?
		return;
	}

	// key is 'modif[+modif]+key'
	// modif is 'shift', 'alt', 'control' or 'ctrl'
	// key is 0-9, a-z, !@#$%^&*()_-+=}{]["';?><,./`~
	// can be also: special keys, arrows, functions etc. see the array in the
	// function.

	this.keyListener = keyListener;
	function keyListener(key, callback, node, bubble)
	{
		var xkey = key.toLowerCase().split("+");
		if (node == undefined)
			node = document;
		if (bubble == undefined)
			bubble = false;
		for (var i = 0, l = xkey.length; i < l; i++)
		{
			if (xkey[i] == 'shift' || xkey[i] == 'control' || xkey[i] == 'ctrl'
					|| xkey[i] == 'alt')
				continue;
		  if (self.specialkeys[xkey[i]] != undefined)
				continue;
			// should be normal char, we take the 1rst one to be sure
			xkey[i] = xkey[i].charAt(0);
		}

		var data = {
			skey :key,
			key :xkey,
			callback :callback,
			node :node,
			bubble :bubble
		};
		self.keys.push(data);
		return;
	}

	this.keycallback = keycallback;
	function keycallback(e)
	{
		var code = browser.getKey(e);
		var c = String.fromCharCode(code).toLowerCase();
		var shift = browser.ifShift(e);
		var ctrl = browser.ifCtrl(e);
		var alt = browser.ifAlt(e);
		for ( var i in self.keys)
		{
			// check any keys combination if ok
			var isok = 0;
			for ( var j in self.keys[i]['key'])
			{
				if (self.keys[i]['key'][j] == 'shift' && shift)
					isok++;
				else if (self.keys[i]['key'][j] == 'alt' && alt)
					isok++;
				else if ((self.keys[i]['key'][j] == 'control' || self.keys[i]['key'][j] == 'ctrl')
						&& ctrl)
					isok++;
				else if (self.specialkeys[self.keys[i]['key'][j]] == code)
					isok++;
				else if (self.keys[i]['key'][j] === c)
					isok++;
			}
			if (isok == self.keys[i]['key'].length)
			{
				self.keys[i]['callback'](e, self.keys[i]['skey']);
			}
		}
		return;
	}

	this.registerFlush = registerFlush;
	function registerFlush(functionflush) {
		self.flushs.push(functionflush);
		return;
	}

	this.flush = flush;
	function flush() {
		return;
		for (i in self.events) {
			for (j in self.events[i]) {
				for (k in self.events[i][j]) {
					if (self.events[i][j][k][0]) {
						if (j == 'mousewheel') {
							self.events[i][j][k][0].removeEventListener(
									'DOMMouseScroll', self.events[i][j][k][2],
									self.events[i][j][k][3]);
						}
						self.events[i][j][k][0].removeEventListener(j,
								self.events[i][j][k][2],
								self.events[i][j][k][3]);
					} else if (self.events[i][j][k][0].detachEvent) {
						self.events[i][j][k][0].detachEvent('on' + j,
								self.events[i][j][k][2]);
					} else {
						self.events[i][j][k][0]['on' + j] = null;
					}
				}
			}
		}

		// then call all flush for other managers
		for ( var i = 0, l = self.flushs.length; i < l; i++) {
			self.flushs[i]();
			self.flushs[i] = null;
		}

		// we stop listening unload and keypress

		delete self.events;
		delete self.flushs;
		delete self.keys;
		self = null;
		return;
	}

	// we take control of unload
	this.addListener('unload', window, this.flush, false);
	// we listen the key binder
	this.addListener('keydown', document, this.keycallback, false);
}

var eventManager = new _eventManager();
// rapid shortcuts
var on = eventManager.addListener;
var off = eventManager.removeListener;
var key = eventManager.keyListener;

