186 lines
5.1 KiB
Lua
186 lines
5.1 KiB
Lua
|
------------------------------------------------------------------------------
|
||
|
-- EventEmitter Class in Node.js Style
|
||
|
-- LICENSE: MIT
|
||
|
-- Simen Li <simenkid@gmail.com>
|
||
|
------------------------------------------------------------------------------
|
||
|
EventEmitter = { defaultMaxListeners = 10 }
|
||
|
local PFX = '__lsn_'
|
||
|
local PFX_LEN = #PFX
|
||
|
|
||
|
setmetatable(EventEmitter, {
|
||
|
__call = function (_, ...) return EventEmitter:new(...) end
|
||
|
})
|
||
|
|
||
|
local function rmEntry(tbl, pred)
|
||
|
local x, len = 0, #tbl
|
||
|
for i = 1, len do
|
||
|
local trusy, idx = false, (i - x)
|
||
|
if (type(pred) == 'function') then trusy = pred(tbl[idx])
|
||
|
else trusy = tbl[idx] == pred
|
||
|
end
|
||
|
|
||
|
if (tbl[idx] ~= nil and trusy) then
|
||
|
tbl[idx] = nil
|
||
|
table.remove(tbl, idx)
|
||
|
x = x + 1
|
||
|
end
|
||
|
end
|
||
|
return tbl
|
||
|
end
|
||
|
|
||
|
function EventEmitter:new(obj)
|
||
|
obj = obj or {}
|
||
|
self.__index = self
|
||
|
setmetatable(obj, self)
|
||
|
obj._on = {}
|
||
|
|
||
|
return obj
|
||
|
end
|
||
|
|
||
|
function EventEmitter:evTable(ev)
|
||
|
if (type(self._on[ev]) ~= 'table') then self._on[ev] = {} end
|
||
|
return self._on[ev]
|
||
|
end
|
||
|
|
||
|
function EventEmitter:getEvTable(ev)
|
||
|
return self._on[ev]
|
||
|
end
|
||
|
|
||
|
-- ************************************************************************ --
|
||
|
-- ** Public APIs * --
|
||
|
-- ************************************************************************ --
|
||
|
function EventEmitter:addListener(ev, listener)
|
||
|
local pfx_ev = PFX .. tostring(ev)
|
||
|
local evtbl = self:evTable(pfx_ev)
|
||
|
local maxLsnNum = self.currentMaxListeners or self.defaultMaxListeners
|
||
|
local lsnNum = self:listenerCount(ev)
|
||
|
table.insert(evtbl, listener)
|
||
|
|
||
|
if (lsnNum > maxLsnNum) then print('WARN: Number of ' .. string.sub(pfx_ev, PFX_LEN + 1) .. " event listeners: " .. tostring(lsnNum)) end
|
||
|
return self
|
||
|
end
|
||
|
|
||
|
function EventEmitter:emit(ev, ...)
|
||
|
local pfx_ev = PFX .. tostring(ev)
|
||
|
local evtbl = self:getEvTable(pfx_ev)
|
||
|
if (evtbl ~= nil) then
|
||
|
for _, lsn in ipairs(evtbl) do
|
||
|
local status, err = pcall(lsn, ...)
|
||
|
if not (status) then print(string.sub(_, PFX_LEN + 1) .. " emit error: " .. tostring(err)) end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
-- one-time listener
|
||
|
pfx_ev = pfx_ev .. ':once'
|
||
|
evtbl = self:getEvTable(pfx_ev)
|
||
|
|
||
|
if (evtbl ~= nil) then
|
||
|
for _, lsn in ipairs(evtbl) do
|
||
|
local status, err = pcall(lsn, ...)
|
||
|
if not (status) then print(string.sub(_, PFX_LEN + 1) .. " emit error: " .. tostring(err)) end
|
||
|
end
|
||
|
|
||
|
rmEntry(evtbl, function (v) return v ~= nil end)
|
||
|
self._on[pfx_ev] = nil
|
||
|
end
|
||
|
return self
|
||
|
end
|
||
|
|
||
|
function EventEmitter:getMaxListeners()
|
||
|
return self.currentMaxListeners or self.defaultMaxListeners
|
||
|
end
|
||
|
|
||
|
function EventEmitter:listenerCount(ev)
|
||
|
local totalNum = 0
|
||
|
local pfx_ev = PFX .. tostring(ev)
|
||
|
local evtbl = self:getEvTable(pfx_ev)
|
||
|
|
||
|
if (evtbl ~= nil) then totalNum = totalNum + #evtbl end
|
||
|
|
||
|
pfx_ev = pfx_ev .. ':once'
|
||
|
evtbl = self:getEvTable(pfx_ev)
|
||
|
|
||
|
if (evtbl ~= nil) then totalNum = totalNum + #evtbl end
|
||
|
|
||
|
return totalNum
|
||
|
end
|
||
|
|
||
|
function EventEmitter:listeners(ev)
|
||
|
local pfx_ev = PFX .. tostring(ev)
|
||
|
local evtbl = self:getEvTable(pfx_ev)
|
||
|
local clone = {}
|
||
|
|
||
|
if (evtbl ~= nil) then
|
||
|
for i, lsn in ipairs(evtbl) do table.insert(clone, lsn) end
|
||
|
end
|
||
|
|
||
|
pfx_ev = pfx_ev .. ':once'
|
||
|
evtbl = self:getEvTable(pfx_ev)
|
||
|
|
||
|
if (evtbl ~= nil) then
|
||
|
for i, lsn in ipairs(evtbl) do table.insert(clone, lsn) end
|
||
|
end
|
||
|
|
||
|
return clone
|
||
|
end
|
||
|
|
||
|
EventEmitter.on = EventEmitter.addListener
|
||
|
|
||
|
function EventEmitter:once(ev, listener)
|
||
|
local pfx_ev = PFX .. tostring(ev) .. ':once'
|
||
|
local evtbl = self:evTable(pfx_ev)
|
||
|
local maxLsnNum = self.currentMaxListeners or self.defaultMaxListeners
|
||
|
local lsnNum = self:listenerCount(ev)
|
||
|
if (lsnNum > maxLsnNum) then print('WARN: Number of ' .. ev .. " event listeners: " .. tostring(lsnNum)) end
|
||
|
|
||
|
table.insert(evtbl, listener)
|
||
|
return self
|
||
|
end
|
||
|
|
||
|
function EventEmitter:removeAllListeners(ev)
|
||
|
if ev ~= nil then
|
||
|
local pfx_ev = PFX .. tostring(ev)
|
||
|
local evtbl = self:evTable(pfx_ev)
|
||
|
rmEntry(evtbl, function (v) return v ~= nil end)
|
||
|
|
||
|
pfx_ev = pfx_ev .. ':once'
|
||
|
evtbl = self:evTable(pfx_ev)
|
||
|
rmEntry(evtbl, function (v) return v ~= nil end)
|
||
|
self._on[pfx_ev] = nil
|
||
|
else
|
||
|
for _pfx_ev, _t in pairs(self._on) do self:removeAllListeners(string.sub(_pfx_ev, PFX_LEN + 1)) end
|
||
|
end
|
||
|
|
||
|
for _pfx_ev, _t in pairs(self._on) do
|
||
|
if (#_t == 0) then self._on[_pfx_ev] = nil end
|
||
|
end
|
||
|
|
||
|
return self
|
||
|
end
|
||
|
|
||
|
function EventEmitter:removeListener(ev, listener)
|
||
|
local pfx_ev = PFX .. tostring(ev)
|
||
|
local evtbl = self:evTable(pfx_ev)
|
||
|
local lsnCount = 0
|
||
|
assert(listener ~= nil, "listener is nil")
|
||
|
-- normal listener
|
||
|
rmEntry(evtbl, listener)
|
||
|
|
||
|
if (#evtbl == 0) then self._on[pfx_ev] = nil end
|
||
|
|
||
|
-- emit-once listener
|
||
|
pfx_ev = pfx_ev .. ':once'
|
||
|
evtbl = self:evTable(pfx_ev)
|
||
|
rmEntry(evtbl, listener)
|
||
|
|
||
|
if (#evtbl == 0) then self._on[pfx_ev] = nil end
|
||
|
return self
|
||
|
end
|
||
|
|
||
|
function EventEmitter:setMaxListeners(n)
|
||
|
self.currentMaxListeners = n
|
||
|
return self
|
||
|
end
|
||
|
|
||
|
return EventEmitter
|