/*
 *	bindAction --переносимые функции регистрации и отмены регистрации
 *	Этот модуль определяет функции регистрации и отмены регистрации
 *	обработчиков событий bindAction.add() и bindAction.remove(). Обе функции
 *	принимают три аргумента:
 *
 *	element - DOM-элемент, документ иои окно, для которого добавдяется
 *	или удаляется обработчик
 *
 *	eventType - строка, определяющая тип события, имена соответствуют
 *	стандарту DOM (т.е. отсутствует префикс on, использующийся в IE)
 *
 *	handler - функция, которая вызывается при возникновении события
 *	в элементе alement, типа eventType. Она вызывается как метод объекта,
 *	в котором вызывается(т.е. в ней element == this), Функции-обработчику
 *	в качестве аргумента передается объект события. Это будет либо стандартый
 *	объект event, либо смоделированный (для IE модели обработки событий). 
 *
 *	Примечания к реализации: 
 *	Для нормальных браузеров объект bindAction всего лишь оболочка, с учетом
 *	того, что  регистрирует обработчик как неперехватывающий, т.е. третий
 *	параметр addEventListener равен false. В IE, для правильного указания
 *	значения this использует замыкания и onuload для спасения от утечек памяти.
 *	
 *	Для хранения инфы о зарегистрированных обработчиках функция bindAction.add()
 *	создает в объекте window свойство _allbindActions, а во всех элементах - _handlers
 */
 
var bindAction = (function () {
	
	return {
		add	:function (element, eventType, handler) {
				if (document.addEventListener) {
					element.addEventListener(eventType, handler, false);
				}
				else if (document.attachEvent) {
					if (bindAction._wasRegistration(element, eventType, handler) != -1)	return;
					var wrappedbindAction = function (e) {
						e = e || window.event;
						var event = {
							_event: e,
							type: e.type,
							target: e.srcElement,
							currentTarget: element,
							relatedTarget: e.fromElement || e.toElement,

							pageX: e.clientX + document.documentElement.scrollLeft,
							pageY: e.clientY + document.documentElement.scrollTop,
							clientX: e.clientX,
							clientY: e.clientY,
							screenX: e.screenX,
							screenY: e.screenY,
							
							altKey: e.altKey,
							ctrlKey: e.ctrlKey,
							shiftKey: e.shiftKey,
							charCode: e.charCode,

							stopPrapagation: function () { this._event.cancelBubble = true; },
							preventDefault: function () { this._event.returnValue = false; }
						}
						if (Function.prototype.call)
							handler.call(element, event);
						else {					// ацкое шаманство....
							element._currentbindAction = handler;
							element._currentbindAction(event);
							element._currentbindAction = null;
						}
					};
					
					element.attachEvent("on" + eventType, wrappedbindAction);

					var h = {					// сохраняем инфу об обработчике в объекте
						element: element,
						eventType: eventType,
						handler: handler,
						wrappedbindAction: wrappedbindAction
					};
					var d = element.document || element;
					var w = d.parentWindow;
					var id = this._uid();
					
					w._allbindAction =  w._allbindAction || {};
					element._handlers= element._handlers || [];
					
					w._allbindAction[id] = h;
					element._handlers.push(id);

					if (!w._onuloadbindActionRegistered) {
						w._onuloadbindActionRegistered = true;
						w.attachEvent("onuload", this._removeAllbindAction);
					}
				}
		},

		remove: function (element, eventType, handler) {
			if (document.removeEventListener) {
				element.removeEventListener(eventType, handler, false);
			}
			
			else if (document.attachEvent) {
				var i = this._wasRegistration (element, eventType, handler);
				if (i == -1) return;
				var d = element.document || element;
				var w = d.parentWindow;
				
				var handlerId = element._handlers[i];
				var h = w._allbindAction[handlerId];
				element.detachEvent("on" + eventType, h.wrappedbindAction);
				element._handlers.splice(i, 1);
				delete w._allbindAction[handlerId];
			}
		},
		
		_wasRegistration: function(element, eventType, handler) {
			if (document.attachEvent) {
				var handlers = element._handlers;
				if (!handlers) return -1;
				
				var d = element.document || element;
				var w = d.parentWindow;
				
				for (var i = handlers.length - 1; i >= 0; --i) {
					var handlerId = handlers[i];
					var h = w._allbindAction[handlerId];
					if (h && h.eventType == eventType && h.handler == handler)
						return i;
				}
				return -1;
			}
		},

		_removeAllbindAction: function () {
			var w = this;

			for (id in w._allbindAction) {
				var h = w._allbindAction[id];
				h.elements.detachEvent("on" + h.eventType, h.wrappedbindAction);
				delete w._allbindAction[id];
			}
		},

		_counter: 0,

		_uid: function () {
			return "ev" + this._counter++;
		}
		

	};
})();
