From 0305bc5bcd03b652ccbdd63414838c42077f4d0d Mon Sep 17 00:00:00 2001 From: James Whitehead II Date: Wed, 26 Mar 2008 12:01:17 +0000 Subject: [PATCH] Removed Dongle from scm control --- Dongle.lua | 1431 ------------------------------------------------------------ 1 file changed, 1431 deletions(-) delete mode 100755 Dongle.lua diff --git a/Dongle.lua b/Dongle.lua deleted file mode 100755 index 804eb98..0000000 --- a/Dongle.lua +++ /dev/null @@ -1,1431 +0,0 @@ ---[[------------------------------------------------------------------------- - Copyright (c) 2006-2007, Dongle Development Team - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are - met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - * Neither the name of the Dongle Development Team nor the names of - its contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ----------------------------------------------------------------------------]] -local major = "DongleStub" -local minor = tonumber(string.match("$Revision: 313 $", "(%d+)") or 1) - -local g = getfenv(0) - -if not g.DongleStub or g.DongleStub:IsNewerVersion(major, minor) then - local lib = setmetatable({}, { - __call = function(t,k) - if type(t.versions) == "table" and t.versions[k] then - return t.versions[k].instance - else - error("Cannot find a library with name '"..tostring(k).."'", 2) - end - end - }) - - function lib:IsNewerVersion(major, minor) - local versionData = self.versions and self.versions[major] - - -- If DongleStub versions have differing major version names - -- such as DongleStub-Beta0 and DongleStub-1.0-RC2 then a second - -- instance will be loaded, with older logic. This code attempts - -- to compensate for that by matching the major version against - -- "^DongleStub", and handling the version check correctly. - - if major:match("^DongleStub") then - local oldmajor,oldminor = self:GetVersion() - if self.versions and self.versions[oldmajor] then - return minor > oldminor - else - return true - end - end - - if not versionData then return true end - local oldmajor,oldminor = versionData.instance:GetVersion() - return minor > oldminor - end - - local function NilCopyTable(src, dest) - for k,v in pairs(dest) do dest[k] = nil end - for k,v in pairs(src) do dest[k] = v end - end - - function lib:Register(newInstance, activate, deactivate) - assert(type(newInstance.GetVersion) == "function", - "Attempt to register a library with DongleStub that does not have a 'GetVersion' method.") - - local major,minor = newInstance:GetVersion() - assert(type(major) == "string", - "Attempt to register a library with DongleStub that does not have a proper major version.") - assert(type(minor) == "number", - "Attempt to register a library with DongleStub that does not have a proper minor version.") - - -- Generate a log of all library registrations - if not self.log then self.log = {} end - table.insert(self.log, string.format("Register: %s, %s", major, minor)) - - if not self:IsNewerVersion(major, minor) then return false end - if not self.versions then self.versions = {} end - - local versionData = self.versions[major] - if not versionData then - -- New major version - versionData = { - ["instance"] = newInstance, - ["deactivate"] = deactivate, - } - - self.versions[major] = versionData - if type(activate) == "function" then - table.insert(self.log, string.format("Activate: %s, %s", major, minor)) - activate(newInstance) - end - return newInstance - end - - local oldDeactivate = versionData.deactivate - local oldInstance = versionData.instance - - versionData.deactivate = deactivate - - local skipCopy - if type(activate) == "function" then - table.insert(self.log, string.format("Activate: %s, %s", major, minor)) - skipCopy = activate(newInstance, oldInstance) - end - - -- Deactivate the old libary if necessary - if type(oldDeactivate) == "function" then - local major, minor = oldInstance:GetVersion() - table.insert(self.log, string.format("Deactivate: %s, %s", major, minor)) - oldDeactivate(oldInstance, newInstance) - end - - -- Re-use the old table, and discard the new one - if not skipCopy then - NilCopyTable(newInstance, oldInstance) - end - return oldInstance - end - - function lib:GetVersion() return major,minor end - - local function Activate(new, old) - -- This code ensures that we'll move the versions table even - -- if the major version names are different, in the case of - -- DongleStub - if not old then old = g.DongleStub end - - if old then - new.versions = old.versions - new.log = old.log - end - g.DongleStub = new - end - - -- Actually trigger libary activation here - local stub = g.DongleStub or lib - lib = stub:Register(lib, Activate) -end - ---[[------------------------------------------------------------------------- - Begin Library Implementation ----------------------------------------------------------------------------]] - -local major = "Dongle-1.1" -local minor = tonumber(string.match("$Revision: 647 $", "(%d+)") or 1) - -assert(DongleStub, string.format("%s requires DongleStub.", major)) - -if not DongleStub:IsNewerVersion(major, minor) then return end - -local Dongle = {} -local methods = { - "RegisterEvent", "UnregisterEvent", "UnregisterAllEvents", "IsEventRegistered", - "RegisterMessage", "UnregisterMessage", "UnregisterAllMessages", "TriggerMessage", "IsMessageRegistered", - "ScheduleTimer", "ScheduleRepeatingTimer", "CancelTimer", "IsTimerScheduled", - "EnableDebug", "IsDebugEnabled", "Print", "PrintF", "Debug", "DebugF", "Echo", "EchoF", - "InitializeDB", - "InitializeSlashCommand", - "NewModule", "HasModule", "IterateModules", -} - -local registry = {} -local lookup = {} -local loadqueue = {} -local loadorder = {} -local events = {} -local databases = {} -local commands = {} -local messages = {} -local timers = {} -local heap = {} - -local frame - ---[[------------------------------------------------------------------------- - Message Localization ----------------------------------------------------------------------------]] - -local L = { - ["ADDMESSAGE_REQUIRED"] = "The frame you specify must have an 'AddMessage' method.", - ["ALREADY_REGISTERED"] = "A Dongle with the name '%s' is already registered.", - ["BAD_ARGUMENT"] = "bad argument #%d to '%s' (%s expected, got %s)", - ["BAD_ARGUMENT_DB"] = "bad argument #%d to '%s' (DongleDB expected)", - ["CANNOT_DELETE_ACTIVE_PROFILE"] = "You cannot delete your active profile. Change profiles, then attempt to delete.", - ["DELETE_NONEXISTANT_PROFILE"] = "You cannot delete a non-existant profile.", - ["MUST_CALLFROM_DBOBJECT"] = "You must call '%s' from a Dongle database object.", - ["MUST_CALLFROM_REGISTERED"] = "You must call '%s' from a registered Dongle.", - ["MUST_CALLFROM_SLASH"] = "You must call '%s' from a Dongle slash command object.", - ["PROFILE_DOES_NOT_EXIST"] = "Profile '%s' doesn't exist.", - ["REPLACE_DEFAULTS"] = "You are attempting to register defaults with a database that already contains defaults.", - ["SAME_SOURCE_DEST"] = "Source/Destination profile cannot be the same profile.", - ["EVENT_REGISTER_SPECIAL"] = "You cannot register for the '%s' event. Use the '%s' method instead.", - ["Unknown"] = "Unknown", - ["INJECTDB_USAGE"] = "Usage: DongleCmd:InjectDBCommands(db, ['copy', 'delete', 'list', 'reset', 'set'])", - ["DBSLASH_PROFILE_COPY_DESC"] = "profile copy - Copies profile into your current profile.", - ["DBSLASH_PROFILE_COPY_PATTERN"] = "^profile copy (.+)$", - ["DBSLASH_PROFILE_DELETE_DESC"] = "profile delete - Deletes the profile .", - ["DBSLASH_PROFILE_DELETE_PATTERN"] = "^profile delete (.+)$", - ["DBSLASH_PROFILE_LIST_DESC"] = "profile list - Lists all valid profiles.", - ["DBSLASH_PROFILE_LIST_PATTERN"] = "^profile list$", - ["DBSLASH_PROFILE_RESET_DESC"] = "profile reset - Resets the current profile.", - ["DBSLASH_PROFILE_RESET_PATTERN"] = "^profile reset$", - ["DBSLASH_PROFILE_SET_DESC"] = "profile set - Sets the current profile to .", - ["DBSLASH_PROFILE_SET_PATTERN"] = "^profile set (.+)$", - ["DBSLASH_PROFILE_LIST_OUT"] = "Profile List:", -} - ---[[------------------------------------------------------------------------- - Utility functions for Dongle use ----------------------------------------------------------------------------]] - -local function assert(level,condition,message) - if not condition then - error(message,level) - end -end - -local function argcheck(value, num, ...) - if type(num) ~= "number" then - error(L["BAD_ARGUMENT"]:format(2, "argcheck", "number", type(num)), 1) - end - - for i=1,select("#", ...) do - if type(value) == select(i, ...) then return end - end - - local types = strjoin(", ", ...) - local name = string.match(debugstack(2,2,0), ": in function [`<](.-)['>]") - error(L["BAD_ARGUMENT"]:format(num, name, types, type(value)), 3) -end - -local function safecall(func,...) - local success,err = pcall(func,...) - if not success then - geterrorhandler()(err) - end -end - ---[[------------------------------------------------------------------------- - Dongle constructor, and DongleModule system ----------------------------------------------------------------------------]] - -function Dongle:New(name, obj) - argcheck(name, 2, "string") - argcheck(obj, 3, "table", "nil") - - if not obj then - obj = {} - end - - if registry[name] then - error(string.format(L["ALREADY_REGISTERED"], name)) - end - - local reg = {["obj"] = obj, ["name"] = name} - - registry[name] = reg - lookup[obj] = reg - lookup[name] = reg - - for k,v in pairs(methods) do - obj[v] = self[v] - end - - -- Add this Dongle to the end of the queue - table.insert(loadqueue, obj) - return obj,name -end - -function Dongle:NewModule(name, obj) - local reg = lookup[self] - assert(3, reg, string.format(L["MUST_CALLFROM_REGISTERED"], "NewModule")) - argcheck(name, 2, "string") - argcheck(obj, 3, "table", "nil") - - obj,name = Dongle:New(name, obj) - - if not reg.modules then reg.modules = {} end - reg.modules[obj] = obj - reg.modules[name] = obj - - return obj,name -end - -function Dongle:HasModule(module) - local reg = lookup[self] - assert(3, reg, string.format(L["MUST_CALLFROM_REGISTERED"], "HasModule")) - argcheck(module, 2, "string", "table") - - return reg.modules and reg.modules[module] -end - -local function ModuleIterator(t, name) - if not t then return end - local obj - repeat - name,obj = next(t, name) - until type(name) == "string" or not name - - return name,obj -end - -function Dongle:IterateModules() - local reg = lookup[self] - assert(3, reg, string.format(L["MUST_CALLFROM_REGISTERED"], "IterateModules")) - - return ModuleIterator, reg.modules -end - ---[[------------------------------------------------------------------------- - Event registration system ----------------------------------------------------------------------------]] - -local function OnEvent(frame, event, ...) - local eventTbl = events[event] - if eventTbl then - for obj,func in pairs(eventTbl) do - if type(func) == "string" then - if type(obj[func]) == "function" then - safecall(obj[func], obj, event, ...) - end - else - safecall(func, event, ...) - end - end - end -end - -local specialEvents = { - ["PLAYER_LOGIN"] = "Enable", - ["PLAYER_LOGOUT"] = "Disable", -} - -function Dongle:RegisterEvent(event, func) - local reg = lookup[self] - assert(3, reg, string.format(L["MUST_CALLFROM_REGISTERED"], "RegisterEvent")) - argcheck(event, 2, "string") - argcheck(func, 3, "string", "function", "nil") - - local special = (self ~= Dongle) and specialEvents[event] - if special then - error(string.format(L["EVENT_REGISTER_SPECIAL"], event, special), 3) - end - - -- Name the method the same as the event if necessary - if not func then func = event end - - if not events[event] then - events[event] = {} - frame:RegisterEvent(event) - end - events[event][self] = func -end - -function Dongle:UnregisterEvent(event) - local reg = lookup[self] - assert(3, reg, string.format(L["MUST_CALLFROM_REGISTERED"], "UnregisterEvent")) - argcheck(event, 2, "string") - - local tbl = events[event] - if tbl then - tbl[self] = nil - if not next(tbl) then - events[event] = nil - frame:UnregisterEvent(event) - end - end -end - -function Dongle:UnregisterAllEvents() - local reg = lookup[self] - assert(3, reg, string.format(L["MUST_CALLFROM_REGISTERED"], "UnregisterAllEvents")) - - for event,tbl in pairs(events) do - tbl[self] = nil - if not next(tbl) then - events[event] = nil - frame:UnregisterEvent(event) - end - end -end - -function Dongle:IsEventRegistered(event) - local reg = lookup[self] - assert(3, reg, string.format(L["MUST_CALLFROM_REGISTERED"], "IsEventRegistered")) - argcheck(event, 2, "string") - - local tbl = events[event] - return tbl -end - ---[[------------------------------------------------------------------------- - Inter-Addon Messaging System ----------------------------------------------------------------------------]] - -function Dongle:RegisterMessage(msg, func) - argcheck(self, 1, "table") - argcheck(msg, 2, "string") - argcheck(func, 3, "string", "function", "nil") - - -- Name the method the same as the message if necessary - if not func then func = msg end - - if not messages[msg] then - messages[msg] = {} - end - messages[msg][self] = func -end - -function Dongle:UnregisterMessage(msg) - argcheck(self, 1, "table") - argcheck(msg, 2, "string") - - local tbl = messages[msg] - if tbl then - tbl[self] = nil - if not next(tbl) then - messages[msg] = nil - end - end -end - -function Dongle:UnregisterAllMessages() - argcheck(self, 1, "table") - - for msg,tbl in pairs(messages) do - tbl[self] = nil - if not next(tbl) then - messages[msg] = nil - end - end -end - -function Dongle:TriggerMessage(msg, ...) - argcheck(self, 1, "table") - argcheck(msg, 2, "string") - local msgTbl = messages[msg] - if not msgTbl then return end - - for obj,func in pairs(msgTbl) do - if type(func) == "string" then - if type(obj[func]) == "function" then - safecall(obj[func], obj, msg, ...) - end - else - safecall(func, msg, ...) - end - end -end - -function Dongle:IsMessageRegistered(msg) - argcheck(self, 1, "table") - argcheck(msg, 2, "string") - - local tbl = messages[msg] - return tbl[self] -end - ---[[------------------------------------------------------------------------- - Timer System ----------------------------------------------------------------------------]] - -local function HeapSwap(i1, i2) - heap[i1], heap[i2] = heap[i2], heap[i1] -end - -local function HeapBubbleUp(index) - while index > 1 do - local parentIndex = math.floor(index / 2) - if heap[index].timeToFire < heap[parentIndex].timeToFire then - HeapSwap(index, parentIndex) - index = parentIndex - else - break - end - end -end - -local function HeapBubbleDown(index) - while 2 * index <= heap.lastIndex do - local leftIndex = 2 * index - local rightIndex = leftIndex + 1 - local current = heap[index] - local leftChild = heap[leftIndex] - local rightChild = heap[rightIndex] - - if not rightChild then - if leftChild.timeToFire < current.timeToFire then - HeapSwap(index, leftIndex) - index = leftIndex - else - break - end - else - if leftChild.timeToFire < current.timeToFire or - rightChild.timeToFire < current.timeToFire then - if leftChild.timeToFire < rightChild.timeToFire then - HeapSwap(index, leftIndex) - index = leftIndex - else - HeapSwap(index, rightIndex) - index = rightIndex - end - else - break - end - end - end -end - -local function OnUpdate(frame, elapsed) - local schedule = heap[1] - while schedule and schedule.timeToFire < GetTime() do - if schedule.cancelled then - HeapSwap(1, heap.lastIndex) - heap[heap.lastIndex] = nil - heap.lastIndex = heap.lastIndex - 1 - HeapBubbleDown(1) - else - if schedule.args then - safecall(schedule.func, schedule.name, unpack(schedule.args)) - else - safecall(schedule.func, schedule.name) - end - - if schedule.repeating then - schedule.timeToFire = schedule.timeToFire + schedule.repeating - HeapBubbleDown(1) - else - HeapSwap(1, heap.lastIndex) - heap[heap.lastIndex] = nil - heap.lastIndex = heap.lastIndex - 1 - HeapBubbleDown(1) - timers[schedule.name] = nil - end - end - schedule = heap[1] - end - if not schedule then frame:Hide() end -end - -function Dongle:ScheduleTimer(name, func, delay, ...) - argcheck(self, 1, "table") - argcheck(name, 2, "string") - argcheck(func, 3, "function") - argcheck(delay, 4, "number") - - if Dongle:IsTimerScheduled(name) then - Dongle:CancelTimer(name) - end - - local schedule = {} - timers[name] = schedule - schedule.timeToFire = GetTime() + delay - schedule.func = func - schedule.name = name - if select('#', ...) ~= 0 then - schedule.args = { ... } - end - - if heap.lastIndex then - heap.lastIndex = heap.lastIndex + 1 - else - heap.lastIndex = 1 - end - heap[heap.lastIndex] = schedule - HeapBubbleUp(heap.lastIndex) - if not frame:IsShown() then - frame:Show() - end -end - -function Dongle:ScheduleRepeatingTimer(name, func, delay, ...) - Dongle:ScheduleTimer(name, func, delay, ...) - timers[name].repeating = delay -end - -function Dongle:IsTimerScheduled(name) - argcheck(self, 1, "table") - argcheck(name, 2, "string") - local schedule = timers[name] - if schedule then - return true, schedule.timeToFire - GetTime() - else - return false - end -end - -function Dongle:CancelTimer(name) - argcheck(self, 1, "table") - argcheck(name, 2, "string") - local schedule = timers[name] - if not schedule then return end - schedule.cancelled = true - timers[name] = nil -end - ---[[------------------------------------------------------------------------- - Debug and Print utility functions ----------------------------------------------------------------------------]] - -function Dongle:EnableDebug(level, frame) - local reg = lookup[self] - assert(3, reg, string.format(L["MUST_CALLFROM_REGISTERED"], "EnableDebug")) - argcheck(level, 2, "number", "nil") - argcheck(frame, 3, "table", "nil") - - assert(3, type(frame) == "nil" or type(frame.AddMessage) == "function", L["ADDMESSAGE_REQUIRED"]) - reg.debugFrame = frame or ChatFrame1 - reg.debugLevel = level -end - -function Dongle:IsDebugEnabled() - local reg = lookup[self] - assert(3, reg, string.format(L["MUST_CALLFROM_REGISTERED"], "EnableDebug")) - - return reg.debugLevel, reg.debugFrame -end - -local function argsToStrings(a1, ...) - if select("#", ...) > 0 then - return tostring(a1), argsToStrings(...) - else - return tostring(a1) - end -end - -local function printHelp(obj, method, header, frame, msg, ...) - local reg = lookup[obj] - assert(4, reg, string.format(L["MUST_CALLFROM_REGISTERED"], method)) - - local name = reg.name - - if header then - msg = "|cFF33FF99"..name.."|r: "..tostring(msg) - end - - if select("#", ...) > 0 then - msg = string.join(", ", msg, argsToStrings(...)) - end - - frame:AddMessage(msg) -end - -local function printFHelp(obj, method, header, frame, msg, ...) - local reg = lookup[obj] - assert(4, reg, string.format(L["MUST_CALLFROM_REGISTERED"], method)) - - local name = reg.name - local success,txt - - if header then - msg = "|cFF33FF99%s|r: " .. msg - success,txt = pcall(string.format, msg, name, ...) - else - success,txt = pcall(string.format, msg, ...) - end - - if success then - frame:AddMessage(txt) - else - error(string.gsub(txt, "'%?'", string.format("'%s'", method)), 3) - end -end - -function Dongle:Print(msg, ...) - local reg = lookup[self] - assert(1, reg, string.format(L["MUST_CALLFROM_REGISTERED"], "Print")) - argcheck(msg, 2, "number", "string", "boolean", "table", "function", "thread", "userdata") - return printHelp(self, "Print", true, DEFAULT_CHAT_FRAME, msg, ...) -end - -function Dongle:PrintF(msg, ...) - local reg = lookup[self] - assert(1, reg, string.format(L["MUST_CALLFROM_REGISTERED"], "PrintF")) - argcheck(msg, 2, "number", "string", "boolean", "table", "function", "thread", "userdata") - return printFHelp(self, "PrintF", true, DEFAULT_CHAT_FRAME, msg, ...) -end - -function Dongle:Echo(msg, ...) - local reg = lookup[self] - assert(1, reg, string.format(L["MUST_CALLFROM_REGISTERED"], "Echo")) - argcheck(msg, 2, "number", "string", "boolean", "table", "function", "thread", "userdata") - return printHelp(self, "Echo", false, DEFAULT_CHAT_FRAME, msg, ...) -end - -function Dongle:EchoF(msg, ...) - local reg = lookup[self] - assert(1, reg, string.format(L["MUST_CALLFROM_REGISTERED"], "EchoF")) - argcheck(msg, 2, "number", "string", "boolean", "table", "function", "thread", "userdata") - return printFHelp(self, "EchoF", false, DEFAULT_CHAT_FRAME, msg, ...) -end - -function Dongle:Debug(level, ...) - local reg = lookup[self] - assert(3, reg, string.format(L["MUST_CALLFROM_REGISTERED"], "Debug")) - argcheck(level, 2, "number") - - if reg.debugLevel and level <= reg.debugLevel then - printHelp(self, "Debug", true, reg.debugFrame, ...) - end -end - -function Dongle:DebugF(level, ...) - local reg = lookup[self] - assert(3, reg, string.format(L["MUST_CALLFROM_REGISTERED"], "DebugF")) - argcheck(level, 2, "number") - - if reg.debugLevel and level <= reg.debugLevel then - printFHelp(self, "DebugF", true, reg.debugFrame, ...) - end -end - ---[[------------------------------------------------------------------------- - Database System ----------------------------------------------------------------------------]] - -local dbMethods = { - "RegisterDefaults", "SetProfile", "GetProfiles", "DeleteProfile", "CopyProfile", - "GetCurrentProfile", "ResetProfile", "ResetDB", - "RegisterNamespace", -} - -local function copyTable(src) - local dest = {} - for k,v in pairs(src) do - if type(k) == "table" then - k = copyTable(k) - end - if type(v) == "table" then - v = copyTable(v) - end - dest[k] = v - end - return dest -end - -local function copyDefaults(dest, src, force) - for k,v in pairs(src) do - if k == "*" then - if type(v) == "table" then - -- Values are tables, need some magic here - local mt = { - __cache = {}, - __index = function(t,k) - local mt = getmetatable(dest) - local cache = rawget(mt, "__cache") - local tbl = rawget(cache, k) - if not tbl then - local parent = t - local parentkey = k - tbl = copyTable(v) - rawset(cache, k, tbl) - local mt = getmetatable(tbl) - if not mt then - mt = {} - setmetatable(tbl, mt) - end - local newindex = function(t,k,v) - rawset(parent, parentkey, t) - rawset(t, k, v) - end - rawset(mt, "__newindex", newindex) - end - return tbl - end, - } - setmetatable(dest, mt) - -- Now need to set the metatable on any child tables - for dkey,dval in pairs(dest) do - copyDefaults(dval, v) - end - else - -- Values are not tables, so this is just a simple return - local mt = {__index = function() return v end} - setmetatable(dest, mt) - end - elseif type(v) == "table" then - if not dest[k] then dest[k] = {} end - copyDefaults(dest[k], v, force) - else - if (dest[k] == nil) or force then - dest[k] = v - end - end - end -end - -local function removeDefaults(db, defaults) - if not db then return end - for k,v in pairs(defaults) do - if k == "*" and type(v) == "table" then - -- check for any defaults that have been changed - local mt = getmetatable(db) - local cache = rawget(mt, "__cache") - - for cacheKey,cacheValue in pairs(cache) do - removeDefaults(cacheValue, v) - if next(cacheValue) ~= nil then - -- Something's changed - rawset(db, cacheKey, cacheValue) - end - end - -- Now loop through all the actual k,v pairs and remove - for key,value in pairs(db) do - removeDefaults(value, v) - end - elseif type(v) == "table" and db[k] then - removeDefaults(db[k], v) - if not next(db[k]) then - db[k] = nil - end - else - if db[k] == defaults[k] then - db[k] = nil - end - end - end -end - -local function initSection(db, section, svstore, key, defaults) - local sv = rawget(db, "sv") - - local tableCreated - if not sv[svstore] then sv[svstore] = {} end - if not sv[svstore][key] then - sv[svstore][key] = {} - tableCreated = true - end - - local tbl = sv[svstore][key] - - if defaults then - copyDefaults(tbl, defaults) - end - rawset(db, section, tbl) - - return tableCreated, tbl -end - -local dbmt = { - __index = function(t, section) - local keys = rawget(t, "keys") - local key = keys[section] - if key then - local defaultTbl = rawget(t, "defaults") - local defaults = defaultTbl and defaultTbl[section] - - if section == "profile" then - local new = initSection(t, section, "profiles", key, defaults) - if new then - Dongle:TriggerMessage("DONGLE_PROFILE_CREATED", t, rawget(t, "parent"), rawget(t, "sv_name"), key) - end - elseif section == "profiles" then - local sv = rawget(t, "sv") - if not sv.profiles then sv.profiles = {} end - rawset(t, "profiles", sv.profiles) - elseif section == "global" then - local sv = rawget(t, "sv") - if not sv.global then sv.global = {} end - if defaults then - copyDefaults(sv.global, defaults) - end - rawset(t, section, sv.global) - else - initSection(t, section, section, key, defaults) - end - end - - return rawget(t, section) - end -} - -local function initdb(parent, name, defaults, defaultProfile, olddb) - -- This allows us to use an arbitrary table as base instead of saved variable name - local sv - if type(name) == "string" then - sv = getglobal(name) - if not sv then - sv = {} - setglobal(name, sv) - end - elseif type(name) == "table" then - sv = name - end - - -- Generate the database keys for each section - local char = string.format("%s - %s", UnitName("player"), GetRealmName()) - local realm = GetRealmName() - local class = select(2, UnitClass("player")) - local race = select(2, UnitRace("player")) - local faction = UnitFactionGroup("player") - local factionrealm = string.format("%s - %s", faction, realm) - - -- Make a container for profile keys - if not sv.profileKeys then sv.profileKeys = {} end - - -- Try to get the profile selected from the char db - local profileKey = sv.profileKeys[char] or defaultProfile or char - sv.profileKeys[char] = profileKey - - local keyTbl= { - ["char"] = char, - ["realm"] = realm, - ["class"] = class, - ["race"] = race, - ["faction"] = faction, - ["factionrealm"] = factionrealm, - ["global"] = true, - ["profile"] = profileKey, - ["profiles"] = true, -- Don't create until we need - } - - -- If we've been passed an old database, clear it out - if olddb then - for k,v in pairs(olddb) do olddb[k] = nil end - end - - -- Give this database the metatable so it initializes dynamically - local db = setmetatable(olddb or {}, dbmt) - - -- Copy methods locally - for idx,method in pairs(dbMethods) do - db[method] = Dongle[method] - end - - -- Set some properties in the object we're returning - db.profiles = sv.profiles - db.keys = keyTbl - db.sv = sv - db.sv_name = name - db.defaults = defaults - db.parent = parent - - databases[db] = true - - return db -end - -function Dongle:InitializeDB(name, defaults, defaultProfile) - local reg = lookup[self] - assert(3, reg, string.format(L["MUST_CALLFROM_REGISTERED"], "InitializeDB")) - argcheck(name, 2, "string", "table") - argcheck(defaults, 3, "table", "nil") - argcheck(defaultProfile, 4, "string", "nil") - - return initdb(self, name, defaults, defaultProfile) -end - --- This function operates on a Dongle DB object -function Dongle.RegisterDefaults(db, defaults) - assert(3, databases[db], string.format(L["MUST_CALLFROM_DBOBJECT"], "RegisterDefaults")) - assert(3, db.defaults == nil, L["REPLACE_DEFAUTS"]) - argcheck(defaults, 2, "table") - - for section,key in pairs(db.keys) do - if defaults[section] and rawget(db, section) then - copyDefaults(db[section], defaults[section]) - end - end - - db.defaults = defaults -end - -function Dongle:ClearDBDefaults() - for db in pairs(databases) do - local defaults = db.defaults - local sv = db.sv - - if db and defaults then - for section,key in pairs(db.keys) do - if defaults[section] and rawget(db, section) then - removeDefaults(db[section], defaults[section]) - end - end - - for section,key in pairs(db.keys) do - local tbl = rawget(db, section) - if tbl and not next(tbl) then - if sv[section] then - if type(key) == "string" then - sv[section][key] = nil - else - sv[section] = nil - end - end - end - end - end - end -end - -function Dongle.SetProfile(db, name) - assert(3, databases[db], string.format(L["MUST_CALLFROM_DBOBJECT"], "SetProfile")) - argcheck(name, 2, "string") - - local old = db.profile - local defaults = db.defaults and db.defaults.profile - - if defaults then - -- Remove the defaults from the old profile - removeDefaults(old, defaults) - end - - db.profile = nil - db.keys["profile"] = name - db.sv.profileKeys[db.keys.char] = name - - Dongle:TriggerMessage("DONGLE_PROFILE_CHANGED", db, db.parent, db.sv_name, db.keys.profile) -end - -function Dongle.GetProfiles(db, tbl) - assert(3, databases[db], string.format(L["MUST_CALLFROM_DBOBJECT"], "GetProfiles")) - argcheck(t, 2, "table", "nil") - - -- Clear the container table - if tbl then - for k,v in pairs(tbl) do tbl[k] = nil end - else - tbl = {} - end - - local i = 0 - for profileKey in pairs(db.profiles) do - i = i + 1 - tbl[i] = profileKey - end - - -- Add the current profile, if it hasn't been created yet - if rawget(db, "profile") == nil then - i = i + 1 - tbl[i] = db.keys.profile - end - - return tbl, i -end - -function Dongle.GetCurrentProfile(db) - assert(3, databases[db], string.format(L["MUST_CALLFROM_DBOBJECT"], "GetCurrentProfile")) - return db.keys.profile -end - -function Dongle.DeleteProfile(db, name) - assert(3, databases[db], string.format(L["MUST_CALLFROM_DBOBJECT"], "DeleteProfile")) - argcheck(name, 2, "string") - - if db.keys.profile == name then - error(L["CANNOT_DELETE_ACTIVE_PROFILE"], 2) - end - - assert(type(db.sv.profiles[name]) == "table", L["DELETE_NONEXISTANT_PROFILE"]) - - db.sv.profiles[name] = nil - Dongle:TriggerMessage("DONGLE_PROFILE_DELETED", db, db.parent, db.sv_name, name) -end - -function Dongle.CopyProfile(db, name) - assert(3, databases[db], string.format(L["MUST_CALLFROM_DBOBJECT"], "CopyProfile")) - argcheck(name, 2, "string") - - assert(3, db.keys.profile ~= name, L["SAME_SOURCE_DEST"]) - assert(3, type(db.sv.profiles[name]) == "table", string.format(L["PROFILE_DOES_NOT_EXIST"], name)) - - local profile = db.profile - local source = db.sv.profiles[name] - - copyDefaults(profile, source, true) - Dongle:TriggerMessage("DONGLE_PROFILE_COPIED", db, db.parent, db.sv_name, name, db.keys.profile) -end - -function Dongle.ResetProfile(db) - assert(3, databases[db], string.format(L["MUST_CALLFROM_DBOBJECT"], "ResetProfile")) - - local profile = db.profile - - for k,v in pairs(profile) do - profile[k] = nil - end - - local defaults = db.defaults and db.defaults.profile - if defaults then - copyDefaults(profile, defaults) - end - Dongle:TriggerMessage("DONGLE_PROFILE_RESET", db, db.parent, db.sv_name, db.keys.profile) -end - - -function Dongle.ResetDB(db, defaultProfile) - assert(3, databases[db], string.format(L["MUST_CALLFROM_DBOBJECT"], "ResetDB")) - argcheck(defaultProfile, 2, "nil", "string") - - local sv = db.sv - for k,v in pairs(sv) do - sv[k] = nil - end - - local parent = db.parent - - initdb(parent, db.sv_name, db.defaults, defaultProfile, db) - Dongle:TriggerMessage("DONGLE_DATABASE_RESET", db, parent, db.sv_name, db.keys.profile) - Dongle:TriggerMessage("DONGLE_PROFILE_CHANGED", db, db.parent, db.sv_name, db.keys.profile) - return db -end - -function Dongle.RegisterNamespace(db, name, defaults) - assert(3, databases[db], string.format(L["MUST_CALLFROM_DBOBJECT"], "RegisterNamespace")) - argcheck(name, 2, "string") - argcheck(defaults, 3, "nil", "table") - - local sv = db.sv - if not sv.namespaces then sv.namespaces = {} end - if not sv.namespaces[name] then - sv.namespaces[name] = {} - end - - local newDB = initdb(db, sv.namespaces[name], defaults, db.keys.profile) - -- Remove the :SetProfile method from newDB - newDB.SetProfile = nil - - if not db.children then db.children = {} end - table.insert(db.children, newDB) - return newDB -end - ---[[------------------------------------------------------------------------- - Slash Command System ----------------------------------------------------------------------------]] - -local slashCmdMethods = { - "InjectDBCommands", - "RegisterSlashHandler", - "PrintUsage", -} - -local function OnSlashCommand(cmd, cmd_line) - if cmd.patterns then - for idx,tbl in pairs(cmd.patterns) do - local pattern = tbl.pattern - if string.match(cmd_line, pattern) then - local handler = tbl.handler - if type(tbl.handler) == "string" then - local obj - -- Look in the command object before we look at the parent object - if cmd[handler] then obj = cmd end - if cmd.parent[handler] then obj = cmd.parent end - if obj then - obj[handler](obj, string.match(cmd_line, pattern)) - end - else - handler(string.match(cmd_line, pattern)) - end - return - end - end - end - cmd:PrintUsage() -end - -function Dongle:InitializeSlashCommand(desc, name, ...) - local reg = lookup[self] - assert(3, reg, string.format(L["MUST_CALLFROM_REGISTERED"], "InitializeSlashCommand")) - argcheck(desc, 2, "string") - argcheck(name, 3, "string") - argcheck(select(1, ...), 4, "string") - for i = 2,select("#", ...) do - argcheck(select(i, ...), i+2, "string") - end - - local cmd = {} - cmd.desc = desc - cmd.name = name - cmd.parent = self - cmd.slashes = { ... } - for idx,method in pairs(slashCmdMethods) do - cmd[method] = Dongle[method] - end - - local genv = getfenv(0) - - for i = 1,select("#", ...) do - genv["SLASH_"..name..tostring(i)] = "/"..select(i, ...) - end - - genv.SlashCmdList[name] = function(...) OnSlashCommand(cmd, ...) end - - commands[cmd] = true - - return cmd -end - -function Dongle.RegisterSlashHandler(cmd, desc, pattern, handler) - assert(3, commands[cmd], string.format(L["MUST_CALLFROM_SLASH"], "RegisterSlashHandler")) - - argcheck(desc, 2, "string") - argcheck(pattern, 3, "string") - argcheck(handler, 4, "function", "string") - - if not cmd.patterns then - cmd.patterns = {} - end - - table.insert(cmd.patterns, { - ["desc"] = desc, - ["handler"] = handler, - ["pattern"] = pattern, - }) -end - -function Dongle.PrintUsage(cmd) - assert(3, commands[cmd], string.format(L["MUST_CALLFROM_SLASH"], "PrintUsage")) - local parent = cmd.parent - - parent:Echo(cmd.desc.."\n".."/"..table.concat(cmd.slashes, ", /")..":\n") - if cmd.patterns then - for idx,tbl in ipairs(cmd.patterns) do - parent:Echo(" - " .. tbl.desc) - end - end -end - -local dbcommands = { - ["copy"] = { - L["DBSLASH_PROFILE_COPY_DESC"], - L["DBSLASH_PROFILE_COPY_PATTERN"], - "CopyProfile", - }, - ["delete"] = { - L["DBSLASH_PROFILE_DELETE_DESC"], - L["DBSLASH_PROFILE_DELETE_PATTERN"], - "DeleteProfile", - }, - ["list"] = { - L["DBSLASH_PROFILE_LIST_DESC"], - L["DBSLASH_PROFILE_LIST_PATTERN"], - }, - ["reset"] = { - L["DBSLASH_PROFILE_RESET_DESC"], - L["DBSLASH_PROFILE_RESET_PATTERN"], - "ResetProfile", - }, - ["set"] = { - L["DBSLASH_PROFILE_SET_DESC"], - L["DBSLASH_PROFILE_SET_PATTERN"], - "SetProfile", - }, -} - -function Dongle.InjectDBCommands(cmd, db, ...) - assert(3, commands[cmd], string.format(L["MUST_CALLFROM_SLASH"], "InjectDBCommands")) - assert(3, databases[db], string.format(L["BAD_ARGUMENT_DB"], 2, "InjectDBCommands")) - local argc = select("#", ...) - assert(3, argc > 0, L["INJECTDB_USAGE"]) - - for i=1,argc do - local cmdname = string.lower(select(i, ...)) - local entry = dbcommands[cmdname] - assert(entry, L["INJECTDB_USAGE"]) - local func = entry[3] - - local handler - if cmdname == "list" then - handler = function(...) - local profiles = db:GetProfiles() - db.parent:Print(L["DBSLASH_PROFILE_LIST_OUT"] .. "\n" .. strjoin("\n", unpack(profiles))) - end - else - handler = function(...) db[entry[3]](db, ...) end - end - - cmd:RegisterSlashHandler(entry[1], entry[2], handler) - end -end - ---[[------------------------------------------------------------------------- - Internal Message/Event Handlers ----------------------------------------------------------------------------]] - -local function PLAYER_LOGOUT(event) - Dongle:ClearDBDefaults() - for k,v in pairs(registry) do - local obj = v.obj - if type(obj["Disable"]) == "function" then - safecall(obj["Disable"], obj) - end - end -end - -local PLAYER_LOGIN -do - local lockPlayerLogin = false - - function PLAYER_LOGIN() - if lockPlayerLogin then return end - - lockPlayerLogin = true - - local obj = table.remove(loadorder, 1) - while obj do - if type(obj.Enable) == "function" then - safecall(obj.Enable, obj) - end - obj = table.remove(loadorder, 1) - end - - lockPlayerLogin = false - end -end - -local function ADDON_LOADED(event, ...) - local obj = table.remove(loadqueue, 1) - while obj do - table.insert(loadorder, obj) - - if type(obj.Initialize) == "function" then - safecall(obj.Initialize, obj) - end - - obj = table.remove(loadqueue, 1) - end - - if IsLoggedIn() then - PLAYER_LOGIN() - end -end - -local function DONGLE_PROFILE_CHANGED(msg, db, parent, sv_name, profileKey) - local children = db.children - if children then - for i,namespace in ipairs(children) do - local old = namespace.profile - local defaults = namespace.defaults and namespace.defaults.profile - - if defaults then - -- Remove the defaults from the old profile - removeDefaults(old, defaults) - end - - namespace.profile = nil - namespace.keys["profile"] = profileKey - end - end -end - ---[[------------------------------------------------------------------------- - DongleStub required functions and registration ----------------------------------------------------------------------------]] - -function Dongle:GetVersion() return major,minor end - -local function Activate(self, old) - if old then - registry = old.registry or registry - lookup = old.lookup or lookup - loadqueue = old.loadqueue or loadqueue - loadorder = old.loadorder or loadorder - events = old.events or events - databases = old.databases or databases - commands = old.commands or commands - messages = old.messages or messages - frame = old.frame or CreateFrame("Frame") - timers = old.timers or timers - heap = old.heap or heap - else - frame = CreateFrame("Frame") - local reg = {obj = self, name = "Dongle"} - registry[major] = reg - lookup[self] = reg - lookup[major] = reg - end - - self.registry = registry - self.lookup = lookup - self.loadqueue = loadqueue - self.loadorder = loadorder - self.events = events - self.databases = databases - self.commands = commands - self.messages = messages - self.frame = frame - self.timers = timers - self.heap = heap - - frame:SetScript("OnEvent", OnEvent) - frame:SetScript("OnUpdate", OnUpdate) - - -- Lets ensure the lookup table has our entry - -- This fixes an issue with upgrades - lookup[self] = lookup[major] - - -- Register for events using Dongle itself - self:RegisterEvent("ADDON_LOADED", ADDON_LOADED) - self:RegisterEvent("PLAYER_LOGIN", PLAYER_LOGIN) - self:RegisterEvent("PLAYER_LOGOUT", PLAYER_LOGOUT) - self:RegisterMessage("DONGLE_PROFILE_CHANGED", DONGLE_PROFILE_CHANGED) - - -- Convert all the modules handles - for name,obj in pairs(registry) do - for k,v in ipairs(methods) do - obj[k] = self[v] - end - end - - -- Convert all database methods - for db in pairs(databases) do - for idx,method in ipairs(dbMethods) do - db[method] = self[method] - end - end - - -- Convert all slash command methods - for cmd in pairs(commands) do - for idx,method in ipairs(slashCmdMethods) do - cmd[method] = self[method] - end - end -end - -Dongle = DongleStub:Register(Dongle, Activate) -- 1.7.9.5