/*!
 * Объект для управления обработчиками событий
 * @author Artur Burtsev
 * с использованием наработок Dean Edwards
 * @version 1.1.2
 */
var ajEvent = new function(){
	/**
	 * Счётчик для создания уникального идентификатора функции в IE
	 */
	this.guid = 1;

	/**
	 * Добавление обработчика к элементу
	 * @param {Element} elem елемент
	 * @param {String} type тип события
	 * @param {Function} handler обработчик
	 */
	this.bind = (function() {
		
		/**
		 * Приводит объект события в IE к стандартному виду
		 * @param {Event} e
		 * @return e
		 */
		var fixEvent = function( e ) {
			e.preventDefault = function(){ e.returnValue = false; };
			e.stopPropagation = function(){ e.cancelBubble = true; };
			e.target = e.srcElement;
			
			var type = e.type;
			// для событий клавиатуры
			if( type == 'keypress' ){
				// нормализация кода клавиши
				e.charCode = e.keyCode;
			}
			return e;
		}
		
		var handleEvent = function() {

			// нормализация объекта события
			var e = fixEvent( window.event );
			// ссылка на хэш с обработчиками данного типа события для данного элемента
			var handlers = this.onpage[ e.type ];
			// выполнение каждого обработчика в хэше
			for ( var i in handlers ){
				handlers[i].call( this, e );
			}
		};
	
		// выбор метода добавления обработчика в зависимости от броузера
		if ( typeof addEventListener != 'undefined' )
			return function( elem, type, handler ) { elem.addEventListener( type, handler, false ) };
		else
			return function( elem, type, handler ) {
				
				// каждому обработчику присваивается guid, если ещё не присвоен
				if ( !handler.$$guid ) {
					handler.$$guid = this.guid++;
				}
				
				/**
				 * Информация об эвентах будет храниться в обработчике onpage элемента
				 * В MSDN написано, что обработка этого события не реализована,
				 * т.е. использовать его по назначению не удастся,
				 * одна при клонировании узла этот обработчик обнуляется,
				 * как и большинство обработчиков, установленных скриптом
				 * поэтому все обработчики узла не скопируются для клонированного узла
				 * ( как в оригинальной версии, когда обработчики хранятся в свойтве 
				 * events элемента ), таким образом IE будет вести себя как и остальные броузеры
				 * т.е. если на узел навешаны эвенты скриптами, и узел склонирован, то эвенты не перенесутся
				 */
				var events = elem.onpage;
				// хэш с типами событий для каждого элемента
				if ( !events ){
					events = elem.onpage = {};
				}
				// ссылка на каждый добавляемый тип события из хэша элемента
				// каждому типу соответвует свой хэш обработчиков
				var handlers = events[ type ];
				
				// если в хэше ещё нет добавляемого типа
				if ( !handlers ) {
					// добавление в хэш соответвующего типа
					handlers = events[ type ] = {};
					// если у элемента уже есть обработчик события
					// добавленый в html например - сохранние его
					// в нулевой элемент хэша обработчиков
					if ( elem[ 'on' + type ] ) {
						handlers[0] = elem[ 'on' + type ];
					}
				}
				
				// добавление глобального обработчика события
				elem[ 'on' + type ] = handleEvent;

				// добавление обработчика в хэш обработчиков
				handlers[ handler.$$guid ] = handler;				
			};
	})();

	/**
	 * Удаление обработчика у элемента
	 * @param {Element} elem елемент
	 * @param {String} type тип события
	 * @param {Function} handler обработчик
	 */
	this.unbind = (function() {
		if ( typeof removeEventListener != 'undefined' ) {
			return function( elem, type, handler ) { elem.removeEventListener( type, handler, false ); };
		} else
			return function( elem, type, handler ) {
				// если такой обработчик есть в хэше
				if ( elem.onpage && elem.onpage[ type ]) {
					// удаление его из хэша
					delete elem.onpage[ type ][ handler.$$guid ];
				}
			};
	})();
	
	/**
	 * Добавление одноразового обработчика
	 * (удаляется сразу после первого выполнения)
	 * @param {Element} elem елемент
	 * @param {String} type тип события
	 * @param {Function} handler обработчик
	 */
	this.one = function( elem, type, handler ){
		var _this = this;
		// Обработчик-обёртка с инструкцией удалить себя при первом исполнении
		var wrapper = function( e ){
			// удаление обработчика-обёртки
			_this.unbind( this, type, wrapper );
			// выполнение переданного обработчика
			handler.call( this, e );
		}
		// добавление обработчика-обёртки
		this.bind( elem, type, wrapper );
		// предотвращение утечек в IE
		elem = null;
	};
	
	/**
	 * Добавляет обработчики готовности DOM-структуры документа
	 * @param {Function} handler обработчик
	 */
	this.domReady = (function(){

		// индикатор выполняющейся проверки готовности
		var checkingReady = false;
		// индикатор готовности DOM-структуры
		var isReady = false;
		// обработчики, ожидающие выполнения
		var readyHandlers = [];
		
		/**
		 * Проверяет готовность DOM-структуры документа
		 */
		var checkReady = function() {
		
			// проверка запускается только один раз
			if( checkingReady ) { return; }
			checkingReady = true;
		
			/**
			 * современные броузеры ( FF2, FF3, Safari 3, Chrome 1, Opera 9)
			 * поддерживают событие DOMContentLoaded
			 */
			if( typeof addEventListener != 'undefined' ){
				document.addEventListener( 'DOMContentLoaded', execHandlers, false );
			}
			// для IE хак с doScroll ( http://javascript.nwbox.com/IEContentLoaded/ )
			else if ( /msie/.test( navigator.userAgent.toLowerCase() ) &&  window == top ){
				(function(){
					try {
				  		document.documentElement.doScroll( 'left' );
				  	} 
				  	catch (error) {
				  		setTimeout( arguments.callee, 0 );
				  		return;
				  	}
				  	execHandlers();
				})();
			}
		};
		/**
		 * Выполняет обработчики по готовности DOM-структуры документа
		 */
		var execHandlers = function(){
			// если функция была вызвана, то DOM-структура готова
			isReady = true;
			// выполнение обработчиков в порядке добавления
			while( readyHandlers.length ){
				readyHandlers.shift()();
			}
		};
		
		return function( handler ) {
			// проверка готовности DOM-структуры
			checkReady();
			
			// если DOM-структура уже готова - немедленное исполнение обработчика
			if( isReady ) {
				handler();
			}
			// если DOM-структура не готова - занесение в очередь
			else {
				readyHandlers.push( handler );
			}
		}
	})();
};
