updated LibCustomMenu

manavortex [04-18-18 - 08:43]
updated LibCustomMenu
Filename
IIfA/libs/LibCustomMenu/LibCustomMenu.lua
diff --git a/IIfA/libs/LibCustomMenu/LibCustomMenu.lua b/IIfA/libs/LibCustomMenu/LibCustomMenu.lua
old mode 100644
new mode 100755
index b4a003d..50b7088
--- a/IIfA/libs/LibCustomMenu/LibCustomMenu.lua
+++ b/IIfA/libs/LibCustomMenu/LibCustomMenu.lua
@@ -1,570 +1,665 @@
--- authors: votan, sirinsidiator
--- thanks to: baertram & circonian
-
--- Register with LibStub
-local MAJOR, MINOR = "LibCustomMenu", 5
-local lib, oldminor = LibStub:NewLibrary(MAJOR, MINOR)
-if not lib then return end -- the same or newer version of this lib is already loaded into memory
-
-local wm = WINDOW_MANAGER
-
------ Common -----
-local function SetupDivider(pool, control)
-	local function GetTextDimensions(self)
-		return 32, 7
-	end
-	local function Noop(self)
-	end
-
-	local label = wm:CreateControlFromVirtual("$(parent)Name", control, "ZO_BaseTooltipDivider")
-	label:ClearAnchors()
-	label:SetAnchor(TOPLEFT, control, TOPLEFT, 0, 2)
-	label:SetAnchor(TOPRIGHT, control, TOPRIGHT, 0, 2)
-	-- First and last time the anchors are set
-	label.ClearAnchors = Noop
-	label.SetAnchor = Noop
-
-	label.SetText = Noop
-	label.SetFont = Noop
-	label.GetTextDimensions = GetTextDimensions
-	label.SetHorizontalAlignment = Noop
-	label:SetHidden(false)
-	control.nameLabel = label
-
-	control:SetMouseEnabled(false)
-end
-
-lib.DIVIDER = "-"
-
------ Sub Menu -----
-
-local Submenu = ZO_Object:Subclass()
-
-local SUBMENU_ITEM_MOUSE_ENTER = 1
-local SUBMENU_ITEM_MOUSE_EXIT = 2
-local SUBMENU_SHOW_TIMEOUT = 350
-local SUBMENU_HIDE_TIMEOUT = 350
-
-local submenuCallLaterHandle
-local nextId = 1
-local function ClearTimeout()
-	if (submenuCallLaterHandle ~= nil) then
-		EVENT_MANAGER:UnregisterForUpdate(submenuCallLaterHandle)
-		submenuCallLaterHandle = nil
-	end
-end
-
-local function SetTimeout(callback)
-	if (submenuCallLaterHandle ~= nil) then ClearTimeout() end
-	submenuCallLaterHandle = "LibCustomMenuSubMenuTimeout" .. nextId
-	nextId = nextId + 1
-
-	EVENT_MANAGER:RegisterForUpdate(submenuCallLaterHandle, SUBMENU_SHOW_TIMEOUT, function()
-		ClearTimeout()
-		if callback then callback() end
-	end )
-end
-
-local function GetValueOrCallback(arg, ...)
-	if type(arg) == "function" then
-		return arg(...)
-	else
-		return arg
-	end
-end
-
-function Submenu:New(...)
-	local object = ZO_Object.New(self)
-	object:Initialize(...)
-	return object
-end
-
-function Submenu:Initialize(name)
-	self.window = ZO_Menus
-
-	local submenuControl = self.window:CreateControl(name, CT_CONTROL)
-	submenuControl:SetClampedToScreen(true)
-	submenuControl:SetMouseEnabled(true)
-	submenuControl:SetHidden(true)
-	-- OnMouseEnter: Stop hiding of submenu initiated by mouse exit of parent
-	submenuControl:SetHandler("OnMouseEnter", ClearTimeout)
-
-	local function ExitSubMenu() if self.parent and self.parent.OnSelect then self.parent:OnSelect(SUBMENU_ITEM_MOUSE_EXIT) end end
-	submenuControl:SetHandler("OnMouseExit", function(control) SetTimeout(ExitSubMenu) end)
-
-	submenuControl:SetHandler("OnHide", function(control) ClearTimeout() self:Clear() end)
-	submenuControl:SetDrawLevel(ZO_Menu:GetDrawLevel() + 1)
-
-	local bg = submenuControl:CreateControl("$(parent)BG", CT_BACKDROP)
-	-- bg:SetCenterColor(0, 0, 0, .93)
-	bg:SetCenterTexture("EsoUI/Art/Tooltips/UI-TooltipCenter.dds")
-	bg:SetEdgeTexture("EsoUI/Art/Tooltips/UI-Border.dds", 128, 16)
-	bg:SetInsets(16, 16, -16, -16)
-	bg:SetAnchorFill()
-
-	local overlay = bg:CreateControl("$(parent)MungeOverlay", CT_TEXTURE)
-	overlay:SetTexture("EsoUI/Art/Tooltips/munge_overlay.dds")
-	overlay:SetAddressMode(TEX_MODE_WRAP)
-	overlay:SetAnchor(TOPLEFT)
-	overlay:SetAnchor(BOTTOMRIGHT)
-
-	self.highlight = CreateControlFromVirtual("$(parent)Highlight", submenuControl, "ZO_SelectionHighlight")
-	self.highlight:SetHidden(true)
-
-	self.control = submenuControl
-
-	local upInside = false
-	local function MouseEnter(control)
-		upInside = true
-		ClearTimeout()
-		self:SetSelectedIndex(control.index)
-	end
-	local function MouseExit(control)
-		upInside = false
-		if (self.selectedIndex == control.index) then
-			self:SetSelectedIndex(nil)
-		end
-	end
-	local function MouseUp(control, button)
-		if upInside == true and button == MOUSE_BUTTON_INDEX_LEFT then
-			ZO_Menu_SetLastCommandWasFromMenu(true)
-			if control.checkbox then
-				-- The checkbox click handler will handle it
-				ZO_CheckButton_OnClicked(control.checkbox, button)
-			else
-				if not control.OnSelect() then
-					ClearMenu()
-				end
-			end
-		end
-	end
-
-	local function ItemFactory(pool)
-		local control = CreateControlFromVirtual("ZO_SubMenuItem", submenuControl, "ZO_MenuItem", pool:GetNextControlId())
-		control.nameLabel = GetControl(control, "Name")
-
-		control:SetHandler("OnMouseEnter", MouseEnter)
-		control:SetHandler("OnMouseExit", MouseExit)
-		control:SetHandler("OnMouseDown", IgnoreMouseDownEditFocusLoss)
-		control:SetHandler("OnMouseUp", MouseUp)
-
-		return control
-	end
-
-	local function ResetFunction(control)
-		control:SetHidden(true)
-		control:ClearAnchors()
-		control.OnSelect = nil
-		control.menuIndex = nil
-	end
-
-	local function DividerFactory(pool)
-		local control = CreateControlFromVirtual("ZO_CustomSubMenuDivider", submenuControl, "ZO_NotificationsRowButton", pool:GetNextControlId())
-		SetupDivider(pool, control)
-		return control
-	end
-
-	local function ResetCheckbox(checkbox)
-		ResetFunction(checkbox)
-	end
-
-	local function CheckBoxMouseEnter(control)
-		MouseEnter(control:GetParent())
-	end
-	local function CheckBoxMouseExit(control)
-		MouseExit(control:GetParent())
-	end
-	local function CheckBoxMouseUp(control)
-		self.refCount =(self.refCount or 0) + 1
-		local parent = control:GetParent()
-		parent.OnSelect(ZO_CheckButton_IsChecked(control))
-	end
-	local function CheckBoxFactory(pool)
-		local control = CreateControlFromVirtual("ZO_CustomSubMenuItemCheckButton", submenuControl, "ZO_CheckButton", pool:GetNextControlId())
-		control.nameLabel = control
-
-		control:SetHandler("OnMouseEnter", CheckBoxMouseEnter)
-		control:SetHandler("OnMouseExit", CheckBoxMouseExit)
-
-		ZO_CheckButton_SetToggleFunction(control, CheckBoxMouseUp)
-
-		return control
-	end
-
-
-	self.itemPool = ZO_ObjectPool:New(ItemFactory, ResetFunction)
-	self.dividerPool = ZO_ObjectPool:New(DividerFactory, ResetFunction)
-	self.checkBoxPool = ZO_ObjectPool:New(CheckBoxFactory, ResetCheckbox)
-	self.items = { }
-
-	EVENT_MANAGER:RegisterForEvent(name .. "_OnGlobalMouseUp", EVENT_GLOBAL_MOUSE_UP, function()
-		if self.refCount ~= nil then
-			local moc = wm:GetMouseOverControl()
-			if (moc:GetOwningWindow() ~= submenuControl) then
-				self.refCount = self.refCount - 1
-				if self.refCount <= 0 then
-					self:Clear()
-				end
-			end
-		end
-	end )
-end
-
-function Submenu:SetSelectedIndex(index)
-	if (index) then
-		index = zo_max(zo_min(index, #self.items), 1)
-	end
-
-	if (self.selectedIndex ~= index) then
-		self:UnselectItem(self.selectedIndex)
-		self:SelectItem(index)
-	end
-end
-
-function Submenu:UnselectItem(index)
-	local item = self.items[index]
-	if item then
-		self.highlight:SetHidden(true)
-		local nameControl = item.nameLabel
-		nameControl:SetColor(nameControl.normalColor:UnpackRGBA())
-
-		self.selectedIndex = nil
-	end
-end
-
-function Submenu:SelectItem(index)
-	local item = self.items[index]
-	if item then
-		local highlight = self.highlight
-
-		highlight:ClearAnchors()
-
-		highlight:SetAnchor(TOPLEFT, item, TOPLEFT, -2, -2)
-		highlight:SetAnchor(BOTTOMRIGHT, item, BOTTOMRIGHT, 2, 2)
-
-		highlight:SetHidden(false)
-
-		local nameControl = item.nameLabel
-		nameControl:SetColor(nameControl.highlightColor:UnpackRGBA())
-
-		self.selectedIndex = index
-	end
-end
-
-function Submenu:UpdateAnchors()
-	local iconSize = self.iconSize
-	local previousItem = self.control
-	local items = self.items
-	local width, height = 0, 0
-	local padding = ZO_Menu.menuPad
-
-	for i = 1, #items do
-		local item = items[i]
-		local textWidth, textHeight = item.nameLabel:GetTextDimensions()
-		width = math.max(textWidth + padding * 2, width)
-		height = height + textHeight
-		item:ClearAnchors()
-		if i == 1 then
-			item:SetAnchor(TOPLEFT, previousItem, TOPLEFT, padding, padding)
-			item:SetAnchor(TOPRIGHT, previousItem, TOPRIGHT, - padding, padding)
-		else
-			item:SetAnchor(TOPLEFT, previousItem, BOTTOMLEFT, 0, item.itemYPad)
-			item:SetAnchor(TOPRIGHT, previousItem, BOTTOMRIGHT, 0, item.itemYPad)
-		end
-
-		item:SetHidden(false)
-		item:SetDimensions(textWidth, textHeight)
-		previousItem = item
-	end
-
-	self.control:SetDimensions(width + padding * 2, height + padding * 2)
-end
-
-function Submenu:Clear()
-	self:UnselectItem(self.selectedIndex)
-	self.items = { }
-	self.itemPool:ReleaseAllObjects()
-	self.dividerPool:ReleaseAllObjects()
-	self.checkBoxPool:ReleaseAllObjects()
-	self.control:SetHidden(true)
-	self.refCount = nil
-end
-
-local DEFAULT_TEXT_COLOR = ZO_ColorDef:New(GetInterfaceColor(INTERFACE_COLOR_TYPE_TEXT_COLORS, INTERFACE_TEXT_COLOR_NORMAL))
-local DEFAULT_TEXT_HIGHLIGHT = ZO_ColorDef:New(GetInterfaceColor(INTERFACE_COLOR_TYPE_TEXT_COLORS, INTERFACE_TEXT_COLOR_CONTEXT_HIGHLIGHT))
-
-function Submenu:AddItem(entry, myfont, normalColor, highlightColor, itemYPad)
-	local visible
-	if entry.visible ~= nil then visible = entry.visible else visible = true end
-	if not GetValueOrCallback(visible, ZO_Menu) then return end
-
-	local item, key
-	local itemType = entry.itemType or MENU_ADD_OPTION_LABEL
-	if itemType == MENU_ADD_OPTION_LABEL then
-		item, key = entry.label ~= lib.DIVIDER and self.itemPool:AcquireObject() or self.dividerPool:AcquireObject()
-	elseif itemType == MENU_ADD_OPTION_CHECKBOX then
-		item, key = self.itemPool:AcquireObject()
-	else
-		error(string.format("Unknown menu entry itemType: %s", itemType))
-	end
-
-	item.OnSelect = entry.callback
-	item.index = #self.items + 1
-	self.items[item.index] = item
-
-	local nameControl = item.nameLabel
-
-	local entryFont = GetValueOrCallback(entry.myfont, ZO_Menu, item) or myfont
-	local normColor = GetValueOrCallback(entry.normalColor, ZO_Menu, item) or normalColor
-	local highColor = GetValueOrCallback(entry.highlightColor, ZO_Menu, item) or highlightColor
-	myfont = entryFont or "ZoFontGame"
-	nameControl.normalColor = normColor or DEFAULT_TEXT_COLOR
-	nameControl.highlightColor = highColor or DEFAULT_TEXT_HIGHLIGHT
-
-	nameControl:SetFont(myfont)
-
-	local text = GetValueOrCallback(entry.label, ZO_Menu, item)
-
-	local checkboxItemControl = nil
-	if itemType == MENU_ADD_OPTION_CHECKBOX then
-		checkboxItemControl = self.checkBoxPool:AcquireObject()
-		checkboxItemControl:SetParent(item)
-		checkboxItemControl.menuIndex = item.index
-		checkboxItemControl:ClearAnchors()
-		checkboxItemControl:SetHidden(false)
-		checkboxItemControl:SetAnchor(LEFT, nil, LEFT, 2, -1)
-		text = string.format(" |u18:0::|u%s", text)
-		ZO_CheckButton_SetCheckState(checkboxItemControl, GetValueOrCallback(entry.checked, ZO_Menu, item) or false)
-	end
-	item.checkbox = checkboxItemControl
-
-	nameControl:SetText(text)
-
-	local enabled = not GetValueOrCallback(entry.disabled or false, ZO_Menu, item)
-	nameControl:SetColor((enabled and nameControl.normalColor or ZO_DEFAULT_DISABLED_COLOR):UnpackRGBA())
-	item:SetMouseEnabled(enabled)
-end
-
-function Submenu:Show(parent)
-	if not self.control:IsHidden() then self:Clear() return false end
-	self:UpdateAnchors()
-
-	local padding = ZO_Menu.menuPad
-	local control = self.control
-	control:ClearAnchors()
-	-- If there is not enough space on the right side, use the left side. Like Windows.
-	if (parent:GetRight() + control:GetWidth()) < GuiRoot:GetRight() then
-		control:SetAnchor(TOPLEFT, parent, TOPRIGHT, -1, - padding)
-	else
-		control:SetAnchor(TOPRIGHT, parent, TOPLEFT, 1, - padding)
-	end
-	control:SetHidden(false)
-	self.parent = parent
-	self.refCount = 2
-
-	return true
-end
-
-local function SubMenuItemFactory(pool)
-	local control = CreateControlFromVirtual("ZO_CustomSubMenuItem", ZO_Menu, "ZO_NotificationsRowButton", pool:GetNextControlId())
-
-	local arrowContainer = control:CreateControl("$(parent)Arrow", CT_CONTROL)
-	-- we need this in order to control the menu with independently of the texture size
-	arrowContainer:SetAnchor(RIGHT, control, RIGHT, 0, 0)
-	arrowContainer:SetDimensions(32, 16)
-
-	local arrow = arrowContainer:CreateControl("$(parent)Texture", CT_TEXTURE)
-	arrow:SetAnchor(RIGHT, arrowContainer, RIGHT, 0, 0)
-	arrow:SetDimensions(16, 20)
-	arrow:SetTexture("EsoUI/Art/Miscellaneous/colorPicker_slider_vertical.dds")
-	arrow:SetTextureCoords(0, 0.5, 0, 1)
-
-	-- we assign the submenu arrow to checkbox because the context menu will add the desired width automatically that way
-	control.checkbox = arrowContainer
-
-	local clicked = false
-	local function MouseEnter(control)
-		ZO_Menu_EnterItem(control)
-		clicked = false
-		SetTimeout( function() if control.OnSelect then control:OnSelect(SUBMENU_ITEM_MOUSE_ENTER) end end)
-	end
-	local function MouseExit(control)
-		ZO_Menu_ExitItem(control)
-		if not clicked then
-			SetTimeout( function() if control.OnSelect then control:OnSelect(SUBMENU_ITEM_MOUSE_EXIT) end end)
-		end
-	end
-	local function MouseDown(control)
-		IgnoreMouseDownEditFocusLoss()
-		-- re-open sub menu on click
-		clicked = true
-		control:OnSelect(SUBMENU_ITEM_MOUSE_ENTER)
-	end
-
-	local label = wm:CreateControl("$(parent)Name", control, CT_LABEL)
-	label:SetAnchor(TOPLEFT)
-	control.nameLabel = label
-
-	control:SetHandler("OnMouseEnter", MouseEnter)
-	control:SetHandler("OnMouseExit", MouseExit)
-	control:SetHandler("OnMouseDown", MouseDown)
-
-	return control
-end
-
------ Standard Menu -----
-
-local function ResetMenuItem(button)
-	button:SetHidden(true)
-	button:ClearAnchors()
-	button.menuIndex = nil
-	button.OnSelect = nil
-end
-
-local function ResetCheckBox(checkBox)
-	ResetMenuItem(checkBox)
-	ZO_CheckButton_SetToggleFunction(checkBox, nil)
-end
-
-local upInside = false
-
-local function MenuItemFactory(pool)
-	local control = CreateControlFromVirtual("ZO_CustomMenuItem", ZO_Menu, "ZO_NotificationsRowButton", pool:GetNextControlId())
-	local function MouseEnter()
-		upInside = true
-		ZO_Menu_EnterItem(control)
-	end
-	local function MouseExit()
-		upInside = false
-		ZO_Menu_ExitItem(control)
-	end
-	local function MouseUp()
-		if upInside == true then
-			ZO_Menu_ClickItem(control, 1)
-		end
-	end
-
-	local label = wm:CreateControl("$(parent)Name", control, CT_LABEL)
-	label:SetAnchor(TOPLEFT)
-	control.nameLabel = label
-
-	control:SetHandler("OnMouseEnter", MouseEnter)
-	control:SetHandler("OnMouseExit", MouseExit)
-	control:SetHandler("OnMouseDown", IgnoreMouseDownEditFocusLoss)
-	control:SetHandler("OnMouseUp", MouseUp)
-
-	return control
-end
-
-local function CheckBoxFactory(pool)
-	local control = CreateControlFromVirtual("ZO_CustomMenuItemCheckButton", ZO_Menu, "ZO_CheckButton", pool:GetNextControlId())
-	control.nameLabel = control
-
-	local function MouseEnter()
-		ZO_Menu_EnterItem(control)
-	end
-	local function MouseExit()
-		ZO_Menu_ExitItem(control)
-	end
-	control:SetHandler("OnMouseEnter", MouseEnter)
-	control:SetHandler("OnMouseExit", MouseExit)
-	return control
-end
-
-local function DividerFactory(pool)
-	local control = CreateControlFromVirtual("ZO_CustomMenuDivider", ZO_Menu, "ZO_NotificationsRowButton", pool:GetNextControlId())
-	SetupDivider(pool, control)
-	return control
-end
-
------ Public API -----
-
-function AddCustomMenuItem(mytext, myfunction, itemType, myFont, normalColor, highlightColor, itemYPad, horizontalAlignment)
-	local orgItemPool = ZO_Menu.itemPool
-	local orgCheckboxItemPool = ZO_Menu.checkBoxPool
-
-	ZO_Menu.itemPool = mytext ~= lib.DIVIDER and lib.itemPool or lib.dividerPool
-	ZO_Menu.checkBoxPool = lib.checkBoxPool
-
-	local index = AddMenuItem(mytext, myfunction, itemType, myFont, normalColor, highlightColor, itemYPad, horizontalAlignment)
-
-	ZO_Menu.itemPool = orgItemPool
-	ZO_Menu.checkBoxPool = orgCheckboxItemPool
-
-	return index
-end
-
-function AddCustomSubMenuItem(mytext, entries, myfont, normalColor, highlightColor, itemYPad)
-	local function CreateSubMenu(control, state)
-		if (state == SUBMENU_ITEM_MOUSE_ENTER) then
-			lib.submenu:Clear()
-			local currentEntries = GetValueOrCallback(entries, ZO_Menu, control)
-			local entry
-			for i = 1, #currentEntries do
-				entry = currentEntries[i]
-				lib.submenu:AddItem(entry, myfont, normalColor, highlightColor, itemYPad)
-			end
-			lib.submenu:Show(control)
-		elseif (state == SUBMENU_ITEM_MOUSE_EXIT) then
-			lib.submenu:Clear()
-		end
-	end
-
-	local orgItemPool = ZO_Menu.itemPool
-	local orgCheckboxItemPool = ZO_Menu.checkBoxPool
-
-	ZO_Menu.itemPool = lib.submenuPool
-	ZO_Menu.checkBoxPool = lib.checkBoxPool
-
-	local index = AddMenuItem(mytext, CreateSubMenu, MENU_ADD_OPTION_LABEL, myfont, normalColor, highlightColor, itemYPad)
-
-	ZO_Menu.itemPool = orgItemPool
-	ZO_Menu.checkBoxPool = orgCheckboxItemPool
-
-	return index
-end
-
-local function HookClearMenu()
-	local orgClearMenu = ClearMenu
-	function ClearMenu()
-		ClearTimeout()
-		orgClearMenu()
-		lib.itemPool:ReleaseAllObjects()
-		lib.submenuPool:ReleaseAllObjects()
-		lib.checkBoxPool:ReleaseAllObjects()
-		lib.dividerPool:ReleaseAllObjects()
-		lib.submenu:Clear()
-	end
-end
-
-local function HookAddSlotAction()
-	function ZO_InventorySlotActions:AddCustomSlotAction(...)
-		local orgItemPool = ZO_Menu.itemPool
-		local orgCheckboxItemPool = ZO_Menu.checkBoxPool
-
-		ZO_Menu.itemPool = lib.itemPool
-		ZO_Menu.checkBoxPool = lib.checkBoxPool
-
-		self:AddSlotAction(...)
-
-		ZO_Menu.itemPool = orgItemPool
-		ZO_Menu.checkBoxPool = orgCheckboxItemPool
-	end
-end
-
----- Init -----
-
-local function OnAddonLoaded(event, name)
-	if name:find("^ZO_") then return end
-	EVENT_MANAGER:UnregisterForEvent(MAJOR, EVENT_ADD_ON_LOADED)
-	lib.itemPool = ZO_ObjectPool:New(MenuItemFactory, ResetMenuItem)
-	lib.submenuPool = ZO_ObjectPool:New(SubMenuItemFactory, ResetMenuItem)
-	lib.checkBoxPool = ZO_ObjectPool:New(CheckBoxFactory, ResetCheckBox)
-	lib.dividerPool = ZO_ObjectPool:New(DividerFactory, ResetMenuItem)
-	lib.submenu = Submenu:New("LibCustomMenuSubmenu")
-	HookClearMenu()
-	HookAddSlotAction()
-end
-
-EVENT_MANAGER:UnregisterForEvent(MAJOR, EVENT_ADD_ON_LOADED)
-EVENT_MANAGER:RegisterForEvent(MAJOR, EVENT_ADD_ON_LOADED, OnAddonLoaded)
+-- authors: votan, sirinsidiator
+-- thanks to: baertram & circonian
+
+-- Register with LibStub
+local MAJOR, MINOR = "LibCustomMenu", 6.5
+local lib, oldminor = LibStub:NewLibrary(MAJOR, MINOR)
+if not lib then return end -- the same or newer version of this lib is already loaded into memory
+
+local wm = WINDOW_MANAGER
+
+----- Common -----
+local function SetupDivider(pool, control)
+	local function GetTextDimensions(self)
+		return 32, 7
+	end
+	local function Noop(self)
+	end
+
+	local label = wm:CreateControlFromVirtual("$(parent)Name", control, "ZO_BaseTooltipDivider")
+	label:ClearAnchors()
+	label:SetAnchor(TOPLEFT, control, TOPLEFT, 0, 2)
+	label:SetAnchor(TOPRIGHT, control, TOPRIGHT, 0, 2)
+	-- First and last time the anchors are set
+	label.ClearAnchors = Noop
+	label.SetAnchor = Noop
+
+	label.SetText = Noop
+	label.SetFont = Noop
+	label.GetTextDimensions = GetTextDimensions
+	label.SetHorizontalAlignment = Noop
+	label:SetHidden(false)
+	control.nameLabel = label
+
+	control:SetMouseEnabled(false)
+end
+
+lib.DIVIDER = "-"
+
+----- Sub Menu -----
+
+local Submenu = ZO_Object:Subclass()
+
+local SUBMENU_ITEM_MOUSE_ENTER = 1
+local SUBMENU_ITEM_MOUSE_EXIT = 2
+local SUBMENU_SHOW_TIMEOUT = 350
+local SUBMENU_HIDE_TIMEOUT = 350
+
+local submenuCallLaterHandle
+local nextId = 1
+local function ClearTimeout()
+	if (submenuCallLaterHandle ~= nil) then
+		EVENT_MANAGER:UnregisterForUpdate(submenuCallLaterHandle)
+		submenuCallLaterHandle = nil
+	end
+end
+
+local function SetTimeout(callback)
+	if (submenuCallLaterHandle ~= nil) then ClearTimeout() end
+	submenuCallLaterHandle = "LibCustomMenuSubMenuTimeout" .. nextId
+	nextId = nextId + 1
+
+	EVENT_MANAGER:RegisterForUpdate(submenuCallLaterHandle, SUBMENU_SHOW_TIMEOUT, function()
+		ClearTimeout()
+		if callback then callback() end
+	end )
+end
+
+local function GetValueOrCallback(arg, ...)
+	if type(arg) == "function" then
+		return arg(...)
+	else
+		return arg
+	end
+end
+
+function Submenu:New(...)
+	local object = ZO_Object.New(self)
+	object:Initialize(...)
+	return object
+end
+
+function Submenu:Initialize(name)
+	self.window = ZO_Menus
+
+	local submenuControl = self.window:CreateControl(name, CT_CONTROL)
+	submenuControl:SetClampedToScreen(true)
+	submenuControl:SetMouseEnabled(true)
+	submenuControl:SetHidden(true)
+	-- OnMouseEnter: Stop hiding of submenu initiated by mouse exit of parent
+	submenuControl:SetHandler("OnMouseEnter", ClearTimeout)
+
+	local function ExitSubMenu() if self.parent and self.parent.OnSelect then self.parent:OnSelect(SUBMENU_ITEM_MOUSE_EXIT) end end
+	submenuControl:SetHandler("OnMouseExit", function(control) SetTimeout(ExitSubMenu) end)
+
+	submenuControl:SetHandler("OnHide", function(control) ClearTimeout() self:Clear() end)
+	submenuControl:SetDrawLevel(ZO_Menu:GetDrawLevel() + 1)
+
+	local bg = submenuControl:CreateControl("$(parent)BG", CT_BACKDROP)
+	-- bg:SetCenterColor(0, 0, 0, .93)
+	bg:SetCenterTexture("EsoUI/Art/Tooltips/UI-TooltipCenter.dds")
+	bg:SetEdgeTexture("EsoUI/Art/Tooltips/UI-Border.dds", 128, 16)
+	bg:SetInsets(16, 16, -16, -16)
+	bg:SetAnchorFill()
+
+	local overlay = bg:CreateControl("$(parent)MungeOverlay", CT_TEXTURE)
+	overlay:SetTexture("EsoUI/Art/Tooltips/munge_overlay.dds")
+	overlay:SetAddressMode(TEX_MODE_WRAP)
+	overlay:SetAnchor(TOPLEFT)
+	overlay:SetAnchor(BOTTOMRIGHT)
+
+	self.highlight = CreateControlFromVirtual("$(parent)Highlight", submenuControl, "ZO_SelectionHighlight")
+	self.highlight:SetHidden(true)
+
+	self.control = submenuControl
+
+	local upInside = false
+	local function MouseEnter(control)
+		upInside = true
+		ClearTimeout()
+		self:SetSelectedIndex(control.index)
+	end
+	local function MouseExit(control)
+		upInside = false
+		if (self.selectedIndex == control.index) then
+			self:SetSelectedIndex(nil)
+		end
+	end
+	local function MouseUp(control, button)
+		if upInside == true and button == MOUSE_BUTTON_INDEX_LEFT then
+			ZO_Menu_SetLastCommandWasFromMenu(true)
+			if control.checkbox then
+				-- The checkbox click handler will handle it
+				ZO_CheckButton_OnClicked(control.checkbox, button)
+			else
+				if not control.OnSelect() then
+					ClearMenu()
+				end
+			end
+		end
+	end
+
+	local function ItemFactory(pool)
+		local control = CreateControlFromVirtual("ZO_SubMenuItem", submenuControl, "ZO_MenuItem", pool:GetNextControlId())
+		control.nameLabel = GetControl(control, "Name")
+
+		control:SetHandler("OnMouseEnter", MouseEnter)
+		control:SetHandler("OnMouseExit", MouseExit)
+		control:SetHandler("OnMouseDown", IgnoreMouseDownEditFocusLoss)
+		control:SetHandler("OnMouseUp", MouseUp)
+
+		return control
+	end
+
+	local function ResetFunction(control)
+		control:SetHidden(true)
+		control:ClearAnchors()
+		control.OnSelect = nil
+		control.menuIndex = nil
+	end
+
+	local function DividerFactory(pool)
+		local control = CreateControlFromVirtual("ZO_CustomSubMenuDivider", submenuControl, "ZO_NotificationsRowButton", pool:GetNextControlId())
+		SetupDivider(pool, control)
+		return control
+	end
+
+	local function ResetCheckbox(checkbox)
+		ResetFunction(checkbox)
+	end
+
+	local function CheckBoxMouseEnter(control)
+		MouseEnter(control:GetParent())
+	end
+	local function CheckBoxMouseExit(control)
+		MouseExit(control:GetParent())
+	end
+	local function CheckBoxMouseUp(control)
+		self.refCount =(self.refCount or 0) + 1
+		local parent = control:GetParent()
+		parent.OnSelect(ZO_CheckButton_IsChecked(control))
+	end
+	local function CheckBoxFactory(pool)
+		local control = CreateControlFromVirtual("ZO_CustomSubMenuItemCheckButton", submenuControl, "ZO_CheckButton", pool:GetNextControlId())
+		control.nameLabel = control
+
+		control:SetHandler("OnMouseEnter", CheckBoxMouseEnter)
+		control:SetHandler("OnMouseExit", CheckBoxMouseExit)
+
+		ZO_CheckButton_SetToggleFunction(control, CheckBoxMouseUp)
+
+		return control
+	end
+
+
+	self.itemPool = ZO_ObjectPool:New(ItemFactory, ResetFunction)
+	self.dividerPool = ZO_ObjectPool:New(DividerFactory, ResetFunction)
+	self.checkBoxPool = ZO_ObjectPool:New(CheckBoxFactory, ResetCheckbox)
+	self.items = { }
+
+	EVENT_MANAGER:RegisterForEvent(name .. "_OnGlobalMouseUp", EVENT_GLOBAL_MOUSE_UP, function()
+		if self.refCount ~= nil then
+			local moc = wm:GetMouseOverControl()
+			if (moc:GetOwningWindow() ~= submenuControl) then
+				self.refCount = self.refCount - 1
+				if self.refCount <= 0 then
+					self:Clear()
+				end
+			end
+		end
+	end )
+end
+
+function Submenu:SetSelectedIndex(index)
+	if (index) then
+		index = zo_max(zo_min(index, #self.items), 1)
+	end
+
+	if (self.selectedIndex ~= index) then
+		self:UnselectItem(self.selectedIndex)
+		self:SelectItem(index)
+	end
+end
+
+function Submenu:UnselectItem(index)
+	local item = self.items[index]
+	if item then
+		self.highlight:SetHidden(true)
+		local nameControl = item.nameLabel
+		nameControl:SetColor(nameControl.normalColor:UnpackRGBA())
+
+		self.selectedIndex = nil
+	end
+end
+
+function Submenu:SelectItem(index)
+	local item = self.items[index]
+	if item then
+		local highlight = self.highlight
+
+		highlight:ClearAnchors()
+
+		highlight:SetAnchor(TOPLEFT, item, TOPLEFT, -2, -2)
+		highlight:SetAnchor(BOTTOMRIGHT, item, BOTTOMRIGHT, 2, 2)
+
+		highlight:SetHidden(false)
+
+		local nameControl = item.nameLabel
+		nameControl:SetColor(nameControl.highlightColor:UnpackRGBA())
+
+		self.selectedIndex = index
+	end
+end
+
+function Submenu:UpdateAnchors()
+	local iconSize = self.iconSize
+	local previousItem = self.control
+	local items = self.items
+	local width, height = 0, 0
+	local padding = ZO_Menu.menuPad
+
+	for i = 1, #items do
+		local item = items[i]
+		local textWidth, textHeight = item.nameLabel:GetTextDimensions()
+		width = math.max(textWidth + padding * 2, width)
+		height = height + textHeight
+		item:ClearAnchors()
+		if i == 1 then
+			item:SetAnchor(TOPLEFT, previousItem, TOPLEFT, padding, padding)
+			item:SetAnchor(TOPRIGHT, previousItem, TOPRIGHT, - padding, padding)
+		else
+			item:SetAnchor(TOPLEFT, previousItem, BOTTOMLEFT, 0, item.itemYPad)
+			item:SetAnchor(TOPRIGHT, previousItem, BOTTOMRIGHT, 0, item.itemYPad)
+		end
+
+		item:SetHidden(false)
+		item:SetDimensions(textWidth, textHeight)
+		previousItem = item
+	end
+
+	self.control:SetDimensions(width + padding * 2, height + padding * 2)
+end
+
+function Submenu:Clear()
+	self:UnselectItem(self.selectedIndex)
+	self.items = { }
+	self.itemPool:ReleaseAllObjects()
+	self.dividerPool:ReleaseAllObjects()
+	self.checkBoxPool:ReleaseAllObjects()
+	self.control:SetHidden(true)
+	self.refCount = nil
+end
+
+local DEFAULT_TEXT_COLOR = ZO_ColorDef:New(GetInterfaceColor(INTERFACE_COLOR_TYPE_TEXT_COLORS, INTERFACE_TEXT_COLOR_NORMAL))
+local DEFAULT_TEXT_HIGHLIGHT = ZO_ColorDef:New(GetInterfaceColor(INTERFACE_COLOR_TYPE_TEXT_COLORS, INTERFACE_TEXT_COLOR_CONTEXT_HIGHLIGHT))
+
+function Submenu:AddItem(entry, myfont, normalColor, highlightColor, itemYPad)
+	local visible
+	if entry.visible ~= nil then visible = entry.visible else visible = true end
+	if not GetValueOrCallback(visible, ZO_Menu) then return end
+
+	local item, key
+	local itemType = entry.itemType or MENU_ADD_OPTION_LABEL
+	if itemType == MENU_ADD_OPTION_LABEL then
+		item, key = entry.label ~= lib.DIVIDER and self.itemPool:AcquireObject() or self.dividerPool:AcquireObject()
+	elseif itemType == MENU_ADD_OPTION_CHECKBOX then
+		item, key = self.itemPool:AcquireObject()
+	else
+		error(string.format("Unknown menu entry itemType: %s", itemType))
+	end
+
+	item.OnSelect = entry.callback
+	item.index = #self.items + 1
+	self.items[item.index] = item
+
+	local nameControl = item.nameLabel
+
+	local entryFont = GetValueOrCallback(entry.myfont, ZO_Menu, item) or myfont
+	local normColor = GetValueOrCallback(entry.normalColor, ZO_Menu, item) or normalColor
+	local highColor = GetValueOrCallback(entry.highlightColor, ZO_Menu, item) or highlightColor
+	myfont = entryFont or "ZoFontGame"
+	nameControl.normalColor = normColor or DEFAULT_TEXT_COLOR
+	nameControl.highlightColor = highColor or DEFAULT_TEXT_HIGHLIGHT
+
+	nameControl:SetFont(myfont)
+
+	local text = GetValueOrCallback(entry.label, ZO_Menu, item)
+
+	local checkboxItemControl = nil
+	if itemType == MENU_ADD_OPTION_CHECKBOX then
+		checkboxItemControl = self.checkBoxPool:AcquireObject()
+		checkboxItemControl:SetParent(item)
+		checkboxItemControl.menuIndex = item.index
+		checkboxItemControl:ClearAnchors()
+		checkboxItemControl:SetHidden(false)
+		checkboxItemControl:SetAnchor(LEFT, nil, LEFT, 2, -1)
+		text = string.format(" |u18:0::|u%s", text)
+		ZO_CheckButton_SetCheckState(checkboxItemControl, GetValueOrCallback(entry.checked, ZO_Menu, item) or false)
+	end
+	item.checkbox = checkboxItemControl
+
+	nameControl:SetText(text)
+
+	local enabled = not GetValueOrCallback(entry.disabled or false, ZO_Menu, item)
+	nameControl:SetColor((enabled and nameControl.normalColor or ZO_DEFAULT_DISABLED_COLOR):UnpackRGBA())
+	item:SetMouseEnabled(enabled)
+end
+
+function Submenu:Show(parent)
+	if not self.control:IsHidden() then self:Clear() return false end
+	self:UpdateAnchors()
+
+	local padding = ZO_Menu.menuPad
+	local control = self.control
+	control:ClearAnchors()
+	-- If there is not enough space on the right side, use the left side. Like Windows.
+	if (parent:GetRight() + control:GetWidth()) < GuiRoot:GetRight() then
+		control:SetAnchor(TOPLEFT, parent, TOPRIGHT, -1, - padding)
+	else
+		control:SetAnchor(TOPRIGHT, parent, TOPLEFT, 1, - padding)
+	end
+	control:SetHidden(false)
+	self.parent = parent
+	self.refCount = 2
+
+	return true
+end
+
+local function SubMenuItemFactory(pool)
+	local control = CreateControlFromVirtual("ZO_CustomSubMenuItem", ZO_Menu, "ZO_NotificationsRowButton", pool:GetNextControlId())
+
+	local arrowContainer = control:CreateControl("$(parent)Arrow", CT_CONTROL)
+	-- we need this in order to control the menu with independently of the texture size
+	arrowContainer:SetAnchor(RIGHT, control, RIGHT, 0, 0)
+	arrowContainer:SetDimensions(32, 16)
+
+	local arrow = arrowContainer:CreateControl("$(parent)Texture", CT_TEXTURE)
+	arrow:SetAnchor(RIGHT, arrowContainer, RIGHT, 0, 0)
+	arrow:SetDimensions(16, 20)
+	arrow:SetTexture("EsoUI/Art/Miscellaneous/colorPicker_slider_vertical.dds")
+	arrow:SetTextureCoords(0, 0.5, 0, 1)
+
+	-- we assign the submenu arrow to checkbox because the context menu will add the desired width automatically that way
+	control.checkbox = arrowContainer
+
+	local clicked = false
+	local function MouseEnter(control)
+		ZO_Menu_EnterItem(control)
+		clicked = false
+		SetTimeout( function() if control.OnSelect then control:OnSelect(SUBMENU_ITEM_MOUSE_ENTER) end end)
+	end
+	local function MouseExit(control)
+		ZO_Menu_ExitItem(control)
+		if not clicked then
+			SetTimeout( function() if control.OnSelect then control:OnSelect(SUBMENU_ITEM_MOUSE_EXIT) end end)
+		end
+	end
+	local function MouseDown(control)
+		IgnoreMouseDownEditFocusLoss()
+		-- re-open sub menu on click
+		clicked = true
+		control:OnSelect(SUBMENU_ITEM_MOUSE_ENTER)
+	end
+
+	local label = wm:CreateControl("$(parent)Name", control, CT_LABEL)
+	label:SetAnchor(TOPLEFT)
+	control.nameLabel = label
+
+	control:SetHandler("OnMouseEnter", MouseEnter)
+	control:SetHandler("OnMouseExit", MouseExit)
+	control:SetHandler("OnMouseDown", MouseDown)
+
+	return control
+end
+
+----- Standard Menu -----
+
+local function ResetMenuItem(button)
+	button:SetHidden(true)
+	button:ClearAnchors()
+	button.menuIndex = nil
+	button.OnSelect = nil
+end
+
+local function ResetCheckBox(checkBox)
+	ResetMenuItem(checkBox)
+	ZO_CheckButton_SetToggleFunction(checkBox, nil)
+end
+
+local upInside = false
+
+local function MenuItemFactory(pool)
+	local control = CreateControlFromVirtual("ZO_CustomMenuItem", ZO_Menu, "ZO_NotificationsRowButton", pool:GetNextControlId())
+	local function MouseEnter()
+		upInside = true
+		ZO_Menu_EnterItem(control)
+	end
+	local function MouseExit()
+		upInside = false
+		ZO_Menu_ExitItem(control)
+	end
+	local function MouseUp()
+		if upInside == true then
+			ZO_Menu_ClickItem(control, 1)
+		end
+	end
+
+	local label = wm:CreateControl("$(parent)Name", control, CT_LABEL)
+	label:SetAnchor(TOPLEFT)
+	control.nameLabel = label
+
+	control:SetHandler("OnMouseEnter", MouseEnter)
+	control:SetHandler("OnMouseExit", MouseExit)
+	control:SetHandler("OnMouseDown", IgnoreMouseDownEditFocusLoss)
+	control:SetHandler("OnMouseUp", MouseUp)
+
+	return control
+end
+
+local function CheckBoxFactory(pool)
+	local control = CreateControlFromVirtual("ZO_CustomMenuItemCheckButton", ZO_Menu, "ZO_CheckButton", pool:GetNextControlId())
+	control.nameLabel = control
+
+	local function MouseEnter()
+		ZO_Menu_EnterItem(control)
+	end
+	local function MouseExit()
+		ZO_Menu_ExitItem(control)
+	end
+	control:SetHandler("OnMouseEnter", MouseEnter)
+	control:SetHandler("OnMouseExit", MouseExit)
+	return control
+end
+
+local function DividerFactory(pool)
+	local control = CreateControlFromVirtual("ZO_CustomMenuDivider", ZO_Menu, "ZO_NotificationsRowButton", pool:GetNextControlId())
+	SetupDivider(pool, control)
+	return control
+end
+
+---- Hook points for context menu -----
+local function PreHook(objectTable, existingFunctionName, hookFunction)
+	if type(objectTable) == "string" then
+		hookFunction = existingFunctionName
+		existingFunctionName = objectTable
+		objectTable = _G
+	end
+
+	local existingFn = objectTable[existingFunctionName]
+	local newFn
+	if existingFn and type(existingFn) == "function" then
+		newFn = function(...)
+			hookFunction(...)
+			return existingFn(...)
+		end
+	else
+		newFn = hookFunction
+	end
+	objectTable[existingFunctionName] = newFn
+end
+
+local function HookContextMenu()
+	local category, registry, inventorySlot, slotActions, entered
+	local function Reset()
+		category, registry, inventorySlot, slotActions = 0, nil, nil, nil
+	end
+	local function RemoveMouseOverKeybinds()
+		if entered then
+			entered = false
+			lib.keybindRegistry:FireCallbacks("Exit")
+		end
+		Reset()
+	end
+	local function addCategory()
+		category = category + 1
+		registry:FireCallbacks(category, inventorySlot, slotActions)
+	end
+	local function AddSlots(...)
+		Reset()
+		inventorySlot, slotActions = ...
+		if slotActions.m_contextMenuMode then
+			registry = lib.contextMenuRegistry
+		else
+			entered = true
+			registry = lib.keybindRegistry
+		end
+	end
+	local function InsertToMenu()
+		if category < 4 and inventorySlot then
+			addCategory()
+		end
+	end
+	local function AppendToMenu()
+		if registry then
+			if inventorySlot then
+				while category <= 6 do addCategory() end
+			end
+			Reset()
+		end
+	end
+	Reset()
+
+	PreHook("ZO_InventorySlot_RemoveMouseOverKeybinds", RemoveMouseOverKeybinds)
+	PreHook("ZO_InventorySlot_OnMouseExit", RemoveMouseOverKeybinds)
+	PreHook("ZO_InventorySlot_DiscoverSlotActionsFromActionList", AddSlots)
+	PreHook(ZO_InventorySlotActions, "AddSlotAction", InsertToMenu)
+	PreHook(ZO_InventorySlotActions, "Show", AppendToMenu)
+	PreHook(ZO_InventorySlotActions, "GetPrimaryActionName", AppendToMenu)
+end
+
+----- Public API -----
+
+function AddCustomMenuItem(mytext, myfunction, itemType, myFont, normalColor, highlightColor, itemYPad, horizontalAlignment)
+	local orgItemPool = ZO_Menu.itemPool
+	local orgCheckboxItemPool = ZO_Menu.checkBoxPool
+
+	ZO_Menu.itemPool = mytext ~= lib.DIVIDER and lib.itemPool or lib.dividerPool
+	ZO_Menu.checkBoxPool = lib.checkBoxPool
+
+	local index = AddMenuItem(mytext, myfunction, itemType, myFont, normalColor, highlightColor, itemYPad, horizontalAlignment)
+
+	ZO_Menu.itemPool = orgItemPool
+	ZO_Menu.checkBoxPool = orgCheckboxItemPool
+
+	return index
+end
+
+function AddCustomSubMenuItem(mytext, entries, myfont, normalColor, highlightColor, itemYPad)
+	local function CreateSubMenu(control, state)
+		if (state == SUBMENU_ITEM_MOUSE_ENTER) then
+			lib.submenu:Clear()
+			local currentEntries = GetValueOrCallback(entries, ZO_Menu, control)
+			local entry
+			for i = 1, #currentEntries do
+				entry = currentEntries[i]
+				lib.submenu:AddItem(entry, myfont, normalColor, highlightColor, itemYPad)
+			end
+			lib.submenu:Show(control)
+		elseif (state == SUBMENU_ITEM_MOUSE_EXIT) then
+			lib.submenu:Clear()
+		end
+	end
+
+	local orgItemPool = ZO_Menu.itemPool
+	local orgCheckboxItemPool = ZO_Menu.checkBoxPool
+
+	ZO_Menu.itemPool = lib.submenuPool
+	ZO_Menu.checkBoxPool = lib.checkBoxPool
+
+	local index = AddMenuItem(mytext, CreateSubMenu, MENU_ADD_OPTION_LABEL, myfont, normalColor, highlightColor, itemYPad)
+
+	ZO_Menu.itemPool = orgItemPool
+	ZO_Menu.checkBoxPool = orgCheckboxItemPool
+
+	return index
+end
+
+local function HookClearMenu()
+	local orgClearMenu = ClearMenu
+	function ClearMenu()
+		ClearTimeout()
+		orgClearMenu()
+		lib.itemPool:ReleaseAllObjects()
+		lib.submenuPool:ReleaseAllObjects()
+		lib.checkBoxPool:ReleaseAllObjects()
+		lib.dividerPool:ReleaseAllObjects()
+		lib.submenu:Clear()
+	end
+end
+
+local function HookAddSlotAction()
+	function ZO_InventorySlotActions:AddCustomSlotAction(...)
+		local orgItemPool = ZO_Menu.itemPool
+		local orgCheckboxItemPool = ZO_Menu.checkBoxPool
+
+		ZO_Menu.itemPool = lib.itemPool
+		ZO_Menu.checkBoxPool = lib.checkBoxPool
+
+		self:AddSlotAction(...)
+
+		ZO_Menu.itemPool = orgItemPool
+		ZO_Menu.checkBoxPool = orgCheckboxItemPool
+	end
+end
+
+function lib:RegisterContextMenu(func, category, ...)
+	category = zo_clamp(category or self.CATEGORY_LATE, self.CATEGORY_EARLY, self.CATEGORY_LATE)
+	self.contextMenuRegistry:RegisterCallback(category, func, ...)
+end
+
+function lib:RegisterKeyStripEnter(func, category, ...)
+	category = zo_clamp(category or self.CATEGORY_LATE, self.CATEGORY_EARLY, self.CATEGORY_LATE)
+	self.keybindRegistry:RegisterCallback(category, func, ...)
+end
+
+function lib:RegisterKeyStripExit(func, ...)
+	self.keybindRegistry:RegisterCallback("Exit", func, ...)
+end
+
+---- Init -----
+
+local function OnAddonLoaded(event, name)
+	if name:find("^ZO_") then return end
+	EVENT_MANAGER:UnregisterForEvent(MAJOR, EVENT_ADD_ON_LOADED)
+	lib.itemPool = ZO_ObjectPool:New(MenuItemFactory, ResetMenuItem)
+	lib.submenuPool = ZO_ObjectPool:New(SubMenuItemFactory, ResetMenuItem)
+	lib.checkBoxPool = ZO_ObjectPool:New(CheckBoxFactory, ResetCheckBox)
+	lib.dividerPool = ZO_ObjectPool:New(DividerFactory, ResetMenuItem)
+	lib.submenu = Submenu:New("LibCustomMenuSubmenu")
+	HookClearMenu()
+	HookAddSlotAction()
+	HookContextMenu()
+end
+
+lib.contextMenuRegistry = lib.contextMenuRegistry or ZO_CallbackObject:New()
+lib.keybindRegistry = lib.keybindRegistry or ZO_CallbackObject:New()
+
+lib.CATEGORY_EARLY = 1
+lib.CATEGORY_PRIMARY = 2
+lib.CATEGORY_SECONDARY = 3
+lib.CATEGORY_TERTIARY = 4
+lib.CATEGORY_QUATERNARY = 5
+lib.CATEGORY_LATE = 6
+
+EVENT_MANAGER:UnregisterForEvent(MAJOR, EVENT_ADD_ON_LOADED)
+EVENT_MANAGER:RegisterForEvent(MAJOR, EVENT_ADD_ON_LOADED, OnAddonLoaded)