Mercurial > wow > reaction
diff lib/AceAddon-2.0/AceAddon-2.0.lua @ 22:1b9323256a1b
Merging in 1.0 dev tree
author | Flick <flickerstreak@gmail.com> |
---|---|
date | Fri, 07 Mar 2008 22:10:55 +0000 |
parents | libs/AceAddon-2.0/AceAddon-2.0.lua@c11ca1d8ed91 |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/AceAddon-2.0/AceAddon-2.0.lua Fri Mar 07 22:10:55 2008 +0000 @@ -0,0 +1,1394 @@ +--[[ +Name: AceAddon-2.0 +Revision: $Rev: 46764 $ +Developed by: The Ace Development Team (http://www.wowace.com/index.php/The_Ace_Development_Team) +Inspired By: Ace 1.x by Turan (turan@gryphon.com) +Website: http://www.wowace.com/ +Documentation: http://www.wowace.com/wiki/AceAddon-2.0 +SVN: http://svn.wowace.com/wowace/trunk/Ace2/AceAddon-2.0 +Description: Base for all Ace addons to inherit from. +Dependencies: AceLibrary, AceOO-2.0, AceEvent-2.0, (optional) AceConsole-2.0 +License: LGPL v2.1 +]] + +local MAJOR_VERSION = "AceAddon-2.0" +local MINOR_VERSION = "$Revision: 46764 $" + +-- This ensures the code is only executed if the libary doesn't already exist, or is a newer version +if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary.") end +if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end + +if not AceLibrary:HasInstance("AceOO-2.0") then error(MAJOR_VERSION .. " requires AceOO-2.0.") end + +local function safecall(func,...) + local success, err = pcall(func,...) + if not success then geterrorhandler()(err:find("%.lua:%d+:") and err or (debugstack():match("\n(.-: )in.-\n") or "") .. err) end +end +-- Localization +local STANDBY, TITLE, NOTES, VERSION, AUTHOR, DATE, CATEGORY, EMAIL, CREDITS, WEBSITE, CATEGORIES, ABOUT, LICENSE, PRINT_ADDON_INFO, DONATE, DONATE_DESC, HOWTO_DONATE_WINDOWS, HOWTO_DONATE_MAC +if GetLocale() == "deDE" then + STANDBY = "|cffff5050(Standby)|r" -- capitalized + + TITLE = "Titel" + NOTES = "Anmerkung" + VERSION = "Version" + AUTHOR = "Autor" + DATE = "Datum" + CATEGORY = "Kategorie" + EMAIL = "E-Mail" + WEBSITE = "Webseite" + CREDITS = "Credits" -- fix + LICENSE = "License" -- fix + + ABOUT = "Über" + PRINT_ADDON_INFO = "Gibt Addondaten aus" + DONATE = "Donate" -- fix + DONATE_DESC = "Give a much-needed donation to the author of this addon." -- fix + HOWTO_DONATE_WINDOWS = "Press Ctrl-A to select the link, then Ctrl-C to copy, then Alt-Tab out of the game, open your favorite web browser, and paste the link into the address bar." -- fix + HOWTO_DONATE_MAC = "Press Cmd-A to select the link, then Cmd-C to copy, then Cmd-Tab out of the game, open your favorite web browser, and paste the link into the address bar." -- fix + + CATEGORIES = { + ["Action Bars"] = "Aktionsleisten", + ["Auction"] = "Auktion", + ["Audio"] = "Audio", + ["Battlegrounds/PvP"] = "Schlachtfeld/PvP", + ["Buffs"] = "Stärkungszauber", + ["Chat/Communication"] = "Chat/Kommunikation", + ["Druid"] = "Druide", + ["Hunter"] = "Jäger", + ["Mage"] = "Magier", + ["Paladin"] = "Paladin", + ["Priest"] = "Priester", + ["Rogue"] = "Schurke", + ["Shaman"] = "Schamane", + ["Warlock"] = "Hexenmeister", + ["Warrior"] = "Krieger", + ["Healer"] = "Heiler", + ["Tank"] = "Tank", + ["Caster"] = "Zauberer", + ["Combat"] = "Kampf", + ["Compilations"] = "Zusammenstellungen", + ["Data Export"] = "Datenexport", + ["Development Tools"] = "Entwicklungs Tools", + ["Guild"] = "Gilde", + ["Frame Modification"] = "Frame Veränderungen", + ["Interface Enhancements"] = "Interface Verbesserungen", + ["Inventory"] = "Inventar", + ["Library"] = "Bibliotheken", + ["Map"] = "Karte", + ["Mail"] = "Post", + ["Miscellaneous"] = "Diverses", + ["Quest"] = "Quest", + ["Raid"] = "Schlachtzug", + ["Tradeskill"] = "Beruf", + ["UnitFrame"] = "Einheiten-Fenster", + } +elseif GetLocale() == "frFR" then + STANDBY = "|cffff5050(attente)|r" + + TITLE = "Titre" + NOTES = "Notes" + VERSION = "Version" + AUTHOR = "Auteur" + DATE = "Date" + CATEGORY = "Catégorie" + EMAIL = "E-mail" + WEBSITE = "Site web" + CREDITS = "Credits" -- fix + LICENSE = "License" -- fix + + ABOUT = "A propos" + PRINT_ADDON_INFO = "Afficher les informations sur l'addon" + DONATE = "Donate" -- fix + DONATE_DESC = "Give a much-needed donation to the author of this addon." -- fix + HOWTO_DONATE_WINDOWS = "Press Ctrl-A to select the link, then Ctrl-C to copy, then Alt-Tab out of the game, open your favorite web browser, and paste the link into the address bar." -- fix + HOWTO_DONATE_MAC = "Press Cmd-A to select the link, then Cmd-C to copy, then Cmd-Tab out of the game, open your favorite web browser, and paste the link into the address bar." -- fix + + CATEGORIES = { + ["Action Bars"] = "Barres d'action", + ["Auction"] = "Hôtel des ventes", + ["Audio"] = "Audio", + ["Battlegrounds/PvP"] = "Champs de bataille/JcJ", + ["Buffs"] = "Buffs", + ["Chat/Communication"] = "Chat/Communication", + ["Druid"] = "Druide", + ["Hunter"] = "Chasseur", + ["Mage"] = "Mage", + ["Paladin"] = "Paladin", + ["Priest"] = "Prêtre", + ["Rogue"] = "Voleur", + ["Shaman"] = "Chaman", + ["Warlock"] = "Démoniste", + ["Warrior"] = "Guerrier", + ["Healer"] = "Soigneur", + ["Tank"] = "Tank", + ["Caster"] = "Casteur", + ["Combat"] = "Combat", + ["Compilations"] = "Compilations", + ["Data Export"] = "Exportation de données", + ["Development Tools"] = "Outils de développement", + ["Guild"] = "Guilde", + ["Frame Modification"] = "Modification des fenêtres", + ["Interface Enhancements"] = "Améliorations de l'interface", + ["Inventory"] = "Inventaire", + ["Library"] = "Bibliothèques", + ["Map"] = "Carte", + ["Mail"] = "Courrier", + ["Miscellaneous"] = "Divers", + ["Quest"] = "Quêtes", + ["Raid"] = "Raid", + ["Tradeskill"] = "Métiers", + ["UnitFrame"] = "Fenêtres d'unité", + } +elseif GetLocale() == "koKR" then + STANDBY = "|cffff5050(사용가능)|r" + + TITLE = "제목" + NOTES = "노트" + VERSION = "버전" + AUTHOR = "저작자" + DATE = "날짜" + CATEGORY = "분류" + EMAIL = "전자 우편" + WEBSITE = "웹 사이트" + CREDITS = "공로자" + LICENSE = "라이센스" + + ABOUT = "정보" + PRINT_ADDON_INFO = "애드온에 대한 정보를 출력합니다." + DONATE = "기부" + DONATE_DESC = "이 애드온의 저작자에게 기부를 합니다." + HOWTO_DONATE_WINDOWS = "Ctrl-A를 눌려 링크를 선택후, Ctrl-C로 복사합니다. Alt-Tab 눌려 게임으로 부터 나간후 웹 브라우저를 엽니다. 복사된 링크를 주소 창에 붙여넣기 합니다." + HOWTO_DONATE_MAC = "Cmd-A를 눌려 링크를 선택후, Cmd-C로 복사합니다. Cmd-Tab 눌려 게임으로 부터 나간후 웹 브라우저를 엽니다. 복사된 링크를 주소 창에 붙여넣기 합니다." + + CATEGORIES = { + ["Action Bars"] = "액션바", + ["Auction"] = "경매", + ["Audio"] = "음향", + ["Battlegrounds/PvP"] = "전장/PvP", + ["Buffs"] = "버프", + ["Chat/Communication"] = "대화/의사소통", + ["Druid"] = "드루이드", + ["Hunter"] = "사냥꾼", + ["Mage"] = "마법사", + ["Paladin"] = "성기사", + ["Priest"] = "사제", + ["Rogue"] = "도적", + ["Shaman"] = "주술사", + ["Warlock"] = "흑마법사", + ["Warrior"] = "전사", + ["Healer"] = "힐러", + ["Tank"] = "탱커", + ["Caster"] = "캐스터", + ["Combat"] = "전투", + ["Compilations"] = "복합", + ["Data Export"] = "자료 출력", + ["Development Tools"] = "개발 도구", + ["Guild"] = "길드", + ["Frame Modification"] = "구조 변경", + ["Interface Enhancements"] = "인터페이스 강화", + ["Inventory"] = "인벤토리", + ["Library"] = "라이브러리", + ["Map"] = "지도", + ["Mail"] = "우편", + ["Miscellaneous"] = "기타", + ["Quest"] = "퀘스트", + ["Raid"] = "공격대", + ["Tradeskill"] = "전문기술", + ["UnitFrame"] = "유닛 프레임", + } +elseif GetLocale() == "zhTW" then + STANDBY = "|cffff5050(待命)|r" + + TITLE = "標題" + NOTES = "註記" + VERSION = "版本" + AUTHOR = "作者" + DATE = "日期" + CATEGORY = "類別" + EMAIL = "電子郵件" + WEBSITE = "網站" + CREDITS = "特別感謝" + LICENSE = "版權" + + ABOUT = "關於" + PRINT_ADDON_INFO = "顯示插件資訊。" + DONATE = "捐贈" + DONATE_DESC = "捐贈金錢給插件作者。" + HOWTO_DONATE_WINDOWS = "請按Ctrl-A選擇網站連結,Ctrl-C複製網址,Alt-Tab切換到電腦桌面,打開瀏覽器,在網址列貼上網址。" + HOWTO_DONATE_MAC = "請按Cmd-A選擇網站連結,Cmd-C複製網址,Cmd-Tab切換到電腦桌面,打開瀏覽器,在網址列貼上網址。" + + CATEGORIES = { + ["Action Bars"] = "動作條", + ["Auction"] = "拍賣", + ["Audio"] = "音效", + ["Battlegrounds/PvP"] = "戰場/PvP", + ["Buffs"] = "增益", + ["Chat/Communication"] = "聊天/通訊", + ["Druid"] = "德魯伊", + ["Hunter"] = "獵人", + ["Mage"] = "法師", + ["Paladin"] = "聖騎士", + ["Priest"] = "牧師", + ["Rogue"] = "盜賊", + ["Shaman"] = "薩滿", + ["Warlock"] = "術士", + ["Warrior"] = "戰士", + ["Healer"] = "治療者", + ["Tank"] = "坦克", + ["Caster"] = "施法者", + ["Combat"] = "戰鬥", + ["Compilations"] = "整合", + ["Data Export"] = "資料匯出", + ["Development Tools"] = "開發工具", + ["Guild"] = "公會", + ["Frame Modification"] = "框架修改", + ["Interface Enhancements"] = "介面增強", + ["Inventory"] = "庫存", + ["Library"] = "程式庫", + ["Map"] = "地圖", + ["Mail"] = "郵件", + ["Miscellaneous"] = "雜項", + ["Quest"] = "任務", + ["Raid"] = "團隊", + ["Tradeskill"] = "交易技能", + ["UnitFrame"] = "單位框架", + } +elseif GetLocale() == "zhCN" then + STANDBY = "|cffff5050(暂挂)|r" + + TITLE = "标题" + NOTES = "附注" + VERSION = "版本" + AUTHOR = "作者" + DATE = "日期" + CATEGORY = "分类" + EMAIL = "电子邮件" + WEBSITE = "网站" + CREDITS = "Credits" -- fix + LICENSE = "License" -- fix + + ABOUT = "关于" + PRINT_ADDON_INFO = "印列出插件信息" + DONATE = "Donate" -- fix + DONATE_DESC = "Give a much-needed donation to the author of this addon." -- fix + HOWTO_DONATE_WINDOWS = "Press Ctrl-A to select the link, then Ctrl-C to copy, then Alt-Tab out of the game, open your favorite web browser, and paste the link into the address bar." -- fix + HOWTO_DONATE_MAC = "Press Cmd-A to select the link, then Cmd-C to copy, then Cmd-Tab out of the game, open your favorite web browser, and paste the link into the address bar." -- fix + + CATEGORIES = { + ["Action Bars"] = "动作条", + ["Auction"] = "拍卖", + ["Audio"] = "音频", + ["Battlegrounds/PvP"] = "战场/PvP", + ["Buffs"] = "增益魔法", + ["Chat/Communication"] = "聊天/交流", + ["Druid"] = "德鲁伊", + ["Hunter"] = "猎人", + ["Mage"] = "法师", + ["Paladin"] = "圣骑士", + ["Priest"] = "牧师", + ["Rogue"] = "盗贼", + ["Shaman"] = "萨满祭司", + ["Warlock"] = "术士", + ["Warrior"] = "战士", +-- ["Healer"] = "治疗保障", +-- ["Tank"] = "近战控制", +-- ["Caster"] = "远程输出", + ["Combat"] = "战斗", + ["Compilations"] = "编译", + ["Data Export"] = "数据导出", + ["Development Tools"] = "开发工具", + ["Guild"] = "公会", + ["Frame Modification"] = "框架修改", + ["Interface Enhancements"] = "界面增强", + ["Inventory"] = "背包", + ["Library"] = "库", + ["Map"] = "地图", + ["Mail"] = "邮件", + ["Miscellaneous"] = "杂项", + ["Quest"] = "任务", + ["Raid"] = "团队", + ["Tradeskill"] = "商业技能", + ["UnitFrame"] = "头像框架", + } +elseif GetLocale() == "esES" then + STANDBY = "|cffff5050(espera)|r" + + TITLE = "Título" + NOTES = "Notas" + VERSION = "Versión" + AUTHOR = "Autor" + DATE = "Fecha" + CATEGORY = "Categoría" + EMAIL = "E-mail" + WEBSITE = "Web" + CREDITS = "Créditos" + LICENSE = "License" -- fix + + ABOUT = "Acerca de" + PRINT_ADDON_INFO = "Muestra información acerca del accesorio." + DONATE = "Donate" -- fix + DONATE_DESC = "Give a much-needed donation to the author of this addon." -- fix + HOWTO_DONATE_WINDOWS = "Press Ctrl-A to select the link, then Ctrl-C to copy, then Alt-Tab out of the game, open your favorite web browser, and paste the link into the address bar." -- fix + HOWTO_DONATE_MAC = "Press Cmd-A to select the link, then Cmd-C to copy, then Cmd-Tab out of the game, open your favorite web browser, and paste the link into the address bar." -- fix + + CATEGORIES = { + ["Action Bars"] = "Barras de Acción", + ["Auction"] = "Subasta", + ["Audio"] = "Audio", + ["Battlegrounds/PvP"] = "Campos de Batalla/JcJ", + ["Buffs"] = "Buffs", + ["Chat/Communication"] = "Chat/Comunicación", + ["Druid"] = "Druida", + ["Hunter"] = "Cazador", + ["Mage"] = "Mago", + ["Paladin"] = "Paladín", + ["Priest"] = "Sacerdote", + ["Rogue"] = "Pícaro", + ["Shaman"] = "Chamán", + ["Warlock"] = "Brujo", + ["Warrior"] = "Guerrero", + ["Healer"] = "Sanador", + ["Tank"] = "Tanque", + ["Caster"] = "Conjurador", + ["Combat"] = "Combate", + ["Compilations"] = "Compilaciones", + ["Data Export"] = "Exportar Datos", + ["Development Tools"] = "Herramientas de Desarrollo", + ["Guild"] = "Hermandad", + ["Frame Modification"] = "Modificación de Marcos", + ["Interface Enhancements"] = "Mejoras de la Interfaz", + ["Inventory"] = "Inventario", + ["Library"] = "Biblioteca", + ["Map"] = "Mapa", + ["Mail"] = "Correo", + ["Miscellaneous"] = "Misceláneo", + ["Quest"] = "Misión", + ["Raid"] = "Banda", + ["Tradeskill"] = "Habilidad de Comercio", + ["UnitFrame"] = "Marco de Unidades", + } +else -- enUS + STANDBY = "|cffff5050(standby)|r" + + TITLE = "Title" + NOTES = "Notes" + VERSION = "Version" + AUTHOR = "Author" + DATE = "Date" + CATEGORY = "Category" + EMAIL = "E-mail" + WEBSITE = "Website" + CREDITS = "Credits" + LICENSE = "License" + + ABOUT = "About" + PRINT_ADDON_INFO = "Show information about the addon." + DONATE = "Donate" + DONATE_DESC = "Give a much-needed donation to the author of this addon." + HOWTO_DONATE_WINDOWS = "Press Ctrl-A to select the link, then Ctrl-C to copy, then Alt-Tab out of the game, open your favorite web browser, and paste the link into the address bar." + HOWTO_DONATE_MAC = "Press Cmd-A to select the link, then Cmd-C to copy, then Cmd-Tab out of the game, open your favorite web browser, and paste the link into the address bar." + + CATEGORIES = { + ["Action Bars"] = "Action Bars", + ["Auction"] = "Auction", + ["Audio"] = "Audio", + ["Battlegrounds/PvP"] = "Battlegrounds/PvP", + ["Buffs"] = "Buffs", + ["Chat/Communication"] = "Chat/Communication", + ["Druid"] = "Druid", + ["Hunter"] = "Hunter", + ["Mage"] = "Mage", + ["Paladin"] = "Paladin", + ["Priest"] = "Priest", + ["Rogue"] = "Rogue", + ["Shaman"] = "Shaman", + ["Warlock"] = "Warlock", + ["Warrior"] = "Warrior", + ["Healer"] = "Healer", + ["Tank"] = "Tank", + ["Caster"] = "Caster", + ["Combat"] = "Combat", + ["Compilations"] = "Compilations", + ["Data Export"] = "Data Export", + ["Development Tools"] = "Development Tools", + ["Guild"] = "Guild", + ["Frame Modification"] = "Frame Modification", + ["Interface Enhancements"] = "Interface Enhancements", + ["Inventory"] = "Inventory", + ["Library"] = "Library", + ["Map"] = "Map", + ["Mail"] = "Mail", + ["Miscellaneous"] = "Miscellaneous", + ["Quest"] = "Quest", + ["Raid"] = "Raid", + ["Tradeskill"] = "Tradeskill", + ["UnitFrame"] = "UnitFrame", + } +end + +setmetatable(CATEGORIES, { __index = function(self, key) -- case-insensitive + local lowerKey = key:lower() + for k,v in pairs(CATEGORIES) do + if k:lower() == lowerKey then + return v + end + end +end }) + +-- Create the library object + +local AceOO = AceLibrary("AceOO-2.0") +local AceAddon = AceOO.Class() +local AceEvent +local AceConsole +local AceModuleCore + +function AceAddon:GetLocalizedCategory(name) + self:argCheck(name, 2, "string") + return CATEGORIES[name] or UNKNOWN +end + +function AceAddon:ToString() + return "AceAddon" +end + +local function print(text) + DEFAULT_CHAT_FRAME:AddMessage(text) +end + +function AceAddon:ADDON_LOADED(name) + local unregister = true + local initAddon = {} + while #self.nextAddon > 0 do + local addon = table.remove(self.nextAddon, 1) + if addon.possibleNames[name] then + table.insert(initAddon, addon) + else + unregister = nil + table.insert(self.skipAddon, addon) + end + end + self.nextAddon, self.skipAddon = self.skipAddon, self.nextAddon + if unregister then + AceAddon:UnregisterEvent("ADDON_LOADED") + end + while #initAddon > 0 do + local addon = table.remove(initAddon, 1) + table.insert(self.addons, addon) + if not self.addons[name] then + self.addons[name] = addon + end + addon.possibleNames = nil + self:InitializeAddon(addon, name) + end +end + +local function RegisterOnEnable(self) + if DEFAULT_CHAT_FRAME and DEFAULT_CHAT_FRAME.defaultLanguage then -- HACK + AceAddon.playerLoginFired = true + end + if AceAddon.playerLoginFired then + AceAddon.addonsStarted[self] = true + if (type(self.IsActive) ~= "function" or self:IsActive()) and (not AceModuleCore or not AceModuleCore:IsModule(self) or AceModuleCore:IsModuleActive(self)) then + AceAddon:ManualEnable(self) + end + else + if not AceAddon.addonsToOnEnable then + AceAddon.addonsToOnEnable = {} + end + table.insert(AceAddon.addonsToOnEnable, self) + end +end + +function AceAddon:InitializeAddon(addon, name) + if addon.name == nil then + addon.name = name + end + if GetAddOnMetadata then + -- TOC checks + if addon.title == nil then + addon.title = GetAddOnMetadata(name, "Title") + end + if type(addon.title) == "string" then + local num = addon.title:find(" |cff7fff7f %-Ace2%-|r$") + if num then + addon.title = addon.title:sub(1, num - 1) + end + addon.title = addon.title:trim() + end + if addon.notes == nil then + addon.notes = GetAddOnMetadata(name, "Notes") + end + if type(addon.notes) == "string" then + addon.notes = addon.notes:trim() + end + if addon.version == nil then + addon.version = GetAddOnMetadata(name, "Version") + end + if type(addon.version) == "string" then + if addon.version:find("%$Revision: (%d+) %$") then + addon.version = addon.version:gsub("%$Revision: (%d+) %$", "%1") + elseif addon.version:find("%$Rev: (%d+) %$") then + addon.version = addon.version:gsub("%$Rev: (%d+) %$", "%1") + elseif addon.version:find("%$LastChangedRevision: (%d+) %$") then + addon.version = addon.version:gsub("%$LastChangedRevision: (%d+) %$", "%1") + end + addon.version = addon.version:trim() + end + if addon.author == nil then + addon.author = GetAddOnMetadata(name, "Author") + end + if type(addon.author) == "string" then + addon.author = addon.author:trim() + end + if addon.credits == nil then + addon.credits = GetAddOnMetadata(name, "X-Credits") + end + if type(addon.credits) == "string" then + addon.credits = addon.credits:trim() + end + if addon.donate == nil then + addon.donate = GetAddOnMetadata(name, "X-Donate") + end + if type(addon.donate) == "string" then + addon.donate = addon.donate:trim() + end + if addon.date == nil then + addon.date = GetAddOnMetadata(name, "X-Date") or GetAddOnMetadata(name, "X-ReleaseDate") + end + if type(addon.date) == "string" then + if addon.date:find("%$Date: (.-) %$") then + addon.date = addon.date:gsub("%$Date: (.-) %$", "%1") + elseif addon.date:find("%$LastChangedDate: (.-) %$") then + addon.date = addon.date:gsub("%$LastChangedDate: (.-) %$", "%1") + end + addon.date = addon.date:trim() + end + + if addon.category == nil then + addon.category = GetAddOnMetadata(name, "X-Category") + end + if type(addon.category) == "string" then + addon.category = addon.category:trim() + end + if addon.email == nil then + addon.email = GetAddOnMetadata(name, "X-eMail") or GetAddOnMetadata(name, "X-Email") + end + if type(addon.email) == "string" then + addon.email = addon.email:trim() + end + if addon.license == nil then + addon.license = GetAddOnMetadata(name, "X-License") + end + if type(addon.license) == "string" then + addon.license = addon.license:trim() + end + if addon.website == nil then + addon.website = GetAddOnMetadata(name, "X-Website") + end + if type(addon.website) == "string" then + addon.website = addon.website:trim() + end + end + local current = addon.class + while true do + if current == AceOO.Class or not current then + break + end + if current.mixins then + for mixin in pairs(current.mixins) do + if type(mixin.OnEmbedInitialize) == "function" then + mixin:OnEmbedInitialize(addon, name) + end + end + end + current = current.super + end + local n = AceAddon.addonsToOnEnable and #AceAddon.addonsToOnEnable or 0 + + if type(addon.OnInitialize) == "function" then + safecall(addon.OnInitialize, addon, name) + end + if AceEvent then + AceEvent:TriggerEvent("Ace2_AddonInitialized", addon) + end + RegisterOnEnable(addon) + local n2 = AceAddon.addonsToOnEnable and #AceAddon.addonsToOnEnable or 0 + if n2 - n > 1 then + local mine = table.remove(AceAddon.addonsToOnEnable) + table.insert(AceAddon.addonsToOnEnable, n+1, mine) + end +end + +local aboutFrame +local function createAboutFrame() + aboutFrame = CreateFrame("Frame", "AceAddon20AboutFrame", UIParent, "DialogBoxFrame") + aboutFrame:SetWidth(500) + aboutFrame:SetHeight(400) + aboutFrame:SetPoint("CENTER") + aboutFrame:SetBackdrop({ + bgFile = [[Interface\DialogFrame\UI-DialogBox-Background]], + edgeFile = [[Interface\Tooltips\UI-Tooltip-Border]], + tile = true, tileSize = 16, edgeSize = 16, + insets = { left = 5, right = 5, top = 5, bottom = 5 } + }) + aboutFrame:SetBackdropColor(0,0,0,1) + + local donateButton = CreateFrame("Button", "AceAddon20AboutFrameDonateButton", aboutFrame, "UIPanelButtonTemplate2") + aboutFrame.donateButton = donateButton + donateButton:SetPoint("BOTTOMRIGHT", -20, 20) + _G.AceAddon20AboutFrameDonateButtonText:SetText(DONATE) + donateButton:SetWidth(_G.AceAddon20AboutFrameDonateButtonText:GetWidth()+20) + donateButton:SetScript("OnClick", function() + aboutFrame.currentAddon:OpenDonationFrame() + end) + + local text = aboutFrame:CreateFontString(nil, "OVERLAY", "GameFontHighlightLarge") + aboutFrame.title = text + text:SetPoint("TOP", 0, -5) + + aboutFrame:Hide() + + aboutFrame.lefts = {} + aboutFrame.rights = {} + aboutFrame.textLefts = {} + aboutFrame.textRights = {} + function aboutFrame:Clear() + self.title:SetText("") + for i = 1, #self.lefts do + self.lefts[i] = nil + self.rights[i] = nil + end + end + + function aboutFrame:AddLine(left, right) + aboutFrame.lefts[#aboutFrame.lefts+1] = left + aboutFrame.rights[#aboutFrame.rights+1] = right + end + + local aboutFrame_Show = aboutFrame.Show + function aboutFrame:Show(...) + local maxLeftWidth = 0 + local maxRightWidth = 0 + local textHeight = 0 + for i = 1, #self.lefts do + if not self.textLefts[i] then + local left = aboutFrame:CreateFontString(nil, "OVERLAY", "GameFontNormal") + self.textLefts[i] = left + local right = aboutFrame:CreateFontString(nil, "OVERLAY", "GameFontHighlight") + self.textRights[i] = right + if i == 1 then + left:SetPoint("TOPRIGHT", aboutFrame, "TOPLEFT", 75, -35) + else + left:SetPoint("TOPRIGHT", self.textLefts[i-1], "BOTTOMRIGHT", 0, -5) + end + right:SetPoint("LEFT", left, "RIGHT", 5, 0) + end + self.textLefts[i]:SetText(self.lefts[i] .. ":") + self.textRights[i]:SetText(self.rights[i]) + local leftWidth = self.textLefts[i]:GetWidth() + local rightWidth = self.textRights[i]:GetWidth() + textHeight = self.textLefts[i]:GetHeight() + if maxLeftWidth < leftWidth then + maxLeftWidth = leftWidth + end + if maxRightWidth < rightWidth then + maxRightWidth = rightWidth + end + end + for i = #self.lefts+1, #self.textLefts do + self.textLefts[i]:SetText('') + self.textRights[i]:SetText('') + end + aboutFrame:SetWidth(75 + maxRightWidth + 20) + aboutFrame:SetHeight(#self.lefts * (textHeight + 5) + 100) + + aboutFrame_Show(self, ...) + end + aboutFrame:Hide() + + createAboutFrame = nil +end +local donateFrame + +local function unobfuscateEmail(email) + return email:gsub(" AT ", "@"):gsub(" DOT ", ".") +end + +local function isGoodVariable(var) + return type(var) == "string" or type(var) == "number" +end +function AceAddon.prototype:PrintAddonInfo() + if createAboutFrame then + createAboutFrame() + end + aboutFrame:Clear() + local x + if isGoodVariable(self.title) then + x = tostring(self.title) + elseif isGoodVariable(self.name) then + x = tostring(self.name) + else + x = "<" .. tostring(self.class) .. " instance>" + end + if type(self.IsActive) == "function" then + if not self:IsActive() then + x = x .. " " .. STANDBY + end + end + aboutFrame.title:SetText(x) + + if isGoodVariable(self.version) then + aboutFrame:AddLine(VERSION, tostring(self.version)) + end + if isGoodVariable(self.notes) then + aboutFrame:AddLine(NOTES, tostring(self.notes)) + end + if isGoodVariable(self.author) then + aboutFrame:AddLine(AUTHOR, tostring(self.author)) + end + if isGoodVariable(self.credits) then + aboutFrame:AddLine(CREDITS, tostring(self.credits)) + end + if isGoodVariable(self.date) then + aboutFrame:AddLine(DATE, tostring(self.date)) + end + if self.category then + local category = CATEGORIES[self.category] + if category then + aboutFrame:AddLine(CATEGORY, tostring(self.category)) + end + end + if isGoodVariable(self.email) then + aboutFrame:AddLine(EMAIL, unobfuscateEmail(tostring(self.email))) + end + if isGoodVariable(self.website) then + aboutFrame:AddLine(WEBSITE, tostring(self.website)) + end + if isGoodVariable(self.license) then + aboutFrame:AddLine(LICENSE, tostring(self.license)) + end + + if donateFrame and donateFrame:IsShown() then + donateFrame:Hide() + end + + aboutFrame.currentAddon = self + + aboutFrame:Show() + + if self.donate then + aboutFrame.donateButton:Show() + else + aboutFrame.donateButton:Hide() + end +end + +local function createDonateFrame() + donateFrame = CreateFrame("Frame", "AceAddon20Frame", UIParent, "DialogBoxFrame") + + donateFrame:SetWidth(500) + donateFrame:SetHeight(200) + donateFrame:SetPoint("CENTER") + donateFrame:SetBackdrop({ + bgFile = [[Interface\DialogFrame\UI-DialogBox-Background]], + edgeFile = [[Interface\Tooltips\UI-Tooltip-Border]], + tile = true, tileSize = 16, edgeSize = 16, + insets = { left = 5, right = 5, top = 5, bottom = 5 } + }) + donateFrame:SetBackdropColor(0,0,0,1) + + local text = donateFrame:CreateFontString(nil, "OVERLAY", "GameFontHighlightLarge") + text:SetPoint("TOP", 0, -5) + text:SetText(DONATE) + + local howto = donateFrame:CreateFontString(nil, "OVERLAY", "GameFontHighlight") + howto:SetPoint("TOP", text, "BOTTOM", 0, -5) + howto:SetPoint("LEFT", 16, 0) + howto:SetPoint("RIGHT", -16, 0) + if not IsMacClient() then + -- Windows or Linux + howto:SetText(HOWTO_DONATE_WINDOWS) + else + howto:SetText(HOWTO_DONATE_MAC) + end + + local scrollFrame = CreateFrame("ScrollFrame", "AceAddon20FrameScrollFrame", donateFrame, "UIPanelScrollFrameTemplate") + scrollFrame:SetToplevel(true) + scrollFrame:SetPoint("TOP", -10, -76) + scrollFrame:SetWidth(455) + scrollFrame:SetHeight(70) + howto:SetPoint("BOTTOM", scrollFrame, "TOP") + + local editBox = CreateFrame("EditBox", nil, scrollFrame) + donateFrame.editBox = editBox + scrollFrame:SetScrollChild(editBox) + editBox:SetFontObject(ChatFontNormal) + editBox:SetMultiLine(true) + editBox:SetMaxLetters(99999) + editBox:SetWidth(450) + editBox:SetHeight(54) + editBox:SetPoint("BOTTOM", 5, 0) + editBox:SetJustifyH("LEFT") + editBox:SetJustifyV("TOP") + editBox:SetAutoFocus(false) + editBox:SetScript("OnTextChanged", function(this) + if this:GetText() ~= this.text then + this:SetText(this.text) + end + end) + editBox:SetScript("OnEscapePressed", function(this) + this:ClearFocus() + end) + createDonateFrame = nil +end + +local function fix(char) + return ("%%%02x"):format(char:byte()) +end + +local function urlencode(text) + return text:gsub("[^0-9A-Za-z]", fix) +end + +function AceAddon.prototype:OpenDonationFrame() + if createDonateFrame then + createDonateFrame() + end + local donate = self.donate + if type(donate) ~= "string" then + donate = "Wowace" + end + local style, data = (":"):split(donate, 2) + style = style:lower() + if style ~= "website" and style ~= "paypal" then + style = "wowace" + end + if style == "wowace" then + donateFrame.editBox.text = "http://www.wowace.com/wiki/Donations" + elseif style == "website" then + donateFrame.editBox.text = data + else -- PayPal + local text = "https://www.paypal.com/cgi-bin/webscr?cmd=_xclick&business=" .. urlencode(unobfuscateEmail(data)) + local name + if type(self.title) == "string" then + name = self.title + elseif type(self.name) == "string" then + name = self.name + end + if name then + name = name:gsub("|c%x%x%x%x%x%x%x%x", ""):gsub("|r", "") + text = text .. "&item_name=" .. urlencode(name) + end + donateFrame.editBox.text = text + end + donateFrame.editBox:SetText(donateFrame.editBox.text) + + if aboutFrame and aboutFrame:IsShown() then + aboutFrame:Hide() + end + + donateFrame:Show() + + donateFrame.editBox:SetFocus() +end + +local options +function AceAddon:GetAceOptionsDataTable(target) + return { + about = { + name = ABOUT, + desc = PRINT_ADDON_INFO, + type = "execute", + func = "PrintAddonInfo", + order = -1, + }, + donate = { + name = DONATE, + desc = DONATE_DESC, + type = "execute", + func = "OpenDonationFrame", + order = -1, + hidden = function() + return not target.donate + end + } + } +end + +function AceAddon:PLAYER_LOGIN() + self.playerLoginFired = true + if self.addonsToOnEnable then + while #self.addonsToOnEnable > 0 do + local addon = table.remove(self.addonsToOnEnable, 1) + self.addonsStarted[addon] = true + if (type(addon.IsActive) ~= "function" or addon:IsActive()) and (not AceModuleCore or not AceModuleCore:IsModule(addon) or AceModuleCore:IsModuleActive(addon)) then + AceAddon:ManualEnable(addon) + end + end + self.addonsToOnEnable = nil + end +end + +function AceAddon.prototype:Inject(t) + AceAddon:argCheck(t, 2, "table") + for k,v in pairs(t) do + self[k] = v + end +end + +function AceAddon.prototype:init() + if not AceEvent then + error(MAJOR_VERSION .. " requires AceEvent-2.0", 4) + end + AceAddon.super.prototype.init(self) + + self.super = self.class.prototype + + AceAddon:RegisterEvent("ADDON_LOADED", "ADDON_LOADED") + local names = {} + for i = 1, GetNumAddOns() do + if IsAddOnLoaded(i) then names[GetAddOnInfo(i)] = true end + end + self.possibleNames = names + table.insert(AceAddon.nextAddon, self) +end + +function AceAddon.prototype:ToString() + local x + if type(self.title) == "string" then + x = self.title + elseif type(self.name) == "string" then + x = self.name + else + x = "<" .. tostring(self.class) .. " instance>" + end + if (type(self.IsActive) == "function" and not self:IsActive()) or (AceModuleCore and AceModuleCore:IsModule(addon) and AceModuleCore:IsModuleActive(addon)) then + x = x .. " " .. STANDBY + end + return x +end + +AceAddon.new = function(self, ...) + local class = AceAddon:pcall(AceOO.Classpool, self, ...) + return class:new() +end + +function AceAddon:ManualEnable(addon) + AceAddon:argCheck(addon, 2, "table") + local first = nil + if AceOO.inherits(addon, "AceAddon-2.0") then + if AceAddon.addonsEnabled and not AceAddon.addonsEnabled[addon] then + first = true + AceAddon.addonsEnabled[addon] = true + end + end + local current = addon.class + while current and current ~= AceOO.Class do + if current.mixins then + for mixin in pairs(current.mixins) do + if type(mixin.OnEmbedEnable) == "function" then + safecall(mixin.OnEmbedEnable, mixin, addon, first) + end + end + end + current = current.super + end + if type(addon.OnEnable) == "function" then + safecall(addon.OnEnable, addon, first) + end + if AceEvent then + AceEvent:TriggerEvent("Ace2_AddonEnabled", addon, first) + end +end + +function AceAddon:ManualDisable(addon) + AceAddon:argCheck(addon, 2, "table") + local current = addon.class + while current and current ~= AceOO.Class do + if current.mixins then + for mixin in pairs(current.mixins) do + if type(mixin.OnEmbedDisable) == "function" then + safecall(mixin.OnEmbedDisable, mixin, addon) + end + end + end + current = current.super + end + if type(module.OnDisable) == "function" then + safecall(module.OnDisable, addon) + end + if AceEvent then + AceEvent:TriggerEvent("Ace2_AddonDisabled", addon) + end +end + +local function external(self, major, instance) + if major == "AceEvent-2.0" then + AceEvent = instance + + AceEvent:embed(self) + + self:RegisterEvent("PLAYER_LOGIN", "PLAYER_LOGIN", true) + elseif major == "AceConsole-2.0" then + AceConsole = instance + + local slashCommands = { "/ace2" } + local _,_,_,enabled,loadable = GetAddOnInfo("Ace") + if not enabled or not loadable then + table.insert(slashCommands, "/ace") + end + local function listAddon(addon, depth) + if not depth then + depth = 0 + end + + local s = (" "):rep(depth) .. " - " .. tostring(addon) + if rawget(addon, 'version') then + s = s .. " - |cffffff7f" .. tostring(addon.version) .. "|r" + end + if rawget(addon, 'slashCommand') then + s = s .. " |cffffff7f(" .. tostring(addon.slashCommand) .. ")|r" + end + print(s) + if type(rawget(addon, 'modules')) == "table" then + local i = 0 + for k,v in pairs(addon.modules) do + i = i + 1 + if i == 6 then + print((" "):rep(depth + 1) .. " - more...") + break + else + listAddon(v, depth + 1) + end + end + end + end + local function listNormalAddon(i) + local name,_,_,enabled,loadable = GetAddOnInfo(i) + if not loadable then + enabled = false + end + if self.addons[name] then + listAddon(self.addons[name]) + else + local s = " - " .. tostring(GetAddOnMetadata(i, "Title") or name) + local version = GetAddOnMetadata(i, "Version") + if version then + if version:find("%$Revision: (%d+) %$") then + version = version:gsub("%$Revision: (%d+) %$", "%1") + elseif version:find("%$Rev: (%d+) %$") then + version = version:gsub("%$Rev: (%d+) %$", "%1") + elseif version:find("%$LastChangedRevision: (%d+) %$") then + version = version:gsub("%$LastChangedRevision: (%d+) %$", "%1") + end + s = s .. " - |cffffff7f" .. version .. "|r" + end + if not enabled then + s = s .. " |cffff0000(disabled)|r" + end + if IsAddOnLoadOnDemand(i) then + s = s .. " |cff00ff00[LoD]|r" + end + print(s) + end + end + local function mySort(alpha, bravo) + return tostring(alpha) < tostring(bravo) + end + AceConsole.RegisterChatCommand(self, slashCommands, { + desc = "AddOn development framework", + name = "Ace2", + type = "group", + args = { + about = { + desc = "Get information about Ace2", + name = "About", + type = "execute", + func = function() + print("|cffffff7fAce2|r - |cffffff7f2.0." .. MINOR_VERSION:gsub("%$Revision: (%d+) %$", "%1") .. "|r - AddOn development framework") + print(" - |cffffff7f" .. AUTHOR .. ":|r Ace Development Team") + print(" - |cffffff7f" .. WEBSITE .. ":|r http://www.wowace.com/") + end + }, + list = { + desc = "List addons", + name = "List", + type = "group", + args = { + ace2 = { + desc = "List addons using Ace2", + name = "Ace2", + type = "execute", + func = function() + print("|cffffff7fAddon list:|r") + table.sort(self.addons, mySort) + for _,v in ipairs(self.addons) do + listAddon(v) + end + end + }, + all = { + desc = "List all addons", + name = "All", + type = "execute", + func = function() + print("|cffffff7fAddon list:|r") + local count = GetNumAddOns() + for i = 1, count do + listNormalAddon(i) + end + end + }, + enabled = { + desc = "List all enabled addons", + name = "Enabled", + type = "execute", + func = function() + print("|cffffff7fAddon list:|r") + local count = GetNumAddOns() + for i = 1, count do + local _,_,_,enabled,loadable = GetAddOnInfo(i) + if enabled and loadable then + listNormalAddon(i) + end + end + end + }, + disabled = { + desc = "List all disabled addons", + name = "Disabled", + type = "execute", + func = function() + print("|cffffff7fAddon list:|r") + local count = GetNumAddOns() + for i = 1, count do + local _,_,_,enabled,loadable = GetAddOnInfo(i) + if not enabled or not loadable then + listNormalAddon(i) + end + end + end + }, + lod = { + desc = "List all LoadOnDemand addons", + name = "LoadOnDemand", + type = "execute", + func = function() + print("|cffffff7fAddon list:|r") + local count = GetNumAddOns() + for i = 1, count do + if IsAddOnLoadOnDemand(i) then + listNormalAddon(i) + end + end + end + }, + ace1 = { + desc = "List all addons using Ace1", + name = "Ace 1.x", + type = "execute", + func = function() + print("|cffffff7fAddon list:|r") + local count = GetNumAddOns() + for i = 1, count do + local dep1, dep2, dep3, dep4 = GetAddOnDependencies(i) + if dep1 == "Ace" or dep2 == "Ace" or dep3 == "Ace" or dep4 == "Ace" then + listNormalAddon(i) + end + end + end + }, + libs = { + desc = "List all libraries using AceLibrary", + name = "Libraries", + type = "execute", + func = function() + if type(AceLibrary) == "table" and type(AceLibrary.libs) == "table" then + print("|cffffff7fLibrary list:|r") + for name, data in pairs(AceLibrary.libs) do + local s + if data.minor then + s = " - " .. tostring(name) .. "." .. tostring(data.minor) + else + s = " - " .. tostring(name) + end + if rawget(AceLibrary(name), 'slashCommand') then + s = s .. " |cffffff7f(" .. tostring(AceLibrary(name).slashCommand) .. "|cffffff7f)" + end + print(s) + end + end + end + }, + search = { + desc = "Search by name", + name = "Search", + type = "text", + usage = "<keyword>", + input = true, + get = false, + set = function(...) + local arg = { ... } + for i,v in ipairs(arg) do + arg[i] = v:gsub('%*', '.*'):gsub('%%', '%%%%'):lower() + end + local count = GetNumAddOns() + for i = 1, count do + local name = GetAddOnInfo(i) + local good = true + for _,v in ipairs(arg) do + if not name:lower():find(v) then + good = false + break + end + end + if good then + listNormalAddon(i) + end + end + end + } + }, + }, + enable = { + desc = "Enable addon(s).", + name = "Enable", + type = "text", + usage = "<addon 1> <addon 2> ...", + get = false, + input = true, + set = function(...) + for i = 1, select("#", ...) do + local addon = select(i, ...) + local name, title, _, enabled, _, reason = GetAddOnInfo(addon) + if reason == "MISSING" then + print(("|cffffff7fAce2:|r AddOn %q does not exist."):format(addon)) + elseif not enabled then + EnableAddOn(addon) + print(("|cffffff7fAce2:|r %s is now enabled."):format(addon or name)) + else + print(("|cffffff7fAce2:|r %s is already enabled."):format(addon or name)) + end + end + end, + }, + disable = { + desc = "Disable addon(s).", + name = "Disable", + type = "text", + usage = "<addon 1> <addon 2> ...", + get = false, + input = true, + set = function(...) + for i = 1, select("#", ...) do + local addon = select(i, ...) + local name, title, _, enabled, _, reason = GetAddOnInfo(addon) + if reason == "MISSING" then + print(("|cffffff7fAce2:|r AddOn %q does not exist."):format(addon)) + elseif enabled then + DisableAddOn(addon) + print(("|cffffff7fAce2:|r %s is now disabled."):format(addon or name)) + else + print(("|cffffff7fAce2:|r %s is already disabled."):format(addon or name)) + end + end + end, + }, + load = { + desc = "Load addon(s).", + name = "Load", + type = "text", + usage = "<addon 1> <addon 2> ...", + get = false, + input = true, + set = function(...) + for i = 1, select("#", ...) do + local addon = select(i, ...) + local name, title, _, _, loadable, reason = GetAddOnInfo(addon) + if reason == "MISSING" then + print(("|cffffff7fAce2:|r AddOn %q does not exist."):format(addon)) + elseif not loadable then + print(("|cffffff7fAce2:|r AddOn %q is not loadable. Reason: %s."):format(addon, reason)) + else + LoadAddOn(addon) + print(("|cffffff7fAce2:|r %s is now loaded."):format(addon or name)) + end + end + end + }, + info = { + desc = "Display information", + name = "Information", + type = "execute", + func = function() + local mem, threshold = gcinfo() + print((" - |cffffff7fMemory usage [|r%.3f MiB|cffffff7f]|r"):format(mem / 1024)) + if threshold then + print((" - |cffffff7fThreshold [|r%.3f MiB|cffffff7f]|r"):format(threshold / 1024)) + end + print((" - |cffffff7fFramerate [|r%.0f fps|cffffff7f]|r"):format(GetFramerate())) + local bandwidthIn, bandwidthOut, latency = GetNetStats() + bandwidthIn, bandwidthOut = floor(bandwidthIn * 1024), floor(bandwidthOut * 1024) + print((" - |cffffff7fLatency [|r%.0f ms|cffffff7f]|r"):format(latency)) + print((" - |cffffff7fBandwidth in [|r%.0f B/s|cffffff7f]|r"):format(bandwidthIn)) + print((" - |cffffff7fBandwidth out [|r%.0f B/s|cffffff7f]|r"):format(bandwidthOut)) + print((" - |cffffff7fTotal addons [|r%d|cffffff7f]|r"):format(GetNumAddOns())) + print((" - |cffffff7fAce2 addons [|r%d|cffffff7f]|r"):format(#self.addons)) + local ace = 0 + local enabled = 0 + local disabled = 0 + local lod = 0 + for i = 1, GetNumAddOns() do + local dep1, dep2, dep3, dep4 = GetAddOnDependencies(i) + if dep1 == "Ace" or dep2 == "Ace" or dep3 == "Ace" or dep4 == "Ace" then + ace = ace + 1 + end + if IsAddOnLoadOnDemand(i) then + lod = lod + 1 + end + local isActive, loadable = select(4, GetAddOnInfo(i)) + if not isActive or not loadable then + disabled = disabled + 1 + else + enabled = enabled + 1 + end + end + print((" - |cffffff7fAce 1.x addons [|r%d|cffffff7f]|r"):format(ace)) + print((" - |cffffff7fLoadOnDemand addons [|r%d|cffffff7f]|r"):format(lod)) + print((" - |cffffff7fenabled addons [|r%d|cffffff7f]|r"):format(enabled)) + print((" - |cffffff7fdisabled addons [|r%d|cffffff7f]|r"):format(disabled)) + local libs = 0 + if type(AceLibrary) == "table" and type(AceLibrary.libs) == "table" then + for _ in pairs(AceLibrary.libs) do + libs = libs + 1 + end + end + print((" - |cffffff7fAceLibrary instances [|r%d|cffffff7f]|r"):format(libs)) + end + } + } + }) + elseif major == "AceModuleCore-2.0" then + AceModuleCore = instance + end +end + +local function activate(self, oldLib, oldDeactivate) + AceAddon = self + + self.playerLoginFired = oldLib and oldLib.playerLoginFired or DEFAULT_CHAT_FRAME and DEFAULT_CHAT_FRAME.defaultLanguage + self.addonsToOnEnable = oldLib and oldLib.addonsToOnEnable + self.addons = oldLib and oldLib.addons or {} + self.nextAddon = oldLib and oldLib.nextAddon or {} + self.skipAddon = oldLib and oldLib.skipAddon or {} + self.addonsStarted = oldLib and oldLib.addonsStarted or {} + self.addonsEnabled = oldLib and oldLib.addonsEnabled or {} + + if oldDeactivate then + oldDeactivate(oldLib) + end +end + +AceLibrary:Register(AceAddon, MAJOR_VERSION, MINOR_VERSION, activate, nil, external)