changeset 124:e31b02b24488

Updated for 8.0 pre-patch and BfA.
author yellowfive
date Tue, 17 Jul 2018 09:57:39 -0700
parents 7a6364917f86
children 4d175fb190f1
files AskMrRobot-Serializer/AskMrRobot-Serializer.lua AskMrRobot.toc CombatLog.lua Constants.lua Core.lua Export.lua Gear.lua Import.lua Libs/AceComm-3.0/AceComm-3.0.lua Libs/AceComm-3.0/ChatThrottleLib.lua Libs/AceEvent-3.0/AceEvent-3.0.lua Libs/AceGUI-3.0/AceGUI-3.0.lua Libs/AceGUI-3.0/widgets/AceGUIContainer-Frame.lua Libs/AceGUI-3.0/widgets/AceGUIContainer-TabGroup.lua Libs/AceGUI-3.0/widgets/AceGUIContainer-TreeGroup.lua Libs/AceGUI-3.0/widgets/AceGUIContainer-Window.lua Libs/AceGUI-3.0/widgets/AceGUIWidget-Button.lua Libs/AceGUI-3.0/widgets/AceGUIWidget-CheckBox.lua Libs/AceGUI-3.0/widgets/AceGUIWidget-DropDown-Items.lua Libs/AceGUI-3.0/widgets/AceGUIWidget-DropDown.lua Libs/AceGUI-3.0/widgets/AceGUIWidget-EditBox.lua Libs/AceGUI-3.0/widgets/AceGUIWidget-Slider.lua Options.lua Shopping.lua localization/enUS.lua localization/frFR.lua localization/itIT.lua localization/ptBR.lua localization/ruRU.lua ui/AmrUiCheckBox.lua ui/AmrUiLabel.lua ui/AmrUiTabGroup.lua ui/Ui.lua
diffstat 33 files changed, 1400 insertions(+), 2233 deletions(-) [+]
line wrap: on
line diff
--- a/AskMrRobot-Serializer/AskMrRobot-Serializer.lua	Mon Feb 12 19:34:09 2018 -0800
+++ b/AskMrRobot-Serializer/AskMrRobot-Serializer.lua	Tue Jul 17 09:57:39 2018 -0700
@@ -1,7 +1,6 @@
 -- AskMrRobot-Serializer will serialize and communicate character data between users.
--- This is used primarily to associate character information to logs uploaded to askmrrobot.com.
 
-local MAJOR, MINOR = "AskMrRobot-Serializer", 57
+local MAJOR, MINOR = "AskMrRobot-Serializer", 58
 local Amr, oldminor = LibStub:NewLibrary(MAJOR, MINOR)
 
 if not Amr then return end -- already loaded by something else
@@ -167,468 +166,13 @@
 	[1712] = true
 }
 
--- just to make life easier, maps ID of each artifact weapon to the spec number (1-4)
-Amr.ArtifactIdToSpecNumber = {
-	[128402] = 1, -- DK
-	[128292] = 2,
-	[128403] = 3,
-	[127829] = 1, -- DH
-	[128832] = 2,
-	[128858] = 1, -- Druid
-	[128860] = 2,
-	[128821] = 3,
-	[128306] = 4,
-	[128861] = 1, -- Hunter
-	[128826] = 2,
-	[128808] = 3,
-	[127857] = 1, -- Mage
-	[128820] = 2,
-	[128862] = 3,
-	[128938] = 1, -- Monk
-	[128937] = 2,
-	[128940] = 3,
-	[128823] = 1, -- Paladin
-	[128866] = 2,
-	[120978] = 3,
-	[128868] = 1, -- Priest
-	[128825] = 2,
-	[128827] = 3,
-	[128870] = 1, -- Rogue
-	[128872] = 2,
-	[128476] = 3,
-	[128935] = 1, -- Shaman
-	[128819] = 2,
-	[128911] = 3,
-	[128942] = 1, -- Warlock
-	[128943] = 2,
-	[128941] = 3,
-	[128910] = 1, -- Warrior
-	[128908] = 2,
-	[128289] = 3,
-	
-	--[128293] = 2, -- Frost OH
-	--[127830] = 1, -- Havoc OH
-	--[128831] = 2, -- Vengeance OH
-	--[128859] = 2, -- Feral OH
-	--[128822] = 3, -- Guardian OH
-	--[133959] = 2, -- Fire OH
-	--[133948] = 3, -- Windwalker OH
-	--[128867] = 2, -- Prot MH
-	--[133958] = 3, -- Shadow OH
-	--[128869] = 1, -- Rogue OH's
-	--[134552] = 2,
-	--[128479] = 3,
-	--[128936] = 1, -- Shaman OH's
-	--[128873] = 2,
-	--[128934] = 3,
-	--[137246] = 2, -- Demo MH
-	--[134553] = 2, -- Fury OH
-	--[128288] = 3  -- Prot MH
-}
-
--- IDs of set tokens that we would care about in a player's inventory
-Amr.SetTokenIds = {
-	[127970] = true,
-	[127969] = true,
-	[127968] = true,
-	[127967] = true,
-	[127966] = true,
-	[127965] = true,
-	[127964] = true,
-	[127963] = true,
-	[127962] = true,
-	[127961] = true,
-	[127960] = true,
-	[127959] = true,
-	[127958] = true,
-	[127957] = true,
-	[127956] = true,
-	[127955] = true,
-	[127954] = true,
-	[127953] = true,
-	[120285] = true,
-	[120284] = true,
-	[120283] = true,
-	[120282] = true,
-	[120281] = true,
-	[120280] = true,
-	[120279] = true,
-	[120278] = true,
-	[120277] = true,
-	[120256] = true,
-	[120255] = true,
-	[120254] = true,
-	[120253] = true,
-	[120252] = true,
-	[120251] = true,
-	[120250] = true,
-	[120249] = true,
-	[120248] = true,
-	[120247] = true,
-	[120246] = true,
-	[120245] = true,
-	[120244] = true,
-	[120243] = true,
-	[120242] = true,
-	[120241] = true,
-	[120240] = true,
-	[120239] = true,
-	[120238] = true,
-	[120237] = true,
-	[120236] = true,
-	[120235] = true,
-	[120234] = true,
-	[120233] = true,
-	[120232] = true,
-	[120231] = true,
-	[120230] = true,
-	[120229] = true,
-	[120228] = true,
-	[120227] = true,
-	[120226] = true,
-	[120225] = true,
-	[120224] = true,
-	[120223] = true,
-	[120222] = true,
-	[120221] = true,
-	[120220] = true,
-	[120219] = true,
-	[120218] = true,
-	[120217] = true,
-	[120216] = true,
-	[120215] = true,
-	[120214] = true,
-	[120213] = true,
-	[120212] = true,
-	[120211] = true,
-	[120210] = true,
-	[120209] = true,
-	[120208] = true,
-	[120207] = true,
-	[120206] = true,
-	[119323] = true,
-	[119322] = true,
-	[119321] = true,
-	[119320] = true,
-	[119319] = true,
-	[119318] = true,
-	[119316] = true,
-	[119315] = true,
-	[119314] = true,
-	[119313] = true,
-	[119312] = true,
-	[119311] = true,
-	[119310] = true,
-	[119309] = true,
-	[119308] = true,
-	[119307] = true,
-	[119306] = true,
-	[119305] = true,
-	[105868] = true,
-	[105867] = true,
-	[105866] = true,
-	[105865] = true,
-	[105864] = true,
-	[105863] = true,
-	[105862] = true,
-	[105861] = true,
-	[105860] = true,
-	[105859] = true,
-	[105858] = true,
-	[105857] = true,
-	[99756] = true,
-	[99755] = true,
-	[99754] = true,
-	[99753] = true,
-	[99752] = true,
-	[99751] = true,
-	[99750] = true,
-	[99749] = true,
-	[99748] = true,
-	[99747] = true,
-	[99746] = true,
-	[99745] = true,
-	[99744] = true,
-	[99743] = true,
-	[99742] = true,
-	[99740] = true,
-	[99739] = true,
-	[99738] = true,
-	[99737] = true,
-	[99736] = true,
-	[99735] = true,
-	[99734] = true,
-	[99733] = true,
-	[99732] = true,
-	[99731] = true,
-	[99730] = true,
-	[99729] = true,
-	[99728] = true,
-	[99727] = true,
-	[99726] = true,
-	[99725] = true,
-	[99724] = true,
-	[99723] = true,
-	[99722] = true,
-	[99721] = true,
-	[99720] = true,
-	[99719] = true,
-	[99718] = true,
-	[99717] = true,
-	[99716] = true,
-	[99715] = true,
-	[99714] = true,
-	[99713] = true,
-	[99712] = true,
-	[99711] = true,
-	[99710] = true,
-	[99709] = true,
-	[99708] = true,
-	[99707] = true,
-	[99706] = true,
-	[99705] = true,
-	[99704] = true,
-	[99703] = true,
-	[99702] = true,
-	[99701] = true,
-	[99700] = true,
-	[99699] = true,
-	[99698] = true,
-	[99697] = true,
-	[99696] = true,
-	[99695] = true,
-	[99694] = true,
-	[99693] = true,
-	[99692] = true,
-	[99691] = true,
-	[99690] = true,
-	[99689] = true,
-	[99688] = true,
-	[99687] = true,
-	[99686] = true,
-	[99685] = true,
-	[99684] = true,
-	[99683] = true,
-	[99682] = true,
-	[99681] = true,
-	[99680] = true,
-	[99679] = true,
-	[99678] = true,
-	[99677] = true,
-	[99676] = true,
-	[99675] = true,
-	[99674] = true,
-	[99673] = true,
-	[99672] = true,
-	[99671] = true,
-	[99670] = true,
-	[99669] = true,
-	[99668] = true,
-	[99667] = true,
-	[96701] = true,
-	[96700] = true,
-	[96699] = true,
-	[96633] = true,
-	[96632] = true,
-	[96631] = true,
-	[96625] = true,
-	[96624] = true,
-	[96623] = true,
-	[96601] = true,
-	[96600] = true,
-	[96599] = true,
-	[96568] = true,
-	[96567] = true,
-	[96566] = true,
-	[95957] = true,
-	[95956] = true,
-	[95955] = true,
-	[95889] = true,
-	[95888] = true,
-	[95887] = true,
-	[95881] = true,
-	[95880] = true,
-	[95879] = true,
-	[95857] = true,
-	[95856] = true,
-	[95855] = true,
-	[95824] = true,
-	[95823] = true,
-	[95822] = true,
-	[95583] = true,
-	[95582] = true,
-	[95581] = true,
-	[95580] = true,
-	[95579] = true,
-	[95578] = true,
-	[95577] = true,
-	[95576] = true,
-	[95575] = true,
-	[95574] = true,
-	[95573] = true,
-	[95572] = true,
-	[95571] = true,
-	[95570] = true,
-	[95569] = true,
-	[89278] = true,
-	[89277] = true,
-	[89276] = true,
-	[89275] = true,
-	[89274] = true,
-	[89273] = true,
-	[89272] = true,
-	[89271] = true,
-	[89270] = true,
-	[89269] = true,
-	[89268] = true,
-	[89267] = true,
-	[89266] = true,
-	[89265] = true,
-	[89264] = true,
-	[89263] = true,
-	[89262] = true,
-	[89261] = true,
-	[89260] = true,
-	[89259] = true,
-	[89258] = true,
-	[89257] = true,
-	[89256] = true,
-	[89255] = true,
-	[89254] = true,
-	[89253] = true,
-	[89252] = true,
-	[89251] = true,
-	[89250] = true,
-	[89249] = true,
-	[89248] = true,
-	[89247] = true,
-	[89246] = true,
-	[89245] = true,
-	[89244] = true,
-	[89243] = true,
-	[89242] = true,
-	[89241] = true,
-	[89240] = true,
-	[89239] = true,
-	[89238] = true,
-	[89237] = true,
-	[89236] = true,
-	[89235] = true,
-	[89234] = true,
-	[78876] = true,
-	[78875] = true,
-	[78874] = true,
-	[78873] = true,
-	[78872] = true,
-	[78871] = true,
-	[78867] = true,
-	[78866] = true,
-	[78865] = true,
-	[78864] = true,
-	[78863] = true,
-	[78862] = true,
-	[78861] = true,
-	[78860] = true,
-	[78859] = true,
-	[78858] = true,
-	[78857] = true,
-	[78856] = true,
-	[78855] = true,
-	[78854] = true,
-	[78853] = true,
-	[78849] = true,
-	[78848] = true,
-	[78847] = true,
-	[78184] = true,
-	[78183] = true,
-	[78181] = true,
-	[78180] = true,
-	[78179] = true,
-	[78178] = true,
-	[78176] = true,
-	[78175] = true,
-	[78174] = true,
-	[78173] = true,
-	[78171] = true,
-	[78170] = true,
-	[71687] = true,
-	[71686] = true,
-	[71685] = true,
-	[71683] = true,
-	[71682] = true,
-	[71680] = true,
-	[71679] = true,
-	[71678] = true,
-	[71676] = true,
-	[71675] = true,
-	[71673] = true,
-	[71672] = true,
-	[71671] = true,
-	[71669] = true,
-	[71668] = true,
-	[67431] = true,
-	[67430] = true,
-	[67429] = true,
-	[67428] = true,
-	[67427] = true,
-	[67426] = true,
-	[67425] = true,
-	[67424] = true,
-	[67423] = true,
-	[66998] = true,
-	[65089] = true,
-	[65088] = true,
-	[65087] = true,
-	[63684] = true,
-	[63683] = true,
-	[63682] = true,
-	[51320] = true,
-	[45652] = true,
-	[45651] = true,
-	[45650] = true,
-	[45649] = true,
-	[45648] = true,
-	[45647] = true,
-	[45643] = true,
-	[45642] = true,
-	[45641] = true,
-	[40630] = true,
-	[40629] = true,
-	[40628] = true,
-	[40621] = true,
-	[40620] = true,
-	[40619] = true,
-	[40618] = true,
-	[40617] = true,
-	[40616] = true,
-	[34544] = true,
-	[31100] = true,
-	[31099] = true,
-	[31098] = true,
-	[31097] = true,
-	[31096] = true,
-	[31095] = true,
-	[30247] = true,
-	[30246] = true,
-	[30245] = true,
-	[30244] = true,
-	[30243] = true,
-	[30242] = true,
-	[29767] = true,
-	[29766] = true,
-	[29765] = true,
-	[29761] = true,
-	[29760] = true,
-	[29759] = true
-}
-
 
 ----------------------------------------------------------------------------------------
 -- Public Utility Methods
 ----------------------------------------------------------------------------------------
 
 local function readBonusIdList(parts, first, last)
-	local ret = {}
+	local ret = {}	
 	for i = first, last do
 		table.insert(ret, tonumber(parts[i]))
 	end
@@ -636,22 +180,10 @@
 	return ret
 end
 
-local function setRelicId(item, index, relicBonuses)
-	local relicId = item.gemIds[index] .. ""
-	for i = 1, #relicBonuses do
-		relicId = relicId .. "." .. relicBonuses[i]
-	end
-	local list = item.gemItemIds or {}
-	list[i] = relicId
-end
-
---|color|Hitem:135820:enchant:gem1:gem2:gem3:gem4:suffixID:uniqueID:playerlevel:spec?:flags:11:numBonusIDs:bonusID1:bonusID2...:playerlevelwhengotitem, 296 for warrior artifact:upgrade ID?:num artifact bonuses?:artifact bonus 1:artifact bonus 2:artifact bonus 3:[item name]
--- 133004 for relic on my warrior, gem2
--- 296::3:767:1507:1809:[item name] this is for warrior artifact with the above relic in storm slot, for parts after the bonus IDs
-
---|cffa335ee|Hitem:itemID:enchant:gem1:gem2:gem3:gem4:suffixID:uniqueID:level:unknown:unknown:instanceDifficultyID:numBonusIDs:bonusID1:bonusID2...|h[item name]|h|r
-
--- item link format:  |cffa335ee|Hitem:itemID:enchant:gem1:gem2:gem3:gem4:suffixID:uniqueID:level:unknown:unknown:instanceDifficultyID:numBonusIDs:bonusID1:bonusID2...|h[item name]|h|r
+--                 1      2    3      4      5      6    7   8   9   10   11       12         
+--                 itemId:ench:gem1  :gem2  :gem3  :gem4:suf:uid:lvl:spec:flags   :instdiffid:numbonusIDs:bonusIDs1...n     :varies:?:relic bonus ids
+--|cffe6cc80|Hitem:128866:    :152046:147100:152025:    :   :   :110:66  :16777472:9         :4          :736:1494:1490:1495:709   :1:3:3610:1472:3528:3:3562:1483:3528:3:3610:1477:3336|h[Truthguard]|h|r
+--
 -- get an object with all of the parts of the item link format that we care about
 function Amr.ParseItemLink(itemLink)
     if not itemLink then return nil end
@@ -661,7 +193,8 @@
     
     local parts = { strsplit(":", str) }
     
-    local item = {}
+	local item = {}
+	item.link = itemLink
     item.id = tonumber(parts[1]) or 0
     item.enchantId = tonumber(parts[2]) or 0
     item.gemIds = { tonumber(parts[3]) or 0, tonumber(parts[4]) or 0, tonumber(parts[5]) or 0, tonumber(parts[6]) or 0 }
@@ -681,39 +214,38 @@
 	item.upgradeId = 0
 	item.level = 0
 	
-	-- the next part after bonus IDs depends on the upgrade id type; is either the "drop level" or upgrade ID, or not sure for artifacts
+	-- the next part after bonus IDs depends on the upgrade id type
 	if upgradeIdType == 4 then
 		item.upgradeId = tonumber(parts[14 + offset]) or 0
 	elseif upgradeIdType == 512 then
 		item.level = tonumber(parts[14 + offset]) or 0
+	elseif #parts > 16 + offset then
+		-- check for relic info
+		item.relicBonusIds = { nil, nil, nil }
+		numBonuses = tonumber(parts[16 + offset])
+		if numBonuses then
+			if numBonuses > 0 then
+				item.relicBonusIds[1] = readBonusIdList(parts, 17 + offset, 16 + offset + numBonuses)
+			end
+					
+			offset= offset + numBonuses
+			if #parts > 17 + offset then
+				numBonuses = tonumber(parts[17 + offset])
+				if numBonuses > 0 then
+					item.relicBonusIds[2] = readBonusIdList(parts, 18 + offset, 17 + offset + numBonuses)
+				end
+
+				offset= offset + numBonuses
+				if #parts > 18 + offset then
+					numBonuses = tonumber(parts[18 + offset])
+					if numBonuses > 0 then
+						item.relicBonusIds[3] = readBonusIdList(parts, 19 + offset, 18 + offset + numBonuses)
+					end	
+				end		
+			end
+		end
 	end
 	
-	-- ignore relic stuff in the item link for now, we read the relic information directly and save it with artifact power info
-	--[[
-	-- the next part is the number of bonus IDs on the first relic slot of the artifact
-	numBonuses = tonumber(parts[15 + offset]) or 0
-	if numBonuses > 0 then
-		local relicBonuses = readBonusIdList(16 + offset, 15 + offset + numBonuses, parts)
-		setRelicId(item, 1, relicBonuses)
-	end
-	
-	-- second relic slot bonus IDs
-	offset = offset + numBonuses
-	numBonuses = tonumber(parts[16 + offset]) or 0
-	if numBonuses > 0 then
-		local relicBonuses = readBonusIdList(17 + offset, 16 + offset + numBonuses, parts)
-		setRelicId(item, 2, relicBonuses)
-	end
-	
-	-- third relic slot bonus IDs
-	offset = offset + numBonuses
-	numBonuses = tonumber(parts[17 + offset]) or 0
-	if numBonuses > 0 then
-		local relicBonuses = readBonusIdList(18 + offset, 17 + offset + numBonuses, parts)
-		setRelicId(item, 3, relicBonuses)
-	end
-    ]]
-	
     return item
 end
 
@@ -801,7 +333,7 @@
 	return tt
 end
 
-function Amr.GetItemLevel(bagId, slotId, link)
+function Amr.GetItemLevel(bagId, slotId, link)	
 	local itemLevelPattern = _G["ITEM_LEVEL"]:gsub("%%d", "(%%d+)")
 	local tt = Amr.GetItemTooltip(bagId, slotId, link)
 	
@@ -836,46 +368,7 @@
 	end
 end
 
---[[
-local function getTalents(specPos)	
-    local talentInfo = {}
-    local maxTiers = 7
-    for tier = 1, maxTiers do
-        for col = 1, 3 do
-            local id, name, _, _, _, spellId, _, t, c, selected = GetTalentInfoBySpecialization(specPos, tier, col)
-            if selected then
-                talentInfo[tier] = col
-            end
-        end
-    end
-    
-    local str = ""
-    for i = 1, maxTiers do
-    	if talentInfo[i] then
-    		str = str .. talentInfo[i]
-    	else
-    		str = str .. '0'
-    	end
-    end    	
-
-	return str
-end
-]]
-
---[[
-local function getGlyphs(specGroup)
-	local glyphs = {}
-	for i = 1, NUM_GLYPH_SLOTS do
-		local _, _, _, glyphSpellID, _, glyphID = GetGlyphSocketInfo(i, specGroup)
-		if (glyphID) then
-			table.insert(glyphs, glyphSpellID)
-		end
-	end
-	return glyphs;
-end
-]]
-
--- get specs and talents
+-- get specs
 local function readSpecs(ret)
 
     for pos = 1, 4 do
@@ -883,20 +376,67 @@
         local specId = GetSpecializationInfo(pos)
         if specId then
             ret.Specs[pos] = Amr.SpecIds[specId]
-			-- TODO: figure out how to read inactive spec talents if possible... used to be able to but they changed it
-			--ret.Talents[pos] = getTalents(pos)
         end
 	end
 end
 
+local function dump(o)
+	if type(o) == 'table' then
+	   local s = '{ '
+	   for k,v in pairs(o) do
+		  if type(k) ~= 'number' then k = '"'..k..'"' end
+		  s = s .. '['..k..'] = ' .. dump(v) .. ','
+	   end
+	   return s .. '} '
+	else
+	   return tostring(o)
+	end
+end
+
+-- read azerite powers on the item in loc and put it on itemData
+function Amr.ReadAzeritePowers(loc)
+	local ret = {}
+	local hasSome = false
+	
+	local tiers = C_AzeriteEmpoweredItem.GetAllTierInfo(loc)
+	for tier, tierInfo in ipairs(tiers) do
+		for _, power in ipairs(tierInfo.azeritePowerIDs) do
+			if C_AzeriteEmpoweredItem.IsPowerSelected(loc, power) then
+				local powerInfo = C_AzeriteEmpoweredItem.GetPowerInfo(power)
+				table.insert(ret, powerInfo.spellID)
+				hasSome = true
+			end
+		end
+	end
+
+	if hasSome then
+		return ret
+	else
+		return nil
+	end
+end
+
 -- get currently equipped items, store with currently active spec
 local function readEquippedItems(ret)
-    local equippedItems = {};
+	local equippedItems = {};
+	local loc = ItemLocation.CreateEmpty()
 	for slotNum = 1, #Amr.SlotIds do
 		local slotId = Amr.SlotIds[slotNum]
 		local itemLink = GetInventoryItemLink("player", slotId)
 		if itemLink then
-			equippedItems[slotId] = itemLink
+			local itemData = Amr.ParseItemLink(itemLink)
+			if itemData then
+				-- see if this is an azerite item and read azerite power ids
+				loc:SetEquipmentSlot(slotId)
+				if C_AzeriteEmpoweredItem.IsAzeriteEmpoweredItem(loc) then
+					local powers = Amr.ReadAzeritePowers(loc)
+					if powers then
+						itemData.azerite = powers
+					end
+				end
+
+				equippedItems[slotId] = itemData
+			end
 		end
 	end
     
@@ -904,6 +444,24 @@
 	ret.Equipped[GetSpecialization()] = equippedItems
 end
 
+local function readHeartOfAzerothLevel(ret)
+	local azeriteItemLocation = C_AzeriteItem.FindActiveAzeriteItem();	
+	if azeriteItemLocation then 
+		local azeriteItem = Item:CreateFromItemLocation(azeriteItemLocation); 
+		ret.HeartOfAzerothLevel = C_AzeriteItem.GetPowerLevel(azeriteItemLocation)
+	else
+		ret.HeartOfAzerothLevel = 0
+	end	
+end
+
+-- Get just the player's currently equipped gear
+function Amr:GetEquipped()
+	local ret= {}
+	ret.Equipped = {}
+	readEquippedItems(ret)
+	return ret
+end
+
 -- Get all data about the player as an object, includes:
 -- serializer version
 -- region/realm/name
@@ -913,7 +471,6 @@
 -- level
 -- professions
 -- spec/talent for all specs
--- artifact for current spec
 -- equipped gear for the current spec
 --
 function Amr:GetPlayerData()
@@ -926,7 +483,8 @@
 	ret.Guild = GetGuildInfo("player")
     ret.ActiveSpec = GetSpecialization()
     ret.Level = UnitLevel("player");
-    
+	readHeartOfAzerothLevel(ret)
+	
     local cls, clsEn = UnitClass("player")
     ret.Class = clsEn;
     
@@ -947,9 +505,7 @@
     ret.Talents = {}
 	readSpecs(ret)
 	
-	ret.Artifacts = {}	
-	
-	ret.Equipped = {}
+	ret.Equipped = {}	
 	readEquippedItems(ret)
 	
 	return ret
@@ -997,20 +553,16 @@
     local prevUpgradeId = 0
     local prevBonusId = 0
 	local prevLevel = 0
+	local prevAzeriteId = 0
+	local prevRelicBonusId = 0
     for i, itemData in ipairs(itemObjects) do
         local itemParts = {}
         
-		-- for now export the item level of artifacts as the "drop level" because it is a pain in the ass to figure it out from the bonus IDs
-		--local _, _, quality = GetItemInfo(itemData.link)
-		--if quality == 6 then
-		--	itemData.level = Amr.GetItemLevel(nil, nil, itemData.link)
-		--end
-		
         table.insert(itemParts, itemData.id - prevItemId)
         prevItemId = itemData.id
         
         if itemData.slot ~= nil then table.insert(itemParts, "s" .. itemData.slot) end
-        if itemData.suffixId ~= 0 then table.insert(itemParts, "f" .. itemData.suffixId) end
+        --if itemData.suffixId ~= 0 then table.insert(itemParts, "f" .. itemData.suffixId) end
         if itemData.upgradeId ~= 0 then 
             table.insert(itemParts, "u" .. (itemData.upgradeId - prevUpgradeId))
             prevUpgradeId = itemData.upgradeId
@@ -1024,7 +576,14 @@
                 table.insert(itemParts, "b" .. (bValue - prevBonusId))
                 prevBonusId = bValue
             end
-        end
+		end
+		
+		if itemData.azerite then
+			for aIndex, aValue in ipairs(itemData.azerite) do
+                table.insert(itemParts, "a" .. (aValue - prevAzeriteId))
+                prevAzeriteId = aValue
+            end
+		end
 		
 		if itemData.gemIds[1] ~= 0 then 
 			table.insert(itemParts, "x" .. (itemData.gemIds[1] - prevGemId))
@@ -1037,46 +596,38 @@
 		if itemData.gemIds[3] ~= 0 then 
 			table.insert(itemParts, "z" .. (itemData.gemIds[3] - prevGemId))
 			prevGemId = itemData.gemIds[3]
-		end
+		end		
         
         if itemData.enchantId ~= 0 then 
             table.insert(itemParts, "e" .. (itemData.enchantId - prevEnchantId))
             prevEnchantId = itemData.enchantId
         end
-    
+	
+		if itemData.relicBonusIds and itemData.relicBonusIds[1] ~= nil then
+			for bIndex, bValue in ipairs(itemData.relicBonusIds[1]) do
+                table.insert(itemParts, "p" .. (bValue - prevRelicBonusId))
+                prevRelicBonusId = bValue
+            end
+		end
+
+		if itemData.relicBonusIds and itemData.relicBonusIds[2] ~= nil then
+			for bIndex, bValue in ipairs(itemData.relicBonusIds[2]) do
+                table.insert(itemParts, "q" .. (bValue - prevRelicBonusId))
+                prevRelicBonusId = bValue
+            end
+		end
+
+		if itemData.relicBonusIds and itemData.relicBonusIds[3] ~= nil then
+			for bIndex, bValue in ipairs(itemData.relicBonusIds[3]) do
+                table.insert(itemParts, "r" .. (bValue - prevRelicBonusId))
+                prevRelicBonusId = bValue
+            end
+		end
+		
         table.insert(fields, table.concat(itemParts, ""))
     end
 end
 
-local function serializeCrucibleInfo(fields, info, pos, prevPowerId)
-
-	if not info.Powers or not info.Active then 
-		return prevPowerId
-	end
-	
-	local parts = {}
-	
-	if pos < 4 then
-		table.insert(parts, pos)
-	else
-		local relic = Amr.ParseItemLink(info.ItemLink)
-		table.insert(parts, Amr.GetItemUniqueId(relic) or "0")
-	end
-	
-	for i,powerId in ipairs(info.Powers) do
-		table.insert(parts, (powerId - prevPowerId) .. "")
-		prevPowerId = powerId
-	end
-	
-	for i,active in ipairs(info.Active) do
-		table.insert(parts, active and "1" or "0")
-	end
-	
-	table.insert(fields, table.concat(parts, ","))
-	
-	return prevPowerId
-end
-
 -- Serialize just the identity portion of a player (region/realm/name) in the same format used by the full serialization
 function Amr:SerializePlayerIdentity(data)
 	local fields = {}    
@@ -1121,7 +672,8 @@
     if raceval == nil then raceval = 1 end
     table.insert(fields, raceval)
     
-    table.insert(fields, data.Level)
+	table.insert(fields, data.Level)
+	table.insert(fields, data.HeartOfAzerothLevel)
     
     local profs = {}
     local noprofs = true
@@ -1147,49 +699,7 @@
         if data.Specs[spec] and (complete or spec == data.ActiveSpec) then
             table.insert(fields, ".s" .. spec) -- indicates the start of a spec block
 			table.insert(fields, data.Specs[spec])
-            table.insert(fields, data.Talents[spec] or "")
-			
-			local powerids = {}
-			local powerranks = {}
-			local reliclinks = {}			
-			local crucibleinfos = {}
-			
-			local artifactInfo = data.Artifacts and data.Artifacts[spec]
-			if artifactInfo and artifactInfo.Powers then
-				for k, v in spairs(artifactInfo.Powers) do
-					table.insert(powerids, k)
-					table.insert(powerranks, v)
-				end	
-			end
-			if artifactInfo and artifactInfo.Relics then
-				for i, link in ipairs(artifactInfo.Relics) do
-					local relic = Amr.ParseItemLink(link)
-					table.insert(reliclinks, Amr.GetItemUniqueId(relic) or "")
-				end
-			end
-			if artifactInfo and artifactInfo.Crucible then
-				local prevPowerId = 0
-				for i = 1,3 do
-					local relicInfo = #artifactInfo.Crucible.Equipped >= i and artifactInfo.Crucible.Equipped[i]
-					if relicInfo then
-						prevPowerId = serializeCrucibleInfo(crucibleinfos, relicInfo, i, prevPowerId)
-					end
-				end
-				if artifactInfo.Crucible.Previewed then
-					for k,relicInfo in pairs(artifactInfo.Crucible.Previewed) do
-						if relicInfo then
-							prevPowerId = serializeCrucibleInfo(crucibleinfos, relicInfo, 4, prevPowerId)
-						end
-					end
-				end
-			end
-			
-			table.insert(fields, toCompressedNumberList(powerids))
-			table.insert(fields, table.concat(powerranks, ","))
-			table.insert(fields, table.concat(reliclinks, ","))
-			table.insert(fields, table.concat(crucibleinfos, "/"))
-			
-            --table.insert(fields, toCompressedNumberList(data.Glyphs[spec]))
+            table.insert(fields, data.Talents[spec] or "")			
         end
     end
     
@@ -1200,10 +710,8 @@
                 table.insert(fields, ".q" .. spec) -- indicates the start of an equipped gear block
                 
                 local itemObjects = {}
-                for k, v in pairs(data.Equipped[spec]) do
-                    local itemData = Amr.ParseItemLink(v)
+                for k, itemData in pairs(data.Equipped[spec]) do
                     itemData.slot = k
-					itemData.link = v
                     table.insert(itemObjects, itemData)
                 end
                 
@@ -1212,58 +720,25 @@
         end
 	end
     
-    -- if doing a complete export, include reputations and bank/bag items too
-    if complete then
-    
-        -- export reputations
-        local reps = {}
-		table.insert(reps, "_")
-		--[[
-        local noreps = true
-        if data.Reputations then
-            for k, v in pairs(data.Reputations) do
-                noreps = false
-                table.insert(reps, k .. ":" .. v)
-            end
-        end
-        if noreps then
-            table.insert(reps, "_")
-        end
-        ]]
-		
-        table.insert(fields, ".r")
-        table.insert(fields, table.concat(reps, ","))    
-    
-        -- export bag and bank
+    -- if doing a complete export, include bank/bag items too
+	if complete then
+		    
         local itemObjects = {}
     	if data.BagItems then
-	        for i, v in ipairs(data.BagItems) do
-				local itemData = Amr.ParseItemLink(v)
-				if itemData ~= nil --[[and (IsEquippableItem(v) or Amr.SetTokenIds[itemData.id])]] then
-					itemData.link = v
+	        for i, itemData in ipairs(data.BagItems) do
+				if itemData then
 					table.insert(itemObjects, itemData)
 				end
 	        end
 	    end
 	    if data.BankItems then
-	        for i, v in ipairs(data.BankItems) do
-	        	local itemData = Amr.ParseItemLink(v)
-				if itemData ~= nil --[[and (IsEquippableItem(v) or Amr.SetTokenIds[itemData.id])]] then
-					itemData.link = v
+	        for i, itemData in ipairs(data.BankItems) do
+				if itemData then
 					table.insert(itemObjects, itemData)
 				end
 	        end
-	    end
-	    if data.VoidItems then
-	        for i, v in ipairs(data.VoidItems) do
-	        	local itemData = Amr.ParseItemLink(v)
-				if itemData ~= nil --[[and (IsEquippableItem(v) or Amr.SetTokenIds[itemData.id])]] then
-					itemData.link = v
-					table.insert(itemObjects, itemData)
-				end
-		    end
-	    end
-        
+		end
+		
         table.insert(fields, ".inv")
         appendItemsToExport(fields, itemObjects)
     end
--- a/AskMrRobot.toc	Mon Feb 12 19:34:09 2018 -0800
+++ b/AskMrRobot.toc	Tue Jul 17 09:57:39 2018 -0700
@@ -1,10 +1,10 @@
-## Interface: 70300
+## Interface: 80000
 ## Title: Ask Mr. Robot
 ## Author: Team Robot, Inc.
-## Version: 57
+## Version: 58
 ## Notes: Gear import/export, combat logging, and more.
 ## URL: www.askmrrobot.com
-## SavedVariables: AskMrRobotDb3
+## SavedVariables: AskMrRobotDb4
 
 Libs\LibStub\LibStub.lua
 Libs\CallbackHandler-1.0\CallbackHandler-1.0.xml
@@ -22,8 +22,8 @@
 localization\enUS.lua
 localization\ruRU.lua
 localization\ptBR.lua
-##localization\frFR.lua
-##localization\itIT.lua
+localization\frFR.lua
+localization\itIT.lua
 
 AskMrRobot-Serializer\AskMrRobot-Serializer.xml
 
@@ -48,6 +48,4 @@
 Shopping.lua
 Gear.lua
 CombatLog.lua
-Loot.lua
-TeamOptimizer.lua
 Options.lua
--- a/CombatLog.lua	Mon Feb 12 19:34:09 2018 -0800
+++ b/CombatLog.lua	Tue Jul 17 09:57:39 2018 -0700
@@ -23,36 +23,36 @@
 	_autoChecks[instanceId] = {}
 	
 	local lbl = AceGUI:Create("AmrUiLabel")
+	container:AddChild(lbl)
 	lbl:SetWidth(200)
 	lbl:SetText(L.InstanceNames[instanceId])
 	lbl:SetFont(Amr.CreateFont("Regular", 20, Amr.Colors.White))
-	container:AddChild(lbl)
 	
 	local line = AceGUI:Create("AmrUiPanel")
 	line:SetHeight(1)
 	line:SetBackgroundColor(Amr.Colors.White)
+	container:AddChild(line)
 	line:SetPoint("TOPLEFT", lbl.frame, "BOTTOMLEFT", 1, -7)
 	line:SetPoint("TOPRIGHT", lbl.frame, "BOTTOMRIGHT", 0, -7)
-	container:AddChild(line)
 	
 	local chkMythic = createDifficultyCheckBox(instanceId, Amr.Difficulties.Mythic)
+	container:AddChild(chkMythic)
 	chkMythic:SetPoint("TOPLEFT", line.frame, "BOTTOMLEFT", 0, -8)
-	container:AddChild(chkMythic)
 	
 	local chkNormal = createDifficultyCheckBox(instanceId, Amr.Difficulties.Normal)
+	container:AddChild(chkNormal)
 	chkNormal:SetPoint("TOPLEFT", line.frame, "BOTTOMLEFT", 0, -30)
-	container:AddChild(chkNormal)
 	
 	-- find the widest of mythic/normal
 	local w = math.max(chkMythic:GetWidth(), chkNormal:GetWidth())
 	
 	local chkHeroic = createDifficultyCheckBox(instanceId, Amr.Difficulties.Heroic)
+	container:AddChild(chkHeroic)
 	chkHeroic:SetPoint("TOPLEFT", line.frame, "BOTTOMLEFT", w + 20, -8)
-	container:AddChild(chkHeroic)
 	
 	local chkLfr = createDifficultyCheckBox(instanceId, Amr.Difficulties.Lfr)
+	container:AddChild(chkLfr)
 	chkLfr:SetPoint("TOPLEFT", line.frame, "BOTTOMLEFT", w + 20, -30)
-	container:AddChild(chkLfr)
 	
 	return lbl, chkNormal
 end
@@ -67,17 +67,17 @@
 	_btnToggle:SetFont(Amr.CreateFont("Bold", 16, Amr.Colors.White))
 	_btnToggle:SetWidth(200)
 	_btnToggle:SetHeight(26)
-	_btnToggle:SetPoint("TOPLEFT", container.content, "TOPLEFT", 0, -40)
 	_btnToggle:SetCallback("OnClick", function() Amr:ToggleLogging() end)
 	container:AddChild(_btnToggle)
+	_btnToggle:SetPoint("TOPLEFT", container.content, "TOPLEFT", 0, -40)
 	
 	_lblLogging = AceGUI:Create("AmrUiLabel")
+	container:AddChild(_lblLogging)
 	_lblLogging:SetText(L.LogNote)
 	_lblLogging:SetWidth(200)	
 	_lblLogging:SetFont(Amr.CreateFont("Italic", 14, Amr.Colors.BrightGreen))
 	_lblLogging:SetJustifyH("MIDDLE")
 	_lblLogging:SetPoint("TOP", _btnToggle.frame, "BOTTOM", 0, -5)
-	container:AddChild(_lblLogging)
 	
 	local btnReload = AceGUI:Create("AmrUiButton")
 	btnReload:SetText(L.LogButtonReloadText)
@@ -85,9 +85,9 @@
 	btnReload:SetFont(Amr.CreateFont("Bold", 16, Amr.Colors.White))
 	btnReload:SetWidth(200)
 	btnReload:SetHeight(26)
-	btnReload:SetPoint("TOPLEFT", _btnToggle.frame, "TOPRIGHT", 40, 0)
 	btnReload:SetCallback("OnClick", ReloadUI)
 	container:AddChild(btnReload)
+	btnReload:SetPoint("TOPLEFT", _btnToggle.frame, "TOPRIGHT", 40, 0)
 	
 	--[[
 	local lbl = AceGUI:Create("AmrUiLabel")
@@ -160,17 +160,17 @@
 	
 	-- auto-logging controls
 	local lbl = AceGUI:Create("AmrUiLabel")
+	container:AddChild(lbl)
 	lbl:SetWidth(600)
 	lbl:SetText(L.LogAutoTitle)
 	lbl:SetFont(Amr.CreateFont("Bold", 24, Amr.Colors.TextHeaderActive))
 	lbl:SetPoint("TOPLEFT", _btnToggle.frame, "BOTTOMLEFT", 0, -40)
-	container:AddChild(lbl)
 	
 	_chkAutoAll = AceGUI:Create("AmrUiCheckBox")
 	_chkAutoAll:SetText(L.LogAutoAllText)
-	_chkAutoAll:SetPoint("TOPLEFT", lbl.frame, "BOTTOMLEFT", 1, -15)
 	_chkAutoAll:SetCallback("OnClick", function(widget) Amr:ToggleAllAutoLog() end)
 	container:AddChild(_chkAutoAll)
+	_chkAutoAll:SetPoint("TOPLEFT", lbl.frame, "BOTTOMLEFT", 1, -15)
 	
 	_autoChecks = {}
 	
@@ -190,8 +190,9 @@
 		table.insert(autoLbls, autoLbl)
 		table.insert(autoChks, autoChk)
 	end
-	autoSections = nil
-	
+	autoLbls = nil
+	autoChks = nil
+
 	-- instructions
 	--[[
 	lbl = AceGUI:Create("AmrUiLabel")
--- a/Constants.lua	Mon Feb 12 19:34:09 2018 -0800
+++ b/Constants.lua	Tue Jul 17 09:57:39 2018 -0700
@@ -2,11 +2,10 @@
 local L = LibStub("AceLocale-3.0"):GetLocale("AskMrRobot", true)
 
 -- min import version that we will read from the website
-Amr.MIN_IMPORT_VERSION = 36
+Amr.MIN_IMPORT_VERSION = 58
 
--- min addon version that we will support for inter-addon communication for e.g. the team optimizer
---  last update to version 36 for Legion pre-patch
-Amr.MIN_ADDON_VERSION = 44
+-- min addon version that we will support for inter-addon communication
+Amr.MIN_ADDON_VERSION = 58
 
 -- import some constants from the serializer for convenience
 Amr.ChatPrefix = Amr.Serializer.ChatPrefix
@@ -22,11 +21,9 @@
 Amr.ParseItemLink = Amr.Serializer.ParseItemLink
 Amr.IsSupportedInstanceId = Amr.Serializer.IsSupportedInstanceId
 Amr.IsSupportedInstance = Amr.Serializer.IsSupportedInstance
-Amr.SetTokenIds = Amr.Serializer.SetTokenIds
 Amr.GetItemTooltip = Amr.Serializer.GetItemTooltip
 Amr.GetItemLevel = Amr.Serializer.GetItemLevel
 Amr.GetItemUniqueId = Amr.Serializer.GetItemUniqueId
-Amr.ArtifactIdToSpecNumber = Amr.Serializer.ArtifactIdToSpecNumber
 
 -- map of slot ID to display text
 Amr.SlotDisplayText = {
@@ -129,8 +126,10 @@
 -- Item Methods
 ------------------------------------------------------------------------------------------
 
--- item link format:  |cffa335ee|Hitem:itemID:enchant:gem1:gem2:gem3:gem4:suffixID:uniqueID:level:upgradeId:instanceDifficultyID:numBonusIDs:bonusID1:bonusID2...|h[item name]|h|r
-
+--                 1      2    3      4      5      6    7   8   9   10   11       12         
+--                 itemId:ench:gem1  :gem2  :gem3  :gem4:suf:uid:lvl:spec:flags   :instdiffid:numbonusIDs:bonusIDs1...n     :varies:?:relic bonus ids
+--|cffe6cc80|Hitem:128866:    :152046:147100:152025:    :   :   :110:66  :16777472:9         :4          :736:1494:1490:1495:709   :1:3:3610:1472:3528:3:3562:1483:3528:3:3610:1477:3336|h[Truthguard]|h|r
+--
 function Amr.CreateItemLink(itemObj)
 
     if itemObj == nil or itemObj.id == nil or itemObj.id == 0 then return nil end
@@ -161,8 +160,6 @@
 		table.insert(parts, 4)
 	elseif itemObj.level and itemObj.level ~= 0 then
 		table.insert(parts, 512)
-	elseif itemObj.relicBonusIds then
-		table.insert(parts, 256)
 	else
 		table.insert(parts, 0)
 	end
@@ -186,25 +183,11 @@
 	else
 		table.insert(parts, 0)
 	end
-	
-	-- sometimes we provide relic bonus IDs
-	if itemObj.relicBonusIds then
-		for i = 1,3 do
-			local bonusList = itemObj.relicBonusIds[i]
-			if bonusList and #bonusList > 0 then
-				table.insert(parts, #bonusList)
-				for bi, bv in ipairs(bonusList) do
-					table.insert(parts, bv)
-				end
-			else
-				table.insert(parts, 0)
-			end
-		end
-	else
-		table.insert(parts, 0)
-		table.insert(parts, 0)
-		table.insert(parts, 0)
-	end
+    
+    -- we don't bother with relic bonus ids anymore when generating links
+    table.insert(parts, 0)
+    table.insert(parts, 0)
+    table.insert(parts, 0)
     
     return table.concat(parts, ":")
 end
--- a/Core.lua	Mon Feb 12 19:34:09 2018 -0800
+++ b/Core.lua	Tue Jul 17 09:57:39 2018 -0700
@@ -1,9 +1,3 @@
--- AskMrRobot
--- Does cool stuff associated with askmrrobot.com:
---   Import/Export gear and optimization solutions from/to the website
---   Improve the combat logging experience and augment it with extra data not available directly in the log file
---   Team Optimizer convenience functionality
-
 AskMrRobot = LibStub("AceAddon-3.0"):NewAddon("AskMrRobot", "AceEvent-3.0", "AceComm-3.0", "AceConsole-3.0", "AceSerializer-3.0")
 local Amr = AskMrRobot
 Amr.Serializer = LibStub("AskMrRobot-Serializer")
@@ -13,8 +7,7 @@
 -- types of inter-addon messages that we receive, used to parcel them out to the proper handlers
 Amr.MessageTypes = {
 	Version = "_V",
-	VersionRequest = "_VR",
-	Team = "_T"
+	VersionRequest = "_VR"
 }
 
 local L = LibStub("AceLocale-3.0"):GetLocale("AskMrRobot", true)
@@ -27,11 +20,11 @@
 	icon = "Interface\\AddOns\\" .. Amr.ADDON_NAME .. "\\Media\\icon",
 	OnClick = function(self, button, down)
 		if button == "LeftButton" then
-			if IsControlKeyDown() then
-				Amr:Wipe()
-			else
+			--if IsControlKeyDown() then
+			--	Amr:Wipe()
+			--else
 				Amr:Toggle()
-			end
+			--end
 		elseif button == "RightButton" then
 			Amr:EquipGearSet()
 		end
@@ -52,31 +45,18 @@
 		char = {
 			FirstUse = true,           -- true if this is first time use, gets cleared after seeing the export help splash window
 			Talents = {},              -- for each spec, selected talents
-			Artifacts = {},            -- for each spec, artifact info
-			Equipped = {},             -- for each spec, slot id to item link
-			BagItems = {},             -- list of item links for bag
-			BankItems = {},            -- list of item links for bank
-			VoidItems = {},            -- list of item links for void storage
+			Equipped = {},             -- for each spec, slot id to item info
+			BagItems = {},             -- list of item info for bags
+			BankItems = {},            -- list of item info for bank
 			BagItemsAndCounts = {},    -- used mainly for the shopping list
 			BankItemsAndCounts = {},   -- used mainly for the shopping list			
-			GearSets = {},             -- imported gear sets, key by spec group (1 or 2), slot id to item object
-			ExtraItemData = {},        -- for each spec group (1 or 2): mainly for legacy support, item id to object with socketColor and duplicateId information
-			ExtraGemData = {},         -- for each spec group (1 or 2): gem enchant id to gem display information, and data used to detect identical gems (mainly for legacy support)
-			ExtraEnchantData = {},     -- for each spec group (1 or 2): enchant id to enchant display information and material information
+			GearSets = {},             -- imported gear sets
+			ExtraEnchantData = {},     -- enchant id to enchant display information and material information
 			Logging = {                -- character logging settings
 				Enabled = false,       -- whether logging is currently on or not
 				LastZone = nil,        -- last zone the player was in
 				LastDiff = nil,        -- last difficulty for the last zone the player was in
 				LastWipe = nil         -- last time a wipe was called by this player
-			},
-			TeamOpt = {
-				AllItems = {},         -- all equippable items no matter where it is, list of item unique ids, used to determine when a player gains a new equippable item
-				History = {},          -- history of drops since joining the current group
-				Rolls = {},            -- current loot choices for a loot distribution in progress
-				Role = nil,            -- Leader or Member, changes UI to the mode most appropriate for this user
-				Loot = {},             -- the last loot seen by the master looter
-				LootGuid = nil,        -- guid of the last unit looted by the master looter, will be "container" if there is no target
-				LootInProgress = false -- true if looting is currently in progress
 			}
 		},
 		profile = {
@@ -84,7 +64,6 @@
 				hide = false
 			},
 			window = {},               -- main window position settings
-			lootWindow = {},           -- loot window position settings
 			shopWindow = {},           -- shopping list window position settings
 			options = {
 				autoGear = false,      -- auto-equip saved gear sets when changing specs
@@ -103,16 +82,11 @@
 				Wipes = {},            -- times that a wipe was called
 				PlayerData = {},       -- player data gathered at fight start
 				PlayerExtras = {}      -- player extra data like auras, gathered at fight start
-			},
-			TeamOpt = {                -- this stuff is stored globally in case a player e.g. switches to an alt in a raid group
-				LootGear = {},         -- gear info that needs to be transmitted with the next loot
-				Rankings = {},         -- last rankings imported by the loot ranker
-				RankingString = nil    -- last ranking string imported, kept around for efficient serialization
 			}
 		}
 	}
 	
-	Amr.db = LibStub("AceDB-3.0"):New("AskMrRobotDb3", defaults)
+	Amr.db = LibStub("AceDB-3.0"):New("AskMrRobotDb4", defaults)
 	
 	-- set defaults for auto logging; if a new zone is added and some other stuff was turned on, turn on the new zone too
 	local hasSomeLogging = false
@@ -175,7 +149,6 @@
 		Amr:InitializeGear()
 		Amr:InitializeExport()
 		Amr:InitializeCombatLog()
-		Amr:InitializeTeamOpt()
 	end)
 end
 
@@ -226,10 +199,10 @@
 	hide      = "Hide",
 	show      = "Show",
 	toggle    = "Toggle",
-	equip     = "EquipGearSet",     -- parameter is "primary" or "secondary", or no parameter to toggle
+	equip     = "EquipGearSet",
 	version   = "PrintVersions",
-	wipe      = "Wipe",
-	undowipe  = "UndoWipe",
+	--wipe      = "Wipe",
+	--undowipe  = "UndoWipe",
 	reset     = "Reset",
 	test      = "Test"
 }
@@ -449,6 +422,20 @@
 	return string.sub(str, 1, string.len(prefix)) == prefix
 end
 
+function Amr.IsEmpty(table)
+	return next(table) == nil
+end
+
+function Amr.Contains(table, value)
+	if not table then return false end
+	for k,v in pairs(table) do
+		if v == value then
+			return true
+		end
+	end
+	return false
+end
+
 -- helper to get the unit identifiers (e.g. to pass to GetUnitName) for all members of the player's current group/raid
 function Amr:GetGroupUnitIdentifiers()
 
@@ -527,12 +514,21 @@
 	return false
 end
 
--- helper to determine if we can equip an item (it is already soulbound or account bound)
+-- helper to determine if we can equip an item (it is soulbound)
 function Amr:CanEquip(bagId, slotId)
-	local tt = Amr.GetItemTooltip(bagId, slotId)
-	if self:IsTextInTooltip(tt, ITEM_SOULBOUND) then return true end
-	if self:IsTextInTooltip(tt, ITEM_BNETACCOUNTBOUND) then return true end
-	if self:IsTextInTooltip(tt, ITEM_ACCOUNTBOUND) then return true end
+	local item = Item:CreateFromBagAndSlot(bagId, slotId)
+	if item then
+		local loc = item:GetItemLocation()
+		return C_Item.IsBound(loc)
+	else
+		-- for now just return true if we can't find the item... will get an error trying to equip if it isn't bound
+		return true
+	end
+
+	--local tt = Amr.GetItemTooltip(bagId, slotId)
+	--if self:IsTextInTooltip(tt, ITEM_SOULBOUND) then return true end
+	--if self:IsTextInTooltip(tt, ITEM_BNETACCOUNTBOUND) then return true end
+	--if self:IsTextInTooltip(tt, ITEM_ACCOUNTBOUND) then return true end
 end
 
 -- helper to determine if an item has a unique constraint
@@ -584,6 +580,7 @@
 	-- any other kind of message is ignored if the version is too old
 	if not ver or ver < Amr.MIN_ADDON_VERSION then return end
 	
+	--[[
 	if Amr.StartsWith(message, Amr.MessageTypes.Team) then	
 		-- if fully initialized, process team optimizer messages
 		if Amr["ProcessTeamMessage"] then
@@ -595,6 +592,7 @@
 			self:ProcessPlayerSnapshot(message)
 		end
 	end
+	]]
 end
 
 
@@ -635,26 +633,60 @@
 ----------------------------------------------------------------------------------------
 -- Debugging
 ----------------------------------------------------------------------------------------
+function Amr:dump(o)
+	if type(o) == 'table' then
+	   local s = '{ '
+	   for k,v in pairs(o) do
+		  if type(k) ~= 'number' then k = '"'..k..'"' end
+		  s = s .. '['..k..'] = ' .. Amr:dump(v) .. ','
+	   end
+	   return s .. '} '
+	else
+	   return tostring(o)
+	end
+end
+
 function Amr:Test()
 
+	--local itemLink = GetInventoryItemLink("player", 17)
+
+	--print(itemLink)
+	
+	--local blah = Amr.ParseItemLink(itemLink)
+
+	--print(dump(blah.relicBonusIds))
+
+	--[[
+	--print(NUM_BANKBAGSLOTS)
+
+	local bagId = NUM_BAG_SLOTS + 1
+
+	local item = Item:CreateFromBagAndSlot(bagId, 2)
+	if item then
+		print(item:GetItemName())
+	else
+		print("no item")
+	end
+
+	local numSlots = GetContainerNumSlots(bagId)
+	print(numSlots .. " bag slots")
+	]]
+
+	-- EquipItemByName
+
+	--[[
+	for slotId = 1, numSlots do
+		local _, itemCount, _, _, _, _, itemLink = GetContainerItemInfo(bagId, slotId)
+		if itemLink ~= nil then
+			print(slotId .. " " .. itemLink)
+		end
+	end
+	]]
+
+	--[[
 	local s = "|cff0070dd|Hitem:127224:5337:0:0:0:0:0:0:100:105:512:22:2:615:656:100|h[Staff of Polarities]|h|r"
 	Amr.GetItemInfo(s, function(obj, name, link, quality, iLevel)
 		print(iLevel)
 	end)
+	]]
 end
-
---[[
-function Amr:Test(val1, val2, val3)
-
-	local link = GetLootSlotLink(tonumber(val1))
-	local index = Amr:TestLootIndex(link)
-	print("loot index: " .. index)
-	
-	if val2 then
-		local candidate = Amr:TestLootCandidate(link, val2, val3)
-		print("loot candidate: " .. candidate)
-		
-		GiveMasterLoot(index, candidate)
-	end
-end
-]]
--- a/Export.lua	Mon Feb 12 19:34:09 2018 -0800
+++ b/Export.lua	Tue Jul 17 09:57:39 2018 -0700
@@ -7,10 +7,10 @@
 
 local function createLabel(container, text, width)
 	local lbl = AceGUI:Create("AmrUiLabel")
+	container:AddChild(lbl)
 	lbl:SetWidth(width or 800)
 	lbl:SetText(text)
 	lbl:SetFont(Amr.CreateFont("Regular", 14, Amr.Colors.Text))
-	container:AddChild(lbl)
 	return lbl
 end
 
@@ -50,23 +50,15 @@
 	lbl2:SetFont(Amr.CreateFont("Regular", 14, Amr.Colors.Text))
 	lbl2:SetPoint("TOPLEFT", lbl.frame, "BOTTOMLEFT", 0, -15)
 	
-	lbl = createLabel(panel, L.ExportSplash3, 650)
-	lbl:SetFont(Amr.CreateFont("Regular", 14, Amr.Colors.Text))
-	lbl:SetPoint("TOPLEFT", lbl2.frame, "BOTTOMLEFT", 0, -15)
-
-	lbl2 = createLabel(panel, L.ExportSplash4, 650)
-	lbl2:SetFont(Amr.CreateFont("Regular", 14, Amr.Colors.Text))
-	lbl2:SetPoint("TOPLEFT", lbl.frame, "BOTTOMLEFT", 0, -15)
-	
 	local btn = AceGUI:Create("AmrUiButton")
 	btn:SetText(L.ExportSplashClose)
 	btn:SetBackgroundColor(Amr.Colors.Green)
 	btn:SetFont(Amr.CreateFont("Bold", 16, Amr.Colors.White))
 	btn:SetWidth(120)
 	btn:SetHeight(28)
-	btn:SetPoint("BOTTOM", panel.content, "BOTTOM", 0, 20)
 	btn:SetCallback("OnClick", onSplashClose)
 	panel:AddChild(btn)
+	btn:SetPoint("BOTTOM", panel.content, "BOTTOM", 0, 20)
 end
 
 -- renders the main UI for the Export tab
@@ -88,10 +80,10 @@
 	_txt = AceGUI:Create("AmrUiTextarea")
 	_txt:SetWidth(800)
 	_txt:SetHeight(300)
-	_txt:SetPoint("TOP", lbl2.frame, "BOTTOM", 0, -20)
 	_txt:SetFont(Amr.CreateFont("Regular", 12, Amr.Colors.Text))
 	_txt:SetCallback("OnTextChanged", onTextChanged)
 	container:AddChild(_txt)
+	_txt:SetPoint("TOP", lbl2.frame, "BOTTOM", 0, -20)
 	
 	local data = self:ExportCharacter()	
 	local txt = Amr.Serializer:SerializePlayerData(data, true)
@@ -119,24 +111,32 @@
 -- use some local variables to deal with the fact that a user can close the bank before a scan completes
 local _lastBankBagId = nil
 local _lastBankSlotId = nil
+local _bankOpen = false
 
 local function scanBag(bagId, isBank, bagTable, bagItemsWithCount)
 	local numSlots = GetContainerNumSlots(bagId)
+	local loc = ItemLocation.CreateEmpty()
 	for slotId = 1, numSlots do
 		local _, itemCount, _, _, _, _, itemLink = GetContainerItemInfo(bagId, slotId)
 		if itemLink ~= nil then
 			local itemData = Amr.Serializer.ParseItemLink(itemLink)
 			if itemData ~= nil then
-			
-				-- only add equippable items to bag data
-				--if IsEquippableItem(itemLink) or Amr.SetTokenIds[itemData.id] then
-	                if isBank then
-                    	_lastBankBagId = bagId
-                    	_lastBankSlotId = slotId
-                	end
+
+				-- see if this is an azerite item and read azerite power ids
+				loc:SetBagAndSlot(bagId, slotId)
+				if C_AzeriteEmpoweredItem.IsAzeriteEmpoweredItem(loc) then
+					local powers = Amr.ReadAzeritePowers(loc)
+					if powers then
+						itemData.azerite = powers
+					end
+				end
+
+				if isBank then
+					_lastBankBagId = bagId
+					_lastBankSlotId = slotId
+				end
 										
-                	table.insert(bagTable, itemLink)
-                --end
+				table.insert(bagTable, itemData)
 				
 				-- all items and counts, used for e.g. shopping list and reagents, etc.
                 if bagItemsWithCount then
@@ -151,14 +151,12 @@
 	end
 end
 
--- get the player's current gear and save it, also returns the data from GetPlayerData for efficiency
-local function getEquipped()
-	local data = Amr.Serializer:GetPlayerData()
-	local spec = GetSpecialization()
+-- cache the currently equipped gear for this spec
+local function cacheEquipped()
+	local data = Amr.Serializer:GetEquipped()
 	
+	local spec = GetSpecialization()	
 	Amr.db.char.Equipped[spec] = data.Equipped[spec]
-	
-	return data
 end
 
 local function scanBags()
@@ -184,20 +182,52 @@
 	scanBag(BANK_CONTAINER, true, bankItems, itemsAndCounts)
 	scanBag(REAGENTBANK_CONTAINER, true, bankItems, itemsAndCounts)
 	for bagId = NUM_BAG_SLOTS + 1, NUM_BAG_SLOTS + NUM_BANKBAGSLOTS do
-		scanBag(bagId, true, bankItems, itemsAndCounts)
+		local bagItems = {}
+		local bagItemsAndCounts = {}
+		scanBag(bagId, true, bagItems, bagItemsAndCounts)
+
+		bankItems[bagId] = bagItems
+		itemsAndCounts[bagId] = bagItemsAndCounts
 	end
 	
 	-- see if the scan completed before the window closed, otherwise we don't overwrite with partial data
-	if _lastBankBagId ~= nil then
+	if _bankOpen and _lastBankBagId then
 		local itemLink = GetContainerItemLink(_lastBankBagId, _lastBankSlotId)
-		if itemLink ~= nil then --still open
+		if itemLink then --still open
             Amr.db.char.BankItems = bankItems
             Amr.db.char.BankItemsAndCounts = itemsAndCounts
 		end
 	end
-
 end
 
+local function onBankOpened()
+	_bankOpen = true
+	scanBank()
+end
+
+local function onBankClosed()
+	_bankOpen = false
+end
+
+-- if a bank bag is updated while the bank is open, re-scan that bag
+local function onBankUpdated(bagID)
+	if _bankOpen and (bagID == BANK_CONTAINER or bagID == REAGENTBANK_CONTAINER or (bagID >= NUM_BAG_SLOTS + 1 and bagID <= NUM_BAG_SLOTS + NUM_BANKBAGSLOTS)) then
+		local bagItems = {}
+		local bagItemsAndCounts = {}
+		scanBag(bagID, true, bagItems, bagItemsAndCounts)
+
+		-- see if the scan completed before the window closed, otherwise we don't overwrite with partial data
+		if _bankOpen and _lastBankBagId == bagID then
+			local itemLink = GetContainerItemLink(_lastBankBagId, _lastBankSlotId)
+			if itemLink then
+				Amr.db.char.BankItems[bagID] = bagItems
+				Amr.db.char.BankItemsAndCounts[bagID] = bagItemsAndCounts
+			end
+		end
+	end
+end
+
+--[[
 -- scan the player's void storage and save the contents, must be at void storage
 local function scanVoid()
 
@@ -222,25 +252,7 @@
 	end
 	
 end
-
-local function getRepStanding(factionId)
-    local name, description, standingId, _ = GetFactionInfoByID(factionId)
-    return standingId - 1; -- our rep enum correspond to what the armory returns, are 1 less than what the game returns
-end
-
-local function getReputations()
-    local reps = {}
-    
-    local repList = {1375,1376,1270,1269,1341,1337,1387,1388,1435}
-    for i, repId in pairs(repList) do
-        local standing = getRepStanding(repId)
-        if standing >= 0 then
-            reps[repId] = standing
-        end
-    end
-    
-    return reps
-end
+]]
 
 local function scanTalents()	
 	local specPos = GetSpecialization()	
@@ -269,279 +281,36 @@
 	Amr.db.char.Talents[specPos] = str
 end
 
-local function scanCrucible()
-	if not Amr.db or not Amr.db.char or not Amr.db.char.Artifacts or not C_ArtifactRelicForgeUI or not C_ArtifactRelicForgeUI.GetSocketedRelicTalents then return end
-
-	local equipped = {}
-	local preview = nil
-	
-	for i = 1,4 do
-		local talents = nil
-		if i == 4 then
-			talents = C_ArtifactRelicForgeUI.GetPreviewRelicTalents()
-			--talents = nil
-		else
-			talents = C_ArtifactRelicForgeUI.GetSocketedRelicTalents(i)
-			
-			--[[
-			-- test data
-			if i == 1 then
-				talents = {}
-				table.insert(talents, {
-					powerID = 1739,
-					isChosen = true				
-				})
-				table.insert(talents, {
-					powerID = 1781,
-					isChosen = true				
-				})
-				table.insert(talents, {
-					powerID = 1770,
-					isChosen = false				
-				})
-				table.insert(talents, {
-					powerID = 791,
-					isChosen = false
-				})
-				table.insert(talents, {
-					powerID = 786,
-					isChosen = false				
-				})
-				table.insert(talents, {
-					powerID = 1537,
-					isChosen = false				
-				})
-			end
-			]]
-		end
-		
-		if talents then
-			local obj = {
-				Powers = {},
-				Active = {}
-			}
-			
-			if i == 4 then
-				obj.ItemLink = C_ArtifactRelicForgeUI.GetPreviewRelicItemLink()
-				if not obj.ItemLink then
-					talents = nil
-				else
-					preview = obj
-				end
-			else
-				table.insert(equipped, obj)
-			end
-			
-			if talents then
-				for k,v in ipairs(talents) do
-					table.insert(obj.Powers, v.powerID)
-					table.insert(obj.Active, v.isChosen)
-				end
-			end
-			
-		elseif i ~= 4 then
-			table.insert(equipped, {})
-		end
-	end
-		
-	
-	local itemID = C_ArtifactUI.GetArtifactInfo()	
-	local spec = Amr.ArtifactIdToSpecNumber[itemID]
-	
-	if spec then
-	
-		-- sometimes this event can fire when no crucible data is available, don't overwrite non-blank crucible data with blank crucible data
-		local badEquipped = false
-		if Amr.db.char.Artifacts[spec] then
-			local oldCrucible = Amr.db.char.Artifacts[spec].Crucible
-			if oldCrucible then
-				if #oldCrucible.Equipped > 0 and oldCrucible.Equipped[1] and not equipped[1] then
-					badEquipped = true
-				end
-			end
-		end
-		
-		local dataz = Amr.db.char.Artifacts[spec]
-		if not dataz then
-			dataz = {}
-			Amr.db.char.Artifacts[spec] = dataz
-		end
-		
-		if not dataz.Crucible then
-			dataz.Crucible = {
-				Equipped = {},
-				Previewed = {}
-			}
-		end		
-		
-		local crucible = dataz.Crucible		
-		
-		if not badEquipped then
-			crucible.Equipped = equipped
-		end
-		
-		if preview then
-			local previewKey = {}
-			table.insert(previewKey, preview.ItemLink)
-			for i,v in ipairs(preview.Powers) do
-				table.insert(previewKey, v .. "=" .. tostring(preview.Active[i]))
-			end			
-			previewKey = table.concat(previewKey, "_")
-			
-			if not crucible.Previewed then
-				crucible.Previewed = {}
-			end
-			crucible.Previewed[previewKey] = preview
-		end
-	end
-end
-
-local function pruneCrucible()
-	if not Amr.db or not Amr.db.char or not Amr.db.char.Artifacts then return end
-	
-	local spec = GetSpecialization()
-	local dataz = Amr.db.char.Artifacts[spec]
-	if not dataz or not dataz.Crucible then return end
-	
-	local crucible = dataz.Crucible
-	
-	-- this was old format, transform to new format
-	if crucible.Inventory then
-		if not crucible.Previewed then
-			crucible.Previewed = {}
-		end
-		
-		for link,preview in pairs(crucible.Inventory) do
-			local previewKey = {}
-			table.insert(previewKey, preview.ItemLink)
-			for i,v in ipairs(preview.Powers) do
-				table.insert(previewKey, v .. "=" .. tostring(preview.Active[i]))
-			end			
-			previewKey = table.concat(previewKey, "_")
-			
-			crucible.Previewed[previewKey] = preview
-		end
-		
-		crucible.Inventory = nil
-	end
-	
-	-- get a hash of every owned, but not-equipped item
-	local ownedItems = {}
-	if Amr.db.char.BagItems then
-		for i,link in ipairs(Amr.db.char.BagItems) do
-			ownedItems[link] = true
-		end
-	end
-	if Amr.db.char.BankItems then
-		for i,link in ipairs(Amr.db.char.BankItems) do
-			ownedItems[link] = true
-		end
-	end
-	if Amr.db.char.VoidItems then
-		for i,link in ipairs(Amr.db.char.VoidItems) do
-			ownedItems[link] = true
-		end
-	end
-	
-	-- prune out any previewed relics that the player no longer owns
-	if crucible.Previewed then
-		local toRemove = {}
-		for k,v in pairs(crucible.Previewed) do
-			if not ownedItems[v.ItemLink] then
-				table.insert(toRemove, k)
-			end
-		end
-		for i,v in ipairs(toRemove) do
-			crucible.Previewed[v] = nil
-		end
-	end
-	
-end
-
-local function scanArtifact()
-	if not Amr.db or not Amr.db.char or not Amr.db.char.Artifacts then return end
-	
-	local powers = C_ArtifactUI.GetPowers()
-	if not powers then return end
-	
-	local powerRanks = {}
-	for k,v in pairs(powers) do
-		local powerInfo = C_ArtifactUI.GetPowerInfo(v)
-		if powerInfo.currentRank - powerInfo.bonusRanks > 0 then
-			powerRanks[v] = powerInfo.currentRank - powerInfo.bonusRanks
-		end
-	end
-	
-	local relicInfo = {}
-	for i = 1,3 do
-		local _, _, _, link = C_ArtifactUI.GetRelicInfo(i);
-		table.insert(relicInfo, link or "")
-	end
-	
-	-- make sure that the artifact UI didn't get closed while we were reading it, GetPowers seems to return nil unless it is open
-	powers = C_ArtifactUI.GetPowers()
-	if not powers then return end
-	
-	-- use the artifact item ID to figure out which spec this is for, since you can open your artifact on any spec
-	local itemID = C_ArtifactUI.GetArtifactInfo()	
-	local spec = Amr.ArtifactIdToSpecNumber[itemID]	
-	--local spec = GetSpecialization()
-	
-	if spec then
-	
-		-- sometimes this event can fire when no relic data is available, don't overwrite non-blank relic data with blank relic data
-		if Amr.db.char.Artifacts[spec] then
-			local oldRelics = Amr.db.char.Artifacts[spec].Relics
-			if oldRelics then
-				for i = 1,3 do
-					if oldRelics[i] and oldRelics[i] ~= "" and (not relicInfo[i] or relicInfo[i] == "") then
-						relicInfo[i] = oldRelics[i]
-					end
-				end
-			end
-		end
-		
-		local dataz = Amr.db.char.Artifacts[spec]
-		if not dataz then
-			dataz = {}
-			Amr.db.char.Artifacts[spec] = dataz
-		end
-
-		if not dataz.Crucible then
-			dataz.Crucible = {
-				Equipped = {},
-				Inventory = {}
-			}
-		end
-		
-		dataz.Powers = powerRanks
-		dataz.Relics = relicInfo	
-		
-	end
-	
-	--scanCrucible()
-end
-
 -- Returns a data object containing all information about the current player needed for an export:
 -- gear, spec, reputations, bag, bank, and void storage items.
 function Amr:ExportCharacter()
 	
-	local data = getEquipped()
+	-- get all necessary player data
+	local data = Amr.Serializer:GetPlayerData()
+
+	-- cache latest-seen equipped gear for current spec
+	local spec = GetSpecialization()	
+	Amr.db.char.Equipped[spec] = data.Equipped[spec]
+
+	-- scan current inventory just before export so that it is always fresh
 	scanBags()
 	
 	-- scan current spec's talents just before exporting
 	scanTalents()
 	
-	-- prune crucible info just before each time we export
-	pruneCrucible()
-	
-	data.Talents = Amr.db.char.Talents
-	data.Artifacts = Amr.db.char.Artifacts
-	data.Equipped = Amr.db.char.Equipped
-	data.Reputations = getReputations()
+	data.Talents = Amr.db.char.Talents	
+	data.Equipped = Amr.db.char.Equipped	
 	data.BagItems = Amr.db.char.BagItems
-	data.BankItems = Amr.db.char.BankItems
-	data.VoidItems = Amr.db.char.VoidItems
+
+	-- flatten bank data (which is stored by bag for more efficient updating)
+	data.BankItems = {}
+	for k,v in pairs(Amr.db.char.BankItems) do
+		for i,v2 in ipairs(v) do
+			table.insert(data.BankItems, v2)
+		end
+	end
+
+	--data.VoidItems = Amr.db.char.VoidItems
 	
 	return data
 end
@@ -549,19 +318,17 @@
 function Amr:InitializeExport()
 	Amr:AddEventHandler("UNIT_INVENTORY_CHANGED", function(unitID)
 		if unitID and unitID ~= "player" then return end
-		getEquipped()
+		cacheEquipped()
 	end)
 end
 
-Amr:AddEventHandler("BANKFRAME_OPENED", scanBank)
-Amr:AddEventHandler("PLAYERBANKSLOTS_CHANGED", scanBank)
+Amr:AddEventHandler("BANKFRAME_OPENED", onBankOpened)
+Amr:AddEventHandler("BANKFRAME_CLOSED", onBankClosed)
+Amr:AddEventHandler("BAG_UPDATE", onBankUpdated)
 
-Amr:AddEventHandler("VOID_STORAGE_OPEN", scanVoid)
-Amr:AddEventHandler("VOID_STORAGE_CONTENTS_UPDATE", scanVoid)
-Amr:AddEventHandler("VOID_STORAGE_DEPOSIT_UPDATE", scanVoid)
-Amr:AddEventHandler("VOID_STORAGE_UPDATE", scanVoid)
+--Amr:AddEventHandler("VOID_STORAGE_OPEN", scanVoid)
+--Amr:AddEventHandler("VOID_STORAGE_CONTENTS_UPDATE", scanVoid)
+--Amr:AddEventHandler("VOID_STORAGE_DEPOSIT_UPDATE", scanVoid)
+--Amr:AddEventHandler("VOID_STORAGE_UPDATE", scanVoid)
 
 Amr:AddEventHandler("PLAYER_TALENT_UPDATE", scanTalents)
-Amr:AddEventHandler("ARTIFACT_UPDATE", scanArtifact)
-Amr:AddEventHandler("ARTIFACT_RELIC_FORGE_UPDATE", scanCrucible)
-Amr:AddEventHandler("ARTIFACT_RELIC_FORGE_PREVIEW_RELIC_CHANGED", scanCrucible)
--- a/Gear.lua	Mon Feb 12 19:34:09 2018 -0800
+++ b/Gear.lua	Tue Jul 17 09:57:39 2018 -0700
@@ -7,9 +7,17 @@
 
 -- Returns a number indicating how different two items are (0 means the same, higher means more different)
 local function countItemDifferences(item1, item2)
-    if item1 == nil and item2 == nil then return 0 end
-    
-    -- different items (id + bonus ids + suffix, constitutes a different physical drop)
+	-- both nil, the same
+	if not item1 and not item2 then 
+		return 0 
+	end 
+	
+	-- one nil and other not, or different id, totally different
+	if (not item1 and item2) or (item1 and not item2) or item1.id ~= item2.id then 
+		return 10000 
+	end
+	
+    -- different versions of same item (id + bonus ids + suffix + drop level, constitutes a different physical drop)
     if Amr.GetItemUniqueId(item1, true) ~= Amr.GetItemUniqueId(item2, true) then
 		return 1000
     end
@@ -17,7 +25,42 @@
     -- different upgrade levels of the same item
     if item1.upgradeId ~= item2.upgradeId then
         return 100
-    end
+	end
+	
+	-- different azerite powers
+	local aztDiffs = 0
+	if item1.azerite or item2.azerite then
+		if item1.azerite and not item2.azerite then
+			aztDiffs = #item1.azerite * 10
+		elseif item2.azerite and not item1.azerite then
+			aztDiffs = #item2.azerite * 10
+		else
+			-- count up number in item1 but missing from item2
+			for i = 1, #item1.azerite do
+				local missing = false
+				for j = 1, #item2.azerite do
+					if item2[j] == item1[i] then
+						missing = false
+					end
+				end
+				if missing then
+					aztDiffs = aztDiffs + 10
+				end
+			end
+			-- count up number in item2 but missing from item1
+			for i = 1, #item2.azerite do
+				local missing = false
+				for j = 1, #item1.azerite do
+					if item1[j] == item2[i] then
+						missing = false
+					end
+				end
+				if missing then
+					aztDiffs = aztDiffs + 10
+				end
+			end
+		end
+	end
     
     -- different gems
     local gemDiffs = 0
@@ -33,26 +76,24 @@
         enchantDiff = 1
     end
     
-    return gemDiffs + enchantDiff
+    return aztDiffs + gemDiffs + enchantDiff
 end
 
 -- given a table of items (keyed or indexed doesn't matter) find closest match to item, or nil if none are a match
-local function findMatchingItemFromTable(item, list, bestLink, bestItem, bestDiff, bestLoc, usedItems, tableType)
+local function findMatchingItemFromTable(item, list, bestItem, bestDiff, bestLoc, usedItems, tableType)
 	if not list then return nil end
 	
 	local found = false
-	for k,v in pairs(list) do
-		local listItem = Amr.ParseItemLink(v)
+	for k,listItem in pairs(list) do
 		if listItem then
 			local diff = countItemDifferences(item, listItem)
 			if diff < bestDiff then
 				-- each physical item can only be used once, the usedItems table has items we can't use in this search
 				local key = string.format("%s_%s", tableType, k)
 				if not usedItems[key] then
-					bestLink = v
 					bestItem = listItem
 					bestDiff = diff
-					bestLoc = string.format("%s_%s", tableType, k)
+					bestLoc = key
 					found = true
 				end
 			end
@@ -60,24 +101,27 @@
 		end
 	end
 	
-	return bestLink, bestItem, bestDiff, bestLoc
+	return bestItem, bestDiff, bestLoc
 end
 
--- search the player's equipped gear, bag, bank, and void storage for an item that best matches the specified item
+-- search the player's equipped gear, bag, and bank for an item that best matches the specified item
 function Amr:FindMatchingItem(item, player, usedItems)
 	if not item then return nil end
 
 	local equipped = player.Equipped and player.Equipped[player.ActiveSpec] or nil
-	local bestLink, bestItem, bestDiff, bestLoc = findMatchingItemFromTable(item, equipped, nil, nil, 1000, nil, usedItems, "equip")
-	bestLink, bestItem, bestDiff, bestLoc = findMatchingItemFromTable(item, player.BagItems, bestLink, bestItem, bestDiff, bestLoc, usedItems, "bag")
-	bestLink, bestItem, bestDiff, bestLoc = findMatchingItemFromTable(item, player.BankItems, bestLink, bestItem, bestDiff, bestLoc, usedItems, "bank")
-	bestLink, bestItem, bestDiff, bestLoc = findMatchingItemFromTable(item, player.VoidItems, bestLink, bestItem, bestDiff, bestLoc, usedItems, "void")
+	local bestItem, bestDiff, bestLoc = findMatchingItemFromTable(item, equipped, nil, 10000, nil, usedItems, "equip")
+	bestItem, bestDiff, bestLoc = findMatchingItemFromTable(item, player.BagItems, bestItem, bestDiff, bestLoc, usedItems, "bag")
+	if player.BankItems then
+		for bagId,bagList in pairs(player.BankItems) do
+			bestItem, bestDiff, bestLoc = findMatchingItemFromTable(item, bagList, bestItem, bestDiff, bestLoc, usedItems, "bank" .. bagId)
+		end
+	end	
 
-	if bestDiff >= 1000 then
-		return nil, nil, 1000
+	if bestDiff >= 10000 then
+		return nil, 10000
 	else
 		usedItems[bestLoc] = true
-		return bestLink, bestItem, bestDiff
+		return bestItem, bestDiff
 	end
 end
 
@@ -86,25 +130,64 @@
 	local panelBlank = AceGUI:Create("AmrUiPanel")
 	panelBlank:SetLayout("None")
 	panelBlank:SetBackgroundColor(Amr.Colors.Black, 0.4)
+	container:AddChild(panelBlank)
 	panelBlank:SetPoint("TOPLEFT", container.content, "TOPLEFT", 6, 0)
 	panelBlank:SetPoint("BOTTOMRIGHT", container.content, "BOTTOMRIGHT")
-	container:AddChild(panelBlank)
 	
 	local lbl = AceGUI:Create("AmrUiLabel")
+	panelBlank:AddChild(lbl)
 	lbl:SetText(L.GearBlank)
 	lbl:SetWidth(700)
 	lbl:SetJustifyH("MIDDLE")
 	lbl:SetFont(Amr.CreateFont("Italic", 16, Amr.Colors.TextTan))		
 	lbl:SetPoint("BOTTOM", panelBlank.content, "CENTER", 0, 20)
-	panelBlank:AddChild(lbl)
 	
 	local lbl2 = AceGUI:Create("AmrUiLabel")
+	panelBlank:AddChild(lbl2)
 	lbl2:SetText(L.GearBlank2)
 	lbl2:SetWidth(700)
 	lbl2:SetJustifyH("MIDDLE")
 	lbl2:SetFont(Amr.CreateFont("Italic", 16, Amr.Colors.TextTan))		
 	lbl2:SetPoint("TOP", lbl.frame, "CENTER", 0, -20)
-	panelBlank:AddChild(lbl2)
+end
+
+-- helper to create a widget for showing a socket or azerite power
+local function createSocketWidget(panelMods, prevWidget, prevIsSocket, isEquipped)
+
+	-- highlight for socket that doesn't match
+	local socketBorder = AceGUI:Create("AmrUiPanel")
+	panelMods:AddChild(socketBorder)
+	if not prevIsSocket then
+		socketBorder:SetPoint("LEFT", prevWidget.frame, "RIGHT", 30, 0)
+	else
+		socketBorder:SetPoint("LEFT", prevWidget.frame, "RIGHT", 2, 0)
+	end
+	socketBorder:SetLayout("None")
+	socketBorder:SetBackgroundColor(Amr.Colors.Black, isEquipped and 0 or 1)
+	socketBorder:SetWidth(26)
+	socketBorder:SetHeight(26)
+	if isEquipped then
+		socketBorder:SetAlpha(0.3)
+	end					
+
+	local socketBg = AceGUI:Create("AmrUiIcon")
+	socketBorder:AddChild(socketBg)
+	socketBg:SetPoint("TOPLEFT", socketBorder.content, "TOPLEFT", 1, -1)
+	socketBg:SetLayout("None")
+	socketBg:SetBorderWidth(2)
+	socketBg:SetIconBorderColor(Amr.Colors.Green, isEquipped and 0 or 1)
+	socketBg:SetWidth(24)
+	socketBg:SetHeight(24)
+
+	local socketIcon = AceGUI:Create("AmrUiIcon")
+	socketBg:AddChild(socketIcon)
+	socketIcon:SetPoint("CENTER", socketBg.content, "CENTER")
+	socketIcon:SetBorderWidth(1)
+	socketIcon:SetIconBorderColor(Amr.Colors.White)
+	socketIcon:SetWidth(18)
+	socketIcon:SetHeight(18)
+	
+	return socketBorder, socketIcon
 end
 
 local function renderGear(spec, container)
@@ -120,16 +203,16 @@
 		local panelGear = AceGUI:Create("AmrUiPanel")
 		panelGear:SetLayout("None")
 		panelGear:SetBackgroundColor(Amr.Colors.Black, 0.3)
+		container:AddChild(panelGear)
 		panelGear:SetPoint("TOPLEFT", container.content, "TOPLEFT", 6, 0)
 		panelGear:SetPoint("BOTTOMRIGHT", container.content, "BOTTOMRIGHT", -300, 0)
-		container:AddChild(panelGear)
 		
 		local panelMods = AceGUI:Create("AmrUiPanel")
 		panelMods:SetLayout("None")
+		panelMods:SetBackgroundColor(Amr.Colors.Black, 0.3)
+		container:AddChild(panelMods)
 		panelMods:SetPoint("TOPLEFT", panelGear.frame, "TOPRIGHT", 15, 0)
 		panelMods:SetPoint("BOTTOMRIGHT", container.content, "BOTTOMRIGHT")
-		panelMods:SetBackgroundColor(Amr.Colors.Black, 0.3)
-		container:AddChild(panelMods)
 		
 		-- spec icon
 		local icon = AceGUI:Create("AmrUiIcon")	
@@ -145,8 +228,8 @@
 		end
 
 		icon:SetIcon("Interface\\Icons\\" .. Amr.SpecIcons[iconSpec])
+		panelGear:AddChild(icon)
 		icon:SetPoint("TOPLEFT", panelGear.content, "TOPLEFT", 10, -10)
-		panelGear:AddChild(icon)
 		
 		local btnEquip = AceGUI:Create("AmrUiButton")
 		btnEquip:SetText(L.GearButtonEquip(L.SpecsShort[player.Specs[spec]]))
@@ -154,12 +237,12 @@
 		btnEquip:SetFont(Amr.CreateFont("Regular", 14, Amr.Colors.White))
 		btnEquip:SetWidth(300)
 		btnEquip:SetHeight(26)
-		btnEquip:SetPoint("LEFT", icon.frame, "RIGHT", 40, 0)
-		btnEquip:SetPoint("RIGHT", panelGear.content, "RIGHT", -40, 0)
 		btnEquip:SetCallback("OnClick", function(widget)
 			Amr:EquipGearSet(spec)			
 		end)
 		panelGear:AddChild(btnEquip)
+		btnEquip:SetPoint("LEFT", icon.frame, "RIGHT", 40, 0)
+		btnEquip:SetPoint("RIGHT", panelGear.content, "RIGHT", -40, 0)
 		
 		-- each physical item can only be used once, this tracks ones we have already used
 		local usedItems = {}
@@ -169,8 +252,8 @@
 		for slotNum = 1, #Amr.SlotIds do
 			local slotId = Amr.SlotIds[slotNum]
 			
-			local equippedItemLink = equipped and equipped[slotId] or nil
-			local equippedItem = Amr.ParseItemLink(equippedItemLink)
+			local equippedItem = equipped and equipped[slotId] or nil
+			local equippedItemLink = equipped and equipped.link or nil
 			local optimalItem = gear[slotId]			
 			local optimalItemLink = Amr.CreateItemLink(optimalItem)
 			
@@ -179,41 +262,43 @@
 			if equippedItem and optimalItem and Amr.GetItemUniqueId(equippedItem) == Amr.GetItemUniqueId(optimalItem) then
 				isEquipped = true
 			end
+
+			local isAzerite = optimalItem and C_AzeriteEmpoweredItem.IsAzeriteEmpoweredItemByID(optimalItem.id)
 			
 			-- find the item in the player's inventory that best matches what the optimization wants to use
-			local matchItemLink, matchItem = Amr:FindMatchingItem(optimalItem, player, usedItems)
+			local matchItem = Amr:FindMatchingItem(optimalItem, player, usedItems)
 			
 			-- slot label
 			local lbl = AceGUI:Create("AmrUiLabel")
+			panelGear:AddChild(lbl)
+			lbl:SetPoint("TOPLEFT", prevElem.frame, "BOTTOMLEFT", 0, -12) 
 			lbl:SetText(Amr.SlotDisplayText[slotId])
 			lbl:SetWidth(85)
 			lbl:SetFont(Amr.CreateFont("Regular", 14, Amr.Colors.White))		
-			lbl:SetPoint("TOPLEFT", prevElem.frame, "BOTTOMLEFT", 0, -12) 
-			panelGear:AddChild(lbl)
 			prevElem = lbl
 			
 			-- ilvl label
 			local lblIlvl = AceGUI:Create("AmrUiLabel")
+			panelGear:AddChild(lblIlvl)
+			lblIlvl:SetPoint("TOPLEFT", lbl.frame, "TOPRIGHT", 0, 0) 
 			lblIlvl:SetWidth(45)
 			lblIlvl:SetFont(Amr.CreateFont("Italic", 14, Amr.Colors.TextTan))		
-			lblIlvl:SetPoint("TOPLEFT", lbl.frame, "TOPRIGHT", 0, 0) 
-			panelGear:AddChild(lblIlvl)
 			
 			-- equipped label
 			local lblEquipped = AceGUI:Create("AmrUiLabel")
+			panelGear:AddChild(lblEquipped)
+			lblEquipped:SetPoint("TOPLEFT", lblIlvl.frame, "TOPRIGHT", 0, 0) 
 			lblEquipped:SetWidth(20)
 			lblEquipped:SetFont(Amr.CreateFont("Regular", 14, Amr.Colors.White))
-			lblEquipped:SetPoint("TOPLEFT", lblIlvl.frame, "TOPRIGHT", 0, 0) 
 			lblEquipped:SetText(isEquipped and "E" or "")
-			panelGear:AddChild(lblEquipped)
 			
 			-- item name/link label
 			local lblItem = AceGUI:Create("AmrUiLabel")
+			panelGear:AddChild(lblItem)
+			lblItem:SetPoint("TOPLEFT", lblEquipped.frame, "TOPRIGHT", 0, 0) 
 			lblItem:SetWordWrap(false)
 			lblItem:SetWidth(345)
 			lblItem:SetFont(Amr.CreateFont(isEquipped and "Regular" or "Bold", isEquipped and 14 or 15, Amr.Colors.White))		
-			lblItem:SetPoint("TOPLEFT", lblEquipped.frame, "TOPRIGHT", 0, 0) 
-			panelGear:AddChild(lblItem)
 			
 			-- fill the name/ilvl labels, which may require asynchronous loading of item information
 			if optimalItemLink then
@@ -221,107 +306,105 @@
 					-- set item name, tooltip, and ilvl
 					obj.nameLabel:SetText(link:gsub("%[", ""):gsub("%]", ""))
 					
-					-- not quite right but whatever... close enough
 					if quality == 6 then
-						local tmprel = optimalItem.relicBonusIds
-						optimalItem.relicBonusIds = nil
-						link = Amr.CreateItemLink(optimalItem)
-						optimalItem.relicBonusIds = tmprel
+						-- not quite right but whatever... close enough, artifacts are a thing of the past
+						local tmprel = obj.optimalItem.relicBonusIds
+						obj.optimalItem.relicBonusIds = nil
+						link = Amr.CreateItemLink(obj.optimalItem)
+						obj.optimalItem.relicBonusIds = tmprel
+
+						-- for artifacts, we consider it equipped if the item id alone matches
+						if obj.equippedItem and obj.equippedItem.id == obj.optimalItem.id then
+							obj.isEquipped = true
+						end
+						obj.equipLabel:SetText(obj.isEquipped and "E" or "")
 					end
 					
-					Amr:SetItemTooltip(obj.nameLabel, link)
+					Amr:SetItemTooltip(obj.nameLabel, link, "ANCHOR_TOPRIGHT")
 					
-					-- the game's info gives the wrong item level, so we have to scan for it
-					--iLevel = (quality ~= 6 or optimalItem.relicBonusIds) and Amr.GetItemLevel(nil, nil, link) or ""
-					obj.ilvlLabel:SetText(iLevel)			
-					
-				end, { ilvlLabel = lblIlvl, nameLabel = lblItem })
+					local itemObj = Item:CreateFromItemLink(link)
+					if itemObj then
+						-- game's GetItemInfo method returns the wrong ilvl sometimes, so use the new item api to get it
+						iLevel = itemObj:GetCurrentItemLevel()
+					end
+					obj.ilvlLabel:SetText(iLevel)					
+
+				end, { 
+					ilvlLabel = lblIlvl, 
+					nameLabel = lblItem, 
+					equipLabel = lblEquipped,
+					optimalItem = optimalItem,
+					equippedItem = equippedItem,
+					isEquipped = isEquipped
+				})
 			end
 						
 			-- modifications
 			if optimalItem then
-				local itemInfo = Amr.db.char.ExtraItemData[spec][optimalItem.id]
 
-				-- gems
-				if itemInfo and itemInfo.socketColors then
-					local prevSocket = nil
-					for i = 1, #itemInfo.socketColors do
+				-- gems or azerite powers
+				local prevSocket = nil
+
+				if isAzerite then
+					local azt = optimalItem.azerite or {}
+					for i,spellId in ipairs(azt) do
+						if spellId and spellId ~= 0 then
+							local equippedAzt = equippedItem and equippedItem.azerite or {}
+							local isPowerActive = Amr.Contains(equippedAzt, spellId)
+
+							local socketBorder, socketIcon = createSocketWidget(panelMods, prevSocket or lblItem, prevSocket, isPowerActive)
+							
+							-- set icon and tooltip
+							local spellName, _, spellIcon = GetSpellInfo(spellId)
+							socketIcon:SetIcon(spellIcon)
+							Amr:SetSpellTooltip(socketIcon, spellId, "ANCHOR_TOPRIGHT")
+							
+							prevSocket = socketBorder
+						end
+					end
+				else
+					for i = 1, #optimalItem.gemIds do
+						-- we rely on the fact that the gear sets coming back from the site will almost always have all sockets filled,
+						-- because it's a pain to get the actual number of sockets on an item from within the game
 						local g = optimalItem.gemIds[i]
-						local isGemEquipped = g ~= 0 and matchItem and matchItem.gemIds and matchItem.gemIds[i] == g
+						if g == 0 then break end
+
+						local isGemEquipped = matchItem and matchItem.gemIds and matchItem.gemIds[i] == g
 						
-						-- highlight for gem that doesn't match
-						local socketBorder = AceGUI:Create("AmrUiPanel")
-						socketBorder:SetLayout("None")
-						socketBorder:SetBackgroundColor(Amr.Colors.Black, isGemEquipped and 0 or 1)
-						socketBorder:SetWidth(26)
-						socketBorder:SetHeight(26)
-						if not prevSocket then
-							socketBorder:SetPoint("LEFT", lblItem.frame, "RIGHT", 30, 0)
-						else
-							socketBorder:SetPoint("LEFT", prevSocket.frame, "RIGHT", 2, 0)
-						end
-						if isGemEquipped then
-							socketBorder:SetAlpha(0.3)
-						end
-						panelMods:AddChild(socketBorder)
-						
-						local socketBg = AceGUI:Create("AmrUiIcon")
-						socketBg:SetLayout("None")
-						socketBg:SetBorderWidth(2)
-						socketBg:SetIconBorderColor(Amr.Colors.Green, isGemEquipped and 0 or 1)
-						socketBg:SetWidth(24)
-						socketBg:SetHeight(24)
-						socketBg:SetPoint("TOPLEFT", socketBorder.content, "TOPLEFT", 1, -1)
-						socketBorder:AddChild(socketBg)
-						
-						local socketIcon = AceGUI:Create("AmrUiIcon")
-						socketIcon:SetBorderWidth(1)
-						socketIcon:SetIconBorderColor(Amr.Colors.White)
-						socketIcon:SetWidth(18)
-						socketIcon:SetHeight(18)
-						socketIcon:SetPoint("CENTER", socketBg.content, "CENTER")
-						socketBg:AddChild(socketIcon)
+						local socketBorder, socketIcon = createSocketWidget(panelMods, prevSocket or lblItem, prevSocket, isGemEquipped)
 						
 						-- get icon for optimized gem
-						if g ~= 0 then
-							local gemInfo = Amr.db.char.ExtraGemData[spec][g]
-							if gemInfo then
-								local gident = gemInfo.id
-								if optimalItem.relicBonusIds then
-									gident = Amr.CreateItemLink({ id = gemInfo.id, enchantId = 0, gemIds = {0,0,0,0}, suffixId = 0, bonusIds = optimalItem.relicBonusIds[i]})
-								end
-								Amr.GetItemInfo(gident, function(obj, name, link, quality, iLevel, reqLevel, class, subclass, maxStack, equipSlot, texture)					
-									-- set icon and a tooltip
-									obj:SetIcon(texture)
-									Amr:SetItemTooltip(obj, link)
-								end, socketIcon)
-							end
-						end
+						Amr.GetItemInfo(g, function(obj, name, link, quality, iLevel, reqLevel, class, subclass, maxStack, equipSlot, texture)					
+							-- set icon and a tooltip
+							obj:SetIcon(texture)
+							Amr:SetItemTooltip(obj, link, "ANCHOR_TOPRIGHT")
+						end, socketIcon)
 						
 						prevSocket = socketBorder
 					end
 				end
-				
+
 				-- enchant
 				if optimalItem.enchantId and optimalItem.enchantId ~= 0 then
 					local isEnchantEquipped = matchItem and matchItem.enchantId and matchItem.enchantId == optimalItem.enchantId
 					
 					local lblEnchant = AceGUI:Create("AmrUiLabel")
+					panelMods:AddChild(lblEnchant)
+					lblEnchant:SetPoint("TOPLEFT", lblItem.frame, "TOPRIGHT", 130, 0)
 					lblEnchant:SetWordWrap(false)
 					lblEnchant:SetWidth(170)
 					lblEnchant:SetFont(Amr.CreateFont(isEnchantEquipped and "Regular" or "Bold", 14, isEnchantEquipped and Amr.Colors.TextGray or Amr.Colors.White))
-					lblEnchant:SetPoint("TOPLEFT", lblItem.frame, "TOPRIGHT", 130, 0)
 					
-					local enchInfo = Amr.db.char.ExtraEnchantData[spec][optimalItem.enchantId]
+					local enchInfo = Amr.db.char.ExtraEnchantData[optimalItem.enchantId]
 					if enchInfo then
 						lblEnchant:SetText(enchInfo.text)
 						
 						Amr.GetItemInfo(enchInfo.itemId, function(obj, name, link)					
-							Amr:SetItemTooltip(obj, link)
+							Amr:SetItemTooltip(obj, link, "ANCHOR_TOPRIGHT")
 						end, lblEnchant)						
 						--Amr:SetSpellTooltip(lblEnchant, enchInfo.spellId)
 					end
-					panelMods:AddChild(lblEnchant)
+					
 				end
 			end
 			
@@ -349,40 +432,40 @@
 	btnImport:SetFont(Amr.CreateFont("Bold", 16, Amr.Colors.White))
 	btnImport:SetWidth(120)
 	btnImport:SetHeight(26)
-	btnImport:SetPoint("TOPLEFT", container.content, "TOPLEFT", 0, -81)
 	btnImport:SetCallback("OnClick", onImportClick)
 	container:AddChild(btnImport)	
+	btnImport:SetPoint("TOPLEFT", container.content, "TOPLEFT", 0, -81)
 	
 	local lbl = AceGUI:Create("AmrUiLabel")
+	container:AddChild(lbl)
 	lbl:SetText(L.GearImportNote)
 	lbl:SetWidth(100)
 	lbl:SetFont(Amr.CreateFont("Italic", 12, Amr.Colors.TextTan))
 	lbl:SetJustifyH("MIDDLE")
 	lbl:SetPoint("TOP", btnImport.frame, "BOTTOM", 0, -5)
-	container:AddChild(lbl)
 	
 	local lbl2 = AceGUI:Create("AmrUiLabel")
+	container:AddChild(lbl2)
 	lbl2:SetText(L.GearTipTitle)
 	lbl2:SetWidth(140)
 	lbl2:SetFont(Amr.CreateFont("Italic", 20, Amr.Colors.Text))
 	lbl2:SetJustifyH("MIDDLE")
 	lbl2:SetPoint("TOP", lbl.frame, "BOTTOM", 0, -50)
-	container:AddChild(lbl2)
 	
 	lbl = AceGUI:Create("AmrUiLabel")
+	container:AddChild(lbl)
 	lbl:SetText(L.GearTipText)
 	lbl:SetWidth(140)
 	lbl:SetFont(Amr.CreateFont("Italic", 12, Amr.Colors.Text))
 	lbl:SetJustifyH("MIDDLE")
 	lbl:SetPoint("TOP", lbl2.frame, "BOTTOM", 0, -5)
-	container:AddChild(lbl)
 	
 	lbl2 = AceGUI:Create("AmrUiLabel")
+	container:AddChild(lbl2)
 	lbl2:SetText(L.GearTipCommands)
 	lbl2:SetWidth(130)
 	lbl2:SetFont(Amr.CreateFont("Italic", 12, Amr.Colors.Text))
 	lbl2:SetPoint("TOP", lbl.frame, "BOTTOM", 10, -5)
-	container:AddChild(lbl2)
 	
 	local t =  AceGUI:Create("AmrUiTabGroup")
 	t:SetLayout("None")
@@ -397,9 +480,9 @@
 	
 	t:SetTabs(tabz)
 	t:SetCallback("OnGroupSelected", onGearTabSelected)
+	container:AddChild(t)	
 	t:SetPoint("TOPLEFT", container.content, "TOPLEFT", 144, -30)
 	t:SetPoint("BOTTOMRIGHT", container.content, "BOTTOMRIGHT")
-	container:AddChild(t)	
 	_gearTabs = t;
 	
 	local btnShop = AceGUI:Create("AmrUiButton")
@@ -408,9 +491,9 @@
 	btnShop:SetFont(Amr.CreateFont("Regular", 14, Amr.Colors.White))
 	btnShop:SetWidth(245)
 	btnShop:SetHeight(26)
-	btnShop:SetPoint("TOPRIGHT", container.content, "TOPRIGHT", -20, -25)
 	btnShop:SetCallback("OnClick", function(widget) Amr:ShowShopWindow() end)
 	container:AddChild(btnShop)
+	btnShop:SetPoint("TOPRIGHT", container.content, "TOPRIGHT", -20, -25)
 	
 	if not _activeTab then
 		_activeTab = tostring(GetSpecialization())
@@ -443,56 +526,13 @@
 -- Gear Set Management
 ------------------------------------------------------------------------------------------------
 local _waitingForSpec = 0
-local _waitingForItemLock = nil
-local _pendingEquip = nil
-local _pendingRemove = nil
+local _pendingGearOps = nil
+local _currentGearOp = nil
+local _itemLockAction = nil
+local _gearOpPasses = 0
+local _gearOpWaiting = nil
 
--- scan a bag for the best matching item
-local function scanBagForItem(item, bagId, bestItem, bestDiff, bestLink)
-	local numSlots = GetContainerNumSlots(bagId)
-	for slotId = 1, numSlots do
-		local _, _, _, _, _, _, itemLink = GetContainerItemInfo(bagId, slotId)
-        -- we skip any stackable item, as far as we know, there is no equippable gear that can be stacked
-		if itemLink then
-			local bagItem = Amr.ParseItemLink(itemLink)
-			if bagItem ~= nil then
-				local diff = countItemDifferences(item, bagItem)
-				if diff < bestDiff then
-					bestItem = { bag = bagId, slot = slotId }
-					bestDiff = diff
-					bestLink = itemLink
-				end
-            end
-		end
-	end
-	return bestItem, bestDiff, bestLink
-end
-
-local function onEquipGearSetComplete()
-	if Amr.db.profile.options.disableEm then return end
-	
-	-- create an equipment manager set
-	local specId, specName = GetSpecializationInfo(GetSpecialization())
-	
-	local item = Amr.ParseItemLink(GetInventoryItemLink("player", INVSLOT_MAINHAND))
-	if not item or not Amr.ArtifactIdToSpecNumber[item.id] then
-		item = Amr.ParseItemLink(GetInventoryItemLink("player", INVSLOT_OFFHAND))
-		if item and not Amr.ArtifactIdToSpecNumber[item.id] then
-			item = nil
-		end
-	end
-	if item then
-		Amr.GetItemInfo(item.id, function(customArg, name, link, quality, iLevel, reqLevel, class, subclass, maxStack, equipSlot, texture)
-			local setname = "AMR " .. specName
-			local setid = C_EquipmentSet.GetEquipmentSetID(setname)
-			if setid then
-				C_EquipmentSet.SaveEquipmentSet(setid, texture)
-			else
-				C_EquipmentSet.CreateEquipmentSet(setname, texture)
-			end
-		end)
-	end
-end
+local beginEquipGearSet, processCurrentGearOp, nextGearOp
 
 -- find the first empty slot in the player's backpack+bags
 local function findFirstEmptyBagSlot()
@@ -516,30 +556,35 @@
 	return nil, nil
 end
 
-local function finishEquipGearSet()
-	if not _pendingEquip then return end
-	
-	_pendingEquip.tries = _pendingEquip.tries + 1
-	if _pendingEquip.tries > 16 then
-		-- too many tries, just give up (shouldn't happen but just to be safe)
-		_pendingEquip = nil
-	else
-		-- start over again, trying any items that could not be equipped in the previous pass (unique constraints)
-		Amr:EquipGearSet(_pendingEquip.spec)
+-- scan a bag for the best matching item
+local function scanBagForItem(item, bagId, bestItem, bestDiff, bestLink)
+	local numSlots = GetContainerNumSlots(bagId)
+	for slotId = 1, numSlots do
+		local _, _, _, _, _, _, itemLink = GetContainerItemInfo(bagId, slotId)
+        -- we skip any stackable item, as far as we know, there is no equippable gear that can be stacked
+		if itemLink then
+			local bagItem = Amr.ParseItemLink(itemLink)
+			if bagItem ~= nil then
+				local diff = countItemDifferences(item, bagItem)
+				if diff < bestDiff then
+					bestItem = { bag = bagId, slot = slotId }
+					bestDiff = diff
+					bestLink = itemLink
+				end
+            end
+		end
 	end
+	return bestItem, bestDiff, bestLink
 end
 
--- equip the next slot in a pending equip
-local function tryEquipNextItem()
-	if not _pendingEquip then return end
-	
-	local item = _pendingEquip.itemsToEquip[_pendingEquip.nextSlot]
-	
+-- find the item in the player's inventory that best matches the current gear op item, favoring stuff already equipped, then in bags, then in bank
+local function findCurrentGearOpItem()
+
+	local item = _currentGearOp.items[_currentGearOp.nextSlot]
+
 	local bestItem = nil
 	local bestLink = nil
-	local bestDiff = 1000
-	
-	-- find the best matching item
+	local bestDiff = 10000
 	
 	-- inventory
 	bestItem, bestDiff, bestLink = scanBagForItem(item, BACKPACK_CONTAINER, bestItem, bestDiff, bestLink)
@@ -550,11 +595,11 @@
 	-- equipped items, but skip slots we have just equipped (to avoid e.g. just moving 2 of the same item back and forth between mh oh weapon slots)
 	for slotNum = 1, #Amr.SlotIds do
 		local slotId = Amr.SlotIds[slotNum]
-		if not _pendingEquip.doneSlots[slotId] then
+		if _currentGearOp.slotsRemaining[slotId] then
 			local itemLink = GetInventoryItemLink("player", slotId)
 			if itemLink then
 				local invItem = Amr.ParseItemLink(itemLink)
-				if invItem ~= nil then
+				if invItem then
 					local diff = countItemDifferences(item, invItem)
 					if diff < bestDiff then
 						bestItem = { slot = slotId }
@@ -567,251 +612,429 @@
 	end
 	
 	-- bank
-	bestItem, bestDiff, bestLink = scanBagForItem(item, BANK_CONTAINER, bestItem, bestDiff, bestLink)
-	for bagId = NUM_BAG_SLOTS + 1, NUM_BAG_SLOTS + NUM_BANKBAGSLOTS do
-		bestItem, bestDiff, bestLink = scanBagForItem(item, bagId, bestItem, bestDiff, bestLink)
+	if bestDiff > 0 then
+		bestItem, bestDiff, bestLink = scanBagForItem(item, BANK_CONTAINER, bestItem, bestDiff, bestLink)
+		for bagId = NUM_BAG_SLOTS + 1, NUM_BAG_SLOTS + NUM_BANKBAGSLOTS do
+			bestItem, bestDiff, bestLink = scanBagForItem(item, bagId, bestItem, bestDiff, bestLink)
+		end
 	end
+
+	return bestItem, bestDiff, bestLink
+end
+
+-- on completion, create an equipment manager set if desired
+local function onEquipGearSetComplete()
+	if Amr.db.profile.options.disableEm then return end
 	
-	ClearCursor()
+	-- create an equipment manager set
+	local specId, specName = GetSpecializationInfo(GetSpecialization())
 	
-	if not bestItem then
-		-- stop if we can't find an item
-		Amr:Print(L.GearEquipErrorNotFound)
-		Amr:Print(L.GearEquipErrorNotFound2)
-		_pendingEquip = nil
-		return
-		
-	elseif bestItem and bestItem.bag and (bestItem.bag == BANK_CONTAINER or bestItem.bag >= NUM_BAG_SLOTS + 1 and bestItem.bag <= NUM_BAG_SLOTS + NUM_BANKBAGSLOTS) then
+	local item = Amr.ParseItemLink(GetInventoryItemLink("player", INVSLOT_MAINHAND))
+	if not item then
+		item = Amr.ParseItemLink(GetInventoryItemLink("player", INVSLOT_OFFHAND))
+	end
+	if item then
+		Amr.GetItemInfo(item.id, function(customArg, name, link, quality, iLevel, reqLevel, class, subclass, maxStack, equipSlot, texture)
+			local setname = "AMR " .. specName
+			local setid = C_EquipmentSet.GetEquipmentSetID(setname)
+			if setid then
+				C_EquipmentSet.SaveEquipmentSet(setid, texture)
+			else
+				C_EquipmentSet.CreateEquipmentSet(setname, texture)
+			end
+		end)
+	end
+end
+
+-- stop any currently in-progress gear swapping operation and clean up
+local function disposeGearOp()
+	_pendingGearOps = nil
+	_currentGearOp = nil
+	_itemLockAction = nil
+	_gearOpPasses = 0
+	_gearOpWaiting = nil
+
+	-- make sure the gear tab is still in sync
+	Amr:RefreshGearTab()
+end
+
+-- initialize a gear op to start running it
+local function initializeGearOp(op, spec, pos)
+	op.pos = pos
+	op.spec = spec
+
+	-- fill the remaining slot list and set the starting slot
+	op.nextSlot = nil
+	op.slotsRemaining = {}	
+	op.isWaiting = false
+	for slotId, item in pairs(op.items) do
+		op.slotsRemaining[slotId] = true
+		if not op.nextSlot then
+			op.nextSlot = slotId
+		end			
+	end
+end
+
+function processCurrentGearOp()
+	if not _currentGearOp then return end
+
+	if _currentGearOp.remove then
+		-- remove the next item
+
+		-- check if the slot is already empty
+		local itemLink = GetInventoryItemLink("player", _currentGearOp.nextSlot)
+		if not itemLink then
+			nextGearOp()
+			return
+		end
+
 		-- find first empty bag slot
 		local invBag, invSlot = findFirstEmptyBagSlot()
 		if not invBag then
 			-- stop if bags are too full
 			Amr:Print(L.GearEquipErrorBagFull)
-			_pendingEquip = nil
+			disposeGearOp()
 			return
 		end
 
-		-- move from bank to bag
-		PickupContainerItem(bestItem.bag, bestItem.slot)
+		PickupInventoryItem(_currentGearOp.nextSlot)
 		PickupContainerItem(invBag, invSlot)
 
-		-- set flag so that when we clear cursor and release the item lock, we can respond to the event and continue
-		_waitingForItemLock = {
+		-- set an action to happen on ITEM_UNLOCKED, triggered by ClearCursor
+		_itemLockAction = {
 			bagId = invBag,
-			slotId = invSlot
+			slotId = invSlot,
+			isRemove = true			
 		}
+
+		ClearCursor()
+		-- wait for remove to complete
+	else
+		-- equip the next item
 		
+		local bestItem, bestDiff, bestLink = findCurrentGearOpItem()
+		
+		_itemLockAction = nil
 		ClearCursor()
-		
-		-- now we need to wait for game event to continue and try this item again after it is in our bag
-		return
-	else
-		if not Amr:CanEquip(bestItem.bag, bestItem.slot) then
+	
+		if not bestItem then
+			-- stop if we can't find an item
+			Amr:Print(L.GearEquipErrorNotFound)
+			Amr:Print(L.GearEquipErrorNotFound2)
+			disposeGearOp()
+			
+		elseif bestItem and bestItem.bag and (bestItem.bag == BANK_CONTAINER or bestItem.bag >= NUM_BAG_SLOTS + 1 and bestItem.bag <= NUM_BAG_SLOTS + NUM_BANKBAGSLOTS) then
+			-- find first empty bag slot
+			local invBag, invSlot = findFirstEmptyBagSlot()
+			if not invBag then
+				-- stop if bags are too full
+				Amr:Print(L.GearEquipErrorBagFull)
+				disposeGearOp()
+				return
+			end
+	
+			-- move from bank to bag
+			PickupContainerItem(bestItem.bag, bestItem.slot)
+			PickupContainerItem(invBag, invSlot)
+	
+			-- set an action to happen on ITEM_UNLOCKED, triggered by ClearCursor
+			_itemLockAction = {
+				bagId = invBag,
+				slotId = invSlot,
+				isBank = true			
+			}
+			
+			ClearCursor()			
+			-- now we need to wait for game event to continue and try this item again after it is in our bag and unlocked
+
+		elseif (bestItem.bag or bestItem.bag == 0) and not Amr:CanEquip(bestItem.bag, bestItem.slot) then
 			-- if an item is not soulbound, then warn the user and quit
 			Amr:Print(L.GearEquipErrorSoulbound(bestLink))
-			_pendingEquip = nil
-			return
+			disposeGearOp()
+
 		else
-			local slotId = _pendingEquip.nextSlot
-			
+
 			-- an item in the player's bags or already equipped, equip it
-			_pendingEquip.bag = bestItem.bag
-			_pendingEquip.slot = bestItem.slot
-			_pendingEquip.destSlot = slotId
-			
 			if bestItem.bag then
 				PickupContainerItem(bestItem.bag, bestItem.slot)
 			else
+				_gearOpWaiting.inventory[bestItem.slot] = true
 				PickupInventoryItem(bestItem.slot)
 			end
-			PickupInventoryItem(slotId)
-			ClearCursor()
+			_gearOpWaiting.inventory[_currentGearOp.nextSlot] = true
+			PickupInventoryItem(_currentGearOp.nextSlot)
+
+			-- don't wait for now, do all equips at once
+			--[[
+			-- set an action to happen on ITEM_UNLOCKED, triggered by ClearCursor
+			_itemLockAction = {
+				bagId = bestItem.bag,
+				slotId = bestItem.slot,
+				invSlot = _currentGearOp.nextSlot,
+				isEquip = true
+			}
+			]]
+
+			ClearCursor()			
+			nextGearOp()			
+		end
+
+	end
+end
+
+-- when a gear op completes successfully, this will advance to the next op or finish
+function nextGearOp()
+	if not _currentGearOp then return end
+
+	local spec = _currentGearOp.spec
+	local pos = _currentGearOp.pos
+	local passes = _gearOpPasses	
+
+	-- mark the slot as done and move to the next
+	if _currentGearOp.nextSlot then
+		_currentGearOp.slotsRemaining[_currentGearOp.nextSlot] = nil
+		_currentGearOp.nextSlot = nil
+		for slotId, item in pairs(_currentGearOp.items) do
+			if _currentGearOp.slotsRemaining[slotId] then
+				_currentGearOp.nextSlot = slotId
+				break
+			end
+		end
+	end
+
+	if not _currentGearOp.nextSlot then
+		-- see if anything is still in progress and we want to wait for it before continuing		
+		local inProgress = not Amr.IsEmpty(_gearOpWaiting.inventory)
+
+		if (_currentGearOp.wait or _currentGearOp.remove) and inProgress then
+			-- this will cause the item unlock handler to call nextGearOp again when all in-progress swaps have unlocked related slots
+			_currentGearOp.isWaiting = true
+		else
+			_currentGearOp = _pendingGearOps[pos + 1]
+			if _currentGearOp then
+				-- we have another op, do it
+				initializeGearOp(_currentGearOp, spec, pos + 1)
+				processCurrentGearOp()
+			else
+				-- we are done
+				disposeGearOp()
+
+				-- this will check if not all items were swapped, and either finish up, try again, or abort if have tried too many times
+				beginEquipGearSet(spec, passes + 1)
+			end
+		end
+	else
+		-- do the next item
+		processCurrentGearOp()
+	end
+
+end
+
+local function handleItemUnlocked(bagId, slotId)
+
+	-- mark anything that is waiting as unlocked if it is no longer locked
+	if _currentGearOp and _gearOpWaiting then
+		for i,s in ipairs(Amr.SlotIds) do
+			if not IsInventoryItemLocked(s) then
+				_gearOpWaiting.inventory[s] = nil
+			end
+		end
+	end
+
+	if _itemLockAction then
+		if _itemLockAction.isRemove then
+			-- waiting for a specific remove op to finish before continuing
+			if bagId == _itemLockAction.bagId and slotId == _itemLockAction.slotId then
+				_itemLockAction = nil
+				nextGearOp()
+			end
+		elseif _itemLockAction.isBank then
+			-- waiting for an item to move from bank into inventory, then reprocess the current op
+			if bagId == _itemLockAction.bagId and slotId == _itemLockAction.slotId then
+				_itemLockAction = nil
+				processCurrentGearOp()
+			end
+
+		elseif _itemLockAction.isEquip then
+			-- this is not currently used... we do all equips at once usually, but could go back to this if it causes problems
+
+			-- waiting for a specific equip op to finish
 			
-			-- wait for game events to continue
+			-- inventory slot we're swapping to is still locked, can't continue yet
+			if IsInventoryItemLocked(_itemLockAction.invSlot) then return end
+
+			if _itemLockAction.bagId then
+				local _, _, locked = GetContainerItemInfo(_itemLockAction.bagId, _itemLockAction.slotId)
+				-- the bag slot we're swapping from is still locked, can't continue yet
+				if locked then return end
+			else
+				-- inventory slot we're swapping from is still locked, can't continue yet
+				if IsInventoryItemLocked(_itemLockAction.slotId) then return end
+			end
+			
+			_itemLockAction = nil
+			nextGearOp()
+		else
+			-- unknown... shouldn't happen
+			_itemLockAction = nil
 		end
+	else
+		
+		-- not waiting on a specific action, check if we are waiting for all locked slots to open up and they are done
+		if _currentGearOp and _gearOpWaiting and _currentGearOp.isWaiting and Amr.IsEmpty(_gearOpWaiting.inventory) then
+			nextGearOp()
+		end	
 	end
 	
 end
 
-local function removeNextItem()
-	if not _pendingRemove then return end
-	
-	local list = _pendingRemove.slotsToRemove
-	local slot = list[#list - _pendingRemove.remaining + 1]
-	
-	-- find first empty bag slot
-	local invBag, invSlot = findFirstEmptyBagSlot()
-	if not invBag then
-		-- stop if bags are too full
-		Amr:Print(L.GearEquipErrorBagFull)
-		_pendingRemove = nil
-		_pendingEquip = nil
-		return
+local function shuffle(tbl)
+	local size = #tbl
+	for i = size, 1, -1 do
+		local rand = math.random(size)
+		tbl[i], tbl[rand] = tbl[rand], tbl[i]
 	end
-	
-	PickupInventoryItem(slot)
-	PickupContainerItem(invBag, invSlot)
-	
-	-- set flag so that when we clear cursor and release the item lock, we can respond to the event and continue
-	_waitingForItemLock = {
-		bagId = invBag,
-		slotId = invSlot,
-		isRemove = true
-	}
-	
-	ClearCursor()
+	return tbl
 end
 
-local function onItemUnlocked(bagId, slotId)
-	
-	if _waitingForItemLock then
-		-- waiting on a move from bank to bags to complete, or waiting on removing an item to complete, just continue as normal afterwards
-		if bagId == _waitingForItemLock.bagId and slotId == _waitingForItemLock.slotId then
-			local isremove = _waitingForItemLock.isRemove
-			_waitingForItemLock = nil
-			
-			if isremove then
-				_pendingRemove.remaining = _pendingRemove.remaining - 1
-				if _pendingRemove.remaining > 0 then
-					removeNextItem()
-				else
-					-- we have removed all items that we want to remove, now do the equip
-					_pendingRemove = nil
-					tryEquipNextItem()
-				end
-			else
-				tryEquipNextItem()
-			end
-		end
-		
-	elseif _pendingEquip and _pendingEquip.destSlot then
-		-- waiting on an item swap to complete successfully so that we can go on to the next item
-		
-		-- inventory slot we're swapping to is still locked, can't continue yet
-		if IsInventoryItemLocked(_pendingEquip.destSlot) then return end
-
-		if _pendingEquip.bag then
-			local _, _, locked = GetContainerItemInfo(_pendingEquip.bag, _pendingEquip.slot)
-			-- the bag slot we're swapping from is still locked, can't continue yet
-			if locked then return end
-		else
-			-- inventory slot we're swapping from is still locked, can't continue yet
-			if IsInventoryItemLocked(_pendingEquip.slot) then return end
-		end
-		
-		-- move on to the next item, this item is done or could not be swapped
-		
-		local item = _pendingEquip.itemsToEquip[_pendingEquip.destSlot]
-		local itemLink = GetInventoryItemLink("player", _pendingEquip.destSlot)
-		if itemLink then
-			local invItem = Amr.ParseItemLink(itemLink)
-			if invItem ~= nil then	
-				local diff = countItemDifferences(item, invItem)
-				if diff == 0 then
-					_pendingEquip.doneSlots[_pendingEquip.destSlot] = true
-				end
-			end
-		end
-		
-		_pendingEquip.itemsToEquip[_pendingEquip.destSlot] = nil
-		_pendingEquip.destSlot = nil
-		_pendingEquip.bag = nil
-		_pendingEquip.slot = nil
-		
-		_pendingEquip.remaining = _pendingEquip.remaining - 1
-		if _pendingEquip.remaining > 0 then
-			for slotId, item in pairs(_pendingEquip.itemsToEquip) do
-				_pendingEquip.nextSlot = slotId
-				break
-			end
-			tryEquipNextItem()
-		else
-			finishEquipGearSet()
-		end
-		
-	end
-end
-
-local function startEquipGearSet(spec)
+function beginEquipGearSet(spec, passes)
 
 	local gear = Amr.db.char.GearSets[spec]
 	if not gear then 
 		Amr:Print(L.GearEquipErrorEmpty)
 		return
 	end
-	
+
+	-- ensure all our stored data is up to date
 	local player = Amr:ExportCharacter()
 
-	local itemsToEquip = {}
+	local itemsToEquip = {
+		legendaries = {},
+		weapons = {},
+		rings = {},
+		trinkets = {},
+		others = {},
+		blanks = {}
+	}
 	local remaining = 0
-	local usedItems = {}
-	local firstSlot = nil
-	
-	-- check for items that need to be equipped
-	for slotNum = 1, #Amr.SlotIds do
-		local slotId = Amr.SlotIds[slotNum]
-		
+	local usedItems = {}	
+
+	-- check for items that need to be equipped, do in a random order to try and defeat any unique constraint issues we might hit
+	local slots = {}
+	for i,s in ipairs(Amr.SlotIds) do
+		table.insert(slots, s)
+	end
+	shuffle(slots)
+
+	for i,slotId in ipairs(slots) do
+
+		-- we do stuff in batches that avoids most unique conflicts
+		local list = itemsToEquip.others
+		if slotId == 16 or slotId == 17 then
+			list = itemsToEquip.weapons
+		elseif slotId == 11 or slotId == 12 then
+			list = itemsToEquip.rings
+		elseif slotId == 13 or slotId == 14 then
+			list = itemsToEquip.trinkets
+		end
+
 		local old = player.Equipped[spec][slotId]
-		old = Amr.ParseItemLink(old)
-		
 		local new = gear[slotId]
+		local prevRemaining = remaining
 		if new then
-			local diff = countItemDifferences(old, new)
-			if diff < 1000 then
-				-- same item, see if inventory has one that is closer (e.g. a duplicate item with correct enchants/gems)
-				local bestLink, bestItem, bestDiff = Amr:FindMatchingItem(new, player, usedItems)
-				if bestDiff and bestDiff < diff then
-					itemsToEquip[slotId] = new
+			-- if the new thing is an artifact, only match the item id
+			local newItem = Item:CreateFromItemID(new.id)
+			local quality = newItem and newItem:GetItemQuality() or 0
+			if quality == 6 then
+				if not old or new.id ~= old.id then
+					list[slotId] = new
 					remaining = remaining + 1
 				end
-			else
-				itemsToEquip[slotId] = new
-				remaining = remaining + 1
+			else				
+				local diff = countItemDifferences(old, new)
+				if diff > 0 and diff < 1000 then
+					-- same item, see if inventory has one that is closer (e.g. a duplicate item with correct enchants/gems)
+					local bestItem, bestDiff = Amr:FindMatchingItem(new, player, usedItems)
+					if bestDiff and bestDiff < diff then
+						new = bestItem
+						diff = bestDiff
+					end
+				end
+
+				if diff > 0 then
+					list[slotId] = new
+					remaining = remaining + 1
+				end
+			end
+		else
+			-- need to remove this item
+			itemsToEquip.blanks[slotId] = {}
+			remaining = remaining + 1
+		end
+
+		if remaining > prevRemaining then
+			-- if we need to swap this slot, see if the old item is a legendary, add a step to remove those first to avoid conflicts
+			if old then
+				local oldItem = Item:CreateFromItemID(old.id)
+				if oldItem and oldItem:GetItemQuality() == 5 then				
+					itemsToEquip.legendaries[slotId] = {}
+				end
 			end
 		end
 	end
+	
+	if remaining > 0 then
 
-	if remaining > 0 then
-		-- if this is not our first try, then remove weapons before starting
-		local toRemove = {}
-		local removesRemaining = 0
-		if _pendingEquip and _pendingEquip.tries > 0 then
-			for slotId, item in pairs(itemsToEquip) do
-				if slotId == 16 or slotId == 17 then
-					table.insert(toRemove, slotId)
-					removesRemaining = removesRemaining + 1
-				end
-			end			
+		if passes < 5 then
+			_pendingGearOps = {}
+
+			if not Amr.IsEmpty(itemsToEquip.blanks) then
+				-- if gear set wants slots to be blank, do that first
+				table.insert(_pendingGearOps, { items = itemsToEquip.blanks, remove = true, label = "blanks" }) 
+			end
+			if not Amr.IsEmpty(itemsToEquip.weapons) then
+				-- change weapons first: remove both, wait, then equip new ones
+				table.insert(_pendingGearOps, { items = itemsToEquip.weapons, remove = true, label = "remove weapons" })				
+				table.insert(_pendingGearOps, { items = itemsToEquip.weapons, wait = true, label = "equip weapons" })
+			end
+			if not Amr.IsEmpty(itemsToEquip.legendaries) then 
+				-- remove any legendaries, wait
+				table.insert(_pendingGearOps, { items = itemsToEquip.legendaries, remove = true, label = "remove legendaries" }) 
+			end
+			if not Amr.IsEmpty(itemsToEquip.rings) then 
+				-- remove both rings, wait, then equip new ones
+				table.insert(_pendingGearOps, { items = itemsToEquip.rings, remove = true, label = "remove rings" })
+				table.insert(_pendingGearOps, { items = itemsToEquip.rings, wait = true, label = "equip rings" })
+			end
+			if not Amr.IsEmpty(itemsToEquip.trinkets) then 
+				-- remove both trinkets, wait, then equip new ones
+				table.insert(_pendingGearOps, { items = itemsToEquip.trinkets, remove = true, label = "remove trinkets" })
+				table.insert(_pendingGearOps, { items = itemsToEquip.trinkets, wait = true, label = "equip trinkets" })
+			end
+			if not Amr.IsEmpty(itemsToEquip.others) then 
+				-- equip all other items, wait for completion
+				table.insert(_pendingGearOps, { items = itemsToEquip.others, wait = true, label = "equip others" }) 
+			end
+
+			-- make the last operation wait no matter what, before this gets called again to check if everything succeeded
+			_pendingGearOps[#_pendingGearOps].wait = true
+
+			if not _gearOpWaiting then
+				_gearOpWaiting = { inventory = {} }
+			end
+
+			_gearOpPasses = passes
+			_currentGearOp = _pendingGearOps[1]
+			initializeGearOp(_currentGearOp, spec, 1)
+
+			processCurrentGearOp()
+		else
+			-- TODO: print message that gear set couldn't be equipped
 		end
-		
-		_pendingEquip = {
-			tries = _pendingEquip and _pendingEquip.spec == spec and _pendingEquip.tries or 0,
-			spec = spec,
-			itemsToEquip = itemsToEquip,
-			remaining = remaining,
-			doneSlots = _pendingEquip and _pendingEquip.spec == spec and _pendingEquip.doneSlots or {},
-			nextSlot = firstSlot
-		}
 
-		-- starting item
-		for slotId, item in pairs(_pendingEquip.itemsToEquip) do
-			_pendingEquip.nextSlot = slotId
-			break
-		end
-		
-		if removesRemaining > 0 then
-			_pendingRemove = {
-				slotsToRemove = toRemove,
-				remaining = removesRemaining
-			}
-			removeNextItem()
-		else
-			tryEquipNextItem()
-		end
 	else
-		_pendingEquip = nil
 		onEquipGearSetComplete()
-	end
+	end	
 end
 
 local function onActiveTalentGroupChanged()
@@ -821,7 +1044,7 @@
 	
 	if currentSpec == _waitingForSpec or auto then
 		-- spec is what we want, now equip the gear
-		startEquipGearSet(currentSpec)
+		beginEquipGearSet(currentSpec, 0)
 	end
 	
 	_waitingForSpec = 0
@@ -856,7 +1079,7 @@
 	end
 end
 
--- moves any gear in bags to the bank if not part of main or off spec gear set
+-- moves any gear in bags to the bank if not part of a gear set
 function Amr:CleanBags()
 	-- TODO: implement
 end
@@ -874,8 +1097,12 @@
 	
 	Amr:AddEventHandler("UNIT_INVENTORY_CHANGED", function(unitID)
 		if unitID and unitID ~= "player" then return end
+		
+		-- don't update during a gear operation, wait until it is totally finished
+		if _pendingGearOps then return end
+
 		Amr:RefreshGearTab()
 	end)
 
-	Amr:AddEventHandler("ITEM_UNLOCKED", onItemUnlocked)
+	Amr:AddEventHandler("ITEM_UNLOCKED", handleItemUnlocked)
 end
--- a/Import.lua	Mon Feb 12 19:34:09 2018 -0800
+++ b/Import.lua	Tue Jul 17 09:57:39 2018 -0700
@@ -42,18 +42,18 @@
 	local panelImport = Amr:RenderCoverChrome(container, 700, 450)
 	
 	local lbl = AceGUI:Create("AmrUiLabel")
+	panelImport:AddChild(lbl)
 	lbl:SetWidth(600)
 	lbl:SetText(L.ImportHeader)
 	lbl:SetPoint("TOP", panelImport.content, "TOP", 0, -10)
-	panelImport:AddChild(lbl)
 
 	_txtImport = AceGUI:Create("AmrUiTextarea")
 	_txtImport:SetWidth(600)
 	_txtImport:SetHeight(300)
-	_txtImport:SetPoint("TOP", lbl.frame, "BOTTOM", 0, -10)
 	_txtImport:SetFont(Amr.CreateFont("Regular", 12, Amr.Colors.Text))
 	_txtImport:SetCallback("OnEnterPressed", onTextEnterPressed)
 	panelImport:AddChild(_txtImport)
+	_txtImport:SetPoint("TOP", lbl.frame, "BOTTOM", 0, -10)
 	
 	local btnImportOk = AceGUI:Create("AmrUiButton")
 	btnImportOk:SetText(L.ImportButtonOk)
@@ -61,9 +61,9 @@
 	btnImportOk:SetFont(Amr.CreateFont("Bold", 16, Amr.Colors.White))
 	btnImportOk:SetWidth(120)
 	btnImportOk:SetHeight(28)
-	btnImportOk:SetPoint("TOPLEFT", _txtImport.frame, "BOTTOMLEFT", 0, -10)
 	btnImportOk:SetCallback("OnClick", onImportOkClick)
 	panelImport:AddChild(btnImportOk)
+	btnImportOk:SetPoint("TOPLEFT", _txtImport.frame, "BOTTOMLEFT", 0, -10)
 	
 	local btnImportCancel = AceGUI:Create("AmrUiButton")
 	btnImportCancel:SetText(L.ImportButtonCancel)
@@ -71,16 +71,16 @@
 	btnImportCancel:SetFont(Amr.CreateFont("Bold", 16, Amr.Colors.White))
 	btnImportCancel:SetWidth(120)
 	btnImportCancel:SetHeight(28)
-	btnImportCancel:SetPoint("LEFT", btnImportOk.frame, "RIGHT", 20, 0)
 	btnImportCancel:SetCallback("OnClick", onImportCancelClick)
 	panelImport:AddChild(btnImportCancel)
+	btnImportCancel:SetPoint("LEFT", btnImportOk.frame, "RIGHT", 20, 0)
 	
 	_lblError = AceGUI:Create("AmrUiLabel")
+	panelImport:AddChild(_lblError)
 	_lblError:SetWidth(600)
 	_lblError:SetFont(Amr.CreateFont("Bold", 14, Amr.Colors.Red))
 	_lblError:SetText("")
 	_lblError:SetPoint("TOPLEFT", btnImportOk.frame, "BOTTOMLEFT", 0, -20)
-	panelImport:AddChild(_lblError)
 	
 	if fromOverwolf then
 		-- show a cover preventing interaction until we receive data from overwolf
@@ -88,18 +88,18 @@
 		_panelCover:SetLayout("None")
 		_panelCover:EnableMouse(true)
 		_panelCover:SetBackgroundColor(Amr.Colors.Black, 0.75)
+		panelImport:AddChild(_panelCover)
 		_panelCover:SetPoint("TOPLEFT", panelImport.frame, "TOPLEFT")
 		_panelCover:SetPoint("BOTTOMRIGHT", panelImport.frame, "BOTTOMRIGHT")
-		panelImport:AddChild(_panelCover)
 
 		local coverMsg = AceGUI:Create("AmrUiLabel")
+		_panelCover:AddChild(coverMsg)
 		coverMsg:SetWidth(500)
 		coverMsg:SetFont(Amr.CreateFont("Regular", 16, Amr.Colors.TextTan))
 		coverMsg:SetJustifyH("MIDDLE")
 		coverMsg:SetJustifyV("MIDDLE")
 		coverMsg:SetText(L.ImportOverwolfWait)
 		coverMsg:SetPoint("CENTER", _panelCover.frame, "CENTER", 0, 20)
-		_panelCover:AddChild(coverMsg)
 		
 		-- after adding, set cover to sit on top of everything
 		_panelCover:SetStrata("FULLSCREEN_DIALOG")
@@ -189,12 +189,9 @@
     end
     
     -- if we make it this far, the data is valid, so read item information
-	local specSlot = tonumber(parts[10])
+	local specSlot = tonumber(parts[11])
 	
     local importData = {}
-
-    local itemInfo = {}
-    local gemInfo = {}
     local enchantInfo = {}
     
     local prevItemId = 0
@@ -202,7 +199,8 @@
     local prevEnchantId = 0
     local prevUpgradeId = 0
     local prevBonusId = 0
-	local prevLevel = 0
+    local prevLevel = 0
+    local prevAzeriteId = 0
     local digits = {
         ["-"] = true,
         ["0"] = true,
@@ -216,17 +214,14 @@
         ["8"] = true,
         ["9"] = true,
     }
-    for i = 18, #parts do
+    for i = 16, #parts do
         local itemString = parts[i]
         if itemString ~= "" and itemString ~= "_" then
             local tokens = {}
             local bonusIds = {}
-			local relicBonusIds = {}
-			table.insert(relicBonusIds, {})
-			table.insert(relicBonusIds, {})
-			table.insert(relicBonusIds, {})
-			local hasRelics = false
+            local azerite = {}
             local hasBonuses = false
+            local hasAzerites = false
             local token = ""
             local prop = "i"
             local tokenComplete = false
@@ -258,20 +253,17 @@
                     elseif prop == "e" then
                         val = val + prevEnchantId
                         prevEnchantId = val
+                    elseif prop == "a" then
+                        val = val + prevAzeriteId
+                        prevAzeriteId = val
                     end
                     
                     if prop == "b" then
                         table.insert(bonusIds, val)
                         hasBonuses = true
-					elseif prop == "m" then
-						table.insert(relicBonusIds[1], val)
-						hasRelics = true
-					elseif prop == "n" then
-						table.insert(relicBonusIds[2], val)
-						hasRelics = true
-					elseif prop == "o" then
-						table.insert(relicBonusIds[3], val)
-						hasRelics = true
+                    elseif prop == "a" then
+                        table.insert(azerite, val)
+                        hasAzerites = true
                     else
                         tokens[prop] = val
                     end
@@ -299,33 +291,14 @@
             table.insert(obj.gemIds, tokens["y"] or 0)
             table.insert(obj.gemIds, tokens["z"] or 0)
             table.insert(obj.gemIds, 0)
-			
-			if hasRelics then
-				obj.relicBonusIds = relicBonusIds
-			end
             
             if hasBonuses then
                 obj.bonusIds = bonusIds
             end
             
-            local itemObj = {}
-            itemObj.id = obj.id
-            itemInfo[obj.id] = itemObj
-            
-            -- look for any socket color information, add to our extra data
-            if tokens["c"] then
-                itemObj.socketColors = {}
-                for j = 1, string.len(tokens["c"]) do
-                    table.insert(itemObj.socketColors, tonumber(string.sub(tokens["c"], j, j)))
-                end                
+            if hasAzerites then
+                obj.azerite = azerite
             end
-            
-            -- look for item ID duplicate info, deals with old SoO items
-            if tokens["d"] then
-                itemObj.duplicateId = tonumber(tokens["d"])
-                itemInfo[itemObj.duplicateId] = itemObj
-            end
-            
         end
     end
     
@@ -334,35 +307,7 @@
     for i = 1, #parts do
         local infoParts = { strsplit("\\", parts[i]) }
         
-        if infoParts[1] == "g" then
-        
-            local gemObj = {}            
-            gemObj.enchantId = tonumber(infoParts[2])
-            gemObj.id = tonumber(infoParts[3])
-            
-            local identicalGems = infoParts[4]
-            if string.len(identicalGems) > 0 then
-                gemObj.identicalGroup = {}
-                identicalGems = { strsplit(",", identicalGems) }
-                for j = 1, #identicalGems do
-                    gemObj.identicalGroup[tonumber(identicalGems[j])] = true
-                end
-            end
-            
-            gemObj.text = string.gsub(infoParts[5], "_(%a+)_", function(s) return L.StatsShort[s] end)
-            if infoParts[6] == nil or string.len(infoParts[6]) == 0 then
-            	gemObj.identicalItemGroup = {[gemObj.id]=true}
-            else
-            	local identicalIds = { strsplit(',', infoParts[6]) }
-            	gemObj.identicalItemGroup = {}
-            	for j = 1, #identicalIds do
-            		gemObj.identicalItemGroup[tonumber(identicalIds[j])] = true
-            	end            	
-            end            
-
-            gemInfo[gemObj.id] = gemObj
-            
-        elseif infoParts[1] == "e" then
+        if infoParts[1] == "e" then
         
             local enchObj = {}
             enchObj.id = tonumber(infoParts[2])
@@ -380,8 +325,7 @@
                 end
             end
             
-            enchantInfo[enchObj.id] = enchObj
-            
+            enchantInfo[enchObj.id] = enchObj            
         end
     end
     
@@ -397,15 +341,14 @@
                 print(blah)
                 print("bad item: " .. v.id)
             end
-        end
-        
-        
+        end                
     else
         -- we have succeeded, record the result
 		Amr.db.char.GearSets[specSlot] = importData
-		Amr.db.char.ExtraItemData[specSlot] = itemInfo
-		Amr.db.char.ExtraGemData[specSlot] = gemInfo
-		Amr.db.char.ExtraEnchantData[specSlot] = enchantInfo
+
+        for k,v in pairs(enchantInfo) do
+            Amr.db.char.ExtraEnchantData[k] = v    
+        end
 		
 		-- also update shopping list after import
 		Amr:UpdateShoppingData(currentPlayerData)
--- a/Libs/AceComm-3.0/AceComm-3.0.lua	Mon Feb 12 19:34:09 2018 -0800
+++ b/Libs/AceComm-3.0/AceComm-3.0.lua	Tue Jul 17 09:57:39 2018 -0700
@@ -9,7 +9,7 @@
 -- make into AceComm.
 -- @class file
 -- @name AceComm-3.0
--- @release $Id: AceComm-3.0.lua 1107 2014-02-19 16:40:32Z nevcairiel $
+-- @release $Id: AceComm-3.0.lua 1174 2018-05-14 17:29:49Z h.leppkes@gmail.com $
 
 --[[ AceComm-3.0
 
@@ -17,15 +17,14 @@
 
 ]]
 
-local MAJOR, MINOR = "AceComm-3.0", 9
+local CallbackHandler = LibStub("CallbackHandler-1.0")
+local CTL = assert(ChatThrottleLib, "AceComm-3.0 requires ChatThrottleLib")
 
+local MAJOR, MINOR = "AceComm-3.0", 12
 local AceComm,oldminor = LibStub:NewLibrary(MAJOR, MINOR)
 
 if not AceComm then return end
 
-local CallbackHandler = LibStub:GetLibrary("CallbackHandler-1.0")
-local CTL = assert(ChatThrottleLib, "AceComm-3.0 requires ChatThrottleLib")
-
 -- Lua APIs
 local type, next, pairs, tostring = type, next, pairs, tostring
 local strsub, strfind = string.sub, string.find
@@ -66,7 +65,11 @@
 	if #prefix > 16 then -- TODO: 15?
 		error("AceComm:RegisterComm(prefix,method): prefix length is limited to 16 characters")
 	end
-	RegisterAddonMessagePrefix(prefix)
+	if C_ChatInfo then
+		C_ChatInfo.RegisterAddonMessagePrefix(prefix)
+	else
+		RegisterAddonMessagePrefix(prefix)
+	end
 
 	return AceComm._RegisterComm(self, prefix, method)	-- created by CallbackHandler
 end
@@ -86,7 +89,7 @@
 	if not( type(prefix)=="string" and
 			type(text)=="string" and
 			type(distribution)=="string" and
-			(target==nil or type(target)=="string") and
+			(target==nil or type(target)=="string" or type(target)=="number") and
 			(prio=="BULK" or prio=="NORMAL" or prio=="ALERT") 
 		) then
 		error('Usage: SendCommMessage(addon, "prefix", "text", "distribution"[, "target"[, "prio"[, callbackFn, callbackarg]]])', 2)
--- a/Libs/AceComm-3.0/ChatThrottleLib.lua	Mon Feb 12 19:34:09 2018 -0800
+++ b/Libs/AceComm-3.0/ChatThrottleLib.lua	Tue Jul 17 09:57:39 2018 -0700
@@ -23,7 +23,7 @@
 -- LICENSE: ChatThrottleLib is released into the Public Domain
 --
 
-local CTL_VERSION = 23
+local CTL_VERSION = 24
 
 local _G = _G
 
@@ -213,9 +213,15 @@
 			return ChatThrottleLib.Hook_SendChatMessage(...)
 		end)
 		--SendAddonMessage
-		hooksecurefunc("SendAddonMessage", function(...)
-			return ChatThrottleLib.Hook_SendAddonMessage(...)
-		end)
+		if _G.C_ChatInfo then
+			hooksecurefunc(_G.C_ChatInfo, "SendAddonMessage", function(...)
+				return ChatThrottleLib.Hook_SendAddonMessage(...)
+			end)
+		else
+			hooksecurefunc("SendAddonMessage", function(...)
+				return ChatThrottleLib.Hook_SendAddonMessage(...)
+			end)
+		end
 	end
 	self.nBypass = 0
 end
@@ -461,7 +467,7 @@
 
 	local nSize = text:len();
 
-	if RegisterAddonMessagePrefix then
+	if C_ChatInfo or RegisterAddonMessagePrefix then
 		if nSize>255 then
 			error("ChatThrottleLib:SendAddonMessage(): message length cannot exceed 255 bytes", 2)
 		end
@@ -478,7 +484,11 @@
 	if not self.bQueueing and nSize < self:UpdateAvail() then
 		self.avail = self.avail - nSize
 		bMyTraffic = true
-		_G.SendAddonMessage(prefix, text, chattype, target)
+		if _G.C_ChatInfo then
+			_G.C_ChatInfo.SendAddonMessage(prefix, text, chattype, target)
+		else
+			_G.SendAddonMessage(prefix, text, chattype, target)
+		end
 		bMyTraffic = false
 		self.Prio[prio].nTotalSent = self.Prio[prio].nTotalSent + nSize
 		if callbackFn then
@@ -490,7 +500,7 @@
 
 	-- Message needs to be queued
 	local msg = NewMsg()
-	msg.f = _G.SendAddonMessage
+	msg.f = _G.C_ChatInfo and _G.C_ChatInfo.SendAddonMessage or _G.SendAddonMessage
 	msg[1] = prefix
 	msg[2] = text
 	msg[3] = chattype
--- a/Libs/AceEvent-3.0/AceEvent-3.0.lua	Mon Feb 12 19:34:09 2018 -0800
+++ b/Libs/AceEvent-3.0/AceEvent-3.0.lua	Tue Jul 17 09:57:39 2018 -0700
@@ -9,8 +9,10 @@
 -- make into AceEvent.
 -- @class file
 -- @name AceEvent-3.0
--- @release $Id: AceEvent-3.0.lua 975 2010-10-23 11:26:18Z nevcairiel $
-local MAJOR, MINOR = "AceEvent-3.0", 3
+-- @release $Id: AceEvent-3.0.lua 1161 2017-08-12 14:30:16Z funkydude $
+local CallbackHandler = LibStub("CallbackHandler-1.0")
+
+local MAJOR, MINOR = "AceEvent-3.0", 4
 local AceEvent = LibStub:NewLibrary(MAJOR, MINOR)
 
 if not AceEvent then return end
@@ -18,8 +20,6 @@
 -- Lua APIs
 local pairs = pairs
 
-local CallbackHandler = LibStub:GetLibrary("CallbackHandler-1.0")
-
 AceEvent.frame = AceEvent.frame or CreateFrame("Frame", "AceEvent30Frame") -- our event frame
 AceEvent.embeds = AceEvent.embeds or {} -- what objects embed this lib
 
--- a/Libs/AceGUI-3.0/AceGUI-3.0.lua	Mon Feb 12 19:34:09 2018 -0800
+++ b/Libs/AceGUI-3.0/AceGUI-3.0.lua	Tue Jul 17 09:57:39 2018 -0700
@@ -24,8 +24,8 @@
 -- f:AddChild(btn)
 -- @class file
 -- @name AceGUI-3.0
--- @release $Id: AceGUI-3.0.lua 1102 2013-10-25 14:15:23Z nevcairiel $
-local ACEGUI_MAJOR, ACEGUI_MINOR = "AceGUI-3.0", 34
+-- @release $Id: AceGUI-3.0.lua 1177 2018-06-25 12:12:48Z nevcairiel $
+local ACEGUI_MAJOR, ACEGUI_MINOR = "AceGUI-3.0", 36
 local AceGUI, oldminor = LibStub:NewLibrary(ACEGUI_MAJOR, ACEGUI_MINOR)
 
 if not AceGUI then return end -- No upgrade needed
@@ -811,3 +811,221 @@
 		height = height + rowheight + 3
 		safecall(content.obj.LayoutFinished, content.obj, nil, height)
 	end)
+
+-- Get alignment method and value. Possible alignment methods are a callback, a number, "start", "middle", "end", "fill" or "TOPLEFT", "BOTTOMRIGHT" etc.
+local GetCellAlign = function (dir, tableObj, colObj, cellObj, cell, child)
+	local fn = cellObj and (cellObj["align" .. dir] or cellObj.align)
+			or colObj and (colObj["align" .. dir] or colObj.align)
+			or tableObj["align" .. dir] or tableObj.align
+			or "CENTERLEFT"
+	local child, cell, val = child or 0, cell or 0, nil
+
+	if type(fn) == "string" then
+		fn = fn:lower()
+		fn = dir == "V" and (fn:sub(1, 3) == "top" and "start" or fn:sub(1, 6) == "bottom" and "end" or fn:sub(1, 6) == "center" and "middle")
+		  or dir == "H" and (fn:sub(-4) == "left" and "start" or fn:sub(-5) == "right" and "end" or fn:sub(-6) == "center" and "middle")
+		  or fn
+		val = (fn == "start" or fn == "fill") and 0 or fn == "end" and cell - child or (cell - child) / 2
+	elseif type(fn) == "function" then
+		val = fn(child or 0, cell, dir)
+	else
+		val = fn
+	end
+
+	return fn, max(0, min(val, cell))
+end
+
+-- Get width or height for multiple cells combined
+local GetCellDimension = function (dir, laneDim, from, to, space)
+	local dim = 0
+	for cell=from,to do
+		dim = dim + (laneDim[cell] or 0)
+	end
+	return dim + max(0, to - from) * (space or 0)
+end
+
+--[[ Options
+============
+Container:
+ - columns ({col, col, ...}): Column settings. "col" can be a number (<= 0: content width, <1: rel. width, <10: weight, >=10: abs. width) or a table with column setting.
+ - space, spaceH, spaceV: Overall, horizontal and vertical spacing between cells.
+ - align, alignH, alignV: Overall, horizontal and vertical cell alignment. See GetCellAlign() for possible values.
+Columns:
+ - width: Fixed column width (nil or <=0: content width, <1: rel. width, >=1: abs. width).
+ - min or 1: Min width for content based width
+ - max or 2: Max width for content based width
+ - weight: Flexible column width. The leftover width after accounting for fixed-width columns is distributed to weighted columns according to their weights.
+ - align, alignH, alignV: Overwrites the container setting for alignment.
+Cell:
+ - colspan: Makes a cell span multiple columns.
+ - rowspan: Makes a cell span multiple rows.
+ - align, alignH, alignV: Overwrites the container and column setting for alignment.
+]]
+AceGUI:RegisterLayout("Table",
+	function (content, children)
+		local obj = content.obj
+		obj:PauseLayout()
+
+		local tableObj = obj:GetUserData("table")
+		local cols = tableObj.columns
+		local spaceH = tableObj.spaceH or tableObj.space or 0
+		local spaceV = tableObj.spaceV or tableObj.space or 0
+		local totalH = (content:GetWidth() or content.width or 0) - spaceH * (#cols - 1)
+		
+		-- We need to reuse these because layout events can come in very frequently
+		local layoutCache = obj:GetUserData("layoutCache")
+		if not layoutCache then
+			layoutCache = {{}, {}, {}, {}, {}, {}}
+			obj:SetUserData("layoutCache", layoutCache)
+		end
+		local t, laneH, laneV, rowspans, rowStart, colStart = unpack(layoutCache)
+		
+		-- Create the grid
+		local n, slotFound = 0
+		for i,child in ipairs(children) do
+			if child:IsShown() then
+				repeat
+					n = n + 1
+					local col = (n - 1) % #cols + 1
+					local row = ceil(n / #cols)
+					local rowspan = rowspans[col]
+					local cell = rowspan and rowspan.child or child
+					local cellObj = cell:GetUserData("cell")
+					slotFound = not rowspan
+
+					-- Rowspan
+					if not rowspan and cellObj and cellObj.rowspan then
+						rowspan = {child = child, from = row, to = row + cellObj.rowspan - 1}
+						rowspans[col] = rowspan
+					end
+					if rowspan and i == #children then
+						rowspan.to = row
+					end
+
+					-- Colspan
+					local colspan = max(0, min((cellObj and cellObj.colspan or 1) - 1, #cols - col))
+					n = n + colspan
+
+					-- Place the cell
+					if not rowspan or rowspan.to == row then
+						t[n] = cell
+						rowStart[cell] = rowspan and rowspan.from or row
+						colStart[cell] = col
+
+						if rowspan then
+							rowspans[col] = nil
+						end
+					end
+				until slotFound
+			end
+		end
+
+		local rows = ceil(n / #cols)
+
+		-- Determine fixed size cols and collect weights
+		local extantH, totalWeight = totalH, 0
+		for col,colObj in ipairs(cols) do
+			laneH[col] = 0
+
+			if type(colObj) == "number" then
+				colObj = {[colObj >= 1 and colObj < 10 and "weight" or "width"] = colObj}
+				cols[col] = colObj
+			end
+
+			if colObj.weight then
+				-- Weight
+				totalWeight = totalWeight + (colObj.weight or 1)
+			else
+				if not colObj.width or colObj.width <= 0 then
+					-- Content width
+					for row=1,rows do
+						local child = t[(row - 1) * #cols + col]
+						if child then
+							local f = child.frame
+							f:ClearAllPoints()
+							local childH = f:GetWidth() or 0
+		
+							laneH[col] = max(laneH[col], childH - GetCellDimension("H", laneH, colStart[child], col - 1, spaceH))
+						end
+					end
+
+					laneH[col] = max(colObj.min or colObj[1] or 0, min(laneH[col], colObj.max or colObj[2] or laneH[col]))
+				else
+					-- Rel./Abs. width
+					laneH[col] = colObj.width < 1 and colObj.width * totalH or colObj.width
+				end
+				extantH = max(0, extantH - laneH[col])
+			end
+		end
+
+		-- Determine sizes based on weight
+		local scale = totalWeight > 0 and extantH / totalWeight or 0
+		for col,colObj in pairs(cols) do
+			if colObj.weight then
+				laneH[col] = scale * colObj.weight
+			end
+		end
+
+		-- Arrange children
+		for row=1,rows do
+			local rowV = 0
+
+			-- Horizontal placement and sizing
+			for col=1,#cols do
+				local child = t[(row - 1) * #cols + col]
+				if child then
+					local colObj = cols[colStart[child]]
+					local cellObj = child:GetUserData("cell")
+					local offsetH = GetCellDimension("H", laneH, 1, colStart[child] - 1, spaceH) + (colStart[child] == 1 and 0 or spaceH)
+					local cellH = GetCellDimension("H", laneH, colStart[child], col, spaceH)
+					
+					local f = child.frame
+					f:ClearAllPoints()
+					local childH = f:GetWidth() or 0
+
+					local alignFn, align = GetCellAlign("H", tableObj, colObj, cellObj, cellH, childH)
+					f:SetPoint("LEFT", content, offsetH + align, 0)
+					if child:IsFullWidth() or alignFn == "fill" or childH > cellH then
+						f:SetPoint("RIGHT", content, "LEFT", offsetH + align + cellH, 0)
+					end
+					
+					if child.DoLayout then
+						child:DoLayout()
+					end
+
+					rowV = max(rowV, (f:GetHeight() or 0) - GetCellDimension("V", laneV, rowStart[child], row - 1, spaceV))
+				end
+			end
+
+			laneV[row] = rowV
+
+			-- Vertical placement and sizing
+			for col=1,#cols do
+				local child = t[(row - 1) * #cols + col]
+				if child then
+					local colObj = cols[colStart[child]]
+					local cellObj = child:GetUserData("cell")
+					local offsetV = GetCellDimension("V", laneV, 1, rowStart[child] - 1, spaceV) + (rowStart[child] == 1 and 0 or spaceV)
+					local cellV = GetCellDimension("V", laneV, rowStart[child], row, spaceV)
+						
+					local f = child.frame
+					local childV = f:GetHeight() or 0
+
+					local alignFn, align = GetCellAlign("V", tableObj, colObj, cellObj, cellV, childV)
+					if child:IsFullHeight() or alignFn == "fill" then
+						f:SetHeight(cellV)
+					end
+					f:SetPoint("TOP", content, 0, -(offsetV + align))
+				end
+			end
+		end
+
+		-- Calculate total height
+		local totalV = GetCellDimension("V", laneV, 1, #laneV, spaceV)
+		
+		-- Cleanup
+		for _,v in pairs(layoutCache) do wipe(v) end
+
+		safecall(obj.LayoutFinished, obj, nil, totalV)
+		obj:ResumeLayout()
+	end)
--- a/Libs/AceGUI-3.0/widgets/AceGUIContainer-Frame.lua	Mon Feb 12 19:34:09 2018 -0800
+++ b/Libs/AceGUI-3.0/widgets/AceGUIContainer-Frame.lua	Tue Jul 17 09:57:39 2018 -0700
@@ -1,7 +1,7 @@
 --[[-----------------------------------------------------------------------------
 Frame Container
 -------------------------------------------------------------------------------]]
-local Type, Version = "Frame", 25
+local Type, Version = "Frame", 26
 local AceGUI = LibStub and LibStub("AceGUI-3.0", true)
 if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end
 
@@ -21,7 +21,7 @@
 Scripts
 -------------------------------------------------------------------------------]]
 local function Button_OnClick(frame)
-	PlaySound("gsTitleOptionExit")
+	PlaySound(799) -- SOUNDKIT.GS_TITLE_OPTION_EXIT
 	frame.obj:Hide()
 end
 
--- a/Libs/AceGUI-3.0/widgets/AceGUIContainer-TabGroup.lua	Mon Feb 12 19:34:09 2018 -0800
+++ b/Libs/AceGUI-3.0/widgets/AceGUIContainer-TabGroup.lua	Tue Jul 17 09:57:39 2018 -0700
@@ -2,7 +2,7 @@
 TabGroup Container
 Container that uses tabs on top to switch between groups.
 -------------------------------------------------------------------------------]]
-local Type, Version = "TabGroup", 35
+local Type, Version = "TabGroup", 36
 local AceGUI = LibStub and LibStub("AceGUI-3.0", true)
 if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end
 
@@ -63,7 +63,7 @@
 -------------------------------------------------------------------------------]]
 local function Tab_OnClick(frame)
 	if not (frame.selected or frame.disabled) then
-		PlaySound("igCharacterInfoTab")
+		PlaySound(841) -- SOUNDKIT.IG_CHARACTER_INFO_TAB
 		frame.obj:SelectTab(frame.value)
 	end
 end
--- a/Libs/AceGUI-3.0/widgets/AceGUIContainer-TreeGroup.lua	Mon Feb 12 19:34:09 2018 -0800
+++ b/Libs/AceGUI-3.0/widgets/AceGUIContainer-TreeGroup.lua	Tue Jul 17 09:57:39 2018 -0700
@@ -2,10 +2,12 @@
 TreeGroup Container
 Container that uses a tree control to switch between groups.
 -------------------------------------------------------------------------------]]
-local Type, Version = "TreeGroup", 40
+local Type, Version = "TreeGroup", 41
 local AceGUI = LibStub and LibStub("AceGUI-3.0", true)
 if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end
 
+local WoW80 = select(4, GetBuildInfo()) >= 80000
+
 -- Lua APIs
 local next, pairs, ipairs, assert, type = next, pairs, ipairs, assert, type
 local math_min, math_max, floor = math.min, math.max, floor
@@ -162,7 +164,7 @@
 local function FirstFrameUpdate(frame)
 	local self = frame.obj
 	frame:SetScript("OnUpdate", nil)
-	self:RefreshTree()
+	self:RefreshTree(nil, true)
 end
 
 local function BuildUniqueValue(...)
@@ -300,6 +302,8 @@
 
 	["OnRelease"] = function(self)
 		self.status = nil
+		self.tree = nil
+		self.frame:SetScript("OnUpdate", nil)
 		for k, v in pairs(self.localstatus) do
 			if k == "groups" then
 				for k2 in pairs(v) do
@@ -388,8 +392,8 @@
 		end
 	end,
 
-	["RefreshTree"] = function(self,scrollToSelection)
-		local buttons = self.buttons 
+	["RefreshTree"] = function(self,scrollToSelection,fromOnUpdate)
+		local buttons = self.buttons
 		local lines = self.lines
 
 		for i, v in ipairs(buttons) do
@@ -420,6 +424,12 @@
 		local maxlines = (floor(((self.treeframe:GetHeight()or 0) - 20 ) / 18))
 		if maxlines <= 0 then return end
 
+		-- workaround for lag spikes on WoW 8.0
+		if WoW80 and self.frame:GetParent() == UIParent and not fromOnUpdate then
+			self.frame:SetScript("OnUpdate", FirstFrameUpdate)
+			return
+		end
+
 		local first, last
 		
 		scrollToSelection = status.scrollToSelection
--- a/Libs/AceGUI-3.0/widgets/AceGUIContainer-Window.lua	Mon Feb 12 19:34:09 2018 -0800
+++ b/Libs/AceGUI-3.0/widgets/AceGUIContainer-Window.lua	Tue Jul 17 09:57:39 2018 -0700
@@ -21,7 +21,7 @@
 ]]
 do
 	local Type = "Window"
-	local Version = 5
+	local Version = 6
 
 	local function frameOnShow(this)
 		this.obj:Fire("OnShow")
@@ -32,7 +32,7 @@
 	end
 	
 	local function closeOnClick(this)
-		PlaySound("gsTitleOptionExit")
+		PlaySound(799) -- SOUNDKIT.GS_TITLE_OPTION_EXIT
 		this.obj:Hide()
 	end
 	
--- a/Libs/AceGUI-3.0/widgets/AceGUIWidget-Button.lua	Mon Feb 12 19:34:09 2018 -0800
+++ b/Libs/AceGUI-3.0/widgets/AceGUIWidget-Button.lua	Tue Jul 17 09:57:39 2018 -0700
@@ -2,7 +2,7 @@
 Button Widget
 Graphical Button.
 -------------------------------------------------------------------------------]]
-local Type, Version = "Button", 23
+local Type, Version = "Button", 24
 local AceGUI = LibStub and LibStub("AceGUI-3.0", true)
 if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end
 
@@ -18,7 +18,7 @@
 -------------------------------------------------------------------------------]]
 local function Button_OnClick(frame, ...)
 	AceGUI:ClearFocus()
-	PlaySound("igMainMenuOption")
+	PlaySound(852) -- SOUNDKIT.IG_MAINMENU_OPTION
 	frame.obj:Fire("OnClick", ...)
 end
 
--- a/Libs/AceGUI-3.0/widgets/AceGUIWidget-CheckBox.lua	Mon Feb 12 19:34:09 2018 -0800
+++ b/Libs/AceGUI-3.0/widgets/AceGUIWidget-CheckBox.lua	Tue Jul 17 09:57:39 2018 -0700
@@ -1,7 +1,7 @@
 --[[-----------------------------------------------------------------------------
 Checkbox Widget
 -------------------------------------------------------------------------------]]
-local Type, Version = "CheckBox", 22
+local Type, Version = "CheckBox", 23
 local AceGUI = LibStub and LibStub("AceGUI-3.0", true)
 if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end
 
@@ -60,9 +60,9 @@
 		self:ToggleChecked()
 
 		if self.checked then
-			PlaySound("igMainMenuOptionCheckBoxOn")
+			PlaySound(856) -- SOUNDKIT.IG_MAINMENU_OPTION_CHECKBOX_ON
 		else -- for both nil and false (tristate)
-			PlaySound("igMainMenuOptionCheckBoxOff")
+			PlaySound(857) -- SOUNDKIT.IG_MAINMENU_OPTION_CHECKBOX_OFF
 		end
 
 		self:Fire("OnValueChanged", self.checked)
--- a/Libs/AceGUI-3.0/widgets/AceGUIWidget-DropDown-Items.lua	Mon Feb 12 19:34:09 2018 -0800
+++ b/Libs/AceGUI-3.0/widgets/AceGUIWidget-DropDown-Items.lua	Tue Jul 17 09:57:39 2018 -0700
@@ -1,4 +1,4 @@
---[[ $Id: AceGUIWidget-DropDown-Items.lua 1153 2016-11-20 09:57:15Z nevcairiel $ ]]--
+--[[ $Id: AceGUIWidget-DropDown-Items.lua 1167 2017-08-29 22:08:48Z funkydude $ ]]--
 
 local AceGUI = LibStub("AceGUI-3.0")
 
@@ -323,7 +323,7 @@
 -- Does not close the pullout on click.
 do
 	local widgetType = "Dropdown-Item-Toggle"
-	local widgetVersion = 3
+	local widgetVersion = 4
 	
 	local function UpdateToggle(self)
 		if self.value then
@@ -343,9 +343,9 @@
 		if self.disabled then return end
 		self.value = not self.value
 		if self.value then
-			PlaySound("igMainMenuOptionCheckBoxOn")
+			PlaySound(856) -- SOUNDKIT.IG_MAINMENU_OPTION_CHECKBOX_ON
 		else
-			PlaySound("igMainMenuOptionCheckBoxOff")
+			PlaySound(857) -- SOUNDKIT.IG_MAINMENU_OPTION_CHECKBOX_OFF
 		end
 		UpdateToggle(self)
 		self:Fire("OnValueChanged", self.value)
--- a/Libs/AceGUI-3.0/widgets/AceGUIWidget-DropDown.lua	Mon Feb 12 19:34:09 2018 -0800
+++ b/Libs/AceGUI-3.0/widgets/AceGUIWidget-DropDown.lua	Tue Jul 17 09:57:39 2018 -0700
@@ -1,4 +1,4 @@
---[[ $Id: AceGUIWidget-DropDown.lua 1116 2014-10-12 08:15:46Z nevcairiel $ ]]--
+--[[ $Id: AceGUIWidget-DropDown.lua 1167 2017-08-29 22:08:48Z funkydude $ ]]--
 local AceGUI = LibStub("AceGUI-3.0")
 
 -- Lua APIs
@@ -356,7 +356,7 @@
 
 do
 	local widgetType = "Dropdown"
-	local widgetVersion = 30
+	local widgetVersion = 31
 	
 	--[[ Static data ]]--
 	
@@ -381,7 +381,7 @@
 	
 	local function Dropdown_TogglePullout(this)
 		local self = this.obj
-		PlaySound("igMainMenuOptionCheckBoxOn") -- missleading name, but the Blizzard code uses this sound
+		PlaySound(856) -- SOUNDKIT.IG_MAINMENU_OPTION_CHECKBOX_ON
 		if self.open then
 			self.open = nil
 			self.pullout:Close()
--- a/Libs/AceGUI-3.0/widgets/AceGUIWidget-EditBox.lua	Mon Feb 12 19:34:09 2018 -0800
+++ b/Libs/AceGUI-3.0/widgets/AceGUIWidget-EditBox.lua	Tue Jul 17 09:57:39 2018 -0700
@@ -1,7 +1,7 @@
 --[[-----------------------------------------------------------------------------
 EditBox Widget
 -------------------------------------------------------------------------------]]
-local Type, Version = "EditBox", 26
+local Type, Version = "EditBox", 28
 local AceGUI = LibStub and LibStub("AceGUI-3.0", true)
 if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end
 
@@ -73,7 +73,7 @@
 	local value = frame:GetText()
 	local cancel = self:Fire("OnEnterPressed", value)
 	if not cancel then
-		PlaySound("igMainMenuOptionCheckBoxOn")
+		PlaySound(856) -- SOUNDKIT.IG_MAINMENU_OPTION_CHECKBOX_ON
 		HideButton(self)
 	end
 end
@@ -81,23 +81,21 @@
 local function EditBox_OnReceiveDrag(frame)
 	local self = frame.obj
 	local type, id, info = GetCursorInfo()
+	local name
 	if type == "item" then
-		self:SetText(info)
-		self:Fire("OnEnterPressed", info)
-		ClearCursor()
+		name = info
 	elseif type == "spell" then
-		local name = GetSpellInfo(id, info)
+		name = GetSpellInfo(id, info)
+	elseif type == "macro" then
+		name = GetMacroInfo(id)
+	end
+	if name then
 		self:SetText(name)
 		self:Fire("OnEnterPressed", name)
 		ClearCursor()
-	elseif type == "macro" then
-		local name = GetMacroInfo(id)
-		self:SetText(name)
-		self:Fire("OnEnterPressed", name)
-		ClearCursor()
+		HideButton(self)
+		AceGUI:ClearFocus()
 	end
-	HideButton(self)
-	AceGUI:ClearFocus()
 end
 
 local function EditBox_OnTextChanged(frame)
--- a/Libs/AceGUI-3.0/widgets/AceGUIWidget-Slider.lua	Mon Feb 12 19:34:09 2018 -0800
+++ b/Libs/AceGUI-3.0/widgets/AceGUIWidget-Slider.lua	Tue Jul 17 09:57:39 2018 -0700
@@ -2,7 +2,7 @@
 Slider Widget
 Graphical Slider, like, for Range values.
 -------------------------------------------------------------------------------]]
-local Type, Version = "Slider", 21
+local Type, Version = "Slider", 22
 local AceGUI = LibStub and LibStub("AceGUI-3.0", true)
 if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end
 
@@ -108,7 +108,7 @@
 	end
 	
 	if value then
-		PlaySound("igMainMenuOptionCheckBoxOn")
+		PlaySound(856) -- SOUNDKIT.IG_MAINMENU_OPTION_CHECKBOX_ON
 		self.slider:SetValue(value)
 		self:Fire("OnMouseUp", value)
 	end
--- a/Options.lua	Mon Feb 12 19:34:09 2018 -0800
+++ b/Options.lua	Tue Jul 17 09:57:39 2018 -0700
@@ -29,7 +29,7 @@
 	end
 end
 
-local function onCheckClick(widget)
+local function onCheckClick(widget)	
 	local setting = widget:GetUserData("setting")
 	local val = widget:GetChecked()
 	
@@ -56,11 +56,11 @@
 	container:AddChild(chk)
 	
 	local desc = AceGUI:Create("AmrUiLabel")
+	container:AddChild(desc)
 	desc:SetWidth(800)
 	desc:SetText(description)
 	desc:SetFont(Amr.CreateFont("Italic", 12, Amr.Colors.TextTan))
 	desc:SetPoint("TOPLEFT", chk.frame, "BOTTOMLEFT", 24, -3)
-	container:AddChild(desc)
 	
 	return chk, desc
 end
@@ -77,18 +77,18 @@
 	container:AddChild(txt)
 	
 	local lbl = AceGUI:Create("AmrUiLabel")
+	container:AddChild(lbl)
 	lbl:SetWidth(600)
 	lbl:SetText(text)
 	lbl:SetFont(Amr.CreateFont("Regular", 14, Amr.Colors.Text))
 	lbl:SetPoint("LEFT", txt.frame, "RIGHT", 6, 0)
-	container:AddChild(lbl)
 	
 	local desc = AceGUI:Create("AmrUiLabel")
+	container:AddChild(desc)
 	desc:SetWidth(800)
 	desc:SetText(description)
 	desc:SetFont(Amr.CreateFont("Italic", 12, Amr.Colors.TextTan))
 	desc:SetPoint("TOPLEFT", lbl.frame, "BOTTOMLEFT", 0, -4)
-	container:AddChild(desc)
 	
 	return txt, desc
 end
@@ -97,11 +97,11 @@
 function Amr:RenderTabOptions(container)
 
 	local header = AceGUI:Create("AmrUiLabel")
+	container:AddChild(header)
 	header:SetWidth(600)
 	header:SetText(L.OptionsHeaderGeneral)
 	header:SetFont(Amr.CreateFont("Bold", 24, Amr.Colors.TextHeaderActive))
 	header:SetPoint("TOPLEFT", container.content, "TOPLEFT", 12, -40)
-	container:AddChild(header)
 
 	local desc, desc2
 	
@@ -126,6 +126,10 @@
 
 function Amr:ReleaseTabOptions()
 	_chkMinimap = nil
+	_chkAutoGear = nil
+	_chkAh = nil
+	_chkEm = nil
+	_txtScale = nil
 end
 
 function Amr:RefreshOptionsUi()
--- a/Shopping.lua	Mon Feb 12 19:34:09 2018 -0800
+++ b/Shopping.lua	Tue Jul 17 09:57:39 2018 -0700
@@ -59,7 +59,9 @@
 	end
 end
 
+
 function Amr:ShowShopWindow()
+
 	if not _frameShop then
 		_frameShop = AceGUI:Create("AmrUiFrame")
 		_frameShop:SetStatusTable(Amr.db.profile.shopWindow) -- window position is remembered in db
@@ -76,13 +78,13 @@
 		end
 		
 		local lbl = AceGUI:Create("AmrUiLabel")
+		_frameShop:AddChild(lbl)
 		lbl:SetWidth(400)
 		lbl:SetFont(Amr.CreateFont("Bold", 28, Amr.Colors.White))
 		lbl:SetText(L.ShopTitle)
 		lbl:SetWordWrap(false)
 		lbl:SetJustifyH("CENTER")
 		lbl:SetPoint("TOP", _frameShop.content, "TOP", 0, 30)
-		_frameShop:AddChild(lbl)
 		
 		lbl:SetCallback("OnMouseDown", function(widget) _frameShop:StartMove() end)
 		lbl:SetCallback("OnMouseUp", function(widget) _frameShop:EndMove() end)
@@ -90,40 +92,40 @@
 		-- player picker
 		_cboPlayers = AceGUI:Create("AmrUiDropDown")
 		_cboPlayers:SetWidth(400)
+		_frameShop:AddChild(_cboPlayers)
 		_cboPlayers:SetPoint("TOPLEFT", _frameShop.content, "TOPLEFT", 0, -30)
-		_frameShop:AddChild(_cboPlayers)
 		
 		-- spec pickers
 		_chk1 = AceGUI:Create("AmrUiCheckBox")
-		_chk1:SetPoint("TOPLEFT", _cboPlayers.frame, "BOTTOMLEFT", 0, -20)
 		_chk1:SetUserData("spec", 1)
 		_chk1:SetCallback("OnClick", onSpecClick)
 		_frameShop:AddChild(_chk1)
+		_chk1:SetPoint("TOPLEFT", _cboPlayers.frame, "BOTTOMLEFT", 0, -20)
 		
 		_chk2 = AceGUI:Create("AmrUiCheckBox")
-		_chk2:SetPoint("LEFT", _chk1.frame, "RIGHT", 30, 0)
 		_chk2:SetUserData("spec", 2)
 		_chk2:SetCallback("OnClick", onSpecClick)
 		_frameShop:AddChild(_chk2)
+		_chk2:SetPoint("LEFT", _chk1.frame, "RIGHT", 30, 0)
 		
 		_chk3 = AceGUI:Create("AmrUiCheckBox")
-		_chk3:SetPoint("LEFT", _chk2.frame, "RIGHT", 30, 0)
 		_chk3:SetUserData("spec", 3)
 		_chk3:SetCallback("OnClick", onSpecClick)
 		_frameShop:AddChild(_chk3)
+		_chk3:SetPoint("LEFT", _chk2.frame, "RIGHT", 30, 0)
 		
 		_chk4 = AceGUI:Create("AmrUiCheckBox")
-		_chk4:SetPoint("LEFT", _chk3.frame, "RIGHT", 30, 0)
 		_chk4:SetUserData("spec", 4)
 		_chk4:SetCallback("OnClick", onSpecClick)
 		_frameShop:AddChild(_chk4)
+		_chk4:SetPoint("LEFT", _chk3.frame, "RIGHT", 30, 0)
 		
 		_panelContent = AceGUI:Create("AmrUiPanel")
 		_panelContent:SetLayout("None")
 		_panelContent:SetTransparent()
+		_frameShop:AddChild(_panelContent)
 		_panelContent:SetPoint("TOPLEFT", _chk1.frame, "BOTTOMLEFT", 0, -10)
 		_panelContent:SetPoint("BOTTOMRIGHT", _frameShop.content, "BOTTOMRIGHT")
-		_frameShop:AddChild(_panelContent)
 		
 		-- update shopping list data
 		local player = Amr:ExportCharacter()
@@ -168,11 +170,11 @@
 	scroll:AddChild(panel)
 	
 	local lbl = AceGUI:Create("AmrUiLabel")
+	panel:AddChild(lbl)
 	lbl:SetWidth(w)
 	lbl:SetFont(Amr.CreateFont("Regular", 18, Amr.Colors.TextHeaderActive))
 	lbl:SetText(header)
 	lbl:SetPoint("BOTTOMLEFT", panel.content, "BOTTOMLEFT")
-	panel:AddChild(lbl)
 	
 	for itemId, count in pairs(list) do
 		panel = AceGUI:Create("AmrUiPanel")
@@ -183,20 +185,20 @@
 		scroll:AddChild(panel)
 		
 		lbl = AceGUI:Create("AmrUiLabel")
+		panel:AddChild(lbl)
 		lbl:SetWidth(40)
 		lbl:SetWordWrap(false)
 		lbl:SetFont(Amr.CreateFont("Bold", 20, Amr.Colors.White))
 		lbl:SetText(count .. "x")
 		lbl:SetPoint("LEFT", panel.content, "LEFT")
-		panel:AddChild(lbl)
 		
 		local icon = AceGUI:Create("AmrUiIcon")
 		icon:SetBorderWidth(1)
 		icon:SetIconBorderColor(Amr.Colors.White)
 		icon:SetWidth(18)
 		icon:SetHeight(18)
+		panel:AddChild(icon)
 		icon:SetPoint("LEFT", lbl.frame, "RIGHT", 5, 0)
-		panel:AddChild(icon)
 		
 		local btn = AceGUI:Create("AmrUiTextButton")
 		btn:SetWidth(w - 30 - 18 - 15)
@@ -204,9 +206,9 @@
 		btn:SetWordWrap(false)
 		btn:SetFont(Amr.CreateFont("Bold", 14, Amr.Colors.White))
 		btn:SetHoverFont(Amr.CreateFont("Bold", 14, Amr.Colors.White))
-		btn:SetPoint("LEFT", icon.frame, "RIGHT", 5, 0)
 		btn:SetCallback("OnClick", onItemClick)
 		panel:AddChild(btn)
+		btn:SetPoint("LEFT", icon.frame, "RIGHT", 5, 0)
 		
 		Amr.GetItemInfo(itemId, function(obj, name, link, quality, iLevel, reqLevel, class, subclass, maxStack, equipSlot, texture)					
 			-- set icon, name, and a tooltip
@@ -228,9 +230,13 @@
 		ret = ret + list[itemId]
 	end
 	
-	list = Amr.db.char.BankItemsAndCounts
-	if list and list[itemId] then
-		ret = ret + list[itemId]
+	local bankBags = Amr.db.char.BankItemsAndCounts
+	if bankBags then
+		for bagId,bagList in pairs(bankBags) do
+			if bagList[itemId] then
+				ret = ret + bagList[itemId]
+			end
+		end
 	end
 	
 	return ret
@@ -279,11 +285,11 @@
 		_panelContent:SetLayout("None")
 		
 		local lbl = AceGUI:Create("AmrUiLabel")
+		_panelContent:AddChild(lbl)
 		lbl:SetFont(Amr.CreateFont("Italic", 18, Amr.Colors.TextTan))
 		lbl:SetText(L.ShopEmpty)
 		lbl:SetJustifyH("CENTER")
 		lbl:SetPoint("TOP", _panelContent.content, "TOP", 0, -30)
-		_panelContent:AddChild(lbl)
 	else
 		local allStuff = { gems = {}, enchants = {}, materials = {} }
 		local hasStuff = false
@@ -354,7 +360,8 @@
 				
 end
 
--- compare gear to everything the player owns, and return the minimum gems/enchants/materials needed to optimize, grouped by inventory ID so that we can combine multiple specs without double-counting
+-- compare gear to everything the player owns, and return the minimum gems/enchants/materials needed to optimize, 
+-- grouped by inventory ID so that we can combine multiple specs without double-counting
 local function getShoppingData(player, gear, spec)
 
 	local ret = {}
@@ -363,15 +370,14 @@
 	local usedItems = {}
 	
 	for slotId, optimalItem in pairs(gear) do
-		local matchItemLink, matchItem = Amr:FindMatchingItem(optimalItem, player, usedItems)
-		local itemInfo = Amr.db.char.ExtraItemData[spec][optimalItem.id]
+		local matchItem = Amr:FindMatchingItem(optimalItem, player, usedItems)
 		local inventoryId = optimalItem.inventoryId or -1
 		
 		-- find gem/enchant differences on the best-matching item
 		
-		-- gems, but skip artifact relics (will have relicBonusIds set)
-		if not optimalItem.relicBonusIds and itemInfo and itemInfo.socketColors then
-			for i = 1, #itemInfo.socketColors do
+		-- gems
+		if not optimalItem.relicBonusIds and (not matchItem or not matchItem.relicBonusIds) then
+			for i = 1, 3 do
 				local g = optimalItem.gemIds[i]
 				local isGemEquipped = g ~= 0 and matchItem and matchItem.gemIds and matchItem.gemIds[i] == g
 				
@@ -390,15 +396,8 @@
 			local isEnchantEquipped = matchItem and matchItem.enchantId and matchItem.enchantId == e
 			
 			if not isEnchantEquipped then
-				-- enchant info, look in all spec extra info cache
-				local enchInfo = nil
-				for specPos = 1,4 do
-					if Amr.db.char.ExtraEnchantData[specPos] then
-						enchInfo = Amr.db.char.ExtraEnchantData[specPos][e]
-						if enchInfo then break end
-					end
-				end
-
+				-- enchant info
+				local enchInfo = Amr.db.char.ExtraEnchantData[e]
 				if enchInfo then
 					if not ret[inventoryId] then
 						ret[inventoryId] = { gems = {}, enchants = {}, materials = {} }
@@ -428,8 +427,13 @@
 	
 	local enchantItemIdToId = {}
 	
-	for spec, gear in pairs(Amr.db.char.GearSets) do
-		required.stuff[spec] = getShoppingData(player, gear, spec)
+	for i, spec in ipairs(required.specs) do
+		local gear = Amr.db.char.GearSets[spec]
+		if gear then
+			required.stuff[i] = getShoppingData(player, gear, spec)
+		else
+			required.stuff[i] = {}
+		end
 	end
 	
 	Amr.db.global.Shopping[player.Name .. "-" .. player.Realm] = required
--- a/localization/enUS.lua	Mon Feb 12 19:34:09 2018 -0800
+++ b/localization/enUS.lua	Tue Jul 17 09:57:39 2018 -0700
@@ -148,7 +148,6 @@
 L.TabExportText = "Export"
 L.TabGearText = "Gear"
 L.TabLogText = "Combat Logs"
-L.TabTeamText = "Team Optimizer"
 L.TabOptionsText = "Options"
 
 L.VersionChatTitle = "AMR Addon Version:"
@@ -161,15 +160,13 @@
 ------------------------------------------------------------------------]]
 L.ExportTitle = "Export Instructions"
 L.ExportHelp1 = "1. Copy the text below by pressing Ctrl+C (or Cmd+C on a Mac)"
-L.ExportHelp2 = "2. Go to https://www.askmrrobot.com/wow/gear and open the character picker"
-L.ExportHelp3 = "3. Paste into the textbox under the ADDON section"
+L.ExportHelp2 = "2. Go to https://www.askmrrobot.com and open the character picker"
+L.ExportHelp3 = "3. Paste into the textbox under the AMR ADDON section"
 
 L.ExportSplashTitle = "Getting Started"
 L.ExportSplashSubtitle = "This is your first time using the new version of the addon. Do the following things to initialize your item database:"
 L.ExportSplash1 = "1. Activate each of your specs once and equip your latest gear for each spec"
-L.ExportSplash2 = "2. Equip and open your artifact weapon for each spec"
-L.ExportSplash3 = "3. Open your bank and leave it open for at least two seconds"
-L.ExportSplash4 = "4. If you have gear in void storage, open it and leave it open for at least two seconds"
+L.ExportSplash2 = "2. Open your bank and leave it open for at least two seconds"
 L.ExportSplashClose = "Continue"
 
 
@@ -187,7 +184,7 @@
 L.GearEquipErrorCombat = "Cannot change spec/gear while in combat!"
 L.GearEquipErrorEmpty = "No saved gear set could be found for the current spec."
 L.GearEquipErrorNotFound = "An item in your saved gear set could not be equipped."
-L.GearEquipErrorNotFound2 = "Try opening your bank and running this command again, or check your void storage."
+L.GearEquipErrorNotFound2 = "Try opening your bank and running this command again."
 L.GearEquipErrorBagFull = "There is not enough room in your bags to equip your saved gear set."
 L.GearEquipErrorSoulbound = function(itemLink)
 	return itemLink .. " could not be equipped because it is not bound to you."
@@ -291,104 +288,6 @@
 
 
 --[[----------------------------------------------------------------------
-Team Optimizer Tab
-------------------------------------------------------------------------]]
-L.TeamTabLeaderText = "Loot Ranker"
-L.TeamTabMemberText = "Team Member"
-
-L.TeamSplashHeader = "How do you usually use the Team Optimizer?"
-L.TeamSplashLeaderLabel = "I am the person who uses askmrrobot.com to rank the loot for our group"
-L.TeamSplashMemberLabel = "Someone else handles using the Team Optimizer for me"
-
-L.TeamMemberText = "Just sit back and relax, everything is being handled by your team leaders."
-L.TeamMemberShowLootLabel = "Looting is in progress!"
-L.TeamMemberShowLoot = "Show Loot Window"
-
-L.TeamButtonVersionText = "Check for Addon"
-L.TeamButtonExportRosterText = "Export Roster"
-L.TeamButtonExportLootText = "Export Loot"
-L.TeamButtonExportClose = "Close"
-L.TeamButtonImportRankingsText = "Import Rankings"
-L.TeamButtonStartLootText = "Start Looting"
-L.TeamButtonResumeLootText = "Resume Looting"
-
-L.TeamExportVersionLabel = "The Team Optimizer is more accurate and easier to use if everyone in your group has the AskMrRobot addon."
-L.TeamExportRosterLabel = "Do this once at the beginning of your raid to initialize the Team Optimizer on the web. If someone joins or leaves your group during a raid, do it again and press 'Import without Reload' on the web to do a quick update of just the players that changed."
-L.TeamExportLootLabel = "Export all drops from the last looted boss and rank them all at once on the web.  Rankings automatically account for loot won on previous bosses or bonus rolls!"
-L.TeamExportLootLabel2 = "Requires Master Loot"
-L.TeamImportRankingsLabel = "Import ranking data from askmrrobot.com so that you can easily view and distribute loot in-game."
-L.TeamStartLootLabel = function(numItems)
-	return numItems .. " items were imported."
-end
-
-L.TeamHistoryTitle = "Loot History"
-L.TeamHistoryNoGroup = "You are not in a group or raid."
-L.TeamHistoryEmpty = "No loot has been handed out yet."
-
-L.TeamVersionTitle = "Version Check"
-L.TeamVersionNoGroup = "You are not in a group or raid."
-L.TeamVersionGood = "Everyone in your group has the addon!"
-L.TeamVersionMissing = "NOT INSTALLED"
-L.TeamVersionOld = "OUT OF DATE"
-
-L.TeamExportRosterLoading = "Gathering player data, please wait..."
-
-L.TeamAlertNoGroup = "You are not in a group!"
-L.TeamAlertNoLoot = "Nothing has been master-looted recently in your party or raid!"
-
-L.TeamExportHelp = "Press Ctrl+C (or Cmd+C on a Mac) to copy the text below."
-L.TeamExportRosterText = "Then go to the Team Optimizer on the website and paste into the roster import box."
-L.TeamExportLootText = "Then go to the Team Optimizer on the website and paste into the loot import box."
--- note to translators: leave "Team Optimizer" in english in the above two texts because our website is not localized yet
-
-L.TeamImportRankingsHeader = "Press Ctrl+V (Cmd+V on a Mac) to paste data from the website into the box below."
-
-L.TeamLootOptionNeed = "Need"
-L.TeamLootOptionOff = "Off Spec"
-L.TeamLootOptionGreed = "Greed"
-L.TeamLootOptionPass = "Pass"
-L.TeamLootOptionDisenchant = "Disenchant"
-
-
---[[----------------------------------------------------------------------
-Loot Window
-------------------------------------------------------------------------]]
-L.LootTitle = "Loot!"
-L.LootEmpty = "There is no loot to hand out right now!"
-L.LootHelpItems = "For each drop..."
-L.LootHelpRanks = "Choose an option:"
-L.LootHelpMaster = "Then master loot:"
-
-L.LootIneligible = "You are not eligible for this drop."
-
-L.LootRankHeaderRank = "Rank"
-L.LootRankHeaderScore = "Percent Upgrade"
-L.LootRankHeaderScoreDisenchant = "Player"
-
--- note to translators: these column headers should be short, abbreviate if necessary, keep to 5 characters max
-L.LootRankHeaderNeed = "Need"
-L.LootRankHeaderOff = "Off"
-L.LootRankHeaderGreed = "Greed"
-L.LootRankHeaderPass = "Pass"
-L.LootRankHeaderRoll = "Roll"
-
-L.LootRankLabelOff = "off spec"
-L.LootRankLabelDisenchant = "disenchanter"
-L.LootRankLabelMasterLooter = "master looter"
-L.LootRankLabelNoAddon = "addon not running"
-
-L.LootMasterRollText = "Roll"
-L.LootMasterDisenchantText = "Disenchant/Sell"
-
-L.LootMasterRollLabel = "automatic /roll for everyone"
-L.LootMasterDisenchantLabel = "check this if no one wants this item"
-L.LootMasterGiveLoot = "Give Loot"
-L.LootMasterGiveDisenchant = "Give for Disenchant/Sell"
-
-L.LootMasterGiveFail = "Either this item or this master loot candidate could not be found :("
-
-
---[[----------------------------------------------------------------------
 Options Tab
 ------------------------------------------------------------------------]]
 L.OptionsHeaderGeneral = "General Options"
--- a/localization/frFR.lua	Mon Feb 12 19:34:09 2018 -0800
+++ b/localization/frFR.lua	Tue Jul 17 09:57:39 2018 -0700
@@ -1,17 +1,3 @@
---[[-------------------------------------------------------------------------------------------------------------
-Master Localization File (English)
-
-Instructions for Translators:
-1. Copy this entire file into a new file in the same folder, named with your locale, e.g. deDE.lua for German.
-2. At the top, replace "enUS" in the first code line with your locale, and change the next parameter from true to false.
-3. Change all the English strings in your file as appropriate.
-
-Note that a couple of the "strings" are functions that are provided variables.  Feel free to modify these
-functions as necessary to output an appropriately worded statement in your language (but don't change the parameters).  
-If you need assistance with the syntax of any used methods like string.format, please contact Team Robot and we will gladly assist you.
----------------------------------------------------------------------------------------------------------------]]
-
--- replace enUS with your locale
 local L = LibStub("AceLocale-3.0"):NewLocale("AskMrRobot", "frFR", false)
 
 if L then
@@ -138,9 +124,7 @@
 L.MinimapTooltip = 
 [[Clic gauche pour ouvrir la fenêtre Ask Mr. Robot.
 
-Clic droit pour changer de spé et equipper le stuff sauvegardé pour cette spé.
-
-Ctrl + Clic gauche pour marquer un essai comme wipe.]]
+Clic droit pour changer de spé et equipper le stuff sauvegardé pour cette spé.]]
 
 L.MainStatusText = function(version, url)
 	return version .. " chargée. Documentation disponible à " .. url
@@ -149,7 +133,6 @@
 L.TabExportText = "Exporter"
 L.TabGearText = "Stuff"
 L.TabLogText = "Combat Logs"
-L.TabTeamText = "Team Optimizer"
 L.TabOptionsText = "Options"
 
 L.VersionChatTitle = "Version Add-on AMR:"
@@ -162,15 +145,13 @@
 ------------------------------------------------------------------------]]
 L.ExportTitle = "Instructions pour exporter"
 L.ExportHelp1 = "1. Copie le texte ci-dessous en appuyant Ctrl+C (ou Cmd+C sur un Mac)"
-L.ExportHelp2 = "2. Va sur http://www.askmrrobot.com/wow/player et charge ton perso"
-L.ExportHelp3 = "3. Clique le lien \"IMPORT (from addon)\" juste au dessus du nom de ton perso"
-L.ExportHelp4 = "4. Colle le texte dans la zone de texte sur le site et clique sur \"Import!\""
+L.ExportHelp2 = "2. Va sur https://www.askmrrobot.com et charge ton perso"
+L.ExportHelp3 = "3. Paste into the textbox under the AMR ADDON section" -- TODO
 
 L.ExportSplashTitle = "Comment Démarrer"
 L.ExportSplashSubtitle = "S'il s'agit de ta première utilisation de cette nouvelle version de l'add-on, procède comme suit pour initialiser la base de données d'items :"
 L.ExportSplash1 = "1. Active chacune de tes spés une fois et pour chaque spé, équippe le stuff approprié"
 L.ExportSplash2 = "2. Ouvre la fenêtre de ta banque et laisse la ouverte pendant au moins deux secondes"
-L.ExportSplash3 = "3. Si tu as du stuff dans ta banque du vide (void storage), ouvre la fenêtre et laisse la ouverte pendant au moins deux secondes"
 L.ExportSplashClose = "Continuer"
 
 
@@ -188,7 +169,7 @@
 L.GearEquipErrorCombat = "Impossible de changer de spé/stuff pendant un combat !"
 L.GearEquipErrorEmpty = "Pas de stuff sauvegardé pour la spé active."
 L.GearEquipErrorNotFound = "Un item de ton stuff sauvegardé pour la spee n'a pas pu être équippé."
-L.GearEquipErrorNotFound2 = "Essaie d'ouvrir la fenêtre de la banque et de lancer cette commande de nouveau, ou bien vérifie ta banque du vide."
+L.GearEquipErrorNotFound2 = "Essaie d'ouvrir la fenêtre de la banque et de lancer cette commande de nouveau."
 L.GearEquipErrorBagFull = "Pas assez de place dans tes sacs pour équipper ton stuff sauvegardé."
 L.GearEquipErrorSoulbound = function(itemLink)
 	return itemLink .. " n'a pas pu être équippé car il n'est pas lié quand ramassé."
@@ -292,104 +273,6 @@
 
 
 --[[----------------------------------------------------------------------
-Team Optimizer Tab
-------------------------------------------------------------------------]]
-L.TeamTabLeaderText = "Master Loot"
-L.TeamTabMemberText = "Membre du Raid"
-
-L.TeamSplashHeader = "Comment utilises-tu d'habitude le Team Optimizer ?"
-L.TeamSplashLeaderLabel = "Je suis la personne qui utilise askmrrobot.com pour classer et distributer le loot pour notre groupe."
-L.TeamSplashMemberLabel = "Quelqu'un d'autre utilise l'optimiseur de Groupe / Raid pour moi."
-
-L.TeamMemberText = "Tu peux te relaxer, tout est géré par les leaders de ton groupe."
-L.TeamMemberShowLootLabel = "Loot en cours !"
-L.TeamMemberShowLoot = "Voir la fenêtre de loot"
-
-L.TeamButtonVersionText = "Vérifier la version de l'add-on"
-L.TeamButtonExportRosterText = "Exporter Roster"
-L.TeamButtonExportLootText = "Exporter Loot"
-L.TeamButtonExportClose = "Fermer"
-L.TeamButtonImportRankingsText = "Importer Rankings"
-L.TeamButtonStartLootText = "Commencer Loot"
-L.TeamButtonResumeLootText = "Continuer Loot"
-
-L.TeamExportVersionLabel = "Le Team Optimizer est plus précis et plus facile à utiliser si tout le monde dans le groupe utilise l'add-on AskMrRobot."
-L.TeamExportRosterLabel = "Fais ceci une fois au début du raid pour initialiser l'Optimizer sur le site. Si quelqu'un rejoint le groupe ou le quitte, refais cette étape et appuie sur ``Import without Reload'' sur le site pour prendre en compte les joueurs qui ont changé."
-L.TeamExportLootLabel = "Exporte tous les drops du dernier boss looté et les classe tous sur le site. Les classements prennent en compte le loot précédemment gagné ainsi que les bonus rolls !"
-L.TeamExportLootLabel2 = "Requiert Master Loot"
-L.TeamImportRankingsLabel = "Importe les données de classement depuis askmrrobot.com, afin que tu puisses facilement voir comment distribuer le loot."
-L.TeamStartLootLabel = function(numItems)
-	return numItems .. " items importés."
-end
-
-L.TeamHistoryTitle = "Historique de loot"
-L.TeamHistoryNoGroup = "Tu n'es pas dans un groupe ou un raid."
-L.TeamHistoryEmpty = "L'add-on n'a pas encore enregistré de distribution de loot."
-
-L.TeamVersionTitle = "Version Check"
-L.TeamVersionNoGroup = "Tu n'es pas dans un groupe ou un raid."
-L.TeamVersionGood = "Tout le monde dans ton groupe a l'add-on !"
-L.TeamVersionMissing = "PAS INSTALLE"
-L.TeamVersionOld = "PAS A JOUR"
-
-L.TeamExportRosterLoading = "En train d'obtenir les données des joueurs..."
-
-L.TeamAlertNoGroup = "Tu n'es pas dans un groupe !"
-L.TeamAlertNoLoot = "Il n'y a pas eu de loot distribué par master-loot recémment dans ton groupe ou raid !"
-
-L.TeamExportHelp = "Appuie sur Ctrl+C (ou Cmd+C sur un Mac) pour copier le texte ci-dessous."
-L.TeamExportRosterText = "Ensuite va dans le Team Optimizer sur le site et colle le texte dans la zone de texte pour importer le roster."
-L.TeamExportLootText = "Ensuite va dans le Team Optimizer sur le site et colle le texte dans la zone de texte pour importer le loot."
--- note to translators: leave "Team Optimizer" in english in the above two texts because our website is not localized yet
-
-L.TeamImportRankingsHeader = "Appuie sur Ctrl+V (ou Cmd+V sur un Mac) pour coller les données du site dans la zone de texte ci-dessous."
-
-L.TeamLootOptionNeed = "Need"
-L.TeamLootOptionOff = "Off Spé"
-L.TeamLootOptionGreed = "Greed"
-L.TeamLootOptionPass = "Pass"
-L.TeamLootOptionDisenchant = "Désenchanter"
-
-
---[[----------------------------------------------------------------------
-Loot Window
-------------------------------------------------------------------------]]
-L.LootTitle = "Loot!"
-L.LootEmpty = "Il n'y a pas de loot à distribuer actuellement !"
-L.LootHelpItems = "Pour chaque drop..."
-L.LootHelpRanks = "Choisis une option :"
-L.LootHelpMaster = "Puis master loot :"
-
-L.LootIneligible = "Tu n'es pas éligible pour ce drop."
-
-L.LootRankHeaderRank = "Classement"
-L.LootRankHeaderScore = "Pourcentage d'upgrade"
-L.LootRankHeaderScoreDisenchant = "Joueur"
-
--- note to translators: these column headers should be short, abbreviate if necessary, keep to 5 characters max
-L.LootRankHeaderNeed = "Need"
-L.LootRankHeaderOff = "Off"
-L.LootRankHeaderGreed = "Greed"
-L.LootRankHeaderPass = "Pass"
-L.LootRankHeaderRoll = "Roll"
-
-L.LootRankLabelOff = "off spé"
-L.LootRankLabelDisenchant = "désenchanteur"
-L.LootRankLabelMasterLooter = "master looter"
-L.LootRankLabelNoAddon = "add-on pas activé"
-
-L.LootMasterRollText = "Roll"
-L.LootMasterDisenchantText = "Désenchanter/Vendre"
-
-L.LootMasterRollLabel = "/roll automatique pour tout le monde"
-L.LootMasterDisenchantLabel = "coche si personne ne veut de l'item"
-L.LootMasterGiveLoot = "Donner Loot"
-L.LootMasterGiveDisenchant = "Donner pour désenchanter/vendre"
-
-L.LootMasterGiveFail = "Soit cet item, soit le joueur à qui il aurait du être donné n'a pas été trouvé."
-
-
---[[----------------------------------------------------------------------
 Options Tab
 ------------------------------------------------------------------------]]
 L.OptionsHeaderGeneral = "Options Générales"
@@ -403,6 +286,7 @@
 L.OptionsShopAhName = "Voir la shopping list automatiquement à l'hôtel des ventes"
 L.OptionsShopAhDesc = "Quand tu ouvres l'hôtel des ventes, la fenêtre de la shopping list s'ouvre automatiquement. Tu peux cliquer sur un item dans la shopping list pour le chercher automatiquement dans l'hôtel des ventes."
 
+-- TODO
 L.OptionsUiScaleName = "Ask Mr. Robot UI scale"
 L.OptionsUiScaleDesc = "Enter a value between 0.5 and 1.5 to change the scale of the Ask Mr. Robot user interface, press Enter, then close/open the window for it take effect. If the positioning gets messed up, use the /amr reset command."
 
--- a/localization/itIT.lua	Mon Feb 12 19:34:09 2018 -0800
+++ b/localization/itIT.lua	Tue Jul 17 09:57:39 2018 -0700
@@ -1,17 +1,3 @@
---[[-------------------------------------------------------------------------------------------------------------
-Master Localization File (English)
-
-Instructions for Translators:
-1. Copy this entire file into a new file in the same folder, named with your locale, e.g. deDE.lua for German.
-2. At the top, replace "enUS" in the first code line with your locale.
-3. Change all the English strings in your file as appropriate.
-
-Note that a couple of the "strings" are functions that are provided variables.  Feel free to modify these
-functions as necessary to output an appropriately worded statement in your language (but don't change the parameters).  
-If you need assistance with the syntax of any used methods like string.format, please contact Team Robot and we will gladly assist you.
----------------------------------------------------------------------------------------------------------------]]
-
--- replace enUS with your locale
 local L = LibStub("AceLocale-3.0"):NewLocale("AskMrRobot", "itIT", false)
 
 if L then
@@ -138,9 +124,7 @@
 L.MinimapTooltip = 
 [[Clic Sinistro per aprire l'interfaccia di Ask Mr Robot.
 
-Tasto Destro per cambiare spec ed equip collegato.
-
-Ctrl + Clic Sinistro per segnare il wipe.]]
+Tasto Destro per cambiare spec ed equip collegato.]]
 
 L.MainStatusText = function(version, url)
 	return version .. " loaded. Documentazione disponibile su " .. url
@@ -149,7 +133,6 @@
 L.TabExportText = "Esporta"
 L.TabGearText = "Equip"
 L.TabLogText = "Combat Logs"
-L.TabTeamText = "Ottimizzatore Raid"
 L.TabOptionsText = "Opzioni"
 
 L.VersionChatTitle = "Versione Addon AMR:"
@@ -162,15 +145,13 @@
 ------------------------------------------------------------------------]]
 L.ExportTitle = "Istruzioni di Esportazione"
 L.ExportHelp1 = "1. Copia il testo qui sotto premendo Ctrl+C (o Cmd+C su un Mac)"
-L.ExportHelp2 = "2. Vai su http://www.askmrrobot.com/wow/player e carica il tuo personaggio"
-L.ExportHelp3 = "3. Premi la scritta verde IMPORT (from addon) subito sopra il nome del tuo personaggio"
-L.ExportHelp4 = "4. Incolla all'interno dell'area di testoe premi il tasto Import!"
+L.ExportHelp2 = "2. Vai su https://www.askmrrobot.com e carica il tuo personaggio"
+L.ExportHelp3 = "3. Paste into the textbox under the AMR ADDON section" -- TODO
 
 L.ExportSplashTitle = "Per Cominciare"
 L.ExportSplashSubtitle = "Questa è la tua prima volta con la nuova versione dell'addon. Esegui queste operazioni per creare il Database del tuo equipaggiamento:"
 L.ExportSplash1 = "1. Attiva Ognuna delle tue spec ed equipaggia gli oggetti migliori che hai"
 L.ExportSplash2 = "2. Apri la tua banca e lasciala aperta almeno per 2 secondi"
-L.ExportSplash3 = "3. Se hai dell'equipaggiamento nella Banca Eterea, Aprila e lasciala aperta per almeno due secondi"
 L.ExportSplashClose = "Continua"
 
 
@@ -292,104 +273,6 @@
 
 
 --[[----------------------------------------------------------------------
-Team Optimizer Tab
-------------------------------------------------------------------------]]
-L.TeamTabLeaderText = "Loot Ranker"
-L.TeamTabMemberText = "Membro"
-
-L.TeamSplashHeader = "Come uso di solito l'ottimizzatore di Incursione?"
-L.TeamSplashLeaderLabel = "Sono la persona che usa askmrrobot.com per determinare il miglior utilizzatore per il bottino"
-L.TeamSplashMemberLabel = "Qualcun'altro utilizza l'ottimizzatore per me"
-
-L.TeamMemberText = "Rilassati, tutto è gestito dal master looter."
-L.TeamMemberShowLootLabel = "Looting in progress!"
-L.TeamMemberShowLoot = "Mostra finestra Bottino"
-
-L.TeamButtonVersionText = "Verifica L'addon"
-L.TeamButtonExportRosterText = "Esporta Lista"
-L.TeamButtonExportLootText = "Esporta Bottino"
-L.TeamButtonExportClose = "Chiudi"
-L.TeamButtonImportRankingsText = "Importa Punteggi"
-L.TeamButtonStartLootText = "Inizia Predazione"
-L.TeamButtonResumeLootText = "Continua Predazione"
-
-L.TeamExportVersionLabel = "L'ottimizzatore di Incursione è più accurato e facile da utilizzare se tutti nel tuo gruppo hanno installato l'addon di AskMrRobot."
-L.TeamExportRosterLabel = "Fallo una volta all'inizio del raid per inizializzare l'addon sul web. Se qualcuno entra od esce dal tuo gruppo durante un'incursione, fallo di nuovo e premi 'Import without Reload' sul web per effettuare un aggiornamento rapido dei giocatori che sono cambiati."
-L.TeamExportLootLabel = "Esporta tutti i drop dell'ultimo boss lootato ed inseriscili nella graduatoria sul web.  L'inserimento tiene conto automaticamente dei loot vinti dai boss precedenti o con un tiro bonus!"
-L.TeamExportLootLabel2 = "Richiedere Master Loot"
-L.TeamImportRankingsLabel = "Importa i punteggi da askmrrobot.com così da semplificarti la distribuzione del bottino in gioco."
-L.TeamStartLootLabel = function(numItems)
-	return numItems .. " oggetti sono stati importati."
-end
-
-L.TeamHistoryTitle = "Loot History"
-L.TeamHistoryNoGroup = "Non sei in gruppo o in incursione."
-L.TeamHistoryEmpty = "Nessun loot è ancora stato assegnato."
-
-L.TeamVersionTitle = "Controlla Versione"
-L.TeamVersionNoGroup = "Non sei in gruppo o in incursione."
-L.TeamVersionGood = "Tutti nel tuo gruppo hanno l'addon!"
-L.TeamVersionMissing = "NON INSTALLATO"
-L.TeamVersionOld = "VERSIONE OBSOLETA"
-
-L.TeamExportRosterLoading = "Gathering player data, please wait..."
-
-L.TeamAlertNoGroup = "Non sei in un gruppo!"
-L.TeamAlertNoLoot = "Non è stato lootato niente recentemente nel tuo gruppo o incursione!"
-
-L.TeamExportHelp = "Premi Ctrl+C (o Cmd+C su un Mac) per copiatre il testo seguente."
-L.TeamExportRosterText = "Quindi vai sul Team Optimizer sul sito ed incolla all'interno della roster import box."
-L.TeamExportLootText = "Quindi vai sul Team Optimizer sul sito ed incolla all'interno della loot import box."
--- note to translators: leave "Team Optimizer" in english in the above two texts because our website is not localized yet
-
-L.TeamImportRankingsHeader = "Premi Ctrl+V (Cmd+V su un Mac) per incollare i dati nella textbox."
-
-L.TeamLootOptionNeed = "Need"
-L.TeamLootOptionOff = "Off Spec"
-L.TeamLootOptionGreed = "Greed"
-L.TeamLootOptionPass = "Pass"
-L.TeamLootOptionDisenchant = "Disenchant"
-
-
---[[----------------------------------------------------------------------
-Loot Window
-------------------------------------------------------------------------]]
-L.LootTitle = "Loot!"
-L.LootEmpty = " Non c'è nessun loot da gestire!"
-L.LootHelpItems = "Per ogni drop..."
-L.LootHelpRanks = "Scegli un opzione:"
-L.LootHelpMaster = "Quindi master loota:"
-
-L.LootIneligible = "Non sei eleggibile per questo Loot."
-
-L.LootRankHeaderRank = "Rank"
-L.LootRankHeaderScore = "Percentuale di Upgrade"
-L.LootRankHeaderScoreDisenchant = "Giocatore"
-
--- note to translators: these column headers should be short, abbreviate if necessary, keep to 5 characters max
-L.LootRankHeaderNeed = "Need"
-L.LootRankHeaderOff = "Off"
-L.LootRankHeaderGreed = "Greed"
-L.LootRankHeaderPass = "Pass"
-L.LootRankHeaderRoll = "Roll"
-
-L.LootRankLabelOff = "off spec"
-L.LootRankLabelDisenchant = "disenchanter"
-L.LootRankLabelMasterLooter = "master looter"
-L.LootRankLabelNoAddon = "addon non funzionante"
-
-L.LootMasterRollText = "Roll"
-L.LootMasterDisenchantText = "Disenchant/Sell"
-
-L.LootMasterRollLabel = "/roll automatico per tutti"
-L.LootMasterDisenchantLabel = "Clicca qui se nessuno vuole il loot"
-L.LootMasterGiveLoot = "Dai Loot"
-L.LootMasterGiveDisenchant = "Dai loot per Disenchant/Sell"
-
-L.LootMasterGiveFail = "Questo item o il vincitore non possono esser trovati :("
-
-
---[[----------------------------------------------------------------------
 Options Tab
 ------------------------------------------------------------------------]]
 L.OptionsHeaderGeneral = "General Options"
--- a/localization/ptBR.lua	Mon Feb 12 19:34:09 2018 -0800
+++ b/localization/ptBR.lua	Tue Jul 17 09:57:39 2018 -0700
@@ -1,17 +1,3 @@
---[[-------------------------------------------------------------------------------------------------------------
-Master Localization File (English)
-
-Instructions for Translators:
-1. Copy this entire file into a new file in the same folder, named with your locale, e.g. deDE.lua for German.
-2. At the top, replace "enUS" in the first code line with your locale, and change the next parameter from true to false.
-3. Change all the English strings in your file as appropriate.
-
-Note that a couple of the "strings" are functions that are provided variables.  Feel free to modify these
-functions as necessary to output an appropriately worded statement in your language (but don't change the parameters).  
-If you need assistance with the syntax of any used methods like string.format, please contact Team Robot and we will gladly assist you.
----------------------------------------------------------------------------------------------------------------]]
-
--- replace enUS with your locale
 local L = LibStub("AceLocale-3.0"):NewLocale("AskMrRobot", "ptBR", false)
 
 if L then
@@ -148,7 +134,6 @@
 L.TabExportText = "Exportar"
 L.TabGearText = "Equipamento"
 L.TabLogText = "Logs"
-L.TabTeamText = "Otimizador de Equipe"
 L.TabOptionsText = "Opções"
 
 L.VersionChatTitle = "Versão do Addon AMR:"
@@ -161,15 +146,13 @@
 ------------------------------------------------------------------------]]
 L.ExportTitle = "Instruções de Exportação"
 L.ExportHelp1 = "1. Copie o texto abaixo pressionando Ctrl+C (ou Cmd+C em um Mac)"
-L.ExportHelp2 = "2. Vá para https://www.askmrrobot.com/wow/gear e clique no seletor de personagem"
+L.ExportHelp2 = "2. Vá para https://www.askmrrobot.com e clique no seletor de personagem"
 L.ExportHelp3 = "3. Cole o texto copiado na caixa de texto da seção ADDON"
 
 L.ExportSplashTitle = "Começando"
 L.ExportSplashSubtitle = "Esta é a primeira vez que você usa a nova versão do addon. Faça o seguinte para inicializar o banco de dados dos seus itens:"
 L.ExportSplash1 = "1. Ative cada um dos seus specs uma vez e equipe seu equipamento atual para cada spec"
-L.ExportSplash2 = "2. Equipe sua arma de artefato para cada spec e abra a tela de artefato"
-L.ExportSplash3 = "3. Abra seu banco e deixe-o aberto por pelo menos dois segundos"
-L.ExportSplash4 = "4. Se você possuir equipamento no cofre etéreo, abra-o e deixe-o aberto por pelo menos dois segundos"
+L.ExportSplash2 = "2. Abra seu banco e deixe-o aberto por pelo menos dois segundos"
 L.ExportSplashClose = "Continuar"
 
 
@@ -291,104 +274,6 @@
 
 
 --[[----------------------------------------------------------------------
-Team Optimizer Tab
-------------------------------------------------------------------------]]
-L.TeamTabLeaderText = "Rankeador de Saque"
-L.TeamTabMemberText = "Membro do Grupo"
-
-L.TeamSplashHeader = "Como você costuma usar o Otimizador de Grupo?"
-L.TeamSplashLeaderLabel = "Eu sou a pessoa que usa o askmrrobot.com para classificar o saque para nosso grupo"
-L.TeamSplashMemberLabel = "Outra pessoa usa o Otimizador de Grupo pra mim"
-
-L.TeamMemberText = "Pegue a pipoca e relaxe, tudo está sendo feito pelos líderes do seu grupo."
-L.TeamMemberShowLootLabel = "Saque em progresso!"
-L.TeamMemberShowLoot = "Mostrar Janela de Saque"
-
-L.TeamButtonVersionText = "Verificar o Addon"
-L.TeamButtonExportRosterText = "Exportar Lista"
-L.TeamButtonExportLootText = "Exportar Saque"
-L.TeamButtonExportClose = "Fechar"
-L.TeamButtonImportRankingsText = "Importar Rankings"
-L.TeamButtonStartLootText = "Iniciar Saque"
-L.TeamButtonResumeLootText = "Resumir Saque"
-
-L.TeamExportVersionLabel = "O Otimizador de Grupo é mais preciso e fácil de usar se todos no seu grupo possuirem o addon do AskMrRobot."
-L.TeamExportRosterLabel = "Faça isso uma vez no início da sua raid para inicializar o Otimizador de Grupo na internet. Se alguém entrar ou sair do seu grupo durante uma raid, aperte novamente 'Import without Reload' na internet para fazer uma atualização rápida apenas desses jogadores."
-L.TeamExportLootLabel = "Exporta todos os espólios do último chefe saqueado e cria um ranking de todos de uma só vez na internet. Os Rankings automaticamente contam como saque vencido nos chefes anteriores ou nos saques extra!"
-L.TeamExportLootLabel2 = "Requer Mestre de Saque"
-L.TeamImportRankingsLabel = "Importar dados de ranking de askmrrobot.com, assim você pode facilmente ver e distribuir o saque no jogo."
-L.TeamStartLootLabel = function(numItems)
-	return numItems .. " itens foram importados."
-end
-
-L.TeamHistoryTitle = "Histórico de Saque"
-L.TeamHistoryNoGroup = "Você não está em um grupo ou raid."
-L.TeamHistoryEmpty = "Nenhum saque foi distribuído ainda."
-
-L.TeamVersionTitle = "Verificação de Versão"
-L.TeamVersionNoGroup = "Você não está em um grupo ou raid.."
-L.TeamVersionGood = "Todos no seu grupo possuem o addon!"
-L.TeamVersionMissing = "NÃO INSTALADO"
-L.TeamVersionOld = "DESATUALIZADO"
-
-L.TeamExportRosterLoading = "Coletando dados dos jogadores, aguarde..."
-
-L.TeamAlertNoGroup = "Você não está em um grupo!"
-L.TeamAlertNoLoot = "Nada foi distribuído via mestre de saque recentemente em seu grupo ou raid!"
-
-L.TeamExportHelp = "Aperte Ctrl+C (ou Cmd+C em um Mac) para copiar o texto abaixo."
-L.TeamExportRosterText = "Em seguida vá para o Team Optimizer no website e cole na caixa de importação das listagens."
-L.TeamExportLootText = "Em seguida vá para o Team Optimizer no website e cole na caixa de importação de saque."
--- note to translators: leave "Team Optimizer" in english in the above two texts because our website is not localized yet
-
-L.TeamImportRankingsHeader = "Aperte Ctrl+V (Cmd+V em um Mac) para colar os dados do website na caixa abaixo."
-
-L.TeamLootOptionNeed = "Necessidade"
-L.TeamLootOptionOff = "Off Spec"
-L.TeamLootOptionGreed = "Ganância"
-L.TeamLootOptionPass = "Passar"
-L.TeamLootOptionDisenchant = "Desencantar"
-
-
---[[----------------------------------------------------------------------
-Loot Window
-------------------------------------------------------------------------]]
-L.LootTitle = "Saque!"
-L.LootEmpty = "Não há saque para distribuir agora!"
-L.LootHelpItems = "Para cada saque..."
-L.LootHelpRanks = "Escolha uma opção:"
-L.LootHelpMaster = "Então mestre de saque:"
-
-L.LootIneligible = "Você não é elegível para este saque."
-
-L.LootRankHeaderRank = "Rank"
-L.LootRankHeaderScore = "Porcentagem de Upgrade"
-L.LootRankHeaderScoreDisenchant = "Jogador"
-
--- note to translators: these column headers should be short, abbreviate if necessary, keep to 5 characters max
-L.LootRankHeaderNeed = "Need"
-L.LootRankHeaderOff = "Off"
-L.LootRankHeaderGreed = "Greed"
-L.LootRankHeaderPass = "Pass"
-L.LootRankHeaderRoll = "Roll"
-
-L.LootRankLabelOff = "spec secundária"
-L.LootRankLabelDisenchant = "desencantador"
-L.LootRankLabelMasterLooter = "mestre de saque"
-L.LootRankLabelNoAddon = "addon não está executando"
-
-L.LootMasterRollText = "Rolar"
-L.LootMasterDisenchantText = "Desencantar/Vender"
-
-L.LootMasterRollLabel = "/roll automático para todos"
-L.LootMasterDisenchantLabel = "marque isso se ninguém deseja este item"
-L.LootMasterGiveLoot = "Entregar Saque"
-L.LootMasterGiveDisenchant = "Entregar para Desencantar/Vender"
-
-L.LootMasterGiveFail = "Ambos o item ou o candidato ao saque não puderam ser encontrados :("
-
-
---[[----------------------------------------------------------------------
 Options Tab
 ------------------------------------------------------------------------]]
 L.OptionsHeaderGeneral = "Opções Gerais"
--- a/localization/ruRU.lua	Mon Feb 12 19:34:09 2018 -0800
+++ b/localization/ruRU.lua	Tue Jul 17 09:57:39 2018 -0700
@@ -1,17 +1,3 @@
---[[-------------------------------------------------------------------------------------------------------------
-Master Localization File (English)
-
-Instructions for Translators:
-1. Copy this entire file into a new file in the same folder, named with your locale, e.g. deDE.lua for German.
-2. At the top, replace "enUS" in the first code line with your locale, and change the next parameter from true to false.
-3. Change all the English strings in your file as appropriate.
-
-Note that a couple of the "strings" are functions that are provided variables.  Feel free to modify these
-functions as necessary to output an appropriately worded statement in your language (but don't change the parameters).  
-If you need assistance with the syntax of any used methods like string.format, please contact Team Robot and we will gladly assist you.
----------------------------------------------------------------------------------------------------------------]]
-
--- replace enUS with your locale
 local L = LibStub("AceLocale-3.0"):NewLocale("AskMrRobot", "ruRU", false)
 
 if L then
@@ -139,9 +125,7 @@
 L.MinimapTooltip = 
 [[Левый клик для открытия окна Ask Mr. Robot.
 
-Правый клик для смены спека и экипирования актуальных предметов.
-
-Ctrl + ЛКМ отметить бой как вайп.]]
+Правый клик для смены спека и экипирования актуальных предметов.]]
 
 L.MainStatusText = function(version, url)
 	return version .. " загружен. Инструкции доступны на " .. url
@@ -150,7 +134,6 @@
 L.TabExportText = "Экспорт"
 L.TabGearText = "Экипировка"
 L.TabLogText = "Логи"
-L.TabTeamText = "Оптимизатор"
 L.TabOptionsText = "Опции"
 
 L.VersionChatTitle = "AMR Addon Version:"
@@ -163,7 +146,7 @@
 ------------------------------------------------------------------------]]
 L.ExportTitle = "Инструкции Экспорта"
 L.ExportHelp1 = "1. Скопируйте текст ниже нажав Ctrl+C (или Cmd+C на Mac)"
-L.ExportHelp2 = "2. Перейдите на https://www.askmrrobot.com/wow/gear и выберете своего персонажа"
+L.ExportHelp2 = "2. Перейдите на https://www.askmrrobot.com и выберете своего персонажа"
 L.ExportHelp3 = "3. Вставьте это в тектовое поле в разделее ADDON"
 
 L.ExportSplashTitle = "Начало работы"
@@ -293,104 +276,6 @@
 
 
 --[[----------------------------------------------------------------------
-Team Optimizer Tab
-------------------------------------------------------------------------]]
-L.TeamTabLeaderText = "Распределить добычу"
-L.TeamTabMemberText = "Команда"
-
-L.TeamSplashHeader = "Как вы планируете использовать Team Optimizer?"
-L.TeamSplashLeaderLabel = "Я тот, кто использует AskMrRobot для распределения добычи в рейде"
-L.TeamSplashMemberLabel = "Пускай другие используют Team Optimizer за меня"
-
-L.TeamMemberText = "Всё, бобер, выдыхай. Просто сядь и расслабься, все будет сделано Вашим лидером."
-L.TeamMemberShowLootLabel = "Распределние добычи в процессе!"
-L.TeamMemberShowLoot = "Показать окно добычи"
-
-L.TeamButtonVersionText = "Наличии аддона"
-L.TeamButtonExportRosterText = "Экспорт ростера"
-L.TeamButtonExportLootText = "Экспорт добычи"
-L.TeamButtonExportClose = "Закрыть"
-L.TeamButtonImportRankingsText = "Импорт Ранкинга"
-L.TeamButtonStartLootText = "Начать распределение"
-L.TeamButtonResumeLootText = "Продолжить распределять"
-
-L.TeamExportVersionLabel = "Team Optimizer является более точным и простыв в использовании если у всех в группе есть аддон AskMrRobot."
-L.TeamExportRosterLabel = "Сделайте это один раз в начале рейда для инициализации ростера в Team Optimizer на сайте. Если кто-то присоединяется или уходит из рейда во время РТ, сделайте тоже самое, только нажмите 'Import without Reload' на сайте, для быстрого обновления игроков который поменялись."
-L.TeamExportLootLabel = "Экспорт всего лута с последнего убитого босса на сайт AskMrRobot для дальнейшей работы с ним. Rankings automatically account for loot won on previous bosses or bonus rolls!"
-L.TeamExportLootLabel2 = "Требуется Мастер Лут"
-L.TeamImportRankingsLabel = "Импорт данных лута с сайта askmrrobot.com чтобы вы могли просматривать и легко распределять добычу в рейде."
-L.TeamStartLootLabel = function(numItems)
-	return numItems .. " предметов было импортировано."
-end
-
-L.TeamHistoryTitle = "История добычи"
-L.TeamHistoryNoGroup = "Вы находитесь вне группы или рейда."
-L.TeamHistoryEmpty = "Добыча ещё не была распределена."
-
-L.TeamVersionTitle = "Проверка версий"
-L.TeamVersionNoGroup = "Вы находитесь вне группы или рейда."
-L.TeamVersionGood = "У всех участников группы/рейда установлен аддон!"
-L.TeamVersionMissing = "НЕ УСТАНОВЛЕН"
-L.TeamVersionOld = "УСТАРЕВШИЙ"
-
-L.TeamExportRosterLoading = "Собираем Ваших рейдеров по крупице, пожалуйста подождите..."
-
-L.TeamAlertNoGroup = "Вы находитесь без группы!"
-L.TeamAlertNoLoot = "В последнее время с помощью мастер лута нечего не было распределено!"
-
-L.TeamExportHelp = "Нажмите Ctrl+C (или Cmd+C на Mac) чтобы скопировать текст ниже."
-L.TeamExportRosterText = "Далее идете на сайт, вкладка Team Optimizer, и вставляете в поле во вкладке Roster."
-L.TeamExportLootText = "Далее переходите во вкладке Team Optimizer на Rank Items и вставляете так же туда Лут."
--- note to translators: leave "Team Optimizer" in english in the above two texts because our website is not localized yet
-
-L.TeamImportRankingsHeader = "Нажмите Ctrl+V (Cmd+V на Mac) чтобы вставить данные с сайта в поле ниже."
-
-L.TeamLootOptionNeed = "Нужно"
-L.TeamLootOptionOff = "Офф спек"
-L.TeamLootOptionGreed = "Продать"
-L.TeamLootOptionPass = "Пропуск"
-L.TeamLootOptionDisenchant = "Распылить"
-
-
---[[----------------------------------------------------------------------
-Loot Window
-------------------------------------------------------------------------]]
-L.LootTitle = "Лут!"
-L.LootEmpty = "Здесь нет лута для раздачи прямо сейчас!"
-L.LootHelpItems = "For each drop..."
-L.LootHelpRanks = "Выберите опцию:"
-L.LootHelpMaster = "Мастер лут:"
-
-L.LootIneligible = "У Вас нет прав на этот предмет."
-
-L.LootRankHeaderRank = "Ранг"
-L.LootRankHeaderScore = "Процент улучшения"
-L.LootRankHeaderScoreDisenchant = "Игрок"
-
--- note to translators: these column headers should be short, abbreviate if necessary, keep to 5 characters max
-L.LootRankHeaderNeed = "Нужно"
-L.LootRankHeaderOff = "Офф"
-L.LootRankHeaderGreed = "Продать"
-L.LootRankHeaderPass = "Пас"
-L.LootRankHeaderRoll = "Ролл"
-
-L.LootRankLabelOff = "офф спек"
-L.LootRankLabelDisenchant = "дизинчантер"
-L.LootRankLabelMasterLooter = "Мастер лутер"
-L.LootRankLabelNoAddon = "АДДОН НЕ УСТАНОВЛЕН"
-
-L.LootMasterRollText = "Ролл"
-L.LootMasterDisenchantText = "Распылить/Продать"
-
-L.LootMasterRollLabel = "автоматически делает /roll за всех"
-L.LootMasterDisenchantLabel = "выберете это, если предмет никому не нужен"
-L.LootMasterGiveLoot = "Отдать"
-L.LootMasterGiveDisenchant = "Отдать на Распыл/Продажу"
-
-L.LootMasterGiveFail = "Этот предмет или выбранный игрок не найден :("
-
-
---[[----------------------------------------------------------------------
 Options Tab
 ------------------------------------------------------------------------]]
 L.OptionsHeaderGeneral = "Основные настройки"
--- a/ui/AmrUiCheckBox.lua	Mon Feb 12 19:34:09 2018 -0800
+++ b/ui/AmrUiCheckBox.lua	Tue Jul 17 09:57:39 2018 -0700
@@ -20,6 +20,8 @@
 -------------------------------------------------------------------------------]]
 local function buttonOnClick(frame, ...)
 	AceGUI:ClearFocus()
+	frame.obj:SetChecked(not frame.obj.isChecked)
+	
 	--PlaySound("igMainMenuOption")
 	frame.obj:Fire("OnClick", ...)
 end
@@ -37,6 +39,10 @@
 		self.frame:ClearAllPoints()
 	end,
 
+	--["OnRelease"] = function(self)
+	--	print(self.name .. " released")
+	--end,
+
 	["SetText"] = function(self, text)
 		self.label:SetText(text)
 		self.frame:SetWidth(16 + 6 + self.label:GetStringWidth())
@@ -47,12 +53,18 @@
 	end,
 	
 	["SetChecked"] = function(self, checked)
-		-- not sure if WoW expects boolean type or not, too lazy to find out so just cast it
-		self.frame:SetChecked(not not checked)
+		self.isChecked = not not checked
+		if checked then
+			self.texNormal:Hide()
+			self.texCheck:Show()
+		else
+			self.texCheck:Hide()
+			self.texNormal:Show()
+		end
 	end,
 	
-	["GetChecked"] = function(self)
-		return self.frame:GetChecked()
+	["GetChecked"] = function(self)		
+		return self.isChecked
 	end,
 	
 	["GetWidth"] = function(self)
@@ -82,7 +94,7 @@
 -------------------------------------------------------------------------------]]
 local function Constructor()
 	local name = "AmrUiCheckBox" .. AceGUI:GetNextWidgetNum(Type)
-	local frame = CreateFrame("CheckButton", name, UIParent)
+	local frame = CreateFrame("Button", nil, UIParent)
 	frame:SetHeight(16)
 	frame:SetPushedTextOffset(0, 0)
 	frame:Hide()
@@ -92,29 +104,34 @@
 	
 	-- unchecked texture
 	local texNormal = frame:CreateTexture(nil, "BACKGROUND")
+	texNormal.name = name
 	texNormal:SetWidth(16)
 	texNormal:SetHeight(16)
 	texNormal:SetTexture("Interface\\AddOns\\" .. Amr.ADDON_NAME .. "\\Media\\check-off")
 	texNormal:SetPoint("LEFT", frame, "LEFT")
-	frame:SetNormalTexture(texNormal)
 	
 	-- checked texture
-	local texCheck = frame:CreateTexture(nil, "BORDER")
+	local texCheck = frame:CreateTexture(nil, "BACKGROUND")
+	texCheck.name = name
+	texCheck:SetWidth(16)
+	texCheck:SetHeight(16)
 	texCheck:SetTexture("Interface\\AddOns\\" .. Amr.ADDON_NAME .. "\\Media\\check-on")
 	texCheck:SetPoint("LEFT", frame, "LEFT")
-	frame:SetCheckedTexture(texCheck)
+	texCheck:Hide()
 	
 	-- label
-	local lbl = frame:CreateFontString(nil, "ARTWORK")
+	local lbl = frame:CreateFontString(nil, "BACKGROUND")
 	lbl:SetJustifyV("MIDDLE")
 	lbl:SetPoint("LEFT", texNormal, "RIGHT", 8, 0)
-	frame:SetFontString(lbl)
 
 	local widget = {
 		texNormal = texNormal,
+		texCheck  = texCheck,
 		label     = lbl,
 		frame     = frame,
-		type      = Type
+		type      = Type,
+		isChecked = false,
+		name      = name
 	}
 	for method, func in pairs(methods) do
 		widget[method] = func
--- a/ui/AmrUiLabel.lua	Mon Feb 12 19:34:09 2018 -0800
+++ b/ui/AmrUiLabel.lua	Tue Jul 17 09:57:39 2018 -0700
@@ -76,7 +76,7 @@
 		-- reset the flag
 		self.resizing = nil
 		-- run the update explicitly
-		updateSize(self)
+		--updateSize(self)
 	end,
 	
 	-- ["OnRelease"] = nil,
@@ -134,6 +134,7 @@
 	frame:SetScript("OnMouseUp", frameOnMouseUp)
 
 	local label = frame:CreateFontString(nil, "ARTWORK")
+	label:SetPoint("TOPLEFT")
 	label:SetFontObject(Amr.CreateFont("Regular", 16, Amr.Colors.Text))
 
 	-- create widget
--- a/ui/AmrUiTabGroup.lua	Mon Feb 12 19:34:09 2018 -0800
+++ b/ui/AmrUiTabGroup.lua	Tue Jul 17 09:57:39 2018 -0700
@@ -225,7 +225,7 @@
 -------------------------------------------------------------------------------]]
 local function Constructor()
 	local num = AceGUI:GetNextWidgetNum(Type)
-	local frame = CreateFrame("Frame",nil,UIParent)
+	local frame = CreateFrame("Frame", nil, UIParent)
 	frame:SetHeight(100)
 	frame:SetWidth(100)
 	frame:SetFrameStrata("FULLSCREEN_DIALOG")
--- a/ui/Ui.lua	Mon Feb 12 19:34:09 2018 -0800
+++ b/ui/Ui.lua	Tue Jul 17 09:57:39 2018 -0700
@@ -168,24 +168,25 @@
 		
 	-- some status text
 	local lblStatus = AceGUI:Create("AmrUiLabel")
+	f:AddChild(lblStatus)
 	lblStatus:SetWidth(900)
 	lblStatus:SetFont(Amr.CreateFont("Italic", 12, Amr.Colors.TextTan))
 	lblStatus:SetText("Ask Mr. Robot " .. L.MainStatusText("v" .. GetAddOnMetadata(Amr.ADDON_NAME, "Version"), "https://www.askmrrobot.com/wow/addon"))
 	lblStatus:SetJustifyH("CENTER")
 	lblStatus:SetWordWrap(false)
 	lblStatus:SetPoint("TOP", f.content, "BOTTOM")
-	f:AddChild(lblStatus)
 		
 	-- create the main UI container
 	local c = AceGUI:Create("AmrUiPanel")
 	c:SetLayout("Fill")
 	c:SetBackgroundColor(Amr.Colors.Black, 0)
+	f:AddChild(c)
 	c:SetPoint("TOPLEFT", f.content, "TOPLEFT")
 	c:SetPoint("BOTTOMRIGHT", f.content, "BOTTOMRIGHT")
-	f:AddChild(c)
 	
 	-- create the main tab strip
 	local t =  AceGUI:Create("AmrUiTabGroup")
+	c:AddChild(t)
 	t:SetLayout("None")
 	t:SetTabs({
 		{text=L.TabExportText, value="Export"}, 
@@ -195,16 +196,15 @@
 		{text=L.TabOptionsText, value="Options"}
 	})
 	t:SetCallback("OnGroupSelected", onMainTabSelected)
-	c:AddChild(t)
 	
 	-- create the cover/overlay container
 	c = AceGUI:Create("AmrUiPanel")
 	c:SetLayout("None")
 	c:EnableMouse(true)
 	c:SetBackgroundColor(Amr.Colors.Black, 0.75)
+	f:AddChild(c)
 	c:SetPoint("TOPLEFT", f.frame, "TOPLEFT")
 	c:SetPoint("BOTTOMRIGHT", f.frame, "BOTTOMRIGHT")
-	f:AddChild(c)
 
 	-- after adding, set cover to sit on top of everything, then hide it
 	c:SetStrata("FULLSCREEN_DIALOG")
@@ -213,13 +213,13 @@
 
 	-- put standard cover ui elements (label, cancel button)
 	local coverMsg = AceGUI:Create("AmrUiLabel")
+	c:AddChild(coverMsg)
 	coverMsg:SetWidth(600)
 	coverMsg:SetFont(Amr.CreateFont("Regular", 16, Amr.Colors.TextTan))
 	coverMsg:SetJustifyH("MIDDLE")
 	coverMsg:SetJustifyV("MIDDLE")
 	coverMsg:SetText("")
 	coverMsg:SetPoint("CENTER", c.frame, "CENTER", 0, 20)
-	c:AddChild(coverMsg)
 	
 	local coverCancel = AceGUI:Create("AmrUiTextButton")
 	coverCancel:SetWidth(200)
@@ -227,8 +227,8 @@
 	coverCancel:SetText(L.CoverCancel)
 	coverCancel:SetFont(Amr.CreateFont("Italic", 14, Amr.Colors.TextHeaderDisabled))
 	coverCancel:SetHoverFont(Amr.CreateFont("Italic", 14, Amr.Colors.TextHeaderActive))
+	c:AddChild(coverCancel)
 	coverCancel:SetPoint("CENTER", c.frame, "CENTER", 0, -20)
-	c:AddChild(coverCancel)
 	
 	coverCancel:SetCallback("OnClick", function(widget)
 		Amr:HideCover()
@@ -238,9 +238,9 @@
 	local coverContent = AceGUI:Create("AmrUiPanel")
 	coverContent:SetLayout("None")
 	coverContent:SetBackgroundColor(Amr.Colors.Black, 0)
+	c:AddChild(coverContent)
 	coverContent:SetPoint("TOPLEFT", c.frame, "TOPLEFT")
 	coverContent:SetPoint("BOTTOMRIGHT", c.frame, "BOTTOMRIGHT")
-	c:AddChild(coverContent)
 
 	_mainFrame = f
 	_mainTabs = t
@@ -288,23 +288,23 @@
 		border:SetBackgroundColor(Amr.Colors.BorderBlue)
 		border:SetWidth(400)
 		border:SetHeight(150)
+		container:AddChild(border)
 		border:SetPoint("CENTER", container.frame, "CENTER")
-		container:AddChild(border)
 
 		local bg = AceGUI:Create("AmrUiPanel")
 		bg:SetLayout("None")
 		bg:SetBackgroundColor(Amr.Colors.Bg)
+		border:AddChild(bg)
 		bg:SetPoint("TOPLEFT", border.frame, "TOPLEFT", 1, -1)
 		bg:SetPoint("BOTTOMRIGHT", border.frame, "BOTTOMRIGHT", -1, 1)
-		border:AddChild(bg)
 		
 		local lbl = AceGUI:Create("AmrUiLabel")
+		bg:AddChild(lbl)
 		lbl:SetWidth(360)
 		lbl:SetFont(Amr.CreateFont("Regular", 16, Amr.Colors.Text))
 		lbl:SetJustifyH("CENTER")
 		lbl:SetText(message)
 		lbl:SetPoint("TOP", bg.content, "TOP", 0, -20)
-		bg:AddChild(lbl)
 
 		local btn = AceGUI:Create("AmrUiButton")
 		btn:SetBackgroundColor(Amr.Colors.Orange)
@@ -312,8 +312,8 @@
 		btn:SetWidth(120)
 		btn:SetHeight(26)
 		btn:SetText(btnText)
+		bg:AddChild(btn)
 		btn:SetPoint("BOTTOM", bg.content, "BOTTOM", 0, 20)
-		bg:AddChild(btn)
 		
 		btn:SetCallback("OnClick", function(widget)
 			Amr:HideCover()
@@ -410,15 +410,15 @@
 	border:SetBackgroundColor(Amr.Colors.BorderBlue)
 	border:SetWidth(width + 2)
 	border:SetHeight(height + 2)
+	container:AddChild(border)
 	border:SetPoint("CENTER", container.frame, "CENTER")
-	container:AddChild(border)
 
 	local bg = AceGUI:Create("AmrUiPanel")
 	bg:SetLayout("None")
 	bg:SetBackgroundColor(Amr.Colors.Bg)
+	border:AddChild(bg)
 	bg:SetPoint("TOPLEFT", border.frame, "TOPLEFT", 1, -1)
 	bg:SetPoint("BOTTOMRIGHT", border.frame, "BOTTOMRIGHT", -1, 1)
-	border:AddChild(bg)
 	
 	return bg, border
 end