Quantcast

-Added support for LibDataBroker-1.1. BagSync now has a broker icon to launch the search window.

Xruptor [08-15-10 - 14:30]
-Added support for LibDataBroker-1.1.  BagSync now has a broker icon to launch the search window.
-Added support to color names by unit class.
-New slash command /bgs unitclass will color names by color.  OFF by default.
-Changed the way slash commands are handled.  Much cleaner code ;)
-Fixed an issue where the throttle wasn't always allowing the information to show on certain tooltips.
-Fixed an issue where sometimes the throttling wasn't initiated.
-Added support for CallbackHandler-1.0.lua.
-Fixed an issue in the BagSync Search window where the scroll bar was off the frame sometimes.
-Fixed an issue that prevent users from linking items from the BagSync Search window.
-Added a few helper functions.
Filename
BagSync.lua
BagSync.toc
BagSync_Search.lua
libs/CallbackHandler-1.0.lua
libs/LibDataBroker-1.1.lua
localization/localization.lua
diff --git a/BagSync.lua b/BagSync.lua
index 2758189..66ea8ee 100644
--- a/BagSync.lua
+++ b/BagSync.lua
@@ -36,6 +36,37 @@ local MOSS = '|cFF80FF00%s|r'
 local TTL_C = '|cFFF4A460%s|r'
 local GN_C = '|cFF65B8C0%s|r'

+------------------------------
+--    LibDataBroker-1.1	    --
+------------------------------
+
+local ldb = LibStub:GetLibrary("LibDataBroker-1.1")
+
+local dataobj = ldb:NewDataObject("BagSyncLDB", {
+	type = "data source",
+	icon = "Interface\\Icons\\INV_Misc_Bag_12",
+	label = "BagSync",
+	text = "BagSync",
+
+	OnClick = function(self, button)
+		if getglobal("BagSync_SearchFrame") then
+			if getglobal("BagSync_SearchFrame"):IsVisible() then
+				getglobal("BagSync_SearchFrame"):Hide()
+			else
+				getglobal("BagSync_SearchFrame"):Show()
+			end
+		end
+	end,
+
+	OnTooltipShow = function(self)
+		self:Hide()
+	end
+})
+
+------------------------------
+--        MAIN OBJ	        --
+------------------------------
+
 local BagSync = CreateFrame("frame", "BagSync", UIParent)

 BagSync:SetScript('OnEvent', function(self, event, ...)
@@ -81,6 +112,9 @@ function BagSync:PLAYER_LOGIN()
 	--save the current user money (before bag update)
 	BS_DB["gold:0:0"] = GetMoney()

+	--save the class information
+	BS_DB["class:0:0"] = playerClass
+
 	--check for player not in guild
 	if IsInGuild() or GetNumGuildMembers(true) > 0 then
 		GuildRoster()
@@ -119,6 +153,15 @@ function BagSync:PLAYER_LOGIN()
 	self:RegisterEvent('MAIL_SHOW')
 	self:RegisterEvent('MAIL_INBOX_UPDATE')

+	local slashChk = {
+		[L["total"]] = "showTotal",
+		[L["guildname"]] = "showGuildNames",
+		[L["throttle"]] = "enableThrottle",
+		[L["guild"]] = "enableGuild",
+		[L["mailbox"]] = "enableMailbox",
+		[L["unitclass"]] = "enableUnitClass",
+	}
+
 	SLASH_BAGSYNC1 = "/bagsync"
 	SLASH_BAGSYNC2 = "/bgs"
 	SlashCmdList["BAGSYNC"] = function(msg)
@@ -153,59 +196,14 @@ function BagSync:PLAYER_LOGIN()
 			elseif c and c:lower() == L["fixdb"] then
 				self:FixDB_Data()
 				return true
-			elseif c and c:lower() == L["total"] then
-				lastDisplayed = {}
-				lastItem = nil
-				if BagSyncOpt.showTotal then
-					BagSyncOpt.showTotal = false
-					print("|cFFFF0000BagSync: "..L["Total:"].." "..L["OFF"])
-				else
-					BagSyncOpt.showTotal = true
-					print("|cFFFF0000BagSync: "..L["Total:"].." "..L["ON"])
-				end
-				return true
-			elseif c and c:lower() == L["guildname"] then
-				lastDisplayed = {}
-				lastItem = nil
-				if BagSyncOpt.showGuildNames then
-					BagSyncOpt.showGuildNames = false
-					print("|cFFFF0000BagSync: "..L["guildname"]..": "..L["OFF"])
-				else
-					BagSyncOpt.showGuildNames = true
-					print("|cFFFF0000BagSync: "..L["guildname"]..": "..L["ON"])
-				end
-				return true
-			elseif c and c:lower() == L["throttle"] then
-				lastDisplayed = {}
-				lastItem = nil
-				if BagSyncOpt.enableThrottle then
-					BagSyncOpt.enableThrottle = false
-					print("|cFFFF0000BagSync: "..L["throttle"]..": "..L["OFF"])
-				else
-					BagSyncOpt.enableThrottle = true
-					print("|cFFFF0000BagSync: "..L["throttle"]..": "..L["ON"])
-				end
-				return true
-			elseif c and c:lower() == L["guild"] then
-				lastDisplayed = {}
-				lastItem = nil
-				if BagSyncOpt.enableGuild then
-					BagSyncOpt.enableGuild = false
-					print("|cFFFF0000BagSync: "..L["guild"]..": "..L["OFF"])
-				else
-					BagSyncOpt.enableGuild = true
-					print("|cFFFF0000BagSync: "..L["guild"]..": "..L["ON"])
-				end
-				return true
-			elseif c and c:lower() == L["mailbox"] then
+			elseif c and slashChk[c:lower()] and BagSyncOpt[slashChk[c:lower()]] ~= nil then
 				lastDisplayed = {}
 				lastItem = nil
-				if BagSyncOpt.enableMailbox then
-					BagSyncOpt.enableMailbox = false
-					print("|cFFFF0000BagSync: "..L["mailbox"]..": "..L["OFF"])
+				BagSyncOpt[slashChk[c:lower()]] = not BagSyncOpt[slashChk[c:lower()]]
+				if BagSyncOpt[slashChk[c:lower()]] then
+					print("|cFFFF0000BagSync: |cFFFFFFFF"..(c:lower()).."|r |cFFFF0000"..L["ON"].."|r")
 				else
-					BagSyncOpt.enableMailbox = true
-					print("|cFFFF0000BagSync: "..L["mailbox"]..": "..L["ON"])
+					print("|cFFFF0000BagSync:|r |cFFFFFFFF"..(c:lower()).."|r |cFFFF0000"..L["OFF"].."|r")
 				end
 				return true
 			elseif c and c:lower() ~= "" then
@@ -336,6 +334,7 @@ function BagSync:StartupDB()
 	if BagSyncOpt.enableThrottle == nil then BagSyncOpt.enableThrottle = true end
 	if BagSyncOpt.enableGuild == nil then BagSyncOpt.enableGuild = true end
 	if BagSyncOpt.enableMailbox == nil then BagSyncOpt.enableMailbox = true end
+	if BagSyncOpt.enableUnitClass == nil then BagSyncOpt.enableUnitClass = false end

 	BagSyncGUILD_DB = BagSyncGUILD_DB or {}
 	BagSyncGUILD_DB[currentRealm] = BagSyncGUILD_DB[currentRealm] or {}
@@ -1029,7 +1028,32 @@ local function pairsByKeys (t, f)
 	return iter
 end

+local function rgbhex(r, g, b)
+  if type(r) == "table" then
+	if r.r then
+	  r, g, b = r.r, r.g, r.b
+	else
+	  r, g, b = unpack(r)
+	end
+  end
+  return string.format("|cff%02x%02x%02x", (r or 1) * 255, (g or 1) * 255, (b or 1) * 255)
+end
+
+
+local function getNameColor(sName, sClass)
+	if not BagSyncOpt.enableUnitClass then
+		return format(MOSS, sName)
+	else
+		if sName ~= "Unknown" and sClass and RAID_CLASS_COLORS[sClass] then
+			return rgbhex(RAID_CLASS_COLORS[sClass])..sName.."|r"
+		end
+	end
+	return format(MOSS, sName)
+end
+
 local function AddOwners(frame, link)
+	frame.BagSyncShowOnce = nil
+
 	local itemLink = ToShortLink(link)
 	if not itemLink then
 		frame:Show()
@@ -1111,12 +1135,15 @@ local function AddOwners(frame, link)
 			end
 		end

+		--get class for the unit if there is one
+		local pClass = v["class:0:0"] or nil
+
 		infoString = CountsToInfoString(invCount or 0, bankCount or 0, equipCount or 0, guildCount or 0, mailboxCount or 0)
 		grandTotal = grandTotal + (invCount or 0) + (bankCount or 0) + (equipCount or 0) + (guildCount or 0) + (mailboxCount or 0)

 		if infoString and infoString ~= '' then
-			frame:AddDoubleLine(format(MOSS, k), infoString)
-			table.insert(lastDisplayed, format(MOSS,(k or 'Unknown')).."@"..(infoString or 'unknown'))
+			frame:AddDoubleLine(getNameColor(k, pClass), infoString)
+			table.insert(lastDisplayed, getNameColor(k or 'Unknown', pClass).."@"..(infoString or 'unknown'))
 		end

 	end
@@ -1150,14 +1177,17 @@ local function HookTip(tooltip)
 			if BagSyncOpt.enableThrottle then
 				if not self.BagSyncThrottle then self.BagSyncThrottle = GetTime() end
 				if not self.BagSyncPrevious then self.BagSyncPrevious = itemName end
+				if not self.BagSyncShowOnce and self:GetName() == "GameTooltip" then self.BagSyncShowOnce = true end

 				if itemName ~= self.BagSyncPrevious then
 					self.BagSyncPrevious = itemName
 					self.BagSyncThrottle = GetTime()
 				end
 				if self:GetName() == "GameTooltip" and (GetTime() - self.BagSyncThrottle) >= 0.05 then
+					self.BagSyncShowOnce = nil
 					AddOwners(self, itemLink)
 				elseif self:GetName() ~= "GameTooltip" then
+					self.BagSyncShowOnce = nil
 					AddOwners(self, itemLink)
 				end
 			else
@@ -1165,6 +1195,16 @@ local function HookTip(tooltip)
 			end
 		end
 	end)
+
+	tooltip:HookScript('OnUpdate', function(self, ...)
+		if self:GetName() == "GameTooltip" and self.BagSyncShowOnce and self.BagSyncThrottle and (GetTime() - self.BagSyncThrottle) >= 0.05 then
+			local _, itemLink = self:GetItem()
+			self.BagSyncShowOnce = nil
+			if itemLink then
+				AddOwners(self, itemLink)
+			end
+		end
+	end)
 end

 HookTip(GameTooltip)
diff --git a/BagSync.toc b/BagSync.toc
index 7cc1c0f..17b60d7 100644
--- a/BagSync.toc
+++ b/BagSync.toc
@@ -2,14 +2,16 @@
 ## Title: BagSync
 ## Notes: BagSync tracks your characters items and displays it within tooltips.
 ## Author: Xruptor
-## Version: 4.1
+## Version: 4.2
 ## SavedVariables: BagSyncDB, BagSyncOpt, BagSyncGUILD_DB, BagSyncTOKEN_DB

 localization\localization.lua
-Libs\LibStub.lua
-Libs\tekKonfigScroll.lua
-Libs\tekKonfigDropdown.lua
-Libs\LibItemSearch-1.0.lua
+libs\LibStub.lua
+libs\CallbackHandler-1.0.lua
+libs\tekKonfigScroll.lua
+libs\tekKonfigDropdown.lua
+libs\LibItemSearch-1.0.lua
+libs\LibDataBroker-1.1.lua

 BagSync_Search.lua
 BagSync_Tokens.lua
diff --git a/BagSync_Search.lua b/BagSync_Search.lua
index d227167..ef7e02d 100644
--- a/BagSync_Search.lua
+++ b/BagSync_Search.lua
@@ -101,7 +101,7 @@ function bgSearch:LoadSlider()

 	local EDGEGAP, ROWHEIGHT, ROWGAP, GAP = 16, 20, 2, 4
 	local FRAME_HEIGHT = bgSearch:GetHeight() - 60
-	local SCROLL_TOP_POSITION = -110
+	local SCROLL_TOP_POSITION = -90
 	local totalRows = math.floor((FRAME_HEIGHT-22)/(ROWHEIGHT + ROWGAP))

 	for i=1, totalRows do
@@ -128,7 +128,13 @@ function bgSearch:LoadSlider()
 			row:SetScript("OnClick", function(self)
 				if self.link then
 					if IsShiftKeyDown() then
-						ChatFrameEditBox:Insert(self.link)
+						for i = 1, NUM_CHAT_WINDOWS do
+							local editBox = _G[("ChatFrame%dEditBox"):format(i)]
+							if editBox:IsVisible() then
+								editBox:Insert(self.link)
+								break
+							end
+						end
 					elseif IsControlKeyDown() then
 						DressUpItemLink(self.link)
 					end
diff --git a/libs/CallbackHandler-1.0.lua b/libs/CallbackHandler-1.0.lua
new file mode 100644
index 0000000..5ad658f
--- /dev/null
+++ b/libs/CallbackHandler-1.0.lua
@@ -0,0 +1,239 @@
+--[[ $Id: CallbackHandler-1.0.lua 60548 2008-02-07 11:04:06Z nevcairiel $ ]]
+local MAJOR, MINOR = "CallbackHandler-1.0", 3
+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}
+
+local type = type
+local pcall = pcall
+local pairs = pairs
+local assert = assert
+local concat = table.concat
+local loadstring = loadstring
+local next = next
+local select = select
+local type = type
+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", concat(OLD_ARGS, ", ")):gsub("ARGS", concat(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"
+			if type(self)~="table" and type(self)~="string" then
+				error("Usage: "..RegisterName.."(self or \"addonId\", eventname, method): 'self or addonId': table or string 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/LibDataBroker-1.1.lua b/libs/LibDataBroker-1.1.lua
new file mode 100644
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/localization/localization.lua b/localization/localization.lua
index bf1f7be..c6e2126 100644
--- a/localization/localization.lua
+++ b/localization/localization.lua
@@ -32,6 +32,7 @@
 -- ["throttle"] = "",
 -- ["guild"] = "",
 -- ["mailbox"] = "",
+-- ["unitclass"] = "",
 -- ["/bgs [itemname] - Does a quick search for an item"] = "",
 -- ["/bgs search - Opens the search window"] = "",
 -- ["/bgs gold - Displays a tooltip with the amount of gold on each character."] = "",