/** * Работа с обработчиками событий * * @package Events * @version 1.0 * @author Григорьев О.С. aka vasa_c * @copyright © vasa_c, 2007 * @license LGPL (http://www.gnu.org/copyleft/lesser.html) * * ВОЗМОЖНОСТИ ЛИБЫ: * - Унификация для всех браузеров: * - установки/удаления обработчиков (без проверки addEventListener/attachEvent) * - доступа к целевому объекту и объекту event * - отмены действия по умолчанию и дальнейшего распространения * - Более гибкий вызов обработчика (см. ниже) * - Группы обработчиков (см.ниже) * - Централизованное удаление всех обработчиков в конце (для борьбы с утечками памяти) * - Возможность указания объектов, как непосредственно, так и через id * - Обращение к установленным обработчикам по идентификаторам * * Недостатки: * - Браузеры не поддерживающие последние модели событий идут лесом (ошибок не происходит, но обработчики не устанавливаются) * - Не поддерживается погружение событий * - Не поддерживается генерация событий * * * ФОРМАТ ОБРАБОТЧИКОВ * В качестве обработчиков можно передавать: * - function * - string - пропускается через eval() * - массив формата [function, объект, параметры, нестандартный вызов], обязателен только первый параметр * "объект" - объект в контексте которого вызывается функция. * не указан или false - window * true - целевой объект события * можно указывать строку, например "one.two.three", при вызове она будет * резволиться в объект (для тех случаев, когда объекта нет на стадии установки обработчиков) * "параметры" - массив параметров с которыми вызывается обработчик * "нестандартный вызов" - если указан, то обработчик считается нестандартным, не указан - стандартным (см. ниже) * * При "стандартном" вызове в обработчик передаются три параметра - node, event. Если был задан массив "параметры", * то они добавляются начиная с третьего. * node - целевой объект * event - объект Event * * При "нестандартном" эти три параметра не передаются. * Он используется для вызова существующих функций с определенным списком параметров. * Разруливание доступа к элементу и event ложится на плечи самого обработчика. * * * ГРУППЫ ОБРАБОТЧИКОВ * Работа приложения может состоять из набора устойчивых состояний и переходов между ними. * Каждое состояние может характеризоваться набором действующих в нем обработчиков. * При переходе из одного в другое, следует удалить все старые обработчики и установить новые. * В данной либе можно устанавливать обработчики один раз, при этом привязывая их к какой-либо * группе, а потом включать/отключать их синхронно. * Группа идентифицируется именем. Группа по умолчанию - "0". * * ФУНКЦИИ * add - установка обработчика * remove, groupRemove, enable, disable, groupEnable, groupDisable - * удаление, включение/выключение отдельных обработчиков и групп * preventDefault, stopPropagation - отмена дальнейшего распространения и действия по умолчанию * using - вывод в глобальное пространство имен * * * Тестировалось в IE6, FF2, Opera9 */ /** * @var object Events - пространство имен библиотеки */ var Events = new function() { /********** PUBLIC **********/ /** * Установка обработчика * * @param mixed node - целевой элемент (или его id) * @param string event - имя события (без "on") * @param mixed handler - обработчик (формат описан в шапке файла) * @param string group - имя группы (по умолчанию "0") * @param bool enb - изначальный вкл/выкл обработчика (по умолчанию true) * * @return int - идентификатор обработчика */ this.add = ( function( node, event, handler, group, enb ) { if ( typeof( node ) != "object" ) { node = document.getElementById( node ); } if ( arguments.length < 5 ) { enb = true; if ( arguments.length < 4 ) { group = "0"; } } if ( ( typeof( handler ) ) == "object" ) { if ( !handler[ 1 ] ) { handler[ 1 ] = window; } else if ( handler[ 1 ] === true ) { handler[ 1 ] = node; } if ( !handler[ 2 ] ) { handler[ 2 ] = []; } } else if ( typeof( handler ) == "function" ) { handler = [ handler, window, [] ]; } var id = aHandlers.length; aHandlers[ id ] = { "node": node, "event": event, "handler": handler, "group": group, "enb": enb, "h": addE( id ) }; if ( !aGroups[ group ] ) { aGroups[ group ] = { "enb": true, "A": [ id ] }; } else { aGroups[ group ].A[ aGroups[ group ].A.length ] = id; } add( node, event, aHandlers[ id ].h ); return id; } ); // add(). /** * Удаление обработчика * * @param int id - его идентификатор */ this.remove = ( function( id ) { var h = aHandlers[ id ]; if ( !h ) { return false; } remove( h.node, h.event, h.h ); aHandlers[ id ] = false; return true; } ); /** * Включение обработчика * * @param int id - его идентификатор */ this.enable = ( function( id ) { if ( aHandlers[ id ] ) { aHandlers[ id ].enb = true; } return true; } ); /** * Выключение обработчика * * @param int id - его идентификатор */ this.disable = ( function( id ) { if ( aHandlers[ id ] ) { aHandlers[ id ].enb = false; } return true; } ); /** * Удаление группы * * @param string group - ее имя */ this.groupRemove = ( function( name ) { if ( !aGroups[ name ] ) { return false; } var g = aGroups[ name ].A; for ( var i = 0; i < g.length; i++ ) { _this.remove( g[ i ] ); } aGroups[ name ] = false; return true; } ); /** * Включение группы * * @param string group - ее имя */ this.groupEnable = ( function( name ) { if ( aGroups[ name ] ) { aGroups[ name ].enb = true; } return true; } ); /** * Выключение группы * * @param string group - ее имя */ this.groupDisable = ( function( name ) { if ( aGroups[ name ] ) { aGroups[ name ].enb = false; } return true; } ); /** * Вывод функций в глобальную область видимости * * @param string prefix - префикс имен функций (по умолчанию "events"). * * Функции либы выводятся в глобальное пространство имен. * При указании непустого префикса, он добавляется к имени функции, * которое приводится к криволинейной нотации. "add" --> "eventsAdd" */ this.using = ( function( prefix ) { if ( arguments.length == 0 ) { prefix = "events"; } for ( var name in this ) { if ( name == "using" ) { continue; } var pName = prefix.length ? prefix + name.charAt( 0 ).toUpperCase() + name.substr( 1, name.length - 1 ) : name; window[ pName ] = this[ name ]; } } ); // using(). /** * Остановка дальнейшего распространения события. * Вызывается внутри обработчика в виде Events.stopPropagation(). */ this.stopPropagation = ( function() { addE.stopPropagation = true; return true; } ); /** * Отмена действия по умолчанию * Вызывается внутри обработчика в виде Events.preventDefault(). */ this.preventDefault = ( function() { addE.preventDefault = true; return true; } ); /********** PRIVATE **********/ /** * @var array aHandlers - обработчики * @var object aGroups - группы */ var aHandlers = []; var aGroups = {}; /** * Получение настоящего обработчик для устанавливаемого обработчика :) * Возвращает функцию, которая будет установлена в качестве обработчика. * В ней разруливаются признаки вкл/выкл, объекты, отмена распространения и т.п. * и вызывается окончательный обработчик. * * @param int id - идентификатор обработчика * @return function - "настоящий" обработчик */ function addE( id ) { return ( function( e ) { var h = aHandlers[ id ]; if ( !( ( h ) && ( h.enb ) && ( aGroups[ h.group ] ) && ( aGroups[ h.group ].enb ) ) ) { return; } if ( typeof( h.handler ) != "object" ) { return eval( h.handler ); } var prm = []; if ( !h.handler[ 3 ] ) { prm[ 0 ] = h.node; e = window.event || e; prm[ 1 ] = e; for ( var i = 0; i < h.handler[ 2 ].length; i++ ) { prm[ i + 2 ] = h.handler[ 2 ][ i ]; } } else prm = h.handler[ 2 ]; var obj = h.handler[ 1 ]; if ( typeof( obj ) != "object" ) { obj = ( function( path ) { path = path.split( "." ); var o = window; for ( var i = 0; i < path.length; i++ ) { if ( !o[ path[ i ] ] ) { return window; } o = o[ path[ i ] ]; } return o; }( obj ) ); } addE.stopPropagation = false; addE.preventDefault = false; var ret = h.handler[ 0 ].apply( obj, prm ); if ( addE.stopPropagation ) { if ( e.stopPropagation ) { e.stopPropagation(); } else { e.cancelBubble = true; } } if ( addE.preventDefault ) { if ( e.preventDefault ) { e.preventDefault(); } else { return false; } } return ret; } ) }; // addE(). /** * Унификация установки/удаления обработчиков. */ if ( window.addEventListener ) { var add = ( function( el, event, handler ) { return el.addEventListener( event, handler, false ); } ); var remove = ( function( el, event, handler ) { return el.removeEventListener( event, handler, false ); } ); } else if ( window.attachEvent ) { var add = ( function( el, event, handler ) { return el.attachEvent( "on" + event, handler ); } ); var remove = ( function( el, event, handler ) { return el.detachEvent( "on" + event, handler ); } ); } else { var add = ( function() {} ); var remove = add; } /** * @var object _this - алиас для this, на случай использования using(). */ var _this = this; /** * Удаление обработчиков по унлоаду */ add( window, "unload", ( function() { for ( var name in aGroups ) { _this.groupRemove( name ); } return true; } ) ); }(); // Events.