Restructuring

Wobin [05-30-14 - 17:08]
Restructuring
Moved all into LibSort
Filename
ItemSort.lua
ItemSort.txt
ItemSort.xml
LibSort-1.0.lua
Libs/LibSort/LibSort-1.0.lua
Libs/LibSort/LibStub.lua
Libs/LibSort/README.md
Libs/LibStub/LibStub.lua
README.md
diff --git a/ItemSort.lua b/ItemSort.lua
index 6370a34..0201fb5 100644
--- a/ItemSort.lua
+++ b/ItemSort.lua
@@ -1,5 +1,7 @@
 ItemSort = {}

+local LibSort = LibStub("LibSort-1.0", 1)
+
 local watchedSlots = {[SLOT_TYPE_GUILD_BANK_ITEM] = true, [SLOT_TYPE_ITEM] = true, [SLOT_TYPE_BANK_ITEM] = true }

 local IS_WEAPON = { [EQUIP_TYPE_MAIN_HAND] = true, [EQUIP_TYPE_OFF_HAND] = true, [EQUIP_TYPE_ONE_HAND] = true, [EQUIP_TYPE_TWO_HAND] = true}
@@ -48,57 +50,42 @@ function ItemSort:Loaded(...)
 	local eventCode, addonName = ...
 	if addonName ~= "ItemSort" then return end

-	ItemSort.hookedFunction = ZO_Inventory_BindSlot
-	ZO_Inventory_BindSlot =
-		function(...)
-			ItemSort.hookedFunction(...)
-			local control, slotType, index, bag = ...
-			local slot = control:GetParent()
-
-			if not slot or not slot.dataEntry or not slot.dataEntry.data then return end
-
-			-- Damnit, it's a subjective item level
-			local iLevel = GetItemLevel(bag, index)
-
-			slot.dataEntry.data.subjectiveItemLevel = iLevel
-			slot.dataEntry.data.weaponType = 0
-			slot.dataEntry.data.armorType = 0
-
-			if watchedSlots[slotType] then
-				local _, _, _, _, _, equipType = GetItemInfo(bag, index)
-				slot.dataEntry.data.armorEquipType = ARMOUR_ORDER[equipType] or 0
-				if equipType > 0 then
-					local link = GetItemLink(bag, index)
-
-					if IS_WEAPON[equipType] then
-						slot.dataEntry.data.weaponType = WEAPON_ORDER[GetItemWeaponType(link)]
-					else
-						slot.dataEntry.data.armorType = GetItemArmorType(link)
-					end
-				end
-			end
-		end
-	local sortKeys = ZO_Inventory_GetDefaultHeaderSortKeys()
-	sortKeys["weaponType"] = {isNumeric = true, tiebreaker = "armorEquipType"}
-	sortKeys["armorEquipType"] = {isNumeric = true, tiebreaker = "armorType"}
-	sortKeys["armorType"] = {isNumeric = true, tiebreaker = "subjectiveItemLevel"}
-	sortKeys["subjectiveItemLevel"] = {isNumeric = true, tiebreaker = "name"}
-	sortKeys["age"] = { tiebreaker = "weaponType", isNumeric = true }
-
-	zo_callLater(ItemSort.SetupArrows, 100)
+
+	LibSort:RegisterDefaultOrder("Item Sort", {"Weapon Type", "Armour Equip Type", "Armour Type", "Subjective Level"})
+
+	LibSort:Register("Item Sort", "Subjective Level", "The calculated subjective level", "subjectiveLevel", function(slotType, bag, index) return GetItemLevel(bag, index) end)
+	LibSort:Register("Item Sort", "Weapon Type", "The type of weapon", "weaponType", function(...) return ItemSort:WeaponType(...) end)
+	LibSort:Register("Item Sort", "Armour Type", "The weight of armour", "armorType", function(...) return ItemSort:ArmorType(...) end)
+	LibSort:Register("Item Sort", "Armour Equip Type", "The type of armour", "armorEquipType", function(...) return ItemSort:ArmourEquipType(...) end)
 end

-function ItemSort:SetupArrows()
+function ItemSort:WeaponType(slotType, bag, index)
+	if watchedSlots[slotType] then
+		local _, _, _, _, _, equipType = GetItemInfo(bag, index)
+		if equipType > 0 then
+			if IS_WEAPON[equipType] then
+				return WEAPON_ORDER[GetItemWeaponType(GetItemLink(bag, index))]
+			end
+		end
+	end
+end

-	ItemSortBank:SetParent(ZO_PlayerBankSortBy)
-	PLAYER_INVENTORY.inventories[INVENTORY_BANK].sortHeaders:AddHeader(ItemSortBank)
-
-	ItemSortGuild:SetParent(ZO_GuildBankSortBy)
-	PLAYER_INVENTORY.inventories[INVENTORY_GUILD_BANK].sortHeaders:AddHeader(ItemSortGuild)
-
+function ItemSort:ArmorType(slotType, bag, index)
+	if watchedSlots[slotType] then
+		local _, _, _, _, _, equipType = GetItemInfo(bag, index)
+		if equipType <= 9 and equipType > 0  then
+			return GetItemArmorType(GetItemLink(bag, index))
+		end
+	end
+end

-	ZO_PreHook(PLAYER_INVENTORY, "ChangeSort", function(self, key, inventoryType, order) d(inventoryType) PLAYER_INVENTORY.inventories[inventoryType].sortFn = nil end)
+function ItemSort:ArmourEquipType(slotType, bag, index)
+	if watchedSlots[slotType] then
+		local _, _, _, _, _, equipType = GetItemInfo(bag, index)
+		return ARMOUR_ORDER[equipType]
+	end
 end


+
 EVENT_MANAGER:RegisterForEvent("ItemSortLoaded", EVENT_ADD_ON_LOADED, function(...) ItemSort:Loaded(...) end)
\ No newline at end of file
diff --git a/ItemSort.txt b/ItemSort.txt
index 3ee070f..92c90d7 100644
--- a/ItemSort.txt
+++ b/ItemSort.txt
@@ -3,5 +3,7 @@
 ## Version: @project-version@
 ## APIVersion: 100004

+Libs/LibStub/LibStub-1.0.lua
+Libs/LibSort/LibSort-1.0.lua
+
 ItemSort.lua
-ItemSort.xml
\ No newline at end of file
diff --git a/ItemSort.xml b/ItemSort.xml
deleted file mode 100644
index ebb484e..0000000
--- a/ItemSort.xml
+++ /dev/null
@@ -1,44 +0,0 @@
-<GuiXml>
-    <Controls>
-        <Control name="ItemSortBank" inherits="ZO_SortHeaderIcon">
-            <Dimensions x="16" y="32" />
-            <Anchor point="RIGHT" offsetX="-15" relativeTo="ZO_PlayerBankSortByName" relativePoint="LEFT"/>
-            <OnInitialized>
-                ZO_SortHeader_SetMouseCallback(self,    function(header, eventType)
-                                                            if(eventType == "OnMouseEnter") then
-                                                                InitializeTooltip(InformationTooltip, self, BOTTOMRIGHT, 0, 32)
-                                                                SetTooltipText(InformationTooltip, "Sort")
-                                                            else
-                                                                ClearTooltip(InformationTooltip)
-                                                            end
-                                                        end)
-                ZO_PlayerInventory_InitSortHeaderIcon(self,
-                                    "EsoUI/Art/Miscellaneous/list_sortHeader_icon_neutral.dds",
-                                    "EsoUI/Art/Miscellaneous/list_sortHeader_icon_sortUp.dds",
-                                    "EsoUI/Art/Miscellaneous/list_sortHeader_icon_sortDown.dds",
-                                    "EsoUI/Art/Miscellaneous/list_sortHeader_icon_over.dds",
-                                    "age")
-            </OnInitialized>
-        </Control>
-        <Control name="ItemSortGuild" inherits="ZO_SortHeaderIcon">
-            <Dimensions x="16" y="32" />
-            <Anchor point="RIGHT" offsetX="-15" relativeTo="ZO_GuildBankSortByName" relativePoint="LEFT"/>
-            <OnInitialized>
-                ZO_SortHeader_SetMouseCallback(self,    function(header, eventType)
-                                                            if(eventType == "OnMouseEnter") then
-                                                                InitializeTooltip(InformationTooltip, self, BOTTOMRIGHT, 0, 32)
-                                                                SetTooltipText(InformationTooltip, "Sort")
-                                                            else
-                                                                ClearTooltip(InformationTooltip)
-                                                            end
-                                                        end)
-                 ZO_PlayerInventory_InitSortHeaderIcon(self,
-                                    "EsoUI/Art/Miscellaneous/list_sortHeader_icon_neutral.dds",
-                                    "EsoUI/Art/Miscellaneous/list_sortHeader_icon_sortUp.dds",
-                                    "EsoUI/Art/Miscellaneous/list_sortHeader_icon_sortDown.dds",
-                                    "EsoUI/Art/Miscellaneous/list_sortHeader_icon_over.dds",
-                                    "age")
-            </OnInitialized>
-        </Control>
-    </Controls>
-</GuiXml>
\ No newline at end of file
diff --git a/LibSort-1.0.lua b/LibSort-1.0.lua
deleted file mode 100644
index e75f536..0000000
--- a/LibSort-1.0.lua
+++ /dev/null
@@ -1,65 +0,0 @@
-local MAJOR, MINOR = "LibSort-1.0", 1
-local LibSort, oldminor = LibStub:NewLibrary(MAJOR, MINOR)
-if not LibSort then return end	--the same or newer version of this lib is already loaded into memory
-
-local defaultType = {["number"] = 0, ["boolean"] = false, ["string"] = ""}
-
-LibSort.RegisteredCallbacks = {}
-
-function LibStub:Loaded(event, name)
-    if name ~= "ZO_Ingame" then return end
-
-    self.savedVars = ZO_SavedVars:New("ZO_Ingame_SavedVariables", 1, "LibSort")
-    self.control:UnregisterForEvent(EVENT_ADD_ON_LOADED)
-
-    -- Hook for BindSlot allows us to inject sort datapoints
-	self.hookedBindSlot = ZO_Inventory_BindSlot
-	ZO_Inventory_BindSlot =
-		function(...)
-			-- First setup the default ZO datapoints
-			self.hookedBindSlot(...)
-			local control, slotType, index, bag = ...
-			local slot = control:GetParent()
-
-			-- If, for whatever reason, we're not actually dealing with sortable stuff...
-			if not slot or not slot.dataEntry or not slot.dataEntry.data then return end
-
-			-- Now inject our requirements
-			for addon, callbacks in pairs(LibSort.RegisteredCallbacks) do
-				for name, data in pairs(callbacks) do
-					slot.dataEntry.data[data.key] = data.func(slotType, bag, index) or defaultType[data.dataType]
-				end
-			end
-		end
-end
-
-EVENT_MANAGER:RegisterForEvent("LibSortLoaded", EVENT_ADD_ON_LOADED, function(...) LibSort:Loaded(...) end)
-
---------- API ---------
-
-function LibSort:Unregister(addonName, name)
-	self.RegisteredCallbacks[name] = nil
-end
-
-function LibSort:Register(addonName, name, desc, key, func)
-	self:RegisterNumeric(addonName, name, key, func)
-end
-
-function LibSort:RegisterNumeric(addonName, name, desc, key, func)
-	if not self.RegisteredCallbacks[addonName] then self.RegisteredCallbacks[addonName] = {} end
-	self.RegisteredCallbacks[addonName][name] = {key = key, func = func, desc = desc, dataType = "number"}
-end
-
-function LibSort:RegisterBoolean(addonName, name, desc, key, func)
-	if not self.RegisteredCallbacks[addonName] then self.RegisteredCallbacks[addonName] = {} end
-	self.RegisteredCallbacks[addonName][name] = {key = key, func = func, desc = desc, dataType = "boolean"}
-end
-
-function LibSort:RegisterString(addonName, name, desc, key, func)
-	if not self.RegisteredCallbacks[addonName] then self.RegisteredCallbacks[addonName] = {} end
-	self.RegisteredCallbacks[addonName][name] = {key = key, func = func, desc = desc, dataType = "string"}
-end
-
-function LibSort:RegisterDefaultOrder(addonName, keyTable)
-	self.DefaultOrders[addonName] = keyTable
-end
diff --git a/Libs/LibSort/LibSort-1.0.lua b/Libs/LibSort/LibSort-1.0.lua
new file mode 100644
index 0000000..76fd3de
--- /dev/null
+++ b/Libs/LibSort/LibSort-1.0.lua
@@ -0,0 +1,190 @@
+local MAJOR, MINOR = "LibSort-1.0", 1
+local LibSort, oldminor = LibStub:NewLibrary(MAJOR, MINOR)
+if not LibSort then return end	--the same or newer version of this lib is already loaded into memory
+
+LIBSORT = LibSort
+
+-- Lookup Tables ---
+local wm = WINDOW_MANAGER
+local defaultType = {["isNumeric"] = 0, ["isBoolean"] = false, ["isString"] = ""}
+local inventoryTypes = {[INVENTORY_BACKPACK] = ZO_PlayerInventoryBackpack, [INVENTORY_BANK] = ZO_PlayerBankBackpack, [INVENTORY_GUILD_BANK] = ZO_GuildBankBackpack}
+
+
+--- Utility functions ---
+
+local function removeSpaces(name)
+	return name:gsub(" ","")
+end
+
+local function makePrefix(name)
+	return removeSpaces(name) .. "_"
+end
+
+function LibSort:Debug(msg)
+	if LibSort.isDebugging then	d("[LibSort]: "..msg) 	end
+end
+
+--- Storage variables ---
+LibSort.RegisteredCallbacks = {}
+LibSort.AddonOrder = {}
+LibSort.DefaultOrders = {}
+
+--- Arrow generation ---
+
+function LibSort:SetupArrows()
+	LibSort.ItemSortBank = wm:CreateControlFromVirtual("ItemSortBank", ZO_PlayerBankSortBy, "ZO_SortHeaderIcon")
+	LibSort.ItemSortBank:SetDimensions(16, 32)
+	LibSort.ItemSortBank:SetAnchor(RIGHT, ZO_PlayerBankSortByName, LEFT, -15)
+	ZO_SortHeader_SetMouseCallback(ItemSortBank,    function(header, eventType)
+                                                            if(eventType == "OnMouseEnter") then
+                                                                InitializeTooltip(InformationTooltip, ItemSortBank, BOTTOMRIGHT, 0, 32)
+                                                                SetTooltipText(InformationTooltip, "Sort")
+                                                            else
+                                                                ClearTooltip(InformationTooltip)
+                                                            end
+                                                        end)
+    ZO_PlayerInventory_InitSortHeaderIcon(LibSort.ItemSortBank,
+                                    "EsoUI/Art/Miscellaneous/list_sortHeader_icon_neutral.dds",
+                                    "EsoUI/Art/Miscellaneous/list_sortHeader_icon_sortUp.dds",
+                                    "EsoUI/Art/Miscellaneous/list_sortHeader_icon_sortDown.dds",
+                                    "EsoUI/Art/Miscellaneous/list_sortHeader_icon_over.dds",
+                                    "age")
+
+    PLAYER_INVENTORY.inventories[INVENTORY_BANK].sortHeaders:AddHeader(ItemSortBank)
+
+    LibSort.ItemSortGuild = wm:CreateControlFromVirtual("ItemSortGuild", ZO_GuildBankSortBy, "ZO_SortHeaderIcon")
+	LibSort.ItemSortGuild:SetDimensions(16, 32)
+	LibSort.ItemSortGuild:SetAnchor(RIGHT, ZO_GuildBankSortByName, LEFT, -15)
+	ZO_SortHeader_SetMouseCallback(ItemSortGuild,    function(header, eventType)
+                                                            if(eventType == "OnMouseEnter") then
+                                                                InitializeTooltip(InformationTooltip, ItemSortGuild, BOTTOMRIGHT, 0, 32)
+                                                                SetTooltipText(InformationTooltip, "Sort")
+                                                            else
+                                                                ClearTooltip(InformationTooltip)
+                                                            end
+                                                        end)
+    ZO_PlayerInventory_InitSortHeaderIcon(LibSort.ItemSortGuild,
+                                    "EsoUI/Art/Miscellaneous/list_sortHeader_icon_neutral.dds",
+                                    "EsoUI/Art/Miscellaneous/list_sortHeader_icon_sortUp.dds",
+                                    "EsoUI/Art/Miscellaneous/list_sortHeader_icon_sortDown.dds",
+                                    "EsoUI/Art/Miscellaneous/list_sortHeader_icon_over.dds",
+                                    "age")
+    PLAYER_INVENTORY.inventories[INVENTORY_GUILD_BANK].sortHeaders:AddHeader(ItemSortGuild)
+end
+
+
+
+--- Main functions ---
+
+LibSort.sortKeys = ZO_Inventory_GetDefaultHeaderSortKeys()
+
+function LibSort:InjectKeys()
+	for addon, callbacks in pairs(self.RegisteredCallbacks) do
+		for name, data in pairs(callbacks) do
+			if not self.sortKeys[data.key] then
+				self.sortKeys[data.key] = {}
+				self.sortKeys[data.key][data.dataType] = true
+			end
+		end
+	end
+	self:ReOrderKeys()
+end
+
+function LibSort:ReOrderKeys()
+	local first
+	local previous
+	for i, addonName in ipairs(self.AddonOrder) do
+		if self.DefaultOrders[addonName] then
+			for _, name in ipairs(self.DefaultOrders[addonName]) do
+				local data = self.RegisteredCallbacks[addonName][name]
+				if data then -- we skip the ones we haven't registered yet
+					first, previous = self:SetKeyOrder(first, previous, data)
+				end
+			end
+		else
+			for name, data in pairs(self.RegisteredCallbacks[addonName]) do
+				first, previous = self:SetKeyOrder(first, previous, data)
+			end
+		end
+	end
+	self.sortKeys[previous].tiebreaker = "name"
+	PLAYER_INVENTORY:ChangeSort("age", INVENTORY_BACKPACK, true)
+	PLAYER_INVENTORY:ChangeSort("age", INVENTORY_BANK, true)
+	PLAYER_INVENTORY:ChangeSort("age", INVENTORY_GUILD_BANK, true)
+end
+
+function LibSort:SetKeyOrder(first, previous, data)
+	if not first then
+		first = true
+		self.sortKeys["age"].tiebreaker = data.key
+		self.sortKeys["age"].reverseTiebreakerSortOrder = nil
+	else
+		if previous then
+			self.sortKeys[previous].tiebreaker = data.key
+		end
+	end
+	previous = data.key
+	return first, previous
+end
+
+function LibSort:ProcessInventory(inventoryType)
+	local container = inventoryTypes[inventoryType]
+	if not container then return end
+	for i, slot in ipairs(container.data) do
+		local slotType, bag, index = slot.data.slotType, slot.data.bagId, slot.data.slotIndex
+		for addon, callbacks in pairs(self.RegisteredCallbacks) do
+			for name, data in pairs(callbacks) do
+				if not slot.data[data.key] then
+					slot.data[data.key] = data.func(slotType, bag, index) or defaultType[data.dataType]
+				end
+			end
+		end
+	end
+end
+
+function LibSort:Loaded(event, name)
+    if name ~= "ZO_Ingame" then return end
+
+    self.savedVars = ZO_SavedVars:New("ZO_Ingame_SavedVariables", 1, "LibSort")
+    EVENT_MANAGER:UnregisterForEvent("LibSortLoaded", EVENT_ADD_ON_LOADED)
+
+	ZO_PreHook(PLAYER_INVENTORY, "ApplySort", function(self, inventoryType) LibSort:ProcessInventory(inventoryType) end)
+	zo_callLater(function() LIBSORT:SetupArrows() end, 3000)
+end
+
+EVENT_MANAGER:RegisterForEvent("LibSortLoaded", EVENT_ADD_ON_LOADED, function(...) LibSort:Loaded(...) end)
+
+--------- API ---------
+
+function LibSort:Unregister(addonName, name)
+	if self.RegisteredCallbacks[addonName] then
+		self.RegisteredCallbacks[addonName][name] = nil
+	end
+end
+
+function LibSort:Register(addonName, name, desc, key, func, dataType)
+	if not dataType then dataType = "isNumeric" end
+	if not self.RegisteredCallbacks[addonName] then self.RegisteredCallbacks[addonName] = {} table.insert(self.AddonOrder, addonName) end
+	self.RegisteredCallbacks[addonName][name] = {key = makePrefix(addonName)..key, func = func, desc = desc, dataType = dataType}
+	self:InjectKeys()
+end
+
+function LibSort:RegisterNumeric(addonName, name, desc, key, func)
+	self:Register(addonName, name, desc, key, func, "isNumeric")
+end
+
+function LibSort:RegisterBoolean(addonName, name, desc, key, func)
+	self:Register(addonName, name, desc, key, func, "isBoolean")
+end
+
+function LibSort:RegisterString(addonName, name, desc, key, func)
+	self:Register(addonName, name, desc, key, func, "isString")
+end
+
+function LibSort:RegisterDefaultOrder(addonName, keyTable)
+	self.DefaultOrders[addonName] = keyTable
+end
+
+function LibSort:SetDebugging(flag)
+	self.isDebugging = flag
+end
\ No newline at end of file
diff --git a/Libs/LibSort/LibStub.lua b/Libs/LibSort/LibStub.lua
new file mode 100644
index 0000000..bfd96df
--- /dev/null
+++ b/Libs/LibSort/LibStub.lua
@@ -0,0 +1,34 @@
+-- LibStub is a simple versioning stub meant for use in Libraries.  http://www.wowace.com/wiki/LibStub for more info
+-- LibStub is hereby placed in the Public Domain Credits: Kaelten, Cladhaire, ckknight, Mikk, Ammo, Nevcairiel, joshborke
+-- LibStub developed for World of Warcraft by above members of the WowAce community.
+-- Ported to Elder Scrolls Online by Seerah
+
+local LIBSTUB_MAJOR, LIBSTUB_MINOR = "LibStub", 1  -- NEVER MAKE THIS AN SVN REVISION! IT NEEDS TO BE USABLE IN ALL REPOS!
+local LibStub = _G[LIBSTUB_MAJOR]
+
+local strformat = string.format
+if not LibStub or LibStub.minor < LIBSTUB_MINOR then
+	LibStub = LibStub or {libs = {}, minors = {} }
+	_G[LIBSTUB_MAJOR] = LibStub
+	LibStub.minor = LIBSTUB_MINOR
+
+	function LibStub:NewLibrary(major, minor)
+		assert(type(major) == "string", "Bad argument #2 to `NewLibrary' (string expected)")
+		minor = assert(tonumber(zo_strmatch(minor, "%d+")), "Minor version must either be a number or contain a number.")
+
+		local oldminor = self.minors[major]
+		if oldminor and oldminor >= minor then return nil end
+		self.minors[major], self.libs[major] = minor, self.libs[major] or {}
+		return self.libs[major], oldminor
+	end
+
+	function LibStub:GetLibrary(major, silent)
+		if not self.libs[major] and not silent then
+			error(("Cannot find a library instance of %q."):strformat(tostring(major)), 2)
+		end
+		return self.libs[major], self.minors[major]
+	end
+
+	function LibStub:IterateLibraries() return pairs(self.libs) end
+	setmetatable(LibStub, { __call = LibStub.GetLibrary })
+end
diff --git a/Libs/LibSort/README.md b/Libs/LibSort/README.md
new file mode 100644
index 0000000..b2136a1
--- /dev/null
+++ b/Libs/LibSort/README.md
@@ -0,0 +1,124 @@
+#LibSort 1.0
+
+**Table of Contents**
+- [Rationale](#user-content-rationale)
+    - [API](#user-content-api)
+        - [Register](#user-content-register)
+        - [RegisterNumeric](#user-content-registernumeric)
+        - [RegisterBoolean](#user-content-registerboolean)
+        - [RegisterString](#user-content-registerstring)
+        - [Unregister](#user-content-unregister)
+        - [RegisterDefaultOrder](#user-content-registerdefaultorder)
+
+##Rationale
+
+Currently, the way ESO sorts items is essentially by column, you have a data point for each item, eg, **name** and you sort the list of objects by that data point.
+
+One thing that this default sorting mechanism offers, however, is a tiebreaker option. If we're sorting by **name** and a tiebreaker is required, because we are comparing two items with the same name, it falls to the tiebreaker datapoint to determine order of result, eg **stackSize**
+
+Now the general sorting of items in the real world doesn't just fall to a single data point, or two. It generally requires a series of different datapoints to sort by, and we can take advantage of this 'tiebreaker' to
+chain together lists of different datapoints to sort by. So we can sort by **Name**, then by **StackSize**, then by **SlotId**.
+
+That's well and fine for what's existing in the current set of datapoints, however we have access to a lot more information about the item than is currently being stored.
+
+To do this, however, we need to inject the required datapoints into the list object so that it has the required information to process the sort order as ascertained by our adjustment of how the tiebreakers chain together.
+
+	ZO_Inventory_BindSlot(control, slotType, index, bag)
+
+is the function we need to hook. It's a C side function, which means we don't actually know what it does exactly, but we don't need to. We might be able to prehook it, but chances are it recreates the data object so any
+prehooking work would be wiped out. So we posthook it.
+
+We now have information about the item, where it's from (*slotType*) and where it is (*bag* and *index*)
+
+And thanks to the new API, we have two new functions that will return information about the item in regards to what sort of weapon or armour it is.
+
+So as an example, we'll use the new Weapon info function to inject data to allow the game to sort by weapontype
+
+	GetItemWeaponType(link)
+
+is the function we're using to obtain the information required.
+
+Or at least through
+
+	local link = GetItemLink(bag, index)
+	local weaponType = GetItemWeaponType(link)
+
+If we then inject this information into the data object
+
+	control:GetParent().dataEntry.data.weaponType = weaponType
+
+we can then include another entry into the sortKeys for default header
+
+	local sortKeys = ZO_Inventory_GetDefaultHeaderSortKeys()
+	sortKeys["weaponType"] = {isNumeric = true, tiebreaker = "name"}
+
+and we will end up sorting by the type of weapon returned by that function. (Note that as it's a pure number, and not necessarily in the order you may want, you'll have to actually adjust the real value of the datapoint to something more suitable)
+
+---
+
+In any case, now that we know what we need to do, this library should do most of the heavy lifting for you. Chances are I'll have to give it it's own Settings panel so people can reorder the sort order as they wish, but you should be able to register your addon to allow data injection and process the index/bag combinations to store whatever datapoints you want.
+
+---
+##API
+
+###Register
+This will register a numeric sortKey
+
+    LibSort:Register(addonName, name, desc, key, func)
+
+(Note this is an alias for **RegisterNumeric** and will assume a numeric sortKey)
+- *addonName* - The name of the registering addon
+    + Example: "Item Sort"
+- *name* - A unique registration name
+    + Example: "ISWeaponSort"
+- *desc* - A description of how the sort applies
+    + Example: "Will sort by Weapon Type"
+- *key* - A unique key used to identify the datapoint
+    + Example: "weaponType"
+- *func* - The function to call to retrieve the sort value. Function signature **needs** to be (slotType, bag, index)
+    + Example: ItemSort.WeaponSort
+
+###RegisterNumeric
+This will register a numeric sortKey
+
+    LibSort:RegisterNumeric(addonName, name, desc, key, func)
+Arguments as above
+
+###RegisterBoolean
+This will register a boolean sortKey
+
+    LibSort:RegisterBoolean(addonName, name, desc, key, func)
+Arguments as above
+
+###RegisterString
+This will register a string sortKey
+
+    LibSort:RegisterString(addonName, name, desc, key, func)
+Arguments as above
+
+###Unregister
+This will unregister a sortKey registration
+
+    LibSort:Unregister(addonName, name)
+
+- *addonName* - The name of the registering addon
+    + Example: "Item Sort"
+- *name* - A unique registration name
+    + Example: "ISWeaponSort"
+
+###RegisterDefaultOrder
+Your addon may have multiple registrations, and this function will allow you to indicate what order you want them in as a block
+
+    LibSort:RegisterDefaultOrder(addonName, keyTable)
+
+- *addonName* -The name of the registering addon
+    + Example: "Item Sort"
+- *keyTable* - A table indicating the order of sortKeys for this addon
+    + Example: {"weaponType", "armorEquipType", "armorType", "subjectiveItemLevel"}
+
+###SetDebugging
+Set the debug flag for the library
+
+    LibSort:SetDebugging(flag)
+
+- *flag* - a boolean indicating if you wish to have debug messages
\ No newline at end of file
diff --git a/Libs/LibStub/LibStub.lua b/Libs/LibStub/LibStub.lua
new file mode 100644
index 0000000..bfd96df
--- /dev/null
+++ b/Libs/LibStub/LibStub.lua
@@ -0,0 +1,34 @@
+-- LibStub is a simple versioning stub meant for use in Libraries.  http://www.wowace.com/wiki/LibStub for more info
+-- LibStub is hereby placed in the Public Domain Credits: Kaelten, Cladhaire, ckknight, Mikk, Ammo, Nevcairiel, joshborke
+-- LibStub developed for World of Warcraft by above members of the WowAce community.
+-- Ported to Elder Scrolls Online by Seerah
+
+local LIBSTUB_MAJOR, LIBSTUB_MINOR = "LibStub", 1  -- NEVER MAKE THIS AN SVN REVISION! IT NEEDS TO BE USABLE IN ALL REPOS!
+local LibStub = _G[LIBSTUB_MAJOR]
+
+local strformat = string.format
+if not LibStub or LibStub.minor < LIBSTUB_MINOR then
+	LibStub = LibStub or {libs = {}, minors = {} }
+	_G[LIBSTUB_MAJOR] = LibStub
+	LibStub.minor = LIBSTUB_MINOR
+
+	function LibStub:NewLibrary(major, minor)
+		assert(type(major) == "string", "Bad argument #2 to `NewLibrary' (string expected)")
+		minor = assert(tonumber(zo_strmatch(minor, "%d+")), "Minor version must either be a number or contain a number.")
+
+		local oldminor = self.minors[major]
+		if oldminor and oldminor >= minor then return nil end
+		self.minors[major], self.libs[major] = minor, self.libs[major] or {}
+		return self.libs[major], oldminor
+	end
+
+	function LibStub:GetLibrary(major, silent)
+		if not self.libs[major] and not silent then
+			error(("Cannot find a library instance of %q."):strformat(tostring(major)), 2)
+		end
+		return self.libs[major], self.minors[major]
+	end
+
+	function LibStub:IterateLibraries() return pairs(self.libs) end
+	setmetatable(LibStub, { __call = LibStub.GetLibrary })
+end
diff --git a/README.md b/README.md
deleted file mode 100644
index 6481a4c..0000000
--- a/README.md
+++ /dev/null
@@ -1,117 +0,0 @@
-#LibSort 1.0
-
-**Table of Contents**
-- [Rationale](#user-content-rationale)
-    - [API](#user-content-api)
-        - [Register](#user-content-register)
-        - [RegisterNumeric](#user-content-registernumeric)
-        - [RegisterBoolean](#user-content-registerboolean)
-        - [RegisterString](#user-content-registerstring)
-        - [Unregister](#user-content-unregister)
-        - [RegisterDefaultOrder](#user-content-registerdefaultorder)
-
-##Rationale
-
-Currently, the way ESO sorts items is essentially by column, you have a data point for each item, eg, **name** and you sort the list of objects by that data point.
-
-One thing that this default sorting mechanism offers, however, is a tiebreaker option. If we're sorting by **name** and a tiebreaker is required, because we are comparing two items with the same name, it falls to the tiebreaker datapoint to determine order of result, eg **stackSize**
-
-Now the general sorting of items in the real world doesn't just fall to a single data point, or two. It generally requires a series of different datapoints to sort by, and we can take advantage of this 'tiebreaker' to
-chain together lists of different datapoints to sort by. So we can sort by **Name**, then by **StackSize**, then by **SlotId**.
-
-That's well and fine for what's existing in the current set of datapoints, however we have access to a lot more information about the item than is currently being stored.
-
-To do this, however, we need to inject the required datapoints into the list object so that it has the required information to process the sort order as ascertained by our adjustment of how the tiebreakers chain together.
-
-	ZO_Inventory_BindSlot(control, slotType, index, bag)
-
-is the function we need to hook. It's a C side function, which means we don't actually know what it does exactly, but we don't need to. We might be able to prehook it, but chances are it recreates the data object so any
-prehooking work would be wiped out. So we posthook it.
-
-We now have information about the item, where it's from (*slotType*) and where it is (*bag* and *index*)
-
-And thanks to the new API, we have two new functions that will return information about the item in regards to what sort of weapon or armour it is.
-
-So as an example, we'll use the new Weapon info function to inject data to allow the game to sort by weapontype
-
-	GetItemWeaponType(link)
-
-is the function we're using to obtain the information required.
-
-Or at least through
-
-	local link = GetItemLink(bag, index)
-	local weaponType = GetItemWeaponType(link)
-
-If we then inject this information into the data object
-
-	control:GetParent().dataEntry.data.weaponType = weaponType
-
-we can then include another entry into the sortKeys for default header
-
-	local sortKeys = ZO_Inventory_GetDefaultHeaderSortKeys()
-	sortKeys["weaponType"] = {isNumeric = true, tiebreaker = "name"}
-
-and we will end up sorting by the type of weapon returned by that function. (Note that as it's a pure number, and not necessarily in the order you may want, you'll have to actually adjust the real value of the datapoint to something more suitable)
-
----
-
-In any case, now that we know what we need to do, this library should do most of the heavy lifting for you. Chances are I'll have to give it it's own Settings panel so people can reorder the sort order as they wish, but you should be able to register your addon to allow data injection and process the index/bag combinations to store whatever datapoints you want.
-
----
-##API
-
-###Register
-This will register a numeric sortKey
-
-    LibSort:Register(addonName, name, desc, key, func)
-
-(Note this is an alias for **RegisterNumeric** and will assume a numeric sortKey)
-- *addonName* - The name of the registering addon
-    + Example: "Item Sort"
-- *name* - A unique registration name
-    + Example: "ISWeaponSort"
-- *desc* - A description of how the sort applies
-    + Example: "Will sort by Weapon Type"
-- *key* - A unique key used to identify the datapoint
-    + Example: "weaponType"
-- *func* - The function to call to retrieve the sort value. Function signature **needs** to be (slotType, bag, index)
-    + Example: ItemSort.WeaponSort
-
-###RegisterNumeric
-This will register a numeric sortKey
-
-    LibSort:RegisterNumeric(addonName, name, desc, key, func)
-Arguments as above
-
-###RegisterBoolean
-This will register a boolean sortKey
-
-    LibSort:RegisterBoolean(addonName, name, desc, key, func)
-Arguments as above
-
-###RegisterString
-This will register a string sortKey
-
-    LibSort:RegisterString(addonName, name, desc, key, func)
-Arguments as above
-
-###Unregister
-This will unregister a sortKey registration
-
-    LibSort:Unregister(addonName, name)
-
-- *addonName* - The name of the registering addon
-    + Example: "Item Sort"
-- *name* - A unique registration name
-    + Example: "ISWeaponSort"
-
-###RegisterDefaultOrder
-Your addon may have multiple registrations, and this function will allow you to indicate what order you want them in as a block
-
-    LibSort:RegisterDefaultOrder(addonName, keyTable)
-
-- *addonName* -The name of the registering addon
-    + Example: "Item Sort"
-- *keyTable* - A table indicating the order of sortKeys for this addon
-    + Example: {"weaponType", "armorEquipType", "armorType", "subjectiveItemLevel"}