diff --git a/Libs/AceAddon-2.0/AceAddon-2.0.lua b/Libs/AceAddon-2.0/AceAddon-2.0.lua index a1f4810..b770a71 100644 --- a/Libs/AceAddon-2.0/AceAddon-2.0.lua +++ b/Libs/AceAddon-2.0/AceAddon-2.0.lua @@ -1,1450 +1,1450 @@ ---[[ -Name: AceAddon-2.0 -Revision: $Rev: 1094 $ -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 = 90000 + tonumber(("$Revision: 1094 $"):match("(%d+)")) - --- 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", - } -elseif GetLocale() == "ruRU" then - STANDBY = "|cffff5050(в режиме ожидания)|r" - - TITLE = "Название" - NOTES = "Описание" - VERSION = "Версия" - AUTHOR = "Автор" - DATE = "Дата" - CATEGORY = "Категория" - EMAIL = "E-mail" - WEBSITE = "Сайт" - CREDITS = "Титры" - LICENSE = "Лицензия" - - ABOUT = "Об аддоне" - PRINT_ADDON_INFO = "Показать информацию об аддоне." - DONATE = "Пожертвовать" - DONATE_DESC = "Отблагодарить автора за разработку аддона." - HOWTO_DONATE_WINDOWS = "Для выделения всей ссылки нажмите Ctrl-A, потом для её копирования Ctrl-C, чтобы свернуть игру - Alt-Tab, откройте ваш браузер и вставьте ссылку в адресную строку - Ctrl-V" - HOWTO_DONATE_MAC = "Для выделения всей ссылки нажмите Cmd-A, потом для её копирования Ctrl-C, чтобы свернуть игру - Cmd-Tab, откройте ваш браузер и вставьте ссылку в адресную строку Cmd-V" - - 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"] = "Фреймы персонажей", - } -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) +--[[ +Name: AceAddon-2.0 +Revision: $Rev: 1094 $ +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 = 90000 + tonumber(("$Revision: 1094 $"):match("(%d+)")) + +-- 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", + } +elseif GetLocale() == "ruRU" then + STANDBY = "|cffff5050(в режиме ожидания)|r" + + TITLE = "Название" + NOTES = "Описание" + VERSION = "Версия" + AUTHOR = "Автор" + DATE = "Дата" + CATEGORY = "Категория" + EMAIL = "E-mail" + WEBSITE = "Сайт" + CREDITS = "Титры" + LICENSE = "Лицензия" + + ABOUT = "Об аддоне" + PRINT_ADDON_INFO = "Показать информацию об аддоне." + DONATE = "Пожертвовать" + DONATE_DESC = "Отблагодарить автора за разработку аддона." + HOWTO_DONATE_WINDOWS = "Для выделения всей ссылки нажмите Ctrl-A, потом для её копирования Ctrl-C, чтобы свернуть игру - Alt-Tab, откройте ваш браузер и вставьте ссылку в адресную строку - Ctrl-V" + HOWTO_DONATE_MAC = "Для выделения всей ссылки нажмите Cmd-A, потом для её копирования Ctrl-C, чтобы свернуть игру - Cmd-Tab, откройте ваш браузер и вставьте ссылку в адресную строку Cmd-V" + + 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"] = "Фреймы персонажей", + } +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) diff --git a/Libs/AceAddon-2.0/AceAddon-2.0.toc b/Libs/AceAddon-2.0/AceAddon-2.0.toc index f2a25c7..76061ce 100644 --- a/Libs/AceAddon-2.0/AceAddon-2.0.toc +++ b/Libs/AceAddon-2.0/AceAddon-2.0.toc @@ -1,16 +1,16 @@ -## Interface: 30000 -## X-Curse-Packaged-Version: r1094 -## X-Curse-Project-Name: Ace2 -## X-Curse-Project-ID: ace2 -## X-Curse-Repository-ID: wow/ace2/mainline - -## 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 +## Interface: 30000 +## X-Curse-Packaged-Version: r1094 +## X-Curse-Project-Name: Ace2 +## X-Curse-Project-ID: ace2 +## X-Curse-Repository-ID: wow/ace2/mainline + +## 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 diff --git a/Libs/AceEvent-2.0/AceEvent-2.0.lua b/Libs/AceEvent-2.0/AceEvent-2.0.lua index 3f31770..8d0e2a2 100644 --- a/Libs/AceEvent-2.0/AceEvent-2.0.lua +++ b/Libs/AceEvent-2.0/AceEvent-2.0.lua @@ -1,998 +1,998 @@ ---[[ -Name: AceEvent-2.0 -Revision: $Rev: 1091 $ -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/wowace/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 = 90000 + tonumber(("$Revision: 1091 $"):match("(%d+)")) - -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 cont = 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 - cont = true - end - end - if not cont 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) +--[[ +Name: AceEvent-2.0 +Revision: $Rev: 1091 $ +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/wowace/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 = 90000 + tonumber(("$Revision: 1091 $"):match("(%d+)")) + +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 cont = 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 + cont = true + end + end + if not cont 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) diff --git a/Libs/AceEvent-2.0/AceEvent-2.0.toc b/Libs/AceEvent-2.0/AceEvent-2.0.toc index 8a39b37..89f9e71 100644 --- a/Libs/AceEvent-2.0/AceEvent-2.0.toc +++ b/Libs/AceEvent-2.0/AceEvent-2.0.toc @@ -1,16 +1,16 @@ -## Interface: 30000 -## X-Curse-Packaged-Version: r1094 -## X-Curse-Project-Name: Ace2 -## X-Curse-Project-ID: ace2 -## X-Curse-Repository-ID: wow/ace2/mainline - -## 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 +## Interface: 30000 +## X-Curse-Packaged-Version: r1094 +## X-Curse-Project-Name: Ace2 +## X-Curse-Project-ID: ace2 +## X-Curse-Repository-ID: wow/ace2/mainline + +## 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 diff --git a/Libs/AceHook-2.1/AceHook-2.1.lua b/Libs/AceHook-2.1/AceHook-2.1.lua index 87f2420..4c5a38b 100644 --- a/Libs/AceHook-2.1/AceHook-2.1.lua +++ b/Libs/AceHook-2.1/AceHook-2.1.lua @@ -1,554 +1,554 @@ ---[[ -Name: AceHook-2.1 -Revision: $Rev: 1091 $ -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/AceHook-2.1 -SVN: http://svn.wowace.com/wowace/trunk/Ace2/AceHook-2.1 -Description: Mixin to allow for safe hooking of functions, methods, and scripts. -Dependencies: AceLibrary, AceOO-2.0 -License: LGPL v2.1 -]] - -local MAJOR_VERSION = "AceHook-2.1" -local MINOR_VERSION = 90000 + tonumber(("$Revision: 1091 $"):match("(%d+)")) - --- 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 - ---[[--------------------------------------------------------------------------------- - Create the library object -----------------------------------------------------------------------------------]] - -local AceOO = AceLibrary:GetInstance("AceOO-2.0") -local AceHook = AceOO.Mixin { - "Hook", - "HookScript", - "SecureHook", - "SecureHookScript", - "Unhook", - "UnhookAll", - "HookReport", - "IsHooked", - } - ---[[--------------------------------------------------------------------------------- - Library Definitions -----------------------------------------------------------------------------------]] - -local protectedScripts = { - OnClick = true, -} - -local _G = getfenv(0) - -local handlers, scripts, actives, registry, onceSecure - ---[[--------------------------------------------------------------------------------- - Private definitions (Not exposed) -----------------------------------------------------------------------------------]] - -local new, del -do - local list = setmetatable({}, {__mode = "k"}) - function new() - local t = next(list) - if not t then - return {} - end - list[t] = nil - return t - end - - function del(t) - setmetatable(t, nil) - for k in pairs(t) do - t[k] = nil - end - list[t] = true - end -end - -local function createFunctionHook(self, func, handler, orig, secure) - if not secure then - if type(handler) == "string" then - -- The handler is a method, need to self it - local uid - uid = function(...) - if actives[uid] then - return self[handler](self, ...) - else - return orig(...) - end - end - return uid - else - -- The handler is a function, just call it - local uid - uid = function(...) - if actives[uid] then - return handler(...) - else - return orig(...) - end - end - return uid - end - else - -- secure hooks don't call the original method - if type(handler) == "string" then - -- The handler is a method, need to self it - local uid - uid = function(...) - if actives[uid] then - return self[handler](self, ...) - end - end - return uid - else - -- The handler is a function, just call it - local uid - uid = function(...) - if actives[uid] then - return handler(...) - end - end - return uid - end - end -end - -local function createMethodHook(self, object, method, handler, orig, secure) - if not secure then - if type(handler) == "string" then - local uid - uid = function(...) - if actives[uid] then - return self[handler](self, ...) - else - return orig(...) - end - end - return uid - else - -- The handler is a function, just call it - local uid - uid = function(...) - if actives[uid] then - return handler(...) - else - return orig(...) - end - end - return uid - end - else - -- secure hooks don't call the original method - if type(handler) == "string" then - local uid - uid = function(...) - if actives[uid] then - return self[handler](self, ...) - end - end - return uid - else - -- The handler is a function, just call it - local uid - uid = function(...) - if actives[uid] then - return handler(...) - end - end - return uid - end - end -end - -local function hookFunction(self, func, handler, secure) - local orig = _G[func] - - if not orig or type(orig) ~= "function" then - AceHook:error("Attempt to hook a non-existant function %q", func) - end - - if not handler then - handler = func - end - - local uid = registry[self][func] - if uid then - if actives[uid] then - -- We have an active hook from this source. Don't multi-hook - AceHook:error("%q already has an active hook from this source.", func) - end - - if handlers[uid] == handler then - -- The hook is inactive, so reactivate it - actives[uid] = true - return - else - self.hooks[func] = nil - registry[self][func] = nil - handlers[uid] = nil - uid = nil - end - end - - if type(handler) == "string" then - if type(self[handler]) ~= "function" then - AceHook:error("Could not find the the handler %q when hooking function %q", handler, func) - end - elseif type(handler) ~= "function" then - AceHook:error("Could not find the handler you supplied when hooking %q", func) - end - - uid = createFunctionHook(self, func, handler, orig, secure) - registry[self][func] = uid - actives[uid] = true - handlers[uid] = handler - - if not secure then - _G[func] = uid - self.hooks[func] = orig - else - hooksecurefunc(func, uid) - end -end - -local function unhookFunction(self, func) - if not registry[self][func] then - AceHook:error("Tried to unhook %q which is not currently hooked.", func) - end - - local uid = registry[self][func] - - if actives[uid] then - -- See if we own the global function - if self.hooks[func] and _G[func] == uid then - _G[func] = self.hooks[func] - self.hooks[func] = nil - registry[self][func] = nil - handlers[uid] = nil - actives[uid] = nil - -- Magically all-done - else - actives[uid] = nil - end - end -end - -local donothing = function() end - -local function hookMethod(self, obj, method, handler, script, secure) - if not handler then - handler = method - end - - if not obj or type(obj) ~= "table" then - AceHook:error("The object you supplied could not be found, or isn't a table.") - end - - local uid = registry[self][obj] and registry[self][obj][method] - if uid then - if actives[uid] then - -- We have an active hook from this source. Don't multi-hook - AceHook:error("%q already has an active hook from this source.", method) - end - - if handlers[uid] == handler then - -- The hook is inactive, reactivate it. - actives[uid] = true - return - else - if self.hooks[obj] then - self.hooks[obj][method] = nil - end - registry[self][obj][method] = nil - handlers[uid] = nil - actives[uid] = nil - scripts[uid] = nil - uid = nil - end - end - - if type(handler) == "string" then - if type(self[handler]) ~= "function" then - AceHook:error("Could not find the handler %q you supplied when hooking method %q", handler, method) - end - elseif type(handler) ~= "function" then - AceHook:error("Could not find the handler you supplied when hooking method %q", method) - end - - local orig - if script then - if not obj.GetScript then - AceHook:error("The object you supplied does not have a GetScript method.") - end - if not obj:HasScript(method) then - AceHook:error("The object you supplied doesn't allow the %q method.", method) - end - - orig = obj:GetScript(method) - if type(orig) ~= "function" then - -- Sometimes there is not a original function for a script. - orig = donothing - end - else - orig = obj[method] - end - if not orig then - AceHook:error("Could not find the method or script %q you are trying to hook.", method) - end - - if not self.hooks[obj] then - self.hooks[obj] = new() - end - if not registry[self][obj] then - registry[self][obj] = new() - end - - local uid = createMethodHook(self, obj, method, handler, orig, secure) - registry[self][obj][method] = uid - actives[uid] = true - handlers[uid] = handler - scripts[uid] = script and true or nil - - if script then - if not secure then - obj:SetScript(method, uid) - self.hooks[obj][method] = orig - else - obj:HookScript(method, uid) - end - else - if not secure then - obj[method] = uid - self.hooks[obj][method] = orig - else - hooksecurefunc(obj, method, uid) - end - end -end - -local function unhookMethod(self, obj, method) - if not registry[self][obj] or not registry[self][obj][method] then - AceHook:error("Attempt to unhook a method %q that is not currently hooked.", method) - return - end - - local uid = registry[self][obj][method] - - if actives[uid] then - if scripts[uid] then -- If this is a script - if obj:GetScript(method) == uid then - -- We own the script. Revert to normal. - if self.hooks[obj][method] == donothing then - obj:SetScript(method, nil) - else - obj:SetScript(method, self.hooks[obj][method]) - end - self.hooks[obj][method] = nil - registry[self][obj][method] = nil - handlers[uid] = nil - scripts[uid] = nil - actives[uid] = nil - else - actives[uid] = nil - end - else - if self.hooks[obj] and self.hooks[obj][method] and obj[method] == uid then - -- We own the method. Revert to normal. - obj[method] = self.hooks[obj][method] - self.hooks[obj][method] = nil - registry[self][obj][method] = nil - handlers[uid] = nil - actives[uid] = nil - else - actives[uid] = nil - end - end - end - if self.hooks[obj] and not next(self.hooks[obj]) then - self.hooks[obj] = del(self.hooks[obj]) - end - if not next(registry[self][obj]) then - registry[self][obj] = del(registry[self][obj]) - end -end - --- ("function" [, handler] [, hookSecure]) or (object, "method" [, handler] [, hookSecure]) -function AceHook:Hook(object, method, handler, hookSecure) - if type(object) == "string" then - method, handler, hookSecure = object, method, handler - if handler == true then - handler, hookSecure = nil, true - end - AceHook:argCheck(handler, 3, "function", "string", "nil") - AceHook:argCheck(hookSecure, 4, "boolean", "nil") - if issecurevariable(method) or onceSecure[method] then - if hookSecure then - onceSecure[method] = true - else - AceHook:error("Attempt to hook secure function %q. Use `SecureHook' or add `true' to the argument list to override.", method) - end - end - hookFunction(self, method, handler, false) - else - if handler == true then - handler, hookSecure = nil, true - end - AceHook:argCheck(object, 2, "table") - AceHook:argCheck(method, 3, "string") - AceHook:argCheck(handler, 4, "function", "string", "nil") - AceHook:argCheck(hookSecure, 5, "boolean", "nil") - if not object[method] then - AceHook:error("Attempt to hook method %q failed, it does not exist in the given object %q.", method, object) - end - if not hookSecure and issecurevariable(object, method) then - AceHook:error("Attempt to hook secure method %q. Use `SecureHook' or add `true' to the argument list to override.", method) - end - hookMethod(self, object, method, handler, false, false) - end -end - --- ("function", handler) or (object, "method", handler) -function AceHook:SecureHook(object, method, handler) - if type(object) == "string" then - method, handler = object, method - AceHook:argCheck(handler, 3, "function", "string", "nil") - hookFunction(self, method, handler, true) - else - AceHook:argCheck(object, 2, "table") - AceHook:argCheck(method, 3, "string") - AceHook:argCheck(handler, 4, "function", "string", "nil") - if not object[method] then - AceHook:error("Attempt to hook method %q failed, it does not exist in the given object %q.", method, object) - end - hookMethod(self, object, method, handler, false, true) - end -end - -function AceHook:HookScript(frame, script, handler) - AceHook:argCheck(frame, 2, "table") - if not frame[0] or type(frame.IsProtected) ~= "function" then - AceHook:error("Bad argument #2 to `HookScript'. Expected frame.") - end - AceHook:argCheck(script, 3, "string") - AceHook:argCheck(handler, 4, "function", "string", "nil") - if frame:IsProtected() and protectedScripts[script] then - AceHook:error("Cannot hook secure script %q.", script) - end - hookMethod(self, frame, script, handler, true, false) -end - -function AceHook:SecureHookScript(frame, script, handler) - AceHook:argCheck(frame, 2, "table") - if not frame[0] or type(frame.IsProtected) ~= "function" then - AceHook:error("Bad argument #2 to `HookScript'. Expected frame.") - end - AceHook:argCheck(script, 3, "string") - AceHook:argCheck(handler, 4, "function", "string", "nil") - hookMethod(self, frame, script, handler, true, true) -end - --- ("function") or (object, "method") -function AceHook:IsHooked(obj, method) - if type(obj) == "string" then - if registry[self][obj] and actives[registry[self][obj]] then - return true, handlers[registry[self][obj]] - end - else - AceHook:argCheck(obj, 2, "string", "table") - AceHook:argCheck(method, 3, "string") - if registry[self][obj] and registry[self][obj][method] and actives[registry[self][obj][method]] then - return true, handlers[registry[self][obj][method]] - end - end - - return false, nil -end - --- ("function") or (object, "method") -function AceHook:Unhook(obj, method) - if type(obj) == "string" then - unhookFunction(self, obj) - else - AceHook:argCheck(obj, 2, "string", "table") - AceHook:argCheck(method, 3, "string") - unhookMethod(self, obj, method) - end -end - -function AceHook:UnhookAll() - if type(registry[self]) ~= "table" then return end - for key, value in pairs(registry[self]) do - if type(key) == "table" then - for method in pairs(value) do - self:Unhook(key, method) - end - else - self:Unhook(key) - end - end -end - -function AceHook:HookReport() - DEFAULT_CHAT_FRAME:AddMessage("This is a list of all active hooks for this object:") - if not next(registry[self]) then - DEFAULT_CHAT_FRAME:AddMessage("No hooks") - end - - for key, value in pairs(registry[self]) do - if type(value) == "table" then - for method, uid in pairs(value) do - DEFAULT_CHAT_FRAME:AddMessage(("object: %s method: %q |cff%s|r%s"):format(tostring(key), method, actives[uid] and "00ff00Active" or "ffff00Inactive", not self.hooks[key][method] and " |cff7f7fff-Secure-|r" or "")) - end - else - DEFAULT_CHAT_FRAME:AddMessage(("function: %q |cff%s|r%s"):format(tostring(key), actives[value] and "00ff00Active" or "ffff00Inactive", not self.hooks[key] and " |cff7f7fff-Secure-|r" or "")) - end - end -end - -function AceHook:OnInstanceInit(object) - if not object.hooks then - object.hooks = new() - end - if not registry[object] then - registry[object] = new() - end -end - -AceHook.OnManualEmbed = AceHook.OnInstanceInit - -function AceHook:OnEmbedDisable(target) - self.UnhookAll(target) -end - -local function activate(self, oldLib, oldDeactivate) - AceHook = self - - self.handlers = oldLib and oldLib.handlers or {} - self.registry = oldLib and oldLib.registry or {} - self.scripts = oldLib and oldLib.scripts or {} - self.actives = oldLib and oldLib.actives or {} - self.onceSecure = oldLib and oldLib.onceSecure or {} - - handlers = self.handlers - registry = self.registry - scripts = self.scripts - actives = self.actives - onceSecure = self.onceSecure - - self:activate(oldLib, oldDeactivate) - - if oldDeactivate then - oldDeactivate(oldLib) - end -end - -AceLibrary:Register(AceHook, MAJOR_VERSION, MINOR_VERSION, activate) +--[[ +Name: AceHook-2.1 +Revision: $Rev: 1091 $ +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/AceHook-2.1 +SVN: http://svn.wowace.com/wowace/trunk/Ace2/AceHook-2.1 +Description: Mixin to allow for safe hooking of functions, methods, and scripts. +Dependencies: AceLibrary, AceOO-2.0 +License: LGPL v2.1 +]] + +local MAJOR_VERSION = "AceHook-2.1" +local MINOR_VERSION = 90000 + tonumber(("$Revision: 1091 $"):match("(%d+)")) + +-- 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 + +--[[--------------------------------------------------------------------------------- + Create the library object +----------------------------------------------------------------------------------]] + +local AceOO = AceLibrary:GetInstance("AceOO-2.0") +local AceHook = AceOO.Mixin { + "Hook", + "HookScript", + "SecureHook", + "SecureHookScript", + "Unhook", + "UnhookAll", + "HookReport", + "IsHooked", + } + +--[[--------------------------------------------------------------------------------- + Library Definitions +----------------------------------------------------------------------------------]] + +local protectedScripts = { + OnClick = true, +} + +local _G = getfenv(0) + +local handlers, scripts, actives, registry, onceSecure + +--[[--------------------------------------------------------------------------------- + Private definitions (Not exposed) +----------------------------------------------------------------------------------]] + +local new, del +do + local list = setmetatable({}, {__mode = "k"}) + function new() + local t = next(list) + if not t then + return {} + end + list[t] = nil + return t + end + + function del(t) + setmetatable(t, nil) + for k in pairs(t) do + t[k] = nil + end + list[t] = true + end +end + +local function createFunctionHook(self, func, handler, orig, secure) + if not secure then + if type(handler) == "string" then + -- The handler is a method, need to self it + local uid + uid = function(...) + if actives[uid] then + return self[handler](self, ...) + else + return orig(...) + end + end + return uid + else + -- The handler is a function, just call it + local uid + uid = function(...) + if actives[uid] then + return handler(...) + else + return orig(...) + end + end + return uid + end + else + -- secure hooks don't call the original method + if type(handler) == "string" then + -- The handler is a method, need to self it + local uid + uid = function(...) + if actives[uid] then + return self[handler](self, ...) + end + end + return uid + else + -- The handler is a function, just call it + local uid + uid = function(...) + if actives[uid] then + return handler(...) + end + end + return uid + end + end +end + +local function createMethodHook(self, object, method, handler, orig, secure) + if not secure then + if type(handler) == "string" then + local uid + uid = function(...) + if actives[uid] then + return self[handler](self, ...) + else + return orig(...) + end + end + return uid + else + -- The handler is a function, just call it + local uid + uid = function(...) + if actives[uid] then + return handler(...) + else + return orig(...) + end + end + return uid + end + else + -- secure hooks don't call the original method + if type(handler) == "string" then + local uid + uid = function(...) + if actives[uid] then + return self[handler](self, ...) + end + end + return uid + else + -- The handler is a function, just call it + local uid + uid = function(...) + if actives[uid] then + return handler(...) + end + end + return uid + end + end +end + +local function hookFunction(self, func, handler, secure) + local orig = _G[func] + + if not orig or type(orig) ~= "function" then + AceHook:error("Attempt to hook a non-existant function %q", func) + end + + if not handler then + handler = func + end + + local uid = registry[self][func] + if uid then + if actives[uid] then + -- We have an active hook from this source. Don't multi-hook + AceHook:error("%q already has an active hook from this source.", func) + end + + if handlers[uid] == handler then + -- The hook is inactive, so reactivate it + actives[uid] = true + return + else + self.hooks[func] = nil + registry[self][func] = nil + handlers[uid] = nil + uid = nil + end + end + + if type(handler) == "string" then + if type(self[handler]) ~= "function" then + AceHook:error("Could not find the the handler %q when hooking function %q", handler, func) + end + elseif type(handler) ~= "function" then + AceHook:error("Could not find the handler you supplied when hooking %q", func) + end + + uid = createFunctionHook(self, func, handler, orig, secure) + registry[self][func] = uid + actives[uid] = true + handlers[uid] = handler + + if not secure then + _G[func] = uid + self.hooks[func] = orig + else + hooksecurefunc(func, uid) + end +end + +local function unhookFunction(self, func) + if not registry[self][func] then + AceHook:error("Tried to unhook %q which is not currently hooked.", func) + end + + local uid = registry[self][func] + + if actives[uid] then + -- See if we own the global function + if self.hooks[func] and _G[func] == uid then + _G[func] = self.hooks[func] + self.hooks[func] = nil + registry[self][func] = nil + handlers[uid] = nil + actives[uid] = nil + -- Magically all-done + else + actives[uid] = nil + end + end +end + +local donothing = function() end + +local function hookMethod(self, obj, method, handler, script, secure) + if not handler then + handler = method + end + + if not obj or type(obj) ~= "table" then + AceHook:error("The object you supplied could not be found, or isn't a table.") + end + + local uid = registry[self][obj] and registry[self][obj][method] + if uid then + if actives[uid] then + -- We have an active hook from this source. Don't multi-hook + AceHook:error("%q already has an active hook from this source.", method) + end + + if handlers[uid] == handler then + -- The hook is inactive, reactivate it. + actives[uid] = true + return + else + if self.hooks[obj] then + self.hooks[obj][method] = nil + end + registry[self][obj][method] = nil + handlers[uid] = nil + actives[uid] = nil + scripts[uid] = nil + uid = nil + end + end + + if type(handler) == "string" then + if type(self[handler]) ~= "function" then + AceHook:error("Could not find the handler %q you supplied when hooking method %q", handler, method) + end + elseif type(handler) ~= "function" then + AceHook:error("Could not find the handler you supplied when hooking method %q", method) + end + + local orig + if script then + if not obj.GetScript then + AceHook:error("The object you supplied does not have a GetScript method.") + end + if not obj:HasScript(method) then + AceHook:error("The object you supplied doesn't allow the %q method.", method) + end + + orig = obj:GetScript(method) + if type(orig) ~= "function" then + -- Sometimes there is not a original function for a script. + orig = donothing + end + else + orig = obj[method] + end + if not orig then + AceHook:error("Could not find the method or script %q you are trying to hook.", method) + end + + if not self.hooks[obj] then + self.hooks[obj] = new() + end + if not registry[self][obj] then + registry[self][obj] = new() + end + + local uid = createMethodHook(self, obj, method, handler, orig, secure) + registry[self][obj][method] = uid + actives[uid] = true + handlers[uid] = handler + scripts[uid] = script and true or nil + + if script then + if not secure then + obj:SetScript(method, uid) + self.hooks[obj][method] = orig + else + obj:HookScript(method, uid) + end + else + if not secure then + obj[method] = uid + self.hooks[obj][method] = orig + else + hooksecurefunc(obj, method, uid) + end + end +end + +local function unhookMethod(self, obj, method) + if not registry[self][obj] or not registry[self][obj][method] then + AceHook:error("Attempt to unhook a method %q that is not currently hooked.", method) + return + end + + local uid = registry[self][obj][method] + + if actives[uid] then + if scripts[uid] then -- If this is a script + if obj:GetScript(method) == uid then + -- We own the script. Revert to normal. + if self.hooks[obj][method] == donothing then + obj:SetScript(method, nil) + else + obj:SetScript(method, self.hooks[obj][method]) + end + self.hooks[obj][method] = nil + registry[self][obj][method] = nil + handlers[uid] = nil + scripts[uid] = nil + actives[uid] = nil + else + actives[uid] = nil + end + else + if self.hooks[obj] and self.hooks[obj][method] and obj[method] == uid then + -- We own the method. Revert to normal. + obj[method] = self.hooks[obj][method] + self.hooks[obj][method] = nil + registry[self][obj][method] = nil + handlers[uid] = nil + actives[uid] = nil + else + actives[uid] = nil + end + end + end + if self.hooks[obj] and not next(self.hooks[obj]) then + self.hooks[obj] = del(self.hooks[obj]) + end + if not next(registry[self][obj]) then + registry[self][obj] = del(registry[self][obj]) + end +end + +-- ("function" [, handler] [, hookSecure]) or (object, "method" [, handler] [, hookSecure]) +function AceHook:Hook(object, method, handler, hookSecure) + if type(object) == "string" then + method, handler, hookSecure = object, method, handler + if handler == true then + handler, hookSecure = nil, true + end + AceHook:argCheck(handler, 3, "function", "string", "nil") + AceHook:argCheck(hookSecure, 4, "boolean", "nil") + if issecurevariable(method) or onceSecure[method] then + if hookSecure then + onceSecure[method] = true + else + AceHook:error("Attempt to hook secure function %q. Use `SecureHook' or add `true' to the argument list to override.", method) + end + end + hookFunction(self, method, handler, false) + else + if handler == true then + handler, hookSecure = nil, true + end + AceHook:argCheck(object, 2, "table") + AceHook:argCheck(method, 3, "string") + AceHook:argCheck(handler, 4, "function", "string", "nil") + AceHook:argCheck(hookSecure, 5, "boolean", "nil") + if not object[method] then + AceHook:error("Attempt to hook method %q failed, it does not exist in the given object %q.", method, object) + end + if not hookSecure and issecurevariable(object, method) then + AceHook:error("Attempt to hook secure method %q. Use `SecureHook' or add `true' to the argument list to override.", method) + end + hookMethod(self, object, method, handler, false, false) + end +end + +-- ("function", handler) or (object, "method", handler) +function AceHook:SecureHook(object, method, handler) + if type(object) == "string" then + method, handler = object, method + AceHook:argCheck(handler, 3, "function", "string", "nil") + hookFunction(self, method, handler, true) + else + AceHook:argCheck(object, 2, "table") + AceHook:argCheck(method, 3, "string") + AceHook:argCheck(handler, 4, "function", "string", "nil") + if not object[method] then + AceHook:error("Attempt to hook method %q failed, it does not exist in the given object %q.", method, object) + end + hookMethod(self, object, method, handler, false, true) + end +end + +function AceHook:HookScript(frame, script, handler) + AceHook:argCheck(frame, 2, "table") + if not frame[0] or type(frame.IsProtected) ~= "function" then + AceHook:error("Bad argument #2 to `HookScript'. Expected frame.") + end + AceHook:argCheck(script, 3, "string") + AceHook:argCheck(handler, 4, "function", "string", "nil") + if frame:IsProtected() and protectedScripts[script] then + AceHook:error("Cannot hook secure script %q.", script) + end + hookMethod(self, frame, script, handler, true, false) +end + +function AceHook:SecureHookScript(frame, script, handler) + AceHook:argCheck(frame, 2, "table") + if not frame[0] or type(frame.IsProtected) ~= "function" then + AceHook:error("Bad argument #2 to `HookScript'. Expected frame.") + end + AceHook:argCheck(script, 3, "string") + AceHook:argCheck(handler, 4, "function", "string", "nil") + hookMethod(self, frame, script, handler, true, true) +end + +-- ("function") or (object, "method") +function AceHook:IsHooked(obj, method) + if type(obj) == "string" then + if registry[self][obj] and actives[registry[self][obj]] then + return true, handlers[registry[self][obj]] + end + else + AceHook:argCheck(obj, 2, "string", "table") + AceHook:argCheck(method, 3, "string") + if registry[self][obj] and registry[self][obj][method] and actives[registry[self][obj][method]] then + return true, handlers[registry[self][obj][method]] + end + end + + return false, nil +end + +-- ("function") or (object, "method") +function AceHook:Unhook(obj, method) + if type(obj) == "string" then + unhookFunction(self, obj) + else + AceHook:argCheck(obj, 2, "string", "table") + AceHook:argCheck(method, 3, "string") + unhookMethod(self, obj, method) + end +end + +function AceHook:UnhookAll() + if type(registry[self]) ~= "table" then return end + for key, value in pairs(registry[self]) do + if type(key) == "table" then + for method in pairs(value) do + self:Unhook(key, method) + end + else + self:Unhook(key) + end + end +end + +function AceHook:HookReport() + DEFAULT_CHAT_FRAME:AddMessage("This is a list of all active hooks for this object:") + if not next(registry[self]) then + DEFAULT_CHAT_FRAME:AddMessage("No hooks") + end + + for key, value in pairs(registry[self]) do + if type(value) == "table" then + for method, uid in pairs(value) do + DEFAULT_CHAT_FRAME:AddMessage(("object: %s method: %q |cff%s|r%s"):format(tostring(key), method, actives[uid] and "00ff00Active" or "ffff00Inactive", not self.hooks[key][method] and " |cff7f7fff-Secure-|r" or "")) + end + else + DEFAULT_CHAT_FRAME:AddMessage(("function: %q |cff%s|r%s"):format(tostring(key), actives[value] and "00ff00Active" or "ffff00Inactive", not self.hooks[key] and " |cff7f7fff-Secure-|r" or "")) + end + end +end + +function AceHook:OnInstanceInit(object) + if not object.hooks then + object.hooks = new() + end + if not registry[object] then + registry[object] = new() + end +end + +AceHook.OnManualEmbed = AceHook.OnInstanceInit + +function AceHook:OnEmbedDisable(target) + self.UnhookAll(target) +end + +local function activate(self, oldLib, oldDeactivate) + AceHook = self + + self.handlers = oldLib and oldLib.handlers or {} + self.registry = oldLib and oldLib.registry or {} + self.scripts = oldLib and oldLib.scripts or {} + self.actives = oldLib and oldLib.actives or {} + self.onceSecure = oldLib and oldLib.onceSecure or {} + + handlers = self.handlers + registry = self.registry + scripts = self.scripts + actives = self.actives + onceSecure = self.onceSecure + + self:activate(oldLib, oldDeactivate) + + if oldDeactivate then + oldDeactivate(oldLib) + end +end + +AceLibrary:Register(AceHook, MAJOR_VERSION, MINOR_VERSION, activate) diff --git a/Libs/AceHook-2.1/AceHook-2.1.toc b/Libs/AceHook-2.1/AceHook-2.1.toc index ff215fc..98be011 100644 --- a/Libs/AceHook-2.1/AceHook-2.1.toc +++ b/Libs/AceHook-2.1/AceHook-2.1.toc @@ -1,16 +1,16 @@ -## Interface: 30000 -## X-Curse-Packaged-Version: r1094 -## X-Curse-Project-Name: Ace2 -## X-Curse-Project-ID: ace2 -## X-Curse-Repository-ID: wow/ace2/mainline - -## Title: Lib: AceHook-2.1 -## 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 - -AceHook-2.1.lua +## Interface: 30000 +## X-Curse-Packaged-Version: r1094 +## X-Curse-Project-Name: Ace2 +## X-Curse-Project-ID: ace2 +## X-Curse-Repository-ID: wow/ace2/mainline + +## Title: Lib: AceHook-2.1 +## 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 + +AceHook-2.1.lua diff --git a/Libs/AceLibrary/AceLibrary.lua b/Libs/AceLibrary/AceLibrary.lua index 8ff1742..26901ca 100644 --- a/Libs/AceLibrary/AceLibrary.lua +++ b/Libs/AceLibrary/AceLibrary.lua @@ -1,799 +1,799 @@ ---[[ -Name: AceLibrary -Revision: $Rev: 1091 $ -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: 1091 $ -Website: http://www.wowace.com/ -Documentation: http://www.wowace.com/index.php/AceLibrary -SVN: http://svn.wowace.com/wowace/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 = 90000 + tonumber(("$Revision: 1091 $"):match("(%d+)")) - -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) +--[[ +Name: AceLibrary +Revision: $Rev: 1091 $ +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: 1091 $ +Website: http://www.wowace.com/ +Documentation: http://www.wowace.com/index.php/AceLibrary +SVN: http://svn.wowace.com/wowace/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 = 90000 + tonumber(("$Revision: 1091 $"):match("(%d+)")) + +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) diff --git a/Libs/AceLibrary/AceLibrary.toc b/Libs/AceLibrary/AceLibrary.toc index 2b0bf2d..967e00f 100644 --- a/Libs/AceLibrary/AceLibrary.toc +++ b/Libs/AceLibrary/AceLibrary.toc @@ -1,14 +1,14 @@ -## Interface: 30000 -## X-Curse-Packaged-Version: r1094 -## X-Curse-Project-Name: Ace2 -## X-Curse-Project-ID: ace2 -## X-Curse-Repository-ID: wow/ace2/mainline - -## 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 +## Interface: 30000 +## X-Curse-Packaged-Version: r1094 +## X-Curse-Project-Name: Ace2 +## X-Curse-Project-ID: ace2 +## X-Curse-Repository-ID: wow/ace2/mainline + +## 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 diff --git a/Libs/AceOO-2.0/AceOO-2.0.lua b/Libs/AceOO-2.0/AceOO-2.0.lua index 4a540f8..7161ee4 100644 --- a/Libs/AceOO-2.0/AceOO-2.0.lua +++ b/Libs/AceOO-2.0/AceOO-2.0.lua @@ -1,980 +1,980 @@ ---[[ -Name: AceOO-2.0 -Revision: $Rev: 1091 $ -Developed by: The Ace Development Team (http://www.wowace.com/index.php/The_Ace_Development_Team) -Inspired By: Ace 1.x by Turan (turan@gryphon.com) -Website: http://www.wowace.com/ -Documentation: http://www.wowace.com/index.php/AceOO-2.0 -SVN: http://svn.wowace.com/wowace/trunk/Ace2/AceOO-2.0 -Description: Library to provide an object-orientation framework. -Dependencies: AceLibrary -License: MIT -]] - -local MAJOR_VERSION = "AceOO-2.0" -local MINOR_VERSION = 90000 + tonumber(("$Revision: 1091 $"):match("(%d+)")) - --- This ensures the code is only executed if the libary doesn't already exist, or is a newer version -if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary.") end -if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end - -local AceOO = { - error = AceLibrary.error, - argCheck = AceLibrary.argCheck -} - --- @function getuid --- @brief Obtain a unique string identifier for the object in question. --- @param t The object to obtain the uid for. --- @return The uid string. -local function getuid(t) - local mt = getmetatable(t) - setmetatable(t, nil) - local str = tostring(t) - setmetatable(t, mt) - local cap = str:match("[^:]*: 0x(.*)$") or str:match("[^:]*: (.*)$") - if cap then - return ("0"):rep(8 - #cap) .. cap - end -end - -local function getlibrary(o) - if type(o) == "table" then - return o - elseif type(o) == "string" then - if not AceLibrary:HasInstance(o) then - AceOO:error("Library %q does not exist.", o) - end - return AceLibrary(o) - end -end - -local function deeprawget(self, k) - while true do - local v = rawget(self, k) - if v ~= nil then - return v - end - local mt = getmetatable(self) - if not mt or type(mt.__index) ~= "table" then - return nil - end - self = mt.__index - end -end - --- @function Factory --- @brief Construct a factory for the creation of objects. --- @param obj The object whose init method will be called on the new factory --- object. --- @param newobj The object whose init method will be called on the new --- objects that the Factory creates, to initialize them. --- @param (...) Arguments which will be passed to obj.init() in addition --- to the Factory object. --- @return The new factory which creates a newobj when its new method is called, --- or when it is called directly (__call metamethod). -local Factory -do - local function getlibraries(...) - if select('#', ...) == 0 then - return - end - return getlibrary((select(1, ...))), getlibraries(select(2, ...)) - end - local arg = {} - local function new(obj, ...) - local t = {} - local uid = getuid(t) - obj:init(t, getlibraries(...)) - t.uid = uid - return t - end - - local function createnew(self, ...) - local o = self.prototype - local x = new(o, getlibraries(...)) - return x - end - - function Factory(obj, newobj, ...) - local t = new(obj, ...) - t.prototype = newobj - t.new = createnew - getmetatable(t).__call = t.new - return t - end -end - - -local function objtostring(self) - if self.ToString then - return self:ToString() - elseif self.GetLibraryVersion then - return (self:GetLibraryVersion()) - elseif self.super then - local s = "Sub-" .. tostring(self.super) - local first = true - if self.interfaces then - for interface in pairs(self.interfaces) do - if first then - s = s .. "(" .. tostring(interface) - first = false - else - s = s .. ", " .. tostring(interface) - end - end - end - if self.mixins then - for mixin in pairs(self.mixins) do - if first then - s = s .. tostring(mixin) - first = false - else - s = s .. ", " .. tostring(mixin) - end - end - end - if first then - if self.uid then - return s .. ":" .. self.uid - else - return s - end - else - return s .. ")" - end - else - return self.uid and 'Subclass:' .. self.uid or 'Subclass' - end -end - --- @table Object --- @brief Base of all objects, including Class. --- --- @method init --- @brief Initialize a new object. --- @param newobject The object to initialize --- @param class The class to make newobject inherit from -local Object -do - Object = {} - function Object:init(newobject, class) - local parent = class or self - if not rawget(newobject, 'uid') then - newobject.uid = getuid(newobject) - end - local mt = { - __index = parent, - __tostring = objtostring, - } - setmetatable(newobject, mt) - end - Object.uid = getuid(Object) - setmetatable(Object, { __tostring = function() return 'Object' end }) -end - -local Interface - -local function validateInterface(object, interface) - if not object.class and object.prototype then - object = object.prototype - end - for k,v in pairs(interface.interface) do - if tostring(type(object[k])) ~= v then - return false - end - end - if interface.superinterfaces then - for superinterface in pairs(interface.superinterfaces) do - if not validateInterface(object, superinterface) then - return false - end - end - end - if type(object.class) == "table" and rawequal(object.class.prototype, object) then - if not object.class.interfaces then - rawset(object.class, 'interfaces', {}) - end - object.class.interfaces[interface] = true - elseif type(object.class) == "table" and type(object.class.prototype) == "table" then - validateInterface(object.class.prototype, interface) - -- check if class is proper, thus preventing future checks. - end - return true -end - --- @function inherits --- @brief Return whether an Object or Class inherits from a given --- parent. --- @param object Object or Class to check --- @param parent Parent to test inheritance from --- @return whether an Object or Class inherits from a given --- parent. -local function inherits(object, parent) - object = getlibrary(object) - if type(parent) == "string" then - if not AceLibrary:HasInstance(parent) then - return false - else - parent = AceLibrary(parent) - end - end - AceOO:argCheck(parent, 2, "table") - if type(object) ~= "table" then - return false - end - local current - local class = deeprawget(object, 'class') - if class then - current = class - else - current = object - end - if type(current) ~= "table" then - return false - end - if rawequal(current, parent) then - return true - end - if parent.class then - while true do - if rawequal(current, Object) then - break - end - if current.mixins then - for mixin in pairs(current.mixins) do - if rawequal(mixin, parent) then - return true - end - end - end - if current.interfaces then - for interface in pairs(current.interfaces) do - if rawequal(interface, parent) then - return true - end - end - end - current = deeprawget(current, 'super') - if type(current) ~= "table" then - break - end - end - - local isInterface = false - local curr = parent.class - while true do - if rawequal(curr, Object) then - break - elseif rawequal(curr, Interface) then - isInterface = true - break - end - curr = deeprawget(curr, 'super') - if type(curr) ~= "table" then - break - end - end - return isInterface and validateInterface(object, parent) - else - while true do - if rawequal(current, parent) then - return true - elseif rawequal(current, Object) then - return false - end - current = deeprawget(current, 'super') - if type(current) ~= "table" then - return false - end - end - end -end - --- @table Class --- @brief An object factory which sets up inheritence and supports --- 'mixins'. --- --- @metamethod Class call --- @brief Call ClassFactory:new() to create a new class. --- --- @method Class new --- @brief Construct a new object. --- @param (...) Arguments to pass to the object init function. --- @return The new object. --- --- @method Class init --- @brief Initialize a new class. --- @param parent Superclass. --- @param (...) Mixins. --- --- @method Class ToString --- @return A string representing the object, in this case 'Class'. -local initStatus -local Class -local Mixin -local autoEmbed = false -local function traverseInterfaces(bit, total) - if bit.superinterfaces then - for interface in pairs(bit.superinterfaces) do - if not total[interface] then - total[interface] = true - traverseInterfaces(interface, total) - end - end - end -end -local class_new -do - Class = Factory(Object, setmetatable({}, {__index = Object}), Object) - Class.super = Object - - local function protostring(t) - return '<' .. tostring(t.class) .. ' prototype>' - end - local function classobjectstring(t) - if t.ToString then - return t:ToString() - elseif t.GetLibraryVersion then - return (t:GetLibraryVersion()) - else - return '<' .. tostring(t.class) .. ' instance>' - end - end - local function classobjectequal(self, other) - if type(self) == "table" and self.Equals then - return self:Equals(other) - elseif type(other) == "table" and other.Equals then - return other:Equals(self) - elseif type(self) == "table" and self.CompareTo then - return self:CompareTo(other) == 0 - elseif type(other) == "table" and other.CompareTo then - return other:CompareTo(self) == 0 - else - return rawequal(self, other) - end - end - local function classobjectlessthan(self, other) - if type(self) == "table" and self.IsLessThan then - return self:IsLessThan(other) - elseif type(other) == "table" and other.IsLessThanOrEqualTo then - return not other:IsLessThanOrEqualTo(self) - elseif type(self) == "table" and self.CompareTo then - return self:CompareTo(other) < 0 - elseif type(other) == "table" and other.CompareTo then - return other:CompareTo(self) > 0 - elseif type(other) == "table" and other.IsLessThan and other.Equals then - return other:Equals(self) or other:IsLessThan(self) - else - AceOO:error("cannot compare two objects") - end - end - local function classobjectlessthanequal(self, other) - if type(self) == "table" and self.IsLessThanOrEqualTo then - return self:IsLessThanOrEqualTo(other) - elseif type(other) == "table" and other.IsLessThan then - return not other:IsLessThan(self) - elseif type(self) == "table" and self.CompareTo then - return self:CompareTo(other) <= 0 - elseif type(other) == "table" and other.CompareTo then - return other:CompareTo(self) >= 0 - elseif type(self) == "table" and self.IsLessThan and self.Equals then - return self:Equals(other) or self:IsLessThan(other) - else - AceOO:error("cannot compare two incompatible objects") - end - end - local function classobjectadd(self, other) - if type(self) == "table" and self.Add then - return self:Add(other) - else - AceOO:error("cannot add two incompatible objects") - end - end - local function classobjectsub(self, other) - if type(self) == "table" and self.Subtract then - return self:Subtract(other) - else - AceOO:error("cannot subtract two incompatible objects") - end - end - local function classobjectunm(self, other) - if type(self) == "table" and self.UnaryNegation then - return self:UnaryNegation(other) - else - AceOO:error("attempt to negate an incompatible object") - end - end - local function classobjectmul(self, other) - if type(self) == "table" and self.Multiply then - return self:Multiply(other) - else - AceOO:error("cannot multiply two incompatible objects") - end - end - local function classobjectdiv(self, other) - if type(self) == "table" and self.Divide then - return self:Divide(other) - else - AceOO:error("cannot divide two incompatible objects") - end - end - local function classobjectpow(self, other) - if type(self) == "table" and self.Exponent then - return self:Exponent(other) - else - AceOO:error("cannot exponentiate two incompatible objects") - end - end - local function classobjectconcat(self, other) - if type(self) == "table" and self.Concatenate then - return self:Concatenate(other) - else - AceOO:error("cannot concatenate two incompatible objects") - end - end - function class_new(self, ...) - if self.virtual then - AceOO:error("Cannot instantiate a virtual class.") - end - - local o = self.prototype - local newobj = {} - if o.class and o.class.instancemeta then - setmetatable(newobj, o.class.instancemeta) - else - Object:init(newobj, o) - end - - if self.interfaces and not self.interfacesVerified then - -- Verify the interfaces - - for interface in pairs(self.interfaces) do - for field,kind in pairs(interface.interface) do - if tostring(type(newobj[field])) ~= kind then - AceOO:error("Class did not satisfy all interfaces. %q is required to be a %s. It is a %s", field, kind, tostring(type(newobj[field]))) - end - end - end - self.interfacesVerified = true - end - local tmp = initStatus - initStatus = newobj - newobj:init(...) - if initStatus then - initStatus = tmp - AceOO:error("Initialization not completed, be sure to call the superclass's init method.") - return - end - initStatus = tmp - return newobj - end - local classmeta = { - __tostring = objtostring, - __call = function(self, ...) - return self:new(...) - end, - } - function Class:init(newclass, parent, ...) - parent = parent or self - - local total - - if parent.class then - total = { parent, ... } - parent = self - else - total = { ... } - end - if not inherits(parent, Class) then - AceOO:error("Classes must inherit from a proper class") - end - if parent.sealed then - AceOO:error("Cannot inherit from a sealed class") - end - for i,v in ipairs(total) do - if inherits(v, Mixin) and v.class then - if v.__deprecated then - AceOO:error(v.__deprecated) - end - if not newclass.mixins then - newclass.mixins = {} - end - if newclass.mixins[v] then - AceOO:error("Cannot explicitly inherit from the same mixin twice") - end - newclass.mixins[v] = true - elseif inherits(v, Interface) and v.class then - if not newclass.interfaces then - newclass.interfaces = {} - end - if newclass.interfaces[v] then - AceOO:error("Cannot explicitly inherit from the same interface twice") - end - newclass.interfaces[v] = true - else - AceOO:error("Classes can only inherit from one or zero classes and any number of mixins or interfaces") - end - end - if parent.interfaces then - if not newclass.interfaces then - newclass.interfaces = {} - end - for interface in pairs(parent.interfaces) do - newclass.interfaces[interface] = true - end - end - for k in pairs(total) do - total[k] = nil - end - - newclass.super = parent - - newclass.prototype = setmetatable(total, { - __index = parent.prototype, - __tostring = protostring, - }) - total = nil - - newclass.instancemeta = { - __index = newclass.prototype, - __tostring = classobjectstring, - __eq = classobjectequal, - __lt = classobjectlessthan, - __le = classobjectlessthanequal, - __add = classobjectadd, - __sub = classobjectsub, - __unm = classobjectunm, - __mul = classobjectmul, - __div = classobjectdiv, - __pow = classobjectpow, - __concat = classobjectconcat, - } - - setmetatable(newclass, classmeta) - - newclass.new = class_new - - if newclass.mixins then - -- Fold in the mixins - local err, msg - for mixin in pairs(newclass.mixins) do - local ret - autoEmbed = true - ret, msg = pcall(mixin.embed, mixin, newclass.prototype) - autoEmbed = false - if not ret then - err = true - break - end - end - - if err then - local pt = newclass.prototype - for k,v in pairs(pt) do - pt[k] = nil - end - - -- method conflict - AceOO:error(msg) - end - end - - newclass.prototype.class = newclass - - if newclass.interfaces then - for interface in pairs(newclass.interfaces) do - traverseInterfaces(interface, newclass.interfaces) - end - end - if newclass.mixins then - for mixin in pairs(newclass.mixins) do - if mixin.interfaces then - if not newclass.interfaces then - newclass.interfaces = {} - end - for interface in pairs(mixin.interfaces) do - newclass.interfaces[interface] = true - end - end - end - end - end - function Class:ToString() - if type(self.GetLibraryVersion) == "function" then - return (self:GetLibraryVersion()) - else - return "Class" - end - end - - local tmp - function Class.prototype:init() - if rawequal(self, initStatus) then - initStatus = nil - else - AceOO:error("Improper self passed to init. You must do MyClass.super.prototype.init(self, ...)", 2) - end - self.uid = getuid(self) - local current = self.class - while true do - if current == Class then - break - end - if current.mixins then - for mixin in pairs(current.mixins) do - if type(mixin.OnInstanceInit) == "function" then - mixin:OnInstanceInit(self) - end - end - end - current = current.super - end - end -end - - --- @object ClassFactory --- @brief A factory for creating classes. Rarely used directly. -local ClassFactory = Factory(Object, Class, Object) - -function Class:new(...) - local x = ClassFactory:new(...) - if AceOO.classes then - AceOO.classes[x] = true - end - return x -end -getmetatable(Class).__call = Class.new - --- @class Mixin --- @brief A class to create mixin objects, which contain methods that get --- "mixed in" to class prototypes. --- --- @object Mixin prototype --- @brief The prototype that mixin objects inherit their methods from. --- --- @method Mixin prototype embed --- @brief Mix in the methods of our object which are listed in our interface --- to the supplied target table. --- --- @method Mixin prototype init --- @brief Initialize the mixin object. --- @param newobj The new object we're initializing. --- @param interface The interface we implement (the list of methods our --- prototype provides which should be mixed into the target --- table by embed). -do - Mixin = Class() - function Mixin:ToString() - if self.GetLibraryVersion then - return (self:GetLibraryVersion()) - else - return 'Mixin' - end - end - local function _Embed(state, field, target) - field = next(state.export, field) - if field == nil then - return - end - - if rawget(target, field) or (target[field] and target[field] ~= state[field]) then - AceOO:error("Method conflict in attempt to mixin. Field %q", field) - end - - target[field] = state[field] - - local ret,msg = pcall(_Embed, state, field, target) - if not ret then - -- Mix in the next method according to the defined interface. If that - -- fails due to a conflict, re-raise to back out the previous mixed - -- methods. - - target[field] = nil - AceOO:error(msg) - end - end - function Mixin.prototype:embed(target) - if self.__deprecated then - AceOO:error(self.__deprecated) - end - local mt = getmetatable(target) - setmetatable(target, nil) - local err, msg = pcall(_Embed, self, nil, target) - if not err then - setmetatable(target, mt) - AceOO:error(msg) - return - end - if type(self.embedList) == "table" then - self.embedList[target] = true - end - if type(target.class) ~= "table" then - target[self] = true - end - if not autoEmbed and type(self.OnManualEmbed) == "function" then - self:OnManualEmbed(target) - end - setmetatable(target, mt) - end - - function Mixin.prototype:activate(oldLib, oldDeactivate) - if oldLib and oldLib.embedList then - for target in pairs(oldLib.embedList) do - local mt = getmetatable(target) - setmetatable(target, nil) - for field in pairs(oldLib.export) do - target[field] = nil - end - setmetatable(target, mt) - end - self.embedList = oldLib.embedList - for target in pairs(self.embedList) do - self:embed(target) - end - else - self.embedList = setmetatable({}, {__mode="k"}) - end - end - - function Mixin.prototype:init(export, ...) - AceOO:argCheck(export, 2, "table") - for k,v in pairs(export) do - if type(k) ~= "number" then - AceOO:error("All keys to argument #2 must be numbers.") - elseif type(v) ~= "string" then - AceOO:error("All values to argument #2 must be strings.") - end - end - local num = #export - for i = 1, num do - local v = export[i] - export[i] = nil - export[v] = true - end - - local interfaces - if select('#', ...) >= 1 then - interfaces = { ... } - for i,v in ipairs(interfaces) do - v = getlibrary(v) - interfaces[i] = v - if not v.class or not inherits(v, Interface) then - AceOO:error("Mixins can inherit only from interfaces") - end - end - local num = #interfaces - for i = 1, num do - local v = interfaces[i] - interfaces[i] = nil - interfaces[v] = true - end - for interface in pairs(interfaces) do - traverseInterfaces(interface, interfaces) - end - for interface in pairs(interfaces) do - for field,kind in pairs(interface.interface) do - if kind ~= "nil" then - local good = false - for bit in pairs(export) do - if bit == field then - good = true - break - end - end - if not good then - AceOO:error("Mixin does not fully accommodate field %q", field) - end - end - end - end - end - self.super = Mixin.prototype - Mixin.super.prototype.init(self) - self.export = export - self.interfaces = interfaces - end -end - --- @class Interface --- @brief A class to create interfaces, which contain contracts that classes --- which inherit from this must comply with. --- --- @object Interface prototype --- @brief The prototype that interface objects must adhere to. --- --- @method Interface prototype init --- @brief Initialize the mixin object. --- @param interface The interface we contract (the hash of fields forced). --- @param (...) Superinterfaces -do - Interface = Class() - function Interface:ToString() - if self.GetLibraryVersion then - return (self:GetLibraryVersion()) - else - return 'Instance' - end - end - function Interface.prototype:init(interface, ...) - Interface.super.prototype.init(self) - AceOO:argCheck(interface, 2, "table") - for k,v in pairs(interface) do - if type(k) ~= "string" then - AceOO:error("All keys to argument #2 must be numbers.") - elseif type(v) ~= "string" then - AceOO:error("All values to argument #2 must be strings.") - elseif v ~= "nil" and v ~= "string" and v ~= "number" and v ~= "table" and v ~= "function" then - AceOO:error('All values to argument #2 must either be "nil", "string", "number", "table", or "function".') - end - end - if select('#', ...) >= 1 then - self.superinterfaces = { ... } - for i,v in ipairs(self.superinterfaces) do - v = getlibrary(v) - self.superinterfaces[i] = v - if not inherits(v, Interface) or not v.class then - AceOO:error('Cannot provide a non-Interface to inherit from') - end - end - local num = #self.superinterfaces - for i = 1, num do - local v = self.superinterfaces[i] - self.superinterfaces[i] = nil - self.superinterfaces[v] = true - end - end - self.interface = interface - end -end - --- @function Classpool --- @brief Obtain a read only class from our pool of classes, indexed by the --- superclass and mixins. --- @param sc The superclass of the class we want. --- @param (m1..m20) Mixins of the class we want's objects. --- @return A read only class from the class pool. -local Classpool -do - local pool = setmetatable({}, {__mode = 'v'}) - local function newindex(k, v) - AceOO:error('Attempt to modify a read-only class.') - end - local function protonewindex(k, v) - AceOO:error('Attempt to modify a read-only class prototype.') - end - local function ts(bit) - if type(bit) ~= "table" then - return tostring(bit) - elseif getmetatable(bit) and bit.__tostring then - return tostring(bit) - elseif type(bit.GetLibraryVersion) == "function" then - return bit:GetLibraryVersion() - else - return tostring(bit) - end - end - local t = {} - local function getcomplexuid(sc, ...) - if sc then - if sc.uid then - table.insert(t, sc.uid) - else - AceOO:error("%s is not an appropriate class/mixin", ts(sc)) - end - end - for i = 1, select('#', ...) do - local m = select(i, ...) - if m.uid then - table.insert(t, m.uid) - else - AceOO:error("%s is not an appropriate mixin", ts(m)) - end - end - table.sort(t) - local uid = table.concat(t, '') - local num = #t - for i = 1, num do - t[i] = nil - end - return uid - end - local classmeta - local arg = {} - function Classpool(superclass, ...) - local l = getlibrary - superclass = getlibrary(superclass) - arg = { ... } - for i, v in ipairs(arg) do - arg[i] = getlibrary(v) - end - if superclass then - if superclass.class then -- mixin - table.insert(arg, 1, superclass) - superclass = Class - end - else - superclass = Class - end - local key = getcomplexuid(superclass, unpack(arg)) - if not pool[key] then - local class = Class(superclass, unpack(arg)) - if not classmeta then - classmeta = {} - local mt = getmetatable(class) - for k,v in pairs(mt) do - classmeta[k] = v - end - classmeta.__newindex = newindex - end - -- Prevent the user from adding methods to this class. - -- NOTE: I'm not preventing modifications of existing class members, - -- but it's likely that only a truly malicious user will be doing so. - class.sealed = true - setmetatable(class, classmeta) - getmetatable(class.prototype).__newindex = protonewindex - pool[key] = class - end - return pool[key] - end -end - -AceOO.Factory = Factory -AceOO.Object = Object -AceOO.Class = Class -AceOO.Mixin = Mixin -AceOO.Interface = Interface -AceOO.Classpool = Classpool -AceOO.inherits = inherits - --- Library handling bits - -local function activate(self, oldLib, oldDeactivate) - AceOO = self - Factory = self.Factory - Object = self.Object - Class = self.Class - ClassFactory.prototype = Class - Mixin = self.Mixin - Interface = self.Interface - Classpool = self.Classpool - - if oldLib then - self.classes = oldLib.classes - end - if not self.classes then - self.classes = setmetatable({}, {__mode="k"}) - else - for class in pairs(self.classes) do - class.new = class_new - end - end - - if oldDeactivate then - oldDeactivate(oldLib) - end -end - -AceLibrary:Register(AceOO, MAJOR_VERSION, MINOR_VERSION, activate) -AceOO = AceLibrary(MAJOR_VERSION) +--[[ +Name: AceOO-2.0 +Revision: $Rev: 1091 $ +Developed by: The Ace Development Team (http://www.wowace.com/index.php/The_Ace_Development_Team) +Inspired By: Ace 1.x by Turan (turan@gryphon.com) +Website: http://www.wowace.com/ +Documentation: http://www.wowace.com/index.php/AceOO-2.0 +SVN: http://svn.wowace.com/wowace/trunk/Ace2/AceOO-2.0 +Description: Library to provide an object-orientation framework. +Dependencies: AceLibrary +License: MIT +]] + +local MAJOR_VERSION = "AceOO-2.0" +local MINOR_VERSION = 90000 + tonumber(("$Revision: 1091 $"):match("(%d+)")) + +-- This ensures the code is only executed if the libary doesn't already exist, or is a newer version +if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary.") end +if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end + +local AceOO = { + error = AceLibrary.error, + argCheck = AceLibrary.argCheck +} + +-- @function getuid +-- @brief Obtain a unique string identifier for the object in question. +-- @param t The object to obtain the uid for. +-- @return The uid string. +local function getuid(t) + local mt = getmetatable(t) + setmetatable(t, nil) + local str = tostring(t) + setmetatable(t, mt) + local cap = str:match("[^:]*: 0x(.*)$") or str:match("[^:]*: (.*)$") + if cap then + return ("0"):rep(8 - #cap) .. cap + end +end + +local function getlibrary(o) + if type(o) == "table" then + return o + elseif type(o) == "string" then + if not AceLibrary:HasInstance(o) then + AceOO:error("Library %q does not exist.", o) + end + return AceLibrary(o) + end +end + +local function deeprawget(self, k) + while true do + local v = rawget(self, k) + if v ~= nil then + return v + end + local mt = getmetatable(self) + if not mt or type(mt.__index) ~= "table" then + return nil + end + self = mt.__index + end +end + +-- @function Factory +-- @brief Construct a factory for the creation of objects. +-- @param obj The object whose init method will be called on the new factory +-- object. +-- @param newobj The object whose init method will be called on the new +-- objects that the Factory creates, to initialize them. +-- @param (...) Arguments which will be passed to obj.init() in addition +-- to the Factory object. +-- @return The new factory which creates a newobj when its new method is called, +-- or when it is called directly (__call metamethod). +local Factory +do + local function getlibraries(...) + if select('#', ...) == 0 then + return + end + return getlibrary((select(1, ...))), getlibraries(select(2, ...)) + end + local arg = {} + local function new(obj, ...) + local t = {} + local uid = getuid(t) + obj:init(t, getlibraries(...)) + t.uid = uid + return t + end + + local function createnew(self, ...) + local o = self.prototype + local x = new(o, getlibraries(...)) + return x + end + + function Factory(obj, newobj, ...) + local t = new(obj, ...) + t.prototype = newobj + t.new = createnew + getmetatable(t).__call = t.new + return t + end +end + + +local function objtostring(self) + if self.ToString then + return self:ToString() + elseif self.GetLibraryVersion then + return (self:GetLibraryVersion()) + elseif self.super then + local s = "Sub-" .. tostring(self.super) + local first = true + if self.interfaces then + for interface in pairs(self.interfaces) do + if first then + s = s .. "(" .. tostring(interface) + first = false + else + s = s .. ", " .. tostring(interface) + end + end + end + if self.mixins then + for mixin in pairs(self.mixins) do + if first then + s = s .. tostring(mixin) + first = false + else + s = s .. ", " .. tostring(mixin) + end + end + end + if first then + if self.uid then + return s .. ":" .. self.uid + else + return s + end + else + return s .. ")" + end + else + return self.uid and 'Subclass:' .. self.uid or 'Subclass' + end +end + +-- @table Object +-- @brief Base of all objects, including Class. +-- +-- @method init +-- @brief Initialize a new object. +-- @param newobject The object to initialize +-- @param class The class to make newobject inherit from +local Object +do + Object = {} + function Object:init(newobject, class) + local parent = class or self + if not rawget(newobject, 'uid') then + newobject.uid = getuid(newobject) + end + local mt = { + __index = parent, + __tostring = objtostring, + } + setmetatable(newobject, mt) + end + Object.uid = getuid(Object) + setmetatable(Object, { __tostring = function() return 'Object' end }) +end + +local Interface + +local function validateInterface(object, interface) + if not object.class and object.prototype then + object = object.prototype + end + for k,v in pairs(interface.interface) do + if tostring(type(object[k])) ~= v then + return false + end + end + if interface.superinterfaces then + for superinterface in pairs(interface.superinterfaces) do + if not validateInterface(object, superinterface) then + return false + end + end + end + if type(object.class) == "table" and rawequal(object.class.prototype, object) then + if not object.class.interfaces then + rawset(object.class, 'interfaces', {}) + end + object.class.interfaces[interface] = true + elseif type(object.class) == "table" and type(object.class.prototype) == "table" then + validateInterface(object.class.prototype, interface) + -- check if class is proper, thus preventing future checks. + end + return true +end + +-- @function inherits +-- @brief Return whether an Object or Class inherits from a given +-- parent. +-- @param object Object or Class to check +-- @param parent Parent to test inheritance from +-- @return whether an Object or Class inherits from a given +-- parent. +local function inherits(object, parent) + object = getlibrary(object) + if type(parent) == "string" then + if not AceLibrary:HasInstance(parent) then + return false + else + parent = AceLibrary(parent) + end + end + AceOO:argCheck(parent, 2, "table") + if type(object) ~= "table" then + return false + end + local current + local class = deeprawget(object, 'class') + if class then + current = class + else + current = object + end + if type(current) ~= "table" then + return false + end + if rawequal(current, parent) then + return true + end + if parent.class then + while true do + if rawequal(current, Object) then + break + end + if current.mixins then + for mixin in pairs(current.mixins) do + if rawequal(mixin, parent) then + return true + end + end + end + if current.interfaces then + for interface in pairs(current.interfaces) do + if rawequal(interface, parent) then + return true + end + end + end + current = deeprawget(current, 'super') + if type(current) ~= "table" then + break + end + end + + local isInterface = false + local curr = parent.class + while true do + if rawequal(curr, Object) then + break + elseif rawequal(curr, Interface) then + isInterface = true + break + end + curr = deeprawget(curr, 'super') + if type(curr) ~= "table" then + break + end + end + return isInterface and validateInterface(object, parent) + else + while true do + if rawequal(current, parent) then + return true + elseif rawequal(current, Object) then + return false + end + current = deeprawget(current, 'super') + if type(current) ~= "table" then + return false + end + end + end +end + +-- @table Class +-- @brief An object factory which sets up inheritence and supports +-- 'mixins'. +-- +-- @metamethod Class call +-- @brief Call ClassFactory:new() to create a new class. +-- +-- @method Class new +-- @brief Construct a new object. +-- @param (...) Arguments to pass to the object init function. +-- @return The new object. +-- +-- @method Class init +-- @brief Initialize a new class. +-- @param parent Superclass. +-- @param (...) Mixins. +-- +-- @method Class ToString +-- @return A string representing the object, in this case 'Class'. +local initStatus +local Class +local Mixin +local autoEmbed = false +local function traverseInterfaces(bit, total) + if bit.superinterfaces then + for interface in pairs(bit.superinterfaces) do + if not total[interface] then + total[interface] = true + traverseInterfaces(interface, total) + end + end + end +end +local class_new +do + Class = Factory(Object, setmetatable({}, {__index = Object}), Object) + Class.super = Object + + local function protostring(t) + return '<' .. tostring(t.class) .. ' prototype>' + end + local function classobjectstring(t) + if t.ToString then + return t:ToString() + elseif t.GetLibraryVersion then + return (t:GetLibraryVersion()) + else + return '<' .. tostring(t.class) .. ' instance>' + end + end + local function classobjectequal(self, other) + if type(self) == "table" and self.Equals then + return self:Equals(other) + elseif type(other) == "table" and other.Equals then + return other:Equals(self) + elseif type(self) == "table" and self.CompareTo then + return self:CompareTo(other) == 0 + elseif type(other) == "table" and other.CompareTo then + return other:CompareTo(self) == 0 + else + return rawequal(self, other) + end + end + local function classobjectlessthan(self, other) + if type(self) == "table" and self.IsLessThan then + return self:IsLessThan(other) + elseif type(other) == "table" and other.IsLessThanOrEqualTo then + return not other:IsLessThanOrEqualTo(self) + elseif type(self) == "table" and self.CompareTo then + return self:CompareTo(other) < 0 + elseif type(other) == "table" and other.CompareTo then + return other:CompareTo(self) > 0 + elseif type(other) == "table" and other.IsLessThan and other.Equals then + return other:Equals(self) or other:IsLessThan(self) + else + AceOO:error("cannot compare two objects") + end + end + local function classobjectlessthanequal(self, other) + if type(self) == "table" and self.IsLessThanOrEqualTo then + return self:IsLessThanOrEqualTo(other) + elseif type(other) == "table" and other.IsLessThan then + return not other:IsLessThan(self) + elseif type(self) == "table" and self.CompareTo then + return self:CompareTo(other) <= 0 + elseif type(other) == "table" and other.CompareTo then + return other:CompareTo(self) >= 0 + elseif type(self) == "table" and self.IsLessThan and self.Equals then + return self:Equals(other) or self:IsLessThan(other) + else + AceOO:error("cannot compare two incompatible objects") + end + end + local function classobjectadd(self, other) + if type(self) == "table" and self.Add then + return self:Add(other) + else + AceOO:error("cannot add two incompatible objects") + end + end + local function classobjectsub(self, other) + if type(self) == "table" and self.Subtract then + return self:Subtract(other) + else + AceOO:error("cannot subtract two incompatible objects") + end + end + local function classobjectunm(self, other) + if type(self) == "table" and self.UnaryNegation then + return self:UnaryNegation(other) + else + AceOO:error("attempt to negate an incompatible object") + end + end + local function classobjectmul(self, other) + if type(self) == "table" and self.Multiply then + return self:Multiply(other) + else + AceOO:error("cannot multiply two incompatible objects") + end + end + local function classobjectdiv(self, other) + if type(self) == "table" and self.Divide then + return self:Divide(other) + else + AceOO:error("cannot divide two incompatible objects") + end + end + local function classobjectpow(self, other) + if type(self) == "table" and self.Exponent then + return self:Exponent(other) + else + AceOO:error("cannot exponentiate two incompatible objects") + end + end + local function classobjectconcat(self, other) + if type(self) == "table" and self.Concatenate then + return self:Concatenate(other) + else + AceOO:error("cannot concatenate two incompatible objects") + end + end + function class_new(self, ...) + if self.virtual then + AceOO:error("Cannot instantiate a virtual class.") + end + + local o = self.prototype + local newobj = {} + if o.class and o.class.instancemeta then + setmetatable(newobj, o.class.instancemeta) + else + Object:init(newobj, o) + end + + if self.interfaces and not self.interfacesVerified then + -- Verify the interfaces + + for interface in pairs(self.interfaces) do + for field,kind in pairs(interface.interface) do + if tostring(type(newobj[field])) ~= kind then + AceOO:error("Class did not satisfy all interfaces. %q is required to be a %s. It is a %s", field, kind, tostring(type(newobj[field]))) + end + end + end + self.interfacesVerified = true + end + local tmp = initStatus + initStatus = newobj + newobj:init(...) + if initStatus then + initStatus = tmp + AceOO:error("Initialization not completed, be sure to call the superclass's init method.") + return + end + initStatus = tmp + return newobj + end + local classmeta = { + __tostring = objtostring, + __call = function(self, ...) + return self:new(...) + end, + } + function Class:init(newclass, parent, ...) + parent = parent or self + + local total + + if parent.class then + total = { parent, ... } + parent = self + else + total = { ... } + end + if not inherits(parent, Class) then + AceOO:error("Classes must inherit from a proper class") + end + if parent.sealed then + AceOO:error("Cannot inherit from a sealed class") + end + for i,v in ipairs(total) do + if inherits(v, Mixin) and v.class then + if v.__deprecated then + AceOO:error(v.__deprecated) + end + if not newclass.mixins then + newclass.mixins = {} + end + if newclass.mixins[v] then + AceOO:error("Cannot explicitly inherit from the same mixin twice") + end + newclass.mixins[v] = true + elseif inherits(v, Interface) and v.class then + if not newclass.interfaces then + newclass.interfaces = {} + end + if newclass.interfaces[v] then + AceOO:error("Cannot explicitly inherit from the same interface twice") + end + newclass.interfaces[v] = true + else + AceOO:error("Classes can only inherit from one or zero classes and any number of mixins or interfaces") + end + end + if parent.interfaces then + if not newclass.interfaces then + newclass.interfaces = {} + end + for interface in pairs(parent.interfaces) do + newclass.interfaces[interface] = true + end + end + for k in pairs(total) do + total[k] = nil + end + + newclass.super = parent + + newclass.prototype = setmetatable(total, { + __index = parent.prototype, + __tostring = protostring, + }) + total = nil + + newclass.instancemeta = { + __index = newclass.prototype, + __tostring = classobjectstring, + __eq = classobjectequal, + __lt = classobjectlessthan, + __le = classobjectlessthanequal, + __add = classobjectadd, + __sub = classobjectsub, + __unm = classobjectunm, + __mul = classobjectmul, + __div = classobjectdiv, + __pow = classobjectpow, + __concat = classobjectconcat, + } + + setmetatable(newclass, classmeta) + + newclass.new = class_new + + if newclass.mixins then + -- Fold in the mixins + local err, msg + for mixin in pairs(newclass.mixins) do + local ret + autoEmbed = true + ret, msg = pcall(mixin.embed, mixin, newclass.prototype) + autoEmbed = false + if not ret then + err = true + break + end + end + + if err then + local pt = newclass.prototype + for k,v in pairs(pt) do + pt[k] = nil + end + + -- method conflict + AceOO:error(msg) + end + end + + newclass.prototype.class = newclass + + if newclass.interfaces then + for interface in pairs(newclass.interfaces) do + traverseInterfaces(interface, newclass.interfaces) + end + end + if newclass.mixins then + for mixin in pairs(newclass.mixins) do + if mixin.interfaces then + if not newclass.interfaces then + newclass.interfaces = {} + end + for interface in pairs(mixin.interfaces) do + newclass.interfaces[interface] = true + end + end + end + end + end + function Class:ToString() + if type(self.GetLibraryVersion) == "function" then + return (self:GetLibraryVersion()) + else + return "Class" + end + end + + local tmp + function Class.prototype:init() + if rawequal(self, initStatus) then + initStatus = nil + else + AceOO:error("Improper self passed to init. You must do MyClass.super.prototype.init(self, ...)", 2) + end + self.uid = getuid(self) + local current = self.class + while true do + if current == Class then + break + end + if current.mixins then + for mixin in pairs(current.mixins) do + if type(mixin.OnInstanceInit) == "function" then + mixin:OnInstanceInit(self) + end + end + end + current = current.super + end + end +end + + +-- @object ClassFactory +-- @brief A factory for creating classes. Rarely used directly. +local ClassFactory = Factory(Object, Class, Object) + +function Class:new(...) + local x = ClassFactory:new(...) + if AceOO.classes then + AceOO.classes[x] = true + end + return x +end +getmetatable(Class).__call = Class.new + +-- @class Mixin +-- @brief A class to create mixin objects, which contain methods that get +-- "mixed in" to class prototypes. +-- +-- @object Mixin prototype +-- @brief The prototype that mixin objects inherit their methods from. +-- +-- @method Mixin prototype embed +-- @brief Mix in the methods of our object which are listed in our interface +-- to the supplied target table. +-- +-- @method Mixin prototype init +-- @brief Initialize the mixin object. +-- @param newobj The new object we're initializing. +-- @param interface The interface we implement (the list of methods our +-- prototype provides which should be mixed into the target +-- table by embed). +do + Mixin = Class() + function Mixin:ToString() + if self.GetLibraryVersion then + return (self:GetLibraryVersion()) + else + return 'Mixin' + end + end + local function _Embed(state, field, target) + field = next(state.export, field) + if field == nil then + return + end + + if rawget(target, field) or (target[field] and target[field] ~= state[field]) then + AceOO:error("Method conflict in attempt to mixin. Field %q", field) + end + + target[field] = state[field] + + local ret,msg = pcall(_Embed, state, field, target) + if not ret then + -- Mix in the next method according to the defined interface. If that + -- fails due to a conflict, re-raise to back out the previous mixed + -- methods. + + target[field] = nil + AceOO:error(msg) + end + end + function Mixin.prototype:embed(target) + if self.__deprecated then + AceOO:error(self.__deprecated) + end + local mt = getmetatable(target) + setmetatable(target, nil) + local err, msg = pcall(_Embed, self, nil, target) + if not err then + setmetatable(target, mt) + AceOO:error(msg) + return + end + if type(self.embedList) == "table" then + self.embedList[target] = true + end + if type(target.class) ~= "table" then + target[self] = true + end + if not autoEmbed and type(self.OnManualEmbed) == "function" then + self:OnManualEmbed(target) + end + setmetatable(target, mt) + end + + function Mixin.prototype:activate(oldLib, oldDeactivate) + if oldLib and oldLib.embedList then + for target in pairs(oldLib.embedList) do + local mt = getmetatable(target) + setmetatable(target, nil) + for field in pairs(oldLib.export) do + target[field] = nil + end + setmetatable(target, mt) + end + self.embedList = oldLib.embedList + for target in pairs(self.embedList) do + self:embed(target) + end + else + self.embedList = setmetatable({}, {__mode="k"}) + end + end + + function Mixin.prototype:init(export, ...) + AceOO:argCheck(export, 2, "table") + for k,v in pairs(export) do + if type(k) ~= "number" then + AceOO:error("All keys to argument #2 must be numbers.") + elseif type(v) ~= "string" then + AceOO:error("All values to argument #2 must be strings.") + end + end + local num = #export + for i = 1, num do + local v = export[i] + export[i] = nil + export[v] = true + end + + local interfaces + if select('#', ...) >= 1 then + interfaces = { ... } + for i,v in ipairs(interfaces) do + v = getlibrary(v) + interfaces[i] = v + if not v.class or not inherits(v, Interface) then + AceOO:error("Mixins can inherit only from interfaces") + end + end + local num = #interfaces + for i = 1, num do + local v = interfaces[i] + interfaces[i] = nil + interfaces[v] = true + end + for interface in pairs(interfaces) do + traverseInterfaces(interface, interfaces) + end + for interface in pairs(interfaces) do + for field,kind in pairs(interface.interface) do + if kind ~= "nil" then + local good = false + for bit in pairs(export) do + if bit == field then + good = true + break + end + end + if not good then + AceOO:error("Mixin does not fully accommodate field %q", field) + end + end + end + end + end + self.super = Mixin.prototype + Mixin.super.prototype.init(self) + self.export = export + self.interfaces = interfaces + end +end + +-- @class Interface +-- @brief A class to create interfaces, which contain contracts that classes +-- which inherit from this must comply with. +-- +-- @object Interface prototype +-- @brief The prototype that interface objects must adhere to. +-- +-- @method Interface prototype init +-- @brief Initialize the mixin object. +-- @param interface The interface we contract (the hash of fields forced). +-- @param (...) Superinterfaces +do + Interface = Class() + function Interface:ToString() + if self.GetLibraryVersion then + return (self:GetLibraryVersion()) + else + return 'Instance' + end + end + function Interface.prototype:init(interface, ...) + Interface.super.prototype.init(self) + AceOO:argCheck(interface, 2, "table") + for k,v in pairs(interface) do + if type(k) ~= "string" then + AceOO:error("All keys to argument #2 must be numbers.") + elseif type(v) ~= "string" then + AceOO:error("All values to argument #2 must be strings.") + elseif v ~= "nil" and v ~= "string" and v ~= "number" and v ~= "table" and v ~= "function" then + AceOO:error('All values to argument #2 must either be "nil", "string", "number", "table", or "function".') + end + end + if select('#', ...) >= 1 then + self.superinterfaces = { ... } + for i,v in ipairs(self.superinterfaces) do + v = getlibrary(v) + self.superinterfaces[i] = v + if not inherits(v, Interface) or not v.class then + AceOO:error('Cannot provide a non-Interface to inherit from') + end + end + local num = #self.superinterfaces + for i = 1, num do + local v = self.superinterfaces[i] + self.superinterfaces[i] = nil + self.superinterfaces[v] = true + end + end + self.interface = interface + end +end + +-- @function Classpool +-- @brief Obtain a read only class from our pool of classes, indexed by the +-- superclass and mixins. +-- @param sc The superclass of the class we want. +-- @param (m1..m20) Mixins of the class we want's objects. +-- @return A read only class from the class pool. +local Classpool +do + local pool = setmetatable({}, {__mode = 'v'}) + local function newindex(k, v) + AceOO:error('Attempt to modify a read-only class.') + end + local function protonewindex(k, v) + AceOO:error('Attempt to modify a read-only class prototype.') + end + local function ts(bit) + if type(bit) ~= "table" then + return tostring(bit) + elseif getmetatable(bit) and bit.__tostring then + return tostring(bit) + elseif type(bit.GetLibraryVersion) == "function" then + return bit:GetLibraryVersion() + else + return tostring(bit) + end + end + local t = {} + local function getcomplexuid(sc, ...) + if sc then + if sc.uid then + table.insert(t, sc.uid) + else + AceOO:error("%s is not an appropriate class/mixin", ts(sc)) + end + end + for i = 1, select('#', ...) do + local m = select(i, ...) + if m.uid then + table.insert(t, m.uid) + else + AceOO:error("%s is not an appropriate mixin", ts(m)) + end + end + table.sort(t) + local uid = table.concat(t, '') + local num = #t + for i = 1, num do + t[i] = nil + end + return uid + end + local classmeta + local arg = {} + function Classpool(superclass, ...) + local l = getlibrary + superclass = getlibrary(superclass) + arg = { ... } + for i, v in ipairs(arg) do + arg[i] = getlibrary(v) + end + if superclass then + if superclass.class then -- mixin + table.insert(arg, 1, superclass) + superclass = Class + end + else + superclass = Class + end + local key = getcomplexuid(superclass, unpack(arg)) + if not pool[key] then + local class = Class(superclass, unpack(arg)) + if not classmeta then + classmeta = {} + local mt = getmetatable(class) + for k,v in pairs(mt) do + classmeta[k] = v + end + classmeta.__newindex = newindex + end + -- Prevent the user from adding methods to this class. + -- NOTE: I'm not preventing modifications of existing class members, + -- but it's likely that only a truly malicious user will be doing so. + class.sealed = true + setmetatable(class, classmeta) + getmetatable(class.prototype).__newindex = protonewindex + pool[key] = class + end + return pool[key] + end +end + +AceOO.Factory = Factory +AceOO.Object = Object +AceOO.Class = Class +AceOO.Mixin = Mixin +AceOO.Interface = Interface +AceOO.Classpool = Classpool +AceOO.inherits = inherits + +-- Library handling bits + +local function activate(self, oldLib, oldDeactivate) + AceOO = self + Factory = self.Factory + Object = self.Object + Class = self.Class + ClassFactory.prototype = Class + Mixin = self.Mixin + Interface = self.Interface + Classpool = self.Classpool + + if oldLib then + self.classes = oldLib.classes + end + if not self.classes then + self.classes = setmetatable({}, {__mode="k"}) + else + for class in pairs(self.classes) do + class.new = class_new + end + end + + if oldDeactivate then + oldDeactivate(oldLib) + end +end + +AceLibrary:Register(AceOO, MAJOR_VERSION, MINOR_VERSION, activate) +AceOO = AceLibrary(MAJOR_VERSION) diff --git a/Libs/AceOO-2.0/AceOO-2.0.toc b/Libs/AceOO-2.0/AceOO-2.0.toc index 3879748..d9ffd38 100644 --- a/Libs/AceOO-2.0/AceOO-2.0.toc +++ b/Libs/AceOO-2.0/AceOO-2.0.toc @@ -1,16 +1,16 @@ -## Interface: 30000 -## X-Curse-Packaged-Version: r1094 -## X-Curse-Project-Name: Ace2 -## X-Curse-Project-ID: ace2 -## X-Curse-Repository-ID: wow/ace2/mainline - -## Title: Lib: AceOO-2.0 -## Notes: AddOn development framework -## Author: Ace Development Team -## LoadOnDemand: 1 -## X-Website: http://www.wowace.com -## X-Category: Library -## X-License: LGPL v2.1 + MIT for AceOO-2.0 -## Dependencies: AceLibrary - -AceOO-2.0.lua +## Interface: 30000 +## X-Curse-Packaged-Version: r1094 +## X-Curse-Project-Name: Ace2 +## X-Curse-Project-ID: ace2 +## X-Curse-Repository-ID: wow/ace2/mainline + +## Title: Lib: AceOO-2.0 +## Notes: AddOn development framework +## Author: Ace Development Team +## LoadOnDemand: 1 +## X-Website: http://www.wowace.com +## X-Category: Library +## X-License: LGPL v2.1 + MIT for AceOO-2.0 +## Dependencies: AceLibrary + +AceOO-2.0.lua