Push for version 0.9.

Khaibit [08-04-14 - 23:58]
Push for version 0.9.
  Added a new smaller view mode for the main window
  Added sales stats!  Click on the "list" icon at the top of the main window to toggle.
  Search field now searches item names
  Fixes for one case where items bought close together don't all trigger alerts; there are still some odd cases I'm working on
Filename
Shopkeeper.lua
Shopkeeper.txt
Shopkeeper.xml
Shopkeeper_Namespace_Init.lua
bindings.xml
i18n/DE.lua
i18n/EN.lua
i18n/FR.lua
readme
diff --git a/Shopkeeper.lua b/Shopkeeper.lua
index 856457e..5a059e6 100644
--- a/Shopkeeper.lua
+++ b/Shopkeeper.lua
@@ -1,10 +1,11 @@
 -- Shopkeeper Main Addon File
--- Last Updated July 15, 2014
+-- Last Updated August 4, 2014
 -- Written July 2014 by Dan Stone (@khaibit) - dankitymao@gmail.com
 -- Released under terms in license accompanying this file.
 -- Distribution without license is prohibited!

 -- Workaround because GetDisplayName() is broken
+-- Maybe not needed anymore, will test
 function Shopkeeper.GetAccountName()
   local acctName = GetDisplayName()
   if acctName == nil or acctName == "" then
@@ -13,7 +14,8 @@ function Shopkeeper.GetAccountName()
       acctName = GetGuildMemberInfo(GetGuildId(1), GetPlayerGuildMemberIndex(GetGuildId(1)))
     -- But if they're in no guilds, we have to use the AccountName CVar, which only works
     -- if they saved their account name on the login page.  Best we can do with patch 1.2.3
-    -- breaking GetDisplayName().
+    -- breaking GetDisplayName(), but if they aren't in any guilds, why are they using this
+    -- addon anyway?
     else
       acctName = "@" .. GetCVar("AccountName")
     end
@@ -28,6 +30,19 @@ function Shopkeeper.translate(stringName)
   return result
 end

+function Shopkeeper.localizedNumber(numberValue)
+  local stringPrice = numberValue
+  -- Insert thousands separators for the price
+  -- local stringPrice = numberValue
+  local subString = "%1" .. Shopkeeper.translate("thousandsSep") .."%2"
+  while true do
+    stringPrice, k = string.gsub(stringPrice, "^(-?%d+)(%d%d%d)", subString)
+    if (k == 0) then break end
+  end
+
+  return stringPrice
+end
+
 -- Create a textual representation of a time interval
 -- (X and Y) or Z in LUA is the equivalent of C-style
 -- ternary syntax X ? Y : Z so long as Y is not false or nil
@@ -83,15 +98,30 @@ end
 function Shopkeeper.OnWindowMoveStop()
   Shopkeeper.savedVariables.winLeft = ShopkeeperWindow:GetLeft()
   Shopkeeper.savedVariables.winTop = ShopkeeperWindow:GetTop()
+  Shopkeeper.savedVariables.miniWinLeft = ShopkeeperMiniWindow:GetLeft()
+  Shopkeeper.savedVariables.miniWinTop = ShopkeeperMiniWindow:GetTop()
+end
+
+function Shopkeeper.OnStatsWindowMoveStop()
+  Shopkeeper.savedVariables.statsWinLeft = ShopkeeperStatsWindow:GetLeft()
+  Shopkeeper.savedVariables.statsWinTop = ShopkeeperStatsWindow:GetTop()
 end

 -- Restore the window position from saved vars
 function Shopkeeper:RestoreWindowPosition()
   local left = self.savedVariables.winLeft
   local top = self.savedVariables.winTop
+  local statsLeft = self.savedVariables.statsWinLeft
+  local statsTop = self.savedVariables.statsWinTop
+  local miniLeft = self.savedVariables.miniWinLeft
+  local miniTop = self.savedVariables.miniWinTop

   ShopkeeperWindow:ClearAnchors()
+  ShopkeeperStatsWindow:ClearAnchors()
+  ShopkeeperMiniWindow:ClearAnchors()
   ShopkeeperWindow:SetAnchor(TOPLEFT, GuiRoot, TOPLEFT, left, top)
+  ShopkeeperStatsWindow:SetAnchor(TOPLEFT, GuiRoot, TOPLEFT, statsLeft, statsTop)
+  ShopkeeperMiniWindow:SetAnchor(TOPLEFT, GuiRoot, TOPLEFT, miniLeft, miniTop)
 end

 -- Handle the changing of main window font settings
@@ -102,6 +132,9 @@ function Shopkeeper:UpdateFonts()
     local look = string.format('%s|16', font)
     local titleLook = string.format('%s|22', font)
     local headerLook = string.format('%s|20', font)
+    local miniLook = string.format('%s|12', font)
+    local miniTitleLook = string.format('%s|18', font)
+    local miniHeaderLook = string.format('%s|16', font)
     ShopkeeperWindowSearchLabel:SetFont(look)
     ShopkeeperWindowSearchBox:SetFont(look)
     ShopkeeperWindowTitle:SetFont(titleLook)
@@ -114,7 +147,25 @@ function Shopkeeper:UpdateFonts()
     ShopkeeperPriceSwitchButton:SetFont(look)
     ShopkeeperResetButton:SetFont(look)
     ShopkeeperRefreshButton:SetFont(look)
-
+    ShopkeeperMiniWindowSearchLabel:SetFont(miniLook)
+    ShopkeeperMiniWindowSearchBox:SetFont(miniLook)
+    ShopkeeperMiniWindowTitle:SetFont(miniTitleLook)
+    ShopkeeperMiniWindowGuild:SetFont(miniHeaderLook)
+    ShopkeeperMiniWindowItemName:SetFont(miniHeaderLook)
+    ShopkeeperMiniWindowSellTime:SetFont(miniHeaderLook)
+    ShopkeeperMiniWindowPrice:SetFont(miniHeaderLook)
+    ShopkeeperMiniSwitchViewButton:SetFont(miniLook)
+    ShopkeeperMiniPriceSwitchButton:SetFont(miniLook)
+    ShopkeeperMiniResetButton:SetFont(miniLook)
+    ShopkeeperMiniRefreshButton:SetFont(miniLook)
+
+    ShopkeeperStatsWindowTitle:SetFont(titleLook)
+    ShopkeeperStatsWindowItemsSoldLabel:SetFont(look)
+    ShopkeeperStatsWindowTotalGoldLabel:SetFont(look)
+    ShopkeeperStatsWindowBiggestSaleLabel:SetFont(look)
+    ShopkeeperStatsWindowSliderSettingLabel:SetFont(look)
+    ShopkeeperStatsWindowSliderLabel:SetFont(look)
+
     for i = 1, #Shopkeeper.DataRows do
       local dataRow = Shopkeeper.DataRows[i]
       dataRow:GetNamedChild("Buyer"):SetFont(look)
@@ -123,22 +174,22 @@ function Shopkeeper:UpdateFonts()
       dataRow:GetNamedChild("Quantity"):SetFont(look)
       dataRow:GetNamedChild("SellTime"):SetFont(look)
       dataRow:GetNamedChild("Price"):SetFont(look)
-    end
+      if i <= #Shopkeeper.MiniDataRows then
+        local miniDataRow = Shopkeeper.MiniDataRows[i]
+        miniDataRow:GetNamedChild("Guild"):SetFont(miniLook)
+        miniDataRow:GetNamedChild("ItemName"):SetFont(miniLook)
+        miniDataRow:GetNamedChild("Quantity"):SetFont(miniLook)
+        miniDataRow:GetNamedChild("SellTime"):SetFont(miniLook)
+        miniDataRow:GetNamedChild("Price"):SetFont(miniLook)
+      end
+    end
   end
 end

 -- Item tooltips
 function Shopkeeper:ShowToolTip(itemName, itemButton)
-  self.itemToolTip = PopupTooltip
-  self.itemToolTip:ClearAnchors()
-  self.itemToolTip:ClearLines()
-  self.itemToolTip:SetLink(itemName)
-  self.itemToolTip:SetHidden(false)
-  self.itemToolTip:SetAnchor(LEFT, itemButton, RIGHT, -5, 0)
-end
-
-function Shopkeeper:HideToolTip()
-  self.itemToolTip:SetHidden(true)
+  InitializeTooltip(ItemTooltip, itemButton)
+  ItemTooltip:SetLink(itemName)
 end

 -- Clear a given row's data
@@ -157,7 +208,24 @@ function Shopkeeper.ClearDataRow(index)
   itemCell:SetText("")
   itemCell:SetHandler("OnMouseDoubleClick", nil)
   itemCell:SetHandler("OnMouseEnter", nil)
-  itemCell:SetHandler("OnMouseExit", nil)
+  dataRow:GetNamedChild("Quantity"):SetText("")
+  dataRow:GetNamedChild("SellTime"):SetText("")
+  dataRow:GetNamedChild("Price"):SetText("")
+end
+
+function Shopkeeper.ClearMiniDataRow(index)
+  if index < 1 or index > 8 then
+    return
+  end
+
+  local dataRow = Shopkeeper.MiniDataRows[index]
+  dataRow:GetNamedChild("Guild"):SetText("")
+  dataRow:GetNamedChild("ItemIcon"):SetTexture(nil)
+  dataRow:GetNamedChild("ItemIcon"):SetHidden(true)
+  local itemCell = dataRow:GetNamedChild("ItemName")
+  itemCell:SetText("")
+  itemCell:SetHandler("OnMouseDoubleClick", nil)
+  itemCell:SetHandler("OnMouseEnter", nil)
   dataRow:GetNamedChild("Quantity"):SetText("")
   dataRow:GetNamedChild("SellTime"):SetText("")
   dataRow:GetNamedChild("Price"):SetText("")
@@ -205,13 +273,13 @@ function Shopkeeper.SetDataRow(index, buyer, guild, itemName, icon, quantity, se

   -- Item name cell
   local itemCell = dataRow:GetNamedChild("ItemName")
-  itemCell:SetText(itemName)
+  itemCell:SetText(zo_strformat("<<t:1>>", itemName))
   -- Insert the item link into the chat box, with a quick substitution so brackets show up
   itemCell:SetHandler("OnMouseDoubleClick", function()
     ZO_ChatWindowTextEntryEditBox:SetText(ZO_ChatWindowTextEntryEditBox:GetText() .. string.gsub(itemName, "|H0", "|H1"))
   end)
   itemCell:SetHandler("OnMouseEnter", function() Shopkeeper:ShowToolTip(itemName, itemCell) end)
-  itemCell:SetHandler("OnMouseExit", function() Shopkeeper:HideToolTip() end)
+  itemCell:SetHandler("OnMouseExit", function() ClearTooltip(ItemTooltip) end)
   local itemCellLabel = itemCell:GetLabelControl()
   itemCellLabel:SetWrapMode(TEXT_WRAP_MODE_ELLIPSIS)

@@ -238,55 +306,194 @@ function Shopkeeper.SetDataRow(index, buyer, guild, itemName, icon, quantity, se
   end

   -- Insert thousands separators for the price
-  local stringPrice = dispPrice
-  local subString = "%1" .. Shopkeeper.translate("thousandsSep") .."%2"
-  while true do
-    stringPrice, k = string.gsub(stringPrice, "^(-?%d+)(%d%d%d)", subString)
-    if (k == 0) then break end
+  local stringPrice = Shopkeeper.localizedNumber(dispPrice)
+
+  -- Finally, set the price
+  dataRow:GetNamedChild("Price"):SetText(stringPrice .. " " .. string.format("|t16:16:%s|t","EsoUI/Art/currency/currency_gold.dds"))
+end
+
+-- Fill out a row with the given data
+function Shopkeeper.SetMiniDataRow(index, guild, itemName, icon, quantity, sellTime, price, seller)
+  if index < 1 or index > 8 then return end
+
+  local dataRow = Shopkeeper.MiniDataRows[index]
+
+  -- Guild cell
+  dataRow:GetNamedChild("Guild"):SetText(guild)
+  local guildCellLabel = dataRow:GetNamedChild("Guild"):GetLabelControl()
+  guildCellLabel:SetWrapMode(TEXT_WRAP_MODE_ELLIPSIS)
+
+  -- Item Icon
+  dataRow:GetNamedChild("ItemIcon"):SetHidden(false)
+  dataRow:GetNamedChild("ItemIcon"):SetTexture(icon)
+
+  -- Item name cell
+  local itemCell = dataRow:GetNamedChild("ItemName")
+  itemCell:SetText(zo_strformat("<<t:1>>", itemName))
+  -- Insert the item link into the chat box, with a quick substitution so brackets show up
+  itemCell:SetHandler("OnMouseDoubleClick", function()
+    ZO_ChatWindowTextEntryEditBox:SetText(ZO_ChatWindowTextEntryEditBox:GetText() .. string.gsub(itemName, "|H0", "|H1"))
+  end)
+  itemCell:SetHandler("OnMouseEnter", function() Shopkeeper:ShowToolTip(itemName, itemCell) end)
+  itemCell:SetHandler("OnMouseExit", function() ClearTooltip(ItemTooltip) end)
+  local itemCellLabel = itemCell:GetLabelControl()
+  itemCellLabel:SetWrapMode(TEXT_WRAP_MODE_ELLIPSIS)
+
+  -- Quantity cell
+  dataRow:GetNamedChild("Quantity"):SetText("x" .. quantity)
+
+  -- Sale time cell
+  dataRow:GetNamedChild("SellTime"):SetText(sellTime)
+
+  -- Handle the setting of whether or not to show pre-cut sale prices
+  -- math.floor(number + 0.5) is a quick shorthand way to round for
+  -- positive values.
+  local dispPrice = price
+  if Shopkeeper.savedVariables.showFullPrice then
+    if Shopkeeper.savedVariables.showUnitPrice and quantity > 0 then
+      dispPrice = math.floor((dispPrice / quantity) + 0.5)
+    end
+  else
+    local cutPrice = price * (1 - (GetTradingHouseCutPercentage() / 100))
+    if Shopkeeper.savedVariables.showUnitPrice and quantity > 0 then
+      cutPrice = cutPrice / quantity
+    end
+    dispPrice = math.floor(cutPrice + 0.5)
   end

+  -- Insert thousands separators for the price
+  local stringPrice = Shopkeeper.localizedNumber(dispPrice)
+
   -- Finally, set the price
-  dataRow:GetNamedChild("Price"):SetText(stringPrice .. " G")
+  dataRow:GetNamedChild("Price"):SetText(stringPrice .. " " .. string.format("|t16:16:%s|t","EsoUI/Art/currency/currency_gold.dds"))
 end

 -- Build the data rows based on the position of the slider
 function Shopkeeper.DisplayRows()
-  local startIndex = Shopkeeper.shopSlider:GetValue()
-  if startIndex + #Shopkeeper.DataRows > #Shopkeeper.SearchTable then
-    startIndex = #Shopkeeper.SearchTable - #Shopkeeper.DataRows
-  end
+  if Shopkeeper.savedVariables.viewSize == "full" then
+    local startIndex = Shopkeeper.shopSlider:GetValue()
+    if startIndex + #Shopkeeper.DataRows > #Shopkeeper.SearchTable then
+      startIndex = #Shopkeeper.SearchTable - #Shopkeeper.DataRows
+    end

-  if startIndex < 1 then startIndex = 0 end
+    if startIndex < 1 then startIndex = 0 end

-  -- Hide the slider if there's less than a full page of results
-  Shopkeeper.shopSlider:SetHidden(#Shopkeeper.SearchTable < 16)
+    -- Hide the slider if there's less than a full page of results
+    Shopkeeper.shopSlider:SetHidden(#Shopkeeper.SearchTable < 16)

-  for i = 1, #Shopkeeper.DataRows do
-    local rowIndex = i + startIndex
-    if rowIndex > #Shopkeeper.SearchTable then
-      Shopkeeper.ClearDataRow(i)
-      Shopkeeper.shopSlider:SetHidden(true)
-    else
-      local scanResult = Shopkeeper.SearchTable[rowIndex]
+    for i = 1, #Shopkeeper.DataRows do
+      local rowIndex = i + startIndex
+      if rowIndex > #Shopkeeper.SearchTable then
+        Shopkeeper.ClearDataRow(i)
+        Shopkeeper.shopSlider:SetHidden(true)
+      else
+        local scanResult = Shopkeeper.SearchTable[rowIndex]

-      Shopkeeper.SetDataRow(i, scanResult[1], scanResult[2], scanResult[3], scanResult[4], scanResult[5], Shopkeeper.textTimeSince(scanResult[6], false), scanResult[7], scanResult[8])
+        Shopkeeper.SetDataRow(i, scanResult[1], scanResult[2], scanResult[3], scanResult[4], scanResult[5], Shopkeeper.textTimeSince(scanResult[6], false), scanResult[7], scanResult[8])
+      end
     end
+
+    -- Scale the slider's range to the number of items we have minus the number of rows
+    local sliderMax = 0
+    local tableToUse = Shopkeeper.ScanResults
+    if Shopkeeper.viewMode == "self" then tableToUse = Shopkeeper.SelfSales end
+    if #tableToUse > 15 then sliderMax = (#tableToUse - 15) end
+    Shopkeeper.shopSlider:SetMinMax(0, sliderMax)
+  else
+    local startIndex = Shopkeeper.miniShopSlider:GetValue()
+    if startIndex + #Shopkeeper.MiniDataRows > #Shopkeeper.SearchTable then
+      startIndex = #Shopkeeper.SearchTable - #Shopkeeper.MiniDataRows
+    end
+
+    if startIndex < 1 then startIndex = 0 end
+
+    -- Hide the slider if there's less than a full page of results
+    Shopkeeper.miniShopSlider:SetHidden(#Shopkeeper.SearchTable < 9)
+
+    for i = 1, #Shopkeeper.MiniDataRows do
+      local rowIndex = i + startIndex
+      if rowIndex > #Shopkeeper.SearchTable then
+        Shopkeeper.ClearMiniDataRow(i)
+        Shopkeeper.miniShopSlider:SetHidden(true)
+      else
+        local scanResult = Shopkeeper.SearchTable[rowIndex]
+
+        Shopkeeper.SetMiniDataRow(i, scanResult[2], scanResult[3], scanResult[4], scanResult[5], Shopkeeper.textTimeSince(scanResult[6], false), scanResult[7], scanResult[8])
+      end
+    end
+
+    -- Scale the slider's range to the number of items we have minus the number of rows
+    local sliderMax = 0
+    local tableToUse = Shopkeeper.ScanResults
+    if Shopkeeper.viewMode == "self" then tableToUse = Shopkeeper.SelfSales end
+    if #tableToUse > 8 then sliderMax = (#tableToUse - 8) end
+    Shopkeeper.miniShopSlider:SetMinMax(0, sliderMax)
   end
+end
+
+function Shopkeeper.ToggleViewMode()
+  if Shopkeeper.savedVariables.viewSize == "full" then
+    Shopkeeper.savedVariables.viewSize = "half"
+    ShopkeeperWindow:SetHidden(true)
+    Shopkeeper.DisplayRows()
+    ShopkeeperMiniWindow:SetHidden(false)

-  -- Scale the slider's range to the number of items we have minus the number of rows
-  local sliderMax = 0
-  if #Shopkeeper.SearchTable > 15 then sliderMax = (#Shopkeeper.SearchTable - 15) end
-  Shopkeeper.shopSlider:SetMinMax(0, sliderMax)
+    if Shopkeeper.savedVariables.openWithMail then
+      MAIL_INBOX_SCENE:RemoveFragment(Shopkeeper.uiFragment)
+      MAIL_SEND_SCENE:RemoveFragment(Shopkeeper.uiFragment)
+      MAIL_INBOX_SCENE:AddFragment(Shopkeeper.miniUiFragment)
+      MAIL_SEND_SCENE:AddFragment(Shopkeeper.miniUiFragment)
+    end
+
+    if Shopkeeper.savedVariables.openWithStore then
+      TRADING_HOUSE_SCENE:RemoveFragment(Shopkeeper.uiFragment)
+      TRADING_HOUSE_SCENE:AddFragment(Shopkeeper.miniUiFragment)
+    end
+  else
+    Shopkeeper.savedVariables.viewSize = "full"
+    ShopkeeperMiniWindow:SetHidden(true)
+    Shopkeeper.DisplayRows()
+    ShopkeeperWindow:SetHidden(false)
+
+    if Shopkeeper.savedVariables.openWithMail then
+      MAIL_INBOX_SCENE:RemoveFragment(Shopkeeper.miniUiFragment)
+      MAIL_SEND_SCENE:RemoveFragment(Shopkeeper.miniUiFragment)
+      MAIL_INBOX_SCENE:AddFragment(Shopkeeper.uiFragment)
+      MAIL_SEND_SCENE:AddFragment(Shopkeeper.uiFragment)
+    end
+
+    if Shopkeeper.savedVariables.openWithStore then
+      TRADING_HOUSE_SCENE:RemoveFragment(Shopkeeper.miniUiFragment)
+      TRADING_HOUSE_SCENE:AddFragment(Shopkeeper.uiFragment)
+    end
+  end
 end

 -- Set the visibility status of the main window to the opposite of its current status
 function Shopkeeper.ToggleShopkeeperWindow()
-  if ShopkeeperWindow:IsHidden() then
-    Shopkeeper.DisplayRows()
-    SetGameCameraUIMode(true)
+  if Shopkeeper.savedVariables.viewSize == "full" then
+    ShopkeeperMiniWindow:SetHidden(true)
+    if ShopkeeperWindow:IsHidden() then
+      Shopkeeper.DisplayRows()
+      SetGameCameraUIMode(true)
+    end
+
+    ShopkeeperWindow:SetHidden(not ShopkeeperWindow:IsHidden())
+  else
+    ShopkeeperWindow:SetHidden(true)
+    if ShopkeeperMiniWindow:IsHidden() then
+      Shopkeeper.DisplayRows()
+      SetGameCameraUIMode(true)
+    end
+
+    ShopkeeperMiniWindow:SetHidden(not ShopkeeperMiniWindow:IsHidden())
   end
+end

-  ShopkeeperWindow:SetHidden(not ShopkeeperWindow:IsHidden())
+-- Set the visibility status of the stats window to the opposite of its current status
+function Shopkeeper.ToggleShopkeeperStatsWindow()
+  if ShopkeeperStatsWindow:IsHidden() then Shopkeeper.UpdateStatsWindow() end
+  ShopkeeperStatsWindow:SetHidden(not ShopkeeperStatsWindow:IsHidden())
 end

 -- Sort the scan results by 'ordering' order (asc/desc).
@@ -313,6 +520,13 @@ function Shopkeeper.SortByPrice(ordering)
           return sortA[7] > sortB[7]
         end
       end)
+      table.sort(Shopkeeper.SelfSales, function(sortA, sortB)
+        if sortA[5] and sortA[5] > 0 and sortB[5] and sortB[5] > 0 then
+          return (sortA[7] / sortA[5]) > (sortB[7] / sortB[5])
+        else
+          return sortA[7] > sortB[7]
+        end
+      end)
     -- Otherwise just sort on pure price.
     else
       table.sort(Shopkeeper.SearchTable, function(sortA, sortB)
@@ -321,6 +535,9 @@ function Shopkeeper.SortByPrice(ordering)
       table.sort(Shopkeeper.ScanResults, function(sortA, sortB)
         return sortA[7] > sortB[7]
       end)
+      table.sort(Shopkeeper.SelfSales, function(sortA, sortB)
+        return sortA[7] > sortB[7]
+      end)
     end
     ShopkeeperWindowSortPrice:SetTexture("/esoui/art/miscellaneous/list_sortheader_icon_sortup.dds")
   else
@@ -332,6 +549,9 @@ function Shopkeeper.SortByPrice(ordering)
       table.sort(Shopkeeper.ScanResults, function(sortA, sortB)
         return (sortA[7] / sortA[5]) < (sortB[7] / sortB[5])
       end)
+      table.sort(Shopkeeper.SelfSales, function(sortA, sortB)
+        return (sortA[7] / sortA[5]) < (sortB[7] / sortB[5])
+      end)
     else
       table.sort(Shopkeeper.SearchTable, function(sortA, sortB)
         return sortA[7] < sortB[7]
@@ -339,6 +559,9 @@ function Shopkeeper.SortByPrice(ordering)
       table.sort(Shopkeeper.ScanResults, function(sortA, sortB)
         return sortA[7] < sortB[7]
       end)
+      table.sort(Shopkeeper.SelfSales, function(sortA, sortB)
+        return sortA[7] < sortB[7]
+      end)
     end
     ShopkeeperWindowSortPrice:SetTexture("/esoui/art/miscellaneous/list_sortheader_icon_sortdown.dds")
   end
@@ -361,6 +584,9 @@ function Shopkeeper.SortByTime(ordering)
     table.sort(Shopkeeper.ScanResults, function(sortA, sortB)
       return sortA[6] < sortB[6]
     end)
+    table.sort(Shopkeeper.SelfSales, function(sortA, sortB)
+      return sortA[6] < sortB[6]
+    end)
     ShopkeeperWindowSortTime:SetTexture("/esoui/art/miscellaneous/list_sortheader_icon_sortup.dds")
   else
     table.sort(Shopkeeper.SearchTable, function(sortA, sortB)
@@ -369,6 +595,9 @@ function Shopkeeper.SortByTime(ordering)
     table.sort(Shopkeeper.ScanResults, function(sortA, sortB)
       return sortA[6] > sortB[6]
     end)
+    table.sort(Shopkeeper.SelfSales, function(sortA, sortB)
+      return sortA[6] > sortB[6]
+    end)
     ShopkeeperWindowSortTime:SetTexture("/esoui/art/miscellaneous/list_sortheader_icon_sortdown.dds")
   end

@@ -397,6 +626,64 @@ function Shopkeeper.TimeSort()
   end
 end

+function Shopkeeper.SalesStats(statsDays)
+  local itemsSold = 0
+  local goldMade = 0
+  local largestSingle = {0, nil}
+  local oldestTime = 0
+  local newestTime = 0
+  -- 86,400 seconds in a day; this will be the epoch time statsDays ago
+  local statsDaysEpoch = GetTimeStamp() - (86400 * statsDays)
+
+  -- Loop through the player's sales and create the stats as appropriate
+  -- (everything or everything with a timestamp after statsDaysEpoch)
+  for i = 1, #Shopkeeper.SelfSales do
+    local theItem = Shopkeeper.SelfSales[i]
+    if statsDays == 0 or theItem[6] > statsDaysEpoch then
+      itemsSold = itemsSold + 1
+      goldMade = goldMade + theItem[7]
+      if oldestTime == 0 or theItem[6] < oldestTime then oldestTime = theItem[6] end
+      if newestTime == 0 or theItem[6] > newestTime then newestTime = theItem[6] end
+      if theItem[7] > largestSingle[1] then largestSingle = {theItem[7], theItem[3]} end
+     end
+  end
+
+  -- Newest timestamp seen minus oldest timestamp seen is the number of seconds between
+  -- them; divided by 86,400 it's the number of days (or at least close enough for this)
+  local timeWindow = newestTime - oldestTime
+  local dayWindow = 1
+  if timeWindow > 86400 then dayWindow = math.floor(timeWindow / 86400) + 1 end
+  local goldPerDay = goldMade / dayWindow
+
+  -- If they have the option set to show prices post-cut, calculate that here
+  if not Shopkeeper.savedVariables.showFullPrice then
+    local cutMult = 1 - (GetTradingHouseCutPercentage() / 100)
+    goldMade = math.floor(goldMade * cutMult + 0.5)
+    goldPerDay = math.floor(goldPerDay * cutMult + 0.5)
+    largestSingle[1] = math.floor(largestSingle[1] * cutMult + 0.5)
+  end
+
+  -- Return the statistical data in a convenient table
+  return { numSold = itemsSold,
+           numDays = dayWindow,
+           totalGold = goldMade,
+           avgGold = goldPerDay,
+           biggestSale = largestSingle }
+end
+
+function Shopkeeper.UpdateStatsWindow()
+  local sliderLevel = Shopkeeper.statsSlider:GetValue()
+  if sliderLevel == 0 then
+    ShopkeeperStatsWindowSliderSettingLabel:SetText(Shopkeeper.translate('statsTimeAll'))
+  else
+    ShopkeeperStatsWindowSliderSettingLabel:SetText(string.format(Shopkeeper.translate('statsTimeSome'), sliderLevel))
+  end
+
+  local newStats = Shopkeeper.SalesStats(sliderLevel)
+  ShopkeeperStatsWindowItemsSoldLabel:SetText(string.format(Shopkeeper.translate('statsItemsSold'), Shopkeeper.localizedNumber(newStats['numSold'])))
+  ShopkeeperStatsWindowTotalGoldLabel:SetText(string.format(Shopkeeper.translate('statsTotalGold'), Shopkeeper.localizedNumber(newStats['totalGold']), Shopkeeper.localizedNumber(newStats['avgGold'])))
+  ShopkeeperStatsWindowBiggestSaleLabel:SetText(string.format(Shopkeeper.translate('statsBiggest'), zo_strformat("<<t:1>>", newStats['biggestSale'][2]), Shopkeeper.localizedNumber(newStats['biggestSale'][1])))
+end

 -- LibAddon init code
 function Shopkeeper:LibAddonInit()
@@ -540,9 +827,15 @@ end

 -- Handle scrolling the main window
 function Shopkeeper.OnSliderMouseWheel(self, delta)
-  local oldSliderLevel = Shopkeeper.shopSlider:GetValue()
-  local newSliderLevel = oldSliderLevel - delta
-  Shopkeeper.shopSlider:SetValue(newSliderLevel)
+  if Shopkeeper.savedVariables.viewSize == "full" then
+    local oldSliderLevel = Shopkeeper.shopSlider:GetValue()
+    local newSliderLevel = oldSliderLevel - delta
+    Shopkeeper.shopSlider:SetValue(newSliderLevel)
+  else
+    local oldSliderLevel = Shopkeeper.miniShopSlider:GetValue()
+    local newSliderLevel = oldSliderLevel - delta
+    Shopkeeper.miniShopSlider:SetValue(newSliderLevel)
+  end
 end

 -- Update the table if the slider moved
@@ -550,42 +843,36 @@ function Shopkeeper.OnSliderMoved(self, sliderLevel, eventReason)
   Shopkeeper.DisplayRows()
 end

--- Filters the ScanResults table into the SearchTable, using the
--- Shopkeeper.viewMode to determine whether or not to limit to
--- player's own sales.  Sadly, we cannot yet search item names
--- because patch 1.2.3 broke every method we know of to get those.
+function Shopkeeper.OnStatsSliderMoved(self, sliderLevel, eventReason)
+  Shopkeeper.UpdateStatsWindow()
+end
+
+-- Filters the ScanResults (or SelfSales) table into the SearchTable,
+-- using the Shopkeeper.viewMode to determine which one to use.
 function Shopkeeper.DoSearch(searchText)
   Shopkeeper.SearchTable = {}
   local searchTerm = string.lower(searchText)
   local acctName = Shopkeeper.GetAccountName()
+  local tableToUse = Shopkeeper.ScanResults
+  if Shopkeeper.viewMode == "self" then tableToUse = Shopkeeper.SelfSales end

   -- Actually carry out the search
-  for j = 1, #Shopkeeper.ScanResults do
-    local result = Shopkeeper.ScanResults[j]
+  for j = 1, #tableToUse do
+    local result = tableToUse[j]
     if result then
-      if Shopkeeper.viewMode == "self" then
-        if result[8] ~= acctName then
-          result = nil
-        end
-      end
-
-      if result then
-        if searchText == nil or searchText == " " then
-          table.insert(Shopkeeper.SearchTable, result)
-        else
-          for i = 1, 3 do
-            local fixedTerm = result[i]
-            if i == 3 then
-              -- Once 1.3.0 is live:
-              -- fixedTerm = GetItemLinkName(fixedTerm)
-              local itName = fixedTerm
-            end
+      if searchText == nil or searchText == " " then
+        table.insert(Shopkeeper.SearchTable, result)
+      else
+        for i = 1, 3 do
+          local fixedTerm = result[i]
+          if i == 3 then
+            fixedTerm = GetItemLinkName(fixedTerm)
+          end

-            if string.lower(fixedTerm):find(searchTerm) then
-              if result[i] ~= nil then
-                table.insert(Shopkeeper.SearchTable, result)
-                break
-              end
+          if string.lower(fixedTerm):find(searchTerm) then
+            if result[i] ~= nil then
+              table.insert(Shopkeeper.SearchTable, result)
+              break
             end
           end
         end
@@ -600,11 +887,15 @@ end
 function Shopkeeper.SwitchViewMode()
   if Shopkeeper.viewMode == "self" then
     ShopkeeperSwitchViewButton:SetText(Shopkeeper.translate('viewModeYourName'))
-    ShopkeeperWindow:GetNamedChild('Title'):SetText("Shopkeeper - " .. Shopkeeper.translate('allSalesTitle'))
+    ShopkeeperWindowTitle:SetText("Shopkeeper - " .. Shopkeeper.translate('allSalesTitle'))
+    ShopkeeperMiniSwitchViewButton:SetText(Shopkeeper.translate('viewModeYourName'))
+    ShopkeeperMiniWindowTitle:SetText("Shopkeeper - " .. Shopkeeper.translate('allSalesTitle'))
     Shopkeeper.viewMode = "all"
   else
     ShopkeeperSwitchViewButton:SetText(Shopkeeper.translate('viewModeAllName'))
-    ShopkeeperWindow:GetNamedChild('Title'):SetText("Shopkeeper - " .. Shopkeeper.translate('yourSalesTitle'))
+    ShopkeeperWindowTitle:SetText("Shopkeeper - " .. Shopkeeper.translate('yourSalesTitle'))
+    ShopkeeperMiniSwitchViewButton:SetText(Shopkeeper.translate('viewModeAllName'))
+    ShopkeeperMiniWindowTitle:SetText("Shopkeeper - " .. Shopkeeper.translate('yourSalesTitle'))
     Shopkeeper.viewMode = "self"
   end

@@ -615,11 +906,15 @@ function Shopkeeper.SwitchPriceMode()
   if Shopkeeper.savedVariables.showUnitPrice then
     Shopkeeper.savedVariables.showUnitPrice = false
     ShopkeeperPriceSwitchButton:SetText(Shopkeeper.translate('showUnitPrice'))
-    ShopkeeperWindow:GetNamedChild('Price'):SetText(Shopkeeper.translate('priceColumnName'))
+    ShopkeeperWindowPrice:SetText(Shopkeeper.translate('priceColumnName'))
+    ShopkeeperMiniPriceSwitchButton:SetText(Shopkeeper.translate('showUnitPrice'))
+    ShopkeeperMiniWindowPrice:SetText(Shopkeeper.translate('priceColumnName'))
   else
     Shopkeeper.savedVariables.showUnitPrice = true
     ShopkeeperPriceSwitchButton:SetText(Shopkeeper.translate('showTotalPrice'))
-    ShopkeeperWindow:GetNamedChild('Price'):SetText(Shopkeeper.translate('priceEachColumnName'))
+    ShopkeeperWindowPrice:SetText(Shopkeeper.translate('priceEachColumnName'))
+    ShopkeeperMiniPriceSwitchButton:SetText(Shopkeeper.translate('showTotalPrice'))
+    ShopkeeperMiniWindowPrice:SetText(Shopkeeper.translate('priceEachColumnName'))
   end

   if Shopkeeper.curSort[1] == "price" then
@@ -632,10 +927,13 @@ end

 -- Actually carries out of the scan of a specific guild store's sales history.
 -- If checkOlder is true, will request older events first if there are any.
--- Inserts all events that occurred after timeStamp into the ScanResults table.
-function Shopkeeper:DoScan(guildID, timeStamp, checkOlder)
+-- Inserts all events that occurred after guildID guild's last scan into the ScanResults table.
+-- Inserts all events that occurred after guildID guild's last scan and were sold by the player
+-- into the SelfSales table.
+function Shopkeeper:DoScan(guildID, checkOlder)
   local numEvents = GetNumGuildEvents(guildID, GUILD_HISTORY_SALES)
-  local thePlayer = Shopkeeper.GetAccountName()
+  local thePlayer = string.lower(Shopkeeper.GetAccountName())
+  local timeStamp = GetTimeStamp()
   for i = 0, numEvents do
     local theEvent = {}
     _, theEvent.secsSince, theEvent.seller, theEvent.buyer,
@@ -645,7 +943,7 @@ function Shopkeeper:DoScan(guildID, timeStamp, checkOlder)
     if theEvent.secsSince ~= nil then
       theEvent.saleTime = timeStamp - theEvent.secsSince

-      if theEvent.saleTime > Shopkeeper.acctSavedVariables.lastScan then
+      if Shopkeeper.acctSavedVariables.lastScan[guildID] == nil or theEvent.saleTime > Shopkeeper.acctSavedVariables.lastScan[guildID] then
         if theEvent.itemName ~= nil and theEvent.seller ~= nil and theEvent.buyer ~= nil and theEvent.salePrice ~= nil then
           -- Grab the icon
           local itemIcon, _, _, _ = GetItemLinkInfo(theEvent.itemName)
@@ -653,7 +951,7 @@ function Shopkeeper:DoScan(guildID, timeStamp, checkOlder)
           -- If the seller is the player and this isn't a deep scan (and thus the first upon login or reset),
           -- queue up an alert
           if not checkOlder and (Shopkeeper.savedVariables.showChatAlerts or Shopkeeper.savedVariables.showAnnounceAlerts)
-             and theEvent.seller == thePlayer then
+             and string.lower(theEvent.seller) == thePlayer then
             table.insert(Shopkeeper.alertQueue, theEvent)
           end

@@ -661,11 +959,19 @@ function Shopkeeper:DoScan(guildID, timeStamp, checkOlder)
           table.insert(Shopkeeper.ScanResults, {theEvent.buyer, theEvent.guild, theEvent.itemName, itemIcon,
                                                 theEvent.quant, theEvent.saleTime, theEvent.salePrice,
                                                 theEvent.seller})
+
+          -- And then, if it's the player's sale, insert into that table
+          if string.lower(theEvent.seller) == thePlayer then
+            table.insert(Shopkeeper.SelfSales, {theEvent.buyer, theEvent.guild, theEvent.itemName, itemIcon,
+                                                theEvent.quant, theEvent.saleTime, theEvent.salePrice,
+                                                theEvent.seller})
+          end
         end
       end
     end
   end

+  Shopkeeper.acctSavedVariables.lastScan[guildID] = GetTimeStamp()
   if guildID < GetNumGuilds() then
     if checkOlder and DoesGuildHistoryCategoryHaveMoreEvents((guildID + 1), GUILD_HISTORY_SALES) then
       RequestGuildHistoryCategoryNewest((guildID + 1), GUILD_HISTORY_SALES)
@@ -676,19 +982,24 @@ function Shopkeeper:DoScan(guildID, timeStamp, checkOlder)
   end
 end

--- Called after store scans complete, updates last scan timestamp,
--- the search table, and slider range, then sorts the fresh table.
+-- Called after store scans complete, updates the search table
+-- and slider range, then sorts the fresh table.
 -- Once this is done writes out to the saved variables scan history
 -- and updates the displayed table, sending a message to chat if
 -- the scan was initiated via the 'refresh' button.
-function Shopkeeper:PostScan(timeStamp, doAlert)
-  Shopkeeper.acctSavedVariables.lastScan = timeStamp
+function Shopkeeper:PostScan(doAlert)
   Shopkeeper.isScanning = false
   Shopkeeper.DoSearch(ShopkeeperWindowSearchBox:GetText())
   -- Scale the slider's range to the number of items we have minus the number of rows
   local sliderMax = 0
-  if #Shopkeeper.ScanResults > 14 then sliderMax = (#Shopkeeper.ScanResults - 14) end
+  local tableToUse = Shopkeeper.ScanResults
+  if Shopkeeper.viewMode == "self" then tableToUse = Shopkeeper.SelfSales end
+  if #tableToUse > 15 then sliderMax = (#tableToUse - 15) end
   Shopkeeper.shopSlider:SetMinMax(0, sliderMax)
+  sliderMax = 0
+  if #tableToUse > 8 then sliderMax = (#tableToUse - 8) end
+  Shopkeeper.miniShopSlider:SetMinMax(0, sliderMax)
+
   if Shopkeeper.curSort[1] == "time" then
     Shopkeeper.SortByTime(Shopkeeper.curSort[2])
   else
@@ -725,12 +1036,7 @@ function Shopkeeper:PostScan(timeStamp, doAlert)
       -- or if there's only one.
       if Shopkeeper.savedVariables.showMultiple or numAlerts == 1 then
         -- Insert thousands separators for the price
-        local stringPrice = dispPrice
-        local subString = "%1" .. Shopkeeper.translate("thousandsSep") .."%2"
-        while true do
-          stringPrice, k = string.gsub(stringPrice, "^(-?%d+)(%d%d%d)", subString)
-          if (k == 0) then break end
-        end
+        local stringPrice = Shopkeeper.localizedNumber(dispPrice)

         -- Only make a sound on the first one if sounds are turned on
         -- To avoid sound spam on multiple sales
@@ -746,17 +1052,17 @@ function Shopkeeper:PostScan(timeStamp, doAlert)
           -- single item sold vs. multiple of an item sold.
           if Shopkeeper.locale == "de" then
             if theEvent.quant > 1 then
-              CENTER_SCREEN_ANNOUNCE:AddMessage(EVENT_SKILL_RANK_UPDATE, CSA_EVENT_SMALL_TEXT, alertSound,
-                string.format(Shopkeeper.translate('salesAlertColor'), theEvent.quant, theEvent.itemName,
+              CENTER_SCREEN_ANNOUNCE:DisplayMessage(CSA_EVENT_SMALL_TEXT, alertSound,
+                string.format(Shopkeeper.translate('salesAlertColor'), theEvent.quant, zo_strformat("<<t:1>>", theEvent.itemName),
                               stringPrice, theEvent.guild, Shopkeeper.textTimeSince(theEvent.saleTime, true)))
             else
-              CENTER_SCREEN_ANNOUNCE:AddMessage(EVENT_SKILL_RANK_UPDATE, CSA_EVENT_SMALL_TEXT, alertSound,
-                string.format(Shopkeeper.translate('salesAlertColorSingle'), theEvent.itemName,
+              CENTER_SCREEN_ANNOUNCE:DisplayMessage(CSA_EVENT_SMALL_TEXT, alertSound,
+                string.format(Shopkeeper.translate('salesAlertColorSingle'), zo_strformat("<<t:1>>", theEvent.itemName),
                               stringPrice, theEvent.guild, Shopkeeper.textTimeSince(theEvent.saleTime, true)))
             end
           else
-            CENTER_SCREEN_ANNOUNCE:AddMessage(EVENT_SKILL_RANK_UPDATE, CSA_EVENT_SMALL_TEXT, alertSound,
-              string.format(Shopkeeper.translate('salesAlertColor'), theEvent.itemName,
+            CENTER_SCREEN_ANNOUNCE:DisplayMessage(CSA_EVENT_SMALL_TEXT, alertSound,
+              string.format(Shopkeeper.translate('salesAlertColor'), zo_strformat("<<t:1>>", theEvent.itemName),
                             theEvent.quant, stringPrice, theEvent.guild, Shopkeeper.textTimeSince(theEvent.saleTime, true)))
           end
         end
@@ -766,14 +1072,14 @@ function Shopkeeper:PostScan(timeStamp, doAlert)
           if Shopkeeper.locale == "de" then
             if theEvent.quant > 1 then
               CHAT_SYSTEM:AddMessage(string.format("[Shopkeeper] " .. Shopkeeper.translate('salesAlert'),
-                                     theEvent.quant, theEvent.itemName, stringPrice, theEvent.guild, Shopkeeper.textTimeSince(theEvent.saleTime, true)))
+                                     theEvent.quant, zo_strformat("<<t:1>>", theEvent.itemName), stringPrice, theEvent.guild, Shopkeeper.textTimeSince(theEvent.saleTime, true)))
             else
               CHAT_SYSTEM:AddMessage(string.format("[Shopkeeper] " .. Shopkeeper.translate('salesAlertSingle'),
-                                     theEvent.itemName, stringPrice, theEvent.guild, Shopkeeper.textTimeSince(theEvent.saleTime, true)))
+                                     zo_strformat("<<t:1>>", theEvent.itemName), stringPrice, theEvent.guild, Shopkeeper.textTimeSince(theEvent.saleTime, true)))
             end
           else
             CHAT_SYSTEM:AddMessage(string.format("[Shopkeeper] " .. Shopkeeper.translate('salesAlert'),
-                                   theEvent.itemName, theEvent.quant, stringPrice, theEvent.guild, Shopkeeper.textTimeSince(theEvent.saleTime, true)))
+                                   zo_strformat("<<t:1>>", theEvent.itemName), theEvent.quant, stringPrice, theEvent.guild, Shopkeeper.textTimeSince(theEvent.saleTime, true)))
           end
         end
       end
@@ -782,15 +1088,10 @@ function Shopkeeper:PostScan(timeStamp, doAlert)
     -- Otherwise, we'll just alert once with a summary at the end
     if not Shopkeeper.savedVariables.showMultiple and numAlerts > 1 then
       -- Insert thousands separators for the price
-      local stringPrice = totalGold
-      local subString = "%1" .. Shopkeeper.translate("thousandsSep") .."%2"
-      while true do
-        stringPrice, k = string.gsub(stringPrice, "^(-?%d+)(%d%d%d)", substring)
-        if (k == 0) then break end
-      end
+      local stringPrice = Shopkeeper.localizedNumber(totalGold)

       if Shopkeeper.savedVariables.showAnnounceAlerts then
-        CENTER_SCREEN_ANNOUNCE:AddMessage(EVENT_SKILL_RANK_UPDATE, CSA_EVENT_SMALL_TEXT, Shopkeeper.savedVariables.alertSoundName,
+        CENTER_SCREEN_ANNOUNCE:DisplayMessage(CSA_EVENT_SMALL_TEXT, Shopkeeper.savedVariables.alertSoundName,
           string.format(Shopkeeper.translate('salesGroupAlertColor'), numSold, stringPrice))
       else
         CHAT_SYSTEM:AddMessage(string.format("[Shopkeeper] " .. Shopkeeper.translate('salesGroupAlert'),
@@ -807,13 +1108,12 @@ end
 -- Idea for spaced callbacks taken from awesomebilly's Luminary Trade/Sales
 -- History addon.
 function Shopkeeper:ScanStores(checkOlder, doAlert)
-  local timeStamp = GetTimeStamp()
   -- If it's been less than a minute since we last scanned the store,
   -- don't do it again so we don't hammer the server either accidentally
   -- or on purpose
-  local timeLimit = timeStamp - 60
-  if not Shopkeeper.isScanning and timeLimit > Shopkeeper.acctSavedVariables.lastScan then
-    local guildNum = GetNumGuilds()
+  local timeLimit = GetTimeStamp() - 60
+  local guildNum = GetNumGuilds()
+  if not Shopkeeper.isScanning and (Shopkeeper.acctSavedVariables.lastScan[guildNum] == nil or timeLimit > Shopkeeper.acctSavedVariables.lastScan[guildNum]) then
     -- Nothing to scan!
     if guildNum == 0 then return end

@@ -830,10 +1130,10 @@ function Shopkeeper:ScanStores(checkOlder, doAlert)

       -- I need a better way to space out these checks than callbacks
       -- It works but feels ugly
-      zo_callLater(function() Shopkeeper:DoScan(guildID, timeStamp, checkOlder) end, (((j - 1) * 2000) + 1000))
+      zo_callLater(function() Shopkeeper:DoScan(guildID, checkOlder) end, (((j - 1) * 2000) + 1000))
     end
     -- Once scans are done, wait a few seconds and do some cleanup
-    zo_callLater(function() Shopkeeper:PostScan(timeStamp, doAlert) end, ((guildNum + 1) * 2000))
+    zo_callLater(function() Shopkeeper:PostScan(doAlert) end, ((guildNum + 1) * 2000))
   end
 end

@@ -853,7 +1153,8 @@ function Shopkeeper.DoRefresh()
   -- don't do it again so we don't hammer the server either accidentally
   -- or on purpose
   local timeLimit = timeStamp - 59
-  if timeLimit > Shopkeeper.acctSavedVariables.lastScan then
+  local guildNum = GetNumGuilds()
+  if timeLimit > Shopkeeper.acctSavedVariables.lastScan[guildNum] then
     CHAT_SYSTEM:AddMessage("[Shopkeeper] " .. Shopkeeper.translate('refreshStart'))
     Shopkeeper:ScanStores(false, true)

@@ -867,9 +1168,10 @@ end
 -- will grab the sales histories from each guild to re-populate.
 function Shopkeeper.DoReset()
   Shopkeeper.ScanResults = {}
+  Shopkeeper.SelfSales = {}
   Shopkeeper.SearchTable = {}
   Shopkeeper.acctSavedVariables.scanHistory = {}
-  Shopkeeper.acctSavedVariables.lastScan = -1
+  Shopkeeper.acctSavedVariables.lastScan = {}
   Shopkeeper.DisplayRows()
   Shopkeeper:ScanStores(true, false)
   CHAT_SYSTEM:AddMessage("[Shopkeeper] " .. Shopkeeper.translate('resetDone'))
@@ -892,19 +1194,31 @@ function Shopkeeper:SetupShopkeeperWindow()
   shopkeeperMail:SetHandler("OnClicked", Shopkeeper.ToggleShopkeeperWindow)

   -- Set column headers and search label from translation
-  ShopkeeperWindow:GetNamedChild('Buyer'):SetText(Shopkeeper.translate('buyerColumnName'))
-  ShopkeeperWindow:GetNamedChild('Guild'):SetText(Shopkeeper.translate('guildColumnName'))
-  ShopkeeperWindow:GetNamedChild('ItemName'):SetText(Shopkeeper.translate('itemColumnName'))
-  ShopkeeperWindow:GetNamedChild('SellTime'):SetText(Shopkeeper.translate('timeColumnName'))
+  ShopkeeperWindowBuyer:SetText(Shopkeeper.translate('buyerColumnName'))
+  ShopkeeperWindowGuild:SetText(Shopkeeper.translate('guildColumnName'))
+  ShopkeeperWindowItemName:SetText(Shopkeeper.translate('itemColumnName'))
+  ShopkeeperWindowSellTime:SetText(Shopkeeper.translate('timeColumnName'))
+  ShopkeeperMiniWindowGuild:SetText(Shopkeeper.translate('guildColumnName'))
+  ShopkeeperMiniWindowItemName:SetText(Shopkeeper.translate('itemColumnName'))
+  ShopkeeperMiniWindowSellTime:SetText(Shopkeeper.translate('timeColumnName'))
+
   if Shopkeeper.savedVariables.showUnitPrice then
-    ShopkeeperWindow:GetNamedChild('Price'):SetText(Shopkeeper.translate('priceEachColumnName'))
+    ShopkeeperWindowPrice:SetText(Shopkeeper.translate('priceEachColumnName'))
+    ShopkeeperMiniWindowPrice:SetText(Shopkeeper.translate('priceEachColumnName'))
   else
-    ShopkeeperWindow:GetNamedChild('Price'):SetText(Shopkeeper.translate('priceColumnName'))
+    ShopkeeperWindowPrice:SetText(Shopkeeper.translate('priceColumnName'))
+    ShopkeeperMiniWindowPrice:SetText(Shopkeeper.translate('priceColumnName'))
   end
-  ShopkeeperWindow:GetNamedChild('SearchLabel'):SetText(Shopkeeper.translate('searchBoxName'))
+  ShopkeeperWindowSearchLabel:SetText(Shopkeeper.translate('searchBoxName'))
+  ShopkeeperMiniWindowSearchLabel:SetText(Shopkeeper.translate('searchBoxName'))

   -- Set second half of window title from translation
-  ShopkeeperWindow:GetNamedChild('Title'):SetText("Shopkeeper - " .. Shopkeeper.translate('yourSalesTitle'))
+  ShopkeeperWindowTitle:SetText("Shopkeeper - " .. Shopkeeper.translate('yourSalesTitle'))
+  ShopkeeperMiniWindowTitle:SetText("Shopkeeper - " .. Shopkeeper.translate('yourSalesTitle'))
+
+  -- And set the stats window title and slider label from translation
+  ShopkeeperStatsWindowTitle:SetText("Shopkeeper " .. Shopkeeper.translate('statsTitle'))
+  ShopkeeperStatsWindowSliderLabel:SetText(Shopkeeper.translate('statsDays'))

   -- Set up some helpful tooltips for the Buyer and Item column headers
   ShopkeeperWindowBuyer:SetHandler("OnMouseEnter", function(self)
@@ -918,18 +1232,37 @@ function Shopkeeper:SetupShopkeeperWindow()
   end)

   ShopkeeperWindowItemName:SetHandler("OnMouseExit", function(self) ZO_Tooltips_HideTextTooltip() end)
+
+  ShopkeeperMiniWindowItemName:SetHandler("OnMouseEnter", function(self)
+    ZO_Tooltips_ShowTextTooltip(self, TOP, Shopkeeper.translate('itemTooltip'))
+  end)
+
+  ShopkeeperMiniWindowItemName:SetHandler("OnMouseExit", function(self) ZO_Tooltips_HideTextTooltip() end)

   ShopkeeperWindowSellTime:SetHandler("OnMouseEnter", function(self)
     ZO_Tooltips_ShowTextTooltip(self, TOP, Shopkeeper.translate('sortTimeTip'))
   end)

   ShopkeeperWindowSellTime:SetHandler("OnMouseExit", function(self) ZO_Tooltips_HideTextTooltip() end)
+
+  ShopkeeperMiniWindowSellTime:SetHandler("OnMouseEnter", function(self)
+    ZO_Tooltips_ShowTextTooltip(self, TOP, Shopkeeper.translate('sortTimeTip'))
+  end)
+
+  ShopkeeperMiniWindowSellTime:SetHandler("OnMouseExit", function(self) ZO_Tooltips_HideTextTooltip() end)

   ShopkeeperWindowPrice:SetHandler("OnMouseEnter", function(self)
     ZO_Tooltips_ShowTextTooltip(self, TOP, Shopkeeper.translate('sortPriceTip'))
   end)

   ShopkeeperWindowPrice:SetHandler("OnMouseExit", function(self) ZO_Tooltips_HideTextTooltip() end)
+
+  ShopkeeperMiniWindowPrice:SetHandler("OnMouseEnter", function(self)
+    ZO_Tooltips_ShowTextTooltip(self, TOP, Shopkeeper.translate('sortPriceTip'))
+  end)
+
+  ShopkeeperMiniWindowPrice:SetHandler("OnMouseExit", function(self) ZO_Tooltips_HideTextTooltip() end)
+

   -- View switch button
   local switchViews = CreateControlFromVirtual("ShopkeeperSwitchViewButton", ShopkeeperWindow, "ZO_DefaultButton")
@@ -937,6 +1270,12 @@ function Shopkeeper:SetupShopkeeperWindow()
   switchViews:SetWidth(175)
   switchViews:SetText(Shopkeeper.translate('viewModeAllName'))
   switchViews:SetHandler("OnClicked", Shopkeeper.SwitchViewMode)
+
+  local miniSwitchViews = CreateControlFromVirtual("ShopkeeperMiniSwitchViewButton", ShopkeeperMiniWindow, "ZO_DefaultButton")
+  miniSwitchViews:SetAnchor(BOTTOMLEFT, ShopkeeperMiniWindow, BOTTOMLEFT, 2, -5)
+  miniSwitchViews:SetWidth(175)
+  miniSwitchViews:SetText(Shopkeeper.translate('viewModeAllName'))
+  miniSwitchViews:SetHandler("OnClicked", Shopkeeper.SwitchViewMode)

   -- Total / unit price switch button
   local unitPrice = CreateControlFromVirtual("ShopkeeperPriceSwitchButton", ShopkeeperWindow, "ZO_DefaultButton")
@@ -948,6 +1287,16 @@ function Shopkeeper:SetupShopkeeperWindow()
     unitPrice:SetText(Shopkeeper.translate('showUnitPrice'))
   end
   unitPrice:SetHandler("OnClicked", Shopkeeper.SwitchPriceMode)
+
+  local miniUnitPrice = CreateControlFromVirtual("ShopkeeperMiniPriceSwitchButton", ShopkeeperMiniWindow, "ZO_DefaultButton")
+  miniUnitPrice:SetAnchor(LEFT, miniSwitchViews, RIGHT, 0, 0)
+  miniUnitPrice:SetWidth(175)
+  if Shopkeeper.savedVariables.showUnitPrice then
+    miniUnitPrice:SetText(Shopkeeper.translate('showTotalPrice'))
+  else
+    miniUnitPrice:SetText(Shopkeeper.translate('showUnitPrice'))
+  end
+  miniUnitPrice:SetHandler("OnClicked", Shopkeeper.SwitchPriceMode)

   -- Refresh button
   local refreshButton = CreateControlFromVirtual("ShopkeeperRefreshButton", ShopkeeperWindow, "ZO_DefaultButton")
@@ -955,6 +1304,11 @@ function Shopkeeper:SetupShopkeeperWindow()
   refreshButton:SetWidth(150)
   refreshButton:SetText(Shopkeeper.translate('refreshLabel'))
   refreshButton:SetHandler("OnClicked", Shopkeeper.DoRefresh)
+  local miniRefreshButton = CreateControlFromVirtual("ShopkeeperMiniRefreshButton", ShopkeeperMiniWindow, "ZO_DefaultButton")
+  miniRefreshButton:SetAnchor(BOTTOMRIGHT, ShopkeeperMiniWindow, BOTTOMRIGHT, -2, -5)
+  miniRefreshButton:SetWidth(150)
+  miniRefreshButton:SetText(Shopkeeper.translate('refreshLabel'))
+  miniRefreshButton:SetHandler("OnClicked", Shopkeeper.DoRefresh)

   -- Reset button
   local resetButton = CreateControlFromVirtual("ShopkeeperResetButton", ShopkeeperWindow, "ZO_DefaultButton")
@@ -962,6 +1316,11 @@ function Shopkeeper:SetupShopkeeperWindow()
   resetButton:SetWidth(150)
   resetButton:SetText(Shopkeeper.translate('resetLabel'))
   resetButton:SetHandler("OnClicked", Shopkeeper.DoReset)
+  local miniResetButton = CreateControlFromVirtual("ShopkeeperMiniResetButton", ShopkeeperMiniWindow, "ZO_DefaultButton")
+  miniResetButton:SetAnchor(BOTTOMRIGHT, ShopkeeperMiniWindow, BOTTOMRIGHT, -152, -5)
+  miniResetButton:SetWidth(150)
+  miniResetButton:SetText(Shopkeeper.translate('resetLabel'))
+  miniResetButton:SetHandler("OnClicked", Shopkeeper.DoReset)

   -- Make the 15 rows that comprise the visible table
   if #Shopkeeper.DataRows == 0 then
@@ -973,11 +1332,61 @@ function Shopkeeper:SetupShopkeeperWindow()
       Shopkeeper.DataRows[i] = dRow
     end
   end
+
+  -- And for the mini window
+  if #Shopkeeper.MiniDataRows == 0 then
+    local dataRowOffsetX = 10
+    local dataRowOffsetY = 74
+    for i = 1,8 do
+      local dRow = CreateControlFromVirtual("ShopkeeperMiniDataRow", ShopkeeperMiniWindow, "ShopkeeperMiniDataRow", i)
+      dRow:SetSimpleAnchorParent(dataRowOffsetX, dataRowOffsetY+((dRow:GetHeight()+2)*(i-1)))
+      Shopkeeper.MiniDataRows[i] = dRow
+    end
+  end

-  -- Close button
+  -- Close buttons
   ShopkeeperWindowCloseButton:SetNormalTexture("/esoui/art/hud/radialicon_cancel_up.dds")
   ShopkeeperWindowCloseButton:SetMouseOverTexture("/esoui/art/hud/radialicon_cancel_over.dds")
   ShopkeeperWindowCloseButton:SetHandler("OnClicked", function() ShopkeeperWindow:SetHidden(true) end)
+  ShopkeeperMiniWindowCloseButton:SetNormalTexture("/esoui/art/hud/radialicon_cancel_up.dds")
+  ShopkeeperMiniWindowCloseButton:SetMouseOverTexture("/esoui/art/hud/radialicon_cancel_over.dds")
+  ShopkeeperMiniWindowCloseButton:SetHandler("OnClicked", function() ShopkeeperMiniWindow:SetHidden(true) end)
+  ShopkeeperStatsWindowCloseButton:SetNormalTexture("/esoui/art/hud/radialicon_cancel_up.dds")
+  ShopkeeperStatsWindowCloseButton:SetMouseOverTexture("/esoui/art/hud/radialicon_cancel_over.dds")
+  ShopkeeperStatsWindowCloseButton:SetHandler("OnClicked", function() ShopkeeperStatsWindow:SetHidden(true) end)
+
+  -- Stats buttons
+  ShopkeeperWindowStatsButton:SetNormalTexture("/esoui/art/tradinghouse/tradinghouse_listings_tabicon_up.dds")
+  ShopkeeperWindowStatsButton:SetMouseOverTexture("/esoui/art/tradinghouse/tradinghouse_listings_tabicon_over.dds")
+  ShopkeeperWindowStatsButton:SetHandler("OnClicked", Shopkeeper.ToggleShopkeeperStatsWindow)
+  ShopkeeperWindowStatsButton:SetHandler("OnMouseEnter", function(self)
+    ZO_Tooltips_ShowTextTooltip(self, TOP, Shopkeeper.translate('statsTooltip'))
+  end)
+  ShopkeeperWindowStatsButton:SetHandler("OnMouseExit", function(self) ZO_Tooltips_HideTextTooltip() end)
+  ShopkeeperMiniWindowStatsButton:SetNormalTexture("/esoui/art/tradinghouse/tradinghouse_listings_tabicon_up.dds")
+  ShopkeeperMiniWindowStatsButton:SetMouseOverTexture("/esoui/art/tradinghouse/tradinghouse_listings_tabicon_over.dds")
+  ShopkeeperMiniWindowStatsButton:SetHandler("OnClicked", Shopkeeper.ToggleShopkeeperStatsWindow)
+  ShopkeeperMiniWindowStatsButton:SetHandler("OnMouseEnter", function(self)
+    ZO_Tooltips_ShowTextTooltip(self, TOP, Shopkeeper.translate('statsTooltip'))
+  end)
+  ShopkeeperMiniWindowStatsButton:SetHandler("OnMouseExit", function(self) ZO_Tooltips_HideTextTooltip() end)
+
+  -- View size change buttons
+  ShopkeeperWindowViewSizeButton:SetNormalTexture("/esoui/art/inventory/inventory_tabicon_quest_up.dds")
+  ShopkeeperWindowViewSizeButton:SetMouseOverTexture("/esoui/art/inventory/inventory_tabicon_quest_over.dds")
+  ShopkeeperWindowViewSizeButton:SetHandler("OnClicked", Shopkeeper.ToggleViewMode)
+  ShopkeeperWindowViewSizeButton:SetHandler("OnMouseEnter", function(self)
+    ZO_Tooltips_ShowTextTooltip(self, TOP, Shopkeeper.translate('sizeTooltip'))
+  end)
+  ShopkeeperWindowViewSizeButton:SetHandler("OnMouseExit", function(self) ZO_Tooltips_HideTextTooltip() end)
+  ShopkeeperMiniWindowViewSizeButton:SetNormalTexture("/esoui/art/inventory/inventory_tabicon_quest_up.dds")
+  ShopkeeperMiniWindowViewSizeButton:SetMouseOverTexture("/esoui/art/inventory/inventory_tabicon_quest_over.dds")
+  ShopkeeperMiniWindowViewSizeButton:SetHandler("OnClicked", Shopkeeper.ToggleViewMode)
+  ShopkeeperMiniWindowViewSizeButton:SetHandler("OnMouseEnter", function(self)
+    ZO_Tooltips_ShowTextTooltip(self, TOP, Shopkeeper.translate('sizeTooltip'))
+  end)
+  ShopkeeperMiniWindowViewSizeButton:SetHandler("OnMouseExit", function(self) ZO_Tooltips_HideTextTooltip() end)
+

   -- Slider setup
   ShopkeeperWindow:SetHandler("OnMouseWheel", Shopkeeper.OnSliderMouseWheel)
@@ -985,21 +1394,66 @@ function Shopkeeper:SetupShopkeeperWindow()
   Shopkeeper.shopSlider.texture = "/esoui/art/miscellaneous/scrollbox_elevator.dds"
   Shopkeeper.shopSlider.offset = 0
   local tex = Shopkeeper.shopSlider.texture
-  Shopkeeper.shopSlider:SetDimensions(20,570)
+  Shopkeeper.shopSlider:SetDimensions(20,561)
   Shopkeeper.shopSlider:SetMouseEnabled(true)
   Shopkeeper.shopSlider:SetThumbTexture(tex,tex,tex,20,50,0,0,1,1)
   Shopkeeper.shopSlider:SetMinMax(0, 100)
   Shopkeeper.shopSlider:SetValue(0)
   Shopkeeper.shopSlider:SetValueStep(1)
-  Shopkeeper.shopSlider:SetAnchor(LEFT, ShopkeeperWindow, RIGHT, -22, 15)
+  Shopkeeper.shopSlider:SetAnchor(LEFT, ShopkeeperWindow, RIGHT, -20, 17)
   Shopkeeper.shopSlider:SetHandler("OnValueChanged", Shopkeeper.OnSliderMoved)
+  Shopkeeper.sliderBG = CreateControl(nil, Shopkeeper.shopSlider, CT_BACKDROP)
+  Shopkeeper.sliderBG:SetCenterColor(0, 0, 0)
+  Shopkeeper.sliderBG:SetAnchor(TOPLEFT, Shopkeeper.shopSlider, TOPLEFT, 0, -4)
+  Shopkeeper.sliderBG:SetAnchor(BOTTOMRIGHT, Shopkeeper.shopSlider, BOTTOMRIGHT, 0, 4)
+  Shopkeeper.sliderBG:SetEdgeTexture("EsoUI\\Art\\Tooltips\\UI-SliderBackdrop.dds", 32, 4)
+
+  ShopkeeperMiniWindow:SetHandler("OnMouseWheel", Shopkeeper.OnSliderMouseWheel)
+  Shopkeeper.miniShopSlider = CreateControl(ShopkeeperMiniWindowSlider, ShopkeeperMiniWindow, CT_SLIDER)
+  Shopkeeper.miniShopSlider.texture = "/esoui/art/miscellaneous/scrollbox_elevator.dds"
+  Shopkeeper.miniShopSlider.offset = 0
+  Shopkeeper.miniShopSlider:SetDimensions(20,300)
+  Shopkeeper.miniShopSlider:SetMouseEnabled(true)
+  Shopkeeper.miniShopSlider:SetThumbTexture(tex,tex,tex,20,50,0,0,1,1)
+  Shopkeeper.miniShopSlider:SetMinMax(0, 100)
+  Shopkeeper.miniShopSlider:SetValue(0)
+  Shopkeeper.miniShopSlider:SetValueStep(1)
+  Shopkeeper.miniShopSlider:SetAnchor(LEFT, ShopkeeperMiniWindow, RIGHT, -20, 17)
+  Shopkeeper.miniShopSlider:SetHandler("OnValueChanged", Shopkeeper.OnSliderMoved)
+  Shopkeeper.miniSliderBG = CreateControl(nil, Shopkeeper.miniShopSlider, CT_BACKDROP)
+  Shopkeeper.miniSliderBG:SetCenterColor(0, 0, 0)
+  Shopkeeper.miniSliderBG:SetAnchor(TOPLEFT, Shopkeeper.miniShopSlider, TOPLEFT, 0, -4)
+  Shopkeeper.miniSliderBG:SetAnchor(BOTTOMRIGHT, Shopkeeper.miniShopSlider, BOTTOMRIGHT, 0, 4)
+  Shopkeeper.miniSliderBG:SetEdgeTexture("EsoUI\\Art\\Tooltips\\UI-SliderBackdrop.dds", 32, 4)
+
+  Shopkeeper.statsSlider = CreateControl(ShopkeeperStatsWindowSlider, ShopkeeperStatsWindow, CT_SLIDER)
+  Shopkeeper.statsSlider.texture = "/esoui/art/miscellaneous/scrollbox_elevator.dds"
+  Shopkeeper.statsSlider.offset = 0
+  Shopkeeper.statsSlider:SetDimensions(375,14)
+  Shopkeeper.statsSlider:SetMouseEnabled(true)
+  Shopkeeper.statsSlider:SetThumbTexture("EsoUI\\Art\\Miscellaneous\\scrollbox_elevator.dds", "EsoUI\\Art\\Miscellaneous\\scrollbox_elevator_disabled.dds", nil, 8, 16)
+  Shopkeeper.statsSlider:SetMinMax(0, 30)
+  Shopkeeper.statsSlider:SetValue(0)
+  Shopkeeper.statsSlider:SetValueStep(1)
+  Shopkeeper.statsSlider:SetOrientation(ORIENTATION_HORIZONTAL)
+  Shopkeeper.statsSlider:SetAnchor(BOTTOM, ShopkeeperStatsWindow, BOTTOM, 25, -15)
+  Shopkeeper.statsSlider:SetHandler("OnValueChanged", Shopkeeper.OnStatsSliderMoved)
+  Shopkeeper.statsSliderBG = CreateControl(nil, Shopkeeper.statsSlider, CT_BACKDROP)
+  Shopkeeper.statsSliderBG:SetCenterColor(0, 0, 0)
+  Shopkeeper.statsSliderBG:SetAnchor(TOPLEFT, Shopkeeper.statsSlider, TOPLEFT, 0, 4)
+  Shopkeeper.statsSliderBG:SetAnchor(BOTTOMRIGHT, Shopkeeper.statsSlider, BOTTOMRIGHT, 0, -4)
+  Shopkeeper.statsSliderBG:SetEdgeTexture("EsoUI\\Art\\Tooltips\\UI-SliderBackdrop.dds", 32, 4)
+

   -- Sorting handlers
   ShopkeeperWindowPrice:SetHandler("OnMouseUp", Shopkeeper.PriceSort)
   ShopkeeperWindowSellTime:SetHandler("OnMouseUp", Shopkeeper.TimeSort)
+  ShopkeeperMiniWindowPrice:SetHandler("OnMouseUp", Shopkeeper.PriceSort)
+  ShopkeeperMiniWindowSellTime:SetHandler("OnMouseUp", Shopkeeper.TimeSort)

-   -- Search handler, for all the good it does us right now
-   ZO_PreHookHandler(ShopkeeperWindowSearchBox, "OnTextChanged", function(self) Shopkeeper.DoSearch(ShopkeeperWindowSearchBox:GetText()) end)
+  -- Search handler
+  ZO_PreHookHandler(ShopkeeperWindowSearchBox, "OnTextChanged", function(self) Shopkeeper.DoSearch(ShopkeeperWindowSearchBox:GetText()) end)
+  ZO_PreHookHandler(ShopkeeperMiniWindowSearchBox, "OnTextChanged", function(self) Shopkeeper.DoSearch(ShopkeeperMiniWindowSearchBox:GetText()) end)

   -- We're all set, so make sure we're using the right font and then update the UI
   Shopkeeper.windowFont = Shopkeeper.savedVariables.windowFont
@@ -1017,16 +1471,21 @@ function Shopkeeper:Initialize()
     ["showFullPrice"] = true,
     ["winLeft"] = 30,
     ["winTop"] = 30,
+    ["miniWinLeft"] = 30,
+    ["miniWinTop"] = 30,
+    ["statsWinLeft"] = 720,
+    ["statsWinTop"] = 820,
     ["windowFont"] = "ProseAntique",
     ["historyDepth"] = 3000,
     ["scanFreq"] = 120,
     ["showAnnounceAlerts"] = true,
     ["alertSoundName"] = "Book_Acquired",
     ["showUnitPrice"] = false,
+    ["viewSize"] = "full",
   }

   local acctDefaults = {
-    ["lastScan"] = -1,
+    ["lastScan"] = {},
     ["scanHistory"] = {},
   }

@@ -1034,6 +1493,14 @@ function Shopkeeper:Initialize()
   self.acctSavedVariables = ZO_SavedVars:NewAccountWide("ShopkeeperSavedVars", 1, Shopkeeper.GetAccountName(), acctDefaults)
   self.ScanResults = Shopkeeper.acctSavedVariables.scanHistory

+  -- Update the lastScan value, as it was previously a number
+  if type(Shopkeeper.acctSavedVariables.lastScan) == "number" then
+    local guildNum = GetNumGuilds()
+    local oldStamp = Shopkeeper.acctSavedVariables.lastScan
+    Shopkeeper.acctSavedVariables.lastScan = {}
+    for i = 1, guildNum do Shopkeeper.acctSavedVariables.lastScan[i] = oldStamp end
+  end
+
   self:LibAddonInit()
   self:SetupShopkeeperWindow()
   self:RestoreWindowPosition()
@@ -1058,40 +1525,79 @@ function Shopkeeper:Initialize()
     end
   end

+  -- Now that we've truncated, populate the SelfSales table
+  local loggedInAccount = string.lower(Shopkeeper.GetAccountName())
+  for i = 1, #self.ScanResults do
+    if string.lower(self.ScanResults[i][8]) == loggedInAccount then table.insert(self.SelfSales, self.ScanResults[i]) end
+  end
+
   -- Populate the search table
   self.DoSearch(nil)

   -- Add the shopkeeper window to the mail and trading house scenes if the
   -- player's settings indicate they want that behavior
   Shopkeeper.uiFragment = ZO_FadeSceneFragment:New(ShopkeeperWindow)
+  Shopkeeper.miniUiFragment = ZO_FadeSceneFragment:New(ShopkeeperMiniWindow)
+
   if self.savedVariables.openWithMail then
-    MAIL_INBOX_SCENE:AddFragment(Shopkeeper.uiFragment)
-    MAIL_SEND_SCENE:AddFragment(Shopkeeper.uiFragment)
+    if self.savedVariables.viewSize == "full" then
+      MAIL_INBOX_SCENE:AddFragment(Shopkeeper.uiFragment)
+      MAIL_SEND_SCENE:AddFragment(Shopkeeper.uiFragment)
+    else
+      MAIL_INBOX_SCENE:AddFragment(Shopkeeper.miniUiFragment)
+      MAIL_SEND_SCENE:AddFragment(Shopkeeper.miniUiFragment)
+    end
   end

   if self.savedVariables.openWithStore then
-    TRADING_HOUSE_SCENE:AddFragment(Shopkeeper.uiFragment)
+    if self.savedVariables.viewSize == "full" then
+      TRADING_HOUSE_SCENE:AddFragment(Shopkeeper.uiFragment)
+    else
+      TRADING_HOUSE_SCENE:AddFragment(Shopkeeper.miniUiFragment)
+    end
   end

   -- Because we allow manual toggling of the Shopkeeper window in those scenes (without
   -- making that setting permanent), we also have to hide the window on closing them
   -- if they're not part of the scene.
   EVENT_MANAGER:RegisterForEvent(Shopkeeper.name, EVENT_MAIL_CLOSE_MAILBOX, function()
-    if not Shopkeeper.savedVariables.openWithMail then ShopkeeperWindow:SetHidden(true) end
+    if not Shopkeeper.savedVariables.openWithMail then
+      ShopkeeperWindow:SetHidden(true)
+      ShopkeeperMiniWindow:SetHidden(true)
+    end
   end)
   EVENT_MANAGER:RegisterForEvent(Shopkeeper.name, EVENT_CLOSE_TRADING_HOUSE, function()
-    if not Shopkeeper.savedVariables.openWithStore then ShopkeeperWindow:SetHidden(true) end
+    if not Shopkeeper.savedVariables.openWithStore then
+      ShopkeeperWindow:SetHidden(true)
+      ShopkeeperMiniWindow:SetHidden(true)
+    end
   end)

-  -- We also want to make sure the Shopkeeper window is hidden in the game menu
-  ZO_PreHookHandler(ZO_GameMenu_InGame, "OnShow", function() ShopkeeperWindow:SetHidden(true) end)
+  -- We also want to make sure the Shopkeeper windows are hidden in the game menu
+  ZO_PreHookHandler(ZO_GameMenu_InGame, "OnShow", function()
+    ShopkeeperWindow:SetHidden(true)
+    ShopkeeperStatsWindow:SetHidden(true)
+    ShopkeeperMiniWindow:SetHidden(true)
+  end)

   -- I could do this with action layer pop/push, but it's kind've a pain
   -- when it's just these I want to hook
-  EVENT_MANAGER:RegisterForEvent(Shopkeeper.name, EVENT_CLOSE_BANK, function() ShopkeeperWindow:SetHidden(true) end)
-  EVENT_MANAGER:RegisterForEvent(Shopkeeper.name, EVENT_CLOSE_GUILD_BANK, function() ShopkeeperWindow:SetHidden(true) end)
-  EVENT_MANAGER:RegisterForEvent(Shopkeeper.name, EVENT_CLOSE_STORE, function() ShopkeeperWindow:SetHidden(true) end)
-  EVENT_MANAGER:RegisterForEvent(Shopkeeper.name, EVENT_END_CRAFTING_STATION_INTERACT, function() ShopkeeperWindow:SetHidden(true) end)
+  EVENT_MANAGER:RegisterForEvent(Shopkeeper.name, EVENT_CLOSE_BANK, function()
+    ShopkeeperWindow:SetHidden(true)
+    ShopkeeperMiniWindow:SetHidden(true)
+  end)
+  EVENT_MANAGER:RegisterForEvent(Shopkeeper.name, EVENT_CLOSE_GUILD_BANK, function()
+    ShopkeeperWindow:SetHidden(true)
+    ShopkeeperMiniWindow:SetHidden(true)
+  end)
+  EVENT_MANAGER:RegisterForEvent(Shopkeeper.name, EVENT_CLOSE_STORE, function()
+    ShopkeeperWindow:SetHidden(true)
+    ShopkeeperMiniWindow:SetHidden(true)
+  end)
+  EVENT_MANAGER:RegisterForEvent(Shopkeeper.name, EVENT_END_CRAFTING_STATION_INTERACT, function()
+    ShopkeeperWindow:SetHidden(true)
+    ShopkeeperMiniWindow:SetHidden(true)
+  end)

   -- Update fonts after each UI load
   EVENT_MANAGER:RegisterForEvent(Shopkeeper.name, EVENT_PLAYER_ACTIVATED, Shopkeeper.PlayerActive)
diff --git a/Shopkeeper.txt b/Shopkeeper.txt
index f31e467..2bef8ca 100644
--- a/Shopkeeper.txt
+++ b/Shopkeeper.txt
@@ -1,10 +1,10 @@
 ## Title: Shopkeeper
-## APIVersion: 100007
+## APIVersion: 100008
 ## Description: Notifies you when you've sold something in a guild store and presents guild sales info in a convenient table.
 ## Author: Dan Stone (@khaibit) - dankitymao@gmail.com
-## Version: 0.3
+## Version: 0.9
 ## License: See license - distribution without license is prohibited!
-## LastUpdated: July 8, 2014
+## LastUpdated: August 4, 2014
 ## SavedVariables: ShopkeeperSavedVars
 ## OptionalDependsOn: LibAddonMenu-2.0 LibMediaProvider-1.0 LibStub

diff --git a/Shopkeeper.xml b/Shopkeeper.xml
index 4cf0da5..d277117 100644
--- a/Shopkeeper.xml
+++ b/Shopkeeper.xml
@@ -1,19 +1,50 @@
 <!--
       Shopkeeper UI Layout File
-      Last Updated July 14, 2014
+      Last Updated August 4, 2014
       Written July 2014 by Dan Stone (@khaibit) - dankitymao@gmail.com
       Released under terms in license accompanying this file.
       Distribution without license is prohibited!
 -->
 <GuiXml>
   <Controls>
+    <TopLevelControl movable="true" mouseEnabled="true" name="ShopkeeperStatsWindow" hidden="true">
+      <Dimensions x="475" y="185" />
+      <OnMoveStop>
+        Shopkeeper.OnStatsWindowMoveStop()
+      </OnMoveStop>
+      <Controls>
+        <Backdrop name="$(parent)BG" inherits="ZO_DefaultBackdrop" />
+        <Label name="$(parent)Title" font="ZoFontGame" height="25" width="95" inheritAlpha="true" color="FFFFFF" verticalAlignment="CENTER" horizontalAlignment="CENTER" text="Shopkeeper Sales Statistics">
+          <Anchor point="TOP" relativeTo="$(parent)" relativePoint="TOP" offsetX="0" offsetY="5" />
+        </Label>
+        <Button name="$(parent)CloseButton" font="ZoFontGame" inheritAlpha="true" verticalAlignment="CENTER" horizontalAlignment="CENTER">
+          <Anchor point="CENTER" relativeTo="$(parent)" relativePoint="TOPRIGHT" offsetX="-20" offsetY="20" />
+          <Dimensions x="48" y="48" />
+        </Button>
+        <Label name="$(parent)ItemsSoldLabel" font="ZoFontGame" height="25" width="95" inheritAlpha="true" color="FFFFFF" verticalAlignment="CENTER" horizontalAlignment="LEFT" text="Items sold: 0">
+          <Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" offsetX="15" offsetY="55" />
+        </Label>
+        <Label name="$(parent)TotalGoldLabel" font="ZoFontGame" height="25" width="95" inheritAlpha="true" color="FFFFFF" verticalAlignment="CENTER" horizontalAlignment="LEFT" text="Total gold: 0 (0 per day)">
+          <Anchor point="TOPRIGHT" relativeTo="$(parent)" relativePoint="TOPRIGHT" offsetX="-15" offsetY="55" />
+        </Label>
+        <Label name="$(parent)BiggestSaleLabel" font="ZoFontGame" height="25" width="95" inheritAlpha="true" color="FFFFFF" verticalAlignment="CENTER" horizontalAlignment="CENTER" text="Biggest sale: (0)">
+          <Anchor point="TOP" relativeTo="$(parent)" relativePoint="TOP" offsetX="0" offsetY="90" />
+        </Label>
+        <Label name="$(parent)SliderSettingLabel" font="ZoFontGame" height="25" width="95" inheritAlpha="true" color="FFFFFF" verticalAlignment="CENTER" horizontalAlignment="CENTER" text="Using all data">
+          <Anchor point="TOP" relativeTo="$(parent)" relativePoint="TOP" offsetX="0" offsetY="125" />
+        </Label>
+        <Label name="$(parent)SliderLabel" font="ZoFontGame" height="25" width="50" inheritAlpha="true" color="FFFFFF" verticalAlignment="CENTER" horizontalAlignment="LEFT" text="Days: ">
+          <Anchor point="BOTTOMLEFT" relativeTo="$(parent)" relativePoint="BOTTOMLEFT" offsetX="10" offsetY="-14" />
+        </Label>
+      </Controls>
+    </TopLevelControl>
     <TopLevelControl movable="true" mouseEnabled="true" name="ShopkeeperWindow" hidden="true">
       <Dimensions x="907" y="685" />
       <OnMoveStop>
         Shopkeeper.OnWindowMoveStop()
       </OnMoveStop>
       <Controls>
-        <Backdrop name="$(parent)BG" inherits="ZO_ThinBackdrop" />
+        <Backdrop name="$(parent)BG" inherits="ZO_DefaultBackdrop" />
         <Label name="$(parent)SearchLabel" font="ZoFontGame" height="25" width="95" inheritAlpha="true" color="FFFFFF" verticalAlignment="CENTER" horizontalAlignment="LEFT" text="Search: ">
           <Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" offsetX="15" offsetY="10" />
         </Label>
@@ -41,8 +72,16 @@
           <Dimensions x="48" y="48" />
         </Button>
         <Label name="$(parent)Title" font="ZoFontGame" height="25" inheritAlpha="true" color="FFFFFF" verticalAlignment="TOP" horizontalAlignment="CENTER" text="Shopkeeper - Your Sales">
-	  <Anchor point="TOP" relativeTo="$(parent)" relativePoint="TOP" offsetX="-10" offsetY="5" />
+	        <Anchor point="TOP" relativeTo="$(parent)" relativePoint="TOP" offsetX="-10" offsetY="5" />
         </Label>
+        <Button name="$(parent)StatsButton" font="ZoFontGame" inheritAlpha="true" verticalAlignment="CENTER" horizontalAlignment="CENTER">
+          <Anchor point="LEFT" relativeTo="$(parent)Title" relativePoint="RIGHT" offsetX="15" offsetY="0" />
+          <Dimensions x="48" y="48" />
+        </Button>
+        <Button name="$(parent)ViewSizeButton" font="ZoFontGame" inheritAlpha="true" verticalAlignment="CENTER" horizontalAlignment="CENTER">
+          <Anchor point="LEFT" relativeTo="$(parent)StatsButton" relativePoint="RIGHT" offsetX="0" offsetY="0" />
+          <Dimensions x="48" y="48" />
+        </Button>
         <Button name="$(parent)Buyer" font="ZoFontGame" inheritAlpha="true" verticalAlignment="TOP" horizontalAlignment="LEFT" text="Buyer">
           <Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" offsetX="30" offsetY="44" />
           <Dimensions x="60" y="25" />
@@ -76,6 +115,78 @@
         </Texture>
       </Controls>
     </TopLevelControl>
+    <TopLevelControl movable="true" mouseEnabled="true" name="ShopkeeperMiniWindow" hidden="true">
+      <Dimensions x="650" y="416" />
+      <OnMoveStop>
+        Shopkeeper.OnWindowMoveStop()
+      </OnMoveStop>
+      <Controls>
+        <Backdrop name="$(parent)BG" inherits="ZO_DefaultBackdrop" />
+        <Label name="$(parent)SearchLabel" font="ZoFontGame" height="25" width="95" inheritAlpha="true" color="FFFFFF" verticalAlignment="CENTER" horizontalAlignment="LEFT" text="Search: ">
+          <Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" offsetX="15" offsetY="10" />
+        </Label>
+        <EditBox name="$(parent)SearchBox" mouseEnabled="true" editEnabled="true" font="ZoFontGame" textType="TEXT_TYPE_ALL" multiLine="false" newLineEnabled="false">
+          <Anchor point="LEFT" relativeTo="$(parent)SearchLabel" relativePoint="RIGHT" offsetX="5" offsetY="0" />
+          <Dimensions x="175" y="25" />
+          <Controls>
+            <Backdrop name="$(parent)TextBG" centerColor="000000" edgeColor="AAAAAA">
+              <AnchorFill />
+              <Edge edgeSize="1" />
+            </Backdrop>
+          </Controls>
+          <OnMouseDown>
+            self:TakeFocus()
+          </OnMouseDown>
+          <OnEnter>
+            self:LoseFocus()
+          </OnEnter>
+          <OnEscape>
+            self:LoseFocus()
+          </OnEscape>
+        </EditBox>
+        <Button name="$(parent)CloseButton" font="ZoFontGame" inheritAlpha="true" verticalAlignment="CENTER" horizontalAlignment="CENTER">
+          <Anchor point="CENTER" relativeTo="$(parent)" relativePoint="TOPRIGHT" offsetX="-20" offsetY="20" />
+          <Dimensions x="48" y="48" />
+        </Button>
+        <Label name="$(parent)Title" font="ZoFontGame" height="25" inheritAlpha="true" color="FFFFFF" verticalAlignment="TOP" horizontalAlignment="CENTER" text="Shopkeeper - Your Sales">
+	        <Anchor point="LEFT" relativeTo="$(parent)SearchBox" relativePoint="RIGHT" offsetX="40" offsetY="0" />
+        </Label>
+        <Button name="$(parent)StatsButton" font="ZoFontGame" inheritAlpha="true" verticalAlignment="CENTER" horizontalAlignment="CENTER">
+          <Anchor point="LEFT" relativeTo="$(parent)Title" relativePoint="RIGHT" offsetX="15" offsetY="0" />
+          <Dimensions x="48" y="48" />
+        </Button>
+        <Button name="$(parent)ViewSizeButton" font="ZoFontGame" inheritAlpha="true" verticalAlignment="CENTER" horizontalAlignment="CENTER">
+          <Anchor point="LEFT" relativeTo="$(parent)StatsButton" relativePoint="RIGHT" offsetX="0" offsetY="0" />
+          <Dimensions x="48" y="48" />
+        </Button>
+        <Label name="$(parent)Guild" font="ZoFontGame" width="120" height="25" inheritAlpha="true" color="FFFFFF" verticalAlignment="TOP" horizontalAlignment="LEFT" text="Guild">
+          <Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" offsetX="15" offsetY="44" />
+        </Label>
+        <Button name="$(parent)ItemName" font="ZoFontGame" inheritAlpha="true" color="FFFFFF" verticalAlignment="TOP" horizontalAlignment="LEFT" text="Item">
+          <Dimensions x="200" y="25" />
+          <Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" offsetX="140" offsetY="44" />
+        </Button>
+        <Button name="$(parent)SellTime" font="ZoFontGame" inheritAlpha="true" color="FFFFFF" verticalAlignment="TOP" horizontalAlignment="LEFT" text="Sale Time">
+          <Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" offsetX="420" offsetY="44" />
+          <Dimensions x="95" y="25" />
+        </Button>
+        <Texture name="$(parent)SortTime" textureFile="/esoui/art/miscellaneous/list_sortheader_icon_sortdown.dds" alpha="1">
+          <Dimensions x="40" y="40" />
+          <Anchor point="LEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" offsetX="495" offsetY="54" />
+          <TextureCoords left="0" right="1" top="0" bottom="1" />
+        </Texture>
+        <Button name="$(parent)Price" font="ZoFontGame" inheritAlpha="true" verticalAlignment="TOP" horizontalAlignment="LEFT" text="Price">
+          <Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" offsetX="540" offsetY="44" />
+          <Dimensions x="85" y="25" />
+          <FontColors normalColor="D5B526" mouseOverColor="D5B526" pressedColor="D5B526" />
+        </Button>
+        <Texture name="$(parent)SortPrice" textureFile="/esoui/art/miscellaneous/list_sortheader_icon_neutral.dds" alpha="1">
+          <Dimensions x="40" y="40" />
+          <Anchor point="LEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" offsetX="600" offsetY="54" />
+          <TextureCoords left="0" right="1" top="0" bottom="1" />
+        </Texture>
+      </Controls>
+    </TopLevelControl>
     <Control name="ShopkeeperDataRow" horizontalAlignment="LEFT" verticalAlignment="CENTER" color="CFDCBD" font="ZoFontGame" virtual="true">
       <Dimensions x="857" y="36" />
       <Anchor point="TOPLEFT" offsetX="25" offsetY="25" />
@@ -112,5 +223,36 @@
         </Label>
       </Controls>
     </Control>
+    <Control name="ShopkeeperMiniDataRow" horizontalAlignment="LEFT" verticalAlignment="CENTER" color="CFDCBD" font="ZoFontGame" virtual="true">
+      <Dimensions x="615" y="36" />
+      <Anchor point="TOPLEFT" offsetX="10" offsetY="25" />
+      <Controls>
+        <Backdrop name="$(parent)BG" inherits="ZO_ThinBackdrop" />
+        <Button name="$(parent)Guild" font="ZoFontGame" inheritAlpha="true" verticalAlignment="CENTER" horizontalAlignment="LEFT" text="Guild">
+          <Anchor point="TOPLEFT" offsetX="5" offsetY="5" />
+          <Dimensions x="120" y="26" />
+          <FontColors normalColor="FFFFFF" mouseOverColor="FFFFFF" pressedColor="FFFFFF" />
+        </Button>
+        <Texture name="$(parent)ItemIcon" alpha="1">
+          <Dimensions x="32" y="32" />
+          <Anchor point="TOPLEFT" offsetX="125" offsetY="3" />
+          <TextureCoords left="0" right="1" top="0" bottom="1" />
+        </Texture>
+        <Button name="$(parent)ItemName" font="ZoFontGame" inheritAlpha="true" verticalAlignment="CENTER" horizontalAlignment="LEFT" text="Item Name">
+          <Anchor point="TOPLEFT" offsetX="160" offsetY="5" />
+          <Dimensions x="215" y="26" />
+          <FontColors normalColor="0000FF" mouseOverColor="0000FF" pressedColor="0000FF" />
+        </Button>
+        <Label name="$(parent)Quantity" font="ZoFontGame" width="30" height="26" inheritAlpha="true" color="FFFFFF" verticalAlignment="CENTER" horizontalAlignment="LEFT" text="x1">
+          <Anchor point="TOPLEFT" offsetX="380" offsetY="7" />
+        </Label>
+        <Label name="$(parent)SellTime" font="ZoFontGame" width="115" height="26" inheritAlpha="true" color="FFFFFF" verticalAlignment="CENTER" horizontalAlignment="LEFT" text="Time">
+          <Anchor point="TOPLEFT" offsetX="410" offsetY="7" />
+        </Label>
+        <Label name="$(parent)Price" font="ZoFontGame" width="115" height="26" inheritAlpha="true" color="D5B526" verticalAlignment="CENTER" horizontalAlignment="LEFT" text="Price">
+          <Anchor point="TOPLEFT" offsetX="530" offsetY="7" />
+        </Label>
+      </Controls>
+    </Control>
   </Controls>
 </GuiXml>
\ No newline at end of file
diff --git a/Shopkeeper_Namespace_Init.lua b/Shopkeeper_Namespace_Init.lua
index ecea85b..ea55f4b 100644
--- a/Shopkeeper_Namespace_Init.lua
+++ b/Shopkeeper_Namespace_Init.lua
@@ -1,25 +1,26 @@
 -- Shopkeeper Namespace Setup
--- Last Updated July 15, 2014
+-- Last Updated August 4, 2014
 -- Written July 2014 by Dan Stone (@khaibit) - dankitymao@gmail.com
 -- Released under terms in license accompanying this file.
 -- Distribution without license is prohibited!

 Shopkeeper = {}
 Shopkeeper.name = "Shopkeeper"
-Shopkeeper.version = "0.3"
+Shopkeeper.version = "0.9"
 Shopkeeper.locale = "en"
 Shopkeeper.viewMode = "self"
 Shopkeeper.isScanning = false
 Shopkeeper.i18n = {}
 Shopkeeper.DataRows = {}
-  -- ScanResults and SearchTable have the following fields:
+Shopkeeper.MiniDataRows = {}
+  -- ScanResults, SelfSales and SearchTable have the following fields:
   -- 1: Buyer  2: Guild  3: Item Name  4: Item Icon
   -- 5: Quantity 6: UNIX Time  7: Price 8: Seller
 Shopkeeper.ScanResults = {}
+Shopkeeper.SelfSales = {}
 Shopkeeper.SearchTable = {}
 Shopkeeper.alertQueue = {}
 Shopkeeper.shopSlider = {}
-Shopkeeper.itemToolTip = {}
 Shopkeeper.curSort = {"time", "desc"}
 Shopkeeper.uiFragment = {}

diff --git a/bindings.xml b/bindings.xml
index 9368e96..b593916 100644
--- a/bindings.xml
+++ b/bindings.xml
@@ -1,6 +1,6 @@
 <!--
       Shopkeeper Key Bindings File
-      Last Updated July 7, 2014
+      Last Updated August 4, 2014
       Written July 2014 by Dan Stone (@khaibit) - dankitymao@gmail.com
       Released under terms in license accompanying this file.
       Distribution without license is prohibited!
@@ -10,7 +10,10 @@
   <Layer name="SI_KEYBINDINGS_CATEGORY_GENERAL">
     <Category name="Shopkeeper">
       <Action name="SHOPKEEPER_TOGGLE">
-		<Down>Shopkeeper.ToggleShopkeeperWindow()</Down>
+		    <Down>Shopkeeper.ToggleShopkeeperWindow()</Down>
+      </Action>
+      <Action name="SHOPKEEPER_STATS_TOGGLE">
+        <Down>Shopkeeper.ToggleShopkeeperStatsWindow()</Down>
       </Action>
     </Category>
   </Layer>
diff --git a/i18n/DE.lua b/i18n/DE.lua
index 82417d1..aa273d7 100644
--- a/i18n/DE.lua
+++ b/i18n/DE.lua
@@ -1,5 +1,5 @@
 -- Shopkeeper German Localization File
--- Last Updated July 14, 2014
+-- Last Updated August 4, 2014
 -- Written July 2014 by Dan Stone (@khaibit) - dankitymao@gmail.com
 -- Released under terms in license.txt accompanying this file.
 -- Distribution without license is prohibited!
@@ -37,6 +37,8 @@ Shopkeeper.i18n.localized = {
   buyerTooltip = "Doppelkick auf einen K\195\164ufer, um diesen anzufl\195\188stern bzw. wenn der Senden-Reiter in Nachrichten ge\195\182ffnet ist, f\195\188gt es den Namen in die Adresszeile ein.",
   sortTimeTip = "Anklicken um nach Verkaufszeit zu sortieren.",
   sortPriceTip = "Anklicken um nach Verkaufspreis zu sortieren.",
+  statsTooltip = "Open statistics window",
+  sizeTooltip = "Change window size",
   timeSecondsAgo = "<<1[Soeben/Vor %d Sekunden]>>",
   timeMinutesAgo = "<<1[Vor 1 Minute/Vor %d Minuten]>>",
   timeHoursAgo = "<<1[Vor 1 Stunde/Vor %d Stunden]>>",
@@ -66,6 +68,14 @@ Shopkeeper.i18n.localized = {
   thousandsSep = ".",
   showUnitPrice = "Zeigt St\195\188ckpreis",
   showTotalPrice = "Zeigt Gesamtpreis",
+  statsTitle = "Sales Statistics",
+  statsTimeAll = "Using all data",
+  statsTimeSome = "Going back %d days",
+  statsItemsSold = "Items sold: %d",
+  statsTotalGold = "Total gold: %s |t16:16:EsoUI/Art/currency/currency_gold.dds|t (%s |t16:16:EsoUI/Art/currency/currency_gold.dds|t per day)",
+  statsBiggest = "Gr\195\182\195\159ter Verkaufs: %s (%s |t16:16:EsoUI/Art/currency/currency_gold.dds|t)",
+  statsDays = "Tagen: ",
 }

-ZO_CreateStringId("SI_BINDING_NAME_SHOPKEEPER_TOGGLE", "Ein-/Ausblenden des Fensters")
\ No newline at end of file
+ZO_CreateStringId("SI_BINDING_NAME_SHOPKEEPER_TOGGLE", "Ein-/Ausblenden des Fensters")
+ZO_CreateStringId("SI_BINDING_NAME_SHOPKEEPER_STATS_TOGGLE", "Show/Hide Stats Window")
\ No newline at end of file
diff --git a/i18n/EN.lua b/i18n/EN.lua
index 27a2253..ac29047 100644
--- a/i18n/EN.lua
+++ b/i18n/EN.lua
@@ -1,5 +1,5 @@
 -- Shopkeeper English Localization File
--- Last Updated July 14, 2014
+-- Last Updated August 4, 2014
 -- Written July 2014 by Dan Stone (@khaibit) - dankitymao@gmail.com
 -- Released under terms in license accompanying this file.
 -- Distribution without license is prohibited!
@@ -36,6 +36,8 @@ Shopkeeper.i18n.localized = {
   buyerTooltip = "Double-click on a buyer to contact them.",
   sortTimeTip = "Click to sort by sale time.",
   sortPriceTip = "Click to sort by sale price.",
+  statsTooltip = "Open statistics window",
+  sizeTooltip = "Change window size",
   timeSecondsAgo = "<<1[Just now/%d seconds ago]>>",
   timeMinutesAgo = "<<1[%d minute ago/%d minutes ago]>>",
   timeHoursAgo = "<<1[%d hour ago/%d hours ago]>>",
@@ -50,12 +52,12 @@ Shopkeeper.i18n.localized = {
   refreshWait = "Please wait a minute or so between refreshes.",
   resetLabel = "Reset",
   resetDone = "Sales history reset.",
-  salesAlert = "You have sold %s x%d for %sG from %s %s.",
-  salesAlertSingle = "You have sold %s for %sG from %s %s.",
-  salesAlertColor = "|cFFFFFFYou have sold %s x%d for |cD5B526%sG |cFFFFFFfrom %s %s.",
-  salesAlertColorSingle = "|cFFFFFFYou have sold %s for |cD5B526%sG |cFFFFFFfrom %s %s.",
-  salesGroupAlert = "You have sold %d items totaling %sG from guild stores.",
-  salesGroupAlertColor = "|cFFFFFFYou have sold %d items totaling |cD5B526%sG |cFFFFFFfrom guild stores.",
+  salesAlert = "You have sold %s x%d for %s " .. string.format("|t16:16:%s|t","EsoUI/Art/currency/currency_gold.dds") .. " from %s %s.",
+  salesAlertSingle = "You have sold %s for %s " .. string.format("|t16:16:%s|t","EsoUI/Art/currency/currency_gold.dds") .. " from %s %s.",
+  salesAlertColor = "|cFFFFFFYou have sold %s x%d for |cD5B526%s " .. string.format("|t16:16:%s|t","EsoUI/Art/currency/currency_gold.dds") .. " |cFFFFFFfrom %s %s.",
+  salesAlertColorSingle = "|cFFFFFFYou have sold %s for |cD5B526%s " .. string.format("|t16:16:%s|t","EsoUI/Art/currency/currency_gold.dds") .. " |cFFFFFFfrom %s %s.",
+  salesGroupAlert = "You have sold %d items totaling %s " .. string.format("|t16:16:%s|t","EsoUI/Art/currency/currency_gold.dds") .. " from guild stores.",
+  salesGroupAlertColor = "|cFFFFFFYou have sold %d items totaling |cD5B526%s " .. string.format("|t16:16:%s|t","EsoUI/Art/currency/currency_gold.dds") .. " |cFFFFFFfrom guild stores.",
   alertOptionsName = "Sales Alert Options",
   alertOptionsTip = "Options for alert type and sound.",
   alertTypeName = "Alert Sound",
@@ -65,6 +67,14 @@ Shopkeeper.i18n.localized = {
   thousandsSep = ",",
   showUnitPrice = "Show Unit Price",
   showTotalPrice = "Show Total Price",
+  statsTitle = "Sales Statistics",
+  statsTimeAll = "Using all data",
+  statsTimeSome = "Going back %d days",
+  statsItemsSold = "Items sold: %d",
+  statsTotalGold = "Total gold: %s |t16:16:EsoUI/Art/currency/currency_gold.dds|t (%s |t16:16:EsoUI/Art/currency/currency_gold.dds|t per day)",
+  statsBiggest = "Biggest sale: %s (%s |t16:16:EsoUI/Art/currency/currency_gold.dds|t)",
+  statsDays = "Days: ",
 }

-ZO_CreateStringId("SI_BINDING_NAME_SHOPKEEPER_TOGGLE", "Show/Hide Shopkeeper Window")
\ No newline at end of file
+ZO_CreateStringId("SI_BINDING_NAME_SHOPKEEPER_TOGGLE", "Show/Hide Main Window")
+ZO_CreateStringId("SI_BINDING_NAME_SHOPKEEPER_STATS_TOGGLE", "Show/Hide Stats Window")
diff --git a/i18n/FR.lua b/i18n/FR.lua
index d4f5fda..8ac2301 100644
--- a/i18n/FR.lua
+++ b/i18n/FR.lua
@@ -1,5 +1,5 @@
 -- Shopkeeper French Localization File
--- Last Updated July 14, 2014
+-- Last Updated August 4, 2014
 -- Written July 2014 by Dan Stone (@khaibit) - dankitymao@gmail.com
 -- Released under terms in license accompanying this file.
 -- Distribution without license is prohibited!
@@ -36,6 +36,8 @@ Shopkeeper.i18n.localized = {
   buyerTooltip = "Double-click on a buyer to contact them.",
   sortTimeTip = "Click to sort by sale time.",
   sortPriceTip = "Click to sort by sale price.",
+  statsTooltip = "Open statistics window",
+  sizeTooltip = "Change window size",
   timeSecondsAgo = "<<1[Just now/%d seconds ago]>>",
   timeMinutesAgo = "<<1[%d minute ago/%d minutes ago]>>",
   timeHoursAgo = "<<1[%d hour ago/%d hours ago]>>",
@@ -65,6 +67,14 @@ Shopkeeper.i18n.localized = {
   thousandsSep = ".",
   showUnitPrice = "Show Unit Price",
   showTotalPrice = "Show Total Price",
+  statsTitle = "Sales Statistics",
+  statsTimeAll = "Using all data",
+  statsTimeSome = "Going back %d days",
+  statsItemsSold = "Items sold: %d",
+  statsTotalGold = "Total gold: %s |t16:16:EsoUI/Art/currency/currency_gold.dds|t (%s |t16:16:EsoUI/Art/currency/currency_gold.dds|t per day)",
+  statsBiggest = "Biggest sale: %s (%s |t16:16:EsoUI/Art/currency/currency_gold.dds|t)",
+  statsDays = "Days: ",
 }

-ZO_CreateStringId("SI_BINDING_NAME_SHOPKEEPER_TOGGLE", "Show/Hide Shopkeeper Window")
\ No newline at end of file
+ZO_CreateStringId("SI_BINDING_NAME_SHOPKEEPER_TOGGLE", "Show/Hide Shopkeeper Window")
+ZO_CreateStringId("SI_BINDING_NAME_SHOPKEEPER_STATS_TOGGLE", "Show/Hide Stats Window")
\ No newline at end of file
diff --git a/readme b/readme
index 7cddb51..1773a47 100644
--- a/readme
+++ b/readme
@@ -4,22 +4,19 @@ trademarks or trademarks of ZeniMax Media Inc. in the United States and/or
 other countries. All rights reserved.


-Known Issues July 16, 2014:
-
-    Due to patch 1.2.3 changing the format for item links, it is not possible to directly parse the name
- of an item from the link anymore. As such, until I or someone else can figure out a way to do so, searching
- item names will not be possible. UPDATE: On PTS 1.3.0, a new API function has been added that does EXACTLY
- what I need - once Update Three goes live, I will be able to have item searching fully operational! I will be
- releasing version 0.3 sometime during the week of July 14, but expect another update the day 1.3.0 hits the live
- servers as I've written all the code needed to implement item searching and just need to un-comment it to enable
- it once the patch goes live.
-
+Known Issues August 4, 2014:
     The API calls to retrieve store sales histories currently return the most recent 100 sales from each guild. The
  function to retrieve older items behaves...inconsistently. If you are part of a busy guild and/or log on infrequently,
  Shopkeeper may miss some of your sales. If this is the case, you'll notice your sales also don't show up in the actual
  guild sales history window either - I can't report on what the servers won't tell me =\ I'm aware of it and trying to
  find a workaround!

+Changelog for 0.9 (version jump due to being nearly feature-complete):
+  Added a new smaller view mode for the main window
+  Added sales stats!  Click on the "list" icon at the top of the main window to toggle.
+  Search field now searches item names
+  Fixes for one case where items bought close together don't all trigger alerts; there are still some odd cases I'm working on
+
 Changelog for 0.3:
   Added ability to toggle between gross/total sales price and per-unit price displays
   Better support for multiple accounts that use the same computer