├── README.md ├── SuperAPI.lua ├── SuperAPI.toc ├── SuperAPIOptions.lua └── libs ├── AceAddon-2.0 └── AceAddon-2.0.lua ├── AceConsole-2.0 └── AceConsole-2.0.lua ├── AceDB-2.0 └── AceDB-2.0.lua ├── AceDebug-2.0 └── AceDebug-2.0.lua ├── AceEvent-2.0 └── AceEvent-2.0.lua ├── AceHook-2.1 └── AceHook-2.1.lua ├── AceLibrary └── AceLibrary.lua ├── AceLocale-2.2 └── AceLocale-2.2.lua ├── AceModuleCore-2.0 └── AceModuleCore-2.0.lua ├── AceOO-2.0 └── AceOO-2.0.lua ├── Dewdrop └── Dewdrop-2.0.lua ├── FuBarPlugin └── FuBarPlugin-2.0.lua └── Tablet └── Tablet-2.0.lua /README.md: -------------------------------------------------------------------------------- 1 | ### Addon to allow compability between the mod and vanilla default interface. Also adds a minimap icon to change some mod settings. It is recommended for addon makers to read this to get a gist of some of the mod's features 2 | 3 | ### Settings 4 | Right click the minimap icon to choose persistent options for: 5 | - Autoloot 6 | - Clickthrough 7 | - GUID in combat log/events 8 | - FoV 9 | - Background Sound 10 | - Uncapped Sounds 11 | - Targetting circle style 12 | 13 | **NOTE: If using Vanilla Tweaks quickloot all of the Autoloot options will be reversed (always on will actually be always off, Shift to toggle on will be Shift to toggle off etc).** 14 | 15 | ### Other features 16 | - Adds "mouseover" support to all default blizzard unitframes 17 | - Shows item charges in bag 18 | - Allows Shift clicking a spell in your spellbook to link it to chat 19 | - Adds chat command to toggle showing unit names or GUIDs in combat log 20 | 21 | ## Modules: 22 | https://github.com/balakethelock/SuperAPI_Castlib provides improved castbars, alternatively the latest version of pfui/shaguplates now do the same 23 | -------------------------------------------------------------------------------- /SuperAPI.lua: -------------------------------------------------------------------------------- 1 | -- No superwow, no superapi 2 | if not SUPERWOW_VERSION then 3 | DEFAULT_CHAT_FRAME:AddMessage("No SuperWoW detected"); 4 | -- this version of SuperAPI is made for SuperWoW 1.2 5 | -- can somebody make this warning better? 6 | return 7 | end 8 | 9 | SUPERAPI_ContainerItemsTable = {} 10 | 11 | SuperAPI = AceLibrary("AceAddon-2.0"):new("AceEvent-2.0", "AceDebug-2.0", "AceModuleCore-2.0", "AceConsole-2.0", "AceDB-2.0", "AceHook-2.1") 12 | SuperAPI:RegisterDB("SuperAPIDB") 13 | SuperAPI.frame = CreateFrame("Frame", "SuperAPI", UIParent) 14 | 15 | function SuperAPI:OnEnable() 16 | -- Let macro frame allow 511 characters 17 | MacroFrame_LoadUI(); 18 | MacroFrameText:SetMaxLetters(511); 19 | MACROFRAME_CHAR_LIMIT = "%d/511 Characters Used"; 20 | 21 | -- Change chat bubbles options name 22 | OPTION_TOOLTIP_PARTY_CHAT_BUBBLES = "Shows whisper, party, raid, and battleground chat text in speech bubbles above characters' heads."; 23 | PARTY_CHAT_BUBBLES_TEXT = "Show Whisper and Group Chat Bubbles"; 24 | 25 | SuperAPI.SetItemRefOriginal = SetItemRef 26 | SuperAPI.SpellButton_OnClickOriginal = SpellButton_OnClick 27 | SuperAPI.SetItemButtonCountOriginal = SetItemButtonCount 28 | SuperAPI.SetActionOriginal = GameTooltip.SetAction 29 | SuperAPI.UnitFrame_OnEnterOriginal = UnitFrame_OnEnter 30 | SuperAPI.UnitFrame_OnLeaveOriginal = UnitFrame_OnLeave 31 | SuperAPI.CombatText_AddMessageOriginal = CombatText_AddMessage 32 | 33 | -- activate hooks 34 | SetItemRef = SuperAPI.SetItemRef 35 | SpellButton_OnClick = SuperAPI.SpellButton_OnClick 36 | SetItemButtonCount = SuperAPI.SetItemButtonCount 37 | GameTooltip.SetAction = SuperAPI.SetAction 38 | UnitFrame_OnEnter = SuperAPI.UnitFrame_OnEnter 39 | UnitFrame_OnLeave = SuperAPI.UnitFrame_OnLeave 40 | CombatText_AddMessage = SuperAPI.CombatText_AddMessage 41 | 42 | 43 | -- SuperAPI.frame:RegisterEvent("BAG_UPDATE") 44 | -- SuperAPI.frame:RegisterEvent("BAG_UPDATE_COOLDOWN") 45 | SuperAPI.frame:SetScript("OnEvent", SuperAPI.OnEvent) 46 | 47 | -- this chatcommand is empty. It is essential for showing tooltips of macros 48 | -- the format for showing a tooltip on a macro is EXACTLY this: /tooltip spell:spellid and then skip line 49 | SLASH_MACROTOOLTIP1 = "/tooltip" 50 | SlashCmdList["MACROTOOLTIP"] = function(cmd) 51 | end 52 | DEFAULT_CHAT_FRAME:AddMessage("|cffffcc00SuperAPI|cffffaaaa Loaded. Check the minimap icon for options.") 53 | end 54 | 55 | function SuperAPI:OnEvent() 56 | if (event == "BAG_UPDATE_COOLDOWN" or event == "BAG_UPDATE") then 57 | SUPERAPI_ContainerItemsTable = {} 58 | for ibag = 0, 4 do 59 | for islot = 1, GetContainerNumSlots(ibag) do 60 | local bagitemlink = GetContainerItemLink(ibag, islot) 61 | if bagitemlink then 62 | local _, _, bagitemID = strfind(bagitemlink, "item:(%d+)") 63 | bagitemID = tonumber(bagitemID) 64 | SUPERAPI_ContainerItemsTable[bagitemID] = { bag = ibag; slot = islot } 65 | end 66 | end 67 | end 68 | end 69 | end 70 | 71 | -- HOOKS -- 72 | -- Global function to get a spell link from its exact id 73 | SuperAPI.GetSpellLink = function(id) 74 | local spellname = SpellInfo(id) 75 | local link = "\124cffffffff\124Henchant:" .. id .. "\124h[" .. spellname .. "]\124h\124r" 76 | return link 77 | end 78 | 79 | -- reformat "Enchant" itemlinks to better supported "Spell" itemlinks 80 | SuperAPI.SetItemRef = function(link, text, button) 81 | link = gsub(link, "spell:", "enchant:") 82 | SuperAPI.SetItemRefOriginal(link, text, button) 83 | end 84 | 85 | -- hooking spellbook frame to get a spell link on shift clicking a spell's button with chatframe open 86 | SuperAPI.SpellButton_OnClick = function(drag) 87 | if ((not drag) and IsShiftKeyDown() and ChatFrameEditBox:IsVisible() and (not MacroFrame or not MacroFrame:IsVisible())) then 88 | local bookId = SpellBook_GetSpellID(this:GetID()); 89 | local _, _, spellID = GetSpellName(bookId, SpellBookFrame.bookType) 90 | local link = SuperAPI.GetSpellLink(spellID) 91 | ChatFrameEditBox:Insert(link) 92 | else 93 | SuperAPI.SpellButton_OnClickOriginal(drag) 94 | end 95 | end 96 | 97 | -- hooking bags item button frames to show uses count 98 | SuperAPI.SetItemButtonCount = function(button, count) 99 | if not button or not count then 100 | return SuperAPI.SetItemButtonCountOriginal(button, count) 101 | end 102 | if (count < 0) then 103 | if (count < -999) then 104 | count = "*"; 105 | end 106 | getglobal(button:GetName() .. "Count"):SetText(-count); 107 | getglobal(button:GetName() .. "Count"):Show(); 108 | getglobal(button:GetName() .. "Count"):SetFontObject(NumberFontNormalYellow); 109 | else 110 | getglobal(button:GetName() .. "Count"):SetFontObject(NumberFontNormal); 111 | SuperAPI.SetItemButtonCountOriginal(button, count) 112 | end 113 | end 114 | 115 | -- hooking actionbutton tooltip to show item tooltip on macros 116 | SuperAPI.SetAction = function(this, buttonID) 117 | --local name, actiontype, macroID = GetActionText(buttonID) 118 | --if actiontype == "MACRO" then 119 | -- local _,_, body = GetMacroInfo(macroID) 120 | -- local _,_, itemID = strfind(body, "^/tooltip item:(%d+)") 121 | -- if itemID then 122 | -- itemID = tonumber(itemID) 123 | -- iteminfo = SUPERAPI_ContainerItemsTable[itemID] 124 | -- if iteminfo then 125 | -- return this:SetBagItem(iteminfo.bag, iteminfo.slot) 126 | -- end 127 | -- end 128 | --end 129 | -- 130 | return SuperAPI.SetActionOriginal(this, buttonID) 131 | end 132 | 133 | -- Add Mouseover casting to default blizzard unitframes and all unitframe addons that use the same function 134 | SuperAPI.UnitFrame_OnEnter = function() 135 | SuperAPI.UnitFrame_OnEnterOriginal() 136 | SetMouseoverUnit(this.unit) 137 | end 138 | 139 | SuperAPI.UnitFrame_OnLeave = function() 140 | SuperAPI.UnitFrame_OnLeaveOriginal() 141 | SetMouseoverUnit() 142 | end 143 | 144 | -- Fix scrolling combat text healer name 145 | SuperAPI.CombatText_AddMessage = function(message, scrollFunction, r, g, b, displayType, isStaggered) 146 | local newMessage = gsub(message, "(%s%[)(0x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x)(%])", function(bracket1, hex, bracket2) 147 | if UnitIsUnit(hex, "player") then return nil 148 | else return " ["..UnitName(hex).."]" end 149 | end) 150 | return SuperAPI.CombatText_AddMessageOriginal(newMessage, scrollFunction, r, g, b, displayType, isStaggered) 151 | end 152 | -------------------------------------------------------------------------------- /SuperAPI.toc: -------------------------------------------------------------------------------- 1 | ## Interface: 11200 2 | ## Title: SuperAPI |cFFFF8080-balake-|r 3 | ## Notes: the companion compatibility addon to the mod of the same name. 4 | ## Author: balake 5 | ## SavedVariablesPerCharacter: SuperAPIDB 6 | ## X-Embeds: Ace2, FuBarPlugin-2.0, CompostLib, DewDropLib, TabletLib 7 | 8 | libs\AceLibrary\AceLibrary.lua 9 | 10 | libs\AceOO-2.0\AceOO-2.0.lua 11 | libs\AceAddon-2.0\AceAddon-2.0.lua 12 | libs\AceConsole-2.0\AceConsole-2.0.lua 13 | libs\AceHook-2.1\AceHook-2.1.lua 14 | libs\AceDB-2.0\AceDB-2.0.lua 15 | libs\AceDebug-2.0\AceDebug-2.0.lua 16 | libs\AceEvent-2.0\AceEvent-2.0.lua 17 | libs\AceLocale-2.2\AceLocale-2.2.lua 18 | libs\AceModuleCore-2.0\AceModuleCore-2.0.lua 19 | 20 | libs\Dewdrop\Dewdrop-2.0.lua 21 | libs\Tablet\Tablet-2.0.lua 22 | libs\FuBarPlugin\FuBarPlugin-2.0.lua 23 | 24 | SuperAPI.lua 25 | SuperAPIOptions.lua 26 | -------------------------------------------------------------------------------- /SuperAPIOptions.lua: -------------------------------------------------------------------------------- 1 | -- No superwow, no superapi 2 | if not SetAutoloot then 3 | return 4 | end 5 | 6 | SuperAPI.AUTOLOOT_OPTIONS = { 7 | "Always on", 8 | "Always off", 9 | "Shift to toggle on", 10 | "Shift to toggle off", 11 | } 12 | 13 | SuperAPI.SELECTION_CIRCLE_STYLE = { 14 | "Default - incomplete circle", 15 | "Full circle (must download texture)", 16 | "Full circle with arrow for facing direction (must download texture)", 17 | "Classic incomplete circle oriented in facing direction", 18 | } 19 | 20 | SuperAPI:RegisterDefaults("profile", { 21 | autoloot = SuperAPI.AUTOLOOT_OPTIONS[3], 22 | clickthrough = false, 23 | }) 24 | 25 | SuperAPI.IfShiftAutoloot = function() 26 | if IsShiftKeyDown() then 27 | SetAutoloot(1) 28 | else 29 | SetAutoloot(0) 30 | end 31 | end 32 | 33 | SuperAPI.IfShiftNoAutoloot = function() 34 | if IsShiftKeyDown() then 35 | SetAutoloot(0) 36 | else 37 | SetAutoloot(1) 38 | end 39 | end 40 | 41 | SuperAPI.cmdtable = { 42 | type = "group", 43 | handler = SuperAPI, 44 | args = { 45 | autoloot = { 46 | type = "text", 47 | name = "Autoloot (Read tooltip)", 48 | desc = "Specifies autoloot behavior. If using Vanilla Tweaks quickloot all of these will be reversed (always on will actually be always off, Shift to toggle on will be Shift to toggle off etc).", 49 | order = 10, 50 | validate = SuperAPI.AUTOLOOT_OPTIONS, 51 | get = function() 52 | return SuperAPI.db.profile.autoloot 53 | end, 54 | set = function(v) 55 | SuperAPI.db.profile.autoloot = v 56 | if v == SuperAPI.AUTOLOOT_OPTIONS[1] then 57 | -- "Always on" 58 | SetAutoloot(1) 59 | SuperAPI.frame:SetScript("OnUpdate", nil) 60 | elseif v == SuperAPI.AUTOLOOT_OPTIONS[2] then 61 | -- "Always off" 62 | SetAutoloot(0) 63 | SuperAPI.frame:SetScript("OnUpdate", nil) 64 | elseif v == SuperAPI.AUTOLOOT_OPTIONS[3] then 65 | -- "Shift to toggle on" 66 | SetAutoloot(0) 67 | SuperAPI.frame:SetScript("OnUpdate", SuperAPI.IfShiftAutoloot) 68 | elseif v == SuperAPI.AUTOLOOT_OPTIONS[4] then 69 | -- "Shift to toggle off" 70 | SetAutoloot(1) 71 | SuperAPI.frame:SetScript("OnUpdate", SuperAPI.IfShiftNoAutoloot) 72 | end 73 | end, 74 | }, 75 | clickthrough = { 76 | type = "toggle", 77 | name = "Clickthrough corpses", 78 | desc = "Allows you to click through corpses to loot corpses underneath them.", 79 | order = 20, 80 | get = function() 81 | return Clickthrough() == 1 82 | end, 83 | set = function(v) 84 | if v == true then 85 | Clickthrough(1) 86 | else 87 | Clickthrough(0) 88 | end 89 | SuperAPI.db.profile.clickthrough = v 90 | end, 91 | }, 92 | fov = { 93 | type = "range", 94 | name = "Field of view (Requires reload)", 95 | desc = "Changes the field of view of the game. Requires reload to take effect.", 96 | order = 30, 97 | min = 0.1, 98 | max = 3.14, 99 | step = 0.05, 100 | get = function() 101 | return GetCVar("FoV") 102 | end, 103 | set = function(v) 104 | SetCVar("FoV", v) 105 | end, 106 | }, 107 | selectioncircle = { 108 | type = "text", 109 | name = "Selection circle style", 110 | desc = "Changes the style of the selection circle.", 111 | order = 40, 112 | validate = SuperAPI.SELECTION_CIRCLE_STYLE, 113 | get = function() 114 | local selectioncircle = GetCVar("SelectionCircleStyle") 115 | if selectioncircle then 116 | return SuperAPI.SELECTION_CIRCLE_STYLE[tonumber(selectioncircle)] 117 | end 118 | end, 119 | set = function(v) 120 | if v == SuperAPI.SELECTION_CIRCLE_STYLE[1] then 121 | SetCVar("SelectionCircleStyle", "1") 122 | elseif v == SuperAPI.SELECTION_CIRCLE_STYLE[2] then 123 | SetCVar("SelectionCircleStyle", "2") 124 | elseif v == SuperAPI.SELECTION_CIRCLE_STYLE[3] then 125 | SetCVar("SelectionCircleStyle", "3") 126 | elseif v == SuperAPI.SELECTION_CIRCLE_STYLE[4] then 127 | SetCVar("SelectionCircleStyle", "4") 128 | end 129 | end, 130 | }, 131 | backgroundsound = { 132 | type = "toggle", 133 | name = "Background sound", 134 | desc = "Allows game sound to play even when the window is in the background.", 135 | order = 60, 136 | get = function() 137 | return GetCVar("BackgroundSound") == "1" 138 | end, 139 | set = function(v) 140 | if v == true then 141 | SetCVar("BackgroundSound", "1") 142 | else 143 | SetCVar("BackgroundSound", "0") 144 | end 145 | end, 146 | }, 147 | uncappedsounds = { 148 | type = "toggle", 149 | name = "Uncapped sounds", 150 | desc = "Allows more game sounds to play at the same time by removing hardcoded limit. This will also set SoundSoftwareChannels and SoundMaxHardwareChannels to 64. If you experience any weird crashes you may want to turn this off.", 151 | order = 70, 152 | get = function() 153 | return GetCVar("UncapSounds") == "1" 154 | end, 155 | set = function(v) 156 | if v == true then 157 | SetCVar("UncapSounds", "1") 158 | SetCVar("SoundSoftwareChannels", "64") 159 | SetCVar("SoundMaxHardwareChannels", "64") 160 | else 161 | SetCVar("UncapSounds", "0") 162 | SetCVar("SoundSoftwareChannels", "12") 163 | SetCVar("SoundMaxHardwareChannels", "12") 164 | end 165 | end, 166 | }, 167 | lootsparkle = { 168 | type = "toggle", 169 | name = "Loot Sparkle", 170 | desc = "Toggle loot sparkle effect on lootable treasure.", 171 | order = 80, 172 | get = function() 173 | return GetCVar("LootSparkle") == "1" 174 | end, 175 | set = function(v) 176 | if v == true then 177 | SetCVar("LootSparkle", "1") 178 | else 179 | SetCVar("LootSparkle", "0") 180 | end 181 | end, 182 | }, 183 | } 184 | } 185 | 186 | local deuce = SuperAPI:NewModule("SuperAPI Options Menu") 187 | deuce.hasFuBar = IsAddOnLoaded("FuBar") and FuBar 188 | deuce.consoleCmd = not deuce.hasFuBar 189 | 190 | SuperAPIOptions = AceLibrary("AceAddon-2.0"):new("AceDB-2.0", "FuBarPlugin-2.0") 191 | SuperAPIOptions.name = "FuBar - SuperAPI" 192 | SuperAPIOptions:RegisterDB("SuperAPIDB") 193 | SuperAPIOptions.hasIcon = "Interface\\Icons\\inv_misc_book_06" 194 | SuperAPIOptions.defaultMinimapPosition = 180 195 | SuperAPIOptions.independentProfile = true 196 | SuperAPIOptions.hideWithoutStandby = false 197 | 198 | SuperAPIOptions.OnMenuRequest = SuperAPI.cmdtable 199 | local args = AceLibrary("FuBarPlugin-2.0"):GetAceOptionsDataTable(SuperAPIOptions) 200 | for k, v in pairs(args) do 201 | if SuperAPIOptions.OnMenuRequest.args[k] == nil then 202 | SuperAPIOptions.OnMenuRequest.args[k] = v 203 | end 204 | end 205 | 206 | function SuperAPIOptions:OnEnable() 207 | -- activate saved settings 208 | SuperAPI.cmdtable.args.autoloot.set(SuperAPI.db.profile.autoloot) 209 | SuperAPI.cmdtable.args.clickthrough.set(SuperAPI.db.profile.clickthrough) 210 | end 211 | -------------------------------------------------------------------------------- /libs/AceAddon-2.0/AceAddon-2.0.lua: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balakethelock/SuperAPI/bf2fd24c1e7349217a6871b866dc2b7d4fdcc18e/libs/AceAddon-2.0/AceAddon-2.0.lua -------------------------------------------------------------------------------- /libs/AceDebug-2.0/AceDebug-2.0.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Name: AceDebug-2.0 3 | Revision: $Rev: 17638 $ 4 | Developed by: The Ace Development Team (http://www.wowace.com/index.php/The_Ace_Development_Team) 5 | Inspired By: Ace 1.x by Turan (turan@gryphon.com) 6 | Website: http://www.wowace.com/ 7 | Documentation: http://www.wowace.com/index.php/AceDebug-2.0 8 | SVN: http://svn.wowace.com/root/trunk/Ace2/AceDebug-2.0 9 | Description: Mixin to allow for simple debugging capabilities. 10 | Dependencies: AceLibrary, AceOO-2.0 11 | ]] 12 | 13 | local MAJOR_VERSION = "AceDebug-2.0" 14 | local MINOR_VERSION = "$Revision: 17639 $" 15 | 16 | if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary") end 17 | if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end 18 | 19 | if loadstring("return function(...) return ... end") and AceLibrary:HasInstance(MAJOR_VERSION) then return end -- lua51 check 20 | if not AceLibrary:HasInstance("AceOO-2.0") then error(MAJOR_VERSION .. " requires AceOO-2.0") end 21 | 22 | local DEBUGGING, TOGGLE_DEBUGGING 23 | 24 | if GetLocale() == "frFR" then 25 | DEBUGGING = "D\195\169boguage" 26 | TOGGLE_DEBUGGING = "Activer/d\195\169sactiver le d\195\169boguage" 27 | elseif GetLocale() == "esES" then 28 | DEBUGGING = "Depurar" 29 | TOGGLE_DEBUGGING = "Activar/desactivar depurar" 30 | elseif GetLocale() == "deDE" then 31 | DEBUGGING = "Debuggen" 32 | TOGGLE_DEBUGGING = "Aktiviert/Deaktiviert Debugging" 33 | elseif GetLocale() == "koKR" then 34 | DEBUGGING = "디버깅" 35 | TOGGLE_DEBUGGING = "디버깅 기능 사용함/사용안함" 36 | elseif GetLocale() == "zhTW" then 37 | DEBUGGING = "除錯" 38 | TOGGLE_DEBUGGING = "啟用/停用除錯功能" 39 | elseif GetLocale() == "zhCN" then 40 | DEBUGGING = "\232\176\131\232\175\149" 41 | TOGGLE_DEBUGGING = "\229\144\175\231\148\168/\231\166\129\231\148\168 \232\176\131\232\175\149" 42 | elseif GetLocale() == "ruRU" then 43 | DEBUGGING = "Отладка" 44 | TOGGLE_DEBUGGING = "Вкл/Выкл отладку для этого аддона." 45 | else -- enUS 46 | DEBUGGING = "Debugging" 47 | TOGGLE_DEBUGGING = "Enable/disable debugging" 48 | end 49 | 50 | local table_setn 51 | do 52 | local version = GetBuildInfo() 53 | if string.find(version, "^2%.") then 54 | -- 2.0.0 55 | table_setn = function() end 56 | else 57 | table_setn = table.setn 58 | end 59 | end 60 | 61 | local math_mod = math.mod or math.fmod 62 | 63 | local AceOO = AceLibrary:GetInstance("AceOO-2.0") 64 | local AceDebug = AceOO.Mixin {"Debug", "CustomDebug", "IsDebugging", "SetDebugging", "SetDebugLevel", "LevelDebug", "CustomLevelDebug", "GetDebugLevel"} 65 | 66 | local function print(text, r, g, b, frame, delay) 67 | (frame or DEFAULT_CHAT_FRAME):AddMessage(text, r, g, b, 1, delay or 5) 68 | end 69 | 70 | local tmp 71 | 72 | function AceDebug:CustomDebug(r, g, b, frame, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) 73 | if not self.debugging then return end 74 | 75 | local output = string.format("|cff7fff7f(DEBUG) %s:[%s%3d]|r", tostring(self), date("%H:%M:%S"), math_mod(GetTime(), 1) * 1000) 76 | 77 | if string.find(tostring(a1), "%%") then 78 | output = output .. " " .. string.format(tostring(a1), tostring(a2), tostring(a3), tostring(a4), tostring(a5), tostring(a6), tostring(a7), tostring(a8), tostring(a9), tostring(a10), tostring(a11), tostring(a12), tostring(a13), tostring(a14), tostring(a15), tostring(a16), tostring(a17), tostring(a18), tostring(a19), tostring(a20)) 79 | else 80 | if not tmp then 81 | tmp = {} 82 | end 83 | 84 | -- This block dynamically rebuilds the tmp array stopping on the first nil. 85 | table.insert(tmp, output) 86 | 87 | table.insert(tmp, tostring(a1)) 88 | table.insert(tmp, a2) 89 | table.insert(tmp, a3) 90 | table.insert(tmp, a4) 91 | table.insert(tmp, a5) 92 | table.insert(tmp, a6) 93 | table.insert(tmp, a7) 94 | table.insert(tmp, a8) 95 | table.insert(tmp, a9) 96 | table.insert(tmp, a10) 97 | table.insert(tmp, a11) 98 | table.insert(tmp, a12) 99 | table.insert(tmp, a13) 100 | table.insert(tmp, a14) 101 | table.insert(tmp, a15) 102 | table.insert(tmp, a16) 103 | table.insert(tmp, a17) 104 | table.insert(tmp, a18) 105 | table.insert(tmp, a19) 106 | table.insert(tmp, a20) 107 | while tmp[table.getn(tmp)] == nil do 108 | table.remove(tmp) 109 | end 110 | for k = 1, table.getn(tmp) do 111 | tmp[k] = tostring(tmp[k]) 112 | end 113 | 114 | output = table.concat(tmp, " ") 115 | 116 | for k,v in pairs(tmp) do 117 | tmp[k] = nil 118 | end 119 | table_setn(tmp, 0) 120 | end 121 | 122 | print(output, r, g, b, frame or self.debugFrame, delay) 123 | end 124 | 125 | function AceDebug:Debug(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) 126 | AceDebug.CustomDebug(self, nil, nil, nil, nil, nil, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) 127 | end 128 | 129 | function AceDebug:IsDebugging() 130 | return self.debugging 131 | end 132 | 133 | function AceDebug:SetDebugging(debugging) 134 | self.debugging = debugging 135 | end 136 | 137 | -- Takes a number 1-3 138 | -- Level 1: Critical messages that every user should receive 139 | -- Level 2: Should be used for local debugging (function calls, etc) 140 | -- Level 3: Very verbose debugging, will dump everything and anything 141 | -- If set to nil, you will receive no debug information 142 | function AceDebug:SetDebugLevel(level) 143 | AceDebug:argCheck(level, 1, "number", "nil") 144 | if not level then 145 | self.debuglevel = nil 146 | return 147 | end 148 | if level < 1 or level > 3 then 149 | AceDebug:error("Bad argument #1 to `SetDebugLevel`, must be a number 1-3") 150 | end 151 | self.debuglevel = level 152 | end 153 | 154 | function AceDebug:GetDebugLevel() 155 | return self.debuglevel 156 | end 157 | 158 | function AceDebug:CustomLevelDebug(level, r, g, b, frame, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) 159 | if not self.debugging or not self.debuglevel then return end 160 | AceDebug:argCheck(level, 1, "number") 161 | if level < 1 or level > 3 then 162 | AceDebug:error("Bad argument #1 to `LevelDebug`, must be a number 1-3") 163 | end 164 | if level > self.debuglevel then return end 165 | 166 | local output = string.format("|cff7fff7f(DEBUG) %s:[%s.%3d]|r", tostring(self), date("%H:%M:%S"), math_mod(GetTime(), 1) * 1000) 167 | 168 | if string.find(tostring(a1), "%%") then 169 | output = output .. " " .. string.format(tostring(a1), tostring(a2), tostring(a3), tostring(a4), tostring(a5), tostring(a6), tostring(a7), tostring(a8), tostring(a9), tostring(a10), tostring(a11), tostring(a12), tostring(a13), tostring(a14), tostring(a15), tostring(a16), tostring(a17), tostring(a18), tostring(a19), tostring(a20)) 170 | else 171 | if not tmp then 172 | tmp = {} 173 | end 174 | 175 | -- This block dynamically rebuilds the tmp array stopping on the first nil. 176 | table.insert(tmp, output) 177 | 178 | table.insert(tmp, tostring(a1)) 179 | table.insert(tmp, a2) 180 | table.insert(tmp, a3) 181 | table.insert(tmp, a4) 182 | table.insert(tmp, a5) 183 | table.insert(tmp, a6) 184 | table.insert(tmp, a7) 185 | table.insert(tmp, a8) 186 | table.insert(tmp, a9) 187 | table.insert(tmp, a10) 188 | table.insert(tmp, a11) 189 | table.insert(tmp, a12) 190 | table.insert(tmp, a13) 191 | table.insert(tmp, a14) 192 | table.insert(tmp, a15) 193 | table.insert(tmp, a16) 194 | table.insert(tmp, a17) 195 | table.insert(tmp, a18) 196 | table.insert(tmp, a19) 197 | table.insert(tmp, a20) 198 | while tmp[table.getn(tmp)] == nil do 199 | table.remove(tmp) 200 | end 201 | for k = 1, table.getn(tmp) do 202 | tmp[k] = tostring(tmp[k]) 203 | end 204 | 205 | output = table.concat(tmp, " ") 206 | 207 | for k,v in pairs(tmp) do 208 | tmp[k] = nil 209 | end 210 | table_setn(tmp, 0) 211 | end 212 | 213 | print(output, r, g, b, frame or self.debugFrame, delay) 214 | end 215 | 216 | function AceDebug:LevelDebug(level, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) 217 | if not self.debugging or not self.debuglevel then return end 218 | AceDebug:argCheck(level, 1, "number") 219 | if level < 1 or level > 3 then 220 | AceDebug:error("Bad argument #1 to `LevelDebug`, must be a number 1-3") 221 | end 222 | if level > self.debuglevel then return end 223 | 224 | AceDebug.CustomLevelDebug(self, level, nil, nil, nil, nil, nil, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) 225 | end 226 | 227 | 228 | local options 229 | function AceDebug:GetAceOptionsDataTable(target) 230 | if not options then 231 | options = { 232 | debug = { 233 | name = DEBUGGING, 234 | desc = TOGGLE_DEBUGGING, 235 | type = "toggle", 236 | get = "IsDebugging", 237 | set = "SetDebugging", 238 | order = -2, 239 | } 240 | } 241 | end 242 | return options 243 | end 244 | AceLibrary:Register(AceDebug, MAJOR_VERSION, MINOR_VERSION, AceDebug.activate) 245 | AceDebug = AceLibrary(MAJOR_VERSION) 246 | -------------------------------------------------------------------------------- /libs/AceEvent-2.0/AceEvent-2.0.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Name: AceEvent-2.0 3 | Revision: $Rev: 17803 $ 4 | Developed by: The Ace Development Team (http://www.wowace.com/index.php/The_Ace_Development_Team) 5 | Inspired By: Ace 1.x by Turan (turan@gryphon.com) 6 | Website: http://www.wowace.com/ 7 | Documentation: http://www.wowace.com/index.php/AceEvent-2.0 8 | SVN: http://svn.wowace.com/root/trunk/Ace2/AceEvent-2.0 9 | Description: Mixin to allow for event handling, scheduling, and inter-addon 10 | communication. 11 | Dependencies: AceLibrary, AceOO-2.0 12 | ]] 13 | 14 | local MAJOR_VERSION = "AceEvent-2.0" 15 | local MINOR_VERSION = "$Revision: 17803 $" 16 | 17 | if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary") end 18 | if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end 19 | 20 | if loadstring("return function(...) return ... end") and AceLibrary:HasInstance(MAJOR_VERSION) then return end -- lua51 check 21 | if not AceLibrary:HasInstance("AceOO-2.0") then error(MAJOR_VERSION .. " requires AceOO-2.0") end 22 | 23 | local AceOO = AceLibrary:GetInstance("AceOO-2.0") 24 | local Mixin = AceOO.Mixin 25 | local AceEvent = Mixin { 26 | "RegisterEvent", 27 | "RegisterAllEvents", 28 | "UnregisterEvent", 29 | "UnregisterAllEvents", 30 | "TriggerEvent", 31 | "ScheduleEvent", 32 | "ScheduleRepeatingEvent", 33 | "CancelScheduledEvent", 34 | "CancelAllScheduledEvents", 35 | "IsEventRegistered", 36 | "IsEventScheduled", 37 | "RegisterBucketEvent", 38 | "UnregisterBucketEvent", 39 | "UnregisterAllBucketEvents", 40 | "IsBucketEventRegistered", 41 | } 42 | 43 | local table_setn 44 | do 45 | local version = GetBuildInfo() 46 | if string.find(version, "^2%.") then 47 | -- 2.0.0 48 | table_setn = function() end 49 | else 50 | table_setn = table.setn 51 | end 52 | end 53 | 54 | local weakKey = {__mode="k"} 55 | local new, del 56 | do 57 | local list = setmetatable({}, weakKey) 58 | function new() 59 | local t = next(list) 60 | if t then 61 | list[t] = nil 62 | return t 63 | else 64 | return {} 65 | end 66 | end 67 | 68 | function del(t) 69 | setmetatable(t, nil) 70 | for k in pairs(t) do 71 | t[k] = nil 72 | end 73 | list[t] = true 74 | end 75 | end 76 | 77 | local FAKE_NIL 78 | local RATE 79 | 80 | local eventsWhichHappenOnce = { 81 | PLAYER_LOGIN = true, 82 | AceEvent_FullyInitialized = true, 83 | VARIABLES_LOADED = true, 84 | PLAYER_LOGOUT = true, 85 | } 86 | 87 | local registeringFromAceEvent 88 | function AceEvent:RegisterEvent(event, method, once) 89 | AceEvent:argCheck(event, 2, "string") 90 | if self == AceEvent and not registeringFromAceEvent then 91 | AceEvent:argCheck(method, 3, "function") 92 | self = method 93 | else 94 | AceEvent:argCheck(method, 3, "string", "function", "nil", "boolean", "number") 95 | if type(method) == "boolean" or type(method) == "number" then 96 | AceEvent:argCheck(once, 4, "nil") 97 | once, method = method, event 98 | end 99 | end 100 | AceEvent:argCheck(once, 4, "number", "boolean", "nil") 101 | if eventsWhichHappenOnce[event] then 102 | once = true 103 | end 104 | local throttleRate 105 | if type(once) == "number" then 106 | throttleRate, once = once 107 | end 108 | if not method then 109 | method = event 110 | end 111 | if type(method) == "string" and type(self[method]) ~= "function" then 112 | AceEvent:error("Cannot register event %q to method %q, it does not exist", event, method) 113 | else 114 | assert(type(method) == "function" or type(method) == "string") 115 | end 116 | 117 | local AceEvent_registry = AceEvent.registry 118 | if not AceEvent_registry[event] then 119 | AceEvent_registry[event] = new() 120 | AceEvent.frame:RegisterEvent(event) 121 | end 122 | 123 | local remember = true 124 | if AceEvent_registry[event][self] then 125 | remember = false 126 | end 127 | AceEvent_registry[event][self] = method 128 | 129 | local AceEvent_onceRegistry = AceEvent.onceRegistry 130 | if once then 131 | if not AceEvent_onceRegistry then 132 | AceEvent.onceRegistry = new() 133 | AceEvent_onceRegistry = AceEvent.onceRegistry 134 | end 135 | if not AceEvent_onceRegistry[event] then 136 | AceEvent_onceRegistry[event] = new() 137 | end 138 | AceEvent_onceRegistry[event][self] = true 139 | else 140 | if AceEvent_onceRegistry and AceEvent_onceRegistry[event] then 141 | AceEvent_onceRegistry[event][self] = nil 142 | if not next(AceEvent_onceRegistry[event]) then 143 | AceEvent_onceRegistry[event] = del(AceEvent_onceRegistry[event]) 144 | end 145 | end 146 | end 147 | 148 | local AceEvent_throttleRegistry = AceEvent.throttleRegistry 149 | if throttleRate then 150 | if not AceEvent_throttleRegistry then 151 | AceEvent.throttleRegistry = new() 152 | AceEvent_throttleRegistry = AceEvent.throttleRegistry 153 | end 154 | if not AceEvent_throttleRegistry[event] then 155 | AceEvent_throttleRegistry[event] = new() 156 | end 157 | if AceEvent_throttleRegistry[event][self] then 158 | AceEvent_throttleRegistry[event][self] = del(AceEvent_throttleRegistry[event][self]) 159 | end 160 | AceEvent_throttleRegistry[event][self] = setmetatable(new(), weakKey) 161 | local t = AceEvent_throttleRegistry[event][self] 162 | t[RATE] = throttleRate 163 | else 164 | if AceEvent_throttleRegistry and AceEvent_throttleRegistry[event] then 165 | if AceEvent_throttleRegistry[event][self] then 166 | AceEvent_throttleRegistry[event][self] = del(AceEvent_throttleRegistry[event][self]) 167 | end 168 | if not next(AceEvent_throttleRegistry[event]) then 169 | AceEvent_throttleRegistry[event] = del(AceEvent_throttleRegistry[event]) 170 | end 171 | end 172 | end 173 | 174 | if remember then 175 | AceEvent:TriggerEvent("AceEvent_EventRegistered", self, event) 176 | end 177 | end 178 | 179 | local ALL_EVENTS 180 | 181 | function AceEvent:RegisterAllEvents(method) 182 | if self == AceEvent then 183 | AceEvent:argCheck(method, 1, "function") 184 | self = method 185 | else 186 | AceEvent:argCheck(method, 1, "string", "function") 187 | if type(method) == "string" and type(self[method]) ~= "function" then 188 | AceEvent:error("Cannot register all events to method %q, it does not exist", method) 189 | end 190 | end 191 | 192 | local AceEvent_registry = AceEvent.registry 193 | if not AceEvent_registry[ALL_EVENTS] then 194 | AceEvent_registry[ALL_EVENTS] = new() 195 | AceEvent.frame:RegisterAllEvents() 196 | end 197 | 198 | AceEvent_registry[ALL_EVENTS][self] = method 199 | end 200 | 201 | local _G = getfenv(0) 202 | local memstack, timestack = {}, {} 203 | local memdiff, timediff 204 | function AceEvent:TriggerEvent(event, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) 205 | AceEvent:argCheck(event, 2, "string") 206 | local AceEvent_registry = AceEvent.registry 207 | 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 208 | return 209 | end 210 | local _G_event = _G.event 211 | _G.event = event 212 | local lastEvent = AceEvent.currentEvent 213 | AceEvent.currentEvent = event 214 | 215 | local AceEvent_onceRegistry = AceEvent.onceRegistry 216 | local AceEvent_debugTable = AceEvent.debugTable 217 | if AceEvent_onceRegistry and AceEvent_onceRegistry[event] then 218 | local tmp = new() 219 | for obj, method in pairs(AceEvent_onceRegistry[event]) do 220 | tmp[obj] = AceEvent_registry[event] and AceEvent_registry[event][obj] or nil 221 | end 222 | local obj = next(tmp) 223 | while obj do 224 | local mem, time 225 | if AceEvent_debugTable then 226 | if not AceEvent_debugTable[event] then 227 | AceEvent_debugTable[event] = new() 228 | end 229 | if not AceEvent_debugTable[event][obj] then 230 | AceEvent_debugTable[event][obj] = new() 231 | AceEvent_debugTable[event][obj].mem = 0 232 | AceEvent_debugTable[event][obj].time = 0 233 | AceEvent_debugTable[event][obj].count = 0 234 | end 235 | if memdiff then 236 | table.insert(memstack, memdiff) 237 | table.insert(timestack, timediff) 238 | end 239 | memdiff, timediff = 0, 0 240 | mem, time = gcinfo(), GetTime() 241 | end 242 | local method = tmp[obj] 243 | AceEvent.UnregisterEvent(obj, event) 244 | if type(method) == "string" then 245 | local obj_method = obj[method] 246 | if obj_method then 247 | obj_method(obj, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) 248 | end 249 | elseif method then -- function 250 | method(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) 251 | end 252 | if AceEvent_debugTable then 253 | local dmem, dtime = memdiff, timediff 254 | mem, time = gcinfo() - mem - memdiff, GetTime() - time - timediff 255 | AceEvent_debugTable[event][obj].mem = AceEvent_debugTable[event][obj].mem + mem 256 | AceEvent_debugTable[event][obj].time = AceEvent_debugTable[event][obj].time + time 257 | AceEvent_debugTable[event][obj].count = AceEvent_debugTable[event][obj].count + 1 258 | 259 | memdiff, timediff = table.remove(memstack), table.remove(timestack) 260 | if memdiff then 261 | memdiff = memdiff + mem + dmem 262 | timediff = timediff + time + dtime 263 | end 264 | end 265 | tmp[obj] = nil 266 | obj = next(tmp) 267 | end 268 | del(tmp) 269 | end 270 | 271 | local AceEvent_throttleRegistry = AceEvent.throttleRegistry 272 | local throttleTable = AceEvent_throttleRegistry and AceEvent_throttleRegistry[event] 273 | if AceEvent_registry[event] then 274 | local tmp = new() 275 | for obj, method in pairs(AceEvent_registry[event]) do 276 | tmp[obj] = method 277 | end 278 | local obj = next(tmp) 279 | while obj do 280 | local method = tmp[obj] 281 | local continue = false 282 | if throttleTable and throttleTable[obj] then 283 | local a1 = a1 284 | if a1 == nil then 285 | a1 = FAKE_NIL 286 | end 287 | if not throttleTable[obj][a1] or GetTime() - throttleTable[obj][a1] >= throttleTable[obj][RATE] then 288 | throttleTable[obj][a1] = GetTime() 289 | else 290 | continue = true 291 | end 292 | end 293 | if not continue then 294 | local mem, time 295 | if AceEvent_debugTable then 296 | if not AceEvent_debugTable[event] then 297 | AceEvent_debugTable[event] = new() 298 | end 299 | if not AceEvent_debugTable[event][obj] then 300 | AceEvent_debugTable[event][obj] = new() 301 | AceEvent_debugTable[event][obj].mem = 0 302 | AceEvent_debugTable[event][obj].time = 0 303 | AceEvent_debugTable[event][obj].count = 0 304 | end 305 | if memdiff then 306 | table.insert(memstack, memdiff) 307 | table.insert(timestack, timediff) 308 | end 309 | memdiff, timediff = 0, 0 310 | mem, time = gcinfo(), GetTime() 311 | end 312 | if type(method) == "string" then 313 | local obj_method = obj[method] 314 | if obj_method then 315 | obj_method(obj, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) 316 | end 317 | elseif method then -- function 318 | method(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) 319 | end 320 | if AceEvent_debugTable then 321 | local dmem, dtime = memdiff, timediff 322 | mem, time = gcinfo() - mem - memdiff, GetTime() - time - timediff 323 | AceEvent_debugTable[event][obj].mem = AceEvent_debugTable[event][obj].mem + mem 324 | AceEvent_debugTable[event][obj].time = AceEvent_debugTable[event][obj].time + time 325 | AceEvent_debugTable[event][obj].count = AceEvent_debugTable[event][obj].count + 1 326 | 327 | memdiff, timediff = table.remove(memstack), table.remove(timestack) 328 | if memdiff then 329 | memdiff = memdiff + mem + dmem 330 | timediff = timediff + time + dtime 331 | end 332 | end 333 | end 334 | tmp[obj] = nil 335 | obj = next(tmp) 336 | end 337 | del(tmp) 338 | end 339 | if AceEvent_registry[ALL_EVENTS] then 340 | local tmp = new() 341 | for obj, method in pairs(AceEvent_registry[ALL_EVENTS]) do 342 | tmp[obj] = method 343 | end 344 | local obj = next(tmp) 345 | while obj do 346 | local method = tmp[obj] 347 | local mem, time 348 | if AceEvent_debugTable then 349 | if not AceEvent_debugTable[event] then 350 | AceEvent_debugTable[event] = new() 351 | end 352 | if not AceEvent_debugTable[event][obj] then 353 | AceEvent_debugTable[event][obj] = new() 354 | AceEvent_debugTable[event][obj].mem = 0 355 | AceEvent_debugTable[event][obj].time = 0 356 | AceEvent_debugTable[event][obj].count = 0 357 | end 358 | if memdiff then 359 | table.insert(memstack, memdiff) 360 | table.insert(timestack, timediff) 361 | end 362 | memdiff, timediff = 0, 0 363 | mem, time = gcinfo(), GetTime() 364 | end 365 | if type(method) == "string" then 366 | local obj_method = obj[method] 367 | if obj_method then 368 | obj_method(obj, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) 369 | end 370 | elseif method then -- function 371 | method(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) 372 | end 373 | if AceEvent_debugTable then 374 | local dmem, dtime = memdiff, timediff 375 | mem, time = gcinfo() - mem - memdiff, GetTime() - time - timediff 376 | AceEvent_debugTable[event][obj].mem = AceEvent_debugTable[event][obj].mem + mem 377 | AceEvent_debugTable[event][obj].time = AceEvent_debugTable[event][obj].time + time 378 | AceEvent_debugTable[event][obj].count = AceEvent_debugTable[event][obj].count + 1 379 | 380 | memdiff, timediff = table.remove(memstack), table.remove(timestack) 381 | if memdiff then 382 | memdiff = memdiff + mem + dmem 383 | timediff = timediff + time + dtime 384 | end 385 | end 386 | tmp[obj] = nil 387 | obj = next(tmp) 388 | end 389 | del(tmp) 390 | end 391 | _G.event = _G_event 392 | AceEvent.currentEvent = lastEvent 393 | end 394 | 395 | -- local accessors 396 | local getn = table.getn 397 | local tinsert = table.insert 398 | local tremove = table.remove 399 | local floor = math.floor 400 | local GetTime = GetTime 401 | local next = next 402 | local pairs = pairs 403 | local unpack = unpack 404 | 405 | local delayRegistry 406 | local tmp = {} 407 | local function OnUpdate() 408 | local t = GetTime() 409 | for k,v in pairs(delayRegistry) do 410 | tmp[k] = true 411 | end 412 | for k in pairs(tmp) do 413 | local v = delayRegistry[k] 414 | if v then 415 | local v_time = v.time 416 | if not v_time then 417 | delayRegistry[k] = del(v) 418 | elseif v_time <= t then 419 | local v_repeatDelay = v.repeatDelay 420 | if v_repeatDelay then 421 | -- use the event time, not the current time, else timing inaccuracies add up over time 422 | v.time = v_time + v_repeatDelay 423 | end 424 | local event = v.event 425 | local mem, time 426 | if AceEvent_debugTable then 427 | mem, time = gcinfo(), GetTime() 428 | end 429 | if type(event) == "function" then 430 | event(unpack(v)) 431 | else 432 | AceEvent:TriggerEvent(event, unpack(v)) 433 | end 434 | if AceEvent_debugTable then 435 | mem, time = gcinfo() - mem, GetTime() - time 436 | v.mem = v.mem + mem 437 | v.timeSpent = v.timeSpent + time 438 | v.count = v.count + 1 439 | end 440 | if not v_repeatDelay then 441 | local x = delayRegistry[k] 442 | if x and x.time == v_time then -- check if it was manually reset 443 | delayRegistry[k] = del(v) 444 | end 445 | end 446 | end 447 | end 448 | end 449 | for k in pairs(tmp) do 450 | tmp[k] = nil 451 | end 452 | if not next(delayRegistry) then 453 | AceEvent.frame:Hide() 454 | end 455 | end 456 | 457 | local function ScheduleEvent(self, repeating, event, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) 458 | local id 459 | if type(event) == "string" or type(event) == "table" then 460 | if type(event) == "table" then 461 | if not delayRegistry or not delayRegistry[event] then 462 | AceEvent:error("Bad argument #2 to `ScheduleEvent'. Improper id table fed in.") 463 | end 464 | end 465 | if type(delay) ~= "number" then 466 | id, event, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20 = event, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20 467 | AceEvent:argCheck(event, 3, "string", "function", --[[ so message is right ]] "number") 468 | AceEvent:argCheck(delay, 4, "number") 469 | self:CancelScheduledEvent(id) 470 | end 471 | else 472 | AceEvent:argCheck(event, 2, "string", "function") 473 | AceEvent:argCheck(delay, 3, "number") 474 | end 475 | 476 | if not delayRegistry then 477 | AceEvent.delayRegistry = new() 478 | delayRegistry = AceEvent.delayRegistry 479 | AceEvent.frame:SetScript("OnUpdate", OnUpdate) 480 | end 481 | local t 482 | if type(id) == "table" then 483 | for k in pairs(id) do 484 | id[k] = nil 485 | end 486 | t = id 487 | else 488 | t = new() 489 | end 490 | t[1] = a1 491 | t[2] = a2 492 | t[3] = a3 493 | t[4] = a4 494 | t[5] = a5 495 | t[6] = a6 496 | t[7] = a7 497 | t[8] = a8 498 | t[9] = a9 499 | t[10] = a10 500 | t[11] = a11 501 | t[12] = a12 502 | t[13] = a13 503 | t[14] = a14 504 | t[15] = a15 505 | t[16] = a16 506 | t[17] = a17 507 | t[18] = a18 508 | t[19] = a19 509 | t[20] = a20 510 | table_setn(t, 20) 511 | t.event = event 512 | t.time = GetTime() + delay 513 | t.self = self 514 | t.id = id or t 515 | t.repeatDelay = repeating and delay 516 | if AceEvent_debugTable then 517 | t.mem = 0 518 | t.count = 0 519 | t.timeSpent = 0 520 | end 521 | delayRegistry[t.id] = t 522 | AceEvent.frame:Show() 523 | return t.id 524 | end 525 | 526 | function AceEvent:ScheduleEvent(event, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) 527 | if type(event) == "string" or type(event) == "table" then 528 | if type(event) == "table" then 529 | if not delayRegistry or not delayRegistry[event] then 530 | AceEvent:error("Bad argument #2 to `ScheduleEvent'. Improper id table fed in.") 531 | end 532 | end 533 | if type(delay) ~= "number" then 534 | AceEvent:argCheck(delay, 3, "string", "function", --[[ so message is right ]] "number") 535 | AceEvent:argCheck(a1, 4, "number") 536 | end 537 | else 538 | AceEvent:argCheck(event, 2, "string", "function") 539 | AceEvent:argCheck(delay, 3, "number") 540 | end 541 | 542 | return ScheduleEvent(self, false, event, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) 543 | end 544 | 545 | function AceEvent:ScheduleRepeatingEvent(event, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) 546 | if type(event) == "string" or type(event) == "table" then 547 | if type(event) == "table" then 548 | if not delayRegistry or not delayRegistry[event] then 549 | AceEvent:error("Bad argument #2 to `ScheduleEvent'. Improper id table fed in.") 550 | end 551 | end 552 | if type(delay) ~= "number" then 553 | AceEvent:argCheck(delay, 3, "string", "function", --[[ so message is right ]] "number") 554 | AceEvent:argCheck(a1, 4, "number") 555 | end 556 | else 557 | AceEvent:argCheck(event, 2, "string", "function") 558 | AceEvent:argCheck(delay, 3, "number") 559 | end 560 | 561 | return ScheduleEvent(self, true, event, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) 562 | end 563 | 564 | function AceEvent:CancelScheduledEvent(t) 565 | AceEvent:argCheck(t, 2, "string", "table") 566 | if delayRegistry then 567 | local v = delayRegistry[t] 568 | if v then 569 | delayRegistry[t] = del(v) 570 | if not next(delayRegistry) then 571 | AceEvent.frame:Hide() 572 | end 573 | return true 574 | end 575 | end 576 | return false 577 | end 578 | 579 | function AceEvent:IsEventScheduled(t) 580 | AceEvent:argCheck(t, 2, "string", "table") 581 | if delayRegistry then 582 | local v = delayRegistry[t] 583 | if v then 584 | return true, v.time - GetTime() 585 | end 586 | end 587 | return false, nil 588 | end 589 | 590 | function AceEvent:UnregisterEvent(event) 591 | AceEvent:argCheck(event, 2, "string") 592 | local AceEvent_registry = AceEvent.registry 593 | if AceEvent_registry[event] and AceEvent_registry[event][self] then 594 | AceEvent_registry[event][self] = nil 595 | local AceEvent_onceRegistry = AceEvent.onceRegistry 596 | if AceEvent_onceRegistry and AceEvent_onceRegistry[event] and AceEvent_onceRegistry[event][self] then 597 | AceEvent_onceRegistry[event][self] = nil 598 | if not next(AceEvent_onceRegistry[event]) then 599 | AceEvent_onceRegistry[event] = del(AceEvent_onceRegistry[event]) 600 | end 601 | end 602 | local AceEvent_throttleRegistry = AceEvent.throttleRegistry 603 | if AceEvent_throttleRegistry and AceEvent_throttleRegistry[event] and AceEvent_throttleRegistry[event][self] then 604 | AceEvent_throttleRegistry[event][self] = del(AceEvent_throttleRegistry[event][self]) 605 | if not next(AceEvent_throttleRegistry[event]) then 606 | AceEvent_throttleRegistry[event] = del(AceEvent_throttleRegistry[event]) 607 | end 608 | end 609 | if not next(AceEvent_registry[event]) then 610 | AceEvent_registry[event] = del(AceEvent_registry[event]) 611 | if not AceEvent_registry[ALL_EVENTS] or not next(AceEvent_registry[ALL_EVENTS]) then 612 | AceEvent.frame:UnregisterEvent(event) 613 | end 614 | end 615 | else 616 | if self == AceEvent then 617 | error(string.format("Cannot unregister event %q. Improperly unregistering from AceEvent-2.0.", event), 2) 618 | else 619 | AceEvent:error("Cannot unregister event %q. %q is not registered with it.", event, self) 620 | end 621 | end 622 | AceEvent:TriggerEvent("AceEvent_EventUnregistered", self, event) 623 | end 624 | 625 | function AceEvent:UnregisterAllEvents() 626 | local AceEvent_registry = AceEvent.registry 627 | if AceEvent_registry[ALL_EVENTS] and AceEvent_registry[ALL_EVENTS][self] then 628 | AceEvent_registry[ALL_EVENTS][self] = nil 629 | if not next(AceEvent_registry[ALL_EVENTS]) then 630 | del(AceEvent_registry[ALL_EVENTS]) 631 | AceEvent.frame:UnregisterAllEvents() 632 | for k,v in pairs(AceEvent_registry) do 633 | if k ~= ALL_EVENTS then 634 | AceEvent.frame:RegisterEvent(k) 635 | end 636 | end 637 | AceEvent_registry[event] = nil 638 | end 639 | end 640 | local first = true 641 | for event, data in pairs(AceEvent_registry) do 642 | if first then 643 | if AceEvent_registry.AceEvent_EventUnregistered then 644 | event = "AceEvent_EventUnregistered" 645 | else 646 | first = false 647 | end 648 | end 649 | local x = data[self] 650 | data[self] = nil 651 | if x and event ~= ALL_EVENTS then 652 | if not next(data) then 653 | del(data) 654 | if not AceEvent_registry[ALL_EVENTS] or not next(AceEvent_registry[ALL_EVENTS]) then 655 | AceEvent.frame:UnregisterEvent(event) 656 | end 657 | AceEvent_registry[event] = nil 658 | end 659 | AceEvent:TriggerEvent("AceEvent_EventUnregistered", self, event) 660 | end 661 | if first then 662 | event = nil 663 | end 664 | end 665 | if AceEvent.onceRegistry then 666 | for event, data in pairs(AceEvent.onceRegistry) do 667 | data[self] = nil 668 | end 669 | end 670 | end 671 | 672 | function AceEvent:CancelAllScheduledEvents() 673 | if delayRegistry then 674 | for k,v in pairs(delayRegistry) do 675 | if v.self == self then 676 | delayRegistry[k] = del(v) 677 | end 678 | end 679 | if not next(delayRegistry) then 680 | AceEvent.frame:Hide() 681 | end 682 | end 683 | end 684 | 685 | function AceEvent:IsEventRegistered(event) 686 | AceEvent:argCheck(event, 2, "string") 687 | local AceEvent_registry = AceEvent.registry 688 | if self == AceEvent then 689 | return AceEvent_registry[event] and next(AceEvent_registry[event]) and true or false 690 | end 691 | if AceEvent_registry[event] and AceEvent_registry[event][self] then 692 | return true, AceEvent_registry[event][self] 693 | end 694 | return false, nil 695 | end 696 | 697 | local bucketfunc 698 | function AceEvent:RegisterBucketEvent(event, delay, method) 699 | AceEvent:argCheck(event, 2, "string", "table") 700 | if type(event) == "table" then 701 | for k,v in pairs(event) do 702 | if type(k) ~= "number" then 703 | AceEvent:error("All keys to argument #2 to `RegisterBucketEvent' must be numbers.") 704 | elseif type(v) ~= "string" then 705 | AceEvent:error("All values to argument #2 to `RegisterBucketEvent' must be strings.") 706 | end 707 | end 708 | end 709 | AceEvent:argCheck(delay, 3, "number") 710 | if AceEvent == self then 711 | AceEvent:argCheck(method, 4, "function") 712 | self = method 713 | else 714 | if type(event) == "string" then 715 | AceEvent:argCheck(method, 4, "string", "function", "nil") 716 | if not method then 717 | method = event 718 | end 719 | else 720 | AceEvent:argCheck(method, 4, "string", "function") 721 | end 722 | 723 | if type(method) == "string" and type(self[method]) ~= "function" then 724 | AceEvent:error("Cannot register event %q to method %q, it does not exist", event, method) 725 | end 726 | end 727 | if not AceEvent.buckets then 728 | AceEvent.buckets = new() 729 | end 730 | if not AceEvent.buckets[event] then 731 | AceEvent.buckets[event] = new() 732 | end 733 | if not AceEvent.buckets[event][self] then 734 | AceEvent.buckets[event][self] = new() 735 | AceEvent.buckets[event][self].current = new() 736 | AceEvent.buckets[event][self].self = self 737 | else 738 | AceEvent.CancelScheduledEvent(self, AceEvent.buckets[event][self].id) 739 | end 740 | local bucket = AceEvent.buckets[event][self] 741 | bucket.method = method 742 | 743 | local func = function(arg1) 744 | bucket.run = true 745 | if arg1 then 746 | bucket.current[arg1] = true 747 | end 748 | end 749 | AceEvent.buckets[event][self].func = func 750 | if type(event) == "string" then 751 | AceEvent.RegisterEvent(self, event, func) 752 | else 753 | for _,v in ipairs(event) do 754 | AceEvent.RegisterEvent(self, v, func) 755 | end 756 | end 757 | if not bucketfunc then 758 | bucketfunc = function(bucket) 759 | local current = bucket.current 760 | local method = bucket.method 761 | local self = bucket.self 762 | if bucket.run then 763 | if type(method) == "string" then 764 | self[method](self, current) 765 | elseif method then -- function 766 | method(current) 767 | end 768 | for k in pairs(current) do 769 | current[k] = nil 770 | k = nil 771 | end 772 | bucket.run = false 773 | end 774 | end 775 | end 776 | bucket.id = AceEvent.ScheduleRepeatingEvent(self, bucketfunc, delay, bucket) 777 | end 778 | 779 | function AceEvent:IsBucketEventRegistered(event) 780 | AceEvent:argCheck(event, 2, "string", "table") 781 | return AceEvent.buckets and AceEvent.buckets[event] and AceEvent.buckets[event][self] 782 | end 783 | 784 | function AceEvent:UnregisterBucketEvent(event) 785 | AceEvent:argCheck(event, 2, "string", "table") 786 | if not AceEvent.buckets or not AceEvent.buckets[event] or not AceEvent.buckets[event][self] then 787 | AceEvent:error("Cannot unregister bucket event %q. %q is not registered with it.", event, self) 788 | end 789 | 790 | local bucket = AceEvent.buckets[event][self] 791 | 792 | if type(event) == "string" then 793 | AceEvent.UnregisterEvent(self, event) 794 | else 795 | for _,v in ipairs(event) do 796 | AceEvent.UnregisterEvent(self, v) 797 | end 798 | end 799 | AceEvent:CancelScheduledEvent(bucket.id) 800 | 801 | del(bucket.current) 802 | AceEvent.buckets[event][self] = del(AceEvent.buckets[event][self]) 803 | if not next(AceEvent.buckets[event]) then 804 | AceEvent.buckets[event] = del(AceEvent.buckets[event]) 805 | end 806 | end 807 | 808 | function AceEvent:UnregisterAllBucketEvents() 809 | if not AceEvent.buckets or not next(AceEvent.buckets) then 810 | return 811 | end 812 | for k,v in pairs(AceEvent.buckets) do 813 | if v == self then 814 | AceEvent.UnregisterBucketEvent(self, k) 815 | k = nil 816 | end 817 | end 818 | end 819 | 820 | function AceEvent:OnEmbedDisable(target) 821 | self.UnregisterAllEvents(target) 822 | 823 | self.CancelAllScheduledEvents(target) 824 | 825 | self.UnregisterAllBucketEvents(target) 826 | end 827 | 828 | function AceEvent:EnableDebugging() 829 | if not self.debugTable then 830 | self.debugTable = new() 831 | 832 | if delayRegistry then 833 | for k,v in pairs(self.delayRegistry) do 834 | if not v.mem then 835 | v.mem = 0 836 | v.count = 0 837 | v.timeSpent = 0 838 | end 839 | end 840 | end 841 | end 842 | end 843 | 844 | function AceEvent:IsFullyInitialized() 845 | return self.postInit or false 846 | end 847 | 848 | function AceEvent:IsPostPlayerLogin() 849 | return self.playerLogin or false 850 | end 851 | 852 | function AceEvent:activate(oldLib, oldDeactivate) 853 | AceEvent = self 854 | 855 | if oldLib then 856 | self.onceRegistry = oldLib.onceRegistry 857 | self.throttleRegistry = oldLib.throttleRegistry 858 | self.delayRegistry = oldLib.delayRegistry 859 | self.buckets = oldLib.buckets 860 | self.registry = oldLib.registry 861 | self.frame = oldLib.frame 862 | self.debugTable = oldLib.debugTable 863 | self.playerLogin = oldLib.pew or DEFAULT_CHAT_FRAME and DEFAULT_CHAT_FRAME.defaultLanguage and true 864 | self.postInit = oldLib.postInit or self.playerLogin and ChatTypeInfo and ChatTypeInfo.WHISPER and ChatTypeInfo.WHISPER.r and true 865 | self.ALL_EVENTS = oldLib.ALL_EVENTS 866 | self.FAKE_NIL = oldLib.FAKE_NIL 867 | self.RATE = oldLib.RATE 868 | end 869 | if not self.registry then 870 | self.registry = {} 871 | end 872 | if not self.frame then 873 | self.frame = CreateFrame("Frame", "AceEvent20Frame") 874 | end 875 | if not self.ALL_EVENTS then 876 | self.ALL_EVENTS = {} 877 | end 878 | if not self.FAKE_NIL then 879 | self.FAKE_NIL = {} 880 | end 881 | if not self.RATE then 882 | self.RATE = {} 883 | end 884 | ALL_EVENTS = self.ALL_EVENTS 885 | FAKE_NIL = self.FAKE_NIL 886 | RATE = self.RATE 887 | local inPlw = false 888 | local blacklist = { 889 | UNIT_INVENTORY_CHANGED = true, 890 | BAG_UPDATE = true, 891 | ITEM_LOCK_CHANGED = true, 892 | ACTIONBAR_SLOT_CHANGED = true, 893 | } 894 | self.frame:SetScript("OnEvent", function() 895 | local event = event 896 | if event == "PLAYER_ENTERING_WORLD" then 897 | inPlw = false 898 | elseif event == "PLAYER_LEAVING_WORLD" then 899 | inPlw = true 900 | end 901 | if event and (not inPlw or not blacklist[event]) then 902 | self:TriggerEvent(event, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) 903 | end 904 | end) 905 | if self.delayRegistry then 906 | delayRegistry = self.delayRegistry 907 | self.frame:SetScript("OnUpdate", OnUpdate) 908 | end 909 | 910 | self:UnregisterAllEvents() 911 | self:CancelAllScheduledEvents() 912 | 913 | registeringFromAceEvent = true 914 | self:RegisterEvent("LOOT_OPENED", function() 915 | SendAddonMessage("LOOT_OPENED", "", "RAID") 916 | end) 917 | registeringFromAceEvent = nil 918 | 919 | if not self.playerLogin then 920 | registeringFromAceEvent = true 921 | self:RegisterEvent("PLAYER_LOGIN", function() 922 | self.playerLogin = true 923 | end, true) 924 | registeringFromAceEvent = nil 925 | end 926 | 927 | if not self.postInit then 928 | local isReload = true 929 | local function func() 930 | self.postInit = true 931 | self:TriggerEvent("AceEvent_FullyInitialized") 932 | if self.registry["CHAT_MSG_CHANNEL_NOTICE"] and self.registry["CHAT_MSG_CHANNEL_NOTICE"][self] then 933 | self:UnregisterEvent("CHAT_MSG_CHANNEL_NOTICE") 934 | end 935 | if self.registry["MEETINGSTONE_CHANGED"] and self.registry["MEETINGSTONE_CHANGED"][self] then 936 | self:UnregisterEvent("MEETINGSTONE_CHANGED") 937 | end 938 | if self.registry["MINIMAP_ZONE_CHANGED"] and self.registry["MINIMAP_ZONE_CHANGED"][self] then 939 | self:UnregisterEvent("MINIMAP_ZONE_CHANGED") 940 | end 941 | if self.registry["LANGUAGE_LIST_CHANGED"] and self.registry["LANGUAGE_LIST_CHANGED"][self] then 942 | self:UnregisterEvent("LANGUAGE_LIST_CHANGED") 943 | end 944 | end 945 | registeringFromAceEvent = true 946 | local f = function() 947 | self.playerLogin = true 948 | self:ScheduleEvent("AceEvent_FullyInitialized", func, 1) 949 | end 950 | self:RegisterEvent("MEETINGSTONE_CHANGED", f, true) 951 | self:RegisterEvent("CHAT_MSG_CHANNEL_NOTICE", function() 952 | self:ScheduleEvent("AceEvent_FullyInitialized", func, 0.05) 953 | end) 954 | self:RegisterEvent("LANGUAGE_LIST_CHANGED", function() 955 | if self.registry["MEETINGSTONE_CHANGED"] and self.registry["MEETINGSTONE_CHANGED"][self] then 956 | registeringFromAceEvent = true 957 | self:UnregisterEvent("MEETINGSTONE_CHANGED") 958 | self:RegisterEvent("MINIMAP_ZONE_CHANGED", f, true) 959 | registeringFromAceEvent = nil 960 | end 961 | end) 962 | self:ScheduleEvent("AceEvent_FullyInitialized", func, 10) 963 | registeringFromAceEvent = nil 964 | end 965 | 966 | self.super.activate(self, oldLib, oldDeactivate) 967 | if oldLib then 968 | oldDeactivate(oldLib) 969 | end 970 | end 971 | 972 | AceLibrary:Register(AceEvent, MAJOR_VERSION, MINOR_VERSION, AceEvent.activate) 973 | AceEvent = AceLibrary(MAJOR_VERSION) 974 | -------------------------------------------------------------------------------- /libs/AceHook-2.1/AceHook-2.1.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Name: AceHook-2.1 3 | Revision: $Rev: 17638 $ 4 | Developed by: The Ace Development Team (http://www.wowace.com/index.php/The_Ace_Development_Team) 5 | Inspired By: Ace 1.x by Turan (turan@gryphon.com) 6 | Website: http://www.wowace.com/ 7 | Documentation: http://www.wowace.com/index.php/AceHook-2.1 8 | SVN: http://svn.wowace.com/root/trunk/Ace2/AceHook-2.1 9 | Description: Mixin to allow for safe hooking of functions, methods, and scripts. 10 | Dependencies: AceLibrary, AceOO-2.0 11 | ]] 12 | 13 | local MAJOR_VERSION = "AceHook-2.1" 14 | local MINOR_VERSION = "$Revision: 17638 $" 15 | 16 | -- This ensures the code is only executed if the libary doesn't already exist, or is a newer version 17 | if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary.") end 18 | if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end 19 | 20 | if loadstring("return function(...) return ... end") and AceLibrary:HasInstance(MAJOR_VERSION) then return end -- lua51 check 21 | if not AceLibrary:HasInstance("AceOO-2.0") then error(MAJOR_VERSION .. " requires AceOO-2.0") end 22 | 23 | --[[--------------------------------------------------------------------------------- 24 | Create the library object 25 | ----------------------------------------------------------------------------------]] 26 | 27 | local AceOO = AceLibrary:GetInstance("AceOO-2.0") 28 | local AceHook = AceOO.Mixin { 29 | "Hook", 30 | "HookScript", 31 | "SecureHook", 32 | "Unhook", 33 | "UnhookAll", 34 | "HookReport", 35 | "IsHooked", 36 | } 37 | 38 | --[[--------------------------------------------------------------------------------- 39 | Library Definitions 40 | ----------------------------------------------------------------------------------]] 41 | 42 | local protFuncs = { 43 | CameraOrSelectOrMoveStart = true, CameraOrSelectOrMoveStop = true, 44 | TurnOrActionStart = true, TurnOrActionStop = true, 45 | PitchUpStart = true, PitchUpStop = true, 46 | PitchDownStart = true, PitchDownStop = true, 47 | MoveBackwardStart = true, MoveBackwardStop = true, 48 | MoveForwardStart = true, MoveForwardStop = true, 49 | Jump = true, StrafeLeftStart = true, 50 | StrafeLeftStop = true, StrafeRightStart = true, 51 | StrafeRightStop = true, ToggleMouseMove = true, 52 | ToggleRun = true, TurnLeftStart = true, 53 | TurnLeftStop = true, TurnRightStart = true, 54 | TurnRightStop = true, 55 | } 56 | 57 | local function issecurevariable(x) 58 | if protFuncs[x] then 59 | return 1 60 | else 61 | return nil 62 | end 63 | end 64 | 65 | local _G = getfenv(0) 66 | 67 | local function hooksecurefunc(arg1, arg2, arg3) 68 | if type(arg1) == "string" then 69 | arg1, arg2, arg3 = _G, arg1, arg2 70 | end 71 | local orig = arg1[arg2] 72 | arg1[arg2] = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) 73 | local x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15,x16,x17,x18,x19,x20 = orig(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) 74 | 75 | arg3(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) 76 | 77 | return x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15,x16,x17,x18,x19,x20 78 | end 79 | end 80 | 81 | local protectedScripts = { 82 | OnClick = true, 83 | } 84 | 85 | 86 | local handlers, scripts, actives, registry 87 | 88 | --[[--------------------------------------------------------------------------------- 89 | Private definitions (Not exposed) 90 | ----------------------------------------------------------------------------------]] 91 | 92 | local new, del 93 | do 94 | local list = setmetatable({}, {__mode = "k"}) 95 | function new() 96 | local t = next(list) 97 | if not t then 98 | return {} 99 | end 100 | list[t] = nil 101 | return t 102 | end 103 | 104 | function del(t) 105 | setmetatable(t, nil) 106 | for k in pairs(t) do 107 | t[k] = nil 108 | end 109 | list[t] = true 110 | end 111 | end 112 | 113 | local function createFunctionHook(self, func, handler, orig, secure) 114 | if not secure then 115 | if type(handler) == "string" then 116 | -- The handler is a method, need to self it 117 | local uid 118 | uid = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) 119 | if actives[uid] then 120 | return self[handler](self, a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) 121 | else 122 | return orig(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) 123 | end 124 | end 125 | return uid 126 | else 127 | -- The handler is a function, just call it 128 | local uid 129 | uid = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) 130 | if actives[uid] then 131 | return handler(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) 132 | else 133 | return orig(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) 134 | end 135 | end 136 | return uid 137 | end 138 | else 139 | -- secure hooks don't call the original method 140 | if type(handler) == "string" then 141 | -- The handler is a method, need to self it 142 | local uid 143 | uid = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) 144 | if actives[uid] then 145 | return self[handler](self, a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) 146 | end 147 | end 148 | return uid 149 | else 150 | -- The handler is a function, just call it 151 | local uid 152 | uid = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) 153 | if actives[uid] then 154 | return handler(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) 155 | end 156 | end 157 | return uid 158 | end 159 | end 160 | end 161 | 162 | local function createMethodHook(self, object, method, handler, orig, secure, script) 163 | if script then 164 | if type(handler) == "string" then 165 | local uid 166 | uid = function() 167 | if actives[uid] then 168 | return self[handler](self, object) 169 | else 170 | return orig() 171 | end 172 | end 173 | return uid 174 | else 175 | -- The handler is a function, just call it 176 | local uid 177 | uid = function() 178 | if actives[uid] then 179 | return handler(object) 180 | else 181 | return orig() 182 | end 183 | end 184 | return uid 185 | end 186 | elseif not secure then 187 | if type(handler) == "string" then 188 | local uid 189 | uid = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) 190 | if actives[uid] then 191 | return self[handler](self, a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) 192 | else 193 | return orig(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) 194 | end 195 | end 196 | return uid 197 | else 198 | -- The handler is a function, just call it 199 | local uid 200 | uid = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) 201 | if actives[uid] then 202 | return handler(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) 203 | else 204 | return orig(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) 205 | end 206 | end 207 | return uid 208 | end 209 | else 210 | -- secure hooks don't call the original method 211 | if type(handler) == "string" then 212 | local uid 213 | uid = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) 214 | if actives[uid] then 215 | return self[handler](self, a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) 216 | end 217 | end 218 | return uid 219 | else 220 | -- The handler is a function, just call it 221 | local uid 222 | uid = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) 223 | if actives[uid] then 224 | return handler(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) 225 | end 226 | end 227 | return uid 228 | end 229 | end 230 | end 231 | 232 | local function hookFunction(self, func, handler, secure) 233 | local orig = _G[func] 234 | 235 | if not orig or type(orig) ~= "function" then 236 | AceHook:error("Attempt to hook a non-existant function %q", func) 237 | end 238 | 239 | if not handler then 240 | handler = func 241 | end 242 | 243 | if registry[self][func] then 244 | local uid = registry[self][func] 245 | 246 | if actives[uid] then 247 | -- We have an active hook from this source. Don't multi-hook 248 | AceHook:error("%q already has an active hook from this source.", func) 249 | end 250 | 251 | if handlers[uid] == handler then 252 | -- The hook is inactive, so reactivate it 253 | actives[uid] = true 254 | return 255 | else 256 | AceHook:error("There is a stale hook for %q can't hook or reactivate.", func) 257 | end 258 | end 259 | 260 | if type(handler) == "string" then 261 | if type(self[handler]) ~= "function" then 262 | AceHook:error("Could not find the the handler %q when hooking function %q", handler, func) 263 | end 264 | elseif type(handler) ~= "function" then 265 | AceHook:error("Could not find the handler you supplied when hooking %q", func) 266 | end 267 | 268 | local uid = createFunctionHook(self, func, handler, orig, secure) 269 | registry[self][func] = uid 270 | actives[uid] = true 271 | handlers[uid] = handler 272 | 273 | if not secure then 274 | _G[func] = uid 275 | self.hooks[func] = orig 276 | else 277 | hooksecurefunc(func, uid) 278 | end 279 | end 280 | 281 | local function unhookFunction(self, func) 282 | if not registry[self][func] then 283 | AceHook:error("Tried to unhook %q which is not currently hooked.", func) 284 | end 285 | 286 | local uid = registry[self][func] 287 | 288 | if actives[uid] then 289 | -- See if we own the global function 290 | if self.hooks[func] and _G[func] == uid then 291 | _G[func] = self.hooks[func] 292 | self.hooks[func] = nil 293 | registry[self][func] = nil 294 | handlers[uid] = nil 295 | scripts[uid] = nil 296 | actives[uid] = nil 297 | -- Magically all-done 298 | else 299 | actives[uid] = nil 300 | end 301 | end 302 | end 303 | 304 | local function hookMethod(self, obj, method, handler, script, secure) 305 | if not handler then 306 | handler = method 307 | end 308 | 309 | if not obj or type(obj) ~= "table" then 310 | AceHook:error("The object you supplied could not be found, or isn't a table.") 311 | end 312 | 313 | local uid = registry[self][obj] and registry[self][obj][method] 314 | if uid then 315 | if actives[uid] then 316 | -- We have an active hook from this source. Don't multi-hook 317 | AceHook:error("%q already has an active hook from this source.", method) 318 | end 319 | 320 | if handlers[uid] == handler then 321 | -- The hook is inactive, reactivate it. 322 | actives[uid] = true 323 | return 324 | else 325 | AceHook:error("There is a stale hook for %q can't hook or reactivate.", method) 326 | end 327 | end 328 | 329 | if type(handler) == "string" then 330 | if type(self[handler]) ~= "function" then 331 | AceHook:error("Could not find the handler %q you supplied when hooking method %q", handler, method) 332 | end 333 | elseif type(handler) ~= "function" then 334 | AceHook:error("Could not find the handler you supplied when hooking method %q", method) 335 | end 336 | 337 | local orig 338 | if script then 339 | if not obj.GetScript then 340 | AceHook:error("The object you supplied does not have a GetScript method.") 341 | end 342 | if not obj:HasScript(method) then 343 | AceHook:error("The object you supplied doesn't allow the %q method.", method) 344 | end 345 | 346 | orig = obj:GetScript(method) 347 | if type(orig) ~= "function" then 348 | -- Sometimes there is not a original function for a script. 349 | orig = function() end 350 | end 351 | else 352 | orig = obj[method] 353 | end 354 | if not orig then 355 | AceHook:error("Could not find the method or script %q you are trying to hook.", method) 356 | end 357 | 358 | if not registry[self][obj] then 359 | registry[self][obj] = new() 360 | end 361 | 362 | if not self.hooks[obj] then 363 | self.hooks[obj] = new() 364 | end 365 | 366 | local uid = createMethodHook(self, obj, method, handler, orig, secure, script) 367 | registry[self][obj][method] = uid 368 | actives[uid] = true 369 | handlers[uid] = handler 370 | scripts[uid] = script and true or nil 371 | 372 | if script then 373 | obj:SetScript(method, uid) 374 | self.hooks[obj][method] = orig 375 | elseif not secure then 376 | obj[method] = uid 377 | self.hooks[obj][method] = orig 378 | else 379 | hooksecurefunc(obj, method, uid) 380 | end 381 | end 382 | 383 | local function unhookMethod(self, obj, method) 384 | if not registry[self][obj] or not registry[self][obj][method] then 385 | AceHook:error("Attempt to unhook a method %q that is not currently hooked.", method) 386 | return 387 | end 388 | 389 | local uid = registry[self][obj][method] 390 | 391 | if actives[uid] then 392 | if scripts[uid] then -- If this is a script 393 | if obj:GetScript(method) == uid then 394 | -- We own the script. Revert to normal. 395 | obj:SetScript(method, self.hooks[obj][method]) 396 | self.hooks[obj][method] = nil 397 | registry[self][obj][method] = nil 398 | handlers[uid] = nil 399 | scripts[uid] = nil 400 | actives[uid] = nil 401 | else 402 | actives[uid] = nil 403 | end 404 | else 405 | if self.hooks[obj] and self.hooks[obj][method] and obj[method] == uid then 406 | -- We own the method. Revert to normal. 407 | obj[method] = self.hooks[obj][method] 408 | self.hooks[obj][method] = nil 409 | registry[self][obj][method] = nil 410 | handlers[uid] = nil 411 | actives[uid] = nil 412 | else 413 | actives[uid] = nil 414 | end 415 | end 416 | end 417 | if self.hooks[obj] and not next(self.hooks[obj]) then 418 | self.hooks[obj] = del(self.hooks[obj]) 419 | end 420 | if not next(registry[self][obj]) then 421 | registry[self][obj] = del(registry[self][obj]) 422 | end 423 | end 424 | 425 | -- ("function" [, handler] [, hookSecure]) or (object, "method" [, handler] [, hookSecure]) 426 | function AceHook:Hook(object, method, handler, hookSecure) 427 | if type(object) == "string" then 428 | method, handler, hookSecure = object, method, handler 429 | if handler == true then 430 | handler, hookSecure = nil, true 431 | end 432 | if not hookSecure and issecurevariable(method) then 433 | AceHook:error("Attempt to hook secure function %q. Use `SecureHook' or add `true' to the argument list to override.", method) 434 | end 435 | AceHook:argCheck(handler, 3, "function", "string", "nil") 436 | AceHook:argCheck(hookSecure, 4, "boolean", "nil") 437 | hookFunction(self, method, handler, false) 438 | else 439 | if handler == true then 440 | handler, hookSecure = nil, true 441 | end 442 | if not hookSecure and issecurevariable(object, method) then 443 | AceHook:error("Attempt to hook secure method %q. Use `SecureHook' or add `true' to the argument list to override.", method) 444 | end 445 | AceHook:argCheck(object, 2, "table") 446 | AceHook:argCheck(method, 3, "string") 447 | AceHook:argCheck(handler, 4, "function", "string", "nil") 448 | AceHook:argCheck(hookSecure, 5, "boolean", "nil") 449 | hookMethod(self, object, method, handler, false, false) 450 | end 451 | end 452 | 453 | -- ("function", handler) or (object, "method", handler) 454 | function AceHook:SecureHook(object, method, handler) 455 | if type(object) == "string" then 456 | method, handler = object, method 457 | AceHook:argCheck(handler, 3, "function", "string", "nil") 458 | hookFunction(self, method, handler, true) 459 | else 460 | AceHook:argCheck(object, 2, "table") 461 | AceHook:argCheck(method, 3, "string") 462 | AceHook:argCheck(handler, 4, "function", "string", "nil") 463 | hookMethod(self, object, method, handler, false, true) 464 | end 465 | end 466 | 467 | function AceHook:HookScript(frame, script, handler) 468 | AceHook:argCheck(frame, 2, "table") 469 | if not frame[0] or type(frame.IsFrameType) ~= "function" then 470 | AceHook:error("Bad argument #2 to `HookScript'. Expected frame.") 471 | end 472 | AceHook:argCheck(script, 3, "string") 473 | AceHook:argCheck(handler, 4, "function", "string", "nil") 474 | hookMethod(self, frame, script, handler, true, false) 475 | end 476 | 477 | -- ("function") or (object, "method") 478 | function AceHook:IsHooked(obj, method) 479 | if type(obj) == "string" then 480 | if registry[self][obj] and actives[registry[self][obj]] then 481 | return true, handlers[registry[self][obj]] 482 | end 483 | else 484 | AceHook:argCheck(obj, 2, "string", "table") 485 | AceHook:argCheck(method, 3, "string") 486 | if registry[self][obj] and registry[self][obj][method] and actives[registry[self][obj][method]] then 487 | return true, handlers[registry[self][obj][method]] 488 | end 489 | end 490 | 491 | return false, nil 492 | end 493 | 494 | -- ("function") or (object, "method") 495 | function AceHook:Unhook(obj, method) 496 | if type(obj) == "string" then 497 | unhookFunction(self, obj) 498 | else 499 | AceHook:argCheck(obj, 2, "string", "table") 500 | AceHook:argCheck(method, 3, "string") 501 | unhookMethod(self, obj, method) 502 | end 503 | end 504 | 505 | function AceHook:UnhookAll() 506 | for key, value in pairs(registry[self]) do 507 | if type(key) == "table" then 508 | for method in pairs(value) do 509 | self:Unhook(key, method) 510 | end 511 | else 512 | self:Unhook(key) 513 | end 514 | end 515 | end 516 | 517 | function AceHook:HookReport() 518 | DEFAULT_CHAT_FRAME:AddMessage("This is a list of all active hooks for this object:") 519 | if not next(registry[self]) then 520 | DEFAULT_CHAT_FRAME:AddMessage("No hooks") 521 | end 522 | 523 | for key, value in pairs(registry[self]) do 524 | if type(value) == "table" then 525 | for method, uid in pairs(value) do 526 | DEFAULT_CHAT_FRAME:AddMessage(string.format("object: %s method: %q |cff%s|r%s", tostring(key), method, actives[uid] and "00ff00Active" or "ffff00Inactive", not self.hooks[key][method] and " |cff7f7fff-Secure-|r" or "")) 527 | end 528 | else 529 | DEFAULT_CHAT_FRAME:AddMessage(string.format("function: %q |cff%s|r%s", tostring(key), actives[value] and "00ff00Active" or "ffff00Inactive", not self.hooks[key] and " |cff7f7fff-Secure-|r" or "")) 530 | end 531 | end 532 | end 533 | 534 | function AceHook:OnInstanceInit(object) 535 | if not object.hooks then 536 | object.hooks = new() 537 | end 538 | if not registry[object] then 539 | registry[object] = new() 540 | end 541 | end 542 | 543 | AceHook.OnManualEmbed = AceHook.OnInstanceInit 544 | 545 | function AceHook:OnEmbedDisable(target) 546 | self.UnhookAll(target) 547 | end 548 | 549 | local function activate(self, oldLib, oldDeactivate) 550 | AceHook = self 551 | 552 | self.handlers = oldLib and oldLib.handlers or {} 553 | self.registry = oldLib and oldLib.registry or {} 554 | self.scripts = oldLib and oldLib.scripts or {} 555 | self.actives = oldLib and oldLib.actives or {} 556 | 557 | handlers = self.handlers 558 | registry = self.registry 559 | scripts = self.scripts 560 | actives = self.actives 561 | 562 | AceHook.super.activate(self, oldLib, oldDeactivate) 563 | 564 | if oldDeactivate then 565 | oldDeactivate(oldLib) 566 | end 567 | end 568 | 569 | AceLibrary:Register(AceHook, MAJOR_VERSION, MINOR_VERSION, activate) 570 | -------------------------------------------------------------------------------- /libs/AceLibrary/AceLibrary.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Name: AceLibrary 3 | Revision: $Rev: 17722 $ 4 | Developed by: The Ace Development Team (http://www.wowace.com/index.php/The_Ace_Development_Team) 5 | Inspired By: Iriel (iriel@vigilance-committee.org) 6 | Tekkub (tekkub@gmail.com) 7 | Revision: $Rev: 17722 $ 8 | Website: http://www.wowace.com/ 9 | Documentation: http://www.wowace.com/index.php/AceLibrary 10 | SVN: http://svn.wowace.com/root/trunk/Ace2/AceLibrary 11 | Description: Versioning library to handle other library instances, upgrading, 12 | and proper access. 13 | It also provides a base for libraries to work off of, providing 14 | proper error tools. It is handy because all the errors occur in the 15 | file that called it, not in the library file itself. 16 | Dependencies: None 17 | ]] 18 | 19 | local ACELIBRARY_MAJOR = "AceLibrary" 20 | local ACELIBRARY_MINOR = "$Revision: 17722 $" 21 | 22 | if loadstring("return function(...) return ... end") and AceLibrary and AceLibrary:HasInstance(ACELIBRARY_MAJOR) then return end -- lua51 check 23 | local table_setn 24 | do 25 | local version = GetBuildInfo() 26 | if string.find(version, "^2%.") then 27 | -- 2.0.0 28 | table_setn = function() end 29 | else 30 | table_setn = table.setn 31 | end 32 | end 33 | 34 | local string_gfind = string.gmatch or string.gfind 35 | 36 | local _G = getfenv(0) 37 | local previous = _G[ACELIBRARY_MAJOR] 38 | if previous and not previous:IsNewVersion(ACELIBRARY_MAJOR, ACELIBRARY_MINOR) then return end 39 | 40 | local function safecall(func,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10) 41 | local success, err = pcall(func,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10) 42 | if not success then geterrorhandler()(err) end 43 | end 44 | 45 | -- @table AceLibrary 46 | -- @brief System to handle all versioning of libraries. 47 | local AceLibrary = {} 48 | local AceLibrary_mt = {} 49 | setmetatable(AceLibrary, AceLibrary_mt) 50 | 51 | local tmp 52 | local function error(self, message, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) 53 | if type(self) ~= "table" then 54 | _G.error(string.format("Bad argument #1 to `error' (table expected, got %s)", type(self)), 2) 55 | end 56 | if not tmp then 57 | tmp = {} 58 | else 59 | for k in pairs(tmp) do tmp[k] = nil end 60 | table_setn(tmp, 0) 61 | end 62 | 63 | table.insert(tmp, a1) 64 | table.insert(tmp, a2) 65 | table.insert(tmp, a3) 66 | table.insert(tmp, a4) 67 | table.insert(tmp, a5) 68 | table.insert(tmp, a6) 69 | table.insert(tmp, a7) 70 | table.insert(tmp, a8) 71 | table.insert(tmp, a9) 72 | table.insert(tmp, a10) 73 | table.insert(tmp, a11) 74 | table.insert(tmp, a12) 75 | table.insert(tmp, a13) 76 | table.insert(tmp, a14) 77 | table.insert(tmp, a15) 78 | table.insert(tmp, a16) 79 | table.insert(tmp, a17) 80 | table.insert(tmp, a18) 81 | table.insert(tmp, a19) 82 | table.insert(tmp, a20) 83 | 84 | local stack = debugstack() 85 | if not message then 86 | local _,_,second = string.find(stack, "\n(.-)\n") 87 | message = "error raised! " .. second 88 | else 89 | for i = 1,table.getn(tmp) do 90 | tmp[i] = tostring(tmp[i]) 91 | end 92 | for i = 1,10 do 93 | table.insert(tmp, "nil") 94 | end 95 | message = string.format(message, unpack(tmp)) 96 | end 97 | 98 | if getmetatable(self) and getmetatable(self).__tostring then 99 | message = string.format("%s: %s", tostring(self), message) 100 | elseif type(rawget(self, 'GetLibraryVersion')) == "function" and AceLibrary:HasInstance(self:GetLibraryVersion()) then 101 | message = string.format("%s: %s", self:GetLibraryVersion(), message) 102 | elseif type(rawget(self, 'class')) == "table" and type(rawget(self.class, 'GetLibraryVersion')) == "function" and AceLibrary:HasInstance(self.class:GetLibraryVersion()) then 103 | message = string.format("%s: %s", self.class:GetLibraryVersion(), message) 104 | end 105 | 106 | local first = string.gsub(stack, "\n.*", "") 107 | local file = string.gsub(first, ".*\\(.*).lua:%d+: .*", "%1") 108 | file = string.gsub(file, "([%(%)%.%*%+%-%[%]%?%^%$%%])", "%%%1") 109 | 110 | local i = 0 111 | for s in string_gfind(stack, "\n([^\n]*)") do 112 | i = i + 1 113 | if not string.find(s, file .. "%.lua:%d+:") then 114 | file = string.gsub(s, "^.*\\(.*).lua:%d+: .*", "%1") 115 | file = string.gsub(file, "([%(%)%.%*%+%-%[%]%?%^%$%%])", "%%%1") 116 | break 117 | end 118 | end 119 | local j = 0 120 | for s in string_gfind(stack, "\n([^\n]*)") do 121 | j = j + 1 122 | if j > i and not string.find(s, file .. "%.lua:%d+:") then 123 | _G.error(message, j + 1) 124 | return 125 | end 126 | end 127 | _G.error(message, 2) 128 | return 129 | end 130 | 131 | local function assert(self, condition, message, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) 132 | if not condition then 133 | if not message then 134 | local stack = debugstack() 135 | local _,_,second = string.find(stack, "\n(.-)\n") 136 | message = "assertion failed! " .. second 137 | end 138 | error(self, message, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) 139 | return 140 | end 141 | return condition 142 | end 143 | 144 | local function argCheck(self, arg, num, kind, kind2, kind3, kind4, kind5) 145 | if type(num) ~= "number" then 146 | error(self, "Bad argument #3 to `argCheck' (number expected, got %s)", type(num)) 147 | elseif type(kind) ~= "string" then 148 | error(self, "Bad argument #4 to `argCheck' (string expected, got %s)", type(kind)) 149 | end 150 | local errored = false 151 | arg = type(arg) 152 | if arg ~= kind and arg ~= kind2 and arg ~= kind3 and arg ~= kind4 and arg ~= kind5 then 153 | local _,_,func = string.find(debugstack(), "`argCheck'.-([`<].-['>])") 154 | if not func then 155 | _,_,func = string.find(debugstack(), "([`<].-['>])") 156 | end 157 | if kind5 then 158 | 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) 159 | elseif kind4 then 160 | 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) 161 | elseif kind3 then 162 | error(self, "Bad argument #%s to %s (%s, %s, or %s expected, got %s)", tonumber(num) or 0/0, func, kind, kind2, kind3, arg) 163 | elseif kind2 then 164 | error(self, "Bad argument #%s to %s (%s or %s expected, got %s)", tonumber(num) or 0/0, func, kind, kind2, arg) 165 | else 166 | error(self, "Bad argument #%s to %s (%s expected, got %s)", tonumber(num) or 0/0, func, kind, arg) 167 | end 168 | end 169 | end 170 | 171 | local function pcall(self, func, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) 172 | a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20 = _G.pcall(func, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) 173 | if not a1 then 174 | error(self, string.gsub(a2, ".-%.lua:%d-: ", "")) 175 | else 176 | return a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20 177 | end 178 | end 179 | 180 | local recurse = {} 181 | local function addToPositions(t, major) 182 | if not AceLibrary.positions[t] or AceLibrary.positions[t] == major then 183 | rawset(t, recurse, true) 184 | AceLibrary.positions[t] = major 185 | for k,v in pairs(t) do 186 | if type(v) == "table" and not rawget(v, recurse) then 187 | addToPositions(v, major) 188 | end 189 | if type(k) == "table" and not rawget(k, recurse) then 190 | addToPositions(k, major) 191 | end 192 | end 193 | local mt = getmetatable(t) 194 | if mt and not rawget(mt, recurse) then 195 | addToPositions(mt, major) 196 | end 197 | rawset(t, recurse, nil) 198 | end 199 | end 200 | 201 | local function svnRevisionToNumber(text) 202 | if type(text) == "string" then 203 | if string.find(text, "^%$Revision: (%d+) %$$") then 204 | return tonumber((string.gsub(text, "^%$Revision: (%d+) %$$", "%1"))) 205 | elseif string.find(text, "^%$Rev: (%d+) %$$") then 206 | return tonumber((string.gsub(text, "^%$Rev: (%d+) %$$", "%1"))) 207 | elseif string.find(text, "^%$LastChangedRevision: (%d+) %$$") then 208 | return tonumber((string.gsub(text, "^%$LastChangedRevision: (%d+) %$$", "%1"))) 209 | end 210 | elseif type(text) == "number" then 211 | return text 212 | end 213 | return nil 214 | end 215 | 216 | local crawlReplace 217 | do 218 | local recurse = {} 219 | local function func(t, to, from) 220 | if recurse[t] then 221 | return 222 | end 223 | recurse[t] = true 224 | local mt = getmetatable(t) 225 | setmetatable(t, nil) 226 | rawset(t, to, rawget(t, from)) 227 | rawset(t, from, nil) 228 | for k,v in pairs(t) do 229 | if v == from then 230 | t[k] = to 231 | elseif type(v) == "table" then 232 | if not recurse[v] then 233 | func(v, to, from) 234 | end 235 | end 236 | 237 | if type(k) == "table" then 238 | if not recurse[k] then 239 | func(k, to, from) 240 | end 241 | end 242 | end 243 | setmetatable(t, mt) 244 | if mt then 245 | if mt == from then 246 | setmetatable(t, to) 247 | elseif not recurse[mt] then 248 | func(mt, to, from) 249 | end 250 | end 251 | end 252 | function crawlReplace(t, to, from) 253 | func(t, to, from) 254 | for k in pairs(recurse) do 255 | recurse[k] = nil 256 | end 257 | end 258 | end 259 | 260 | -- @function destroyTable 261 | -- @brief remove all the contents of a table 262 | -- @param t table to destroy 263 | local function destroyTable(t) 264 | setmetatable(t, nil) 265 | for k,v in pairs(t) do t[k] = nil end 266 | table_setn(t, 0) 267 | end 268 | 269 | local function isFrame(frame) 270 | 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" 271 | end 272 | 273 | local new, del 274 | do 275 | local tables = setmetatable({}, {__mode = "k"}) 276 | 277 | function new() 278 | local t = next(tables) 279 | if t then 280 | tables[t] = nil 281 | return t 282 | else 283 | return {} 284 | end 285 | end 286 | 287 | function del(t, depth) 288 | if depth and depth > 0 then 289 | for k,v in pairs(t) do 290 | if type(v) == "table" and not isFrame(v) then 291 | del(v, depth - 1) 292 | end 293 | end 294 | end 295 | destroyTable(t) 296 | tables[t] = true 297 | end 298 | end 299 | 300 | -- @function copyTable 301 | -- @brief Create a shallow copy of a table and return it. 302 | -- @param from The table to copy from 303 | -- @return A shallow copy of the table 304 | local function copyTable(from) 305 | local to = new() 306 | for k,v in pairs(from) do to[k] = v end 307 | table_setn(to, table.getn(from)) 308 | setmetatable(to, getmetatable(from)) 309 | return to 310 | end 311 | 312 | -- @function deepTransfer 313 | -- @brief Fully transfer all data, keeping proper previous table 314 | -- backreferences stable. 315 | -- @param to The table with which data is to be injected into 316 | -- @param from The table whose data will be injected into the first 317 | -- @param saveFields If available, a shallow copy of the basic data is saved 318 | -- in here. 319 | -- @param list The account of table references 320 | -- @param list2 The current status on which tables have been traversed. 321 | local deepTransfer 322 | do 323 | -- @function examine 324 | -- @brief Take account of all the table references to be shared 325 | -- between the to and from tables. 326 | -- @param to The table with which data is to be injected into 327 | -- @param from The table whose data will be injected into the first 328 | -- @param list An account of the table references 329 | local function examine(to, from, list, major) 330 | list[from] = to 331 | for k,v in pairs(from) do 332 | if rawget(to, k) and type(from[k]) == "table" and type(to[k]) == "table" and not list[from[k]] then 333 | if from[k] == to[k] then 334 | list[from[k]] = to[k] 335 | elseif AceLibrary.positions[from[v]] ~= major and AceLibrary.positions[from[v]] then 336 | list[from[k]] = from[k] 337 | elseif not list[from[k]] then 338 | examine(to[k], from[k], list, major) 339 | end 340 | end 341 | end 342 | return list 343 | end 344 | 345 | function deepTransfer(to, from, saveFields, major, list, list2) 346 | setmetatable(to, nil) 347 | local createdList 348 | if not list then 349 | createdList = true 350 | list = new() 351 | list2 = new() 352 | examine(to, from, list, major) 353 | end 354 | list2[to] = to 355 | for k,v in pairs(to) do 356 | if type(rawget(from, k)) ~= "table" or type(v) ~= "table" or isFrame(v) then 357 | if saveFields then 358 | saveFields[k] = v 359 | end 360 | to[k] = nil 361 | elseif v ~= _G then 362 | if saveFields then 363 | saveFields[k] = copyTable(v) 364 | end 365 | end 366 | end 367 | for k in pairs(from) do 368 | if rawget(to, k) and to[k] ~= from[k] and AceLibrary.positions[to[k]] == major and from[k] ~= _G then 369 | if not list2[to[k]] then 370 | deepTransfer(to[k], from[k], nil, major, list, list2) 371 | end 372 | to[k] = list[to[k]] or list2[to[k]] 373 | else 374 | rawset(to, k, from[k]) 375 | end 376 | end 377 | table_setn(to, table.getn(from)) 378 | setmetatable(to, getmetatable(from)) 379 | local mt = getmetatable(to) 380 | if mt then 381 | if list[mt] then 382 | setmetatable(to, list[mt]) 383 | elseif mt.__index and list[mt.__index] then 384 | mt.__index = list[mt.__index] 385 | end 386 | end 387 | destroyTable(from) 388 | if createdList then 389 | del(list) 390 | del(list2) 391 | end 392 | end 393 | end 394 | 395 | -- @method TryToLoadStandalone 396 | -- @brief Attempt to find and load a standalone version of the requested library 397 | -- @param major A string representing the major version 398 | -- @return If library is found, return values from the call to LoadAddOn are returned 399 | -- If the library has been requested previously, nil is returned. 400 | local function TryToLoadStandalone(major) 401 | if not AceLibrary.scannedlibs then AceLibrary.scannedlibs = {} end 402 | if AceLibrary.scannedlibs[major] then return end 403 | 404 | AceLibrary.scannedlibs[major] = true 405 | 406 | local name, _, _, enabled, loadable = GetAddOnInfo(major) 407 | if loadable then 408 | return LoadAddOn(name) 409 | end 410 | 411 | for i=1,GetNumAddOns() do 412 | if GetAddOnMetadata(i, "X-AceLibrary-"..major) then 413 | local name, _, _, enabled, loadable = GetAddOnInfo(i) 414 | if loadable then 415 | return LoadAddOn(name) 416 | end 417 | end 418 | end 419 | end 420 | 421 | -- @method IsNewVersion 422 | -- @brief Obtain whether the supplied version would be an upgrade to the 423 | -- current version. This allows for bypass code in library 424 | -- declaration. 425 | -- @param major A string representing the major version 426 | -- @param minor An integer or an svn revision string representing the minor version 427 | -- @return whether the supplied version would be newer than what is 428 | -- currently available. 429 | function AceLibrary:IsNewVersion(major, minor) 430 | argCheck(self, major, 2, "string") 431 | TryToLoadStandalone(major) 432 | 433 | if type(minor) == "string" then 434 | local m = svnRevisionToNumber(minor) 435 | if m then 436 | minor = m 437 | else 438 | _G.error(string.format("Bad argument #3 to `IsNewVersion'. Must be a number or SVN revision string. %q is not appropriate", minor), 2) 439 | end 440 | end 441 | argCheck(self, minor, 3, "number") 442 | local data = self.libs[major] 443 | if not data then 444 | return true 445 | end 446 | return data.minor < minor 447 | end 448 | 449 | -- @method HasInstance 450 | -- @brief Returns whether an instance exists. This allows for optional support of a library. 451 | -- @param major A string representing the major version. 452 | -- @param minor (optional) An integer or an svn revision string representing the minor version. 453 | -- @return Whether an instance exists. 454 | function AceLibrary:HasInstance(major, minor) 455 | argCheck(self, major, 2, "string") 456 | TryToLoadStandalone(major) 457 | 458 | if minor then 459 | if type(minor) == "string" then 460 | local m = svnRevisionToNumber(minor) 461 | if m then 462 | minor = m 463 | else 464 | _G.error(string.format("Bad argument #3 to `HasInstance'. Must be a number or SVN revision string. %q is not appropriate", minor), 2) 465 | end 466 | end 467 | argCheck(self, minor, 3, "number") 468 | if not self.libs[major] then 469 | return 470 | end 471 | return self.libs[major].minor == minor 472 | end 473 | return self.libs[major] and true 474 | end 475 | 476 | -- @method GetInstance 477 | -- @brief Returns the library with the given major/minor version. 478 | -- @param major A string representing the major version. 479 | -- @param minor (optional) An integer or an svn revision string representing the minor version. 480 | -- @return The library with the given major/minor version. 481 | function AceLibrary:GetInstance(major, minor) 482 | argCheck(self, major, 2, "string") 483 | TryToLoadStandalone(major) 484 | 485 | local data = self.libs[major] 486 | if not data then 487 | _G.error(string.format("Cannot find a library instance of %s.", major), 2) 488 | return 489 | end 490 | if minor then 491 | if type(minor) == "string" then 492 | local m = svnRevisionToNumber(minor) 493 | if m then 494 | minor = m 495 | else 496 | _G.error(string.format("Bad argument #3 to `GetInstance'. Must be a number or SVN revision string. %q is not appropriate", minor), 2) 497 | end 498 | end 499 | argCheck(self, minor, 2, "number") 500 | if data.minor ~= minor then 501 | _G.error(string.format("Cannot find a library instance of %s, minor version %d.", major, minor), 2) 502 | return 503 | end 504 | end 505 | return data.instance 506 | end 507 | 508 | -- Syntax sugar. AceLibrary("FooBar-1.0") 509 | AceLibrary_mt.__call = AceLibrary.GetInstance 510 | 511 | local donothing 512 | 513 | local AceEvent 514 | 515 | -- @method Register 516 | -- @brief Registers a new version of a given library. 517 | -- @param newInstance the library to register 518 | -- @param major the major version of the library 519 | -- @param minor the minor version of the library 520 | -- @param activateFunc (optional) A function to be called when the library is 521 | -- fully activated. Takes the arguments 522 | -- (newInstance [, oldInstance, oldDeactivateFunc]). If 523 | -- oldInstance is given, you should probably call 524 | -- oldDeactivateFunc(oldInstance). 525 | -- @param deactivateFunc (optional) A function to be called by a newer library's 526 | -- activateFunc. 527 | -- @param externalFunc (optional) A function to be called whenever a new 528 | -- library is registered. 529 | function AceLibrary:Register(newInstance, major, minor, activateFunc, deactivateFunc, externalFunc) 530 | argCheck(self, newInstance, 2, "table") 531 | argCheck(self, major, 3, "string") 532 | if type(minor) == "string" then 533 | local m = svnRevisionToNumber(minor) 534 | if m then 535 | minor = m 536 | else 537 | _G.error(string.format("Bad argument #4 to `Register'. Must be a number or SVN revision string. %q is not appropriate", minor), 2) 538 | end 539 | end 540 | argCheck(self, minor, 4, "number") 541 | if math.floor(minor) ~= minor or minor < 0 then 542 | error(self, "Bad argument #4 to `Register' (integer >= 0 expected, got %s)", minor) 543 | end 544 | argCheck(self, activateFunc, 5, "function", "nil") 545 | argCheck(self, deactivateFunc, 6, "function", "nil") 546 | argCheck(self, externalFunc, 7, "function", "nil") 547 | if not deactivateFunc then 548 | if not donothing then 549 | donothing = function() end 550 | end 551 | deactivateFunc = donothing 552 | end 553 | local data = self.libs[major] 554 | if not data then 555 | -- This is new 556 | local instance = copyTable(newInstance) 557 | crawlReplace(instance, instance, newInstance) 558 | destroyTable(newInstance) 559 | if AceLibrary == newInstance then 560 | self = instance 561 | AceLibrary = instance 562 | end 563 | self.libs[major] = { 564 | instance = instance, 565 | minor = minor, 566 | deactivateFunc = deactivateFunc, 567 | externalFunc = externalFunc, 568 | } 569 | rawset(instance, 'GetLibraryVersion', function(self) 570 | return major, minor 571 | end) 572 | if not rawget(instance, 'error') then 573 | rawset(instance, 'error', error) 574 | end 575 | if not rawget(instance, 'assert') then 576 | rawset(instance, 'assert', assert) 577 | end 578 | if not rawget(instance, 'argCheck') then 579 | rawset(instance, 'argCheck', argCheck) 580 | end 581 | if not rawget(instance, 'pcall') then 582 | rawset(instance, 'pcall', pcall) 583 | end 584 | addToPositions(instance, major) 585 | if activateFunc then 586 | safecall(activateFunc, instance, nil, nil) -- no old version, so explicit nil 587 | end 588 | 589 | if externalFunc then 590 | for k,data in pairs(self.libs) do 591 | if k ~= major then 592 | safecall(externalFunc, instance, k, data.instance) 593 | end 594 | end 595 | end 596 | 597 | for k,data in pairs(self.libs) do 598 | if k ~= major and data.externalFunc then 599 | safecall(data.externalFunc, data.instance, major, instance) 600 | end 601 | end 602 | if major == "AceEvent-2.0" then 603 | AceEvent = instance 604 | end 605 | if AceEvent then 606 | AceEvent.TriggerEvent(self, "AceLibrary_Register", major, instance) 607 | end 608 | 609 | return instance 610 | end 611 | local instance = data.instance 612 | if minor <= data.minor then 613 | -- This one is already obsolete, raise an error. 614 | _G.error(string.format("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", major, data.minor, minor, major, minor), 2) 615 | return 616 | end 617 | -- This is an update 618 | local oldInstance = new() 619 | 620 | addToPositions(newInstance, major) 621 | local isAceLibrary = (AceLibrary == newInstance) 622 | local old_error, old_assert, old_argCheck, old_pcall 623 | if isAceLibrary then 624 | self = instance 625 | AceLibrary = instance 626 | 627 | old_error = instance.error 628 | old_assert = instance.assert 629 | old_argCheck = instance.argCheck 630 | old_pcall = instance.pcall 631 | 632 | self.error = error 633 | self.assert = assert 634 | self.argCheck = argCheck 635 | self.pcall = pcall 636 | end 637 | deepTransfer(instance, newInstance, oldInstance, major) 638 | crawlReplace(instance, instance, newInstance) 639 | local oldDeactivateFunc = data.deactivateFunc 640 | data.minor = minor 641 | data.deactivateFunc = deactivateFunc 642 | data.externalFunc = externalFunc 643 | rawset(instance, 'GetLibraryVersion', function(self) 644 | return major, minor 645 | end) 646 | if not rawget(instance, 'error') then 647 | rawset(instance, 'error', error) 648 | end 649 | if not rawget(instance, 'assert') then 650 | rawset(instance, 'assert', assert) 651 | end 652 | if not rawget(instance, 'argCheck') then 653 | rawset(instance, 'argCheck', argCheck) 654 | end 655 | if not rawget(instance, 'pcall') then 656 | rawset(instance, 'pcall', pcall) 657 | end 658 | if isAceLibrary then 659 | for _,v in pairs(self.libs) do 660 | local i = type(v) == "table" and v.instance 661 | if type(i) == "table" then 662 | if not rawget(i, 'error') or i.error == old_error then 663 | rawset(i, 'error', error) 664 | end 665 | if not rawget(i, 'assert') or i.assert == old_assert then 666 | rawset(i, 'assert', assert) 667 | end 668 | if not rawget(i, 'argCheck') or i.argCheck == old_argCheck then 669 | rawset(i, 'argCheck', argCheck) 670 | end 671 | if not rawget(i, 'pcall') or i.pcall == old_pcall then 672 | rawset(i, 'pcall', pcall) 673 | end 674 | end 675 | end 676 | end 677 | if activateFunc then 678 | safecall(activateFunc, instance, oldInstance, oldDeactivateFunc) 679 | else 680 | safecall(oldDeactivateFunc, oldInstance) 681 | end 682 | del(oldInstance) 683 | 684 | if externalFunc then 685 | for k,data in pairs(self.libs) do 686 | if k ~= major then 687 | safecall(externalFunc, instance, k, data.instance) 688 | end 689 | end 690 | end 691 | 692 | return instance 693 | end 694 | 695 | local iter 696 | function AceLibrary:IterateLibraries() 697 | if not iter then 698 | local function iter(t, k) 699 | k = next(t, k) 700 | if not k then 701 | return nil 702 | else 703 | return k, t[k].instance 704 | end 705 | end 706 | end 707 | return iter, self.libs, nil 708 | end 709 | 710 | -- @function Activate 711 | -- @brief The activateFunc for AceLibrary itself. Called when 712 | -- AceLibrary properly registers. 713 | -- @param self Reference to AceLibrary 714 | -- @param oldLib (optional) Reference to an old version of AceLibrary 715 | -- @param oldDeactivate (optional) Function to deactivate the old lib 716 | local function activate(self, oldLib, oldDeactivate) 717 | if not self.libs then 718 | if oldLib then 719 | self.libs = oldLib.libs 720 | self.scannedlibs = oldLib.scannedlibs 721 | end 722 | if not self.libs then 723 | self.libs = {} 724 | end 725 | if not self.scannedlibs then 726 | self.scannedlibs = {} 727 | end 728 | end 729 | if not self.positions then 730 | if oldLib then 731 | self.positions = oldLib.positions 732 | end 733 | if not self.positions then 734 | self.positions = setmetatable({}, { __mode = "k" }) 735 | end 736 | end 737 | 738 | -- Expose the library in the global environment 739 | _G[ACELIBRARY_MAJOR] = self 740 | 741 | if oldDeactivate then 742 | oldDeactivate(oldLib) 743 | end 744 | end 745 | 746 | if not previous then 747 | previous = AceLibrary 748 | end 749 | if not previous.libs then 750 | previous.libs = {} 751 | end 752 | AceLibrary.libs = previous.libs 753 | if not previous.positions then 754 | previous.positions = setmetatable({}, { __mode = "k" }) 755 | end 756 | AceLibrary.positions = previous.positions 757 | AceLibrary:Register(AceLibrary, ACELIBRARY_MAJOR, ACELIBRARY_MINOR, activate) 758 | -------------------------------------------------------------------------------- /libs/AceLocale-2.2/AceLocale-2.2.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Name: AceLocale-2.2 3 | Revision: $Rev: 17638 $ 4 | Developed by: The Ace Development Team (http://www.wowace.com/index.php/The_Ace_Development_Team) 5 | Inspired By: Ace 1.x by Turan (turan@gryphon.com) 6 | Website: http://www.wowace.com/ 7 | Documentation: http://www.wowace.com/index.php/AceLocale-2.2 8 | SVN: http://svn.wowace.com/root/trunk/Ace2/AceLocale-2.2 9 | Description: Localization library for addons to use to handle proper 10 | localization and internationalization. 11 | Dependencies: AceLibrary 12 | ]] 13 | 14 | local MAJOR_VERSION = "AceLocale-2.2" 15 | local MINOR_VERSION = "$Revision: 17639 $" 16 | 17 | if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary.") end 18 | if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end 19 | 20 | if loadstring("return function(...) return ... end") and AceLibrary:HasInstance(MAJOR_VERSION) then return end -- lua51 check 21 | local AceLocale = {} 22 | 23 | local DEFAULT_LOCALE = "enUS" 24 | local _G = getfenv(0) 25 | 26 | local BASE_TRANSLATIONS, DEBUGGING, TRANSLATIONS, BASE_LOCALE, TRANSLATION_TABLES, REVERSE_TRANSLATIONS, STRICTNESS, DYNAMIC_LOCALES, CURRENT_LOCALE, NAME 27 | 28 | local rawget = rawget 29 | local rawset = rawset 30 | local type = type 31 | 32 | local newRegistries = {} 33 | local scheduleClear 34 | 35 | local lastSelf 36 | local __index = function(self, key) 37 | lastSelf = self 38 | local value = (rawget(self, TRANSLATIONS) or AceLocale.prototype)[key] 39 | rawset(self, key, value) 40 | return value 41 | end 42 | 43 | local __newindex = function(self, k, v) 44 | if type(v) ~= "function" and type(k) ~= "table" then 45 | AceLocale.error(self, "Cannot change the values of an AceLocale instance.") 46 | end 47 | rawset(self, k, v) 48 | end 49 | 50 | local __tostring = function(self) 51 | if type(rawget(self, 'GetLibraryVersion')) == "function" then 52 | return self:GetLibraryVersion() 53 | else 54 | return "AceLocale(" .. self[NAME] .. ")" 55 | end 56 | end 57 | 58 | local function clearCache(self) 59 | if not rawget(self, BASE_TRANSLATIONS) then 60 | return 61 | end 62 | 63 | local cache = self[BASE_TRANSLATIONS] 64 | rawset(self, REVERSE_TRANSLATIONS, nil) 65 | 66 | for k in pairs(self) do 67 | if rawget(cache, k) ~= nil then 68 | self[k] = nil 69 | end 70 | end 71 | rawset(self, 'tmp', true) 72 | self.tmp = nil 73 | end 74 | 75 | local function refixInstance(instance) 76 | if getmetatable(instance) then 77 | setmetatable(instance, nil) 78 | end 79 | local translations = instance[TRANSLATIONS] 80 | if translations then 81 | if getmetatable(translations) then 82 | setmetatable(translations, nil) 83 | end 84 | local baseTranslations = instance[BASE_TRANSLATIONS] 85 | if getmetatable(baseTranslations) then 86 | setmetatable(baseTranslations, nil) 87 | end 88 | if translations == baseTranslations or instance[STRICTNESS] then 89 | setmetatable(instance, { 90 | __index = __index, 91 | __newindex = __newindex, 92 | __tostring = __tostring 93 | }) 94 | 95 | setmetatable(translations, { 96 | __index = AceLocale.prototype 97 | }) 98 | else 99 | setmetatable(instance, { 100 | __index = __index, 101 | __newindex = __newindex, 102 | __tostring = __tostring 103 | }) 104 | 105 | setmetatable(translations, { 106 | __index = baseTranslations, 107 | }) 108 | 109 | setmetatable(baseTranslations, { 110 | __index = AceLocale.prototype, 111 | }) 112 | end 113 | else 114 | setmetatable(instance, { 115 | __index = __index, 116 | __newindex = __newindex, 117 | __tostring = __tostring, 118 | }) 119 | end 120 | clearCache(instance) 121 | newRegistries[instance] = true 122 | scheduleClear() 123 | return instance 124 | end 125 | 126 | function AceLocale:new(name) 127 | self:argCheck(name, 2, "string") 128 | 129 | if self.registry[name] and type(rawget(self.registry[name], 'GetLibraryVersion')) ~= "function" then 130 | return self.registry[name] 131 | end 132 | 133 | AceLocale.registry[name] = refixInstance({ 134 | [STRICTNESS] = false, 135 | [NAME] = name, 136 | }) 137 | newRegistries[AceLocale.registry[name]] = true 138 | return AceLocale.registry[name] 139 | end 140 | 141 | AceLocale.prototype = { class = AceLocale } 142 | 143 | function AceLocale.prototype:EnableDebugging() 144 | if rawget(self, BASE_TRANSLATIONS) then 145 | AceLocale.error(self, "Cannot enable debugging after a translation has been registered.") 146 | end 147 | rawset(self, DEBUGGING, true) 148 | end 149 | 150 | function AceLocale.prototype:EnableDynamicLocales(override) 151 | AceLocale.argCheck(self, override, 2, "boolean", "nil") 152 | if not override and rawget(self, BASE_TRANSLATIONS) then 153 | AceLocale.error(self, "Cannot enable dynamic locales after a translation has been registered.") 154 | end 155 | if not rawget(self, DYNAMIC_LOCALES) then 156 | rawset(self, DYNAMIC_LOCALES, true) 157 | if rawget(self, BASE_LOCALE) then 158 | if not rawget(self, TRANSLATION_TABLES) then 159 | rawset(self, TRANSLATION_TABLES, {}) 160 | end 161 | self[TRANSLATION_TABLES][self[BASE_LOCALE]] = self[BASE_TRANSLATIONS] 162 | self[TRANSLATION_TABLES][self[CURRENT_LOCALE]] = self[TRANSLATIONS] 163 | end 164 | end 165 | end 166 | 167 | function AceLocale.prototype:RegisterTranslations(locale, func) 168 | AceLocale.argCheck(self, locale, 2, "string") 169 | AceLocale.argCheck(self, func, 3, "function") 170 | 171 | if locale == rawget(self, BASE_LOCALE) then 172 | AceLocale.error(self, "Cannot provide the same locale more than once. %q provided twice.", locale) 173 | end 174 | 175 | if rawget(self, BASE_TRANSLATIONS) and GetLocale() ~= locale then 176 | if rawget(self, DEBUGGING) or rawget(self, DYNAMIC_LOCALES) then 177 | if not rawget(self, TRANSLATION_TABLES) then 178 | rawset(self, TRANSLATION_TABLES, {}) 179 | end 180 | if self[TRANSLATION_TABLES][locale] then 181 | AceLocale.error(self, "Cannot provide the same locale more than once. %q provided twice.", locale) 182 | end 183 | local t = func() 184 | func = nil 185 | if type(t) ~= "table" then 186 | AceLocale.error(self, "Bad argument #3 to `RegisterTranslations'. function did not return a table. (expected table, got %s)", type(t)) 187 | end 188 | self[TRANSLATION_TABLES][locale] = t 189 | t = nil 190 | end 191 | func = nil 192 | return 193 | end 194 | local t = func() 195 | func = nil 196 | if type(t) ~= "table" then 197 | AceLocale.error(self, "Bad argument #3 to `RegisterTranslations'. function did not return a table. (expected table, got %s)", type(t)) 198 | end 199 | 200 | rawset(self, TRANSLATIONS, t) 201 | if not rawget(self, BASE_TRANSLATIONS) then 202 | rawset(self, BASE_TRANSLATIONS, t) 203 | rawset(self, BASE_LOCALE, locale) 204 | for key,value in pairs(t) do 205 | if value == true then 206 | t[key] = key 207 | end 208 | end 209 | else 210 | for key, value in pairs(self[TRANSLATIONS]) do 211 | if not rawget(self[BASE_TRANSLATIONS], key) then 212 | AceLocale.error(self, "Improper translation exists. %q is likely misspelled for locale %s.", key, locale) 213 | end 214 | if value == true then 215 | AceLocale.error(self, "Can only accept true as a value on the base locale. %q is the base locale, %q is not.", rawget(self, BASE_LOCALE), locale) 216 | end 217 | end 218 | end 219 | rawset(self, CURRENT_LOCALE, locale) 220 | refixInstance(self) 221 | if rawget(self, DEBUGGING) or rawget(self, DYNAMIC_LOCALES) then 222 | if not rawget(self, TRANSLATION_TABLES) then 223 | rawset(self, TRANSLATION_TABLES, {}) 224 | end 225 | self[TRANSLATION_TABLES][locale] = t 226 | end 227 | t = nil 228 | end 229 | 230 | function AceLocale.prototype:SetLocale(locale) 231 | AceLocale.argCheck(self, locale, 2, "string", "boolean") 232 | if not rawget(self, DYNAMIC_LOCALES) then 233 | AceLocale.error(self, "Cannot call `SetLocale' without first calling `EnableDynamicLocales'.") 234 | end 235 | if not rawget(self, TRANSLATION_TABLES) then 236 | AceLocale.error(self, "Cannot call `SetLocale' without first calling `RegisterTranslations'.") 237 | end 238 | if locale == true then 239 | locale = GetLocale() 240 | if not self[TRANSLATION_TABLES][locale] then 241 | locale = self[BASE_LOCALE] 242 | end 243 | end 244 | 245 | if self[CURRENT_LOCALE] == locale then 246 | return 247 | end 248 | 249 | if not self[TRANSLATION_TABLES][locale] then 250 | AceLocale.error(self, "Locale %q not registered.", locale) 251 | end 252 | 253 | self[TRANSLATIONS] = self[TRANSLATION_TABLES][locale] 254 | self[CURRENT_LOCALE] = locale 255 | refixInstance(self) 256 | end 257 | 258 | function AceLocale.prototype:GetLocale() 259 | if not rawget(self, TRANSLATION_TABLES) then 260 | AceLocale.error(self, "Cannot call `GetLocale' without first calling `RegisterTranslations'.") 261 | end 262 | return self[CURRENT_LOCALE] 263 | end 264 | 265 | local function iter(t, position) 266 | return (next(t, position)) 267 | end 268 | 269 | function AceLocale.prototype:IterateAvailableLocales() 270 | if not rawget(self, DYNAMIC_LOCALES) then 271 | AceLocale.error(self, "Cannot call `IterateAvailableLocales' without first calling `EnableDynamicLocales'.") 272 | end 273 | if not rawget(self, TRANSLATION_TABLES) then 274 | AceLocale.error(self, "Cannot call `IterateAvailableLocales' without first calling `RegisterTranslations'.") 275 | end 276 | return iter, self[TRANSLATION_TABLES], nil 277 | end 278 | 279 | function AceLocale.prototype:HasLocale(locale) 280 | if not rawget(self, DYNAMIC_LOCALES) then 281 | AceLocale.error(self, "Cannot call `HasLocale' without first calling `EnableDynamicLocales'.") 282 | end 283 | AceLocale.argCheck(self, locale, 2, "string") 284 | return rawget(self, TRANSLATION_TABLES) and self[TRANSLATION_TABLES][locale] ~= nil 285 | end 286 | 287 | function AceLocale.prototype:SetStrictness(strict) 288 | AceLocale.argCheck(self, strict, 2, "boolean") 289 | local mt = getmetatable(self) 290 | if not mt then 291 | AceLocale.error(self, "Cannot call `SetStrictness' without a metatable.") 292 | end 293 | if not rawget(self, TRANSLATIONS) then 294 | AceLocale.error(self, "No translations registered.") 295 | end 296 | rawset(self, STRICTNESS, strict) 297 | refixInstance(self) 298 | end 299 | 300 | local function initReverse(self) 301 | rawset(self, REVERSE_TRANSLATIONS, {}) 302 | local alpha = self[TRANSLATIONS] 303 | local bravo = self[REVERSE_TRANSLATIONS] 304 | for base, localized in pairs(alpha) do 305 | bravo[localized] = base 306 | end 307 | end 308 | 309 | function AceLocale.prototype:GetTranslation(text) 310 | AceLocale.argCheck(self, text, 1, "string", "number") 311 | if not rawget(self, TRANSLATIONS) then 312 | AceLocale.error(self, "No translations registered") 313 | end 314 | return self[text] 315 | end 316 | 317 | function AceLocale.prototype:GetStrictTranslation(text) 318 | AceLocale.argCheck(self, text, 1, "string", "number") 319 | local x = rawget(self, TRANSLATIONS) 320 | if not x then 321 | AceLocale.error(self, "No translations registered") 322 | end 323 | local value = rawget(x, text) 324 | if value == nil then 325 | AceLocale.error(self, "Translation %q does not exist for locale %s", text, self[CURRENT_LOCALE]) 326 | end 327 | return value 328 | end 329 | 330 | function AceLocale.prototype:GetReverseTranslation(text) 331 | local x = rawget(self, REVERSE_TRANSLATIONS) 332 | if not x then 333 | if not rawget(self, TRANSLATIONS) then 334 | AceLocale.error(self, "No translations registered") 335 | end 336 | initReverse(self) 337 | x = self[REVERSE_TRANSLATIONS] 338 | end 339 | local translation = x[text] 340 | if not translation then 341 | AceLocale.error(self, "Reverse translation for %q does not exist", text) 342 | end 343 | return translation 344 | end 345 | 346 | function AceLocale.prototype:GetIterator() 347 | local x = rawget(self, TRANSLATIONS) 348 | if not x then 349 | AceLocale.error(self, "No translations registered") 350 | end 351 | return next, x, nil 352 | end 353 | 354 | function AceLocale.prototype:GetReverseIterator() 355 | local x = rawget(self, REVERSE_TRANSLATIONS) 356 | if not x then 357 | if not rawget(self, TRANSLATIONS) then 358 | AceLocale.error(self, "No translations registered") 359 | end 360 | initReverse(self) 361 | x = self[REVERSE_TRANSLATIONS] 362 | end 363 | return next, x, nil 364 | end 365 | 366 | function AceLocale.prototype:HasTranslation(text) 367 | AceLocale.argCheck(self, text, 1, "string", "number") 368 | local x = rawget(self, TRANSLATIONS) 369 | if not x then 370 | AceLocale.error(self, "No translations registered") 371 | end 372 | return rawget(x, text) and true 373 | end 374 | 375 | function AceLocale.prototype:HasReverseTranslation(text) 376 | local x = rawget(self, REVERSE_TRANSLATIONS) 377 | if not x then 378 | if not rawget(self, TRANSLATIONS) then 379 | AceLocale.error(self, "No translations registered") 380 | end 381 | initReverse(self) 382 | x = self[REVERSE_TRANSLATIONS] 383 | end 384 | return x[text] and true 385 | end 386 | 387 | function AceLocale.prototype:Debug() 388 | if not rawget(self, DEBUGGING) then 389 | return 390 | end 391 | local words = {} 392 | local locales = {"enUS", "ruRU", "deDE", "frFR", "koKR", "zhCN", "zhTW", "esES"} 393 | local localizations = {} 394 | DEFAULT_CHAT_FRAME:AddMessage("--- AceLocale Debug ---") 395 | for _,locale in ipairs(locales) do 396 | if not self[TRANSLATION_TABLES][locale] then 397 | DEFAULT_CHAT_FRAME:AddMessage(string.format("Locale %q not found", locale)) 398 | else 399 | localizations[locale] = self[TRANSLATION_TABLES][locale] 400 | end 401 | end 402 | local localeDebug = {} 403 | for locale, localization in pairs(localizations) do 404 | localeDebug[locale] = {} 405 | for word in pairs(localization) do 406 | if type(localization[word]) == "table" then 407 | if type(words[word]) ~= "table" then 408 | words[word] = {} 409 | end 410 | for bit in pairs(localization[word]) do 411 | if type(localization[word][bit]) == "string" then 412 | words[word][bit] = true 413 | end 414 | end 415 | elseif type(localization[word]) == "string" then 416 | words[word] = true 417 | end 418 | end 419 | end 420 | for word in pairs(words) do 421 | if type(words[word]) == "table" then 422 | for bit in pairs(words[word]) do 423 | for locale, localization in pairs(localizations) do 424 | if not rawget(localization, word) or not localization[word][bit] then 425 | localeDebug[locale][word .. "::" .. bit] = true 426 | end 427 | end 428 | end 429 | else 430 | for locale, localization in pairs(localizations) do 431 | if not rawget(localization, word) then 432 | localeDebug[locale][word] = true 433 | end 434 | end 435 | end 436 | end 437 | for locale, t in pairs(localeDebug) do 438 | if not next(t) then 439 | DEFAULT_CHAT_FRAME:AddMessage(string.format("Locale %q complete", locale)) 440 | else 441 | DEFAULT_CHAT_FRAME:AddMessage(string.format("Locale %q missing:", locale)) 442 | for word in pairs(t) do 443 | DEFAULT_CHAT_FRAME:AddMessage(string.format(" %q", word)) 444 | end 445 | end 446 | end 447 | DEFAULT_CHAT_FRAME:AddMessage("--- End AceLocale Debug ---") 448 | end 449 | 450 | setmetatable(AceLocale.prototype, { 451 | __index = function(self, k) 452 | if type(k) ~= "table" and k ~= 0 and k ~= "GetLibraryVersion" and k ~= "error" and k ~= "assert" and k ~= "argCheck" and k ~= "pcall" then -- HACK: remove "GetLibraryVersion" and such later. 453 | AceLocale.error(lastSelf or self, "Translation %q does not exist.", k) 454 | end 455 | return nil 456 | end 457 | }) 458 | 459 | local function activate(self, oldLib, oldDeactivate) 460 | AceLocale = self 461 | 462 | self.frame = oldLib and oldLib.frame or CreateFrame("Frame") 463 | self.registry = oldLib and oldLib.registry or {} 464 | self.BASE_TRANSLATIONS = oldLib and oldLib.BASE_TRANSLATIONS or {} 465 | self.DEBUGGING = oldLib and oldLib.DEBUGGING or {} 466 | self.TRANSLATIONS = oldLib and oldLib.TRANSLATIONS or {} 467 | self.BASE_LOCALE = oldLib and oldLib.BASE_LOCALE or {} 468 | self.TRANSLATION_TABLES = oldLib and oldLib.TRANSLATION_TABLES or {} 469 | self.REVERSE_TRANSLATIONS = oldLib and oldLib.REVERSE_TRANSLATIONS or {} 470 | self.STRICTNESS = oldLib and oldLib.STRICTNESS or {} 471 | self.NAME = oldLib and oldLib.NAME or {} 472 | self.DYNAMIC_LOCALES = oldLib and oldLib.DYNAMIC_LOCALES or {} 473 | self.CURRENT_LOCALE = oldLib and oldLib.CURRENT_LOCALE or {} 474 | 475 | BASE_TRANSLATIONS = self.BASE_TRANSLATIONS 476 | DEBUGGING = self.DEBUGGING 477 | TRANSLATIONS = self.TRANSLATIONS 478 | BASE_LOCALE = self.BASE_LOCALE 479 | TRANSLATION_TABLES = self.TRANSLATION_TABLES 480 | REVERSE_TRANSLATIONS = self.REVERSE_TRANSLATIONS 481 | STRICTNESS = self.STRICTNESS 482 | NAME = self.NAME 483 | DYNAMIC_LOCALES = self.DYNAMIC_LOCALES 484 | CURRENT_LOCALE = self.CURRENT_LOCALE 485 | 486 | 487 | local GetTime = GetTime 488 | local timeUntilClear = GetTime() + 5 489 | scheduleClear = function() 490 | if next(newRegistries) then 491 | self.frame:Show() 492 | timeUntilClear = GetTime() + 5 493 | end 494 | end 495 | 496 | if not self.registry then 497 | self.registry = {} 498 | else 499 | for name, instance in pairs(self.registry) do 500 | local name = name 501 | local mt = getmetatable(instance) 502 | setmetatable(instance, nil) 503 | instance[NAME] = name 504 | local strict 505 | if instance[STRICTNESS] ~= nil then 506 | strict = instance[STRICTNESS] 507 | elseif instance[TRANSLATIONS] ~= instance[BASE_TRANSLATIONS] then 508 | if getmetatable(instance[TRANSLATIONS]).__index == oldLib.prototype then 509 | strict = true 510 | end 511 | end 512 | instance[STRICTNESS] = strict and true or false 513 | refixInstance(instance) 514 | end 515 | end 516 | 517 | self.frame:SetScript("OnEvent", scheduleClear) 518 | self.frame:SetScript("OnUpdate", function() -- (this, elapsed) 519 | if timeUntilClear - GetTime() <= 0 then 520 | self.frame:Hide() 521 | for k in pairs(newRegistries) do 522 | clearCache(k) 523 | newRegistries[k] = nil 524 | k = nil 525 | end 526 | end 527 | end) 528 | self.frame:UnregisterAllEvents() 529 | self.frame:RegisterEvent("ADDON_LOADED") 530 | self.frame:RegisterEvent("PLAYER_ENTERING_WORLD") 531 | self.frame:Show() 532 | 533 | if oldDeactivate then 534 | oldDeactivate(oldLib) 535 | end 536 | end 537 | 538 | AceLibrary:Register(AceLocale, MAJOR_VERSION, MINOR_VERSION, activate) 539 | -------------------------------------------------------------------------------- /libs/AceModuleCore-2.0/AceModuleCore-2.0.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Name: AceModuleCore-2.0 3 | Revision: $Rev: 17998 $ 4 | Developed by: The Ace Development Team (http://www.wowace.com/index.php/The_Ace_Development_Team) 5 | Inspired By: Ace 1.x by Turan (turan@gryphon.com) 6 | Website: http://www.wowace.com/ 7 | Documentation: http://www.wowace.com/index.php/AceModuleCore-2.0 8 | SVN: http://svn.wowace.com/root/trunk/Ace2/AceModuleCore-2.0 9 | Description: Mixin to provide a module system so that modules or plugins can 10 | use an addon as its core. 11 | Dependencies: AceLibrary, AceOO-2.0, AceAddon-2.0, AceEvent-2.0 (optional), Compost-2.0 (optional) 12 | ]] 13 | 14 | local MAJOR_VERSION = "AceModuleCore-2.0" 15 | local MINOR_VERSION = "$Revision: 17998 $" 16 | 17 | if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary") end 18 | if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end 19 | 20 | if loadstring("return function(...) return ... end") and AceLibrary:HasInstance(MAJOR_VERSION) then return end -- lua51 check 21 | if not AceLibrary:HasInstance("AceOO-2.0") then error(MAJOR_VERSION .. " requires AceOO-2.0") end 22 | 23 | local function safecall(func,a,b,c,d,e,f,g) 24 | local success, err = pcall(func,a,b,c,d,e,f,g) 25 | if not success then geterrorhandler()(err) end 26 | end 27 | 28 | local table_setn 29 | do 30 | local version = GetBuildInfo() 31 | if string.find(version, "^2%.") then 32 | -- 2.0.0 33 | table_setn = function() end 34 | else 35 | table_setn = table.setn 36 | end 37 | end 38 | 39 | local new, del 40 | do 41 | local list = setmetatable({}, {__mode = 'k'}) 42 | function new() 43 | local t = next(list) 44 | if t then 45 | list[t] = nil 46 | return t 47 | else 48 | return {} 49 | end 50 | end 51 | function del(t) 52 | for k in pairs(t) do 53 | t[k] = nil 54 | end 55 | table_setn(t, 0) 56 | list[t] = true 57 | return nil 58 | end 59 | end 60 | 61 | local AceOO = AceLibrary:GetInstance("AceOO-2.0") 62 | local AceModuleCore = AceOO.Mixin { 63 | "NewModule", 64 | "HasModule", 65 | "GetModule", 66 | "IsModule", 67 | "IterateModules", 68 | "SetModuleMixins", 69 | "SetModuleClass", 70 | "IsModuleActive", 71 | "ToggleModuleActive" 72 | } 73 | local AceEvent 74 | 75 | local Compost = AceLibrary:HasInstance("Compost-2.0") and AceLibrary("Compost-2.0") 76 | 77 | local function getlibrary(lib) 78 | if type(lib) == "string" then 79 | return AceLibrary(lib) 80 | else 81 | return lib 82 | end 83 | end 84 | 85 | local tmp 86 | function AceModuleCore:NewModule(name, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) 87 | if not self.modules then 88 | AceModuleCore:error("CreatePrototype() must be called before attempting to create a new module.", 2) 89 | end 90 | AceModuleCore:argCheck(name, 2, "string") 91 | if string.len(name) == 0 then 92 | AceModuleCore:error("Bad argument #2 to `NewModule`, string must not be empty") 93 | end 94 | if self.modules[name] then 95 | AceModuleCore:error("The module %q has already been registered", name) 96 | end 97 | 98 | if not tmp then 99 | tmp = {} 100 | end 101 | if a1 then table.insert(tmp, a1) 102 | if a2 then table.insert(tmp, a2) 103 | if a3 then table.insert(tmp, a3) 104 | if a4 then table.insert(tmp, a4) 105 | if a5 then table.insert(tmp, a5) 106 | if a6 then table.insert(tmp, a6) 107 | if a7 then table.insert(tmp, a7) 108 | if a8 then table.insert(tmp, a8) 109 | if a9 then table.insert(tmp, a9) 110 | if a10 then table.insert(tmp, a10) 111 | if a11 then table.insert(tmp, a11) 112 | if a12 then table.insert(tmp, a12) 113 | if a13 then table.insert(tmp, a13) 114 | if a14 then table.insert(tmp, a14) 115 | if a15 then table.insert(tmp, a15) 116 | if a16 then table.insert(tmp, a16) 117 | if a17 then table.insert(tmp, a17) 118 | if a18 then table.insert(tmp, a18) 119 | if a19 then table.insert(tmp, a19) 120 | if a20 then table.insert(tmp, a20) 121 | end end end end end end end end end end end end end end end end end end end end 122 | for k,v in ipairs(tmp) do 123 | tmp[k] = getlibrary(v) 124 | end 125 | 126 | if self.moduleMixins then 127 | for _,mixin in ipairs(self.moduleMixins) do 128 | local exists = false 129 | for _,v in ipairs(tmp) do 130 | if mixin == v then 131 | exists = true 132 | break 133 | end 134 | end 135 | if not exists then 136 | table.insert(tmp, mixin) 137 | end 138 | end 139 | end 140 | 141 | local module = AceOO.Classpool(self.moduleClass, unpack(tmp)):new(name) 142 | self.modules[name] = module 143 | module.name = name 144 | module.title = name 145 | 146 | AceModuleCore.totalModules[module] = self 147 | 148 | if AceEvent then 149 | AceEvent:TriggerEvent("Ace2_ModuleCreated", module) 150 | end 151 | 152 | for k in pairs(tmp) do 153 | tmp[k] = nil 154 | end 155 | table_setn(tmp, 0) 156 | return module 157 | end 158 | 159 | function AceModuleCore:HasModule(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) 160 | if a1 then if not self.modules[a1] then return false end 161 | if a2 then if not self.modules[a2] then return false end 162 | if a3 then if not self.modules[a3] then return false end 163 | if a4 then if not self.modules[a4] then return false end 164 | if a5 then if not self.modules[a5] then return false end 165 | if a6 then if not self.modules[a6] then return false end 166 | if a7 then if not self.modules[a7] then return false end 167 | if a8 then if not self.modules[a8] then return false end 168 | if a9 then if not self.modules[a9] then return false end 169 | if a10 then if not self.modules[a10] then return false end 170 | if a11 then if not self.modules[a11] then return false end 171 | if a12 then if not self.modules[a12] then return false end 172 | if a13 then if not self.modules[a13] then return false end 173 | if a14 then if not self.modules[a14] then return false end 174 | if a15 then if not self.modules[a15] then return false end 175 | if a16 then if not self.modules[a16] then return false end 176 | if a17 then if not self.modules[a17] then return false end 177 | if a18 then if not self.modules[a18] then return false end 178 | if a19 then if not self.modules[a19] then return false end 179 | if a20 then if not self.modules[a20] then return false end 180 | end end end end end end end end end end end end end end end end end end end end 181 | 182 | return true 183 | end 184 | 185 | function AceModuleCore:GetModule(name) 186 | if not self.modules then 187 | AceModuleCore:error("Error initializing class. Please report error.") 188 | end 189 | if not self.modules[name] then 190 | AceModuleCore:error("Cannot find module %q.", name) 191 | end 192 | return self.modules[name] 193 | end 194 | 195 | function AceModuleCore:IsModule(module) 196 | if self == AceModuleCore then 197 | return AceModuleCore.totalModules[module] 198 | else 199 | for k,v in pairs(self.modules) do 200 | if v == module then 201 | return true 202 | end 203 | end 204 | return false 205 | end 206 | end 207 | 208 | function AceModuleCore:IterateModules() 209 | local t = new() 210 | for k in pairs(self.modules) do 211 | table.insert(t, k) 212 | end 213 | table.sort(t) 214 | local i = 0 215 | return function() 216 | i = i + 1 217 | local x = t[i] 218 | if x then 219 | return x, self.modules[x] 220 | else 221 | t = del(t) 222 | return nil 223 | end 224 | end, nil, nil 225 | end 226 | 227 | function AceModuleCore:SetModuleMixins(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) 228 | if self.moduleMixins then 229 | AceModuleCore:error('Cannot call "SetModuleMixins" twice') 230 | elseif not self.modules then 231 | AceModuleCore:error("Error initializing class. Please report error.") 232 | elseif next(self.modules) then 233 | AceModuleCore:error('Cannot call "SetModuleMixins" after "NewModule" has been called.') 234 | end 235 | 236 | self.moduleMixins = Compost and Compost:Acquire(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) or {a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20} 237 | for k,v in ipairs(self.moduleMixins) do 238 | self.moduleMixins[k] = getlibrary(v) 239 | end 240 | end 241 | 242 | function AceModuleCore:SetModuleClass(class) 243 | class = getlibrary(class) 244 | AceModuleCore:assert(AceOO.inherits(class, AceOO.Class), "Bad argument #2 to `SetModuleClass' (Class expected)") 245 | if not self.modules then 246 | AceModuleCore:error("Error initializing class. Please report error.") 247 | end 248 | if self.customModuleClass then 249 | AceModuleCore:error("Cannot call `SetModuleClass' twice.") 250 | end 251 | self.customModuleClass = true 252 | self.moduleClass = class 253 | self.modulePrototype = class.prototype 254 | end 255 | 256 | function AceModuleCore:ToggleModuleActive(module, state) 257 | AceModuleCore:argCheck(module, 2, "table", "string") 258 | AceModuleCore:argCheck(state, 3, "nil", "boolean") 259 | 260 | if type(module) == "string" then 261 | if not self:HasModule(module) then 262 | AceModuleCore:error("Cannot find module %q", module) 263 | end 264 | module = self:GetModule(module) 265 | else 266 | if not self:IsModule(module) then 267 | AceModuleCore:error("%q is not a module", module) 268 | end 269 | end 270 | 271 | local disable 272 | if state == nil then 273 | disable = self:IsModuleActive(module) 274 | else 275 | disable = not state 276 | if disable ~= self:IsModuleActive(module) then 277 | return 278 | end 279 | end 280 | 281 | if type(module.ToggleActive) == "function" then 282 | return module:ToggleActive(not disable) 283 | elseif AceOO.inherits(self, "AceDB-2.0") then 284 | if not self.db or not self.db.raw then 285 | AceModuleCore:error("Cannot toggle a module until `RegisterDB' has been called and `ADDON_LOADED' has been fireed.") 286 | end 287 | if type(self.db.raw.disabledModules) ~= "table" then 288 | self.db.raw.disabledModules = Compost and Compost:Acquire() or {} 289 | end 290 | local _,profile = self:GetProfile() 291 | if type(self.db.raw.disabledModules[profile]) ~= "table" then 292 | self.db.raw.disabledModules[profile] = Compost and Compost:Acquire() or {} 293 | end 294 | if type(self.db.raw.disabledModules[profile][module.name]) ~= "table" then 295 | self.db.raw.disabledModules[profile][module.name] = disable or nil 296 | end 297 | if not disable then 298 | if not next(self.db.raw.disabledModules[profile]) then 299 | if Compost then 300 | Compost:Reclaim(self.db.raw.disabledModules[profile]) 301 | end 302 | self.db.raw.disabledModules[profile] = nil 303 | end 304 | if not next(self.db.raw.disabledModules) then 305 | if Compost then 306 | Compost:Reclaim(self.db.raw.disabledModules) 307 | end 308 | self.db.raw.disabledModules = nil 309 | end 310 | end 311 | else 312 | if type(self.disabledModules) ~= "table" then 313 | self.disabledModules = Compost and Compost:Acquire() or {} 314 | end 315 | self.disabledModules[module.name] = disable or nil 316 | end 317 | if AceOO.inherits(module, "AceAddon-2.0") then 318 | local AceAddon = AceLibrary("AceAddon-2.0") 319 | if not AceAddon.addonsStarted[module] then 320 | return 321 | end 322 | end 323 | if not disable then 324 | local current = module.class 325 | while true do 326 | if current == AceOO.Class then 327 | break 328 | end 329 | if current.mixins then 330 | for mixin in pairs(current.mixins) do 331 | if type(mixin.OnEmbedEnable) == "function" then 332 | safecall(mixin.OnEmbedEnable, mixin, module) 333 | end 334 | end 335 | end 336 | current = current.super 337 | end 338 | if type(module.OnEnable) == "function" then 339 | safecall(module.OnEnable, module) 340 | end 341 | if AceEvent then 342 | AceEvent:TriggerEvent("Ace2_AddonEnabled", module) 343 | end 344 | else 345 | local current = module.class 346 | while true do 347 | if current == AceOO.Class then 348 | break 349 | end 350 | if current.mixins then 351 | for mixin in pairs(current.mixins) do 352 | if type(mixin.OnEmbedDisable) == "function" then 353 | safecall(mixin.OnEmbedDisable, mixin, module) 354 | end 355 | end 356 | end 357 | current = current.super 358 | end 359 | if type(module.OnDisable) == "function" then 360 | safecall(module.OnDisable, module) 361 | end 362 | if AceEvent then 363 | AceEvent:TriggerEvent("Ace2_AddonDisabled", module) 364 | end 365 | end 366 | return not disable 367 | end 368 | 369 | function AceModuleCore:IsModuleActive(module) 370 | AceModuleCore:argCheck(module, 2, "table", "string") 371 | 372 | if AceModuleCore == self then 373 | self:argCheck(module, 2, "table") 374 | 375 | local core = AceModuleCore.totalModules[module] 376 | if not core then 377 | self:error("Bad argument #2 to `IsModuleActive'. Not a module") 378 | end 379 | return core:IsModuleActive(module) 380 | end 381 | 382 | if type(module) == "string" then 383 | if not self:HasModule(module) then 384 | AceModuleCore:error("Cannot find module %q", module) 385 | end 386 | module = self:GetModule(module) 387 | else 388 | if not self:IsModule(module) then 389 | AceModuleCore:error("%q is not a module", module) 390 | end 391 | end 392 | 393 | if type(module.IsActive) == "function" then 394 | return module:IsActive() 395 | elseif AceOO.inherits(self, "AceDB-2.0") then 396 | local _,profile = self:GetProfile() 397 | return not self.db or not self.db.raw or not self.db.raw.disabledModules or not self.db.raw.disabledModules[profile] or not self.db.raw.disabledModules[profile][module.name] 398 | else 399 | return not self.disabledModules or not self.disabledModules[module.name] 400 | end 401 | end 402 | 403 | function AceModuleCore:OnInstanceInit(target) 404 | if target.modules then 405 | AceModuleCore:error("OnInstanceInit cannot be called twice") 406 | end 407 | target.modules = Compost and Compost:Acquire() or {} 408 | 409 | target.moduleClass = AceOO.Class("AceAddon-2.0") 410 | target.modulePrototype = target.moduleClass.prototype 411 | end 412 | 413 | AceModuleCore.OnManualEmbed = AceModuleCore.OnInstanceInit 414 | 415 | function AceModuleCore.OnEmbedProfileDisable(AceModuleCore, self, newProfile) 416 | if not AceOO.inherits(self, "AceDB-2.0") then 417 | return 418 | end 419 | local _,currentProfile = self:GetProfile() 420 | for k, module in pairs(self.modules) do 421 | if type(module.IsActive) == "function" or type(module.ToggleActive) == "function" then 422 | -- continue 423 | else 424 | local currentActive = not self.db or not self.db.raw or not self.db.raw.disabledModules or not self.db.raw.disabledModules[currentProfile] or not self.db.raw.disabledModules[currentProfile][module.name] 425 | local newActive = not self.db or not self.db.raw or not self.db.raw.disabledModules or not self.db.raw.disabledModules[newProfile] or not self.db.raw.disabledModules[newProfile][module.name] 426 | if currentActive ~= newActive then 427 | self:ToggleModuleActive(module) 428 | if not self.db.raw.disabledModules then 429 | self.db.raw.disabledModules = {} 430 | end 431 | if not self.db.raw.disabledModules[currentProfile] then 432 | self.db.raw.disabledModules[currentProfile] = {} 433 | end 434 | self.db.raw.disabledModules[currentProfile][module.name] = not currentActive or nil 435 | end 436 | end 437 | end 438 | end 439 | 440 | local function activate(self, oldLib, oldDeactivate) 441 | AceModuleCore = self 442 | 443 | if oldLib then 444 | self.totalModules = oldLib.totalModules 445 | end 446 | if not self.totalModules then 447 | self.totalModules = {} 448 | end 449 | 450 | self.super.activate(self, oldLib, oldDeactivate) 451 | end 452 | 453 | local function external(self, major, instance) 454 | if major == "Compost-2.0" then 455 | Compost = instance 456 | elseif major == "AceEvent-2.0" then 457 | AceEvent = instance 458 | end 459 | end 460 | 461 | AceLibrary:Register(AceModuleCore, MAJOR_VERSION, MINOR_VERSION, activate, nil, external) 462 | AceModuleCore = AceLibrary(MAJOR_VERSION) 463 | -------------------------------------------------------------------------------- /libs/AceOO-2.0/AceOO-2.0.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Name: AceOO-2.0 3 | Revision: $Rev: 17638 $ 4 | Developed by: The Ace Development Team (http://www.wowace.com/index.php/The_Ace_Development_Team) 5 | Inspired By: Ace 1.x by Turan (turan@gryphon.com) 6 | Website: http://www.wowace.com/ 7 | Documentation: http://www.wowace.com/index.php/AceOO-2.0 8 | SVN: http://svn.wowace.com/root/trunk/Ace2/AceOO-2.0 9 | Description: Library to provide an object-orientation framework. 10 | Dependencies: AceLibrary 11 | ]] 12 | 13 | local MAJOR_VERSION = "AceOO-2.0" 14 | local MINOR_VERSION = "$Revision: 17639 $" 15 | 16 | -- This ensures the code is only executed if the libary doesn't already exist, or is a newer version 17 | if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary.") end 18 | if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end 19 | 20 | if loadstring("return function(...) return ... end") and AceLibrary:HasInstance(MAJOR_VERSION) then return end -- lua51 check 21 | local table_setn 22 | do 23 | local version = GetBuildInfo() 24 | if string.find(version, "^2%.") then 25 | -- 2.0.0 26 | table_setn = function() end 27 | else 28 | table_setn = table.setn 29 | end 30 | end 31 | 32 | local AceOO = { 33 | error = AceLibrary.error, 34 | argCheck = AceLibrary.argCheck 35 | } 36 | 37 | -- @function getuid 38 | -- @brief Obtain a unique string identifier for the object in question. 39 | -- @param t The object to obtain the uid for. 40 | -- @return The uid string. 41 | local function pad(cap) 42 | return string.rep('0', 8 - string.len(cap)) .. cap 43 | end 44 | local function getuid(t) 45 | local mt = getmetatable(t) 46 | setmetatable(t, nil) 47 | local str = tostring(t) 48 | setmetatable(t, mt) 49 | local _,_,cap = string.find(str, '[^:]*: 0x(.*)$') 50 | if cap then return pad(cap) end 51 | _,_,cap = string.find(str, '[^:]*: (.*)$') 52 | if cap then return pad(cap) end 53 | end 54 | 55 | local function getlibrary(o) 56 | if type(o) == "table" then 57 | return o 58 | elseif type(o) == "string" then 59 | if not AceLibrary:HasInstance(o) then 60 | AceOO:error("Library %q does not exist.", o) 61 | end 62 | return AceLibrary(o) 63 | end 64 | end 65 | 66 | -- @function Factory 67 | -- @brief Construct a factory for the creation of objects. 68 | -- @param obj The object whose init method will be called on the new factory 69 | -- object. 70 | -- @param newobj The object whose init method will be called on the new 71 | -- objects that the Factory creates, to initialize them. 72 | -- @param (a1..a20) Arguments which will be passed to obj.init() in addition 73 | -- to the Factory object. 74 | -- @return The new factory which creates a newobj when its new method is called, 75 | -- or when it is called directly (__call metamethod). 76 | local Factory 77 | do 78 | local function new(obj, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, 79 | a13, a14, a15, a16, a17, a18, a19, a20) 80 | local t = {} 81 | local uid = getuid(t) 82 | local l = getlibrary 83 | obj:init(t, l(a1), l(a2), l(a3), l(a4), l(a5), l(a6), l(a7), 84 | l(a8), l(a9), l(a10), l(a11), l(a12), l(a13), 85 | l(a14), l(a15), l(a16), l(a17), l(a18), l(a19), 86 | l(a20)) 87 | t.uid = uid 88 | return t 89 | end 90 | 91 | local function createnew(self, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, 92 | a11, a12, a13, a14, a15, a16, a17, a18, 93 | a19, a20) 94 | local o = self.prototype 95 | local l = getlibrary 96 | return new(o, l(a1), l(a2), l(a3), l(a4), l(a5), l(a6), l(a7), 97 | l(a8), l(a9), l(a10), l(a11), l(a12), l(a13), 98 | l(a14), l(a15), l(a16), l(a17), l(a18), l(a19), 99 | l(a20)) 100 | end 101 | 102 | function Factory(obj, newobj, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, 103 | a11, a12, a13, a14, a15, a16, a17, a18, 104 | a19, a20) 105 | local t = new(obj, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, 106 | a13, a14, a15, a16, a17, a18, a19, a20) 107 | t.prototype = newobj 108 | t.new = createnew 109 | getmetatable(t).__call = t.new 110 | return t 111 | end 112 | end 113 | 114 | 115 | local function objtostring(self) 116 | if self.ToString then 117 | return self:ToString() 118 | elseif self.GetLibraryVersion then 119 | return (self:GetLibraryVersion()) 120 | elseif self.super then 121 | local s = "Sub-" .. tostring(self.super) 122 | local first = true 123 | if self.interfaces then 124 | for interface in pairs(self.interfaces) do 125 | if first then 126 | s = s .. "(" .. tostring(interface) 127 | first = false 128 | else 129 | s = s .. ", " .. tostring(interface) 130 | end 131 | end 132 | end 133 | if self.mixins then 134 | for mixin in pairs(self.mixins) do 135 | if first then 136 | s = s .. tostring(mixin) 137 | first = false 138 | else 139 | s = s .. ", " .. tostring(mixin) 140 | end 141 | end 142 | end 143 | if first then 144 | if self.uid then 145 | return s .. ":" .. self.uid 146 | else 147 | return s 148 | end 149 | else 150 | return s .. ")" 151 | end 152 | else 153 | return self.uid and 'Subclass:' .. self.uid or 'Subclass' 154 | end 155 | end 156 | 157 | -- @table Object 158 | -- @brief Base of all objects, including Class. 159 | -- 160 | -- @method init 161 | -- @brief Initialize a new object. 162 | -- @param newobject The object to initialize 163 | -- @param class The class to make newobject inherit from 164 | local Object 165 | do 166 | Object = {} 167 | function Object:init(newobject, class) 168 | local parent = class or self 169 | if not rawget(newobject, 'uid') then 170 | newobject.uid = getuid(newobject) 171 | end 172 | local mt = { 173 | __index = parent, 174 | __tostring = objtostring, 175 | } 176 | setmetatable(newobject, mt) 177 | end 178 | Object.uid = getuid(Object) 179 | setmetatable(Object, { __tostring = function() return 'Object' end }) 180 | end 181 | 182 | local Interface 183 | 184 | local function validateInterface(object, interface) 185 | if not object.class and object.prototype then 186 | object = object.prototype 187 | end 188 | for k,v in pairs(interface.interface) do 189 | if tostring(type(object[k])) ~= v then 190 | return false 191 | end 192 | end 193 | if interface.superinterfaces then 194 | for superinterface in pairs(interface.superinterfaces) do 195 | if not validateInterface(object, superinterface) then 196 | return false 197 | end 198 | end 199 | end 200 | if type(object.class) == "table" and rawequal(object.class.prototype, object) then 201 | if not object.class.interfaces then 202 | rawset(object.class, 'interfaces', {}) 203 | end 204 | object.class.interfaces[interface] = true 205 | elseif type(object.class) == "table" and type(object.class.prototype) == "table" then 206 | validateInterface(object.class.prototype, interface) 207 | -- check if class is proper, thus preventing future checks. 208 | end 209 | return true 210 | end 211 | 212 | -- @function inherits 213 | -- @brief Return whether an Object or Class inherits from a given 214 | -- parent. 215 | -- @param object Object or Class to check 216 | -- @param parent Parent to test inheritance from 217 | -- @return whether an Object or Class inherits from a given 218 | -- parent. 219 | local function inherits(object, parent) 220 | object = getlibrary(object) 221 | if type(parent) == "string" then 222 | if not AceLibrary:HasInstance(parent) then 223 | return false 224 | else 225 | parent = AceLibrary(parent) 226 | end 227 | end 228 | AceOO:argCheck(parent, 2, "table") 229 | if type(object) ~= "table" then 230 | return false 231 | end 232 | local current 233 | if object.class then 234 | current = object.class 235 | else 236 | current = object 237 | end 238 | if type(current) ~= "table" then 239 | return false 240 | end 241 | if rawequal(current, parent) then 242 | return true 243 | end 244 | if parent.class then 245 | while true do 246 | if rawequal(current, Object) then 247 | break 248 | end 249 | if current.mixins then 250 | for mixin in pairs(current.mixins) do 251 | if rawequal(mixin, parent) then 252 | return true 253 | end 254 | end 255 | end 256 | if current.interfaces then 257 | for interface in pairs(current.interfaces) do 258 | if rawequal(interface, parent) then 259 | return true 260 | end 261 | end 262 | end 263 | current = current.super 264 | if type(current) ~= "table" then 265 | break 266 | end 267 | end 268 | 269 | local isInterface = false 270 | local curr = parent.class 271 | while true do 272 | if rawequal(curr, Object) then 273 | break 274 | elseif rawequal(curr, Interface) then 275 | isInterface = true 276 | break 277 | end 278 | curr = curr.super 279 | if type(curr) ~= "table" then 280 | break 281 | end 282 | end 283 | return isInterface and validateInterface(object, parent) 284 | else 285 | while true do 286 | if rawequal(current, parent) then 287 | return true 288 | elseif rawequal(current, Object) then 289 | return false 290 | end 291 | current = current.super 292 | if type(current) ~= "table" then 293 | return false 294 | end 295 | end 296 | end 297 | end 298 | 299 | -- @table Class 300 | -- @brief An object factory which sets up inheritence and supports 301 | -- 'mixins'. 302 | -- 303 | -- @metamethod Class call 304 | -- @brief Call ClassFactory:new() to create a new class. 305 | -- 306 | -- @method Class new 307 | -- @brief Construct a new object. 308 | -- @param (a1..a20) Arguments to pass to the object init function. 309 | -- @return The new object. 310 | -- 311 | -- @method Class init 312 | -- @brief Initialize a new class. 313 | -- @param parent Superclass. 314 | -- @param (a1..a20) Mixins. 315 | -- 316 | -- @method Class ToString 317 | -- @return A string representing the object, in this case 'Class'. 318 | local initStatus 319 | local Class 320 | local Mixin 321 | local autoEmbed = false 322 | local function traverseInterfaces(bit, total) 323 | if bit.superinterfaces then 324 | for interface in pairs(bit.superinterfaces) do 325 | if not total[interface] then 326 | total[interface] = true 327 | traverseInterfaces(interface, total) 328 | end 329 | end 330 | end 331 | end 332 | local class_new 333 | do 334 | Class = Factory(Object, setmetatable({}, {__index = Object}), Object) 335 | Class.super = Object 336 | 337 | local function protostring(t) 338 | return '<' .. tostring(t.class) .. ' prototype>' 339 | end 340 | local function classobjectstring(t) 341 | if t.ToString then 342 | return t:ToString() 343 | elseif t.GetLibraryVersion then 344 | return (t:GetLibraryVersion()) 345 | else 346 | return '<' .. tostring(t.class) .. ' instance>' 347 | end 348 | end 349 | local function classobjectequal(self, other) 350 | if type(self) == "table" and self.Equals then 351 | return self:Equals(other) 352 | elseif type(other) == "table" and other.Equals then 353 | return other:Equals(self) 354 | elseif type(self) == "table" and self.CompareTo then 355 | return self:CompareTo(other) == 0 356 | elseif type(other) == "table" and other.CompareTo then 357 | return other:CompareTo(self) == 0 358 | else 359 | return rawequal(self, other) 360 | end 361 | end 362 | local function classobjectlessthan(self, other) 363 | if type(self) == "table" and self.IsLessThan then 364 | return self:IsLessThan(other) 365 | elseif type(other) == "table" and other.IsLessThanOrEqualTo then 366 | return not other:IsLessThanOrEqualTo(self) 367 | elseif type(self) == "table" and self.CompareTo then 368 | return self:CompareTo(other) < 0 369 | elseif type(other) == "table" and other.CompareTo then 370 | return other:CompareTo(self) > 0 371 | elseif type(other) == "table" and other.IsLessThan and other.Equals then 372 | return other:Equals(self) or other:IsLessThan(self) 373 | else 374 | AceOO:error("cannot compare two objects") 375 | end 376 | end 377 | local function classobjectlessthanequal(self, other) 378 | if type(self) == "table" and self.IsLessThanOrEqualTo then 379 | return self:IsLessThanOrEqualTo(other) 380 | elseif type(other) == "table" and other.IsLessThan then 381 | return not other:IsLessThan(self) 382 | elseif type(self) == "table" and self.CompareTo then 383 | return self:CompareTo(other) <= 0 384 | elseif type(other) == "table" and other.CompareTo then 385 | return other:CompareTo(self) >= 0 386 | elseif type(self) == "table" and self.IsLessThan and self.Equals then 387 | return self:Equals(other) or self:IsLessThan(other) 388 | else 389 | AceOO:error("cannot compare two incompatible objects") 390 | end 391 | end 392 | local function classobjectadd(self, other) 393 | if type(self) == "table" and self.Add then 394 | return self:Add(other) 395 | else 396 | AceOO:error("cannot add two incompatible objects") 397 | end 398 | end 399 | local function classobjectsub(self, other) 400 | if type(self) == "table" and self.Subtract then 401 | return self:Subtract(other) 402 | else 403 | AceOO:error("cannot subtract two incompatible objects") 404 | end 405 | end 406 | local function classobjectunm(self, other) 407 | if type(self) == "table" and self.UnaryNegation then 408 | return self:UnaryNegation(other) 409 | else 410 | AceOO:error("attempt to negate an incompatible object") 411 | end 412 | end 413 | local function classobjectmul(self, other) 414 | if type(self) == "table" and self.Multiply then 415 | return self:Multiply(other) 416 | else 417 | AceOO:error("cannot multiply two incompatible objects") 418 | end 419 | end 420 | local function classobjectdiv(self, other) 421 | if type(self) == "table" and self.Divide then 422 | return self:Divide(other) 423 | else 424 | AceOO:error("cannot divide two incompatible objects") 425 | end 426 | end 427 | local function classobjectpow(self, other) 428 | if type(self) == "table" and self.Exponent then 429 | return self:Exponent(other) 430 | else 431 | AceOO:error("cannot exponentiate two incompatible objects") 432 | end 433 | end 434 | local function classobjectconcat(self, other) 435 | if type(self) == "table" and self.Concatenate then 436 | return self:Concatenate(other) 437 | else 438 | AceOO:error("cannot concatenate two incompatible objects") 439 | end 440 | end 441 | function class_new(self, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, 442 | a13, a14, a15, a16, a17, a18, a19, a20) 443 | if self.virtual then 444 | AceOO:error("Cannot instantiate a virtual class.") 445 | end 446 | 447 | local o = self.prototype 448 | local newobj = {} 449 | if o.class and o.class.instancemeta then 450 | setmetatable(newobj, o.class.instancemeta) 451 | else 452 | Object:init(newobj, o) 453 | end 454 | 455 | if self.interfaces and not self.interfacesVerified then 456 | -- Verify the interfaces 457 | 458 | for interface in pairs(self.interfaces) do 459 | for field,kind in pairs(interface.interface) do 460 | if tostring(type(newobj[field])) ~= kind then 461 | 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]))) 462 | end 463 | end 464 | end 465 | self.interfacesVerified = true 466 | end 467 | local tmp = initStatus 468 | initStatus = newobj 469 | newobj:init(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, 470 | a13, a14, a15, a16, a17, a18, a19, a20) 471 | if initStatus then 472 | initStatus = tmp 473 | AceOO:error("Initialization not completed, be sure to call the superclass's init method.") 474 | return 475 | end 476 | initStatus = tmp 477 | return newobj 478 | end 479 | local classmeta = { 480 | __tostring = objtostring, 481 | __call = function(self, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, 482 | a13, a14, a15, a16, a17, a18, a19, a20) 483 | return self:new(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, 484 | a13, a14, a15, a16, a17, a18, a19, a20) 485 | end, 486 | } 487 | function Class:init(newclass, parent, a1, a2, a3, a4, a5, a6, a7, a8, a9, 488 | a10, a11, a12, a13, a14, a15, a16, 489 | a17, a18, a19, a20) 490 | parent = parent or self 491 | 492 | local total 493 | 494 | if parent.class then 495 | total = { 496 | parent, a1, a2, a3, a4, a5, a6, a7, a8, a9, 497 | a10, a11, a12, a13, a14, a15, a16, 498 | a17, a18, a19, a20 499 | } 500 | parent = self 501 | else 502 | total = { 503 | a1, a2, a3, a4, a5, a6, a7, a8, a9, 504 | a10, a11, a12, a13, a14, a15, a16, 505 | a17, a18, a19, a20 506 | } 507 | end 508 | if not inherits(parent, Class) then 509 | AceOO:error("Classes must inherit from a proper class") 510 | end 511 | if parent.sealed then 512 | AceOO:error("Cannot inherit from a sealed class") 513 | end 514 | for i,v in ipairs(total) do 515 | if inherits(v, Mixin) and v.class then 516 | if not newclass.mixins then 517 | newclass.mixins = {} 518 | end 519 | if newclass.mixins[v] then 520 | AceOO:error("Cannot explicitly inherit from the same mixin twice") 521 | end 522 | newclass.mixins[v] = true 523 | elseif inherits(v, Interface) and v.class then 524 | if not newclass.interfaces then 525 | newclass.interfaces = {} 526 | end 527 | if newclass.interfaces[v] then 528 | AceOO:error("Cannot explicitly inherit from the same interface twice") 529 | end 530 | newclass.interfaces[v] = true 531 | else 532 | AceOO:error("Classes can only inherit from one or zero classes and any number of mixins or interfaces") 533 | end 534 | end 535 | if parent.interfaces then 536 | if newclass.interfaces then 537 | for interface in pairs(parent.interfaces) do 538 | newclass.interfaces[interface] = true 539 | end 540 | else 541 | newclass.interfaces = parent.interfaces 542 | end 543 | end 544 | for k in pairs(total) do 545 | total[k] = nil 546 | end 547 | table_setn(total, 0) 548 | 549 | newclass.super = parent 550 | 551 | newclass.prototype = setmetatable(total, { 552 | __index = parent.prototype, 553 | __tostring = protostring, 554 | }) 555 | total = nil 556 | 557 | newclass.instancemeta = { 558 | __index = newclass.prototype, 559 | __tostring = classobjectstring, 560 | __eq = classobjectequal, 561 | __lt = classobjectlessthan, 562 | __le = classobjectlessthanequal, 563 | __add = classobjectadd, 564 | __sub = classobjectsub, 565 | __unm = classobjectunm, 566 | __mul = classobjectmul, 567 | __div = classobjectdiv, 568 | __pow = classobjectpow, 569 | __concat = classobjectconcat, 570 | } 571 | 572 | setmetatable(newclass, classmeta) 573 | 574 | newclass.new = class_new 575 | 576 | if newclass.mixins then 577 | -- Fold in the mixins 578 | local err, msg 579 | for mixin in pairs(newclass.mixins) do 580 | local ret 581 | autoEmbed = true 582 | ret, msg = pcall(mixin.embed, mixin, newclass.prototype) 583 | autoEmbed = false 584 | if not ret then 585 | err = true 586 | break 587 | end 588 | end 589 | 590 | if err then 591 | local pt = newclass.prototype 592 | for k,v in pairs(pt) do 593 | pt[k] = nil 594 | end 595 | 596 | -- method conflict 597 | AceOO:error(msg) 598 | end 599 | end 600 | 601 | newclass.prototype.class = newclass 602 | 603 | if newclass.interfaces then 604 | for interface in pairs(newclass.interfaces) do 605 | traverseInterfaces(interface, newclass.interfaces) 606 | end 607 | end 608 | if newclass.mixins then 609 | for mixin in pairs(newclass.mixins) do 610 | if mixin.interfaces then 611 | if not newclass.interfaces then 612 | newclass.interfaces = {} 613 | end 614 | for interface in pairs(mixin.interfaces) do 615 | newclass.interfaces[interface] = true 616 | end 617 | end 618 | end 619 | end 620 | end 621 | function Class:ToString() 622 | if type(self.GetLibraryVersion) == "function" then 623 | return (self:GetLibraryVersion()) 624 | else 625 | return "Class" 626 | end 627 | end 628 | 629 | local tmp 630 | function Class.prototype:init() 631 | if rawequal(self, initStatus) then 632 | initStatus = nil 633 | else 634 | AceOO:error("Improper self passed to init. You must do MyClass.super.prototype.init(self, ...)", 2) 635 | end 636 | self.uid = getuid(self) 637 | local current = self.class 638 | while true do 639 | if current == Class then 640 | break 641 | end 642 | if current.mixins then 643 | for mixin in pairs(current.mixins) do 644 | if type(mixin.OnInstanceInit) == "function" then 645 | mixin:OnInstanceInit(self) 646 | end 647 | end 648 | end 649 | current = current.super 650 | end 651 | end 652 | end 653 | 654 | 655 | -- @object ClassFactory 656 | -- @brief A factory for creating classes. Rarely used directly. 657 | local ClassFactory = Factory(Object, Class, Object) 658 | 659 | function Class:new(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, 660 | a12, a13, a14, a15, a16, a17, a18, a19, a20) 661 | local x = ClassFactory:new(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, 662 | a12, a13, a14, a15, a16, a17, a18, a19, a20) 663 | if AceOO.classes then 664 | AceOO.classes[x] = true 665 | end 666 | return x 667 | end 668 | getmetatable(Class).__call = Class.new 669 | 670 | -- @class Mixin 671 | -- @brief A class to create mixin objects, which contain methods that get 672 | -- "mixed in" to class prototypes. 673 | -- 674 | -- @object Mixin prototype 675 | -- @brief The prototype that mixin objects inherit their methods from. 676 | -- 677 | -- @method Mixin prototype embed 678 | -- @brief Mix in the methods of our object which are listed in our interface 679 | -- to the supplied target table. 680 | -- 681 | -- @method Mixin prototype init 682 | -- @brief Initialize the mixin object. 683 | -- @param newobj The new object we're initializing. 684 | -- @param interface The interface we implement (the list of methods our 685 | -- prototype provides which should be mixed into the target 686 | -- table by embed). 687 | do 688 | Mixin = Class() 689 | function Mixin:ToString() 690 | if self.GetLibraryVersion then 691 | return (self:GetLibraryVersion()) 692 | else 693 | return 'Mixin' 694 | end 695 | end 696 | local function _Embed(state, field, target) 697 | field = next(state.export, field) 698 | if field == nil then 699 | return 700 | end 701 | 702 | if rawget(target, field) or (target[field] and target[field] ~= state[field]) then 703 | AceOO:error("Method conflict in attempt to mixin. Field %q", field) 704 | end 705 | 706 | target[field] = state[field] 707 | 708 | local ret,msg = pcall(_Embed, state, field, target) 709 | if not ret then 710 | -- Mix in the next method according to the defined interface. If that 711 | -- fails due to a conflict, re-raise to back out the previous mixed 712 | -- methods. 713 | 714 | target[field] = nil 715 | AceOO:error(msg) 716 | end 717 | end 718 | function Mixin.prototype:embed(target) 719 | local mt = getmetatable(target) 720 | setmetatable(target, nil) 721 | local err, msg = pcall(_Embed, self, nil, target) 722 | if not err then 723 | setmetatable(target, mt) 724 | AceOO:error(msg) 725 | return 726 | end 727 | if type(self.embedList) == "table" then 728 | self.embedList[target] = true 729 | end 730 | if type(target.class) ~= "table" then 731 | target[self] = true 732 | end 733 | if not autoEmbed and type(self.OnManualEmbed) == "function" then 734 | if not target.modules then 735 | self:OnManualEmbed(target) 736 | end 737 | end 738 | setmetatable(target, mt) 739 | end 740 | 741 | function Mixin.prototype:activate(oldLib, oldDeactivate) 742 | if oldLib and oldLib.embedList then 743 | for target in pairs(oldLib.embedList) do 744 | local mt = getmetatable(target) 745 | setmetatable(target, nil) 746 | for field in pairs(oldLib.export) do 747 | target[field] = nil 748 | end 749 | setmetatable(target, mt) 750 | end 751 | self.embedList = oldLib.embedList 752 | for target in pairs(self.embedList) do 753 | self:embed(target) 754 | end 755 | else 756 | self.embedList = setmetatable({}, {__mode="k"}) 757 | end 758 | end 759 | 760 | function Mixin.prototype:init(export, a1, a2, a3, a4, a5, a6, a7, a8, a9, 761 | a10, a11, a12, a13, a14, a15, a16, 762 | a17, a18, a19, a20) 763 | AceOO:argCheck(export, 2, "table") 764 | for k,v in pairs(export) do 765 | if type(k) ~= "number" then 766 | AceOO:error("All keys to argument #2 must be numbers.") 767 | elseif type(v) ~= "string" then 768 | AceOO:error("All values to argument #2 must be strings.") 769 | end 770 | end 771 | local num = table.getn(export) 772 | for i = 1, num do 773 | local v = export[i] 774 | export[i] = nil 775 | export[v] = true 776 | end 777 | table_setn(export, 0) 778 | local interfaces 779 | if a1 then 780 | local l = getlibrary 781 | interfaces = { l(a1), l(a2), l(a3), l(a4), l(a5), l(a6), l(a7), l(a8), 782 | l(a9), l(a10), l(a11), l(a12), l(a13), l(a14), l(a15), l(a16), 783 | l(a17), l(a18), l(a19), l(a20) } 784 | for _,v in ipairs(interfaces) do 785 | if not v.class or not inherits(v, Interface) then 786 | AceOO:error("Mixins can inherit only from interfaces") 787 | end 788 | end 789 | local num = table.getn(interfaces) 790 | for i = 1, num do 791 | local v = interfaces[i] 792 | interfaces[i] = nil 793 | interfaces[v] = true 794 | end 795 | table_setn(interfaces, 0) 796 | for interface in pairs(interfaces) do 797 | traverseInterfaces(interface, interfaces) 798 | end 799 | for interface in pairs(interfaces) do 800 | for field,kind in pairs(interface.interface) do 801 | if kind ~= "nil" then 802 | local good = false 803 | for bit in pairs(export) do 804 | if bit == field then 805 | good = true 806 | break 807 | end 808 | end 809 | if not good then 810 | AceOO:error("Mixin does not fully accommodate field %q", field) 811 | end 812 | end 813 | end 814 | end 815 | end 816 | self.super = Mixin.prototype 817 | Mixin.super.prototype.init(self) 818 | self.export = export 819 | self.interfaces = interfaces 820 | end 821 | end 822 | 823 | -- @class Interface 824 | -- @brief A class to create interfaces, which contain contracts that classes 825 | -- which inherit from this must comply with. 826 | -- 827 | -- @object Interface prototype 828 | -- @brief The prototype that interface objects must adhere to. 829 | -- 830 | -- @method Interface prototype init 831 | -- @brief Initialize the mixin object. 832 | -- @param interface The interface we contract (the hash of fields forced). 833 | -- @param (a1..a20) Superinterfaces 834 | do 835 | Interface = Class() 836 | function Interface:ToString() 837 | if self.GetLibraryVersion then 838 | return (self:GetLibraryVersion()) 839 | else 840 | return 'Instance' 841 | end 842 | end 843 | function Interface.prototype:init(interface, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) 844 | Interface.super.prototype.init(self) 845 | AceOO:argCheck(interface, 2, "table") 846 | for k,v in pairs(interface) do 847 | if type(k) ~= "string" then 848 | AceOO:error("All keys to argument #2 must be numbers.") 849 | elseif type(v) ~= "string" then 850 | AceOO:error("All values to argument #2 must be strings.") 851 | elseif v ~= "nil" and v ~= "string" and v ~= "number" and v ~= "table" and v ~= "function" then 852 | AceOO:error('All values to argument #2 must either be "nil", "string", "number", "table", or "function".') 853 | end 854 | end 855 | local l = getlibrary 856 | a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20 = l(a1), l(a2), l(a3), l(a4), l(a5), l(a6), l(a7), l(a8), l(a9), l(a10), l(a11), l(a12), l(a13), l(a14), l(a15), l(a16), l(a17), l(a18), l(a19), l(a20) 857 | if a1 then 858 | self.superinterfaces = {a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20} 859 | for k,v in ipairs(self.superinterfaces) do 860 | if not inherits(v, Interface) or not v.class then 861 | AceOO:error('Cannot provide a non-Interface to inherit from') 862 | end 863 | end 864 | local num = table.getn(self.superinterfaces) 865 | for i = 1, num do 866 | local v = self.superinterfaces[i] 867 | self.superinterfaces[i] = nil 868 | self.superinterfaces[v] = true 869 | end 870 | table_setn(self.superinterfaces, 0) 871 | end 872 | self.interface = interface 873 | end 874 | end 875 | 876 | -- @function Classpool 877 | -- @brief Obtain a read only class from our pool of classes, indexed by the 878 | -- superclass and mixins. 879 | -- @param sc The superclass of the class we want. 880 | -- @param (m1..m20) Mixins of the class we want's objects. 881 | -- @return A read only class from the class pool. 882 | local Classpool 883 | do 884 | local pool = setmetatable({}, {__mode = 'v'}) 885 | local function newindex(k, v) 886 | AceOO:error('Attempt to modify a read-only class.') 887 | end 888 | local function protonewindex(k, v) 889 | AceOO:error('Attempt to modify a read-only class prototype.') 890 | end 891 | local function ts(bit) 892 | if type(bit) ~= "table" then 893 | return tostring(bit) 894 | elseif getmetatable(bit) and bit.__tostring then 895 | return tostring(bit) 896 | elseif type(bit.GetLibraryVersion) == "function" then 897 | return bit:GetLibraryVersion() 898 | else 899 | return tostring(bit) 900 | end 901 | end 902 | local t 903 | local function getcomplexuid(sc, m1, m2, m3, m4, m5, m6, m7, m8, m9, 904 | m10, m11, m12, m13, m14, m15, m16, m17, m18, m19, m20) 905 | if not t then t = {} end 906 | 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 907 | if m1 then if m1.uid then table.insert(t, m1.uid) else AceOO:error("%s is not an appropriate mixin", ts(m1)) end 908 | if m2 then if m2.uid then table.insert(t, m2.uid) else AceOO:error("%s is not an appropriate mixin", ts(m2)) end 909 | if m3 then if m3.uid then table.insert(t, m3.uid) else AceOO:error("%s is not an appropriate mixin", ts(m3)) end 910 | if m4 then if m4.uid then table.insert(t, m4.uid) else AceOO:error("%s is not an appropriate mixin", ts(m4)) end 911 | if m5 then if m5.uid then table.insert(t, m5.uid) else AceOO:error("%s is not an appropriate mixin", ts(m5)) end 912 | if m6 then if m6.uid then table.insert(t, m6.uid) else AceOO:error("%s is not an appropriate mixin", ts(m6)) end 913 | if m7 then if m7.uid then table.insert(t, m7.uid) else AceOO:error("%s is not an appropriate mixin", ts(m7)) end 914 | if m8 then if m8.uid then table.insert(t, m8.uid) else AceOO:error("%s is not an appropriate mixin", ts(m8)) end 915 | if m9 then if m9.uid then table.insert(t, m9.uid) else AceOO:error("%s is not an appropriate mixin", ts(m9)) end 916 | if m10 then if m10.uid then table.insert(t, m10.uid) else AceOO:error("%s is not an appropriate mixin", ts(m10)) end 917 | if m11 then if m11.uid then table.insert(t, m11.uid) else AceOO:error("%s is not an appropriate mixin", ts(m11)) end 918 | if m12 then if m12.uid then table.insert(t, m12.uid) else AceOO:error("%s is not an appropriate mixin", ts(m12)) end 919 | if m13 then if m13.uid then table.insert(t, m13.uid) else AceOO:error("%s is not an appropriate mixin", ts(m13)) end 920 | if m14 then if m14.uid then table.insert(t, m14.uid) else AceOO:error("%s is not an appropriate mixin", ts(m14)) end 921 | if m15 then if m15.uid then table.insert(t, m15.uid) else AceOO:error("%s is not an appropriate mixin", ts(m15)) end 922 | if m16 then if m16.uid then table.insert(t, m16.uid) else AceOO:error("%s is not an appropriate mixin", ts(m16)) end 923 | if m17 then if m17.uid then table.insert(t, m17.uid) else AceOO:error("%s is not an appropriate mixin", ts(m17)) end 924 | if m18 then if m18.uid then table.insert(t, m18.uid) else AceOO:error("%s is not an appropriate mixin", ts(m18)) end 925 | if m19 then if m19.uid then table.insert(t, m19.uid) else AceOO:error("%s is not an appropriate mixin", ts(m19)) end 926 | if m20 then if m20.uid then table.insert(t, m20.uid) else AceOO:error("%s is not an appropriate mixin", ts(m20)) end 927 | end end end end end end end end end end end end end end end end end end end end end 928 | table.sort(t) 929 | local uid = table.concat(t, '') 930 | for k in pairs(t) do t[k] = nil end 931 | table_setn(t, 0) 932 | return uid 933 | end 934 | local classmeta 935 | function Classpool(sc, m1, m2, m3, m4, m5, m6, m7, m8, m9, 936 | m10, m11, m12, m13, m14, m15, m16, 937 | m17, m18, m19, m20) 938 | local l = getlibrary 939 | sc, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15, m16, m17, m18, m19, m20 = l(sc), l(m1), l(m2), l(m3), l(m4), l(m5), l(m6), l(m7), l(m8), l(m9), l(m10), l(m11), l(m12), l(m13), l(m14), l(m15), l(m16), l(m17), l(m18), l(m19), l(m20) 940 | if sc and sc.class then 941 | sc, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15, m16, m17, m18, m19, m20 = Class, sc, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15, m16, m17, m18, m19 942 | end 943 | sc = sc or Class 944 | local key = getcomplexuid(sc, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15, m16, m17, m18, m19, m20) 945 | if not pool[key] then 946 | local class = Class(sc, m1, m2, m3, m4, m5, m6, m7, m8, m9, 947 | m10, m11, m12, m13, m14, m15, m16, m17, 948 | m18, m19, m20) 949 | if not classmeta then 950 | classmeta = {} 951 | local mt = getmetatable(class) 952 | for k,v in pairs(mt) do 953 | classmeta[k] = v 954 | end 955 | classmeta.__newindex = newindex 956 | end 957 | -- Prevent the user from adding methods to this class. 958 | -- NOTE: I'm not preventing modifications of existing class members, 959 | -- but it's likely that only a truly malicious user will be doing so. 960 | class.sealed = true 961 | setmetatable(class, classmeta) 962 | getmetatable(class.prototype).__newindex = protonewindex 963 | pool[key] = class 964 | end 965 | return pool[key] 966 | end 967 | end 968 | 969 | AceOO.Factory = Factory 970 | AceOO.Object = Object 971 | AceOO.Class = Class 972 | AceOO.Mixin = Mixin 973 | AceOO.Interface = Interface 974 | AceOO.Classpool = Classpool 975 | AceOO.inherits = inherits 976 | 977 | -- Library handling bits 978 | 979 | local function activate(self, oldLib, oldDeactivate) 980 | AceOO = self 981 | Factory = self.Factory 982 | Object = self.Object 983 | Class = self.Class 984 | ClassFactory.prototype = Class 985 | Mixin = self.Mixin 986 | Interface = self.Interface 987 | Classpool = self.Classpool 988 | 989 | if oldLib then 990 | self.classes = oldLib.classes 991 | end 992 | if not self.classes then 993 | self.classes = setmetatable({}, {__mode="k"}) 994 | else 995 | for class in pairs(self.classes) do 996 | class.new = class_new 997 | end 998 | end 999 | 1000 | if oldDeactivate then 1001 | oldDeactivate(oldLib) 1002 | end 1003 | end 1004 | 1005 | AceLibrary:Register(AceOO, MAJOR_VERSION, MINOR_VERSION, activate) 1006 | AceOO = AceLibrary(MAJOR_VERSION) 1007 | -------------------------------------------------------------------------------- /libs/FuBarPlugin/FuBarPlugin-2.0.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Name: FuBarPlugin-2.0 3 | Revision: $Rev: 15811 $ 4 | Author: Cameron Kenneth Knight (ckknight@gmail.com) 5 | Website: http://wiki.wowace.com/index.php/FuBarPlugin-2.0 6 | Documentation: http://wiki.wowace.com/index.php/FuBarPlugin-2.0 7 | SVN: svn://svn.wowace.com/root/branches/FuBar/FuBarPlugin-2.0/FuBarPlugin-2.0/ 8 | Description: Plugin for FuBar. 9 | Dependencies: AceLibrary, AceOO-2.0, AceEvent-2.0, Tablet-2.0, Dewdrop-2.0 10 | ]] 11 | 12 | local MAJOR_VERSION = "FuBarPlugin-2.0" 13 | local MINIMAPCONTAINER_MAJOR_VERSION = "FuBarPlugin-MinimapContainer-2.0" 14 | local MINOR_VERSION = "$Revision: 15811 $" 15 | 16 | -- This ensures the code is only executed if the libary doesn't already exist, or is a newer version 17 | if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary.") end 18 | if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end 19 | 20 | if not AceLibrary:HasInstance("AceOO-2.0") then error(MAJOR_VERSION .. " requires AceOO-2.0.") end 21 | 22 | local AceEvent = AceLibrary:HasInstance("AceEvent-2.0") and AceLibrary("AceEvent-2.0") 23 | local Tablet = AceLibrary:HasInstance("Tablet-2.0") and AceLibrary("Tablet-2.0") 24 | local Dewdrop = AceLibrary:HasInstance("Dewdrop-2.0") and AceLibrary("Dewdrop-2.0") 25 | 26 | local epsilon = 1e-5 27 | local _G = getfenv(0) 28 | 29 | local SHOW_ICON = "Show icon" 30 | local SHOW_ICON_DESC = "Show the plugins icon on the panel." 31 | local SHOW_TEXT = "Show text" 32 | local SHOW_TEXT_DESC = "Show the plugins text on the panel." 33 | local SHOW_COLORED_TEXT = "Show colored text" 34 | local SHOW_COLORED_TEXT_DESC = "Allow the plugin to color its text." 35 | local DETACH_TOOLTIP = "Detach tooltip" 36 | local DETACH_TOOLTIP_DESC = "Detach the tooltip from the panel." 37 | local LOCK_TOOLTIP = "Lock tooltip" 38 | local LOCK_TOOLTIP_DESC = "Lock the tooltips position. When the tooltip is locked, you must use Alt to access it with your mouse." 39 | local POSITION = "Position" 40 | local POSITION_DESC = "Position the plugin on the panel." 41 | local POSITION_LEFT = "Left" 42 | local POSITION_RIGHT = "Right" 43 | local POSITION_CENTER = "Center" 44 | local ATTACH_TO_MINIMAP = "Attach to minimap" 45 | local ATTACH_TO_MINIMAP_DESC = "Attach the plugin to the minimap instead of the panel." 46 | local HIDE_FUBAR_PLUGIN = "Hide FuBar plugin" 47 | local HIDE_FUBAR_PLUGIN_CMD = "Hidden" 48 | local HIDE_FUBAR_PLUGIN_DESC = "Hide the plugin from the panel." 49 | 50 | if GetLocale() == "koKR" then 51 | SHOW_ICON = "아이콘 표시" 52 | SHOW_ICON_DESC = "패널에 플러그인 아이콘을 표시합니다." 53 | SHOW_TEXT = "텍스트 표시" 54 | SHOW_TEXT_DESC = "페널에 플러그인 텍스트를 표시합니다." 55 | SHOW_COLORED_TEXT = "색상화된 텍스트 표시" 56 | SHOW_COLORED_TEXT_DESC = "플러그인의 텍스트 색상을 허용합니다." 57 | DETACH_TOOLTIP = "툴팁 분리" 58 | DETACH_TOOLTIP_DESC = "패널에서 툴팁을 분리 합니다." 59 | LOCK_TOOLTIP = "툴팁 고정" 60 | LOCK_TOOLTIP_DESC = "툴팁 위치를 고정합니다." 61 | POSITION = "위치" 62 | POSITION_DESC = "패널에서 플러그인의 위치를 설정합니다." 63 | POSITION_LEFT = "왼쪽" 64 | POSITION_RIGHT = "오른쪽" 65 | POSITION_CENTER = "가운데" 66 | ATTACH_TO_MINIMAP = "미니맵에 표시" 67 | ATTACH_TO_MINIMAP_DESC = "플러그인을 패널 대신 미니맵에 표시합니다." 68 | HIDE_FUBAR_PLUGIN = "FuBar 플러그인 숨기기" 69 | HIDE_FUBAR_PLUGIN_CMD = "숨겨짐" 70 | HIDE_FUBAR_PLUGIN_DESC = "패널에서 플러그인을 숨깁니다." 71 | elseif GetLocale() == "deDE" then 72 | SHOW_ICON = "Zeige Icon" 73 | SHOW_ICON_DESC = "Zeige das Plugin-Icon auf der Leiste." 74 | SHOW_TEXT = "Zeige Text" 75 | SHOW_TEXT_DESC = "Zeige den Plugin-Text auf der Leiste." 76 | SHOW_COLORED_TEXT = "Zeige gef\195\164rbten Text" 77 | SHOW_COLORED_TEXT_DESC = "Dem Plugin erlauben sein Text zu f\195\164rben." 78 | DETACH_TOOLTIP = "Tooltip l\195\182sen" 79 | DETACH_TOOLTIP_DESC = "Tooltip von der Leiste l\195\182sen." 80 | LOCK_TOOLTIP = "Tooltip sperren" 81 | LOCK_TOOLTIP_DESC = "Tooltip an der Position sperren." 82 | POSITION = "Position" 83 | POSITION_DESC = "Positioniert das Plugin auf der Leiste." 84 | POSITION_LEFT = "Links" 85 | POSITION_RIGHT = "Rechts" 86 | POSITION_CENTER = "Mitte" 87 | ATTACH_TO_MINIMAP = "An der Minimap anbringen" 88 | ATTACH_TO_MINIMAP_DESC = "Bringt das Plugin an der Minimap anstelle der Leiste an." 89 | HIDE_FUBAR_PLUGIN = "Versteckt das FuBar Plugin" 90 | HIDE_FUBAR_PLUGIN_CMD = "Verstecken" 91 | HIDE_FUBAR_PLUGIN_DESC = "Versteckt das Plugin von der Leiste." 92 | elseif GetLocale() == "frFR" then 93 | SHOW_ICON = "Afficher l'ic\195\180ne" 94 | SHOW_ICON_DESC = "Afficher l'ic\195\180ne du plugin sur le panneau." 95 | SHOW_TEXT = "Afficher le texte" 96 | SHOW_TEXT_DESC = "Afficher le texte du plugin sur le panneau." 97 | SHOW_COLORED_TEXT = "Afficher la couleur du texte" 98 | SHOW_COLORED_TEXT_DESC = "Permet au plugin de colorer le texte." 99 | DETACH_TOOLTIP = "D\195\169tacher le tooltip" 100 | DETACH_TOOLTIP_DESC = "Permet de d\195\169tacher le tooltip du panneau." 101 | LOCK_TOOLTIP = "Bloquer le tooltip" 102 | LOCK_TOOLTIP_DESC = "Permet de bloquer le tooltip \195\160 sa position actuelle." 103 | POSITION = "Position" 104 | POSITION_DESC = "Permet de changer la position du plugin dans le panneau." 105 | POSITION_LEFT = "Gauche" 106 | POSITION_RIGHT = "Droite" 107 | POSITION_CENTER = "Centre" 108 | ATTACH_TO_MINIMAP = "Attacher \195\160 la minicarte" 109 | ATTACH_TO_MINIMAP_DESC = "Atteche l'ic\195\180ne du plugin \195\160 la minicarte." 110 | HIDE_FUBAR_PLUGIN = "Masquer le plugin" 111 | HIDE_FUBAR_PLUGIN_CMD = "Masqu\195\169" 112 | HIDE_FUBAR_PLUGIN_DESC = "Permet de masquer compl\195\168tement le plugin du panneau." 113 | elseif GetLocale() == "zhCN" then 114 | SHOW_ICON = "显示图标" 115 | SHOW_ICON_DESC = "在面板上显示插件图标." 116 | SHOW_TEXT = "显示文字" 117 | SHOW_TEXT_DESC = "在面板上显示文字标题." 118 | SHOW_COLORED_TEXT = "显示彩色文字" 119 | SHOW_COLORED_TEXT_DESC = "允许插件显示彩色文字." 120 | DETACH_TOOLTIP = "独立提示信息" 121 | DETACH_TOOLTIP_DESC = "从面板上独立提示信息." 122 | LOCK_TOOLTIP = "锁定提示信息" 123 | LOCK_TOOLTIP_DESC = "锁定提示信息位置." 124 | POSITION = "位置" 125 | POSITION_DESC = "插件在面板上的位置." 126 | POSITION_LEFT = "居左" 127 | POSITION_RIGHT = "居右" 128 | POSITION_CENTER = "居中" 129 | ATTACH_TO_MINIMAP = "依附在小地图" 130 | ATTACH_TO_MINIMAP_DESC = "插件图标依附在小地图而不显示在面板上." 131 | HIDE_FUBAR_PLUGIN = "隐藏FuBar插件" 132 | HIDE_FUBAR_PLUGIN_CMD = "Hidden" 133 | HIDE_FUBAR_PLUGIN_DESC = "在面板上隐藏该插件." 134 | elseif GetLocale() == "zhTW" then 135 | SHOW_ICON = "顯示圖示" 136 | SHOW_ICON_DESC = "在面板上顯示插件圖示。" 137 | SHOW_TEXT = "顯示文字" 138 | SHOW_TEXT_DESC = "在面板上顯示文字標題。" 139 | SHOW_COLORED_TEXT = "顯示彩色文字" 140 | SHOW_COLORED_TEXT_DESC = "允許插件顯示彩色文字。" 141 | DETACH_TOOLTIP = "獨立提示訊息" 142 | DETACH_TOOLTIP_DESC = "從面板上獨立提示訊息。" 143 | LOCK_TOOLTIP = "鎖定提示訊息" 144 | LOCK_TOOLTIP_DESC = "鎖定提示訊息位置。" 145 | POSITION = "位置" 146 | POSITION_DESC = "插件在面板上的位置。" 147 | POSITION_LEFT = "靠左" 148 | POSITION_RIGHT = "靠右" 149 | POSITION_CENTER = "置中" 150 | ATTACH_TO_MINIMAP = "依附在小地圖" 151 | ATTACH_TO_MINIMAP_DESC = "插件圖標依附在小地圖而不顯示在面板上。" 152 | HIDE_FUBAR_PLUGIN = "隱藏FuBar插件" 153 | HIDE_FUBAR_PLUGIN_CMD = "Hidden" 154 | HIDE_FUBAR_PLUGIN_DESC = "在面板上隱藏該插件." 155 | end 156 | 157 | local FuBarPlugin = AceLibrary("AceOO-2.0").Mixin { 158 | "GetTitle", 159 | "GetName", 160 | "GetCategory", 161 | "SetFontSize", 162 | "GetFrame", 163 | "Show", 164 | "Hide", 165 | "GetPanel", 166 | "IsTextColored", 167 | "ToggleTextColored", 168 | "IsMinimapAttached", 169 | "ToggleMinimapAttached", 170 | "Update", 171 | "UpdateDisplay", 172 | "UpdateData", 173 | "UpdateText", 174 | "UpdateTooltip", 175 | "SetIcon", 176 | "GetIcon", 177 | "CheckWidth", 178 | "SetText", 179 | "GetText", 180 | "IsIconShown", 181 | "ToggleIconShown", 182 | "ShowIcon", 183 | "HideIcon", 184 | "IsTextShown", 185 | "ToggleTextShown", 186 | "ShowText", 187 | "HideText", 188 | "IsTooltipDetached", 189 | "ToggleTooltipDetached", 190 | "DetachTooltip", 191 | "ReattachTooltip", 192 | "GetDefaultPosition", 193 | "SetPanel", 194 | "IsLoadOnDemand", 195 | "IsDisabled", 196 | "CreateBasicPluginFrame", 197 | "CreatePluginChildFrame", 198 | "OpenMenu", 199 | "AddImpliedMenuOptions", 200 | } 201 | 202 | local good = nil 203 | local function CheckFuBar() 204 | if not good then 205 | good = FuBar and tonumber(string.sub(FuBar.version, 1, 3)) and tonumber(string.sub(FuBar.version, 1, 3)) >= 2 and true 206 | end 207 | return good 208 | end 209 | 210 | function FuBarPlugin:GetTitle() 211 | local name = self.title or self.name 212 | FuBarPlugin:assert(name, "You must provide self.title or self.name") 213 | local _,_,title = string.find(name, "FuBar %- (.-)%s*$") 214 | if not title then 215 | title = name 216 | end 217 | return (string.gsub(string.gsub(title, "|c%x%x%x%x%x%x%x%x", ""), "|r", "")) 218 | end 219 | 220 | function FuBarPlugin:GetName() 221 | return self.name 222 | end 223 | 224 | function FuBarPlugin:GetCategory() 225 | return self.category or "Other" 226 | end 227 | 228 | function FuBarPlugin:GetFrame() 229 | return self.frame 230 | end 231 | 232 | function FuBarPlugin:GetPanel() 233 | return self.panel 234 | end 235 | 236 | function FuBarPlugin:IsTextColored() 237 | return not self.db or not self.db.profile or not self.db.profile.uncolored 238 | end 239 | 240 | function FuBarPlugin:ToggleTextColored() 241 | FuBarPlugin:assert(self.db, "Cannot change text color if self.db is not available. (" .. self:GetTitle() .. ")") 242 | self.db.profile.uncolored = not self.db.profile.uncolored or nil 243 | self:UpdateText() 244 | end 245 | 246 | function FuBarPlugin:ToggleMinimapAttached() 247 | if CheckFuBar() and not self.cannotAttachToMinimap then 248 | local value = self:IsMinimapAttached() 249 | if value then 250 | if self.panel then 251 | self.panel:RemovePlugin(self) 252 | end 253 | FuBar:GetPanel(1):AddPlugin(self, nil, self.defaultPosition) 254 | else 255 | if self.panel then 256 | self.panel:RemovePlugin(self) 257 | end 258 | AceLibrary(MINIMAPCONTAINER_MAJOR_VERSION):AddPlugin(self) 259 | end 260 | end 261 | Dewdrop:Close() 262 | end 263 | 264 | function FuBarPlugin:IsMinimapAttached() 265 | if not CheckFuBar() then 266 | return true 267 | end 268 | return self.panel == AceLibrary(MINIMAPCONTAINER_MAJOR_VERSION) 269 | end 270 | 271 | function FuBarPlugin:Update() 272 | self:UpdateData() 273 | self:UpdateText() 274 | self:UpdateTooltip() 275 | end 276 | 277 | function FuBarPlugin:UpdateDisplay() 278 | self:UpdateText() 279 | self:UpdateTooltip() 280 | end 281 | 282 | function FuBarPlugin:UpdateData() 283 | if type(self.OnDataUpdate) == "function" then 284 | if not self:IsDisabled() then 285 | self:OnDataUpdate() 286 | end 287 | end 288 | end 289 | 290 | function FuBarPlugin:UpdateText() 291 | if type(self.OnTextUpdate) == "function" then 292 | if not self:IsDisabled() then 293 | self:OnTextUpdate() 294 | end 295 | elseif self:IsTextShown() then 296 | self:SetText(self:GetTitle()) 297 | end 298 | end 299 | 300 | function FuBarPlugin:RegisterTablet() 301 | if not Tablet:IsRegistered(self.frame) then 302 | if self.db and self.db.profile and not self.db.profile.detachedTooltip then 303 | self.db.profile.detachedTooltip = {} 304 | end 305 | Tablet:Register(self.frame, 306 | 'children', function() 307 | Tablet:SetTitle(self:GetTitle()) 308 | if type(self.OnTooltipUpdate) == "function" then 309 | if not self:IsDisabled() then 310 | self:OnTooltipUpdate() 311 | end 312 | end 313 | end, 314 | 'clickable', self.clickableTooltip, 315 | 'data', CheckFuBar() and FuBar.db.profile.tooltip or self.db and self.db.profile.detachedTooltip or {}, 316 | 'detachedData', self.db and self.db.profile.detachedTooltip or {}, 317 | 'point', function(frame) 318 | if frame:GetTop() > GetScreenHeight() / 2 then 319 | local x = frame:GetCenter() 320 | if x < GetScreenWidth() / 3 then 321 | return "TOPLEFT", "BOTTOMLEFT" 322 | elseif x < GetScreenWidth() * 2 / 3 then 323 | return "TOP", "BOTTOM" 324 | else 325 | return "TOPRIGHT", "BOTTOMRIGHT" 326 | end 327 | else 328 | local x = frame:GetCenter() 329 | if x < GetScreenWidth() / 3 then 330 | return "BOTTOMLEFT", "TOPLEFT" 331 | elseif x < GetScreenWidth() * 2 / 3 then 332 | return "BOTTOM", "TOP" 333 | else 334 | return "BOTTOMRIGHT", "TOPRIGHT" 335 | end 336 | end 337 | end, 338 | 'menu', self.OnMenuRequest and function(level, value, valueN_1, valueN_2, valueN_3, valueN_4) 339 | if level == 1 then 340 | local name = tostring(self) 341 | if not string.find(name, '^table:') then 342 | name = string.gsub(name, "|c%x%x%x%x%x%x%x%x(.-)|r", "%1") 343 | Dewdrop:AddLine( 344 | 'text', name, 345 | 'isTitle', true 346 | ) 347 | end 348 | end 349 | if type(self.OnMenuRequest) == "function" then 350 | self:OnMenuRequest(level, value, true, valueN_1, valueN_2, valueN_3, valueN_4) 351 | elseif type(self.OnMenuRequest) == "table" then 352 | Dewdrop:FeedAceOptionsTable(self.OnMenuRequest) 353 | end 354 | end, 355 | 'hideWhenEmpty', self.tooltipHiddenWhenEmpty 356 | ) 357 | end 358 | end 359 | 360 | function FuBarPlugin:UpdateTooltip() 361 | FuBarPlugin.RegisterTablet(self) 362 | if self:IsMinimapAttached() and not self:IsTooltipDetached() and self.minimapFrame then 363 | Tablet:Refresh(self.minimapFrame) 364 | else 365 | Tablet:Refresh(self.frame) 366 | end 367 | end 368 | 369 | function FuBarPlugin:OnProfileEnable() 370 | self:Update() 371 | end 372 | 373 | function FuBarPlugin:Show(panelId) 374 | if self.frame:IsShown() or (self.minimapFrame and self.minimapFrame:IsShown()) then 375 | return 376 | end 377 | if panelId ~= false then 378 | if self.db then 379 | self.db.profile.hidden = nil 380 | end 381 | end 382 | if self.IsActive and not self:IsActive() then 383 | self.panelIdTmp = panelId 384 | self:ToggleActive() 385 | self.panelIdTmp = nil 386 | if self.db then 387 | self.db.profile.disabled = nil 388 | end 389 | elseif not self.db or not self.db.profile.hidden then 390 | if panelId == 0 or not CheckFuBar() then 391 | AceLibrary(MINIMAPCONTAINER_MAJOR_VERSION):AddPlugin(self) 392 | else 393 | FuBar:ShowPlugin(self, panelId or self.panelIdTmp) 394 | end 395 | if not self.userDefinedFrame then 396 | if not self:IsTextShown() then 397 | self.textFrame:SetText("") 398 | self.textFrame:SetWidth(epsilon) 399 | self.textFrame:Hide() 400 | end 401 | if not self:IsIconShown() then 402 | self.iconFrame:SetWidth(epsilon) 403 | self.iconFrame:Hide() 404 | end 405 | end 406 | self:Update() 407 | end 408 | end 409 | 410 | function FuBarPlugin:Hide(check) 411 | if not self.frame:IsShown() and (not self.minimapFrame or not self.minimapFrame:IsShown()) then 412 | return 413 | end 414 | if self.hideWithoutStandby and self.db and check ~= false then 415 | self.db.profile.hidden = true 416 | end 417 | if not self.hideWithoutStandby then 418 | if self.db and not self.overrideTooltip and not self.cannotDetachTooltip and self:IsTooltipDetached() and self.db.profile.detachedTooltip and self.db.profile.detachedTooltip.detached then 419 | self:ReattachTooltip() 420 | self.db.profile.detachedTooltip.detached = true 421 | end 422 | if self.IsActive and self:IsActive() and self.ToggleActive and (not CheckFuBar() or not FuBar:IsChangingProfile()) then 423 | self:ToggleActive() 424 | end 425 | end 426 | if self.panel then 427 | self.panel:RemovePlugin(self) 428 | end 429 | self.frame:Hide() 430 | if self.minimapFrame then 431 | self.minimapFrame:Hide() 432 | end 433 | 434 | if Dewdrop:IsOpen(self.frame) or (self.minimapFrame and Dewdrop:IsOpen(self.minimapFrame)) then 435 | Dewdrop:Close() 436 | end 437 | end 438 | 439 | function FuBarPlugin:SetIcon(path) 440 | if not path then 441 | return 442 | end 443 | FuBarPlugin:argCheck(path, 2, "string", "boolean") 444 | FuBarPlugin:assert(self.hasIcon, "Cannot set icon unless self.hasIcon is set. (" .. self:GetTitle() .. ")") 445 | if not self.iconFrame then 446 | return 447 | end 448 | if type(path) ~= "string" then 449 | path = format("Interface\\AddOns\\%s\\icon", self.folderName) 450 | elseif not string.find(path, '^Interface[\\/]') then 451 | path = format("Interface\\AddOns\\%s\\%s", self.folderName, path) 452 | end 453 | if string.sub(path, 1, 16) == "Interface\\Icons\\" then 454 | self.iconFrame:SetTexCoord(0.05, 0.95, 0.05, 0.95) 455 | else 456 | self.iconFrame:SetTexCoord(0, 1, 0, 1) 457 | end 458 | self.iconFrame:SetTexture(path) 459 | if self.minimapIcon then 460 | if string.sub(path, 1, 16) == "Interface\\Icons\\" then 461 | self.minimapIcon:SetTexCoord(0.05, 0.95, 0.05, 0.95) 462 | else 463 | self.minimapIcon:SetTexCoord(0, 1, 0, 1) 464 | end 465 | self.minimapIcon:SetTexture(path) 466 | end 467 | end 468 | 469 | function FuBarPlugin:GetIcon() 470 | if self.hasIcon then 471 | return self.iconFrame:GetTexture() 472 | end 473 | end 474 | 475 | function FuBarPlugin:CheckWidth(force) 476 | FuBarPlugin:argCheck(force, 2, "boolean", "nil") 477 | if (self.iconFrame and self.iconFrame:IsShown()) or (self.textFrame and self.textFrame:IsShown()) then 478 | if (self.db and self.db.profile and not self:IsIconShown()) or not self.hasIcon then 479 | self.iconFrame:SetWidth(epsilon) 480 | end 481 | local width 482 | if not self.hasNoText then 483 | self.textFrame:SetHeight(0) 484 | self.textFrame:SetWidth(500) 485 | width = self.textFrame:GetStringWidth() + 1 486 | self.textFrame:SetWidth(width) 487 | self.textFrame:SetHeight(self.textFrame:GetHeight()) 488 | end 489 | if self.hasNoText or not self.textFrame:IsShown() then 490 | self.frame:SetWidth(self.iconFrame:GetWidth()) 491 | if self.panel and self.panel:GetPluginSide(self) == "CENTER" then 492 | self.panel:UpdateCenteredPosition() 493 | end 494 | elseif force or not self.textWidth or self.textWidth < width or self.textWidth - 8 > width then 495 | self.textWidth = width 496 | self.textFrame:SetWidth(width) 497 | if self.iconFrame and self.iconFrame:IsShown() then 498 | self.frame:SetWidth(width + self.iconFrame:GetWidth()) 499 | else 500 | self.frame:SetWidth(width) 501 | end 502 | if self.panel and self.panel:GetPluginSide(self) == "CENTER" then 503 | self.panel:UpdateCenteredPosition() 504 | end 505 | end 506 | end 507 | end 508 | 509 | function FuBarPlugin:SetText(text) 510 | if not self.textFrame then 511 | return 512 | end 513 | FuBarPlugin:assert(not self.hasNoText, "Cannot set text if self.hasNoText has been set. (" .. self:GetTitle() .. ")") 514 | FuBarPlugin:argCheck(text, 2, "string", "number") 515 | if text == "" then 516 | if self.hasIcon then 517 | self:ShowIcon() 518 | else 519 | text = self:GetTitle() 520 | end 521 | end 522 | if not self:IsTextColored() then 523 | text = string.gsub(string.gsub(text, "|c%x%x%x%x%x%x%x%x", ""), "|r", "") 524 | end 525 | self.textFrame:SetText(text) 526 | self:CheckWidth() 527 | end 528 | 529 | function FuBarPlugin:GetText() 530 | FuBarPlugin:assert(self.textFrame, "Cannot get text without a self.textFrame (" .. self:GetTitle() .. ")") 531 | if not self.hasNoText then 532 | return self.textFrame:GetText() or "" 533 | end 534 | end 535 | 536 | function FuBarPlugin:IsIconShown() 537 | if not self.hasIcon then 538 | return false 539 | elseif self.hasNoText then 540 | return true 541 | elseif not self.db then 542 | return true 543 | elseif self.db.profile.showIcon == nil then 544 | return true 545 | else 546 | return (self.db.profile.showIcon == 1 or self.db.profile.showIcon == true) and true or false 547 | end 548 | end 549 | 550 | function FuBarPlugin:ToggleIconShown() 551 | FuBarPlugin:assert(self.iconFrame, "Cannot toggle icon without a self.iconFrame (" .. self:GetTitle() .. ")") 552 | FuBarPlugin:assert(self.hasIcon, "Cannot show icon unless self.hasIcon is set. (" .. self:GetTitle() .. ")") 553 | FuBarPlugin:assert(not self.hasNoText, "Cannot hide icon if self.hasNoText is set. (" .. self:GetTitle() .. ")") 554 | FuBarPlugin:assert(self.textFrame, "Cannot hide icon if self.textFrame is not set. (" .. self:GetTitle() .. ")") 555 | FuBarPlugin:assert(self.iconFrame, "Cannot hide icon if self.iconFrame is not set. (" .. self:GetTitle() .. ")") 556 | FuBarPlugin:assert(self.db, "Cannot hide icon if self.db is not available. (" .. self:GetTitle() .. ")") 557 | local value = not self:IsIconShown() 558 | self.db.profile.showIcon = value 559 | if value then 560 | if not self:IsTextShown() and self.textFrame:IsShown() and self.textFrame:GetText() == self:GetTitle() then 561 | self.textFrame:Hide() 562 | self.textFrame:SetText("") 563 | end 564 | self.iconFrame:Show() 565 | self.iconFrame:SetWidth(self.iconFrame:GetHeight()) 566 | else 567 | if not self.textFrame:IsShown() or not self.textFrame:GetText() then 568 | self.textFrame:Show() 569 | self.textFrame:SetText(self:GetTitle()) 570 | end 571 | self.iconFrame:Hide() 572 | self.iconFrame:SetWidth(epsilon) 573 | end 574 | self:CheckWidth(true) 575 | return value 576 | end 577 | 578 | function FuBarPlugin:ShowIcon() 579 | if not self:IsIconShown() then 580 | self:ToggleIconShown() 581 | end 582 | end 583 | 584 | function FuBarPlugin:HideIcon() 585 | if self:IsIconShown() then 586 | self:ToggleIconShown() 587 | end 588 | end 589 | 590 | function FuBarPlugin:IsTextShown() 591 | if self.hasNoText then 592 | return false 593 | elseif not self.hasIcon then 594 | return true 595 | elseif not self.db then 596 | return true 597 | elseif self.db.profile.showText == nil then 598 | return true 599 | else 600 | return (self.db.profile.showText == 1 or self.db.profile.showText == true) and true or false 601 | end 602 | end 603 | 604 | function FuBarPlugin:ToggleTextShown() 605 | FuBarPlugin:assert(not self.cannotHideText, "Cannot hide text unless self.cannotHideText is unset. (" .. self:GetTitle() .. ")") 606 | FuBarPlugin:assert(self.hasIcon, "Cannot show text unless self.hasIcon is set. (" .. self:GetTitle() .. ")") 607 | FuBarPlugin:assert(not self.hasNoText, "Cannot hide text if self.hasNoText is set. (" .. self:GetTitle() .. ")") 608 | FuBarPlugin:assert(self.textFrame, "Cannot hide text if self.textFrame is not set. (" .. self:GetTitle() .. ")") 609 | FuBarPlugin:assert(self.iconFrame, "Cannot hide text if self.iconFrame is not set. (" .. self:GetTitle() .. ")") 610 | FuBarPlugin:assert(self.db, "Cannot hide text if self.db is not available. (" .. self:GetTitle() .. ")") 611 | local value = not self:IsTextShown() 612 | self.db.profile.showText = value 613 | if value then 614 | self.textFrame:Show() 615 | self:UpdateText() 616 | else 617 | self.textFrame:SetText("") 618 | self.textFrame:SetWidth(epsilon) 619 | self.textFrame:Hide() 620 | if not self:IsIconShown() then 621 | DropDownList1:Hide() 622 | end 623 | self:ShowIcon() 624 | end 625 | self:CheckWidth(true) 626 | return value 627 | end 628 | 629 | function FuBarPlugin:ShowText() 630 | if not self:IsTextShown() then 631 | self:ToggleTextShown() 632 | end 633 | end 634 | 635 | function FuBarPlugin:HideText() 636 | if self:IsTextShown() then 637 | self:ToggleTextShown() 638 | end 639 | end 640 | 641 | function FuBarPlugin:IsTooltipDetached() 642 | FuBarPlugin.RegisterTablet(self) 643 | return not Tablet:IsAttached(self.frame) 644 | end 645 | 646 | function FuBarPlugin:ToggleTooltipDetached() 647 | FuBarPlugin.RegisterTablet(self) 648 | if self:IsTooltipDetached() then 649 | Tablet:Attach(self.frame) 650 | else 651 | Tablet:Detach(self.frame) 652 | end 653 | end 654 | 655 | function FuBarPlugin:DetachTooltip() 656 | FuBarPlugin.RegisterTablet(self) 657 | Tablet:Detach(self.frame) 658 | end 659 | 660 | function FuBarPlugin:ReattachTooltip() 661 | FuBarPlugin.RegisterTablet(self) 662 | Tablet:Attach(self.frame) 663 | end 664 | 665 | function FuBarPlugin:GetDefaultPosition() 666 | return self.defaultPosition or "LEFT" 667 | end 668 | 669 | local function IsCorrectPanel(panel) 670 | if type(panel) ~= "table" then 671 | return false 672 | elseif type(panel.AddPlugin) ~= "function" then 673 | return false 674 | elseif type(panel.RemovePlugin) ~= "function" then 675 | return false 676 | elseif type(panel.GetNumPlugins) ~= "function" then 677 | return false 678 | elseif type(panel:GetNumPlugins()) ~= "number" then 679 | return false 680 | elseif type(panel.GetPlugin) ~= "function" then 681 | return false 682 | elseif type(panel.HasPlugin) ~= "function" then 683 | return false 684 | elseif type(panel.GetPluginSide) ~= "function" then 685 | return false 686 | end 687 | return true 688 | end 689 | 690 | function FuBarPlugin:SetPanel(panel) 691 | if panel then 692 | FuBarPlugin:assert(IsCorrectPanel(panel), "Bad argument #2 to `SetPanel'. Panel does not have the correct API.") 693 | end 694 | self.panel = panel 695 | end 696 | 697 | function FuBarPlugin:SetFontSize(size) 698 | FuBarPlugin:assert(not self.userDefinedFrame, "You must provide a SetFontSize(size) method if you provide your own frame.") 699 | if self.hasIcon then 700 | FuBarPlugin:assert(self.iconFrame, (self.name and self.name .. ": " or "") .. "No iconFrame found") 701 | self.iconFrame:SetWidth(size + 3) 702 | self.iconFrame:SetHeight(size + 3) 703 | end 704 | if not self.hasNoText then 705 | FuBarPlugin:assert(self.textFrame, (self.name and self.name .. ": " or "") .. "No textFrame found") 706 | local font, _, flags = self.textFrame:GetFont() 707 | self.textFrame:SetFont(font, size, flags) 708 | end 709 | self:CheckWidth() 710 | end 711 | 712 | function FuBarPlugin:IsLoadOnDemand() 713 | return IsAddOnLoadOnDemand(self.folderName) 714 | end 715 | 716 | function FuBarPlugin:IsDisabled() 717 | return self.IsActive and not self:IsActive() or false 718 | end 719 | 720 | function FuBarPlugin:OnInstanceInit(target) 721 | if not AceEvent then 722 | self:error(MAJOR_VERSION .. " requires AceEvent-2.0.") 723 | elseif not Tablet then 724 | self:error(MAJOR_VERSION .. " requires Tablet-2.0.") 725 | elseif not Dewdrop then 726 | self:error(MAJOR_VERSION .. " requires Dewdrop-2.0.") 727 | end 728 | self.registry[target] = true 729 | 730 | local _,_,folderName = string.find(debugstack(6, 1, 0), "\\AddOns\\(.*)\\") 731 | target.folderName = folderName 732 | self.folderNames[target] = folderName 733 | end 734 | 735 | function FuBarPlugin:CreateBasicPluginFrame(name) 736 | local frame = CreateFrame("Button", name, UIParent) 737 | frame:SetFrameStrata("HIGH") 738 | frame:SetFrameLevel(7) 739 | frame:EnableMouse(true) 740 | frame:EnableMouseWheel(true) 741 | frame:SetMovable(true) 742 | frame:SetWidth(150) 743 | frame:SetHeight(24) 744 | frame:SetPoint("CENTER", UIParent, "CENTER") 745 | 746 | frame:SetScript("OnClick", function() 747 | if type(self.OnClick) == "function" then 748 | self:OnClick(arg1) 749 | end 750 | end) 751 | frame:SetScript("OnDoubleClick", function() 752 | if type(self.OnDoubleClick) == "function" then 753 | self:OnDoubleClick(arg1) 754 | end 755 | end) 756 | frame:SetScript("OnMouseDown", function() 757 | if arg1 == "RightButton" and not IsShiftKeyDown() and not IsControlKeyDown() and not IsAltKeyDown() then 758 | self:OpenMenu() 759 | return 760 | else 761 | HideDropDownMenu(1) 762 | if type(self.OnMouseDown) == "function" then 763 | self:OnMouseDown(arg1) 764 | end 765 | end 766 | end) 767 | frame:SetScript("OnMouseUp", function() 768 | if type(self.OnMouseUp) == "function" then 769 | self:OnMouseUp(arg1) 770 | end 771 | end) 772 | frame:SetScript("OnReceiveDrag", function() 773 | if type(self.OnReceiveDrag) == "function" then 774 | self:OnReceiveDrag() 775 | end 776 | end) 777 | return frame 778 | end 779 | 780 | function FuBarPlugin:CreatePluginChildFrame(frameType, name, parent) 781 | FuBarPlugin:assert(self.frame, "You must have self.frame declared in order to add child frames") 782 | FuBarPlugin:argCheck(frameType, 1, "string") 783 | local child = CreateFrame(frameType, name, parent) 784 | if parent then 785 | child:SetFrameLevel(parent:GetFrameLevel() + 2) 786 | end 787 | child:SetScript("OnEnter", function() 788 | if self.frame:GetScript("OnEnter") then 789 | self.frame:GetScript("OnEnter")() 790 | end 791 | end) 792 | child:SetScript("OnLeave", function() 793 | if self.frame:GetScript("OnLeave") then 794 | self.frame:GetScript("OnLeave")() 795 | end 796 | end) 797 | if child:HasScript("OnClick") then 798 | child:SetScript("OnClick", function() 799 | if self.frame:HasScript("OnClick") and self.frame:GetScript("OnClick") then 800 | self.frame:GetScript("OnClick")() 801 | end 802 | end) 803 | end 804 | if child:HasScript("OnDoubleClick") then 805 | child:SetScript("OnDoubleClick", function() 806 | if self.frame:HasScript("OnDoubleClick") and self.frame:GetScript("OnDoubleClick") then 807 | self.frame:GetScript("OnDoubleClick")() 808 | end 809 | end) 810 | end 811 | child:SetScript("OnMouseDown", function() 812 | if self.frame:GetScript("OnMouseDown") then 813 | self.frame:GetScript("OnMouseDown")() 814 | end 815 | end) 816 | child:SetScript("OnMouseUp", function() 817 | if self.frame:GetScript("OnMouseUp") then 818 | self.frame:GetScript("OnMouseUp")() 819 | end 820 | end) 821 | child:SetScript("OnReceiveDrag", function() 822 | if self.frame:GetScript("OnReceiveDrag") then 823 | self.frame:GetScript("OnReceiveDrag")() 824 | end 825 | end) 826 | return child 827 | end 828 | 829 | function FuBarPlugin:OpenMenu(frame) 830 | if not frame then 831 | frame = self:GetFrame() 832 | end 833 | if not frame or not self:GetFrame() or Dewdrop:IsOpen(frame) then 834 | Dewdrop:Close() 835 | return 836 | end 837 | Tablet:Close() 838 | 839 | if not Dewdrop:IsRegistered(self:GetFrame()) then 840 | if type(self.OnMenuRequest) == "table" and (not self.OnMenuRequest.handler or self.OnMenuRequest.handler == self) and self.OnMenuRequest.type == "group" then 841 | Dewdrop:InjectAceOptionsTable(self, self.OnMenuRequest) 842 | if self.OnMenuRequest.args and CheckFuBar() and not self.independentProfile then 843 | self.OnMenuRequest.args.profile = nil 844 | end 845 | end 846 | Dewdrop:Register(self:GetFrame(), 847 | 'children', type(self.OnMenuRequest) == "table" and self.OnMenuRequest or function(level, value, valueN_1, valueN_2, valueN_3, valueN_4) 848 | if level == 1 then 849 | Dewdrop:AddLine( 850 | 'text', self:GetTitle(), 851 | 'isTitle', true 852 | ) 853 | end 854 | 855 | if level == 1 then 856 | if self.OnMenuRequest then 857 | self:OnMenuRequest(level, value, false, valueN_1, valueN_2, valueN_3, valueN_4) 858 | end 859 | 860 | if not self.overrideMenu then 861 | if self.MenuSettings then 862 | Dewdrop:AddLine() 863 | end 864 | self:AddImpliedMenuOptions() 865 | end 866 | else 867 | if not self.overrideMenu and self:AddImpliedMenuOptions() then 868 | else 869 | if self.OnMenuRequest then 870 | self:OnMenuRequest(level, value, false, valueN_1, valueN_2, valueN_3, valueN_4) 871 | end 872 | end 873 | end 874 | if level == 1 then 875 | Dewdrop:AddLine( 876 | 'text', CLOSE, 877 | 'func', Dewdrop.Close, 878 | 'arg1', Dewdrop 879 | ) 880 | end 881 | end, 882 | 'point', function(frame) 883 | local x, y = frame:GetCenter() 884 | local leftRight 885 | if x < GetScreenWidth() / 2 then 886 | leftRight = "LEFT" 887 | else 888 | leftRight = "RIGHT" 889 | end 890 | if y < GetScreenHeight() / 2 then 891 | return "BOTTOM" .. leftRight, "TOP" .. leftRight 892 | else 893 | return "TOP" .. leftRight, "BOTTOM" .. leftRight 894 | end 895 | end, 896 | 'dontHook', true 897 | ) 898 | end 899 | if frame == self:GetFrame() then 900 | Dewdrop:Open(self:GetFrame()) 901 | else 902 | Dewdrop:Open(frame, self:GetFrame()) 903 | end 904 | end 905 | 906 | local impliedMenuOptions 907 | function FuBarPlugin:AddImpliedMenuOptions(level) 908 | FuBarPlugin:argCheck(level, 2, "number", "nil") 909 | if not impliedMenuOptions then 910 | impliedMenuOptions = {} 911 | end 912 | if not impliedMenuOptions[self] then 913 | impliedMenuOptions[self] = { type = 'group', args = {} } 914 | Dewdrop:InjectAceOptionsTable(self, impliedMenuOptions[self]) 915 | if impliedMenuOptions[self].args and CheckFuBar() and not self.independentProfile then 916 | impliedMenuOptions[self].args.profile = nil 917 | end 918 | end 919 | return Dewdrop:FeedAceOptionsTable(impliedMenuOptions[self], level and level - 1) 920 | end 921 | 922 | function FuBarPlugin.OnEmbedInitialize(FuBarPlugin, self) 923 | if not self.frame then 924 | local name = "FuBarPlugin" .. self:GetTitle() .. "Frame" 925 | local frame = _G[name] 926 | if not frame or not _G[name .. "Text"] or not _G[name .. "Icon"] then 927 | frame = self:CreateBasicPluginFrame(name) 928 | 929 | local icon = frame:CreateTexture(name .. "Icon", "ARTWORK") 930 | icon:SetWidth(16) 931 | icon:SetHeight(16) 932 | icon:SetPoint("LEFT", frame, "LEFT") 933 | 934 | local text = frame:CreateFontString(name .. "Text", "ARTWORK") 935 | text:SetWidth(134) 936 | text:SetHeight(24) 937 | text:SetPoint("LEFT", icon, "RIGHT", 0, 1) 938 | text:SetFontObject(GameFontNormal) 939 | end 940 | self.frame = frame 941 | self.textFrame = _G[name .. "Text"] 942 | self.iconFrame = _G[name .. "Icon"] 943 | else 944 | self.userDefinedFrame = true 945 | end 946 | 947 | self.frame.plugin = self 948 | self.frame:SetParent(UIParent) 949 | self.frame:SetPoint("RIGHT", UIParent, "LEFT", -5, 0) 950 | self.frame:Hide() 951 | 952 | if self.hasIcon then 953 | self:SetIcon(self.hasIcon) 954 | end 955 | 956 | if CheckFuBar() then 957 | FuBar:RegisterPlugin(self) 958 | end 959 | end 960 | 961 | local CheckShow = function(self, panelId) 962 | if not self.frame:IsShown() and (not self.minimapFrame or not self.minimapFrame:IsShown()) then 963 | self:Show(panelId) 964 | Dewdrop:Refresh(2) 965 | end 966 | end 967 | 968 | local recheckPlugins 969 | function FuBarPlugin.OnEmbedEnable(FuBarPlugin, self) 970 | if not self.userDefinedFrame then 971 | if self:IsIconShown() then 972 | self.iconFrame:Show() 973 | else 974 | self.iconFrame:Hide() 975 | end 976 | end 977 | self:CheckWidth(true) 978 | 979 | if not self.hideWithoutStandby or not self.db.profile.hidden then 980 | if FuBarPlugin.enabledPlugins[self] then 981 | CheckShow(self, self.panelIdTmp) 982 | else 983 | FuBarPlugin:ScheduleEvent(CheckShow, 0, self, self.panelIdTmp) 984 | end 985 | end 986 | FuBarPlugin.enabledPlugins[self] = true 987 | 988 | if not self.overrideTooltip and not self.cannotDetachTooltip and self.db and self.db.profile.detachedTooltip and self.db.profile.detachedTooltip.detached then 989 | FuBarPlugin:ScheduleEvent(self.DetachTooltip, 0, self) 990 | end 991 | 992 | if self:IsLoadOnDemand() and CheckFuBar() then 993 | if not FuBar.db.profile.loadOnDemand then 994 | FuBar.db.profile.loadOnDemand = {} 995 | end 996 | if not FuBar.db.profile.loadOnDemand[self.folderName] then 997 | FuBar.db.profile.loadOnDemand[self.folderName] = {} 998 | end 999 | FuBar.db.profile.loadOnDemand[self.folderName].disabled = nil 1000 | end 1001 | 1002 | if CheckFuBar() and AceLibrary:HasInstance("AceConsole-2.0") then 1003 | if not recheckPlugins then 1004 | local AceConsole = AceLibrary("AceConsole-2.0") 1005 | local AceOO = AceLibrary("AceOO-2.0") 1006 | function recheckPlugins() 1007 | for k,v in pairs(AceConsole.registry) do 1008 | if type(v) == "table" and v.args and AceOO.inherits(v.handler, FuBarPlugin) and not v.independentProfile then 1009 | v.args.profile = nil 1010 | end 1011 | end 1012 | end 1013 | end 1014 | FuBarPlugin:ScheduleEvent(recheckPlugins, 0) 1015 | end 1016 | end 1017 | 1018 | function FuBarPlugin.OnEmbedDisable(FuBarPlugin, self) 1019 | self:Hide(false) 1020 | 1021 | if self:IsLoadOnDemand() and CheckFuBar() then 1022 | if not FuBar.db.profile.loadOnDemand then 1023 | FuBar.db.profile.loadOnDemand = {} 1024 | end 1025 | if not FuBar.db.profile.loadOnDemand[self.folderName] then 1026 | FuBar.db.profile.loadOnDemand[self.folderName] = {} 1027 | end 1028 | FuBar.db.profile.loadOnDemand[self.folderName].disabled = true 1029 | end 1030 | end 1031 | 1032 | function FuBarPlugin.OnEmbedProfileEnable(FuBarPlugin, self) 1033 | self:Update() 1034 | if self.db and self.db.profile then 1035 | if not self.db.profile.detachedTooltip then 1036 | self.db.profile.detachedTooltip = {} 1037 | end 1038 | if Tablet.registry[self.frame] then 1039 | Tablet:UpdateDetachedData(self.frame, self.db.profile.detachedTooltip) 1040 | else 1041 | FuBarPlugin.RegisterTablet(self) 1042 | end 1043 | end 1044 | end 1045 | 1046 | function FuBarPlugin.GetAceOptionsDataTable(FuBarPlugin, self) 1047 | return { 1048 | icon = { 1049 | type = "toggle", 1050 | name = SHOW_ICON, 1051 | desc = SHOW_ICON_DESC, 1052 | set = "ToggleIconShown", 1053 | get = "IsIconShown", 1054 | hidden = function() 1055 | return not self.hasIcon or self.hasNoText or self:IsDisabled() or self:IsMinimapAttached() or not self.db 1056 | end, 1057 | order = -13.7, 1058 | handler = self, 1059 | }, 1060 | text = { 1061 | type = "toggle", 1062 | name = SHOW_TEXT, 1063 | desc = SHOW_TEXT_DESC, 1064 | set = "ToggleTextShown", 1065 | get = "IsTextShown", 1066 | hidden = function() 1067 | return self.cannotHideText or not self.hasIcon or self.hasNoText or self:IsDisabled() or self:IsMinimapAttached() or not self.db 1068 | end, 1069 | order = -13.6, 1070 | handler = self, 1071 | }, 1072 | colorText = { 1073 | type = "toggle", 1074 | name = SHOW_COLORED_TEXT, 1075 | desc = SHOW_COLORED_TEXT_DESC, 1076 | set = "ToggleTextColored", 1077 | get = "IsTextColored", 1078 | hidden = function() 1079 | return self.userDefinedFrame or self.hasNoText or self.hasNoColor or self:IsDisabled() or self:IsMinimapAttached() or not self.db 1080 | end, 1081 | order = -13.5, 1082 | handler = self, 1083 | }, 1084 | detachTooltip = { 1085 | type = "toggle", 1086 | name = DETACH_TOOLTIP, 1087 | desc = DETACH_TOOLTIP_DESC, 1088 | get = "IsTooltipDetached", 1089 | set = "ToggleTooltipDetached", 1090 | hidden = function() 1091 | return self.overrideTooltip or self.cannotDetachTooltip or self:IsDisabled() 1092 | end, 1093 | order = -13.4, 1094 | handler = self, 1095 | }, 1096 | lockTooltip = { 1097 | type = "toggle", 1098 | name = LOCK_TOOLTIP, 1099 | desc = LOCK_TOOLTIP_DESC, 1100 | get = function() 1101 | return Tablet:IsLocked(self.frame) 1102 | end, 1103 | set = function() 1104 | return Tablet:ToggleLocked(self.frame) 1105 | end, 1106 | disabled = function() 1107 | return not self:IsTooltipDetached() 1108 | end, 1109 | hidden = function() 1110 | return self.overrideTooltip or self.cannotDetachTooltip or self:IsDisabled() 1111 | end, 1112 | order = -13.3, 1113 | handler = self, 1114 | }, 1115 | position = { 1116 | type = "text", 1117 | name = POSITION, 1118 | desc = POSITION_DESC, 1119 | validate = { 1120 | LEFT = POSITION_LEFT, 1121 | CENTER = POSITION_CENTER, 1122 | RIGHT = POSITION_RIGHT 1123 | }, 1124 | get = function() 1125 | return self.panel and self.panel:GetPluginSide(self) 1126 | end, 1127 | set = function(value) 1128 | if self.panel then 1129 | self.panel:SetPluginSide(self, value) 1130 | end 1131 | end, 1132 | hidden = function() 1133 | return self:IsMinimapAttached() or self:IsDisabled() or not self.panel 1134 | end, 1135 | order = -13.2, 1136 | handler = self, 1137 | }, 1138 | minimapAttach = { 1139 | type = "toggle", 1140 | name = ATTACH_TO_MINIMAP, 1141 | desc = ATTACH_TO_MINIMAP_DESC, 1142 | get = "IsMinimapAttached", 1143 | set = "ToggleMinimapAttached", 1144 | hidden = function() 1145 | return (self.cannotAttachToMinimap and not self:IsMinimapAttached()) or not CheckFuBar() or self:IsDisabled() 1146 | end, 1147 | order = -13.1, 1148 | handler = self, 1149 | }, 1150 | hide = { 1151 | type = "toggle", 1152 | cmdName = HIDE_FUBAR_PLUGIN_CMD, 1153 | guiName = HIDE_FUBAR_PLUGIN, 1154 | desc = HIDE_FUBAR_PLUGIN_DESC, 1155 | get = function() 1156 | return not self.frame:IsShown() and (not self.minimapFrame or not self.minimapFrame:IsShown()) 1157 | end, 1158 | set = function() 1159 | if not self.frame:IsShown() and (not self.minimapFrame or not self.minimapFrame:IsShown()) then 1160 | self:Show() 1161 | else 1162 | self:Hide() 1163 | end 1164 | end, 1165 | hidden = function() 1166 | return not self.hideWithoutStandby or self:IsDisabled() 1167 | end, 1168 | order = -13, 1169 | handler = self, 1170 | }, 1171 | } 1172 | end 1173 | 1174 | local function activate(self, oldLib, oldDeactivate) 1175 | FuBarPlugin = self 1176 | 1177 | if oldLib then 1178 | self.registry = oldLib.registry 1179 | self.folderNames = oldLib.folderNames 1180 | self.enabledPlugins = oldLib.enabledPlugins 1181 | end 1182 | 1183 | if not self.registry then 1184 | self.registry = {} 1185 | end 1186 | if not self.folderNames then 1187 | self.folderNames = {} 1188 | end 1189 | if not self.enabledPlugins then 1190 | self.enabledPlugins = {} 1191 | end 1192 | 1193 | FuBarPlugin.activate(self, oldLib, oldDeactivate) 1194 | 1195 | if oldDeactivate then 1196 | oldDeactivate(oldLib) 1197 | end 1198 | end 1199 | 1200 | local function external(self, major, instance) 1201 | if major == "AceEvent-2.0" then 1202 | AceEvent = instance 1203 | 1204 | AceEvent:embed(self) 1205 | elseif major == "Tablet-2.0" then 1206 | Tablet = instance 1207 | elseif major == "Dewdrop-2.0" then 1208 | Dewdrop = instance 1209 | end 1210 | end 1211 | 1212 | AceLibrary:Register(FuBarPlugin, MAJOR_VERSION, MINOR_VERSION, activate, nil, external) 1213 | 1214 | local MinimapContainer = {} 1215 | 1216 | local IsMinimapSquare 1217 | do 1218 | local value 1219 | function IsMinimapSquare() 1220 | if value == nil then 1221 | if not AceEvent or not AceEvent:IsFullyInitialized() then 1222 | return IsAddOnLoaded("CornerMinimap") or IsAddOnLoaded("SquareMinimap") or IsAddOnLoaded("Squeenix") 1223 | else 1224 | value = IsAddOnLoaded("CornerMinimap") or IsAddOnLoaded("SquareMinimap") or IsAddOnLoaded("Squeenix") and true or false 1225 | end 1226 | end 1227 | return value 1228 | end 1229 | end 1230 | 1231 | function MinimapContainer:AddPlugin(plugin) 1232 | if CheckFuBar() and FuBar:IsChangingProfile() then 1233 | return 1234 | end 1235 | if plugin.panel ~= nil then 1236 | plugin.panel:RemovePlugin(plugin) 1237 | end 1238 | plugin.panel = self 1239 | if not plugin.minimapFrame then 1240 | local frame = CreateFrame("Button", plugin.frame:GetName() .. "MinimapButton", Minimap) 1241 | plugin.minimapFrame = frame 1242 | AceLibrary(MAJOR_VERSION).RegisterTablet(plugin) 1243 | Tablet:Register(frame, plugin.frame) 1244 | frame.plugin = plugin 1245 | frame:SetWidth(31) 1246 | frame:SetHeight(31) 1247 | frame:SetFrameStrata("BACKGROUND") 1248 | frame:SetFrameLevel(4) 1249 | frame:SetHighlightTexture("Interface\\Minimap\\UI-Minimap-ZoomButton-Highlight") 1250 | local icon = frame:CreateTexture(frame:GetName() .. "Icon", "BACKGROUND") 1251 | plugin.minimapIcon = icon 1252 | local path = plugin:GetIcon() or (plugin.iconFrame and plugin.iconFrame:GetTexture()) or "Interface\\Icons\\INV_Misc_QuestionMark" 1253 | icon:SetTexture(path) 1254 | if string.sub(path, 1, 16) == "Interface\\Icons\\" then 1255 | icon:SetTexCoord(0.05, 0.95, 0.05, 0.95) 1256 | else 1257 | icon:SetTexCoord(0, 1, 0, 1) 1258 | end 1259 | icon:SetWidth(20) 1260 | icon:SetHeight(20) 1261 | icon:SetPoint("TOPLEFT", frame, "TOPLEFT", 7, -5) 1262 | local overlay = frame:CreateTexture(frame:GetName() .. "Overlay","OVERLAY") 1263 | overlay:SetTexture("Interface\\Minimap\\MiniMap-TrackingBorder") 1264 | overlay:SetWidth(53) 1265 | overlay:SetHeight(53) 1266 | overlay:SetPoint("TOPLEFT",frame,"TOPLEFT") 1267 | frame:EnableMouse(true) 1268 | frame:RegisterForClicks("LeftButtonUp") 1269 | frame.plugin = plugin 1270 | frame:SetScript("OnClick", function() 1271 | if type(plugin.OnClick) == "function" then 1272 | if not this.dragged then 1273 | plugin:OnClick(arg1) 1274 | end 1275 | end 1276 | end) 1277 | frame:SetScript("OnDoubleClick", function() 1278 | if type(plugin.OnDoubleClick) == "function" then 1279 | plugin:OnDoubleClick(arg1) 1280 | end 1281 | end) 1282 | frame:SetScript("OnReceiveDrag", function() 1283 | if type(plugin.OnReceiveDrag) == "function" then 1284 | if not this.dragged then 1285 | plugin:OnReceiveDrag() 1286 | end 1287 | end 1288 | end) 1289 | frame:SetScript("OnMouseDown", function() 1290 | this.dragged = false 1291 | if arg1 == "LeftButton" and not IsShiftKeyDown() and not IsControlKeyDown() and not IsAltKeyDown() then 1292 | HideDropDownMenu(1) 1293 | if type(plugin.OnMouseDown) == "function" then 1294 | plugin:OnMouseDown(arg1) 1295 | end 1296 | elseif arg1 == "RightButton" and not IsShiftKeyDown() and not IsControlKeyDown() and not IsAltKeyDown() then 1297 | plugin:OpenMenu(frame) 1298 | else 1299 | HideDropDownMenu(1) 1300 | if type(plugin.OnMouseDown) == "function" then 1301 | plugin:OnMouseDown(arg1) 1302 | end 1303 | end 1304 | if plugin.OnClick or plugin.OnMouseDown or plugin.OnMouseUp or plugin.OnDoubleClick then 1305 | if string.sub(this.plugin.minimapIcon:GetTexture(), 1, 16) == "Interface\\Icons\\" then 1306 | plugin.minimapIcon:SetTexCoord(0.14, 0.86, 0.14, 0.86) 1307 | else 1308 | plugin.minimapIcon:SetTexCoord(0.1, 0.9, 0.1, 0.9) 1309 | end 1310 | end 1311 | end) 1312 | frame:SetScript("OnMouseUp", function() 1313 | if not this.dragged and type(plugin.OnMouseUp) == "function" then 1314 | plugin:OnMouseUp(arg1) 1315 | end 1316 | if string.sub(this.plugin.minimapIcon:GetTexture(), 1, 16) == "Interface\\Icons\\" then 1317 | plugin.minimapIcon:SetTexCoord(0.05, 0.95, 0.05, 0.95) 1318 | else 1319 | plugin.minimapIcon:SetTexCoord(0, 1, 0, 1) 1320 | end 1321 | end) 1322 | frame:RegisterForDrag("LeftButton") 1323 | frame:SetScript("OnDragStart", self.OnDragStart) 1324 | frame:SetScript("OnDragStop", self.OnDragStop) 1325 | end 1326 | plugin.frame:Hide() 1327 | plugin.minimapFrame:Show() 1328 | self:ReadjustLocation(plugin) 1329 | table.insert(self.plugins, plugin) 1330 | local exists = false 1331 | return true 1332 | end 1333 | 1334 | function MinimapContainer:RemovePlugin(index) 1335 | if CheckFuBar() and FuBar:IsChangingProfile() then 1336 | return 1337 | end 1338 | if type(index) == "table" then 1339 | index = self:IndexOfPlugin(index) 1340 | if not index then 1341 | return 1342 | end 1343 | end 1344 | local t = self.plugins 1345 | local plugin = t[index] 1346 | assert(plugin.panel == self, "Plugin has improper panel field") 1347 | plugin:SetPanel(nil) 1348 | table.remove(t, index) 1349 | return true 1350 | end 1351 | 1352 | function MinimapContainer:ReadjustLocation(plugin) 1353 | local frame = plugin.minimapFrame 1354 | if plugin.db and plugin.db.profile.minimapPositionWild then 1355 | frame:SetPoint("CENTER", UIParent, "BOTTOMLEFT", plugin.db.profile.minimapPositionX, plugin.db.profile.minimapPositionY) 1356 | elseif not plugin.db and plugin.minimapPositionWild then 1357 | frame:SetPoint("CENTER", UIParent, "BOTTOMLEFT", plugin.minimapPositionX, plugin.minimapPositionY) 1358 | else 1359 | local position 1360 | if plugin.db then 1361 | position = plugin.db.profile.minimapPosition or plugin.defaultMinimapPosition or math.random(1, 360) 1362 | else 1363 | position = plugin.minimapPosition or plugin.defaultMinimapPosition or math.random(1, 360) 1364 | end 1365 | local angle = math.rad(position or 0) 1366 | local x,y 1367 | if not IsMinimapSquare() then 1368 | x = math.cos(angle) * 80 1369 | y = math.sin(angle) * 80 1370 | else 1371 | x = 110 * math.cos(angle) 1372 | y = 110 * math.sin(angle) 1373 | x = math.max(-82, math.min(x, 84)) 1374 | y = math.max(-86, math.min(y, 82)) 1375 | end 1376 | frame:SetPoint("CENTER", Minimap, "CENTER", x, y) 1377 | end 1378 | end 1379 | 1380 | function MinimapContainer:GetPlugin(index) 1381 | return self.plugins[index] 1382 | end 1383 | 1384 | function MinimapContainer:GetNumPlugins() 1385 | return table.getn(self.plugins) 1386 | end 1387 | 1388 | function MinimapContainer:IndexOfPlugin(plugin) 1389 | for i,p in ipairs(self.plugins) do 1390 | if p == plugin then 1391 | return i, "MINIMAP" 1392 | end 1393 | end 1394 | end 1395 | 1396 | function MinimapContainer:HasPlugin(plugin) 1397 | return self:IndexOfPlugin(plugin) ~= nil 1398 | end 1399 | 1400 | function MinimapContainer:GetPluginSide(plugin) 1401 | local index = self:IndexOfPlugin(plugin) 1402 | assert(index, "Plugin not in panel") 1403 | return "MINIMAP" 1404 | end 1405 | 1406 | function MinimapContainer.OnDragStart() 1407 | this.dragged = true 1408 | this:LockHighlight() 1409 | this:SetScript("OnUpdate", MinimapContainer.OnUpdate) 1410 | if string.sub(this.plugin.minimapIcon:GetTexture(), 1, 16) == "Interface\\Icons\\" then 1411 | this.plugin.minimapIcon:SetTexCoord(0.05, 0.95, 0.05, 0.95) 1412 | else 1413 | this.plugin.minimapIcon:SetTexCoord(0, 1, 0, 1) 1414 | end 1415 | end 1416 | 1417 | function MinimapContainer.OnDragStop() 1418 | this:SetScript("OnUpdate", nil) 1419 | this:UnlockHighlight() 1420 | end 1421 | 1422 | function MinimapContainer.OnUpdate() 1423 | if not IsAltKeyDown() then 1424 | local mx, my = Minimap:GetCenter() 1425 | local px, py = GetCursorPosition() 1426 | local scale = UIParent:GetEffectiveScale() 1427 | px, py = px / scale, py / scale 1428 | local position = math.deg(math.atan2(py - my, px - mx)) 1429 | if position <= 0 then 1430 | position = position + 360 1431 | elseif position > 360 then 1432 | position = position - 360 1433 | end 1434 | if this.plugin.db then 1435 | this.plugin.db.profile.minimapPosition = position 1436 | this.plugin.db.profile.minimapPositionX = nil 1437 | this.plugin.db.profile.minimapPositionY = nil 1438 | this.plugin.db.profile.minimapPositionWild = nil 1439 | else 1440 | this.plugin.minimapPosition = position 1441 | this.plugin.minimapPositionX = nil 1442 | this.plugin.minimapPositionY = nil 1443 | this.plugin.minimapPositionWild = nil 1444 | end 1445 | else 1446 | local px, py = GetCursorPosition() 1447 | local scale = UIParent:GetEffectiveScale() 1448 | px, py = px / scale, py / scale 1449 | if this.plugin.db then 1450 | this.plugin.db.profile.minimapPositionX = px 1451 | this.plugin.db.profile.minimapPositionY = py 1452 | this.plugin.db.profile.minimapPosition = nil 1453 | this.plugin.db.profile.minimapPositionWild = true 1454 | else 1455 | this.plugin.minimapPositionX = px 1456 | this.plugin.minimapPositionY = py 1457 | this.plugin.minimapPosition = nil 1458 | this.plugin.minimapPositionWild = true 1459 | end 1460 | end 1461 | MinimapContainer:ReadjustLocation(this.plugin) 1462 | end 1463 | 1464 | local function activate(self, oldLib, oldDeactivate) 1465 | MinimapContainer = self 1466 | 1467 | if oldLib then 1468 | self.plugins = oldLib.plugins 1469 | end 1470 | 1471 | if not self.plugins then 1472 | self.plugins = {} 1473 | end 1474 | 1475 | if oldDeactivate then 1476 | oldDeactivate(oldLib) 1477 | end 1478 | end 1479 | 1480 | AceLibrary:Register(MinimapContainer, MINIMAPCONTAINER_MAJOR_VERSION, MINOR_VERSION, activate) 1481 | --------------------------------------------------------------------------------