changeset 84:3bec0ea44607

Cleaned the Inventorium folder; moved all classes to classes directory, modules to modules directory and support addons to plugins directory. In addition support addons are now references within XML files rather than inside the TOC. Fixed the default local item count setting, you can now exclude bag and AH data from it. Fixed some mover algorithm bugs. Mover can no longer freeze the game but instead will terminate the process after a 1000 passes. Now reversing the moves table after making it, rather than every single time it is used. Fixed guild bank support. Now displaying the amount of items moved. Scanner now scans all guild bank tabs rather than only the current. Fixed a bug with local item data not being retrieved properly. Disabled ?enterClicksFirstButton? within dialogs as this causes the dialog to consume all keypress. Events are now at the addon object rather than local.
author Zerotorescue
date Thu, 06 Jan 2011 20:05:30 +0100
parents 6b60f7a1410c
children e01e6642df57
files AuctionAddons/AuctionLite.lua AuctionAddons/AuctionMaster.lua AuctionAddons/AuctionProfitMaster.lua AuctionAddons/Auctionator.lua AuctionAddons/Auctioneer.lua AuctionAddons/Others.lua AuctionAddons/ZeroAuctions.lua Classes/ContainerItem.class.lua Classes/ItemData.class.lua Config.lua ContainerItem.class.lua Core.lua CraftingAddons/AdvancedTradeSkillWindow.lua CraftingAddons/Cauldron.lua CraftingAddons/GnomeWorks.lua CraftingAddons/Skillet.lua Inventorium.toc ItemCountAddons/Altoholic.lua ItemCountAddons/DataStore (current account only).lua ItemCountAddons/DataStore (with guilds).lua ItemCountAddons/DataStore (without guilds).lua ItemCountAddons/ItemCount.lua ItemData.class.lua Libraries.xml Modules/Config.lua Modules/Mover.lua Modules/Queue.lua Modules/Scanner.lua Modules/Summary.lua Modules/Widgets.lua Mover.lua Plugins/AuctionAddons/AuctionAddons.xml Plugins/AuctionAddons/AuctionLite.lua Plugins/AuctionAddons/AuctionMaster.lua Plugins/AuctionAddons/AuctionProfitMaster.lua Plugins/AuctionAddons/Auctionator.lua Plugins/AuctionAddons/Auctioneer.lua Plugins/AuctionAddons/Others.lua Plugins/AuctionAddons/ZeroAuctions.lua Plugins/CraftingAddons/AdvancedTradeSkillWindow.lua Plugins/CraftingAddons/Cauldron.lua Plugins/CraftingAddons/CraftingAddons.xml Plugins/CraftingAddons/GnomeWorks.lua Plugins/CraftingAddons/Skillet.lua Plugins/ItemCountAddons/Altoholic.lua Plugins/ItemCountAddons/DataStore (current account only).lua Plugins/ItemCountAddons/DataStore (with guilds).lua Plugins/ItemCountAddons/DataStore (without guilds).lua Plugins/ItemCountAddons/ItemCount.lua Plugins/ItemCountAddons/ItemCountAddons.xml Plugins/Plugins.xml Queue.lua Scanner.lua Summary.lua Widgets.lua embeds.xml
diffstat 56 files changed, 4354 insertions(+), 4293 deletions(-) [+]
line wrap: on
line diff
--- a/AuctionAddons/AuctionLite.lua	Thu Jan 06 11:04:55 2011 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,21 +0,0 @@
-do
-	
-	local function GetValue(link)
-		local history = AuctionLite:GetHistoricalPriceById(itemID);
-		
-		return (history and hist.items > 0 and math.floor(hist.price)) or -1;
-	end
-	
-	local function IsEnabled()
-		return (AuctionLite and AuctionLite.GetHistoricalPriceById);
-	end
-	
-	local function OnSelect()
-		local addonName = "|r|cfffed000AuctionLite|r|cffff6600";
-		
-		print("|cffff6600" .. addonName .. " appears to not be working properly at this time. Please try another addon. This will be corrected in the future, sorry for the inconvenience.|r");
-	end
-	
-	IMRegisterPricingAddon("AuctionLite", GetValue, IsEnabled, OnSelect);
-	
-end
--- a/AuctionAddons/AuctionMaster.lua	Thu Jan 06 11:04:55 2011 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,16 +0,0 @@
-do
-	
-	local function GetValue(link)
-		local _, _, _, lowerBuyout, _, _, numAuctions, _ = AucMasGetCurrentAuctionInfo(link, false, true);
-		--local avgBid, avgBuyout, lowerBid, lowerBuyout, upperBid, upperBuyout, numAuctions, numBuyouts
-		
-		return (numAuctions > 0 and lowerBuyout) or -1;
-	end
-	
-	local function IsEnabled()
-		return (AucMasGetCurrentAuctionInfo);
-	end
-	
-	IMRegisterPricingAddon("AuctionMaster", GetValue, IsEnabled);
-	
-end
--- a/AuctionAddons/AuctionProfitMaster.lua	Thu Jan 06 11:04:55 2011 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,28 +0,0 @@
-do
-
-	local APM;
-	
-	local function GetValue(link)
-		return (APM and APM:GetModule("Scan"):GetLowestAuction(APM:GetSafeLink(link))) or -1;
-	end
-	
-	local function IsEnabled()
-		local isEnabled = (select(6, GetAddOnInfo("AuctionProfitMaster")) == nil);
-		
-		if isEnabled then
-			APM = LibStub("AceAddon-3.0"):GetAddon("AuctionProfitMaster");
-		end
-		
-		return isEnabled and APM;
-	end
-	
-	local function OnSelect()
-		local addonName = "|r|cfffed000AuctionProfitMaster|r|cffff6600";
-		
-		print("|cffff6600Using " .. addonName .. " as pricing addon is not recommended. Items at the auction house above the \"ignore stacks over\" will always be ignored (and thus not affect the displayed price) and if you are the only one posting, " .. addonName .. " will act as if there are no auctions up.|r");
-	end
-	
-	IMRegisterPricingAddon("AuctionProfitMaster", GetValue, IsEnabled, OnSelect);
-	
-end
-
--- a/AuctionAddons/Auctionator.lua	Thu Jan 06 11:04:55 2011 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,13 +0,0 @@
-do
-	
-	local function GetValue(link)
-		return Atr_GetAuctionBuyout(link) or -1;
-	end
-	
-	local function IsEnabled()
-		return (Atr_GetAuctionBuyout);
-	end
-	
-	IMRegisterPricingAddon("Auctionator", GetValue, IsEnabled);
-	
-end
--- a/AuctionAddons/Auctioneer.lua	Thu Jan 06 11:04:55 2011 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,21 +0,0 @@
-do
-	
-	local function GetValue(link)
-		local imgSeen, _, _, _, _, lowBuy, _, _ = AucAdvanced.Modules.Util.SimpleAuction.Private.GetItems(link);
-		--local imgseen, image, matchBid, matchBuy, lowBid, lowBuy, aveBuy, aSeen 
-		
-		if imgSeen <= 0 then
-			-- No auctions up at this time
-			return -1;
-		end
-		
-		return lowBuy or -5;
-	end
-	
-	local function IsEnabled()
-		return (AucAdvanced ~= nil and AucAdvanced.Modules.Util.SimpleAuction ~= nil and AucAdvanced.Modules.Util.SimpleAuction.Private.GetItems ~= nil);
-	end
-	
-	IMRegisterPricingAddon("Auctioneer", GetValue, IsEnabled);
-	
-end
--- a/AuctionAddons/Others.lua	Thu Jan 06 11:04:55 2011 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,13 +0,0 @@
-do
-	
-	local function GetValue(link)
-		return GetAuctionBuyout(link) or -1;
-	end
-	
-	local function IsEnabled()
-		return (GetAuctionBuyout);
-	end
-	
-	IMRegisterPricingAddon("Others", GetValue, IsEnabled);
-	
-end
--- a/AuctionAddons/ZeroAuctions.lua	Thu Jan 06 11:04:55 2011 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,28 +0,0 @@
-do
-
-	local ZA;
-	
-	local function GetValue(link)
-		return (ZA and ZA:GetModule("Scan"):GetLowestAuction(ZA:GetSafeLink(link))) or -1;
-	end
-	
-	local function IsEnabled()
-		local isEnabled = (select(6, GetAddOnInfo("ZeroAuctions")) == nil);
-		
-		if isEnabled then
-			ZA = LibStub("AceAddon-3.0"):GetAddon("ZeroAuctions");
-		end
-		
-		return isEnabled and ZA;
-	end
-	
-	local function OnSelect()
-		local addonName = "|r|cfffed000ZeroAuctions|r|cffff6600";
-		
-		print("|cffff6600Using " .. addonName .. " as pricing addon is not recommended. Items at the auction house above the \"ignore stacks over\" will always be ignored (and thus not affect the displayed price) and if you are the only one posting, " .. addonName .. " will act as if there are no auctions up.|r");
-	end
-	
-	IMRegisterPricingAddon("ZeroAuctions", GetValue, IsEnabled, OnSelect);
-	
-end
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Classes/ContainerItem.class.lua	Thu Jan 06 20:05:30 2011 +0100
@@ -0,0 +1,32 @@
+local addon = select(2, ...);
+
+-- Define the class
+
+addon.ContainerItem = {};
+addon.ContainerItem.__index = addon.ContainerItem;
+
+-- Construct
+function addon.ContainerItem:New(id)
+	local self = {};
+	
+	setmetatable(self, addon.ContainerItem);
+	
+	-- Standard info everything needs
+	self.id = id;
+	self.totalCount = 0;
+	self.locations = {};
+	
+	return self;
+end
+
+function addon.ContainerItem:AddLocation(container, slot, count)
+	table.insert(self.locations, {
+		container = container,
+		slot = slot,
+		count = count,
+	});
+	
+	self.totalCount = (self.totalCount + count);
+	
+	return true;
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Classes/ItemData.class.lua	Thu Jan 06 20:05:30 2011 +0100
@@ -0,0 +1,68 @@
+local addon = select(2, ...);
+
+-- Define the class
+
+addon.ItemData = {};
+addon.ItemData.__index = addon.ItemData;
+
+-- Construct
+function addon.ItemData:New(itemId)
+	local self = {};
+	
+	setmetatable(self, addon.ItemData);
+	
+	local itemName, itemLink, itemRarity, _, _, _, _, _, _, itemTexture = GetItemInfo(itemId);
+	
+	-- Standard info everything needs
+	self.id = itemId;
+	self.name = itemName;
+	self.link = itemLink;
+	self.rarity = itemRarity;
+	self.icon = itemTexture;
+	
+	-- Detailed stuff
+	self.value = -3;
+	self.globalCount = -3;
+	self.localCount = -3;
+	self.set = {};
+	
+	return self;
+end
+
+function addon.ItemData:AddToGroup(groupName)
+	if self:InGroup() then
+		return false;
+	end
+	
+	if not addon.db.profile.groups[groupName].items then
+		addon.db.profile.groups[groupName].items = {};
+	end
+	
+	-- Set this item
+	addon.db.profile.groups[groupName].items[self.id] = true;
+	
+	return true;
+end
+
+-- To remove an item without groupname just do RemoveFromGroup(InGroup()), although providing the group name is a nice sanity check
+function addon.ItemData:RemoveFromGroup(groupName)
+	if self:InGroup() ~= groupName then
+		return false;
+	end
+	
+	-- Unset this item
+	addon.db.profile.groups[groupName].items[self.id] = nil;
+	
+	return true;
+end
+
+function addon.ItemData:InGroup()
+	-- Go through all groups to see if this item is already somewhere
+	for groupName, values in pairs(addon.db.profile.groups) do
+		if values.items and values.items[self.id] then
+			return groupName;
+		end
+	end
+	
+	return;
+end
--- a/Config.lua	Thu Jan 06 11:04:55 2011 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,2135 +0,0 @@
-local addon = select(2, ...);
-local mod = addon:NewModule("Config");
-
-local options, groupIdToName, groupIsVirtual, temp, count, includeTradeSkillItems, currentGroupType = {}, {}, {}, {}, 0, 500, "Normal";
-local AceConfigDialog, AceConfigRegistry, AceSerializer;
-
-local unknownItemName = "Unknown (#%d)";
-
--- Private functions and tables
-
-local function SetOption(info, value, multiSelectEnabled)
-	local groupName = groupIdToName[info[2]];
-	local optionName = info[#info];
-	
-	-- Special treatment for override toggle boxes
-	if optionName:find("override") then
-		if not value and info.arg then
-			-- If this override was disabled and a saved variable name was provided, set it to nil rather than false
-			
-			value = nil;
-			
-			-- If this is an override toggler then also set the related field to nil
-			addon.db.profile.groups[groupName][info.arg] = nil;
-		elseif value and info.arg then
-			-- If this override is now enabled, we need to copy the default into this field (unless it is not nil (which is supposed to be impossible), in which case we'll use the already selected value)
-			
-			local inheritedValue = addon:GetOptionByKey(groupName, info.arg);
-			
-			addon.db.profile.groups[groupName][info.arg] = (type(inheritedValue) == "table" and CopyTable(inheritedValue)) or inheritedValue; -- copying defaults by reference would let one (unintendedly) change the defaults
-		end
-	end
-	
-	if multiSelectEnabled ~= nil then
-		-- The saved vars for a multiselect will always be an array, it may not yet exist in which case it must be created.
-		if not addon.db.profile.groups[groupName][optionName] then
-			addon.db.profile.groups[groupName][optionName] = {};
-		end
-		
-		addon.db.profile.groups[groupName][optionName][value] = multiSelectEnabled or nil;
-	else
-		addon.db.profile.groups[groupName][optionName] = value;
-	end
-end
-
-local function GetOption(info)
-	local groupName = groupIdToName[info[2]];
-	local optionName = info[#info];
-	
-	local noDefault;
-	
-	if optionName:find("override") then
-		noDefault = true;
-	end
-	
-	return addon:GetOptionByKey(groupName, optionName, noDefault);
-end
-
-local function GetMultiOption(info, value)
-	local groupName = groupIdToName[info[2]];
-	local optionName = info[#info];
-	
-	local multiSelectValue = addon:GetOptionByKey(groupName, optionName);
-	
-	return multiSelectValue and multiSelectValue[value];
-end
-
-local function GetDisabled(info)
-	local groupName = groupIdToName[info[2]];
-	local optionName = info[#info];
-	
-	if optionName:find("override") or not info.arg then
-		return false;
-	end
-	
-	return (addon:GetOptionByKey(groupName, info.arg, true) == nil);
-end
-
-local function ValidateGroupName(_, value)
-	value = string.lower(string.trim(value or ""));
-	
-	for name, _ in pairs(addon.db.profile.groups) do
-		if string.lower(name) == value then
-			return ("A group named \"%s\" already exists."):format(name);
-		end
-	end
-	
-	return true;
-end
-
-local tblAddItemTemplate = {
-	order = 0,
-	type = "input",
-	name = function(info)
-		local itemName, _, itemRarity = GetItemInfo(info[#info]);
-		return tostring( 7 - (itemRarity or 0) ) .. (itemName or "");
-	end,
-	get = function(info)
-		return tostring(info[#info]); -- Ace is going to be anal about this if it's a numeric value, so we transmute it into a string here then back to a number on the other side
-	end,
-	set = function(groupId, itemData)
-    	-- This is NOT a real "set", we pass the widget reference to this function which contains similar, but not the same, info.
-    	
-    	if itemData then
-			local groupName = groupIdToName[groupId];
-			
-			if not itemData:AddToGroup(groupName) then
-				print("|cffff0000Couldn't add the item with itemId (" .. itemData.id .. ") because it is already in a group.|r");
-			end
-			
-			if AceConfigRegistry then
-				-- Now rebuild the list
-				AceConfigRegistry:NotifyChange("InventoriumOptions");
-			end
-    	end
-	end,
-	width = "double",
-	dialogControl = "ConfigItemLinkButton",
-};
-
-local tblRemoveItemTemplate = {
-	order = 0,
-	type = "input",
-	name = function(info)
-		local itemName, _, itemRarity = GetItemInfo(info[#info]);
-		return tostring( 7 - (itemRarity or 0) ) .. (itemName or "");
-	end,
-	get = function(info)
-		return tostring(info[#info]); -- Ace is going to be anal about this if it's a numeric value, so we transmute it into a string here then back to a number on the other side
-	end,
-	set = function(groupId, itemData)
-    	-- This is NOT a real "set", we pass the widget reference to this function which contains similar, but not the same, info.
-    	
-    	if itemData then
-	    	local groupName = groupIdToName[groupId];
-	    	
-	    	itemData:RemoveFromGroup(groupName);
-			
-			if AceConfigRegistry then
-				-- Now rebuild the list
-				AceConfigRegistry:NotifyChange("InventoriumOptions");
-			end
-    	end
-	end,
-	width = "double",
-	dialogControl = "ConfigItemLinkButton",
-};
-
-local function UpdateAddItemList(info)
-	local groupName = groupIdToName[info[2]];
-	
-	if not addon.db.profile.groups[groupName].items then
-		addon.db.profile.groups[groupName].items = {};
-	end
-	
-	-- Merge all items from all groups together
-	local items = {};
-	for groupName, values in pairs(addon.db.profile.groups) do
-		if values.items then
-			for itemId, _ in pairs(values.items) do
-				items[itemId] = true;
-			end
-		end
-	end
-	
-	local ref = options.args.groups.args[info[2]].args.add.args.list.args;
-	
-	-- Remaking the list, so out with the old, in with the new
-	table.wipe(ref);
-	
-	-- Parse bags and show these
-	for bagID = 4, 0, -1 do
-		for slot = 1, GetContainerNumSlots(bagID) do
-			local itemId = addon:GetItemId(GetContainerItemLink(bagID, slot));
-			
-			if itemId then
-				if not items[itemId] then
-					-- If this item isn't used in any group yet
-					ref[itemId] = tblAddItemTemplate;
-				else
-					-- It's already used in a group, don't show it
-					ref[itemId] = nil;
-				end
-			end
-		end
-	end
-	
-	if includeTradeSkillItems ~= 500 then
-		-- Include tradeskill items
-		
-		-- Go through all trade skills for the profession
-		for i = 1, GetNumTradeSkills() do
-			-- Try to retrieve the itemlink, this will be nil if current item is a group instead
-			local itemLink = GetTradeSkillItemLink(i);
-			
-			if itemLink then
-				local itemId = addon:GetItemId(itemLink);
-				if not itemId then
-					-- If this isn't an item, it can only be an enchant instead
-					itemId = tonumber(itemLink:match("|Henchant:([-0-9]+)|h"));
-					
-					itemId = addon.scrollIds[itemId]; -- change enchantIds into scrollIds
-				end
-				
-				if itemId then
-					local itemLevel = select(4, GetItemInfo(itemId)) or 0;
-					
-					if includeTradeSkillItems == 0 or itemLevel >= includeTradeSkillItems then
-						if not items[itemId] then
-							-- If this item isn't used in any group yet
-							ref[itemId] = tblAddItemTemplate;
-						else
-							-- It's already used in a group, don't show it
-							ref[itemId] = nil;
-						end
-					end
-				else
-					addon:Debug("|cffff0000ERROR|r: Couldn't find proper item id for " .. itemLink);
-				end
-			end
-		end
-	end
-end
-
-local function UpdateRemoveItemList(info)
-	local groupName = groupIdToName[info[2]];
-	
-	if not addon.db.profile.groups[groupName].items then
-		addon.db.profile.groups[groupName].items = {};
-	end
-	
-	local ref = options.args.groups.args[info[2]].args.remove.args.list.args;
-	
-	-- Unset all
-	for itemId, _ in pairs(ref) do
-		ref[itemId] = nil;
-	end
-	
-	-- Parse items in group and show these
-	for itemId, _ in pairs(addon.db.profile.groups[groupName].items) do
-		ref[itemId] = tblRemoveItemTemplate;
-	end
-end
-
--- Default group
-local defaultGroup = {
-	order = 0,
-	type = "group",
-	childGroups = "tab",
-	name = function(info)
-		local groupId = info[#info];
-		if groupIsVirtual[groupId] then
-			return ("%s  |cfffed000Virtual|r"):format(groupIdToName[groupId]);
-		else
-			return groupIdToName[groupId];
-		end
-	end,
-	desc = function(info)
-		local groupId = info[#info];
-		if groupIsVirtual[groupId] then
-			return "This is a virtual group, you can use it to override the defaults for other groups.";
-		end
-	end,
-	args = {
-		general = {
-			order = 10,
-			type = "group",
-			name = "General",
-			desc = "Change the general settings for just this group.",
-			args = {
-				general = {
-					order = 0,
-					type = "group",
-					inline = true,
-					name = "General",
-					set = SetOption,
-					get = GetOption,
-					disabled = GetDisabled,
-					args = {
-						description = {
-							order = 0,
-							type = "description",
-							name = function(info)
-								local groupName = groupIdToName[info[2]];
-								
-								local t = "Here you can set general settings for the currently selected group. If you do not wish to override a setting, the default setting specified in the general group will be used.\n\n";
-								
-								local currentAddon, selectedAddonName = addon:GetItemCountAddon(groupName);
-								local preferedAddon = addon:GetOptionByKey(groupName, "itemCountAddon");
-								
-								if currentAddon then
-									--GetCharacterCount
-									--addon.supportedAddons.itemCount[selectedExternalAddon]
-									t = t .. "Currently using |cfffed000" .. selectedAddonName .. "|r as your item count addon. This addon is " .. ((currentAddon.IsEnabled() and "|cff00ff00enabled|r") or "|cffff0000disabled|r") .. ".";
-									
-									if currentAddon.GetTotalCount and currentAddon.GetCharacterCount then
-										t = t .. " This addon supports |cfffed000both total as local|r item counts.";
-									elseif currentAddon.GetTotalCount then
-										t = t .. " This addon supports |cfffed000only total|r item counts.";
-									elseif currentAddon.GetCharacterCount then
-										t = t .. " This addon supports |cfffed000only local|r item counts.";
-									end
-									
-									if preferedAddon ~= selectedAddonName then
-										t = t .. "\n\n|cffff0000You have selected |cfffed000" .. preferedAddon .. "|r|cffff0000 as your item count addon, but this appears to be disabled and thus a random alternative was selected.|r";
-									end
-								end
-								
-								return t;
-							end,
-						},
-						header = {
-							order = 5,
-							type = "header",
-							name = "",
-						},
-						overrideAuctionPricingAddon = {
-							order = 9,
-							type = "toggle",
-							name = "Override pricing addon",
-							desc = "Allows you to override the pricing addon setting for this group.",
-							arg = "auctionPricingAddon",
-						},
-						auctionPricingAddon = {
-							order = 10,
-							type = "select",
-							name = "Prefered pricing addon",
-							desc = "Select the addon you prefer data for this group to be retrieved from. A random supported addon will be used if the selected addon can not be found.",
-							values = function()
-								local temp = {};
-								for name, value in pairs(addon.supportedAddons.auctionPricing) do
-									temp[name] = name;
-								end
-								
-								return temp;
-							end,
-							set = function(info, value)
-								local groupName = groupIdToName[info[2]];
-								local optionName = info[#info];
-								
-								addon.db.profile.groups[groupName][optionName] = value ~= "" and value;
-								
-								if addon.supportedAddons.auctionPricing[value].OnSelect then
-									addon.supportedAddons.auctionPricing[value].OnSelect();
-								end
-							end,
-							arg = "overrideAuctionPricingAddon",
-						},
-						overrideItemCountAddon = {
-							order = 19,
-							type = "toggle",
-							name = "Override item count addon",
-							desc = "Allows you to override the item count addon setting for this group.",
-							arg = "itemCountAddon",
-						},
-						itemCountAddon = {
-							order = 20,
-							type = "select",
-							name = "Prefered item count addon",
-							desc = "Select the addon you prefer data for this group to be retrieved from. A random supported addon will be used if the selected addon can not be found.",
-							values = function()
-								local temp = {};
-								for name, value in pairs(addon.supportedAddons.itemCount) do
-									temp[name] = name;
-								end
-								
-								return temp;
-							end,
-							set = function(info, value)
-								local groupName = groupIdToName[info[2]];
-								local optionName = info[#info];
-								
-								addon.db.profile.groups[groupName][optionName] = value ~= "" and value;
-								
-								if addon.supportedAddons.itemCount[value].OnSelect then
-									addon.supportedAddons.itemCount[value].OnSelect();
-								end
-							end,
-							arg = "overrideItemCountAddon",
-						},
-						overrideCraftingAddon = {
-							order = 29,
-							type = "toggle",
-							name = "Override crafting addon",
-							desc = "Allows you to override the crafting addon setting for this group.",
-							arg = "craftingAddon",
-						},
-						craftingAddon = {
-							order = 30,
-							type = "select",
-							name = "Prefered crafting addon",
-							desc = "Select the addon you prefer data from this group to be queued into. A random supported addon will be used if the selected addon can not be found.",
-							values = function()
-								local temp = {};
-								for name, value in pairs(addon.supportedAddons.crafting) do
-									temp[name] = name;
-								end
-								
-								return temp;
-							end,
-							set = function(info, value)
-								local groupName = groupIdToName[info[2]];
-								local optionName = info[#info];
-								
-								addon.db.profile.groups[groupName][optionName] = value ~= "" and value;
-								
-								if addon.supportedAddons.crafting[value].OnSelect then
-									addon.supportedAddons.crafting[value].OnSelect();
-								end
-							end,
-							arg = "overrideCraftingAddon",
-						},
-						overrideLocalItemData = {
-							order = 39,
-							type = "toggle",
-							name = "Override local item data",
-							desc = "Allows you to override the local item data setting for this group.",
-							arg = "localItemData",
-						},
-						localItemData = {
-							order = 40,
-							type = "multiselect",
-							name = "Include in local item data",
-							desc = "Select which data should be included in the local item data.",
-							values = {
-								["Bag"] = "Bag",
-								["Bank"] = "Bank",
-								["Auction House"] = "Auction House",
-								["Mailbox"] = "Mailbox",
-							},
-							get = GetMultiOption,
-							dialogControl = "Dropdown", -- this is not standard, normal multiselect control gives us a list of all chars with toggle-boxes. UGLY! We want a multiselect-box instead.
-							arg = "overrideLocalItemData",
-						},
-						virtualGroup = {
-							order = 50,
-							type = "select",
-							name = "Use virtual group settings", 
-							desc = "Use the settings from a virtual group before using the general defaults.\n\n|cffff9933This is an advanced option, you will probably not need it unless you manage a lot of groups.|r\n\n|cfffed000Off|r: Use the overridden options in this group and then the defaults.\n\n|cfffed000On|r: Use the overridden options in this group, then the ones in the selected virtual group and then the defaults.",
-							values = function(info)
-								local groupName = groupIdToName[info[2]];
-								
-								local temp = {};
-								
-								temp[""] = "";
-								for name, values in pairs(addon.db.profile.groups) do
-									if values.isVirtual and name ~= groupName then
-										temp[name] = name;
-									end
-								end
-								
-								return temp;
-							end,
-							set = function(info, value)
-								local groupName = groupIdToName[info[2]];
-								local optionName = info[#info];
-								
-								addon.db.profile.groups[groupName][optionName] = value ~= "" and value;
-							end,
-							disabled = false,
-						},
-					},
-				},
-				minimumStock = {
-					order = 10,
-					type = "group",
-					inline = true,
-					name = "Minimum stock",
-					set = SetOption,
-					get = GetOption,
-					disabled = GetDisabled,
-					args = {
-						description = {
-							order = 0,
-							type = "description",
-							name = "Here you can specify the minimum amount of items you wish to keep in stock and related settings for the currently selected group. Please note the values entered here do not affect the queued quantities, you must set settings for that in the area below.",
-						},
-						header = {
-							order = 5,
-							type = "header",
-							name = "",
-						},
-						
-						overrideMinLocalStock = {
-							order = 10,
-							type = "toggle",
-							name = "Override min local stock",
-							desc = "Allows you to override the minimum local stock setting for this group.",
-							arg = "minLocalStock",
-						},
-						minLocalStock = {
-							order = 11,
-							type = "range",
-							min = 0,
-							max = 100000,
-							softMax = 100,
-							step = 1,
-							name = "Minimum local stock",
-							desc = "You can manually enter a value between 100 and 100.000 in the text box below if the provided range is insufficient.",
-							arg = "overrideMinLocalStock",
-						},
-						overrideAlertBelowLocalMinimum = {
-							order = 15,
-							type = "toggle",
-							name = "Override local minimum alert",
-							desc = "Allows you to override wether an alert should be shown when an item in this group gets below the local minimum stock threshold.",
-							arg = "alertBelowLocalMinimum",
-						},
-						alertBelowLocalMinimum = {
-							order = 16,
-							type = "toggle",
-							name = "Alert when below local minimum (NYI)",
-							desc = "Show an alert when an item in this group gets below the local minimum stock threshold.",
-							arg = "overrideAlertBelowLocalMinimum",
-						},
-						overrideAutoRefill = {
-							order = 17,
-							type = "toggle",
-							name = "Override auto refill",
-							desc = "Allows you to override wether you want to automatically refill items when below the minimum local stock.",
-							arg = "autoRefill",
-						},
-						autoRefill = {
-							order = 18,
-							type = "toggle",
-							name = "Auto refill from (guild) bank",
-							desc = "Automatically refill items from the bank (unless this is included in the local count) or guild bank when below the minimum local stock.",
-							arg = "overrideAutoRefill",
-						},
-						
-						overrideMinGlobalStock = {
-							order = 20,
-							type = "toggle",
-							name = "Override min global stock",
-							desc = "Allows you to override the minimum global stock setting for this group.",
-							arg = "minGlobalStock",
-						},
-						minGlobalStock = {
-							order = 21,
-							type = "range",
-							min = 0,
-							max = 100000,
-							softMax = 100,
-							step = 1,
-							name = "Minimum global stock",
-							desc = "You can manually enter a value between 100 and 100.000 in the text box below if the provided range is insufficient.",
-							arg = "overrideMinGlobalStock",
-						},
-						overrideAlertBelowGlobalMinimum = {
-							order = 25,
-							type = "toggle",
-							name = "Override global minimum alert",
-							desc = "Allows you to override wether an alert should be shown when an item in this group gets below the global minimum stock threshold.",
-							arg = "alertBelowGlobalMinimum",
-						},
-						alertBelowGlobalMinimum = {
-							order = 26,
-							type = "toggle",
-							name = "Alert when below global minimum (NYI)",
-							desc = "Show an alert when an item in this group gets below the global minimum stock threshold.",
-							arg = "overrideAlertBelowGlobalMinimum",
-						},
-						
-						overrideSummaryThresholdShow = {
-							order = 34,
-							type = "toggle",
-							name = "Override summary showing",
-							desc = "Allows you to override when this group should appear in the summary.",
-							arg = "summaryThresholdShow",
-						},
-						summaryThresholdShow = {
-							order = 35,
-							type = "range",
-							min = 0,
-							max = 10,
-							softMax = 100,
-							step = 0.05,
-							isPercent = true,
-							name = "Show in summary when below",
-							desc = "Show items in the summary when below the specified percentage of the minimum stock.\n\nYou can manually enter a value between 1.000% and 10.000% in the edit box if the provided range is insufficient.",
-							arg = "overrideSummaryThresholdShow",
-						},
-						overrideTrackAtCharacters = {
-							order = 39,
-							type = "toggle",
-							name = "Override track at",
-							desc = "Allows you to override at which characters items in this group should appear in the summary and generate alerts.",
-							arg = "trackAtCharacters",
-						},
-						trackAtCharacters = {
-							order = 40,
-							type = "multiselect",
-							name = "Track at",
-							desc = "Select at which characters this should appear in the summary and generate alerts.",
-							values = function()
-								local temp = {};
-								for charName in pairs(addon.db.factionrealm.characters) do
-									temp[charName] = charName;
-								end
-								
-								return temp;
-							end,
-							get = GetMultiOption,
-							dialogControl = "Dropdown", -- this is not standard, normal multiselect control gives us a list of all chars with toggle-boxes. UGLY! We want a multiselect-box instead.
-							arg = "overrideTrackAtCharacters",
-						},
-					},
-				},
-				refill = {
-					order = 20,
-					type = "group",
-					inline = true,
-					name = "Replenishing stock",
-					set = SetOption,
-					get = GetOption,
-					disabled = GetDisabled,
-					args = {
-						description = {
-							order = 0,
-							type = "description",
-							name = function(info)
-								local groupName = groupIdToName[info[2]];
-								local r = "Here you can specify the amount of items to which you wish to restock when you are collecting new items for the currently selected group. This may be higher than the minimum stock.\n\n";
-								
-								r = r .. "When restocking the target amount is |cfffed000" .. addon:GetOptionByKey(groupName, "restockTarget") .. "|r of every item. Not queueing craftable items when only missing |cfffed000" .. floor( addon:GetOptionByKey(groupName, "minCraftingQueue") * addon:GetOptionByKey(groupName, "restockTarget") ) .. "|r (|cfffed000" .. ( addon:GetOptionByKey(groupName, "minCraftingQueue") * 100 ) .. "%|r) of the restock target and making |cfffed000" .. floor( ( addon:GetOptionByKey(groupName, "bonusQueue") * addon:GetOptionByKey(groupName, "restockTarget") ) + .5 ) .. "|r (|cfffed000" .. ( addon:GetOptionByKey(groupName, "bonusQueue") * 100 ) .. "%|r) extra items when you completely ran out. ";
-								
-								if addon:GetOptionByKey(groupName, "priceThreshold") == 0 then
-									r = r .. "Queueing items at |cfffed000any|r auction value.";
-								else
-									r = r .. "Queueing items worth |cfffed000" .. addon:ReadableMoney(addon:GetOptionByKey(groupName, "priceThreshold")) .. "|r or more.";
-								end
-								
-								return r;
-							end,
-						},
-						header = {
-							order = 5,
-							type = "header",
-							name = "",
-						},
-						overrideRestockTarget = {
-							order = 9,
-							type = "toggle",
-							name = "Override restock target",
-							desc = "Allows you to override the restock target setting for this group.",
-							arg = "restockTarget",
-						},
-						restockTarget = {
-							order = 10,
-							type = "range",
-							min = 0,
-							max = 100000,
-							softMax = 100,
-							step = 1,
-							name = "Restock target",
-							desc = "You can manually enter a value between 100 and 100.000 in the edit box if the provided range is insufficient.",
-							arg = "overrideRestockTarget",
-						},
-						overrideMinCraftingQueue = {
-							order = 19,
-							type = "toggle",
-							name = "Override min queue",
-							desc = "Allows you to override the minimum craftable items queue setting for this group.",
-							arg = "minCraftingQueue",
-						},
-						minCraftingQueue = {
-							order = 20,
-							type = "range",
-							min = 0,
-							max = 1,
-							step = 0.01,
-							isPercent = true,
-							name = "Don't queue if I only miss",
-							desc = "Don't add a craftable item to the queue if I only miss this much or less of the restock target.\n\nExample: if your restock target is set to 60 and this is set to 5%, an item won't be queued unless you are missing more than 3 of it.",
-							arg = "overrideMinCraftingQueue",
-						},
-						overrideBonusQueue = {
-							order = 29,
-							type = "toggle",
-							name = "Override bonus queue",
-							desc = "Allows you to override the bonus craftable items queue setting for this group.",
-							arg = "bonusQueue",
-						},
-						bonusQueue = {
-							order = 30,
-							type = "range",
-							min = 0,
-							max = 10, -- 1000%
-							step = 0.01, -- 1%
-							isPercent = true,
-							name = "Bonus queue",
-							desc = "Get additional items when there are none left.\n\nExample: if your restock target is set to 60 and this is set to 10%, you will get 66 items instead of just 60 if you end up with none left while queueing.",
-							arg = "overrideBonusQueue",
-						},
-						overridePriceThreshold = {
-							order = 39,
-							type = "toggle",
-							name = "Override price threshold",
-							desc = "Allows you to override the price threshold setting for this group.",
-							arg = "priceThreshold",
-						},
-						priceThreshold = {
-							order = 40,
-							type = "input",
-							name = "Price threshold",
-							desc = "Only queue craftable items when they are worth at least this much according to your auction house addon.\n\nSet to 0 to ignore auction prices.",
-							validate = function(info, value) return addon:ValidateReadableMoney(info, value); end,
-							get = function(i) return addon:ReadableMoney(GetOption(i)); end,
-							set = function(i, v) SetOption(i, addon:ReadableMoneyToCopper(v)); end,
-							arg = "overridePriceThreshold",
-						},
-						overrideSummaryHidePriceThreshold = {
-							order = 49,
-							type = "toggle",
-							name = "Override summary showing",
-							desc = "Allows you to override if items in this group should be hidden from the summary while their value is below the price threshold.",
-							arg = "summaryHidePriceThreshold",
-						},
-						summaryHidePriceThreshold = {
-							order = 50,
-							type = "toggle",
-							name = "Hide when below threshold",
-							desc = "Hide items from the summary when their value is below the set price threshold.",
-							arg = "overrideSummaryHidePriceThreshold",
-						},
-						overrideAlwaysGetAuctionValue = {
-							order = 59,
-							type = "toggle",
-							name = "Override auction value showing",
-							desc = "Allows you to override if the auction value of items in this group should be cached and displayed even when the price threshold is set to 0|cffeda55fc|r.",
-							arg = "alwaysGetAuctionValue",
-						},
-						alwaysGetAuctionValue = {
-							order = 60,
-							type = "toggle",
-							name = "Always show auction value",
-							desc = "Always cache and show the auction value of items in this group, even if the price threshold is set to 0|cffeda55fc|r.",
-							arg = "overrideAlwaysGetAuctionValue",
-						},
-					},
-				},
-			},
-		},
-		group = {
-			order = 20,
-			type = "group",
-			name = "Management",
-			desc = "Rename, delete, duplicate or export this group.",
-			args = {
-				actions = {
-					order = 10,
-					type = "group",
-					name = "Actions",
-					inline = true,
-					args = {
-						rename = {
-							order = 10,
-							type = "input",
-							name = "Rename group - New name",
-							desc = "Change the name of this group to something else. You can also use item links here as you wish.",
-							validate = ValidateGroupName,
-							set = function(info, value)
-								local oldGroupName = groupIdToName[info[2]];
-								
-								addon.db.profile.groups[value] = CopyTable(addon.db.profile.groups[oldGroupName]);
-								addon.db.profile.groups[oldGroupName] = nil;
-								
-								groupIdToName[info[2]] = value;
-								groupIdToName[value] = true;
-								groupIdToName[oldGroupName] = nil;
-								
-								mod:FillGroupOptions();
-							end,
-							get = function(info)
-								return groupIdToName[info[2]];
-							end,
-						},
-						duplicate = {
-							order = 20,
-							type = "input",
-							name = "Duplicate group - New name",
-							desc = "Duplicate this group. You can also use item links here as you wish.\n\nAll item data will be erased.",
-							validate = ValidateGroupName,
-							set = function(info, value)
-								local oldGroupName = groupIdToName[info[2]];
-								
-								addon.db.profile.groups[value] = CopyTable(addon.db.profile.groups[oldGroupName]);
-								
-								-- Reset item data (duplicate items me no want)
-								addon.db.profile.groups[value].items = nil;
-								
-								mod:FillGroupOptions();
-							end,
-							get = false,
-						},
-						delete = {
-							order = 30,
-							type = "execute",
-							name = "Delete group",
-							desc = "Delete the currently selected group.",
-							confirm = true,
-							confirmText = "Are you sure you wish to |cffff0000DELETE|r this group? This action is not reversable!",
-							func = function(info)
-								local groupName = groupIdToName[info[2]];
-								
-								addon.db.profile.groups[groupName] = nil;
-								
-								mod:FillGroupOptions();
-							end,
-						},
-					},
-				},
-				export = {
-					order = 40,
-					type = "group",
-					name = "Export",
-					inline = true,
-					args = {
-						input = {
-							order = 10,
-							type = "input",
-							multiline = true,
-							name = "Group data",
-							width = "full",
-							desc = "Export the group data for the currently selected group. Press CTRL-A to select all and CTRL-C to copy the text.",
-							set = false,
-							get = function(info)
-								local groupName = groupIdToName[info[2]];
-								
-								-- We want to include the group name, so we copy the table then set another value
-								local temp = CopyTable(addon.db.profile.groups[groupName]);
-								temp.name = groupName;
-								temp.trackAtCharacters = nil;
-								temp.overrideTrackAtCharacters = nil;
-								
-								if not AceSerializer then
-									AceSerializer = LibStub("AceSerializer-3.0");
-								end
-								
-								return AceSerializer:Serialize(temp);
-							end,
-						},
-					},
-				},
-			},
-		},
-		add = {
-			order = 30,
-			type = "group",
-			name = "Add items",
-			desc = "Add new items to this group.",
-			hidden = function(info) return groupIsVirtual[info[2]]; end,
-			args = {
-				singleAdd = {
-					order = 10,
-					type = "group",
-					inline = true,
-					name = "Add items",
-					args = {
-						help = {
-							order = 10,
-							type = "description",
-							name = "You can add a single item to this group at a time by pasting the item-id or an item-link in the field to the left or you can also import multiple items at once by pasting exported item data in the field to the right. Scroll further down to add items based on your inventory contents.",
-						},
-						itemLink = {
-							order = 20,
-							type = "input",
-							name = "Single item add (item-link or item-id)",
-							desc = "Shift-click an item-link or enter an item-id to add the related item to this group. You can only add one item link or item id at a time.",
-							validate = function(info, value)
-								-- If the value is empty we'll allow passing to clear the carret
-								if value == "" then return true; end
-								
-								local groupName = groupIdToName[info[2]];
-								
-								local itemId = (addon:GetItemId(string.trim(value)) or tonumber(string.trim(value)));
-								
-								local itemData = addon.ItemData:New(itemId);
-								
-								if not itemId then
-									return "This is not a valid item link or id.";
-								elseif itemData:InGroup() then
-									return ("This item is already in the group |cfffed000%s|r."):format(itemData:InGroup());
-								end
-								
-								return true;
-							end,
-							set = function(info, value)
-								if value and value ~= "" then
-									local groupName = groupIdToName[info[2]];
-									
-									local itemId = (addon:GetItemId(string.trim(value)) or tonumber(string.trim(value)));
-									
-									local itemData = addon.ItemData:New(itemId);
-									
-									if itemData:AddToGroup(groupName) then
-										addon.Print(("Added %s to the selected group."):format( (itemData.link or unknownItemName:format(itemId)) ), addon.Colors.Green);
-										
-										if AceConfigRegistry then
-											-- Now rebuild the list
-											AceConfigRegistry:NotifyChange("InventoriumOptions");
-										end
-									else
-										addon.Print(("%s is already in the group |cfffed000%s|r."):format( (itemData.link or unknownItemName:format(itemId)), itemData:InGroup() ), addon.Colors.Red);
-									end
-								end
-							end,
-							get = false,
-						},
-						importItemData = {
-							order = 30,
-							type = "input",
-							name = "Import item data",
-							desc = "Import item data from an exported item data-string. Any items already grouped will be skipped.",
-							set = function(info, value)
-								local groupName = groupIdToName[info[2]];
-								
-								local allItemIds = { string.split(";", value or "") };
-								
-								for _, value in pairs(allItemIds) do
-									local itemId = tonumber(value);
-									
-									if itemId then
-										local itemData = addon.ItemData:New(itemId);
-										
-										if itemData:InGroup() then
-											addon.Print(("Skipping %s (#%d) as it is already in the group |cfffed000%s|r."):format(itemData.link or "Unknown", itemId, itemData:InGroup()), addon.Colors.Red);
-										elseif itemData:AddToGroup(groupName) then
-											addon.Print(("Added %s to the group |cfffed000%s|r."):format(itemData.link or unknownItemName:format(itemId), groupName), addon.Colors.Green);
-										end
-									else
-										addon.Print(("\"%s\" is not a number."):format(value), addon.Colors.Red);
-									end
-								end
-								
-								if AceConfigRegistry then
-									-- Now rebuild the list
-									AceConfigRegistry:NotifyChange("InventoriumOptions");
-								end
-							end,
-							get = false,
-						},
-						importPremadeData = {
-							order = 40,
-							type = "select",
-							name = "Import premade data",
-							desc = "Import item data from a premade item-group. Any items already grouped will be skipped.",
-							values = function()
-								local temp = {};
-								for key, group in pairs(addon.defaultGroups) do
-									temp[key] = key;
-								end
-								
-								return temp;
-							end,
-							set = function(info, value)
-								local groupName = groupIdToName[info[2]];
-								
-								print(("Importing items from the premade group \"|cfffed000%s|r\"."):format(value));
-								
-								-- Remember we imported this group and it's version so if it is ever changed, people can be notified
-								if not addon.db.profile.groups[groupName].premadeGroups then
-									addon.db.profile.groups[groupName].premadeGroups = {};
-								end
-								addon.db.profile.groups[groupName].premadeGroups[value] = addon.defaultGroups[value].version;
-								
-								for itemId, version in pairs(addon.defaultGroups[value].items) do
-									if version > 0 then
-										-- Sanity check
-										itemId = itemId and tonumber(itemId);
-										
-										local itemData = addon.ItemData:New(itemId);
-										
-										if not itemId then
-											addon.Print(("\"|cfffed000%s|r\" is not a number."):format(value), addon.Colors.Red);
-										elseif itemData:InGroup() then
-											addon.Print(("Skipping |cfffed000%s|r as it is already in the group |cfffed000%s|r."):format( (itemData.link or unknownItemName:format(itemId)), itemData:InGroup() ), addon.Colors.Red);
-										elseif itemData:AddToGroup(groupName) then
-											addon.Print(("Added %s to the group |cfffed000%s|r."):format( (itemData.link or unknownItemName:format(itemId)), groupName ), addon.Colors.Green);
-										end
-									end
-								end
-								
-								if AceConfigRegistry then
-									-- Now rebuild the list
-									AceConfigRegistry:NotifyChange("InventoriumOptions");
-								end
-							end,
-							get = false,
-						},
-					},
-				},
-				massAdd = {
-					order = 20,
-					type = "group",
-					inline = true,
-					name = "Mass add",
-					args = {
-						help = {
-							order = 10,
-							type = "description",
-							name = "Click the items you wish to add to this group or add multiple of these items at once by providing a name filter in the field below.",
-						},
-						massAdd = {
-							order = 20,
-							type = "input",
-							name = "Add all items matching...",
-							desc = "Add every item in your inventory matching the name entered in this field. If you enter \"Glyph\" as a filter, any items in your inventory containing this in their name will be added to this group.",
-							set = function(info, value)
-						    	local groupName = groupIdToName[info[2]];
-						    	
-								if not value then return; end
-								
-								value = value:lower();
-								
-								local ref = options.args.groups.args[info[2]].args.add.args.list.args;
-								
-								for itemId, test in pairs(ref) do
-									if test then
-										local itemData = addon.ItemData:New(itemId);
-										
-										if itemData.name:lower():find(value) then
-									    	if itemData:AddToGroup(groupName) then
-												addon.Print(("Added %s to the selected group."):format( (itemData.link or unknownItemName:format(itemId)) ), addon.Colors.Green);
-									    	else
-									    		addon.Print(("Couldn't add %s because it is already in a group."):format(itemData.link or unknownItemName:format(itemId)), addon.Colors.Red);
-									    	end
-										end
-									end
-								end
-								
-								if AceConfigRegistry then
-									-- Now rebuild the list
-									AceConfigRegistry:NotifyChange("InventoriumOptions");
-								end
-							end,
-							get = false,
-						},
-						minItemLevel = {
-							order = 40,
-							type = "select",
-							values = function()
-								local temp = {};
-								
-								temp[0] = "Include everything";
-								
-								local itemLevelTemplate = "Itemlevel >= %d";
-								
-								for i = 1, 49 do
-									temp[( i * 10 )] = itemLevelTemplate:format(( i * 10 ));
-								end
-								
-								temp[500] = "Include nothing";
-								
-								return temp;
-							end,
-							name = "Include tradeskill items",
-							desc = "Include all items above this item level from the currently opened tradeskill window in the below item list.\n\nSetting this very low this might considerably slow down this config window.\n\nSet to 500 to disable showing of items completely.",
-							set = function(i, v) includeTradeSkillItems = v; end,
-							get = function() return includeTradeSkillItems; end,
-							disabled = function()
-								if GetTradeSkillLine() == "UNKNOWN" then
-									includeTradeSkillItems = 500;
-									return true; -- disabled
-								else
-									return false;
-								end
-							end,
-						},
-					},
-				},
-				list = {
-					order = 30,
-					type = "group",
-					inline = true,
-					name = "Item list",
-					hidden = UpdateAddItemList,
-					args = {
-					
-					},
-				},
-			},
-		},
-		remove = {
-			order = 40,
-			type = "group",
-			name = "Current items",
-			desc = "View, export or remove items from this group.",
-			hidden = function(info) return groupIsVirtual[info[2]]; end,
-			args = {
-				help = {
-					order = 10,
-					type = "group",
-					inline = true,
-					name = "Help",
-					hidden = false,
-					args = {
-						help = {
-							order = 10,
-							type = "description",
-							name = "Click the items you wish to remove from this group.",
-						},
-						massRemove = {
-							order = 20,
-							type = "input",
-							name = "Remove all items matching...",
-							desc = "Remove every item in this group matching the name entered in this field. If you enter \"Glyph\" as a filter, any items in this group containing this in their name will be removed from this group.",
-							set = function(info, value)
-						    	local groupName = groupIdToName[info[2]];
-						    	
-								if not value then return; end
-								
-								value = value:lower();
-								
-								local ref = options.args.groups.args[info[2]].args.remove.args.list.args;
-								
-								for itemId, test in pairs(ref) do
-									if test then
-										local itemData = addon.ItemData:New(itemId);
-										
-										if itemData.name:lower():find(value) then
-											itemData:RemoveFromGroup(groupName);
-											
-											addon.Print(("Removed %s from the selected group."):format( (itemData.link or unknownItemName:format(itemId)) ), addon.Colors.Red);
-										end
-									end
-								end
-								
-								if AceConfigRegistry then
-									-- Now rebuild the list
-									AceConfigRegistry:NotifyChange("InventoriumOptions");
-								end
-							end,
-							get = false,
-						},
-						premadeGroups = {
-							order = 30,
-							type = "select",
-							name = "Imported premade groups",
-							desc = "This is a list of all premade groups that were imported into this group. You will be notified when any of these premade groups have changed and you will be able to import these changes.\n\nSelect a group to stop reminding you of changes to the premade group (the item list will be unaffected). Doing so will require you to manually update this when new items are added to the game.",
-							values = function(info)
-						    	local groupName = groupIdToName[info[2]];
-						    	
-						    	local temp = {};
-								temp[""] = "";
-						    	if addon.db.profile.groups[groupName].premadeGroups then
-							    	for name, version in pairs(addon.db.profile.groups[groupName].premadeGroups) do
-							    		temp[name] = name;
-							    	end
-						    	end
-						    	
-						    	return temp;
-							end,
-							set = function(info, value)
-								if value and value ~= "" then
-									-- Remove premade group from this group
-							    	local groupName = groupIdToName[info[2]];
-							    	
-							    	addon.db.profile.groups[groupName].premadeGroups[value] = nil;
-							    	
-							    	print(("No longer notifying you about changes made to the premade group named \"|cfffed000%s|r\"."):format(value));
-						    	end
-							end,
-							get = false,
-							disabled = function(info)
-						    	local groupName = groupIdToName[info[2]];
-						    	
-								return (not addon.db.profile.groups[groupName].premadeGroups);
-							end,
-						},
-					},
-				},
-				list = {
-					order = 20,
-					type = "group",
-					inline = true,
-					name = "Item list",
-					hidden = UpdateRemoveItemList,
-					args = {
-					
-					},
-				},
-				export = {
-					order = 30,
-					type = "group",
-					name = "Export",
-					inline = true,
-					args = {
-						input = {
-							order = 10,
-							type = "input",
-							name = "Item data",
-							width = "full",
-							desc = "Export the item data for the currently selected group. Press CTRL-A to select all and CTRL-C to copy the text.",
-							set = false,
-							get = function(info)
-								local groupName = groupIdToName[info[2]];
-								
-								local combinedItemIds;
-								-- Parse items in group and show these
-								for itemId, _ in pairs(addon.db.profile.groups[groupName].items) do
-									if not combinedItemIds then
-										combinedItemIds = tostring(itemId);
-									else
-										combinedItemIds = combinedItemIds .. (";%d"):format(itemId);
-									end
-								end
-								
-								return combinedItemIds; -- We don't serialize this because we actually DO want people to be able to manually modify it - besides, parsing it isn't going to be hard
-							end,
-						},
-					},
-				},
-			},
-		},
-	},
-};
-
-
-
-
-
-
-
-
-
--- Object functions
-
-function mod:OnEnable()
-	-- Register our config slash command
-	-- /im config
-	addon:RegisterSlash(function(this)
-		-- We don't want any other windows open at this time.
-		for name, module in this:IterateModules() do
-			if module.CloseFrame then
-				module:CloseFrame();
-			end
-		end
-		
-		this:GetModule("Config"):Show();
-	end, { "c", "config", "conf", "option", "options", "opt", "setting", "settings" }, "|Hfunction:InventoriumCommandHandler:config|h|cff00fff7/im config|r|h (or /im c) - Open the config window to change the settings and manage groups.");
-	
-	-- Whenever the profile is changed, update the groups | args: (object for the functionName, eventName, functionName)
-	addon.db.RegisterCallback(self, "OnProfileChanged", "RefreshConfig");
-	addon.db.RegisterCallback(self, "OnProfileCopied", "RefreshConfig");
-	addon.db.RegisterCallback(self, "OnProfileReset", "RefreshConfig");
-	
-	-- Register our custom widgets
-	local Widgets = addon:GetModule("Widgets");
-	Widgets:ItemLinkButton();
-	Widgets:ConfigItemLinkButton();
-end
-
-function mod:RefreshConfig()
-	self:PremadeGroupsCheck();
-	
-	self:FillGroupOptions();
-end
-
-function mod:Load()
-	if not AceConfigDialog and not AceConfigRegistry then
-		self:PremadeGroupsCheck();
-		
-		self:FillOptions();
-	
-		-- Build options dialog
-		AceConfigDialog = LibStub("AceConfigDialog-3.0");
-		AceConfigRegistry = LibStub("AceConfigRegistry-3.0");
-		-- Register options table
-		LibStub("AceConfig-3.0"):RegisterOptionsTable("InventoriumOptions", options);
-		-- Set a nice default size (so that 4 normal sized elements fit next to eachother)
-		AceConfigDialog:SetDefaultSize("InventoriumOptions", 975, 600);
-		
-		 -- In case the addon is loaded from another condition, always call the remove interface options
-		if AddonLoader and AddonLoader.RemoveInterfaceOptions then
-			AddonLoader:RemoveInterfaceOptions("Inventorium");
-		end
-		
-		-- Add to the blizzard addons options thing
-		AceConfigDialog:AddToBlizOptions("InventoriumOptions");
-	end
-end
-
-function mod:Show()
-	self:Load();
-	
-	AceConfigDialog:Open("InventoriumOptions");
-end
-
-function mod:FillOptions()
-	options = {
-		type = "group",	
-		name = "Inventorium Config",
-		childGroups = "tree",
-		args = {
-		},
-	};
-	
-	-- General
-	self:FillGeneralOptions();
-	
-	-- Help
-	self:FillHelpOptions();
-	
-	-- Profile
-	options.args.profiles = LibStub("AceDBOptions-3.0"):GetOptionsTable(addon.db, true);
-	options.args.profiles.order = 200;
-	
-	-- Groups
-	self:MakeGroupOptions();
-	
-	-- Groups-contents
-	self:FillGroupOptions();
-end
-
-function mod:FillGeneralOptions()
-	options.args.general = {
-		order = 100,
-		type = "group",
-		name = "General",
-		desc = "Change general Inventorium settings.",
-		args = {
-			general = {
-				order = 1,
-				type = "group",
-				inline = true,
-				name = "General",
-				args = {
-					description = {
-						order = 0,
-						type = "description",
-						name = function()
-							local t = "Here you can set general settings. The settings entered here will be used when you choose not to override the settings within an individual group.\n\n";
-							
-							local currentAddon, selectedAddonName = addon:GetItemCountAddon();
-							local preferedAddon = addon.db.profile.defaults.itemCountAddon;
-							
-							if currentAddon then
-								t = t .. "Currently using |cfffed000" .. selectedAddonName .. "|r as your item count addon. This addon is " .. ((currentAddon.IsEnabled() and "|cff00ff00enabled|r") or "|cffff0000disabled|r") .. ".";
-								
-								if currentAddon.GetTotalCount and currentAddon.GetCharacterCount then
-									t = t .. " This addon supports |cfffed000both total as local|r item counts.";
-								elseif currentAddon.GetTotalCount then
-									t = t .. " This addon supports |cfffed000only total|r item counts.";
-								elseif currentAddon.GetCharacterCount then
-									t = t .. " This addon supports |cfffed000only local|r item counts.";
-								end
-								
-								if preferedAddon ~= selectedAddonName then
-									t = t .. "\n\n|cffff0000You have selected |cfffed000" .. preferedAddon .. "|r|cffff0000 as your item count addon, but this appears to be disabled and thus a random alternative was selected.|r";
-								end
-							end
-							
-							return t;
-						end,
-					},
-					header = {
-						order = 5,
-						type = "header",
-						name = "",
-					},
-					auctionPricingAddon = {
-						order = 10,
-						type = "select",
-						name = "Prefered pricing addon",
-						desc = "Select the addon you prefer data to be retrieved from. A random supported addon will be used if the selected addon can not be found.",
-						values = function()
-							local temp = {};
-							for name, value in pairs(addon.supportedAddons.auctionPricing) do
-								temp[name] = name;
-							end
-							
-							return temp;
-						end,
-						get = function() return addon.db.profile.defaults.auctionPricingAddon; end,
-						set = function(i, v)
-							addon.db.profile.defaults.auctionPricingAddon = v;
-							
-							if addon.supportedAddons.auctionPricing[v].OnSelect then
-								addon.supportedAddons.auctionPricing[v].OnSelect();
-							end
-						end,
-					},
-					itemCountAddon = {
-						order = 20,
-						type = "select",
-						name = "Prefered item count addon",
-						desc = "Select the addon you prefer data to be retrieved from. A random supported addon will be used if the selected addon can not be found.",
-						values = function()
-							local temp = {};
-							for name, value in pairs(addon.supportedAddons.itemCount) do
-								temp[name] = name;
-							end
-							
-							return temp;
-						end,
-						get = function() return addon.db.profile.defaults.itemCountAddon; end,
-						set = function(i, v)
-							addon.db.profile.defaults.itemCountAddon = v;
-							
-							if addon.supportedAddons.itemCount[v].OnSelect then
-								addon.supportedAddons.itemCount[v].OnSelect();
-							end
-						end,
-					},
-					craftingAddon = {
-						order = 30,
-						type = "select",
-						name = "Prefered crafting addon",
-						desc = "Select the addon you prefer data to be queued into. A random supported addon will be used if the selected addon can not be found.",
-						values = function()
-							local temp = {};
-							for name, value in pairs(addon.supportedAddons.crafting) do
-								temp[name] = name;
-							end
-							
-							return temp;
-						end,
-						get = function() return addon.db.profile.defaults.craftingAddon; end,
-						set = function(i, v)
-							addon.db.profile.defaults.craftingAddon = v;
-							
-							if addon.supportedAddons.crafting[v].OnSelect then
-								addon.supportedAddons.crafting[v].OnSelect();
-							end
-						end,
-					},
-					localItemData = {
-						order = 40,
-						type = "multiselect",
-						name = "Include in local item data",
-						desc = "Select which data should be included in the local item data.",
-						values = {
-							["Bag"] = "Bag",
-							["Bank"] = "Bank",
-							["Auction House"] = "Auction House",
-							["Mailbox"] = "Mailbox",
-						},
-						get = function(i, v) return addon.db.profile.defaults.localItemData and addon.db.profile.defaults.localItemData[v]; end,
-						set = function(i, v, e) addon.db.profile.defaults.localItemData[v] = e or nil; end,
-						dialogControl = "Dropdown", -- this is not standard, normal multiselect control gives us a list of all chars with toggle-boxes. UGLY! We want a multiselect-box instead.
-					},
-				},
-			},
-			minimumStock = {
-				order = 10,
-				type = "group",
-				inline = true,
-				name = "Minimum stock",
-				args = {
-					description = {
-						order = 0,
-						type = "description",
-						name = "Here you can specify the default minimum amount of items you wish to keep in stock and related settings. The settings entered here will be used when you choose not to override the settings within an individual group.",
-					},
-					header = {
-						order = 5,
-						type = "header",
-						name = "",
-					},
-					minLocalStock = {
-						order = 10,
-						type = "range",
-						min = 0,
-						max = 100000,
-						softMax = 100,
-						step = 1,
-						name = "Minimum local stock",
-						desc = "You can manually enter a value between 100 and 100.000 in the text box below if the provided range is insufficient.",
-						get = function() return addon.db.profile.defaults.minLocalStock; end,
-						set = function(i, v) addon.db.profile.defaults.minLocalStock = v; end,
-					},
-					alertBelowLocalMinimum = {
-						order = 11,
-						type = "toggle",
-						name = "Alert when below local minimum (NYI)",
-						desc = "Show an alert when this item gets below this threshold.",
-						get = function() return addon.db.profile.defaults.alertBelowLocalMinimum; end,
-						set = function(i, v) addon.db.profile.defaults.alertBelowLocalMinimum = v; end,
-					},
-					autoRefill = {
-						order = 12,
-						type = "toggle",
-						name = "Auto refill from (guild) bank",
-						desc = "Automatically refill items from the bank (unless this is included in the local count) or guild bank when below the minimum local stock.",
-						get = function() return addon.db.profile.defaults.autoRefill; end,
-						set = function(i, v) addon.db.profile.defaults.autoRefill = v; end,
-					},
-					minGlobalStock = {
-						order = 20,
-						type = "range",
-						min = 0,
-						max = 100000,
-						softMax = 100,
-						step = 1,
-						name = "Minimum global stock",
-						desc = "You can manually enter a value between 100 and 100.000 in the text box below if the provided range is insufficient.",
-						get = function() return addon.db.profile.defaults.minGlobalStock; end,
-						set = function(i, v) addon.db.profile.defaults.minGlobalStock = v; end,
-					},
-					alertBelowGlobalMinimum = {
-						order = 21,
-						type = "toggle",
-						name = "Alert when below global minimum (NYI)",
-						desc = "Show an alert when this item gets below this threshold.",
-						get = function() return addon.db.profile.defaults.alertBelowGlobalMinimum; end,
-						set = function(i, v) addon.db.profile.defaults.alertBelowGlobalMinimum = v; end,
-					},
-					summaryThresholdShow = {
-						order = 30,
-						type = "range",
-						min = 0,
-						max = 10,
-						softMax = 100,
-						step = 0.05,
-						isPercent = true,
-						name = "Show in summary when below",
-						desc = "Show items in the summary when below this percentage of the minimum stock. This can be either below the minimum or the global stock.\n\nYou can manually enter a value between 1.000% and 10.000% in the edit box if the provided range is insufficient.",
-						get = function() return addon.db.profile.defaults.summaryThresholdShow; end,
-						set = function(i, v) addon.db.profile.defaults.summaryThresholdShow = v; end,
-					},
-					trackAtCharacters = {
-						order = 40,
-						type = "multiselect",
-						name = "Track at",
-						desc = "Select at which characters this should appear in the summary and generate alerts.",
-						values = function()
-							local temp = {};
-							for charName in pairs(addon.db.factionrealm.characters) do
-								temp[charName] = charName;
-							end
-							
-							return temp;
-						end,
-						get = function(i, v)
-							return addon.db.profile.defaults.trackAtCharacters[v];
-						end,
-						set = function(i, v, e)
-							addon.db.profile.defaults.trackAtCharacters[v] = e or nil;
-						end,
-						dialogControl = "Dropdown", -- this is not standard, normal multiselect control gives us a list of all chars with toggle-boxes. UGLY! We want a multiselect-box instead.
-					},
-				},
-			},
-			refill = {
-				order = 20,
-				type = "group",
-				inline = true,
-				name = "Replenishing stock",
-				args = {
-					description = {
-						order = 0,
-						type = "description",
-						name = function()
-							local r = "Here you can specify the default amount of items to which you wish to restock when you are collecting new items. This may be higher than the minimum stock. The settings entered here will be used when you choose not to override the settings within an individual group.\n\n";
-							
-							r = r .. "When restocking the target amount is |cfffed000" .. addon.db.profile.defaults.restockTarget .. "|r of every item. Not queueing craftable items when only missing |cfffed000" .. floor( addon.db.profile.defaults.minCraftingQueue * addon.db.profile.defaults.restockTarget ) .. "|r (|cfffed000" .. ( addon.db.profile.defaults.minCraftingQueue * 100 ) .. "%|r) of the restock target.";
-							
-							return r;
-						end,
-					},
-					header = {
-						order = 5,
-						type = "header",
-						name = "",
-					},
-					restockTarget = {
-						order = 10,
-						type = "range",
-						min = 0,
-						max = 100000,
-						softMax = 100,
-						step = 1,
-						name = "Restock target",
-						desc = "You can manually enter a value between 100 and 100.000 in the edit box if the provided range is insufficient.",
-						get = function() return addon.db.profile.defaults.restockTarget; end,
-						set = function(i, v) addon.db.profile.defaults.restockTarget = v; end,
-					},
-					minCraftingQueue = {
-						order = 20,
-						type = "range",
-						min = 0,
-						max = 1,
-						step = 0.01, -- 1%
-						isPercent = true,
-						name = "Don't queue if I only miss",
-						desc = "Don't add a craftable item to the queue if I only miss this much or less of the restock target.\n\nExample: if your restock target is set to 60 and this is set to 5%, an item won't be queued unless you are missing more than 3 of it.",
-						get = function() return addon.db.profile.defaults.minCraftingQueue; end,
-						set = function(i, v) addon.db.profile.defaults.minCraftingQueue = v; end,
-					},
-					bonusQueue = {
-						order = 30,
-						type = "range",
-						min = 0,
-						max = 10, -- 1000%
-						step = 0.01, -- 1%
-						isPercent = true,
-						name = "Bonus queue",
-						desc = "Get additional items when there are none left.\n\nExample: if your restock target is set to 60 and this is set to 10%, you will get 66 items instead of just 60 if you end up with none left while queueing.",
-						get = function() return addon.db.profile.defaults.bonusQueue; end,
-						set = function(i, v) addon.db.profile.defaults.bonusQueue = v; end,
-					},
-					priceThreshold = {
-						order = 40,
-						type = "input",
-						name = "Price threshold",
-						desc = "Only queue craftable items when they are worth at least this much according to your auction house addon.\n\nSet to 0 to ignore auction prices.",
-						validate = function(info, value) return addon:ValidateReadableMoney(info, value); end,
-						get = function() return addon:ReadableMoney(addon.db.profile.defaults.priceThreshold); end,
-						set = function(i, v) addon.db.profile.defaults.priceThreshold = addon:ReadableMoneyToCopper(v); end,
-					},
-					summaryHidePriceThreshold = {
-						order = 50,
-						type = "toggle",
-						name = "Hide when below threshold",
-						desc = "Hide items from the summary when their value is below the set price threshold.",
-						get = function() return addon.db.profile.defaults.summaryHidePriceThreshold; end,
-						set = function(i, v) addon.db.profile.defaults.summaryHidePriceThreshold = v; end,
-					},
-					alwaysGetAuctionValue = {
-						order = 60,
-						type = "toggle",
-						name = "Always show auction value",
-						desc = "Always cache and show the auction value of items, even if the price threshold is set to 0|cffeda55fc|r.",
-						get = function() return addon.db.profile.defaults.alwaysGetAuctionValue; end,
-						set = function(i, v) addon.db.profile.defaults.alwaysGetAuctionValue = v; end,
-					},
-				},
-			},
-			colorCodes = {
-				order = 30,
-				type = "group",
-				inline = true,
-				name = "Color codes",
-				args = {
-					description = {
-						order = 0,
-						type = "description",
-						name = "Change the color code thresholds based on the current stock remaining of the required minimum stock.",
-					},
-					header = {
-						order = 5,
-						type = "header",
-						name = "",
-					},
-					green = {
-						order = 10,
-						type = "range",
-						min = 0,
-						max = 1,
-						step = 0.01,
-						isPercent = true,
-						name = "|cff00ff00Green|r",
-						desc = "Show quantity in green when at least this much of the minimum stock is available.",
-						get = function() return addon.db.profile.defaults.colors.green; end,
-						set = function(i, v) addon.db.profile.defaults.colors.green = v; end,
-					},
-					yellow = {
-						order = 20,
-						type = "range",
-						min = 0,
-						max = 1,
-						step = 0.01,
-						isPercent = true,
-						name = "|cffffff00Yellow|r",
-						desc = "Show quantity in yellow when at least this much of the minimum stock is available.",
-						get = function() return addon.db.profile.defaults.colors.yellow; end,
-						set = function(i, v) addon.db.profile.defaults.colors.yellow = v; end,
-					},
-					orange = {
-						order = 30,
-						type = "range",
-						min = 0,
-						max = 1,
-						step = 0.01,
-						isPercent = true,
-						name = "|cffff9933Orange|r",
-						desc = "Show quantity in orange when at least this much of the minimum stock is available.",
-						get = function() return addon.db.profile.defaults.colors.orange; end,
-						set = function(i, v) addon.db.profile.defaults.colors.orange = v; end,
-					},
-					red = {
-						order = 40,
-						type = "range",
-						min = 0,
-						max = 1,
-						step = 0.01,
-						isPercent = true,
-						name = "|cffff0000Red|r",
-						desc = "Show quantity in red when at least this much of the minimum stock is available.",
-						get = function() return addon.db.profile.defaults.colors.red; end,
-						set = function(i, v) addon.db.profile.defaults.colors.red = v; end,
-					},
-				},
-			},
-			extra = {
-				order = 40,
-				type = "group",
-				inline = true,
-				name = "Extra",
-				args = {
-					description = {
-						order = 0,
-						type = "description",
-						name = "Additional optional settings.",
-					},
-					header = {
-						order = 5,
-						type = "header",
-						name = "",
-					},
-					removeCharacter = {
-						order = 40,
-						type = "select",
-						name = "Forget character",
-						desc = "Select a character to remove all traces of it from Inventorium's memory.\n\nYour current character can not be removed, you must login to a different character to do so.",
-						values = function()
-							local temp = {};
-							
-							temp[""] = "";
-							
-							local playerName = UnitName("player");
-							for charName in pairs(addon.db.factionrealm.characters) do
-								if playerName ~= charName then
-									temp[charName] = charName;
-								end
-							end
-							
-							return temp;
-						end,
-						set = function(i, value)
-							if value and value ~= "" then
-								addon.db.factionrealm.characters[value] = nil;
-								addon.db.profile.defaults.trackAtCharacters[value] = nil;
-								for name, values in pairs(addon.db.profile.groups) do
-									if values.trackAtCharacters then
-										values.trackAtCharacters[name] = nil;
-									end
-								end
-							end
-						end,
-					},
-				},
-			},
-		},
-	};
-end
-
-function mod:FillHelpOptions()
-	options.args.help = {
-		order = 150,
-		type = "group",
-		childGroups = "tab",
-		name = "Help",
-		desc = "Useful information for if you're unfamiliar with a part of the addon.",
-		args = {
-			general = {
-				order = 1,
-				type = "group",
-				name = "General",
-				args = {
-					description = {
-						order = 0,
-						type = "description",
-						name = "Please note that all multi-select |cfffed000dropdown|r boxes were turned into multi-select |cfffed000toggle|r boxes. I do not intend to keep it this way, however it can not yet be reverted due to a major bug in one of the libraries used by Inventorium. The layout of this config may look terribly organized in it's current state.\n\n" .. 
-								"Since this is a beta some functionality might not be implemented yet while the options are available (usually - but not always - tagged as \"NYI\"). These options are used to indicate a feature is on the way and will be implemented before Inventorium is tagged as a release.\n\n" .. 
-								"Please request things you want and report anything that's clunky, weird, vague or otherwise buggy at |cfffed000the Inventorium development addon page|r. You can find this by searching for \"|cfffed000Inventorium|r\" at |cfffed000CurseForge.com|r.\n\n" .. 
-								"Tutorials for Inventorium will be created after the first stable release. If you require any help before that you can always contact me in the |cfffed000#JMTC|r IRC channel at |cfffed000QuakeNet.org|r. You may also report issues and request things there if you wish.\n\n" .. 
-								"You might notice the summary window currently gets very slow when refreshed once you get over 100-200 items in the list, this is a known issue and will be fixed in |cfffed000version 1.1|r or a little later (which is after the initial release).",
-					},
-				},
-			},
-			manual = {
-				order = 2,
-				type = "group",
-				name = "Manual",
-				args = {
-					intro = {
-						order = 1,
-						type = "group",
-						inline = true,
-						name = "Intro",
-						args = {
-							description = {
-								order = 0,
-								type = "description",
-								name = "|cfffed000Inventorium|r is an inventory tracking and restocking addon aimed towards making it extremely easy to keep enough stock of specific items at your characters. It provides a quick overview where you can see your current stock compared to the required stock and the current item value at the auction house whenever you find this relevant. From this overview you can queue craftable items into your favorite crafting profession addon and even restock from a vendor.",
-							},
-						},
-					},
-					Overview = {
-						order = 2,
-						type = "group",
-						inline = true,
-						name = "Overview",
-						args = {
-							description = {
-								order = 0,
-								type = "description",
-								name = "In the stock overview, which is called the summary, you can view a list with all items relevant to your current character with their updated stock and auction house values. You can sort this list on item quality, current stock, percentage of stock missing, and item values and this way easily manually find the items you wish to process. If you prefer to automate the process, you can also configure the groups exactly as you wish and hit the queue button to process everything in that group.\n\n" .. 
-								
-										"The item count data can be retrieved from most popular item count addons. This includes |cfffed000ItemCount|r and |cfffed000DataStore|r, but also |cfffed000Altoholic|r (even though this is not really a proper item count addon).\n" .. 
-										"The auction house values data can also be retrieved from most popular auction house addons. This includes |cfffed000Auctionator|r, |cfffed000Auctioneer|r, |cfffed000AuctionLite|r, |cfffed000AuctionMaster|r and any other addon implementing the standard for retrieving item values. Item values from the summary windows of |cfffed000AuctionProfitMaster|r and |cfffed000ZeroAuctions|r are also supported, but not recommended.",
-							},
-						},
-					},
-					Groups = {
-						order = 3,
-						type = "group",
-						inline = true,
-						name = "Groups",
-						args = {
-							description = {
-								order = 0,
-								type = "description",
-								name = "All items can be distributed over multiple groups to configure them exactly as you want. You can put all your glyphs in one group, gems in another and scrolls in a third with every single one of them having it's own set of unique settings. Per group you can set a price threshold, required item count, relevant characters and much more. The setup will feel very familiar to anyone that has used Quick Auctions to some extend as it uses a similar standard (though with many enhancements).",
-							},
-						},
-					},
-					Queueing = {
-						order = 4,
-						type = "group",
-						inline = true,
-						name = "Queueing",
-						args = {
-							description = {
-								order = 0,
-								type = "description",
-								name = "Queueing items into your crafting profession addon can be done per group or for all visible groups at once. This requires the relevant profession window to be open. All queueing is done based on the filters set in the config for the related group, any items falling outside will be ignored.\n\n" .. 
-									
-										"The queueing can be done into most popular crafting profession window replacing addons. This includes |cfffed000AdvancedTradeSkillWindow|r, |cfffed000GnomeWorks|r and |cfffed000Skillet|r.",
-							},
-						},
-					},
-					Configuring = {
-						order = 5,
-						type = "group",
-						inline = true,
-						name = "Configuring",
-						args = {
-							description = {
-								order = 0,
-								type = "description",
-								name = "The system resources used by this addon while it is stand-by are at a minimum, so you can just leave it enabled for all your characters unless you wish otherwise.\n\n" .. 
-										
-										"To start using Inventorium write |cfffed000/im|r in your chat. This will show a list of all available commands; |cfffed000/im config|r, |cfffed000summary|r and some others. Tip: Most of these commands have short alternatives, such as |cfffed000c|r for |cfffed000config|r and |cfffed000s|r for |cfffed000summary|r.\n\n" .. 
-										
-										"Write |cfffed000/im c|r to open the config window to start configuring your groups. The first tab you will find opened is the |cfffed000General|r config. Go ahead and select your prefered item count, crafting and pricing addons. Scroll down and quickly take note of all other options there, you won't really need everything (yet) but knowing it is available will save you from searching later on.\n\n" .. 
-										
-										"Click on the |cfffed000Groups|r tab. Here you will see a view handy options, in the future you might be able to find complete groups to import at popular blogs or forums making Inventorium very easy to import and initially setup, but for now we will have to make one manually. Quickly think about an easy-to-use and handy naming pattern and enter a name for your new group (e.g. |cfffed000Glyphs (Hunter)|r) and hit enter (leave the group type to the default). This group will be created and added to the list under the groups tab. Open the config for this group by clicking the group name.\n\n" .. 
-										
-										"You are now seeing a copy of the general config with override boxes next to everything. Configure this as you wish, for example override the |cfffed000minimum global stock|r to 20, |cfffed000track at|r to your banker and crafter (ensure your current char is also included) and the |cfffed000price threshold|r to 5g or so. Once done go to the \"add items\" tab to add items to this group so this group manages them. Again observice the available functionality so you know what you can do. Based on our previous example of Hunter glyphs we have three possibilities: one is to get all Hunter glyphs in our bags and add these, the second is to open the profession window and include everything above or equal to an item level of 0 and the third and in this case best possibility is to add items from a premade group. Select |cfffed000Glyphs (Hunter)|r from the |cfffed000premade groups|r select box to import all Hunter glyphs into this group. After doing so you can close the config window to check the results. Write |cfffed000/im s|r to view the summary window.\n\n" .. 
-										
-										"You can now copy this group and repeat the same steps to add all other groups you require. Think of things like one group per class for glyphs, one group per color for gems, one group per type of scroll (e.g. Heirloom, Twinks and Popular), one group for craftable epics, one group for flasks, one group for inks, one for parchments, etc.",
-							},
-						},
-					},
-					VirtualGroups = {
-						order = 6,
-						type = "group",
-						inline = true,
-						name = "Virtual groups",
-						args = {
-							description = {
-								order = 0,
-								type = "description",
-								name = "|cffccccccThis is advanced optional functionality. I recommend skipping this paragraph if you are new to Inventorium.|r\n\n" .. 
-										
-										"To allow changing the settings of multiple groups at once you can make virtual groups. These groups are basically like the general tab as their values are used when you aren't overriding a setting in a more specific group. After making a virtual group you still have to select it in all the \"child\"-groups so these groups know where to look for their settings when they are empty. Going back to our |cfffed000Glyphs (Hunter)|r group, we could make a virtual group called |cfffed000Glyphs|r which contained info like a default price threshold of 5g and a minimum crafting queue for all glyph groups. In each specific group you can then further specify that you would - for example - want 20 Hunter glyphs but 40 Druid glyphs as these might just sell a lot more often.",
-							},
-						},
-					},
-				},
-			},
-			FAQ = {
-				order = 3,
-				type = "group",
-				name = "FAQ",
-				args = {
-					description = {
-						order = 0,
-						type = "description",
-						name = "|cfffed000My groups don't appear in the summary window.|r\nPlease ensure your current character is toggled on at the \"track at\" option beneath the \"minimum stock\" category within a group or the defaults.\n\n" .. 
-								"|cfffed000The auction value collumn always shows a \"-\".|r\nThe auction value will not be cached when you set the \"price threshold\" beneath the \"replenishing stock\" category to |cfffed0000c|r. You can change this behavior by adjusting this value or toggling the \"Always show auction value\" option on.\n\n" .. 
-								"|cfffed000What relation does Inventorium have to ZeroAuctions or AuctionProfitMaster?|r\nNone. ZA/APM and IM are two completely seperate addons and do not interfere with eachother. At best you can use the auction pricing data displayed at the ZA/APM summary window as pricing source by selecting either addon as pricing addon, but neither IM nor ZA/APM will adjust any settings nor execute any other actions at one another. ZA/APM is an auction house addon. IM is a stock management addon. We are not related. We do not work together. We probably never will.\n\n" .. 
-								"|cfffed000What use do profiles have?|r\nBecause there is already the \"track at\" option, profiles may not be useful to anyone. Nevertheless someone might find a use for it in some way and thus it is left available. You can use it to test certain things for example without the risk of your main groups being destroyed (although this should never be an excuse not to back up your settings from time to time).\n\n" .. 
-								"|cfffed000Can you provide me with a sample scenario for virtual groups?|r\nNot really. If you are just getting to know Inventorium then I suggest leaving this functionality for the moment. It only makes things more complicated.\n\nAnyway, the simplest (and possibly most popular) setup to imagine are glyphs. There are over 300 glyphs available distributed over 10 classes. Glyphs for certain classes (such as the tribrids; Druids & Paladins) might sell a lot more often than those for others (such as pure DPS; Hunters, etc.).\n\nTo get an easily adjustable setup you can make one virtual group, called \"Glyphs\" and override all your prefered settings in there. After you are done, make a glyph-group for each class (such as \"Glyphs (Death Knight)\" etc.) and select \"Glyphs\" as virtual group for every one of them (you can easily insert item data to these class specific groups by selecting premade data).\n\nNow, to change the settings for all glyph groups you can just change the settings within the virtual \"Glyph\" group. To change the settings for one class in particular, such as Paladins because they sell more often than others, you can click this group and override the appropriate settings for just that group. There are many more possibilities for you to find out.\n\n" .. 
-								"",
-					},
-				},
-			},
-		},
-	};
-end
-
-function mod:MakeGroupOptions()
-	options.args.groups = {
-		order = 1100,
-		type = "group",
-		name = "Groups",
-		desc = "Change a group.",
-		args = {
-			create = {
-				order = 10,
-				type = "group",
-				inline = true,
-				name = "Create a brand new group",
-				args = {
-					name = {
-						order = 10,
-						type = "input",
-						name = "Group name",
-						desc = "The name of the group. You can also use item links as you wish.",
-						validate = ValidateGroupName,
-						set = function(_, value)
-							addon.db.profile.groups[value] = {};
-							if currentGroupType == "Virtual" then
-								addon.db.profile.groups[value].isVirtual = true;
-							end
-							
-							mod:FillGroupOptions();
-						end,
-						get = false,
-						width = "double",
-					},
-					type = {
-						order = 20,
-						type = "select",
-						name = "Type (advanced)",
-						desc = "The type of the new group. This can not be changed at a later time.\n\n|cffff9933This is an advanced option, you will probably not need it unless you manage a lot of groups.|r\n\n|cfffed000Normal|r: A normal group with complete functionality.\n\n|cfffed000Virtual|r: A virtual group which you can use to override the defaults for a set of groups. You can not add items to virtual groups.",
-						values = {
-							["Normal"] = "Normal",
-							["Virtual"] = "Virtual",
-						},
-						set = function(_, value) currentGroupType = value; end,
-						get = function() return currentGroupType; end,
-					},
-				},
-			},
-			import = {
-				order = 20,
-				type = "group",
-				inline = true,
-				name = "Import a group",
-				args = {
-					input = {
-						order = 10,
-						type = "input",
-						multiline = true,
-						name = "Group data",
-						desc = "Paste the group data as provided by a group export. If you are trying to import multiple groups at the same time, make sure to use newlines to seperate them.",
-						set = function(info, value)
-							local data = { string.split("\n", value or "") };
-							
-							for _, current in pairs(data) do
-								if not AceSerializer then
-									AceSerializer = LibStub("AceSerializer-3.0");
-								end
-								
-								local result, temp = AceSerializer:Deserialize(current);
-								
-								if not temp.name then
-									print("|cffff0000The provided data is not supported.|r");
-								elseif ValidateGroupName(nil, temp.name) ~= true then
-									print(("|cffff0000Aborting: A group named \"%s\" already exists.|r"):format(temp.name));
-								else
-									local name = temp.name;
-									temp.name = nil;
-									print(("Importing %s..."):format(name));
-									
-									if temp.items then
-										-- Remove items that are already in another group
-										for value, _ in pairs(temp.items) do
-											local itemId = tonumber(value);
-											
-											local itemData = addon.ItemData:New(itemId);
-											
-											if not itemId then
-												print(("\"%s\" is not a number."):format(value));
-												temp.items[value] = nil;
-											elseif itemData:InGroup() then
-												print(("Skipping %s as it is already in the group |cfffed000%s|r."):format( (itemData.link or unknownItemName:format(itemId)), itemData:InGroup() ));
-												temp.items[value] = nil;
-											else
-												-- Ensure the keys are numeric
-												temp.items[value] = nil;
-												temp.items[itemId] = true;
-											end
-										end
-									end
-									
-									-- Ensure this data isn't received (this would be silly/buggy as exports from other accounts - with different characters - won't know what to do with this)
-									temp.trackAtCharacters = nil;
-									temp.overrideTrackAtCharacters = nil;
-									
-									addon.db.profile.groups[name] = temp;
-								end
-							end
-							
-							self:FillGroupOptions();
-						end,
-						get = false,
-						width = "full",
-					},
-				},
-			},
-		},
-	};
-end
-
-function mod:FillGroupOptions()
-	for id, name in pairs(groupIdToName) do
-		if type(name) == "string" and not addon.db.profile.groups[name] then
-			options.args.groups.args[id] = nil;
-			groupIdToName[id] = nil;
-			groupIdToName[name] = nil;
-			groupIsVirtual[id] = nil;
-		end
-	end
-	
-	for name, values in pairs(addon.db.profile.groups) do
-		if not groupIdToName[name] then
-			options.args.groups.args[tostring(count)] = defaultGroup;
-			
-			groupIdToName[tostring(count)] = name;
-			groupIdToName[name] = true;
-			if values.isVirtual then
-				groupIsVirtual[tostring(count)] = true;
-			end
-			
-			count = ( count + 1 );
-		end
-	end
-end
-
-
-
-
-
-
--- Verify premade groups
-
-function mod:PremadeGroupsCheck(updateGroupName, updateKey, accept)
-	-- Compare the current premade groups with those used, notify about changes
-	if addon.defaultGroups then
-		for premadeGroupName, groupInfo in pairs(addon.defaultGroups) do
-			-- Go through all default groups
-			
-			for groupName, values in pairs(addon.db.profile.groups) do
-				-- Go through all groups to find those with this premade group
-				
-				if values.premadeGroups and values.premadeGroups[premadeGroupName] and values.premadeGroups[premadeGroupName] < groupInfo.version then
-					-- Outdated group
-					
-					if updateGroupName and updateKey then
-						-- This function was called after pressing yes or no in a confirm box
-						
-						if accept then
-							-- Yes was clicked
-							
-							for itemId, version in pairs(groupInfo.items) do
-								-- Go through all items in this premade group
-								
-								local itemData = addon.ItemData:New(itemId);
-								
-								if version > values.premadeGroups[premadeGroupName] then
-									-- This item was added in a more recent version than this group: Add item
-									
-									if itemData:InGroup() then
-										print(("Skipping %s as it is already in the group |cfffed000%s|r."):format( (itemData.link or unknownItemName:format(itemId)), itemData:InGroup() ));
-									elseif itemData:AddToGroup(groupName) then
-										addon.Print(("Added %s found in the premade group |cfffed000%s|r to the group |cfffed000%s|r."):format( (itemData.link or unknownItemName:format(itemId)), premadeGroupName, itemData:InGroup() ), addon.Colors.Green);
-									end
-								elseif ( version * -1 ) > values.premadeGroups[premadeGroupName] then
-									if itemData:InGroup() == groupName then
-										itemData:RemoveFromGroup(groupName);
-										
-										addon.Print(("Removed %s from the group |cfffed000%s|r as it was removed from the premade group |cfffed000%s|r."):format( (itemData.link or unknownItemName:format(itemId)), itemData:InGroup(), premadeGroupName ), addon.Colors.Red);
-									else
-										print(("Skipping the removal of %s as it isn't in the group |cfffed000%s|r."):format( (itemData.link or unknownItemName:format(itemId)), itemData:InGroup() ));
-									end
-								end
-							end
-							
-							if AceConfigRegistry then
-								-- Now rebuild the list
-								AceConfigRegistry:NotifyChange("InventoriumOptions");
-							end
-							
-							-- Remember the new version
-							values.premadeGroups[premadeGroupName] = groupInfo.version;
-						else
-							-- No was clicked
-							
-							-- Let user know what was not added
-							for itemId, version in pairs(groupInfo.items) do
-								-- Go through all items in this premade group
-								
-								local itemData = addon.ItemData:New(itemId);
-								
-								if version > values.premadeGroups[premadeGroupName] then
-									-- This item was added in a more recent version than this group: don't add (since we clicked no), but announce it
-									
-									print(("Skipping %s found in the premade group |cfffed000%s|r."):format( (itemData.link or unknownItemName:format(itemId)), itemData:InGroup() ));
-								end
-							end
-							
-							-- Remember the new version
-							values.premadeGroups[premadeGroupName] = groupInfo.version;
-						end
-					else
-						StaticPopupDialogs["InventoriumConfirmUpdatePremadeGroup"] = {
-							text = "The premade group |cfffed000%s|r used in the group |cfffed000%s|r has been changed. Do you wish to copy these changes?",
-							button1 = YES,
-							button2 = NO,
-							OnAccept = function()
-								addon:PremadeGroupsCheck(groupName, premadeGroupName, true);
-							end,
-							OnCancel = function(_, _, reason)
-								if reason == "clicked" then
-									addon:PremadeGroupsCheck(groupName, premadeGroupName, false);
-								end
-							end,
-							timeout = 0,
-							whileDead = 1,
-							hideOnEscape = 1,
-						};
-						StaticPopup_Show("InventoriumConfirmUpdatePremadeGroup", premadeGroupName, groupName);
-						
-						return;
-					end
-				end
-			end
-		end
-	end
-end
--- a/ContainerItem.class.lua	Thu Jan 06 11:04:55 2011 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,32 +0,0 @@
-local addon = select(2, ...);
-
--- Define the class
-
-addon.ContainerItem = {};
-addon.ContainerItem.__index = addon.ContainerItem;
-
--- Construct
-function addon.ContainerItem:New(id)
-	local self = {};
-	
-	setmetatable(self, addon.ContainerItem);
-	
-	-- Standard info everything needs
-	self.id = id;
-	self.totalCount = 0;
-	self.locations = {};
-	
-	return self;
-end
-
-function addon.ContainerItem:AddLocation(container, slot, count)
-	table.insert(self.locations, {
-		container = container,
-		slot = slot,
-		count = count,
-	});
-	
-	self.totalCount = (self.totalCount + count);
-	
-	return true;
-end
--- a/Core.lua	Thu Jan 06 11:04:55 2011 +0100
+++ b/Core.lua	Thu Jan 06 20:05:30 2011 +0100
@@ -191,13 +191,6 @@
 	end
 end
 
-function addon:GetItemId(itemLink)
-	itemLink = itemLink and itemLink:match("|Hitem:([-0-9]+):"); -- if itemLink is nil, it won't execute the second part
-	itemLink = itemLink and tonumber(itemLink);
-	
-	return itemLink;
-end
-
 function addon:GetOptionByKey(groupName, optionName, noDefault)
 	if groupName and addon.db.profile.groups[groupName] and addon.db.profile.groups[groupName][optionName] ~= nil then
 		-- If this option exists within the settings of this group
@@ -447,6 +440,13 @@
 	DEFAULT_CHAT_FRAME:AddMessage(text or "", red, green, blue, nil, 5);
 end
 
+function addon:GetItemID(itemLink)
+	itemLink = itemLink and itemLink:match("|Hitem:([-0-9]+):"); -- if itemLink is nil, it won't execute the second part
+	itemLink = itemLink and tonumber(itemLink);
+	
+	return itemLink;
+end
+
 -- Debug
 
 function addon:Debug(t)
--- a/CraftingAddons/AdvancedTradeSkillWindow.lua	Thu Jan 06 11:04:55 2011 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,16 +0,0 @@
-do
-	
-	local function Queue(tradeSkillIndex, amount)
-		local link = GetTradeSkillItemLink(tradeSkillIndex);
-		local itemName = link:match("%[([^%[%]]*)%]");
-		
-		return ATSW_AddJob(itemName, amount);
-	end
-	
-	local function IsEnabled()
-		return (ATSW_AddJob);
-	end
-	
-	IMRegisterCraftingAddon("AdvancedTradeSkillWindow", Queue, IsEnabled);
-	
-end
--- a/CraftingAddons/Cauldron.lua	Thu Jan 06 11:04:55 2011 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,39 +0,0 @@
-do
-	
-	local function Queue(tradeSkillIndex, amount)
-		local tradeskillName, currentLevel, maxLevel = GetTradeSkillLine();
-		
-		local link = GetTradeSkillItemLink(tradeSkillIndex);
-		local itemName = link and link:match("%[([^%[%]]*)%]");
-		
-		if not itemName then
-			return;
-		end
-		
-		local skillInfo = Cauldron:GetSkillInfo(tradeskillName, itemName);
-	
-		CauldronQueue:AddItem(Cauldron.db.realm.userdata[Cauldron.vars.playername].queue, skillInfo, amount);
-			
-		Cauldron:UpdateQueue();
-		
-		-- update the shopping list
-		Cauldron:UpdateShoppingListFromQueue();
-		
-		Cauldron:UpdateButtons();
-		
-		return;
-	end
-	
-	local function IsEnabled()
-		return (Cauldron and CauldronQueue and CauldronQueue.AddItem);
-	end
-	
-	local function OnSelect()
-		local addonName = "|r|cfffed000Cauldron|r|cffff6600";
-		
-		print("|cffff6600" .. addonName .. " has not yet been tested. Please report any of your experiences.|r");
-	end
-	
-	IMRegisterCraftingAddon("Cauldron", Queue, IsEnabled, OnSelect);
-	
-end
--- a/CraftingAddons/GnomeWorks.lua	Thu Jan 06 11:04:55 2011 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,17 +0,0 @@
-do
-	
-	local function Queue(tradeSkillIndex, amount)
-		local link = GetTradeSkillRecipeLink(tradeSkillIndex);
-		local recipeId = tonumber(link:match("|Henchant:([-0-9]+)|h"));
-		
-		GnomeWorks:ShowQueueList();
-		return GnomeWorks:AddToQueue(GnomeWorks.player, tradeSkillIndex, recipeId, amount);
-	end
-	
-	local function IsEnabled()
-		return (GnomeWorks and GnomeWorks.AddToQueue);
-	end
-	
-	IMRegisterCraftingAddon("GnomeWorks", Queue, IsEnabled);
-	
-end
--- a/CraftingAddons/Skillet.lua	Thu Jan 06 11:04:55 2011 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,17 +0,0 @@
-do
-	
-	local function Queue(tradeSkillIndex, amount)
-		local link = GetTradeSkillRecipeLink(tradeSkillIndex);
-		local recipeId = tonumber(link:match("|Henchant:([-0-9]+)|h"));
-		
-		local newCommand = Skillet:QueueCommandIterate(recipeId, amount);
-		return Skillet:AddToQueue(newCommand);
-	end
-	
-	local function IsEnabled()
-		return (Skillet and Skillet.QueueCommandIterate and Skillet.AddToQueue);
-	end
-	
-	IMRegisterCraftingAddon("Skillet", Queue, IsEnabled);
-	
-end
--- a/Inventorium.toc	Thu Jan 06 11:04:55 2011 +0100
+++ b/Inventorium.toc	Thu Jan 06 20:05:30 2011 +0100
@@ -8,46 +8,27 @@
 
 # Libraries
 #@no-lib-strip@
-embeds.xml
+Libraries.xml
 #@end-no-lib-strip@
 
 # Main
 Core.lua
 
+# Classes
+Classes\ItemData.class.lua
+Classes\ContainerItem.class.lua
+
 # Modules
-Config.lua
-Mover.lua
-Scanner.lua
-Summary.lua
-Queue.lua
-
-# Stuff
-Widgets.lua
-ItemData.class.lua
-ContainerItem.class.lua
+Modules\Config.lua
+Modules\Mover.lua
+Modules\Scanner.lua
+Modules\Summary.lua
+Modules\Queue.lua
+Modules\Widgets.lua
 
 # Data
 Data\PremadeGroups.lua
 Data\Scrolls.lua
 
-# Auction pricing addon support
-AuctionAddons\Auctionator.lua
-AuctionAddons\Auctioneer.lua
-AuctionAddons\AuctionLite.lua
-AuctionAddons\AuctionMaster.lua
-AuctionAddons\AuctionProfitMaster.lua
-AuctionAddons\Others.lua
-AuctionAddons\ZeroAuctions.lua
-
-# Item count addon support
-ItemCountAddons\Altoholic.lua
-ItemCountAddons\DataStore (current account only).lua
-ItemCountAddons\DataStore (with guilds).lua
-ItemCountAddons\DataStore (without guilds).lua
-ItemCountAddons\ItemCount.lua
-
-# Crafting addon support
-CraftingAddons\AdvancedTradeSkillWindow.lua
-CraftingAddons\Cauldron.lua
-CraftingAddons\GnomeWorks.lua
-CraftingAddons\Skillet.lua
+# Optional addon support
+Plugins\Plugins.xml
--- a/ItemCountAddons/Altoholic.lua	Thu Jan 06 11:04:55 2011 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,19 +0,0 @@
-do
-	
-	local function GetTotalCount(itemId)
-		return Altoholic:GetItemCount(itemId) or -1;
-	end
-	
-	local function IsEnabled()
-		return (Altoholic and Altoholic.GetItemCount);
-	end
-	
-	local function OnSelect()
-		local addonName = "|r|cfffed000Altoholic|r|cffff6600";
-		
-		print("|cffff6600Note: " .. addonName .. " can not provide local item data.|r");
-	end
-	
-	IMRegisterItemCountAddon("Altoholic", GetTotalCount, nil, IsEnabled, OnSelect);
-	
-end
--- a/ItemCountAddons/DataStore (current account only).lua	Thu Jan 06 11:04:55 2011 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,37 +0,0 @@
-do
-	
-	local function GetTotalCount(itemId)
-		local realm = GetRealmName();
-		
-		local totalCount = 0;
-		
-		-- Process all charracters on this account (GetCharacters defaults to THIS_ACCOUNT)
-		for characterName, character in pairs(DataStore:GetCharacters(realm, nil)) do
-			-- Get only useful info (currency / gear shouldn't contain the stuff we are interested in)
-			local bag, bank = DataStore:GetContainerItemCount(character, itemId) or 0, 0;
-			local auctionHouse = DataStore:GetAuctionHouseItemCount(character, itemId) or 0;
-			local mail = DataStore:GetMailItemCount(character, itemId) or 0;
-			
-			totalCount = totalCount + bag + bank + auctionHouse + mail;
-		end
-		
-		return totalCount or -1;
-	end
-	
-	local function GetCharacterCount(itemId)
-		local character = DataStore:GetCharacter();
-		
-		local bag, bank = DataStore:GetContainerItemCount(character, itemId) or 0, 0;
-		local auctionHouse = DataStore:GetAuctionHouseItemCount(character, itemId) or 0;
-		local mail = DataStore:GetMailItemCount(character, itemId) or 0;
-		
-		return bag, bank, auctionHouse, mail;
-	end
-	
-	local function IsEnabled()
-		return (DataStore and DataStore.GetContainerItemCount and DataStore.GetAuctionHouseItemCount and DataStore.GetMailItemCount);
-	end
-	
-	IMRegisterItemCountAddon("DataStore (current account only)", GetTotalCount, GetCharacterCount, IsEnabled);
-	
-end
--- a/ItemCountAddons/DataStore (with guilds).lua	Thu Jan 06 11:04:55 2011 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,56 +0,0 @@
-do
-	
-	local function GetTotalCount(itemId)
-		local realm = GetRealmName();
-		
-		local totalCount = 0;
-		
-		local guilds = {};
-		
-		-- Process all accounts
-		for accountName in pairs(DataStore:GetAccounts()) do
-		
-			-- Process all charracters
-			for characterName, character in pairs(DataStore:GetCharacters(realm, accountName)) do
-				-- Get only useful info (currency / gear shouldn't contain the stuff we are interested in)
-				local bag, bank = DataStore:GetContainerItemCount(character, itemId) or 0, 0;
-				local auctionHouse = DataStore:GetAuctionHouseItemCount(character, itemId) or 0;
-				local mail = DataStore:GetMailItemCount(character, itemId) or 0;
-				
-				totalCount = totalCount + bag + bank + auctionHouse + mail;
-			end
-			
-			-- Process all guilds
-			for guildName, guild in pairs(DataStore:GetGuilds(realm, accountName)) do
-				if not guilds[guildName] then
-					-- We don't want itemcounts from a single guild to be counted twice, so first to present data wins
-					
-					guilds[guildName] = true;
-					
-					local guild = DataStore:GetGuildBankItemCount(guild, itemId) or 0;
-				
-					totalCount = totalCount + guild;
-				end
-			end
-		end
-		
-		return totalCount or -1;
-	end
-	
-	local function GetCharacterCount(itemId)
-		local character = DataStore:GetCharacter();
-		
-		local bag, bank = DataStore:GetContainerItemCount(character, itemId) or 0, 0;
-		local auctionHouse = DataStore:GetAuctionHouseItemCount(character, itemId) or 0;
-		local mail = DataStore:GetMailItemCount(character, itemId) or 0;
-		
-		return bag, bank, auctionHouse, mail;
-	end
-	
-	local function IsEnabled()
-		return (DataStore and DataStore.GetContainerItemCount and DataStore.GetAuctionHouseItemCount and DataStore.GetMailItemCount);
-	end
-	
-	IMRegisterItemCountAddon("DataStore (with guilds)", GetTotalCount, GetCharacterCount, IsEnabled);
-	
-end
--- a/ItemCountAddons/DataStore (without guilds).lua	Thu Jan 06 11:04:55 2011 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,41 +0,0 @@
-do
-	
-	local function GetTotalCount(itemId)
-		local realm = GetRealmName();
-		
-		local totalCount = 0;
-		
-		-- Process all accounts
-		for accountName in pairs(DataStore:GetAccounts()) do
-		
-			-- Process all charracters
-			for characterName, character in pairs(DataStore:GetCharacters(realm, accountName)) do
-				-- Get only useful info (currency / gear shouldn't contain the stuff we are interested in)
-				local bag, bank = DataStore:GetContainerItemCount(character, itemId) or 0, 0;
-				local auctionHouse = DataStore:GetAuctionHouseItemCount(character, itemId) or 0;
-				local mail = DataStore:GetMailItemCount(character, itemId) or 0;
-				
-				totalCount = totalCount + bag + bank + auctionHouse + mail;
-			end
-		end
-		
-		return totalCount or -1;
-	end
-	
-	local function GetCharacterCount(itemId)
-		local character = DataStore:GetCharacter();
-		
-		local bag, bank = DataStore:GetContainerItemCount(character, itemId) or 0, 0;
-		local auctionHouse = DataStore:GetAuctionHouseItemCount(character, itemId) or 0;
-		local mail = DataStore:GetMailItemCount(character, itemId) or 0;
-		
-		return bag, bank, auctionHouse, mail;
-	end
-	
-	local function IsEnabled()
-		return (DataStore and DataStore.GetContainerItemCount and DataStore.GetAuctionHouseItemCount and DataStore.GetMailItemCount);
-	end
-	
-	IMRegisterItemCountAddon("DataStore (without guilds)", GetTotalCount, GetCharacterCount, IsEnabled);
-	
-end
--- a/ItemCountAddons/ItemCount.lua	Thu Jan 06 11:04:55 2011 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,24 +0,0 @@
-do
-	
-	local function GetTotalCount(itemId)
-		return ICGetItemCountTotal(itemId) or -1;
-	end
-	
-	local function GetCharacterCount(itemId)
-		local bags, bank, auctionHouse, mail = ICGetItemCountCharacter(itemId);
-		return bags or 0, bank or 0, auctionHouse or 0, mail or 0;
-	end
-	
-	local function IsEnabled()
-		return (ICGetItemCountTotal and ICGetItemCountCharacter);
-	end
-	
-	local function OnSelect()
-		local addonName = "|r|cfffed000ItemCount|r|cffff6600";
-		
-		print("|cffff6600Note: " .. addonName .. " can't record mailbox item data and thus this will not be included in any item counts.|r");
-	end
-	
-	IMRegisterItemCountAddon("ItemCount", GetTotalCount, GetCharacterCount, IsEnabled, OnSelect);
-	
-end
--- a/ItemData.class.lua	Thu Jan 06 11:04:55 2011 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,68 +0,0 @@
-local addon = select(2, ...);
-
--- Define the class
-
-addon.ItemData = {};
-addon.ItemData.__index = addon.ItemData;
-
--- Construct
-function addon.ItemData:New(itemId)
-	local self = {};
-	
-	setmetatable(self, addon.ItemData);
-	
-	local itemName, itemLink, itemRarity, _, _, _, _, _, _, itemTexture = GetItemInfo(itemId);
-	
-	-- Standard info everything needs
-	self.id = itemId;
-	self.name = itemName;
-	self.link = itemLink;
-	self.rarity = itemRarity;
-	self.icon = itemTexture;
-	
-	-- Detailed stuff
-	self.value = -3;
-	self.globalCount = -3;
-	self.localCount = -3;
-	self.set = {};
-	
-	return self;
-end
-
-function addon.ItemData:AddToGroup(groupName)
-	if self:InGroup() then
-		return false;
-	end
-	
-	if not addon.db.profile.groups[groupName].items then
-		addon.db.profile.groups[groupName].items = {};
-	end
-	
-	-- Set this item
-	addon.db.profile.groups[groupName].items[self.id] = true;
-	
-	return true;
-end
-
--- To remove an item without groupname just do RemoveFromGroup(InGroup()), although providing the group name is a nice sanity check
-function addon.ItemData:RemoveFromGroup(groupName)
-	if self:InGroup() ~= groupName then
-		return false;
-	end
-	
-	-- Unset this item
-	addon.db.profile.groups[groupName].items[self.id] = nil;
-	
-	return true;
-end
-
-function addon.ItemData:InGroup()
-	-- Go through all groups to see if this item is already somewhere
-	for groupName, values in pairs(addon.db.profile.groups) do
-		if values.items and values.items[self.id] then
-			return groupName;
-		end
-	end
-	
-	return;
-end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Libraries.xml	Thu Jan 06 20:05:30 2011 +0100
@@ -0,0 +1,21 @@
+<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">
+	<!-- Libraries -->
+	<!-- Ace 3 -->
+		<Script file="Libs\LibStub\LibStub.lua"/>
+		<Include file="Libs\CallbackHandler-1.0\CallbackHandler-1.0.xml"/>
+		<Include file="Libs\AceAddon-3.0\AceAddon-3.0.xml"/>
+		<Include file="Libs\AceDB-3.0\AceDB-3.0.xml"/>
+		<!--<Include file="Libs\AceDBOptions-3.0\AceDBOptions-3.0.xml"/>-->
+		<Include file="Libs\AceGUI-3.0\AceGUI-3.0.xml"/>
+		<!--<Include file="Libs\AceLocale-3.0\AceLocale-3.0.xml"/>-->
+		<!--<Include file="Libs\AceHook-3.0\AceHook-3.0.xml"/>-->
+		<Include file="Libs\AceConfig-3.0\AceConfig-3.0.xml"/>
+		<Include file="Libs\AceEvent-3.0\AceEvent-3.0.xml"/>
+		<!--<Include file="Libs\AceSerializer-3.0\AceSerializer-3.0.xml"/>-->
+		<Include file="Libs\AceTimer-3.0\AceTimer-3.0.xml"/>
+	<!-- end Ace 3 -->
+	<!--<Include file="Libs\LibSharedMedia-3.0\lib.xml"/>-->
+	<!--<Include file="Libs\AceGUI-3.0-SharedMediaWidgets\widget.xml"/>-->
+	<Script file="Libs\ChatHyperlinks\ChatHyperlinks.lua"/>
+</Ui>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Modules/Config.lua	Thu Jan 06 20:05:30 2011 +0100
@@ -0,0 +1,2135 @@
+local addon = select(2, ...);
+local mod = addon:NewModule("Config");
+
+local options, groupIdToName, groupIsVirtual, temp, count, includeTradeSkillItems, currentGroupType = {}, {}, {}, {}, 0, 500, "Normal";
+local AceConfigDialog, AceConfigRegistry, AceSerializer;
+
+local unknownItemName = "Unknown (#%d)";
+
+-- Private functions and tables
+
+local function SetOption(info, value, multiSelectEnabled)
+	local groupName = groupIdToName[info[2]];
+	local optionName = info[#info];
+	
+	-- Special treatment for override toggle boxes
+	if optionName:find("override") then
+		if not value and info.arg then
+			-- If this override was disabled and a saved variable name was provided, set it to nil rather than false
+			
+			value = nil;
+			
+			-- If this is an override toggler then also set the related field to nil
+			addon.db.profile.groups[groupName][info.arg] = nil;
+		elseif value and info.arg then
+			-- If this override is now enabled, we need to copy the default into this field (unless it is not nil (which is supposed to be impossible), in which case we'll use the already selected value)
+			
+			local inheritedValue = addon:GetOptionByKey(groupName, info.arg);
+			
+			addon.db.profile.groups[groupName][info.arg] = (type(inheritedValue) == "table" and CopyTable(inheritedValue)) or inheritedValue; -- copying defaults by reference would let one (unintendedly) change the defaults
+		end
+	end
+	
+	if multiSelectEnabled ~= nil then
+		-- The saved vars for a multiselect will always be an array, it may not yet exist in which case it must be created.
+		if not addon.db.profile.groups[groupName][optionName] then
+			addon.db.profile.groups[groupName][optionName] = {};
+		end
+		
+		addon.db.profile.groups[groupName][optionName][value] = multiSelectEnabled or nil;
+	else
+		addon.db.profile.groups[groupName][optionName] = value;
+	end
+end
+
+local function GetOption(info)
+	local groupName = groupIdToName[info[2]];
+	local optionName = info[#info];
+	
+	local noDefault;
+	
+	if optionName:find("override") then
+		noDefault = true;
+	end
+	
+	return addon:GetOptionByKey(groupName, optionName, noDefault);
+end
+
+local function GetMultiOption(info, value)
+	local groupName = groupIdToName[info[2]];
+	local optionName = info[#info];
+	
+	local multiSelectValue = addon:GetOptionByKey(groupName, optionName);
+	
+	return multiSelectValue and multiSelectValue[value];
+end
+
+local function GetDisabled(info)
+	local groupName = groupIdToName[info[2]];
+	local optionName = info[#info];
+	
+	if optionName:find("override") or not info.arg then
+		return false;
+	end
+	
+	return (addon:GetOptionByKey(groupName, info.arg, true) == nil);
+end
+
+local function ValidateGroupName(_, value)
+	value = string.lower(string.trim(value or ""));
+	
+	for name, _ in pairs(addon.db.profile.groups) do
+		if string.lower(name) == value then
+			return ("A group named \"%s\" already exists."):format(name);
+		end
+	end
+	
+	return true;
+end
+
+local tblAddItemTemplate = {
+	order = 0,
+	type = "input",
+	name = function(info)
+		local itemName, _, itemRarity = GetItemInfo(info[#info]);
+		return tostring( 7 - (itemRarity or 0) ) .. (itemName or "");
+	end,
+	get = function(info)
+		return tostring(info[#info]); -- Ace is going to be anal about this if it's a numeric value, so we transmute it into a string here then back to a number on the other side
+	end,
+	set = function(groupId, itemData)
+    	-- This is NOT a real "set", we pass the widget reference to this function which contains similar, but not the same, info.
+    	
+    	if itemData then
+			local groupName = groupIdToName[groupId];
+			
+			if not itemData:AddToGroup(groupName) then
+				print("|cffff0000Couldn't add the item with itemId (" .. itemData.id .. ") because it is already in a group.|r");
+			end
+			
+			if AceConfigRegistry then
+				-- Now rebuild the list
+				AceConfigRegistry:NotifyChange("InventoriumOptions");
+			end
+    	end
+	end,
+	width = "double",
+	dialogControl = "ConfigItemLinkButton",
+};
+
+local tblRemoveItemTemplate = {
+	order = 0,
+	type = "input",
+	name = function(info)
+		local itemName, _, itemRarity = GetItemInfo(info[#info]);
+		return tostring( 7 - (itemRarity or 0) ) .. (itemName or "");
+	end,
+	get = function(info)
+		return tostring(info[#info]); -- Ace is going to be anal about this if it's a numeric value, so we transmute it into a string here then back to a number on the other side
+	end,
+	set = function(groupId, itemData)
+    	-- This is NOT a real "set", we pass the widget reference to this function which contains similar, but not the same, info.
+    	
+    	if itemData then
+	    	local groupName = groupIdToName[groupId];
+	    	
+	    	itemData:RemoveFromGroup(groupName);
+			
+			if AceConfigRegistry then
+				-- Now rebuild the list
+				AceConfigRegistry:NotifyChange("InventoriumOptions");
+			end
+    	end
+	end,
+	width = "double",
+	dialogControl = "ConfigItemLinkButton",
+};
+
+local function UpdateAddItemList(info)
+	local groupName = groupIdToName[info[2]];
+	
+	if not addon.db.profile.groups[groupName].items then
+		addon.db.profile.groups[groupName].items = {};
+	end
+	
+	-- Merge all items from all groups together
+	local items = {};
+	for groupName, values in pairs(addon.db.profile.groups) do
+		if values.items then
+			for itemId, _ in pairs(values.items) do
+				items[itemId] = true;
+			end
+		end
+	end
+	
+	local ref = options.args.groups.args[info[2]].args.add.args.list.args;
+	
+	-- Remaking the list, so out with the old, in with the new
+	table.wipe(ref);
+	
+	-- Parse bags and show these
+	for bagID = 4, 0, -1 do
+		for slot = 1, GetContainerNumSlots(bagID) do
+			local itemId = addon:GetItemId(GetContainerItemLink(bagID, slot));
+			
+			if itemId then
+				if not items[itemId] then
+					-- If this item isn't used in any group yet
+					ref[itemId] = tblAddItemTemplate;
+				else
+					-- It's already used in a group, don't show it
+					ref[itemId] = nil;
+				end
+			end
+		end
+	end
+	
+	if includeTradeSkillItems ~= 500 then
+		-- Include tradeskill items
+		
+		-- Go through all trade skills for the profession
+		for i = 1, GetNumTradeSkills() do
+			-- Try to retrieve the itemlink, this will be nil if current item is a group instead
+			local itemLink = GetTradeSkillItemLink(i);
+			
+			if itemLink then
+				local itemId = addon:GetItemId(itemLink);
+				if not itemId then
+					-- If this isn't an item, it can only be an enchant instead
+					itemId = tonumber(itemLink:match("|Henchant:([-0-9]+)|h"));
+					
+					itemId = addon.scrollIds[itemId]; -- change enchantIds into scrollIds
+				end
+				
+				if itemId then
+					local itemLevel = select(4, GetItemInfo(itemId)) or 0;
+					
+					if includeTradeSkillItems == 0 or itemLevel >= includeTradeSkillItems then
+						if not items[itemId] then
+							-- If this item isn't used in any group yet
+							ref[itemId] = tblAddItemTemplate;
+						else
+							-- It's already used in a group, don't show it
+							ref[itemId] = nil;
+						end
+					end
+				else
+					addon:Debug("|cffff0000ERROR|r: Couldn't find proper item id for " .. itemLink);
+				end
+			end
+		end
+	end
+end
+
+local function UpdateRemoveItemList(info)
+	local groupName = groupIdToName[info[2]];
+	
+	if not addon.db.profile.groups[groupName].items then
+		addon.db.profile.groups[groupName].items = {};
+	end
+	
+	local ref = options.args.groups.args[info[2]].args.remove.args.list.args;
+	
+	-- Unset all
+	for itemId, _ in pairs(ref) do
+		ref[itemId] = nil;
+	end
+	
+	-- Parse items in group and show these
+	for itemId, _ in pairs(addon.db.profile.groups[groupName].items) do
+		ref[itemId] = tblRemoveItemTemplate;
+	end
+end
+
+-- Default group
+local defaultGroup = {
+	order = 0,
+	type = "group",
+	childGroups = "tab",
+	name = function(info)
+		local groupId = info[#info];
+		if groupIsVirtual[groupId] then
+			return ("%s  |cfffed000Virtual|r"):format(groupIdToName[groupId]);
+		else
+			return groupIdToName[groupId];
+		end
+	end,
+	desc = function(info)
+		local groupId = info[#info];
+		if groupIsVirtual[groupId] then
+			return "This is a virtual group, you can use it to override the defaults for other groups.";
+		end
+	end,
+	args = {
+		general = {
+			order = 10,
+			type = "group",
+			name = "General",
+			desc = "Change the general settings for just this group.",
+			args = {
+				general = {
+					order = 0,
+					type = "group",
+					inline = true,
+					name = "General",
+					set = SetOption,
+					get = GetOption,
+					disabled = GetDisabled,
+					args = {
+						description = {
+							order = 0,
+							type = "description",
+							name = function(info)
+								local groupName = groupIdToName[info[2]];
+								
+								local t = "Here you can set general settings for the currently selected group. If you do not wish to override a setting, the default setting specified in the general group will be used.\n\n";
+								
+								local currentAddon, selectedAddonName = addon:GetItemCountAddon(groupName);
+								local preferedAddon = addon:GetOptionByKey(groupName, "itemCountAddon");
+								
+								if currentAddon then
+									--GetCharacterCount
+									--addon.supportedAddons.itemCount[selectedExternalAddon]
+									t = t .. "Currently using |cfffed000" .. selectedAddonName .. "|r as your item count addon. This addon is " .. ((currentAddon.IsEnabled() and "|cff00ff00enabled|r") or "|cffff0000disabled|r") .. ".";
+									
+									if currentAddon.GetTotalCount and currentAddon.GetCharacterCount then
+										t = t .. " This addon supports |cfffed000both total as local|r item counts.";
+									elseif currentAddon.GetTotalCount then
+										t = t .. " This addon supports |cfffed000only total|r item counts.";
+									elseif currentAddon.GetCharacterCount then
+										t = t .. " This addon supports |cfffed000only local|r item counts.";
+									end
+									
+									if preferedAddon ~= selectedAddonName then
+										t = t .. "\n\n|cffff0000You have selected |cfffed000" .. preferedAddon .. "|r|cffff0000 as your item count addon, but this appears to be disabled and thus a random alternative was selected.|r";
+									end
+								end
+								
+								return t;
+							end,
+						},
+						header = {
+							order = 5,
+							type = "header",
+							name = "",
+						},
+						overrideAuctionPricingAddon = {
+							order = 9,
+							type = "toggle",
+							name = "Override pricing addon",
+							desc = "Allows you to override the pricing addon setting for this group.",
+							arg = "auctionPricingAddon",
+						},
+						auctionPricingAddon = {
+							order = 10,
+							type = "select",
+							name = "Prefered pricing addon",
+							desc = "Select the addon you prefer data for this group to be retrieved from. A random supported addon will be used if the selected addon can not be found.",
+							values = function()
+								local temp = {};
+								for name, value in pairs(addon.supportedAddons.auctionPricing) do
+									temp[name] = name;
+								end
+								
+								return temp;
+							end,
+							set = function(info, value)
+								local groupName = groupIdToName[info[2]];
+								local optionName = info[#info];
+								
+								addon.db.profile.groups[groupName][optionName] = value ~= "" and value;
+								
+								if addon.supportedAddons.auctionPricing[value].OnSelect then
+									addon.supportedAddons.auctionPricing[value].OnSelect();
+								end
+							end,
+							arg = "overrideAuctionPricingAddon",
+						},
+						overrideItemCountAddon = {
+							order = 19,
+							type = "toggle",
+							name = "Override item count addon",
+							desc = "Allows you to override the item count addon setting for this group.",
+							arg = "itemCountAddon",
+						},
+						itemCountAddon = {
+							order = 20,
+							type = "select",
+							name = "Prefered item count addon",
+							desc = "Select the addon you prefer data for this group to be retrieved from. A random supported addon will be used if the selected addon can not be found.",
+							values = function()
+								local temp = {};
+								for name, value in pairs(addon.supportedAddons.itemCount) do
+									temp[name] = name;
+								end
+								
+								return temp;
+							end,
+							set = function(info, value)
+								local groupName = groupIdToName[info[2]];
+								local optionName = info[#info];
+								
+								addon.db.profile.groups[groupName][optionName] = value ~= "" and value;
+								
+								if addon.supportedAddons.itemCount[value].OnSelect then
+									addon.supportedAddons.itemCount[value].OnSelect();
+								end
+							end,
+							arg = "overrideItemCountAddon",
+						},
+						overrideCraftingAddon = {
+							order = 29,
+							type = "toggle",
+							name = "Override crafting addon",
+							desc = "Allows you to override the crafting addon setting for this group.",
+							arg = "craftingAddon",
+						},
+						craftingAddon = {
+							order = 30,
+							type = "select",
+							name = "Prefered crafting addon",
+							desc = "Select the addon you prefer data from this group to be queued into. A random supported addon will be used if the selected addon can not be found.",
+							values = function()
+								local temp = {};
+								for name, value in pairs(addon.supportedAddons.crafting) do
+									temp[name] = name;
+								end
+								
+								return temp;
+							end,
+							set = function(info, value)
+								local groupName = groupIdToName[info[2]];
+								local optionName = info[#info];
+								
+								addon.db.profile.groups[groupName][optionName] = value ~= "" and value;
+								
+								if addon.supportedAddons.crafting[value].OnSelect then
+									addon.supportedAddons.crafting[value].OnSelect();
+								end
+							end,
+							arg = "overrideCraftingAddon",
+						},
+						overrideLocalItemData = {
+							order = 39,
+							type = "toggle",
+							name = "Override local item data",
+							desc = "Allows you to override the local item data setting for this group.",
+							arg = "localItemData",
+						},
+						localItemData = {
+							order = 40,
+							type = "multiselect",
+							name = "Include in local item data",
+							desc = "Select which data should be included in the local item data.",
+							values = {
+								["Bag"] = "Bag",
+								["Bank"] = "Bank",
+								["Auction House"] = "Auction House",
+								["Mailbox"] = "Mailbox",
+							},
+							get = GetMultiOption,
+							dialogControl = "Dropdown", -- this is not standard, normal multiselect control gives us a list of all chars with toggle-boxes. UGLY! We want a multiselect-box instead.
+							arg = "overrideLocalItemData",
+						},
+						virtualGroup = {
+							order = 50,
+							type = "select",
+							name = "Use virtual group settings", 
+							desc = "Use the settings from a virtual group before using the general defaults.\n\n|cffff9933This is an advanced option, you will probably not need it unless you manage a lot of groups.|r\n\n|cfffed000Off|r: Use the overridden options in this group and then the defaults.\n\n|cfffed000On|r: Use the overridden options in this group, then the ones in the selected virtual group and then the defaults.",
+							values = function(info)
+								local groupName = groupIdToName[info[2]];
+								
+								local temp = {};
+								
+								temp[""] = "";
+								for name, values in pairs(addon.db.profile.groups) do
+									if values.isVirtual and name ~= groupName then
+										temp[name] = name;
+									end
+								end
+								
+								return temp;
+							end,
+							set = function(info, value)
+								local groupName = groupIdToName[info[2]];
+								local optionName = info[#info];
+								
+								addon.db.profile.groups[groupName][optionName] = value ~= "" and value;
+							end,
+							disabled = false,
+						},
+					},
+				},
+				minimumStock = {
+					order = 10,
+					type = "group",
+					inline = true,
+					name = "Minimum stock",
+					set = SetOption,
+					get = GetOption,
+					disabled = GetDisabled,
+					args = {
+						description = {
+							order = 0,
+							type = "description",
+							name = "Here you can specify the minimum amount of items you wish to keep in stock and related settings for the currently selected group. Please note the values entered here do not affect the queued quantities, you must set settings for that in the area below.",
+						},
+						header = {
+							order = 5,
+							type = "header",
+							name = "",
+						},
+						
+						overrideMinLocalStock = {
+							order = 10,
+							type = "toggle",
+							name = "Override min local stock",
+							desc = "Allows you to override the minimum local stock setting for this group.",
+							arg = "minLocalStock",
+						},
+						minLocalStock = {
+							order = 11,
+							type = "range",
+							min = 0,
+							max = 100000,
+							softMax = 100,
+							step = 1,
+							name = "Minimum local stock",
+							desc = "You can manually enter a value between 100 and 100.000 in the text box below if the provided range is insufficient.",
+							arg = "overrideMinLocalStock",
+						},
+						overrideAlertBelowLocalMinimum = {
+							order = 15,
+							type = "toggle",
+							name = "Override local minimum alert",
+							desc = "Allows you to override wether an alert should be shown when an item in this group gets below the local minimum stock threshold.",
+							arg = "alertBelowLocalMinimum",
+						},
+						alertBelowLocalMinimum = {
+							order = 16,
+							type = "toggle",
+							name = "Alert when below local minimum (NYI)",
+							desc = "Show an alert when an item in this group gets below the local minimum stock threshold.",
+							arg = "overrideAlertBelowLocalMinimum",
+						},
+						overrideAutoRefill = {
+							order = 17,
+							type = "toggle",
+							name = "Override auto refill",
+							desc = "Allows you to override wether you want to automatically refill items when below the minimum local stock.",
+							arg = "autoRefill",
+						},
+						autoRefill = {
+							order = 18,
+							type = "toggle",
+							name = "Auto refill from (guild) bank",
+							desc = "Automatically refill items from the bank (unless this is included in the local count) or guild bank when below the minimum local stock.",
+							arg = "overrideAutoRefill",
+						},
+						
+						overrideMinGlobalStock = {
+							order = 20,
+							type = "toggle",
+							name = "Override min global stock",
+							desc = "Allows you to override the minimum global stock setting for this group.",
+							arg = "minGlobalStock",
+						},
+						minGlobalStock = {
+							order = 21,
+							type = "range",
+							min = 0,
+							max = 100000,
+							softMax = 100,
+							step = 1,
+							name = "Minimum global stock",
+							desc = "You can manually enter a value between 100 and 100.000 in the text box below if the provided range is insufficient.",
+							arg = "overrideMinGlobalStock",
+						},
+						overrideAlertBelowGlobalMinimum = {
+							order = 25,
+							type = "toggle",
+							name = "Override global minimum alert",
+							desc = "Allows you to override wether an alert should be shown when an item in this group gets below the global minimum stock threshold.",
+							arg = "alertBelowGlobalMinimum",
+						},
+						alertBelowGlobalMinimum = {
+							order = 26,
+							type = "toggle",
+							name = "Alert when below global minimum (NYI)",
+							desc = "Show an alert when an item in this group gets below the global minimum stock threshold.",
+							arg = "overrideAlertBelowGlobalMinimum",
+						},
+						
+						overrideSummaryThresholdShow = {
+							order = 34,
+							type = "toggle",
+							name = "Override summary showing",
+							desc = "Allows you to override when this group should appear in the summary.",
+							arg = "summaryThresholdShow",
+						},
+						summaryThresholdShow = {
+							order = 35,
+							type = "range",
+							min = 0,
+							max = 10,
+							softMax = 100,
+							step = 0.05,
+							isPercent = true,
+							name = "Show in summary when below",
+							desc = "Show items in the summary when below the specified percentage of the minimum stock.\n\nYou can manually enter a value between 1.000% and 10.000% in the edit box if the provided range is insufficient.",
+							arg = "overrideSummaryThresholdShow",
+						},
+						overrideTrackAtCharacters = {
+							order = 39,
+							type = "toggle",
+							name = "Override track at",
+							desc = "Allows you to override at which characters items in this group should appear in the summary and generate alerts.",
+							arg = "trackAtCharacters",
+						},
+						trackAtCharacters = {
+							order = 40,
+							type = "multiselect",
+							name = "Track at",
+							desc = "Select at which characters this should appear in the summary and generate alerts.",
+							values = function()
+								local temp = {};
+								for charName in pairs(addon.db.factionrealm.characters) do
+									temp[charName] = charName;
+								end
+								
+								return temp;
+							end,
+							get = GetMultiOption,
+							dialogControl = "Dropdown", -- this is not standard, normal multiselect control gives us a list of all chars with toggle-boxes. UGLY! We want a multiselect-box instead.
+							arg = "overrideTrackAtCharacters",
+						},
+					},
+				},
+				refill = {
+					order = 20,
+					type = "group",
+					inline = true,
+					name = "Replenishing stock",
+					set = SetOption,
+					get = GetOption,
+					disabled = GetDisabled,
+					args = {
+						description = {
+							order = 0,
+							type = "description",
+							name = function(info)
+								local groupName = groupIdToName[info[2]];
+								local r = "Here you can specify the amount of items to which you wish to restock when you are collecting new items for the currently selected group. This may be higher than the minimum stock.\n\n";
+								
+								r = r .. "When restocking the target amount is |cfffed000" .. addon:GetOptionByKey(groupName, "restockTarget") .. "|r of every item. Not queueing craftable items when only missing |cfffed000" .. floor( addon:GetOptionByKey(groupName, "minCraftingQueue") * addon:GetOptionByKey(groupName, "restockTarget") ) .. "|r (|cfffed000" .. ( addon:GetOptionByKey(groupName, "minCraftingQueue") * 100 ) .. "%|r) of the restock target and making |cfffed000" .. floor( ( addon:GetOptionByKey(groupName, "bonusQueue") * addon:GetOptionByKey(groupName, "restockTarget") ) + .5 ) .. "|r (|cfffed000" .. ( addon:GetOptionByKey(groupName, "bonusQueue") * 100 ) .. "%|r) extra items when you completely ran out. ";
+								
+								if addon:GetOptionByKey(groupName, "priceThreshold") == 0 then
+									r = r .. "Queueing items at |cfffed000any|r auction value.";
+								else
+									r = r .. "Queueing items worth |cfffed000" .. addon:ReadableMoney(addon:GetOptionByKey(groupName, "priceThreshold")) .. "|r or more.";
+								end
+								
+								return r;
+							end,
+						},
+						header = {
+							order = 5,
+							type = "header",
+							name = "",
+						},
+						overrideRestockTarget = {
+							order = 9,
+							type = "toggle",
+							name = "Override restock target",
+							desc = "Allows you to override the restock target setting for this group.",
+							arg = "restockTarget",
+						},
+						restockTarget = {
+							order = 10,
+							type = "range",
+							min = 0,
+							max = 100000,
+							softMax = 100,
+							step = 1,
+							name = "Restock target",
+							desc = "You can manually enter a value between 100 and 100.000 in the edit box if the provided range is insufficient.",
+							arg = "overrideRestockTarget",
+						},
+						overrideMinCraftingQueue = {
+							order = 19,
+							type = "toggle",
+							name = "Override min queue",
+							desc = "Allows you to override the minimum craftable items queue setting for this group.",
+							arg = "minCraftingQueue",
+						},
+						minCraftingQueue = {
+							order = 20,
+							type = "range",
+							min = 0,
+							max = 1,
+							step = 0.01,
+							isPercent = true,
+							name = "Don't queue if I only miss",
+							desc = "Don't add a craftable item to the queue if I only miss this much or less of the restock target.\n\nExample: if your restock target is set to 60 and this is set to 5%, an item won't be queued unless you are missing more than 3 of it.",
+							arg = "overrideMinCraftingQueue",
+						},
+						overrideBonusQueue = {
+							order = 29,
+							type = "toggle",
+							name = "Override bonus queue",
+							desc = "Allows you to override the bonus craftable items queue setting for this group.",
+							arg = "bonusQueue",
+						},
+						bonusQueue = {
+							order = 30,
+							type = "range",
+							min = 0,
+							max = 10, -- 1000%
+							step = 0.01, -- 1%
+							isPercent = true,
+							name = "Bonus queue",
+							desc = "Get additional items when there are none left.\n\nExample: if your restock target is set to 60 and this is set to 10%, you will get 66 items instead of just 60 if you end up with none left while queueing.",
+							arg = "overrideBonusQueue",
+						},
+						overridePriceThreshold = {
+							order = 39,
+							type = "toggle",
+							name = "Override price threshold",
+							desc = "Allows you to override the price threshold setting for this group.",
+							arg = "priceThreshold",
+						},
+						priceThreshold = {
+							order = 40,
+							type = "input",
+							name = "Price threshold",
+							desc = "Only queue craftable items when they are worth at least this much according to your auction house addon.\n\nSet to 0 to ignore auction prices.",
+							validate = function(info, value) return addon:ValidateReadableMoney(info, value); end,
+							get = function(i) return addon:ReadableMoney(GetOption(i)); end,
+							set = function(i, v) SetOption(i, addon:ReadableMoneyToCopper(v)); end,
+							arg = "overridePriceThreshold",
+						},
+						overrideSummaryHidePriceThreshold = {
+							order = 49,
+							type = "toggle",
+							name = "Override summary showing",
+							desc = "Allows you to override if items in this group should be hidden from the summary while their value is below the price threshold.",
+							arg = "summaryHidePriceThreshold",
+						},
+						summaryHidePriceThreshold = {
+							order = 50,
+							type = "toggle",
+							name = "Hide when below threshold",
+							desc = "Hide items from the summary when their value is below the set price threshold.",
+							arg = "overrideSummaryHidePriceThreshold",
+						},
+						overrideAlwaysGetAuctionValue = {
+							order = 59,
+							type = "toggle",
+							name = "Override auction value showing",
+							desc = "Allows you to override if the auction value of items in this group should be cached and displayed even when the price threshold is set to 0|cffeda55fc|r.",
+							arg = "alwaysGetAuctionValue",
+						},
+						alwaysGetAuctionValue = {
+							order = 60,
+							type = "toggle",
+							name = "Always show auction value",
+							desc = "Always cache and show the auction value of items in this group, even if the price threshold is set to 0|cffeda55fc|r.",
+							arg = "overrideAlwaysGetAuctionValue",
+						},
+					},
+				},
+			},
+		},
+		group = {
+			order = 20,
+			type = "group",
+			name = "Management",
+			desc = "Rename, delete, duplicate or export this group.",
+			args = {
+				actions = {
+					order = 10,
+					type = "group",
+					name = "Actions",
+					inline = true,
+					args = {
+						rename = {
+							order = 10,
+							type = "input",
+							name = "Rename group - New name",
+							desc = "Change the name of this group to something else. You can also use item links here as you wish.",
+							validate = ValidateGroupName,
+							set = function(info, value)
+								local oldGroupName = groupIdToName[info[2]];
+								
+								addon.db.profile.groups[value] = CopyTable(addon.db.profile.groups[oldGroupName]);
+								addon.db.profile.groups[oldGroupName] = nil;
+								
+								groupIdToName[info[2]] = value;
+								groupIdToName[value] = true;
+								groupIdToName[oldGroupName] = nil;
+								
+								mod:FillGroupOptions();
+							end,
+							get = function(info)
+								return groupIdToName[info[2]];
+							end,
+						},
+						duplicate = {
+							order = 20,
+							type = "input",
+							name = "Duplicate group - New name",
+							desc = "Duplicate this group. You can also use item links here as you wish.\n\nAll item data will be erased.",
+							validate = ValidateGroupName,
+							set = function(info, value)
+								local oldGroupName = groupIdToName[info[2]];
+								
+								addon.db.profile.groups[value] = CopyTable(addon.db.profile.groups[oldGroupName]);
+								
+								-- Reset item data (duplicate items me no want)
+								addon.db.profile.groups[value].items = nil;
+								
+								mod:FillGroupOptions();
+							end,
+							get = false,
+						},
+						delete = {
+							order = 30,
+							type = "execute",
+							name = "Delete group",
+							desc = "Delete the currently selected group.",
+							confirm = true,
+							confirmText = "Are you sure you wish to |cffff0000DELETE|r this group? This action is not reversable!",
+							func = function(info)
+								local groupName = groupIdToName[info[2]];
+								
+								addon.db.profile.groups[groupName] = nil;
+								
+								mod:FillGroupOptions();
+							end,
+						},
+					},
+				},
+				export = {
+					order = 40,
+					type = "group",
+					name = "Export",
+					inline = true,
+					args = {
+						input = {
+							order = 10,
+							type = "input",
+							multiline = true,
+							name = "Group data",
+							width = "full",
+							desc = "Export the group data for the currently selected group. Press CTRL-A to select all and CTRL-C to copy the text.",
+							set = false,
+							get = function(info)
+								local groupName = groupIdToName[info[2]];
+								
+								-- We want to include the group name, so we copy the table then set another value
+								local temp = CopyTable(addon.db.profile.groups[groupName]);
+								temp.name = groupName;
+								temp.trackAtCharacters = nil;
+								temp.overrideTrackAtCharacters = nil;
+								
+								if not AceSerializer then
+									AceSerializer = LibStub("AceSerializer-3.0");
+								end
+								
+								return AceSerializer:Serialize(temp);
+							end,
+						},
+					},
+				},
+			},
+		},
+		add = {
+			order = 30,
+			type = "group",
+			name = "Add items",
+			desc = "Add new items to this group.",
+			hidden = function(info) return groupIsVirtual[info[2]]; end,
+			args = {
+				singleAdd = {
+					order = 10,
+					type = "group",
+					inline = true,
+					name = "Add items",
+					args = {
+						help = {
+							order = 10,
+							type = "description",
+							name = "You can add a single item to this group at a time by pasting the item-id or an item-link in the field to the left or you can also import multiple items at once by pasting exported item data in the field to the right. Scroll further down to add items based on your inventory contents.",
+						},
+						itemLink = {
+							order = 20,
+							type = "input",
+							name = "Single item add (item-link or item-id)",
+							desc = "Shift-click an item-link or enter an item-id to add the related item to this group. You can only add one item link or item id at a time.",
+							validate = function(info, value)
+								-- If the value is empty we'll allow passing to clear the carret
+								if value == "" then return true; end
+								
+								local groupName = groupIdToName[info[2]];
+								
+								local itemId = (addon:GetItemId(string.trim(value)) or tonumber(string.trim(value)));
+								
+								local itemData = addon.ItemData:New(itemId);
+								
+								if not itemId then
+									return "This is not a valid item link or id.";
+								elseif itemData:InGroup() then
+									return ("This item is already in the group |cfffed000%s|r."):format(itemData:InGroup());
+								end
+								
+								return true;
+							end,
+							set = function(info, value)
+								if value and value ~= "" then
+									local groupName = groupIdToName[info[2]];
+									
+									local itemId = (addon:GetItemId(string.trim(value)) or tonumber(string.trim(value)));
+									
+									local itemData = addon.ItemData:New(itemId);
+									
+									if itemData:AddToGroup(groupName) then
+										addon.Print(("Added %s to the selected group."):format( (itemData.link or unknownItemName:format(itemId)) ), addon.Colors.Green);
+										
+										if AceConfigRegistry then
+											-- Now rebuild the list
+											AceConfigRegistry:NotifyChange("InventoriumOptions");
+										end
+									else
+										addon.Print(("%s is already in the group |cfffed000%s|r."):format( (itemData.link or unknownItemName:format(itemId)), itemData:InGroup() ), addon.Colors.Red);
+									end
+								end
+							end,
+							get = false,
+						},
+						importItemData = {
+							order = 30,
+							type = "input",
+							name = "Import item data",
+							desc = "Import item data from an exported item data-string. Any items already grouped will be skipped.",
+							set = function(info, value)
+								local groupName = groupIdToName[info[2]];
+								
+								local allItemIds = { string.split(";", value or "") };
+								
+								for _, value in pairs(allItemIds) do
+									local itemId = tonumber(value);
+									
+									if itemId then
+										local itemData = addon.ItemData:New(itemId);
+										
+										if itemData:InGroup() then
+											addon.Print(("Skipping %s (#%d) as it is already in the group |cfffed000%s|r."):format(itemData.link or "Unknown", itemId, itemData:InGroup()), addon.Colors.Red);
+										elseif itemData:AddToGroup(groupName) then
+											addon.Print(("Added %s to the group |cfffed000%s|r."):format(itemData.link or unknownItemName:format(itemId), groupName), addon.Colors.Green);
+										end
+									else
+										addon.Print(("\"%s\" is not a number."):format(value), addon.Colors.Red);
+									end
+								end
+								
+								if AceConfigRegistry then
+									-- Now rebuild the list
+									AceConfigRegistry:NotifyChange("InventoriumOptions");
+								end
+							end,
+							get = false,
+						},
+						importPremadeData = {
+							order = 40,
+							type = "select",
+							name = "Import premade data",
+							desc = "Import item data from a premade item-group. Any items already grouped will be skipped.",
+							values = function()
+								local temp = {};
+								for key, group in pairs(addon.defaultGroups) do
+									temp[key] = key;
+								end
+								
+								return temp;
+							end,
+							set = function(info, value)
+								local groupName = groupIdToName[info[2]];
+								
+								print(("Importing items from the premade group \"|cfffed000%s|r\"."):format(value));
+								
+								-- Remember we imported this group and it's version so if it is ever changed, people can be notified
+								if not addon.db.profile.groups[groupName].premadeGroups then
+									addon.db.profile.groups[groupName].premadeGroups = {};
+								end
+								addon.db.profile.groups[groupName].premadeGroups[value] = addon.defaultGroups[value].version;
+								
+								for itemId, version in pairs(addon.defaultGroups[value].items) do
+									if version > 0 then
+										-- Sanity check
+										itemId = itemId and tonumber(itemId);
+										
+										local itemData = addon.ItemData:New(itemId);
+										
+										if not itemId then
+											addon.Print(("\"|cfffed000%s|r\" is not a number."):format(value), addon.Colors.Red);
+										elseif itemData:InGroup() then
+											addon.Print(("Skipping |cfffed000%s|r as it is already in the group |cfffed000%s|r."):format( (itemData.link or unknownItemName:format(itemId)), itemData:InGroup() ), addon.Colors.Red);
+										elseif itemData:AddToGroup(groupName) then
+											addon.Print(("Added %s to the group |cfffed000%s|r."):format( (itemData.link or unknownItemName:format(itemId)), groupName ), addon.Colors.Green);
+										end
+									end
+								end
+								
+								if AceConfigRegistry then
+									-- Now rebuild the list
+									AceConfigRegistry:NotifyChange("InventoriumOptions");
+								end
+							end,
+							get = false,
+						},
+					},
+				},
+				massAdd = {
+					order = 20,
+					type = "group",
+					inline = true,
+					name = "Mass add",
+					args = {
+						help = {
+							order = 10,
+							type = "description",
+							name = "Click the items you wish to add to this group or add multiple of these items at once by providing a name filter in the field below.",
+						},
+						massAdd = {
+							order = 20,
+							type = "input",
+							name = "Add all items matching...",
+							desc = "Add every item in your inventory matching the name entered in this field. If you enter \"Glyph\" as a filter, any items in your inventory containing this in their name will be added to this group.",
+							set = function(info, value)
+						    	local groupName = groupIdToName[info[2]];
+						    	
+								if not value then return; end
+								
+								value = value:lower();
+								
+								local ref = options.args.groups.args[info[2]].args.add.args.list.args;
+								
+								for itemId, test in pairs(ref) do
+									if test then
+										local itemData = addon.ItemData:New(itemId);
+										
+										if itemData.name:lower():find(value) then
+									    	if itemData:AddToGroup(groupName) then
+												addon.Print(("Added %s to the selected group."):format( (itemData.link or unknownItemName:format(itemId)) ), addon.Colors.Green);
+									    	else
+									    		addon.Print(("Couldn't add %s because it is already in a group."):format(itemData.link or unknownItemName:format(itemId)), addon.Colors.Red);
+									    	end
+										end
+									end
+								end
+								
+								if AceConfigRegistry then
+									-- Now rebuild the list
+									AceConfigRegistry:NotifyChange("InventoriumOptions");
+								end
+							end,
+							get = false,
+						},
+						minItemLevel = {
+							order = 40,
+							type = "select",
+							values = function()
+								local temp = {};
+								
+								temp[0] = "Include everything";
+								
+								local itemLevelTemplate = "Itemlevel >= %d";
+								
+								for i = 1, 49 do
+									temp[( i * 10 )] = itemLevelTemplate:format(( i * 10 ));
+								end
+								
+								temp[500] = "Include nothing";
+								
+								return temp;
+							end,
+							name = "Include tradeskill items",
+							desc = "Include all items above this item level from the currently opened tradeskill window in the below item list.\n\nSetting this very low this might considerably slow down this config window.\n\nSet to 500 to disable showing of items completely.",
+							set = function(i, v) includeTradeSkillItems = v; end,
+							get = function() return includeTradeSkillItems; end,
+							disabled = function()
+								if GetTradeSkillLine() == "UNKNOWN" then
+									includeTradeSkillItems = 500;
+									return true; -- disabled
+								else
+									return false;
+								end
+							end,
+						},
+					},
+				},
+				list = {
+					order = 30,
+					type = "group",
+					inline = true,
+					name = "Item list",
+					hidden = UpdateAddItemList,
+					args = {
+					
+					},
+				},
+			},
+		},
+		remove = {
+			order = 40,
+			type = "group",
+			name = "Current items",
+			desc = "View, export or remove items from this group.",
+			hidden = function(info) return groupIsVirtual[info[2]]; end,
+			args = {
+				help = {
+					order = 10,
+					type = "group",
+					inline = true,
+					name = "Help",
+					hidden = false,
+					args = {
+						help = {
+							order = 10,
+							type = "description",
+							name = "Click the items you wish to remove from this group.",
+						},
+						massRemove = {
+							order = 20,
+							type = "input",
+							name = "Remove all items matching...",
+							desc = "Remove every item in this group matching the name entered in this field. If you enter \"Glyph\" as a filter, any items in this group containing this in their name will be removed from this group.",
+							set = function(info, value)
+						    	local groupName = groupIdToName[info[2]];
+						    	
+								if not value then return; end
+								
+								value = value:lower();
+								
+								local ref = options.args.groups.args[info[2]].args.remove.args.list.args;
+								
+								for itemId, test in pairs(ref) do
+									if test then
+										local itemData = addon.ItemData:New(itemId);
+										
+										if itemData.name:lower():find(value) then
+											itemData:RemoveFromGroup(groupName);
+											
+											addon.Print(("Removed %s from the selected group."):format( (itemData.link or unknownItemName:format(itemId)) ), addon.Colors.Red);
+										end
+									end
+								end
+								
+								if AceConfigRegistry then
+									-- Now rebuild the list
+									AceConfigRegistry:NotifyChange("InventoriumOptions");
+								end
+							end,
+							get = false,
+						},
+						premadeGroups = {
+							order = 30,
+							type = "select",
+							name = "Imported premade groups",
+							desc = "This is a list of all premade groups that were imported into this group. You will be notified when any of these premade groups have changed and you will be able to import these changes.\n\nSelect a group to stop reminding you of changes to the premade group (the item list will be unaffected). Doing so will require you to manually update this when new items are added to the game.",
+							values = function(info)
+						    	local groupName = groupIdToName[info[2]];
+						    	
+						    	local temp = {};
+								temp[""] = "";
+						    	if addon.db.profile.groups[groupName].premadeGroups then
+							    	for name, version in pairs(addon.db.profile.groups[groupName].premadeGroups) do
+							    		temp[name] = name;
+							    	end
+						    	end
+						    	
+						    	return temp;
+							end,
+							set = function(info, value)
+								if value and value ~= "" then
+									-- Remove premade group from this group
+							    	local groupName = groupIdToName[info[2]];
+							    	
+							    	addon.db.profile.groups[groupName].premadeGroups[value] = nil;
+							    	
+							    	print(("No longer notifying you about changes made to the premade group named \"|cfffed000%s|r\"."):format(value));
+						    	end
+							end,
+							get = false,
+							disabled = function(info)
+						    	local groupName = groupIdToName[info[2]];
+						    	
+								return (not addon.db.profile.groups[groupName].premadeGroups);
+							end,
+						},
+					},
+				},
+				list = {
+					order = 20,
+					type = "group",
+					inline = true,
+					name = "Item list",
+					hidden = UpdateRemoveItemList,
+					args = {
+					
+					},
+				},
+				export = {
+					order = 30,
+					type = "group",
+					name = "Export",
+					inline = true,
+					args = {
+						input = {
+							order = 10,
+							type = "input",
+							name = "Item data",
+							width = "full",
+							desc = "Export the item data for the currently selected group. Press CTRL-A to select all and CTRL-C to copy the text.",
+							set = false,
+							get = function(info)
+								local groupName = groupIdToName[info[2]];
+								
+								local combinedItemIds;
+								-- Parse items in group and show these
+								for itemId, _ in pairs(addon.db.profile.groups[groupName].items) do
+									if not combinedItemIds then
+										combinedItemIds = tostring(itemId);
+									else
+										combinedItemIds = combinedItemIds .. (";%d"):format(itemId);
+									end
+								end
+								
+								return combinedItemIds; -- We don't serialize this because we actually DO want people to be able to manually modify it - besides, parsing it isn't going to be hard
+							end,
+						},
+					},
+				},
+			},
+		},
+	},
+};
+
+
+
+
+
+
+
+
+
+-- Object functions
+
+function mod:OnEnable()
+	-- Register our config slash command
+	-- /im config
+	addon:RegisterSlash(function(this)
+		-- We don't want any other windows open at this time.
+		for name, module in this:IterateModules() do
+			if module.CloseFrame then
+				module:CloseFrame();
+			end
+		end
+		
+		this:GetModule("Config"):Show();
+	end, { "c", "config", "conf", "option", "options", "opt", "setting", "settings" }, "|Hfunction:InventoriumCommandHandler:config|h|cff00fff7/im config|r|h (or /im c) - Open the config window to change the settings and manage groups.");
+	
+	-- Whenever the profile is changed, update the groups | args: (object for the functionName, eventName, functionName)
+	addon.db.RegisterCallback(self, "OnProfileChanged", "RefreshConfig");
+	addon.db.RegisterCallback(self, "OnProfileCopied", "RefreshConfig");
+	addon.db.RegisterCallback(self, "OnProfileReset", "RefreshConfig");
+	
+	-- Register our custom widgets
+	local Widgets = addon:GetModule("Widgets");
+	Widgets:ItemLinkButton();
+	Widgets:ConfigItemLinkButton();
+end
+
+function mod:RefreshConfig()
+	self:PremadeGroupsCheck();
+	
+	self:FillGroupOptions();
+end
+
+function mod:Load()
+	if not AceConfigDialog and not AceConfigRegistry then
+		self:PremadeGroupsCheck();
+		
+		self:FillOptions();
+	
+		-- Build options dialog
+		AceConfigDialog = LibStub("AceConfigDialog-3.0");
+		AceConfigRegistry = LibStub("AceConfigRegistry-3.0");
+		-- Register options table
+		LibStub("AceConfig-3.0"):RegisterOptionsTable("InventoriumOptions", options);
+		-- Set a nice default size (so that 4 normal sized elements fit next to eachother)
+		AceConfigDialog:SetDefaultSize("InventoriumOptions", 975, 600);
+		
+		 -- In case the addon is loaded from another condition, always call the remove interface options
+		if AddonLoader and AddonLoader.RemoveInterfaceOptions then
+			AddonLoader:RemoveInterfaceOptions("Inventorium");
+		end
+		
+		-- Add to the blizzard addons options thing
+		AceConfigDialog:AddToBlizOptions("InventoriumOptions");
+	end
+end
+
+function mod:Show()
+	self:Load();
+	
+	AceConfigDialog:Open("InventoriumOptions");
+end
+
+function mod:FillOptions()
+	options = {
+		type = "group",	
+		name = "Inventorium Config",
+		childGroups = "tree",
+		args = {
+		},
+	};
+	
+	-- General
+	self:FillGeneralOptions();
+	
+	-- Help
+	self:FillHelpOptions();
+	
+	-- Profile
+	options.args.profiles = LibStub("AceDBOptions-3.0"):GetOptionsTable(addon.db, true);
+	options.args.profiles.order = 200;
+	
+	-- Groups
+	self:MakeGroupOptions();
+	
+	-- Groups-contents
+	self:FillGroupOptions();
+end
+
+function mod:FillGeneralOptions()
+	options.args.general = {
+		order = 100,
+		type = "group",
+		name = "General",
+		desc = "Change general Inventorium settings.",
+		args = {
+			general = {
+				order = 1,
+				type = "group",
+				inline = true,
+				name = "General",
+				args = {
+					description = {
+						order = 0,
+						type = "description",
+						name = function()
+							local t = "Here you can set general settings. The settings entered here will be used when you choose not to override the settings within an individual group.\n\n";
+							
+							local currentAddon, selectedAddonName = addon:GetItemCountAddon();
+							local preferedAddon = addon.db.profile.defaults.itemCountAddon;
+							
+							if currentAddon then
+								t = t .. "Currently using |cfffed000" .. selectedAddonName .. "|r as your item count addon. This addon is " .. ((currentAddon.IsEnabled() and "|cff00ff00enabled|r") or "|cffff0000disabled|r") .. ".";
+								
+								if currentAddon.GetTotalCount and currentAddon.GetCharacterCount then
+									t = t .. " This addon supports |cfffed000both total as local|r item counts.";
+								elseif currentAddon.GetTotalCount then
+									t = t .. " This addon supports |cfffed000only total|r item counts.";
+								elseif currentAddon.GetCharacterCount then
+									t = t .. " This addon supports |cfffed000only local|r item counts.";
+								end
+								
+								if preferedAddon ~= selectedAddonName then
+									t = t .. "\n\n|cffff0000You have selected |cfffed000" .. preferedAddon .. "|r|cffff0000 as your item count addon, but this appears to be disabled and thus a random alternative was selected.|r";
+								end
+							end
+							
+							return t;
+						end,
+					},
+					header = {
+						order = 5,
+						type = "header",
+						name = "",
+					},
+					auctionPricingAddon = {
+						order = 10,
+						type = "select",
+						name = "Prefered pricing addon",
+						desc = "Select the addon you prefer data to be retrieved from. A random supported addon will be used if the selected addon can not be found.",
+						values = function()
+							local temp = {};
+							for name, value in pairs(addon.supportedAddons.auctionPricing) do
+								temp[name] = name;
+							end
+							
+							return temp;
+						end,
+						get = function() return addon.db.profile.defaults.auctionPricingAddon; end,
+						set = function(i, v)
+							addon.db.profile.defaults.auctionPricingAddon = v;
+							
+							if addon.supportedAddons.auctionPricing[v].OnSelect then
+								addon.supportedAddons.auctionPricing[v].OnSelect();
+							end
+						end,
+					},
+					itemCountAddon = {
+						order = 20,
+						type = "select",
+						name = "Prefered item count addon",
+						desc = "Select the addon you prefer data to be retrieved from. A random supported addon will be used if the selected addon can not be found.",
+						values = function()
+							local temp = {};
+							for name, value in pairs(addon.supportedAddons.itemCount) do
+								temp[name] = name;
+							end
+							
+							return temp;
+						end,
+						get = function() return addon.db.profile.defaults.itemCountAddon; end,
+						set = function(i, v)
+							addon.db.profile.defaults.itemCountAddon = v;
+							
+							if addon.supportedAddons.itemCount[v].OnSelect then
+								addon.supportedAddons.itemCount[v].OnSelect();
+							end
+						end,
+					},
+					craftingAddon = {
+						order = 30,
+						type = "select",
+						name = "Prefered crafting addon",
+						desc = "Select the addon you prefer data to be queued into. A random supported addon will be used if the selected addon can not be found.",
+						values = function()
+							local temp = {};
+							for name, value in pairs(addon.supportedAddons.crafting) do
+								temp[name] = name;
+							end
+							
+							return temp;
+						end,
+						get = function() return addon.db.profile.defaults.craftingAddon; end,
+						set = function(i, v)
+							addon.db.profile.defaults.craftingAddon = v;
+							
+							if addon.supportedAddons.crafting[v].OnSelect then
+								addon.supportedAddons.crafting[v].OnSelect();
+							end
+						end,
+					},
+					localItemData = {
+						order = 40,
+						type = "multiselect",
+						name = "Include in local item data",
+						desc = "Select which data should be included in the local item data.",
+						values = {
+							["Bag"] = "Bag",
+							["Bank"] = "Bank",
+							["Auction House"] = "Auction House",
+							["Mailbox"] = "Mailbox",
+						},
+						get = function(i, v) return addon.db.profile.defaults.localItemData and addon.db.profile.defaults.localItemData[v]; end,
+						set = function(i, v, e) addon.db.profile.defaults.localItemData[v] = e; end, -- can't be nil or the defaults will be used
+						dialogControl = "Dropdown", -- this is not standard, normal multiselect control gives us a list of all chars with toggle-boxes. UGLY! We want a multiselect-box instead.
+					},
+				},
+			},
+			minimumStock = {
+				order = 10,
+				type = "group",
+				inline = true,
+				name = "Minimum stock",
+				args = {
+					description = {
+						order = 0,
+						type = "description",
+						name = "Here you can specify the default minimum amount of items you wish to keep in stock and related settings. The settings entered here will be used when you choose not to override the settings within an individual group.",
+					},
+					header = {
+						order = 5,
+						type = "header",
+						name = "",
+					},
+					minLocalStock = {
+						order = 10,
+						type = "range",
+						min = 0,
+						max = 100000,
+						softMax = 100,
+						step = 1,
+						name = "Minimum local stock",
+						desc = "You can manually enter a value between 100 and 100.000 in the text box below if the provided range is insufficient.",
+						get = function() return addon.db.profile.defaults.minLocalStock; end,
+						set = function(i, v) addon.db.profile.defaults.minLocalStock = v; end,
+					},
+					alertBelowLocalMinimum = {
+						order = 11,
+						type = "toggle",
+						name = "Alert when below local minimum (NYI)",
+						desc = "Show an alert when this item gets below this threshold.",
+						get = function() return addon.db.profile.defaults.alertBelowLocalMinimum; end,
+						set = function(i, v) addon.db.profile.defaults.alertBelowLocalMinimum = v; end,
+					},
+					autoRefill = {
+						order = 12,
+						type = "toggle",
+						name = "Auto refill from (guild) bank",
+						desc = "Automatically refill items from the bank (unless this is included in the local count) or guild bank when below the minimum local stock.",
+						get = function() return addon.db.profile.defaults.autoRefill; end,
+						set = function(i, v) addon.db.profile.defaults.autoRefill = v; end,
+					},
+					minGlobalStock = {
+						order = 20,
+						type = "range",
+						min = 0,
+						max = 100000,
+						softMax = 100,
+						step = 1,
+						name = "Minimum global stock",
+						desc = "You can manually enter a value between 100 and 100.000 in the text box below if the provided range is insufficient.",
+						get = function() return addon.db.profile.defaults.minGlobalStock; end,
+						set = function(i, v) addon.db.profile.defaults.minGlobalStock = v; end,
+					},
+					alertBelowGlobalMinimum = {
+						order = 21,
+						type = "toggle",
+						name = "Alert when below global minimum (NYI)",
+						desc = "Show an alert when this item gets below this threshold.",
+						get = function() return addon.db.profile.defaults.alertBelowGlobalMinimum; end,
+						set = function(i, v) addon.db.profile.defaults.alertBelowGlobalMinimum = v; end,
+					},
+					summaryThresholdShow = {
+						order = 30,
+						type = "range",
+						min = 0,
+						max = 10,
+						softMax = 100,
+						step = 0.05,
+						isPercent = true,
+						name = "Show in summary when below",
+						desc = "Show items in the summary when below this percentage of the minimum stock. This can be either below the minimum or the global stock.\n\nYou can manually enter a value between 1.000% and 10.000% in the edit box if the provided range is insufficient.",
+						get = function() return addon.db.profile.defaults.summaryThresholdShow; end,
+						set = function(i, v) addon.db.profile.defaults.summaryThresholdShow = v; end,
+					},
+					trackAtCharacters = {
+						order = 40,
+						type = "multiselect",
+						name = "Track at",
+						desc = "Select at which characters this should appear in the summary and generate alerts.",
+						values = function()
+							local temp = {};
+							for charName in pairs(addon.db.factionrealm.characters) do
+								temp[charName] = charName;
+							end
+							
+							return temp;
+						end,
+						get = function(i, v)
+							return addon.db.profile.defaults.trackAtCharacters[v];
+						end,
+						set = function(i, v, e)
+							addon.db.profile.defaults.trackAtCharacters[v] = e or nil;
+						end,
+						dialogControl = "Dropdown", -- this is not standard, normal multiselect control gives us a list of all chars with toggle-boxes. UGLY! We want a multiselect-box instead.
+					},
+				},
+			},
+			refill = {
+				order = 20,
+				type = "group",
+				inline = true,
+				name = "Replenishing stock",
+				args = {
+					description = {
+						order = 0,
+						type = "description",
+						name = function()
+							local r = "Here you can specify the default amount of items to which you wish to restock when you are collecting new items. This may be higher than the minimum stock. The settings entered here will be used when you choose not to override the settings within an individual group.\n\n";
+							
+							r = r .. "When restocking the target amount is |cfffed000" .. addon.db.profile.defaults.restockTarget .. "|r of every item. Not queueing craftable items when only missing |cfffed000" .. floor( addon.db.profile.defaults.minCraftingQueue * addon.db.profile.defaults.restockTarget ) .. "|r (|cfffed000" .. ( addon.db.profile.defaults.minCraftingQueue * 100 ) .. "%|r) of the restock target.";
+							
+							return r;
+						end,
+					},
+					header = {
+						order = 5,
+						type = "header",
+						name = "",
+					},
+					restockTarget = {
+						order = 10,
+						type = "range",
+						min = 0,
+						max = 100000,
+						softMax = 100,
+						step = 1,
+						name = "Restock target",
+						desc = "You can manually enter a value between 100 and 100.000 in the edit box if the provided range is insufficient.",
+						get = function() return addon.db.profile.defaults.restockTarget; end,
+						set = function(i, v) addon.db.profile.defaults.restockTarget = v; end,
+					},
+					minCraftingQueue = {
+						order = 20,
+						type = "range",
+						min = 0,
+						max = 1,
+						step = 0.01, -- 1%
+						isPercent = true,
+						name = "Don't queue if I only miss",
+						desc = "Don't add a craftable item to the queue if I only miss this much or less of the restock target.\n\nExample: if your restock target is set to 60 and this is set to 5%, an item won't be queued unless you are missing more than 3 of it.",
+						get = function() return addon.db.profile.defaults.minCraftingQueue; end,
+						set = function(i, v) addon.db.profile.defaults.minCraftingQueue = v; end,
+					},
+					bonusQueue = {
+						order = 30,
+						type = "range",
+						min = 0,
+						max = 10, -- 1000%
+						step = 0.01, -- 1%
+						isPercent = true,
+						name = "Bonus queue",
+						desc = "Get additional items when there are none left.\n\nExample: if your restock target is set to 60 and this is set to 10%, you will get 66 items instead of just 60 if you end up with none left while queueing.",
+						get = function() return addon.db.profile.defaults.bonusQueue; end,
+						set = function(i, v) addon.db.profile.defaults.bonusQueue = v; end,
+					},
+					priceThreshold = {
+						order = 40,
+						type = "input",
+						name = "Price threshold",
+						desc = "Only queue craftable items when they are worth at least this much according to your auction house addon.\n\nSet to 0 to ignore auction prices.",
+						validate = function(info, value) return addon:ValidateReadableMoney(info, value); end,
+						get = function() return addon:ReadableMoney(addon.db.profile.defaults.priceThreshold); end,
+						set = function(i, v) addon.db.profile.defaults.priceThreshold = addon:ReadableMoneyToCopper(v); end,
+					},
+					summaryHidePriceThreshold = {
+						order = 50,
+						type = "toggle",
+						name = "Hide when below threshold",
+						desc = "Hide items from the summary when their value is below the set price threshold.",
+						get = function() return addon.db.profile.defaults.summaryHidePriceThreshold; end,
+						set = function(i, v) addon.db.profile.defaults.summaryHidePriceThreshold = v; end,
+					},
+					alwaysGetAuctionValue = {
+						order = 60,
+						type = "toggle",
+						name = "Always show auction value",
+						desc = "Always cache and show the auction value of items, even if the price threshold is set to 0|cffeda55fc|r.",
+						get = function() return addon.db.profile.defaults.alwaysGetAuctionValue; end,
+						set = function(i, v) addon.db.profile.defaults.alwaysGetAuctionValue = v; end,
+					},
+				},
+			},
+			colorCodes = {
+				order = 30,
+				type = "group",
+				inline = true,
+				name = "Color codes",
+				args = {
+					description = {
+						order = 0,
+						type = "description",
+						name = "Change the color code thresholds based on the current stock remaining of the required minimum stock.",
+					},
+					header = {
+						order = 5,
+						type = "header",
+						name = "",
+					},
+					green = {
+						order = 10,
+						type = "range",
+						min = 0,
+						max = 1,
+						step = 0.01,
+						isPercent = true,
+						name = "|cff00ff00Green|r",
+						desc = "Show quantity in green when at least this much of the minimum stock is available.",
+						get = function() return addon.db.profile.defaults.colors.green; end,
+						set = function(i, v) addon.db.profile.defaults.colors.green = v; end,
+					},
+					yellow = {
+						order = 20,
+						type = "range",
+						min = 0,
+						max = 1,
+						step = 0.01,
+						isPercent = true,
+						name = "|cffffff00Yellow|r",
+						desc = "Show quantity in yellow when at least this much of the minimum stock is available.",
+						get = function() return addon.db.profile.defaults.colors.yellow; end,
+						set = function(i, v) addon.db.profile.defaults.colors.yellow = v; end,
+					},
+					orange = {
+						order = 30,
+						type = "range",
+						min = 0,
+						max = 1,
+						step = 0.01,
+						isPercent = true,
+						name = "|cffff9933Orange|r",
+						desc = "Show quantity in orange when at least this much of the minimum stock is available.",
+						get = function() return addon.db.profile.defaults.colors.orange; end,
+						set = function(i, v) addon.db.profile.defaults.colors.orange = v; end,
+					},
+					red = {
+						order = 40,
+						type = "range",
+						min = 0,
+						max = 1,
+						step = 0.01,
+						isPercent = true,
+						name = "|cffff0000Red|r",
+						desc = "Show quantity in red when at least this much of the minimum stock is available.",
+						get = function() return addon.db.profile.defaults.colors.red; end,
+						set = function(i, v) addon.db.profile.defaults.colors.red = v; end,
+					},
+				},
+			},
+			extra = {
+				order = 40,
+				type = "group",
+				inline = true,
+				name = "Extra",
+				args = {
+					description = {
+						order = 0,
+						type = "description",
+						name = "Additional optional settings.",
+					},
+					header = {
+						order = 5,
+						type = "header",
+						name = "",
+					},
+					removeCharacter = {
+						order = 40,
+						type = "select",
+						name = "Forget character",
+						desc = "Select a character to remove all traces of it from Inventorium's memory.\n\nYour current character can not be removed, you must login to a different character to do so.",
+						values = function()
+							local temp = {};
+							
+							temp[""] = "";
+							
+							local playerName = UnitName("player");
+							for charName in pairs(addon.db.factionrealm.characters) do
+								if playerName ~= charName then
+									temp[charName] = charName;
+								end
+							end
+							
+							return temp;
+						end,
+						set = function(i, value)
+							if value and value ~= "" then
+								addon.db.factionrealm.characters[value] = nil;
+								addon.db.profile.defaults.trackAtCharacters[value] = nil;
+								for name, values in pairs(addon.db.profile.groups) do
+									if values.trackAtCharacters then
+										values.trackAtCharacters[name] = nil;
+									end
+								end
+							end
+						end,
+					},
+				},
+			},
+		},
+	};
+end
+
+function mod:FillHelpOptions()
+	options.args.help = {
+		order = 150,
+		type = "group",
+		childGroups = "tab",
+		name = "Help",
+		desc = "Useful information for if you're unfamiliar with a part of the addon.",
+		args = {
+			general = {
+				order = 1,
+				type = "group",
+				name = "General",
+				args = {
+					description = {
+						order = 0,
+						type = "description",
+						name = "Please note that all multi-select |cfffed000dropdown|r boxes were turned into multi-select |cfffed000toggle|r boxes. I do not intend to keep it this way, however it can not yet be reverted due to a major bug in one of the libraries used by Inventorium. The layout of this config may look terribly organized in it's current state.\n\n" .. 
+								"Since this is a beta some functionality might not be implemented yet while the options are available (usually - but not always - tagged as \"NYI\"). These options are used to indicate a feature is on the way and will be implemented before Inventorium is tagged as a release.\n\n" .. 
+								"Please request things you want and report anything that's clunky, weird, vague or otherwise buggy at |cfffed000the Inventorium development addon page|r. You can find this by searching for \"|cfffed000Inventorium|r\" at |cfffed000CurseForge.com|r.\n\n" .. 
+								"Tutorials for Inventorium will be created after the first stable release. If you require any help before that you can always contact me in the |cfffed000#JMTC|r IRC channel at |cfffed000QuakeNet.org|r. You may also report issues and request things there if you wish.\n\n" .. 
+								"You might notice the summary window currently gets very slow when refreshed once you get over 100-200 items in the list, this is a known issue and will be fixed in |cfffed000version 1.1|r or a little later (which is after the initial release).",
+					},
+				},
+			},
+			manual = {
+				order = 2,
+				type = "group",
+				name = "Manual",
+				args = {
+					intro = {
+						order = 1,
+						type = "group",
+						inline = true,
+						name = "Intro",
+						args = {
+							description = {
+								order = 0,
+								type = "description",
+								name = "|cfffed000Inventorium|r is an inventory tracking and restocking addon aimed towards making it extremely easy to keep enough stock of specific items at your characters. It provides a quick overview where you can see your current stock compared to the required stock and the current item value at the auction house whenever you find this relevant. From this overview you can queue craftable items into your favorite crafting profession addon and even restock from a vendor.",
+							},
+						},
+					},
+					Overview = {
+						order = 2,
+						type = "group",
+						inline = true,
+						name = "Overview",
+						args = {
+							description = {
+								order = 0,
+								type = "description",
+								name = "In the stock overview, which is called the summary, you can view a list with all items relevant to your current character with their updated stock and auction house values. You can sort this list on item quality, current stock, percentage of stock missing, and item values and this way easily manually find the items you wish to process. If you prefer to automate the process, you can also configure the groups exactly as you wish and hit the queue button to process everything in that group.\n\n" .. 
+								
+										"The item count data can be retrieved from most popular item count addons. This includes |cfffed000ItemCount|r and |cfffed000DataStore|r, but also |cfffed000Altoholic|r (even though this is not really a proper item count addon).\n" .. 
+										"The auction house values data can also be retrieved from most popular auction house addons. This includes |cfffed000Auctionator|r, |cfffed000Auctioneer|r, |cfffed000AuctionLite|r, |cfffed000AuctionMaster|r and any other addon implementing the standard for retrieving item values. Item values from the summary windows of |cfffed000AuctionProfitMaster|r and |cfffed000ZeroAuctions|r are also supported, but not recommended.",
+							},
+						},
+					},
+					Groups = {
+						order = 3,
+						type = "group",
+						inline = true,
+						name = "Groups",
+						args = {
+							description = {
+								order = 0,
+								type = "description",
+								name = "All items can be distributed over multiple groups to configure them exactly as you want. You can put all your glyphs in one group, gems in another and scrolls in a third with every single one of them having it's own set of unique settings. Per group you can set a price threshold, required item count, relevant characters and much more. The setup will feel very familiar to anyone that has used Quick Auctions to some extend as it uses a similar standard (though with many enhancements).",
+							},
+						},
+					},
+					Queueing = {
+						order = 4,
+						type = "group",
+						inline = true,
+						name = "Queueing",
+						args = {
+							description = {
+								order = 0,
+								type = "description",
+								name = "Queueing items into your crafting profession addon can be done per group or for all visible groups at once. This requires the relevant profession window to be open. All queueing is done based on the filters set in the config for the related group, any items falling outside will be ignored.\n\n" .. 
+									
+										"The queueing can be done into most popular crafting profession window replacing addons. This includes |cfffed000AdvancedTradeSkillWindow|r, |cfffed000GnomeWorks|r and |cfffed000Skillet|r.",
+							},
+						},
+					},
+					Configuring = {
+						order = 5,
+						type = "group",
+						inline = true,
+						name = "Configuring",
+						args = {
+							description = {
+								order = 0,
+								type = "description",
+								name = "The system resources used by this addon while it is stand-by are at a minimum, so you can just leave it enabled for all your characters unless you wish otherwise.\n\n" .. 
+										
+										"To start using Inventorium write |cfffed000/im|r in your chat. This will show a list of all available commands; |cfffed000/im config|r, |cfffed000summary|r and some others. Tip: Most of these commands have short alternatives, such as |cfffed000c|r for |cfffed000config|r and |cfffed000s|r for |cfffed000summary|r.\n\n" .. 
+										
+										"Write |cfffed000/im c|r to open the config window to start configuring your groups. The first tab you will find opened is the |cfffed000General|r config. Go ahead and select your prefered item count, crafting and pricing addons. Scroll down and quickly take note of all other options there, you won't really need everything (yet) but knowing it is available will save you from searching later on.\n\n" .. 
+										
+										"Click on the |cfffed000Groups|r tab. Here you will see a view handy options, in the future you might be able to find complete groups to import at popular blogs or forums making Inventorium very easy to import and initially setup, but for now we will have to make one manually. Quickly think about an easy-to-use and handy naming pattern and enter a name for your new group (e.g. |cfffed000Glyphs (Hunter)|r) and hit enter (leave the group type to the default). This group will be created and added to the list under the groups tab. Open the config for this group by clicking the group name.\n\n" .. 
+										
+										"You are now seeing a copy of the general config with override boxes next to everything. Configure this as you wish, for example override the |cfffed000minimum global stock|r to 20, |cfffed000track at|r to your banker and crafter (ensure your current char is also included) and the |cfffed000price threshold|r to 5g or so. Once done go to the \"add items\" tab to add items to this group so this group manages them. Again observice the available functionality so you know what you can do. Based on our previous example of Hunter glyphs we have three possibilities: one is to get all Hunter glyphs in our bags and add these, the second is to open the profession window and include everything above or equal to an item level of 0 and the third and in this case best possibility is to add items from a premade group. Select |cfffed000Glyphs (Hunter)|r from the |cfffed000premade groups|r select box to import all Hunter glyphs into this group. After doing so you can close the config window to check the results. Write |cfffed000/im s|r to view the summary window.\n\n" .. 
+										
+										"You can now copy this group and repeat the same steps to add all other groups you require. Think of things like one group per class for glyphs, one group per color for gems, one group per type of scroll (e.g. Heirloom, Twinks and Popular), one group for craftable epics, one group for flasks, one group for inks, one for parchments, etc.",
+							},
+						},
+					},
+					VirtualGroups = {
+						order = 6,
+						type = "group",
+						inline = true,
+						name = "Virtual groups",
+						args = {
+							description = {
+								order = 0,
+								type = "description",
+								name = "|cffccccccThis is advanced optional functionality. I recommend skipping this paragraph if you are new to Inventorium.|r\n\n" .. 
+										
+										"To allow changing the settings of multiple groups at once you can make virtual groups. These groups are basically like the general tab as their values are used when you aren't overriding a setting in a more specific group. After making a virtual group you still have to select it in all the \"child\"-groups so these groups know where to look for their settings when they are empty. Going back to our |cfffed000Glyphs (Hunter)|r group, we could make a virtual group called |cfffed000Glyphs|r which contained info like a default price threshold of 5g and a minimum crafting queue for all glyph groups. In each specific group you can then further specify that you would - for example - want 20 Hunter glyphs but 40 Druid glyphs as these might just sell a lot more often.",
+							},
+						},
+					},
+				},
+			},
+			FAQ = {
+				order = 3,
+				type = "group",
+				name = "FAQ",
+				args = {
+					description = {
+						order = 0,
+						type = "description",
+						name = "|cfffed000My groups don't appear in the summary window.|r\nPlease ensure your current character is toggled on at the \"track at\" option beneath the \"minimum stock\" category within a group or the defaults.\n\n" .. 
+								"|cfffed000The auction value collumn always shows a \"-\".|r\nThe auction value will not be cached when you set the \"price threshold\" beneath the \"replenishing stock\" category to |cfffed0000c|r. You can change this behavior by adjusting this value or toggling the \"Always show auction value\" option on.\n\n" .. 
+								"|cfffed000What relation does Inventorium have to ZeroAuctions or AuctionProfitMaster?|r\nNone. ZA/APM and IM are two completely seperate addons and do not interfere with eachother. At best you can use the auction pricing data displayed at the ZA/APM summary window as pricing source by selecting either addon as pricing addon, but neither IM nor ZA/APM will adjust any settings nor execute any other actions at one another. ZA/APM is an auction house addon. IM is a stock management addon. We are not related. We do not work together. We probably never will.\n\n" .. 
+								"|cfffed000What use do profiles have?|r\nBecause there is already the \"track at\" option, profiles may not be useful to anyone. Nevertheless someone might find a use for it in some way and thus it is left available. You can use it to test certain things for example without the risk of your main groups being destroyed (although this should never be an excuse not to back up your settings from time to time).\n\n" .. 
+								"|cfffed000Can you provide me with a sample scenario for virtual groups?|r\nNot really. If you are just getting to know Inventorium then I suggest leaving this functionality for the moment. It only makes things more complicated.\n\nAnyway, the simplest (and possibly most popular) setup to imagine are glyphs. There are over 300 glyphs available distributed over 10 classes. Glyphs for certain classes (such as the tribrids; Druids & Paladins) might sell a lot more often than those for others (such as pure DPS; Hunters, etc.).\n\nTo get an easily adjustable setup you can make one virtual group, called \"Glyphs\" and override all your prefered settings in there. After you are done, make a glyph-group for each class (such as \"Glyphs (Death Knight)\" etc.) and select \"Glyphs\" as virtual group for every one of them (you can easily insert item data to these class specific groups by selecting premade data).\n\nNow, to change the settings for all glyph groups you can just change the settings within the virtual \"Glyph\" group. To change the settings for one class in particular, such as Paladins because they sell more often than others, you can click this group and override the appropriate settings for just that group. There are many more possibilities for you to find out.\n\n" .. 
+								"",
+					},
+				},
+			},
+		},
+	};
+end
+
+function mod:MakeGroupOptions()
+	options.args.groups = {
+		order = 1100,
+		type = "group",
+		name = "Groups",
+		desc = "Change a group.",
+		args = {
+			create = {
+				order = 10,
+				type = "group",
+				inline = true,
+				name = "Create a brand new group",
+				args = {
+					name = {
+						order = 10,
+						type = "input",
+						name = "Group name",
+						desc = "The name of the group. You can also use item links as you wish.",
+						validate = ValidateGroupName,
+						set = function(_, value)
+							addon.db.profile.groups[value] = {};
+							if currentGroupType == "Virtual" then
+								addon.db.profile.groups[value].isVirtual = true;
+							end
+							
+							mod:FillGroupOptions();
+						end,
+						get = false,
+						width = "double",
+					},
+					type = {
+						order = 20,
+						type = "select",
+						name = "Type (advanced)",
+						desc = "The type of the new group. This can not be changed at a later time.\n\n|cffff9933This is an advanced option, you will probably not need it unless you manage a lot of groups.|r\n\n|cfffed000Normal|r: A normal group with complete functionality.\n\n|cfffed000Virtual|r: A virtual group which you can use to override the defaults for a set of groups. You can not add items to virtual groups.",
+						values = {
+							["Normal"] = "Normal",
+							["Virtual"] = "Virtual",
+						},
+						set = function(_, value) currentGroupType = value; end,
+						get = function() return currentGroupType; end,
+					},
+				},
+			},
+			import = {
+				order = 20,
+				type = "group",
+				inline = true,
+				name = "Import a group",
+				args = {
+					input = {
+						order = 10,
+						type = "input",
+						multiline = true,
+						name = "Group data",
+						desc = "Paste the group data as provided by a group export. If you are trying to import multiple groups at the same time, make sure to use newlines to seperate them.",
+						set = function(info, value)
+							local data = { string.split("\n", value or "") };
+							
+							for _, current in pairs(data) do
+								if not AceSerializer then
+									AceSerializer = LibStub("AceSerializer-3.0");
+								end
+								
+								local result, temp = AceSerializer:Deserialize(current);
+								
+								if not temp.name then
+									print("|cffff0000The provided data is not supported.|r");
+								elseif ValidateGroupName(nil, temp.name) ~= true then
+									print(("|cffff0000Aborting: A group named \"%s\" already exists.|r"):format(temp.name));
+								else
+									local name = temp.name;
+									temp.name = nil;
+									print(("Importing %s..."):format(name));
+									
+									if temp.items then
+										-- Remove items that are already in another group
+										for value, _ in pairs(temp.items) do
+											local itemId = tonumber(value);
+											
+											local itemData = addon.ItemData:New(itemId);
+											
+											if not itemId then
+												print(("\"%s\" is not a number."):format(value));
+												temp.items[value] = nil;
+											elseif itemData:InGroup() then
+												print(("Skipping %s as it is already in the group |cfffed000%s|r."):format( (itemData.link or unknownItemName:format(itemId)), itemData:InGroup() ));
+												temp.items[value] = nil;
+											else
+												-- Ensure the keys are numeric
+												temp.items[value] = nil;
+												temp.items[itemId] = true;
+											end
+										end
+									end
+									
+									-- Ensure this data isn't received (this would be silly/buggy as exports from other accounts - with different characters - won't know what to do with this)
+									temp.trackAtCharacters = nil;
+									temp.overrideTrackAtCharacters = nil;
+									
+									addon.db.profile.groups[name] = temp;
+								end
+							end
+							
+							self:FillGroupOptions();
+						end,
+						get = false,
+						width = "full",
+					},
+				},
+			},
+		},
+	};
+end
+
+function mod:FillGroupOptions()
+	for id, name in pairs(groupIdToName) do
+		if type(name) == "string" and not addon.db.profile.groups[name] then
+			options.args.groups.args[id] = nil;
+			groupIdToName[id] = nil;
+			groupIdToName[name] = nil;
+			groupIsVirtual[id] = nil;
+		end
+	end
+	
+	for name, values in pairs(addon.db.profile.groups) do
+		if not groupIdToName[name] then
+			options.args.groups.args[tostring(count)] = defaultGroup;
+			
+			groupIdToName[tostring(count)] = name;
+			groupIdToName[name] = true;
+			if values.isVirtual then
+				groupIsVirtual[tostring(count)] = true;
+			end
+			
+			count = ( count + 1 );
+		end
+	end
+end
+
+
+
+
+
+
+-- Verify premade groups
+
+function mod:PremadeGroupsCheck(updateGroupName, updateKey, accept)
+	-- Compare the current premade groups with those used, notify about changes
+	if addon.defaultGroups then
+		for premadeGroupName, groupInfo in pairs(addon.defaultGroups) do
+			-- Go through all default groups
+			
+			for groupName, values in pairs(addon.db.profile.groups) do
+				-- Go through all groups to find those with this premade group
+				
+				if values.premadeGroups and values.premadeGroups[premadeGroupName] and values.premadeGroups[premadeGroupName] < groupInfo.version then
+					-- Outdated group
+					
+					if updateGroupName and updateKey then
+						-- This function was called after pressing yes or no in a confirm box
+						
+						if accept then
+							-- Yes was clicked
+							
+							for itemId, version in pairs(groupInfo.items) do
+								-- Go through all items in this premade group
+								
+								local itemData = addon.ItemData:New(itemId);
+								
+								if version > values.premadeGroups[premadeGroupName] then
+									-- This item was added in a more recent version than this group: Add item
+									
+									if itemData:InGroup() then
+										print(("Skipping %s as it is already in the group |cfffed000%s|r."):format( (itemData.link or unknownItemName:format(itemId)), itemData:InGroup() ));
+									elseif itemData:AddToGroup(groupName) then
+										addon.Print(("Added %s found in the premade group |cfffed000%s|r to the group |cfffed000%s|r."):format( (itemData.link or unknownItemName:format(itemId)), premadeGroupName, itemData:InGroup() ), addon.Colors.Green);
+									end
+								elseif ( version * -1 ) > values.premadeGroups[premadeGroupName] then
+									if itemData:InGroup() == groupName then
+										itemData:RemoveFromGroup(groupName);
+										
+										addon.Print(("Removed %s from the group |cfffed000%s|r as it was removed from the premade group |cfffed000%s|r."):format( (itemData.link or unknownItemName:format(itemId)), itemData:InGroup(), premadeGroupName ), addon.Colors.Red);
+									else
+										print(("Skipping the removal of %s as it isn't in the group |cfffed000%s|r."):format( (itemData.link or unknownItemName:format(itemId)), itemData:InGroup() ));
+									end
+								end
+							end
+							
+							if AceConfigRegistry then
+								-- Now rebuild the list
+								AceConfigRegistry:NotifyChange("InventoriumOptions");
+							end
+							
+							-- Remember the new version
+							values.premadeGroups[premadeGroupName] = groupInfo.version;
+						else
+							-- No was clicked
+							
+							-- Let user know what was not added
+							for itemId, version in pairs(groupInfo.items) do
+								-- Go through all items in this premade group
+								
+								local itemData = addon.ItemData:New(itemId);
+								
+								if version > values.premadeGroups[premadeGroupName] then
+									-- This item was added in a more recent version than this group: don't add (since we clicked no), but announce it
+									
+									print(("Skipping %s found in the premade group |cfffed000%s|r."):format( (itemData.link or unknownItemName:format(itemId)), itemData:InGroup() ));
+								end
+							end
+							
+							-- Remember the new version
+							values.premadeGroups[premadeGroupName] = groupInfo.version;
+						end
+					else
+						StaticPopupDialogs["InventoriumConfirmUpdatePremadeGroup"] = {
+							text = "The premade group |cfffed000%s|r used in the group |cfffed000%s|r has been changed. Do you wish to copy these changes?",
+							button1 = YES,
+							button2 = NO,
+							OnAccept = function()
+								addon:PremadeGroupsCheck(groupName, premadeGroupName, true);
+							end,
+							OnCancel = function(_, _, reason)
+								if reason == "clicked" then
+									addon:PremadeGroupsCheck(groupName, premadeGroupName, false);
+								end
+							end,
+							timeout = 0,
+							whileDead = 1,
+							hideOnEscape = 1,
+						};
+						StaticPopup_Show("InventoriumConfirmUpdatePremadeGroup", premadeGroupName, groupName);
+						
+						return;
+					end
+				end
+			end
+		end
+	end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Modules/Mover.lua	Thu Jan 06 20:05:30 2011 +0100
@@ -0,0 +1,400 @@
+local addon = select(2, ...);
+local mod = addon:NewModule("Mover", "AceEvent-3.0", "AceTimer-3.0");
+
+local Scanner;
+local queuedMoves = {}; -- table storing all queued moves before BeginMove is called
+local combinedMoves = {}; -- table storing all combined moves (with source and target) that is to be processed by the actual mover in the order of the index (1 to #)
+local movesSource;
+
+function mod:AddMove(itemId, amount)
+	table.insert(queuedMoves, {
+		id = itemId,
+		num = amount,
+	});
+end
+
+function mod:HasMoves()
+	return (#queuedMoves ~= 0);
+end
+
+if not table.reverse then
+	table.reverse = function(orig)
+		local temp = {};
+		local origLength = #orig;
+		for i = 1, origLength do
+			temp[(origLength - i + 1)] = orig[i];
+		end
+		
+-- 		-- Update the original table (can't do orig = temp as that would change the reference-link instead of the original table)
+-- 		for i, v in pairs(temp) do
+-- 			orig[i] = v;
+-- 		end
+		return temp; -- for speed we choose to do a return instead
+	end
+end
+
+function mod:BeginMove(location, onFinish)
+	addon:Debug("BeginMove");
+	
+	-- Find the outgoing moves
+	-- We need the source container and slot, find all the requires sources and put them in a list which we go through later to find matching targets
+	
+	-- Get a list of items in the source container
+	local sourceContents = Scanner:CacheLocation(location, false);
+	
+	local outgoingMoves = {};
+
+	addon:Debug(#queuedMoves .. " moves were queued.");
+	
+	for _, singleMove in pairs(queuedMoves) do
+		local sourceItem = sourceContents[singleMove.id];
+		if not sourceItem then
+			print("Can't move " .. IdToItemLink(singleMove.id) .. ", non-existant in source");
+		else
+			-- We want to move the smallest stacks first to keep stuff pretty (and minimize space usage, splitting a stack takes 2 slots, moving something only 1)
+			table.sort(sourceItem.locations, function(a, b)
+				return a.count < b.count;
+			end);
+			
+			for _, itemLocation in pairs(sourceItem.locations) do
+				-- if this location has more items than we need, only move what we need, otherwise move everything in this stack
+				local movingNum = ((itemLocation.count > singleMove.num and singleMove.num) or itemLocation.count);
+				
+				table.insert(outgoingMoves, {
+					itemId = singleMove.id,
+					num = movingNum,
+					container = itemLocation.container,
+					slot = itemLocation.slot,
+				});
+				
+				singleMove.num = (singleMove.num - movingNum);
+				
+				if singleMove.num == 0 then
+					-- If we have prepared everything we wanted, go to the next queued move
+					break; -- stop the locations-loop
+				end
+			end
+		end
+	end
+
+	addon:Debug(#outgoingMoves .. " outgoing moves are possible.");
+	
+	-- No longer needed
+	table.wipe(queuedMoves);
+	
+	-- Process every single outgoing move and find fitting targets
+	
+	-- Get a list of items already in the target container
+	local targetContents = Scanner:CacheLocation(addon.Locations.Bag, false);
+	
+	-- Find all empty slots
+	
+	local emptySlots = {};
+	
+	local start = 0;
+	local stop = NUM_BAG_SLOTS;
+	
+	-- Go through all our bags, including the backpack
+	for bagId = start, stop do
+		-- Go through all our slots
+		for slotId = 1, GetContainerNumSlots(bagId) do
+			local itemId = GetContainerItemID(bagId, slotId); -- we're scanning our local bags here, so no need to get messy with guild bank support
+			
+			if not itemId then
+				table.insert(emptySlots, {
+					container = bagId,
+					slot = slotId,
+				});
+			end
+		end
+	end
+
+	addon:Debug(#emptySlots .. " empty slots are available.");
+	
+	-- Remember where we're moving from
+	movesSource = location;
+	
+	local backup = 0;
+	
+	while #outgoingMoves ~= 0 do
+		-- A not equal-comparison should be quicker than a larger/smaller than-comparison
+		
+		for _, outgoingMove in pairs(outgoingMoves) do
+			-- itemId  will be set to nil when this outgoing move was processed - sanity check
+			if outgoingMove.itemId then
+				local targetItem = targetContents[outgoingMove.itemId];
+				
+				if not targetItem then
+					-- grab an empty slot
+					-- make new instance of ItemMove
+					-- populate targetContents with it so future moves of this item can be put on top of it if this isn't a full stack
+					
+					local firstAvailableSlot = emptySlots[1];
+					
+					if not firstAvailableSlot then
+						print("Bags are full. Skipping " .. IdToItemLink(outgoingMove.itemId) .. ".");
+						
+						outgoingMove.itemId = nil; -- remove this record from the outgoingMoves-table
+					else
+						table.insert(combinedMoves, {
+							itemId = outgoingMove.itemId,
+							num = outgoingMove.num,
+							sourceContainer = outgoingMove.container,
+							sourceSlot = outgoingMove.slot,
+							targetContainer = firstAvailableSlot.container,
+							targetSlot = firstAvailableSlot.slot,
+						});
+						
+						-- We filled an empty slot so the target contents now has one more item,
+						-- make a new instance of the ItemMove class so any additional items with this id can be stacked on top of it
+						local itemMove = addon.ContainerItem:New();
+						itemMove:AddLocation(firstAvailableSlot.container, firstAvailableSlot.slot, outgoingMove.num);
+						targetContents[outgoingMove.itemId] = itemMove;
+						
+						table.remove(emptySlots, 1); -- no longer empty
+						
+						outgoingMove.num = 0; -- nothing remaining - sanity check
+						outgoingMove.itemId = nil; -- remove this record from the outgoingMoves-table
+					end
+				else
+					-- Find the maximum stack size for this item
+					local itemStackCount = select(8, GetItemInfo(outgoingMove.itemId));
+					
+					-- We want to move to the largest stacks first to keep stuff pretty
+					table.sort(targetItem.locations, function(a, b)
+						return a.count > b.count;
+					end);
+					
+					for _, itemLocation in pairs(targetItem.locations) do
+						if itemLocation.count < itemStackCount and outgoingMove.num > 0 then
+							-- Check if this stack isn't already full (and we still need to move this item)
+							
+							local remainingSpace = (itemStackCount - itemLocation.count);
+							if remainingSpace >= outgoingMove.num then
+								-- Enough room to move this entire stack
+								-- Deposit this item and then forget this outgoing move as everything in it was processed
+								
+								table.insert(combinedMoves, {
+									itemId = outgoingMove.itemId,
+									num = outgoingMove.num,
+									sourceContainer = outgoingMove.container,
+									sourceSlot = outgoingMove.slot,
+									targetContainer = itemLocation.container,
+									targetSlot = itemLocation.slot,
+								});
+								
+								itemLocation.count = (itemLocation.count + outgoingMove.num);
+								outgoingMove.num = 0; -- nothing remaining
+								outgoingMove.itemId = nil; -- remove this record from the outgoingMoves-table
+								break; -- stop the locations-loop
+							else
+								-- Deposit this item but don't remove the outgoing move as there are some items left to move
+								
+								table.insert(combinedMoves, {
+									itemId = outgoingMove.itemId,
+									num = outgoingMove.num,
+									sourceContainer = outgoingMove.container,
+									sourceSlot = outgoingMove.slot,
+									targetContainer = itemLocation.container,
+									targetSlot = itemLocation.slot,
+								});
+								
+								-- The target will be full when we complete, but the source will still have remaining items left to be moved
+								itemLocation.count = itemStackCount;
+								outgoingMove.num = (outgoingMove.num - remainingSpace);
+							end
+						end
+					end
+					
+					if outgoingMove.num > 0 then
+						-- We went through all matching items and checked their stack sizes if we could move this there, no room available
+						-- So forget about the target item (even though it may just have full locations, these are useless anyway) and the next loop move it onto an empty slot
+						targetContents[outgoingMove.itemId] = nil;
+					end
+				end
+			end
+		end
+		
+		-- Loop through the array to find items that should be removed, start with the last element or the loop would break
+		local numOutgoingMoves = #outgoingMoves; -- since LUA-tables start at an index of 1, this is actually an existing index (outgoingMoves[#outgoingMoves] would return a value)
+		while numOutgoingMoves ~= 0 do
+			-- A not equal-comparison should be quicker than a larger/smaller than-comparison
+			
+			-- Check if the item id is nil, this is set to nil when this outgoing move has been processed
+			if not outgoingMoves[numOutgoingMoves].itemId or outgoingMoves[numOutgoingMoves].num == 0 then
+				-- Remove this element from the array
+				table.remove(outgoingMoves, numOutgoingMoves);
+			end
+			
+			-- Proceed with the next element (or previous considering we're going from last to first)
+			numOutgoingMoves = (numOutgoingMoves - 1);
+		end
+
+		addon:Debug(#outgoingMoves .. " moves remaining.");
+		
+		backup = (backup + 1);
+		if backup > 1000 then
+			dump(nil, outgoingMoves);
+			table.wipe(outgoingMoves);
+			self:Abort("mover crashed", "Error preparing moves, hit an endless loop");
+			onFinish();
+			return;
+		end
+	end
+	
+	-- Reverse table, we need to go through it from last to first because we'll be removing elements, but we don't want the actions to be executed in a different order
+	combinedMoves = table.reverse(combinedMoves);
+
+	addon:Debug(#combinedMoves .. " moves should be possible.");
+	
+	-- No longer needed
+	table.wipe(emptySlots);
+	
+	self:ProcessMove();
+	
+	-- Even though we aren't completely done yet, allow requeueing
+	onFinish();
+end
+
+function mod:ProcessMove()
+	addon:Debug("ProcessMove");
+	
+	if #combinedMoves == 0 then
+		print("Nothing to move.");
+		
+		self:Abort();
+		
+		return;
+	end
+	
+	self:RegisterEvent("BAG_UPDATE");
+	self:RegisterEvent("UI_ERROR_MESSAGE");
+	
+	-- combinedMoves now has all moves in it (source -> target)
+	-- go through list, move everything inside it
+	-- add source and target to lists, if either is already in this list, skip the move
+	-- repeat every few seconds until we're completely done
+	
+	local sourceLocationsLocked = {};
+	local targetLocationsLocked = {};
+	
+	local _GetContainerItemId = GetContainerItemID;
+	if movesSource == addon.Locations.Guild then
+		_GetContainerItemId = function(tabId, slotId) return addon:GetItemID(GetGuildBankItemLink(tabId, slotId)); end;
+	end
+    
+	local combinedMovesOriginalLength = #combinedMoves;
+	local numCurrentMove = combinedMovesOriginalLength;
+	while numCurrentMove ~= 0 do
+		local move = combinedMoves[numCurrentMove];
+		
+		-- sourceContainer, sourceSlot, targetContainer, targetSlot, itemId, num
+		if move and (not sourceLocationsLocked[move.sourceContainer] or not sourceLocationsLocked[move.sourceContainer][move.sourceSlot]) and 
+			(not targetLocationsLocked[move.targetContainer] or not targetLocationsLocked[move.targetContainer][move.targetSlot]) then
+			
+			print(("Moving %dx%s."):format(move.num, IdToItemLink(move.itemId)));
+			
+			addon:Debug(("Moving %dx%s from (%d,%d) to (%d,%d)"):format(move.num, IdToItemLink(move.itemId), move.sourceContainer, move.sourceSlot, move.targetContainer, move.targetSlot));
+			
+			if _GetContainerItemId(move.sourceContainer, move.sourceSlot) ~= move.itemId then
+				self:Abort("source changed", "Source (" .. move.sourceContainer .. "," .. move.sourceSlot .. ") is not " .. IdToItemLink(move.itemId));
+				return;
+			end
+			
+			-- Pickup stack
+			if movesSource == addon.Locations.Bank then
+				SplitContainerItem(move.sourceContainer, move.sourceSlot, move.num);
+			elseif movesSource == addon.Locations.Guild then
+				SplitGuildBankItem(move.sourceContainer, move.sourceSlot, move.num);
+			end
+			
+			-- Remember we picked this item up and thus it is now locked
+			if not sourceLocationsLocked[move.sourceContainer] then
+				sourceLocationsLocked[move.sourceContainer] = {};
+			end
+			sourceLocationsLocked[move.sourceContainer][move.sourceSlot] = true;
+			
+			if movesSource == addon.Locations.Guild or CursorHasItem() then -- CursorHasItem is always false if source is a guild tab
+				-- We are moving into our local bags, so the below must check normal
+				local targetItemId = GetContainerItemID(move.targetContainer, move.targetSlot);
+				if targetItemId and targetItemId ~= move.itemId then
+					self:Abort("target changed", "Target (" .. move.targetContainer .. "," .. move.targetSlot .. ") is not " .. IdToItemLink(move.itemId) .. " nor empty");
+					return;
+				end
+				
+				-- And drop it (this is always a local bag so no need to do any guild-checks)
+				PickupContainerItem(move.targetContainer, move.targetSlot);
+				
+				-- Remember we dropped an item here and thus this is now locked
+				if not targetLocationsLocked[move.targetContainer] then
+					targetLocationsLocked[move.targetContainer] = {};
+				end
+				targetLocationsLocked[move.targetContainer][move.targetSlot] = true;
+				
+				-- This move was processed
+				table.remove(combinedMoves, numCurrentMove);
+			else
+				self:Abort("item disappeared from mouse", "Couldn't move " .. IdToItemLink(move.itemId) .. ", CursorHasItem() is false");
+				return;
+			end
+		end
+		
+		-- Proceed with the next element (or previous considering we're going from last to first)
+		numCurrentMove = (numCurrentMove - 1);
+	end
+	
+	addon:Debug((combinedMovesOriginalLength - #combinedMoves) .. " moves processed. " .. #combinedMoves .. " moves remaining.");
+	
+	if #combinedMoves == 0 then
+		print("Finished.");
+		
+		self:Abort();
+		
+		return;
+	end
+end
+
+local tmrProcessNext;
+function mod:BAG_UPDATE()
+	self:CancelTimer(tmrProcessNext, true); -- silent
+	tmrProcessNext = self:ScheduleTimer("ProcessMove", 1);
+end
+
+function IdToItemLink(itemId)
+	local itemLink = select(2, GetItemInfo(itemId));
+	itemLink = itemLink or "Unknown (" .. itemId .. ")";
+	return itemLink;
+end
+
+function mod:UI_ERROR_MESSAGE(e, errorMessage)
+	if errorMessage == ERR_SPLIT_FAILED then
+		self:Abort("splitting failed", "Splitting failed.");
+	end
+end
+
+function mod:Abort(simple, debugMsg)
+	if debugMsg then
+		addon:Debug("Aborting:" .. debugMsg);
+	end
+	if simple then
+		print("|cffff0000Aborting: " .. simple .. ".|r");
+	end
+	table.wipe(combinedMoves);
+	movesSource = nil;
+	
+	-- Stop timer
+	self:UnregisterEvent("BAG_UPDATE");
+	self:CancelTimer(tmrProcessNext, true); -- silent
+	
+	self:UnregisterEvent("UI_ERROR_MESSAGE");
+end
+
+function mod:OnEnable()
+	Scanner = addon:GetModule("Scanner");
+end
+
+function mod:OnDisable()
+	Scanner = nil;
+	
+	self:Abort();
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Modules/Queue.lua	Thu Jan 06 20:05:30 2011 +0100
@@ -0,0 +1,151 @@
+local addon = select(2, ...);
+local mod = addon:NewModule("Queue", "AceEvent-3.0", "AceTimer-3.0");
+
+local pairs = pairs;
+
+function mod:OnEnable()
+	-- Register our own slash commands
+	-- /im queue
+	addon:RegisterSlash(function()
+		mod:QueueAll();
+	end, { "q", "que", "queue" }, "|Hfunction:InventoriumCommandHandler:queue|h|cff00fff7/im queue|r|h (or /im q) - Queue all items found in the currently opened profession that are within the groups tracked at this current character.");
+	
+	self:RegisterMessage("IM_QUEUE_ALL");
+	self:RegisterMessage("IM_QUEUE_GROUP");
+end
+
+function mod:IM_QUEUE_ALL()
+	self:QueueAll();
+end
+
+function mod:IM_QUEUE_GROUP(event, groupName)
+	self:QueueGroup(groupName);
+end
+
+function mod:QueueAll()
+	local playerName = UnitName("player");
+	
+	-- Go through all groups
+	for groupName, values in pairs(addon.db.profile.groups) do
+		local trackAt = addon:GetOptionByKey(groupName, "trackAtCharacters");
+		
+		if trackAt[playerName] then
+			self:QueueGroup(groupName);
+		end
+	end
+end
+
+function mod:QueueGroup(groupName)
+	if not addon.db.profile.groups[groupName] then
+		print(("Tried to queue items from a group named \"%s\", but no such group exists."):format(groupName));
+		return;
+	end
+	
+	local temp = {};
+	
+	local tradeskillName, currentLevel, maxLevel = GetTradeSkillLine();
+	
+	if tradeskillName ~= "UNKNOWN" then
+		-- Go through all trade skills for the profession
+		for i = 1, GetNumTradeSkills() do
+			-- Process every single tradeskill
+			self:ProcessTradeSkill(i, groupName, temp);
+		end
+	end
+	
+	if addon.db.profile.groups[groupName].items then
+		for itemId, _ in pairs(addon.db.profile.groups[groupName].items) do
+			if not temp[itemId] then
+				local itemLink = select(2, GetItemInfo(itemId));
+				
+				print("Couldn't queue " .. (itemLink or itemId or "???") .. " (not part of this profession)");
+			end
+		end
+	end
+end
+
+function mod:ProcessTradeSkill(i, groupName, temp)
+	-- Try to retrieve the itemlink, this will be nil if current item is a group instead
+	local itemLink = GetTradeSkillItemLink(i);
+	
+	if itemLink then
+		local itemId = addon:GetItemId(itemLink);
+		if not itemId then
+			-- If this isn't an item, it can only be an enchant instead
+			itemId = tonumber(itemLink:match("|Henchant:([-0-9]+)|h"));
+			
+			itemId = addon.scrollIds[itemId]; -- change enchantIds into scrollIds
+		end
+		
+		if addon.db.profile.groups[groupName].items[itemId] then
+			-- This item is in this group, queue it!
+			
+			if temp then
+				-- Remember which items have been processed
+				temp[itemId] = true;
+			end
+			
+			local currentStock = addon:GetItemCount(itemId, groupName);
+			
+			if currentStock >= 0 then
+				-- Current stock will be -1 when no itemcount addon was found
+				
+				-- Retrieve group settings
+				local restockTarget = addon:GetOptionByKey(groupName, "restockTarget");
+				local bonusQueue = addon:GetOptionByKey(groupName, "bonusQueue");
+				local minCraftingQueue = floor( addon:GetOptionByKey(groupName, "minCraftingQueue") * restockTarget );
+				
+				-- Calculate the amount to be queued
+				local amount = ( restockTarget - currentStock );
+				
+				if currentStock == 0 and bonusQueue > 0 then
+					-- If we have none left and the bonus queue is enabled, modify the amount to  be queued
+					
+					amount = floor( ( amount * ( bonusQueue + 1 ) ) + .5 ); -- round
+				end
+				
+				if amount > 0 and amount >= minCraftingQueue then
+					-- If we are queueing at least one AND more than the minimum amount, then proceed
+					
+					-- Auction value settings
+					local priceThreshold = addon:GetOptionByKey(groupName, "priceThreshold");
+					
+					if priceThreshold == 0 or addon:GetAuctionValue(itemLink, groupName) >= priceThreshold then
+						-- If no price threshold is set or the auction value is equal to or larger than the price threshold, then proceed
+						
+						self:Queue(i, amount, groupName);
+						
+						print("Queued " .. amount .. " of " .. itemLink);
+					end
+				end
+			else
+				print("No usable itemcount addon found.");
+			end
+		end
+	end
+end
+
+function mod:Queue(tradeSkillIndex, amount, group)
+	tradeSkillIndex = tonumber(tradeSkillIndex);
+	amount = tonumber(amount);
+	
+	if not tradeSkillIndex or not amount then return; end
+	
+	local selectedExternalAddon = addon:GetOptionByKey(group, "craftingAddon");
+	
+	if addon.supportedAddons.crafting[selectedExternalAddon] and addon.supportedAddons.crafting[selectedExternalAddon].IsEnabled() then
+		-- Try to use the default auction pricing addon
+		
+		return addon.supportedAddons.crafting[selectedExternalAddon].Queue(tradeSkillIndex, amount);
+	else
+		-- Default not available, get the first one then
+		
+		for name, value in pairs(addon.supportedAddons.crafting) do
+			if value.IsEnabled() then
+				return value.Queue(tradeSkillIndex, amount);
+			end
+		end
+	end
+	
+	return -2;
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Modules/Scanner.lua	Thu Jan 06 20:05:30 2011 +0100
@@ -0,0 +1,274 @@
+local addon = select(2, ...);
+local mod = addon:NewModule("Scanner", "AceEvent-3.0", "AceTimer-3.0");
+
+addon.Locations = {
+	Bag = 0,
+	Bank = 1,
+	Guild = 2,
+};
+
+local Mover, paused;
+local itemCache = {};
+
+function mod:ClearCache()
+	table.wipe(itemCache);
+end
+
+function mod:CacheLocation(location, remember)
+	if location == addon.Locations.Bag or location == addon.Locations.Bank then
+		-- Reset cache just in case it was filled
+		self:ClearCache();
+		
+		local start, stop;
+		if location == addon.Locations.Bag then
+			start = 0;
+			stop = NUM_BAG_SLOTS;
+		else
+			-- If we requested the bank then we don't want the bag info
+			start = ( NUM_BAG_SLOTS + 1 );
+			stop = ( NUM_BAG_SLOTS + NUM_BANKBAGSLOTS );
+		end
+		
+		-- Go through all our bags, including the backpack
+		for i = start, ((location == addon.Locations.Bag and stop) or (location == addon.Locations.Bank and (stop + 1))) do -- if scanning bags stop at normal bag slot, if scanning bank, stop one later to allow BANK_CONTAINER to be scanned too
+			-- Scan the default 100 slots whenever we're at a non-existing index
+			local bagId = (i == (stop + 1) and BANK_CONTAINER) or i;
+			local slotId = GetContainerNumSlots(bagId);
+			
+			while slotId ~= 0 do
+				-- A not equal-comparison should be quicker than a larger than-comparison
+				
+				local itemId = GetContainerItemID(bagId, slotId);
+				local itemCount = itemId and select(2, GetContainerItemInfo(bagId, slotId));
+				
+				if itemId and itemCount and itemCount > 0 then
+					local itemMove;
+					if not itemCache[itemId] then
+						-- If this is the first time we see this item, make a new object
+						itemMove = addon.ContainerItem:New();
+						itemCache[itemId] = itemMove;
+					else
+						-- If we had this item in another slot too
+						itemMove = itemCache[itemId];
+					end
+					
+					itemMove:AddLocation(bagId, slotId, itemCount);
+				end
+			
+				-- Continue scanning a different slot
+				slotId = (slotId - 1);
+			end
+		end
+	elseif location == addon.Locations.Guild then
+		-- Reset cache before we scan
+		self:ClearCache();
+		
+		for tabId = 1, GetNumGuildBankTabs() do
+			local isViewable = select(3, GetGuildBankTabInfo(tabId));
+			
+			if isViewable == 1 then
+				local slotId = (MAX_GUILDBANK_SLOTS_PER_TAB or 98); -- start by scanning the last slot
+				
+				while slotId ~= 0 do
+					-- A not equal-comparison should be quicker than a larger than-comparison
+					
+					local itemLink = GetGuildBankItemLink(tabId, slotId);
+					local itemId = itemLink and addon:GetItemID(itemLink);
+					local itemCount = itemLink and select(2, GetGuildBankItemInfo(tabId, slotId));
+						
+					if itemLink and itemId and itemCount and itemCount > 0 then
+						-- If there is actually an item in this slot
+						local itemMove;
+						if not itemCache[itemId] then
+							-- If this is the first time we see this item, make a new object
+							itemMove = addon.ContainerItem:New();
+							itemCache[itemId] = itemMove;
+						else
+							-- If we had this item in another slot too
+							itemMove = itemCache[itemId];
+						end
+						
+						itemMove:AddLocation(tabId, slotId, itemCount);
+					end
+					
+					-- Continue scanning a different slot
+					slotId = (slotId - 1);
+				end
+			end
+		end
+	else
+		error("Invalid location provided for CacheLocation. Must be Bank or Guild.");
+	end
+	
+	if not remember then
+		-- Copy the table as clearing the cache wipes it empty (and tables are passed by reference)
+		local cacheCopy = CopyTable(itemCache);
+		
+		self:ClearCache();
+		
+		return cacheCopy;
+	end
+end
+
+function mod:Scan(location)
+	-- We might pause the scanning when we invoke moves ourself
+	if paused then
+		return;
+	end
+	
+	local playerName = UnitName("player");
+	
+	self:CacheLocation(location, true);
+	
+	-- Go through all groups
+	for groupName, values in pairs(addon.db.profile.groups) do
+		local trackAt = addon:GetOptionByKey(groupName, "trackAtCharacters");
+		local localItemData = addon:GetOptionByKey(groupName, "localItemData");
+		
+		if values.items and trackAt[playerName] and addon:GetOptionByKey(groupName, "autoRefill") and (location ~= addon.Locations.Bank or not localItemData or not localItemData["Bank"]) then
+			-- Is this character interested in this data?
+			
+			local minLocalStock = addon:GetOptionByKey(groupName, "minLocalStock");
+			
+			-- Go through all items
+			for itemId, _ in pairs(values.items) do
+				
+				-- Check if we have enough items local (but only do so if this location also has enough available)
+				local missingItems = itemCache[itemId] and (minLocalStock - addon:GetLocalItemCount(itemId, groupName));
+				
+				if itemCache[itemId] and missingItems > 0 then
+					-- Check how many are available
+					local availableItems = ((itemCache[itemId] and itemCache[itemId].totalCount) or 0);
+					
+					if availableItems > 0 then
+						print("Insufficient " .. select(2, GetItemInfo(itemId)) .. " but this location has " .. availableItems .. " (moving " .. missingItems .. ")");
+						
+						Mover:AddMove(itemId, missingItems);
+					else
+						print("Insufficient " .. IdToItemLink(itemId));
+					end
+				end
+			end
+		end
+	end
+	
+	self:ClearCache();
+	
+	if Mover:HasMoves() then
+		StaticPopupDialogs["InventoriumRefill"] = {
+			text = "There are items that can be refilled from this location, do you wish to proceed?",
+			button1 = YES,
+			button2 = NO,
+			OnAccept = function()
+				mod:Pause();
+				Mover:BeginMove(location, self.Unpause);
+			end,
+			timeout = 0,
+			whileDead = 1,
+			hideOnEscape = 1,
+			exclusive = 1,
+		};
+		StaticPopup_Show("InventoriumRefill");
+	end
+end
+
+
+
+-- Events
+
+-- Player bank
+
+function mod:BANKFRAME_OPENED()
+	addon:Debug("Scanner:BANKFRAME_OPENED");
+	
+	mod:RegisterEvent("BANKFRAME_CLOSED");
+	
+	-- Scan once when the bank is opened, but no need to scan after
+	mod:Scan(addon.Locations.Bank);
+end
+
+function mod:BANKFRAME_CLOSED()
+	addon:Debug("Scanner:BANKFRAME_CLOSED");
+	
+	mod:UnregisterEvent("BANKFRAME_CLOSED");
+	
+	StaticPopup_Hide("InventoriumRefill");
+end
+
+-- Guild bank
+
+local tmrScanGuild, scanned;
+function mod:GUILDBANKBAGSLOTS_CHANGED()
+	-- This event is spammed the first time the guild bank is opened
+	if not scanned then
+		self:CancelTimer(tmrScanGuild, true); -- silent
+		tmrScanGuild = self:ScheduleTimer("DoScanGuild", 1);
+	end
+end
+
+function mod:DoScanGuild()
+	if not scanned then
+		addon:Debug("Scanner:GUILDBANKBAGSLOTS_CHANGED");
+		
+		scanned = true;
+		
+		self:Scan(addon.Locations.Guild);
+	end
+end
+
+function mod:GUILDBANKFRAME_CLOSED()
+	addon:Debug("Scanner:GUILDBANKFRAME_CLOSED");
+	
+	scanned = nil;
+	
+	self:UnregisterEvent("GUILDBANKFRAME_CLOSED");
+	self:UnregisterEvent("GUILDBANKBAGSLOTS_CHANGED");
+	
+	self:CancelTimer(tmrScanGuild, true); -- silent
+	
+	StaticPopup_Hide("InventoriumRefill");
+end
+
+function mod:GUILDBANKFRAME_OPENED()
+	addon:Debug("Scanner:GUILDBANKFRAME_OPENED");
+	
+	scanned = nil;
+	
+	-- Get the contents for every tab into our cache
+	for tabId = 1, GetNumGuildBankTabs() do
+		local isViewable = select(3, GetGuildBankTabInfo(tabId));
+		if isViewable == 1 then		
+			QueryGuildBankTab(tabId);
+		end
+	end
+	
+	self:RegisterEvent("GUILDBANKFRAME_CLOSED");
+	self:RegisterEvent("GUILDBANKBAGSLOTS_CHANGED");
+end
+
+function mod:OnEnable()
+	-- Scan once when the bankframe is opened
+	self:RegisterEvent("BANKFRAME_OPENED");
+	self:RegisterEvent("GUILDBANKFRAME_OPENED");
+	
+	Mover = addon:GetModule("Mover");
+end
+
+function mod:OnDisable()
+	Mover = nil;
+	
+	-- Bank
+	self:UnregisterEvent("BANKFRAME_OPENED");
+	
+	-- Guild
+	self:GUILDBANKFRAME_CLOSED();
+	self:UnregisterEvent("GUILDBANKFRAME_OPENED");
+end
+
+function mod:Pause()
+	paused = true;
+end
+
+function mod:Unpause()
+	paused = nil;
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Modules/Summary.lua	Thu Jan 06 20:05:30 2011 +0100
@@ -0,0 +1,603 @@
+local addon = select(2, ...); -- Get a reference to the main addon object
+local mod = addon:NewModule("Summary", "AceEvent-3.0", "AceTimer-3.0"); -- register a new module, Summary: resposible for building the summary window
+
+local unknownItemName = "Unknown (#%d)";
+
+local CACHE_ITEMS_TOTAL, CACHE_ITEMS_CURRENT, itemsCache = 0, 0, {};
+local AceGUI, cacheStart;
+
+local _G = _G; -- prevent looking up of the global table
+local printf, sgsub, supper, tinsert, pairs, ceil, GetItemInfo = _G.string.format, _G.string.gsub, _G.string.upper, _G.table.insert, _G.pairs, _G.ceil, _G.GetItemInfo; -- prevent looking up of the most used globals all the time
+
+function mod:OnEnable()
+	-- Register the summary specific widget
+	addon:GetModule("Widgets"):InlineGroupWithButton();
+	
+	AceGUI = LibStub("AceGUI-3.0");
+	
+	-- Register our own slash commands
+	-- /im summary
+	addon:RegisterSlash(function()
+		mod:BuildMain();
+		mod:Build();
+	end, { "s", "sum", "summary" }, "|Hfunction:InventoriumCommandHandler:summary|h|cff00fff7/im summary|r|h (or /im s) - Show the summary window containing an overview of all items within the groups tracked at this current character.");
+	
+	-- /im reset
+	addon:RegisterSlash(function()
+		if mod.frame then
+			mod.frame:SetWidth(700);
+			mod.frame:SetHeight(600);
+			
+			print("Resetting width and height of the summary frame.");
+		end
+	end, { "r", "reset" }, "|Hfunction:InventoriumCommandHandler:reset|h|cff00fff7/im reset|r|h (or /im r) - Reset the size of the summary frame.");
+end
+
+local function ShowTooltip(self)
+	-- If this function is called from a widget, self is the widget and self.frame the actual frame
+	local this = self.frame or self;
+	
+	GameTooltip:SetOwner(this, "ANCHOR_NONE");
+	GameTooltip:SetPoint("BOTTOM", this, "TOP");
+	GameTooltip:SetText(this.tooltipTitle, 1, .82, 0, 1);
+	
+	if type(this.tooltip) == "string" then
+		GameTooltip:AddLine(this.tooltip, 1, 1, 1, 1);
+	end
+	
+	GameTooltip:Show();
+end
+
+local function HideTooltip()
+	GameTooltip:Hide();
+end
+
+function mod:BuildMain()
+	LibStub("AceConfigDialog-3.0"):Close("InventoriumOptions");
+	
+	self:CloseFrame();
+	
+	-- Main Window
+	mod.frame = AceGUI:Create("Frame");
+	_G["InventoriumSummary"] = mod.frame; -- name the global frame so it can be put in the UISpecialFrames
+	mod.frame:SetTitle("Inventory Summary");
+	mod.frame:SetLayout("Fill");
+	mod.frame:SetCallback("OnClose", function(widget)
+		mod:CancelTimer(self.tmrUpdater, true);
+		mod:CloseFrame();
+	end);
+	mod.frame:SetWidth(addon.db.profile.defaults.summary.width);
+	mod.frame:SetHeight(addon.db.profile.defaults.summary.height);
+	mod.frame.OnWidthSet = function(_, width)
+		addon.db.profile.defaults.summary.width = width;
+	end;
+	mod.frame.OnHeightSet = function(_, height)
+		addon.db.profile.defaults.summary.height = height;
+	end;
+	
+	-- Close on escape
+	tinsert(UISpecialFrames, "InventoriumSummary");
+	
+	-- ScrollFrame child
+	mod.scrollFrame = AceGUI:Create("ScrollFrame");
+	mod.scrollFrame:SetLayout("Flow");
+	
+	mod.frame:AddChild(mod.scrollFrame);
+	
+	-- Reset items cache
+	table.wipe(itemsCache);
+end
+
+function mod:CloseFrame()
+	if mod.frame then
+		mod.frame:Release();
+		mod.frame = nil;
+		
+		-- Stop caching
+		
+		-- Stop timer
+		self:CancelTimer(self.tmrUpdater, true);
+		
+		-- Reset trackers
+		CACHE_ITEMS_TOTAL = 0;
+		CACHE_ITEMS_CURRENT = 0;
+	end
+end
+
+local sortMethod = "item";
+local sortDirectory = "ASC";
+local function ReSort(subject)
+	if sortMethod == subject then
+		sortDirectory = (sortDirectory == "ASC" and "DESC") or "ASC";
+	else
+		sortDirectory = "ASC";
+	end
+	sortMethod = subject;
+	
+	mod:Build();
+end
+
+-- From http://www.wowwiki.com/API_sort
+local function pairsByKeys (t, f)
+	local a = {}
+	for n in pairs(t) do tinsert(a, n) end
+		table.sort(a, f)
+	local i = 0      -- iterator variable
+	local iter = function ()   -- iterator function
+		i = i + 1
+		if a[i] == nil then return nil
+		else return a[i], t[a[i]]
+		end
+	end
+	return iter
+end
+
+function mod:Build()
+	local buildStartTime, times = GetTime(), {};
+	
+	-- We are going to add hunderds of widgets to this container, but don't want it to also cause hunderds of reflows, thus pause reflowing and just do it once when everything is prepared
+	-- This appears to be required for each container we wish to pause, so also do this for the contents
+	mod.scrollFrame:PauseLayout();
+	
+	mod.scrollFrame:ReleaseChildren();
+	
+	-- Refresh button
+	local btnRefresh = AceGUI:Create("Button");
+	btnRefresh:SetText("Refresh");
+	btnRefresh:SetRelativeWidth(.2);
+	btnRefresh:SetCallback("OnClick", function()
+		-- Reset items cache
+		table.wipe(itemsCache);
+		
+		-- Rebuild itemlist and start caching
+		mod:Build();
+	end);
+	btnRefresh:SetCallback("OnEnter", ShowTooltip);
+	btnRefresh:SetCallback("OnLeave", HideTooltip);
+	btnRefresh.frame.tooltipTitle = "Refresh Cache";
+	btnRefresh.frame.tooltip = "Refresh the list and recache the item counts and auction values.";
+	
+	mod.scrollFrame:AddChild(btnRefresh);
+	
+	local lblSpacer = AceGUI:Create("Label");
+	lblSpacer:SetRelativeWidth(.03);
+	
+	mod.scrollFrame:AddChild(lblSpacer);
+	
+	-- Speed slider
+	local sdrSpeed = AceGUI:Create("Slider");
+	sdrSpeed:SetLabel("Processing speed");
+	sdrSpeed:SetSliderValues(0.01, 5, 0.01); -- min, max, interval
+	sdrSpeed:SetIsPercent(true);
+	sdrSpeed:SetRelativeWidth(.3);
+	sdrSpeed:SetCallback("OnMouseUp", function(self, event, value)
+		addon.db.profile.defaults.summary.speed = ceil( ( ( value * 100 ) / 5) - .5 );
+		
+		CACHE_ITEMS_PER_UPDATE = addon.db.profile.defaults.summary.speed; -- max = 20, min = 1
+	end);
+	sdrSpeed:SetValue( addon.db.profile.defaults.summary.speed * 5 / 100 );
+	sdrSpeed:SetCallback("OnEnter", ShowTooltip);
+	sdrSpeed:SetCallback("OnLeave", HideTooltip);
+	sdrSpeed.frame.tooltipTitle = "Caching Processing Speed";
+	sdrSpeed.frame.tooltip = "Change the speed at which item counts and auction values are being cached. Higher is faster but may drastically reduce your FPS while caching.\n\nAnything above 100% will probably become uncomfortable.";
+	
+	mod.scrollFrame:AddChild(sdrSpeed);
+	
+	local lblSpacer = AceGUI:Create("Label");
+	lblSpacer:SetRelativeWidth(.23);
+	
+	mod.scrollFrame:AddChild(lblSpacer);
+	
+	-- Config button
+	--[[local btnConfig = AceGUI:Create("Button");
+	btnConfig:SetText("Config");
+	btnConfig:SetRelativeWidth(.2);
+	btnConfig:SetCallback("OnClick", function()
+		--TODO: Tidy up
+		SlashCmdList["INVENTORIUM"]("config");
+	end);
+	btnConfig:SetCallback("OnEnter", ShowTooltip);
+	btnConfig:SetCallback("OnLeave", HideTooltip);
+	btnConfig.frame.tooltipTitle = "Config";
+	btnConfig.frame.tooltip = "Click to open the config window. This will close the current window.";
+	
+	mod.scrollFrame:AddChild(btnConfig);]]
+	
+	local lblSpacer = AceGUI:Create("Label");
+	lblSpacer:SetRelativeWidth(.03);
+	
+	mod.scrollFrame:AddChild(lblSpacer);
+	
+	-- Queue all button
+	local btnQueueAll = AceGUI:Create("Button");
+	btnQueueAll:SetText("Queue All");
+	btnQueueAll:SetRelativeWidth(.2);
+	btnQueueAll:SetCallback("OnClick", function()
+		self:SendMessage("IM_QUEUE_ALL");
+	end);
+	btnQueueAll:SetCallback("OnEnter", ShowTooltip);
+	btnQueueAll:SetCallback("OnLeave", HideTooltip);
+	btnQueueAll.frame.tooltipTitle = "Queue all";
+	btnQueueAll.frame.tooltip = "Queue everything that requires restocking within every single visible group.";
+	
+	mod.scrollFrame:AddChild(btnQueueAll);
+	
+	times.init = ceil( ( GetTime() - buildStartTime ) * 1000 );
+	addon:Debug("Time spent legend: (init / sorting / preparing / building / all).");
+	
+	local playerName = UnitName("player");
+	
+	-- Go through all our stored groups
+	for groupName, values in pairsByKeys(addon.db.profile.groups, function(a, b) return a:lower() < b:lower(); end) do
+		local groupStartTime, groupTimes = GetTime(), {};
+		
+		local trackAt = addon:GetOptionByKey(groupName, "trackAtCharacters");
+		
+		
+		-- Does this group have any items and do we want to track it at this char?
+		if values.items and trackAt[playerName] then
+		
+			
+			-- Get group settings
+			local minLocalStock = addon:GetOptionByKey(groupName, "minLocalStock");
+			local minGlobalStock = addon:GetOptionByKey(groupName, "minGlobalStock");
+			local showWhenBelow = addon:GetOptionByKey(groupName, "summaryThresholdShow");
+			local priceThreshold = addon:GetOptionByKey(groupName, "priceThreshold");
+			local hideWhenBelowPriceThreshold = addon:GetOptionByKey(groupName, "summaryHidePriceThreshold");
+			local alwaysGetAuctionValue = addon:GetOptionByKey(groupName, "alwaysGetAuctionValue");
+			
+			-- Make group container
+			local iGroup = AceGUI:Create("InlineGroupWithButton");
+			iGroup:PauseLayout();
+			iGroup:SetTitle(groupName);
+			iGroup:SetFullWidth(true);
+			iGroup:SetLayout("Flow");
+			iGroup:MakeButton({
+				name = "Queue",
+				desc = "Queue all items in this group.",
+				exec = function()
+					print("Queueing all items within " .. groupName .. " craftable by the currently open profession.");
+					self:SendMessage("IM_QUEUE_GROUP", groupName);
+				end,
+			});
+			
+			
+			
+			-- Headers
+			
+			-- Itemlink
+			local lblItem = AceGUI:Create("InteractiveLabel");
+			lblItem:SetText("|cfffed000Item|r");
+			lblItem:SetFontObject(GameFontHighlight);
+			lblItem:SetRelativeWidth(.7);
+			lblItem:SetCallback("OnClick", function() ReSort("item"); end);
+			lblItem:SetCallback("OnEnter", ShowTooltip);
+			lblItem:SetCallback("OnLeave", HideTooltip);
+			lblItem.frame.tooltipTitle = "Item";
+			lblItem.frame.tooltip = "Sort on the item quality, then name.";
+			
+			iGroup:AddChild(lblItem);
+			
+			-- Local quantity
+			local lblLocal = AceGUI:Create("InteractiveLabel");
+			lblLocal:SetText("|cfffed000Loc.|r");
+			lblLocal:SetFontObject(GameFontHighlight);
+			lblLocal:SetRelativeWidth(.099);
+			lblLocal:SetCallback("OnClick", function() ReSort("local"); end);
+			lblLocal:SetCallback("OnEnter", ShowTooltip);
+			lblLocal:SetCallback("OnLeave", HideTooltip);
+			lblLocal.frame.tooltipTitle = "Local stock";
+			lblLocal.frame.tooltip = "Sort on the amount of items currently in local stock.";
+			
+			iGroup:AddChild(lblLocal);
+			
+			-- Current quantity
+			local lblQuantity = AceGUI:Create("InteractiveLabel");
+			lblQuantity:SetText("|cfffed000Cur.|r");
+			lblQuantity:SetFontObject(GameFontHighlight);
+			lblQuantity:SetRelativeWidth(.099);
+			lblQuantity:SetCallback("OnClick", function() ReSort("current"); end);
+			lblQuantity:SetCallback("OnEnter", ShowTooltip);
+			lblQuantity:SetCallback("OnLeave", HideTooltip);
+			lblQuantity.frame.tooltipTitle = "Current stock";
+			lblQuantity.frame.tooltip = "Sort on the amount of items currently in stock.";
+			
+			iGroup:AddChild(lblQuantity);
+			
+			-- Lowest value
+			local lblValue = AceGUI:Create("InteractiveLabel");
+			lblValue:SetText("|cfffed000Value|r");
+			lblValue:SetFontObject(GameFontHighlight);
+			lblValue:SetRelativeWidth(.099);
+			lblValue:SetCallback("OnClick", function() ReSort("value"); end);
+			lblValue:SetCallback("OnEnter", ShowTooltip);
+			lblValue:SetCallback("OnLeave", HideTooltip);
+			lblValue.frame.tooltipTitle = "Value";
+			lblValue.frame.tooltip = "Sort on the item auction value.";
+			
+			iGroup:AddChild(lblValue);
+			
+			
+			-- Retrieve items list
+			if not itemsCache[groupName] then
+				itemsCache[groupName] = {};
+			end
+			 
+			if #(itemsCache[groupName]) == 0 then
+				-- If the item cache is empty
+				for itemId, _ in pairs(values.items) do
+					local newItemData = addon.ItemData:New(itemId);
+					
+					-- if no price threshold is set for this item and you don't want to always get the auction value, then don't look it up either
+					if priceThreshold == 0 and not alwaysGetAuctionValue then
+						newItemData.value = -4;
+					end
+					
+					tinsert(itemsCache[groupName], newItemData);
+					
+					CACHE_ITEMS_TOTAL = CACHE_ITEMS_TOTAL + 1;
+				end
+			end
+			
+			groupTimes.init = ceil( ( GetTime() - groupStartTime ) * 1000 );
+			
+			
+			
+			-- Sort items
+			table.sort(itemsCache[groupName], function(a, b)
+				local aRarity = a.rarity or 1;
+				local bRarity = b.rarity or 1;
+				
+				if sortMethod == "item" and aRarity == bRarity then
+					-- Do a name-compare for items of the same rarity
+					-- Otherwise epics first, then junk
+					
+					local aName = a.name or printf(unknownItemName, a.id);
+					local bName = b.name or printf(unknownItemName, b.id);
+					
+					if sortDirectory == "ASC" then
+						return supper(aName) < supper(bName);
+					else
+						return supper(aName) > supper(bName);
+					end
+				elseif sortMethod == "item"  then
+					if sortDirectory == "ASC" then
+						return aRarity > bRarity; -- the comparers were reversed because we want epics first
+					else
+						return aRarity < bRarity; -- the comparers were reversed because we want epics first
+					end
+				elseif sortMethod == "current" then
+					if sortDirectory == "ASC" then
+						return a.globalCount < b.globalCount;
+					else
+						return a.globalCount > b.globalCount;
+					end
+				elseif sortMethod == "local" then
+					if sortDirectory == "ASC" then
+						return a.localCount < b.localCount;
+					else
+						return a.localCount > b.localCount;
+					end
+				elseif sortMethod == "value" then
+					if sortDirectory == "ASC" then
+						return a.value < b.value;
+					else
+						return a.value > b.value;
+					end
+				end
+			end);
+			
+			groupTimes.sorting = ceil( ( GetTime() - groupStartTime ) * 1000 );
+			
+			
+			
+			
+			-- Show itemslist
+			for i, item in pairs(itemsCache[groupName]) do
+				-- Go through all items for this group
+				
+				if (( item.globalCount / minGlobalStock ) < showWhenBelow or ( item.localCount / minLocalStock ) < showWhenBelow) and (not hideWhenBelowPriceThreshold or priceThreshold == 0 or item.value < 0 or item.value >= priceThreshold) then
+																-- if the option "hide when below threshold" is disabled or no price threshold is set or the value is above the price threshold or the value could not be determined, proceed
+					
+					local btnItemLink = AceGUI:Create("ItemLinkButton");
+					btnItemLink:SetUserData("exec", function(_, itemId, _, buttonName)
+						local itemName, itemLink = GetItemInfo(itemId);
+						
+						if buttonName == "LeftButton" and IsShiftKeyDown() and itemLink then
+							ChatEdit_InsertLink(itemLink);
+						elseif buttonName == "LeftButton" and IsAltKeyDown() and itemName and AuctionFrame and AuctionFrame:IsShown() and AuctionFrameBrowse then
+							-- Start search at page 0
+							AuctionFrameBrowse.page = 0;
+							
+							-- Set the search field (even though we could use "ChatEdit_InsertLink", this ensures the right field is updated)
+							BrowseName:SetText(itemName)
+							
+							QueryAuctionItems(itemName, nil, nil, 0, 0, 0, 0, 0, 0);
+						end
+					end);
+					btnItemLink:SetRelativeWidth(.7);
+					btnItemLink:SetItem(item);
+					
+					iGroup:AddChild(btnItemLink);
+					
+					-- Local quantity
+					local lblLocal = AceGUI:Create("Label");
+					lblLocal:SetText(self:DisplayItemCount(item.localCount, minLocalStock));
+					lblLocal:SetRelativeWidth(.099);
+					
+					iGroup:AddChild(lblLocal);
+					
+					-- Current quantity
+					local lblQuantity = AceGUI:Create("Label");
+					lblQuantity:SetText(self:DisplayItemCount(item.globalCount, minGlobalStock));
+					lblQuantity:SetRelativeWidth(.099);
+					
+					iGroup:AddChild(lblQuantity);
+					
+					-- Value
+					local lblValue = AceGUI:Create("Label");
+					lblValue:SetText(self:DisplayMoney(item.value, priceThreshold));
+					lblValue:SetRelativeWidth(.099);
+					
+					iGroup:AddChild(lblValue);
+					
+					-- Remember references to the value and current fields so we can fill them later
+					item.set.value = lblValue;
+					item.set.globalCount = lblQuantity;
+					item.set.localCount = lblLocal;
+				end
+			end
+			
+			groupTimes.preparing = ceil( ( GetTime() - groupStartTime ) * 1000 );
+			
+			iGroup:ResumeLayout();
+			mod.scrollFrame:AddChild(iGroup); -- this can take up to .5 seconds, might need to look into again at a later time
+			
+			groupTimes.building = ceil( ( GetTime() - groupStartTime ) * 1000 );
+		end
+		
+		if groupStartTime and groupTimes then
+			addon:Debug(printf("Building of %s took %d ms (%d / %d / %d / %d / %d).", groupName, ceil( ( GetTime() - groupStartTime ) * 1000 ), groupTimes.init or 0, groupTimes.sorting or 0, groupTimes.preparing or 0, groupTimes.building or 0, ceil( ( GetTime() - buildStartTime ) * 1000 )));
+		end
+	end
+	
+	mod.scrollFrame:ResumeLayout();
+	mod.scrollFrame:DoLayout();
+	
+	addon:Debug(printf("Done building summary after %d ms.", ceil( ( GetTime() - buildStartTime ) * 1000 )));
+	
+	if CACHE_ITEMS_TOTAL > 0 then
+		cacheStart = GetTime();
+		self:CancelTimer(self.tmrUpdater, true);
+		self.tmrUpdater = self:ScheduleRepeatingTimer("UpdateNextItem", .01); -- Once every 100 frames (or once every x frames if you have less than 100 FPS, basically, once every frame)
+	end
+end
+
+function mod:UpdateNextItem()
+	local i = 0;
+	
+	for groupName, items in pairs(itemsCache) do
+		local minGlobalStock = addon:GetOptionByKey(groupName, "minGlobalStock");
+		local minLocalStock = addon:GetOptionByKey(groupName, "minLocalStock");
+		local priceThreshold = addon:GetOptionByKey(groupName, "priceThreshold");
+		
+		for _, item in pairs(items) do
+			if item.globalCount == -3 or item.localCount == -3 or item.value == -3 then
+				if item.globalCount == -3 then
+					-- Only if item count was queued, update it
+					item.globalCount = addon:GetItemCount(item.id, groupName);
+					if item.set.globalCount and item.set.globalCount.SetText then
+						item.set.globalCount:SetText(self:DisplayItemCount(item.globalCount, minGlobalStock));
+					end
+				end
+				
+				if item.localCount == -3 then
+					-- Only if item count was queued, update it
+					item.localCount = addon:GetLocalItemCount(item.id, groupName);
+					if item.set.localCount and item.set.localCount.SetText then
+						item.set.localCount:SetText(self:DisplayItemCount(item.localCount, minLocalStock));
+					end
+				end
+				
+				if item.value == -3 then
+					-- Only if item value was queued, update it
+					
+					-- The itemlink might not have been loaded yet in which case we retry
+					item.link = item.link or select(2, GetItemInfo(item.id));
+					
+					if item.link then
+						item.value = addon:GetAuctionValue(item.link, groupName);
+						if item.set.value and item.set.value.SetText then
+							item.set.value:SetText(self:DisplayMoney(item.value, priceThreshold));
+						end
+					end
+				end
+				
+				i = i + 1;
+				CACHE_ITEMS_CURRENT = CACHE_ITEMS_CURRENT + 1;
+				
+				if mod.frame then
+					mod.frame:SetStatusText(printf("Caching auction values and item-counts... %d%% has already been processed.", floor(CACHE_ITEMS_CURRENT / CACHE_ITEMS_TOTAL * 100)));
+				end
+				
+				if i >= addon.db.profile.defaults.summary.speed then
+					return;
+				end
+			end
+		end
+	end
+	
+	-- Reset trackers
+	CACHE_ITEMS_TOTAL = 0;
+	CACHE_ITEMS_CURRENT = 0;
+	
+	-- Stop timer
+	self:CancelTimer(self.tmrUpdater, true);
+		
+	-- Rebuild list so hidden items due to too low prices get added
+	self:Build();
+	
+	-- Announce
+	mod.frame:SetStatusText("All required prices and itemcounts have been cached. This process took " .. ceil(GetTime() - cacheStart) .. " seconds.");
+	
+	-- Forget time
+	cacheStart = nil;
+end
+
+function mod:ColorCode(num, required)
+	local percentage = ( num / required );
+	
+	if percentage >= addon.db.profile.defaults.colors.green then
+		return printf("|cff00ff00%d|r", num);
+	elseif percentage >= addon.db.profile.defaults.colors.yellow then
+		return printf("|cffffff00%d|r", num);
+	elseif percentage >= addon.db.profile.defaults.colors.orange then
+		return printf("|cffff9933%d|r", num);
+	elseif percentage >= addon.db.profile.defaults.colors.red then
+		return printf("|cffff0000%d|r", num);
+	else
+		return num;
+	end
+end
+
+function mod:DisplayMoney(value, priceThreshold)
+	if value == -1 then
+		return "|cff0000ffNone up|r";
+	elseif value == -2 then
+		return "|cff0000ffNo AH mod|r";
+	elseif value == -3 then
+		return "|cffffff00Unknown|r";
+	elseif value == -4 then
+		return "|cff00ff00-|r";
+	elseif value == -5 then
+		return "|cffff9933Error|r";
+	elseif priceThreshold and value < priceThreshold then
+		return printf("|cffaaaaaa%s|r", sgsub(addon:ReadableMoney(value or 0, true), "|([a-fA-F0-9]+)([gsc]+)|r", "%2"));
+	else
+		return addon:ReadableMoney(value or 0, true);
+	end
+end
+
+function mod:DisplayItemCount(value, minimumStock)
+	if value == -1 then
+		return "|cffffff00Unknown|r";
+	elseif value == -3 then
+		return "|cffffff00Unknown|r";
+	else
+		return printf("%s / %d", self:ColorCode(value, minimumStock), minimumStock);
+	end
+end
+
+function mod:NumberFormat(num)
+	local formatted = sgsub(num, "(%d)(%d%d%d)$", "%1,%2", 1);
+	
+	while true do
+		formatted, matches = sgsub(formatted, "(%d)(%d%d%d),", "%1,%2,", 1);
+		
+		if matches == 0 then
+			break;
+		end
+	end
+	
+	return formatted;
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Modules/Widgets.lua	Thu Jan 06 20:05:30 2011 +0100
@@ -0,0 +1,213 @@
+local addon = select(2, ...);
+local Widgets = addon:NewModule("Widgets");
+
+local AceGUI = LibStub("AceGUI-3.0");
+
+function Widgets:ItemLinkButton()
+	--[[
+	[ ItemLinkButton ]
+	This custom widget has to show an icon with the item link next to it.
+	Upon hover it must show the item tooltip.
+	Upon click it must execute the function provided through user data.
+	
+	UserData: itemId, onClickEvent
+	
+	OnEnter: tooltip show
+	OnLeave: tooltip hide
+	OnClick: UserData.onClickEvent
+	]]
+	
+	local widgetType = "ItemLinkButton";
+	local widgetVersion = 1;
+	
+	local function Constructor()
+	    local widget = AceGUI:Create("InteractiveLabel");
+	    widget.type = widgetType;
+	    
+	    -- We overwrite the OnAcquire as we want to set our callbacks even
+	    -- when the widget is re-used from the widget pool
+	    widget.originalOnAcquire = widget.OnAcquire;
+	    widget.OnAcquire = function(self, ...)
+		    
+		    
+	    	-- We overwrite the setcallback because we don't want anything else 
+	    	-- to overwrite our OnEnter, OnLeave and OnClick events
+	    	-- which would be done by the AceConfigDialog after a widget gets re-used
+		    if not self.originalSetCallBack then
+			    self.originalSetCallBack = self.SetCallback;
+			    self.SetCallback = function(this, event, func, ...)
+			    	if event == "OnEnter" or event == "OnLeave" or event == "OnClick" then
+			    		-- Don't allow overwriting of these events
+			    		return;
+			    	elseif event == "CustomOnEnter" then
+			    		return this.originalSetCallBack(this, "OnEnter", func, ...);
+			    	elseif event == "CustomOnLeave" then
+			    		return this.originalSetCallBack(this, "OnLeave", func, ...);
+			    	elseif event == "CustomOnClick" then
+			    		return this.originalSetCallBack(this, "OnClick", func, ...);
+			    	else
+			    		return this.originalSetCallBack(this, event, func, ...);
+			    	end
+			    end;
+		    end
+		    
+		    
+		    
+		    -- Set our own events, since we disabled the normal event-names, we'll call them our custom versions
+		    self:SetCallback("CustomOnEnter", function(this)
+		    	local itemData = this:GetUserData("ItemData");
+		    	
+		    	if itemData then
+			    	GameTooltip:SetOwner(this.frame, "ANCHOR_TOPRIGHT");
+			    	GameTooltip:SetHyperlink(("item:%d"):format(itemData.id));
+					GameTooltip:Show();
+		    	end
+		    end);
+		    self:SetCallback("CustomOnLeave", function(this)
+		    	GameTooltip:Hide();
+		    end);
+		    self:SetCallback("CustomOnClick", function(this, ...)
+		    	-- Below is used in child widgets to prepare for onclick
+		    	if this.OnClick then
+		    		this.OnClick(this, ...);
+		    	end
+		    	
+		    	local func = this:GetUserData("exec");
+		    	local itemData = this:GetUserData("ItemData");
+		    	
+		    	if func then
+		    		-- If this is a config option we will need the group id
+		    		local path = this:GetUserData("path");
+		    		local groupId = (path and path[2]) or nil;
+		    		
+		    		func(groupId, itemData, ...);
+		    	end
+		    end);
+		    
+		    
+		    
+		    -- Then also do whatever it wanted to do
+		    self.originalOnAcquire(self, ...);
+	    end;
+	    
+	    -- Remember the original SetText as this might get overwritten by the config-widget
+	    widget.originalSetText = widget.SetText;
+	    
+	    widget.SetItem = function(self, item)
+ 		   	self:SetUserData("ItemData", item);
+ 		   	
+	   		-- Put the icon in front of it
+			self:SetImage(item.icon);
+			-- Standardize the size
+			self:SetImageSize(16, 16);
+    		
+			-- Make readable font
+    		self:SetFontObject(GameFontHighlight);
+    		
+    		if not item.link then
+    			-- If no item link was cached, give it another try
+    			
+				local itemName, itemLink, itemRarity = GetItemInfo(item.id);
+    			
+				-- item is a table, so passed by reference, so updatable
+				item.name = itemName;
+				item.link = itemLink;
+				item.rarity = itemRarity;
+    		end
+    		
+    		-- We don't want to set the itemId as text, but rather the item link, so get that.
+    		local itemLink = item.link or ("Unknown (#%d)"):format(item.id);
+    		
+    		self:originalSetText(itemLink);
+	    end;
+		
+		return widget;
+	end
+    
+	AceGUI:RegisterWidgetType(widgetType, Constructor, widgetVersion);
+end
+
+function Widgets:ConfigItemLinkButton()
+	-- Define out custom item link button widget
+	-- This will be called as if it's an input element, we overwrite some of the related functions which are called for default input fields
+	
+	local widgetType = "ConfigItemLinkButton";
+	local widgetVersion = 1;
+	
+	-- Empty function for disabling functions
+	local function Dummy() end
+	
+	-- Makes an instance of our ItemLinkButton widget
+	local function GetItemLinkButton()
+	    local widget = AceGUI:Create("ItemLinkButton");
+	    widget.type = widgetType;
+	    
+	    -- We can only provide custom widgets for input, select and multiselect fields
+	    -- Input being the simplest, we use that - however, it provides two parameters: label and text. We only need one, disable the other.
+	    widget.SetLabel = Dummy;
+	    
+	    -- SetText is called when this button is being created and contains the itemId
+	    -- Forward that itemId to the ItemLinkButton
+	    widget.SetText = function(self, value, ...)
+	    	if value and tonumber(value) then
+				local newItemData = addon.ItemData:New(tonumber(value));
+		    	
+	 		   	self:SetItem(newItemData);
+	    	end
+		end;
+		
+		widget.OnClick = function(self, ...)
+			local option = self:GetUserData("option");
+	    	
+	    	if option and option.set then
+				self:SetUserData("exec", option.set);
+			end
+		end;
+	    
+	    return widget;
+	end
+    
+	AceGUI:RegisterWidgetType(widgetType, GetItemLinkButton, widgetVersion);
+end
+
+function Widgets:InlineGroupWithButton()
+	-- Register InlineGroupWithButton-widget
+	-- This widget adds a button next to the header of an inline group
+	-- SetPoint doesn't seem usable within AceGUI.
+	
+	local widgetType = "InlineGroupWithButton";
+	local widgetVersion = 1;
+
+	local function Constructor() 
+	    local widget = AceGUI:Create("InlineGroup");
+	    widget.type = widgetType;
+	    
+	    widget.MakeButton = function(self, buttonSettings)
+			if type(buttonSettings) == "table" then
+				if not self.btnQueue then
+					-- Because widgets are re-used, we don't want to recreate this button
+					self.btnQueue = CreateFrame("Button", nil, self.frame, "UIPanelButtonTemplate");
+					self.btnQueue:SetHeight(22);
+					self.btnQueue:SetWidth(120);
+				end
+				self.btnQueue:SetText(buttonSettings.name);
+				self.btnQueue:SetPoint("TOPRIGHT", self.frame, "TOPRIGHT", -10, 5);
+				
+				-- Triggers
+				self.btnQueue:SetScript("OnClick", buttonSettings.exec);
+				
+				-- Tooltip
+				self.btnQueue.tooltipTitle = buttonSettings.name;
+				self.btnQueue.tooltip = buttonSettings.desc or "";
+				self.btnQueue:SetScript("OnEnter", ShowTooltip);
+				self.btnQueue:SetScript("OnLeave", HideTooltip);
+			else
+				error("settings must be a table - usage: MakeButton(table);");
+			end
+	    end;
+		
+	    return widget;
+	end
+    
+	AceGUI:RegisterWidgetType(widgetType, Constructor, widgetVersion);
+end
\ No newline at end of file
--- a/Mover.lua	Thu Jan 06 11:04:55 2011 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,394 +0,0 @@
-local addon = select(2, ...);
-local mod = addon:NewModule("Mover", "AceEvent-3.0", "AceTimer-3.0");
-
-local Scanner;
-local queuedMoves = {}; -- table storing all queued moves before BeginMove is called
-local combinedMoves = {}; -- table storing all combined moves (with source and target) that is to be processed by the actual mover in the order of the index (1 to #)
-local movesSource;
-
-function mod:AddMove(itemId, amount)
-	table.insert(queuedMoves, {
-		id = itemId,
-		num = amount,
-	});
-end
-
-function mod:HasMoves()
-	return (#queuedMoves ~= 0);
-end
-
-function mod:BeginMove(location, onFinish)
-	addon:Debug("BeginMove");
-	
-	-- Find the outgoing moves
-	-- We need the source container and slot, find all the requires sources and put them in a list which we go through later to find matching targets
-	
-	-- Get a list of items in the source container
-	local sourceContents = Scanner:CacheLocation(location, false);
-	
-	local outgoingMoves = {};
-
-	addon:Debug(#queuedMoves .. " moves were queued.");
-	
-	for _, singleMove in pairs(queuedMoves) do
-		local sourceItem = sourceContents[singleMove.id];
-		if not sourceItem then
-			print("Can't move " .. IdToItemLink(singleMove.id) .. ", non-existant in source");
-		else
-			-- We want to move the smallest stacks first to keep stuff pretty (and minimize space usage, splitting a stack takes 2 slots, moving something only 1)
-			table.sort(sourceItem.locations, function(a, b)
-				return a.count < b.count;
-			end);
-			
-			for _, itemLocation in pairs(sourceItem.locations) do
-				-- if this location has more items than we need, only move what we need, otherwise move everything in this stack
-				local movingNum = ((itemLocation.count > singleMove.num and singleMove.num) or itemLocation.count);
-				
-				table.insert(outgoingMoves, {
-					itemId = singleMove.id,
-					num = movingNum,
-					container = itemLocation.container,
-					slot = itemLocation.slot,
-				});
-				
-				singleMove.num = (singleMove.num - movingNum);
-				
-				if singleMove.num == 0 then
-					-- If we have prepared everything we wanted, go to the next queued move
-					break; -- stop the locations-loop
-				end
-			end
-		end
-	end
-
-	addon:Debug(#outgoingMoves .. " outgoing moves are possible.");
-	
-	-- No longer needed
-	table.wipe(queuedMoves);
-	
-	-- Process every single outgoing move and find fitting targets
-	
-	-- Get a list of items already in the target container
-	local targetContents = Scanner:CacheLocation(addon.Locations.Bag, false);
-	
-	-- Find all empty slots
-	
-	local emptySlots = {};
-	
-	local start = 0;
-	local stop = NUM_BAG_SLOTS;
-	
-	-- Go through all our bags, including the backpack
-	for bagId = start, stop do
-		-- Go through all our slots
-		for slotId = 1, GetContainerNumSlots(bagId) do
-			local itemId = GetContainerItemID(bagId, slotId); -- we're scanning our local bags here, so no need to get messy with guild bank support
-			
-			if not itemId then
-				table.insert(emptySlots, {
-					container = bagId,
-					slot = slotId,
-				});
-			end
-		end
-	end
-
-	addon:Debug(#emptySlots .. " empty slots are available.");
-	
-	-- Remember where we're moving from
-	movesSource = location;
-	
-	while #outgoingMoves ~= 0 do
-		-- A not equal-comparison should be quicker than a larger/smaller than-comparison
-		
-		for _, outgoingMove in pairs(outgoingMoves) do
-			-- itemId  will be set to nil when this outgoing move was processed - sanity check
-			if outgoingMove.itemId then
-				local targetItem = targetContents[outgoingMove.itemId];
-				
-				if not targetItem then
-					-- grab an empty slot
-					-- make new instance of ItemMove
-					-- populate targetContents with it so future moves of this item can be put on top of it if this isn't a full stack
-					
-					local firstAvailableSlot = emptySlots[1];
-					
-					if not firstAvailableSlot then
-						print("Bags are full. Skipping " .. IdToItemLink(outgoingMove.itemId) .. ".");
-						
-						outgoingMove.itemId = nil; -- remove this record from the outgoingMoves-table
-					else
-						table.insert(combinedMoves, {
-							itemId = outgoingMove.itemId,
-							num = outgoingMove.num,
-							sourceContainer = outgoingMove.container,
-							sourceSlot = outgoingMove.slot,
-							targetContainer = firstAvailableSlot.container,
-							targetSlot = firstAvailableSlot.slot,
-						});
-						
-						-- We filled an empty slot so the target contents now has one more item,
-						-- make a new instance of the ItemMove class so any additional items with this id can be stacked on top of it
-						local itemMove = addon.ContainerItem:New();
-						itemMove:AddLocation(firstAvailableSlot.container, firstAvailableSlot.slot, outgoingMove.num);
-						targetContents[outgoingMove.itemId] = itemMove;
-						
-						table.remove(emptySlots, 1); -- no longer empty
-						
-						outgoingMove.num = 0; -- nothing remaining - sanity check
-						outgoingMove.itemId = nil; -- remove this record from the outgoingMoves-table
-					end
-				else
-					-- Find the maximum stack size for this item
-					local itemStackCount = select(8, GetItemInfo(outgoingMove.itemId));
-					
-					-- We want to move to the largest stacks first to keep stuff pretty
-					table.sort(targetItem.locations, function(a, b)
-						return a.count > b.count;
-					end);
-					
-					for _, itemLocation in pairs(targetItem.locations) do
-						if itemLocation.count < itemStackCount and outgoingMove.num > 0 then
-							-- Check if this stack isn't already full (and we still need to move this item)
-							
-							local remainingSpace = (itemStackCount - itemLocation.count);
-							if remainingSpace > outgoingMove.num then
-								-- Enough room to move this entire stack
-								-- Deposit this item and then forget this outgoing move as everything in it was processed
-								
-								table.insert(combinedMoves, {
-									itemId = outgoingMove.itemId,
-									num = outgoingMove.num,
-									sourceContainer = outgoingMove.container,
-									sourceSlot = outgoingMove.slot,
-									targetContainer = itemLocation.container,
-									targetSlot = itemLocation.slot,
-								});
-								
-								itemLocation.count = (itemLocation.count + outgoingMove.num);
-								outgoingMove.count = 0; -- nothing remaining
-								outgoingMove.itemId = nil; -- remove this record from the outgoingMoves-table
-								break; -- stop the locations-loop
-							else
-								-- Deposit this item but don't remove the outgoing move as there are some items left to move
-								
-								table.insert(combinedMoves, {
-									itemId = outgoingMove.itemId,
-									num = outgoingMove.num,
-									sourceContainer = outgoingMove.container,
-									sourceSlot = outgoingMove.slot,
-									targetContainer = itemLocation.container,
-									targetSlot = itemLocation.slot,
-								});
-								
-								-- The target will be full when we complete, but the source will still have remaining items left to be moved
-								itemLocation.count = itemStackCount;
-								outgoingMove.num = (outgoingMove.num - remainingSpace);
-							end
-						end
-					end
-					
-					if outgoingMove.num > 0 then
-						-- We went through all matching items and checked their stack sizes if we could move this there, no room available
-						-- So forget about the target item (even though it may just have full locations, these are useless anyway) and the next loop move it onto an empty slot
-						targetItem = nil;
-					end
-				end
-			end
-		end
-		
-		-- Loop through the array to find items that should be removed, start with the last element or the loop would break
-		local numOutgoingMoves = #outgoingMoves; -- since LUA-tables start at an index of 1, this is actually an existing index (outgoingMoves[#outgoingMoves] would return a value)
-		while numOutgoingMoves ~= 0 do
-			-- A not equal-comparison should be quicker than a larger/smaller than-comparison
-			
-			-- Check if the item id is nil, this is set to nil when this outgoing move has been processed
-			if not outgoingMoves[numOutgoingMoves].itemId then
-				-- Remove this element from the array
-				table.remove(outgoingMoves, numOutgoingMoves);
-			end
-			
-			-- Proceed with the next element (or previous considering we're going from last to first)
-			numOutgoingMoves = (numOutgoingMoves - 1);
-		end
-	end
-
-	addon:Debug(#combinedMoves .. " moves should be possible.");
-	
-	-- No longer needed
-	table.wipe(emptySlots);
-	
-	self:ProcessMove();
-	
-	-- Even though we aren't completely done yet, allow requeueing
-	onFinish();
-end
-
-if not table.reverse then
--- 	table.reverse = function(orig)
--- 		local temp = CopyTable(orig);
--- 		local origLength = #temp;
--- 		for i = 1, origLength do
--- 			orig[(origLength - i + 1)] = temp[i];
--- 		end
--- 	end
-	table.reverse = function(orig)
-		local temp = {};
-		local origLength = #orig;
-		for i = 1, origLength do
-			temp[(origLength - i + 1)] = orig[i];
-		end
-		
--- 		-- Update the original table (can't do orig = temp as that would change the reference-link instead of the original table)
--- 		for i, v in pairs(temp) do
--- 			orig[i] = v;
--- 		end
-		return temp; -- for speed we choose to do a return instead
-	end
-end
-
-function mod:ProcessMove()
-	addon:Debug("ProcessMove");
-	
-	if #combinedMoves == 0 then
-		print("Nothing to move.");
-		
-		self:Abort();
-		
-		return;
-	end
-	
-	self:RegisterEvent("BAG_UPDATE");
-	self:RegisterEvent("UI_ERROR_MESSAGE");
-	
-	-- combinedMoves now has all moves in it (source -> target)
-	-- go through list, move everything inside it
-	-- add source and target to lists, if either is already in this list, skip the move
-	-- repeat every few seconds until we're completely done
-	
-	local sourceLocationsLocked = {};
-	local targetLocationsLocked = {};
-	
-	-- Reverse table, we need to go through it from last to first because we'll be removing elements, but we don't want the actions to be executed in a different order
-	combinedMoves = table.reverse(combinedMoves);
-	
-	local GetContainerItemID;
-	if movesSource == addon.Locations.Guild then
-		GetContainerItemID = GetGuildBankItemLink;
-	else
-		GetContainerItemID = GetContainerItemID;
-	end
-    
-	local combinedMovesOriginalLength = #combinedMoves;
-	local numCurrentMove = combinedMovesOriginalLength;
-	while numCurrentMove ~= 0 do
-		local move = combinedMoves[numCurrentMove];
-		
-		-- sourceContainer, sourceSlot, targetContainer, targetSlot, itemId, num
-		if move and (not sourceLocationsLocked[move.sourceContainer] or not sourceLocationsLocked[move.sourceContainer][move.sourceSlot]) and 
-			(not targetLocationsLocked[move.targetContainer] or not targetLocationsLocked[move.targetContainer][move.targetSlot]) then
-			
-			print("Moving " .. IdToItemLink(move.itemId));
-			
-			addon:Debug(("Moving %dx%s from (%d,%d) to (%d,%d)"):format(move.num, IdToItemLink(move.itemId), move.sourceContainer, move.sourceSlot, move.targetContainer, move.targetSlot));
-			
-			if GetContainerItemID(move.sourceContainer, move.sourceSlot) ~= move.itemId then
-				self:Abort("source changed", "Source (" .. move.sourceContainer .. "," .. move.sourceSlot .. ") is not " .. IdToItemLink(move.itemId));
-				return;
-			end
-			
-			-- Pickup stack
-			if movesSource == addon.Locations.Bank then
-				SplitContainerItem(move.sourceContainer, move.sourceSlot, move.num);
-			elseif movesSource == addon.Locations.Guild then
-				SplitGuildBankItem(move.sourceContainer, move.sourceSlot, move.num);
-			end
-			
-			-- Remember we picked this item up and thus it is now locked
-			if not sourceLocationsLocked[move.sourceContainer] then
-				sourceLocationsLocked[move.sourceContainer] = {};
-			end
-			sourceLocationsLocked[move.sourceContainer][move.sourceSlot] = true;
-			
-			if movesSource == addon.Locations.Guild or CursorHasItem() then -- CursorHasItem is always false if source is a guild tab
-				if GetContainerItemID(move.targetContainer, move.targetSlot) and GetContainerItemID(move.targetContainer, move.targetSlot) ~= move.itemId then
-					self:Abort("target changed", "Target (" .. move.targetContainer .. "," .. move.targetSlot .. ") is not " .. IdToItemLink(move.itemId) .. " nor empty");
-					return;
-				end
-				
-				-- And drop it (this is always a local bag so no need to do any guild-checks)
-				PickupContainerItem(move.targetContainer, move.targetSlot);
-				
-				-- Remember we dropped an item here and thus this is now locked
-				if not targetLocationsLocked[move.targetContainer] then
-					targetLocationsLocked[move.targetContainer] = {};
-				end
-				targetLocationsLocked[move.targetContainer][move.targetSlot] = true;
-				
-				-- This move was processed
-				table.remove(combinedMoves, numCurrentMove);
-			else
-				self:Abort("item disappeared from mouse", "Couldn't move " .. IdToItemLink(move.itemId) .. ", CursorHasItem() is false");
-				return;
-			end
-		end
-		
-		-- Proceed with the next element (or previous considering we're going from last to first)
-		numCurrentMove = (numCurrentMove - 1);
-	end
-	
-	addon:Debug((combinedMovesOriginalLength - #combinedMoves) .. " moves processed. " .. #combinedMoves .. " moves remaining.");
-	
-	if #combinedMoves == 0 then
-		print("Finished.");
-		
-		self:Abort();
-		
-		return;
-	end
-end
-
-local tmrProcessNext;
-function mod:BAG_UPDATE()
-	self:CancelTimer(tmrProcessNext, true); -- silent
-	tmrProcessNext = self:ScheduleTimer("ProcessMove", 1);
-end
-
-function IdToItemLink(itemId)
-	local itemLink = select(2, GetItemInfo(itemId));
-	itemLink = itemLink or "Unknown (" .. itemId .. ")";
-	return itemLink;
-end
-
-function mod:UI_ERROR_MESSAGE(e, errorMessage)
-	if errorMessage == ERR_SPLIT_FAILED then
-		self:Abort("splitting failed", "Splitting failed.");
-	end
-end
-
-function mod:Abort(simple, debugMsg)
-	if debugMsg then
-		addon:Debug("Aborting:" .. debugMsg);
-	end
-	if simple then
-		print("|cffff0000Aborting: " .. simple .. ".|r");
-	end
-	table.wipe(combinedMoves);
-	movesSource = nil;
-	
-	-- Stop timer
-	self:UnregisterEvent("BAG_UPDATE");
-	self:CancelTimer(tmrProcessNext, true); -- silent
-	
-	self:UnregisterEvent("UI_ERROR_MESSAGE");
-end
-
-function mod:OnEnable()
-	Scanner = addon:GetModule("Scanner");
-end
-
-function mod:OnDisable()
-	Scanner = nil;
-	
-	self:Abort();
-end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/AuctionAddons/AuctionAddons.xml	Thu Jan 06 20:05:30 2011 +0100
@@ -0,0 +1,10 @@
+<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="Auctionator.lua"/>
+	<Script file="Auctioneer.lua"/>
+	<Script file="AuctionLite.lua"/>
+	<Script file="AuctionMaster.lua"/>
+	<Script file="AuctionProfitMaster.lua"/>
+	<Script file="Others.lua"/>
+	<Script file="ZeroAuctions.lua"/>
+</Ui>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/AuctionAddons/AuctionLite.lua	Thu Jan 06 20:05:30 2011 +0100
@@ -0,0 +1,21 @@
+do
+	
+	local function GetValue(link)
+		local history = AuctionLite:GetHistoricalPriceById(itemID);
+		
+		return (history and hist.items > 0 and math.floor(hist.price)) or -1;
+	end
+	
+	local function IsEnabled()
+		return (AuctionLite and AuctionLite.GetHistoricalPriceById);
+	end
+	
+	local function OnSelect()
+		local addonName = "|r|cfffed000AuctionLite|r|cffff6600";
+		
+		print("|cffff6600" .. addonName .. " appears to not be working properly at this time. Please try another addon. This will be corrected in the future, sorry for the inconvenience.|r");
+	end
+	
+	IMRegisterPricingAddon("AuctionLite", GetValue, IsEnabled, OnSelect);
+	
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/AuctionAddons/AuctionMaster.lua	Thu Jan 06 20:05:30 2011 +0100
@@ -0,0 +1,16 @@
+do
+	
+	local function GetValue(link)
+		local _, _, _, lowerBuyout, _, _, numAuctions, _ = AucMasGetCurrentAuctionInfo(link, false, true);
+		--local avgBid, avgBuyout, lowerBid, lowerBuyout, upperBid, upperBuyout, numAuctions, numBuyouts
+		
+		return (numAuctions > 0 and lowerBuyout) or -1;
+	end
+	
+	local function IsEnabled()
+		return (AucMasGetCurrentAuctionInfo);
+	end
+	
+	IMRegisterPricingAddon("AuctionMaster", GetValue, IsEnabled);
+	
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/AuctionAddons/AuctionProfitMaster.lua	Thu Jan 06 20:05:30 2011 +0100
@@ -0,0 +1,28 @@
+do
+
+	local APM;
+	
+	local function GetValue(link)
+		return (APM and APM:GetModule("Scan"):GetLowestAuction(APM:GetSafeLink(link))) or -1;
+	end
+	
+	local function IsEnabled()
+		local isEnabled = (select(6, GetAddOnInfo("AuctionProfitMaster")) == nil);
+		
+		if isEnabled then
+			APM = LibStub("AceAddon-3.0"):GetAddon("AuctionProfitMaster");
+		end
+		
+		return isEnabled and APM;
+	end
+	
+	local function OnSelect()
+		local addonName = "|r|cfffed000AuctionProfitMaster|r|cffff6600";
+		
+		print("|cffff6600Using " .. addonName .. " as pricing addon is not recommended. Items at the auction house above the \"ignore stacks over\" will always be ignored (and thus not affect the displayed price) and if you are the only one posting, " .. addonName .. " will act as if there are no auctions up.|r");
+	end
+	
+	IMRegisterPricingAddon("AuctionProfitMaster", GetValue, IsEnabled, OnSelect);
+	
+end
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/AuctionAddons/Auctionator.lua	Thu Jan 06 20:05:30 2011 +0100
@@ -0,0 +1,13 @@
+do
+	
+	local function GetValue(link)
+		return Atr_GetAuctionBuyout(link) or -1;
+	end
+	
+	local function IsEnabled()
+		return (Atr_GetAuctionBuyout);
+	end
+	
+	IMRegisterPricingAddon("Auctionator", GetValue, IsEnabled);
+	
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/AuctionAddons/Auctioneer.lua	Thu Jan 06 20:05:30 2011 +0100
@@ -0,0 +1,21 @@
+do
+	
+	local function GetValue(link)
+		local imgSeen, _, _, _, _, lowBuy, _, _ = AucAdvanced.Modules.Util.SimpleAuction.Private.GetItems(link);
+		--local imgseen, image, matchBid, matchBuy, lowBid, lowBuy, aveBuy, aSeen 
+		
+		if imgSeen <= 0 then
+			-- No auctions up at this time
+			return -1;
+		end
+		
+		return lowBuy or -5;
+	end
+	
+	local function IsEnabled()
+		return (AucAdvanced ~= nil and AucAdvanced.Modules.Util.SimpleAuction ~= nil and AucAdvanced.Modules.Util.SimpleAuction.Private.GetItems ~= nil);
+	end
+	
+	IMRegisterPricingAddon("Auctioneer", GetValue, IsEnabled);
+	
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/AuctionAddons/Others.lua	Thu Jan 06 20:05:30 2011 +0100
@@ -0,0 +1,13 @@
+do
+	
+	local function GetValue(link)
+		return GetAuctionBuyout(link) or -1;
+	end
+	
+	local function IsEnabled()
+		return (GetAuctionBuyout);
+	end
+	
+	IMRegisterPricingAddon("Others", GetValue, IsEnabled);
+	
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/AuctionAddons/ZeroAuctions.lua	Thu Jan 06 20:05:30 2011 +0100
@@ -0,0 +1,28 @@
+do
+
+	local ZA;
+	
+	local function GetValue(link)
+		return (ZA and ZA:GetModule("Scan"):GetLowestAuction(ZA:GetSafeLink(link))) or -1;
+	end
+	
+	local function IsEnabled()
+		local isEnabled = (select(6, GetAddOnInfo("ZeroAuctions")) == nil);
+		
+		if isEnabled then
+			ZA = LibStub("AceAddon-3.0"):GetAddon("ZeroAuctions");
+		end
+		
+		return isEnabled and ZA;
+	end
+	
+	local function OnSelect()
+		local addonName = "|r|cfffed000ZeroAuctions|r|cffff6600";
+		
+		print("|cffff6600Using " .. addonName .. " as pricing addon is not recommended. Items at the auction house above the \"ignore stacks over\" will always be ignored (and thus not affect the displayed price) and if you are the only one posting, " .. addonName .. " will act as if there are no auctions up.|r");
+	end
+	
+	IMRegisterPricingAddon("ZeroAuctions", GetValue, IsEnabled, OnSelect);
+	
+end
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/CraftingAddons/AdvancedTradeSkillWindow.lua	Thu Jan 06 20:05:30 2011 +0100
@@ -0,0 +1,16 @@
+do
+	
+	local function Queue(tradeSkillIndex, amount)
+		local link = GetTradeSkillItemLink(tradeSkillIndex);
+		local itemName = link:match("%[([^%[%]]*)%]");
+		
+		return ATSW_AddJob(itemName, amount);
+	end
+	
+	local function IsEnabled()
+		return (ATSW_AddJob);
+	end
+	
+	IMRegisterCraftingAddon("AdvancedTradeSkillWindow", Queue, IsEnabled);
+	
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/CraftingAddons/Cauldron.lua	Thu Jan 06 20:05:30 2011 +0100
@@ -0,0 +1,39 @@
+do
+	
+	local function Queue(tradeSkillIndex, amount)
+		local tradeskillName, currentLevel, maxLevel = GetTradeSkillLine();
+		
+		local link = GetTradeSkillItemLink(tradeSkillIndex);
+		local itemName = link and link:match("%[([^%[%]]*)%]");
+		
+		if not itemName then
+			return;
+		end
+		
+		local skillInfo = Cauldron:GetSkillInfo(tradeskillName, itemName);
+	
+		CauldronQueue:AddItem(Cauldron.db.realm.userdata[Cauldron.vars.playername].queue, skillInfo, amount);
+			
+		Cauldron:UpdateQueue();
+		
+		-- update the shopping list
+		Cauldron:UpdateShoppingListFromQueue();
+		
+		Cauldron:UpdateButtons();
+		
+		return;
+	end
+	
+	local function IsEnabled()
+		return (Cauldron and CauldronQueue and CauldronQueue.AddItem);
+	end
+	
+	local function OnSelect()
+		local addonName = "|r|cfffed000Cauldron|r|cffff6600";
+		
+		print("|cffff6600" .. addonName .. " has not yet been tested. Please report any of your experiences.|r");
+	end
+	
+	IMRegisterCraftingAddon("Cauldron", Queue, IsEnabled, OnSelect);
+	
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/CraftingAddons/CraftingAddons.xml	Thu Jan 06 20:05:30 2011 +0100
@@ -0,0 +1,7 @@
+<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="AdvancedTradeSkillWindow.lua"/>
+	<Script file="Cauldron.lua"/>
+	<Script file="GnomeWorks.lua"/>
+	<Script file="Skillet.lua"/>
+</Ui>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/CraftingAddons/GnomeWorks.lua	Thu Jan 06 20:05:30 2011 +0100
@@ -0,0 +1,17 @@
+do
+	
+	local function Queue(tradeSkillIndex, amount)
+		local link = GetTradeSkillRecipeLink(tradeSkillIndex);
+		local recipeId = tonumber(link:match("|Henchant:([-0-9]+)|h"));
+		
+		GnomeWorks:ShowQueueList();
+		return GnomeWorks:AddToQueue(GnomeWorks.player, tradeSkillIndex, recipeId, amount);
+	end
+	
+	local function IsEnabled()
+		return (GnomeWorks and GnomeWorks.AddToQueue);
+	end
+	
+	IMRegisterCraftingAddon("GnomeWorks", Queue, IsEnabled);
+	
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/CraftingAddons/Skillet.lua	Thu Jan 06 20:05:30 2011 +0100
@@ -0,0 +1,17 @@
+do
+	
+	local function Queue(tradeSkillIndex, amount)
+		local link = GetTradeSkillRecipeLink(tradeSkillIndex);
+		local recipeId = tonumber(link:match("|Henchant:([-0-9]+)|h"));
+		
+		local newCommand = Skillet:QueueCommandIterate(recipeId, amount);
+		return Skillet:AddToQueue(newCommand);
+	end
+	
+	local function IsEnabled()
+		return (Skillet and Skillet.QueueCommandIterate and Skillet.AddToQueue);
+	end
+	
+	IMRegisterCraftingAddon("Skillet", Queue, IsEnabled);
+	
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/ItemCountAddons/Altoholic.lua	Thu Jan 06 20:05:30 2011 +0100
@@ -0,0 +1,19 @@
+do
+	
+	local function GetTotalCount(itemId)
+		return Altoholic:GetItemCount(itemId) or -1;
+	end
+	
+	local function IsEnabled()
+		return (Altoholic and Altoholic.GetItemCount);
+	end
+	
+	local function OnSelect()
+		local addonName = "|r|cfffed000Altoholic|r|cffff6600";
+		
+		print("|cffff6600Note: " .. addonName .. " can not provide local item data.|r");
+	end
+	
+	IMRegisterItemCountAddon("Altoholic", GetTotalCount, nil, IsEnabled, OnSelect);
+	
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/ItemCountAddons/DataStore (current account only).lua	Thu Jan 06 20:05:30 2011 +0100
@@ -0,0 +1,37 @@
+do
+	
+	local function GetTotalCount(itemId)
+		local realm = GetRealmName();
+		
+		local totalCount = 0;
+		
+		-- Process all charracters on this account (GetCharacters defaults to THIS_ACCOUNT)
+		for characterName, character in pairs(DataStore:GetCharacters(realm, nil)) do
+			-- Get only useful info (currency / gear shouldn't contain the stuff we are interested in)
+			local bag, bank = DataStore:GetContainerItemCount(character, itemId) or 0, 0;
+			local auctionHouse = DataStore:GetAuctionHouseItemCount(character, itemId) or 0;
+			local mail = DataStore:GetMailItemCount(character, itemId) or 0;
+			
+			totalCount = totalCount + bag + bank + auctionHouse + mail;
+		end
+		
+		return totalCount or -1;
+	end
+	
+	local function GetCharacterCount(itemId)
+		local character = DataStore:GetCharacter();
+		
+		local bag, bank = DataStore:GetContainerItemCount(character, itemId) or 0, 0;
+		local auctionHouse = DataStore:GetAuctionHouseItemCount(character, itemId) or 0;
+		local mail = DataStore:GetMailItemCount(character, itemId) or 0;
+		
+		return bag, bank, auctionHouse, mail;
+	end
+	
+	local function IsEnabled()
+		return (DataStore and DataStore.GetContainerItemCount and DataStore.GetAuctionHouseItemCount and DataStore.GetMailItemCount);
+	end
+	
+	IMRegisterItemCountAddon("DataStore (current account only)", GetTotalCount, GetCharacterCount, IsEnabled);
+	
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/ItemCountAddons/DataStore (with guilds).lua	Thu Jan 06 20:05:30 2011 +0100
@@ -0,0 +1,56 @@
+do
+	
+	local function GetTotalCount(itemId)
+		local realm = GetRealmName();
+		
+		local totalCount = 0;
+		
+		local guilds = {};
+		
+		-- Process all accounts
+		for accountName in pairs(DataStore:GetAccounts()) do
+		
+			-- Process all charracters
+			for characterName, character in pairs(DataStore:GetCharacters(realm, accountName)) do
+				-- Get only useful info (currency / gear shouldn't contain the stuff we are interested in)
+				local bag, bank = DataStore:GetContainerItemCount(character, itemId) or 0, 0;
+				local auctionHouse = DataStore:GetAuctionHouseItemCount(character, itemId) or 0;
+				local mail = DataStore:GetMailItemCount(character, itemId) or 0;
+				
+				totalCount = totalCount + bag + bank + auctionHouse + mail;
+			end
+			
+			-- Process all guilds
+			for guildName, guild in pairs(DataStore:GetGuilds(realm, accountName)) do
+				if not guilds[guildName] then
+					-- We don't want itemcounts from a single guild to be counted twice, so first to present data wins
+					
+					guilds[guildName] = true;
+					
+					local guild = DataStore:GetGuildBankItemCount(guild, itemId) or 0;
+				
+					totalCount = totalCount + guild;
+				end
+			end
+		end
+		
+		return totalCount or -1;
+	end
+	
+	local function GetCharacterCount(itemId)
+		local character = DataStore:GetCharacter();
+		
+		local bag, bank = DataStore:GetContainerItemCount(character, itemId) or 0, 0;
+		local auctionHouse = DataStore:GetAuctionHouseItemCount(character, itemId) or 0;
+		local mail = DataStore:GetMailItemCount(character, itemId) or 0;
+		
+		return bag, bank, auctionHouse, mail;
+	end
+	
+	local function IsEnabled()
+		return (DataStore and DataStore.GetContainerItemCount and DataStore.GetAuctionHouseItemCount and DataStore.GetMailItemCount);
+	end
+	
+	IMRegisterItemCountAddon("DataStore (with guilds)", GetTotalCount, GetCharacterCount, IsEnabled);
+	
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/ItemCountAddons/DataStore (without guilds).lua	Thu Jan 06 20:05:30 2011 +0100
@@ -0,0 +1,41 @@
+do
+	
+	local function GetTotalCount(itemId)
+		local realm = GetRealmName();
+		
+		local totalCount = 0;
+		
+		-- Process all accounts
+		for accountName in pairs(DataStore:GetAccounts()) do
+		
+			-- Process all charracters
+			for characterName, character in pairs(DataStore:GetCharacters(realm, accountName)) do
+				-- Get only useful info (currency / gear shouldn't contain the stuff we are interested in)
+				local bag, bank = DataStore:GetContainerItemCount(character, itemId) or 0, 0;
+				local auctionHouse = DataStore:GetAuctionHouseItemCount(character, itemId) or 0;
+				local mail = DataStore:GetMailItemCount(character, itemId) or 0;
+				
+				totalCount = totalCount + bag + bank + auctionHouse + mail;
+			end
+		end
+		
+		return totalCount or -1;
+	end
+	
+	local function GetCharacterCount(itemId)
+		local character = DataStore:GetCharacter();
+		
+		local bag, bank = DataStore:GetContainerItemCount(character, itemId) or 0, 0;
+		local auctionHouse = DataStore:GetAuctionHouseItemCount(character, itemId) or 0;
+		local mail = DataStore:GetMailItemCount(character, itemId) or 0;
+		
+		return bag, bank, auctionHouse, mail;
+	end
+	
+	local function IsEnabled()
+		return (DataStore and DataStore.GetContainerItemCount and DataStore.GetAuctionHouseItemCount and DataStore.GetMailItemCount);
+	end
+	
+	IMRegisterItemCountAddon("DataStore (without guilds)", GetTotalCount, GetCharacterCount, IsEnabled);
+	
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/ItemCountAddons/ItemCount.lua	Thu Jan 06 20:05:30 2011 +0100
@@ -0,0 +1,24 @@
+do
+	
+	local function GetTotalCount(itemId)
+		return ICGetItemCountTotal(itemId) or -1;
+	end
+	
+	local function GetCharacterCount(itemId)
+		local bags, bank, auctionHouse, mail = ICGetItemCountCharacter(itemId);
+		return bags or 0, bank or 0, auctionHouse or 0, mail or 0;
+	end
+	
+	local function IsEnabled()
+		return (ICGetItemCountTotal and ICGetItemCountCharacter);
+	end
+	
+	local function OnSelect()
+		local addonName = "|r|cfffed000ItemCount|r|cffff6600";
+		
+		print("|cffff6600Note: " .. addonName .. " can't record mailbox item data and thus this will not be included in any item counts.|r");
+	end
+	
+	IMRegisterItemCountAddon("ItemCount", GetTotalCount, GetCharacterCount, IsEnabled, OnSelect);
+	
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/ItemCountAddons/ItemCountAddons.xml	Thu Jan 06 20:05:30 2011 +0100
@@ -0,0 +1,8 @@
+<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="Altoholic.lua"/>
+	<Script file="DataStore (current account only).lua"/>
+	<Script file="DataStore (with guilds).lua"/>
+	<Script file="DataStore (without guilds).lua"/>
+	<Script file="ItemCount.lua"/>
+</Ui>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Plugins.xml	Thu Jan 06 20:05:30 2011 +0100
@@ -0,0 +1,6 @@
+<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">
+	<Include file="AuctionAddons\AuctionAddons.xml"/>
+	<Include file="CraftingAddons\CraftingAddons.xml"/>
+	<Include file="ItemCountAddons\ItemCountAddons.xml"/>
+</Ui>
\ No newline at end of file
--- a/Queue.lua	Thu Jan 06 11:04:55 2011 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,151 +0,0 @@
-local addon = select(2, ...);
-local mod = addon:NewModule("Queue", "AceEvent-3.0", "AceTimer-3.0");
-
-local pairs = pairs;
-
-function mod:OnEnable()
-	-- Register our own slash commands
-	-- /im queue
-	addon:RegisterSlash(function()
-		mod:QueueAll();
-	end, { "q", "que", "queue" }, "|Hfunction:InventoriumCommandHandler:queue|h|cff00fff7/im queue|r|h (or /im q) - Queue all items found in the currently opened profession that are within the groups tracked at this current character.");
-	
-	self:RegisterMessage("IM_QUEUE_ALL");
-	self:RegisterMessage("IM_QUEUE_GROUP");
-end
-
-function mod:IM_QUEUE_ALL()
-	self:QueueAll();
-end
-
-function mod:IM_QUEUE_GROUP(event, groupName)
-	self:QueueGroup(groupName);
-end
-
-function mod:QueueAll()
-	local playerName = UnitName("player");
-	
-	-- Go through all groups
-	for groupName, values in pairs(addon.db.profile.groups) do
-		local trackAt = addon:GetOptionByKey(groupName, "trackAtCharacters");
-		
-		if trackAt[playerName] then
-			self:QueueGroup(groupName);
-		end
-	end
-end
-
-function mod:QueueGroup(groupName)
-	if not addon.db.profile.groups[groupName] then
-		print(("Tried to queue items from a group named \"%s\", but no such group exists."):format(groupName));
-		return;
-	end
-	
-	local temp = {};
-	
-	local tradeskillName, currentLevel, maxLevel = GetTradeSkillLine();
-	
-	if tradeskillName ~= "UNKNOWN" then
-		-- Go through all trade skills for the profession
-		for i = 1, GetNumTradeSkills() do
-			-- Process every single tradeskill
-			self:ProcessTradeSkill(i, groupName, temp);
-		end
-	end
-	
-	if addon.db.profile.groups[groupName].items then
-		for itemId, _ in pairs(addon.db.profile.groups[groupName].items) do
-			if not temp[itemId] then
-				local itemLink = select(2, GetItemInfo(itemId));
-				
-				print("Couldn't queue " .. (itemLink or itemId or "???") .. " (not part of this profession)");
-			end
-		end
-	end
-end
-
-function mod:ProcessTradeSkill(i, groupName, temp)
-	-- Try to retrieve the itemlink, this will be nil if current item is a group instead
-	local itemLink = GetTradeSkillItemLink(i);
-	
-	if itemLink then
-		local itemId = addon:GetItemId(itemLink);
-		if not itemId then
-			-- If this isn't an item, it can only be an enchant instead
-			itemId = tonumber(itemLink:match("|Henchant:([-0-9]+)|h"));
-			
-			itemId = addon.scrollIds[itemId]; -- change enchantIds into scrollIds
-		end
-		
-		if addon.db.profile.groups[groupName].items[itemId] then
-			-- This item is in this group, queue it!
-			
-			if temp then
-				-- Remember which items have been processed
-				temp[itemId] = true;
-			end
-			
-			local currentStock = addon:GetItemCount(itemId, groupName);
-			
-			if currentStock >= 0 then
-				-- Current stock will be -1 when no itemcount addon was found
-				
-				-- Retrieve group settings
-				local restockTarget = addon:GetOptionByKey(groupName, "restockTarget");
-				local bonusQueue = addon:GetOptionByKey(groupName, "bonusQueue");
-				local minCraftingQueue = floor( addon:GetOptionByKey(groupName, "minCraftingQueue") * restockTarget );
-				
-				-- Calculate the amount to be queued
-				local amount = ( restockTarget - currentStock );
-				
-				if currentStock == 0 and bonusQueue > 0 then
-					-- If we have none left and the bonus queue is enabled, modify the amount to  be queued
-					
-					amount = floor( ( amount * ( bonusQueue + 1 ) ) + .5 ); -- round
-				end
-				
-				if amount > 0 and amount >= minCraftingQueue then
-					-- If we are queueing at least one AND more than the minimum amount, then proceed
-					
-					-- Auction value settings
-					local priceThreshold = addon:GetOptionByKey(groupName, "priceThreshold");
-					
-					if priceThreshold == 0 or addon:GetAuctionValue(itemLink, groupName) >= priceThreshold then
-						-- If no price threshold is set or the auction value is equal to or larger than the price threshold, then proceed
-						
-						self:Queue(i, amount, groupName);
-						
-						print("Queued " .. amount .. " of " .. itemLink);
-					end
-				end
-			else
-				print("No usable itemcount addon found.");
-			end
-		end
-	end
-end
-
-function mod:Queue(tradeSkillIndex, amount, group)
-	tradeSkillIndex = tonumber(tradeSkillIndex);
-	amount = tonumber(amount);
-	
-	if not tradeSkillIndex or not amount then return; end
-	
-	local selectedExternalAddon = addon:GetOptionByKey(group, "craftingAddon");
-	
-	if addon.supportedAddons.crafting[selectedExternalAddon] and addon.supportedAddons.crafting[selectedExternalAddon].IsEnabled() then
-		-- Try to use the default auction pricing addon
-		
-		return addon.supportedAddons.crafting[selectedExternalAddon].Queue(tradeSkillIndex, amount);
-	else
-		-- Default not available, get the first one then
-		
-		for name, value in pairs(addon.supportedAddons.crafting) do
-			if value.IsEnabled() then
-				return value.Queue(tradeSkillIndex, amount);
-			end
-		end
-	end
-	
-	return -2;
-end
--- a/Scanner.lua	Thu Jan 06 11:04:55 2011 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,254 +0,0 @@
-local addon = select(2, ...);
-local mod = addon:NewModule("Scanner", "AceEvent-3.0");
-
-addon.Locations = {
-	Bag = 0,
-	Bank = 1,
-	Guild = 2,
-};
-
-local Mover, paused;
-local itemCache = {};
-
-local function GetItemID(link)
-	return tonumber(link:match("|Hitem:([-0-9]+):"));
-end
-
-function mod:ClearCache()
-	table.wipe(itemCache);
-end
-
-function mod:CacheLocation(location, remember)
-	if location == addon.Locations.Bag or location == addon.Locations.Bank then
-		-- Reset cache just in case it was filled
-		self:ClearCache();
-		
-		local start, stop;
-		if location == addon.Locations.Bag then
-			start = 0;
-			stop = NUM_BAG_SLOTS;
-		else
-			-- If we requested the bank then we don't want the bag info
-			start = ( NUM_BAG_SLOTS + 1 );
-			stop = ( NUM_BAG_SLOTS + NUM_BANKBAGSLOTS );
-		end
-		
-		-- Go through all our bags, including the backpack
-		for i = start, ((location == addon.Locations.Bag and stop) or (location == addon.Locations.Bank and (stop + 1))) do -- if scanning bags stop at normal bag slot, if scanning bank, stop one later to allow BANK_CONTAINER to be scanned too
-			-- Scan the default 100 slots whenever we're at a non-existing index
-			local bagId = (i == (stop + 1) and BANK_CONTAINER) or i;
-			local slotId = GetContainerNumSlots(bagId);
-			
-			while slotId ~= 0 do
-				-- A not equal-comparison should be quicker than a larger than-comparison
-				
-				local itemId = GetContainerItemID(bagId, slotId);
-				local itemCount = itemId and select(2, GetContainerItemInfo(bagId, slotId));
-				
-				if itemId and itemCount and itemCount > 0 then
-					local itemMove;
-					if not itemCache[itemId] then
-						-- If this is the first time we see this item, make a new object
-						itemMove = addon.ContainerItem:New();
-						itemCache[itemId] = itemMove;
-					else
-						-- If we had this item in another slot too
-						itemMove = itemCache[itemId];
-					end
-					
-					itemMove:AddLocation(bagId, slotId, itemCount);
-				end
-			
-				-- Continue scanning a different slot
-				slotId = (slotId - 1);
-			end
-		end
-	elseif location == addon.Locations.Guild then
-		-- Reset cache before we scan
-		self:ClearCache();
-		
-		local tabId = GetCurrentGuildBankTab();
-		local slotId = (MAX_GUILDBANK_SLOTS_PER_TAB or 98); -- start by scanning the last slot
-		
-		if tabId == nil or tabId < 1 then return; end
-		
-		while slotId ~= 0 do
-			-- A not equal-comparison should be quicker than a larger than-comparison
-			
-				-- If there is actually an item in this slot
-				
-			local itemLink = GetGuildBankItemLink(tabId, slotId);
-			local itemId = itemLink and GetItemID(itemLink);
-			local itemCount = itemLink and select(2, GetGuildBankItemInfo(tabId, slotId));
-				
-			if itemLink and itemId and itemCount and itemCount > 0 then
-				local itemMove;
-				if not itemCache[itemId] then
-					-- If this is the first time we see this item, make a new object
-					itemMove = addon.ContainerItem:New();
-					itemCache[itemId] = itemMove;
-				else
-					-- If we had this item in another slot too
-					itemMove = itemCache[itemId];
-				end
-				
-				itemMove:AddLocation(tabId, slotId, itemCount);
-			end
-			
-			-- Continue scanning a different slot
-			slotId = (slotId - 1);
-		end
-	else
-		error("Invalid location provided for CacheLocation. Must be Bank or Guild.");
-	end
-	
-	if not remember then
-		-- Copy the table as clearing the cache wipes it empty (and tables are passed by reference)
-		local cacheCopy = CopyTable(itemCache);
-		
-		self:ClearCache();
-		
-		return cacheCopy;
-	end
-end
-
-function mod:Scan(location)
-	-- We might pause the scanning when we invoke moves ourself
-	if paused then
-		return;
-	end
-	
-	local playerName = UnitName("player");
-	
-	self:CacheLocation(location, true);
-	
-	-- Go through all groups
-	for groupName, values in pairs(addon.db.profile.groups) do
-		local trackAt = addon:GetOptionByKey(groupName, "trackAtCharacters");
-		local localItemData = self:GetOptionByKey(groupName, "localItemData");
-		
-		if values.items and trackAt[playerName] and addon:GetOptionByKey(groupName, "autoRefill") and (location ~= addon.Locations.Bank or not localItemData or not localItemData["Bank"]) then
-			-- Is this character interested in this data?
-			
-			local minLocalStock = addon:GetOptionByKey(groupName, "minLocalStock");
-			
-			-- Go through all items
-			for itemId, _ in pairs(values.items) do
-				
-				-- Check if we have enough items local (but only do so if this location also has enough available)
-				local missingItems = itemCache[itemId] and (minLocalStock - addon:GetLocalItemCount(itemId, groupName));
-				
-				if itemCache[itemId] and missingItems > 0 then
-					-- Check how many are available
-					local availableItems = ((itemCache[itemId] and itemCache[itemId].totalCount) or 0);
-					
-					if availableItems > 0 then
-						print("Insufficient " .. select(2, GetItemInfo(itemId)) .. " but this location has " .. availableItems .. " (moving " .. missingItems .. ")");
-						
-						Mover:AddMove(itemId, missingItems);
-					else
-						print("Insufficient " .. IdToItemLink(itemId));
-					end
-				end
-			end
-		end
-	end
-	
-	self:ClearCache();
-	
-	if Mover:HasMoves() then
-		StaticPopupDialogs["InventoriumRefill"] = {
-			text = "There are items that can be refilled from this location, do you wish to proceed?",
-			button1 = YES,
-			button2 = NO,
-			OnAccept = function()
-				mod:Pause();
-				Mover:BeginMove(location, self.Unpause);
-			end,
-			timeout = 0,
-			whileDead = 1,
-			hideOnEscape = 1,
-			exclusive = 1,
-			enterClicksFirstButton = 1,
-		};
-		StaticPopup_Show("InventoriumRefill");
-	end
-end
-
-local function BANKFRAME_CLOSED()
-	addon:Debug("Scanner:BANKFRAME_CLOSED");
-	
-	mod:UnregisterEvent("BANKFRAME_CLOSED");
-	
-	StaticPopup_Hide("InventoriumRefill");
-end
-
-local function BANKFRAME_OPENED()
-	addon:Debug("Scanner:BANKFRAME_OPENED");
-	
-	mod:RegisterEvent("BANKFRAME_CLOSED", BANKFRAME_CLOSED);
-	
-	-- Scan once when the bank is opened, but no need to scan after
-	mod:Scan(addon.Locations.Bank);
-end
-
--- Remember which tabs were scanned and don't scan them again
-local guildBankTabsScanned = {};
-
-local function GUILDBANKFRAME_CLOSED()
-	addon:Debug("Scanner:GUILDBANKFRAME_CLOSED");
-	
-	table.wipe(guildBankTabsScanned);
-	
-	mod:UnregisterEvent("GUILDBANKFRAME_CLOSED");
-	mod:UnregisterEvent("GUILDBANKBAGSLOTS_CHANGED");
-	
-	StaticPopup_Hide("InventoriumRefill");
-end
-
-local function GUILDBANKBAGSLOTS_CHANGED()
-	if not guildBankTabsScanned[GetCurrentGuildBankTab()] then
-		addon:Debug("Scanner:GUILDBANKBAGSLOTS_CHANGED (" .. GetCurrentGuildBankTab() .. ") - scanning");
-		
-		mod:Scan(addon.Locations.Guild);
-		guildBankTabsScanned[GetCurrentGuildBankTab()] = true;
-	else
-		addon:Debug("Scanner:GUILDBANKBAGSLOTS_CHANGED (" .. GetCurrentGuildBankTab() .. ") - not scanning");
-	end
-end
-
-local function GUILDBANKFRAME_OPENED()
-	addon:Debug("Scanner:GUILDBANKFRAME_OPENED");
-	
-	table.wipe(guildBankTabsScanned);
-	
-	mod:RegisterEvent("GUILDBANKFRAME_CLOSED", GUILDBANKFRAME_CLOSED);
-	mod:RegisterEvent("GUILDBANKBAGSLOTS_CHANGED", GUILDBANKBAGSLOTS_CHANGED);
-end
-
-function mod:OnEnable()
-	-- Scan once when the bankframe is opened
-	mod:RegisterEvent("BANKFRAME_OPENED", BANKFRAME_OPENED);
-	mod:RegisterEvent("GUILDBANKFRAME_OPENED", GUILDBANKFRAME_OPENED);
-	
-	Mover = addon:GetModule("Mover");
-end
-
-function mod:OnDisable()
-	Mover = nil;
-	
-	-- Bank
-	mod:UnregisterEvent("BANKFRAME_OPENED");
-	
-	-- Guild
-	GUILDBANKFRAME_CLOSED();
-	mod:UnregisterEvent("GUILDBANKFRAME_OPENED");
-end
-
-function mod:Pause()
-	paused = true;
-end
-
-function mod:Unpause()
-	paused = nil;
-end
--- a/Summary.lua	Thu Jan 06 11:04:55 2011 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,603 +0,0 @@
-local addon = select(2, ...); -- Get a reference to the main addon object
-local mod = addon:NewModule("Summary", "AceEvent-3.0", "AceTimer-3.0"); -- register a new module, Summary: resposible for building the summary window
-
-local unknownItemName = "Unknown (#%d)";
-
-local CACHE_ITEMS_TOTAL, CACHE_ITEMS_CURRENT, itemsCache = 0, 0, {};
-local AceGUI, cacheStart;
-
-local _G = _G; -- prevent looking up of the global table
-local printf, sgsub, supper, tinsert, pairs, ceil, GetItemInfo = _G.string.format, _G.string.gsub, _G.string.upper, _G.table.insert, _G.pairs, _G.ceil, _G.GetItemInfo; -- prevent looking up of the most used globals all the time
-
-function mod:OnEnable()
-	-- Register the summary specific widget
-	addon:GetModule("Widgets"):InlineGroupWithButton();
-	
-	AceGUI = LibStub("AceGUI-3.0");
-	
-	-- Register our own slash commands
-	-- /im summary
-	addon:RegisterSlash(function()
-		mod:BuildMain();
-		mod:Build();
-	end, { "s", "sum", "summary" }, "|Hfunction:InventoriumCommandHandler:summary|h|cff00fff7/im summary|r|h (or /im s) - Show the summary window containing an overview of all items within the groups tracked at this current character.");
-	
-	-- /im reset
-	addon:RegisterSlash(function()
-		if mod.frame then
-			mod.frame:SetWidth(700);
-			mod.frame:SetHeight(600);
-			
-			print("Resetting width and height of the summary frame.");
-		end
-	end, { "r", "reset" }, "|Hfunction:InventoriumCommandHandler:reset|h|cff00fff7/im reset|r|h (or /im r) - Reset the size of the summary frame.");
-end
-
-local function ShowTooltip(self)
-	-- If this function is called from a widget, self is the widget and self.frame the actual frame
-	local this = self.frame or self;
-	
-	GameTooltip:SetOwner(this, "ANCHOR_NONE");
-	GameTooltip:SetPoint("BOTTOM", this, "TOP");
-	GameTooltip:SetText(this.tooltipTitle, 1, .82, 0, 1);
-	
-	if type(this.tooltip) == "string" then
-		GameTooltip:AddLine(this.tooltip, 1, 1, 1, 1);
-	end
-	
-	GameTooltip:Show();
-end
-
-local function HideTooltip()
-	GameTooltip:Hide();
-end
-
-function mod:BuildMain()
-	LibStub("AceConfigDialog-3.0"):Close("InventoriumOptions");
-	
-	self:CloseFrame();
-	
-	-- Main Window
-	mod.frame = AceGUI:Create("Frame");
-	_G["InventoriumSummary"] = mod.frame; -- name the global frame so it can be put in the UISpecialFrames
-	mod.frame:SetTitle("Inventory Summary");
-	mod.frame:SetLayout("Fill");
-	mod.frame:SetCallback("OnClose", function(widget)
-		mod:CancelTimer(self.tmrUpdater, true);
-		mod:CloseFrame();
-	end);
-	mod.frame:SetWidth(addon.db.profile.defaults.summary.width);
-	mod.frame:SetHeight(addon.db.profile.defaults.summary.height);
-	mod.frame.OnWidthSet = function(_, width)
-		addon.db.profile.defaults.summary.width = width;
-	end;
-	mod.frame.OnHeightSet = function(_, height)
-		addon.db.profile.defaults.summary.height = height;
-	end;
-	
-	-- Close on escape
-	tinsert(UISpecialFrames, "InventoriumSummary");
-	
-	-- ScrollFrame child
-	mod.scrollFrame = AceGUI:Create("ScrollFrame");
-	mod.scrollFrame:SetLayout("Flow");
-	
-	mod.frame:AddChild(mod.scrollFrame);
-	
-	-- Reset items cache
-	table.wipe(itemsCache);
-end
-
-function mod:CloseFrame()
-	if mod.frame then
-		mod.frame:Release();
-		mod.frame = nil;
-		
-		-- Stop caching
-		
-		-- Stop timer
-		self:CancelTimer(self.tmrUpdater, true);
-		
-		-- Reset trackers
-		CACHE_ITEMS_TOTAL = 0;
-		CACHE_ITEMS_CURRENT = 0;
-	end
-end
-
-local sortMethod = "item";
-local sortDirectory = "ASC";
-local function ReSort(subject)
-	if sortMethod == subject then
-		sortDirectory = (sortDirectory == "ASC" and "DESC") or "ASC";
-	else
-		sortDirectory = "ASC";
-	end
-	sortMethod = subject;
-	
-	mod:Build();
-end
-
--- From http://www.wowwiki.com/API_sort
-local function pairsByKeys (t, f)
-	local a = {}
-	for n in pairs(t) do tinsert(a, n) end
-		table.sort(a, f)
-	local i = 0      -- iterator variable
-	local iter = function ()   -- iterator function
-		i = i + 1
-		if a[i] == nil then return nil
-		else return a[i], t[a[i]]
-		end
-	end
-	return iter
-end
-
-function mod:Build()
-	local buildStartTime, times = GetTime(), {};
-	
-	-- We are going to add hunderds of widgets to this container, but don't want it to also cause hunderds of reflows, thus pause reflowing and just do it once when everything is prepared
-	-- This appears to be required for each container we wish to pause, so also do this for the contents
-	mod.scrollFrame:PauseLayout();
-	
-	mod.scrollFrame:ReleaseChildren();
-	
-	-- Refresh button
-	local btnRefresh = AceGUI:Create("Button");
-	btnRefresh:SetText("Refresh");
-	btnRefresh:SetRelativeWidth(.2);
-	btnRefresh:SetCallback("OnClick", function()
-		-- Reset items cache
-		table.wipe(itemsCache);
-		
-		-- Rebuild itemlist and start caching
-		mod:Build();
-	end);
-	btnRefresh:SetCallback("OnEnter", ShowTooltip);
-	btnRefresh:SetCallback("OnLeave", HideTooltip);
-	btnRefresh.frame.tooltipTitle = "Refresh Cache";
-	btnRefresh.frame.tooltip = "Refresh the list and recache the item counts and auction values.";
-	
-	mod.scrollFrame:AddChild(btnRefresh);
-	
-	local lblSpacer = AceGUI:Create("Label");
-	lblSpacer:SetRelativeWidth(.03);
-	
-	mod.scrollFrame:AddChild(lblSpacer);
-	
-	-- Speed slider
-	local sdrSpeed = AceGUI:Create("Slider");
-	sdrSpeed:SetLabel("Processing speed");
-	sdrSpeed:SetSliderValues(0.01, 5, 0.01); -- min, max, interval
-	sdrSpeed:SetIsPercent(true);
-	sdrSpeed:SetRelativeWidth(.3);
-	sdrSpeed:SetCallback("OnMouseUp", function(self, event, value)
-		addon.db.profile.defaults.summary.speed = ceil( ( ( value * 100 ) / 5) - .5 );
-		
-		CACHE_ITEMS_PER_UPDATE = addon.db.profile.defaults.summary.speed; -- max = 20, min = 1
-	end);
-	sdrSpeed:SetValue( addon.db.profile.defaults.summary.speed * 5 / 100 );
-	sdrSpeed:SetCallback("OnEnter", ShowTooltip);
-	sdrSpeed:SetCallback("OnLeave", HideTooltip);
-	sdrSpeed.frame.tooltipTitle = "Caching Processing Speed";
-	sdrSpeed.frame.tooltip = "Change the speed at which item counts and auction values are being cached. Higher is faster but may drastically reduce your FPS while caching.\n\nAnything above 100% will probably become uncomfortable.";
-	
-	mod.scrollFrame:AddChild(sdrSpeed);
-	
-	local lblSpacer = AceGUI:Create("Label");
-	lblSpacer:SetRelativeWidth(.23);
-	
-	mod.scrollFrame:AddChild(lblSpacer);
-	
-	-- Config button
-	--[[local btnConfig = AceGUI:Create("Button");
-	btnConfig:SetText("Config");
-	btnConfig:SetRelativeWidth(.2);
-	btnConfig:SetCallback("OnClick", function()
-		--TODO: Tidy up
-		SlashCmdList["INVENTORIUM"]("config");
-	end);
-	btnConfig:SetCallback("OnEnter", ShowTooltip);
-	btnConfig:SetCallback("OnLeave", HideTooltip);
-	btnConfig.frame.tooltipTitle = "Config";
-	btnConfig.frame.tooltip = "Click to open the config window. This will close the current window.";
-	
-	mod.scrollFrame:AddChild(btnConfig);]]
-	
-	local lblSpacer = AceGUI:Create("Label");
-	lblSpacer:SetRelativeWidth(.03);
-	
-	mod.scrollFrame:AddChild(lblSpacer);
-	
-	-- Queue all button
-	local btnQueueAll = AceGUI:Create("Button");
-	btnQueueAll:SetText("Queue All");
-	btnQueueAll:SetRelativeWidth(.2);
-	btnQueueAll:SetCallback("OnClick", function()
-		self:SendMessage("IM_QUEUE_ALL");
-	end);
-	btnQueueAll:SetCallback("OnEnter", ShowTooltip);
-	btnQueueAll:SetCallback("OnLeave", HideTooltip);
-	btnQueueAll.frame.tooltipTitle = "Queue all";
-	btnQueueAll.frame.tooltip = "Queue everything that requires restocking within every single visible group.";
-	
-	mod.scrollFrame:AddChild(btnQueueAll);
-	
-	times.init = ceil( ( GetTime() - buildStartTime ) * 1000 );
-	addon:Debug("Time spent legend: (init / sorting / preparing / building / all).");
-	
-	local playerName = UnitName("player");
-	
-	-- Go through all our stored groups
-	for groupName, values in pairsByKeys(addon.db.profile.groups, function(a, b) return a:lower() < b:lower(); end) do
-		local groupStartTime, groupTimes = GetTime(), {};
-		
-		local trackAt = addon:GetOptionByKey(groupName, "trackAtCharacters");
-		
-		
-		-- Does this group have any items and do we want to track it at this char?
-		if values.items and trackAt[playerName] then
-		
-			
-			-- Get group settings
-			local minLocalStock = addon:GetOptionByKey(groupName, "minLocalStock");
-			local minGlobalStock = addon:GetOptionByKey(groupName, "minGlobalStock");
-			local showWhenBelow = addon:GetOptionByKey(groupName, "summaryThresholdShow");
-			local priceThreshold = addon:GetOptionByKey(groupName, "priceThreshold");
-			local hideWhenBelowPriceThreshold = addon:GetOptionByKey(groupName, "summaryHidePriceThreshold");
-			local alwaysGetAuctionValue = addon:GetOptionByKey(groupName, "alwaysGetAuctionValue");
-			
-			-- Make group container
-			local iGroup = AceGUI:Create("InlineGroupWithButton");
-			iGroup:PauseLayout();
-			iGroup:SetTitle(groupName);
-			iGroup:SetFullWidth(true);
-			iGroup:SetLayout("Flow");
-			iGroup:MakeButton({
-				name = "Queue",
-				desc = "Queue all items in this group.",
-				exec = function()
-					print("Queueing all items within " .. groupName .. " craftable by the currently open profession.");
-					self:SendMessage("IM_QUEUE_GROUP", groupName);
-				end,
-			});
-			
-			
-			
-			-- Headers
-			
-			-- Itemlink
-			local lblItem = AceGUI:Create("InteractiveLabel");
-			lblItem:SetText("|cfffed000Item|r");
-			lblItem:SetFontObject(GameFontHighlight);
-			lblItem:SetRelativeWidth(.7);
-			lblItem:SetCallback("OnClick", function() ReSort("item"); end);
-			lblItem:SetCallback("OnEnter", ShowTooltip);
-			lblItem:SetCallback("OnLeave", HideTooltip);
-			lblItem.frame.tooltipTitle = "Item";
-			lblItem.frame.tooltip = "Sort on the item quality, then name.";
-			
-			iGroup:AddChild(lblItem);
-			
-			-- Local quantity
-			local lblLocal = AceGUI:Create("InteractiveLabel");
-			lblLocal:SetText("|cfffed000Loc.|r");
-			lblLocal:SetFontObject(GameFontHighlight);
-			lblLocal:SetRelativeWidth(.099);
-			lblLocal:SetCallback("OnClick", function() ReSort("local"); end);
-			lblLocal:SetCallback("OnEnter", ShowTooltip);
-			lblLocal:SetCallback("OnLeave", HideTooltip);
-			lblLocal.frame.tooltipTitle = "Local stock";
-			lblLocal.frame.tooltip = "Sort on the amount of items currently in local stock.";
-			
-			iGroup:AddChild(lblLocal);
-			
-			-- Current quantity
-			local lblQuantity = AceGUI:Create("InteractiveLabel");
-			lblQuantity:SetText("|cfffed000Cur.|r");
-			lblQuantity:SetFontObject(GameFontHighlight);
-			lblQuantity:SetRelativeWidth(.099);
-			lblQuantity:SetCallback("OnClick", function() ReSort("current"); end);
-			lblQuantity:SetCallback("OnEnter", ShowTooltip);
-			lblQuantity:SetCallback("OnLeave", HideTooltip);
-			lblQuantity.frame.tooltipTitle = "Current stock";
-			lblQuantity.frame.tooltip = "Sort on the amount of items currently in stock.";
-			
-			iGroup:AddChild(lblQuantity);
-			
-			-- Lowest value
-			local lblValue = AceGUI:Create("InteractiveLabel");
-			lblValue:SetText("|cfffed000Value|r");
-			lblValue:SetFontObject(GameFontHighlight);
-			lblValue:SetRelativeWidth(.099);
-			lblValue:SetCallback("OnClick", function() ReSort("value"); end);
-			lblValue:SetCallback("OnEnter", ShowTooltip);
-			lblValue:SetCallback("OnLeave", HideTooltip);
-			lblValue.frame.tooltipTitle = "Value";
-			lblValue.frame.tooltip = "Sort on the item auction value.";
-			
-			iGroup:AddChild(lblValue);
-			
-			
-			-- Retrieve items list
-			if not itemsCache[groupName] then
-				itemsCache[groupName] = {};
-			end
-			 
-			if #(itemsCache[groupName]) == 0 then
-				-- If the item cache is empty
-				for itemId, _ in pairs(values.items) do
-					local newItemData = addon.ItemData:New(itemId);
-					
-					-- if no price threshold is set for this item and you don't want to always get the auction value, then don't look it up either
-					if priceThreshold == 0 and not alwaysGetAuctionValue then
-						newItemData.value = -4;
-					end
-					
-					tinsert(itemsCache[groupName], newItemData);
-					
-					CACHE_ITEMS_TOTAL = CACHE_ITEMS_TOTAL + 1;
-				end
-			end
-			
-			groupTimes.init = ceil( ( GetTime() - groupStartTime ) * 1000 );
-			
-			
-			
-			-- Sort items
-			table.sort(itemsCache[groupName], function(a, b)
-				local aRarity = a.rarity or 1;
-				local bRarity = b.rarity or 1;
-				
-				if sortMethod == "item" and aRarity == bRarity then
-					-- Do a name-compare for items of the same rarity
-					-- Otherwise epics first, then junk
-					
-					local aName = a.name or printf(unknownItemName, a.id);
-					local bName = b.name or printf(unknownItemName, b.id);
-					
-					if sortDirectory == "ASC" then
-						return supper(aName) < supper(bName);
-					else
-						return supper(aName) > supper(bName);
-					end
-				elseif sortMethod == "item"  then
-					if sortDirectory == "ASC" then
-						return aRarity > bRarity; -- the comparers were reversed because we want epics first
-					else
-						return aRarity < bRarity; -- the comparers were reversed because we want epics first
-					end
-				elseif sortMethod == "current" then
-					if sortDirectory == "ASC" then
-						return a.globalCount < b.globalCount;
-					else
-						return a.globalCount > b.globalCount;
-					end
-				elseif sortMethod == "local" then
-					if sortDirectory == "ASC" then
-						return a.localCount < b.localCount;
-					else
-						return a.localCount > b.localCount;
-					end
-				elseif sortMethod == "value" then
-					if sortDirectory == "ASC" then
-						return a.value < b.value;
-					else
-						return a.value > b.value;
-					end
-				end
-			end);
-			
-			groupTimes.sorting = ceil( ( GetTime() - groupStartTime ) * 1000 );
-			
-			
-			
-			
-			-- Show itemslist
-			for i, item in pairs(itemsCache[groupName]) do
-				-- Go through all items for this group
-				
-				if (( item.globalCount / minGlobalStock ) < showWhenBelow or ( item.localCount / minLocalStock ) < showWhenBelow) and (not hideWhenBelowPriceThreshold or priceThreshold == 0 or item.value < 0 or item.value >= priceThreshold) then
-																-- if the option "hide when below threshold" is disabled or no price threshold is set or the value is above the price threshold or the value could not be determined, proceed
-					
-					local btnItemLink = AceGUI:Create("ItemLinkButton");
-					btnItemLink:SetUserData("exec", function(_, itemId, _, buttonName)
-						local itemName, itemLink = GetItemInfo(itemId);
-						
-						if buttonName == "LeftButton" and IsShiftKeyDown() and itemLink then
-							ChatEdit_InsertLink(itemLink);
-						elseif buttonName == "LeftButton" and IsAltKeyDown() and itemName and AuctionFrame and AuctionFrame:IsShown() and AuctionFrameBrowse then
-							-- Start search at page 0
-							AuctionFrameBrowse.page = 0;
-							
-							-- Set the search field (even though we could use "ChatEdit_InsertLink", this ensures the right field is updated)
-							BrowseName:SetText(itemName)
-							
-							QueryAuctionItems(itemName, nil, nil, 0, 0, 0, 0, 0, 0);
-						end
-					end);
-					btnItemLink:SetRelativeWidth(.7);
-					btnItemLink:SetItem(item);
-					
-					iGroup:AddChild(btnItemLink);
-					
-					-- Local quantity
-					local lblLocal = AceGUI:Create("Label");
-					lblLocal:SetText(self:DisplayItemCount(item.localCount, minLocalStock));
-					lblLocal:SetRelativeWidth(.099);
-					
-					iGroup:AddChild(lblLocal);
-					
-					-- Current quantity
-					local lblQuantity = AceGUI:Create("Label");
-					lblQuantity:SetText(self:DisplayItemCount(item.globalCount, minGlobalStock));
-					lblQuantity:SetRelativeWidth(.099);
-					
-					iGroup:AddChild(lblQuantity);
-					
-					-- Value
-					local lblValue = AceGUI:Create("Label");
-					lblValue:SetText(self:DisplayMoney(item.value, priceThreshold));
-					lblValue:SetRelativeWidth(.099);
-					
-					iGroup:AddChild(lblValue);
-					
-					-- Remember references to the value and current fields so we can fill them later
-					item.set.value = lblValue;
-					item.set.globalCount = lblQuantity;
-					item.set.localCount = lblLocal;
-				end
-			end
-			
-			groupTimes.preparing = ceil( ( GetTime() - groupStartTime ) * 1000 );
-			
-			iGroup:ResumeLayout();
-			mod.scrollFrame:AddChild(iGroup); -- this can take up to .5 seconds, might need to look into again at a later time
-			
-			groupTimes.building = ceil( ( GetTime() - groupStartTime ) * 1000 );
-		end
-		
-		if groupStartTime and groupTimes then
-			addon:Debug(printf("Building of %s took %d ms (%d / %d / %d / %d / %d).", groupName, ceil( ( GetTime() - groupStartTime ) * 1000 ), groupTimes.init or 0, groupTimes.sorting or 0, groupTimes.preparing or 0, groupTimes.building or 0, ceil( ( GetTime() - buildStartTime ) * 1000 )));
-		end
-	end
-	
-	mod.scrollFrame:ResumeLayout();
-	mod.scrollFrame:DoLayout();
-	
-	addon:Debug(printf("Done building summary after %d ms.", ceil( ( GetTime() - buildStartTime ) * 1000 )));
-	
-	if CACHE_ITEMS_TOTAL > 0 then
-		cacheStart = GetTime();
-		self:CancelTimer(self.tmrUpdater, true);
-		self.tmrUpdater = self:ScheduleRepeatingTimer("UpdateNextItem", .01); -- Once every 100 frames (or once every x frames if you have less than 100 FPS, basically, once every frame)
-	end
-end
-
-function mod:UpdateNextItem()
-	local i = 0;
-	
-	for groupName, items in pairs(itemsCache) do
-		local minGlobalStock = addon:GetOptionByKey(groupName, "minGlobalStock");
-		local minLocalStock = addon:GetOptionByKey(groupName, "minLocalStock");
-		local priceThreshold = addon:GetOptionByKey(groupName, "priceThreshold");
-		
-		for _, item in pairs(items) do
-			if item.globalCount == -3 or item.localCount == -3 or item.value == -3 then
-				if item.globalCount == -3 then
-					-- Only if item count was queued, update it
-					item.globalCount = addon:GetItemCount(item.id, groupName);
-					if item.set.globalCount and item.set.globalCount.SetText then
-						item.set.globalCount:SetText(self:DisplayItemCount(item.globalCount, minGlobalStock));
-					end
-				end
-				
-				if item.localCount == -3 then
-					-- Only if item count was queued, update it
-					item.localCount = addon:GetLocalItemCount(item.id, groupName);
-					if item.set.localCount and item.set.localCount.SetText then
-						item.set.localCount:SetText(self:DisplayItemCount(item.localCount, minLocalStock));
-					end
-				end
-				
-				if item.value == -3 then
-					-- Only if item value was queued, update it
-					
-					-- The itemlink might not have been loaded yet in which case we retry
-					item.link = item.link or select(2, GetItemInfo(item.id));
-					
-					if item.link then
-						item.value = addon:GetAuctionValue(item.link, groupName);
-						if item.set.value and item.set.value.SetText then
-							item.set.value:SetText(self:DisplayMoney(item.value, priceThreshold));
-						end
-					end
-				end
-				
-				i = i + 1;
-				CACHE_ITEMS_CURRENT = CACHE_ITEMS_CURRENT + 1;
-				
-				if mod.frame then
-					mod.frame:SetStatusText(printf("Caching auction values and item-counts... %d%% has already been processed.", floor(CACHE_ITEMS_CURRENT / CACHE_ITEMS_TOTAL * 100)));
-				end
-				
-				if i >= addon.db.profile.defaults.summary.speed then
-					return;
-				end
-			end
-		end
-	end
-	
-	-- Reset trackers
-	CACHE_ITEMS_TOTAL = 0;
-	CACHE_ITEMS_CURRENT = 0;
-	
-	-- Stop timer
-	self:CancelTimer(self.tmrUpdater, true);
-		
-	-- Rebuild list so hidden items due to too low prices get added
-	self:Build();
-	
-	-- Announce
-	mod.frame:SetStatusText("All required prices and itemcounts have been cached. This process took " .. ceil(GetTime() - cacheStart) .. " seconds.");
-	
-	-- Forget time
-	cacheStart = nil;
-end
-
-function mod:ColorCode(num, required)
-	local percentage = ( num / required );
-	
-	if percentage >= addon.db.profile.defaults.colors.green then
-		return printf("|cff00ff00%d|r", num);
-	elseif percentage >= addon.db.profile.defaults.colors.yellow then
-		return printf("|cffffff00%d|r", num);
-	elseif percentage >= addon.db.profile.defaults.colors.orange then
-		return printf("|cffff9933%d|r", num);
-	elseif percentage >= addon.db.profile.defaults.colors.red then
-		return printf("|cffff0000%d|r", num);
-	else
-		return num;
-	end
-end
-
-function mod:DisplayMoney(value, priceThreshold)
-	if value == -1 then
-		return "|cff0000ffNone up|r";
-	elseif value == -2 then
-		return "|cff0000ffNo AH mod|r";
-	elseif value == -3 then
-		return "|cffffff00Unknown|r";
-	elseif value == -4 then
-		return "|cff00ff00-|r";
-	elseif value == -5 then
-		return "|cffff9933Error|r";
-	elseif priceThreshold and value < priceThreshold then
-		return printf("|cffaaaaaa%s|r", sgsub(addon:ReadableMoney(value or 0, true), "|([a-fA-F0-9]+)([gsc]+)|r", "%2"));
-	else
-		return addon:ReadableMoney(value or 0, true);
-	end
-end
-
-function mod:DisplayItemCount(value, minimumStock)
-	if value == -1 then
-		return "|cffffff00Unknown|r";
-	elseif value == -3 then
-		return "|cffffff00Unknown|r";
-	else
-		return printf("%s / %d", self:ColorCode(value, minimumStock), minimumStock);
-	end
-end
-
-function mod:NumberFormat(num)
-	local formatted = sgsub(num, "(%d)(%d%d%d)$", "%1,%2", 1);
-	
-	while true do
-		formatted, matches = sgsub(formatted, "(%d)(%d%d%d),", "%1,%2,", 1);
-		
-		if matches == 0 then
-			break;
-		end
-	end
-	
-	return formatted;
-end
--- a/Widgets.lua	Thu Jan 06 11:04:55 2011 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,213 +0,0 @@
-local addon = select(2, ...);
-local Widgets = addon:NewModule("Widgets");
-
-local AceGUI = LibStub("AceGUI-3.0");
-
-function Widgets:ItemLinkButton()
-	--[[
-	[ ItemLinkButton ]
-	This custom widget has to show an icon with the item link next to it.
-	Upon hover it must show the item tooltip.
-	Upon click it must execute the function provided through user data.
-	
-	UserData: itemId, onClickEvent
-	
-	OnEnter: tooltip show
-	OnLeave: tooltip hide
-	OnClick: UserData.onClickEvent
-	]]
-	
-	local widgetType = "ItemLinkButton";
-	local widgetVersion = 1;
-	
-	local function Constructor()
-	    local widget = AceGUI:Create("InteractiveLabel");
-	    widget.type = widgetType;
-	    
-	    -- We overwrite the OnAcquire as we want to set our callbacks even
-	    -- when the widget is re-used from the widget pool
-	    widget.originalOnAcquire = widget.OnAcquire;
-	    widget.OnAcquire = function(self, ...)
-		    
-		    
-	    	-- We overwrite the setcallback because we don't want anything else 
-	    	-- to overwrite our OnEnter, OnLeave and OnClick events
-	    	-- which would be done by the AceConfigDialog after a widget gets re-used
-		    if not self.originalSetCallBack then
-			    self.originalSetCallBack = self.SetCallback;
-			    self.SetCallback = function(this, event, func, ...)
-			    	if event == "OnEnter" or event == "OnLeave" or event == "OnClick" then
-			    		-- Don't allow overwriting of these events
-			    		return;
-			    	elseif event == "CustomOnEnter" then
-			    		return this.originalSetCallBack(this, "OnEnter", func, ...);
-			    	elseif event == "CustomOnLeave" then
-			    		return this.originalSetCallBack(this, "OnLeave", func, ...);
-			    	elseif event == "CustomOnClick" then
-			    		return this.originalSetCallBack(this, "OnClick", func, ...);
-			    	else
-			    		return this.originalSetCallBack(this, event, func, ...);
-			    	end
-			    end;
-		    end
-		    
-		    
-		    
-		    -- Set our own events, since we disabled the normal event-names, we'll call them our custom versions
-		    self:SetCallback("CustomOnEnter", function(this)
-		    	local itemData = this:GetUserData("ItemData");
-		    	
-		    	if itemData then
-			    	GameTooltip:SetOwner(this.frame, "ANCHOR_TOPRIGHT");
-			    	GameTooltip:SetHyperlink(("item:%d"):format(itemData.id));
-					GameTooltip:Show();
-		    	end
-		    end);
-		    self:SetCallback("CustomOnLeave", function(this)
-		    	GameTooltip:Hide();
-		    end);
-		    self:SetCallback("CustomOnClick", function(this, ...)
-		    	-- Below is used in child widgets to prepare for onclick
-		    	if this.OnClick then
-		    		this.OnClick(this, ...);
-		    	end
-		    	
-		    	local func = this:GetUserData("exec");
-		    	local itemData = this:GetUserData("ItemData");
-		    	
-		    	if func then
-		    		-- If this is a config option we will need the group id
-		    		local path = this:GetUserData("path");
-		    		local groupId = (path and path[2]) or nil;
-		    		
-		    		func(groupId, itemData, ...);
-		    	end
-		    end);
-		    
-		    
-		    
-		    -- Then also do whatever it wanted to do
-		    self.originalOnAcquire(self, ...);
-	    end;
-	    
-	    -- Remember the original SetText as this might get overwritten by the config-widget
-	    widget.originalSetText = widget.SetText;
-	    
-	    widget.SetItem = function(self, item)
- 		   	self:SetUserData("ItemData", item);
- 		   	
-	   		-- Put the icon in front of it
-			self:SetImage(item.icon);
-			-- Standardize the size
-			self:SetImageSize(16, 16);
-    		
-			-- Make readable font
-    		self:SetFontObject(GameFontHighlight);
-    		
-    		if not item.link then
-    			-- If no item link was cached, give it another try
-    			
-				local itemName, itemLink, itemRarity = GetItemInfo(item.id);
-    			
-				-- item is a table, so passed by reference, so updatable
-				item.name = itemName;
-				item.link = itemLink;
-				item.rarity = itemRarity;
-    		end
-    		
-    		-- We don't want to set the itemId as text, but rather the item link, so get that.
-    		local itemLink = item.link or ("Unknown (#%d)"):format(item.id);
-    		
-    		self:originalSetText(itemLink);
-	    end;
-		
-		return widget;
-	end
-    
-	AceGUI:RegisterWidgetType(widgetType, Constructor, widgetVersion);
-end
-
-function Widgets:ConfigItemLinkButton()
-	-- Define out custom item link button widget
-	-- This will be called as if it's an input element, we overwrite some of the related functions which are called for default input fields
-	
-	local widgetType = "ConfigItemLinkButton";
-	local widgetVersion = 1;
-	
-	-- Empty function for disabling functions
-	local function Dummy() end
-	
-	-- Makes an instance of our ItemLinkButton widget
-	local function GetItemLinkButton()
-	    local widget = AceGUI:Create("ItemLinkButton");
-	    widget.type = widgetType;
-	    
-	    -- We can only provide custom widgets for input, select and multiselect fields
-	    -- Input being the simplest, we use that - however, it provides two parameters: label and text. We only need one, disable the other.
-	    widget.SetLabel = Dummy;
-	    
-	    -- SetText is called when this button is being created and contains the itemId
-	    -- Forward that itemId to the ItemLinkButton
-	    widget.SetText = function(self, value, ...)
-	    	if value and tonumber(value) then
-				local newItemData = addon.ItemData:New(tonumber(value));
-		    	
-	 		   	self:SetItem(newItemData);
-	    	end
-		end;
-		
-		widget.OnClick = function(self, ...)
-			local option = self:GetUserData("option");
-	    	
-	    	if option and option.set then
-				self:SetUserData("exec", option.set);
-			end
-		end;
-	    
-	    return widget;
-	end
-    
-	AceGUI:RegisterWidgetType(widgetType, GetItemLinkButton, widgetVersion);
-end
-
-function Widgets:InlineGroupWithButton()
-	-- Register InlineGroupWithButton-widget
-	-- This widget adds a button next to the header of an inline group
-	-- SetPoint doesn't seem usable within AceGUI.
-	
-	local widgetType = "InlineGroupWithButton";
-	local widgetVersion = 1;
-
-	local function Constructor() 
-	    local widget = AceGUI:Create("InlineGroup");
-	    widget.type = widgetType;
-	    
-	    widget.MakeButton = function(self, buttonSettings)
-			if type(buttonSettings) == "table" then
-				if not self.btnQueue then
-					-- Because widgets are re-used, we don't want to recreate this button
-					self.btnQueue = CreateFrame("Button", nil, self.frame, "UIPanelButtonTemplate");
-					self.btnQueue:SetHeight(22);
-					self.btnQueue:SetWidth(120);
-				end
-				self.btnQueue:SetText(buttonSettings.name);
-				self.btnQueue:SetPoint("TOPRIGHT", self.frame, "TOPRIGHT", -10, 5);
-				
-				-- Triggers
-				self.btnQueue:SetScript("OnClick", buttonSettings.exec);
-				
-				-- Tooltip
-				self.btnQueue.tooltipTitle = buttonSettings.name;
-				self.btnQueue.tooltip = buttonSettings.desc or "";
-				self.btnQueue:SetScript("OnEnter", ShowTooltip);
-				self.btnQueue:SetScript("OnLeave", HideTooltip);
-			else
-				error("settings must be a table - usage: MakeButton(table);");
-			end
-	    end;
-		
-	    return widget;
-	end
-    
-	AceGUI:RegisterWidgetType(widgetType, Constructor, widgetVersion);
-end
\ No newline at end of file
--- a/embeds.xml	Thu Jan 06 11:04:55 2011 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,20 +0,0 @@
-<Ui xsi:schemaLocation="http://www.blizzard.com/wow/ui/ ..\FrameXML\UI.xsd">
-  <!-- Libraries -->
-  <!-- Ace 3 -->
-	  <Script file="Libs\LibStub\LibStub.lua"/>
-	  <Include file="Libs\CallbackHandler-1.0\CallbackHandler-1.0.xml"/>
-	  <Include file="Libs\AceAddon-3.0\AceAddon-3.0.xml"/>
-	  <Include file="Libs\AceDB-3.0\AceDB-3.0.xml"/>
-	  <!--<Include file="Libs\AceDBOptions-3.0\AceDBOptions-3.0.xml"/>-->
-	  <Include file="Libs\AceGUI-3.0\AceGUI-3.0.xml"/>
-	  <!--<Include file="Libs\AceLocale-3.0\AceLocale-3.0.xml"/>-->
-	  <!--<Include file="Libs\AceHook-3.0\AceHook-3.0.xml"/>-->
-	  <Include file="Libs\AceConfig-3.0\AceConfig-3.0.xml"/>
-	  <Include file="Libs\AceEvent-3.0\AceEvent-3.0.xml"/>
-	  <!--<Include file="Libs\AceSerializer-3.0\AceSerializer-3.0.xml"/>-->
-	  <Include file="Libs\AceTimer-3.0\AceTimer-3.0.xml"/>
-  <!-- end Ace 3 -->
-  <!--<Include file="Libs\LibSharedMedia-3.0\lib.xml"/>-->
-  <!--<Include file="Libs\AceGUI-3.0-SharedMediaWidgets\widget.xml"/>-->
-  <Include file="Libs\ChatHyperlinks\ChatHyperlinks.lua"/>
-</Ui>