/*
 * nwevents.js
 *
 * Author: Diego Perini <diego.perini at gmail com>
 * Version: 1.0
 * Release: 20061022
 * License: GPL/CC
 *
 * Based on previous work by Dean Edwards (see blog)
 * http://dean.edwards.name/weblog/2005/10/add-event2/
 *
 */

// modify handlers execution order (needed in capturing)
var EVENTS_FIFO=true;

// use event registration (DOM2) or inline events (DOM1)
var EVENTS_W3C=true;

// missing in IE Event model
if(typeof Event=='undefined'){
	var Event={
		NONE:0,
		CAPTURING_PHASE:1,
		AT_TARGET:2,
		BUBBLING_PHASE:3,
		STOP:4
	};
}

// add event
var _addEvent=(window.addEventListener&&EVENTS_W3C)?
	function(o,t,h,c){
		return o.addEventListener(t,h,c||false);
	}:(window.attachEvent&&EVENTS_W3C)?
	function(o,t,h,c){
		if(c&&/mouse|click/.test(t)&&o.nodeType==1){o.setCapture();}
		return o.attachEvent('on'+t,h);
	}:
	function(o,t,h,c){
		if(c&&o.nodeType==1){o.captureEvents(Event[t.toUpperCase()]);}
		o['on'+t]=function(e){handleEvent.call(this,e);};
		return true;
	};

// remove event
var _removeEvent=(window.removeEventListener&&EVENTS_W3C)?
	function(o,t,h,c){
		return o.removeEventListener(t,h,c||false);
	}:(window.detachEvent&&EVENTS_W3C)?
	function(o,t,h,c){
		if(c&&/mouse|click/.test(t)&&o.nodeType==1){o.releaseCapture();}
		return o.detachEvent('on'+t,h);
	}:
	function(o,t,h,c){
		if(c&&o.nodeType==1){o.releaseEvents(Event[t.toUpperCase()]);}
		o['on'+t]=null;
		return true;
	};

// add handler to chain
var addListener=
	function(o,t,h,c){
		o.Listeners=o.Listeners||{};
		o.Listeners[t]=o.Listeners[t]||{items:[]};
		if(inArray(o.Listeners[t].items,h)===false){
			o.Listeners[t].items.push(h);
			if(o.Listeners[t].items.length===1){
				o.Handler=(!window.addEventListener||window.opera)?
					function(e){
						handleEvent.call(o,e);
					}:handleEvent;
				return _addEvent(o,t,o.Handler,c);
			}else{
				return true;
			}
		}
		event_log('addListener',arguments);
		return false;
	};

// remove handler from chain
var removeListener=
	function(o,t,h,c){
		if(o.Listeners&&o.Listeners[t]){
			var key=inArray(o.Listeners[t].items,h);
			if(key!==false){
				o.Listeners[t].items.splice(key,1);
				if(o.Listeners[t].items.length===0){
					var r=_removeEvent(o,t,o.Handler,c);
					delete o.Listeners[t];
					return r;
				}else{
					return true;
				}
			}
		}
		event_log('removeListener',arguments);
		return false;
	};

// handle chain for event
var handleEvent=
	function(e){
		var i=0,r,t=e.type;
		if(!window.addEventListener){e=fixEvent.call(this,e);}
		if(this.Listeners&&this.Listeners[t]){
			var f=this.Listeners[t];
			if(EVENTS_FIFO===true){
				// process chain in fifo order
				for(i=0;f.items.length>i;i++){
					if(typeof f.items[i]=='function'){
						r=f.items[i].call(this,e);
						if(r===false){break;}
					}
				}
			}else{
				// process chain in lifo order
				for(i=f.items.length-1;i>=0;i--){
					if(typeof f.items[i]=='function'){
						r=f.items[i].call(this,e);
						if(r===false){break;}
					}
				}
			}
		}
		return r;
	};

// fix IE event properties to comply with W3C standards
var fixEvent=
	function(e){
		e.currentTarget=this;
		e.target=e.target||e.srcElement;
		e.eventPhase=e.target==e.currentTarget?Event.AT_TARGET:Event.BUBBLING_PHASE;
		if(e.target&&(/3|4/).test(e.target.nodeType)){
			e.target=e.target.parentNode;
		}
		if(!e.relatedTarget){
			if(e.type=='mouseout'){e.relatedTarget=e.toElement;}
			if(e.type=='mouseover'){e.relatedTarget=e.fromElement;}
		}
		e.preventDefault=fixEvent.preventDefault;
		e.stopPropagation=fixEvent.stopPropagation;
		return e;
	};

fixEvent.preventDefault=
	function(){
		this.returnValue=false;
	};

fixEvent.stopPropagation=
	function(){
		this.cancelBubble=true;
	};

// block event
var stopEvent=
	function(e){
		if(e.stopPropagation){e.stopPropagation();}else{e.cancelBubble=true;}
		if(e.preventDefault){e.preventDefault();}else{e.returnValue=false;}
		return false;
	};

// log message to warn about event inconsistencies
function event_log(fname,args){
	alert(fname+' error ! '+(args[0].nodeName||'#window')+' - '+args[1]+'\n'+
		(/add/.test(fname)?'Adding duplicate event handler:':'Removing non existent event handler:')+'\n\n'+args[2]);
}

// find item value in array, return index or false
function inArray(a,v){
	var i=0,found=false;
	for(;a.length>i;i++){
		if(a[i]===v){
			found=i;
			break;
		}
	}
	return found;
}

