diff --git a/.pkgmeta b/.pkgmeta
index c7efa0c..64dced3 100644
--- a/.pkgmeta
+++ b/.pkgmeta
@@ -3,6 +3,8 @@ package-as: RatingBuster
externals:
libs/LibStub:
url: https://repos.wowace.com/wow/libstub/trunk
+ libs/CallbackHandler-1.0:
+ url: https://repos.wowace.com/wow/callbackhandler/trunk
libs/AceLocale-3.0:
url: https://repos.wowace.com/wow/ace3/trunk/AceLocale-3.0
libs/LibBabble-Inventory-3.0:
@@ -11,12 +13,18 @@ externals:
url: https://repos.wowace.com/wow/ace3/trunk/AceConsole-3.0
libs/AceEvent-3.0:
url: https://repos.wowace.com/wow/ace3/trunk/AceEvent-3.0
+ libs/AceTimer-3.0:
+ url: https://repos.wowace.com/wow/ace3/trunk/AceTimer-3.0
libs/AceBucket-3.0:
url: https://repos.wowace.com/wow/ace3/trunk/AceBucket-3.0
libs/AceDB-3.0:
url: https://repos.wowace.com/wow/ace3/trunk/AceDB-3.0
+ libs/AceDBOptions-3.0:
+ url: https://repos.wowace.com/wow/ace3/trunk/AceDBOptions-3.0
libs/AceAddon-3.0:
url: https://repos.wowace.com/wow/ace3/trunk/AceAddon-3.0
+ libs/AceGUI-3.0:
+ url: https://repos.wowace.com/wow/ace3/trunk/AceGUI-3.0
libs/AceConfig-3.0:
url: https://repos.wowace.com/wow/ace3/trunk/AceConfig-3.0
diff --git a/embeds.xml b/embeds.xml
index a2fb064..c2b5db3 100644
--- a/embeds.xml
+++ b/embeds.xml
@@ -2,23 +2,28 @@
..\FrameXML\UI.xsd">
+
-
-
-
+
-
+
-
+
+
+
-
-
+
+
-
-
+
+
+
+
+
+
diff --git a/libs/AceTimer-3.0/AceTimer-3.0.lua b/libs/AceTimer-3.0/AceTimer-3.0.lua
new file mode 100644
index 0000000..f576fe7
--- /dev/null
+++ b/libs/AceTimer-3.0/AceTimer-3.0.lua
@@ -0,0 +1,278 @@
+--- **AceTimer-3.0** provides a central facility for registering timers.
+-- AceTimer supports one-shot timers and repeating timers. All timers are stored in an efficient
+-- data structure that allows easy dispatching and fast rescheduling. Timers can be registered
+-- or canceled at any time, even from within a running timer, without conflict or large overhead.\\
+-- AceTimer is currently limited to firing timers at a frequency of 0.01s as this is what the WoW timer API
+-- restricts us to.
+--
+-- All `:Schedule` functions will return a handle to the current timer, which you will need to store if you
+-- need to cancel the timer you just registered.
+--
+-- **AceTimer-3.0** can be embeded into your addon, either explicitly by calling AceTimer:Embed(MyAddon) or by
+-- specifying it as an embeded library in your AceAddon. All functions will be available on your addon object
+-- and can be accessed directly, without having to explicitly call AceTimer itself.\\
+-- It is recommended to embed AceTimer, otherwise you'll have to specify a custom `self` on all calls you
+-- make into AceTimer.
+-- @class file
+-- @name AceTimer-3.0
+-- @release $Id: AceTimer-3.0.lua 1202 2019-05-15 23:11:22Z nevcairiel $
+
+local MAJOR, MINOR = "AceTimer-3.0", 17 -- Bump minor on changes
+local AceTimer, oldminor = LibStub:NewLibrary(MAJOR, MINOR)
+
+if not AceTimer then return end -- No upgrade needed
+AceTimer.activeTimers = AceTimer.activeTimers or {} -- Active timer list
+local activeTimers = AceTimer.activeTimers -- Upvalue our private data
+
+-- Lua APIs
+local type, unpack, next, error, select = type, unpack, next, error, select
+-- WoW APIs
+local GetTime, C_TimerAfter = GetTime, C_Timer.After
+
+local function new(self, loop, func, delay, ...)
+ if delay < 0.01 then
+ delay = 0.01 -- Restrict to the lowest time that the C_Timer API allows us
+ end
+
+ local timer = {
+ object = self,
+ func = func,
+ looping = loop,
+ argsCount = select("#", ...),
+ delay = delay,
+ ends = GetTime() + delay,
+ ...
+ }
+
+ activeTimers[timer] = timer
+
+ -- Create new timer closure to wrap the "timer" object
+ timer.callback = function()
+ if not timer.cancelled then
+ if type(timer.func) == "string" then
+ -- We manually set the unpack count to prevent issues with an arg set that contains nil and ends with nil
+ -- e.g. local t = {1, 2, nil, 3, nil} print(#t) will result in 2, instead of 5. This fixes said issue.
+ timer.object[timer.func](timer.object, unpack(timer, 1, timer.argsCount))
+ else
+ timer.func(unpack(timer, 1, timer.argsCount))
+ end
+
+ if timer.looping and not timer.cancelled then
+ -- Compensate delay to get a perfect average delay, even if individual times don't match up perfectly
+ -- due to fps differences
+ local time = GetTime()
+ local delay = timer.delay - (time - timer.ends)
+ -- Ensure the delay doesn't go below the threshold
+ if delay < 0.01 then delay = 0.01 end
+ C_TimerAfter(delay, timer.callback)
+ timer.ends = time + delay
+ else
+ activeTimers[timer.handle or timer] = nil
+ end
+ end
+ end
+
+ C_TimerAfter(delay, timer.callback)
+ return timer
+end
+
+--- Schedule a new one-shot timer.
+-- The timer will fire once in `delay` seconds, unless canceled before.
+-- @param callback Callback function for the timer pulse (funcref or method name).
+-- @param delay Delay for the timer, in seconds.
+-- @param ... An optional, unlimited amount of arguments to pass to the callback function.
+-- @usage
+-- MyAddOn = LibStub("AceAddon-3.0"):NewAddon("MyAddOn", "AceTimer-3.0")
+--
+-- function MyAddOn:OnEnable()
+-- self:ScheduleTimer("TimerFeedback", 5)
+-- end
+--
+-- function MyAddOn:TimerFeedback()
+-- print("5 seconds passed")
+-- end
+function AceTimer:ScheduleTimer(func, delay, ...)
+ if not func or not delay then
+ error(MAJOR..": ScheduleTimer(callback, delay, args...): 'callback' and 'delay' must have set values.", 2)
+ end
+ if type(func) == "string" then
+ if type(self) ~= "table" then
+ error(MAJOR..": ScheduleTimer(callback, delay, args...): 'self' - must be a table.", 2)
+ elseif not self[func] then
+ error(MAJOR..": ScheduleTimer(callback, delay, args...): Tried to register '"..func.."' as the callback, but it doesn't exist in the module.", 2)
+ end
+ end
+ return new(self, nil, func, delay, ...)
+end
+
+--- Schedule a repeating timer.
+-- The timer will fire every `delay` seconds, until canceled.
+-- @param callback Callback function for the timer pulse (funcref or method name).
+-- @param delay Delay for the timer, in seconds.
+-- @param ... An optional, unlimited amount of arguments to pass to the callback function.
+-- @usage
+-- MyAddOn = LibStub("AceAddon-3.0"):NewAddon("MyAddOn", "AceTimer-3.0")
+--
+-- function MyAddOn:OnEnable()
+-- self.timerCount = 0
+-- self.testTimer = self:ScheduleRepeatingTimer("TimerFeedback", 5)
+-- end
+--
+-- function MyAddOn:TimerFeedback()
+-- self.timerCount = self.timerCount + 1
+-- print(("%d seconds passed"):format(5 * self.timerCount))
+-- -- run 30 seconds in total
+-- if self.timerCount == 6 then
+-- self:CancelTimer(self.testTimer)
+-- end
+-- end
+function AceTimer:ScheduleRepeatingTimer(func, delay, ...)
+ if not func or not delay then
+ error(MAJOR..": ScheduleRepeatingTimer(callback, delay, args...): 'callback' and 'delay' must have set values.", 2)
+ end
+ if type(func) == "string" then
+ if type(self) ~= "table" then
+ error(MAJOR..": ScheduleRepeatingTimer(callback, delay, args...): 'self' - must be a table.", 2)
+ elseif not self[func] then
+ error(MAJOR..": ScheduleRepeatingTimer(callback, delay, args...): Tried to register '"..func.."' as the callback, but it doesn't exist in the module.", 2)
+ end
+ end
+ return new(self, true, func, delay, ...)
+end
+
+--- Cancels a timer with the given id, registered by the same addon object as used for `:ScheduleTimer`
+-- Both one-shot and repeating timers can be canceled with this function, as long as the `id` is valid
+-- and the timer has not fired yet or was canceled before.
+-- @param id The id of the timer, as returned by `:ScheduleTimer` or `:ScheduleRepeatingTimer`
+function AceTimer:CancelTimer(id)
+ local timer = activeTimers[id]
+
+ if not timer then
+ return false
+ else
+ timer.cancelled = true
+ activeTimers[id] = nil
+ return true
+ end
+end
+
+--- Cancels all timers registered to the current addon object ('self')
+function AceTimer:CancelAllTimers()
+ for k,v in next, activeTimers do
+ if v.object == self then
+ AceTimer.CancelTimer(self, k)
+ end
+ end
+end
+
+--- Returns the time left for a timer with the given id, registered by the current addon object ('self').
+-- This function will return 0 when the id is invalid.
+-- @param id The id of the timer, as returned by `:ScheduleTimer` or `:ScheduleRepeatingTimer`
+-- @return The time left on the timer.
+function AceTimer:TimeLeft(id)
+ local timer = activeTimers[id]
+ if not timer then
+ return 0
+ else
+ return timer.ends - GetTime()
+ end
+end
+
+
+-- ---------------------------------------------------------------------
+-- Upgrading
+
+-- Upgrade from old hash-bucket based timers to C_Timer.After timers.
+if oldminor and oldminor < 10 then
+ -- disable old timer logic
+ AceTimer.frame:SetScript("OnUpdate", nil)
+ AceTimer.frame:SetScript("OnEvent", nil)
+ AceTimer.frame:UnregisterAllEvents()
+ -- convert timers
+ for object,timers in next, AceTimer.selfs do
+ for handle,timer in next, timers do
+ if type(timer) == "table" and timer.callback then
+ local newTimer
+ if timer.delay then
+ newTimer = AceTimer.ScheduleRepeatingTimer(timer.object, timer.callback, timer.delay, timer.arg)
+ else
+ newTimer = AceTimer.ScheduleTimer(timer.object, timer.callback, timer.when - GetTime(), timer.arg)
+ end
+ -- Use the old handle for old timers
+ activeTimers[newTimer] = nil
+ activeTimers[handle] = newTimer
+ newTimer.handle = handle
+ end
+ end
+ end
+ AceTimer.selfs = nil
+ AceTimer.hash = nil
+ AceTimer.debug = nil
+elseif oldminor and oldminor < 17 then
+ -- Upgrade from old animation based timers to C_Timer.After timers.
+ AceTimer.inactiveTimers = nil
+ AceTimer.frame = nil
+ local oldTimers = AceTimer.activeTimers
+ -- Clear old timer table and update upvalue
+ AceTimer.activeTimers = {}
+ activeTimers = AceTimer.activeTimers
+ for handle, timer in next, oldTimers do
+ local newTimer
+ -- Stop the old timer animation
+ local duration, elapsed = timer:GetDuration(), timer:GetElapsed()
+ timer:GetParent():Stop()
+ if timer.looping then
+ newTimer = AceTimer.ScheduleRepeatingTimer(timer.object, timer.func, duration, unpack(timer.args, 1, timer.argsCount))
+ else
+ newTimer = AceTimer.ScheduleTimer(timer.object, timer.func, duration - elapsed, unpack(timer.args, 1, timer.argsCount))
+ end
+ -- Use the old handle for old timers
+ activeTimers[newTimer] = nil
+ activeTimers[handle] = newTimer
+ newTimer.handle = handle
+ end
+
+ -- Migrate transitional handles
+ if oldminor < 13 and AceTimer.hashCompatTable then
+ for handle, id in next, AceTimer.hashCompatTable do
+ local t = activeTimers[id]
+ if t then
+ activeTimers[id] = nil
+ activeTimers[handle] = t
+ t.handle = handle
+ end
+ end
+ AceTimer.hashCompatTable = nil
+ end
+end
+
+-- ---------------------------------------------------------------------
+-- Embed handling
+
+AceTimer.embeds = AceTimer.embeds or {}
+
+local mixins = {
+ "ScheduleTimer", "ScheduleRepeatingTimer",
+ "CancelTimer", "CancelAllTimers",
+ "TimeLeft"
+}
+
+function AceTimer:Embed(target)
+ AceTimer.embeds[target] = true
+ for _,v in next, mixins do
+ target[v] = AceTimer[v]
+ end
+ return target
+end
+
+-- AceTimer:OnEmbedDisable(target)
+-- target (object) - target object that AceTimer is embedded in.
+--
+-- cancel all timers registered for the object
+function AceTimer:OnEmbedDisable(target)
+ target:CancelAllTimers()
+end
+
+for addon in next, AceTimer.embeds do
+ AceTimer:Embed(addon)
+end
diff --git a/libs/AceTimer-3.0/AceTimer-3.0.xml b/libs/AceTimer-3.0/AceTimer-3.0.xml
new file mode 100644
index 0000000..1987d78
--- /dev/null
+++ b/libs/AceTimer-3.0/AceTimer-3.0.xml
@@ -0,0 +1,4 @@
+
+
+