WoWInterface SVN StunWatch

Compare Revisions

  • This comparison shows the changes necessary to convert path
    /
    from Rev 4 to Rev 5
    Reverse comparison

Rev 4 → Rev 5

trunk/StunWatch/StunWatch.toc
1,10 → 1,23
## Interface: 20400
## Title: StunWatch
## Author: Macca
## Version: 2.1
## Author: nwkegan (Formerly Maccaa)
## Version: 2.2
## Notes: Displays a progress bar for Rogue abilites
## SavedVariables: StunWatchDB
## OptionalDeps: Ace2, LibStub, LibCandyBar, Waterfall-1.0
 
## X-Embeds: Ace2, LibStub, LibCandyBar, Waterfall-1.0
## X-Credits: WoWUIDev and WoWAce
## X-WoWIPortal: nwkegan
## X-RelSite-WoWI: 8677
 
 
Libs\LibStub\LibStub.lua
Libs\LibCandyBar\LibCandyBar.lua
StunWatch.lua
Libs\AceLibrary\AceLibrary.lua
Libs\AceAddon-2.0\AceAddon-2.0.lua
Libs\AceConsole-2.0\AceConsole-2.0.lua
Libs\AceDB-2.0\AceDB-2.0.lua
Libs\AceEvent-2.0\AceEvent-2.0.lua
Libs\Waterfall-1.0\Waterfall-1.0\Waterfall-1.0.lua
SWcore.lua
\ No newline at end of file
trunk/StunWatch/Libs/AceAddon-2.0/AceAddon-2.0.lua New file
0,0 → 1,1393
--[[
Name: AceAddon-2.0
Revision: $Rev: 57245 $
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: 57245 $"
 
-- 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"] = "Healer",
["Tank"] = "Tank",
["Caster"] = "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
self[lowerKey] = v
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 isGoodVariable(self.category) then
local category = CATEGORIES[self.category]
aboutFrame:AddLine(CATEGORY, category or tostring(self.category))
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)
trunk/StunWatch/Libs/AceAddon-2.0/AceAddon-2.0.toc New file
0,0 → 1,12
## Interface: 20400
 
## 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
trunk/StunWatch/Libs/AceDB-2.0/AceDB-2.0.toc New file
0,0 → 1,12
## Interface: 20400
 
## Title: Lib: AceDB-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, AceEvent-2.0, AceOO-2.0
 
AceDB-2.0.lua
trunk/StunWatch/Libs/AceDB-2.0/AceDB-2.0.lua New file
0,0 → 1,2191
--[[
Name: AceDB-2.0
Revision: $Rev: 56534 $
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/AceDB-2.0
SVN: http://svn.wowace.com/root/trunk/Ace2/AceDB-2.0
Description: Mixin to allow for fast, clean, and featureful saved variable
access.
Dependencies: AceLibrary, AceOO-2.0, AceEvent-2.0
License: LGPL v2.1
]]
 
local MAJOR_VERSION = "AceDB-2.0"
local MINOR_VERSION = "$Revision: 56534 $"
 
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) end
end
 
local ACTIVE, ENABLED, STATE, TOGGLE_ACTIVE, MAP_ACTIVESUSPENDED, SET_PROFILE, SET_PROFILE_USAGE, PROFILE, PLAYER_OF_REALM, CHOOSE_PROFILE_DESC, CHOOSE_PROFILE_GUI, COPY_PROFILE_DESC, COPY_PROFILE_GUI, OTHER_PROFILE_DESC, OTHER_PROFILE_GUI, OTHER_PROFILE_USAGE, RESET_PROFILE, RESET_PROFILE_DESC, CHARACTER_COLON, REALM_COLON, CLASS_COLON, DEFAULT, ALTERNATIVE
 
-- Move these into "enUS" when they've been translated in all other locales
local DELETE_PROFILE = "Delete"
local DELETE_PROFILE_DESC = "Deletes a profile. Note that no check is made whether this profile is in use by other characters or not."
local DELETE_PROFILE_USAGE = "<profile name>"
 
if GetLocale() == "deDE" then
DELETE_PROFILE = "L\195\182schen"
DELETE_PROFILE_DESC = "L\195\182scht ein Profil. Beachte das nicht \195\188berpr\195\188ft wird ob das zu l\195\182schende Profil von anderen Charakteren genutzt wird oder nicht."
DELETE_PROFILE_USAGE = "<profil name>"
 
ACTIVE = "Aktiv"
ENABLED = "Aktiviert"
STATE = "Status"
TOGGLE_ACTIVE = "Stoppt/Aktiviert dieses Addon."
MAP_ACTIVESUSPENDED = { [true] = "|cff00ff00Aktiv|r", [false] = "|cffff0000Gestoppt|r" }
SET_PROFILE = "Setzt das Profil f\195\188r dieses Addon."
SET_PROFILE_USAGE = "{Charakter || Klasse || Realm || <Profilname>}"
PROFILE = "Profil"
PLAYER_OF_REALM = "%s von %s"
CHOOSE_PROFILE_DESC = "W\195\164hle ein Profil."
CHOOSE_PROFILE_GUI = "W\195\164hle"
COPY_PROFILE_DESC = "Kopiert Einstellungen von einem anderem Profil."
COPY_PROFILE_GUI = "Kopiere von"
OTHER_PROFILE_DESC = "W\195\164hle ein anderes Profil."
OTHER_PROFILE_GUI = "Anderes"
OTHER_PROFILE_USAGE = "<Profilname>"
RESET_PROFILE = "Resette das Profil"
RESET_PROFILE_DESC = "Entfernt alle Einstellungen des gegenw\195\164rtigen Profils."
 
CHARACTER_COLON = "Charakter: "
REALM_COLON = "Realm: "
CLASS_COLON = "Klasse: "
 
DEFAULT = "Vorgabe"
ALTERNATIVE = "Alternativ"
elseif GetLocale() == "frFR" then
ACTIVE = "Actif"
ENABLED = "Activ\195\169"
STATE = "Etat"
TOGGLE_ACTIVE = "Suspend/active cet addon."
MAP_ACTIVESUSPENDED = { [true] = "|cff00ff00Actif|r", [false] = "|cffff0000Suspendu|r" }
SET_PROFILE = "S\195\169lectionne le profil pour cet addon."
SET_PROFILE_USAGE = "{perso || classe || royaume || <nom de profil>}"
PROFILE = "Profil"
PLAYER_OF_REALM = "%s de %s"
CHOOSE_PROFILE_DESC = "Choisissez un profil."
CHOOSE_PROFILE_GUI = "Choix"
COPY_PROFILE_DESC = "Copier les param\195\168tres d'un autre profil."
COPY_PROFILE_GUI = "Copier \195\160 partir de"
OTHER_PROFILE_DESC = "Choisissez un autre profil."
OTHER_PROFILE_GUI = "Autre"
OTHER_PROFILE_USAGE = "<nom de profil>"
RESET_PROFILE = "Reset profile" -- fix
RESET_PROFILE_DESC = "Clear all settings of the current profile." -- fix
 
CHARACTER_COLON = "Personnage: "
REALM_COLON = "Royaume: "
CLASS_COLON = "Classe: "
 
DEFAULT = "Default" -- fix
ALTERNATIVE = "Alternative" -- fix
elseif GetLocale() == "koKR" then
DELETE_PROFILE = "삭제"
DELETE_PROFILE_DESC = "프로필을 삭제합니다."
DELETE_PROFILE_USAGE = "<프로필명>"
 
ACTIVE = "사용"
ENABLED = "사용"
STATE = "상태"
TOGGLE_ACTIVE = "이 애드온 중지/다시 시작"
MAP_ACTIVESUSPENDED = { [true] = "|cff00ff00사용|r", [false] = "|cffff0000중지|r" }
SET_PROFILE = "이 애드온에 프로필 설정"
SET_PROFILE_USAGE = "{캐릭터명 || 직업 || 서버명 || <프로필명>}"
PROFILE = "프로필"
PLAYER_OF_REALM = "%s (%s 서버)"
CHOOSE_PROFILE_DESC = "프로필을 선택합니다."
CHOOSE_PROFILE_GUI = "선택"
COPY_PROFILE_DESC = "다른 프로필 설정을 복사합니다."
COPY_PROFILE_GUI = "복사"
OTHER_PROFILE_DESC = "다른 프로필을 선택합니다."
OTHER_PROFILE_GUI = "기타"
OTHER_PROFILE_USAGE = "<프로필명>"
RESET_PROFILE = "프로필 초기화"
RESET_PROFILE_DESC = "모든 세팅에서 현재 프로필을 초기화 합니다."
 
CHARACTER_COLON = "캐릭터: "
REALM_COLON = "서버: "
CLASS_COLON = "직업: "
 
DEFAULT = "기본값"
ALTERNATIVE = "대체"
elseif GetLocale() == "zhTW" then
DELETE_PROFILE = "刪除"
DELETE_PROFILE_DESC = "刪除記錄檔。注意,有可能別的角色也使用這個記錄檔。"
DELETE_PROFILE_USAGE = "<記錄檔名稱>"
 
ACTIVE = "啟動"
ENABLED = "啟用"
STATE = "狀態"
TOGGLE_ACTIVE = "暫停/繼續使用這個插件。"
MAP_ACTIVESUSPENDED = { [true] = "|cff00ff00啟動|r", [false] = "|cffff0000已暫停|r" }
SET_PROFILE = "設定這插件的記錄檔。"
SET_PROFILE_USAGE = "{角色 || 職業 || 伺服器 || <記錄檔名稱>}"
PROFILE = "記錄檔"
PLAYER_OF_REALM = "%s - %s"
CHOOSE_PROFILE_DESC = "選擇一個記錄檔。"
CHOOSE_PROFILE_GUI = "選擇"
COPY_PROFILE_DESC = "由其他記錄檔複製設定。"
COPY_PROFILE_GUI = "複製自"
OTHER_PROFILE_DESC = "選擇其他記錄檔。"
OTHER_PROFILE_GUI = "其他"
OTHER_PROFILE_USAGE = "<記錄檔名稱>"
RESET_PROFILE = "重設記錄檔"
RESET_PROFILE_DESC = "清除目前的記錄檔上的所有設定。"
 
CHARACTER_COLON = "角色: "
REALM_COLON = "伺服器: "
CLASS_COLON = "職業: "
 
DEFAULT = "預設"
ALTERNATIVE = "替代"
elseif GetLocale() == "zhCN" then
ACTIVE = "\230\156\137\230\149\136"
ENABLED = "\229\144\175\231\148\168"
STATE = "\231\138\182\230\128\129"
TOGGLE_ACTIVE = "\230\154\130\229\129\156/\230\129\162\229\164\141 \230\173\164\230\143\146\228\187\182."
MAP_ACTIVESUSPENDED = { [true] = "|cff00ff00\230\156\137\230\149\136|r", [false] = "|cffff0000\230\154\130\229\129\156|r" }
SET_PROFILE = "\232\174\190\231\189\174\233\133\141\231\189\174\230\150\135\228\187\182\228\184\186\232\191\153\230\143\146\228\187\182."
SET_PROFILE_USAGE = "{\229\173\151\231\172\166 || \233\128\137\228\187\182\231\177\187 || \229\159\159 || <\233\133\141\231\189\174\230\150\135\228\187\182\229\144\141\229\173\151>}"
PROFILE = "\233\133\141\231\189\174\230\150\135\228\187\182"
PLAYER_OF_REALM = "%s \231\154\132 %s"
CHOOSE_PROFILE_DESC = "\233\128\137\230\139\169\233\133\141\231\189\174\230\150\135\228\187\182."
CHOOSE_PROFILE_GUI = "\233\128\137\230\139\169"
COPY_PROFILE_DESC = "\229\164\141\229\136\182\232\174\190\231\189\174\228\187\142\229\143\166\228\184\128\228\184\170\233\133\141\231\189\174\230\150\135\228\187\182."
COPY_PROFILE_GUI = "\229\164\141\229\136\182\228\187\142"
OTHER_PROFILE_DESC = "\233\128\137\230\139\169\229\143\166\228\184\128\228\184\170\233\133\141\231\189\174\230\150\135\228\187\182."
OTHER_PROFILE_GUI = "\229\133\182\228\187\150"
OTHER_PROFILE_USAGE = "<\233\133\141\231\189\174\230\150\135\228\187\182\229\144\141\229\173\151>"
RESET_PROFILE = "Reset profile" -- fix
RESET_PROFILE_DESC = "Clear all settings of the current profile." -- fix
 
CHARACTER_COLON = "\229\173\151\231\172\166: "
REALM_COLON = "\229\159\159: "
CLASS_COLON = "\233\128\137\228\187\182\231\177\187: "
 
DEFAULT = "Default" -- fix
ALTERNATIVE = "Alternative" -- fix
elseif GetLocale() == "esES" then
ACTIVE = "Activo"
ENABLED = "Activado"
STATE = "Estado"
TOGGLE_ACTIVE = "Parar/Continuar este accesorio"
MAP_ACTIVESUSPENDED = { [true] = "|cff00ff00Activo|r", [false] = "|cffff0000Parado|r" }
SET_PROFILE = "Selecciona el perfil para este accesorio."
SET_PROFILE_USAGE = "{perso || clase || reino || <nombre del perfil>}"
PROFILE = "Perfil"
PLAYER_OF_REALM = "%s de %s"
CHOOSE_PROFILE_DESC = "Elige un perfil."
CHOOSE_PROFILE_GUI = "Elige"
COPY_PROFILE_DESC = "Copiar de un perfil a otro"
COPY_PROFILE_GUI = "Copiar desde"
OTHER_PROFILE_DESC = "Elige otro perfil."
OTHER_PROFILE_GUI = "Otro"
OTHER_PROFILE_USAGE = "<nombre del perfil>"
RESET_PROFILE = "Reset profile" -- fix
RESET_PROFILE_DESC = "Clear all settings of the current profile." -- fix
 
CHARACTER_COLON = "Personaje: "
REALM_COLON = "Reino: "
CLASS_COLON = "Clase: "
 
DEFAULT = "Por defecto"
ALTERNATIVE = "Alternativo"
else -- enUS
ACTIVE = "Active"
ENABLED = "Enabled"
STATE = "State"
TOGGLE_ACTIVE = "Suspend/resume this addon."
MAP_ACTIVESUSPENDED = { [true] = "|cff00ff00Active|r", [false] = "|cffff0000Suspended|r" }
SET_PROFILE = "Set profile for this addon."
SET_PROFILE_USAGE = "{char || class || realm || <profile name>}"
PROFILE = "Profile"
PLAYER_OF_REALM = "%s of %s"
CHOOSE_PROFILE_DESC = "Choose a profile."
CHOOSE_PROFILE_GUI = "Choose"
COPY_PROFILE_DESC = "Copy settings from another profile."
COPY_PROFILE_GUI = "Copy from"
OTHER_PROFILE_DESC = "Choose another profile."
OTHER_PROFILE_GUI = "Other"
OTHER_PROFILE_USAGE = "<profile name>"
RESET_PROFILE = "Reset profile"
RESET_PROFILE_DESC = "Clear all settings of the current profile."
 
CHARACTER_COLON = "Character: "
REALM_COLON = "Realm: "
CLASS_COLON = "Class: "
 
DEFAULT = "Default"
ALTERNATIVE = "Alternative"
end
local convertFromOldCharID
do
local matchStr = "^" .. PLAYER_OF_REALM:gsub("([%(%)%.%*%+%-%[%]%?%^%$%%])", "%%%1"):gsub("%%s", "(.+)") .. "$"
function convertFromOldCharID(str)
local player, realm = str:match(matchStr)
if not player then
return str
end
return player .. " - " .. realm
end
end
 
local AceOO = AceLibrary("AceOO-2.0")
local AceEvent
local Mixin = AceOO.Mixin
local AceDB = Mixin {
"RegisterDB",
"RegisterDefaults",
"ResetDB",
"SetProfile",
"GetProfile",
"CopyProfileFrom",
"DeleteProfile",
"ToggleActive",
"IsActive",
"AcquireDBNamespace",
}
local Dewdrop = AceLibrary:HasInstance("Dewdrop-2.0") and AceLibrary("Dewdrop-2.0")
 
local _G = getfenv(0)
 
local function inheritDefaults(t, defaults)
if not defaults then
return t
end
for k,v in pairs(defaults) do
if k == "*" or k == "**" then
local v = v
if type(v) == "table" then
setmetatable(t, {
__index = function(self, key)
if key == nil then
return nil
end
self[key] = {}
inheritDefaults(self[key], v)
return self[key]
end
} )
else
setmetatable(t, {
__index = function(self, key)
if key == nil then
return nil
end
self[key] = v
return self[key]
end
} )
end
for key in pairs(t) do
if (defaults[key] == nil or key == k) and type(t[key]) == "table" then
inheritDefaults(t[key], v)
end
end
else
if type(v) == "table" then
if type(rawget(t, k)) ~= "table" then
t[k] = {}
end
inheritDefaults(t[k], v)
if defaults["**"] then
inheritDefaults(t[k], defaults["**"])
end
elseif rawget(t, k) == nil then
t[k] = v
end
end
end
return t
end
 
local _,race = UnitRace("player")
local faction
if race == "Orc" or race == "Scourge" or race == "Troll" or race == "Tauren" or race == "BloodElf" then
faction = FACTION_HORDE
else
faction = FACTION_ALLIANCE
end
local server = GetRealmName():trim()
local charID = UnitName("player") .. " - " .. server
local realmID = server .. " - " .. faction
local classID = UnitClass("player")
 
AceDB.CHAR_ID = charID
AceDB.REALM_ID = realmID
AceDB.CLASS_ID = classID
 
AceDB.FACTION = faction
AceDB.REALM = server
AceDB.NAME = UnitName("player")
 
local new, del
do
local list = setmetatable({}, {__mode="k"})
function new()
local t = next(list)
if t then
list[t] = nil
return t
else
return {}
end
end
 
function del(t)
setmetatable(t, nil)
for k in pairs(t) do
t[k] = nil
end
list[t] = true
end
end
 
local caseInsensitive_mt = {
__index = function(self, key)
if type(key) ~= "string" then
return nil
end
local lowerKey = key:lower()
for k,v in pairs(self) do
if k:lower() == lowerKey then
return self[k]
end
end
end,
__newindex = function(self, key, value)
if type(key) ~= "string" then
return error("table index is nil", 2)
end
local lowerKey = key:lower()
for k in pairs(self) do
if k:lower() == lowerKey then
rawset(self, k, nil)
rawset(self, key, value)
return
end
end
rawset(self, key, value)
end
}
 
local db_mt = { __index = function(db, key)
if key == "char" then
if db.charName then
if type(_G[db.charName]) ~= "table" then
_G[db.charName] = {}
end
if type(_G[db.charName].global) ~= "table" then
_G[db.charName].global = {}
end
rawset(db, 'char', _G[db.charName].global)
else
if type(db.raw.chars) ~= "table" then
db.raw.chars = {}
end
local id = charID
if type(db.raw.chars[id]) ~= "table" then
db.raw.chars[id] = {}
end
rawset(db, 'char', db.raw.chars[id])
end
if db.defaults and db.defaults.char then
inheritDefaults(db.char, db.defaults.char)
end
return db.char
elseif key == "realm" then
if type(db.raw.realms) ~= "table" then
db.raw.realms = {}
end
local id = realmID
if type(db.raw.realms[id]) ~= "table" then
db.raw.realms[id] = {}
end
rawset(db, 'realm', db.raw.realms[id])
if db.defaults and db.defaults.realm then
inheritDefaults(db.realm, db.defaults.realm)
end
return db.realm
elseif key == "server" then
if type(db.raw.servers) ~= "table" then
db.raw.servers = {}
end
local id = server
if type(db.raw.servers[id]) ~= "table" then
db.raw.servers[id] = {}
end
rawset(db, 'server', db.raw.servers[id])
if db.defaults and db.defaults.server then
inheritDefaults(db.server, db.defaults.server)
end
return db.server
elseif key == "account" then
if type(db.raw.account) ~= "table" then
db.raw.account = {}
end
rawset(db, 'account', db.raw.account)
if db.defaults and db.defaults.account then
inheritDefaults(db.account, db.defaults.account)
end
return db.account
elseif key == "faction" then
if type(db.raw.factions) ~= "table" then
db.raw.factions = {}
end
local id = faction
if type(db.raw.factions[id]) ~= "table" then
db.raw.factions[id] = {}
end
rawset(db, 'faction', db.raw.factions[id])
if db.defaults and db.defaults.faction then
inheritDefaults(db.faction, db.defaults.faction)
end
return db.faction
elseif key == "class" then
if type(db.raw.classes) ~= "table" then
db.raw.classes = {}
end
local id = classID
if type(db.raw.classes[id]) ~= "table" then
db.raw.classes[id] = {}
end
rawset(db, 'class', db.raw.classes[id])
if db.defaults and db.defaults.class then
inheritDefaults(db.class, db.defaults.class)
end
return db.class
elseif key == "profile" then
if type(db.raw.profiles) ~= "table" then
db.raw.profiles = setmetatable({}, caseInsensitive_mt)
else
setmetatable(db.raw.profiles, caseInsensitive_mt)
end
local id = db.raw.currentProfile[charID]
if id == "char" then
id = "char/" .. charID
elseif id == "class" then
id = "class/" .. classID
elseif id == "realm" then
id = "realm/" .. realmID
end
if type(db.raw.profiles[id]) ~= "table" then
db.raw.profiles[id] = {}
end
rawset(db, 'profile', db.raw.profiles[id])
if db.defaults and db.defaults.profile then
inheritDefaults(db.profile, db.defaults.profile)
end
return db.profile
elseif key == "raw" or key == "defaults" or key == "name" or key == "charName" or key == "namespaces" then
return nil
end
error(("Cannot access key %q in db table. You may want to use db.profile[%q]"):format(tostring(key), tostring(key)), 2)
end, __newindex = function(db, key, value)
error(("Cannot access key %q in db table. You may want to use db.profile[%q]"):format(tostring(key), tostring(key)), 2)
end }
 
local function RecalculateAceDBCopyFromList(target)
local db = target.db
local t = target['acedb-profile-copylist']
for k,v in pairs(t) do
t[k] = nil
end
local _,currentProfile = AceDB.GetProfile(target)
if db and db.raw then
if db.raw.profiles then
for k in pairs(db.raw.profiles) do
if currentProfile ~= k then
if k:find("^char/") then
local name = k:sub(6)
local player, realm = name:match("^(.*) %- (.*)$")
if player then
name = PLAYER_OF_REALM:format(player, realm)
end
t[k] = CHARACTER_COLON .. name
elseif k:find("^realm/") then
local name = k:sub(7)
t[k] = REALM_COLON .. name
elseif k:find("^class/") then
local name = k:sub(7)
t[k] = CLASS_COLON .. name
else
t[k] = k
end
end
end
end
if db.raw.namespaces then
for _,n in pairs(db.raw.namespaces) do
if n.profiles then
for k in pairs(n.profiles) do
if currentProfile ~= k then
if k:find('^char/') then
local name = k:sub(6)
local player, realm = name:match("^(.*) %- (.*)$")
if player then
name = PLAYER_OF_REALM:format(player, realm)
end
t[k] = CHARACTER_COLON .. name
elseif k:find('^realm/') then
local name = k:sub(7)
t[k] = REALM_COLON .. name
elseif k:find('^class/') then
local name = k:sub(7)
t[k] = CLASS_COLON .. name
else
t[k] = k
end
end
end
end
end
end
end
if t.Default then
t.Default = DEFAULT
end
if t.Alternative then
t.Alternative = ALTERNATIVE
end
end
 
local function RecalculateAceDBProfileList(target)
local t = target['acedb-profile-list']
for k,v in pairs(t) do
t[k] = nil
end
t.char = CHARACTER_COLON .. PLAYER_OF_REALM:format(UnitName("player"), server)
t.realm = REALM_COLON .. realmID
t.class = CLASS_COLON .. classID
t.Default = DEFAULT
local db = target.db
if db and db.raw then
if db.raw.profiles then
for k in pairs(db.raw.profiles) do
if not k:find("^char/") and not k:find("^realm/") and not k:find("^class/") then
t[k] = k
end
end
end
if db.raw.namespaces then
for _,n in pairs(db.raw.namespaces) do
if n.profiles then
for k in pairs(n.profiles) do
if not k:find("^char/") and not k:find("^realm/") and not k:find("^class/") then
t[k] = k
end
end
end
end
end
local curr = db.raw.currentProfile and db.raw.currentProfile[charID]
if curr and not t[curr] then
t[curr] = curr
end
end
if t.Alternative then
t.Alternative = ALTERNATIVE
end
end
 
local CrawlForSerialization
local CrawlForDeserialization
 
local function SerializeObject(o)
local t = { o:Serialize() }
CrawlForSerialization(t)
t[0] = o.class:GetLibraryVersion()
return t
end
 
local function DeserializeObject(t)
CrawlForDeserialization(t)
local className = t[0]
t[0] = nil
return AceLibrary(className):Deserialize(unpack(t))
end
 
local function IsSerializable(t)
return AceOO.inherits(t, AceOO.Class) and t.class and type(t.class.Deserialize) == "function" and type(t.Serialize) == "function" and type(t.class.GetLibraryVersion) == "function"
end
 
function CrawlForSerialization(t)
local tmp = new()
for k,v in pairs(t) do
tmp[k] = v
end
for k,v in pairs(tmp) do
if type(v) == "table" and type(rawget(v, 0)) ~= "userdata" then
if IsSerializable(v) then
v = SerializeObject(v)
t[k] = v
else
CrawlForSerialization(v)
end
end
if type(k) == "table" and type(rawget(k, 0)) ~= "userdata" then
if IsSerializable(k) then
t[k] = nil
t[SerializeObject(k)] = v
else
CrawlForSerialization(k)
end
end
tmp[k] = nil
k = nil
end
tmp = del(tmp)
end
 
local function IsDeserializable(t)
return type(rawget(t, 0)) == "string" and AceLibrary:HasInstance(rawget(t, 0))
end
 
function CrawlForDeserialization(t)
local tmp = new()
for k,v in pairs(t) do
tmp[k] = v
end
for k,v in pairs(tmp) do
if type(v) == "table" then
if IsDeserializable(v) then
t[k] = DeserializeObject(v)
del(v)
v = t[k]
elseif type(rawget(v, 0)) ~= "userdata" then
CrawlForDeserialization(v)
end
end
if type(k) == "table" then
if IsDeserializable(k) then
t[k] = nil
t[DeserializeObject(k)] = v
del(k)
elseif type(rawget(k, 0)) ~= "userdata" then
CrawlForDeserialization(k)
end
end
tmp[k] = nil
k = nil
end
tmp = del(tmp)
end
 
local namespace_mt = { __index = function(namespace, key)
local db = namespace.db
local name = namespace.name
if key == "char" then
if db.charName then
if type(_G[db.charName]) ~= "table" then
_G[db.charName] = {}
end
if type(_G[db.charName].namespaces) ~= "table" then
_G[db.charName].namespaces = {}
end
if type(_G[db.charName].namespaces[name]) ~= "table" then
_G[db.charName].namespaces[name] = {}
end
rawset(namespace, 'char', _G[db.charName].namespaces[name])
else
if type(db.raw.namespaces) ~= "table" then
db.raw.namespaces = {}
end
if type(db.raw.namespaces[name]) ~= "table" then
db.raw.namespaces[name] = {}
end
if type(db.raw.namespaces[name].chars) ~= "table" then
db.raw.namespaces[name].chars = {}
end
local id = charID
if type(db.raw.namespaces[name].chars[id]) ~= "table" then
db.raw.namespaces[name].chars[id] = {}
end
rawset(namespace, 'char', db.raw.namespaces[name].chars[id])
end
if namespace.defaults and namespace.defaults.char then
inheritDefaults(namespace.char, namespace.defaults.char)
end
return namespace.char
elseif key == "realm" then
if type(db.raw.namespaces) ~= "table" then
db.raw.namespaces = {}
end
if type(db.raw.namespaces[name]) ~= "table" then
db.raw.namespaces[name] = {}
end
if type(db.raw.namespaces[name].realms) ~= "table" then
db.raw.namespaces[name].realms = {}
end
local id = realmID
if type(db.raw.namespaces[name].realms[id]) ~= "table" then
db.raw.namespaces[name].realms[id] = {}
end
rawset(namespace, 'realm', db.raw.namespaces[name].realms[id])
if namespace.defaults and namespace.defaults.realm then
inheritDefaults(namespace.realm, namespace.defaults.realm)
end
return namespace.realm
elseif key == "server" then
if type(db.raw.namespaces) ~= "table" then
db.raw.namespaces = {}
end
if type(db.raw.namespaces[name]) ~= "table" then
db.raw.namespaces[name] = {}
end
if type(db.raw.namespaces[name].servers) ~= "table" then
db.raw.namespaces[name].servers = {}
end
local id = server
if type(db.raw.namespaces[name].servers[id]) ~= "table" then
db.raw.namespaces[name].servers[id] = {}
end
rawset(namespace, 'server', db.raw.namespaces[name].servers[id])
if namespace.defaults and namespace.defaults.server then
inheritDefaults(namespace.server, namespace.defaults.server)
end
return namespace.server
elseif key == "account" then
if type(db.raw.namespaces) ~= "table" then
db.raw.namespaces = {}
end
if type(db.raw.namespaces[name]) ~= "table" then
db.raw.namespaces[name] = {}
end
if type(db.raw.namespaces[name].account) ~= "table" then
db.raw.namespaces[name].account = {}
end
rawset(namespace, 'account', db.raw.namespaces[name].account)
if namespace.defaults and namespace.defaults.account then
inheritDefaults(namespace.account, namespace.defaults.account)
end
return namespace.account
elseif key == "faction" then
if type(db.raw.namespaces) ~= "table" then
db.raw.namespaces = {}
end
if type(db.raw.namespaces[name]) ~= "table" then
db.raw.namespaces[name] = {}
end
if type(db.raw.namespaces[name].factions) ~= "table" then
db.raw.namespaces[name].factions = {}
end
local id = faction
if type(db.raw.namespaces[name].factions[id]) ~= "table" then
db.raw.namespaces[name].factions[id] = {}
end
rawset(namespace, 'faction', db.raw.namespaces[name].factions[id])
if namespace.defaults and namespace.defaults.faction then
inheritDefaults(namespace.faction, namespace.defaults.faction)
end
return namespace.faction
elseif key == "class" then
if type(db.raw.namespaces) ~= "table" then
db.raw.namespaces = {}
end
if type(db.raw.namespaces[name]) ~= "table" then
db.raw.namespaces[name] = {}
end
if type(db.raw.namespaces[name].classes) ~= "table" then
db.raw.namespaces[name].classes = {}
end
local id = classID
if type(db.raw.namespaces[name].classes[id]) ~= "table" then
db.raw.namespaces[name].classes[id] = {}
end
rawset(namespace, 'class', db.raw.namespaces[name].classes[id])
if namespace.defaults and namespace.defaults.class then
inheritDefaults(namespace.class, namespace.defaults.class)
end
return namespace.class
elseif key == "profile" then
if type(db.raw.namespaces) ~= "table" then
db.raw.namespaces = {}
end
if type(db.raw.namespaces[name]) ~= "table" then
db.raw.namespaces[name] = {}
end
if type(db.raw.namespaces[name].profiles) ~= "table" then
db.raw.namespaces[name].profiles = setmetatable({}, caseInsensitive_mt)
else
setmetatable(db.raw.namespaces[name].profiles, caseInsensitive_mt)
end
local id = db.raw.currentProfile[charID]
if id == "char" then
id = "char/" .. charID
elseif id == "class" then
id = "class/" .. classID
elseif id == "realm" then
id = "realm/" .. realmID
end
if type(db.raw.namespaces[name].profiles[id]) ~= "table" then
db.raw.namespaces[name].profiles[id] = {}
end
rawset(namespace, 'profile', db.raw.namespaces[name].profiles[id])
if namespace.defaults and namespace.defaults.profile then
inheritDefaults(namespace.profile, namespace.defaults.profile)
end
return namespace.profile
elseif key == "defaults" or key == "name" or key == "db" then
return nil
end
error(("Cannot access key %q in db table. You may want to use db.profile[%q]"):format(tostring(key), tostring(key)), 2)
end, __newindex = function(db, key, value)
error(("Cannot access key %q in db table. You may want to use db.profile[%q]"):format(tostring(key), tostring(key)), 2)
end }
 
local tmp = {}
function AceDB:InitializeDB(addonName)
local db = self.db
 
if not db then
if addonName then
AceDB.addonsLoaded[addonName] = true
end
return
end
 
if db.raw then
-- someone manually initialized
return
end
 
if type(_G[db.name]) ~= "table" then
_G[db.name] = {}
else
CrawlForDeserialization(_G[db.name])
end
if db.charName then
if type(_G[db.charName]) ~= "table" then
_G[db.charName] = {}
else
CrawlForDeserialization(_G[db.charName])
end
end
rawset(db, 'raw', _G[db.name])
if not db.raw.currentProfile then
db.raw.currentProfile = {}
else
for k,v in pairs(db.raw.currentProfile) do
tmp[convertFromOldCharID(k)] = v
db.raw.currentProfile[k] = nil
end
for k,v in pairs(tmp) do
db.raw.currentProfile[k] = v
tmp[k] = nil
end
end
if not db.raw.currentProfile[charID] then
db.raw.currentProfile[charID] = AceDB.registry[self] or "Default"
end
if db.raw.profiles then
for k,v in pairs(db.raw.profiles) do
local new_k = k
if k:find("^char/") then
new_k = "char/" .. convertFromOldCharID(k:sub(6))
end
tmp[new_k] = v
db.raw.profiles[k] = nil
end
for k,v in pairs(tmp) do
db.raw.profiles[k] = v
tmp[k] = nil
end
end
if db.raw.disabledModules then -- AceModuleCore-2.0
for k,v in pairs(db.raw.disabledModules) do
local new_k = k
if k:find("^char/") then
new_k = "char/" .. convertFromOldCharID(k:sub(6))
end
tmp[new_k] = v
db.raw.disabledModules[k] = nil
end
for k,v in pairs(tmp) do
db.raw.disabledModules[k] = v
tmp[k] = nil
end
end
if db.raw.chars then
for k,v in pairs(db.raw.chars) do
tmp[convertFromOldCharID(k)] = v
db.raw.chars[k] = nil
end
for k,v in pairs(tmp) do
db.raw.chars[k] = v
tmp[k] = nil
end
end
if db.raw.namespaces then
for l,u in pairs(db.raw.namespaces) do
if u.chars then
for k,v in pairs(u.chars) do
tmp[convertFromOldCharID(k)] = v
u.chars[k] = nil
end
for k,v in pairs(tmp) do
u.chars[k] = v
tmp[k] = nil
end
end
end
end
if db.raw.disabled then
setmetatable(db.raw.disabled, caseInsensitive_mt)
end
if self['acedb-profile-copylist'] then
RecalculateAceDBCopyFromList(self)
end
if self['acedb-profile-list'] then
RecalculateAceDBProfileList(self)
end
setmetatable(db, db_mt)
end
 
function AceDB:OnEmbedInitialize(target, name)
if name then
self:ADDON_LOADED(name)
end
self.InitializeDB(target, name)
end
 
function AceDB:RegisterDB(name, charName, defaultProfile)
AceDB:argCheck(name, 2, "string")
AceDB:argCheck(charName, 3, "string", "nil")
AceDB:argCheck(defaultProfile, 4, "string", "nil")
if self.db then
AceDB:error("Cannot call \"RegisterDB\" if self.db is set.")
end
local stack = debugstack()
local addonName = stack:gsub(".-\n.-\\AddOns\\(.-)\\.*", "%1")
self.db = {
name = name,
charName = charName
}
AceDB.registry[self] = defaultProfile or "Default"
if AceDB.addonsLoaded[addonName] then
AceDB.InitializeDB(self, addonName)
else
AceDB.addonsToBeInitialized[self] = addonName
end
end
 
function AceDB:RegisterDefaults(kind, defaults, a3)
local name
if a3 then
name, kind, defaults = kind, defaults, a3
AceDB:argCheck(name, 2, "string")
AceDB:argCheck(kind, 3, "string")
AceDB:argCheck(defaults, 4, "table")
else
AceDB:argCheck(kind, 2, "string")
AceDB:argCheck(defaults, 3, "table")
end
if kind ~= "char" and kind ~= "class" and kind ~= "profile" and kind ~= "account" and kind ~= "realm" and kind ~= "faction" and kind ~= "server" then
AceDB:error("Bad argument #%d to `RegisterDefaults' (\"char\", \"class\", \"profile\", \"account\", \"realm\", \"server\", or \"faction\" expected, got %q)", a3 and 3 or 2, kind)
end
if type(self.db) ~= "table" or type(self.db.name) ~= "string" then
AceDB:error("Cannot call \"RegisterDefaults\" unless \"RegisterDB\" has been previously called.")
end
local db
if name then
local namespace = self:AcquireDBNamespace(name)
if namespace.defaults and namespace.defaults[kind] then
AceDB:error("\"RegisterDefaults\" has already been called for %q::%q.", name, kind)
end
db = namespace
else
if self.db.defaults and self.db.defaults[kind] then
AceDB:error("\"RegisterDefaults\" has already been called for %q.", kind)
end
db = self.db
end
if not db.defaults then
rawset(db, 'defaults', {})
end
db.defaults[kind] = defaults
if rawget(db, kind) then
inheritDefaults(db[kind], defaults)
end
end
 
function AceDB:ResetDB(kind, a2)
local name
if a2 then
name, kind = kind, a2
AceDB:argCheck(name, 2, "nil", "string")
AceDB:argCheck(kind, 3, "nil", "string")
else
AceDB:argCheck(kind, 2, "nil", "string")
if kind ~= "char" and kind ~= "class" and kind ~= "profile" and kind ~= "account" and kind ~= "realm" and kind ~= "faction" and kind ~= "server" then
name, kind = kind, nil
end
end
if not self.db or not self.db.raw then
AceDB:error("Cannot call \"ResetDB\" before \"RegisterDB\" has been called and before \"ADDON_LOADED\" has been fired.")
end
local db = self.db
if not kind then
if not name then
if db.charName then
_G[db.charName] = nil
end
_G[db.name] = nil
rawset(db, 'raw', nil)
AceDB.InitializeDB(self)
if db.namespaces then
for name,v in pairs(db.namespaces) do
rawset(v, 'account', nil)
rawset(v, 'char', nil)
rawset(v, 'class', nil)
rawset(v, 'profile', nil)
rawset(v, 'realm', nil)
rawset(v, 'server', nil)
rawset(v, 'faction', nil)
end
end
else
if db.raw.namespaces then
db.raw.namespaces[name] = nil
end
if db.namespaces then
local v = db.namespaces[name]
if v then
rawset(v, 'account', nil)
rawset(v, 'char', nil)
rawset(v, 'class', nil)
rawset(v, 'profile', nil)
rawset(v, 'realm', nil)
rawset(v, 'server', nil)
rawset(v, 'faction', nil)
end
end
end
elseif kind == "account" then
if name then
db.raw.account = nil
rawset(db, 'account', nil)
if db.raw.namespaces then
for name,v in pairs(db.raw.namespaces) do
v.account = nil
end
end
if db.namespaces then
for name,v in pairs(db.namespaces) do
rawset(v, 'account', nil)
end
end
else
if db.raw.namespaces and db.raw.namespaces[name] then
db.raw.namespaces[name].account = nil
end
if db.namespaces then
local v = db.namespaces[name]
if v then
rawset(v, 'account', nil)
end
end
end
elseif kind == "char" then
if name then
if db.charName then
_G[db.charName] = nil
else
if db.raw.chars then
db.raw.chars[charID] = nil
end
if db.raw.namespaces then
for name,v in pairs(db.raw.namespaces) do
if v.chars then
v.chars[charID] = nil
end
end
end
end
rawset(db, 'char', nil)
if db.namespaces then
for name,v in pairs(db.namespaces) do
rawset(v, 'char', nil)
end
end
else
if db.charName then
local x = _G[db.charName]
if x.namespaces then
x.namespaces[name] = nil
end
else
if db.raw.namespaces then
local v = db.namespaces[name]
if v and v.chars then
v.chars[charID] = nil
end
end
end
if db.namespaces then
local v = db.namespaces[name]
if v then
rawset(v, 'char', nil)
end
end
end
elseif kind == "realm" then
if not name then
if db.raw.realms then
db.raw.realms[realmID] = nil
end
rawset(db, 'realm', nil)
if db.raw.namespaces then
for name,v in pairs(db.raw.namespaces) do
if v.realms then
v.realms[realmID] = nil
end
end
end
if db.namespaces then
for name,v in pairs(db.namespaces) do
rawset(v, 'realm', nil)
end
end
else
if db.raw.namespaces then
local v = db.raw.namespaces[name]
if v and v.realms then
v.realms[realmID] = nil
end
end
if db.namespaces then
local v = db.namespaces[name]
if v then
rawset(v, 'realm', nil)
end
end
end
elseif kind == "server" then
if not name then
if db.raw.servers then
db.raw.servers[server] = nil
end
rawset(db, 'server', nil)
if db.raw.namespaces then
for name,v in pairs(db.raw.namespaces) do
if v.servers then
v.servers[server] = nil
end
end
end
if db.namespaces then
for name,v in pairs(db.namespaces) do
rawset(v, 'server', nil)
end
end
else
if db.raw.namespaces then
local v = db.raw.namespaces[name]
if v and v.servers then
v.servers[server] = nil
end
end
if db.namespaces then
local v = db.namespaces[name]
if v then
rawset(v, 'server', nil)
end
end
end
elseif kind == "faction" then
if not name then
if db.raw.factions then
db.raw.factions[faction] = nil
end
rawset(db, 'faction', nil)
if db.raw.namespaces then
for name,v in pairs(db.raw.namespaces) do
if v.factions then
v.factions[faction] = nil
end
end
end
if db.namespaces then
for name,v in pairs(db.namespaces) do
rawset(v, 'faction', nil)
end
end
else
if db.raw.namespaces then
local v = db.raw.namespaces[name]
if v and v.factions then
v.factions[faction] = nil
end
end
if db.namespaces then
local v = db.namespaces[name]
if v then
rawset(v, 'faction', nil)
end
end
end
elseif kind == "class" then
if not name then
if db.raw.realms then
db.raw.realms[classID] = nil
end
rawset(db, 'class', nil)
if db.raw.namespaces then
for name,v in pairs(db.raw.namespaces) do
if v.classes then
v.classes[classID] = nil
end
end
end
if db.namespaces then
for name,v in pairs(db.namespaces) do
rawset(v, 'class', nil)
end
end
else
if db.raw.namespaces then
local v = db.raw.namespaces[name]
if v and v.classes then
v.classes[classID] = nil
end
end
if db.namespaces then
local v = db.namespaces[name]
if v then
rawset(v, 'class', nil)
end
end
end
elseif kind == "profile" then
local id = db.raw.currentProfile and db.raw.currentProfile[charID] or AceDB.registry[self] or "Default"
if id == "char" then
id = "char/" .. charID
elseif id == "class" then
id = "class/" .. classID
elseif id == "realm" then
id = "realm/" .. realmID
end
 
local current = self.class
while current and current ~= AceOO.Class do
if current.mixins then
for mixin in pairs(current.mixins) do
if type(mixin.OnEmbedProfileDisable) == "function" then
safecall(mixin.OnEmbedProfileDisable, mixin, self, id)
end
end
end
current = current.super
end
if type(self.OnProfileDisable) == "function" then
safecall(self.OnProfileDisable, self, id)
end
local active = self:IsActive()
 
if not name then
if db.raw.profiles then
db.raw.profiles[id] = nil
end
rawset(db, 'profile', nil)
if db.raw.namespaces then
for name,v in pairs(db.raw.namespaces) do
if v.profiles then
v.profiles[id] = nil
end
end
end
if db.namespaces then
for name,v in pairs(db.namespaces) do
rawset(v, 'profile', nil)
end
end
else
if db.raw.namespaces then
local v = db.raw.namespaces[name]
if v and v.profiles then
v.profiles[id] = nil
end
end
if db.namespaces then
local v = db.namespaces[name]
if v then
rawset(v, 'profile', nil)
end
end
end
 
local current = self.class
while current and current ~= AceOO.Class do
if current.mixins then
for mixin in pairs(current.mixins) do
if type(mixin.OnEmbedProfileEnable) == "function" then
safecall(mixin.OnEmbedProfileEnable, mixin, self, id)
end
end
end
current = current.super
end
if type(self.OnProfileEnable) == "function" then
safecall(self.OnProfileEnable, self, id)
end
local newactive = self:IsActive()
if active ~= newactive then
if newactive then
local first = nil
if AceOO.inherits(self, "AceAddon-2.0") then
local AceAddon = AceLibrary("AceAddon-2.0")
if not AceAddon.addonsStarted[self] then
return
end
if AceAddon.addonsEnabled and not AceAddon.addonsEnabled[self] then
AceAddon.addonsEnabled[self] = true
first = true
end
end
local current = self.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, self, first)
end
end
end
current = current.super
end
if type(self.OnEnable) == "function" then
safecall(self.OnEnable, self, first)
end
if AceEvent then
AceEvent:TriggerEvent("Ace2_AddonEnabled", self, first)
end
else
local current = self.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, self)
end
end
end
current = current.super
end
if type(self.OnDisable) == "function" then
safecall(self.OnDisable, self)
end
if AceEvent then
AceEvent:TriggerEvent("Ace2_AddonDisabled", self)
end
end
end
else
return -- skip event
end
if AceEvent then
AceEvent:TriggerEvent("AceDB20_ResetDB", self, self.db.name, kind)
end
end
 
local function cleanDefaults(t, defaults, blocker)
if defaults then
for k,v in pairs(t) do
if (not blocker or (blocker[k] == nil and blocker['*'] == nil and blocker['**'] == nil)) and (defaults[k] ~= nil or defaults['*'] ~= nil or defaults['**'] ~= nil) then
local u = defaults[k]
if u == nil then
u = defaults['*']
if u == nil then
u = defaults['**']
end
end
if v == u then
t[k] = nil
elseif type(v) == "table" and type(u) == "table" then
if cleanDefaults(v, u) then
t[k] = nil
else
local w = defaults['**']
if w ~= u then
if cleanDefaults(v, w, u) then
t[k] = nil
end
end
end
end
end
end
end
return t and next(t) == nil
end
 
function AceDB:GetProfile()
if not self.db or not self.db.raw then
return nil
end
if not self.db.raw.currentProfile then
self.db.raw.currentProfile = {}
end
if not self.db.raw.currentProfile[charID] then
self.db.raw.currentProfile[charID] = AceDB.registry[self] or "Default"
end
local profile = self.db.raw.currentProfile[charID]
if profile == "char" then
return "char", "char/" .. charID
elseif profile == "class" then
return "class", "class/" .. classID
elseif profile == "realm" then
return "realm", "realm/" .. realmID
end
return profile, profile
end
 
local function copyTable(to, from)
setmetatable(to, nil)
for k,v in pairs(from) do
if type(k) == "table" then
k = copyTable({}, k)
end
if type(v) == "table" then
v = copyTable({}, v)
end
to[k] = v
end
setmetatable(to, from)
return to
end
 
function AceDB:SetProfile(name)
AceDB:argCheck(name, 2, "string")
if not self.db or not self.db.raw then
AceDB:error("Cannot call \"SetProfile\" before \"RegisterDB\" has been called and before \"ADDON_LOADED\" has been fired.")
end
local db = self.db
local lowerName = name:lower()
if lowerName:find("^char/") or lowerName:find("^realm/") or lowerName:find("^class/") then
if lowerName:find("^char/") then
name = "char"
else
name = lowerName:sub(1, 5)
end
lowerName = name:lower()
end
local oldName = db.raw.currentProfile[charID]
if oldName:lower() == name:lower() then
return
end
local oldProfileData = db.profile
local realName = name
if lowerName == "char" then
realName = name .. "/" .. charID
elseif lowerName == "realm" then
realName = name .. "/" .. realmID
elseif lowerName == "class" then
realName = name .. "/" .. classID
end
local current = self.class
while current and current ~= AceOO.Class do
if current.mixins then
for mixin in pairs(current.mixins) do
if type(mixin.OnEmbedProfileDisable) == "function" then
safecall(mixin.OnEmbedProfileDisable, mixin, self, realName)
end
end
end
current = current.super
end
if type(self.OnProfileDisable) == "function" then
safecall(self.OnProfileDisable, self, realName)
end
local active = self:IsActive()
db.raw.currentProfile[charID] = name
rawset(db, 'profile', nil)
if db.namespaces then
for k,v in pairs(db.namespaces) do
rawset(v, 'profile', nil)
end
end
local current = self.class
while current and current ~= AceOO.Class do
if current.mixins then
for mixin in pairs(current.mixins) do
if type(mixin.OnEmbedProfileEnable) == "function" then
safecall(mixin.OnEmbedProfileEnable, mixin, self, oldName, oldProfileData)
end
end
end
current = current.super
end
if type(self.OnProfileEnable) == "function" then
safecall(self.OnProfileEnable, self, oldName, oldProfileData)
end
if cleanDefaults(oldProfileData, db.defaults and db.defaults.profile) then
db.raw.profiles[oldName] = nil
if not next(db.raw.profiles) then
db.raw.profiles = nil
end
end
local newactive = self:IsActive()
if active ~= newactive then
local first = nil
if AceOO.inherits(self, "AceAddon-2.0") then
local AceAddon = AceLibrary("AceAddon-2.0")
if not AceAddon.addonsStarted[self] then
return
end
if AceAddon.addonsEnabled and not AceAddon.addonsEnabled[self] then
first = true
end
end
if newactive then
local current = self.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, self, first)
end
end
end
current = current.super
end
if type(self.OnEnable) == "function" then
safecall(self.OnEnable, self, first)
end
if AceEvent then
AceEvent:TriggerEvent("Ace2_AddonEnabled", self, first)
end
else
local current = self.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, self)
end
end
end
current = current.super
end
if type(self.OnDisable) == "function" then
safecall(self.OnDisable, self)
end
if AceEvent then
AceEvent:TriggerEvent("Ace2_AddonDisabled", self)
end
end
end
if self['acedb-profile-list'] then
RecalculateAceDBProfileList(self)
end
if self['acedb-profile-copylist'] then
RecalculateAceDBCopyFromList(self)
end
if Dewdrop then
Dewdrop:Refresh()
end
end
 
function AceDB:CopyProfileFrom(copyFrom)
AceDB:argCheck(copyFrom, 2, "string")
if not self.db or not self.db.raw then
AceDB:error("Cannot call \"CopyProfileFrom\" before \"RegisterDB\" has been called and before \"ADDON_LOADED\" has been fired.")
end
local db = self.db
local lowerCopyFrom = copyFrom:lower()
if not db.raw.profiles or not db.raw.profiles[copyFrom] then
local good = false
if db.raw.namespaces then
for _,n in pairs(db.raw.namespaces) do
if n.profiles and n.profiles[copyFrom] then
good = true
break
end
end
end
if not good then
AceDB:error("Cannot copy from profile %q, it does not exist.", copyFrom)
end
end
local currentProfile = db.raw.currentProfile[charID]
if currentProfile:lower() == lowerCopyFrom then
AceDB:error("Cannot copy from profile %q, it is currently in use.", copyFrom)
end
local oldProfileData = db.profile
local current = self.class
while current and current ~= AceOO.Class do
if current.mixins then
for mixin in pairs(current.mixins) do
if type(mixin.OnEmbedProfileDisable) == "function" then
safecall(mixin.OnEmbedProfileDisable, mixin, self, currentProfile)
end
end
end
current = current.super
end
if type(self.OnProfileDisable) == "function" then
safecall(self.OnProfileDisable, self, realName)
end
local active = self:IsActive()
for k,v in pairs(db.profile) do
db.profile[k] = nil
end
if db.raw.profiles[copyFrom] then
copyTable(db.profile, db.raw.profiles[copyFrom])
end
inheritDefaults(db.profile, db.defaults and db.defaults.profile)
if db.namespaces then
for l,u in pairs(db.namespaces) do
for k,v in pairs(u.profile) do
u.profile[k] = nil
end
if db.raw.namespaces[l].profiles[copyFrom] then
copyTable(u.profile, db.raw.namespaces[l].profiles[copyFrom])
end
inheritDefaults(u.profile, u.defaults and u.defaults.profile)
end
end
local current = self.class
while current and current ~= AceOO.Class do
if current.mixins then
for mixin in pairs(current.mixins) do
if type(mixin.OnEmbedProfileEnable) == "function" then
safecall(mixin.OnEmbedProfileEnable, mixin, self, copyFrom, oldProfileData, copyFrom)
end
end
end
current = current.super
end
if type(self.OnProfileEnable) == "function" then
safecall(self.OnProfileEnable, self, copyFrom, oldProfileData, copyFrom)
end
local newactive = self:IsActive()
if active ~= newactive then
if AceOO.inherits(self, "AceAddon-2.0") then
local AceAddon = AceLibrary("AceAddon-2.0")
if not AceAddon.addonsStarted[self] then
return
end
end
if newactive then
local current = self.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, self)
end
end
end
current = current.super
end
if type(self.OnEnable) == "function" then
safecall(self.OnEnable, self)
end
if AceEvent then
AceEvent:TriggerEvent("Ace2_AddonEnabled", self)
end
else
local current = self.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, self)
end
end
end
current = current.super
end
if type(self.OnDisable) == "function" then
safecall(self.OnDisable, self)
end
if AceEvent then
AceEvent:TriggerEvent("Ace2_AddonDisabled", self)
end
end
end
if self['acedb-profile-list'] then
RecalculateAceDBProfileList(self)
end
if self['acedb-profile-copylist'] then
RecalculateAceDBCopyFromList(self)
end
if Dewdrop then
Dewdrop:Refresh()
end
end
 
function AceDB:DeleteProfile(profile, noconfirm)
AceDB:argCheck(profile , 2, "string")
if not self.db or not self.db.raw then
AceDB:error("Cannot call \"DeleteProfile\" before \"RegisterDB\" has been called and before \"ADDON_LOADED\" has been fired.")
end
local db = self.db
 
local currentProfile = db.raw.currentProfile[charID]
if currentProfile:lower() == profile:lower() then
AceDB:error("Cannot delete profile %q, it is currently in use.", profile)
end
 
if not (noconfirm or IsShiftKeyDown()) then
if not StaticPopupDialogs["ACEDB20_CONFIRM_DELETE_DIALOG"] then
StaticPopupDialogs["ACEDB20_CONFIRM_DELETE_DIALOG"] = {}
end
local t = StaticPopupDialogs["ACEDB20_CONFIRM_DELETE_DIALOG"]
t.text = format("%s: %s?", DELETE_PROFILE, profile)
t.button1 = DELETE_PROFILE
t.button2 = CANCEL or "Cancel"
t.OnAccept = function()
self:DeleteProfile(profile, true)
end
t.timeout = 0
t.whileDead = 1
t.hideOnEscape = 1
 
StaticPopup_Show("ACEDB20_CONFIRM_DELETE_DIALOG")
return;
end
 
local good = false
if db.raw.profiles and db.raw.profiles[profile] then
good = true;
db.raw.profiles[profile] = nil;
end
 
if db.raw.namespaces then
for _,n in pairs(db.raw.namespaces) do
if n.profiles and n.profiles[profile] then
n.profiles[profile] = nil;
good = true
end
end
end
 
if not good then
AceDB:error("Cannot delete profile %q, it does not exist.", profile)
end
 
if self['acedb-profile-list'] then
RecalculateAceDBProfileList(self)
end
if self['acedb-profile-copylist'] then
RecalculateAceDBCopyFromList(self)
end
 
if Dewdrop then
Dewdrop:Refresh()
end
end
 
function AceDB:IsActive()
return not self.db or not self.db.raw or not self.db.raw.disabled or not self.db.raw.disabled[self.db.raw.currentProfile[charID]]
end
 
function AceDB:ToggleActive(state)
AceDB:argCheck(state, 2, "boolean", "nil")
if not self.db or not self.db.raw then
AceDB:error("Cannot call \"ToggleActive\" before \"RegisterDB\" has been called and before \"ADDON_LOADED\" has been fired.")
end
local db = self.db
if not db.raw.disabled then
db.raw.disabled = setmetatable({}, caseInsensitive_mt)
end
local profile = db.raw.currentProfile[charID]
local disable
if state == nil then
disable = not db.raw.disabled[profile]
else
disable = not state
if disable == db.raw.disabled[profile] then
return
end
end
db.raw.disabled[profile] = disable or nil
if AceOO.inherits(self, "AceAddon-2.0") then
local AceAddon = AceLibrary("AceAddon-2.0")
if not AceAddon.addonsStarted[self] then
return
end
end
if not disable then
local current = self.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, self)
end
end
end
current = current.super
end
if type(self.OnEnable) == "function" then
safecall(self.OnEnable, self)
end
if AceEvent then
AceEvent:TriggerEvent("Ace2_AddonEnabled", self)
end
else
local current = self.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, self)
end
end
end
current = current.super
end
if type(self.OnDisable) == "function" then
safecall(self.OnDisable, self)
end
if AceEvent then
AceEvent:TriggerEvent("Ace2_AddonDisabled", self)
end
end
return not disable
end
 
function AceDB:embed(target)
self.super.embed(self, target)
if not AceEvent then
AceDB:error(MAJOR_VERSION .. " requires AceEvent-2.0")
end
end
 
function AceDB:ADDON_LOADED(name)
AceDB.addonsLoaded[name] = true
for addon, addonName in pairs(AceDB.addonsToBeInitialized) do
if name == addonName then
AceDB.InitializeDB(addon, name)
AceDB.addonsToBeInitialized[addon] = nil
end
end
end
 
function AceDB:PLAYER_LOGOUT()
for addon, defaultProfile in pairs(AceDB.registry) do
local db = addon.db
if db then
if type(addon.OnDatabaseCleanup) == "function" then
safecall(addon.OnDatabaseCleanup, addon)
end
setmetatable(db, nil)
CrawlForSerialization(db.raw)
if type(_G[db.charName]) == "table" then
CrawlForSerialization(_G[db.charName])
end
if db.char and cleanDefaults(db.char, db.defaults and db.defaults.char) then
if db.charName and _G[db.charName] and _G[db.charName].global == db.char then
_G[db.charName].global = nil
if not next(_G[db.charName]) then
_G[db.charName] = nil
end
else
if db.raw.chars then
db.raw.chars[charID] = nil
if not next(db.raw.chars) then
db.raw.chars = nil
end
end
end
end
if db.realm and cleanDefaults(db.realm, db.defaults and db.defaults.realm) then
if db.raw.realms then
db.raw.realms[realmID] = nil
if not next(db.raw.realms) then
db.raw.realms = nil
end
end
end
if db.server and cleanDefaults(db.server, db.defaults and db.defaults.server) then
if db.raw.servers then
db.raw.servers[server] = nil
if not next(db.raw.servers) then
db.raw.servers = nil
end
end
end
if db.faction and cleanDefaults(db.faction, db.defaults and db.defaults.faction) then
if db.raw.factions then
db.raw.factions[faction] = nil
if not next(db.raw.factions) then
db.raw.factions = nil
end
end
end
if db.class and cleanDefaults(db.class, db.defaults and db.defaults.class) then
if db.raw.classes then
db.raw.classes[classID] = nil
if not next(db.raw.classes) then
db.raw.classes = nil
end
end
end
if db.account and cleanDefaults(db.account, db.defaults and db.defaults.account) then
db.raw.account = nil
end
if db.profile and cleanDefaults(db.profile, db.defaults and db.defaults.profile) then
if db.raw.profiles then
db.raw.profiles[db.raw.currentProfile and db.raw.currentProfile[charID] or defaultProfile or "Default"] = nil
if not next(db.raw.profiles) then
db.raw.profiles = nil
end
end
end
if db.namespaces and db.raw.namespaces then
for name,v in pairs(db.namespaces) do
if db.raw.namespaces[name] then
setmetatable(v, nil)
if v.char and cleanDefaults(v.char, v.defaults and v.defaults.char) then
if db.charName and _G[db.charName] and _G[db.charName].namespaces and _G[db.charName].namespaces[name] == v then
_G[db.charName].namespaces[name] = nil
if not next(_G[db.charName].namespaces) then
_G[db.charName].namespaces = nil
if not next(_G[db.charName]) then
_G[db.charName] = nil
end
end
else
if db.raw.namespaces[name].chars then
db.raw.namespaces[name].chars[charID] = nil
if not next(db.raw.namespaces[name].chars) then
db.raw.namespaces[name].chars = nil
end
end
end
end
if v.realm and cleanDefaults(v.realm, v.defaults and v.defaults.realm) then
if db.raw.namespaces[name].realms then
db.raw.namespaces[name].realms[realmID] = nil
if not next(db.raw.namespaces[name].realms) then
db.raw.namespaces[name].realms = nil
end
end
end
if v.server and cleanDefaults(v.server, v.defaults and v.defaults.server) then
if db.raw.namespaces[name].servers then
db.raw.namespaces[name].servers[server] = nil
if not next(db.raw.namespaces[name].servers) then
db.raw.namespaces[name].servers = nil
end
end
end
if v.faction and cleanDefaults(v.faction, v.defaults and v.defaults.faction) then
if db.raw.namespaces[name].factions then
db.raw.namespaces[name].factions[faction] = nil
if not next(db.raw.namespaces[name].factions) then
db.raw.namespaces[name].factions = nil
end
end
end
if v.class and cleanDefaults(v.class, v.defaults and v.defaults.class) then
if db.raw.namespaces[name].classes then
db.raw.namespaces[name].classes[classID] = nil
if not next(db.raw.namespaces[name].classes) then
db.raw.namespaces[name].classes = nil
end
end
end
if v.account and cleanDefaults(v.account, v.defaults and v.defaults.account) then
db.raw.namespaces[name].account = nil
end
if v.profile and cleanDefaults(v.profile, v.defaults and v.defaults.profile) then
if db.raw.namespaces[name].profiles then
db.raw.namespaces[name].profiles[db.raw.currentProfile and db.raw.currentProfile[charID] or defaultProfile or "Default"] = nil
if not next(db.raw.namespaces[name].profiles) then
db.raw.namespaces[name].profiles = nil
end
end
end
if not next(db.raw.namespaces[name]) then
db.raw.namespaces[name] = nil
end
end
end
if not next(db.raw.namespaces) then
db.raw.namespaces = nil
end
end
if db.raw.disabled and not next(db.raw.disabled) then
db.raw.disabled = nil
end
if db.raw.currentProfile then
for k,v in pairs(db.raw.currentProfile) do
if v:lower() == (defaultProfile or "Default"):lower() then
db.raw.currentProfile[k] = nil
end
end
if not next(db.raw.currentProfile) then
db.raw.currentProfile = nil
end
end
if _G[db.name] and not next(_G[db.name]) then
_G[db.name] = nil
end
end
end
end
 
function AceDB:AcquireDBNamespace(name)
AceDB:argCheck(name, 2, "string")
local db = self.db
if not db then
AceDB:error("Cannot call `AcquireDBNamespace' before `RegisterDB' has been called.", 2)
end
if not db.namespaces then
rawset(db, 'namespaces', {})
end
if not db.namespaces[name] then
local namespace = {}
db.namespaces[name] = namespace
namespace.db = db
namespace.name = name
setmetatable(namespace, namespace_mt)
end
return db.namespaces[name]
end
 
function AceDB:GetAceOptionsDataTable(target)
if not target['acedb-profile-list'] then
target['acedb-profile-list'] = setmetatable({}, caseInsensitive_mt)
RecalculateAceDBProfileList(target)
end
if not target['acedb-profile-copylist'] then
target['acedb-profile-copylist'] = setmetatable({}, caseInsensitive_mt)
RecalculateAceDBCopyFromList(target)
end
return {
standby = {
cmdName = STATE,
guiName = ENABLED,
name = ACTIVE,
desc = TOGGLE_ACTIVE,
type = "toggle",
get = "IsActive",
set = "ToggleActive",
map = MAP_ACTIVESUSPENDED,
order = -3,
},
profile = {
type = 'group',
name = PROFILE,
desc = SET_PROFILE,
order = -3.5,
get = "GetProfile",
args = {
choose = {
guiName = CHOOSE_PROFILE_GUI,
cmdName = PROFILE,
desc = CHOOSE_PROFILE_DESC,
type = 'text',
get = "GetProfile",
set = "SetProfile",
validate = target['acedb-profile-list']
},
copy = {
guiName = COPY_PROFILE_GUI,
cmdName = PROFILE,
desc = COPY_PROFILE_DESC,
type = 'text',
get = false,
set = "CopyProfileFrom",
validate = target['acedb-profile-copylist'],
disabled = function()
return not next(target['acedb-profile-copylist'])
end,
},
other = {
guiName = OTHER_PROFILE_GUI,
cmdName = PROFILE,
desc = OTHER_PROFILE_DESC,
usage = OTHER_PROFILE_USAGE,
type = 'text',
get = "GetProfile",
set = "SetProfile",
},
delete = {
name = DELETE_PROFILE,
desc = DELETE_PROFILE_DESC,
usage = DELETE_PROFILE_USAGE,
type = 'text',
set = "DeleteProfile",
get = false,
validate = target['acedb-profile-copylist'],
disabled = function()
return not next(target['acedb-profile-copylist'])
end,
},
reset = {
name = RESET_PROFILE,
desc = RESET_PROFILE_DESC,
type = 'execute',
func = function()
target:ResetDB('profile')
end,
confirm = true,
}
}
},
}
end
 
local function activate(self, oldLib, oldDeactivate)
AceDB = self
AceEvent = AceLibrary:HasInstance("AceEvent-2.0") and AceLibrary("AceEvent-2.0")
 
self.addonsToBeInitialized = oldLib and oldLib.addonsToBeInitialized or {}
self.addonsLoaded = oldLib and oldLib.addonsLoaded or {}
self.registry = oldLib and oldLib.registry or {}
for k, v in pairs(self.registry) do
if v == true then
self.registry[k] = "Default"
end
end
 
self:activate(oldLib, oldDeactivate)
 
for t in pairs(self.embedList) do
if t.db then
rawset(t.db, 'char', nil)
rawset(t.db, 'realm', nil)
rawset(t.db, 'class', nil)
rawset(t.db, 'account', nil)
rawset(t.db, 'server', nil)
rawset(t.db, 'faction', nil)
rawset(t.db, 'profile', nil)
setmetatable(t.db, db_mt)
end
end
 
if oldLib then
oldDeactivate(oldLib)
end
end
 
local function external(self, major, instance)
if major == "AceEvent-2.0" then
AceEvent = instance
 
AceEvent:embed(self)
 
self:RegisterEvent("ADDON_LOADED")
self:RegisterEvent("PLAYER_LOGOUT")
elseif major == "Dewdrop-2.0" then
Dewdrop = instance
end
end
 
AceLibrary:Register(AceDB, MAJOR_VERSION, MINOR_VERSION, activate, nil, external)
AceDB = AceLibrary(MAJOR_VERSION)
trunk/StunWatch/Libs/AceLibrary/AceLibrary.lua New file
0,0 → 1,799
--[[
Name: AceLibrary
Revision: $Rev: 58127 $
Developed by: The Ace Development Team (http://www.wowace.com/index.php/The_Ace_Development_Team)
Inspired By: Iriel (iriel@vigilance-committee.org)
Tekkub (tekkub@gmail.com)
Revision: $Rev: 58127 $
Website: http://www.wowace.com/
Documentation: http://www.wowace.com/index.php/AceLibrary
SVN: http://svn.wowace.com/root/trunk/Ace2/AceLibrary
Description: Versioning library to handle other library instances, upgrading,
and proper access.
It also provides a base for libraries to work off of, providing
proper error tools. It is handy because all the errors occur in the
file that called it, not in the library file itself.
Dependencies: None
License: LGPL v2.1
]]
 
local ACELIBRARY_MAJOR = "AceLibrary"
local ACELIBRARY_MINOR = "$Revision: 58127 $"
 
local _G = getfenv(0)
local previous = _G[ACELIBRARY_MAJOR]
if previous and not previous:IsNewVersion(ACELIBRARY_MAJOR, ACELIBRARY_MINOR) then return end
 
do
-- 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
end
local LibStub = _G.LibStub
 
-- If you don't want AceLibrary to enable libraries that are LoadOnDemand but
-- disabled in the addon screen, set this to true.
local DONT_ENABLE_LIBRARIES = nil
 
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
 
-- @table AceLibrary
-- @brief System to handle all versioning of libraries.
local AceLibrary = {}
local AceLibrary_mt = {}
setmetatable(AceLibrary, AceLibrary_mt)
 
local function error(self, message, ...)
if type(self) ~= "table" then
return _G.error(("Bad argument #1 to `error' (table expected, got %s)"):format(type(self)), 2)
end
 
local stack = debugstack()
if not message then
local second = stack:match("\n(.-)\n")
message = "error raised! " .. second
else
local arg = { ... } -- not worried about table creation, as errors don't happen often
 
for i = 1, #arg do
arg[i] = tostring(arg[i])
end
for i = 1, 10 do
table.insert(arg, "nil")
end
message = message:format(unpack(arg))
end
 
if getmetatable(self) and getmetatable(self).__tostring then
message = ("%s: %s"):format(tostring(self), message)
elseif type(rawget(self, 'GetLibraryVersion')) == "function" and AceLibrary:HasInstance(self:GetLibraryVersion()) then
message = ("%s: %s"):format(self:GetLibraryVersion(), message)
elseif type(rawget(self, 'class')) == "table" and type(rawget(self.class, 'GetLibraryVersion')) == "function" and AceLibrary:HasInstance(self.class:GetLibraryVersion()) then
message = ("%s: %s"):format(self.class:GetLibraryVersion(), message)
end
 
local first = stack:gsub("\n.*", "")
local file = first:gsub(".*\\(.*).lua:%d+: .*", "%1")
file = file:gsub("([%(%)%.%*%+%-%[%]%?%^%$%%])", "%%%1")
 
 
local i = 0
for s in stack:gmatch("\n([^\n]*)") do
i = i + 1
if not s:find(file .. "%.lua:%d+:") and not s:find("%(tail call%)") then
file = s:gsub("^.*\\(.*).lua:%d+: .*", "%1")
file = file:gsub("([%(%)%.%*%+%-%[%]%?%^%$%%])", "%%%1")
break
end
end
local j = 0
for s in stack:gmatch("\n([^\n]*)") do
j = j + 1
if j > i and not s:find(file .. "%.lua:%d+:") and not s:find("%(tail call%)") then
return _G.error(message, j+1)
end
end
return _G.error(message, 2)
end
 
local type = type
local function argCheck(self, arg, num, kind, kind2, kind3, kind4, kind5)
if type(num) ~= "number" then
return error(self, "Bad argument #3 to `argCheck' (number expected, got %s)", type(num))
elseif type(kind) ~= "string" then
return error(self, "Bad argument #4 to `argCheck' (string expected, got %s)", type(kind))
end
arg = type(arg)
if arg ~= kind and arg ~= kind2 and arg ~= kind3 and arg ~= kind4 and arg ~= kind5 then
local stack = debugstack()
local func = stack:match("`argCheck'.-([`<].-['>])")
if not func then
func = stack:match("([`<].-['>])")
end
if kind5 then
return error(self, "Bad argument #%s to %s (%s, %s, %s, %s, or %s expected, got %s)", tonumber(num) or 0/0, func, kind, kind2, kind3, kind4, kind5, arg)
elseif kind4 then
return error(self, "Bad argument #%s to %s (%s, %s, %s, or %s expected, got %s)", tonumber(num) or 0/0, func, kind, kind2, kind3, kind4, arg)
elseif kind3 then
return error(self, "Bad argument #%s to %s (%s, %s, or %s expected, got %s)", tonumber(num) or 0/0, func, kind, kind2, kind3, arg)
elseif kind2 then
return error(self, "Bad argument #%s to %s (%s or %s expected, got %s)", tonumber(num) or 0/0, func, kind, kind2, arg)
else
return error(self, "Bad argument #%s to %s (%s expected, got %s)", tonumber(num) or 0/0, func, kind, arg)
end
end
end
 
local pcall
do
local function check(self, ret, ...)
if not ret then
local s = ...
return error(self, (s:gsub(".-%.lua:%d-: ", "")))
else
return ...
end
end
 
function pcall(self, func, ...)
return check(self, _G.pcall(func, ...))
end
end
 
local recurse = {}
local function addToPositions(t, major)
if not AceLibrary.positions[t] or AceLibrary.positions[t] == major then
rawset(t, recurse, true)
AceLibrary.positions[t] = major
for k,v in pairs(t) do
if type(v) == "table" and not rawget(v, recurse) then
addToPositions(v, major)
end
if type(k) == "table" and not rawget(k, recurse) then
addToPositions(k, major)
end
end
local mt = getmetatable(t)
if mt and not rawget(mt, recurse) then
addToPositions(mt, major)
end
rawset(t, recurse, nil)
end
end
 
local function svnRevisionToNumber(text)
local kind = type(text)
if kind == "number" or tonumber(text) then
return tonumber(text)
elseif kind == "string" then
if text:find("^%$Revision: (%d+) %$$") then
return tonumber((text:match("^%$Revision: (%d+) %$$")))
elseif text:find("^%$Rev: (%d+) %$$") then
return tonumber((text:match("^%$Rev: (%d+) %$$")))
elseif text:find("^%$LastChangedRevision: (%d+) %$$") then
return tonumber((text:match("^%$LastChangedRevision: (%d+) %$$")))
end
end
return nil
end
 
local crawlReplace
do
local recurse = {}
local function func(t, to, from)
if recurse[t] then
return
end
recurse[t] = true
local mt = getmetatable(t)
setmetatable(t, nil)
rawset(t, to, rawget(t, from))
rawset(t, from, nil)
for k,v in pairs(t) do
if v == from then
t[k] = to
elseif type(v) == "table" then
if not recurse[v] then
func(v, to, from)
end
end
 
if type(k) == "table" then
if not recurse[k] then
func(k, to, from)
end
end
end
setmetatable(t, mt)
if mt then
if mt == from then
setmetatable(t, to)
elseif not recurse[mt] then
func(mt, to, from)
end
end
end
function crawlReplace(t, to, from)
func(t, to, from)
for k in pairs(recurse) do
recurse[k] = nil
end
end
end
 
-- @function destroyTable
-- @brief remove all the contents of a table
-- @param t table to destroy
local function destroyTable(t)
setmetatable(t, nil)
for k,v in pairs(t) do
t[k] = nil
end
end
 
local function isFrame(frame)
return type(frame) == "table" and type(rawget(frame, 0)) == "userdata" and type(rawget(frame, 'IsFrameType')) == "function" and getmetatable(frame) and type(rawget(getmetatable(frame), '__index')) == "function"
end
 
-- @function copyTable
-- @brief Create a shallow copy of a table and return it.
-- @param from The table to copy from
-- @return A shallow copy of the table
local function copyTable(from, to)
if not to then
to = {}
end
for k,v in pairs(from) do
to[k] = v
end
setmetatable(to, getmetatable(from))
return to
end
 
-- @function deepTransfer
-- @brief Fully transfer all data, keeping proper previous table
-- backreferences stable.
-- @param to The table with which data is to be injected into
-- @param from The table whose data will be injected into the first
-- @param saveFields If available, a shallow copy of the basic data is saved
-- in here.
-- @param list The account of table references
-- @param list2 The current status on which tables have been traversed.
local deepTransfer
do
-- @function examine
-- @brief Take account of all the table references to be shared
-- between the to and from tables.
-- @param to The table with which data is to be injected into
-- @param from The table whose data will be injected into the first
-- @param list An account of the table references
local function examine(to, from, list, major)
list[from] = to
for k,v in pairs(from) do
if rawget(to, k) and type(from[k]) == "table" and type(to[k]) == "table" and not list[from[k]] then
if from[k] == to[k] then
list[from[k]] = to[k]
elseif AceLibrary.positions[from[v]] ~= major and AceLibrary.positions[from[v]] then
list[from[k]] = from[k]
elseif not list[from[k]] then
examine(to[k], from[k], list, major)
end
end
end
return list
end
 
function deepTransfer(to, from, saveFields, major, list, list2)
setmetatable(to, nil)
if not list then
list = {}
list2 = {}
examine(to, from, list, major)
end
list2[to] = to
for k,v in pairs(to) do
if type(rawget(from, k)) ~= "table" or type(v) ~= "table" or isFrame(v) then
if saveFields then
saveFields[k] = v
end
to[k] = nil
elseif v ~= _G then
if saveFields then
saveFields[k] = copyTable(v)
end
end
end
for k in pairs(from) do
if rawget(to, k) and to[k] ~= from[k] and AceLibrary.positions[to[k]] == major and from[k] ~= _G then
if not list2[to[k]] then
deepTransfer(to[k], from[k], nil, major, list, list2)
end
to[k] = list[to[k]] or list2[to[k]]
else
rawset(to, k, from[k])
end
end
setmetatable(to, getmetatable(from))
local mt = getmetatable(to)
if mt then
if list[mt] then
setmetatable(to, list[mt])
elseif mt.__index and list[mt.__index] then
mt.__index = list[mt.__index]
end
end
destroyTable(from)
end
end
 
local function TryToEnable(addon)
if DONT_ENABLE_LIBRARIES then return end
local isondemand = IsAddOnLoadOnDemand(addon)
if isondemand 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
 
-- @method TryToLoadStandalone
-- @brief Attempt to find and load a standalone version of the requested library
-- @param major A string representing the major version
-- @return If library is found and loaded, true is return. If not loadable, false is returned.
-- If the library has been requested previously, nil is returned.
local function TryToLoadStandalone(major)
if not AceLibrary.scannedlibs then AceLibrary.scannedlibs = {} end
if AceLibrary.scannedlibs[major] then return end
 
AceLibrary.scannedlibs[major] = true
 
local name, _, _, enabled, loadable = GetAddOnInfo(major)
 
loadable = (enabled and loadable) or TryToEnable(name)
 
local loaded = false
if loadable then
loaded = true
LoadAddOn(name)
end
 
local field = "X-AceLibrary-" .. major
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
end
 
-- @method IsNewVersion
-- @brief Obtain whether the supplied version would be an upgrade to the
-- current version. This allows for bypass code in library
-- declaration.
-- @param major A string representing the major version
-- @param minor An integer or an svn revision string representing the minor version
-- @return whether the supplied version would be newer than what is
-- currently available.
function AceLibrary:IsNewVersion(major, minor)
argCheck(self, major, 2, "string")
TryToLoadStandalone(major)
 
if type(minor) == "string" then
local m = svnRevisionToNumber(minor)
if m then
minor = m
else
_G.error(("Bad argument #3 to `IsNewVersion'. Must be a number or SVN revision string. %q is not appropriate"):format(minor), 2)
end
end
argCheck(self, minor, 3, "number")
local lib, oldMinor = LibStub:GetLibrary(major, true)
if lib then
return oldMinor < minor
end
local data = self.libs[major]
if not data then
return true
end
return data.minor < minor
end
 
-- @method HasInstance
-- @brief Returns whether an instance exists. This allows for optional support of a library.
-- @param major A string representing the major version.
-- @param minor (optional) An integer or an svn revision string representing the minor version.
-- @return Whether an instance exists.
function AceLibrary:HasInstance(major, minor)
argCheck(self, major, 2, "string")
if minor ~= false then
TryToLoadStandalone(major)
end
 
local lib, ver = LibStub:GetLibrary(major, true)
if not lib and self.libs[major] then
lib, ver = self.libs[major].instance, self.libs[major].minor
end
if minor then
if type(minor) == "string" then
local m = svnRevisionToNumber(minor)
if m then
minor = m
else
_G.error(("Bad argument #3 to `HasInstance'. Must be a number or SVN revision string. %q is not appropriate"):format(minor), 2)
end
end
argCheck(self, minor, 3, "number")
if not lib then
return false
end
return ver == minor
end
return not not lib
end
 
-- @method GetInstance
-- @brief Returns the library with the given major/minor version.
-- @param major A string representing the major version.
-- @param minor (optional) An integer or an svn revision string representing the minor version.
-- @return The library with the given major/minor version.
function AceLibrary:GetInstance(major, minor)
argCheck(self, major, 2, "string")
if minor ~= false then
TryToLoadStandalone(major)
end
 
local data, ver = LibStub:GetLibrary(major, true)
if not data then
if self.libs[major] then
data, ver = self.libs[major].instance, self.libs[major].minor
else
_G.error(("Cannot find a library instance of %s."):format(major), 2)
return
end
end
if minor then
if type(minor) == "string" then
local m = svnRevisionToNumber(minor)
if m then
minor = m
else
_G.error(("Bad argument #3 to `GetInstance'. Must be a number or SVN revision string. %q is not appropriate"):format(minor), 2)
end
end
argCheck(self, minor, 2, "number")
if ver ~= minor then
_G.error(("Cannot find a library instance of %s, minor version %d."):format(major, minor), 2)
end
end
return data
end
 
-- Syntax sugar. AceLibrary("FooBar-1.0")
AceLibrary_mt.__call = AceLibrary.GetInstance
 
local donothing = function() end
 
local AceEvent
 
local tmp = {}
 
-- @method Register
-- @brief Registers a new version of a given library.
-- @param newInstance the library to register
-- @param major the major version of the library
-- @param minor the minor version of the library
-- @param activateFunc (optional) A function to be called when the library is
-- fully activated. Takes the arguments
-- (newInstance [, oldInstance, oldDeactivateFunc]). If
-- oldInstance is given, you should probably call
-- oldDeactivateFunc(oldInstance).
-- @param deactivateFunc (optional) A function to be called by a newer library's
-- activateFunc.
-- @param externalFunc (optional) A function to be called whenever a new
-- library is registered.
function AceLibrary:Register(newInstance, major, minor, activateFunc, deactivateFunc, externalFunc)
argCheck(self, newInstance, 2, "table")
argCheck(self, major, 3, "string")
if major ~= ACELIBRARY_MAJOR then
for k,v in pairs(_G) do
if v == newInstance then
geterrorhandler()((debugstack():match("(.-: )in.-\n") or "") .. ("Cannot register library %q. It is part of the global table in _G[%q]."):format(major, k))
end
end
end
if major ~= ACELIBRARY_MAJOR and not major:find("^[%a%-][%a%d%-]*%-%d+%.%d+$") then
_G.error(string.format("Bad argument #3 to `Register'. Must be in the form of \"Name-1.0\". %q is not appropriate", major), 2)
end
if type(minor) == "string" then
local m = svnRevisionToNumber(minor)
if m then
minor = m
else
_G.error(("Bad argument #4 to `Register'. Must be a number or SVN revision string. %q is not appropriate"):format(minor), 2)
end
end
argCheck(self, minor, 4, "number")
if math.floor(minor) ~= minor or minor < 0 then
error(self, "Bad argument #4 to `Register' (integer >= 0 expected, got %s)", minor)
end
argCheck(self, activateFunc, 5, "function", "nil")
argCheck(self, deactivateFunc, 6, "function", "nil")
argCheck(self, externalFunc, 7, "function", "nil")
if not deactivateFunc then
deactivateFunc = donothing
end
local data = self.libs[major]
if not data then
-- This is new
if LibStub:GetLibrary(major, true) then
error(self, "Cannot register library %q. It is already registered with LibStub.", major)
end
local instance = LibStub:NewLibrary(major, minor)
copyTable(newInstance, instance)
crawlReplace(instance, instance, newInstance)
destroyTable(newInstance)
if AceLibrary == newInstance then
self = instance
AceLibrary = instance
end
self.libs[major] = {
instance = instance,
minor = minor,
deactivateFunc = deactivateFunc,
externalFunc = externalFunc,
}
rawset(instance, 'GetLibraryVersion', function(self)
return major, minor
end)
if not rawget(instance, 'error') then
rawset(instance, 'error', error)
end
if not rawget(instance, 'argCheck') then
rawset(instance, 'argCheck', argCheck)
end
if not rawget(instance, 'pcall') then
rawset(instance, 'pcall', pcall)
end
addToPositions(instance, major)
if activateFunc then
safecall(activateFunc, instance, nil, nil) -- no old version, so explicit nil
end
 
if externalFunc then
for k, data_instance in LibStub:IterateLibraries() do -- all libraries
tmp[k] = data_instance
end
for k, data in pairs(self.libs) do -- Ace libraries which may not have been registered with LibStub
tmp[k] = data.instance
end
for k, data_instance in pairs(tmp) do
if k ~= major then
safecall(externalFunc, instance, k, data_instance)
end
tmp[k] = nil
end
end
 
for k,data in pairs(self.libs) do -- only Ace libraries
if k ~= major and data.externalFunc then
safecall(data.externalFunc, data.instance, major, instance)
end
end
if major == "AceEvent-2.0" then
AceEvent = instance
end
if AceEvent then
AceEvent.TriggerEvent(self, "AceLibrary_Register", major, instance)
end
 
return instance
end
if minor <= data.minor then
-- This one is already obsolete, raise an error.
_G.error(("Obsolete library registered. %s is already registered at version %d. You are trying to register version %d. Hint: if not AceLibrary:IsNewVersion(%q, %d) then return end"):format(major, data.minor, minor, major, minor), 2)
return
end
local instance = data.instance
-- This is an update
local oldInstance = {}
 
local libStubInstance = LibStub:GetLibrary(major, true)
if not libStubInstance then -- non-LibStub AceLibrary registered the library
-- pass
elseif libStubInstance ~= instance then
error(self, "Cannot register library %q. It is already registered with LibStub.", major)
else
LibStub:NewLibrary(major, minor) -- upgrade the minor version
end
 
addToPositions(newInstance, major)
local isAceLibrary = (AceLibrary == newInstance)
local old_error, old_argCheck, old_pcall
if isAceLibrary then
self = instance
AceLibrary = instance
 
old_error = instance.error
old_argCheck = instance.argCheck
old_pcall = instance.pcall
 
self.error = error
self.argCheck = argCheck
self.pcall = pcall
end
deepTransfer(instance, newInstance, oldInstance, major)
crawlReplace(instance, instance, newInstance)
local oldDeactivateFunc = data.deactivateFunc
data.minor = minor
data.deactivateFunc = deactivateFunc
data.externalFunc = externalFunc
rawset(instance, 'GetLibraryVersion', function()
return major, minor
end)
if not rawget(instance, 'error') then
rawset(instance, 'error', error)
end
if not rawget(instance, 'argCheck') then
rawset(instance, 'argCheck', argCheck)
end
if not rawget(instance, 'pcall') then
rawset(instance, 'pcall', pcall)
end
if isAceLibrary then
for _,v in pairs(self.libs) do
local i = type(v) == "table" and v.instance
if type(i) == "table" then
if not rawget(i, 'error') or i.error == old_error then
rawset(i, 'error', error)
end
if not rawget(i, 'argCheck') or i.argCheck == old_argCheck then
rawset(i, 'argCheck', argCheck)
end
if not rawget(i, 'pcall') or i.pcall == old_pcall then
rawset(i, 'pcall', pcall)
end
end
end
end
if activateFunc then
safecall(activateFunc, instance, oldInstance, oldDeactivateFunc)
else
safecall(oldDeactivateFunc, oldInstance)
end
oldInstance = nil
 
if externalFunc then
for k, data_instance in LibStub:IterateLibraries() do -- all libraries
tmp[k] = data_instance
end
for k, data in pairs(self.libs) do -- Ace libraries which may not have been registered with LibStub
tmp[k] = data.instance
end
for k, data_instance in pairs(tmp) do
if k ~= major then
safecall(externalFunc, instance, k, data_instance)
end
tmp[k] = nil
end
end
 
return instance
end
 
function AceLibrary:IterateLibraries()
local t = {}
for major, instance in LibStub:IterateLibraries() do
t[major] = instance
end
for major, data in pairs(self.libs) do
t[major] = data.instance
end
return pairs(t)
end
 
local function manuallyFinalize(major, instance)
if AceLibrary.libs[major] then
-- don't work on Ace libraries
return
end
local finalizedExternalLibs = AceLibrary.finalizedExternalLibs
if finalizedExternalLibs[major] then
return
end
finalizedExternalLibs[major] = true
 
for k,data in pairs(AceLibrary.libs) do -- only Ace libraries
if k ~= major and data.externalFunc then
safecall(data.externalFunc, data.instance, major, instance)
end
end
end
 
-- @function Activate
-- @brief The activateFunc for AceLibrary itself. Called when
-- AceLibrary properly registers.
-- @param self Reference to AceLibrary
-- @param oldLib (optional) Reference to an old version of AceLibrary
-- @param oldDeactivate (optional) Function to deactivate the old lib
local function activate(self, oldLib, oldDeactivate)
AceLibrary = self
if not self.libs then
self.libs = oldLib and oldLib.libs or {}
self.scannedlibs = oldLib and oldLib.scannedlibs or {}
end
if not self.positions then
self.positions = oldLib and oldLib.positions or setmetatable({}, { __mode = "k" })
end
self.finalizedExternalLibs = oldLib and oldLib.finalizedExternalLibs or {}
self.frame = oldLib and oldLib.frame or CreateFrame("Frame")
self.frame:UnregisterAllEvents()
self.frame:RegisterEvent("ADDON_LOADED")
self.frame:SetScript("OnEvent", function()
for major, instance in LibStub:IterateLibraries() do
manuallyFinalize(major, instance)
end
end)
for major, instance in LibStub:IterateLibraries() do
manuallyFinalize(major, instance)
end
 
-- Expose the library in the global environment
_G[ACELIBRARY_MAJOR] = self
 
if oldDeactivate then
oldDeactivate(oldLib)
end
end
 
if not previous then
previous = AceLibrary
end
if not previous.libs then
previous.libs = {}
end
AceLibrary.libs = previous.libs
if not previous.positions then
previous.positions = setmetatable({}, { __mode = "k" })
end
AceLibrary.positions = previous.positions
AceLibrary:Register(AceLibrary, ACELIBRARY_MAJOR, ACELIBRARY_MINOR, activate, nil)
trunk/StunWatch/Libs/AceLibrary/AceLibrary.toc New file
0,0 → 1,10
## Interface: 20400
 
## Title: Lib: AceLibrary
## Notes: AddOn development framework
## Author: Ace Development Team
## X-Website: http://www.wowace.com
## X-Category: Library
## X-License: LGPL v2.1 + MIT for AceOO-2.0
 
AceLibrary.lua
trunk/StunWatch/Libs/Waterfall-1.0/Waterfall-1.0.toc New file
0,0 → 1,14
## Interface: 20400
## LoadOnDemand: 1
## Title: Lib: Waterfall-1.0
## Notes: GUI Configuration Library
## Author: Nargiddley
## eMail: nargiddley@gmail.com
## X-Category: Library
## OptionalDeps: Ace2
## X-AceLibrary-Waterfall-1.0: true
## X-License: LGPL v2.1
 
embeds.xml
 
Waterfall-1.0\Waterfall-1.0.lua
trunk/StunWatch/Libs/Waterfall-1.0/ziplist.wau New file
0,0 → 1,4
Waterfall-1.0/Changelog-Waterfall-1.0-r76021.txt
Waterfall-1.0/embeds.xml
Waterfall-1.0/Waterfall-1.0.toc
Waterfall-1.0/Waterfall-1.0/Waterfall-1.0.lua
trunk/StunWatch/Libs/Waterfall-1.0/source.wau New file
0,0 → 1,4
<?xml version="1.0" encoding="utf-8"?><item xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><interface xmlns="http://www.wowace.com/xmlns/wowaddon/">20400</interface><author>Nargiddley</author><category>Library</category><description>GUI Configuration Library</description><enclosure length="24370" type="application/zip" url="http://files.wowace.com/Waterfall-1.0/no-ext/Waterfall-1.0-r76021.zip" /><guid>http://files.wowace.com/Waterfall-1.0/no-ext/Waterfall-1.0-r76021.zip</guid><pubDate>Wed, 04 Jun 2008 20:14:26 +0000</pubDate><stable xmlns="http://www.wowace.com/xmlns/wowaddon/">false</stable><title>Waterfall-1.0</title><version xmlns="http://www.wowace.com/xmlns/wowaddon/">76021</version></item>
\ No newline at end of file
trunk/StunWatch/Libs/Waterfall-1.0/Waterfall-1.0/Waterfall-1.0.lua New file
0,0 → 1,4647
--[[
Name: Waterfall-1.0
Revision: $Revision: 76021 $
Author(s): Nargiddley (nargiddley@gmail.com)
Inspired By: Dewdrop by ckknight
Website: http://www.wowace.com/wiki/Waterfall-1.0
Documentation: http://www.wowace.com/wiki/Waterfall-1.0
SVN: http://svn.wowace.com/wowace/trunk/Waterfall-1.0
Description: Gui Configuration Library
License: LGPL 2.1
Dependencies: AceOO-2.0
]]
 
local MAJOR_VERSION = "Waterfall-1.0"
local MINOR_VERSION = "$Revision: 76021 $"
 
local CONTROL_LIMIT = 250
local _
 
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("AceOO-2.0")
 
local Waterfall = {}
 
local currentframe
 
local OPTIONS = "Options"
local ARROW = " -> "
local ARE_YOU_SURE = "Are you sure you want to %s?"
 
if GetLocale() == "zhCN" then
OPTIONS = "选项"
ARROW = " -> "
ARE_YOU_SURE = "你确定要 %s么?"
elseif GetLocale() == "zhTW" then
OPTIONS = "選項"
ARROW = " > "
ARE_YOU_SURE = "你確定要「%s」嗎?"
elseif GetLocale() == "koKR" then
OPTIONS = "설정"
ARE_YOU_SURE = "정말 당신은 `%s'|1을;를; 하시겠습니까?"
end
 
local DEFAULT_CONTROL_WIDTH = 180
--[[
Passing an AceOO class will get an object of that class from the pool
You dont need to specify the class when releasing it
Passing a string will get a table from a named pool
the string must be passed when releasing tables like this
--]]
local getObj, releaseObj
do
local objPools = {}
function getObj(class,...)
if not objPools[class] then
objPools[class] = {}
end
local newObj = tremove(objPools[class])
if not newObj then
if type(class.new) == "function" then
newObj = class:new(...)
else
newObj = {}
end
end
return newObj
end
function releaseObj(obj,class)
if not class then
class = obj.class
end
if not class then error("You must supply a class to release non AceOO objects") end
if not objPools[class] then
objPools[class] = {}
end
if type(obj.CleanUp) == "function" then
obj:CleanUp()
end
tinsert(objPools[class],obj)
end
end
 
--borrowed from Dewdrop-2.0
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 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 function confirmPopup(message, func, ...)
if not StaticPopupDialogs["WATERFALL01_CONFIRM_DIALOG"] then
StaticPopupDialogs["WATERFALL01_CONFIRM_DIALOG"] = {}
end
local t = StaticPopupDialogs["WATERFALL01_CONFIRM_DIALOG"]
for k in pairs(t) do
t[k] = nil
end
t.text = message
t.button1 = ACCEPT
t.button2 = CANCEL
t.OnAccept = function()
func(unpack(t))
end
for i = 1, select('#', ...) do
t[i] = select(i, ...) or false
end
t.timeout = 0
t.whileDead = 1
t.hideOnEscape = 1
 
StaticPopup_Show("WATERFALL01_CONFIRM_DIALOG")
end
 
local function showGameTooltip(this)
if this.tooltipTitle or this.tooltipText then
GameTooltip_SetDefaultAnchor(GameTooltip, this.frame or this)
if this.tooltipTitle then
GameTooltip:SetText(this.tooltipTitle, 1, 1, 1, 1)
if this.tooltipText then
GameTooltip:AddLine(this.tooltipText, NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b, 1)
end
else
GameTooltip:SetText(this.tooltipText, 1, 1, 1, 1)
end
GameTooltip:Show()
end
end
--end of borrowing
 
local function parseList(list)
local token, rest = list:match("([^%s]+)%s+(.+)")
if not rest then
return (tonumber(list) or list)
else
return (tonumber(token) or token), parseList(rest)
end
end
 
local function compareOptions(a,b)
if not a then
return true
end
if not b then
return false
end
local A, B = a.order or 100, b.order or 100
if A == B then
local NameA = a.guiName or a.name or ""
local NameB = b.guiName or b.name or ""
return NameA:upper() < NameB:upper()
end
if A < 0 then
if B > 0 then
return false
end
else
if B < 0 then
return true
end
end
return A < B
end
 
function Waterfall:Register(id,...)
local settings = new(...)
 
if settings.aceOptions then
assert(type(settings.aceOptions) == "table","\"aceOptions\" must be a table")
else
assert(type(settings.tree) == "table","\"tree\" must be a table")
assert(type(settings.children) == "function","\"children\" must be a function")
end
if not settings.title then
settings.title = id
end
 
if settings.treeType == "SECTIONS" then
settings.hideTreeRoot = true
settings.treeLevels = 3
end
 
if self.registry[id] then
self:UnRegister(id)
end
self.registry[id] = settings
end
 
function Waterfall:UnRegister(id)
self.registry[id] = nil
end
 
function Waterfall:IsRegistered(id)
return not not self.registry[id]
end
 
local function releaseTree(tree)
for i, v in ipairs(tree) do
tree[i] = nil
releaseTree(v)
releaseObj(v,"table")
end
for k, v in pairs(tree) do
tree[k] = nil
end
end
 
local function checkNegatable(attr,handler,param)
local ret
if type(attr) == "function" then
ret = attr(param)
elseif type(attr) == "string" and handler then
local neg = attr:match("^~(.+)$")
if neg then
attr = neg
end
ret = handler[attr](handler,param)
if neg then
ret = not ret
end
else
ret = attr
end
return ret
end
 
local function buildAceOptionsTree(tree, status, options, numLevels, path, rootHandler)
local settings = Waterfall.registry[currentframe.id]
local handler = options.handler or rootHandler
local args = options.args
assert(type(args) == "table",("args must be a table: Current Path:%s"):format(path or "."))
local root
 
if path then
tree.id = path
tree.text = options.guiName or options.name or (options.handler and options.handler.name) or path
tree.name = tree.text
tree.tooltipTitle = tree.text
tree.tooltipText = options.desc
tree.order = options.order
tree.disabled = checkNegatable(options.disabled, handler, options.passValue)
tree.handler = handler
tree.icon = options.icon
tree.hidden = checkNegatable(options.wfHidden or options.guiHidden or options.hidden,handler, options.passValue)
local hasOptions
for k, item in pairs(args) do
if item.type ~= "group" and item.type ~= "heading" then
if not checkNegatable(item.wfHidden or item.guiHidden or item.hidden,handler, item.passValue) then
hasOptions = true
end
end
end
tree.hasOptions = hasOptions
else
releaseTree(tree)
local hasOptions
for k, item in pairs(args) do
if item.type ~= "group" and item.type ~= "heading" then
hasOptions = true
end
end
if hasOptions then
root = getObj("table")
root.text = options.guiName or options.name or (options.handler and options.handler.name) or path
root.name = tree.text
root.tooltipTitle = tree.text
root.tooltipText = options.desc
root.order = options.order
root.disabled = checkNegatable(options.disabled, handler, options.passValue)
root.handler = handler
root.icon = options.icon
root.hidden = checkNegatable(options.wfHidden or options.guiHidden or options.hidden,handler, options.passValue)
root.id = "."
root.hasOptions = true
if status['.'] == nil then
status['.'] = true
end
tinsert(tree,root)
if not settings.hideTreeRoot then
tree = root
end
end
settings.treeRootShown = hasOptions and not settings.hideTreeRoot
end
 
if not numLevels or numLevels > 1 then
for k, item in pairs(args) do
if item.type == "group" then
local child = getObj("table")
local nextPath
if path then
nextPath = path.."."..k
else
nextPath = k
end
if numLevels then
buildAceOptionsTree(child, status, item,numLevels - 1,nextPath, handler)
else
buildAceOptionsTree(child, status, item,nil,nextPath, handler)
end
tinsert(tree,child)
end
end
end
sort(tree,compareOptions)
end
 
local function checkAceOptionsPath(path, options, frame)
local t = options
local validpath
for k in path:gmatch("[^\.]+") do
if tonumber(k) and t.args[tonumber(k)] then
k = tonumber(k)
end
if t.args and t.args[k] then
t = t.args[k]
if validpath then
validpath = validpath..'.'..k
else
validpath = k
end
else
if frame then
frame:SetSelected(validpath, true)
end
break
end
end
return validpath or '.'
end
 
local function feedFromOptionsTree(paneid, options)
if not paneid then return end
local settings = Waterfall.registry[currentframe.id]
paneid = checkAceOptionsPath(paneid, options, settings.frame)
local curlevel = 1
local levels = 1
for x in paneid:gmatch("[^%.]+") do
curlevel = curlevel + 1
end
if settings.treeLevels == curlevel then
levels = nil
end
if paneid == "." then
Waterfall:FeedAceOptionsTable(options, nil, levels)
return options.name or (options.handler and options.handler.name) or OPTIONS
else
local crumbs
if settings.treeRootShown then
crumbs = options.name or (options.handler and options.handler.name) or OPTIONS
end
local t = options
Waterfall:FeedAceOptionsTable(options,paneid,levels)
for k in paneid:gmatch("[^\.]+") do
if tonumber(k) and t.args[tonumber(k)] then
k = tonumber(k)
end
if t.args and t.args[k] then
t = t.args[k]
if crumbs then
crumbs = crumbs..ARROW..(t.guiName or t.name or "")
else
crumbs = (t.guiName or t.name or "")
end
else
Waterfall:error("Invalid path \"%s\" for Aceoptions table",paneid)
end
end
return crumbs
end
end
 
local old_CloseSpecialWindows
 
function Waterfall:Open(id, pane)
if not old_CloseSpecialWindows then
old_CloseSpecialWindows = CloseSpecialWindows
CloseSpecialWindows = function()
local found = old_CloseSpecialWindows()
return self:CloseAll() or found
end
end
local info = self.registry[id]
if not info then
error("You cannot open a waterfall without registering it first")
end
--already open
if info.frame then
if pane then
info.frame:SetSelected(pane)
end
return
end
local frame = getObj(WaterfallFrame)
frame:SetID(id)
frame.lib = self
info.frame = frame
if info.title then
frame:SetTitle(info.title)
end
local r,g,b = info.colorR or 1, info.colorG or 0.6, info.colorB or 0
frame:SetColor(r,g,b)
 
if info.aceOptions then
 
if not info.tree then
info.tree = {}
end
if not info.treestatus then
info.treestatus = {}
end
currentframe = frame
frame:SetTree(info.tree, info.treestatus, buildAceOptionsTree, info.aceOptions, info.treeLevels)
currentframe = nil
frame:SetChildren(feedFromOptionsTree, info.aceOptions)
if pane then
frame.treeview:Collapse()
end
frame:SetSelected(pane or info.defaultPane or ".")
frame.treeview:SetType(info.treeType)
else
if not info.treestatus then
info.treestatus = {}
end
frame:SetTree(info.tree, info.treestatus)
frame:SetChildren(info.children)
if pane or info.defaultPane then
frame:SetSelected(pane or info.defaultPane)
end
frame.treeview:SetType(info.treeType)
end
 
if info.height then
frame:SetHeight(info.height)
end
if info.width then
frame:SetWidth(info.width)
end
frame:Refresh()
frame:Show()
 
end
 
function Waterfall:IsOpen(id)
return not not self.registry[id].frame
end
 
function Waterfall:Refresh(id)
local info = self.registry[id]
if not info then
error("You can't refresh a waterfall without registering it first")
end
if info.frame then
info.frame:Refresh()
end
end
 
function Waterfall:SetSize(id, width, height)
local info = self.registry[id]
if not info then
error("You refresh open a waterfall without registering it first")
end
if info.frame then
info.frame:SetWidth(width)
info.frame:SetHeight(height)
end
end
 
local function setCommonAttributes(control,info)
control.width = info.width
control.height = info.height
control.noNewLine = info.noNewLine
control.causesRefresh = info.causesRefresh
control.fullRefresh = info.fullRefresh
control.treeRefresh = info.treeRefresh
control.tooltipTitle = info.tooltipTitle
control.tooltipText = info.tooltipText
control:SetText(info.text or "")
end
 
function Waterfall:AddControl(...)
currentframe.controlcount = currentframe.controlcount + 1
if not currentframe then
error("AddControl must be called from within a children function")
end
local control
local info = self.registry[currentframe.id]
local limit = info.controlLimit or CONTROL_LIMIT
if currentframe.controlcount > limit then
if currentframe.controlcount == limit + 1 then
control = getObj(WaterfallLabel)
control.r = 1
control.g = 0
control.b = 0
control:SetText(("More than %s Controls in current pane....."):format(limit))
control:Refresh()
currentframe:AddControl(control)
end
return
end
local info = tmp2(...)
 
if info.type == "label" then
control = getObj(WaterfallLabel)
setCommonAttributes(control,info)
control.justifyH = info.justifyH
control.r = info.r
control.g = info.g
control.b = info.b
control:Refresh()
elseif info.type == "dragLink" then
control = getObj(WaterfallDragLink)
setCommonAttributes(control,info)
control:SetFunc("set",info.setFunc,getArgs(info,"setArg",1))
control:SetFunc("get",info.getFunc,getArgs(info,"getArg",1))
if type(info.disabled) == "function" then
control:SetFunc("disabled",info.disabled,getArgs(info,"disabledArg",1))
control.inverseDisabled = info.inverseDisabled
else
control.disabled = info.disabled
end
 
control:Refresh()
elseif info.type == "linklabel" then
control = getObj(WaterfallLinklabel)
setCommonAttributes(control,info)
control.justifyH = info.justifyH
control.r = info.r
control.g = info.g
control.b = info.b
 
control:SetFunc("link",info.linkFunc,getArgs(info,"linkArg",1))
if type(info.disabled) == "function" then
control:SetFunc("disabled",info.disabled,getArgs(info,"disabledArg",1))
control.inverseDisabled = info.inverseDisabled
else
control.disabled = info.disabled
end
control.confirm = info.confirm
control:Refresh()
 
elseif info.type == "button" then
control = getObj(WaterfallButton)
setCommonAttributes(control,info)
control:SetFunc("exec",info.execFunc,getArgs(info,"execArg",1))
if type(info.disabled) == "function" then
control:SetFunc("disabled",info.disabled,getArgs(info,"disabledArg",1))
control.inverseDisabled = info.inverseDisabled
else
control.disabled = info.disabled
end
control.confirm = info.confirm
control.link = info.link
 
control:Refresh()
 
elseif info.type == "heading" then
control = getObj(WaterfallHeading)
control.justifyH = info.justifyH
setCommonAttributes(control,info)
control:Refresh()
elseif info.type == "textbox" then
control = getObj(WaterfallTextBox)
setCommonAttributes(control,info)
control:SetFunc("set",info.setFunc,getArgs(info,"setArg",1))
control:SetFunc("get",info.getFunc,getArgs(info,"getArg",1))
control:SetFunc("changed",info.changedFunc,getArgs(info,"changedArg",1))
if type(info.disabled) == "function" then
control:SetFunc("disabled",info.disabled,getArgs(info,"disabledArg",1))
control.inverseDisabled = info.inverseDisabled
else
control.disabled = info.disabled
end
control.toNumber = info.toNumber
control.parseList = info.parseList
control.setOnTextChanged = info.setOnTextChanged
control:SetFunc("validate",info.validateFunc,getArgs(info,"validateArg",1))
control.validateModifies = info.validateModifies
control:Refresh()
 
elseif info.type == "dropdown" then
control = getObj(WaterfallDropdown)
setCommonAttributes(control,info)
control:SetFunc("set",info.setFunc,getArgs(info,"setArg",1))
control:SetFunc("get",info.getFunc,getArgs(info,"getArg",1))
if type(info.disabled) == "function" then
control:SetFunc("disabled",info.disabled,getArgs(info,"disabledArg",1))
control.inverseDisabled = info.inverseDisabled
else
control.disabled = info.disabled
end
control.parseList = info.parseList
control.list = info.list
control.columns = info.columns
control.strict = info.strict
 
control:Refresh()
 
elseif info.type == "checkbox" then
control = getObj(WaterfallCheckBox)
setCommonAttributes(control,info)
control:SetChecked(info.checked)
control.isRadio = info.isRadio
control:SetFunc("set",info.setFunc,getArgs(info,"setArg",1))
control:SetFunc("get",info.getFunc,getArgs(info,"getArg",1))
if type(info.disabled) == "function" then
control:SetFunc("disabled",info.disabled,getArgs(info,"disabledArg",1))
control.inverseDisabled = info.inverseDisabled
else
control.disabled = info.disabled
end
 
control:Refresh()
elseif info.type == "slider" then
control = getObj(WaterfallSlider)
setCommonAttributes(control,info)
control:SetFunc("set",info.setFunc,getArgs(info,"setArg",1))
control:SetFunc("get",info.getFunc,getArgs(info,"getArg",1))
control.min = info.min
control.max = info.max
control.step = info.step
control.bigStep = info.bigStep or info.step
control.finalSetOnly = info.finalSetOnly
if type(info.disabled) == "function" then
control:SetFunc("disabled",info.disabled,getArgs(info,"disabledArg",1))
control.inverseDisabled = info.inverseDisabled
else
control.disabled = info.disabled
end
control:Refresh()
elseif info.type == "color" then
control = getObj(WaterfallColorSwatch)
setCommonAttributes(control,info)
control:SetFunc("set",info.setFunc,getArgs(info,"setArg",1))
control:SetFunc("get",info.getFunc,getArgs(info,"getArg",1))
if type(info.disabled) == "function" then
control:SetFunc("disabled",info.disabled,getArgs(info,"disabledArg",1))
control.inverseDisabled = info.inverseDisabled
else
control.disabled = info.disabled
end
control.hasOpacity = info.hasOpacity
control.r = info.r
control.g = info.g
control.b = info.b
control.opacity = info.opacity
control.hasOpacity = info.hasOpacity
control:Refresh()
elseif info.type == "keybind" then
control = getObj(WaterfallKeybinding)
control:SetText("Binding")
setCommonAttributes(control,info)
control:SetFunc("set",info.setFunc,getArgs(info,"setArg",1))
control:SetFunc("get",info.getFunc,getArgs(info,"getArg",1))
if type(info.disabled) == "function" then
control:SetFunc("disabled",info.disabled,getArgs(info,"disabledArg",1))
control.inverseDisabled = info.inverseDisabled
else
control.disabled = info.disabled
end
control.keybindingOnly = info.keybindingOnly
control.keybindingExcept = info.keybindingExcept
control:Refresh()
else
error("Invalid or missing type")
end
currentframe:AddControl(control)
end
 
 
local function addDisabled(t,disabled,handler,param)
if disabled then
if type(disabled) == "function" or type(disabled) == "boolean" then
tinsert(t,"disabled")
tinsert(t,disabled)
if param then
tinsert(t,"disabledArg1")
tinsert(t,param)
end
elseif type(disabled) == "string" then
local neg = disabled:match("^~(.+)$")
if neg then
disabled = neg
end
if handler and handler[disabled] then
tinsert(t,"disabled")
tinsert(t,handler[disabled])
tinsert(t,"disabledArg1")
tinsert(t,handler)
if param then
tinsert(t,"disabledArg2")
tinsert(t,param)
end
end
if neg then
tinsert(t,"inverseDisabled")
tinsert(t,true)
end
end
end
end
 
local function appendArgs(t,name,start,...)
local i = start
local count = 1
local arg = select(count,...)
while arg do
tinsert(t,name.."Arg"..i)
tinsert(t,arg)
i = i + 1
count = count + 1
arg = select(count,...)
end
end
 
local function addFunction(t,name,func,handler,...)
if type(func) == "function" then
tinsert(t,name.."Func")
tinsert(t,func)
appendArgs(t,name,1,...)
elseif type(func) == "string" and handler and handler[func] then
tinsert(t,name.."Func")
tinsert(t,handler[func])
tinsert(t,name.."Arg1")
tinsert(t,handler)
appendArgs(t,name,2,...)
end
end
 
local function validateRange(min,max,step,value)
value = tonumber(value)
if step > 0 then
value = math.floor((value - min) / step + 0.5) * step + min
end
if value >= min and value <= max then
return value
else
return nil
end
end
 
--[[
Feeds an ace options table into the right pane, must be called within a "children" function
root is the root of your options table, this is important if you use any methods for resolving the handler
path is the path into the table seperated by "." e.g. "Items.Compress" to feed the table given by root.args.Items.args.Compress
maxLevels is how many sub groups to feed as well, 1 is only the current level 2 will feed sub groups etc. nil assumes no limit
]]
function Waterfall:FeedAceOptionsTable(root,path,maxLevels)
local feedtmp = getObj("table")
local feedkeys = getObj("table")
local tmpargs = getObj("table")
 
local t
local handler = root.handler
local groupDisabled = checkNegatable(root.disabled,root.handler,root.passValue)
local passSet
local passGet
if root.pass then
passSet = root.set
passGet = root.get
else
passSet = nil
passGet = nil
end
if path then
t = root
for k in path:gmatch("[^\.]+") do
if tonumber(k) and t.args[tonumber(k)] then
k = tonumber(k)
end
if t.args and t.args[k] then
t = t.args[k]
handler = t.handler or handler
groupDisabled = checkNegatable(t.disabled,handler,t.passValue) or groupDisabled
if t.pass then
passSet = t.set
passGet = t.get
else
passSet = nil
passGet = nil
end
else
self:error("Invalid path \"%s\" for Aceoptions table",path)
end
end
else
t = root
end
if t.type ~= "group" then
self:error("You must feed an ace options group")
end
for k, v in pairs(t.args) do
tinsert(feedtmp, v)
feedkeys[v] = k
end
 
table.sort(feedtmp, compareOptions)
local lastheader
for i, v in ipairs(feedtmp) do
--only add headers if they arent immediately followed by a group
if lastheader then
if v.type ~= "group" then
--self:AddControl("type","label","text","","width",80)
--self:AddControl("type","heading","text",lastheader,"width",DEFAULT_CONTROL_WIDTH,"justifyH","CENTER","noNewLine",true)
self:AddControl("type","heading","text",lastheader,"width",DEFAULT_CONTROL_WIDTH,"justifyH","CENTER")
end
lastheader = nil
end
local hidden
if v.guiHidden ~= nil then
hidden = v.guiHidden
elseif v.wfHidden ~= nil then
hidden = v.wfHidden
else
hidden = v.hidden
end
hidden = checkNegatable(hidden,v.handler or handler,v.passValue)
 
local name = v.guiName or v.name
if name and ( checkNegatable(v.disabled,v.handler or handler,v.passValue) or groupDisabled ) then
name = "|cFF808080"..name
end
if not hidden then
if v.type == "toggle" then
--self:AddControl("type","label","text","","width",80)
tinsert(tmpargs,"type")
tinsert(tmpargs,"checkbox")
--tinsert(tmpargs,"noNewLine")
--tinsert(tmpargs,true)
tinsert(tmpargs,"text")
tinsert(tmpargs,name)
tinsert(tmpargs,"tooltipTitle")
tinsert(tmpargs,name)
tinsert(tmpargs,"tooltipText")
tinsert(tmpargs,v.desc or false)
tinsert(tmpargs,"isRadio")
tinsert(tmpargs,not not v.isRadio)
if passGet and v.get == nil then
addFunction(tmpargs,"get",passGet,v.handler or handler,v.passValue or feedkeys[v])
else
addFunction(tmpargs,"get",v.get,v.handler or handler,v.passValue)
end
if passSet and v.set == nil then
addFunction(tmpargs,"set",passSet,v.handler or handler,v.passValue or feedkeys[v])
else
addFunction(tmpargs,"set",v.set,v.handler or handler,v.passValue)
end
 
addDisabled(tmpargs,groupDisabled or v.disabled,v.handler or handler,v.passValue)
tinsert(tmpargs,"fullRefresh")
tinsert(tmpargs,true)
self:AddControl(unpack(tmpargs))
elseif v.type == "dragLink" then
tinsert(tmpargs,"type")
tinsert(tmpargs,"dragLink")
tinsert(tmpargs,"text")
tinsert(tmpargs,name)
tinsert(tmpargs,"tooltipText")
tinsert(tmpargs,v.desc or false)
tinsert(tmpargs,"tooltipTitle")
local tooltipTitle = "" .. tostring(v.itemId)
tinsert(tmpargs,tooltipTitle)
tinsert(tmpargs,"linkInfo")
tinsert(tmpargs,v.linkInfo or {})
tinsert(tmpargs,"icon")
tinsert(tmpargs,v.icon or "")
tinsert(tmpargs,"iconWidth")
tinsert(tmpargs,v.iconWidth or WaterfallDragLink.defaultIconSize)
tinsert(tmpargs,"iconHeight")
tinsert(tmpargs,v.iconHeight or WaterfallDragLink.defaultIconSize)
if passGet and v.get == nil then
addFunction(tmpargs,"get",passGet,v.handler or handler,v.passValue or feedkeys[v])
else
addFunction(tmpargs,"get",v.get,v.handler or handler,v.passValue)
end
if passSet and v.set == nil then
addFunction(tmpargs,"set",passSet,v.handler or handler,v.passValue or feedkeys[v])
else
addFunction(tmpargs,"set",v.set,v.handler,v.passValue)
end
 
addDisabled(tmpargs,groupDisabled or v.disabled,v.handler or handler,v.passValue)
 
self:AddControl(unpack(tmpargs))
elseif v.type == "group" then
if not maxLevels or maxLevels > 1 then
--self:AddControl("type","label","text","","width",80)
if checkNegatable(v.disabled,v.handler or handler,v.passValue) or groupDisabled then
name = "|cFF808080"..name
end
--self:AddControl("type","heading","text",name,"width",DEFAULT_CONTROL_WIDTH,"justifyH","CENTER","noNewLine",true)
self:AddControl("type","heading","text",name,"width",DEFAULT_CONTROL_WIDTH,"justifyH","CENTER")
local nextPath
local nextMaxLevels
if path then
nextPath = path.."."..feedkeys[v]
else
nextPath = feedkeys[v]
end
if maxLevels then
nextMaxLevels = maxLevels - 1
end
self:FeedAceOptionsTable(root,nextPath,nextMaxLevels)
self:AddControl("type","heading")
end
elseif v.type == "text" then
if type(v.validate) == "table" then
if v.multiToggle then
self:AddControl("type","heading","text",name)
for key, value in pairs(v.validate) do
if type(key) == "number" then
key = value
end
self:AddControl("type","label","text","","width",12)
tinsert(tmpargs,"type")
tinsert(tmpargs,"checkbox")
tinsert(tmpargs,"text")
tinsert(tmpargs,value)
tinsert(tmpargs,"noNewLine")
tinsert(tmpargs,true)
tinsert(tmpargs,"tooltipTitle")
tinsert(tmpargs,name)
tinsert(tmpargs,"tooltipText")
tinsert(tmpargs,value)
if v.passValue then
if passGet and v.get == nil then
addFunction(tmpargs,"get",passGet,v.handler or handler,v.passValue or feedkeys[v],key)
else
addFunction(tmpargs,"get",v.get,v.handler or handler,v.passValue,key)
end
if passSet and v.set == nil then
addFunction(tmpargs,"set",passSet,v.handler or handler,v.passValue or feedkeys[v],key)
else
addFunction(tmpargs,"set",v.set,v.handler or handler,v.passValue,key)
end
else
if passGet and v.get == nil then
addFunction(tmpargs,"get",passGet,v.handler or handler,feedkeys[v],key)
else
addFunction(tmpargs,"get",v.get,v.handler or handler,key)
end
if passSet and v.set == nil then
addFunction(tmpargs,"set",passSet,v.handler or handler,feedkeys[v],key)
else
addFunction(tmpargs,"set",v.set,v.handler or handler,key)
end
end
 
addDisabled(tmpargs,groupDisabled or v.disabled,v.handler or handler,v.passValue,key)
tinsert(tmpargs,"fullRefresh")
tinsert(tmpargs,true)
self:AddControl(unpack(tmpargs))
for i = 0,#tmpargs do
tmpargs[i] = nil
end
end
else
--self:AddControl("type","label","text",name,"width",80,"justifyH","RIGHT")
self:AddControl("type","label","text",name)
tinsert(tmpargs,"type")
tinsert(tmpargs,"dropdown")
tinsert(tmpargs,"strict")
tinsert(tmpargs,true)
--tinsert(tmpargs,"noNewLine")
--tinsert(tmpargs,true)
tinsert(tmpargs,"tooltipTitle")
tinsert(tmpargs,name)
tinsert(tmpargs,"tooltipText")
tinsert(tmpargs,v.desc or false)
if passGet and v.get == nil then
addFunction(tmpargs,"get",passGet,v.handler or handler,v.passValue or feedkeys[v])
else
addFunction(tmpargs,"get",v.get,v.handler or handler,v.passValue)
end
if passSet and v.set == nil then
addFunction(tmpargs,"set",passSet,v.handler or handler,v.passValue or feedkeys[v])
else
addFunction(tmpargs,"set",v.set,v.handler or handler,v.passValue)
end
 
addDisabled(tmpargs,groupDisabled or v.disabled,v.handler or handler,v.passValue)
 
tinsert(tmpargs,"list")
tinsert(tmpargs,v.validate)
tinsert(tmpargs,"columns")
tinsert(tmpargs,v.columns)
tinsert(tmpargs,"parseList")
tinsert(tmpargs, not not v.input)
tinsert(tmpargs,"fullRefresh")
tinsert(tmpargs,true)
self:AddControl(unpack(tmpargs))
end
elseif v.validate == "keybinding" then
--self:AddControl("type","label","text",name,"width",80,"justifyH","RIGHT")
self:AddControl("type","label","text",name)
tinsert(tmpargs,"type")
tinsert(tmpargs,"keybind")
--tinsert(tmpargs,"noNewLine")
--tinsert(tmpargs,true)
if passGet and v.get == nil then
addFunction(tmpargs,"get",passGet,v.handler or handler,v.passValue or feedkeys[v])
else
addFunction(tmpargs,"get",v.get,v.handler or handler,v.passValue)
end
if passSet and v.set == nil then
addFunction(tmpargs,"set",passSet,v.handler or handler,v.passValue or feedkeys[v])
else
addFunction(tmpargs,"set",v.set,v.handler or handler,v.passValue)
end
tinsert(tmpargs,"keybindingOnly")
tinsert(tmpargs,v.keybindingOnly or false)
tinsert(tmpargs,"keybindingExcept")
tinsert(tmpargs,v.keybindingExcept or false)
tinsert(tmpargs,"tooltipTitle")
tinsert(tmpargs,name)
tinsert(tmpargs,"tooltipText")
tinsert(tmpargs,v.desc or false)
tinsert(tmpargs,"fullRefresh")
tinsert(tmpargs,true)
 
self:AddControl(unpack(tmpargs))
else
--self:AddControl("type","label","text",name,"width",80,"justifyH","RIGHT")
self:AddControl("type","label","text",name)
tinsert(tmpargs,"type")
tinsert(tmpargs,"textbox")
--tinsert(tmpargs,"noNewLine")
--tinsert(tmpargs,true)
tinsert(tmpargs,"tooltipTitle")
tinsert(tmpargs,name)
tinsert(tmpargs,"tooltipText")
tinsert(tmpargs,v.desc or false)
if passGet and v.get == nil then
addFunction(tmpargs,"get",passGet,v.handler or handler,v.passValue or feedkeys[v])
else
addFunction(tmpargs,"get",v.get,v.handler or handler,v.passValue)
end
if passSet and v.set == nil then
addFunction(tmpargs,"set",passSet,v.handler or handler,v.passValue or feedkeys[v])
else
addFunction(tmpargs,"set",v.set,v.handler or handler,v.passValue)
end
 
addDisabled(tmpargs,groupDisabled or v.disabled,v.handler or handler,v.passValue)
tinsert(tmpargs,"parseList")
tinsert(tmpargs, not not v.input)
tinsert(tmpargs,"fullRefresh")
tinsert(tmpargs,true)
 
addFunction(tmpargs,"validate",v.validate,v.handler or handler)
 
self:AddControl(unpack(tmpargs))
end
elseif v.type == "execute" then
--self:AddControl("type","label","text","","width",80)
tinsert(tmpargs,"type")
tinsert(tmpargs,"button")
--tinsert(tmpargs,"noNewLine")
--tinsert(tmpargs,true)
tinsert(tmpargs,"text")
tinsert(tmpargs,name)
tinsert(tmpargs,"confirm")
tinsert(tmpargs, v.confirm or false)
tinsert(tmpargs,"tooltipTitle")
tinsert(tmpargs,name)
tinsert(tmpargs,"tooltipText")
tinsert(tmpargs,v.desc or false)
tinsert(tmpargs, "link")
tinsert(tmpargs,v.link or false)
addFunction(tmpargs,"exec",v.func,v.handler or handler,v.passValue)
 
addDisabled(tmpargs,groupDisabled or v.disabled,v.handler or handler,v.passValue)
 
tinsert(tmpargs,"fullRefresh")
tinsert(tmpargs,true)
self:AddControl(unpack(tmpargs))
elseif v.type == "header" then
if name and ( checkNegatable(v.disabled,v.handler or handler,v.passValue) or groupDisabled ) then
lastheader = "|cFF808080"..name
else
lastheader = name
end
elseif v.type == "range" then
--self:AddControl("type","label","text",name,"width",80,"justifyH","RIGHT")
self:AddControl("type","label","text",name)
tinsert(tmpargs,"type")
tinsert(tmpargs,"slider")
--tinsert(tmpargs,"noNewLine")
--tinsert(tmpargs,true)
local min,max,step
min = v.min or 0
max = v.max or 1
step = v.step or 0
tinsert(tmpargs,"min")
tinsert(tmpargs,v.min or 0)
tinsert(tmpargs,"max")
tinsert(tmpargs,v.max or 1)
tinsert(tmpargs,"step")
tinsert(tmpargs,v.step or 0)
tinsert(tmpargs,"bigStep")
tinsert(tmpargs,v.bigStep or v.step or 0)
tinsert(tmpargs,"finalSetOnly")
tinsert(tmpargs,v.finalSetOnly or false)
--if no step is given or there are more than 20 steps possible
--local complex = step == 0 or ((max - min) / step) > 20
local complex = true
tinsert(tmpargs,"text")
tinsert(tmpargs,"")
tinsert(tmpargs,"tooltipTitle")
tinsert(tmpargs,name)
tinsert(tmpargs,"tooltipText")
tinsert(tmpargs,v.desc or false)
if passGet and v.get == nil then
addFunction(tmpargs,"get",passGet,v.handler or handler,v.passValue or feedkeys[v])
else
addFunction(tmpargs,"get",v.get,v.handler or handler,v.passValue)
end
if passSet and v.set == nil then
addFunction(tmpargs,"set",passSet,v.handler or handler,v.passValue or feedkeys[v])
else
addFunction(tmpargs,"set",v.set,v.handler or handler,v.passValue)
end
addDisabled(tmpargs,groupDisabled or v.disabled,v.handler or handler,v.passValue)
 
tinsert(tmpargs,"fullRefresh")
tinsert(tmpargs,true)
self:AddControl(unpack(tmpargs))
if complex then
tinsert(tmpargs,"type")
tinsert(tmpargs,"textbox")
tinsert(tmpargs,"noNewLine")
tinsert(tmpargs,true)
tinsert(tmpargs,"toNumber")
tinsert(tmpargs,true)
tinsert(tmpargs,"validateModifies")
tinsert(tmpargs,true)
tinsert(tmpargs,"width")
tinsert(tmpargs,60)
tinsert(tmpargs,"tooltipTitle")
tinsert(tmpargs,name)
tinsert(tmpargs,"tooltipText")
tinsert(tmpargs,v.desc or false)
if passGet and v.get == nil then
addFunction(tmpargs,"get",passGet,v.handler or handler,v.passValue or feedkeys[v])
else
addFunction(tmpargs,"get",v.get,v.handler or handler,v.passValue)
end
if passSet and v.set == nil then
addFunction(tmpargs,"set",passSet,v.handler or handler,v.passValue or feedkeys[v])
else
addFunction(tmpargs,"set",v.set,v.handler or handler,v.passValue)
end
 
addFunction(tmpargs,"validate",validateRange,v.handler or handler,min,max,step)
addDisabled(tmpargs,groupDisabled or v.disabled,v.handler or handler,v.passValue)
 
tinsert(tmpargs,"fullRefresh")
tinsert(tmpargs,true)
self:AddControl(unpack(tmpargs))
end
elseif v.type == "color" then
--self:AddControl("type","label","text","","width",80)
tinsert(tmpargs,"type")
tinsert(tmpargs,"color")
--tinsert(tmpargs,"noNewLine")
--tinsert(tmpargs,true)
tinsert(tmpargs,"text")
tinsert(tmpargs,name)
if passGet and v.get == nil then
addFunction(tmpargs,"get",passGet,v.handler or handler,v.passValue or feedkeys[v])
else
addFunction(tmpargs,"get",v.get,v.handler or handler,v.passValue)
end
if passSet and v.set == nil then
addFunction(tmpargs,"set",passSet,v.handler or handler,v.passValue or feedkeys[v])
else
addFunction(tmpargs,"set",v.set,v.handler or handler,v.passValue)
end
addDisabled(tmpargs,groupDisabled or v.disabled,v.handler or handler,v.passValue)
tinsert(tmpargs,"tooltipTitle")
tinsert(tmpargs,name)
tinsert(tmpargs,"tooltipText")
tinsert(tmpargs,v.desc or false)
tinsert(tmpargs,"hasOpacity")
tinsert(tmpargs, not not v.hasAlpha)
self:AddControl(unpack(tmpargs))
end
 
for i = 0,#tmpargs do
tmpargs[i] = nil
end
end
end
for i = 0,#feedtmp do
feedtmp[i] = nil
end
for k in pairs(feedkeys) do
feedkeys[k] = nil
end
 
releaseObj(feedtmp,"table")
releaseObj(feedkeys,"table")
releaseObj(tmpargs,"table")
end
 
function Waterfall:Close(id)
local info = self.registry[id]
if not info then
error("You cannot close a waterfall without registering it first")
end
local frame = info.frame
if frame then
local OnClose = info.OnClose
if type(OnClose) == "function" then
OnClose(id)
end
releaseObj(frame)
end
info.frame = nil
end
 
function Waterfall:CloseAll()
local closed
for id, v in pairs(self.registry) do
if v.frame then
self:Close(id)
closed = true
end
end
return closed
end
 
-----------------
-- Gui Classes --
-----------------
local FrameBackdrop = {
bgFile = "Interface\\Tooltips\\UI-Tooltip-Background",
edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
tile = true, tileSize = 16, edgeSize = 16,
insets = { left = 3, right = 3, top = 30, bottom = 3 }
}
 
local PaneBackdrop = {
bgFile = "Interface\\Tooltips\\UI-Tooltip-Background",
edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
tile = true, tileSize = 16, edgeSize = 16,
insets = { left = 3, right = 3, top = 23, bottom = 3 }
}
 
local ControlBackdrop = {
bgFile = "Interface\\Tooltips\\UI-Tooltip-Background",
edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
tile = true, tileSize = 16, edgeSize = 16,
insets = { left = 3, right = 3, top = 3, bottom = 3 }
}
 
 
 
-- virtual class to contain frame functions
local FramePassthrough = AceOO.Class()
FramePassthrough.virtual = true
 
function FramePassthrough.prototype:SetPoint(point,relativeTo,relativePoint,xOffset,yOffset)
self.frame:SetPoint(point,(relativeTo and relativeTo.frame) or relativeTo,relativePoint,xOffset,yOffset)
end
function FramePassthrough.prototype:SetAllPoints(frame)
self.frame:SetAllPoints(frame.frame or frame)
end
function FramePassthrough.prototype:SetWidth(...)
self.frame:SetWidth(...)
end
function FramePassthrough.prototype:SetHeight(...)
self.frame:SetHeight(...)
end
function FramePassthrough.prototype:SetMinResize(...)
self.frame:SetMinResize(...)
end
function FramePassthrough.prototype:SetMaxResize(...)
self.frame:SetMaxResize(...)
end
function FramePassthrough.prototype:ClearAllPoints()
self.frame:ClearAllPoints()
end
function FramePassthrough.prototype:Show()
self.frame:Show()
end
function FramePassthrough.prototype:Hide()
self.frame:Hide()
end
function FramePassthrough.prototype:GetHeight()
return self.frame:GetHeight()
end
function FramePassthrough.prototype:GetWidth()
return self.frame:GetWidth()
end
 
-- Base Class for Gui Controls
local WaterfallControl = AceOO.Class(FramePassthrough)
WaterfallControl.virtual = true
 
function WaterfallControl.prototype:init(frametype,inherits)
WaterfallControl.super.prototype.init(self)
local frame = CreateFrame(frametype or "Frame",nil,nil,inherits)
self.frame = frame
frame.obj = self
frame:SetFrameLevel(10)
frame:SetFrameStrata("DIALOG")
end
 
function WaterfallControl.prototype:SetParent(parent)
self.parent = parent
self.frame:SetParent(parent.scrollchild)
end
 
function WaterfallControl.prototype:Refresh()
 
end
 
--basic cleanup function, usually overridden
function WaterfallControl.prototype:CleanUp()
self.frame:ClearAllPoints()
self.frame:Hide()
self.width = nil
self.height = nil
self.noNewLine = nil
self.causesRefresh = nil
self.fullRefresh = nil
self.treeRefresh = nil
self.disabled = nil
self:SetFunc("disabled",nil)
end
 
function WaterfallControl.prototype:SetFunc(funcType,func,...)
self[funcType.."Func"] = func
local i = 1
while self[funcType.."Arg"..i] do
self[funcType.."Arg"..i] = nil
i = i + 1
end
i = 1
local arg = (select(i,...))
while (select(i,...)) ~= nil do
self[funcType.."Arg"..i] = arg
i = i + 1
arg = (select(i,...))
end
end
 
local function Control_OnEnter(this)
showGameTooltip(this.obj)
end
 
local function Control_OnLeave(this)
GameTooltip:Hide()
end
 
--the main waterfall frame
WaterfallFrame = AceOO.Class(FramePassthrough)
--the right side pane with gui components in it
WaterfallPane = AceOO.Class(FramePassthrough)
 
--gui components to be placed on the pane
WaterfallLabel = AceOO.Class(WaterfallControl)
WaterfallLinklabel = AceOO.Class(WaterfallControl)
WaterfallCheckBox = AceOO.Class(WaterfallControl)
WaterfallDragLink = AceOO.Class(WaterfallControl)
WaterfallButton = AceOO.Class(WaterfallControl)
WaterfallHeading = AceOO.Class(WaterfallControl)
WaterfallTextBox = AceOO.Class(WaterfallControl)
WaterfallSlider = AceOO.Class(WaterfallControl)
WaterfallColorSwatch = AceOO.Class(WaterfallControl)
WaterfallDropdown = AceOO.Class(WaterfallControl)
WaterfallKeybinding = AceOO.Class(WaterfallControl)
 
--the left side treeview pane
WaterfallTreeView = AceOO.Class(FramePassthrough)
WaterfallTreeLine = AceOO.Class(FramePassthrough)
WaterfallTreeSection = AceOO.Class(FramePassthrough)
 
---------------------------
-- Waterfall Frame Class --
---------------------------
 
local function frameOnClose(this)
this.obj.lib:Close(this.obj.id)
end
 
local function closeOnClick(this)
this.obj:Hide()
end
 
local function frameOnMouseDown(this)
this:GetParent():StartMoving()
end
 
local function frameOnMouseUp(this)
this:GetParent():StopMovingOrSizing()
end
 
local function sizerseOnMouseDown(this)
this:GetParent():StartSizing("BOTTOMRIGHT")
end
 
local function sizersOnMouseDown(this)
this:GetParent():StartSizing("BOTTOM")
end
 
local function sizereOnMouseDown(this)
this:GetParent():StartSizing("RIGHT")
end
 
local function sizerOnMouseUp(this)
this:GetParent():StopMovingOrSizing()
end
 
 
function WaterfallFrame.prototype:init()
WaterfallFrame.super.prototype.init(self)
local frame = CreateFrame("Frame",nil,UIParent)
self.frame = frame
frame.obj = self
frame:SetWidth(550)
frame:SetHeight(500)
frame:SetPoint("CENTER",UIParent,"CENTER",0,0)
frame:EnableMouse()
frame:SetMovable(true)
frame:SetResizable(true)
frame:SetMinResize(500,300)
frame:SetFrameLevel(1)
frame:SetFrameStrata("DIALOG")
 
frame:SetBackdrop(FrameBackdrop)
frame:SetBackdropColor(0,0,0)
frame:SetScript("OnHide",frameOnClose)
 
local titlebar = frame:CreateTexture(nil,"BACKGROUND")
self.titlebar = titlebar
local titlebar2 = frame:CreateTexture(nil,"BACKGROUND")
self.titlebar2 = titlebar2
local r,g,b = 1, 0.6, 0
self.colorR = r
self.colorG = g
self.colorB = b
 
titlebar:SetPoint("TOPLEFT",frame,"TOPLEFT",3,-4)
titlebar:SetPoint("TOPRIGHT",frame,"TOPRIGHT",-3,-4)
titlebar:SetHeight(13)
 
titlebar2:SetPoint("TOPLEFT",titlebar,"BOTTOMLEFT",0,0)
titlebar2:SetPoint("TOPRIGHT",titlebar,"BOTTOMRIGHT",0,0)
titlebar2:SetHeight(13)
 
titlebar:SetGradientAlpha("VERTICAL",r*0.6,g*0.6,b*0.6,1,r,g,b,1)
titlebar:SetTexture(r,g,b,1)
titlebar2:SetGradientAlpha("VERTICAL",r*0.9,g*0.9,b*0.9,1,r*0.6,g*0.6,b*0.6,1)
titlebar2:SetTexture(r,g,b,1)
 
local closebutton = CreateFrame("Button",nil,frame,"UIPanelCloseButton")
closebutton:SetScript("OnClick", closeOnClick)
closebutton:SetPoint("TOPRIGHT",frame,"TOPRIGHT",-2,-2)
 
self.closebutton = closebutton
closebutton.obj = self
 
local title = CreateFrame("Frame",nil,frame)
self.title = title
title:EnableMouse()
title:SetScript("OnMouseDown",frameOnMouseDown)
title:SetScript("OnMouseUp", frameOnMouseUp)
 
 
title:SetPoint("TOPLEFT",titlebar,"TOPLEFT",0,0)
title:SetPoint("BOTTOMRIGHT",titlebar2,"BOTTOMRIGHT",0,0)
 
local titletext = title:CreateFontString(nil,"OVERLAY","QuestTitleFont")
titletext:SetPoint("TOPLEFT",title,"TOPLEFT",0,0)
titletext:SetPoint("TOPRIGHT",title,"TOPRIGHT",0,0)
titletext:SetHeight(26)
titletext:SetShadowColor(0,0,0)
titletext:SetShadowOffset(1,-1)
titletext:SetTextColor(1,1,1)
 
self.titletext = titletext
 
local sizer_se = CreateFrame("Frame",nil,frame)
sizer_se:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",0,0)
sizer_se:SetWidth(25)
sizer_se:SetHeight(25)
sizer_se:EnableMouse()
sizer_se:SetScript("OnMouseDown",sizerseOnMouseDown)
sizer_se:SetScript("OnMouseUp", sizerOnMouseUp)
self.sizer_se = sizer_se
 
local sizer_s = CreateFrame("Frame",nil,frame)
sizer_s:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",-25,0)
sizer_s:SetPoint("BOTTOMLEFT",frame,"BOTTOMLEFT",0,0)
sizer_s:SetHeight(25)
sizer_s:EnableMouse()
sizer_s:SetScript("OnMouseDown",sizersOnMouseDown)
sizer_s:SetScript("OnMouseUp", sizerOnMouseUp)
self.sizer_s = sizer_s
 
local sizer_e = CreateFrame("Frame",nil,frame)
sizer_e:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",0,25)
sizer_e:SetPoint("TOPRIGHT",frame,"TOPRIGHT",0,0)
sizer_e:SetWidth(25)
sizer_e:EnableMouse()
sizer_e:SetScript("OnMouseDown",sizereOnMouseDown)
sizer_e:SetScript("OnMouseUp", sizerOnMouseUp)
self.sizer_e = sizer_e
 
local treeview = WaterfallTreeView:new(self)
self.treeview = treeview
treeview:SetPoint("TOPLEFT",frame,"TOPLEFT",11,-32)
treeview:SetPoint("BOTTOMLEFT",frame,"BOTTOMLEFT",11,10)
treeview:SetWidth(200)
treeview:SetMinResize(200,0)
treeview:SetMaxResize(400,10000)
 
local mainpane = WaterfallPane:new(self)
 
mainpane:SetPoint("TOPLEFT",treeview.frame,"TOPRIGHT",-3,0)
mainpane:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",-10,10)
self.mainpane = mainpane
 
end
 
function WaterfallFrame.prototype:SetColor(r,g,b)
self.colorR, self.colorG, self.colorB = r,g,b
local titlebar = self.titlebar
local titlebar2 = self.titlebar2
titlebar:SetGradientAlpha("VERTICAL",r*0.6,g*0.6,b*0.6,1,r,g,b,1)
titlebar:SetTexture(r,g,b,1)
titlebar2:SetGradientAlpha("VERTICAL",r*0.9,g*0.9,b*0.9,1,r*0.6,g*0.6,b*0.6,1)
titlebar2:SetTexture(r,g,b,1)
self.mainpane:SetColor(r,g,b)
end
 
function WaterfallFrame.prototype:GetColor()
return self.colorR, self.colorG, self.colorB
end
 
function WaterfallFrame.prototype:Hide()
self.frame:Hide()
end
 
function WaterfallFrame.prototype:Show()
self.frame:Show()
end
 
function WaterfallFrame.prototype:SetID(id)
self.id = id
end
 
function WaterfallFrame.prototype:ChangePane(newpane)
self.mainpane:Clear(newpane)
self.controlcount = 0
currentframe = self
if self.children then
local title = self.children(newpane, self.childrenArg)
self.mainpane.titletext:SetText(title)
end
currentframe = nil
self.mainpane:DoLayout()
self.mainpane.paneid = newpane
end
 
function WaterfallFrame.prototype:SetSelected(id, noupdate)
self.treeview:SetSelected(id, noupdate)
end
 
function WaterfallFrame.prototype:SetTitle(title)
self.titletext:SetText(title)
end
 
function WaterfallFrame.prototype:SetChildren(children, arg)
self.children = children
self.childrenArg = arg
end
function WaterfallFrame.prototype:SetTree(tree, treestatus, func, ...)
self.treeview:SetContent(tree, treestatus, func, ...)
end
 
function WaterfallFrame.prototype:AddControl(control)
self.mainpane:AddControl(control)
end
 
function WaterfallFrame.prototype:Refresh()
self.treeview:Refresh()
self:ChangePane(self.treeview:GetSelected())
end
 
function WaterfallFrame.prototype:CleanUp()
self.children = nil
self.tree = nil
self.treeview:CleanUp()
self:ChangePane()
self:Hide()
end
 
function WaterfallFrame.prototype:ReAnchorTree()
self.treeview:SetPoint("TOPLEFT",self.frame,"TOPLEFT",11,-32)
self.treeview:SetPoint("BOTTOMLEFT",self.frame,"BOTTOMLEFT",11,10)
end
-------------------------
-- WaterfallPane Class --
-------------------------
 
local function paneOnSizeChanged(this)
this.obj:SizeChanged()
end
local function paneOnMouseWheel(this,value)
this.obj:MoveScroll(value)
end
local function paneOnSizeChanged(this)
this.obj:DoLayout()
end
local function paneOnScrollValueChanged(this,value)
this.obj:SetScroll(value)
end
local numpanes = 0
function WaterfallPane.prototype:init(parent)
WaterfallPane.super.prototype.init(self)
numpanes = numpanes + 1
self.parent = parent
local name = "WaterfallPane"..numpanes
local frame = CreateFrame("Frame",name,parent.frame)
frame:SetBackdrop(PaneBackdrop)
frame:SetBackdropBorderColor(0.6, 0.6, 0.6)
frame:SetBackdropColor(0.1, 0.1, 0.1)
frame:SetFrameLevel(2)
frame:SetFrameStrata("DIALOG")
self.frame = frame
frame.obj = self
frame:SetScript("OnSizeChanged",paneOnSizeChanged)
 
--local title = CreateFrame("Frame",nil,frame)
--title:SetPoint("TOPLEFT",frame,"TOPLEFT",4,-4)
--title:SetPoint("TOPRIGHT",frame,"TOPRIGHT",-4,-4)
--title:SetHeight(24)
--title:SetBackdrop(PaneBackdrop)
--title:SetBackdropColor(0,0,0,0.2)
--self.title = title
 
local scrollframe = CreateFrame("ScrollFrame",nil,frame)
local scrollchild = CreateFrame("Frame",nil,scrollframe)
local scrollbar = CreateFrame("Slider",name.."ScrollBar",scrollframe,"UIPanelScrollBarTemplate")
self.scrollframe = scrollframe
self.scrollchild = scrollchild
self.scrollbar = scrollbar
 
scrollframe:SetScrollChild(scrollchild)
scrollframe:SetPoint("TOPLEFT",frame,"TOPLEFT",9,-29)
scrollframe:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",-28,12)
scrollframe:EnableMouseWheel(true)
scrollframe:SetScript("OnMouseWheel", paneOnMouseWheel)
scrollframe:SetScript("OnSizeChanged", paneOnSizeChanged)
scrollframe.obj = self
 
--scrollchild:SetPoint("TOP",scrollframe,"TOP",-20,0)
--scrollchild:SetPoint("BOTTOMLEFT",scrollframe,"BOTTOMLEFT",0,0)
scrollchild:SetWidth(400)
 
scrollbar:SetPoint("TOPLEFT",scrollframe,"TOPRIGHT",0,-16)
scrollbar:SetPoint("BOTTOMLEFT",scrollframe,"BOTTOMRIGHT",0,16)
scrollbar:SetMinMaxValues(0,1000)
scrollbar:SetValueStep(1)
scrollbar:SetValue(0)
scrollbar:SetWidth(16)
scrollbar:SetScript("OnValueChanged", paneOnScrollValueChanged)
scrollbar.obj = self
self.scrollvalue = 0
 
local titletext = frame:CreateFontString(nil,"OVERLAY","GameFontNormal")
titletext:SetPoint("TOPLEFT",frame,"TOPLEFT",8,-5)
titletext:SetPoint("TOPRIGHT",frame,"TOPRIGHT",-8,-5)
titletext:SetHeight(16)
titletext:SetText("")
titletext:SetTextColor(1,1,1)
 
local r,g,b = 1, 0.6, 0
self.colorR = r
self.colorG = g
self.colorB = b
local titlebar = frame:CreateTexture(nil,"BACKGROUND")
self.titlebar = titlebar
local titlebar2 = frame:CreateTexture(nil,"BACKGROUND")
self.titlebar2 = titlebar2
titlebar:SetPoint("TOPLEFT",frame,"TOPLEFT",4,-4)
titlebar:SetPoint("TOPRIGHT",frame,"TOPRIGHT",-4,-4)
titlebar:SetHeight(10)
titlebar2:SetPoint("TOPLEFT",titlebar,"BOTTOMLEFT",0,0)
titlebar2:SetPoint("TOPRIGHT",titlebar,"BOTTOMRIGHT",0,0)
titlebar2:SetHeight(10)
 
titlebar:SetGradientAlpha("VERTICAL",r*0.6,g*0.6,b*0.6,1,r,g,b,1)
titlebar:SetTexture(r,g,b,1)
titlebar2:SetGradientAlpha("VERTICAL",r*0.9,g*0.9,b*0.9,1,r*0.6,g*0.6,b*0.6,1)
titlebar2:SetTexture(r,g,b,1)
 
--titletext:SetTextColor(1,1,1)
self.titletext = titletext
 
self.controls = {}
end
 
function WaterfallPane.prototype:GetID()
return self.parent.id
end
 
function WaterfallPane.prototype:SetScroll(value)
local frame, child = self.scrollframe, self.scrollchild
local viewheight = frame:GetHeight()
local height = child:GetHeight()
local width, viewwidth = frame:GetWidth(), child:GetWidth()
local offset
local xOffset = max(0, (width-viewwidth)/2 )
if viewheight > height then
offset = 0
else
offset = floor((height - viewheight) / 1000.0 * value)
end
child:ClearAllPoints()
child:SetPoint("TOPLEFT",frame,"TOPLEFT",xOffset,offset)
child.offset = offset
 
self.scrollvalue = value
end
 
--[[
horiz scrolling version
function WaterfallPane.prototype:SetScroll(value)
local frame, child = self.scrollframe, self.scrollchild
local viewheight = frame:GetWidth()
local height = child:GetWidth()
local offset
if viewheight > height then
offset = 0
else
offset = floor((height - viewheight) / 1000.0 * value)
end
child:ClearAllPoints()
child:SetPoint("TOPLEFT",frame,"TOPLEFT",-offset,0)
child:SetPoint("BOTTOMLEFT",frame,"BOTTOMLEFT",-offset,0)
child.offset = offset
 
self.scrollvalue = value
end
]]
function WaterfallPane.prototype:MoveScroll(value)
local frame, child = self.scrollframe, self.scrollchild
local height, viewheight = frame:GetHeight(), child:GetHeight()
if height > viewheight then
self.scrollbar:Hide()
else
self.scrollbar:Show()
local diff = height - viewheight
local delta = 1
if value < 0 then
delta = -1
end
self.scrollbar:SetValue(min(max(self.scrollvalue + delta*(1000/(diff/45)),0), 1000))
end
end
 
function WaterfallPane.prototype:SetColor(r,g,b)
self.colorR, self.colorG, self.colorB = r,g,b
local titlebar = self.titlebar
local titlebar2 = self.titlebar2
titlebar:SetGradientAlpha("VERTICAL",r*0.6,g*0.6,b*0.6,1,r,g,b,1)
titlebar:SetTexture(r,g,b,1)
titlebar2:SetGradientAlpha("VERTICAL",r*0.9,g*0.9,b*0.9,1,r*0.6,g*0.6,b*0.6,1)
titlebar2:SetTexture(r,g,b,1)
end
 
function WaterfallPane.prototype:Clear(newpane)
for i = 1, #self.controls do
releaseObj(self.controls[i])
self.controls[i] = nil
end
if self.paneid ~= newpane then
self:SetScroll(0)
end
end
 
function WaterfallPane.prototype:ClearFocus()
for i, v in ipairs(self.controls) do
if v.class == WaterfallTextBox then
v.frame:ClearFocus()
end
end
end
 
function WaterfallPane.prototype:AddControl(control)
tinsert(self.controls, control)
control:SetParent(self)
end
 
--simple one under the other layout for now
function WaterfallPane.prototype:DoLayout()
local usedheight = 0
local maxwidth = 0
local rowwidth = 1
local rowheight = 1
for i, control in ipairs(self.controls) do
control:ClearAllPoints()
if i == 1 then
control:SetPoint("TOPLEFT",self.scrollchild,"TOPLEFT",10,0)
rowheight = control:GetHeight()
rowwidth = control:GetWidth() + 10
else
if control.noNewLine then
control:SetPoint("TOPLEFT",self.controls[i-1],"TOPRIGHT",0,0)
rowheight = max(rowheight,control:GetHeight())
rowwidth = rowwidth + control:GetWidth()
else
usedheight = usedheight + rowheight
maxwidth = max(rowwidth,maxwidth)
control:SetPoint("TOPLEFT",self.scrollchild,"TOPLEFT",10,-usedheight)
rowheight = control:GetHeight()
rowwidth = control:GetWidth()
end
end
control:Show()
end
maxwidth = max(rowwidth,maxwidth)
 
self.scrollchild:SetHeight(usedheight+rowheight)
self.scrollchild:SetWidth(maxwidth+5)
 
self:FixScroll()
end
 
--[[
function WaterfallPane.prototype:DoLayout()
local colheight = 0
local sectionheight = 0
local sectionwidth = 0
local colwidth = 0
local usedwidth = 0
local frameheight = self.scrollchild:GetHeight()
for i, control in ipairs(self.controls) do
colheight = colheight + control:GetHeight()
colwidth = max(colwidth, control:GetWidth())
control:ClearAllPoints()
if colheight <= (frameheight - 10) then
if i == 1 then
control:SetPoint("TOPLEFT",self.scrollchild,"TOPLEFT",4,0)
else
control:SetPoint("TOPLEFT",self.controls[i-1],"BOTTOMLEFT",0,0)
end
else
usedwidth = usedwidth + colwidth + 6
colwidth = 0
colheight = 0
control:SetPoint("TOPLEFT",self.scrollchild,"TOPLEFT",4+usedwidth,0)
end
control:Show()
end
usedwidth = usedwidth + colwidth
 
self.scrollchild:SetWidth(usedwidth)
self:FixScroll()
end
]]
function WaterfallPane.prototype:SizeChanged()
self:FixScroll()
end
 
function WaterfallPane.prototype:FixScroll()
local frame, child = self.scrollframe, self.scrollchild
local height, viewheight = frame:GetHeight(), child:GetHeight()
local width, viewwidth = frame:GetWidth(), child:GetWidth()
local offset = self.scrollchild.offset
if not offset then
offset = 0
end
local curvalue = self.scrollbar:GetValue()
local xOffset = max(0, (width-viewwidth)/2 )
if viewheight < height then
self.scrollbar:Hide()
--self.scrollbar:SetValue(0)
child:ClearAllPoints()
child:SetPoint("TOPLEFT",frame,"TOPLEFT",xOffset,0)
child.offset = 0
else
self.scrollbar:Show()
local value = (offset / (viewheight - height) * 1000)
if value > 1000 then value = 1000 end
self.scrollbar:SetValue(value)
self:SetScroll(value)
if value < 1000 then
child:ClearAllPoints()
child:SetPoint("TOPLEFT",frame,"TOPLEFT",xOffset,offset)
child.offset = offset
end
end
end
--[[
horiz scrolling version
function WaterfallPane.prototype:FixScroll()
local frame, child = self.scrollframe, self.scrollchild
local height, viewheight = frame:GetWidth(), child:GetWidth()
local offset = self.scrollchild.offset
if not offset then
offset = 0
end
local curvalue = self.scrollbar:GetValue()
 
if viewheight < height then
self.scrollbar:EnableMouse(false)
self.scrollbar:SetValue(0)
else
self.scrollbar:EnableMouse(true)
local value = (offset / (viewheight - height) * 1000)
if value > 1000 then value = 1000 end
self.scrollbar:SetValue(value)
self:SetScroll(value)
if value < 1000 then
child:ClearAllPoints()
child:SetPoint("TOPLEFT",frame,"TOPLEFT",-offset,0)
child:SetPoint("BOTTOMLEFT",frame,"BOTTOMLEFT",-offset,0)
child.offset = offset
end
end
end
]]
function WaterfallPane.prototype:Refresh(fullRefresh)
if fullRefresh then
self.parent:Refresh()
else
for i, control in ipairs(self.controls) do
control:Refresh()
end
end
self:FixScroll()
end
 
-----------------
-- Label Class --
-----------------
 
function WaterfallLabel.prototype:init()
WaterfallLabel.super.prototype.init(self)
local frame = self.frame
local text = frame:CreateFontString(nil,"OVERLAY","GameFontNormalSmall")
self.text = text
frame.text = text
 
text:SetText("Waterfalllabel")
text:SetJustifyH("LEFT")
text:SetTextColor(1,1,1)
frame:SetHeight(24)
frame:SetWidth(DEFAULT_CONTROL_WIDTH)
text:SetHeight(16)
text:SetPoint("LEFT",frame,"LEFT")
end
 
--fontstrings dont return the correct width untill they have been drawn, wait a frame to set the width
local function labelFixWidth(this)
if not this.obj.width then
this:SetWidth(this.text:GetWidth()+3)
end
this:SetScript("OnUpdate",nil)
end
 
function WaterfallLabel.prototype:SetText(text)
self.text:SetText(text)
self:Refresh()
end
 
function WaterfallLabel.prototype:CleanUp()
self.frame:ClearAllPoints()
self.frame:Hide()
self.width = nil
self.height = nil
self.noNewLine = nil
self.causesRefresh = nil
self.fullRefresh = nil
self.treeRefresh = nil
self:SetText("")
end
 
function WaterfallLabel.prototype:Refresh()
self.frame:SetHeight(self.height or 16)
if self.width then
self.text:ClearAllPoints()
self.text:SetPoint("LEFT",self.frame,"LEFT")
self.text:SetPoint("RIGHT",self.frame,"RIGHT")
self:SetWidth(self.width)
else
self.text:ClearAllPoints()
self.text:SetPoint("LEFT")
self.frame:SetScript("OnUpdate", labelFixWidth)
end
self.text:SetJustifyH(self.justifyH or "LEFT")
self.text:SetTextColor(self.r or 1, self.g or 1, self.b or 1)
end
 
-----------------
-- Linklabel Class --
-----------------
local function Linklabel_OnEnter(this)
local self = this.obj
if not self.disabled then
self.highlight:Show()
showGameTooltip(self)
end
end
 
local function Linklabel_OnLeave(this)
this.obj.highlight:Hide()
GameTooltip:Hide()
end
 
local function linkConfirmFunc(self, refresh, full, func, ...)
func(...)
 
if refresh or full then
self.parent:Refresh(full)
end
end
 
local function Linklabel_OnClick(this)
local self = this.obj
local func = self.linkFunc
local confirm = this.obj.confirm
 
if func then
if confirm then
if type(confirm) == "string" then
confirmPopup(confirm,linkConfirmFunc,self,self.causesRefresh,self.fullRefresh,func,getArgs(this.obj,"linkArg",1))
else
confirmPopup((ARE_YOU_SURE):format(this.obj.text:GetText()),linkConfirmFunc,
self,self.causesRefresh,self.fullRefresh,func,getArgs(this.obj,"linkArg",1))
end
else
func(getArgs(this.obj,"linkArg",1))
end
if self.causesRefresh or self.fullRefresh then
self.parent:Refresh(self.fullRefresh)
end
end
end
 
function WaterfallLinklabel.prototype:init()
WaterfallLinklabel.super.prototype.init(self, "Button")
local frame = self.frame
local text = frame:CreateFontString(nil,"OVERLAY","GameFontNormalSmall")
self.text = text
frame.text = text
frame:SetScript("OnClick", Linklabel_OnClick)
frame:SetScript("OnEnter", Linklabel_OnEnter)
frame:SetScript("OnLeave", Linklabel_OnLeave)
 
text:SetText("WaterfallLinklabel")
text:SetJustifyH("LEFT")
text:SetTextColor(1,1,1)
frame:SetHeight(24)
frame:SetWidth(DEFAULT_CONTROL_WIDTH)
text:SetHeight(16)
text:SetPoint("LEFT",frame,"LEFT")
 
local highlight = frame:CreateTexture(nil, "BACKGROUND")
self.highlight = highlight
highlight:SetTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight")
highlight:SetBlendMode("ADD")
highlight:SetAllPoints(frame)
highlight:Hide()
 
frame:EnableMouse()
end
 
function WaterfallLinklabel.prototype:SetText(text)
self.text:SetText(text)
self:Refresh()
end
 
function WaterfallLinklabel.prototype:CleanUp()
self.frame:ClearAllPoints()
self.frame:Hide()
self.width = nil
self.height = nil
self.noNewLine = nil
self.causesRefresh = nil
self.fullRefresh = nil
self.treeRefresh = nil
self:SetText("")
end
 
function WaterfallLinklabel.prototype:Refresh()
self.frame:SetHeight(self.height or 16)
 
if self.width then
self.text:ClearAllPoints()
self.text:SetPoint("LEFT",self.frame,"LEFT")
self.text:SetPoint("RIGHT",self.frame,"RIGHT")
self:SetWidth(self.width)
else
self.text:ClearAllPoints()
self.text:SetPoint("LEFT")
self.frame:SetScript("OnUpdate", labelFixWidth)
end
self.text:SetJustifyH(self.justifyH or "LEFT")
self.text:SetTextColor(self.r or 1, self.g or 1, self.b or 1)
end
 
function WaterfallLinklabel.prototype:SetColor(r,g,b)
self.r = r
self.g = g
self.b = b
self:Refresh()
end
 
-----------------
-- ColorSwatch Class --
-----------------
local tmpt = setmetatable({}, {mode='v'})
local function ColorSwatch_OnClick(this)
local self = this.obj
if not self.disabled then
local func = self.setFunc
local hasOpacity = self.hasOpacity
 
for k in pairs(tmpt) do
tmpt[k] = nil
end
for i = 1, 1000 do
local x = self['setArg'..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))
self:SetColor(r,g,b,a)
tmpt[n+1] = nil
tmpt[n+2] = nil
tmpt[n+3] = nil
tmpt[n+4] = nil
if self.causesRefresh or self.fullRefresh then
self.parent:Refresh(self.fullRefresh)
end
end
end
ColorPickerFrame.hasOpacity = self.hasOpacity
ColorPickerFrame.opacityFunc = ColorPickerFrame.func
ColorPickerFrame.opacity = 1 - (self.opacity or 0)
ColorPickerFrame:SetColorRGB(self.r, self.g, self.b)
local r, g, b, a = self.r, self.g, self.b, self.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))
self:SetColor(r,g,b,a)
for i = 1, n+4 do
tmpt[i] = nil
end
end
end
ShowUIPanel(ColorPickerFrame)
end
end
 
local function ColorSwatch_OnEnter(this)
local self = this.obj
if not self.disabled then
self.highlight:Show()
showGameTooltip(self)
end
end
 
local function ColorSwatch_OnLeave(this)
 
this.obj.highlight:Hide()
GameTooltip:Hide()
end
 
function WaterfallColorSwatch.prototype:init()
WaterfallColorSwatch.super.prototype.init(self,"Button")
local frame = self.frame
 
local text = frame:CreateFontString(nil,"OVERLAY","GameFontNormalSmall")
self.text = text
text:SetJustifyH("LEFT")
text:SetTextColor(1,1,1)
frame:SetHeight(24)
frame:SetWidth(DEFAULT_CONTROL_WIDTH)
text:SetHeight(24)
frame:SetScript("OnClick", ColorSwatch_OnClick)
frame:SetScript("OnEnter",ColorSwatch_OnEnter)
frame:SetScript("OnLeave",ColorSwatch_OnLeave)
 
local colorSwatch = frame:CreateTexture(nil, "OVERLAY")
self.colorSwatch = colorSwatch
colorSwatch:SetWidth(24)
colorSwatch:SetHeight(24)
colorSwatch:SetTexture("Interface\\ChatFrame\\ChatFrameColorSwatch")
local texture = frame:CreateTexture(nil, "OVERLAY")
colorSwatch.texture = texture
texture:SetTexture(1, 1, 1)
texture:SetWidth(13.8)
texture:SetHeight(13.8)
texture:Show()
 
local highlight = frame:CreateTexture(nil, "BACKGROUND")
self.highlight = highlight
highlight:SetTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight")
highlight:SetBlendMode("ADD")
highlight:SetAllPoints(frame)
highlight:Hide()
 
texture:SetPoint("CENTER", colorSwatch, "CENTER")
colorSwatch:SetPoint("LEFT", frame, "LEFT", 0, 0)
text:SetPoint("LEFT",colorSwatch,"RIGHT",2,0)
text:SetPoint("RIGHT",frame,"RIGHT")
end
 
function WaterfallColorSwatch.prototype:SetText(text)
self.text:SetText(text)
end
 
function WaterfallColorSwatch.prototype:SetColor(r,g,b,a)
self.r = r
self.g = g
self.b = b
self.opacity = a
self:Refresh()
end
 
function WaterfallColorSwatch.prototype:Refresh()
if self.getFunc then
self.r, self.g, self.b, self.opacity = self.getFunc(getArgs(self,"getArg",1))
end
if self.disabledFunc then
self.disabled = self.disabledFunc(getArgs(self,"disabledArg",1))
if self.inverseDisabled then
self.disabled = not self.disabled
end
end
self.colorSwatch.texture:SetTexture(self.r,self.g,self.b)
if self.disabled then
self.text:SetTextColor(0.5,0.5,0.5)
else
self.text:SetTextColor(1,1,1)
end
if self.width then
self:SetWidth(self.width)
else
self:SetWidth(DEFAULT_CONTROL_WIDTH)
end
end
 
function WaterfallColorSwatch.prototype:CleanUp()
self.frame:ClearAllPoints()
self.frame:Hide()
self.width = nil
self.height = nil
self.noNewLine = nil
self.causesRefresh = nil
self.fullRefresh = nil
self.treeRefresh = nil
self:SetText("")
self:SetFunc("color",nil)
self.r = nil
self.g = nil
self.b = nil
self.hasOpacity = nil
self.opacity = nil
self.disabled = nil
self:SetFunc("disabled",nil)
end
 
 
 
-----------------
-- Heading Class --
-----------------
 
function WaterfallHeading.prototype:init()
WaterfallHeading.super.prototype.init(self)
local frame = self.frame
local text = frame:CreateFontString(nil,"OVERLAY","GameFontNormal")
self.text = text
frame.text = text
 
text:SetJustifyH("LEFT")
frame:SetHeight(24)
frame:SetWidth(DEFAULT_CONTROL_WIDTH)
text:SetHeight(24)
text:SetPoint("LEFT",frame,"LEFT")
end
 
function WaterfallHeading.prototype:SetText(text)
self.text:SetText(text)
self.frame:SetScript("OnUpdate", labelFixWidth)
end
 
function WaterfallHeading.prototype:CleanUp()
self.frame:ClearAllPoints()
self.frame:Hide()
self.width = nil
self.height = nil
self.noNewLine = nil
self.causesRefresh = nil
self.fullRefresh = nil
self.treeRefresh = nil
self:SetText("")
end
 
function WaterfallHeading.prototype:Refresh()
self.frame:SetHeight(self.height or 16)
if self.width then
self.text:ClearAllPoints()
self.text:SetPoint("LEFT",self.frame,"LEFT")
self.text:SetPoint("RIGHT",self.frame,"RIGHT")
self:SetWidth(self.width)
else
self.text:ClearAllPoints()
self.text:SetPoint("LEFT")
self.frame:SetScript("OnUpdate", labelFixWidth)
end
self.text:SetJustifyH(self.justifyH or "LEFT")
end
-----------------
-- CheckBox Class --
-----------------
 
local function CheckBox_OnEnter(this)
local self = this.obj
if not self.disabled then
self.highlight:Show()
showGameTooltip(self)
end
end
 
local function CheckBox_OnLeave(this)
local self = this.obj
if not self.down then
self.highlight:Hide()
end
GameTooltip:Hide()
end
 
local function CheckBox_OnMouseUp(this)
local self = this.obj
if not self.disabled then
self:ToggleChecked()
self.text:SetPoint("LEFT",self.check,"RIGHT",0,0)
end
self.down = nil
end
 
local function CheckBox_OnMouseDown(this)
local self = this.obj
if not self.disabled then
self.text:SetPoint("LEFT",self.check,"RIGHT",1,-1)
self.down = true
end
end
 
function WaterfallCheckBox.prototype:init()
WaterfallCheckBox.super.prototype.init(self,"Button")
 
local frame = self.frame
local text = frame:CreateFontString(nil,"OVERLAY","GameFontNormalSmall")
self.text = text
 
frame:SetScript("OnEnter",CheckBox_OnEnter)
frame:SetScript("OnLeave",CheckBox_OnLeave)
frame:SetScript("OnMouseUp",CheckBox_OnMouseUp)
frame:SetScript("OnMouseDown",CheckBox_OnMouseDown)
frame:EnableMouse()
local checkbg = frame:CreateTexture(nil,"ARTWORK")
self.checkbg = checkbg
checkbg:SetWidth(20)
checkbg:SetHeight(20)
checkbg:SetPoint("LEFT",frame,"LEFT",0,0)
checkbg:SetTexture("Interface\\Buttons\\UI-CheckBox-Up")
local check = frame:CreateTexture(nil,"OVERLAY")
self.check = check
check:SetWidth(20)
check:SetHeight(20)
check:SetPoint("LEFT",frame,"LEFT",0,0)
check:SetTexture("Interface\\Buttons\\UI-CheckBox-Check")
 
self:UpdateCheck()
 
local highlight = frame:CreateTexture(nil, "BACKGROUND")
self.highlight = highlight
highlight:SetTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight")
highlight:SetBlendMode("ADD")
highlight:SetAllPoints(frame)
highlight:Hide()
 
 
text:SetJustifyH("LEFT")
text:SetTextColor(1,1,1)
frame:SetHeight(24)
frame:SetWidth(DEFAULT_CONTROL_WIDTH)
text:SetHeight(24)
text:SetPoint("LEFT",check,"RIGHT",0,0)
end
 
function WaterfallCheckBox.prototype:UpdateTexture()
local checkbg = self.checkbg
local check = self.check
local highlight = self.highlight
 
if self.isRadio then
checkbg:SetTexture("Interface\\Buttons\\UI-RadioButton")
checkbg:SetTexCoord(0,0.25,0,1)
check:SetTexture("Interface\\Buttons\\UI-RadioButton")
check:SetTexCoord(0.5,0.75,0,1)
check:SetBlendMode("ADD")
else
checkbg:SetTexture("Interface\\Buttons\\UI-CheckBox-Up")
checkbg:SetTexCoord(0,1,0,1)
check:SetTexture("Interface\\Buttons\\UI-CheckBox-Check")
check:SetTexCoord(0,1,0,1)
check:SetBlendMode("BLEND")
end
end
 
function WaterfallCheckBox.prototype:SetText(text)
self.text:SetText(text)
end
 
function WaterfallCheckBox.prototype:UpdateCheck()
if self.checked then
self.check:Show()
else
self.check:Hide()
end
end
 
function WaterfallCheckBox.prototype:ToggleChecked()
self.checked = not self.checked
 
if self.setFunc then
self.setFunc(getArgs(self,"setArg",1,self.checked))
end
 
if self.causesRefresh or self.fullRefresh then
self.parent:Refresh(self.fullRefresh)
else
self:Refresh()
end
end
 
function WaterfallCheckBox.prototype:SetChecked(value)
self.checked = value
self:UpdateCheck()
end
 
function WaterfallCheckBox.prototype:GetChecked()
return self.checked
end
 
function WaterfallCheckBox.prototype:CleanUp()
self.frame:ClearAllPoints()
self.frame:Hide()
self.highlight:Hide()
self.width = nil
self.height = nil
self.noNewLine = nil
self.causesRefresh = nil
self.fullRefresh = nil
self.treeRefresh = nil
self:SetText("")
self:SetChecked(false)
self:SetFunc("disabled",nil)
self:SetFunc("get",nil)
self:SetFunc("set",nil)
self.isRadio = nil
end
 
function WaterfallCheckBox.prototype:Refresh()
if self.getFunc then
local checked = self.getFunc(getArgs(self,"getArg",1))
self:SetChecked(checked)
end
if self.disabledFunc then
self.disabled = self.disabledFunc(getArgs(self,"disabledArg",1))
if self.inverseDisabled then
self.disabled = not self.disabled
end
end
if self.disabled then
self.text:SetTextColor(0.5,0.5,0.5)
SetDesaturation(self.check, true)
else
self.text:SetTextColor(1,1,1)
SetDesaturation(self.check, false)
end
if self.width then
self:SetWidth(self.width)
else
self:SetWidth(DEFAULT_CONTROL_WIDTH)
end
self:UpdateTexture()
end
 
-----------------
-- DragLink Class --
-----------------
 
-- Provides an icon and description of a link based on on its linkInfo.itemType, itemId, itemInfo
-- Dragging onto the linkIcon sets linkInfo.itemType, linkInfo.itemId, linkInfo.itemInfo
-- If linkInfo.itemType == "spell" then linkInfo.spellName, linkInfo.spellRank, linkInfo.spellClass (current player class) are also set
-- Dragging between two DragLink objects will swap their contents
 
function Waterfall:GetDraggingObject()
return Waterfall.fromObject
end
 
 
function Waterfall:SetDraggingObject(fromObject)
Waterfall.fromObject = fromObject
end
 
local function DragLinkOnDragStart(this)
local fromObject = this.obj
Waterfall:SetDraggingObject(fromObject)
end
 
local function DragLinkOnReceiveDrag(this)
local self = this.obj
local toObject = this.obj
local fromObject = Waterfall:GetDraggingObject()
local refreshNeeded = false
if (fromObject and fromObject ~= toObject) then
--DEFAULT_CHAT_FRAME:AddMessage("DragLinkOnReceiveDrag toObject " .. tostring(toObject))
fromObject.itemType, toObject.itemType = toObject.itemType, fromObject.itemType
fromObject.itemId, toObject.itemId = toObject.itemId, fromObject.itemId
fromObject.itemInfo, toObject.itemInfo = toObject.itemInfo, fromObject.itemInfo
fromObject.spellName, toObject.spellName = toObject.spellName, fromObject.spellName
fromObject.spellRank, toObject.spellRank = toObject.spellRank, fromObject.spellRank
fromObject.spellClass, toObject.spellClass = toObject.spellClass, fromObject.spellClass
if fromObject.causesRefresh or fromObject.fullRefresh then
fromObject.parent:Refresh(fromObject.fullRefresh)
end
refreshNeeded = true
else
local itemType, itemId, itemInfo = GetCursorInfo()
--DEFAULT_CHAT_FRAME:AddMessage("DragLinkOnReceiveDrag itemType " .. tostring(itemType) .. " itemId " .. tostring(itemId) .. " itemInfo " .. tostring(itemInfo))
if (itemType == "item" or itemType == "spell" or itemType == "macro") then
local linkInfo = self.linkInfo
linkInfo.itemType = itemType
linkInfo.itemId = itemId
linkInfo.itemInfo = itemInfo
if (itemType == "spell") then
local spellName, spellRank = GetSpellName(itemId, itemInfo)
linkInfo.spellName = spellName
linkInfo.spellRank = spellRank
linkInfo.spellClass = WaterfallDragLink.CLASS
else
linkInfo.spellName = nil
linkInfo.spellRank = nil
linkInfo.spellClass = nil
end
--DEFAULT_CHAT_FRAME:AddMessage("DragLinkOnReceiveDrag itemType " .. tostring(itemType) .. " itemId " .. tostring(itemId) .. " itemInfo " .. tostring(itemInfo))
--DEFAULT_CHAT_FRAME:AddMessage("DragLinkOnReceiveDrag self.setFunc " .. tostring(self.setFunc) .. " self.disabled " .. tostring(self.disabled))
if (self.setFunc and not self.disabled) then
self.setFunc(getArgs(self,"setArg",1,linkInfo))
end
refreshNeeded = true
ClearCursor()
end
end
 
if (refreshNeeded) then
if self.causesRefresh or self.fullRefresh then
self.parent:Refresh(self.fullRefresh)
else
self:Refresh()
end
end
Waterfall:SetDraggingObject(nil)
end
 
local function DragLinkOnClick(this)
DragLinkOnReceiveDrag(this)
end
 
local function DragLinkOnEnter(this)
local self = this.obj
local linkInfo = self.linkInfo
if (linkInfo) then
if (linkInfo.itemType == "item") then
GameTooltip:SetOwner(this, "ANCHOR_PRESERVE")
if (linkInfo.itemId) then
GameTooltip:SetHyperlink("item:" .. linkInfo.itemId .. ":0:0:0:0:0:0:0")
else
GameTooltip:SetHyperlink(tostring(linkInfo.itemInfo))
end
elseif (linkInfo.itemType == "spell") then
if (linkInfo.spellName) then
-- Cannot set this without preserving spell book & spell id. Even then may be from different class. So lame. Awaiting a Blizzard API that doesnt suck.
-- GameTooltip:SetSpell(linkInfo.spellId, linkInfo.spellTab)
GameTooltip:SetOwner(this, "ANCHOR_PRESERVE")
GameTooltip:AddLine(linkInfo.spellName, 1, 1, 1)
local name, rank, icon, cost, isFunnel, powerType, castTime, minRange, maxRange = GetSpellInfo(linkInfo.spellName)
if (name) then
if (powerType == 0) then
cost = tostring(cost) .. " " .. MANA
elseif (powerType == 1) then
cost = tostring(cost) .. " " .. RAGE
elseif (powerType == 2) then
cost = tostring(cost) .. " " .. FOCUS
elseif (powerType == 3) then
cost = tostring(cost) .. " " .. ENERGY
elseif (powerType == 4) then
cost = tostring(cost) .. " " .. HAPPINESS
end
local range = ""
if (maxRange) then
range = SPELL_RANGE:format(maxRange)
end
GameTooltip:AddDoubleLine(cost, range, 1, 1, 1, 1, 1, 1)
if (castTime and castTime > 0) then
GameTooltip:AddLine(SPELL_CAST_TIME_SEC:format(castTime), 1, 1, 1)
else
GameTooltip:AddLine(SPELL_CAST_TIME_INSTANT, 1, 1, 1)
end
if (rank) then
GameTooltip:AddLine(tostring(rank))
end
GameTooltip:Show()
end
end
elseif (linkInfo.itemType == "macro") then
if (linkInfo.itemId) then
local name, icon, body = GetMacroInfo(linkInfo.itemId)
if (name) then
GameTooltip:SetOwner(this, "ANCHOR_PRESERVE")
GameTooltip:AddLine(tostring(name), 0.2, 0.8, 0.8, 1)
GameTooltip:AddTexture(icon)
GameTooltip:AddLine(tostring(body), 1, 1, 1, 1)
GameTooltip:Show()
end
end
end
end
end
 
local function DragLinkOnLeave(this)
GameTooltip:Hide()
end
 
local function DragLinkGetTexture(self)
local linkInfo = self.linkInfo
local texture
if (linkInfo) then
if (linkInfo.itemType == "item") then
if (linkInfo.itemId and tonumber(linkInfo.itemId)) then
_,_,_,_,_,_,_,_,_,texture = GetItemInfo(tonumber(linkInfo.itemId))
if (texture) then
return texture
end
end
elseif (linkInfo.itemType == "spell") then
if (linkInfo.spellName) then
_, _, texture = GetSpellInfo(linkInfo.spellName)
if (texture) then
return texture
end
end
elseif (linkInfo.itemType == "macro") then
if (linkInfo.itemId) then
_, texture = GetMacroInfo(linkInfo.itemId)
if (texture) then
return texture
end
end
end
end
return "Interface\\Icons\\INV_Misc_Gift_01"
end
 
WaterfallDragLink.defaultIconSize = 36
_, WaterfallDragLink.CLASS = UnitClass("player") -- Use the capitalized english class name
 
function WaterfallDragLink.prototype:init()
WaterfallDragLink.super.prototype.init(self, "Button")
 
local frame = self.frame
local text = frame:CreateFontString(nil,"OVERLAY","GameFontNormalSmall")
self.text = text
 
frame:SetScript("OnDragStart", DragLinkOnDragStart)
frame:SetScript("OnReceiveDrag", DragLinkOnReceiveDrag)
frame:SetScript("OnClick", DragLinkOnClick)
frame:SetScript("OnEnter", DragLinkOnEnter)
frame:SetScript("OnLeave", DragLinkOnLeave)
 
frame:EnableMouse()
frame:RegisterForDrag("LeftButton")
frame:RegisterForClicks("LeftButtonUp", "RightButtonUp")
 
local linkIcon = frame:CreateTexture(nil, "OVERLAY")
linkIcon:SetWidth(self.iconWidth or WaterfallDragLink.defaultIconSize)
linkIcon:SetHeight(self.iconHeight or WaterfallDragLink.defaultIconSize)
linkIcon:SetPoint("LEFT",frame,"LEFT",0,0)
linkIcon:SetTexture(DragLinkGetTexture(self))
linkIcon:SetTexCoord(0,1,0,1)
linkIcon:Show()
self.linkIcon = linkIcon
 
text:SetJustifyH("LEFT")
text:SetTextColor(1,1,1)
 
frame:SetHeight(self.iconHeight or WaterfallDragLink.defaultIconSize + 4)
frame:SetWidth(DEFAULT_CONTROL_WIDTH)
 
text:SetHeight(self.iconHeight or WaterfallDragLink.defaultIconSize + 4)
text:SetPoint("LEFT",check,"RIGHT",0,0)
end
 
function WaterfallDragLink.prototype:SetText(text)
self.text:SetText(text)
end
 
function WaterfallDragLink.prototype:CleanUp()
self.frame:ClearAllPoints()
self.frame:Hide()
self.width = nil
self.height = nil
self.noNewLine = nil
self.causesRefresh = nil
self.fullRefresh = nil
self.treeRefresh = nil
self:SetText("")
self:SetFunc("disabled", nil)
self.linkIcon:Hide()
end
 
function WaterfallDragLink.prototype:Refresh()
if self.disabledFunc then
self.disabled = self.disabledFunc(getArgs(self,"disabledArg",1))
if self.inverseDisabled then
self.disabled = not self.disabled
end
end
if self.disabled then
self.frame:Disable()
else
self.frame:Enable()
end
if self.width then
self:SetWidth(self.width)
else
self:SetWidth(DEFAULT_CONTROL_WIDTH)
end
if (not self.linkIcon) then
local linkIcon = self.frame:CreateTexture(nil, "OVERLAY")
linkIcon:SetWidth(self.iconWidth or WaterfallDragLink.defaultIconSize)
linkIcon:SetHeight(self.iconHeight or WaterfallDragLink.defaultIconSize)
linkIcon:SetPoint("TOP", self.frame, "TOP", 0, 0)
linkIcon:SetTexCoord(0,1,0,1)
linkIcon:Show()
self.linkIcon = linkIcon
end
if self.getFunc then
self.linkInfo = self.getFunc(getArgs(self,"getArg",1))
else
assert(self.linkInfo, "wth?")
end
self.linkIcon:SetTexture(DragLinkGetTexture(self))
self.linkIcon:Show()
if (self.linkInfo.itemId) then
self:SetText((self.linkInfo.itemInfo or "") .. " (" .. tostring(self.linkInfo.itemId) .. ")")
else
self:SetText("")
end
--DEFAULT_CHAT_FRAME:AddMessage("WaterfallDragLink.prototype:Refresh texture " .. tostring(DragLinkGetTexture(self)) .. " self.linkInfo.itemId " .. tostring(self.linkInfo.itemId) .. " self.linkInfo.itemInfo " .. tostring(self.linkInfo.itemInfo))
end
 
-----------------
-- Button Class --
-----------------
local function buttonConfirmFunc(self, refresh, full, func, ...)
func(...)
 
if refresh or full then
self.parent:Refresh(full)
end
end
 
local function Button_OnClick(this)
local self = this.obj
local func = self.execFunc
local confirm = this.obj.confirm
if func then
if confirm then
if type(confirm) == "string" then
confirmPopup(confirm,buttonConfirmFunc,self,self.causesRefresh,self.fullRefresh,func,getArgs(this.obj,"execArg",1))
else
confirmPopup((ARE_YOU_SURE):format(this.obj.frame:GetText()),buttonConfirmFunc,
self,self.causesRefresh,self.fullRefresh,func,getArgs(this.obj,"execArg",1))
end
else
func(getArgs(this.obj,"execArg",1))
end
if self.causesRefresh or self.fullRefresh then
self.parent:Refresh(self.fullRefresh)
end
end
end
 
local function Control_OnEnter(this)
if this.obj.link then
GameTooltip:SetOwner(this, "ANCHOR_PRESERVE")
GameTooltip:SetHyperlink(this.obj.link)
else
showGameTooltip(this.obj)
end
end
 
local function Control_OnLeave(this)
GameTooltip:Hide()
end
 
function WaterfallButton.prototype:init()
WaterfallButton.super.prototype.init(self,"Button","UIPanelButtonTemplate")
local frame = self.frame
--local text = frame:CreateFontString(nil,"OVERLAY","GameFontNormalSmall")
local text = frame:GetFontString()
text:SetPoint("LEFT",frame,"LEFT",7,0)
text:SetPoint("RIGHT",frame,"RIGHT",-7,0)
 
frame:SetScript("OnClick",Button_OnClick)
frame:SetScript("OnEnter",Control_OnEnter)
frame:SetScript("OnLeave",Control_OnLeave)
 
frame:EnableMouse()
 
frame:SetHeight(24)
frame:SetWidth(DEFAULT_CONTROL_WIDTH)
 
end
 
function WaterfallButton.prototype:SetText(text)
self.frame:SetText(text)
end
 
function WaterfallButton.prototype:CleanUp()
self.frame:ClearAllPoints()
self.frame:Hide()
self.width = nil
self.height = nil
self.noNewLine = nil
self.causesRefresh = nil
self.fullRefresh = nil
self.treeRefresh = nil
self:SetText("")
self:SetFunc("disabled",nil)
self:SetFunc("exec",nil)
end
 
function WaterfallButton.prototype:Refresh()
if self.disabledFunc then
self.disabled = self.disabledFunc(getArgs(self,"disabledArg",1))
if self.inverseDisabled then
self.disabled = not self.disabled
end
end
if self.disabled then
self.frame:Disable()
else
self.frame:Enable()
end
if self.width then
self:SetWidth(self.width)
else
self:SetWidth(DEFAULT_CONTROL_WIDTH)
end
end
 
-----------------
-- Keybinding Class --
-----------------
 
local function Keybinding_OnClick(this, button)
local self = this.obj
 
if button == "LeftButton" or button == "RightButton" then
if self.waitingForKey then
this:EnableKeyboard(false)
self.msgframe:Hide()
this:UnlockHighlight()
self.waitingForKey = nil
else
this:EnableKeyboard(true)
self.parent:ClearFocus()
self.msgframe:Show()
this:LockHighlight()
self.waitingForKey = true
end
end
end
 
 
local function Keybinding_OnKeyDown(this, key)
local self = this.obj
if self.waitingForKey then
local keyPressed = key;
if keyPressed == "ESCAPE" then
keyPressed = ""
else
if ( keyPressed == "BUTTON1" or keyPressed == "BUTTON2" ) then
return;
end
if ( keyPressed == "UNKNOWN" ) then
return;
end
if ( keyPressed == "SHIFT" or keyPressed == "CTRL" or keyPressed == "ALT") then
return;
end
if ( keyPressed == "LSHIFT" or keyPressed == "LCTRL" or keyPressed == "LALT") then
return;
end
if ( keyPressed == "RSHIFT" or keyPressed == "RCTRL" or keyPressed == "RALT") then
return;
end
if ( IsShiftKeyDown() ) then
keyPressed = "SHIFT-"..keyPressed;
end
if ( IsControlKeyDown() ) then
keyPressed = "CTRL-"..keyPressed;
end
if ( IsAltKeyDown() ) then
keyPressed = "ALT-"..keyPressed;
end
end
 
if self.setFunc and not self.disabled then
local validated = true
if keyPressed == "" then
keyPressed = nil
else
if self.keybindingOnly and not self.keybindingOnly[keyPressed] then
validated = false
end
if self.keybindingExcept and self.keybindingExcept[keyPressed] then
validated = false
end
end
if validated then
self.setFunc(getArgs(self,"setArg",1,keyPressed))
end
if self.causesRefresh or self.fullRefresh then
self.parent:Refresh(self.fullRefresh)
end
end
self:Refresh()
 
this:EnableKeyboard(false)
self.msgframe:Hide()
this:UnlockHighlight()
self.waitingForKey = nil
end
end
 
local function Keybinding_OnMouseDown(this, button)
if ( button == "LeftButton" or button == "RightButton" ) then
return
elseif ( button == "MiddleButton" ) then
button = "BUTTON3";
elseif ( button == "Button4" ) then
button = "BUTTON4"
elseif ( button == "Button5" ) then
button = "BUTTON5"
end
Keybinding_OnKeyDown(this, button)
end
 
local function keybindingMsgFixWidth(this)
this:SetWidth(this.msg:GetWidth()+10)
this:SetScript("OnUpdate",nil)
end
 
function WaterfallKeybinding.prototype:init()
WaterfallKeybinding.super.prototype.init(self,"Button","UIPanelButtonTemplate")
local frame = self.frame
--local text = frame:CreateFontString(nil,"OVERLAY","GameFontNormalSmall")
local text = frame:GetFontString()
text:SetPoint("LEFT",frame,"LEFT",7,0)
text:SetPoint("RIGHT",frame,"RIGHT",-7,0)
 
frame:SetScript("OnClick",Keybinding_OnClick)
frame:SetScript("OnKeyDown",Keybinding_OnKeyDown)
frame:SetScript("OnEnter",Control_OnEnter)
frame:SetScript("OnLeave",Control_OnLeave)
frame:SetScript("OnMouseDown",Keybinding_OnMouseDown)
frame:RegisterForClicks("AnyDown")
frame:EnableMouse()
 
frame:SetHeight(24)
frame:SetWidth(DEFAULT_CONTROL_WIDTH)
 
local msgframe = CreateFrame("Frame",nil,UIParent)
msgframe:SetHeight(30)
msgframe:SetBackdrop(ControlBackdrop)
msgframe:SetBackdropColor(0,0,0)
msgframe:SetFrameStrata("DIALOG")
msgframe:SetFrameLevel(20)
self.msgframe = msgframe
local msg = msgframe:CreateFontString(nil,"OVERLAY","GameFontNormal")
msg:SetText("Press a key to bind, ESC to clear the binding or click the button again to cancel")
msgframe.msg = msg
msg:SetPoint("TOPLEFT",msgframe,"TOPLEFT",5,-5)
msgframe:SetScript("OnUpdate", keybindingMsgFixWidth)
msgframe:SetPoint("TOP",frame,"BOTTOM",0,-2)
msgframe:Hide()
end
 
function WaterfallKeybinding.prototype:SetText(text)
self.frame:SetText(text)
end
 
function WaterfallKeybinding.prototype:CleanUp()
self.frame:ClearAllPoints()
self.frame:Hide()
self.width = nil
self.height = nil
self.noNewLine = nil
self.causesRefresh = nil
self.fullRefresh = nil
self.treeRefresh = nil
self:SetText("")
self:SetFunc("disabled",nil)
self:SetFunc("exec",nil)
self.waitingForKey = nil
self.msgframe:Hide()
end
 
function WaterfallKeybinding.prototype:Refresh()
if self.getFunc then
local text = self.getFunc(getArgs(self,"getArg",1))
self:SetText(text or "")
end
if self.disabledFunc then
self.disabled = self.disabledFunc(getArgs(self,"disabledArg",1))
if self.inverseDisabled then
self.disabled = not self.disabled
end
end
if self.disabled then
self.frame:Disable()
else
self.frame:Enable()
end
if self.width then
self:SetWidth(self.width)
else
self:SetWidth(DEFAULT_CONTROL_WIDTH)
end
end
 
-----------------
-- Slider Class --
-----------------
 
local function Slider_OnValueChanged(this)
local self = this.obj
if not this.setup then
local func = self.setFunc
local newvalue
newvalue = this:GetValue()
if newvalue ~= self.value and not self.disabled then
if self.bigStep > 0 then
self.value = math.floor((newvalue - self.min) / self.bigStep + 0.5) * self.bigStep + self.min
else
self.value = newvalue
end
if func and not self.isInit then
if (self.finalSetOnly) then
self.finalSetValue = self.value
else
func(getArgs(this.obj,"setArg",1,self.value))
end
end
end
if self.value then
this.obj.valuetext:SetText(math.floor(self.value*10)/10)
end
end
end
 
local function Slider_OnMouseUp(this)
local self = this.obj
if (self.finalSetValue) then
local func = self.setFunc
if func then
func(getArgs(this.obj,"setArg",1,self.finalSetValue))
end
end
if self.causesRefresh or self.fullRefresh then
self.parent:Refresh(self.fullRefresh)
end
end
 
local SliderBackdrop = {
bgFile = "Interface\\Buttons\\UI-SliderBar-Background",
edgeFile = "Interface\\Buttons\\UI-SliderBar-Border",
tile = true, tileSize = 8, edgeSize = 8,
insets = { left = 3, right = 3, top = 6, bottom = 6 }
}
 
function WaterfallSlider.prototype:init()
WaterfallSlider.super.prototype.init(self,"Frame")
local frame = self.frame
 
self.slider = CreateFrame("Slider",nil,frame)
local slider = self.slider
slider:SetScript("OnEnter",Control_OnEnter)
slider:SetScript("OnLeave",Control_OnLeave)
slider:SetScript("OnMouseUp", Slider_OnMouseUp)
slider.obj = self
slider:SetOrientation("HORIZONTAL")
frame:SetWidth(DEFAULT_CONTROL_WIDTH)
slider:SetHeight(17)
slider:SetHitRectInsets(0,0,-10,-10)
slider:SetBackdrop(SliderBackdrop)
self.text = slider:CreateFontString(nil,"ARTWORK","GameFontNormalSmall")
self.text:SetPoint("TOP",frame,"TOP",0,-2)
 
self.lowtext = slider:CreateFontString(nil,"ARTWORK","GameFontHighlightSmall")
self.lowtext:SetPoint("TOPLEFT",slider,"BOTTOMLEFT",2,3)
 
self.hightext = slider:CreateFontString(nil,"ARTWORK","GameFontHighlightSmall")
self.hightext:SetPoint("TOPRIGHT",slider,"BOTTOMRIGHT",-2,3)
 
self.valuetext = slider:CreateFontString(nil,"ARTWORK","GameFontHighlightSmall")
self.valuetext:SetPoint("TOP",slider,"BOTTOM",0,3)
 
 
slider:SetThumbTexture("Interface\\Buttons\\UI-SliderBar-Button-Horizontal")
 
frame:SetWidth(DEFAULT_CONTROL_WIDTH)
frame:SetHeight(40)
slider:SetPoint("TOP",self.text,"BOTTOM",0,0)
slider:SetPoint("LEFT",frame,"LEFT",0,0)
slider:SetPoint("RIGHT",frame,"RIGHT",0,0)
 
if self.getFunc then
self.isInit = true
self.value = self.getFunc(getArgs(self,"getArg",1))
self.isInit = nil
end
slider:SetValue(self.value or 0)
slider:SetScript("OnValueChanged",Slider_OnValueChanged)
 
end
 
function WaterfallSlider.prototype:CleanUp()
self.frame:ClearAllPoints()
self.frame:Hide()
self.width = nil
self.height = nil
self.noNewLine = nil
self.causesRefresh = nil
self.fullRefresh = nil
self.treeRefresh = nil
self:SetText("")
self:SetFunc("disabled",nil)
self:SetFunc("get",nil)
self:SetFunc("set",nil)
self.min = nil
self.max = nil
self.step = nil
self.value = nil
self.parent = nil
self.disabled = nil
end
 
function WaterfallSlider.prototype:SetText(text)
self.text:SetText(text)
end
 
function WaterfallSlider.prototype:Refresh()
local frame = self.slider
frame.setup = true
frame:SetMinMaxValues(self.min or 0,self.max or 100)
self.lowtext:SetText(self.min or 0)
self.hightext:SetText(self.max or 100)
frame:SetValueStep(self.step or 1)
frame.setup = nil
if self.getFunc then
local newvalue
newvalue = self.getFunc(getArgs(self,"getArg",1))
if newvalue ~= self.value then
self.isInit = true
frame:SetValue(newvalue)
self.isInit = nil
end
end
 
if self.disabledFunc then
self.disabled = self.disabledFunc(getArgs(self,"disabledArg",1))
if self.inverseDisabled then
self.disabled = not self.disabled
end
end
if self.disabled then
self.slider:EnableMouse(false)
self.text:SetTextColor(.5,.5,.5)
self.hightext:SetTextColor(.5,.5,.5)
self.lowtext:SetTextColor(.5,.5,.5)
self.valuetext:SetTextColor(.5,.5,.5)
else
self.slider:EnableMouse(true)
self.text:SetTextColor(1,1,1)
self.hightext:SetTextColor(1,1,1)
self.lowtext:SetTextColor(1,1,1)
self.valuetext:SetTextColor(1,1,1)
end
 
if self.width then
self:SetWidth(self.width)
else
self:SetWidth(DEFAULT_CONTROL_WIDTH)
end
end
 
-------------------
-- TextBox Class --
-------------------
 
local function TextBox_OnEscapePressed(this)
this:ClearFocus()
end
 
local function TextBox_OnEnterPressed(this)
local self = this.obj
local value = this:GetText()
if self.toNumber then
value = tonumber(value)
--TODO: same message as a failed validation
if not value then return end
end
if self.setFunc and not self.disabled then
local validated = true
if type(self.validateFunc) == "function" then
validated = self.validateFunc(getArgs(self,"validateArg",1,value))
if validated and self.validateModifies then
value = validated
local setOnTextChanged = self.setOnTextChanged
self.setOnTextChanged = nil
this:SetText(value)
self.setOnTextChanged = setOnTextChanged
end
elseif type(self.validateFunc) == "table" then
validated = self.validateFunc[value]
end
--TODO: message for failed validation
if validated then
if self.parseList then
self.setFunc(getArgs(self,"setArg",1,parseList(value)))
else
self.setFunc(getArgs(self,"setArg",1,value))
end
end
if self.getFunc == false then
self:SetText("")
end
if self.causesRefresh or self.fullRefresh then
self.parent:Refresh(self.fullRefresh)
end
end
end
 
local function TextBox_OnTextChanged(this)
local self = this.obj
local value = this:GetText()
if self.setOnTextChanged then
TextBox_OnEnterPressed(this)
end
if self.changedFunc and not self.disabled then
if self.parseList then
self.changedFunc(getArgs(self,"changedArg",1,parseList(value)))
else
self.changedFunc(getArgs(self,"changedArg",1,value))
end
end
end
 
function WaterfallTextBox.prototype:init()
WaterfallTextBox.super.prototype.init(self,"EditBox")
local frame = self.frame
--local text = frame:CreateFontString(nil,"OVERLAY","GameFontNormalSmall")
--self.text = text
frame:SetScript("OnEnter",Control_OnEnter)
frame:SetScript("OnLeave",Control_OnLeave)
frame:SetFontObject(ChatFontNormal)
frame:SetScript("OnEscapePressed",TextBox_OnEscapePressed)
frame:SetScript("OnEnterPressed",TextBox_OnEnterPressed)
frame:SetScript("OnTextChanged",TextBox_OnTextChanged)
frame:SetTextInsets(5,5,3,3)
frame:SetMaxLetters(256)
frame:SetAutoFocus(false)
frame:SetHeight(26)
frame:SetWidth(DEFAULT_CONTROL_WIDTH)
frame:SetBackdrop(ControlBackdrop)
frame:SetBackdropColor(0,0,0)
end
 
local function UglyScrollLeft()
this:HighlightText(0,1);
this:Insert(" "..strsub(this:GetText(),1,1));
this:HighlightText(0,1);
this:Insert("");
this:SetScript("OnUpdate", nil);
end
 
function WaterfallTextBox.prototype:SetText(text)
self.frame:SetText(text)
self.frame:SetScript("OnUpdate", UglyScrollLeft);
end
 
function WaterfallTextBox.prototype:CleanUp()
self.frame:ClearAllPoints()
self.frame:Hide()
self.width = nil
self.height = nil
self.noNewLine = nil
self.causesRefresh = nil
self.fullRefresh = nil
self.treeRefresh = nil
self.setOnTextChanged = nil
self:SetText("")
self:SetFunc("disabled",nil)
self:SetFunc("get",nil)
self:SetFunc("set",nil)
self:SetFunc("changed",nil)
end
 
function WaterfallTextBox.prototype:Refresh()
if self.getFunc then
local text = self.getFunc(getArgs(self,"getArg",1))
if text then
self:SetText(text)
end
end
 
if self.disabledFunc then
self.disabled = self.disabledFunc(getArgs(self,"disabledArg",1))
if self.inverseDisabled then
self.disabled = not self.disabled
end
end
if self.disabled then
self.frame:EnableMouse(false)
self.frame:ClearFocus()
self.frame:SetTextColor(0.5,0.5,0.5)
else
self.frame:EnableMouse(true)
self.frame:SetTextColor(1,1,1)
end
if self.width then
self:SetWidth(self.width)
else
self:SetWidth(DEFAULT_CONTROL_WIDTH)
end
end
 
 
--------------------
-- DropDown Class --
--------------------
 
local function Dropdown_OnEnterPressed(this)
local self = this.obj
if self.setFunc and not self.disabled then
local ret
if self.strict and this.value then
ret = this.value
else
ret = this:GetText()
end
if self.parseList then
self.setFunc(getArgs(self,"setArg",1,parseList(ret)))
else
self.setFunc(getArgs(self,"setArg",1,ret))
end
if self.causesRefresh or self.fullRefresh then
self.parent:Refresh(self.fullRefresh)
end
end
end
 
local function Dropdown_TogglePullout(this)
local self = this.obj
if self.open then
self.open = nil
self.pullout:Hide()
else
self.open = true
self:BuildPullout()
if self.lines[1] and self.lines[1]:IsShown() then
self.pullout:Show()
end
end
end
 
local function Dropdown_OnHide(this)
this.obj.pullout:Hide()
end
 
local function Dropdown_LineClicked(this)
local self = this.obj
self.open = false
self.pullout:Hide()
self.editbox:SetText(this.text:GetText())
self.editbox.value = this.value
Dropdown_OnEnterPressed(self.editbox)
end
 
local function Dropdown_LineEnter(this)
this.highlight:Show()
end
 
local function Dropdown_LineLeave(this)
this.highlight:Hide()
end
 
function WaterfallDropdown.prototype:init()
WaterfallDropdown.super.prototype.init(self,"Frame")
local frame = self.frame
frame.obj = self
--local text = frame:CreateFontString(nil,"OVERLAY","GameFontNormalSmall")
--self.text = text
local editbox = CreateFrame("EditBox",nil,frame)
self.editbox = editbox
editbox.obj = self
editbox:SetFontObject(ChatFontNormal)
editbox:SetScript("OnEscapePressed",TextBox_OnEscapePressed)
editbox:SetScript("OnEnterPressed",Dropdown_OnEnterPressed)
frame:SetScript("OnEnter",Control_OnEnter)
frame:SetScript("OnLeave",Control_OnLeave)
editbox:SetScript("OnEnter",Control_OnEnter)
editbox:SetScript("OnLeave",Control_OnLeave)
editbox:SetTextInsets(5,5,3,3)
editbox:SetMaxLetters(256)
editbox:SetAutoFocus(false)
editbox:SetBackdrop(ControlBackdrop)
editbox:SetBackdropColor(0,0,0)
 
editbox:SetPoint("TOPLEFT",frame,"TOPLEFT",0,0)
editbox:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",-20,0)
local button = CreateFrame("Button",nil,frame)
self.button = button
button.obj = self
button:SetWidth(24)
button:SetHeight(24)
button:SetScript("OnEnter",Control_OnEnter)
button:SetScript("OnLeave",Control_OnLeave)
button:SetNormalTexture("Interface\\ChatFrame\\UI-ChatIcon-ScrollDown-Up")
button:GetNormalTexture():SetTexCoord(.09,.91,.09,.91)
button:SetPushedTexture("Interface\\ChatFrame\\UI-ChatIcon-ScrollDown-Down")
button:GetPushedTexture():SetTexCoord(.09,.91,.09,.91)
button:SetDisabledTexture("Interface\\ChatFrame\\UI-ChatIcon-ScrollDown-Disabled")
button:GetDisabledTexture():SetTexCoord(.09,.91,.09,.91)
button:SetHighlightTexture("Interface\\Buttons\\UI-Common-MouseHilight", "ADD")
button:GetHighlightTexture():SetTexCoord(.09,.91,.09,.91)
button:SetPoint("RIGHT",frame,"RIGHT",0,0)
button:SetScript("OnClick",Dropdown_TogglePullout)
frame:SetHeight(26)
frame:SetWidth(DEFAULT_CONTROL_WIDTH)
frame:SetScript("OnHide",Dropdown_OnHide)
local pullout = CreateFrame("Frame",nil,UIParent)
self.pullout = pullout
frame:EnableMouse()
pullout:SetBackdrop(ControlBackdrop)
pullout:SetBackdropColor(0,0,0)
pullout:SetFrameStrata("DIALOG")
pullout:SetFrameLevel(20)
pullout:SetPoint("TOPLEFT",frame,"BOTTOMLEFT",0,0)
pullout:SetPoint("TOPRIGHT",frame,"BOTTOMRIGHT",-24,0)
pullout:SetClampedToScreen(true)
pullout:Hide()
 
self.lines = {}
end
 
function WaterfallDropdown.prototype:SetText(text)
self.editbox:SetText(text)
end
 
function WaterfallDropdown.prototype:CleanUp()
self.frame:ClearAllPoints()
self.frame:Hide()
self.width = nil
self.height = nil
self.noNewLine = nil
self.causesRefresh = nil
self.fullRefresh = nil
self.treeRefresh = nil
self:SetText("")
self:SetFunc("disabled",nil)
self:SetFunc("get",nil)
self:SetFunc("set",nil)
self.editbox.value = nil
self.columns = nil
end
 
function WaterfallDropdown.prototype:Refresh()
local list = self.list
if self.getFunc then
local newval = self.getFunc(getArgs(self,"getArg",1))
if newval then
if self.strict then
if type(list) == "table" then
local value
for k, text in pairs(list) do
if type(k) == "string" then
value = k
else
value = text
end
if newval == value then
self:SetText(text)
self.editbox.value = value
break
end
end
elseif type(list) == "function" then
for i, text, value in list() do
if newval == value then
self:SetText(text)
self.editbox.value = value
break
end
end
end
else
self.editbox:SetText(newval)
self.editbox.value = newval
end
else
self.editbox:SetText("")
self.editbox.value = newval
end
end
 
if self.disabledFunc then
self.disabled = self.disabledFunc(getArgs(self,"disabledArg",1))
if self.inverseDisabled then
self.disabled = not self.disabled
end
end
if self.disabled then
self.editbox:EnableMouse(false)
self.editbox:ClearFocus()
self.editbox:SetTextColor(0.5,0.5,0.5)
self.button:Disable()
else
self.button:Enable()
if self.strict then
self.editbox:EnableMouse(false)
self.editbox:ClearFocus()
self.editbox:SetTextColor(1,1,1)
else
self.editbox:EnableMouse(true)
self.editbox:SetTextColor(1,1,1)
end
end
if self.width then
self:SetWidth(self.width)
else
self:SetWidth(DEFAULT_CONTROL_WIDTH)
end
end
 
local ddsort = {}
function WaterfallDropdown.prototype:BuildPullout()
local list = self.list
local lines = self.lines
local totalheight = 10
self:ClearPullout()
if type(list) == "table" then
for k, v in pairs(list) do
tinsert(ddsort,k)
end
table.sort(ddsort)
local columns = tonumber(self.columns)
local columnHeight, columnWidth
local columnCount = 0
self.pullout:ClearAllPoints()
if (columns) then
columnHeight = math.floor((# ddsort) / columns) + 2
columnWidth = self.pullout:GetWidth()
self.pullout:SetPoint("TOPLEFT",self.frame,"BOTTOMRIGHT", -1 * DEFAULT_CONTROL_WIDTH * columns - 24,0)
self.pullout:SetPoint("TOPRIGHT",self.frame,"BOTTOMRIGHT",-24,0)
else
self.pullout:SetPoint("TOPLEFT",self.frame,"BOTTOMRIGHT", -1 * DEFAULT_CONTROL_WIDTH - 24,0)
self.pullout:SetPoint("TOPRIGHT",self.frame,"BOTTOMRIGHT",-24,0)
columnHeight = # ddsort + 1
end
for i, value in pairs(ddsort) do
local text = list[value]
if ((i - 1) % (columnHeight - 1) == 0) then
columnCount = columnCount + 1
end
if (lines[i]) then
self:UpdateLine(lines[i], math.fmod(i-1, (columnHeight-1)), columnCount)
else
lines[i] = self:CreateLine(math.fmod(i-1, (columnHeight-1)), columnCount)
end
if i == 1 then
lines[i]:SetPoint("TOP",self.pullout,"TOP",0,-5)
else
if (columns and ((i - 1) % columnHeight == 0)) then
lines[i]:SetPoint("TOP",self.pullout,"TOP",0,-5)
else
lines[i]:SetPoint("TOP",lines[i-1],"BOTTOM",0,0)
end
end
lines[i].text:SetText(text)
if type(value) == "string" then
lines[i].value = value
else
lines[i].value = text
end
if lines[i].value == self.editbox.value and self.getFunc then
lines[i].check:Show()
else
lines[i].check:Hide()
end
lines[i]:Show()
end
totalheight = totalheight + 17 * (columnHeight-1)
for k in pairs(ddsort) do
ddsort[k] = nil
end
elseif type(list) == "function" then
for i, text, value in list() do
if not lines[i] then
lines[i] = self:CreateLine()
if i == 1 then
lines[i]:SetPoint("TOP",self.pullout,"TOP",0,-5)
else
lines[i]:SetPoint("TOP",lines[i-1],"BOTTOM",0,0)
end
end
lines[i].text:SetText(text)
lines[i].value = value
lines[i]:Show()
totalheight = totalheight + 17
end
end
self.pullout:SetHeight(totalheight)
end
 
function WaterfallDropdown.prototype:ClearPullout()
if self.lines then
for i, line in ipairs(self.lines) do
line.text:SetText("")
line:Hide()
end
end
self.pullout:SetHeight(10)
self.pullout:SetWidth(DEFAULT_CONTROL_WIDTH)
end
 
-- For large list, if specified, 0 based row, 1 based column gives a grid.
function WaterfallDropdown.prototype:CreateLine(row, column)
local frame = CreateFrame("Button",nil,self.pullout)
frame.obj = self
 
frame.text = frame:CreateFontString(nil,"OVERLAY","GameFontNormalSmall")
frame.text:SetTextColor(1,1,1)
frame.text:SetJustifyH("LEFT")
 
frame:SetFrameLevel(25)
frame:SetFrameStrata("DIALOG")
 
local highlight = frame:CreateTexture(nil, "OVERLAY")
frame.highlight = highlight
highlight:SetTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight")
highlight:SetBlendMode("ADD")
 
local check = frame:CreateTexture("OVERLAY")
frame.check = check
check:SetWidth(16)
check:SetHeight(16)
check:SetTexture("Interface\\Buttons\\UI-CheckBox-Check")
 
frame:SetScript("OnClick",Dropdown_LineClicked)
frame:SetScript("OnEnter",Dropdown_LineEnter)
frame:SetScript("OnLeave",Dropdown_LineLeave)
return self:UpdateLine(frame, row, column)
end
 
 
function WaterfallDropdown.prototype:UpdateLine(frame, row, column)
if (column) then
frame:SetPoint("TOPLEFT",self.pullout,"TOPLEFT",6 + (column - 1) * DEFAULT_CONTROL_WIDTH,-5 - row*17)
frame:SetPoint("BOTTOMRIGHT",self.pullout,"TOPLEFT",-6 + column * DEFAULT_CONTROL_WIDTH,-5 - (row+1)*17)
else
frame:SetPoint("LEFT",self.pullout,"LEFT",6,0)
frame:SetPoint("RIGHT",self.pullout,"RIGHT",-6,0)
end
 
frame.text:SetPoint("TOPLEFT",frame,"TOPLEFT",16,0)
frame.text:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",0,0)
 
frame.highlight:SetAllPoints(frame)
frame.highlight:Hide()
 
frame.check:SetPoint("LEFT",frame,"LEFT",0,0)
 
return frame
end
 
-----------------------------
-- WaterfallTreeView Class --
-----------------------------
 
local function treeviewOnSizeChanged(this)
this.obj:SizeChanged()
end
local function treeviewOnMouseWheel(this,value)
this.obj:MoveScroll(value)
end
local function treeviewOnScrollValueChanged(this,value)
this.obj:SetScroll(value)
end
local function treeviewSizerOnMouseUp(this)
this:GetParent():StopMovingOrSizing() this.obj.parent:ReAnchorTree()
end
 
function WaterfallTreeView.prototype:init(parent)
WaterfallTreeView.super.prototype.init(self)
--Parent WaterfallFrame Object
self.parent = parent
local frame = CreateFrame("Frame",nil,parent.frame)
frame.obj = self
local scrollframe = CreateFrame("ScrollFrame",nil,frame)
local scrollchild = CreateFrame("Frame",nil,scrollframe)
local scrollbar = CreateFrame("Slider",nil,scrollframe,"UIPanelScrollBarTemplate")
frame:SetBackdrop(ControlBackdrop)
frame:SetBackdropBorderColor(0.6, 0.6, 0.6)
frame:SetBackdropColor(0.1, 0.1, 0.1)
frame:SetScript("OnSizeChanged", treeviewOnSizeChanged)
frame:SetFrameLevel(2)
frame:SetFrameStrata("DIALOG")
frame:SetResizable(true)
 
self.scrollframe = scrollframe
self.scrollchild = scrollchild
self.frame = frame
self.scrollbar = scrollbar
 
scrollframe:SetScrollChild(scrollchild)
scrollframe:SetPoint("TOPLEFT",frame,"TOPLEFT",8,-12)
scrollframe:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",-28,12)
scrollframe:EnableMouseWheel(true)
scrollframe:SetScript("OnMouseWheel", treeviewOnMouseWheel)
scrollframe.obj = self
 
scrollchild:SetPoint("TOPLEFT",scrollframe,"TOPLEFT",0,0)
scrollchild:SetPoint("TOPRIGHT",scrollframe,"TOPRIGHT",0,0)
scrollchild:SetHeight(400)
 
scrollbar:SetPoint("TOPLEFT",scrollframe,"TOPRIGHT",0,-16)
scrollbar:SetPoint("BOTTOMLEFT",scrollframe,"BOTTOMRIGHT",0,16)
scrollbar:SetMinMaxValues(0,1000)
scrollbar:SetValueStep(1)
scrollbar:SetValue(0)
scrollbar:SetWidth(16)
scrollbar:SetScript("OnValueChanged", treeviewOnScrollValueChanged)
self.scrollvalue = 0
scrollchild.lines = {}
self.linetables = {}
self.sections = {}
scrollbar.obj = self
 
local sizer = CreateFrame("Frame",nil,frame)
sizer:SetPoint("TOPRIGHT",frame,"TOPRIGHT",0,0)
sizer:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",0,0)
sizer:SetWidth(7)
sizer:EnableMouse()
sizer:SetScript("OnMouseDown",sizereOnMouseDown)
sizer:SetScript("OnMouseUp", treeviewSizerOnMouseUp)
self.sizer = sizer
sizer.obj = self
 
self:Refresh()
end
 
function WaterfallTreeView.prototype:SetScroll(value)
local frame, child = self.scrollframe, self.scrollchild
local viewheight = frame:GetHeight()
local height = child:GetHeight()
local offset
if viewheight > height then
offset = 0
else
offset = floor((height - viewheight) / 1000.0 * value)
end
child:ClearAllPoints()
child:SetPoint("TOPLEFT",frame,"TOPLEFT",0,offset)
child:SetPoint("TOPRIGHT",frame,"TOPRIGHT",0,offset)
child.offset = offset
 
self.scrollvalue = value
end
 
function WaterfallTreeView.prototype:SizeChanged()
self:FixScroll()
self.parent:ReAnchorTree()
end
 
function WaterfallTreeView.prototype:MoveScroll(value)
local frame, child = self.scrollframe, self.scrollchild
local height, viewheight = frame:GetHeight(), child:GetHeight()
if height > viewheight then
self.scrollbar:Hide()
else
self.scrollbar:Show()
local diff = height - viewheight
local delta = 1
if value < 0 then
delta = -1
end
self.scrollbar:SetValue(math.min(math.max(self.scrollvalue + delta*(1000/(diff/45)),0), 1000))
end
end
 
function WaterfallTreeView.prototype:FixScroll()
if self.noFixScroll then return end
local frame, child = self.scrollframe, self.scrollchild
local height, viewheight = frame:GetHeight(), child:GetHeight()
local offset = self.scrollchild.offset
if not offset then
offset = 0
end
local curvalue = self.scrollbar:GetValue()
 
if viewheight < height then
self.scrollbar:Hide()
self.scrollbar:SetValue(0)
else
self.scrollbar:Show()
local value = (offset / (viewheight - height) * 1000)
if value > 1000 then value = 1000 end
self.scrollbar:SetValue(value)
self:SetScroll(value)
if value < 1000 then
child:ClearAllPoints()
child:SetPoint("TOPLEFT",frame,"TOPLEFT",0,offset)
child:SetPoint("TOPRIGHT",frame,"TOPRIGHT",0,offset)
child.offset = offset
end
end
end
 
function WaterfallTreeView.prototype:ToggleExpand(id)
self.status[id] = not self.status[id]
self:Refresh()
end
 
function WaterfallTreeView.prototype:Refresh(noupdate)
self.noFixScroll = true
self:Clear()
if not self.contents then return end
if self.contentsFunc and not noupdate then
self.parent.controlcount = 0
currentframe = self.parent
self.contentsFunc(self.contents, self.status, getArgs(self, "contentsArg", 1))
currentframe = nil
end
if self.type == "SECTIONS" then
for i, section in ipairs(self.contents) do
if not section.hidden then
self:AddSection(i,section)
end
end
local height = 0
for i, section in ipairs(self.sections) do
height = height + section:GetHeight()
end
self.scrollchild:SetHeight(height+3)
else
local level = 0
local index = 1
for i, line in ipairs(self.contents) do
if not line.hidden then
index = self:AddLine(index,line,level)
end
end
self.scrollchild:SetHeight(index*16)
end
self.noFixScroll = nil
self:FixScroll()
end
 
local function isSectionSelected(section, selected)
if not selected then return end
if section.id == selected then
return true
end
for i,v in ipairs(section.lines) do
if i <= section.numlines then
if v.id == selected then
return true
end
end
end
end
 
function WaterfallTreeView.prototype:Collapse()
for k in pairs(self.status) do
self.status[k] = false
end
end
 
function WaterfallTreeView.prototype:AddSection(index,line)
local sections = self.sections
local id = line.id
if not sections[index] then
sections[index] = getObj(WaterfallTreeSection,self)
local section = sections[index]
section:SetParent(self)
if index == 1 then
section:SetPoint("TOPLEFT",self.scrollchild,"TOPLEFT",0,0)
section:SetPoint("TOPRIGHT",self.scrollchild,"TOPRIGHT",0,0)
else
section:SetPoint("TOPLEFT",sections[index-1],"BOTTOMLEFT",0,0)
section:SetPoint("TOPRIGHT",sections[index-1],"BOTTOMRIGHT",0,0)
end
end
local section = sections[index]
section:SetText(line.text)
section:SetID(line.id)
section:SetTooltipText(line.tooltipTitle,line.tooltipText)
if self.status[id] == nil then
self.status[id] = not not line.isOpen
end
 
local handler = line.handler
local disabled = checkNegatable(line.disabled,handler)
section.disabled = disabled
section:SetColor(self.parent:GetColor())
if line.hasOptions then
section:AddLine(line.text,line.id,line.id == self.selected,disabled,line.tooltipTitle,line.tooltipText)
end
local firstid
for i, item in ipairs(line) do
if not item.hidden then
if not firstid then
firstid = item.id
end
local itemdisabled = checkNegatable(item.disabled,handler)
section:AddLine(item.text,item.id,item.id == self.selected,itemdisabled or disabled,item.tooltipTitle,item.tooltipText)
end
end
if self.selected == section.id and not line.hasOptions then
self:SetSelected(firstid)
return
end
 
if isSectionSelected(section, self.selected) then
self.status[section.id] = true
end
section:SetExpanded(self.status[line.id] and not disabled)
section:Show()
end
 
function WaterfallTreeView.prototype:Clear()
for i, frame in ipairs(self.scrollchild.lines) do
releaseObj(frame)
self.scrollchild.lines[i] = nil
end
for k in pairs(self.linetables) do
self.linetables[k] = nil
end
for i, section in ipairs(self.sections) do
releaseObj(section)
self.sections[i] = nil
end
self.numlines = 0
self.scrollchild:SetHeight(1)
self:FixScroll()
end
 
function WaterfallTreeView.prototype:ClearSelection()
self.selected = nil
end
 
function WaterfallTreeView.prototype:GetSelected()
return self.selected
end
 
function WaterfallTreeView.prototype:SetSelected(lineid, noupdate)
self.selected = lineid
self:Refresh(noupdate)
if not noupdate then
self.parent:ChangePane(lineid)
end
end
 
function WaterfallTreeView.prototype:AddLine(index,line,level,disabled)
local lines = self.scrollchild.lines
local text, hasChildren, isSelected, isOpen
text = line.text or ""
hasChildren = #line > 0
local id = line.id
if self.status[id] == nil then
self.status[id] = not not line.isOpen
end
isOpen = self.status[id] and not disabled
isSelected = self.selected == id
if not lines[index] then
lines[index] = getObj(WaterfallTreeLine)
lines[index]:SetParent(self)
end
local disabled
if type(line.disabled) == "function" then
disabled = line.disabled()
else
disabled = line.disabled
end
lines[index]:Setup(index,id,text,level,hasChildren,isSelected,isOpen,disabled)
lines[index]:SetTooltipText(line.tooltipTitle,line.tooltipText)
lines[index]:SetIcon(line.icon)
self.linetables[index] = line
index = index + 1
if isOpen then
for i, childline in ipairs(line) do
if not childline.hidden then
index = self:AddLine(index,childline,level+1)
end
end
end
 
return index
end
 
function WaterfallTreeView.prototype:SetType(type)
if not type then
self.type = "TREE"
else
self.type = type
end
end
 
function WaterfallTreeView.prototype:SetContent(contents, status, func, ...)
self.contents = contents
self.status = status
self.contentsFunc = func
local i = 1
while self["contentsArg"..i] do
self["contentsArg"..i] = nil
i = i + 1
end
i = 1
local arg = (select(i,...))
while (select(i,...)) ~= nil do
self["contentsArg"..i] = arg
i = i + 1
arg = (select(i,...))
end
self:Refresh()
self:FixScroll()
end
 
function WaterfallTreeView.prototype:CleanUp()
self.contents = nil
self.selected = nil
self.contentsFunc = nil
local i = 1
while self["contentsArg"..i] do
self["contentsArg"..i] = nil
i = i + 1
end
self.selected = nil
self:Refresh()
end
 
--------------------
-- WaterfallTreeLine Class --
--------------------
 
local function TreeLine_OnEnter(this)
if not this.obj.disabled then
this.highlight:SetAlpha(1)
this.highlight:Show()
showGameTooltip(this.obj)
end
end
 
local function TreeLine_OnLeave(this)
if not this.obj.isSelected then
this.highlight:Hide()
else
this.highlight:SetAlpha(0.7)
end
GameTooltip:Hide()
end
 
local function TreeLine_OnClick(this)
if not this.obj.disabled then
this.parent:SetSelected(this.obj.id)
end
end
 
local function TreeLine_OnExpandClick(this)
if not this.obj.disabled then
this.obj.parent:ToggleExpand(this.obj.id)
end
end
 
function WaterfallTreeLine.prototype:init()
WaterfallTreeLine.super.prototype.init(self)
local frame = CreateFrame("Button",nil,nil)
self.frame = frame
local text = frame:CreateFontString(nil,"OVERLAY","GameFontNormal")
 
text:SetJustifyH("LEFT")
text:SetTextColor(1,1,1)
frame:SetHeight(16)
frame:EnableMouse(true)
frame:SetScript("OnEnter",TreeLine_OnEnter)
frame:SetScript("OnLeave",TreeLine_OnLeave)
frame:SetScript("OnClick",TreeLine_OnClick)
frame:SetFrameLevel(10)
frame:SetFrameStrata("DIALOG")
 
local highlight = frame:CreateTexture(nil, "BACKGROUND")
highlight:SetTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight")
highlight:SetBlendMode("ADD")
highlight:SetAllPoints(frame)
highlight:Hide()
 
local expand = CreateFrame("Button",nil,frame)
expand.obj = self
expand:SetNormalTexture("Interface\\Buttons\\UI-PlusButton-Up")
expand:SetPushedTexture("Interface\\Buttons\\UI-PlusButton-Down")
expand:SetDisabledTexture("Interface\\Buttons\\UI-PlusButton-Disabled")
expand:SetHighlightTexture("Interface\\Buttons\\UI-PlusButton-Hilight")
expand:SetScript("OnClick",TreeLine_OnExpandClick)
expand:SetFrameLevel(21)
expand:SetWidth(16)
expand:SetHeight(16)
 
local icon = frame:CreateTexture(nil,"OVERLAY")
icon:SetWidth(16)
icon:SetHeight(16)
icon:SetPoint("LEFT",expand,"RIGHT",1,0)
self.icon = icon
 
 
expand:SetPoint("LEFT",frame,"LEFT",0,0)
text:SetPoint("LEFT",icon,"RIGHT",0,0)
text:SetPoint("RIGHT",frame,"RIGHT",0,0)
 
frame.obj = self
frame.text = text
frame.highlight = highlight
frame.expand = expand
self.expand = expand
self.highlight = highlight
self.text = text
end
 
function WaterfallTreeLine.prototype:UpdateExpandButton()
local expand = self.expand
if self.hasChildren then
if self.isOpen then
expand:SetNormalTexture("Interface\\Buttons\\UI-MinusButton-Up")
expand:SetPushedTexture("Interface\\Buttons\\UI-MinusButton-Down")
expand:SetDisabledTexture("Interface\\Buttons\\UI-MinusButton-Disabled")
expand:Enable()
expand:SetWidth(16)
else
if self.disabled then
expand:Disable()
else
expand:Enable()
end
expand:SetNormalTexture("Interface\\Buttons\\UI-PlusButton-Up")
expand:SetPushedTexture("Interface\\Buttons\\UI-PlusButton-Down")
expand:SetDisabledTexture("Interface\\Buttons\\UI-PlusButton-Disabled")
 
expand:SetWidth(16)
end
else
expand:SetNormalTexture(nil)
expand:SetPushedTexture(nil)
expand:SetDisabledTexture(nil)
expand:Disable()
expand:SetWidth(16)
end
end
 
function WaterfallTreeLine.prototype:CleanUp()
self:ClearAllPoints()
self:Hide()
self:SetText("")
self.disabled = nil
self.handler = nil
self.name = nil
end
 
function WaterfallTreeLine.prototype:Setup(index, id, text, level, hasChildren, isSelected, isOpen, disabled)
self.level = level
self.hasChildren = hasChildren
self.isSelected = isSelected
self.isOpen = isOpen and not disabled
self.index = index
self.id = id
self.disabled = disabled
 
self:UpdateExpandButton()
 
self.expand:SetPoint("LEFT",self.frame,"LEFT",level*8,0)
self:SetText(text)
if disabled then
self.text:SetTextColor(0.5,0.5,0.5)
else
self.text:SetTextColor(1,1,1)
end
self.frame:SetPoint("TOPLEFT",self.parent.scrollchild,"TOPLEFT",0,-(index-1)*16)
self.frame:SetPoint("TOPRIGHT",self.parent.scrollchild,"TOPRIGHT",0,-(index-1)*16)
if self.isSelected then
self.highlight:Show()
self.highlight:SetAlpha(0.7)
else
self.highlight:Hide()
end
self.frame:Show()
end
 
function WaterfallTreeLine.prototype:SetIcon(iconpath)
local icon = self.icon
if iconpath then
icon:SetTexture(iconpath)
icon:SetWidth(16)
else
icon:SetTexture(nil)
icon:SetWidth(1)
end
end
 
function WaterfallTreeLine.prototype:SetText(text)
self.text:SetText(text)
end
 
function WaterfallTreeLine.prototype:SetTooltipText(title,text)
self.tooltipTitle = title
self.tooltipText = text
end
 
function WaterfallTreeLine.prototype:SetParent(parent)
self.parent = parent
self.frame.parent = parent
self.frame:SetParent(parent.scrollchild)
end
 
-----------------------
-- TreeSection Class --
-----------------------
 
local function SectionTitle_OnClick(this)
local self = this.obj
local parent = self.parent
if not self.disabled then
local selected = self.parent.selected
local lines = self.lines
if self.expanded then
--search before changing pane, since it will release and reinit this section
local found
for i = 1, self.numlines do
if lines[i].id == selected then
found = true
end
end
--if expanded close
 
-- if one of our children is selected then clear the pane
if found then
parent.selected = nil
parent:ToggleExpand(self.id)
parent:SetSelected(nil)
else
parent:ToggleExpand(self.id)
end
else
--save before changing pane, since it will release and reinit this section
local newid
if lines[1] then
newid = lines[1].id
end
parent:ToggleExpand(self.id)
if newid and not parent.selected then
parent:SetSelected(newid)
end
end
end
end
 
local function SectionTitle_OnEnter(this)
local self = this.obj
if not self.disabled then
self.hover = true
self:UpdateColor()
end
showGameTooltip(self)
end
 
local function SectionTitle_OnLeave(this)
local self = this.obj
self.hover = false
self:UpdateColor()
GameTooltip:Hide()
end
 
local function SectionItem_OnEnter(this)
if not this.disabled then
this.highlight:SetAlpha(1)
this.highlight:Show()
showGameTooltip(this)
end
end
 
local function SectionItem_OnClick(this)
if not this.disabled then
this.obj.parent:SetSelected(this.id)
end
end
 
local function SectionItem_OnLeave(this)
if not this.isSelected then
this.highlight:Hide()
else
this.highlight:SetAlpha(0.7)
end
GameTooltip:Hide()
end
 
function WaterfallTreeSection.prototype:init(parent)
WaterfallTreeSection.super.prototype.init(self)
 
self.parent = parent
local frame = CreateFrame("Frame",nil,parent.scrollchild)
frame.obj = self
self.frame = frame
frame:SetBackdrop(PaneBackdrop)
frame:SetBackdropBorderColor(0.6, 0.6, 0.6)
frame:SetBackdropColor(0.1, 0.1, 0.1)
frame:SetFrameLevel(10)
frame:SetFrameStrata("DIALOG")
 
local titletext = frame:CreateFontString(nil,"OVERLAY","GameFontNormal")
self.titletext = titletext
titletext:SetPoint("TOPLEFT",frame,"TOPLEFT",0,-5)
titletext:SetPoint("TOPRIGHT",frame,"TOPRIGHT",0,-5)
titletext:SetHeight(16)
titletext:SetText("Section")
titletext:SetTextColor(1,1,1)
 
local arrow = frame:CreateTexture(nil,"OVERLAY")
self.arrow = arrow
arrow:SetPoint("TOPRIGHT",frame,"TOPRIGHT",-6,-4)
arrow:SetWidth(20)
arrow:SetHeight(20)
arrow:SetTexture("Interface\\MiniMap\\ROTATING-MINIMAPARROW")
 
local r,g,b = 1, 0.6, 0
self.colorR = r
self.colorG = g
self.colorB = b
local titlebar = frame:CreateTexture(nil,"BACKGROUND")
self.titlebar = titlebar
local titlebar2 = frame:CreateTexture(nil,"BACKGROUND")
self.titlebar2 = titlebar2
titlebar:SetPoint("TOPLEFT",frame,"TOPLEFT",4,-4)
titlebar:SetPoint("TOPRIGHT",frame,"TOPRIGHT",-4,-4)
titlebar:SetHeight(10)
titlebar2:SetPoint("TOPLEFT",titlebar,"BOTTOMLEFT",0,0)
titlebar2:SetPoint("TOPRIGHT",titlebar,"BOTTOMRIGHT",0,0)
titlebar2:SetHeight(10)
 
titlebar:SetGradientAlpha("VERTICAL",r*0.6,g*0.6,b*0.6,1,r,g,b,1)
titlebar:SetTexture(r,g,b,1)
titlebar2:SetGradientAlpha("VERTICAL",r*0.9,g*0.9,b*0.9,1,r*0.6,g*0.6,b*0.6,1)
titlebar2:SetTexture(r,g,b,1)
 
local title = CreateFrame("Button",nil,frame)
self.title = title
title:EnableMouse()
title.obj = self
title:SetScript("OnClick", SectionTitle_OnClick)
title:SetScript("OnEnter", SectionTitle_OnEnter)
title:SetScript("OnLeave", SectionTitle_OnLeave)
title:SetPoint("TOPLEFT",titlebar,"TOPLEFT",0,0)
title:SetPoint("BOTTOMRIGHT",titlebar2,"BOTTOMRIGHT",0,0)
 
self.lines = {}
self.numlines = 0
 
frame:SetWidth(100)
frame:SetHeight(100)
self.expanded = true
 
 
 
frame:SetPoint("CENTER",UIParent,"CENTER")
end
 
function WaterfallTreeSection.prototype:SetParent(parent)
self.parent = parent
self.frame:SetParent(parent.scrollchild)
end
 
function WaterfallTreeSection.prototype:UpdateColor()
local r,g,b
self.arrow:Show()
if self.disabled then
r,g,b = 0.5,0.5,0.5
self.arrow:Hide()
elseif self.hover then
r,g,b = self.colorR+((1-self.colorR)/2), self.colorG+((1-self.colorG)/2), self.colorB+((1-self.colorB)/2)
else
r,g,b = self.colorR, self.colorG, self.colorB
end
 
local titlebar = self.titlebar
local titlebar2 = self.titlebar2
titlebar:SetGradientAlpha("VERTICAL",r*0.6,g*0.6,b*0.6,1,r,g,b,1)
titlebar:SetTexture(r,g,b,1)
titlebar2:SetGradientAlpha("VERTICAL",r*0.9,g*0.9,b*0.9,1,r*0.6,g*0.6,b*0.6,1)
titlebar2:SetTexture(r,g,b,1)
end
 
function WaterfallTreeSection.prototype:SetColor(r,g,b)
if r and g and b then
self.colorR, self.colorG, self.colorB = r,g,b
end
self:UpdateColor()
end
 
function WaterfallTreeSection.prototype:ToggleExpand()
self:SetExpanded(not self.expanded)
end
 
function WaterfallTreeSection.prototype:SetText(text)
self.titletext:SetText(text)
end
 
 
function WaterfallTreeSection.prototype:SetTooltipText(title,text)
self.tooltipTitle = title
self.tooltipText = text
end
 
function WaterfallTreeSection.prototype:SetID(id)
self.id = id
end
 
function WaterfallTreeSection.prototype:AddLine(text,id,selected,disabled,tooltipTitle,tooltipText)
local numlines = self.numlines + 1
self.numlines = numlines
local lines = self.lines
if not lines[numlines] then
self:CreateLine(numlines)
end
local line = lines[numlines]
 
line.text:SetText(text)
line.id = id
line.disabled = disabled
line.isSelected = selected
line.tooltipTitle = tooltipTitle
line.tooltipText = tooltipText
if selected then
line.highlight:SetAlpha(0.7)
line.highlight:Show()
else
line.highlight:Hide()
end
if disabled then
line.text:SetTextColor(0.5,0.5,0.5)
else
line.text:SetTextColor(1,1,1)
end
line:Show()
self:FixHeight()
end
 
function WaterfallTreeSection.prototype:SetExpanded(expanded)
self.expanded = expanded
local lines = self.lines
if self.expanded then
for i = 1, self.numlines do
lines[i]:Show()
end
self.arrow:SetTexCoord(.18,.85,.15,.82)
else
for i = 1, #lines do
lines[i]:Hide()
end
self.arrow:SetTexCoord(.85,.18,.82,.15)
end
self:FixHeight()
end
 
function WaterfallTreeSection.prototype:FixHeight()
if self.expanded then
self.frame:SetHeight(33 + self.numlines * 18)
else
self.frame:SetHeight(28)
end
end
 
function WaterfallTreeSection.prototype:CreateLine(index)
local frame = self.frame
self.lines[index] = CreateFrame("Button",nil,frame)
local line = self.lines[index]
line.obj = self
 
line:SetScript("OnEnter",SectionItem_OnEnter)
line:SetScript("OnLeave",SectionItem_OnLeave)
line:SetScript("OnClick",SectionItem_OnClick)
 
line:SetPoint("LEFT",frame,"LEFT",5,0)
line:SetPoint("RIGHT",frame,"RIGHT",-5,0)
line:SetHeight(16)
if index == 1 then
line:SetPoint("TOP",frame,"TOP",0,-28)
else
line:SetPoint("TOP",self.lines[index-1],"BOTTOM",0,-2)
end
 
local text = line:CreateFontString(nil,"OVERLAY","GameFontNormal")
line.text = text
text:SetAllPoints(line)
text:SetTextColor(1,1,1)
 
local highlight = line:CreateTexture(nil, "BACKGROUND")
line.highlight = highlight
highlight:SetTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight")
highlight:SetBlendMode("ADD")
highlight:SetAllPoints(line)
highlight:Hide()
end
 
function WaterfallTreeSection.prototype:CleanUp()
for i = 1, #self.lines do
self.lines[i]:Hide()
end
self.numlines = 0
self.frame:Hide()
self.frame:ClearAllPoints()
self.id = nil
self.disabled = nil
self.colorR = 1
self.colorG = 0.6
self.colorB = 0
end
 
local function activate(self, oldLib, oldDeactivate)
if oldLib and oldLib.registry then
self.registry = oldLib.registry
else
self.registry = {}
end
if oldDeactivate then
oldDeactivate(oldLib)
end
end
 
AceLibrary:Register(Waterfall, MAJOR_VERSION, MINOR_VERSION, activate)
Waterfall = AceLibrary("Waterfall-1.0")
trunk/StunWatch/Libs/Waterfall-1.0/embeds.xml New file
0,0 → 1,4
<?xml version="1.0" encoding="utf-8"?>
<Ui xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.blizzard.com/wow/ui/" xsi:schemaLocation="http://www.blizzard.com/wow/ui/
..\FrameXML\UI.xsd">
<!-- Removed by files.wowace.com for no-ext zip. --></Ui>
trunk/StunWatch/Libs/Waterfall-1.0/Changelog-Waterfall-1.0-r76021.txt New file
0,0 → 1,8
------------------------------------------------------------------------
r76021 | toadkiller | 2008-06-04 15:40:32 -0400 (Wed, 04 Jun 2008) | 2 lines
Changed paths:
M /trunk/Waterfall-1.0/Waterfall-1.0/Waterfall-1.0.lua
 
Waterfall-1.0 :
-- Use English name for spell CLASS.
------------------------------------------------------------------------
trunk/StunWatch/Libs/AceEvent-2.0/AceEvent-2.0.lua New file
0,0 → 1,998
--[[
Name: AceEvent-2.0
Revision: $Rev: 66043 $
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: 66043 $"
 
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 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, ...)
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 continue = nil
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
local method = tmp[obj]
local t = type(method)
if t == "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 t == "function" 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
else
AceEvent:error("Cannot trigger event %q. %q's handler, %q, is not a method or function (%s).", event, obj, method, t)
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]
local t = type(method)
if t == "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 t == "function" then
local success, err = pcall(method, ...)
if not success then geterrorhandler()(err:find("%.lua:%d+:") and err or (debugstack():match("(.-: )in.-\n") or "") .. err) end
else
AceEvent:error("Cannot trigger event %q. %q's handler, %q, is not a method or function (%s).", event, obj, method, t)
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
local t = type(event)
if t == "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
elseif t == "string" then
AceEvent:TriggerEvent(event, unpack(v, 1, v.n))
else
AceEvent:error("Cannot trigger event %q, it's not a method or function (%s).", event, t)
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 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" and 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)
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 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()
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" and type(delay) ~= "number" then
AceEvent:argCheck(delay, 3, "string", "function", --[[ so message is right ]] "number")
AceEvent:argCheck(..., 4, "number")
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" and type(delay) ~= "number" then
AceEvent:argCheck(delay, 3, "string", "function", --[[ so message is right ]] "number")
AceEvent:argCheck(..., 4, "number")
else
AceEvent:argCheck(event, 2, "string", "function")
AceEvent:argCheck(delay, 3, "number")
end
 
return ScheduleEvent(self, true, event, delay, ...)
end
 
function AceEvent:CancelScheduledEvent(t)
AceEvent:argCheck(t, 2, "string")
if delayRegistry then
local v = delayRegistry[t]
if v then
if 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)
AceEvent:argCheck(t, 2, "string")
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 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 = {}
t.current = {}
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 = nil
end
else
for _,v in ipairs(event) do
AceEvent.RegisterEvent(self, v, func)
if isUnitBucket and not v:find("^UNIT_") then
isUnitBucket = nil
end
end
end
bucket.unit = isUnitBucket
if not bucketfunc then
bucketfunc = function(bucket)
if bucket.run then
local current = bucket.current
local method = bucket.method
local self = bucket.self
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 = nil
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 = nil
AceEvent.buckets[event][self] = nil
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
 
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
 
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()
 
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
end
registeringFromAceEvent = true
self:RegisterEvent("MEETINGSTONE_CHANGED", func, true)
self:RegisterEvent("CHAT_MSG_CHANNEL_NOTICE", func, true)
 
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
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")
self:RegisterEvent("LOOT_OPENED", function()
SendAddonMessage("LOOT_OPENED", "", "RAID")
end)
inCombat = InCombatLockdown()
registeringFromAceEvent = nil
 
self:activate(oldLib, oldDeactivate)
if oldLib then
oldDeactivate(oldLib)
end
end
 
AceLibrary:Register(AceEvent, MAJOR_VERSION, MINOR_VERSION, activate)
trunk/StunWatch/Libs/AceEvent-2.0/AceEvent-2.0.toc New file
0,0 → 1,12
## Interface: 20400
 
## 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
trunk/StunWatch/Libs/AceConsole-2.0/AceConsole-2.0.lua New file
0,0 → 1,2675
--[[
Name: AceConsole-2.0
Revision: $Rev: 67789 $
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/AceConsole-2.0
SVN: http://svn.wowace.com/wowace/trunk/Ace2/AceConsole-2.0
Description: Mixin to allow for input/output capabilities. This uses the
AceOptions data table format to determine input.
http://www.wowace.com/index.php/AceOptions_data_table
Dependencies: AceLibrary, AceOO-2.0
License: LGPL v2.1
]]
 
local MAJOR_VERSION = "AceConsole-2.0"
local MINOR_VERSION = "$Revision: 67789 $"
 
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
 
-- #AUTODOC_NAMESPACE AceConsole
 
local MAP_ONOFF, USAGE, IS_CURRENTLY_SET_TO, IS_NOW_SET_TO, IS_NOT_A_VALID_OPTION_FOR, IS_NOT_A_VALID_VALUE_FOR, NO_OPTIONS_AVAILABLE, OPTION_HANDLER_NOT_FOUND, OPTION_HANDLER_NOT_VALID, OPTION_IS_DISABLED, KEYBINDING_USAGE, DEFAULT_CONFIRM_MESSAGE
if GetLocale() == "deDE" then
MAP_ONOFF = { [false] = "|cffff0000Aus|r", [true] = "|cff00ff00An|r" }
USAGE = "Benutzung"
IS_CURRENTLY_SET_TO = "|cffffff7f%s|r steht momentan auf |cffffff7f[|r%s|cffffff7f]|r"
IS_NOW_SET_TO = "|cffffff7f%s|r ist nun auf |cffffff7f[|r%s|cffffff7f]|r gesetzt"
IS_NOT_A_VALID_OPTION_FOR = "[|cffffff7f%s|r] ist keine g\195\188ltige Option f\195\188r |cffffff7f%s|r"
IS_NOT_A_VALID_VALUE_FOR = "[|cffffff7f%s|r] ist kein g\195\188ltiger Wert f\195\188r |cffffff7f%s|r"
NO_OPTIONS_AVAILABLE = "Keine Optionen verfügbar"
OPTION_HANDLER_NOT_FOUND = "Optionen handler |cffffff7f%q|r nicht gefunden."
OPTION_HANDLER_NOT_VALID = "Optionen handler nicht g\195\188ltig."
OPTION_IS_DISABLED = "Option |cffffff7f%s|r deaktiviert."
KEYBINDING_USAGE = "<ALT-CTRL-SHIFT-KEY>" -- fix
DEFAULT_CONFIRM_MESSAGE = "Are you sure you want to perform `%s'?" -- fix
elseif GetLocale() == "frFR" then
MAP_ONOFF = { [false] = "|cffff0000Inactif|r", [true] = "|cff00ff00Actif|r" }
USAGE = "Utilisation"
IS_CURRENTLY_SET_TO = "|cffffff7f%s|r est actuellement positionn\195\169 sur |cffffff7f[|r%s|cffffff7f]|r"
IS_NOW_SET_TO = "|cffffff7f%s|r est maintenant positionn\195\169 sur |cffffff7f[|r%s|cffffff7f]|r"
IS_NOT_A_VALID_OPTION_FOR = "[|cffffff7f%s|r] n'est pas une option valide pour |cffffff7f%s|r"
IS_NOT_A_VALID_VALUE_FOR = "[|cffffff7f%s|r] n'est pas une valeur valide pour |cffffff7f%s|r"
NO_OPTIONS_AVAILABLE = "Pas d'options disponibles"
OPTION_HANDLER_NOT_FOUND = "Le gestionnaire d'option |cffffff7f%q|r n'a pas \195\169t\195\169 trouv\195\169."
OPTION_HANDLER_NOT_VALID = "Le gestionnaire d'option n'est pas valide."
OPTION_IS_DISABLED = "L'option |cffffff7f%s|r est d\195\169sactiv\195\169e."
KEYBINDING_USAGE = "<ALT-CTRL-SHIFT-KEY>" -- fix
DEFAULT_CONFIRM_MESSAGE = "Are you sure you want to perform `%s'?" -- fix
elseif GetLocale() == "koKR" then
MAP_ONOFF = { [false] = "|cffff0000끔|r", [true] = "|cff00ff00켬|r" }
USAGE = "사용법"
IS_CURRENTLY_SET_TO = "|cffffff7f%s|r|1은;는; 현재 상태는 |cffffff7f[|r%s|cffffff7f]|r|1으로;로; 설정되어 있습니다."
IS_NOW_SET_TO = "|cffffff7f%s|r|1을;를; |cffffff7f[|r%s|cffffff7f]|r 상태로 변경합니다."
IS_NOT_A_VALID_OPTION_FOR = "[|cffffff7f%s|r]|1은;는; |cffffff7f%s|r에서 사용 불가능한 설정입니다."
IS_NOT_A_VALID_VALUE_FOR = "[|cffffff7f%s|r]|1은;는; |cffffff7f%s|r에서 사용 불가능한 설정 값입니다."
NO_OPTIONS_AVAILABLE = "가능한 설정이 없습니다."
OPTION_HANDLER_NOT_FOUND = "설정 조정 값인 |cffffff7f%q|r|1을;를; 찾지 못했습니다."
OPTION_HANDLER_NOT_VALID = "설정 조정 값이 올바르지 않습니다."
OPTION_IS_DISABLED = "|cffffff7f%s|r 설정은 사용할 수 없습니다."
KEYBINDING_USAGE = "<ALT-CTRL-SHIFT-KEY>"
DEFAULT_CONFIRM_MESSAGE = "정말 당신은 `%s'|1을;를; 하시겠습니까?"
elseif GetLocale() == "zhCN" then
MAP_ONOFF = { [false] = "|cffff0000\229\133\179\233\151\173|r", [true] = "|cff00ff00\229\188\128\229\144\175|r" }
USAGE = "\231\148\168\230\179\149"
IS_CURRENTLY_SET_TO = "|cffffff7f%s|r \229\189\147\229\137\141\232\162\171\232\174\190\231\189\174 |cffffff7f[|r%s|cffffff7f]|r"
IS_NOW_SET_TO = "|cffffff7f%s|r \231\142\176\229\156\168\232\162\171\232\174\190\231\189\174\228\184\186 |cffffff7f[|r%s|cffffff7f]|r"
IS_NOT_A_VALID_OPTION_FOR = "[|cffffff7f%s|r] \228\184\141\230\152\175\228\184\128\228\184\170\230\156\137\230\149\136\231\154\132\233\128\137\233\161\185 \228\184\186 |cffffff7f%s|r"
IS_NOT_A_VALID_VALUE_FOR = "[|cffffff7f%s|r] \228\184\141\230\152\175\228\184\128\228\184\170\230\156\137\230\149\136\229\128\188 \228\184\186 |cffffff7f%s|r"
NO_OPTIONS_AVAILABLE = "\230\178\161\230\156\137\233\128\137\233\161\185\229\143\175\231\148\168"
OPTION_HANDLER_NOT_FOUND = "\233\128\137\233\161\185\229\164\132\231\144\134\231\168\139\229\186\143 |cffffff7f%q|r \230\178\161\230\159\165\230\137\190."
OPTION_HANDLER_NOT_VALID = "\233\128\137\233\161\185\229\164\132\231\144\134\231\168\139\229\186\143 \230\151\160\230\149\136."
OPTION_IS_DISABLED = "\233\128\137\233\161\185 |cffffff7f%s|r \228\184\141\229\174\140\230\149\180."
KEYBINDING_USAGE = "<ALT-CTRL-SHIFT-KEY>" -- fix
DEFAULT_CONFIRM_MESSAGE = "Are you sure you want to perform `%s'?" -- fix
elseif GetLocale() == "zhTW" then
MAP_ONOFF = { [false] = "|cffff0000關閉|r", [true] = "|cff00ff00開啟|r" }
USAGE = "用法"
IS_CURRENTLY_SET_TO = "|cffffff7f%s|r目前的設定為|cffffff7f[|r%s|cffffff7f]|r"
IS_NOW_SET_TO = "|cffffff7f%s|r現在被設定為|cffffff7f[|r%s|cffffff7f]|r"
IS_NOT_A_VALID_OPTION_FOR = "對於|cffffff7f%2$s|r,[|cffffff7f%1$s|r]是一個不符合規定的選項"
IS_NOT_A_VALID_VALUE_FOR = "對於|cffffff7f%2$s|r,[|cffffff7f%1$s|r]是一個不符合規定的數值"
NO_OPTIONS_AVAILABLE = "沒有可用的選項"
OPTION_HANDLER_NOT_FOUND = "找不到|cffffff7f%q|r選項處理器。"
OPTION_HANDLER_NOT_VALID = "選項處理器不符合規定。"
OPTION_IS_DISABLED = "|cffffff7f%s|r已被停用。"
KEYBINDING_USAGE = "<Alt-Ctrl-Shift-鍵>"
DEFAULT_CONFIRM_MESSAGE = "是否執行「%s」?"
elseif GetLocale() == "esES" then
MAP_ONOFF = { [false] = "|cffff0000Desactivado|r", [true] = "|cff00ff00Activado|r" }
USAGE = "Uso"
IS_CURRENTLY_SET_TO = "|cffffff7f%s|r est\195\161 establecido actualmente a |cffffff7f[|r%s|cffffff7f]|r"
IS_NOW_SET_TO = "|cffffff7f%s|r se ha establecido a |cffffff7f[|r%s|cffffff7f]|r"
IS_NOT_A_VALID_OPTION_FOR = "[|cffffff7f%s|r] no es una opci\195\179n valida para |cffffff7f%s|r"
IS_NOT_A_VALID_VALUE_FOR = "[|cffffff7f%s|r] no es un valor v\195\161lido para |cffffff7f%s|r"
NO_OPTIONS_AVAILABLE = "No hay opciones disponibles"
OPTION_HANDLER_NOT_FOUND = "Gestor de opciones |cffffff7f%q|r no encontrado."
OPTION_HANDLER_NOT_VALID = "Gestor de opciones no v\195\161lido."
OPTION_IS_DISABLED = "La opci\195\179n |cffffff7f%s|r est\195\161 desactivada."
KEYBINDING_USAGE = "<ALT-CTRL-SHIFT-KEY>"
DEFAULT_CONFIRM_MESSAGE = "Are you sure you want to perform `%s'?" -- fix
else -- enUS
MAP_ONOFF = { [false] = "|cffff0000Off|r", [true] = "|cff00ff00On|r" }
USAGE = "Usage"
IS_CURRENTLY_SET_TO = "|cffffff7f%s|r is currently set to |cffffff7f[|r%s|cffffff7f]|r"
IS_NOW_SET_TO = "|cffffff7f%s|r is now set to |cffffff7f[|r%s|cffffff7f]|r"
IS_NOT_A_VALID_OPTION_FOR = "[|cffffff7f%s|r] is not a valid option for |cffffff7f%s|r"
IS_NOT_A_VALID_VALUE_FOR = "[|cffffff7f%s|r] is not a valid value for |cffffff7f%s|r"
NO_OPTIONS_AVAILABLE = "No options available"
OPTION_HANDLER_NOT_FOUND = "Option handler |cffffff7f%q|r not found."
OPTION_HANDLER_NOT_VALID = "Option handler not valid."
OPTION_IS_DISABLED = "Option |cffffff7f%s|r is disabled."
KEYBINDING_USAGE = "<ALT-CTRL-SHIFT-KEY>"
DEFAULT_CONFIRM_MESSAGE = "Are you sure you want to perform `%s'?"
end
 
local NONE = NONE or "None"
 
local AceOO = AceLibrary("AceOO-2.0")
local AceEvent
 
local AceConsole = AceOO.Mixin { "Print", "PrintComma", "PrintLiteral", "CustomPrint", "RegisterChatCommand" }
local Dewdrop
 
local _G = getfenv(0)
 
local function print(text, name, r, g, b, frame, delay)
if not text or text:len() == 0 then
text = " "
end
if not name or name == AceConsole then
else
text = "|cffffff78" .. tostring(name) .. ":|r " .. text
end
local last_color
for t in text:gmatch("[^\n]+") do
(frame or DEFAULT_CHAT_FRAME):AddMessage(last_color and "|cff" .. last_color .. t or t, r, g, b, nil, delay or 5)
if not last_color or t:find("|r") or t:find("|c") then
last_color = t:match(".*|c[fF][fF](%x%x%x%x%x%x)[^|]-$")
end
end
return text
end
 
local real_tostring = tostring
 
local function tostring(t)
if type(t) == "table" then
if type(rawget(t, 0)) == "userdata" and type(t.GetObjectType) == "function" then
return ("<%s:%s>"):format(t:GetObjectType(), t:GetName() or "(anon)")
end
end
return real_tostring(t)
end
 
local getkeystring
 
local function isList(t)
local n = #t
for k,v in pairs(t) do
if type(k) ~= "number" then
return false
elseif k < 1 or k > n then
return false
end
end
return true
end
 
local findGlobal = setmetatable({}, {__index=function(self, t)
for k,v in pairs(_G) do
if v == t then
k = tostring(k)
self[v] = k
return k
end
end
self[t] = false
return false
end})
 
local recurse = {}
local timeToEnd
local GetTime = GetTime
local type = type
 
local new, del
do
local cache = setmetatable({},{__mode='k'})
function new()
local t = next(cache)
if t then
cache[t] = nil
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 function ignoreCaseSort(alpha, bravo)
if not alpha or not bravo then
return false
end
return tostring(alpha):lower() < tostring(bravo):lower()
end
 
local function specialSort(alpha, bravo)
if alpha == nil or bravo == nil then
return false
end
local type_alpha, type_bravo = type(alpha), type(bravo)
if type_alpha ~= type_bravo then
return type_alpha < type_bravo
end
if type_alpha == "string" then
return alpha:lower() < bravo:lower()
elseif type_alpha == "number" then
return alpha < bravo
elseif type_alpha == "table" then
return #alpha < #bravo
elseif type_alpha == "boolean" then
return not alpha
else
return false
end
end
 
local function escapeChar(c)
return ("\\%03d"):format(c:byte())
end
 
local function literal_tostring_prime(t, depth)
if type(t) == "string" then
return ("|cff00ff00%q|r"):format((t:gsub("|", "||"))):gsub("[\001-\012\014-\031\128-\255]", escapeChar)
elseif type(t) == "table" then
if t == _G then
return "|cffffea00_G|r"
end
if type(rawget(t, 0)) == "userdata" and type(t.GetObjectType) == "function" then
return ("|cffffea00<%s:%s>|r"):format(t:GetObjectType(), t:GetName() or "(anon)")
end
if next(t) == nil then
local mt = getmetatable(t)
if type(mt) == "table" and type(mt.__raw) == "table" then
t = mt.__raw
end
end
if recurse[t] then
local g = findGlobal[t]
if g then
return ("|cff9f9f9f<Recursion _G[%q]>|r"):format(g)
else
return ("|cff9f9f9f<Recursion %s>|r"):format(real_tostring(t):gsub("|", "||"))
end
elseif GetTime() > timeToEnd then
local g = findGlobal[t]
if g then
return ("|cff9f9f9f<Timeout _G[%q]>|r"):format(g)
else
return ("|cff9f9f9f<Timeout %s>|r"):format(real_tostring(t):gsub("|", "||"))
end
elseif depth >= 2 then
local g = findGlobal[t]
if g then
return ("|cff9f9f9f<_G[%q]>|r"):format(g)
else
return ("|cff9f9f9f<%s>|r"):format(real_tostring(t):gsub("|", "||"))
end
end
recurse[t] = true
if next(t) == nil then
return "{}"
elseif next(t, (next(t))) == nil then
local k, v = next(t)
if k == 1 then
return "{ " .. literal_tostring_prime(v, depth+1) .. " }"
else
return "{ " .. getkeystring(k, depth+1) .. " = " .. literal_tostring_prime(v, depth+1) .. " }"
end
end
local s
local g = findGlobal[t]
if g then
s = ("{ |cff9f9f9f-- _G[%q]|r\n"):format(g)
else
s = "{ |cff9f9f9f-- " .. real_tostring(t):gsub("|", "||") .. "|r\n"
end
if isList(t) then
for i = 1, #t do
s = s .. (" "):rep(depth+1) .. literal_tostring_prime(t[i], depth+1) .. (i == #t and "\n" or ",\n")
end
else
local tmp = new()
for k in pairs(t) do
tmp[#tmp+1] = k
end
table.sort(tmp, specialSort)
for i,k in ipairs(tmp) do
tmp[i] = nil
local v = t[k]
s = s .. (" "):rep(depth+1) .. getkeystring(k, depth+1) .. " = " .. literal_tostring_prime(v, depth+1) .. (tmp[i+1] == nil and "\n" or ",\n")
end
tmp = del(tmp)
end
if g then
s = s .. (" "):rep(depth) .. string.format("} |cff9f9f9f-- _G[%q]|r", g)
else
s = s .. (" "):rep(depth) .. "} |cff9f9f9f-- " .. real_tostring(t):gsub("|", "||")
end
return s
end
if type(t) == "number" then
return "|cffff7fff" .. real_tostring(t) .. "|r"
elseif type(t) == "boolean" then
return "|cffff9100" .. real_tostring(t) .. "|r"
elseif t == nil then
return "|cffff7f7f" .. real_tostring(t) .. "|r"
else
return "|cffffea00" .. real_tostring(t) .. "|r"
end
end
 
function getkeystring(t, depth)
if type(t) == "string" then
if t:find("^[%a_][%a%d_]*$") then
return "|cff7fd5ff" .. t .. "|r"
end
end
return "[" .. literal_tostring_prime(t, depth) .. "]"
end
 
local get_stringed_args
do
local function g(value, ...)
if select('#', ...) == 0 then
return literal_tostring_prime(value, 1)
end
return literal_tostring_prime(value, 1) .. ", " .. g(...)
end
 
local function f(success, ...)
if not success then
return
end
return g(...)
end
 
function get_stringed_args(func, ...)
return f(pcall(func, ...))
end
end
 
local function literal_tostring_frame(t)
local s = ("|cffffea00<%s:%s|r\n"):format(t:GetObjectType(), t:GetName() or "(anon)")
local __index = getmetatable(t).__index
local tmp, tmp2, tmp3 = new(), new(), new()
for k in pairs(t) do
if k ~= 0 then
tmp3[k] = true
tmp2[k] = true
end
end
for k in pairs(__index) do
tmp2[k] = true
end
for k in pairs(tmp2) do
tmp[#tmp+1] = k
tmp2[k] = nil
end
table.sort(tmp, ignoreCaseSort)
local first = true
for i,k in ipairs(tmp) do
local v = t[k]
local good = true
if k == "GetPoint" then
for i = 1, t:GetNumPoints() do
if not first then
s = s .. ",\n"
else
first = false
end
s = s .. " " .. getkeystring(k, 1) .. "(" .. literal_tostring_prime(i, 1) .. ") => " .. get_stringed_args(v, t, i)
end
elseif type(v) == "function" and type(k) == "string" and (k:find("^Is") or k:find("^Get") or k:find("^Can")) then
local q = get_stringed_args(v, t)
if q then
if not first then
s = s .. ",\n"
else
first = false
end
s = s .. " " .. getkeystring(k, 1) .. "() => " .. q
end
elseif type(v) ~= "function" or (type(v) == "function" and type(k) == "string" and tmp3[k]) then
if not first then
s = s .. ",\n"
else
first = false
end
s = s .. " " .. getkeystring(k, 1) .. " = " .. literal_tostring_prime(v, 1)
else
good = false
end
end
tmp, tmp2, tmp3 = del(tmp), del(tmp2), del(tmp3)
s = s .. "\n|cffffea00>|r"
return s
end
 
local function literal_tostring(t, only)
timeToEnd = GetTime() + 0.2
local s
if only and type(t) == "table" and type(rawget(t, 0)) == "userdata" and type(t.GetObjectType) == "function" then
s = literal_tostring_frame(t)
else
s = literal_tostring_prime(t, 0)
end
for k,v in pairs(recurse) do
recurse[k] = nil
end
for k,v in pairs(findGlobal) do
findGlobal[k] = nil
end
return s
end
 
local function tostring_args(a1, ...)
if select('#', ...) < 1 then
return tostring(a1)
end
return tostring(a1), tostring_args(...)
end
 
local function literal_tostring_args(a1, ...)
if select('#', ...) < 1 then
return literal_tostring(a1)
end
return literal_tostring(a1), literal_tostring_args(...)
end
 
function AceConsole:CustomPrint(r, g, b, frame, delay, connector, a1, ...)
if connector == true then
local s
if select('#', ...) == 0 then
s = literal_tostring(a1, true)
else
s = (", "):join(literal_tostring_args(a1, ...))
end
return print(s, self, r, g, b, frame or self.printFrame, delay)
elseif tostring(a1):find("%%") and select('#', ...) >= 1 then
local success, text = pcall(string.format, tostring_args(a1, ...))
if success then
return print(text, self, r, g, b, frame or self.printFrame, delay)
end
end
return print((connector or " "):join(tostring_args(a1, ...)), self, r, g, b, frame or self.printFrame, delay)
end
 
function AceConsole:Print(...)
return AceConsole.CustomPrint(self, nil, nil, nil, nil, nil, " ", ...)
end
 
function AceConsole:PrintComma(...)
return AceConsole.CustomPrint(self, nil, nil, nil, nil, nil, ", ", ...)
end
 
function AceConsole:PrintLiteral(...)
return AceConsole.CustomPrint(self, nil, nil, nil, nil, nil, true, ...)
end
 
local work
local argwork
 
local function findTableLevel(self, options, chat, text, index, passTable)
if not index then
index = 1
if work then
for k,v in pairs(work) do
work[k] = nil
end
for k,v in pairs(argwork) do
argwork[k] = nil
end
else
work = {}
argwork = {}
end
local len = text:len()
local count
repeat
text, count = text:gsub("(|cff%x%x%x%x%x%x|Hitem:%d-:%d-:%d-:%d-|h%[[^%]]-) (.-%]|h|r)", "%1\001%2")
until count == 0
text = text:gsub("(%]|h|r)(|cff%x%x%x%x%x%x|Hitem:%d-:%d-:%d-:%d-|h%[)", "%1 %2")
for token in text:gmatch("([^%s]+)") do
local token = token
local num = tonumber(token)
if num then
token = num
else
token = token:gsub("\001", " ")
end
table.insert(work, token)
end
end
 
local path = chat
for i = 1, index - 1 do
path = path .. " " .. tostring(work[i])
end
 
local passValue = options.passValue or (passTable and work[index-1])
passTable = passTable or options
 
if type(options.args) == "table" then
local disabled, hidden = options.disabled, options.cmdHidden or options.hidden
if hidden then
if type(hidden) == "function" then
hidden = hidden(passValue)
elseif type(hidden) == "string" then
local handler = options.handler or self
local f = hidden
local neg = f:match("^~(.-)$")
if neg then
f = neg
end
if type(handler[f]) ~= "function" then
AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(f)))
end
hidden = handler[f](handler, passValue)
if neg then
hidden = not hidden
end
end
end
if hidden then
disabled = true
elseif disabled then
if type(disabled) == "function" then
disabled = disabled(passValue)
elseif type(disabled) == "string" then
local handler = options.handler or self
local f = disabled
local neg = f:match("^~(.-)$")
if neg then
f = neg
end
if type(handler[f]) ~= "function" then
AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(f)))
end
disabled = handler[f](handler, passValue)
if neg then
disabled = not disabled
end
end
end
if not disabled then
local next = work[index] and tostring(work[index]):lower()
local next_num = tonumber(next)
if next then
for k,v in pairs(options.args) do
local good = false
if tostring(k):gsub("%s", "-"):lower() == next then
good = true
elseif k == next_num then
good = true
elseif type(v.aliases) == "table" then
for _,alias in ipairs(v.aliases) do
if alias:gsub("%s", "-"):lower() == next then
good = true
break
end
end
elseif type(v.aliases) == "string" and v.aliases:gsub("%s", "-"):lower() == next then
good = true
end
if good then
work[index] = k -- revert it back to its original form as supplied in args
if options.pass then
passTable = passTable or options
if options.get and options.set then
passTable = options
end
else
passTable = nil
end
return findTableLevel(options.handler or self, v, chat, text, index + 1, passTable)
end
end
end
end
end
for i = index, #work do
table.insert(argwork, work[i])
end
return options, path, argwork, options.handler or self, passTable, passValue
end
 
local function validateOptionsMethods(self, options, position)
if type(options) ~= "table" then
return "Options must be a table.", position
end
self = options.handler or self
if options.type == "execute" then
if options.func and type(options.func) ~= "string" and type(options.func) ~= "function" then
return "func must be a string or function", position
end
if options.func and type(options.func) == "string" and type(self[options.func]) ~= "function" then
return ("%q is not a proper function"):format(tostring(options.func)), position
end
else
if options.get then
if type(options.get) ~= "string" and type(options.get) ~= "function" then
return "get must be a string or function", position
end
if type(options.get) == "string" then
local f = options.get
if options.type == "toggle" then
f = f:match("^~(.-)$") or f
end
if type(self[f]) ~= "function" then
return ("%q is not a proper function"):format(tostring(f)), position
end
end
end
if options.set 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.set) == "string" and type(self[options.set]) ~= "function" then
return ("%q is not a proper function"):format(tostring(options.set)), position
end
end
if options.validate and type(options.validate) ~= "table" and options.validate ~= "keybinding" then
if type(options.validate) ~= "string" and type(options.validate) ~= "function" then
return "validate must be a string or function", position
end
if type(options.validate) == "string" and type(self[options.validate]) ~= "function" then
return ("%q is not a proper function"):format(tostring(options.validate)), position
end
end
end
if options.disabled and type(options.disabled) == "string" then
local f = options.disabled
f = f:match("^~(.-)$") or f
if type(self[f]) ~= "function" then
return ("%q is not a proper function"):format(tostring(f)), position
end
end
if options.cmdHidden and type(options.cmdHidden) == "string" then
local f = options.cmdHidden
f = f:match("^~(.-)$") or f
if type(self[f]) ~= "function" then
return ("%q is not a proper function"):format(tostring(f)), position
end
end
if options.guiHidden and type(options.guiHidden) == "string" then
local f = options.guiHidden
f = f:match("^~(.-)$") or f
if type(self[f]) ~= "function" then
return ("%q is not a proper function"):format(tostring(f)), position
end
end
if options.hidden and type(options.hidden) == "string" then
local f = options.hidden
f = f:match("^~(.-)$") or f
if type(self[f]) ~= "function" then
return ("%q is not a proper function"):format(tostring(f)), position
end
end
if options.type == "group" and type(options.args) == "table" then
for k,v in pairs(options.args) do
if type(v) == "table" then
local newposition
if position then
newposition = position .. ".args." .. k
else
newposition = "args." .. k
end
local err, pos = validateOptionsMethods(self, v, newposition)
if err then
return err, pos
end
end
end
end
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 ~= "header" then
return '"type" must either be "range", "text", "group", "toggle", "execute", "color", 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 <= #validate', 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
 
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.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
if type(k) == "number" then
return '"args" values must be tables', position and position .. "[" .. k .. "]" or "[" .. k .. "]"
else
return '"args" values must be tables', position and position .. "." .. k or k
end
end
local newposition
if type(k) == "number" then
newposition = position and position .. ".args[" .. k .. "]" or "args[" .. k .. "]"
else
newposition = position and position .. ".args." .. k or "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
end
 
local colorTable
local colorFunc
local colorCancelFunc
 
local function keybindingValidateFunc(text)
if text == nil or text == "NONE" then
return nil
end
text = 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:byte() < 128 or text:len() > 4) and not _G["KEY_" .. text] then
return false
end
local s = text
if shift then
s = "SHIFT-" .. s
end
if ctrl then
s = "CTRL-" .. s
end
if alt then
s = "ALT-" .. s
end
return s
end
AceConsole.keybindingValidateFunc = keybindingValidateFunc
 
local order
 
local mysort_args
local mysort
 
local function icaseSort(alpha, bravo)
if type(alpha) == "number" and type(bravo) == "number" then
return alpha < bravo
end
return tostring(alpha):lower() < tostring(bravo):lower()
end
 
local tmp = {}
local function printUsage(self, handler, realOptions, options, path, args, passValue, quiet, filter)
if filter then
filter = "^" .. filter:gsub("([%(%)%.%*%+%-%[%]%?%^%$%%])", "%%%1")
end
local hidden, disabled = options.cmdHidden or options.hidden, options.disabled
if hidden then
if type(hidden) == "function" then
hidden = hidden(options.passValue)
elseif type(hidden) == "string" then
local f = hidden
local neg = f:match("^~(.-)$")
if neg then
f = neg
end
if type(handler[f]) ~= "function" then
AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(f)))
end
hidden = handler[f](handler, options.passValue)
if neg then
hidden = not hidden
end
end
end
if hidden then
disabled = true
elseif disabled then
if type(disabled) == "function" then
disabled = disabled(options.passValue)
elseif type(disabled) == "string" then
local f = disabled
local neg = f:match("^~(.-)$")
if neg then
f = neg
end
if type(handler[f]) ~= "function" then
AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(f)))
end
disabled = handler[f](handler, options.passValue)
if neg then
disabled = not disabled
end
end
end
local kind = (options.type or "group"):lower()
if disabled then
print(OPTION_IS_DISABLED:format(path), realOptions.cmdName or realOptions.name or self)
elseif kind == "text" then
local var
local multiToggle
for k in pairs(tmp) do
tmp[k] = nil
end
if passTable then
multiToggle = passTable.multiToggle
if not passTable.get then
elseif type(passTable.get) == "function" then
if not multiToggle then
var = passTable.get(passValue)
else
var = tmp
for k,v in pairs(options.validate) do
local val = type(k) ~= "number" and k or v
if passValue == nil then
var[val] = passTable.get(val) or nil
else
var[val] = passTable.get(passValue, val) or nil
end
end
end
else
local handler = passTable.handler or handler
if type(handler[passTable.get]) ~= "function" then
AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(passTable.get)))
end
var = handler[passTable.get](handler, passValue)
if not multiToggle then
var = handler[passTable.get](handler, passValue)
else
var = tmp
for k,v in pairs(options.validate) do
local val = type(k) ~= "number" and k or v
if passValue == nil then
var[val] = handler[passTable.get](handler, val) or nil
else
var[val] = handler[passTable.get](handler, passValue, val) or nil
end
end
end
end
else
multiToggle = options.multiToggle
if not options.get then
elseif type(options.get) == "function" then
if not multiToggle then
var = options.get(passValue)
else
var = tmp
for k,v in pairs(options.validate) do
local val = type(k) ~= "number" and k or v
if passValue == nil then
var[val] = options.get(val) or nil
else
var[val] = options.get(passValue, val) or nil
end
end
end
else
local handler = options.handler or handler
if type(handler[options.get]) ~= "function" then
AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(options.get)))
end
if not multiToggle then
var = handler[options.get](handler, passValue)
else
var = tmp
for k,v in pairs(options.validate) do
local val = type(k) ~= "number" and k or v
if passValue == nil then
var[val] = handler[options.get](handler, val) or nil
else
var[val] = handler[options.get](handler, passValue, val) or nil
end
end
end
end
end
 
local usage
if type(options.validate) == "table" then
if filter then
if not order then
order = {}
end
for k,v in pairs(options.validate) do
if v:find(filter) then
table.insert(order, v)
end
end
table.sort(order, icaseSort)
usage = "{" .. table.concat(order, " || ") .. "}"
for k in pairs(order) do
order[k] = nil
end
else
if not order then
order = {}
end
for k,v in pairs(options.validate) do
table.insert(order, v)
end
table.sort(order, icaseSort)
usage = "{" .. table.concat(order, " || ") .. "}"
for k in pairs(order) do
order[k] = nil
end
end
if multiToggle then
if not next(var) then
var = NONE
else
if not order then
order = {}
end
for k in pairs(var) do
if options.validate[k] then
order[#order+1] = options.validate[k]
else
for _,v in pairs(options.validate) do
if v == k or (type(v) == "string" and type(k) == "string" and v:lower() == k:lower()) then
order[#order+1] = v
break
end
end
end
end
table.sort(order, icaseSort)
var = table.concat(order, ", ")
for k in pairs(order) do
order[k] = nil
end
end
else
var = options.validate[var] or var
end
elseif options.validate == "keybinding" then
usage = KEYBINDING_USAGE
else
usage = options.usage or "<value>"
end
if not quiet then
print(("|cffffff7f%s:|r %s %s"):format(USAGE, path, usage), realOptions.cmdName or realOptions.name or self)
end
if (passTable and passTable.get) or options.get then
print((options.current or IS_CURRENTLY_SET_TO):format(tostring(options.cmdName or options.name), tostring(var or NONE)))
end
elseif kind == "range" then
local var
if passTable then
if type(passTable.get) == "function" then
var = passTable.get(passValue)
else
local handler = passTable.handler or handler
if type(handler[passTable.get]) ~= "function" then
AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(passTable.get)))
end
var = handler[passTable.get](handler, passValue)
end
else
if type(options.get) == "function" then
var = options.get(passValue)
else
local handler = options.handler or handler
if type(handler[options.get]) ~= "function" then
AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(options.get)))
end
var = handler[options.get](handler, passValue)
end
end
 
local usage
local min = options.min or 0
local max = options.max or 1
if options.isPercent then
min, max = min * 100, max * 100
var = tostring(var * 100) .. "%"
end
local bit = "-"
if min < 0 or max < 0 then
bit = " - "
end
usage = ("(%s%s%s)"):format(min, bit, max)
if not quiet then
print(("|cffffff7f%s:|r %s %s"):format(USAGE, path, usage), realOptions.cmdName or realOptions.name or self)
end
print((options.current or IS_CURRENTLY_SET_TO):format(tostring(options.cmdName or options.name), tostring(var or NONE)))
elseif kind == "group" then
local usage
if next(options.args) then
if not order then
order = {}
end
for k,v in pairs(options.args) do
if v.type ~= "header" then
local hidden = v.cmdHidden or v.hidden
if hidden then
if type(hidden) == "function" then
hidden = hidden()
elseif type(hidden) == "string" then
local f = hidden
local neg = f:match("^~(.-)$")
if neg then
f = neg
end
if type(handler[f]) ~= "function" then
AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(f)))
end
hidden = handler[f](handler)
if neg then
hidden = not hidden
end
end
end
if not hidden then
if filter then
if k:find(filter) then
table.insert(order, k)
elseif type(v.aliases) == "table" then
for _,bit in ipairs(v.aliases) do
if bit:find(filter) then
table.insert(order, k)
break
end
end
elseif type(v.aliases) == "string" then
if v.aliases:find(filter) then
table.insert(order, k)
end
end
else
table.insert(order, k)
end
end
end
end
if not mysort then
mysort = function(a, b)
local alpha, bravo = mysort_args[a], mysort_args[b]
local alpha_order = alpha and alpha.order or 100
local bravo_order = bravo and bravo.order or 100
if alpha_order == bravo_order then
return tostring(a):lower() < tostring(b):lower()
else
if alpha_order < 0 then
if bravo_order > 0 then
return false
end
else
if bravo_order < 0 then
return true
end
end
if alpha_order > 0 and bravo_order > 0 then
return tostring(a):lower() < tostring(b):lower()
end
return alpha_order < bravo_order
end
end
end
mysort_args = options.args
table.sort(order, mysort)
mysort_args = nil
if not quiet then
if options == realOptions then
if options.desc then
print(tostring(options.desc), realOptions.cmdName or realOptions.name or self)
print(("|cffffff7f%s:|r %s %s"):format(USAGE, path, "{" .. table.concat(order, " || ") .. "}"))
elseif self.description or self.notes then
print(tostring(self.description or self.notes), realOptions.cmdName or realOptions.name or self)
print(("|cffffff7f%s:|r %s %s"):format(USAGE, path, "{" .. table.concat(order, " || ") .. "}"))
else
print(("|cffffff7f%s:|r %s %s"):format(USAGE, path, "{" .. table.concat(order, " || ") .. "}"), realOptions.cmdName or realOptions.name or self)
end
else
if options.desc then
print(("|cffffff7f%s:|r %s %s"):format(USAGE, path, "{" .. table.concat(order, " || ") .. "}"), realOptions.cmdName or realOptions.name or self)
print(tostring(options.desc))
else
print(("|cffffff7f%s:|r %s %s"):format(USAGE, path, "{" .. table.concat(order, " || ") .. "}"), realOptions.cmdName or realOptions.name or self)
end
end
end
local passTable = options.pass and options or nil
for _,k in ipairs(order) do
local passValue = passTable and k or nil
local real_k = k
local v = options.args[k]
if v then
local v_p = passTable or v
if v.get and v.set then
v_p = v
passValue = nil
end
if v.passValue then
passValue = v.passValue
end
local k = tostring(k):gsub("%s", "-")
local disabled = v.disabled
if disabled then
if type(disabled) == "function" then
disabled = disabled(passValue)
elseif type(disabled) == "string" then
local f = disabled
local neg = f:match("^~(.-)$")
if neg then
f = neg
end
if type(handler[f]) ~= "function" then
AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(f)))
end
disabled = handler[f](handler, passValue)
if neg then
disabled = not disabled
end
end
end
if type(v.aliases) == "table" then
for _,s in ipairs(v.aliases) do
k = k .. " || " .. s:gsub("%s", "-")
end
elseif type(v.aliases) == "string" then
k = k .. " || " .. v.aliases:gsub("%s", "-")
end
if v_p.get then
local a1,a2,a3,a4
local multiToggle = v_p.type == "text" and v_p.multiToggle
for k in pairs(tmp) do
tmp[k] = nil
end
if type(v_p.get) == "function" then
if multiToggle then
a1 = tmp
for k,v in pairs(v.validate) do
local val = type(k) ~= "number" and k or v
if passValue == nil then
a1[val] = v_p.get(val) or nil
else
a1[val] = v_p.get(passValue, val) or nil
end
end
else
a1,a2,a3,a4 = v_p.get(passValue)
end
else
local handler = v_p.handler or handler
local f = v_p.get
local neg
if v.type == "toggle" then
neg = f:match("^~(.-)$")
if neg then
f = neg
end
end
if type(handler[f]) ~= "function" then
AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(f)))
end
if multiToggle then
a1 = tmp
for k,v in pairs(v.validate) do
local val = type(k) ~= "number" and k or v
if passValue == nil then
a1[val] = handler[f](handler, val) or nil
else
a1[val] = handler[f](handler, passValue, val) or nil
end
end
else
a1,a2,a3,a4 = handler[f](handler, passValue)
end
if neg then
a1 = not a1
end
end
local s
if v.type == "color" then
if v.hasAlpha then
if not a1 or not a2 or not a3 or not a4 then
s = NONE
else
s = ("|c%02x%02x%02x%02x%02x%02x%02x%02x|r"):format(a4*255, a1*255, a2*255, a3*255, a4*255, a1*255, a2*255, a3*255)
end
else
if not a1 or not a2 or not a3 then
s = NONE
else
s = ("|cff%02x%02x%02x%02x%02x%02x|r"):format(a1*255, a2*255, a3*255, a1*255, a2*255, a3*255)
end
end
elseif v.type == "toggle" then
if v.map then
s = tostring(v.map[a1 and true or false] or NONE)
else
s = tostring(MAP_ONOFF[a1 and true or false] or NONE)
end
elseif v.type == "range" then
if v.isPercent then
s = tostring(a1 * 100) .. "%"
else
s = tostring(a1)
end
elseif v.type == "text" and type(v.validate) == "table" then
if multiToggle then
if not next(a1) then
s = NONE
else
s = ''
for k in pairs(a1) do
if v.validate[k] then
if s == '' then
s = v.validate[k]
else
s = s .. ', ' .. v.validate[k]
end
else
for _,u in pairs(v.validate) do
if u == k or (type(v) == "string" and type(k) == "string" and v:lower() == k:lower()) then
if s == '' then
s = u
else
s = s .. ', ' .. u
end
break
end
end
end
end
end
else
s = tostring(v.validate[a1] or a1 or NONE)
end
else
s = tostring(a1 or NONE)
end
if disabled then
local s = s:gsub("|cff%x%x%x%x%x%x(.-)|r", "%1")
local desc = (v.desc or NONE):gsub("|cff%x%x%x%x%x%x(.-)|r", "%1")
print(("|cffcfcfcf - %s: [%s] %s|r"):format(k, s, desc))
else
print((" - |cffffff7f%s: [|r%s|cffffff7f]|r %s"):format(k, s, v.desc or NONE))
end
else
if disabled then
local desc = (v.desc or NONE):gsub("|cff%x%x%x%x%x%x(.-)|r", "%1")
print(("|cffcfcfcf - %s: %s"):format(k, desc))
else
print((" - |cffffff7f%s:|r %s"):format(k, v.desc or NONE))
end
end
end
end
for k in pairs(order) do
order[k] = nil
end
else
if options.desc then
print(("|cffffff7f%s:|r %s"):format(USAGE, path), realOptions.cmdName or realOptions.name or self)
print(tostring(options.desc))
elseif options == realOptions and (self.description or self.notes) then
print(tostring(self.description or self.notes), realOptions.cmdName or realOptions.name or self)
print(("|cffffff7f%s:|r %s"):format(USAGE, path))
else
print(("|cffffff7f%s:|r %s"):format(USAGE, path), realOptions.cmdName or realOptions.name or self)
end
print(NO_OPTIONS_AVAILABLE)
end
end
end
 
local function confirmPopup(message, func, ...)
if not StaticPopupDialogs["ACECONSOLE20_CONFIRM_DIALOG"] then
StaticPopupDialogs["ACECONSOLE20_CONFIRM_DIALOG"] = {}
end
local t = StaticPopupDialogs["ACECONSOLE20_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
 
StaticPopup_Show("ACECONSOLE20_CONFIRM_DIALOG")
end
 
local function handlerFunc(self, chat, msg, options)
if not msg then
msg = ""
else
msg = msg:gsub("^%s*(.-)%s*$", "%1")
msg = msg:gsub("%s+", " ")
end
 
local realOptions = options
local options, path, args, handler, passTable, passValue = findTableLevel(self, options, chat, msg)
if options.type == "execute" then
if options.func then
passTable = nil
end
else
if options.get and options.set then
passTable = nil
end
end
passValue = options.passValue or passTable and passValue
 
local hidden, disabled = options.cmdHidden or options.hidden, options.disabled
if hidden then
if type(hidden) == "function" then
hidden = hidden(passValue)
elseif type(hidden) == "string" then
local f = hidden
local neg = f:match("^~(.-)$")
if neg then
f = neg
end
if type(handler[f]) ~= "function" then
AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(f)))
end
hidden = handler[f](handler, passValue)
if neg then
hidden = not hidden
end
end
end
if hidden then
disabled = true
elseif disabled then
if type(disabled) == "function" then
disabled = disabled(passValue)
elseif type(disabled) == "string" then
local f = disabled
local neg = f:match("^~(.-)$")
if neg then
f = neg
end
if type(handler[f]) ~= "function" then
AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(f)))
end
disabled = handler[f](handler, passValue)
if neg then
disabled = not disabled
end
end
end
local _G_this = this
local kind = (options.type or "group"):lower()
local options_p = passTable or options
if disabled then
print(OPTION_IS_DISABLED:format(path), realOptions.cmdName or realOptions.name or self)
elseif kind == "text" then
if #args > 0 then
if (type(options.validate) == "table" and #args > 1) or (type(options.validate) ~= "table" and not options.input) then
local arg = table.concat(args, " ")
for k,v in pairs(args) do
args[k] = nil
end
args[1] = arg
end
if options.validate then
local good
if type(options.validate) == "function" then
good = options.validate(unpack(args))
elseif type(options.validate) == "table" then
local arg = args[1]
arg = tostring(arg):lower()
for k,v in pairs(options.validate) do
if v:lower() == arg then
args[1] = type(k) == "string" and k or v
good = true
break
end
end
if not good and type((next(options.validate))) == "string" then
for k,v in pairs(options.validate) do
if type(k) == "string" and k:lower() == arg then
args[1] = k
good = true
break
end
end
end
elseif options.validate == "keybinding" then
good = keybindingValidateFunc(unpack(args))
if good ~= false then
args[1] = good
end
else
if type(handler[options.validate]) ~= "function" then
AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(options.validate)))
end
good = handler[options.validate](handler, unpack(args))
end
if not good then
local usage
if type(options.validate) == "table" then
if not order then
order = {}
end
for k,v in pairs(options.validate) do
table.insert(order, v)
end
usage = "{" .. table.concat(order, " || ") .. "}"
for k in pairs(order) do
order[k] = nil
end
elseif options.validate == "keybinding" then
usage = KEYBINDING_USAGE
else
usage = options.usage or "<value>"
end
print((options.error or IS_NOT_A_VALID_OPTION_FOR):format(tostring(table.concat(args, " ")), path), realOptions.cmdName or realOptions.name or self)
print(("|cffffff7f%s:|r %s %s"):format(USAGE, path, usage))
return
end
end
 
local var
local multiToggle
for k in pairs(tmp) do
tmp[k] = nil
end
multiToggle = options_p.multiToggle
if not options_p.get then
elseif type(options_p.get) == "function" then
if multiToggle then
var = tmp
for k,v in pairs(options.validate) do
local val = type(k) ~= "number" and k or v
if passValue then
var[val] = options_p.get(passValue, val) or nil
else
var[val] = options_p.get(val) or nil
end
end
else
var = options_p.get(passValue)
end
else
if type(handler[options_p.get]) ~= "function" then
AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(options_p.get)))
end
if multiToggle then
var = tmp
for k,v in pairs(options.validate) do
local val = type(k) ~= "number" and k or v
if passValue then
var[val] = handler[options_p.get](handler, passValue, val) or nil
else
var[val] = handler[options_p.get](handler, val) or nil
end
end
else
var = handler[options_p.get](handler, passValue)
end
end
 
if multiToggle or var ~= args[1] then
if multiToggle then
local current = var[args[1]]
if current == nil and type(args[1]) == "string" then
for k in pairs(var) do
if type(k) == "string" and k:lower() == args[1]:lower() then
current = true
break
end
end
end
args[2] = not current
end
if type(options_p.set) == "function" then
if passValue then
options_p.set(passValue, unpack(args))
else
options_p.set(unpack(args))
end
else
if type(handler[options_p.set]) ~= "function" then
AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(options_p.set)))
end
if passValue then
handler[options_p.set](handler, passValue, unpack(args))
else
handler[options_p.set](handler, unpack(args))
end
end
end
end
 
if #args > 0 then
local var
local multiToggle
for k in pairs(tmp) do
tmp[k] = nil
end
multiToggle = options_p.multiToggle
if not options_p.get then
elseif type(options_p.get) == "function" then
if multiToggle then
var = tmp
for k,v in pairs(options_p.validate) do
local val = type(k) ~= "number" and k or v
if passValue then
var[val] = options_p.get(passValue, val) or nil
else
var[val] = options_p.get(val) or nil
end
end
else
var = options_p.get(passValue)
end
else
if type(handler[options_p.get]) ~= "function" then
AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(options_p.get)))
end
if multiToggle then
var = tmp
for k,v in pairs(options.validate) do
local val = type(k) ~= "number" and k or v
if passValue then
var[val] = handler[options_p.get](handler, passValue, val) or nil
else
var[val] = handler[options_p.get](handler, val) or nil
end
end
else
var = handler[options_p.get](handler, passValue)
end
end
if multiToggle then
if not next(var) then
var = NONE
else
if not order then
order = {}
end
for k in pairs(var) do
if options.validate[k] then
order[#order+1] = options.validate[k]
else
for _,v in pairs(options.validate) do
if v == k or (type(v) == "string" and type(k) == "string" and v:lower() == k:lower()) then
order[#order+1] = v
break
end
end
end
end
table.sort(order, icaseSort)
var = table.concat(order, ", ")
for k in pairs(order) do
order[k] = nil
end
end
elseif type(options.validate) == "table" then
var = options.validate[var] or var
end
if options_p.get then
print((options.message or IS_NOW_SET_TO):format(tostring(options.cmdName or options.name), tostring(var or NONE)), realOptions.cmdName or realOptions.name or self)
end
if var == args[1] then
return
end
else
printUsage(self, handler, realOptions, options, path, args, passValue)
return
end
elseif kind == "execute" then
local confirm = options.confirm
if confirm == true then
confirm = DEFAULT_CONFIRM_MESSAGE:format(options.desc or options.name or UNKNOWN or "Unknown")
end
if type(options_p.func) == "function" then
if confirm then
confirmPopup(confirm, options_p.func, passValue)
else
options_p.func(passValue)
end
else
if type(handler[options_p.func]) ~= "function" then
AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(options_p.func)))
end
if confirm then
confirmPopup(confirm, handler[options_p.func], handler, passValue)
else
handler[options_p.func](handler, passValue)
end
end
elseif kind == "toggle" then
local var
if type(options_p.get) == "function" then
var = options_p.get(passValue)
else
local f = options_p.get
local neg = f:match("^~(.-)$")
if neg then
f = neg
end
if type(handler[f]) ~= "function" then
AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(f)))
end
var = handler[f](handler, passValue)
if neg then
var = not var
end
end
if type(options_p.set) == "function" then
if passValue then
options_p.set(passValue, not var)
else
options_p.set(not var)
end
else
if type(handler[options_p.set]) ~= "function" then
AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(options_p.set)))
end
if passValue then
handler[options_p.set](handler, passValue, not var)
else
handler[options_p.set](handler, not var)
end
end
if type(options_p.get) == "function" then
var = options_p.get(passValue)
else
local f = options_p.get
local neg = f:match("^~(.-)$")
if neg then
f = neg
end
var = handler[f](handler, passValue)
if neg then
var = not var
end
end
 
print((options.message or IS_NOW_SET_TO):format(tostring(options.cmdName or options.name), (options.map or MAP_ONOFF)[var and true or false] or NONE), realOptions.cmdName or realOptions.name or self)
elseif kind == "range" then
local arg
if #args <= 1 then
arg = args[1]
else
arg = table.concat(args, " ")
end
 
if arg then
local min = options.min or 0
local max = options.max or 1
local good = false
if type(arg) == "number" then
if options.isPercent then
arg = arg / 100
end
 
if arg >= min and arg <= max then
good = true
end
 
if good and type(options.step) == "number" and options.step > 0 then
local step = options.step
arg = math.floor((arg - min) / step + 0.5) * step + min
if arg > max then
arg = max
elseif arg < min then
arg = min
end
end
end
if not good then
local usage
local min = options.min or 0
local max = options.max or 1
if options.isPercent then
min, max = min * 100, max * 100
end
local bit = "-"
if min < 0 or max < 0 then
bit = " - "
end
usage = ("(%s%s%s)"):format(min, bit, max)
print((options.error or IS_NOT_A_VALID_VALUE_FOR):format(tostring(arg), path), realOptions.cmdName or realOptions.name or self)
print(("|cffffff7f%s:|r %s %s"):format(USAGE, path, usage))
return
end
 
local var
if type(options_p.get) == "function" then
var = options_p.get(passValue)
else
if type(handler[options_p.get]) ~= "function" then
AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(options_p.get)))
end
var = handler[options_p.get](handler, passValue)
end
 
if var ~= arg then
if type(options_p.set) == "function" then
if passValue then
options_p.set(passValue, arg)
else
options_p.set(arg)
end
else
if type(handler[options_p.set]) ~= "function" then
AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(options_p.set)))
end
if passValue then
handler[options_p.set](handler, passValue, arg)
else
handler[options_p.set](handler, arg)
end
end
end
end
 
if arg then
local var
if type(options_p.get) == "function" then
var = options_p.get(passValue)
else
if type(handler[options_p.get]) ~= "function" then
AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(options_p.get)))
end
var = handler[options_p.get](handler, passValue)
end
 
if var and options.isPercent then
var = tostring(var * 100) .. "%"
end
print((options.message or IS_NOW_SET_TO):format(tostring(options.cmdName or options.name), tostring(var or NONE)), realOptions.cmdName or realOptions.name or self)
if var == arg then
return
end
else
printUsage(self, handler, realOptions, options, path, args, passValue)
return
end
elseif kind == "color" then
if #args > 0 then
local r,g,b,a
if #args == 1 then
local arg = tostring(args[1])
if options.hasAlpha then
if arg:len() == 8 and arg:find("^%x*$") then
r,g,b,a = tonumber(arg:sub(1, 2), 16) / 255, tonumber(arg:sub(3, 4), 16) / 255, tonumber(arg:sub(5, 6), 16) / 255, tonumber(arg:sub(7, 8), 16) / 255
end
else
if arg:len() == 6 and arg:find("^%x*$") then
r,g,b = tonumber(arg:sub(1, 2), 16) / 255, tonumber(arg:sub(3, 4), 16) / 255, tonumber(arg:sub(5, 6), 16) / 255
end
end
elseif #args == 4 and options.hasAlpha then
local a1,a2,a3,a4 = args[1], args[2], args[3], args[4]
if type(a1) == "number" and type(a2) == "number" and type(a3) == "number" and type(a4) == "number" and a1 <= 1 and a2 <= 1 and a3 <= 1 and a4 <= 1 then
r,g,b,a = a1,a2,a3,a4
elseif (type(a1) == "number" or a1:len() == 2) and a1:find("^%x*$") and (type(a2) == "number" or a2:len() == 2) and a2:find("^%x*$") and (type(a3) == "number" or a3:len() == 2) and a3:find("^%x*$") and (type(a4) == "number" or a4:len() == 2) and a4:find("^%x*$") then
r,g,b,a = tonumber(a1, 16) / 255, tonumber(a2, 16) / 255, tonumber(a3, 16) / 255, tonumber(a4, 16) / 255
end
elseif #args == 3 and not options.hasAlpha then
local a1,a2,a3 = args[1], args[2], args[3]
if type(a1) == "number" and type(a2) == "number" and type(a3) == "number" and a1 <= 1 and a2 <= 1 and a3 <= 1 then
r,g,b = a1,a2,a3
elseif (type(a1) == "number" or a1:len() == 2) and a1:find("^%x*$") and (type(a2) == "number" or a2:len() == 2) and a2:find("^%x*$") and (type(a3) == "number" or a3:len() == 2) and a3:find("^%x*$") then
r,g,b = tonumber(a1, 16) / 255, tonumber(a2, 16) / 255, tonumber(a3, 16) / 255
end
end
if not r then
print((options.error or IS_NOT_A_VALID_OPTION_FOR):format(table.concat(args, ' '), path), realOptions.cmdName or realOptions.name or self)
print(("|cffffff7f%s:|r %s {0-1} {0-1} {0-1}%s"):format(USAGE, path, options.hasAlpha and " {0-1}" or ""))
return
end
 
if type(options_p.set) == "function" then
if passValue then
options_p.set(passValue, r,g,b,a)
else
options_p.set(r,g,b,a)
end
else
if type(handler[options_p.set]) ~= "function" then
AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(options_p.set)))
end
if passValue then
handler[options_p.set](handler, passValue, r,g,b,a)
else
handler[options_p.set](handler, r,g,b,a)
end
end
 
local r,g,b,a
if type(options_p.get) == "function" then
r,g,b,a = options_p.get(passValue)
else
if type(handler[options_p.get]) ~= "function" then
AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(options_p.get)))
end
r,g,b,a = handler[options_p.get](handler, passValue)
end
 
local s
if type(r) == "number" and type(g) == "number" and type(b) == "number" then
if options.hasAlpha and type(a) == "number" then
s = ("|c%02x%02x%02x%02x%02x%02x%02x%02x|r"):format(a*255, r*255, g*255, b*255, r*255, g*255, b*255, a*255)
else
s = ("|cff%02x%02x%02x%02x%02x%02x|r"):format(r*255, g*255, b*255, r*255, g*255, b*255)
end
else
s = NONE
end
print((options.message or IS_NOW_SET_TO):format(tostring(options.cmdName or options.name), s), realOptions.cmdName or realOptions.name or self)
else
local r,g,b,a
if type(options_p.get) == "function" then
r,g,b,a = options_p.get(passValue)
else
if type(handler[options_p.get]) ~= "function" then
AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(options_p.get)))
end
r,g,b,a = handler[options_p.get](handler, passValue)
end
 
if not colorTable then
colorTable = {}
local t = colorTable
 
if ColorPickerOkayButton then
local ColorPickerOkayButton_OnClick = ColorPickerOkayButton:GetScript("OnClick")
ColorPickerOkayButton:SetScript("OnClick", function()
if ColorPickerOkayButton_OnClick then
ColorPickerOkayButton_OnClick()
end
if t.active then
ColorPickerFrame.cancelFunc = nil
ColorPickerFrame.func = nil
ColorPickerFrame.opacityFunc = nil
local r,g,b,a
if t.passValue then
if type(t.get) == "function" then
r,g,b,a = t.get(t.passValue)
else
if type(t.handler[t.get]) ~= "function" then
AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(t.get)))
end
r,g,b,a = t.handler[t.get](t.handler, t.passValue)
end
else
if type(t.get) == "function" then
r,g,b,a = t.get()
else
if type(t.handler[t.get]) ~= "function" then
AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(t.get)))
end
r,g,b,a = t.handler[t.get](t.handler)
end
end
if r ~= t.r or g ~= t.g or b ~= t.b or (t.hasAlpha and a ~= t.a) then
local s
if type(r) == "number" and type(g) == "number" and type(b) == "number" then
if t.hasAlpha and type(a) == "number" then
s = ("|c%02x%02x%02x%02x%02x%02x%02x%02x|r"):format(a*255, r*255, g*255, b*255, r*255, g*255, b*255, a*255)
else
s = ("|cff%02x%02x%02x%02x%02x%02x|r"):format(r*255, g*255, b*255, r*255, g*255, b*255)
end
else
s = NONE
end
print(t.message:format(tostring(t.name), s), t.realOptions.cmdName or t.realOptions.name or self)
end
for k,v in pairs(t) do
t[k] = nil
end
end
end)
end
else
for k,v in pairs(colorTable) do
colorTable[k] = nil
end
end
 
if type(r) ~= "number" or type(g) ~= "number" or type(b) ~= "number" then
r,g,b = 1, 1, 1
end
if type(a) ~= "number" then
a = 1
end
local t = colorTable
t.r = r
t.g = g
t.b = b
if hasAlpha then
t.a = a
end
t.realOptions = realOptions
t.hasAlpha = options.hasAlpha
t.handler = handler
t.set = options_p.set
t.get = options_p.get
t.name = options.cmdName or options.name
t.message = options.message or IS_NOW_SET_TO
t.passValue = passValue
t.active = true
 
if not colorFunc then
colorFunc = function()
local r,g,b = ColorPickerFrame:GetColorRGB()
if t.hasAlpha then
local a = 1 - OpacitySliderFrame:GetValue()
if type(t.set) == "function" then
if t.passValue then
t.set(t.passValue, r,g,b,a)
else
t.set(r,g,b,a)
end
else
if type(t.handler[t.set]) ~= "function" then
AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(t.set)))
end
if t.passValue then
t.handler[t.set](t.handler, t.passValue, r,g,b,a)
else
t.handler[t.set](t.handler, r,g,b,a)
end
end
else
if type(t.set) == "function" then
if t.passValue then
t.set(t.passValue, r,g,b)
else
t.set(r,g,b)
end
else
if type(t.handler[t.set]) ~= "function" then
AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(t.set)))
end
if t.passValue then
t.handler[t.set](t.handler, t.passValue, r,g,b)
else
t.handler[t.set](t.handler, r,g,b)
end
end
end
end
end
 
ColorPickerFrame.func = colorFunc
ColorPickerFrame.hasOpacity = options.hasAlpha
if options.hasAlpha then
ColorPickerFrame.opacityFunc = ColorPickerFrame.func
ColorPickerFrame.opacity = 1 - a
end
ColorPickerFrame:SetColorRGB(r,g,b)
 
if not colorCancelFunc then
colorCancelFunc = function()
if t.hasAlpha then
if type(t.set) == "function" then
if t.passValue then
t.set(t.passValue, t.r,t.g,t.b,t.a)
else
t.set(t.r,t.g,t.b,t.a)
end
else
if type(t.handler[t.get]) ~= "function" then
AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(t.get)))
end
if t.passValue then
t.handler[t.set](t.handler, t.passValue, t.r,t.g,t.b,t.a)
else
t.handler[t.set](t.handler, t.r,t.g,t.b,t.a)
end
end
else
if type(t.set) == "function" then
if t.passValue then
t.set(t.passValue, t.r,t.g,t.b)
else
t.set(t.r,t.g,t.b)
end
else
if type(t.handler[t.set]) ~= "function" then
AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(t.set)))
end
if t.passValue then
t.handler[t.set](t.handler, t.passValue, t.r,t.g,t.b)
else
t.handler[t.set](t.handler, t.r,t.g,t.b)
end
end
end
for k,v in pairs(t) do
t[k] = nil
end
ColorPickerFrame.cancelFunc = nil
ColorPickerFrame.func = nil
ColorPickerFrame.opacityFunc = nil
end
end
 
ColorPickerFrame.cancelFunc = colorCancelFunc
 
ShowUIPanel(ColorPickerFrame)
end
return
elseif kind == "group" then
if #args == 0 then
printUsage(self, handler, realOptions, options, path, args, passValue)
else
-- invalid argument
print((options.error or IS_NOT_A_VALID_OPTION_FOR):format(args[1], path), realOptions.cmdName or realOptions.name or self)
end
return
end
this = _G_this
if Dewdrop then
Dewdrop:Refresh()
end
end
 
local external
local tmp
function AceConsole:RegisterChatCommand(...) -- slashCommands, options, name
local slashCommands, options, name
if type((...)) == "string" then
if not tmp then
tmp = {}
else
for i in ipairs(tmp) do
tmp[i] = nil
end
end
for i = 1, select('#', ...)+1 do
local v = select(i, ...)
if type(v) == "string" then
tmp[#tmp+1] = v
else
slashCommands = tmp
options = v
name = select(i+1, ...)
break
end
end
else
slashCommands, options, name = ...
end
if type(slashCommands) ~= "table" and slashCommands ~= false then
AceConsole:error("Bad argument #2 to `RegisterChatCommand' (expected table, got %s)", type(slashCommands))
end
if not slashCommands and type(name) ~= "string" then
AceConsole:error("Bad argument #4 to `RegisterChatCommand' (expected string, got %s)", type(name))
end
if type(options) ~= "table" and type(options) ~= "function" and options ~= nil then
AceConsole:error("Bad argument #3 to `RegisterChatCommand' (expected table, function, or nil, got %s)", type(options))
end
if name then
if type(name) ~= "string" then
AceConsole:error("Bad argument #4 to `RegisterChatCommand' (expected string or nil, got %s)", type(name))
elseif not name:find("^%w+$") or name:upper() ~= name or name:len() == 0 then
AceConsole:error("Argument #4 must be an uppercase, letters-only string with at least 1 character")
end
end
if slashCommands then
if #slashCommands == 0 then
AceConsole:error("Argument #2 to `RegisterChatCommand' must include at least one string")
end
 
for k,v in pairs(slashCommands) do
if type(k) ~= "number" then
AceConsole:error("All keys in argument #2 to `RegisterChatCommand' must be numbers")
end
if type(v) ~= "string" then
AceConsole:error("All values in argument #2 to `RegisterChatCommand' must be strings")
elseif not v:find("^/[A-Za-z][A-Za-z0-9_]*$") then
AceConsole:error("All values in argument #2 to `RegisterChatCommand' must be in the form of \"/word\"")
end
end
end
 
if not options then
options = {
type = 'group',
args = {},
handler = self
}
end
 
if type(options) == "table" then
local err, position = validateOptions(options)
if err then
if position then
AceConsole:error(position .. ": " .. err)
else
AceConsole:error(err)
end
end
 
if not options.handler then
options.handler = self
end
 
if options.handler == self and options.type:lower() == "group" and self.class then
AceConsole:InjectAceOptionsTable(self, options)
end
end
 
local chat
if slashCommands then
chat = slashCommands[1]
else
chat = _G["SLASH_"..name..1]
end
 
local handler
if type(options) == "function" then
handler = options
for k,v in pairs(_G) do
if handler == v then
local k = k
handler = function(msg)
return _G[k](msg)
end
end
end
else
function handler(msg)
handlerFunc(self, chat, msg, options)
end
end
 
if not _G.SlashCmdList then
_G.SlashCmdList = {}
end
 
if not name and type(slashCommands) == "table" and type(slashCommands[1]) == "string" then
name = slashCommands[1]:gsub("%A", ""):upper()
end
 
if not name then
local A = ('A'):byte()
repeat
name = string.char(math.random(26) + A - 1) .. string.char(math.random(26) + A - 1) .. string.char(math.random(26) + A - 1) .. string.char(math.random(26) + A - 1) .. string.char(math.random(26) + A - 1) .. string.char(math.random(26) + A - 1) .. string.char(math.random(26) + A - 1) .. string.char(math.random(26) + A - 1)
until not _G.SlashCmdList[name]
end
 
if slashCommands then
if _G.SlashCmdList[name] then
local i = 0
while true do
i = i + 1
if _G["SLASH_"..name..i] then
_G["SLASH_"..name..i] = nil
else
break
end
end
end
 
local i = 0
for _,command in ipairs(slashCommands) do
i = i + 1
_G["SLASH_"..name..i] = command
if command:lower() ~= command then
i = i + 1
_G["SLASH_"..name..i] = command:lower()
end
end
end
_G.SlashCmdList[name] = handler
if self ~= AceConsole and self.slashCommand == nil then
self.slashCommand = chat
end
 
if not AceEvent and AceLibrary:HasInstance("AceEvent-2.0") then
external(AceConsole, "AceEvent-2.0", AceLibrary("AceEvent-2.0"))
end
if AceEvent then
if not AceConsole.nextAddon then
AceConsole.nextAddon = {}
end
if type(options) == "table" then
AceConsole.nextAddon[self] = options
if not self.playerLogin then
AceConsole:RegisterEvent("PLAYER_LOGIN", "PLAYER_LOGIN", true)
end
end
end
 
AceConsole.registry[name] = options
 
if slashCommands == tmp then
for i in ipairs(tmp) do
tmp[i] = nil
end
end
end
 
function AceConsole:InjectAceOptionsTable(handler, options)
self:argCheck(handler, 2, "table")
self:argCheck(options, 3, "table")
if 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 AceConsole:PLAYER_LOGIN()
self.playerLogin = true
for addon, options in pairs(self.nextAddon) do
local err, position = validateOptionsMethods(addon, options)
if err then
if position then
geterrorhandler()(tostring(addon) .. ": AceConsole: " .. position .. ": " .. err)
else
geterrorhandler()(tostring(addon) .. ": AceConsole: " .. err)
end
end
self.nextAddon[addon] = nil
end
end
 
function AceConsole:TabCompleteInfo(cmdpath)
local cmd = cmdpath:match("(/%S+)")
if not cmd then
return
end
local path = cmdpath:sub(cmd:len() + 2)
for name in pairs(SlashCmdList) do --global
if AceConsole.registry[name] then
local i = 0
while true do
i = i + 1
local scmd = _G["SLASH_"..name..i]
if not scmd then break end
if cmd == scmd then
return name, cmd, path
end
end
end
end
end
 
function external(self, major, instance)
if major == "AceEvent-2.0" then
if not AceEvent then
AceEvent = instance
 
AceEvent:embed(self)
end
elseif major == "AceTab-2.0" then
instance:RegisterTabCompletion("AceConsole", "%/.*", function(t, cmdpath, pos)
local name, cmd, path = self:TabCompleteInfo(cmdpath:sub(1, pos))
 
if not self.registry[name] then
return false
else
local validArgs, _, _, handler = findTableLevel(self, self.registry[name], cmd, path or "")
if validArgs.args then
for arg, v in pairs(validArgs.args) do
local hidden = v.hidden
local handler = v.handler or handler
if hidden then
if type(hidden) == "function" then
hidden = hidden(v.passValue)
elseif type(hidden) == "string" then
local f = hidden
local neg = f:match("^~(.-)$")
if neg then
f = neg
end
if type(handler[f]) ~= "function" then
self:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(f)))
end
hidden = handler[f](handler, v.passValue)
if neg then
hidden = not hidden
end
end
end
local disabled = hidden or v.disabled
if disabled then
if type(disabled) == "function" then
disabled = disabled(v.passValue)
elseif type(disabled) == "string" then
local f = disabled
local neg = f:match("^~(.-)$")
if neg then
f = neg
end
if type(handler[f]) ~= "function" then
self:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(f)))
end
disabled = handler[f](handler, v.passValue)
if neg then
disabled = not disabled
end
end
end
if not hidden and not disabled and v.type ~= "header" then
table.insert(t, (tostring(arg):gsub("%s", "-")))
end
end
end
end
end, function(u, matches, gcs, cmdpath)
local name, cmd, path = self:TabCompleteInfo(cmdpath)
if self.registry[name] then
local validArgs, path2, argwork, handler = findTableLevel(self, self.registry[name], cmd, path)
printUsage(self, validArgs.handler or handler, self.registry[name], validArgs, path2, argwork, argwork[#argwork], not gcs or gcs ~= "", gcs)
end
end)
elseif major == "Dewdrop-2.0" then
Dewdrop = instance
end
end
 
local function activate(self, oldLib, oldDeactivate)
AceConsole = self
 
if oldLib then
self.registry = oldLib.registry
self.nextAddon = oldLib.nextAddon
end
 
if not self.registry then
self.registry = {}
else
for name,options in pairs(self.registry) do
self:RegisterChatCommand(false, options, name)
end
end
 
self:RegisterChatCommand("/reload", "/rl", "/reloadui", ReloadUI, "RELOAD")
self:RegisterChatCommand("/gm", ToggleHelpFrame, "GM")
local t = { "/print", "/echo" }
local _,_,_,enabled,loadable = GetAddOnInfo("DevTools")
if not enabled and not loadable then
table.insert(t, "/dump")
end
self:RegisterChatCommand(t, function(text)
text = text:trim():match("^(.-);*$")
local f, err = loadstring("AceLibrary('AceConsole-2.0'):PrintLiteral(" .. text .. ")")
if not f then
self:Print("|cffff0000Error:|r", err)
else
f()
end
end, "PRINT")
 
self:activate(oldLib, oldDeactivate)
 
if oldDeactivate then
oldDeactivate(oldLib)
end
end
 
AceLibrary:Register(AceConsole, MAJOR_VERSION, MINOR_VERSION, activate, nil, external)
trunk/StunWatch/Libs/AceConsole-2.0/AceConsole-2.0.toc New file
0,0 → 1,12
## Interface: 20400
 
## Title: Lib: AceConsole-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, AceEvent-2.0, AceOO-2.0
 
AceConsole-2.0.lua
trunk/StunWatch/SWcore.lua New file
0,0 → 1,555
---------------------------------------------
-- StunWatch v2.2 BETA2
-- Changes:
----Aceified
----Fixed reapplication of stuns
----Scale feature
----Diminishing Returns
----Selective Stun Tracking
-- TODO:
----Switch to defaults option in slash menu
----Fix twitchy checkboxes in Waterfall config GUI
---------------------------------------------
 
StunWatch = AceLibrary("AceAddon-2.0"):new("AceConsole-2.0", "AceEvent-2.0", "AceDB-2.0")
 
local UnitName = UnitName
local UnitGUID = UnitGUID
local StunWatch = StunWatch
--We'll assign this later
local db
--This, too
local sdb = {}
--For tracking GUIDs so that we can unregister all candybars after a unit dies
local unitDB = {}
--For sap
local sapID
--Frame, identifier, and libs
local StunWatchFrame
local StunWatchName = "StunWatchBars"
local candy = LibStub:GetLibrary("LibCandyBar")
local waterfall = AceLibrary:HasInstance("Waterfall-1.0") and AceLibrary("Waterfall-1.0") or nil
 
--Remove a candybar based on the ID given
function StunWatch:KillTimer(id)
candy:StopCandyBar(id)
candy:UnregisterCandyBar(id)
--Get the spell ID and unit GUID from the ID we were given
local spellName, unitGUID = strsplit(":", id)
--Remove this timer from the database
unitDB[unitGUID][spellName] = nil
end
 
--Remove ALL timers from the specified unit
function StunWatch:KillAllTimers(unitGUID)
--match found, iterate through the table removing all candybars associated with the unit
for k, v in pairs(unitDB[unitGUID]) do
candy:UnregisterCandyBar(v)
unitDB[unitGUID][k] = nil
end
unitDB[unitGUID] = nil
end
 
do
--Check for sap, this way, we can delete any other sap timers if we happen to be out of range for SPELL_AURA_REMOVED
local function sap(id)
if (spellName == StunWatch.db.profile.sdbn[6770]) then
if (sapID) then
if candy:IsCandyBarRegistered(sapID) then
candy:UnregisterCandyBar(sapID)
end
end
sapID = id
end
end
 
function StunWatch:StartTimer(spellName, unitGUID, unitName, duration)
--Create a unique ID based on the spell name/unit GUID
local id = (spellName .. ":" .. unitGUID)
sap(id)
--Check if we have a database on the current unit
if (not unitDB[unitGUID]) then
--if not, create one with the specified spell
unitDB[unitGUID] = { [spellName] = id }
else
--if there is, add the spell to the DB
unitDB[unitGUID][spellName] = id
end
--Create the text of the bar based on the spell and unit names
local text = (spellName .. ": " .. unitName)
local invert = (tostring(db.invert))
candy:RegisterCandyBar(id, duration, text, nil, 1, 1, 0)
candy:SetCandyBarReversed(id, invert)
candy:RegisterCandyBarWithGroup(id, StunWatchName)
candy:SetWidth(id, 160)
candy:SetHeight(id, 10)
candy:SetCandyBarScale(id, db.scale)
candy:StartCandyBar(id, true)
end
 
function StunWatch:RetainDebuff(spellName, unitGUID, unitName, duration, maxDuration)
--This way, we can start a new timer without screwing up existing ones
local id = (spellName .. ":" .. unitGUID)
local invert = (tostring(db.invert))
candy:SetCandyBarReversed(id, invert)
candy:SetCandyBarTime(id, maxDuration)
candy:SetCandyBarTimeLeft(id, duration)
end
end
 
function StunWatch:CreateMainFrame()
--Create and edit the frame which the user uses to move the candybar group
local frame = StunWatchFrame
frame:SetWidth("170")
frame:SetHeight("20")
frame:SetBackdrop({
bgFile = "Interface\\Tooltips\\UI-Tooltip-Background",
tile = true,
tileSize = 16,
edgeSize = 0,
insets = { left = 0, right = 0, top = 0, bottom = 0 },
})
frame:SetMovable(true)
frame:SetClampedToScreen(true)
frame:SetPoint("CENTER", UIParent, "CENTER", 0 ,0)
frame.Text = frame:CreateFontString(nil, "OVERLAY")
frame.Text:SetFontObject("GameFontNormal")
frame.Text:SetWidth(170)
frame.Text:SetHeight(15)
frame.Text:SetPoint("CENTER")
frame.Text:SetText(StunWatchName)
--SetScripts
frame:SetScript("OnMouseDown", function(self, button) if (button == "LeftButton") then self:StartMoving(); end end)
frame:SetScript("OnMouseUp", function(self, button) if (button == "LeftButton") then self:StopMovingOrSizing(); end end)
end
 
function StunWatch:Initialize()
local select = select
local sdb = {
-- Cheap Shot
[1833] = select(1, GetSpellInfo(1833)),
-- Kidney Shot
[408] = select(1, GetSpellInfo(408)),
-- Blind
[2094] = select(1, GetSpellInfo(2094)),
-- Gouge
[38764] = select(1, GetSpellInfo(38764)),
-- Sap
[6770] = select(1, GetSpellInfo(6770)),
-- Expose Armor
[8647] = select(1, GetSpellInfo(8647)),
-- Rupture
[1943] = select(1, GetSpellInfo(1943)),
-- Garrote
[703] = select(1, GetSpellInfo(703)),
-- Mace Stun
[5530] = select(1, GetSpellInfo(5530)),
-- Blade Twisting
[31125] = select(1, GetSpellInfo(31125)),
--Deadly Throw
[26679] = select(1, GetSpellInfo(26679)),
--Imp. Kick
[18425] = select(1, GetSpellInfo(18425)),
--Riposte
[14251] = select(1, GetSpellInfo(14251)),
}
--set up our database
self.defaults = {
lock = true,
grow = true,
invert = 1,
target = true,
focus = true,
scale = 1,
sdbn = {},
}
--Set up spell name database, for checking against spells in the UNIT_AURA event, and various other places
for k,v in pairs(sdb) do
self.defaults.sdbn[v] = 1
end
self:RegisterDB("StunWatchDB")
self:RegisterDefaults("profile", self.defaults)
db = self.db.profile
 
local frame = StunWatchFrame
--set up our options
self.opts = {
type ='group',
name = "StunWatch",
args = {
lock = {
type = 'toggle',
name = "Lock",
desc = "Lock StunWatch",
order = 2,
get = function() return db.lock end,
set = function(v)
db.lock = v
if (not db.lock) then
frame:EnableMouse(true)
frame:Show()
else
frame:EnableMouse(false)
frame:Hide()
end
 
end,
},
invert = {
type = 'toggle',
name = "Invert",
desc = "Invert filling of bars",
order = 3,
get = function() return db.invert end,
set = function(v)
if (not v) then
db.invert = false
else
db.invert = "1"
end
end,
},
grow = {
type = 'toggle',
name = "Grow Up",
desc = "Toggle Growth up and down",
order = 4,
get = function() return db.grow end,
set = function(v)
if (v) then
candy:SetCandyBarGroupGrowth(StunWatchName, true) -- bars grow upwards
else
candy:SetCandyBarGroupGrowth(StunWatchName, false) --bars grow downwards
end
 
db.grow = v
end,
},
target = {
type = 'toggle',
name = "Target (recommended)",
desc = "Track Stuns/Debuffs on your target",
order = 5,
get = function() return db.target end,
set = function(v)
if (not v) then
db.target = false
self:UnregisterEvent("PLAYER_TARGET_CHANGED", "TargetFocusUpdate")
else
db.target = true
self:RegisterEvent("PLAYER_TARGET_CHANGED", "TargetFocusUpdate")
end
end,
},
focus = {
type = 'toggle',
name = "Focus",
desc = "Track Stuns/Debuffs on your focus target",
order = 6,
get = function() return db.focus end,
set = function(v)
if (not v) then
db.focus = false
self:UnregisterEvent("PLAYER_FOCUS_CHANGED", "TargetFocusUpdate")
else
db.focus = true
self:RegisterEvent("PLAYER_FOCUS_CHANGED", "TargetFocusUpdate")
end
end,
},
scale = {
type = 'range',
name = "Scale",
desc = "Scale of StunWatch bars",
order = 7,
get = function() return db.scale end,
set = function(v)
db.scale = v
frame:SetScale(v)
end,
min = .5,
max = 1.5,
step = .1,
},
debuffFilter = {
type = 'group',
name = "Debuffs",
desc = "Select which debuffs to track",
order = 8,
args = {
cheapshot = {
type = 'toggle',
name = sdb[1833],
desc = "Toggle " .. sdb[1833],
order = 9,
get = function() return db.sdbn[sdb[1833]] end,
set = function(v)
if (not v) then
db.sdbn[sdb[1833]] = false
else
db.sdbn[sdb[1833]] = 1
end
end,
},
kidneyshot = {
type = 'toggle',
name = sdb[408],
desc = "Toggle " .. sdb[408],
order = 10,
get = function() return db.sdbn[sdb[408]] end,
set = function(v)
if (not v) then
db.sdbn[sdb[408]] = false
else
db.sdbn[sdb[408]] = 1
end
end,
},
blind = {
type = 'toggle',
name = sdb[2094],
desc = "Toggle " .. sdb[2094],
order = 11,
get = function() return db.sdbn[sdb[2094]] end,
set = function(v)
if (not v) then
db.sdbn[sdb[2094]] = false
else
db.sdbn[sdb[2094]] = 1
end
end,
},
gouge = {
type = 'toggle',
name = sdb[38764],
desc = "Toggle " .. sdb[38764],
order = 12,
get = function() return db.sdbn[sdb[38764]] end,
set = function(v)
if (not v) then
db.sdbn[sdb[38764]] = false
else
db.sdbn[sdb[38764]] = 1
end
end,
},
sap = {
type = 'toggle',
name = sdb[6770],
desc = "Toggle " .. sdb[6770],
order = 13,
get = function() return db.sdbn[sdb[6770]] end,
set = function(v)
if (not v) then
db.sdbn[sdb[6770]] = false
else
db.sdbn[sdb[6770]] = 1
end
end,
},
exposearmor = {
type = 'toggle',
name = sdb[8647],
desc = "Toggle " .. sdb[8647],
order = 14,
get = function() return db.sdbn[sdb[8647]] end,
set = function(v)
if (not v) then
db.sdbn[sdb[8647]] = false
else
db.sdbn[sdb[8647]] = 1
end
end,
},
rupture = {
type = 'toggle',
name = sdb[1943],
desc = "Toggle " .. sdb[1943],
order = 15,
get = function() return db.sdbn[sdb[1943]] end,
set = function(v)
if (not v) then
db.sdbn[sdb[1943]] = false
else
db.sdbn[sdb[1943]] = 1
end
end,
},
garrote = {
type = 'toggle',
name = sdb[703],
desc = "Toggle " .. sdb[703],
order = 16,
get = function() return db.sdbn[sdb[703]] end,
set = function(v)
if (not v) then
db.sdbn[sdb[703]] = false
else
db.sdbn[sdb[703]] = 1
end
end,
},
macestun = {
type = 'toggle',
name = sdb[5530],
desc = "Toggle " .. sdb[5530],
order = 17,
get = function() return db.sdbn[sdb[5530]] end,
set = function(v)
if (not v) then
db.sdbn[sdb[5530]] = false
else
db.sdbn[sdb[5530]] = 1
end
end,
},
bladetwisting = {
type = 'toggle',
name = sdb[31125],
desc = "Toggle " .. sdb[31125],
order = 18,
get = function() return db.sdbn[sdb[31125]] end,
set = function(v)
if (not v) then
db.sdbn[sdb[31125]] = false
else
db.sdbn[sdb[31125]] = 1
end
end,
},
deadlythrow = {
type = 'toggle',
name = sdb[26679],
desc = "Toggle " .. sdb[26679],
order = 19,
get = function() return db.sdbn[sdb[26679]] end,
set = function(v)
if (not v) then
db.sdbn[sdb[26679]] = false
else
db.sdbn[sdb[26679]] = 1
end
end,
},
impkick = {
type = 'toggle',
name = sdb[18425],
desc = "Toggle " .. sdb[18425],
order = 20,
get = function() return db.sdbn[sdb[18425]] end,
set = function(v)
if (not v) then
db.sdbn[sdb[18425]] = false
else
db.sdbn[sdb[18425]] = 1
end
end,
},
riposte = {
type = 'toggle',
name = sdb[14251],
desc = "Toggle " .. sdb[14251],
order = 21,
get = function() return db.sdbn[sdb[14251]] end,
set = function(v)
if (not v) then
db.sdbn[sdb[14251]] = false
else
db.sdbn[sdb[14251]] = 1
end
end,
},
},
},
},
}
--Set up our slash command, depending upon waterfall's availibility
if (waterfall) then
waterfall:Register("StunWatch", "aceOptions", self.opts, "colorR", 0.2, "colorG", 0.3, "colorB", 0.6)
self:RegisterChatCommand("/sw", "/stunwatch", function() waterfall:Open("StunWatch") end)
else
self:RegisterChatCommand("/sw", "/stunwatch", self.opts)
end
 
if (not db.lock) then
frame:EnableMouse(true)
frame:Show()
else
frame:Hide()
frame:EnableMouse(false)
end
local name = StunWatchName
if (db.grow) then
candy:SetCandyBarGroupGrowth(name, true) --bars grow upwards /please test
else
candy:SetCandyBarGroupGrowth(name, false) --bars grow downwards /please test
end
if (not db.lock) then
frame:Show()
else
frame:Hide()
end
candy:RegisterCandyBarGroup(name)
candy:SetCandyBarGroupPoint(name, "BOTTOM", frame, "BOTTOM", 0, 0)
end
 
 
function StunWatch:UpdateBars(token)
for i=1,40 do
spellName, _, _, _, _, maxDuration, timeLeft = UnitDebuff(token, i)
if (not spellName) then
return
end
--Check if it's a spell we want to track
if (db.sdbn[spellName] and timeLeft and timeLeft > 0) then
--We need these variables to check for and start timers
local GUID, name = UnitGUID(token), UnitName(token)
if (not candy:IsCandyBarRegistered(spellName .. ":" .. GUID)) then
self:StartTimer(spellName, GUID, name, timeLeft)
else
self:RetainDebuff(spellName, GUID, name, timeLeft, maxDuration)
end
end
end
end
 
function StunWatch:UNIT_AURA(token)
if (db[token]) then
self:UpdateBars(token)
end
end
 
function StunWatch:COMBAT_LOG_EVENT_UNFILTERED(...)
--Retrieve the specific combat event
local _, cmbEvent = select(1, ...)
if (cmbEvent == "UNIT_DIED") then
--Check if there are any timers for the unit that died, if there are, clear them all
local dstGUID = select(6, ...)
if (unitDB[dstGUID]) then
self:KillAllTimers(dstGUID)
end
elseif ((cmbEvent == "SPELL_AURA_REMOVED") or (cmbEvent == "SPELL_AURA_DISPELLED")) then
--Clear any debuff timers associated with the removed debuff
local dstGUID, _, _, _, spellName = select(6, ...)
local id = (spellName .. ":" .. dstGUID)
if candy:IsCandyBarRegistered(id) then
self:KillTimer(id)
end
end
end
 
function StunWatch:TargetFocusUpdate()
self:UpdateBars("target")
self:UpdateBars("focus")
end
 
function StunWatch:OnInitialize()
self:CreateMainFrame()
self:Initialize()
self:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
self:RegisterEvent("PLAYER_TARGET_CHANGED", "TargetFocusUpdate")
self:RegisterEvent("PLAYER_FOCUS_CHANGED", "TargetFocusUpdate")
self:RegisterEvent("UNIT_AURA")
end
 
StunWatchFrame = CreateFrame("Frame", "StunWatchMainFrame", UIParent)
\ No newline at end of file