/*
 * JavaScript Event framework: js
 * Version 0.2.0a
 * Dmitrij Sosnovsenko
 * biodiscus@gmail.com
 *
 * last update: 2011-12-13
 */

/*
 + onSglClick - real single click and sindgle touch, ignore* double click oder double touche
 js(sel).addEvent('sglclick', handler, arguments);

 + onDblClick - real double click and double touche, ignore* single click or single touche
 1. in JS
 js(sel).addEvent('dblclick', handler, arguments);
 js.event.add(element, 'dblclick', handler, arguments);
 2. in line of html
 <elm onmouseup="js.event.dblclick(this, handler, arguments)">

 * - the onSglClick and onDblClick you can apply to same element

 + onDblClick - <+double touche

 // TODO: https://github.com/343max/jquery.nestedTouch/blob/master/js/jquery.nestedTouch.js
 */

;js.event= {
	guid: 0,
	prev: '',
	touchable: 0,

	_init: function(event){
		event= event || window.event;

		if(event.isInit){return event;}
		event.isInit = true ;

		event.preventDefault= event.preventDefault || function(){this.returnValue= false;}
		event.stopPropagation= event.stopPropagaton || function(){this.cancelBubble= true;}

		// for IE
		if(!event.target){
			event.target= event.srcElement || document;
		}
		// safari
		if(event.target.nodeType == 3){
			event.target= event.target.parentNode;
		}
		// for IE
		if(!event.relatedTarget && event.fromElement){
			event.relatedTarget= event.fromElement == event.target ? event.toElement : event.fromElement;
		}

		//pageX/pageY for IE
		if(!event.touches && event.pageX == null && event.clientX != null){
			var doc = document.documentElement, body = document.body;
			event.posX= event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc.clientLeft || 0);
			event.posY= event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc.clientTop || 0);
		}else
		// mobile
		if(event.touches){
			if(event.touches[0]){
				event.posX= event.touches[0].clientX;
				event.posY= event.touches[0].clientY;
			}
		}else{
			event.posX= event.pageX;
			event.posY= event.pageY;
		}

		// mouse button for IE, // 1 == left; 2 == middle; 3 == right
		if(!event.which && event.button){
			event.which= (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) ));
		}

		// for key events
		if(!event.which && ((event.charCode || event.charCode === 0) ? event.charCode : event.keyCode)){
			event.which= event.charCode || event.keyCode;
		}

		// Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs)
		if(!event.metaKey && event.ctrlKey){
			event.metaKey = event.ctrlKey;
		}

		return event;
	},

	// call all handles by the event; this == element
	_call: function(event){
		for(var guid in this.events[event.type]){
			var args;
			if(['sglclick','dblclick'].indexOf(event.type) >= 0){
				args= this.events.args[js.event.mouseup][event.type]
			}else{
				args= this.events.args[event.type][guid];
			}
			var res= this.events[event.type][guid].call(this, event, args);
			js.event.prev= event.type;
			if(event.isInit && res === false ){
				event.preventDefault();
				event.stopPropagation();
			}
		}
	},

	add: function(elm, type, handler, args){
		if(!elm){return false;}
		type= type.toLowerCase();
		// IE-bugfix
		if(elm.setInterval && ( elm != window && !elm.frameElement )){elm = window;}

		// events[type]
		if(!handler.guid){
			if(!this.guid){this.guid= 0}
			handler.guid = ++this.guid;
		}
		// bugfix - event after unload
		if(!elm.events){
			elm.events= {};
			elm.handle= function(event){
				if(!js.event){return}
				event= js.event._init(event);
				js.event._call.call(elm, event);
				return;
			}
		}
		// single click and double click
		if(['sglclick','dblclick'].indexOf(type) >= 0){
			var method= type;
			var guid= handler.guid;
			if(!elm.events[type]){elm.events[type]={}}
			elm.events[type][guid]= handler;
			handler= function(){js.event[method].call(js.event[method],elm)}
			handler.guid= type;
			type= js.event.mouseup;
		}
		// init event
		if(!elm.events[type]){
			elm.events[type]= {};
			elm.addEventListener ? elm.addEventListener(type, elm.handle, false) : elm.attachEvent('on' + type, elm.handle);
		}
		// add user event
		if(!elm.events[type][handler.guid]){
			elm.events[type][handler.guid]= handler;
		}
		// save arguments to handler
		if(!elm.events.args){
			elm.events.args= {}
		}
		if(!elm.events.args[type]){
			elm.events.args[type]= {}
		}
		elm.events.args[type][handler.guid]= args ? args : null;
	},

	remove: function(elm, type, handler){
		type= type.toLowerCase();

		var handlers= elm.events && elm.events[type];
		if(!handlers){return;}
		delete handlers[handler.guid];

		for(var any in handlers){return;}

		// wenn alle handlers in type-method gesöscht sind, DANN muss die aus mouseup[type] auch gelöscht werden
		if(['sglclick','dblclick'].indexOf(type) >= 0){
			delete elm.events[type];
			var clickType= js.event.mouseup;
			if(elm.events[clickType] && elm.events[clickType][type]){
				delete elm.events[clickType][type];
				for(var any in elm.events[clickType]){return;}
				type= clickType;
 			}
 		}

		elm.removeEventListener ? elm.removeEventListener(type, elm.handle, false) : elm.detachEvent('on' + type, elm.handle);
		delete elm.events[type];

		for(var any in elm.events){return;}
		try {
			delete elm.handle;
			delete elm.events;
		} catch(e) { // IE
			elm.removeAttribute('handle');
			elm.removeAttribute('events');
		}
	},

	copy: function(elm1, elm2){
		for(var type in elm1.events){
			if(type == 'args'){continue;}
			for(var handler in elm1.events[type]){//alert(elm1.events.args+ ' - '+type);
				js.event.add(elm2, type, elm1.events[type][handler], elm1.events.args[type][handler]);
			}
		}
	},

	sglclick: function(elm){
		var self= this;
		if(!self.count){self.count= 0}
		if(!self.count){
			setTimeout(function(){
				if(self.count <= 1){
					js.event._call.call(elm,{type:'sglclick'});
				}
				self.count=0;
			},js.event.dblclick.timeout+10);
		}
		self.count++;
	},

	dblclick: function(elm){
		var self= this.dblclick || this;
		self.id= '';
		self.timer= null;
		if(!self.count){self.count= 0}
		if(!elm.id){elm.id= js.uniqId();}
		// TODO: if(self.id != elm.id){self.id= elm.id;self.count=0;}js('debug').elm.innerHTML+= self.count+'='+self.id+' - '+elm.id+' | ';
		if(!self.click){
			self.timer= setTimeout(function(){self.count=0},self.timeout);
		}
		self.count++;
		if(self.count < 2){return false;}

		clearTimeout(self.timer);
		self.count=0;
		if(arguments[1]){try{return arguments[1].call(elm)}catch(e){}}
		js.event._call.call(elm,{type:'dblclick'});
	},

	getTarget: function(event){
		if(!event){var event= window.event;}
		if(!event){return null;}
		return event.target || event.srcElement;
	},

	stopSelection: function(){
		window.getSelection ? window.getSelection().removeAllRanges() : document.selection.empty();
	}

}// event

js.event.dblclick.timeout= 240;
js.event.touchable= ('ontouchstart' in document.documentElement) ? 1:0;
if(js.event.touchable){
	js.event.mousedown= 'touchstart';
	js.event.mouseup= 'touchend';
	js.event.mousemove= 'touchmove';
}else{
	js.event.mousedown= 'mousedown';
	js.event.mouseup= 'mouseup';
	js.event.mousemove= 'mousemove';
}

// set event by type; call handler(event, args)
js.addEvent= function(type, handler, args){
	js.event.add(this.elm,type, handler, args);
}
js.removeEvent= function(type, handler){
	js.event.remove(this.elm,type, handler);
}

js.drag= function(opt){
	var elmDrag= null;
	js.drag.mouseOffset= {x:0,y:0};

	var axis_x= (opt.axis && opt.axis == 'x') ? 1:0;
	var axis_y= (opt.axis && opt.axis == 'y') ? 1:0;
	if(!axis_x && !axis_y){axis_x=1;axis_y=1}

	var start= function(e, opt){
		elmDrag= this;
		js.drag.isDrag= true;
		// TODO: correct position calc
		var pos= $(elmDrag).position();
		js.drag.mouseOffset= {
			x: e.posX - pos.left,
			y: e.posY - pos.top
		}
		elmDrag.style.position= 'absolute';
		try{opt.onStart.call(elmDrag,e);}catch(err){}

		js.event.add(document, js.event.mousemove, move, opt);
		js.event.add(document, js.event.mouseup, stop, opt);
		js.event.stopSelection();
		return false;
	}

	var move= function(e, opt){
		var mouseOffset= js.drag.mouseOffset;
		if(axis_x){
			elmDrag.style.left= (e.posX - mouseOffset.x) + 'px';
		}
		if(axis_y){
			elmDrag.style.top= (e.posY - mouseOffset.y) + 'px';
		}

		var res= true;
		try{res= opt.onMove.call(elmDrag,e);}catch(err){}
		if(res === false){stop(e, opt)}
		return false;
	}

	var stop= function(e, opt){
		js.event.remove(document, js.event.mousemove, move);
		js.event.remove(document, js.event.mouseup, stop);
		try{opt.onStop.call(elmDrag,e);}catch(err){}
		elmDrag= null;
		js.drag.isDrag= false;
		return false;
	}
	js.event.add(this.elm, js.event.mousedown, start, opt);
}


js.event.keyCode= {
	BACKSPACE: 8,
	CAPS_LOCK: 20,
	COMMA: 188,
	CONTROL: 17,
	DELETE: 46,
	DOWN: 40,
	END: 35,
	ENTER: 13,
	ESCAPE: 27,
	HOME: 36,
	INSERT: 45,
	LEFT: 37,
	NUMPAD_ADD: 107,
	NUMPAD_DECIMAL: 110,
	NUMPAD_DIVIDE: 111,
	NUMPAD_ENTER: 108,
	NUMPAD_MULTIPLY: 106,
	NUMPAD_SUBTRACT: 109,
	PAGE_DOWN: 34,
	PAGE_UP: 33,
	PERIOD: 190,
	RIGHT: 39,
	SHIFT: 16,
	SPACE: 32,
	TAB: 9,
	UP: 38
}
