-- Prosody IM
-- Copyright (C) 2008-2010 Matthew Wild
-- Copyright (C) 2008-2010 Waqas Hussain
--
-- This project is MIT/X11 licensed. Please see the
-- COPYING file in the source package for more information.
--
local pairs = pairs ;
local t_insert = table.insert ;
local t_remove = table.remove ;
local t_sort = table.sort ;
local setmetatable = setmetatable ;
local next = next ;
local _ENV = nil ;
-- luacheck: std none
local function new ( )
-- Map event name to ordered list of handlers (lazily built): handlers[event_name] = array_of_handler_functions
local handlers = { } ;
-- Array of wrapper functions that wrap all events (nil if empty)
local global_wrappers ;
-- Per-event wrappers: wrappers[event_name] = wrapper_function
local wrappers = { } ;
-- Event map: event_map[handler_function] = priority_number
local event_map = { } ;
-- Debug hook, if any
local active_debug_hook = nil ;
-- Called on-demand to build handlers entries
local function _rebuild_index ( self , event )
local _handlers = event_map [ event ] ;
if not _handlers or next ( _handlers ) == nil then return ; end
local index = { } ;
for handler in pairs ( _handlers ) do
t_insert ( index , handler ) ;
end
t_sort ( index , function ( a , b ) return _handlers [ a ] > _handlers [ b ] ; end ) ;
self [ event ] = index ;
return index ;
end ;
setmetatable ( handlers , { __index = _rebuild_index } ) ;
local function add_handler ( event , handler , priority )
local map = event_map [ event ] ;
if map then
map [ handler ] = priority or 0 ;
else
map = { [ handler ] = priority or 0 } ;
event_map [ event ] = map ;
end
handlers [ event ] = nil ;
end ;
local function remove_handler ( event , handler )
local map = event_map [ event ] ;
if map then
map [ handler ] = nil ;
handlers [ event ] = nil ;
if next ( map ) == nil then
event_map [ event ] = nil ;
end
end
end ;
local function get_handlers ( event )
return handlers [ event ] ;
end ;
local function add_handlers ( self )
for event , handler in pairs ( self ) do
add_handler ( event , handler ) ;
end
end ;
local function remove_handlers ( self )
for event , handler in pairs ( self ) do
remove_handler ( event , handler ) ;
end
end ;
local function _fire_event ( event_name , event_data )
local h = handlers [ event_name ] ;
if h and not active_debug_hook then
for i = 1 , # h do
local ret = h [ i ] ( event_data ) ;
if ret ~= nil then return ret ; end
end
elseif h and active_debug_hook then
for i = 1 , # h do
local ret = active_debug_hook ( h [ i ] , event_name , event_data ) ;
if ret ~= nil then return ret ; end
end
end
end ;
local function fire_event ( event_name , event_data )
-- luacheck: ignore 432/event_name 432/event_data
local w = wrappers [ event_name ] or global_wrappers ;
if w then
local curr_wrapper = # w ;
local function c ( event_name , event_data )
curr_wrapper = curr_wrapper - 1 ;
if curr_wrapper == 0 then
if global_wrappers == nil or w == global_wrappers then
return _fire_event ( event_name , event_data ) ;
end
w , curr_wrapper = global_wrappers , # global_wrappers ;
return w [ curr_wrapper ] ( c , event_name , event_data ) ;
else
return w [ curr_wrapper ] ( c , event_name , event_data ) ;
end
end
return w [ curr_wrapper ] ( c , event_name , event_data ) ;
end
return _fire_event ( event_name , event_data ) ;
end
local function add_wrapper ( event_name , wrapper )
local w ;
if event_name == false then
w = global_wrappers ;
if not w then
w = { } ;
global_wrappers = w ;
end
else
w = wrappers [ event_name ] ;
if not w then
w = { } ;
wrappers [ event_name ] = w ;
end
end
w [ # w + 1 ] = wrapper ;
end
local function remove_wrapper ( event_name , wrapper )
local w ;
if event_name == false then
w = global_wrappers ;
else
w = wrappers [ event_name ] ;
end
if not w then return ; end
for i = # w , 1 , - 1 do
if w [ i ] == wrapper then
t_remove ( w , i ) ;
end
end
if # w == 0 then
if event_name == false then
global_wrappers = nil ;
else
wrappers [ event_name ] = nil ;
end
end
end
local function set_debug_hook ( new_hook )
local old_hook = active_debug_hook ;
active_debug_hook = new_hook ;
return old_hook ;
end
return {
add_handler = add_handler ;
remove_handler = remove_handler ;
add_handlers = add_handlers ;
remove_handlers = remove_handlers ;
get_handlers = get_handlers ;
wrappers = {
add_handler = add_wrapper ;
remove_handler = remove_wrapper ;
} ;
add_wrapper = add_wrapper ;
remove_wrapper = remove_wrapper ;
set_debug_hook = set_debug_hook ;
fire_event = fire_event ;
_handlers = handlers ;
_event_map = event_map ;
} ;
end
return {
new = new ;
} ;