diff lib/AceEvent-2.0/AceEvent-2.0.lua @ 22:1b9323256a1b

Merging in 1.0 dev tree
author Flick <flickerstreak@gmail.com>
date Fri, 07 Mar 2008 22:10:55 +0000
parents libs/AceEvent-2.0/AceEvent-2.0.lua@c11ca1d8ed91
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/AceEvent-2.0/AceEvent-2.0.lua	Fri Mar 07 22:10:55 2008 +0000
@@ -0,0 +1,1082 @@
+--[[
+Name: AceEvent-2.0
+Revision: $Rev: 49307 $
+Developed by: The Ace Development Team (http://www.wowace.com/index.php/The_Ace_Development_Team)
+Inspired By: Ace 1.x by Turan (turan@gryphon.com)
+Website: http://www.wowace.com/
+Documentation: http://www.wowace.com/index.php/AceEvent-2.0
+SVN: http://svn.wowace.com/root/trunk/Ace2/AceEvent-2.0
+Description: Mixin to allow for event handling, scheduling, and inter-addon
+             communication.
+Dependencies: AceLibrary, AceOO-2.0
+License: LGPL v2.1
+]]
+
+local MAJOR_VERSION = "AceEvent-2.0"
+local MINOR_VERSION = "$Revision: 49307 $"
+
+if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary") end
+if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end
+
+if not AceLibrary:HasInstance("AceOO-2.0") then error(MAJOR_VERSION .. " requires AceOO-2.0") end
+
+local AceOO = AceLibrary:GetInstance("AceOO-2.0")
+local Mixin = AceOO.Mixin
+local AceEvent = Mixin {
+	"RegisterEvent",
+	"RegisterAllEvents",
+	"UnregisterEvent",
+	"UnregisterAllEvents",
+	"TriggerEvent",
+	"ScheduleEvent",
+	"ScheduleRepeatingEvent",
+	"CancelScheduledEvent",
+	"CancelAllScheduledEvents",
+	"IsEventRegistered",
+	"IsEventScheduled",
+	"RegisterBucketEvent",
+	"UnregisterBucketEvent",
+	"UnregisterAllBucketEvents",
+	"IsBucketEventRegistered",
+	"ScheduleLeaveCombatAction",
+	"CancelAllCombatSchedules",
+}
+
+local weakKey = {__mode="k"}
+
+local FAKE_NIL
+local RATE
+
+local eventsWhichHappenOnce = {
+	PLAYER_LOGIN = true,
+	AceEvent_FullyInitialized = true,
+	VARIABLES_LOADED = true,
+	PLAYER_LOGOUT = true,
+}
+local next = next
+local pairs = pairs
+local pcall = pcall
+local type = type
+local GetTime = GetTime
+local gcinfo = gcinfo
+local unpack = unpack
+local geterrorhandler = geterrorhandler
+
+local build = GetBuildInfo()
+local useTablesAsIDs = build:find("^2%.0%.") or build:find("^2%.1%.") or build:find("^0%.1%.")
+
+local new, del
+do
+	local cache = setmetatable({}, {__mode='k'})
+	function new(...)
+		local t = next(cache)
+		if t then
+			cache[t] = nil
+			for i = 1, select('#', ...) do
+				t[i] = select(i, ...)
+			end
+			return t
+		else
+			return { ... }
+		end
+	end
+	function del(t)
+		for k in pairs(t) do
+			t[k] = nil
+		end
+		cache[t] = true
+		return nil
+	end
+end
+
+local registeringFromAceEvent
+--[[----------------------------------------------------------------------------------
+Notes:
+	* Registers the addon with a Blizzard event or a custom AceEvent, which will cause the given method to be called when that is triggered.
+Arguments:
+	string - name of the event to register
+	[optional] string or function - name of the method or function to call. Default: same name as "event".
+	[optional] boolean - whether to have method called only once. Default: false
+------------------------------------------------------------------------------------]]
+function AceEvent:RegisterEvent(event, method, once)
+	AceEvent:argCheck(event, 2, "string")
+	if self == AceEvent and not registeringFromAceEvent then
+		AceEvent:argCheck(method, 3, "function")
+		self = method
+	else
+		AceEvent:argCheck(method, 3, "string", "function", "nil", "boolean", "number")
+		if type(method) == "boolean" or type(method) == "number" then
+			AceEvent:argCheck(once, 4, "nil")
+			once, method = method, event
+		end
+	end
+	AceEvent:argCheck(once, 4, "number", "boolean", "nil")
+	if eventsWhichHappenOnce[event] then
+		once = true
+	end
+	local throttleRate
+	if type(once) == "number" then
+		throttleRate, once = once
+	end
+	if not method then
+		method = event
+	end
+	if type(method) == "string" and type(self[method]) ~= "function" then
+		AceEvent:error("Cannot register event %q to method %q, it does not exist", event, method)
+	else
+		assert(type(method) == "function" or type(method) == "string")
+	end
+
+	local AceEvent_registry = AceEvent.registry
+	if not AceEvent_registry[event] then
+		AceEvent_registry[event] = new()
+		AceEvent.frame:RegisterEvent(event)
+	end
+
+	local remember = true
+	if AceEvent_registry[event][self] then
+		remember = false
+	end
+	AceEvent_registry[event][self] = method
+
+	local AceEvent_onceRegistry = AceEvent.onceRegistry
+	if once then
+		if not AceEvent_onceRegistry then
+			AceEvent.onceRegistry = {}
+			AceEvent_onceRegistry = AceEvent.onceRegistry
+		end
+		if not AceEvent_onceRegistry[event] then
+			AceEvent_onceRegistry[event] = new()
+		end
+		AceEvent_onceRegistry[event][self] = true
+	else
+		if AceEvent_onceRegistry and AceEvent_onceRegistry[event] then
+			AceEvent_onceRegistry[event][self] = nil
+			if not next(AceEvent_onceRegistry[event]) then
+				AceEvent_onceRegistry[event] = del(AceEvent_onceRegistry[event])
+			end
+		end
+	end
+
+	local AceEvent_throttleRegistry = AceEvent.throttleRegistry
+	if throttleRate then
+		if not AceEvent_throttleRegistry then
+			AceEvent.throttleRegistry = {}
+			AceEvent_throttleRegistry = AceEvent.throttleRegistry
+		end
+		if not AceEvent_throttleRegistry[event] then
+			AceEvent_throttleRegistry[event] = new()
+		end
+		if AceEvent_throttleRegistry[event][self] then
+			AceEvent_throttleRegistry[event][self] = nil
+		end
+		AceEvent_throttleRegistry[event][self] = setmetatable(new(), weakKey)
+		local t = AceEvent_throttleRegistry[event][self]
+		t[RATE] = throttleRate
+	else
+		if AceEvent_throttleRegistry and AceEvent_throttleRegistry[event] then
+			if AceEvent_throttleRegistry[event][self] then
+				AceEvent_throttleRegistry[event][self] = nil
+			end
+			if not next(AceEvent_throttleRegistry[event]) then
+				AceEvent_throttleRegistry[event] = del(AceEvent_throttleRegistry[event])
+			end
+		end
+	end
+
+	if remember then
+		AceEvent:TriggerEvent("AceEvent_EventRegistered", self, event)
+	end
+end
+
+local ALL_EVENTS
+
+--[[----------------------------------------------------------------------------------
+Notes:
+	* Registers all events to the given method
+	* To access the current event, check AceEvent.currentEvent
+	* To access the current event's unique identifier, check AceEvent.currentEventUID
+	* This is only for debugging purposes.
+Arguments:
+	[optional] string or function - name of the method or function to call. Default: same name as "event".
+------------------------------------------------------------------------------------]]
+function AceEvent:RegisterAllEvents(method)
+	if self == AceEvent then
+		AceEvent:argCheck(method, 1, "function")
+		self = method
+	else
+		AceEvent:argCheck(method, 1, "string", "function")
+		if type(method) == "string" and type(self[method]) ~= "function" then
+			AceEvent:error("Cannot register all events to method %q, it does not exist", method)
+		end
+	end
+	
+	local AceEvent_registry = AceEvent.registry
+	if not AceEvent_registry[ALL_EVENTS] then
+		AceEvent_registry[ALL_EVENTS] = new()
+		AceEvent.frame:RegisterAllEvents()
+	end
+	
+	local remember = not AceEvent_registry[ALL_EVENTS][self]
+	AceEvent_registry[ALL_EVENTS][self] = method
+	if remember then
+		AceEvent:TriggerEvent("AceEvent_EventRegistered", self, "all")
+	end
+end
+
+--[[----------------------------------------------------------------------------------
+Notes:
+	* Trigger a custom AceEvent.
+	* This should never be called to simulate fake Blizzard events.
+	* Custom events should be in the form of AddonName_SpecificEvent
+Arguments:
+	string - name of the event
+	tuple - list of arguments to pass along
+------------------------------------------------------------------------------------]]
+function AceEvent:TriggerEvent(event, ...)
+	if type(event) ~= "string" then
+		DEFAULT_CHAT_FRAME:AddMessage(debugstack())
+	end
+	AceEvent:argCheck(event, 2, "string")
+	local AceEvent_registry = AceEvent.registry
+	if (not AceEvent_registry[event] or not next(AceEvent_registry[event])) and (not AceEvent_registry[ALL_EVENTS] or not next(AceEvent_registry[ALL_EVENTS])) then
+		return
+	end
+	local lastEvent = AceEvent.currentEvent
+	AceEvent.currentEvent = event
+	local lastEventUID = AceEvent.currentEventUID
+	local uid = AceEvent.UID_NUM + 1
+	AceEvent.UID_NUM = uid
+	AceEvent.currentEventUID = uid
+
+	local tmp = new()
+
+	local AceEvent_onceRegistry = AceEvent.onceRegistry
+	if AceEvent_onceRegistry and AceEvent_onceRegistry[event] then
+		for obj, method in pairs(AceEvent_onceRegistry[event]) do
+			tmp[obj] = AceEvent_registry[event] and AceEvent_registry[event][obj] or nil
+		end
+		local obj = next(tmp)
+		while obj do
+			local method = tmp[obj]
+			AceEvent.UnregisterEvent(obj, event)
+			if type(method) == "string" then
+				local obj_method = obj[method]
+				if obj_method then
+					local success, err = pcall(obj_method, obj, ...)
+					if not success then geterrorhandler()(err:find("%.lua:%d+:") and err or (debugstack():match("(.-: )in.-\n") or "") .. err) end
+				end
+			elseif method then -- function
+				local success, err = pcall(method, ...)
+				if not success then geterrorhandler()(err:find("%.lua:%d+:") and err or (debugstack():match("(.-: )in.-\n") or "") .. err) end
+			end
+			tmp[obj] = nil
+			obj = next(tmp)
+		end
+	end
+
+	local AceEvent_throttleRegistry = AceEvent.throttleRegistry
+	local throttleTable = AceEvent_throttleRegistry and AceEvent_throttleRegistry[event]
+	if AceEvent_registry[event] then
+		for obj, method in pairs(AceEvent_registry[event]) do
+			tmp[obj] = method
+		end
+		local obj = next(tmp)
+		while obj do
+			local method = tmp[obj]
+			local continue = false
+			if throttleTable and throttleTable[obj] then
+				local a1 = ...
+				if a1 == nil then
+					a1 = FAKE_NIL
+				end
+				if not throttleTable[obj][a1] or GetTime() - throttleTable[obj][a1] >= throttleTable[obj][RATE] then
+					throttleTable[obj][a1] = GetTime()
+				else
+					continue = true
+				end
+			end
+			if not continue then
+				if type(method) == "string" then
+					local obj_method = obj[method]
+					if obj_method then
+						local success, err = pcall(obj_method, obj, ...)
+						if not success then geterrorhandler()(err:find("%.lua:%d+:") and err or (debugstack():match("(.-: )in.-\n") or "") .. err) end
+					end
+				elseif method then -- function
+					local success, err = pcall(method, ...)
+					if not success then geterrorhandler()(err:find("%.lua:%d+:") and err or (debugstack():match("(.-: )in.-\n") or "") .. err) end
+				end
+			end
+			tmp[obj] = nil
+			obj = next(tmp)
+		end
+	end
+	if AceEvent_registry[ALL_EVENTS] then
+		for obj, method in pairs(AceEvent_registry[ALL_EVENTS]) do
+			tmp[obj] = method
+		end
+		local obj = next(tmp)
+		while obj do
+			local method = tmp[obj]
+			if type(method) == "string" then
+				local obj_method = obj[method]
+				if obj_method then
+					local success, err = pcall(obj_method, obj, ...)
+					if not success then geterrorhandler()(err:find("%.lua:%d+:") and err or (debugstack():match("(.-: )in.-\n") or "") .. err) end
+				end
+			elseif method then -- function
+				local success, err = pcall(method, ...)
+				if not success then geterrorhandler()(err:find("%.lua:%d+:") and err or (debugstack():match("(.-: )in.-\n") or "") .. err) end
+			end
+			tmp[obj] = nil
+			obj = next(tmp)
+		end
+	end
+	tmp = del(tmp)
+	AceEvent.currentEvent = lastEvent
+	AceEvent.currentEventUID = lastEventUID
+end
+
+local delayRegistry
+local OnUpdate
+do
+	local tmp = {}
+	OnUpdate = function()
+		local t = GetTime()
+		for k,v in pairs(delayRegistry) do
+			tmp[k] = true
+		end
+		for k in pairs(tmp) do
+			local v = delayRegistry[k]
+			if v then
+				local v_time = v.time
+				if not v_time then
+					delayRegistry[k] = nil
+				elseif v_time <= t then
+					local v_repeatDelay = v.repeatDelay
+					if v_repeatDelay then
+						-- use the event time, not the current time, else timing inaccuracies add up over time
+						v.time = v_time + v_repeatDelay
+					end
+					local event = v.event
+					if type(event) == "function" then
+						local uid = AceEvent.UID_NUM + 1
+						AceEvent.UID_NUM = uid
+						AceEvent.currentEventUID = uid
+						local success, err = pcall(event, unpack(v, 1, v.n))
+						if not success then geterrorhandler()(err:find("%.lua:%d+:") and err or (debugstack():match("(.-: )in.-\n") or "") .. err) end
+						AceEvent.currentEventUID = nil
+					else
+						AceEvent:TriggerEvent(event, unpack(v, 1, v.n))
+					end
+					if not v_repeatDelay then
+						local x = delayRegistry[k]
+						if x and x.time == v_time then -- check if it was manually reset
+							if not useTablesAsIDs or type(k) == "string" then
+								del(delayRegistry[k])
+							end
+							delayRegistry[k] = nil
+						end
+					end
+				end
+			end
+		end
+		for k in pairs(tmp) do
+			tmp[k] = nil
+		end
+		if not next(delayRegistry) then
+			AceEvent.frame:Hide()
+		end
+	end
+end
+
+local function ScheduleEvent(self, repeating, event, delay, ...)
+	local id
+	if type(event) == "string" or (useTablesAsIDs and type(event) == "table") then
+		if useTablesAsIDs and type(event) == "table" then
+			if not delayRegistry or not delayRegistry[event] then
+				AceEvent:error("Bad argument #2 to `ScheduleEvent'. Improper id table fed in.")
+			end
+		end
+		if type(delay) ~= "number" then
+			id, event, delay = event, delay, ...
+			AceEvent:argCheck(event, 3, "string", "function", --[[ so message is right ]] "number")
+			AceEvent:argCheck(delay, 4, "number")
+			self:CancelScheduledEvent(id)
+		end
+	else
+		AceEvent:argCheck(event, 2, "string", "function")
+		AceEvent:argCheck(delay, 3, "number")
+	end
+
+	if not delayRegistry then
+		AceEvent.delayRegistry = {}
+		delayRegistry = AceEvent.delayRegistry
+		AceEvent.frame:SetScript("OnUpdate", OnUpdate)
+	end
+	local t
+	if useTablesAsIDs and type(id) == "table" then
+		for k in pairs(id) do
+			id[k] = nil
+		end
+		t = id
+		for i = 2, select('#', ...) do
+			t[i-1] = select(i, ...)
+		end
+		t.n = select('#', ...) - 1
+	elseif id then
+		t = new(select(2, ...))
+		t.n = select('#', ...) - 1
+	else
+		t = new(...)
+		t.n = select('#', ...)
+	end
+	t.event = event
+	t.time = GetTime() + delay
+	t.self = self
+	t.id = id or t
+	t.repeatDelay = repeating and delay
+	delayRegistry[t.id] = t
+	AceEvent.frame:Show()
+	if useTablesAsIDs then
+		return t.id
+	else
+		return
+	end
+end
+
+--[[----------------------------------------------------------------------------------
+Notes:
+	* Schedule an event to fire.
+	* To fire on the next frame, specify a delay of 0.
+Arguments:
+	string or function - name of the event to fire, or a function to call.
+	number - the amount of time to wait until calling.
+	tuple - a list of arguments to pass along.
+------------------------------------------------------------------------------------]]
+function AceEvent:ScheduleEvent(event, delay, ...)
+	if type(event) == "string" or (useTablesAsIDs and type(event) == "table") then
+		if useTablesAsIDs and type(event) == "table" then
+			if not delayRegistry or not delayRegistry[event] then
+				AceEvent:error("Bad argument #2 to `ScheduleEvent'. Improper id table fed in.")
+			end
+		end
+		if type(delay) ~= "number" then
+			AceEvent:argCheck(delay, 3, "string", "function", --[[ so message is right ]] "number")
+			AceEvent:argCheck(..., 4, "number")
+		end
+	else
+		AceEvent:argCheck(event, 2, "string", "function")
+		AceEvent:argCheck(delay, 3, "number")
+	end
+
+	return ScheduleEvent(self, false, event, delay, ...)
+end
+
+function AceEvent:ScheduleRepeatingEvent(event, delay, ...)
+	if type(event) == "string" or (useTablesAsIDs and type(event) == "table") then
+		if useTablesAsIDs and type(event) == "table" then
+			if not delayRegistry or not delayRegistry[event] then
+				AceEvent:error("Bad argument #2 to `ScheduleEvent'. Improper id table fed in.")
+			end
+		end
+		if type(delay) ~= "number" then
+			AceEvent:argCheck(delay, 3, "string", "function", --[[ so message is right ]] "number")
+			AceEvent:argCheck(..., 4, "number")
+		end
+	else
+		AceEvent:argCheck(event, 2, "string", "function")
+		AceEvent:argCheck(delay, 3, "number")
+	end
+
+	return ScheduleEvent(self, true, event, delay, ...)
+end
+
+function AceEvent:CancelScheduledEvent(t)
+	if useTablesAsIDs then
+		AceEvent:argCheck(t, 2, "string", "table")
+	else
+		AceEvent:argCheck(t, 2, "string")
+	end
+	if delayRegistry then
+		local v = delayRegistry[t]
+		if v then
+			if not useTablesAsIDs or type(t) == "string" then
+				del(delayRegistry[t])
+			end
+			delayRegistry[t] = nil
+			if not next(delayRegistry) then
+				AceEvent.frame:Hide()
+			end
+			return true
+		end
+	end
+	return false
+end
+
+function AceEvent:IsEventScheduled(t)
+	if useTablesAsIDs then
+		AceEvent:argCheck(t, 2, "string", "table")
+	else
+		AceEvent:argCheck(t, 2, "string")
+	end
+	if delayRegistry then
+		local v = delayRegistry[t]
+		if v then
+			return true, v.time - GetTime()
+		end
+	end
+	return false, nil
+end
+
+function AceEvent:UnregisterEvent(event)
+	AceEvent:argCheck(event, 2, "string")
+	local AceEvent_registry = AceEvent.registry
+	if AceEvent_registry[event] and AceEvent_registry[event][self] then
+		AceEvent_registry[event][self] = nil
+		local AceEvent_onceRegistry = AceEvent.onceRegistry
+		if AceEvent_onceRegistry and AceEvent_onceRegistry[event] and AceEvent_onceRegistry[event][self] then
+			AceEvent_onceRegistry[event][self] = nil
+			if not next(AceEvent_onceRegistry[event]) then
+				AceEvent_onceRegistry[event] = del(AceEvent_onceRegistry[event])
+			end
+		end
+		local AceEvent_throttleRegistry = AceEvent.throttleRegistry
+		if AceEvent_throttleRegistry and AceEvent_throttleRegistry[event] and AceEvent_throttleRegistry[event][self] then
+			AceEvent_throttleRegistry[event][self] = nil
+			if not next(AceEvent_throttleRegistry[event]) then
+				AceEvent_throttleRegistry[event] = del(AceEvent_throttleRegistry[event])
+			end
+		end
+		if not next(AceEvent_registry[event]) then
+			AceEvent_registry[event] = del(AceEvent_registry[event])
+			if not AceEvent_registry[ALL_EVENTS] or not next(AceEvent_registry[ALL_EVENTS]) then
+				AceEvent.frame:UnregisterEvent(event)
+			end
+		end
+	else
+		if self == AceEvent then
+			error(("Cannot unregister event %q. Improperly unregistering from AceEvent-2.0."):format(event), 2)
+		else
+			AceEvent:error("Cannot unregister event %q. %q is not registered with it.", event, self)
+		end
+	end
+	AceEvent:TriggerEvent("AceEvent_EventUnregistered", self, event)
+end
+
+function AceEvent:UnregisterAllEvents()
+	local AceEvent_registry = AceEvent.registry
+	if AceEvent_registry[ALL_EVENTS] and AceEvent_registry[ALL_EVENTS][self] then
+		AceEvent_registry[ALL_EVENTS][self] = nil
+		if not next(AceEvent_registry[ALL_EVENTS]) then
+			AceEvent_registry[ALL_EVENTS] = del(AceEvent_registry[ALL_EVENTS])
+			AceEvent.frame:UnregisterAllEvents()
+			for k,v in pairs(AceEvent_registry) do
+				AceEvent.frame:RegisterEvent(k)
+			end
+		end
+	end
+	if AceEvent_registry.AceEvent_EventUnregistered then
+		local event, data = "AceEvent_EventUnregistered", AceEvent_registry.AceEvent_EventUnregistered
+		local x = data[self]
+		data[self] = nil
+		if x then
+			if not next(data) then
+				if not AceEvent_registry[ALL_EVENTS] then
+					AceEvent.frame:UnregisterEvent(event)
+				end
+				AceEvent_registry[event] = del(AceEvent_registry[event])
+			end
+			AceEvent:TriggerEvent("AceEvent_EventUnregistered", self, event)
+		end
+	end
+	for event, data in pairs(AceEvent_registry) do
+		local x = data[self]
+		data[self] = nil
+		if x and event ~= ALL_EVENTS then
+			if not next(data) then
+				if not AceEvent_registry[ALL_EVENTS] then
+					AceEvent.frame:UnregisterEvent(event)
+				end
+				AceEvent_registry[event] = del(AceEvent_registry[event])
+			end
+			AceEvent:TriggerEvent("AceEvent_EventUnregistered", self, event)
+		end
+	end
+	if AceEvent.onceRegistry then
+		for event, data in pairs(AceEvent.onceRegistry) do
+			data[self] = nil
+		end
+	end
+end
+
+function AceEvent:CancelAllScheduledEvents()
+	if delayRegistry then
+		for k,v in pairs(delayRegistry) do
+			if v.self == self then
+				if not useTablesAsIDs or type(k) == "string" then
+					del(delayRegistry[k])
+				end
+				delayRegistry[k] = nil
+			end
+		end
+		if not next(delayRegistry) then
+			AceEvent.frame:Hide()
+		end
+	end
+end
+
+function AceEvent:IsEventRegistered(event)
+	AceEvent:argCheck(event, 2, "string")
+	local AceEvent_registry = AceEvent.registry
+	if self == AceEvent then
+		return AceEvent_registry[event] and next(AceEvent_registry[event]) or AceEvent_registry[ALL_EVENTS] and next(AceEvent_registry[ALL_EVENTS]) and true or false
+	end
+	if AceEvent_registry[event] and AceEvent_registry[event][self] then
+		return true, AceEvent_registry[event][self]
+	end
+	if AceEvent_registry[ALL_EVENTS] and AceEvent_registry[ALL_EVENTS][self] then
+		return true, AceEvent_registry[ALL_EVENTS][self]
+	end
+	return false, nil
+end
+
+local UnitExists = UnitExists
+local bucketfunc
+function AceEvent:RegisterBucketEvent(event, delay, method, ...)
+	AceEvent:argCheck(event, 2, "string", "table")
+	if type(event) == "table" then
+		for k,v in pairs(event) do
+			if type(k) ~= "number" then
+				AceEvent:error("All keys to argument #2 to `RegisterBucketEvent' must be numbers.")
+			elseif type(v) ~= "string" then
+				AceEvent:error("All values to argument #2 to `RegisterBucketEvent' must be strings.")
+			end
+		end
+	end
+	AceEvent:argCheck(delay, 3, "number")
+	if AceEvent == self then
+		AceEvent:argCheck(method, 4, "function")
+		self = method
+	else
+		if type(event) == "string" then
+			AceEvent:argCheck(method, 4, "string", "function", "nil")
+			if not method then
+				method = event
+			end
+		else
+			AceEvent:argCheck(method, 4, "string", "function")
+		end
+
+		if type(method) == "string" and type(self[method]) ~= "function" then
+			AceEvent:error("Cannot register event %q to method %q, it does not exist", event, method)
+		end
+	end
+	local buckets = AceEvent.buckets
+	if not buckets[event] then
+		buckets[event] = new()
+	end
+	if not buckets[event][self] then
+		local t = new()
+		t.current = new()
+		t.self = self
+		buckets[event][self] = t
+	else
+		AceEvent.CancelScheduledEvent(self, buckets[event][self].id)
+	end
+	local bucket = buckets[event][self]
+	bucket.method = method
+	
+	local n = select('#', ...)
+	if n > 0 then
+		for i = 1, n do
+			bucket[i] = select(i, ...)
+		end
+	end
+	bucket.n = n
+
+	local func = function(arg1)
+		bucket.run = true
+		if arg1 then
+			bucket.current[arg1] = true
+		end
+	end
+	buckets[event][self].func = func
+	local isUnitBucket = true
+	if type(event) == "string" then
+		AceEvent.RegisterEvent(self, event, func)
+		if not event:find("^UNIT_") then
+			isUnitBucket = false
+		end
+	else
+		for _,v in ipairs(event) do
+			AceEvent.RegisterEvent(self, v, func)
+			if isUnitBucket and not v:find("^UNIT_") then
+				isUnitBucket = false
+			end
+		end
+	end
+	bucket.unit = isUnitBucket
+	if not bucketfunc then
+		bucketfunc = function(bucket)
+			local current = bucket.current
+			local method = bucket.method
+			local self = bucket.self
+			if bucket.run then
+				if bucket.unit then
+					for unit in pairs(current) do
+						if not UnitExists(unit) then
+							current[unit] = nil
+						end
+					end
+				end
+				if type(method) == "string" then
+					self[method](self, current, unpack(bucket, 1, bucket.n))
+				elseif method then -- function
+					method(current, unpack(bucket, 1, bucket.n))
+				end
+				for k in pairs(current) do
+					current[k] = nil
+					k = nil
+				end
+				bucket.run = false
+			end
+		end
+	end
+	bucket.id = "AceEvent-Bucket-" .. tostring(bucket)
+	AceEvent.ScheduleRepeatingEvent(self, bucket.id, bucketfunc, delay, bucket)
+end
+
+function AceEvent:IsBucketEventRegistered(event)
+	AceEvent:argCheck(event, 2, "string", "table")
+	return AceEvent.buckets and AceEvent.buckets[event] and AceEvent.buckets[event][self]
+end
+
+function AceEvent:UnregisterBucketEvent(event)
+	AceEvent:argCheck(event, 2, "string", "table")
+	if not AceEvent.buckets or not AceEvent.buckets[event] or not AceEvent.buckets[event][self] then
+		AceEvent:error("Cannot unregister bucket event %q. %q is not registered with it.", event, self)
+	end
+
+	local bucket = AceEvent.buckets[event][self]
+
+	if type(event) == "string" then
+		AceEvent.UnregisterEvent(self, event)
+	else
+		for _,v in ipairs(event) do
+			AceEvent.UnregisterEvent(self, v)
+		end
+	end
+	AceEvent:CancelScheduledEvent(bucket.id)
+
+	bucket.current = del(bucket.current)
+	AceEvent.buckets[event][self] = del(bucket)
+	if not next(AceEvent.buckets[event]) then
+		AceEvent.buckets[event] = del(AceEvent.buckets[event])
+	end
+end
+
+function AceEvent:UnregisterAllBucketEvents()
+	if not AceEvent.buckets or not next(AceEvent.buckets) then
+		return
+	end
+	for k,v in pairs(AceEvent.buckets) do
+		if v == self then
+			AceEvent.UnregisterBucketEvent(self, k)
+			k = nil
+		end
+	end
+end
+
+local combatSchedules
+function AceEvent:CancelAllCombatSchedules()
+	local i = 0
+	while true do
+		i = i + 1
+		if not combatSchedules[i] then
+			break
+		end
+		local v = combatSchedules[i]
+		if v.self == self then
+			v = del(v)
+			table.remove(combatSchedules, i)
+			i = i - 1
+		end
+	end
+end
+
+local inCombat = false
+
+function AceEvent:PLAYER_REGEN_DISABLED()
+	inCombat = true
+end
+
+do
+	local tmp = {}
+	function AceEvent:PLAYER_REGEN_ENABLED()
+		inCombat = false
+		for i, v in ipairs(combatSchedules) do
+			tmp[i] = v
+			combatSchedules[i] = nil
+		end
+		for i, v in ipairs(tmp) do
+			local func = v.func
+			if func then
+				local success, err = pcall(func, unpack(v, 1, v.n))
+				if not success then geterrorhandler()(err:find("%.lua:%d+:") and err or (debugstack():match("(.-: )in.-\n") or "") .. err) end
+			else
+				local obj = v.obj or v.self
+				local method = v.method
+				local obj_method = obj[method]
+				if obj_method then
+					local success, err = pcall(obj_method, obj, unpack(v, 1, v.n))
+					if not success then geterrorhandler()(err:find("%.lua:%d+:") and err or (debugstack():match("(.-: )in.-\n") or "") .. err) end
+				end
+			end
+			tmp[i] = del(v)
+		end
+	end
+end
+
+function AceEvent:ScheduleLeaveCombatAction(method, ...)
+	local style = type(method)
+	if self == AceEvent then
+		if style == "table" then
+			local func = (...)
+			AceEvent:argCheck(func, 3, "string")
+			if type(method[func]) ~= "function" then
+				AceEvent:error("Cannot schedule a combat action to method %q, it does not exist", func)
+			end
+		else
+			AceEvent:argCheck(method, 2, "function", --[[so message is right]] "table")
+		end
+		self = method
+	else
+		AceEvent:argCheck(method, 2, "function", "string", "table")
+		if style == "string" and type(self[method]) ~= "function" then
+			AceEvent:error("Cannot schedule a combat action to method %q, it does not exist", method)
+		elseif style == "table" then
+			local func = (...)
+			AceEvent:argCheck(func, 3, "string")
+			if type(method[func]) ~= "function" then
+				AceEvent:error("Cannot schedule a combat action to method %q, it does not exist", func)
+			end
+		end
+	end
+	
+	if not inCombat then
+		local success, err
+		if type(method) == "function" then
+			success, err = pcall(method, ...)
+		elseif type(method) == "table" then
+			local func = (...)
+			success, err = pcall(method[func], method, select(2, ...))
+		else
+			success, err = pcall(self[method], self, ...)
+		end
+		if not success then geterrorhandler()(err:find("%.lua:%d+:") and err or (debugstack():match("(.-: )in.-\n") or "") .. err) end
+		return
+	end
+	local t
+	local n = select('#', ...)
+	if style == "table" then
+		t = new(select(2, ...))
+		t.obj = method
+		t.method = (...)
+		t.n = n-1
+	else
+		t = new(...)
+		t.n = n
+		if style == "function" then
+			t.func = method
+		else
+			t.method = method
+		end
+	end
+	t.self = self
+	table.insert(combatSchedules, t)
+end
+
+function AceEvent:OnEmbedDisable(target)
+	self.UnregisterAllEvents(target)
+
+	self.CancelAllScheduledEvents(target)
+
+	self.UnregisterAllBucketEvents(target)
+	
+	self.CancelAllCombatSchedules(target)
+end
+
+function AceEvent:IsFullyInitialized()
+	return self.postInit or false
+end
+
+function AceEvent:IsPostPlayerLogin()
+	return IsLoggedIn() and true or false
+end
+
+local function activate(self, oldLib, oldDeactivate)
+	AceEvent = self
+	
+	self.onceRegistry = oldLib and oldLib.onceRegistry or {}
+	self.throttleRegistry = oldLib and oldLib.throttleRegistry or {}
+	self.delayRegistry = oldLib and oldLib.delayRegistry or {}
+	self.buckets = oldLib and oldLib.buckets or {}
+	self.registry = oldLib and oldLib.registry or {}
+	self.frame = oldLib and oldLib.frame or CreateFrame("Frame", "AceEvent20Frame")
+	self.playerLogin = IsLoggedIn() and true
+	self.postInit = oldLib and oldLib.postInit or self.playerLogin and ChatTypeInfo and ChatTypeInfo.WHISPER and ChatTypeInfo.WHISPER.r and true
+	self.ALL_EVENTS = oldLib and oldLib.ALL_EVENTS or _G.newproxy()
+	self.FAKE_NIL = oldLib and oldLib.FAKE_NIL or _G.newproxy()
+	self.RATE = oldLib and oldLib.RATE or _G.newproxy()
+	self.combatSchedules = oldLib and oldLib.combatSchedules or {}
+	self.UID_NUM = oldLib and oldLib.UID_NUM or 0
+	
+	-- Delete this down the road.  Makes sure that the addonframes from revisions 33121 - 36174 get their events unregistered.
+	local addonframes = oldLib and oldLib.addonframes
+	if addonframes then
+		for _, v in pairs(addonframes) do
+			v:UnregisterAllEvents()
+		end
+	end
+	
+	combatSchedules = self.combatSchedules
+	ALL_EVENTS = self.ALL_EVENTS
+	FAKE_NIL = self.FAKE_NIL
+	RATE = self.RATE
+	local inPlw = false
+	local blacklist = {
+		UNIT_INVENTORY_CHANGED = true,
+		BAG_UPDATE = true,
+		ITEM_LOCK_CHANGED = true,
+		ACTIONBAR_SLOT_CHANGED = true,
+	}
+	self.frame:SetScript("OnEvent", function(_, event, ...)
+		if event == "PLAYER_ENTERING_WORLD" then
+			inPlw = false
+		elseif event == "PLAYER_LEAVING_WORLD" then
+			inPlw = true
+		end
+		if event and (not inPlw or not blacklist[event]) then
+			self:TriggerEvent(event, ...)
+		end
+	end)
+	if self.delayRegistry then
+		delayRegistry = self.delayRegistry
+		self.frame:SetScript("OnUpdate", OnUpdate)
+	end
+
+	self:UnregisterAllEvents()
+	self:CancelAllScheduledEvents()
+
+	registeringFromAceEvent = true
+	self:RegisterEvent("LOOT_OPENED", function()
+		SendAddonMessage("LOOT_OPENED", "", "RAID")
+	end)
+	registeringFromAceEvent = nil
+
+	local function handleFullInit()
+		if not self.postInit then
+			local function func()
+				self.postInit = true
+				self:TriggerEvent("AceEvent_FullyInitialized")
+				if self.registry["CHAT_MSG_CHANNEL_NOTICE"] and self.registry["CHAT_MSG_CHANNEL_NOTICE"][self] then
+					self:UnregisterEvent("CHAT_MSG_CHANNEL_NOTICE")
+				end
+				if self.registry["MEETINGSTONE_CHANGED"] and self.registry["MEETINGSTONE_CHANGED"][self] then
+					self:UnregisterEvent("MEETINGSTONE_CHANGED")
+				end
+				if self.registry["MINIMAP_ZONE_CHANGED"] and self.registry["MINIMAP_ZONE_CHANGED"][self] then
+					self:UnregisterEvent("MINIMAP_ZONE_CHANGED")
+				end
+				if self.registry["LANGUAGE_LIST_CHANGED"] and self.registry["LANGUAGE_LIST_CHANGED"][self] then
+					self:UnregisterEvent("LANGUAGE_LIST_CHANGED")
+				end
+				collectgarbage('collect')
+			end
+			registeringFromAceEvent = true
+			local f = function()
+				self.playerLogin = true
+				self:ScheduleEvent("AceEvent_FullyInitialized", func, 1)
+			end
+			self:RegisterEvent("MEETINGSTONE_CHANGED", f, true)
+			self:RegisterEvent("CHAT_MSG_CHANNEL_NOTICE", function()
+				self:ScheduleEvent("AceEvent_FullyInitialized", func, 0.15)
+			end)
+			self:RegisterEvent("LANGUAGE_LIST_CHANGED", function()
+				if self.registry["MEETINGSTONE_CHANGED"] and self.registry["MEETINGSTONE_CHANGED"][self] then
+					registeringFromAceEvent = true
+					self:UnregisterEvent("MEETINGSTONE_CHANGED")
+					self:RegisterEvent("MINIMAP_ZONE_CHANGED", fd, true)
+					registeringFromAceEvent = nil
+				end
+			end)
+			self:ScheduleEvent("AceEvent_FullyInitialized", func, 10)
+			registeringFromAceEvent = nil
+		end
+	end
+	
+	if not self.playerLogin then
+		registeringFromAceEvent = true
+		self:RegisterEvent("PLAYER_LOGIN", function()
+			self.playerLogin = true
+			handleFullInit()
+			handleFullInit = nil
+			collectgarbage('collect')
+		end, true)
+		registeringFromAceEvent = nil
+	else
+		handleFullInit()
+		handleFullInit = nil
+	end
+	
+	if not AceEvent20EditBox then
+	    CreateFrame("Editbox", "AceEvent20EditBox")
+	end
+	local editbox = AceEvent20EditBox
+	function editbox:Execute(line)
+		local defaulteditbox = DEFAULT_CHAT_FRAME.editBox
+		self:SetAttribute("chatType", defaulteditbox:GetAttribute("chatType"))
+		self:SetAttribute("tellTarget", defaulteditbox:GetAttribute("tellTarget"))
+		self:SetAttribute("channelTarget", defaulteditbox:GetAttribute("channelTarget"))
+		self:SetText(line)
+		ChatEdit_SendText(self)
+	end
+	editbox:Hide()
+	_G["SLASH_IN1"] = "/in"
+	SlashCmdList["IN"] = function(msg)
+		local seconds, command, rest = msg:match("^([^%s]+)%s+(/[^%s]+)(.*)$")
+		seconds = tonumber(seconds)
+		if not seconds then
+			DEFAULT_CHAT_FRAME:AddMessage("Error, bad arguments to /in. Must be in the form of `/in 5 /say hi'")
+			return
+		end
+		if IsSecureCmd(command) then
+			DEFAULT_CHAT_FRAME:AddMessage(("Error, /in cannot call secure command: %s"):format(command))
+			return
+		end
+		self:ScheduleEvent("AceEventSlashIn-" .. math.random(1, 1000000000), editbox.Execute, seconds, editbox, command .. rest)
+	end
+	registeringFromAceEvent = true
+	self:RegisterEvent("PLAYER_REGEN_ENABLED")
+	self:RegisterEvent("PLAYER_REGEN_DISABLED")
+	inCombat = InCombatLockdown()
+	registeringFromAceEvent = nil
+	
+	-- another hack to make sure that we clean up properly from rev 33121 - 36174
+	if self.registry[ALL_EVENTS] then
+		self.frame:RegisterAllEvents()
+	else
+		for event in pairs(self.registry) do
+			self.frame:RegisterEvent(event)
+		end
+	end
+	
+	self:activate(oldLib, oldDeactivate)
+	if oldLib then
+		oldDeactivate(oldLib)
+	end
+end
+
+AceLibrary:Register(AceEvent, MAJOR_VERSION, MINOR_VERSION, activate)