changeset 30:0d95ce7a9ec2

- added Ace3 externs - converted ReAction_ConfigUI to use blizzard interface addons panel via AceConfigDialog-3.0 - partially converted FuBar module to LibRock, deprecated it (going to remove it entirely later) - cleaned up a couple other tidbits
author Flick <flickerstreak@gmail.com>
date Wed, 02 Apr 2008 23:31:13 +0000
parents 9c89042bc328
children 27dde2743f43
files Bar.lua Bindings.xml ReAction.lua ReAction.toc lib/embeds.xml locale/enUS.lua modules/FuBar_ReActionFu/FuBar_ReActionFu.lua modules/FuBar_ReActionFu/FuBar_ReActionFu.xml modules/FuBar_ReActionFu/lib/AceAddon-2.0/AceAddon-2.0.lua modules/FuBar_ReActionFu/lib/AceAddon-2.0/AceAddon-2.0.toc modules/FuBar_ReActionFu/lib/AceEvent-2.0/AceEvent-2.0.lua modules/FuBar_ReActionFu/lib/AceEvent-2.0/AceEvent-2.0.toc modules/FuBar_ReActionFu/lib/AceOO-2.0/AceOO-2.0.lua modules/FuBar_ReActionFu/lib/AceOO-2.0/AceOO-2.0.toc modules/FuBar_ReActionFu/lib/Dewdrop-2.0/Dewdrop-2.0.lua modules/FuBar_ReActionFu/lib/LibFuBarPlugin-3.0/Changelog-LibFuBarPlugin-3.0-r63707.txt modules/FuBar_ReActionFu/lib/LibFuBarPlugin-3.0/LibFuBarPlugin-3.0.lua modules/FuBar_ReActionFu/lib/LibFuBarPlugin-3.0/LibFuBarPlugin-3.0.toc modules/FuBar_ReActionFu/lib/LibFuBarPlugin-3.0/lib.xml modules/FuBar_ReActionFu/lib/LibRock-1.0/Changelog-LibRock-1.0-r63317.txt modules/FuBar_ReActionFu/lib/LibRock-1.0/LibRock-1.0.lua modules/FuBar_ReActionFu/lib/LibRock-1.0/LibRock-1.0.toc modules/FuBar_ReActionFu/lib/LibRock-1.0/LibStub/LibStub.lua modules/FuBar_ReActionFu/lib/LibRock-1.0/lib.xml modules/FuBar_ReActionFu/lib/embeds.xml modules/ReAction_Action/ReAction_Action.lua modules/ReAction_ConfigUI/ReAction_ConfigUI.lua modules/ReAction_ConfigUI/ReAction_ConfigUI.xml modules/ReAction_ConfigUI/lib/embeds.xml modules/ReAction_HideBlizzard/ReAction_HideBlizzard.lua modules/modules.xml
diffstat 31 files changed, 9009 insertions(+), 7186 deletions(-) [+]
line wrap: on
line diff
--- a/Bar.lua	Wed Apr 02 18:22:02 2008 +0000
+++ b/Bar.lua	Wed Apr 02 23:31:13 2008 +0000
@@ -21,8 +21,8 @@
 
   local f = CreateFrame("Frame",nil,config.parent or UIParent,"SecureStateDriverTemplate")
   f:SetFrameStrata("MEDIUM")
-  config.width = config.width or 400
-  config.height = config.height or 80
+  config.width = config.width or 480
+  config.height = config.height or 40
   f:SetWidth(config.width)
   f:SetWidth(config.height)
 
--- a/Bindings.xml	Wed Apr 02 18:22:02 2008 +0000
+++ b/Bindings.xml	Wed Apr 02 23:31:13 2008 +0000
@@ -1,10 +1,10 @@
 <Bindings>
   <!-- general keybindings -->
   <Binding name="REACTION_TOGGLELOCK" header="REACTION">
-    ReActionAddOn:ToggleLocked()
+
   </Binding>
   <Binding name="REACTION_TOGGLEKEYBIND">
-    ReActionAddOn:ToggleKeybindMode()
+
   </Binding>
 
   <!-- dummy keybinds -->
--- a/ReAction.lua	Wed Apr 02 18:22:02 2008 +0000
+++ b/ReAction.lua	Wed Apr 02 23:31:13 2008 +0000
@@ -1,13 +1,17 @@
 -- ReAction.lua
 -- See modules/ReAction_ModuleTemplate for Module API listing
 -- See Bar.lua for Bar object listing
+local MAJOR_VERSION = GetAddOnMetadata("ReAction","Version")
 
 ------ LIBRARIES ------
 local L = LibStub("AceLocale-3.0"):GetLocale("ReAction")
 
 ------ CORE ------
-local ReAction = LibStub("AceAddon-3.0"):NewAddon( "ReAction" )
+local ReAction = LibStub("AceAddon-3.0"):NewAddon( "ReAction",
+  "AceConsole-3.0"
+)
 ReAction.revision = tonumber(("$Revision: 1 $"):match("%d+"))
+ReAction.version = MAJOR_VERSION
 ReAction.L = L
 
 ------ GLOBALS ------
@@ -25,7 +29,7 @@
 end
 
 ------ PRIVATE ------
-local SelectBar, DestroyBar, InitializeBars, TearDownBars, DeepCopy, SafeCall, CheckMethod
+local SelectBar, DestroyBar, InitializeBars, TearDownBars, DeepCopy, SafeCall, CheckMethod, SlashHandler
 do
   local pcall = pcall
   local geterrorhandler = geterrorhandler
@@ -106,6 +110,34 @@
       error("Invalid method")
     end
   end
+
+  local function CallOptsModule(method,...)
+   local m = ReAction:GetModule("ConfigUI",true)
+   if not m then
+     LoadAddOn("ReAction_ConfigUI")
+     m = ReAction:GetModule("ConfigUI")
+   end
+   if m and type(m) == "table" and type(m[method]) == "function" then
+     m[method](m,...)
+   else
+     ReAction:Print("Options module not found")
+   end
+  end
+
+  SlashHandler = function(option)
+    if option == "config" then
+      CallOptsModule("OpenConfig",true)
+    elseif option == "unlock" then
+      CallOptsModule("SetConfigMode",true)
+    elseif option == "lock" then
+      CallOptsModule("SetConfigMode",false)
+    else
+      ReAction:Print(ReAction.version)
+      ReAction:Print("/reaction config")
+      ReAction:Print("/reaction unlock")
+      ReAction:Print("/reaction lock")
+    end
+  end
 end
 
 
@@ -121,6 +153,9 @@
     -- default profile is character-specific
   )
   self.db.RegisterCallback(self,"OnProfileChanged")
+  self.callbacks = LibStub("CallbackHandler-1.0"):New(self)
+  self:RegisterChatCommand("reaction", SlashHandler)
+  self:RegisterChatCommand("rxn", SlashHandler)
 
   self.bars = {}
 end
@@ -193,6 +228,7 @@
   local bar = self.Bar:new( name, profile.bars[name] )  -- ReAction.Bar defined in Bar.lua
   self:CallMethodOnAllModules("ApplyToBar", bar)
   self.bars[name] = bar
+  self.callbacks:Fire("OnCreateBar", bar)
   return bar
 end
 
@@ -202,6 +238,7 @@
     DestroyBar(bar)
     self.db.profile.bars[name] = nil
     self:CallMethodOnAllModules("EraseBarConfig", name)
+    self.callbacks:Fire("OnEraseBar",name)
   end
 end
 
@@ -221,7 +258,40 @@
     local cfg = self.db.profile.bars
     cfg[newname], cfg[name] = cfg[name], nil
     self:CallMethodOnAllModules("RenameBarConfig", name, newname)
+    self.callbacks:Fire("OnRenameBar", name, newname)
   end
 end
 
+ReAction.options = {}
+-- See modules/ReAction_ConfigUI for valid options contexts.
+function ReAction:RegisterOptions(context, module, opts)
+  if module == nil or context == nil then
+    error("ReAction:RegisterOptions requires a module object and context ID")
+  end
+  if not self.options[context] then
+    self.options[context] = {}
+  end
+  self.options[context][module] = opts
+  self.callbacks:Fire("OnOptionsRegistered", context, module, opts)
+end
 
+function ReAction:GetOptions(context)
+  if context then
+    if not self.options[context] then
+      self.options[context] = { }
+    end
+    return self.options[context]
+  end
+end
+
+function ReAction:GetOptionContextList()
+  local c = {}
+  for k in self.options do
+    tinsert(c,k)
+  end
+  return c
+end
+
+function ReAction:RefreshOptions()
+  self.callbacks:Fire("OnOptionsRefreshed")
+end
--- a/ReAction.toc	Wed Apr 02 18:22:02 2008 +0000
+++ b/ReAction.toc	Wed Apr 02 23:31:13 2008 +0000
@@ -1,4 +1,4 @@
-## Interface: 20300
+## Interface: 20400
 ## Title: ReAction
 ## Notes: Action button layout and configuration
 ## DefaultState: enabled
--- a/lib/embeds.xml	Wed Apr 02 18:22:02 2008 +0000
+++ b/lib/embeds.xml	Wed Apr 02 23:31:13 2008 +0000
@@ -7,6 +7,8 @@
   <Include file="AceAddon-3.0\AceAddon-3.0.xml"/>
   <Include file="AceLocale-3.0\AceLocale-3.0.xml"/>
   <Include file="AceDB-3.0\AceDB-3.0.xml"/>
+  <Include file="AceDBOptions-3.0\AceDBOptions-3.0.xml"/>
+  <Include file="AceConsole-3.0\AceConsole-3.0.xml"/>
   <Include file="AceEvent-3.0\AceEvent-3.0.xml"/>
 
 </Ui>
--- a/locale/enUS.lua	Wed Apr 02 18:22:02 2008 +0000
+++ b/locale/enUS.lua	Wed Apr 02 23:31:13 2008 +0000
@@ -11,7 +11,7 @@
 L["ReAction: name already in use"] = true
 
 -- modules/ReAction_HideBlizzard
-L["Hide Default Action Bars"] = true
+L["Hide Blizzard Action Bars"] = true
 L["Hide the default main bar and extra action bars"] = true
 
 -- modules/ReAction_Action
@@ -19,6 +19,8 @@
 L["Create a new bar of standard action buttons"] = true
 
 -- modules/ReAction_ConfigUI
+L["Customizable replacement for Blizzard's Action Bars"] = true
+L["Action Bars"] = true
 L["Delete Bar"] = true
 L["Remove the bar from the current profile"] = true
 L["Rename Bar"] = true
--- a/modules/FuBar_ReActionFu/FuBar_ReActionFu.lua	Wed Apr 02 18:22:02 2008 +0000
+++ b/modules/FuBar_ReActionFu/FuBar_ReActionFu.lua	Wed Apr 02 23:31:13 2008 +0000
@@ -5,27 +5,28 @@
 
 -- local imports
 local ReAction = ReAction
-local L = ReAction.L
-local _G = _G
-local Tablet = AceLibrary("Tablet-2.0")
-local Dewdrop = AceLibrary("Dewdrop-2.0")
 
 -- module declaration
 local moduleID = "ReActionFu"
-local rmodule = ReAction:NewModule( moduleID )
-local module  = AceLibrary("AceAddon-2.0"):new("FuBarPlugin-2.0")
-rmodule.fubar = module
+local module = ReAction:NewModule( moduleID,
+  "FuBarPlugin-3.0"
+)
 
-module.hasIcon = "Interface\\Icons\\INV_Qiraj_JewelEncased"
-module.hasNoColor = true
-module.clickableTooltip = false
-module.cannotDetachTooltip = true
-module.hideMenuTitle = true
-module.independentProfile = true
-module.defaultPosition = "LEFT"
-module.defaultMinimapPosition = 240 -- degrees
+local fubarOptions = {
+  iconPath = "Interface\\Icons\\INV_Qiraj_JewelEncased",
+  hasNoColor = true,
+  tooltipType = "Tablet-2.0",
+  configType = "Dewdrop-2.0",
+  hasNoText = true,
+  defaultPosition = "LEFT",
+  defaultMinimapPosition = 240, -- degrees
+  clickableTooltip = false,
+  independentProfile = true
+  cannotDetachTooltip = true
+  hideMenuTitle = true
+}
 
-function rmodule:OnInitialize()
+function module:OnInitialize()
   self.db = ReAction.db:RegisterNamespace(moduleID,
     {
       profile = {
@@ -39,17 +40,35 @@
     self:Hide()
   end
 
+  self.OnMenuRequest = {
+    type = "group",
+    handler = ReAction,
+    args = {
+      -- include profile management by default
+      profile = LibStub("AceDBOptions-3.0"):GetOptionsTable(ReAction.db)
+    }
+  }
+
+  -- insert each module's registered global opts into the table top level
+  local opts = self.OnMenuRequest
+  for m, tbl in pairs(ReAction:GetOptions("global")) do
+    if tbl.args then
+      for k, v in pairs(tbl.args) do
+        opts.args[k] = v
+      end
+    end
+  end
+
+  -- listen for new module adds
+  ReAction.RegisterCallback(self, "OnOptionsRegistered")
+
+  -- configure FuBarPlugin
+  for k, v in pairs(fubarOptions) do
+    self:SetFuBarOption(k,v)
+  end
 end
 
-function rmodule:OnEnable()
-
-end
-
-function rmodule:OnDisable()
-
-end
-
-function rmodule:OnProfileChanged()
+function module:OnProfileChanged()
   if self.db.profile.requireFuBar == true then
     module:Hide()
   else
@@ -57,32 +76,23 @@
   end
 end
 
-function module:OnTooltipUpdate()
-
-end
-
-function module:OnClick(button)
-
-end
-
-function module:OnMenuRequest( level, value, inTooltip, valueN_1, valueN_2, valueN_3, valueN_4 )
-  if not self.aceOptionsTable then
-    local opts = { 
-      type = "group",
-      handler = ReAction,
-      args = {
-      }
-    }
-    Dewdrop:InjectAceOptionsTable(ReAction,opts)
-    self.aceOptionsTable = opts
-  end
-  for _, m in ReAction:IterateModules() do
-    if m and type(m.GetGlobalOptions) == "function" then
-      for k,v in pairs(m:GetGlobalOptions()) do
-        self.aceOptionsTable.args[k] = v
+function module:OnOptionsRegistered( evtName, context, module, opts )
+  if context == "global" then
+    if opts.args then
+      for k, v in pairs(tbl.args) do
+        opts.args[k] = v
       end
     end
   end
-  Dewdrop:FeedAceOptionsTable(self.aceOptionsTable)
 end
 
+function module:OnFuBarClick(button)
+  -- TODO:
+  -- alt, shift, ctrl clicks
+end
+
+function module:OnUpdateFuBarTooltip()
+  -- TODO:
+  -- display status of config mode, keybind mode, etc
+end
+
--- a/modules/FuBar_ReActionFu/FuBar_ReActionFu.xml	Wed Apr 02 18:22:02 2008 +0000
+++ b/modules/FuBar_ReActionFu/FuBar_ReActionFu.xml	Wed Apr 02 23:31:13 2008 +0000
@@ -2,14 +2,7 @@
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
     xsi:schemaLocation="http://www.blizzard.com/wow/ui/..\FrameXML\UI.xsd">
 
-  <Script file="lib\AceLibrary\AceLibrary.lua"/>
-  <Script file="lib\AceOO-2.0\AceOO-2.0.lua"/>
-  <Script file="lib\AceEvent-2.0\AceEvent-2.0.lua"/>
-  <Script file="lib\AceAddon-2.0\AceAddon-2.0.lua"/>
-  <Script file="lib\FuBarPlugin-2.0\FuBarPlugin-2.0.lua"/>
-  <Script file="lib\Tablet-2.0\Tablet-2.0.lua"/>
-  <Script file="lib\Dewdrop-2.0\Dewdrop-2.0.lua"/>
-
+  <Include file="lib\embeds.xml"/>
   <Script file="FuBar_ReActionFu.lua"/>
 
 </Ui>
\ No newline at end of file
--- a/modules/FuBar_ReActionFu/lib/AceAddon-2.0/AceAddon-2.0.lua	Wed Apr 02 18:22:02 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1394 +0,0 @@
---[[
-Name: AceAddon-2.0
-Revision: $Rev: 46764 $
-Developed by: The Ace Development Team (http://www.wowace.com/index.php/The_Ace_Development_Team)
-Inspired By: Ace 1.x by Turan (turan@gryphon.com)
-Website: http://www.wowace.com/
-Documentation: http://www.wowace.com/wiki/AceAddon-2.0
-SVN: http://svn.wowace.com/wowace/trunk/Ace2/AceAddon-2.0
-Description: Base for all Ace addons to inherit from.
-Dependencies: AceLibrary, AceOO-2.0, AceEvent-2.0, (optional) AceConsole-2.0
-License: LGPL v2.1
-]]
-
-local MAJOR_VERSION = "AceAddon-2.0"
-local MINOR_VERSION = "$Revision: 46764 $"
-
--- This ensures the code is only executed if the libary doesn't already exist, or is a newer version
-if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary.") end
-if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end
-
-if not AceLibrary:HasInstance("AceOO-2.0") then error(MAJOR_VERSION .. " requires AceOO-2.0.") end
-
-local function safecall(func,...)
-	local success, err = pcall(func,...)
-	if not success then geterrorhandler()(err:find("%.lua:%d+:") and err or (debugstack():match("\n(.-: )in.-\n") or "") .. err) end
-end
--- Localization
-local STANDBY, TITLE, NOTES, VERSION, AUTHOR, DATE, CATEGORY, EMAIL, CREDITS, WEBSITE, CATEGORIES, ABOUT, LICENSE, PRINT_ADDON_INFO, DONATE, DONATE_DESC, HOWTO_DONATE_WINDOWS, HOWTO_DONATE_MAC
-if GetLocale() == "deDE" then
-	STANDBY = "|cffff5050(Standby)|r" -- capitalized
-
-	TITLE = "Titel"
-	NOTES = "Anmerkung"
-	VERSION = "Version"
-	AUTHOR = "Autor"
-	DATE = "Datum"
-	CATEGORY = "Kategorie"
-	EMAIL = "E-Mail"
-	WEBSITE = "Webseite"
-	CREDITS = "Credits" -- fix
-	LICENSE = "License" -- fix
-
-	ABOUT = "Über"
-	PRINT_ADDON_INFO = "Gibt Addondaten aus"
-	DONATE = "Donate" -- fix
-	DONATE_DESC = "Give a much-needed donation to the author of this addon." -- fix
-	HOWTO_DONATE_WINDOWS = "Press Ctrl-A to select the link, then Ctrl-C to copy, then Alt-Tab out of the game, open your favorite web browser, and paste the link into the address bar." -- fix
-	HOWTO_DONATE_MAC = "Press Cmd-A to select the link, then Cmd-C to copy, then Cmd-Tab out of the game, open your favorite web browser, and paste the link into the address bar." -- fix
-
-	CATEGORIES = {
-		["Action Bars"] = "Aktionsleisten",
-		["Auction"] = "Auktion",
-		["Audio"] = "Audio",
-		["Battlegrounds/PvP"] = "Schlachtfeld/PvP",
-		["Buffs"] = "Stärkungszauber",
-		["Chat/Communication"] = "Chat/Kommunikation",
-		["Druid"] = "Druide",
-		["Hunter"] = "Jäger",
-		["Mage"] = "Magier",
-		["Paladin"] = "Paladin",
-		["Priest"] = "Priester",
-		["Rogue"] = "Schurke",
-		["Shaman"] = "Schamane",
-		["Warlock"] = "Hexenmeister",
-		["Warrior"] = "Krieger",
-		["Healer"] = "Heiler",
-		["Tank"] = "Tank",
-		["Caster"] = "Zauberer",
-		["Combat"] = "Kampf",
-		["Compilations"] = "Zusammenstellungen",
-		["Data Export"] = "Datenexport",
-		["Development Tools"] = "Entwicklungs Tools",
-		["Guild"] = "Gilde",
-		["Frame Modification"] = "Frame Veränderungen",
-		["Interface Enhancements"] = "Interface Verbesserungen",
-		["Inventory"] = "Inventar",
-		["Library"] = "Bibliotheken",
-		["Map"] = "Karte",
-		["Mail"] = "Post",
-		["Miscellaneous"] = "Diverses",
-		["Quest"] = "Quest",
-		["Raid"] = "Schlachtzug",
-		["Tradeskill"] = "Beruf",
-		["UnitFrame"] = "Einheiten-Fenster",
-	}
-elseif GetLocale() == "frFR" then
-	STANDBY = "|cffff5050(attente)|r"
-
-	TITLE = "Titre"
-	NOTES = "Notes"
-	VERSION = "Version"
-	AUTHOR = "Auteur"
-	DATE = "Date"
-	CATEGORY = "Catégorie"
-	EMAIL = "E-mail"
-	WEBSITE = "Site web"
-	CREDITS = "Credits" -- fix
-	LICENSE = "License" -- fix
-
-	ABOUT = "A propos"
-	PRINT_ADDON_INFO = "Afficher les informations sur l'addon"
-	DONATE = "Donate" -- fix
-	DONATE_DESC = "Give a much-needed donation to the author of this addon." -- fix
-	HOWTO_DONATE_WINDOWS = "Press Ctrl-A to select the link, then Ctrl-C to copy, then Alt-Tab out of the game, open your favorite web browser, and paste the link into the address bar." -- fix
-	HOWTO_DONATE_MAC = "Press Cmd-A to select the link, then Cmd-C to copy, then Cmd-Tab out of the game, open your favorite web browser, and paste the link into the address bar." -- fix
-
-	CATEGORIES = {
-		["Action Bars"] = "Barres d'action",
-		["Auction"] = "Hôtel des ventes",
-		["Audio"] = "Audio",
-		["Battlegrounds/PvP"] = "Champs de bataille/JcJ",
-		["Buffs"] = "Buffs",
-		["Chat/Communication"] = "Chat/Communication",
-		["Druid"] = "Druide",
-		["Hunter"] = "Chasseur",
-		["Mage"] = "Mage",
-		["Paladin"] = "Paladin",
-		["Priest"] = "Prêtre",
-		["Rogue"] = "Voleur",
-		["Shaman"] = "Chaman",
-		["Warlock"] = "Démoniste",
-		["Warrior"] = "Guerrier",
-		["Healer"] = "Soigneur",
-		["Tank"] = "Tank",
-		["Caster"] = "Casteur",
-		["Combat"] = "Combat",
-		["Compilations"] = "Compilations",
-		["Data Export"] = "Exportation de données",
-		["Development Tools"] = "Outils de développement",
-		["Guild"] = "Guilde",
-		["Frame Modification"] = "Modification des fenêtres",
-		["Interface Enhancements"] = "Améliorations de l'interface",
-		["Inventory"] = "Inventaire",
-		["Library"] = "Bibliothèques",
-		["Map"] = "Carte",
-		["Mail"] = "Courrier",
-		["Miscellaneous"] = "Divers",
-		["Quest"] = "Quêtes",
-		["Raid"] = "Raid",
-		["Tradeskill"] = "Métiers",
-		["UnitFrame"] = "Fenêtres d'unité",
-	}
-elseif GetLocale() == "koKR" then
-	STANDBY = "|cffff5050(사용가능)|r"
-
-	TITLE = "제목"
-	NOTES = "노트"
-	VERSION = "버전"
-	AUTHOR = "저작자"
-	DATE = "날짜"
-	CATEGORY = "분류"
-	EMAIL = "전자 우편"
-	WEBSITE = "웹 사이트"
-	CREDITS = "공로자"
-	LICENSE = "라이센스"
-
-	ABOUT = "정보"
-	PRINT_ADDON_INFO = "애드온에 대한 정보를 출력합니다."
-	DONATE = "기부"
-	DONATE_DESC = "이 애드온의 저작자에게 기부를 합니다."
-	HOWTO_DONATE_WINDOWS = "Ctrl-A를 눌려 링크를 선택후, Ctrl-C로 복사합니다. Alt-Tab 눌려 게임으로 부터 나간후 웹 브라우저를 엽니다. 복사된 링크를 주소 창에 붙여넣기 합니다."
-	HOWTO_DONATE_MAC = "Cmd-A를 눌려 링크를 선택후, Cmd-C로 복사합니다. Cmd-Tab 눌려 게임으로 부터 나간후 웹 브라우저를 엽니다. 복사된 링크를 주소 창에 붙여넣기 합니다."
-
-	CATEGORIES = {
-		["Action Bars"] = "액션바",
-		["Auction"] = "경매",
-		["Audio"] = "음향",
-		["Battlegrounds/PvP"] = "전장/PvP",
-		["Buffs"] = "버프",
-		["Chat/Communication"] = "대화/의사소통",
-		["Druid"] = "드루이드",
-		["Hunter"] = "사냥꾼",
-		["Mage"] = "마법사",
-		["Paladin"] = "성기사",
-		["Priest"] = "사제",
-		["Rogue"] = "도적",
-		["Shaman"] = "주술사",
-		["Warlock"] = "흑마법사",
-		["Warrior"] = "전사",
-		["Healer"] = "힐러",
-		["Tank"] = "탱커",
-		["Caster"] = "캐스터",
-		["Combat"] = "전투",
-		["Compilations"] = "복합",
-		["Data Export"] = "자료 출력",
-		["Development Tools"] = "개발 도구",
-		["Guild"] = "길드",
-		["Frame Modification"] = "구조 변경",
-		["Interface Enhancements"] = "인터페이스 강화",
-		["Inventory"] = "인벤토리",
-		["Library"] = "라이브러리",
-		["Map"] = "지도",
-		["Mail"] = "우편",
-		["Miscellaneous"] = "기타",
-		["Quest"] = "퀘스트",
-		["Raid"] = "공격대",
-		["Tradeskill"] = "전문기술",
-		["UnitFrame"] = "유닛 프레임",
-	}
-elseif GetLocale() == "zhTW" then
-	STANDBY = "|cffff5050(待命)|r"
-
-	TITLE = "標題"
-	NOTES = "註記"
-	VERSION = "版本"
-	AUTHOR = "作者"
-	DATE = "日期"
-	CATEGORY = "類別"
-	EMAIL = "電子郵件"
-	WEBSITE = "網站"
-	CREDITS = "特別感謝"
-	LICENSE = "版權"
-
-	ABOUT = "關於"
-	PRINT_ADDON_INFO = "顯示插件資訊。"
-	DONATE = "捐贈"
-	DONATE_DESC = "捐贈金錢給插件作者。"
-	HOWTO_DONATE_WINDOWS = "請按Ctrl-A選擇網站連結,Ctrl-C複製網址,Alt-Tab切換到電腦桌面,打開瀏覽器,在網址列貼上網址。"
-	HOWTO_DONATE_MAC = "請按Cmd-A選擇網站連結,Cmd-C複製網址,Cmd-Tab切換到電腦桌面,打開瀏覽器,在網址列貼上網址。"
-
-	CATEGORIES = {
-		["Action Bars"] = "動作條",
-		["Auction"] = "拍賣",
-		["Audio"] = "音效",
-		["Battlegrounds/PvP"] = "戰場/PvP",
-		["Buffs"] = "增益",
-		["Chat/Communication"] = "聊天/通訊",
-		["Druid"] = "德魯伊",
-		["Hunter"] = "獵人",
-		["Mage"] = "法師",
-		["Paladin"] = "聖騎士",
-		["Priest"] = "牧師",
-		["Rogue"] = "盜賊",
-		["Shaman"] = "薩滿",
-		["Warlock"] = "術士",
-		["Warrior"] = "戰士",
-		["Healer"] = "治療者",
-		["Tank"] = "坦克",
-		["Caster"] = "施法者",
-		["Combat"] = "戰鬥",
-		["Compilations"] = "整合",
-		["Data Export"] = "資料匯出",
-		["Development Tools"] = "開發工具",
-		["Guild"] = "公會",
-		["Frame Modification"] = "框架修改",
-		["Interface Enhancements"] = "介面增強",
-		["Inventory"] = "庫存",
-		["Library"] = "程式庫",
-		["Map"] = "地圖",
-		["Mail"] = "郵件",
-		["Miscellaneous"] = "雜項",
-		["Quest"] = "任務",
-		["Raid"] = "團隊",
-		["Tradeskill"] = "交易技能",
-		["UnitFrame"] = "單位框架",
-	}
-elseif GetLocale() == "zhCN" then
-	STANDBY = "|cffff5050(暂挂)|r"
-
-	TITLE = "标题"
-	NOTES = "附注"
-	VERSION = "版本"
-	AUTHOR = "作者"
-	DATE = "日期"
-	CATEGORY = "分类"
-	EMAIL = "电子邮件"
-	WEBSITE = "网站"
-	CREDITS = "Credits" -- fix
-	LICENSE = "License" -- fix
-
-	ABOUT = "关于"
-	PRINT_ADDON_INFO = "印列出插件信息"
-	DONATE = "Donate" -- fix
-	DONATE_DESC = "Give a much-needed donation to the author of this addon." -- fix
-	HOWTO_DONATE_WINDOWS = "Press Ctrl-A to select the link, then Ctrl-C to copy, then Alt-Tab out of the game, open your favorite web browser, and paste the link into the address bar." -- fix
-	HOWTO_DONATE_MAC = "Press Cmd-A to select the link, then Cmd-C to copy, then Cmd-Tab out of the game, open your favorite web browser, and paste the link into the address bar." -- fix
-
-	CATEGORIES = {
-		["Action Bars"] = "动作条",
-		["Auction"] = "拍卖",
-		["Audio"] = "音频",
-		["Battlegrounds/PvP"] = "战场/PvP",
-		["Buffs"] = "增益魔法",
-		["Chat/Communication"] = "聊天/交流",
-		["Druid"] = "德鲁伊",
-		["Hunter"] = "猎人",
-		["Mage"] = "法师",
-		["Paladin"] = "圣骑士",
-		["Priest"] = "牧师",
-		["Rogue"] = "盗贼",
-		["Shaman"] = "萨满祭司",
-		["Warlock"] = "术士",
-		["Warrior"] = "战士",
---		["Healer"] = "治疗保障",
---		["Tank"] = "近战控制",
---		["Caster"] = "远程输出",
-		["Combat"] = "战斗",
-		["Compilations"] = "编译",
-		["Data Export"] = "数据导出",
-		["Development Tools"] = "开发工具",
-		["Guild"] = "公会",
-		["Frame Modification"] = "框架修改",
-		["Interface Enhancements"] = "界面增强",
-		["Inventory"] = "背包",
-		["Library"] = "库",
-		["Map"] = "地图",
-		["Mail"] = "邮件",
-		["Miscellaneous"] = "杂项",
-		["Quest"] = "任务",
-		["Raid"] = "团队",
-		["Tradeskill"] = "商业技能",
-		["UnitFrame"] = "头像框架",
-	}
-elseif GetLocale() == "esES" then
-	STANDBY = "|cffff5050(espera)|r"
-
-	TITLE = "Título"
-	NOTES = "Notas"
-	VERSION = "Versión"
-	AUTHOR = "Autor"
-	DATE = "Fecha"
-	CATEGORY = "Categoría"
-	EMAIL = "E-mail"
-	WEBSITE = "Web"
-	CREDITS = "Créditos"
-	LICENSE = "License" -- fix
-
-	ABOUT = "Acerca de"
-	PRINT_ADDON_INFO = "Muestra información acerca del accesorio."
-	DONATE = "Donate" -- fix
-	DONATE_DESC = "Give a much-needed donation to the author of this addon." -- fix
-	HOWTO_DONATE_WINDOWS = "Press Ctrl-A to select the link, then Ctrl-C to copy, then Alt-Tab out of the game, open your favorite web browser, and paste the link into the address bar." -- fix
-	HOWTO_DONATE_MAC = "Press Cmd-A to select the link, then Cmd-C to copy, then Cmd-Tab out of the game, open your favorite web browser, and paste the link into the address bar." -- fix
-
-	CATEGORIES = {
-		["Action Bars"] = "Barras de Acción",
-		["Auction"] = "Subasta",
-		["Audio"] = "Audio",
-		["Battlegrounds/PvP"] = "Campos de Batalla/JcJ",
-		["Buffs"] = "Buffs",
-		["Chat/Communication"] = "Chat/Comunicación",
-		["Druid"] = "Druida",
-		["Hunter"] = "Cazador",
-		["Mage"] = "Mago",
-		["Paladin"] = "Paladín",
-		["Priest"] = "Sacerdote",
-		["Rogue"] = "Pícaro",
-		["Shaman"] = "Chamán",
-		["Warlock"] = "Brujo",
-		["Warrior"] = "Guerrero",
-		["Healer"] = "Sanador",
-		["Tank"] = "Tanque",
-		["Caster"] = "Conjurador",
-		["Combat"] = "Combate",
-		["Compilations"] = "Compilaciones",
-		["Data Export"] = "Exportar Datos",
-		["Development Tools"] = "Herramientas de Desarrollo",
-		["Guild"] = "Hermandad",
-		["Frame Modification"] = "Modificación de Marcos",
-		["Interface Enhancements"] = "Mejoras de la Interfaz",
-		["Inventory"] = "Inventario",
-		["Library"] = "Biblioteca",
-		["Map"] = "Mapa",
-		["Mail"] = "Correo",
-		["Miscellaneous"] = "Misceláneo",
-		["Quest"] = "Misión",
-		["Raid"] = "Banda",
-		["Tradeskill"] = "Habilidad de Comercio",
-		["UnitFrame"] = "Marco de Unidades",
-	}
-else -- enUS
-	STANDBY = "|cffff5050(standby)|r"
-
-	TITLE = "Title"
-	NOTES = "Notes"
-	VERSION = "Version"
-	AUTHOR = "Author"
-	DATE = "Date"
-	CATEGORY = "Category"
-	EMAIL = "E-mail"
-	WEBSITE = "Website"
-	CREDITS = "Credits"
-	LICENSE = "License"
-
-	ABOUT = "About"
-	PRINT_ADDON_INFO = "Show information about the addon."
-	DONATE = "Donate"
-	DONATE_DESC = "Give a much-needed donation to the author of this addon."
-	HOWTO_DONATE_WINDOWS = "Press Ctrl-A to select the link, then Ctrl-C to copy, then Alt-Tab out of the game, open your favorite web browser, and paste the link into the address bar."
-	HOWTO_DONATE_MAC = "Press Cmd-A to select the link, then Cmd-C to copy, then Cmd-Tab out of the game, open your favorite web browser, and paste the link into the address bar."
-
-	CATEGORIES = {
-		["Action Bars"] = "Action Bars",
-		["Auction"] = "Auction",
-		["Audio"] = "Audio",
-		["Battlegrounds/PvP"] = "Battlegrounds/PvP",
-		["Buffs"] = "Buffs",
-		["Chat/Communication"] = "Chat/Communication",
-		["Druid"] = "Druid",
-		["Hunter"] = "Hunter",
-		["Mage"] = "Mage",
-		["Paladin"] = "Paladin",
-		["Priest"] = "Priest",
-		["Rogue"] = "Rogue",
-		["Shaman"] = "Shaman",
-		["Warlock"] = "Warlock",
-		["Warrior"] = "Warrior",
-		["Healer"] = "Healer",
-		["Tank"] = "Tank",
-		["Caster"] = "Caster",
-		["Combat"] = "Combat",
-		["Compilations"] = "Compilations",
-		["Data Export"] = "Data Export",
-		["Development Tools"] = "Development Tools",
-		["Guild"] = "Guild",
-		["Frame Modification"] = "Frame Modification",
-		["Interface Enhancements"] = "Interface Enhancements",
-		["Inventory"] = "Inventory",
-		["Library"] = "Library",
-		["Map"] = "Map",
-		["Mail"] = "Mail",
-		["Miscellaneous"] = "Miscellaneous",
-		["Quest"] = "Quest",
-		["Raid"] = "Raid",
-		["Tradeskill"] = "Tradeskill",
-		["UnitFrame"] = "UnitFrame",
-	}
-end
-
-setmetatable(CATEGORIES, { __index = function(self, key) -- case-insensitive
-	local lowerKey = key:lower()
-	for k,v in pairs(CATEGORIES) do
-		if k:lower() == lowerKey then
-			return v
-		end
-	end
-end })
-
--- Create the library object
-
-local AceOO = AceLibrary("AceOO-2.0")
-local AceAddon = AceOO.Class()
-local AceEvent
-local AceConsole
-local AceModuleCore
-
-function AceAddon:GetLocalizedCategory(name)
-	self:argCheck(name, 2, "string")
-	return CATEGORIES[name] or UNKNOWN
-end
-
-function AceAddon:ToString()
-	return "AceAddon"
-end
-
-local function print(text)
-	DEFAULT_CHAT_FRAME:AddMessage(text)
-end
-
-function AceAddon:ADDON_LOADED(name)
-	local unregister = true
-	local initAddon = {}
-	while #self.nextAddon > 0 do
-		local addon = table.remove(self.nextAddon, 1)
-		if addon.possibleNames[name] then
-			table.insert(initAddon, addon)
-		else
-			unregister = nil
-			table.insert(self.skipAddon, addon)
-		end
-	end
-	self.nextAddon, self.skipAddon = self.skipAddon, self.nextAddon
-	if unregister then
-		AceAddon:UnregisterEvent("ADDON_LOADED")
-	end
-	while #initAddon > 0 do
-		local addon = table.remove(initAddon, 1)
-		table.insert(self.addons, addon)
-		if not self.addons[name] then
-			self.addons[name] = addon
-		end
-		addon.possibleNames = nil
-		self:InitializeAddon(addon, name)
-	end
-end
-
-local function RegisterOnEnable(self)
-	if DEFAULT_CHAT_FRAME and DEFAULT_CHAT_FRAME.defaultLanguage then -- HACK
-		AceAddon.playerLoginFired = true
-	end
-	if AceAddon.playerLoginFired then
-		AceAddon.addonsStarted[self] = true
-		if (type(self.IsActive) ~= "function" or self:IsActive()) and (not AceModuleCore or not AceModuleCore:IsModule(self) or AceModuleCore:IsModuleActive(self)) then
-			AceAddon:ManualEnable(self)
-		end
-	else
-		if not AceAddon.addonsToOnEnable then
-			AceAddon.addonsToOnEnable = {}
-		end
-		table.insert(AceAddon.addonsToOnEnable, self)
-	end
-end
-
-function AceAddon:InitializeAddon(addon, name)
-	if addon.name == nil then
-		addon.name = name
-	end
-	if GetAddOnMetadata then
-		-- TOC checks
-		if addon.title == nil then
-			addon.title = GetAddOnMetadata(name, "Title")
-		end
-		if type(addon.title) == "string" then
-			local num = addon.title:find(" |cff7fff7f %-Ace2%-|r$")
-			if num then
-				addon.title = addon.title:sub(1, num - 1)
-			end
-			addon.title = addon.title:trim()
-		end
-		if addon.notes == nil then
-			addon.notes = GetAddOnMetadata(name, "Notes")
-		end
-		if type(addon.notes) == "string" then
-			addon.notes = addon.notes:trim()
-		end
-		if addon.version == nil then
-			addon.version = GetAddOnMetadata(name, "Version")
-		end
-		if type(addon.version) == "string" then
-			if addon.version:find("%$Revision: (%d+) %$") then
-				addon.version = addon.version:gsub("%$Revision: (%d+) %$", "%1")
-			elseif addon.version:find("%$Rev: (%d+) %$") then
-				addon.version = addon.version:gsub("%$Rev: (%d+) %$", "%1")
-			elseif addon.version:find("%$LastChangedRevision: (%d+) %$") then
-				addon.version = addon.version:gsub("%$LastChangedRevision: (%d+) %$", "%1")
-			end
-			addon.version = addon.version:trim()
-		end
-		if addon.author == nil then
-			addon.author = GetAddOnMetadata(name, "Author")
-		end
-		if type(addon.author) == "string" then
-			addon.author = addon.author:trim()
-		end
-		if addon.credits == nil then
-			addon.credits = GetAddOnMetadata(name, "X-Credits")
-		end
-		if type(addon.credits) == "string" then
-			addon.credits = addon.credits:trim()
-		end
-		if addon.donate == nil then
-			addon.donate = GetAddOnMetadata(name, "X-Donate")
-		end
-		if type(addon.donate) == "string" then
-			addon.donate = addon.donate:trim()
-		end
-		if addon.date == nil then
-			addon.date = GetAddOnMetadata(name, "X-Date") or GetAddOnMetadata(name, "X-ReleaseDate")
-		end
-		if type(addon.date) == "string" then
-			if addon.date:find("%$Date: (.-) %$") then
-				addon.date = addon.date:gsub("%$Date: (.-) %$", "%1")
-			elseif addon.date:find("%$LastChangedDate: (.-) %$") then
-				addon.date = addon.date:gsub("%$LastChangedDate: (.-) %$", "%1")
-			end
-			addon.date = addon.date:trim()
-		end
-
-		if addon.category == nil then
-			addon.category = GetAddOnMetadata(name, "X-Category")
-		end
-		if type(addon.category) == "string" then
-			addon.category = addon.category:trim()
-		end
-		if addon.email == nil then
-			addon.email = GetAddOnMetadata(name, "X-eMail") or GetAddOnMetadata(name, "X-Email")
-		end
-		if type(addon.email) == "string" then
-			addon.email = addon.email:trim()
-		end
-		if addon.license == nil then
-			addon.license = GetAddOnMetadata(name, "X-License")
-		end
-		if type(addon.license) == "string" then
-			addon.license = addon.license:trim()
-		end
-		if addon.website == nil then
-			addon.website = GetAddOnMetadata(name, "X-Website")
-		end
-		if type(addon.website) == "string" then
-			addon.website = addon.website:trim()
-		end
-	end
-	local current = addon.class
-	while true do
-		if current == AceOO.Class or not current then
-			break
-		end
-		if current.mixins then
-			for mixin in pairs(current.mixins) do
-				if type(mixin.OnEmbedInitialize) == "function" then
-					mixin:OnEmbedInitialize(addon, name)
-				end
-			end
-		end
-		current = current.super
-	end
-	local n = AceAddon.addonsToOnEnable and #AceAddon.addonsToOnEnable or 0
-
-	if type(addon.OnInitialize) == "function" then
-		safecall(addon.OnInitialize, addon, name)
-	end
-	if AceEvent then
-		AceEvent:TriggerEvent("Ace2_AddonInitialized", addon)
-	end
-	RegisterOnEnable(addon)
-	local n2 = AceAddon.addonsToOnEnable and #AceAddon.addonsToOnEnable or 0
-	if n2 - n > 1 then
-		local mine = table.remove(AceAddon.addonsToOnEnable)
-		table.insert(AceAddon.addonsToOnEnable, n+1, mine)
-	end
-end
-
-local aboutFrame
-local function createAboutFrame()
-	aboutFrame = CreateFrame("Frame", "AceAddon20AboutFrame", UIParent, "DialogBoxFrame")
-	aboutFrame:SetWidth(500)
-	aboutFrame:SetHeight(400)
-	aboutFrame:SetPoint("CENTER")
-	aboutFrame:SetBackdrop({
-		bgFile = [[Interface\DialogFrame\UI-DialogBox-Background]],
-	    edgeFile = [[Interface\Tooltips\UI-Tooltip-Border]],
-	    tile = true, tileSize = 16, edgeSize = 16,
-	    insets = { left = 5, right = 5, top = 5, bottom = 5 }
-	})
-	aboutFrame:SetBackdropColor(0,0,0,1)
-
-	local donateButton = CreateFrame("Button", "AceAddon20AboutFrameDonateButton", aboutFrame, "UIPanelButtonTemplate2")
-	aboutFrame.donateButton = donateButton
-	donateButton:SetPoint("BOTTOMRIGHT", -20, 20)
-	_G.AceAddon20AboutFrameDonateButtonText:SetText(DONATE)
-	donateButton:SetWidth(_G.AceAddon20AboutFrameDonateButtonText:GetWidth()+20)
-	donateButton:SetScript("OnClick", function()
-		aboutFrame.currentAddon:OpenDonationFrame()
-	end)
-
-	local text = aboutFrame:CreateFontString(nil, "OVERLAY", "GameFontHighlightLarge")
-	aboutFrame.title = text
-	text:SetPoint("TOP", 0, -5)
-
-	aboutFrame:Hide()
-
-	aboutFrame.lefts = {}
-	aboutFrame.rights = {}
-	aboutFrame.textLefts = {}
-	aboutFrame.textRights = {}
-	function aboutFrame:Clear()
-		self.title:SetText("")
-		for i = 1, #self.lefts do
-			self.lefts[i] = nil
-			self.rights[i] = nil
-		end
-	end
-
-	function aboutFrame:AddLine(left, right)
-		aboutFrame.lefts[#aboutFrame.lefts+1] = left
-		aboutFrame.rights[#aboutFrame.rights+1] = right
-	end
-
-	local aboutFrame_Show = aboutFrame.Show
-	function aboutFrame:Show(...)
-		local maxLeftWidth = 0
-		local maxRightWidth = 0
-		local textHeight = 0
-		for i = 1, #self.lefts do
-			if not self.textLefts[i] then
-				local left = aboutFrame:CreateFontString(nil, "OVERLAY", "GameFontNormal")
-				self.textLefts[i] = left
-				local right = aboutFrame:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
-				self.textRights[i] = right
-				if i == 1 then
-					left:SetPoint("TOPRIGHT", aboutFrame, "TOPLEFT", 75, -35)
-				else
-					left:SetPoint("TOPRIGHT", self.textLefts[i-1], "BOTTOMRIGHT", 0, -5)
-				end
-				right:SetPoint("LEFT", left, "RIGHT", 5, 0)
-			end
-			self.textLefts[i]:SetText(self.lefts[i] .. ":")
-			self.textRights[i]:SetText(self.rights[i])
-			local leftWidth = self.textLefts[i]:GetWidth()
-			local rightWidth = self.textRights[i]:GetWidth()
-			textHeight = self.textLefts[i]:GetHeight()
-			if maxLeftWidth < leftWidth then
-				maxLeftWidth = leftWidth
-			end
-			if maxRightWidth < rightWidth then
-				maxRightWidth = rightWidth
-			end
-		end
-		for i = #self.lefts+1, #self.textLefts do
-			self.textLefts[i]:SetText('')
-			self.textRights[i]:SetText('')
-		end
-		aboutFrame:SetWidth(75 + maxRightWidth + 20)
-		aboutFrame:SetHeight(#self.lefts * (textHeight + 5) + 100)
-
-		aboutFrame_Show(self, ...)
-	end
-	aboutFrame:Hide()
-
-	createAboutFrame = nil
-end
-local donateFrame
-
-local function unobfuscateEmail(email)
-	return email:gsub(" AT ", "@"):gsub(" DOT ", ".")
-end
-
-local function isGoodVariable(var)
-	return type(var) == "string" or type(var) == "number"
-end
-function AceAddon.prototype:PrintAddonInfo()
-	if createAboutFrame then
-		createAboutFrame()
-	end
-	aboutFrame:Clear()
-	local x
-	if isGoodVariable(self.title) then
-		x = tostring(self.title)
-	elseif isGoodVariable(self.name) then
-		x = tostring(self.name)
-	else
-		x = "<" .. tostring(self.class) .. " instance>"
-	end
-	if type(self.IsActive) == "function" then
-		if not self:IsActive() then
-			x = x .. " " .. STANDBY
-		end
-	end
-	aboutFrame.title:SetText(x)
-
-	if isGoodVariable(self.version) then
-		aboutFrame:AddLine(VERSION, tostring(self.version))
-	end
-	if isGoodVariable(self.notes) then
-		aboutFrame:AddLine(NOTES, tostring(self.notes))
-	end
-	if isGoodVariable(self.author) then
-		aboutFrame:AddLine(AUTHOR, tostring(self.author))
-	end
-	if isGoodVariable(self.credits) then
-		aboutFrame:AddLine(CREDITS, tostring(self.credits))
-	end
-	if isGoodVariable(self.date) then
-		aboutFrame:AddLine(DATE, tostring(self.date))
-	end
-	if self.category then
-		local category = CATEGORIES[self.category]
-		if category then
-			aboutFrame:AddLine(CATEGORY, tostring(self.category))
-		end
-	end
-	if isGoodVariable(self.email) then
-		aboutFrame:AddLine(EMAIL, unobfuscateEmail(tostring(self.email)))
-	end
-	if isGoodVariable(self.website) then
-		aboutFrame:AddLine(WEBSITE, tostring(self.website))
-	end
-	if isGoodVariable(self.license) then
-		aboutFrame:AddLine(LICENSE, tostring(self.license))
-	end
-
-	if donateFrame and donateFrame:IsShown() then
-		donateFrame:Hide()
-	end
-
-	aboutFrame.currentAddon = self
-
-	aboutFrame:Show()
-
-	if self.donate then
-		aboutFrame.donateButton:Show()
-	else
-		aboutFrame.donateButton:Hide()
-	end
-end
-
-local function createDonateFrame()
-	donateFrame = CreateFrame("Frame", "AceAddon20Frame", UIParent, "DialogBoxFrame")
-
-	donateFrame:SetWidth(500)
-	donateFrame:SetHeight(200)
-	donateFrame:SetPoint("CENTER")
-	donateFrame:SetBackdrop({
-		bgFile = [[Interface\DialogFrame\UI-DialogBox-Background]],
-		edgeFile = [[Interface\Tooltips\UI-Tooltip-Border]],
-		tile = true, tileSize = 16, edgeSize = 16,
-		insets = { left = 5, right = 5, top = 5, bottom = 5 }
-	})
-	donateFrame:SetBackdropColor(0,0,0,1)
-
-	local text = donateFrame:CreateFontString(nil, "OVERLAY", "GameFontHighlightLarge")
-	text:SetPoint("TOP", 0, -5)
-	text:SetText(DONATE)
-
-	local howto = donateFrame:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
-	howto:SetPoint("TOP", text, "BOTTOM", 0, -5)
-	howto:SetPoint("LEFT", 16, 0)
-	howto:SetPoint("RIGHT", -16, 0)
-	if not IsMacClient() then
-		-- Windows or Linux
-		howto:SetText(HOWTO_DONATE_WINDOWS)
-	else
-		howto:SetText(HOWTO_DONATE_MAC)
-	end
-
-	local scrollFrame = CreateFrame("ScrollFrame", "AceAddon20FrameScrollFrame", donateFrame, "UIPanelScrollFrameTemplate")
-	scrollFrame:SetToplevel(true)
-	scrollFrame:SetPoint("TOP", -10, -76)
-	scrollFrame:SetWidth(455)
-	scrollFrame:SetHeight(70)
-	howto:SetPoint("BOTTOM", scrollFrame, "TOP")
-
-	local editBox = CreateFrame("EditBox", nil, scrollFrame)
-	donateFrame.editBox = editBox
-	scrollFrame:SetScrollChild(editBox)
-	editBox:SetFontObject(ChatFontNormal)
-	editBox:SetMultiLine(true)
-	editBox:SetMaxLetters(99999)
-	editBox:SetWidth(450)
-	editBox:SetHeight(54)
-	editBox:SetPoint("BOTTOM", 5, 0)
-	editBox:SetJustifyH("LEFT")
-	editBox:SetJustifyV("TOP")
-	editBox:SetAutoFocus(false)
-	editBox:SetScript("OnTextChanged", function(this)
-		if this:GetText() ~= this.text then
-			this:SetText(this.text)
-		end
-	end)
-	editBox:SetScript("OnEscapePressed", function(this)
-		this:ClearFocus()
-	end)
-	createDonateFrame = nil
-end
-
-local function fix(char)
-	return ("%%%02x"):format(char:byte())
-end
-
-local function urlencode(text)
-	return text:gsub("[^0-9A-Za-z]", fix)
-end
-
-function AceAddon.prototype:OpenDonationFrame()
-	if createDonateFrame then
-		createDonateFrame()
-	end
-	local donate = self.donate
-	if type(donate) ~= "string" then
-		donate = "Wowace"
-	end
-	local style, data = (":"):split(donate, 2)
-	style = style:lower()
-	if style ~= "website" and style ~= "paypal" then
-		style = "wowace"
-	end
-	if style == "wowace" then
-		donateFrame.editBox.text = "http://www.wowace.com/wiki/Donations"
-	elseif style == "website" then
-		donateFrame.editBox.text = data
-	else -- PayPal
-		local text = "https://www.paypal.com/cgi-bin/webscr?cmd=_xclick&business=" .. urlencode(unobfuscateEmail(data))
-		local name
-		if type(self.title) == "string" then
-			name = self.title
-		elseif type(self.name) == "string" then
-			name = self.name
-		end
-		if name then
-			name = name:gsub("|c%x%x%x%x%x%x%x%x", ""):gsub("|r", "")
-			text = text .. "&item_name=" .. urlencode(name)
-		end
-		donateFrame.editBox.text = text
-	end
-	donateFrame.editBox:SetText(donateFrame.editBox.text)
-
-	if aboutFrame and aboutFrame:IsShown() then
-		aboutFrame:Hide()
-	end
-
-	donateFrame:Show()
-
-	donateFrame.editBox:SetFocus()
-end
-
-local options
-function AceAddon:GetAceOptionsDataTable(target)
-	return {
-		about = {
-			name = ABOUT,
-			desc = PRINT_ADDON_INFO,
-			type = "execute",
-			func = "PrintAddonInfo",
-			order = -1,
-		},
-		donate = {
-			name = DONATE,
-			desc = DONATE_DESC,
-			type = "execute",
-			func = "OpenDonationFrame",
-			order = -1,
-			hidden = function()
-				return not target.donate
-			end
-		}
-	}
-end
-
-function AceAddon:PLAYER_LOGIN()
-	self.playerLoginFired = true
-	if self.addonsToOnEnable then
-		while #self.addonsToOnEnable > 0 do
-			local addon = table.remove(self.addonsToOnEnable, 1)
-			self.addonsStarted[addon] = true
-			if (type(addon.IsActive) ~= "function" or addon:IsActive()) and (not AceModuleCore or not AceModuleCore:IsModule(addon) or AceModuleCore:IsModuleActive(addon)) then
-				AceAddon:ManualEnable(addon)
-			end
-		end
-		self.addonsToOnEnable = nil
-	end
-end
-
-function AceAddon.prototype:Inject(t)
-	AceAddon:argCheck(t, 2, "table")
-	for k,v in pairs(t) do
-		self[k] = v
-	end
-end
-
-function AceAddon.prototype:init()
-	if not AceEvent then
-		error(MAJOR_VERSION .. " requires AceEvent-2.0", 4)
-	end
-	AceAddon.super.prototype.init(self)
-
-	self.super = self.class.prototype
-
-	AceAddon:RegisterEvent("ADDON_LOADED", "ADDON_LOADED")
-	local names = {}
-	for i = 1, GetNumAddOns() do
-		if IsAddOnLoaded(i) then names[GetAddOnInfo(i)] = true end
-	end
-	self.possibleNames = names
-	table.insert(AceAddon.nextAddon, self)
-end
-
-function AceAddon.prototype:ToString()
-	local x
-	if type(self.title) == "string" then
-		x = self.title
-	elseif type(self.name) == "string" then
-		x = self.name
-	else
-		x = "<" .. tostring(self.class) .. " instance>"
-	end
-	if (type(self.IsActive) == "function" and not self:IsActive()) or (AceModuleCore and AceModuleCore:IsModule(addon) and AceModuleCore:IsModuleActive(addon)) then
-		x = x .. " " .. STANDBY
-	end
-	return x
-end
-
-AceAddon.new = function(self, ...)
-	local class = AceAddon:pcall(AceOO.Classpool, self, ...)
-	return class:new()
-end
-
-function AceAddon:ManualEnable(addon)
-	AceAddon:argCheck(addon, 2, "table")
-	local first = nil
-	if AceOO.inherits(addon, "AceAddon-2.0") then
-		if AceAddon.addonsEnabled and not AceAddon.addonsEnabled[addon] then
-			first = true
-			AceAddon.addonsEnabled[addon] = true
-		end
-	end
-	local current = addon.class
-	while current and current ~= AceOO.Class do
-		if current.mixins then
-			for mixin in pairs(current.mixins) do
-				if type(mixin.OnEmbedEnable) == "function" then
-					safecall(mixin.OnEmbedEnable, mixin, addon, first)
-				end
-			end
-		end
-		current = current.super
-	end
-	if type(addon.OnEnable) == "function" then
-		safecall(addon.OnEnable, addon, first)
-	end
-	if AceEvent then
-		AceEvent:TriggerEvent("Ace2_AddonEnabled", addon, first)
-	end
-end
-
-function AceAddon:ManualDisable(addon)
-	AceAddon:argCheck(addon, 2, "table")
-	local current = addon.class
-	while current and current ~= AceOO.Class do
-		if current.mixins then
-			for mixin in pairs(current.mixins) do
-				if type(mixin.OnEmbedDisable) == "function" then
-					safecall(mixin.OnEmbedDisable, mixin, addon)
-				end
-			end
-		end
-		current = current.super
-	end
-	if type(module.OnDisable) == "function" then
-		safecall(module.OnDisable, addon)
-	end
-	if AceEvent then
-		AceEvent:TriggerEvent("Ace2_AddonDisabled", addon)
-	end
-end
-
-local function external(self, major, instance)
-	if major == "AceEvent-2.0" then
-		AceEvent = instance
-
-		AceEvent:embed(self)
-
-		self:RegisterEvent("PLAYER_LOGIN", "PLAYER_LOGIN", true)
-	elseif major == "AceConsole-2.0" then
-		AceConsole = instance
-
-		local slashCommands = { "/ace2" }
-		local _,_,_,enabled,loadable = GetAddOnInfo("Ace")
-		if not enabled or not loadable then
-			table.insert(slashCommands, "/ace")
-		end
-		local function listAddon(addon, depth)
-			if not depth then
-				depth = 0
-			end
-
-			local s = ("  "):rep(depth) .. " - " .. tostring(addon)
-			if rawget(addon, 'version') then
-				s = s .. " - |cffffff7f" .. tostring(addon.version) .. "|r"
-			end
-			if rawget(addon, 'slashCommand') then
-				s = s .. " |cffffff7f(" .. tostring(addon.slashCommand) .. ")|r"
-			end
-			print(s)
-			if type(rawget(addon, 'modules')) == "table" then
-				local i = 0
-				for k,v in pairs(addon.modules) do
-					i = i + 1
-					if i == 6 then
-						print(("  "):rep(depth + 1) .. " - more...")
-						break
-					else
-						listAddon(v, depth + 1)
-					end
-				end
-			end
-		end
-		local function listNormalAddon(i)
-			local name,_,_,enabled,loadable = GetAddOnInfo(i)
-			if not loadable then
-				enabled = false
-			end
-			if self.addons[name] then
-				listAddon(self.addons[name])
-			else
-				local s = " - " .. tostring(GetAddOnMetadata(i, "Title") or name)
-				local version = GetAddOnMetadata(i, "Version")
-				if version then
-					if version:find("%$Revision: (%d+) %$") then
-						version = version:gsub("%$Revision: (%d+) %$", "%1")
-					elseif version:find("%$Rev: (%d+) %$") then
-						version = version:gsub("%$Rev: (%d+) %$", "%1")
-					elseif version:find("%$LastChangedRevision: (%d+) %$") then
-						version = version:gsub("%$LastChangedRevision: (%d+) %$", "%1")
-					end
-					s = s .. " - |cffffff7f" .. version .. "|r"
-				end
-				if not enabled then
-					s = s .. " |cffff0000(disabled)|r"
-				end
-				if IsAddOnLoadOnDemand(i) then
-					s = s .. " |cff00ff00[LoD]|r"
-				end
-				print(s)
-			end
-		end
-		local function mySort(alpha, bravo)
-			return tostring(alpha) < tostring(bravo)
-		end
-		AceConsole.RegisterChatCommand(self, slashCommands, {
-			desc = "AddOn development framework",
-			name = "Ace2",
-			type = "group",
-			args = {
-				about = {
-					desc = "Get information about Ace2",
-					name = "About",
-					type = "execute",
-					func = function()
-						print("|cffffff7fAce2|r - |cffffff7f2.0." .. MINOR_VERSION:gsub("%$Revision: (%d+) %$", "%1") .. "|r - AddOn development framework")
-						print(" - |cffffff7f" .. AUTHOR .. ":|r Ace Development Team")
-						print(" - |cffffff7f" .. WEBSITE .. ":|r http://www.wowace.com/")
-					end
-				},
-				list = {
-					desc = "List addons",
-					name = "List",
-					type = "group",
-					args = {
-						ace2 = {
-							desc = "List addons using Ace2",
-							name = "Ace2",
-							type = "execute",
-							func = function()
-								print("|cffffff7fAddon list:|r")
-								table.sort(self.addons, mySort)
-								for _,v in ipairs(self.addons) do
-									listAddon(v)
-								end
-							end
-						},
-						all = {
-							desc = "List all addons",
-							name = "All",
-							type = "execute",
-							func = function()
-								print("|cffffff7fAddon list:|r")
-								local count = GetNumAddOns()
-								for i = 1, count do
-									listNormalAddon(i)
-								end
-							end
-						},
-						enabled = {
-							desc = "List all enabled addons",
-							name = "Enabled",
-							type = "execute",
-							func = function()
-								print("|cffffff7fAddon list:|r")
-								local count = GetNumAddOns()
-								for i = 1, count do
-									local _,_,_,enabled,loadable = GetAddOnInfo(i)
-									if enabled and loadable then
-										listNormalAddon(i)
-									end
-								end
-							end
-						},
-						disabled = {
-							desc = "List all disabled addons",
-							name = "Disabled",
-							type = "execute",
-							func = function()
-								print("|cffffff7fAddon list:|r")
-								local count = GetNumAddOns()
-								for i = 1, count do
-									local _,_,_,enabled,loadable = GetAddOnInfo(i)
-									if not enabled or not loadable then
-										listNormalAddon(i)
-									end
-								end
-							end
-						},
-						lod = {
-							desc = "List all LoadOnDemand addons",
-							name = "LoadOnDemand",
-							type = "execute",
-							func = function()
-								print("|cffffff7fAddon list:|r")
-								local count = GetNumAddOns()
-								for i = 1, count do
-									if IsAddOnLoadOnDemand(i) then
-										listNormalAddon(i)
-									end
-								end
-							end
-						},
-						ace1 = {
-							desc = "List all addons using Ace1",
-							name = "Ace 1.x",
-							type = "execute",
-							func = function()
-								print("|cffffff7fAddon list:|r")
-								local count = GetNumAddOns()
-								for i = 1, count do
-									local dep1, dep2, dep3, dep4 = GetAddOnDependencies(i)
-									if dep1 == "Ace" or dep2 == "Ace" or dep3 == "Ace" or dep4 == "Ace" then
-										listNormalAddon(i)
-									end
-								end
-							end
-						},
-						libs = {
-							desc = "List all libraries using AceLibrary",
-							name = "Libraries",
-							type = "execute",
-							func = function()
-								if type(AceLibrary) == "table" and type(AceLibrary.libs) == "table" then
-									print("|cffffff7fLibrary list:|r")
-									for name, data in pairs(AceLibrary.libs) do
-										local s
-										if data.minor then
-											s = " - " .. tostring(name) .. "." .. tostring(data.minor)
-										else
-											s = " - " .. tostring(name)
-										end
-										if rawget(AceLibrary(name), 'slashCommand') then
-											s = s .. " |cffffff7f(" .. tostring(AceLibrary(name).slashCommand) .. "|cffffff7f)"
-										end
-										print(s)
-									end
-								end
-							end
-						},
-						search = {
-							desc = "Search by name",
-							name = "Search",
-							type = "text",
-							usage = "<keyword>",
-							input = true,
-							get = false,
-							set = function(...)
-								local arg = { ... }
-								for i,v in ipairs(arg) do
-									arg[i] = v:gsub('%*', '.*'):gsub('%%', '%%%%'):lower()
-								end
-								local count = GetNumAddOns()
-								for i = 1, count do
-									local name = GetAddOnInfo(i)
-									local good = true
-									for _,v in ipairs(arg) do
-										if not name:lower():find(v) then
-											good = false
-											break
-										end
-									end
-									if good then
-										listNormalAddon(i)
-									end
-								end
-							end
-						}
-					},
-				},
-				enable = {
-					desc = "Enable addon(s).",
-					name = "Enable",
-					type = "text",
-					usage = "<addon 1> <addon 2> ...",
-					get = false,
-					input = true,
-					set = function(...)
-						for i = 1, select("#", ...) do
-							local addon = select(i, ...)
-							local name, title, _, enabled, _, reason = GetAddOnInfo(addon)
-							if reason == "MISSING" then
-								print(("|cffffff7fAce2:|r AddOn %q does not exist."):format(addon))
-							elseif not enabled then
-								EnableAddOn(addon)
-								print(("|cffffff7fAce2:|r %s is now enabled."):format(addon or name))
-							else
-								print(("|cffffff7fAce2:|r %s is already enabled."):format(addon or name))
-							end
-						end
-					end,
-				},
-				disable = {
-					desc = "Disable addon(s).",
-					name = "Disable",
-					type = "text",
-					usage = "<addon 1> <addon 2> ...",
-					get = false,
-					input = true,
-					set = function(...)
-						for i = 1, select("#", ...) do
-							local addon = select(i, ...)
-							local name, title, _, enabled, _, reason = GetAddOnInfo(addon)
-							if reason == "MISSING" then
-							print(("|cffffff7fAce2:|r AddOn %q does not exist."):format(addon))
-							elseif enabled then
-								DisableAddOn(addon)
-								print(("|cffffff7fAce2:|r %s is now disabled."):format(addon or name))
-							else
-								print(("|cffffff7fAce2:|r %s is already disabled."):format(addon or name))
-							end
-						end
-					end,
-				},
-				load = {
-					desc = "Load addon(s).",
-					name = "Load",
-					type = "text",
-					usage = "<addon 1> <addon 2> ...",
-					get = false,
-					input = true,
-					set = function(...)
-						for i = 1, select("#", ...) do
-							local addon = select(i, ...)
-							local name, title, _, _, loadable, reason = GetAddOnInfo(addon)
-							if reason == "MISSING" then
-								print(("|cffffff7fAce2:|r AddOn %q does not exist."):format(addon))
-							elseif not loadable then
-								print(("|cffffff7fAce2:|r AddOn %q is not loadable. Reason: %s."):format(addon, reason))
-							else
-								LoadAddOn(addon)
-								print(("|cffffff7fAce2:|r %s is now loaded."):format(addon or name))
-							end
-						end
-					end
-				},
-				info = {
-					desc = "Display information",
-					name = "Information",
-					type = "execute",
-					func = function()
-						local mem, threshold = gcinfo()
-						print((" - |cffffff7fMemory usage [|r%.3f MiB|cffffff7f]|r"):format(mem / 1024))
-						if threshold then
-							print((" - |cffffff7fThreshold [|r%.3f MiB|cffffff7f]|r"):format(threshold / 1024))
-						end
-						print((" - |cffffff7fFramerate [|r%.0f fps|cffffff7f]|r"):format(GetFramerate()))
-						local bandwidthIn, bandwidthOut, latency = GetNetStats()
-						bandwidthIn, bandwidthOut = floor(bandwidthIn * 1024), floor(bandwidthOut * 1024)
-						print((" - |cffffff7fLatency [|r%.0f ms|cffffff7f]|r"):format(latency))
-						print((" - |cffffff7fBandwidth in [|r%.0f B/s|cffffff7f]|r"):format(bandwidthIn))
-						print((" - |cffffff7fBandwidth out [|r%.0f B/s|cffffff7f]|r"):format(bandwidthOut))
-						print((" - |cffffff7fTotal addons [|r%d|cffffff7f]|r"):format(GetNumAddOns()))
-						print((" - |cffffff7fAce2 addons [|r%d|cffffff7f]|r"):format(#self.addons))
-						local ace = 0
-						local enabled = 0
-						local disabled = 0
-						local lod = 0
-						for i = 1, GetNumAddOns() do
-							local dep1, dep2, dep3, dep4 = GetAddOnDependencies(i)
-							if dep1 == "Ace" or dep2 == "Ace" or dep3 == "Ace" or dep4 == "Ace" then
-								ace = ace + 1
-							end
-							if IsAddOnLoadOnDemand(i) then
-								lod = lod + 1
-							end
-							local isActive, loadable = select(4, GetAddOnInfo(i))
-							if not isActive or not loadable then
-								disabled = disabled + 1
-							else
-								enabled = enabled + 1
-							end
-						end
-						print((" - |cffffff7fAce 1.x addons [|r%d|cffffff7f]|r"):format(ace))
-						print((" - |cffffff7fLoadOnDemand addons [|r%d|cffffff7f]|r"):format(lod))
-						print((" - |cffffff7fenabled addons [|r%d|cffffff7f]|r"):format(enabled))
-						print((" - |cffffff7fdisabled addons [|r%d|cffffff7f]|r"):format(disabled))
-						local libs = 0
-						if type(AceLibrary) == "table" and type(AceLibrary.libs) == "table" then
-							for _ in pairs(AceLibrary.libs) do
-								libs = libs + 1
-							end
-						end
-						print((" - |cffffff7fAceLibrary instances [|r%d|cffffff7f]|r"):format(libs))
-					end
-				}
-			}
-		})
-	elseif major == "AceModuleCore-2.0" then
-		AceModuleCore = instance
-	end
-end
-
-local function activate(self, oldLib, oldDeactivate)
-	AceAddon = self
-
-	self.playerLoginFired = oldLib and oldLib.playerLoginFired or DEFAULT_CHAT_FRAME and DEFAULT_CHAT_FRAME.defaultLanguage
-	self.addonsToOnEnable = oldLib and oldLib.addonsToOnEnable
-	self.addons = oldLib and oldLib.addons or {}
-	self.nextAddon = oldLib and oldLib.nextAddon or {}
-	self.skipAddon = oldLib and oldLib.skipAddon or {}
-	self.addonsStarted = oldLib and oldLib.addonsStarted or {}
-	self.addonsEnabled = oldLib and oldLib.addonsEnabled or {}
-
-	if oldDeactivate then
-		oldDeactivate(oldLib)
-	end
-end
-
-AceLibrary:Register(AceAddon, MAJOR_VERSION, MINOR_VERSION, activate, nil, external)
--- a/modules/FuBar_ReActionFu/lib/AceAddon-2.0/AceAddon-2.0.toc	Wed Apr 02 18:22:02 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,12 +0,0 @@
-## Interface: 20200
-
-## Title: Lib: AceAddon-2.0
-## Notes: AddOn development framework
-## Author: Ace Development Team
-## LoadOnDemand: 1
-## X-Website: http://www.wowace.com
-## X-Category: Library
-## X-License: LGPL v2.1 + MIT for AceOO-2.0
-## Dependencies: AceLibrary, AceOO-2.0
- 
-AceAddon-2.0.lua
--- a/modules/FuBar_ReActionFu/lib/AceEvent-2.0/AceEvent-2.0.lua	Wed Apr 02 18:22:02 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1082 +0,0 @@
---[[
-Name: AceEvent-2.0
-Revision: $Rev: 49307 $
-Developed by: The Ace Development Team (http://www.wowace.com/index.php/The_Ace_Development_Team)
-Inspired By: Ace 1.x by Turan (turan@gryphon.com)
-Website: http://www.wowace.com/
-Documentation: http://www.wowace.com/index.php/AceEvent-2.0
-SVN: http://svn.wowace.com/root/trunk/Ace2/AceEvent-2.0
-Description: Mixin to allow for event handling, scheduling, and inter-addon
-             communication.
-Dependencies: AceLibrary, AceOO-2.0
-License: LGPL v2.1
-]]
-
-local MAJOR_VERSION = "AceEvent-2.0"
-local MINOR_VERSION = "$Revision: 49307 $"
-
-if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary") end
-if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end
-
-if not AceLibrary:HasInstance("AceOO-2.0") then error(MAJOR_VERSION .. " requires AceOO-2.0") end
-
-local AceOO = AceLibrary:GetInstance("AceOO-2.0")
-local Mixin = AceOO.Mixin
-local AceEvent = Mixin {
-	"RegisterEvent",
-	"RegisterAllEvents",
-	"UnregisterEvent",
-	"UnregisterAllEvents",
-	"TriggerEvent",
-	"ScheduleEvent",
-	"ScheduleRepeatingEvent",
-	"CancelScheduledEvent",
-	"CancelAllScheduledEvents",
-	"IsEventRegistered",
-	"IsEventScheduled",
-	"RegisterBucketEvent",
-	"UnregisterBucketEvent",
-	"UnregisterAllBucketEvents",
-	"IsBucketEventRegistered",
-	"ScheduleLeaveCombatAction",
-	"CancelAllCombatSchedules",
-}
-
-local weakKey = {__mode="k"}
-
-local FAKE_NIL
-local RATE
-
-local eventsWhichHappenOnce = {
-	PLAYER_LOGIN = true,
-	AceEvent_FullyInitialized = true,
-	VARIABLES_LOADED = true,
-	PLAYER_LOGOUT = true,
-}
-local next = next
-local pairs = pairs
-local pcall = pcall
-local type = type
-local GetTime = GetTime
-local gcinfo = gcinfo
-local unpack = unpack
-local geterrorhandler = geterrorhandler
-
-local build = GetBuildInfo()
-local useTablesAsIDs = build:find("^2%.0%.") or build:find("^2%.1%.") or build:find("^0%.1%.")
-
-local new, del
-do
-	local cache = setmetatable({}, {__mode='k'})
-	function new(...)
-		local t = next(cache)
-		if t then
-			cache[t] = nil
-			for i = 1, select('#', ...) do
-				t[i] = select(i, ...)
-			end
-			return t
-		else
-			return { ... }
-		end
-	end
-	function del(t)
-		for k in pairs(t) do
-			t[k] = nil
-		end
-		cache[t] = true
-		return nil
-	end
-end
-
-local registeringFromAceEvent
---[[----------------------------------------------------------------------------------
-Notes:
-	* Registers the addon with a Blizzard event or a custom AceEvent, which will cause the given method to be called when that is triggered.
-Arguments:
-	string - name of the event to register
-	[optional] string or function - name of the method or function to call. Default: same name as "event".
-	[optional] boolean - whether to have method called only once. Default: false
-------------------------------------------------------------------------------------]]
-function AceEvent:RegisterEvent(event, method, once)
-	AceEvent:argCheck(event, 2, "string")
-	if self == AceEvent and not registeringFromAceEvent then
-		AceEvent:argCheck(method, 3, "function")
-		self = method
-	else
-		AceEvent:argCheck(method, 3, "string", "function", "nil", "boolean", "number")
-		if type(method) == "boolean" or type(method) == "number" then
-			AceEvent:argCheck(once, 4, "nil")
-			once, method = method, event
-		end
-	end
-	AceEvent:argCheck(once, 4, "number", "boolean", "nil")
-	if eventsWhichHappenOnce[event] then
-		once = true
-	end
-	local throttleRate
-	if type(once) == "number" then
-		throttleRate, once = once
-	end
-	if not method then
-		method = event
-	end
-	if type(method) == "string" and type(self[method]) ~= "function" then
-		AceEvent:error("Cannot register event %q to method %q, it does not exist", event, method)
-	else
-		assert(type(method) == "function" or type(method) == "string")
-	end
-
-	local AceEvent_registry = AceEvent.registry
-	if not AceEvent_registry[event] then
-		AceEvent_registry[event] = new()
-		AceEvent.frame:RegisterEvent(event)
-	end
-
-	local remember = true
-	if AceEvent_registry[event][self] then
-		remember = false
-	end
-	AceEvent_registry[event][self] = method
-
-	local AceEvent_onceRegistry = AceEvent.onceRegistry
-	if once then
-		if not AceEvent_onceRegistry then
-			AceEvent.onceRegistry = {}
-			AceEvent_onceRegistry = AceEvent.onceRegistry
-		end
-		if not AceEvent_onceRegistry[event] then
-			AceEvent_onceRegistry[event] = new()
-		end
-		AceEvent_onceRegistry[event][self] = true
-	else
-		if AceEvent_onceRegistry and AceEvent_onceRegistry[event] then
-			AceEvent_onceRegistry[event][self] = nil
-			if not next(AceEvent_onceRegistry[event]) then
-				AceEvent_onceRegistry[event] = del(AceEvent_onceRegistry[event])
-			end
-		end
-	end
-
-	local AceEvent_throttleRegistry = AceEvent.throttleRegistry
-	if throttleRate then
-		if not AceEvent_throttleRegistry then
-			AceEvent.throttleRegistry = {}
-			AceEvent_throttleRegistry = AceEvent.throttleRegistry
-		end
-		if not AceEvent_throttleRegistry[event] then
-			AceEvent_throttleRegistry[event] = new()
-		end
-		if AceEvent_throttleRegistry[event][self] then
-			AceEvent_throttleRegistry[event][self] = nil
-		end
-		AceEvent_throttleRegistry[event][self] = setmetatable(new(), weakKey)
-		local t = AceEvent_throttleRegistry[event][self]
-		t[RATE] = throttleRate
-	else
-		if AceEvent_throttleRegistry and AceEvent_throttleRegistry[event] then
-			if AceEvent_throttleRegistry[event][self] then
-				AceEvent_throttleRegistry[event][self] = nil
-			end
-			if not next(AceEvent_throttleRegistry[event]) then
-				AceEvent_throttleRegistry[event] = del(AceEvent_throttleRegistry[event])
-			end
-		end
-	end
-
-	if remember then
-		AceEvent:TriggerEvent("AceEvent_EventRegistered", self, event)
-	end
-end
-
-local ALL_EVENTS
-
---[[----------------------------------------------------------------------------------
-Notes:
-	* Registers all events to the given method
-	* To access the current event, check AceEvent.currentEvent
-	* To access the current event's unique identifier, check AceEvent.currentEventUID
-	* This is only for debugging purposes.
-Arguments:
-	[optional] string or function - name of the method or function to call. Default: same name as "event".
-------------------------------------------------------------------------------------]]
-function AceEvent:RegisterAllEvents(method)
-	if self == AceEvent then
-		AceEvent:argCheck(method, 1, "function")
-		self = method
-	else
-		AceEvent:argCheck(method, 1, "string", "function")
-		if type(method) == "string" and type(self[method]) ~= "function" then
-			AceEvent:error("Cannot register all events to method %q, it does not exist", method)
-		end
-	end
-	
-	local AceEvent_registry = AceEvent.registry
-	if not AceEvent_registry[ALL_EVENTS] then
-		AceEvent_registry[ALL_EVENTS] = new()
-		AceEvent.frame:RegisterAllEvents()
-	end
-	
-	local remember = not AceEvent_registry[ALL_EVENTS][self]
-	AceEvent_registry[ALL_EVENTS][self] = method
-	if remember then
-		AceEvent:TriggerEvent("AceEvent_EventRegistered", self, "all")
-	end
-end
-
---[[----------------------------------------------------------------------------------
-Notes:
-	* Trigger a custom AceEvent.
-	* This should never be called to simulate fake Blizzard events.
-	* Custom events should be in the form of AddonName_SpecificEvent
-Arguments:
-	string - name of the event
-	tuple - list of arguments to pass along
-------------------------------------------------------------------------------------]]
-function AceEvent:TriggerEvent(event, ...)
-	if type(event) ~= "string" then
-		DEFAULT_CHAT_FRAME:AddMessage(debugstack())
-	end
-	AceEvent:argCheck(event, 2, "string")
-	local AceEvent_registry = AceEvent.registry
-	if (not AceEvent_registry[event] or not next(AceEvent_registry[event])) and (not AceEvent_registry[ALL_EVENTS] or not next(AceEvent_registry[ALL_EVENTS])) then
-		return
-	end
-	local lastEvent = AceEvent.currentEvent
-	AceEvent.currentEvent = event
-	local lastEventUID = AceEvent.currentEventUID
-	local uid = AceEvent.UID_NUM + 1
-	AceEvent.UID_NUM = uid
-	AceEvent.currentEventUID = uid
-
-	local tmp = new()
-
-	local AceEvent_onceRegistry = AceEvent.onceRegistry
-	if AceEvent_onceRegistry and AceEvent_onceRegistry[event] then
-		for obj, method in pairs(AceEvent_onceRegistry[event]) do
-			tmp[obj] = AceEvent_registry[event] and AceEvent_registry[event][obj] or nil
-		end
-		local obj = next(tmp)
-		while obj do
-			local method = tmp[obj]
-			AceEvent.UnregisterEvent(obj, event)
-			if type(method) == "string" then
-				local obj_method = obj[method]
-				if obj_method then
-					local success, err = pcall(obj_method, obj, ...)
-					if not success then geterrorhandler()(err:find("%.lua:%d+:") and err or (debugstack():match("(.-: )in.-\n") or "") .. err) end
-				end
-			elseif method then -- function
-				local success, err = pcall(method, ...)
-				if not success then geterrorhandler()(err:find("%.lua:%d+:") and err or (debugstack():match("(.-: )in.-\n") or "") .. err) end
-			end
-			tmp[obj] = nil
-			obj = next(tmp)
-		end
-	end
-
-	local AceEvent_throttleRegistry = AceEvent.throttleRegistry
-	local throttleTable = AceEvent_throttleRegistry and AceEvent_throttleRegistry[event]
-	if AceEvent_registry[event] then
-		for obj, method in pairs(AceEvent_registry[event]) do
-			tmp[obj] = method
-		end
-		local obj = next(tmp)
-		while obj do
-			local method = tmp[obj]
-			local continue = false
-			if throttleTable and throttleTable[obj] then
-				local a1 = ...
-				if a1 == nil then
-					a1 = FAKE_NIL
-				end
-				if not throttleTable[obj][a1] or GetTime() - throttleTable[obj][a1] >= throttleTable[obj][RATE] then
-					throttleTable[obj][a1] = GetTime()
-				else
-					continue = true
-				end
-			end
-			if not continue then
-				if type(method) == "string" then
-					local obj_method = obj[method]
-					if obj_method then
-						local success, err = pcall(obj_method, obj, ...)
-						if not success then geterrorhandler()(err:find("%.lua:%d+:") and err or (debugstack():match("(.-: )in.-\n") or "") .. err) end
-					end
-				elseif method then -- function
-					local success, err = pcall(method, ...)
-					if not success then geterrorhandler()(err:find("%.lua:%d+:") and err or (debugstack():match("(.-: )in.-\n") or "") .. err) end
-				end
-			end
-			tmp[obj] = nil
-			obj = next(tmp)
-		end
-	end
-	if AceEvent_registry[ALL_EVENTS] then
-		for obj, method in pairs(AceEvent_registry[ALL_EVENTS]) do
-			tmp[obj] = method
-		end
-		local obj = next(tmp)
-		while obj do
-			local method = tmp[obj]
-			if type(method) == "string" then
-				local obj_method = obj[method]
-				if obj_method then
-					local success, err = pcall(obj_method, obj, ...)
-					if not success then geterrorhandler()(err:find("%.lua:%d+:") and err or (debugstack():match("(.-: )in.-\n") or "") .. err) end
-				end
-			elseif method then -- function
-				local success, err = pcall(method, ...)
-				if not success then geterrorhandler()(err:find("%.lua:%d+:") and err or (debugstack():match("(.-: )in.-\n") or "") .. err) end
-			end
-			tmp[obj] = nil
-			obj = next(tmp)
-		end
-	end
-	tmp = del(tmp)
-	AceEvent.currentEvent = lastEvent
-	AceEvent.currentEventUID = lastEventUID
-end
-
-local delayRegistry
-local OnUpdate
-do
-	local tmp = {}
-	OnUpdate = function()
-		local t = GetTime()
-		for k,v in pairs(delayRegistry) do
-			tmp[k] = true
-		end
-		for k in pairs(tmp) do
-			local v = delayRegistry[k]
-			if v then
-				local v_time = v.time
-				if not v_time then
-					delayRegistry[k] = nil
-				elseif v_time <= t then
-					local v_repeatDelay = v.repeatDelay
-					if v_repeatDelay then
-						-- use the event time, not the current time, else timing inaccuracies add up over time
-						v.time = v_time + v_repeatDelay
-					end
-					local event = v.event
-					if type(event) == "function" then
-						local uid = AceEvent.UID_NUM + 1
-						AceEvent.UID_NUM = uid
-						AceEvent.currentEventUID = uid
-						local success, err = pcall(event, unpack(v, 1, v.n))
-						if not success then geterrorhandler()(err:find("%.lua:%d+:") and err or (debugstack():match("(.-: )in.-\n") or "") .. err) end
-						AceEvent.currentEventUID = nil
-					else
-						AceEvent:TriggerEvent(event, unpack(v, 1, v.n))
-					end
-					if not v_repeatDelay then
-						local x = delayRegistry[k]
-						if x and x.time == v_time then -- check if it was manually reset
-							if not useTablesAsIDs or type(k) == "string" then
-								del(delayRegistry[k])
-							end
-							delayRegistry[k] = nil
-						end
-					end
-				end
-			end
-		end
-		for k in pairs(tmp) do
-			tmp[k] = nil
-		end
-		if not next(delayRegistry) then
-			AceEvent.frame:Hide()
-		end
-	end
-end
-
-local function ScheduleEvent(self, repeating, event, delay, ...)
-	local id
-	if type(event) == "string" or (useTablesAsIDs and type(event) == "table") then
-		if useTablesAsIDs and type(event) == "table" then
-			if not delayRegistry or not delayRegistry[event] then
-				AceEvent:error("Bad argument #2 to `ScheduleEvent'. Improper id table fed in.")
-			end
-		end
-		if type(delay) ~= "number" then
-			id, event, delay = event, delay, ...
-			AceEvent:argCheck(event, 3, "string", "function", --[[ so message is right ]] "number")
-			AceEvent:argCheck(delay, 4, "number")
-			self:CancelScheduledEvent(id)
-		end
-	else
-		AceEvent:argCheck(event, 2, "string", "function")
-		AceEvent:argCheck(delay, 3, "number")
-	end
-
-	if not delayRegistry then
-		AceEvent.delayRegistry = {}
-		delayRegistry = AceEvent.delayRegistry
-		AceEvent.frame:SetScript("OnUpdate", OnUpdate)
-	end
-	local t
-	if useTablesAsIDs and type(id) == "table" then
-		for k in pairs(id) do
-			id[k] = nil
-		end
-		t = id
-		for i = 2, select('#', ...) do
-			t[i-1] = select(i, ...)
-		end
-		t.n = select('#', ...) - 1
-	elseif id then
-		t = new(select(2, ...))
-		t.n = select('#', ...) - 1
-	else
-		t = new(...)
-		t.n = select('#', ...)
-	end
-	t.event = event
-	t.time = GetTime() + delay
-	t.self = self
-	t.id = id or t
-	t.repeatDelay = repeating and delay
-	delayRegistry[t.id] = t
-	AceEvent.frame:Show()
-	if useTablesAsIDs then
-		return t.id
-	else
-		return
-	end
-end
-
---[[----------------------------------------------------------------------------------
-Notes:
-	* Schedule an event to fire.
-	* To fire on the next frame, specify a delay of 0.
-Arguments:
-	string or function - name of the event to fire, or a function to call.
-	number - the amount of time to wait until calling.
-	tuple - a list of arguments to pass along.
-------------------------------------------------------------------------------------]]
-function AceEvent:ScheduleEvent(event, delay, ...)
-	if type(event) == "string" or (useTablesAsIDs and type(event) == "table") then
-		if useTablesAsIDs and type(event) == "table" then
-			if not delayRegistry or not delayRegistry[event] then
-				AceEvent:error("Bad argument #2 to `ScheduleEvent'. Improper id table fed in.")
-			end
-		end
-		if type(delay) ~= "number" then
-			AceEvent:argCheck(delay, 3, "string", "function", --[[ so message is right ]] "number")
-			AceEvent:argCheck(..., 4, "number")
-		end
-	else
-		AceEvent:argCheck(event, 2, "string", "function")
-		AceEvent:argCheck(delay, 3, "number")
-	end
-
-	return ScheduleEvent(self, false, event, delay, ...)
-end
-
-function AceEvent:ScheduleRepeatingEvent(event, delay, ...)
-	if type(event) == "string" or (useTablesAsIDs and type(event) == "table") then
-		if useTablesAsIDs and type(event) == "table" then
-			if not delayRegistry or not delayRegistry[event] then
-				AceEvent:error("Bad argument #2 to `ScheduleEvent'. Improper id table fed in.")
-			end
-		end
-		if type(delay) ~= "number" then
-			AceEvent:argCheck(delay, 3, "string", "function", --[[ so message is right ]] "number")
-			AceEvent:argCheck(..., 4, "number")
-		end
-	else
-		AceEvent:argCheck(event, 2, "string", "function")
-		AceEvent:argCheck(delay, 3, "number")
-	end
-
-	return ScheduleEvent(self, true, event, delay, ...)
-end
-
-function AceEvent:CancelScheduledEvent(t)
-	if useTablesAsIDs then
-		AceEvent:argCheck(t, 2, "string", "table")
-	else
-		AceEvent:argCheck(t, 2, "string")
-	end
-	if delayRegistry then
-		local v = delayRegistry[t]
-		if v then
-			if not useTablesAsIDs or type(t) == "string" then
-				del(delayRegistry[t])
-			end
-			delayRegistry[t] = nil
-			if not next(delayRegistry) then
-				AceEvent.frame:Hide()
-			end
-			return true
-		end
-	end
-	return false
-end
-
-function AceEvent:IsEventScheduled(t)
-	if useTablesAsIDs then
-		AceEvent:argCheck(t, 2, "string", "table")
-	else
-		AceEvent:argCheck(t, 2, "string")
-	end
-	if delayRegistry then
-		local v = delayRegistry[t]
-		if v then
-			return true, v.time - GetTime()
-		end
-	end
-	return false, nil
-end
-
-function AceEvent:UnregisterEvent(event)
-	AceEvent:argCheck(event, 2, "string")
-	local AceEvent_registry = AceEvent.registry
-	if AceEvent_registry[event] and AceEvent_registry[event][self] then
-		AceEvent_registry[event][self] = nil
-		local AceEvent_onceRegistry = AceEvent.onceRegistry
-		if AceEvent_onceRegistry and AceEvent_onceRegistry[event] and AceEvent_onceRegistry[event][self] then
-			AceEvent_onceRegistry[event][self] = nil
-			if not next(AceEvent_onceRegistry[event]) then
-				AceEvent_onceRegistry[event] = del(AceEvent_onceRegistry[event])
-			end
-		end
-		local AceEvent_throttleRegistry = AceEvent.throttleRegistry
-		if AceEvent_throttleRegistry and AceEvent_throttleRegistry[event] and AceEvent_throttleRegistry[event][self] then
-			AceEvent_throttleRegistry[event][self] = nil
-			if not next(AceEvent_throttleRegistry[event]) then
-				AceEvent_throttleRegistry[event] = del(AceEvent_throttleRegistry[event])
-			end
-		end
-		if not next(AceEvent_registry[event]) then
-			AceEvent_registry[event] = del(AceEvent_registry[event])
-			if not AceEvent_registry[ALL_EVENTS] or not next(AceEvent_registry[ALL_EVENTS]) then
-				AceEvent.frame:UnregisterEvent(event)
-			end
-		end
-	else
-		if self == AceEvent then
-			error(("Cannot unregister event %q. Improperly unregistering from AceEvent-2.0."):format(event), 2)
-		else
-			AceEvent:error("Cannot unregister event %q. %q is not registered with it.", event, self)
-		end
-	end
-	AceEvent:TriggerEvent("AceEvent_EventUnregistered", self, event)
-end
-
-function AceEvent:UnregisterAllEvents()
-	local AceEvent_registry = AceEvent.registry
-	if AceEvent_registry[ALL_EVENTS] and AceEvent_registry[ALL_EVENTS][self] then
-		AceEvent_registry[ALL_EVENTS][self] = nil
-		if not next(AceEvent_registry[ALL_EVENTS]) then
-			AceEvent_registry[ALL_EVENTS] = del(AceEvent_registry[ALL_EVENTS])
-			AceEvent.frame:UnregisterAllEvents()
-			for k,v in pairs(AceEvent_registry) do
-				AceEvent.frame:RegisterEvent(k)
-			end
-		end
-	end
-	if AceEvent_registry.AceEvent_EventUnregistered then
-		local event, data = "AceEvent_EventUnregistered", AceEvent_registry.AceEvent_EventUnregistered
-		local x = data[self]
-		data[self] = nil
-		if x then
-			if not next(data) then
-				if not AceEvent_registry[ALL_EVENTS] then
-					AceEvent.frame:UnregisterEvent(event)
-				end
-				AceEvent_registry[event] = del(AceEvent_registry[event])
-			end
-			AceEvent:TriggerEvent("AceEvent_EventUnregistered", self, event)
-		end
-	end
-	for event, data in pairs(AceEvent_registry) do
-		local x = data[self]
-		data[self] = nil
-		if x and event ~= ALL_EVENTS then
-			if not next(data) then
-				if not AceEvent_registry[ALL_EVENTS] then
-					AceEvent.frame:UnregisterEvent(event)
-				end
-				AceEvent_registry[event] = del(AceEvent_registry[event])
-			end
-			AceEvent:TriggerEvent("AceEvent_EventUnregistered", self, event)
-		end
-	end
-	if AceEvent.onceRegistry then
-		for event, data in pairs(AceEvent.onceRegistry) do
-			data[self] = nil
-		end
-	end
-end
-
-function AceEvent:CancelAllScheduledEvents()
-	if delayRegistry then
-		for k,v in pairs(delayRegistry) do
-			if v.self == self then
-				if not useTablesAsIDs or type(k) == "string" then
-					del(delayRegistry[k])
-				end
-				delayRegistry[k] = nil
-			end
-		end
-		if not next(delayRegistry) then
-			AceEvent.frame:Hide()
-		end
-	end
-end
-
-function AceEvent:IsEventRegistered(event)
-	AceEvent:argCheck(event, 2, "string")
-	local AceEvent_registry = AceEvent.registry
-	if self == AceEvent then
-		return AceEvent_registry[event] and next(AceEvent_registry[event]) or AceEvent_registry[ALL_EVENTS] and next(AceEvent_registry[ALL_EVENTS]) and true or false
-	end
-	if AceEvent_registry[event] and AceEvent_registry[event][self] then
-		return true, AceEvent_registry[event][self]
-	end
-	if AceEvent_registry[ALL_EVENTS] and AceEvent_registry[ALL_EVENTS][self] then
-		return true, AceEvent_registry[ALL_EVENTS][self]
-	end
-	return false, nil
-end
-
-local UnitExists = UnitExists
-local bucketfunc
-function AceEvent:RegisterBucketEvent(event, delay, method, ...)
-	AceEvent:argCheck(event, 2, "string", "table")
-	if type(event) == "table" then
-		for k,v in pairs(event) do
-			if type(k) ~= "number" then
-				AceEvent:error("All keys to argument #2 to `RegisterBucketEvent' must be numbers.")
-			elseif type(v) ~= "string" then
-				AceEvent:error("All values to argument #2 to `RegisterBucketEvent' must be strings.")
-			end
-		end
-	end
-	AceEvent:argCheck(delay, 3, "number")
-	if AceEvent == self then
-		AceEvent:argCheck(method, 4, "function")
-		self = method
-	else
-		if type(event) == "string" then
-			AceEvent:argCheck(method, 4, "string", "function", "nil")
-			if not method then
-				method = event
-			end
-		else
-			AceEvent:argCheck(method, 4, "string", "function")
-		end
-
-		if type(method) == "string" and type(self[method]) ~= "function" then
-			AceEvent:error("Cannot register event %q to method %q, it does not exist", event, method)
-		end
-	end
-	local buckets = AceEvent.buckets
-	if not buckets[event] then
-		buckets[event] = new()
-	end
-	if not buckets[event][self] then
-		local t = new()
-		t.current = new()
-		t.self = self
-		buckets[event][self] = t
-	else
-		AceEvent.CancelScheduledEvent(self, buckets[event][self].id)
-	end
-	local bucket = buckets[event][self]
-	bucket.method = method
-	
-	local n = select('#', ...)
-	if n > 0 then
-		for i = 1, n do
-			bucket[i] = select(i, ...)
-		end
-	end
-	bucket.n = n
-
-	local func = function(arg1)
-		bucket.run = true
-		if arg1 then
-			bucket.current[arg1] = true
-		end
-	end
-	buckets[event][self].func = func
-	local isUnitBucket = true
-	if type(event) == "string" then
-		AceEvent.RegisterEvent(self, event, func)
-		if not event:find("^UNIT_") then
-			isUnitBucket = false
-		end
-	else
-		for _,v in ipairs(event) do
-			AceEvent.RegisterEvent(self, v, func)
-			if isUnitBucket and not v:find("^UNIT_") then
-				isUnitBucket = false
-			end
-		end
-	end
-	bucket.unit = isUnitBucket
-	if not bucketfunc then
-		bucketfunc = function(bucket)
-			local current = bucket.current
-			local method = bucket.method
-			local self = bucket.self
-			if bucket.run then
-				if bucket.unit then
-					for unit in pairs(current) do
-						if not UnitExists(unit) then
-							current[unit] = nil
-						end
-					end
-				end
-				if type(method) == "string" then
-					self[method](self, current, unpack(bucket, 1, bucket.n))
-				elseif method then -- function
-					method(current, unpack(bucket, 1, bucket.n))
-				end
-				for k in pairs(current) do
-					current[k] = nil
-					k = nil
-				end
-				bucket.run = false
-			end
-		end
-	end
-	bucket.id = "AceEvent-Bucket-" .. tostring(bucket)
-	AceEvent.ScheduleRepeatingEvent(self, bucket.id, bucketfunc, delay, bucket)
-end
-
-function AceEvent:IsBucketEventRegistered(event)
-	AceEvent:argCheck(event, 2, "string", "table")
-	return AceEvent.buckets and AceEvent.buckets[event] and AceEvent.buckets[event][self]
-end
-
-function AceEvent:UnregisterBucketEvent(event)
-	AceEvent:argCheck(event, 2, "string", "table")
-	if not AceEvent.buckets or not AceEvent.buckets[event] or not AceEvent.buckets[event][self] then
-		AceEvent:error("Cannot unregister bucket event %q. %q is not registered with it.", event, self)
-	end
-
-	local bucket = AceEvent.buckets[event][self]
-
-	if type(event) == "string" then
-		AceEvent.UnregisterEvent(self, event)
-	else
-		for _,v in ipairs(event) do
-			AceEvent.UnregisterEvent(self, v)
-		end
-	end
-	AceEvent:CancelScheduledEvent(bucket.id)
-
-	bucket.current = del(bucket.current)
-	AceEvent.buckets[event][self] = del(bucket)
-	if not next(AceEvent.buckets[event]) then
-		AceEvent.buckets[event] = del(AceEvent.buckets[event])
-	end
-end
-
-function AceEvent:UnregisterAllBucketEvents()
-	if not AceEvent.buckets or not next(AceEvent.buckets) then
-		return
-	end
-	for k,v in pairs(AceEvent.buckets) do
-		if v == self then
-			AceEvent.UnregisterBucketEvent(self, k)
-			k = nil
-		end
-	end
-end
-
-local combatSchedules
-function AceEvent:CancelAllCombatSchedules()
-	local i = 0
-	while true do
-		i = i + 1
-		if not combatSchedules[i] then
-			break
-		end
-		local v = combatSchedules[i]
-		if v.self == self then
-			v = del(v)
-			table.remove(combatSchedules, i)
-			i = i - 1
-		end
-	end
-end
-
-local inCombat = false
-
-function AceEvent:PLAYER_REGEN_DISABLED()
-	inCombat = true
-end
-
-do
-	local tmp = {}
-	function AceEvent:PLAYER_REGEN_ENABLED()
-		inCombat = false
-		for i, v in ipairs(combatSchedules) do
-			tmp[i] = v
-			combatSchedules[i] = nil
-		end
-		for i, v in ipairs(tmp) do
-			local func = v.func
-			if func then
-				local success, err = pcall(func, unpack(v, 1, v.n))
-				if not success then geterrorhandler()(err:find("%.lua:%d+:") and err or (debugstack():match("(.-: )in.-\n") or "") .. err) end
-			else
-				local obj = v.obj or v.self
-				local method = v.method
-				local obj_method = obj[method]
-				if obj_method then
-					local success, err = pcall(obj_method, obj, unpack(v, 1, v.n))
-					if not success then geterrorhandler()(err:find("%.lua:%d+:") and err or (debugstack():match("(.-: )in.-\n") or "") .. err) end
-				end
-			end
-			tmp[i] = del(v)
-		end
-	end
-end
-
-function AceEvent:ScheduleLeaveCombatAction(method, ...)
-	local style = type(method)
-	if self == AceEvent then
-		if style == "table" then
-			local func = (...)
-			AceEvent:argCheck(func, 3, "string")
-			if type(method[func]) ~= "function" then
-				AceEvent:error("Cannot schedule a combat action to method %q, it does not exist", func)
-			end
-		else
-			AceEvent:argCheck(method, 2, "function", --[[so message is right]] "table")
-		end
-		self = method
-	else
-		AceEvent:argCheck(method, 2, "function", "string", "table")
-		if style == "string" and type(self[method]) ~= "function" then
-			AceEvent:error("Cannot schedule a combat action to method %q, it does not exist", method)
-		elseif style == "table" then
-			local func = (...)
-			AceEvent:argCheck(func, 3, "string")
-			if type(method[func]) ~= "function" then
-				AceEvent:error("Cannot schedule a combat action to method %q, it does not exist", func)
-			end
-		end
-	end
-	
-	if not inCombat then
-		local success, err
-		if type(method) == "function" then
-			success, err = pcall(method, ...)
-		elseif type(method) == "table" then
-			local func = (...)
-			success, err = pcall(method[func], method, select(2, ...))
-		else
-			success, err = pcall(self[method], self, ...)
-		end
-		if not success then geterrorhandler()(err:find("%.lua:%d+:") and err or (debugstack():match("(.-: )in.-\n") or "") .. err) end
-		return
-	end
-	local t
-	local n = select('#', ...)
-	if style == "table" then
-		t = new(select(2, ...))
-		t.obj = method
-		t.method = (...)
-		t.n = n-1
-	else
-		t = new(...)
-		t.n = n
-		if style == "function" then
-			t.func = method
-		else
-			t.method = method
-		end
-	end
-	t.self = self
-	table.insert(combatSchedules, t)
-end
-
-function AceEvent:OnEmbedDisable(target)
-	self.UnregisterAllEvents(target)
-
-	self.CancelAllScheduledEvents(target)
-
-	self.UnregisterAllBucketEvents(target)
-	
-	self.CancelAllCombatSchedules(target)
-end
-
-function AceEvent:IsFullyInitialized()
-	return self.postInit or false
-end
-
-function AceEvent:IsPostPlayerLogin()
-	return IsLoggedIn() and true or false
-end
-
-local function activate(self, oldLib, oldDeactivate)
-	AceEvent = self
-	
-	self.onceRegistry = oldLib and oldLib.onceRegistry or {}
-	self.throttleRegistry = oldLib and oldLib.throttleRegistry or {}
-	self.delayRegistry = oldLib and oldLib.delayRegistry or {}
-	self.buckets = oldLib and oldLib.buckets or {}
-	self.registry = oldLib and oldLib.registry or {}
-	self.frame = oldLib and oldLib.frame or CreateFrame("Frame", "AceEvent20Frame")
-	self.playerLogin = IsLoggedIn() and true
-	self.postInit = oldLib and oldLib.postInit or self.playerLogin and ChatTypeInfo and ChatTypeInfo.WHISPER and ChatTypeInfo.WHISPER.r and true
-	self.ALL_EVENTS = oldLib and oldLib.ALL_EVENTS or _G.newproxy()
-	self.FAKE_NIL = oldLib and oldLib.FAKE_NIL or _G.newproxy()
-	self.RATE = oldLib and oldLib.RATE or _G.newproxy()
-	self.combatSchedules = oldLib and oldLib.combatSchedules or {}
-	self.UID_NUM = oldLib and oldLib.UID_NUM or 0
-	
-	-- Delete this down the road.  Makes sure that the addonframes from revisions 33121 - 36174 get their events unregistered.
-	local addonframes = oldLib and oldLib.addonframes
-	if addonframes then
-		for _, v in pairs(addonframes) do
-			v:UnregisterAllEvents()
-		end
-	end
-	
-	combatSchedules = self.combatSchedules
-	ALL_EVENTS = self.ALL_EVENTS
-	FAKE_NIL = self.FAKE_NIL
-	RATE = self.RATE
-	local inPlw = false
-	local blacklist = {
-		UNIT_INVENTORY_CHANGED = true,
-		BAG_UPDATE = true,
-		ITEM_LOCK_CHANGED = true,
-		ACTIONBAR_SLOT_CHANGED = true,
-	}
-	self.frame:SetScript("OnEvent", function(_, event, ...)
-		if event == "PLAYER_ENTERING_WORLD" then
-			inPlw = false
-		elseif event == "PLAYER_LEAVING_WORLD" then
-			inPlw = true
-		end
-		if event and (not inPlw or not blacklist[event]) then
-			self:TriggerEvent(event, ...)
-		end
-	end)
-	if self.delayRegistry then
-		delayRegistry = self.delayRegistry
-		self.frame:SetScript("OnUpdate", OnUpdate)
-	end
-
-	self:UnregisterAllEvents()
-	self:CancelAllScheduledEvents()
-
-	registeringFromAceEvent = true
-	self:RegisterEvent("LOOT_OPENED", function()
-		SendAddonMessage("LOOT_OPENED", "", "RAID")
-	end)
-	registeringFromAceEvent = nil
-
-	local function handleFullInit()
-		if not self.postInit then
-			local function func()
-				self.postInit = true
-				self:TriggerEvent("AceEvent_FullyInitialized")
-				if self.registry["CHAT_MSG_CHANNEL_NOTICE"] and self.registry["CHAT_MSG_CHANNEL_NOTICE"][self] then
-					self:UnregisterEvent("CHAT_MSG_CHANNEL_NOTICE")
-				end
-				if self.registry["MEETINGSTONE_CHANGED"] and self.registry["MEETINGSTONE_CHANGED"][self] then
-					self:UnregisterEvent("MEETINGSTONE_CHANGED")
-				end
-				if self.registry["MINIMAP_ZONE_CHANGED"] and self.registry["MINIMAP_ZONE_CHANGED"][self] then
-					self:UnregisterEvent("MINIMAP_ZONE_CHANGED")
-				end
-				if self.registry["LANGUAGE_LIST_CHANGED"] and self.registry["LANGUAGE_LIST_CHANGED"][self] then
-					self:UnregisterEvent("LANGUAGE_LIST_CHANGED")
-				end
-				collectgarbage('collect')
-			end
-			registeringFromAceEvent = true
-			local f = function()
-				self.playerLogin = true
-				self:ScheduleEvent("AceEvent_FullyInitialized", func, 1)
-			end
-			self:RegisterEvent("MEETINGSTONE_CHANGED", f, true)
-			self:RegisterEvent("CHAT_MSG_CHANNEL_NOTICE", function()
-				self:ScheduleEvent("AceEvent_FullyInitialized", func, 0.15)
-			end)
-			self:RegisterEvent("LANGUAGE_LIST_CHANGED", function()
-				if self.registry["MEETINGSTONE_CHANGED"] and self.registry["MEETINGSTONE_CHANGED"][self] then
-					registeringFromAceEvent = true
-					self:UnregisterEvent("MEETINGSTONE_CHANGED")
-					self:RegisterEvent("MINIMAP_ZONE_CHANGED", fd, true)
-					registeringFromAceEvent = nil
-				end
-			end)
-			self:ScheduleEvent("AceEvent_FullyInitialized", func, 10)
-			registeringFromAceEvent = nil
-		end
-	end
-	
-	if not self.playerLogin then
-		registeringFromAceEvent = true
-		self:RegisterEvent("PLAYER_LOGIN", function()
-			self.playerLogin = true
-			handleFullInit()
-			handleFullInit = nil
-			collectgarbage('collect')
-		end, true)
-		registeringFromAceEvent = nil
-	else
-		handleFullInit()
-		handleFullInit = nil
-	end
-	
-	if not AceEvent20EditBox then
-	    CreateFrame("Editbox", "AceEvent20EditBox")
-	end
-	local editbox = AceEvent20EditBox
-	function editbox:Execute(line)
-		local defaulteditbox = DEFAULT_CHAT_FRAME.editBox
-		self:SetAttribute("chatType", defaulteditbox:GetAttribute("chatType"))
-		self:SetAttribute("tellTarget", defaulteditbox:GetAttribute("tellTarget"))
-		self:SetAttribute("channelTarget", defaulteditbox:GetAttribute("channelTarget"))
-		self:SetText(line)
-		ChatEdit_SendText(self)
-	end
-	editbox:Hide()
-	_G["SLASH_IN1"] = "/in"
-	SlashCmdList["IN"] = function(msg)
-		local seconds, command, rest = msg:match("^([^%s]+)%s+(/[^%s]+)(.*)$")
-		seconds = tonumber(seconds)
-		if not seconds then
-			DEFAULT_CHAT_FRAME:AddMessage("Error, bad arguments to /in. Must be in the form of `/in 5 /say hi'")
-			return
-		end
-		if IsSecureCmd(command) then
-			DEFAULT_CHAT_FRAME:AddMessage(("Error, /in cannot call secure command: %s"):format(command))
-			return
-		end
-		self:ScheduleEvent("AceEventSlashIn-" .. math.random(1, 1000000000), editbox.Execute, seconds, editbox, command .. rest)
-	end
-	registeringFromAceEvent = true
-	self:RegisterEvent("PLAYER_REGEN_ENABLED")
-	self:RegisterEvent("PLAYER_REGEN_DISABLED")
-	inCombat = InCombatLockdown()
-	registeringFromAceEvent = nil
-	
-	-- another hack to make sure that we clean up properly from rev 33121 - 36174
-	if self.registry[ALL_EVENTS] then
-		self.frame:RegisterAllEvents()
-	else
-		for event in pairs(self.registry) do
-			self.frame:RegisterEvent(event)
-		end
-	end
-	
-	self:activate(oldLib, oldDeactivate)
-	if oldLib then
-		oldDeactivate(oldLib)
-	end
-end
-
-AceLibrary:Register(AceEvent, MAJOR_VERSION, MINOR_VERSION, activate)
--- a/modules/FuBar_ReActionFu/lib/AceEvent-2.0/AceEvent-2.0.toc	Wed Apr 02 18:22:02 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,12 +0,0 @@
-## Interface: 20200
-
-## Title: Lib: AceEvent-2.0
-## Notes: AddOn development framework
-## Author: Ace Development Team
-## LoadOnDemand: 1
-## X-Website: http://www.wowace.com
-## X-Category: Library
-## X-License: LGPL v2.1 + MIT for AceOO-2.0
-## Dependencies: AceLibrary, AceOO-2.0
- 
-AceEvent-2.0.lua
--- a/modules/FuBar_ReActionFu/lib/AceOO-2.0/AceOO-2.0.lua	Wed Apr 02 18:22:02 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,980 +0,0 @@
---[[
-Name: AceOO-2.0
-Revision: $Rev: 38641 $
-Developed by: The Ace Development Team (http://www.wowace.com/index.php/The_Ace_Development_Team)
-Inspired By: Ace 1.x by Turan (turan@gryphon.com)
-Website: http://www.wowace.com/
-Documentation: http://www.wowace.com/index.php/AceOO-2.0
-SVN: http://svn.wowace.com/root/trunk/Ace2/AceOO-2.0
-Description: Library to provide an object-orientation framework.
-Dependencies: AceLibrary
-License: MIT
-]]
-
-local MAJOR_VERSION = "AceOO-2.0"
-local MINOR_VERSION = "$Revision: 38641 $"
-
--- This ensures the code is only executed if the libary doesn't already exist, or is a newer version
-if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary.") end
-if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end
-
-local AceOO = {
-	error = AceLibrary.error,
-	argCheck = AceLibrary.argCheck
-}
-
--- @function	getuid
--- @brief		Obtain a unique string identifier for the object in question.
--- @param t		The object to obtain the uid for.
--- @return		The uid string.
-local function getuid(t)
-	local mt = getmetatable(t)
-	setmetatable(t, nil)
-	local str = tostring(t)
-	setmetatable(t, mt)
-	local cap = str:match("[^:]*: 0x(.*)$") or str:match("[^:]*: (.*)$")
-	if cap then
-		return ("0"):rep(8 - #cap) .. cap
-	end
-end
-
-local function getlibrary(o)
-	if type(o) == "table" then
-		return o
-	elseif type(o) == "string" then
-		if not AceLibrary:HasInstance(o) then
-			AceOO:error("Library %q does not exist.", o)
-		end
-		return AceLibrary(o)
-	end
-end
-
-local function deeprawget(self, k)
-	while true do
-		local v = rawget(self, k)
-		if v ~= nil then
-			return v
-		end
-		local mt = getmetatable(self)
-		if not mt or type(mt.__index) ~= "table" then
-			return nil
-		end
-		self = mt.__index
-	end
-end
-
--- @function		Factory
--- @brief			Construct a factory for the creation of objects.
--- @param obj		The object whose init method will be called on the new factory
---					object.
--- @param newobj	The object whose init method will be called on the new
---					objects that the Factory creates, to initialize them.
--- @param (...) Arguments which will be passed to obj.init() in addition
---					to the Factory object.
--- @return			The new factory which creates a newobj when its new method is called,
---					or when it is called directly (__call metamethod).
-local Factory
-do
-	local function getlibraries(...)
-		if select('#', ...) == 0 then
-			return
-		end
-		return getlibrary((select(1, ...))), getlibraries(select(2, ...))
-	end
-	local arg = {}
-	local function new(obj, ...)
-		local t = {}
-		local uid = getuid(t)
-		obj:init(t, getlibraries(...))
-		t.uid = uid
-		return t
-	end
-	
-	local function createnew(self, ...)
-		local o = self.prototype
-		local x = new(o, getlibraries(...))
-		return x
-	end
-
-	function Factory(obj, newobj, ...)
-		local t = new(obj, ...)
-		t.prototype = newobj
-		t.new = createnew
-		getmetatable(t).__call = t.new
-		return t
-	end
-end
-
-
-local function objtostring(self)
-	if self.ToString then
-		return self:ToString()
-	elseif self.GetLibraryVersion then
-		return (self:GetLibraryVersion())
-	elseif self.super then
-		local s = "Sub-" .. tostring(self.super)
-		local first = true
-		if self.interfaces then
-			for interface in pairs(self.interfaces) do
-				if first then
-					s = s .. "(" .. tostring(interface)
-					first = false
-				else
-					s = s .. ", " .. tostring(interface)
-				end
-			end
-		end
-		if self.mixins then
-			for mixin in pairs(self.mixins) do
-				if first then
-					s = s .. tostring(mixin)
-					first = false
-				else
-					s = s .. ", " .. tostring(mixin)
-				end
-			end
-		end
-		if first then
-			if self.uid then
-				return s .. ":" .. self.uid
-			else
-				return s
-			end
-		else
-			return s .. ")"
-		end
-	else
-		return self.uid and 'Subclass:' .. self.uid or 'Subclass'
-	end
-end
-
--- @table			Object
--- @brief			Base of all objects, including Class.
---
--- @method			init
--- @brief			Initialize a new object.
--- @param newobject The object to initialize
--- @param class		The class to make newobject inherit from
-local Object
-do
-	Object = {}
-	function Object:init(newobject, class)
-		local parent = class or self
-		if not rawget(newobject, 'uid') then
-			newobject.uid = getuid(newobject)
-		end
-		local mt = {
-			__index = parent,
-			__tostring = objtostring,
-		}
-		setmetatable(newobject, mt)
-	end
-	Object.uid = getuid(Object)
-	setmetatable(Object, { __tostring = function() return 'Object' end })
-end
-
-local Interface
-
-local function validateInterface(object, interface)
-	if not object.class and object.prototype then
-		object = object.prototype
-	end
-	for k,v in pairs(interface.interface) do
-		if tostring(type(object[k])) ~= v then
-			return false
-		end
-	end
-	if interface.superinterfaces then
-		for superinterface in pairs(interface.superinterfaces) do
-			if not validateInterface(object, superinterface) then
-				return false
-			end
-		end
-	end
-	if type(object.class) == "table" and rawequal(object.class.prototype, object) then
-		if not object.class.interfaces then
-			rawset(object.class, 'interfaces', {})
-		end
-		object.class.interfaces[interface] = true
-	elseif type(object.class) == "table" and type(object.class.prototype) == "table" then
-		validateInterface(object.class.prototype, interface)
-		-- check if class is proper, thus preventing future checks.
-	end
-	return true
-end
-
--- @function		inherits
--- @brief			Return whether an Object or Class inherits from a given
---					parent.
--- @param object	Object or Class to check
--- @param parent	Parent to test inheritance from
--- @return			whether an Object or Class inherits from a given
---					parent.
-local function inherits(object, parent)
-	object = getlibrary(object)
-	if type(parent) == "string" then
-		if not AceLibrary:HasInstance(parent) then
-			return false
-		else
-			parent = AceLibrary(parent)
-		end
-	end
-	AceOO:argCheck(parent, 2, "table")
-	if type(object) ~= "table" then
-		return false
-	end
-	local current
-	local class = deeprawget(object, 'class')
-	if class then
-		current = class
-	else
-		current = object
-	end
-	if type(current) ~= "table" then
-		return false
-	end
-	if rawequal(current, parent) then
-		return true
-	end
-	if parent.class then
-		while true do
-			if rawequal(current, Object) then
-				break
-			end
-			if current.mixins then
-				for mixin in pairs(current.mixins) do
-					if rawequal(mixin, parent) then
-						return true
-					end
-				end
-			end
-			if current.interfaces then
-				for interface in pairs(current.interfaces) do
-					if rawequal(interface, parent) then
-						return true
-					end
-				end
-			end
-			current = deeprawget(current, 'super')
-			if type(current) ~= "table" then
-				break
-			end
-		end
-		
-		local isInterface = false
-		local curr = parent.class
-		while true do
-			if rawequal(curr, Object) then
-				break
-			elseif rawequal(curr, Interface) then
-				isInterface = true
-				break
-			end
-			curr = deeprawget(curr, 'super')
-			if type(curr) ~= "table" then
-				break
-			end
-		end
-		return isInterface and validateInterface(object, parent)
-	else
-		while true do
-			if rawequal(current, parent) then
-				return true
-			elseif rawequal(current, Object) then
-				return false
-			end
-			current = deeprawget(current, 'super')
-			if type(current) ~= "table" then
-				return false
-			end
-		end
-	end
-end
-
--- @table			Class
--- @brief			An object factory which sets up inheritence and supports
---					'mixins'.
---
--- @metamethod		Class call
--- @brief			Call ClassFactory:new() to create a new class.
---
--- @method			Class new
--- @brief			Construct a new object.
--- @param (...) Arguments to pass to the object init function.
--- @return			The new object.
---
--- @method			Class init
--- @brief			Initialize a new class.
--- @param parent	Superclass.
--- @param (...) Mixins.
---
--- @method			Class ToString
--- @return			A string representing the object, in this case 'Class'.
-local initStatus
-local Class
-local Mixin
-local autoEmbed = false
-local function traverseInterfaces(bit, total)
-	if bit.superinterfaces then
-		for interface in pairs(bit.superinterfaces) do
-			if not total[interface] then
-				total[interface] = true
-				traverseInterfaces(interface, total)
-			end
-		end
-	end
-end
-local class_new
-do
-	Class = Factory(Object, setmetatable({}, {__index = Object}), Object)
-	Class.super = Object
-	
-	local function protostring(t)
-		return '<' .. tostring(t.class) .. ' prototype>'
-	end
-	local function classobjectstring(t)
-		if t.ToString then
-			return t:ToString()
-		elseif t.GetLibraryVersion then
-			return (t:GetLibraryVersion())
-		else
-			return '<' .. tostring(t.class) .. ' instance>'
-		end
-	end
-	local function classobjectequal(self, other)
-		if type(self) == "table" and self.Equals then
-			return self:Equals(other)
-		elseif type(other) == "table" and other.Equals then
-			return other:Equals(self)
-		elseif type(self) == "table" and self.CompareTo then
-			return self:CompareTo(other) == 0
-		elseif type(other) == "table" and other.CompareTo then
-			return other:CompareTo(self) == 0
-		else
-			return rawequal(self, other)
-		end
-	end
-	local function classobjectlessthan(self, other)
-		if type(self) == "table" and self.IsLessThan then
-			return self:IsLessThan(other)
-		elseif type(other) == "table" and other.IsLessThanOrEqualTo then
-			return not other:IsLessThanOrEqualTo(self)
-		elseif type(self) == "table" and self.CompareTo then
-			return self:CompareTo(other) < 0
-		elseif type(other) == "table" and other.CompareTo then
-			return other:CompareTo(self) > 0
-		elseif type(other) == "table" and other.IsLessThan and other.Equals then
-			return other:Equals(self) or other:IsLessThan(self)
-		else
-			AceOO:error("cannot compare two objects")
-		end
-	end
-	local function classobjectlessthanequal(self, other)
-		if type(self) == "table" and self.IsLessThanOrEqualTo then
-			return self:IsLessThanOrEqualTo(other)
-		elseif type(other) == "table" and other.IsLessThan then
-			return not other:IsLessThan(self)
-		elseif type(self) == "table" and self.CompareTo then
-			return self:CompareTo(other) <= 0
-		elseif type(other) == "table" and other.CompareTo then
-			return other:CompareTo(self) >= 0
-		elseif type(self) == "table" and self.IsLessThan and self.Equals then
-			return self:Equals(other) or self:IsLessThan(other)
-		else
-			AceOO:error("cannot compare two incompatible objects")
-		end
-	end
-	local function classobjectadd(self, other)
-		if type(self) == "table" and self.Add then
-			return self:Add(other)
-		else
-			AceOO:error("cannot add two incompatible objects")
-		end
-	end
-	local function classobjectsub(self, other)
-		if type(self) == "table" and self.Subtract then
-			return self:Subtract(other)
-		else
-			AceOO:error("cannot subtract two incompatible objects")
-		end
-	end
-	local function classobjectunm(self, other)
-		if type(self) == "table" and self.UnaryNegation then
-			return self:UnaryNegation(other)
-		else
-			AceOO:error("attempt to negate an incompatible object")
-		end
-	end
-	local function classobjectmul(self, other)
-		if type(self) == "table" and self.Multiply then
-			return self:Multiply(other)
-		else
-			AceOO:error("cannot multiply two incompatible objects")
-		end
-	end
-	local function classobjectdiv(self, other)
-		if type(self) == "table" and self.Divide then
-			return self:Divide(other)
-		else
-			AceOO:error("cannot divide two incompatible objects")
-		end
-	end
-	local function classobjectpow(self, other)
-		if type(self) == "table" and self.Exponent then
-			return self:Exponent(other)
-		else
-			AceOO:error("cannot exponentiate two incompatible objects")
-		end
-	end
-	local function classobjectconcat(self, other)
-		if type(self) == "table" and self.Concatenate then
-			return self:Concatenate(other)
-		else
-			AceOO:error("cannot concatenate two incompatible objects")
-		end
-	end
-	function class_new(self, ...)
-		if self.virtual then
-			AceOO:error("Cannot instantiate a virtual class.")
-		end
-		
-		local o = self.prototype
-		local newobj = {}
-		if o.class and o.class.instancemeta then
-			setmetatable(newobj, o.class.instancemeta)
-		else
-			Object:init(newobj, o)
-		end
-		
-		if self.interfaces and not self.interfacesVerified then
-			-- Verify the interfaces
-			
-			for interface in pairs(self.interfaces) do
-				for field,kind in pairs(interface.interface) do
-					if tostring(type(newobj[field])) ~= kind then
-						AceOO:error("Class did not satisfy all interfaces. %q is required to be a %s. It is a %s", field, kind, tostring(type(newobj[field])))
-					end
-				end
-			end
-			self.interfacesVerified = true
-		end
-		local tmp = initStatus
-		initStatus = newobj
-		newobj:init(...)
-		if initStatus then
-			initStatus = tmp
-			AceOO:error("Initialization not completed, be sure to call the superclass's init method.")
-			return
-		end
-		initStatus = tmp
-		return newobj
-	end
-	local classmeta = {
-		__tostring = objtostring,
-		__call = function(self, ...)
-			return self:new(...)
-		end,
-	}
-	function Class:init(newclass, parent, ...)
-		parent = parent or self
-		
-		local total
-		
-		if parent.class then
-			total = { parent, ... }
-			parent = self
-		else
-			total = { ... }
-		end
-		if not inherits(parent, Class) then
-			AceOO:error("Classes must inherit from a proper class")
-		end
-		if parent.sealed then
-			AceOO:error("Cannot inherit from a sealed class")
-		end
-		for i,v in ipairs(total) do
-			if inherits(v, Mixin) and v.class then
-				if v.__deprecated then
-					AceOO:error(v.__deprecated)
-				end
-				if not newclass.mixins then
-					newclass.mixins = {}
-				end
-				if newclass.mixins[v] then
-					AceOO:error("Cannot explicitly inherit from the same mixin twice")
-				end
-				newclass.mixins[v] = true
-			elseif inherits(v, Interface) and v.class then
-				if not newclass.interfaces then
-					newclass.interfaces = {}
-				end
-				if newclass.interfaces[v] then
-					AceOO:error("Cannot explicitly inherit from the same interface twice")
-				end
-				newclass.interfaces[v] = true
-			else
-				AceOO:error("Classes can only inherit from one or zero classes and any number of mixins or interfaces")
-			end
-		end
-		if parent.interfaces then
-			if not newclass.interfaces then
-				newclass.interfaces = {}
-			end
-			for interface in pairs(parent.interfaces) do
-				newclass.interfaces[interface] = true
-			end
-		end
-		for k in pairs(total) do
-			total[k] = nil
-		end
-		
-		newclass.super = parent
-		
-		newclass.prototype = setmetatable(total, {
-			__index = parent.prototype,
-			__tostring = protostring,
-		})
-		total = nil
-		
-		newclass.instancemeta = {
-			__index = newclass.prototype,
-			__tostring = classobjectstring,
-			__eq = classobjectequal,
-			__lt = classobjectlessthan,
-			__le = classobjectlessthanequal,
-			__add = classobjectadd,
-			__sub = classobjectsub,
-			__unm = classobjectunm,
-			__mul = classobjectmul,
-			__div = classobjectdiv,
-			__pow = classobjectpow,
-			__concat = classobjectconcat,
-		}
-		
-		setmetatable(newclass, classmeta)
-		
-		newclass.new = class_new
-		
-		if newclass.mixins then
-			-- Fold in the mixins
-			local err, msg
-			for mixin in pairs(newclass.mixins) do
-				local ret
-				autoEmbed = true
-				ret, msg = pcall(mixin.embed, mixin, newclass.prototype)
-				autoEmbed = false
-				if not ret then
-					err = true
-					break
-				end
-			end
-	
-			if err then
-				local pt = newclass.prototype
-				for k,v in pairs(pt) do
-					pt[k] = nil
-				end
-	
-				-- method conflict
-				AceOO:error(msg)
-			end
-		end
-		
-		newclass.prototype.class = newclass
-		
-		if newclass.interfaces then
-			for interface in pairs(newclass.interfaces) do
-				traverseInterfaces(interface, newclass.interfaces)
-			end
-		end
-		if newclass.mixins then
-			for mixin in pairs(newclass.mixins) do
-				if mixin.interfaces then
-					if not newclass.interfaces then
-						newclass.interfaces = {}
-					end
-					for interface in pairs(mixin.interfaces) do
-						newclass.interfaces[interface] = true
-					end
-				end
-			end
-		end
-	end
-	function Class:ToString()
-		if type(self.GetLibraryVersion) == "function" then
-			return (self:GetLibraryVersion())
-		else
-			return "Class"
-		end
-	end
-	
-	local tmp
-	function Class.prototype:init()
-		if rawequal(self, initStatus) then
-			initStatus = nil
-		else
-			AceOO:error("Improper self passed to init. You must do MyClass.super.prototype.init(self, ...)", 2)
-		end
-		self.uid = getuid(self)
-		local current = self.class
-		while true do
-			if current == Class then
-				break
-			end
-			if current.mixins then
-				for mixin in pairs(current.mixins) do
-					if type(mixin.OnInstanceInit) == "function" then
-						mixin:OnInstanceInit(self)
-					end
-				end
-			end
-			current = current.super
-		end
-	end
-end
-
-
--- @object	ClassFactory
--- @brief	A factory for creating classes.	Rarely used directly.
-local ClassFactory = Factory(Object, Class, Object)
-
-function Class:new(...)
-	local x = ClassFactory:new(...)
-	if AceOO.classes then
-		AceOO.classes[x] = true
-	end
-	return x
-end
-getmetatable(Class).__call = Class.new
-
--- @class	Mixin
--- @brief	A class to create mixin objects, which contain methods that get
--- "mixed in" to class prototypes.
---
--- @object	Mixin prototype
--- @brief	The prototype that mixin objects inherit their methods from.
---
--- @method	Mixin prototype embed
--- @brief	Mix in the methods of our object which are listed in our interface
---		 to the supplied target table.
---
--- @method	Mixin prototype init
--- @brief	Initialize the mixin object.
--- @param	newobj	 The new object we're initializing.
--- @param	interface	The interface we implement (the list of methods our
---					prototype provides which should be mixed into the target
---					table by embed).
-do
-	Mixin = Class()
-	function Mixin:ToString()
-		if self.GetLibraryVersion then
-			return (self:GetLibraryVersion())
-		else
-			return 'Mixin'
-		end
-	end
-	local function _Embed(state, field, target)
-		field = next(state.export, field)
-		if field == nil then
-			return
-		end
-
-		if rawget(target, field) or (target[field] and target[field] ~= state[field]) then
-			AceOO:error("Method conflict in attempt to mixin. Field %q", field)
-		end
-
-		target[field] = state[field]
-
-		local ret,msg = pcall(_Embed, state, field, target)
-		if not ret then
-			-- Mix in the next method according to the defined interface.	If that
-			-- fails due to a conflict, re-raise to back out the previous mixed
-			-- methods.
-
-			target[field] = nil
-			AceOO:error(msg)
-		end
-	end
-	function Mixin.prototype:embed(target)
-		if self.__deprecated then
-			AceOO:error(self.__deprecated)
-		end
-		local mt = getmetatable(target)
-		setmetatable(target, nil)
-		local err, msg = pcall(_Embed, self, nil, target)
-		if not err then
-			setmetatable(target, mt)
-			AceOO:error(msg)
-			return
-		end
-		if type(self.embedList) == "table" then
-			self.embedList[target] = true
-		end
-		if type(target.class) ~= "table" then
-			target[self] = true
-		end
-		if not autoEmbed and type(self.OnManualEmbed) == "function" then
-			self:OnManualEmbed(target)
-		end
-		setmetatable(target, mt)
-	end
-	
-	function Mixin.prototype:activate(oldLib, oldDeactivate)
-		if oldLib and oldLib.embedList then
-			for target in pairs(oldLib.embedList) do
-				local mt = getmetatable(target)
-				setmetatable(target, nil)
-				for field in pairs(oldLib.export) do
-					target[field] = nil
-				end
-				setmetatable(target, mt)
-			end
-			self.embedList = oldLib.embedList
-			for target in pairs(self.embedList) do
-				self:embed(target)
-			end
-		else
-			self.embedList = setmetatable({}, {__mode="k"})
-		end
-	end
-	
-	function Mixin.prototype:init(export, ...)
-		AceOO:argCheck(export, 2, "table")
-		for k,v in pairs(export) do
-			if type(k) ~= "number" then
-				AceOO:error("All keys to argument #2 must be numbers.")
-			elseif type(v) ~= "string" then
-				AceOO:error("All values to argument #2 must be strings.")
-			end
-		end
-		local num = #export
-		for i = 1, num do
-			local v = export[i]
-			export[i] = nil
-			export[v] = true
-		end
-		
-		local interfaces
-		if select('#', ...) >= 1 then
-			interfaces = { ... }
-			for i,v in ipairs(interfaces) do
-				v = getlibrary(v)
-				interfaces[i] = v
-				if not v.class or not inherits(v, Interface) then
-					AceOO:error("Mixins can inherit only from interfaces")
-				end
-			end
-			local num = #interfaces
-			for i = 1, num do
-				local v = interfaces[i]
-				interfaces[i] = nil
-				interfaces[v] = true
-			end
-			for interface in pairs(interfaces) do
-				traverseInterfaces(interface, interfaces)
-			end
-			for interface in pairs(interfaces) do
-				for field,kind in pairs(interface.interface) do
-					if kind ~= "nil" then
-						local good = false
-						for bit in pairs(export) do
-							if bit == field then
-								good = true
-								break
-							end
-						end
-						if not good then
-							AceOO:error("Mixin does not fully accommodate field %q", field)
-						end
-					end
-				end
-			end
-		end
-		self.super = Mixin.prototype
-		Mixin.super.prototype.init(self)
-		self.export = export
-		self.interfaces = interfaces
-	end
-end
-
--- @class Interface
--- @brief A class to create interfaces, which contain contracts that classes
---			which inherit from this must comply with.
---
--- @object Interface prototype
--- @brief	The prototype that interface objects must adhere to.
---
--- @method Interface prototype init
--- @brief	Initialize the mixin object.
--- @param	interface	The interface we contract (the hash of fields forced).
--- @param	(...)	Superinterfaces
-do
-	Interface = Class()
-	function Interface:ToString()
-		if self.GetLibraryVersion then
-			return (self:GetLibraryVersion())
-		else
-			return 'Instance'
-		end
-	end
-	function Interface.prototype:init(interface, ...)
-		Interface.super.prototype.init(self)
-		AceOO:argCheck(interface, 2, "table")
-		for k,v in pairs(interface) do
-			if type(k) ~= "string" then
-				AceOO:error("All keys to argument #2 must be numbers.")
-			elseif type(v) ~= "string" then
-				AceOO:error("All values to argument #2 must be strings.")
-			elseif v ~= "nil" and v ~= "string" and v ~= "number" and v ~= "table" and v ~= "function" then
-				AceOO:error('All values to argument #2 must either be "nil", "string", "number", "table", or "function".')
-			end
-		end
-		if select('#', ...) >= 1 then
-			self.superinterfaces = { ... }
-			for i,v in ipairs(self.superinterfaces) do
-				v = getlibrary(v)
-				self.superinterfaces[i] = v
-				if not inherits(v, Interface) or not v.class then
-					AceOO:error('Cannot provide a non-Interface to inherit from')
-				end
-			end
-			local num = #self.superinterfaces
-			for i = 1, num do
-				local v = self.superinterfaces[i]
-				self.superinterfaces[i] = nil
-				self.superinterfaces[v] = true
-			end
-		end
-		self.interface = interface
-	end
-end
-
--- @function Classpool
--- @brief	Obtain a read only class from our pool of classes, indexed by the
---		 superclass and mixins.
--- @param	sc		 The superclass of the class we want.
--- @param	(m1..m20)	Mixins of the class we want's objects.
--- @return A read only class from the class pool.
-local Classpool
-do
-	local pool = setmetatable({}, {__mode = 'v'})
-	local function newindex(k, v)
-		AceOO:error('Attempt to modify a read-only class.')
-	end
-	local function protonewindex(k, v)
-		AceOO:error('Attempt to modify a read-only class prototype.')
-	end
-	local function ts(bit)
-		if type(bit) ~= "table" then
-			return tostring(bit)
-		elseif getmetatable(bit) and bit.__tostring then
-			return tostring(bit)
-		elseif type(bit.GetLibraryVersion) == "function" then
-			return bit:GetLibraryVersion()
-		else
-			return tostring(bit)
-		end
-	end
-	local t = {}
-	local function getcomplexuid(sc, ...)
-		if sc then
-			if sc.uid then
-				table.insert(t, sc.uid)
-			else
-				AceOO:error("%s is not an appropriate class/mixin", ts(sc))
-			end
-		end
-		for i = 1, select('#', ...) do
-			local m = select(i, ...)
-			if m.uid then
-				table.insert(t, m.uid)
-			else
-				AceOO:error("%s is not an appropriate mixin", ts(m))
-			end
-		end
-		table.sort(t)
-		local uid = table.concat(t, '')
-		local num = #t
-		for i = 1, num do
-			t[i] = nil
-		end
-		return uid
-	end
-	local classmeta
-	local arg = {}
-	function Classpool(superclass, ...)
-		local l = getlibrary
-		superclass = getlibrary(superclass)
-		arg = { ... }
-		for i, v in ipairs(arg) do
-			arg[i] = getlibrary(v)
-		end
-		if superclass then
-			if superclass.class then -- mixin
-				table.insert(arg, 1, superclass)
-				superclass = Class
-			end
-		else
-			superclass = Class
-		end
-		local key = getcomplexuid(superclass, unpack(arg))
-		if not pool[key] then
-			local class = Class(superclass, unpack(arg))
-			if not classmeta then
-				classmeta = {}
-				local mt = getmetatable(class)
-				for k,v in pairs(mt) do
-					classmeta[k] = v
-				end
-				classmeta.__newindex = newindex
-			end
-			-- Prevent the user from adding methods to this class.
-			-- NOTE: I'm not preventing modifications of existing class members,
-			-- but it's likely that only a truly malicious user will be doing so.
-			class.sealed = true
-			setmetatable(class, classmeta)
-			getmetatable(class.prototype).__newindex = protonewindex
-			pool[key] = class
-		end
-		return pool[key]
-	end
-end
-
-AceOO.Factory = Factory
-AceOO.Object = Object
-AceOO.Class = Class
-AceOO.Mixin = Mixin
-AceOO.Interface = Interface
-AceOO.Classpool = Classpool
-AceOO.inherits = inherits
-
--- Library handling bits
-
-local function activate(self, oldLib, oldDeactivate)
-	AceOO = self
-	Factory = self.Factory
-	Object = self.Object
-	Class = self.Class
-	ClassFactory.prototype = Class
-	Mixin = self.Mixin
-	Interface = self.Interface
-	Classpool = self.Classpool
-	
-	if oldLib then
-		self.classes = oldLib.classes
-	end
-	if not self.classes then
-		self.classes = setmetatable({}, {__mode="k"})
-	else
-		for class in pairs(self.classes) do
-			class.new = class_new
-		end
-	end
-	
-	if oldDeactivate then
-		oldDeactivate(oldLib)
-	end
-end
-
-AceLibrary:Register(AceOO, MAJOR_VERSION, MINOR_VERSION, activate)
-AceOO = AceLibrary(MAJOR_VERSION)
--- a/modules/FuBar_ReActionFu/lib/AceOO-2.0/AceOO-2.0.toc	Wed Apr 02 18:22:02 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,12 +0,0 @@
-## Interface: 20200
-
-## Title: Lib: AceOO-2.0
-## Notes: AddOn development framework
-## Author: Ace Development Team
-## LoadOnDemand: 1
-## X-Website: http://www.wowace.com
-## X-Category: Library
-## X-License: LGPL v2.1 + MIT for AceOO-2.0
-## Dependencies: AceLibrary
- 
-AceOO-2.0.lua
--- a/modules/FuBar_ReActionFu/lib/Dewdrop-2.0/Dewdrop-2.0.lua	Wed Apr 02 18:22:02 2008 +0000
+++ b/modules/FuBar_ReActionFu/lib/Dewdrop-2.0/Dewdrop-2.0.lua	Wed Apr 02 23:31:13 2008 +0000
@@ -1,3487 +1,3487 @@
---[[
-Name: Dewdrop-2.0
-Revision: $Rev: 48630 $
-Author(s): ckknight (ckknight@gmail.com)
-Website: http://ckknight.wowinterface.com/
-Documentation: http://wiki.wowace.com/index.php/Dewdrop-2.0
-SVN: http://svn.wowace.com/root/trunk/DewdropLib/Dewdrop-2.0
-Description: A library to provide a clean dropdown menu interface.
-Dependencies: AceLibrary
-License: LGPL v2.1
-]]
-
-local MAJOR_VERSION = "Dewdrop-2.0"
-local MINOR_VERSION = "$Revision: 48630 $"
-
-if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary") end
-if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end
-
-local Dewdrop = {}
-
-local SharedMedia
-
-local CLOSE = "Close"
-local CLOSE_DESC = "Close the menu."
-local VALIDATION_ERROR = "Validation error."
-local USAGE_TOOLTIP = "Usage: %s."
-local RANGE_TOOLTIP = "Note that you can scroll your mouse wheel while over the slider to step by one."
-local RESET_KEYBINDING_DESC = "Hit escape to clear the keybinding."
-local KEY_BUTTON1 = "Left Mouse"
-local KEY_BUTTON2 = "Right Mouse"
-local DISABLED = "Disabled"
-local DEFAULT_CONFIRM_MESSAGE = "Are you sure you want to perform `%s'?"
-
-if GetLocale() == "deDE" then
-	CLOSE = "Schlie\195\159en"
-	CLOSE_DESC = "Men\195\188 schlie\195\159en."
-	VALIDATION_ERROR = "Validierungsfehler."
-	USAGE_TOOLTIP = "Benutzung: %s."
-	RANGE_TOOLTIP = "Beachte das du mit dem Mausrad scrollen kannst solange du \195\188ber dem Schieberegler bist, um 10er Spr\195\188nge zu machen."
-	RESET_KEYBINDING_DESC = "Escape dr\195\188cken, um die Tastenbelegung zu l\195\182schen."
-	KEY_BUTTON1 = "Linke Maustaste"
-	KEY_BUTTON2 = "Rechte Maustaste"
-	DISABLED = "Deaktiviert"
-	DEFAULT_CONFIRM_MESSAGE = "Bist du sicher das du `%s' machen willst?"
-elseif GetLocale() == "koKR" then
-	CLOSE = "닫기"
-	CLOSE_DESC = "메뉴를 닫습니다."
-	VALIDATION_ERROR = "오류 확인."
-	USAGE_TOOLTIP = "사용법: %s."
-	RANGE_TOOLTIP = "알림 : 슬라이더 위에서 마우스 휠을 사용하면 한단계씩 조절할 수 있습니다."
-	RESET_KEYBINDING_DESC = "단축키를 해제하려면 ESC키를 누르세요."
-	KEY_BUTTON1 = "왼쪽 마우스"
-	KEY_BUTTON2 = "오른쪽 마우스"
-	DISABLED = "비활성화됨"
-	DEFAULT_CONFIRM_MESSAGE = "정말로 `%s' 실행을 하시겠습니까 ?"
-elseif GetLocale() == "frFR" then
-	CLOSE = "Fermer"
-	CLOSE_DESC = "Ferme le menu."
-	VALIDATION_ERROR = "Erreur de validation."
-	USAGE_TOOLTIP = "Utilisation : %s."
-	RANGE_TOOLTIP = "Vous pouvez aussi utiliser la molette de la souris pour pour modifier progressivement."
-	RESET_KEYBINDING_DESC = "Appuyez sur la touche Echappement pour effacer le raccourci."
-	KEY_BUTTON1 = "Clic gauche"
-	KEY_BUTTON2 = "Clic droit"
-	DISABLED = "D\195\169sactiv\195\169"
-	DEFAULT_CONFIRM_MESSAGE = "\195\138tes-vous s\195\187r de vouloir effectuer '%s' ?"
-elseif GetLocale() == "esES" then
-	CLOSE = "Cerrar"
-	CLOSE_DESC = "Cierra el menú."
-	VALIDATION_ERROR = "Error de validación."
-	USAGE_TOOLTIP = "Uso: %s."
-	RANGE_TOOLTIP = "Puedes desplazarte verticalmente con la rueda del ratón sobre el desplazador."
-	RESET_KEYBINDING_DESC = "Pulsa Escape para borrar la asignación de tecla."
-	KEY_BUTTON1 = "Clic Izquierdo"
-	KEY_BUTTON2 = "Clic Derecho"
-	DISABLED = "Desactivado"
-	DEFAULT_CONFIRM_MESSAGE = "¿Estás seguro de querer realizar `%s'?"
-elseif GetLocale() == "zhTW" then
-	CLOSE = "關閉"
-	CLOSE_DESC = "關閉選單。"
-	VALIDATION_ERROR = "驗證錯誤。"
-	USAGE_TOOLTIP = "用法: %s。"
-	RANGE_TOOLTIP = "你可以在捲動條上使用滑鼠滾輪來捲動。"
-	RESET_KEYBINDING_DESC = "按Esc鍵清除快捷鍵。"
-	KEY_BUTTON1 = "滑鼠左鍵"
-	KEY_BUTTON2 = "滑鼠右鍵"
-	DISABLED = "停用"
-	DEFAULT_CONFIRM_MESSAGE = "是否執行「%s」?"
-elseif GetLocale() == "zhCN" then
-	CLOSE = "关闭"
-	CLOSE_DESC = "关闭菜单"
-	VALIDATION_ERROR = "验证错误."
-	USAGE_TOOLTIP = "用法: %s."
-	RANGE_TOOLTIP = "你可以在滚动条上使用鼠标滚轮来翻页."
-	RESET_KEYBINDING_DESC = "按ESC键清除按键绑定"
-	KEY_BUTTON1 = "鼠标左键"
-	KEY_BUTTON2 = "鼠标右键"
-	DISABLED = "禁用"
-	DEFAULT_CONFIRM_MESSAGE = "是否执行'%s'?"
-end
-
-Dewdrop.KEY_BUTTON1 = KEY_BUTTON1
-Dewdrop.KEY_BUTTON2 = KEY_BUTTON2
-
-local function new(...)
-	local t = {}
-	for i = 1, select('#', ...), 2 do
-		local k = select(i, ...)
-		if k then
-			t[k] = select(i+1, ...)
-		else
-			break
-		end
-	end
-	return t
-end
-
-local tmp
-do
-	local t = {}
-	function tmp(...)
-		for k in pairs(t) do
-			t[k] = nil
-		end
-		for i = 1, select('#', ...), 2 do
-			local k = select(i, ...)
-			if k then
-				t[k] = select(i+1, ...)
-			else
-				break
-			end
-		end
-		return t
-	end
-end
-local tmp2
-do
-	local t = {}
-	function tmp2(...)
-		for k in pairs(t) do
-			t[k] = nil
-		end
-		for i = 1, select('#', ...), 2 do
-			local k = select(i, ...)
-			if k then
-				t[k] = select(i+1, ...)
-			else
-				break
-			end
-		end
-		return t
-	end
-end
-local levels
-local buttons
-
-
--- Secure frame handling:
--- Rather than using secure buttons in the menu (has problems), we have one
--- master secureframe that we pop onto menu items on mouseover. This requires
--- some dark magic with OnLeave etc, but it's not too bad.
-
-local secureFrame = CreateFrame("Button", nil, nil, "SecureActionButtonTemplate")
-secureFrame:Hide()
-
-local function secureFrame_Show(self)
-  local owner = self.owner
-
-  if self.secure then	-- Leftovers from previos owner, clean up! ("Shouldn't" happen but does..)
-	  for k,v in pairs(self.secure) do
-	    self:SetAttribute(k, nil)
-	  end
-  end
-  self.secure = owner.secure;	-- Grab hold of new secure data
-
-  local scale = owner:GetEffectiveScale()
-
-  self:SetPoint("TOPLEFT", nil, "BOTTOMLEFT", owner:GetLeft() * scale, owner:GetTop() * scale)
-  self:SetPoint("BOTTOMRIGHT", nil, "BOTTOMLEFT", owner:GetRight() * scale, owner:GetBottom() * scale)
-  self:EnableMouse(true)
-  for k,v in pairs(self.secure) do
-    self:SetAttribute(k, v)
-  end
-
-	secureFrame:SetFrameStrata(owner:GetFrameStrata())
-	secureFrame:SetFrameLevel(owner:GetFrameLevel()+1)
-
-  self:Show()
-end
-
-local function secureFrame_Hide(self)
-  self:Hide()
-  if self.secure then
-	  for k,v in pairs(self.secure) do
-	    self:SetAttribute(k, nil)
-	  end
-	end
-  self.secure = nil
-end
-
-secureFrame:SetScript("OnEvent",
-	function()
-		if event=="PLAYER_REGEN_ENABLED" then
-			this.combat = false
-			if not this:IsShown() and this.owner then
-				secureFrame_Show(this)
-			end
-		elseif event=="PLAYER_REGEN_DISABLED" then
-			this.combat = true
-			if this:IsShown() then
-				secureFrame_Hide(this)
-			end
-		end
-	end
-)
-secureFrame:RegisterEvent("PLAYER_REGEN_ENABLED")
-secureFrame:RegisterEvent("PLAYER_REGEN_DISABLED")
-
-secureFrame:SetScript("OnLeave",
-	function()
-		local owner=this.owner
-		this:Deactivate()
-		owner:GetScript("OnLeave")()
-	end
-)
-
-secureFrame:HookScript("OnClick",
-	function()
-		local realthis = this
-		this = this.owner
-		this:GetScript("OnClick")()
-	end
-)
-
-function secureFrame:IsOwnedBy(frame)
-	return self.owner == frame
-end
-
-function secureFrame:Activate(owner)
-	if self.owner then		-- "Shouldn't" happen but apparently it does and I cba to troubleshoot...
-		if not self.combat then
-			secureFrame_Hide(self)
-		end
-	end
-	self.owner = owner
-	if not self.combat then
-		secureFrame_Show(self)
-	end
-end
-
-function secureFrame:Deactivate()
-	if not self.combat then
-		secureFrame_Hide(self)
-	end
-	self.owner = nil
-end
-
--- END secure frame utilities
-
-
--- Underline on mouseover - use a single global underline that we move around, no point in creating lots of copies
-local underlineFrame = CreateFrame("Frame", nil)
-underlineFrame.tx = underlineFrame:CreateTexture()
-underlineFrame.tx:SetTexture(1,1,0.5,0.75)
-underlineFrame:SetScript("OnHide", function(this) this:Hide(); end)
-underlineFrame:SetScript("OnShow", function(this) 	-- change sizing on the fly to catch runtime uiscale changes
-    underlineFrame.tx:SetPoint("TOPLEFT", -1, -2/this:GetEffectiveScale())
-    underlineFrame.tx:SetPoint("RIGHT", 1,0)
-    underlineFrame.tx:SetHeight(0.6 / this:GetEffectiveScale());
-end)
-underlineFrame:SetHeight(1)
-
--- END underline on mouseover
-
-
-local function GetScaledCursorPosition()
-	local x, y = GetCursorPosition()
-	local scale = UIParent:GetEffectiveScale()
-	return x / scale, y / scale
-end
-
-local function StartCounting(self, level)
-	for i = level, 1, -1 do
-		if levels[i] then
-			levels[i].count = 3
-		end
-	end
-end
-
-local function StopCounting(self, level)
-	for i = level, 1, -1 do
-		if levels[i] then
-			levels[i].count = nil
-		end
-	end
-end
-
-local function OnUpdate(self, elapsed)
-	for _,level in ipairs(levels) do
-		local count = level.count
-		if count then
-			count = count - elapsed
-			if count < 0 then
-				level.count = nil
-				self:Close(level.num)
-			else
-				level.count = count
-			end
-		end
-	end
-end
-
-local function CheckDualMonitor(self, frame)
-	local ratio = GetScreenWidth() / GetScreenHeight()
-	if ratio >= 2.4 and frame:GetRight() > GetScreenWidth() / 2 and frame:GetLeft() < GetScreenWidth() / 2 then
-		local offsetx
-		if GetCursorPosition() / GetScreenHeight() * 768 < GetScreenWidth() / 2 then
-			offsetx = GetScreenWidth() / 2 - frame:GetRight()
-		else
-			offsetx = GetScreenWidth() / 2 - frame:GetLeft()
-		end
-		local point, parent, relativePoint, x, y = frame:GetPoint(1)
-		frame:SetPoint(point, parent, relativePoint, (x or 0) + offsetx, y or 0)
-	end
-end
-
-local function CheckSize(self, level)
-	if not level.buttons then
-		return
-	end
-	local height = 20
-	for _, button in ipairs(level.buttons) do
-		height = height + button:GetHeight()
-	end
-	level:SetHeight(height)
-	local width = 160
-	for _, button in ipairs(level.buttons) do
-		local extra = 1
-		if button.hasArrow or button.hasColorSwatch then
-			extra = extra + 16
-		end
-		if not button.notCheckable then
-			extra = extra + 24
-		end
-		button.text:SetFont(STANDARD_TEXT_FONT, button.textHeight)
-		if button.text:GetWidth() + extra > width then
-			width = button.text:GetWidth() + extra
-		end
-	end
-	level:SetWidth(width + 20)
-	if level:GetLeft() and level:GetRight() and level:GetTop() and level:GetBottom() and (level:GetLeft() < 0 or level:GetRight() > GetScreenWidth() or level:GetTop() > GetScreenHeight() or level:GetBottom() < 0) then
-		level:ClearAllPoints()
-		local parent = level.parent or level:GetParent()
-		if type(parent) ~= "table" then
-			parent = UIParent
-		end
-		if level.lastDirection == "RIGHT" then
-			if level.lastVDirection == "DOWN" then
-				level:SetPoint("TOPLEFT", parent, "TOPRIGHT", 5, 10)
-			else
-				level:SetPoint("BOTTOMLEFT", parent, "BOTTOMRIGHT", 5, -10)
-			end
-		else
-			if level.lastVDirection == "DOWN" then
-				level:SetPoint("TOPRIGHT", parent, "TOPLEFT", -5, 10)
-			else
-				level:SetPoint("BOTTOMRIGHT", parent, "BOTTOMLEFT", -5, -10)
-			end
-		end
-	end
-	local dirty = false
-	if not level:GetRight() then
-		self:Close()
-		return
-	end
-	if level:GetRight() > GetScreenWidth() and level.lastDirection == "RIGHT" then
-		level.lastDirection = "LEFT"
-		dirty = true
-	elseif level:GetLeft() < 0 and level.lastDirection == "LEFT" then
-		level.lastDirection = "RIGHT"
-		dirty = true
-	end
-	if level:GetTop() > GetScreenHeight() and level.lastVDirection == "UP" then
-		level.lastVDirection = "DOWN"
-		dirty = true
-	elseif level:GetBottom() < 0 and level.lastVDirection == "DOWN" then
-		level.lastVDirection = "UP"
-		dirty = true
-	end
-	if dirty then
-		level:ClearAllPoints()
-		local parent = level.parent or level:GetParent()
-		if type(parent) ~= "table" then
-			parent = UIParent
-		end
-		if level.lastDirection == "RIGHT" then
-			if level.lastVDirection == "DOWN" then
-				level:SetPoint("TOPLEFT", parent, "TOPRIGHT", 5, 10)
-			else
-				level:SetPoint("BOTTOMLEFT", parent, "BOTTOMRIGHT", 5, -10)
-			end
-		else
-			if level.lastVDirection == "DOWN" then
-				level:SetPoint("TOPRIGHT", parent, "TOPLEFT", -5, 10)
-			else
-				level:SetPoint("BOTTOMRIGHT", parent, "BOTTOMLEFT", -5, -10)
-			end
-		end
-	end
-	if level:GetTop() > GetScreenHeight() then
-		local top = level:GetTop()
-		local point, parent, relativePoint, x, y = level:GetPoint(1)
-		level:ClearAllPoints()
-		level:SetPoint(point, parent, relativePoint, x or 0, (y or 0) + GetScreenHeight() - top)
-	elseif level:GetBottom() < 0 then
-		local bottom = level:GetBottom()
-		local point, parent, relativePoint, x, y = level:GetPoint(1)
-		level:ClearAllPoints()
-		level:SetPoint(point, parent, relativePoint, x or 0, (y or 0) - bottom)
-	end
-	CheckDualMonitor(self, level)
-	if mod(level.num, 5) == 0 then
-		local left, bottom = level:GetLeft(), level:GetBottom()
-		level:ClearAllPoints()
-		level:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT", left, bottom)
-	end
-end
-
-local Open
-local OpenSlider
-local OpenEditBox
-local Refresh
-local Clear
-local function ReleaseButton(self, level, index)
-	if not level.buttons then
-		return
-	end
-	if not level.buttons[index] then
-		return
-	end
-	local button = level.buttons[index]
-	button:Hide()
-	if button.highlight then
-		button.highlight:Hide()
-	end
---	button.arrow:SetVertexColor(1, 1, 1)
---	button.arrow:SetHeight(16)
---	button.arrow:SetWidth(16)
-	table.remove(level.buttons, index)
-	table.insert(buttons, button)
-	for k in pairs(button) do
-		if k ~= 0 and k ~= "text" and k ~= "check" and k ~= "arrow" and k ~= "colorSwatch" and k ~= "highlight" and k ~= "radioHighlight" then
-			button[k] = nil
-		end
-	end
-	return true
-end
-
-local function Scroll(self, level, down)
-	if down then
-		if level:GetBottom() < 0 then
-			local point, parent, relativePoint, x, y = level:GetPoint(1)
-			level:SetPoint(point, parent, relativePoint, x, y + 50)
-			if level:GetBottom() > 0 then
-				level:SetPoint(point, parent, relativePoint, x, y + 50 - level:GetBottom())
-			end
-		end
-	else
-		if level:GetTop() > GetScreenHeight() then
-			local point, parent, relativePoint, x, y = level:GetPoint(1)
-			level:SetPoint(point, parent, relativePoint, x, y - 50)
-			if level:GetTop() < GetScreenHeight() then
-				level:SetPoint(point, parent, relativePoint, x, y - 50 + GetScreenHeight() - level:GetTop())
-			end
-		end
-	end
-end
-
-local function getArgs(t, str, num, ...)
-	local x = t[str .. num]
-	if x == nil then
-		return ...
-	else
-		return x, getArgs(t, str, num + 1, ...)
-	end
-end
-
-local sliderFrame
-local editBoxFrame
-
-local normalFont
-local lastSetFont
-local justSetFont = false
-local regionTmp = {}
-local function fillRegionTmp(...)
-	for i = 1, select('#', ...) do
-		regionTmp[i] = select(i, ...)
-	end
-end
-
-local function showGameTooltip(this)
-	if this.tooltipTitle or this.tooltipText then
-		GameTooltip_SetDefaultAnchor(GameTooltip, this)
-		local disabled = not this.isTitle and this.disabled
-		local font
-		if this.tooltipTitle then
-			if SharedMedia and SharedMedia:IsValid("font", this.tooltipTitle) then
-				font = SharedMedia:Fetch("font", this.tooltipTitle)
-			end
-			if disabled then
-				GameTooltip:SetText(this.tooltipTitle, 0.5, 0.5, 0.5, 1)
-			else
-				GameTooltip:SetText(this.tooltipTitle, 1, 1, 1, 1)
-			end
-			if this.tooltipText then
-				if not font and SharedMedia and SharedMedia:IsValid("font", this.tooltipText) then
-					font = SharedMedia:Fetch("font", this.tooltipText)
-				end
-				if disabled then
-					GameTooltip:AddLine(this.tooltipText, (NORMAL_FONT_COLOR.r + 0.5) / 2, (NORMAL_FONT_COLOR.g + 0.5) / 2, (NORMAL_FONT_COLOR.b + 0.5) / 2, 1)
-				else
-					GameTooltip:AddLine(this.tooltipText, NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b, 1)
-				end
-			end
-		else
-			if SharedMedia and SharedMedia:IsValid("font", this.tooltipText) then
-				font = SharedMedia:Fetch("font", this.tooltipText)
-			end
-			if disabled then
-				GameTooltip:SetText(this.tooltipText, 0.5, 0.5, 0.5, 1)
-			else
-				GameTooltip:SetText(this.tooltipText, 1, 1, 1, 1)
-			end
-		end
-		if font then
-			fillRegionTmp(GameTooltip:GetRegions())
-			lastSetFont = font
-			justSetFont = true
-			for i,v in ipairs(regionTmp) do
-				if v.SetFont then
-					local norm,size,outline = v:GetFont()
-					v:SetFont(font, size, outline)
-					if not normalFont then
-						normalFont = norm
-					end
-				end
-				regionTmp[i] = nil
-			end
-		elseif not normalFont then
-			fillRegionTmp(GameTooltip:GetRegions())
-			for i,v in ipairs(regionTmp) do
-				if v.GetFont and not normalFont then
-					normalFont = v:GetFont()
-				end
-				regionTmp[i] = nil
-			end
-		end
-		GameTooltip:Show()
-	end
-	if this.tooltipFunc then
-		GameTooltip:SetOwner(this, "ANCHOR_NONE")
-		GameTooltip:SetPoint("TOPLEFT", this, "TOPRIGHT", 5, 0)
-		this.tooltipFunc(getArgs(this, 'tooltipArg', 1))
-		GameTooltip:Show()
-	end
-end
-
-local tmpt = setmetatable({}, {mode='v'})
-local numButtons = 0
-local function AcquireButton(self, level)
-	if not levels[level] then
-		return
-	end
-	level = levels[level]
-	if not level.buttons then
-		level.buttons = {}
-	end
-	local button
-	if #buttons == 0 then
-		numButtons = numButtons + 1
-		button = CreateFrame("Button", "Dewdrop20Button" .. numButtons, nil)
-		button:SetFrameStrata("FULLSCREEN_DIALOG")
-		button:SetHeight(16)
-		local highlight = button:CreateTexture(nil, "BACKGROUND")
-		highlight:SetTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight")
-		button.highlight = highlight
-		highlight:SetBlendMode("ADD")
-		highlight:SetAllPoints(button)
-		highlight:Hide()
-		local check = button:CreateTexture(nil, "ARTWORK")
-		button.check = check
-		check:SetTexture("Interface\\Buttons\\UI-CheckBox-Check")
-		check:SetPoint("CENTER", button, "LEFT", 12, 0)
-		check:SetWidth(24)
-		check:SetHeight(24)
-		local radioHighlight = button:CreateTexture(nil, "ARTWORK")
-		button.radioHighlight = radioHighlight
-		radioHighlight:SetTexture("Interface\\Buttons\\UI-RadioButton")
-		radioHighlight:SetAllPoints(check)
-		radioHighlight:SetBlendMode("ADD")
-		radioHighlight:SetTexCoord(0.5, 0.75, 0, 1)
-		radioHighlight:Hide()
-		button:SetScript("OnEnter", function()
-			if (sliderFrame and sliderFrame:IsShown() and sliderFrame.mouseDown and sliderFrame.level == this.level.num + 1) or (editBoxFrame and editBoxFrame:IsShown() and editBoxFrame.mouseDown and editBoxFrame.level == this.level.num + 1) then
-				for i = 1, this.level.num do
-					Refresh(self, levels[i])
-				end
-				return
-			end
-			self:Close(this.level.num + 1)
-			if not this.disabled then
-				if this.secure then
-					secureFrame:Activate(this)
-				elseif this.hasSlider then
-					OpenSlider(self, this)
-				elseif this.hasEditBox then
-					OpenEditBox(self, this)
-				elseif this.hasArrow then
-					Open(self, this, nil, this.level.num + 1, this.value)
-				end
-			end
-			if not this.level then -- button reclaimed
-				return
-			end
-			StopCounting(self, this.level.num + 1)
-			if not this.disabled then
-				highlight:Show()
-				if this.isRadio then
-					button.radioHighlight:Show()
-				end
-				if this.mouseoverUnderline then
-					underlineFrame:SetParent(this)
-					underlineFrame:SetPoint("BOTTOMLEFT",this.text,0,0)
-					underlineFrame:SetWidth(this.text:GetWidth())
-					underlineFrame:Show()
-				end
-			end
-			showGameTooltip(this)
-		end)
-		button:SetScript("OnHide", function()
-			if this.secure and secureFrame:IsOwnedBy(this) then
-				secureFrame:Deactivate()
-			end
-		end)
-		button:SetScript("OnLeave", function()
-			if this.secure and secureFrame:IsShown() then
-				return;	-- it's ok, we didn't actually mouse out of the button, only onto the secure frame on top of it
-			end
-			underlineFrame:Hide()
-			if not this.selected then
-				highlight:Hide()
-			end
-			button.radioHighlight:Hide()
-			if this.level then
-				StartCounting(self, this.level.num)
-			end
-			GameTooltip:Hide()
-		end)
-		local first = true
-		button:SetScript("OnClick", function()
-			if not this.disabled then
-				if this.hasColorSwatch then
-					local func = button.colorFunc
-					local hasOpacity = this.hasOpacity
-					local this = this
-					for k in pairs(tmpt) do
-						tmpt[k] = nil
-					end
-					for i = 1, 1000 do
-						local x = this['colorArg'..i]
-						if x == nil then
-							break
-						else
-							tmpt[i] = x
-						end
-					end
-					ColorPickerFrame.func = function()
-						if func then
-							local r,g,b = ColorPickerFrame:GetColorRGB()
-							local a = hasOpacity and 1 - OpacitySliderFrame:GetValue() or nil
-							local n = #tmpt
-							tmpt[n+1] = r
-							tmpt[n+2] = g
-							tmpt[n+3] = b
-							tmpt[n+4] = a
-							func(unpack(tmpt))
-							tmpt[n+1] = nil
-							tmpt[n+2] = nil
-							tmpt[n+3] = nil
-							tmpt[n+4] = nil
-						end
-					end
-					ColorPickerFrame.hasOpacity = this.hasOpacity
-					ColorPickerFrame.opacityFunc = ColorPickerFrame.func
-					ColorPickerFrame.opacity = 1 - this.opacity
-					ColorPickerFrame:SetColorRGB(this.r, this.g, this.b)
-					local r, g, b, a = this.r, this.g, this.b, this.opacity
-					ColorPickerFrame.cancelFunc = function()
-						if func then
-							local n = #tmpt
-							tmpt[n+1] = r
-							tmpt[n+2] = g
-							tmpt[n+3] = b
-							tmpt[n+4] = a
-							func(unpack(tmpt))
-							for i = 1, n+4 do
-								tmpt[i] = nil
-							end
-						end
-					end
-					self:Close(1)
-					ShowUIPanel(ColorPickerFrame)
-				elseif this.func then
-					local level = this.level
-					if type(this.func) == "string" then
-						if type(this.arg1[this.func]) ~= "function" then
-							self:error("Cannot call method %q", this.func)
-						end
-						this.arg1[this.func](this.arg1, getArgs(this, 'arg', 2))
-					else
-						this.func(getArgs(this, 'arg', 1))
-					end
-					if this.closeWhenClicked then
-						self:Close()
-					elseif level:IsShown() then
-						for i = 1, level.num do
-							Refresh(self, levels[i])
-						end
-						local value = levels[level.num].value
-						for i = level.num-1, 1, -1 do
-							local level = levels[i]
-							local good = false
-							for _,button in ipairs(level.buttons) do
-								if button.value == value then
-									good = true
-									break
-								end
-							end
-							if not good then
-								Dewdrop:Close(i+1)
-							end
-							value = levels[i].value
-						end
-					end
-				elseif this.closeWhenClicked then
-					self:Close()
-				end
-			end
-		end)
-		local text = button:CreateFontString(nil, "ARTWORK")
-		button.text = text
-		text:SetFontObject(GameFontHighlightSmall)
-		button.text:SetFont(STANDARD_TEXT_FONT, UIDROPDOWNMENU_DEFAULT_TEXT_HEIGHT)
-		button:SetScript("OnMouseDown", function()
-			if not this.disabled and (this.func or this.colorFunc or this.closeWhenClicked) then
-				text:SetPoint("LEFT", button, "LEFT", this.notCheckable and 1 or 25, -1)
-			end
-		end)
-		button:SetScript("OnMouseUp", function()
-			if not this.disabled and (this.func or this.colorFunc or this.closeWhenClicked) then
-				text:SetPoint("LEFT", button, "LEFT", this.notCheckable and 0 or 24, 0)
-			end
-		end)
-		local arrow = button:CreateTexture(nil, "ARTWORK")
-		button.arrow = arrow
-		arrow:SetPoint("LEFT", button, "RIGHT", -16, 0)
-		arrow:SetWidth(16)
-		arrow:SetHeight(16)
-		arrow:SetTexture("Interface\\ChatFrame\\ChatFrameExpandArrow")
-		local colorSwatch = button:CreateTexture(nil, "ARTWORK")
-		button.colorSwatch = colorSwatch
-		colorSwatch:SetWidth(20)
-		colorSwatch:SetHeight(20)
-		colorSwatch:SetTexture("Interface\\ChatFrame\\ChatFrameColorSwatch")
-		local texture = button:CreateTexture(nil, "OVERLAY")
-		colorSwatch.texture = texture
-		texture:SetTexture("Interface\\Buttons\\WHITE8X8")
-		texture:SetWidth(11.5)
-		texture:SetHeight(11.5)
-		texture:Show()
-		texture:SetPoint("CENTER", colorSwatch, "CENTER")
-		colorSwatch:SetPoint("RIGHT", button, "RIGHT", 0, 0)
-	else
-		button = table.remove(buttons)
-	end
-	button:ClearAllPoints()
-	button:SetParent(level)
-	button:SetFrameStrata(level:GetFrameStrata())
-	button:SetFrameLevel(level:GetFrameLevel() + 1)
-	button:SetPoint("LEFT", level, "LEFT", 10, 0)
-	button:SetPoint("RIGHT", level, "RIGHT", -10, 0)
-	if #level.buttons == 0 then
-		button:SetPoint("TOP", level, "TOP", 0, -10)
-	else
-		button:SetPoint("TOP", level.buttons[#level.buttons], "BOTTOM", 0, 0)
-	end
-	button.text:SetPoint("LEFT", button, "LEFT", 24, 0)
-	button:Show()
-	button.level = level
-	table.insert(level.buttons, button)
-	if not level.parented then
-		level.parented = true
-		level:ClearAllPoints()
-		if level.num == 1 then
-			if level.parent ~= UIParent and type(level.parent) == "table" then
-				level:SetPoint("TOPRIGHT", level.parent, "TOPLEFT")
-			else
-				level:SetPoint("CENTER", UIParent, "CENTER")
-			end
-		else
-			if level.lastDirection == "RIGHT" then
-				if level.lastVDirection == "DOWN" then
-					level:SetPoint("TOPLEFT", level.parent, "TOPRIGHT", 5, 10)
-				else
-					level:SetPoint("BOTTOMLEFT", level.parent, "BOTTOMRIGHT", 5, -10)
-				end
-			else
-				if level.lastVDirection == "DOWN" then
-					level:SetPoint("TOPRIGHT", level.parent, "TOPLEFT", -5, 10)
-				else
-					level:SetPoint("BOTTOMRIGHT", level.parent, "BOTTOMLEFT", -5, -10)
-				end
-			end
-		end
-		level:SetFrameStrata("FULLSCREEN_DIALOG")
-	end
-	button:SetAlpha(1)
-	return button
-end
-
-local numLevels = 0
-local function AcquireLevel(self, level)
-	if not levels[level] then
-		for i = #levels + 1, level, -1 do
-			local i = i
-			numLevels = numLevels + 1
-			local frame = CreateFrame("Button", "Dewdrop20Level" .. numLevels, nil)
-			if i == 1 then
-				local old_CloseSpecialWindows = CloseSpecialWindows
-				function CloseSpecialWindows()
-					local found = old_CloseSpecialWindows()
-					if levels[1]:IsShown() then
-						self:Close()
-						return 1
-					end
-					return found
-				end
-			end
-			levels[i] = frame
-			frame.num = i
-			frame:SetParent(UIParent)
-			frame:SetFrameStrata("FULLSCREEN_DIALOG")
-			frame:Hide()
-			frame:SetWidth(180)
-			frame:SetHeight(10)
-			frame:SetFrameLevel(i * 3)
-			frame:SetScript("OnHide", function()
-				self:Close(level + 1)
-			end)
-			if frame.SetTopLevel then
-				frame:SetTopLevel(true)
-			end
-			frame:EnableMouse(true)
-			frame:EnableMouseWheel(true)
-			local backdrop = CreateFrame("Frame", nil, frame)
-			backdrop:SetAllPoints(frame)
-			backdrop:SetBackdrop(tmp(
-				'bgFile', "Interface\\Tooltips\\UI-Tooltip-Background",
-				'edgeFile', "Interface\\Tooltips\\UI-Tooltip-Border",
-				'tile', true,
-				'insets', tmp2(
-					'left', 5,
-					'right', 5,
-					'top', 5,
-					'bottom', 5
-				),
-				'tileSize', 16,
-				'edgeSize', 16
-			))
-			backdrop:SetBackdropBorderColor(TOOLTIP_DEFAULT_COLOR.r, TOOLTIP_DEFAULT_COLOR.g, TOOLTIP_DEFAULT_COLOR.b)
-			backdrop:SetBackdropColor(TOOLTIP_DEFAULT_BACKGROUND_COLOR.r, TOOLTIP_DEFAULT_BACKGROUND_COLOR.g, TOOLTIP_DEFAULT_BACKGROUND_COLOR.b)
-			frame:SetScript("OnClick", function()
-				self:Close(i)
-			end)
-			frame:SetScript("OnEnter", function()
-				StopCounting(self, i)
-			end)
-			frame:SetScript("OnLeave", function()
-				StartCounting(self, i)
-			end)
-			frame:SetScript("OnMouseWheel", function()
-				Scroll(self, frame, arg1 < 0)
-			end)
-			if i == 1 then
-				frame:SetScript("OnUpdate", function(this, arg1)
-					OnUpdate(self, arg1)
-				end)
-				levels[1].lastDirection = "RIGHT"
-				levels[1].lastVDirection = "DOWN"
-			else
-				levels[i].lastDirection = levels[i - 1].lastDirection
-				levels[i].lastVDirection = levels[i - 1].lastVDirection
-			end
-		end
-	end
-	local fullscreenFrame = GetUIPanel("fullscreen")
-	local l = levels[level]
-	local strata, framelevel = l:GetFrameStrata(), l:GetFrameLevel()
-	if fullscreenFrame then
-		l:SetParent(fullscreenFrame)
-	else
-		l:SetParent(UIParent)
-	end
-	l:SetFrameStrata(strata)
-	l:SetFrameLevel(framelevel)
-	l:SetAlpha(1)
-	return l
-end
-
-local function validateOptions(options, position, baseOptions, fromPass)
-	if not baseOptions then
-		baseOptions = options
-	end
-	if type(options) ~= "table" then
-		return "Options must be a table.", position
-	end
-	local kind = options.type
-	if type(kind) ~= "string" then
-		return '"type" must be a string.', position
-	elseif kind ~= "group" and kind ~= "range" and kind ~= "text" and kind ~= "execute" and kind ~= "toggle" and kind ~= "color" and kind ~= "dragLink" and kind ~= "header" then
-		return '"type" must either be "range", "text", "group", "toggle", "execute", "color", "dragLink", or "header".', position
-	end
-	if options.aliases then
-		if type(options.aliases) ~= "table" and type(options.aliases) ~= "string" then
-			return '"alias" must be a table or string', position
-		end
-	end
-	if not fromPass then
-		if kind == "execute" then
-			if type(options.func) ~= "string" and type(options.func) ~= "function" then
-				return '"func" must be a string or function', position
-			end
-		elseif kind == "range" or kind == "text" or kind == "toggle" then
-			if type(options.set) ~= "string" and type(options.set) ~= "function" then
-				return '"set" must be a string or function', position
-			end
-			if kind == "text" and options.get == false then
-			elseif type(options.get) ~= "string" and type(options.get) ~= "function" then
-				return '"get" must be a string or function', position
-			end
-		elseif kind == "group" and options.pass then
-			if options.pass ~= true then
-				return '"pass" must be either nil, true, or false', position
-			end
-			if not options.func then
-				if type(options.set) ~= "string" and type(options.set) ~= "function" then
-					return '"set" must be a string or function', position
-				end
-				if type(options.get) ~= "string" and type(options.get) ~= "function" then
-					return '"get" must be a string or function', position
-				end
-			elseif type(options.func) ~= "string" and type(options.func) ~= "function" then
-				return '"func" must be a string or function', position
-			end
-		end
-	end
-	if options ~= baseOptions then
-		if kind == "header" then
-		elseif type(options.desc) ~= "string" then
-			return '"desc" must be a string', position
-		elseif options.desc:len() == 0 then
-			return '"desc" cannot be a 0-length string', position
-		end
-	end
-	if options ~= baseOptions or kind == "range" or kind == "text" or kind == "toggle" or kind == "color" then
-		if options.type == "header" and not options.cmdName and not options.name then
-		elseif options.cmdName then
-			if type(options.cmdName) ~= "string" then
-				return '"cmdName" must be a string or nil', position
-			elseif options.cmdName:len() == 0 then
-				return '"cmdName" cannot be a 0-length string', position
-			end
-			if type(options.guiName) ~= "string" then
-				if not options.guiNameIsMap then
-					return '"guiName" must be a string or nil', position
-				end
-			elseif options.guiName:len() == 0 then
-				return '"guiName" cannot be a 0-length string', position
-			end
-		else
-			if type(options.name) ~= "string" then
-				return '"name" must be a string', position
-			elseif options.name:len() == 0 then
-				return '"name" cannot be a 0-length string', position
-			end
-		end
-	end
-	if options.guiNameIsMap then
-		if type(options.guiNameIsMap) ~= "boolean" then
-			return '"guiNameIsMap" must be a boolean or nil', position
-		elseif options.type ~= "toggle" then
-			return 'if "guiNameIsMap" is true, then "type" must be set to \'toggle\'', position
-		elseif type(options.map) ~= "table" then
-			return '"map" must be a table', position
-		end
-	end
-	if options.message and type(options.message) ~= "string" then
-		return '"message" must be a string or nil', position
-	end
-	if options.error and type(options.error) ~= "string" then
-		return '"error" must be a string or nil', position
-	end
-	if options.current and type(options.current) ~= "string" then
-		return '"current" must be a string or nil', position
-	end
-	if options.order then
-		if type(options.order) ~= "number" or (-1 < options.order and options.order < 0.999) then
-			return '"order" must be a non-zero number or nil', position
-		end
-	end
-	if options.disabled then
-		if type(options.disabled) ~= "function" and type(options.disabled) ~= "string" and options.disabled ~= true then
-			return '"disabled" must be a function, string, or boolean', position
-		end
-	end
-	if options.cmdHidden then
-		if type(options.cmdHidden) ~= "function" and type(options.cmdHidden) ~= "string" and options.cmdHidden ~= true then
-			return '"cmdHidden" must be a function, string, or boolean', position
-		end
-	end
-	if options.guiHidden then
-		if type(options.guiHidden) ~= "function" and type(options.guiHidden) ~= "string" and options.guiHidden ~= true then
-			return '"guiHidden" must be a function, string, or boolean', position
-		end
-	end
-	if options.hidden then
-		if type(options.hidden) ~= "function" and type(options.hidden) ~= "string" and options.hidden ~= true then
-			return '"hidden" must be a function, string, or boolean', position
-		end
-	end
-	if kind == "text" then
-		if type(options.validate) == "table" then
-			local t = options.validate
-			local iTable = nil
-			for k,v in pairs(t) do
-				if type(k) == "number" then
-					if iTable == nil then
-						iTable = true
-					elseif not iTable then
-						return '"validate" must either have all keys be indexed numbers or strings', position
-					elseif k < 1 or k > #t then
-						return '"validate" numeric keys must be indexed properly. >= 1 and <= #t', position
-					end
-				else
-					if iTable == nil then
-						iTable = false
-					elseif iTable then
-						return '"validate" must either have all keys be indexed numbers or strings', position
-					end
-				end
-				if type(v) ~= "string" then
-					return '"validate" values must all be strings', position
-				end
-			end
-			if options.multiToggle and options.multiToggle ~= true then
-				return '"multiToggle" must be a boolean or nil if "validate" is a table', position
-			end
-		elseif options.validate == "keybinding" then
-			-- no other checks
-		else
-			if type(options.usage) ~= "string" then
-				return '"usage" must be a string', position
-			elseif options.validate and type(options.validate) ~= "string" and type(options.validate) ~= "function" then
-				return '"validate" must be a string, function, or table', position
-			end
-		end
-		if options.multiToggle and type(options.validate) ~= "table" then
-			return '"validate" must be a table if "multiToggle" is true', position
-		end
-	elseif kind == "range" then
-		if options.min or options.max then
-			if type(options.min) ~= "number" then
-				return '"min" must be a number', position
-			elseif type(options.max) ~= "number" then
-				return '"max" must be a number', position
-			elseif options.min >= options.max then
-				return '"min" must be less than "max"', position
-			end
-		end
-		if options.step then
-			if type(options.step) ~= "number" then
-				return '"step" must be a number', position
-			elseif options.step < 0 then
-				return '"step" must be nonnegative', position
-			end
-		end
-		if options.bigStep then
-			if type(options.bigStep) ~= "number" then
-				return '"bigStep" must be a number', position
-			elseif options.bigStep < 0 then
-				return '"bigStep" must be nonnegative', position
-			end
-		end
-		if options.isPercent and options.isPercent ~= true then
-			return '"isPercent" must either be nil, true, or false', position
-		end
-	elseif kind == "toggle" then
-		if options.map then
-			if type(options.map) ~= "table" then
-				return '"map" must be a table', position
-			elseif type(options.map[true]) ~= "string" then
-				return '"map[true]" must be a string', position
-			elseif type(options.map[false]) ~= "string" then
-				return '"map[false]" must be a string', position
-			end
-		end
-	elseif kind == "color" then
-		if options.hasAlpha and options.hasAlpha ~= true then
-			return '"hasAlpha" must be nil, true, or false', position
-		end
-	elseif kind == "group" then
-		if options.pass and options.pass ~= true then
-			return '"pass" must be nil, true, or false', position
-		end
-		if type(options.args) ~= "table" then
-			return '"args" must be a table', position
-		end
-		for k,v in pairs(options.args) do
-			if type(k) ~= "number" then
-				if type(k) ~= "string" then
-					return '"args" keys must be strings or numbers', position
-				elseif k:len() == 0 then
-					return '"args" keys must not be 0-length strings.', position
-				end
-			end
-			if type(v) ~= "table" then
-				return '"args" values must be tables', position and position .. "." .. k or k
-			end
-			local newposition
-			if position then
-				newposition = position .. ".args." .. k
-			else
-				newposition = "args." .. k
-			end
-			local err, pos = validateOptions(v, newposition, baseOptions, options.pass)
-			if err then
-				return err, pos
-			end
-		end
-	elseif kind == "execute" then
-		if type(options.confirm) ~= "string" and type(options.confirm) ~= "boolean" and type(options.confirm) ~= "nil" then
-			return '"confirm" must be a string, boolean, or nil', position
-		end
-	end
-	if options.icon and type(options.icon) ~= "string" then
-		return'"icon" must be a string', position
-	end
-	if options.iconWidth or options.iconHeight then
-		if type(options.iconWidth) ~= "number" or type(options.iconHeight) ~= "number" then
-			return '"iconHeight" and "iconWidth" must be numbers', position
-		end
-	end
-	if options.iconCoordLeft or options.iconCoordRight or options.iconCoordTop or options.iconCoordBottom then
-		if type(options.iconCoordLeft) ~= "number" or type(options.iconCoordRight) ~= "number" or type(options.iconCoordTop) ~= "number" or type(options.iconCoordBottom) ~= "number" then
-			return '"iconCoordLeft", "iconCoordRight", "iconCoordTop", and "iconCoordBottom" must be numbers', position
-		end
-	end
-end
-
-local validatedOptions
-
-local values
-local mysort_args
-local mysort
-local othersort
-local othersort_validate
-
-local baseFunc, currentLevel
-
-local function confirmPopup(message, func, ...)
-	if not StaticPopupDialogs["DEWDROP20_CONFIRM_DIALOG"] then
-		StaticPopupDialogs["DEWDROP20_CONFIRM_DIALOG"] = {}
-	end
-	local t = StaticPopupDialogs["DEWDROP20_CONFIRM_DIALOG"]
-	for k in pairs(t) do
-		t[k] = nil
-	end
-	t.text = message
-	t.button1 = ACCEPT or "Accept"
-	t.button2 = CANCEL or "Cancel"
-	t.OnAccept = function()
-		func(unpack(t))
-	end
-	for i = 1, select('#', ...) do
-		t[i] = select(i, ...)
-	end
-	t.timeout = 0
-	t.whileDead = 1
-	t.hideOnEscape = 1
-
-	Dewdrop:Close()
-	StaticPopup_Show("DEWDROP20_CONFIRM_DIALOG")
-end
-
-
-local function getMethod(settingname, handler, v, methodName, ...)	-- "..." is simply returned straight out cause you can't do "a,b,c = 111,f(),222"
-	assert(v and type(v)=="table")
-	assert(methodName and type(methodName)=="string")
-
-	local method = v[methodName]
-	if type(method)=="function" then
-		return method, ...
-	elseif type(method)=="string" then
-		if not handler then
-			Dewdrop:error("[%s] 'handler' is required if providing a method name: %q", tostring(settingname), method)
-		elseif not handler[method] then
-			Dewdrop:error("[%s] 'handler' method %q not defined", tostring(settingname), method)
-		end
-		return handler[method], handler, ...
-	end
-
-	Dewdrop:error("[%s] Missing %q directive", tostring(settingname), methodName)
-end
-
-local function callMethod(settingname, handler, v, methodName, ...)
-	assert(v and type(v)=="table")
-	assert(methodName and type(methodName)=="string")
-
-	local method = v[methodName]
-	if type(method)=="function" then
-		local success, ret,ret2,ret3,ret4 = pcall(v[methodName], ...)
-		if not success then
-			geterrorhandler()(ret)
-			return nil
-		end
-		return ret,ret2,ret3,ret4
-
-	elseif type(method)=="string" then
-
-		local neg = method:match("^~(.-)$")
-		if neg then
-			method = neg
-		end
-		if not handler then
-			Dewdrop:error("[%s] 'handler' is required if providing a method name: %q", tostring(settingname), method)
-		elseif not handler[method] then
-			Dewdrop:error("[%s] 'handler' (%q) method %q not defined", tostring(settingname), handler.name or "(unnamed)", method)
-		end
-		local success, ret,ret2,ret3,ret4 = pcall(handler[method], handler, ...)
-		if not success then
-			geterrorhandler()(ret)
-			return nil
-		end
-		if neg then
-			return not ret
-		end
-		return ret,ret2,ret3,ret4
-	elseif method == false then
-		return nil
-	end
-
-	Dewdrop:error("[%s] Missing %q directive in %q", tostring(settingname), methodName, v.name or "(unnamed)")
-end
-
-local function skip1Nil(...)
-	if select(1,...)==nil then
-		return select(2,...)
-	end
-	return ...
-end
-
-function Dewdrop:FeedAceOptionsTable(options, difference)
-	self:argCheck(options, 2, "table")
-	self:argCheck(difference, 3, "nil", "number")
-	if not currentLevel then
-		self:error("Cannot call `FeedAceOptionsTable' outside of a Dewdrop declaration")
-	end
-	if not difference then
-		difference = 0
-	end
-	if not validatedOptions then
-		validatedOptions = {}
-	end
-	if not validatedOptions[options] then
-		local err, position = validateOptions(options)
-
-		if err then
-			if position then
-				Dewdrop:error(position .. ": " .. err)
-			else
-				Dewdrop:error(err)
-			end
-		end
-
-		validatedOptions[options] = true
-	end
-	local level = levels[currentLevel]
-	if not level then
-		self:error("Improper level given")
-	end
-	if not values then
-		values = {}
-	else
-		for k,v in pairs(values) do
-			values[k] = nil
-		end
-	end
-
-	local current = level
-	while current do		-- this traverses from higher level numbers to lower, building "values" with leaf nodes first and trunk nodes later
-		if current.num == difference + 1 then
-			break
-		end
-		table.insert(values, current.value)
-		current = levels[current.num - 1]
-	end
-
-	local realOptions = options
-	local handler = options.handler
-	local passTable
-	local passValue
-	while #values > 0 do	-- This loop traverses values from the END (trunk nodes first, then onto leaf nodes)
-		if options.pass then
-			if options.get and options.set then
-				passTable = options
-			elseif not passTable then
-				passTable = options
-			end
-		else
-			passTable = nil
-		end
-		local value = table.remove(values)
-		options = options.args and options.args[value]
-		if not options then
-			return
-		end
-		handler = options.handler or handler
-		passValue = passTable and value or nil
-	end
-
-	if options.type == "group" then
-		local hidden = options.hidden
-		if type(hidden) == "function" or type(hidden) == "string" then
-			hidden = callMethod(options.name or "(options root)", handler, options, "hidden", options.passValue) or false
-		end
-		if hidden then
-			return
-		end
-		local disabled = options.disabled
-		if type(disabled) == "function" or type(disabled) == "string" then
-			disabled = callMethod(options.name or "(options root)", handler, options, "disabled", options.passValue) or false
-		end
-		if disabled then
-			self:AddLine(
-				'text', DISABLED,
-				'disabled', true
-			)
-			return
-		end
-		for k in pairs(options.args) do
-			table.insert(values, k)
-		end
-		if options.pass then
-			if options.get and options.set then
-				passTable = options
-			elseif not passTable then
-				passTable = options
-			end
-		else
-			passTable = nil
-		end
-		if not mysort then
-			mysort = function(a, b)
-				local alpha, bravo = mysort_args[a], mysort_args[b]
-				local alpha_order = alpha.order or 100
-				local bravo_order = bravo.order or 100
-				local alpha_name = alpha.guiName or alpha.name
-				local bravo_name = bravo.guiName or bravo.name
-				if alpha_order == bravo_order then
-					if not alpha_name then
-						return bravo_name
-					elseif not bravo_name then
-						return false
-					else
-						return alpha_name:gsub("|c%x%x%x%x%x%x%x%x", ""):gsub("|r", ""):upper() < bravo_name:gsub("|c%x%x%x%x%x%x%x%x", ""):gsub("|r", ""):upper()
-					end
-				else
-					if alpha_order < 0 then
-						if bravo_order > 0 then
-							return false
-						end
-					else
-						if bravo_order < 0 then
-							return true
-						end
-					end
-					return alpha_order < bravo_order
-				end
-			end
-		end
-		mysort_args = options.args
-		table.sort(values, mysort)
-		mysort_args = nil
-		local hasBoth = #values >= 1 and (options.args[values[1]].order or 100) > 0 and (options.args[values[#values]].order or 100) < 0
-		local last_order = 1
-		for _,k in ipairs(values) do
-			local v = options.args[k]
-			local handler = v.handler or handler
-			if hasBoth and last_order > 0 and (v.order or 100) < 0 then
-				hasBoth = false
-				self:AddLine()
-			end
-			local hidden, disabled = v.guiHidden or v.hidden, v.disabled
-
-			if type(hidden) == "function" or type(hidden) == "string" then
-				hidden = callMethod(k, handler, v, "hidden", v.passValue) or false
-			end
-			if not hidden then
-				if type(disabled) == "function" or type(disabled) == "string" then
-					disabled = callMethod(k, handler, v, "disabled", v.passValue) or false
-				end
-				local name = (v.guiIconOnly and v.icon) and "" or (v.guiName or v.name)
-				local desc = v.guiDesc or v.desc
-				local iconHeight = v.iconHeight or 16
-				local iconWidth = v.iconWidth or 16
-				local iconCoordLeft = v.iconCoordLeft
-				local iconCoordRight = v.iconCoordRight
-				local iconCoordBottom = v.iconCoordBottom
-				local iconCoordTop = v.iconCoordTop
-				local tooltipTitle, tooltipText
-				tooltipTitle = name
-				if name ~= desc then
-					tooltipText = desc
-				end
-				if type(v.usage) == "string" and v.usage:trim():len() > 0 then
-					if tooltipText then
-						tooltipText = tooltipText .. "\n\n" .. USAGE_TOOLTIP:format(v.usage)
-					else
-						tooltipText = USAGE_TOOLTIP:format(v.usage)
-					end
-				end
-				local v_p = passTable
-				if not v_p or (v.type ~= "execute" and v.get and v.set) or (v.type == "execute" and v.func) then
-					v_p = v
-				end
-				local passValue = v.passValue or (v_p~=v and k) or nil
-				if v.type == "toggle" then
-					local checked = callMethod(name, handler, v_p, "get", passValue) or false
-					local checked_arg = checked
-					if type(v_p.get)=="string" and v_p.get:match("^~") then
-						checked_arg = not checked
-					end
-					local func, arg1, arg2, arg3 = getMethod(name, handler, v_p, "set",   skip1Nil(passValue, not checked_arg))
-					if v.guiNameIsMap then
-						checked = checked and true or false
-						name = tostring(v.map and v.map[checked]):gsub("|c%x%x%x%x%x%x%x%x(.-)|r", "%1")
-						tooltipTitle = name
-						checked = true--nil
-					end
-					self:AddLine(
-						'text', name,
-						'checked', checked,
-						'isRadio', v.isRadio,
-						'func', func,
-						'arg1', arg1,
-						'arg2', arg2,
-						'arg3', arg3,
-						'disabled', disabled,
-						'tooltipTitle', tooltipTitle,
-						'tooltipText', tooltipText
-					)
-				elseif v.type == "execute" then
-					local func, arg1, arg2, arg3, arg4
-					local confirm = v.confirm
-					if confirm == true then
-						confirm = DEFAULT_CONFIRM_MESSAGE:format(tooltipText or tooltipTitle)
-						func,arg1,arg2,arg3,arg4 = confirmPopup, confirm, getMethod(name, handler, v_p, "func", passValue)
-					elseif type(confirm) == "string" then
-						func,arg1,arg2,arg3,arg4 = confirmPopup, confirm, getMethod(name, handler, v_p, "func", passValue)
-					else
-						func,arg1,arg2 = getMethod(name, handler, v_p, "func", passValue)
-					end
-					self:AddLine(
-						'text', name,
-						'checked', checked,
-						'func', func,
-						'arg1', arg1,
-						'arg2', arg2,
-						'arg3', arg3,
-						'arg4', arg4,
-						'disabled', disabled,
-						'tooltipTitle', tooltipTitle,
-						'tooltipText', tooltipText,
-						'icon', v.icon,
-						'iconHeight', iconHeight,
-						'iconWidth', iconWidth,
-						'iconCoordLeft', iconCoordLeft,
-						'iconCoordRight', iconCoordRight,
-						'iconCoordTop', iconCoordTop,
-						'iconCoordBottom', iconCoordBottom
-					)
-				elseif v.type == "range" then
-					local sliderValue
-					sliderValue = callMethod(name, handler, v_p, "get", passValue) or 0
-					local sliderFunc, sliderArg1, sliderArg2 = getMethod(name, handler, v_p, "set",    passValue)
-					if tooltipText then
-						tooltipText = format("%s\n\n%s", tooltipText, RANGE_TOOLTIP)
-					else
-						tooltipText = RANGE_TOOLTIP
-					end
-					self:AddLine(
-						'text', name,
-						'hasArrow', true,
-						'hasSlider', true,
-						'sliderMin', v.min or 0,
-						'sliderMax', v.max or 1,
-						'sliderStep', v.step or 0,
-						'sliderBigStep', v.bigStep or nil,
-						'sliderIsPercent', v.isPercent or false,
-						'sliderValue', sliderValue,
-						'sliderFunc', sliderFunc,
-						'sliderArg1', sliderArg1,
-						'sliderArg2', sliderArg2,
-						'fromAceOptions', true,
-						'disabled', disabled,
-						'tooltipTitle', tooltipTitle,
-						'tooltipText', tooltipText,
-						'icon', v.icon,
-						'iconHeight', iconHeight,
-						'iconWidth', iconWidth,
-						'iconCoordLeft', iconCoordLeft,
-						'iconCoordRight', iconCoordRight,
-						'iconCoordTop', iconCoordTop,
-						'iconCoordBottom', iconCoordBottom
-					)
-				elseif v.type == "color" then
-					local r,g,b,a = callMethod(name, handler, v_p, "get", passValue)
-					if not r then
-						r,g,b,a = 0,0,0,0
-					end
-					local colorFunc, colorArg1, colorArg2 = getMethod(name, handler, v_p, "set",    passValue)
-					self:AddLine(
-						'text', name,
-						'hasArrow', true,
-						'hasColorSwatch', true,
-						'r', r,
-						'g', g,
-						'b', b,
-						'opacity', v.hasAlpha and a or nil,
-						'hasOpacity', v.hasAlpha,
-						'colorFunc', colorFunc,
-						'colorArg1', colorArg1,
-						'colorArg2', colorArg2,
-						'disabled', disabled,
-						'tooltipTitle', tooltipTitle,
-						'tooltipText', tooltipText
-					)
-				elseif v.type == "text" then
-						if type(v.validate) == "table" then
-						local func,arg1,arg2
-						if v.onClick then
-							func,arg1,arg2 = getMethod(name, handler, v, "onClick",    passValue)
-						end
-						local checked
-						if v.isChecked then
-							checked = callMethod(name, handler, v, "isChecked",    passValue) or false
-						end
-						self:AddLine(
-							'text', name,
-							'hasArrow', true,
-							'value', k,
-							'func', func,
-							'arg1', arg1,
-							'arg2', arg2,
-							'mouseoverUnderline', func and true or nil,
-							'disabled', disabled,
-							'checked', checked,
-							'tooltipTitle', tooltipTitle,
-							'tooltipText', tooltipText,
-							'icon', v.icon,
-							'iconHeight', iconHeight,
-							'iconWidth', iconWidth,
-							'iconCoordLeft', iconCoordLeft,
-							'iconCoordRight', iconCoordRight,
-							'iconCoordTop', iconCoordTop,
-							'iconCoordBottom', iconCoordBottom
-						)
-					else
-						local editBoxText
-						editBoxText = callMethod(name, handler, v_p, "get", passValue) or ""
-						local editBoxFunc, editBoxArg1, editBoxArg2 = getMethod(name, handler, v_p, "set",    passValue)
-
-						local editBoxValidateFunc, editBoxValidateArg1
-
-						if v.validate and v.validate ~= "keybinding" then
-							if v.validate == "keybinding" then
-								if tooltipText then
-									tooltipText = format("%s\n\n%s", tooltipText, RESET_KEYBINDING_DESC)
-								else
-									tooltipText = RESET_KEYBINDING_DESC
-								end
-							else
-								editBoxValidateFunc, editBoxValidateArg1 = getMethod(name, handler, v, "validate") -- no passvalue!
-							end
-						end
-
-						self:AddLine(
-							'text', name,
-							'hasArrow', true,
-							'icon', v.icon,
-							'iconHeight', iconHeight,
-							'iconWidth', iconWidth,
-							'iconCoordLeft', iconCoordLeft,
-							'iconCoordRight', iconCoordRight,
-							'iconCoordTop', iconCoordTop,
-							'iconCoordBottom', iconCoordBottom,
-							'hasEditBox', true,
-							'editBoxText', editBoxText,
-							'editBoxFunc', editBoxFunc,
-							'editBoxArg1', editBoxArg1,
-							'editBoxArg2', editBoxArg2,
-							'editBoxValidateFunc', editBoxValidateFunc,
-							'editBoxValidateArg1', editBoxValidateArg1,
-							'editBoxIsKeybinding', v.validate == "keybinding",
-							'editBoxKeybindingOnly', v.keybindingOnly,
-							'editBoxKeybindingExcept', v.keybindingExcept,
-							'disabled', disabled,
-							'tooltipTitle', tooltipTitle,
-							'tooltipText', tooltipText
-						)
-					end
-				elseif v.type == "group" then
-					local func,arg1,arg2
-					if v.onClick then
-						func,arg1,arg2 = getMethod(name, handler, v, "onClick",    passValue)
-					end
-					local checked
-					if v.isChecked then
-						checked = callMethod(name, handler, v, "isChecked",    passValue) or false
-					end
-					self:AddLine(
-						'text', name,
-						'hasArrow', true,
-						'value', k,
-						'func', func,
-						'arg1', arg1,
-						'arg2', arg2,
-						'mouseoverUnderline', func and true or nil,
-						'disabled', disabled,
-						'checked', checked,
-						'tooltipTitle', tooltipTitle,
-						'tooltipText', tooltipText,
-						'icon', v.icon,
-						'iconHeight', iconHeight,
-						'iconWidth', iconWidth,
-						'iconCoordLeft', iconCoordLeft,
-						'iconCoordRight', iconCoordRight,
-						'iconCoordTop', iconCoordTop,
-						'iconCoordBottom', iconCoordBottom
-					)
-				elseif v.type == "header" then
-					if name == "" or not name then
-						self:AddLine(
-							'isTitle', true,
-							'icon', v.icon,
-							'iconHeight', iconHeight,
-							'iconWidth', iconWidth,
-							'iconCoordLeft', iconCoordLeft,
-							'iconCoordRight', iconCoordRight,
-							'iconCoordTop', iconCoordTop,
-							'iconCoordBottom', iconCoordBottom
-						)
-					else
-						self:AddLine(
-							'text', name,
-							'isTitle', true,
-							'icon', v.icon,
-							'iconHeight', iconHeight,
-							'iconWidth', iconWidth,
-							'iconCoordLeft', iconCoordLeft,
-							'iconCoordRight', iconCoordRight,
-							'iconCoordTop', iconCoordTop,
-							'iconCoordBottom', iconCoordBottom
-						)
-					end
-				end
-			end
-			last_order = v.order or 100
-		end
-	elseif options.type == "text" and type(options.validate) == "table" then
-		local current
-		local options_p = passTable
-		if not options_p or (options.get and options.set) then
-			options_p = options
-			passTable = nil
-			passValue = nil
-		end
-		local multiToggle = options.multiToggle
-		local passValue = options.passValue or passValue
-		if not multiToggle then
-			current = callMethod(k, handler, options_p, "get", passValue)
-		end
-		local indexed = true
-		for k,v in pairs(options.validate) do
-			if type(k) ~= "number" then
-				indexed = false
-			end
-			table.insert(values, k)
-		end
-		if not indexed then
-			if not othersort then
-				othersort = function(alpha, bravo)
-					return othersort_validate[alpha]:gsub("|c%x%x%x%x%x%x%x%x", ""):gsub("|r", ""):upper() < othersort_validate[bravo]:gsub("|c%x%x%x%x%x%x%x%x", ""):gsub("|r", ""):upper()
-				end
-			end
-			othersort_validate = options.validate
-			table.sort(values, othersort)
-			othersort_validate = nil
-		end
-		for _,k in ipairs(values) do
-			local v = options.validate[k]
-			if type(k) == "number" then
-				k = v
-			end
-			local func, arg1, arg2, arg3, arg4 = getMethod(k, handler, options_p, "set",    skip1Nil(passValue, k))
-			local checked
-			if multiToggle then
-				checked = callMethod(k, handler, options_p, "get", skip1Nil(passValue, k)) or false
-				if arg2 == nil then
-					arg2 = not checked
-				elseif arg3 == nil then
-					arg3 = not checked
-				else
-					arg4 = not checked
-				end
-			else
-				checked = (k == current or (type(k) == "string" and type(current) == "string" and k:lower() == current:lower()))
-				if checked then
-					func, arg1, arg2, arg3, arg4 = nil, nil, nil, nil, nil
-				end
-			end
-			local tooltipTitle
-			local tooltipText
-			if options.validateDesc then
-				tooltipTitle = v
-				tooltipText = options.validateDesc[k]
-			else
-				tooltipTitle = options.guiName or options.name
-				tooltipText = v
-			end
-			self:AddLine(
-				'text', v,
-				'func', func,
-				'arg1', arg1,
-				'arg2', arg2,
-				'arg3', arg3,
-				'arg4', arg4,
-				'isRadio', not multiToggle,
-				'checked',  checked,
-				'tooltipTitle', tooltipTitle,
-				'tooltipText', tooltipText
-			)
-		end
-		for k in pairs(values) do
-			values[k] = nil
-		end
-	else
-		return false
-	end
-	return true
-end
-
-function Dewdrop:FeedTable(s, difference)
-	self:argCheck(s, 2, "table")
-	self:argCheck(difference, 3, "nil", "number")
-	if not currentLevel then
-		self:error("Cannot call `FeedTable' outside of a Dewdrop declaration")
-	end
-	if not difference then
-		difference = 0
-	end
-	local level = levels[currentLevel]
-	if not level then
-		self:error("Improper level given")
-	end
-	if not values then
-		values = {}
-	else
-		for k,v in pairs(values) do
-			values[k] = nil
-		end
-	end
-	local t = s.subMenu and s or {subMenu = s}
-	local current = level
-	while current do
-		if current.num == difference + 1 then
-			break
-		end
-		table.insert(values, current.value)
-		current = levels[current.num - 1]
-	end
-
-	while #values > 0 do
-		local value = table.remove(values)
-		t = t.subMenu and t.subMenu[value]
-		if not t then
-			return
-		end
-	end
-
-	if t.subMenu or current.num == 1 then
-		for k in pairs(t.subMenu) do
-			table.insert(values, k)
-		end
-		table.sort(values)
-		for _,k in ipairs(values) do
-			local argTable = {"value", k}
-			for key, val in pairs(t.subMenu[k]) do
-				table.insert(argTable, key)
-				table.insert(argTable, val)
-			end
-			self:AddLine(unpack(argTable))
-		end
-		for k in pairs(values) do
-			values[k] = nil
-		end
-		return false
-	end
-	return true
-end
-
-function Refresh(self, level)
-	if type(level) == "number" then
-		level = levels[level]
-	end
-	if not level then
-		return
-	end
-	if baseFunc then
-		Clear(self, level)
-		currentLevel = level.num
-		if type(baseFunc) == "table" then
-			if currentLevel == 1 then
-				local handler = baseFunc.handler
-				if handler then
-					local name = tostring(handler)
-					if not name:find('^table:') and not handler.hideMenuTitle then
-						name = name:gsub("|c%x%x%x%x%x%x%x%x(.-)|r", "%1")
-						self:AddLine(
-							'text', name,
-							'isTitle', true
-						)
-					end
-				end
---			elseif level.parentText then
---				self:AddLine(
---					'text', level.parentText,
---					'tooltipTitle', level.parentTooltipTitle,
---					'tooltipText', level.parentTooltipText,
---					'tooltipFunc', level.parentTooltipFunc,
---					'isTitle', true
---				)
-			end
-			self:FeedAceOptionsTable(baseFunc)
-			if currentLevel == 1 then
-				self:AddLine(
-					'text', CLOSE,
-					'tooltipTitle', CLOSE,
-					'tooltipText', CLOSE_DESC,
-					'closeWhenClicked', true
-				)
-			end
-		else
---			if level.parentText then
---				self:AddLine(
---					'text', level.parentText,
---					'tooltipTitle', level.parentTooltipTitle,
---					'tooltipText', level.parentTooltipText,
---					'tooltipFunc', level.parentTooltipFunc,
---					'isTitle', true
---				)
---			end
-			baseFunc(currentLevel, level.value, levels[level.num - 1] and levels[level.num - 1].value, levels[level.num - 2] and levels[level.num - 2].value, levels[level.num - 3] and levels[level.num - 3].value, levels[level.num - 4] and levels[level.num - 4].value)
-		end
-		currentLevel = nil
-		CheckSize(self, level)
-	end
-end
-
-function Dewdrop:Refresh(level)
-	self:argCheck(level, 2, "number", "nil")
-	if not level then
-		for k,v in pairs(levels) do
-			Refresh(self, v)
-		end
-	else
-		Refresh(self, levels[level])
-	end
-end
-
-function OpenSlider(self, parent)
-	if not sliderFrame then
-		sliderFrame = CreateFrame("Frame", nil, nil)
-		sliderFrame:SetWidth(100)
-		sliderFrame:SetHeight(170)
-		sliderFrame:SetScale(UIParent:GetScale())
-		sliderFrame:SetBackdrop(tmp(
-			'bgFile', "Interface\\Tooltips\\UI-Tooltip-Background",
-			'edgeFile', "Interface\\Tooltips\\UI-Tooltip-Border",
-			'tile', true,
-			'insets', tmp2(
-				'left', 5,
-				'right', 5,
-				'top', 5,
-				'bottom', 5
-			),
-			'tileSize', 16,
-			'edgeSize', 16
-		))
-		sliderFrame:SetFrameStrata("FULLSCREEN_DIALOG")
-		if sliderFrame.SetTopLevel then
-			sliderFrame:SetTopLevel(true)
-		end
-		sliderFrame:SetBackdropBorderColor(TOOLTIP_DEFAULT_COLOR.r, TOOLTIP_DEFAULT_COLOR.g, TOOLTIP_DEFAULT_COLOR.b)
-		sliderFrame:SetBackdropColor(TOOLTIP_DEFAULT_BACKGROUND_COLOR.r, TOOLTIP_DEFAULT_BACKGROUND_COLOR.g, TOOLTIP_DEFAULT_BACKGROUND_COLOR.b)
-		sliderFrame:EnableMouse(true)
-		sliderFrame:EnableMouseWheel(true)
-		sliderFrame:Hide()
-		sliderFrame:SetPoint("CENTER", UIParent, "CENTER")
-		local slider = CreateFrame("Slider", nil, sliderFrame)
-		sliderFrame.slider = slider
-		slider:SetOrientation("VERTICAL")
-		slider:SetMinMaxValues(0, 1)
-		slider:SetValueStep(0.000000001)
-		slider:SetValue(0.5)
-		slider:SetWidth(16)
-		slider:SetHeight(128)
-		slider:SetPoint("LEFT", sliderFrame, "LEFT", 15, 0)
-		slider:SetBackdrop(tmp(
-			'bgFile', "Interface\\Buttons\\UI-SliderBar-Background",
-			'edgeFile', "Interface\\Buttons\\UI-SliderBar-Border",
-			'tile', true,
-			'edgeSize', 8,
-			'tileSize', 8,
-			'insets', tmp2(
-				'left', 3,
-				'right', 3,
-				'top', 3,
-				'bottom', 3
-			)
-		))
-		local texture = slider:CreateTexture()
-		slider:SetThumbTexture("Interface\\Buttons\\UI-SliderBar-Button-Vertical")
-		local text = slider:CreateFontString(nil, "ARTWORK")
-		sliderFrame.topText = text
-		text:SetFontObject(GameFontGreenSmall)
-		text:SetText("100%")
-		text:SetPoint("BOTTOM", slider, "TOP")
-		local text = slider:CreateFontString(nil, "ARTWORK")
-		sliderFrame.bottomText = text
-		text:SetFontObject(GameFontGreenSmall)
-		text:SetText("0%")
-		text:SetPoint("TOP", slider, "BOTTOM")
-		local editBox = CreateFrame("EditBox", nil, sliderFrame)
-		sliderFrame.currentText = editBox
-		editBox:SetFontObject(ChatFontNormal)
-		editBox:SetHeight(13)
-		editBox:SetPoint("RIGHT", sliderFrame, "RIGHT", -16, 0)
-		editBox:SetPoint("LEFT", slider, "RIGHT", 12, 0)
-		editBox:SetText("50%")
-		editBox:SetJustifyH("CENTER")
-
-		local width = editBox:GetWidth()/2 + 10
-		local left = editBox:CreateTexture(nil, "BACKGROUND")
-		left:SetTexture("Interface\\ChatFrame\\UI-ChatInputBorder-Left")
-		left:SetTexCoord(0, width / 256, 0, 1)
-		left:SetWidth(width)
-		left:SetHeight(32)
-		left:SetPoint("LEFT", editBox, "LEFT", -10, 0)
-		local right = editBox:CreateTexture(nil, "BACKGROUND")
-		right:SetTexture("Interface\\ChatFrame\\UI-ChatInputBorder-Right")
-		right:SetTexCoord(1 - width / 256, 1, 0, 1)
-		right:SetWidth(width)
-		right:SetHeight(32)
-		right:SetPoint("RIGHT", editBox, "RIGHT", 10, 0)
-
-		local changed = false
-		local inside = false
-		slider:SetScript("OnValueChanged", function()
-			if sliderFrame.changing then
-				return
-			end
-			changed = true
-			local done = false
-			if sliderFrame.parent and sliderFrame.parent.sliderFunc then
-				local min = sliderFrame.parent.sliderMin or 0
-				local max = sliderFrame.parent.sliderMax or 1
-				local step
-				if sliderFrame.fineStep then
-					step = sliderFrame.parent.sliderStep or (max - min) / 100
-				else
-					step = sliderFrame.parent.sliderBigStep or sliderFrame.parent.sliderStep or (max - min) / 100
-				end
-				local value = (1 - slider:GetValue()) * (max - min) + min
-				if step > 0 then
-					value = math.floor((value - min) / step + 0.5) * step + min
-					if value > max then
-						value = max
-					elseif value < min then
-						value = min
-					end
-				end
-				if value == sliderFrame.lastValue then
-					return
-				end
-				sliderFrame.lastValue = value
-				local text = sliderFrame.parent.sliderFunc(getArgs(sliderFrame.parent, 'sliderArg', 1, value))
-				if sliderFrame.parent.fromAceOptions then
-					text = nil
-				elseif type(text) == "string" or type(text) == "number" then
-					sliderFrame.currentText:SetText(text)
-					done = true
-				end
-			end
-			if not done then
-				local min = sliderFrame.parent.sliderMin or 0
-				local max = sliderFrame.parent.sliderMax or 1
-				local step
-				if sliderFrame.fineStep then
-					step = sliderFrame.parent.sliderStep or (max - min) / 100
-				else
-					step = sliderFrame.parent.sliderBigStep or sliderFrame.parent.sliderStep or (max - min) / 100
-				end
-				local value = (1 - slider:GetValue()) * (max - min) + min
-				if step > 0 then
-					value = math.floor((value - min) / step + 0.5) * step + min
-					if value > max then
-						value = max
-					elseif value < min then
-						value = min
-					end
-				end
-				if sliderFrame.parent.sliderIsPercent then
-					sliderFrame.currentText:SetText(string.format("%.0f%%", value * 100))
-				else
-					if step < 0.1 then
-						sliderFrame.currentText:SetText(string.format("%.2f", value))
-					elseif step < 1 then
-						sliderFrame.currentText:SetText(string.format("%.1f", value))
-					else
-						sliderFrame.currentText:SetText(string.format("%.0f", value))
-					end
-				end
-			end
-		end)
-		local function onEnter()
-			StopCounting(self, sliderFrame.level)
-			showGameTooltip(sliderFrame.parent)
-		end
-		local function onLeave()
-			GameTooltip:Hide()
-		end
-		sliderFrame:SetScript("OnEnter", onEnter)
-		sliderFrame:SetScript("OnLeave", function()
-			GameTooltip:Hide()
-			if changed then
-				local parent = sliderFrame.parent
-				local sliderFunc = parent.sliderFunc
-				for i = 1, sliderFrame.level - 1 do
-					Refresh(self, levels[i])
-				end
-				local newParent
-				for _,button in ipairs(levels[sliderFrame.level-1].buttons) do
-					if button.sliderFunc == sliderFunc then
-						newParent = button
-						break
-					end
-				end
-				if newParent then
-					OpenSlider(self, newParent)
-				else
-					sliderFrame:Hide()
-				end
-			end
-		end)
-		editBox:SetScript("OnEnter", onEnter)
-		editBox:SetScript("OnLeave", onLeave)
-		slider:SetScript("OnMouseDown", function()
-			sliderFrame.mouseDown = true
-			GameTooltip:Hide()
-		end)
-		slider:SetScript("OnMouseUp", function()
-			sliderFrame.mouseDown = false
-			if changed--[[ and not inside]] then
-				local parent = sliderFrame.parent
-				local sliderFunc = parent.sliderFunc
-				for i = 1, sliderFrame.level - 1 do
-					Refresh(self, levels[i])
-				end
-				local newParent
-				for _,button in ipairs(levels[sliderFrame.level-1].buttons) do
-					if button.sliderFunc == sliderFunc then
-						newParent = button
-						break
-					end
-				end
-				if newParent then
-					OpenSlider(self, newParent)
-				else
-					sliderFrame:Hide()
-				end
-			end
-			if inside then
-				showGameTooltip(sliderFrame.parent)
-			end
-		end)
-		slider:SetScript("OnEnter", function()
-			inside = true
-			StopCounting(self, sliderFrame.level)
-			showGameTooltip(sliderFrame.parent)
-		end)
-		slider:SetScript("OnLeave", function()
-			inside = false
-			GameTooltip:Hide()
-			if changed and not sliderFrame.mouseDown then
-				local parent = sliderFrame.parent
-				local sliderFunc = parent.sliderFunc
-				for i = 1, sliderFrame.level - 1 do
-					Refresh(self, levels[i])
-				end
-				local newParent
-				for _,button in ipairs(levels[sliderFrame.level-1].buttons) do
-					if button.sliderFunc == sliderFunc then
-						newParent = button
-						break
-					end
-				end
-				if newParent then
-					OpenSlider(self, newParent)
-				else
-					sliderFrame:Hide()
-				end
-
-				changed = false
-			end
-		end)
-		sliderFrame:SetScript("OnMouseWheel", function(t, a1)
-			local arg1 = a1 or arg1
-			local up = arg1 > 0
-
-			local min = sliderFrame.parent.sliderMin or 0
-			local max = sliderFrame.parent.sliderMax or 1
-			local step = sliderFrame.parent.sliderStep or (max - min) / 100
-			if step <= 0 then
-				step = (max - min) / 100
-			end
-
-			local value = (1 - slider:GetValue()) * (max - min) + min
-			if up then
-				value = value + step
-			else
-				value = value - step
-			end
-			if value > max then
-				value = max
-			elseif value < min then
-				value = min
-			end
-			sliderFrame.fineStep = true
-			if max<=min then
-				slider:SetValue(0)
-			else
-				slider:SetValue(1 - (value - min) / (max - min))
-			end
-			sliderFrame.fineStep = nil
-		end)
-		slider:SetScript("OnMouseWheel", sliderFrame:GetScript("OnMouseWheel"))
-		editBox:SetScript("OnEnterPressed", function(t, a1)
-			local value = editBox:GetNumber()
-
-			if sliderFrame.parent.sliderIsPercent then
-				value = value / 100
-			end
-
-			local min = sliderFrame.parent.sliderMin or 0
-			local max = sliderFrame.parent.sliderMax or 1
-
-			if value > max then
-				value = max
-			elseif value < min then
-				value = min
-			end
-			sliderFrame.fineStep = true
-			if max <= min then
-				slider:SetValue(0)
-			else
-				slider:SetValue(1 - (value - min) / (max - min))
-			end
-			sliderFrame.fineStep = nil
-
-			StartCounting(self, sliderFrame.level)
-		end)
-		editBox:SetScript("OnEscapePressed", function()
-			self:Close(sliderFrame.level)
-			StartCounting(self, sliderFrame.level)
-		end)
-		editBox:SetAutoFocus(false)
-	end
-	sliderFrame.parent = parent
-	sliderFrame.level = parent.level.num + 1
-	sliderFrame.parentValue = parent.level.value
-	sliderFrame:SetFrameLevel(parent.level:GetFrameLevel() + 3)
-	sliderFrame.slider:SetFrameLevel(sliderFrame:GetFrameLevel() + 1)
-	sliderFrame.currentText:SetFrameLevel(sliderFrame:GetFrameLevel() + 1)
-	sliderFrame.currentText:ClearFocus()
-	sliderFrame.changing = true
-	if not parent.sliderMin or not parent.sliderMax then
-		return
-	end
-
-	if parent.arrow then
---		parent.arrow:SetVertexColor(0.2, 0.6, 0)
---		parent.arrow:SetHeight(24)
---		parent.arrow:SetWidth(24)
-		parent.selected = true
-		parent.highlight:Show()
-	end
-
-	sliderFrame:SetClampedToScreen(false)
-	if not parent.sliderValue then
-		parent.sliderValue = (parent.sliderMin + parent.sliderMax) / 2
-	end
-	if parent.sliderMax <= parent.sliderMin then
-		sliderFrame.slider:SetValue(0)
-	else
-		sliderFrame.slider:SetValue(1 - (parent.sliderValue - parent.sliderMin) / (parent.sliderMax - parent.sliderMin))
-	end
-	sliderFrame.changing = false
-	sliderFrame.bottomText:SetText(parent.sliderMinText or "0")
-	sliderFrame.topText:SetText(parent.sliderMaxText or "1")
-	local text
-	if parent.sliderFunc and not parent.fromAceOptions then
-		text = parent.sliderFunc(getArgs(parent, 'sliderArg', 1, parent.sliderValue))
-	end
-	if type(text) == "number" or type(text) == "string" then
-		sliderFrame.currentText:SetText(text)
-	elseif parent.sliderIsPercent then
-		sliderFrame.currentText:SetText(string.format("%.0f%%", parent.sliderValue * 100))
-	else
-		if parent.sliderStep < 0.1 then
-			sliderFrame.currentText:SetText(string.format("%.2f", parent.sliderValue))
-		elseif parent.sliderStep < 1 then
-			sliderFrame.currentText:SetText(string.format("%.1f", parent.sliderValue))
-		else
-			sliderFrame.currentText:SetText(string.format("%.0f", parent.sliderValue))
-		end
-	end
-
-
-	sliderFrame.lastValue = parent.sliderValue
-
-	local level = parent.level
-	sliderFrame:Show()
-	sliderFrame:ClearAllPoints()
-	if level.lastDirection == "RIGHT" then
-		if level.lastVDirection == "DOWN" then
-			sliderFrame:SetPoint("TOPLEFT", parent, "TOPRIGHT", 5, 10)
-		else
-			sliderFrame:SetPoint("BOTTOMLEFT", parent, "BOTTOMRIGHT", 5, -10)
-		end
-	else
-		if level.lastVDirection == "DOWN" then
-			sliderFrame:SetPoint("TOPRIGHT", parent, "TOPLEFT", -5, 10)
-		else
-			sliderFrame:SetPoint("BOTTOMRIGHT", parent, "BOTTOMLEFT", -5, -10)
-		end
-	end
-	local dirty
-	if level.lastDirection == "RIGHT" then
-		if sliderFrame:GetRight() > GetScreenWidth() then
-			level.lastDirection = "LEFT"
-			dirty = true
-		end
-	elseif sliderFrame:GetLeft() < 0 then
-		level.lastDirection = "RIGHT"
-		dirty = true
-	end
-	if level.lastVDirection == "DOWN" then
-		if sliderFrame:GetBottom() < 0 then
-			level.lastVDirection = "UP"
-			dirty = true
-		end
-	elseif sliderFrame:GetTop() > GetScreenWidth() then
-		level.lastVDirection = "DOWN"
-		dirty = true
-	end
-	if dirty then
-		sliderFrame:ClearAllPoints()
-		if level.lastDirection == "RIGHT" then
-			if level.lastVDirection == "DOWN" then
-				sliderFrame:SetPoint("TOPLEFT", parent, "TOPRIGHT", 5, 10)
-			else
-				sliderFrame:SetPoint("BOTTOMLEFT", parent, "BOTTOMRIGHT", 5, -10)
-			end
-		else
-			if level.lastVDirection == "DOWN" then
-				sliderFrame:SetPoint("TOPRIGHT", parent, "TOPLEFT", -5, 10)
-			else
-				sliderFrame:SetPoint("BOTTOMRIGHT", parent, "BOTTOMLEFT", -5, -10)
-			end
-		end
-	end
-	local left, bottom = sliderFrame:GetLeft(), sliderFrame:GetBottom()
-	sliderFrame:ClearAllPoints()
-	sliderFrame:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT", left, bottom)
-	if mod(level.num, 5) == 0 then
-		local left, bottom = level:GetLeft(), level:GetBottom()
-		level:ClearAllPoints()
-		level:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT", left, bottom)
-	end
-	sliderFrame:SetClampedToScreen(true)
-end
-
-function OpenEditBox(self, parent)
-	if not editBoxFrame then
-		editBoxFrame = CreateFrame("Frame", nil, nil)
-		editBoxFrame:SetWidth(200)
-		editBoxFrame:SetHeight(40)
-		editBoxFrame:SetScale(UIParent:GetScale())
-		editBoxFrame:SetBackdrop(tmp(
-			'bgFile', "Interface\\Tooltips\\UI-Tooltip-Background",
-			'edgeFile', "Interface\\Tooltips\\UI-Tooltip-Border",
-			'tile', true,
-			'insets', tmp2(
-				'left', 5,
-				'right', 5,
-				'top', 5,
-				'bottom', 5
-			),
-			'tileSize', 16,
-			'edgeSize', 16
-		))
-		editBoxFrame:SetFrameStrata("FULLSCREEN_DIALOG")
-		if editBoxFrame.SetTopLevel then
-			editBoxFrame:SetTopLevel(true)
-		end
-		editBoxFrame:SetBackdropBorderColor(TOOLTIP_DEFAULT_COLOR.r, TOOLTIP_DEFAULT_COLOR.g, TOOLTIP_DEFAULT_COLOR.b)
-		editBoxFrame:SetBackdropColor(TOOLTIP_DEFAULT_BACKGROUND_COLOR.r, TOOLTIP_DEFAULT_BACKGROUND_COLOR.g, TOOLTIP_DEFAULT_BACKGROUND_COLOR.b)
-		editBoxFrame:EnableMouse(true)
-		editBoxFrame:EnableMouseWheel(true)
-		editBoxFrame:Hide()
-		editBoxFrame:SetPoint("CENTER", UIParent, "CENTER")
-
-		local editBox = CreateFrame("EditBox", nil, editBoxFrame)
-		editBoxFrame.editBox = editBox
-		editBox:SetFontObject(ChatFontNormal)
-		editBox:SetWidth(160)
-		editBox:SetHeight(13)
-		editBox:SetPoint("CENTER", editBoxFrame, "CENTER", 0, 0)
-
-		local left = editBox:CreateTexture(nil, "BACKGROUND")
-		left:SetTexture("Interface\\ChatFrame\\UI-ChatInputBorder-Left")
-		left:SetTexCoord(0, 100 / 256, 0, 1)
-		left:SetWidth(100)
-		left:SetHeight(32)
-		left:SetPoint("LEFT", editBox, "LEFT", -10, 0)
-		local right = editBox:CreateTexture(nil, "BACKGROUND")
-		right:SetTexture("Interface\\ChatFrame\\UI-ChatInputBorder-Right")
-		right:SetTexCoord(156/256, 1, 0, 1)
-		right:SetWidth(100)
-		right:SetHeight(32)
-		right:SetPoint("RIGHT", editBox, "RIGHT", 10, 0)
-
-		editBox:SetScript("OnEnterPressed", function()
-			if editBoxFrame.parent and editBoxFrame.parent.editBoxValidateFunc then
-				local t = editBox.realText or editBox:GetText() or ""
-				local result = editBoxFrame.parent.editBoxValidateFunc(getArgs(editBoxFrame.parent, 'editBoxValidateArg', 1, t))
-				if not result then
-					UIErrorsFrame:AddMessage(VALIDATION_ERROR, 1, 0, 0)
-					return
-				end
-			end
-			if editBoxFrame.parent and editBoxFrame.parent.editBoxFunc then
-				local t
-				if editBox.realText ~= "NONE" then
-					t = editBox.realText or editBox:GetText() or ""
-				end
-				editBoxFrame.parent.editBoxFunc(getArgs(editBoxFrame.parent, 'editBoxArg', 1, t))
-			end
-			self:Close(editBoxFrame.level)
-			for i = 1, editBoxFrame.level - 1 do
-				Refresh(self, levels[i])
-			end
-			StartCounting(self, editBoxFrame.level-1)
-		end)
-		editBox:SetScript("OnEscapePressed", function()
-			self:Close(editBoxFrame.level)
-			StartCounting(self, editBoxFrame.level-1)
-		end)
-		editBox:SetScript("OnReceiveDrag", function(this)
-			if GetCursorInfo then
-				local type, alpha, bravo = GetCursorInfo()
-				local text
-				if type == "spell" then
-					text = GetSpellName(alpha, bravo)
-				elseif type == "item" then
-					text = bravo
-				end
-				if not text then
-					return
-				end
-				ClearCursor()
-				editBox:SetText(text)
-			end
-		end)
-		local changing = false
-		local skipNext = false
-
-		function editBox:SpecialSetText(text)
-			local oldText = editBox:GetText() or ""
-			if not text then
-				text = ""
-			end
-			if text ~= oldText then
-				changing = true
-				self:SetText(tostring(text))
-				changing = false
-				skipNext = true
-			end
-		end
-
-		editBox:SetScript("OnTextChanged", function()
-			if skipNext then
-				skipNext = false
-			elseif not changing and editBoxFrame.parent and editBoxFrame.parent.editBoxChangeFunc then
-				local t
-				if editBox.realText ~= "NONE" then
-					t = editBox.realText or editBox:GetText() or ""
-				end
-				local text = editBoxFrame.parent.editBoxChangeFunc(getArgs(editBoxFrame.parent, 'editBoxChangeArg', 1, t))
-				if text then
-					editBox:SpecialSetText(text)
-				end
-			end
-		end)
-		editBoxFrame:SetScript("OnEnter", function()
-			StopCounting(self, editBoxFrame.level)
-			showGameTooltip(editBoxFrame.parent)
-		end)
-		editBoxFrame:SetScript("OnLeave", function()
-			GameTooltip:Hide()
-		end)
-		editBox:SetScript("OnEnter", function()
-			StopCounting(self, editBoxFrame.level)
-			showGameTooltip(editBoxFrame.parent)
-		end)
-		editBox:SetScript("OnLeave", function()
-			GameTooltip:Hide()
-		end)
-		editBoxFrame:SetScript("OnKeyDown", function(this, a1)
-			if not editBox.keybinding then
-				return
-			end
-			local arg1 = a1 or arg1
-			local screenshotKey = GetBindingKey("SCREENSHOT")
-			if screenshotKey and arg1 == screenshotKey then
-				Screenshot()
-				return
-			end
-
-			if arg1 == "LeftButton" then
-				arg1 = "BUTTON1"
-			elseif arg1 == "RightButton" then
-				arg1 = "BUTTON2"
-			elseif arg1 == "MiddleButton" then
-				arg1 = "BUTTON3"
-			elseif arg1 == "Button4" then
-				arg1 = "BUTTON4"
-			elseif arg1 == "Button5" then
-				arg1 = "BUTTON5"
-			end
-			if arg1 == "UNKNOWN" then
-				return
-			elseif arg1 == "SHIFT" or arg1 == "CTRL" or arg1 == "ALT" then
-				return
-			elseif arg1 == "ENTER" then
-				if editBox.keybindingOnly and not editBox.keybindingOnly[editBox.realText] then
-					return editBox:GetScript("OnEscapePressed")()
-				elseif editBox.keybindingExcept and editBox.keybindingExcept[editBox.realText] then
-					return editBox:GetScript("OnEscapePressed")()
-				else
-					return editBox:GetScript("OnEnterPressed")()
-				end
-			elseif arg1 == "ESCAPE" then
-				if editBox.realText == "NONE" then
-					return editBox:GetScript("OnEscapePressed")()
-				else
-					editBox:SpecialSetText(NONE or "NONE")
-					editBox.realText = "NONE"
-					return
-				end
-			elseif editBox.keybindingOnly and not editBox.keybindingOnly[arg1] then
-				return
-			elseif editBox.keybindingExcept and editBox.keybindingExcept[arg1] then
-				return
-			end
-			local s = GetBindingText(arg1, "KEY_")
-			if s == "BUTTON1" then
-				s = KEY_BUTTON1
-			elseif s == "BUTTON2" then
-				s = KEY_BUTTON2
-			end
-			local real = arg1
-			if IsShiftKeyDown() then
-				s = "Shift-" .. s
-				real = "SHIFT-" .. real
-			end
-			if IsControlKeyDown() then
-				s = "Ctrl-" .. s
-				real = "CTRL-" .. real
-			end
-			if IsAltKeyDown() then
-				s = "Alt-" .. s
-				real = "ALT-" .. real
-			end
-			if editBox:GetText() ~= s then
-				editBox:SpecialSetText("-")
-				editBox:SpecialSetText(s)
-				editBox.realText = real
-				return editBox:GetScript("OnTextChanged")()
-			end
-		end)
-		editBoxFrame:SetScript("OnMouseDown", editBoxFrame:GetScript("OnKeyDown"))
-		editBox:SetScript("OnMouseDown", function(this, ...)
-			if GetCursorInfo and (CursorHasItem() or CursorHasSpell()) then
-				return editBox:GetScript("OnReceiveDrag")(this, ...)
-			end
-			return editBoxFrame:GetScript("OnKeyDown")(this, ...)
-		end)
-		editBoxFrame:SetScript("OnMouseWheel", function(t, a1)
-			local arg1 = a1 or arg1
-			local up = arg1 > 0
-			arg1 = up and "MOUSEWHEELUP" or "MOUSEWHEELDOWN"
-			return editBoxFrame:GetScript("OnKeyDown")(t or this, arg1)
-		end)
-		editBox:SetScript("OnMouseWheel", editBoxFrame:GetScript("OnMouseWheel"))
-	end
-	editBoxFrame.parent = parent
-	editBoxFrame.level = parent.level.num + 1
-	editBoxFrame.parentValue = parent.level.value
-	editBoxFrame:SetFrameLevel(parent.level:GetFrameLevel() + 3)
-	editBoxFrame.editBox:SetFrameLevel(editBoxFrame:GetFrameLevel() + 1)
-	editBoxFrame.editBox.realText = nil
-	editBoxFrame:SetClampedToScreen(false)
-
-	editBoxFrame.editBox:SpecialSetText("")
-	if parent.editBoxIsKeybinding then
-		local s = parent.editBoxText
-		if s == "" then
-			s =  "NONE"
-		end
-		editBoxFrame.editBox.realText = s
-		if s and s ~= "NONE" then
-			local alpha,bravo = s:match("^(.+)%-(.+)$")
-			if not bravo then
-				alpha = nil
-				bravo = s
-			end
-			bravo = GetBindingText(bravo, "KEY_")
-			if alpha then
-				editBoxFrame.editBox:SpecialSetText(alpha:upper() .. "-" .. bravo)
-			else
-				editBoxFrame.editBox:SpecialSetText(bravo)
-			end
-		else
-			editBoxFrame.editBox:SpecialSetText(NONE or "NONE")
-		end
-	else
-		editBoxFrame.editBox:SpecialSetText(parent.editBoxText)
-	end
-
-	editBoxFrame.editBox.keybinding = parent.editBoxIsKeybinding
-	editBoxFrame.editBox.keybindingOnly = parent.editBoxKeybindingOnly
-	editBoxFrame.editBox.keybindingExcept = parent.editBoxKeybindingExcept
-	editBoxFrame.editBox:EnableKeyboard(not parent.editBoxIsKeybinding)
-	editBoxFrame:EnableKeyboard(parent.editBoxIsKeybinding)
-
-	if parent.arrow then
---		parent.arrow:SetVertexColor(0.2, 0.6, 0)
---		parent.arrow:SetHeight(24)
---		parent.arrow:SetWidth(24)
-		parent.selected = true
-		parent.highlight:Show()
-	end
-
-	local level = parent.level
-	editBoxFrame:Show()
-	editBoxFrame:ClearAllPoints()
-	if level.lastDirection == "RIGHT" then
-		if level.lastVDirection == "DOWN" then
-			editBoxFrame:SetPoint("TOPLEFT", parent, "TOPRIGHT", 5, 10)
-		else
-			editBoxFrame:SetPoint("BOTTOMLEFT", parent, "BOTTOMRIGHT", 5, -10)
-		end
-	else
-		if level.lastVDirection == "DOWN" then
-			editBoxFrame:SetPoint("TOPRIGHT", parent, "TOPLEFT", -5, 10)
-		else
-			editBoxFrame:SetPoint("BOTTOMRIGHT", parent, "BOTTOMLEFT", -5, -10)
-		end
-	end
-	local dirty
-	if level.lastDirection == "RIGHT" then
-		if editBoxFrame:GetRight() > GetScreenWidth() then
-			level.lastDirection = "LEFT"
-			dirty = true
-		end
-	elseif editBoxFrame:GetLeft() < 0 then
-		level.lastDirection = "RIGHT"
-		dirty = true
-	end
-	if level.lastVDirection == "DOWN" then
-		if editBoxFrame:GetBottom() < 0 then
-			level.lastVDirection = "UP"
-			dirty = true
-		end
-	elseif editBoxFrame:GetTop() > GetScreenWidth() then
-		level.lastVDirection = "DOWN"
-		dirty = true
-	end
-	if dirty then
-		editBoxFrame:ClearAllPoints()
-		if level.lastDirection == "RIGHT" then
-			if level.lastVDirection == "DOWN" then
-				editBoxFrame:SetPoint("TOPLEFT", parent, "TOPRIGHT", 5, 10)
-			else
-				editBoxFrame:SetPoint("BOTTOMLEFT", parent, "BOTTOMRIGHT", 5, -10)
-			end
-		else
-			if level.lastVDirection == "DOWN" then
-				editBoxFrame:SetPoint("TOPRIGHT", parent, "TOPLEFT", -5, 10)
-			else
-				editBoxFrame:SetPoint("BOTTOMRIGHT", parent, "BOTTOMLEFT", -5, -10)
-			end
-		end
-	end
-	local left, bottom = editBoxFrame:GetLeft(), editBoxFrame:GetBottom()
-	editBoxFrame:ClearAllPoints()
-	editBoxFrame:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT", left, bottom)
-	if mod(level.num, 5) == 0 then
-		local left, bottom = level:GetLeft(), level:GetBottom()
-		level:ClearAllPoints()
-		level:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT", left, bottom)
-	end
-	editBoxFrame:SetClampedToScreen(true)
-end
-
-function Dewdrop:EncodeKeybinding(text)
-	if text == nil or text == "NONE" then
-		return nil
-	end
-	text = tostring(text):upper()
-	local shift, ctrl, alt
-	local modifier
-	while true do
-		if text == "-" then
-			break
-		end
-		modifier, text = strsplit('-', text, 2)
-		if text then
-			if modifier ~= "SHIFT" and modifier ~= "CTRL" and modifier ~= "ALT" then
-				return false
-			end
-			if modifier == "SHIFT" then
-				if shift then
-					return false
-				end
-				shift = true
-			end
-			if modifier == "CTRL" then
-				if ctrl then
-					return false
-				end
-				ctrl = true
-			end
-			if modifier == "ALT" then
-				if alt then
-					return false
-				end
-				alt = true
-			end
-		else
-			text = modifier
-			break
-		end
-	end
-	if not text:find("^F%d+$") and text ~= "CAPSLOCK" and text:len() ~= 1 and (text:len() == 0 or text:byte() < 128 or text:len() > 4) and not _G["KEY_" .. text] and text ~= "BUTTON1" and text ~= "BUTTON2" then
-		return false
-	end
-	local s = GetBindingText(text, "KEY_")
-	if s == "BUTTON1" then
-		s = KEY_BUTTON1
-	elseif s == "BUTTON2" then
-		s = KEY_BUTTON2
-	end
-	if shift then
-		s = "Shift-" .. s
-	end
-	if ctrl then
-		s = "Ctrl-" .. s
-	end
-	if alt then
-		s = "Alt-" .. s
-	end
-	return s
-end
-
-function Dewdrop:IsOpen(parent)
-	self:argCheck(parent, 2, "table", "string", "nil")
-	return levels[1] and levels[1]:IsShown() and (not parent or parent == levels[1].parent or parent == levels[1]:GetParent())
-end
-
-function Dewdrop:GetOpenedParent()
-	return (levels[1] and levels[1]:IsShown()) and (levels[1].parent or levels[1]:GetParent())
-end
-
-function Open(self, parent, func, level, value, point, relativePoint, cursorX, cursorY)
-	self:Close(level)
-	if DewdropLib then
-		local d = DewdropLib:GetInstance('1.0')
-		local ret, val = pcall(d, IsOpen, d)
-		if ret and val then
-			DewdropLib:GetInstance('1.0'):Close()
-		end
-	end
-	if type(parent) == "table" then
-		parent:GetCenter()
-	end
-	local frame = AcquireLevel(self, level)
-	if level == 1 then
-		frame.lastDirection = "RIGHT"
-		frame.lastVDirection = "DOWN"
-	else
-		frame.lastDirection = levels[level - 1].lastDirection
-		frame.lastVDirection = levels[level - 1].lastVDirection
-	end
-	frame:SetFrameStrata("FULLSCREEN_DIALOG")
-	frame:ClearAllPoints()
-	frame.parent = parent
-	frame:SetPoint("LEFT", UIParent, "RIGHT", 10000, 0)
-	frame:Show()
-	if level == 1 then
-		baseFunc = func
-	end
-	levels[level].value = value
---	levels[level].parentText = parent.text and parent.text:GetText() or nil
---	levels[level].parentTooltipTitle = parent.tooltipTitle
---	levels[level].parentTooltipText = parent.tooltipText
---	levels[level].parentTooltipFunc = parent.tooltipFunc
-	if type(parent) == "table" and parent.arrow then
---		parent.arrow:SetVertexColor(0.2, 0.6, 0)
---		parent.arrow:SetHeight(24)
---		parent.arrow:SetWidth(24)
-		parent.selected = true
-		parent.highlight:Show()
-	end
-	relativePoint = relativePoint or point
-	Refresh(self, levels[level])
-	if point or (cursorX and cursorY) then
-		frame:ClearAllPoints()
-		if cursorX and cursorY then
-			local curX, curY = GetScaledCursorPosition()
-			if curY < GetScreenHeight() / 2 then
-				point, relativePoint = "BOTTOM", "BOTTOM"
-			else
-				point, relativePoint = "TOP", "TOP"
-			end
-			if curX < GetScreenWidth() / 2 then
-				point, relativePoint = point .. "LEFT", relativePoint .. "RIGHT"
-			else
-				point, relativePoint = point .. "RIGHT", relativePoint .. "LEFT"
-			end
-		end
-		frame:SetPoint(point, type(parent) == "table" and parent or UIParent, relativePoint)
-		if cursorX and cursorY then
-			local left = frame:GetLeft()
-			local width = frame:GetWidth()
-			local bottom = frame:GetBottom()
-			local height = frame:GetHeight()
-			local curX, curY = GetScaledCursorPosition()
-			frame:ClearAllPoints()
-			relativePoint = relativePoint or point
-			if point == "BOTTOM" or point == "TOP" then
-				if curX < GetScreenWidth() / 2 then
-					point = point .. "LEFT"
-				else
-					point = point .. "RIGHT"
-				end
-			elseif point == "CENTER" then
-				if curX < GetScreenWidth() / 2 then
-					point = "LEFT"
-				else
-					point = "RIGHT"
-				end
-			end
-			local xOffset, yOffset = 0, 0
-			if curY > GetScreenHeight() / 2 then
-				yOffset = -height
-			end
-			if curX > GetScreenWidth() / 2 then
-				xOffset = -width
-			end
-			frame:SetPoint(point, type(parent) == "table" and parent or UIParent, relativePoint, curX - left + xOffset, curY - bottom + yOffset)
-			if level == 1 then
-				frame.lastDirection = "RIGHT"
-			end
-		elseif cursorX then
-			local left = frame:GetLeft()
-			local width = frame:GetWidth()
-			local curX, curY = GetScaledCursorPosition()
-			frame:ClearAllPoints()
-			relativePoint = relativePoint or point
-			if point == "BOTTOM" or point == "TOP" then
-				if curX < GetScreenWidth() / 2 then
-					point = point .. "LEFT"
-				else
-					point = point .. "RIGHT"
-				end
-			elseif point == "CENTER" then
-				if curX < GetScreenWidth() / 2 then
-					point = "LEFT"
-				else
-					point = "RIGHT"
-				end
-			end
-			frame:SetPoint(point, type(parent) == "table" and parent or UIParent, relativePoint, curX - left - width / 2, 0)
-			if level == 1 then
-				frame.lastDirection = "RIGHT"
-			end
-		elseif cursorY then
-			local bottom = frame:GetBottom()
-			local height = frame:GetHeight()
-			local curX, curY = GetScaledCursorPosition()
-			frame:ClearAllPoints()
-			relativePoint = relativePoint or point
-			if point == "LEFT" or point == "RIGHT" then
-				if curX < GetScreenHeight() / 2 then
-					point = point .. "BOTTOM"
-				else
-					point = point .. "TOP"
-				end
-			elseif point == "CENTER" then
-				if curX < GetScreenHeight() / 2 then
-					point = "BOTTOM"
-				else
-					point = "TOP"
-				end
-			end
-			frame:SetPoint(point, type(parent) == "table" and parent or UIParent, relativePoint, 0, curY - bottom - height / 2)
-			if level == 1 then
-				frame.lastDirection = "DOWN"
-			end
-		end
-		if (strsub(point, 1, 3) ~= strsub(relativePoint, 1, 3)) then
-			if frame:GetBottom() < 0 then
-				local point, parent, relativePoint, x, y = frame:GetPoint(1)
-				local change = GetScreenHeight() - frame:GetTop()
-				local otherChange = -frame:GetBottom()
-				if otherChange < change then
-					change = otherChange
-				end
-				frame:SetPoint(point, parent, relativePoint, x, y + change)
-			elseif frame:GetTop() > GetScreenHeight() then
-				local point, parent, relativePoint, x, y = frame:GetPoint(1)
-				local change = GetScreenHeight() - frame:GetTop()
-				local otherChange = -frame:GetBottom()
-				if otherChange < change then
-					change = otherChange
-				end
-				frame:SetPoint(point, parent, relativePoint, x, y + change)
-			end
-		end
-	end
-	CheckDualMonitor(self, frame)
-	frame:SetClampedToScreen(true)
-	frame:SetClampedToScreen(false)
-	StartCounting(self, level)
-end
-
-function Dewdrop:IsRegistered(parent)
-	self:argCheck(parent, 2, "table", "string")
-	return not not self.registry[parent]
-end
-
-function Dewdrop:Register(parent, ...)
-	self:argCheck(parent, 2, "table", "string")
-	if self.registry[parent] then
-		self:Unregister(parent)
-	end
-	local info = new(...)
-	if type(info.children) == "table" then
-		local err, position = validateOptions(info.children)
-
-		if err then
-			if position then
-				Dewdrop:error(position .. ": " .. err)
-			else
-				Dewdrop:error(err)
-			end
-		end
-	end
-	self.registry[parent] = info
-	if not info.dontHook and not self.onceRegistered[parent] and type(parent) == "table" then
-		if parent:HasScript("OnMouseUp") then
-			local script = parent:GetScript("OnMouseUp")
-			parent:SetScript("OnMouseUp", function(this, ...)
-				if script then
-					script(this, ...)
-				end
-				if arg1 == "RightButton" and self.registry[parent] then
-					if self:IsOpen(parent) then
-						self:Close()
-					else
-						self:Open(parent)
-					end
-				end
-			end)
-		end
-		if parent:HasScript("OnMouseDown") then
-			local script = parent:GetScript("OnMouseDown")
-			parent:SetScript("OnMouseDown", function(this, ...)
-				if script then
-					script(this, ...)
-				end
-				if self.registry[parent] then
-					self:Close()
-				end
-			end)
-		end
-	end
-	self.onceRegistered[parent] = true
-end
-
-function Dewdrop:Unregister(parent)
-	self:argCheck(parent, 2, "table", "string")
-	self.registry[parent] = nil
-end
-
-function Dewdrop:Open(parent, ...)
-	self:argCheck(parent, 2, "table", "string")
-	local info
-	local k1 = ...
-	if type(k1) == "table" and k1[0] and k1.IsFrameType and self.registry[k1] then
-		info = tmp(select(2, ...))
-		for k,v in pairs(self.registry[k1]) do
-			if info[k] == nil then
-				info[k] = v
-			end
-		end
-	else
-		info = tmp(...)
-		if self.registry[parent] then
-			for k,v in pairs(self.registry[parent]) do
-				if info[k] == nil then
-					info[k] = v
-				end
-			end
-		end
-	end
-	local point = info.point
-	local relativePoint = info.relativePoint
-	local cursorX = info.cursorX
-	local cursorY = info.cursorY
-	if type(point) == "function" then
-		local b
-		point, b = point(parent)
-		if b then
-			relativePoint = b
-		end
-	end
-	if type(relativePoint) == "function" then
-		relativePoint = relativePoint(parent)
-	end
-	Open(self, parent, info.children, 1, nil, point, relativePoint, cursorX, cursorY)
-end
-
-function Clear(self, level)
-	if level then
-		if level.buttons then
-			for i = #level.buttons, 1, -1 do
-				ReleaseButton(self, level, i)
-			end
-		end
-	end
-end
-
-function Dewdrop:Close(level)
-	if DropDownList1:IsShown() then
-		DropDownList1:Hide()
-	end
-	if DewdropLib then
-		local d = DewdropLib:GetInstance('1.0')
-		local ret, val = pcall(d, IsOpen, d)
-		if ret and val then
-			DewdropLib:GetInstance('1.0'):Close()
-		end
-	end
-	self:argCheck(level, 2, "number", "nil")
-	if not level then
-		level = 1
-	end
-	if level == 1 and levels[level] then
-		levels[level].parented = false
-	end
-	if level > 1 and levels[level-1].buttons then
-		local buttons = levels[level-1].buttons
-		for _,button in ipairs(buttons) do
---			button.arrow:SetWidth(16)
---			button.arrow:SetHeight(16)
-			button.selected = nil
-			button.highlight:Hide()
---			button.arrow:SetVertexColor(1, 1, 1)
-		end
-	end
-	if sliderFrame and sliderFrame.level >= level then
-		sliderFrame:Hide()
-	end
-	if editBoxFrame and editBoxFrame.level >= level then
-		editBoxFrame:Hide()
-	end
-	for i = level, #levels do
-		Clear(self, levels[level])
-		levels[i]:Hide()
-		levels[i]:ClearAllPoints()
-		levels[i]:SetPoint("CENTER", UIParent, "CENTER")
-		levels[i].value = nil
-	end
-end
-
-function Dewdrop:AddSeparator(level)
-	level = levels[level or currentLevel]
-	if not level or not level.buttons then return; end
-
-	local prevbutton = level.buttons[#level.buttons]
-	if not prevbutton then return; end
-
-	if prevbutton.disabled and prevbutton.text:GetText() == "" then
-		return
-	end
-	self:AddLine("text", "", "disabled", true)
-end
-
-function Dewdrop:AddLine(...)
-	local info = tmp(...)
-	local level = info.level or currentLevel
-	info.level = nil
-	local button = AcquireButton(self, level)
-	if not next(info) then
-		info.disabled = true
-	end
-	button.disabled = info.isTitle or info.notClickable or info.disabled or (self.combat and info.secure)
-	button.isTitle = info.isTitle
-	button.notClickable = info.notClickable
-	if button.isTitle then
-		button.text:SetFontObject(GameFontNormalSmall)
-	elseif button.notClickable then
-		button.text:SetFontObject(GameFontHighlightSmall)
-	elseif button.disabled then
-		button.text:SetFontObject(GameFontDisableSmall)
-	else
-		button.text:SetFontObject(GameFontHighlightSmall)
-	end
-	if info.disabled then
-		button.arrow:SetDesaturated(true)
-		button.check:SetDesaturated(true)
-	else
-		button.arrow:SetDesaturated(false)
-		button.check:SetDesaturated(false)
-	end
-	if info.textR and info.textG and info.textB then
-		button.textR = info.textR
-		button.textG = info.textG
-		button.textB = info.textB
-		button.text:SetTextColor(button.textR, button.textG, button.textB)
-	else
-		button.text:SetTextColor(button.text:GetFontObject():GetTextColor())
-	end
-	button.notCheckable = info.notCheckable
-	button.text:SetPoint("LEFT", button, "LEFT", button.notCheckable and 0 or 24, 0)
-	button.checked = not info.notCheckable and info.checked
-	button.mouseoverUnderline = info.mouseoverUnderline
-	button.isRadio = not info.notCheckable and info.isRadio
-	if info.isRadio then
-		button.check:Show()
-		button.check:SetTexture(info.checkIcon or "Interface\\Buttons\\UI-RadioButton")
-		if button.checked then
-			button.check:SetTexCoord(0.25, 0.5, 0, 1)
-			button.check:SetVertexColor(1, 1, 1, 1)
-		else
-			button.check:SetTexCoord(0, 0.25, 0, 1)
-			button.check:SetVertexColor(1, 1, 1, 0.5)
-		end
-		button.radioHighlight:SetTexture(info.checkIcon or "Interface\\Buttons\\UI-RadioButton")
-		button.check:SetWidth(16)
-		button.check:SetHeight(16)
-	elseif info.icon then
-		button.check:Show()
-		button.check:SetTexture(info.icon)
-		if info.iconWidth and info.iconHeight then
-			button.check:SetWidth(info.iconWidth)
-			button.check:SetHeight(info.iconHeight)
-		else
-			button.check:SetWidth(16)
-			button.check:SetHeight(16)
-		end
-		if info.iconCoordLeft and info.iconCoordRight and info.iconCoordTop and info.iconCoordBottom then
-			button.check:SetTexCoord(info.iconCoordLeft, info.iconCoordRight, info.iconCoordTop, info.iconCoordBottom)
-		elseif info.icon:find("^Interface\\Icons\\") then
-			button.check:SetTexCoord(0.05, 0.95, 0.05, 0.95)
-		else
-			button.check:SetTexCoord(0, 1, 0, 1)
-		end
-		button.check:SetVertexColor(1, 1, 1, 1)
-	else
-		if button.checked then
-			if info.checkIcon then
-				button.check:SetWidth(16)
-				button.check:SetHeight(16)
-				button.check:SetTexture(info.checkIcon)
-				if info.checkIcon:find("^Interface\\Icons\\") then
-					button.check:SetTexCoord(0.05, 0.95, 0.05, 0.95)
-				else
-					button.check:SetTexCoord(0, 1, 0, 1)
-				end
-			else
-				button.check:SetWidth(24)
-				button.check:SetHeight(24)
-				button.check:SetTexture("Interface\\Buttons\\UI-CheckBox-Check")
-				button.check:SetTexCoord(0, 1, 0, 1)
-			end
-			button.check:SetVertexColor(1, 1, 1, 1)
-		else
-			button.check:SetVertexColor(1, 1, 1, 0)
-		end
-	end
-	if not button.disabled then
-		button.func = info.func
-		button.secure = info.secure
-	end
-	button.hasColorSwatch = info.hasColorSwatch
-	if button.hasColorSwatch then
-		button.colorSwatch:Show()
-		button.colorSwatch.texture:Show()
-		button.r = info.r or 1
-		button.g = info.g or 1
-		button.b = info.b or 1
-		button.colorSwatch.texture:SetVertexColor(button.r, button.g, button.b)
-		button.checked = false
-		button.func = nil
-		button.colorFunc = info.colorFunc
-		local i = 1
-		while true do
-			local k = "colorArg" .. i
-			local x = info[k]
-			if x == nil then
-				break
-			end
-			button[k] = x
-			i = i + 1
-		end
-		button.hasOpacity = info.hasOpacity
-		button.opacity = info.opacity or 1
-	else
-		button.colorSwatch:Hide()
-		button.colorSwatch.texture:Hide()
-	end
-	button.hasArrow = not button.hasColorSwatch and (info.value or info.hasSlider or info.hasEditBox) and info.hasArrow
-	if button.hasArrow then
-		button.arrow:SetAlpha(1)
-		if info.hasSlider then
-			button.hasSlider = true
-			button.sliderMin = info.sliderMin or 0
-			button.sliderMax = info.sliderMax or 1
-			button.sliderStep = info.sliderStep or 0
-			button.sliderBigStep = info.sliderBigStep or button.sliderStep
-			if button.sliderBigStep < button.sliderStep then
-				button.sliderBigStep = button.sliderStep
-			end
-			button.sliderIsPercent = info.sliderIsPercent and true or false
-			button.sliderMinText = info.sliderMinText or button.sliderIsPercent and string.format("%.0f%%", button.sliderMin * 100) or button.sliderMin
-			button.sliderMaxText = info.sliderMaxText or button.sliderIsPercent and string.format("%.0f%%", button.sliderMax * 100) or button.sliderMax
-			button.sliderFunc = info.sliderFunc
-			button.sliderValue = info.sliderValue
-			button.fromAceOptions = info.fromAceOptions
-			local i = 1
-			while true do
-				local k = "sliderArg" .. i
-				local x = info[k]
-				if x == nil then
-					break
-				end
-				button[k] = x
-				i = i + 1
-			end
-		elseif info.hasEditBox then
-			button.hasEditBox = true
-			button.editBoxText = info.editBoxText or ""
-			button.editBoxFunc = info.editBoxFunc
-			local i = 1
-			while true do
-				local k = "editBoxArg" .. i
-				local x = info[k]
-				if x == nil then
-					break
-				end
-				button[k] = x
-				i = i + 1
-			end
-			button.editBoxChangeFunc = info.editBoxChangeFunc
-			local i = 1
-			while true do
-				local k = "editBoxChangeArg" .. i
-				local x = info[k]
-				if x == nil then
-					break
-				end
-				button[k] = x
-				i = i + 1
-			end
-			button.editBoxValidateFunc = info.editBoxValidateFunc
-			local i = 1
-			while true do
-				local k = "editBoxValidateArg" .. i
-				local x = info[k]
-				if x == nil then
-					break
-				end
-				button[k] = x
-				i = i + 1
-			end
-			button.editBoxIsKeybinding = info.editBoxIsKeybinding
-			button.editBoxKeybindingOnly = info.editBoxKeybindingOnly
-			button.editBoxKeybindingExcept = info.editBoxKeybindingExcept
-		else
-			button.value = info.value
-			local l = levels[level+1]
-			if l and info.value == l.value then
---				button.arrow:SetWidth(24)
---				button.arrow:SetHeight(24)
-				button.selected = true
-				button.highlight:Show()
-			end
-		end
-	else
-		button.arrow:SetAlpha(0)
-	end
-	local i = 1
-	while true do
-		local k = "arg" .. i
-		local x = info[k]
-		if x == nil then
-			break
-		end
-		button[k] = x
-		i = i + 1
-	end
-	button.closeWhenClicked = info.closeWhenClicked
-	button.textHeight = info.textHeight or UIDROPDOWNMENU_DEFAULT_TEXT_HEIGHT or 10
-	local font,_ = button.text:GetFont()
-	button.text:SetFont(STANDARD_TEXT_FONT or "Fonts\\FRIZQT__.TTF", button.textHeight)
-	button:SetHeight(button.textHeight + 6)
-	button.text:SetPoint("RIGHT", button.arrow, (button.hasColorSwatch or button.hasArrow) and "LEFT" or "RIGHT")
-	button.text:SetJustifyH(info.justifyH or "LEFT")
-	button.text:SetText(info.text)
-	button.tooltipTitle = info.tooltipTitle
-	button.tooltipText = info.tooltipText
-	button.tooltipFunc = info.tooltipFunc
-	local i = 1
-	while true do
-		local k = "tooltipArg" .. i
-		local x = info[k]
-		if x == nil then
-			break
-		end
-		button[k] = x
-		i = i + 1
-	end
-	if not button.tooltipTitle and not button.tooltipText and not button.tooltipFunc and not info.isTitle then
-		button.tooltipTitle = info.text
-	end
-	if type(button.func) == "string" then
-		if type(button.arg1) ~= "table" then
-			self:error("Cannot call method %q on a non-table", button.func)
-		end
-		if type(button.arg1[button.func]) ~= "function" then
-			self:error("Method %q nonexistant.", button.func)
-		end
-	end
-end
-
-function Dewdrop:InjectAceOptionsTable(handler, options)
-	self:argCheck(handler, 2, "table")
-	self:argCheck(options, 3, "table")
-	if tostring(options.type):lower() ~= "group" then
-		self:error('Cannot inject into options table argument #3 if its type is not "group"')
-	end
-	if options.handler ~= nil and options.handler ~= handler then
-		self:error("Cannot inject into options table argument #3 if it has a different handler than argument #2")
-	end
-	options.handler = handler
-	local class = handler.class
-	if not AceLibrary:HasInstance("AceOO-2.0") or not class then
-		if Rock then
-			-- possible Rock object
-			for mixin in Rock:IterateObjectMixins(handler) do
-				if type(mixin.GetAceOptionsDataTable) == "function" then
-					local t = mixin:GetAceOptionsDataTable(handler)
-					for k,v in pairs(t) do
-						if type(options.args) ~= "table" then
-							options.args = {}
-						end
-						if options.args[k] == nil then
-							options.args[k] = v
-						end
-					end
-				end
-			end
-		end
-	else
-		-- Ace2 object
-		while class and class ~= AceLibrary("AceOO-2.0").Class do
-			if type(class.GetAceOptionsDataTable) == "function" then
-				local t = class:GetAceOptionsDataTable(handler)
-				for k,v in pairs(t) do
-					if type(options.args) ~= "table" then
-						options.args = {}
-					end
-					if options.args[k] == nil then
-						options.args[k] = v
-					end
-				end
-			end
-			local mixins = class.mixins
-			if mixins then
-				for mixin in pairs(mixins) do
-					if type(mixin.GetAceOptionsDataTable) == "function" then
-						local t = mixin:GetAceOptionsDataTable(handler)
-						for k,v in pairs(t) do
-							if type(options.args) ~= "table" then
-								options.args = {}
-							end
-							if options.args[k] == nil then
-								options.args[k] = v
-							end
-						end
-					end
-				end
-			end
-			class = class.super
-		end
-	end
-	return options
-end
-
-function Dewdrop:OnTooltipHide()
-	if lastSetFont then
-		if lastSetFont == normalFont then
-			lastSetFont = nil
-			return
-		end
-		fillRegionTmp(GameTooltip:GetRegions())
-		for i,v in ipairs(regionTmp) do
-			if v.GetFont then
-				local font,size,outline = v:GetFont()
-				if font == lastSetFont then
-					v:SetFont(normalFont, size, outline)
-				end
-			end
-			regionTmp[i] = nil
-		end
-		lastSetFont = nil
-	end
-end
-
-local function activate(self, oldLib, oldDeactivate)
-	Dewdrop = self
-	if oldLib and oldLib.registry then
-		self.registry = oldLib.registry
-		self.onceRegistered = oldLib.onceRegistered
-	else
-		self.registry = {}
-		self.onceRegistered = {}
-
-		local WorldFrame_OnMouseDown = WorldFrame:GetScript("OnMouseDown")
-		local WorldFrame_OnMouseUp = WorldFrame:GetScript("OnMouseUp")
-		local oldX, oldY, clickTime
-		WorldFrame:SetScript("OnMouseDown", function(this, ...)
-			oldX,oldY = GetCursorPosition()
-			clickTime = GetTime()
-			if WorldFrame_OnMouseDown then
-				WorldFrame_OnMouseDown(this, ...)
-			end
-		end)
-
-		WorldFrame:SetScript("OnMouseUp", function(this, ...)
-			local x,y = GetCursorPosition()
-			if not oldX or not oldY or not x or not y or not clickTime then
-				self:Close()
-				if WorldFrame_OnMouseUp then
-					WorldFrame_OnMouseUp(this, ...)
-				end
-				return
-			end
-			local d = math.abs(x - oldX) + math.abs(y - oldY)
-			if d <= 5 and GetTime() - clickTime < 0.5 then
-				self:Close()
-			end
-			if WorldFrame_OnMouseUp then
-				WorldFrame_OnMouseUp(this, ...)
-			end
-		end)
-
-		hooksecurefunc(DropDownList1, "Show", function()
-			if levels[1] and levels[1]:IsVisible() then
-				self:Close()
-			end
-		end)
-
-		hooksecurefunc("HideDropDownMenu", function()
-			if levels[1] and levels[1]:IsVisible() then
-				self:Close()
-			end
-		end)
-
-		hooksecurefunc("CloseDropDownMenus", function()
-			if levels[1] and levels[1]:IsVisible() then
-				local stack = debugstack()
-				if not stack:find("`TargetFrame_OnHide'") then
-					self:Close()
-				end
-			end
-		end)
-	end
-	self.frame = oldLib and oldLib.frame or CreateFrame("Frame")
-	self.frame:UnregisterAllEvents()
-	self.frame:RegisterEvent("PLAYER_REGEN_ENABLED")
-	self.frame:RegisterEvent("PLAYER_REGEN_DISABLED")
-	self.frame:Hide()
-	self.frame:SetScript("OnEvent", function(this, event)
-		this:Show()
-		if event=="PLAYER_REGEN_ENABLED" then			-- track combat state for secure frame operations
-			self.combat = false
-		elseif event=="PLAYER_REGEN_DISABLED" then
-			self.combat = true
-		end
-	end)
-	self.frame:SetScript("OnUpdate", function(this)
-		this:Hide()
-		self:Refresh(1)
-	end)
-	self.hookedTooltip = true
-	if not oldLib or not oldLib.hookedTooltip then
-		local OnTooltipHide = GameTooltip:GetScript("OnHide")
-		GameTooltip:SetScript("OnHide", function(this, ...)
-			if OnTooltipHide then
-				OnTooltipHide(this, ...)
-			end
-			if type(self.OnTooltipHide) == "function" then
-				self:OnTooltipHide()
-			end
-		end)
-	end
-	levels = {}
-	buttons = {}
-
-	if oldDeactivate then
-		oldDeactivate(oldLib)
-	end
-end
-
-local function external(lib, major, instance)
-	if major == "SharedMedia-1.0" then
-		SharedMedia = instance
-	end
-end
-
-AceLibrary:Register(Dewdrop, MAJOR_VERSION, MINOR_VERSION, activate, nil, external)
+--[[
+Name: Dewdrop-2.0
+Revision: $Rev: 56529 $
+Author(s): ckknight (ckknight@gmail.com)
+Website: http://ckknight.wowinterface.com/
+Documentation: http://wiki.wowace.com/index.php/Dewdrop-2.0
+SVN: http://svn.wowace.com/root/trunk/DewdropLib/Dewdrop-2.0
+Description: A library to provide a clean dropdown menu interface.
+Dependencies: AceLibrary
+License: LGPL v2.1
+]]
+
+local MAJOR_VERSION = "Dewdrop-2.0"
+local MINOR_VERSION = "$Revision: 56529 $"
+
+if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary") end
+if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end
+
+local Dewdrop = {}
+
+local SharedMedia
+
+local CLOSE = "Close"
+local CLOSE_DESC = "Close the menu."
+local VALIDATION_ERROR = "Validation error."
+local USAGE_TOOLTIP = "Usage: %s."
+local RANGE_TOOLTIP = "Note that you can scroll your mouse wheel while over the slider to step by one."
+local RESET_KEYBINDING_DESC = "Hit escape to clear the keybinding."
+local KEY_BUTTON1 = "Left Mouse"
+local KEY_BUTTON2 = "Right Mouse"
+local DISABLED = "Disabled"
+local DEFAULT_CONFIRM_MESSAGE = "Are you sure you want to perform `%s'?"
+
+if GetLocale() == "deDE" then
+	CLOSE = "Schlie\195\159en"
+	CLOSE_DESC = "Men\195\188 schlie\195\159en."
+	VALIDATION_ERROR = "Validierungsfehler."
+	USAGE_TOOLTIP = "Benutzung: %s."
+	RANGE_TOOLTIP = "Beachte das du mit dem Mausrad scrollen kannst solange du mit dem Mauszeiger \195\188ber dem Schieberegler bist, um feinere Spr\195\188nge zu machen."
+	RESET_KEYBINDING_DESC = "Escape dr\195\188cken, um die Tastenbelegung zu l\195\182schen."
+	KEY_BUTTON1 = "Linke Maustaste"
+	KEY_BUTTON2 = "Rechte Maustaste"
+	DISABLED = "Deaktiviert"
+	DEFAULT_CONFIRM_MESSAGE = "Bist du sicher das du `%s' machen willst?"
+elseif GetLocale() == "koKR" then
+	CLOSE = "닫기"
+	CLOSE_DESC = "메뉴를 닫습니다."
+	VALIDATION_ERROR = "오류 확인."
+	USAGE_TOOLTIP = "사용법: %s."
+	RANGE_TOOLTIP = "알림 : 슬라이더 위에서 마우스 휠을 사용하면 한단계씩 조절할 수 있습니다."
+	RESET_KEYBINDING_DESC = "단축키를 해제하려면 ESC키를 누르세요."
+	KEY_BUTTON1 = "왼쪽 마우스"
+	KEY_BUTTON2 = "오른쪽 마우스"
+	DISABLED = "비활성화됨"
+	DEFAULT_CONFIRM_MESSAGE = "정말로 `%s' 실행을 하시겠습니까 ?"
+elseif GetLocale() == "frFR" then
+	CLOSE = "Fermer"
+	CLOSE_DESC = "Ferme le menu."
+	VALIDATION_ERROR = "Erreur de validation."
+	USAGE_TOOLTIP = "Utilisation : %s."
+	RANGE_TOOLTIP = "Vous pouvez aussi utiliser la molette de la souris pour pour modifier progressivement."
+	RESET_KEYBINDING_DESC = "Appuyez sur la touche Echappement pour effacer le raccourci."
+	KEY_BUTTON1 = "Clic gauche"
+	KEY_BUTTON2 = "Clic droit"
+	DISABLED = "D\195\169sactiv\195\169"
+	DEFAULT_CONFIRM_MESSAGE = "\195\138tes-vous s\195\187r de vouloir effectuer '%s' ?"
+elseif GetLocale() == "esES" then
+	CLOSE = "Cerrar"
+	CLOSE_DESC = "Cierra el menú."
+	VALIDATION_ERROR = "Error de validación."
+	USAGE_TOOLTIP = "Uso: %s."
+	RANGE_TOOLTIP = "Puedes desplazarte verticalmente con la rueda del ratón sobre el desplazador."
+	RESET_KEYBINDING_DESC = "Pulsa Escape para borrar la asignación de tecla."
+	KEY_BUTTON1 = "Clic Izquierdo"
+	KEY_BUTTON2 = "Clic Derecho"
+	DISABLED = "Desactivado"
+	DEFAULT_CONFIRM_MESSAGE = "¿Estás seguro de querer realizar `%s'?"
+elseif GetLocale() == "zhTW" then
+	CLOSE = "關閉"
+	CLOSE_DESC = "關閉選單。"
+	VALIDATION_ERROR = "驗證錯誤。"
+	USAGE_TOOLTIP = "用法: %s。"
+	RANGE_TOOLTIP = "你可以在捲動條上使用滑鼠滾輪來捲動。"
+	RESET_KEYBINDING_DESC = "按Esc鍵清除快捷鍵。"
+	KEY_BUTTON1 = "滑鼠左鍵"
+	KEY_BUTTON2 = "滑鼠右鍵"
+	DISABLED = "停用"
+	DEFAULT_CONFIRM_MESSAGE = "是否執行「%s」?"
+elseif GetLocale() == "zhCN" then
+	CLOSE = "关闭"
+	CLOSE_DESC = "关闭菜单"
+	VALIDATION_ERROR = "验证错误."
+	USAGE_TOOLTIP = "用法: %s."
+	RANGE_TOOLTIP = "你可以在滚动条上使用鼠标滚轮来翻页."
+	RESET_KEYBINDING_DESC = "按ESC键清除按键绑定"
+	KEY_BUTTON1 = "鼠标左键"
+	KEY_BUTTON2 = "鼠标右键"
+	DISABLED = "禁用"
+	DEFAULT_CONFIRM_MESSAGE = "是否执行'%s'?"
+end
+
+Dewdrop.KEY_BUTTON1 = KEY_BUTTON1
+Dewdrop.KEY_BUTTON2 = KEY_BUTTON2
+
+local function new(...)
+	local t = {}
+	for i = 1, select('#', ...), 2 do
+		local k = select(i, ...)
+		if k then
+			t[k] = select(i+1, ...)
+		else
+			break
+		end
+	end
+	return t
+end
+
+local tmp
+do
+	local t = {}
+	function tmp(...)
+		for k in pairs(t) do
+			t[k] = nil
+		end
+		for i = 1, select('#', ...), 2 do
+			local k = select(i, ...)
+			if k then
+				t[k] = select(i+1, ...)
+			else
+				break
+			end
+		end
+		return t
+	end
+end
+local tmp2
+do
+	local t = {}
+	function tmp2(...)
+		for k in pairs(t) do
+			t[k] = nil
+		end
+		for i = 1, select('#', ...), 2 do
+			local k = select(i, ...)
+			if k then
+				t[k] = select(i+1, ...)
+			else
+				break
+			end
+		end
+		return t
+	end
+end
+local levels
+local buttons
+
+
+-- Secure frame handling:
+-- Rather than using secure buttons in the menu (has problems), we have one
+-- master secureframe that we pop onto menu items on mouseover. This requires
+-- some dark magic with OnLeave etc, but it's not too bad.
+
+local secureFrame = CreateFrame("Button", nil, nil, "SecureActionButtonTemplate")
+secureFrame:Hide()
+
+local function secureFrame_Show(self)
+  local owner = self.owner
+
+  if self.secure then	-- Leftovers from previos owner, clean up! ("Shouldn't" happen but does..)
+	  for k,v in pairs(self.secure) do
+	    self:SetAttribute(k, nil)
+	  end
+  end
+  self.secure = owner.secure;	-- Grab hold of new secure data
+
+  local scale = owner:GetEffectiveScale()
+
+  self:SetPoint("TOPLEFT", nil, "BOTTOMLEFT", owner:GetLeft() * scale, owner:GetTop() * scale)
+  self:SetPoint("BOTTOMRIGHT", nil, "BOTTOMLEFT", owner:GetRight() * scale, owner:GetBottom() * scale)
+  self:EnableMouse(true)
+  for k,v in pairs(self.secure) do
+    self:SetAttribute(k, v)
+  end
+
+	secureFrame:SetFrameStrata(owner:GetFrameStrata())
+	secureFrame:SetFrameLevel(owner:GetFrameLevel()+1)
+
+  self:Show()
+end
+
+local function secureFrame_Hide(self)
+  self:Hide()
+  if self.secure then
+	  for k,v in pairs(self.secure) do
+	    self:SetAttribute(k, nil)
+	  end
+	end
+  self.secure = nil
+end
+
+secureFrame:SetScript("OnEvent",
+	function()
+		if event=="PLAYER_REGEN_ENABLED" then
+			this.combat = false
+			if not this:IsShown() and this.owner then
+				secureFrame_Show(this)
+			end
+		elseif event=="PLAYER_REGEN_DISABLED" then
+			this.combat = true
+			if this:IsShown() then
+				secureFrame_Hide(this)
+			end
+		end
+	end
+)
+secureFrame:RegisterEvent("PLAYER_REGEN_ENABLED")
+secureFrame:RegisterEvent("PLAYER_REGEN_DISABLED")
+
+secureFrame:SetScript("OnLeave",
+	function()
+		local owner=this.owner
+		this:Deactivate()
+		owner:GetScript("OnLeave")()
+	end
+)
+
+secureFrame:HookScript("OnClick",
+	function()
+		local realthis = this
+		this = this.owner
+		this:GetScript("OnClick")()
+	end
+)
+
+function secureFrame:IsOwnedBy(frame)
+	return self.owner == frame
+end
+
+function secureFrame:Activate(owner)
+	if self.owner then		-- "Shouldn't" happen but apparently it does and I cba to troubleshoot...
+		if not self.combat then
+			secureFrame_Hide(self)
+		end
+	end
+	self.owner = owner
+	if not self.combat then
+		secureFrame_Show(self)
+	end
+end
+
+function secureFrame:Deactivate()
+	if not self.combat then
+		secureFrame_Hide(self)
+	end
+	self.owner = nil
+end
+
+-- END secure frame utilities
+
+
+-- Underline on mouseover - use a single global underline that we move around, no point in creating lots of copies
+local underlineFrame = CreateFrame("Frame", nil)
+underlineFrame.tx = underlineFrame:CreateTexture()
+underlineFrame.tx:SetTexture(1,1,0.5,0.75)
+underlineFrame:SetScript("OnHide", function(this) this:Hide(); end)
+underlineFrame:SetScript("OnShow", function(this) 	-- change sizing on the fly to catch runtime uiscale changes
+    underlineFrame.tx:SetPoint("TOPLEFT", -1, -2/this:GetEffectiveScale())
+    underlineFrame.tx:SetPoint("RIGHT", 1,0)
+    underlineFrame.tx:SetHeight(0.6 / this:GetEffectiveScale());
+end)
+underlineFrame:SetHeight(1)
+
+-- END underline on mouseover
+
+
+local function GetScaledCursorPosition()
+	local x, y = GetCursorPosition()
+	local scale = UIParent:GetEffectiveScale()
+	return x / scale, y / scale
+end
+
+local function StartCounting(self, level)
+	for i = level, 1, -1 do
+		if levels[i] then
+			levels[i].count = 3
+		end
+	end
+end
+
+local function StopCounting(self, level)
+	for i = level, 1, -1 do
+		if levels[i] then
+			levels[i].count = nil
+		end
+	end
+end
+
+local function OnUpdate(self, elapsed)
+	for _,level in ipairs(levels) do
+		local count = level.count
+		if count then
+			count = count - elapsed
+			if count < 0 then
+				level.count = nil
+				self:Close(level.num)
+			else
+				level.count = count
+			end
+		end
+	end
+end
+
+local function CheckDualMonitor(self, frame)
+	local ratio = GetScreenWidth() / GetScreenHeight()
+	if ratio >= 2.4 and frame:GetRight() > GetScreenWidth() / 2 and frame:GetLeft() < GetScreenWidth() / 2 then
+		local offsetx
+		if GetCursorPosition() / GetScreenHeight() * 768 < GetScreenWidth() / 2 then
+			offsetx = GetScreenWidth() / 2 - frame:GetRight()
+		else
+			offsetx = GetScreenWidth() / 2 - frame:GetLeft()
+		end
+		local point, parent, relativePoint, x, y = frame:GetPoint(1)
+		frame:SetPoint(point, parent, relativePoint, (x or 0) + offsetx, y or 0)
+	end
+end
+
+local function CheckSize(self, level)
+	if not level.buttons then
+		return
+	end
+	local height = 20
+	for _, button in ipairs(level.buttons) do
+		height = height + button:GetHeight()
+	end
+	level:SetHeight(height)
+	local width = 160
+	for _, button in ipairs(level.buttons) do
+		local extra = 1
+		if button.hasArrow or button.hasColorSwatch then
+			extra = extra + 16
+		end
+		if not button.notCheckable then
+			extra = extra + 24
+		end
+		button.text:SetFont(STANDARD_TEXT_FONT, button.textHeight)
+		if button.text:GetWidth() + extra > width then
+			width = button.text:GetWidth() + extra
+		end
+	end
+	level:SetWidth(width + 20)
+	if level:GetLeft() and level:GetRight() and level:GetTop() and level:GetBottom() and (level:GetLeft() < 0 or level:GetRight() > GetScreenWidth() or level:GetTop() > GetScreenHeight() or level:GetBottom() < 0) then
+		level:ClearAllPoints()
+		local parent = level.parent or level:GetParent()
+		if type(parent) ~= "table" then
+			parent = UIParent
+		end
+		if level.lastDirection == "RIGHT" then
+			if level.lastVDirection == "DOWN" then
+				level:SetPoint("TOPLEFT", parent, "TOPRIGHT", 5, 10)
+			else
+				level:SetPoint("BOTTOMLEFT", parent, "BOTTOMRIGHT", 5, -10)
+			end
+		else
+			if level.lastVDirection == "DOWN" then
+				level:SetPoint("TOPRIGHT", parent, "TOPLEFT", -5, 10)
+			else
+				level:SetPoint("BOTTOMRIGHT", parent, "BOTTOMLEFT", -5, -10)
+			end
+		end
+	end
+	local dirty = false
+	if not level:GetRight() then
+		self:Close()
+		return
+	end
+	if level:GetRight() > GetScreenWidth() and level.lastDirection == "RIGHT" then
+		level.lastDirection = "LEFT"
+		dirty = true
+	elseif level:GetLeft() < 0 and level.lastDirection == "LEFT" then
+		level.lastDirection = "RIGHT"
+		dirty = true
+	end
+	if level:GetTop() > GetScreenHeight() and level.lastVDirection == "UP" then
+		level.lastVDirection = "DOWN"
+		dirty = true
+	elseif level:GetBottom() < 0 and level.lastVDirection == "DOWN" then
+		level.lastVDirection = "UP"
+		dirty = true
+	end
+	if dirty then
+		level:ClearAllPoints()
+		local parent = level.parent or level:GetParent()
+		if type(parent) ~= "table" then
+			parent = UIParent
+		end
+		if level.lastDirection == "RIGHT" then
+			if level.lastVDirection == "DOWN" then
+				level:SetPoint("TOPLEFT", parent, "TOPRIGHT", 5, 10)
+			else
+				level:SetPoint("BOTTOMLEFT", parent, "BOTTOMRIGHT", 5, -10)
+			end
+		else
+			if level.lastVDirection == "DOWN" then
+				level:SetPoint("TOPRIGHT", parent, "TOPLEFT", -5, 10)
+			else
+				level:SetPoint("BOTTOMRIGHT", parent, "BOTTOMLEFT", -5, -10)
+			end
+		end
+	end
+	if level:GetTop() > GetScreenHeight() then
+		local top = level:GetTop()
+		local point, parent, relativePoint, x, y = level:GetPoint(1)
+		level:ClearAllPoints()
+		level:SetPoint(point, parent, relativePoint, x or 0, (y or 0) + GetScreenHeight() - top)
+	elseif level:GetBottom() < 0 then
+		local bottom = level:GetBottom()
+		local point, parent, relativePoint, x, y = level:GetPoint(1)
+		level:ClearAllPoints()
+		level:SetPoint(point, parent, relativePoint, x or 0, (y or 0) - bottom)
+	end
+	CheckDualMonitor(self, level)
+	if mod(level.num, 5) == 0 then
+		local left, bottom = level:GetLeft(), level:GetBottom()
+		level:ClearAllPoints()
+		level:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT", left, bottom)
+	end
+end
+
+local Open
+local OpenSlider
+local OpenEditBox
+local Refresh
+local Clear
+local function ReleaseButton(self, level, index)
+	if not level.buttons then
+		return
+	end
+	if not level.buttons[index] then
+		return
+	end
+	local button = level.buttons[index]
+	button:Hide()
+	if button.highlight then
+		button.highlight:Hide()
+	end
+--	button.arrow:SetVertexColor(1, 1, 1)
+--	button.arrow:SetHeight(16)
+--	button.arrow:SetWidth(16)
+	table.remove(level.buttons, index)
+	table.insert(buttons, button)
+	for k in pairs(button) do
+		if k ~= 0 and k ~= "text" and k ~= "check" and k ~= "arrow" and k ~= "colorSwatch" and k ~= "highlight" and k ~= "radioHighlight" then
+			button[k] = nil
+		end
+	end
+	return true
+end
+
+local function Scroll(self, level, down)
+	if down then
+		if level:GetBottom() < 0 then
+			local point, parent, relativePoint, x, y = level:GetPoint(1)
+			level:SetPoint(point, parent, relativePoint, x, y + 50)
+			if level:GetBottom() > 0 then
+				level:SetPoint(point, parent, relativePoint, x, y + 50 - level:GetBottom())
+			end
+		end
+	else
+		if level:GetTop() > GetScreenHeight() then
+			local point, parent, relativePoint, x, y = level:GetPoint(1)
+			level:SetPoint(point, parent, relativePoint, x, y - 50)
+			if level:GetTop() < GetScreenHeight() then
+				level:SetPoint(point, parent, relativePoint, x, y - 50 + GetScreenHeight() - level:GetTop())
+			end
+		end
+	end
+end
+
+local function getArgs(t, str, num, ...)
+	local x = t[str .. num]
+	if x == nil then
+		return ...
+	else
+		return x, getArgs(t, str, num + 1, ...)
+	end
+end
+
+local sliderFrame
+local editBoxFrame
+
+local normalFont
+local lastSetFont
+local justSetFont = false
+local regionTmp = {}
+local function fillRegionTmp(...)
+	for i = 1, select('#', ...) do
+		regionTmp[i] = select(i, ...)
+	end
+end
+
+local function showGameTooltip(this)
+	if this.tooltipTitle or this.tooltipText then
+		GameTooltip_SetDefaultAnchor(GameTooltip, this)
+		local disabled = not this.isTitle and this.disabled
+		local font
+		if this.tooltipTitle then
+			if SharedMedia and SharedMedia:IsValid("font", this.tooltipTitle) then
+				font = SharedMedia:Fetch("font", this.tooltipTitle)
+			end
+			if disabled then
+				GameTooltip:SetText(this.tooltipTitle, 0.5, 0.5, 0.5, 1)
+			else
+				GameTooltip:SetText(this.tooltipTitle, 1, 1, 1, 1)
+			end
+			if this.tooltipText then
+				if not font and SharedMedia and SharedMedia:IsValid("font", this.tooltipText) then
+					font = SharedMedia:Fetch("font", this.tooltipText)
+				end
+				if disabled then
+					GameTooltip:AddLine(this.tooltipText, (NORMAL_FONT_COLOR.r + 0.5) / 2, (NORMAL_FONT_COLOR.g + 0.5) / 2, (NORMAL_FONT_COLOR.b + 0.5) / 2, 1)
+				else
+					GameTooltip:AddLine(this.tooltipText, NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b, 1)
+				end
+			end
+		else
+			if SharedMedia and SharedMedia:IsValid("font", this.tooltipText) then
+				font = SharedMedia:Fetch("font", this.tooltipText)
+			end
+			if disabled then
+				GameTooltip:SetText(this.tooltipText, 0.5, 0.5, 0.5, 1)
+			else
+				GameTooltip:SetText(this.tooltipText, 1, 1, 1, 1)
+			end
+		end
+		if font then
+			fillRegionTmp(GameTooltip:GetRegions())
+			lastSetFont = font
+			justSetFont = true
+			for i,v in ipairs(regionTmp) do
+				if v.SetFont then
+					local norm,size,outline = v:GetFont()
+					v:SetFont(font, size, outline)
+					if not normalFont then
+						normalFont = norm
+					end
+				end
+				regionTmp[i] = nil
+			end
+		elseif not normalFont then
+			fillRegionTmp(GameTooltip:GetRegions())
+			for i,v in ipairs(regionTmp) do
+				if v.GetFont and not normalFont then
+					normalFont = v:GetFont()
+				end
+				regionTmp[i] = nil
+			end
+		end
+		GameTooltip:Show()
+	end
+	if this.tooltipFunc then
+		GameTooltip:SetOwner(this, "ANCHOR_NONE")
+		GameTooltip:SetPoint("TOPLEFT", this, "TOPRIGHT", 5, 0)
+		this.tooltipFunc(getArgs(this, 'tooltipArg', 1))
+		GameTooltip:Show()
+	end
+end
+
+local tmpt = setmetatable({}, {mode='v'})
+local numButtons = 0
+local function AcquireButton(self, level)
+	if not levels[level] then
+		return
+	end
+	level = levels[level]
+	if not level.buttons then
+		level.buttons = {}
+	end
+	local button
+	if #buttons == 0 then
+		numButtons = numButtons + 1
+		button = CreateFrame("Button", "Dewdrop20Button" .. numButtons, nil)
+		button:SetFrameStrata("FULLSCREEN_DIALOG")
+		button:SetHeight(16)
+		local highlight = button:CreateTexture(nil, "BACKGROUND")
+		highlight:SetTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight")
+		button.highlight = highlight
+		highlight:SetBlendMode("ADD")
+		highlight:SetAllPoints(button)
+		highlight:Hide()
+		local check = button:CreateTexture(nil, "ARTWORK")
+		button.check = check
+		check:SetTexture("Interface\\Buttons\\UI-CheckBox-Check")
+		check:SetPoint("CENTER", button, "LEFT", 12, 0)
+		check:SetWidth(24)
+		check:SetHeight(24)
+		local radioHighlight = button:CreateTexture(nil, "ARTWORK")
+		button.radioHighlight = radioHighlight
+		radioHighlight:SetTexture("Interface\\Buttons\\UI-RadioButton")
+		radioHighlight:SetAllPoints(check)
+		radioHighlight:SetBlendMode("ADD")
+		radioHighlight:SetTexCoord(0.5, 0.75, 0, 1)
+		radioHighlight:Hide()
+		button:SetScript("OnEnter", function()
+			if (sliderFrame and sliderFrame:IsShown() and sliderFrame.mouseDown and sliderFrame.level == this.level.num + 1) or (editBoxFrame and editBoxFrame:IsShown() and editBoxFrame.mouseDown and editBoxFrame.level == this.level.num + 1) then
+				for i = 1, this.level.num do
+					Refresh(self, levels[i])
+				end
+				return
+			end
+			self:Close(this.level.num + 1)
+			if not this.disabled then
+				if this.secure then
+					secureFrame:Activate(this)
+				elseif this.hasSlider then
+					OpenSlider(self, this)
+				elseif this.hasEditBox then
+					OpenEditBox(self, this)
+				elseif this.hasArrow then
+					Open(self, this, nil, this.level.num + 1, this.value)
+				end
+			end
+			if not this.level then -- button reclaimed
+				return
+			end
+			StopCounting(self, this.level.num + 1)
+			if not this.disabled then
+				highlight:Show()
+				if this.isRadio then
+					button.radioHighlight:Show()
+				end
+				if this.mouseoverUnderline then
+					underlineFrame:SetParent(this)
+					underlineFrame:SetPoint("BOTTOMLEFT",this.text,0,0)
+					underlineFrame:SetWidth(this.text:GetWidth())
+					underlineFrame:Show()
+				end
+			end
+			showGameTooltip(this)
+		end)
+		button:SetScript("OnHide", function()
+			if this.secure and secureFrame:IsOwnedBy(this) then
+				secureFrame:Deactivate()
+			end
+		end)
+		button:SetScript("OnLeave", function()
+			if this.secure and secureFrame:IsShown() then
+				return;	-- it's ok, we didn't actually mouse out of the button, only onto the secure frame on top of it
+			end
+			underlineFrame:Hide()
+			if not this.selected then
+				highlight:Hide()
+			end
+			button.radioHighlight:Hide()
+			if this.level then
+				StartCounting(self, this.level.num)
+			end
+			GameTooltip:Hide()
+		end)
+		local first = true
+		button:SetScript("OnClick", function()
+			if not this.disabled then
+				if this.hasColorSwatch then
+					local func = button.colorFunc
+					local hasOpacity = this.hasOpacity
+					local this = this
+					for k in pairs(tmpt) do
+						tmpt[k] = nil
+					end
+					for i = 1, 1000 do
+						local x = this['colorArg'..i]
+						if x == nil then
+							break
+						else
+							tmpt[i] = x
+						end
+					end
+					ColorPickerFrame.func = function()
+						if func then
+							local r,g,b = ColorPickerFrame:GetColorRGB()
+							local a = hasOpacity and 1 - OpacitySliderFrame:GetValue() or nil
+							local n = #tmpt
+							tmpt[n+1] = r
+							tmpt[n+2] = g
+							tmpt[n+3] = b
+							tmpt[n+4] = a
+							func(unpack(tmpt))
+							tmpt[n+1] = nil
+							tmpt[n+2] = nil
+							tmpt[n+3] = nil
+							tmpt[n+4] = nil
+						end
+					end
+					ColorPickerFrame.hasOpacity = this.hasOpacity
+					ColorPickerFrame.opacityFunc = ColorPickerFrame.func
+					ColorPickerFrame.opacity = 1 - this.opacity
+					ColorPickerFrame:SetColorRGB(this.r, this.g, this.b)
+					local r, g, b, a = this.r, this.g, this.b, this.opacity
+					ColorPickerFrame.cancelFunc = function()
+						if func then
+							local n = #tmpt
+							tmpt[n+1] = r
+							tmpt[n+2] = g
+							tmpt[n+3] = b
+							tmpt[n+4] = a
+							func(unpack(tmpt))
+							for i = 1, n+4 do
+								tmpt[i] = nil
+							end
+						end
+					end
+					self:Close(1)
+					ShowUIPanel(ColorPickerFrame)
+				elseif this.func then
+					local level = this.level
+					if type(this.func) == "string" then
+						if type(this.arg1[this.func]) ~= "function" then
+							self:error("Cannot call method %q", this.func)
+						end
+						this.arg1[this.func](this.arg1, getArgs(this, 'arg', 2))
+					else
+						this.func(getArgs(this, 'arg', 1))
+					end
+					if this.closeWhenClicked then
+						self:Close()
+					elseif level:IsShown() then
+						for i = 1, level.num do
+							Refresh(self, levels[i])
+						end
+						local value = levels[level.num].value
+						for i = level.num-1, 1, -1 do
+							local level = levels[i]
+							local good = false
+							for _,button in ipairs(level.buttons) do
+								if button.value == value then
+									good = true
+									break
+								end
+							end
+							if not good then
+								Dewdrop:Close(i+1)
+							end
+							value = levels[i].value
+						end
+					end
+				elseif this.closeWhenClicked then
+					self:Close()
+				end
+			end
+		end)
+		local text = button:CreateFontString(nil, "ARTWORK")
+		button.text = text
+		text:SetFontObject(GameFontHighlightSmall)
+		button.text:SetFont(STANDARD_TEXT_FONT, UIDROPDOWNMENU_DEFAULT_TEXT_HEIGHT)
+		button:SetScript("OnMouseDown", function()
+			if not this.disabled and (this.func or this.colorFunc or this.closeWhenClicked) then
+				text:SetPoint("LEFT", button, "LEFT", this.notCheckable and 1 or 25, -1)
+			end
+		end)
+		button:SetScript("OnMouseUp", function()
+			if not this.disabled and (this.func or this.colorFunc or this.closeWhenClicked) then
+				text:SetPoint("LEFT", button, "LEFT", this.notCheckable and 0 or 24, 0)
+			end
+		end)
+		local arrow = button:CreateTexture(nil, "ARTWORK")
+		button.arrow = arrow
+		arrow:SetPoint("LEFT", button, "RIGHT", -16, 0)
+		arrow:SetWidth(16)
+		arrow:SetHeight(16)
+		arrow:SetTexture("Interface\\ChatFrame\\ChatFrameExpandArrow")
+		local colorSwatch = button:CreateTexture(nil, "ARTWORK")
+		button.colorSwatch = colorSwatch
+		colorSwatch:SetWidth(20)
+		colorSwatch:SetHeight(20)
+		colorSwatch:SetTexture("Interface\\ChatFrame\\ChatFrameColorSwatch")
+		local texture = button:CreateTexture(nil, "OVERLAY")
+		colorSwatch.texture = texture
+		texture:SetTexture("Interface\\Buttons\\WHITE8X8")
+		texture:SetWidth(11.5)
+		texture:SetHeight(11.5)
+		texture:Show()
+		texture:SetPoint("CENTER", colorSwatch, "CENTER")
+		colorSwatch:SetPoint("RIGHT", button, "RIGHT", 0, 0)
+	else
+		button = table.remove(buttons)
+	end
+	button:ClearAllPoints()
+	button:SetParent(level)
+	button:SetFrameStrata(level:GetFrameStrata())
+	button:SetFrameLevel(level:GetFrameLevel() + 1)
+	button:SetPoint("LEFT", level, "LEFT", 10, 0)
+	button:SetPoint("RIGHT", level, "RIGHT", -10, 0)
+	if #level.buttons == 0 then
+		button:SetPoint("TOP", level, "TOP", 0, -10)
+	else
+		button:SetPoint("TOP", level.buttons[#level.buttons], "BOTTOM", 0, 0)
+	end
+	button.text:SetPoint("LEFT", button, "LEFT", 24, 0)
+	button:Show()
+	button.level = level
+	table.insert(level.buttons, button)
+	if not level.parented then
+		level.parented = true
+		level:ClearAllPoints()
+		if level.num == 1 then
+			if level.parent ~= UIParent and type(level.parent) == "table" then
+				level:SetPoint("TOPRIGHT", level.parent, "TOPLEFT")
+			else
+				level:SetPoint("CENTER", UIParent, "CENTER")
+			end
+		else
+			if level.lastDirection == "RIGHT" then
+				if level.lastVDirection == "DOWN" then
+					level:SetPoint("TOPLEFT", level.parent, "TOPRIGHT", 5, 10)
+				else
+					level:SetPoint("BOTTOMLEFT", level.parent, "BOTTOMRIGHT", 5, -10)
+				end
+			else
+				if level.lastVDirection == "DOWN" then
+					level:SetPoint("TOPRIGHT", level.parent, "TOPLEFT", -5, 10)
+				else
+					level:SetPoint("BOTTOMRIGHT", level.parent, "BOTTOMLEFT", -5, -10)
+				end
+			end
+		end
+		level:SetFrameStrata("FULLSCREEN_DIALOG")
+	end
+	button:SetAlpha(1)
+	return button
+end
+
+local numLevels = 0
+local function AcquireLevel(self, level)
+	if not levels[level] then
+		for i = #levels + 1, level, -1 do
+			local i = i
+			numLevels = numLevels + 1
+			local frame = CreateFrame("Button", "Dewdrop20Level" .. numLevels, nil)
+			if i == 1 then
+				local old_CloseSpecialWindows = CloseSpecialWindows
+				function CloseSpecialWindows()
+					local found = old_CloseSpecialWindows()
+					if levels[1]:IsShown() then
+						self:Close()
+						return 1
+					end
+					return found
+				end
+			end
+			levels[i] = frame
+			frame.num = i
+			frame:SetParent(UIParent)
+			frame:SetFrameStrata("FULLSCREEN_DIALOG")
+			frame:Hide()
+			frame:SetWidth(180)
+			frame:SetHeight(10)
+			frame:SetFrameLevel(i * 3)
+			frame:SetScript("OnHide", function()
+				self:Close(level + 1)
+			end)
+			if frame.SetTopLevel then
+				frame:SetTopLevel(true)
+			end
+			frame:EnableMouse(true)
+			frame:EnableMouseWheel(true)
+			local backdrop = CreateFrame("Frame", nil, frame)
+			backdrop:SetAllPoints(frame)
+			backdrop:SetBackdrop(tmp(
+				'bgFile', "Interface\\Tooltips\\UI-Tooltip-Background",
+				'edgeFile', "Interface\\Tooltips\\UI-Tooltip-Border",
+				'tile', true,
+				'insets', tmp2(
+					'left', 5,
+					'right', 5,
+					'top', 5,
+					'bottom', 5
+				),
+				'tileSize', 16,
+				'edgeSize', 16
+			))
+			backdrop:SetBackdropBorderColor(TOOLTIP_DEFAULT_COLOR.r, TOOLTIP_DEFAULT_COLOR.g, TOOLTIP_DEFAULT_COLOR.b)
+			backdrop:SetBackdropColor(TOOLTIP_DEFAULT_BACKGROUND_COLOR.r, TOOLTIP_DEFAULT_BACKGROUND_COLOR.g, TOOLTIP_DEFAULT_BACKGROUND_COLOR.b)
+			frame:SetScript("OnClick", function()
+				self:Close(i)
+			end)
+			frame:SetScript("OnEnter", function()
+				StopCounting(self, i)
+			end)
+			frame:SetScript("OnLeave", function()
+				StartCounting(self, i)
+			end)
+			frame:SetScript("OnMouseWheel", function()
+				Scroll(self, frame, arg1 < 0)
+			end)
+			if i == 1 then
+				frame:SetScript("OnUpdate", function(this, arg1)
+					OnUpdate(self, arg1)
+				end)
+				levels[1].lastDirection = "RIGHT"
+				levels[1].lastVDirection = "DOWN"
+			else
+				levels[i].lastDirection = levels[i - 1].lastDirection
+				levels[i].lastVDirection = levels[i - 1].lastVDirection
+			end
+		end
+	end
+	local fullscreenFrame = GetUIPanel("fullscreen")
+	local l = levels[level]
+	local strata, framelevel = l:GetFrameStrata(), l:GetFrameLevel()
+	if fullscreenFrame then
+		l:SetParent(fullscreenFrame)
+	else
+		l:SetParent(UIParent)
+	end
+	l:SetFrameStrata(strata)
+	l:SetFrameLevel(framelevel)
+	l:SetAlpha(1)
+	return l
+end
+
+local function validateOptions(options, position, baseOptions, fromPass)
+	if not baseOptions then
+		baseOptions = options
+	end
+	if type(options) ~= "table" then
+		return "Options must be a table.", position
+	end
+	local kind = options.type
+	if type(kind) ~= "string" then
+		return '"type" must be a string.', position
+	elseif kind ~= "group" and kind ~= "range" and kind ~= "text" and kind ~= "execute" and kind ~= "toggle" and kind ~= "color" and kind ~= "dragLink" and kind ~= "header" then
+		return '"type" must either be "range", "text", "group", "toggle", "execute", "color", "dragLink", or "header".', position
+	end
+	if options.aliases then
+		if type(options.aliases) ~= "table" and type(options.aliases) ~= "string" then
+			return '"alias" must be a table or string', position
+		end
+	end
+	if not fromPass then
+		if kind == "execute" then
+			if type(options.func) ~= "string" and type(options.func) ~= "function" then
+				return '"func" must be a string or function', position
+			end
+		elseif kind == "range" or kind == "text" or kind == "toggle" then
+			if type(options.set) ~= "string" and type(options.set) ~= "function" then
+				return '"set" must be a string or function', position
+			end
+			if kind == "text" and options.get == false then
+			elseif type(options.get) ~= "string" and type(options.get) ~= "function" then
+				return '"get" must be a string or function', position
+			end
+		elseif kind == "group" and options.pass then
+			if options.pass ~= true then
+				return '"pass" must be either nil, true, or false', position
+			end
+			if not options.func then
+				if type(options.set) ~= "string" and type(options.set) ~= "function" then
+					return '"set" must be a string or function', position
+				end
+				if type(options.get) ~= "string" and type(options.get) ~= "function" then
+					return '"get" must be a string or function', position
+				end
+			elseif type(options.func) ~= "string" and type(options.func) ~= "function" then
+				return '"func" must be a string or function', position
+			end
+		end
+	end
+	if options ~= baseOptions then
+		if kind == "header" then
+		elseif type(options.desc) ~= "string" then
+			return '"desc" must be a string', position
+		elseif options.desc:len() == 0 then
+			return '"desc" cannot be a 0-length string', position
+		end
+	end
+	if options ~= baseOptions or kind == "range" or kind == "text" or kind == "toggle" or kind == "color" then
+		if options.type == "header" and not options.cmdName and not options.name then
+		elseif options.cmdName then
+			if type(options.cmdName) ~= "string" then
+				return '"cmdName" must be a string or nil', position
+			elseif options.cmdName:len() == 0 then
+				return '"cmdName" cannot be a 0-length string', position
+			end
+			if type(options.guiName) ~= "string" then
+				if not options.guiNameIsMap then
+					return '"guiName" must be a string or nil', position
+				end
+			elseif options.guiName:len() == 0 then
+				return '"guiName" cannot be a 0-length string', position
+			end
+		else
+			if type(options.name) ~= "string" then
+				return '"name" must be a string', position
+			elseif options.name:len() == 0 then
+				return '"name" cannot be a 0-length string', position
+			end
+		end
+	end
+	if options.guiNameIsMap then
+		if type(options.guiNameIsMap) ~= "boolean" then
+			return '"guiNameIsMap" must be a boolean or nil', position
+		elseif options.type ~= "toggle" then
+			return 'if "guiNameIsMap" is true, then "type" must be set to \'toggle\'', position
+		elseif type(options.map) ~= "table" then
+			return '"map" must be a table', position
+		end
+	end
+	if options.message and type(options.message) ~= "string" then
+		return '"message" must be a string or nil', position
+	end
+	if options.error and type(options.error) ~= "string" then
+		return '"error" must be a string or nil', position
+	end
+	if options.current and type(options.current) ~= "string" then
+		return '"current" must be a string or nil', position
+	end
+	if options.order then
+		if type(options.order) ~= "number" or (-1 < options.order and options.order < 0.999) then
+			return '"order" must be a non-zero number or nil', position
+		end
+	end
+	if options.disabled then
+		if type(options.disabled) ~= "function" and type(options.disabled) ~= "string" and options.disabled ~= true then
+			return '"disabled" must be a function, string, or boolean', position
+		end
+	end
+	if options.cmdHidden then
+		if type(options.cmdHidden) ~= "function" and type(options.cmdHidden) ~= "string" and options.cmdHidden ~= true then
+			return '"cmdHidden" must be a function, string, or boolean', position
+		end
+	end
+	if options.guiHidden then
+		if type(options.guiHidden) ~= "function" and type(options.guiHidden) ~= "string" and options.guiHidden ~= true then
+			return '"guiHidden" must be a function, string, or boolean', position
+		end
+	end
+	if options.hidden then
+		if type(options.hidden) ~= "function" and type(options.hidden) ~= "string" and options.hidden ~= true then
+			return '"hidden" must be a function, string, or boolean', position
+		end
+	end
+	if kind == "text" then
+		if type(options.validate) == "table" then
+			local t = options.validate
+			local iTable = nil
+			for k,v in pairs(t) do
+				if type(k) == "number" then
+					if iTable == nil then
+						iTable = true
+					elseif not iTable then
+						return '"validate" must either have all keys be indexed numbers or strings', position
+					elseif k < 1 or k > #t then
+						return '"validate" numeric keys must be indexed properly. >= 1 and <= #t', position
+					end
+				else
+					if iTable == nil then
+						iTable = false
+					elseif iTable then
+						return '"validate" must either have all keys be indexed numbers or strings', position
+					end
+				end
+				if type(v) ~= "string" then
+					return '"validate" values must all be strings', position
+				end
+			end
+			if options.multiToggle and options.multiToggle ~= true then
+				return '"multiToggle" must be a boolean or nil if "validate" is a table', position
+			end
+		elseif options.validate == "keybinding" then
+			-- no other checks
+		else
+			if type(options.usage) ~= "string" then
+				return '"usage" must be a string', position
+			elseif options.validate and type(options.validate) ~= "string" and type(options.validate) ~= "function" then
+				return '"validate" must be a string, function, or table', position
+			end
+		end
+		if options.multiToggle and type(options.validate) ~= "table" then
+			return '"validate" must be a table if "multiToggle" is true', position
+		end
+	elseif kind == "range" then
+		if options.min or options.max then
+			if type(options.min) ~= "number" then
+				return '"min" must be a number', position
+			elseif type(options.max) ~= "number" then
+				return '"max" must be a number', position
+			elseif options.min >= options.max then
+				return '"min" must be less than "max"', position
+			end
+		end
+		if options.step then
+			if type(options.step) ~= "number" then
+				return '"step" must be a number', position
+			elseif options.step < 0 then
+				return '"step" must be nonnegative', position
+			end
+		end
+		if options.bigStep then
+			if type(options.bigStep) ~= "number" then
+				return '"bigStep" must be a number', position
+			elseif options.bigStep < 0 then
+				return '"bigStep" must be nonnegative', position
+			end
+		end
+		if options.isPercent and options.isPercent ~= true then
+			return '"isPercent" must either be nil, true, or false', position
+		end
+	elseif kind == "toggle" then
+		if options.map then
+			if type(options.map) ~= "table" then
+				return '"map" must be a table', position
+			elseif type(options.map[true]) ~= "string" then
+				return '"map[true]" must be a string', position
+			elseif type(options.map[false]) ~= "string" then
+				return '"map[false]" must be a string', position
+			end
+		end
+	elseif kind == "color" then
+		if options.hasAlpha and options.hasAlpha ~= true then
+			return '"hasAlpha" must be nil, true, or false', position
+		end
+	elseif kind == "group" then
+		if options.pass and options.pass ~= true then
+			return '"pass" must be nil, true, or false', position
+		end
+		if type(options.args) ~= "table" then
+			return '"args" must be a table', position
+		end
+		for k,v in pairs(options.args) do
+			if type(k) ~= "number" then
+				if type(k) ~= "string" then
+					return '"args" keys must be strings or numbers', position
+				elseif k:len() == 0 then
+					return '"args" keys must not be 0-length strings.', position
+				end
+			end
+			if type(v) ~= "table" then
+				return '"args" values must be tables', position and position .. "." .. k or k
+			end
+			local newposition
+			if position then
+				newposition = position .. ".args." .. k
+			else
+				newposition = "args." .. k
+			end
+			local err, pos = validateOptions(v, newposition, baseOptions, options.pass)
+			if err then
+				return err, pos
+			end
+		end
+	elseif kind == "execute" then
+		if type(options.confirm) ~= "string" and type(options.confirm) ~= "boolean" and type(options.confirm) ~= "nil" then
+			return '"confirm" must be a string, boolean, or nil', position
+		end
+	end
+	if options.icon and type(options.icon) ~= "string" then
+		return'"icon" must be a string', position
+	end
+	if options.iconWidth or options.iconHeight then
+		if type(options.iconWidth) ~= "number" or type(options.iconHeight) ~= "number" then
+			return '"iconHeight" and "iconWidth" must be numbers', position
+		end
+	end
+	if options.iconCoordLeft or options.iconCoordRight or options.iconCoordTop or options.iconCoordBottom then
+		if type(options.iconCoordLeft) ~= "number" or type(options.iconCoordRight) ~= "number" or type(options.iconCoordTop) ~= "number" or type(options.iconCoordBottom) ~= "number" then
+			return '"iconCoordLeft", "iconCoordRight", "iconCoordTop", and "iconCoordBottom" must be numbers', position
+		end
+	end
+end
+
+local validatedOptions
+
+local values
+local mysort_args
+local mysort
+local othersort
+local othersort_validate
+
+local baseFunc, currentLevel
+
+local function confirmPopup(message, func, ...)
+	if not StaticPopupDialogs["DEWDROP20_CONFIRM_DIALOG"] then
+		StaticPopupDialogs["DEWDROP20_CONFIRM_DIALOG"] = {}
+	end
+	local t = StaticPopupDialogs["DEWDROP20_CONFIRM_DIALOG"]
+	for k in pairs(t) do
+		t[k] = nil
+	end
+	t.text = message
+	t.button1 = ACCEPT or "Accept"
+	t.button2 = CANCEL or "Cancel"
+	t.OnAccept = function()
+		func(unpack(t))
+	end
+	for i = 1, select('#', ...) do
+		t[i] = select(i, ...)
+	end
+	t.timeout = 0
+	t.whileDead = 1
+	t.hideOnEscape = 1
+
+	Dewdrop:Close()
+	StaticPopup_Show("DEWDROP20_CONFIRM_DIALOG")
+end
+
+
+local function getMethod(settingname, handler, v, methodName, ...)	-- "..." is simply returned straight out cause you can't do "a,b,c = 111,f(),222"
+	assert(v and type(v)=="table")
+	assert(methodName and type(methodName)=="string")
+
+	local method = v[methodName]
+	if type(method)=="function" then
+		return method, ...
+	elseif type(method)=="string" then
+		if not handler then
+			Dewdrop:error("[%s] 'handler' is required if providing a method name: %q", tostring(settingname), method)
+		elseif not handler[method] then
+			Dewdrop:error("[%s] 'handler' method %q not defined", tostring(settingname), method)
+		end
+		return handler[method], handler, ...
+	end
+
+	Dewdrop:error("[%s] Missing %q directive", tostring(settingname), methodName)
+end
+
+local function callMethod(settingname, handler, v, methodName, ...)
+	assert(v and type(v)=="table")
+	assert(methodName and type(methodName)=="string")
+
+	local method = v[methodName]
+	if type(method)=="function" then
+		local success, ret,ret2,ret3,ret4 = pcall(v[methodName], ...)
+		if not success then
+			geterrorhandler()(ret)
+			return nil
+		end
+		return ret,ret2,ret3,ret4
+
+	elseif type(method)=="string" then
+
+		local neg = method:match("^~(.-)$")
+		if neg then
+			method = neg
+		end
+		if not handler then
+			Dewdrop:error("[%s] 'handler' is required if providing a method name: %q", tostring(settingname), method)
+		elseif not handler[method] then
+			Dewdrop:error("[%s] 'handler' (%q) method %q not defined", tostring(settingname), handler.name or "(unnamed)", method)
+		end
+		local success, ret,ret2,ret3,ret4 = pcall(handler[method], handler, ...)
+		if not success then
+			geterrorhandler()(ret)
+			return nil
+		end
+		if neg then
+			return not ret
+		end
+		return ret,ret2,ret3,ret4
+	elseif method == false then
+		return nil
+	end
+
+	Dewdrop:error("[%s] Missing %q directive in %q", tostring(settingname), methodName, v.name or "(unnamed)")
+end
+
+local function skip1Nil(...)
+	if select(1,...)==nil then
+		return select(2,...)
+	end
+	return ...
+end
+
+function Dewdrop:FeedAceOptionsTable(options, difference)
+	self:argCheck(options, 2, "table")
+	self:argCheck(difference, 3, "nil", "number")
+	if not currentLevel then
+		self:error("Cannot call `FeedAceOptionsTable' outside of a Dewdrop declaration")
+	end
+	if not difference then
+		difference = 0
+	end
+	if not validatedOptions then
+		validatedOptions = {}
+	end
+	if not validatedOptions[options] then
+		local err, position = validateOptions(options)
+
+		if err then
+			if position then
+				Dewdrop:error(position .. ": " .. err)
+			else
+				Dewdrop:error(err)
+			end
+		end
+
+		validatedOptions[options] = true
+	end
+	local level = levels[currentLevel]
+	if not level then
+		self:error("Improper level given")
+	end
+	if not values then
+		values = {}
+	else
+		for k,v in pairs(values) do
+			values[k] = nil
+		end
+	end
+
+	local current = level
+	while current do		-- this traverses from higher level numbers to lower, building "values" with leaf nodes first and trunk nodes later
+		if current.num == difference + 1 then
+			break
+		end
+		table.insert(values, current.value)
+		current = levels[current.num - 1]
+	end
+
+	local realOptions = options
+	local handler = options.handler
+	local passTable
+	local passValue
+	while #values > 0 do	-- This loop traverses values from the END (trunk nodes first, then onto leaf nodes)
+		if options.pass then
+			if options.get and options.set then
+				passTable = options
+			elseif not passTable then
+				passTable = options
+			end
+		else
+			passTable = nil
+		end
+		local value = table.remove(values)
+		options = options.args and options.args[value]
+		if not options then
+			return
+		end
+		handler = options.handler or handler
+		passValue = passTable and value or nil
+	end
+
+	if options.type == "group" then
+		local hidden = options.hidden
+		if type(hidden) == "function" or type(hidden) == "string" then
+			hidden = callMethod(options.name or "(options root)", handler, options, "hidden", options.passValue) or false
+		end
+		if hidden then
+			return
+		end
+		local disabled = options.disabled
+		if type(disabled) == "function" or type(disabled) == "string" then
+			disabled = callMethod(options.name or "(options root)", handler, options, "disabled", options.passValue) or false
+		end
+		if disabled then
+			self:AddLine(
+				'text', DISABLED,
+				'disabled', true
+			)
+			return
+		end
+		for k in pairs(options.args) do
+			table.insert(values, k)
+		end
+		if options.pass then
+			if options.get and options.set then
+				passTable = options
+			elseif not passTable then
+				passTable = options
+			end
+		else
+			passTable = nil
+		end
+		if not mysort then
+			mysort = function(a, b)
+				local alpha, bravo = mysort_args[a], mysort_args[b]
+				local alpha_order = alpha.order or 100
+				local bravo_order = bravo.order or 100
+				local alpha_name = alpha.guiName or alpha.name
+				local bravo_name = bravo.guiName or bravo.name
+				if alpha_order == bravo_order then
+					if not alpha_name then
+						return bravo_name
+					elseif not bravo_name then
+						return false
+					else
+						return alpha_name:gsub("|c%x%x%x%x%x%x%x%x", ""):gsub("|r", ""):upper() < bravo_name:gsub("|c%x%x%x%x%x%x%x%x", ""):gsub("|r", ""):upper()
+					end
+				else
+					if alpha_order < 0 then
+						if bravo_order > 0 then
+							return false
+						end
+					else
+						if bravo_order < 0 then
+							return true
+						end
+					end
+					return alpha_order < bravo_order
+				end
+			end
+		end
+		mysort_args = options.args
+		table.sort(values, mysort)
+		mysort_args = nil
+		local hasBoth = #values >= 1 and (options.args[values[1]].order or 100) > 0 and (options.args[values[#values]].order or 100) < 0
+		local last_order = 1
+		for _,k in ipairs(values) do
+			local v = options.args[k]
+			local handler = v.handler or handler
+			if hasBoth and last_order > 0 and (v.order or 100) < 0 then
+				hasBoth = false
+				self:AddLine()
+			end
+			local hidden, disabled = v.guiHidden or v.hidden, v.disabled
+
+			if type(hidden) == "function" or type(hidden) == "string" then
+				hidden = callMethod(k, handler, v, "hidden", v.passValue) or false
+			end
+			if not hidden then
+				if type(disabled) == "function" or type(disabled) == "string" then
+					disabled = callMethod(k, handler, v, "disabled", v.passValue) or false
+				end
+				local name = (v.guiIconOnly and v.icon) and "" or (v.guiName or v.name)
+				local desc = v.guiDesc or v.desc
+				local iconHeight = v.iconHeight or 16
+				local iconWidth = v.iconWidth or 16
+				local iconCoordLeft = v.iconCoordLeft
+				local iconCoordRight = v.iconCoordRight
+				local iconCoordBottom = v.iconCoordBottom
+				local iconCoordTop = v.iconCoordTop
+				local tooltipTitle, tooltipText
+				tooltipTitle = name
+				if name ~= desc then
+					tooltipText = desc
+				end
+				if type(v.usage) == "string" and v.usage:trim():len() > 0 then
+					if tooltipText then
+						tooltipText = tooltipText .. "\n\n" .. USAGE_TOOLTIP:format(v.usage)
+					else
+						tooltipText = USAGE_TOOLTIP:format(v.usage)
+					end
+				end
+				local v_p = passTable
+				if not v_p or (v.type ~= "execute" and v.get and v.set) or (v.type == "execute" and v.func) then
+					v_p = v
+				end
+				local passValue = v.passValue or (v_p~=v and k) or nil
+				if v.type == "toggle" then
+					local checked = callMethod(name, handler, v_p, "get", passValue) or false
+					local checked_arg = checked
+					if type(v_p.get)=="string" and v_p.get:match("^~") then
+						checked_arg = not checked
+					end
+					local func, arg1, arg2, arg3 = getMethod(name, handler, v_p, "set",   skip1Nil(passValue, not checked_arg))
+					if v.guiNameIsMap then
+						checked = checked and true or false
+						name = tostring(v.map and v.map[checked]):gsub("|c%x%x%x%x%x%x%x%x(.-)|r", "%1")
+						tooltipTitle = name
+						checked = true--nil
+					end
+					self:AddLine(
+						'text', name,
+						'checked', checked,
+						'isRadio', v.isRadio,
+						'func', func,
+						'arg1', arg1,
+						'arg2', arg2,
+						'arg3', arg3,
+						'disabled', disabled,
+						'tooltipTitle', tooltipTitle,
+						'tooltipText', tooltipText
+					)
+				elseif v.type == "execute" then
+					local func, arg1, arg2, arg3, arg4
+					local confirm = v.confirm
+					if confirm == true then
+						confirm = DEFAULT_CONFIRM_MESSAGE:format(tooltipText or tooltipTitle)
+						func,arg1,arg2,arg3,arg4 = confirmPopup, confirm, getMethod(name, handler, v_p, "func", passValue)
+					elseif type(confirm) == "string" then
+						func,arg1,arg2,arg3,arg4 = confirmPopup, confirm, getMethod(name, handler, v_p, "func", passValue)
+					else
+						func,arg1,arg2 = getMethod(name, handler, v_p, "func", passValue)
+					end
+					self:AddLine(
+						'text', name,
+						'checked', checked,
+						'func', func,
+						'arg1', arg1,
+						'arg2', arg2,
+						'arg3', arg3,
+						'arg4', arg4,
+						'disabled', disabled,
+						'tooltipTitle', tooltipTitle,
+						'tooltipText', tooltipText,
+						'icon', v.icon,
+						'iconHeight', iconHeight,
+						'iconWidth', iconWidth,
+						'iconCoordLeft', iconCoordLeft,
+						'iconCoordRight', iconCoordRight,
+						'iconCoordTop', iconCoordTop,
+						'iconCoordBottom', iconCoordBottom
+					)
+				elseif v.type == "range" then
+					local sliderValue
+					sliderValue = callMethod(name, handler, v_p, "get", passValue) or 0
+					local sliderFunc, sliderArg1, sliderArg2 = getMethod(name, handler, v_p, "set",    passValue)
+					if tooltipText then
+						tooltipText = format("%s\n\n%s", tooltipText, RANGE_TOOLTIP)
+					else
+						tooltipText = RANGE_TOOLTIP
+					end
+					self:AddLine(
+						'text', name,
+						'hasArrow', true,
+						'hasSlider', true,
+						'sliderMin', v.min or 0,
+						'sliderMax', v.max or 1,
+						'sliderStep', v.step or 0,
+						'sliderBigStep', v.bigStep or nil,
+						'sliderIsPercent', v.isPercent or false,
+						'sliderValue', sliderValue,
+						'sliderFunc', sliderFunc,
+						'sliderArg1', sliderArg1,
+						'sliderArg2', sliderArg2,
+						'fromAceOptions', true,
+						'disabled', disabled,
+						'tooltipTitle', tooltipTitle,
+						'tooltipText', tooltipText,
+						'icon', v.icon,
+						'iconHeight', iconHeight,
+						'iconWidth', iconWidth,
+						'iconCoordLeft', iconCoordLeft,
+						'iconCoordRight', iconCoordRight,
+						'iconCoordTop', iconCoordTop,
+						'iconCoordBottom', iconCoordBottom
+					)
+				elseif v.type == "color" then
+					local r,g,b,a = callMethod(name, handler, v_p, "get", passValue)
+					if not r then
+						r,g,b,a = 0,0,0,0
+					end
+					local colorFunc, colorArg1, colorArg2 = getMethod(name, handler, v_p, "set",    passValue)
+					self:AddLine(
+						'text', name,
+						'hasArrow', true,
+						'hasColorSwatch', true,
+						'r', r,
+						'g', g,
+						'b', b,
+						'opacity', v.hasAlpha and a or nil,
+						'hasOpacity', v.hasAlpha,
+						'colorFunc', colorFunc,
+						'colorArg1', colorArg1,
+						'colorArg2', colorArg2,
+						'disabled', disabled,
+						'tooltipTitle', tooltipTitle,
+						'tooltipText', tooltipText
+					)
+				elseif v.type == "text" then
+						if type(v.validate) == "table" then
+						local func,arg1,arg2
+						if v.onClick then
+							func,arg1,arg2 = getMethod(name, handler, v, "onClick",    passValue)
+						end
+						local checked
+						if v.isChecked then
+							checked = callMethod(name, handler, v, "isChecked",    passValue) or false
+						end
+						self:AddLine(
+							'text', name,
+							'hasArrow', true,
+							'value', k,
+							'func', func,
+							'arg1', arg1,
+							'arg2', arg2,
+							'mouseoverUnderline', func and true or nil,
+							'disabled', disabled,
+							'checked', checked,
+							'tooltipTitle', tooltipTitle,
+							'tooltipText', tooltipText,
+							'icon', v.icon,
+							'iconHeight', iconHeight,
+							'iconWidth', iconWidth,
+							'iconCoordLeft', iconCoordLeft,
+							'iconCoordRight', iconCoordRight,
+							'iconCoordTop', iconCoordTop,
+							'iconCoordBottom', iconCoordBottom
+						)
+					else
+						local editBoxText
+						editBoxText = callMethod(name, handler, v_p, "get", passValue) or ""
+						local editBoxFunc, editBoxArg1, editBoxArg2 = getMethod(name, handler, v_p, "set",    passValue)
+
+						local editBoxValidateFunc, editBoxValidateArg1
+
+						if v.validate and v.validate ~= "keybinding" then
+							if v.validate == "keybinding" then
+								if tooltipText then
+									tooltipText = format("%s\n\n%s", tooltipText, RESET_KEYBINDING_DESC)
+								else
+									tooltipText = RESET_KEYBINDING_DESC
+								end
+							else
+								editBoxValidateFunc, editBoxValidateArg1 = getMethod(name, handler, v, "validate") -- no passvalue!
+							end
+						end
+
+						self:AddLine(
+							'text', name,
+							'hasArrow', true,
+							'icon', v.icon,
+							'iconHeight', iconHeight,
+							'iconWidth', iconWidth,
+							'iconCoordLeft', iconCoordLeft,
+							'iconCoordRight', iconCoordRight,
+							'iconCoordTop', iconCoordTop,
+							'iconCoordBottom', iconCoordBottom,
+							'hasEditBox', true,
+							'editBoxText', editBoxText,
+							'editBoxFunc', editBoxFunc,
+							'editBoxArg1', editBoxArg1,
+							'editBoxArg2', editBoxArg2,
+							'editBoxValidateFunc', editBoxValidateFunc,
+							'editBoxValidateArg1', editBoxValidateArg1,
+							'editBoxIsKeybinding', v.validate == "keybinding",
+							'editBoxKeybindingOnly', v.keybindingOnly,
+							'editBoxKeybindingExcept', v.keybindingExcept,
+							'disabled', disabled,
+							'tooltipTitle', tooltipTitle,
+							'tooltipText', tooltipText
+						)
+					end
+				elseif v.type == "group" then
+					local func,arg1,arg2
+					if v.onClick then
+						func,arg1,arg2 = getMethod(name, handler, v, "onClick",    passValue)
+					end
+					local checked
+					if v.isChecked then
+						checked = callMethod(name, handler, v, "isChecked",    passValue) or false
+					end
+					self:AddLine(
+						'text', name,
+						'hasArrow', true,
+						'value', k,
+						'func', func,
+						'arg1', arg1,
+						'arg2', arg2,
+						'mouseoverUnderline', func and true or nil,
+						'disabled', disabled,
+						'checked', checked,
+						'tooltipTitle', tooltipTitle,
+						'tooltipText', tooltipText,
+						'icon', v.icon,
+						'iconHeight', iconHeight,
+						'iconWidth', iconWidth,
+						'iconCoordLeft', iconCoordLeft,
+						'iconCoordRight', iconCoordRight,
+						'iconCoordTop', iconCoordTop,
+						'iconCoordBottom', iconCoordBottom
+					)
+				elseif v.type == "header" then
+					if name == "" or not name then
+						self:AddLine(
+							'isTitle', true,
+							'icon', v.icon,
+							'iconHeight', iconHeight,
+							'iconWidth', iconWidth,
+							'iconCoordLeft', iconCoordLeft,
+							'iconCoordRight', iconCoordRight,
+							'iconCoordTop', iconCoordTop,
+							'iconCoordBottom', iconCoordBottom
+						)
+					else
+						self:AddLine(
+							'text', name,
+							'isTitle', true,
+							'icon', v.icon,
+							'iconHeight', iconHeight,
+							'iconWidth', iconWidth,
+							'iconCoordLeft', iconCoordLeft,
+							'iconCoordRight', iconCoordRight,
+							'iconCoordTop', iconCoordTop,
+							'iconCoordBottom', iconCoordBottom
+						)
+					end
+				end
+			end
+			last_order = v.order or 100
+		end
+	elseif options.type == "text" and type(options.validate) == "table" then
+		local current
+		local options_p = passTable
+		if not options_p or (options.get and options.set) then
+			options_p = options
+			passTable = nil
+			passValue = nil
+		end
+		local multiToggle = options.multiToggle
+		local passValue = options.passValue or passValue
+		if not multiToggle then
+			current = callMethod(k, handler, options_p, "get", passValue)
+		end
+		local indexed = true
+		for k,v in pairs(options.validate) do
+			if type(k) ~= "number" then
+				indexed = false
+			end
+			table.insert(values, k)
+		end
+		if not indexed then
+			if not othersort then
+				othersort = function(alpha, bravo)
+					return othersort_validate[alpha]:gsub("|c%x%x%x%x%x%x%x%x", ""):gsub("|r", ""):upper() < othersort_validate[bravo]:gsub("|c%x%x%x%x%x%x%x%x", ""):gsub("|r", ""):upper()
+				end
+			end
+			othersort_validate = options.validate
+			table.sort(values, othersort)
+			othersort_validate = nil
+		end
+		for _,k in ipairs(values) do
+			local v = options.validate[k]
+			if type(k) == "number" then
+				k = v
+			end
+			local func, arg1, arg2, arg3, arg4 = getMethod(k, handler, options_p, "set",    skip1Nil(passValue, k))
+			local checked
+			if multiToggle then
+				checked = callMethod(k, handler, options_p, "get", skip1Nil(passValue, k)) or false
+				if arg2 == nil then
+					arg2 = not checked
+				elseif arg3 == nil then
+					arg3 = not checked
+				else
+					arg4 = not checked
+				end
+			else
+				checked = (k == current or (type(k) == "string" and type(current) == "string" and k:lower() == current:lower()))
+				if checked then
+					func, arg1, arg2, arg3, arg4 = nil, nil, nil, nil, nil
+				end
+			end
+			local tooltipTitle
+			local tooltipText
+			if options.validateDesc then
+				tooltipTitle = v
+				tooltipText = options.validateDesc[k]
+			else
+				tooltipTitle = options.guiName or options.name
+				tooltipText = v
+			end
+			self:AddLine(
+				'text', v,
+				'func', func,
+				'arg1', arg1,
+				'arg2', arg2,
+				'arg3', arg3,
+				'arg4', arg4,
+				'isRadio', not multiToggle,
+				'checked',  checked,
+				'tooltipTitle', tooltipTitle,
+				'tooltipText', tooltipText
+			)
+		end
+		for k in pairs(values) do
+			values[k] = nil
+		end
+	else
+		return false
+	end
+	return true
+end
+
+function Dewdrop:FeedTable(s, difference)
+	self:argCheck(s, 2, "table")
+	self:argCheck(difference, 3, "nil", "number")
+	if not currentLevel then
+		self:error("Cannot call `FeedTable' outside of a Dewdrop declaration")
+	end
+	if not difference then
+		difference = 0
+	end
+	local level = levels[currentLevel]
+	if not level then
+		self:error("Improper level given")
+	end
+	if not values then
+		values = {}
+	else
+		for k,v in pairs(values) do
+			values[k] = nil
+		end
+	end
+	local t = s.subMenu and s or {subMenu = s}
+	local current = level
+	while current do
+		if current.num == difference + 1 then
+			break
+		end
+		table.insert(values, current.value)
+		current = levels[current.num - 1]
+	end
+
+	while #values > 0 do
+		local value = table.remove(values)
+		t = t.subMenu and t.subMenu[value]
+		if not t then
+			return
+		end
+	end
+
+	if t.subMenu or current.num == 1 then
+		for k in pairs(t.subMenu) do
+			table.insert(values, k)
+		end
+		table.sort(values)
+		for _,k in ipairs(values) do
+			local argTable = {"value", k}
+			for key, val in pairs(t.subMenu[k]) do
+				table.insert(argTable, key)
+				table.insert(argTable, val)
+			end
+			self:AddLine(unpack(argTable))
+		end
+		for k in pairs(values) do
+			values[k] = nil
+		end
+		return false
+	end
+	return true
+end
+
+function Refresh(self, level)
+	if type(level) == "number" then
+		level = levels[level]
+	end
+	if not level then
+		return
+	end
+	if baseFunc then
+		Clear(self, level)
+		currentLevel = level.num
+		if type(baseFunc) == "table" then
+			if currentLevel == 1 then
+				local handler = baseFunc.handler
+				if handler then
+					local name = tostring(handler)
+					if not name:find('^table:') and not handler.hideMenuTitle then
+						name = name:gsub("|c%x%x%x%x%x%x%x%x(.-)|r", "%1")
+						self:AddLine(
+							'text', name,
+							'isTitle', true
+						)
+					end
+				end
+--			elseif level.parentText then
+--				self:AddLine(
+--					'text', level.parentText,
+--					'tooltipTitle', level.parentTooltipTitle,
+--					'tooltipText', level.parentTooltipText,
+--					'tooltipFunc', level.parentTooltipFunc,
+--					'isTitle', true
+--				)
+			end
+			self:FeedAceOptionsTable(baseFunc)
+			if currentLevel == 1 then
+				self:AddLine(
+					'text', CLOSE,
+					'tooltipTitle', CLOSE,
+					'tooltipText', CLOSE_DESC,
+					'closeWhenClicked', true
+				)
+			end
+		else
+--			if level.parentText then
+--				self:AddLine(
+--					'text', level.parentText,
+--					'tooltipTitle', level.parentTooltipTitle,
+--					'tooltipText', level.parentTooltipText,
+--					'tooltipFunc', level.parentTooltipFunc,
+--					'isTitle', true
+--				)
+--			end
+			baseFunc(currentLevel, level.value, levels[level.num - 1] and levels[level.num - 1].value, levels[level.num - 2] and levels[level.num - 2].value, levels[level.num - 3] and levels[level.num - 3].value, levels[level.num - 4] and levels[level.num - 4].value)
+		end
+		currentLevel = nil
+		CheckSize(self, level)
+	end
+end
+
+function Dewdrop:Refresh(level)
+	self:argCheck(level, 2, "number", "nil")
+	if not level then
+		for k,v in pairs(levels) do
+			Refresh(self, v)
+		end
+	else
+		Refresh(self, levels[level])
+	end
+end
+
+function OpenSlider(self, parent)
+	if not sliderFrame then
+		sliderFrame = CreateFrame("Frame", nil, nil)
+		sliderFrame:SetWidth(100)
+		sliderFrame:SetHeight(170)
+		sliderFrame:SetScale(UIParent:GetScale())
+		sliderFrame:SetBackdrop(tmp(
+			'bgFile', "Interface\\Tooltips\\UI-Tooltip-Background",
+			'edgeFile', "Interface\\Tooltips\\UI-Tooltip-Border",
+			'tile', true,
+			'insets', tmp2(
+				'left', 5,
+				'right', 5,
+				'top', 5,
+				'bottom', 5
+			),
+			'tileSize', 16,
+			'edgeSize', 16
+		))
+		sliderFrame:SetFrameStrata("FULLSCREEN_DIALOG")
+		if sliderFrame.SetTopLevel then
+			sliderFrame:SetTopLevel(true)
+		end
+		sliderFrame:SetBackdropBorderColor(TOOLTIP_DEFAULT_COLOR.r, TOOLTIP_DEFAULT_COLOR.g, TOOLTIP_DEFAULT_COLOR.b)
+		sliderFrame:SetBackdropColor(TOOLTIP_DEFAULT_BACKGROUND_COLOR.r, TOOLTIP_DEFAULT_BACKGROUND_COLOR.g, TOOLTIP_DEFAULT_BACKGROUND_COLOR.b)
+		sliderFrame:EnableMouse(true)
+		sliderFrame:EnableMouseWheel(true)
+		sliderFrame:Hide()
+		sliderFrame:SetPoint("CENTER", UIParent, "CENTER")
+		local slider = CreateFrame("Slider", nil, sliderFrame)
+		sliderFrame.slider = slider
+		slider:SetOrientation("VERTICAL")
+		slider:SetMinMaxValues(0, 1)
+		slider:SetValueStep(0.000000001)
+		slider:SetValue(0.5)
+		slider:SetWidth(16)
+		slider:SetHeight(128)
+		slider:SetPoint("LEFT", sliderFrame, "LEFT", 15, 0)
+		slider:SetBackdrop(tmp(
+			'bgFile', "Interface\\Buttons\\UI-SliderBar-Background",
+			'edgeFile', "Interface\\Buttons\\UI-SliderBar-Border",
+			'tile', true,
+			'edgeSize', 8,
+			'tileSize', 8,
+			'insets', tmp2(
+				'left', 3,
+				'right', 3,
+				'top', 3,
+				'bottom', 3
+			)
+		))
+		local texture = slider:CreateTexture()
+		slider:SetThumbTexture("Interface\\Buttons\\UI-SliderBar-Button-Vertical")
+		local text = slider:CreateFontString(nil, "ARTWORK")
+		sliderFrame.topText = text
+		text:SetFontObject(GameFontGreenSmall)
+		text:SetText("100%")
+		text:SetPoint("BOTTOM", slider, "TOP")
+		local text = slider:CreateFontString(nil, "ARTWORK")
+		sliderFrame.bottomText = text
+		text:SetFontObject(GameFontGreenSmall)
+		text:SetText("0%")
+		text:SetPoint("TOP", slider, "BOTTOM")
+		local editBox = CreateFrame("EditBox", nil, sliderFrame)
+		sliderFrame.currentText = editBox
+		editBox:SetFontObject(ChatFontNormal)
+		editBox:SetHeight(13)
+		editBox:SetPoint("RIGHT", sliderFrame, "RIGHT", -16, 0)
+		editBox:SetPoint("LEFT", slider, "RIGHT", 12, 0)
+		editBox:SetText("50%")
+		editBox:SetJustifyH("CENTER")
+
+		local width = editBox:GetWidth()/2 + 10
+		local left = editBox:CreateTexture(nil, "BACKGROUND")
+		left:SetTexture("Interface\\ChatFrame\\UI-ChatInputBorder-Left")
+		left:SetTexCoord(0, width / 256, 0, 1)
+		left:SetWidth(width)
+		left:SetHeight(32)
+		left:SetPoint("LEFT", editBox, "LEFT", -10, 0)
+		local right = editBox:CreateTexture(nil, "BACKGROUND")
+		right:SetTexture("Interface\\ChatFrame\\UI-ChatInputBorder-Right")
+		right:SetTexCoord(1 - width / 256, 1, 0, 1)
+		right:SetWidth(width)
+		right:SetHeight(32)
+		right:SetPoint("RIGHT", editBox, "RIGHT", 10, 0)
+
+		local changed = false
+		local inside = false
+		slider:SetScript("OnValueChanged", function()
+			if sliderFrame.changing then
+				return
+			end
+			changed = true
+			local done = false
+			if sliderFrame.parent and sliderFrame.parent.sliderFunc then
+				local min = sliderFrame.parent.sliderMin or 0
+				local max = sliderFrame.parent.sliderMax or 1
+				local step
+				if sliderFrame.fineStep then
+					step = sliderFrame.parent.sliderStep or (max - min) / 100
+				else
+					step = sliderFrame.parent.sliderBigStep or sliderFrame.parent.sliderStep or (max - min) / 100
+				end
+				local value = (1 - slider:GetValue()) * (max - min) + min
+				if step > 0 then
+					value = math.floor((value - min) / step + 0.5) * step + min
+					if value > max then
+						value = max
+					elseif value < min then
+						value = min
+					end
+				end
+				if value == sliderFrame.lastValue then
+					return
+				end
+				sliderFrame.lastValue = value
+				local text = sliderFrame.parent.sliderFunc(getArgs(sliderFrame.parent, 'sliderArg', 1, value))
+				if sliderFrame.parent.fromAceOptions then
+					text = nil
+				elseif type(text) == "string" or type(text) == "number" then
+					sliderFrame.currentText:SetText(text)
+					done = true
+				end
+			end
+			if not done then
+				local min = sliderFrame.parent.sliderMin or 0
+				local max = sliderFrame.parent.sliderMax or 1
+				local step
+				if sliderFrame.fineStep then
+					step = sliderFrame.parent.sliderStep or (max - min) / 100
+				else
+					step = sliderFrame.parent.sliderBigStep or sliderFrame.parent.sliderStep or (max - min) / 100
+				end
+				local value = (1 - slider:GetValue()) * (max - min) + min
+				if step > 0 then
+					value = math.floor((value - min) / step + 0.5) * step + min
+					if value > max then
+						value = max
+					elseif value < min then
+						value = min
+					end
+				end
+				if sliderFrame.parent.sliderIsPercent then
+					sliderFrame.currentText:SetText(string.format("%.0f%%", value * 100))
+				else
+					if step < 0.1 then
+						sliderFrame.currentText:SetText(string.format("%.2f", value))
+					elseif step < 1 then
+						sliderFrame.currentText:SetText(string.format("%.1f", value))
+					else
+						sliderFrame.currentText:SetText(string.format("%.0f", value))
+					end
+				end
+			end
+		end)
+		local function onEnter()
+			StopCounting(self, sliderFrame.level)
+			showGameTooltip(sliderFrame.parent)
+		end
+		local function onLeave()
+			GameTooltip:Hide()
+		end
+		sliderFrame:SetScript("OnEnter", onEnter)
+		sliderFrame:SetScript("OnLeave", function()
+			GameTooltip:Hide()
+			if changed then
+				local parent = sliderFrame.parent
+				local sliderFunc = parent.sliderFunc
+				for i = 1, sliderFrame.level - 1 do
+					Refresh(self, levels[i])
+				end
+				local newParent
+				for _,button in ipairs(levels[sliderFrame.level-1].buttons) do
+					if button.sliderFunc == sliderFunc then
+						newParent = button
+						break
+					end
+				end
+				if newParent then
+					OpenSlider(self, newParent)
+				else
+					sliderFrame:Hide()
+				end
+			end
+		end)
+		editBox:SetScript("OnEnter", onEnter)
+		editBox:SetScript("OnLeave", onLeave)
+		slider:SetScript("OnMouseDown", function()
+			sliderFrame.mouseDown = true
+			GameTooltip:Hide()
+		end)
+		slider:SetScript("OnMouseUp", function()
+			sliderFrame.mouseDown = false
+			if changed--[[ and not inside]] then
+				local parent = sliderFrame.parent
+				local sliderFunc = parent.sliderFunc
+				for i = 1, sliderFrame.level - 1 do
+					Refresh(self, levels[i])
+				end
+				local newParent
+				for _,button in ipairs(levels[sliderFrame.level-1].buttons) do
+					if button.sliderFunc == sliderFunc then
+						newParent = button
+						break
+					end
+				end
+				if newParent then
+					OpenSlider(self, newParent)
+				else
+					sliderFrame:Hide()
+				end
+			end
+			if inside then
+				showGameTooltip(sliderFrame.parent)
+			end
+		end)
+		slider:SetScript("OnEnter", function()
+			inside = true
+			StopCounting(self, sliderFrame.level)
+			showGameTooltip(sliderFrame.parent)
+		end)
+		slider:SetScript("OnLeave", function()
+			inside = false
+			GameTooltip:Hide()
+			if changed and not sliderFrame.mouseDown then
+				local parent = sliderFrame.parent
+				local sliderFunc = parent.sliderFunc
+				for i = 1, sliderFrame.level - 1 do
+					Refresh(self, levels[i])
+				end
+				local newParent
+				for _,button in ipairs(levels[sliderFrame.level-1].buttons) do
+					if button.sliderFunc == sliderFunc then
+						newParent = button
+						break
+					end
+				end
+				if newParent then
+					OpenSlider(self, newParent)
+				else
+					sliderFrame:Hide()
+				end
+
+				changed = false
+			end
+		end)
+		sliderFrame:SetScript("OnMouseWheel", function(t, a1)
+			local arg1 = a1 or arg1
+			local up = arg1 > 0
+
+			local min = sliderFrame.parent.sliderMin or 0
+			local max = sliderFrame.parent.sliderMax or 1
+			local step = sliderFrame.parent.sliderStep or (max - min) / 100
+			if step <= 0 then
+				step = (max - min) / 100
+			end
+
+			local value = (1 - slider:GetValue()) * (max - min) + min
+			if up then
+				value = value + step
+			else
+				value = value - step
+			end
+			if value > max then
+				value = max
+			elseif value < min then
+				value = min
+			end
+			sliderFrame.fineStep = true
+			if max<=min then
+				slider:SetValue(0)
+			else
+				slider:SetValue(1 - (value - min) / (max - min))
+			end
+			sliderFrame.fineStep = nil
+		end)
+		slider:SetScript("OnMouseWheel", sliderFrame:GetScript("OnMouseWheel"))
+		editBox:SetScript("OnEnterPressed", function(t, a1)
+			local value = editBox:GetNumber()
+
+			if sliderFrame.parent.sliderIsPercent then
+				value = value / 100
+			end
+
+			local min = sliderFrame.parent.sliderMin or 0
+			local max = sliderFrame.parent.sliderMax or 1
+
+			if value > max then
+				value = max
+			elseif value < min then
+				value = min
+			end
+			sliderFrame.fineStep = true
+			if max <= min then
+				slider:SetValue(0)
+			else
+				slider:SetValue(1 - (value - min) / (max - min))
+			end
+			sliderFrame.fineStep = nil
+
+			StartCounting(self, sliderFrame.level)
+		end)
+		editBox:SetScript("OnEscapePressed", function()
+			self:Close(sliderFrame.level)
+			StartCounting(self, sliderFrame.level)
+		end)
+		editBox:SetAutoFocus(false)
+	end
+	sliderFrame.parent = parent
+	sliderFrame.level = parent.level.num + 1
+	sliderFrame.parentValue = parent.level.value
+	sliderFrame:SetFrameLevel(parent.level:GetFrameLevel() + 3)
+	sliderFrame.slider:SetFrameLevel(sliderFrame:GetFrameLevel() + 1)
+	sliderFrame.currentText:SetFrameLevel(sliderFrame:GetFrameLevel() + 1)
+	sliderFrame.currentText:ClearFocus()
+	sliderFrame.changing = true
+	if not parent.sliderMin or not parent.sliderMax then
+		return
+	end
+
+	if parent.arrow then
+--		parent.arrow:SetVertexColor(0.2, 0.6, 0)
+--		parent.arrow:SetHeight(24)
+--		parent.arrow:SetWidth(24)
+		parent.selected = true
+		parent.highlight:Show()
+	end
+
+	sliderFrame:SetClampedToScreen(false)
+	if not parent.sliderValue then
+		parent.sliderValue = (parent.sliderMin + parent.sliderMax) / 2
+	end
+	if parent.sliderMax <= parent.sliderMin then
+		sliderFrame.slider:SetValue(0)
+	else
+		sliderFrame.slider:SetValue(1 - (parent.sliderValue - parent.sliderMin) / (parent.sliderMax - parent.sliderMin))
+	end
+	sliderFrame.changing = false
+	sliderFrame.bottomText:SetText(parent.sliderMinText or "0")
+	sliderFrame.topText:SetText(parent.sliderMaxText or "1")
+	local text
+	if parent.sliderFunc and not parent.fromAceOptions then
+		text = parent.sliderFunc(getArgs(parent, 'sliderArg', 1, parent.sliderValue))
+	end
+	if type(text) == "number" or type(text) == "string" then
+		sliderFrame.currentText:SetText(text)
+	elseif parent.sliderIsPercent then
+		sliderFrame.currentText:SetText(string.format("%.0f%%", parent.sliderValue * 100))
+	else
+		if parent.sliderStep < 0.1 then
+			sliderFrame.currentText:SetText(string.format("%.2f", parent.sliderValue))
+		elseif parent.sliderStep < 1 then
+			sliderFrame.currentText:SetText(string.format("%.1f", parent.sliderValue))
+		else
+			sliderFrame.currentText:SetText(string.format("%.0f", parent.sliderValue))
+		end
+	end
+
+
+	sliderFrame.lastValue = parent.sliderValue
+
+	local level = parent.level
+	sliderFrame:Show()
+	sliderFrame:ClearAllPoints()
+	if level.lastDirection == "RIGHT" then
+		if level.lastVDirection == "DOWN" then
+			sliderFrame:SetPoint("TOPLEFT", parent, "TOPRIGHT", 5, 10)
+		else
+			sliderFrame:SetPoint("BOTTOMLEFT", parent, "BOTTOMRIGHT", 5, -10)
+		end
+	else
+		if level.lastVDirection == "DOWN" then
+			sliderFrame:SetPoint("TOPRIGHT", parent, "TOPLEFT", -5, 10)
+		else
+			sliderFrame:SetPoint("BOTTOMRIGHT", parent, "BOTTOMLEFT", -5, -10)
+		end
+	end
+	local dirty
+	if level.lastDirection == "RIGHT" then
+		if sliderFrame:GetRight() > GetScreenWidth() then
+			level.lastDirection = "LEFT"
+			dirty = true
+		end
+	elseif sliderFrame:GetLeft() < 0 then
+		level.lastDirection = "RIGHT"
+		dirty = true
+	end
+	if level.lastVDirection == "DOWN" then
+		if sliderFrame:GetBottom() < 0 then
+			level.lastVDirection = "UP"
+			dirty = true
+		end
+	elseif sliderFrame:GetTop() > GetScreenWidth() then
+		level.lastVDirection = "DOWN"
+		dirty = true
+	end
+	if dirty then
+		sliderFrame:ClearAllPoints()
+		if level.lastDirection == "RIGHT" then
+			if level.lastVDirection == "DOWN" then
+				sliderFrame:SetPoint("TOPLEFT", parent, "TOPRIGHT", 5, 10)
+			else
+				sliderFrame:SetPoint("BOTTOMLEFT", parent, "BOTTOMRIGHT", 5, -10)
+			end
+		else
+			if level.lastVDirection == "DOWN" then
+				sliderFrame:SetPoint("TOPRIGHT", parent, "TOPLEFT", -5, 10)
+			else
+				sliderFrame:SetPoint("BOTTOMRIGHT", parent, "BOTTOMLEFT", -5, -10)
+			end
+		end
+	end
+	local left, bottom = sliderFrame:GetLeft(), sliderFrame:GetBottom()
+	sliderFrame:ClearAllPoints()
+	sliderFrame:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT", left, bottom)
+	if mod(level.num, 5) == 0 then
+		local left, bottom = level:GetLeft(), level:GetBottom()
+		level:ClearAllPoints()
+		level:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT", left, bottom)
+	end
+	sliderFrame:SetClampedToScreen(true)
+end
+
+function OpenEditBox(self, parent)
+	if not editBoxFrame then
+		editBoxFrame = CreateFrame("Frame", nil, nil)
+		editBoxFrame:SetWidth(200)
+		editBoxFrame:SetHeight(40)
+		editBoxFrame:SetScale(UIParent:GetScale())
+		editBoxFrame:SetBackdrop(tmp(
+			'bgFile', "Interface\\Tooltips\\UI-Tooltip-Background",
+			'edgeFile', "Interface\\Tooltips\\UI-Tooltip-Border",
+			'tile', true,
+			'insets', tmp2(
+				'left', 5,
+				'right', 5,
+				'top', 5,
+				'bottom', 5
+			),
+			'tileSize', 16,
+			'edgeSize', 16
+		))
+		editBoxFrame:SetFrameStrata("FULLSCREEN_DIALOG")
+		if editBoxFrame.SetTopLevel then
+			editBoxFrame:SetTopLevel(true)
+		end
+		editBoxFrame:SetBackdropBorderColor(TOOLTIP_DEFAULT_COLOR.r, TOOLTIP_DEFAULT_COLOR.g, TOOLTIP_DEFAULT_COLOR.b)
+		editBoxFrame:SetBackdropColor(TOOLTIP_DEFAULT_BACKGROUND_COLOR.r, TOOLTIP_DEFAULT_BACKGROUND_COLOR.g, TOOLTIP_DEFAULT_BACKGROUND_COLOR.b)
+		editBoxFrame:EnableMouse(true)
+		editBoxFrame:EnableMouseWheel(true)
+		editBoxFrame:Hide()
+		editBoxFrame:SetPoint("CENTER", UIParent, "CENTER")
+
+		local editBox = CreateFrame("EditBox", nil, editBoxFrame)
+		editBoxFrame.editBox = editBox
+		editBox:SetFontObject(ChatFontNormal)
+		editBox:SetWidth(160)
+		editBox:SetHeight(13)
+		editBox:SetPoint("CENTER", editBoxFrame, "CENTER", 0, 0)
+
+		local left = editBox:CreateTexture(nil, "BACKGROUND")
+		left:SetTexture("Interface\\ChatFrame\\UI-ChatInputBorder-Left")
+		left:SetTexCoord(0, 100 / 256, 0, 1)
+		left:SetWidth(100)
+		left:SetHeight(32)
+		left:SetPoint("LEFT", editBox, "LEFT", -10, 0)
+		local right = editBox:CreateTexture(nil, "BACKGROUND")
+		right:SetTexture("Interface\\ChatFrame\\UI-ChatInputBorder-Right")
+		right:SetTexCoord(156/256, 1, 0, 1)
+		right:SetWidth(100)
+		right:SetHeight(32)
+		right:SetPoint("RIGHT", editBox, "RIGHT", 10, 0)
+
+		editBox:SetScript("OnEnterPressed", function()
+			if editBoxFrame.parent and editBoxFrame.parent.editBoxValidateFunc then
+				local t = editBox.realText or editBox:GetText() or ""
+				local result = editBoxFrame.parent.editBoxValidateFunc(getArgs(editBoxFrame.parent, 'editBoxValidateArg', 1, t))
+				if not result then
+					UIErrorsFrame:AddMessage(VALIDATION_ERROR, 1, 0, 0)
+					return
+				end
+			end
+			if editBoxFrame.parent and editBoxFrame.parent.editBoxFunc then
+				local t
+				if editBox.realText ~= "NONE" then
+					t = editBox.realText or editBox:GetText() or ""
+				end
+				editBoxFrame.parent.editBoxFunc(getArgs(editBoxFrame.parent, 'editBoxArg', 1, t))
+			end
+			self:Close(editBoxFrame.level)
+			for i = 1, editBoxFrame.level - 1 do
+				Refresh(self, levels[i])
+			end
+			StartCounting(self, editBoxFrame.level-1)
+		end)
+		editBox:SetScript("OnEscapePressed", function()
+			self:Close(editBoxFrame.level)
+			StartCounting(self, editBoxFrame.level-1)
+		end)
+		editBox:SetScript("OnReceiveDrag", function(this)
+			if GetCursorInfo then
+				local type, alpha, bravo = GetCursorInfo()
+				local text
+				if type == "spell" then
+					text = GetSpellName(alpha, bravo)
+				elseif type == "item" then
+					text = bravo
+				end
+				if not text then
+					return
+				end
+				ClearCursor()
+				editBox:SetText(text)
+			end
+		end)
+		local changing = false
+		local skipNext = false
+
+		function editBox:SpecialSetText(text)
+			local oldText = editBox:GetText() or ""
+			if not text then
+				text = ""
+			end
+			if text ~= oldText then
+				changing = true
+				self:SetText(tostring(text))
+				changing = false
+				skipNext = true
+			end
+		end
+
+		editBox:SetScript("OnTextChanged", function()
+			if skipNext then
+				skipNext = false
+			elseif not changing and editBoxFrame.parent and editBoxFrame.parent.editBoxChangeFunc then
+				local t
+				if editBox.realText ~= "NONE" then
+					t = editBox.realText or editBox:GetText() or ""
+				end
+				local text = editBoxFrame.parent.editBoxChangeFunc(getArgs(editBoxFrame.parent, 'editBoxChangeArg', 1, t))
+				if text then
+					editBox:SpecialSetText(text)
+				end
+			end
+		end)
+		editBoxFrame:SetScript("OnEnter", function()
+			StopCounting(self, editBoxFrame.level)
+			showGameTooltip(editBoxFrame.parent)
+		end)
+		editBoxFrame:SetScript("OnLeave", function()
+			GameTooltip:Hide()
+		end)
+		editBox:SetScript("OnEnter", function()
+			StopCounting(self, editBoxFrame.level)
+			showGameTooltip(editBoxFrame.parent)
+		end)
+		editBox:SetScript("OnLeave", function()
+			GameTooltip:Hide()
+		end)
+		editBoxFrame:SetScript("OnKeyDown", function(this, a1)
+			if not editBox.keybinding then
+				return
+			end
+			local arg1 = a1 or arg1
+			local screenshotKey = GetBindingKey("SCREENSHOT")
+			if screenshotKey and arg1 == screenshotKey then
+				Screenshot()
+				return
+			end
+
+			if arg1 == "LeftButton" then
+				arg1 = "BUTTON1"
+			elseif arg1 == "RightButton" then
+				arg1 = "BUTTON2"
+			elseif arg1 == "MiddleButton" then
+				arg1 = "BUTTON3"
+			elseif arg1 == "Button4" then
+				arg1 = "BUTTON4"
+			elseif arg1 == "Button5" then
+				arg1 = "BUTTON5"
+			end
+			if arg1 == "UNKNOWN" then
+				return
+			elseif arg1 == "SHIFT" or arg1 == "CTRL" or arg1 == "ALT" then
+				return
+			elseif arg1 == "ENTER" then
+				if editBox.keybindingOnly and not editBox.keybindingOnly[editBox.realText] then
+					return editBox:GetScript("OnEscapePressed")()
+				elseif editBox.keybindingExcept and editBox.keybindingExcept[editBox.realText] then
+					return editBox:GetScript("OnEscapePressed")()
+				else
+					return editBox:GetScript("OnEnterPressed")()
+				end
+			elseif arg1 == "ESCAPE" then
+				if editBox.realText == "NONE" then
+					return editBox:GetScript("OnEscapePressed")()
+				else
+					editBox:SpecialSetText(NONE or "NONE")
+					editBox.realText = "NONE"
+					return
+				end
+			elseif editBox.keybindingOnly and not editBox.keybindingOnly[arg1] then
+				return
+			elseif editBox.keybindingExcept and editBox.keybindingExcept[arg1] then
+				return
+			end
+			local s = GetBindingText(arg1, "KEY_")
+			if s == "BUTTON1" then
+				s = KEY_BUTTON1
+			elseif s == "BUTTON2" then
+				s = KEY_BUTTON2
+			end
+			local real = arg1
+			if IsShiftKeyDown() then
+				s = "Shift-" .. s
+				real = "SHIFT-" .. real
+			end
+			if IsControlKeyDown() then
+				s = "Ctrl-" .. s
+				real = "CTRL-" .. real
+			end
+			if IsAltKeyDown() then
+				s = "Alt-" .. s
+				real = "ALT-" .. real
+			end
+			if editBox:GetText() ~= s then
+				editBox:SpecialSetText("-")
+				editBox:SpecialSetText(s)
+				editBox.realText = real
+				return editBox:GetScript("OnTextChanged")()
+			end
+		end)
+		editBoxFrame:SetScript("OnMouseDown", editBoxFrame:GetScript("OnKeyDown"))
+		editBox:SetScript("OnMouseDown", function(this, ...)
+			if GetCursorInfo and (CursorHasItem() or CursorHasSpell()) then
+				return editBox:GetScript("OnReceiveDrag")(this, ...)
+			end
+			return editBoxFrame:GetScript("OnKeyDown")(this, ...)
+		end)
+		editBoxFrame:SetScript("OnMouseWheel", function(t, a1)
+			local arg1 = a1 or arg1
+			local up = arg1 > 0
+			arg1 = up and "MOUSEWHEELUP" or "MOUSEWHEELDOWN"
+			return editBoxFrame:GetScript("OnKeyDown")(t or this, arg1)
+		end)
+		editBox:SetScript("OnMouseWheel", editBoxFrame:GetScript("OnMouseWheel"))
+	end
+	editBoxFrame.parent = parent
+	editBoxFrame.level = parent.level.num + 1
+	editBoxFrame.parentValue = parent.level.value
+	editBoxFrame:SetFrameLevel(parent.level:GetFrameLevel() + 3)
+	editBoxFrame.editBox:SetFrameLevel(editBoxFrame:GetFrameLevel() + 1)
+	editBoxFrame.editBox.realText = nil
+	editBoxFrame:SetClampedToScreen(false)
+
+	editBoxFrame.editBox:SpecialSetText("")
+	if parent.editBoxIsKeybinding then
+		local s = parent.editBoxText
+		if s == "" then
+			s =  "NONE"
+		end
+		editBoxFrame.editBox.realText = s
+		if s and s ~= "NONE" then
+			local alpha,bravo = s:match("^(.+)%-(.+)$")
+			if not bravo then
+				alpha = nil
+				bravo = s
+			end
+			bravo = GetBindingText(bravo, "KEY_")
+			if alpha then
+				editBoxFrame.editBox:SpecialSetText(alpha:upper() .. "-" .. bravo)
+			else
+				editBoxFrame.editBox:SpecialSetText(bravo)
+			end
+		else
+			editBoxFrame.editBox:SpecialSetText(NONE or "NONE")
+		end
+	else
+		editBoxFrame.editBox:SpecialSetText(parent.editBoxText)
+	end
+
+	editBoxFrame.editBox.keybinding = parent.editBoxIsKeybinding
+	editBoxFrame.editBox.keybindingOnly = parent.editBoxKeybindingOnly
+	editBoxFrame.editBox.keybindingExcept = parent.editBoxKeybindingExcept
+	editBoxFrame.editBox:EnableKeyboard(not parent.editBoxIsKeybinding)
+	editBoxFrame:EnableKeyboard(parent.editBoxIsKeybinding)
+
+	if parent.arrow then
+--		parent.arrow:SetVertexColor(0.2, 0.6, 0)
+--		parent.arrow:SetHeight(24)
+--		parent.arrow:SetWidth(24)
+		parent.selected = true
+		parent.highlight:Show()
+	end
+
+	local level = parent.level
+	editBoxFrame:Show()
+	editBoxFrame:ClearAllPoints()
+	if level.lastDirection == "RIGHT" then
+		if level.lastVDirection == "DOWN" then
+			editBoxFrame:SetPoint("TOPLEFT", parent, "TOPRIGHT", 5, 10)
+		else
+			editBoxFrame:SetPoint("BOTTOMLEFT", parent, "BOTTOMRIGHT", 5, -10)
+		end
+	else
+		if level.lastVDirection == "DOWN" then
+			editBoxFrame:SetPoint("TOPRIGHT", parent, "TOPLEFT", -5, 10)
+		else
+			editBoxFrame:SetPoint("BOTTOMRIGHT", parent, "BOTTOMLEFT", -5, -10)
+		end
+	end
+	local dirty
+	if level.lastDirection == "RIGHT" then
+		if editBoxFrame:GetRight() > GetScreenWidth() then
+			level.lastDirection = "LEFT"
+			dirty = true
+		end
+	elseif editBoxFrame:GetLeft() < 0 then
+		level.lastDirection = "RIGHT"
+		dirty = true
+	end
+	if level.lastVDirection == "DOWN" then
+		if editBoxFrame:GetBottom() < 0 then
+			level.lastVDirection = "UP"
+			dirty = true
+		end
+	elseif editBoxFrame:GetTop() > GetScreenWidth() then
+		level.lastVDirection = "DOWN"
+		dirty = true
+	end
+	if dirty then
+		editBoxFrame:ClearAllPoints()
+		if level.lastDirection == "RIGHT" then
+			if level.lastVDirection == "DOWN" then
+				editBoxFrame:SetPoint("TOPLEFT", parent, "TOPRIGHT", 5, 10)
+			else
+				editBoxFrame:SetPoint("BOTTOMLEFT", parent, "BOTTOMRIGHT", 5, -10)
+			end
+		else
+			if level.lastVDirection == "DOWN" then
+				editBoxFrame:SetPoint("TOPRIGHT", parent, "TOPLEFT", -5, 10)
+			else
+				editBoxFrame:SetPoint("BOTTOMRIGHT", parent, "BOTTOMLEFT", -5, -10)
+			end
+		end
+	end
+	local left, bottom = editBoxFrame:GetLeft(), editBoxFrame:GetBottom()
+	editBoxFrame:ClearAllPoints()
+	editBoxFrame:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT", left, bottom)
+	if mod(level.num, 5) == 0 then
+		local left, bottom = level:GetLeft(), level:GetBottom()
+		level:ClearAllPoints()
+		level:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT", left, bottom)
+	end
+	editBoxFrame:SetClampedToScreen(true)
+end
+
+function Dewdrop:EncodeKeybinding(text)
+	if text == nil or text == "NONE" then
+		return nil
+	end
+	text = tostring(text):upper()
+	local shift, ctrl, alt
+	local modifier
+	while true do
+		if text == "-" then
+			break
+		end
+		modifier, text = strsplit('-', text, 2)
+		if text then
+			if modifier ~= "SHIFT" and modifier ~= "CTRL" and modifier ~= "ALT" then
+				return false
+			end
+			if modifier == "SHIFT" then
+				if shift then
+					return false
+				end
+				shift = true
+			end
+			if modifier == "CTRL" then
+				if ctrl then
+					return false
+				end
+				ctrl = true
+			end
+			if modifier == "ALT" then
+				if alt then
+					return false
+				end
+				alt = true
+			end
+		else
+			text = modifier
+			break
+		end
+	end
+	if not text:find("^F%d+$") and text ~= "CAPSLOCK" and text:len() ~= 1 and (text:len() == 0 or text:byte() < 128 or text:len() > 4) and not _G["KEY_" .. text] and text ~= "BUTTON1" and text ~= "BUTTON2" then
+		return false
+	end
+	local s = GetBindingText(text, "KEY_")
+	if s == "BUTTON1" then
+		s = KEY_BUTTON1
+	elseif s == "BUTTON2" then
+		s = KEY_BUTTON2
+	end
+	if shift then
+		s = "Shift-" .. s
+	end
+	if ctrl then
+		s = "Ctrl-" .. s
+	end
+	if alt then
+		s = "Alt-" .. s
+	end
+	return s
+end
+
+function Dewdrop:IsOpen(parent)
+	self:argCheck(parent, 2, "table", "string", "nil")
+	return levels[1] and levels[1]:IsShown() and (not parent or parent == levels[1].parent or parent == levels[1]:GetParent())
+end
+
+function Dewdrop:GetOpenedParent()
+	return (levels[1] and levels[1]:IsShown()) and (levels[1].parent or levels[1]:GetParent())
+end
+
+function Open(self, parent, func, level, value, point, relativePoint, cursorX, cursorY)
+	self:Close(level)
+	if DewdropLib then
+		local d = DewdropLib:GetInstance('1.0')
+		local ret, val = pcall(d, IsOpen, d)
+		if ret and val then
+			DewdropLib:GetInstance('1.0'):Close()
+		end
+	end
+	if type(parent) == "table" then
+		parent:GetCenter()
+	end
+	local frame = AcquireLevel(self, level)
+	if level == 1 then
+		frame.lastDirection = "RIGHT"
+		frame.lastVDirection = "DOWN"
+	else
+		frame.lastDirection = levels[level - 1].lastDirection
+		frame.lastVDirection = levels[level - 1].lastVDirection
+	end
+	frame:SetFrameStrata("FULLSCREEN_DIALOG")
+	frame:ClearAllPoints()
+	frame.parent = parent
+	frame:SetPoint("LEFT", UIParent, "RIGHT", 10000, 0)
+	frame:Show()
+	if level == 1 then
+		baseFunc = func
+	end
+	levels[level].value = value
+--	levels[level].parentText = parent.text and parent.text:GetText() or nil
+--	levels[level].parentTooltipTitle = parent.tooltipTitle
+--	levels[level].parentTooltipText = parent.tooltipText
+--	levels[level].parentTooltipFunc = parent.tooltipFunc
+	if type(parent) == "table" and parent.arrow then
+--		parent.arrow:SetVertexColor(0.2, 0.6, 0)
+--		parent.arrow:SetHeight(24)
+--		parent.arrow:SetWidth(24)
+		parent.selected = true
+		parent.highlight:Show()
+	end
+	relativePoint = relativePoint or point
+	Refresh(self, levels[level])
+	if point or (cursorX and cursorY) then
+		frame:ClearAllPoints()
+		if cursorX and cursorY then
+			local curX, curY = GetScaledCursorPosition()
+			if curY < GetScreenHeight() / 2 then
+				point, relativePoint = "BOTTOM", "BOTTOM"
+			else
+				point, relativePoint = "TOP", "TOP"
+			end
+			if curX < GetScreenWidth() / 2 then
+				point, relativePoint = point .. "LEFT", relativePoint .. "RIGHT"
+			else
+				point, relativePoint = point .. "RIGHT", relativePoint .. "LEFT"
+			end
+		end
+		frame:SetPoint(point, type(parent) == "table" and parent or UIParent, relativePoint)
+		if cursorX and cursorY then
+			local left = frame:GetLeft()
+			local width = frame:GetWidth()
+			local bottom = frame:GetBottom()
+			local height = frame:GetHeight()
+			local curX, curY = GetScaledCursorPosition()
+			frame:ClearAllPoints()
+			relativePoint = relativePoint or point
+			if point == "BOTTOM" or point == "TOP" then
+				if curX < GetScreenWidth() / 2 then
+					point = point .. "LEFT"
+				else
+					point = point .. "RIGHT"
+				end
+			elseif point == "CENTER" then
+				if curX < GetScreenWidth() / 2 then
+					point = "LEFT"
+				else
+					point = "RIGHT"
+				end
+			end
+			local xOffset, yOffset = 0, 0
+			if curY > GetScreenHeight() / 2 then
+				yOffset = -height
+			end
+			if curX > GetScreenWidth() / 2 then
+				xOffset = -width
+			end
+			frame:SetPoint(point, type(parent) == "table" and parent or UIParent, relativePoint, curX - left + xOffset, curY - bottom + yOffset)
+			if level == 1 then
+				frame.lastDirection = "RIGHT"
+			end
+		elseif cursorX then
+			local left = frame:GetLeft()
+			local width = frame:GetWidth()
+			local curX, curY = GetScaledCursorPosition()
+			frame:ClearAllPoints()
+			relativePoint = relativePoint or point
+			if point == "BOTTOM" or point == "TOP" then
+				if curX < GetScreenWidth() / 2 then
+					point = point .. "LEFT"
+				else
+					point = point .. "RIGHT"
+				end
+			elseif point == "CENTER" then
+				if curX < GetScreenWidth() / 2 then
+					point = "LEFT"
+				else
+					point = "RIGHT"
+				end
+			end
+			frame:SetPoint(point, type(parent) == "table" and parent or UIParent, relativePoint, curX - left - width / 2, 0)
+			if level == 1 then
+				frame.lastDirection = "RIGHT"
+			end
+		elseif cursorY then
+			local bottom = frame:GetBottom()
+			local height = frame:GetHeight()
+			local curX, curY = GetScaledCursorPosition()
+			frame:ClearAllPoints()
+			relativePoint = relativePoint or point
+			if point == "LEFT" or point == "RIGHT" then
+				if curX < GetScreenHeight() / 2 then
+					point = point .. "BOTTOM"
+				else
+					point = point .. "TOP"
+				end
+			elseif point == "CENTER" then
+				if curX < GetScreenHeight() / 2 then
+					point = "BOTTOM"
+				else
+					point = "TOP"
+				end
+			end
+			frame:SetPoint(point, type(parent) == "table" and parent or UIParent, relativePoint, 0, curY - bottom - height / 2)
+			if level == 1 then
+				frame.lastDirection = "DOWN"
+			end
+		end
+		if (strsub(point, 1, 3) ~= strsub(relativePoint, 1, 3)) then
+			if frame:GetBottom() < 0 then
+				local point, parent, relativePoint, x, y = frame:GetPoint(1)
+				local change = GetScreenHeight() - frame:GetTop()
+				local otherChange = -frame:GetBottom()
+				if otherChange < change then
+					change = otherChange
+				end
+				frame:SetPoint(point, parent, relativePoint, x, y + change)
+			elseif frame:GetTop() > GetScreenHeight() then
+				local point, parent, relativePoint, x, y = frame:GetPoint(1)
+				local change = GetScreenHeight() - frame:GetTop()
+				local otherChange = -frame:GetBottom()
+				if otherChange < change then
+					change = otherChange
+				end
+				frame:SetPoint(point, parent, relativePoint, x, y + change)
+			end
+		end
+	end
+	CheckDualMonitor(self, frame)
+	frame:SetClampedToScreen(true)
+	frame:SetClampedToScreen(false)
+	StartCounting(self, level)
+end
+
+function Dewdrop:IsRegistered(parent)
+	self:argCheck(parent, 2, "table", "string")
+	return not not self.registry[parent]
+end
+
+function Dewdrop:Register(parent, ...)
+	self:argCheck(parent, 2, "table", "string")
+	if self.registry[parent] then
+		self:Unregister(parent)
+	end
+	local info = new(...)
+	if type(info.children) == "table" then
+		local err, position = validateOptions(info.children)
+
+		if err then
+			if position then
+				Dewdrop:error(position .. ": " .. err)
+			else
+				Dewdrop:error(err)
+			end
+		end
+	end
+	self.registry[parent] = info
+	if not info.dontHook and not self.onceRegistered[parent] and type(parent) == "table" then
+		if parent:HasScript("OnMouseUp") then
+			local script = parent:GetScript("OnMouseUp")
+			parent:SetScript("OnMouseUp", function(this, ...)
+				if script then
+					script(this, ...)
+				end
+				if arg1 == "RightButton" and self.registry[parent] then
+					if self:IsOpen(parent) then
+						self:Close()
+					else
+						self:Open(parent)
+					end
+				end
+			end)
+		end
+		if parent:HasScript("OnMouseDown") then
+			local script = parent:GetScript("OnMouseDown")
+			parent:SetScript("OnMouseDown", function(this, ...)
+				if script then
+					script(this, ...)
+				end
+				if self.registry[parent] then
+					self:Close()
+				end
+			end)
+		end
+	end
+	self.onceRegistered[parent] = true
+end
+
+function Dewdrop:Unregister(parent)
+	self:argCheck(parent, 2, "table", "string")
+	self.registry[parent] = nil
+end
+
+function Dewdrop:Open(parent, ...)
+	self:argCheck(parent, 2, "table", "string")
+	local info
+	local k1 = ...
+	if type(k1) == "table" and k1[0] and k1.IsFrameType and self.registry[k1] then
+		info = tmp(select(2, ...))
+		for k,v in pairs(self.registry[k1]) do
+			if info[k] == nil then
+				info[k] = v
+			end
+		end
+	else
+		info = tmp(...)
+		if self.registry[parent] then
+			for k,v in pairs(self.registry[parent]) do
+				if info[k] == nil then
+					info[k] = v
+				end
+			end
+		end
+	end
+	local point = info.point
+	local relativePoint = info.relativePoint
+	local cursorX = info.cursorX
+	local cursorY = info.cursorY
+	if type(point) == "function" then
+		local b
+		point, b = point(parent)
+		if b then
+			relativePoint = b
+		end
+	end
+	if type(relativePoint) == "function" then
+		relativePoint = relativePoint(parent)
+	end
+	Open(self, parent, info.children, 1, nil, point, relativePoint, cursorX, cursorY)
+end
+
+function Clear(self, level)
+	if level then
+		if level.buttons then
+			for i = #level.buttons, 1, -1 do
+				ReleaseButton(self, level, i)
+			end
+		end
+	end
+end
+
+function Dewdrop:Close(level)
+	if DropDownList1:IsShown() then
+		DropDownList1:Hide()
+	end
+	if DewdropLib then
+		local d = DewdropLib:GetInstance('1.0')
+		local ret, val = pcall(d, IsOpen, d)
+		if ret and val then
+			DewdropLib:GetInstance('1.0'):Close()
+		end
+	end
+	self:argCheck(level, 2, "number", "nil")
+	if not level then
+		level = 1
+	end
+	if level == 1 and levels[level] then
+		levels[level].parented = false
+	end
+	if level > 1 and levels[level-1].buttons then
+		local buttons = levels[level-1].buttons
+		for _,button in ipairs(buttons) do
+--			button.arrow:SetWidth(16)
+--			button.arrow:SetHeight(16)
+			button.selected = nil
+			button.highlight:Hide()
+--			button.arrow:SetVertexColor(1, 1, 1)
+		end
+	end
+	if sliderFrame and sliderFrame.level >= level then
+		sliderFrame:Hide()
+	end
+	if editBoxFrame and editBoxFrame.level >= level then
+		editBoxFrame:Hide()
+	end
+	for i = level, #levels do
+		Clear(self, levels[level])
+		levels[i]:Hide()
+		levels[i]:ClearAllPoints()
+		levels[i]:SetPoint("CENTER", UIParent, "CENTER")
+		levels[i].value = nil
+	end
+end
+
+function Dewdrop:AddSeparator(level)
+	level = levels[level or currentLevel]
+	if not level or not level.buttons then return; end
+
+	local prevbutton = level.buttons[#level.buttons]
+	if not prevbutton then return; end
+
+	if prevbutton.disabled and prevbutton.text:GetText() == "" then
+		return
+	end
+	self:AddLine("text", "", "disabled", true)
+end
+
+function Dewdrop:AddLine(...)
+	local info = tmp(...)
+	local level = info.level or currentLevel
+	info.level = nil
+	local button = AcquireButton(self, level)
+	if not next(info) then
+		info.disabled = true
+	end
+	button.disabled = info.isTitle or info.notClickable or info.disabled or (self.combat and info.secure)
+	button.isTitle = info.isTitle
+	button.notClickable = info.notClickable
+	if button.isTitle then
+		button.text:SetFontObject(GameFontNormalSmall)
+	elseif button.notClickable then
+		button.text:SetFontObject(GameFontHighlightSmall)
+	elseif button.disabled then
+		button.text:SetFontObject(GameFontDisableSmall)
+	else
+		button.text:SetFontObject(GameFontHighlightSmall)
+	end
+	if info.disabled then
+		button.arrow:SetDesaturated(true)
+		button.check:SetDesaturated(true)
+	else
+		button.arrow:SetDesaturated(false)
+		button.check:SetDesaturated(false)
+	end
+	if info.textR and info.textG and info.textB then
+		button.textR = info.textR
+		button.textG = info.textG
+		button.textB = info.textB
+		button.text:SetTextColor(button.textR, button.textG, button.textB)
+	else
+		button.text:SetTextColor(button.text:GetFontObject():GetTextColor())
+	end
+	button.notCheckable = info.notCheckable
+	button.text:SetPoint("LEFT", button, "LEFT", button.notCheckable and 0 or 24, 0)
+	button.checked = not info.notCheckable and info.checked
+	button.mouseoverUnderline = info.mouseoverUnderline
+	button.isRadio = not info.notCheckable and info.isRadio
+	if info.isRadio then
+		button.check:Show()
+		button.check:SetTexture(info.checkIcon or "Interface\\Buttons\\UI-RadioButton")
+		if button.checked then
+			button.check:SetTexCoord(0.25, 0.5, 0, 1)
+			button.check:SetVertexColor(1, 1, 1, 1)
+		else
+			button.check:SetTexCoord(0, 0.25, 0, 1)
+			button.check:SetVertexColor(1, 1, 1, 0.5)
+		end
+		button.radioHighlight:SetTexture(info.checkIcon or "Interface\\Buttons\\UI-RadioButton")
+		button.check:SetWidth(16)
+		button.check:SetHeight(16)
+	elseif info.icon then
+		button.check:Show()
+		button.check:SetTexture(info.icon)
+		if info.iconWidth and info.iconHeight then
+			button.check:SetWidth(info.iconWidth)
+			button.check:SetHeight(info.iconHeight)
+		else
+			button.check:SetWidth(16)
+			button.check:SetHeight(16)
+		end
+		if info.iconCoordLeft and info.iconCoordRight and info.iconCoordTop and info.iconCoordBottom then
+			button.check:SetTexCoord(info.iconCoordLeft, info.iconCoordRight, info.iconCoordTop, info.iconCoordBottom)
+		elseif info.icon:find("^Interface\\Icons\\") then
+			button.check:SetTexCoord(0.05, 0.95, 0.05, 0.95)
+		else
+			button.check:SetTexCoord(0, 1, 0, 1)
+		end
+		button.check:SetVertexColor(1, 1, 1, 1)
+	else
+		if button.checked then
+			if info.checkIcon then
+				button.check:SetWidth(16)
+				button.check:SetHeight(16)
+				button.check:SetTexture(info.checkIcon)
+				if info.checkIcon:find("^Interface\\Icons\\") then
+					button.check:SetTexCoord(0.05, 0.95, 0.05, 0.95)
+				else
+					button.check:SetTexCoord(0, 1, 0, 1)
+				end
+			else
+				button.check:SetWidth(24)
+				button.check:SetHeight(24)
+				button.check:SetTexture("Interface\\Buttons\\UI-CheckBox-Check")
+				button.check:SetTexCoord(0, 1, 0, 1)
+			end
+			button.check:SetVertexColor(1, 1, 1, 1)
+		else
+			button.check:SetVertexColor(1, 1, 1, 0)
+		end
+	end
+	if not button.disabled then
+		button.func = info.func
+		button.secure = info.secure
+	end
+	button.hasColorSwatch = info.hasColorSwatch
+	if button.hasColorSwatch then
+		button.colorSwatch:Show()
+		button.colorSwatch.texture:Show()
+		button.r = info.r or 1
+		button.g = info.g or 1
+		button.b = info.b or 1
+		button.colorSwatch.texture:SetVertexColor(button.r, button.g, button.b)
+		button.checked = false
+		button.func = nil
+		button.colorFunc = info.colorFunc
+		local i = 1
+		while true do
+			local k = "colorArg" .. i
+			local x = info[k]
+			if x == nil then
+				break
+			end
+			button[k] = x
+			i = i + 1
+		end
+		button.hasOpacity = info.hasOpacity
+		button.opacity = info.opacity or 1
+	else
+		button.colorSwatch:Hide()
+		button.colorSwatch.texture:Hide()
+	end
+	button.hasArrow = not button.hasColorSwatch and (info.value or info.hasSlider or info.hasEditBox) and info.hasArrow
+	if button.hasArrow then
+		button.arrow:SetAlpha(1)
+		if info.hasSlider then
+			button.hasSlider = true
+			button.sliderMin = info.sliderMin or 0
+			button.sliderMax = info.sliderMax or 1
+			button.sliderStep = info.sliderStep or 0
+			button.sliderBigStep = info.sliderBigStep or button.sliderStep
+			if button.sliderBigStep < button.sliderStep then
+				button.sliderBigStep = button.sliderStep
+			end
+			button.sliderIsPercent = info.sliderIsPercent and true or false
+			button.sliderMinText = info.sliderMinText or button.sliderIsPercent and string.format("%.0f%%", button.sliderMin * 100) or button.sliderMin
+			button.sliderMaxText = info.sliderMaxText or button.sliderIsPercent and string.format("%.0f%%", button.sliderMax * 100) or button.sliderMax
+			button.sliderFunc = info.sliderFunc
+			button.sliderValue = info.sliderValue
+			button.fromAceOptions = info.fromAceOptions
+			local i = 1
+			while true do
+				local k = "sliderArg" .. i
+				local x = info[k]
+				if x == nil then
+					break
+				end
+				button[k] = x
+				i = i + 1
+			end
+		elseif info.hasEditBox then
+			button.hasEditBox = true
+			button.editBoxText = info.editBoxText or ""
+			button.editBoxFunc = info.editBoxFunc
+			local i = 1
+			while true do
+				local k = "editBoxArg" .. i
+				local x = info[k]
+				if x == nil then
+					break
+				end
+				button[k] = x
+				i = i + 1
+			end
+			button.editBoxChangeFunc = info.editBoxChangeFunc
+			local i = 1
+			while true do
+				local k = "editBoxChangeArg" .. i
+				local x = info[k]
+				if x == nil then
+					break
+				end
+				button[k] = x
+				i = i + 1
+			end
+			button.editBoxValidateFunc = info.editBoxValidateFunc
+			local i = 1
+			while true do
+				local k = "editBoxValidateArg" .. i
+				local x = info[k]
+				if x == nil then
+					break
+				end
+				button[k] = x
+				i = i + 1
+			end
+			button.editBoxIsKeybinding = info.editBoxIsKeybinding
+			button.editBoxKeybindingOnly = info.editBoxKeybindingOnly
+			button.editBoxKeybindingExcept = info.editBoxKeybindingExcept
+		else
+			button.value = info.value
+			local l = levels[level+1]
+			if l and info.value == l.value then
+--				button.arrow:SetWidth(24)
+--				button.arrow:SetHeight(24)
+				button.selected = true
+				button.highlight:Show()
+			end
+		end
+	else
+		button.arrow:SetAlpha(0)
+	end
+	local i = 1
+	while true do
+		local k = "arg" .. i
+		local x = info[k]
+		if x == nil then
+			break
+		end
+		button[k] = x
+		i = i + 1
+	end
+	button.closeWhenClicked = info.closeWhenClicked
+	button.textHeight = info.textHeight or UIDROPDOWNMENU_DEFAULT_TEXT_HEIGHT or 10
+	local font,_ = button.text:GetFont()
+	button.text:SetFont(STANDARD_TEXT_FONT or "Fonts\\FRIZQT__.TTF", button.textHeight)
+	button:SetHeight(button.textHeight + 6)
+	button.text:SetPoint("RIGHT", button.arrow, (button.hasColorSwatch or button.hasArrow) and "LEFT" or "RIGHT")
+	button.text:SetJustifyH(info.justifyH or "LEFT")
+	button.text:SetText(info.text)
+	button.tooltipTitle = info.tooltipTitle
+	button.tooltipText = info.tooltipText
+	button.tooltipFunc = info.tooltipFunc
+	local i = 1
+	while true do
+		local k = "tooltipArg" .. i
+		local x = info[k]
+		if x == nil then
+			break
+		end
+		button[k] = x
+		i = i + 1
+	end
+	if not button.tooltipTitle and not button.tooltipText and not button.tooltipFunc and not info.isTitle then
+		button.tooltipTitle = info.text
+	end
+	if type(button.func) == "string" then
+		if type(button.arg1) ~= "table" then
+			self:error("Cannot call method %q on a non-table", button.func)
+		end
+		if type(button.arg1[button.func]) ~= "function" then
+			self:error("Method %q nonexistant.", button.func)
+		end
+	end
+end
+
+function Dewdrop:InjectAceOptionsTable(handler, options)
+	self:argCheck(handler, 2, "table")
+	self:argCheck(options, 3, "table")
+	if tostring(options.type):lower() ~= "group" then
+		self:error('Cannot inject into options table argument #3 if its type is not "group"')
+	end
+	if options.handler ~= nil and options.handler ~= handler then
+		self:error("Cannot inject into options table argument #3 if it has a different handler than argument #2")
+	end
+	options.handler = handler
+	local class = handler.class
+	if not AceLibrary:HasInstance("AceOO-2.0") or not class then
+		if Rock then
+			-- possible Rock object
+			for mixin in Rock:IterateObjectMixins(handler) do
+				if type(mixin.GetAceOptionsDataTable) == "function" then
+					local t = mixin:GetAceOptionsDataTable(handler)
+					for k,v in pairs(t) do
+						if type(options.args) ~= "table" then
+							options.args = {}
+						end
+						if options.args[k] == nil then
+							options.args[k] = v
+						end
+					end
+				end
+			end
+		end
+	else
+		-- Ace2 object
+		while class and class ~= AceLibrary("AceOO-2.0").Class do
+			if type(class.GetAceOptionsDataTable) == "function" then
+				local t = class:GetAceOptionsDataTable(handler)
+				for k,v in pairs(t) do
+					if type(options.args) ~= "table" then
+						options.args = {}
+					end
+					if options.args[k] == nil then
+						options.args[k] = v
+					end
+				end
+			end
+			local mixins = class.mixins
+			if mixins then
+				for mixin in pairs(mixins) do
+					if type(mixin.GetAceOptionsDataTable) == "function" then
+						local t = mixin:GetAceOptionsDataTable(handler)
+						for k,v in pairs(t) do
+							if type(options.args) ~= "table" then
+								options.args = {}
+							end
+							if options.args[k] == nil then
+								options.args[k] = v
+							end
+						end
+					end
+				end
+			end
+			class = class.super
+		end
+	end
+	return options
+end
+
+function Dewdrop:OnTooltipHide()
+	if lastSetFont then
+		if lastSetFont == normalFont then
+			lastSetFont = nil
+			return
+		end
+		fillRegionTmp(GameTooltip:GetRegions())
+		for i,v in ipairs(regionTmp) do
+			if v.GetFont then
+				local font,size,outline = v:GetFont()
+				if font == lastSetFont then
+					v:SetFont(normalFont, size, outline)
+				end
+			end
+			regionTmp[i] = nil
+		end
+		lastSetFont = nil
+	end
+end
+
+local function activate(self, oldLib, oldDeactivate)
+	Dewdrop = self
+	if oldLib and oldLib.registry then
+		self.registry = oldLib.registry
+		self.onceRegistered = oldLib.onceRegistered
+	else
+		self.registry = {}
+		self.onceRegistered = {}
+
+		local WorldFrame_OnMouseDown = WorldFrame:GetScript("OnMouseDown")
+		local WorldFrame_OnMouseUp = WorldFrame:GetScript("OnMouseUp")
+		local oldX, oldY, clickTime
+		WorldFrame:SetScript("OnMouseDown", function(this, ...)
+			oldX,oldY = GetCursorPosition()
+			clickTime = GetTime()
+			if WorldFrame_OnMouseDown then
+				WorldFrame_OnMouseDown(this, ...)
+			end
+		end)
+
+		WorldFrame:SetScript("OnMouseUp", function(this, ...)
+			local x,y = GetCursorPosition()
+			if not oldX or not oldY or not x or not y or not clickTime then
+				self:Close()
+				if WorldFrame_OnMouseUp then
+					WorldFrame_OnMouseUp(this, ...)
+				end
+				return
+			end
+			local d = math.abs(x - oldX) + math.abs(y - oldY)
+			if d <= 5 and GetTime() - clickTime < 0.5 then
+				self:Close()
+			end
+			if WorldFrame_OnMouseUp then
+				WorldFrame_OnMouseUp(this, ...)
+			end
+		end)
+
+		hooksecurefunc(DropDownList1, "Show", function()
+			if levels[1] and levels[1]:IsVisible() then
+				self:Close()
+			end
+		end)
+
+		hooksecurefunc("HideDropDownMenu", function()
+			if levels[1] and levels[1]:IsVisible() then
+				self:Close()
+			end
+		end)
+
+		hooksecurefunc("CloseDropDownMenus", function()
+			if levels[1] and levels[1]:IsVisible() then
+				local stack = debugstack()
+				if not stack:find("`TargetFrame_OnHide'") then
+					self:Close()
+				end
+			end
+		end)
+	end
+	self.frame = oldLib and oldLib.frame or CreateFrame("Frame")
+	self.frame:UnregisterAllEvents()
+	self.frame:RegisterEvent("PLAYER_REGEN_ENABLED")
+	self.frame:RegisterEvent("PLAYER_REGEN_DISABLED")
+	self.frame:Hide()
+	self.frame:SetScript("OnEvent", function(this, event)
+		this:Show()
+		if event=="PLAYER_REGEN_ENABLED" then			-- track combat state for secure frame operations
+			self.combat = false
+		elseif event=="PLAYER_REGEN_DISABLED" then
+			self.combat = true
+		end
+	end)
+	self.frame:SetScript("OnUpdate", function(this)
+		this:Hide()
+		self:Refresh(1)
+	end)
+	self.hookedTooltip = true
+	if not oldLib or not oldLib.hookedTooltip then
+		local OnTooltipHide = GameTooltip:GetScript("OnHide")
+		GameTooltip:SetScript("OnHide", function(this, ...)
+			if OnTooltipHide then
+				OnTooltipHide(this, ...)
+			end
+			if type(self.OnTooltipHide) == "function" then
+				self:OnTooltipHide()
+			end
+		end)
+	end
+	levels = {}
+	buttons = {}
+
+	if oldDeactivate then
+		oldDeactivate(oldLib)
+	end
+end
+
+local function external(lib, major, instance)
+	if major == "SharedMedia-1.0" then
+		SharedMedia = instance
+	end
+end
+
+AceLibrary:Register(Dewdrop, MAJOR_VERSION, MINOR_VERSION, activate, nil, external)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/FuBar_ReActionFu/lib/LibFuBarPlugin-3.0/Changelog-LibFuBarPlugin-3.0-r63707.txt	Wed Apr 02 23:31:13 2008 +0000
@@ -0,0 +1,25 @@
+------------------------------------------------------------------------
+r63707 | arrowmaster | 2008-03-05 19:54:23 -0500 (Wed, 05 Mar 2008) | 1 line
+Changed paths:
+   M /trunk/LibFuBarPlugin-3.0/LibFuBarPlugin-3.0.lua
+
+LibFuBarPlugin-3.0: fix MINOR_VERSION to stop going negative
+------------------------------------------------------------------------
+r63318 | ckknight | 2008-03-01 12:59:56 -0500 (Sat, 01 Mar 2008) | 1 line
+Changed paths:
+   M /trunk/LibFuBarPlugin-3.0/LibFuBarPlugin-3.0.lua
+
+LibFuBarPlugin-3.0 - remove a pointless :OnEmbedProfileEnable
+------------------------------------------------------------------------
+r62709 | ckknight | 2008-02-23 21:15:11 -0500 (Sat, 23 Feb 2008) | 1 line
+Changed paths:
+   M /trunk/LibFuBarPlugin-3.0/LibFuBarPlugin-3.0.lua
+
+LibFuBarPlugin-3.0 - move around tricorner minimaps properly.
+------------------------------------------------------------------------
+r61479 | ckknight | 2008-02-15 12:18:37 -0500 (Fri, 15 Feb 2008) | 1 line
+Changed paths:
+   M /trunk/LibFuBarPlugin-3.0/LibFuBarPlugin-3.0.lua
+
+LibFuBarPlugin-3.0 - properly move around SIDE-TOP and SIDE-BOTTOM minimaps
+------------------------------------------------------------------------
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/FuBar_ReActionFu/lib/LibFuBarPlugin-3.0/LibFuBarPlugin-3.0.lua	Wed Apr 02 23:31:13 2008 +0000
@@ -0,0 +1,2145 @@
+--[[
+Name: LibFuBarPlugin-3.0
+Revision: $Rev: 63707 $
+Developed by: ckknight (ckknight@gmail.com)
+Website: http://www.wowace.com/
+Description: Plugin for FuBar.
+Dependencies: LibRock-1.0
+License: LGPL v2.1
+]]
+
+local MAJOR_VERSION = "LibFuBarPlugin-3.0"
+local MINOR_VERSION = tonumber(("$Revision: 63707 $"):match("(%d+)")) - 60000
+
+if not Rock then error(MAJOR_VERSION .. " requires LibRock-1.0") end
+
+local FuBarPlugin, oldLib = Rock:NewLibrary(MAJOR_VERSION, MINOR_VERSION)
+if not FuBarPlugin then
+	return
+end
+
+local SHOW_FUBAR_ICON = "Show FuBar icon"
+local SHOW_FUBAR_ICON_DESC = "Show the FuBar plugin's icon on the panel."
+local SHOW_FUBAR_TEXT = "Show FuBar text"
+local SHOW_FUBAR_TEXT_DESC = "Show the FuBar plugin's text on the panel."
+local SHOW_COLORED_FUBAR_TEXT = "Show colored FuBar text"
+local SHOW_COLORED_FUBAR_TEXT_DESC = "Allow the FuBar plugin to color its text on the panel."
+local DETACH_FUBAR_TOOLTIP = "Detach FuBar tooltip"
+local DETACH_FUBAR_TOOLTIP_DESC = "Detach the FuBar tooltip from the panel."
+local LOCK_FUBAR_TOOLTIP = "Lock tooltip"
+local LOCK_FUBAR_TOOLTIP_DESC = "Lock the tooltips position. When the tooltip is locked, you must use Alt to access it with your mouse."
+local POSITION_ON_FUBAR = "Position on FuBar"
+local POSITION_ON_FUBAR_DESC = "Position the FuBar plugin on the panel."
+local POSITION_LEFT = "Left"
+local POSITION_RIGHT = "Right"
+local POSITION_CENTER = "Center"
+local ATTACH_PLUGIN_TO_MINIMAP = "Attach FuBar plugin to minimap"
+local ATTACH_PLUGIN_TO_MINIMAP_DESC = "Attach the FuBar plugin to the minimap instead of the panel."
+local HIDE_FUBAR_PLUGIN = "Hide FuBar plugin"
+local HIDE_MINIMAP_BUTTON = "Hide minimap button"
+local HIDE_FUBAR_PLUGIN_DESC = "Hide the FuBar plugin from the panel or minimap, leaving the addon running."
+local OTHER = "Other"
+local CLOSE = "Close"
+local CLOSE_DESC = "Close the menu."
+
+if GetLocale() == "zhCN" then
+	SHOW_FUBAR_ICON = "显示FuBar图标"
+	SHOW_FUBAR_ICON_DESC = "在面板上显示FuBar插件的图标."
+	SHOW_FUBAR_TEXT = "显示FuBar文字"
+	SHOW_FUBAR_TEXT_DESC = "在面板上显示Fubar插件文字标题"
+	SHOW_COLORED_FUBAR_TEXT = "显示彩色文字"
+	SHOW_COLORED_FUBAR_TEXT_DESC = "允许插件显示彩色文字."
+	DETACH_FUBAR_TOOLTIP = "独立提示信息"
+	DETACH_FUBAR_TOOLTIP_DESC = "从面板上独立显示信息"
+	LOCK_FUBAR_TOOLTIP = "锁定提示信息"
+	LOCK_FUBAR_TOOLTIP_DESC = "锁定提示信息位置.当提示信息被锁定时,你必须要按Alt-鼠标方可查看."
+	POSITION_ON_FUBAR = "位置"
+	POSITION_ON_FUBAR_DESC = "FuBar插件在面板上的位置."
+	POSITION_LEFT = "居左"
+	POSITION_RIGHT = "居右"
+	POSITION_CENTER = "居中"
+	ATTACH_PLUGIN_TO_MINIMAP = "依附在小地图"
+	ATTACH_PLUGIN_TO_MINIMAP_DESC = "插件图标依附在小地图而不显示在面板上."
+	HIDE_FUBAR_PLUGIN = "隐藏FuBar插件"
+	HIDE_MINIMAP_BUTTON = "隐藏小地图按钮"
+	HIDE_FUBAR_PLUGIN_DESC = "隐藏在面板或小地图上的FuBar插件,暂定插件工作."
+	OTHER = "其他"
+	CLOSE = "关闭"
+	LOSE_DESC = "关闭目录."
+elseif GetLocale() == "zhTW" then
+	SHOW_FUBAR_ICON = "顯示圖示"
+	SHOW_FUBAR_ICON_DESC = "在面板上顯示插件圖示。"
+	SHOW_FUBAR_TEXT = "顯示文字"
+	SHOW_FUBAR_TEXT_DESC = "在面板上顯示插件文字。"
+	SHOW_COLORED_FUBAR_TEXT = "允許彩色文字"
+	SHOW_COLORED_FUBAR_TEXT_DESC = "允許插件在面板上使用彩色文字。"
+	DETACH_FUBAR_TOOLTIP = "獨立提示訊息"
+	DETACH_FUBAR_TOOLTIP_DESC = "從面板上獨立提示訊息。"
+	LOCK_FUBAR_TOOLTIP = "鎖定提示訊息"
+	LOCK_FUBAR_TOOLTIP_DESC = "鎖定提示訊息位置。當提示訊息鎖定時,需要用Alt鍵使用提示訊息的功能。"
+	POSITION_ON_FUBAR = "位置"
+	POSITION_ON_FUBAR_DESC = "插件在面板上的位置。"
+	POSITION_LEFT = "靠左"
+	POSITION_RIGHT = "靠右"
+	POSITION_CENTER = "置中"
+	ATTACH_PLUGIN_TO_MINIMAP = "依附在小地圖"
+	ATTACH_PLUGIN_TO_MINIMAP_DESC = "插件圖標依附在小地圖而不顯示在面板上。"
+	HIDE_FUBAR_PLUGIN = "隱藏插件"
+	HIDE_MINIMAP_BUTTON = "隱藏小地圖按鈕"
+	HIDE_FUBAR_PLUGIN_DESC = "在面板或小地圖上隱藏該插件,但保持執行狀態。"
+	OTHER = "其他"
+	CLOSE = "關閉"
+	CLOSE_DESC = "關閉選單。"
+elseif GetLocale() == "koKR" then
+	SHOW_FUBAR_ICON = "FuBar 아이콘 표시"
+	SHOW_FUBAR_ICON_DESC = "FuBar 패널에 플러그인 아이콘을 표시합니다."
+	SHOW_FUBAR_TEXT = "FuBar 텍스트 표시"
+	SHOW_FUBAR_TEXT_DESC = "FuBar 페널에 플러그인 텍스트를 표시합니다."
+	SHOW_COLORED_FUBAR_TEXT = "색상화된 FuBar 텍스트 표시"
+	SHOW_COLORED_FUBAR_TEXT_DESC = "패널의 FuBar 플러그인의 텍스트 색상을 허용합니다."
+	DETACH_FUBAR_TOOLTIP = "FuBar 툴팁 분리"
+	DETACH_FUBAR_TOOLTIP_DESC = "패널에서 FuBar 툴팁을 분리합니다."
+	LOCK_FUBAR_TOOLTIP = "툴팁 고정"
+	LOCK_FUBAR_TOOLTIP_DESC = "툴팁 위치를 고정시킵니다. 툴팁이 고정되어 있을때, 마우스로 접근하기 위해 Alt키를 사용하여야 합니다."
+	POSITION_ON_FUBAR = "FuBar 위치"
+	POSITION_ON_FUBAR_DESC = "패널 위의 FuBar 플러그인의 위치를 설정합니다."
+	POSITION_LEFT = "좌측"
+	POSITION_RIGHT = "우측"
+	POSITION_CENTER = "중앙"
+	ATTACH_PLUGIN_TO_MINIMAP = "FuBar 플러그인 미니맵 표시"
+	ATTACH_PLUGIN_TO_MINIMAP_DESC = "FuBar 플러그인을 패널 대신 미니맵에 표시합니다."
+	HIDE_FUBAR_PLUGIN = "FuBar 플러그인 숨김"
+	HIDE_MINIMAP_BUTTON = "미니맵 버튼 숨김"
+	HIDE_FUBAR_PLUGIN_DESC = "FuBar 플러그인을 패널이나 미니맵으로 부터 숨김니다."
+	OTHER = "기타"
+	CLOSE = "닫기"
+	CLOSE_DESC = "메뉴를 닫습니다."
+elseif GetLocale() == "frFR" then
+	SHOW_FUBAR_ICON = "Afficher l'icône FuBar"
+	SHOW_FUBAR_ICON_DESC = "Affiche l'icône du plugin FuBar sur le panneau."
+	SHOW_FUBAR_TEXT = "Afficher le texte FuBar"
+	SHOW_FUBAR_TEXT_DESC = "Affiche le texte du plugin FuBar sur le panneau."
+	SHOW_COLORED_FUBAR_TEXT = "Afficher le texte FuBar coloré"
+	SHOW_COLORED_FUBAR_TEXT_DESC = "Autorise le plugin FuBar à colorer son texte sur le panneau."
+	DETACH_FUBAR_TOOLTIP = "Détacher l'infobulle FuBar"
+	DETACH_FUBAR_TOOLTIP_DESC = "Détache l'infobulle FuBar du panneau."
+	LOCK_FUBAR_TOOLTIP = "Verrouiller l'infobulle"
+	LOCK_FUBAR_TOOLTIP_DESC = "Verrouille l'infobulle dans sa position actuelle. Quand l'infobulle est verrouillée, vous devez utiliser la touche Alt pour y interagir avec la souris."
+	POSITION_ON_FUBAR = "Position sur FuBar"
+	POSITION_ON_FUBAR_DESC = "Position du plugin FuBar sur le panneau."
+	POSITION_LEFT = "Gauche"
+	POSITION_RIGHT = "Droite"
+	POSITION_CENTER = "Centre"
+	ATTACH_PLUGIN_TO_MINIMAP = "Attacher le plugin FuBar sur la minicarte"
+	ATTACH_PLUGIN_TO_MINIMAP_DESC = "Attache le plugin FuBar sur la minicarte au lieu du panneau."
+	HIDE_FUBAR_PLUGIN = "Masquer le plugin FuBar"
+	HIDE_MINIMAP_BUTTON = "Masquer le bouton de la minicarte"
+	HIDE_FUBAR_PLUGIN_DESC = "Masque le plugin FuBar du panneau ou de la minicarte, laissant l'addon fonctionner."
+	OTHER = "Autre"
+	CLOSE = "Fermer"
+	CLOSE_DESC = "Ferme le menu."
+end
+
+-- #AUTODOC_NAMESPACE FuBarPlugin
+
+local precondition, argCheck = Rock:GetContractFunctions(MAJOR_VERSION, "precondition", "argCheck")
+local newList, del = Rock:GetRecyclingFunctions(MAJOR_VERSION, "newList", "del")
+
+FuBarPlugin.pluginToFrame = oldLib and oldLib.pluginToFrame or {}
+local pluginToFrame = FuBarPlugin.pluginToFrame
+FuBarPlugin.pluginToMinimapFrame = oldLib and oldLib.pluginToMinimapFrame or {}
+local pluginToMinimapFrame = FuBarPlugin.pluginToMinimapFrame
+FuBarPlugin.pluginToPanel = oldLib and oldLib.pluginToPanel or {}
+local pluginToPanel = FuBarPlugin.pluginToPanel
+FuBarPlugin.pluginToOptions = oldLib and oldLib.pluginToOptions or {}
+local pluginToOptions = FuBarPlugin.pluginToOptions
+FuBarPlugin.folderNames = oldLib and oldLib.folderNames or {}
+local folderNames = FuBarPlugin.folderNames
+
+local Tablet20
+local Dewdrop20
+
+FuBarPlugin.MinimapContainer = oldLib and oldLib.MinimapContainer or {}
+local MinimapContainer = FuBarPlugin.MinimapContainer
+
+local epsilon = 1e-5
+
+-- #AUTODOC_NAMESPACE FuBarPlugin
+
+--[[---------------------------------------------------------------------------
+Notes:
+	*Set metadata about a certain plugin.
+	; tooltipType : string -
+	: "GameTooltip"
+	:: Use Blizzard's GameTooltip. (default if not given)
+	: "Tablet-2.0"
+	:: Use Tablet-2.0.
+	: "Custom"
+	:: LibFuBarPlugin-3.0 will not provide any extra mechanisms, all done manually.
+	; configType : string -
+	: "LibRockConfig-1.0"
+	:: Use LibRockConfig-1.0 to show configuration. (default if not given)
+	: "Dewdrop-2.0"
+	:: Use Dewdrop-2.0.
+	; hasNoText : boolean - If set to true, then it will be a text-less frame.
+	; iconPath : string - the path of the icon to show.
+	; hasNoColor : boolean - If set to true, then it is assumed that no color will be in the text (and thus not show the menu item)
+	; cannotHideText : boolean - If set to true, then the menu item to hide text will not be shown.
+	; overrideMenu : boolean - If set to true, then the menu will not show any of the standard menu items
+	; hideMenuTitle : boolean - If set to true, the plugins name will not be added to the top of the menu as a header.
+	; defaultPosition : string -
+	: "LEFT"
+	::show on the left. (default if not given)
+	: "CENTER"
+	::show in the center.
+	: "RIGHT"
+	::show on the right.
+	: "MINIMAP"
+	::show on the minimap.
+	; defaultMinimapPosition : number - Angle on the minimap, in degrees. [0, 360)
+	; clickableTooltip : boolean - Whether you can drag your mouse onto the tooltip and click a line
+	; tooltipHiddenWhenEmpty : boolean - Whether the detached tooltip is hidden when it is empty.
+	; cannotDetachTooltip : boolean - Whether the tooltip cannot be detached from the plugin text.
+	::Normally, a tooltip can detach (if using Tablet-2.0). This should be set if there is no relevant data in the tooltip.
+	; independentProfile : boolean - If set to true, then the profile setting will not be stripped from .OnMenuRequest, and FuBar will not set the plugin's profile when it changes.
+	::non-FuBar-centric plugins should set this to true.
+Arguments:
+	string - the key to set
+	value - the value to set said key to.
+Example:
+	self:SetFuBarOption('tooltipType', "Tablet-2.0")
+-----------------------------------------------------------------------------]]
+function FuBarPlugin:SetFuBarOption(key, value)
+	local pluginToOptions_self = pluginToOptions[self]
+	if not pluginToOptions_self then
+		pluginToOptions_self = {}
+		pluginToOptions[self] = pluginToOptions_self
+	end
+
+	pluginToOptions_self[key] = value
+
+	if key == 'tooltipType' then
+		if value == "Tablet-2.0" then
+			Tablet20 = Rock("Tablet-2.0", false, true)
+			if not Tablet20 then
+				error(("Cannot specify %q = %q if %q is not loaded."):format(key, value, value), 2)
+			end
+		end
+	end
+	if key == 'configType' then
+		if value == "Dewdrop-2.0" then
+			Dewdrop20 = Rock("Dewdrop-2.0", false, true)
+			if not Dewdrop20 then
+				error(("Cannot specify %q = %q if %q is not loaded."):format(key, value, value), 2)
+			end
+		end
+	end
+end
+precondition(FuBarPlugin, 'SetFuBarOption', function(self, key, value)
+	argCheck(self, 1, "table")
+	argCheck(key, 2, "string")
+	argCheck(value, 3, "string", "number", "boolean")
+
+	if pluginToOptions[self] and pluginToOptions[self][key] ~= nil then
+		error(("Bad argument #2 to `SetFuBarOption'. Cannot specify %q more than once."):format(key), 3)
+	end
+end)
+
+local function getPluginOption(object, key, default)
+	local pluginToOptions_object = pluginToOptions[object]
+	if pluginToOptions_object == nil then
+		return default
+	end
+	local value = pluginToOptions_object[key]
+	if value == nil then
+		return default
+	end
+	return value
+end
+
+local good = nil
+local function CheckFuBar()
+	if not good then
+		if FuBar then
+			local version = FuBar.version
+			if type(version) == "string" then
+			 	local num = version:match("^(%d+%.?%d*)")
+				if num then
+					num = tonumber(num)
+					good = num >= 3
+				end
+			end
+		end
+	end
+	return good
+end
+
+--[[---------------------------------------------------------------------------
+Returns:
+	string - the localized name of the plugin, not including the "FuBar - " part.
+Example
+	local title = self:GetTitle()
+-----------------------------------------------------------------------------]]
+function FuBarPlugin:GetTitle()
+	local name = self.title or self.name
+	if type(name) ~= "string" then
+		error("You must provide self.title or self.name", 2)
+	end
+	local title = name:match("[Ff][Uu][Bb][Aa][Rr]%s*%-%s*(.-)%s*$") or name
+	return title:gsub("|c%x%x%x%x%x%x%x%x", ""):gsub("|r", "")
+end
+
+--[[---------------------------------------------------------------------------
+Returns:
+	string - name of the plugin.
+Notes:
+	This is here for FuBar core to communicate properly.
+Example:
+	local name = self:GetName()
+-----------------------------------------------------------------------------]]
+function FuBarPlugin:GetName()
+	return self.name
+end
+
+--[[---------------------------------------------------------------------------
+Returns:
+	string - category of the plugin.
+Notes:
+	This is here for FuBar core to communicate properly.
+Example:
+	local category = self:GetCategory()
+-----------------------------------------------------------------------------]]
+function FuBarPlugin:GetCategory()
+	return self.category or OTHER
+end
+
+--[[---------------------------------------------------------------------------
+Returns:
+	frame - frame for the plugin.
+Notes:
+	This is here for FuBar core to communicate properly.
+Example:
+	local frame = self:GetFrame()
+-----------------------------------------------------------------------------]]
+function FuBarPlugin:GetFrame()
+	return pluginToFrame[self]
+end
+
+--[[---------------------------------------------------------------------------
+Returns:
+	object - panel for the plugin.
+Notes:
+	This is here for FuBar core to communicate properly.
+Example:
+	local panel = self:GetPanel()
+-----------------------------------------------------------------------------]]
+function FuBarPlugin:GetPanel()
+	return pluginToPanel[self]
+end
+
+local function getLazyDatabaseValueDefault(object, value, ...)
+	local object_db = object.db
+	if type(object_db) ~= "table" then
+		return value
+	end
+	local current = object_db.profile
+	for i = 1, select('#', ...) do
+		-- traverse through, make sure tables exist.
+		if type(current) ~= "table" then
+			return value
+		end
+		current = current[(select(i, ...))]
+	end
+	if current == nil then
+		return value
+	else
+		return current
+	end
+end
+
+local function getLazyDatabaseValue(object, ...)
+	return getLazyDatabaseValueDefault(object, nil, ...)
+end
+
+local function setLazyDatabaseValue(object, value, ...)
+	local object_db = object.db
+	if type(object_db) ~= "table" then
+		return nil
+	end
+	local current = object_db.profile
+	if type(current) ~= "table" then
+		return nil
+	end
+	local n = select('#', ...)
+	for i = 1, n-1 do
+		-- traverse through, create tables if necessary.
+		local nextOne = current[(select(i, ...))]
+		if type(nextOne) ~= "table" then
+			if nextOne ~= nil then
+				return nil
+			end
+			nextOne = {}
+			current[(select(i, ...))] = nextOne
+		end
+		current = nextOne
+	end
+	current[select(n, ...)] = value
+	return true
+end
+
+--[[---------------------------------------------------------------------------
+Returns:
+	boolean - whether the text has color applied.
+Example:
+	local colored = self:IsFuBarTextColored()
+-----------------------------------------------------------------------------]]
+function FuBarPlugin:IsFuBarTextColored()
+	return not getLazyDatabaseValue(self, 'uncolored')
+end
+
+--[[---------------------------------------------------------------------------
+Notes:
+	Toggles whether the text has color applied
+Example:
+	self:ToggleTextColored()
+-----------------------------------------------------------------------------]]
+function FuBarPlugin:ToggleFuBarTextColored()
+	if not setLazyDatabaseValue(self, not getLazyDatabaseValue(self, 'uncolored') or nil, 'uncolored') then
+		error(("%s: Cannot change text color if self.db is not available."):format(self:GetTitle()), 2)
+	end
+	self:UpdateFuBarText()
+end
+
+--[[---------------------------------------------------------------------------
+Returns:
+	boolean - whether the plugin is attached to the minimap.
+Example:
+	local attached = self:IsMinimapAttached()
+-----------------------------------------------------------------------------]]
+function FuBarPlugin:IsFuBarMinimapAttached()
+	if not CheckFuBar() then
+		return true
+	end
+	return pluginToPanel[self] == MinimapContainer
+end
+
+--[[---------------------------------------------------------------------------
+Notes:
+	Toggles whether the plugin is attached to the minimap.
+Example:
+	self:ToggleMinimapAttached()
+-----------------------------------------------------------------------------]]
+function FuBarPlugin:ToggleFuBarMinimapAttached()
+	if CheckFuBar() and not getPluginOption(self, 'cannotAttachToMinimap', false) then
+		local panel = pluginToPanel[self]
+		local value = panel == MinimapContainer
+		if value then
+			panel:RemovePlugin(self)
+			local defaultPosition = getPluginOption(self, 'defaultPosition', "LEFT")
+			FuBar:GetPanel(1):AddPlugin(self, nil, defaultPosition == "MINIMAP" and "LEFT" or defaultPosition)
+		else
+			if panel then
+				panel:RemovePlugin(self)
+			end
+			MinimapContainer:AddPlugin(self)
+		end
+	end
+end
+
+--[[---------------------------------------------------------------------------
+Notes:
+	Calls :UpdateFuBarText() and :UpdateFuBarTooltip(), in that order.
+Example:
+	self:UpdateFuBarPlugin()
+-----------------------------------------------------------------------------]]
+function FuBarPlugin:UpdateFuBarPlugin()
+	self:UpdateFuBarText()
+	self:UpdateFuBarTooltip()
+end
+
+--[[---------------------------------------------------------------------------
+Notes:
+	* Calls :OnUpdateFuBarText() if it is available and the plugin is not disabled.
+	* It is expected to update the icon in :OnUpdateFuBarText as well as text.
+Example:
+	self:UpdateFuBarText()
+-----------------------------------------------------------------------------]]
+function FuBarPlugin:UpdateFuBarText()
+	if type(self.OnUpdateFuBarText) == "function" then
+		if not self:IsDisabled() then
+			self:OnUpdateFuBarText()
+		end
+	elseif self:IsFuBarTextShown() then
+		self:SetFuBarText(self:GetTitle())
+	end
+end
+
+local function Tablet20_point(frame)
+	if frame:GetTop() > GetScreenHeight() / 2 then
+		local x = frame:GetCenter()
+		if x < GetScreenWidth() / 3 then
+			return "TOPLEFT", "BOTTOMLEFT"
+		elseif x < GetScreenWidth() * 2 / 3 then
+			return "TOP", "BOTTOM"
+		else
+			return "TOPRIGHT", "BOTTOMRIGHT"
+		end
+	else
+		local x = frame:GetCenter()
+		if x < GetScreenWidth() / 3 then
+			return "BOTTOMLEFT", "TOPLEFT"
+		elseif x < GetScreenWidth() * 2 / 3 then
+			return "BOTTOM", "TOP"
+		else
+			return "BOTTOMRIGHT", "TOPRIGHT"
+		end
+	end
+end
+
+local function RegisterTablet20(self)
+	local frame = pluginToFrame[self]
+	if not Tablet20:IsRegistered(frame) then
+		local db = getLazyDatabaseValue(self)
+		if db and not db.detachedTooltip then
+			db.detachedTooltip = {}
+		end
+		Tablet20:Register(frame,
+			'children', function()
+				Tablet20:SetTitle(self:GetTitle())
+				if type(self.OnUpdateFuBarTooltip) == "function" then
+					if not self:IsDisabled() then
+						self:OnUpdateFuBarTooltip()
+					end
+				end
+			end,
+			'clickable', getPluginOption(self, 'clickableTooltip', false),
+			'data', CheckFuBar() and FuBar.db.profile.tooltip or db and db.detachedTooltip or {},
+			'detachedData', db and db.detachedTooltip or {},
+			'point', Tablet20_point,
+			'menu', self.OnMenuRequest and function(level, value, valueN_1, valueN_2, valueN_3, valueN_4)
+				if level == 1 then
+					local name = tostring(self)
+					if not name:find('^table:') then
+						name = name:gsub("|c%x%x%x%x%x%x%x%x(.-)|r", "%1")
+						Rock("Dewdrop-2.0"):AddLine(
+							'text', name,
+							'isTitle', true
+						)
+					end
+				end
+				if type(self.OnMenuRequest) == "function" then
+					self:OnMenuRequest(level, value, true, valueN_1, valueN_2, valueN_3, valueN_4)
+				elseif type(self.OnMenuRequest) == "table" then
+					Rock("Dewdrop-2.0"):FeedAceOptionsTable(self.OnMenuRequest)
+				end
+			end,
+			'hideWhenEmpty', getPluginOption(self, 'tooltipHiddenWhenEmpty', false)
+		)
+		local func = pluginToFrame[self]:GetScript("OnEnter")
+		frame:SetScript("OnEnter", function(this, ...)
+			-- HACK
+			func(this, ...)
+
+			if FuBar and FuBar.IsHidingTooltipsInCombat and FuBar:IsHidingTooltipsInCombat() and InCombatLockdown() then
+				if Tablet20:IsAttached(this) then
+					Tablet20:Close(this)
+				end
+			end
+		end)
+	end
+end
+
+--[[---------------------------------------------------------------------------
+Notes:
+	Calls :OnUpdateFuBarTooltip() if it is available, the plugin is not disabled, and the tooltip is shown.
+Example:
+	self:UpdateFuBarTooltip()
+-----------------------------------------------------------------------------]]
+function FuBarPlugin:UpdateFuBarTooltip()
+	local tooltipType = getPluginOption(self, 'tooltipType', "GameTooltip")
+
+	if tooltipType == "GameTooltip" then
+		local frame = self:IsFuBarMinimapAttached() and pluginToMinimapFrame[self] or pluginToFrame[self]
+		if not GameTooltip:IsOwned(frame) then
+			return
+		end
+		GameTooltip:Hide()
+
+		local anchor
+		if frame:GetTop() > GetScreenHeight() / 2 then
+			local x = frame:GetCenter()
+			if x < GetScreenWidth() / 2 then
+				anchor = "ANCHOR_BOTTOMRIGHT"
+			else
+				anchor = "ANCHOR_BOTTOMLEFT"
+			end
+		else
+			local x = frame:GetCenter()
+			if x < GetScreenWidth() / 2 then
+				anchor = "ANCHOR_TOPLEFT"
+			else
+				anchor = "ANCHOR_TOPRIGHT"
+			end
+		end
+		GameTooltip:SetOwner(frame, anchor)
+		if type(self.OnUpdateFuBarTooltip) == "function" and not self:IsDisabled() then
+			self:OnUpdateFuBarTooltip()
+		end
+		GameTooltip:Show()
+		return
+	elseif tooltipType == "Custom" then
+		if type(self.OnUpdateFuBarTooltip) == "function" and not self:IsDisabled() then
+			self:OnUpdateFuBarTooltip()
+		end
+		return
+	elseif tooltipType == "Tablet-2.0" then
+		RegisterTablet20(self)
+		if self:IsFuBarMinimapAttached() and not self:IsFuBarTooltipDetached() and pluginToMinimapFrame[self] then
+			Tablet20:Refresh(pluginToMinimapFrame[self])
+		else
+			Tablet20:Refresh(pluginToFrame[self])
+		end
+	elseif tooltipType == "None" then
+		return
+	else
+		error(("Unknown %s option for %q: %q"):format(MAJOR_VERSION, 'tooltipType', tostring(tooltipType)), 2)
+	end
+end
+
+--[[---------------------------------------------------------------------------
+Notes:
+	Shows the plugin, enables the plugin if previously disabled, and calls :UpdateFuBarPlugin().
+Example:
+	self:Show()
+-----------------------------------------------------------------------------]]
+function FuBarPlugin:Show(panelId)
+	if pluginToFrame[self]:IsShown() or (pluginToMinimapFrame[self] and pluginToMinimapFrame[self]:IsShown()) then
+		return
+	end
+	if panelId ~= false then
+		setLazyDatabaseValue(self, nil, 'hidden')
+	end
+	if self.IsActive and not self:IsActive() then
+		self.panelIdTmp = panelId
+		self:ToggleActive()
+		self.panelIdTmp = nil
+		setLazyDatabaseValue(self, nil, 'disabled')
+	elseif not getLazyDatabaseValue(self, 'hidden') then
+		if panelId == 0 or not CheckFuBar() then
+			MinimapContainer:AddPlugin(self)
+		else
+			FuBar:ShowPlugin(self, panelId or self.panelIdTmp)
+		end
+		if not getPluginOption(self, 'userDefinedFrame', false) then
+			if not self:IsFuBarTextShown() then
+				local text = pluginToFrame[self].text
+				text:SetText("")
+				text:SetWidth(epsilon)
+				text:Hide()
+			end
+			if not self:IsFuBarIconShown() then
+				local icon = pluginToFrame[self].icon
+				icon:SetWidth(epsilon)
+				icon:Hide()
+			end
+		end
+		self:UpdateFuBarPlugin()
+	end
+end
+
+--[[---------------------------------------------------------------------------
+Notes:
+	Hides the plugin, disables the plugin if cannot hide without standby.
+Arguments:
+	[optional] boolean - internal variable. Do not set this.
+Example:
+	self:Hide()
+-----------------------------------------------------------------------------]]
+function FuBarPlugin:Hide(check)
+	if not pluginToFrame[self]:IsShown() and (not pluginToMinimapFrame[self] or not pluginToMinimapFrame[self]:IsShown()) then
+		return
+	end
+	local hideWithoutStandby = getPluginOption(self, 'hideWithoutStandby', false)
+	if hideWithoutStandby and check ~= false then
+		setLazyDatabaseValue(self, true, 'hidden')
+	end
+	if not hideWithoutStandby then
+		if getPluginOption(self, 'tooltipType', "GameTooltip") == "Tablet-2.0" and not getPluginOption(self, 'cannotDetachTooltip', false) and self:IsFuBarTooltipDetached() and getLazyDatabaseValue(self, 'detachedTooltip', 'detached') then
+			self:ReattachTooltip()
+			setLazyDatabaseValue(self, true, 'detachedTooltip', 'detached')
+		end
+		if self.IsActive and self:IsActive() and self.ToggleActive and (not CheckFuBar() or not FuBar:IsChangingProfile()) then
+			self:ToggleActive()
+		end
+	end
+	if pluginToPanel[self] then
+		pluginToPanel[self]:RemovePlugin(self)
+	end
+	pluginToFrame[self]:Hide()
+	if pluginToMinimapFrame[self] then
+		pluginToMinimapFrame[self]:Hide()
+	end
+end
+
+--[[---------------------------------------------------------------------------
+Notes:
+	Sets the path to the icon for the plugin.
+Arguments:
+	string or nil - The path to the icon. If nil, then no icon.
+Example:
+	self:SetFuBarIcon("Interface\\AddOns\\MyAddon\\otherIcon")
+-----------------------------------------------------------------------------]]
+function FuBarPlugin:SetFuBarIcon(path)
+	if not path then
+		return
+	end
+	if not pluginToFrame[self] or not pluginToFrame[self].icon then
+		return
+	end
+	if path:match([[^Interface\Icons\]]) then
+		pluginToFrame[self].icon:SetTexCoord(0.05, 0.95, 0.05, 0.95)
+	else
+		pluginToFrame[self].icon:SetTexCoord(0, 1, 0, 1)
+	end
+	pluginToFrame[self].icon:SetTexture(path)
+	if pluginToMinimapFrame[self] and pluginToMinimapFrame[self].icon then
+		if path:match([[^Interface\Icons\]]) then
+			pluginToMinimapFrame[self].icon:SetTexCoord(0.05, 0.95, 0.05, 0.95)
+		else
+			pluginToMinimapFrame[self].icon:SetTexCoord(0, 1, 0, 1)
+		end
+		pluginToMinimapFrame[self].icon:SetTexture(path)
+	end
+end
+precondition(FuBarPlugin, 'SetFuBarIcon', function(self, path)
+	if not path then
+		return
+	end
+	argCheck(path, 2, "string", "nil")
+	if not getPluginOption(self, 'iconPath', false) then
+		error(("%s: Cannot set icon unless 'iconPath' is set."):format(self:GetTitle()), 3)
+	end
+end)
+
+--[[---------------------------------------------------------------------------
+Returns:
+	string or nil - The path to the icon for the plugin. If nil, then no icon.
+Example:
+	local path = self:GetFuBarIcon()
+-----------------------------------------------------------------------------]]
+function FuBarPlugin:GetFuBarIcon()
+	if getPluginOption(self, 'iconPath', false) then
+		return pluginToFrame[self] and pluginToFrame[self].icon and pluginToFrame[self].icon:GetTexture()
+	end
+end
+
+--[[---------------------------------------------------------------------------
+Notes:
+	Checks the current width of the icon and text, then updates frame to expand/shrink to it if necessary.
+Arguments:
+	[optional] boolean - if true, Shrink/expand no matter what, otherwise if the width is less than 8 pixels smaller, don't shrink.
+Example:
+	self:CheckWidth(true)
+-----------------------------------------------------------------------------]]
+function FuBarPlugin:CheckWidth(force)
+	local frame = pluginToFrame[self]
+	if not frame then
+		return
+	end
+	local icon = frame.icon
+	local text = frame.text
+	if (not icon or not icon:IsShown()) and (not text or not text:IsShown()) then
+		return
+	end
+
+	local db = getLazyDatabaseValue(self)
+
+	if (db and not self:IsFuBarIconShown()) or not getPluginOption(self, 'iconPath', false) then
+		icon:SetWidth(epsilon)
+	end
+	local width
+	if not getPluginOption(self, 'hasNoText', false) then
+		text:SetHeight(0)
+		text:SetWidth(500)
+		width = text:GetStringWidth() + 1
+		text:SetWidth(width)
+		text:SetHeight(text:GetHeight())
+	end
+	local panel = pluginToPanel[self]
+	if getPluginOption(self, 'hasNoText', false) or not text:IsShown() then
+		frame:SetWidth(icon:GetWidth())
+		if panel and panel:GetPluginSide(self) == "CENTER" then
+			panel:UpdateCenteredPosition()
+		end
+	elseif force or not frame.textWidth or frame.textWidth < width or frame.textWidth - 8 > width then
+		frame.textWidth = width
+		text:SetWidth(width)
+		if icon and icon:IsShown() then
+			frame:SetWidth(width + icon:GetWidth())
+		else
+			frame:SetWidth(width)
+		end
+		if panel and panel:GetPluginSide(self) == "CENTER" then
+			panel:UpdateCenteredPosition()
+		end
+	end
+end
+precondition(FuBarPlugin, 'CheckWidth', function(self, force)
+	argCheck(force, 2, "boolean", "nil")
+end)
+
+--[[---------------------------------------------------------------------------
+Notes:
+	Sets the text of the plugin. Should only be called from within :OnFuBarUpdateText()
+Arguments:
+	string - text to set the plugin to. If not given, set to title.
+Example:
+	myAddon.OnFuBarUpdateText = function(self)
+		self:SetFuBarText("Hello")
+	fend
+-----------------------------------------------------------------------------]]
+function FuBarPlugin:SetFuBarText(text)
+	local frame = pluginToFrame[self]
+	if not frame or not frame.text then
+		return
+	end
+	if text == "" then
+		if getPluginOption(self, 'iconPath', false) then
+			self:ShowFuBarIcon()
+		else
+			text = self:GetTitle()
+		end
+	end
+	if not self:IsFuBarTextColored() then
+		text = text:gsub("|c%x%x%x%x%x%x%x%x", ""):gsub("|r", "")
+	end
+	frame.text:SetText(text)
+	self:CheckWidth()
+end
+precondition(FuBarPlugin, 'SetFuBarText', function(self, text)
+	local frame = pluginToFrame[self]
+	if not frame or not frame.text then
+		return
+	end
+	if getPluginOption(self, 'hasNoText', false) then
+		error(("%s: Cannot set text if 'hasNoText' has been set."):format(self:GetTitle()), 3)
+	end
+	argCheck(text, 2, "string", "number")
+end)
+
+--[[---------------------------------------------------------------------------
+Returns:
+	string - The current text of the plugin.
+Example:
+	local text = self:GetFuBarText()
+-----------------------------------------------------------------------------]]
+function FuBarPlugin:GetFuBarText()
+	local frame = pluginToFrame[self]
+	if not frame or not frame.text then
+		error(("%s: Cannot get text without a text frame."):format(self:GetTitle()), 2)
+	end
+	if not getPluginOption(self, 'hasNoText', false) then
+		return frame.text:GetText() or ""
+	end
+end
+
+--[[---------------------------------------------------------------------------
+Returns:
+	boolean - whether the icon for the plugin is showing.
+Example:
+	local isIconShowing = self:IsFuBarIconShown()
+-----------------------------------------------------------------------------]]
+function FuBarPlugin:IsFuBarIconShown()
+	if not getPluginOption(self, 'iconPath', false) then
+		return false
+	elseif getPluginOption(self, 'hasNoText', false) then
+		return true
+	end
+	return not not getLazyDatabaseValueDefault(self, true, 'showIcon')
+end
+
+--[[---------------------------------------------------------------------------
+Notes:
+	Toggles whether the icon for the plugin is showing.
+Example:
+	self:ToggleFuBarIconShown()
+-----------------------------------------------------------------------------]]
+function FuBarPlugin:ToggleFuBarIconShown()
+	local frame = pluginToFrame[self]
+	local icon = frame and frame.icon
+	local text = frame and frame.text
+	if not icon then
+		error(("%s: Cannot toggle icon without an icon frame."):format(self:GetTitle()), 2)
+	elseif not text then
+		error(("%s: Cannot toggle icon without a text frame."):format(self:GetTitle()), 2)
+	elseif not getPluginOption(self, 'iconPath', false) then
+		error(("%s: Cannot show icon unless 'iconPath' is set."):format(self:GetTitle()), 2)
+	elseif getPluginOption(self, 'hasNoText', false) then
+		error(("%s: Cannot show icon if 'hasNoText' is set."):format(self:GetTitle()), 2)
+	elseif not getLazyDatabaseValue(self) then
+		error(("%s: Cannot hide icon if self.db is not available."):format(self:GetTitle()), 2)
+	end
+	local value = not self:IsFuBarIconShown()
+	setLazyDatabaseValue(self, value, 'showIcon')
+	if value then
+		if not self:IsFuBarTextShown() and text:IsShown() and text:GetText() == self:GetTitle() then
+			text:Hide()
+			text:SetText("")
+		end
+		icon:Show()
+		icon:SetWidth(pluginToFrame[self].icon:GetHeight())
+		self:UpdateFuBarText()
+	else
+		if not text:IsShown() or not text:GetText() or text:GetText() == "" then
+			text:Show()
+			text:SetText(self:GetTitle())
+		end
+		icon:Hide()
+		icon:SetWidth(epsilon)
+	end
+	self:CheckWidth(true)
+	return value
+end
+
+--[[---------------------------------------------------------------------------
+Notes:
+	Shows the icon of the plugin if hidden.
+Example:
+	self:ShowFuBarIcon()
+-----------------------------------------------------------------------------]]
+function FuBarPlugin:ShowFuBarIcon()
+	if not self:IsFuBarIconShown() then
+		self:ToggleFuBarIconShown()
+	end
+end
+
+--[[---------------------------------------------------------------------------
+Notes:
+	Hides the icon of the plugin if shown.
+Example:
+	self:HideFuBarIcon()
+-----------------------------------------------------------------------------]]
+function FuBarPlugin:HideFuBarIcon()
+	if self:IsFuBarIconShown() then
+		self:ToggleFuBarIconShown()
+	end
+end
+
+--[[---------------------------------------------------------------------------
+Returns:
+	boolean - whether the text for the plugin is showing.
+Example:
+	local isTextShowing = self:IsFuBarTextShown()
+-----------------------------------------------------------------------------]]
+function FuBarPlugin:IsFuBarTextShown()
+	if getPluginOption(self, 'hasNoText', false) then
+		return false
+	elseif not getPluginOption(self, 'iconPath', false) then
+		return true
+	end
+	return not not getLazyDatabaseValueDefault(self, true, 'showText')
+end
+
+--[[---------------------------------------------------------------------------
+Notes:
+	Toggles whether the text for the plugin is showing.
+Example:
+	self:ToggleFuBarTextShown()
+-----------------------------------------------------------------------------]]
+function FuBarPlugin:ToggleFuBarTextShown()
+	local frame = pluginToFrame[self]
+	local icon = frame and frame.icon
+	local text = frame and frame.text
+	if not icon then
+		error(("%s: Cannot toggle text without an icon frame."):format(self:GetTitle()), 2)
+	elseif not text then
+		error(("%s: Cannot toggle text without a text frame."):format(self:GetTitle()), 2)
+	elseif getPluginOption(self, 'cannotHideText', false) then
+		error(("%s: Cannot toggle text if 'cannotHideText' is set."):format(self:GetTitle()), 2)
+	elseif not getPluginOption(self, 'iconPath', false) then
+		error(("%s: Cannot toggle text unless 'iconPath' is set."):format(self:GetTitle()), 2)
+	elseif getPluginOption(self, 'hasNoText', false) then
+		error(("%s: Cannot toggle text if 'hasNoText' is set."):format(self:GetTitle()), 2)
+	elseif not getLazyDatabaseValue(self) then
+		error(("%s: Cannot toggle text if self.db is not available."):format(self:GetTitle()), 2)
+	end
+	local value = not self:IsFuBarTextShown()
+	setLazyDatabaseValue(self, value, 'showText')
+	if value then
+		text:Show()
+		self:UpdateFuBarText()
+	else
+		text:SetText("")
+		text:SetWidth(epsilon)
+		text:Hide()
+		self:ShowFuBarIcon()
+	end
+	self:CheckWidth(true)
+	return value
+end
+
+--[[---------------------------------------------------------------------------
+Notes:
+	Shows the text of the plugin if hidden.
+Example:
+	self:ShowFuBarText()
+-----------------------------------------------------------------------------]]
+function FuBarPlugin:ShowFuBarText()
+	if not self:IsFuBarTextShown() then
+		self:ToggleFuBarTextShown()
+	end
+end
+
+--[[---------------------------------------------------------------------------
+Notes:
+	Hides the text of the plugin if shown.
+Example:
+	self:HideFuBarText()
+-----------------------------------------------------------------------------]]
+function FuBarPlugin:HideFuBarText()
+	if self:IsFuBarTextShown() then
+		self:ToggleFuBarTextShown()
+	end
+end
+
+--[[---------------------------------------------------------------------------
+Returns:
+	string - default position of the plugin.
+Notes:
+	This is here for FuBar core to communicate properly.
+Example:
+	local pos = self:GetDefaultPosition()
+-----------------------------------------------------------------------------]]
+function FuBarPlugin:GetDefaultPosition()
+	return getPluginOption(self, 'defaultPosition', "LEFT")
+end
+
+--[[---------------------------------------------------------------------------
+Returns:
+	boolean - Whether the tooltip is detached.
+Example:
+	local detached = self:IsFuBarTooltipDetached()
+-----------------------------------------------------------------------------]]
+function FuBarPlugin:IsFuBarTooltipDetached()
+	local tooltipType = getPluginOption(self, 'tooltipType', "GameTooltip")
+	if tooltipType ~= "Tablet-2.0" then
+		return
+	end
+
+	RegisterTablet20(self)
+	return not Tablet20:IsAttached(pluginToFrame[self])
+end
+
+--[[---------------------------------------------------------------------------
+Notes:
+	Toggles whether the tooltip is detached.
+Example:
+	self:ToggleFuBarTooltipDetached()
+-----------------------------------------------------------------------------]]
+function FuBarPlugin:ToggleFuBarTooltipDetached()
+	local tooltipType = getPluginOption(self, 'tooltipType', "GameTooltip")
+	if tooltipType ~= "Tablet-2.0" then
+		return
+	end
+
+	RegisterTablet20(self)
+	if Tablet20:IsAttached(pluginToFrame[self]) then
+		Tablet20:Detach(pluginToFrame[self])
+	else
+		Tablet20:Attach(pluginToFrame[self])
+	end
+end
+
+--[[---------------------------------------------------------------------------
+Notes:
+	* Detaches the tooltip from the plugin.
+	* This does nothing if already detached.
+Example:
+	self:DetachFuBarTooltip()
+-----------------------------------------------------------------------------]]
+function FuBarPlugin:DetachFuBarTooltip()
+	if not self:IsFuBarTooltipDetached() then
+		self:ToggleFuBarTooltipDetached()
+	end
+end
+
+--[[---------------------------------------------------------------------------
+Notes:
+	Reattaches the tooltip to the plugin.
+	This does nothing if already attached.
+Example:
+	self:ReattachFuBarTooltip()
+-----------------------------------------------------------------------------]]
+function FuBarPlugin:ReattachFuBarTooltip()
+	if self:IsFuBarTooltipDetached() then
+		self:ToggleFuBarTooltipDetached()
+	end
+end
+
+local function IsCorrectPanel(panel)
+	if type(panel) ~= "table" then
+		return false
+	elseif type(panel.AddPlugin) ~= "function" then
+		return false
+	elseif type(panel.RemovePlugin) ~= "function" then
+		return false
+	elseif type(panel.GetNumPlugins) ~= "function" then
+		return false
+	elseif type(panel:GetNumPlugins()) ~= "number" then
+		return false
+	elseif type(panel.GetPlugin) ~= "function" then
+		return false
+	elseif type(panel.HasPlugin) ~= "function" then
+		return false
+	elseif type(panel.GetPluginSide) ~= "function" then
+		return false
+	end
+	return true
+end
+
+-- #NODOC
+-- this is used internally by FuBar
+function FuBarPlugin:SetPanel(panel)
+	pluginToPanel[self] = panel
+end
+precondition(FuBarPlugin, 'SetPanel', function(self, panel)
+	argCheck(panel, 2, "table", "nil")
+	if panel and not IsCorrectPanel(panel) then
+		error("Bad argument #2 to `SetPanel'. Panel does not have the correct API.", 3)
+	end
+end)
+
+-- #NODOC
+-- this is used internally by FuBar
+function FuBarPlugin:SetFontSize(size)
+	if getPluginOption(self, 'userDefinedFrame', false) then
+		error(("%sYou must provide a :SetFontSize(size) method if you have 'userDefinedFrame' set."):format(self.name and self.name .. ": " or ""), 2)
+	end
+	if getPluginOption(self, 'iconPath', false) then
+		local frame = pluginToFrame[self]
+		local icon = frame and frame.icon
+		if not icon then
+			error(("%sno icon frame found."):format(self.name and self.name .. ": " or ""), 2)
+		end
+		icon:SetWidth(size + 3)
+		icon:SetHeight(size + 3)
+	end
+	if not getPluginOption(self, 'hasNoText', false) then
+		local frame = pluginToFrame[self]
+		local text = frame and frame.text
+		if not text then
+			error(("%sno text frame found."):format(self.name and self.name .. ": " or ""), 2)
+		end
+		local font, _, flags = text:GetFont()
+		text:SetFont(font, size, flags)
+	end
+	self:CheckWidth()
+end
+
+local function IsLoadOnDemand(plugin)
+	return IsAddOnLoadOnDemand(folderNames[plugin] or "")
+end
+
+-- #NODOC
+-- this is used internally by FuBar.
+function FuBarPlugin:IsDisabled()
+	return type(self.IsActive) == "function" and not self:IsActive() or false
+end
+
+function FuBarPlugin:OnEmbed(target)
+	local folder = Rock.addonToFolder[target]
+	if not folder then
+		for i = 6, 3, -1 do
+			folder = debugstack(i, 1, 0):match([[\AddOns\(.*)\]])
+			if folder then
+				break
+			end
+		end
+	end
+	folderNames[target] = folder
+end
+
+local frame_OnClick, frame_OnDoubleClick, frame_OnMouseDown, frame_OnMouseUp, frame_OnReceiveDrag, frame_OnEnter, frame_OnLeave
+--[[---------------------------------------------------------------------------
+Arguments:
+	[optional] string - name of the frame
+Returns:
+	frame - a frame with the basic scripts to be considered a plugin frame.
+Example:
+	MyPlugin.frame = MyPlugin:CreateBasicPluginFrame("FuBar_MyPluginFrame")
+-----------------------------------------------------------------------------]]
+function FuBarPlugin:CreateBasicPluginFrame(name)
+	local frame = CreateFrame("Button", name, UIParent)
+	frame:SetFrameStrata("HIGH")
+	frame:SetFrameLevel(7)
+	frame:EnableMouse(true)
+	frame:EnableMouseWheel(true)
+	frame:SetMovable(true)
+	frame:SetWidth(150)
+	frame:SetHeight(24)
+	frame:SetPoint("CENTER", UIParent, "CENTER")
+	frame.self = self
+	if not frame_OnEnter then
+		function frame_OnEnter(this)
+			local self = this.self
+			local tooltipType = getPluginOption(self, 'tooltipType', "GameTooltip")
+			if tooltipType == "GameTooltip" then
+				GameTooltip:SetOwner(self:IsFuBarMinimapAttached() and pluginToMinimapFrame[self] or pluginToFrame[self], "ANCHOR_CURSOR")
+				self:UpdateFuBarTooltip()
+			end
+			if type(self.OnFuBarEnter) == "function" then
+				self:OnFuBarEnter()
+			end
+		end
+	end
+	frame:SetScript("OnEnter", frame_OnEnter)
+	if not frame_OnLeave then
+		function frame_OnLeave(this)
+			local self = this.self
+			if type(self.OnFuBarLeave) == "function" then
+				self:OnFuBarLeave()
+			end
+			local tooltipType = getPluginOption(self, 'tooltipType', "GameTooltip")
+			if tooltipType == "GameTooltip" and GameTooltip:IsOwned(self:IsFuBarMinimapAttached() and pluginToMinimapFrame[self] or pluginToFrame[self]) then
+				GameTooltip:Hide()
+			end
+		end
+	end
+	frame:SetScript("OnLeave", frame_OnLeave)
+	if not frame_OnClick then
+		function frame_OnClick(this, button)
+			local self = this.self
+			if self:IsFuBarMinimapAttached() and this.dragged then return end
+			if type(self.OnFuBarClick) == "function" then
+				self:OnFuBarClick(button)
+			end
+		end
+	end
+	frame:SetScript("OnClick", frame_OnClick)
+	if not frame_OnDoubleClick then
+		function frame_OnDoubleClick(this, button)
+			local self = this.self
+			if type(self.OnFuBarDoubleClick) == "function" then
+				self:OnFuBarDoubleClick(button)
+			end
+		end
+	end
+	frame:SetScript("OnDoubleClick", frame_OnDoubleClick)
+	if not frame_OnMouseDown then
+		function frame_OnMouseDown(this, button)
+			local self = this.self
+			if button == "RightButton" and not IsShiftKeyDown() and not IsControlKeyDown() and not IsAltKeyDown() then
+				self:OpenMenu()
+				return
+			else
+				if type(self.OnFuBarMouseDown) == "function" then
+					self:OnFuBarMouseDown(button)
+				end
+			end
+		end
+	end
+	frame:SetScript("OnMouseDown", frame_OnMouseDown)
+	if not frame_OnMouseUp then
+		function frame_OnMouseUp(this, button)
+			local self = this.self
+			if type(self.OnFuBarMouseUp) == "function" then
+				self:OnFuBarMouseUp(button)
+			end
+		end
+	end
+	frame:SetScript("OnMouseUp", frame_OnMouseUp)
+	if not frame_OnReceiveDrag then
+		function frame_OnReceiveDrag(this)
+			local self = this.self
+			if (self:IsFuBarMinimapAttached() and not this.dragged) and type(self.OnReceiveDrag) == "function" then
+				self:OnFuBarReceiveDrag()
+			end
+		end
+	end
+	frame:SetScript("OnReceiveDrag", frame_OnReceiveDrag)
+	return frame
+end
+
+local child_OnEnter, child_OnLeave, child_OnClick, child_OnDoubleClick, child_OnMouseDown, child_OnMouseUp, child_OnReceiveDrag
+--[[---------------------------------------------------------------------------
+Arguments:
+	string - type of the frame, e.g. "Frame", "Button", etc.
+	[optional] string - name of the frame
+	[optional] frame - parent frame
+Returns:
+	frame - a child frame that can be manipulated and used
+Example:
+	local child = self:CreatePluginChildFrame("Frame", nil, self.frame)
+-----------------------------------------------------------------------------]]
+function FuBarPlugin:CreatePluginChildFrame(frameType, name, parent)
+	local child = CreateFrame(frameType, name, parent)
+	if parent then
+		child:SetFrameLevel(parent:GetFrameLevel() + 2)
+	end
+	child.self = self
+	if not child_OnEnter then
+		function child_OnEnter(this, ...)
+			local self = this.self
+			local frame = pluginToFrame[self]
+			if frame:GetScript("OnEnter") then
+				frame:GetScript("OnEnter")(frame, ...)
+			end
+		end
+	end
+	child:SetScript("OnEnter", child_OnEnter)
+	if not child_OnLeave then
+		function child_OnLeave(this, ...)
+			local self = this.self
+			local frame = pluginToFrame[self]
+			if frame:GetScript("OnLeave") then
+				frame:GetScript("OnLeave")(frame, ...)
+			end
+		end
+	end
+	child:SetScript("OnLeave", child_OnLeave)
+	if child:HasScript("OnClick") then
+		if not child_OnClick then
+			function child_OnClick(this, ...)
+				local self = this.self
+				local frame = pluginToFrame[self]
+				if frame:HasScript("OnClick") and frame:GetScript("OnClick") then
+					frame:GetScript("OnClick")(frame, ...)
+				end
+			end
+		end
+		child:SetScript("OnClick", child_OnClick)
+	end
+	if child:HasScript("OnDoubleClick") then
+		if not child_OnDoubleClick then
+			function child_OnDoubleClick(this, ...)
+				local self = this.self
+				local frame = pluginToFrame[self]
+				if frame:HasScript("OnDoubleClick") and frame:GetScript("OnDoubleClick") then
+					frame:GetScript("OnDoubleClick")(frame, ...)
+				end
+			end
+		end
+		child:SetScript("OnDoubleClick", child_OnDoubleClick)
+	end
+	if not child_OnMouseDown then
+		function child_OnMouseDown(this, ...)
+			local self = this.self
+			local frame = pluginToFrame[self]
+			if frame:HasScript("OnMouseDown") and frame:GetScript("OnMouseDown") then
+				frame:GetScript("OnMouseDown")(frame, ...)
+			end
+		end
+	end
+	child:SetScript("OnMouseDown", child_OnMouseDown)
+	if not child_OnMouseUp then
+		function child_OnMouseUp(this, ...)
+			local self = this.self
+			local frame = pluginToFrame[self]
+			if frame:HasScript("OnMouseUp") and frame:GetScript("OnMouseUp") then
+				frame:GetScript("OnMouseUp")(frame, ...)
+			end
+		end
+	end
+	child:SetScript("OnMouseUp", child_OnMouseUp)
+	if not child_OnReceiveDrag then
+		function child_OnReceiveDrag(this, ...)
+			local self = this.self
+			local frame = pluginToFrame[self]
+			if frame:HasScript("OnReceiveDrag") and frame:GetScript("OnReceiveDrag") then
+				frame:GetScript("OnReceiveDrag")(frame, ...)
+			end
+		end
+	end
+	child:SetScript("OnReceiveDrag", child_OnReceiveDrag)
+	return child
+end
+precondition(FuBarPlugin, 'CreatePluginChildFrame', function(self, frameType, name, parent)
+	if not pluginToFrame[self] then
+		error(("%sYou must have self.frame declared in order to add child frames."):format(self.name and self.name .. ": " or ""), 3)
+	end
+	argCheck(frameType, 2, "string")
+end)
+
+--[[---------------------------------------------------------------------------
+Notes:
+	Opens the configuration menu associated with this plugin.
+Example:
+	self:OpenMenu()
+-----------------------------------------------------------------------------]]
+function FuBarPlugin:OpenMenu(frame)
+	if not frame then
+		frame = self:IsFuBarMinimapAttached() and pluginToMinimapFrame[self] or pluginToFrame[self]
+	end
+	if not frame:IsVisible() then
+		frame = UIParent
+	end
+	local configType = getPluginOption(self, 'configType', "LibRockConfig-1.0")
+	if configType == "Dewdrop-2.0" then
+		if not frame or not self:GetFrame() or Dewdrop20:IsOpen(frame) then
+			Dewdrop20:Close()
+			return
+		end
+		local tooltipType = getPluginOption(self, 'tooltipType', "GameTooltip")
+		if tooltipType == "GameTooltip" then
+			if GameTooltip:IsOwned(frame) then
+				GameTooltip:Hide()
+			end
+		elseif tooltipType == "Custom" and type(self.CloseTooltip) == "function" then
+			self:CloseTooltip()
+		elseif tooltipType == "Tablet-2.0" and Tablet20 then
+			Tablet20:Close()
+		end
+
+		if not Dewdrop20:IsRegistered(self:GetFrame()) then
+			if type(self.OnMenuRequest) == "table" and (not self.OnMenuRequest.handler or self.OnMenuRequest.handler == self) and self.OnMenuRequest.type == "group" then
+				Dewdrop20:InjectAceOptionsTable(self, self.OnMenuRequest)
+				if self.OnMenuRequest.args and CheckFuBar() and not getPluginOption(self, 'independentProfile', false) then
+					self.OnMenuRequest.args.profile = nil
+					if self.OnMenuRequest.extraArgs then
+						self.OnMenuRequest.extraArgs.profile = nil
+					end
+				end
+			end
+			Dewdrop20:Register(self:GetFrame(),
+				'children', type(self.OnMenuRequest) == "table" and self.OnMenuRequest or function(level, value, valueN_1, valueN_2, valueN_3, valueN_4)
+					if level == 1 then
+						if not getPluginOption(self, 'hideMenuTitle', false) then
+							Dewdrop20:AddLine(
+								'text', self:GetTitle(),
+								'isTitle', true
+							)
+						end
+
+						if self.OnMenuRequest then
+							self:OnMenuRequest(level, value, false, valueN_1, valueN_2, valueN_3, valueN_4)
+						end
+
+						if not getPluginOption(self, 'overrideMenu', false) then
+							if self.MenuSettings and not getPluginOption(self, 'hideMenuTitle', false) then
+								Dewdrop20:AddLine()
+							end
+							self:AddImpliedMenuOptions()
+						end
+					else
+						if not getPluginOption(self, 'overrideMenu', false) and self:AddImpliedMenuOptions() then
+						else
+							if self.OnMenuRequest then
+								self:OnMenuRequest(level, value, false, valueN_1, valueN_2, valueN_3, valueN_4)
+							end
+						end
+					end
+					if level == 1 then
+						Dewdrop20:AddLine(
+							'text', CLOSE,
+							'tooltipTitle', CLOSE,
+							'tooltipText', CLOSE_DESC,
+							'func', Dewdrop.Close,
+							'arg1', Dewdrop
+						)
+					end
+				end,
+				'point', function(frame)
+					local x, y = frame:GetCenter()
+					local leftRight
+					if x < GetScreenWidth() / 2 then
+						leftRight = "LEFT"
+					else
+						leftRight = "RIGHT"
+					end
+					if y < GetScreenHeight() / 2 then
+						return "BOTTOM" .. leftRight, "TOP" .. leftRight
+					else
+						return "TOP" .. leftRight, "BOTTOM" .. leftRight
+					end
+				end,
+				'dontHook', true
+			)
+		end
+		if frame == self:GetFrame() then
+			Dewdrop20:Open(self:GetFrame())
+		elseif frame ~= UIParent then
+			Dewdrop20:Open(frame, self:GetFrame())
+		else
+			Dewdrop20:Open(frame, self:GetFrame(), 'cursorX', true, 'cursorY', true)
+		end
+	elseif configType == "LibRockConfig-1.0" then
+		local RockConfig = Rock("LibRockConfig-1.0", false, true)
+		if RockConfig then
+			RockConfig.OpenConfigMenu(self)
+		end
+	else
+		-- TODO: add more possibilities
+	end
+end
+
+function FuBarPlugin.OnEmbedInitialize(FuBarPlugin, self)
+	if not self.frame then
+		local name = MAJOR_VERSION .. "_" .. self:GetTitle() .. "_" .. "Frame"
+		local frame = _G[name]
+		if not frame or not _G[name .. "Text"] or not _G[name .. "Icon"] then
+			frame = FuBarPlugin.CreateBasicPluginFrame(self, name)
+
+			local icon = frame:CreateTexture(name .. "Icon", "ARTWORK")
+			frame.icon = icon
+			icon:SetWidth(16)
+			icon:SetHeight(16)
+			icon:SetPoint("LEFT", frame, "LEFT")
+
+			local text = frame:CreateFontString(name .. "Text", "ARTWORK")
+			frame.text = text
+			text:SetWidth(134)
+			text:SetHeight(24)
+			text:SetPoint("LEFT", icon, "RIGHT", 0, 1)
+			text:SetFontObject(GameFontNormal)
+		end
+		pluginToFrame[self] = frame
+	else
+		pluginToFrame[self] = self.frame
+		if not pluginToOptions[self] then
+			pluginToOptions[self] = {}
+		end
+		pluginToOptions[self].userDefinedFrame = true
+	end
+
+	local frame = pluginToFrame[self]
+	frame.plugin = self
+	frame:SetParent(UIParent)
+	frame:SetPoint("RIGHT", UIParent, "LEFT", -5, 0)
+	frame:Hide()
+
+	local iconPath = getPluginOption(self, 'iconPath', false)
+	if iconPath then
+		self:SetFuBarIcon(iconPath)
+	end
+
+	if CheckFuBar() then
+		FuBar:RegisterPlugin(self)
+	end
+end
+
+
+local CheckShow = function(self, panelId)
+	if not pluginToFrame[self]:IsShown() and (not pluginToMinimapFrame[self] or not pluginToMinimapFrame[self]:IsShown()) then
+		self:Show(panelId)
+	end
+end
+
+local schedules = {}
+local f = CreateFrame("Frame")
+f:SetScript("OnUpdate", function(this)
+	for i,v in ipairs(schedules) do
+		local success, ret = pcall(unpack(v))
+		if not success then
+			geterrorhandler()(ret)
+		end
+		schedules[i] = del(v)
+	end
+	f:Hide()
+end)
+
+local recheckPlugins
+local AceConsole
+function FuBarPlugin.OnEmbedEnable(FuBarPlugin, self, first)
+	if not getPluginOption(self, 'userDefinedFrame', false) then
+		local icon = pluginToFrame[self].icon
+		if self:IsFuBarIconShown() then
+			icon:Show()
+		else
+			icon:Hide()
+		end
+	end
+	self:CheckWidth(true)
+
+	if not getPluginOption(self, 'hideWithoutStandby', false) or (getLazyDatabaseValue(self) and not getLazyDatabaseValue(self, 'hidden')) then
+		if not first then
+			CheckShow(self, self.panelIdTmp)
+		else
+			schedules[#schedules+1] = newList(CheckShow, self, self.panelIdTmp)
+			f:Show()
+		end
+	end
+
+	local tooltipType = getPluginOption(self, 'tooltipType', "GameTooltip")
+	if tooltipType == "Tablet-2.0" and not getPluginOption(self, 'cannotDetachTooltip', false) and getLazyDatabaseValue(self, 'detachedTooltip', 'detached') then
+		schedules[#schedules+1] = newList(self.DetachFuBarTooltip, self)
+		f:Show()
+	end
+
+	if IsLoadOnDemand(self) and CheckFuBar() then
+		if not FuBar.db.profile.loadOnDemand then
+			FuBar.db.profile.loadOnDemand = {}
+		end
+		if not FuBar.db.profile.loadOnDemand[folderNames[self]] then
+			FuBar.db.profile.loadOnDemand[folderNames[self]] = {}
+		end
+		FuBar.db.profile.loadOnDemand[folderNames[self]].disabled = nil
+	end
+	--[[
+	if CheckFuBar() and AceLibrary:HasInstance("AceConsole-2.0") then
+		if not recheckPlugins then
+			if not AceConsole then
+				AceConsole = AceLibrary("AceConsole-2.0")
+			end
+			recheckPlugins = function()
+				for k,v in pairs(AceConsole.registry) do
+					if type(v) == "table" and v.args and AceOO.inherits(v.handler, FuBarPlugin) and not v.handler.independentProfile then
+						v.args.profile = nil
+					end
+				end
+			end
+		end
+		FuBarPlugin:ScheduleEvent("FuBarPlugin-recheckPlugins", recheckPlugins, 0)
+	end
+	]]
+end
+
+function FuBarPlugin.OnEmbedDisable(FuBarPlugin, self)
+	self:Hide(false)
+
+	if IsLoadOnDemand(self) and CheckFuBar() then
+		if not FuBar.db.profile.loadOnDemand then
+			FuBar.db.profile.loadOnDemand = {}
+		end
+		if not FuBar.db.profile.loadOnDemand[folderNames[self]] then
+			FuBar.db.profile.loadOnDemand[folderNames[self]] = {}
+		end
+		FuBar.db.profile.loadOnDemand[folderNames[self]].disabled = true
+	end
+end
+
+function FuBarPlugin.OnEmbedProfileEnable(FuBarPlugin, self)
+	self:UpdateFuBarPlugin()
+	if getLazyDatabaseValue(self) then
+		if not getLazyDatabaseValue(self, 'detachedTooltip') then
+			setLazyDatabaseValue(self, {}, 'detachedTooltip')
+		end
+		local tooltipType = getPluginOption(self, 'tooltipType', "GameTooltip")
+		if tooltipType == "Tablet-2.0" and Tablet20 then
+			if Tablet20.registry[pluginToFrame[self]] then
+				Tablet20:UpdateDetachedData(pluginToFrame[self], getLazyDatabaseValue(self, 'detachedTooltip'))
+			else
+				RegisterTablet20(self)
+			end
+		end
+		if MinimapContainer:HasPlugin(self) then
+			MinimapContainer:ReadjustLocation(self)
+		end
+	end
+end
+
+-- #NODOC
+function FuBarPlugin.GetEmbedRockConfigOptions(FuBarPlugin, self)
+	return 'icon', {
+		type = 'boolean',
+		name = SHOW_FUBAR_ICON,
+		desc = SHOW_FUBAR_ICON_DESC,
+		set = "ToggleFuBarIconShown",
+		get = "IsFuBarIconShown",
+		hidden = function()
+			return not getPluginOption(self, 'iconPath', false) or getPluginOption(self, 'hasNoText', false) or self:IsDisabled() or self:IsFuBarMinimapAttached() or not getLazyDatabaseValue(self)
+		end,
+		order = -13.7,
+		handler = self,
+	}, 'text', {
+		type = 'boolean',
+		name = SHOW_FUBAR_TEXT,
+		desc = SHOW_FUBAR_TEXT_DESC,
+		set = "ToggleFuBarTextShown",
+		get = "IsFuBarTextShown",
+		hidden = function()
+			return getPluginOption(self, 'cannotHideText', false) or not getPluginOption(self, 'iconPath', false) or getPluginOption(self, 'hasNoText') or self:IsDisabled() or self:IsFuBarMinimapAttached() or not getLazyDatabaseValue(self)
+		end,
+		order = -13.6,
+		handler = self,
+	}, 'colorText', {
+		type = 'boolean',
+		name = SHOW_COLORED_FUBAR_TEXT,
+		desc = SHOW_COLORED_FUBAR_TEXT_DESC,
+		set = "ToggleFuBarTextColored",
+		get = "IsFuBarTextColored",
+		hidden = function()
+			return getPluginOption(self, 'userDefinedFrame', false) or getPluginOption(self, 'hasNoText', false) or getPluginOption(self, 'hasNoColor', false) or self:IsDisabled() or self:IsFuBarMinimapAttached() or not getLazyDatabaseValue(self)
+		end,
+		order = -13.5,
+		handler = self,
+	}, 'detachTooltip', {
+		type = 'boolean',
+		name = DETACH_FUBAR_TOOLTIP,
+		desc = DETACH_FUBAR_TOOLTIP_DESC,
+		get = "IsFuBarTooltipDetached",
+		set = "ToggleFuBarTooltipDetached",
+		hidden = function()
+			return not Tablet20 or getPluginOption(self, 'tooltipType', "GameTooltip") ~= "Tablet-2.0" or self:IsDisabled()
+		end,
+		order = -13.4,
+		handler = self,
+	}, 'lockTooltip', {
+		type = 'boolean',
+		name = LOCK_FUBAR_TOOLTIP,
+		desc = LOCK_FUBAR_TOOLTIP_DESC,
+		get = function()
+			return Tablet20:IsLocked(pluginToFrame[self])
+		end,
+		set = function()
+			return Tablet20:ToggleLocked(pluginToFrame[self])
+		end,
+		disabled = function()
+			return not self:IsFuBarTooltipDetached()
+		end,
+		hidden = function()
+			return not Tablet20 or getPluginOption(self, 'tooltipType', "GameTooltip") ~= "Tablet-2.0" or getPluginOption(self, 'cannotDetachTooltip', false) or self:IsDisabled()
+		end,
+		order = -13.3,
+		handler = self,
+	}, 'position', {
+		type = 'choice',
+		name = POSITION_ON_FUBAR,
+		desc = POSITION_ON_FUBAR_DESC,
+		choices = {
+			LEFT = POSITION_LEFT,
+			CENTER = POSITION_CENTER,
+			RIGHT = POSITION_RIGHT
+		},
+		choiceSort = {
+			"LEFT",
+			"CENTER",
+			"RIGHT",
+		},
+		get = function()
+			return self:GetPanel() and self:GetPanel():GetPluginSide(self)
+		end,
+		set = function(value)
+			if self:GetPanel() then
+				self:GetPanel():SetPluginSide(self, value)
+			end
+		end,
+		hidden = function()
+			return self:IsFuBarMinimapAttached() or self:IsDisabled() or not pluginToPanel[self]
+		end,
+		order = -13.2,
+		handler = self,
+	}, 'minimapAttach', {
+		type = 'boolean',
+		name = ATTACH_PLUGIN_TO_MINIMAP,
+		desc = ATTACH_PLUGIN_TO_MINIMAP_DESC,
+		get = "IsFuBarMinimapAttached",
+		set = "ToggleFuBarMinimapAttached",
+		hidden = function()
+			return (getPluginOption(self, 'cannotAttachToMinimap', false) and not self:IsFuBarMinimapAttached()) or not CheckFuBar() or self:IsDisabled()
+		end,
+		order = -13.1,
+		handler = self,
+	}, 'hide', {
+		type = 'boolean',
+		name = function()
+			if self:IsFuBarMinimapAttached() then
+				return HIDE_MINIMAP_BUTTON
+			else
+				return HIDE_FUBAR_PLUGIN
+			end
+		end,
+		desc = HIDE_FUBAR_PLUGIN_DESC,
+		get = function()
+			return not pluginToFrame[self]:IsShown() and (not pluginToMinimapFrame[self] or not pluginToMinimapFrame[self]:IsShown())
+		end,
+		set = function(value)
+			if not value then
+				self:Show()
+			else
+				self:Hide()
+			end
+		end,
+		hidden = function()
+			return not getPluginOption(self, 'hideWithoutStandby', false) or self:IsDisabled()
+		end,
+		order = -13,
+		handler = self,
+	}
+end
+
+local plugins = MinimapContainer.plugins or {}
+for k in pairs(MinimapContainer) do
+	MinimapContainer[k] = nil
+end
+MinimapContainer.plugins = plugins
+
+local minimap_OnMouseDown, minimap_OnMouseUp
+function MinimapContainer:AddPlugin(plugin)
+	if CheckFuBar() and FuBar:IsChangingProfile() then
+		return
+	end
+	if pluginToPanel[plugin] then
+		pluginToPanel[plugin]:RemovePlugin(plugin)
+	end
+	pluginToPanel[plugin] = self
+	if not pluginToMinimapFrame[plugin] then
+		local frame = CreateFrame("Button", pluginToFrame[plugin]:GetName() .. "MinimapButton", Minimap)
+		pluginToMinimapFrame[plugin] = frame
+		plugin.minimapFrame = frame
+		frame.plugin = plugin
+		frame:SetWidth(31)
+		frame:SetHeight(31)
+		frame:SetFrameStrata("BACKGROUND")
+		frame:SetFrameLevel(4)
+		frame:SetHighlightTexture("Interface\\Minimap\\UI-Minimap-ZoomButton-Highlight")
+		local icon = frame:CreateTexture(frame:GetName() .. "Icon", "BACKGROUND")
+		plugin.minimapIcon = icon
+		local path = plugin:GetFuBarIcon() or (pluginToFrame[plugin].icon and pluginToFrame[plugin].icon:GetTexture()) or "Interface\\Icons\\INV_Misc_QuestionMark"
+		icon:SetTexture(path)
+		if path:sub(1, 16) == "Interface\\Icons\\" then
+			icon:SetTexCoord(0.07, 0.93, 0.07, 0.93)
+		else
+			icon:SetTexCoord(0, 1, 0, 1)
+		end
+		icon:SetWidth(20)
+		icon:SetHeight(20)
+		icon:SetPoint("TOPLEFT", frame, "TOPLEFT", 7, -5)
+		local overlay = frame:CreateTexture(frame:GetName() .. "Overlay","OVERLAY")
+		overlay:SetTexture("Interface\\Minimap\\MiniMap-TrackingBorder")
+		overlay:SetWidth(53)
+		overlay:SetHeight(53)
+		overlay:SetPoint("TOPLEFT",frame,"TOPLEFT")
+		frame:EnableMouse(true)
+		frame:RegisterForClicks("LeftButtonUp")
+
+		frame.self = plugin
+		if not frame_OnEnter then
+			function frame_OnEnter(this)
+				if type(this.self.OnFuBarEnter) == "function" then
+					this.self:OnFuBarEnter()
+				end
+			end
+		end
+		frame:SetScript("OnEnter", frame_OnEnter)
+		if not frame_OnLeave then
+			function frame_OnLeave(this)
+				if type(this.self.OnFuBarLeave) == "function" then
+					this.self:OnFuBarLeave()
+				end
+			end
+		end
+		frame:SetScript("OnLeave", frame_OnLeave)
+		if not frame_OnClick then
+			function frame_OnClick(this, arg1)
+				if this.self:IsMinimapAttached() and this.dragged then return end
+				if type(this.self.OnFuBarClick) == "function" then
+					this.self:OnFuBarClick(arg1)
+				end
+			end
+		end
+		frame:SetScript("OnClick", frame_OnClick)
+		if not frame_OnDoubleClick then
+			function frame_OnDoubleClick(this, arg1)
+				if type(this.self.OnFuBarDoubleClick) == "function" then
+					this.self:OnFuBarDoubleClick(arg1)
+				end
+			end
+		end
+		frame:SetScript("OnDoubleClick", frame_OnDoubleClick)
+		if not frame_OnReceiveDrag then
+			function frame_OnReceiveDrag(this)
+				if this.self:IsMinimapAttached() and this.dragged then return end
+				if type(this.self.OnFuBarReceiveDrag) == "function" then
+					this.self:OnFuBarReceiveDrag()
+				end
+			end
+		end
+		frame:SetScript("OnReceiveDrag", frame_OnReceiveDrag)
+		if not minimap_OnMouseDown then
+			function minimap_OnMouseDown(this, arg1)
+				this.dragged = false
+				if arg1 == "LeftButton" and not IsShiftKeyDown() and not IsControlKeyDown() and not IsAltKeyDown() then
+					HideDropDownMenu(1)
+					if type(this.self.OnFuBarMouseDown) == "function" then
+						this.self:OnFuBarMouseDown(arg1)
+					end
+				elseif arg1 == "RightButton" and not IsShiftKeyDown() and not IsControlKeyDown() and not IsAltKeyDown() then
+					this.self:OpenMenu(this)
+				else
+					HideDropDownMenu(1)
+					if type(this.self.OnFuBarMouseDown) == "function" then
+						this.self:OnFuBarMouseDown(arg1)
+					end
+				end
+				if this.self.OnFuBarClick or this.self.OnFuBarMouseDown or this.self.OnFuBarMouseUp or this.self.OnFuBarDoubleClick then
+					if this.self.minimapIcon:GetTexture():sub(1, 16) == "Interface\\Icons\\" then
+						this.self.minimapIcon:SetTexCoord(0.14, 0.86, 0.14, 0.86)
+					else
+						this.self.minimapIcon:SetTexCoord(0.1, 0.9, 0.1, 0.9)
+					end
+				end
+			end
+		end
+		frame:SetScript("OnMouseDown", minimap_OnMouseDown)
+		if not minimap_OnMouseUp then
+			function minimap_OnMouseUp(this, arg1)
+				if not this.dragged and type(this.self.OnFuBarMouseUp) == "function" then
+					this.self:OnFuBarMouseUp(arg1)
+				end
+				if this.self.minimapIcon:GetTexture():sub(1, 16) == "Interface\\Icons\\" then
+					this.self.minimapIcon:SetTexCoord(0.05, 0.95, 0.05, 0.95)
+				else
+					this.self.minimapIcon:SetTexCoord(0, 1, 0, 1)
+				end
+			end
+		end
+		frame:SetScript("OnMouseUp", minimap_OnMouseUp)
+		frame:RegisterForDrag("LeftButton")
+		frame:SetScript("OnDragStart", self.OnDragStart)
+		frame:SetScript("OnDragStop", self.OnDragStop)
+
+		if getPluginOption(plugin, 'tooltipType', "GameTooltip") == "Tablet-2.0" then
+			-- Note that we have to do this after :SetScript("OnEnter"), etc,
+			-- so that Tablet-2.0 can override it properly.
+			RegisterTablet20(plugin)
+			Tablet20:Register(frame, pluginToFrame[plugin])
+		end
+	end
+	pluginToFrame[plugin]:Hide()
+	pluginToMinimapFrame[plugin]:Show()
+	self:ReadjustLocation(plugin)
+	table.insert(self.plugins, plugin)
+	local exists = false
+	return true
+end
+
+function MinimapContainer:RemovePlugin(index)
+	if CheckFuBar() and FuBar:IsChangingProfile() then
+		return
+	end
+	if type(index) == "table" then
+		index = self:IndexOfPlugin(index)
+		if not index then
+			return
+		end
+	end
+	local t = self.plugins
+	local plugin = t[index]
+	assert(pluginToPanel[plugin] == self, "Plugin has improper panel field")
+	plugin:SetPanel(nil)
+	table.remove(t, index)
+	return true
+end
+
+function MinimapContainer:ReadjustLocation(plugin)
+	local frame = pluginToMinimapFrame[plugin]
+	if plugin.db and plugin.db.profile.minimapPositionWild then
+		frame:SetPoint("CENTER", UIParent, "BOTTOMLEFT", plugin.db.profile.minimapPositionX, plugin.db.profile.minimapPositionY)
+	elseif not plugin.db and plugin.minimapPositionWild then
+		frame:SetPoint("CENTER", UIParent, "BOTTOMLEFT", plugin.minimapPositionX, plugin.minimapPositionY)
+	else
+		local position
+		if plugin.db then
+			position = plugin.db.profile.minimapPosition or getPluginOption(plugin, 'defaultMinimapPosition', nil) or math.random(1, 360)
+		else
+			position = plugin.minimapPosition or getPluginOption(plugin, 'defaultMinimapPosition', nil) or math.random(1, 360)
+		end
+		local angle = math.rad(position or 0)
+		local x,y
+		local minimapShape = GetMinimapShape and GetMinimapShape() or "ROUND"
+		local cos = math.cos(angle)
+		local sin = math.sin(angle)
+
+		local round = true
+		if minimapShape == "ROUND" then
+			-- do nothing
+		elseif minimapShape == "SQUARE" then
+			round = false
+		elseif minimapShape == "CORNER-TOPRIGHT" then
+			if cos < 0 or sin < 0 then
+				round = false
+			end
+		elseif minimapShape == "CORNER-TOPLEFT" then
+			if cos > 0 or sin < 0 then
+				round = false
+			end
+		elseif minimapShape == "CORNER-BOTTOMRIGHT" then
+			if cos < 0 or sin > 0 then
+				round = false
+			end
+		elseif minimapShape == "CORNER-BOTTOMLEFT" then
+			if cos > 0 or sin > 0 then
+				round = false
+			end
+		elseif minimapShape == "SIDE-LEFT" then
+			if cos > 0 then
+				round = false
+			end
+		elseif minimapShape == "SIDE-RIGHT" then
+			if cos < 0 then
+				round = false
+			end
+		elseif minimapShape == "SIDE-TOP" then
+			if sin < 0 then
+				round = false
+			end
+		elseif minimapShape == "SIDE-BOTTOM" then
+			if sin > 0 then
+				round = false
+			end
+		elseif minimapShape == "TRICORNER-TOPRIGHT" then
+			if cos < 0 and sin < 0 then
+				round = false
+			end
+		elseif minimapShape == "TRICORNER-TOPLEFT" then
+			if cos > 0 and sin < 0 then
+				round = false
+			end
+		elseif minimapShape == "TRICORNER-BOTTOMRIGHT" then
+			if cos < 0 and sin > 0 then
+				round = false
+			end
+		elseif minimapShape == "TRICORNER-BOTTOMLEFT" then
+			if cos > 0 and sin > 0 then
+				round = false
+			end
+		end
+
+		if round then
+			x = cos * 80
+			y = sin * 80
+		else
+			x = 80 * 2^0.5 * cos
+			y = 80 * 2^0.5 * sin
+			if x < -80 then
+				x = -80
+			elseif x > 80 then
+				x = 80
+			end
+			if y < -80 then
+				y = -80
+			elseif y > 80 then
+				y = 80
+			end
+		end
+		frame:SetPoint("CENTER", Minimap, "CENTER", x, y)
+	end
+end
+
+function MinimapContainer:GetPlugin(index)
+	return self.plugins[index]
+end
+
+function MinimapContainer:GetNumPlugins()
+	return #self.plugins
+end
+
+function MinimapContainer:IndexOfPlugin(plugin)
+	for i,p in ipairs(self.plugins) do
+		if p == plugin then
+			return i, "MINIMAP"
+		end
+	end
+end
+
+function MinimapContainer:HasPlugin(plugin)
+	return self:IndexOfPlugin(plugin) ~= nil
+end
+
+function MinimapContainer:GetPluginSide(plugin)
+	local index = self:IndexOfPlugin(plugin)
+	assert(index, "Plugin not in panel")
+	return "MINIMAP"
+end
+
+function MinimapContainer.OnDragStart(this)
+	this.dragged = true
+	this:LockHighlight()
+	this:SetScript("OnUpdate", MinimapContainer.OnUpdate)
+	if this.self.minimapIcon:GetTexture():sub(1, 16) == "Interface\\Icons\\" then
+		this.self.minimapIcon:SetTexCoord(0.05, 0.95, 0.05, 0.95)
+	else
+		this.self.minimapIcon:SetTexCoord(0, 1, 0, 1)
+	end
+end
+
+function MinimapContainer.OnDragStop(this)
+	this:SetScript("OnUpdate", nil)
+	this:UnlockHighlight()
+end
+
+function MinimapContainer.OnUpdate(this, elapsed)
+	if not IsAltKeyDown() then
+		local mx, my = Minimap:GetCenter()
+		local px, py = GetCursorPosition()
+		local scale = UIParent:GetEffectiveScale()
+		px, py = px / scale, py / scale
+		local position = math.deg(math.atan2(py - my, px - mx))
+		if position <= 0 then
+			position = position + 360
+		elseif position > 360 then
+			position = position - 360
+		end
+		if this.self.db then
+			this.self.db.profile.minimapPosition = position
+			this.self.db.profile.minimapPositionX = nil
+			this.self.db.profile.minimapPositionY = nil
+			this.self.db.profile.minimapPositionWild = nil
+		else
+			this.self.minimapPosition = position
+			this.self.minimapPositionX = nil
+			this.self.minimapPositionY = nil
+			this.self.minimapPositionWild = nil
+		end
+	else
+		local px, py = GetCursorPosition()
+		local scale = UIParent:GetEffectiveScale()
+		px, py = px / scale, py / scale
+		if this.self.db then
+			this.self.db.profile.minimapPositionX = px
+			this.self.db.profile.minimapPositionY = py
+			this.self.db.profile.minimapPosition = nil
+			this.self.db.profile.minimapPositionWild = true
+		else
+			this.self.minimapPositionX = px
+			this.self.minimapPositionY = py
+			this.self.minimapPosition = nil
+			this.self.minimapPositionWild = true
+		end
+	end
+	MinimapContainer:ReadjustLocation(this.self)
+end
+
+FuBarPlugin:SetExportedMethods(
+	"SetFuBarOption",
+	"GetTitle",
+	"GetName",
+	"GetCategory",
+	"SetFontSize",
+	"GetFrame",
+	"Show",
+	"Hide",
+	"GetPanel",
+	"IsFuBarTextColored",
+	"ToggleFuBarTextColored",
+	"IsFuBarMinimapAttached",
+	"ToggleFuBarMinimapAttached",
+	"UpdateFuBarPlugin",
+	"UpdateFuBarText",
+	"UpdateFuBarTooltip",
+	"SetFuBarIcon",
+	"GetFuBarIcon",
+	"CheckWidth",
+	"SetFuBarText",
+	"GetFuBarText",
+	"IsFuBarIconShown",
+	"ToggleFuBarIconShown",
+	"ShowFuBarIcon",
+	"HideFuBarIcon",
+	"IsFuBarTextShown",
+	"ToggleFuBarTextShown",
+	"ShowFuBarText",
+	"HideFuBarText",
+	"IsFuBarTooltipDetached",
+	"ToggleFuBarTooltipDetached",
+	"DetachFuBarTooltip",
+	"ReattachFuBarTooltip",
+	"GetDefaultPosition",
+	"SetPanel",
+	"IsDisabled",
+	"CreateBasicPluginFrame",
+	"CreatePluginChildFrame",
+	"OpenMenu"
+)
+
+Rock:FinalizeLibrary(MAJOR_VERSION)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/FuBar_ReActionFu/lib/LibFuBarPlugin-3.0/LibFuBarPlugin-3.0.toc	Wed Apr 02 23:31:13 2008 +0000
@@ -0,0 +1,15 @@
+## Interface: 20300
+## LoadOnDemand: 1
+## Title: LibFuBarPlugin-3.0
+## Notes: A library to provide a means create a FuBar-compatible plugin.
+## Notes-zhTW: 一個提供支援FuBar所需功能的插件。
+## Notes-esES: Una biblioteca para crear plugins compatibles con Fubar.
+## Author: ckknight
+## eMail: ckknight@gmail.com
+## Version: 2.0 $Revision: 44269 $
+## X-Category: Library
+## Dependencies: LibRock-1.0
+## OptionalDeps: FuBar
+## X-License: LGPL v2.1
+
+lib.xml
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/FuBar_ReActionFu/lib/LibFuBarPlugin-3.0/lib.xml	Wed Apr 02 23:31:13 2008 +0000
@@ -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="LibFuBarPlugin-3.0.lua" />
+</Ui>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/FuBar_ReActionFu/lib/LibRock-1.0/Changelog-LibRock-1.0-r63317.txt	Wed Apr 02 23:31:13 2008 +0000
@@ -0,0 +1,44 @@
+------------------------------------------------------------------------
+r63317 | ckknight | 2008-03-01 12:57:51 -0500 (Sat, 01 Mar 2008) | 2 lines
+Changed paths:
+   M /trunk/LibRock-1.0/LibRock-1.0.lua
+
+LibRock-1.0 - support LibStub-based mixins that have rawget(library, "Embed") which is a function on em.
+ROCK-136
+------------------------------------------------------------------------
+r61418 | pb_ee1 | 2008-02-15 05:25:45 -0500 (Fri, 15 Feb 2008) | 2 lines
+Changed paths:
+   M /trunk/LibRock-1.0
+
+LibRock-1.0:
+- Adding tsvn:logtemplate, i said -_-'
+------------------------------------------------------------------------
+r61417 | pb_ee1 | 2008-02-15 05:24:03 -0500 (Fri, 15 Feb 2008) | 3 lines
+Changed paths:
+   M /trunk/LibRock-1.0/LibRock-1.0.lua
+
+LibRock-1.0:
+- coller -> collez
+- Adding tsvn:logtemplate
+------------------------------------------------------------------------
+r61415 | pettigrow | 2008-02-15 05:13:24 -0500 (Fri, 15 Feb 2008) | 1 line
+Changed paths:
+   M /trunk/LibRock-1.0/LibRock-1.0.lua
+
+LibRock-1.0: frFR Update
+------------------------------------------------------------------------
+r61118 | ckknight | 2008-02-13 01:32:54 -0500 (Wed, 13 Feb 2008) | 1 line
+Changed paths:
+   M /trunk/LibRock-1.0/LibRock-1.0.lua
+   M /trunk/LibRockComm-1.0/LibRockComm-1.0.lua
+   M /trunk/LibRockConfig-1.0/LibRockConfig-1.0.lua
+   M /trunk/LibRockConsole-1.0/LibRockConsole-1.0.lua
+   M /trunk/LibRockDB-1.0/LibRockDB-1.0.lua
+   M /trunk/LibRockEvent-1.0/LibRockEvent-1.0.lua
+   M /trunk/LibRockHook-1.0/LibRockHook-1.0.lua
+   M /trunk/LibRockLocale-1.0/LibRockLocale-1.0.lua
+   M /trunk/LibRockModuleCore-1.0/LibRockModuleCore-1.0.lua
+   M /trunk/LibRockTimer-1.0/LibRockTimer-1.0.lua
+
+.LibRock-1.0 - don't use negative reversion numbers because the LibStub patch for supporting that was never accepted.
+------------------------------------------------------------------------
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/FuBar_ReActionFu/lib/LibRock-1.0/LibRock-1.0.lua	Wed Apr 02 23:31:13 2008 +0000
@@ -0,0 +1,2968 @@
+--[[
+Name: LibRock-1.0
+Revision: $Rev: 63317 $
+Developed by: ckknight (ckknight@gmail.com)
+Website: http://www.wowace.com/
+Description: Library to allow for library and addon creation and easy table recycling functions.
+License: LGPL v2.1
+]]
+
+local MAJOR_VERSION = "LibRock-1.0"
+local MINOR_VERSION = tonumber(("$Revision: 63317 $"):match("(%d+)")) - 60000
+
+local _G = _G
+local GetLocale = _G.GetLocale
+local CATEGORIES
+if GetLocale() == "deDE" then
+	CATEGORIES = {
+		["Action Bars"] = "Aktionsleisten",
+		["Auction"] = "Auktion",
+		["Audio"] = "Audio",
+		["Battlegrounds/PvP"] = "Schlachtfeld/PvP",
+		["Buffs"] = "Stärkungszauber",
+		["Chat/Communication"] = "Chat/Kommunikation",
+		["Druid"] = "Druide",
+		["Hunter"] = "Jäger",
+		["Mage"] = "Magier",
+		["Paladin"] = "Paladin",
+		["Priest"] = "Priester",
+		["Rogue"] = "Schurke",
+		["Shaman"] = "Schamane",
+		["Warlock"] = "Hexenmeister",
+		["Warrior"] = "Krieger",
+		["Healer"] = "Heiler",
+		["Tank"] = "Tank",
+		["Caster"] = "Zauberer",
+		["Combat"] = "Kampf",
+		["Compilations"] = "Zusammenstellungen",
+		["Data Export"] = "Datenexport",
+		["Development Tools"] = "Entwicklungstools",
+		["Guild"] = "Gilde",
+		["Frame Modification"] = "Frameveränderungen",
+		["Interface Enhancements"] = "Interfaceverbesserungen",
+		["Inventory"] = "Inventar",
+		["Library"] = "Bibliotheken",
+		["Map"] = "Karte",
+		["Mail"] = "Post",
+		["Miscellaneous"] = "Diverses",
+		["Quest"] = "Quest",
+		["Raid"] = "Schlachtzug",
+		["Tradeskill"] = "Beruf",
+		["UnitFrame"] = "Einheiten-Fenster",
+	}
+elseif GetLocale() == "frFR" then
+	CATEGORIES = {
+		["Action Bars"] = "Barres d'action",
+		["Auction"] = "Hôtel des ventes",
+		["Audio"] = "Audio",
+		["Battlegrounds/PvP"] = "Champs de bataille/JcJ",
+		["Buffs"] = "Buffs",
+		["Chat/Communication"] = "Chat/Communication",
+		["Druid"] = "Druide",
+		["Hunter"] = "Chasseur",
+		["Mage"] = "Mage",
+		["Paladin"] = "Paladin",
+		["Priest"] = "Prêtre",
+		["Rogue"] = "Voleur",
+		["Shaman"] = "Chaman",
+		["Warlock"] = "Démoniste",
+		["Warrior"] = "Guerrier",
+		["Healer"] = "Soigneur",
+		["Tank"] = "Tank",
+		["Caster"] = "Casteur",
+		["Combat"] = "Combat",
+		["Compilations"] = "Compilations",
+		["Data Export"] = "Exportation de données",
+		["Development Tools"] = "Outils de développement",
+		["Guild"] = "Guilde",
+		["Frame Modification"] = "Modification des fenêtres",
+		["Interface Enhancements"] = "Améliorations de l'interface",
+		["Inventory"] = "Inventaire",
+		["Library"] = "Bibliothèques",
+		["Map"] = "Carte",
+		["Mail"] = "Courrier",
+		["Miscellaneous"] = "Divers",
+		["Quest"] = "Quêtes",
+		["Raid"] = "Raid",
+		["Tradeskill"] = "Métiers",
+		["UnitFrame"] = "Fenêtres d'unité",
+	}
+elseif GetLocale() == "koKR" then
+	CATEGORIES = {
+		["Action Bars"] = "액션바",
+		["Auction"] = "경매",
+		["Audio"] = "음향",
+		["Battlegrounds/PvP"] = "전장/PvP",
+		["Buffs"] = "버프",
+		["Chat/Communication"] = "대화/의사소통",
+		["Druid"] = "드루이드",
+		["Hunter"] = "사냥꾼",
+		["Mage"] = "마법사",
+		["Paladin"] = "성기사",
+		["Priest"] = "사제",
+		["Rogue"] = "도적",
+		["Shaman"] = "주술사",
+		["Warlock"] = "흑마법사",
+		["Warrior"] = "전사",
+		["Healer"] = "힐러",
+		["Tank"] = "탱커",
+		["Caster"] = "캐스터",
+		["Combat"] = "전투",
+		["Compilations"] = "복합",
+		["Data Export"] = "자료 출력",
+		["Development Tools"] = "개발 도구",
+		["Guild"] = "길드",
+		["Frame Modification"] = "구조 변경",
+		["Interface Enhancements"] = "인터페이스 강화",
+		["Inventory"] = "인벤토리",
+		["Library"] = "라이브러리",
+		["Map"] = "지도",
+		["Mail"] = "우편",
+		["Miscellaneous"] = "기타",
+		["Quest"] = "퀘스트",
+		["Raid"] = "공격대",
+		["Tradeskill"] = "전문기술",
+		["UnitFrame"] = "유닛 프레임",
+	}
+elseif GetLocale() == "zhTW" then
+	CATEGORIES = {
+		["Action Bars"] = "動作列",
+		["Auction"] = "拍賣",
+		["Audio"] = "音效",
+		["Battlegrounds/PvP"] = "戰場/PvP",
+		["Buffs"] = "增益",
+		["Chat/Communication"] = "聊天/通訊",
+		["Druid"] = "德魯伊",
+		["Hunter"] = "獵人",
+		["Mage"] = "法師",
+		["Paladin"] = "聖騎士",
+		["Priest"] = "牧師",
+		["Rogue"] = "盜賊",
+		["Shaman"] = "薩滿",
+		["Warlock"] = "術士",
+		["Warrior"] = "戰士",
+		["Healer"] = "治療者",
+		["Tank"] = "坦克",
+		["Caster"] = "施法者",
+		["Combat"] = "戰鬥",
+		["Compilations"] = "整合",
+		["Data Export"] = "資料匯出",
+		["Development Tools"] = "開發工具",
+		["Guild"] = "公會",
+		["Frame Modification"] = "框架修改",
+		["Interface Enhancements"] = "介面增強",
+		["Inventory"] = "庫存",
+		["Library"] = "程式庫",
+		["Map"] = "地圖",
+		["Mail"] = "郵件",
+		["Miscellaneous"] = "雜項",
+		["Quest"] = "任務",
+		["Raid"] = "團隊",
+		["Tradeskill"] = "交易技能",
+		["UnitFrame"] = "頭像框架",
+	}
+elseif GetLocale() == "zhCN" then
+	CATEGORIES = {
+		["Action Bars"] = "动作条",
+		["Auction"] = "拍卖",
+		["Audio"] = "音频",
+		["Battlegrounds/PvP"] = "战场/PvP",
+		["Buffs"] = "增益魔法",
+		["Chat/Communication"] = "聊天/交流",
+		["Druid"] = "德鲁伊",
+		["Hunter"] = "猎人",
+		["Mage"] = "法师",
+		["Paladin"] = "圣骑士",
+		["Priest"] = "牧师",
+		["Rogue"] = "潜行者",
+		["Shaman"] = "萨满祭司",
+		["Warlock"] = "术士",
+		["Warrior"] = "战士",
+		["Healer"] = "治疗",
+		["Tank"] = "坦克",
+		["Caster"] = "远程输出",
+		["Combat"] = "战斗",
+		["Compilations"] = "编译",
+		["Data Export"] = "数据导出",
+		["Development Tools"] = "开发工具",
+		["Guild"] = "公会",
+		["Frame Modification"] = "框架修改",
+		["Interface Enhancements"] = "界面增强",
+		["Inventory"] = "背包",
+		["Library"] = "库",
+		["Map"] = "地图",
+		["Mail"] = "邮件",
+		["Miscellaneous"] = "杂项",
+		["Quest"] = "任务",
+		["Raid"] = "团队",
+		["Tradeskill"] = "商业技能",
+		["UnitFrame"] = "头像框架",
+	}
+elseif GetLocale() == "esES" then
+	CATEGORIES = {
+		["Action Bars"] = "Barras de Acción",
+		["Auction"] = "Subasta",
+		["Audio"] = "Audio",
+		["Battlegrounds/PvP"] = "Campos de Batalla/JcJ",
+		["Buffs"] = "Buffs",
+		["Chat/Communication"] = "Chat/Comunicación",
+		["Druid"] = "Druida",
+		["Hunter"] = "Cazador",
+		["Mage"] = "Mago",
+		["Paladin"] = "Paladín",
+		["Priest"] = "Sacerdote",
+		["Rogue"] = "Pícaro",
+		["Shaman"] = "Chamán",
+		["Warlock"] = "Brujo",
+		["Warrior"] = "Guerrero",
+		["Healer"] = "Sanador",
+		["Tank"] = "Tanque",
+		["Caster"] = "Conjurador",
+		["Combat"] = "Combate",
+		["Compilations"] = "Compilaciones",
+		["Data Export"] = "Exportar Datos",
+		["Development Tools"] = "Herramientas de Desarrollo",
+		["Guild"] = "Hermandad",
+		["Frame Modification"] = "Modificación de Marcos",
+		["Interface Enhancements"] = "Mejoras de la Interfaz",
+		["Inventory"] = "Inventario",
+		["Library"] = "Biblioteca",
+		["Map"] = "Mapa",
+		["Mail"] = "Correo",
+		["Miscellaneous"] = "Misceláneo",
+		["Quest"] = "Misión",
+		["Raid"] = "Banda",
+		["Tradeskill"] = "Habilidad de Comercio",
+		["UnitFrame"] = "Marco de Unidades",
+	}
+else -- enUS
+	CATEGORIES = {
+		["Action Bars"] = "Action Bars",
+		["Auction"] = "Auction",
+		["Audio"] = "Audio",
+		["Battlegrounds/PvP"] = "Battlegrounds/PvP",
+		["Buffs"] = "Buffs",
+		["Chat/Communication"] = "Chat/Communication",
+		["Druid"] = "Druid",
+		["Hunter"] = "Hunter",
+		["Mage"] = "Mage",
+		["Paladin"] = "Paladin",
+		["Priest"] = "Priest",
+		["Rogue"] = "Rogue",
+		["Shaman"] = "Shaman",
+		["Warlock"] = "Warlock",
+		["Warrior"] = "Warrior",
+		["Healer"] = "Healer",
+		["Tank"] = "Tank",
+		["Caster"] = "Caster",
+		["Combat"] = "Combat",
+		["Compilations"] = "Compilations",
+		["Data Export"] = "Data Export",
+		["Development Tools"] = "Development Tools",
+		["Guild"] = "Guild",
+		["Frame Modification"] = "Frame Modification",
+		["Interface Enhancements"] = "Interface Enhancements",
+		["Inventory"] = "Inventory",
+		["Library"] = "Library",
+		["Map"] = "Map",
+		["Mail"] = "Mail",
+		["Miscellaneous"] = "Miscellaneous",
+		["Quest"] = "Quest",
+		["Raid"] = "Raid",
+		["Tradeskill"] = "Tradeskill",
+		["UnitFrame"] = "UnitFrame",
+	}
+end
+
+local select = _G.select
+local tostring = _G.tostring
+local pairs = _G.pairs
+local ipairs = _G.ipairs
+local error = _G.error
+local setmetatable = _G.setmetatable
+local getmetatable = _G.getmetatable
+local type = _G.type
+local pcall = _G.pcall
+local next = _G.next
+local tonumber = _G.tonumber
+local strmatch = _G.strmatch
+local table_remove = _G.table.remove
+local debugstack = _G.debugstack
+local LoadAddOn = _G.LoadAddOn
+local GetAddOnInfo = _G.GetAddOnInfo
+local GetAddOnMetadata = _G.GetAddOnMetadata
+local GetNumAddOns = _G.GetNumAddOns
+local DisableAddOn = _G.DisableAddOn
+local EnableAddOn = _G.EnableAddOn
+local IsAddOnLoadOnDemand = _G.IsAddOnLoadOnDemand
+local IsLoggedIn = _G.IsLoggedIn
+local geterrorhandler = _G.geterrorhandler
+local assert = _G.assert
+local collectgarbage = _G.collectgarbage
+local table_sort = _G.table.sort
+local table_concat = _G.table.concat
+
+-- #AUTODOC_NAMESPACE Rock
+
+
+local LibStub = _G.LibStub
+
+local Rock = LibStub:GetLibrary(MAJOR_VERSION, true) or _G.Rock
+local oldRock
+if not Rock then
+	Rock = LibStub:NewLibrary(MAJOR_VERSION, MINOR_VERSION)
+	if not Rock then
+		return
+	end
+	Rock.name = MAJOR_VERSION
+else
+	Rock, oldRock = Rock:NewLibrary(MAJOR_VERSION, MINOR_VERSION)
+	if not Rock then
+		return
+	end
+end
+_G.Rock = Rock
+
+local L = setmetatable({}, {__index=function(self,key) self[key] = key; return key end})
+if GetLocale() == "zhCN" then
+	L["Advanced options"] = "高级选项"
+	L["Advanced options for developers and power users."] = "开发者与高级用户的高级选项"
+	L["Unit tests"] = "框体测试"
+	L["Enable unit tests to be run. This is for developers only.\n\nYou must ReloadUI for changes to take effect."] = "开启框体测试,仅供开发者使用。\n\n需要重载用户界面。"
+	L["Contracts"] = "侦错协定"
+	L["Enable contracts to be run. This is for developers and anyone wanting to file a bug. Do not file a bug unless contracts are enabled. This will slightly slow down your addons if enabled."] = "启用侦错协定,这是给插件作者用来通报错误所使用。"
+	L["Reload UI"] = "重载UI"
+	L["Reload the User Interface for some changes to take effect."] = "部分功能更改需要重载用户界面才会生效。"
+	L["Reload"] = "重载"
+	L["Give donation"] = "捐赠"
+	L["Donate"] = "捐赠"
+	L["Give a much-needed donation to the author of this addon."] = "给插件作者捐赠支持插件开发。"
+	L["File issue"] = "通报错误"
+	L["Report"] = "报告"
+	L["File a bug or request a new feature or an improvement to this addon."] = "发送错误报告或请求新功能及要改进的部分。"
+	L["Press Ctrl-C to copy, then Alt-Tab out of the game, open your favorite web browser, and paste the link into the address bar."] = "Ctrl-C复制网址,Alt-Tab切换到桌面,打开浏览器,在地址栏贴上网址。"
+	L["Press Cmd-C to copy, then Cmd-Tab out of the game, open your favorite web browser, and paste the link into the address bar."] = "Cmd-C复制网址,Cmd-Tab切换到电脑桌面,打开浏览器,在地址栏贴上网址。"
+	L["Enabled"] = "开启"
+	L["Enable or disable this addon."] = "启用这个插件。"	
+
+elseif GetLocale() == "zhTW" then
+	L["Advanced options"] = "進階選項"
+	L["Advanced options for developers and power users."] = "插件作者、進階用戶選項"
+	L["Unit tests"] = "單元測試"
+	L["Enable unit tests to be run. This is for developers only.\n\nYou must ReloadUI for changes to take effect."] = "啟用單元測試,這是給插件作者使用的功能。\n\n需要重載介面才能使用。"
+	L["Contracts"] = "偵錯協定"
+	L["Enable contracts to be run. This is for developers and anyone wanting to file a bug. Do not file a bug unless contracts are enabled. This will slightly slow down your addons if enabled."] = "啟用偵錯協定,這是給插件作者用來通報錯誤所使用。"
+	L["Reload UI"] = "重載介面"
+	L["Reload the User Interface for some changes to take effect."] = "重新載入使用者介面,部分功能才會生效。"
+	L["Reload"] = "重載"
+	L["Give donation"] = "捐贈"
+	L["Donate"] = "捐贈"
+	L["Give a much-needed donation to the author of this addon."] = "捐贈金錢給插件作者。"
+	L["File issue"] = "通報錯誤"
+	L["Report"] = "報告"
+	L["File a bug or request a new feature or an improvement to this addon."] = "發出錯誤報告或請求新功能及要改進的部分。"
+	L["Press Ctrl-C to copy, then Alt-Tab out of the game, open your favorite web browser, and paste the link into the address bar."] = "Ctrl-C複製網址,Alt-Tab切換到電腦桌面,打開瀏覽器,在網址列貼上網址。"
+	L["Press Cmd-C to copy, then Cmd-Tab out of the game, open your favorite web browser, and paste the link into the address bar."] = "Cmd-C複製網址,Cmd-Tab切換到電腦桌面,打開瀏覽器,在網址列貼上網址。"
+	L["Enabled"] = "啟用"
+	L["Enable or disable this addon."] = "啟用這個插件。"
+elseif GetLocale() == "koKR" then
+	L["Advanced options"] = "상세 옵션"
+	L["Advanced options for developers and power users."] = "개발자와 파워 사용자를 위한 상세 옵션입니다."
+	L["Unit tests"] = "유닛 테스트"
+	L["Enable unit tests to be run. This is for developers only.\n\nYou must ReloadUI for changes to take effect."] = "유닛 테스트를 사용합니다. 이것은 개발자만을 위한 옵션입니다.\n\n변경된 결과를 적용하기 위해 당신의 UI를 재실행 합니다."
+	L["Contracts"] = "계약"
+	L["Enable contracts to be run. This is for developers and anyone wanting to file a bug. Do not file a bug unless contracts are enabled. This will slightly slow down your addons if enabled."] = "계약을 사용합니다. 이것은 개발자와 버그 파일을 알릴 분이면 누구나 사용 가능합니다. 계약이 가능하지 않으면 버그 파일을 보내지 마십시오. 이것은 당신의 애드온 속도를 약간 떨어뜨립니다."
+	L["Reload UI"] = "UI 재실행"
+	L["Reload the User Interface for some changes to take effect."] = "변경된 결과를 적용하기 위해 사용자 인터페이스를 재실행합니다."
+	L["Reload"] = "재실행"
+	L["Give donation"] = "기부"
+	L["Donate"] = "기부"
+	L["Give a much-needed donation to the author of this addon."] = "이 애드온의 제작자에게 필요한 기부를 합니다."
+	L["File issue"] = "파일 이슈"
+	L["Report"] = "보고"
+	L["File a bug or request a new feature or an improvement to this addon."] = "버그 파일을 알리거나 새로운 기능 또는 이 애드온에 대한 개선을 부탁합니다."
+	L["Press Ctrl-C to copy, then Alt-Tab out of the game, open your favorite web browser, and paste the link into the address bar."] = "Ctrl-C로 복사합니다. Alt-Tab 눌려 게임으로 부터 나간후 웹 브라우저를 엽니다. 복사된 링크를 주소 창에 붙여넣기 합니다."
+	L["Press Cmd-C to copy, then Cmd-Tab out of the game, open your favorite web browser, and paste the link into the address bar."] = "Cmd-C로 복사합니다. Cmd-Tab 눌려 게임으로 부터 나간후 웹 브라우저를 엽니다. 복사된 링크를 주소 창에 붙여넣기 합니다."
+	L["Enabled"] = "사용"
+	L["Enable or disable this addon."] = "이 애드온을 사용하거나 사용하지 않습니다."
+elseif GetLocale() == "frFR" then
+	L["Advanced options"] = "Options avancées"
+	L["Advanced options for developers and power users."] = "Options avancées à l'attention des développeurs et des utilisateurs expérimentés."
+	L["Reload UI"] = "Recharger IU"
+	L["Reload the User Interface for some changes to take effect."] = "Recharge l'interface utilisateur afin que certains changements prennent effet."
+	L["Reload"] = "Recharger"
+	L["Give donation"] = "Faire un don"
+	L["Donate"] = "Don"
+	L["Give a much-needed donation to the author of this addon."] = "Permet de faire un don bien mérité à l'auteur de cet addon."
+	L["File issue"] = "Problème"
+	L["Report"] = "Signaler"
+	L["File a bug or request a new feature or an improvement to this addon."] = "Permet de signaler un bogue ou de demander une amélioration à cet addon."
+	L["Press Ctrl-C to copy, then Alt-Tab out of the game, open your favorite web browser, and paste the link into the address bar."] = "Ctrl-C pour copier, puis Alt-Tab pour sortir du jeu. Ouvrez votre navigateur internet et collez le lien dans la barre d'adresse."
+	L["Press Cmd-C to copy, then Cmd-Tab out of the game, open your favorite web browser, and paste the link into the address bar."] = "Cmd-C pour copier, puis Alt-Tab pour sortir du jeu. Ouvrez votre navigateur internet et collez le lien dans la barre d'adresse."
+	L["Enabled"] = "Activé"
+	L["Enable or disable this addon."] = "Active ou désactive cet addon."
+end
+
+local isStandalone = debugstack():match("[Oo%.][Nn%.][Ss%.]\\([^\\]+)\\") == MAJOR_VERSION or nil
+local unitTestDB, enableContracts
+
+local weakKey = { __mode = 'k' }
+
+-- frame to manage events from
+Rock.frame = oldRock and oldRock.frame or _G.CreateFrame("Frame")
+local frame = Rock.frame
+-- dict of libraries in { ["major"] = object } form
+Rock.libraries = oldRock and oldRock.libraries or { [MAJOR_VERSION] = Rock }
+local libraries = Rock.libraries
+-- set of libraries which have gone through the finalization process in { [object] = true } form
+Rock.finalizedLibraries = setmetatable(oldRock and oldRock.finalizedLibraries or { }, weakKey)
+local finalizedLibraries = Rock.finalizedLibraries
+-- set of libraries which have been tried to be loaded.
+Rock.scannedLibraries = oldRock and oldRock.scannedLibraries or {}
+local scannedLibraries = Rock.scannedLibraries
+-- exportedMethods[library] = { "method1", "method2" }
+Rock.exportedMethods = setmetatable(oldRock and oldRock.exportedMethods or {}, weakKey)
+local exportedMethods = Rock.exportedMethods
+-- mixinToObject[mixin][object] = true
+Rock.mixinToObject = setmetatable(oldRock and oldRock.mixinToObject or {}, weakKey)
+local mixinToObject = Rock.mixinToObject
+-- dict of addons in { ["name"] = object } form
+Rock.addons = oldRock and oldRock.addons or {}
+local addons = Rock.addons
+-- set of libraries that should be finalized before ADDON_LOADED.
+Rock.pendingLibraries = setmetatable(oldRock and oldRock.pendingLibraries or { }, weakKey)
+local pendingLibraries = Rock.pendingLibraries
+-- list of addons in order of created that need to be initialized by ADDON_LOADED.
+Rock.pendingAddons = oldRock and oldRock.pendingAddons or {}
+local pendingAddons = Rock.pendingAddons
+-- dictionary of addons to their folder names
+Rock.addonToFolder = oldRock and oldRock.addonToFolder or {}
+local addonToFolder = Rock.addonToFolder
+-- set of folders which have been loaded
+Rock.foldersLoaded = oldRock and oldRock.foldersLoaded or {}
+local foldersLoaded = Rock.foldersLoaded
+-- list of addons in order of created that need to be enabled by PLAYER_LOGIN.
+Rock.pendingAddonsEnable = oldRock and oldRock.pendingAddonsEnable or {}
+local pendingAddonsEnable = Rock.pendingAddonsEnable
+-- set of addons which have been enabled at least once.
+Rock.addonsAlreadyEnabled = oldRock and oldRock.addonsAlreadyEnabled or {}
+local addonsAlreadyEnabled = Rock.addonsAlreadyEnabled
+-- set of addons which have no database and are set to be inactive.
+Rock.inactiveAddons = oldRock and oldRock.inactiveAddons or {}
+local inactiveAddons = Rock.inactiveAddons
+-- set of addons which are currently enabled (not necessarily should be)
+Rock.currentlyEnabledAddons = oldRock and oldRock.currentlyEnabledAddons or {}
+local currentlyEnabledAddons = Rock.currentlyEnabledAddons
+-- dictionary of namespace to list of functions which will be run.
+Rock.unitTests = oldRock and oldRock.unitTests or {}
+local unitTests = Rock.unitTests
+-- metatable for addons
+Rock.addon_mt = oldRock and oldRock.addon_mt or {}
+local addon_mt = Rock.addon_mt
+for k in pairs(addon_mt) do
+	addon_mt[k] = nil
+end
+function addon_mt:__tostring()
+	return tostring(self.name)
+end
+
+local function better_tostring(self)
+	if type(self) == "table" and self.name then
+		return tostring(self.name)
+	end
+	return tostring(self)
+end
+
+local function figureCurrentAddon(pos)
+	local stack = debugstack(pos+1, 1, 0)
+	local folder = stack:match("[Oo%.][Nn%.][Ss%.]\\([^\\]+)\\")
+	if folder then
+		return folder
+	end
+
+	local partFolder = stack:match("...([^\\]+)\\")
+	if partFolder then
+		local partFolder_len = #partFolder
+		for i = 1, GetNumAddOns() do
+			local name = GetAddOnInfo(i)
+			if #name >= partFolder_len then
+				local partName = name:sub(-partFolder_len)
+				if partName == partFolder then
+					return name
+				end
+			end
+		end
+	end
+	return nil
+end
+
+--[[---------------------------------------------------------------------------
+Returns:
+	string - the localized name of the given category.
+Arguments:
+	string - the English name of the category.
+Example:
+	local uf = Rock:GetLocalizedCategory("UnitFrame")
+-----------------------------------------------------------------------------]]
+function Rock:GetLocalizedCategory(name)
+	if type(name) ~= "string" then
+		error(("Bad argument #2 to `GetLocalizedCategory'. Expected %q, got %q."):format("string", type(name)), 2)
+	end
+	local cat = CATEGORIES[name]
+	if cat then
+		return cat
+	end
+	local name_lower = name:lower()
+	for k in pairs(CATEGORIES) do
+		if k:lower() == name_lower then
+			return k
+		end
+	end
+	return _G.UNKNOWN or "Unknown"
+end
+
+local weak = {__mode = 'kv'}
+
+Rock.recycleData = oldRock and oldRock.recycleData or {}
+local recycleData = Rock.recycleData
+if recycleData.pools then
+	setmetatable(recycleData.pools, weak)
+end
+if recycleData.debugPools then
+	setmetatable(recycleData.debugPools, weak)
+end
+if recycleData.newList then
+	setmetatable(recycleData.newList, weak)
+end
+if recycleData.newDict then
+	setmetatable(recycleData.newDict, weak)
+end
+if recycleData.newSet then
+	setmetatable(recycleData.newSet, weak)
+end
+if recycleData.del then
+	setmetatable(recycleData.del, weak)
+end
+
+local tmp = {}
+local function myUnpack(t, start)
+	if not start then
+		start = 1
+	end
+	local value = t[start]
+	if value == nil then
+		return
+	end
+	t[start] = nil
+	return value, myUnpack(t, start+1)
+end
+
+--[[---------------------------------------------------------------------------
+Notes:
+	* Returns functions for the specified namespace based on what is provided.
+	* function types:
+	; "newList" : to create a list
+	; "newDict" : to create a dictionary
+	; "newSet" : to create a set
+	; "del" : to delete a table
+	; "unpackListAndDel" : deletes a table and returns what its contents were as a list, in order.
+	; "unpackSetAndDel" : deletes a table and returns what its contents were as a set, in no particular order.
+	; "unpackDictAndDel" : deletes a table and returns what its contents were as a dictionary, in no particular order.
+	* If you provide "Debug" as the last argument, then the namespace can be debugged with ''':DebugRecycle'''
+	* It is '''not recommended''' to use table recycling with tables that have more than 128 keys, as it is typically faster to let lua's garbage collector handle it.
+Arguments:
+	string - the namespace. ''Note: this doesn't necessarily have to be a string.''
+Example:
+	local newList, newDict, newSet, del, unpackListAndDel, unpackSetAndDel, unpackDictAndDel = Rock:GetRecyclingFunctions("MyNamespace", "newList", "newDict", "newSet", "del", "unpackListAndDel", "unpackSetAndDel", "unpackDictAndDel")
+
+	local t = newList('alpha', 'bravo') -- same as t = {'alpha', 'bravo'}
+	local u = newDict('alpha', 'bravo') -- same as t = {['alpha'] = 'bravo'}
+	local v = newSet('alpha', 'bravo') -- same as t = {['alpha'] = true, ['bravo'] = true}
+	t = del(t) -- you want to clear your reference as well as deleting.
+	u = del(u)
+	v = del(v)
+
+	-- for debugging
+	local newList = Rock:GetRecyclingFunctions("MyNamespace", "newList", "Debug")
+	local t = newList()
+	Rock:DebugRecycle("MyNamespace")
+	t = del(t)
+
+	-- unpacking functions
+	unpackListAndDel(newList(...)) => ...
+	unpackSetAndDel(newSet(...)) => ...
+	unpackDictAndDel(newDict(...)) => ...
+	newList(unpackListAndDel(t)) => t
+	newSet(unpackSetAndDel(t)) => t
+	newDict(unpackDictAndDel(t)) => t
+	-- as you can see, they are inverses of each other.
+-----------------------------------------------------------------------------]]
+function Rock:GetRecyclingFunctions(namespace, ...)
+	local pools = recycleData.pools
+	if not pools then
+		pools = setmetatable({}, weak)
+		recycleData.pools = pools
+	end
+	if namespace == "newList" or namespace == "newSet" or namespace == "newDict" or namespace == "del" or namespace == "unpackListAndDel" or namespace == "unpackSetAndDel" or namespace == "unpackDictAndDel" then
+		error(("Bad argument #2 to `GetRecyclingFunctions'. Cannot be %q"):format(namespace), 2)
+	end
+	local pool = pools[namespace]
+	if not pool then
+		pool = setmetatable({}, weak)
+		pools[namespace] = pool
+	end
+	local n = select('#', ...)
+	local debug = select(n, ...) == "Debug"
+	if debug then
+		n = n - 1
+		local debugPools = recycleData.debugPools
+		if not debugPools then
+			debugPools = setmetatable({}, weak)
+			recycleData.debugPools = debugPools
+		end
+		debug = debugPools[namespace]
+		if not debug then
+			debug = { num = 0 }
+			debugPools[namespace] = debug
+		end
+	elseif recycleData.debugPools and recycleData.debugPools[namespace] then
+		debug = recycleData.debugPools[namespace]
+	end
+	for i = 1, n do
+		local func = select(i, ...)
+		local recycleData_func = recycleData[func]
+		if not recycleData_func then
+			recycleData_func = setmetatable({}, weak)
+			recycleData[func] = recycleData_func
+		end
+		if func == "newList" then
+			local newList = recycleData_func[namespace]
+			if not newList then
+				function newList(...)
+					local t = next(pool)
+					local n = select('#', ...)
+					if t then
+						pool[t] = nil
+						for i = 1, n do
+							t[i] = select(i, ...)
+						end
+					else
+						t = { ... }
+					end
+
+					if debug then
+						debug[t] = debugstack(2)
+						debug.num = debug.num + 1
+					end
+
+					return t, n
+				end
+				recycleData_func[namespace] = newList
+			end
+			tmp[i] = newList
+		elseif func == "newDict" then
+			local newDict = recycleData_func[namespace]
+			if not newDict then
+				function newDict(...)
+					local t = next(pool)
+					if t then
+						pool[t] = nil
+					else
+						t = {}
+					end
+
+					for i = 1, select('#', ...), 2 do
+						t[select(i, ...)] = select(i+1, ...)
+					end
+
+					if debug then
+						debug[t] = debugstack(2)
+						debug.num = debug.num + 1
+					end
+
+					return t
+				end
+				recycleData_func[namespace] = newDict
+			end
+			tmp[i] = newDict
+		elseif func == "newSet" then
+			local newSet = recycleData_func[namespace]
+			if not newSet then
+				function newSet(...)
+					local t = next(pool)
+					if t then
+						pool[t] = nil
+					else
+						t = {}
+					end
+
+					for i = 1, select('#', ...) do
+						t[select(i, ...)] = true
+					end
+
+					if debug then
+						debug[t] = debugstack(2)
+						debug.num = debug.num + 1
+					end
+
+					return t
+				end
+				recycleData_func[namespace] = newSet
+			end
+			tmp[i] = newSet
+		elseif func == "del" then
+			local del = recycleData_func[namespace]
+			if not del then
+				function del(t)
+					if not t then
+						error(("Bad argument #1 to `del'. Expected %q, got %q."):format("table", type(t)), 2)
+					end
+					if pool[t] then
+						local _, ret = pcall(error, "Error, double-free syndrome.", 3)
+						geterrorhandler()(ret)
+					end
+					setmetatable(t, nil)
+					for k in pairs(t) do
+						t[k] = nil
+					end
+					t[true] = true
+					t[true] = nil
+					pool[t] = true
+
+					if debug then
+						debug[t] = nil
+						debug.num = debug.num - 1
+					end
+					return nil
+				end
+				recycleData_func[namespace] = del
+			end
+			tmp[i] = del
+		elseif func == "unpackListAndDel" then
+			local unpackListAndDel = recycleData_func[namespace]
+			if not unpackListAndDel then
+				local function f(t, start, finish)
+					if start > finish then
+						for k in pairs(t) do
+							t[k] = nil
+						end
+						t[true] = true
+						t[true] = nil
+						pool[t] = true
+						return
+					end
+					return t[start], f(t, start+1, finish)
+				end
+				function unpackListAndDel(t, start, finish)
+					if not t then
+						error(("Bad argument #1 to `unpackListAndDel'. Expected %q, got %q."):format("table", type(t)), 2)
+					end
+					if not start then
+						start = 1
+					end
+					if not finish then
+						finish = #t
+					end
+					setmetatable(t, nil)
+					if debug then
+						debug[t] = nil
+						debug.num = debug.num - 1
+					end
+					return f(t, start, finish)
+				end
+			end
+			tmp[i] = unpackListAndDel
+		elseif func == "unpackSetAndDel" then
+			local unpackSetAndDel = recycleData_func[namespace]
+			if not unpackSetAndDel then
+				local function f(t, current)
+					current = next(t, current)
+					if current == nil then
+						for k in pairs(t) do
+							t[k] = nil
+						end
+						t[true] = true
+						t[true] = nil
+						pool[t] = true
+						return
+					end
+					return current, f(t, current)
+				end
+				function unpackSetAndDel(t)
+					if not t then
+						error(("Bad argument #1 to `unpackListAndDel'. Expected %q, got %q."):format("table", type(t)), 2)
+					end
+					setmetatable(t, nil)
+					if debug then
+						debug[t] = nil
+						debug.num = debug.num - 1
+					end
+					return f(t, nil)
+				end
+			end
+			tmp[i] = unpackSetAndDel
+		elseif func == "unpackDictAndDel" then
+			local unpackDictAndDel = recycleData_func[namespace]
+			if not unpackDictAndDel then
+				local function f(t, current)
+					local value
+					current, value = next(t, current)
+					if current == nil then
+						for k in pairs(t) do
+							t[k] = nil
+						end
+						t[true] = true
+						t[true] = nil
+						pool[t] = true
+						return
+					end
+					return current, value, f(t, current)
+				end
+				function unpackDictAndDel(t)
+					if not t then
+						error(("Bad argument #1 to `unpackListAndDel'. Expected %q, got %q."):format("table", type(t)), 2)
+					end
+					setmetatable(t, nil)
+					if debug then
+						debug[t] = nil
+						debug.num = debug.num - 1
+					end
+					return f(t, nil)
+				end
+			end
+			tmp[i] = unpackDictAndDel
+		else
+			error(("Bad argument #%d to `GetRecyclingFunctions': %q, %q, %q, %q, %q, %q, or %q expected, got %s"):format(i+2, "newList", "newDict", "newSet", "del", "unpackListAndDel", "unpackSetAndDel", "unpackDictAndDel", type(func) == "string" and ("%q"):format(func) or tostring(func)), 2)
+		end
+	end
+	return myUnpack(tmp)
+end
+
+--[[---------------------------------------------------------------------------
+Notes:
+	* Prints information about the specified recycling namespace, including what tables are still in play and where they come from and how many there are.
+	* This goes in tandem with ''':GetRecyclingFunctions'''
+Arguments:
+	string - the namespace. ''Note: this doesn't necessarily have to be a string.''
+Example:
+	local newList = Rock:GetRecyclingFunctions("MyNamespace", "newList", "Debug")
+	local t = newList()
+	Rock:DebugRecycle("MyNamespace")
+	t = del(t)
+-----------------------------------------------------------------------------]]
+function Rock:DebugRecycle(namespace)
+	local debug = recycleData.debugPools and recycleData.debugPools[namespace]
+	if not debug then
+		return
+	end
+	for k, v in pairs(debug) do
+		if k ~= "num" then
+			_G.DEFAULT_CHAT_FRAME:AddMessage(v)
+			_G.DEFAULT_CHAT_FRAME:AddMessage("------")
+		end
+	end
+	_G.DEFAULT_CHAT_FRAME:AddMessage(("%s: %d tables in action."):format(tostring(namespace), debug.num))
+end
+
+local newList, del, unpackListAndDel, unpackDictAndDel = Rock:GetRecyclingFunctions(MAJOR_VERSION, "newList", "del", "unpackListAndDel", "unpackDictAndDel")
+
+--[[---------------------------------------------------------------------------
+Notes:
+	* Adds a unit test for the specified namespace
+	* The function provided is called, and it should be where tests are performed, if a problem occurs, an error should fire. If no problems occur, it should return silently.
+	* You can have as many tests per namespace as you want.
+Arguments:
+	string - the namespace.
+	function - the function to call.
+Example:
+	Rock:AddUnitTest("LibMonkey-1.0", function()
+		local LibMonkey = Rock("LibMonkey-1.0")
+		assert(LibMonkey:Fling() == "Poo")
+	end)
+-----------------------------------------------------------------------------]]
+function Rock:AddUnitTest(namespace, func)
+	if not isStandalone then
+		return
+	end
+	if type(namespace) ~= "string" then
+		error(("Bad argument #2 to `AddUnitTest'. Expected %q, got %q."):format("string", type(namespace)), 2)
+	end
+	if namespace:find("^Lib[A-Z]") then
+		local addon = figureCurrentAddon(2)
+		if addon ~= namespace then
+			return
+		end
+	end
+	if type(func) ~= "function" then
+		error(("Bad argument #3 to `AddUnitTest'. Expected %q, got %q."):format("function", type(func)), 2)
+	end
+	local addon = figureCurrentAddon(2)
+	if libraries[namespace] and addon ~= namespace then
+		-- only work on standalone libraries.
+		return
+	end
+	local unitTests_namespace = unitTests[namespace]
+	if not unitTests_namespace then
+		unitTests_namespace = newList()
+		unitTests[namespace] = unitTests_namespace
+	end
+	if not unitTests_namespace.addon then
+		unitTests_namespace.addon = addon
+	end
+	if unitTestDB and not unitTestDB[namespace] then
+		return
+	end
+	unitTests_namespace[#unitTests_namespace+1] = func
+end
+
+local LibRockEvent
+local LibRockModuleCore
+local OpenDonationFrame, OpenIssueFrame
+function Rock:OnLibraryLoad(major, library)
+	if major == "LibRockEvent-1.0" then
+		LibRockEvent = library
+		LibRockEvent:Embed(Rock)
+	elseif major == "LibRockModuleCore-1.0" then
+		LibRockModuleCore = library
+	elseif major == "LibRockConfig-1.0" then
+		if isStandalone then
+			library.rockOptions.args.advanced = {
+				type = 'group',
+				groupType = 'inline',
+				name = L["Advanced options"],
+				desc = L["Advanced options for developers and power users."],
+				order = -1,
+				args = {
+					unitTests = {
+						type = 'multichoice',
+						name = L["Unit tests"],
+						desc = L["Enable unit tests to be run. This is for developers only.\n\nYou must ReloadUI for changes to take effect."],
+						get = function(key)
+							return unitTestDB[key]
+						end,
+						set = function(key, value)
+							unitTestDB[key] = value or nil
+						end,
+						choices = function()
+							local t = newList()
+							for k in pairs(unitTests) do
+								t[k] = k
+							end
+							return "@dict", unpackDictAndDel(t)
+						end
+					},
+					contracts = {
+						type = 'boolean',
+						name = L["Contracts"],
+						desc = L["Enable contracts to be run. This is for developers and anyone wanting to file a bug. Do not file a bug unless contracts are enabled. This will slightly slow down your addons if enabled."],
+						get = function()
+							return enableContracts
+						end,
+						set = function(value)
+							_G.LibRock_1_0DB.contracts = value or nil
+							enableContracts = value
+						end,
+					}
+				}
+			}
+		end
+		library.rockOptions.args.reloadui = {
+			type = 'execute',
+			name = L["Reload UI"],
+			desc = L["Reload the User Interface for some changes to take effect."],
+			buttonText = L["Reload"],
+			func = function()
+				_G.ReloadUI()
+			end,
+			order = -2,
+		}
+		Rock.donate = "Paypal:ckknight AT gmail DOT com"
+		library.rockOptions.args.donate = {
+			type = 'execute',
+			name = L["Give donation"],
+			buttonText = L["Donate"],
+			desc = L["Give a much-needed donation to the author of this addon."],
+			func = OpenDonationFrame,
+			passValue = Rock,
+			order = -3,
+		}
+		Rock.issueTracker = "Wowace:10027"
+		library.rockOptions.args.issue = {
+			type = 'execute',
+			name = L["File issue"],
+			buttonText = L["Report"],
+			desc = L["File a bug or request a new feature or an improvement to this addon."],
+			func = OpenIssueFrame,
+			passValue = Rock,
+			order = -4,
+		}
+	end
+end
+
+addon_mt.__index = {}
+local addon_mt___index = addon_mt.__index
+--[[---------------------------------------------------------------------------
+#FORCE_DOC
+Notes:
+	* This is exported to all addons.
+	* This information is retrieved from LibRockModuleCore-1.0 if it is a module, otherwise from LibRockDB-1.0 if it uses that as a mixin, otherwise it keeps a variable locally.
+Returns:
+	boolean - whether the addon is in an active state or not.
+Example:
+	local active = MyAddon:IsActive()
+-----------------------------------------------------------------------------]]
+function addon_mt___index:IsActive()
+	if LibRockModuleCore then
+		local core = LibRockModuleCore:HasModule(self)
+		if core then
+			return core:IsModuleActive(self)
+		end
+	end
+
+	local self_db = self.db
+	if self_db then
+		local disabled
+		local self_db_raw = self_db.raw
+		if self_db_raw then
+			local self_db_raw_disabled = self_db_raw.disabled
+			if self_db_raw_disabled then
+				local profile = type(self.GetProfile) == "function" and select(2, self:GetProfile()) or false
+				disabled = self_db_raw_disabled[profile]
+			end
+		else
+			return false
+		end
+		return not disabled
+	end
+
+	return not inactiveAddons[self]
+end
+--[[---------------------------------------------------------------------------
+#FORCE_DOC
+Notes:
+	* This is exported to all addons.
+	* If it enables the addon, it will call :OnEnable(first) on the addon and :OnEmbedEnable(addon, first) on all its mixins.
+	* If it disables the addon, it will call :OnDisable(first) on the addon and :OnEmbedDisable(addon, first) on all its mixins.
+	* This information is stored by LibRockModuleCore-1.0 if it is a module, otherwise from LibRockDB-1.0 if it uses that as a mixin, otherwise it keeps a variable locally.
+Arguments:
+	[optional] boolean - whether the addon should be in an active state or not. Default: not :IsActive()
+Returns:
+	boolean - whether the addon is in an active state or not.
+Example:
+	MyAddon:ToggleActive() -- switch
+	MyAddon:ToggleActive(true) -- force on
+	MyAddon:ToggleActive(false) -- force off
+-----------------------------------------------------------------------------]]
+function addon_mt___index:ToggleActive(state)
+	if state and state ~= true then
+		error(("Bad argument #2 to `ToggleActive'. Expected %q or %q, got %q."):format("boolean", "nil", type(state)), 2)
+	end
+	if LibRockModuleCore then
+		local core = LibRockModuleCore:HasModule(self)
+		if core then
+			return core:ToggleModuleActive(self, state)
+		end
+	end
+
+	local self_db = self.db
+	if self_db then
+		local self_db_raw = self_db.raw
+		if not self_db_raw then
+			error("Error saving to database with `ToggleActive'. db.raw not available.", 2)
+		end
+		local self_db_raw_disabled = self_db_raw.disabled
+		if not self_db_raw_disabled then
+			self_db_raw_disabled = newList()
+			self_db_raw.disabled = self_db_raw_disabled
+		end
+		local profile = type(self.GetProfile) == "function" and select(2, self:GetProfile()) or false
+		if state == nil then
+			state = not not self_db_raw_disabled[profile]
+		elseif (not self_db_raw_disabled[profile]) == state then
+			return
+		end
+		self_db_raw_disabled[profile] = not state or nil
+		if next(self_db_raw_disabled) == nil then
+			self_db_raw.disabled = del(self_db_raw_disabled)
+		end
+	else
+		if state == nil then
+			state = not not inactiveAddons[self]
+		elseif (not inactiveAddons[self]) == state then
+			return
+		end
+		inactiveAddons[self] = not state or nil
+	end
+
+	Rock:RecheckEnabledStates()
+
+	return state
+end
+
+local function noop() end
+
+do
+	local preconditions = setmetatable({}, weakKey)
+	local postconditions = setmetatable({}, weakKey)
+	local postconditionsOld = setmetatable({}, weakKey)
+
+	local currentMethod = nil
+
+	local function hook(object, method)
+		local object_method = object[method]
+		object[method] = function(...)
+			local pre = preconditions[object_method]
+			local post = postconditions[object_method]
+			if pre then
+				local old_currentMethod = currentMethod
+				currentMethod = method
+				pre(...)
+				currentMethod = old_currentMethod
+			end
+			if not post then
+				return object_method(...)
+			end
+			local oldFunc = postconditionsOld[object_method]
+			local old
+			if oldFunc then
+				old = newList()
+				oldFunc(old, ...)
+			end
+
+			local old_currentMethod = currentMethod
+			currentMethod = nil
+			local ret, n = newList(object_method(...))
+
+			currentMethod = method
+			if old then
+				post(old, ret, ...)
+				old = del(old)
+			else
+				post(ret, ...)
+			end
+			currentMethod = old_currentMethod
+			return unpackListAndDel(ret, 1, n)
+		end
+	end
+
+	local function precondition(object, method, func)
+		if type(object) ~= "table" then
+			error(("Bad argument #1 to `precondition'. Expected %q, got %q."):format("table", type(object)), 2)
+		end
+		if type(object[method]) ~= "function" then
+			error(("Method %q not found on object %s. Expected %q, got %q."):format(tostring(method), tostring(object), "function", type(object[method])), 2)
+		end
+		if type(func) ~= "function" then
+			error(("Bad argument #3 to `precondition'. Expected %q, got %q."):format("function", type(func)), 2)
+		end
+
+		local object_method = object[method]
+		if preconditions[object_method] then
+			error("Cannot call `preconditon' on the same method twice.", 2)
+		end
+		preconditions[object_method] = func
+
+		if not postconditions[object_method] then
+			hook(object, method)
+		end
+	end
+
+	local function postcondition(object, method, func, fillOld)
+		if type(object) ~= "table" then
+			error(("Bad argument #1 to `postcondition'. Expected %q, got %q."):format("table", type(object)), 2)
+		end
+		if type(object[method]) ~= "function" then
+			error(("Method %q not found on object %s. Expected %q, got %q."):format(tostring(method), tostring(object), "function", type(object[method])), 2)
+		end
+		if type(func) ~= "function" then
+			error(("Bad argument #3 to `postcondition'. Expected %q, got %q."):format("function", type(func)), 2)
+		end
+		if fillOld and type(fillOld) ~= "function" then
+			error(("Bad argument #4 to `postcondition'. Expected %q or %q, got %q."):format("function", "nil", type(func)), 2)
+		end
+
+		local object_method = object[method]
+		if postconditions[object_method] then
+			error("Cannot call `postcondition' on the same method twice.", 2)
+		end
+		postconditions[object_method] = func
+		postconditionsOld[object_method] = fillOld
+
+		if not preconditions[object_method] then
+			hook(object, method)
+		end
+	end
+
+	local function argCheck(value, position, ...)
+		if not currentMethod then
+			error("Cannot call `argCheck' outside of a pre/post-condition.", 2)
+		end
+		if type(position) ~= "number" then
+			error(("Bad argument #2 to `argCheck'. Expected %q, got %q"):format("number", type(position)), 2)
+		end
+		local type_value = type(value)
+		for i = 1, select('#', ...) do
+			local v = select(i, ...)
+			if type(v) ~= "string" then
+				error(("Bad argument #%d to `argCheck'. Expected %q, got %q"):format(i+1, "string", type(v)), 2)
+			end
+			if v == type_value then
+				return
+			end
+		end
+		local t = newList(...)
+		t[#t] = nil
+		for i,v in ipairs(t) do
+			t[i] = ("%q"):format(v)
+		end
+		local s
+		if #t == 0 then
+			s = ("%q"):format((...))
+		elseif #t == 1 then
+			s = ("%q or %q"):format(...)
+		else
+			s = table_concat(t, ", ") .. ", or " .. ("%q"):format(select(#t+1, ...))
+		end
+		t = del(t)
+
+		error(("Bad argument #%d to `%s'. Expected %s, got %q."):format(position, tostring(currentMethod), s, type_value), 4)
+	end
+
+	--[[---------------------------------------------------------------------------
+	Notes:
+		* Returns functions for the specified namespace based on what is provided.
+		* function types:
+		; "precondition" : to set the pre-condition for a method.
+		; "postcondition" : to set the post-condition for a method.
+		; "argCheck" : to check the type of an argument, to be executed within a pre-condition.
+		* preconditon is in the form of <tt>precondition(object, "methodName", func(self, ...))</tt>
+		* postcondition is in the form of either <tt>postcondition(object, "methodName", func(returnValues, self, ...))</tt> or <tt>postcondition(object, "methodName", func(oldValues, returnValues, self, ...), populateOld(oldValues, self, ...))</tt>
+		** returnValues is the list of return values, empty if no return values were sent.
+		** if the populateOld function is provided, then the empty oldValues table is provided and expected to be filled, and then given to the func.
+		* argCheck is in the form of <tt>argCheck(value, n, "type1" [, "type2", ...])</tt>
+		** value is the value provided to the function you're checking.
+		** n is the argument position. ''Note: 1 is the position of `self'. 2 would be the first "real" position.''
+		** the tuple of types can be any string, but specifically "nil", "boolean", "string", "number", "function", "userdata", "table", etc.
+	Arguments:
+		string - the namespace. ''Note: this doesn't necessarily have to be a string.''
+	Example:
+		local precondition, postcondition, argCheck = Rock:GetRecyclingFunctions("Stack", "precondition", "postcondition", "argCheck")
+
+		local stack = {}
+		stack.IsEmpty = function(self)
+			return self[1] == nil
+		end
+		stack.GetLength = function(self)
+			return #self
+		end
+		stack.Push = function(self, value)
+			self[#self+1] = value
+		end
+		precondition(stack, "Push", function(self, value)
+			argCheck(value, 2, "string") -- only accept strings, no other values
+		end)
+		postcondition(stack, "Push", function(old, ret, self, value)
+			assert(self:GetLength() == old.length+1)
+			assert(not self:IsEmpty())
+		end, function(old, self)
+			old.length = self:GetLength()
+		end)
+		stack.Pop = function(self)
+			local value = self[#self]
+			self[#self] = nil
+			return value
+		end
+		precondition(stack, "Pop", function(self)
+			assert(self:GetLength() >= 1)
+		end)
+		postcondition(stack, "Pop", function(old, ret, self)
+			assert(self:GetLength() == old.length-1)
+		end, function(old, self)
+			old.length = self:GetLength()
+		end)
+		stack.Peek = function(self)
+			return self[#self]
+		end
+		precondition(stack, "Peek", function(self)
+			assert(self:GetLength() >= 1)
+		end)
+		postcondition(stack, "Peek", function(old, ret, self)
+			assert(self:GetLength() == old.length)
+		end, function(old, self)
+			old.length = self:GetLength()
+		end)
+
+		local t = setmetatable({}, {__index=stack})
+		t:Push("Alpha")
+		t:Push("Bravo")
+		t:Push(5) -- error, only strings
+		assert(t:Pop() == "Bravo")
+		assert(t:Pop() == "Alpha")
+		t:Pop() -- error, out of values
+	-----------------------------------------------------------------------------]]
+	function Rock:GetContractFunctions(namespace, ...)
+		if namespace == "precondition" or namespace == "postcondition" or namespace == "argCheck" then
+			error(("Bad argument #2 to `GetContractFunctions'. Cannot be %q."):format(namespace), 2)
+		end
+		local t = newList()
+		if enableContracts then
+			for i = 1, select('#', ...) do
+				local v = select(i, ...)
+				if v == "precondition" then
+					t[i] = precondition
+				elseif v == "postcondition" then
+					t[i] = postcondition
+				elseif v == "argCheck" then
+					t[i] = argCheck
+				else
+					error(("Bad argument #%d to `GetContractFunctions'. Expected %q, %q, or %q, got %q."):format(i+2, "precondition", "postcondition", "argCheck", tostring(v)))
+				end
+			end
+		else
+			for i = 1, select('#', ...) do
+				t[i] = noop
+			end
+		end
+		return unpackListAndDel(t)
+	end
+end
+
+--[[---------------------------------------------------------------------------
+Notes:
+	* convert a revision string to a number
+Arguments:
+	string - revision string
+Returns:
+	string or number - the string given or the number retrieved from it.
+-----------------------------------------------------------------------------]]
+local function coerceRevisionToNumber(version)
+	if type(version) == "string" then
+		return tonumber(version:match("(%-?%d+)")) or version
+	else
+		return version
+	end
+end
+
+--[[---------------------------------------------------------------------------
+Notes:
+	* try to enable the standalone library specified
+Arguments:
+	string - name of the library.
+Returns:
+	boolean - whether the library is properly enabled and loadable.
+-----------------------------------------------------------------------------]]
+local function TryToEnable(addon)
+	local islod = IsAddOnLoadOnDemand(addon)
+	if islod then
+		local _, _, _, enabled = GetAddOnInfo(addon)
+		EnableAddOn(addon)
+		local _, _, _, _, loadable = GetAddOnInfo(addon)
+		if not loadable and not enabled then
+			DisableAddOn(addon)
+		end
+
+		return loadable
+	end
+end
+
+--[[---------------------------------------------------------------------------
+Notes:
+	* try to load the standalone library specified
+Arguments:
+	string - name of the library.
+Returns:
+	boolean - whether the library is loaded.
+-----------------------------------------------------------------------------]]
+local function TryToLoadStandalone(major)
+	major = major:lower()
+	if scannedLibraries[major] then
+		return
+	end
+	scannedLibraries[major] = true
+	local name, _, _, enabled, loadable, state = GetAddOnInfo(major)
+	if state == "MISSING" or not IsAddOnLoadOnDemand(major) then
+		-- backwards compatibility for X-AceLibrary
+		local field = "X-AceLibrary-" .. major
+		local loaded
+		for i = 1, GetNumAddOns() do
+			if GetAddOnMetadata(i, field) then
+				name, _, _, enabled, loadable = GetAddOnInfo(i)
+
+				loadable = (enabled and loadable) or TryToEnable(name)
+				if loadable then
+					loaded = true
+					LoadAddOn(name)
+				end
+			end
+		end
+
+		return loaded
+	elseif (enabled and loadable) or TryToEnable(major) then
+		LoadAddOn(major)
+		return true
+	else
+		return false
+	end
+end
+
+--[[---------------------------------------------------------------------------
+Notes:
+	* Return the LibStub library, casing is unimportant.
+Arguments:
+	string - name of the library.
+Returns:
+	table or nil - library
+	number - minor version
+-----------------------------------------------------------------------------]]
+local function GetLibStubLibrary(major)
+	local lib, minor = LibStub:GetLibrary(major, true)
+	if lib then
+		return lib, minor
+	end
+	major = major:lower()
+	for m, lib in LibStub:IterateLibraries() do
+		if m:lower() == major then
+			return LibStub:GetLibrary(m)
+		end
+	end
+	return nil, nil
+end
+
+local finishLibraryRegistration
+--[[---------------------------------------------------------------------------
+Notes:
+	* create a new library if the version provided is not out of date.
+Arguments:
+	string - name of the library.
+	number - version of the library.
+Returns:
+	library, oldLibrary
+	* table or nil - the library with which to manipulate
+	* table or nil - the old version of the library to upgrade from
+Example:
+	local LibMonkey, oldLib = Rock:NewLibrary("LibMonkey-1.0", 50)
+	if not LibMonkey then
+		-- opt out now, out of date
+		return
+	end
+-----------------------------------------------------------------------------]]
+function Rock:NewLibrary(major, version)
+	if type(major) ~= "string" then
+		error(("Bad argument #2 to `NewLibrary'. Expected %q, got %q."):format("string", type(major)), 2)
+	end
+	if not major:match("^Lib[A-Z][A-Za-z%d%-]*%-%d+%.%d+$") then
+		error(("Bad argument #2 to `NewLibrary'. Must match %q, got %q."):format("^Lib[A-Z][A-Za-z%d%-]*%-%d+%.%d+$", major), 2)
+	end
+	TryToLoadStandalone(major)
+	version = coerceRevisionToNumber(version)
+	if type(version) ~= "number" then
+		error(("Bad argument #3 to `NewLibrary'. Expected %q, got %q."):format("number", type(version)), 2)
+	end
+	local library, oldMinor = LibStub:GetLibrary(major, true)
+	if oldMinor and oldMinor >= version then
+		-- in case LibStub is acting funny
+		return nil, nil
+	end
+	local library, oldMinor = LibStub:NewLibrary(major, version)
+	if not library then
+		return nil, nil
+	end
+	local unitTests_major = unitTests[major]
+	if unitTests_major then
+		for k,v in pairs(unitTests_major) do
+			unitTests_major[k] = nil
+		end
+	end
+	for k, v in pairs(recycleData) do
+		v[major] = nil
+	end
+	local mixinToObject_library = mixinToObject[library]
+
+	local oldLib
+	if oldMinor then
+		-- previous version exists
+		local mixins = newList()
+		for mixin, objectSet in pairs(mixinToObject) do
+			if objectSet[library] then
+				mixins[mixin] = true
+			end
+		end
+		for mixin in pairs(mixins) do
+			mixin:Unembed(library)
+		end
+		mixins = del(mixins)
+		oldLib = newList()
+		for k, v in pairs(library) do
+			oldLib[k] = v
+			library[k] = nil
+		end
+		setmetatable(oldLib, getmetatable(library))
+		setmetatable(library, nil)
+	end
+	finishLibraryRegistration(major, version, library, figureCurrentAddon(2))
+
+	return library, oldLib
+end
+function finishLibraryRegistration(major, version, library, folder)
+	library.name = major
+
+	libraries[major] = library
+	pendingLibraries[library] = folder
+	local exportedMethods_library = exportedMethods[library]
+	if exportedMethods_library then
+		local mixinToObject_library = mixinToObject[library]
+		if mixinToObject_library then
+			for object in pairs(mixinToObject_library) do
+				for _,v in ipairs(exportedMethods_library) do
+					object[v] = nil
+				end
+			end
+		end
+		exportedMethods[library] = del(exportedMethods_library)
+	end
+	if library ~= Rock then
+		Rock:Embed(library)
+	end
+
+	frame:Show()
+end
+if not oldRock then
+	finishLibraryRegistration(MAJOR_VERSION, MINOR_VERSION, Rock, figureCurrentAddon(1))
+end
+
+-- #NODOC
+local function __removeLibrary(libName)
+	libraries[libName] = nil
+	if LibStub.libs then
+		LibStub.libs[libName] = nil
+	end
+	if LibStub.minors then
+		LibStub.minors[libName] = nil
+	end
+	local lastCount
+	repeat
+		lastCount = collectgarbage('count')
+		collectgarbage('collect')
+	until lastCount == collectgarbage('count')
+end
+local function run(_,a)
+	if a < 1/30 then
+		collectgarbage('step')
+	end
+end
+
+--[[---------------------------------------------------------------------------
+Notes:
+	* properly finalizes the library, essentially stating that it has loaded properly.
+	* This will call :OnLibraryLoad("major", library) on every other library
+	* This will also call :OnLibraryLoad("major", library) on the library provided, using every other library as the arguments.
+	* An error will occur if this is not done before ADDON_LOADED.
+Arguments:
+	string - name of the library.
+Example:
+	local LibMonkey, oldLib = Rock:NewLibrary("LibMonkey-1.0", 50)
+	if not LibMonkey then
+		-- opt out now, out of date
+		return
+	end
+	Rock:FinalizeLibrary("LibMonkey-1.0")
+-----------------------------------------------------------------------------]]
+function Rock:FinalizeLibrary(major)
+	if type(major) ~= "string" then
+		error(("Bad argument #2 to `FinalizeLibrary'. Expected %q, got %q."):format("string", type(major)), 2)
+	end
+	local library = libraries[major]
+	if not library then
+		error(("Bad argument #2 to `FinalizeLibrary'. %q is not a library."):format("string", major), 2)
+	end
+	pendingLibraries[library] = nil
+	local library_OnLibraryLoad = library.OnLibraryLoad
+	if library_OnLibraryLoad then
+		for maj, lib in LibStub:IterateLibraries() do -- for all libraries
+			if maj ~= major then
+				local success, ret = pcall(library_OnLibraryLoad, library, maj, lib)
+				if not success then
+					geterrorhandler()(ret)
+					break
+				end
+			end
+		end
+	end
+	if finalizedLibraries[library] then
+		return
+	end
+	finalizedLibraries[library] = true
+	for maj, lib in pairs(libraries) do -- just Rock libraries
+		if maj ~= major then
+			local lib_OnLibraryLoad = lib.OnLibraryLoad
+			if lib_OnLibraryLoad then
+				local success, ret = pcall(lib_OnLibraryLoad, lib, major, library)
+				if not success then
+					geterrorhandler()(ret)
+				end
+			end
+		end
+	end
+	if LibRockEvent then
+		self:DispatchEvent("LibraryLoad", major, library)
+	end
+end
+
+local function manualFinalize(major, library)
+	if libraries[major] then -- non-Rock libraries only
+		return
+	end
+	if finalizedLibraries[library] then -- don't do it twice
+		return
+	end
+	finalizedLibraries[library] = true
+	for maj, lib in pairs(libraries) do -- just Rock libraries
+		if maj ~= major then
+			local lib_OnLibraryLoad = lib.OnLibraryLoad
+			if lib_OnLibraryLoad then
+				local success, ret = pcall(lib_OnLibraryLoad, lib, major, library)
+				if not success then
+					geterrorhandler()(ret)
+				end
+			end
+		end
+	end
+	if LibRockEvent then
+		Rock:DispatchEvent("LibraryLoad", major, library)
+	end
+end
+
+--[[---------------------------------------------------------------------------
+Arguments:
+	string - name of the library.
+	[optional] boolean - whether to not load a library if it is not found. Default: false
+	[optional] boolean - whether to not error if a library is not found. Default: false
+Returns:
+	library
+	* table or nil - the library requested
+Example:
+	local LibMonkey = Rock:GetLibrary("LibMonkey-1.0")
+	-- or
+	local LibMonkey = Rock("LibMonkey-1.0")
+-----------------------------------------------------------------------------]]
+function Rock:GetLibrary(major, dontLoad, dontError)
+	if type(major) ~= "string" then
+		error(("Bad argument #2 to `GetLibrary'. Expected %q, got %q."):format("string", type(major)), 2)
+	end
+	if dontLoad and dontLoad ~= true then
+		error(("Bad argument #3 to `GetLibrary'. Expected %q or %q, got %q."):format("boolean", "nil", type(dontLoad)), 2)
+	end
+	if dontError and dontError ~= true then
+		error(("Bad argument #4 to `GetLibrary'. Expected %q or %q, got %q."):format("boolean", "nil", type(dontError)), 2)
+	end
+	if not dontLoad then
+		TryToLoadStandalone(major)
+	end
+
+	local library = GetLibStubLibrary(major)
+	if not library then
+		if dontError then
+			return nil
+		end
+		error(("Library %q not found."):format(major), 2)
+	end
+
+	return library
+end
+
+setmetatable(Rock, { __call = Rock.GetLibrary })
+
+--[[---------------------------------------------------------------------------
+Arguments:
+	string - name of the library.
+Returns:
+	boolean - whether the library exists and is a proper mixin which can be embedded.
+Example:
+	local isMixin = Rock:IsLibraryMixin("LibMonkey-1.0")
+-----------------------------------------------------------------------------]]
+function Rock:IsLibraryMixin(name)
+	local library = self:GetLibrary(name, false, true)
+	if not library then
+		return false
+	end
+	return not not exportedMethods[library]
+end
+
+--[[---------------------------------------------------------------------------
+Arguments:
+	string - name of the library.
+	[optional] boolean - whether to not load a library if it is not found. Default: false
+Returns:
+	library
+	* table or nil - the library requested
+Example:
+	local hasLibMonkey = Rock:HasLibrary("LibMonkey-1.0")
+-----------------------------------------------------------------------------]]
+function Rock:HasLibrary(major, dontLoad)
+	if type(major) ~= "string" then
+		error(("Bad argument #2 to `HasLibrary'. Expected %q, got %q."):format("string", type(major)), 2)
+	end
+	if dontLoad and dontLoad ~= true then
+		error(("Bad argument #3 to `HasLibrary'. Expected %q or %q, got %q."):format("boolean", "nil", type(dontLoad)), 2)
+	end
+	if not dontLoad then
+		TryToLoadStandalone(major)
+	end
+	return not not GetLibStubLibrary(major)
+end
+
+--[[---------------------------------------------------------------------------
+Notes:
+	* This is exported to all libraries
+Returns:
+	major, minor
+	* string - name of the library
+	* number - version of the library
+Example:
+	local major, minor = Rock:GetLibraryVersion() -- will be "LibRock-1.0", 12345
+	local major, minor = LibMonkey:GetLibraryVersion() -- will be "LibMonkey-1.0", 50
+-----------------------------------------------------------------------------]]
+function Rock:GetLibraryVersion()
+	if type(self) ~= "table" then
+		return nil, nil
+	end
+	local major
+	local name = self.name
+	if name and GetLibStubLibrary(name) == self then
+		major = name
+	else
+		for m, instance in LibStub:IterateLibraries() do
+			if instance == self then
+				major = m
+				break
+			end
+		end
+		if not major then
+			return nil, nil
+		end
+	end
+	local _, minor = GetLibStubLibrary(major)
+	return major, minor
+end
+
+--[[---------------------------------------------------------------------------
+Returns:
+	an iterator to traverse all registered libraries.
+Example:
+	for major, library in Rock:IterateLibraries() do
+		-- do something with major and library
+	end
+-----------------------------------------------------------------------------]]
+function Rock:IterateLibraries()
+	return LibStub:IterateLibraries()
+end
+
+--[[---------------------------------------------------------------------------
+Notes:
+	* This is exported to all libraries
+	* Allows you to set precisely what methods for the library to export.
+	* This automatically turns a library into a mixin.
+Arguments:
+	tuple - the list of method names to export.
+Example:
+	local LibMonkey = Rock:NewLibrary("LibMonkey-1.0", 50)
+	LibMonkey.FlingPoo = function(self)
+		return "Splat!"
+	end
+	LibMonkey:SetExportedMethods("FlingPoo")
+	-- later
+	local Darwin = Rock:NewAddon("Darwin", "LibMonkey-1.0")
+	assert(Darwin:FlingPoo() == "Splat!")
+-----------------------------------------------------------------------------]]
+function Rock:SetExportedMethods(...)
+	if exportedMethods[self] then
+		error("Cannot call `SetExportedMethods' more than once.", 2)
+	end
+	local t = newList(...)
+	if #t == 0 then
+		error("Must supply at least 1 method to `SetExportedMethods'.", 2)
+	end
+	for i,v in ipairs(t) do
+		if type(self[v]) ~= "function" then
+			error(("Bad argument #%d to `SetExportedMethods'. Method %q does not exist."):format(i+1, tostring(v)), 2)
+		end
+	end
+	exportedMethods[self] = t
+
+	local mixinToObject_library = mixinToObject[self]
+	if mixinToObject_library then
+		for object in pairs(mixinToObject_library) do
+			for _,method in ipairs(t) do
+				object[method] = self[method]
+			end
+		end
+	end
+end
+
+--[[---------------------------------------------------------------------------
+Notes:
+	* This is exported to all libraries
+	* Embeds all the methods previously set to export onto a table.
+	* This will call :OnEmbed(object) on the library if it is available.
+Arguments:
+	table - the table with which to export methods onto.
+Returns:
+	The table provided, after embedding.
+Example:
+	local LibMonkey = Rock:NewLibrary("LibMonkey-1.0", 50)
+	LibMonkey.FlingPoo = function(self)
+		return "Splat!"
+	end
+	LibMonkey:SetExportedMethods("FlingPoo")
+	-- later
+	local Darwin = {}
+	Rock("LibMonkey-1.0"):Embed(Darwin)
+	assert(Darwin:FlingPoo() == "Splat!")
+-----------------------------------------------------------------------------]]
+function Rock:Embed(object)
+	if not exportedMethods[self]  then
+		error(("Cannot call `Embed' for library %q if `SetExportedMethods' has not been called."):format(tostring(self.name)), 2)
+	end
+	if type(object) ~= "table" then
+		error(("Bad argument #2 to `Embed'. Expected %q, got %q."):format("table", type(object)), 2)
+	end
+
+	for i,v in ipairs(exportedMethods[self]) do
+		if type(self[v]) ~= "function" then
+			error(("Problem embedding method %q from library %q. Expected %q, got %q."):format(tostring(v), better_tostring(self), "function", type(self[v])))
+		end
+		object[v] = self[v]
+	end
+
+	if not mixinToObject[self] then
+		-- weak because objects come and go
+		mixinToObject[self] = setmetatable(newList(), weakKey)
+	end
+	if mixinToObject[self][object] then
+		error(("Cannot embed library %q into the same object %q more than once."):format(better_tostring(self), better_tostring(object)), 2)
+	end
+	mixinToObject[self][object] = true
+	if type(rawget(object, 'mixins')) == "table" then
+		object.mixins[self] = true
+	end
+
+	local self_OnEmbed = self.OnEmbed
+	if self_OnEmbed then
+		local success, ret = pcall(self_OnEmbed, self, object)
+		if not success then
+			geterrorhandler()(ret)
+		end
+	end
+
+	return object
+end
+
+--[[---------------------------------------------------------------------------
+Notes:
+	* This is exported to all libraries
+	* Unembeds all the methods previously set to export onto a table.
+	* This will error if the library is not embedded on the object
+	* This will call :OnUnembed(object) on the library if it is available.
+Arguments:
+	table - the table with which to export methods onto.
+Returns:
+	The table provided, after embedding.
+Example:
+	local LibMonkey = Rock:NewLibrary("LibMonkey-1.0", 50)
+	LibMonkey.FlingPoo = function(self)
+		return "Splat!"
+	end
+	LibMonkey:SetExportedMethods("FlingPoo")
+	-- later
+	local Darwin = {}
+	Rock("LibMonkey-1.0"):Embed(Darwin)
+	assert(Darwin:FlingPoo() == "Splat!")
+	Rock("LibMonkey-1.0"):Unembed(Darwin)
+	assert(Darwin.FlingPoo == nil)
+-----------------------------------------------------------------------------]]
+function Rock:Unembed(object)
+	if not exportedMethods[self]  then
+		error(("Cannot call `Unembed' for library %q if `SetExportedMethods' has not been called."):format(better_tostring(self)), 2)
+	end
+
+	if not mixinToObject[self] or not mixinToObject[self][object] then
+		error(("Cannot unembed library %q from object %q, since it is not embedded originally."):format(better_tostring(self), better_tostring(object)), 2)
+	end
+	local mixinToObject_self = mixinToObject[self]
+	mixinToObject_self[object] = nil
+	if not next(mixinToObject_self) then
+		mixinToObject[self] = del(mixinToObject_self)
+	end
+
+	local mixin_OnUnembed = self.OnUnembed
+	if mixin_OnUnembed then
+		local success, ret = pcall(mixin_OnUnembed, self, object)
+		if not success then
+			geterrorhandler()(ret)
+		end
+	end
+
+	for i,v in ipairs(exportedMethods[self]) do
+		object[v] = nil
+	end
+end
+
+local function embedAce2Mixin(mixin, object)
+	if not mixinToObject[mixin] then
+		mixinToObject[mixin] = setmetatable(newList(), weakKey)
+	end
+	mixinToObject[mixin][object] = true
+	mixin:embed(object)
+end
+
+local function embedLibStubMixin(mixin, object)
+	if not mixinToObject[mixin] then
+		mixinToObject[mixin] = setmetatable(newList(), weakKey)
+	end
+	mixinToObject[mixin][object] = true
+	mixin:Embed(object)
+end
+
+--[[---------------------------------------------------------------------------
+Notes:
+	* create a new addon with the specified name.
+Arguments:
+	string - name of the addon.
+	tuple - list of mixins with which to embed into this addon.
+Returns:
+	addon
+	* table - the addon with which to manipulate
+Example:
+	local MyAddon = Rock:NewAddon("MyAddon", "Mixin-1.0", "OtherMixin-2.0")
+-----------------------------------------------------------------------------]]
+function Rock:NewAddon(name, ...)
+	if type(name) ~= "string" then
+		error(("Bad argument #2 to `NewAddon'. Expected %q, got %q"):format("string", type(name)), 2)
+	end
+	if name:match("^Lib[A-Z]") then
+		error(("Bad argument #2 to `NewAddon'. Cannot start with %q, got %q."):format("Lib", name), 2)
+	end
+	if self == Rock and name:match("_") then
+		error(("Bad argument #2 to `NewAddon'. Cannot contain underscores, got %q."):format(name), 2)
+	end
+
+	if addons[name] then
+		error(("Bad argument #2 to `NewAddon'. Addon %q already created."):format(name), 2)
+	end
+	local addon = setmetatable(newList(), addon_mt)
+	addon.name = name
+
+	local mixinSet = newList()
+
+	for i = 1, select('#', ...) do
+		local libName = select(i, ...)
+		if mixinSet[libName] then
+			error(("Bad argument #%d to `NewAddon'. %q already stated."):format(i+2, tostring(libName)), 2)
+		end
+		mixinSet[libName] = true
+		TryToLoadStandalone(libName)
+		local library = Rock:GetLibrary(libName, false, true)
+		if not library then
+			error(("Bad argument #%d to `NewAddon'. Library %q is not found."):format(i+2, tostring(libName)), 2)
+		end
+		
+		local style = 'rock'
+
+		if not exportedMethods[library] then
+			local good = false
+			if AceLibrary then
+				local AceOO = AceLibrary:HasInstance("AceOO-2.0", false) and AceLibrary("AceOO-2.0")
+				if AceOO.inherits(library, AceOO.Mixin) then
+					good = true
+					style = 'ace2'
+				end
+			end
+			if not good and type(rawget(library, 'Embed')) == "function" then
+				good = true
+				style = 'libstub'
+			end
+			if not good then
+				error(("Bad argument #%d to `NewAddon'. Library %q is not a mixin."):format(i+2, tostring(libName)), 2)
+			end
+		end
+
+		if library == Rock then
+			error(("Bad argument #%d to `NewAddon'. Cannot use %q as a mixin."):format(i+2, tostring(libName)), 2)
+		end
+
+		if style == 'rock' then
+			library:Embed(addon)
+		elseif style == 'ace2' then
+			embedAce2Mixin(library, addon)
+		elseif style == 'libstub' then
+			embedLibStubMixin(library, addon)
+		end
+	end
+
+	mixinSet = del(mixinSet)
+
+	addons[name] = addon
+	pendingAddons[#pendingAddons+1] = addon
+	pendingAddonsEnable[#pendingAddonsEnable+1] = addon
+	addonToFolder[addon] = figureCurrentAddon(self == Rock and 2 or 4)
+
+	frame:Show()
+
+	return addon
+end
+
+--[[---------------------------------------------------------------------------
+Arguments:
+	string - name of the addon.
+Returns:
+	addon
+	* table or nil - the addon requested
+Example:
+	local MyAddon = Rock:GetAddon("MyAddon")
+-----------------------------------------------------------------------------]]
+function Rock:GetAddon(name)
+	if type(name) ~= "string" then
+		return nil
+	end
+	local addon = addons[name]
+	if addon then
+		return addon
+	end
+	name = name:lower()
+	for k, v in pairs(addons) do
+		if k:lower() == name then
+			return v
+		end
+	end
+	return nil
+end
+
+--[[---------------------------------------------------------------------------
+Arguments:
+	string or table - name of the addon or the addon itself.
+Returns:
+	boolean - whether the addon requested exists.
+Example:
+	local hasMyAddon = Rock:HasAddon("MyAddon")
+	-- or
+	local hasMyAddon = Rock:HasAddon(MyAddon)
+-----------------------------------------------------------------------------]]
+function Rock:HasAddon(name)
+	if type(name) == "string" then
+		local addon = addons[name]
+		if addon then
+			return true
+		end
+		name = name:lower()
+		for k, v in pairs(addons) do
+			if k:lower() == name then
+				return true
+			end
+		end
+	elseif type(name) == "table" then
+		for k,v in pairs(addons) do
+			if v == name then
+				return true
+			end
+		end
+	end
+	return false
+end
+
+--[[---------------------------------------------------------------------------
+Returns:
+	an iterator to traverse all addons created with Rock.
+Example:
+	for name, addon in Rock:IterateAddons() do
+		-- do something with name and addon
+	end
+-----------------------------------------------------------------------------]]
+function Rock:IterateAddons()
+	return pairs(addons)
+end
+
+--[[---------------------------------------------------------------------------
+Arguments:
+	string - major version of the mixin library
+Returns:
+	an iterator to traverse all objects that the given mixin has embedded into
+Example:
+	local LibMonkey = Rock:NewLibrary("LibMonkey-1.0")
+	local Darwin = Rock:NewAddon("Darwin", "LibMonkey-1.0")
+	for object in LibMonkey:IterateMixinObjects("LibMonkey-1.0") do
+		assert(object == Darwin)
+	end
+-----------------------------------------------------------------------------]]
+function Rock:IterateMixinObjects(mixinName)
+	local mixin
+	if type(mixinName) == "table" then
+		mixin = mixinName
+	else
+		if type(mixinName) ~= "string" then
+			error(("Bad argument #2 to `IterateMixinObjects'. Expected %q or %q, got %q."):format("table", "string", type(mixinName)), 2)
+		end
+		mixin = libraries[mixinName]
+	end
+	local mixinToObject_mixin = mixinToObject[mixin]
+	if not mixinToObject_mixin then
+		return noop
+	end
+	return pairs(mixinToObject_mixin)
+end
+
+local function iter(object, mixin)
+	mixin = next(mixinToObject, mixin)
+	if not mixin then
+		return nil
+	elseif mixinToObject[mixin][object] then
+		return mixin
+	end
+	return iter(object, mixin) -- try next mixin
+end
+--[[---------------------------------------------------------------------------
+Returns:
+	an iterator to traverse all mixins that an object has embedded
+Example:
+	local LibMonkey = Rock:NewLibrary("LibMonkey-1.0")
+	local Darwin = Rock:NewAddon("Darwin", "LibMonkey-1.0")
+	for mixin in Rock:IterateObjectMixins(Darwin) do
+		assert(mixin == LibMonkey)
+	end
+-----------------------------------------------------------------------------]]
+function Rock:IterateObjectMixins(object)
+	if type(object) ~= "table" then
+		error(("Bad argument #2 to `IterateObjectMixins'. Expected %q, got %q."):format("table", type(object)), 2)
+	end
+	return iter, object, nil
+end
+
+--[[---------------------------------------------------------------------------
+Arguments:
+	table - the object to check
+	string - the mixin to check
+Returns:
+	boolean - whether the object has the given mixin embedded into it.
+Example:
+	local LibMonkey = Rock:NewLibrary("LibMonkey-1.0")
+	local Darwin = Rock:NewAddon("Darwin", "LibMonkey-1.0")
+	assert(Rock:DoesObjectUseMixin(Darwin, "LibMonkey-1.0"))
+-----------------------------------------------------------------------------]]
+function Rock:DoesObjectUseMixin(object, mixinName)
+	if type(object) ~= "table" then
+		error(("Bad argument #2 to `IterateObjectMixins'. Expected %q, got %q."):format("table", type(object)), 2)
+	end
+	local mixin
+	if type(mixinName) == "table" then
+		mixin = mixinName
+	else
+		if type(mixinName) ~= "string" then
+			error(("Bad argument #3 to `IterateMiDoesObjectUseMixininObjects'. Expected %q or %q, got %q."):format("table", "string", type(mixinName)), 2)
+		end
+		mixin = libraries[mixinName]
+	end
+	if not mixin then
+		return false
+	end
+
+	local mixinToObject_mixin = mixinToObject[mixin]
+	if not mixinToObject_mixin then
+		return false
+	end
+	return not not mixinToObject_mixin[object]
+end
+
+Rock.UID_NUM = oldRock and oldRock.UID_NUM or 0
+--[[---------------------------------------------------------------------------
+Notes:
+	* This UID is not unique across sessions. If you save a UID in a saved variable, the same UID can be generated in another session.
+Returns:
+	number - a unique number.
+Example:
+	local UID = Rock:GetUID()
+-----------------------------------------------------------------------------]]
+function Rock:GetUID()
+	local num = Rock.UID_NUM + 1
+	Rock.UID_NUM = num
+	return num
+end
+
+local function unobfuscateEmail(email)
+	return email:gsub(" AT ", "@"):gsub(" DOT ", ".")
+end
+local function fix(char)
+	return ("%%%02x"):format(char:byte())
+end
+local function urlencode(text)
+	return text:gsub("[^0-9A-Za-z]", fix)
+end
+
+local url
+local function makeURLFrame()
+	makeURLFrame = nil
+	local function bumpFrameLevels(frame, amount)
+		frame:SetFrameLevel(frame:GetFrameLevel()+amount)
+		local children = newList(frame:GetChildren())
+		for _,v in ipairs(children) do
+			bumpFrameLevels(v, amount)
+		end
+		children = del(children)
+	end
+	-- some code borrowed from Prat here
+	StaticPopupDialogs["ROCK_SHOW_URL"] = {
+		text = not IsMacClient() and L["Press Ctrl-C to copy, then Alt-Tab out of the game, open your favorite web browser, and paste the link into the address bar."] or L["Press Cmd-C to copy, then Cmd-Tab out of the game, open your favorite web browser, and paste the link into the address bar."],
+		button2 = ACCEPT,
+		hasEditBox = 1,
+		hasWideEditBox = 1,
+		showAlert = 1, -- HACK : it's the only way I found to make de StaticPopup have sufficient width to show WideEditBox :(
+
+		OnShow = function()
+			local editBox = _G[this:GetName() .. "WideEditBox"]
+			editBox:SetText(url)
+			editBox:SetFocus()
+			editBox:HighlightText(0)
+			editBox:SetScript("OnTextChanged", function() StaticPopup_EditBoxOnTextChanged() end)
+
+			local button = _G[this:GetName() .. "Button2"]
+			button:ClearAllPoints()
+			button:SetWidth(200)
+			button:SetPoint("CENTER", editBox, "CENTER", 0, -30)
+
+			_G[this:GetName() .. "AlertIcon"]:Hide()  -- HACK : we hide the false AlertIcon
+			this:SetFrameStrata("FULLSCREEN_DIALOG")
+			bumpFrameLevels(this, 30)
+		end,
+		OnHide = function()
+			local editBox = _G[this:GetName() .. "WideEditBox"]
+			editBox:SetScript("OnTextChanged", nil)
+			this:SetFrameStrata("DIALOG")
+			bumpFrameLevels(this, -30)
+		end,
+		OnAccept = function() end,
+		OnCancel = function() end,
+		EditBoxOnEscapePressed = function() this:GetParent():Hide() end,
+		EditBoxOnTextChanged = function()
+			this:SetText(url)
+			this:SetFocus()
+			this:HighlightText(0)
+		end,
+		timeout = 0,
+		whileDead = 1,
+		hideOnEscape = 1
+	}
+end
+
+function OpenDonationFrame(self)
+	if makeURLFrame then
+		makeURLFrame()
+	end
+
+	local donate = self.donate
+	if type(donate) ~= "string" then
+		donate = "Wowace"
+	end
+	local style, data = (":"):split(donate, 2)
+	style = style:lower()
+	if style ~= "website" and style ~= "paypal" then
+		style = "wowace"
+	end
+	if style == "wowace" then
+		url = "http://www.wowace.com/wiki/Donations"
+	elseif style == "website" then
+		url = data
+	else -- PayPal
+		local text = "https://www.paypal.com/cgi-bin/webscr?cmd=_xclick&business=" .. urlencode(unobfuscateEmail(data))
+		local name
+		if type(self.title) == "string" then
+			name = self.title
+		elseif type(self.name) == "string" then
+			name = self.name
+		end
+		if name == MAJOR_VERSION then
+			name = "Rock"
+		end
+		if name then
+			name = name:gsub("|c%x%x%x%x%x%x%x%x", ""):gsub("|r", "")
+			text = text .. "&item_name=" .. urlencode(name)
+		end
+		url = text
+	end
+
+	StaticPopup_Show("ROCK_SHOW_URL")
+end
+function OpenIssueFrame(self)
+	if makeURLFrame then
+		makeURLFrame()
+	end
+
+	local issueTracker = self.issueTracker
+	if type(issueTracker) ~= "string" then
+		return
+	end
+	local style, data = (":"):split(issueTracker, 2)
+	style = style:lower()
+	if style ~= "website" and style ~= "wowace" then
+		return
+	end
+	if style == "wowace" then
+		url = "http://jira.wowace.com/secure/CreateIssue.jspa?pid=" .. data
+	elseif style == "website" then
+		url = data
+	end
+
+	StaticPopup_Show("ROCK_SHOW_URL")
+end
+local function donate_hidden(addon)
+	return type(addon.donate) ~= "string"
+end
+
+local function issue_hidden(addon)
+	return type(addon.issueTracker) ~= "string"
+end
+
+-- #NODOC
+function Rock:GetRockConfigOptions(addon)
+	return 'active', {
+		type = 'boolean',
+		name = L["Enabled"],
+		desc = L["Enable or disable this addon."],
+		get = 'IsActive',
+		set = 'ToggleActive',
+		handler = addon,
+		order = -1,
+	}, 'donate', {
+		type = 'execute',
+		name = L["Give donation"],
+		buttonText = L["Donate"],
+		desc = L["Give a much-needed donation to the author of this addon."],
+		func = OpenDonationFrame,
+		hidden = donate_hidden,
+		passValue = addon,
+		order = -2,
+	}, 'issue', {
+		type = 'execute',
+		name = L["File issue"],
+		buttonText = L["Report"],
+		desc = L["File a bug or request a new feature or an improvement to this addon."],
+		func = OpenIssueFrame,
+		hidden = issue_hidden,
+		passValue = addon,
+		order = -3,
+	}
+end
+
+local function initAddon(addon, name)
+	name = addonToFolder[addon] or name or ""
+	-- TOC checks
+	if addon.title == nil then
+		addon.title = GetAddOnMetadata(name, "Title")
+	end
+	if type(addon.title) == "string" then
+		addon.title = addon.title:gsub("|c%x%x%x%x%x%x%x%x", ""):gsub("|r", ""):gsub("%-Rock%-$", ""):trim()
+	end
+	if addon.notes == nil then
+		addon.notes = GetAddOnMetadata(name, "Notes")
+	end
+	if type(addon.notes) == "string" then
+		addon.notes = addon.notes:trim()
+	end
+	if addon.version == nil then
+		addon.version = GetAddOnMetadata(name, "Version")
+	end
+	if type(addon.version) == "string" then
+		addon.version = addon.version:trim()
+	end
+	if addon.author == nil then
+		addon.author = GetAddOnMetadata(name, "Author")
+	end
+	if type(addon.author) == "string" then
+		addon.author = addon.author:trim()
+	end
+	if addon.credits == nil then
+		addon.credits = GetAddOnMetadata(name, "X-Credits")
+	end
+	if type(addon.credits) == "string" then
+		addon.credits = addon.credits:trim()
+	end
+	if addon.donate == nil then
+		addon.donate = GetAddOnMetadata(name, "X-Donate")
+	end
+	if type(addon.donate) == "string" then
+		addon.donate = addon.donate:trim()
+	end
+	if addon.issueTracker == nil then
+		addon.issueTracker = GetAddOnMetadata(name, "X-IssueTracker")
+	end
+	if type(addon.issueTracker) == "string" then
+		addon.issueTracker = addon.issueTracker:trim()
+	end
+	if addon.category == nil then
+		addon.category = GetAddOnMetadata(name, "X-Category")
+	end
+	if type(addon.category) == "string" then
+		addon.category = addon.category:trim()
+	end
+	if addon.email == nil then
+		addon.email = GetAddOnMetadata(name, "X-eMail") or GetAddOnMetadata(name, "X-Email")
+	end
+	if type(addon.email) == "string" then
+		addon.email = addon.email:trim()
+	end
+	if addon.license == nil then
+		addon.license = GetAddOnMetadata(name, "X-License")
+	end
+	if type(addon.license) == "string" then
+		addon.license = addon.license:trim()
+	end
+	if addon.website == nil then
+		addon.website = GetAddOnMetadata(name, "X-Website")
+	end
+	if type(addon.website) == "string" then
+		addon.website = addon.website:trim()
+	end
+
+	for mixin in Rock:IterateObjectMixins(addon) do
+		local mixin_OnEmbedInitialize = mixin.OnEmbedInitialize
+		if mixin_OnEmbedInitialize then
+			local success, ret = pcall(mixin_OnEmbedInitialize, mixin, addon)
+			if not success then
+				geterrorhandler()(ret)
+			end
+		end
+	end
+
+	local addon_OnInitialize = addon.OnInitialize
+	if addon_OnInitialize then
+		local success, ret = pcall(addon_OnInitialize, addon)
+		if not success then
+			geterrorhandler()(ret)
+		end
+	end
+
+	if LibRockEvent then
+		Rock:DispatchEvent("AddonInitialized", addon)
+	end
+end
+
+
+local function manualEnable(addon)
+	for i,v in ipairs(pendingAddons) do
+		if v == addon then
+			return
+		end
+	end
+	if currentlyEnabledAddons[addon] then
+		return false
+	end
+	currentlyEnabledAddons[addon] = true
+
+	local first = not addonsAlreadyEnabled[addon]
+	addonsAlreadyEnabled[addon] = true
+
+	for mixin in Rock:IterateObjectMixins(addon) do
+		local mixin_OnEmbedEnable = mixin.OnEmbedEnable
+		if mixin_OnEmbedEnable then
+			local success, ret = pcall(mixin_OnEmbedEnable, mixin, addon, first)
+			if not success then
+				geterrorhandler()(ret)
+			end
+		end
+	end
+	local addon_OnEnable = addon.OnEnable
+	if addon_OnEnable then
+		local success, ret = pcall(addon_OnEnable, addon, first)
+		if not success then
+			geterrorhandler()(ret)
+		end
+	end
+
+	if LibRockEvent then
+		Rock:DispatchEvent("AddonEnabled", addon, first)
+	end
+
+	return true, first
+end
+
+local function manualDisable(addon)
+	if not currentlyEnabledAddons[addon] then
+		return false
+	end
+	currentlyEnabledAddons[addon] = nil
+
+	for mixin in Rock:IterateObjectMixins(addon) do
+		local mixin_OnEmbedDisable = mixin.OnEmbedDisable
+		if mixin_OnEmbedDisable then
+			local success, ret = pcall(mixin_OnEmbedDisable, mixin, addon)
+			if not success then
+				geterrorhandler()(ret)
+			end
+		end
+	end
+	local addon_OnDisable = addon.OnDisable
+	if addon_OnDisable then
+		local success, ret = pcall(addon_OnDisable, addon)
+		if not success then
+			geterrorhandler()(ret)
+		end
+	end
+
+	if LibRockEvent then
+		Rock:DispatchEvent("AddonDisabled", addon)
+	end
+	return true
+end
+
+local function enableAddon(addon)
+	for i,v in ipairs(pendingAddons) do
+		if v == addon then
+			return
+		end
+	end
+	if addon_mt___index.IsActive(addon) then
+		manualEnable(addon)
+	end
+end
+
+-- #NODOC
+-- This is used by internal Rock libraries after updating the active state.
+function Rock:RecheckEnabledStates()
+	local changed = false
+	for _,addon in pairs(addons) do
+		local good = true
+		for _,a in ipairs(pendingAddonsEnable) do
+			if addon == a then
+				good = false
+				break
+			end
+		end
+		if good then
+			if addon_mt___index.IsActive(addon) then
+				if manualEnable(addon) then
+					changed = true
+				end
+			else
+				if manualDisable(addon) then
+					changed = true
+				end
+			end
+		end
+	end
+	if changed then
+		return self:RecheckEnabledStates()
+	end
+end
+
+frame:UnregisterAllEvents()
+frame:RegisterEvent("ADDON_LOADED")
+frame:RegisterEvent("PLAYER_LOGIN")
+local function runMainAddonLoadedChunk(name)
+	local tmp = newList()
+	tmp, pendingAddons = pendingAddons, tmp
+	for i, addon in ipairs(tmp) do
+		local folder = addonToFolder[addon]
+		if name and folder and not foldersLoaded[folder] then
+			for j = i, #tmp do
+				pendingAddons[#pendingAddons+1] = tmp[j]
+				tmp[j] = nil
+			end
+			break
+		end
+		initAddon(addon, name)
+	end
+
+	if IsLoggedIn() then
+		for i, addon in ipairs(tmp) do
+			for j, v in ipairs(pendingAddonsEnable) do
+				if v == addon then
+					table_remove(pendingAddonsEnable, i)
+					break
+				end
+			end
+			enableAddon(addon)
+		end
+		for i, addon in ipairs(pendingAddonsEnable) do
+			local good = true
+			for j, v in ipairs(pendingAddons) do
+				if v == addon then
+					good = false
+					break
+				end
+			end
+			if not good then
+				break
+			end
+			pendingAddonsEnable[i] = nil
+			enableAddon(addon)
+		end
+	end
+	tmp = del(tmp)
+	for library, addonName in pairs(pendingLibraries) do
+		if not name or foldersLoaded[addonName] then
+			local success, ret = pcall(error, ("Library %q not finalized before ADDON_LOADED."):format(better_tostring(library)), 3)
+			geterrorhandler()(ret)
+			Rock:FinalizeLibrary((library:GetLibraryVersion()))
+		end
+	end
+
+	if isStandalone then
+		local LibRock_1_0DB = _G.LibRock_1_0DB
+		if type(LibRock_1_0DB) ~= "table" then
+			LibRock_1_0DB = {}
+			_G.LibRock_1_0DB = LibRock_1_0DB
+		end
+		if type(LibRock_1_0DB.unitTests) ~= "table" then
+			LibRock_1_0DB.unitTests = {}
+		end
+		enableContracts = LibRock_1_0DB.contracts or false
+		unitTestDB = LibRock_1_0DB.unitTests
+		for namespace, data in pairs(unitTests) do
+			if not unitTestDB[namespace] then
+				if data then
+					del(data)
+					unitTests[namespace] = false
+				end
+			elseif data and (not name or data.addon == name) then
+				local stats = newList()
+				for i,v in ipairs(data) do
+					data[i] = nil
+
+					local libs = newList()
+					for k,v in pairs(libraries) do
+						libs[k] = v
+					end
+
+					local success, ret = pcall(v)
+					if not success then
+						geterrorhandler()(ret)
+						stats[i] = ret
+					else
+						stats[i] = false
+					end
+
+					for k in pairs(libraries) do
+						if not libs[k] then
+							__removeLibrary(k)
+						end
+					end
+					libs = del(libs)
+
+					local lastCount
+					repeat
+						lastCount = collectgarbage('count')
+						collectgarbage('collect')
+					until lastCount == collectgarbage('count')
+				end
+				del(data)
+				unitTests[namespace] = false
+				if #stats >= 1 then
+					local pass, fail = 0, 0
+					for i,v in ipairs(stats) do
+						if v then
+							fail = fail + 1
+						else
+							pass = pass + 1
+						end
+					end
+
+					local color
+					if fail == 0 then
+						_G.DEFAULT_CHAT_FRAME:AddMessage(("|cff00ff00%s: %d unit test(s) passed."):format(namespace, pass))
+					elseif pass > 0 then
+						_G.DEFAULT_CHAT_FRAME:AddMessage(("|cffff0000%s: %d unit test(s) passed, %d unit test(s) failed."):format(namespace, pass, fail))
+					else
+						_G.DEFAULT_CHAT_FRAME:AddMessage(("|cffff0000%s: %d unit test(s) failed."):format(namespace, fail))
+					end
+					for i,v in ipairs(stats) do
+						if v then
+							_G.DEFAULT_CHAT_FRAME:AddMessage(("|cffff0000%s|r"):format(tostring(v)))
+						end
+					end
+					if fail > 0 then
+						_G.DEFAULT_CHAT_FRAME:AddMessage("|cffff0000----------|r")
+					end
+				end
+				stats = del(stats)
+			end
+		end
+	end
+	if isStandalone and name == MAJOR_VERSION then
+		Rock("LibRockEvent-1.0", false, true) -- load if possible
+		Rock("LibRockConsole-1.0", false, true) -- load if possible - I like the default chat commands
+		Rock("LibRockComm-1.0", false, true) -- load if possible - has version checking and the like
+		Rock("LibRockConfig-1.0", false, true) -- load if possible - LibRock-1.0 registers with it.
+	end
+
+	for major, library in LibStub:IterateLibraries() do
+		manualFinalize(major, library)
+	end
+
+	if IsLoggedIn() then
+		collectgarbage('collect')
+	end
+end
+
+frame:Show()
+frame:SetScript("OnUpdate", function(this, elapsed)
+	-- capture all un-initialized addons.
+	runMainAddonLoadedChunk()
+	collectgarbage('collect')
+	this:SetScript("OnUpdate", run)
+end)
+frame:SetScript("OnEvent", function(this, event, ...)
+	if event == "ADDON_LOADED" then
+		-- this creates a new table and flushes the old in case someone LoDs an addon inside ADDON_LOADED.
+		local name = ...
+		foldersLoaded[name] = true
+		runMainAddonLoadedChunk(name)
+		frame:Show()
+	elseif event == "PLAYER_LOGIN" then
+		for i, addon in ipairs(pendingAddonsEnable) do
+			local good = true
+			for _, a in ipairs(pendingAddons) do
+				if a == addon then
+					good = false
+					break
+				end
+			end
+			if good then
+				pendingAddonsEnable[i] = nil
+				enableAddon(addon)
+			end
+		end
+		collectgarbage('collect')
+	end
+end)
+
+Rock:SetExportedMethods("SetExportedMethods", "Embed", "Unembed", "GetLibraryVersion")
+
+Rock:FinalizeLibrary(MAJOR_VERSION)
+
+for major, library in LibStub:IterateLibraries() do
+	manualFinalize(major, library)
+end
+
+Rock:AddUnitTest(MAJOR_VERSION, function()
+	-- test recycling
+	local newList, newDict, newSet, del = Rock:GetRecyclingFunctions(MAJOR_VERSION .. "_UnitTest", "newList", "newDict", "newSet", "del", "Debug")
+	local t = newList("Alpha", "Bravo", "Charlie")
+	assert(t[1] == "Alpha")
+	assert(t[2] == "Bravo")
+	assert(t[3] == "Charlie")
+	t = del(t)
+	t = newList("Alpha", "Bravo", "Charlie")
+	-- check recycled table
+	assert(t[1] == "Alpha")
+	assert(t[2] == "Bravo")
+	assert(t[3] == "Charlie")
+	t = del(t)
+	t = newDict("Alpha", "Bravo", "Charlie", "Delta")
+	assert(t.Alpha == "Bravo")
+	assert(t.Charlie == "Delta")
+	t = del(t)
+	t = newSet("Alpha", "Bravo", "Charlie")
+	assert(t.Alpha)
+	assert(t.Bravo)
+	assert(t.Charlie)
+	t = del(t)
+
+	local debug = recycleData.debugPools[MAJOR_VERSION .. "_UnitTest"]
+	assert(debug.num == 0)
+	t = newList()
+	assert(debug.num == 1)
+	t[1] = newList()
+	assert(debug.num == 2)
+	t[2] = newList()
+	assert(debug.num == 3)
+	t[1] = del(t[1])
+	assert(debug.num == 2)
+	t[2] = del(t[2])
+	assert(debug.num == 1)
+	t = del(t)
+	assert(debug.num == 0)
+end)
+
+Rock:AddUnitTest(MAJOR_VERSION, function()
+	-- test :GetUID()
+	local t = {}
+	for i = 1, 10000 do
+		local uid = Rock:GetUID()
+		if t[i] then
+			error(("UID match for iteration %d, UID %s"):format(i, uid))
+		end
+		t[i] = true
+	end
+end)
+
+Rock:AddUnitTest(MAJOR_VERSION, function()
+	-- test basic creation and deletion
+	assert(not LibStub:GetLibrary("LibRockFakeLib-1.0", true))
+	assert(not Rock:HasLibrary("LibRockFakeLib-1.0"))
+	local lib = Rock:NewLibrary("LibRockFakeLib-1.0", 1)
+	Rock:FinalizeLibrary("LibRockFakeLib-1.0")
+	lib = nil
+	assert(LibStub:GetLibrary("LibRockFakeLib-1.0", true))
+	assert(Rock:HasLibrary("LibRockFakeLib-1.0"))
+	local good = false
+	for _, lib in pairs(libraries) do
+		if lib.name == "LibRockFakeLib-1.0" then
+			good = true
+			break
+		end
+	end
+	assert(good)
+	__removeLibrary("LibRockFakeLib-1.0")
+	for _, lib in pairs(libraries) do
+		assert(lib.name ~= "LibRockFakeLib-1.0")
+	end
+	assert(not LibStub:GetLibrary("LibRockFakeLib-1.0", true))
+	assert(not Rock:HasLibrary("LibRockFakeLib-1.0"))
+end)
+
+Rock:AddUnitTest(MAJOR_VERSION, function()
+	-- test library creation and the like
+	assert(not Rock:HasLibrary("LibRockFakeLib-1.0"))
+	for name in Rock:IterateLibraries() do
+		assert(name ~= "LibRockFakeLib-1.0")
+	end
+
+	local myLib, oldLib = Rock:NewLibrary("LibRockFakeLib-1.0", 1)
+	assert(myLib)
+	assert(myLib.name == "LibRockFakeLib-1.0")
+	assert(not oldLib)
+
+	assert(myLib:GetLibraryVersion() == "LibRockFakeLib-1.0")
+	assert(select(2, myLib:GetLibraryVersion()) == 1)
+
+	local good = false
+	for name in Rock:IterateLibraries() do
+		if name == "LibRockFakeLib-1.0" then
+			good = true
+			break
+		end
+	end
+	assert(good)
+	assert(Rock:HasLibrary("LibRockFakeLib-1.0"))
+	assert(Rock:GetLibrary("LibRockFakeLib-1.0") == myLib)
+	assert(Rock("LibRockFakeLib-1.0") == myLib)
+
+	assert(not Rock:IsLibraryMixin("LibRockFakeLib-1.0"))
+	function myLib:DoSomething()
+		return "Something"
+	end
+	myLib:SetExportedMethods("DoSomething")
+	assert(Rock:IsLibraryMixin("LibRockFakeLib-1.0"))
+	local t = {}
+	assert(not Rock:DoesObjectUseMixin(t, "LibRockFakeLib-1.0"))
+	assert(not t.DoSomething)
+	for mixin in Rock:IterateObjectMixins(t) do
+		assert(false)
+	end
+	for object in Rock:IterateMixinObjects("LibRockFakeLib-1.0") do
+		assert(false)
+	end
+	myLib:Embed(t)
+	assert(t:DoSomething() == "Something")
+	assert(Rock:DoesObjectUseMixin(t, "LibRockFakeLib-1.0"))
+	for mixin in Rock:IterateObjectMixins(t) do
+		assert(mixin == myLib)
+	end
+	for object in Rock:IterateMixinObjects("LibRockFakeLib-1.0") do
+		assert(object == t)
+	end
+
+	Rock:FinalizeLibrary("LibRockFakeLib-1.0")
+
+	local myNewLib, oldLib = Rock:NewLibrary("LibRockFakeLib-1.0", 2)
+	assert(myNewLib == myLib)
+	assert(oldLib)
+	assert(Rock:GetLibrary("LibRockFakeLib-1.0") == myLib)
+
+	function myLib:DoSomething()
+		return "Something else"
+	end
+	function myLib:TrySomething()
+		return "Blah"
+	end
+	myLib:SetExportedMethods("DoSomething", "TrySomething")
+	assert(Rock:IsLibraryMixin("LibRockFakeLib-1.0"))
+	assert(t:DoSomething() == "Something else")
+	assert(t:TrySomething() == "Blah")
+	assert(Rock:DoesObjectUseMixin(t, "LibRockFakeLib-1.0"))
+	for mixin in Rock:IterateObjectMixins(t) do
+		assert(mixin == myLib)
+	end
+	for object in Rock:IterateMixinObjects("LibRockFakeLib-1.0") do
+		assert(object == t)
+	end
+
+	Rock:FinalizeLibrary("LibRockFakeLib-1.0")
+
+	local myNewLib, oldLib = Rock:NewLibrary("LibRockFakeLib-1.0", 3)
+	assert(myNewLib == myLib)
+	assert(oldLib)
+	assert(Rock:GetLibrary("LibRockFakeLib-1.0") == myLib)
+
+	function myLib:DoSomething()
+		return "Something"
+	end
+	myLib:SetExportedMethods("DoSomething")
+	assert(Rock:IsLibraryMixin("LibRockFakeLib-1.0"))
+	assert(t:DoSomething() == "Something")
+	assert(t.TrySomething == nil)
+	assert(Rock:DoesObjectUseMixin(t, "LibRockFakeLib-1.0"))
+	for mixin in Rock:IterateObjectMixins(t) do
+		assert(mixin == myLib)
+	end
+	for object in Rock:IterateMixinObjects("LibRockFakeLib-1.0") do
+		assert(object == t)
+	end
+
+	Rock:FinalizeLibrary("LibRockFakeLib-1.0")
+
+	assert(not Rock:NewLibrary("LibRockFakeLib-1.0", 2)) -- out of date
+	assert(not Rock:NewLibrary("LibRockFakeLib-1.0", 3)) -- same revision
+end)
+
+Rock:AddUnitTest(MAJOR_VERSION, function()
+	assert(not Rock:HasAddon("RockFakeAddon"))
+	for name in Rock:IterateAddons() do
+		assert(name ~= "RockFakeAddon")
+	end
+
+	local myAddon = Rock:NewAddon("RockFakeAddon")
+
+	assert(myAddon)
+	assert(myAddon.name == "RockFakeAddon")
+
+	local good = false
+	for name in Rock:IterateAddons() do
+		if name == "RockFakeAddon" then
+			good = true
+			break
+		end
+	end
+	assert(good)
+	assert(Rock:HasAddon("RockFakeAddon"))
+	assert(Rock:GetAddon("RockFakeAddon") == myAddon)
+end)
+
+Rock:AddUnitTest(MAJOR_VERSION, function()
+	-- test :OnLibraryLoad
+	local lib = Rock:NewLibrary("LibRockFakeLib-1.0", 1)
+	local triggered = false
+	function lib:OnLibraryLoad(major, instance)
+		if major == "LibRockFakeLib-2.0" then
+			triggered = true
+		end
+	end
+	Rock:FinalizeLibrary("LibRockFakeLib-1.0")
+
+	local lib = Rock:NewLibrary("LibRockFakeLib-2.0", 1)
+	assert(not triggered)
+	Rock:FinalizeLibrary("LibRockFakeLib-2.0")
+	assert(triggered)
+	triggered = false
+	local lib = Rock:NewLibrary("LibRockFakeLib-2.0", 2)
+	assert(not triggered)
+	Rock:FinalizeLibrary("LibRockFakeLib-2.0")
+	assert(not triggered)
+end)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/FuBar_ReActionFu/lib/LibRock-1.0/LibRock-1.0.toc	Wed Apr 02 23:31:13 2008 +0000
@@ -0,0 +1,14 @@
+## Interface: 20300
+
+## Title: LibRock-1.0
+## Notes: AddOn development framework
+## Author: ckknight
+## X-Website: http://www.wowace.com
+## X-Category: Library
+## X-Donate: Paypal:ckknight AT gmail DOT com
+## X-eMail: ckknight AT gmail DOT com
+## X-License: LGPL v2.1
+## SavedVariables: LibRock_1_0DB
+## OptionalDeps: !BugGrabber, !Swatter
+
+lib.xml
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/FuBar_ReActionFu/lib/LibRock-1.0/LibStub/LibStub.lua	Wed Apr 02 23:31:13 2008 +0000
@@ -0,0 +1,30 @@
+-- LibStub is a simple versioning stub meant for use in Libraries.  http://www.wowace.com/wiki/LibStub for more info
+-- LibStub is hereby placed in the Public Domain Credits: Kaelten, Cladhaire, ckknight, Mikk, Ammo, Nevcairiel, joshborke
+local LIBSTUB_MAJOR, LIBSTUB_MINOR = "LibStub", 2  -- NEVER MAKE THIS AN SVN REVISION! IT NEEDS TO BE USABLE IN ALL REPOS!
+local LibStub = _G[LIBSTUB_MAJOR]
+
+if not LibStub or LibStub.minor < LIBSTUB_MINOR then
+	LibStub = LibStub or {libs = {}, minors = {} }
+	_G[LIBSTUB_MAJOR] = LibStub
+	LibStub.minor = LIBSTUB_MINOR
+	
+	function LibStub:NewLibrary(major, minor)
+		assert(type(major) == "string", "Bad argument #2 to `NewLibrary' (string expected)")
+		minor = assert(tonumber(strmatch(minor, "%d+")), "Minor version must either be a number or contain a number.")
+		
+		local oldminor = self.minors[major]
+		if oldminor and oldminor >= minor then return nil end
+		self.minors[major], self.libs[major] = minor, self.libs[major] or {}
+		return self.libs[major], oldminor
+	end
+	
+	function LibStub:GetLibrary(major, silent)
+		if not self.libs[major] and not silent then
+			error(("Cannot find a library instance of %q."):format(tostring(major)), 2)
+		end
+		return self.libs[major], self.minors[major]
+	end
+	
+	function LibStub:IterateLibraries() return pairs(self.libs) end
+	setmetatable(LibStub, { __call = LibStub.GetLibrary })
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/FuBar_ReActionFu/lib/LibRock-1.0/lib.xml	Wed Apr 02 23:31:13 2008 +0000
@@ -0,0 +1,5 @@
+<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="LibStub\LibStub.lua" />
+	<Script file="LibRock-1.0.lua" />
+</Ui>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/FuBar_ReActionFu/lib/embeds.xml	Wed Apr 02 23:31:13 2008 +0000
@@ -0,0 +1,12 @@
+<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">
+
+  <Include file="LibRock-1.0\lib.xml"/>
+  <Include file="LibFuBarPlugin-3.0\lib.xml"/>
+
+  <Script file="AceLibrary\AceLibrary.lua"/>
+  <Script file="Dewdrop-2.0\Dewdrop-2.0.lua"/>
+  <Script file="Tablet-2.0\Tablet-2.0.lua"/>
+
+</Ui>
--- a/modules/ReAction_Action/ReAction_Action.lua	Wed Apr 02 18:22:02 2008 +0000
+++ b/modules/ReAction_Action/ReAction_Action.lua	Wed Apr 02 23:31:13 2008 +0000
@@ -195,11 +195,7 @@
   -- TODO: re-implement ActionButton event handlers that don't do secure stuff
 
   -- this will probably cause taint, using right now for display/debugging purposes
-  f:SetScript("OnAttributeChanged",
-    function()
-      ActionButton_UpdateAction()
-    end
-  )
+  f:SetScript("OnAttributeChanged", ActionButton_UpdateAction)
   f:SetAttribute("action", config.actionID)
   barFrame:SetAttribute("addchild",f)
   self.frame = f
--- a/modules/ReAction_ConfigUI/ReAction_ConfigUI.lua	Wed Apr 02 18:22:02 2008 +0000
+++ b/modules/ReAction_ConfigUI/ReAction_ConfigUI.lua	Wed Apr 02 23:31:13 2008 +0000
@@ -4,21 +4,18 @@
   This modules creates and manages ReAction configuration
   elements, including:
 
-   - waterfall config menu system
+   - Interface Options panel
    - bar dragging and resizing control overlays
    - contextual menus
 
   Individual modules are responsible for populating these
-  configuration elements via the following functions:
-
-  module:GetGlobalOptions( configModule )
-  module:GetGlobalBarOptions( configModule )
-  module:GetModuleOptions( configModule )
-  module:GetBarConfigOptions( bar, configModule )
-  module:GetBarMenuOptions( bar, configModule )
-
-  the ReAction_ConfigUI module is passed in as a parameter so that
-  option handlers can refresh the config UI.
+  configuration elements via ReAction:RegisterOptions(). The
+  valid values of 'context' are:
+  
+   - 'global'  : added to the Global Settings tab
+   - 'module'  : added to the Module Settings tab
+   - 'bar'     : added to the Bar Settings tab
+   - 'barMenu' : shown on the bar contextual menu
 
 --]]
 
@@ -26,71 +23,68 @@
 local ReAction = ReAction
 local L = ReAction.L
 local _G = _G
-local Waterfall = AceLibrary("Waterfall-1.0")
+local InCombatLockdown = InCombatLockdown
+
 local Dewdrop = AceLibrary("Dewdrop-2.0")
-local print = ReAction.print
-local InCombatLockdown = InCombatLockdown
 
 -- module declaration
 local moduleID = "ConfigUI"
 local module = ReAction:NewModule( moduleID,
   "AceEvent-3.0"
-  -- mixins go here
 )
 
-module.globalOptions = {
-  type = "group",
-  args = {
-    unlock = {
-      type     = "toggle",
-      handler  = module,
-      name     = L["unlock"],
-      guiName  = L["Unlock Bars"],
-      desc     = L["Unlock bars for dragging and resizing with the mouse"],
-      get      = function() return module.configMode end,
-      set      = "SetConfigMode",
-      disabled = InCombatLockdown,
-      order    = 1
-    },
-    config = {
-      type     = "execute",
-      handler  = module,
-      name     = L["config"],
-      guiName  = L["Configure..."],
-      desc     = L["Open the configuration dialogue"],
-      func     = "OpenConfig",
-      disabled = InCombatLockdown,
-      wfHidden = true, -- don't show this in the waterfall config
-      order    = 2
-    },
-  }
-}
-
 module.configOptions = {
   type = "group",
+  childGroups = "tab",
   args = {
+    desc = {
+      type = "description",
+      name = L["Customizable replacement for Blizzard's Action Bars"],
+    },
     global = {
       type = "group",
       name = L["Global Settings"],
       desc = L["Global configuration settings"],
-      args = { },
+      args = { 
+        unlock = {
+          type     = "toggle",
+          handler  = module,
+          name     = L["Unlock Bars"],
+          desc     = L["Unlock bars for dragging and resizing with the mouse"],
+          get      = function() return module.configMode end,
+          set      = function(info, value) module:SetConfigMode(value) end,
+          disabled = InCombatLockdown,
+          order    = 1
+        },
+      },
       order = 1,
     },
     module = {
       type = "group",
+      childGroups = "select",
       name = L["Module Settings"],
       desc = L["Configuration settings for each module"],
-      args = { },
-      order = 2,
+      args = { 
+        configUI = {
+          type = "group",
+          name = "Config UI",
+          desc = "description",
+          args = {
+            foo = {
+              type = "toggle",
+              handler = module,
+              name = "foo",
+              desc = "description",
+              get = function() return true end,
+              set = function() end,
+            }
+          }
+        },
+      },
+      order = -1,
     },
-    bar = {
-      type = "group",
-      name = L["Bars"],
-      desc = L["Configuration settings for bars"],
-      args = { },
-      order = 3,
-    },
-  }
+  },
+  plugins = { }
 }
 
 -- module methods
@@ -100,30 +94,41 @@
       profile = { }
     }
   )
+  self:InitializeOptions()
+  LibStub("AceConfig-3.0"):RegisterOptionsTable("ReAction",self.configOptions)
+  LibStub("AceConfigDialog-3.0"):AddToBlizOptions("ReAction", "ReAction")
+  self:RegisterEvent("PLAYER_REGEN_DISABLED")
 end
 
-function module:OnEnable()
-  self:RegisterEvent("PLAYER_REGEN_DISABLED")
- 	Waterfall:Register("ReAction",
-    "aceOptions", self.configOptions,
-    "treeLevels", nil, -- infinite
-    "colorR", 0.8,
-    "colorG", 0.65,
-    "colorB", 0,
-    "defaultPane", "global" )
+function module:InitializeOptions()
+  for _, m in pairs(ReAction:GetOptions("global")) do
+    for k, v in pairs(m) do
+      self.configOptions.args.global.args[k] = v
+    end
+  end
+  ReAction.RegisterCallback(self,"OnOptionsRegistered")
 end
 
-function module:OnDisable()
-  self:UnregisterEvent("PLAYER_REGEN_DISABLED")
-  self:SetConfigMode(false)
-  Waterfall:UnRegister("ReAction")
+function module:OnOptionsRegistered(evt, context, module, opts)
+  if context == "global" then
+    for k, v in pairs(opts) do
+      self.configOptions.args.global.args[k] = v
+    end
+  elseif context == "module" then
+    for k, v in pairs(opts) do
+      self.configOptions.args.module.args[k] = v
+    end
+  elseif context == "bar" then
+
+  elseif context == "barMenu" then
+
+  end
 end
 
 function module:PLAYER_REGEN_DISABLED()
   if self.configMode == true then
     UIErrorsFrame:AddMessage(L["ReAction config mode disabled during combat."])
     self:SetConfigMode(false)
-    Waterfall:Close("ReAction")
   end
 end
 
@@ -149,107 +154,20 @@
   end
 end
 
-local function refreshWaterfall()
-  module:RefreshConfig()
-end
-
-local function SafeCall(module, method, ...)
+local function safecall(module, method, ...)
   if module and type(module[method]) == "function" then
-    return module[method](...)
-  end
-end
-
-function module:RefreshOptions()
-  local opts = self.configOptions.args
-  
-  for _, m in ReAction:IterateModules() do
-    local o = SafeCall(m,"GetGlobalOptions",self)
-    if o then
-      for k, v in pairs(o) do
-        opts.global.args[k] = v
-      end
-    end
-  end
-
-  for _, m in ReAction:IterateModules() do
-    local o = SafeCall(m,"GetGlobalBarOptions",self)
-    if o then
-      for k, v in pairs(o) do
-        opts.bar.args[k] = v
-      end
-    end
-  end
-
-  for _, m in ReAction:IterateModules() do
-    local o = SafeCall(m,"GetModuleOptions",self)
-    if o then
-      for k, v in pairs(o) do
-        opts.module.args[k] = v
-      end
-    end
-  end
-
-  local barOpts = opts.bar.args
-  for name, bar in pairs(ReAction.bars) do
-    if bar then
-      if barOpts[name] == nil then
-        barOpts[name] = {
-          type = "group",
-          name = name,
-          desc = name,
-          handler = bar,
-          args = { 
-            delete = {
-              type = "execute",
-              name = L["Delete Bar"],
-              desc = L["Remove the bar from the current profile"],
-              func = function() ReAction:EraseBar(bar); self:RefreshConfig() end
-            },
-            rename = {
-              type = "text",
-              name = L["Rename Bar"],
-              desc = L["Set a name for the bar"],
-              get  = "GetName",
-              set  = function(name) ReAction:RenameBar(bar,name); self:RefreshConfig() end
-            }
-          }
-        }
-      end
-      if bar.modConfigOpts == nil then 
-        bar.modConfigOpts = { }
-      end
-      for _, m in ReAction:IterateModules() do
-        local o = SafeCall(m,"GetBarConfigOptions",bar,self)
-        if o then
-          for k, v in pairs(o) do
-            barOpts[name].args[k] = v
-          end
-        end
-      end
-    end
-  end
-  -- remove obsolete bar tables
-  for name, opt in pairs(barOpts) do
-    if opt.type == "group" and ReAction.bars[name] == nil then
-      barOpts[name] = nil
-    end
+    return module[method](method, ...)
   end
 end
 
 function module:OpenConfig(bar)
-  self:RefreshOptions()
   Dewdrop:Close()
-  Waterfall:Open("ReAction",bar and "bar."..bar:GetName())
-end
-
-function module:RefreshConfig()
-  self:RefreshOptions()
-  Waterfall:Refresh("ReAction")
+  InterfaceOptionsFrame_OpenToFrame("ReAction")
 end
 
 function module:ApplyToBar(bar)
   if self.configMode then
-    bar:ShowControls(true)
+    bar:ShowControls(self.configMode)
   end
 end
 
@@ -262,10 +180,6 @@
   end
 end
 
-function module:GetGlobalOptions()
-  return self.globalOptions.args
-end
-
 
 
 
@@ -547,9 +461,9 @@
       -- add bar type and status information to name
       local name = bar.name
       for _, m in ReAction:IterateModules() do
-        local suffix = SafeCall(m,"GetBarNameModifier",bar)
+        local suffix = safecall(m,"GetBarNameModifier",bar)
         if suffix then
-          name = format("%s %s",name,suffix)
+          name = ("%s %s"):format(name,suffix)
         end
       end
       
@@ -611,7 +525,7 @@
     self.modMenuOpts = { }
   end
   for _, m in ReAction:IterateModules() do
-    local opts = SafeCall(m,"GetBarMenuOptions",self,module)
+    local opts = safecall(m,"GetBarMenuOptions",self,module)
     if opts then
       for k, v in pairs(opts) do
         self.menuOpts.args[k] = v
--- a/modules/ReAction_ConfigUI/ReAction_ConfigUI.xml	Wed Apr 02 18:22:02 2008 +0000
+++ b/modules/ReAction_ConfigUI/ReAction_ConfigUI.xml	Wed Apr 02 23:31:13 2008 +0000
@@ -2,11 +2,7 @@
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
     xsi:schemaLocation="http://www.blizzard.com/wow/ui/..\FrameXML\UI.xsd">
 
-  <Script file="lib\AceLibrary\AceLibrary.lua"/>
-  <Script file="lib\AceOO-2.0\AceOO-2.0.lua"/>
-  <Script file="lib\Waterfall-1.0\Waterfall-1.0.lua"/>
-  <Script file="lib\Dewdrop-2.0\Dewdrop-2.0.lua"/>
-
-  <Script file="ReAction_ConfigUI.lua"/>
+  <Include file="lib\embeds.xml"/>
+  <Script  file="ReAction_ConfigUI.lua"/>
 
 </Ui>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/ReAction_ConfigUI/lib/embeds.xml	Wed Apr 02 23:31:13 2008 +0000
@@ -0,0 +1,14 @@
+<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="AceLibrary\AceLibrary.lua"/>
+  <Script file="AceOO-2.0\AceOO-2.0.lua"/>
+  <Script file="Waterfall-1.0\Waterfall-1.0.lua"/>
+  <Script file="Dewdrop-2.0\Dewdrop-2.0.lua"/>
+
+  <Include file="AceGUI-3.0\AceGUI-3.0.xml"/>
+  <Include file="AceConfig-3.0\AceConfig-3.0.xml"/>
+
+</Ui>
+ 
\ No newline at end of file
--- a/modules/ReAction_HideBlizzard/ReAction_HideBlizzard.lua	Wed Apr 02 18:22:02 2008 +0000
+++ b/modules/ReAction_HideBlizzard/ReAction_HideBlizzard.lua	Wed Apr 02 23:31:13 2008 +0000
@@ -29,33 +29,23 @@
 
   self.hiddenFrame = CreateFrame("Frame")
   self.hiddenFrame:Hide()
-
-  -- disable the buttons to hide/show the blizzard multiaction bars
-  -- see UIOptionsFrame.lua and .xml
-  -- This is called every time the options panel is shown, after it is set up
-  local disabledOptionsButtons = {
-    _G["UIOptionsFrameCheckButton"..UIOptionsFrameCheckButtons["SHOW_MULTIBAR1_TEXT"].index],
-    _G["UIOptionsFrameCheckButton"..UIOptionsFrameCheckButtons["SHOW_MULTIBAR2_TEXT"].index],
-    _G["UIOptionsFrameCheckButton"..UIOptionsFrameCheckButtons["SHOW_MULTIBAR3_TEXT"].index],
-    _G["UIOptionsFrameCheckButton"..UIOptionsFrameCheckButtons["SHOW_MULTIBAR4_TEXT"].index],
-    _G["UIOptionsFrameCheckButton"..UIOptionsFrameCheckButtons["ALWAYS_SHOW_MULTIBARS_TEXT"].index],
-  }
-  hooksecurefunc("UIOptionsFrame_Load",
-    function()
-      if self.db.profile.hide then
-        for _, f in pairs(disabledOptionsButtons) do
-          f.disabled = true
-          OptionsFrame_DisableCheckBox(f)
-        end
-      end
-    end
-  )
 end
 
 function module:OnEnable()
   if self.db.profile.hide then
     self:HideAll(true)
   end
+  ReAction:RegisterOptions("global", self, {
+    hideBlizzard = {
+      type = "toggle",
+      handler = self,
+      name = L["Hide Blizzard Action Bars"],
+      desc = L["Hide the default main bar and extra action bars"],
+      get  = "IsHidden",
+      set  = function(info,value) self:SetHidden(value) end,
+      disabled = InCombatLockdown
+    }
+  })
 end
 
 function module:OnDisable()
@@ -143,19 +133,3 @@
   end
 end
 
-function module:GetGlobalOptions()
-  if self.globalOptions == nil then
-    self.globalOptions = {
-      hideBlizzard = {
-        type = "toggle",
-        handler = self,
-        name = L["Hide Default Action Bars"],
-        desc = L["Hide the default main bar and extra action bars"],
-        get  = "IsHidden",
-        set  = "SetHidden",
-        disabled = function() return InCombatLockdown() end
-      }
-    }
-  end
-  return self.globalOptions
-end
--- a/modules/modules.xml	Wed Apr 02 18:22:02 2008 +0000
+++ b/modules/modules.xml	Wed Apr 02 23:31:13 2008 +0000
@@ -2,10 +2,19 @@
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
     xsi:schemaLocation="http://www.blizzard.com/wow/ui/..\FrameXML\UI.xsd">
 
-<Include file="ReAction_Action\ReAction_Action.xml"/>
+<!-- config/layout modules -->
 <Include file="ReAction_ConfigUI\ReAction_ConfigUI.xml"/>
 <Include file="ReAction_HideBlizzard\ReAction_HideBlizzard.xml"/>
+
+<!-- action button modules -->
+<Include file="ReAction_Action\ReAction_Action.xml"/>
+
+
+
+<!-- deprecated
 <Include file="FuBar_ReActionFu\FuBar_ReActionFu.xml"/>
+-->
+
 
 <!-- in progress
 <Include file="ReAction_PetAction\ReAction_PetAction.xml"/>