var EventFactory = {
	create: function(type, listener, object, add){
		this._setLinkToObject(object);

		switch (typeof(type)) {
			case 'string':
				return this._create([type], listener, object, add)[0];
			case 'object':
				return this._create(type, listener, object, add);
		}

		return null;
	},
	add: function(type, object){
		this._setEvent(type, object, true);
	},
	remove: function(type, object){
		this._setEvent(type, object, false);
	},
	_setEvent: function(type, object, set){
		var listeners = object.events[type];
		for (var i = 0, l = listeners.length; i < l; i++){
			if (set){
				listeners[i].add();
			}else{
				listeners[i].remove();
			}
		}
	},
	_create: function(type, listener, object, add){
		var eventsCount = type.length, events = new Array(eventsCount);
		for (var i = 0; i < eventsCount; i++){
			events[i] = this._createEvent(type[i], listener, object, add);
		}
		return events;
	},
	_createEvent: function(type, listener, object, add){
		if (!object.events[type]){
			object.events[type] = [];
		}
		var len = object.events[type].length;
		object.events[type][len] = new AEventListener(type, listener, object, add);
		return object.events[type][len];
	},
	_setLinkToObject: function(object){
		if (!object.events){
			object.events = [];
		}
	}
}

function AEventListener(type, listener, object, add, useCapture)
{
	this.type = type;
	this.listener = listener;
	this.object = object;
	this.useCapture = useCapture || false;

	var _this = this;

	function _listener(e)
	{
		_this.listener(_this._handleEvent(e));
	}

	this._listener = _listener;

	this._on_type = 'on' + type;
	this._added = false;

	if (add || false) {
		this.add();
	}
}

AEventListener.prototype =
{
	add: function()
	{
		if (this._added) return;

		this._add();
		this._added = true;
	},

	remove: function()
	{
		if (!this._added) return;

		this._remove();
		this._added = false;
	},

	_handleEvent: function(e)
	{
		return e;
	}
};

if (document.addEventListener) {

	// DOM functions
	AEventListener.prototype._add = function()
	{
		this.object.addEventListener(this.type, this._listener, this.useCapture);
	};

	AEventListener.prototype._remove = function()
	{
		this.object.removeEventListener(this.type, this._listener, this.useCapture);
	};

} else if (document.attachEvent) {

	// IE functions
	function __add()
	{
		this.object.attachEvent(this._on_type, this._listener);
	}
	AEventListener.prototype._add = __add;

	function __remove()
	{
		this.object.detachEvent(this._on_type, this._listener);
	}
	AEventListener.prototype._remove = __remove;

	function __handleEvent(evt)
	{
		var e = {};
		for (var i in evt) {
			e[i] = evt[i];
		}

		var type = e.type;

		e.timeStamp = new Date().getTime();
		e.charCode = ((type == 'keypress') ? e.keyCode : 0);
		e.isChar = (e.charCode > 0);
		e.target = e.srcElement;
		e.metaKey = e.altKey;

		e.preventDefault = function()
		{
			evt.returnValue = false;
		};

		e.stopPropagation = function()
		{
			evt.cancelBubble = true;
		};

		if (type == 'mouseout') {
			e.relatedTarget = e.toElement;
		} else if (type == 'mouseover') {
			e.relatedTarget = e.fromElement;
		}

		if (typeof(e.button) != 'undefined') {
			switch (e.button) {
				case 1: // left
					e.button = 0;
					break;

				case 4: // middle
					e.button = 1;
					break;

				case 2: // right
					e.button = 2;
					break;
			}
		}

		var root = document.body;
		if (!root) {
			root = document.documentElement;
		}

		e.pageX = e.clientX + root.scrollLeft;
		e.pageY = e.clientY + root.scrollTop;

		delete root;

		return e;
	}
	AEventListener.prototype._handleEvent = __handleEvent;

} else {

	// Old browsers functions
	AEventListener.prototype._add = function()
	{
		this.object[this._on_type] = this._listener;
	};

	AEventListener.prototype._remove = function()
	{
		this.object[this._on_type] = null;
	};
}


// Mouse Event
function AMouseEvent(type, object, dispatch)
{
	this.type = type;
	this.object = object;
	this.dispatch = dispatch || true;

	this._on_type = 'on' + type;

	this.event = this._createEvent();
	this._initEvent();

	if (this.dispatch) {
		this.dispatchEvent();
	}
}

AMouseEvent.prototype =
{
	_createEvent: function()
	{
		return document.createEvent('MouseEvents');
	},

	_initEvent: function()
	{
		this.event.initEvent(this.type, true, true);
	},

	dispatchEvent: function()
	{
		this.object.dispatchEvent(this.event);
	}
};

if (document.createEventObject) {
	function __createEvent()
	{
		return document.createEventObject();
	}
	AMouseEvent.prototype._createEvent = __createEvent;

	function __initEvent() {}
	AMouseEvent.prototype._initEvent = __initEvent;

	function __dispatchEvent()
	{
		this.object.fireEvent(this._on_type, this.event);
	}
	AMouseEvent.prototype.dispatchEvent = __dispatchEvent;
}


// ShortCutListener Object
/*
 keys = [
 	{charCode:XXX, keyCode:XXX, ctrl:false, alt:false, shift:false},
	{charCode:XXX, keyCode:XXX, ctrl:false, alt:false, shift:false}
	...
 ]

*/

function ShortCutListener(listener, object, keys)
{
	this.object = object || document;
	this.listener = listener;

	var _this = this;

	function checkKeyPress(e)
	{
		for (var i in keys) {
			var key = keys[i];
			var ch = key.ch || null;

			//alert(e.charCode);
			if (ch) {
				if (e.charCode == ch.toLowerCase().charCodeAt() || e.charCode == ch.toUpperCase().charCodeAt()) {
					return true;
				}
			} else {
				var code = key.charCode || -1;
				if (e.charCode == code) {
					return true;
				}
			}
		}

		return false;
	}

	function checkShortCut(e)
	{
		for (var i in keys) {
			var key = keys[i];
			var ctrl = key.ctrl || false;
			var alt = key.alt || false;
			var shift = key.shift || false;
			var ch = key.ch || null;

			var control = (e.ctrlKey == ctrl) && (e.altKey == alt) && (e.shiftKey == shift);

			//alert(e.keyCode);
			if (ch) {
				if ((e.keyCode == ch.toUpperCase().charCodeAt()) && control) {
					return true;
				}
			} else {
				var code = key.keyCode || -1;
				if ((e.keyCode == code) && control) {
					return true;
				}
			}
		}

		return false;
	}

	function isNotTarget(e)
	{
		var elem = e.target;
		var tag = elem.tagName.toLowerCase();

		var type = elem.type;
		if (tag == 'input' && (type == 'text' || type == 'password' || type == 'file' || type == 'search')) {
			return true;
		}
		if (tag == 'textarea') {
			return true;
		}
		return false;
	}

	this.keypress_listener = function(e)
	{
		if (isNotTarget(e)) {
			return true;
		}

		if (checkKeyPress(e)) {
			e.preventDefault();
			return false;
		}
		return true;
	};

	this.keyup_listener = function(e)
	{
		if (isNotTarget(e)) {
			return true;
		}

		if (checkShortCut(e)) {
			listener(e);
			return false;
		}
		return true;
	};

	this.keypressEventListener = new AEventListener('keypress', this.keypress_listener, this.object, false);
	this.keyupEventListener = new AEventListener('keyup', this.keyup_listener, this.object, false);

	this.add();
}

ShortCutListener.prototype =
{
	add: function() {
		this.keypressEventListener.add();
		this.keyupEventListener.add();
	},

	remove: function() {
		this.keypressEventListener.remove();
		this.keyupEventListener.remove();
	}
};

