Quantcast

Cross Realm Fix and More!

Xruptor [08-20-16 - 23:16]
Cross Realm Fix and More!
-Fixed an issue where realms with spaces in their names would not be counted in the Cross-Realm Option.  Sadly because of this issue, I've had to make adjustments to the database.  Those users whom play on a server with a space in the name, will have to login to each toon individually.  This is because I've removed all servers from the DB that have spaces in it.  In the future they will be stored with whitespace removed as Blizzard uses in their code. I've made a small database to store the true server names that include spaces and symbols for display purposes only (like in the tooltip)  SORRY!
-I've cleaned up inconsistent coding throughout the entire addon.  There where times I was using single quotes and then double quotes.  It was annoying me lol.
-The options panel code has been completely reworked from the ground up.  You won't notice a change but it will be easy for me to add options in the future.
-I've added color selection options for the tooltips!  Hurray!  Now you can change the color of the tooltips.
-Moved all the libraries into individual folders instead of the way they used to be before.  This is more common practice with addons anyways.  Don't know what I was thinking back in the day.
-Fixed a slight bug in LibSimpleOptions-1.0 as SetTexture has been changed to SetColorTexture
-Added new localization for new color options in config.

TODO:
-Fix the profiles window to allow support for cross realms and BNET support.  I may just make it global lol to solve that issue.
-Fix the tokens window to support cross-realm and BNET support.
-Rework the search frames and pretty much any frame that uses a scroll slider.  The current way it's coded just annoys me LOL.
Filename
BagSync.lua
BagSync.toc
BagSync_Blacklist.lua
BagSync_Config.lua
BagSync_Crafts.lua
BagSync_Profiles.lua
BagSync_Search.lua
BagSync_Tokens.lua
libs/CallbackHandler-1.0.lua
libs/CallbackHandler-1.0/CallbackHandler-1.0.lua
libs/CallbackHandler-1.0/CallbackHandler-1.0.xml
libs/LibDataBroker-1.1.lua
libs/LibDataBroker-1.1/LibDataBroker-1.1.lua
libs/LibDataBroker-1.1/README.textile
libs/LibItemSearch-1.0.lua
libs/LibItemSearch-1.0/LibItemSearch-1.0.lua
libs/LibSimpleOptions-1.0/Changelog-LibSimpleOptions-1.0-r46.txt
libs/LibSimpleOptions-1.0/LibSimpleOptions-1.0.lua
libs/LibSimpleOptions-1.0/LibSimpleOptions-1.0.toc
libs/LibSimpleOptions-1.0/LibStub/LibStub.lua
libs/LibSimpleOptions-1.0/lib.xml
libs/LibStub.lua
libs/LibStub/LibStub.lua
libs/LibStub/LibStub.toc
libs/LibStub/tests/test.lua
libs/LibStub/tests/test2.lua
libs/LibStub/tests/test3.lua
libs/LibStub/tests/test4.lua
localization/localization.lua
diff --git a/BagSync.lua b/BagSync.lua
index 5b5770e..70c8590 100644
--- a/BagSync.lua
+++ b/BagSync.lua
@@ -23,6 +23,7 @@ local BS_GD
 local BS_TD
 local BS_CD
 local BS_BL
+local BS_RK
 local MAX_GUILDBANK_SLOTS_PER_TAB = 98
 local doTokenUpdate = 0
 local guildTabQueryQueue = {}
@@ -31,11 +32,6 @@ local atVoidBank = false
 local atGuildBank = false
 local isCheckingMail = false

-local SILVER = '|cffc7c7cf%s|r'
-local MOSS = '|cFF80FF00%s|r'
-local TTL_C = '|cFFF4A460%s|r'
-local GN_C = '|cFF65B8C0%s|r'
-
 local debugf = tekDebug and tekDebug:GetFrame("BagSync")
 local function Debug(...)
     if debugf then debugf:AddMessage(string.join(", ", tostringall(...))) end
@@ -58,15 +54,15 @@ local dataobj = ldb:NewDataObject("BagSyncLDB", {
 	text = "BagSync",

 	OnClick = function(self, button)
-		if button == 'LeftButton' and BagSync_SearchFrame then
+		if button == "LeftButton" and BagSync_SearchFrame then
 			if BagSync_SearchFrame:IsVisible() then
 				BagSync_SearchFrame:Hide()
 			else
 				BagSync_SearchFrame:Show()
 			end
-		elseif button == 'RightButton' and BagSync_TokensFrame then
+		elseif button == "RightButton" and BagSync_TokensFrame then
 			if bgsMinimapDD then
-				ToggleDropDownMenu(1, nil, bgsMinimapDD, 'cursor', 0, 0)
+				ToggleDropDownMenu(1, nil, bgsMinimapDD, "cursor", 0, 0)
 			end
 		end
 	end,
@@ -84,7 +80,7 @@ local dataobj = ldb:NewDataObject("BagSyncLDB", {

 local BagSync = CreateFrame("Frame", "BagSync", UIParent)

-BagSync:SetScript('OnEvent', function(self, event, ...)
+BagSync:SetScript("OnEvent", function(self, event, ...)
 	if self[event] then
 		self[event](self, event, ...)
 	end
@@ -94,7 +90,31 @@ function BagSync:Debug(...)
 	Debug(...)
 end

-if IsLoggedIn() then BagSync:PLAYER_LOGIN() else BagSync:RegisterEvent('PLAYER_LOGIN') end
+if IsLoggedIn() then BagSync:PLAYER_LOGIN() else BagSync:RegisterEvent("PLAYER_LOGIN") end
+
+----------------------
+--      Local       --
+----------------------
+
+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 tooltipColor(color, str)
+  return string.format("|cff%02x%02x%02x%s|r", (color.r or 1) * 255, (color.g or 1) * 255, (color.b or 1) * 255, str)
+end
+
+local function ToShortLink(link)
+	if not link then return nil end
+	return link:match("item:(%d+):") or nil
+end

 ----------------------
 --   DB Functions   --
@@ -117,6 +137,15 @@ local function StartupDB()
 	if BagSyncOpt.enableCrossRealmsItems == nil then BagSyncOpt.enableCrossRealmsItems = true end
 	if BagSyncOpt.enableBNetAccountItems == nil then BagSyncOpt.enableBNetAccountItems = false end

+	--setup the default colors
+	if BagSyncOpt.colors == nil then BagSyncOpt.colors = {} end
+	if BagSyncOpt.colors.FIRST == nil then BagSyncOpt.colors.FIRST = { r = 128/255, g = 1, b = 0 }  end
+	if BagSyncOpt.colors.SECOND == nil then BagSyncOpt.colors.SECOND = { r = 199/255, g = 199/255, b = 207/255 }  end
+	if BagSyncOpt.colors.TOTAL == nil then BagSyncOpt.colors.TOTAL = { r = 244/255, g = 164/255, b = 96/255 }  end
+	if BagSyncOpt.colors.GUILD == nil then BagSyncOpt.colors.GUILD = { r = 101/255, g = 184/255, b = 192/255 }  end
+	if BagSyncOpt.colors.CROSS == nil then BagSyncOpt.colors.CROSS = { r = 1, g = 125/255, b = 10/255 }  end
+	if BagSyncOpt.colors.BNET == nil then BagSyncOpt.colors.BNET = { r = 53/255, g = 136/255, b = 1 }  end
+
 	--new format, get rid of old
 	if not BagSyncOpt.dbversion or not tonumber(BagSyncOpt.dbversion) or tonumber(BagSyncOpt.dbversion) < 7 then
 		BagSyncDB = {}
@@ -146,10 +175,13 @@ local function StartupDB()
 	BagSyncBLACKLIST_DB[currentRealm] = BagSyncBLACKLIST_DB[currentRealm] or {}
 	BS_BL = BagSyncBLACKLIST_DB[currentRealm]

+	BagSync_REALMKEY = BagSync_REALMKEY or {}
+	BagSync_REALMKEY[currentRealm] = GetRealmName()
+	BS_RK = BagSync_REALMKEY
+
 end

 function BagSync:FixDB_Data(onlyChkGuild)
-	if debugging then return end
 	--Removes obsolete character information
 	--Removes obsolete guild information
 	--Removes obsolete characters from tokens db
@@ -161,16 +193,21 @@ function BagSync:FixDB_Data(onlyChkGuild)
 	local storeGuilds = {}

 	for realm, rd in pairs(BagSyncDB) do
-		--realm
-		storeUsers[realm] = storeUsers[realm] or {}
-		storeGuilds[realm] = storeGuilds[realm] or {}
-		for k, v in pairs(rd) do
-			--users
-			storeUsers[realm][k] = storeUsers[realm][k] or 1
-			if v.realm == nil then v.realm = realm end  --Adds realm name to characters profiles if missing, v8.6
-			for q, r in pairs(v) do
-				if q == 'guild' then
-					storeGuilds[realm][r] = true
+		if string.find(realm, " ") then
+			--get rid of old realm names with whitespaces, we aren't going to use it anymore
+			BagSyncDB[realm] = nil
+		else
+			--realm
+			storeUsers[realm] = storeUsers[realm] or {}
+			storeGuilds[realm] = storeGuilds[realm] or {}
+			for k, v in pairs(rd) do
+				--users
+				storeUsers[realm][k] = storeUsers[realm][k] or 1
+				if v.realm == nil then v.realm = realm end  --Adds realm name to characters profiles if missing, v8.6
+				for q, r in pairs(v) do
+					if q == "guild" then
+						storeGuilds[realm][r] = true
+					end
 				end
 			end
 		end
@@ -178,12 +215,17 @@ function BagSync:FixDB_Data(onlyChkGuild)

 	--guildbank data
 	for realm, rd in pairs(BagSyncGUILD_DB) do
-		--realm
-		for k, v in pairs(rd) do
-			--users
-			if not storeGuilds[realm][k] then
-				--delete the guild because no one has it
-				BagSyncGUILD_DB[realm][k] = nil
+		if string.find(realm, " ") then
+			--get rid of old realm names with whitespaces, we aren't going to use it anymore
+			BagSyncGUILD_DB[realm] = nil
+		else
+			--realm
+			for k, v in pairs(rd) do
+				--users
+				if not storeGuilds[realm][k] then
+					--delete the guild because no one has it
+					BagSyncGUILD_DB[realm][k] = nil
+				end
 			end
 		end
 	end
@@ -194,21 +236,26 @@ function BagSync:FixDB_Data(onlyChkGuild)

 		--fix tokens
 		for realm, rd in pairs(BagSyncTOKEN_DB) do
-			--realm
-			if not storeUsers[realm] then
-				--if it's not a realm that ANY users are on then delete it
+			if string.find(realm, " ") then
+				--get rid of old realm names with whitespaces, we aren't going to use it anymore
 				BagSyncTOKEN_DB[realm] = nil
 			else
-				--delete old db information for tokens if it exists
-				if BagSyncTOKEN_DB[realm] and BagSyncTOKEN_DB[realm][1] then BagSyncTOKEN_DB[realm][1] = nil end
-				if BagSyncTOKEN_DB[realm] and BagSyncTOKEN_DB[realm][2] then BagSyncTOKEN_DB[realm][2] = nil end
-
-				for k, v in pairs(rd) do
-					for x, y in pairs(v) do
-						if x ~= "icon" and x ~= "header" then
-							if not storeUsers[realm][x] then
-								--if the user doesn't exist then delete data
-								BagSyncTOKEN_DB[realm][k][x] = nil
+				--realm
+				if not storeUsers[realm] then
+					--if it's not a realm that ANY users are on then delete it
+					BagSyncTOKEN_DB[realm] = nil
+				else
+					--delete old db information for tokens if it exists
+					if BagSyncTOKEN_DB[realm] and BagSyncTOKEN_DB[realm][1] then BagSyncTOKEN_DB[realm][1] = nil end
+					if BagSyncTOKEN_DB[realm] and BagSyncTOKEN_DB[realm][2] then BagSyncTOKEN_DB[realm][2] = nil end
+
+					for k, v in pairs(rd) do
+						for x, y in pairs(v) do
+							if x ~= "icon" and x ~= "header" then
+								if not storeUsers[realm][x] then
+									--if the user doesn't exist then delete data
+									BagSyncTOKEN_DB[realm][k][x] = nil
+								end
 							end
 						end
 					end
@@ -218,19 +265,32 @@ function BagSync:FixDB_Data(onlyChkGuild)

 		--fix professions
 		for realm, rd in pairs(BagSyncCRAFT_DB) do
-			--realm
-			if not storeUsers[realm] then
-				--if it's not a realm that ANY users are on then delete it
+			if string.find(realm, " ") then
+				--get rid of old realm names with whitespaces, we aren't going to use it anymore
 				BagSyncCRAFT_DB[realm] = nil
 			else
-				for k, v in pairs(rd) do
-					if not storeUsers[realm][k] then
-						--if the user doesn't exist then delete data
-						BagSyncCRAFT_DB[realm][k] = nil
+				--realm
+				if not storeUsers[realm] then
+					--if it's not a realm that ANY users are on then delete it
+					BagSyncCRAFT_DB[realm] = nil
+				else
+					for k, v in pairs(rd) do
+						if not storeUsers[realm][k] then
+							--if the user doesn't exist then delete data
+							BagSyncCRAFT_DB[realm][k] = nil
+						end
 					end
 				end
 			end
 		end
+
+		--fix blacklist
+		for realm, rd in pairs(BagSyncBLACKLIST_DB) do
+			if string.find(realm, " ") then
+				--get rid of old realm names with whitespaces, we aren't going to use it anymore
+				BagSyncBLACKLIST_DB[realm] = nil
+			end
+		end

 		DEFAULT_CHAT_FRAME:AddMessage("|cFF99CC33BagSync:|r |cFFFF9900"..L["A FixDB has been performed on BagSync!  The database is now optimized!"].."|r")
 	end
@@ -266,22 +326,25 @@ end

 function BagSync:getCharacterRealmInfo(charName, charRealm)

-	local yName, yRealm  = strsplit('^', charName)
-
+	local yName, yRealm  = strsplit("^", charName)
+	local realmFullName = charRealm
+
+	if BS_RK[charRealm] then realmFullName = BS_RK[charRealm] end
+
 	--add Cross-Realm and BNet identifiers to Characters not on same realm
 	if BagSyncOpt.enableBNetAccountItems then
 		if charRealm and charRealm ~= currentRealm then
 			if not crossRealmNames[charRealm] then
-				charName = yName.." |cff3588ff[BNet-"..charRealm.."]|r"
+				charName = yName.." "..rgbhex(BagSyncOpt.colors.BNET).."[BNet-"..realmFullName.."]|r"
 			else
-				charName = yName.." |cffff7d0a[XR-"..charRealm.."]|r"
+				charName = yName.." "..rgbhex(BagSyncOpt.colors.CROSS).."[XR-"..realmFullName.."]|r"
 			end
 		else
 			charName = yName
 		end
 	elseif BagSyncOpt.enableCrossRealmsItems then
 		if charRealm and charRealm ~= currentRealm then
-			charName = yName.." |cffff7d0a[XR-"..charRealm.."]|r"
+			charName = yName.." "..rgbhex(BagSyncOpt.colors.CROSS).."[XR-"..realmFullName.."]|r"
 		else
 			charName = yName
 		end
@@ -295,20 +358,24 @@ end

 function BagSync:getGuildRealmInfo(guildName, guildRealm)

+	local realmFullName = guildRealm
+
+	if BS_RK[guildRealm] then realmFullName = BS_RK[guildRealm] end
+
 	--add Cross-Realm and BNet identifiers to Guilds not on same realm
 	if BagSyncOpt.enableBNetAccountItems then
 		if guildRealm and guildRealm ~= currentRealm then
 			if not crossRealmNames[guildRealm] then
-				guildName = guildName.." |cff3588ff[BNet-"..guildRealm.."]|r"
+				guildName = guildName.." "..rgbhex(BagSyncOpt.colors.BNET).."[BNet-"..realmFullName.."]|r"
 			else
-				guildName = guildName.." |cffff7d0a[XR-"..guildRealm.."]|r"
+				guildName = guildName.." "..rgbhex(BagSyncOpt.colors.CROSS).."[XR-"..realmFullName.."]|r"
 			end
 		else
 			guildName = guildName
 		end
 	elseif BagSyncOpt.enableCrossRealmsItems then
 		if guildRealm and guildRealm ~= currentRealm then
-			guildName = guildName.." |cffff7d0a[XR-"..guildRealm.."]|r"
+			guildName = guildName.." "..rgbhex(BagSyncOpt.colors.CROSS).."[XR-"..realmFullName.."]|r"
 		else
 			guildName = guildName
 		end
@@ -319,22 +386,6 @@ function BagSync:getGuildRealmInfo(guildName, guildRealm)

 	return guildName
 end
-----------------------
---      Local       --
-----------------------
-
-local function doRegularTradeSkill(numIndex, dbIdx)
-	local name, icon, skillLevel, maxSkillLevel, numAbilities, spelloffset, skillLine, skillModifier = GetProfessionInfo(numIndex)
-	if name and skillLevel then
-		BS_CD[dbIdx] = format('%s,%s', name, skillLevel)
-	end
-end
-
-local function ToShortLink(link)
-	if not link then return nil end
-	return link:match("item:(%d+):") or nil
-end
-

 ----------------------
 --  Bag Functions   --
@@ -357,7 +408,7 @@ local function SaveBag(bagname, bagid)
 			if ToShortLink(link) then
 				count = (count > 1 and count) or nil
 				if count then
-					slotItems[slot] = format('%s,%d', ToShortLink(link), count)
+					slotItems[slot] = format("%s,%d", ToShortLink(link), count)
 				else
 					slotItems[slot] = ToShortLink(link)
 				end
@@ -376,40 +427,40 @@ local function SaveEquipment()
 	lastDisplayed = {}

 	if not BS_DB then StartupDB() end
-	BS_DB['equip'] = BS_DB['equip'] or {}
+	BS_DB["equip"] = BS_DB["equip"] or {}

 	local slotItems = {}
 	--start at 1, 0 used to be the old range slot (not needed anymore)
 	for slot = 1, NUM_EQUIPMENT_SLOTS do
-		local link = GetInventoryItemLink('player', slot)
+		local link = GetInventoryItemLink("player", slot)
 		if link and ToShortLink(link) then
-			local count =  GetInventoryItemCount('player', slot)
+			local count =  GetInventoryItemCount("player", slot)
 			count = (count and count > 1) or nil
 			if count then
-				slotItems[slot] = format('%s,%d', ToShortLink(link), count)
+				slotItems[slot] = format("%s,%d", ToShortLink(link), count)
 			else
 				slotItems[slot] = ToShortLink(link)
 			end
 		end
 	end
-	BS_DB['equip'][0] = slotItems
+	BS_DB["equip"][0] = slotItems
 end

 local function ScanEntireBank()
 	--force scan of bank bag -1, since blizzard never sends updates for it
-	SaveBag('bank', BANK_CONTAINER)
+	SaveBag("bank", BANK_CONTAINER)
 	for i = NUM_BAG_SLOTS + 1, NUM_BAG_SLOTS + NUM_BANKBAGSLOTS do
-		SaveBag('bank', i)
+		SaveBag("bank", i)
 	end
 	if IsReagentBankUnlocked() then
-		SaveBag('reagentbank', REAGENTBANK_CONTAINER)
+		SaveBag("reagentbank", REAGENTBANK_CONTAINER)
 	end
 end

 local function ScanVoidBank()
 	if VoidStorageFrame and VoidStorageFrame:IsShown() then
 		if not BS_DB then StartupDB() end
-		BS_DB['void'] = BS_DB['void'] or {}
+		BS_DB["void"] = BS_DB["void"] or {}

 		--reset our tooltip data since we scanned new items (we want current data not old)
 		lastItem = nil
@@ -429,7 +480,7 @@ local function ScanVoidBank()
 			end
 		end

-		BS_DB['void'][0] = slotItems
+		BS_DB["void"][0] = slotItems
 	end
 end

@@ -462,7 +513,7 @@ local function ScanGuildBank()
 					count = (count > 1 and count) or nil

 					if count then
-						slotItems[index] = format('%s,%d', ToShortLink(link), count)
+						slotItems[index] = format("%s,%d", ToShortLink(link), count)
 					else
 						slotItems[index] = ToShortLink(link)
 					end
@@ -485,7 +536,7 @@ local function ScanMailbox()
 	CheckInbox()

 	if not BS_DB then StartupDB() end
-	BS_DB['mailbox'] = BS_DB['mailbox'] or {}
+	BS_DB["mailbox"] = BS_DB["mailbox"] or {}

 	local slotItems = {}
 	local mailCount = 0
@@ -506,7 +557,7 @@ local function ScanMailbox()
 					mailCount = mailCount + 1
 					count = (count > 1 and count) or nil
 					if count then
-						slotItems[mailCount] = format('%s,%d', ToShortLink(link), count)
+						slotItems[mailCount] = format("%s,%d", ToShortLink(link), count)
 					else
 						slotItems[mailCount] = ToShortLink(link)
 					end
@@ -515,13 +566,13 @@ local function ScanMailbox()
 		end
 	end

-	BS_DB['mailbox'][0] = slotItems
+	BS_DB["mailbox"][0] = slotItems
 	isCheckingMail = false
 end

 local function ScanAuctionHouse()
 	if not BS_DB then StartupDB() end
-	BS_DB['auction'] = BS_DB['auction'] or {}
+	BS_DB["auction"] = BS_DB["auction"] or {}

 	local slotItems = {}
 	local ahCount = 0
@@ -541,13 +592,13 @@ local function ScanAuctionHouse()
 				if link and ToShortLink(link) and timeLeft then
 					ahCount = ahCount + 1
 					count = (count or 1)
-					slotItems[ahCount] = format('%s,%s,%s', ToShortLink(link), count, timeLeft)
+					slotItems[ahCount] = format("%s,%s,%s", ToShortLink(link), count, timeLeft)
 				end
 			end
 		end
 	end

-	BS_DB['auction'][0] = slotItems
+	BS_DB["auction"][0] = slotItems
 	BS_DB.AH_Count = ahCount
 end

@@ -562,16 +613,16 @@ local function RemoveExpiredAuctions()
 			--users k=name, v=values
 			if BagSyncDB[realm][k].AH_LastScan and BagSyncDB[realm][k].AH_Count then --only proceed if we have an auction house time to work with
 				--check to see if we even have something to work with
-				if BagSyncDB[realm][k]['auction'] then
+				if BagSyncDB[realm][k]["auction"] then
 					--we do so lets do a loop
 					local bVal = BagSyncDB[realm][k].AH_Count
 					--do a loop through all of them and check to see if any expired
 					for x = 1, bVal do
-						if BagSyncDB[realm][k]['auction'][0][x] then
+						if BagSyncDB[realm][k]["auction"][0][x] then
 							--check for expired and remove if necessary
 							--it's okay if the auction count is showing more then actually stored, it's just used as a means
 							--to scan through all our items.  Even if we have only 3 and the count is 6 it will just skip the last 3.
-							local dblink, dbcount, dbtimeleft = strsplit(',', BagSyncDB[realm][k]['auction'][0][x])
+							local dblink, dbcount, dbtimeleft = strsplit(",", BagSyncDB[realm][k]["auction"][0][x])

 							--only proceed if we have everything to work with, otherwise this auction data is corrupt
 							if dblink and dbcount and dbtimeleft then
@@ -580,11 +631,11 @@ local function RemoveExpiredAuctions()
 								local diff = time() - BagSyncDB[realm][k].AH_LastScan
 								if diff > timestampChk[tonumber(dbtimeleft)] then
 									--technically this isn't very realiable.  but I suppose it's better the  nothing
-									BagSyncDB[realm][k]['auction'][0][x] = nil
+									BagSyncDB[realm][k]["auction"][0][x] = nil
 								end
 							else
 								--it's corrupt delete it
-								BagSyncDB[realm][k]['auction'][0][x] = nil
+								BagSyncDB[realm][k]["auction"][0][x] = nil
 							end
 						end
 					end
@@ -662,7 +713,7 @@ function BagSync:ShowMoneyTooltip()

 	local usrData = {}

-	tooltip:SetOwner(UIParent, 'ANCHOR_NONE')
+	tooltip:SetOwner(UIParent, "ANCHOR_NONE")
 	tooltip:ClearLines()
 	tooltip:ClearAllPoints()
 	tooltip:SetPoint("CENTER",UIParent,"CENTER",0,0)
@@ -689,7 +740,7 @@ function BagSync:ShowMoneyTooltip()
 	end
 	if BagSyncOpt.showTotal and gldTotal > 0 then
 		tooltip:AddLine(" ")
-		tooltip:AddDoubleLine(format(TTL_C, L["Total:"]), buildMoneyString(gldTotal, false), 1, 1, 1, 1, 1, 1)
+		tooltip:AddDoubleLine(tooltipColor(BagSyncOpt.colors.TOTAL, buildMoneyString(gldTotal, false)), 1, 1, 1, 1, 1, 1)
 	end

 	tooltip:AddLine(" ")
@@ -721,7 +772,7 @@ local function ScanTokens()
 	if IsInBG() or IsInArena() or InCombatLockdown() or UnitAffectingCombat("player") then
 		--avoid (Honor point spam), avoid (arena point spam), if it's world PVP...well then it sucks to be you
 		doTokenUpdate = 1
-		BagSync:RegisterEvent('PLAYER_REGEN_ENABLED')
+		BagSync:RegisterEvent("PLAYER_REGEN_ENABLED")
 		return
 	end

@@ -772,79 +823,79 @@ local function CountsToInfoString(countTable)
 	local info
 	local total = 0

-	if countTable['bag'] > 0 then
-		info = L["Bags: %d"]:format(countTable['bag'])
-		total = total + countTable['bag']
+	if countTable["bag"] > 0 then
+		info = L["Bags: %d"]:format(countTable["bag"])
+		total = total + countTable["bag"]
 	end

-	if countTable['bank'] > 0 then
-		local count = L["Bank: %d"]:format(countTable['bank'])
+	if countTable["bank"] > 0 then
+		local count = L["Bank: %d"]:format(countTable["bank"])
 		if info then
-			info = strjoin(', ', info, count)
+			info = strjoin(", ", info, count)
 		else
 			info = count
 		end
-		total = total + countTable['bank']
+		total = total + countTable["bank"]
 	end

-	if countTable['reagentbank'] > 0 then
-		local count = L["Reagent: %d"]:format(countTable['reagentbank'])
+	if countTable["reagentbank"] > 0 then
+		local count = L["Reagent: %d"]:format(countTable["reagentbank"])
 		if info then
-			info = strjoin(', ', info, count)
+			info = strjoin(", ", info, count)
 		else
 			info = count
 		end
-		total = total + countTable['reagentbank']
+		total = total + countTable["reagentbank"]
 	end

-	if countTable['equip'] > 0 then
-		local count = L["Equip: %d"]:format(countTable['equip'])
+	if countTable["equip"] > 0 then
+		local count = L["Equip: %d"]:format(countTable["equip"])
 		if info then
-			info = strjoin(', ', info, count)
+			info = strjoin(", ", info, count)
 		else
 			info = count
 		end
-		total = total + countTable['equip']
+		total = total + countTable["equip"]
 	end

-	if countTable['guild'] > 0 and BagSyncOpt.enableGuild and not BagSyncOpt.showGuildNames then
-		local count = L["Guild: %d"]:format(countTable['guild'])
+	if countTable["guild"] > 0 and BagSyncOpt.enableGuild and not BagSyncOpt.showGuildNames then
+		local count = L["Guild: %d"]:format(countTable["guild"])
 		if info then
-			info = strjoin(', ', info, count)
+			info = strjoin(", ", info, count)
 		else
 			info = count
 		end
-		total = total + countTable['guild'] --add the guild count only if we don't have showguildnames on, otherwise it's counted twice
+		total = total + countTable["guild"] --add the guild count only if we don't have showguildnames on, otherwise it's counted twice
 	end

-	if countTable['mailbox'] > 0 and BagSyncOpt.enableMailbox then
-		local count = L["Mail: %d"]:format(countTable['mailbox'])
+	if countTable["mailbox"] > 0 and BagSyncOpt.enableMailbox then
+		local count = L["Mail: %d"]:format(countTable["mailbox"])
 		if info then
-			info = strjoin(', ', info, count)
+			info = strjoin(", ", info, count)
 		else
 			info = count
 		end
-		total = total + countTable['mailbox']
+		total = total + countTable["mailbox"]
 	end

-	if countTable['void'] > 0 then
-		local count = L["Void: %d"]:format(countTable['void'])
+	if countTable["void"] > 0 then
+		local count = L["Void: %d"]:format(countTable["void"])
 		if info then
-			info = strjoin(', ', info, count)
+			info = strjoin(", ", info, count)
 		else
 			info = count
 		end
-		total = total + countTable['void']
+		total = total + countTable["void"]
 	end

-	if countTable['auction'] > 0 and BagSyncOpt.enableAuction then
-		local count = L["AH: %d"]:format(countTable['auction'])
+	if countTable["auction"] > 0 and BagSyncOpt.enableAuction then
+		local count = L["AH: %d"]:format(countTable["auction"])
 		if info then
-			info = strjoin(', ', info, count)
+			info = strjoin(", ", info, count)
 		else
 			info = count
 		end
-		total = total + countTable['auction']
+		total = total + countTable["auction"]
 	end

 	if info and info ~= "" then
@@ -858,10 +909,10 @@ local function CountsToInfoString(countTable)
 		end

 		if not totalPass then
-			local totalStr = format(MOSS, total)
-			return totalStr .. format(SILVER, format(' (%s)', info))
+			local totalStr = tooltipColor(BagSyncOpt.colors.FIRST, total)
+			return totalStr .. tooltipColor(BagSyncOpt.colors.SECOND, format(" (%s)", info))
 		else
-			return format(MOSS, info)
+			return tooltipColor(BagSyncOpt.colors.FIRST, info)
 		end
 	end
 end
@@ -881,26 +932,15 @@ 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)
+		return tooltipColor(BagSyncOpt.colors.FIRST, 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)
+	return tooltipColor(BagSyncOpt.colors.FIRST, sName)
 end

 local function getPlayerNameColor(sName)
@@ -908,7 +948,7 @@ local function getPlayerNameColor(sName)
 		local sClass = BagSyncDB[currentRealm][sName].class
 		return getNameColor(sName, sClass)
 	end
-	return format(MOSS, sName)
+	return tooltipColor(BagSyncOpt.colors.FIRST, sName)
 end

 local function AddCurrencyToTooltip(frame, currencyName)
@@ -950,7 +990,7 @@ local function AddItemToTooltip(frame, link) --workaround
 	if lastItem and itemLink and itemLink == lastItem then
 		if table.getn(lastDisplayed) > 0 then
 			for i = 1, #lastDisplayed do
-				local ename, ecount  = strsplit('@', lastDisplayed[i])
+				local ename, ecount  = strsplit("@", lastDisplayed[i])
 				if ename and ecount then
 					frame:AddDoubleLine(ename, ecount)
 				end
@@ -1002,7 +1042,7 @@ local function AddItemToTooltip(frame, link) --workaround
 						--slotID = slotid for specific bagid, itemValue = data of specific slotid
 						if type(bagInfo) == "table" then
 							for slotID, itemValue in pairs(bagInfo) do
-								local dblink, dbcount = strsplit(',', itemValue)
+								local dblink, dbcount = strsplit(",", itemValue)
 								if dblink and dblink == itemLink then
 									allowList[q] = allowList[q] + (dbcount or 1)
 									grandTotal = grandTotal + (dbcount or 1)
@@ -1026,7 +1066,7 @@ local function AddItemToTooltip(frame, link) --workaround
 						--we only really need to see this information once per guild
 						local tmpCount = 0
 						for q, r in pairs(BagSyncGUILD_DB[v.realm][guildN]) do
-							local dblink, dbcount = strsplit(',', r)
+							local dblink, dbcount = strsplit(",", r)
 							if dblink and dblink == itemLink then
 								allowList["guild"] = allowList["guild"] + (dbcount or 1)
 								tmpCount = tmpCount + (dbcount or 1)
@@ -1042,9 +1082,9 @@ local function AddItemToTooltip(frame, link) --workaround
 			local pClass = v.class or nil
 			infoString = CountsToInfoString(allowList)

-			if infoString and infoString ~= '' then
+			if infoString and infoString ~= "" then
 				k = BagSync:getCharacterRealmInfo(k, v.realm)
-				table.insert(lastDisplayed, getNameColor(k or 'Unknown', pClass).."@"..(infoString or 'unknown'))
+				table.insert(lastDisplayed, getNameColor(k or "Unknown", pClass).."@"..(infoString or "unknown"))
 			end

 		end
@@ -1059,7 +1099,7 @@ local function AddItemToTooltip(frame, link) --workaround
 		for k, v in pairsByKeys(previousGuilds) do
 			--only print stuff higher then zero
 			if v > 0 then
-				table.insert(lastDisplayed, format(GN_C, k).."@"..format(SILVER, v))
+				table.insert(lastDisplayed, tooltipColor(BagSyncOpt.colors.GUILD, k).."@"..tooltipColor(BagSyncOpt.colors.SECOND, v))
 			end
 		end
 	end
@@ -1067,7 +1107,7 @@ local function AddItemToTooltip(frame, link) --workaround
 	--show grand total if we have something
 	--don't show total if there is only one item
 	if BagSyncOpt.showTotal and grandTotal > 0 and getn(lastDisplayed) > 1 then
-		table.insert(lastDisplayed, format(TTL_C, L["Total:"]).."@"..format(SILVER, grandTotal))
+		table.insert(lastDisplayed, tooltipColor(BagSyncOpt.colors.TOTAL, L["Total:"]).."@"..tooltipColor(BagSyncOpt.colors.SECOND, grandTotal))
 	end

 	--now check for seperater and only add if we have something in the table already
@@ -1078,7 +1118,7 @@ local function AddItemToTooltip(frame, link) --workaround
 	--add it all together now
 	if table.getn(lastDisplayed) > 0 then
 		for i = 1, #lastDisplayed do
-			local ename, ecount  = strsplit('@', lastDisplayed[i])
+			local ename, ecount  = strsplit("@", lastDisplayed[i])
 			if ename and ecount then
 				frame:AddDoubleLine(ename, ecount)
 			end
@@ -1099,11 +1139,11 @@ local function hookTip(tooltip)
 		modified = false
 		self.lastHyperLink = nil
 	end)
-	tooltip:HookScript('OnTooltipCleared', function(self)
+	tooltip:HookScript("OnTooltipCleared", function(self)
 		modified = false
 	end)

-	tooltip:HookScript('OnTooltipSetItem', function(self)
+	tooltip:HookScript("OnTooltipSetItem", function(self)
 		if modified or not BagSyncOpt.enableTooltips then return end
 		local name, link = self:GetItem()
 		if link and ToShortLink(link) then
@@ -1128,28 +1168,28 @@ local function hookTip(tooltip)
 	end)
 	---------------------------------
 	--Special thanks to GetItem() being broken we need to capture the ItemLink before the tooltip shows sometimes
-	hooksecurefunc(tooltip, 'SetBagItem', function(self, tab, slot)
+	hooksecurefunc(tooltip, "SetBagItem", function(self, tab, slot)
 		if not BagSyncOpt.enableTooltips then return end
 		local link = GetContainerItemLink(tab, slot)
 		if link and ToShortLink(link) then
 			self.lastHyperLink = link
 		end
 	end)
-	hooksecurefunc(tooltip, 'SetInventoryItem', function(self, tab, slot)
+	hooksecurefunc(tooltip, "SetInventoryItem", function(self, tab, slot)
 		if not BagSyncOpt.enableTooltips then return end
 		local link = GetInventoryItemLink(tab, slot)
 		if link and ToShortLink(link) then
 			self.lastHyperLink = link
 		end
 	end)
-	hooksecurefunc(tooltip, 'SetGuildBankItem', function(self, tab, slot)
+	hooksecurefunc(tooltip, "SetGuildBankItem", function(self, tab, slot)
 		if not BagSyncOpt.enableTooltips then return end
 		local link = GetGuildBankItemLink(tab, slot)
 		if link and ToShortLink(link) then
 			self.lastHyperLink = link
 		end
 	end)
-	hooksecurefunc(tooltip, 'SetHyperlink', function(self, link)
+	hooksecurefunc(tooltip, "SetHyperlink", function(self, link)
 		if modified or not BagSyncOpt.enableTooltips then return end
 		if link and ToShortLink(link) then
 			--I'm pretty sure there is a better way to do this but since Recipes fire OnTooltipSetItem with empty/nil GetItem().  There is really no way to my knowledge to grab the current itemID
@@ -1161,7 +1201,7 @@ local function hookTip(tooltip)
 	---------------------------------

 	--lets hook other frames so we can show tooltips there as well
-	hooksecurefunc(tooltip, 'SetRecipeReagentItem', function(self, recipeID, reagentIndex)
+	hooksecurefunc(tooltip, "SetRecipeReagentItem", function(self, recipeID, reagentIndex)
 		if modified or not BagSyncOpt.enableTooltips then return end
 		local link = C_TradeSkillUI.GetRecipeReagentItemLink(recipeID, reagentIndex)
 		if link and ToShortLink(link) then
@@ -1169,7 +1209,7 @@ local function hookTip(tooltip)
 			AddItemToTooltip(self, link)
 		end
 	end)
-	hooksecurefunc(tooltip, 'SetRecipeResultItem', function(self, recipeID)
+	hooksecurefunc(tooltip, "SetRecipeResultItem", function(self, recipeID)
 		if modified or not BagSyncOpt.enableTooltips then return end
 		local link = C_TradeSkillUI.GetRecipeItemLink(recipeID)
 		if link and ToShortLink(link) then
@@ -1177,7 +1217,7 @@ local function hookTip(tooltip)
 			AddItemToTooltip(self, link)
 		end
 	end)
-	hooksecurefunc(tooltip, 'SetQuestLogItem', function(self, itemType, index)
+	hooksecurefunc(tooltip, "SetQuestLogItem", function(self, itemType, index)
 		if modified or not BagSyncOpt.enableTooltips then return end
 		local link = GetQuestLogItemLink(itemType, index)
 		if link and ToShortLink(link) then
@@ -1185,7 +1225,7 @@ local function hookTip(tooltip)
 			AddItemToTooltip(self, link)
 		end
 	end)
-	hooksecurefunc(tooltip, 'SetQuestItem', function(self, itemType, index)
+	hooksecurefunc(tooltip, "SetQuestItem", function(self, itemType, index)
 		if modified or not BagSyncOpt.enableTooltips then return end
 		local link = GetQuestItemLink(itemType, index)
 		if link and ToShortLink(link) then
@@ -1202,19 +1242,19 @@ local function hookTip(tooltip)
 	-- end)

 	--------------------------------------------------
-	hooksecurefunc(tooltip, 'SetCurrencyToken', function(self, index)
+	hooksecurefunc(tooltip, "SetCurrencyToken", function(self, index)
 		if modified or not BagSyncOpt.enableTooltips then return end
 		modified = true
 		local currencyName = GetCurrencyListInfo(index)
 		AddCurrencyToTooltip(self, currencyName)
 	end)
-	hooksecurefunc(tooltip, 'SetCurrencyByID', function(self, id)
+	hooksecurefunc(tooltip, "SetCurrencyByID", function(self, id)
 		if modified or not BagSyncOpt.enableTooltips then return end
 		modified = true
 		local currencyName = GetCurrencyInfo(id)
 		AddCurrencyToTooltip(self, currencyName)
 	end)
-	hooksecurefunc(tooltip, 'SetBackpackToken', function(self, index)
+	hooksecurefunc(tooltip, "SetBackpackToken", function(self, index)
 		if modified or not BagSyncOpt.enableTooltips then return end
 		modified = true
 		local currencyName = GetBackpackCurrencyInfo(index)
@@ -1248,8 +1288,8 @@ function BagSync:PLAYER_LOGIN()
 	local ver = GetAddOnMetadata("BagSync","Version") or 0

 	--load our player info after login
-	currentPlayer = UnitName('player')
-	currentRealm = GetRealmName()
+	currentPlayer = UnitName("player")
+	currentRealm = select(2, UnitFullName("player")) --get shortend realm name with no spaces and dashes
 	playerClass = select(2, UnitClass("player"))
 	playerFaction = UnitFactionGroup("player")

@@ -1264,6 +1304,9 @@ function BagSync:PLAYER_LOGIN()
 	--initiate the db
 	StartupDB()

+	--load the options menu
+	BSOpt_Startup()
+
 	--do DB cleanup check by version number
 	if not BagSyncOpt.dbversion or BagSyncOpt.dbversion ~= ver then
 		self:FixDB_Data()
@@ -1293,7 +1336,7 @@ function BagSync:PLAYER_LOGIN()

 	--save all inventory data, including backpack(0)
 	for i = BACKPACK_CONTAINER, BACKPACK_CONTAINER + NUM_BAG_SLOTS do
-		SaveBag('bag', i)
+		SaveBag("bag", i)
 	end

 	--force an equipment scan
@@ -1312,28 +1355,28 @@ function BagSync:PLAYER_LOGIN()
 		BagSync_MinimapButton:Hide()
 	end

-	self:RegisterEvent('PLAYER_MONEY')
-	self:RegisterEvent('BANKFRAME_OPENED')
-	self:RegisterEvent('BANKFRAME_CLOSED')
-	self:RegisterEvent('GUILDBANKFRAME_OPENED')
-	self:RegisterEvent('GUILDBANKFRAME_CLOSED')
-	self:RegisterEvent('GUILDBANKBAGSLOTS_CHANGED')
-	self:RegisterEvent('PLAYERREAGENTBANKSLOTS_CHANGED')
-	self:RegisterEvent('BAG_UPDATE')
-	self:RegisterEvent('PLAYERBANKSLOTS_CHANGED')
-	self:RegisterEvent('UNIT_INVENTORY_CHANGED')
-	self:RegisterEvent('GUILD_ROSTER_UPDATE')
-	self:RegisterEvent('MAIL_SHOW')
-	self:RegisterEvent('MAIL_INBOX_UPDATE')
+	self:RegisterEvent("PLAYER_MONEY")
+	self:RegisterEvent("BANKFRAME_OPENED")
+	self:RegisterEvent("BANKFRAME_CLOSED")
+	self:RegisterEvent("GUILDBANKFRAME_OPENED")
+	self:RegisterEvent("GUILDBANKFRAME_CLOSED")
+	self:RegisterEvent("GUILDBANKBAGSLOTS_CHANGED")
+	self:RegisterEvent("PLAYERREAGENTBANKSLOTS_CHANGED")
+	self:RegisterEvent("BAG_UPDATE")
+	self:RegisterEvent("PLAYERBANKSLOTS_CHANGED")
+	self:RegisterEvent("UNIT_INVENTORY_CHANGED")
+	self:RegisterEvent("GUILD_ROSTER_UPDATE")
+	self:RegisterEvent("MAIL_SHOW")
+	self:RegisterEvent("MAIL_INBOX_UPDATE")
 	self:RegisterEvent("AUCTION_HOUSE_SHOW")
 	self:RegisterEvent("AUCTION_OWNED_LIST_UPDATE")

 	--currency
-	self:RegisterEvent('CURRENCY_DISPLAY_UPDATE')
+	self:RegisterEvent("CURRENCY_DISPLAY_UPDATE")

 	--void storage
-	self:RegisterEvent('VOID_STORAGE_OPEN')
-	self:RegisterEvent('VOID_STORAGE_CLOSE')
+	self:RegisterEvent("VOID_STORAGE_OPEN")
+	self:RegisterEvent("VOID_STORAGE_CLOSE")
 	self:RegisterEvent("VOID_STORAGE_UPDATE")
 	self:RegisterEvent("VOID_STORAGE_CONTENTS_UPDATE")
 	self:RegisterEvent("VOID_TRANSFER_DONE")
@@ -1485,14 +1528,14 @@ function BagSync:BAG_UPDATE(event, bagid)
 		--get the correct bag name based on it's id, trying NOT to use numbers as Blizzard may change bagspace in the future
 		--so instead I'm using constants :)
 		if ((bagid >= NUM_BAG_SLOTS + 1) and (bagid <= NUM_BAG_SLOTS + NUM_BANKBAGSLOTS)) then
-			bagname = 'bank'
+			bagname = "bank"
 		elseif (bagid >= BACKPACK_CONTAINER) and (bagid <= BACKPACK_CONTAINER + NUM_BAG_SLOTS) then
-			bagname = 'bag'
+			bagname = "bag"
 		else
 			return
 		end

-		if bagname == 'bank' and not atBank then return; end
+		if bagname == "bank" and not atBank then return; end
 		--now save the item information in the bag from bagupdate, this could be bag or bank
 		SaveBag(bagname, bagid)

@@ -1501,7 +1544,7 @@ end

 function BagSync:UNIT_INVENTORY_CHANGED(event, unit)
 	if debugging then return end
-	if unit == 'player' then
+	if unit == "player" then
 		SaveEquipment()
 	end
 end
@@ -1525,7 +1568,7 @@ function BagSync:PLAYERBANKSLOTS_CHANGED(event, slotid)
 	if debugging then return end
 	--Remove atBank when/if Blizzard allows Bank access without being at the bank
 	if atBank then
-		SaveBag('bank', BANK_CONTAINER)
+		SaveBag("bank", BANK_CONTAINER)
 	end
 end

@@ -1535,7 +1578,7 @@ end

 function BagSync:PLAYERREAGENTBANKSLOTS_CHANGED()
 	if debugging then return end
-	SaveBag('reagentbank', REAGENTBANK_CONTAINER)
+	SaveBag("reagentbank", REAGENTBANK_CONTAINER)
 end

 ------------------------------
@@ -1648,6 +1691,13 @@ end
 --     PROFESSION           --
 ------------------------------

+local function doRegularTradeSkill(numIndex, dbIdx)
+	local name, icon, skillLevel, maxSkillLevel, numAbilities, spelloffset, skillLine, skillModifier = GetProfessionInfo(numIndex)
+	if name and skillLevel then
+		BS_CD[dbIdx] = format("%s,%s", name, skillLevel)
+	end
+end
+
 function BagSync:TRADE_SKILL_SHOW()
 	if debugging then return end

diff --git a/BagSync.toc b/BagSync.toc
index 390eecd..9c098b3 100644
--- a/BagSync.toc
+++ b/BagSync.toc
@@ -2,16 +2,17 @@
 ## Title: BagSync
 ## Notes: BagSync tracks your characters items and displays it within tooltips.
 ## Author: Xruptor
-## Version: 9.1
+## Version: 9.2
 ## OptionalDeps: tekDebug
-## SavedVariables: BagSyncDB, BagSyncOpt, BagSyncGUILD_DB, BagSyncTOKEN_DB, BagSyncCRAFT_DB, BagSyncBLACKLIST_DB
+## SavedVariables: BagSyncDB, BagSyncOpt, BagSyncGUILD_DB, BagSyncTOKEN_DB, BagSyncCRAFT_DB, BagSyncBLACKLIST_DB, BagSync_REALMKEY

 localization\localization.lua
-libs\LibStub.lua
-libs\CallbackHandler-1.0.lua
+libs\LibStub\LibStub.lua
+libs\CallbackHandler-1.0\CallbackHandler-1.0.xml
+libs\LibItemSearch-1.0\LibItemSearch-1.0.lua
+libs\LibDataBroker-1.1\LibDataBroker-1.1.lua
+libs\LibSimpleOptions-1.0\LibSimpleOptions-1.0.lua
 libs\tekKonfigScroll.lua
-libs\LibItemSearch-1.0.lua
-libs\LibDataBroker-1.1.lua

 BagSync_Minimap.lua
 BagSync_Search.lua
diff --git a/BagSync_Blacklist.lua b/BagSync_Blacklist.lua
index 9e0e2f4..36b8681 100644
--- a/BagSync_Blacklist.lua
+++ b/BagSync_Blacklist.lua
@@ -1,8 +1,8 @@
 local L = BAGSYNC_L
 local blacklistTable = {}
 local tRows, tAnchor = {}
-local currentPlayer = UnitName('player')
-local currentRealm = GetRealmName()
+local currentPlayer = UnitName("player")
+local currentRealm = select(2, UnitFullName("player")) --get shortend realm name with no spaces and dashes
 local storedBarCount = 0
 local prevClickedBar

diff --git a/BagSync_Config.lua b/BagSync_Config.lua
index d079678..412850d 100644
--- a/BagSync_Config.lua
+++ b/BagSync_Config.lua
@@ -1,291 +1,247 @@
 local L = BAGSYNC_L
-local currentPlayer = UnitName('player')
-local currentRealm = GetRealmName()
+local currentPlayer = UnitName("player")
+local currentRealm = select(2, UnitFullName("player"))
 local ver = GetAddOnMetadata("BagSync","Version") or 0

-local bgsOpt = CreateFrame("Frame", "BagSyncConfig", InterfaceOptionsFramePanelContainer)
-bgsOpt:Hide()
-bgsOpt.name = "BagSync"
-
-bgsOpt:SetScript("OnShow", function()
-	if BagSyncOpt then
-		BagSyncConfig_Total:SetChecked(BagSyncOpt["showTotal"])
-		BagSyncConfig_GuildNames:SetChecked(BagSyncOpt["showGuildNames"])
-		BagSyncConfig_BothFactions:SetChecked(BagSyncOpt["enableFaction"])
-		BagSyncConfig_ClassColors:SetChecked(BagSyncOpt["enableUnitClass"])
-		BagSyncConfig_Minimap:SetChecked(BagSyncOpt["enableMinimap"])
-		BagSyncConfig_GuildInfo:SetChecked(BagSyncOpt["enableGuild"])
-		BagSyncConfig_MailboxInfo:SetChecked(BagSyncOpt["enableMailbox"])
-		BagSyncConfig_AuctionInfo:SetChecked(BagSyncOpt["enableAuction"])
-		BagSyncConfig_TooltipSearchOnly:SetChecked(BagSyncOpt["tooltipOnlySearch"])
-		BagSyncConfig_EnableBagSyncTooltips:SetChecked(BagSyncOpt["enableTooltips"])
-		BagSyncConfig_EnableBagSyncTooltipsSeperator:SetChecked(BagSyncOpt["enableTooltipSeperator"])
-		BagSyncConfig_EnableCrossRealmsItems:SetChecked(BagSyncOpt["enableCrossRealmsItems"])
-		BagSyncConfig_EnableBNetAccountItems:SetChecked(BagSyncOpt["enableBNetAccountItems"])
-	end
-end)
-
-local title = bgsOpt:CreateFontString(nil, "ARTWORK", "GameFontNormalLarge")
-title:SetPoint("TOPLEFT", 16, -16)
-title:SetText("|cFF99CC33BagSync|r [|cFFDF2B2B"..ver.."|r]")
-InterfaceOptions_AddCategory(bgsOpt)
-
---[[ Total ]]--
-local bgs_Total_Opt = CreateFrame("CheckButton", "BagSyncConfig_Total", bgsOpt, "OptionsBaseCheckButtonTemplate")
-bgs_Total_Opt:SetPoint("TOPLEFT", 16, -45)
-bgs_Total_Opt:SetScript("OnClick", function(frame)
-	if BagSyncOpt then
-		if frame:GetChecked() then
-			PlaySound("igMainMenuOptionCheckBoxOn")
-			BagSyncOpt["showTotal"] = true
-			if BagSync then BagSync:resetTooltip() end
-		else
-			PlaySound("igMainMenuOptionCheckBoxOff")
-			BagSyncOpt["showTotal"] = false
-			if BagSync then BagSync:resetTooltip() end
-		end
-	end
-end)
-local bgs_Total_OptText = bgs_Total_Opt:CreateFontString(nil, "ARTWORK", "GameFontHighlight")
-bgs_Total_OptText:SetPoint("LEFT", bgs_Total_Opt, "RIGHT", 0, 1)
-bgs_Total_OptText:SetText(L["Display [Total] in tooltips and gold display."])
-
---[[ Guild Names ]]--
-local bgs_GuildNames_Opt = CreateFrame("CheckButton", "BagSyncConfig_GuildNames", bgsOpt, "OptionsBaseCheckButtonTemplate")
-bgs_GuildNames_Opt:SetPoint("TOPLEFT", 16, -73)
-bgs_GuildNames_Opt:SetScript("OnClick", function(frame)
-	if BagSyncOpt then
-		if frame:GetChecked() then
-			PlaySound("igMainMenuOptionCheckBoxOn")
-			BagSyncOpt["showGuildNames"] = true
-			if BagSync then BagSync:resetTooltip() end
-		else
-			PlaySound("igMainMenuOptionCheckBoxOff")
-			BagSyncOpt["showGuildNames"] = false
-			if BagSync then BagSync:resetTooltip() end
-		end
-	end
-end)
-local bgs_GuildNames_OptText = bgs_GuildNames_Opt:CreateFontString(nil, "ARTWORK", "GameFontHighlight")
-bgs_GuildNames_OptText:SetPoint("LEFT", bgs_GuildNames_Opt, "RIGHT", 0, 1)
-bgs_GuildNames_OptText:SetText(L["Display [Guild Name] display in tooltips."])
-
---[[ Display Both Factions ]]--
-local bgs_BothFactions_Opt = CreateFrame("CheckButton", "BagSyncConfig_BothFactions", bgsOpt, "OptionsBaseCheckButtonTemplate")
-bgs_BothFactions_Opt:SetPoint("TOPLEFT", 16, -101)
-bgs_BothFactions_Opt:SetScript("OnClick", function(frame)
-	if BagSyncOpt then
-		if frame:GetChecked() then
-			PlaySound("igMainMenuOptionCheckBoxOn")
-			BagSyncOpt["enableFaction"] = true
-			if BagSync then BagSync:resetTooltip() end
-		else
-			PlaySound("igMainMenuOptionCheckBoxOff")
-			BagSyncOpt["enableFaction"] = false
-			if BagSync then BagSync:resetTooltip() end
-		end
-	end
-end)
-local bgs_BothFactions_OptText = bgs_BothFactions_Opt:CreateFontString(nil, "ARTWORK", "GameFontHighlight")
-bgs_BothFactions_OptText:SetPoint("LEFT", bgs_BothFactions_Opt, "RIGHT", 0, 1)
-bgs_BothFactions_OptText:SetText(L["Display items for both factions (Alliance/Horde)."])
-
---[[ Class Colors ]]--
-local bgs_ClassColors_Opt = CreateFrame("CheckButton", "BagSyncConfig_ClassColors", bgsOpt, "OptionsBaseCheckButtonTemplate")
-bgs_ClassColors_Opt:SetPoint("TOPLEFT", 16, -129)
-bgs_ClassColors_Opt:SetScript("OnClick", function(frame)
-	if BagSyncOpt then
-		if frame:GetChecked() then
-			PlaySound("igMainMenuOptionCheckBoxOn")
-			BagSyncOpt["enableUnitClass"] = true
-			if BagSync then BagSync:resetTooltip() end
-		else
-			PlaySound("igMainMenuOptionCheckBoxOff")
-			BagSyncOpt["enableUnitClass"] = false
-			if BagSync then BagSync:resetTooltip() end
-		end
-	end
-end)
-local bgs_ClassColors_OptText = bgs_ClassColors_Opt:CreateFontString(nil, "ARTWORK", "GameFontHighlight")
-bgs_ClassColors_OptText:SetPoint("LEFT", bgs_ClassColors_Opt, "RIGHT", 0, 1)
-bgs_ClassColors_OptText:SetText(L["Display class colors for characters."])
-
---[[ Minimap ]]--
-local bgs_Minimap_Opt = CreateFrame("CheckButton", "BagSyncConfig_Minimap", bgsOpt, "OptionsBaseCheckButtonTemplate")
-bgs_Minimap_Opt:SetPoint("TOPLEFT", 16, -157)
-bgs_Minimap_Opt:SetScript("OnClick", function(frame)
-	if BagSyncOpt then
-		if frame:GetChecked() then
-			PlaySound("igMainMenuOptionCheckBoxOn")
-			BagSyncOpt["enableMinimap"] = true
-			if BagSync_MinimapButton and not BagSync_MinimapButton:IsVisible() then BagSync_MinimapButton:Show() end
-		else
-			PlaySound("igMainMenuOptionCheckBoxOff")
-			BagSyncOpt["enableMinimap"] = false
-			if BagSync_MinimapButton and BagSync_MinimapButton:IsVisible() then BagSync_MinimapButton:Hide() end
-		end
-	end
-end)
-local bgs_Minimap_OptText = bgs_Minimap_Opt:CreateFontString(nil, "ARTWORK", "GameFontHighlight")
-bgs_Minimap_OptText:SetPoint("LEFT", bgs_Minimap_Opt, "RIGHT", 0, 1)
-bgs_Minimap_OptText:SetText(L["Display BagSync minimap button."])
-
---[[ Enable Guild Info ]]--
-local bgs_GuildInfo_Opt = CreateFrame("CheckButton", "BagSyncConfig_GuildInfo", bgsOpt, "OptionsBaseCheckButtonTemplate")
-bgs_GuildInfo_Opt:SetPoint("TOPLEFT", 16, -185)
-bgs_GuildInfo_Opt:SetScript("OnClick", function(frame)
-	if BagSyncOpt then
-		if frame:GetChecked() then
-			PlaySound("igMainMenuOptionCheckBoxOn")
-			BagSyncOpt["enableGuild"] = true
-			if BagSync then BagSync:resetTooltip() end
-		else
-			PlaySound("igMainMenuOptionCheckBoxOff")
-			BagSyncOpt["enableGuild"] = false
-			if BagSync then BagSync:resetTooltip() end
-		end
-	end
-end)
-local bgs_GuildInfo_OptText = bgs_GuildInfo_Opt:CreateFontString(nil, "ARTWORK", "GameFontHighlight")
-bgs_GuildInfo_OptText:SetPoint("LEFT", bgs_GuildInfo_Opt, "RIGHT", 0, 1)
-bgs_GuildInfo_OptText:SetText(L["Enable guild bank items."])
-
---[[ Enable Mailbox Info ]]--
-local bgs_MailboxInfo_Opt = CreateFrame("CheckButton", "BagSyncConfig_MailboxInfo", bgsOpt, "OptionsBaseCheckButtonTemplate")
-bgs_MailboxInfo_Opt:SetPoint("TOPLEFT", 16, -213)
-bgs_MailboxInfo_Opt:SetScript("OnClick", function(frame)
-	if BagSyncOpt then
-		if frame:GetChecked() then
-			PlaySound("igMainMenuOptionCheckBoxOn")
-			BagSyncOpt["enableMailbox"] = true
-			if BagSync then BagSync:resetTooltip() end
-		else
-			PlaySound("igMainMenuOptionCheckBoxOff")
-			BagSyncOpt["enableMailbox"] = false
-			if BagSync then BagSync:resetTooltip() end
-		end
-	end
-end)
-local bgs_MailboxInfo_OptText = bgs_MailboxInfo_Opt:CreateFontString(nil, "ARTWORK", "GameFontHighlight")
-bgs_MailboxInfo_OptText:SetPoint("LEFT", bgs_MailboxInfo_Opt, "RIGHT", 0, 1)
-bgs_MailboxInfo_OptText:SetText(L["Enable mailbox items."])
-
---[[ Enable Auction House Info Info ]]--
-local bgs_AuctionInfo_Opt = CreateFrame("CheckButton", "BagSyncConfig_AuctionInfo", bgsOpt, "OptionsBaseCheckButtonTemplate")
-bgs_AuctionInfo_Opt:SetPoint("TOPLEFT", 16, -241)
-bgs_AuctionInfo_Opt:SetScript("OnClick", function(frame)
-	if BagSyncOpt then
-		if frame:GetChecked() then
-			PlaySound("igMainMenuOptionCheckBoxOn")
-			BagSyncOpt["enableAuction"] = true
-			if BagSync then BagSync:resetTooltip() end
-		else
-			PlaySound("igMainMenuOptionCheckBoxOff")
-			BagSyncOpt["enableAuction"] = false
-			if BagSync then BagSync:resetTooltip() end
-		end
-	end
-end)
-local bgs_AuctionInfo_OptText = bgs_AuctionInfo_Opt:CreateFontString(nil, "ARTWORK", "GameFontHighlight")
-bgs_AuctionInfo_OptText:SetPoint("LEFT", bgs_AuctionInfo_Opt, "RIGHT", 0, 1)
-bgs_AuctionInfo_OptText:SetText(L["Enable auction house items."])
-
---[[ Display tooltips only in the BagSync Search window ]]--
-local bgs_TooltipSearchOnly_Opt = CreateFrame("CheckButton", "BagSyncConfig_TooltipSearchOnly", bgsOpt, "OptionsBaseCheckButtonTemplate")
-bgs_TooltipSearchOnly_Opt:SetPoint("TOPLEFT", 16, -269)
-bgs_TooltipSearchOnly_Opt:SetScript("OnClick", function(frame)
-	if BagSyncOpt then
-		if frame:GetChecked() then
-			PlaySound("igMainMenuOptionCheckBoxOn")
-			BagSyncOpt["tooltipOnlySearch"] = true
-			if BagSync then BagSync:resetTooltip() end
-		else
-			PlaySound("igMainMenuOptionCheckBoxOff")
-			BagSyncOpt["tooltipOnlySearch"] = false
-			if BagSync then BagSync:resetTooltip() end
-		end
-	end
-end)
-local bgs_TooltipSearchOnly_OptText = bgs_TooltipSearchOnly_Opt:CreateFontString(nil, "ARTWORK", "GameFontHighlight")
-bgs_TooltipSearchOnly_OptText:SetPoint("LEFT", bgs_TooltipSearchOnly_Opt, "RIGHT", 0, 1)
-bgs_TooltipSearchOnly_OptText:SetText(L["Display modified tooltips ONLY in the BagSync Search window."])
-
---[[ Toggle for BagSync tooltips]]--
-local bgs_EnableBagSyncTooltips_Opt = CreateFrame("CheckButton", "BagSyncConfig_EnableBagSyncTooltips", bgsOpt, "OptionsBaseCheckButtonTemplate")
-bgs_EnableBagSyncTooltips_Opt:SetPoint("TOPLEFT", 16, -297)
-bgs_EnableBagSyncTooltips_Opt:SetScript("OnClick", function(frame)
-	if BagSyncOpt then
-		if frame:GetChecked() then
-			PlaySound("igMainMenuOptionCheckBoxOn")
-			BagSyncOpt["enableTooltips"] = true
-			if BagSync then BagSync:resetTooltip() end
-		else
-			PlaySound("igMainMenuOptionCheckBoxOff")
-			BagSyncOpt["enableTooltips"] = false
-			if BagSync then BagSync:resetTooltip() end
-		end
-	end
-end)
-local bgs_EnableBagSyncTooltips_OptText = bgs_EnableBagSyncTooltips_Opt:CreateFontString(nil, "ARTWORK", "GameFontHighlight")
-bgs_EnableBagSyncTooltips_OptText:SetPoint("LEFT", bgs_EnableBagSyncTooltips_Opt, "RIGHT", 0, 1)
-bgs_EnableBagSyncTooltips_OptText:SetText(L["Enable BagSync Tooltips"])
-
---[[ Toggle for BagSync Tooltip Seperator]]--
-local bgs_EnableBagSyncTooltipsSeperator_Opt = CreateFrame("CheckButton", "BagSyncConfig_EnableBagSyncTooltipsSeperator", bgsOpt, "OptionsBaseCheckButtonTemplate")
-bgs_EnableBagSyncTooltipsSeperator_Opt:SetPoint("TOPLEFT", 16, -325)
-bgs_EnableBagSyncTooltipsSeperator_Opt:SetScript("OnClick", function(frame)
-	if BagSyncOpt then
-		if frame:GetChecked() then
-			PlaySound("igMainMenuOptionCheckBoxOn")
-			BagSyncOpt["enableTooltipSeperator"] = true
-			if BagSync then BagSync:resetTooltip() end
-		else
-			PlaySound("igMainMenuOptionCheckBoxOff")
-			BagSyncOpt["enableTooltipSeperator"] = false
-			if BagSync then BagSync:resetTooltip() end
-		end
-	end
-end)
-local bgs_EnableBagSyncTooltipsSeperator_OptText = bgs_EnableBagSyncTooltipsSeperator_Opt:CreateFontString(nil, "ARTWORK", "GameFontHighlight")
-bgs_EnableBagSyncTooltipsSeperator_OptText:SetPoint("LEFT", bgs_EnableBagSyncTooltipsSeperator_Opt, "RIGHT", 0, 1)
-bgs_EnableBagSyncTooltipsSeperator_OptText:SetText(L["Enable empty line seperator above BagSync tooltip display."])
-
---[[ Toggle for Cross-Realms Items]]--
-local bgs_EnableCrossRealmsItems_Opt = CreateFrame("CheckButton", "BagSyncConfig_EnableCrossRealmsItems", bgsOpt, "OptionsBaseCheckButtonTemplate")
-bgs_EnableCrossRealmsItems_Opt:SetPoint("TOPLEFT", 16, -353)
-bgs_EnableCrossRealmsItems_Opt:SetScript("OnClick", function(frame)
-	if BagSyncOpt then
-		if frame:GetChecked() then
-			PlaySound("igMainMenuOptionCheckBoxOn")
-			BagSyncOpt["enableCrossRealmsItems"] = true
-			if BagSync then BagSync:resetTooltip() end
-		else
-			PlaySound("igMainMenuOptionCheckBoxOff")
-			BagSyncOpt["enableCrossRealmsItems"] = false
-			if BagSync then BagSync:resetTooltip() end
-		end
-	end
-end)
-local bgs_EnableCrossRealmsItems_OptText = bgs_EnableCrossRealmsItems_Opt:CreateFontString(nil, "ARTWORK", "GameFontHighlight")
-bgs_EnableCrossRealmsItems_OptText:SetPoint("LEFT", bgs_EnableCrossRealmsItems_Opt, "RIGHT", 0, 1)
-bgs_EnableCrossRealmsItems_OptText:SetText(L["Enable items for Cross-Realms characters."])
-
---[[ Toggle for current Battle.Net Account Character Items]]--
-local bgs_EnableBNetAccountItems_Opt = CreateFrame("CheckButton", "BagSyncConfig_EnableBNetAccountItems", bgsOpt, "OptionsBaseCheckButtonTemplate")
-bgs_EnableBNetAccountItems_Opt:SetPoint("TOPLEFT", 16, -381)
-bgs_EnableBNetAccountItems_Opt:SetScript("OnClick", function(frame)
-	if BagSyncOpt then
-		if frame:GetChecked() then
-			PlaySound("igMainMenuOptionCheckBoxOn")
-			BagSyncOpt["enableBNetAccountItems"] = true
-			if BagSync then BagSync:resetTooltip() end
-		else
-			PlaySound("igMainMenuOptionCheckBoxOff")
-			BagSyncOpt["enableBNetAccountItems"] = false
-			if BagSync then BagSync:resetTooltip() end
-		end
-	end
-end)
-local bgs_EnableBNetAccountItems_OptText = bgs_EnableBNetAccountItems_Opt:CreateFontString(nil, "ARTWORK", "GameFontHighlight")
-bgs_EnableBNetAccountItems_OptText:SetPoint("LEFT", bgs_EnableBNetAccountItems_Opt, "RIGHT", 0, 1)
-bgs_EnableBNetAccountItems_OptText:SetText(L["Enable items for current Battle.Net Account characters. |cFFDF2B2B((Not Recommended))|r"])
\ No newline at end of file
+local SO = LibStub("LibSimpleOptions-1.0")
+
+function BSOpt_Startup()
+
+	local panel = SO.AddOptionsPanel("BagSync", function() end)
+
+	local title, subText = panel:MakeTitleTextAndSubText("|cFF99CC33BagSync|r [|cFFDF2B2B"..ver.."|r]", "These options allow you to customize the BagSync displays data.")
+
+	--toggle BagSync tooltips
+	panel:MakeToggle(
+		"name", L["Enable BagSync Tooltips"],
+		"description", "",
+		"default", false,
+		"getFunc", function() return BagSyncOpt["enableTooltips"] end,
+		"setFunc", function(value)
+			BagSyncOpt["enableTooltips"] = value
+			BagSync:resetTooltip()
+			end
+	):SetPoint("TOPLEFT", subText, "BOTTOMLEFT", 0, -16)
+
+	--tooltip seperator
+	panel:MakeToggle(
+		"name", L["Enable empty line seperator above BagSync tooltip display."],
+		"description", "",
+		"default", false,
+		"getFunc", function() return BagSyncOpt["enableTooltipSeperator"] end,
+		"setFunc", function(value)
+			BagSyncOpt["enableTooltipSeperator"] = value
+			BagSync:resetTooltip()
+			end
+	):SetPoint("TOPLEFT", subText, "BOTTOMLEFT", 0, -41)
+
+	--total
+	panel:MakeToggle(
+		"name", L["Display [Total] in tooltips and gold display."],
+		"description", "",
+		"default", false,
+		"getFunc", function() return BagSyncOpt["showTotal"] end,
+		"setFunc", function(value)
+			BagSyncOpt["showTotal"] = value
+			BagSync:resetTooltip()
+			end
+	):SetPoint("TOPLEFT", subText, "BOTTOMLEFT", 0, -66)
+
+	--guild names
+	panel:MakeToggle(
+		"name", L["Display [Guild Name] display in tooltips."],
+		"description", "",
+		"default", false,
+		"getFunc", function() return BagSyncOpt["showGuildNames"] end,
+		"setFunc", function(value)
+			BagSyncOpt["showGuildNames"] = value
+			BagSync:resetTooltip()
+			end
+	):SetPoint("TOPLEFT", subText, "BOTTOMLEFT", 0, -91)
+
+	--factions
+	panel:MakeToggle(
+		"name", L["Display items for both factions (Alliance/Horde)."],
+		"description", "",
+		"default", false,
+		"getFunc", function() return BagSyncOpt["enableFaction"] end,
+		"setFunc", function(value)
+			BagSyncOpt["enableFaction"] = value
+			BagSync:resetTooltip()
+			end
+	):SetPoint("TOPLEFT", subText, "BOTTOMLEFT", 0, -116)
+
+	--class colors
+	panel:MakeToggle(
+		"name", L["Display class colors for characters."],
+		"description", "",
+		"default", false,
+		"getFunc", function() return BagSyncOpt["enableUnitClass"] end,
+		"setFunc", function(value)
+			BagSyncOpt["enableUnitClass"] = value
+			BagSync:resetTooltip()
+			end
+	):SetPoint("TOPLEFT", subText, "BOTTOMLEFT", 0, -141)
+
+	--minimap
+	panel:MakeToggle(
+		"name", L["Display BagSync minimap button."],
+		"description", "",
+		"default", false,
+		"getFunc", function() return BagSyncOpt["enableMinimap"] end,
+		"setFunc", function(value)
+			BagSyncOpt["enableMinimap"] = value
+			if value then BagSync_MinimapButton:Show() else BagSync_MinimapButton:Hide() end
+			end
+	):SetPoint("TOPLEFT", subText, "BOTTOMLEFT", 0, -166)
+
+	--guild info
+	panel:MakeToggle(
+		"name", L["Enable guild bank items."],
+		"description", "",
+		"default", false,
+		"getFunc", function() return BagSyncOpt["enableGuild"] end,
+		"setFunc", function(value)
+			BagSyncOpt["enableGuild"] = value
+			BagSync:resetTooltip()
+			end
+	):SetPoint("TOPLEFT", subText, "BOTTOMLEFT", 0, -191)
+
+	--mailbox info
+	panel:MakeToggle(
+		"name", L["Enable mailbox items."],
+		"description", "",
+		"default", false,
+		"getFunc", function() return BagSyncOpt["enableMailbox"] end,
+		"setFunc", function(value)
+			BagSyncOpt["enableMailbox"] = value
+			BagSync:resetTooltip()
+			end
+	):SetPoint("TOPLEFT", subText, "BOTTOMLEFT", 0, -216)
+
+	--auction house
+	panel:MakeToggle(
+		"name", L["Enable auction house items."],
+		"description", "",
+		"default", false,
+		"getFunc", function() return BagSyncOpt["enableAuction"] end,
+		"setFunc", function(value)
+			BagSyncOpt["enableAuction"] = value
+			BagSync:resetTooltip()
+			end
+	):SetPoint("TOPLEFT", subText, "BOTTOMLEFT", 0, -241)
+
+	--tooltip only on bagsync search window
+	panel:MakeToggle(
+		"name", L["Display modified tooltips ONLY in the BagSync Search window."],
+		"description", "",
+		"default", false,
+		"getFunc", function() return BagSyncOpt["tooltipOnlySearch"] end,
+		"setFunc", function(value)
+			BagSyncOpt["tooltipOnlySearch"] = value
+			BagSync:resetTooltip()
+			end
+	):SetPoint("TOPLEFT", subText, "BOTTOMLEFT", 0, -266)
+
+	--cross realms
+	panel:MakeToggle(
+		"name", L["Enable items for Cross-Realms characters."],
+		"description", "",
+		"default", false,
+		"getFunc", function() return BagSyncOpt["enableCrossRealmsItems"] end,
+		"setFunc", function(value)
+			BagSyncOpt["enableCrossRealmsItems"] = value
+			BagSync:resetTooltip()
+			end
+	):SetPoint("TOPLEFT", subText, "BOTTOMLEFT", 0, -291)
+
+	--battle.net account characters
+	panel:MakeToggle(
+		"name", L["Enable items for current Battle.Net Account characters. |cFFDF2B2B((Not Recommended))|r"],
+		"description", "",
+		"default", false,
+		"getFunc", function() return BagSyncOpt["enableBNetAccountItems"] end,
+		"setFunc", function(value)
+			BagSyncOpt["enableBNetAccountItems"] = value
+			BagSync:resetTooltip()
+			end
+	):SetPoint("TOPLEFT", subText, "BOTTOMLEFT", 0, -316)
+
+	--first color (default moss)
+	panel:MakeColorPicker(
+	    "name", L["Primary BagSync tooltip color."],
+	    "description", "",
+	    "hasAlpha", false,
+	    "defaultR", 128/255,
+	    "defaultG", 1,
+	    "defaultB", 0,
+	    "getFunc", function() return BagSyncOpt.colors.FIRST.r, BagSyncOpt.colors.FIRST.g, BagSyncOpt.colors.FIRST.b end,
+	    "setFunc", function(r, g, b) BagSyncOpt.colors.FIRST.r, BagSyncOpt.colors.FIRST.g, BagSyncOpt.colors.FIRST.b = r, g, b end
+	):SetPoint("TOPLEFT", subText, "BOTTOMLEFT", 0, -341)
+
+	--second color (default silver)
+	panel:MakeColorPicker(
+	    "name", L["Secondary BagSync tooltip color."],
+	    "description", "",
+	    "hasAlpha", false,
+	    "defaultR", 199/255,
+	    "defaultG", 199/255,
+	    "defaultB", 207/255,
+	    "getFunc", function() return BagSyncOpt.colors.SECOND.r, BagSyncOpt.colors.SECOND.g, BagSyncOpt.colors.SECOND.b end,
+	    "setFunc", function(r, g, b) BagSyncOpt.colors.SECOND.r, BagSyncOpt.colors.SECOND.g, BagSyncOpt.colors.SECOND.b = r, g, b end
+	):SetPoint("TOPLEFT", subText, "BOTTOMLEFT", 0, -366)
+
+	--total color
+	panel:MakeColorPicker(
+	    "name", L["BagSync [Total] tooltip color."],
+	    "description", "",
+	    "hasAlpha", false,
+	    "defaultR", 244/255,
+	    "defaultG", 164/255,
+	    "defaultB", 96/255,
+	    "getFunc", function() return BagSyncOpt.colors.TOTAL.r, BagSyncOpt.colors.TOTAL.g, BagSyncOpt.colors.TOTAL.b end,
+	    "setFunc", function(r, g, b) BagSyncOpt.colors.TOTAL.r, BagSyncOpt.colors.TOTAL.g, BagSyncOpt.colors.TOTAL.b = r, g, b end
+	):SetPoint("TOPLEFT", subText, "BOTTOMLEFT", 0, -391)
+
+	--guild color
+	panel:MakeColorPicker(
+	    "name", L["BagSync [Guild] tooltip color."],
+	    "description", "",
+	    "hasAlpha", false,
+	    "defaultR", 101/255,
+	    "defaultG", 184/255,
+	    "defaultB", 192/255,
+	    "getFunc", function() return BagSyncOpt.colors.GUILD.r, BagSyncOpt.colors.GUILD.g, BagSyncOpt.colors.GUILD.b end,
+	    "setFunc", function(r, g, b) BagSyncOpt.colors.GUILD.r, BagSyncOpt.colors.GUILD.g, BagSyncOpt.colors.GUILD.b = r, g, b end
+	):SetPoint("TOPLEFT", subText, "BOTTOMLEFT", 0, -416)
+
+	--cross realm color
+	panel:MakeColorPicker(
+	    "name", L["BagSync [Cross-Realms] tooltip color."],
+	    "description", "",
+	    "hasAlpha", false,
+	    "defaultR", 1,
+	    "defaultG", 125/255,
+	    "defaultB", 10/255,
+	    "getFunc", function() return BagSyncOpt.colors.CROSS.r, BagSyncOpt.colors.CROSS.g, BagSyncOpt.colors.CROSS.b end,
+	    "setFunc", function(r, g, b) BagSyncOpt.colors.CROSS.r, BagSyncOpt.colors.CROSS.g, BagSyncOpt.colors.CROSS.b = r, g, b end
+	):SetPoint("TOPLEFT", subText, "BOTTOMLEFT", 0, -441)
+
+	--bnet color
+	panel:MakeColorPicker(
+	    "name", L["BagSync [Battle.Net] tooltip color."],
+	    "description", "",
+	    "hasAlpha", false,
+	    "defaultR", 53/255,
+	    "defaultG", 136/255,
+	    "defaultB", 1,
+	    "getFunc", function() return BagSyncOpt.colors.BNET.r, BagSyncOpt.colors.BNET.g, BagSyncOpt.colors.BNET.b end,
+	    "setFunc", function(r, g, b) BagSyncOpt.colors.BNET.r, BagSyncOpt.colors.BNET.g, BagSyncOpt.colors.BNET.b = r, g, b end
+	):SetPoint("TOPLEFT", subText, "BOTTOMLEFT", 0, -466)
+
+
+	--i'm calling a refresh for the panel, because sometimes (like the color picker) some of the items aren't refreshed on the screen due to a /reload
+	--so instead I'm just going to force the getFunc for all the controls
+	panel:Refresh()
+
+end
\ No newline at end of file
diff --git a/BagSync_Crafts.lua b/BagSync_Crafts.lua
index e5dc424..675dfe3 100644
--- a/BagSync_Crafts.lua
+++ b/BagSync_Crafts.lua
@@ -1,8 +1,8 @@
 local L = BAGSYNC_L
 local craftsTable = {}
 local tRows, tAnchor = {}
-local currentPlayer = UnitName('player')
-local currentRealm = GetRealmName()
+local currentPlayer = UnitName("player")
+local currentRealm = select(2, UnitFullName("player"))

 local bgCrafts = CreateFrame("Frame","BagSync_CraftsFrame", UIParent)

@@ -11,7 +11,7 @@ local function LoadSlider()
 	local function OnEnter(self)
 		if self.canLink and self.owner then
 			GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
-			GameTooltip:AddLine(format('|cFF99CC33%s|r', self.owner))
+			GameTooltip:AddLine(format("|cFF99CC33%s|r", self.owner))
 			GameTooltip:AddLine(L["Left Click = Link to view tradeskill."])
 			GameTooltip:AddLine(L["Right Click = Insert tradeskill link."])
 			GameTooltip:Show()
@@ -50,7 +50,7 @@ local function LoadSlider()
 			row:SetScript("OnClick", function (self, button, down)
 				if self.link then
 					if button == "LeftButton" then
-						DEFAULT_CHAT_FRAME:AddMessage(format('%s|cFF99CC33%s|r ==> %s', L["Click to view profession: "], self.owner, self.link))
+						DEFAULT_CHAT_FRAME:AddMessage(format("%s|cFF99CC33%s|r ==> %s", L["Click to view profession: "], self.owner, self.link))
 					else
 						local editBox = ChatEdit_ChooseBoxForSend()

@@ -76,9 +76,9 @@ local function LoadSlider()
 						row.title:SetText("|cFFFFFFFF"..craftsTable[i + offset].name.."|r")
 					else
 						if craftsTable[i + offset].isLink then
-							row.title:SetText( format('|cFF99CC33%s|r |cFFFFFFFF(%s)|r', craftsTable[i + offset].name,  craftsTable[i + offset].level))
+							row.title:SetText( format("|cFF99CC33%s|r |cFFFFFFFF(%s)|r", craftsTable[i + offset].name,  craftsTable[i + offset].level))
 						else
-							row.title:SetText( format('|cFF6699FF%s|r |cFFFFFFFF(%s)|r', craftsTable[i + offset].name,  craftsTable[i + offset].level))
+							row.title:SetText( format("|cFF6699FF%s|r |cFFFFFFFF(%s)|r", craftsTable[i + offset].name,  craftsTable[i + offset].level))
 						end
 					end

@@ -148,7 +148,7 @@ local function DoCrafts()
 			tmp = {}
 			for q, r in pairs(v) do
 				if type(r) == "string" then
-					local trName, trSkillLevel = strsplit(',', r)
+					local trName, trSkillLevel = strsplit(",", r)
 					if trName and trSkillLevel then
 						table.insert(tmp, { name=trName, level=trSkillLevel, isLink=false, owner=k} )
 					end
diff --git a/BagSync_Profiles.lua b/BagSync_Profiles.lua
index afb3ebe..8a92d9f 100644
--- a/BagSync_Profiles.lua
+++ b/BagSync_Profiles.lua
@@ -1,11 +1,11 @@
 local L = BAGSYNC_L
-local currentPlayer = UnitName('player')
-local currentRealm = GetRealmName()
+local currentPlayer = UnitName("player")
+local currentRealm = select(2, UnitFullName("player"))
 local bgProfiles = CreateFrame("Frame","BagSync_ProfilesFrame", UIParent)

 --lets do the dropdown menu of DOOM
 local bgsProfilesDD = CreateFrame("Frame", "bgsProfilesDD")
-bgsProfilesDD.displayMode = 'MENU'
+bgsProfilesDD.displayMode = "MENU"

 local function addButton(level, text, isTitle, notCheckable, hasArrow, value, func)
 	local info = UIDropDownMenu_CreateInfo()
@@ -31,7 +31,7 @@ bgsProfilesDD.initialize = function(self, level)
 	table.sort(tmp, function(a,b) return (a < b) end)

 	if level == 1 then
-		PlaySound('gsTitleOptionExit')
+		PlaySound("gsTitleOptionExit")

 		for i=1, #tmp do
 			addButton(level, tmp[i], nil, 1, nil, tmp[i], function(frame, ...)
@@ -93,7 +93,7 @@ bgProfiles.toonName:SetBackdrop({
 	bgFile = "Interface\\Buttons\\WHITE8x8",
 })
 bgProfiles.toonName:SetBackdropColor(0,1,0,0.25)
-bgProfiles.toonName:SetScript("OnClick", function() ToggleDropDownMenu(1, nil, bgsProfilesDD, 'cursor', 0, 0)  end)
+bgProfiles.toonName:SetScript("OnClick", function() ToggleDropDownMenu(1, nil, bgsProfilesDD, "cursor", 0, 0)  end)
 bgProfiles.toonName.text = buttonText

 bgProfiles.deleteButton = CreateFrame("Button", nil, bgProfiles, "UIPanelButtonTemplate");
diff --git a/BagSync_Search.lua b/BagSync_Search.lua
index d59df67..48e3d2a 100644
--- a/BagSync_Search.lua
+++ b/BagSync_Search.lua
@@ -1,20 +1,20 @@
 local L = BAGSYNC_L
 local searchTable = {}
 local rows, anchor = {}
-local currentRealm = GetRealmName()
-local GetItemInfo = _G['GetItemInfo']
-local currentPlayer = UnitName('player')
+local currentRealm = select(2, UnitFullName("player"))
+local GetItemInfo = _G["GetItemInfo"]
+local currentPlayer = UnitName("player")

-local ItemSearch = LibStub('LibItemSearch-1.0')
+local ItemSearch = LibStub("LibItemSearch-1.0")
 local bgSearch = CreateFrame("Frame","BagSync_SearchFrame", UIParent)

 --add class search
-local tooltipScanner = _G['LibItemSearchTooltipScanner'] or CreateFrame('GameTooltip', 'LibItemSearchTooltipScanner', UIParent, 'GameTooltipTemplate')
+local tooltipScanner = _G["LibItemSearchTooltipScanner"] or CreateFrame("GameTooltip", "LibItemSearchTooltipScanner", UIParent, "GameTooltipTemplate")
 local tooltipCache = setmetatable({}, {__index = function(t, k) local v = {} t[k] = v return v end})

 ItemSearch:RegisterTypedSearch{
-	id = 'classRestriction',
-	tags = {'c', 'class'},
+	id = "classRestriction",
+	tags = {"c", "class"},

 	canSearch = function(self, _, search)
 		return search
@@ -23,7 +23,7 @@ ItemSearch:RegisterTypedSearch{
 	findItem = function(self, link, _, search)
 		if link:find("battlepet") then return false end

-		local itemID = link:match('item:(%d+)')
+		local itemID = link:match("item:(%d+)")
 		if not itemID then
 			return
 		end
@@ -33,7 +33,7 @@ ItemSearch:RegisterTypedSearch{
 			return cachedResult
 		end

-		tooltipScanner:SetOwner(UIParent, 'ANCHOR_NONE')
+		tooltipScanner:SetOwner(UIParent, "ANCHOR_NONE")
 		tooltipScanner:SetHyperlink(link)

 		local result = false
@@ -41,7 +41,7 @@ ItemSearch:RegisterTypedSearch{
 		local pattern = string.gsub(ITEM_CLASSES_ALLOWED:lower(), "%%s", "(.+)")

 		for i = 1, tooltipScanner:NumLines() do
-			local text =  _G[tooltipScanner:GetName() .. 'TextLeft' .. i]:GetText():lower()
+			local text =  _G[tooltipScanner:GetName() .. "TextLeft" .. i]:GetText():lower()
 			local textChk = string.find(text, pattern)

 			if textChk and tostring(text):find(search) then
@@ -117,7 +117,7 @@ local function LoadSlider()
 					if searchTable[i + offset].rarity then
 						--local hex = (select(4, GetItemQualityColor(searchTable[i + offset].rarity)))
 						local hex = (select(4, GetItemQualityColor(searchTable[i + offset].rarity)))
-						row.title:SetText(format('|c%s%s|r', hex, searchTable[i + offset].name) or searchTable[i + offset].name)
+						row.title:SetText(format("|c%s%s|r", hex, searchTable[i + offset].name) or searchTable[i + offset].name)
 					else
 						row.title:SetText(searchTable[i + offset].name)
 					end
@@ -199,7 +199,7 @@ local function DoSearch()
 		for k, v in pairs(xDB) do

 			local pFaction = v.faction or playerFaction --just in case ;) if we dont know the faction yet display it anyways
-			local yName, yRealm  = strsplit('^', k)
+			local yName, yRealm  = strsplit("^", k)

 			--check if we should show both factions or not
 			if BagSyncOpt.enableFaction or pFaction == playerFaction then
@@ -214,7 +214,7 @@ local function DoSearch()
 							--slotID = slotid for specific bagid, itemValue = data of specific slotid
 							if type(bagInfo) == "table" then
 								for slotID, itemValue in pairs(bagInfo) do
-									local dblink, dbcount = strsplit(',', itemValue)
+									local dblink, dbcount = strsplit(",", itemValue)
 									if dblink then
 										local dName, dItemLink, dRarity = GetItemInfo(dblink)
 										if dName and dItemLink then
@@ -249,7 +249,7 @@ local function DoSearch()
 						if not previousGuilds[gName] then
 							--we only really need to see this information once per guild
 							for q, r in pairs(BagSyncGUILD_DB[v.realm][guildN]) do
-								local dblink, dbcount = strsplit(',', r)
+								local dblink, dbcount = strsplit(",", r)
 								if dblink then
 									local dName, dItemLink, dRarity = GetItemInfo(dblink)
 									if dName then
diff --git a/BagSync_Tokens.lua b/BagSync_Tokens.lua
index 3297881..626ba3f 100644
--- a/BagSync_Tokens.lua
+++ b/BagSync_Tokens.lua
@@ -1,23 +1,25 @@
 local L = BAGSYNC_L
 local tokensTable = {}
 local tRows, tAnchor = {}
-local currentPlayer = UnitName('player')
-local currentRealm = GetRealmName()
-local GetItemInfo = _G['GetItemInfo']
-local SILVER = '|cffc7c7cf%s|r'
-local MOSS = '|cFF80FF00%s|r'
+local currentPlayer = UnitName("player")
+local currentRealm = select(2, UnitFullName("player"))
+local GetItemInfo = _G["GetItemInfo"]

 local bgTokens = CreateFrame("Frame","BagSync_TokensFrame", UIParent)

+local function tooltipColor(color, str)
+  return string.format("|cff%02x%02x%02x%s|r", (color.r or 1) * 255, (color.g or 1) * 255, (color.b or 1) * 255, str)
+end
+
 local function LoadSlider()

 	local function OnEnter(self)
 		if self.name and self.tooltip then
 			GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
 			GameTooltip:AddLine(self.name)
-			GameTooltip:AddLine(' ')
+			GameTooltip:AddLine(" ")
 			for i=1, #self.tooltip do
-				GameTooltip:AddDoubleLine(format(MOSS, self.tooltip[i].name), format(SILVER, self.tooltip[i].count))
+				GameTooltip:AddDoubleLine(tooltipColor(BagSyncOpt.colors.FIRST, self.tooltip[i].name), tooltipColor(BagSyncOpt.colors.SECOND, self.tooltip[i].count))
 			end
 			GameTooltip:Show()
 		end
diff --git a/libs/CallbackHandler-1.0.lua b/libs/CallbackHandler-1.0.lua
deleted file mode 100644
index 5ad658f..0000000
--- a/libs/CallbackHandler-1.0.lua
+++ /dev/null
@@ -1,239 +0,0 @@
---[[ $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/CallbackHandler-1.0/CallbackHandler-1.0.lua b/libs/CallbackHandler-1.0/CallbackHandler-1.0.lua
new file mode 100644
index 0000000..2a64013
--- /dev/null
+++ b/libs/CallbackHandler-1.0/CallbackHandler-1.0.lua
@@ -0,0 +1,238 @@
+--[[ $Id: CallbackHandler-1.0.lua 18 2014-10-16 02:52:20Z 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)
+
+	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/CallbackHandler-1.0/CallbackHandler-1.0.xml b/libs/CallbackHandler-1.0/CallbackHandler-1.0.xml
new file mode 100644
index 0000000..876df83
--- /dev/null
+++ b/libs/CallbackHandler-1.0/CallbackHandler-1.0.xml
@@ -0,0 +1,4 @@
+<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/
+..\FrameXML\UI.xsd">
+	<Script file="CallbackHandler-1.0.lua"/>
+</Ui>
\ No newline at end of file
diff --git a/libs/LibDataBroker-1.1.lua b/libs/LibDataBroker-1.1.lua
deleted file mode 100644
index f47c0cd..0000000
--- a/libs/LibDataBroker-1.1.lua
+++ /dev/null
@@ -1,90 +0,0 @@
-
-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/LibDataBroker-1.1/LibDataBroker-1.1.lua b/libs/LibDataBroker-1.1/LibDataBroker-1.1.lua
new file mode 100644
index 0000000..f47c0cd
--- /dev/null
+++ b/libs/LibDataBroker-1.1/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/LibDataBroker-1.1/README.textile b/libs/LibDataBroker-1.1/README.textile
new file mode 100644
index 0000000..ef16fed
--- /dev/null
+++ b/libs/LibDataBroker-1.1/README.textile
@@ -0,0 +1,13 @@
+LibDataBroker is a small WoW addon library designed to provide a "MVC":http://en.wikipedia.org/wiki/Model-view-controller interface for use in various addons.
+LDB's primary goal is to "detach" plugins for TitanPanel and FuBar from the display addon.
+Plugins can provide data into a simple table, and display addons can receive callbacks to refresh their display of this data.
+LDB also provides a place for addons to register "quicklaunch" functions, removing the need for authors to embed many large libraries to create minimap buttons.
+Users who do not wish to be "plagued" by these buttons simply do not install an addon to render them.
+
+Due to it's simple generic design, LDB can be used for any design where you wish to have an addon notified of changes to a table.
+
+h2. Links
+
+* "API documentation":http://github.com/tekkub/libdatabroker-1-1/wikis/api
+* "Data specifications":http://github.com/tekkub/libdatabroker-1-1/wikis/data-specifications
+* "Addons using LDB":http://github.com/tekkub/libdatabroker-1-1/wikis/addons-using-ldb
diff --git a/libs/LibItemSearch-1.0.lua b/libs/LibItemSearch-1.0.lua
deleted file mode 100644
index ab83398..0000000
--- a/libs/LibItemSearch-1.0.lua
+++ /dev/null
@@ -1,480 +0,0 @@
---[[
-	ItemSearch
-		An item text search engine of some sort
-
-	Grammar:
-		<search> 			:=	<intersect search>
-		<intersect search> 	:=	<union search> & <union search> ; <union search>
-		<union search>		:=	<negatable search>  | <negatable search> ; <negatable search>
-		<negatable search> 	:=	!<primitive search> ; <primitive search>
-		<primitive search>	:=	<tooltip search> ; <quality search> ; <type search> ; <text search>
-		<tooltip search>	:=  bop ; boa ; bou ; boe ; quest
-		<quality search>	:=	q<op><text> ; q<op><digit>
-		<ilvl search>		:=	ilvl<op><number>
-		<type search>		:=	t:<text>
-		<text search>		:=	<text>
-		<item set search>	:=	s:<setname> (setname can be * for all sets)
-		<op>				:=  : | = | == | != | ~= | < | > | <= | >=
---]]
-
-local Lib = LibStub:NewLibrary('LibItemSearch-1.0', 9)
-if not Lib then
-  return
-else
-  Lib.searchTypes = Lib.searchTypes or {}
-end
-
-
---[[ Locals ]]--
-
-local tonumber, select, split = tonumber, select, strsplit
-local function useful(a) -- check if the search has a decent size
-  return a and #a >= 1
-end
-
-local function compare(op, a, b)
-  if op == '<=' then
-    return a <= b
-  end
-
-  if op == '<' then
-    return a < b
-  end
-
-  if op == '>' then
-    return a > b
-  end
-
-  if op == '>=' then
-    return a >= b
-  end
-
-  return a == b
-end
-
-local function match(search, ...)
-  for i = 1, select('#', ...) do
-    local text = select(i, ...)
-    if text and text:lower():find(search) then
-      return true
-    end
-  end
-  return false
-end
-
-
---[[ User API ]]--
-
-function Lib:Find(itemLink, search)
-	if not useful(search) then
-		return true
-	end
-
-	if not itemLink then
-		return false
-	end
-
-  return self:FindUnionSearch(itemLink, split('\124', search:lower()))
-end
-
-
---[[ Top-Layer Processing ]]--
-
--- union search: <search>&<search>
-function Lib:FindUnionSearch(item, ...)
-	for i = 1, select('#', ...) do
-		local search = select(i, ...)
-		if useful(search) and self:FindIntersectSearch(item, split('\038', search)) then
-      		return true
-		end
-	end
-end
-
-
--- intersect search: <search>|<search>
-function Lib:FindIntersectSearch(item, ...)
-	for i = 1, select('#', ...) do
-		local search = select(i, ...)
-		if useful(search) and not self:FindNegatableSearch(item, search) then
-        	return false
-		end
-	end
-	return true
-end
-
-
--- negated search: !<search>
-function Lib:FindNegatableSearch(item, search)
-  local negatedSearch = search:match('^[!~][%s]*(.+)$')
-  if negatedSearch then
-    return not self:FindTypedSearch(item, negatedSearch)
-  end
-  return self:FindTypedSearch(item, search, true)
-end
-
-
---[[
-     Search Types:
-      easly defined search types
-
-      A typed search object should look like the following:
-        {
-          string id
-            unique identifier for the search type,
-
-          string searchCapture = function canSearch(self, search)
-            returns a capture if the given search matches this typed search
-
-          bool isMatch = function findItem(self, itemLink, searchCapture)
-            returns true if <itemLink> is in the search defined by <searchCapture>
-          }
---]]
-
-function Lib:RegisterTypedSearch(object)
-	self.searchTypes[object.id] = object
-end
-
-function Lib:GetTypedSearches()
-	return pairs(self.searchTypes)
-end
-
-function Lib:GetTypedSearch(id)
-	return self.searchTypes[id]
-end
-
-function Lib:FindTypedSearch(item, search, default)
-  if not useful(search) then
-    return default
-  end
-
-  local tag, rest = search:match('^[%s]*(%w+):(.*)$')
-  if tag then
-    if useful(rest) then
-      search = rest
-    else
-      return default
-    end
-  end
-
-  local operator, search = search:match('^[%s]*([%>%<%=]*)[%s]*(.*)$')
-  if useful(search) then
-    operator = useful(operator) and operator
-  else
-    return default
-  end
-
-  if tag then
-    tag = '^' .. tag
-    for id, searchType in self:GetTypedSearches() do
-      if searchType.tags then
-        for _, value in pairs(searchType.tags) do
-          if value:find(tag) then
-            return self:UseTypedSearch(searchType, item, operator, search)
-          end
-        end
-      end
-    end
-  else
-    for id, searchType in self:GetTypedSearches() do
-      if not searchType.onlyTags and self:UseTypedSearch(searchType, item, operator, search) then
-        return true
-      end
-    end
-    return false
-  end
-
-  return default
-end
-
-function Lib:UseTypedSearch(searchType, item, operator, search)
-  local capture1, capture2, capture3 = searchType:canSearch(operator, search)
-  if capture1 then
-    if searchType:findItem(item, operator, capture1, capture2, capture3) then
-      return true
-    end
-  end
-end
-
-
---[[ Item name ]]--
-
-Lib:RegisterTypedSearch{
-  id = 'itemName',
-  tags = {'n', 'name'},
-
-	canSearch = function(self, operator, search)
-		return not operator and search
-	end,
-
-	findItem = function(self, item, _, search)
-		local name = item:match('%[(.-)%]')
-		return match(search, name)
-	end
-}
-
-
---[[ Item type, subtype and equiploc ]]--
-
-Lib:RegisterTypedSearch{
-	id = 'itemType',
-	tags = {'t', 'type', 'slot'},
-
-	canSearch = function(self, operator, search)
-		return not operator and search
-	end,
-
-	findItem = function(self, item, _, search)
-		local type, subType, _, equipSlot = select(6, GetItemInfo(item))
-		return match(search, type, subType, _G[equipSlot])
-	end
-}
-
-
---[[ Item quality ]]--
-
-local qualities = {}
-for i = 0, #ITEM_QUALITY_COLORS do
-  qualities[i] = _G['ITEM_QUALITY' .. i .. '_DESC']:lower()
-end
-
-Lib:RegisterTypedSearch{
-	id = 'itemQuality',
-	tags = {'q', 'quality'},
-
-	canSearch = function(self, _, search)
-		for i, name in pairs(qualities) do
-		  if name:find(search) then
-			return i
-		  end
-		end
-	end,
-
-	findItem = function(self, link, operator, num)
-		local quality = select(3, GetItemInfo(link))
-		return compare(operator, quality, num)
-	end,
-}
-
-
---[[ Item level ]]--
-
-Lib:RegisterTypedSearch{
-	id = 'itemLevel',
-	tags = {'l', 'level', 'lvl'},
-
-	canSearch = function(self, _, search)
-		return tonumber(search)
-	end,
-
-	findItem = function(self, link, operator, num)
-		local lvl = select(4, GetItemInfo(link))
-		if lvl then
-			return compare(operator, lvl, num)
-		end
-	end,
-}
-
-
---[[ Tooltip searches ]]--
-
-local tooltipCache = setmetatable({}, {__index = function(t, k) local v = {} t[k] = v return v end})
-local tooltipScanner = _G['LibItemSearchTooltipScanner'] or CreateFrame('GameTooltip', 'LibItemSearchTooltipScanner', UIParent, 'GameTooltipTemplate')
-
-local function link_FindSearchInTooltip(itemLink, search)
-	local itemID = itemLink:match('item:(%d+)')
-	if not itemID then
-		return
-	end
-
-	local cachedResult = tooltipCache[search][itemID]
-	if cachedResult ~= nil then
-		return cachedResult
-	end
-
-	tooltipScanner:SetOwner(UIParent, 'ANCHOR_NONE')
-	tooltipScanner:SetHyperlink(itemLink)
-
-	local result = false
-	if tooltipScanner:NumLines() > 1 and _G[tooltipScanner:GetName() .. 'TextLeft2']:GetText() == search then
-		result = true
-	elseif tooltipScanner:NumLines() > 2 and _G[tooltipScanner:GetName() .. 'TextLeft3']:GetText() == search then
-		result = true
-	end
-
-	tooltipCache[search][itemID] = result
-	return result
-end
-
-
-Lib:RegisterTypedSearch{
-	id = 'bindType',
-
-	canSearch = function(self, _, search)
-		return self.keywords[search]
-	end,
-
-	findItem = function(self, itemLink, _, search)
-		return search and link_FindSearchInTooltip(itemLink, search)
-	end,
-
-	keywords = {
-    		['soulbound'] = ITEM_BIND_ON_PICKUP,
-    		['bound'] = ITEM_BIND_ON_PICKUP,
-		['boe'] = ITEM_BIND_ON_EQUIP,
-		['bop'] = ITEM_BIND_ON_PICKUP,
-		['bou'] = ITEM_BIND_ON_USE,
-		['quest'] = ITEM_BIND_QUEST,
-		['boa'] = ITEM_BIND_TO_BNETACCOUNT
-	}
-}
-
-Lib:RegisterTypedSearch{
-	id = 'tooltip',
-	tags = {'tt', 'tip', 'tooltip'},
-	onlyTags = true,
-
-	canSearch = function(self, _, search)
-		return search
-	end,
-
-	findItem = function(self, link, _, search)
-		tooltipScanner:SetOwner(UIParent, 'ANCHOR_NONE')
-		tooltipScanner:SetHyperlink(link)
-
-		for i = 1, tooltipScanner:NumLines() do
-			local text =  _G[tooltipScanner:GetName() .. 'TextLeft' .. i]:GetText():lower()
-
-			if text:find(search) then
-				return true
-			end
-		end
-
-		return false
-	end,
-}
-
-
---[[ Equipment sets ]]--
-
---Placeholder variables; will be replaced with references to the addon-appropriate handlers at runtime
-local ES_FindSets, ES_CheckItem
-
---Helper: Global Pattern Matching Function (matches ANY set name if search is *, or the EXACT set name if exactMatch is true, or any set name STARTING with the provided search terms if exactMatch is false (this means it will not match in middle of strings). all equipment set searches below use this function to FIRST try to find a set with the EXACT name entered, and if that fails they'll look for all sets that START with the search term, using recursive calls.
-local function ES_TrySetName(setName, search, exactMatch)
-	return (search == '*') or (exactMatch and setName:lower() == search) or (not exactMatch and setName:lower():sub(1,strlen(search)) == search)
-end
-
---Addon Support: ItemRack
-if IsAddOnLoaded('ItemRack') then
-	function ES_FindSets(setList, search, exactMatch)
-		for setName, _ in pairs(ItemRackUser.Sets) do
-			if ES_TrySetName(setName, search, exactMatch) then
-				if (search ~= '*') or (search == '*' and setName:sub(1,1) ~= '~') then --note: this additional tilde check skips internal ItemRack sets when doing a global set search (internal sets are prefixed with tilde, such as ~Unequip, and they contain temporary data that should not be part of a global search)
-					table.insert(setList, setName)
-				end
-			end
-		end
-		if (search ~= '*') and exactMatch and #setList == 0 then --if we just finished an exact, non-global (not "*"), name match search and still have no results, try one more time with partial ("starts with") set name matching instead
-			ES_FindSets(setList, search, false)
-		end
-	end
-
-	local irSameID = (ItemRack and ItemRack.SameID or nil) --set up local reference for speed if they're an ItemRack user
-	function ES_CheckItem(itemLink, setList)
-		local itemID = string.match(itemLink or '','item:(%-?%d+)') or 0 --grab the baseID of the item we are searching for (we don't need the full itemString, since we'll only be doing a loose baseID comparison below)
-
-		for _, setName in pairs(setList) do
-			for _, irItemData in pairs(ItemRackUser.Sets[setName].equip) do --note: do not change this to ipairs() or it will abort scanning at empty slots in a set
-				--[[ commented out due to libItemSearch lacking a "best match before generic match" priority matching system, so we'll have to go for "generic match" only (below), which matches items that have the same base ItemID as items from the set, as ItemRack cannot guarantee that the stored ItemString will be valid anymore (if the user has modified the item since last saving the set)
-				if itemString == irItemData then -- strict match: perform a strict match to check if this is the *exact* same item (same gems, enchants, etc)
-					return true
-				end]]--
-
-				if irSameID(itemID, irItemData) then --loose match: use ItemRack's built-in "Base ItemID" comparison function to allow us to match any items that have the same base itemID (disregarding strict matching of gems, enchants, etc); due to libItemSearch limitations it's the best compromise and guarantees to always highlight the correct items even if we may catch some extras/duplicates that weren't part of the set
-					return true
-				end
-			end
-		end
-
-		return false
-	end
-
---Addon Support: Wardrobe
-elseif IsAddOnLoaded('Wardrobe') then
-	function ES_FindSets(setList, search, exactMatch)
-		for _, waOutfit in ipairs(Wardrobe.CurrentConfig.Outfit) do
-			if ES_TrySetName(waOutfit.OutfitName, search, exactMatch) then
-				table.insert(setList, waOutfit) --insert an actual reference to the matching set's data table, instead of just storing the /name/ of the set. we do this due to how Wardrobe works (all sets are in a numerically indexed table and storing the table offset would therefore be unreliable)
-			end
-		end
-		if (search ~= '*') and exactMatch and #setList == 0 then --if we just finished an exact, non-global (not "*"), name match search and still have no results, try one more time with partial ("starts with") set name matching instead
-			ES_FindSets(setList, search, false)
-		end
-	end
-
-	function ES_CheckItem(itemLink, setList)
-		local itemID = tonumber(string.match(itemLink or '','item:(%-?%d+)') or 0) --grab the baseID of the item we are searching for (we don't need the full itemString, since we'll only be doing a loose baseID comparison below)
-
-		for _, waOutfit in pairs(setList) do
-			for _, waItemData in pairs(waOutfit.Item) do
-				if (waItemData.IsSlotUsed == 1) and (waItemData.ItemID == itemID) then --loose match: compare the current item's baseID to the baseID of the set item
-					return true
-				end
-			end
-		end
-
-		return false
-	end
-
---Last Resort: Blizzard Equipment Manager
-else
-	function ES_FindSets(setList, search, exactMatch)
-		for i = 1, GetNumEquipmentSets() do
-			local setName = GetEquipmentSetInfo(i)
-			if ES_TrySetName(setName, search, exactMatch) then
-				table.insert(setList, setName)
-			end
-		end
-		if (search ~= '*') and exactMatch and #setList == 0 then --if we just finished an exact, non-global (not "*"), name match search and still have no results, try one more time with partial ("starts with") set name matching instead
-			ES_FindSets(setList, search, false)
-		end
-	end
-
-	function ES_CheckItem(itemLink, setList)
-		local itemID = tonumber(string.match(itemLink or '','item:(%-?%d+)') or 0) --grab the baseID of the item we are searching for (we don't need the full itemString, since we'll only be doing a loose baseID comparison below)
-
-		for _, setName in pairs(setList) do
-			local bzSetItemIDs = GetEquipmentSetItemIDs(setName)
-			for _, bzItemID in pairs(bzSetItemIDs) do --note: do not change this to ipairs() or it will abort scanning at empty slots in a set
-				if itemID == bzItemID then --loose match: compare the current item's baseID to the baseID of the set item
-					return true
-				end
-			end
-		end
-
-		return false
-	end
-end
-
-Lib:RegisterTypedSearch{
-	id = 'equipmentSet',
-	tags = {'s', 'set'},
-
-	canSearch = function(self, operator, search)
-		return not operator and search
-	end,
-
-	findItem = function(self, itemLink, _, search)
-		--this is an item-set search and we know that the only items that can possibly match will be *equippable* items, so we'll short-circuit the response for non-equippable items to speed up searches.
-		if not IsEquippableItem(itemLink) then return false end
-
-		--default to matching *all* equipment sets if no set name has been provided yet
-		if search == '' then search = '*' end
-
-		--generate a list of all equipment sets whose names begin with the search term (or a single set if an exact set name match is found), then look for our item in those equipment sets
-		local setList = {}
-		ES_FindSets(setList, search, true)
-		if #setList == 0 then return false end
-		return ES_CheckItem(itemLink, setList)
-	end,
-}
\ No newline at end of file
diff --git a/libs/LibItemSearch-1.0/LibItemSearch-1.0.lua b/libs/LibItemSearch-1.0/LibItemSearch-1.0.lua
new file mode 100644
index 0000000..ab83398
--- /dev/null
+++ b/libs/LibItemSearch-1.0/LibItemSearch-1.0.lua
@@ -0,0 +1,480 @@
+--[[
+	ItemSearch
+		An item text search engine of some sort
+
+	Grammar:
+		<search> 			:=	<intersect search>
+		<intersect search> 	:=	<union search> & <union search> ; <union search>
+		<union search>		:=	<negatable search>  | <negatable search> ; <negatable search>
+		<negatable search> 	:=	!<primitive search> ; <primitive search>
+		<primitive search>	:=	<tooltip search> ; <quality search> ; <type search> ; <text search>
+		<tooltip search>	:=  bop ; boa ; bou ; boe ; quest
+		<quality search>	:=	q<op><text> ; q<op><digit>
+		<ilvl search>		:=	ilvl<op><number>
+		<type search>		:=	t:<text>
+		<text search>		:=	<text>
+		<item set search>	:=	s:<setname> (setname can be * for all sets)
+		<op>				:=  : | = | == | != | ~= | < | > | <= | >=
+--]]
+
+local Lib = LibStub:NewLibrary('LibItemSearch-1.0', 9)
+if not Lib then
+  return
+else
+  Lib.searchTypes = Lib.searchTypes or {}
+end
+
+
+--[[ Locals ]]--
+
+local tonumber, select, split = tonumber, select, strsplit
+local function useful(a) -- check if the search has a decent size
+  return a and #a >= 1
+end
+
+local function compare(op, a, b)
+  if op == '<=' then
+    return a <= b
+  end
+
+  if op == '<' then
+    return a < b
+  end
+
+  if op == '>' then
+    return a > b
+  end
+
+  if op == '>=' then
+    return a >= b
+  end
+
+  return a == b
+end
+
+local function match(search, ...)
+  for i = 1, select('#', ...) do
+    local text = select(i, ...)
+    if text and text:lower():find(search) then
+      return true
+    end
+  end
+  return false
+end
+
+
+--[[ User API ]]--
+
+function Lib:Find(itemLink, search)
+	if not useful(search) then
+		return true
+	end
+
+	if not itemLink then
+		return false
+	end
+
+  return self:FindUnionSearch(itemLink, split('\124', search:lower()))
+end
+
+
+--[[ Top-Layer Processing ]]--
+
+-- union search: <search>&<search>
+function Lib:FindUnionSearch(item, ...)
+	for i = 1, select('#', ...) do
+		local search = select(i, ...)
+		if useful(search) and self:FindIntersectSearch(item, split('\038', search)) then
+      		return true
+		end
+	end
+end
+
+
+-- intersect search: <search>|<search>
+function Lib:FindIntersectSearch(item, ...)
+	for i = 1, select('#', ...) do
+		local search = select(i, ...)
+		if useful(search) and not self:FindNegatableSearch(item, search) then
+        	return false
+		end
+	end
+	return true
+end
+
+
+-- negated search: !<search>
+function Lib:FindNegatableSearch(item, search)
+  local negatedSearch = search:match('^[!~][%s]*(.+)$')
+  if negatedSearch then
+    return not self:FindTypedSearch(item, negatedSearch)
+  end
+  return self:FindTypedSearch(item, search, true)
+end
+
+
+--[[
+     Search Types:
+      easly defined search types
+
+      A typed search object should look like the following:
+        {
+          string id
+            unique identifier for the search type,
+
+          string searchCapture = function canSearch(self, search)
+            returns a capture if the given search matches this typed search
+
+          bool isMatch = function findItem(self, itemLink, searchCapture)
+            returns true if <itemLink> is in the search defined by <searchCapture>
+          }
+--]]
+
+function Lib:RegisterTypedSearch(object)
+	self.searchTypes[object.id] = object
+end
+
+function Lib:GetTypedSearches()
+	return pairs(self.searchTypes)
+end
+
+function Lib:GetTypedSearch(id)
+	return self.searchTypes[id]
+end
+
+function Lib:FindTypedSearch(item, search, default)
+  if not useful(search) then
+    return default
+  end
+
+  local tag, rest = search:match('^[%s]*(%w+):(.*)$')
+  if tag then
+    if useful(rest) then
+      search = rest
+    else
+      return default
+    end
+  end
+
+  local operator, search = search:match('^[%s]*([%>%<%=]*)[%s]*(.*)$')
+  if useful(search) then
+    operator = useful(operator) and operator
+  else
+    return default
+  end
+
+  if tag then
+    tag = '^' .. tag
+    for id, searchType in self:GetTypedSearches() do
+      if searchType.tags then
+        for _, value in pairs(searchType.tags) do
+          if value:find(tag) then
+            return self:UseTypedSearch(searchType, item, operator, search)
+          end
+        end
+      end
+    end
+  else
+    for id, searchType in self:GetTypedSearches() do
+      if not searchType.onlyTags and self:UseTypedSearch(searchType, item, operator, search) then
+        return true
+      end
+    end
+    return false
+  end
+
+  return default
+end
+
+function Lib:UseTypedSearch(searchType, item, operator, search)
+  local capture1, capture2, capture3 = searchType:canSearch(operator, search)
+  if capture1 then
+    if searchType:findItem(item, operator, capture1, capture2, capture3) then
+      return true
+    end
+  end
+end
+
+
+--[[ Item name ]]--
+
+Lib:RegisterTypedSearch{
+  id = 'itemName',
+  tags = {'n', 'name'},
+
+	canSearch = function(self, operator, search)
+		return not operator and search
+	end,
+
+	findItem = function(self, item, _, search)
+		local name = item:match('%[(.-)%]')
+		return match(search, name)
+	end
+}
+
+
+--[[ Item type, subtype and equiploc ]]--
+
+Lib:RegisterTypedSearch{
+	id = 'itemType',
+	tags = {'t', 'type', 'slot'},
+
+	canSearch = function(self, operator, search)
+		return not operator and search
+	end,
+
+	findItem = function(self, item, _, search)
+		local type, subType, _, equipSlot = select(6, GetItemInfo(item))
+		return match(search, type, subType, _G[equipSlot])
+	end
+}
+
+
+--[[ Item quality ]]--
+
+local qualities = {}
+for i = 0, #ITEM_QUALITY_COLORS do
+  qualities[i] = _G['ITEM_QUALITY' .. i .. '_DESC']:lower()
+end
+
+Lib:RegisterTypedSearch{
+	id = 'itemQuality',
+	tags = {'q', 'quality'},
+
+	canSearch = function(self, _, search)
+		for i, name in pairs(qualities) do
+		  if name:find(search) then
+			return i
+		  end
+		end
+	end,
+
+	findItem = function(self, link, operator, num)
+		local quality = select(3, GetItemInfo(link))
+		return compare(operator, quality, num)
+	end,
+}
+
+
+--[[ Item level ]]--
+
+Lib:RegisterTypedSearch{
+	id = 'itemLevel',
+	tags = {'l', 'level', 'lvl'},
+
+	canSearch = function(self, _, search)
+		return tonumber(search)
+	end,
+
+	findItem = function(self, link, operator, num)
+		local lvl = select(4, GetItemInfo(link))
+		if lvl then
+			return compare(operator, lvl, num)
+		end
+	end,
+}
+
+
+--[[ Tooltip searches ]]--
+
+local tooltipCache = setmetatable({}, {__index = function(t, k) local v = {} t[k] = v return v end})
+local tooltipScanner = _G['LibItemSearchTooltipScanner'] or CreateFrame('GameTooltip', 'LibItemSearchTooltipScanner', UIParent, 'GameTooltipTemplate')
+
+local function link_FindSearchInTooltip(itemLink, search)
+	local itemID = itemLink:match('item:(%d+)')
+	if not itemID then
+		return
+	end
+
+	local cachedResult = tooltipCache[search][itemID]
+	if cachedResult ~= nil then
+		return cachedResult
+	end
+
+	tooltipScanner:SetOwner(UIParent, 'ANCHOR_NONE')
+	tooltipScanner:SetHyperlink(itemLink)
+
+	local result = false
+	if tooltipScanner:NumLines() > 1 and _G[tooltipScanner:GetName() .. 'TextLeft2']:GetText() == search then
+		result = true
+	elseif tooltipScanner:NumLines() > 2 and _G[tooltipScanner:GetName() .. 'TextLeft3']:GetText() == search then
+		result = true
+	end
+
+	tooltipCache[search][itemID] = result
+	return result
+end
+
+
+Lib:RegisterTypedSearch{
+	id = 'bindType',
+
+	canSearch = function(self, _, search)
+		return self.keywords[search]
+	end,
+
+	findItem = function(self, itemLink, _, search)
+		return search and link_FindSearchInTooltip(itemLink, search)
+	end,
+
+	keywords = {
+    		['soulbound'] = ITEM_BIND_ON_PICKUP,
+    		['bound'] = ITEM_BIND_ON_PICKUP,
+		['boe'] = ITEM_BIND_ON_EQUIP,
+		['bop'] = ITEM_BIND_ON_PICKUP,
+		['bou'] = ITEM_BIND_ON_USE,
+		['quest'] = ITEM_BIND_QUEST,
+		['boa'] = ITEM_BIND_TO_BNETACCOUNT
+	}
+}
+
+Lib:RegisterTypedSearch{
+	id = 'tooltip',
+	tags = {'tt', 'tip', 'tooltip'},
+	onlyTags = true,
+
+	canSearch = function(self, _, search)
+		return search
+	end,
+
+	findItem = function(self, link, _, search)
+		tooltipScanner:SetOwner(UIParent, 'ANCHOR_NONE')
+		tooltipScanner:SetHyperlink(link)
+
+		for i = 1, tooltipScanner:NumLines() do
+			local text =  _G[tooltipScanner:GetName() .. 'TextLeft' .. i]:GetText():lower()
+
+			if text:find(search) then
+				return true
+			end
+		end
+
+		return false
+	end,
+}
+
+
+--[[ Equipment sets ]]--
+
+--Placeholder variables; will be replaced with references to the addon-appropriate handlers at runtime
+local ES_FindSets, ES_CheckItem
+
+--Helper: Global Pattern Matching Function (matches ANY set name if search is *, or the EXACT set name if exactMatch is true, or any set name STARTING with the provided search terms if exactMatch is false (this means it will not match in middle of strings). all equipment set searches below use this function to FIRST try to find a set with the EXACT name entered, and if that fails they'll look for all sets that START with the search term, using recursive calls.
+local function ES_TrySetName(setName, search, exactMatch)
+	return (search == '*') or (exactMatch and setName:lower() == search) or (not exactMatch and setName:lower():sub(1,strlen(search)) == search)
+end
+
+--Addon Support: ItemRack
+if IsAddOnLoaded('ItemRack') then
+	function ES_FindSets(setList, search, exactMatch)
+		for setName, _ in pairs(ItemRackUser.Sets) do
+			if ES_TrySetName(setName, search, exactMatch) then
+				if (search ~= '*') or (search == '*' and setName:sub(1,1) ~= '~') then --note: this additional tilde check skips internal ItemRack sets when doing a global set search (internal sets are prefixed with tilde, such as ~Unequip, and they contain temporary data that should not be part of a global search)
+					table.insert(setList, setName)
+				end
+			end
+		end
+		if (search ~= '*') and exactMatch and #setList == 0 then --if we just finished an exact, non-global (not "*"), name match search and still have no results, try one more time with partial ("starts with") set name matching instead
+			ES_FindSets(setList, search, false)
+		end
+	end
+
+	local irSameID = (ItemRack and ItemRack.SameID or nil) --set up local reference for speed if they're an ItemRack user
+	function ES_CheckItem(itemLink, setList)
+		local itemID = string.match(itemLink or '','item:(%-?%d+)') or 0 --grab the baseID of the item we are searching for (we don't need the full itemString, since we'll only be doing a loose baseID comparison below)
+
+		for _, setName in pairs(setList) do
+			for _, irItemData in pairs(ItemRackUser.Sets[setName].equip) do --note: do not change this to ipairs() or it will abort scanning at empty slots in a set
+				--[[ commented out due to libItemSearch lacking a "best match before generic match" priority matching system, so we'll have to go for "generic match" only (below), which matches items that have the same base ItemID as items from the set, as ItemRack cannot guarantee that the stored ItemString will be valid anymore (if the user has modified the item since last saving the set)
+				if itemString == irItemData then -- strict match: perform a strict match to check if this is the *exact* same item (same gems, enchants, etc)
+					return true
+				end]]--
+
+				if irSameID(itemID, irItemData) then --loose match: use ItemRack's built-in "Base ItemID" comparison function to allow us to match any items that have the same base itemID (disregarding strict matching of gems, enchants, etc); due to libItemSearch limitations it's the best compromise and guarantees to always highlight the correct items even if we may catch some extras/duplicates that weren't part of the set
+					return true
+				end
+			end
+		end
+
+		return false
+	end
+
+--Addon Support: Wardrobe
+elseif IsAddOnLoaded('Wardrobe') then
+	function ES_FindSets(setList, search, exactMatch)
+		for _, waOutfit in ipairs(Wardrobe.CurrentConfig.Outfit) do
+			if ES_TrySetName(waOutfit.OutfitName, search, exactMatch) then
+				table.insert(setList, waOutfit) --insert an actual reference to the matching set's data table, instead of just storing the /name/ of the set. we do this due to how Wardrobe works (all sets are in a numerically indexed table and storing the table offset would therefore be unreliable)
+			end
+		end
+		if (search ~= '*') and exactMatch and #setList == 0 then --if we just finished an exact, non-global (not "*"), name match search and still have no results, try one more time with partial ("starts with") set name matching instead
+			ES_FindSets(setList, search, false)
+		end
+	end
+
+	function ES_CheckItem(itemLink, setList)
+		local itemID = tonumber(string.match(itemLink or '','item:(%-?%d+)') or 0) --grab the baseID of the item we are searching for (we don't need the full itemString, since we'll only be doing a loose baseID comparison below)
+
+		for _, waOutfit in pairs(setList) do
+			for _, waItemData in pairs(waOutfit.Item) do
+				if (waItemData.IsSlotUsed == 1) and (waItemData.ItemID == itemID) then --loose match: compare the current item's baseID to the baseID of the set item
+					return true
+				end
+			end
+		end
+
+		return false
+	end
+
+--Last Resort: Blizzard Equipment Manager
+else
+	function ES_FindSets(setList, search, exactMatch)
+		for i = 1, GetNumEquipmentSets() do
+			local setName = GetEquipmentSetInfo(i)
+			if ES_TrySetName(setName, search, exactMatch) then
+				table.insert(setList, setName)
+			end
+		end
+		if (search ~= '*') and exactMatch and #setList == 0 then --if we just finished an exact, non-global (not "*"), name match search and still have no results, try one more time with partial ("starts with") set name matching instead
+			ES_FindSets(setList, search, false)
+		end
+	end
+
+	function ES_CheckItem(itemLink, setList)
+		local itemID = tonumber(string.match(itemLink or '','item:(%-?%d+)') or 0) --grab the baseID of the item we are searching for (we don't need the full itemString, since we'll only be doing a loose baseID comparison below)
+
+		for _, setName in pairs(setList) do
+			local bzSetItemIDs = GetEquipmentSetItemIDs(setName)
+			for _, bzItemID in pairs(bzSetItemIDs) do --note: do not change this to ipairs() or it will abort scanning at empty slots in a set
+				if itemID == bzItemID then --loose match: compare the current item's baseID to the baseID of the set item
+					return true
+				end
+			end
+		end
+
+		return false
+	end
+end
+
+Lib:RegisterTypedSearch{
+	id = 'equipmentSet',
+	tags = {'s', 'set'},
+
+	canSearch = function(self, operator, search)
+		return not operator and search
+	end,
+
+	findItem = function(self, itemLink, _, search)
+		--this is an item-set search and we know that the only items that can possibly match will be *equippable* items, so we'll short-circuit the response for non-equippable items to speed up searches.
+		if not IsEquippableItem(itemLink) then return false end
+
+		--default to matching *all* equipment sets if no set name has been provided yet
+		if search == '' then search = '*' end
+
+		--generate a list of all equipment sets whose names begin with the search term (or a single set if an exact set name match is found), then look for our item in those equipment sets
+		local setList = {}
+		ES_FindSets(setList, search, true)
+		if #setList == 0 then return false end
+		return ES_CheckItem(itemLink, setList)
+	end,
+}
\ No newline at end of file
diff --git a/libs/LibSimpleOptions-1.0/Changelog-LibSimpleOptions-1.0-r46.txt b/libs/LibSimpleOptions-1.0/Changelog-LibSimpleOptions-1.0-r46.txt
new file mode 100644
index 0000000..54b7fb1
--- /dev/null
+++ b/libs/LibSimpleOptions-1.0/Changelog-LibSimpleOptions-1.0-r46.txt
@@ -0,0 +1,15 @@
+------------------------------------------------------------------------
+r46 | cladhaire | 2012-09-09 08:40:07 +0000 (Sun, 09 Sep 2012) | 2 lines
+Changed paths:
+   M /trunk/LibSimpleOptions-1.0.lua
+   M /trunk/LibSimpleOptions-1.0.toc
+
+Update for MoP
+
+------------------------------------------------------------------------
+r45 | nevcairiel | 2009-03-06 10:12:36 +0000 (Fri, 06 Mar 2009) | 1 line
+Changed paths:
+   M /trunk/LibSimpleOptions-1.0.toc
+
+Restore LoD to Libraries, as 3.0.8 fixed all bugs that blocked LoD OptionalDeps to load properly.
+------------------------------------------------------------------------
diff --git a/libs/LibSimpleOptions-1.0/LibSimpleOptions-1.0.lua b/libs/LibSimpleOptions-1.0/LibSimpleOptions-1.0.lua
new file mode 100644
index 0000000..38f81a5
--- /dev/null
+++ b/libs/LibSimpleOptions-1.0/LibSimpleOptions-1.0.lua
@@ -0,0 +1,957 @@
+--[[
+Name: LibSimpleOptions-1.0
+Revision: $Rev: 46 $
+Author(s): ckknight (ckknight@gmail.com)
+Website: http://ckknight.wowinterface.com/
+Description: A library to provide a way to easily create controls for Blizzard's options system
+License: MIT
+]]
+
+local MAJOR_VERSION = "LibSimpleOptions-1.0"
+local MINOR_VERSION = 90000 + tonumber(("$Revision: 47 $"):match("(%d+)"))
+
+if not LibStub then error(MAJOR_VERSION .. " requires LibStub") end
+
+-- #AUTODOC_NAMESPACE LibSimpleOptions
+
+local LibSimpleOptions, oldLib = LibStub:NewLibrary(MAJOR_VERSION, MINOR_VERSION)
+if not LibSimpleOptions then
+	return
+end
+if oldLib then
+	oldLib = {}
+	for k, v in pairs(LibSimpleOptions) do
+		LibSimpleOptions[k] = nil
+		oldLib[k] = v
+	end
+end
+
+local getArgs, doneArgs
+do
+	local tmp = {}
+	function getArgs(...)
+		assert(next(tmp) == nil)
+		for i = 1, select('#', ...), 2 do
+			local k, v = select(i, ...)
+			if type(k) ~= "string" then
+				error(("Received a bad key, must be a %q, got %q (%s)"):format("string", type(k), tostring(k)), 3)
+			elseif tmp[k] ~= nil then
+				error(("Received key %q twice"):format(k), 3)
+			end
+			tmp[k] = v
+		end
+		return tmp
+	end
+	function doneArgs(args)
+		assert(args == tmp)
+		for k in pairs(args) do
+			args[k] = nil
+		end
+		return nil
+	end
+end
+
+local WotLK = not not ToggleAchievementFrame
+
+local panels
+if oldLib then
+	panels = oldLib.panels or {}
+else
+	panels = {}
+end
+LibSimpleOptions.panels = panels
+
+local panelMeta
+if oldLib then
+	panelMeta = oldLib.panelMeta or {}
+else
+	panelMeta = {}
+end
+LibSimpleOptions.panelMeta = panelMeta
+for funcName in pairs(panelMeta) do
+	for panel in pairs(panels) do
+		panel[funcName] = nil
+	end
+	panelMeta[funcName] = nil
+end
+
+do
+	local function update(control, ...)
+		if (...) ~= control.value then
+			control:SetValue(...)
+		end
+	end
+	--- Refresh a panel's controls
+	-- This updates any controls that provide a getFunc
+	-- When a panel is shown, this is automatically called
+	-- @name panel:Refresh
+	-- @usage panel:Refresh()
+	function panelMeta:Refresh()
+		for control in pairs(self.controls) do
+			if control.getFunc then
+				update(control, control.getFunc())
+			end
+		end
+		if self.refreshFunc then
+			self:refreshFunc()
+		end
+	end
+	local function panel_OnShow(self)
+		self:SetScript("OnShow", self.Refresh)
+		self:controlCreationFunc()
+		self.controlCreationFunc = nil
+		self:Refresh()
+	end
+	local function panel_okay(self)
+		for control in pairs(self.controls) do
+			control.oldValue = control.value
+			if control.okayFunc then
+				control.okayFunc()
+			end
+		end
+	end
+	local function panel_cancel(self)
+		for control in pairs(self.controls) do
+			control:SetValue(control.oldValue)
+			if control.cancelFunc then
+				control.cancelFunc()
+			end
+		end
+	end
+	local function panel_default(self)
+		for control in pairs(self.controls) do
+			control:SetValue(control.default)
+			if control.defaultFunc then
+				control.defaultFunc()
+			end
+		end
+	end
+	local function makePanel(name, parentName, controlCreationFunc)
+		local panel
+		if not parentName then
+			panel = CreateFrame("Frame", name .. "_Panel")
+		else
+			panel = CreateFrame("Frame", parentName .. "_Panel_" .. name)
+		end
+		panels[panel] = true
+
+		panel.name = name
+		panel.controls = {}
+		panel.parent = parentName
+
+		panel.okay = panel_okay
+		panel.cancel = panel_cancel
+		panel.default = panel_default
+
+		InterfaceOptions_AddCategory(panel)
+
+		panel.controlCreationFunc = controlCreationFunc
+		panel:SetScript("OnShow", panel_OnShow)
+		for k, v in pairs(panelMeta) do
+			panel[k] = v
+		end
+
+		return panel
+	end
+
+	--- Make a new options panel and add it to the Blizzard Interface Options
+	-- @param name name of your panel
+	-- @param controlCreationFunc function to call when the panel is first shown
+	-- @usage LibStub("LibSimpleOptions-1.0").AddOptionsPanel("My Options", function(panel) ... end)
+	-- @return the created panel
+	function LibSimpleOptions.AddOptionsPanel(name, controlCreationFunc)
+		return makePanel(name, nil, controlCreationFunc)
+	end
+
+	--- Make a new options panel that is a child of another options panel and add it to the Blizzard Interface Options
+	-- @param parentName name of the parent panel
+	-- @param name name of your panel
+	-- @param controlCreationFunc function to call when the panel is first shown
+	-- @usage LibStub("LibSimpleOptions-1.0").AddOptionsPanel("My Options", "My Suboptions", function(panel) ... end)
+	-- @return the created panel
+	function LibSimpleOptions.AddSuboptionsPanel(parentName, name, controlCreationFunc)
+		return makePanel(name, parentName, controlCreationFunc)
+	end
+end
+
+--- Return a new title text and sub-text for a panel.
+-- Note that this automatically places the title and sub-text appropriately
+-- @name panel:MakeTitleTextAndSubText
+-- @param titleText the text to show as the title
+-- @param subTextText the text to show as the sub-text
+-- @usage local title, subText = panel:MakeTitleTextAndSubText("My Options", "These allow you to change assorted options")
+-- @return the title FontString
+-- @return the sub-text FontString
+function panelMeta:MakeTitleTextAndSubText(titleText, subTextText)
+	local title = self:CreateFontString(nil, "ARTWORK", "GameFontNormalLarge")
+	title:SetText(titleText)
+	title:SetJustifyH("LEFT")
+	title:SetJustifyV("TOP")
+	title:SetPoint("TOPLEFT", 16, -16)
+
+	local subText = self:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
+	subText:SetText(subTextText)
+	subText:SetNonSpaceWrap(true)
+	subText:SetJustifyH("LEFT")
+	subText:SetJustifyV("TOP")
+	subText:SetPoint("TOPLEFT", title, "BOTTOMLEFT", 0, -8)
+	subText:SetPoint("RIGHT", -32, 0)
+
+	return title, subText
+end
+
+do
+	local backdrop = {
+		bgFile = [=[Interface\Buttons\WHITE8X8]=],
+		edgeFile = [=[Interface\Tooltips\UI-Tooltip-Border]=],
+		tile = true,
+		tileSize = 16,
+		edgeSize = 16,
+		insets = { left = 3, right = 3, top = 3, bottom = 3 },
+	}
+	--- Return a scrollable frame to organize controls within
+	-- This is useful to create if you have too many controls to properly fit within one panel
+	-- @name panel:MakeScrollFrame
+	-- @usage local scrollFrame = panel:MakeScrollFrame()
+	-- @return the ScrollFrame
+	function panelMeta:MakeScrollFrame()
+		local name
+		local i = 0
+		repeat
+			i = i + 1
+			name = self:GetName() .. "_ScrollFrame" .. i
+		until not _G[name]
+		local scrollFrame = CreateFrame("ScrollFrame", name, self, "UIPanelScrollFrameTemplate")
+		scrollFrame:SetFrameLevel(scrollFrame:GetFrameLevel() + 1)
+		local bg = CreateFrame("Frame", nil, scrollFrame)
+		bg:SetPoint("TOPLEFT", scrollFrame, "TOPLEFT", -3, 3)
+		bg:SetPoint("BOTTOMRIGHT", scrollFrame, "BOTTOMRIGHT", 3, -3)
+		bg:SetBackdrop(backdrop)
+		bg:SetBackdropColor(0, 0, 0, 0.25)
+		local scrollChild = CreateFrame("Frame", name .. "_Child", scrollFrame)
+		scrollFrame:SetScrollChild(scrollChild)
+		scrollChild:SetWidth(1)
+		scrollChild:SetHeight(1)
+		return scrollFrame, scrollChild
+	end
+end
+
+do
+	local function slider_OnValueChanged(self)
+		self.value = self:GetValue()
+		self:SetValue(self:GetValue())
+	end
+
+	local function slider_SetValue(self, value)
+		getmetatable(self).__index.SetValue(self, value)
+		self.value = value
+		self.changeFunc(value)
+		if self.currentText then
+			self.currentText:SetText(self.currentTextFunc(value))
+		end
+	end
+
+	--- Return a horizontal slider
+	-- This is primarily for manipulating numbers within a range
+	-- @name panel:MakeSlider
+	-- @param ... tuple of key-value pairs<br/>
+	--     name: What the slider displays above it<br/>
+	--     description: What the tooltip displays when hovering over<br/>
+	--     minText: What the slider shows on the left side<br/>
+	--     maxText: What the slider shows on the right side<br/>
+	--     minValue: The minimum value of the slider<br/>
+	--     maxValue: The maximum value of the slider<br/>
+	--     [optional] step: The amount that the slider steps between movements<br/>
+	--     default: The default value<br/>
+	--     current: The current value - can provide either this or getFunc<br/>
+	--     getFunc: Function to get the current value<br/>
+	--     setFunc: What is called when the value changes<br/>
+	--     [optional] currentTextFunc: What is called to get text value at the bottom<br/>
+	--     [optional] okayFunc: Called when the okay button is pressed<br/>
+	--     [optional] cancelFunc: Called when the cancel button is pressed<br/>
+	--     [optional] defaultFunc: Called when the default button is pressed
+	-- @usage panel:MakeSlider(
+	--     'name', 'Range',
+	--     'description', 'Specify your tooltip description',
+	--     'minText', '0%',
+	--     'maxText', '100%',
+	--     'minValue', 0,
+	--     'maxValue', 1,
+	--     'step', 0.05,
+	--     'default', 0.5,
+	--     'current', db.currentRange,
+	--     'setFunc', function(value) db.currentRange = value end,
+	--     'currentTextFunc', function(value) return ("%.0f%%"):format(value * 100) end
+	-- )
+	-- @return the Slider
+	function panelMeta:MakeSlider(...)
+		local args = getArgs(...)
+		if type(args.name) ~= "string" then
+			error(("name must be %q, got %q (%s)"):format("string", type(args.name), tostring(args.name)), 2)
+		elseif type(args.description) ~= "string" then
+			error(("description must be %q, got %q (%s)"):format("string", type(args.description), tostring(args.description)), 2)
+		elseif type(args.minText) ~= "string" then
+			error(("minText must be %q, got %q (%s)"):format("string", type(args.minText), tostring(args.minText)), 2)
+		elseif type(args.maxText) ~= "string" then
+			error(("maxText must be %q, got %q (%s)"):format("string", type(args.maxText), tostring(args.maxText)), 2)
+		elseif type(args.minValue) ~= "number" then
+			error(("minValue must be %q, got %q (%s)"):format("number", type(args.minValue), tostring(args.minValue)), 2)
+		elseif type(args.maxValue) ~= "number" then
+			error(("maxValue must be %q, got %q (%s)"):format("number", type(args.maxValue), tostring(args.maxValue)), 2)
+		elseif args.step and type(args.step) ~= "number" then
+			error(("step must be %q or %q, got %q (%s)"):format("nil", "number", type(args.step), tostring(args.step)), 2)
+		elseif type(args.default) ~= "number" then
+			error(("default must be %q, got %q (%s)"):format("number", type(args.default), tostring(args.default)), 2)
+		elseif args.default < args.minValue or args.default > args.maxValue then
+			error(("default must be [%s, %s], got %s"):format(args.minValue, args.maxValue, tostring(args.default)), 2)
+		elseif not args.current == not args.getFunc then
+			error(("either current or getFunc must be supplied, but not both"), 2)
+		elseif args.current and type(args.current) ~= "number" then
+			error(("current must be %q, got %q (%s)"):format("number", type(args.current), tostring(args.current)), 2)
+		elseif args.getFunc and type(args.getFunc) ~= "function" then
+			error(("getFunc must be %q, got %q (%s)"):format("function", type(args.getFunc), tostring(args.getFunc)), 2)
+		elseif type(args.setFunc) ~= "function" then
+			error(("setFunc must be %q, got %q (%s)"):format("function", type(args.setFunc), tostring(args.setFunc)), 2)
+		elseif args.currentTextFunc and type(args.currentTextFunc) ~= "function" then
+			error(("currentTextFunc must be %q or %q, got %q (%s)"):format("nil", "function", type(args.currentTextFunc), tostring(args.currentTextFunc)), 2)
+		elseif args.okayFunc and type(args.okayFunc) ~= "function" then
+			error(("okayFunc must be %q or %q, got %q (%s)"):format("nil", "function", type(args.okayFunc), tostring(args.okayFunc)), 2)
+		elseif args.cancelFunc and type(args.cancelFunc) ~= "function" then
+			error(("cancelFunc must be %q or %q, got %q (%s)"):format("nil", "function", type(args.cancelFunc), tostring(args.cancelFunc)), 2)
+		elseif args.defaultFunc and type(args.defaultFunc) ~= "function" then
+			error(("defaultFunc must be %q or %q, got %q (%s)"):format("nil", "function", type(args.defaultFunc), tostring(args.defaultFunc)), 2)
+		end
+
+		local name
+		local i = 0
+		repeat
+			i = i + 1
+			name = self:GetName() .. "_Slider" .. i
+		until not _G[name]
+		local slider = CreateFrame("Slider", name, self, "OptionsSliderTemplate")
+		self.controls[slider] = true
+		_G[slider:GetName() .. "Text"]:SetText(args.name)
+		slider.tooltipText = args.description
+		_G[slider:GetName() .. "Text"]:SetTextColor(NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b)
+		_G[slider:GetName() .. "Low"]:SetText(args.minText)
+		_G[slider:GetName() .. "Low"]:SetTextColor(HIGHLIGHT_FONT_COLOR.r, HIGHLIGHT_FONT_COLOR.g, HIGHLIGHT_FONT_COLOR.b)
+		_G[slider:GetName() .. "High"]:SetText(args.maxText)
+		_G[slider:GetName() .. "High"]:SetTextColor(HIGHLIGHT_FONT_COLOR.r, HIGHLIGHT_FONT_COLOR.g, HIGHLIGHT_FONT_COLOR.b)
+
+		local current
+		if args.getFunc then
+			slider.getFunc = args.getFunc
+			current = args.getFunc()
+		else
+			current = args.current
+		end
+
+		if args.currentTextFunc then
+			slider.currentTextFunc = args.currentTextFunc
+			local currentText = slider:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
+			slider.currentText = currentText
+			currentText:SetPoint("TOP", slider, "CENTER", 0, -8)
+			currentText:SetText(args.currentTextFunc(current))
+		end
+
+		slider.default = args.default
+		slider:SetMinMaxValues(args.minValue, args.maxValue)
+		if args.step then
+			slider:SetValueStep(args.step)
+		end
+		slider.oldValue = current
+		slider.value = current
+		slider:SetValue(current)
+		slider.changeFunc = args.setFunc
+		slider.SetValue = slider_SetValue
+		slider:SetScript("OnValueChanged", slider_OnValueChanged)
+		slider.okayFunc = args.okayFunc
+		slider.cancelFunc = args.cancelFunc
+		slider.defaultFunc = args.defaultFunc
+		args = doneArgs(args)
+		return slider
+	end
+end
+
+local function generic_OnEnter(self)
+	GameTooltip:SetOwner(self, "ANCHOR_TOPRIGHT")
+	GameTooltip:SetText(self.tooltipText, nil, nil, nil, nil, 1)
+end
+local function generic_OnLeave(self)
+	GameTooltip:Hide()
+end
+
+do
+	local function dropDown_SetValue(self, value)
+		self.value = value
+		UIDropDownMenu_SetSelectedValue(self, value)
+		self.changeFunc(value)
+	end
+	local helper__num, helper__values
+	local function helper()
+		local value, text = helper__values[helper__num], helper__values[helper__num+1]
+		if value == nil then
+			helper__num, helper__values = nil, nil
+			return nil
+		end
+		helper__num = helper__num + 2
+		return value, text
+	end
+	local function get_iter(values)
+		if type(values) == "function" then
+			return values
+		end
+		helper__num = 1
+		helper__values = values
+		return helper
+	end
+	local SetValue_wrapper
+	if WotLK then
+		function SetValue_wrapper(self, ...)
+			return dropDown_SetValue(...)
+		end
+	else
+		SetValue_wrapper = dropDown_SetValue
+	end
+	local function dropDown_menu(self)
+		for value, text in get_iter(self.values) do
+			local info = UIDropDownMenu_CreateInfo()
+			info.text = text
+			info.value = value
+			info.checked = self.value == value
+			info.func = SetValue_wrapper
+			info.arg1 = self
+			info.arg2 = value
+			UIDropDownMenu_AddButton(info)
+		end
+	end
+
+	local tmp = {}
+	--- Return a single-choice dropdown menu
+	-- This is for choosing a single choice among many
+	-- @name panel:MakeDropDown
+	-- @param ... tuple of key-value pairs<br/>
+	--     name: What shows above the dropdown<br/>
+	--     description: What shows when hovering over the dropdown<br/>
+	--     values: A list of options, in order, where the odd keys are the key and even are its corresponding value<br/>
+	--     default: The default key<br/>
+	--     current: The current key - you can either provide this or getFunc<br/>
+	--     getFunc: Function to return the current key<br/>
+	--     setFunc: What is called when the key changes<br/>
+	--     [optional] okayFunc: Called when the okay button is pressed<br/>
+	--     [optional] cancelFunc: Called when the cancel button is pressed<br/>
+	--     [optional] defaultFunc: Called when the default button is pressed
+	-- @usage panel:MakeDropDown(
+	--     'name', 'Choose',
+	--     'description', 'Specify your tooltip description',
+	--     'values', {
+	--         'ONE', "One",
+	--         'TWO', "Two",
+	--         'THREE', "Three",
+	--      },
+	--     'default', 'ONE',
+	--     'current', db.choice,
+	--     'setFunc', function(value) db.choice = value end,
+	-- )
+	-- @return the DropDown frame
+	function panelMeta:MakeDropDown(...)
+		local args = getArgs(...)
+		if type(args.name) ~= "string" then
+			error(("name must be %q, got %q (%s)"):format("string", type(args.name), tostring(args.name)), 2)
+		elseif type(args.description) ~= "string" then
+			error(("description must be %q, got %q (%s)"):format("string", type(args.description), tostring(args.description)), 2)
+		elseif type(args.values) ~= "function" then
+			if type(args.values) ~= "table" then
+				error(("values must be %q, got %q (%s)"):format("table", type(args.values), tostring(args.values)), 2)
+			elseif #args.values%2 ~= 0 then
+				error(("values must have an even number of items, got %d"):format(#args.values), 2)
+			end
+			for i = 1, #args.values, 2 do
+				local k, v = args.values[i], args.values[2]
+				if type(k) ~= "string" and type(k) ~= "number" then
+					error(("values' keys must be %q or %q, got %q (%s)"):format("string", "number", type(k), tostring(k)))
+				elseif type(v) ~= "string" then
+					error(("values' values must be %q, got %q (%s)"):format("string", type(v), tostring(v)))
+				end
+				tmp[k] = v
+			end
+		end
+		if type(args.default) ~= "number" and type(args.default) ~= "string" then
+			error(("default must be %q or %q, got %q (%s)"):format("number", "string", type(args.default), tostring(args.default)), 2)
+		elseif type(args.values) ~= "function" and not tmp[args.default] then
+			error(("default must be in values, %s is not"):format(tostring(args.default)), 2)
+		elseif not args.current == not args.getFunc then
+			error(("either current or getFunc must be supplied, but not both"), 2)
+		elseif args.current and type(args.current) ~= "string" and type(args.current) ~= "number" then
+			error(("current must be %q or %q, got %q (%s)"):format("string", "number", type(args.current), tostring(args.current)), 2)
+		elseif type(args.values) ~= "function" and args.current and not tmp[args.current] then
+			error(("current must be in values, %s is not"):format(tostring(args.current)), 2)
+		elseif args.getFunc and type(args.getFunc) ~= "function" then
+			error(("getFunc must be %q, got %q (%s)"):format("function", type(args.getFunc), tostring(args.getFunc)), 2)
+		elseif type(args.setFunc) ~= "function" then
+			error(("setFunc must be %q, got %q (%s)"):format("function", type(args.setFunc), tostring(args.setFunc)), 2)
+		elseif args.okayFunc and type(args.okayFunc) ~= "function" then
+			error(("okayFunc must be %q or %q, got %q (%s)"):format("nil", "function", type(args.okayFunc), tostring(args.okayFunc)), 2)
+		elseif args.cancelFunc and type(args.cancelFunc) ~= "function" then
+			error(("cancelFunc must be %q or %q, got %q (%s)"):format("nil", "function", type(args.cancelFunc), tostring(args.cancelFunc)), 2)
+		elseif args.defaultFunc and type(args.defaultFunc) ~= "function" then
+			error(("defaultFunc must be %q or %q, got %q (%s)"):format("nil", "function", type(args.defaultFunc), tostring(args.defaultFunc)), 2)
+		end
+		for k in pairs(tmp) do
+			tmp[k] = nil
+		end
+		local name
+		local i = 0
+		repeat
+			i = i + 1
+			name = self:GetName() .. "_DropDown" .. i
+		until not _G[name]
+
+		local dropDown = CreateFrame("Frame", name, self, "UIDropDownMenuTemplate")
+		self.controls[dropDown] = true
+		if args.name ~= "" then
+			local label = dropDown:CreateFontString(nil, "BACKGROUND", "GameFontNormal")
+			label:SetText(args.name)
+			label:SetPoint("BOTTOMLEFT", dropDown, "TOPLEFT", 16, 3)
+		end
+		dropDown.tooltipText = args.description
+		dropDown.values = args.values
+		UIDropDownMenu_Initialize(dropDown, function()
+			dropDown_menu(dropDown)
+		end)
+		if WotLK then
+			UIDropDownMenu_SetWidth(dropDown, 90)
+		else
+			UIDropDownMenu_SetWidth(90, dropDown)
+		end
+		local current
+		if args.getFunc then
+			dropDown.getFunc = args.getFunc
+			current = args.getFunc()
+		else
+			current = args.current
+		end
+		UIDropDownMenu_SetSelectedValue(dropDown, current)
+		dropDown.default = args.default
+		dropDown.value = args.current
+		dropDown.oldValue = args.current
+		dropDown.changeFunc = args.setFunc
+		dropDown.SetValue = dropDown_SetValue
+		dropDown:EnableMouse(true)
+		dropDown:SetScript("OnEnter", generic_OnEnter)
+		dropDown:SetScript("OnLeave", generic_OnLeave)
+		dropDown.okayFunc = args.okayFunc
+		dropDown.cancelFunc = args.cancelFunc
+		dropDown.defaultFunc = args.defaultFunc
+		args = doneArgs(args)
+		return dropDown
+	end
+end
+
+do
+	local function donothing() end
+	local function button_OnClick(self)
+		self.clickFunc()
+	end
+	--- Return a button
+	-- @name panel:MakeButton
+	-- @param ... tuple of key-value pairs<br/>
+	--     name: What shows above the dropdown<br/>
+	--     description: What shows when hovering over the dropdown<br/>
+	--     func: What is called when the button is pressed
+	-- @usage panel:MakeButton(
+	--     'name', 'Click',
+	--     'description', 'Specify your tooltip description',
+	--     'func', function() DEFAULT_CHAT_FRAME:AddMessage("Clicked!") end
+	-- )
+	-- @return the Button
+	function panelMeta:MakeButton(...)
+		local args = getArgs(...)
+		if type(args.name) ~= "string" then
+			error(("name must be %q, got %q (%s)"):format("string", type(args.name), tostring(args.name)), 2)
+		elseif type(args.description) ~= "string" then
+			error(("description must be %q, got %q (%s)"):format("string", type(args.description), tostring(args.description)), 2)
+		elseif type(args.func) ~= "function" then
+			error(("description must be %q, got %q (%s)"):format("function", type(args.func), tostring(args.func)), 2)
+		end
+		local name
+		local i = 0
+		repeat
+			i = i + 1
+			name = self:GetName() .. "_Button" .. i
+		until not _G[name]
+
+		local button = CreateFrame("Button", name, self, "UIPanelButtonTemplate")
+		self.controls[button] = true
+		button:SetText(args.name)
+		button.tooltipText = args.description
+		button:SetWidth(120)
+		button:SetHeight(22)
+		button.SetValue = donothing
+		button.clickFunc = args.func
+		button:SetScript("OnClick", button_OnClick)
+		button:SetScript("OnEnter", generic_OnEnter)
+		button:SetScript("OnLeave", generic_OnLeave)
+		args = doneArgs(args)
+		return button
+	end
+end
+
+do
+	local function toggle_SetValue(self, value)
+		value = not not value
+		self.changeFunc(value)
+		self.value = value
+		self:SetChecked(value)
+	end
+	local function toggle_OnClick(self)
+		self:SetValue(not not self:GetChecked())
+	end
+	--- Return a checkbox
+	-- @name panel:MakeToggle
+	-- @param ... tuple of key-value pairs<br/>
+	--     name: What appears to the right of the checkbox<br/>
+	--     description: What the tooltip shows when hovering over<br/>
+	--     default: The default value<br/>
+	--     current: The current value - you can provide this or getFunc<br/>
+	--     getFunc: Function to return the current value<br/>
+	--     setFunc: What is called when the value changes<br/>
+	--     [optional] okayFunc: Called when the okay button is pressed<br/>
+	--     [optional] cancelFunc: Called when the cancel button is pressed<br/>
+	--     [optional] defaultFunc: Called when the default button is pressed
+	-- @usage panel:MakeToggle(
+	--     'name', 'Toggle',
+	--     'description', 'Specify your tooltip description',
+	--     'default', false,
+	--     'getFunc', function() return db.myToggle end
+	--     'setFunc', function(value) db.myToggle = value end
+	-- )
+	-- @return the CheckButton
+	function panelMeta:MakeToggle(...)
+		local args = getArgs(...)
+		if type(args.name) ~= "string" then
+			error(("name must be %q, got %q (%s)"):format("string", type(args.name), tostring(args.name)), 2)
+		elseif type(args.description) ~= "string" then
+			error(("description must be %q, got %q (%s)"):format("string", type(args.description), tostring(args.description)), 2)
+		elseif type(args.default) ~= "boolean" then
+			error(("default must be %q, got %q (%s)"):format("boolean", type(args.default), tostring(args.default)), 2)
+		elseif (args.current == nil) == not args.getFunc then
+			error(("either current or getFunc must be supplied, but not both"), 2)
+		elseif args.current and type(args.current) ~= "boolean" then
+			error(("current must be %q, got %q (%s)"):format("boolean", type(args.current), tostring(args.current)), 2)
+		elseif args.getFunc and type(args.getFunc) ~= "function" then
+			error(("getFunc must be %q, got %q (%s)"):format("function", type(args.getFunc), tostring(args.getFunc)), 2)
+		elseif type(args.setFunc) ~= "function" then
+			error(("setFunc must be %q, got %q (%s)"):format("function", type(args.setFunc), tostring(args.setFunc)), 2)
+		elseif args.okayFunc and type(args.okayFunc) ~= "function" then
+			error(("okayFunc must be %q or %q, got %q (%s)"):format("nil", "function", type(args.okayFunc), tostring(args.okayFunc)), 2)
+		elseif args.cancelFunc and type(args.cancelFunc) ~= "function" then
+			error(("cancelFunc must be %q or %q, got %q (%s)"):format("nil", "function", type(args.cancelFunc), tostring(args.cancelFunc)), 2)
+		elseif args.defaultFunc and type(args.defaultFunc) ~= "function" then
+			error(("defaultFunc must be %q or %q, got %q (%s)"):format("nil", "function", type(args.defaultFunc), tostring(args.defaultFunc)), 2)
+		end
+		local name
+		local i = 0
+		repeat
+			i = i + 1
+			name = self:GetName() .. "_Toggle" .. i
+		until not _G[name]
+
+		local toggle = CreateFrame("CheckButton", name, self, "InterfaceOptionsCheckButtonTemplate")
+		self.controls[toggle] = true
+		_G[toggle:GetName() .. "Text"]:SetText(args.name)
+		toggle:SetHitRectInsets(0, -_G[toggle:GetName() .. "Text"]:GetWidth() - 1, 0, 0)
+		toggle.tooltipText = args.description
+		toggle.default = args.default
+		local current
+		if args.getFunc then
+			toggle.getFunc = args.getFunc
+			current = args.getFunc()
+		else
+			current = args.current
+		end
+		toggle.value = current
+		toggle.oldValue = current
+		toggle.changeFunc = args.setFunc
+		toggle.SetValue = toggle_SetValue
+		toggle:SetScript("OnClick", toggle_OnClick)
+		toggle:SetChecked(current)
+		toggle:SetScript("OnEnter", generic_OnEnter)
+		toggle:SetScript("OnLeave", generic_OnLeave)
+		toggle.okayFunc = args.okayFunc
+		toggle.cancelFunc = args.cancelFunc
+		toggle.defaultFunc = args.defaultFunc
+		args = doneArgs(args)
+		return toggle
+	end
+end
+
+do
+	local function update(self, r, g, b, a)
+		if not self.hasAlpha then
+			a = 1
+		end
+		self.info.r = r
+		self.info.g = g
+		self.info.b = b
+		if self.hasAlpha then
+			self.info.opacity = a
+		end
+		self.color:SetColorTexture(r, g, b, a)
+		if self.value == self.oldValue then
+			self.value = {}
+		end
+		self.value[1] = r
+		self.value[2] = g
+		self.value[3] = b
+		if self.hasAlpha then
+			self.value[4] = a
+		end
+		self.info.r = r
+		self.info.g = g
+		self.info.b = b
+		if self.hasAlpha then
+			self.info.opacity = 1 - a
+		end
+		if self.hasAlpha then
+			self.changeFunc(r, g, b, a)
+		else
+			self.changeFunc(r, g, b)
+		end
+	end
+	local function button_SetValue(self, ...)
+		if select('#', ...) == 1 and type((...)) == "table" then
+			return button_SetValue(self, unpack(value))
+		end
+		update(self, ...)
+	end
+	local function button_OnClick(self)
+		OpenColorPicker(self.info)
+	end
+	local function swatchFunc(self)
+		local r, g, b = ColorPickerFrame:GetColorRGB()
+		local opacity = 1 - OpacitySliderFrame:GetValue()
+
+		update(self, r, g, b, opacity)
+	end
+	local function cancelFunc(self)
+		local previousValues = ColorPickerFrame.previousValues
+		local r, g, b, opacity = previousValues.r, previousValues.g, previousValues.b, hasAlpha and 1 - previousValues.opacity or 1
+
+		update(self, r, g, b, opacity)
+	end
+	--- Return a color swatch that opens a color picker
+	-- @name panel:MakeColorPicker
+	-- @param ... tuple of key-value pairs<br/>
+	--     name: What shows up next to the swatch<br />
+	--     description: What shows up in the tooltip on hover<br />
+	--     hasAlpha: Whether the color picker should have an alpha setting<br />
+	--     defaultR: Default red value [0, 1]<br />
+	--     defaultG: Default green value [0, 1]<br />
+	--     defaultB: Default blue value [0, 1]<br />
+	--     defaultA: Default alpha value [0, 1], only needed if hasAlpha is true<br />
+	--     currentR: The current red value - you can provide this or getFunc<br />
+	--     currentG: The current green value - you can provide this or getFunc<br />
+	--     currentB: The current blue value - you can provide this or getFunc<br />
+	--     currentA: The current alpha value - you can provide this or getFunc<br />
+	--     getFunc: Function to return the current color as a tuple<br />
+	--     setFunc: What is called when the color changes<br />
+	--     [optional] okayFunc: Called when the okay button is pressed<br />
+	--     [optional] cancelFunc: Called when the cancel button is pressed<br />
+	--     [optional] defaultFunc: Called when the default button is pressed
+	-- @usage panel:MakeColorPicker(
+	--     'name', 'Pick a color',
+	--     'description', 'Specify your tooltip description',
+	--     'hasAlpha', false,
+	--     'defaultR', 1,
+	--     'defaultG', 0.82,
+	--     'defaultB', 0,
+	--     'getFunc', function() return unpack(db.color) end
+	--     'setFunc', function(r, g, b) db.color[1], db.color[2], db.color[3] = r, g, b end
+	-- )
+	-- @usage panel:MakeColorPicker(
+	--     'name', 'Pick a color',
+	--     'description', 'Specify your tooltip description',
+	--     'hasAlpha', true,
+	--     'defaultR', 0,
+	--     'defaultG', 1,
+	--     'defaultB', 0,
+	--     'defaultA', 0.5,
+	--     'currentR', db.color2.r,
+	--     'currentG', db.color2.g,
+	--     'currentB', db.color2.b,
+	--     'currentA', db.color2.a,
+	--     'setFunc', function(r, g, b, a) db.color2.r, db.color2.g db.color2.b, db.color2.a = r, g, b, a end
+	-- )
+	-- @return the color swatch
+	function panelMeta:MakeColorPicker(...)
+		local args = getArgs(...)
+		if type(args.name) ~= "string" then
+			error(("name must be %q, got %q (%s)"):format("string", type(args.name), tostring(args.name)), 2)
+		elseif type(args.description) ~= "string" then
+			error(("description must be %q, got %q (%s)"):format("string", type(args.description), tostring(args.description)), 2)
+		elseif type(args.hasAlpha) ~= "boolean" then
+			error(("hasAlpha must be %q, got %q (%s)"):format("boolean", type(args.hasAlpha), tostring(args.hasAlpha)), 2)
+		elseif type(args.defaultR) ~= "number" then
+			error(("defaultR must be %q, got %q (%s)"):format("number", type(args.defaultR), tostring(args.defaultR)), 2)
+		elseif args.defaultR < 0 or args.defaultR > 1 then
+			error(("defaultR must be [0, 1], got %s"):format(tostring(args.defaultR)), 2)
+		elseif type(args.defaultG) ~= "number" then
+			error(("defaultG must be %q, got %q (%s)"):format("number", type(args.defaultG), tostring(args.defaultG)), 2)
+		elseif args.defaultG < 0 or args.defaultG > 1 then
+			error(("defaultG must be [0, 1], got %s"):format(tostring(args.defaultG)), 2)
+		elseif type(args.defaultB) ~= "number" then
+			error(("defaultB must be %q, got %q (%s)"):format("number", type(args.defaultB), tostring(args.defaultB)), 2)
+		elseif args.defaultB < 0 or args.defaultB > 1 then
+			error(("defaultB must be [0, 1], got %s"):format(tostring(args.defaultB)), 2)
+		elseif args.hasAlpha and type(args.defaultA) ~= "number" then
+			error(("defaultA must be %q, got %q (%s)"):format("number", type(args.defaultA), tostring(args.defaultA)), 2)
+		elseif args.hasAlpha and (args.defaultA < 0 or args.defaultA > 1) then
+			error(("defaultA must be [0, 1], got %s"):format(tostring(args.defaultA)), 2)
+		elseif not args.currentR == not args.getFunc then
+			error(("either currentR or getFunc must be supplied, but not both"), 2)
+		elseif args.currentR and (not args.currentG or not args.currentB or (args.hasAlpha and not args.currentA)) then
+			error(("if you supply currentR, you must supply currentG and currentB (and currentA if hasAlpha)"), 2)
+		elseif args.currentR and type(args.currentR) ~= "number" then
+			error(("current must be %q, got %q (%s)"):format("number", type(args.currentR), tostring(args.currentR)), 2)
+		elseif args.currentG and type(args.currentG) ~= "number" then
+			error(("current must be %q, got %q (%s)"):format("number", type(args.currentG), tostring(args.currentG)), 2)
+		elseif args.currentB and type(args.currentB) ~= "number" then
+			error(("current must be %q, got %q (%s)"):format("number", type(args.currentB), tostring(args.currentB)), 2)
+		elseif args.currentA and type(args.currentA) ~= "number" then
+			error(("current must be %q, got %q (%s)"):format("number", type(args.currentA), tostring(args.currentA)), 2)
+		elseif args.getFunc and type(args.getFunc) ~= "function" then
+			error(("getFunc must be %q, got %q (%s)"):format("function", type(args.getFunc), tostring(args.getFunc)), 2)
+		elseif type(args.setFunc) ~= "function" then
+			error(("setFunc must be %q, got %q (%s)"):format("function", type(args.setFunc), tostring(args.setFunc)), 2)
+		elseif args.okayFunc and type(args.okayFunc) ~= "function" then
+			error(("okayFunc must be %q or %q, got %q (%s)"):format("nil", "function", type(args.okayFunc), tostring(args.okayFunc)), 2)
+		elseif args.cancelFunc and type(args.cancelFunc) ~= "function" then
+			error(("cancelFunc must be %q or %q, got %q (%s)"):format("nil", "function", type(args.cancelFunc), tostring(args.cancelFunc)), 2)
+		elseif args.defaultFunc and type(args.defaultFunc) ~= "function" then
+			error(("defaultFunc must be %q or %q, got %q (%s)"):format("nil", "function", type(args.defaultFunc), tostring(args.defaultFunc)), 2)
+		end
+
+		local name
+		local i = 0
+		repeat
+			i = i + 1
+			name = self:GetName() .. "_ColorPicker" .. i
+		until not _G[name]
+
+		if not args.hasAlpha then
+			args.defaultA = 1
+		end
+
+		local button = CreateFrame("Button", name, self)
+		self.controls[button] = true
+
+
+		local currentR, currentG, currentB, currentA
+		if args.getFunc then
+			button.getFunc = args.getFunc
+			currentR, currentG, currentB, currentA = button.getFunc()
+			if not args.hasAlpha then
+				currentA = 1
+			end
+		else
+			currentR = args.currentR
+			currentG = args.currentG
+			currentB = args.currentB
+			if not args.hasAlpha then
+				currentA = 1
+			else
+				currentR = args.currentA
+			end
+		end
+
+		button.tooltipText = args.description
+		local text = button:CreateFontString(nil, "ARTWORK", "GameFontHighlight")
+		text:SetText(args.name)
+		text:SetPoint("LEFT", button, "RIGHT", 0, 1)
+		button:SetHitRectInsets(0, -text:GetWidth() - 1, 0, 0)
+		local color = button:CreateTexture(nil, "ARTWORK")
+		button.color = color
+		color:SetColorTexture(currentR, currentG, currentB, currentA)
+		local background = button:CreateTexture(nil, "BORDER")
+		background:SetTexture([=[Tileset\Generic\Checkers]=])
+		background:SetTexCoord(0, 0.5, 0, 0.5)
+		local border = button:CreateTexture(nil, "BACKGROUND")
+		border:SetTexture([=[Interface\ChatFrame\ChatFrameColorSwatch]=])
+		button:SetWidth(26)
+		button:SetHeight(26)
+		background:SetPoint("CENTER")
+		background:SetWidth(14)
+		background:SetHeight(14)
+		color:SetPoint("CENTER")
+		color:SetWidth(14)
+		color:SetHeight(14)
+		border:SetAllPoints(button)
+		button.default = { args.defaultR, args.defaultG, args.defaultB }
+		button.oldValue = { currentR, currentG, currentB }
+		if hasAlpha then
+			button.default[4] = args.defaultA
+			button.oldValue[4] = currentA
+		end
+		button.value = button.oldValue
+		button.hasAlpha = args.hasAlpha
+		button.changeFunc = args.setFunc
+		button.SetValue = button_SetValue
+		local function swatchFunc_wrapper()
+			swatchFunc(button)
+		end
+		local function cancelFunc_wrapper()
+			cancelFunc(button)
+		end
+		button.info = {
+			swatchFunc = swatchFunc_wrapper,
+			hasOpacity = args.hasAlpha,
+			opacityFunc = args.hasAlpha and swatchFunc_wrapper or nil,
+			r = currentR,
+			g = currentB,
+			b = currentG,
+			opacity = args.hasAlpha and 1 - currentA or nil,
+			cancelFunc = cancelFunc_wrapper,
+		}
+		button:SetScript("OnClick", button_OnClick)
+		button:SetScript("OnEnter", generic_OnEnter)
+		button:SetScript("OnLeave", generic_OnLeave)
+		button:RegisterForClicks("LeftButtonUp")
+		button.okayFunc = args.okayFunc
+		button.cancelFunc = args.cancelFunc
+		button.defaultFunc = args.defaultFunc
+		args = doneArgs(args)
+		return button
+	end
+end
+
+--- Add a slash command to open a specific options panel
+-- @param name name of the panel to open
+-- @param ... tuple of slash commands
+-- @usage LibStub("LibSimpleOptions-1.0").AddSlashCommand("My Options", "/MyOpt", "/MO")
+function LibSimpleOptions.AddSlashCommand(name, ...)
+	local num = 0
+	local name_upper = name:upper()
+	for i = 1, select('#', ...) do
+		local cmd = select(i, ...)
+		num = num + 1
+		_G["SLASH_" .. name_upper .. num] = cmd
+		local cmd_lower = cmd:lower()
+		if cmd_lower ~= cmd then
+			num = num + 1
+			_G["SLASH_" .. name_upper .. num] = cmd_lower
+		end
+	end
+
+	_G.hash_SlashCmdList[name_upper] = nil
+	_G.SlashCmdList[name_upper] = function()
+		InterfaceOptionsFrame_OpenToCategory(name)
+	end
+end
+
+for funcName, func in pairs(panelMeta) do
+	LibSimpleOptions[funcName] = func
+	for panel in pairs(panels) do
+		panel[funcName] = func
+	end
+end
diff --git a/libs/LibSimpleOptions-1.0/LibSimpleOptions-1.0.toc b/libs/LibSimpleOptions-1.0/LibSimpleOptions-1.0.toc
new file mode 100644
index 0000000..de94cc4
--- /dev/null
+++ b/libs/LibSimpleOptions-1.0/LibSimpleOptions-1.0.toc
@@ -0,0 +1,15 @@
+## Interface: 50001
+## LoadOnDemand: 1
+## Title: Lib: SimpleOptions-1.0
+## Notes: A library to provide a way to easily create controls for Blizzard's options system
+## Author: ckknight
+## X-eMail: ckknight@gmail.com
+## X-Category: Library
+## X-License: MIT
+## X-Curse-Packaged-Version: r46
+## X-Curse-Project-Name: LibSimpleOptions-1.0
+## X-Curse-Project-ID: libsimpleoptions-1-0
+## X-Curse-Repository-ID: wow/libsimpleoptions-1-0/mainline
+
+LibStub\LibStub.lua
+lib.xml
diff --git a/libs/LibSimpleOptions-1.0/LibStub/LibStub.lua b/libs/LibSimpleOptions-1.0/LibStub/LibStub.lua
new file mode 100644
index 0000000..0a41ac0
--- /dev/null
+++ b/libs/LibSimpleOptions-1.0/LibStub/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
diff --git a/libs/LibSimpleOptions-1.0/lib.xml b/libs/LibSimpleOptions-1.0/lib.xml
new file mode 100644
index 0000000..d3450fb
--- /dev/null
+++ b/libs/LibSimpleOptions-1.0/lib.xml
@@ -0,0 +1,4 @@
+<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/
+..\FrameXML\UI.xsd">
+	<Script file="LibSimpleOptions-1.0.lua" />
+</Ui>
\ No newline at end of file
diff --git a/libs/LibStub.lua b/libs/LibStub.lua
deleted file mode 100644
index 0a41ac0..0000000
--- a/libs/LibStub.lua
+++ /dev/null
@@ -1,30 +0,0 @@
--- 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
diff --git a/libs/LibStub/LibStub.lua b/libs/LibStub/LibStub.lua
new file mode 100644
index 0000000..7e9b5cd
--- /dev/null
+++ b/libs/LibStub/LibStub.lua
@@ -0,0 +1,51 @@
+-- $Id: LibStub.lua 103 2014-10-16 03:02:50Z mikk $
+-- LibStub is a simple versioning stub meant for use in Libraries.  http://www.wowace.com/addons/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]
+
+-- Check to see is this version of the stub is obsolete
+if not LibStub or LibStub.minor < LIBSTUB_MINOR then
+	LibStub = LibStub or {libs = {}, minors = {} }
+	_G[LIBSTUB_MAJOR] = LibStub
+	LibStub.minor = LIBSTUB_MINOR
+
+	-- LibStub:NewLibrary(major, minor)
+	-- major (string) - the major version of the library
+	-- minor (string or number ) - the minor version of the library
+	--
+	-- returns nil if a newer or same version of the lib is already present
+	-- returns empty library object or old library object if upgrade is needed
+	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
+
+	-- LibStub:GetLibrary(major, [silent])
+	-- major (string) - the major version of the library
+	-- silent (boolean) - if true, library is optional, silently return nil if its not found
+	--
+	-- throws an error if the library can not be found (except silent is set)
+	-- returns the library object if found
+	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
+
+	-- LibStub:IterateLibraries()
+	--
+	-- Returns an iterator for the currently registered libraries
+	function LibStub:IterateLibraries()
+		return pairs(self.libs)
+	end
+
+	setmetatable(LibStub, { __call = LibStub.GetLibrary })
+end
diff --git a/libs/LibStub/LibStub.toc b/libs/LibStub/LibStub.toc
new file mode 100644
index 0000000..c6d9574
--- /dev/null
+++ b/libs/LibStub/LibStub.toc
@@ -0,0 +1,13 @@
+## Interface: 70000
+## Title: Lib: LibStub
+## Notes: Universal Library Stub
+## Credits: Kaelten, Cladhaire, ckknight, Mikk, Ammo, Nevcairiel
+## X-Website: http://www.wowace.com/addons/libstub/
+## X-Category: Library
+## X-License: Public Domain
+## X-Curse-Packaged-Version: 1.0.2.60000
+## X-Curse-Project-Name: LibStub
+## X-Curse-Project-ID: libstub
+## X-Curse-Repository-ID: wow/libstub/mainline
+
+LibStub.lua
diff --git a/libs/LibStub/tests/test.lua b/libs/LibStub/tests/test.lua
new file mode 100644
index 0000000..276ddab
--- /dev/null
+++ b/libs/LibStub/tests/test.lua
@@ -0,0 +1,41 @@
+debugstack = debug.traceback
+strmatch = string.match
+
+loadfile("../LibStub.lua")()
+
+local lib, oldMinor = LibStub:NewLibrary("Pants", 1) -- make a new thingy
+assert(lib) -- should return the library table
+assert(not oldMinor) -- should not return the old minor, since it didn't exist
+
+-- the following is to create data and then be able to check if the same data exists after the fact
+function lib:MyMethod()
+end
+local MyMethod = lib.MyMethod
+lib.MyTable = {}
+local MyTable = lib.MyTable
+
+local newLib, newOldMinor = LibStub:NewLibrary("Pants", 1) -- try to register a library with the same version, should silently fail
+assert(not newLib) -- should not return since out of date
+
+local newLib, newOldMinor = LibStub:NewLibrary("Pants", 0) -- try to register a library with a previous, should silently fail
+assert(not newLib) -- should not return since out of date
+
+local newLib, newOldMinor = LibStub:NewLibrary("Pants", 2) -- register a new version
+assert(newLib) -- library table
+assert(rawequal(newLib, lib)) -- should be the same reference as the previous
+assert(newOldMinor == 1) -- should return the minor version of the previous version
+
+assert(rawequal(lib.MyMethod, MyMethod)) -- verify that values were saved
+assert(rawequal(lib.MyTable, MyTable)) -- verify that values were saved
+
+local newLib, newOldMinor = LibStub:NewLibrary("Pants", "Blah 3 Blah") -- register a new version with a string minor version (instead of a number)
+assert(newLib) -- library table
+assert(newOldMinor == 2) -- previous version was 2
+
+local newLib, newOldMinor = LibStub:NewLibrary("Pants", "Blah 4 and please ignore 15 Blah") -- register a new version with a string minor version (instead of a number)
+assert(newLib)
+assert(newOldMinor == 3) -- previous version was 3 (even though it gave a string)
+
+local newLib, newOldMinor = LibStub:NewLibrary("Pants", 5) -- register a new library, using a normal number instead of a string
+assert(newLib)
+assert(newOldMinor == 4) -- previous version was 4 (even though it gave a string)
\ No newline at end of file
diff --git a/libs/LibStub/tests/test2.lua b/libs/LibStub/tests/test2.lua
new file mode 100644
index 0000000..eae7172
--- /dev/null
+++ b/libs/LibStub/tests/test2.lua
@@ -0,0 +1,27 @@
+debugstack = debug.traceback
+strmatch = string.match
+
+loadfile("../LibStub.lua")()
+
+for major, library in LibStub:IterateLibraries() do
+	-- check that MyLib doesn't exist yet, by iterating through all the libraries
+	assert(major ~= "MyLib")
+end
+
+assert(not LibStub:GetLibrary("MyLib", true)) -- check that MyLib doesn't exist yet by direct checking
+assert(not pcall(LibStub.GetLibrary, LibStub, "MyLib")) -- don't silently fail, thus it should raise an error.
+local lib = LibStub:NewLibrary("MyLib", 1) -- create the lib
+assert(lib) -- check it exists
+assert(rawequal(LibStub:GetLibrary("MyLib"), lib)) -- verify that :GetLibrary("MyLib") properly equals the lib reference
+
+assert(LibStub:NewLibrary("MyLib", 2))	-- create a new version
+
+local count=0
+for major, library in LibStub:IterateLibraries() do
+	-- check that MyLib exists somewhere in the libraries, by iterating through all the libraries
+	if major == "MyLib" then -- we found it!
+		count = count +1
+		assert(rawequal(library, lib)) -- verify that the references are equal
+	end
+end
+assert(count == 1) -- verify that we actually found it, and only once
diff --git a/libs/LibStub/tests/test3.lua b/libs/LibStub/tests/test3.lua
new file mode 100644
index 0000000..30f7b94
--- /dev/null
+++ b/libs/LibStub/tests/test3.lua
@@ -0,0 +1,14 @@
+debugstack = debug.traceback
+strmatch = string.match
+
+loadfile("../LibStub.lua")()
+
+local proxy = newproxy() -- non-string
+
+assert(not pcall(LibStub.NewLibrary, LibStub, proxy, 1)) -- should error, proxy is not a string, it's userdata
+local success, ret = pcall(LibStub.GetLibrary, proxy, true)
+assert(not success or not ret) -- either error because proxy is not a string or because it's not actually registered.
+
+assert(not pcall(LibStub.NewLibrary, LibStub, "Something", "No number in here")) -- should error, minor has no string in it.
+
+assert(not LibStub:GetLibrary("Something", true)) -- shouldn't've created it from the above statement
\ No newline at end of file
diff --git a/libs/LibStub/tests/test4.lua b/libs/LibStub/tests/test4.lua
new file mode 100644
index 0000000..43eb338
--- /dev/null
+++ b/libs/LibStub/tests/test4.lua
@@ -0,0 +1,41 @@
+debugstack = debug.traceback
+strmatch = string.match
+
+loadfile("../LibStub.lua")()
+
+
+-- Pretend like loaded libstub is old and doesn't have :IterateLibraries
+assert(LibStub.minor)
+LibStub.minor = LibStub.minor - 0.0001
+LibStub.IterateLibraries = nil
+
+loadfile("../LibStub.lua")()
+
+assert(type(LibStub.IterateLibraries)=="function")
+
+
+-- Now pretend that we're the same version -- :IterateLibraries should NOT be re-created
+LibStub.IterateLibraries = 123
+
+loadfile("../LibStub.lua")()
+
+assert(LibStub.IterateLibraries == 123)
+
+
+-- Now pretend that a newer version is loaded -- :IterateLibraries should NOT be re-created
+LibStub.minor = LibStub.minor + 0.0001
+
+loadfile("../LibStub.lua")()
+
+assert(LibStub.IterateLibraries == 123)
+
+
+-- Again with a huge number
+LibStub.minor = LibStub.minor + 1234567890
+
+loadfile("../LibStub.lua")()
+
+assert(LibStub.IterateLibraries == 123)
+
+
+print("OK")
\ No newline at end of file
diff --git a/localization/localization.lua b/localization/localization.lua
index cb478ab..138acdd 100644
--- a/localization/localization.lua
+++ b/localization/localization.lua
@@ -74,6 +74,12 @@
 -- ["Enable empty line seperator above BagSync tooltip display."] = "",
 -- ["Enable items for Cross-Realms characters."] = "",
 -- ["Enable items for current Battle.Net Account characters |cFFDF2B2B(Not Recommended)|r."] = "",
+-- ["Primary BagSync tooltip color."] = "",
+-- ["Secondary BagSync tooltip color."] = "",
+-- ["BagSync [Total] tooltip color."] = "",
+-- ["BagSync [Guild] tooltip color."] = "",
+-- ["BagSync [Cross-Realms] tooltip color."] = "",
+-- ["BagSync [Battle.Net] tooltip color."] = "",

 ---------------------
 --Major shout out and special thanks to ytzyt at Curse for the zhCN and zhTW translations!  Thanks!