util.events: Add local reference to table.remove (fixes traceback)
[prosody.git] / util / events.lua
1 -- Prosody IM
2 -- Copyright (C) 2008-2010 Matthew Wild
3 -- Copyright (C) 2008-2010 Waqas Hussain
4 --
5 -- This project is MIT/X11 licensed. Please see the
6 -- COPYING file in the source package for more information.
7 --
8
9
10 local pairs = pairs;
11 local t_insert = table.insert;
12 local t_remove = table.remove;
13 local t_sort = table.sort;
14 local setmetatable = setmetatable;
15 local next = next;
16
17 module "events"
18
19 function new()
20         local handlers = {};
21         local global_wrappers;
22         local wrappers = {};
23         local event_map = {};
24         local function _rebuild_index(handlers, event)
25                 local _handlers = event_map[event];
26                 if not _handlers or next(_handlers) == nil then return; end
27                 local index = {};
28                 for handler in pairs(_handlers) do
29                         t_insert(index, handler);
30                 end
31                 t_sort(index, function(a, b) return _handlers[a] > _handlers[b]; end);
32                 handlers[event] = index;
33                 return index;
34         end;
35         setmetatable(handlers, { __index = _rebuild_index });
36         local function add_handler(event, handler, priority)
37                 local map = event_map[event];
38                 if map then
39                         map[handler] = priority or 0;
40                 else
41                         map = {[handler] = priority or 0};
42                         event_map[event] = map;
43                 end
44                 handlers[event] = nil;
45         end;
46         local function remove_handler(event, handler)
47                 local map = event_map[event];
48                 if map then
49                         map[handler] = nil;
50                         handlers[event] = nil;
51                         if next(map) == nil then
52                                 event_map[event] = nil;
53                         end
54                 end
55         end;
56         local function add_handlers(handlers)
57                 for event, handler in pairs(handlers) do
58                         add_handler(event, handler);
59                 end
60         end;
61         local function remove_handlers(handlers)
62                 for event, handler in pairs(handlers) do
63                         remove_handler(event, handler);
64                 end
65         end;
66         local function _fire_event(event_name, event_data)
67                 local h = handlers[event_name];
68                 if h then
69                         for i=1,#h do
70                                 local ret = h[i](event_data);
71                                 if ret ~= nil then return ret; end
72                         end
73                 end
74         end;
75         local function fire_event(event_name, event_data)
76                 local w = wrappers[event_name] or global_wrappers;
77                 if w then
78                         local curr_wrapper = #w;
79                         local function c(event_name, event_data)
80                                 curr_wrapper = curr_wrapper - 1;
81                                 if curr_wrapper == 0 then
82                                         if global_wrappers == nil or w == global_wrappers then
83                                                 return _fire_event(event_name, event_data);
84                                         end
85                                         w, curr_wrapper = global_wrappers, #global_wrappers;
86                                         return w[curr_wrapper](c, event_name, event_data);
87                                 else
88                                         return w[curr_wrapper](c, event_name, event_data);
89                                 end
90                         end
91                         return w[curr_wrapper](c, event_name, event_data);
92                 end
93                 return _fire_event(event_name, event_data);
94         end
95         local function add_wrapper(event_name, wrapper)
96                 local w;
97                 if event_name == false then
98                         w = global_wrappers;
99                         if not w then
100                                 w = {};
101                                 global_wrappers = w;
102                         end
103                 else
104                         w = wrappers[event_name];
105                         if not w then
106                                 w = {};
107                                 wrappers[event_name] = w;
108                         end
109                 end
110                 w[#w+1] = wrapper;
111         end
112         local function remove_wrapper(event_name, wrapper)
113                 local w;
114                 if event_name == false then
115                         w = global_wrappers;
116                 else
117                         w = wrappers[event_name];
118                 end
119                 if not w then return; end
120                 for i = #w, 1 do
121                         if w[i] == wrapper then
122                                 t_remove(w, i);
123                         end
124                 end
125                 if #w == 0 then
126                         if event_name == nil then
127                                 global_wrappers = nil;
128                         else
129                                 wrappers[event_name] = nil;
130                         end
131                 end
132         end
133         return {
134                 add_handler = add_handler;
135                 remove_handler = remove_handler;
136                 add_handlers = add_handlers;
137                 remove_handlers = remove_handlers;
138                 wrappers = {
139                         add_handler = add_wrapper;
140                         remove_handler = remove_wrapper;
141                 };
142                 add_wrapper = add_wrapper;
143                 remove_wrapper = remove_wrapper;
144                 fire_event = fire_event;
145                 _handlers = handlers;
146                 _event_map = event_map;
147         };
148 end
149
150 return _M;