util.events: Change from nil to false to indicate adding a global wrapper
[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_sort = table.sort;
13 local setmetatable = setmetatable;
14 local next = next;
15
16 module "events"
17
18 function new()
19         local handlers = {};
20         local global_wrappers;
21         local wrappers = {};
22         local event_map = {};
23         local function _rebuild_index(handlers, event)
24                 local _handlers = event_map[event];
25                 if not _handlers or next(_handlers) == nil then return; end
26                 local index = {};
27                 for handler in pairs(_handlers) do
28                         t_insert(index, handler);
29                 end
30                 t_sort(index, function(a, b) return _handlers[a] > _handlers[b]; end);
31                 handlers[event] = index;
32                 return index;
33         end;
34         setmetatable(handlers, { __index = _rebuild_index });
35         local function add_handler(event, handler, priority)
36                 local map = event_map[event];
37                 if map then
38                         map[handler] = priority or 0;
39                 else
40                         map = {[handler] = priority or 0};
41                         event_map[event] = map;
42                 end
43                 handlers[event] = nil;
44         end;
45         local function remove_handler(event, handler)
46                 local map = event_map[event];
47                 if map then
48                         map[handler] = nil;
49                         handlers[event] = nil;
50                         if next(map) == nil then
51                                 event_map[event] = nil;
52                         end
53                 end
54         end;
55         local function add_handlers(handlers)
56                 for event, handler in pairs(handlers) do
57                         add_handler(event, handler);
58                 end
59         end;
60         local function remove_handlers(handlers)
61                 for event, handler in pairs(handlers) do
62                         remove_handler(event, handler);
63                 end
64         end;
65         local function _fire_event(event_name, event_data)
66                 local h = handlers[event_name];
67                 if h then
68                         for i=1,#h do
69                                 local ret = h[i](event_data);
70                                 if ret ~= nil then return ret; end
71                         end
72                 end
73         end;
74         local function fire_event(event_name, event_data)
75                 local w = wrappers[event_name] or global_wrappers;
76                 if w then
77                         local curr_wrapper = #w;
78                         local function c(event_name, event_data)
79                                 curr_wrapper = curr_wrapper - 1;
80                                 if curr_wrapper == 0 then
81                                         if global_wrappers == nil or w == global_wrappers then
82                                                 return _fire_event(event_name, event_data);
83                                         end
84                                         w, curr_wrapper = global_wrappers, #global_wrappers;
85                                         return w[curr_wrapper](c, event_name, event_data);
86                                 else
87                                         return w[curr_wrapper](c, event_name, event_data);
88                                 end
89                         end
90                         return w[curr_wrapper](c, event_name, event_data);
91                 end
92                 return _fire_event(event_name, event_data);
93         end
94         local function add_wrapper(event_name, wrapper)
95                 local w;
96                 if event_name == false then
97                         w = global_wrappers;
98                         if not w then
99                                 w = {};
100                                 global_wrappers = w;
101                         end
102                 else
103                         w = wrappers[event_name];
104                         if not w then
105                                 w = {};
106                                 wrappers[event_name] = w;
107                         end
108                 end
109                 w[#w+1] = wrapper;
110         end
111         local function remove_wrapper(event_name, wrapper)
112                 local w;
113                 if event_name == false then
114                         w = global_wrappers;
115                 else
116                         w = wrappers[event_name];
117                 end
118                 if not w then return; end
119                 for i = #w, 1 do
120                         if w[i] == wrapper then
121                                 table.remove(w, i);
122                         end
123                 end
124                 if #w == 0 then
125                         if event_name == nil then
126                                 global_wrappers = nil;
127                         else
128                                 wrappers[event_name] = nil;
129                         end
130                 end
131         end
132         return {
133                 add_handler = add_handler;
134                 remove_handler = remove_handler;
135                 add_handlers = add_handlers;
136                 remove_handlers = remove_handlers;
137                 wrappers = {
138                         add_handler = add_wrapper;
139                         remove_handler = remove_wrapper;
140                 };
141                 add_wrapper = add_wrapper;
142                 remove_wrapper = remove_wrapper;
143                 fire_event = fire_event;
144                 _handlers = handlers;
145                 _event_map = event_map;
146         };
147 end
148
149 return _M;