Quantcast

v7.1.0 - Major rework

Pawel [12-03-16 - 14:50]
v7.1.0 - Major rework
Filename
Libs/AceAddon-3.0/AceAddon-3.0.lua
Libs/AceAddon-3.0/AceAddon-3.0.xml
Libs/AceConsole-3.0/AceConsole-3.0.lua
Libs/AceConsole-3.0/AceConsole-3.0.xml
Libs/AceDB-3.0/AceDB-3.0.lua
Libs/AceDB-3.0/AceDB-3.0.xml
Libs/AceEvent-3.0/AceEvent-3.0.lua
Libs/AceEvent-3.0/AceEvent-3.0.xml
Libs/AceTimer-3.0/AceTimer-3.0.lua
Libs/AceTimer-3.0/AceTimer-3.0.xml
Libs/BigLibTimer/BigLibTimer.lua
MaxDps.toc
Modules/module.lua.template
Modules/module.template.lua
TDButtons.lua
TDDps.lua
TDDps.toc
TDHelper.lua
TDSettings.lua
buttons.lua
core.lua
helper.lua
diff --git a/Libs/AceAddon-3.0/AceAddon-3.0.lua b/Libs/AceAddon-3.0/AceAddon-3.0.lua
new file mode 100644
index 0000000..a7f7279
--- /dev/null
+++ b/Libs/AceAddon-3.0/AceAddon-3.0.lua
@@ -0,0 +1,674 @@
+--- **AceAddon-3.0** provides a template for creating addon objects.
+-- It'll provide you with a set of callback functions that allow you to simplify the loading
+-- process of your addon.\\
+-- Callbacks provided are:\\
+-- * **OnInitialize**, which is called directly after the addon is fully loaded.
+-- * **OnEnable** which gets called during the PLAYER_LOGIN event, when most of the data provided by the game is already present.
+-- * **OnDisable**, which is only called when your addon is manually being disabled.
+-- @usage
+-- -- A small (but complete) addon, that doesn't do anything,
+-- -- but shows usage of the callbacks.
+-- local MyAddon = LibStub("AceAddon-3.0"):NewAddon("MyAddon")
+--
+-- function MyAddon:OnInitialize()
+--   -- do init tasks here, like loading the Saved Variables,
+--   -- or setting up slash commands.
+-- end
+--
+-- function MyAddon:OnEnable()
+--   -- Do more initialization here, that really enables the use of your addon.
+--   -- Register Events, Hook functions, Create Frames, Get information from
+--   -- the game that wasn't available in OnInitialize
+-- end
+--
+-- function MyAddon:OnDisable()
+--   -- Unhook, Unregister Events, Hide frames that you created.
+--   -- You would probably only use an OnDisable if you want to
+--   -- build a "standby" mode, or be able to toggle modules on/off.
+-- end
+-- @class file
+-- @name AceAddon-3.0.lua
+-- @release $Id: AceAddon-3.0.lua 1084 2013-04-27 20:14:11Z nevcairiel $
+
+local MAJOR, MINOR = "AceAddon-3.0", 12
+local AceAddon, oldminor = LibStub:NewLibrary(MAJOR, MINOR)
+
+if not AceAddon then return end -- No Upgrade needed.
+
+AceAddon.frame = AceAddon.frame or CreateFrame("Frame", "AceAddon30Frame") -- Our very own frame
+AceAddon.addons = AceAddon.addons or {} -- addons in general
+AceAddon.statuses = AceAddon.statuses or {} -- statuses of addon.
+AceAddon.initializequeue = AceAddon.initializequeue or {} -- addons that are new and not initialized
+AceAddon.enablequeue = AceAddon.enablequeue or {} -- addons that are initialized and waiting to be enabled
+AceAddon.embeds = AceAddon.embeds or setmetatable({}, {__index = function(tbl, key) tbl[key] = {} return tbl[key] end }) -- contains a list of libraries embedded in an addon
+
+-- Lua APIs
+local tinsert, tconcat, tremove = table.insert, table.concat, table.remove
+local fmt, tostring = string.format, tostring
+local select, pairs, next, type, unpack = select, pairs, next, type, unpack
+local loadstring, assert, error = loadstring, assert, error
+local setmetatable, getmetatable, rawset, rawget = setmetatable, getmetatable, rawset, rawget
+
+-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
+-- List them here for Mikk's FindGlobals script
+-- GLOBALS: LibStub, IsLoggedIn, geterrorhandler
+
+--[[
+	 xpcall safecall implementation
+]]
+local xpcall = xpcall
+
+local function errorhandler(err)
+	return geterrorhandler()(err)
+end
+
+local function CreateDispatcher(argCount)
+	local code = [[
+		local xpcall, eh = ...
+		local method, ARGS
+		local function call() return method(ARGS) end
+
+		local function dispatch(func, ...)
+			 method = func
+			 if not method then return end
+			 ARGS = ...
+			 return xpcall(call, eh)
+		end
+
+		return dispatch
+	]]
+
+	local ARGS = {}
+	for i = 1, argCount do ARGS[i] = "arg"..i end
+	code = code:gsub("ARGS", tconcat(ARGS, ", "))
+	return assert(loadstring(code, "safecall Dispatcher["..argCount.."]"))(xpcall, errorhandler)
+end
+
+local Dispatchers = setmetatable({}, {__index=function(self, argCount)
+	local dispatcher = CreateDispatcher(argCount)
+	rawset(self, argCount, dispatcher)
+	return dispatcher
+end})
+Dispatchers[0] = function(func)
+	return xpcall(func, errorhandler)
+end
+
+local function safecall(func, ...)
+	-- we check to see if the func is passed is actually a function here and don't error when it isn't
+	-- this safecall is used for optional functions like OnInitialize OnEnable etc. When they are not
+	-- present execution should continue without hinderance
+	if type(func) == "function" then
+		return Dispatchers[select('#', ...)](func, ...)
+	end
+end
+
+-- local functions that will be implemented further down
+local Enable, Disable, EnableModule, DisableModule, Embed, NewModule, GetModule, GetName, SetDefaultModuleState, SetDefaultModuleLibraries, SetEnabledState, SetDefaultModulePrototype
+
+-- used in the addon metatable
+local function addontostring( self ) return self.name end
+
+-- Check if the addon is queued for initialization
+local function queuedForInitialization(addon)
+	for i = 1, #AceAddon.initializequeue do
+		if AceAddon.initializequeue[i] == addon then
+			return true
+		end
+	end
+	return false
+end
+
+--- Create a new AceAddon-3.0 addon.
+-- Any libraries you specified will be embeded, and the addon will be scheduled for
+-- its OnInitialize and OnEnable callbacks.
+-- The final addon object, with all libraries embeded, will be returned.
+-- @paramsig [object ,]name[, lib, ...]
+-- @param object Table to use as a base for the addon (optional)
+-- @param name Name of the addon object to create
+-- @param lib List of libraries to embed into the addon
+-- @usage
+-- -- Create a simple addon object
+-- MyAddon = LibStub("AceAddon-3.0"):NewAddon("MyAddon", "AceEvent-3.0")
+--
+-- -- Create a Addon object based on the table of a frame
+-- local MyFrame = CreateFrame("Frame")
+-- MyAddon = LibStub("AceAddon-3.0"):NewAddon(MyFrame, "MyAddon", "AceEvent-3.0")
+function AceAddon:NewAddon(objectorname, ...)
+	local object,name
+	local i=1
+	if type(objectorname)=="table" then
+		object=objectorname
+		name=...
+		i=2
+	else
+		name=objectorname
+	end
+	if type(name)~="string" then
+		error(("Usage: NewAddon([object,] name, [lib, lib, lib, ...]): 'name' - string expected got '%s'."):format(type(name)), 2)
+	end
+	if self.addons[name] then
+		error(("Usage: NewAddon([object,] name, [lib, lib, lib, ...]): 'name' - Addon '%s' already exists."):format(name), 2)
+	end
+
+	object = object or {}
+	object.name = name
+
+	local addonmeta = {}
+	local oldmeta = getmetatable(object)
+	if oldmeta then
+		for k, v in pairs(oldmeta) do addonmeta[k] = v end
+	end
+	addonmeta.__tostring = addontostring
+
+	setmetatable( object, addonmeta )
+	self.addons[name] = object
+	object.modules = {}
+	object.orderedModules = {}
+	object.defaultModuleLibraries = {}
+	Embed( object ) -- embed NewModule, GetModule methods
+	self:EmbedLibraries(object, select(i,...))
+
+	-- add to queue of addons to be initialized upon ADDON_LOADED
+	tinsert(self.initializequeue, object)
+	return object
+end
+
+
+--- Get the addon object by its name from the internal AceAddon registry.
+-- Throws an error if the addon object cannot be found (except if silent is set).
+-- @param name unique name of the addon object
+-- @param silent if true, the addon is optional, silently return nil if its not found
+-- @usage
+-- -- Get the Addon
+-- MyAddon = LibStub("AceAddon-3.0"):GetAddon("MyAddon")
+function AceAddon:GetAddon(name, silent)
+	if not silent and not self.addons[name] then
+		error(("Usage: GetAddon(name): 'name' - Cannot find an AceAddon '%s'."):format(tostring(name)), 2)
+	end
+	return self.addons[name]
+end
+
+-- - Embed a list of libraries into the specified addon.
+-- This function will try to embed all of the listed libraries into the addon
+-- and error if a single one fails.
+--
+-- **Note:** This function is for internal use by :NewAddon/:NewModule
+-- @paramsig addon, [lib, ...]
+-- @param addon addon object to embed the libs in
+-- @param lib List of libraries to embed into the addon
+function AceAddon:EmbedLibraries(addon, ...)
+	for i=1,select("#", ... ) do
+		local libname = select(i, ...)
+		self:EmbedLibrary(addon, libname, false, 4)
+	end
+end
+
+-- - Embed a library into the addon object.
+-- This function will check if the specified library is registered with LibStub
+-- and if it has a :Embed function to call. It'll error if any of those conditions
+-- fails.
+--
+-- **Note:** This function is for internal use by :EmbedLibraries
+-- @paramsig addon, libname[, silent[, offset]]
+-- @param addon addon object to embed the library in
+-- @param libname name of the library to embed
+-- @param silent marks an embed to fail silently if the library doesn't exist (optional)
+-- @param offset will push the error messages back to said offset, defaults to 2 (optional)
+function AceAddon:EmbedLibrary(addon, libname, silent, offset)
+	local lib = LibStub:GetLibrary(libname, true)
+	if not lib and not silent then
+		error(("Usage: EmbedLibrary(addon, libname, silent, offset): 'libname' - Cannot find a library instance of %q."):format(tostring(libname)), offset or 2)
+	elseif lib and type(lib.Embed) == "function" then
+		lib:Embed(addon)
+		tinsert(self.embeds[addon], libname)
+		return true
+	elseif lib then
+		error(("Usage: EmbedLibrary(addon, libname, silent, offset): 'libname' - Library '%s' is not Embed capable"):format(libname), offset or 2)
+	end
+end
+
+--- Return the specified module from an addon object.
+-- Throws an error if the addon object cannot be found (except if silent is set)
+-- @name //addon//:GetModule
+-- @paramsig name[, silent]
+-- @param name unique name of the module
+-- @param silent if true, the module is optional, silently return nil if its not found (optional)
+-- @usage
+-- -- Get the Addon
+-- MyAddon = LibStub("AceAddon-3.0"):GetAddon("MyAddon")
+-- -- Get the Module
+-- MyModule = MyAddon:GetModule("MyModule")
+function GetModule(self, name, silent)
+	if not self.modules[name] and not silent then
+		error(("Usage: GetModule(name, silent): 'name' - Cannot find module '%s'."):format(tostring(name)), 2)
+	end
+	return self.modules[name]
+end
+
+local function IsModuleTrue(self) return true end
+
+--- Create a new module for the addon.
+-- The new module can have its own embeded libraries and/or use a module prototype to be mixed into the module.\\
+-- A module has the same functionality as a real addon, it can have modules of its own, and has the same API as
+-- an addon object.
+-- @name //addon//:NewModule
+-- @paramsig name[, prototype|lib[, lib, ...]]
+-- @param name unique name of the module
+-- @param prototype object to derive this module from, methods and values from this table will be mixed into the module (optional)
+-- @param lib List of libraries to embed into the addon
+-- @usage
+-- -- Create a module with some embeded libraries
+-- MyModule = MyAddon:NewModule("MyModule", "AceEvent-3.0", "AceHook-3.0")
+--
+-- -- Create a module with a prototype
+-- local prototype = { OnEnable = function(self) print("OnEnable called!") end }
+-- MyModule = MyAddon:NewModule("MyModule", prototype, "AceEvent-3.0", "AceHook-3.0")
+function NewModule(self, name, prototype, ...)
+	if type(name) ~= "string" then error(("Usage: NewModule(name, [prototype, [lib, lib, lib, ...]): 'name' - string expected got '%s'."):format(type(name)), 2) end
+	if type(prototype) ~= "string" and type(prototype) ~= "table" and type(prototype) ~= "nil" then error(("Usage: NewModule(name, [prototype, [lib, lib, lib, ...]): 'prototype' - table (prototype), string (lib) or nil expected got '%s'."):format(type(prototype)), 2) end
+
+	if self.modules[name] then error(("Usage: NewModule(name, [prototype, [lib, lib, lib, ...]): 'name' - Module '%s' already exists."):format(name), 2) end
+
+	-- modules are basically addons. We treat them as such. They will be added to the initializequeue properly as well.
+	-- NewModule can only be called after the parent addon is present thus the modules will be initialized after their parent is.
+	local module = AceAddon:NewAddon(fmt("%s_%s", self.name or tostring(self), name))
+
+	module.IsModule = IsModuleTrue
+	module:SetEnabledState(self.defaultModuleState)
+	module.moduleName = name
+
+	if type(prototype) == "string" then
+		AceAddon:EmbedLibraries(module, prototype, ...)
+	else
+		AceAddon:EmbedLibraries(module, ...)
+	end
+	AceAddon:EmbedLibraries(module, unpack(self.defaultModuleLibraries))
+
+	if not prototype or type(prototype) == "string" then
+		prototype = self.defaultModulePrototype or nil
+	end
+
+	if type(prototype) == "table" then
+		local mt = getmetatable(module)
+		mt.__index = prototype
+		setmetatable(module, mt)  -- More of a Base class type feel.
+	end
+
+	safecall(self.OnModuleCreated, self, module) -- Was in Ace2 and I think it could be a cool thing to have handy.
+	self.modules[name] = module
+	tinsert(self.orderedModules, module)
+
+	return module
+end
+
+--- Returns the real name of the addon or module, without any prefix.
+-- @name //addon//:GetName
+-- @paramsig
+-- @usage
+-- print(MyAddon:GetName())
+-- -- prints "MyAddon"
+function GetName(self)
+	return self.moduleName or self.name
+end
+
+--- Enables the Addon, if possible, return true or false depending on success.
+-- This internally calls AceAddon:EnableAddon(), thus dispatching a OnEnable callback
+-- and enabling all modules of the addon (unless explicitly disabled).\\
+-- :Enable() also sets the internal `enableState` variable to true
+-- @name //addon//:Enable
+-- @paramsig
+-- @usage
+-- -- Enable MyModule
+-- MyAddon = LibStub("AceAddon-3.0"):GetAddon("MyAddon")
+-- MyModule = MyAddon:GetModule("MyModule")
+-- MyModule:Enable()
+function Enable(self)
+	self:SetEnabledState(true)
+
+	-- nevcairiel 2013-04-27: don't enable an addon/module if its queued for init still
+	-- it'll be enabled after the init process
+	if not queuedForInitialization(self) then
+		return AceAddon:EnableAddon(self)
+	end
+end
+
+--- Disables the Addon, if possible, return true or false depending on success.
+-- This internally calls AceAddon:DisableAddon(), thus dispatching a OnDisable callback
+-- and disabling all modules of the addon.\\
+-- :Disable() also sets the internal `enableState` variable to false
+-- @name //addon//:Disable
+-- @paramsig
+-- @usage
+-- -- Disable MyAddon
+-- MyAddon = LibStub("AceAddon-3.0"):GetAddon("MyAddon")
+-- MyAddon:Disable()
+function Disable(self)
+	self:SetEnabledState(false)
+	return AceAddon:DisableAddon(self)
+end
+
+--- Enables the Module, if possible, return true or false depending on success.
+-- Short-hand function that retrieves the module via `:GetModule` and calls `:Enable` on the module object.
+-- @name //addon//:EnableModule
+-- @paramsig name
+-- @usage
+-- -- Enable MyModule using :GetModule
+-- MyAddon = LibStub("AceAddon-3.0"):GetAddon("MyAddon")
+-- MyModule = MyAddon:GetModule("MyModule")
+-- MyModule:Enable()
+--
+-- -- Enable MyModule using the short-hand
+-- MyAddon = LibStub("AceAddon-3.0"):GetAddon("MyAddon")
+-- MyAddon:EnableModule("MyModule")
+function EnableModule(self, name)
+	local module = self:GetModule( name )
+	return module:Enable()
+end
+
+--- Disables the Module, if possible, return true or false depending on success.
+-- Short-hand function that retrieves the module via `:GetModule` and calls `:Disable` on the module object.
+-- @name //addon//:DisableModule
+-- @paramsig name
+-- @usage
+-- -- Disable MyModule using :GetModule
+-- MyAddon = LibStub("AceAddon-3.0"):GetAddon("MyAddon")
+-- MyModule = MyAddon:GetModule("MyModule")
+-- MyModule:Disable()
+--
+-- -- Disable MyModule using the short-hand
+-- MyAddon = LibStub("AceAddon-3.0"):GetAddon("MyAddon")
+-- MyAddon:DisableModule("MyModule")
+function DisableModule(self, name)
+	local module = self:GetModule( name )
+	return module:Disable()
+end
+
+--- Set the default libraries to be mixed into all modules created by this object.
+-- Note that you can only change the default module libraries before any module is created.
+-- @name //addon//:SetDefaultModuleLibraries
+-- @paramsig lib[, lib, ...]
+-- @param lib List of libraries to embed into the addon
+-- @usage
+-- -- Create the addon object
+-- MyAddon = LibStub("AceAddon-3.0"):NewAddon("MyAddon")
+-- -- Configure default libraries for modules (all modules need AceEvent-3.0)
+-- MyAddon:SetDefaultModuleLibraries("AceEvent-3.0")
+-- -- Create a module
+-- MyModule = MyAddon:NewModule("MyModule")
+function SetDefaultModuleLibraries(self, ...)
+	if next(self.modules) then
+		error("Usage: SetDefaultModuleLibraries(...): cannot change the module defaults after a module has been registered.", 2)
+	end
+	self.defaultModuleLibraries = {...}
+end
+
+--- Set the default state in which new modules are being created.
+-- Note that you can only change the default state before any module is created.
+-- @name //addon//:SetDefaultModuleState
+-- @paramsig state
+-- @param state Default state for new modules, true for enabled, false for disabled
+-- @usage
+-- -- Create the addon object
+-- MyAddon = LibStub("AceAddon-3.0"):NewAddon("MyAddon")
+-- -- Set the default state to "disabled"
+-- MyAddon:SetDefaultModuleState(false)
+-- -- Create a module and explicilty enable it
+-- MyModule = MyAddon:NewModule("MyModule")
+-- MyModule:Enable()
+function SetDefaultModuleState(self, state)
+	if next(self.modules) then
+		error("Usage: SetDefaultModuleState(state): cannot change the module defaults after a module has been registered.", 2)
+	end
+	self.defaultModuleState = state
+end
+
+--- Set the default prototype to use for new modules on creation.
+-- Note that you can only change the default prototype before any module is created.
+-- @name //addon//:SetDefaultModulePrototype
+-- @paramsig prototype
+-- @param prototype Default prototype for the new modules (table)
+-- @usage
+-- -- Define a prototype
+-- local prototype = { OnEnable = function(self) print("OnEnable called!") end }
+-- -- Set the default prototype
+-- MyAddon:SetDefaultModulePrototype(prototype)
+-- -- Create a module and explicitly Enable it
+-- MyModule = MyAddon:NewModule("MyModule")
+-- MyModule:Enable()
+-- -- should print "OnEnable called!" now
+-- @see NewModule
+function SetDefaultModulePrototype(self, prototype)
+	if next(self.modules) then
+		error("Usage: SetDefaultModulePrototype(prototype): cannot change the module defaults after a module has been registered.", 2)
+	end
+	if type(prototype) ~= "table" then
+		error(("Usage: SetDefaultModulePrototype(prototype): 'prototype' - table expected got '%s'."):format(type(prototype)), 2)
+	end
+	self.defaultModulePrototype = prototype
+end
+
+--- Set the state of an addon or module
+-- This should only be called before any enabling actually happend, e.g. in/before OnInitialize.
+-- @name //addon//:SetEnabledState
+-- @paramsig state
+-- @param state the state of an addon or module  (enabled=true, disabled=false)
+function SetEnabledState(self, state)
+	self.enabledState = state
+end
+
+
+--- Return an iterator of all modules associated to the addon.
+-- @name //addon//:IterateModules
+-- @paramsig
+-- @usage
+-- -- Enable all modules
+-- for name, module in MyAddon:IterateModules() do
+--    module:Enable()
+-- end
+local function IterateModules(self) return pairs(self.modules) end
+
+-- Returns an iterator of all embeds in the addon
+-- @name //addon//:IterateEmbeds
+-- @paramsig
+local function IterateEmbeds(self) return pairs(AceAddon.embeds[self]) end
+
+--- Query the enabledState of an addon.
+-- @name //addon//:IsEnabled
+-- @paramsig
+-- @usage
+-- if MyAddon:IsEnabled() then
+--     MyAddon:Disable()
+-- end
+local function IsEnabled(self) return self.enabledState end
+local mixins = {
+	NewModule = NewModule,
+	GetModule = GetModule,
+	Enable = Enable,
+	Disable = Disable,
+	EnableModule = EnableModule,
+	DisableModule = DisableModule,
+	IsEnabled = IsEnabled,
+	SetDefaultModuleLibraries = SetDefaultModuleLibraries,
+	SetDefaultModuleState = SetDefaultModuleState,
+	SetDefaultModulePrototype = SetDefaultModulePrototype,
+	SetEnabledState = SetEnabledState,
+	IterateModules = IterateModules,
+	IterateEmbeds = IterateEmbeds,
+	GetName = GetName,
+}
+local function IsModule(self) return false end
+local pmixins = {
+	defaultModuleState = true,
+	enabledState = true,
+	IsModule = IsModule,
+}
+-- Embed( target )
+-- target (object) - target object to embed aceaddon in
+--
+-- this is a local function specifically since it's meant to be only called internally
+function Embed(target, skipPMixins)
+	for k, v in pairs(mixins) do
+		target[k] = v
+	end
+	if not skipPMixins then
+		for k, v in pairs(pmixins) do
+			target[k] = target[k] or v
+		end
+	end
+end
+
+
+-- - Initialize the addon after creation.
+-- This function is only used internally during the ADDON_LOADED event
+-- It will call the **OnInitialize** function on the addon object (if present),
+-- and the **OnEmbedInitialize** function on all embeded libraries.
+--
+-- **Note:** Do not call this function manually, unless you're absolutely sure that you know what you are doing.
+-- @param addon addon object to intialize
+function AceAddon:InitializeAddon(addon)
+	safecall(addon.OnInitialize, addon)
+
+	local embeds = self.embeds[addon]
+	for i = 1, #embeds do
+		local lib = LibStub:GetLibrary(embeds[i], true)
+		if lib then safecall(lib.OnEmbedInitialize, lib, addon) end
+	end
+
+	-- we don't call InitializeAddon on modules specifically, this is handled
+	-- from the event handler and only done _once_
+end
+
+-- - Enable the addon after creation.
+-- Note: This function is only used internally during the PLAYER_LOGIN event, or during ADDON_LOADED,
+-- if IsLoggedIn() already returns true at that point, e.g. for LoD Addons.
+-- It will call the **OnEnable** function on the addon object (if present),
+-- and the **OnEmbedEnable** function on all embeded libraries.\\
+-- This function does not toggle the enable state of the addon itself, and will return early if the addon is disabled.
+--
+-- **Note:** Do not call this function manually, unless you're absolutely sure that you know what you are doing.
+-- Use :Enable on the addon itself instead.
+-- @param addon addon object to enable
+function AceAddon:EnableAddon(addon)
+	if type(addon) == "string" then addon = AceAddon:GetAddon(addon) end
+	if self.statuses[addon.name] or not addon.enabledState then return false end
+
+	-- set the statuses first, before calling the OnEnable. this allows for Disabling of the addon in OnEnable.
+	self.statuses[addon.name] = true
+
+	safecall(addon.OnEnable, addon)
+
+	-- make sure we're still enabled before continueing
+	if self.statuses[addon.name] then
+		local embeds = self.embeds[addon]
+		for i = 1, #embeds do
+			local lib = LibStub:GetLibrary(embeds[i], true)
+			if lib then safecall(lib.OnEmbedEnable, lib, addon) end
+		end
+
+		-- enable possible modules.
+		local modules = addon.orderedModules
+		for i = 1, #modules do
+			self:EnableAddon(modules[i])
+		end
+	end
+	return self.statuses[addon.name] -- return true if we're disabled
+end
+
+-- - Disable the addon
+-- Note: This function is only used internally.
+-- It will call the **OnDisable** function on the addon object (if present),
+-- and the **OnEmbedDisable** function on all embeded libraries.\\
+-- This function does not toggle the enable state of the addon itself, and will return early if the addon is still enabled.
+--
+-- **Note:** Do not call this function manually, unless you're absolutely sure that you know what you are doing.
+-- Use :Disable on the addon itself instead.
+-- @param addon addon object to enable
+function AceAddon:DisableAddon(addon)
+	if type(addon) == "string" then addon = AceAddon:GetAddon(addon) end
+	if not self.statuses[addon.name] then return false end
+
+	-- set statuses first before calling OnDisable, this allows for aborting the disable in OnDisable.
+	self.statuses[addon.name] = false
+
+	safecall( addon.OnDisable, addon )
+
+	-- make sure we're still disabling...
+	if not self.statuses[addon.name] then
+		local embeds = self.embeds[addon]
+		for i = 1, #embeds do
+			local lib = LibStub:GetLibrary(embeds[i], true)
+			if lib then safecall(lib.OnEmbedDisable, lib, addon) end
+		end
+		-- disable possible modules.
+		local modules = addon.orderedModules
+		for i = 1, #modules do
+			self:DisableAddon(modules[i])
+		end
+	end
+
+	return not self.statuses[addon.name] -- return true if we're disabled
+end
+
+--- Get an iterator over all registered addons.
+-- @usage
+-- -- Print a list of all installed AceAddon's
+-- for name, addon in AceAddon:IterateAddons() do
+--   print("Addon: " .. name)
+-- end
+function AceAddon:IterateAddons() return pairs(self.addons) end
+
+--- Get an iterator over the internal status registry.
+-- @usage
+-- -- Print a list of all enabled addons
+-- for name, status in AceAddon:IterateAddonStatus() do
+--   if status then
+--     print("EnabledAddon: " .. name)
+--   end
+-- end
+function AceAddon:IterateAddonStatus() return pairs(self.statuses) end
+
+-- Following Iterators are deprecated, and their addon specific versions should be used
+-- e.g. addon:IterateEmbeds() instead of :IterateEmbedsOnAddon(addon)
+function AceAddon:IterateEmbedsOnAddon(addon) return pairs(self.embeds[addon]) end
+function AceAddon:IterateModulesOfAddon(addon) return pairs(addon.modules) end
+
+-- Event Handling
+local function onEvent(this, event, arg1)
+	-- 2011-08-17 nevcairiel - ignore the load event of Blizzard_DebugTools, so a potential startup error isn't swallowed up
+	if (event == "ADDON_LOADED"  and arg1 ~= "Blizzard_DebugTools") or event == "PLAYER_LOGIN" then
+		-- if a addon loads another addon, recursion could happen here, so we need to validate the table on every iteration
+		while(#AceAddon.initializequeue > 0) do
+			local addon = tremove(AceAddon.initializequeue, 1)
+			-- this might be an issue with recursion - TODO: validate
+			if event == "ADDON_LOADED" then addon.baseName = arg1 end
+			AceAddon:InitializeAddon(addon)
+			tinsert(AceAddon.enablequeue, addon)
+		end
+
+		if IsLoggedIn() then
+			while(#AceAddon.enablequeue > 0) do
+				local addon = tremove(AceAddon.enablequeue, 1)
+				AceAddon:EnableAddon(addon)
+			end
+		end
+	end
+end
+
+AceAddon.frame:RegisterEvent("ADDON_LOADED")
+AceAddon.frame:RegisterEvent("PLAYER_LOGIN")
+AceAddon.frame:SetScript("OnEvent", onEvent)
+
+-- upgrade embeded
+for name, addon in pairs(AceAddon.addons) do
+	Embed(addon, true)
+end
+
+-- 2010-10-27 nevcairiel - add new "orderedModules" table
+if oldminor and oldminor < 10 then
+	for name, addon in pairs(AceAddon.addons) do
+		addon.orderedModules = {}
+		for module_name, module in pairs(addon.modules) do
+			tinsert(addon.orderedModules, module)
+		end
+	end
+end
diff --git a/Libs/AceAddon-3.0/AceAddon-3.0.xml b/Libs/AceAddon-3.0/AceAddon-3.0.xml
new file mode 100644
index 0000000..e6ad639
--- /dev/null
+++ b/Libs/AceAddon-3.0/AceAddon-3.0.xml
@@ -0,0 +1,4 @@
+<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/
+..\FrameXML\UI.xsd">
+	<Script file="AceAddon-3.0.lua"/>
+</Ui>
\ No newline at end of file
diff --git a/Libs/AceConsole-3.0/AceConsole-3.0.lua b/Libs/AceConsole-3.0/AceConsole-3.0.lua
new file mode 100644
index 0000000..c001123
--- /dev/null
+++ b/Libs/AceConsole-3.0/AceConsole-3.0.lua
@@ -0,0 +1,250 @@
+--- **AceConsole-3.0** provides registration facilities for slash commands.
+-- You can register slash commands to your custom functions and use the `GetArgs` function to parse them
+-- to your addons individual needs.
+--
+-- **AceConsole-3.0** can be embeded into your addon, either explicitly by calling AceConsole: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 AceConsole itself.\\
+-- It is recommended to embed AceConsole, otherwise you'll have to specify a custom `self` on all calls you
+-- make into AceConsole.
+-- @class file
+-- @name AceConsole-3.0
+-- @release $Id: AceConsole-3.0.lua 878 2009-11-02 18:51:58Z nevcairiel $
+local MAJOR,MINOR = "AceConsole-3.0", 7
+
+local AceConsole, oldminor = LibStub:NewLibrary(MAJOR, MINOR)
+
+if not AceConsole then return end -- No upgrade needed
+
+AceConsole.embeds = AceConsole.embeds or {} -- table containing objects AceConsole is embedded in.
+AceConsole.commands = AceConsole.commands or {} -- table containing commands registered
+AceConsole.weakcommands = AceConsole.weakcommands or {} -- table containing self, command => func references for weak commands that don't persist through enable/disable
+
+-- Lua APIs
+local tconcat, tostring, select = table.concat, tostring, select
+local type, pairs, error = type, pairs, error
+local format, strfind, strsub = string.format, string.find, string.sub
+local max = math.max
+
+-- WoW APIs
+local _G = _G
+
+-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
+-- List them here for Mikk's FindGlobals script
+-- GLOBALS: DEFAULT_CHAT_FRAME, SlashCmdList, hash_SlashCmdList
+
+local tmp={}
+local function Print(self,frame,...)
+	local n=0
+	if self ~= AceConsole then
+		n=n+1
+		tmp[n] = "|cff33ff99"..tostring( self ).."|r:"
+	end
+	for i=1, select("#", ...) do
+		n=n+1
+		tmp[n] = tostring(select(i, ...))
+	end
+	frame:AddMessage( tconcat(tmp," ",1,n) )
+end
+
+--- Print to DEFAULT_CHAT_FRAME or given ChatFrame (anything with an .AddMessage function)
+-- @paramsig [chatframe ,] ...
+-- @param chatframe Custom ChatFrame to print to (or any frame with an .AddMessage function)
+-- @param ... List of any values to be printed
+function AceConsole:Print(...)
+	local frame = ...
+	if type(frame) == "table" and frame.AddMessage then	-- Is first argument something with an .AddMessage member?
+		return Print(self, frame, select(2,...))
+	else
+		return Print(self, DEFAULT_CHAT_FRAME, ...)
+	end
+end
+
+
+--- Formatted (using format()) print to DEFAULT_CHAT_FRAME or given ChatFrame (anything with an .AddMessage function)
+-- @paramsig [chatframe ,] "format"[, ...]
+-- @param chatframe Custom ChatFrame to print to (or any frame with an .AddMessage function)
+-- @param format Format string - same syntax as standard Lua format()
+-- @param ... Arguments to the format string
+function AceConsole:Printf(...)
+	local frame = ...
+	if type(frame) == "table" and frame.AddMessage then	-- Is first argument something with an .AddMessage member?
+		return Print(self, frame, format(select(2,...)))
+	else
+		return Print(self, DEFAULT_CHAT_FRAME, format(...))
+	end
+end
+
+
+
+
+--- Register a simple chat command
+-- @param command Chat command to be registered WITHOUT leading "/"
+-- @param func Function to call when the slash command is being used (funcref or methodname)
+-- @param persist if false, the command will be soft disabled/enabled when aceconsole is used as a mixin (default: true)
+function AceConsole:RegisterChatCommand( command, func, persist )
+	if type(command)~="string" then error([[Usage: AceConsole:RegisterChatCommand( "command", func[, persist ]): 'command' - expected a string]], 2) end
+
+	if persist==nil then persist=true end	-- I'd rather have my addon's "/addon enable" around if the author screws up. Having some extra slash regged when it shouldnt be isn't as destructive. True is a better default. /Mikk
+
+	local name = "ACECONSOLE_"..command:upper()
+
+	if type( func ) == "string" then
+		SlashCmdList[name] = function(input, editBox)
+			self[func](self, input, editBox)
+		end
+	else
+		SlashCmdList[name] = func
+	end
+	_G["SLASH_"..name.."1"] = "/"..command:lower()
+	AceConsole.commands[command] = name
+	-- non-persisting commands are registered for enabling disabling
+	if not persist then
+		if not AceConsole.weakcommands[self] then AceConsole.weakcommands[self] = {} end
+		AceConsole.weakcommands[self][command] = func
+	end
+	return true
+end
+
+--- Unregister a chatcommand
+-- @param command Chat command to be unregistered WITHOUT leading "/"
+function AceConsole:UnregisterChatCommand( command )
+	local name = AceConsole.commands[command]
+	if name then
+		SlashCmdList[name] = nil
+		_G["SLASH_" .. name .. "1"] = nil
+		hash_SlashCmdList["/" .. command:upper()] = nil
+		AceConsole.commands[command] = nil
+	end
+end
+
+--- Get an iterator over all Chat Commands registered with AceConsole
+-- @return Iterator (pairs) over all commands
+function AceConsole:IterateChatCommands() return pairs(AceConsole.commands) end
+
+
+local function nils(n, ...)
+	if n>1 then
+		return nil, nils(n-1, ...)
+	elseif n==1 then
+		return nil, ...
+	else
+		return ...
+	end
+end
+
+
+--- Retreive one or more space-separated arguments from a string.
+-- Treats quoted strings and itemlinks as non-spaced.
+-- @param string The raw argument string
+-- @param numargs How many arguments to get (default 1)
+-- @param startpos Where in the string to start scanning (default  1)
+-- @return Returns arg1, arg2, ..., nextposition\\
+-- Missing arguments will be returned as nils. 'nextposition' is returned as 1e9 at the end of the string.
+function AceConsole:GetArgs(str, numargs, startpos)
+	numargs = numargs or 1
+	startpos = max(startpos or 1, 1)
+
+	local pos=startpos
+
+	-- find start of new arg
+	pos = strfind(str, "[^ ]", pos)
+	if not pos then	-- whoops, end of string
+		return nils(numargs, 1e9)
+	end
+
+	if numargs<1 then
+		return pos
+	end
+
+	-- quoted or space separated? find out which pattern to use
+	local delim_or_pipe
+	local ch = strsub(str, pos, pos)
+	if ch=='"' then
+		pos = pos + 1
+		delim_or_pipe='([|"])'
+	elseif ch=="'" then
+		pos = pos + 1
+		delim_or_pipe="([|'])"
+	else
+		delim_or_pipe="([| ])"
+	end
+
+	startpos = pos
+
+	while true do
+		-- find delimiter or hyperlink
+		local ch,_
+		pos,_,ch = strfind(str, delim_or_pipe, pos)
+
+		if not pos then break end
+
+		if ch=="|" then
+			-- some kind of escape
+
+			if strsub(str,pos,pos+1)=="|H" then
+				-- It's a |H....|hhyper link!|h
+				pos=strfind(str, "|h", pos+2)	-- first |h
+				if not pos then break end
+
+				pos=strfind(str, "|h", pos+2)	-- second |h
+				if not pos then break end
+			elseif strsub(str,pos, pos+1) == "|T" then
+				-- It's a |T....|t  texture
+				pos=strfind(str, "|t", pos+2)
+				if not pos then break end
+			end
+
+			pos=pos+2 -- skip past this escape (last |h if it was a hyperlink)
+
+		else
+			-- found delimiter, done with this arg
+			return strsub(str, startpos, pos-1), AceConsole:GetArgs(str, numargs-1, pos+1)
+		end
+
+	end
+
+	-- search aborted, we hit end of string. return it all as one argument. (yes, even if it's an unterminated quote or hyperlink)
+	return strsub(str, startpos), nils(numargs-1, 1e9)
+end
+
+
+--- embedding and embed handling
+
+local mixins = {
+	"Print",
+	"Printf",
+	"RegisterChatCommand",
+	"UnregisterChatCommand",
+	"GetArgs",
+}
+
+-- Embeds AceConsole into the target object making the functions from the mixins list available on target:..
+-- @param target target object to embed AceBucket in
+function AceConsole:Embed( target )
+	for k, v in pairs( mixins ) do
+		target[v] = self[v]
+	end
+	self.embeds[target] = true
+	return target
+end
+
+function AceConsole:OnEmbedEnable( target )
+	if AceConsole.weakcommands[target] then
+		for command, func in pairs( AceConsole.weakcommands[target] ) do
+			target:RegisterChatCommand( command, func, false, true ) -- nonpersisting and silent registry
+		end
+	end
+end
+
+function AceConsole:OnEmbedDisable( target )
+	if AceConsole.weakcommands[target] then
+		for command, func in pairs( AceConsole.weakcommands[target] ) do
+			target:UnregisterChatCommand( command ) -- TODO: this could potentially unregister a command from another application in case of command conflicts. Do we care?
+		end
+	end
+end
+
+for addon in pairs(AceConsole.embeds) do
+	AceConsole:Embed(addon)
+end
diff --git a/Libs/AceConsole-3.0/AceConsole-3.0.xml b/Libs/AceConsole-3.0/AceConsole-3.0.xml
new file mode 100644
index 0000000..be9f47c
--- /dev/null
+++ b/Libs/AceConsole-3.0/AceConsole-3.0.xml
@@ -0,0 +1,4 @@
+<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/
+..\FrameXML\UI.xsd">
+	<Script file="AceConsole-3.0.lua"/>
+</Ui>
\ No newline at end of file
diff --git a/Libs/AceDB-3.0/AceDB-3.0.lua b/Libs/AceDB-3.0/AceDB-3.0.lua
new file mode 100644
index 0000000..a17c6f1
--- /dev/null
+++ b/Libs/AceDB-3.0/AceDB-3.0.lua
@@ -0,0 +1,745 @@
+--- **AceDB-3.0** manages the SavedVariables of your addon.
+-- It offers profile management, smart defaults and namespaces for modules.\\
+-- Data can be saved in different data-types, depending on its intended usage.
+-- The most common data-type is the `profile` type, which allows the user to choose
+-- the active profile, and manage the profiles of all of his characters.\\
+-- The following data types are available:
+-- * **char** Character-specific data. Every character has its own database.
+-- * **realm** Realm-specific data. All of the players characters on the same realm share this database.
+-- * **class** Class-specific data. All of the players characters of the same class share this database.
+-- * **race** Race-specific data. All of the players characters of the same race share this database.
+-- * **faction** Faction-specific data. All of the players characters of the same faction share this database.
+-- * **factionrealm** Faction and realm specific data. All of the players characters on the same realm and of the same faction share this database.
+-- * **global** Global Data. All characters on the same account share this database.
+-- * **profile** Profile-specific data. All characters using the same profile share this database. The user can control which profile should be used.
+--
+-- Creating a new Database using the `:New` function will return a new DBObject. A database will inherit all functions
+-- of the DBObjectLib listed here. \\
+-- If you create a new namespaced child-database (`:RegisterNamespace`), you'll get a DBObject as well, but note
+-- that the child-databases cannot individually change their profile, and are linked to their parents profile - and because of that,
+-- the profile related APIs are not available. Only `:RegisterDefaults` and `:ResetProfile` are available on child-databases.
+--
+-- For more details on how to use AceDB-3.0, see the [[AceDB-3.0 Tutorial]].
+--
+-- You may also be interested in [[libdualspec-1-0|LibDualSpec-1.0]] to do profile switching automatically when switching specs.
+--
+-- @usage
+-- MyAddon = LibStub("AceAddon-3.0"):NewAddon("DBExample")
+--
+-- -- declare defaults to be used in the DB
+-- local defaults = {
+--   profile = {
+--     setting = true,
+--   }
+-- }
+--
+-- function MyAddon:OnInitialize()
+--   -- Assuming the .toc says ## SavedVariables: MyAddonDB
+--   self.db = LibStub("AceDB-3.0"):New("MyAddonDB", defaults, true)
+-- end
+-- @class file
+-- @name AceDB-3.0.lua
+-- @release $Id: AceDB-3.0.lua 1124 2014-10-27 21:00:07Z funkydude $
+local ACEDB_MAJOR, ACEDB_MINOR = "AceDB-3.0", 26
+local AceDB, oldminor = LibStub:NewLibrary(ACEDB_MAJOR, ACEDB_MINOR)
+
+if not AceDB then return end -- No upgrade needed
+
+-- Lua APIs
+local type, pairs, next, error = type, pairs, next, error
+local setmetatable, getmetatable, rawset, rawget = setmetatable, getmetatable, rawset, rawget
+
+-- WoW APIs
+local _G = _G
+
+-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
+-- List them here for Mikk's FindGlobals script
+-- GLOBALS: LibStub
+
+AceDB.db_registry = AceDB.db_registry or {}
+AceDB.frame = AceDB.frame or CreateFrame("Frame")
+
+local CallbackHandler
+local CallbackDummy = { Fire = function() end }
+
+local DBObjectLib = {}
+
+--[[-------------------------------------------------------------------------
+	AceDB Utility Functions
+---------------------------------------------------------------------------]]
+
+-- Simple shallow copy for copying defaults
+local function copyTable(src, dest)
+	if type(dest) ~= "table" then dest = {} end
+	if type(src) == "table" then
+		for k,v in pairs(src) do
+			if type(v) == "table" then
+				-- try to index the key first so that the metatable creates the defaults, if set, and use that table
+				v = copyTable(v, dest[k])
+			end
+			dest[k] = v
+		end
+	end
+	return dest
+end
+
+-- Called to add defaults to a section of the database
+--
+-- When a ["*"] default section is indexed with a new key, a table is returned
+-- and set in the host table.  These tables must be cleaned up by removeDefaults
+-- in order to ensure we don't write empty default tables.
+local function copyDefaults(dest, src)
+	-- this happens if some value in the SV overwrites our default value with a non-table
+	--if type(dest) ~= "table" then return end
+	for k, v in pairs(src) do
+		if k == "*" or k == "**" then
+			if type(v) == "table" then
+				-- This is a metatable used for table defaults
+				local mt = {
+					-- This handles the lookup and creation of new subtables
+					__index = function(t,k)
+							if k == nil then return nil end
+							local tbl = {}
+							copyDefaults(tbl, v)
+							rawset(t, k, tbl)
+							return tbl
+						end,
+				}
+				setmetatable(dest, mt)
+				-- handle already existing tables in the SV
+				for dk, dv in pairs(dest) do
+					if not rawget(src, dk) and type(dv) == "table" then
+						copyDefaults(dv, v)
+					end
+				end
+			else
+				-- Values are not tables, so this is just a simple return
+				local mt = {__index = function(t,k) return k~=nil and v or nil end}
+				setmetatable(dest, mt)
+			end
+		elseif type(v) == "table" then
+			if not rawget(dest, k) then rawset(dest, k, {}) end
+			if type(dest[k]) == "table" then
+				copyDefaults(dest[k], v)
+				if src['**'] then
+					copyDefaults(dest[k], src['**'])
+				end
+			end
+		else
+			if rawget(dest, k) == nil then
+				rawset(dest, k, v)
+			end
+		end
+	end
+end
+
+-- Called to remove all defaults in the default table from the database
+local function removeDefaults(db, defaults, blocker)
+	-- remove all metatables from the db, so we don't accidentally create new sub-tables through them
+	setmetatable(db, nil)
+	-- loop through the defaults and remove their content
+	for k,v in pairs(defaults) do
+		if k == "*" or k == "**" then
+			if type(v) == "table" then
+				-- Loop through all the actual k,v pairs and remove
+				for key, value in pairs(db) do
+					if type(value) == "table" then
+						-- if the key was not explicitly specified in the defaults table, just strip everything from * and ** tables
+						if defaults[key] == nil and (not blocker or blocker[key] == nil) then
+							removeDefaults(value, v)
+							-- if the table is empty afterwards, remove it
+							if next(value) == nil then
+								db[key] = nil
+							end
+						-- if it was specified, only strip ** content, but block values which were set in the key table
+						elseif k == "**" then
+							removeDefaults(value, v, defaults[key])
+						end
+					end
+				end
+			elseif k == "*" then
+				-- check for non-table default
+				for key, value in pairs(db) do
+					if defaults[key] == nil and v == value then
+						db[key] = nil
+					end
+				end
+			end
+		elseif type(v) == "table" and type(db[k]) == "table" then
+			-- if a blocker was set, dive into it, to allow multi-level defaults
+			removeDefaults(db[k], v, blocker and blocker[k])
+			if next(db[k]) == nil then
+				db[k] = nil
+			end
+		else
+			-- check if the current value matches the default, and that its not blocked by another defaults table
+			if db[k] == defaults[k] and (not blocker or blocker[k] == nil) then
+				db[k] = nil
+			end
+		end
+	end
+end
+
+-- This is called when a table section is first accessed, to set up the defaults
+local function initSection(db, section, svstore, key, defaults)
+	local sv = rawget(db, "sv")
+
+	local tableCreated
+	if not sv[svstore] then sv[svstore] = {} end
+	if not sv[svstore][key] then
+		sv[svstore][key] = {}
+		tableCreated = true
+	end
+
+	local tbl = sv[svstore][key]
+
+	if defaults then
+		copyDefaults(tbl, defaults)
+	end
+	rawset(db, section, tbl)
+
+	return tableCreated, tbl
+end
+
+-- Metatable to handle the dynamic creation of sections and copying of sections.
+local dbmt = {
+	__index = function(t, section)
+			local keys = rawget(t, "keys")
+			local key = keys[section]
+			if key then
+				local defaultTbl = rawget(t, "defaults")
+				local defaults = defaultTbl and defaultTbl[section]
+
+				if section == "profile" then
+					local new = initSection(t, section, "profiles", key, defaults)
+					if new then
+						-- Callback: OnNewProfile, database, newProfileKey
+						t.callbacks:Fire("OnNewProfile", t, key)
+					end
+				elseif section == "profiles" then
+					local sv = rawget(t, "sv")
+					if not sv.profiles then sv.profiles = {} end
+					rawset(t, "profiles", sv.profiles)
+				elseif section == "global" then
+					local sv = rawget(t, "sv")
+					if not sv.global then sv.global = {} end
+					if defaults then
+						copyDefaults(sv.global, defaults)
+					end
+					rawset(t, section, sv.global)
+				else
+					initSection(t, section, section, key, defaults)
+				end
+			end
+
+			return rawget(t, section)
+		end
+}
+
+local function validateDefaults(defaults, keyTbl, offset)
+	if not defaults then return end
+	offset = offset or 0
+	for k in pairs(defaults) do
+		if not keyTbl[k] or k == "profiles" then
+			error(("Usage: AceDBObject:RegisterDefaults(defaults): '%s' is not a valid datatype."):format(k), 3 + offset)
+		end
+	end
+end
+
+local preserve_keys = {
+	["callbacks"] = true,
+	["RegisterCallback"] = true,
+	["UnregisterCallback"] = true,
+	["UnregisterAllCallbacks"] = true,
+	["children"] = true,
+}
+
+local realmKey = GetRealmName()
+local charKey = UnitName("player") .. " - " .. realmKey
+local _, classKey = UnitClass("player")
+local _, raceKey = UnitRace("player")
+local factionKey = UnitFactionGroup("player")
+local factionrealmKey = factionKey .. " - " .. realmKey
+local localeKey = GetLocale():lower()
+
+local regionTable = { "US", "KR", "EU", "TW", "CN" }
+local regionKey = regionTable[GetCurrentRegion()]
+local factionrealmregionKey = factionrealmKey .. " - " .. regionKey
+
+-- Actual database initialization function
+local function initdb(sv, defaults, defaultProfile, olddb, parent)
+	-- Generate the database keys for each section
+
+	-- map "true" to our "Default" profile
+	if defaultProfile == true then defaultProfile = "Default" end
+
+	local profileKey
+	if not parent then
+		-- Make a container for profile keys
+		if not sv.profileKeys then sv.profileKeys = {} end
+
+		-- Try to get the profile selected from the char db
+		profileKey = sv.profileKeys[charKey] or defaultProfile or charKey
+
+		-- save the selected profile for later
+		sv.profileKeys[charKey] = profileKey
+	else
+		-- Use the profile of the parents DB
+		profileKey = parent.keys.profile or defaultProfile or charKey
+
+		-- clear the profileKeys in the DB, namespaces don't need to store them
+		sv.profileKeys = nil
+	end
+
+	-- This table contains keys that enable the dynamic creation
+	-- of each section of the table.  The 'global' and 'profiles'
+	-- have a key of true, since they are handled in a special case
+	local keyTbl= {
+		["char"] = charKey,
+		["realm"] = realmKey,
+		["class"] = classKey,
+		["race"] = raceKey,
+		["faction"] = factionKey,
+		["factionrealm"] = factionrealmKey,
+		["factionrealmregion"] = factionrealmregionKey,
+		["profile"] = profileKey,
+		["locale"] = localeKey,
+		["global"] = true,
+		["profiles"] = true,
+	}
+
+	validateDefaults(defaults, keyTbl, 1)
+
+	-- This allows us to use this function to reset an entire database
+	-- Clear out the old database
+	if olddb then
+		for k,v in pairs(olddb) do if not preserve_keys[k] then olddb[k] = nil end end
+	end
+
+	-- Give this database the metatable so it initializes dynamically
+	local db = setmetatable(olddb or {}, dbmt)
+
+	if not rawget(db, "callbacks") then
+		-- try to load CallbackHandler-1.0 if it loaded after our library
+		if not CallbackHandler then CallbackHandler = LibStub:GetLibrary("CallbackHandler-1.0", true) end
+		db.callbacks = CallbackHandler and CallbackHandler:New(db) or CallbackDummy
+	end
+
+	-- Copy methods locally into the database object, to avoid hitting
+	-- the metatable when calling methods
+
+	if not parent then
+		for name, func in pairs(DBObjectLib) do
+			db[name] = func
+		end
+	else
+		-- hack this one in
+		db.RegisterDefaults = DBObjectLib.RegisterDefaults
+		db.ResetProfile = DBObjectLib.ResetProfile
+	end
+
+	-- Set some properties in the database object
+	db.profiles = sv.profiles
+	db.keys = keyTbl
+	db.sv = sv
+	--db.sv_name = name
+	db.defaults = defaults
+	db.parent = parent
+
+	-- store the DB in the registry
+	AceDB.db_registry[db] = true
+
+	return db
+end
+
+-- handle PLAYER_LOGOUT
+-- strip all defaults from all databases
+-- and cleans up empty sections
+local function logoutHandler(frame, event)
+	if event == "PLAYER_LOGOUT" then
+		for db in pairs(AceDB.db_registry) do
+			db.callbacks:Fire("OnDatabaseShutdown", db)
+			db:RegisterDefaults(nil)
+
+			-- cleanup sections that are empty without defaults
+			local sv = rawget(db, "sv")
+			for section in pairs(db.keys) do
+				if rawget(sv, section) then
+					-- global is special, all other sections have sub-entrys
+					-- also don't delete empty profiles on main dbs, only on namespaces
+					if section ~= "global" and (section ~= "profiles" or rawget(db, "parent")) then
+						for key in pairs(sv[section]) do
+							if not next(sv[section][key]) then
+								sv[section][key] = nil
+							end
+						end
+					end
+					if not next(sv[section]) then
+						sv[section] = nil
+					end
+				end
+			end
+		end
+	end
+end
+
+AceDB.frame:RegisterEvent("PLAYER_LOGOUT")
+AceDB.frame:SetScript("OnEvent", logoutHandler)
+
+
+--[[-------------------------------------------------------------------------
+	AceDB Object Method Definitions
+---------------------------------------------------------------------------]]
+
+--- Sets the defaults table for the given database object by clearing any
+-- that are currently set, and then setting the new defaults.
+-- @param defaults A table of defaults for this database
+function DBObjectLib:RegisterDefaults(defaults)
+	if defaults and type(defaults) ~= "table" then
+		error("Usage: AceDBObject:RegisterDefaults(defaults): 'defaults' - table or nil expected.", 2)
+	end
+
+	validateDefaults(defaults, self.keys)
+
+	-- Remove any currently set defaults
+	if self.defaults then
+		for section,key in pairs(self.keys) do
+			if self.defaults[section] and rawget(self, section) then
+				removeDefaults(self[section], self.defaults[section])
+			end
+		end
+	end
+
+	-- Set the DBObject.defaults table
+	self.defaults = defaults
+
+	-- Copy in any defaults, only touching those sections already created
+	if defaults then
+		for section,key in pairs(self.keys) do
+			if defaults[section] and rawget(self, section) then
+				copyDefaults(self[section], defaults[section])
+			end
+		end
+	end
+end
+
+--- Changes the profile of the database and all of it's namespaces to the
+-- supplied named profile
+-- @param name The name of the profile to set as the current profile
+function DBObjectLib:SetProfile(name)
+	if type(name) ~= "string" then
+		error("Usage: AceDBObject:SetProfile(name): 'name' - string expected.", 2)
+	end
+
+	-- changing to the same profile, dont do anything
+	if name == self.keys.profile then return end
+
+	local oldProfile = self.profile
+	local defaults = self.defaults and self.defaults.profile
+
+	-- Callback: OnProfileShutdown, database
+	self.callbacks:Fire("OnProfileShutdown", self)
+
+	if oldProfile and defaults then
+		-- Remove the defaults from the old profile
+		removeDefaults(oldProfile, defaults)
+	end
+
+	self.profile = nil
+	self.keys["profile"] = name
+
+	-- if the storage exists, save the new profile
+	-- this won't exist on namespaces.
+	if self.sv.profileKeys then
+		self.sv.profileKeys[charKey] = name
+	end
+
+	-- populate to child namespaces
+	if self.children then
+		for _, db in pairs(self.children) do
+			DBObjectLib.SetProfile(db, name)
+		end
+	end
+
+	-- Callback: OnProfileChanged, database, newProfileKey
+	self.callbacks:Fire("OnProfileChanged", self, name)
+end
+
+--- Returns a table with the names of the existing profiles in the database.
+-- You can optionally supply a table to re-use for this purpose.
+-- @param tbl A table to store the profile names in (optional)
+function DBObjectLib:GetProfiles(tbl)
+	if tbl and type(tbl) ~= "table" then
+		error("Usage: AceDBObject:GetProfiles(tbl): 'tbl' - table or nil expected.", 2)
+	end
+
+	-- Clear the container table
+	if tbl then
+		for k,v in pairs(tbl) do tbl[k] = nil end
+	else
+		tbl = {}
+	end
+
+	local curProfile = self.keys.profile
+
+	local i = 0
+	for profileKey in pairs(self.profiles) do
+		i = i + 1
+		tbl[i] = profileKey
+		if curProfile and profileKey == curProfile then curProfile = nil end
+	end
+
+	-- Add the current profile, if it hasn't been created yet
+	if curProfile then
+		i = i + 1
+		tbl[i] = curProfile
+	end
+
+	return tbl, i
+end
+
+--- Returns the current profile name used by the database
+function DBObjectLib:GetCurrentProfile()
+	return self.keys.profile
+end
+
+--- Deletes a named profile.  This profile must not be the active profile.
+-- @param name The name of the profile to be deleted
+-- @param silent If true, do not raise an error when the profile does not exist
+function DBObjectLib:DeleteProfile(name, silent)
+	if type(name) ~= "string" then
+		error("Usage: AceDBObject:DeleteProfile(name): 'name' - string expected.", 2)
+	end
+
+	if self.keys.profile == name then
+		error("Cannot delete the active profile in an AceDBObject.", 2)
+	end
+
+	if not rawget(self.profiles, name) and not silent then
+		error("Cannot delete profile '" .. name .. "'. It does not exist.", 2)
+	end
+
+	self.profiles[name] = nil
+
+	-- populate to child namespaces
+	if self.children then
+		for _, db in pairs(self.children) do
+			DBObjectLib.DeleteProfile(db, name, true)
+		end
+	end
+
+	-- switch all characters that use this profile back to the default
+	if self.sv.profileKeys then
+		for key, profile in pairs(self.sv.profileKeys) do
+			if profile == name then
+				self.sv.profileKeys[key] = nil
+			end
+		end
+	end
+
+	-- Callback: OnProfileDeleted, database, profileKey
+	self.callbacks:Fire("OnProfileDeleted", self, name)
+end
+
+--- Copies a named profile into the current profile, overwriting any conflicting
+-- settings.
+-- @param name The name of the profile to be copied into the current profile
+-- @param silent If true, do not raise an error when the profile does not exist
+function DBObjectLib:CopyProfile(name, silent)
+	if type(name) ~= "string" then
+		error("Usage: AceDBObject:CopyProfile(name): 'name' - string expected.", 2)
+	end
+
+	if name == self.keys.profile then
+		error("Cannot have the same source and destination profiles.", 2)
+	end
+
+	if not rawget(self.profiles, name) and not silent then
+		error("Cannot copy profile '" .. name .. "'. It does not exist.", 2)
+	end
+
+	-- Reset the profile before copying
+	DBObjectLib.ResetProfile(self, nil, true)
+
+	local profile = self.profile
+	local source = self.profiles[name]
+
+	copyTable(source, profile)
+
+	-- populate to child namespaces
+	if self.children then
+		for _, db in pairs(self.children) do
+			DBObjectLib.CopyProfile(db, name, true)
+		end
+	end
+
+	-- Callback: OnProfileCopied, database, sourceProfileKey
+	self.callbacks:Fire("OnProfileCopied", self, name)
+end
+
+--- Resets the current profile to the default values (if specified).
+-- @param noChildren if set to true, the reset will not be populated to the child namespaces of this DB object
+-- @param noCallbacks if set to true, won't fire the OnProfileReset callback
+function DBObjectLib:ResetProfile(noChildren, noCallbacks)
+	local profile = self.profile
+
+	for k,v in pairs(profile) do
+		profile[k] = nil
+	end
+
+	local defaults = self.defaults and self.defaults.profile
+	if defaults then
+		copyDefaults(profile, defaults)
+	end
+
+	-- populate to child namespaces
+	if self.children and not noChildren then
+		for _, db in pairs(self.children) do
+			DBObjectLib.ResetProfile(db, nil, noCallbacks)
+		end
+	end
+
+	-- Callback: OnProfileReset, database
+	if not noCallbacks then
+		self.callbacks:Fire("OnProfileReset", self)
+	end
+end
+
+--- Resets the entire database, using the string defaultProfile as the new default
+-- profile.
+-- @param defaultProfile The profile name to use as the default
+function DBObjectLib:ResetDB(defaultProfile)
+	if defaultProfile and type(defaultProfile) ~= "string" then
+		error("Usage: AceDBObject:ResetDB(defaultProfile): 'defaultProfile' - string or nil expected.", 2)
+	end
+
+	local sv = self.sv
+	for k,v in pairs(sv) do
+		sv[k] = nil
+	end
+
+	local parent = self.parent
+
+	initdb(sv, self.defaults, defaultProfile, self)
+
+	-- fix the child namespaces
+	if self.children then
+		if not sv.namespaces then sv.namespaces = {} end
+		for name, db in pairs(self.children) do
+			if not sv.namespaces[name] then sv.namespaces[name] = {} end
+			initdb(sv.namespaces[name], db.defaults, self.keys.profile, db, self)
+		end
+	end
+
+	-- Callback: OnDatabaseReset, database
+	self.callbacks:Fire("OnDatabaseReset", self)
+	-- Callback: OnProfileChanged, database, profileKey
+	self.callbacks:Fire("OnProfileChanged", self, self.keys["profile"])
+
+	return self
+end
+
+--- Creates a new database namespace, directly tied to the database.  This
+-- is a full scale database in it's own rights other than the fact that
+-- it cannot control its profile individually
+-- @param name The name of the new namespace
+-- @param defaults A table of values to use as defaults
+function DBObjectLib:RegisterNamespace(name, defaults)
+	if type(name) ~= "string" then
+		error("Usage: AceDBObject:RegisterNamespace(name, defaults): 'name' - string expected.", 2)
+	end
+	if defaults and type(defaults) ~= "table" then
+		error("Usage: AceDBObject:RegisterNamespace(name, defaults): 'defaults' - table or nil expected.", 2)
+	end
+	if self.children and self.children[name] then
+		error ("Usage: AceDBObject:RegisterNamespace(name, defaults): 'name' - a namespace with that name already exists.", 2)
+	end
+
+	local sv = self.sv
+	if not sv.namespaces then sv.namespaces = {} end
+	if not sv.namespaces[name] then
+		sv.namespaces[name] = {}
+	end
+
+	local newDB = initdb(sv.namespaces[name], defaults, self.keys.profile, nil, self)
+
+	if not self.children then self.children = {} end
+	self.children[name] = newDB
+	return newDB
+end
+
+--- Returns an already existing namespace from the database object.
+-- @param name The name of the new namespace
+-- @param silent if true, the addon is optional, silently return nil if its not found
+-- @usage
+-- local namespace = self.db:GetNamespace('namespace')
+-- @return the namespace object if found
+function DBObjectLib:GetNamespace(name, silent)
+	if type(name) ~= "string" then
+		error("Usage: AceDBObject:GetNamespace(name): 'name' - string expected.", 2)
+	end
+	if not silent and not (self.children and self.children[name]) then
+		error ("Usage: AceDBObject:GetNamespace(name): 'name' - namespace does not exist.", 2)
+	end
+	if not self.children then self.children = {} end
+	return self.children[name]
+end
+
+--[[-------------------------------------------------------------------------
+	AceDB Exposed Methods
+---------------------------------------------------------------------------]]
+
+--- Creates a new database object that can be used to handle database settings and profiles.
+-- By default, an empty DB is created, using a character specific profile.
+--
+-- You can override the default profile used by passing any profile name as the third argument,
+-- or by passing //true// as the third argument to use a globally shared profile called "Default".
+--
+-- Note that there is no token replacement in the default profile name, passing a defaultProfile as "char"
+-- will use a profile named "char", and not a character-specific profile.
+-- @param tbl The name of variable, or table to use for the database
+-- @param defaults A table of database defaults
+-- @param defaultProfile The name of the default profile. If not set, a character specific profile will be used as the default.
+-- You can also pass //true// to use a shared global profile called "Default".
+-- @usage
+-- -- Create an empty DB using a character-specific default profile.
+-- self.db = LibStub("AceDB-3.0"):New("MyAddonDB")
+-- @usage
+-- -- Create a DB using defaults and using a shared default profile
+-- self.db = LibStub("AceDB-3.0"):New("MyAddonDB", defaults, true)
+function AceDB:New(tbl, defaults, defaultProfile)
+	if type(tbl) == "string" then
+		local name = tbl
+		tbl = _G[name]
+		if not tbl then
+			tbl = {}
+			_G[name] = tbl
+		end
+	end
+
+	if type(tbl) ~= "table" then
+		error("Usage: AceDB:New(tbl, defaults, defaultProfile): 'tbl' - table expected.", 2)
+	end
+
+	if defaults and type(defaults) ~= "table" then
+		error("Usage: AceDB:New(tbl, defaults, defaultProfile): 'defaults' - table expected.", 2)
+	end
+
+	if defaultProfile and type(defaultProfile) ~= "string" and defaultProfile ~= true then
+		error("Usage: AceDB:New(tbl, defaults, defaultProfile): 'defaultProfile' - string or true expected.", 2)
+	end
+
+	return initdb(tbl, defaults, defaultProfile)
+end
+
+-- upgrade existing databases
+for db in pairs(AceDB.db_registry) do
+	if not db.parent then
+		for name,func in pairs(DBObjectLib) do
+			db[name] = func
+		end
+	else
+		db.RegisterDefaults = DBObjectLib.RegisterDefaults
+		db.ResetProfile = DBObjectLib.ResetProfile
+	end
+end
diff --git a/Libs/AceDB-3.0/AceDB-3.0.xml b/Libs/AceDB-3.0/AceDB-3.0.xml
new file mode 100644
index 0000000..46b20ba
--- /dev/null
+++ b/Libs/AceDB-3.0/AceDB-3.0.xml
@@ -0,0 +1,4 @@
+<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/
+..\FrameXML\UI.xsd">
+	<Script file="AceDB-3.0.lua"/>
+</Ui>
\ No newline at end of file
diff --git a/Libs/AceEvent-3.0/AceEvent-3.0.lua b/Libs/AceEvent-3.0/AceEvent-3.0.lua
new file mode 100644
index 0000000..578ae25
--- /dev/null
+++ b/Libs/AceEvent-3.0/AceEvent-3.0.lua
@@ -0,0 +1,126 @@
+--- AceEvent-3.0 provides event registration and secure dispatching.
+-- All dispatching is done using **CallbackHandler-1.0**. AceEvent is a simple wrapper around
+-- CallbackHandler, and dispatches all game events or addon message to the registrees.
+--
+-- **AceEvent-3.0** can be embeded into your addon, either explicitly by calling AceEvent: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 AceEvent itself.\\
+-- It is recommended to embed AceEvent, otherwise you'll have to specify a custom `self` on all calls you
+-- make into AceEvent.
+-- @class file
+-- @name AceEvent-3.0
+-- @release $Id: AceEvent-3.0.lua 975 2010-10-23 11:26:18Z nevcairiel $
+local MAJOR, MINOR = "AceEvent-3.0", 3
+local AceEvent = LibStub:NewLibrary(MAJOR, MINOR)
+
+if not AceEvent then return end
+
+-- Lua APIs
+local pairs = pairs
+
+local CallbackHandler = LibStub:GetLibrary("CallbackHandler-1.0")
+
+AceEvent.frame = AceEvent.frame or CreateFrame("Frame", "AceEvent30Frame") -- our event frame
+AceEvent.embeds = AceEvent.embeds or {} -- what objects embed this lib
+
+-- APIs and registry for blizzard events, using CallbackHandler lib
+if not AceEvent.events then
+	AceEvent.events = CallbackHandler:New(AceEvent,
+		"RegisterEvent", "UnregisterEvent", "UnregisterAllEvents")
+end
+
+function AceEvent.events:OnUsed(target, eventname)
+	AceEvent.frame:RegisterEvent(eventname)
+end
+
+function AceEvent.events:OnUnused(target, eventname)
+	AceEvent.frame:UnregisterEvent(eventname)
+end
+
+
+-- APIs and registry for IPC messages, using CallbackHandler lib
+if not AceEvent.messages then
+	AceEvent.messages = CallbackHandler:New(AceEvent,
+		"RegisterMessage", "UnregisterMessage", "UnregisterAllMessages"
+	)
+	AceEvent.SendMessage = AceEvent.messages.Fire
+end
+
+--- embedding and embed handling
+local mixins = {
+	"RegisterEvent", "UnregisterEvent",
+	"RegisterMessage", "UnregisterMessage",
+	"SendMessage",
+	"UnregisterAllEvents", "UnregisterAllMessages",
+}
+
+--- Register for a Blizzard Event.
+-- The callback will be called with the optional `arg` as the first argument (if supplied), and the event name as the second (or first, if no arg was supplied)
+-- Any arguments to the event will be passed on after that.
+-- @name AceEvent:RegisterEvent
+-- @class function
+-- @paramsig event[, callback [, arg]]
+-- @param event The event to register for
+-- @param callback The callback function to call when the event is triggered (funcref or method, defaults to a method with the event name)
+-- @param arg An optional argument to pass to the callback function
+
+--- Unregister an event.
+-- @name AceEvent:UnregisterEvent
+-- @class function
+-- @paramsig event
+-- @param event The event to unregister
+
+--- Register for a custom AceEvent-internal message.
+-- The callback will be called with the optional `arg` as the first argument (if supplied), and the event name as the second (or first, if no arg was supplied)
+-- Any arguments to the event will be passed on after that.
+-- @name AceEvent:RegisterMessage
+-- @class function
+-- @paramsig message[, callback [, arg]]
+-- @param message The message to register for
+-- @param callback The callback function to call when the message is triggered (funcref or method, defaults to a method with the event name)
+-- @param arg An optional argument to pass to the callback function
+
+--- Unregister a message
+-- @name AceEvent:UnregisterMessage
+-- @class function
+-- @paramsig message
+-- @param message The message to unregister
+
+--- Send a message over the AceEvent-3.0 internal message system to other addons registered for this message.
+-- @name AceEvent:SendMessage
+-- @class function
+-- @paramsig message, ...
+-- @param message The message to send
+-- @param ... Any arguments to the message
+
+
+-- Embeds AceEvent into the target object making the functions from the mixins list available on target:..
+-- @param target target object to embed AceEvent in
+function AceEvent:Embed(target)
+	for k, v in pairs(mixins) do
+		target[v] = self[v]
+	end
+	self.embeds[target] = true
+	return target
+end
+
+-- AceEvent:OnEmbedDisable( target )
+-- target (object) - target object that is being disabled
+--
+-- Unregister all events messages etc when the target disables.
+-- this method should be called by the target manually or by an addon framework
+function AceEvent:OnEmbedDisable(target)
+	target:UnregisterAllEvents()
+	target:UnregisterAllMessages()
+end
+
+-- Script to fire blizzard events into the event listeners
+local events = AceEvent.events
+AceEvent.frame:SetScript("OnEvent", function(this, event, ...)
+	events:Fire(event, ...)
+end)
+
+--- Finally: upgrade our old embeds
+for target, v in pairs(AceEvent.embeds) do
+	AceEvent:Embed(target)
+end
diff --git a/Libs/AceEvent-3.0/AceEvent-3.0.xml b/Libs/AceEvent-3.0/AceEvent-3.0.xml
new file mode 100644
index 0000000..313ef4d
--- /dev/null
+++ b/Libs/AceEvent-3.0/AceEvent-3.0.xml
@@ -0,0 +1,4 @@
+<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/
+..\FrameXML\UI.xsd">
+	<Script file="AceEvent-3.0.lua"/>
+</Ui>
\ No newline at end of file
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..8ba6b3c
--- /dev/null
+++ b/Libs/AceTimer-3.0/AceTimer-3.0.lua
@@ -0,0 +1,276 @@
+--- **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 1119 2014-10-14 17:23:29Z 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 = {...}
+	timer.object = self
+	timer.func = func
+	timer.looping = loop
+	timer.argsCount = select("#", ...)
+	timer.delay = delay
+	timer.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 pairs(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 pairs(AceTimer.selfs) do
+		for handle,timer in pairs(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 pairs(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 pairs(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 pairs(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 pairs(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..38e9021
--- /dev/null
+++ b/Libs/AceTimer-3.0/AceTimer-3.0.xml
@@ -0,0 +1,4 @@
+<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/
+..\FrameXML\UI.xsd">
+	<Script file="AceTimer-3.0.lua"/>
+</Ui>
\ No newline at end of file
diff --git a/Libs/BigLibTimer/BigLibTimer.lua b/Libs/BigLibTimer/BigLibTimer.lua
deleted file mode 100644
index 3727a09..0000000
--- a/Libs/BigLibTimer/BigLibTimer.lua
+++ /dev/null
@@ -1,253 +0,0 @@
-
-local MajorVersion = "BigLibTimer6"
-local BigLibTimer = LibStub:NewLibrary(MajorVersion, tonumber("20150826224730") or tonumber(date("%Y%m%d%H%M%S")))
-if not BigLibTimer then return end
-
-BigLibTimer.API = BigLibTimer.API or {}
-
-function BigLibTimer:Register(handler)
-	if type(handler) ~= "table" then
-		handler = {}
-	elseif handler[MajorVersion] then
-		return
-	end
-	handler[MajorVersion] = {}
-	handler[MajorVersion].RECYCLE_TABLES = setmetatable({}, {__mode = "k"})
-	handler[MajorVersion].TimerFrame = CreateFrame("Frame")
-	handler[MajorVersion].TimerFrame:Hide()
-	handler[MajorVersion].TIMER = {}
-	handler[MajorVersion].OnUpdate = function() BigLibTimer.OnUpdate(handler) end
-	handler[MajorVersion].TimerFrame:SetScript("OnUpdate", handler[MajorVersion].OnUpdate)
-	for key in pairs(BigLibTimer.API) do
-		handler[key] = function(...) return BigLibTimer.API[key](...) end
-	end
-	return handler
-end
-
-function BigLibTimer.OnUpdate(handler)
-	local TIMER = handler[MajorVersion].TIMER
-	if next(TIMER) then
-		if not handler[MajorVersion].Running then
-			handler[MajorVersion].Running = 1
-			for Name in pairs(TIMER) do
-				if TIMER and TIMER[Name] and not TIMER[Name].Running and TIMER[Name].Seconds <= GetTime() then
-					if TIMER[Name].Function then
-						TIMER[Name].Function(unpack(TIMER[Name].Args))
-						if TIMER and TIMER[Name] and TIMER[Name].Seconds <= GetTime() then
-							if TIMER[Name].RepeatSeconds > 0 then
-								TIMER[Name].Seconds = GetTime() + TIMER[Name].RepeatSeconds
-							else
-								TIMER[Name].Args = handler:RecycleTable(TIMER[Name].Args)
-								TIMER[Name] = handler:RecycleTable(TIMER[Name])
-							end
-						end
-					elseif TIMER[Name].RepeatSeconds > 0 then
-						TIMER[Name].Seconds = GetTime() + TIMER[Name].RepeatSeconds
-					else
-						TIMER[Name].Args = handler:RecycleTable(TIMER[Name].Args)
-						TIMER[Name] = handler:RecycleTable(TIMER[Name])
-					end
-				end
-			end
-			if not next(TIMER) then
-				handler[MajorVersion].TimerFrame:Hide()
-			end
-			handler[MajorVersion].Running = nil
-		end
-	elseif not handler[MajorVersion].Running then
-		handler[MajorVersion].TimerFrame:Hide()
-	end
-end
-
-function BigLibTimer.API:SetTimer(Name, Seconds, RepeatSeconds, Function, ...)
-	local TIMER = self[MajorVersion].TIMER
-	if type(Name) == "string" and TIMER then
-		if TIMER[Name] then
-			TIMER[Name].Args = self:RecycleTable(TIMER[Name].Args)
-		end
-		TIMER[Name] = self:CreateTable(TIMER[Name])
-		TIMER[Name].Running = 1
-		if type(Seconds) == "number" and Seconds > 0 then
-			TIMER[Name].Seconds = GetTime() + Seconds
-		else
-			TIMER[Name].Seconds = 0
-		end
-		if type(RepeatSeconds) == "number" and RepeatSeconds > 0 then
-			TIMER[Name].RepeatSeconds = RepeatSeconds
-		else
-			TIMER[Name].RepeatSeconds = 0
-		end
-		if type(Function) == "function" then
-			TIMER[Name].Function = Function
-			TIMER[Name].Args = self:CreateTable(TIMER[Name].Args)
-			local n = select("#", ...)
-			if n > 0 then
-				for i = 1, n do
-					TIMER[Name].Args[i] = select(i, ...)
-				end
-			end
-		end
-		if TIMER[Name].Seconds == 0 and TIMER[Name].Function then
-			Function(...)
-			if TIMER and TIMER[Name] and TIMER[Name].Seconds <= GetTime() then
-				if TIMER[Name].RepeatSeconds > 0 then
-					TIMER[Name].Seconds = GetTime() + TIMER[Name].RepeatSeconds
-				else
-					TIMER[Name].Args = self:RecycleTable(TIMER[Name].Args)
-					TIMER[Name] = self:RecycleTable(TIMER[Name])
-				end
-			end
-		end
-		if TIMER and TIMER[Name] then
-			TIMER[Name].Running = nil
-			self[MajorVersion].TimerFrame:Show()
-		end
-	end
-end
-
-function BigLibTimer.API:ReplaceTimer(Name, Seconds, RepeatSeconds, Function, ...)
-	local TIMER = self[MajorVersion].TIMER
-	if type(Name) == "string" and TIMER[Name] then
-		if type(Seconds) == "number" and Seconds > 0 then
-			TIMER[Name].Seconds = GetTime() + Seconds
-		elseif Seconds ~= nil then
-			TIMER[Name].Seconds = 0
-		end
-		if type(RepeatSeconds) == "number" and RepeatSeconds > 0 then
-			TIMER[Name].RepeatSeconds = RepeatSeconds
-		elseif RepeatSeconds ~= nil then
-			TIMER[Name].RepeatSeconds = 0
-		end
-		if type(Function) == "function" then
-			TIMER[Name].Function = Function
-			TIMER[Name].Args = self:CreateTable(TIMER[Name].Args)
-			local n = select("#", ...)
-			if n > 0 then
-				for i = 1, n do
-					TIMER[Name].Args[i] = select(i, ...)
-				end
-			end
-		elseif Function ~= nil then
-			TIMER[Name].Function = nil
-		end
-		return true
-	end
-	return false
-end
-
-function BigLibTimer.API:ClearTimer(Name, Search)
-	local TIMER = self[MajorVersion].TIMER
-	local found = nil
-	if type(Name) == "string" then
-		if Search then
-			for key in pairs(TIMER) do
-				if key:match(Name) and ( TIMER[key].RepeatSeconds > 0 or TIMER[key].Seconds - GetTime() > 0 ) then
-					TIMER[key].Args = self:RecycleTable(TIMER[key].Args)
-					TIMER[key] = self:RecycleTable(TIMER[key])
-					found = true
-				end
-			end
-		elseif TIMER[Name] and ( TIMER[Name].RepeatSeconds > 0 or TIMER[Name].Seconds - GetTime() > 0 ) then
-			TIMER[Name].Args = self:RecycleTable(TIMER[Name].Args)
-			TIMER[Name] = self:RecycleTable(TIMER[Name])
-			return true
-		end
-	end
-	return found
-end
-
-function BigLibTimer.API:ClearAllTimers()
-	wipe(self[MajorVersion].TIMER)
-end
-
-function BigLibTimer.API:IsTimer(Name, Search)
-	local TIMER = self[MajorVersion].TIMER
-	if type(Name) == "string" then
-		if Search then
-			for key in pairs(TIMER) do
-				if key:match(Name) and ( TIMER[key].RepeatSeconds > 0 or TIMER[key].Seconds - GetTime() > 0 ) then
-					return true
-				end
-			end
-		elseif TIMER[Name] and ( TIMER[Name].RepeatSeconds > 0 or TIMER[Name].Seconds - GetTime() > 0 ) then
-			return true
-		end
-	end
-	return false
-end
-
-function BigLibTimer.API:IsRepeatTimer(Name, Search)
-	local TIMER = self[MajorVersion].TIMER
-	if type(Name) == "string" then
-		if Search then
-			for key in pairs(TIMER) do
-				if key:match(Name) and TIMER[key].RepeatSeconds > 0 then
-					return true
-				end
-			end
-		elseif TIMER[Name] and TIMER[Name].RepeatSeconds > 0 then
-			return true
-		end
-	end
-	return false
-end
-
-function BigLibTimer.API:GetTimer(Name)
-	local TIMER = self[MajorVersion].TIMER
-	if type(Name) == "string" and TIMER[Name] then
-		local TimeRemaining = TIMER[Name].Seconds - GetTime()
-		if TimeRemaining > 0 then
-			return TimeRemaining
-		end
-	end
-	return 0
-end
-
-function BigLibTimer.API:CreateTable(Table, All)
-	if type(Table) == "table" and type(Table[0]) ~= "userdata" then
-		if All then
-			self:RecycleTable(Table, All)
-		else
-			wipe(Table)
-			return Table
-		end
-	end
-	local t = next(self[MajorVersion].RECYCLE_TABLES)
-	if t then
-		self[MajorVersion].RECYCLE_TABLES[t] = nil
-		if next(t) then
-			return self:CreateTable()
-		end
-		return t
-	end
-	return {}
-end
-
-function BigLibTimer.RecycleAllTables(self, Table, CompareList)
-	if not CompareList[Table] then
-		CompareList[Table] = 1
-		for k, v in pairs(Table) do
-			if type(v) == "table" and type(v[0]) ~= "userdata" then
-				BigLibTimer.RecycleAllTables(self, v, CompareList)
-			end
-			if type(k) == "table" and type(k[0]) ~= "userdata" then
-				BigLibTimer.RecycleAllTables(self, k, CompareList)
-			end
-		end
-		self:RecycleTable(Table)
-	end
-end
-
-function BigLibTimer.API:RecycleTable(Table, All)
-	if type(Table) == "table" and type(Table[0]) ~= "userdata" then
-		if All then
-			local CompareList = self:CreateTable()
-			BigLibTimer.RecycleAllTables(self, Table, CompareList)
-			self:RecycleTable(CompareList)
-		else
-			wipe(Table)
-			self[MajorVersion].RECYCLE_TABLES[Table] = 1
-		end
-	end
-	return nil
-end
diff --git a/MaxDps.toc b/MaxDps.toc
new file mode 100644
index 0000000..b832c94
--- /dev/null
+++ b/MaxDps.toc
@@ -0,0 +1,22 @@
+## Title: MaxDps
+## Notes: Rotation helper framework.
+## Version: 7.1.0
+## Author: Kaminaris
+## Interface: 70100
+## SavedVariables: MaxDpsOptions
+## OptionalDependencies: Bartender4, ElvUI, ButtonForge, SVUI_ActionBars
+
+Libs\LibStub\LibStub.lua
+Libs\CallbackHandler-1.0\CallbackHandler-1.0.xml
+Libs\LibSharedMedia-3.0\lib.xml
+Libs\AceAddon-3.0\AceAddon-3.0.xml
+Libs\AceTimer-3.0\AceTimer-3.0.xml
+Libs\AceEvent-3.0\AceEvent-3.0.xml
+Libs\AceDB-3.0\AceDB-3.0.xml
+Libs\AceGUI-3.0\AceGUI-3.0.xml
+Libs\AceGUI-3.0-SharedMediaWidgets\widget.xml
+Libs\AceConfig-3.0\AceConfig-3.0.xml
+
+core.lua
+buttons.lua
+helper.lua
\ No newline at end of file
diff --git a/Modules/module.lua.template b/Modules/module.lua.template
deleted file mode 100644
index 60ebc96..0000000
--- a/Modules/module.lua.template
+++ /dev/null
@@ -1,65 +0,0 @@
--- Author      : Kaminari
--- Create Date : 13:03 2015-04-20
-
--- Spells
-local _Spell = 23881;
-
--- Auras
-local _Aura = 12880;
-
--- Talents
-local _isTalent = false;
-
-----------------------------------------------
--- Pre enable, checking talents
-----------------------------------------------
-TDDps_[Class]_CheckTalents = function()
-	_isTalent = TD_TalentEnabled('Talent Name');
-	-- other checking functions
-end
-
-----------------------------------------------
--- Enabling Addon
-----------------------------------------------
-function TDDps_[Class]_EnableAddon(mode)
-	mode = mode or 1;
-	TDDps.Description = "TD [Class] DPS supports: [Spec1], [Spec2], [Spec3]";
-	TDDps.OnEnable = TDDps_[Class]_CheckTalents;
-	if mode == 1 then
-		TDDps.NextSpell = TDDps_[Class]_[Spec1];
-	end;
-	if mode == 2 then
-		TDDps.NextSpell = TDDps_[Class]_[Spec2];
-	end;
-	if mode == 3 then
-		TDDps.NextSpell = TDDps_[Class]_[Spec3];
-	end;
-	TDDps:EnableAddon();
-end
-
-----------------------------------------------
--- Main rotation: [Spec1]
-----------------------------------------------
-TDDps_[Class]_[Spec1] = function()
-	local timeShift, currentSpell = TD_EndCast();
-
-	return _Spell;
-end
-
-----------------------------------------------
--- Main rotation: [Spec2]
-----------------------------------------------
-TDDps_[Class]_[Spec2] = function()
-	local timeShift, currentSpell = TD_EndCast();
-
-	return _Spell;
-end
-
-----------------------------------------------
--- Main rotation: [Spec3]
-----------------------------------------------
-TDDps_[Class]_[Spec3] = function()
-	local timeShift, currentSpell = TD_EndCast();
-
-	return _Spell;
-end
\ No newline at end of file
diff --git a/Modules/module.template.lua b/Modules/module.template.lua
new file mode 100644
index 0000000..b41c801
--- /dev/null
+++ b/Modules/module.template.lua
@@ -0,0 +1,46 @@
+-- Replace all occurances of Class with Class Name ex. Warrior
+-- Replace Spec1 - Spec3 with specialization names ex. Protection
+-- Spells
+local _Spell = 23881;
+
+-- Talents
+local _isTalent = false;
+MaxDps.Class = {};
+
+MaxDps.Class.CheckTalents = function()
+	_isTalent = MaxDps:TalentEnabled('Talent Name');
+	-- other checking functions
+end
+
+function MaxDps:EnableRotationModule(mode)
+	mode = mode or 1;
+	MaxDps.Description = "Class Module [Spec1, Spec2, Spec3]";
+	MaxDps.ModuleOnEnable = MaxDps.Class.CheckTalents;
+	if mode == 1 then
+		MaxDps.NextSpell = MaxDps.Class.Spec1;
+	end;
+	if mode == 2 then
+		MaxDps.NextSpell = MaxDps.Class.Spec2;
+	end;
+	if mode == 3 then
+		MaxDps.NextSpell = MaxDps.Class.Spec3;
+	end;
+end
+
+function MaxDps.Class.Spec1()
+	local timeShift, currentSpell, gcd = MaxDps:EndCast();
+
+	return _Spell;
+end
+
+function MaxDps.Class.Spec2()
+	local timeShift, currentSpell, gcd = MaxDps:EndCast();
+
+	return _Spell;
+end
+
+function MaxDps.Class.Spec3()
+	local timeShift, currentSpell, gcd = MaxDps:EndCast();
+
+	return _Spell;
+end
\ No newline at end of file
diff --git a/TDButtons.lua b/TDButtons.lua
deleted file mode 100644
index 24f9aa5..0000000
--- a/TDButtons.lua
+++ /dev/null
@@ -1,415 +0,0 @@
-TDButton = {};
-TDButton.Spells = {};
-TDButton.Flags = {};
-TDButton.SpellsGlowing = {};
-TDButton.FramePool = {};
-TDButton.Frames = {};
-
-function TDButton.CreateOverlay(parent, id, texture, r, g, b)
-	local frame = tremove(TDButton.FramePool);
-	if not frame then
-		frame = CreateFrame('Frame', 'TDButton_Overlay_' .. id, parent);
-	else
---		frame:SetAttribute('name', 'TDButton_Overlay_' .. id);
-	end
-
-	frame:SetParent(parent);
-	frame:SetFrameStrata('HIGH');
-	frame:SetPoint('CENTER', 0, 0);
-	frame:SetWidth(parent:GetWidth() * 1.4);
-	frame:SetHeight(parent:GetHeight() * 1.4);
-
-	local t = frame.texture;
-	if not t then
-		t = frame:CreateTexture('GlowOverlay', 'OVERLAY');
-		t:SetTexture(texture or TDDps_Options_GetTexture());
-		t:SetBlendMode('ADD');
-		frame.texture = t;
-	end
-
-	t:SetAllPoints(frame);
-	t:SetVertexColor(
-		r or TDDps_Options.highlightColor.r,
-		g or TDDps_Options.highlightColor.g,
-		b or TDDps_Options.highlightColor.b,
-		TDDps_Options.highlightColor.a
-	);
-
-	tinsert(TDButton.Frames, frame);
-	return frame;
-end
-
-function TDButton.DestroyAllOverlays()
-	local frame;
-	for key, frame in pairs(TDButton.Frames) do
-		frame:GetParent().tdOverlays = nil;
-		frame:ClearAllPoints();
-		frame:Hide();
-		frame:SetParent(UIParent);
-		frame.width = nil;
-		frame.height = nil;
-	end
-	for key, frame in pairs(TDButton.Frames) do
-		tinsert(TDButton.FramePool, frame);
-		TDButton.Frames[key] = nil;
-	end
-end
-
-function TDButton.UpdateButtonGlow()
-	local LAB;
-	local LBG;
-	local origShow;
-	local noFunction = function() end;
-
-	if IsAddOnLoaded('ElvUI') then
-		LAB = LibStub:GetLibrary('LibActionButton-1.0-ElvUI');
-		LBG = LibStub:GetLibrary('LibButtonGlow-1.0');
-		origShow = LBG.ShowOverlayGlow;
-	elseif IsAddOnLoaded('Bartender4') then
-		LAB = LibStub:GetLibrary('LibActionButton-1.0');
-	end
-
-	if TDDps_Options.disableButtonGlow then
-		ActionBarActionEventsFrame:UnregisterEvent('SPELL_ACTIVATION_OVERLAY_GLOW_SHOW');
-		if LAB then
-			LAB.eventFrame:UnregisterEvent('SPELL_ACTIVATION_OVERLAY_GLOW_SHOW');
-		end
-
-		if LBG then
-			LBG.ShowOverlayGlow = noFunction;
-		end
-	else
-		ActionBarActionEventsFrame:RegisterEvent('SPELL_ACTIVATION_OVERLAY_GLOW_SHOW');
-		if LAB then
-			LAB.eventFrame:RegisterEvent('SPELL_ACTIVATION_OVERLAY_GLOW_SHOW');
-		end
-
-		if LBG then
-			LBG.ShowOverlayGlow = origShow;
-		end
-	end
-end
-
-----------------------------------------------
--- Show Overlay on button
-----------------------------------------------
-function TDButton.Glow(button, id, r, g, b, texture)
-	if button.tdOverlays and button.tdOverlays[id] then
-		button.tdOverlays[id]:Show();
-	else
-		if not button.tdOverlays then
-			button.tdOverlays = {};
-		end
-
-		button.tdOverlays[id] = TDButton.CreateOverlay(button, id, texture, r, g, b);
-		button.tdOverlays[id]:Show();
-	end
-end
-
-----------------------------------------------
--- Hide Overlay on button
-----------------------------------------------
-function TDButton.HideGlow(button, id)
-	if button.tdOverlays and button.tdOverlays[id] then
-		button.tdOverlays[id]:Hide();
-	end
-end
-
-----------------------------------------------
--- Fetch button spells
-----------------------------------------------
-function TDButton.Fetch()
-	local origEna = TDDps.rotationEnabled;
-	TDDps.rotationEnabled = false;
-	TDDps.Spell = nil;
-
-	TDButton.GlowClear();
-	TDButton.Spells = {};
-	TDButton.Flags = {};
-	TDButton.SpellsGlowing = {};
-	local isBartender = IsAddOnLoaded('Bartender4');
-	local isElv = IsAddOnLoaded('ElvUI');
-	local isSv = IsAddOnLoaded('SVUI_ActionBars');
-
-	if (isBartender) then
-		TDButton.FetchBartender4();
-	elseif (isElv) then
-		TDButton.FetchElvUI();
-	elseif (isSv) then
-		TDButton.FetchSuperVillain();
-	else
-		TDButton.FetchBlizzard();
-	end
-
-	-- It does not alter original button frames so it needs to be fetched too
-	if IsAddOnLoaded('ButtonForge') then
-		TDButton.FetchButtonForge();
-	end
-
-	TDDps.rotationEnabled = origEna;
-	--TDDps:Print(_tdInfo, 'Fetched action bars!');
-	-- after fetching invoke spell check
-	if TDDps.rotationEnabled then
-		TDDps:InvokeNextSpell();
-	end
-end
-
-----------------------------------------------
--- Button spells on original blizzard UI
-----------------------------------------------
-function TDButton.FetchBlizzard()
-	local TDActionBarsBlizzard = {'Action', 'MultiBarBottomLeft', 'MultiBarBottomRight', 'MultiBarRight', 'MultiBarLeft'};
-	for _, barName in pairs(TDActionBarsBlizzard) do
-		for i = 1, 12 do
-			local button = _G[barName .. 'Button' .. i];
-			local slot = ActionButton_GetPagedID(button) or ActionButton_CalculateAction(button) or button:GetAttribute('action') or 0;
-			if HasAction(slot) then
-				local actionName, _;
-				local actionType, id = GetActionInfo(slot);
-				if actionType == 'macro' then _, _ , id = GetMacroSpell(id) end
-				if actionType == 'item' then
-					actionName = GetItemInfo(id);
-				elseif actionType == 'spell' or (actionType == 'macro' and id) then
-					actionName = GetSpellInfo(id);
-				end
-				if actionName then
-					if TDButton.Spells[actionName] == nil then
-						TDButton.Spells[actionName] = {};
-					end
-
-					tinsert(TDButton.Spells[actionName], button);
-				end
-			end
-		end
-	end
-end
-
-----------------------------------------------
--- Button spells on original button forge
-----------------------------------------------
-function TDButton.FetchButtonForge()
-	local i = 1;
-	while true do
-		local button = _G['ButtonForge' .. i];
-		if not button then
-			break;
-		end
-		i = i + 1;
-
-		local type = button:GetAttribute('type');
-		if type then
-			local actionType = button:GetAttribute(type);
-			local id;
-			local actionName;
-			if type == 'macro' then
-				local id = GetMacroSpell(actionType);
-				if id then
-					actionName = GetSpellInfo(id);
-				end
-			elseif type == 'item' then
-				actionName = GetItemInfo(actionType);
-			elseif type == 'spell' then
-				actionName = GetSpellInfo(actionType);
-			end
-			if actionName then
-				if TDButton.Spells[actionName] == nil then
-					TDButton.Spells[actionName] = {};
-				end
-
-				tinsert(TDButton.Spells[actionName], button);
-			end
-		end
-	end
-end
-
-----------------------------------------------
--- Button spells on ElvUI
-----------------------------------------------
-function TDButton.FetchElvUI()
-	local ret = false;
-	for x = 1, 10 do
-		for i = 1, 12 do
-			local button = _G['ElvUI_Bar' .. x .. 'Button' .. i];
-			if button then
-				local spellId = button:GetSpellId();
-				if spellId then
-					local actionName, _ = GetSpellInfo(spellId);
-					if actionName then
-						if TDButton.Spells[actionName] == nil then
-							TDButton.Spells[actionName] = {};
-						end
-						ret = true;
-						tinsert(TDButton.Spells[actionName], button);
-					end
-				end
-			end
-		end
-	end
-	return ret;
-end
-
-----------------------------------------------
--- Button spells on SuperVillain
-----------------------------------------------
-function TDButton.FetchSuperVillain()
-	local ret = false;
-	for x = 1, 10 do
-		for i = 1, 12 do
-			local button = _G['SVUI_ActionBar' .. x .. 'Button' .. i];
-			if button then
-				local spellId = button:GetSpellId();
-				if spellId then
-					local actionName, _ = GetSpellInfo(spellId);
-					if actionName then
-						if TDButton.Spells[actionName] == nil then
-							TDButton.Spells[actionName] = {};
-						end
-						ret = true;
-						tinsert(TDButton.Spells[actionName], button);
-					end
-				end
-			end
-		end
-	end
-	return ret;
-end
-
-----------------------------------------------
--- Button spells on Bartender4
-----------------------------------------------
-function TDButton.FetchBartender4()
-	local ret = false;
-	for i = 1, 120 do
-		local button = _G['BT4Button' .. i];
-		if button then
-			local spellId = button:GetSpellId();
-			if spellId then
-				local actionName, _ = GetSpellInfo(spellId);
-				if actionName then
-					if TDButton.Spells[actionName] == nil then
-						TDButton.Spells[actionName] = {};
-					end
-					ret = true;
-					tinsert(TDButton.Spells[actionName], button);
-				end
-			end
-		end
-	end
-	return ret;
-end
-
-----------------------------------------------
--- Dump spells for debug
-----------------------------------------------
-function TDButton.Dump()
-	local s = '';
-	for k, v in pairs(TDButton.Spells) do
-		s = s .. ', ' .. k;
-	end
-	print(s);
-end
-
-----------------------------------------------
--- Find button on action bars
-----------------------------------------------
-function TDButton.FindSpell(spellName)
-	local name = GetSpellInfo(spellName) or spellName;
-	return TDButton.Spells[name];
-end
-
-----------------------------------------------
--- Glow independent button by spell name
-----------------------------------------------
-function TDButton.GlowIndependent(spellName, id, r, g, b, texture)
-	local name = GetSpellInfo(spellName) or spellName;
-	if TDButton.Spells[name] ~= nil then
-		for k, button in pairs(TDButton.Spells[name]) do
-			TDButton.Glow(button, id, r, g, b, texture);
-		end
-	end
-end
-
-----------------------------------------------
--- Clear glow independent button by spell name
-----------------------------------------------
-function TDButton.ClearGlowIndependent(spellName, id)
-	local name = GetSpellInfo(spellName) or spellName;
-	if TDButton.Spells[name] ~= nil then
-		for k, button in pairs(TDButton.Spells[name]) do
-			TDButton.HideGlow(button, id);
-		end
-	end
-end
-
-----------------------------------------------
--- Glow cooldown
-----------------------------------------------
-function TDButton.GlowCooldown(spell, condition)
-	if TDButton.Flags[spell] == nil then
-		TDButton.Flags[spell] = false;
-	end
-	if condition and not TDButton.Flags[spell] then
-		TDButton.Flags[spell] = true;
-		TDButton.GlowIndependent(spell, spell, 0, 1, 0);
-	end
-	if not condition and TDButton.Flags[spell] then
-		TDButton.Flags[spell] = false;
-		TDButton.ClearGlowIndependent(spell, spell);
-	end
-end
-
-function TDButton_GlowCooldown(spell, condition)
-	TDButton.GlowCooldown(spell, condition);
-end
-----------------------------------------------
--- Glow spell by name
-----------------------------------------------
-function TDButton.GlowSpell(spellName)
-	if TDButton.Spells[spellName] ~= nil then
-		for k, button in pairs(TDButton.Spells[spellName]) do
-			TDButton.Glow(button, 'next');
-		end
-		TDButton.SpellsGlowing[spellName] = 1;
-	else
-		TDDps:Print(_tdError, 'Spell not found on action bars: ' .. spellName);
-	end
-end
-
-----------------------------------------------
--- Glow spell by id
-----------------------------------------------
-function TDButton.GlowSpellId(spellId)
-	local name = GetSpellInfo(spellId);
-	TDButton.GlowSpell(name);
-end
-
-----------------------------------------------
--- Glow next spell by name
-----------------------------------------------
-function TDButton.GlowNextSpell(spellName)
-	TDButton.GlowClear();
-	TDButton.GlowSpell(spellName);
-end
-
-----------------------------------------------
--- Glow next spell by id
-----------------------------------------------
-function TDButton.GlowNextSpellId(spellId)
-	local spellName = GetSpellInfo(spellId);
-	TDButton.GlowClear();
-	TDButton.GlowSpell(spellName);
-end
-
-----------------------------------------------
--- Clear next spell glows
-----------------------------------------------
-function TDButton.GlowClear()
-	for spellName, v in pairs(TDButton.SpellsGlowing) do
-		if v == 1 then
-			for k, button in pairs(TDButton.Spells[spellName]) do
-				TDButton.HideGlow(button, 'next');
-			end
-			TDButton.SpellsGlowing[spellName] = 0;
-		end
-	end
-end
\ No newline at end of file
diff --git a/TDDps.lua b/TDDps.lua
deleted file mode 100644
index 58d7c77..0000000
--- a/TDDps.lua
+++ /dev/null
@@ -1,241 +0,0 @@
-_TD = _TD or {}; -- depreciated
-
-local timer = LibStub:GetLibrary("BigLibTimer6"):Register(timer);
-
-local TDDps = CreateFrame('Frame', 'TDDps');
-TDDps.AddonEnabled = false;
-TDDps.rotationEnabled = false;
-TDDps.ModuleOnEnable = nil;
-TDDps.NextSpell = nil;
-TDDps.Spell = nil;
-TDDps.Description = nil;
-TDDps.Time = 0;
-
-TDDps.Classes = {
-	[1] = 'Warrior',
-	[2] = 'Paladin',
-	[3] = 'Hunter',
-	[4] = 'Rogue',
-	[5] = 'Priest',
-	[6] = 'DeathKnight',
-	[7] = 'Shaman',
-	[8] = 'Mage',
-	[9] = 'Warlock',
-	[10] = 'Monk',
-	[11] = 'Druid',
-	[12] = 'DemonHunter',
-}
-
--- Name and colors
-TDDpsName = 'TDDPS';
-_tdInfo = '|cFF1394CC';
-_tdError = '|cFFF0563D';
-_tdSuccess = '|cFFBCCF02';
-
--- Globals for time to die
-TDDps_TargetGuid = nil;
-TD_Hp0, TD_T0, TD_Hpm, TD_Tm = nil, nil, nil, nil;
-
-function TDDps:Print(color, message, force)
-	if force or TDDps_Options.debugMode or (not TDDps_Options.disabledInfo and color == _tdError) then
-		print(color .. TDDpsName .. ': ' .. message);
-	end
-end
-
-----------------------------------------------
--- Disable dps addon functionality
-----------------------------------------------
-function TDDps:DisableAddon()
-	if not TDDps.AddonEnabled then
-		return;
-	end
-
-	TDButton.DestroyAllOverlays();
-	TDDps:Print(_tdInfo, 'Disabling', true);
-	TDDps:SetScript('OnUpdate', nil);
-	TDDps.Spell = nil;
-	TDDps.rotationEnabled = false;
-	TDDps.AddonEnabled = false;
-end
-
-----------------------------------------------
--- Initialize dps addon functionality
-----------------------------------------------
-function TDDps:InitAddon()
-	TDDps:Show();
-
-	TDDps:RegisterEvent('PLAYER_TARGET_CHANGED');
-	TDDps:RegisterEvent('PLAYER_TALENT_UPDATE');
-	TDDps:RegisterEvent('ACTIONBAR_SLOT_CHANGED');
-	TDDps:RegisterEvent('PLAYER_REGEN_DISABLED');
-	TDDps:RegisterEvent('PLAYER_ENTERING_WORLD');
-
-	TDDps:RegisterEvent('ACTIONBAR_HIDEGRID');
-	TDDps:RegisterEvent('ACTIONBAR_PAGE_CHANGED');
-	TDDps:RegisterEvent('LEARNED_SPELL_IN_TAB');
-	TDDps:RegisterEvent('CHARACTER_POINTS_CHANGED');
-	TDDps:RegisterEvent('ACTIVE_TALENT_GROUP_CHANGED');
-	TDDps:RegisterEvent('PLAYER_SPECIALIZATION_CHANGED');
-	TDDps:RegisterEvent('UPDATE_MACROS');
-	TDDps:RegisterEvent('VEHICLE_UPDATE');
---	TDDps:RegisterEvent('PLAYER_REGEN_ENABLED');
-
-	TDDps:SetScript('OnEvent', self.OnEvent);
-
-	TDDps:Print(_tdInfo, 'Initialized');
-end
-
-----------------------------------------------
--- Enable dps addon functionality
-----------------------------------------------
-function TDDps:EnableAddon()
-	TDDps:Print(_tdInfo, 'Enabling');
-
-	if TDDps.NextSpell == nil or TDDps.AddonEnabled then
-		TDDps:Print(_tdError, 'Failed to enable addon!', true);
-		return;
-	end
-	TDDps:Print(_tdInfo, 'Fetching');
-	TDButton.Fetch();
-
-	if TDDps.ModuleOnEnable then
-		TDDps.ModuleOnEnable();
-	end
-
-	TDDps:SetScript('OnUpdate', TDDps.OnUpdate);
-
-	TDDps.AddonEnabled = true;
-	TDDps:Print(_tdSuccess, 'Enabled', true);
-end
-
-function TDDps_EnableAddon()
-	-- backwards compatibility, don't load it until we say so
-end
-
-----------------------------------------------
--- Event Script, Target Change, Specializaton Change
-----------------------------------------------
-function TDDps:InvokeNextSpell()
-	-- invoke spell check
-	local oldSkill = TDDps.Spell;
-
-	TDDps.Spell = TDDps.NextSpell();
-
-	if (oldSkill ~= TDDps.Spell or oldSkill == nil) and TDDps.Spell ~= nil then
-		TDButton.GlowNextSpellId(TDDps.Spell);
-	end
-	if TDDps.Spell == nil and oldSkill ~= nil then
-		TDButton.GlowClear();
-	end
-end
-
-----------------------------------------------
--- Event Script, Target Change, Specializaton Change
-----------------------------------------------
-function TDDps.OnEvent(self, event)
-	if event == 'PLAYER_TALENT_UPDATE' then
-		TDDps:DisableAddon();
-	elseif event == 'PLAYER_ENTERING_WORLD' then
-		TDButton.UpdateButtonGlow();
-	elseif event == 'ACTIONBAR_SLOT_CHANGED' or
-			event == 'ACTIONBAR_HIDEGRID' or
-			event == 'ACTIONBAR_PAGE_CHANGED' or
-			event == 'LEARNED_SPELL_IN_TAB' or
-			event == 'CHARACTER_POINTS_CHANGED' or
-			event == 'ACTIVE_TALENT_GROUP_CHANGED' or
-			event == 'PLAYER_SPECIALIZATION_CHANGED' or
-			event == 'UPDATE_MACROS' or
-			event == 'VEHICLE_UPDATE' then
-			if TDDps.rotationEnabled then
-				timer:SetTimer("TDButton_Fetch", 0.5, 0, TDButton.Fetch);
-			end
-		return;
-	end
-	if event == 'PLAYER_TARGET_CHANGED' then
-		TD_Hp0, TD_T0, TD_Hpm, TD_Tm = nil, nil, nil, nil;
-
-		if UnitExists('target') and not UnitIsFriend('player', 'target') then
-			TDDps_TargetGuid = UnitGUID('target');
-		else
-			TDDps_TargetGuid = nil;
-		end
-	end
-	if TDDps.rotationEnabled then
-		if event == 'PLAYER_TARGET_CHANGED' then
-			if (UnitIsFriend('player', 'target')) then
-				return;
-			else
-				TDDps:InvokeNextSpell();
-			end
-		end
-	end
-	if event == 'PLAYER_REGEN_DISABLED' and TDDps_Options.onCombatEnter and not TDDps.rotationEnabled then
-		TDDps:Print(_tdSuccess, 'Auto enable on combat!');
-		TDDps.rotationEnabled = true;
-		TDDps:LoadModule();
-	end
---	if event == 'PLAYER_REGEN_ENABLED' then
---		TDDps:Print(_tdSuccess, 'Auto disable on combat!');
---		TDDps.rotationEnabled = false;
---		TDDps:DisableAddon();
---	end
-end
-
-----------------------------------------------
--- Update script (timer)
-----------------------------------------------
-function TDDps.OnUpdate(self, elapsed)
-	TDDps.Time = TDDps.Time + elapsed;
-	if TDDps.Time >= TDDps_Options.interval then
-		TDDps.Time = 0;
-		TDDps:InvokeNextSpell();
-	end
-end
-
-----------------------------------------------
--- Load appropriate addon for class
-----------------------------------------------
-function TDDps:LoadModule()
-	TDDps.rotationEnabled = true;
-
-	TDDps:Print(_tdInfo, 'Loading class module');
-	local _, _, classId = UnitClass('player');
-	if TDDps.Classes[classId] == nil then
-		TDDps:Print(_tdError, 'Invalid player class, please contact author of addon.', true);
-		return;
-	end
-
-	local module = 'TDDps_' .. TDDps.Classes[classId];
-
-	if not IsAddOnLoaded(module) then
-		LoadAddOn(module);
-	end
-
-	if not IsAddOnLoaded(module) then
-		TDDps:Print(_tdError, 'Could not find class module.', true);
-		return;
-	end
-
-	local mode = GetSpecialization();
-	local init = module .. '_EnableAddon';
-
-	_G[init](mode);
-
-	-- backward compatiblity
-	if _TD['DPS_NextSpell'] ~= nil then
-		TDDps:Print(_tdInfo, 'Backward compatibility mode');
-		TDDps.NextSpell = _TD['DPS_NextSpell'];
-		TDDps.ModuleOnEnable = _TD['DPS_OnEnable'];
-		TDDps.Description = _TD['DPS_Description'];
-	end
-
-	TDDps:EnableAddon();
-
-	if TDDps.NextSpell == nil then
-		TDDps.rotationEnabled = false;
-		TDDps:Print(_tdError, 'Specialization is not supported.', true);
-	end
-	TDDps:Print(_tdSuccess, 'Finished Loading class module');
-end
-
-TDDps:InitAddon();
\ No newline at end of file
diff --git a/TDDps.toc b/TDDps.toc
deleted file mode 100644
index 532179c..0000000
--- a/TDDps.toc
+++ /dev/null
@@ -1,20 +0,0 @@
-## Title: TDDps
-## Notes: Rotation helper framework.
-## Version: 2.0
-## Author: Kaminaris
-## Interface: 70100
-## SavedVariables: TDDps_Options
-## OptionalDependencies: Bartender4, ElvUI, ButtonForge, SVUI_ActionBars
-
-Libs\LibStub\LibStub.lua
-Libs\CallbackHandler-1.0\CallbackHandler-1.0.xml
-Libs\LibSharedMedia-3.0\lib.xml
-Libs\AceGUI-3.0\AceGUI-3.0.xml
-Libs\AceGUI-3.0-SharedMediaWidgets\widget.xml
-Libs\AceConfig-3.0\AceConfig-3.0.xml
-Libs\BigLibTimer\BigLibTimer.lua
-
-TDSettings.lua
-TDButtons.lua
-TDHelper.lua
-TDDps.lua
\ No newline at end of file
diff --git a/TDHelper.lua b/TDHelper.lua
deleted file mode 100644
index eb6ecfc..0000000
--- a/TDHelper.lua
+++ /dev/null
@@ -1,270 +0,0 @@
-
--- Global cooldown spell id
-_GlobalCooldown		= 61304;
-
--- Bloodlust effects
-_Bloodlust			= 2825;
-_TimeWrap			= 80353;
-_Heroism			= 32182;
-_AncientHysteria	= 90355;
-_Netherwinds		= 160452;
-_DrumsOfFury		= 178207;
-_Exhaustion			= 57723;
-
-local INF = 2147483647;
-
-local _Bloodlusts = {_Bloodlust, _TimeWrap, _Heroism, _AncientHysteria, _Netherwinds, _DrumsOfFury};
-
-----------------------------------------------
--- Current Specialisation name
-----------------------------------------------
-function TD_SpecName()
-	local currentSpec = GetSpecialization();
-	local currentSpecName = currentSpec and select(2, GetSpecializationInfo(currentSpec)) or 'None';
-	return currentSpecName;
-end
-
-----------------------------------------------
--- Is talent enabled
-----------------------------------------------
-function TD_TalentEnabled(talent)
-	local found = false;
-	for i=1,7 do
-		for j=1,3 do
-			local id, n, x, sel = GetTalentInfo(i,j,GetActiveSpecGroup());
-			if (id == talent or n == talent) and sel then
-				found = true;
-			end
-		end
-	end
-	return found;
-end
-
-----------------------------------------------
--- Is aura on player
-----------------------------------------------
-function TD_PersistentAura(name)
-	local spellName = GetSpellInfo(name);
-	local aura, _, _, count = UnitAura('player', spellName);
-	if aura then
-		return true, count;
-	end
-	return false, 0;
-end
-
-----------------------------------------------
--- Is aura on player
-----------------------------------------------
-function TD_Aura(name, timeShift)
-	timeShift = timeShift or 0.2;
-	local spellName = GetSpellInfo(name);
-	local _, _, _, count, _, _, expirationTime = UnitAura('player', spellName);
-	if expirationTime ~= nil and (expirationTime - GetTime()) > timeShift then
-		return true, count;
-	end
-	return false, 0;
-end
-
-----------------------------------------------
--- Is aura on specific unit
-----------------------------------------------
-function TD_UnitAura(name, timeShift, unit)
-	timeShift = timeShift or 0.2;
-	local spellName = GetSpellInfo(name);
-	local _, _, _, count, _, _, expirationTime = UnitAura(unit, spellName);
-	if expirationTime ~= nil and (expirationTime - GetTime()) > timeShift then
-		return true, count;
-	end
-	return false, 0;
-end
-
-----------------------------------------------
--- Is aura on target
-----------------------------------------------
-function TD_TargetAura(name, timeShift)
-	timeShift = timeShift or 0;
-	local spellName = GetSpellInfo(name) or name;
-	local _, _, _, _, _, _, expirationTime = UnitAura('target', spellName, nil, 'PLAYER|HARMFUL');
-	if expirationTime ~= nil and (expirationTime - GetTime()) > timeShift then
-		local cd = expirationTime - GetTime() - (timeShift or 0);
-		return true, cd;
-	end
-	return false, 0;
-end
-
-----------------------------------------------
--- When current cast will end
-----------------------------------------------
-function TD_EndCast(target)
-	local t = GetTime();
-	local c = t * 1000;
-	local spell, _, _, _, _, endTime = UnitCastingInfo(target or 'player');
-	local gstart, gduration = GetSpellCooldown(_GlobalCooldown);
-	local gcd = gduration - (t - gstart);
-	if gcd < 0 then gcd = 0; end;
-	if endTime == nil then
-		return gcd, '', gcd;
-	end
-	local timeShift = (endTime - c) / 1000;
-	if gcd > timeShift then
-		timeShift = gcd;
-	end
-	return timeShift, spell, gcd;
-end
-
-----------------------------------------------
--- Target Percent Health
-----------------------------------------------
-function TD_TargetPercentHealth()
-	local health = UnitHealth('target');
-	if health <= 0 then
-		return 0;
-	end;
-	local healthMax = UnitHealthMax('target');
-	if healthMax <= 0 then
-		return 0;
-	end;
-	return health/healthMax;
-end
-
-----------------------------------------------
--- Simple calculation of global cooldown
-----------------------------------------------
-function TD_GlobalCooldown()
-	local haste = UnitSpellHaste('player');
-	local gcd = 1.5 / ((haste / 100) + 1);
-	if gcd < 1 then
-		gcd = 1;
-	end
-	return gcd;
-end
-
-
-----------------------------------------------
--- Stacked spell CD, charges and max charges
-----------------------------------------------
-function TD_SpellCharges(spell, timeShift)
-	local currentCharges, maxCharges, cooldownStart, cooldownDuration = GetSpellCharges(spell);
-	if currentCharges == nil then
-		local cd = TD_Cooldown(spell, timeShift);
-		if cd <= 0 then
-			return 0, 1, 0;
-		else
-			return cd, 0, 1;
-		end
-	end
-	local cd = cooldownDuration - (GetTime() - cooldownStart) - (timeShift or 0);
-	if cd > cooldownDuration then
-		cd = 0;
-	end
-	return cd, currentCharges, maxCharges;
-end
-
-----------------------------------------------
--- Is Spell Available
-----------------------------------------------
-function TD_SpellAvailable(spell, timeShift)
-	local cd = TD_Cooldown(spell, timeShift);
-	return cd <= 0, cd;
-end
-
-----------------------------------------------
--- Extract tooltip number
-----------------------------------------------
-function TD_ExtractTooltip(spell, pattern)
-	local _pattern = gsub(pattern, "%%s", "([%%d%.,]+)");
-
-	if not TDSpellTooltip then
-		CreateFrame('GameTooltip', 'TDSpellTooltip', UIParent, 'GameTooltipTemplate');
-		TDSpellTooltip:SetOwner(UIParent, "ANCHOR_NONE")
-	end
-	TDSpellTooltip:SetSpellByID(spell);
-
-	for i = 2, 4 do
-		local line = _G['TDSpellTooltipTextLeft' .. i];
-		local text = line:GetText();
-
-		if text then
-			local cost = strmatch(text, _pattern);
-			if cost then
-				cost = cost and tonumber((gsub(cost, "%D", "")));
-				return cost;
-			end
-		end
-	end
-
-	return 0;
-end
-
-----------------------------------------------
--- Spell Cooldown
-----------------------------------------------
-function TD_Cooldown(spell, timeShift)
-	local start, duration, enabled = GetSpellCooldown(spell);
-	if enabled and duration == 0 and start == 0 then
-		return 0;
-	elseif enabled then
-		return (duration - (GetTime() - start) - (timeShift or 0));
-	else
-		return 100000;
-	end;
-end
-
-----------------------------------------------
--- Time to die - NOT YET WORKING
-----------------------------------------------
---TD_Hp0, TD_T0, TD_Hpm, TD_Tm
-function TD_TimeToDie(health)
-	local unit = UnitGUID('target');
-	if unit ~= TDDps_TargetGuid then
-		--print('phial');
-		return INF;
-	end
-
-	health = health or UnitHealth('target');
-
-	if health == UnitHealthMax('target') then
-		TD_Hp0, TD_T0, TD_Hpm, TD_Tm = nil, nil, nil, nil;
-		--print('phial2');
-		return INF;
-	end
-
-	local time = GetTime();
-
-	if not TD_Hp0 then
-		TD_Hp0, TD_T0 = health, time;
-		TD_Hpm, TD_Tm = health, time;
-		--print('phial3');
-		return INF;
-	end
-
-	TD_Hpm = (TD_Hpm + health) * .5;
-	TD_Tm = (TD_Tm + time) * .5;
-
-	if TD_Hpm >= TD_Hp0 then
-		TD_Hp0, TD_T0, TD_Hpm, TD_Tm = nil, nil, nil, nil;
-	else
-		return health * (TD_T0 - TD_Tm) / (TD_Hpm - TD_Hp0);
-	end
-end
-
-----------------------------------------------
--- Current or Future Mana Percent
-----------------------------------------------
-function TD_Mana(minus, timeShift)
-	local _, casting = GetManaRegen();
-	local mana = UnitPower('player', 0) - minus + (casting * timeShift);
-	return mana / UnitPowerMax('player', 0), mana;
-end
-
-----------------------------------------------
--- Is bloodlust or similar effect
-----------------------------------------------
-function TD_Bloodlust(timeShift)
-	-- @TODO: detect exhausted/seated debuff instead of 6 auras
-	for k, v in pairs (_Bloodlusts) do
-		if TD_Aura(v, timeShift or 0) then return true; end
-	end
-
-	return false;
-end
diff --git a/TDSettings.lua b/TDSettings.lua
deleted file mode 100644
index 6835a44..0000000
--- a/TDSettings.lua
+++ /dev/null
@@ -1,147 +0,0 @@
-local AceGUI = LibStub('AceGUI-3.0');
-local lsm = LibStub('AceGUISharedMediaWidgets-1.0');
-local media = LibStub('LibSharedMedia-3.0');
-
-TDDps_textures = {
-	['Ping'] = 'Interface\\Cooldown\\ping4',
-	['Star'] = 'Interface\\Cooldown\\star4',
-	['Starburst'] = 'Interface\\Cooldown\\starburst',
-};
-
-TDDps_Options = {
-	enabled = true,
-	disabledInfo = false,
-	debugMode = false,
-	disableButtonGlow = false,
-	onCombatEnter = true,
-	texture = '',
-	customTexture = '',
-	highlightColor = {
-		r = 1, g = 1, b = 1, a = 1
-	},
-	interval = 0.15
-}
-
-TDDps_Temp = {};
-
-function TDDps_Options_GetTexture()
-	if TDDps_Options.customTexture ~= '' and TDDps_Options.customTexture ~= nil then
-		TDDps_Temp.finalTexture = TDDps_Options.customTexture;
-		return TDDps_Temp.finalTexture;
-	end
-
-	TDDps_Temp.finalTexture = TDDps_textures[TDDps_Options.texture];
-	if TDDps_Temp.finalTexture == '' or TDDps_Temp.finalTexture == nil then
-		TDDps_Temp.finalTexture = 'Interface\\Cooldown\\ping4';
-	end
-
-	return TDDps_Temp.finalTexture;
-end
-
-local options = {
-	type = 'group',
-	name = 'TD Dps Options',
-	inline = false,
-	args = {
-		enable = {
-			name = 'Enable',
-			desc = 'Enables / disables the addon',
-			type = 'toggle',
-			width = 'full',
-			set = function(info, val)
-				TDDps_Options.enabled = val;
-			end,
-			get = function(info) return TDDps_Options.enabled end
-		},
-		disabledInfo = {
-			name = 'Disable info messages',
-			desc = 'Enables / disables info messages, if you have issues with addon, make sure to deselect this.',
-			type = 'toggle',
-			width = 'full',
-			set = function(info, val)
-				TDDps_Options.disabledInfo = val;
-			end,
-			get = function(info) return TDDps_Options.disabledInfo end
-		},
-		debugMode = {
-			name = 'Enable debug mode',
-			desc = 'Enables spammy chat messages (use this when addon does not work for you)',
-			type = 'toggle',
-			width = 'full',
-			set = function(info, val)
-				TDDps_Options.debugMode = val;
-			end,
-			get = function(info) return TDDps_Options.debugMode end
-		},
-		disableButtonGlow = {
-			name = 'Dissable blizzard button glow (experimental)',
-			desc = 'Disables original blizzard button glow',
-			type = 'toggle',
-			width = 'full',
-			set = function(info, val)
-				TDDps_Options.disableButtonGlow = val;
-				TDButton_UpdateButtonGlow();
-			end,
-			get = function(info) return TDDps_Options.disableButtonGlow end
-		},
-		onCombatEnter = {
-			name = 'Enable upon entering combat',
-			desc = 'Automatically enables helper upon entering combat',
-			type = 'toggle',
-			width = 'full',
-			set = function(info, val)
-				TDDps_Options.onCombatEnter = val;
-			end,
-			get = function(info) return TDDps_Options.onCombatEnter end
-		},
-		customTexture = {
-			name = 'Custom Texture',
-			desc = 'Sets Highlight texture, has priority over selected one (changing this requires UI Reload)',
-			type = 'input',
-			set = function(info, val) TDDps_Options.customTexture = strtrim(val or ''); end,
-			get = function(info) return strtrim(TDDps_Options.customTexture or '') end
-		},
-		texture = {
-			type = 'select',
-			dialogControl = 'LSM30_Background',
-			name = 'Texture',
-			desc = 'Sets Highlight texture (changing this requires UI Reload)',
-			values = function()
-				return TDDps_textures;
-			end,
-			get = function()
-				return TDDps_Options.texture;
-			end,
-			set = function(self, val)
-				TDDps_Options.texture = val;
-			end,
-		},
-		highlightColor = {
-			name = 'Highlight color',
-			desc = 'Sets Highlight color',
-			type = 'color',
-			set = function(info, r, g, b, a)
-				TDDps_Options.highlightColor.r = r;
-				TDDps_Options.highlightColor.g = g;
-				TDDps_Options.highlightColor.b = b;
-				TDDps_Options.highlightColor.a = a;
-			end,
-			get = function(info)
-				return TDDps_Options.highlightColor.r, TDDps_Options.highlightColor.g, TDDps_Options.highlightColor.b, TDDps_Options.highlightColor.a;
-			end,
-			hasAlpha = true
-		},
-		interval = {
-			name = 'Interval in seconds',
-			desc = 'Sets how frequent rotation updates will be. Low value will result in fps drops.',
-			type = 'range',
-			min = 0.01,
-			max = 2,
-			set = function(info,val) TDDps_Options.interval = val end,
-			get = function(info) return TDDps_Options.interval end
-		},
-	},
-}
-
-LibStub('AceConfigRegistry-3.0'):RegisterOptionsTable('TDDps_Settings', options)
-LibStub('AceConfigDialog-3.0'):AddToBlizOptions('TDDps_Settings', 'TD Dps')
diff --git a/buttons.lua b/buttons.lua
new file mode 100644
index 0000000..65e0bfb
--- /dev/null
+++ b/buttons.lua
@@ -0,0 +1,355 @@
+MaxDps.Spells = {};
+MaxDps.Flags = {};
+MaxDps.SpellsGlowing = {};
+MaxDps.FramePool = {};
+MaxDps.Frames = {};
+
+function MaxDps:CreateOverlay(parent, id, texture, r, g, b)
+	local frame = tremove(self.FramePool);
+	if not frame then
+		frame = CreateFrame('Frame', 'MaxDps_Overlay_' .. id, parent);
+	end
+
+	frame:SetParent(parent);
+	frame:SetFrameStrata('HIGH');
+	frame:SetPoint('CENTER', 0, 0);
+	frame:SetWidth(parent:GetWidth() * 1.4);
+	frame:SetHeight(parent:GetHeight() * 1.4);
+
+	local t = frame.texture;
+	if not t then
+		t = frame:CreateTexture('GlowOverlay', 'OVERLAY');
+		t:SetTexture(texture or MaxDps:GetTexture());
+		t:SetBlendMode('ADD');
+		frame.texture = t;
+	end
+
+	t:SetAllPoints(frame);
+	t:SetVertexColor(
+		r or self.db.global.highlightColor.r,
+		g or self.db.global.highlightColor.g,
+		b or self.db.global.highlightColor.b,
+		self.db.global.highlightColor.a
+	);
+
+	tinsert(self.Frames, frame);
+	return frame;
+end
+
+function MaxDps:DestroyAllOverlays()
+	local frame;
+	for key, frame in pairs(self.Frames) do
+		frame:GetParent().MaxDpsOverlays = nil;
+		frame:ClearAllPoints();
+		frame:Hide();
+		frame:SetParent(UIParent);
+		frame.width = nil;
+		frame.height = nil;
+	end
+	for key, frame in pairs(self.Frames) do
+		tinsert(self.FramePool, frame);
+		self.Frames[key] = nil;
+	end
+end
+
+function MaxDps:UpdateButtonGlow()
+	local LAB;
+	local LBG;
+	local origShow;
+	local noFunction = function() end;
+
+	if IsAddOnLoaded('ElvUI') then
+		LAB = LibStub:GetLibrary('LibActionButton-1.0-ElvUI');
+		LBG = LibStub:GetLibrary('LibButtonGlow-1.0');
+		origShow = LBG.ShowOverlayGlow;
+	elseif IsAddOnLoaded('Bartender4') then
+		LAB = LibStub:GetLibrary('LibActionButton-1.0');
+	end
+
+	if self.db.global.disableButtonGlow then
+		ActionBarActionEventsFrame:UnregisterEvent('SPELL_ACTIVATION_OVERLAY_GLOW_SHOW');
+		if LAB then
+			LAB.eventFrame:UnregisterEvent('SPELL_ACTIVATION_OVERLAY_GLOW_SHOW');
+		end
+
+		if LBG then
+			LBG.ShowOverlayGlow = noFunction;
+		end
+	else
+		ActionBarActionEventsFrame:RegisterEvent('SPELL_ACTIVATION_OVERLAY_GLOW_SHOW');
+		if LAB then
+			LAB.eventFrame:RegisterEvent('SPELL_ACTIVATION_OVERLAY_GLOW_SHOW');
+		end
+
+		if LBG then
+			LBG.ShowOverlayGlow = origShow;
+		end
+	end
+end
+
+function MaxDps:Glow(button, id, r, g, b, texture)
+	if button.MaxDpsOverlays and button.MaxDpsOverlays[id] then
+		button.MaxDpsOverlays[id]:Show();
+	else
+		if not button.MaxDpsOverlays then
+			button.MaxDpsOverlays = {};
+		end
+
+		button.MaxDpsOverlays[id] = self:CreateOverlay(button, id, texture, r, g, b);
+		button.MaxDpsOverlays[id]:Show();
+	end
+end
+
+function MaxDps:HideGlow(button, id)
+	if button.MaxDpsOverlays and button.MaxDpsOverlays[id] then
+		button.MaxDpsOverlays[id]:Hide();
+	end
+end
+
+function MaxDps:Fetch()
+	self = MaxDps;
+	if self.rotationEnabled then
+		self:DisableRotationTimer();
+	end
+	self.Spell = nil;
+
+	self:GlowClear();
+	self.Spells = {};
+	self.Flags = {};
+	self.SpellsGlowing = {};
+	local isBartender = IsAddOnLoaded('Bartender4');
+	local isElv = IsAddOnLoaded('ElvUI');
+	local isSv = IsAddOnLoaded('SVUI_ActionBars');
+
+	if (isBartender) then
+		self:FetchBartender4();
+	elseif (isElv) then
+		self:FetchElvUI();
+	elseif (isSv) then
+		self:FetchSuperVillain();
+	else
+		self:FetchBlizzard();
+	end
+
+	-- It does not alter original button frames so it needs to be fetched too
+	if IsAddOnLoaded('ButtonForge') then
+		self:FetchButtonForge();
+	end
+
+	if self.rotationEnabled then
+		self:EnableRotationTimer();
+		self:InvokeNextSpell();
+	end
+end
+
+function MaxDps:FetchBlizzard()
+	local TDActionBarsBlizzard = {'Action', 'MultiBarBottomLeft', 'MultiBarBottomRight', 'MultiBarRight', 'MultiBarLeft'};
+	for _, barName in pairs(TDActionBarsBlizzard) do
+		for i = 1, 12 do
+			local button = _G[barName .. 'Button' .. i];
+			local slot = ActionButton_GetPagedID(button) or ActionButton_CalculateAction(button) or button:GetAttribute('action') or 0;
+			if HasAction(slot) then
+				local actionName, _;
+				local actionType, id = GetActionInfo(slot);
+				if actionType == 'macro' then _, _ , id = GetMacroSpell(id) end
+				if actionType == 'item' then
+					actionName = GetItemInfo(id);
+				elseif actionType == 'spell' or (actionType == 'macro' and id) then
+					actionName = GetSpellInfo(id);
+				end
+				if actionName then
+					if self.Spells[actionName] == nil then
+						self.Spells[actionName] = {};
+					end
+
+					tinsert(self.Spells[actionName], button);
+				end
+			end
+		end
+	end
+end
+
+function MaxDps:FetchButtonForge()
+	local i = 1;
+	while true do
+		local button = _G['ButtonForge' .. i];
+		if not button then
+			break;
+		end
+		i = i + 1;
+
+		local type = button:GetAttribute('type');
+		if type then
+			local actionType = button:GetAttribute(type);
+			local id;
+			local actionName;
+			if type == 'macro' then
+				local id = GetMacroSpell(actionType);
+				if id then
+					actionName = GetSpellInfo(id);
+				end
+			elseif type == 'item' then
+				actionName = GetItemInfo(actionType);
+			elseif type == 'spell' then
+				actionName = GetSpellInfo(actionType);
+			end
+			if actionName then
+				if self.Spells[actionName] == nil then
+					self.Spells[actionName] = {};
+				end
+
+				tinsert(self.Spells[actionName], button);
+			end
+		end
+	end
+end
+
+function MaxDps:FetchElvUI()
+	local ret = false;
+	for x = 1, 10 do
+		for i = 1, 12 do
+			local button = _G['ElvUI_Bar' .. x .. 'Button' .. i];
+			if button then
+				local spellId = button:GetSpellId();
+				if spellId then
+					local actionName, _ = GetSpellInfo(spellId);
+					if actionName then
+						if self.Spells[actionName] == nil then
+							self.Spells[actionName] = {};
+						end
+						ret = true;
+						tinsert(self.Spells[actionName], button);
+					end
+				end
+			end
+		end
+	end
+	return ret;
+end
+
+function MaxDps:FetchSuperVillain()
+	local ret = false;
+	for x = 1, 10 do
+		for i = 1, 12 do
+			local button = _G['SVUI_ActionBar' .. x .. 'Button' .. i];
+			if button then
+				local spellId = button:GetSpellId();
+				if spellId then
+					local actionName, _ = GetSpellInfo(spellId);
+					if actionName then
+						if self.Spells[actionName] == nil then
+							self.Spells[actionName] = {};
+						end
+						ret = true;
+						tinsert(self.Spells[actionName], button);
+					end
+				end
+			end
+		end
+	end
+	return ret;
+end
+
+function MaxDps:FetchBartender4()
+	local ret = false;
+	for i = 1, 120 do
+		local button = _G['BT4Button' .. i];
+		if button then
+			local spellId = button:GetSpellId();
+			if spellId then
+				local actionName, _ = GetSpellInfo(spellId);
+				if actionName then
+					if self.Spells[actionName] == nil then
+						self.Spells[actionName] = {};
+					end
+					ret = true;
+					tinsert(self.Spells[actionName], button);
+				end
+			end
+		end
+	end
+	return ret;
+end
+
+function MaxDps:Dump()
+	local s = '';
+	for k, v in pairs(self.Spells) do
+		s = s .. ', ' .. k;
+	end
+	print(s);
+end
+
+function MaxDps:FindSpell(spellName)
+	local name = GetSpellInfo(spellName) or spellName;
+	return self.Spells[name];
+end
+
+function MaxDps:GlowIndependent(spellName, id, r, g, b, texture)
+	local name = GetSpellInfo(spellName) or spellName;
+	if self.Spells[name] ~= nil then
+		for k, button in pairs(self.Spells[name]) do
+			self:Glow(button, id, r, g, b, texture);
+		end
+	end
+end
+
+function MaxDps:ClearGlowIndependent(spellName, id)
+	local name = GetSpellInfo(spellName) or spellName;
+	if self.Spells[name] ~= nil then
+		for k, button in pairs(self.Spells[name]) do
+			self:HideGlow(button, id);
+		end
+	end
+end
+
+function MaxDps:GlowCooldown(spell, condition)
+	if self.Flags[spell] == nil then
+		self.Flags[spell] = false;
+	end
+	if condition and not self.Flags[spell] then
+		self.Flags[spell] = true;
+		self:GlowIndependent(spell, spell, 0, 1, 0);
+	end
+	if not condition and self.Flags[spell] then
+		self.Flags[spell] = false;
+		self:ClearGlowIndependent(spell, spell);
+	end
+end
+
+function MaxDps:GlowSpell(spellName)
+	if self.Spells[spellName] ~= nil then
+		for k, button in pairs(self.Spells[spellName]) do
+			self:Glow(button, 'next');
+		end
+		self.SpellsGlowing[spellName] = 1;
+	else
+		self:Print(self.Colors.Error .. 'Spell not found on action bars: ' .. spellName);
+	end
+end
+
+function MaxDps:GlowSpellId(spellId)
+	local name = GetSpellInfo(spellId);
+	self:GlowSpell(name);
+end
+
+function MaxDps:GlowNextSpell(spellName)
+	self:GlowClear();
+	self:GlowSpell(spellName);
+end
+
+function MaxDps:GlowNextSpellId(spellId)
+	local spellName = GetSpellInfo(spellId);
+	self:GlowClear();
+	self:GlowSpell(spellName);
+end
+
+function MaxDps:GlowClear()
+	for spellName, v in pairs(self.SpellsGlowing) do
+		if v == 1 then
+			for k, button in pairs(self.Spells[spellName]) do
+				self:HideGlow(button, 'next');
+			end
+			self.SpellsGlowing[spellName] = 0;
+		end
+	end
+end
\ No newline at end of file
diff --git a/core.lua b/core.lua
new file mode 100644
index 0000000..e86b331
--- /dev/null
+++ b/core.lua
@@ -0,0 +1,329 @@
+local AceGUI = LibStub('AceGUI-3.0');
+local lsm = LibStub('AceGUISharedMediaWidgets-1.0');
+local media = LibStub('LibSharedMedia-3.0');
+
+MaxDps = LibStub('AceAddon-3.0'):NewAddon('MaxDps', 'AceConsole-3.0', 'AceEvent-3.0', 'AceTimer-3.0');
+
+MaxDps.Textures = {
+	['Ping'] = 'Interface\\Cooldown\\ping4',
+	['Star'] = 'Interface\\Cooldown\\star4',
+	['Starburst'] = 'Interface\\Cooldown\\starburst',
+};
+MaxDps.FinalTexture = nil;
+
+MaxDps.Colors = {
+	Info = '|cFF1394CC',
+	Error = '|cFFF0563D',
+	Success = '|cFFBCCF02',
+}
+
+MaxDps.Classes = {
+	[1] = 'Warrior',
+	[2] = 'Paladin',
+	[3] = 'Hunter',
+	[4] = 'Rogue',
+	[5] = 'Priest',
+	[6] = 'DeathKnight',
+	[7] = 'Shaman',
+	[8] = 'Mage',
+	[9] = 'Warlock',
+	[10] = 'Monk',
+	[11] = 'Druid',
+	[12] = 'DemonHunter',
+}
+
+local defaultOptions = {
+	global = {
+		enabled = true,
+		disabledInfo = false,
+		debugMode = false,
+		disableButtonGlow = false,
+		onCombatEnter = true,
+		texture = '',
+		customTexture = '',
+		highlightColor = {
+			r = 1, g = 1, b = 1, a = 1
+		},
+		interval = 0.15
+	}
+}
+
+local options = {
+	type = 'group',
+	name = 'MaxDps Options',
+	inline = false,
+	args = {
+		enable = {
+			name = 'Enable',
+			desc = 'Enables / disables the addon',
+			type = 'toggle',
+			width = 'full',
+			set = function(info, val)
+				MaxDps.db.global.enabled = val;
+			end,
+			get = function(info) return MaxDps.db.global.enabled end
+		},
+		disabledInfo = {
+			name = 'Disable info messages',
+			desc = 'Enables / disables info messages, if you have issues with addon, make sure to deselect this.',
+			type = 'toggle',
+			width = 'full',
+			set = function(info, val)
+				MaxDps.db.global.disabledInfo = val;
+			end,
+			get = function(info) return MaxDps.db.global.disabledInfo end
+		},
+		debugMode = {
+			name = 'Enable debug mode',
+			desc = 'Enables spammy chat messages (use this when addon does not work for you)',
+			type = 'toggle',
+			width = 'full',
+			set = function(info, val)
+				MaxDps.db.global.debugMode = val;
+			end,
+			get = function(info) return MaxDps.db.global.debugMode end
+		},
+		disableButtonGlow = {
+			name = 'Dissable blizzard button glow (experimental)',
+			desc = 'Disables original blizzard button glow',
+			type = 'toggle',
+			width = 'full',
+			set = function(info, val)
+				MaxDps.db.global.disableButtonGlow = val;
+				MaxDps:UpdateButtonGlow();
+			end,
+			get = function(info) return MaxDps.db.global.disableButtonGlow end
+		},
+		onCombatEnter = {
+			name = 'Enable upon entering combat',
+			desc = 'Automatically enables helper upon entering combat',
+			type = 'toggle',
+			width = 'full',
+			set = function(info, val)
+				MaxDps.db.global.onCombatEnter = val;
+			end,
+			get = function(info) return MaxDps.db.global.onCombatEnter end
+		},
+		customTexture = {
+			name = 'Custom Texture',
+			desc = 'Sets Highlight texture, has priority over selected one (changing this requires UI Reload)',
+			type = 'input',
+			set = function(info, val) MaxDps.db.global.customTexture = strtrim(val or ''); end,
+			get = function(info) return strtrim(MaxDps.db.global.customTexture or '') end
+		},
+		texture = {
+			type = 'select',
+			dialogControl = 'LSM30_Background',
+			name = 'Texture',
+			desc = 'Sets Highlight texture (changing this requires UI Reload)',
+			values = function()
+				return MaxDps.Textures;
+			end,
+			get = function()
+				return MaxDps.db.global.texture;
+			end,
+			set = function(self, val)
+				MaxDps.db.global.texture = val;
+			end,
+		},
+		highlightColor = {
+			name = 'Highlight color',
+			desc = 'Sets Highlight color',
+			type = 'color',
+			set = function(info, r, g, b, a)
+				MaxDps.db.global.highlightColor.r = r;
+				MaxDps.db.global.highlightColor.g = g;
+				MaxDps.db.global.highlightColor.b = b;
+				MaxDps.db.global.highlightColor.a = a;
+			end,
+			get = function(info)
+				return MaxDps.db.global.highlightColor.r, MaxDps.db.global.highlightColor.g, MaxDps.db.global.highlightColor.b, MaxDps.db.global.highlightColor.a;
+			end,
+			hasAlpha = true
+		},
+		interval = {
+			name = 'Interval in seconds',
+			desc = 'Sets how frequent rotation updates will be. Low value will result in fps drops.',
+			type = 'range',
+			min = 0.01,
+			max = 2,
+			set = function(info,val) MaxDps.db.global.interval = val end,
+			get = function(info) return MaxDps.db.global.interval end
+		},
+	},
+}
+
+function MaxDps:GetTexture()
+	if self.db.global.customTexture ~= '' and self.db.global.customTexture ~= nil then
+		self.FinalTexture = self.db.global.customTexture;
+		return self.FinalTexture;
+	end
+
+	self.FinalTexture = self.Textures[self.db.global.texture];
+	if self.FinalTexture == '' or self.FinalTexture == nil then
+		self.FinalTexture = 'Interface\\Cooldown\\ping4';
+	end
+
+	return self.FinalTexture;
+end
+
+function MaxDps:OnInitialize()
+	LibStub('AceConfig-3.0'):RegisterOptionsTable('MaxDps', options, {'/maxdps'});
+	self.db = LibStub('AceDB-3.0'):New('MaxDpsOptions', defaultOptions);
+	self.optionsFrame = LibStub('AceConfigDialog-3.0'):AddToBlizOptions('MaxDps', 'MaxDps');
+end
+
+function MaxDps:EnableRotation()
+	self:Print(self.Colors.Info .. 'Enabling');
+
+	if self.NextSpell == nil or self.rotationEnabled then
+		self:Print(self.Colors.Error .. 'Failed to enable addon!');
+		return;
+	end
+	self:Print(self.Colors.Info .. 'Fetching');
+	self.Fetch();
+
+	if self.ModuleOnEnable then
+		self.ModuleOnEnable();
+	end
+
+	self:EnableRotationTimer();
+
+	self.rotationEnabled = true;
+	self:Print(self.Colors.Success .. 'Enabled');
+end
+
+function MaxDps:EnableRotationTimer()
+	self.RotationTimer = self:ScheduleRepeatingTimer('InvokeNextSpell', self.db.global.interval);
+end
+
+function MaxDps:DisableRotation()
+	if not self.rotationEnabled then
+		return;
+	end
+
+	self:DisableRotationTimer();
+
+	self:DestroyAllOverlays();
+	self:Print(self.Colors.Info .. 'Disabling');
+
+	self.Spell = nil;
+	self.rotationEnabled = false;
+end
+
+function MaxDps:DisableRotationTimer()
+	if self.RotationTimer then
+		self:CancelTimer(self.RotationTimer);
+	end
+end
+
+function MaxDps:OnEnable()
+	self:RegisterEvent('PLAYER_TARGET_CHANGED');
+	self:RegisterEvent('PLAYER_TALENT_UPDATE');
+	self:RegisterEvent('ACTIONBAR_SLOT_CHANGED');
+	self:RegisterEvent('PLAYER_REGEN_DISABLED');
+	self:RegisterEvent('PLAYER_ENTERING_WORLD');
+
+	self:RegisterEvent('ACTIONBAR_HIDEGRID');
+	self:RegisterEvent('ACTIONBAR_PAGE_CHANGED');
+	self:RegisterEvent('LEARNED_SPELL_IN_TAB');
+	self:RegisterEvent('CHARACTER_POINTS_CHANGED');
+	self:RegisterEvent('ACTIVE_TALENT_GROUP_CHANGED');
+	self:RegisterEvent('PLAYER_SPECIALIZATION_CHANGED');
+	self:RegisterEvent('UPDATE_MACROS');
+	self:RegisterEvent('VEHICLE_UPDATE');
+	--	self:RegisterEvent('PLAYER_REGEN_ENABLED');
+
+	self:Print(self.Colors.Info .. 'Initialized');
+end
+
+function MaxDps:PLAYER_TALENT_UPDATE()
+	self:DisableRotation();
+end
+
+function MaxDps:PLAYER_ENTERING_WORLD()
+	self:UpdateButtonGlow();
+end
+
+function MaxDps:PLAYER_TARGET_CHANGED()
+	if self.rotationEnabled then
+		if (UnitIsFriend('player', 'target')) then
+			return;
+		else
+			self:InvokeNextSpell();
+		end
+	end
+end
+
+function MaxDps:PLAYER_REGEN_DISABLED()
+	if self.db.global.onCombatEnter and not self.rotationEnabled then
+		self:Print(self.Colors.Success .. 'Auto enable on combat!');
+		self:LoadModule();
+		self:EnableRotation();
+	end
+end
+
+function MaxDps:ButtonFetch()
+	if self.rotationEnabled then
+		if self.fetchTimer then
+			self:CancelTimer(self.fetchTimer);
+		end
+		self.fetchTimer = self:ScheduleTimer('Fetch', 0.5);
+	end
+end
+
+MaxDps.ACTIONBAR_SLOT_CHANGED = MaxDps.ButtonFetch;
+MaxDps.ACTIONBAR_HIDEGRID = MaxDps.ButtonFetch;
+MaxDps.ACTIONBAR_PAGE_CHANGED = MaxDps.ButtonFetch;
+MaxDps.LEARNED_SPELL_IN_TAB = MaxDps.ButtonFetch;
+MaxDps.CHARACTER_POINTS_CHANGED = MaxDps.ButtonFetch;
+MaxDps.ACTIVE_TALENT_GROUP_CHANGED = MaxDps.ButtonFetch;
+MaxDps.PLAYER_SPECIALIZATION_CHANGED = MaxDps.ButtonFetch;
+MaxDps.UPDATE_MACROS = MaxDps.ButtonFetch;
+MaxDps.VEHICLE_UPDATE = MaxDps.ButtonFetch;
+
+function MaxDps:InvokeNextSpell()
+	-- invoke spell check
+	local oldSkill = self.Spell;
+
+	self.Spell = self:NextSpell();
+
+	if (oldSkill ~= self.Spell or oldSkill == nil) and self.Spell ~= nil then
+		self:GlowNextSpellId(self.Spell);
+	end
+	if self.Spell == nil and oldSkill ~= nil then
+		self:GlowClear();
+	end
+end
+
+function MaxDps:LoadModule()
+	if self.ModuleLoaded then
+		return;
+	end
+
+	self:Print(self.Colors.Info .. 'Loading class module');
+	local _, _, classId = UnitClass('player');
+	if self.Classes[classId] == nil then
+		self:Print(_tdError, 'Invalid player class, please contact author of addon.');
+		return;
+	end
+
+	local module = 'MaxDps_' .. self.Classes[classId];
+
+	if not IsAddOnLoaded(module) then
+		LoadAddOn(module);
+	end
+
+	if not IsAddOnLoaded(module) then
+		self:Print(self.Colors.Error .. 'Could not find class module.');
+		return;
+	end
+
+	local mode = GetSpecialization();
+
+	self:EnableRotationModule(mode);
+	self:Print(self.Colors.Info .. self.Description);
+
+	self:Print(self.Colors.Info .. 'Finished Loading class module');
+	self.ModuleLoaded = true;
+end
\ No newline at end of file
diff --git a/helper.lua b/helper.lua
new file mode 100644
index 0000000..30ddfc8
--- /dev/null
+++ b/helper.lua
@@ -0,0 +1,219 @@
+
+-- Global cooldown spell id
+_GlobalCooldown		= 61304;
+
+-- Bloodlust effects
+_Bloodlust			= 2825;
+_TimeWrap			= 80353;
+_Heroism			= 32182;
+_AncientHysteria	= 90355;
+_Netherwinds		= 160452;
+_DrumsOfFury		= 178207;
+_Exhaustion			= 57723;
+
+local INF = 2147483647;
+
+local _Bloodlusts = {_Bloodlust, _TimeWrap, _Heroism, _AncientHysteria, _Netherwinds, _DrumsOfFury};
+
+function MaxDps:SpecName()
+	local currentSpec = GetSpecialization();
+	local currentSpecName = currentSpec and select(2, GetSpecializationInfo(currentSpec)) or 'None';
+	return currentSpecName;
+end
+
+function MaxDps:TalentEnabled(talent)
+	local found = false;
+	for i=1,7 do
+		for j=1,3 do
+			local id, n, x, sel = GetTalentInfo(i,j,GetActiveSpecGroup());
+			if (id == talent or n == talent) and sel then
+				found = true;
+			end
+		end
+	end
+	return found;
+end
+
+function MaxDps:PersistentAura(name)
+	local spellName = GetSpellInfo(name);
+	local aura, _, _, count = UnitAura('player', spellName);
+	if aura then
+		return true, count;
+	end
+	return false, 0;
+end
+
+function MaxDps:Aura(name, timeShift)
+	timeShift = timeShift or 0.2;
+	local spellName = GetSpellInfo(name);
+	local _, _, _, count, _, _, expirationTime = UnitAura('player', spellName);
+	local time = GetTime();
+	if expirationTime ~= nil and (expirationTime - time) > timeShift then
+		return true, count, (expirationTime - time);
+	end
+	return false, 0, 0;
+end
+
+function MaxDps:UnitAura(name, timeShift, unit)
+	timeShift = timeShift or 0.2;
+	local spellName = GetSpellInfo(name);
+	local _, _, _, count, _, _, expirationTime = UnitAura(unit, spellName);
+	if expirationTime ~= nil and (expirationTime - GetTime()) > timeShift then
+		return true, count;
+	end
+	return false, 0;
+end
+
+function MaxDps:TargetAura(name, timeShift)
+	timeShift = timeShift or 0;
+	local spellName = GetSpellInfo(name) or name;
+	local _, _, _, _, _, _, expirationTime = UnitAura('target', spellName, nil, 'PLAYER|HARMFUL');
+	if expirationTime ~= nil and (expirationTime - GetTime()) > timeShift then
+		local cd = expirationTime - GetTime() - (timeShift or 0);
+		return true, cd;
+	end
+	return false, 0;
+end
+
+function MaxDps:EndCast(target)
+	local t = GetTime();
+	local c = t * 1000;
+	local spell, _, _, _, _, endTime = UnitCastingInfo(target or 'player');
+	local gstart, gduration = GetSpellCooldown(_GlobalCooldown);
+	local gcd = gduration - (t - gstart);
+	if gcd < 0 then gcd = 0; end;
+	if endTime == nil then
+		return gcd, '', gcd;
+	end
+	local timeShift = (endTime - c) / 1000;
+	if gcd > timeShift then
+		timeShift = gcd;
+	end
+	return timeShift, spell, gcd;
+end
+
+function MaxDps:TargetPercentHealth()
+	local health = UnitHealth('target');
+	if health <= 0 then
+		return 0;
+	end;
+	local healthMax = UnitHealthMax('target');
+	if healthMax <= 0 then
+		return 0;
+	end;
+	return health/healthMax;
+end
+
+function MaxDps:GlobalCooldown()
+	local haste = UnitSpellHaste('player');
+	local gcd = 1.5 / ((haste / 100) + 1);
+	if gcd < 1 then
+		gcd = 1;
+	end
+	return gcd;
+end
+
+function MaxDps:SpellCharges(spell, timeShift)
+	local currentCharges, maxCharges, cooldownStart, cooldownDuration = GetSpellCharges(spell);
+	if currentCharges == nil then
+		local cd = MaxDps:Cooldown(spell, timeShift);
+		if cd <= 0 then
+			return 0, 1, 0;
+		else
+			return cd, 0, 1;
+		end
+	end
+	local cd = cooldownDuration - (GetTime() - cooldownStart) - (timeShift or 0);
+	if cd > cooldownDuration then
+		cd = 0;
+	end
+	return cd, currentCharges, maxCharges;
+end
+
+function MaxDps:SpellAvailable(spell, timeShift)
+	local cd = MaxDps:Cooldown(spell, timeShift);
+	return cd <= 0, cd;
+end
+
+function MaxDps:ExtractTooltip(spell, pattern)
+	local _pattern = gsub(pattern, "%%s", "([%%d%.,]+)");
+
+	if not TDSpellTooltip then
+		CreateFrame('GameTooltip', 'TDSpellTooltip', UIParent, 'GameTooltipTemplate');
+		TDSpellTooltip:SetOwner(UIParent, "ANCHOR_NONE")
+	end
+	TDSpellTooltip:SetSpellByID(spell);
+
+	for i = 2, 4 do
+		local line = _G['TDSpellTooltipTextLeft' .. i];
+		local text = line:GetText();
+
+		if text then
+			local cost = strmatch(text, _pattern);
+			if cost then
+				cost = cost and tonumber((gsub(cost, "%D", "")));
+				return cost;
+			end
+		end
+	end
+
+	return 0;
+end
+
+function MaxDps:Cooldown(spell, timeShift)
+	local start, duration, enabled = GetSpellCooldown(spell);
+	if enabled and duration == 0 and start == 0 then
+		return 0;
+	elseif enabled then
+		return (duration - (GetTime() - start) - (timeShift or 0));
+	else
+		return 100000;
+	end;
+end
+
+function MaxDps:TimeToDie(health)
+	local unit = UnitGUID('target');
+	if unit ~= TDDps_TargetGuid then
+		return INF;
+	end
+
+	health = health or UnitHealth('target');
+
+	if health == UnitHealthMax('target') then
+		TD_Hp0, TD_T0, TD_Hpm, TD_Tm = nil, nil, nil, nil;
+		return INF;
+	end
+
+	local time = GetTime();
+
+	if not TD_Hp0 then
+		TD_Hp0, TD_T0 = health, time;
+		TD_Hpm, TD_Tm = health, time;
+		--print('phial3');
+		return INF;
+	end
+
+	TD_Hpm = (TD_Hpm + health) * .5;
+	TD_Tm = (TD_Tm + time) * .5;
+
+	if TD_Hpm >= TD_Hp0 then
+		TD_Hp0, TD_T0, TD_Hpm, TD_Tm = nil, nil, nil, nil;
+	else
+		return health * (TD_T0 - TD_Tm) / (TD_Hpm - TD_Hp0);
+	end
+end
+
+function MaxDps:Mana(minus, timeShift)
+	local _, casting = GetManaRegen();
+	local mana = UnitPower('player', 0) - minus + (casting * timeShift);
+	return mana / UnitPowerMax('player', 0), mana;
+end
+
+function MaxDps:Bloodlust(timeShift)
+	-- @TODO: detect exhausted/seated debuff instead of 6 auras
+	for k, v in pairs (_Bloodlusts) do
+		if MaxDps:Aura(v, timeShift or 0) then return true; end
+	end
+
+	return false;
+end