Johnny C. Lam [07-13-14 - 11:30]
diff --git a/WoWAPI.lua b/WoWAPI.lua
index 934cc93..64498ed 100644
--- a/WoWAPI.lua
+++ b/WoWAPI.lua
@@ -7,47 +7,378 @@
file accompanying this program.
--]]--------------------------------------------------------------------
---[[
- This file implements parts of the WoW API for development and unit-testing.
-
- This file is not meant to be loaded into the addon. It should be used only
- outside of the WoW environment, such as when loaded by a standalone Lua 5.1
- interpreter.
-
- It provides global symbols.
---]]
-
--- RAID_CLASS_COLORS is useful mostly as a table to loop through the keys, which are
--- the class tokens for all playable classes.
-RAID_CLASS_COLORS = RAID_CLASS_COLORS or {
- DEATHKNIGHT = true,
- DRUID = true,
- HUNTER = true,
- MAGE = true,
- MONK = true,
- PALADIN = true,
- PRIEST = true,
- ROGUE = true,
- SHAMAN = true,
- WARLOCK = true,
- WARRIOR = true,
+--[[--------------------------------------------------------------------
+ This file implements parts of the WoW API for development and
+ unit-testing.
+
+ This file is not meant to be loaded into the addon. It should be
+ used only outside of the WoW environment, such as when loaded by a
+ standalone Lua 5.1 interpreter.
+--]]--------------------------------------------------------------------
+
+-- Globally-accessible module table.
+WoWAPI = {}
+
+--<private-static-properties>
+local self_state = {}
+local self_privateSymbol = {
+ ["ExportSymbols"] = true,
+ ["Initialize"] = true,
}
--- wipe() is a non-standard Lua function that clears the contents of a table
--- and leaves the table pointer intact.
-wipe = wipe or function(t)
- for k in pairs(t) do
- t[k] = nil
+-- Metatable to provide __index method to tables so that if the requested key
+-- is missing from the table, then a new key is inserted with the value being
+-- the same as the missing key.
+local KeysAreMissingValuesMetatable = {
+ __index = function(t, k)
+ rawset(t, k, k)
+ return k
+ end,
+}
+--</private-static-properties>
+
+--<private-static-methods>
+function DeepCopy(orig)
+ local orig_type = type(orig)
+ local copy
+ if orig_type == 'table' then
+ copy = {}
+ for orig_key, orig_value in next, orig, nil do
+ copy[DeepCopy(orig_key)] = DeepCopy(orig_value)
+ end
+ setmetatable(copy, DeepCopy(getmetatable(orig)))
+ else -- number, string, boolean, etc
+ copy = orig
+ end
+ return copy
+end
+--</private-static-methods>
+
+--<private-static-properties>
+--[[--------------------------------
+ Fake library implementations.
+--]]--------------------------------
+
+-- AceAddon-3.0
+local AceAddon = nil
+do
+ local lib = {}
+ AceAddon = lib
+
+ lib.initializationQueue = {}
+
+ local prototype = {}
+
+ prototype.GetModule = function(addon, name)
+ addon.modules = addon.modules or {}
+ return addon.modules[name]
+ end
+
+ prototype.GetName = function(addon)
+ return addon.moduleName or addon.name
+ end
+
+ prototype.IterateModules = function(addon)
+ return pairs(addon.modules)
+ end
+
+ prototype.NewModule = function(addon, name, ...)
+ local args = { ... }
+ local mod = lib:NewAddon(string.format("%s_%s", addon.name, name))
+ mod.moduleName = name
+ -- Embed methods from named libraries.
+ for _, libName in ipairs(args) do
+ local lib = LibStub(libName)
+ if lib then
+ for k, v in pairs(lib) do
+ mod[k] = v
+ end
+ end
+ end
+ addon.modules = addon.modules or {}
+ addon.modules[name] = mod
+ return mod
+ end
+
+ lib.GetAddon = function(lib, name)
+ lib.addons = lib.addons or {}
+ return lib.addons[name]
+ end
+
+ lib.ADDON_LOADED = function(lib, event)
+ for _, addon in ipairs(lib.initializationQueue) do
+ if addon.OnInitialize then
+ addon:OnInitialize()
+ end
+ end
+ end
+
+ lib.IterateAddons = function(lib)
+ return pairs(lib.addons)
+ end
+
+ lib.NewAddon = function(lib, name, ...)
+ local addon
+ local args
+ if type(name) == "nil" then
+ addon = {}
+ name = ...
+ args = { select(2, ...) }
+ elseif type(name) == "table" then
+ addon = name
+ name = ...
+ args = { select(2, ...) }
+ else
+ addon = {}
+ args = { ... }
+ end
+ -- Copy addon prototype.
+ for k, v in pairs(prototype) do
+ addon[k] = v
+ end
+ -- Embed methods from named libraries.
+ for _, libName in ipairs(args) do
+ local lib = LibStub(libName)
+ if lib then
+ for k, v in pairs(lib) do
+ addon[k] = v
+ end
+ end
+ end
+ addon.name = name
+ lib.addons = lib.addons or {}
+ lib.addons[name] = addon
+ lib.initializationQueue[#lib.initializationQueue + 1] = addon
+ return addon
end
end
-table.wipe = table.wipe or wipe
--- strsplit() is a non-standard Lua function that splits a string and returns
--- multiple return values for each substring delimited by the named delimiter
--- character.
---
--- This implementaiton is taken verbatim from http://lua-users.org/wiki/SplitJoin
-strsplit = strsplit or function(delim, str, maxNb)
+-- AceConfig-3.0
+local AceConfig = nil
+do
+ local lib = {}
+ AceConfig = lib
+ lib.RegisterOptionsTable = function(lib, ...) end
+end
+
+-- AceConfigDialog-3.0
+local AceConfigDialog = nil
+do
+ local lib = {}
+ AceConfigDialog = lib
+ lib.AddToBlizOptions = function(lib, ...) end
+end
+
+-- AceConsole-3.0
+local AceConsole = nil
+do
+ local lib = {}
+ AceConsole = lib
+
+ lib.Print = function(lib, ...)
+ print(...)
+ end
+
+ lib.Printf = function(lib, ...)
+ print(string.format(...))
+ end
+end
+
+-- AceDB-3.0
+local AceDB = nil
+do
+ local lib = {}
+ AceDB = lib
+
+ lib.New = function(lib, name, template)
+ template = template or {}
+ local db = DeepCopy(template)
+ db.RegisterCallback = function(...) end
+ return db
+ end
+end
+
+-- AceDBOptions-3.0
+local AceDBOptions = nil
+do
+ local lib = {}
+ AceDBOptions = lib
+ lib.GetOptionsTable = function(db) end
+end
+
+-- AceEvent-3.0
+local AceEvent = nil
+do
+ local lib = {}
+ AceEvent = lib
+ lib.SendMessage = function(lib, message, ...) end
+end
+
+-- AceGUI-3.0
+local AceGUI = nil
+do
+ local lib = {}
+ AceGUI = lib
+ lib.RegisterWidgetType = function(...) end
+end
+
+-- AceLocale-3.0
+local AceLocale = nil
+do
+ local lib = {}
+ AceLocale = lib
+
+ lib.GetLocale = function(lib, name)
+ local L
+ if lib.locale and lib.locale[name] then
+ L = lib.locale[name]
+ else
+ L = lib:NewLocale(name, nil)
+ end
+ return L
+ end
+
+ lib.NewLocale = function(lib, name, locale)
+ local L = setmetatable({}, KeysAreMissingValuesMetatable)
+ lib.locale = lib.locale or {}
+ lib.locale[name] = L
+ return L
+ end
+end
+
+-- LibBabble-CreatureType-3.0
+local LibBabbleCreatureType = nil
+do
+ local lib = {}
+ LibBabbleCreatureType = lib
+
+ lib.GetLookupTable = function(lib)
+ local tbl = lib.lookupTable or setmetatable({}, KeysAreMissingValuesMetatable)
+ lib.lookupTable = tbl
+ return tbl
+ end
+end
+
+-- LibStub
+local LibStub = nil
+do
+ local lib = {}
+ LibStub = lib
+
+ lib.library = {
+ ["AceAddon-3.0"] = AceAddon,
+ ["AceConfig-3.0"] = AceConfig,
+ ["AceConfigDialog-3.0"] = AceConfigDialog,
+ ["AceConsole-3.0"] = AceConsole,
+ ["AceDB-3.0"] = AceDB,
+ ["AceDBOptions-3.0"] = AceDBOptions,
+ ["AceEvent-3.0"] = AceEvent,
+ ["AceGUI-3.0"] = AceGUI,
+ ["AceLocale-3.0"] = AceLocale,
+ ["LibBabble-CreatureType-3.0"] = LibBabbleCreatureType,
+ }
+
+ local mt = {
+ __call = function(lib, name, flag)
+ return lib:GetLibrary(name, flag)
+ end,
+ }
+
+ lib.GetLibrary = function(lib, name, flag)
+ return lib.library[name]
+ end
+
+ setmetatable(lib, mt)
+end
+--</private-static-properties>
+
+--<public-static-properties>
+--[[----------------------
+ FrameXML/Constants
+--]]----------------------
+
+-- Inventory slots
+WoWAPI.INVSLOT_AMMO = 0
+WoWAPI.INVSLOT_HEAD = 1
+WoWAPI.INVSLOT_NECK = 2
+WoWAPI.INVSLOT_SHOULDER = 3
+WoWAPI.INVSLOT_BODY = 4
+WoWAPI.INVSLOT_CHEST = 5
+WoWAPI.INVSLOT_WAIST = 6
+WoWAPI.INVSLOT_LEGS = 7
+WoWAPI.INVSLOT_FEET = 8
+WoWAPI.INVSLOT_WRIST = 9
+WoWAPI.INVSLOT_HAND = 10
+WoWAPI.INVSLOT_FINGER1 = 11
+WoWAPI.INVSLOT_FINGER2 = 12
+WoWAPI.INVSLOT_TRINKET1 = 13
+WoWAPI.INVSLOT_TRINKET2 = 14
+WoWAPI.INVSLOT_BACK = 15
+WoWAPI.INVSLOT_MAINHAND = 16
+WoWAPI.INVSLOT_OFFHAND = 17
+WoWAPI.INVSLOT_RANGED = 18
+WoWAPI.INVSLOT_TABARD = 19
+WoWAPI.INVSLOT_FIRST_EQUIPPED = WoWAPI.INVSLOT_HEAD
+WoWAPI.INVSLOT_LAST_EQUIPPED = WoWAPI.INVSLOT_TABARD
+
+-- Power Types
+WoWAPI.SPELL_POWER_MANA = 0
+WoWAPI.SPELL_POWER_RAGE = 1
+WoWAPI.SPELL_POWER_FOCUS = 2
+WoWAPI.SPELL_POWER_ENERGY = 3
+--WoWAPI.SPELL_POWER_CHI = 4 -- This is obsolete now.
+WoWAPI.SPELL_POWER_RUNES = 5
+WoWAPI.SPELL_POWER_RUNIC_POWER = 6
+WoWAPI.SPELL_POWER_SOUL_SHARDS = 7
+WoWAPI.SPELL_POWER_ECLIPSE = 8
+WoWAPI.SPELL_POWER_HOLY_POWER = 9
+WoWAPI.SPELL_POWER_ALTERNATE_POWER = 10
+WoWAPI.SPELL_POWER_DARK_FORCE = 11
+WoWAPI.SPELL_POWER_CHI = 12
+WoWAPI.SPELL_POWER_SHADOW_ORBS = 13
+WoWAPI.SPELL_POWER_BURNING_EMBERS = 14
+WoWAPI.SPELL_POWER_DEMONIC_FURY = 15
+
+WoWAPI.RAID_CLASS_COLORS = {
+ ["HUNTER"] = { r = 0.67, g = 0.83, b = 0.45, colorStr = "ffabd473" },
+ ["WARLOCK"] = { r = 0.58, g = 0.51, b = 0.79, colorStr = "ff9482c9" },
+ ["PRIEST"] = { r = 1.0, g = 1.0, b = 1.0, colorStr = "ffffffff" },
+ ["PALADIN"] = { r = 0.96, g = 0.55, b = 0.73, colorStr = "fff58cba" },
+ ["MAGE"] = { r = 0.41, g = 0.8, b = 0.94, colorStr = "ff69ccf0" },
+ ["ROGUE"] = { r = 1.0, g = 0.96, b = 0.41, colorStr = "fffff569" },
+ ["DRUID"] = { r = 1.0, g = 0.49, b = 0.04, colorStr = "ffff7d0a" },
+ ["SHAMAN"] = { r = 0.0, g = 0.44, b = 0.87, colorStr = "ff0070de" },
+ ["WARRIOR"] = { r = 0.78, g = 0.61, b = 0.43, colorStr = "ffc79c6e" },
+ ["DEATHKNIGHT"] = { r = 0.77, g = 0.12 , b = 0.23, colorStr = "ffc41f3b" },
+ ["MONK"] = { r = 0.0, g = 1.00 , b = 0.59, colorStr = "ff00ff96" },
+}
+
+--[[--------------------------
+ FrameXML/GlobalStrings
+--]]--------------------------
+
+WoWAPI.ITEM_LEVEL = "Item Level %d"
+
+--[[--------------------------------------------------------------------
+ debugprofilestop() is a non-standard Lua function that returns the
+ current time in milliseconds.
+
+ This is a trivial implementation to just get the Profiler module
+ working.
+--]]--------------------------------------------------------------------
+WoWAPI.debugprofilestop = function()
+ return 0
+end
+
+--[[--------------------------------------------------------------------
+ strsplit() is a non-standard Lua function that splits a string and
+ returns multiple return values for each substring delimited by the
+ named delimiter character.
+
+ This implementation is taken verbatim from:
+ http://lua-users.org/wiki/SplitJoin
+--]]--------------------------------------------------------------------
+WoWAPI.strsplit = function(delim, str, maxNb)
-- Eliminate bad cases...
if string.find(str, delim) == nil then
return str
@@ -72,16 +403,262 @@ strsplit = strsplit or function(delim, str, maxNb)
return unpack(result)
end
--- LoadAddonFile() does the equivalent of dofile(), but strips out the WoW addon
--- file line that uses ... to get the file arguments.
-LoadAddonFile = LoadAddonFile or function(filename)
- local lineList = {}
- for line in io.lines(filename) do
- if not string.match(line, "^%s*local%s+[%w%s_,]*%s*=%s*[.][.][.]%s*$") then
- table.insert(lineList, line)
+--[[--------------------------------------------------------------------
+ wipe() is a non-standard Lua function that clears the contents of a
+ table and leaves the table pointer intact.
+--]]--------------------------------------------------------------------
+WoWAPI.wipe = function(t)
+ for k in pairs(t) do
+ t[k] = nil
+ end
+end
+
+--[[-------------------------------------------------
+ Fake Blizzard API functions for unit testing.
+--]]-------------------------------------------------
+
+WoWAPI.CreateFrame = function(...)
+ return {
+ SetOwner = function(...) end,
+ }
+end
+
+WoWAPI.GetAuctionItemSubClasses = function(classIndex)
+ return
+ "One-Handed Axes",
+ "Two-Handed Axes",
+ "Bows",
+ "Guns",
+ "One-Handed Maces",
+ "Two-Handed Maces",
+ "Polearms",
+ "One-Handed Swords",
+ "Two-Handed Swords",
+ "Staves",
+ "Fist Weapons",
+ "Miscellaneous",
+ "Daggers",
+ "Thrown",
+ "Crossbows",
+ "Wands",
+ "Fishing Poles"
+end
+
+WoWAPI.GetItemInfo = function(item)
+ if type(item) == "number" then
+ item = string.format("Item Name Of %d", item)
+ end
+ return item
+end
+
+WoWAPI.GetSpellInfo = function(spell)
+ if type(spell) == "number" then
+ spell = string.format("Spell Name Of %d", spell)
+ end
+ return spell
+end
+
+WoWAPI.RegisterAddonMessagePrefix = function(prefixString) end
+
+WoWAPI.UnitClass = function()
+ local class = self_state.class
+ return class, class
+end
+
+WoWAPI.UnitLevel = function()
+ return self_state.level
+end
+
+WoWAPI.bit = {
+ band = function(...) end,
+ bor = function(...) end,
+}
+
+WoWAPI.LibStub = LibStub
+--</public-static-properties>
+
+--<private-static-methods>
+local function FileExists(filename, directory, verbose)
+ if directory then
+ filename = directory .. filename
+ end
+ local fh = io.open(filename, "r")
+ if fh then
+ fh:close()
+ return true
+ else
+ if verbose then
+ print(string.format("Warning: '%s' not found.", filename))
+ end
+ return false
+ end
+end
+--</private-static-methods>
+
+--<public-static-methods>
+function WoWAPI:Initialize(addonName, state)
+ state = state or {}
+ for k, v in pairs(state) do
+ self_state[k] = v
+ end
+ self_state.addonName = addonName
+end
+
+-- Export symbols to the given namespace, taking care not to overwrite existing symbols.
+function WoWAPI:ExportSymbols(namespace)
+ -- Default to adding symbols to the global namespace.
+ namespace = namespace or _G
+ for k, v in pairs(self) do
+ if not self_privateSymbol[k] then
+ namespace[k] = namespace[k] or v
+ end
+ end
+ -- Special handling for wipe() to add to "table" module.
+ table.wipe = table.wipe or WoWAPI.wipe
+end
+
+--[[--------------------------------------------------------------------
+ LoadAddOnFile() dispatches to the proper method to load the file
+ based on the file extension.
+--]]--------------------------------------------------------------------
+function WoWAPI:LoadAddonFile(filename, directory, verbose)
+ local s = directory and (directory .. filename) or filename
+ directory, filename = string.match(s, "^(.+/)([^/]+[.][%w]+)$")
+ if not directory then
+ filename = s
+ end
+ if string.find(filename, "[.]lua$") then
+ return self:LoadLua(filename, directory, verbose)
+ elseif string.find(filename, "[.]toc$") then
+ return self:LoadTOC(filename, directory, verbose)
+ elseif string.find(filename, "[.]xml$") then
+ return self:LoadXML(filename, directory, verbose)
+ end
+end
+
+--[[--------------------------------------------------------------------
+ LoadAddonFile() does the equivalent of dofile(), but munges the WoW
+ addon file line that uses ... to get the file arguments.
+--]]--------------------------------------------------------------------
+function WoWAPI:LoadLua(filename, directory, verbose)
+ if directory then
+ filename = directory .. filename
+ end
+ if verbose then
+ print(string.format("Loading Lua: %s", filename))
+ end
+
+ local ok = FileExists(filename, nil, verbose)
+ if ok then
+ local list = {}
+ for line in io.lines(filename) do
+ local varName = string.match(line, "^local%s+([%w_]+)%s*,[%w%s_,]*=%s*[.][.][.]%s*$")
+ if varName then
+ line = string.format("local %s = %q", varName, self_state.addonName)
+ end
+ table.insert(list, line)
+ end
+
+ local fileString = table.concat(list, "\n")
+ local func = loadstring(fileString)
+ if func then
+ func()
+ else
+ print(string.format("Error loading '%s'.", filename))
+ ok = false
+ end
+ end
+ return ok
+end
+
+--[[--------------------------------------------------------------------
+ LoadTOC() loads all of the addon's files listed in the TOC file.
+--]]--------------------------------------------------------------------
+function WoWAPI:LoadTOC(filename, directory, verbose)
+ if directory then
+ filename = directory .. filename
+ end
+ if verbose then
+ print(string.format("Loading TOC: %s", filename))
+ end
+
+ local ok = FileExists(filename, nil, verbose)
+ if ok then
+ local list = {}
+ for line in io.lines(filename) do
+ line = string.gsub(line, "\\", "/")
+ local t = {}
+ t.directory, t.file = string.match(line, "^([^#]+/)([^/]+[.][%w]+)$")
+ if t.directory then
+ if directory then
+ t.directory = directory .. t.directory
+ end
+ else
+ t.directory = directory
+ t.file = string.match(line, "^[%w_]+[.][%w]+$")
+ end
+ if t.file then
+ table.insert(list, t)
+ end
+ end
+ for _, t in ipairs(list) do
+ if string.find(t.file, "[.]lua$") then
+ ok = ok and self:LoadLua(t.file, t.directory, verbose)
+ elseif string.find(t.file, "[.]xml$") then
+ ok = ok and self:LoadXML(t.file, t.directory, verbose)
+ end
+ if not ok then
+ break
+ end
+ end
+ end
+ return ok
+end
+
+--[[--------------------------------------------------------------------
+ LoadXML() loads all of the addon's Lua files listed in the XML file.
+--]]--------------------------------------------------------------------
+function WoWAPI:LoadXML(filename, directory, verbose)
+ if directory then
+ filename = directory .. filename
+ end
+ if verbose then
+ print(string.format("Loading XML: %s", filename))
+ end
+
+ local ok = FileExists(filename, nil, verbose)
+ if ok then
+ local list = {}
+ for line in io.lines(filename) do
+ local s = string.match(line, '<Script file="([^"]+)"')
+ if s then
+ s = string.gsub(s, "\\", "/")
+ local t = {}
+ t.directory, t.file = string.match(s, "^(.+/)([^/]+[.][%w]+)$")
+ if t.directory then
+ if directory then
+ t.directory = directory .. t.directory
+ end
+ else
+ t.directory = directory
+ t.file = s
+ end
+ if t.file then
+ table.insert(list, t)
+ end
+ end
+ end
+ for _, t in ipairs(list) do
+ if FileExists(t.file, t.directory, verbose) then
+ if string.find(t.file, "[.]lua$") then
+ ok = ok and self:LoadLua(t.file, t.directory, verbose)
+ if not ok then
+ break
+ end
+ end
+ end
end
end
- local fileString = table.concat(lineList, "\n")
- local func = loadstring(fileString)
- func()
+ return ok
end
+--</public-static-methods>