From e3f39538a3748391e339ad5648d68d3297b3df6d Mon Sep 17 00:00:00 2001 From: moonwitch Date: Sun, 4 Jan 2015 16:33:34 +0100 Subject: [PATCH] added a little LDB - this is more for DPS tracking since I don't use a DPS meter anymore. --- LookInTehCorner.toc | 7 +- ldb.lua | 15 ++ libs/CallbackHandler-1.0.lua | 240 +++++++++++++++++++++++++++++++ libs/LibCargoShip-2.1.lua | 323 ++++++++++++++++++++++++++++++++++++++++++ libs/LibDataBroker-1.1.lua | 90 ++++++++++++ libs/LibStub.lua | 30 ++++ 6 files changed, 704 insertions(+), 1 deletion(-) create mode 100755 ldb.lua create mode 100755 libs/CallbackHandler-1.0.lua create mode 100755 libs/LibCargoShip-2.1.lua create mode 100755 libs/LibDataBroker-1.1.lua create mode 100755 libs/LibStub.lua diff --git a/LookInTehCorner.toc b/LookInTehCorner.toc index a7aa9dc..4885e55 100755 --- a/LookInTehCorner.toc +++ b/LookInTehCorner.toc @@ -9,9 +9,14 @@ ## X-Category: Map ## OptionalDeps: Blizzard_TimeManager +libs\LibStub.lua +libs\CallbackHandler-1.0.lua +libs\LibDataBroker-1.1.lua +libs\LibCargoShip-2.1.lua + core.lua clock.lua -compass.lua +ldb.lua coordinates.lua #tooltip.lua rightclick.lua diff --git a/ldb.lua b/ldb.lua new file mode 100755 index 0000000..23b1857 --- /dev/null +++ b/ldb.lua @@ -0,0 +1,15 @@ +------------------------------------------------------------------------ +-- Simple DataBroker frame at the bottom of the minimap +------------------------------------------------------------------------ +local brokerwidth = Minimap:GetWidth() + +local LCS = LibStub("LibCargoShip-2.1") +local block = LCS:CreateBlock{ + width = brokerwidth, + parent = Minimap, + noIcon = false, + scale = 1, + fontSize = 11 +} +block:SetDataObject("DPS") +block:SetPoint("TOP", Minimap, "BOTTOM", 0, 0) \ No newline at end of file diff --git a/libs/CallbackHandler-1.0.lua b/libs/CallbackHandler-1.0.lua new file mode 100755 index 0000000..bc311d9 --- /dev/null +++ b/libs/CallbackHandler-1.0.lua @@ -0,0 +1,240 @@ +--[[ $Id: CallbackHandler-1.0.lua 965 2010-08-09 00:47:52Z mikk $ ]] +local MAJOR, MINOR = "CallbackHandler-1.0", 6 +local CallbackHandler = LibStub:NewLibrary(MAJOR, MINOR) + +if not CallbackHandler then return end -- No upgrade needed + +local meta = {__index = function(tbl, key) tbl[key] = {} return tbl[key] end} + +-- Lua APIs +local tconcat = table.concat +local assert, error, loadstring = assert, error, loadstring +local setmetatable, rawset, rawget = setmetatable, rawset, rawget +local next, select, pairs, type, tostring = next, select, pairs, type, tostring + +-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded +-- List them here for Mikk's FindGlobals script +-- GLOBALS: geterrorhandler + +local xpcall = xpcall + +local function errorhandler(err) + return geterrorhandler()(err) +end + +local function CreateDispatcher(argCount) + local code = [[ + local next, xpcall, eh = ... + + local method, ARGS + local function call() method(ARGS) end + + local function dispatch(handlers, ...) + local index + index, method = next(handlers) + if not method then return end + local OLD_ARGS = ARGS + ARGS = ... + repeat + xpcall(call, eh) + index, method = next(handlers, index) + until not method + ARGS = OLD_ARGS + end + + return dispatch + ]] + + local ARGS, OLD_ARGS = {}, {} + for i = 1, argCount do ARGS[i], OLD_ARGS[i] = "arg"..i, "old_arg"..i end + code = code:gsub("OLD_ARGS", tconcat(OLD_ARGS, ", ")):gsub("ARGS", tconcat(ARGS, ", ")) + return assert(loadstring(code, "safecall Dispatcher["..argCount.."]"))(next, xpcall, errorhandler) +end + +local Dispatchers = setmetatable({}, {__index=function(self, argCount) + local dispatcher = CreateDispatcher(argCount) + rawset(self, argCount, dispatcher) + return dispatcher +end}) + +-------------------------------------------------------------------------- +-- CallbackHandler:New +-- +-- target - target object to embed public APIs in +-- RegisterName - name of the callback registration API, default "RegisterCallback" +-- UnregisterName - name of the callback unregistration API, default "UnregisterCallback" +-- UnregisterAllName - name of the API to unregister all callbacks, default "UnregisterAllCallbacks". false == don't publish this API. + +function CallbackHandler:New(target, RegisterName, UnregisterName, UnregisterAllName, OnUsed, OnUnused) + -- TODO: Remove this after beta has gone out + assert(not OnUsed and not OnUnused, "ACE-80: OnUsed/OnUnused are deprecated. Callbacks are now done to registry.OnUsed and registry.OnUnused") + + RegisterName = RegisterName or "RegisterCallback" + UnregisterName = UnregisterName or "UnregisterCallback" + if UnregisterAllName==nil then -- false is used to indicate "don't want this method" + UnregisterAllName = "UnregisterAllCallbacks" + end + + -- we declare all objects and exported APIs inside this closure to quickly gain access + -- to e.g. function names, the "target" parameter, etc + + + -- Create the registry object + local events = setmetatable({}, meta) + local registry = { recurse=0, events=events } + + -- registry:Fire() - fires the given event/message into the registry + function registry:Fire(eventname, ...) + if not rawget(events, eventname) or not next(events[eventname]) then return end + local oldrecurse = registry.recurse + registry.recurse = oldrecurse + 1 + + Dispatchers[select('#', ...) + 1](events[eventname], eventname, ...) + + registry.recurse = oldrecurse + + if registry.insertQueue and oldrecurse==0 then + -- Something in one of our callbacks wanted to register more callbacks; they got queued + for eventname,callbacks in pairs(registry.insertQueue) do + local first = not rawget(events, eventname) or not next(events[eventname]) -- test for empty before. not test for one member after. that one member may have been overwritten. + for self,func in pairs(callbacks) do + events[eventname][self] = func + -- fire OnUsed callback? + if first and registry.OnUsed then + registry.OnUsed(registry, target, eventname) + first = nil + end + end + end + registry.insertQueue = nil + end + end + + -- Registration of a callback, handles: + -- self["method"], leads to self["method"](self, ...) + -- self with function ref, leads to functionref(...) + -- "addonId" (instead of self) with function ref, leads to functionref(...) + -- all with an optional arg, which, if present, gets passed as first argument (after self if present) + target[RegisterName] = function(self, eventname, method, ... --[[actually just a single arg]]) + if type(eventname) ~= "string" then + error("Usage: "..RegisterName.."(eventname, method[, arg]): 'eventname' - string expected.", 2) + end + + method = method or eventname + + local first = not rawget(events, eventname) or not next(events[eventname]) -- test for empty before. not test for one member after. that one member may have been overwritten. + + if type(method) ~= "string" and type(method) ~= "function" then + error("Usage: "..RegisterName.."(\"eventname\", \"methodname\"): 'methodname' - string or function expected.", 2) + end + + local regfunc + + if type(method) == "string" then + -- self["method"] calling style + if type(self) ~= "table" then + error("Usage: "..RegisterName.."(\"eventname\", \"methodname\"): self was not a table?", 2) + elseif self==target then + error("Usage: "..RegisterName.."(\"eventname\", \"methodname\"): do not use Library:"..RegisterName.."(), use your own 'self'", 2) + elseif type(self[method]) ~= "function" then + error("Usage: "..RegisterName.."(\"eventname\", \"methodname\"): 'methodname' - method '"..tostring(method).."' not found on self.", 2) + end + + if select("#",...)>=1 then -- this is not the same as testing for arg==nil! + local arg=select(1,...) + regfunc = function(...) self[method](self,arg,...) end + else + regfunc = function(...) self[method](self,...) end + end + else + -- function ref with self=object or self="addonId" or self=thread + if type(self)~="table" and type(self)~="string" and type(self)~="thread" then + error("Usage: "..RegisterName.."(self or \"addonId\", eventname, method): 'self or addonId': table or string or thread expected.", 2) + end + + if select("#",...)>=1 then -- this is not the same as testing for arg==nil! + local arg=select(1,...) + regfunc = function(...) method(arg,...) end + else + regfunc = method + end + end + + + if events[eventname][self] or registry.recurse<1 then + -- if registry.recurse<1 then + -- we're overwriting an existing entry, or not currently recursing. just set it. + events[eventname][self] = regfunc + -- fire OnUsed callback? + if registry.OnUsed and first then + registry.OnUsed(registry, target, eventname) + end + else + -- we're currently processing a callback in this registry, so delay the registration of this new entry! + -- yes, we're a bit wasteful on garbage, but this is a fringe case, so we're picking low implementation overhead over garbage efficiency + registry.insertQueue = registry.insertQueue or setmetatable({},meta) + registry.insertQueue[eventname][self] = regfunc + end + end + + -- Unregister a callback + target[UnregisterName] = function(self, eventname) + if not self or self==target then + error("Usage: "..UnregisterName.."(eventname): bad 'self'", 2) + end + if type(eventname) ~= "string" then + error("Usage: "..UnregisterName.."(eventname): 'eventname' - string expected.", 2) + end + if rawget(events, eventname) and events[eventname][self] then + events[eventname][self] = nil + -- Fire OnUnused callback? + if registry.OnUnused and not next(events[eventname]) then + registry.OnUnused(registry, target, eventname) + end + end + if registry.insertQueue and rawget(registry.insertQueue, eventname) and registry.insertQueue[eventname][self] then + registry.insertQueue[eventname][self] = nil + end + end + + -- OPTIONAL: Unregister all callbacks for given selfs/addonIds + if UnregisterAllName then + target[UnregisterAllName] = function(...) + if select("#",...)<1 then + error("Usage: "..UnregisterAllName.."([whatFor]): missing 'self' or \"addonId\" to unregister events for.", 2) + end + if select("#",...)==1 and ...==target then + error("Usage: "..UnregisterAllName.."([whatFor]): supply a meaningful 'self' or \"addonId\"", 2) + end + + + for i=1,select("#",...) do + local self = select(i,...) + if registry.insertQueue then + for eventname, callbacks in pairs(registry.insertQueue) do + if callbacks[self] then + callbacks[self] = nil + end + end + end + for eventname, callbacks in pairs(events) do + if callbacks[self] then + callbacks[self] = nil + -- Fire OnUnused callback? + if registry.OnUnused and not next(callbacks) then + registry.OnUnused(registry, target, eventname) + end + end + end + end + end + end + + return registry +end + + +-- CallbackHandler purposefully does NOT do explicit embedding. Nor does it +-- try to upgrade old implicit embeds since the system is selfcontained and +-- relies on closures to work. + diff --git a/libs/LibCargoShip-2.1.lua b/libs/LibCargoShip-2.1.lua new file mode 100755 index 0000000..9332845 --- /dev/null +++ b/libs/LibCargoShip-2.1.lua @@ -0,0 +1,323 @@ +--[[ +Name: LibCargoShip-2.1 +Author: Cargor (xconstruct@gmail.com) +Dependencies: LibStub, LibDataBroker-1.1 +License: GPL 2 +Description: LibDataBroker block display library +]] + +assert(LibStub, "LibCargoShip-2.1 requires LibStub") +local LDB = LibStub:GetLibrary("LibDataBroker-1.1") +local lib, oldminor = LibStub:NewLibrary("LibCargoShip-2.1", 5) +if(not lib) then return end + +local defaults = {__index={ + parent = UIParent, + width = 70, + height = 12, + scale = 1, + alpha = 1, + + fontObject = nil, + font = "Fonts\\FRIZQT__.TTF", + fontSize = 10, + fontStyle = nil, + textColor = {1, 1, 1, 1}, + + noShadow = nil, + shadowX = 1, + shadowY = -1, + + noIcon = nil, + noText = nil, +}} + +local getDataObject +local objects = {} +local updateFunctions = {} +local assertf = function(cond, ...) return assert(cond, format(...)) end +local Prototype = CreateFrame"Button" +local mt_prototype = {__index = Prototype} +lib.Prototype = {__index = Prototype} +lib.Objects = objects + +--[[***************************** + lib:CreateBlock([name] [, options]) + Creates a new block from the DataObject of the same name + The name can either be delivered as arg #1, making the options-table optional, + or defined in options.name, where options is passed as arg #1 +*******************************]] +function lib:CreateBlock(name, opt) + if(type(name) == "table" and not opt) then + opt, name = name, name.name + end + opt = setmetatable(opt or {}, defaults) + + local object = setmetatable(CreateFrame("Button", nil, opt.parent), self.Prototype) + object:RegisterForClicks("anyUp") + object:Hide() + object.tagString = opt.tagString + object.UpdateFunctions = opt.updateFunctions + + if(opt.style) then + opt.style(object, opt) + else + object:Style(opt) + end + + return object, object:SetDataObject(name) +end +setmetatable(lib, {__call = lib.CreateBlock}) + +--[[***************************** + lib:Get(dataObject) + Return a table of all current blocks using the defined dataObject +*******************************]] +function lib:Get(arg1) + local name = getDataObject(arg1) + return name and objects[name] +end + +--[[***************************** + lib:GetFirst(dataObject) + Convenience function, get the first block using the dataObject +*******************************]] +function lib:GetFirst(arg1) + local name = getDataObject(arg1) + return name and objects[name] and next(objects[name]) +end + +--[[***************************** + lib:GetUnused(verbose) + Return a table of all unused dataobjects and (optionally) prints them to the chat +*******************************]] +local unused +function lib:GetUnused(verbose) + unused = unused or {} + if(verbose) then print("Unused LDB objects:") end + for name, dataobj in LDB:DataObjectIterator() do + unused[name] = not objects[name] and dataobj + if(verbose and unused[name]) then + print(name) + end + end + return unused +end + +--[[***************************** + lib:Embed(target) + Embeds the library functions in your own frame/table +*******************************]] +function lib:Embed(target) + for k,v in pairs(lib) do + target[k] = v + end +end + +LDB.RegisterCallback(lib, "LibDataBroker_DataObjectCreated", function (event, name, dataobj) + if(not objects[name]) then return end + for object in pairs(objects[name]) do + object:SetDataObject(dataobj) + end +end) + +--[[################################## + Block Prototype Functions +###################################]] + +Prototype.UpdateFunctions = updateFunctions + +--[[***************************** + Prototype:SetDataObject([dataObject]) + Sets the prototype's displayed dataObject + Returns: + true: dataObject set + false: waiting for dataobject to be created + nil: no dataObject set +*******************************]] +function Prototype:SetDataObject(arg1) + if(self.DataObject) then + self.DataObject = nil + LDB.UnregisterCallback(self, "LibDataBroker_AttributeChanged_"..self.name) + objects[self.name][self] = nil + self:Hide() + end + + if(not arg1) then return end + + local name, dataobj = getDataObject(arg1) + + self.name = name + objects[name] = objects[name] or {} + objects[name][self] = true + + if(not dataobj) then return false end + self.DataObject = dataobj + LDB.RegisterCallback(self, "LibDataBroker_AttributeChanged_"..name, self.AttributeChanged, self) + self:Update() + self:Show() + return true +end + +--[[***************************** + Prototype:Update("attribute" or nil) + Update one or all attributes from the dataobject +*******************************]] +function Prototype:Update(attr) + if(attr) then + if(self.UpdateFunctions and self.UpdateFunctions[attr]) then + self.UpdateFunctions[attr](self, attr, self.DataObject) + end + else + self:Update("icon") + self:Update("text") + self:Update("tooltip") + self:Update("OnClick") + end +end + +function Prototype:AttributeChanged(event, name, attr, value, dataobj) + self:Update(attr, dataobj) +end + +--[[***************************** + Prototype:Style(optionsTable) + Callback to initialize and style the block +*******************************]] +function Prototype:Style(opt) + -- Default dimensions + self:SetWidth(opt.width) + self:SetHeight(opt.height) + self:SetScale(opt.scale) + self:SetAlpha(opt.alpha) + + if(not opt.noIcon) then -- Icon left side + local icon = self:CreateTexture(nil, "OVERLAY") + icon:SetPoint("TOPLEFT") + icon:SetPoint("BOTTOMLEFT") + icon:SetWidth(opt.height) + self.Icon = icon + end + + if(not opt.noText) then -- Text right + local text = self:CreateFontString(nil, "OVERLAY", opt.fontObject) + if(not opt.fontObject) then + text:SetFont(opt.font, opt.fontSize, opt.fontStyle) + if(not opt.noShadow) then + text:SetShadowOffset(opt.shadowX, opt.shadowY) + end + end + text:SetTextColor(unpack(opt.textColor)) + text:SetJustifyH("CENTER") + if(self.Icon) then -- Don't overlap the icon! + text:SetPoint("TOPLEFT", self.Icon, "TOPRIGHT", 5, 0) + else + text:SetPoint("TOPLEFT") + end + text:SetPoint("BOTTOMLEFT") + self.Text = text + elseif(self.Icon) then + self:SetWidth(opt.height) + end +end + +--[[################################## + Private Object Functions +###################################]] + +getDataObject = function(arg1) + if(type(arg1) == "table") then + return LDB:GetNameByDataObject(arg1), arg1 + else + return arg1, LDB:GetDataObjectByName(arg1) + end +end + +local function getTipAnchor(frame) + local x,y = frame:GetCenter() + if not x or not y then return "TOPLEFT", "BOTTOMLEFT" end + local hhalf = (x > UIParent:GetWidth()*2/3) and "RIGHT" or (x < UIParent:GetWidth()/3) and "LEFT" or "" + local vhalf = (y > UIParent:GetHeight()/2) and "TOP" or "BOTTOM" + return vhalf..hhalf, frame, (vhalf == "TOP" and "BOTTOM" or "TOP")..hhalf +end + +local function showTooltip(self) + local dataobj = self.DataObject + local frame = dataobj.tooltip or GameTooltip + frame:SetOwner(self, getTipAnchor(self)) + if(not dataobj.tooltip and dataobj.OnTooltipShow) then + dataobj.OnTooltipShow(frame) + end + frame:Show() +end + +local function hideTooltip(self) + local frame = self.DataObject.tooltip or GameTooltip + frame:Hide() +end + +local taggedObject +local function tag(word) + return taggedObject.DataObject[word] or taggedObject[word] +end + +--[[################################## + Default Update functions +###################################]] + +updateFunctions.icon = function(self, attr, dataobj) + if(not self.Icon) then return end + self.Icon:SetTexture(dataobj.icon) + self:Update("iconCoords") + self:Update("iconR") +end +updateFunctions.iconCoords = function(self, attr, dataobj) + if(dataobj.iconCoords) then + self.Icon:SetTexCoord(unpack(dataobj.iconCoords)) + else + self.Icon:SetTexCoord(0, 1, 0, 1) + end +end +updateFunctions.iconR = function(self, attr, dataobj) + self.Icon:SetVertexColor(dataobj.iconR or 1, dataobj.iconG or 1, dataobj.iconB or 1) + end +updateFunctions.text = function(self, attr, dataobj) + if(not self.Text) then return end + if(self.tagString) then + taggedObject = self + local text = self.tagString:gsub("%[(%w+)%]", tag) + self.Text:SetText(text) + else + local text = self.useLabel and (dataobj.label or self.name) or "" + if(dataobj.text) then + if(self.useLabel) then + text = text..": "..dataobj.text + else + text = dataobj.text + end + end + self.Text:SetText(text) + end + local iconWidth = self.Icon and self.Icon:GetWidth()+5 or 0 + local textWidth = self.Text:GetWidth() or 0 + self:SetWidth(iconWidth+textWidth) +end +updateFunctions.OnEnter = function(self, attr, dataobj) + self:SetScript("OnEnter", (dataobj.tooltip and showTooltip) or dataobj.OnEnter or (dataobj.OnTooltipShow and showTooltip)) +end +updateFunctions.OnLeave = function(self, attr, dataobj) + self:SetScript("OnLeave", (dataobj.tooltip and hideTooltip) or dataobj.OnLeave or (dataobj.OnTooltipShow and hideTooltip)) +end +updateFunctions.tooltip = function(self, attr, dataobj) + self:Update("OnEnter") + self:Update("OnLeave") +end +updateFunctions.OnClick = function(self, attr, dataobj) + self:SetScript("OnClick", dataobj.OnClick) +end + +updateFunctions.value = updateFunctions.text +updateFunctions.suffix = updateFunctions.text +updateFunctions.iconG = updateFunctions.iconR +updateFunctions.iconB = updateFunctions.iconR +updateFunctions.OnTooltipShow = updateFunctions.tooltip \ No newline at end of file diff --git a/libs/LibDataBroker-1.1.lua b/libs/LibDataBroker-1.1.lua new file mode 100755 index 0000000..f47c0cd --- /dev/null +++ b/libs/LibDataBroker-1.1.lua @@ -0,0 +1,90 @@ + +assert(LibStub, "LibDataBroker-1.1 requires LibStub") +assert(LibStub:GetLibrary("CallbackHandler-1.0", true), "LibDataBroker-1.1 requires CallbackHandler-1.0") + +local lib, oldminor = LibStub:NewLibrary("LibDataBroker-1.1", 4) +if not lib then return end +oldminor = oldminor or 0 + + +lib.callbacks = lib.callbacks or LibStub:GetLibrary("CallbackHandler-1.0"):New(lib) +lib.attributestorage, lib.namestorage, lib.proxystorage = lib.attributestorage or {}, lib.namestorage or {}, lib.proxystorage or {} +local attributestorage, namestorage, callbacks = lib.attributestorage, lib.namestorage, lib.callbacks + +if oldminor < 2 then + lib.domt = { + __metatable = "access denied", + __index = function(self, key) return attributestorage[self] and attributestorage[self][key] end, + } +end + +if oldminor < 3 then + lib.domt.__newindex = function(self, key, value) + if not attributestorage[self] then attributestorage[self] = {} end + if attributestorage[self][key] == value then return end + attributestorage[self][key] = value + local name = namestorage[self] + if not name then return end + callbacks:Fire("LibDataBroker_AttributeChanged", name, key, value, self) + callbacks:Fire("LibDataBroker_AttributeChanged_"..name, name, key, value, self) + callbacks:Fire("LibDataBroker_AttributeChanged_"..name.."_"..key, name, key, value, self) + callbacks:Fire("LibDataBroker_AttributeChanged__"..key, name, key, value, self) + end +end + +if oldminor < 2 then + function lib:NewDataObject(name, dataobj) + if self.proxystorage[name] then return end + + if dataobj then + assert(type(dataobj) == "table", "Invalid dataobj, must be nil or a table") + self.attributestorage[dataobj] = {} + for i,v in pairs(dataobj) do + self.attributestorage[dataobj][i] = v + dataobj[i] = nil + end + end + dataobj = setmetatable(dataobj or {}, self.domt) + self.proxystorage[name], self.namestorage[dataobj] = dataobj, name + self.callbacks:Fire("LibDataBroker_DataObjectCreated", name, dataobj) + return dataobj + end +end + +if oldminor < 1 then + function lib:DataObjectIterator() + return pairs(self.proxystorage) + end + + function lib:GetDataObjectByName(dataobjectname) + return self.proxystorage[dataobjectname] + end + + function lib:GetNameByDataObject(dataobject) + return self.namestorage[dataobject] + end +end + +if oldminor < 4 then + local next = pairs(attributestorage) + function lib:pairs(dataobject_or_name) + local t = type(dataobject_or_name) + assert(t == "string" or t == "table", "Usage: ldb:pairs('dataobjectname') or ldb:pairs(dataobject)") + + local dataobj = self.proxystorage[dataobject_or_name] or dataobject_or_name + assert(attributestorage[dataobj], "Data object not found") + + return next, attributestorage[dataobj], nil + end + + local ipairs_iter = ipairs(attributestorage) + function lib:ipairs(dataobject_or_name) + local t = type(dataobject_or_name) + assert(t == "string" or t == "table", "Usage: ldb:ipairs('dataobjectname') or ldb:ipairs(dataobject)") + + local dataobj = self.proxystorage[dataobject_or_name] or dataobject_or_name + assert(attributestorage[dataobj], "Data object not found") + + return ipairs_iter, attributestorage[dataobj], 0 + end +end diff --git a/libs/LibStub.lua b/libs/LibStub.lua new file mode 100755 index 0000000..0a41ac0 --- /dev/null +++ b/libs/LibStub.lua @@ -0,0 +1,30 @@ +-- LibStub is a simple versioning stub meant for use in Libraries. http://www.wowace.com/wiki/LibStub for more info +-- LibStub is hereby placed in the Public Domain Credits: Kaelten, Cladhaire, ckknight, Mikk, Ammo, Nevcairiel, joshborke +local LIBSTUB_MAJOR, LIBSTUB_MINOR = "LibStub", 2 -- NEVER MAKE THIS AN SVN REVISION! IT NEEDS TO BE USABLE IN ALL REPOS! +local LibStub = _G[LIBSTUB_MAJOR] + +if not LibStub or LibStub.minor < LIBSTUB_MINOR then + LibStub = LibStub or {libs = {}, minors = {} } + _G[LIBSTUB_MAJOR] = LibStub + LibStub.minor = LIBSTUB_MINOR + + function LibStub:NewLibrary(major, minor) + assert(type(major) == "string", "Bad argument #2 to `NewLibrary' (string expected)") + minor = assert(tonumber(strmatch(minor, "%d+")), "Minor version must either be a number or contain a number.") + + local oldminor = self.minors[major] + if oldminor and oldminor >= minor then return nil end + self.minors[major], self.libs[major] = minor, self.libs[major] or {} + return self.libs[major], oldminor + end + + function LibStub:GetLibrary(major, silent) + if not self.libs[major] and not silent then + error(("Cannot find a library instance of %q."):format(tostring(major)), 2) + end + return self.libs[major], self.minors[major] + end + + function LibStub:IterateLibraries() return pairs(self.libs) end + setmetatable(LibStub, { __call = LibStub.GetLibrary }) +end -- 1.7.9.5