Visual

torsten.philipp [03-28-18 - 18:21]
Visual
Filename
.gitignore
TaosGroupTools/TaosGroupTools.lua
TaosGroupTools/TaosGroupTools.txt
TaosGroupTools/communication/Communicator.lua
TaosGroupTools/i18n/de.lua
TaosGroupTools/i18n/en.lua
TaosGroupTools/i18n/fr.lua
TaosGroupTools/libs/LibAddonMenu-2.0/LICENSE
TaosGroupTools/libs/LibAddonMenu-2.0/LibAddonMenu-2.0.lua
TaosGroupTools/libs/LibAddonMenu-2.0/controls/button.lua
TaosGroupTools/libs/LibAddonMenu-2.0/controls/checkbox.lua
TaosGroupTools/libs/LibAddonMenu-2.0/controls/colorpicker.lua
TaosGroupTools/libs/LibAddonMenu-2.0/controls/custom.lua
TaosGroupTools/libs/LibAddonMenu-2.0/controls/description.lua
TaosGroupTools/libs/LibAddonMenu-2.0/controls/divider.lua
TaosGroupTools/libs/LibAddonMenu-2.0/controls/dropdown.lua
TaosGroupTools/libs/LibAddonMenu-2.0/controls/editbox.lua
TaosGroupTools/libs/LibAddonMenu-2.0/controls/header.lua
TaosGroupTools/libs/LibAddonMenu-2.0/controls/iconpicker.lua
TaosGroupTools/libs/LibAddonMenu-2.0/controls/panel.lua
TaosGroupTools/libs/LibAddonMenu-2.0/controls/slider.lua
TaosGroupTools/libs/LibAddonMenu-2.0/controls/submenu.lua
TaosGroupTools/libs/LibAddonMenu-2.0/controls/texture.lua
TaosGroupTools/libs/LibGPS/LICENSE
TaosGroupTools/libs/LibGPS/LibGPS.lua
TaosGroupTools/libs/LibMapPing/LICENSE
TaosGroupTools/libs/LibMapPing/LibMapPing.lua
TaosGroupTools/libs/LibStub/LibStub.lua
TaosGroupTools/logic/CommandsHandler.lua
TaosGroupTools/logic/GroupHandler.lua
TaosGroupTools/logic/MapPingHandler.lua
TaosGroupTools/logic/SettingsHandler.lua
TaosGroupTools/logic/UltimateGroupHandler.lua
TaosGroupTools/ui/CompactSwimlaneList.lua
TaosGroupTools/ui/CompactSwimlaneList.xml
TaosGroupTools/ui/GroupUltimateSelector.lua
TaosGroupTools/ui/GroupUltimateSelector.xml
TaosGroupTools/ui/SettingsWindow.lua
TaosGroupTools/ui/SimpleList.lua
TaosGroupTools/ui/SimpleList.xml
TaosGroupTools/ui/SwimlaneList.lua
TaosGroupTools/ui/SwimlaneList.xml
TaosGroupTools/ui/UltimateGroupMenu.lua
TaosGroupTools/ui/VirtualControls.xml
TaosGroupTools/util/DebugLogger.lua
TaosGroupTools/util/UiHelper.lua
diff --git a/.gitignore b/.gitignore
index 2717331..87b9949 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,3 @@
-*.luaproj
-*.sln
+*.luaproj
+*.sln
 *.suo
\ No newline at end of file
diff --git a/TaosGroupTools/TaosGroupTools.lua b/TaosGroupTools/TaosGroupTools.lua
index 61d27a9..8d2acac 100644
--- a/TaosGroupTools/TaosGroupTools.lua
+++ b/TaosGroupTools/TaosGroupTools.lua
@@ -1,99 +1,99 @@
---[[
-	Addon: Taos Group Ultimate
-	Author: TProg Taonnor
-	Created by @Taonnor
-]]--
-
---[[
-	Global variables
-]]--
--- Callbacks
-TGU_GROUP_CHANGED = "TGU-GroupChanged"
-TGU_UNIT_GROUPED_CHANGED = "TGU-UnitGroupedChanged"
-TGU_MAP_PING_CHANGED = "TGU-MapPingChanged"
-TGU_PLAYER_DATA_CHANGED = "TGU-PlayerDataChanged"
-TGU_STYLE_CHANGED = "TGU-StyleChanged"
-TGU_MOVABLE_CHANGED = "TGU-MovableChanged"
-TGU_IS_ZONE_CHANGED = "TGU-IsZoneChanged"
-TGU_STATIC_ULTIMATE_ID_CHANGED = "TGU-StaticUltimateIDChanged"
-TGU_SWIMLANE_ULTIMATE_GROUP_ID_CHANGED = "TGU-SwimlaneUltimateGroupIdChanged"
-TGU_SHOW_ULTIMATE_GROUP_MENU = "TGU-ShowUltimateGroupMenu"
-TGU_SET_ULTIMATE_GROUP = "TGU-SetUltimateGroup"
-
---[[
-	Local variables
-]]--
-local MAJOR = "1"
-local MINOR = "5"
-local PATCH = "1"
-
-local ISMOCKED = false
-
-local LOG_NAME = "TGU-DebugLogger"
-local LOG_COMMAND = "/tgulogs"
-local TRACE_ACTIVE = false
-local DEBUG_ACTIVE = false
-local ERROR_ACTIVE = true
-local DIRECT_PRINT = true
-local CATCH_LUA_ERRORS = false
-
---[[
-	Table TaosGroupUltimate
-]]--
-TaosGroupUltimate = {}
-TaosGroupUltimate.__index = TaosGroupUltimate
-
---[[
-	Table Members
-]]--
-TaosGroupUltimate.Name = "TaosGroupTools"
-
---[[
-	TaosGroupUltimate:initialize initializes addon
-]]--
-function TaosGroupUltimate:initialize()
-    -- Initialize logging
-    local logger = TaosDebugLogger(LOG_NAME, LOG_COMMAND, TRACE_ACTIVE, DEBUG_ACTIVE, ERROR_ACTIVE, DIRECT_PRINT, CATCH_LUA_ERRORS)
-    logger:logTrace("TaosGroupUltimate:initialize")
-
-    -- Initialize settings
-    TGU_SettingsHandler.Initialize(logger)
-
-    -- Initialize communication
-    TGU_Communicator.Initialize(logger, ISMOCKED)
-
-    -- Initialize logic
-    TGU_GroupHandler.Initialize(logger, ISMOCKED)
-    TGU_MapPingHandler.Initialize(logger, ISMOCKED)
-    TGU_UltimateGroupHandler.Initialize(logger)
-    TGU_CommandsHandler.Initialize(logger)
-
-    -- Initialize ui
-    TGU_SettingsWindow.Initialize(logger, MAJOR, MINOR, PATCH)
-
-    TGU_UltimateGroupMenu.Initialize(logger)
-    TGU_GroupUltimateSelector.Initialize(logger)
-
-    TGU_SimpleList.Initialize(logger, ISMOCKED)
-    TGU_SwimlaneList.Initialize(logger, ISMOCKED)
-    TGU_CompactSwimlaneList.Initialize(logger, ISMOCKED)
-
-    logger:logTrace("TaosGroupUltimate:initialized")
-end
-
---[[
-	OnAddOnLoaded if TaosGroupUltimate is loaded, initialize
-]]--
-local function OnAddOnLoaded(eventCode, addOnName)
-	if addOnName == TaosGroupUltimate.Name then
-
-        -- Unregister Loaded Callback
-        EVENT_MANAGER:UnregisterForEvent(TaosGroupUltimate.Name, EVENT_ADD_ON_LOADED)
-
-        -- Initialize
-		TaosGroupUltimate:initialize()
-	end
-end
-
--- Register Loaded Callback
+--[[
+	Addon: Taos Group Ultimate
+	Author: TProg Taonnor
+	Created by @Taonnor
+]]--
+
+--[[
+	Global variables
+]]--
+-- Callbacks
+TGU_GROUP_CHANGED = "TGU-GroupChanged"
+TGU_UNIT_GROUPED_CHANGED = "TGU-UnitGroupedChanged"
+TGU_MAP_PING_CHANGED = "TGU-MapPingChanged"
+TGU_PLAYER_DATA_CHANGED = "TGU-PlayerDataChanged"
+TGU_STYLE_CHANGED = "TGU-StyleChanged"
+TGU_MOVABLE_CHANGED = "TGU-MovableChanged"
+TGU_IS_ZONE_CHANGED = "TGU-IsZoneChanged"
+TGU_STATIC_ULTIMATE_ID_CHANGED = "TGU-StaticUltimateIDChanged"
+TGU_SWIMLANE_ULTIMATE_GROUP_ID_CHANGED = "TGU-SwimlaneUltimateGroupIdChanged"
+TGU_SHOW_ULTIMATE_GROUP_MENU = "TGU-ShowUltimateGroupMenu"
+TGU_SET_ULTIMATE_GROUP = "TGU-SetUltimateGroup"
+
+--[[
+	Local variables
+]]--
+local MAJOR = "1"
+local MINOR = "5"
+local PATCH = "1"
+
+local ISMOCKED = false
+
+local LOG_NAME = "TGU-DebugLogger"
+local LOG_COMMAND = "/tgulogs"
+local TRACE_ACTIVE = false
+local DEBUG_ACTIVE = false
+local ERROR_ACTIVE = true
+local DIRECT_PRINT = true
+local CATCH_LUA_ERRORS = false
+
+--[[
+	Table TaosGroupUltimate
+]]--
+TaosGroupUltimate = {}
+TaosGroupUltimate.__index = TaosGroupUltimate
+
+--[[
+	Table Members
+]]--
+TaosGroupUltimate.Name = "TaosGroupTools"
+
+--[[
+	TaosGroupUltimate:initialize initializes addon
+]]--
+function TaosGroupUltimate:initialize()
+    -- Initialize logging
+    local logger = TaosDebugLogger(LOG_NAME, LOG_COMMAND, TRACE_ACTIVE, DEBUG_ACTIVE, ERROR_ACTIVE, DIRECT_PRINT, CATCH_LUA_ERRORS)
+    logger:logTrace("TaosGroupUltimate:initialize")
+
+    -- Initialize settings
+    TGU_SettingsHandler.Initialize(logger)
+
+    -- Initialize communication
+    TGU_Communicator.Initialize(logger, ISMOCKED)
+
+    -- Initialize logic
+    TGU_GroupHandler.Initialize(logger, ISMOCKED)
+    TGU_MapPingHandler.Initialize(logger, ISMOCKED)
+    TGU_UltimateGroupHandler.Initialize(logger)
+    TGU_CommandsHandler.Initialize(logger)
+
+    -- Initialize ui
+    TGU_SettingsWindow.Initialize(logger, MAJOR, MINOR, PATCH)
+
+    TGU_UltimateGroupMenu.Initialize(logger)
+    TGU_GroupUltimateSelector.Initialize(logger)
+
+    TGU_SimpleList.Initialize(logger, ISMOCKED)
+    TGU_SwimlaneList.Initialize(logger, ISMOCKED)
+    TGU_CompactSwimlaneList.Initialize(logger, ISMOCKED)
+
+    logger:logTrace("TaosGroupUltimate:initialized")
+end
+
+--[[
+	OnAddOnLoaded if TaosGroupUltimate is loaded, initialize
+]]--
+local function OnAddOnLoaded(eventCode, addOnName)
+	if addOnName == TaosGroupUltimate.Name then
+
+        -- Unregister Loaded Callback
+        EVENT_MANAGER:UnregisterForEvent(TaosGroupUltimate.Name, EVENT_ADD_ON_LOADED)
+
+        -- Initialize
+		TaosGroupUltimate:initialize()
+	end
+end
+
+-- Register Loaded Callback
 EVENT_MANAGER:RegisterForEvent(TaosGroupUltimate.Name, EVENT_ADD_ON_LOADED, OnAddOnLoaded);
\ No newline at end of file
diff --git a/TaosGroupTools/TaosGroupTools.txt b/TaosGroupTools/TaosGroupTools.txt
index 1e09518..2b5dfd9 100644
--- a/TaosGroupTools/TaosGroupTools.txt
+++ b/TaosGroupTools/TaosGroupTools.txt
@@ -1,53 +1,53 @@
-; This Add-on is not created by, affiliated with or sponsored by ZeniMax
-; Media Inc. or its affiliates. The Elder Scrolls and related logos are
-; registered trademarks or trademarks of ZeniMax Media Inc. in the United
-; States and/or other countries. All rights reserved.
-; You can read the full terms at https://account.elderscrollsonline.com/add-on-terms
-
-## APIVersion: 100023
-## Description: Provides different group features, like group ultimates
-## Title: |ca0a0a0Taos Group Tools|r 1.0.0
-## Version: 1.0.0
-## Author: |ca0a0a0TProg|r Taonnor
-## SavedVariables: TaosGroupToolSettings
-## OptionalDependsOn: LibAddonMenu-2.0
-
-libs/LibStub/LibStub.lua
-
-libs/LibAddonMenu-2.0/LibAddonMenu-2.0.lua
-libs/LibAddonMenu-2.0/controls/panel.lua
-libs/LibAddonMenu-2.0/controls/header.lua
-libs/LibAddonMenu-2.0/controls/checkbox.lua
-libs/LibAddonMenu-2.0/controls/dropdown.lua
-
-libs/LibMapPing/LibMapPing.lua
-libs/LibGPS/LibGPS.lua
-
-i18n/de.lua
-i18n/$(language).lua
-
-util/DebugLogger.lua
-util/UiHelper.lua
-
-communication/Communicator.lua
-communication/UltimateHandler.lua
-
-logic/SettingsHandler.lua
-logic/GroupHandler.lua
-logic/MapPingHandler.lua
-logic/UltimateGroupHandler.lua
-logic/CommandsHandler.lua
-
-ui/VirtualControls.xml
-ui/SettingsWindow.lua
-ui/GroupUltimateSelector.xml
-ui/GroupUltimateSelector.lua
-ui/SimpleList.xml
-ui/SimpleList.lua
-ui/SwimlaneList.xml
-ui/SwimlaneList.lua
-ui/CompactSwimlaneList.xml
-ui/CompactSwimlaneList.lua
-ui/UltimateGroupMenu.lua
-
+; This Add-on is not created by, affiliated with or sponsored by ZeniMax
+; Media Inc. or its affiliates. The Elder Scrolls and related logos are
+; registered trademarks or trademarks of ZeniMax Media Inc. in the United
+; States and/or other countries. All rights reserved.
+; You can read the full terms at https://account.elderscrollsonline.com/add-on-terms
+
+## APIVersion: 100023
+## Description: Provides different group features, like group ultimates
+## Title: |ca0a0a0Taos Group Tools|r 1.0.0
+## Version: 1.0.0
+## Author: |ca0a0a0TProg|r Taonnor
+## SavedVariables: TaosGroupToolSettings
+## OptionalDependsOn: LibAddonMenu-2.0
+
+libs/LibStub/LibStub.lua
+
+libs/LibAddonMenu-2.0/LibAddonMenu-2.0.lua
+libs/LibAddonMenu-2.0/controls/panel.lua
+libs/LibAddonMenu-2.0/controls/header.lua
+libs/LibAddonMenu-2.0/controls/checkbox.lua
+libs/LibAddonMenu-2.0/controls/dropdown.lua
+
+libs/LibMapPing/LibMapPing.lua
+libs/LibGPS/LibGPS.lua
+
+i18n/de.lua
+i18n/$(language).lua
+
+util/DebugLogger.lua
+util/UiHelper.lua
+
+communication/Communicator.lua
+communication/UltimateHandler.lua
+
+logic/SettingsHandler.lua
+logic/GroupHandler.lua
+logic/MapPingHandler.lua
+logic/UltimateGroupHandler.lua
+logic/CommandsHandler.lua
+
+ui/VirtualControls.xml
+ui/SettingsWindow.lua
+ui/GroupUltimateSelector.xml
+ui/GroupUltimateSelector.lua
+ui/SimpleList.xml
+ui/SimpleList.lua
+ui/SwimlaneList.xml
+ui/SwimlaneList.lua
+ui/CompactSwimlaneList.xml
+ui/CompactSwimlaneList.lua
+ui/UltimateGroupMenu.lua
+
 TaosGroupUltimate.lua
\ No newline at end of file
diff --git a/TaosGroupTools/communication/Communicator.lua b/TaosGroupTools/communication/Communicator.lua
index e461633..cbc1d58 100644
--- a/TaosGroupTools/communication/Communicator.lua
+++ b/TaosGroupTools/communication/Communicator.lua
@@ -1,256 +1,256 @@
---[[
-	Addon: Taos Group Ultimate
-	Author: TProg Taonnor
-	Created by @Taonnor
-]]--
-
---[[
-	Local variables
-]]--
-local LOG_ACTIVE = false
-
-local LMP = LibStub("LibMapPing")
-if(not LMP) then
-	error("Cannot load without LibMapPing")
-end
-
-local _logger = nil
-
-local ABILITY_COEFFICIENT = 100
-local ULTIMATE_COEFFICIENT = 1000
-
---[[
-	Table TGU_Communicator
-]]--
-TGU_Communicator = {}
-TGU_Communicator.__index = TGU_Communicator
-
---[[
-	Table Members
-]]--
-TGU_Communicator.Name = "TGU-Communicator"
-TGU_Communicator.IsMocked = false
-
---[[
-	Called on map ping from LibMapPing
-]]--
-function TGU_Communicator.OnMapPing(pingType, pingTag, offsetX, offsetY, isLocalPlayerOwner)
-    if (LOG_ACTIVE) then
-        _logger:logTrace("TGU_Communicator.OnMapPing")
-        --_logger:logDebug("pingTag; offsetX; offsetY", pingTag, offsetX, offsetY)
-    end
-
-	if (pingType == MAP_PIN_TYPE_PING and
-        LMP:IsPositionOnMap(offsetX, offsetY) and
-		TGU_Communicator.IsPossiblePing(offsetX, offsetY)) then
-
-        if (LOG_ACTIVE) then
-            _logger:logDebug("SuppressPing ->", pingType, pingTag)
-        end
-
-        LMP:SuppressPing(pingType, pingTag)
-
-        local abilityPing = TGU_Communicator.GetAbilityPing(offsetX)
-		local relativeUltimate = TGU_Communicator.GetRelativeUltimate(offsetY)
-
-        if (LOG_ACTIVE) then
-            _logger:logDebug("pingTag; abilityPing; relativeUltimate", pingTag, abilityPing, relativeUltimate)
-        end
-
-        if (abilityPing ~= -1 and relativeUltimate ~= -1) then
-            CALLBACK_MANAGER:FireCallbacks(TGU_MAP_PING_CHANGED, pingTag, abilityPing, relativeUltimate)
-        else
-            _logger:logError("TGU_Communicator.OnMapPing, Ping invalid abilityPing: " .. tostring(abilityPing) .. "; relativeUltimate: " .. tostring(relativeUltimate))
-        end
-    end
-end
-
---[[
-	Called on map ping from LibMapPing
-]]--
-function TGU_Communicator.OnMapPingFinished(pingType, pingTag, offsetX, offsetY, isLocalPlayerOwner)
-    offsetX, offsetY = LMP:GetMapPing(pingType, pingTag) -- load from LMP, because offsetX, offsetY from PING_EVENT_REMOVED are 0,0
-
-	if (LOG_ACTIVE) then
-        _logger:logTrace("TGU_Communicator.OnMapPingFinished")
-		--_logger:logDebug("pingType; pingTag; offsetX; offsetY", pingType, pingTag, offsetX, offsetY)
-    end
-
-    if (pingType == MAP_PIN_TYPE_PING and
-        LMP:IsPositionOnMap(offsetX, offsetY) and
-		TGU_Communicator.IsPossiblePing(offsetX, offsetY)) then
-
-        if (LOG_ACTIVE) then
-            _logger:logDebug("UnsuppressPing ->", pingType, pingTag)
-        end
-
-        LMP:UnsuppressPing(pingType, pingTag)
-    end
-end
-
---[[
-	Called on refresh of timer
-]]--
-function TGU_Communicator.SendData(abilityGroup)
-    if (LOG_ACTIVE) then
-		_logger:logTrace("TGU_Communicator.SendData")
-		--_logger:logDebug("abilityGroup", abilityGroup)
-	end
-
-    if (abilityGroup ~= nil) then
-        local current, max, effective_max = GetUnitPower("player", POWERTYPE_ULTIMATE)
-        local abilityCost = math.max(1, GetAbilityCost(abilityGroup.GroupAbilityId))
-
-        -- Mocked
-        if (TGU_Communicator.IsMocked) then
-            TGU_Communicator.SendFakePings()
-        -- Standard communication
-        else
-            local relativeUltimate = math.floor((current / abilityCost) * 100)
-
-	        if (relativeUltimate > 100) then
-		        relativeUltimate = 100
-	        end
-
-	        local abilityPing = abilityGroup.GroupAbilityPing / ABILITY_COEFFICIENT
-            local ultimatePing = 0.0001 -- Zero, if you send "0", the map ping will be invalid
-
-            if (relativeUltimate > 0) then
-	            ultimatePing = relativeUltimate / ULTIMATE_COEFFICIENT
-            end
-
-            if (LOG_ACTIVE) then
-		        --_logger:logDebug("abilityGroup.GroupName", abilityGroup.GroupName)
-                --_logger:logDebug("relativeUltimate", relativeUltimate)
-                --_logger:logDebug("abilityPing", abilityPing)
-                --_logger:logDebug("ultimatePing", ultimatePing)
-            end
-
-            LMP:SetMapPing(MAP_PIN_TYPE_PING, MAP_TYPE_LOCATION_CENTERED, abilityPing, ultimatePing)
-        end
-    else
-        _logger:logError("TGU_Communicator.SendData, abilityGroup is nil.")
-    end
-end
-
---[[
-	Check if map ping is in possible range
-]]--
-function TGU_Communicator.IsPossiblePing(offsetX, offsetY)
-    if (LOG_ACTIVE) then
-        --_logger:logTrace("TGU_Communicator.IsPossiblePing")
-        --_logger:logDebug("offsetX; offsetY", offsetX, offsetY)
-    end
-
-	local isValidPing = (offsetX ~= 0 or offsetY ~= 0)
-	local isCorrectOffsetX = (offsetX >= 0.009 and offsetX <= 0.30)
-	local isCorrectOffsetY = (offsetY >= 0.000 and offsetY <= 0.11)
-
-    if (LOG_ACTIVE) then
-        --_logger:logDebug("isValidPing; isCorrectOffsetX; isCorrectOffsetY", isValidPing, isCorrectOffsetX, isCorrectOffsetY)
-    end
-
-	return isValidPing and (isCorrectOffsetX and isCorrectOffsetY)
-end
-
---[[
-	Gets ability ID
-]]--
-function TGU_Communicator.GetAbilityPing(offset)
-    if (LOG_ACTIVE) then
-        --_logger:logTrace("TGU_Communicator.GetAbilityPing")
-        --_logger:logDebug("offset", offset)
-    end
-
-    if (offset > 0) then
-        local abilityPing = math.floor((offset * ABILITY_COEFFICIENT) + 0.5)
-        if (abilityPing >= 1 and abilityPing <= 29) then
-            return abilityPing
-        else
-            _logger:logError("offset is incorrect: " .. tostring(abilityPing) .. "; offset: " .. tostring(offset))
-            return -1
-        end
-    else
-        _logger:logError("offset is incorrect: " .. tostring(offset))
-        return -1
-    end
-end
-
---[[
-	Gets relative ultimate
-]]--
-function TGU_Communicator.GetRelativeUltimate(offset)
-    if (LOG_ACTIVE) then
-        --_logger:logTrace("TGU_Communicator.GetRelativeUltimate")
-        --_logger:logDebug("offset", offset)
-    end
-
-    if (offset >= 0) then
-        local relativeUltimate = math.floor((offset * ULTIMATE_COEFFICIENT) + 0.5)
-        if (relativeUltimate >= 0 and relativeUltimate <= 100) then
-            return relativeUltimate
-        elseif (relativeUltimate >= 100 and relativeUltimate <= 110) then
-            if (LOG_ACTIVE) then _logger:logDebug("offset; relativeUltimate", offset, relativeUltimate) end
-            return 100
-        else
-            _logger:logError("relativeUltimate is incorrect: " .. tostring(relativeUltimate) .. "; offset: " .. tostring(offset))
-            return -1
-        end
-    else
-        _logger:logError("offset is incorrect: " .. tostring(offset))
-        return -1
-    end
-end
-
---[[
-	Sends fake pings for all group members
-]]--
-function TGU_Communicator.SendFakePings()
-    if (LOG_ACTIVE) then  _logger:logTrace("TGU_Communicator.SendFakePings") end
-
-    local ultimateGroups = TGU_UltimateGroupHandler.GetUltimateGroups()
-
-    -- Directly send to test only UI
-    CALLBACK_MANAGER:FireCallbacks(TGU_MAP_PING_CHANGED, "group1", ultimateGroups[1].GroupAbilityPing, 100)
-    CALLBACK_MANAGER:FireCallbacks(TGU_MAP_PING_CHANGED, "group2", ultimateGroups[1].GroupAbilityPing, 100)
-    CALLBACK_MANAGER:FireCallbacks(TGU_MAP_PING_CHANGED, "group3", ultimateGroups[1].GroupAbilityPing, math.random(90, 100))
-    CALLBACK_MANAGER:FireCallbacks(TGU_MAP_PING_CHANGED, "group4", ultimateGroups[1].GroupAbilityPing, math.random(90, 100))
-    CALLBACK_MANAGER:FireCallbacks(TGU_MAP_PING_CHANGED, "group5", ultimateGroups[1].GroupAbilityPing, math.random(90, 100))
-    CALLBACK_MANAGER:FireCallbacks(TGU_MAP_PING_CHANGED, "group6", ultimateGroups[6].GroupAbilityPing, math.random(90, 100))
-    CALLBACK_MANAGER:FireCallbacks(TGU_MAP_PING_CHANGED, "group7", ultimateGroups[6].GroupAbilityPing, 100)
-    CALLBACK_MANAGER:FireCallbacks(TGU_MAP_PING_CHANGED, "group8", ultimateGroups[6].GroupAbilityPing, math.random(90, 100))
-    CALLBACK_MANAGER:FireCallbacks(TGU_MAP_PING_CHANGED, "group9", ultimateGroups[6].GroupAbilityPing, math.random(90, 100))
-    CALLBACK_MANAGER:FireCallbacks(TGU_MAP_PING_CHANGED, "group10", ultimateGroups[6].GroupAbilityPing, math.random(90, 100))
-    CALLBACK_MANAGER:FireCallbacks(TGU_MAP_PING_CHANGED, "group11", ultimateGroups[1].GroupAbilityPing, math.random(90, 100))
-    CALLBACK_MANAGER:FireCallbacks(TGU_MAP_PING_CHANGED, "group12", ultimateGroups[6].GroupAbilityPing, 100)
-    CALLBACK_MANAGER:FireCallbacks(TGU_MAP_PING_CHANGED, "group13", ultimateGroups[16].GroupAbilityPing, 100)
-    CALLBACK_MANAGER:FireCallbacks(TGU_MAP_PING_CHANGED, "group14", ultimateGroups[16].GroupAbilityPing, 100)
-    CALLBACK_MANAGER:FireCallbacks(TGU_MAP_PING_CHANGED, "group15", ultimateGroups[16].GroupAbilityPing, math.random(90, 100))
-    CALLBACK_MANAGER:FireCallbacks(TGU_MAP_PING_CHANGED, "group16", ultimateGroups[16].GroupAbilityPing, math.random(90, 100))
-    CALLBACK_MANAGER:FireCallbacks(TGU_MAP_PING_CHANGED, "group17", ultimateGroups[16].GroupAbilityPing, math.random(90, 100))
-    CALLBACK_MANAGER:FireCallbacks(TGU_MAP_PING_CHANGED, "group18", ultimateGroups[16].GroupAbilityPing, math.random(90, 100))
-    CALLBACK_MANAGER:FireCallbacks(TGU_MAP_PING_CHANGED, "group19", ultimateGroups[13].GroupAbilityPing, 100)
-    CALLBACK_MANAGER:FireCallbacks(TGU_MAP_PING_CHANGED, "group20", ultimateGroups[13].GroupAbilityPing, 100)
-    CALLBACK_MANAGER:FireCallbacks(TGU_MAP_PING_CHANGED, "group21", ultimateGroups[13].GroupAbilityPing, math.random(90, 100))
-    CALLBACK_MANAGER:FireCallbacks(TGU_MAP_PING_CHANGED, "group22", ultimateGroups[13].GroupAbilityPing, math.random(90, 100))
-    CALLBACK_MANAGER:FireCallbacks(TGU_MAP_PING_CHANGED, "group23", ultimateGroups[13].GroupAbilityPing, math.random(90, 100))
-    CALLBACK_MANAGER:FireCallbacks(TGU_MAP_PING_CHANGED, "group24", ultimateGroups[13].GroupAbilityPing, math.random(90, 100))
-end
-
---[[
-	Initialize initializes TGU_Communicator
-]]--
-function TGU_Communicator.Initialize(logger, isMocked)
-    if (LOG_ACTIVE) then
-        logger:logTrace("TGU_Communicator.Initialize")
-        logger:logDebug("isMocked", isMocked)
-    end
-
-    _logger = logger
-
-    TGU_Communicator.IsMocked = isMocked
-
-    -- Register events
-    LMP:RegisterCallback("BeforePingAdded", TGU_Communicator.OnMapPing)
-    LMP:RegisterCallback("AfterPingRemoved", TGU_Communicator.OnMapPingFinished)
+--[[
+	Addon: Taos Group Ultimate
+	Author: TProg Taonnor
+	Created by @Taonnor
+]]--
+
+--[[
+	Local variables
+]]--
+local LOG_ACTIVE = false
+
+local LMP = LibStub("LibMapPing")
+if(not LMP) then
+	error("Cannot load without LibMapPing")
+end
+
+local _logger = nil
+
+local ABILITY_COEFFICIENT = 100
+local ULTIMATE_COEFFICIENT = 1000
+
+--[[
+	Table TGU_Communicator
+]]--
+TGU_Communicator = {}
+TGU_Communicator.__index = TGU_Communicator
+
+--[[
+	Table Members
+]]--
+TGU_Communicator.Name = "TGU-Communicator"
+TGU_Communicator.IsMocked = false
+
+--[[
+	Called on map ping from LibMapPing
+]]--
+function TGU_Communicator.OnMapPing(pingType, pingTag, offsetX, offsetY, isLocalPlayerOwner)
+    if (LOG_ACTIVE) then
+        _logger:logTrace("TGU_Communicator.OnMapPing")
+        --_logger:logDebug("pingTag; offsetX; offsetY", pingTag, offsetX, offsetY)
+    end
+
+	if (pingType == MAP_PIN_TYPE_PING and
+        LMP:IsPositionOnMap(offsetX, offsetY) and
+		TGU_Communicator.IsPossiblePing(offsetX, offsetY)) then
+
+        if (LOG_ACTIVE) then
+            _logger:logDebug("SuppressPing ->", pingType, pingTag)
+        end
+
+        LMP:SuppressPing(pingType, pingTag)
+
+        local abilityPing = TGU_Communicator.GetAbilityPing(offsetX)
+		local relativeUltimate = TGU_Communicator.GetRelativeUltimate(offsetY)
+
+        if (LOG_ACTIVE) then
+            _logger:logDebug("pingTag; abilityPing; relativeUltimate", pingTag, abilityPing, relativeUltimate)
+        end
+
+        if (abilityPing ~= -1 and relativeUltimate ~= -1) then
+            CALLBACK_MANAGER:FireCallbacks(TGU_MAP_PING_CHANGED, pingTag, abilityPing, relativeUltimate)
+        else
+            _logger:logError("TGU_Communicator.OnMapPing, Ping invalid abilityPing: " .. tostring(abilityPing) .. "; relativeUltimate: " .. tostring(relativeUltimate))
+        end
+    end
+end
+
+--[[
+	Called on map ping from LibMapPing
+]]--
+function TGU_Communicator.OnMapPingFinished(pingType, pingTag, offsetX, offsetY, isLocalPlayerOwner)
+    offsetX, offsetY = LMP:GetMapPing(pingType, pingTag) -- load from LMP, because offsetX, offsetY from PING_EVENT_REMOVED are 0,0
+
+	if (LOG_ACTIVE) then
+        _logger:logTrace("TGU_Communicator.OnMapPingFinished")
+		--_logger:logDebug("pingType; pingTag; offsetX; offsetY", pingType, pingTag, offsetX, offsetY)
+    end
+
+    if (pingType == MAP_PIN_TYPE_PING and
+        LMP:IsPositionOnMap(offsetX, offsetY) and
+		TGU_Communicator.IsPossiblePing(offsetX, offsetY)) then
+
+        if (LOG_ACTIVE) then
+            _logger:logDebug("UnsuppressPing ->", pingType, pingTag)
+        end
+
+        LMP:UnsuppressPing(pingType, pingTag)
+    end
+end
+
+--[[
+	Called on refresh of timer
+]]--
+function TGU_Communicator.SendData(abilityGroup)
+    if (LOG_ACTIVE) then
+		_logger:logTrace("TGU_Communicator.SendData")
+		--_logger:logDebug("abilityGroup", abilityGroup)
+	end
+
+    if (abilityGroup ~= nil) then
+        local current, max, effective_max = GetUnitPower("player", POWERTYPE_ULTIMATE)
+        local abilityCost = math.max(1, GetAbilityCost(abilityGroup.GroupAbilityId))
+
+        -- Mocked
+        if (TGU_Communicator.IsMocked) then
+            TGU_Communicator.SendFakePings()
+        -- Standard communication
+        else
+            local relativeUltimate = math.floor((current / abilityCost) * 100)
+
+	        if (relativeUltimate > 100) then
+		        relativeUltimate = 100
+	        end
+
+	        local abilityPing = abilityGroup.GroupAbilityPing / ABILITY_COEFFICIENT
+            local ultimatePing = 0.0001 -- Zero, if you send "0", the map ping will be invalid
+
+            if (relativeUltimate > 0) then
+	            ultimatePing = relativeUltimate / ULTIMATE_COEFFICIENT
+            end
+
+            if (LOG_ACTIVE) then
+		        --_logger:logDebug("abilityGroup.GroupName", abilityGroup.GroupName)
+                --_logger:logDebug("relativeUltimate", relativeUltimate)
+                --_logger:logDebug("abilityPing", abilityPing)
+                --_logger:logDebug("ultimatePing", ultimatePing)
+            end
+
+            LMP:SetMapPing(MAP_PIN_TYPE_PING, MAP_TYPE_LOCATION_CENTERED, abilityPing, ultimatePing)
+        end
+    else
+        _logger:logError("TGU_Communicator.SendData, abilityGroup is nil.")
+    end
+end
+
+--[[
+	Check if map ping is in possible range
+]]--
+function TGU_Communicator.IsPossiblePing(offsetX, offsetY)
+    if (LOG_ACTIVE) then
+        --_logger:logTrace("TGU_Communicator.IsPossiblePing")
+        --_logger:logDebug("offsetX; offsetY", offsetX, offsetY)
+    end
+
+	local isValidPing = (offsetX ~= 0 or offsetY ~= 0)
+	local isCorrectOffsetX = (offsetX >= 0.009 and offsetX <= 0.30)
+	local isCorrectOffsetY = (offsetY >= 0.000 and offsetY <= 0.11)
+
+    if (LOG_ACTIVE) then
+        --_logger:logDebug("isValidPing; isCorrectOffsetX; isCorrectOffsetY", isValidPing, isCorrectOffsetX, isCorrectOffsetY)
+    end
+
+	return isValidPing and (isCorrectOffsetX and isCorrectOffsetY)
+end
+
+--[[
+	Gets ability ID
+]]--
+function TGU_Communicator.GetAbilityPing(offset)
+    if (LOG_ACTIVE) then
+        --_logger:logTrace("TGU_Communicator.GetAbilityPing")
+        --_logger:logDebug("offset", offset)
+    end
+
+    if (offset > 0) then
+        local abilityPing = math.floor((offset * ABILITY_COEFFICIENT) + 0.5)
+        if (abilityPing >= 1 and abilityPing <= 29) then
+            return abilityPing
+        else
+            _logger:logError("offset is incorrect: " .. tostring(abilityPing) .. "; offset: " .. tostring(offset))
+            return -1
+        end
+    else
+        _logger:logError("offset is incorrect: " .. tostring(offset))
+        return -1
+    end
+end
+
+--[[
+	Gets relative ultimate
+]]--
+function TGU_Communicator.GetRelativeUltimate(offset)
+    if (LOG_ACTIVE) then
+        --_logger:logTrace("TGU_Communicator.GetRelativeUltimate")
+        --_logger:logDebug("offset", offset)
+    end
+
+    if (offset >= 0) then
+        local relativeUltimate = math.floor((offset * ULTIMATE_COEFFICIENT) + 0.5)
+        if (relativeUltimate >= 0 and relativeUltimate <= 100) then
+            return relativeUltimate
+        elseif (relativeUltimate >= 100 and relativeUltimate <= 110) then
+            if (LOG_ACTIVE) then _logger:logDebug("offset; relativeUltimate", offset, relativeUltimate) end
+            return 100
+        else
+            _logger:logError("relativeUltimate is incorrect: " .. tostring(relativeUltimate) .. "; offset: " .. tostring(offset))
+            return -1
+        end
+    else
+        _logger:logError("offset is incorrect: " .. tostring(offset))
+        return -1
+    end
+end
+
+--[[
+	Sends fake pings for all group members
+]]--
+function TGU_Communicator.SendFakePings()
+    if (LOG_ACTIVE) then  _logger:logTrace("TGU_Communicator.SendFakePings") end
+
+    local ultimateGroups = TGU_UltimateGroupHandler.GetUltimateGroups()
+
+    -- Directly send to test only UI
+    CALLBACK_MANAGER:FireCallbacks(TGU_MAP_PING_CHANGED, "group1", ultimateGroups[1].GroupAbilityPing, 100)
+    CALLBACK_MANAGER:FireCallbacks(TGU_MAP_PING_CHANGED, "group2", ultimateGroups[1].GroupAbilityPing, 100)
+    CALLBACK_MANAGER:FireCallbacks(TGU_MAP_PING_CHANGED, "group3", ultimateGroups[1].GroupAbilityPing, math.random(90, 100))
+    CALLBACK_MANAGER:FireCallbacks(TGU_MAP_PING_CHANGED, "group4", ultimateGroups[1].GroupAbilityPing, math.random(90, 100))
+    CALLBACK_MANAGER:FireCallbacks(TGU_MAP_PING_CHANGED, "group5", ultimateGroups[1].GroupAbilityPing, math.random(90, 100))
+    CALLBACK_MANAGER:FireCallbacks(TGU_MAP_PING_CHANGED, "group6", ultimateGroups[6].GroupAbilityPing, math.random(90, 100))
+    CALLBACK_MANAGER:FireCallbacks(TGU_MAP_PING_CHANGED, "group7", ultimateGroups[6].GroupAbilityPing, 100)
+    CALLBACK_MANAGER:FireCallbacks(TGU_MAP_PING_CHANGED, "group8", ultimateGroups[6].GroupAbilityPing, math.random(90, 100))
+    CALLBACK_MANAGER:FireCallbacks(TGU_MAP_PING_CHANGED, "group9", ultimateGroups[6].GroupAbilityPing, math.random(90, 100))
+    CALLBACK_MANAGER:FireCallbacks(TGU_MAP_PING_CHANGED, "group10", ultimateGroups[6].GroupAbilityPing, math.random(90, 100))
+    CALLBACK_MANAGER:FireCallbacks(TGU_MAP_PING_CHANGED, "group11", ultimateGroups[1].GroupAbilityPing, math.random(90, 100))
+    CALLBACK_MANAGER:FireCallbacks(TGU_MAP_PING_CHANGED, "group12", ultimateGroups[6].GroupAbilityPing, 100)
+    CALLBACK_MANAGER:FireCallbacks(TGU_MAP_PING_CHANGED, "group13", ultimateGroups[16].GroupAbilityPing, 100)
+    CALLBACK_MANAGER:FireCallbacks(TGU_MAP_PING_CHANGED, "group14", ultimateGroups[16].GroupAbilityPing, 100)
+    CALLBACK_MANAGER:FireCallbacks(TGU_MAP_PING_CHANGED, "group15", ultimateGroups[16].GroupAbilityPing, math.random(90, 100))
+    CALLBACK_MANAGER:FireCallbacks(TGU_MAP_PING_CHANGED, "group16", ultimateGroups[16].GroupAbilityPing, math.random(90, 100))
+    CALLBACK_MANAGER:FireCallbacks(TGU_MAP_PING_CHANGED, "group17", ultimateGroups[16].GroupAbilityPing, math.random(90, 100))
+    CALLBACK_MANAGER:FireCallbacks(TGU_MAP_PING_CHANGED, "group18", ultimateGroups[16].GroupAbilityPing, math.random(90, 100))
+    CALLBACK_MANAGER:FireCallbacks(TGU_MAP_PING_CHANGED, "group19", ultimateGroups[13].GroupAbilityPing, 100)
+    CALLBACK_MANAGER:FireCallbacks(TGU_MAP_PING_CHANGED, "group20", ultimateGroups[13].GroupAbilityPing, 100)
+    CALLBACK_MANAGER:FireCallbacks(TGU_MAP_PING_CHANGED, "group21", ultimateGroups[13].GroupAbilityPing, math.random(90, 100))
+    CALLBACK_MANAGER:FireCallbacks(TGU_MAP_PING_CHANGED, "group22", ultimateGroups[13].GroupAbilityPing, math.random(90, 100))
+    CALLBACK_MANAGER:FireCallbacks(TGU_MAP_PING_CHANGED, "group23", ultimateGroups[13].GroupAbilityPing, math.random(90, 100))
+    CALLBACK_MANAGER:FireCallbacks(TGU_MAP_PING_CHANGED, "group24", ultimateGroups[13].GroupAbilityPing, math.random(90, 100))
+end
+
+--[[
+	Initialize initializes TGU_Communicator
+]]--
+function TGU_Communicator.Initialize(logger, isMocked)
+    if (LOG_ACTIVE) then
+        logger:logTrace("TGU_Communicator.Initialize")
+        logger:logDebug("isMocked", isMocked)
+    end
+
+    _logger = logger
+
+    TGU_Communicator.IsMocked = isMocked
+
+    -- Register events
+    LMP:RegisterCallback("BeforePingAdded", TGU_Communicator.OnMapPing)
+    LMP:RegisterCallback("AfterPingRemoved", TGU_Communicator.OnMapPingFinished)
 end
\ No newline at end of file
diff --git a/TaosGroupTools/i18n/de.lua b/TaosGroupTools/i18n/de.lua
index 2e628fd..7f1f34f 100644
--- a/TaosGroupTools/i18n/de.lua
+++ b/TaosGroupTools/i18n/de.lua
@@ -1,51 +1,51 @@
--- Taos Group Ultimate german localization file
-
--- Options
-local strings = {
-    TGU_OPTIONS_HEADER =                 "Optionen",
-    TGU_OPTIONS_DRAG_LABEL =             "Elemente verschieben",
-    TGU_OPTIONS_DRAG_TOOLTIP =           "Wenn die Option aktiviert ist, können alle Elemente verschoben werden.",
-    TGU_OPTIONS_ONLY_AVA_LABEL =         "Nur im AvA Gebiet anzeigen",
-    TGU_OPTIONS_ONLY_AVA_TOOLTIP =       "Wenn die Option aktiviert ist, sind alle Elemente nur in Cyrodiil (AvA) sichtbar.",
-    TGU_OPTIONS_USE_SORTING_LABEL =      "Listen nach Ulti-Fortschritt sortieren",
-    TGU_OPTIONS_USE_SORTING_TOOLTIP =    "Wenn die Option aktiviert ist, werden alle Listen nach Ulti-Fortschritt sortiert (Volle Ultis oben).",
-    TGU_OPTIONS_STYLE_LABEL =            "Style auswählen",
-    TGU_OPTIONS_STYLE_TOOLTIP =          "Wähle den gewünschten Style aus. Simple-Liste, Schwimmbahn-Liste oder Kompakte Schwimmbahn-Liste",
-    TGU_OPTIONS_STYLE_SIMPLE =           "Simple-Liste",
-    TGU_OPTIONS_STYLE_SWIM =             "Schwimmbahn-Liste",
-    TGU_OPTIONS_STYLE_SHORT_SWIM =       "Kompakte Schwimmbahn-Liste",
-    TGU_DESCRIPTIONS_NEGATE =            "Magienegation Ultimates der Zauberer Klasse",
-    TGU_DESCRIPTIONS_ATRO =              "Atronach Ultimates der Zauberer Klasse",
-    TGU_DESCRIPTIONS_OVER =              "Überladung Ultimates der Zauberer Klasse",
-    TGU_DESCRIPTIONS_SWEEP =             "Schwung Ultimates der Templer Klasse",
-    TGU_DESCRIPTIONS_NOVA =              "Nova Ultimates der Templer Klasse",
-    TGU_DESCRIPTIONS_TPHEAL =            "Heil Ultimates der Templer Klasse",
-    TGU_DESCRIPTIONS_STAND =             "Standarten Ultimates der Drachenritter Klasse",
-    TGU_DESCRIPTIONS_LEAP =              "Drachensprung Ultimates der Drachenritter Klasse",
-    TGU_DESCRIPTIONS_MAGMA =             "Magma Ultimates der Drachenritter Klasse",
-    TGU_DESCRIPTIONS_STROKE =            "Todesstoß Ultimates der Nachtklingen Klasse",
-    TGU_DESCRIPTIONS_VEIL =              "Schleier Ultimates der Nachtklingen Klasse",
-    TGU_DESCRIPTIONS_NBSOUL =            "Seelenfetzen Ultimates der Nachtklingen Klasse",
-    TGU_DESCRIPTIONS_FREEZE =            "Sturm Ultimates der Hüter Klasse",
-    TGU_DESCRIPTIONS_WDHEAL =            "Hain Ultimates der Hüter Klasse",
-    TGU_DESCRIPTIONS_ICE =               "Frost Ultimates des Zerstörungsstabes",
-    TGU_DESCRIPTIONS_FIRE =              "Feuer Ultimates des Zerstörungsstabes",
-    TGU_DESCRIPTIONS_LIGHT =             "Blitz Ultimates des Zerstörungsstabes",
-    TGU_DESCRIPTIONS_STHEAL =            "Heil Ultimates des Heilstabes",
-    TGU_DESCRIPTIONS_BERSERK =           "Berserker Ultimates des Zweihänders",
-    TGU_DESCRIPTIONS_SHIELD =            "Schild Ultimates von Einhand und Schild",
-    TGU_DESCRIPTIONS_DUAL =              "Zerfleischen Ultimates von Zwei Waffen",
-    TGU_DESCRIPTIONS_BOW =               "Schnellfeuer Ultimates von Bogen",
-    TGU_DESCRIPTIONS_SOUL =              "Seelenschlag Ultimates aus der Seelenmagie Linie",
-    TGU_DESCRIPTIONS_WERE =              "Werwolfverwandlung Ultimates aus der Werwolf Linie",
-    TGU_DESCRIPTIONS_VAMP =              "Schwarm Ultimates aus der Vampir Linie",
-    TGU_DESCRIPTIONS_METEOR =            "Meteor Ultimates aus der Magiergilde",
-    TGU_DESCRIPTIONS_DAWN =              "Dämmerbrecher Ultimates aus der Kriegergilde",
-    TGU_DESCRIPTIONS_BARRIER =           "Barriere Ultimates aus der Unterstützung Linie",
-    TGU_DESCRIPTIONS_HORN =              "Kriegshorn Ultimates aus der Sturmangriff Linie"
-}
-
-for stringId, stringValue in pairs(strings) do
-	ZO_CreateStringId(stringId, stringValue)
-	SafeAddVersion(stringId, 1)
+-- Taos Group Ultimate german localization file
+
+-- Options
+local strings = {
+    TGU_OPTIONS_HEADER =                 "Optionen",
+    TGU_OPTIONS_DRAG_LABEL =             "Elemente verschieben",
+    TGU_OPTIONS_DRAG_TOOLTIP =           "Wenn die Option aktiviert ist, können alle Elemente verschoben werden.",
+    TGU_OPTIONS_ONLY_AVA_LABEL =         "Nur im AvA Gebiet anzeigen",
+    TGU_OPTIONS_ONLY_AVA_TOOLTIP =       "Wenn die Option aktiviert ist, sind alle Elemente nur in Cyrodiil (AvA) sichtbar.",
+    TGU_OPTIONS_USE_SORTING_LABEL =      "Listen nach Ulti-Fortschritt sortieren",
+    TGU_OPTIONS_USE_SORTING_TOOLTIP =    "Wenn die Option aktiviert ist, werden alle Listen nach Ulti-Fortschritt sortiert (Volle Ultis oben).",
+    TGU_OPTIONS_STYLE_LABEL =            "Style auswählen",
+    TGU_OPTIONS_STYLE_TOOLTIP =          "Wähle den gewünschten Style aus. Simple-Liste, Schwimmbahn-Liste oder Kompakte Schwimmbahn-Liste",
+    TGU_OPTIONS_STYLE_SIMPLE =           "Simple-Liste",
+    TGU_OPTIONS_STYLE_SWIM =             "Schwimmbahn-Liste",
+    TGU_OPTIONS_STYLE_SHORT_SWIM =       "Kompakte Schwimmbahn-Liste",
+    TGU_DESCRIPTIONS_NEGATE =            "Magienegation Ultimates der Zauberer Klasse",
+    TGU_DESCRIPTIONS_ATRO =              "Atronach Ultimates der Zauberer Klasse",
+    TGU_DESCRIPTIONS_OVER =              "Überladung Ultimates der Zauberer Klasse",
+    TGU_DESCRIPTIONS_SWEEP =             "Schwung Ultimates der Templer Klasse",
+    TGU_DESCRIPTIONS_NOVA =              "Nova Ultimates der Templer Klasse",
+    TGU_DESCRIPTIONS_TPHEAL =            "Heil Ultimates der Templer Klasse",
+    TGU_DESCRIPTIONS_STAND =             "Standarten Ultimates der Drachenritter Klasse",
+    TGU_DESCRIPTIONS_LEAP =              "Drachensprung Ultimates der Drachenritter Klasse",
+    TGU_DESCRIPTIONS_MAGMA =             "Magma Ultimates der Drachenritter Klasse",
+    TGU_DESCRIPTIONS_STROKE =            "Todesstoß Ultimates der Nachtklingen Klasse",
+    TGU_DESCRIPTIONS_VEIL =              "Schleier Ultimates der Nachtklingen Klasse",
+    TGU_DESCRIPTIONS_NBSOUL =            "Seelenfetzen Ultimates der Nachtklingen Klasse",
+    TGU_DESCRIPTIONS_FREEZE =            "Sturm Ultimates der Hüter Klasse",
+    TGU_DESCRIPTIONS_WDHEAL =            "Hain Ultimates der Hüter Klasse",
+    TGU_DESCRIPTIONS_ICE =               "Frost Ultimates des Zerstörungsstabes",
+    TGU_DESCRIPTIONS_FIRE =              "Feuer Ultimates des Zerstörungsstabes",
+    TGU_DESCRIPTIONS_LIGHT =             "Blitz Ultimates des Zerstörungsstabes",
+    TGU_DESCRIPTIONS_STHEAL =            "Heil Ultimates des Heilstabes",
+    TGU_DESCRIPTIONS_BERSERK =           "Berserker Ultimates des Zweihänders",
+    TGU_DESCRIPTIONS_SHIELD =            "Schild Ultimates von Einhand und Schild",
+    TGU_DESCRIPTIONS_DUAL =              "Zerfleischen Ultimates von Zwei Waffen",
+    TGU_DESCRIPTIONS_BOW =               "Schnellfeuer Ultimates von Bogen",
+    TGU_DESCRIPTIONS_SOUL =              "Seelenschlag Ultimates aus der Seelenmagie Linie",
+    TGU_DESCRIPTIONS_WERE =              "Werwolfverwandlung Ultimates aus der Werwolf Linie",
+    TGU_DESCRIPTIONS_VAMP =              "Schwarm Ultimates aus der Vampir Linie",
+    TGU_DESCRIPTIONS_METEOR =            "Meteor Ultimates aus der Magiergilde",
+    TGU_DESCRIPTIONS_DAWN =              "Dämmerbrecher Ultimates aus der Kriegergilde",
+    TGU_DESCRIPTIONS_BARRIER =           "Barriere Ultimates aus der Unterstützung Linie",
+    TGU_DESCRIPTIONS_HORN =              "Kriegshorn Ultimates aus der Sturmangriff Linie"
+}
+
+for stringId, stringValue in pairs(strings) do
+	ZO_CreateStringId(stringId, stringValue)
+	SafeAddVersion(stringId, 1)
 end
\ No newline at end of file
diff --git a/TaosGroupTools/i18n/en.lua b/TaosGroupTools/i18n/en.lua
index 501e9ae..8a216bc 100644
--- a/TaosGroupTools/i18n/en.lua
+++ b/TaosGroupTools/i18n/en.lua
@@ -1,51 +1,51 @@
--- Taos Group Ultimate english localization file
-
--- Options
-local strings = {
-    TGU_OPTIONS_HEADER =                 "Options",
-    TGU_OPTIONS_DRAG_LABEL =             "Drag elements",
-    TGU_OPTIONS_DRAG_TOOLTIP =           "If activated, you can drag all elements.",
-    TGU_OPTIONS_ONLY_AVA_LABEL =         "Show only in AvA",
-    TGU_OPTIONS_ONLY_AVA_TOOLTIP =       "If activated, all elements will only be visible in Cyrodiil (AvA).",
-    TGU_OPTIONS_USE_SORTING_LABEL =      "Sort lists by ultimate progress",
-    TGU_OPTIONS_USE_SORTING_TOOLTIP =    "If activated, all lists will be sorted by ultimate progress (Maximum on top).",
-    TGU_OPTIONS_STYLE_LABEL =            "Choose style",
-    TGU_OPTIONS_STYLE_TOOLTIP =          "Choose your style. Simple-List, Swimlane-List or Compact Swimlane-List",
-    TGU_OPTIONS_STYLE_SIMPLE =           "Simple-List",
-    TGU_OPTIONS_STYLE_SWIM =             "Swimlane-List",
-    TGU_OPTIONS_STYLE_SHORT_SWIM =       "Compact Swimlane-List",
-    TGU_DESCRIPTIONS_NEGATE =            "Negate ultimates from Sorcerer class",
-    TGU_DESCRIPTIONS_ATRO =              "Atronach ultimates from Sorcerer class",
-    TGU_DESCRIPTIONS_OVER =              "Overload ultimates from Sorcerer class",
-    TGU_DESCRIPTIONS_SWEEP =             "Sweep ultimates from Templar class",
-    TGU_DESCRIPTIONS_NOVA =              "Nova ultimates from Templar class",
-    TGU_DESCRIPTIONS_TPHEAL =            "Heal ultimates from Templar class",
-    TGU_DESCRIPTIONS_STAND =             "Standard ultimates from Dragonknight class",
-    TGU_DESCRIPTIONS_LEAP =              "Leap ultimates from Dragonknight class",
-    TGU_DESCRIPTIONS_MAGMA =             "Magma ultimates from Dragonknight class",
-    TGU_DESCRIPTIONS_STROKE =            "Death Stroke ultimates from Nightblade class",
-    TGU_DESCRIPTIONS_VEIL =              "Veil of Blades ultimates from Nightblade class",
-    TGU_DESCRIPTIONS_NBSOUL =            "Soul ultimates from Nightblade class",
-    TGU_DESCRIPTIONS_FREEZE =            "Storm ultimates from Warden class",
-    TGU_DESCRIPTIONS_WDHEAL =            "Heal ultimates from Warden class",
-    TGU_DESCRIPTIONS_ICE =               "Ice ultimates from Destruction Staff weapon",
-    TGU_DESCRIPTIONS_FIRE =              "Fire ultimates from Destruction Staff weapon",
-    TGU_DESCRIPTIONS_LIGHT =             "Lightning ultimates from Destruction Staff weapon",
-    TGU_DESCRIPTIONS_STHEAL =            "Heal ultimates from Healing Staff weapon",
-    TGU_DESCRIPTIONS_BERSERK =           "Berserker ultimates from Twohand weapon",
-    TGU_DESCRIPTIONS_SHIELD =            "Shield ultimates from One hand and Shield weapon",
-    TGU_DESCRIPTIONS_DUAL =              "Dual wield ultimates from Dual Wield weapons",
-    TGU_DESCRIPTIONS_BOW =               "Bow ultimates from Bow weapon",
-    TGU_DESCRIPTIONS_SOUL =              "Soul ultimates from Soul Magic skill line",
-    TGU_DESCRIPTIONS_WERE =              "Werewolf ultimates from Werewolf skill line",
-    TGU_DESCRIPTIONS_VAMP =              "Vamp ultimates from Vampire skill line",
-    TGU_DESCRIPTIONS_METEOR =            "Meteor ultimates from Mages guild",
-    TGU_DESCRIPTIONS_DAWN =              "Dawnbreaker ultimates from Fighters guild",
-    TGU_DESCRIPTIONS_BARRIER =           "Barrier ultimates from Support alliance skill line",
-    TGU_DESCRIPTIONS_HORN =              "Horn ultimates from Assoult alliance skill line"
-}
-
-for stringId, stringValue in pairs(strings) do
-	ZO_CreateStringId(stringId, stringValue)
-	SafeAddVersion(stringId, 1)
+-- Taos Group Ultimate english localization file
+
+-- Options
+local strings = {
+    TGU_OPTIONS_HEADER =                 "Options",
+    TGU_OPTIONS_DRAG_LABEL =             "Drag elements",
+    TGU_OPTIONS_DRAG_TOOLTIP =           "If activated, you can drag all elements.",
+    TGU_OPTIONS_ONLY_AVA_LABEL =         "Show only in AvA",
+    TGU_OPTIONS_ONLY_AVA_TOOLTIP =       "If activated, all elements will only be visible in Cyrodiil (AvA).",
+    TGU_OPTIONS_USE_SORTING_LABEL =      "Sort lists by ultimate progress",
+    TGU_OPTIONS_USE_SORTING_TOOLTIP =    "If activated, all lists will be sorted by ultimate progress (Maximum on top).",
+    TGU_OPTIONS_STYLE_LABEL =            "Choose style",
+    TGU_OPTIONS_STYLE_TOOLTIP =          "Choose your style. Simple-List, Swimlane-List or Compact Swimlane-List",
+    TGU_OPTIONS_STYLE_SIMPLE =           "Simple-List",
+    TGU_OPTIONS_STYLE_SWIM =             "Swimlane-List",
+    TGU_OPTIONS_STYLE_SHORT_SWIM =       "Compact Swimlane-List",
+    TGU_DESCRIPTIONS_NEGATE =            "Negate ultimates from Sorcerer class",
+    TGU_DESCRIPTIONS_ATRO =              "Atronach ultimates from Sorcerer class",
+    TGU_DESCRIPTIONS_OVER =              "Overload ultimates from Sorcerer class",
+    TGU_DESCRIPTIONS_SWEEP =             "Sweep ultimates from Templar class",
+    TGU_DESCRIPTIONS_NOVA =              "Nova ultimates from Templar class",
+    TGU_DESCRIPTIONS_TPHEAL =            "Heal ultimates from Templar class",
+    TGU_DESCRIPTIONS_STAND =             "Standard ultimates from Dragonknight class",
+    TGU_DESCRIPTIONS_LEAP =              "Leap ultimates from Dragonknight class",
+    TGU_DESCRIPTIONS_MAGMA =             "Magma ultimates from Dragonknight class",
+    TGU_DESCRIPTIONS_STROKE =            "Death Stroke ultimates from Nightblade class",
+    TGU_DESCRIPTIONS_VEIL =              "Veil of Blades ultimates from Nightblade class",
+    TGU_DESCRIPTIONS_NBSOUL =            "Soul ultimates from Nightblade class",
+    TGU_DESCRIPTIONS_FREEZE =            "Storm ultimates from Warden class",
+    TGU_DESCRIPTIONS_WDHEAL =            "Heal ultimates from Warden class",
+    TGU_DESCRIPTIONS_ICE =               "Ice ultimates from Destruction Staff weapon",
+    TGU_DESCRIPTIONS_FIRE =              "Fire ultimates from Destruction Staff weapon",
+    TGU_DESCRIPTIONS_LIGHT =             "Lightning ultimates from Destruction Staff weapon",
+    TGU_DESCRIPTIONS_STHEAL =            "Heal ultimates from Healing Staff weapon",
+    TGU_DESCRIPTIONS_BERSERK =           "Berserker ultimates from Twohand weapon",
+    TGU_DESCRIPTIONS_SHIELD =            "Shield ultimates from One hand and Shield weapon",
+    TGU_DESCRIPTIONS_DUAL =              "Dual wield ultimates from Dual Wield weapons",
+    TGU_DESCRIPTIONS_BOW =               "Bow ultimates from Bow weapon",
+    TGU_DESCRIPTIONS_SOUL =              "Soul ultimates from Soul Magic skill line",
+    TGU_DESCRIPTIONS_WERE =              "Werewolf ultimates from Werewolf skill line",
+    TGU_DESCRIPTIONS_VAMP =              "Vamp ultimates from Vampire skill line",
+    TGU_DESCRIPTIONS_METEOR =            "Meteor ultimates from Mages guild",
+    TGU_DESCRIPTIONS_DAWN =              "Dawnbreaker ultimates from Fighters guild",
+    TGU_DESCRIPTIONS_BARRIER =           "Barrier ultimates from Support alliance skill line",
+    TGU_DESCRIPTIONS_HORN =              "Horn ultimates from Assoult alliance skill line"
+}
+
+for stringId, stringValue in pairs(strings) do
+	ZO_CreateStringId(stringId, stringValue)
+	SafeAddVersion(stringId, 1)
 end
\ No newline at end of file
diff --git a/TaosGroupTools/i18n/fr.lua b/TaosGroupTools/i18n/fr.lua
index 46eab84..f6f9002 100644
--- a/TaosGroupTools/i18n/fr.lua
+++ b/TaosGroupTools/i18n/fr.lua
@@ -1,51 +1,51 @@
--- Taos Group ultimate french localization file
-
--- Options
-local strings = {
-    TGU_OPTIONS_HEADER =                 "Options",
-    TGU_OPTIONS_DRAG_LABEL =             "Drag elements",
-    TGU_OPTIONS_DRAG_TOOLTIP =           "If activated, you can drag all elements.",
-    TGU_OPTIONS_ONLY_AVA_LABEL =         "Show only in AvA",
-    TGU_OPTIONS_ONLY_AVA_TOOLTIP =       "If activated, all elements will only be visible in Cyrodiil (AvA).",
-    TGU_OPTIONS_USE_SORTING_LABEL =      "Sort lists by ultimate progress",
-    TGU_OPTIONS_USE_SORTING_TOOLTIP =    "If activated, all lists will be sorted by ultimate progress (Maximum on top).",
-    TGU_OPTIONS_STYLE_LABEL =            "Choose style",
-    TGU_OPTIONS_STYLE_TOOLTIP =          "Choose your style. Simple-List, Swimlane-List or Compact Swimlane-List",
-    TGU_OPTIONS_STYLE_SIMPLE =           "Simple-List",
-    TGU_OPTIONS_STYLE_SWIM =             "Swimlane-List",
-    TGU_OPTIONS_STYLE_SHORT_SWIM =       "Compact Swimlane-List",
-    TGU_DESCRIPTIONS_NEGATE =            "Negate ultimates from Sorcerer class",
-    TGU_DESCRIPTIONS_ATRO =              "Atronach ultimates from Sorcerer class",
-    TGU_DESCRIPTIONS_OVER =              "Overload ultimates from Sorcerer class",
-    TGU_DESCRIPTIONS_SWEEP =             "Sweep ultimates from Templar class",
-    TGU_DESCRIPTIONS_NOVA =              "Nova ultimates from Templar class",
-    TGU_DESCRIPTIONS_TPHEAL =            "Heal ultimates from Templar class",
-    TGU_DESCRIPTIONS_STAND =             "Standard ultimates from Dragonknight class",
-    TGU_DESCRIPTIONS_LEAP =              "Leap ultimates from Dragonknight class",
-    TGU_DESCRIPTIONS_MAGMA =             "Magma ultimates from Dragonknight class",
-    TGU_DESCRIPTIONS_STROKE =            "Death Stroke ultimates from Nightblade class",
-    TGU_DESCRIPTIONS_VEIL =              "Veil of Blades ultimates from Nightblade class",
-    TGU_DESCRIPTIONS_NBSOUL =            "Soul ultimates from Nightblade class",
-    TGU_DESCRIPTIONS_FREEZE =            "Storm ultimates from Warden class",
-    TGU_DESCRIPTIONS_WDHEAL =            "Heal ultimates from Warden class",
-    TGU_DESCRIPTIONS_ICE =               "Ice ultimates from Destruction Staff weapon",
-    TGU_DESCRIPTIONS_FIRE =              "Fire ultimates from Destruction Staff weapon",
-    TGU_DESCRIPTIONS_LIGHT =             "Lightning ultimates from Destruction Staff weapon",
-    TGU_DESCRIPTIONS_STHEAL =            "Heal ultimates from Healing Staff weapon",
-    TGU_DESCRIPTIONS_BERSERK =           "2H ultimates from 2H line",
-    TGU_DESCRIPTIONS_SHIELD =            "Shield ultimates from shield line",
-    TGU_DESCRIPTIONS_DUAL =              "Dual wield ultimates from dual wield line",
-    TGU_DESCRIPTIONS_BOW =               "Bow ultimates from bow line",
-    TGU_DESCRIPTIONS_SOUL =              "Soul magic ultimates from soul line",
-    TGU_DESCRIPTIONS_WERE =              "Werewolf ultimates from werewolf line",
-    TGU_DESCRIPTIONS_VAMP =              "Vamp ultimates from vamp line",
-    TGU_DESCRIPTIONS_METEOR =            "Meteor ultimates from Mages guild",
-    TGU_DESCRIPTIONS_DAWN =              "Dawnbreaker ultimates from Fighters guild",
-    TGU_DESCRIPTIONS_BARRIER =           "Barrier ultimates from Support alliance skill line",
-    TGU_DESCRIPTIONS_HORN =              "Horn ultimates from Assoult alliance skill line"
-}
-
-for stringId, stringValue in pairs(strings) do
-	ZO_CreateStringId(stringId, stringValue)
-	SafeAddVersion(stringId, 1)
+-- Taos Group ultimate french localization file
+
+-- Options
+local strings = {
+    TGU_OPTIONS_HEADER =                 "Options",
+    TGU_OPTIONS_DRAG_LABEL =             "Drag elements",
+    TGU_OPTIONS_DRAG_TOOLTIP =           "If activated, you can drag all elements.",
+    TGU_OPTIONS_ONLY_AVA_LABEL =         "Show only in AvA",
+    TGU_OPTIONS_ONLY_AVA_TOOLTIP =       "If activated, all elements will only be visible in Cyrodiil (AvA).",
+    TGU_OPTIONS_USE_SORTING_LABEL =      "Sort lists by ultimate progress",
+    TGU_OPTIONS_USE_SORTING_TOOLTIP =    "If activated, all lists will be sorted by ultimate progress (Maximum on top).",
+    TGU_OPTIONS_STYLE_LABEL =            "Choose style",
+    TGU_OPTIONS_STYLE_TOOLTIP =          "Choose your style. Simple-List, Swimlane-List or Compact Swimlane-List",
+    TGU_OPTIONS_STYLE_SIMPLE =           "Simple-List",
+    TGU_OPTIONS_STYLE_SWIM =             "Swimlane-List",
+    TGU_OPTIONS_STYLE_SHORT_SWIM =       "Compact Swimlane-List",
+    TGU_DESCRIPTIONS_NEGATE =            "Negate ultimates from Sorcerer class",
+    TGU_DESCRIPTIONS_ATRO =              "Atronach ultimates from Sorcerer class",
+    TGU_DESCRIPTIONS_OVER =              "Overload ultimates from Sorcerer class",
+    TGU_DESCRIPTIONS_SWEEP =             "Sweep ultimates from Templar class",
+    TGU_DESCRIPTIONS_NOVA =              "Nova ultimates from Templar class",
+    TGU_DESCRIPTIONS_TPHEAL =            "Heal ultimates from Templar class",
+    TGU_DESCRIPTIONS_STAND =             "Standard ultimates from Dragonknight class",
+    TGU_DESCRIPTIONS_LEAP =              "Leap ultimates from Dragonknight class",
+    TGU_DESCRIPTIONS_MAGMA =             "Magma ultimates from Dragonknight class",
+    TGU_DESCRIPTIONS_STROKE =            "Death Stroke ultimates from Nightblade class",
+    TGU_DESCRIPTIONS_VEIL =              "Veil of Blades ultimates from Nightblade class",
+    TGU_DESCRIPTIONS_NBSOUL =            "Soul ultimates from Nightblade class",
+    TGU_DESCRIPTIONS_FREEZE =            "Storm ultimates from Warden class",
+    TGU_DESCRIPTIONS_WDHEAL =            "Heal ultimates from Warden class",
+    TGU_DESCRIPTIONS_ICE =               "Ice ultimates from Destruction Staff weapon",
+    TGU_DESCRIPTIONS_FIRE =              "Fire ultimates from Destruction Staff weapon",
+    TGU_DESCRIPTIONS_LIGHT =             "Lightning ultimates from Destruction Staff weapon",
+    TGU_DESCRIPTIONS_STHEAL =            "Heal ultimates from Healing Staff weapon",
+    TGU_DESCRIPTIONS_BERSERK =           "2H ultimates from 2H line",
+    TGU_DESCRIPTIONS_SHIELD =            "Shield ultimates from shield line",
+    TGU_DESCRIPTIONS_DUAL =              "Dual wield ultimates from dual wield line",
+    TGU_DESCRIPTIONS_BOW =               "Bow ultimates from bow line",
+    TGU_DESCRIPTIONS_SOUL =              "Soul magic ultimates from soul line",
+    TGU_DESCRIPTIONS_WERE =              "Werewolf ultimates from werewolf line",
+    TGU_DESCRIPTIONS_VAMP =              "Vamp ultimates from vamp line",
+    TGU_DESCRIPTIONS_METEOR =            "Meteor ultimates from Mages guild",
+    TGU_DESCRIPTIONS_DAWN =              "Dawnbreaker ultimates from Fighters guild",
+    TGU_DESCRIPTIONS_BARRIER =           "Barrier ultimates from Support alliance skill line",
+    TGU_DESCRIPTIONS_HORN =              "Horn ultimates from Assoult alliance skill line"
+}
+
+for stringId, stringValue in pairs(strings) do
+	ZO_CreateStringId(stringId, stringValue)
+	SafeAddVersion(stringId, 1)
 end
\ No newline at end of file
diff --git a/TaosGroupTools/libs/LibAddonMenu-2.0/LICENSE b/TaosGroupTools/libs/LibAddonMenu-2.0/LICENSE
index f69cbd4..755f075 100644
--- a/TaosGroupTools/libs/LibAddonMenu-2.0/LICENSE
+++ b/TaosGroupTools/libs/LibAddonMenu-2.0/LICENSE
@@ -1,201 +1,201 @@
-               The Artistic License 2.0
-
-           Copyright (c) 2016 Ryan Lakanen (Seerah)
-
-     Everyone is permitted to copy and distribute verbatim copies
-      of this license document, but changing it is not allowed.
-
-Preamble
-
-This license establishes the terms under which a given free software
-Package may be copied, modified, distributed, and/or redistributed.
-The intent is that the Copyright Holder maintains some artistic
-control over the development of that Package while still keeping the
-Package available as open source and free software.
-
-You are always permitted to make arrangements wholly outside of this
-license directly with the Copyright Holder of a given Package.  If the
-terms of this license do not permit the full use that you propose to
-make of the Package, you should contact the Copyright Holder and seek
-a different licensing arrangement.
-
-Definitions
-
-    "Copyright Holder" means the individual(s) or organization(s)
-    named in the copyright notice for the entire Package.
-
-    "Contributor" means any party that has contributed code or other
-    material to the Package, in accordance with the Copyright Holder's
-    procedures.
-
-    "You" and "your" means any person who would like to copy,
-    distribute, or modify the Package.
-
-    "Package" means the collection of files distributed by the
-    Copyright Holder, and derivatives of that collection and/or of
-    those files. A given Package may consist of either the Standard
-    Version, or a Modified Version.
-
-    "Distribute" means providing a copy of the Package or making it
-    accessible to anyone else, or in the case of a company or
-    organization, to others outside of your company or organization.
-
-    "Distributor Fee" means any fee that you charge for Distributing
-    this Package or providing support for this Package to another
-    party.  It does not mean licensing fees.
-
-    "Standard Version" refers to the Package if it has not been
-    modified, or has been modified only in ways explicitly requested
-    by the Copyright Holder.
-
-    "Modified Version" means the Package, if it has been changed, and
-    such changes were not explicitly requested by the Copyright
-    Holder.
-
-    "Original License" means this Artistic License as Distributed with
-    the Standard Version of the Package, in its current version or as
-    it may be modified by The Perl Foundation in the future.
-
-    "Source" form means the source code, documentation source, and
-    configuration files for the Package.
-
-    "Compiled" form means the compiled bytecode, object code, binary,
-    or any other form resulting from mechanical transformation or
-    translation of the Source form.
-
-
-Permission for Use and Modification Without Distribution
-
-(1)  You are permitted to use the Standard Version and create and use
-Modified Versions for any purpose without restriction, provided that
-you do not Distribute the Modified Version.
-
-
-Permissions for Redistribution of the Standard Version
-
-(2)  You may Distribute verbatim copies of the Source form of the
-Standard Version of this Package in any medium without restriction,
-either gratis or for a Distributor Fee, provided that you duplicate
-all of the original copyright notices and associated disclaimers.  At
-your discretion, such verbatim copies may or may not include a
-Compiled form of the Package.
-
-(3)  You may apply any bug fixes, portability changes, and other
-modifications made available from the Copyright Holder.  The resulting
-Package will still be considered the Standard Version, and as such
-will be subject to the Original License.
-
-
-Distribution of Modified Versions of the Package as Source
-
-(4)  You may Distribute your Modified Version as Source (either gratis
-or for a Distributor Fee, and with or without a Compiled form of the
-Modified Version) provided that you clearly document how it differs
-from the Standard Version, including, but not limited to, documenting
-any non-standard features, executables, or modules, and provided that
-you do at least ONE of the following:
-
-    (a)  make the Modified Version available to the Copyright Holder
-    of the Standard Version, under the Original License, so that the
-    Copyright Holder may include your modifications in the Standard
-    Version.
-
-    (b)  ensure that installation of your Modified Version does not
-    prevent the user installing or running the Standard Version. In
-    addition, the Modified Version must bear a name that is different
-    from the name of the Standard Version.
-
-    (c)  allow anyone who receives a copy of the Modified Version to
-    make the Source form of the Modified Version available to others
-    under
-
-    (i)  the Original License or
-
-    (ii)  a license that permits the licensee to freely copy,
-    modify and redistribute the Modified Version using the same
-    licensing terms that apply to the copy that the licensee
-    received, and requires that the Source form of the Modified
-    Version, and of any works derived from it, be made freely
-    available in that license fees are prohibited but Distributor
-    Fees are allowed.
-
-
-Distribution of Compiled Forms of the Standard Version
-or Modified Versions without the Source
-
-(5)  You may Distribute Compiled forms of the Standard Version without
-the Source, provided that you include complete instructions on how to
-get the Source of the Standard Version.  Such instructions must be
-valid at the time of your distribution.  If these instructions, at any
-time while you are carrying out such distribution, become invalid, you
-must provide new instructions on demand or cease further distribution.
-If you provide valid instructions or cease distribution within thirty
-days after you become aware that the instructions are invalid, then
-you do not forfeit any of your rights under this license.
-
-(6)  You may Distribute a Modified Version in Compiled form without
-the Source, provided that you comply with Section 4 with respect to
-the Source of the Modified Version.
-
-
-Aggregating or Linking the Package
-
-(7)  You may aggregate the Package (either the Standard Version or
-Modified Version) with other packages and Distribute the resulting
-aggregation provided that you do not charge a licensing fee for the
-Package.  Distributor Fees are permitted, and licensing fees for other
-components in the aggregation are permitted. The terms of this license
-apply to the use and Distribution of the Standard or Modified Versions
-as included in the aggregation.
-
-(8) You are permitted to link Modified and Standard Versions with
-other works, to embed the Package in a larger work of your own, or to
-build stand-alone binary or bytecode versions of applications that
-include the Package, and Distribute the result without restriction,
-provided the result does not expose a direct interface to the Package.
-
-
-Items That are Not Considered Part of a Modified Version
-
-(9) Works (including, but not limited to, modules and scripts) that
-merely extend or make use of the Package, do not, by themselves, cause
-the Package to be a Modified Version.  In addition, such works are not
-considered parts of the Package itself, and are not subject to the
-terms of this license.
-
-
-General Provisions
-
-(10)  Any use, modification, and distribution of the Standard or
-Modified Versions is governed by this Artistic License. By using,
-modifying or distributing the Package, you accept this license. Do not
-use, modify, or distribute the Package, if you do not accept this
-license.
-
-(11)  If your Modified Version has been derived from a Modified
-Version made by someone other than you, you are nevertheless required
-to ensure that your Modified Version complies with the requirements of
-this license.
-
-(12)  This license does not grant you the right to use any trademark,
-service mark, tradename, or logo of the Copyright Holder.
-
-(13)  This license includes the non-exclusive, worldwide,
-free-of-charge patent license to make, have made, use, offer to sell,
-sell, import and otherwise transfer the Package with respect to any
-patent claims licensable by the Copyright Holder that are necessarily
-infringed by the Package. If you institute patent litigation
-(including a cross-claim or counterclaim) against any party alleging
-that the Package constitutes direct or contributory patent
-infringement, then this Artistic License to you shall terminate on the
-date that such litigation is filed.
-
-(14)  Disclaimer of Warranty:
-THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS
-IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. THE IMPLIED
-WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
-NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY YOUR LOCAL
-LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR CONTRIBUTOR WILL
-BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
-DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE, EVEN IF
-ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+               The Artistic License 2.0
+
+           Copyright (c) 2016 Ryan Lakanen (Seerah)
+
+     Everyone is permitted to copy and distribute verbatim copies
+      of this license document, but changing it is not allowed.
+
+Preamble
+
+This license establishes the terms under which a given free software
+Package may be copied, modified, distributed, and/or redistributed.
+The intent is that the Copyright Holder maintains some artistic
+control over the development of that Package while still keeping the
+Package available as open source and free software.
+
+You are always permitted to make arrangements wholly outside of this
+license directly with the Copyright Holder of a given Package.  If the
+terms of this license do not permit the full use that you propose to
+make of the Package, you should contact the Copyright Holder and seek
+a different licensing arrangement.
+
+Definitions
+
+    "Copyright Holder" means the individual(s) or organization(s)
+    named in the copyright notice for the entire Package.
+
+    "Contributor" means any party that has contributed code or other
+    material to the Package, in accordance with the Copyright Holder's
+    procedures.
+
+    "You" and "your" means any person who would like to copy,
+    distribute, or modify the Package.
+
+    "Package" means the collection of files distributed by the
+    Copyright Holder, and derivatives of that collection and/or of
+    those files. A given Package may consist of either the Standard
+    Version, or a Modified Version.
+
+    "Distribute" means providing a copy of the Package or making it
+    accessible to anyone else, or in the case of a company or
+    organization, to others outside of your company or organization.
+
+    "Distributor Fee" means any fee that you charge for Distributing
+    this Package or providing support for this Package to another
+    party.  It does not mean licensing fees.
+
+    "Standard Version" refers to the Package if it has not been
+    modified, or has been modified only in ways explicitly requested
+    by the Copyright Holder.
+
+    "Modified Version" means the Package, if it has been changed, and
+    such changes were not explicitly requested by the Copyright
+    Holder.
+
+    "Original License" means this Artistic License as Distributed with
+    the Standard Version of the Package, in its current version or as
+    it may be modified by The Perl Foundation in the future.
+
+    "Source" form means the source code, documentation source, and
+    configuration files for the Package.
+
+    "Compiled" form means the compiled bytecode, object code, binary,
+    or any other form resulting from mechanical transformation or
+    translation of the Source form.
+
+
+Permission for Use and Modification Without Distribution
+
+(1)  You are permitted to use the Standard Version and create and use
+Modified Versions for any purpose without restriction, provided that
+you do not Distribute the Modified Version.
+
+
+Permissions for Redistribution of the Standard Version
+
+(2)  You may Distribute verbatim copies of the Source form of the
+Standard Version of this Package in any medium without restriction,
+either gratis or for a Distributor Fee, provided that you duplicate
+all of the original copyright notices and associated disclaimers.  At
+your discretion, such verbatim copies may or may not include a
+Compiled form of the Package.
+
+(3)  You may apply any bug fixes, portability changes, and other
+modifications made available from the Copyright Holder.  The resulting
+Package will still be considered the Standard Version, and as such
+will be subject to the Original License.
+
+
+Distribution of Modified Versions of the Package as Source
+
+(4)  You may Distribute your Modified Version as Source (either gratis
+or for a Distributor Fee, and with or without a Compiled form of the
+Modified Version) provided that you clearly document how it differs
+from the Standard Version, including, but not limited to, documenting
+any non-standard features, executables, or modules, and provided that
+you do at least ONE of the following:
+
+    (a)  make the Modified Version available to the Copyright Holder
+    of the Standard Version, under the Original License, so that the
+    Copyright Holder may include your modifications in the Standard
+    Version.
+
+    (b)  ensure that installation of your Modified Version does not
+    prevent the user installing or running the Standard Version. In
+    addition, the Modified Version must bear a name that is different
+    from the name of the Standard Version.
+
+    (c)  allow anyone who receives a copy of the Modified Version to
+    make the Source form of the Modified Version available to others
+    under
+
+    (i)  the Original License or
+
+    (ii)  a license that permits the licensee to freely copy,
+    modify and redistribute the Modified Version using the same
+    licensing terms that apply to the copy that the licensee
+    received, and requires that the Source form of the Modified
+    Version, and of any works derived from it, be made freely
+    available in that license fees are prohibited but Distributor
+    Fees are allowed.
+
+
+Distribution of Compiled Forms of the Standard Version
+or Modified Versions without the Source
+
+(5)  You may Distribute Compiled forms of the Standard Version without
+the Source, provided that you include complete instructions on how to
+get the Source of the Standard Version.  Such instructions must be
+valid at the time of your distribution.  If these instructions, at any
+time while you are carrying out such distribution, become invalid, you
+must provide new instructions on demand or cease further distribution.
+If you provide valid instructions or cease distribution within thirty
+days after you become aware that the instructions are invalid, then
+you do not forfeit any of your rights under this license.
+
+(6)  You may Distribute a Modified Version in Compiled form without
+the Source, provided that you comply with Section 4 with respect to
+the Source of the Modified Version.
+
+
+Aggregating or Linking the Package
+
+(7)  You may aggregate the Package (either the Standard Version or
+Modified Version) with other packages and Distribute the resulting
+aggregation provided that you do not charge a licensing fee for the
+Package.  Distributor Fees are permitted, and licensing fees for other
+components in the aggregation are permitted. The terms of this license
+apply to the use and Distribution of the Standard or Modified Versions
+as included in the aggregation.
+
+(8) You are permitted to link Modified and Standard Versions with
+other works, to embed the Package in a larger work of your own, or to
+build stand-alone binary or bytecode versions of applications that
+include the Package, and Distribute the result without restriction,
+provided the result does not expose a direct interface to the Package.
+
+
+Items That are Not Considered Part of a Modified Version
+
+(9) Works (including, but not limited to, modules and scripts) that
+merely extend or make use of the Package, do not, by themselves, cause
+the Package to be a Modified Version.  In addition, such works are not
+considered parts of the Package itself, and are not subject to the
+terms of this license.
+
+
+General Provisions
+
+(10)  Any use, modification, and distribution of the Standard or
+Modified Versions is governed by this Artistic License. By using,
+modifying or distributing the Package, you accept this license. Do not
+use, modify, or distribute the Package, if you do not accept this
+license.
+
+(11)  If your Modified Version has been derived from a Modified
+Version made by someone other than you, you are nevertheless required
+to ensure that your Modified Version complies with the requirements of
+this license.
+
+(12)  This license does not grant you the right to use any trademark,
+service mark, tradename, or logo of the Copyright Holder.
+
+(13)  This license includes the non-exclusive, worldwide,
+free-of-charge patent license to make, have made, use, offer to sell,
+sell, import and otherwise transfer the Package with respect to any
+patent claims licensable by the Copyright Holder that are necessarily
+infringed by the Package. If you institute patent litigation
+(including a cross-claim or counterclaim) against any party alleging
+that the Package constitutes direct or contributory patent
+infringement, then this Artistic License to you shall terminate on the
+date that such litigation is filed.
+
+(14)  Disclaimer of Warranty:
+THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS
+IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. THE IMPLIED
+WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
+NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY YOUR LOCAL
+LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR CONTRIBUTOR WILL
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/TaosGroupTools/libs/LibAddonMenu-2.0/LibAddonMenu-2.0.lua b/TaosGroupTools/libs/LibAddonMenu-2.0/LibAddonMenu-2.0.lua
index 3c4ab31..f661ced 100644
--- a/TaosGroupTools/libs/LibAddonMenu-2.0/LibAddonMenu-2.0.lua
+++ b/TaosGroupTools/libs/LibAddonMenu-2.0/LibAddonMenu-2.0.lua
@@ -1,1226 +1,1226 @@
--- LibAddonMenu-2.0 & its files © Ryan Lakanen (Seerah)         --
--- Distributed under The Artistic License 2.0 (see LICENSE)     --
-------------------------------------------------------------------
-
-
---Register LAM with LibStub
-local MAJOR, MINOR = "LibAddonMenu-2.0", 25
-local lam, oldminor = LibStub:NewLibrary(MAJOR, MINOR)
-if not lam then return end --the same or newer version of this lib is already loaded into memory
-
-local messages = {}
-local MESSAGE_PREFIX = "[LAM2] "
-local function PrintLater(msg)
-    if CHAT_SYSTEM.primaryContainer then
-        d(MESSAGE_PREFIX .. msg)
-    else
-        messages[#messages + 1] = msg
-    end
-end
-
-local function FlushMessages()
-    for i = 1, #messages do
-        d(MESSAGE_PREFIX .. messages[i])
-    end
-    messages = {}
-end
-
-if LAMSettingsPanelCreated and not LAMCompatibilityWarning then
-    PrintLater("An old version of LibAddonMenu with compatibility issues was detected. For more information on how to proceed search for LibAddonMenu on esoui.com")
-    LAMCompatibilityWarning = true
-end
-
---UPVALUES--
-local wm = WINDOW_MANAGER
-local em = EVENT_MANAGER
-local sm = SCENE_MANAGER
-local cm = CALLBACK_MANAGER
-local tconcat = table.concat
-local tinsert = table.insert
-
-local MIN_HEIGHT = 26
-local HALF_WIDTH_LINE_SPACING = 2
-local OPTIONS_CREATION_RUNNING = 1
-local OPTIONS_CREATED = 2
-local LAM_CONFIRM_DIALOG = "LAM_CONFIRM_DIALOG"
-local LAM_DEFAULTS_DIALOG = "LAM_DEFAULTS"
-local LAM_RELOAD_DIALOG = "LAM_RELOAD_DIALOG"
-
-local addonsForList = {}
-local addonToOptionsMap = {}
-local optionsState = {}
-lam.widgets = lam.widgets or {}
-local widgets = lam.widgets
-lam.util = lam.util or {}
-local util = lam.util
-lam.controlsForReload = lam.controlsForReload or {}
-local controlsForReload = lam.controlsForReload
-
-local function GetDefaultValue(default)
-    if type(default) == "function" then
-        return default()
-    end
-    return default
-end
-
-local function GetStringFromValue(value)
-    if type(value) == "function" then
-        return value()
-    elseif type(value) == "number" then
-        return GetString(value)
-    end
-    return value
-end
-
-local function CreateBaseControl(parent, controlData, controlName)
-    local control = wm:CreateControl(controlName or controlData.reference, parent.scroll or parent, CT_CONTROL)
-    control.panel = parent.panel or parent -- if this is in a submenu, panel is the submenu's parent
-    control.data = controlData
-
-    control.isHalfWidth = controlData.width == "half"
-    local width = 510 -- set default width in case a custom parent object is passed
-    if control.panel.GetWidth ~= nil then width = control.panel:GetWidth() - 60 end
-    control:SetWidth(width)
-    return control
-end
-
-local function CreateLabelAndContainerControl(parent, controlData, controlName)
-    local control = CreateBaseControl(parent, controlData, controlName)
-    local width = control:GetWidth()
-
-    local container = wm:CreateControl(nil, control, CT_CONTROL)
-    container:SetDimensions(width / 3, MIN_HEIGHT)
-    control.container = container
-
-    local label = wm:CreateControl(nil, control, CT_LABEL)
-    label:SetFont("ZoFontWinH4")
-    label:SetHeight(MIN_HEIGHT)
-    label:SetWrapMode(TEXT_WRAP_MODE_ELLIPSIS)
-    label:SetText(GetStringFromValue(controlData.name))
-    control.label = label
-
-    if control.isHalfWidth then
-        control:SetDimensions(width / 2, MIN_HEIGHT * 2 + HALF_WIDTH_LINE_SPACING)
-        label:SetAnchor(TOPLEFT, control, TOPLEFT, 0, 0)
-        label:SetAnchor(TOPRIGHT, control, TOPRIGHT, 0, 0)
-        container:SetAnchor(TOPRIGHT, control.label, BOTTOMRIGHT, 0, HALF_WIDTH_LINE_SPACING)
-    else
-        control:SetDimensions(width, MIN_HEIGHT)
-        container:SetAnchor(TOPRIGHT, control, TOPRIGHT, 0, 0)
-        label:SetAnchor(TOPLEFT, control, TOPLEFT, 0, 0)
-        label:SetAnchor(TOPRIGHT, container, TOPLEFT, 5, 0)
-    end
-
-    control.data.tooltipText = GetStringFromValue(control.data.tooltip)
-    control:SetMouseEnabled(true)
-    control:SetHandler("OnMouseEnter", ZO_Options_OnMouseEnter)
-    control:SetHandler("OnMouseExit", ZO_Options_OnMouseExit)
-    return control
-end
-
-local function GetTopPanel(panel)
-    while panel.panel and panel.panel ~= panel do
-        panel = panel.panel
-    end
-    return panel
-end
-
-local function IsSame(objA, objB)
-    if #objA ~= #objB then return false end
-    for i = 1, #objA do
-        if objA[i] ~= objB[i] then return false end
-    end
-    return true
-end
-
-local function RefreshReloadUIButton()
-    lam.requiresReload = false
-
-    for i = 1, #controlsForReload do
-        local reloadControl = controlsForReload[i]
-        if not IsSame(reloadControl.startValue, {reloadControl.data.getFunc()}) then
-            lam.requiresReload = true
-            break
-        end
-    end
-
-    lam.applyButton:SetHidden(not lam.requiresReload)
-end
-
-local function RequestRefreshIfNeeded(control)
-    -- if our parent window wants to refresh controls, then fire the callback
-    local panel = GetTopPanel(control.panel)
-    local panelData = panel.data
-    if panelData.registerForRefresh then
-        cm:FireCallbacks("LAM-RefreshPanel", control)
-    end
-    RefreshReloadUIButton()
-end
-
-local function RegisterForRefreshIfNeeded(control)
-    -- if our parent window wants to refresh controls, then add this to the list
-    local panel = GetTopPanel(control.panel)
-    local panelData = panel.data
-    if panelData.registerForRefresh or panelData.registerForDefaults then
-        tinsert(panel.controlsToRefresh or {}, control) -- prevent errors on custom panels
-    end
-end
-
-local function RegisterForReloadIfNeeded(control)
-    if control.data.requiresReload then
-        tinsert(controlsForReload, control)
-        control.startValue = {control.data.getFunc()}
-    end
-end
-
-local function GetConfirmDialog()
-    if(not ESO_Dialogs[LAM_CONFIRM_DIALOG]) then
-        ESO_Dialogs[LAM_CONFIRM_DIALOG] = {
-            canQueue = true,
-            title = {
-                text = "",
-            },
-            mainText = {
-                text = "",
-            },
-            buttons = {
-                [1] = {
-                    text = SI_DIALOG_CONFIRM,
-                    callback = function(dialog) end,
-                },
-                [2] = {
-                    text = SI_DIALOG_CANCEL,
-                }
-            }
-        }
-    end
-    return ESO_Dialogs[LAM_CONFIRM_DIALOG]
-end
-
-local function ShowConfirmationDialog(title, body, callback)
-    local dialog = GetConfirmDialog()
-    dialog.title.text = title
-    dialog.mainText.text = body
-    dialog.buttons[1].callback = callback
-    ZO_Dialogs_ShowDialog(LAM_CONFIRM_DIALOG)
-end
-
-local function GetDefaultsDialog()
-    if(not ESO_Dialogs[LAM_DEFAULTS_DIALOG]) then
-        ESO_Dialogs[LAM_DEFAULTS_DIALOG] = {
-            canQueue = true,
-            title = {
-                text = SI_INTERFACE_OPTIONS_RESET_TO_DEFAULT_TOOLTIP,
-            },
-            mainText = {
-                text = SI_OPTIONS_RESET_PROMPT,
-            },
-            buttons = {
-                [1] = {
-                    text = SI_OPTIONS_RESET,
-                    callback = function(dialog) end,
-                },
-                [2] = {
-                    text = SI_DIALOG_CANCEL,
-                }
-            }
-        }
-    end
-    return ESO_Dialogs[LAM_DEFAULTS_DIALOG]
-end
-
-local function ShowDefaultsDialog(panel)
-    local dialog = GetDefaultsDialog()
-    dialog.buttons[1].callback = function()
-        panel:ForceDefaults()
-        RefreshReloadUIButton()
-    end
-    ZO_Dialogs_ShowDialog(LAM_DEFAULTS_DIALOG)
-end
-
-local function DiscardChangesOnReloadControls()
-    for i = 1, #controlsForReload do
-        local reloadControl = controlsForReload[i]
-        if not IsSame(reloadControl.startValue, {reloadControl.data.getFunc()}) then
-            reloadControl:UpdateValue(false, unpack(reloadControl.startValue))
-        end
-    end
-    lam.requiresReload = false
-    lam.applyButton:SetHidden(true)
-end
-
-local function StorePanelForReopening()
-    local saveData = ZO_Ingame_SavedVariables["LAM"] or {}
-    saveData.reopenPanel = lam.currentAddonPanel:GetName()
-    ZO_Ingame_SavedVariables["LAM"] = saveData
-end
-
-local function RetrievePanelForReopening()
-    local saveData = ZO_Ingame_SavedVariables["LAM"]
-    if(saveData) then
-        ZO_Ingame_SavedVariables["LAM"] = nil
-        return _G[saveData.reopenPanel]
-    end
-end
-
-local function HandleReloadUIPressed()
-    StorePanelForReopening()
-    ReloadUI()
-end
-
-local function HandleLoadDefaultsPressed()
-    ShowDefaultsDialog(lam.currentAddonPanel)
-end
-
-local function GetReloadDialog()
-    if(not ESO_Dialogs[LAM_RELOAD_DIALOG]) then
-        ESO_Dialogs[LAM_RELOAD_DIALOG] = {
-            canQueue = true,
-            title = {
-                text = util.L["RELOAD_DIALOG_TITLE"],
-            },
-            mainText = {
-                text = util.L["RELOAD_DIALOG_TEXT"],
-            },
-            buttons = {
-                [1] = {
-                    text = util.L["RELOAD_DIALOG_RELOAD_BUTTON"],
-                    callback = function() ReloadUI() end,
-                },
-                [2] = {
-                    text = util.L["RELOAD_DIALOG_DISCARD_BUTTON"],
-                    callback = DiscardChangesOnReloadControls,
-                }
-            },
-            noChoiceCallback = DiscardChangesOnReloadControls,
-        }
-    end
-    return ESO_Dialogs[LAM_CONFIRM_DIALOG]
-end
-
-local function ShowReloadDialogIfNeeded()
-    if lam.requiresReload then
-        local dialog = GetReloadDialog()
-        ZO_Dialogs_ShowDialog(LAM_RELOAD_DIALOG)
-    end
-end
-
-local function UpdateWarning(control)
-    local warning
-    if control.data.warning ~= nil then
-        warning = util.GetStringFromValue(control.data.warning)
-    end
-
-    if control.data.requiresReload then
-        if not warning then
-            warning = string.format("|cff0000%s", util.L["RELOAD_UI_WARNING"])
-        else
-            warning = string.format("%s\n\n|cff0000%s", warning, util.L["RELOAD_UI_WARNING"])
-        end
-    end
-
-    if not warning then
-        control.warning:SetHidden(true)
-    else
-        control.warning.data = {tooltipText = warning}
-        control.warning:SetHidden(false)
-    end
-end
-
-local localization = {
-    en = {
-        PANEL_NAME = "Addons",
-        AUTHOR = string.format("%s: <<X:1>>", GetString(SI_ADDON_MANAGER_AUTHOR)), -- "Author: <<X:1>>"
-        VERSION = "Version: <<X:1>>",
-        WEBSITE = "Visit Website",
-        PANEL_INFO_FONT = "$(CHAT_FONT)|14|soft-shadow-thin",
-        RELOAD_UI_WARNING = "Changes to this setting require an UI reload in order to take effect.",
-        RELOAD_DIALOG_TITLE = "UI Reload required",
-        RELOAD_DIALOG_TEXT = "Some changes require an UI reload in order to take effect. Do you want to reload now or discard the changes?",
-        RELOAD_DIALOG_RELOAD_BUTTON = "Reload",
-        RELOAD_DIALOG_DISCARD_BUTTON = "Discard",
-    },
-    it = { -- provided by JohnnyKing
-        PANEL_NAME = "Addon",
-        VERSION = "Versione: <<X:1>>",
-        WEBSITE = "Visita il Sitoweb",
-        RELOAD_UI_WARNING = "Cambiare questa impostazione richiede un Ricarica UI al fine che faccia effetto.",
-        RELOAD_DIALOG_TITLE = "Ricarica UI richiesto",
-        RELOAD_DIALOG_TEXT = "Alcune modifiche richiedono un Ricarica UI al fine che facciano effetto. Sei sicuro di voler ricaricare ora o di voler annullare le modifiche?",
-        RELOAD_DIALOG_RELOAD_BUTTON = "Ricarica",
-        RELOAD_DIALOG_DISCARD_BUTTON = "Annulla",
-    },
-    fr = { -- provided by Ayantir
-        PANEL_NAME = "Extensions",
-        WEBSITE = "Visiter le site Web",
-        RELOAD_UI_WARNING = "La modification de ce paramètre requiert un rechargement de l'UI pour qu'il soit pris en compte.",
-        RELOAD_DIALOG_TITLE = "Reload UI requis",
-        RELOAD_DIALOG_TEXT = "Certaines modifications requièrent un rechargement de l'UI pour qu'ils soient pris en compte. Souhaitez-vous recharger l'interface maintenant ou annuler les modifications ?",
-        RELOAD_DIALOG_RELOAD_BUTTON = "Recharger",
-        RELOAD_DIALOG_DISCARD_BUTTON = "Annuler",
-    },
-    de = { -- provided by sirinsidiator
-        PANEL_NAME = "Erweiterungen",
-        WEBSITE = "Webseite besuchen",
-        RELOAD_UI_WARNING = "Änderungen an dieser Option werden erst übernommen nachdem die Benutzeroberfläche neu geladen wird.",
-        RELOAD_DIALOG_TITLE = "Neuladen benötigt",
-        RELOAD_DIALOG_TEXT = "Einige Änderungen werden erst übernommen nachdem die Benutzeroberfläche neu geladen wird. Wollt Ihr sie jetzt neu laden oder die Änderungen verwerfen?",
-        RELOAD_DIALOG_RELOAD_BUTTON = "Neu laden",
-        RELOAD_DIALOG_DISCARD_BUTTON = "Verwerfen",
-    },
-    ru = { -- provided by TERAB1T
-        PANEL_NAME = "Дополнения",
-        VERSION = "Версия: <<X:1>>",
-        WEBSITE = "Посетить сайт",
-        PANEL_INFO_FONT = "RuESO/fonts/Univers57.otf|14|soft-shadow-thin",
-        RELOAD_UI_WARNING = "Для применения этой настройки необходима перезагрузка интерфейса.",
-        RELOAD_DIALOG_TITLE = "Необходима перезагрузка интерфейса",
-        RELOAD_DIALOG_TEXT = "Для применения некоторых изменений необходима перезагрузка интерфейса. Перезагрузить интерфейс сейчас или отменить изменения?",
-        RELOAD_DIALOG_RELOAD_BUTTON = "Перезагрузить",
-        RELOAD_DIALOG_DISCARD_BUTTON = "Отменить изменения",
-    },
-    es = { -- provided by Morganlefai, checked by Kwisatz
-        PANEL_NAME = "Configuración",
-        VERSION = "Versión: <<X:1>>",
-        WEBSITE = "Visita la página web",
-        RELOAD_UI_WARNING = "Cambiar este ajuste recargará la interfaz del usuario.",
-        RELOAD_DIALOG_TITLE = "Requiere recargar la interfaz",
-        RELOAD_DIALOG_TEXT = "Algunos cambios requieren recargar la interfaz para poder aplicarse. Quieres aplicar los cambios y recargar la interfaz?",
-        RELOAD_DIALOG_RELOAD_BUTTON = "Recargar",
-        RELOAD_DIALOG_DISCARD_BUTTON = "Cancelar",
-    },
-    jp = { -- provided by k0ta0uchi
-        PANEL_NAME = "アドオン設定",
-        WEBSITE = "ウェブサイトを見る",
-    },
-    zh = { -- provided by bssthu
-        PANEL_NAME = "插件",
-        VERSION = "版本: <<X:1>>",
-        WEBSITE = "访问网站",
-        PANEL_INFO_FONT = "EsoZh/fonts/univers57.otf|14|soft-shadow-thin",
-    },
-    pl = { -- provided by EmiruTegryfon
-        PANEL_NAME = "Dodatki",
-        VERSION = "Wersja: <<X:1>>",
-        WEBSITE = "Odwiedź stronę",
-        RELOAD_UI_WARNING = "Zmiany będą widoczne po ponownym załadowaniu UI.",
-        RELOAD_DIALOG_TITLE = "Wymagane przeładowanie UI",
-        RELOAD_DIALOG_TEXT = "Niektóre zmiany wymagają ponownego załadowania UI. Czy chcesz teraz ponownie załadować, czy porzucić zmiany?",
-        RELOAD_DIALOG_RELOAD_BUTTON = "Przeładuj",
-        RELOAD_DIALOG_DISCARD_BUTTON = "Porzuć",
-    },
-}
-
-util.L = ZO_ShallowTableCopy(localization[GetCVar("Language.2")], localization["en"])
-util.GetTooltipText = GetStringFromValue -- deprecated, use util.GetStringFromValue instead
-util.GetStringFromValue = GetStringFromValue
-util.GetDefaultValue = GetDefaultValue
-util.CreateBaseControl = CreateBaseControl
-util.CreateLabelAndContainerControl = CreateLabelAndContainerControl
-util.RequestRefreshIfNeeded = RequestRefreshIfNeeded
-util.RegisterForRefreshIfNeeded = RegisterForRefreshIfNeeded
-util.RegisterForReloadIfNeeded = RegisterForReloadIfNeeded
-util.GetTopPanel = GetTopPanel
-util.ShowConfirmationDialog = ShowConfirmationDialog
-util.UpdateWarning = UpdateWarning
-
-local ADDON_DATA_TYPE = 1
-local RESELECTING_DURING_REBUILD = true
-local USER_REQUESTED_OPEN = true
-
-
---INTERNAL FUNCTION
---scrolls ZO_ScrollList `list` to move the row corresponding to `data`
--- into view (does nothing if there is no such row in the list)
---unlike ZO_ScrollList_ScrollDataIntoView, this function accounts for
--- fading near the list's edges - it avoids the fading area by scrolling
--- a little further than the ZO function
-local function ScrollDataIntoView(list, data)
-    local targetIndex = data.sortIndex
-    if not targetIndex then return end
-
-    local scrollMin, scrollMax = list.scrollbar:GetMinMax()
-    local scrollTop = list.scrollbar:GetValue()
-    local controlHeight = list.uniformControlHeight or list.controlHeight
-    local targetMin = controlHeight * (targetIndex - 1) - 64
-    -- subtracting 64 ain't arbitrary, it's the maximum fading height
-    -- (libraries/zo_templates/scrolltemplates.lua/UpdateScrollFade)
-
-    if targetMin < scrollTop then
-        ZO_ScrollList_ScrollAbsolute(list, zo_max(targetMin, scrollMin))
-    else
-        local listHeight = ZO_ScrollList_GetHeight(list)
-        local targetMax = controlHeight * targetIndex + 64 - listHeight
-
-        if targetMax > scrollTop then
-            ZO_ScrollList_ScrollAbsolute(list, zo_min(targetMax, scrollMax))
-        end
-    end
-end
-
-
---INTERNAL FUNCTION
---constructs a string pattern from the text in `searchEdit` control
--- * metacharacters are escaped, losing their special meaning
--- * whitespace matches anything (including empty substring)
---if there is nothing but whitespace, returns nil
---otherwise returns a filter function, which takes a `data` table argument
--- and returns true iff `data.filterText` matches the pattern
-local function GetSearchFilterFunc(searchEdit)
-    local text = searchEdit:GetText():lower()
-    local pattern = text:match("(%S+.-)%s*$")
-
-    if not pattern then -- nothing but whitespace
-        return nil
-    end
-
-    -- escape metacharacters, e.g. "ESO-Datenbank.de" => "ESO%-Datenbank%.de"
-    pattern = pattern:gsub("[-*+?^$().[%]%%]", "%%%0")
-
-    -- replace whitespace with "match shortest anything"
-    pattern = pattern:gsub("%s+", ".-")
-
-    return function(data)
-        return data.filterText:lower():find(pattern) ~= nil
-    end
-end
-
-
---INTERNAL FUNCTION
---populates `addonList` with entries from `addonsForList`
--- addonList = ZO_ScrollList control
--- filter = [optional] function(data)
-local function PopulateAddonList(addonList, filter)
-    local entryList = ZO_ScrollList_GetDataList(addonList)
-    local numEntries = 0
-    local selectedData = nil
-    local selectionIsFinal = false
-
-    ZO_ScrollList_Clear(addonList)
-
-    for i, data in ipairs(addonsForList) do
-        if not filter or filter(data) then
-            local dataEntry = ZO_ScrollList_CreateDataEntry(ADDON_DATA_TYPE, data)
-            numEntries = numEntries + 1
-            data.sortIndex = numEntries
-            entryList[numEntries] = dataEntry
-            -- select the first panel passing the filter, or the currently
-            -- shown panel, but only if it passes the filter as well
-            if selectedData == nil or data.panel == lam.pendingAddonPanel or data.panel == lam.currentAddonPanel then
-                if not selectionIsFinal then
-                    selectedData = data
-                end
-                if data.panel == lam.pendingAddonPanel then
-                    lam.pendingAddonPanel = nil
-                    selectionIsFinal = true
-                end
-            end
-        else
-            data.sortIndex = nil
-        end
-    end
-
-    ZO_ScrollList_Commit(addonList)
-
-    if selectedData then
-        if selectedData.panel == lam.currentAddonPanel then
-            ZO_ScrollList_SelectData(addonList, selectedData, nil, RESELECTING_DURING_REBUILD)
-        else
-            ZO_ScrollList_SelectData(addonList, selectedData, nil)
-        end
-        ScrollDataIntoView(addonList, selectedData)
-    end
-end
-
-
---METHOD: REGISTER WIDGET--
---each widget has its version checked before loading,
---so we only have the most recent one in memory
---Usage:
--- widgetType = "string"; the type of widget being registered
--- widgetVersion = integer; the widget's version number
-LAMCreateControl = LAMCreateControl or {}
-local lamcc = LAMCreateControl
-
-function lam:RegisterWidget(widgetType, widgetVersion)
-    if widgets[widgetType] and widgets[widgetType] >= widgetVersion then
-        return false
-    else
-        widgets[widgetType] = widgetVersion
-        return true
-    end
-end
-
--- INTERNAL METHOD: hijacks the handlers for the actions in the OptionsWindow layer if not already done
-local function InitKeybindActions()
-    if not lam.keybindsInitialized then
-        lam.keybindsInitialized = true
-        ZO_PreHook(KEYBOARD_OPTIONS, "ApplySettings", function()
-            if lam.currentPanelOpened then
-                if not lam.applyButton:IsHidden() then
-                    HandleReloadUIPressed()
-                end
-                return true
-            end
-        end)
-        ZO_PreHook("ZO_Dialogs_ShowDialog", function(dialogName)
-            if lam.currentPanelOpened and dialogName == "OPTIONS_RESET_TO_DEFAULTS" then
-                if not lam.defaultButton:IsHidden() then
-                    HandleLoadDefaultsPressed()
-                end
-                return true
-            end
-        end)
-    end
-end
-
--- INTERNAL METHOD: fires the LAM-PanelOpened callback if not already done
-local function OpenCurrentPanel()
-    if lam.currentAddonPanel and not lam.currentPanelOpened then
-        lam.currentPanelOpened = true
-        lam.defaultButton:SetHidden(not lam.currentAddonPanel.data.registerForDefaults)
-        cm:FireCallbacks("LAM-PanelOpened", lam.currentAddonPanel)
-    end
-end
-
--- INTERNAL METHOD: fires the LAM-PanelClosed callback if not already done
-local function CloseCurrentPanel()
-    if lam.currentAddonPanel and lam.currentPanelOpened then
-        lam.currentPanelOpened = false
-        cm:FireCallbacks("LAM-PanelClosed", lam.currentAddonPanel)
-    end
-end
-
---METHOD: OPEN TO ADDON PANEL--
---opens to a specific addon's option panel
---Usage:
--- panel = userdata; the panel returned by the :RegisterOptionsPanel method
-local locSettings = GetString(SI_GAME_MENU_SETTINGS)
-function lam:OpenToPanel(panel)
-
-    -- find and select the panel's row in addon list
-
-    local addonList = lam.addonList
-    local selectedData = nil
-
-    for _, addonData in ipairs(addonsForList) do
-        if addonData.panel == panel then
-            selectedData = addonData
-            ScrollDataIntoView(addonList, selectedData)
-            lam.pendingAddonPanel = addonData.panel
-            break
-        end
-    end
-
-    ZO_ScrollList_SelectData(addonList, selectedData)
-    ZO_ScrollList_RefreshVisible(addonList, selectedData)
-
-    local srchEdit = LAMAddonSettingsWindow:GetNamedChild("SearchFilterEdit")
-    srchEdit:Clear()
-
-    -- note that ZO_ScrollList doesn't require `selectedData` to be actually
-    -- present in the list, and that the list will only be populated once LAM
-    -- "Addon Settings" menu entry is selected for the first time
-
-    local function openAddonSettingsMenu()
-        local gameMenu = ZO_GameMenu_InGame.gameMenu
-        local settingsMenu = gameMenu.headerControls[locSettings]
-
-        if settingsMenu then -- an instance of ZO_TreeNode
-            local children = settingsMenu:GetChildren()
-            for i = 1, (children and #children or 0) do
-                local childNode = children[i]
-                local data = childNode:GetData()
-                if data and data.id == lam.panelId then
-                    -- found LAM "Addon Settings" node, yay!
-                    childNode:GetTree():SelectNode(childNode)
-                    break
-                end
-            end
-        end
-    end
-
-    if sm:GetScene("gameMenuInGame"):GetState() == SCENE_SHOWN then
-        openAddonSettingsMenu()
-    else
-        sm:CallWhen("gameMenuInGame", SCENE_SHOWN, openAddonSettingsMenu)
-        sm:Show("gameMenuInGame")
-    end
-end
-
-local TwinOptionsContainer_Index = 0
-local function TwinOptionsContainer(parent, leftWidget, rightWidget)
-    TwinOptionsContainer_Index = TwinOptionsContainer_Index + 1
-    local cParent = parent.scroll or parent
-    local panel = parent.panel or cParent
-    local container = wm:CreateControl("$(parent)TwinContainer" .. tostring(TwinOptionsContainer_Index),
-        cParent, CT_CONTROL)
-    container:SetResizeToFitDescendents(true)
-    container:SetAnchor(select(2, leftWidget:GetAnchor(0) ))
-
-    leftWidget:ClearAnchors()
-    leftWidget:SetAnchor(TOPLEFT, container, TOPLEFT)
-    rightWidget:SetAnchor(TOPLEFT, leftWidget, TOPRIGHT, 5, 0)
-
-    leftWidget:SetWidth( leftWidget:GetWidth() - 2.5 ) -- fixes bad alignment with 'full' controls
-    rightWidget:SetWidth( rightWidget:GetWidth() - 2.5 )
-
-    leftWidget:SetParent(container)
-    rightWidget:SetParent(container)
-
-    container.data = {type = "container"}
-    container.panel = panel
-    return container
-end
-
---INTERNAL FUNCTION
---creates controls when options panel is first shown
---controls anchoring of these controls in the panel
-local function CreateOptionsControls(panel)
-    local addonID = panel:GetName()
-    if(optionsState[addonID] == OPTIONS_CREATED) then
-        return false
-    elseif(optionsState[addonID] == OPTIONS_CREATION_RUNNING) then
-        return true
-    end
-    optionsState[addonID] = OPTIONS_CREATION_RUNNING
-
-    local function CreationFinished()
-        optionsState[addonID] = OPTIONS_CREATED
-        cm:FireCallbacks("LAM-PanelControlsCreated", panel)
-        OpenCurrentPanel()
-    end
-
-    local optionsTable = addonToOptionsMap[addonID]
-    if optionsTable then
-        local function CreateAndAnchorWidget(parent, widgetData, offsetX, offsetY, anchorTarget, wasHalf)
-            local widget
-            local status, err = pcall(function() widget = LAMCreateControl[widgetData.type](parent, widgetData) end)
-            if not status then
-                return err or true, offsetY, anchorTarget, wasHalf
-            else
-                local isHalf = (widgetData.width == "half")
-                if not anchorTarget then -- the first widget in a panel is just placed in the top left corner
-                    widget:SetAnchor(TOPLEFT)
-                    anchorTarget = widget
-                elseif wasHalf and isHalf then -- when the previous widget was only half width and this one is too, we place it on the right side
-                    widget.lineControl = anchorTarget
-                    isHalf = false
-                    offsetY = 0
-                    anchorTarget = TwinOptionsContainer(parent, anchorTarget, widget)
-                else -- otherwise we just put it below the previous one normally
-                    widget:SetAnchor(TOPLEFT, anchorTarget, BOTTOMLEFT, 0, 15)
-                    offsetY = 0
-                    anchorTarget = widget
-                end
-                return false, offsetY, anchorTarget, isHalf
-            end
-        end
-
-        local THROTTLE_TIMEOUT, THROTTLE_COUNT = 10, 20
-        local fifo = {}
-        local anchorOffset, lastAddedControl, wasHalf
-        local CreateWidgetsInPanel, err
-
-        local function PrepareForNextPanel()
-            anchorOffset, lastAddedControl, wasHalf = 0, nil, false
-        end
-
-        local function SetupCreationCalls(parent, widgetDataTable)
-            fifo[#fifo + 1] = PrepareForNextPanel
-            local count = #widgetDataTable
-            for i = 1, count, THROTTLE_COUNT do
-                fifo[#fifo + 1] = function()
-                    CreateWidgetsInPanel(parent, widgetDataTable, i, zo_min(i + THROTTLE_COUNT - 1, count))
-                end
-            end
-            return count ~= NonContiguousCount(widgetDataTable)
-        end
-
-        CreateWidgetsInPanel = function(parent, widgetDataTable, startIndex, endIndex)
-            for i=startIndex,endIndex do
-                local widgetData = widgetDataTable[i]
-                if not widgetData then
-                    PrintLater("Skipped creation of missing entry in the settings menu of " .. addonID .. ".")
-                else
-                    local widgetType = widgetData.type
-                    local offsetX = 0
-                    local isSubmenu = (widgetType == "submenu")
-                    if isSubmenu then
-                        wasHalf = false
-                        offsetX = 5
-                    end
-
-                    err, anchorOffset, lastAddedControl, wasHalf = CreateAndAnchorWidget(parent, widgetData, offsetX, anchorOffset, lastAddedControl, wasHalf)
-                    if err then
-                        PrintLater(("Could not create %s '%s' of %s."):format(widgetData.type, GetStringFromValue(widgetData.name or "unnamed"), addonID))
-                    end
-
-                    if isSubmenu then
-                        if SetupCreationCalls(lastAddedControl, widgetData.controls) then
-                            PrintLater(("The sub menu '%s' of %s is missing some entries."):format(GetStringFromValue(widgetData.name or "unnamed"), addonID))
-                        end
-                    end
-                end
-            end
-        end
-
-        local function DoCreateSettings()
-            if #fifo > 0 then
-                local nextCall = table.remove(fifo, 1)
-                nextCall()
-                if(nextCall == PrepareForNextPanel) then
-                    DoCreateSettings()
-                else
-                    zo_callLater(DoCreateSettings, THROTTLE_TIMEOUT)
-                end
-            else
-                CreationFinished()
-            end
-        end
-
-        if SetupCreationCalls(panel, optionsTable) then
-            PrintLater(("The settings menu of %s is missing some entries."):format(addonID))
-        end
-        DoCreateSettings()
-    else
-        CreationFinished()
-    end
-
-    return true
-end
-
---INTERNAL FUNCTION
---handles switching between panels
-local function ToggleAddonPanels(panel) --called in OnShow of newly shown panel
-    local currentlySelected = lam.currentAddonPanel
-    if currentlySelected and currentlySelected ~= panel then
-        currentlySelected:SetHidden(true)
-        CloseCurrentPanel()
-    end
-    lam.currentAddonPanel = panel
-
-    -- refresh visible rows to reflect panel IsHidden status
-    ZO_ScrollList_RefreshVisible(lam.addonList)
-
-    if not CreateOptionsControls(panel) then
-        OpenCurrentPanel()
-    end
-
-    cm:FireCallbacks("LAM-RefreshPanel", panel)
-end
-
-local CheckSafetyAndInitialize
-
---METHOD: REGISTER ADDON PANEL
---registers your addon with LibAddonMenu and creates a panel
---Usage:
--- addonID = "string"; unique ID which will be the global name of your panel
--- panelData = table; data object for your panel - see controls\panel.lua
-function lam:RegisterAddonPanel(addonID, panelData)
-    CheckSafetyAndInitialize(addonID)
-    local container = lam:GetAddonPanelContainer()
-    local panel = lamcc.panel(container, panelData, addonID) --addonID==global name of panel
-    panel:SetHidden(true)
-    panel:SetAnchorFill(container)
-    panel:SetHandler("OnShow", ToggleAddonPanels)
-
-    local function stripMarkup(str)
-        return str:gsub("|[Cc]%x%x%x%x%x%x", ""):gsub("|[Rr]", "")
-    end
-
-    local filterParts = {panelData.name, nil, nil}
-    -- append keywords and author separately, the may be nil
-    filterParts[#filterParts + 1] = panelData.keywords
-    filterParts[#filterParts + 1] = panelData.author
-
-    local addonData = {
-        panel = panel,
-        name = stripMarkup(panelData.name),
-        filterText = stripMarkup(tconcat(filterParts, "\t")):lower(),
-    }
-
-    tinsert(addonsForList, addonData)
-
-    if panelData.slashCommand then
-        SLASH_COMMANDS[panelData.slashCommand] = function()
-            lam:OpenToPanel(panel)
-        end
-    end
-
-    return panel --return for authors creating options manually
-end
-
-
---METHOD: REGISTER OPTION CONTROLS
---registers the options you want shown for your addon
---these are stored in a table where each key-value pair is the order
---of the options in the panel and the data for that control, respectively
---see exampleoptions.lua for an example
---see controls\<widget>.lua for each widget type
---Usage:
--- addonID = "string"; the same string passed to :RegisterAddonPanel
--- optionsTable = table; the table containing all of the options controls and their data
-function lam:RegisterOptionControls(addonID, optionsTable) --optionsTable = {sliderData, buttonData, etc}
-    addonToOptionsMap[addonID] = optionsTable
-end
-
---INTERNAL FUNCTION
---creates LAM's Addon Settings entry in ZO_GameMenu
-local function CreateAddonSettingsMenuEntry()
-    local panelData = {
-        id = KEYBOARD_OPTIONS.currentPanelId,
-        name = util.L["PANEL_NAME"],
-    }
-
-    KEYBOARD_OPTIONS.currentPanelId = panelData.id + 1
-    KEYBOARD_OPTIONS.panelNames[panelData.id] = panelData.name
-
-    lam.panelId = panelData.id
-
-    local addonListSorted = false
-
-    function panelData.callback()
-        sm:AddFragment(lam:GetAddonSettingsFragment())
-        KEYBOARD_OPTIONS:ChangePanels(lam.panelId)
-
-        local title = LAMAddonSettingsWindow:GetNamedChild("Title")
-        title:SetText(panelData.name)
-
-        if not addonListSorted and #addonsForList > 0 then
-            local searchEdit = LAMAddonSettingsWindow:GetNamedChild("SearchFilterEdit")
-            --we're about to show our list for the first time - let's sort it
-            table.sort(addonsForList, function(a, b) return a.name < b.name end)
-            PopulateAddonList(lam.addonList, GetSearchFilterFunc(searchEdit))
-            addonListSorted = true
-        end
-    end
-
-    function panelData.unselectedCallback()
-        sm:RemoveFragment(lam:GetAddonSettingsFragment())
-        if SetCameraOptionsPreviewModeEnabled then -- available since API version 100011
-            SetCameraOptionsPreviewModeEnabled(false)
-        end
-    end
-
-    ZO_GameMenu_AddSettingPanel(panelData)
-end
-
-
---INTERNAL FUNCTION
---creates the left-hand menu in LAM's window
-local function CreateAddonList(name, parent)
-    local addonList = wm:CreateControlFromVirtual(name, parent, "ZO_ScrollList")
-
-    local function addonListRow_OnMouseDown(control, button)
-        if button == 1 then
-            local data = ZO_ScrollList_GetData(control)
-            ZO_ScrollList_SelectData(addonList, data, control)
-        end
-    end
-
-    local function addonListRow_OnMouseEnter(control)
-        ZO_ScrollList_MouseEnter(addonList, control)
-    end
-
-    local function addonListRow_OnMouseExit(control)
-        ZO_ScrollList_MouseExit(addonList, control)
-    end
-
-    local function addonListRow_Select(previouslySelectedData, selectedData, reselectingDuringRebuild)
-        if not reselectingDuringRebuild then
-            if previouslySelectedData then
-                previouslySelectedData.panel:SetHidden(true)
-            end
-            if selectedData then
-                selectedData.panel:SetHidden(false)
-                PlaySound(SOUNDS.MENU_SUBCATEGORY_SELECTION)
-            end
-        end
-    end
-
-    local function addonListRow_Setup(control, data)
-        control:SetText(data.name)
-        control:SetSelected(not data.panel:IsHidden())
-    end
-
-    ZO_ScrollList_AddDataType(addonList, ADDON_DATA_TYPE, "ZO_SelectableLabel", 28, addonListRow_Setup)
-    -- I don't know how to make highlights clear properly; they often
-    -- get stuck and after a while the list is full of highlighted rows
-    --ZO_ScrollList_EnableHighlight(addonList, "ZO_ThinListHighlight")
-    ZO_ScrollList_EnableSelection(addonList, "ZO_ThinListHighlight", addonListRow_Select)
-
-    local addonDataType = ZO_ScrollList_GetDataTypeTable(addonList, ADDON_DATA_TYPE)
-    local addonListRow_CreateRaw = addonDataType.pool.m_Factory
-
-    local function addonListRow_Create(pool)
-        local control = addonListRow_CreateRaw(pool)
-        control:SetHandler("OnMouseDown", addonListRow_OnMouseDown)
-        --control:SetHandler("OnMouseEnter", addonListRow_OnMouseEnter)
-        --control:SetHandler("OnMouseExit", addonListRow_OnMouseExit)
-        control:SetHeight(28)
-        control:SetFont("ZoFontHeader")
-        control:SetHorizontalAlignment(TEXT_ALIGN_LEFT)
-        control:SetVerticalAlignment(TEXT_ALIGN_CENTER)
-        control:SetWrapMode(TEXT_WRAP_MODE_ELLIPSIS)
-        return control
-    end
-
-    addonDataType.pool.m_Factory = addonListRow_Create
-
-    return addonList
-end
-
-
---INTERNAL FUNCTION
-local function CreateSearchFilterBox(name, parent)
-    local boxControl = wm:CreateControl(name, parent, CT_CONTROL)
-
-    local srchButton =  wm:CreateControl("$(parent)Button", boxControl, CT_BUTTON)
-    srchButton:SetDimensions(32, 32)
-    srchButton:SetAnchor(LEFT, nil, LEFT, 2, 0)
-    srchButton:SetNormalTexture("EsoUI/Art/LFG/LFG_tabIcon_groupTools_up.dds")
-    srchButton:SetPressedTexture("EsoUI/Art/LFG/LFG_tabIcon_groupTools_down.dds")
-    srchButton:SetMouseOverTexture("EsoUI/Art/LFG/LFG_tabIcon_groupTools_over.dds")
-
-    local srchEdit = wm:CreateControlFromVirtual("$(parent)Edit", boxControl, "ZO_DefaultEdit")
-    srchEdit:SetAnchor(LEFT, srchButton, RIGHT, 4, 1)
-    srchEdit:SetAnchor(RIGHT, nil, RIGHT, -4, 1)
-    srchEdit:SetColor(ZO_NORMAL_TEXT:UnpackRGBA())
-
-    local srchBg = wm:CreateControl("$(parent)Bg", boxControl, CT_BACKDROP)
-    srchBg:SetAnchorFill()
-    srchBg:SetAlpha(0)
-    srchBg:SetCenterColor(0, 0, 0, 0.5)
-    srchBg:SetEdgeColor(ZO_DISABLED_TEXT:UnpackRGBA())
-    srchBg:SetEdgeTexture("", 1, 1, 0, 0)
-
-    -- search backdrop should appear whenever you hover over either
-    -- the magnifying glass button or the edit field (which is only
-    -- visible when it contains some text), and also while the edit
-    -- field has keyboard focus
-
-    local srchActive = false
-    local srchHover = false
-
-    local function srchBgUpdateAlpha()
-        if srchActive or srchEdit:HasFocus() then
-            srchBg:SetAlpha(srchHover and 0.8 or 0.6)
-        else
-            srchBg:SetAlpha(srchHover and 0.6 or 0.0)
-        end
-    end
-
-    local function srchMouseEnter(control)
-        srchHover = true
-        srchBgUpdateAlpha()
-    end
-
-    local function srchMouseExit(control)
-        srchHover = false
-        srchBgUpdateAlpha()
-    end
-
-    boxControl:SetMouseEnabled(true)
-    boxControl:SetHitInsets(1, 1, -1, -1)
-    boxControl:SetHandler("OnMouseEnter", srchMouseEnter)
-    boxControl:SetHandler("OnMouseExit", srchMouseExit)
-
-    srchButton:SetHandler("OnMouseEnter", srchMouseEnter)
-    srchButton:SetHandler("OnMouseExit", srchMouseExit)
-
-    local focusLostTime = 0
-
-    srchButton:SetHandler("OnClicked", function(self)
-        srchEdit:Clear()
-        if GetFrameTimeMilliseconds() - focusLostTime < 100 then
-            -- re-focus the edit box if it lost focus due to this
-            -- button click (note that this handler may run a few
-            -- frames later)
-            srchEdit:TakeFocus()
-        end
-    end)
-
-    srchEdit:SetHandler("OnMouseEnter", srchMouseEnter)
-    srchEdit:SetHandler("OnMouseExit", srchMouseExit)
-    srchEdit:SetHandler("OnFocusGained", srchBgUpdateAlpha)
-
-    srchEdit:SetHandler("OnFocusLost", function()
-        focusLostTime = GetFrameTimeMilliseconds()
-        srchBgUpdateAlpha()
-    end)
-
-    srchEdit:SetHandler("OnEscape", function(self)
-        self:Clear()
-        self:LoseFocus()
-    end)
-
-    srchEdit:SetHandler("OnTextChanged", function(self)
-        local filterFunc = GetSearchFilterFunc(self)
-        if filterFunc then
-            srchActive = true
-            srchBg:SetEdgeColor(ZO_SECOND_CONTRAST_TEXT:UnpackRGBA())
-            srchButton:SetState(BSTATE_PRESSED)
-        else
-            srchActive = false
-            srchBg:SetEdgeColor(ZO_DISABLED_TEXT:UnpackRGBA())
-            srchButton:SetState(BSTATE_NORMAL)
-        end
-        srchBgUpdateAlpha()
-        PopulateAddonList(lam.addonList, filterFunc)
-        PlaySound(SOUNDS.SPINNER_DOWN)
-    end)
-
-    return boxControl
-end
-
-
---INTERNAL FUNCTION
---creates LAM's Addon Settings top-level window
-local function CreateAddonSettingsWindow()
-    local tlw = wm:CreateTopLevelWindow("LAMAddonSettingsWindow")
-    tlw:SetHidden(true)
-    tlw:SetDimensions(1010, 914) -- same height as ZO_OptionsWindow
-
-    ZO_ReanchorControlForLeftSidePanel(tlw)
-
-    -- create black background for the window (mimic ZO_RightFootPrintBackground)
-
-    local bgLeft = wm:CreateControl("$(parent)BackgroundLeft", tlw, CT_TEXTURE)
-    bgLeft:SetTexture("EsoUI/Art/Miscellaneous/centerscreen_left.dds")
-    bgLeft:SetDimensions(1024, 1024)
-    bgLeft:SetAnchor(TOPLEFT, nil, TOPLEFT)
-    bgLeft:SetDrawLayer(DL_BACKGROUND)
-    bgLeft:SetExcludeFromResizeToFitExtents(true)
-
-    local bgRight = wm:CreateControl("$(parent)BackgroundRight", tlw, CT_TEXTURE)
-    bgRight:SetTexture("EsoUI/Art/Miscellaneous/centerscreen_right.dds")
-    bgRight:SetDimensions(64, 1024)
-    bgRight:SetAnchor(TOPLEFT, bgLeft, TOPRIGHT)
-    bgRight:SetDrawLayer(DL_BACKGROUND)
-    bgRight:SetExcludeFromResizeToFitExtents(true)
-
-    -- create gray background for addon list (mimic ZO_TreeUnderlay)
-
-    local underlayLeft = wm:CreateControl("$(parent)UnderlayLeft", tlw, CT_TEXTURE)
-    underlayLeft:SetTexture("EsoUI/Art/Miscellaneous/centerscreen_indexArea_left.dds")
-    underlayLeft:SetDimensions(256, 1024)
-    underlayLeft:SetAnchor(TOPLEFT, bgLeft, TOPLEFT)
-    underlayLeft:SetDrawLayer(DL_BACKGROUND)
-    underlayLeft:SetExcludeFromResizeToFitExtents(true)
-
-    local underlayRight = wm:CreateControl("$(parent)UnderlayRight", tlw, CT_TEXTURE)
-    underlayRight:SetTexture("EsoUI/Art/Miscellaneous/centerscreen_indexArea_right.dds")
-    underlayRight:SetDimensions(128, 1024)
-    underlayRight:SetAnchor(TOPLEFT, underlayLeft, TOPRIGHT)
-    underlayRight:SetDrawLayer(DL_BACKGROUND)
-    underlayRight:SetExcludeFromResizeToFitExtents(true)
-
-    -- create title bar (mimic ZO_OptionsWindow)
-
-    local title = wm:CreateControl("$(parent)Title", tlw, CT_LABEL)
-    title:SetAnchor(TOPLEFT, nil, TOPLEFT, 65, 70)
-    title:SetFont("ZoFontWinH1")
-    title:SetModifyTextType(MODIFY_TEXT_TYPE_UPPERCASE)
-
-    local divider = wm:CreateControlFromVirtual("$(parent)Divider", tlw, "ZO_Options_Divider")
-    divider:SetAnchor(TOPLEFT, nil, TOPLEFT, 65, 108)
-
-    -- create search filter box
-
-    local srchBox = CreateSearchFilterBox("$(parent)SearchFilter", tlw)
-    srchBox:SetAnchor(TOPLEFT, nil, TOPLEFT, 63, 120)
-    srchBox:SetDimensions(260, 30)
-
-    -- create scrollable addon list
-
-    local addonList = CreateAddonList("$(parent)AddonList", tlw)
-    addonList:SetAnchor(TOPLEFT, nil, TOPLEFT, 65, 160)
-    addonList:SetDimensions(285, 665)
-
-    lam.addonList = addonList -- for easy access from elsewhere
-
-    -- create container for option panels
-
-    local panelContainer = wm:CreateControl("$(parent)PanelContainer", tlw, CT_CONTROL)
-    panelContainer:SetAnchor(TOPLEFT, nil, TOPLEFT, 365, 120)
-    panelContainer:SetDimensions(645, 675)
-
-    local defaultButton = wm:CreateControlFromVirtual("$(parent)ResetToDefaultButton", tlw, "ZO_DialogButton")
-    ZO_KeybindButtonTemplate_Setup(defaultButton, "OPTIONS_LOAD_DEFAULTS", HandleLoadDefaultsPressed, GetString(SI_OPTIONS_DEFAULTS))
-    defaultButton:SetAnchor(TOPLEFT, panelContainer, BOTTOMLEFT, 0, 2)
-    lam.defaultButton = defaultButton
-
-    local applyButton = wm:CreateControlFromVirtual("$(parent)ApplyButton", tlw, "ZO_DialogButton")
-    ZO_KeybindButtonTemplate_Setup(applyButton, "OPTIONS_APPLY_CHANGES", HandleReloadUIPressed, GetString(SI_ADDON_MANAGER_RELOAD))
-    applyButton:SetAnchor(TOPRIGHT, panelContainer, BOTTOMRIGHT, 0, 2)
-    applyButton:SetHidden(true)
-    lam.applyButton = applyButton
-
-    return tlw
-end
-
-
---INITIALIZING
-local safeToInitialize = false
-local hasInitialized = false
-
-local eventHandle = table.concat({MAJOR, MINOR}, "r")
-local function OnLoad(_, addonName)
-    -- wait for the first loaded event
-    em:UnregisterForEvent(eventHandle, EVENT_ADD_ON_LOADED)
-    safeToInitialize = true
-end
-em:RegisterForEvent(eventHandle, EVENT_ADD_ON_LOADED, OnLoad)
-
-local function OnActivated(_, initial)
-    em:UnregisterForEvent(eventHandle, EVENT_PLAYER_ACTIVATED)
-    FlushMessages()
-
-    local reopenPanel = RetrievePanelForReopening()
-    if not initial and reopenPanel then
-        lam:OpenToPanel(reopenPanel)
-    end
-end
-em:RegisterForEvent(eventHandle, EVENT_PLAYER_ACTIVATED, OnActivated)
-
-function CheckSafetyAndInitialize(addonID)
-    if not safeToInitialize then
-        local msg = string.format("The panel with id '%s' was registered before addon loading has completed. This might break the AddOn Settings menu.", addonID)
-        PrintLater(msg)
-    end
-    if not hasInitialized then
-        hasInitialized = true
-    end
-end
-
-
---TODO documentation
-function lam:GetAddonPanelContainer()
-    local fragment = lam:GetAddonSettingsFragment()
-    local window = fragment:GetControl()
-    return window:GetNamedChild("PanelContainer")
-end
-
-
---TODO documentation
-function lam:GetAddonSettingsFragment()
-    assert(hasInitialized or safeToInitialize)
-    if not LAMAddonSettingsFragment then
-        local window = CreateAddonSettingsWindow()
-        LAMAddonSettingsFragment = ZO_FadeSceneFragment:New(window, true, 100)
-        LAMAddonSettingsFragment:RegisterCallback("StateChange", function(oldState, newState)
-            if(newState == SCENE_FRAGMENT_SHOWN) then
-                InitKeybindActions()
-                PushActionLayerByName("OptionsWindow")
-                OpenCurrentPanel()
-            elseif(newState == SCENE_FRAGMENT_HIDDEN) then
-                CloseCurrentPanel()
-                RemoveActionLayerByName("OptionsWindow")
-                ShowReloadDialogIfNeeded()
-            end
-        end)
-        CreateAddonSettingsMenuEntry()
-    end
-    return LAMAddonSettingsFragment
-end
+-- LibAddonMenu-2.0 & its files © Ryan Lakanen (Seerah)         --
+-- Distributed under The Artistic License 2.0 (see LICENSE)     --
+------------------------------------------------------------------
+
+
+--Register LAM with LibStub
+local MAJOR, MINOR = "LibAddonMenu-2.0", 25
+local lam, oldminor = LibStub:NewLibrary(MAJOR, MINOR)
+if not lam then return end --the same or newer version of this lib is already loaded into memory
+
+local messages = {}
+local MESSAGE_PREFIX = "[LAM2] "
+local function PrintLater(msg)
+    if CHAT_SYSTEM.primaryContainer then
+        d(MESSAGE_PREFIX .. msg)
+    else
+        messages[#messages + 1] = msg
+    end
+end
+
+local function FlushMessages()
+    for i = 1, #messages do
+        d(MESSAGE_PREFIX .. messages[i])
+    end
+    messages = {}
+end
+
+if LAMSettingsPanelCreated and not LAMCompatibilityWarning then
+    PrintLater("An old version of LibAddonMenu with compatibility issues was detected. For more information on how to proceed search for LibAddonMenu on esoui.com")
+    LAMCompatibilityWarning = true
+end
+
+--UPVALUES--
+local wm = WINDOW_MANAGER
+local em = EVENT_MANAGER
+local sm = SCENE_MANAGER
+local cm = CALLBACK_MANAGER
+local tconcat = table.concat
+local tinsert = table.insert
+
+local MIN_HEIGHT = 26
+local HALF_WIDTH_LINE_SPACING = 2
+local OPTIONS_CREATION_RUNNING = 1
+local OPTIONS_CREATED = 2
+local LAM_CONFIRM_DIALOG = "LAM_CONFIRM_DIALOG"
+local LAM_DEFAULTS_DIALOG = "LAM_DEFAULTS"
+local LAM_RELOAD_DIALOG = "LAM_RELOAD_DIALOG"
+
+local addonsForList = {}
+local addonToOptionsMap = {}
+local optionsState = {}
+lam.widgets = lam.widgets or {}
+local widgets = lam.widgets
+lam.util = lam.util or {}
+local util = lam.util
+lam.controlsForReload = lam.controlsForReload or {}
+local controlsForReload = lam.controlsForReload
+
+local function GetDefaultValue(default)
+    if type(default) == "function" then
+        return default()
+    end
+    return default
+end
+
+local function GetStringFromValue(value)
+    if type(value) == "function" then
+        return value()
+    elseif type(value) == "number" then
+        return GetString(value)
+    end
+    return value
+end
+
+local function CreateBaseControl(parent, controlData, controlName)
+    local control = wm:CreateControl(controlName or controlData.reference, parent.scroll or parent, CT_CONTROL)
+    control.panel = parent.panel or parent -- if this is in a submenu, panel is the submenu's parent
+    control.data = controlData
+
+    control.isHalfWidth = controlData.width == "half"
+    local width = 510 -- set default width in case a custom parent object is passed
+    if control.panel.GetWidth ~= nil then width = control.panel:GetWidth() - 60 end
+    control:SetWidth(width)
+    return control
+end
+
+local function CreateLabelAndContainerControl(parent, controlData, controlName)
+    local control = CreateBaseControl(parent, controlData, controlName)
+    local width = control:GetWidth()
+
+    local container = wm:CreateControl(nil, control, CT_CONTROL)
+    container:SetDimensions(width / 3, MIN_HEIGHT)
+    control.container = container
+
+    local label = wm:CreateControl(nil, control, CT_LABEL)
+    label:SetFont("ZoFontWinH4")
+    label:SetHeight(MIN_HEIGHT)
+    label:SetWrapMode(TEXT_WRAP_MODE_ELLIPSIS)
+    label:SetText(GetStringFromValue(controlData.name))
+    control.label = label
+
+    if control.isHalfWidth then
+        control:SetDimensions(width / 2, MIN_HEIGHT * 2 + HALF_WIDTH_LINE_SPACING)
+        label:SetAnchor(TOPLEFT, control, TOPLEFT, 0, 0)
+        label:SetAnchor(TOPRIGHT, control, TOPRIGHT, 0, 0)
+        container:SetAnchor(TOPRIGHT, control.label, BOTTOMRIGHT, 0, HALF_WIDTH_LINE_SPACING)
+    else
+        control:SetDimensions(width, MIN_HEIGHT)
+        container:SetAnchor(TOPRIGHT, control, TOPRIGHT, 0, 0)
+        label:SetAnchor(TOPLEFT, control, TOPLEFT, 0, 0)
+        label:SetAnchor(TOPRIGHT, container, TOPLEFT, 5, 0)
+    end
+
+    control.data.tooltipText = GetStringFromValue(control.data.tooltip)
+    control:SetMouseEnabled(true)
+    control:SetHandler("OnMouseEnter", ZO_Options_OnMouseEnter)
+    control:SetHandler("OnMouseExit", ZO_Options_OnMouseExit)
+    return control
+end
+
+local function GetTopPanel(panel)
+    while panel.panel and panel.panel ~= panel do
+        panel = panel.panel
+    end
+    return panel
+end
+
+local function IsSame(objA, objB)
+    if #objA ~= #objB then return false end
+    for i = 1, #objA do
+        if objA[i] ~= objB[i] then return false end
+    end
+    return true
+end
+
+local function RefreshReloadUIButton()
+    lam.requiresReload = false
+
+    for i = 1, #controlsForReload do
+        local reloadControl = controlsForReload[i]
+        if not IsSame(reloadControl.startValue, {reloadControl.data.getFunc()}) then
+            lam.requiresReload = true
+            break
+        end
+    end
+
+    lam.applyButton:SetHidden(not lam.requiresReload)
+end
+
+local function RequestRefreshIfNeeded(control)
+    -- if our parent window wants to refresh controls, then fire the callback
+    local panel = GetTopPanel(control.panel)
+    local panelData = panel.data
+    if panelData.registerForRefresh then
+        cm:FireCallbacks("LAM-RefreshPanel", control)
+    end
+    RefreshReloadUIButton()
+end
+
+local function RegisterForRefreshIfNeeded(control)
+    -- if our parent window wants to refresh controls, then add this to the list
+    local panel = GetTopPanel(control.panel)
+    local panelData = panel.data
+    if panelData.registerForRefresh or panelData.registerForDefaults then
+        tinsert(panel.controlsToRefresh or {}, control) -- prevent errors on custom panels
+    end
+end
+
+local function RegisterForReloadIfNeeded(control)
+    if control.data.requiresReload then
+        tinsert(controlsForReload, control)
+        control.startValue = {control.data.getFunc()}
+    end
+end
+
+local function GetConfirmDialog()
+    if(not ESO_Dialogs[LAM_CONFIRM_DIALOG]) then
+        ESO_Dialogs[LAM_CONFIRM_DIALOG] = {
+            canQueue = true,
+            title = {
+                text = "",
+            },
+            mainText = {
+                text = "",
+            },
+            buttons = {
+                [1] = {
+                    text = SI_DIALOG_CONFIRM,
+                    callback = function(dialog) end,
+                },
+                [2] = {
+                    text = SI_DIALOG_CANCEL,
+                }
+            }
+        }
+    end
+    return ESO_Dialogs[LAM_CONFIRM_DIALOG]
+end
+
+local function ShowConfirmationDialog(title, body, callback)
+    local dialog = GetConfirmDialog()
+    dialog.title.text = title
+    dialog.mainText.text = body
+    dialog.buttons[1].callback = callback
+    ZO_Dialogs_ShowDialog(LAM_CONFIRM_DIALOG)
+end
+
+local function GetDefaultsDialog()
+    if(not ESO_Dialogs[LAM_DEFAULTS_DIALOG]) then
+        ESO_Dialogs[LAM_DEFAULTS_DIALOG] = {
+            canQueue = true,
+            title = {
+                text = SI_INTERFACE_OPTIONS_RESET_TO_DEFAULT_TOOLTIP,
+            },
+            mainText = {
+                text = SI_OPTIONS_RESET_PROMPT,
+            },
+            buttons = {
+                [1] = {
+                    text = SI_OPTIONS_RESET,
+                    callback = function(dialog) end,
+                },
+                [2] = {
+                    text = SI_DIALOG_CANCEL,
+                }
+            }
+        }
+    end
+    return ESO_Dialogs[LAM_DEFAULTS_DIALOG]
+end
+
+local function ShowDefaultsDialog(panel)
+    local dialog = GetDefaultsDialog()
+    dialog.buttons[1].callback = function()
+        panel:ForceDefaults()
+        RefreshReloadUIButton()
+    end
+    ZO_Dialogs_ShowDialog(LAM_DEFAULTS_DIALOG)
+end
+
+local function DiscardChangesOnReloadControls()
+    for i = 1, #controlsForReload do
+        local reloadControl = controlsForReload[i]
+        if not IsSame(reloadControl.startValue, {reloadControl.data.getFunc()}) then
+            reloadControl:UpdateValue(false, unpack(reloadControl.startValue))
+        end
+    end
+    lam.requiresReload = false
+    lam.applyButton:SetHidden(true)
+end
+
+local function StorePanelForReopening()
+    local saveData = ZO_Ingame_SavedVariables["LAM"] or {}
+    saveData.reopenPanel = lam.currentAddonPanel:GetName()
+    ZO_Ingame_SavedVariables["LAM"] = saveData
+end
+
+local function RetrievePanelForReopening()
+    local saveData = ZO_Ingame_SavedVariables["LAM"]
+    if(saveData) then
+        ZO_Ingame_SavedVariables["LAM"] = nil
+        return _G[saveData.reopenPanel]
+    end
+end
+
+local function HandleReloadUIPressed()
+    StorePanelForReopening()
+    ReloadUI()
+end
+
+local function HandleLoadDefaultsPressed()
+    ShowDefaultsDialog(lam.currentAddonPanel)
+end
+
+local function GetReloadDialog()
+    if(not ESO_Dialogs[LAM_RELOAD_DIALOG]) then
+        ESO_Dialogs[LAM_RELOAD_DIALOG] = {
+            canQueue = true,
+            title = {
+                text = util.L["RELOAD_DIALOG_TITLE"],
+            },
+            mainText = {
+                text = util.L["RELOAD_DIALOG_TEXT"],
+            },
+            buttons = {
+                [1] = {
+                    text = util.L["RELOAD_DIALOG_RELOAD_BUTTON"],
+                    callback = function() ReloadUI() end,
+                },
+                [2] = {
+                    text = util.L["RELOAD_DIALOG_DISCARD_BUTTON"],
+                    callback = DiscardChangesOnReloadControls,
+                }
+            },
+            noChoiceCallback = DiscardChangesOnReloadControls,
+        }
+    end
+    return ESO_Dialogs[LAM_CONFIRM_DIALOG]
+end
+
+local function ShowReloadDialogIfNeeded()
+    if lam.requiresReload then
+        local dialog = GetReloadDialog()
+        ZO_Dialogs_ShowDialog(LAM_RELOAD_DIALOG)
+    end
+end
+
+local function UpdateWarning(control)
+    local warning
+    if control.data.warning ~= nil then
+        warning = util.GetStringFromValue(control.data.warning)
+    end
+
+    if control.data.requiresReload then
+        if not warning then
+            warning = string.format("|cff0000%s", util.L["RELOAD_UI_WARNING"])
+        else
+            warning = string.format("%s\n\n|cff0000%s", warning, util.L["RELOAD_UI_WARNING"])
+        end
+    end
+
+    if not warning then
+        control.warning:SetHidden(true)
+    else
+        control.warning.data = {tooltipText = warning}
+        control.warning:SetHidden(false)
+    end
+end
+
+local localization = {
+    en = {
+        PANEL_NAME = "Addons",
+        AUTHOR = string.format("%s: <<X:1>>", GetString(SI_ADDON_MANAGER_AUTHOR)), -- "Author: <<X:1>>"
+        VERSION = "Version: <<X:1>>",
+        WEBSITE = "Visit Website",
+        PANEL_INFO_FONT = "$(CHAT_FONT)|14|soft-shadow-thin",
+        RELOAD_UI_WARNING = "Changes to this setting require an UI reload in order to take effect.",
+        RELOAD_DIALOG_TITLE = "UI Reload required",
+        RELOAD_DIALOG_TEXT = "Some changes require an UI reload in order to take effect. Do you want to reload now or discard the changes?",
+        RELOAD_DIALOG_RELOAD_BUTTON = "Reload",
+        RELOAD_DIALOG_DISCARD_BUTTON = "Discard",
+    },
+    it = { -- provided by JohnnyKing
+        PANEL_NAME = "Addon",
+        VERSION = "Versione: <<X:1>>",
+        WEBSITE = "Visita il Sitoweb",
+        RELOAD_UI_WARNING = "Cambiare questa impostazione richiede un Ricarica UI al fine che faccia effetto.",
+        RELOAD_DIALOG_TITLE = "Ricarica UI richiesto",
+        RELOAD_DIALOG_TEXT = "Alcune modifiche richiedono un Ricarica UI al fine che facciano effetto. Sei sicuro di voler ricaricare ora o di voler annullare le modifiche?",
+        RELOAD_DIALOG_RELOAD_BUTTON = "Ricarica",
+        RELOAD_DIALOG_DISCARD_BUTTON = "Annulla",
+    },
+    fr = { -- provided by Ayantir
+        PANEL_NAME = "Extensions",
+        WEBSITE = "Visiter le site Web",
+        RELOAD_UI_WARNING = "La modification de ce paramètre requiert un rechargement de l'UI pour qu'il soit pris en compte.",
+        RELOAD_DIALOG_TITLE = "Reload UI requis",
+        RELOAD_DIALOG_TEXT = "Certaines modifications requièrent un rechargement de l'UI pour qu'ils soient pris en compte. Souhaitez-vous recharger l'interface maintenant ou annuler les modifications ?",
+        RELOAD_DIALOG_RELOAD_BUTTON = "Recharger",
+        RELOAD_DIALOG_DISCARD_BUTTON = "Annuler",
+    },
+    de = { -- provided by sirinsidiator
+        PANEL_NAME = "Erweiterungen",
+        WEBSITE = "Webseite besuchen",
+        RELOAD_UI_WARNING = "Änderungen an dieser Option werden erst übernommen nachdem die Benutzeroberfläche neu geladen wird.",
+        RELOAD_DIALOG_TITLE = "Neuladen benötigt",
+        RELOAD_DIALOG_TEXT = "Einige Änderungen werden erst übernommen nachdem die Benutzeroberfläche neu geladen wird. Wollt Ihr sie jetzt neu laden oder die Änderungen verwerfen?",
+        RELOAD_DIALOG_RELOAD_BUTTON = "Neu laden",
+        RELOAD_DIALOG_DISCARD_BUTTON = "Verwerfen",
+    },
+    ru = { -- provided by TERAB1T
+        PANEL_NAME = "Дополнения",
+        VERSION = "Версия: <<X:1>>",
+        WEBSITE = "Посетить сайт",
+        PANEL_INFO_FONT = "RuESO/fonts/Univers57.otf|14|soft-shadow-thin",
+        RELOAD_UI_WARNING = "Для применения этой настройки необходима перезагрузка интерфейса.",
+        RELOAD_DIALOG_TITLE = "Необходима перезагрузка интерфейса",
+        RELOAD_DIALOG_TEXT = "Для применения некоторых изменений необходима перезагрузка интерфейса. Перезагрузить интерфейс сейчас или отменить изменения?",
+        RELOAD_DIALOG_RELOAD_BUTTON = "Перезагрузить",
+        RELOAD_DIALOG_DISCARD_BUTTON = "Отменить изменения",
+    },
+    es = { -- provided by Morganlefai, checked by Kwisatz
+        PANEL_NAME = "Configuración",
+        VERSION = "Versión: <<X:1>>",
+        WEBSITE = "Visita la página web",
+        RELOAD_UI_WARNING = "Cambiar este ajuste recargará la interfaz del usuario.",
+        RELOAD_DIALOG_TITLE = "Requiere recargar la interfaz",
+        RELOAD_DIALOG_TEXT = "Algunos cambios requieren recargar la interfaz para poder aplicarse. Quieres aplicar los cambios y recargar la interfaz?",
+        RELOAD_DIALOG_RELOAD_BUTTON = "Recargar",
+        RELOAD_DIALOG_DISCARD_BUTTON = "Cancelar",
+    },
+    jp = { -- provided by k0ta0uchi
+        PANEL_NAME = "アドオン設定",
+        WEBSITE = "ウェブサイトを見る",
+    },
+    zh = { -- provided by bssthu
+        PANEL_NAME = "插件",
+        VERSION = "版本: <<X:1>>",
+        WEBSITE = "访问网站",
+        PANEL_INFO_FONT = "EsoZh/fonts/univers57.otf|14|soft-shadow-thin",
+    },
+    pl = { -- provided by EmiruTegryfon
+        PANEL_NAME = "Dodatki",
+        VERSION = "Wersja: <<X:1>>",
+        WEBSITE = "Odwiedź stronę",
+        RELOAD_UI_WARNING = "Zmiany będą widoczne po ponownym załadowaniu UI.",
+        RELOAD_DIALOG_TITLE = "Wymagane przeładowanie UI",
+        RELOAD_DIALOG_TEXT = "Niektóre zmiany wymagają ponownego załadowania UI. Czy chcesz teraz ponownie załadować, czy porzucić zmiany?",
+        RELOAD_DIALOG_RELOAD_BUTTON = "Przeładuj",
+        RELOAD_DIALOG_DISCARD_BUTTON = "Porzuć",
+    },
+}
+
+util.L = ZO_ShallowTableCopy(localization[GetCVar("Language.2")], localization["en"])
+util.GetTooltipText = GetStringFromValue -- deprecated, use util.GetStringFromValue instead
+util.GetStringFromValue = GetStringFromValue
+util.GetDefaultValue = GetDefaultValue
+util.CreateBaseControl = CreateBaseControl
+util.CreateLabelAndContainerControl = CreateLabelAndContainerControl
+util.RequestRefreshIfNeeded = RequestRefreshIfNeeded
+util.RegisterForRefreshIfNeeded = RegisterForRefreshIfNeeded
+util.RegisterForReloadIfNeeded = RegisterForReloadIfNeeded
+util.GetTopPanel = GetTopPanel
+util.ShowConfirmationDialog = ShowConfirmationDialog
+util.UpdateWarning = UpdateWarning
+
+local ADDON_DATA_TYPE = 1
+local RESELECTING_DURING_REBUILD = true
+local USER_REQUESTED_OPEN = true
+
+
+--INTERNAL FUNCTION
+--scrolls ZO_ScrollList `list` to move the row corresponding to `data`
+-- into view (does nothing if there is no such row in the list)
+--unlike ZO_ScrollList_ScrollDataIntoView, this function accounts for
+-- fading near the list's edges - it avoids the fading area by scrolling
+-- a little further than the ZO function
+local function ScrollDataIntoView(list, data)
+    local targetIndex = data.sortIndex
+    if not targetIndex then return end
+
+    local scrollMin, scrollMax = list.scrollbar:GetMinMax()
+    local scrollTop = list.scrollbar:GetValue()
+    local controlHeight = list.uniformControlHeight or list.controlHeight
+    local targetMin = controlHeight * (targetIndex - 1) - 64
+    -- subtracting 64 ain't arbitrary, it's the maximum fading height
+    -- (libraries/zo_templates/scrolltemplates.lua/UpdateScrollFade)
+
+    if targetMin < scrollTop then
+        ZO_ScrollList_ScrollAbsolute(list, zo_max(targetMin, scrollMin))
+    else
+        local listHeight = ZO_ScrollList_GetHeight(list)
+        local targetMax = controlHeight * targetIndex + 64 - listHeight
+
+        if targetMax > scrollTop then
+            ZO_ScrollList_ScrollAbsolute(list, zo_min(targetMax, scrollMax))
+        end
+    end
+end
+
+
+--INTERNAL FUNCTION
+--constructs a string pattern from the text in `searchEdit` control
+-- * metacharacters are escaped, losing their special meaning
+-- * whitespace matches anything (including empty substring)
+--if there is nothing but whitespace, returns nil
+--otherwise returns a filter function, which takes a `data` table argument
+-- and returns true iff `data.filterText` matches the pattern
+local function GetSearchFilterFunc(searchEdit)
+    local text = searchEdit:GetText():lower()
+    local pattern = text:match("(%S+.-)%s*$")
+
+    if not pattern then -- nothing but whitespace
+        return nil
+    end
+
+    -- escape metacharacters, e.g. "ESO-Datenbank.de" => "ESO%-Datenbank%.de"
+    pattern = pattern:gsub("[-*+?^$().[%]%%]", "%%%0")
+
+    -- replace whitespace with "match shortest anything"
+    pattern = pattern:gsub("%s+", ".-")
+
+    return function(data)
+        return data.filterText:lower():find(pattern) ~= nil
+    end
+end
+
+
+--INTERNAL FUNCTION
+--populates `addonList` with entries from `addonsForList`
+-- addonList = ZO_ScrollList control
+-- filter = [optional] function(data)
+local function PopulateAddonList(addonList, filter)
+    local entryList = ZO_ScrollList_GetDataList(addonList)
+    local numEntries = 0
+    local selectedData = nil
+    local selectionIsFinal = false
+
+    ZO_ScrollList_Clear(addonList)
+
+    for i, data in ipairs(addonsForList) do
+        if not filter or filter(data) then
+            local dataEntry = ZO_ScrollList_CreateDataEntry(ADDON_DATA_TYPE, data)
+            numEntries = numEntries + 1
+            data.sortIndex = numEntries
+            entryList[numEntries] = dataEntry
+            -- select the first panel passing the filter, or the currently
+            -- shown panel, but only if it passes the filter as well
+            if selectedData == nil or data.panel == lam.pendingAddonPanel or data.panel == lam.currentAddonPanel then
+                if not selectionIsFinal then
+                    selectedData = data
+                end
+                if data.panel == lam.pendingAddonPanel then
+                    lam.pendingAddonPanel = nil
+                    selectionIsFinal = true
+                end
+            end
+        else
+            data.sortIndex = nil
+        end
+    end
+
+    ZO_ScrollList_Commit(addonList)
+
+    if selectedData then
+        if selectedData.panel == lam.currentAddonPanel then
+            ZO_ScrollList_SelectData(addonList, selectedData, nil, RESELECTING_DURING_REBUILD)
+        else
+            ZO_ScrollList_SelectData(addonList, selectedData, nil)
+        end
+        ScrollDataIntoView(addonList, selectedData)
+    end
+end
+
+
+--METHOD: REGISTER WIDGET--
+--each widget has its version checked before loading,
+--so we only have the most recent one in memory
+--Usage:
+-- widgetType = "string"; the type of widget being registered
+-- widgetVersion = integer; the widget's version number
+LAMCreateControl = LAMCreateControl or {}
+local lamcc = LAMCreateControl
+
+function lam:RegisterWidget(widgetType, widgetVersion)
+    if widgets[widgetType] and widgets[widgetType] >= widgetVersion then
+        return false
+    else
+        widgets[widgetType] = widgetVersion
+        return true
+    end
+end
+
+-- INTERNAL METHOD: hijacks the handlers for the actions in the OptionsWindow layer if not already done
+local function InitKeybindActions()
+    if not lam.keybindsInitialized then
+        lam.keybindsInitialized = true
+        ZO_PreHook(KEYBOARD_OPTIONS, "ApplySettings", function()
+            if lam.currentPanelOpened then
+                if not lam.applyButton:IsHidden() then
+                    HandleReloadUIPressed()
+                end
+                return true
+            end
+        end)
+        ZO_PreHook("ZO_Dialogs_ShowDialog", function(dialogName)
+            if lam.currentPanelOpened and dialogName == "OPTIONS_RESET_TO_DEFAULTS" then
+                if not lam.defaultButton:IsHidden() then
+                    HandleLoadDefaultsPressed()
+                end
+                return true
+            end
+        end)
+    end
+end
+
+-- INTERNAL METHOD: fires the LAM-PanelOpened callback if not already done
+local function OpenCurrentPanel()
+    if lam.currentAddonPanel and not lam.currentPanelOpened then
+        lam.currentPanelOpened = true
+        lam.defaultButton:SetHidden(not lam.currentAddonPanel.data.registerForDefaults)
+        cm:FireCallbacks("LAM-PanelOpened", lam.currentAddonPanel)
+    end
+end
+
+-- INTERNAL METHOD: fires the LAM-PanelClosed callback if not already done
+local function CloseCurrentPanel()
+    if lam.currentAddonPanel and lam.currentPanelOpened then
+        lam.currentPanelOpened = false
+        cm:FireCallbacks("LAM-PanelClosed", lam.currentAddonPanel)
+    end
+end
+
+--METHOD: OPEN TO ADDON PANEL--
+--opens to a specific addon's option panel
+--Usage:
+-- panel = userdata; the panel returned by the :RegisterOptionsPanel method
+local locSettings = GetString(SI_GAME_MENU_SETTINGS)
+function lam:OpenToPanel(panel)
+
+    -- find and select the panel's row in addon list
+
+    local addonList = lam.addonList
+    local selectedData = nil
+
+    for _, addonData in ipairs(addonsForList) do
+        if addonData.panel == panel then
+            selectedData = addonData
+            ScrollDataIntoView(addonList, selectedData)
+            lam.pendingAddonPanel = addonData.panel
+            break
+        end
+    end
+
+    ZO_ScrollList_SelectData(addonList, selectedData)
+    ZO_ScrollList_RefreshVisible(addonList, selectedData)
+
+    local srchEdit = LAMAddonSettingsWindow:GetNamedChild("SearchFilterEdit")
+    srchEdit:Clear()
+
+    -- note that ZO_ScrollList doesn't require `selectedData` to be actually
+    -- present in the list, and that the list will only be populated once LAM
+    -- "Addon Settings" menu entry is selected for the first time
+
+    local function openAddonSettingsMenu()
+        local gameMenu = ZO_GameMenu_InGame.gameMenu
+        local settingsMenu = gameMenu.headerControls[locSettings]
+
+        if settingsMenu then -- an instance of ZO_TreeNode
+            local children = settingsMenu:GetChildren()
+            for i = 1, (children and #children or 0) do
+                local childNode = children[i]
+                local data = childNode:GetData()
+                if data and data.id == lam.panelId then
+                    -- found LAM "Addon Settings" node, yay!
+                    childNode:GetTree():SelectNode(childNode)
+                    break
+                end
+            end
+        end
+    end
+
+    if sm:GetScene("gameMenuInGame"):GetState() == SCENE_SHOWN then
+        openAddonSettingsMenu()
+    else
+        sm:CallWhen("gameMenuInGame", SCENE_SHOWN, openAddonSettingsMenu)
+        sm:Show("gameMenuInGame")
+    end
+end
+
+local TwinOptionsContainer_Index = 0
+local function TwinOptionsContainer(parent, leftWidget, rightWidget)
+    TwinOptionsContainer_Index = TwinOptionsContainer_Index + 1
+    local cParent = parent.scroll or parent
+    local panel = parent.panel or cParent
+    local container = wm:CreateControl("$(parent)TwinContainer" .. tostring(TwinOptionsContainer_Index),
+        cParent, CT_CONTROL)
+    container:SetResizeToFitDescendents(true)
+    container:SetAnchor(select(2, leftWidget:GetAnchor(0) ))
+
+    leftWidget:ClearAnchors()
+    leftWidget:SetAnchor(TOPLEFT, container, TOPLEFT)
+    rightWidget:SetAnchor(TOPLEFT, leftWidget, TOPRIGHT, 5, 0)
+
+    leftWidget:SetWidth( leftWidget:GetWidth() - 2.5 ) -- fixes bad alignment with 'full' controls
+    rightWidget:SetWidth( rightWidget:GetWidth() - 2.5 )
+
+    leftWidget:SetParent(container)
+    rightWidget:SetParent(container)
+
+    container.data = {type = "container"}
+    container.panel = panel
+    return container
+end
+
+--INTERNAL FUNCTION
+--creates controls when options panel is first shown
+--controls anchoring of these controls in the panel
+local function CreateOptionsControls(panel)
+    local addonID = panel:GetName()
+    if(optionsState[addonID] == OPTIONS_CREATED) then
+        return false
+    elseif(optionsState[addonID] == OPTIONS_CREATION_RUNNING) then
+        return true
+    end
+    optionsState[addonID] = OPTIONS_CREATION_RUNNING
+
+    local function CreationFinished()
+        optionsState[addonID] = OPTIONS_CREATED
+        cm:FireCallbacks("LAM-PanelControlsCreated", panel)
+        OpenCurrentPanel()
+    end
+
+    local optionsTable = addonToOptionsMap[addonID]
+    if optionsTable then
+        local function CreateAndAnchorWidget(parent, widgetData, offsetX, offsetY, anchorTarget, wasHalf)
+            local widget
+            local status, err = pcall(function() widget = LAMCreateControl[widgetData.type](parent, widgetData) end)
+            if not status then
+                return err or true, offsetY, anchorTarget, wasHalf
+            else
+                local isHalf = (widgetData.width == "half")
+                if not anchorTarget then -- the first widget in a panel is just placed in the top left corner
+                    widget:SetAnchor(TOPLEFT)
+                    anchorTarget = widget
+                elseif wasHalf and isHalf then -- when the previous widget was only half width and this one is too, we place it on the right side
+                    widget.lineControl = anchorTarget
+                    isHalf = false
+                    offsetY = 0
+                    anchorTarget = TwinOptionsContainer(parent, anchorTarget, widget)
+                else -- otherwise we just put it below the previous one normally
+                    widget:SetAnchor(TOPLEFT, anchorTarget, BOTTOMLEFT, 0, 15)
+                    offsetY = 0
+                    anchorTarget = widget
+                end
+                return false, offsetY, anchorTarget, isHalf
+            end
+        end
+
+        local THROTTLE_TIMEOUT, THROTTLE_COUNT = 10, 20
+        local fifo = {}
+        local anchorOffset, lastAddedControl, wasHalf
+        local CreateWidgetsInPanel, err
+
+        local function PrepareForNextPanel()
+            anchorOffset, lastAddedControl, wasHalf = 0, nil, false
+        end
+
+        local function SetupCreationCalls(parent, widgetDataTable)
+            fifo[#fifo + 1] = PrepareForNextPanel
+            local count = #widgetDataTable
+            for i = 1, count, THROTTLE_COUNT do
+                fifo[#fifo + 1] = function()
+                    CreateWidgetsInPanel(parent, widgetDataTable, i, zo_min(i + THROTTLE_COUNT - 1, count))
+                end
+            end
+            return count ~= NonContiguousCount(widgetDataTable)
+        end
+
+        CreateWidgetsInPanel = function(parent, widgetDataTable, startIndex, endIndex)
+            for i=startIndex,endIndex do
+                local widgetData = widgetDataTable[i]
+                if not widgetData then
+                    PrintLater("Skipped creation of missing entry in the settings menu of " .. addonID .. ".")
+                else
+                    local widgetType = widgetData.type
+                    local offsetX = 0
+                    local isSubmenu = (widgetType == "submenu")
+                    if isSubmenu then
+                        wasHalf = false
+                        offsetX = 5
+                    end
+
+                    err, anchorOffset, lastAddedControl, wasHalf = CreateAndAnchorWidget(parent, widgetData, offsetX, anchorOffset, lastAddedControl, wasHalf)
+                    if err then
+                        PrintLater(("Could not create %s '%s' of %s."):format(widgetData.type, GetStringFromValue(widgetData.name or "unnamed"), addonID))
+                    end
+
+                    if isSubmenu then
+                        if SetupCreationCalls(lastAddedControl, widgetData.controls) then
+                            PrintLater(("The sub menu '%s' of %s is missing some entries."):format(GetStringFromValue(widgetData.name or "unnamed"), addonID))
+                        end
+                    end
+                end
+            end
+        end
+
+        local function DoCreateSettings()
+            if #fifo > 0 then
+                local nextCall = table.remove(fifo, 1)
+                nextCall()
+                if(nextCall == PrepareForNextPanel) then
+                    DoCreateSettings()
+                else
+                    zo_callLater(DoCreateSettings, THROTTLE_TIMEOUT)
+                end
+            else
+                CreationFinished()
+            end
+        end
+
+        if SetupCreationCalls(panel, optionsTable) then
+            PrintLater(("The settings menu of %s is missing some entries."):format(addonID))
+        end
+        DoCreateSettings()
+    else
+        CreationFinished()
+    end
+
+    return true
+end
+
+--INTERNAL FUNCTION
+--handles switching between panels
+local function ToggleAddonPanels(panel) --called in OnShow of newly shown panel
+    local currentlySelected = lam.currentAddonPanel
+    if currentlySelected and currentlySelected ~= panel then
+        currentlySelected:SetHidden(true)
+        CloseCurrentPanel()
+    end
+    lam.currentAddonPanel = panel
+
+    -- refresh visible rows to reflect panel IsHidden status
+    ZO_ScrollList_RefreshVisible(lam.addonList)
+
+    if not CreateOptionsControls(panel) then
+        OpenCurrentPanel()
+    end
+
+    cm:FireCallbacks("LAM-RefreshPanel", panel)
+end
+
+local CheckSafetyAndInitialize
+
+--METHOD: REGISTER ADDON PANEL
+--registers your addon with LibAddonMenu and creates a panel
+--Usage:
+-- addonID = "string"; unique ID which will be the global name of your panel
+-- panelData = table; data object for your panel - see controls\panel.lua
+function lam:RegisterAddonPanel(addonID, panelData)
+    CheckSafetyAndInitialize(addonID)
+    local container = lam:GetAddonPanelContainer()
+    local panel = lamcc.panel(container, panelData, addonID) --addonID==global name of panel
+    panel:SetHidden(true)
+    panel:SetAnchorFill(container)
+    panel:SetHandler("OnShow", ToggleAddonPanels)
+
+    local function stripMarkup(str)
+        return str:gsub("|[Cc]%x%x%x%x%x%x", ""):gsub("|[Rr]", "")
+    end
+
+    local filterParts = {panelData.name, nil, nil}
+    -- append keywords and author separately, the may be nil
+    filterParts[#filterParts + 1] = panelData.keywords
+    filterParts[#filterParts + 1] = panelData.author
+
+    local addonData = {
+        panel = panel,
+        name = stripMarkup(panelData.name),
+        filterText = stripMarkup(tconcat(filterParts, "\t")):lower(),
+    }
+
+    tinsert(addonsForList, addonData)
+
+    if panelData.slashCommand then
+        SLASH_COMMANDS[panelData.slashCommand] = function()
+            lam:OpenToPanel(panel)
+        end
+    end
+
+    return panel --return for authors creating options manually
+end
+
+
+--METHOD: REGISTER OPTION CONTROLS
+--registers the options you want shown for your addon
+--these are stored in a table where each key-value pair is the order
+--of the options in the panel and the data for that control, respectively
+--see exampleoptions.lua for an example
+--see controls\<widget>.lua for each widget type
+--Usage:
+-- addonID = "string"; the same string passed to :RegisterAddonPanel
+-- optionsTable = table; the table containing all of the options controls and their data
+function lam:RegisterOptionControls(addonID, optionsTable) --optionsTable = {sliderData, buttonData, etc}
+    addonToOptionsMap[addonID] = optionsTable
+end
+
+--INTERNAL FUNCTION
+--creates LAM's Addon Settings entry in ZO_GameMenu
+local function CreateAddonSettingsMenuEntry()
+    local panelData = {
+        id = KEYBOARD_OPTIONS.currentPanelId,
+        name = util.L["PANEL_NAME"],
+    }
+
+    KEYBOARD_OPTIONS.currentPanelId = panelData.id + 1
+    KEYBOARD_OPTIONS.panelNames[panelData.id] = panelData.name
+
+    lam.panelId = panelData.id
+
+    local addonListSorted = false
+
+    function panelData.callback()
+        sm:AddFragment(lam:GetAddonSettingsFragment())
+        KEYBOARD_OPTIONS:ChangePanels(lam.panelId)
+
+        local title = LAMAddonSettingsWindow:GetNamedChild("Title")
+        title:SetText(panelData.name)
+
+        if not addonListSorted and #addonsForList > 0 then
+            local searchEdit = LAMAddonSettingsWindow:GetNamedChild("SearchFilterEdit")
+            --we're about to show our list for the first time - let's sort it
+            table.sort(addonsForList, function(a, b) return a.name < b.name end)
+            PopulateAddonList(lam.addonList, GetSearchFilterFunc(searchEdit))
+            addonListSorted = true
+        end
+    end
+
+    function panelData.unselectedCallback()
+        sm:RemoveFragment(lam:GetAddonSettingsFragment())
+        if SetCameraOptionsPreviewModeEnabled then -- available since API version 100011
+            SetCameraOptionsPreviewModeEnabled(false)
+        end
+    end
+
+    ZO_GameMenu_AddSettingPanel(panelData)
+end
+
+
+--INTERNAL FUNCTION
+--creates the left-hand menu in LAM's window
+local function CreateAddonList(name, parent)
+    local addonList = wm:CreateControlFromVirtual(name, parent, "ZO_ScrollList")
+
+    local function addonListRow_OnMouseDown(control, button)
+        if button == 1 then
+            local data = ZO_ScrollList_GetData(control)
+            ZO_ScrollList_SelectData(addonList, data, control)
+        end
+    end
+
+    local function addonListRow_OnMouseEnter(control)
+        ZO_ScrollList_MouseEnter(addonList, control)
+    end
+
+    local function addonListRow_OnMouseExit(control)
+        ZO_ScrollList_MouseExit(addonList, control)
+    end
+
+    local function addonListRow_Select(previouslySelectedData, selectedData, reselectingDuringRebuild)
+        if not reselectingDuringRebuild then
+            if previouslySelectedData then
+                previouslySelectedData.panel:SetHidden(true)
+            end
+            if selectedData then
+                selectedData.panel:SetHidden(false)
+                PlaySound(SOUNDS.MENU_SUBCATEGORY_SELECTION)
+            end
+        end
+    end
+
+    local function addonListRow_Setup(control, data)
+        control:SetText(data.name)
+        control:SetSelected(not data.panel:IsHidden())
+    end
+
+    ZO_ScrollList_AddDataType(addonList, ADDON_DATA_TYPE, "ZO_SelectableLabel", 28, addonListRow_Setup)
+    -- I don't know how to make highlights clear properly; they often
+    -- get stuck and after a while the list is full of highlighted rows
+    --ZO_ScrollList_EnableHighlight(addonList, "ZO_ThinListHighlight")
+    ZO_ScrollList_EnableSelection(addonList, "ZO_ThinListHighlight", addonListRow_Select)
+
+    local addonDataType = ZO_ScrollList_GetDataTypeTable(addonList, ADDON_DATA_TYPE)
+    local addonListRow_CreateRaw = addonDataType.pool.m_Factory
+
+    local function addonListRow_Create(pool)
+        local control = addonListRow_CreateRaw(pool)
+        control:SetHandler("OnMouseDown", addonListRow_OnMouseDown)
+        --control:SetHandler("OnMouseEnter", addonListRow_OnMouseEnter)
+        --control:SetHandler("OnMouseExit", addonListRow_OnMouseExit)
+        control:SetHeight(28)
+        control:SetFont("ZoFontHeader")
+        control:SetHorizontalAlignment(TEXT_ALIGN_LEFT)
+        control:SetVerticalAlignment(TEXT_ALIGN_CENTER)
+        control:SetWrapMode(TEXT_WRAP_MODE_ELLIPSIS)
+        return control
+    end
+
+    addonDataType.pool.m_Factory = addonListRow_Create
+
+    return addonList
+end
+
+
+--INTERNAL FUNCTION
+local function CreateSearchFilterBox(name, parent)
+    local boxControl = wm:CreateControl(name, parent, CT_CONTROL)
+
+    local srchButton =  wm:CreateControl("$(parent)Button", boxControl, CT_BUTTON)
+    srchButton:SetDimensions(32, 32)
+    srchButton:SetAnchor(LEFT, nil, LEFT, 2, 0)
+    srchButton:SetNormalTexture("EsoUI/Art/LFG/LFG_tabIcon_groupTools_up.dds")
+    srchButton:SetPressedTexture("EsoUI/Art/LFG/LFG_tabIcon_groupTools_down.dds")
+    srchButton:SetMouseOverTexture("EsoUI/Art/LFG/LFG_tabIcon_groupTools_over.dds")
+
+    local srchEdit = wm:CreateControlFromVirtual("$(parent)Edit", boxControl, "ZO_DefaultEdit")
+    srchEdit:SetAnchor(LEFT, srchButton, RIGHT, 4, 1)
+    srchEdit:SetAnchor(RIGHT, nil, RIGHT, -4, 1)
+    srchEdit:SetColor(ZO_NORMAL_TEXT:UnpackRGBA())
+
+    local srchBg = wm:CreateControl("$(parent)Bg", boxControl, CT_BACKDROP)
+    srchBg:SetAnchorFill()
+    srchBg:SetAlpha(0)
+    srchBg:SetCenterColor(0, 0, 0, 0.5)
+    srchBg:SetEdgeColor(ZO_DISABLED_TEXT:UnpackRGBA())
+    srchBg:SetEdgeTexture("", 1, 1, 0, 0)
+
+    -- search backdrop should appear whenever you hover over either
+    -- the magnifying glass button or the edit field (which is only
+    -- visible when it contains some text), and also while the edit
+    -- field has keyboard focus
+
+    local srchActive = false
+    local srchHover = false
+
+    local function srchBgUpdateAlpha()
+        if srchActive or srchEdit:HasFocus() then
+            srchBg:SetAlpha(srchHover and 0.8 or 0.6)
+        else
+            srchBg:SetAlpha(srchHover and 0.6 or 0.0)
+        end
+    end
+
+    local function srchMouseEnter(control)
+        srchHover = true
+        srchBgUpdateAlpha()
+    end
+
+    local function srchMouseExit(control)
+        srchHover = false
+        srchBgUpdateAlpha()
+    end
+
+    boxControl:SetMouseEnabled(true)
+    boxControl:SetHitInsets(1, 1, -1, -1)
+    boxControl:SetHandler("OnMouseEnter", srchMouseEnter)
+    boxControl:SetHandler("OnMouseExit", srchMouseExit)
+
+    srchButton:SetHandler("OnMouseEnter", srchMouseEnter)
+    srchButton:SetHandler("OnMouseExit", srchMouseExit)
+
+    local focusLostTime = 0
+
+    srchButton:SetHandler("OnClicked", function(self)
+        srchEdit:Clear()
+        if GetFrameTimeMilliseconds() - focusLostTime < 100 then
+            -- re-focus the edit box if it lost focus due to this
+            -- button click (note that this handler may run a few
+            -- frames later)
+            srchEdit:TakeFocus()
+        end
+    end)
+
+    srchEdit:SetHandler("OnMouseEnter", srchMouseEnter)
+    srchEdit:SetHandler("OnMouseExit", srchMouseExit)
+    srchEdit:SetHandler("OnFocusGained", srchBgUpdateAlpha)
+
+    srchEdit:SetHandler("OnFocusLost", function()
+        focusLostTime = GetFrameTimeMilliseconds()
+        srchBgUpdateAlpha()
+    end)
+
+    srchEdit:SetHandler("OnEscape", function(self)
+        self:Clear()
+        self:LoseFocus()
+    end)
+
+    srchEdit:SetHandler("OnTextChanged", function(self)
+        local filterFunc = GetSearchFilterFunc(self)
+        if filterFunc then
+            srchActive = true
+            srchBg:SetEdgeColor(ZO_SECOND_CONTRAST_TEXT:UnpackRGBA())
+            srchButton:SetState(BSTATE_PRESSED)
+        else
+            srchActive = false
+            srchBg:SetEdgeColor(ZO_DISABLED_TEXT:UnpackRGBA())
+            srchButton:SetState(BSTATE_NORMAL)
+        end
+        srchBgUpdateAlpha()
+        PopulateAddonList(lam.addonList, filterFunc)
+        PlaySound(SOUNDS.SPINNER_DOWN)
+    end)
+
+    return boxControl
+end
+
+
+--INTERNAL FUNCTION
+--creates LAM's Addon Settings top-level window
+local function CreateAddonSettingsWindow()
+    local tlw = wm:CreateTopLevelWindow("LAMAddonSettingsWindow")
+    tlw:SetHidden(true)
+    tlw:SetDimensions(1010, 914) -- same height as ZO_OptionsWindow
+
+    ZO_ReanchorControlForLeftSidePanel(tlw)
+
+    -- create black background for the window (mimic ZO_RightFootPrintBackground)
+
+    local bgLeft = wm:CreateControl("$(parent)BackgroundLeft", tlw, CT_TEXTURE)
+    bgLeft:SetTexture("EsoUI/Art/Miscellaneous/centerscreen_left.dds")
+    bgLeft:SetDimensions(1024, 1024)
+    bgLeft:SetAnchor(TOPLEFT, nil, TOPLEFT)
+    bgLeft:SetDrawLayer(DL_BACKGROUND)
+    bgLeft:SetExcludeFromResizeToFitExtents(true)
+
+    local bgRight = wm:CreateControl("$(parent)BackgroundRight", tlw, CT_TEXTURE)
+    bgRight:SetTexture("EsoUI/Art/Miscellaneous/centerscreen_right.dds")
+    bgRight:SetDimensions(64, 1024)
+    bgRight:SetAnchor(TOPLEFT, bgLeft, TOPRIGHT)
+    bgRight:SetDrawLayer(DL_BACKGROUND)
+    bgRight:SetExcludeFromResizeToFitExtents(true)
+
+    -- create gray background for addon list (mimic ZO_TreeUnderlay)
+
+    local underlayLeft = wm:CreateControl("$(parent)UnderlayLeft", tlw, CT_TEXTURE)
+    underlayLeft:SetTexture("EsoUI/Art/Miscellaneous/centerscreen_indexArea_left.dds")
+    underlayLeft:SetDimensions(256, 1024)
+    underlayLeft:SetAnchor(TOPLEFT, bgLeft, TOPLEFT)
+    underlayLeft:SetDrawLayer(DL_BACKGROUND)
+    underlayLeft:SetExcludeFromResizeToFitExtents(true)
+
+    local underlayRight = wm:CreateControl("$(parent)UnderlayRight", tlw, CT_TEXTURE)
+    underlayRight:SetTexture("EsoUI/Art/Miscellaneous/centerscreen_indexArea_right.dds")
+    underlayRight:SetDimensions(128, 1024)
+    underlayRight:SetAnchor(TOPLEFT, underlayLeft, TOPRIGHT)
+    underlayRight:SetDrawLayer(DL_BACKGROUND)
+    underlayRight:SetExcludeFromResizeToFitExtents(true)
+
+    -- create title bar (mimic ZO_OptionsWindow)
+
+    local title = wm:CreateControl("$(parent)Title", tlw, CT_LABEL)
+    title:SetAnchor(TOPLEFT, nil, TOPLEFT, 65, 70)
+    title:SetFont("ZoFontWinH1")
+    title:SetModifyTextType(MODIFY_TEXT_TYPE_UPPERCASE)
+
+    local divider = wm:CreateControlFromVirtual("$(parent)Divider", tlw, "ZO_Options_Divider")
+    divider:SetAnchor(TOPLEFT, nil, TOPLEFT, 65, 108)
+
+    -- create search filter box
+
+    local srchBox = CreateSearchFilterBox("$(parent)SearchFilter", tlw)
+    srchBox:SetAnchor(TOPLEFT, nil, TOPLEFT, 63, 120)
+    srchBox:SetDimensions(260, 30)
+
+    -- create scrollable addon list
+
+    local addonList = CreateAddonList("$(parent)AddonList", tlw)
+    addonList:SetAnchor(TOPLEFT, nil, TOPLEFT, 65, 160)
+    addonList:SetDimensions(285, 665)
+
+    lam.addonList = addonList -- for easy access from elsewhere
+
+    -- create container for option panels
+
+    local panelContainer = wm:CreateControl("$(parent)PanelContainer", tlw, CT_CONTROL)
+    panelContainer:SetAnchor(TOPLEFT, nil, TOPLEFT, 365, 120)
+    panelContainer:SetDimensions(645, 675)
+
+    local defaultButton = wm:CreateControlFromVirtual("$(parent)ResetToDefaultButton", tlw, "ZO_DialogButton")
+    ZO_KeybindButtonTemplate_Setup(defaultButton, "OPTIONS_LOAD_DEFAULTS", HandleLoadDefaultsPressed, GetString(SI_OPTIONS_DEFAULTS))
+    defaultButton:SetAnchor(TOPLEFT, panelContainer, BOTTOMLEFT, 0, 2)
+    lam.defaultButton = defaultButton
+
+    local applyButton = wm:CreateControlFromVirtual("$(parent)ApplyButton", tlw, "ZO_DialogButton")
+    ZO_KeybindButtonTemplate_Setup(applyButton, "OPTIONS_APPLY_CHANGES", HandleReloadUIPressed, GetString(SI_ADDON_MANAGER_RELOAD))
+    applyButton:SetAnchor(TOPRIGHT, panelContainer, BOTTOMRIGHT, 0, 2)
+    applyButton:SetHidden(true)
+    lam.applyButton = applyButton
+
+    return tlw
+end
+
+
+--INITIALIZING
+local safeToInitialize = false
+local hasInitialized = false
+
+local eventHandle = table.concat({MAJOR, MINOR}, "r")
+local function OnLoad(_, addonName)
+    -- wait for the first loaded event
+    em:UnregisterForEvent(eventHandle, EVENT_ADD_ON_LOADED)
+    safeToInitialize = true
+end
+em:RegisterForEvent(eventHandle, EVENT_ADD_ON_LOADED, OnLoad)
+
+local function OnActivated(_, initial)
+    em:UnregisterForEvent(eventHandle, EVENT_PLAYER_ACTIVATED)
+    FlushMessages()
+
+    local reopenPanel = RetrievePanelForReopening()
+    if not initial and reopenPanel then
+        lam:OpenToPanel(reopenPanel)
+    end
+end
+em:RegisterForEvent(eventHandle, EVENT_PLAYER_ACTIVATED, OnActivated)
+
+function CheckSafetyAndInitialize(addonID)
+    if not safeToInitialize then
+        local msg = string.format("The panel with id '%s' was registered before addon loading has completed. This might break the AddOn Settings menu.", addonID)
+        PrintLater(msg)
+    end
+    if not hasInitialized then
+        hasInitialized = true
+    end
+end
+
+
+--TODO documentation
+function lam:GetAddonPanelContainer()
+    local fragment = lam:GetAddonSettingsFragment()
+    local window = fragment:GetControl()
+    return window:GetNamedChild("PanelContainer")
+end
+
+
+--TODO documentation
+function lam:GetAddonSettingsFragment()
+    assert(hasInitialized or safeToInitialize)
+    if not LAMAddonSettingsFragment then
+        local window = CreateAddonSettingsWindow()
+        LAMAddonSettingsFragment = ZO_FadeSceneFragment:New(window, true, 100)
+        LAMAddonSettingsFragment:RegisterCallback("StateChange", function(oldState, newState)
+            if(newState == SCENE_FRAGMENT_SHOWN) then
+                InitKeybindActions()
+                PushActionLayerByName("OptionsWindow")
+                OpenCurrentPanel()
+            elseif(newState == SCENE_FRAGMENT_HIDDEN) then
+                CloseCurrentPanel()
+                RemoveActionLayerByName("OptionsWindow")
+                ShowReloadDialogIfNeeded()
+            end
+        end)
+        CreateAddonSettingsMenuEntry()
+    end
+    return LAMAddonSettingsFragment
+end
diff --git a/TaosGroupTools/libs/LibAddonMenu-2.0/controls/button.lua b/TaosGroupTools/libs/LibAddonMenu-2.0/controls/button.lua
index 82b5032..be55dfc 100644
--- a/TaosGroupTools/libs/LibAddonMenu-2.0/controls/button.lua
+++ b/TaosGroupTools/libs/LibAddonMenu-2.0/controls/button.lua
@@ -1,91 +1,91 @@
---[[buttonData = {
-    type = "button",
-    name = "My Button", -- string id or function returning a string
-    func = function() end,
-    tooltip = "Button's tooltip text.", -- string id or function returning a string (optional)
-    width = "full", --or "half" (optional)
-    disabled = function() return db.someBooleanSetting end, --or boolean (optional)
-    icon = "icon\\path.dds", --(optional)
-    isDangerous = false, -- boolean, if set to true, the button text will be red and a confirmation dialog with the button label and warning text will show on click before the callback is executed (optional)
-    warning = "Will need to reload the UI.", --(optional)
-    reference = "MyAddonButton", -- unique global reference to control (optional)
-} ]]
-
-local widgetVersion = 11
-local LAM = LibStub("LibAddonMenu-2.0")
-if not LAM:RegisterWidget("button", widgetVersion) then return end
-
-local wm = WINDOW_MANAGER
-
-local function UpdateDisabled(control)
-    local disable = control.data.disabled
-    if type(disable) == "function" then
-        disable = disable()
-    end
-    control.button:SetEnabled(not disable)
-end
-
---controlName is optional
-local MIN_HEIGHT = 28 -- default_button height
-local HALF_WIDTH_LINE_SPACING = 2
-function LAMCreateControl.button(parent, buttonData, controlName)
-    local control = LAM.util.CreateBaseControl(parent, buttonData, controlName)
-    control:SetMouseEnabled(true)
-
-    local width = control:GetWidth()
-    if control.isHalfWidth then
-        control:SetDimensions(width / 2, MIN_HEIGHT * 2 + HALF_WIDTH_LINE_SPACING)
-    else
-        control:SetDimensions(width, MIN_HEIGHT)
-    end
-
-    if buttonData.icon then
-        control.button = wm:CreateControl(nil, control, CT_BUTTON)
-        control.button:SetDimensions(26, 26)
-        control.button:SetNormalTexture(buttonData.icon)
-        control.button:SetPressedOffset(2, 2)
-    else
-        --control.button = wm:CreateControlFromVirtual(controlName.."Button", control, "ZO_DefaultButton")
-        control.button = wm:CreateControlFromVirtual(nil, control, "ZO_DefaultButton")
-        control.button:SetWidth(width / 3)
-        control.button:SetText(LAM.util.GetStringFromValue(buttonData.name))
-        if buttonData.isDangerous then control.button:SetNormalFontColor(ZO_ERROR_COLOR:UnpackRGBA()) end
-    end
-    local button = control.button
-    button:SetAnchor(control.isHalfWidth and CENTER or RIGHT)
-    button:SetClickSound("Click")
-    button.data = {tooltipText = LAM.util.GetStringFromValue(buttonData.tooltip)}
-    button:SetHandler("OnMouseEnter", ZO_Options_OnMouseEnter)
-    button:SetHandler("OnMouseExit", ZO_Options_OnMouseExit)
-    button:SetHandler("OnClicked", function(...)
-        local args = {...}
-        local function callback()
-            buttonData.func(unpack(args))
-            LAM.util.RequestRefreshIfNeeded(control)
-        end
-
-        if(buttonData.isDangerous) then
-            local title = LAM.util.GetStringFromValue(buttonData.name)
-            local body = LAM.util.GetStringFromValue(buttonData.warning)
-            LAM.util.ShowConfirmationDialog(title, body, callback)
-        else
-            callback()
-        end
-    end)
-
-    if buttonData.warning ~= nil then
-        control.warning = wm:CreateControlFromVirtual(nil, control, "ZO_Options_WarningIcon")
-        control.warning:SetAnchor(RIGHT, button, LEFT, -5, 0)
-        control.UpdateWarning = LAM.util.UpdateWarning
-        control:UpdateWarning()
-    end
-
-    if buttonData.disabled ~= nil then
-        control.UpdateDisabled = UpdateDisabled
-        control:UpdateDisabled()
-    end
-
-    LAM.util.RegisterForRefreshIfNeeded(control)
-
-    return control
-end
+--[[buttonData = {
+    type = "button",
+    name = "My Button", -- string id or function returning a string
+    func = function() end,
+    tooltip = "Button's tooltip text.", -- string id or function returning a string (optional)
+    width = "full", --or "half" (optional)
+    disabled = function() return db.someBooleanSetting end, --or boolean (optional)
+    icon = "icon\\path.dds", --(optional)
+    isDangerous = false, -- boolean, if set to true, the button text will be red and a confirmation dialog with the button label and warning text will show on click before the callback is executed (optional)
+    warning = "Will need to reload the UI.", --(optional)
+    reference = "MyAddonButton", -- unique global reference to control (optional)
+} ]]
+
+local widgetVersion = 11
+local LAM = LibStub("LibAddonMenu-2.0")
+if not LAM:RegisterWidget("button", widgetVersion) then return end
+
+local wm = WINDOW_MANAGER
+
+local function UpdateDisabled(control)
+    local disable = control.data.disabled
+    if type(disable) == "function" then
+        disable = disable()
+    end
+    control.button:SetEnabled(not disable)
+end
+
+--controlName is optional
+local MIN_HEIGHT = 28 -- default_button height
+local HALF_WIDTH_LINE_SPACING = 2
+function LAMCreateControl.button(parent, buttonData, controlName)
+    local control = LAM.util.CreateBaseControl(parent, buttonData, controlName)
+    control:SetMouseEnabled(true)
+
+    local width = control:GetWidth()
+    if control.isHalfWidth then
+        control:SetDimensions(width / 2, MIN_HEIGHT * 2 + HALF_WIDTH_LINE_SPACING)
+    else
+        control:SetDimensions(width, MIN_HEIGHT)
+    end
+
+    if buttonData.icon then
+        control.button = wm:CreateControl(nil, control, CT_BUTTON)
+        control.button:SetDimensions(26, 26)
+        control.button:SetNormalTexture(buttonData.icon)
+        control.button:SetPressedOffset(2, 2)
+    else
+        --control.button = wm:CreateControlFromVirtual(controlName.."Button", control, "ZO_DefaultButton")
+        control.button = wm:CreateControlFromVirtual(nil, control, "ZO_DefaultButton")
+        control.button:SetWidth(width / 3)
+        control.button:SetText(LAM.util.GetStringFromValue(buttonData.name))
+        if buttonData.isDangerous then control.button:SetNormalFontColor(ZO_ERROR_COLOR:UnpackRGBA()) end
+    end
+    local button = control.button
+    button:SetAnchor(control.isHalfWidth and CENTER or RIGHT)
+    button:SetClickSound("Click")
+    button.data = {tooltipText = LAM.util.GetStringFromValue(buttonData.tooltip)}
+    button:SetHandler("OnMouseEnter", ZO_Options_OnMouseEnter)
+    button:SetHandler("OnMouseExit", ZO_Options_OnMouseExit)
+    button:SetHandler("OnClicked", function(...)
+        local args = {...}
+        local function callback()
+            buttonData.func(unpack(args))
+            LAM.util.RequestRefreshIfNeeded(control)
+        end
+
+        if(buttonData.isDangerous) then
+            local title = LAM.util.GetStringFromValue(buttonData.name)
+            local body = LAM.util.GetStringFromValue(buttonData.warning)
+            LAM.util.ShowConfirmationDialog(title, body, callback)
+        else
+            callback()
+        end
+    end)
+
+    if buttonData.warning ~= nil then
+        control.warning = wm:CreateControlFromVirtual(nil, control, "ZO_Options_WarningIcon")
+        control.warning:SetAnchor(RIGHT, button, LEFT, -5, 0)
+        control.UpdateWarning = LAM.util.UpdateWarning
+        control:UpdateWarning()
+    end
+
+    if buttonData.disabled ~= nil then
+        control.UpdateDisabled = UpdateDisabled
+        control:UpdateDisabled()
+    end
+
+    LAM.util.RegisterForRefreshIfNeeded(control)
+
+    return control
+end
diff --git a/TaosGroupTools/libs/LibAddonMenu-2.0/controls/checkbox.lua b/TaosGroupTools/libs/LibAddonMenu-2.0/controls/checkbox.lua
index 6696dd7..84710de 100644
--- a/TaosGroupTools/libs/LibAddonMenu-2.0/controls/checkbox.lua
+++ b/TaosGroupTools/libs/LibAddonMenu-2.0/controls/checkbox.lua
@@ -1,142 +1,142 @@
---[[checkboxData = {
-    type = "checkbox",
-    name = "My Checkbox", -- or string id or function returning a string
-    getFunc = function() return db.var end,
-    setFunc = function(value) db.var = value doStuff() end,
-    tooltip = "Checkbox's tooltip text.", -- or string id or function returning a string (optional)
-    width = "full", -- or "half" (optional)
-    disabled = function() return db.someBooleanSetting end, --or boolean (optional)
-    warning = "May cause permanent awesomeness.", -- or string id or function returning a string (optional)
-    requiresReload = false, -- boolean, if set to true, the warning text will contain a notice that changes are only applied after an UI reload and any change to the value will make the "Apply Settings" button appear on the panel which will reload the UI when pressed (optional)
-    default = defaults.var, -- a boolean or function that returns a boolean (optional)
-    reference = "MyAddonCheckbox", -- unique global reference to control (optional)
-} ]]
-
-
-local widgetVersion = 14
-local LAM = LibStub("LibAddonMenu-2.0")
-if not LAM:RegisterWidget("checkbox", widgetVersion) then return end
-
-local wm = WINDOW_MANAGER
-
---label
-local enabledColor = ZO_DEFAULT_ENABLED_COLOR
-local enabledHLcolor = ZO_HIGHLIGHT_TEXT
-local disabledColor = ZO_DEFAULT_DISABLED_COLOR
-local disabledHLcolor = ZO_DEFAULT_DISABLED_MOUSEOVER_COLOR
---checkbox
-local checkboxColor = ZO_NORMAL_TEXT
-local checkboxHLcolor = ZO_HIGHLIGHT_TEXT
-
-
-local function UpdateDisabled(control)
-    local disable
-    if type(control.data.disabled) == "function" then
-        disable = control.data.disabled()
-    else
-        disable = control.data.disabled
-    end
-
-    control.label:SetColor((disable and ZO_DEFAULT_DISABLED_COLOR or control.value and ZO_DEFAULT_ENABLED_COLOR or ZO_DEFAULT_DISABLED_COLOR):UnpackRGBA())
-    control.checkbox:SetColor((disable and ZO_DEFAULT_DISABLED_COLOR or ZO_NORMAL_TEXT):UnpackRGBA())
-    --control:SetMouseEnabled(not disable)
-    --control:SetMouseEnabled(true)
-
-    control.isDisabled = disable
-end
-
-local function ToggleCheckbox(control)
-    if control.value then
-        control.label:SetColor(ZO_DEFAULT_ENABLED_COLOR:UnpackRGBA())
-        control.checkbox:SetText(control.checkedText)
-    else
-        control.label:SetColor(ZO_DEFAULT_DISABLED_COLOR:UnpackRGBA())
-        control.checkbox:SetText(control.uncheckedText)
-    end
-end
-
-local function UpdateValue(control, forceDefault, value)
-    if forceDefault then --if we are forcing defaults
-        value = LAM.util.GetDefaultValue(control.data.default)
-        control.data.setFunc(value)
-    elseif value ~= nil then --our value could be false
-        control.data.setFunc(value)
-        --after setting this value, let's refresh the others to see if any should be disabled or have their settings changed
-        LAM.util.RequestRefreshIfNeeded(control)
-    else
-        value = control.data.getFunc()
-    end
-    control.value = value
-
-    ToggleCheckbox(control)
-end
-
-local function OnMouseEnter(control)
-    ZO_Options_OnMouseEnter(control)
-
-    if control.isDisabled then return end
-
-    local label = control.label
-    if control.value then
-        label:SetColor(ZO_HIGHLIGHT_TEXT:UnpackRGBA())
-    else
-        label:SetColor(ZO_DEFAULT_DISABLED_MOUSEOVER_COLOR:UnpackRGBA())
-    end
-    control.checkbox:SetColor(ZO_HIGHLIGHT_TEXT:UnpackRGBA())
-end
-
-local function OnMouseExit(control)
-    ZO_Options_OnMouseExit(control)
-
-    if control.isDisabled then return end
-
-    local label = control.label
-    if control.value then
-        label:SetColor(ZO_DEFAULT_ENABLED_COLOR:UnpackRGBA())
-    else
-        label:SetColor(ZO_DEFAULT_DISABLED_COLOR:UnpackRGBA())
-    end
-    control.checkbox:SetColor(ZO_NORMAL_TEXT:UnpackRGBA())
-end
-
---controlName is optional
-function LAMCreateControl.checkbox(parent, checkboxData, controlName)
-    local control = LAM.util.CreateLabelAndContainerControl(parent, checkboxData, controlName)
-    control:SetHandler("OnMouseEnter", OnMouseEnter)
-    control:SetHandler("OnMouseExit", OnMouseExit)
-    control:SetHandler("OnMouseUp", function(control)
-        if control.isDisabled then return end
-        PlaySound(SOUNDS.DEFAULT_CLICK)
-        control.value = not control.value
-        control:UpdateValue(false, control.value)
-    end)
-
-    control.checkbox = wm:CreateControl(nil, control.container, CT_LABEL)
-    local checkbox = control.checkbox
-    checkbox:SetAnchor(LEFT, control.container, LEFT, 0, 0)
-    checkbox:SetFont("ZoFontGameBold")
-    checkbox:SetColor(ZO_NORMAL_TEXT:UnpackRGBA())
-    control.checkedText = GetString(SI_CHECK_BUTTON_ON):upper()
-    control.uncheckedText = GetString(SI_CHECK_BUTTON_OFF):upper()
-
-    if checkboxData.warning ~= nil or checkboxData.requiresReload then
-        control.warning = wm:CreateControlFromVirtual(nil, control, "ZO_Options_WarningIcon")
-        control.warning:SetAnchor(RIGHT, checkbox, LEFT, -5, 0)
-        control.UpdateWarning = LAM.util.UpdateWarning
-        control:UpdateWarning()
-    end
-
-    control.data.tooltipText = LAM.util.GetStringFromValue(checkboxData.tooltip)
-
-    control.UpdateValue = UpdateValue
-    control:UpdateValue()
-    if checkboxData.disabled ~= nil then
-        control.UpdateDisabled = UpdateDisabled
-        control:UpdateDisabled()
-    end
-
-    LAM.util.RegisterForRefreshIfNeeded(control)
-    LAM.util.RegisterForReloadIfNeeded(control)
-
-    return control
-end
+--[[checkboxData = {
+    type = "checkbox",
+    name = "My Checkbox", -- or string id or function returning a string
+    getFunc = function() return db.var end,
+    setFunc = function(value) db.var = value doStuff() end,
+    tooltip = "Checkbox's tooltip text.", -- or string id or function returning a string (optional)
+    width = "full", -- or "half" (optional)
+    disabled = function() return db.someBooleanSetting end, --or boolean (optional)
+    warning = "May cause permanent awesomeness.", -- or string id or function returning a string (optional)
+    requiresReload = false, -- boolean, if set to true, the warning text will contain a notice that changes are only applied after an UI reload and any change to the value will make the "Apply Settings" button appear on the panel which will reload the UI when pressed (optional)
+    default = defaults.var, -- a boolean or function that returns a boolean (optional)
+    reference = "MyAddonCheckbox", -- unique global reference to control (optional)
+} ]]
+
+
+local widgetVersion = 14
+local LAM = LibStub("LibAddonMenu-2.0")
+if not LAM:RegisterWidget("checkbox", widgetVersion) then return end
+
+local wm = WINDOW_MANAGER
+
+--label
+local enabledColor = ZO_DEFAULT_ENABLED_COLOR
+local enabledHLcolor = ZO_HIGHLIGHT_TEXT
+local disabledColor = ZO_DEFAULT_DISABLED_COLOR
+local disabledHLcolor = ZO_DEFAULT_DISABLED_MOUSEOVER_COLOR
+--checkbox
+local checkboxColor = ZO_NORMAL_TEXT
+local checkboxHLcolor = ZO_HIGHLIGHT_TEXT
+
+
+local function UpdateDisabled(control)
+    local disable
+    if type(control.data.disabled) == "function" then
+        disable = control.data.disabled()
+    else
+        disable = control.data.disabled
+    end
+
+    control.label:SetColor((disable and ZO_DEFAULT_DISABLED_COLOR or control.value and ZO_DEFAULT_ENABLED_COLOR or ZO_DEFAULT_DISABLED_COLOR):UnpackRGBA())
+    control.checkbox:SetColor((disable and ZO_DEFAULT_DISABLED_COLOR or ZO_NORMAL_TEXT):UnpackRGBA())
+    --control:SetMouseEnabled(not disable)
+    --control:SetMouseEnabled(true)
+
+    control.isDisabled = disable
+end
+
+local function ToggleCheckbox(control)
+    if control.value then
+        control.label:SetColor(ZO_DEFAULT_ENABLED_COLOR:UnpackRGBA())
+        control.checkbox:SetText(control.checkedText)
+    else
+        control.label:SetColor(ZO_DEFAULT_DISABLED_COLOR:UnpackRGBA())
+        control.checkbox:SetText(control.uncheckedText)
+    end
+end
+
+local function UpdateValue(control, forceDefault, value)
+    if forceDefault then --if we are forcing defaults
+        value = LAM.util.GetDefaultValue(control.data.default)
+        control.data.setFunc(value)
+    elseif value ~= nil then --our value could be false
+        control.data.setFunc(value)
+        --after setting this value, let's refresh the others to see if any should be disabled or have their settings changed
+        LAM.util.RequestRefreshIfNeeded(control)
+    else
+        value = control.data.getFunc()
+    end
+    control.value = value
+
+    ToggleCheckbox(control)
+end
+
+local function OnMouseEnter(control)
+    ZO_Options_OnMouseEnter(control)
+
+    if control.isDisabled then return end
+
+    local label = control.label
+    if control.value then
+        label:SetColor(ZO_HIGHLIGHT_TEXT:UnpackRGBA())
+    else
+        label:SetColor(ZO_DEFAULT_DISABLED_MOUSEOVER_COLOR:UnpackRGBA())
+    end
+    control.checkbox:SetColor(ZO_HIGHLIGHT_TEXT:UnpackRGBA())
+end
+
+local function OnMouseExit(control)
+    ZO_Options_OnMouseExit(control)
+
+    if control.isDisabled then return end
+
+    local label = control.label
+    if control.value then
+        label:SetColor(ZO_DEFAULT_ENABLED_COLOR:UnpackRGBA())
+    else
+        label:SetColor(ZO_DEFAULT_DISABLED_COLOR:UnpackRGBA())
+    end
+    control.checkbox:SetColor(ZO_NORMAL_TEXT:UnpackRGBA())
+end
+
+--controlName is optional
+function LAMCreateControl.checkbox(parent, checkboxData, controlName)
+    local control = LAM.util.CreateLabelAndContainerControl(parent, checkboxData, controlName)
+    control:SetHandler("OnMouseEnter", OnMouseEnter)
+    control:SetHandler("OnMouseExit", OnMouseExit)
+    control:SetHandler("OnMouseUp", function(control)
+        if control.isDisabled then return end
+        PlaySound(SOUNDS.DEFAULT_CLICK)
+        control.value = not control.value
+        control:UpdateValue(false, control.value)
+    end)
+
+    control.checkbox = wm:CreateControl(nil, control.container, CT_LABEL)
+    local checkbox = control.checkbox
+    checkbox:SetAnchor(LEFT, control.container, LEFT, 0, 0)
+    checkbox:SetFont("ZoFontGameBold")
+    checkbox:SetColor(ZO_NORMAL_TEXT:UnpackRGBA())
+    control.checkedText = GetString(SI_CHECK_BUTTON_ON):upper()
+    control.uncheckedText = GetString(SI_CHECK_BUTTON_OFF):upper()
+
+    if checkboxData.warning ~= nil or checkboxData.requiresReload then
+        control.warning = wm:CreateControlFromVirtual(nil, control, "ZO_Options_WarningIcon")
+        control.warning:SetAnchor(RIGHT, checkbox, LEFT, -5, 0)
+        control.UpdateWarning = LAM.util.UpdateWarning
+        control:UpdateWarning()
+    end
+
+    control.data.tooltipText = LAM.util.GetStringFromValue(checkboxData.tooltip)
+
+    control.UpdateValue = UpdateValue
+    control:UpdateValue()
+    if checkboxData.disabled ~= nil then
+        control.UpdateDisabled = UpdateDisabled
+        control:UpdateDisabled()
+    end
+
+    LAM.util.RegisterForRefreshIfNeeded(control)
+    LAM.util.RegisterForReloadIfNeeded(control)
+
+    return control
+end
diff --git a/TaosGroupTools/libs/LibAddonMenu-2.0/controls/colorpicker.lua b/TaosGroupTools/libs/LibAddonMenu-2.0/controls/colorpicker.lua
index a57aab0..db21260 100644
--- a/TaosGroupTools/libs/LibAddonMenu-2.0/controls/colorpicker.lua
+++ b/TaosGroupTools/libs/LibAddonMenu-2.0/controls/colorpicker.lua
@@ -1,106 +1,106 @@
---[[colorpickerData = {
-    type = "colorpicker",
-    name = "My Color Picker", -- or string id or function returning a string
-    getFunc = function() return db.r, db.g, db.b, db.a end, --(alpha is optional)
-    setFunc = function(r,g,b,a) db.r=r, db.g=g, db.b=b, db.a=a end, --(alpha is optional)
-    tooltip = "Color Picker's tooltip text.", -- or string id or function returning a string (optional)
-    width = "full", --or "half" (optional)
-    disabled = function() return db.someBooleanSetting end, --or boolean (optional)
-    warning = "May cause permanent awesomeness.", -- or string id or function returning a string (optional)
-    requiresReload = false, -- boolean, if set to true, the warning text will contain a notice that changes are only applied after an UI reload and any change to the value will make the "Apply Settings" button appear on the panel which will reload the UI when pressed (optional)
-    default = {r = defaults.r, g = defaults.g, b = defaults.b, a = defaults.a}, --(optional) table of default color values (or default = defaultColor, where defaultColor is a table with keys of r, g, b[, a]) or a function that returns the color
-    reference = "MyAddonColorpicker" -- unique global reference to control (optional)
-} ]]
-
-
-local widgetVersion = 13
-local LAM = LibStub("LibAddonMenu-2.0")
-if not LAM:RegisterWidget("colorpicker", widgetVersion) then return end
-
-local wm = WINDOW_MANAGER
-
-local function UpdateDisabled(control)
-    local disable
-    if type(control.data.disabled) == "function" then
-        disable = control.data.disabled()
-    else
-        disable = control.data.disabled
-    end
-
-    if disable then
-        control.label:SetColor(ZO_DEFAULT_DISABLED_COLOR:UnpackRGBA())
-    else
-        control.label:SetColor(ZO_DEFAULT_ENABLED_COLOR:UnpackRGBA())
-    end
-
-    control.isDisabled = disable
-end
-
-local function UpdateValue(control, forceDefault, valueR, valueG, valueB, valueA)
-    if forceDefault then --if we are forcing defaults
-        local color = LAM.util.GetDefaultValue(control.data.default)
-        valueR, valueG, valueB, valueA = color.r, color.g, color.b, color.a
-        control.data.setFunc(valueR, valueG, valueB, valueA)
-    elseif valueR and valueG and valueB then
-        control.data.setFunc(valueR, valueG, valueB, valueA or 1)
-        --after setting this value, let's refresh the others to see if any should be disabled or have their settings changed
-        LAM.util.RequestRefreshIfNeeded(control)
-    else
-        valueR, valueG, valueB, valueA = control.data.getFunc()
-    end
-
-    control.thumb:SetColor(valueR, valueG, valueB, valueA or 1)
-end
-
-function LAMCreateControl.colorpicker(parent, colorpickerData, controlName)
-    local control = LAM.util.CreateLabelAndContainerControl(parent, colorpickerData, controlName)
-
-    control.color = control.container
-    local color = control.color
-
-    control.thumb = wm:CreateControl(nil, color, CT_TEXTURE)
-    local thumb = control.thumb
-    thumb:SetDimensions(36, 18)
-    thumb:SetAnchor(LEFT, color, LEFT, 4, 0)
-
-    color.border = wm:CreateControl(nil, color, CT_TEXTURE)
-    local border = color.border
-    border:SetTexture("EsoUI\\Art\\ChatWindow\\chatOptions_bgColSwatch_frame.dds")
-    border:SetTextureCoords(0, .625, 0, .8125)
-    border:SetDimensions(40, 22)
-    border:SetAnchor(CENTER, thumb, CENTER, 0, 0)
-
-    local function ColorPickerCallback(r, g, b, a)
-        control:UpdateValue(false, r, g, b, a)
-    end
-
-    control:SetHandler("OnMouseUp", function(self, btn, upInside)
-        if self.isDisabled then return end
-
-        if upInside then
-            local r, g, b, a = colorpickerData.getFunc()
-            COLOR_PICKER:Show(ColorPickerCallback, r, g, b, a, LAM.util.GetStringFromValue(colorpickerData.name))
-        end
-    end)
-
-    if colorpickerData.warning ~= nil or colorpickerData.requiresReload then
-        control.warning = wm:CreateControlFromVirtual(nil, control, "ZO_Options_WarningIcon")
-        control.warning:SetAnchor(RIGHT, control.color, LEFT, -5, 0)
-        control.UpdateWarning = LAM.util.UpdateWarning
-        control:UpdateWarning()
-    end
-
-    control.data.tooltipText = LAM.util.GetStringFromValue(colorpickerData.tooltip)
-
-    control.UpdateValue = UpdateValue
-    control:UpdateValue()
-    if colorpickerData.disabled ~= nil then
-        control.UpdateDisabled = UpdateDisabled
-        control:UpdateDisabled()
-    end
-
-    LAM.util.RegisterForRefreshIfNeeded(control)
-    LAM.util.RegisterForReloadIfNeeded(control)
-
-    return control
-end
+--[[colorpickerData = {
+    type = "colorpicker",
+    name = "My Color Picker", -- or string id or function returning a string
+    getFunc = function() return db.r, db.g, db.b, db.a end, --(alpha is optional)
+    setFunc = function(r,g,b,a) db.r=r, db.g=g, db.b=b, db.a=a end, --(alpha is optional)
+    tooltip = "Color Picker's tooltip text.", -- or string id or function returning a string (optional)
+    width = "full", --or "half" (optional)
+    disabled = function() return db.someBooleanSetting end, --or boolean (optional)
+    warning = "May cause permanent awesomeness.", -- or string id or function returning a string (optional)
+    requiresReload = false, -- boolean, if set to true, the warning text will contain a notice that changes are only applied after an UI reload and any change to the value will make the "Apply Settings" button appear on the panel which will reload the UI when pressed (optional)
+    default = {r = defaults.r, g = defaults.g, b = defaults.b, a = defaults.a}, --(optional) table of default color values (or default = defaultColor, where defaultColor is a table with keys of r, g, b[, a]) or a function that returns the color
+    reference = "MyAddonColorpicker" -- unique global reference to control (optional)
+} ]]
+
+
+local widgetVersion = 13
+local LAM = LibStub("LibAddonMenu-2.0")
+if not LAM:RegisterWidget("colorpicker", widgetVersion) then return end
+
+local wm = WINDOW_MANAGER
+
+local function UpdateDisabled(control)
+    local disable
+    if type(control.data.disabled) == "function" then
+        disable = control.data.disabled()
+    else
+        disable = control.data.disabled
+    end
+
+    if disable then
+        control.label:SetColor(ZO_DEFAULT_DISABLED_COLOR:UnpackRGBA())
+    else
+        control.label:SetColor(ZO_DEFAULT_ENABLED_COLOR:UnpackRGBA())
+    end
+
+    control.isDisabled = disable
+end
+
+local function UpdateValue(control, forceDefault, valueR, valueG, valueB, valueA)
+    if forceDefault then --if we are forcing defaults
+        local color = LAM.util.GetDefaultValue(control.data.default)
+        valueR, valueG, valueB, valueA = color.r, color.g, color.b, color.a
+        control.data.setFunc(valueR, valueG, valueB, valueA)
+    elseif valueR and valueG and valueB then
+        control.data.setFunc(valueR, valueG, valueB, valueA or 1)
+        --after setting this value, let's refresh the others to see if any should be disabled or have their settings changed
+        LAM.util.RequestRefreshIfNeeded(control)
+    else
+        valueR, valueG, valueB, valueA = control.data.getFunc()
+    end
+
+    control.thumb:SetColor(valueR, valueG, valueB, valueA or 1)
+end
+
+function LAMCreateControl.colorpicker(parent, colorpickerData, controlName)
+    local control = LAM.util.CreateLabelAndContainerControl(parent, colorpickerData, controlName)
+
+    control.color = control.container
+    local color = control.color
+
+    control.thumb = wm:CreateControl(nil, color, CT_TEXTURE)
+    local thumb = control.thumb
+    thumb:SetDimensions(36, 18)
+    thumb:SetAnchor(LEFT, color, LEFT, 4, 0)
+
+    color.border = wm:CreateControl(nil, color, CT_TEXTURE)
+    local border = color.border
+    border:SetTexture("EsoUI\\Art\\ChatWindow\\chatOptions_bgColSwatch_frame.dds")
+    border:SetTextureCoords(0, .625, 0, .8125)
+    border:SetDimensions(40, 22)
+    border:SetAnchor(CENTER, thumb, CENTER, 0, 0)
+
+    local function ColorPickerCallback(r, g, b, a)
+        control:UpdateValue(false, r, g, b, a)
+    end
+
+    control:SetHandler("OnMouseUp", function(self, btn, upInside)
+        if self.isDisabled then return end
+
+        if upInside then
+            local r, g, b, a = colorpickerData.getFunc()
+            COLOR_PICKER:Show(ColorPickerCallback, r, g, b, a, LAM.util.GetStringFromValue(colorpickerData.name))
+        end
+    end)
+
+    if colorpickerData.warning ~= nil or colorpickerData.requiresReload then
+        control.warning = wm:CreateControlFromVirtual(nil, control, "ZO_Options_WarningIcon")
+        control.warning:SetAnchor(RIGHT, control.color, LEFT, -5, 0)
+        control.UpdateWarning = LAM.util.UpdateWarning
+        control:UpdateWarning()
+    end
+
+    control.data.tooltipText = LAM.util.GetStringFromValue(colorpickerData.tooltip)
+
+    control.UpdateValue = UpdateValue
+    control:UpdateValue()
+    if colorpickerData.disabled ~= nil then
+        control.UpdateDisabled = UpdateDisabled
+        control:UpdateDisabled()
+    end
+
+    LAM.util.RegisterForRefreshIfNeeded(control)
+    LAM.util.RegisterForReloadIfNeeded(control)
+
+    return control
+end
diff --git a/TaosGroupTools/libs/LibAddonMenu-2.0/controls/custom.lua b/TaosGroupTools/libs/LibAddonMenu-2.0/controls/custom.lua
index 40a7c42..5d6111c 100644
--- a/TaosGroupTools/libs/LibAddonMenu-2.0/controls/custom.lua
+++ b/TaosGroupTools/libs/LibAddonMenu-2.0/controls/custom.lua
@@ -1,35 +1,35 @@
---[[customData = {
-    type = "custom",
-    reference = "MyAddonCustomControl", --(optional) unique name for your control to use as reference
-    refreshFunc = function(customControl) end, --(optional) function to call when panel/controls refresh
-    width = "full", --or "half" (optional)
-} ]]
-
-local widgetVersion = 7
-local LAM = LibStub("LibAddonMenu-2.0")
-if not LAM:RegisterWidget("custom", widgetVersion) then return end
-
-local function UpdateValue(control)
-    if control.data.refreshFunc then
-        control.data.refreshFunc(control)
-    end
-end
-
-local MIN_HEIGHT = 26
-function LAMCreateControl.custom(parent, customData, controlName)
-    local control = LAM.util.CreateBaseControl(parent, customData, controlName)
-    local width = control:GetWidth()
-    control:SetResizeToFitDescendents(true)
-
-    if control.isHalfWidth then --note these restrictions
-        control:SetDimensionConstraints(width / 2, MIN_HEIGHT, width / 2, MIN_HEIGHT * 4)
-    else
-        control:SetDimensionConstraints(width, MIN_HEIGHT, width, MIN_HEIGHT * 4)
-    end
-
-    control.UpdateValue = UpdateValue
-
-    LAM.util.RegisterForRefreshIfNeeded(control)
-
-    return control
-end
+--[[customData = {
+    type = "custom",
+    reference = "MyAddonCustomControl", --(optional) unique name for your control to use as reference
+    refreshFunc = function(customControl) end, --(optional) function to call when panel/controls refresh
+    width = "full", --or "half" (optional)
+} ]]
+
+local widgetVersion = 7
+local LAM = LibStub("LibAddonMenu-2.0")
+if not LAM:RegisterWidget("custom", widgetVersion) then return end
+
+local function UpdateValue(control)
+    if control.data.refreshFunc then
+        control.data.refreshFunc(control)
+    end
+end
+
+local MIN_HEIGHT = 26
+function LAMCreateControl.custom(parent, customData, controlName)
+    local control = LAM.util.CreateBaseControl(parent, customData, controlName)
+    local width = control:GetWidth()
+    control:SetResizeToFitDescendents(true)
+
+    if control.isHalfWidth then --note these restrictions
+        control:SetDimensionConstraints(width / 2, MIN_HEIGHT, width / 2, MIN_HEIGHT * 4)
+    else
+        control:SetDimensionConstraints(width, MIN_HEIGHT, width, MIN_HEIGHT * 4)
+    end
+
+    control.UpdateValue = UpdateValue
+
+    LAM.util.RegisterForRefreshIfNeeded(control)
+
+    return control
+end
diff --git a/TaosGroupTools/libs/LibAddonMenu-2.0/controls/description.lua b/TaosGroupTools/libs/LibAddonMenu-2.0/controls/description.lua
index da207a0..27c7192 100644
--- a/TaosGroupTools/libs/LibAddonMenu-2.0/controls/description.lua
+++ b/TaosGroupTools/libs/LibAddonMenu-2.0/controls/description.lua
@@ -1,60 +1,60 @@
---[[descriptionData = {
-    type = "description",
-    text = "My description text to display.", -- or string id or function returning a string
-    title = "My Title", -- or string id or function returning a string (optional)
-    width = "full", --or "half" (optional)
-    reference = "MyAddonDescription" -- unique global reference to control (optional)
-} ]]
-
-
-local widgetVersion = 8
-local LAM = LibStub("LibAddonMenu-2.0")
-if not LAM:RegisterWidget("description", widgetVersion) then return end
-
-local wm = WINDOW_MANAGER
-
-local function UpdateValue(control)
-    if control.title then
-        control.title:SetText(LAM.util.GetStringFromValue(control.data.title))
-    end
-    control.desc:SetText(LAM.util.GetStringFromValue(control.data.text))
-end
-
-function LAMCreateControl.description(parent, descriptionData, controlName)
-    local control = LAM.util.CreateBaseControl(parent, descriptionData, controlName)
-    local isHalfWidth = control.isHalfWidth
-    local width = control:GetWidth()
-    control:SetResizeToFitDescendents(true)
-
-    if isHalfWidth then
-        control:SetDimensionConstraints(width / 2, 0, width / 2, 0)
-    else
-        control:SetDimensionConstraints(width, 0, width, 0)
-    end
-
-    control.desc = wm:CreateControl(nil, control, CT_LABEL)
-    local desc = control.desc
-    desc:SetVerticalAlignment(TEXT_ALIGN_TOP)
-    desc:SetFont("ZoFontGame")
-    desc:SetText(LAM.util.GetStringFromValue(descriptionData.text))
-    desc:SetWidth(isHalfWidth and width / 2 or width)
-
-    if descriptionData.title then
-        control.title = wm:CreateControl(nil, control, CT_LABEL)
-        local title = control.title
-        title:SetWidth(isHalfWidth and width / 2 or width)
-        title:SetAnchor(TOPLEFT, control, TOPLEFT)
-        title:SetFont("ZoFontWinH4")
-        title:SetText(LAM.util.GetStringFromValue(descriptionData.title))
-        desc:SetAnchor(TOPLEFT, title, BOTTOMLEFT)
-    else
-        desc:SetAnchor(TOPLEFT)
-    end
-
-    control.UpdateValue = UpdateValue
-
-    LAM.util.RegisterForRefreshIfNeeded(control)
-
-    return control
-
-end
+--[[descriptionData = {
+    type = "description",
+    text = "My description text to display.", -- or string id or function returning a string
+    title = "My Title", -- or string id or function returning a string (optional)
+    width = "full", --or "half" (optional)
+    reference = "MyAddonDescription" -- unique global reference to control (optional)
+} ]]
+
+
+local widgetVersion = 8
+local LAM = LibStub("LibAddonMenu-2.0")
+if not LAM:RegisterWidget("description", widgetVersion) then return end
+
+local wm = WINDOW_MANAGER
+
+local function UpdateValue(control)
+    if control.title then
+        control.title:SetText(LAM.util.GetStringFromValue(control.data.title))
+    end
+    control.desc:SetText(LAM.util.GetStringFromValue(control.data.text))
+end
+
+function LAMCreateControl.description(parent, descriptionData, controlName)
+    local control = LAM.util.CreateBaseControl(parent, descriptionData, controlName)
+    local isHalfWidth = control.isHalfWidth
+    local width = control:GetWidth()
+    control:SetResizeToFitDescendents(true)
+
+    if isHalfWidth then
+        control:SetDimensionConstraints(width / 2, 0, width / 2, 0)
+    else
+        control:SetDimensionConstraints(width, 0, width, 0)
+    end
+
+    control.desc = wm:CreateControl(nil, control, CT_LABEL)
+    local desc = control.desc
+    desc:SetVerticalAlignment(TEXT_ALIGN_TOP)
+    desc:SetFont("ZoFontGame")
+    desc:SetText(LAM.util.GetStringFromValue(descriptionData.text))
+    desc:SetWidth(isHalfWidth and width / 2 or width)
+
+    if descriptionData.title then
+        control.title = wm:CreateControl(nil, control, CT_LABEL)
+        local title = control.title
+        title:SetWidth(isHalfWidth and width / 2 or width)
+        title:SetAnchor(TOPLEFT, control, TOPLEFT)
+        title:SetFont("ZoFontWinH4")
+        title:SetText(LAM.util.GetStringFromValue(descriptionData.title))
+        desc:SetAnchor(TOPLEFT, title, BOTTOMLEFT)
+    else
+        desc:SetAnchor(TOPLEFT)
+    end
+
+    control.UpdateValue = UpdateValue
+
+    LAM.util.RegisterForRefreshIfNeeded(control)
+
+    return control
+
+end
diff --git a/TaosGroupTools/libs/LibAddonMenu-2.0/controls/divider.lua b/TaosGroupTools/libs/LibAddonMenu-2.0/controls/divider.lua
index 8089539..b24e2fc 100644
--- a/TaosGroupTools/libs/LibAddonMenu-2.0/controls/divider.lua
+++ b/TaosGroupTools/libs/LibAddonMenu-2.0/controls/divider.lua
@@ -1,45 +1,45 @@
---[[dividerData = {
-    type = "divider",
-    width = "full", --or "half" (optional)
-    height = 10, (optional)
-    alpha = 0.25, (optional)
-    reference = "MyAddonDivider" -- unique global reference to control (optional)
-} ]]
-
-
-local widgetVersion = 2
-local LAM = LibStub("LibAddonMenu-2.0")
-if not LAM:RegisterWidget("divider", widgetVersion) then return end
-
-local wm = WINDOW_MANAGER
-
-local MIN_HEIGHT = 10
-local MAX_HEIGHT = 50
-local MIN_ALPHA = 0
-local MAX_ALPHA = 1
-local DEFAULT_ALPHA = 0.25
-
-local function GetValueInRange(value, min, max, default)
-    if not value or type(value) ~= "number" then
-        return default
-    end
-    return math.min(math.max(min, value), max)
-end
-
-function LAMCreateControl.divider(parent, dividerData, controlName)
-    local control = LAM.util.CreateBaseControl(parent, dividerData, controlName)
-    local isHalfWidth = control.isHalfWidth
-    local width = control:GetWidth()
-    local height = GetValueInRange(dividerData.height, MIN_HEIGHT, MAX_HEIGHT, MIN_HEIGHT)
-    local alpha = GetValueInRange(dividerData.alpha, MIN_ALPHA, MAX_ALPHA, DEFAULT_ALPHA)
-
-    control:SetDimensions(isHalfWidth and width / 2 or width, height)
-
-    control.divider = wm:CreateControlFromVirtual(nil, control, "ZO_Options_Divider")
-    local divider = control.divider
-    divider:SetWidth(isHalfWidth and width / 2 or width)
-    divider:SetAnchor(TOPLEFT)
-    divider:SetAlpha(alpha)
-
-    return control
-end
+--[[dividerData = {
+    type = "divider",
+    width = "full", --or "half" (optional)
+    height = 10, (optional)
+    alpha = 0.25, (optional)
+    reference = "MyAddonDivider" -- unique global reference to control (optional)
+} ]]
+
+
+local widgetVersion = 2
+local LAM = LibStub("LibAddonMenu-2.0")
+if not LAM:RegisterWidget("divider", widgetVersion) then return end
+
+local wm = WINDOW_MANAGER
+
+local MIN_HEIGHT = 10
+local MAX_HEIGHT = 50
+local MIN_ALPHA = 0
+local MAX_ALPHA = 1
+local DEFAULT_ALPHA = 0.25
+
+local function GetValueInRange(value, min, max, default)
+    if not value or type(value) ~= "number" then
+        return default
+    end
+    return math.min(math.max(min, value), max)
+end
+
+function LAMCreateControl.divider(parent, dividerData, controlName)
+    local control = LAM.util.CreateBaseControl(parent, dividerData, controlName)
+    local isHalfWidth = control.isHalfWidth
+    local width = control:GetWidth()
+    local height = GetValueInRange(dividerData.height, MIN_HEIGHT, MAX_HEIGHT, MIN_HEIGHT)
+    local alpha = GetValueInRange(dividerData.alpha, MIN_ALPHA, MAX_ALPHA, DEFAULT_ALPHA)
+
+    control:SetDimensions(isHalfWidth and width / 2 or width, height)
+
+    control.divider = wm:CreateControlFromVirtual(nil, control, "ZO_Options_Divider")
+    local divider = control.divider
+    divider:SetWidth(isHalfWidth and width / 2 or width)
+    divider:SetAnchor(TOPLEFT)
+    divider:SetAlpha(alpha)
+
+    return control
+end
diff --git a/TaosGroupTools/libs/LibAddonMenu-2.0/controls/dropdown.lua b/TaosGroupTools/libs/LibAddonMenu-2.0/controls/dropdown.lua
index 70e23bb..c67624a 100644
--- a/TaosGroupTools/libs/LibAddonMenu-2.0/controls/dropdown.lua
+++ b/TaosGroupTools/libs/LibAddonMenu-2.0/controls/dropdown.lua
@@ -1,387 +1,387 @@
---[[dropdownData = {
-    type = "dropdown",
-    name = "My Dropdown", -- or string id or function returning a string
-    choices = {"table", "of", "choices"},
-    choicesValues = {"foo", 2, "three"}, -- if specified, these values will get passed to setFunc instead (optional)
-    getFunc = function() return db.var end,
-    setFunc = function(var) db.var = var doStuff() end,
-    tooltip = "Dropdown's tooltip text.", -- or string id or function returning a string (optional)
-    choicesTooltips = {"tooltip 1", "tooltip 2", "tooltip 3"}, -- or array of string ids or array of functions returning a string (optional)
-    sort = "name-up", --or "name-down", "numeric-up", "numeric-down", "value-up", "value-down", "numericvalue-up", "numericvalue-down" (optional) - if not provided, list will not be sorted
-    width = "full", --or "half" (optional)
-    scrollable = true, -- boolean or number, if set the dropdown will feature a scroll bar if there are a large amount of choices and limit the visible lines to the specified number or 10 if true is used (optional)
-    disabled = function() return db.someBooleanSetting end, --or boolean (optional)
-    warning = "May cause permanent awesomeness.", -- or string id or function returning a string (optional)
-    requiresReload = false, -- boolean, if set to true, the warning text will contain a notice that changes are only applied after an UI reload and any change to the value will make the "Apply Settings" button appear on the panel which will reload the UI when pressed (optional)
-    default = defaults.var, -- default value or function that returns the default value (optional)
-    reference = "MyAddonDropdown" -- unique global reference to control (optional)
-} ]]
-
-
-local widgetVersion = 18
-local LAM = LibStub("LibAddonMenu-2.0")
-if not LAM:RegisterWidget("dropdown", widgetVersion) then return end
-
-local wm = WINDOW_MANAGER
-local SORT_BY_VALUE         = { ["value"] = {} }
-local SORT_BY_VALUE_NUMERIC = { ["value"] = { isNumeric = true } }
-local SORT_TYPES = {
-    name = ZO_SORT_BY_NAME,
-    numeric = ZO_SORT_BY_NAME_NUMERIC,
-    value = SORT_BY_VALUE,
-    numericvalue = SORT_BY_VALUE_NUMERIC,
-}
-local SORT_ORDERS = {
-    up = ZO_SORT_ORDER_UP,
-    down = ZO_SORT_ORDER_DOWN,
-}
-
-local function UpdateDisabled(control)
-    local disable
-    if type(control.data.disabled) == "function" then
-        disable = control.data.disabled()
-    else
-        disable = control.data.disabled
-    end
-
-    control.dropdown:SetEnabled(not disable)
-    if disable then
-        control.label:SetColor(ZO_DEFAULT_DISABLED_COLOR:UnpackRGBA())
-    else
-        control.label:SetColor(ZO_DEFAULT_ENABLED_COLOR:UnpackRGBA())
-    end
-end
-
-local function UpdateValue(control, forceDefault, value)
-    if forceDefault then --if we are forcing defaults
-        value = LAM.util.GetDefaultValue(control.data.default)
-        control.data.setFunc(value)
-        control.dropdown:SetSelectedItem(control.choices[value])
-    elseif value then
-        control.data.setFunc(value)
-        --after setting this value, let's refresh the others to see if any should be disabled or have their settings changed
-        LAM.util.RequestRefreshIfNeeded(control)
-    else
-        value = control.data.getFunc()
-        control.dropdown:SetSelectedItem(control.choices[value])
-    end
-end
-
-local function DropdownCallback(control, choiceText, choice)
-    choice.control:UpdateValue(false, choice.value or choiceText)
-end
-
-local function SetupTooltips(comboBox, choicesTooltips)
-    local function ShowTooltip(control)
-        InitializeTooltip(InformationTooltip, control, TOPLEFT, 0, 0, BOTTOMRIGHT)
-        SetTooltipText(InformationTooltip, LAM.util.GetStringFromValue(control.tooltip))
-        InformationTooltipTopLevel:BringWindowToTop()
-    end
-    local function HideTooltip(control)
-        ClearTooltip(InformationTooltip)
-    end
-
-    -- allow for tooltips on the drop down entries
-    local originalShow = comboBox.ShowDropdownInternal
-    comboBox.ShowDropdownInternal = function(comboBox)
-        originalShow(comboBox)
-        local entries = ZO_Menu.items
-        for i = 1, #entries do
-            local entry = entries[i]
-            local control = entries[i].item
-            control.tooltip = choicesTooltips[i]
-            entry.onMouseEnter = control:GetHandler("OnMouseEnter")
-            entry.onMouseExit = control:GetHandler("OnMouseExit")
-            ZO_PreHookHandler(control, "OnMouseEnter", ShowTooltip)
-            ZO_PreHookHandler(control, "OnMouseExit", HideTooltip)
-        end
-    end
-
-    local originalHide = comboBox.HideDropdownInternal
-    comboBox.HideDropdownInternal = function(self)
-        local entries = ZO_Menu.items
-        for i = 1, #entries do
-            local entry = entries[i]
-            local control = entries[i].item
-            control:SetHandler("OnMouseEnter", entry.onMouseEnter)
-            control:SetHandler("OnMouseExit", entry.onMouseExit)
-            control.tooltip = nil
-        end
-        originalHide(self)
-    end
-end
-
-local function UpdateChoices(control, choices, choicesValues, choicesTooltips)
-    control.dropdown:ClearItems() --remove previous choices --(need to call :SetSelectedItem()?)
-    ZO_ClearTable(control.choices)
-
-    --build new list of choices
-    local choices = choices or control.data.choices
-    local choicesValues = choicesValues or control.data.choicesValues
-    local choicesTooltips = choicesTooltips or control.data.choicesTooltips
-
-    if choicesValues then
-        assert(#choices == #choicesValues, "choices and choicesValues need to have the same size")
-    end
-
-    if choicesTooltips then
-        assert(#choices == #choicesTooltips, "choices and choicesTooltips need to have the same size")
-        if not control.scrollHelper then -- only do this for non-scrollable
-            SetupTooltips(control.dropdown, choicesTooltips)
-        end
-    end
-
-    for i = 1, #choices do
-        local entry = control.dropdown:CreateItemEntry(choices[i], DropdownCallback)
-        entry.control = control
-        if choicesValues then
-            entry.value = choicesValues[i]
-        end
-        if choicesTooltips and control.scrollHelper then
-            entry.tooltip = choicesTooltips[i]
-        end
-        control.choices[entry.value or entry.name] = entry.name
-        control.dropdown:AddItem(entry, not control.data.sort and ZO_COMBOBOX_SUPRESS_UPDATE) --if sort type/order isn't specified, then don't sort
-    end
-end
-
-local function GrabSortingInfo(sortInfo)
-    local t, i = {}, 1
-    for info in string.gmatch(sortInfo, "([^%-]+)") do
-        t[i] = info
-        i = i + 1
-    end
-
-    return t
-end
-
-local DEFAULT_VISIBLE_ROWS = 10
-local SCROLLABLE_ENTRY_TEMPLATE_HEIGHT = 25 -- same as in zo_combobox.lua
-local CONTENT_PADDING = 24
-local SCROLLBAR_PADDING = 16
-local PADDING = GetMenuPadding() / 2 -- half the amount looks closer to the regular dropdown
-local ROUNDING_MARGIN = 0.01 -- needed to avoid rare issue with too many anchors processed
-local ScrollableDropdownHelper = ZO_Object:Subclass()
-
-function ScrollableDropdownHelper:New(...)
-    local object = ZO_Object.New(self)
-    object:Initialize(...)
-    return object
-end
-
-function ScrollableDropdownHelper:Initialize(parent, control, visibleRows)
-    local combobox = control.combobox
-    local dropdown = control.dropdown
-    self.parent = parent
-    self.control = control
-    self.combobox = combobox
-    self.dropdown = dropdown
-    self.visibleRows = visibleRows
-
-    -- clear anchors so we can adjust the width dynamically
-    dropdown.m_dropdown:ClearAnchors()
-    dropdown.m_dropdown:SetAnchor(TOPLEFT, combobox, BOTTOMLEFT)
-
-    -- handle dropdown or settingsmenu opening/closing
-    local function onShow() self:OnShow() end
-    local function onHide() self:OnHide() end
-    local function doHide() self:DoHide() end
-
-    ZO_PreHook(dropdown, "ShowDropdownOnMouseUp", onShow)
-    ZO_PreHook(dropdown, "HideDropdownInternal", onHide)
-    combobox:SetHandler("OnEffectivelyHidden", onHide)
-    parent:SetHandler("OnEffectivelyHidden", doHide)
-
-    -- dont fade entries near the edges
-    local scrollList = dropdown.m_scroll
-    scrollList.selectionTemplate = nil
-    scrollList.highlightTemplate = nil
-    ZO_ScrollList_EnableSelection(scrollList, "ZO_SelectionHighlight")
-    ZO_ScrollList_EnableHighlight(scrollList, "ZO_SelectionHighlight")
-    ZO_Scroll_SetUseFadeGradient(scrollList, false)
-
-    -- adjust scroll content anchor to mimic menu padding
-    local scroll = dropdown.m_dropdown:GetNamedChild("Scroll")
-    local anchor1 = {scroll:GetAnchor(0)}
-    local anchor2 = {scroll:GetAnchor(1)}
-    scroll:ClearAnchors()
-    scroll:SetAnchor(anchor1[2], anchor1[3], anchor1[4], anchor1[5] + PADDING, anchor1[6] + PADDING)
-    scroll:SetAnchor(anchor2[2], anchor2[3], anchor2[4], anchor2[5] - PADDING, anchor2[6] - PADDING)
-    ZO_ScrollList_Commit(scrollList)
-
-    -- hook mouse enter/exit
-    local function onMouseEnter(control) self:OnMouseEnter(control) end
-    local function onMouseExit(control) self:OnMouseExit(control) end
-
-    -- adjust row setup to mimic the highlight padding
-    local dataType1 = ZO_ScrollList_GetDataTypeTable(dropdown.m_scroll, 1)
-    local dataType2 = ZO_ScrollList_GetDataTypeTable(dropdown.m_scroll, 2)
-    local oSetup = dataType1.setupCallback -- both types have the same setup function
-    local function SetupEntry(control, data, list)
-        oSetup(control, data, list)
-        control.m_label:SetAnchor(LEFT, nil, nil, 2)
-        -- no need to store old ones since we have full ownership of our dropdown controls
-        if not control.hookedMouseHandlers then --only do it once per control
-            control.hookedMouseHandlers = true
-            ZO_PreHookHandler(control, "OnMouseEnter", onMouseEnter)
-            ZO_PreHookHandler(control, "OnMouseExit", onMouseExit)
-            -- we could also just replace the handlers
-            --control:SetHandler("OnMouseEnter", onMouseEnter)
-            --control:SetHandler("OnMouseExit", onMouseExit)
-        end
-    end
-    dataType1.setupCallback = SetupEntry
-    dataType2.setupCallback = SetupEntry
-
-    -- adjust dimensions based on entries
-    local scrollContent = scroll:GetNamedChild("Contents")
-    ZO_PreHook(dropdown, "AddMenuItems", function()
-        local width = PADDING * 2 + zo_max(self:GetMaxWidth(), combobox:GetWidth())
-        local numItems = #dropdown.m_sortedItems
-        local anchorOffset = 0
-        if(numItems > self.visibleRows) then
-            width = width + CONTENT_PADDING + SCROLLBAR_PADDING
-            anchorOffset = -SCROLLBAR_PADDING
-            numItems = self.visibleRows
-        end
-        scrollContent:SetAnchor(BOTTOMRIGHT, nil, nil, anchorOffset)
-        local height = PADDING * 2 + numItems * (SCROLLABLE_ENTRY_TEMPLATE_HEIGHT + dropdown.m_spacing) - dropdown.m_spacing + ROUNDING_MARGIN
-        dropdown.m_dropdown:SetWidth(width)
-        dropdown.m_dropdown:SetHeight(height)
-    end)
-end
-
-function ScrollableDropdownHelper:OnShow()
-    local dropdown = self.dropdown
-    if dropdown.m_lastParent ~= ZO_Menus then
-        dropdown.m_lastParent = dropdown.m_dropdown:GetParent()
-        dropdown.m_dropdown:SetParent(ZO_Menus)
-        ZO_Menus:BringWindowToTop()
-    end
-end
-
-function ScrollableDropdownHelper:OnHide()
-    local dropdown = self.dropdown
-    if dropdown.m_lastParent then
-        dropdown.m_dropdown:SetParent(dropdown.m_lastParent)
-        dropdown.m_lastParent = nil
-    end
-end
-
-function ScrollableDropdownHelper:DoHide()
-    local dropdown = self.dropdown
-    if dropdown:IsDropdownVisible() then
-        dropdown:HideDropdown()
-    end
-end
-
-function ScrollableDropdownHelper:GetMaxWidth()
-    local dropdown = self.dropdown
-    local dataType = ZO_ScrollList_GetDataTypeTable(dropdown.m_scroll, 1)
-
-    local dummy = dataType.pool:AcquireObject()
-    dataType.setupCallback(dummy, {
-        m_owner = dropdown,
-        name = "Dummy"
-    }, dropdown)
-
-    local maxWidth = 0
-    local label = dummy.m_label
-    local entries = dropdown.m_sortedItems
-    local numItems = #entries
-    for index = 1, numItems do
-        label:SetText(entries[index].name)
-        local width = label:GetTextWidth()
-        if (width > maxWidth) then
-            maxWidth = width
-        end
-    end
-
-    dataType.pool:ReleaseObject(dummy.key)
-    return maxWidth
-end
-
-function ScrollableDropdownHelper:OnMouseEnter(control)
-    -- call original code if we replace instead of hook the handler
-        --ZO_ScrollableComboBox_Entry_OnMouseEnter(control)
-    -- show tooltip
-    if control.m_data.tooltip then
-        InitializeTooltip(InformationTooltip, control, TOPLEFT, 0, 0, BOTTOMRIGHT)
-        SetTooltipText(InformationTooltip, LAM.util.GetStringFromValue(control.m_data.tooltip))
-        InformationTooltipTopLevel:BringWindowToTop()
-    end
-end
-function ScrollableDropdownHelper:OnMouseExit(control)
-    -- call original code if we replace instead of hook the handler
-        --ZO_ScrollableComboBox_Entry_OnMouseExit(control)
-    -- hide tooltip
-    if control.m_data.tooltip then
-        ClearTooltip(InformationTooltip)
-    end
-end
-
-function LAMCreateControl.dropdown(parent, dropdownData, controlName)
-    local control = LAM.util.CreateLabelAndContainerControl(parent, dropdownData, controlName)
-    control.choices = {}
-
-    local countControl = parent
-    local name = parent:GetName()
-    if not name or #name == 0 then
-        countControl = LAMCreateControl
-        name = "LAM"
-    end
-    local comboboxCount = (countControl.comboboxCount or 0) + 1
-    countControl.comboboxCount = comboboxCount
-    control.combobox = wm:CreateControlFromVirtual(zo_strjoin(nil, name, "Combobox", comboboxCount), control.container, dropdownData.scrollable and "ZO_ScrollableComboBox" or "ZO_ComboBox")
-
-    local combobox = control.combobox
-    combobox:SetAnchor(TOPLEFT)
-    combobox:SetDimensions(control.container:GetDimensions())
-    combobox:SetHandler("OnMouseEnter", function() ZO_Options_OnMouseEnter(control) end)
-    combobox:SetHandler("OnMouseExit", function() ZO_Options_OnMouseExit(control) end)
-    control.dropdown = ZO_ComboBox_ObjectFromContainer(combobox)
-    local dropdown = control.dropdown
-    dropdown:SetSortsItems(false) -- need to sort ourselves in order to be able to sort by value
-
-    if dropdownData.scrollable then
-        local visibleRows = type(dropdownData.scrollable) == "number" and dropdownData.scrollable or DEFAULT_VISIBLE_ROWS
-        control.scrollHelper = ScrollableDropdownHelper:New(parent, control, visibleRows)
-    end
-
-    ZO_PreHook(dropdown, "UpdateItems", function(self)
-        assert(not self.m_sortsItems, "built-in dropdown sorting was reactivated, sorting is handled by LAM")
-        if control.m_sortOrder ~= nil and control.m_sortType then
-            local sortKey = next(control.m_sortType)
-            local sortFunc = function(item1, item2) return ZO_TableOrderingFunction(item1, item2, sortKey, control.m_sortType, control.m_sortOrder) end
-            table.sort(self.m_sortedItems, sortFunc)
-        end
-    end)
-
-    if dropdownData.sort then
-        local sortInfo = GrabSortingInfo(dropdownData.sort)
-        control.m_sortType, control.m_sortOrder = SORT_TYPES[sortInfo[1]], SORT_ORDERS[sortInfo[2]]
-    elseif dropdownData.choicesValues then
-        control.m_sortType, control.m_sortOrder = ZO_SORT_ORDER_UP, SORT_BY_VALUE
-    end
-
-    if dropdownData.warning ~= nil or dropdownData.requiresReload then
-        control.warning = wm:CreateControlFromVirtual(nil, control, "ZO_Options_WarningIcon")
-        control.warning:SetAnchor(RIGHT, combobox, LEFT, -5, 0)
-        control.UpdateWarning = LAM.util.UpdateWarning
-        control:UpdateWarning()
-    end
-
-    control.UpdateChoices = UpdateChoices
-    control:UpdateChoices(dropdownData.choices, dropdownData.choicesValues)
-    control.UpdateValue = UpdateValue
-    control:UpdateValue()
-    if dropdownData.disabled ~= nil then
-        control.UpdateDisabled = UpdateDisabled
-        control:UpdateDisabled()
-    end
-
-    LAM.util.RegisterForRefreshIfNeeded(control)
-    LAM.util.RegisterForReloadIfNeeded(control)
-
-    return control
-end
+--[[dropdownData = {
+    type = "dropdown",
+    name = "My Dropdown", -- or string id or function returning a string
+    choices = {"table", "of", "choices"},
+    choicesValues = {"foo", 2, "three"}, -- if specified, these values will get passed to setFunc instead (optional)
+    getFunc = function() return db.var end,
+    setFunc = function(var) db.var = var doStuff() end,
+    tooltip = "Dropdown's tooltip text.", -- or string id or function returning a string (optional)
+    choicesTooltips = {"tooltip 1", "tooltip 2", "tooltip 3"}, -- or array of string ids or array of functions returning a string (optional)
+    sort = "name-up", --or "name-down", "numeric-up", "numeric-down", "value-up", "value-down", "numericvalue-up", "numericvalue-down" (optional) - if not provided, list will not be sorted
+    width = "full", --or "half" (optional)
+    scrollable = true, -- boolean or number, if set the dropdown will feature a scroll bar if there are a large amount of choices and limit the visible lines to the specified number or 10 if true is used (optional)
+    disabled = function() return db.someBooleanSetting end, --or boolean (optional)
+    warning = "May cause permanent awesomeness.", -- or string id or function returning a string (optional)
+    requiresReload = false, -- boolean, if set to true, the warning text will contain a notice that changes are only applied after an UI reload and any change to the value will make the "Apply Settings" button appear on the panel which will reload the UI when pressed (optional)
+    default = defaults.var, -- default value or function that returns the default value (optional)
+    reference = "MyAddonDropdown" -- unique global reference to control (optional)
+} ]]
+
+
+local widgetVersion = 18
+local LAM = LibStub("LibAddonMenu-2.0")
+if not LAM:RegisterWidget("dropdown", widgetVersion) then return end
+
+local wm = WINDOW_MANAGER
+local SORT_BY_VALUE         = { ["value"] = {} }
+local SORT_BY_VALUE_NUMERIC = { ["value"] = { isNumeric = true } }
+local SORT_TYPES = {
+    name = ZO_SORT_BY_NAME,
+    numeric = ZO_SORT_BY_NAME_NUMERIC,
+    value = SORT_BY_VALUE,
+    numericvalue = SORT_BY_VALUE_NUMERIC,
+}
+local SORT_ORDERS = {
+    up = ZO_SORT_ORDER_UP,
+    down = ZO_SORT_ORDER_DOWN,
+}
+
+local function UpdateDisabled(control)
+    local disable
+    if type(control.data.disabled) == "function" then
+        disable = control.data.disabled()
+    else
+        disable = control.data.disabled
+    end
+
+    control.dropdown:SetEnabled(not disable)
+    if disable then
+        control.label:SetColor(ZO_DEFAULT_DISABLED_COLOR:UnpackRGBA())
+    else
+        control.label:SetColor(ZO_DEFAULT_ENABLED_COLOR:UnpackRGBA())
+    end
+end
+
+local function UpdateValue(control, forceDefault, value)
+    if forceDefault then --if we are forcing defaults
+        value = LAM.util.GetDefaultValue(control.data.default)
+        control.data.setFunc(value)
+        control.dropdown:SetSelectedItem(control.choices[value])
+    elseif value then
+        control.data.setFunc(value)
+        --after setting this value, let's refresh the others to see if any should be disabled or have their settings changed
+        LAM.util.RequestRefreshIfNeeded(control)
+    else
+        value = control.data.getFunc()
+        control.dropdown:SetSelectedItem(control.choices[value])
+    end
+end
+
+local function DropdownCallback(control, choiceText, choice)
+    choice.control:UpdateValue(false, choice.value or choiceText)
+end
+
+local function SetupTooltips(comboBox, choicesTooltips)
+    local function ShowTooltip(control)
+        InitializeTooltip(InformationTooltip, control, TOPLEFT, 0, 0, BOTTOMRIGHT)
+        SetTooltipText(InformationTooltip, LAM.util.GetStringFromValue(control.tooltip))
+        InformationTooltipTopLevel:BringWindowToTop()
+    end
+    local function HideTooltip(control)
+        ClearTooltip(InformationTooltip)
+    end
+
+    -- allow for tooltips on the drop down entries
+    local originalShow = comboBox.ShowDropdownInternal
+    comboBox.ShowDropdownInternal = function(comboBox)
+        originalShow(comboBox)
+        local entries = ZO_Menu.items
+        for i = 1, #entries do
+            local entry = entries[i]
+            local control = entries[i].item
+            control.tooltip = choicesTooltips[i]
+            entry.onMouseEnter = control:GetHandler("OnMouseEnter")
+            entry.onMouseExit = control:GetHandler("OnMouseExit")
+            ZO_PreHookHandler(control, "OnMouseEnter", ShowTooltip)
+            ZO_PreHookHandler(control, "OnMouseExit", HideTooltip)
+        end
+    end
+
+    local originalHide = comboBox.HideDropdownInternal
+    comboBox.HideDropdownInternal = function(self)
+        local entries = ZO_Menu.items
+        for i = 1, #entries do
+            local entry = entries[i]
+            local control = entries[i].item
+            control:SetHandler("OnMouseEnter", entry.onMouseEnter)
+            control:SetHandler("OnMouseExit", entry.onMouseExit)
+            control.tooltip = nil
+        end
+        originalHide(self)
+    end
+end
+
+local function UpdateChoices(control, choices, choicesValues, choicesTooltips)
+    control.dropdown:ClearItems() --remove previous choices --(need to call :SetSelectedItem()?)
+    ZO_ClearTable(control.choices)
+
+    --build new list of choices
+    local choices = choices or control.data.choices
+    local choicesValues = choicesValues or control.data.choicesValues
+    local choicesTooltips = choicesTooltips or control.data.choicesTooltips
+
+    if choicesValues then
+        assert(#choices == #choicesValues, "choices and choicesValues need to have the same size")
+    end
+
+    if choicesTooltips then
+        assert(#choices == #choicesTooltips, "choices and choicesTooltips need to have the same size")
+        if not control.scrollHelper then -- only do this for non-scrollable
+            SetupTooltips(control.dropdown, choicesTooltips)
+        end
+    end
+
+    for i = 1, #choices do
+        local entry = control.dropdown:CreateItemEntry(choices[i], DropdownCallback)
+        entry.control = control
+        if choicesValues then
+            entry.value = choicesValues[i]
+        end
+        if choicesTooltips and control.scrollHelper then
+            entry.tooltip = choicesTooltips[i]
+        end
+        control.choices[entry.value or entry.name] = entry.name
+        control.dropdown:AddItem(entry, not control.data.sort and ZO_COMBOBOX_SUPRESS_UPDATE) --if sort type/order isn't specified, then don't sort
+    end
+end
+
+local function GrabSortingInfo(sortInfo)
+    local t, i = {}, 1
+    for info in string.gmatch(sortInfo, "([^%-]+)") do
+        t[i] = info
+        i = i + 1
+    end
+
+    return t
+end
+
+local DEFAULT_VISIBLE_ROWS = 10
+local SCROLLABLE_ENTRY_TEMPLATE_HEIGHT = 25 -- same as in zo_combobox.lua
+local CONTENT_PADDING = 24
+local SCROLLBAR_PADDING = 16
+local PADDING = GetMenuPadding() / 2 -- half the amount looks closer to the regular dropdown
+local ROUNDING_MARGIN = 0.01 -- needed to avoid rare issue with too many anchors processed
+local ScrollableDropdownHelper = ZO_Object:Subclass()
+
+function ScrollableDropdownHelper:New(...)
+    local object = ZO_Object.New(self)
+    object:Initialize(...)
+    return object
+end
+
+function ScrollableDropdownHelper:Initialize(parent, control, visibleRows)
+    local combobox = control.combobox
+    local dropdown = control.dropdown
+    self.parent = parent
+    self.control = control
+    self.combobox = combobox
+    self.dropdown = dropdown
+    self.visibleRows = visibleRows
+
+    -- clear anchors so we can adjust the width dynamically
+    dropdown.m_dropdown:ClearAnchors()
+    dropdown.m_dropdown:SetAnchor(TOPLEFT, combobox, BOTTOMLEFT)
+
+    -- handle dropdown or settingsmenu opening/closing
+    local function onShow() self:OnShow() end
+    local function onHide() self:OnHide() end
+    local function doHide() self:DoHide() end
+
+    ZO_PreHook(dropdown, "ShowDropdownOnMouseUp", onShow)
+    ZO_PreHook(dropdown, "HideDropdownInternal", onHide)
+    combobox:SetHandler("OnEffectivelyHidden", onHide)
+    parent:SetHandler("OnEffectivelyHidden", doHide)
+
+    -- dont fade entries near the edges
+    local scrollList = dropdown.m_scroll
+    scrollList.selectionTemplate = nil
+    scrollList.highlightTemplate = nil
+    ZO_ScrollList_EnableSelection(scrollList, "ZO_SelectionHighlight")
+    ZO_ScrollList_EnableHighlight(scrollList, "ZO_SelectionHighlight")
+    ZO_Scroll_SetUseFadeGradient(scrollList, false)
+
+    -- adjust scroll content anchor to mimic menu padding
+    local scroll = dropdown.m_dropdown:GetNamedChild("Scroll")
+    local anchor1 = {scroll:GetAnchor(0)}
+    local anchor2 = {scroll:GetAnchor(1)}
+    scroll:ClearAnchors()
+    scroll:SetAnchor(anchor1[2], anchor1[3], anchor1[4], anchor1[5] + PADDING, anchor1[6] + PADDING)
+    scroll:SetAnchor(anchor2[2], anchor2[3], anchor2[4], anchor2[5] - PADDING, anchor2[6] - PADDING)
+    ZO_ScrollList_Commit(scrollList)
+
+    -- hook mouse enter/exit
+    local function onMouseEnter(control) self:OnMouseEnter(control) end
+    local function onMouseExit(control) self:OnMouseExit(control) end
+
+    -- adjust row setup to mimic the highlight padding
+    local dataType1 = ZO_ScrollList_GetDataTypeTable(dropdown.m_scroll, 1)
+    local dataType2 = ZO_ScrollList_GetDataTypeTable(dropdown.m_scroll, 2)
+    local oSetup = dataType1.setupCallback -- both types have the same setup function
+    local function SetupEntry(control, data, list)
+        oSetup(control, data, list)
+        control.m_label:SetAnchor(LEFT, nil, nil, 2)
+        -- no need to store old ones since we have full ownership of our dropdown controls
+        if not control.hookedMouseHandlers then --only do it once per control
+            control.hookedMouseHandlers = true
+            ZO_PreHookHandler(control, "OnMouseEnter", onMouseEnter)
+            ZO_PreHookHandler(control, "OnMouseExit", onMouseExit)
+            -- we could also just replace the handlers
+            --control:SetHandler("OnMouseEnter", onMouseEnter)
+            --control:SetHandler("OnMouseExit", onMouseExit)
+        end
+    end
+    dataType1.setupCallback = SetupEntry
+    dataType2.setupCallback = SetupEntry
+
+    -- adjust dimensions based on entries
+    local scrollContent = scroll:GetNamedChild("Contents")
+    ZO_PreHook(dropdown, "AddMenuItems", function()
+        local width = PADDING * 2 + zo_max(self:GetMaxWidth(), combobox:GetWidth())
+        local numItems = #dropdown.m_sortedItems
+        local anchorOffset = 0
+        if(numItems > self.visibleRows) then
+            width = width + CONTENT_PADDING + SCROLLBAR_PADDING
+            anchorOffset = -SCROLLBAR_PADDING
+            numItems = self.visibleRows
+        end
+        scrollContent:SetAnchor(BOTTOMRIGHT, nil, nil, anchorOffset)
+        local height = PADDING * 2 + numItems * (SCROLLABLE_ENTRY_TEMPLATE_HEIGHT + dropdown.m_spacing) - dropdown.m_spacing + ROUNDING_MARGIN
+        dropdown.m_dropdown:SetWidth(width)
+        dropdown.m_dropdown:SetHeight(height)
+    end)
+end
+
+function ScrollableDropdownHelper:OnShow()
+    local dropdown = self.dropdown
+    if dropdown.m_lastParent ~= ZO_Menus then
+        dropdown.m_lastParent = dropdown.m_dropdown:GetParent()
+        dropdown.m_dropdown:SetParent(ZO_Menus)
+        ZO_Menus:BringWindowToTop()
+    end
+end
+
+function ScrollableDropdownHelper:OnHide()
+    local dropdown = self.dropdown
+    if dropdown.m_lastParent then
+        dropdown.m_dropdown:SetParent(dropdown.m_lastParent)
+        dropdown.m_lastParent = nil
+    end
+end
+
+function ScrollableDropdownHelper:DoHide()
+    local dropdown = self.dropdown
+    if dropdown:IsDropdownVisible() then
+        dropdown:HideDropdown()
+    end
+end
+
+function ScrollableDropdownHelper:GetMaxWidth()
+    local dropdown = self.dropdown
+    local dataType = ZO_ScrollList_GetDataTypeTable(dropdown.m_scroll, 1)
+
+    local dummy = dataType.pool:AcquireObject()
+    dataType.setupCallback(dummy, {
+        m_owner = dropdown,
+        name = "Dummy"
+    }, dropdown)
+
+    local maxWidth = 0
+    local label = dummy.m_label
+    local entries = dropdown.m_sortedItems
+    local numItems = #entries
+    for index = 1, numItems do
+        label:SetText(entries[index].name)
+        local width = label:GetTextWidth()
+        if (width > maxWidth) then
+            maxWidth = width
+        end
+    end
+
+    dataType.pool:ReleaseObject(dummy.key)
+    return maxWidth
+end
+
+function ScrollableDropdownHelper:OnMouseEnter(control)
+    -- call original code if we replace instead of hook the handler
+        --ZO_ScrollableComboBox_Entry_OnMouseEnter(control)
+    -- show tooltip
+    if control.m_data.tooltip then
+        InitializeTooltip(InformationTooltip, control, TOPLEFT, 0, 0, BOTTOMRIGHT)
+        SetTooltipText(InformationTooltip, LAM.util.GetStringFromValue(control.m_data.tooltip))
+        InformationTooltipTopLevel:BringWindowToTop()
+    end
+end
+function ScrollableDropdownHelper:OnMouseExit(control)
+    -- call original code if we replace instead of hook the handler
+        --ZO_ScrollableComboBox_Entry_OnMouseExit(control)
+    -- hide tooltip
+    if control.m_data.tooltip then
+        ClearTooltip(InformationTooltip)
+    end
+end
+
+function LAMCreateControl.dropdown(parent, dropdownData, controlName)
+    local control = LAM.util.CreateLabelAndContainerControl(parent, dropdownData, controlName)
+    control.choices = {}
+
+    local countControl = parent
+    local name = parent:GetName()
+    if not name or #name == 0 then
+        countControl = LAMCreateControl
+        name = "LAM"
+    end
+    local comboboxCount = (countControl.comboboxCount or 0) + 1
+    countControl.comboboxCount = comboboxCount
+    control.combobox = wm:CreateControlFromVirtual(zo_strjoin(nil, name, "Combobox", comboboxCount), control.container, dropdownData.scrollable and "ZO_ScrollableComboBox" or "ZO_ComboBox")
+
+    local combobox = control.combobox
+    combobox:SetAnchor(TOPLEFT)
+    combobox:SetDimensions(control.container:GetDimensions())
+    combobox:SetHandler("OnMouseEnter", function() ZO_Options_OnMouseEnter(control) end)
+    combobox:SetHandler("OnMouseExit", function() ZO_Options_OnMouseExit(control) end)
+    control.dropdown = ZO_ComboBox_ObjectFromContainer(combobox)
+    local dropdown = control.dropdown
+    dropdown:SetSortsItems(false) -- need to sort ourselves in order to be able to sort by value
+
+    if dropdownData.scrollable then
+        local visibleRows = type(dropdownData.scrollable) == "number" and dropdownData.scrollable or DEFAULT_VISIBLE_ROWS
+        control.scrollHelper = ScrollableDropdownHelper:New(parent, control, visibleRows)
+    end
+
+    ZO_PreHook(dropdown, "UpdateItems", function(self)
+        assert(not self.m_sortsItems, "built-in dropdown sorting was reactivated, sorting is handled by LAM")
+        if control.m_sortOrder ~= nil and control.m_sortType then
+            local sortKey = next(control.m_sortType)
+            local sortFunc = function(item1, item2) return ZO_TableOrderingFunction(item1, item2, sortKey, control.m_sortType, control.m_sortOrder) end
+            table.sort(self.m_sortedItems, sortFunc)
+        end
+    end)
+
+    if dropdownData.sort then
+        local sortInfo = GrabSortingInfo(dropdownData.sort)
+        control.m_sortType, control.m_sortOrder = SORT_TYPES[sortInfo[1]], SORT_ORDERS[sortInfo[2]]
+    elseif dropdownData.choicesValues then
+        control.m_sortType, control.m_sortOrder = ZO_SORT_ORDER_UP, SORT_BY_VALUE
+    end
+
+    if dropdownData.warning ~= nil or dropdownData.requiresReload then
+        control.warning = wm:CreateControlFromVirtual(nil, control, "ZO_Options_WarningIcon")
+        control.warning:SetAnchor(RIGHT, combobox, LEFT, -5, 0)
+        control.UpdateWarning = LAM.util.UpdateWarning
+        control:UpdateWarning()
+    end
+
+    control.UpdateChoices = UpdateChoices
+    control:UpdateChoices(dropdownData.choices, dropdownData.choicesValues)
+    control.UpdateValue = UpdateValue
+    control:UpdateValue()
+    if dropdownData.disabled ~= nil then
+        control.UpdateDisabled = UpdateDisabled
+        control:UpdateDisabled()
+    end
+
+    LAM.util.RegisterForRefreshIfNeeded(control)
+    LAM.util.RegisterForReloadIfNeeded(control)
+
+    return control
+end
diff --git a/TaosGroupTools/libs/LibAddonMenu-2.0/controls/editbox.lua b/TaosGroupTools/libs/LibAddonMenu-2.0/controls/editbox.lua
index d6baf11..bcc6a7c 100644
--- a/TaosGroupTools/libs/LibAddonMenu-2.0/controls/editbox.lua
+++ b/TaosGroupTools/libs/LibAddonMenu-2.0/controls/editbox.lua
@@ -1,156 +1,156 @@
---[[editboxData = {
-    type = "editbox",
-    name = "My Editbox", -- or string id or function returning a string
-    getFunc = function() return db.text end,
-    setFunc = function(text) db.text = text doStuff() end,
-    tooltip = "Editbox's tooltip text.", -- or string id or function returning a string (optional)
-    isMultiline = true, --boolean (optional)
-    isExtraWide = true, --boolean (optional)
-    width = "full", --or "half" (optional)
-    disabled = function() return db.someBooleanSetting end, --or boolean (optional)
-    warning = "May cause permanent awesomeness.", -- or string id or function returning a string (optional)
-    requiresReload = false, -- boolean, if set to true, the warning text will contain a notice that changes are only applied after an UI reload and any change to the value will make the "Apply Settings" button appear on the panel which will reload the UI when pressed (optional)
-    default = defaults.text, -- default value or function that returns the default value (optional)
-    reference = "MyAddonEditbox" -- unique global reference to control (optional)
-} ]]
-
-
-local widgetVersion = 14
-local LAM = LibStub("LibAddonMenu-2.0")
-if not LAM:RegisterWidget("editbox", widgetVersion) then return end
-
-local wm = WINDOW_MANAGER
-
-local function UpdateDisabled(control)
-    local disable
-    if type(control.data.disabled) == "function" then
-        disable = control.data.disabled()
-    else
-        disable = control.data.disabled
-    end
-
-    if disable then
-        control.label:SetColor(ZO_DEFAULT_DISABLED_COLOR:UnpackRGBA())
-        control.editbox:SetColor(ZO_DEFAULT_DISABLED_MOUSEOVER_COLOR:UnpackRGBA())
-    else
-        control.label:SetColor(ZO_DEFAULT_ENABLED_COLOR:UnpackRGBA())
-        control.editbox:SetColor(ZO_DEFAULT_ENABLED_COLOR:UnpackRGBA())
-    end
-    --control.editbox:SetEditEnabled(not disable)
-    control.editbox:SetMouseEnabled(not disable)
-end
-
-local function UpdateValue(control, forceDefault, value)
-    if forceDefault then --if we are forcing defaults
-        value = LAM.util.GetDefaultValue(control.data.default)
-        control.data.setFunc(value)
-        control.editbox:SetText(value)
-    elseif value then
-        control.data.setFunc(value)
-        --after setting this value, let's refresh the others to see if any should be disabled or have their settings changed
-        LAM.util.RequestRefreshIfNeeded(control)
-    else
-        value = control.data.getFunc()
-        control.editbox:SetText(value)
-    end
-end
-
-local MIN_HEIGHT = 24
-local HALF_WIDTH_LINE_SPACING = 2
-function LAMCreateControl.editbox(parent, editboxData, controlName)
-    local control = LAM.util.CreateLabelAndContainerControl(parent, editboxData, controlName)
-
-    local container = control.container
-    control.bg = wm:CreateControlFromVirtual(nil, container, "ZO_EditBackdrop")
-    local bg = control.bg
-    bg:SetAnchorFill()
-
-    if editboxData.isMultiline then
-        control.editbox = wm:CreateControlFromVirtual(nil, bg, "ZO_DefaultEditMultiLineForBackdrop")
-        control.editbox:SetHandler("OnMouseWheel", function(self, delta)
-            if self:HasFocus() then --only set focus to new spots if the editbox is currently in use
-                local cursorPos = self:GetCursorPosition()
-                local text = self:GetText()
-                local textLen = text:len()
-                local newPos
-                if delta > 0 then --scrolling up
-                    local reverseText = text:reverse()
-                    local revCursorPos = textLen - cursorPos
-                    local revPos = reverseText:find("\n", revCursorPos+1)
-                    newPos = revPos and textLen - revPos
-                else --scrolling down
-                    newPos = text:find("\n", cursorPos+1)
-                end
-                if newPos then --if we found a new line, then scroll, otherwise don't
-                    self:SetCursorPosition(newPos)
-                end
-            end
-        end)
-    else
-        control.editbox = wm:CreateControlFromVirtual(nil, bg, "ZO_DefaultEditForBackdrop")
-    end
-    local editbox = control.editbox
-    editbox:SetText(editboxData.getFunc())
-    editbox:SetMaxInputChars(3000)
-    editbox:SetHandler("OnFocusLost", function(self) control:UpdateValue(false, self:GetText()) end)
-    editbox:SetHandler("OnEscape", function(self) self:LoseFocus() control:UpdateValue(false, self:GetText()) end)
-    editbox:SetHandler("OnMouseEnter", function() ZO_Options_OnMouseEnter(control) end)
-    editbox:SetHandler("OnMouseExit", function() ZO_Options_OnMouseExit(control) end)
-
-    local MIN_WIDTH = (parent.GetWidth and (parent:GetWidth() / 10)) or (parent.panel.GetWidth and (parent.panel:GetWidth() / 10)) or 0
-
-    control.label:ClearAnchors()
-    container:ClearAnchors()
-
-    control.label:SetAnchor(TOPLEFT, control, TOPLEFT, 0, 0)
-    container:SetAnchor(BOTTOMRIGHT, control, BOTTOMRIGHT, 0, 0)
-
-    if control.isHalfWidth then
-        container:SetAnchor(BOTTOMRIGHT, control, BOTTOMRIGHT, 0, 0)
-    end
-
-    if editboxData.isExtraWide then
-        container:SetAnchor(BOTTOMLEFT, control, BOTTOMLEFT, 0, 0)
-    else
-        container:SetWidth(MIN_WIDTH * 3.2)
-    end
-
-    if editboxData.isMultiline then
-        container:SetHeight(MIN_HEIGHT * 3)
-    else
-        container:SetHeight(MIN_HEIGHT)
-    end
-
-    if control.isHalfWidth ~= true and editboxData.isExtraWide ~= true then
-        control:SetHeight(container:GetHeight())
-    else
-        control:SetHeight(container:GetHeight() + control.label:GetHeight())
-    end
-
-    editbox:ClearAnchors()
-    editbox:SetAnchor(TOPLEFT, container, TOPLEFT, 2, 2)
-    editbox:SetAnchor(BOTTOMRIGHT, container, BOTTOMRIGHT, -2, -2)
-
-    if editboxData.warning ~= nil or editboxData.requiresReload then
-        control.warning = wm:CreateControlFromVirtual(nil, control, "ZO_Options_WarningIcon")
-        if editboxData.isExtraWide then
-            control.warning:SetAnchor(BOTTOMRIGHT, control.bg, TOPRIGHT, 2, 0)
-        else
-            control.warning:SetAnchor(TOPRIGHT, control.bg, TOPLEFT, -5, 0)
-        end
-        control.UpdateWarning = LAM.util.UpdateWarning
-        control:UpdateWarning()
-    end
-
-    control.UpdateValue = UpdateValue
-    control:UpdateValue()
-    if editboxData.disabled ~= nil then
-        control.UpdateDisabled = UpdateDisabled
-        control:UpdateDisabled()
-    end
-
-    LAM.util.RegisterForRefreshIfNeeded(control)
-    LAM.util.RegisterForReloadIfNeeded(control)
-
-    return control
-end
+--[[editboxData = {
+    type = "editbox",
+    name = "My Editbox", -- or string id or function returning a string
+    getFunc = function() return db.text end,
+    setFunc = function(text) db.text = text doStuff() end,
+    tooltip = "Editbox's tooltip text.", -- or string id or function returning a string (optional)
+    isMultiline = true, --boolean (optional)
+    isExtraWide = true, --boolean (optional)
+    width = "full", --or "half" (optional)
+    disabled = function() return db.someBooleanSetting end, --or boolean (optional)
+    warning = "May cause permanent awesomeness.", -- or string id or function returning a string (optional)
+    requiresReload = false, -- boolean, if set to true, the warning text will contain a notice that changes are only applied after an UI reload and any change to the value will make the "Apply Settings" button appear on the panel which will reload the UI when pressed (optional)
+    default = defaults.text, -- default value or function that returns the default value (optional)
+    reference = "MyAddonEditbox" -- unique global reference to control (optional)
+} ]]
+
+
+local widgetVersion = 14
+local LAM = LibStub("LibAddonMenu-2.0")
+if not LAM:RegisterWidget("editbox", widgetVersion) then return end
+
+local wm = WINDOW_MANAGER
+
+local function UpdateDisabled(control)
+    local disable
+    if type(control.data.disabled) == "function" then
+        disable = control.data.disabled()
+    else
+        disable = control.data.disabled
+    end
+
+    if disable then
+        control.label:SetColor(ZO_DEFAULT_DISABLED_COLOR:UnpackRGBA())
+        control.editbox:SetColor(ZO_DEFAULT_DISABLED_MOUSEOVER_COLOR:UnpackRGBA())
+    else
+        control.label:SetColor(ZO_DEFAULT_ENABLED_COLOR:UnpackRGBA())
+        control.editbox:SetColor(ZO_DEFAULT_ENABLED_COLOR:UnpackRGBA())
+    end
+    --control.editbox:SetEditEnabled(not disable)
+    control.editbox:SetMouseEnabled(not disable)
+end
+
+local function UpdateValue(control, forceDefault, value)
+    if forceDefault then --if we are forcing defaults
+        value = LAM.util.GetDefaultValue(control.data.default)
+        control.data.setFunc(value)
+        control.editbox:SetText(value)
+    elseif value then
+        control.data.setFunc(value)
+        --after setting this value, let's refresh the others to see if any should be disabled or have their settings changed
+        LAM.util.RequestRefreshIfNeeded(control)
+    else
+        value = control.data.getFunc()
+        control.editbox:SetText(value)
+    end
+end
+
+local MIN_HEIGHT = 24
+local HALF_WIDTH_LINE_SPACING = 2
+function LAMCreateControl.editbox(parent, editboxData, controlName)
+    local control = LAM.util.CreateLabelAndContainerControl(parent, editboxData, controlName)
+
+    local container = control.container
+    control.bg = wm:CreateControlFromVirtual(nil, container, "ZO_EditBackdrop")
+    local bg = control.bg
+    bg:SetAnchorFill()
+
+    if editboxData.isMultiline then
+        control.editbox = wm:CreateControlFromVirtual(nil, bg, "ZO_DefaultEditMultiLineForBackdrop")
+        control.editbox:SetHandler("OnMouseWheel", function(self, delta)
+            if self:HasFocus() then --only set focus to new spots if the editbox is currently in use
+                local cursorPos = self:GetCursorPosition()
+                local text = self:GetText()
+                local textLen = text:len()
+                local newPos
+                if delta > 0 then --scrolling up
+                    local reverseText = text:reverse()
+                    local revCursorPos = textLen - cursorPos
+                    local revPos = reverseText:find("\n", revCursorPos+1)
+                    newPos = revPos and textLen - revPos
+                else --scrolling down
+                    newPos = text:find("\n", cursorPos+1)
+                end
+                if newPos then --if we found a new line, then scroll, otherwise don't
+                    self:SetCursorPosition(newPos)
+                end
+            end
+        end)
+    else
+        control.editbox = wm:CreateControlFromVirtual(nil, bg, "ZO_DefaultEditForBackdrop")
+    end
+    local editbox = control.editbox
+    editbox:SetText(editboxData.getFunc())
+    editbox:SetMaxInputChars(3000)
+    editbox:SetHandler("OnFocusLost", function(self) control:UpdateValue(false, self:GetText()) end)
+    editbox:SetHandler("OnEscape", function(self) self:LoseFocus() control:UpdateValue(false, self:GetText()) end)
+    editbox:SetHandler("OnMouseEnter", function() ZO_Options_OnMouseEnter(control) end)
+    editbox:SetHandler("OnMouseExit", function() ZO_Options_OnMouseExit(control) end)
+
+    local MIN_WIDTH = (parent.GetWidth and (parent:GetWidth() / 10)) or (parent.panel.GetWidth and (parent.panel:GetWidth() / 10)) or 0
+
+    control.label:ClearAnchors()
+    container:ClearAnchors()
+
+    control.label:SetAnchor(TOPLEFT, control, TOPLEFT, 0, 0)
+    container:SetAnchor(BOTTOMRIGHT, control, BOTTOMRIGHT, 0, 0)
+
+    if control.isHalfWidth then
+        container:SetAnchor(BOTTOMRIGHT, control, BOTTOMRIGHT, 0, 0)
+    end
+
+    if editboxData.isExtraWide then
+        container:SetAnchor(BOTTOMLEFT, control, BOTTOMLEFT, 0, 0)
+    else
+        container:SetWidth(MIN_WIDTH * 3.2)
+    end
+
+    if editboxData.isMultiline then
+        container:SetHeight(MIN_HEIGHT * 3)
+    else
+        container:SetHeight(MIN_HEIGHT)
+    end
+
+    if control.isHalfWidth ~= true and editboxData.isExtraWide ~= true then
+        control:SetHeight(container:GetHeight())
+    else
+        control:SetHeight(container:GetHeight() + control.label:GetHeight())
+    end
+
+    editbox:ClearAnchors()
+    editbox:SetAnchor(TOPLEFT, container, TOPLEFT, 2, 2)
+    editbox:SetAnchor(BOTTOMRIGHT, container, BOTTOMRIGHT, -2, -2)
+
+    if editboxData.warning ~= nil or editboxData.requiresReload then
+        control.warning = wm:CreateControlFromVirtual(nil, control, "ZO_Options_WarningIcon")
+        if editboxData.isExtraWide then
+            control.warning:SetAnchor(BOTTOMRIGHT, control.bg, TOPRIGHT, 2, 0)
+        else
+            control.warning:SetAnchor(TOPRIGHT, control.bg, TOPLEFT, -5, 0)
+        end
+        control.UpdateWarning = LAM.util.UpdateWarning
+        control:UpdateWarning()
+    end
+
+    control.UpdateValue = UpdateValue
+    control:UpdateValue()
+    if editboxData.disabled ~= nil then
+        control.UpdateDisabled = UpdateDisabled
+        control:UpdateDisabled()
+    end
+
+    LAM.util.RegisterForRefreshIfNeeded(control)
+    LAM.util.RegisterForReloadIfNeeded(control)
+
+    return control
+end
diff --git a/TaosGroupTools/libs/LibAddonMenu-2.0/controls/header.lua b/TaosGroupTools/libs/LibAddonMenu-2.0/controls/header.lua
index eadff38..3290c89 100644
--- a/TaosGroupTools/libs/LibAddonMenu-2.0/controls/header.lua
+++ b/TaosGroupTools/libs/LibAddonMenu-2.0/controls/header.lua
@@ -1,42 +1,42 @@
---[[headerData = {
-    type = "header",
-    name = "My Header", -- or string id or function returning a string
-    width = "full", --or "half" (optional)
-    reference = "MyAddonHeader" -- unique global reference to control (optional)
-} ]]
-
-
-local widgetVersion = 8
-local LAM = LibStub("LibAddonMenu-2.0")
-if not LAM:RegisterWidget("header", widgetVersion) then return end
-
-local wm = WINDOW_MANAGER
-
-local function UpdateValue(control)
-    control.header:SetText(LAM.util.GetStringFromValue(control.data.name))
-end
-
-local MIN_HEIGHT = 30
-function LAMCreateControl.header(parent, headerData, controlName)
-    local control = LAM.util.CreateBaseControl(parent, headerData, controlName)
-    local isHalfWidth = control.isHalfWidth
-    local width = control:GetWidth()
-    control:SetDimensions(isHalfWidth and width / 2 or width, MIN_HEIGHT)
-
-    control.divider = wm:CreateControlFromVirtual(nil, control, "ZO_Options_Divider")
-    local divider = control.divider
-    divider:SetWidth(isHalfWidth and width / 2 or width)
-    divider:SetAnchor(TOPLEFT)
-
-    control.header = wm:CreateControlFromVirtual(nil, control, "ZO_Options_SectionTitleLabel")
-    local header = control.header
-    header:SetAnchor(TOPLEFT, divider, BOTTOMLEFT)
-    header:SetAnchor(BOTTOMRIGHT)
-    header:SetText(LAM.util.GetStringFromValue(headerData.name))
-
-    control.UpdateValue = UpdateValue
-
-    LAM.util.RegisterForRefreshIfNeeded(control)
-
-    return control
-end
+--[[headerData = {
+    type = "header",
+    name = "My Header", -- or string id or function returning a string
+    width = "full", --or "half" (optional)
+    reference = "MyAddonHeader" -- unique global reference to control (optional)
+} ]]
+
+
+local widgetVersion = 8
+local LAM = LibStub("LibAddonMenu-2.0")
+if not LAM:RegisterWidget("header", widgetVersion) then return end
+
+local wm = WINDOW_MANAGER
+
+local function UpdateValue(control)
+    control.header:SetText(LAM.util.GetStringFromValue(control.data.name))
+end
+
+local MIN_HEIGHT = 30
+function LAMCreateControl.header(parent, headerData, controlName)
+    local control = LAM.util.CreateBaseControl(parent, headerData, controlName)
+    local isHalfWidth = control.isHalfWidth
+    local width = control:GetWidth()
+    control:SetDimensions(isHalfWidth and width / 2 or width, MIN_HEIGHT)
+
+    control.divider = wm:CreateControlFromVirtual(nil, control, "ZO_Options_Divider")
+    local divider = control.divider
+    divider:SetWidth(isHalfWidth and width / 2 or width)
+    divider:SetAnchor(TOPLEFT)
+
+    control.header = wm:CreateControlFromVirtual(nil, control, "ZO_Options_SectionTitleLabel")
+    local header = control.header
+    header:SetAnchor(TOPLEFT, divider, BOTTOMLEFT)
+    header:SetAnchor(BOTTOMRIGHT)
+    header:SetText(LAM.util.GetStringFromValue(headerData.name))
+
+    control.UpdateValue = UpdateValue
+
+    LAM.util.RegisterForRefreshIfNeeded(control)
+
+    return control
+end
diff --git a/TaosGroupTools/libs/LibAddonMenu-2.0/controls/iconpicker.lua b/TaosGroupTools/libs/LibAddonMenu-2.0/controls/iconpicker.lua
index 65c7782..3485740 100644
--- a/TaosGroupTools/libs/LibAddonMenu-2.0/controls/iconpicker.lua
+++ b/TaosGroupTools/libs/LibAddonMenu-2.0/controls/iconpicker.lua
@@ -1,436 +1,436 @@
---[[iconpickerData = {
-    type = "iconpicker",
-    name = "My Icon Picker", -- or string id or function returning a string
-    choices = {"texture path 1", "texture path 2", "texture path 3"},
-    getFunc = function() return db.var end,
-    setFunc = function(var) db.var = var doStuff() end,
-    tooltip = "Color Picker's tooltip text.", -- or string id or function returning a string (optional)
-    choicesTooltips = {"icon tooltip 1", "icon tooltip 2", "icon tooltip 3"}, -- or array of string ids or array of functions returning a string (optional)
-    maxColumns = 5, -- number of icons in one row (optional)
-    visibleRows = 4.5, -- number of visible rows (optional)
-    iconSize = 28, -- size of the icons (optional)
-    defaultColor = ZO_ColorDef:New("FFFFFF"), -- default color of the icons (optional)
-    width = "full", --or "half" (optional)
-    beforeShow = function(control, iconPicker) return preventShow end, --(optional)
-    disabled = function() return db.someBooleanSetting end, --or boolean (optional)
-    warning = "May cause permanent awesomeness.", -- or string id or function returning a string (optional)
-    requiresReload = false, -- boolean, if set to true, the warning text will contain a notice that changes are only applied after an UI reload and any change to the value will make the "Apply Settings" button appear on the panel which will reload the UI when pressed (optional)
-    default = defaults.var, -- default value or function that returns the default value (optional)
-    reference = "MyAddonIconPicker" -- unique global reference to control (optional)
-} ]]
-
-local widgetVersion = 8
-local LAM = LibStub("LibAddonMenu-2.0")
-if not LAM:RegisterWidget("iconpicker", widgetVersion) then return end
-
-local wm = WINDOW_MANAGER
-
-local IconPickerMenu = ZO_Object:Subclass()
-local iconPicker
-LAM.util.GetIconPickerMenu = function()
-    if not iconPicker then
-        iconPicker = IconPickerMenu:New("LAMIconPicker")
-        local sceneFragment = LAM:GetAddonSettingsFragment()
-        ZO_PreHook(sceneFragment, "OnHidden", function()
-            if not iconPicker.control:IsHidden() then
-                iconPicker:Clear()
-            end
-        end)
-    end
-    return iconPicker
-end
-
-function IconPickerMenu:New(...)
-    local object = ZO_Object.New(self)
-    object:Initialize(...)
-    return object
-end
-
-function IconPickerMenu:Initialize(name)
-    local control = wm:CreateTopLevelWindow(name)
-    control:SetDrawTier(DT_HIGH)
-    control:SetHidden(true)
-    self.control = control
-
-    local scrollContainer = wm:CreateControlFromVirtual(name .. "ScrollContainer", control, "ZO_ScrollContainer")
-    -- control:SetDimensions(control.container:GetWidth(), height) -- adjust to icon size / col count
-    scrollContainer:SetAnchorFill()
-    ZO_Scroll_SetUseFadeGradient(scrollContainer, false)
-    ZO_Scroll_SetHideScrollbarOnDisable(scrollContainer, false)
-    ZO_VerticalScrollbarBase_OnMouseExit(scrollContainer:GetNamedChild("ScrollBar")) -- scrollbar initialization seems to be broken so we force it to update the correct alpha value
-    local scroll = GetControl(scrollContainer, "ScrollChild")
-    self.scroll = scroll
-    self.scrollContainer = scrollContainer
-
-    local bg = wm:CreateControl(nil, scrollContainer, CT_BACKDROP)
-    bg:SetAnchor(TOPLEFT, scrollContainer, TOPLEFT, 0, -3)
-    bg:SetAnchor(BOTTOMRIGHT, scrollContainer, BOTTOMRIGHT, 2, 5)
-    bg:SetEdgeTexture("EsoUI\\Art\\Tooltips\\UI-Border.dds", 128, 16)
-    bg:SetCenterTexture("EsoUI\\Art\\Tooltips\\UI-TooltipCenter.dds")
-    bg:SetInsets(16, 16, -16, -16)
-
-    local mungeOverlay = wm:CreateControl(nil, bg, CT_TEXTURE)
-    mungeOverlay:SetTexture("EsoUI/Art/Tooltips/munge_overlay.dds")
-    mungeOverlay:SetDrawLevel(1)
-    mungeOverlay:SetAddressMode(TEX_MODE_WRAP)
-    mungeOverlay:SetAnchorFill()
-
-    local mouseOver = wm:CreateControl(nil, scrollContainer, CT_TEXTURE)
-    mouseOver:SetDrawLevel(2)
-    mouseOver:SetTexture("EsoUI/Art/Buttons/minmax_mouseover.dds")
-    mouseOver:SetHidden(true)
-
-    local function IconFactory(pool)
-        local icon = wm:CreateControl(name .. "Entry" .. pool:GetNextControlId(), scroll, CT_TEXTURE)
-        icon:SetMouseEnabled(true)
-        icon:SetDrawLevel(3)
-        icon:SetHandler("OnMouseEnter", function()
-            mouseOver:SetAnchor(TOPLEFT, icon, TOPLEFT, 0, 0)
-            mouseOver:SetAnchor(BOTTOMRIGHT, icon, BOTTOMRIGHT, 0, 0)
-            mouseOver:SetHidden(false)
-            if self.customOnMouseEnter then
-                self.customOnMouseEnter(icon)
-            else
-                self:OnMouseEnter(icon)
-            end
-        end)
-        icon:SetHandler("OnMouseExit", function()
-            mouseOver:ClearAnchors()
-            mouseOver:SetHidden(true)
-            if self.customOnMouseExit then
-                self.customOnMouseExit(icon)
-            else
-                self:OnMouseExit(icon)
-            end
-        end)
-        icon:SetHandler("OnMouseUp", function(control, ...)
-            PlaySound("Click")
-            icon.OnSelect(icon, icon.texture)
-            self:Clear()
-        end)
-        return icon
-    end
-
-    local function ResetFunction(icon)
-        icon:ClearAnchors()
-    end
-
-    self.iconPool = ZO_ObjectPool:New(IconFactory, ResetFunction)
-    self:SetMaxColumns(1)
-    self.icons = {}
-    self.color = ZO_DEFAULT_ENABLED_COLOR
-
-    EVENT_MANAGER:RegisterForEvent(name .. "_OnGlobalMouseUp", EVENT_GLOBAL_MOUSE_UP, function()
-        if self.refCount ~= nil then
-            local moc = wm:GetMouseOverControl()
-            if(moc:GetOwningWindow() ~= control) then
-                self.refCount = self.refCount - 1
-                if self.refCount <= 0 then
-                    self:Clear()
-                end
-            end
-        end
-    end)
-end
-
-function IconPickerMenu:OnMouseEnter(icon)
-    InitializeTooltip(InformationTooltip, icon, TOPLEFT, 0, 0, BOTTOMRIGHT)
-    SetTooltipText(InformationTooltip, LAM.util.GetStringFromValue(icon.tooltip))
-    InformationTooltipTopLevel:BringWindowToTop()
-end
-
-function IconPickerMenu:OnMouseExit(icon)
-    ClearTooltip(InformationTooltip)
-end
-
-function IconPickerMenu:SetMaxColumns(value)
-    self.maxCols = value ~= nil and value or 5
-end
-
-local DEFAULT_SIZE = 28
-function IconPickerMenu:SetIconSize(value)
-    local iconSize = DEFAULT_SIZE
-    if value ~= nil then iconSize = math.max(iconSize, value) end
-    self.iconSize = iconSize
-end
-
-function IconPickerMenu:SetVisibleRows(value)
-    self.visibleRows = value ~= nil and value or 4.5
-end
-
-function IconPickerMenu:SetMouseHandlers(onEnter, onExit)
-    self.customOnMouseEnter = onEnter
-    self.customOnMouseExit = onExit
-end
-
-function IconPickerMenu:UpdateDimensions()
-    local iconSize = self.iconSize
-    local width = iconSize * self.maxCols + 20
-    local height = iconSize * self.visibleRows
-    self.control:SetDimensions(width, height)
-
-    local icons = self.icons
-    for i = 1, #icons do
-        local icon = icons[i]
-        icon:SetDimensions(iconSize, iconSize)
-    end
-end
-
-function IconPickerMenu:UpdateAnchors()
-    local iconSize = self.iconSize
-    local col, maxCols = 1, self.maxCols
-    local previousCol, previousRow
-    local scroll = self.scroll
-    local icons = self.icons
-
-    for i = 1, #icons do
-        local icon = icons[i]
-        icon:ClearAnchors()
-        if i == 1 then
-            icon:SetAnchor(TOPLEFT, scroll, TOPLEFT, 0, 0)
-            previousRow = icon
-        elseif col == 1 then
-            icon:SetAnchor(TOPLEFT, previousRow, BOTTOMLEFT, 0, 0)
-            previousRow = icon
-        else
-            icon:SetAnchor(TOPLEFT, previousCol, TOPRIGHT, 0, 0)
-        end
-        previousCol = icon
-        col = col >= maxCols and 1 or col + 1
-    end
-end
-
-function IconPickerMenu:Clear()
-    self.icons = {}
-    self.iconPool:ReleaseAllObjects()
-    self.control:SetHidden(true)
-    self.color = ZO_DEFAULT_ENABLED_COLOR
-    self.refCount = nil
-    self.parent = nil
-    self.customOnMouseEnter = nil
-    self.customOnMouseExit = nil
-end
-
-function IconPickerMenu:AddIcon(texturePath, callback, tooltip)
-    local icon, key = self.iconPool:AcquireObject()
-    icon:SetTexture(texturePath)
-    icon:SetColor(self.color:UnpackRGBA())
-    icon.texture = texturePath
-    icon.tooltip = tooltip
-    icon.OnSelect = callback
-    self.icons[#self.icons + 1] = icon
-end
-
-function IconPickerMenu:Show(parent)
-    if #self.icons == 0 then return false end
-    if not self.control:IsHidden() then self:Clear() return false end
-    self:UpdateDimensions()
-    self:UpdateAnchors()
-
-    local control = self.control
-    control:ClearAnchors()
-    control:SetAnchor(TOPLEFT, parent, BOTTOMLEFT, 0, 8)
-    control:SetHidden(false)
-    control:BringWindowToTop()
-    self.parent = parent
-    self.refCount = 2
-
-    return true
-end
-
-function IconPickerMenu:SetColor(color)
-    local icons = self.icons
-    self.color = color
-    for i = 1, #icons do
-        local icon = icons[i]
-        icon:SetColor(color:UnpackRGBA())
-    end
-end
-
--------------------------------------------------------------
-
-local function UpdateChoices(control, choices, choicesTooltips)
-    local data = control.data
-    if not choices then
-        choices, choicesTooltips = data.choices, data.choicesTooltips or {}
-    end
-    local addedChoices = {}
-
-    local iconPicker = LAM.util.GetIconPickerMenu()
-    iconPicker:Clear()
-    for i = 1, #choices do
-        local texture = choices[i]
-        if not addedChoices[texture] then -- remove duplicates
-            iconPicker:AddIcon(choices[i], function(self, texture)
-                control.icon:SetTexture(texture)
-                data.setFunc(texture)
-                LAM.util.RequestRefreshIfNeeded(control)
-            end, LAM.util.GetStringFromValue(choicesTooltips[i]))
-        addedChoices[texture] = true
-        end
-    end
-end
-
-local function IsDisabled(control)
-    if type(control.data.disabled) == "function" then
-        return control.data.disabled()
-    else
-        return control.data.disabled
-    end
-end
-
-local function SetColor(control, color)
-    local icon = control.icon
-    if IsDisabled(control) then
-        icon:SetColor(ZO_DEFAULT_DISABLED_COLOR:UnpackRGBA())
-    else
-        icon.color = color or control.data.defaultColor or ZO_DEFAULT_ENABLED_COLOR
-        icon:SetColor(icon.color:UnpackRGBA())
-    end
-
-    local iconPicker = LAM.util.GetIconPickerMenu()
-    if iconPicker.parent == control.container and not iconPicker.control:IsHidden() then
-        iconPicker:SetColor(icon.color)
-    end
-end
-
-local function UpdateDisabled(control)
-    local disable = IsDisabled(control)
-
-    control.dropdown:SetMouseEnabled(not disable)
-    control.dropdownButton:SetEnabled(not disable)
-
-    local iconPicker = LAM.util.GetIconPickerMenu()
-    if iconPicker.parent == control.container and not iconPicker.control:IsHidden() then
-        iconPicker:Clear()
-    end
-
-    SetColor(control, control.icon.color)
-    if disable then
-        control.label:SetColor(ZO_DEFAULT_DISABLED_COLOR:UnpackRGBA())
-    else
-        control.label:SetColor(ZO_DEFAULT_ENABLED_COLOR:UnpackRGBA())
-    end
-end
-
-local function UpdateValue(control, forceDefault, value)
-    if forceDefault then --if we are forcing defaults
-        value = LAM.util.GetDefaultValue(control.data.default)
-        control.data.setFunc(value)
-        control.icon:SetTexture(value)
-    elseif value then
-        control.data.setFunc(value)
-        --after setting this value, let's refresh the others to see if any should be disabled or have their settings changed
-        LAM.util.RequestRefreshIfNeeded(control)
-    else
-        value = control.data.getFunc()
-        control.icon:SetTexture(value)
-    end
-end
-
-local MIN_HEIGHT = 26
-local HALF_WIDTH_LINE_SPACING = 2
-local function SetIconSize(control, size)
-    local icon = control.icon
-    icon.size = size
-    icon:SetDimensions(size, size)
-
-    local height = size + 4
-    control.dropdown:SetDimensions(size + 20, height)
-    height = math.max(height, MIN_HEIGHT)
-    control.container:SetHeight(height)
-    if control.lineControl then
-        control.lineControl:SetHeight(MIN_HEIGHT + size + HALF_WIDTH_LINE_SPACING)
-    else
-        control:SetHeight(height)
-    end
-
-    local iconPicker = LAM.util.GetIconPickerMenu()
-    if iconPicker.parent == control.container and not iconPicker.control:IsHidden() then
-        iconPicker:SetIconSize(size)
-        iconPicker:UpdateDimensions()
-        iconPicker:UpdateAnchors()
-    end
-end
-
-function LAMCreateControl.iconpicker(parent, iconpickerData, controlName)
-    local control = LAM.util.CreateLabelAndContainerControl(parent, iconpickerData, controlName)
-
-    local function ShowIconPicker()
-        local iconPicker = LAM.util.GetIconPickerMenu()
-        if iconPicker.parent == control.container then
-            iconPicker:Clear()
-        else
-            iconPicker:SetMaxColumns(iconpickerData.maxColumns)
-            iconPicker:SetVisibleRows(iconpickerData.visibleRows)
-            iconPicker:SetIconSize(control.icon.size)
-            UpdateChoices(control)
-            iconPicker:SetColor(control.icon.color)
-            if iconpickerData.beforeShow then
-                if iconpickerData.beforeShow(control, iconPicker) then
-                    iconPicker:Clear()
-                    return
-                end
-            end
-            iconPicker:Show(control.container)
-        end
-    end
-
-    local iconSize = iconpickerData.iconSize ~= nil and iconpickerData.iconSize or DEFAULT_SIZE
-    control.dropdown = wm:CreateControl(nil, control.container, CT_CONTROL)
-    local dropdown = control.dropdown
-    dropdown:SetAnchor(LEFT, control.container, LEFT, 0, 0)
-    dropdown:SetMouseEnabled(true)
-    dropdown:SetHandler("OnMouseUp", ShowIconPicker)
-    dropdown:SetHandler("OnMouseEnter", function() ZO_Options_OnMouseEnter(control) end)
-    dropdown:SetHandler("OnMouseExit", function() ZO_Options_OnMouseExit(control) end)
-
-    control.icon = wm:CreateControl(nil, dropdown, CT_TEXTURE)
-    local icon = control.icon
-    icon:SetAnchor(LEFT, dropdown, LEFT, 3, 0)
-    icon:SetDrawLevel(2)
-
-    local dropdownButton = wm:CreateControlFromVirtual(nil, dropdown, "ZO_DropdownButton")
-    dropdownButton:SetDimensions(16, 16)
-    dropdownButton:SetHandler("OnClicked", ShowIconPicker)
-    dropdownButton:SetAnchor(RIGHT, dropdown, RIGHT, -3, 0)
-    control.dropdownButton = dropdownButton
-
-    control.bg = wm:CreateControl(nil, dropdown, CT_BACKDROP)
-    local bg = control.bg
-    bg:SetAnchor(TOPLEFT, dropdown, TOPLEFT, 0, -3)
-    bg:SetAnchor(BOTTOMRIGHT, dropdown, BOTTOMRIGHT, 2, 5)
-    bg:SetEdgeTexture("EsoUI/Art/Tooltips/UI-Border.dds", 128, 16)
-    bg:SetCenterTexture("EsoUI/Art/Tooltips/UI-TooltipCenter.dds")
-    bg:SetInsets(16, 16, -16, -16)
-    local mungeOverlay = wm:CreateControl(nil, bg, CT_TEXTURE)
-    mungeOverlay:SetTexture("EsoUI/Art/Tooltips/munge_overlay.dds")
-    mungeOverlay:SetDrawLevel(1)
-    mungeOverlay:SetAddressMode(TEX_MODE_WRAP)
-    mungeOverlay:SetAnchorFill()
-
-    if iconpickerData.warning ~= nil or iconpickerData.requiresReload then
-        control.warning = wm:CreateControlFromVirtual(nil, control, "ZO_Options_WarningIcon")
-        control.warning:SetAnchor(RIGHT, control.container, LEFT, -5, 0)
-        control.UpdateWarning = LAM.util.UpdateWarning
-        control:UpdateWarning()
-    end
-
-    control.UpdateChoices = UpdateChoices
-    control.UpdateValue = UpdateValue
-    control:UpdateValue()
-    control.SetColor = SetColor
-    control:SetColor()
-    control.SetIconSize = SetIconSize
-    control:SetIconSize(iconSize)
-
-    if iconpickerData.disabled ~= nil then
-        control.UpdateDisabled = UpdateDisabled
-        control:UpdateDisabled()
-    end
-
-    LAM.util.RegisterForRefreshIfNeeded(control)
-    LAM.util.RegisterForReloadIfNeeded(control)
-
-    return control
-end
+--[[iconpickerData = {
+    type = "iconpicker",
+    name = "My Icon Picker", -- or string id or function returning a string
+    choices = {"texture path 1", "texture path 2", "texture path 3"},
+    getFunc = function() return db.var end,
+    setFunc = function(var) db.var = var doStuff() end,
+    tooltip = "Color Picker's tooltip text.", -- or string id or function returning a string (optional)
+    choicesTooltips = {"icon tooltip 1", "icon tooltip 2", "icon tooltip 3"}, -- or array of string ids or array of functions returning a string (optional)
+    maxColumns = 5, -- number of icons in one row (optional)
+    visibleRows = 4.5, -- number of visible rows (optional)
+    iconSize = 28, -- size of the icons (optional)
+    defaultColor = ZO_ColorDef:New("FFFFFF"), -- default color of the icons (optional)
+    width = "full", --or "half" (optional)
+    beforeShow = function(control, iconPicker) return preventShow end, --(optional)
+    disabled = function() return db.someBooleanSetting end, --or boolean (optional)
+    warning = "May cause permanent awesomeness.", -- or string id or function returning a string (optional)
+    requiresReload = false, -- boolean, if set to true, the warning text will contain a notice that changes are only applied after an UI reload and any change to the value will make the "Apply Settings" button appear on the panel which will reload the UI when pressed (optional)
+    default = defaults.var, -- default value or function that returns the default value (optional)
+    reference = "MyAddonIconPicker" -- unique global reference to control (optional)
+} ]]
+
+local widgetVersion = 8
+local LAM = LibStub("LibAddonMenu-2.0")
+if not LAM:RegisterWidget("iconpicker", widgetVersion) then return end
+
+local wm = WINDOW_MANAGER
+
+local IconPickerMenu = ZO_Object:Subclass()
+local iconPicker
+LAM.util.GetIconPickerMenu = function()
+    if not iconPicker then
+        iconPicker = IconPickerMenu:New("LAMIconPicker")
+        local sceneFragment = LAM:GetAddonSettingsFragment()
+        ZO_PreHook(sceneFragment, "OnHidden", function()
+            if not iconPicker.control:IsHidden() then
+                iconPicker:Clear()
+            end
+        end)
+    end
+    return iconPicker
+end
+
+function IconPickerMenu:New(...)
+    local object = ZO_Object.New(self)
+    object:Initialize(...)
+    return object
+end
+
+function IconPickerMenu:Initialize(name)
+    local control = wm:CreateTopLevelWindow(name)
+    control:SetDrawTier(DT_HIGH)
+    control:SetHidden(true)
+    self.control = control
+
+    local scrollContainer = wm:CreateControlFromVirtual(name .. "ScrollContainer", control, "ZO_ScrollContainer")
+    -- control:SetDimensions(control.container:GetWidth(), height) -- adjust to icon size / col count
+    scrollContainer:SetAnchorFill()
+    ZO_Scroll_SetUseFadeGradient(scrollContainer, false)
+    ZO_Scroll_SetHideScrollbarOnDisable(scrollContainer, false)
+    ZO_VerticalScrollbarBase_OnMouseExit(scrollContainer:GetNamedChild("ScrollBar")) -- scrollbar initialization seems to be broken so we force it to update the correct alpha value
+    local scroll = GetControl(scrollContainer, "ScrollChild")
+    self.scroll = scroll
+    self.scrollContainer = scrollContainer
+
+    local bg = wm:CreateControl(nil, scrollContainer, CT_BACKDROP)
+    bg:SetAnchor(TOPLEFT, scrollContainer, TOPLEFT, 0, -3)
+    bg:SetAnchor(BOTTOMRIGHT, scrollContainer, BOTTOMRIGHT, 2, 5)
+    bg:SetEdgeTexture("EsoUI\\Art\\Tooltips\\UI-Border.dds", 128, 16)
+    bg:SetCenterTexture("EsoUI\\Art\\Tooltips\\UI-TooltipCenter.dds")
+    bg:SetInsets(16, 16, -16, -16)
+
+    local mungeOverlay = wm:CreateControl(nil, bg, CT_TEXTURE)
+    mungeOverlay:SetTexture("EsoUI/Art/Tooltips/munge_overlay.dds")
+    mungeOverlay:SetDrawLevel(1)
+    mungeOverlay:SetAddressMode(TEX_MODE_WRAP)
+    mungeOverlay:SetAnchorFill()
+
+    local mouseOver = wm:CreateControl(nil, scrollContainer, CT_TEXTURE)
+    mouseOver:SetDrawLevel(2)
+    mouseOver:SetTexture("EsoUI/Art/Buttons/minmax_mouseover.dds")
+    mouseOver:SetHidden(true)
+
+    local function IconFactory(pool)
+        local icon = wm:CreateControl(name .. "Entry" .. pool:GetNextControlId(), scroll, CT_TEXTURE)
+        icon:SetMouseEnabled(true)
+        icon:SetDrawLevel(3)
+        icon:SetHandler("OnMouseEnter", function()
+            mouseOver:SetAnchor(TOPLEFT, icon, TOPLEFT, 0, 0)
+            mouseOver:SetAnchor(BOTTOMRIGHT, icon, BOTTOMRIGHT, 0, 0)
+            mouseOver:SetHidden(false)
+            if self.customOnMouseEnter then
+                self.customOnMouseEnter(icon)
+            else
+                self:OnMouseEnter(icon)
+            end
+        end)
+        icon:SetHandler("OnMouseExit", function()
+            mouseOver:ClearAnchors()
+            mouseOver:SetHidden(true)
+            if self.customOnMouseExit then
+                self.customOnMouseExit(icon)
+            else
+                self:OnMouseExit(icon)
+            end
+        end)
+        icon:SetHandler("OnMouseUp", function(control, ...)
+            PlaySound("Click")
+            icon.OnSelect(icon, icon.texture)
+            self:Clear()
+        end)
+        return icon
+    end
+
+    local function ResetFunction(icon)
+        icon:ClearAnchors()
+    end
+
+    self.iconPool = ZO_ObjectPool:New(IconFactory, ResetFunction)
+    self:SetMaxColumns(1)
+    self.icons = {}
+    self.color = ZO_DEFAULT_ENABLED_COLOR
+
+    EVENT_MANAGER:RegisterForEvent(name .. "_OnGlobalMouseUp", EVENT_GLOBAL_MOUSE_UP, function()
+        if self.refCount ~= nil then
+            local moc = wm:GetMouseOverControl()
+            if(moc:GetOwningWindow() ~= control) then
+                self.refCount = self.refCount - 1
+                if self.refCount <= 0 then
+                    self:Clear()
+                end
+            end
+        end
+    end)
+end
+
+function IconPickerMenu:OnMouseEnter(icon)
+    InitializeTooltip(InformationTooltip, icon, TOPLEFT, 0, 0, BOTTOMRIGHT)
+    SetTooltipText(InformationTooltip, LAM.util.GetStringFromValue(icon.tooltip))
+    InformationTooltipTopLevel:BringWindowToTop()
+end
+
+function IconPickerMenu:OnMouseExit(icon)
+    ClearTooltip(InformationTooltip)
+end
+
+function IconPickerMenu:SetMaxColumns(value)
+    self.maxCols = value ~= nil and value or 5
+end
+
+local DEFAULT_SIZE = 28
+function IconPickerMenu:SetIconSize(value)
+    local iconSize = DEFAULT_SIZE
+    if value ~= nil then iconSize = math.max(iconSize, value) end
+    self.iconSize = iconSize
+end
+
+function IconPickerMenu:SetVisibleRows(value)
+    self.visibleRows = value ~= nil and value or 4.5
+end
+
+function IconPickerMenu:SetMouseHandlers(onEnter, onExit)
+    self.customOnMouseEnter = onEnter
+    self.customOnMouseExit = onExit
+end
+
+function IconPickerMenu:UpdateDimensions()
+    local iconSize = self.iconSize
+    local width = iconSize * self.maxCols + 20
+    local height = iconSize * self.visibleRows
+    self.control:SetDimensions(width, height)
+
+    local icons = self.icons
+    for i = 1, #icons do
+        local icon = icons[i]
+        icon:SetDimensions(iconSize, iconSize)
+    end
+end
+
+function IconPickerMenu:UpdateAnchors()
+    local iconSize = self.iconSize
+    local col, maxCols = 1, self.maxCols
+    local previousCol, previousRow
+    local scroll = self.scroll
+    local icons = self.icons
+
+    for i = 1, #icons do
+        local icon = icons[i]
+        icon:ClearAnchors()
+        if i == 1 then
+            icon:SetAnchor(TOPLEFT, scroll, TOPLEFT, 0, 0)
+            previousRow = icon
+        elseif col == 1 then
+            icon:SetAnchor(TOPLEFT, previousRow, BOTTOMLEFT, 0, 0)
+            previousRow = icon
+        else
+            icon:SetAnchor(TOPLEFT, previousCol, TOPRIGHT, 0, 0)
+        end
+        previousCol = icon
+        col = col >= maxCols and 1 or col + 1
+    end
+end
+
+function IconPickerMenu:Clear()
+    self.icons = {}
+    self.iconPool:ReleaseAllObjects()
+    self.control:SetHidden(true)
+    self.color = ZO_DEFAULT_ENABLED_COLOR
+    self.refCount = nil
+    self.parent = nil
+    self.customOnMouseEnter = nil
+    self.customOnMouseExit = nil
+end
+
+function IconPickerMenu:AddIcon(texturePath, callback, tooltip)
+    local icon, key = self.iconPool:AcquireObject()
+    icon:SetTexture(texturePath)
+    icon:SetColor(self.color:UnpackRGBA())
+    icon.texture = texturePath
+    icon.tooltip = tooltip
+    icon.OnSelect = callback
+    self.icons[#self.icons + 1] = icon
+end
+
+function IconPickerMenu:Show(parent)
+    if #self.icons == 0 then return false end
+    if not self.control:IsHidden() then self:Clear() return false end
+    self:UpdateDimensions()
+    self:UpdateAnchors()
+
+    local control = self.control
+    control:ClearAnchors()
+    control:SetAnchor(TOPLEFT, parent, BOTTOMLEFT, 0, 8)
+    control:SetHidden(false)
+    control:BringWindowToTop()
+    self.parent = parent
+    self.refCount = 2
+
+    return true
+end
+
+function IconPickerMenu:SetColor(color)
+    local icons = self.icons
+    self.color = color
+    for i = 1, #icons do
+        local icon = icons[i]
+        icon:SetColor(color:UnpackRGBA())
+    end
+end
+
+-------------------------------------------------------------
+
+local function UpdateChoices(control, choices, choicesTooltips)
+    local data = control.data
+    if not choices then
+        choices, choicesTooltips = data.choices, data.choicesTooltips or {}
+    end
+    local addedChoices = {}
+
+    local iconPicker = LAM.util.GetIconPickerMenu()
+    iconPicker:Clear()
+    for i = 1, #choices do
+        local texture = choices[i]
+        if not addedChoices[texture] then -- remove duplicates
+            iconPicker:AddIcon(choices[i], function(self, texture)
+                control.icon:SetTexture(texture)
+                data.setFunc(texture)
+                LAM.util.RequestRefreshIfNeeded(control)
+            end, LAM.util.GetStringFromValue(choicesTooltips[i]))
+        addedChoices[texture] = true
+        end
+    end
+end
+
+local function IsDisabled(control)
+    if type(control.data.disabled) == "function" then
+        return control.data.disabled()
+    else
+        return control.data.disabled
+    end
+end
+
+local function SetColor(control, color)
+    local icon = control.icon
+    if IsDisabled(control) then
+        icon:SetColor(ZO_DEFAULT_DISABLED_COLOR:UnpackRGBA())
+    else
+        icon.color = color or control.data.defaultColor or ZO_DEFAULT_ENABLED_COLOR
+        icon:SetColor(icon.color:UnpackRGBA())
+    end
+
+    local iconPicker = LAM.util.GetIconPickerMenu()
+    if iconPicker.parent == control.container and not iconPicker.control:IsHidden() then
+        iconPicker:SetColor(icon.color)
+    end
+end
+
+local function UpdateDisabled(control)
+    local disable = IsDisabled(control)
+
+    control.dropdown:SetMouseEnabled(not disable)
+    control.dropdownButton:SetEnabled(not disable)
+
+    local iconPicker = LAM.util.GetIconPickerMenu()
+    if iconPicker.parent == control.container and not iconPicker.control:IsHidden() then
+        iconPicker:Clear()
+    end
+
+    SetColor(control, control.icon.color)
+    if disable then
+        control.label:SetColor(ZO_DEFAULT_DISABLED_COLOR:UnpackRGBA())
+    else
+        control.label:SetColor(ZO_DEFAULT_ENABLED_COLOR:UnpackRGBA())
+    end
+end
+
+local function UpdateValue(control, forceDefault, value)
+    if forceDefault then --if we are forcing defaults
+        value = LAM.util.GetDefaultValue(control.data.default)
+        control.data.setFunc(value)
+        control.icon:SetTexture(value)
+    elseif value then
+        control.data.setFunc(value)
+        --after setting this value, let's refresh the others to see if any should be disabled or have their settings changed
+        LAM.util.RequestRefreshIfNeeded(control)
+    else
+        value = control.data.getFunc()
+        control.icon:SetTexture(value)
+    end
+end
+
+local MIN_HEIGHT = 26
+local HALF_WIDTH_LINE_SPACING = 2
+local function SetIconSize(control, size)
+    local icon = control.icon
+    icon.size = size
+    icon:SetDimensions(size, size)
+
+    local height = size + 4
+    control.dropdown:SetDimensions(size + 20, height)
+    height = math.max(height, MIN_HEIGHT)
+    control.container:SetHeight(height)
+    if control.lineControl then
+        control.lineControl:SetHeight(MIN_HEIGHT + size + HALF_WIDTH_LINE_SPACING)
+    else
+        control:SetHeight(height)
+    end
+
+    local iconPicker = LAM.util.GetIconPickerMenu()
+    if iconPicker.parent == control.container and not iconPicker.control:IsHidden() then
+        iconPicker:SetIconSize(size)
+        iconPicker:UpdateDimensions()
+        iconPicker:UpdateAnchors()
+    end
+end
+
+function LAMCreateControl.iconpicker(parent, iconpickerData, controlName)
+    local control = LAM.util.CreateLabelAndContainerControl(parent, iconpickerData, controlName)
+
+    local function ShowIconPicker()
+        local iconPicker = LAM.util.GetIconPickerMenu()
+        if iconPicker.parent == control.container then
+            iconPicker:Clear()
+        else
+            iconPicker:SetMaxColumns(iconpickerData.maxColumns)
+            iconPicker:SetVisibleRows(iconpickerData.visibleRows)
+            iconPicker:SetIconSize(control.icon.size)
+            UpdateChoices(control)
+            iconPicker:SetColor(control.icon.color)
+            if iconpickerData.beforeShow then
+                if iconpickerData.beforeShow(control, iconPicker) then
+                    iconPicker:Clear()
+                    return
+                end
+            end
+            iconPicker:Show(control.container)
+        end
+    end
+
+    local iconSize = iconpickerData.iconSize ~= nil and iconpickerData.iconSize or DEFAULT_SIZE
+    control.dropdown = wm:CreateControl(nil, control.container, CT_CONTROL)
+    local dropdown = control.dropdown
+    dropdown:SetAnchor(LEFT, control.container, LEFT, 0, 0)
+    dropdown:SetMouseEnabled(true)
+    dropdown:SetHandler("OnMouseUp", ShowIconPicker)
+    dropdown:SetHandler("OnMouseEnter", function() ZO_Options_OnMouseEnter(control) end)
+    dropdown:SetHandler("OnMouseExit", function() ZO_Options_OnMouseExit(control) end)
+
+    control.icon = wm:CreateControl(nil, dropdown, CT_TEXTURE)
+    local icon = control.icon
+    icon:SetAnchor(LEFT, dropdown, LEFT, 3, 0)
+    icon:SetDrawLevel(2)
+
+    local dropdownButton = wm:CreateControlFromVirtual(nil, dropdown, "ZO_DropdownButton")
+    dropdownButton:SetDimensions(16, 16)
+    dropdownButton:SetHandler("OnClicked", ShowIconPicker)
+    dropdownButton:SetAnchor(RIGHT, dropdown, RIGHT, -3, 0)
+    control.dropdownButton = dropdownButton
+
+    control.bg = wm:CreateControl(nil, dropdown, CT_BACKDROP)
+    local bg = control.bg
+    bg:SetAnchor(TOPLEFT, dropdown, TOPLEFT, 0, -3)
+    bg:SetAnchor(BOTTOMRIGHT, dropdown, BOTTOMRIGHT, 2, 5)
+    bg:SetEdgeTexture("EsoUI/Art/Tooltips/UI-Border.dds", 128, 16)
+    bg:SetCenterTexture("EsoUI/Art/Tooltips/UI-TooltipCenter.dds")
+    bg:SetInsets(16, 16, -16, -16)
+    local mungeOverlay = wm:CreateControl(nil, bg, CT_TEXTURE)
+    mungeOverlay:SetTexture("EsoUI/Art/Tooltips/munge_overlay.dds")
+    mungeOverlay:SetDrawLevel(1)
+    mungeOverlay:SetAddressMode(TEX_MODE_WRAP)
+    mungeOverlay:SetAnchorFill()
+
+    if iconpickerData.warning ~= nil or iconpickerData.requiresReload then
+        control.warning = wm:CreateControlFromVirtual(nil, control, "ZO_Options_WarningIcon")
+        control.warning:SetAnchor(RIGHT, control.container, LEFT, -5, 0)
+        control.UpdateWarning = LAM.util.UpdateWarning
+        control:UpdateWarning()
+    end
+
+    control.UpdateChoices = UpdateChoices
+    control.UpdateValue = UpdateValue
+    control:UpdateValue()
+    control.SetColor = SetColor
+    control:SetColor()
+    control.SetIconSize = SetIconSize
+    control:SetIconSize(iconSize)
+
+    if iconpickerData.disabled ~= nil then
+        control.UpdateDisabled = UpdateDisabled
+        control:UpdateDisabled()
+    end
+
+    LAM.util.RegisterForRefreshIfNeeded(control)
+    LAM.util.RegisterForReloadIfNeeded(control)
+
+    return control
+end
diff --git a/TaosGroupTools/libs/LibAddonMenu-2.0/controls/panel.lua b/TaosGroupTools/libs/LibAddonMenu-2.0/controls/panel.lua
index 1404686..d6956d8 100644
--- a/TaosGroupTools/libs/LibAddonMenu-2.0/controls/panel.lua
+++ b/TaosGroupTools/libs/LibAddonMenu-2.0/controls/panel.lua
@@ -1,126 +1,126 @@
---[[panelData = {
-    type = "panel",
-    name = "Window Title", -- or string id or function returning a string
-    displayName = "My Longer Window Title",  -- or string id or function returning a string (optional) (can be useful for long addon names or if you want to colorize it)
-    author = "Seerah",  -- or string id or function returning a string (optional)
-    version = "2.0",  -- or string id or function returning a string (optional)
-    website = "http://www.esoui.com/downloads/info7-LibAddonMenu.html", -- URL of website where the addon can be updated (optional)
-    keywords = "settings", -- additional keywords for search filter (it looks for matches in name..keywords..author) (optional)
-    slashCommand = "/myaddon", -- will register a keybind to open to this panel (don't forget to include the slash!) (optional)
-    registerForRefresh = true, --boolean (optional) (will refresh all options controls when a setting is changed and when the panel is shown)
-    registerForDefaults = true, --boolean (optional) (will set all options controls back to default values)
-    resetFunc = function() print("defaults reset") end, --(optional) custom function to run after settings are reset to defaults
-} ]]
-
-
-local widgetVersion = 13
-local LAM = LibStub("LibAddonMenu-2.0")
-if not LAM:RegisterWidget("panel", widgetVersion) then return end
-
-local wm = WINDOW_MANAGER
-local cm = CALLBACK_MANAGER
-
-local function RefreshPanel(control)
-    local panel = LAM.util.GetTopPanel(control) --callback can be fired by a single control, by the panel showing or by a nested submenu
-    local panelControls = panel.controlsToRefresh
-
-    for i = 1, #panelControls do
-        local updateControl = panelControls[i]
-        if updateControl ~= control and updateControl.UpdateValue then
-            updateControl:UpdateValue()
-        end
-        if updateControl.UpdateDisabled then
-            updateControl:UpdateDisabled()
-        end
-        if updateControl.UpdateWarning then
-            updateControl:UpdateWarning()
-        end
-    end
-end
-
-local function ForceDefaults(panel)
-    local panelControls = panel.controlsToRefresh
-
-    for i = 1, #panelControls do
-        local updateControl = panelControls[i]
-        if updateControl.UpdateValue and updateControl.data.default ~= nil then
-            updateControl:UpdateValue(true)
-        end
-    end
-
-    if panel.data.resetFunc then
-        panel.data.resetFunc()
-    end
-
-    cm:FireCallbacks("LAM-RefreshPanel", panel)
-end
-
-local callbackRegistered = false
-LAMCreateControl.scrollCount = LAMCreateControl.scrollCount or 1
-local SEPARATOR = " - "
-local LINK_COLOR = ZO_ColorDef:New("5959D5")
-local LINK_MOUSE_OVER_COLOR = ZO_ColorDef:New("B8B8D3")
-
-function LAMCreateControl.panel(parent, panelData, controlName)
-    local control = wm:CreateControl(controlName, parent, CT_CONTROL)
-
-    control.label = wm:CreateControlFromVirtual(nil, control, "ZO_Options_SectionTitleLabel")
-    local label = control.label
-    label:SetAnchor(TOPLEFT, control, TOPLEFT, 0, 4)
-    label:SetText(LAM.util.GetStringFromValue(panelData.displayName or panelData.name))
-
-    if panelData.author or panelData.version then
-        control.info = wm:CreateControl(nil, control, CT_LABEL)
-        local info = control.info
-        info:SetFont(LAM.util.L["PANEL_INFO_FONT"])
-        info:SetAnchor(TOPLEFT, label, BOTTOMLEFT, 0, -2)
-
-        local output = {}
-        if panelData.author then
-            output[#output + 1] = zo_strformat(LAM.util.L["AUTHOR"], LAM.util.GetStringFromValue(panelData.author))
-        end
-        if panelData.version then
-            output[#output + 1] = zo_strformat(LAM.util.L["VERSION"], LAM.util.GetStringFromValue(panelData.version))
-        end
-        info:SetText(table.concat(output, SEPARATOR))
-    end
-
-    if panelData.website then
-        control.website = wm:CreateControl(nil, control, CT_BUTTON)
-        local website = control.website
-        website:SetClickSound("Click")
-        website:SetFont(LAM.util.L["PANEL_INFO_FONT"])
-        website:SetNormalFontColor(LINK_COLOR:UnpackRGBA())
-        website:SetMouseOverFontColor(LINK_MOUSE_OVER_COLOR:UnpackRGBA())
-        if(control.info) then
-            website:SetAnchor(TOPLEFT, control.info, TOPRIGHT, 0, 0)
-            website:SetText(string.format("|cffffff%s|r%s", SEPARATOR, LAM.util.L["WEBSITE"]))
-        else
-            website:SetAnchor(TOPLEFT, label, BOTTOMLEFT, 0, -2)
-            website:SetText(LAM.util.L["WEBSITE"])
-        end
-        website:SetDimensions(website:GetLabelControl():GetTextDimensions())
-        website:SetHandler("OnClicked", function()
-            RequestOpenUnsafeURL(panelData.website)
-        end)
-    end
-
-    control.container = wm:CreateControlFromVirtual("LAMAddonPanelContainer"..LAMCreateControl.scrollCount, control, "ZO_ScrollContainer")
-    LAMCreateControl.scrollCount = LAMCreateControl.scrollCount + 1
-    local container = control.container
-    container:SetAnchor(TOPLEFT, control.info or label, BOTTOMLEFT, 0, 20)
-    container:SetAnchor(BOTTOMRIGHT, control, BOTTOMRIGHT, -3, -3)
-    control.scroll = GetControl(control.container, "ScrollChild")
-    control.scroll:SetResizeToFitPadding(0, 20)
-
-    if panelData.registerForRefresh and not callbackRegistered then --don't want to register our callback more than once
-        cm:RegisterCallback("LAM-RefreshPanel", RefreshPanel)
-        callbackRegistered = true
-    end
-
-    control.ForceDefaults = ForceDefaults
-    control.data = panelData
-    control.controlsToRefresh = {}
-
-    return control
-end
+--[[panelData = {
+    type = "panel",
+    name = "Window Title", -- or string id or function returning a string
+    displayName = "My Longer Window Title",  -- or string id or function returning a string (optional) (can be useful for long addon names or if you want to colorize it)
+    author = "Seerah",  -- or string id or function returning a string (optional)
+    version = "2.0",  -- or string id or function returning a string (optional)
+    website = "http://www.esoui.com/downloads/info7-LibAddonMenu.html", -- URL of website where the addon can be updated (optional)
+    keywords = "settings", -- additional keywords for search filter (it looks for matches in name..keywords..author) (optional)
+    slashCommand = "/myaddon", -- will register a keybind to open to this panel (don't forget to include the slash!) (optional)
+    registerForRefresh = true, --boolean (optional) (will refresh all options controls when a setting is changed and when the panel is shown)
+    registerForDefaults = true, --boolean (optional) (will set all options controls back to default values)
+    resetFunc = function() print("defaults reset") end, --(optional) custom function to run after settings are reset to defaults
+} ]]
+
+
+local widgetVersion = 13
+local LAM = LibStub("LibAddonMenu-2.0")
+if not LAM:RegisterWidget("panel", widgetVersion) then return end
+
+local wm = WINDOW_MANAGER
+local cm = CALLBACK_MANAGER
+
+local function RefreshPanel(control)
+    local panel = LAM.util.GetTopPanel(control) --callback can be fired by a single control, by the panel showing or by a nested submenu
+    local panelControls = panel.controlsToRefresh
+
+    for i = 1, #panelControls do
+        local updateControl = panelControls[i]
+        if updateControl ~= control and updateControl.UpdateValue then
+            updateControl:UpdateValue()
+        end
+        if updateControl.UpdateDisabled then
+            updateControl:UpdateDisabled()
+        end
+        if updateControl.UpdateWarning then
+            updateControl:UpdateWarning()
+        end
+    end
+end
+
+local function ForceDefaults(panel)
+    local panelControls = panel.controlsToRefresh
+
+    for i = 1, #panelControls do
+        local updateControl = panelControls[i]
+        if updateControl.UpdateValue and updateControl.data.default ~= nil then
+            updateControl:UpdateValue(true)
+        end
+    end
+
+    if panel.data.resetFunc then
+        panel.data.resetFunc()
+    end
+
+    cm:FireCallbacks("LAM-RefreshPanel", panel)
+end
+
+local callbackRegistered = false
+LAMCreateControl.scrollCount = LAMCreateControl.scrollCount or 1
+local SEPARATOR = " - "
+local LINK_COLOR = ZO_ColorDef:New("5959D5")
+local LINK_MOUSE_OVER_COLOR = ZO_ColorDef:New("B8B8D3")
+
+function LAMCreateControl.panel(parent, panelData, controlName)
+    local control = wm:CreateControl(controlName, parent, CT_CONTROL)
+
+    control.label = wm:CreateControlFromVirtual(nil, control, "ZO_Options_SectionTitleLabel")
+    local label = control.label
+    label:SetAnchor(TOPLEFT, control, TOPLEFT, 0, 4)
+    label:SetText(LAM.util.GetStringFromValue(panelData.displayName or panelData.name))
+
+    if panelData.author or panelData.version then
+        control.info = wm:CreateControl(nil, control, CT_LABEL)
+        local info = control.info
+        info:SetFont(LAM.util.L["PANEL_INFO_FONT"])
+        info:SetAnchor(TOPLEFT, label, BOTTOMLEFT, 0, -2)
+
+        local output = {}
+        if panelData.author then
+            output[#output + 1] = zo_strformat(LAM.util.L["AUTHOR"], LAM.util.GetStringFromValue(panelData.author))
+        end
+        if panelData.version then
+            output[#output + 1] = zo_strformat(LAM.util.L["VERSION"], LAM.util.GetStringFromValue(panelData.version))
+        end
+        info:SetText(table.concat(output, SEPARATOR))
+    end
+
+    if panelData.website then
+        control.website = wm:CreateControl(nil, control, CT_BUTTON)
+        local website = control.website
+        website:SetClickSound("Click")
+        website:SetFont(LAM.util.L["PANEL_INFO_FONT"])
+        website:SetNormalFontColor(LINK_COLOR:UnpackRGBA())
+        website:SetMouseOverFontColor(LINK_MOUSE_OVER_COLOR:UnpackRGBA())
+        if(control.info) then
+            website:SetAnchor(TOPLEFT, control.info, TOPRIGHT, 0, 0)
+            website:SetText(string.format("|cffffff%s|r%s", SEPARATOR, LAM.util.L["WEBSITE"]))
+        else
+            website:SetAnchor(TOPLEFT, label, BOTTOMLEFT, 0, -2)
+            website:SetText(LAM.util.L["WEBSITE"])
+        end
+        website:SetDimensions(website:GetLabelControl():GetTextDimensions())
+        website:SetHandler("OnClicked", function()
+            RequestOpenUnsafeURL(panelData.website)
+        end)
+    end
+
+    control.container = wm:CreateControlFromVirtual("LAMAddonPanelContainer"..LAMCreateControl.scrollCount, control, "ZO_ScrollContainer")
+    LAMCreateControl.scrollCount = LAMCreateControl.scrollCount + 1
+    local container = control.container
+    container:SetAnchor(TOPLEFT, control.info or label, BOTTOMLEFT, 0, 20)
+    container:SetAnchor(BOTTOMRIGHT, control, BOTTOMRIGHT, -3, -3)
+    control.scroll = GetControl(control.container, "ScrollChild")
+    control.scroll:SetResizeToFitPadding(0, 20)
+
+    if panelData.registerForRefresh and not callbackRegistered then --don't want to register our callback more than once
+        cm:RegisterCallback("LAM-RefreshPanel", RefreshPanel)
+        callbackRegistered = true
+    end
+
+    control.ForceDefaults = ForceDefaults
+    control.data = panelData
+    control.controlsToRefresh = {}
+
+    return control
+end
diff --git a/TaosGroupTools/libs/LibAddonMenu-2.0/controls/slider.lua b/TaosGroupTools/libs/LibAddonMenu-2.0/controls/slider.lua
index bd721c5..7a85d57 100644
--- a/TaosGroupTools/libs/LibAddonMenu-2.0/controls/slider.lua
+++ b/TaosGroupTools/libs/LibAddonMenu-2.0/controls/slider.lua
@@ -1,212 +1,212 @@
---[[sliderData = {
-    type = "slider",
-    name = "My Slider", -- or string id or function returning a string
-    getFunc = function() return db.var end,
-    setFunc = function(value) db.var = value doStuff() end,
-    min = 0,
-    max = 20,
-    step = 1, --(optional)
-    clampInput = true, -- boolean, if set to false the input won't clamp to min and max and allow any number instead (optional)
-    decimals = 0, -- when specified the input value is rounded to the specified number of decimals (optional)
-    autoSelect = false, -- boolean, automatically select everything in the text input field when it gains focus (optional)
-    inputLocation = "below", -- or "right", determines where the input field is shown. This should not be used within the addon menu and is for custom sliders (optional)
-    tooltip = "Slider's tooltip text.", -- or string id or function returning a string (optional)
-    width = "full", --or "half" (optional)
-    disabled = function() return db.someBooleanSetting end, --or boolean (optional)
-    warning = "May cause permanent awesomeness.", -- or string id or function returning a string (optional)
-    requiresReload = false, -- boolean, if set to true, the warning text will contain a notice that changes are only applied after an UI reload and any change to the value will make the "Apply Settings" button appear on the panel which will reload the UI when pressed (optional)
-    default = defaults.var, -- default value or function that returns the default value (optional)
-    reference = "MyAddonSlider" -- unique global reference to control (optional)
-} ]]
-
-local widgetVersion = 12
-local LAM = LibStub("LibAddonMenu-2.0")
-if not LAM:RegisterWidget("slider", widgetVersion) then return end
-
-local wm = WINDOW_MANAGER
-local strformat = string.format
-
-local function RoundDecimalToPlace(d, place)
-    return tonumber(strformat("%." .. tostring(place) .. "f", d))
-end
-
-local function UpdateDisabled(control)
-    local disable
-    if type(control.data.disabled) == "function" then
-        disable = control.data.disabled()
-    else
-        disable = control.data.disabled
-    end
-
-    control.slider:SetEnabled(not disable)
-    control.slidervalue:SetEditEnabled(not disable)
-    if disable then
-        control.label:SetColor(ZO_DEFAULT_DISABLED_COLOR:UnpackRGBA())
-        control.minText:SetColor(ZO_DEFAULT_DISABLED_COLOR:UnpackRGBA())
-        control.maxText:SetColor(ZO_DEFAULT_DISABLED_COLOR:UnpackRGBA())
-        control.slidervalue:SetColor(ZO_DEFAULT_DISABLED_MOUSEOVER_COLOR:UnpackRGBA())
-    else
-        control.label:SetColor(ZO_DEFAULT_ENABLED_COLOR:UnpackRGBA())
-        control.minText:SetColor(ZO_DEFAULT_ENABLED_COLOR:UnpackRGBA())
-        control.maxText:SetColor(ZO_DEFAULT_ENABLED_COLOR:UnpackRGBA())
-        control.slidervalue:SetColor(ZO_DEFAULT_ENABLED_COLOR:UnpackRGBA())
-    end
-end
-
-local function UpdateValue(control, forceDefault, value)
-    if forceDefault then --if we are forcing defaults
-        value = LAM.util.GetDefaultValue(control.data.default)
-        control.data.setFunc(value)
-    elseif value then
-        if control.data.decimals then
-            value = RoundDecimalToPlace(value, control.data.decimals)
-        end
-        if control.data.clampInput ~= false then
-            value = math.max(math.min(value, control.data.max), control.data.min)
-        end
-        control.data.setFunc(value)
-        --after setting this value, let's refresh the others to see if any should be disabled or have their settings changed
-        LAM.util.RequestRefreshIfNeeded(control)
-    else
-        value = control.data.getFunc()
-    end
-
-    control.slider:SetValue(value)
-    control.slidervalue:SetText(value)
-end
-
-function LAMCreateControl.slider(parent, sliderData, controlName)
-    local control = LAM.util.CreateLabelAndContainerControl(parent, sliderData, controlName)
-    local isInputOnRight = sliderData.inputLocation == "right"
-
-    --skipping creating the backdrop...  Is this the actual slider texture?
-    control.slider = wm:CreateControl(nil, control.container, CT_SLIDER)
-    local slider = control.slider
-    slider:SetAnchor(TOPLEFT)
-    slider:SetHeight(14)
-    if(isInputOnRight) then
-        slider:SetAnchor(TOPRIGHT, nil, nil, -60)
-    else
-        slider:SetAnchor(TOPRIGHT)
-    end
-    slider:SetMouseEnabled(true)
-    slider:SetOrientation(ORIENTATION_HORIZONTAL)
-    --put nil for highlighted texture file path, and what look to be texture coords
-    slider:SetThumbTexture("EsoUI\\Art\\Miscellaneous\\scrollbox_elevator.dds", "EsoUI\\Art\\Miscellaneous\\scrollbox_elevator_disabled.dds", nil, 8, 16)
-    local minValue = sliderData.min
-    local maxValue = sliderData.max
-    slider:SetMinMax(minValue, maxValue)
-    slider:SetHandler("OnMouseEnter", function() ZO_Options_OnMouseEnter(control) end)
-    slider:SetHandler("OnMouseExit", function() ZO_Options_OnMouseExit(control) end)
-
-    slider.bg = wm:CreateControl(nil, slider, CT_BACKDROP)
-    local bg = slider.bg
-    bg:SetCenterColor(0, 0, 0)
-    bg:SetAnchor(TOPLEFT, slider, TOPLEFT, 0, 4)
-    bg:SetAnchor(BOTTOMRIGHT, slider, BOTTOMRIGHT, 0, -4)
-    bg:SetEdgeTexture("EsoUI\\Art\\Tooltips\\UI-SliderBackdrop.dds", 32, 4)
-
-    control.minText = wm:CreateControl(nil, slider, CT_LABEL)
-    local minText = control.minText
-    minText:SetFont("ZoFontGameSmall")
-    minText:SetAnchor(TOPLEFT, slider, BOTTOMLEFT)
-    minText:SetText(sliderData.min)
-
-    control.maxText = wm:CreateControl(nil, slider, CT_LABEL)
-    local maxText = control.maxText
-    maxText:SetFont("ZoFontGameSmall")
-    maxText:SetAnchor(TOPRIGHT, slider, BOTTOMRIGHT)
-    maxText:SetText(sliderData.max)
-
-    control.slidervalueBG = wm:CreateControlFromVirtual(nil, slider, "ZO_EditBackdrop")
-    if(isInputOnRight) then
-        control.slidervalueBG:SetDimensions(60, 26)
-        control.slidervalueBG:SetAnchor(LEFT, slider, RIGHT, 5, 0)
-    else
-        control.slidervalueBG:SetDimensions(50, 16)
-        control.slidervalueBG:SetAnchor(TOP, slider, BOTTOM, 0, 0)
-    end
-    control.slidervalue = wm:CreateControlFromVirtual(nil, control.slidervalueBG, "ZO_DefaultEditForBackdrop")
-    local slidervalue = control.slidervalue
-    slidervalue:ClearAnchors()
-    slidervalue:SetAnchor(TOPLEFT, control.slidervalueBG, TOPLEFT, 3, 1)
-    slidervalue:SetAnchor(BOTTOMRIGHT, control.slidervalueBG, BOTTOMRIGHT, -3, -1)
-    slidervalue:SetTextType(TEXT_TYPE_NUMERIC)
-    if(isInputOnRight) then
-        slidervalue:SetFont("ZoFontGameLarge")
-    else
-        slidervalue:SetFont("ZoFontGameSmall")
-    end
-
-    local isHandlingChange = false
-    local function HandleValueChanged(value)
-        if isHandlingChange then return end
-        if sliderData.decimals then
-            value = RoundDecimalToPlace(value, sliderData.decimals)
-        end
-        isHandlingChange = true
-        slider:SetValue(value)
-        slidervalue:SetText(value)
-        isHandlingChange = false
-    end
-
-    slidervalue:SetHandler("OnEscape", function(self)
-        HandleValueChanged(sliderData.getFunc())
-        self:LoseFocus()
-    end)
-    slidervalue:SetHandler("OnEnter", function(self)
-        self:LoseFocus()
-    end)
-    slidervalue:SetHandler("OnFocusLost", function(self)
-        local value = tonumber(self:GetText())
-        control:UpdateValue(false, value)
-    end)
-    slidervalue:SetHandler("OnTextChanged", function(self)
-        local input = self:GetText()
-        if(#input > 1 and not input:sub(-1):match("[0-9]")) then return end
-        local value = tonumber(input)
-        if(value) then
-            HandleValueChanged(value)
-        end
-    end)
-    if(sliderData.autoSelect) then
-        ZO_PreHookHandler(slidervalue, "OnFocusGained", function(self)
-            self:SelectAll()
-        end)
-    end
-
-    local range = maxValue - minValue
-    slider:SetValueStep(sliderData.step or 1)
-    slider:SetHandler("OnValueChanged", function(self, value, eventReason)
-        if eventReason == EVENT_REASON_SOFTWARE then return end
-        HandleValueChanged(value)
-    end)
-    slider:SetHandler("OnSliderReleased", function(self, value)
-        control:UpdateValue(false, value)
-    end)
-    slider:SetHandler("OnMouseWheel", function(self, value)
-        if(not self:GetEnabled()) then return end
-        local new_value = (tonumber(slidervalue:GetText()) or sliderData.min or 0) + ((sliderData.step or 1) * value)
-        control:UpdateValue(false, new_value)
-    end)
-
-    if sliderData.warning ~= nil or sliderData.requiresReload then
-        control.warning = wm:CreateControlFromVirtual(nil, control, "ZO_Options_WarningIcon")
-        control.warning:SetAnchor(RIGHT, slider, LEFT, -5, 0)
-        control.UpdateWarning = LAM.util.UpdateWarning
-        control:UpdateWarning()
-    end
-
-    control.UpdateValue = UpdateValue
-    control:UpdateValue()
-
-    if sliderData.disabled ~= nil then
-        control.UpdateDisabled = UpdateDisabled
-        control:UpdateDisabled()
-    end
-
-    LAM.util.RegisterForRefreshIfNeeded(control)
-    LAM.util.RegisterForReloadIfNeeded(control)
-
-    return control
-end
+--[[sliderData = {
+    type = "slider",
+    name = "My Slider", -- or string id or function returning a string
+    getFunc = function() return db.var end,
+    setFunc = function(value) db.var = value doStuff() end,
+    min = 0,
+    max = 20,
+    step = 1, --(optional)
+    clampInput = true, -- boolean, if set to false the input won't clamp to min and max and allow any number instead (optional)
+    decimals = 0, -- when specified the input value is rounded to the specified number of decimals (optional)
+    autoSelect = false, -- boolean, automatically select everything in the text input field when it gains focus (optional)
+    inputLocation = "below", -- or "right", determines where the input field is shown. This should not be used within the addon menu and is for custom sliders (optional)
+    tooltip = "Slider's tooltip text.", -- or string id or function returning a string (optional)
+    width = "full", --or "half" (optional)
+    disabled = function() return db.someBooleanSetting end, --or boolean (optional)
+    warning = "May cause permanent awesomeness.", -- or string id or function returning a string (optional)
+    requiresReload = false, -- boolean, if set to true, the warning text will contain a notice that changes are only applied after an UI reload and any change to the value will make the "Apply Settings" button appear on the panel which will reload the UI when pressed (optional)
+    default = defaults.var, -- default value or function that returns the default value (optional)
+    reference = "MyAddonSlider" -- unique global reference to control (optional)
+} ]]
+
+local widgetVersion = 12
+local LAM = LibStub("LibAddonMenu-2.0")
+if not LAM:RegisterWidget("slider", widgetVersion) then return end
+
+local wm = WINDOW_MANAGER
+local strformat = string.format
+
+local function RoundDecimalToPlace(d, place)
+    return tonumber(strformat("%." .. tostring(place) .. "f", d))
+end
+
+local function UpdateDisabled(control)
+    local disable
+    if type(control.data.disabled) == "function" then
+        disable = control.data.disabled()
+    else
+        disable = control.data.disabled
+    end
+
+    control.slider:SetEnabled(not disable)
+    control.slidervalue:SetEditEnabled(not disable)
+    if disable then
+        control.label:SetColor(ZO_DEFAULT_DISABLED_COLOR:UnpackRGBA())
+        control.minText:SetColor(ZO_DEFAULT_DISABLED_COLOR:UnpackRGBA())
+        control.maxText:SetColor(ZO_DEFAULT_DISABLED_COLOR:UnpackRGBA())
+        control.slidervalue:SetColor(ZO_DEFAULT_DISABLED_MOUSEOVER_COLOR:UnpackRGBA())
+    else
+        control.label:SetColor(ZO_DEFAULT_ENABLED_COLOR:UnpackRGBA())
+        control.minText:SetColor(ZO_DEFAULT_ENABLED_COLOR:UnpackRGBA())
+        control.maxText:SetColor(ZO_DEFAULT_ENABLED_COLOR:UnpackRGBA())
+        control.slidervalue:SetColor(ZO_DEFAULT_ENABLED_COLOR:UnpackRGBA())
+    end
+end
+
+local function UpdateValue(control, forceDefault, value)
+    if forceDefault then --if we are forcing defaults
+        value = LAM.util.GetDefaultValue(control.data.default)
+        control.data.setFunc(value)
+    elseif value then
+        if control.data.decimals then
+            value = RoundDecimalToPlace(value, control.data.decimals)
+        end
+        if control.data.clampInput ~= false then
+            value = math.max(math.min(value, control.data.max), control.data.min)
+        end
+        control.data.setFunc(value)
+        --after setting this value, let's refresh the others to see if any should be disabled or have their settings changed
+        LAM.util.RequestRefreshIfNeeded(control)
+    else
+        value = control.data.getFunc()
+    end
+
+    control.slider:SetValue(value)
+    control.slidervalue:SetText(value)
+end
+
+function LAMCreateControl.slider(parent, sliderData, controlName)
+    local control = LAM.util.CreateLabelAndContainerControl(parent, sliderData, controlName)
+    local isInputOnRight = sliderData.inputLocation == "right"
+
+    --skipping creating the backdrop...  Is this the actual slider texture?
+    control.slider = wm:CreateControl(nil, control.container, CT_SLIDER)
+    local slider = control.slider
+    slider:SetAnchor(TOPLEFT)
+    slider:SetHeight(14)
+    if(isInputOnRight) then
+        slider:SetAnchor(TOPRIGHT, nil, nil, -60)
+    else
+        slider:SetAnchor(TOPRIGHT)
+    end
+    slider:SetMouseEnabled(true)
+    slider:SetOrientation(ORIENTATION_HORIZONTAL)
+    --put nil for highlighted texture file path, and what look to be texture coords
+    slider:SetThumbTexture("EsoUI\\Art\\Miscellaneous\\scrollbox_elevator.dds", "EsoUI\\Art\\Miscellaneous\\scrollbox_elevator_disabled.dds", nil, 8, 16)
+    local minValue = sliderData.min
+    local maxValue = sliderData.max
+    slider:SetMinMax(minValue, maxValue)
+    slider:SetHandler("OnMouseEnter", function() ZO_Options_OnMouseEnter(control) end)
+    slider:SetHandler("OnMouseExit", function() ZO_Options_OnMouseExit(control) end)
+
+    slider.bg = wm:CreateControl(nil, slider, CT_BACKDROP)
+    local bg = slider.bg
+    bg:SetCenterColor(0, 0, 0)
+    bg:SetAnchor(TOPLEFT, slider, TOPLEFT, 0, 4)
+    bg:SetAnchor(BOTTOMRIGHT, slider, BOTTOMRIGHT, 0, -4)
+    bg:SetEdgeTexture("EsoUI\\Art\\Tooltips\\UI-SliderBackdrop.dds", 32, 4)
+
+    control.minText = wm:CreateControl(nil, slider, CT_LABEL)
+    local minText = control.minText
+    minText:SetFont("ZoFontGameSmall")
+    minText:SetAnchor(TOPLEFT, slider, BOTTOMLEFT)
+    minText:SetText(sliderData.min)
+
+    control.maxText = wm:CreateControl(nil, slider, CT_LABEL)
+    local maxText = control.maxText
+    maxText:SetFont("ZoFontGameSmall")
+    maxText:SetAnchor(TOPRIGHT, slider, BOTTOMRIGHT)
+    maxText:SetText(sliderData.max)
+
+    control.slidervalueBG = wm:CreateControlFromVirtual(nil, slider, "ZO_EditBackdrop")
+    if(isInputOnRight) then
+        control.slidervalueBG:SetDimensions(60, 26)
+        control.slidervalueBG:SetAnchor(LEFT, slider, RIGHT, 5, 0)
+    else
+        control.slidervalueBG:SetDimensions(50, 16)
+        control.slidervalueBG:SetAnchor(TOP, slider, BOTTOM, 0, 0)
+    end
+    control.slidervalue = wm:CreateControlFromVirtual(nil, control.slidervalueBG, "ZO_DefaultEditForBackdrop")
+    local slidervalue = control.slidervalue
+    slidervalue:ClearAnchors()
+    slidervalue:SetAnchor(TOPLEFT, control.slidervalueBG, TOPLEFT, 3, 1)
+    slidervalue:SetAnchor(BOTTOMRIGHT, control.slidervalueBG, BOTTOMRIGHT, -3, -1)
+    slidervalue:SetTextType(TEXT_TYPE_NUMERIC)
+    if(isInputOnRight) then
+        slidervalue:SetFont("ZoFontGameLarge")
+    else
+        slidervalue:SetFont("ZoFontGameSmall")
+    end
+
+    local isHandlingChange = false
+    local function HandleValueChanged(value)
+        if isHandlingChange then return end
+        if sliderData.decimals then
+            value = RoundDecimalToPlace(value, sliderData.decimals)
+        end
+        isHandlingChange = true
+        slider:SetValue(value)
+        slidervalue:SetText(value)
+        isHandlingChange = false
+    end
+
+    slidervalue:SetHandler("OnEscape", function(self)
+        HandleValueChanged(sliderData.getFunc())
+        self:LoseFocus()
+    end)
+    slidervalue:SetHandler("OnEnter", function(self)
+        self:LoseFocus()
+    end)
+    slidervalue:SetHandler("OnFocusLost", function(self)
+        local value = tonumber(self:GetText())
+        control:UpdateValue(false, value)
+    end)
+    slidervalue:SetHandler("OnTextChanged", function(self)
+        local input = self:GetText()
+        if(#input > 1 and not input:sub(-1):match("[0-9]")) then return end
+        local value = tonumber(input)
+        if(value) then
+            HandleValueChanged(value)
+        end
+    end)
+    if(sliderData.autoSelect) then
+        ZO_PreHookHandler(slidervalue, "OnFocusGained", function(self)
+            self:SelectAll()
+        end)
+    end
+
+    local range = maxValue - minValue
+    slider:SetValueStep(sliderData.step or 1)
+    slider:SetHandler("OnValueChanged", function(self, value, eventReason)
+        if eventReason == EVENT_REASON_SOFTWARE then return end
+        HandleValueChanged(value)
+    end)
+    slider:SetHandler("OnSliderReleased", function(self, value)
+        control:UpdateValue(false, value)
+    end)
+    slider:SetHandler("OnMouseWheel", function(self, value)
+        if(not self:GetEnabled()) then return end
+        local new_value = (tonumber(slidervalue:GetText()) or sliderData.min or 0) + ((sliderData.step or 1) * value)
+        control:UpdateValue(false, new_value)
+    end)
+
+    if sliderData.warning ~= nil or sliderData.requiresReload then
+        control.warning = wm:CreateControlFromVirtual(nil, control, "ZO_Options_WarningIcon")
+        control.warning:SetAnchor(RIGHT, slider, LEFT, -5, 0)
+        control.UpdateWarning = LAM.util.UpdateWarning
+        control:UpdateWarning()
+    end
+
+    control.UpdateValue = UpdateValue
+    control:UpdateValue()
+
+    if sliderData.disabled ~= nil then
+        control.UpdateDisabled = UpdateDisabled
+        control:UpdateDisabled()
+    end
+
+    LAM.util.RegisterForRefreshIfNeeded(control)
+    LAM.util.RegisterForReloadIfNeeded(control)
+
+    return control
+end
diff --git a/TaosGroupTools/libs/LibAddonMenu-2.0/controls/submenu.lua b/TaosGroupTools/libs/LibAddonMenu-2.0/controls/submenu.lua
index 1766a1f..94087cb 100644
--- a/TaosGroupTools/libs/LibAddonMenu-2.0/controls/submenu.lua
+++ b/TaosGroupTools/libs/LibAddonMenu-2.0/controls/submenu.lua
@@ -1,108 +1,108 @@
---[[submenuData = {
-    type = "submenu",
-    name = "Submenu Title", -- or string id or function returning a string
-    tooltip = "My submenu tooltip", -- -- or string id or function returning a string (optional)
-    controls = {sliderData, buttonData} --(optional) used by LAM
-    reference = "MyAddonSubmenu" --(optional) unique global reference to control
-} ]]
-
-local widgetVersion = 11
-local LAM = LibStub("LibAddonMenu-2.0")
-if not LAM:RegisterWidget("submenu", widgetVersion) then return end
-
-local wm = WINDOW_MANAGER
-local am = ANIMATION_MANAGER
-
-local function UpdateValue(control)
-    control.label:SetText(LAM.util.GetStringFromValue(control.data.name))
-    if control.data.tooltip then
-        control.label.data.tooltipText = LAM.util.GetStringFromValue(control.data.tooltip)
-    end
-end
-
-local function AnimateSubmenu(clicked)
-    local control = clicked:GetParent()
-    control.open = not control.open
-
-    if control.open then
-        control.animation:PlayFromStart()
-    else
-        control.animation:PlayFromEnd()
-    end
-end
-
-function LAMCreateControl.submenu(parent, submenuData, controlName)
-    local width = parent:GetWidth() - 45
-    local control = wm:CreateControl(controlName or submenuData.reference, parent.scroll or parent, CT_CONTROL)
-    control.panel = parent
-    control.data = submenuData
-
-    control.label = wm:CreateControlFromVirtual(nil, control, "ZO_Options_SectionTitleLabel")
-    local label = control.label
-    label:SetAnchor(TOPLEFT, control, TOPLEFT, 5, 5)
-    label:SetDimensions(width, 30)
-    label:SetWrapMode(TEXT_WRAP_MODE_ELLIPSIS)
-    label:SetText(LAM.util.GetStringFromValue(submenuData.name))
-    label:SetMouseEnabled(true)
-    if submenuData.tooltip then
-        label.data = {tooltipText = LAM.util.GetStringFromValue(submenuData.tooltip)}
-        label:SetHandler("OnMouseEnter", ZO_Options_OnMouseEnter)
-        label:SetHandler("OnMouseExit", ZO_Options_OnMouseExit)
-    end
-
-    control.scroll = wm:CreateControl(nil, control, CT_SCROLL)
-    local scroll = control.scroll
-    scroll:SetParent(control)
-    scroll:SetAnchor(TOPLEFT, label, BOTTOMLEFT, 0, 10)
-    scroll:SetDimensionConstraints(width + 5, 0, width + 5, 0)
-
-    control.bg = wm:CreateControl(nil, label, CT_BACKDROP)
-    local bg = control.bg
-    bg:SetAnchor(TOPLEFT, label, TOPLEFT, -5, -5)
-    bg:SetAnchor(BOTTOMRIGHT, scroll, BOTTOMRIGHT, -7, 0)
-    bg:SetEdgeTexture("EsoUI\\Art\\Tooltips\\UI-Border.dds", 128, 16)
-    bg:SetCenterTexture("EsoUI\\Art\\Tooltips\\UI-TooltipCenter.dds")
-    bg:SetInsets(16, 16, -16, -16)
-
-    control.arrow = wm:CreateControl(nil, bg, CT_TEXTURE)
-    local arrow = control.arrow
-    arrow:SetDimensions(28, 28)
-    arrow:SetTexture("EsoUI\\Art\\Miscellaneous\\list_sortdown.dds") --list_sortup for the other way
-    arrow:SetAnchor(TOPRIGHT, bg, TOPRIGHT, -5, 5)
-
-    --figure out the cool animation later...
-    control.animation = am:CreateTimeline()
-    local animation = control.animation
-    animation:SetPlaybackType(ANIMATION_SIZE, 0) --2nd arg = loop count
-
-    control:SetResizeToFitDescendents(true)
-    control.open = false
-    label:SetHandler("OnMouseUp", AnimateSubmenu)
-    animation:SetHandler("OnStop", function(self, completedPlaying)
-        scroll:SetResizeToFitDescendents(control.open)
-        if control.open then
-            control.arrow:SetTexture("EsoUI\\Art\\Miscellaneous\\list_sortup.dds")
-            scroll:SetResizeToFitPadding(5, 20)
-        else
-            control.arrow:SetTexture("EsoUI\\Art\\Miscellaneous\\list_sortdown.dds")
-            scroll:SetResizeToFitPadding(5, 0)
-            scroll:SetHeight(0)
-        end
-    end)
-
-    --small strip at the bottom of the submenu that you can click to close it
-    control.btmToggle = wm:CreateControl(nil, control, CT_TEXTURE)
-    local btmToggle = control.btmToggle
-    btmToggle:SetMouseEnabled(true)
-    btmToggle:SetAnchor(BOTTOMLEFT, control.scroll, BOTTOMLEFT)
-    btmToggle:SetAnchor(BOTTOMRIGHT, control.scroll, BOTTOMRIGHT)
-    btmToggle:SetHeight(15)
-    btmToggle:SetAlpha(0)
-    btmToggle:SetHandler("OnMouseUp", AnimateSubmenu)
-
-    control.UpdateValue = UpdateValue
-
-    LAM.util.RegisterForRefreshIfNeeded(control)
-
-    return control
-end
+--[[submenuData = {
+    type = "submenu",
+    name = "Submenu Title", -- or string id or function returning a string
+    tooltip = "My submenu tooltip", -- -- or string id or function returning a string (optional)
+    controls = {sliderData, buttonData} --(optional) used by LAM
+    reference = "MyAddonSubmenu" --(optional) unique global reference to control
+} ]]
+
+local widgetVersion = 11
+local LAM = LibStub("LibAddonMenu-2.0")
+if not LAM:RegisterWidget("submenu", widgetVersion) then return end
+
+local wm = WINDOW_MANAGER
+local am = ANIMATION_MANAGER
+
+local function UpdateValue(control)
+    control.label:SetText(LAM.util.GetStringFromValue(control.data.name))
+    if control.data.tooltip then
+        control.label.data.tooltipText = LAM.util.GetStringFromValue(control.data.tooltip)
+    end
+end
+
+local function AnimateSubmenu(clicked)
+    local control = clicked:GetParent()
+    control.open = not control.open
+
+    if control.open then
+        control.animation:PlayFromStart()
+    else
+        control.animation:PlayFromEnd()
+    end
+end
+
+function LAMCreateControl.submenu(parent, submenuData, controlName)
+    local width = parent:GetWidth() - 45
+    local control = wm:CreateControl(controlName or submenuData.reference, parent.scroll or parent, CT_CONTROL)
+    control.panel = parent
+    control.data = submenuData
+
+    control.label = wm:CreateControlFromVirtual(nil, control, "ZO_Options_SectionTitleLabel")
+    local label = control.label
+    label:SetAnchor(TOPLEFT, control, TOPLEFT, 5, 5)
+    label:SetDimensions(width, 30)
+    label:SetWrapMode(TEXT_WRAP_MODE_ELLIPSIS)
+    label:SetText(LAM.util.GetStringFromValue(submenuData.name))
+    label:SetMouseEnabled(true)
+    if submenuData.tooltip then
+        label.data = {tooltipText = LAM.util.GetStringFromValue(submenuData.tooltip)}
+        label:SetHandler("OnMouseEnter", ZO_Options_OnMouseEnter)
+        label:SetHandler("OnMouseExit", ZO_Options_OnMouseExit)
+    end
+
+    control.scroll = wm:CreateControl(nil, control, CT_SCROLL)
+    local scroll = control.scroll
+    scroll:SetParent(control)
+    scroll:SetAnchor(TOPLEFT, label, BOTTOMLEFT, 0, 10)
+    scroll:SetDimensionConstraints(width + 5, 0, width + 5, 0)
+
+    control.bg = wm:CreateControl(nil, label, CT_BACKDROP)
+    local bg = control.bg
+    bg:SetAnchor(TOPLEFT, label, TOPLEFT, -5, -5)
+    bg:SetAnchor(BOTTOMRIGHT, scroll, BOTTOMRIGHT, -7, 0)
+    bg:SetEdgeTexture("EsoUI\\Art\\Tooltips\\UI-Border.dds", 128, 16)
+    bg:SetCenterTexture("EsoUI\\Art\\Tooltips\\UI-TooltipCenter.dds")
+    bg:SetInsets(16, 16, -16, -16)
+
+    control.arrow = wm:CreateControl(nil, bg, CT_TEXTURE)
+    local arrow = control.arrow
+    arrow:SetDimensions(28, 28)
+    arrow:SetTexture("EsoUI\\Art\\Miscellaneous\\list_sortdown.dds") --list_sortup for the other way
+    arrow:SetAnchor(TOPRIGHT, bg, TOPRIGHT, -5, 5)
+
+    --figure out the cool animation later...
+    control.animation = am:CreateTimeline()
+    local animation = control.animation
+    animation:SetPlaybackType(ANIMATION_SIZE, 0) --2nd arg = loop count
+
+    control:SetResizeToFitDescendents(true)
+    control.open = false
+    label:SetHandler("OnMouseUp", AnimateSubmenu)
+    animation:SetHandler("OnStop", function(self, completedPlaying)
+        scroll:SetResizeToFitDescendents(control.open)
+        if control.open then
+            control.arrow:SetTexture("EsoUI\\Art\\Miscellaneous\\list_sortup.dds")
+            scroll:SetResizeToFitPadding(5, 20)
+        else
+            control.arrow:SetTexture("EsoUI\\Art\\Miscellaneous\\list_sortdown.dds")
+            scroll:SetResizeToFitPadding(5, 0)
+            scroll:SetHeight(0)
+        end
+    end)
+
+    --small strip at the bottom of the submenu that you can click to close it
+    control.btmToggle = wm:CreateControl(nil, control, CT_TEXTURE)
+    local btmToggle = control.btmToggle
+    btmToggle:SetMouseEnabled(true)
+    btmToggle:SetAnchor(BOTTOMLEFT, control.scroll, BOTTOMLEFT)
+    btmToggle:SetAnchor(BOTTOMRIGHT, control.scroll, BOTTOMRIGHT)
+    btmToggle:SetHeight(15)
+    btmToggle:SetAlpha(0)
+    btmToggle:SetHandler("OnMouseUp", AnimateSubmenu)
+
+    control.UpdateValue = UpdateValue
+
+    LAM.util.RegisterForRefreshIfNeeded(control)
+
+    return control
+end
diff --git a/TaosGroupTools/libs/LibAddonMenu-2.0/controls/texture.lua b/TaosGroupTools/libs/LibAddonMenu-2.0/controls/texture.lua
index 29dda7c..4604fea 100644
--- a/TaosGroupTools/libs/LibAddonMenu-2.0/controls/texture.lua
+++ b/TaosGroupTools/libs/LibAddonMenu-2.0/controls/texture.lua
@@ -1,45 +1,45 @@
---[[textureData = {
-    type = "texture",
-    image = "file/path.dds",
-    imageWidth = 64, --max of 250 for half width, 510 for full
-    imageHeight = 32, --max of 100
-    tooltip = "Image's tooltip text.", -- or string id or function returning a string (optional)
-    width = "full", --or "half" (optional)
-    reference = "MyAddonTexture" --(optional) unique global reference to control
-} ]]
-
---add texture coords support?
-
-local widgetVersion = 9
-local LAM = LibStub("LibAddonMenu-2.0")
-if not LAM:RegisterWidget("texture", widgetVersion) then return end
-
-local wm = WINDOW_MANAGER
-
-local MIN_HEIGHT = 26
-function LAMCreateControl.texture(parent, textureData, controlName)
-    local control = LAM.util.CreateBaseControl(parent, textureData, controlName)
-    local width = control:GetWidth()
-    control:SetResizeToFitDescendents(true)
-
-    if control.isHalfWidth then --note these restrictions
-        control:SetDimensionConstraints(width / 2, MIN_HEIGHT, width / 2, MIN_HEIGHT * 4)
-    else
-        control:SetDimensionConstraints(width, MIN_HEIGHT, width, MIN_HEIGHT * 4)
-    end
-
-    control.texture = wm:CreateControl(nil, control, CT_TEXTURE)
-    local texture = control.texture
-    texture:SetAnchor(CENTER)
-    texture:SetDimensions(textureData.imageWidth, textureData.imageHeight)
-    texture:SetTexture(textureData.image)
-
-    if textureData.tooltip then
-        texture:SetMouseEnabled(true)
-        texture.data = {tooltipText = LAM.util.GetStringFromValue(textureData.tooltip)}
-        texture:SetHandler("OnMouseEnter", ZO_Options_OnMouseEnter)
-        texture:SetHandler("OnMouseExit", ZO_Options_OnMouseExit)
-    end
-
-    return control
-end
+--[[textureData = {
+    type = "texture",
+    image = "file/path.dds",
+    imageWidth = 64, --max of 250 for half width, 510 for full
+    imageHeight = 32, --max of 100
+    tooltip = "Image's tooltip text.", -- or string id or function returning a string (optional)
+    width = "full", --or "half" (optional)
+    reference = "MyAddonTexture" --(optional) unique global reference to control
+} ]]
+
+--add texture coords support?
+
+local widgetVersion = 9
+local LAM = LibStub("LibAddonMenu-2.0")
+if not LAM:RegisterWidget("texture", widgetVersion) then return end
+
+local wm = WINDOW_MANAGER
+
+local MIN_HEIGHT = 26
+function LAMCreateControl.texture(parent, textureData, controlName)
+    local control = LAM.util.CreateBaseControl(parent, textureData, controlName)
+    local width = control:GetWidth()
+    control:SetResizeToFitDescendents(true)
+
+    if control.isHalfWidth then --note these restrictions
+        control:SetDimensionConstraints(width / 2, MIN_HEIGHT, width / 2, MIN_HEIGHT * 4)
+    else
+        control:SetDimensionConstraints(width, MIN_HEIGHT, width, MIN_HEIGHT * 4)
+    end
+
+    control.texture = wm:CreateControl(nil, control, CT_TEXTURE)
+    local texture = control.texture
+    texture:SetAnchor(CENTER)
+    texture:SetDimensions(textureData.imageWidth, textureData.imageHeight)
+    texture:SetTexture(textureData.image)
+
+    if textureData.tooltip then
+        texture:SetMouseEnabled(true)
+        texture.data = {tooltipText = LAM.util.GetStringFromValue(textureData.tooltip)}
+        texture:SetHandler("OnMouseEnter", ZO_Options_OnMouseEnter)
+        texture:SetHandler("OnMouseExit", ZO_Options_OnMouseExit)
+    end
+
+    return control
+end
diff --git a/TaosGroupTools/libs/LibGPS/LICENSE b/TaosGroupTools/libs/LibGPS/LICENSE
index ed4a182..08d4a0b 100644
--- a/TaosGroupTools/libs/LibGPS/LICENSE
+++ b/TaosGroupTools/libs/LibGPS/LICENSE
@@ -1,201 +1,201 @@
-               The Artistic License 2.0
-
-           Copyright (c) 2017 sirinsidiator
-
-     Everyone is permitted to copy and distribute verbatim copies
-      of this license document, but changing it is not allowed.
-
-Preamble
-
-This license establishes the terms under which a given free software
-Package may be copied, modified, distributed, and/or redistributed.
-The intent is that the Copyright Holder maintains some artistic
-control over the development of that Package while still keeping the
-Package available as open source and free software.
-
-You are always permitted to make arrangements wholly outside of this
-license directly with the Copyright Holder of a given Package.  If the
-terms of this license do not permit the full use that you propose to
-make of the Package, you should contact the Copyright Holder and seek
-a different licensing arrangement.
-
-Definitions
-
-    "Copyright Holder" means the individual(s) or organization(s)
-    named in the copyright notice for the entire Package.
-
-    "Contributor" means any party that has contributed code or other
-    material to the Package, in accordance with the Copyright Holder's
-    procedures.
-
-    "You" and "your" means any person who would like to copy,
-    distribute, or modify the Package.
-
-    "Package" means the collection of files distributed by the
-    Copyright Holder, and derivatives of that collection and/or of
-    those files. A given Package may consist of either the Standard
-    Version, or a Modified Version.
-
-    "Distribute" means providing a copy of the Package or making it
-    accessible to anyone else, or in the case of a company or
-    organization, to others outside of your company or organization.
-
-    "Distributor Fee" means any fee that you charge for Distributing
-    this Package or providing support for this Package to another
-    party.  It does not mean licensing fees.
-
-    "Standard Version" refers to the Package if it has not been
-    modified, or has been modified only in ways explicitly requested
-    by the Copyright Holder.
-
-    "Modified Version" means the Package, if it has been changed, and
-    such changes were not explicitly requested by the Copyright
-    Holder.
-
-    "Original License" means this Artistic License as Distributed with
-    the Standard Version of the Package, in its current version or as
-    it may be modified by The Perl Foundation in the future.
-
-    "Source" form means the source code, documentation source, and
-    configuration files for the Package.
-
-    "Compiled" form means the compiled bytecode, object code, binary,
-    or any other form resulting from mechanical transformation or
-    translation of the Source form.
-
-
-Permission for Use and Modification Without Distribution
-
-(1)  You are permitted to use the Standard Version and create and use
-Modified Versions for any purpose without restriction, provided that
-you do not Distribute the Modified Version.
-
-
-Permissions for Redistribution of the Standard Version
-
-(2)  You may Distribute verbatim copies of the Source form of the
-Standard Version of this Package in any medium without restriction,
-either gratis or for a Distributor Fee, provided that you duplicate
-all of the original copyright notices and associated disclaimers.  At
-your discretion, such verbatim copies may or may not include a
-Compiled form of the Package.
-
-(3)  You may apply any bug fixes, portability changes, and other
-modifications made available from the Copyright Holder.  The resulting
-Package will still be considered the Standard Version, and as such
-will be subject to the Original License.
-
-
-Distribution of Modified Versions of the Package as Source
-
-(4)  You may Distribute your Modified Version as Source (either gratis
-or for a Distributor Fee, and with or without a Compiled form of the
-Modified Version) provided that you clearly document how it differs
-from the Standard Version, including, but not limited to, documenting
-any non-standard features, executables, or modules, and provided that
-you do at least ONE of the following:
-
-    (a)  make the Modified Version available to the Copyright Holder
-    of the Standard Version, under the Original License, so that the
-    Copyright Holder may include your modifications in the Standard
-    Version.
-
-    (b)  ensure that installation of your Modified Version does not
-    prevent the user installing or running the Standard Version. In
-    addition, the Modified Version must bear a name that is different
-    from the name of the Standard Version.
-
-    (c)  allow anyone who receives a copy of the Modified Version to
-    make the Source form of the Modified Version available to others
-    under
-
-    (i)  the Original License or
-
-    (ii)  a license that permits the licensee to freely copy,
-    modify and redistribute the Modified Version using the same
-    licensing terms that apply to the copy that the licensee
-    received, and requires that the Source form of the Modified
-    Version, and of any works derived from it, be made freely
-    available in that license fees are prohibited but Distributor
-    Fees are allowed.
-
-
-Distribution of Compiled Forms of the Standard Version
-or Modified Versions without the Source
-
-(5)  You may Distribute Compiled forms of the Standard Version without
-the Source, provided that you include complete instructions on how to
-get the Source of the Standard Version.  Such instructions must be
-valid at the time of your distribution.  If these instructions, at any
-time while you are carrying out such distribution, become invalid, you
-must provide new instructions on demand or cease further distribution.
-If you provide valid instructions or cease distribution within thirty
-days after you become aware that the instructions are invalid, then
-you do not forfeit any of your rights under this license.
-
-(6)  You may Distribute a Modified Version in Compiled form without
-the Source, provided that you comply with Section 4 with respect to
-the Source of the Modified Version.
-
-
-Aggregating or Linking the Package
-
-(7)  You may aggregate the Package (either the Standard Version or
-Modified Version) with other packages and Distribute the resulting
-aggregation provided that you do not charge a licensing fee for the
-Package.  Distributor Fees are permitted, and licensing fees for other
-components in the aggregation are permitted. The terms of this license
-apply to the use and Distribution of the Standard or Modified Versions
-as included in the aggregation.
-
-(8) You are permitted to link Modified and Standard Versions with
-other works, to embed the Package in a larger work of your own, or to
-build stand-alone binary or bytecode versions of applications that
-include the Package, and Distribute the result without restriction,
-provided the result does not expose a direct interface to the Package.
-
-
-Items That are Not Considered Part of a Modified Version
-
-(9) Works (including, but not limited to, modules and scripts) that
-merely extend or make use of the Package, do not, by themselves, cause
-the Package to be a Modified Version.  In addition, such works are not
-considered parts of the Package itself, and are not subject to the
-terms of this license.
-
-
-General Provisions
-
-(10)  Any use, modification, and distribution of the Standard or
-Modified Versions is governed by this Artistic License. By using,
-modifying or distributing the Package, you accept this license. Do not
-use, modify, or distribute the Package, if you do not accept this
-license.
-
-(11)  If your Modified Version has been derived from a Modified
-Version made by someone other than you, you are nevertheless required
-to ensure that your Modified Version complies with the requirements of
-this license.
-
-(12)  This license does not grant you the right to use any trademark,
-service mark, tradename, or logo of the Copyright Holder.
-
-(13)  This license includes the non-exclusive, worldwide,
-free-of-charge patent license to make, have made, use, offer to sell,
-sell, import and otherwise transfer the Package with respect to any
-patent claims licensable by the Copyright Holder that are necessarily
-infringed by the Package. If you institute patent litigation
-(including a cross-claim or counterclaim) against any party alleging
-that the Package constitutes direct or contributory patent
-infringement, then this Artistic License to you shall terminate on the
-date that such litigation is filed.
-
-(14)  Disclaimer of Warranty:
-THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS
-IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. THE IMPLIED
-WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
-NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY YOUR LOCAL
-LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR CONTRIBUTOR WILL
-BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
-DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE, EVEN IF
-ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+               The Artistic License 2.0
+
+           Copyright (c) 2017 sirinsidiator
+
+     Everyone is permitted to copy and distribute verbatim copies
+      of this license document, but changing it is not allowed.
+
+Preamble
+
+This license establishes the terms under which a given free software
+Package may be copied, modified, distributed, and/or redistributed.
+The intent is that the Copyright Holder maintains some artistic
+control over the development of that Package while still keeping the
+Package available as open source and free software.
+
+You are always permitted to make arrangements wholly outside of this
+license directly with the Copyright Holder of a given Package.  If the
+terms of this license do not permit the full use that you propose to
+make of the Package, you should contact the Copyright Holder and seek
+a different licensing arrangement.
+
+Definitions
+
+    "Copyright Holder" means the individual(s) or organization(s)
+    named in the copyright notice for the entire Package.
+
+    "Contributor" means any party that has contributed code or other
+    material to the Package, in accordance with the Copyright Holder's
+    procedures.
+
+    "You" and "your" means any person who would like to copy,
+    distribute, or modify the Package.
+
+    "Package" means the collection of files distributed by the
+    Copyright Holder, and derivatives of that collection and/or of
+    those files. A given Package may consist of either the Standard
+    Version, or a Modified Version.
+
+    "Distribute" means providing a copy of the Package or making it
+    accessible to anyone else, or in the case of a company or
+    organization, to others outside of your company or organization.
+
+    "Distributor Fee" means any fee that you charge for Distributing
+    this Package or providing support for this Package to another
+    party.  It does not mean licensing fees.
+
+    "Standard Version" refers to the Package if it has not been
+    modified, or has been modified only in ways explicitly requested
+    by the Copyright Holder.
+
+    "Modified Version" means the Package, if it has been changed, and
+    such changes were not explicitly requested by the Copyright
+    Holder.
+
+    "Original License" means this Artistic License as Distributed with
+    the Standard Version of the Package, in its current version or as
+    it may be modified by The Perl Foundation in the future.
+
+    "Source" form means the source code, documentation source, and
+    configuration files for the Package.
+
+    "Compiled" form means the compiled bytecode, object code, binary,
+    or any other form resulting from mechanical transformation or
+    translation of the Source form.
+
+
+Permission for Use and Modification Without Distribution
+
+(1)  You are permitted to use the Standard Version and create and use
+Modified Versions for any purpose without restriction, provided that
+you do not Distribute the Modified Version.
+
+
+Permissions for Redistribution of the Standard Version
+
+(2)  You may Distribute verbatim copies of the Source form of the
+Standard Version of this Package in any medium without restriction,
+either gratis or for a Distributor Fee, provided that you duplicate
+all of the original copyright notices and associated disclaimers.  At
+your discretion, such verbatim copies may or may not include a
+Compiled form of the Package.
+
+(3)  You may apply any bug fixes, portability changes, and other
+modifications made available from the Copyright Holder.  The resulting
+Package will still be considered the Standard Version, and as such
+will be subject to the Original License.
+
+
+Distribution of Modified Versions of the Package as Source
+
+(4)  You may Distribute your Modified Version as Source (either gratis
+or for a Distributor Fee, and with or without a Compiled form of the
+Modified Version) provided that you clearly document how it differs
+from the Standard Version, including, but not limited to, documenting
+any non-standard features, executables, or modules, and provided that
+you do at least ONE of the following:
+
+    (a)  make the Modified Version available to the Copyright Holder
+    of the Standard Version, under the Original License, so that the
+    Copyright Holder may include your modifications in the Standard
+    Version.
+
+    (b)  ensure that installation of your Modified Version does not
+    prevent the user installing or running the Standard Version. In
+    addition, the Modified Version must bear a name that is different
+    from the name of the Standard Version.
+
+    (c)  allow anyone who receives a copy of the Modified Version to
+    make the Source form of the Modified Version available to others
+    under
+
+    (i)  the Original License or
+
+    (ii)  a license that permits the licensee to freely copy,
+    modify and redistribute the Modified Version using the same
+    licensing terms that apply to the copy that the licensee
+    received, and requires that the Source form of the Modified
+    Version, and of any works derived from it, be made freely
+    available in that license fees are prohibited but Distributor
+    Fees are allowed.
+
+
+Distribution of Compiled Forms of the Standard Version
+or Modified Versions without the Source
+
+(5)  You may Distribute Compiled forms of the Standard Version without
+the Source, provided that you include complete instructions on how to
+get the Source of the Standard Version.  Such instructions must be
+valid at the time of your distribution.  If these instructions, at any
+time while you are carrying out such distribution, become invalid, you
+must provide new instructions on demand or cease further distribution.
+If you provide valid instructions or cease distribution within thirty
+days after you become aware that the instructions are invalid, then
+you do not forfeit any of your rights under this license.
+
+(6)  You may Distribute a Modified Version in Compiled form without
+the Source, provided that you comply with Section 4 with respect to
+the Source of the Modified Version.
+
+
+Aggregating or Linking the Package
+
+(7)  You may aggregate the Package (either the Standard Version or
+Modified Version) with other packages and Distribute the resulting
+aggregation provided that you do not charge a licensing fee for the
+Package.  Distributor Fees are permitted, and licensing fees for other
+components in the aggregation are permitted. The terms of this license
+apply to the use and Distribution of the Standard or Modified Versions
+as included in the aggregation.
+
+(8) You are permitted to link Modified and Standard Versions with
+other works, to embed the Package in a larger work of your own, or to
+build stand-alone binary or bytecode versions of applications that
+include the Package, and Distribute the result without restriction,
+provided the result does not expose a direct interface to the Package.
+
+
+Items That are Not Considered Part of a Modified Version
+
+(9) Works (including, but not limited to, modules and scripts) that
+merely extend or make use of the Package, do not, by themselves, cause
+the Package to be a Modified Version.  In addition, such works are not
+considered parts of the Package itself, and are not subject to the
+terms of this license.
+
+
+General Provisions
+
+(10)  Any use, modification, and distribution of the Standard or
+Modified Versions is governed by this Artistic License. By using,
+modifying or distributing the Package, you accept this license. Do not
+use, modify, or distribute the Package, if you do not accept this
+license.
+
+(11)  If your Modified Version has been derived from a Modified
+Version made by someone other than you, you are nevertheless required
+to ensure that your Modified Version complies with the requirements of
+this license.
+
+(12)  This license does not grant you the right to use any trademark,
+service mark, tradename, or logo of the Copyright Holder.
+
+(13)  This license includes the non-exclusive, worldwide,
+free-of-charge patent license to make, have made, use, offer to sell,
+sell, import and otherwise transfer the Package with respect to any
+patent claims licensable by the Copyright Holder that are necessarily
+infringed by the Package. If you institute patent litigation
+(including a cross-claim or counterclaim) against any party alleging
+that the Package constitutes direct or contributory patent
+infringement, then this Artistic License to you shall terminate on the
+date that such litigation is filed.
+
+(14)  Disclaimer of Warranty:
+THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS
+IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. THE IMPLIED
+WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
+NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY YOUR LOCAL
+LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR CONTRIBUTOR WILL
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/TaosGroupTools/libs/LibGPS/LibGPS.lua b/TaosGroupTools/libs/LibGPS/LibGPS.lua
index 982a447..22c44fa 100644
--- a/TaosGroupTools/libs/LibGPS/LibGPS.lua
+++ b/TaosGroupTools/libs/LibGPS/LibGPS.lua
@@ -1,770 +1,770 @@
--- LibGPS2 & its files © sirinsidiator                          --
--- Distributed under The Artistic License 2.0 (see LICENSE)     --
-------------------------------------------------------------------
-
-local LIB_NAME = "LibGPS2"
-local lib = LibStub:NewLibrary(LIB_NAME, 14)
-
-if not lib then
-    return
-    -- already loaded and no upgrade necessary
-end
-
-local LMP = LibStub("LibMapPing", true)
-if(not LMP) then
-    error(string.format("[%s] Cannot load without LibMapPing", LIB_NAME))
-end
-
-local DUMMY_PIN_TYPE = LIB_NAME .. "DummyPin"
-local LIB_IDENTIFIER_FINALIZE = LIB_NAME .. "_Finalize"
-lib.LIB_EVENT_STATE_CHANGED = "OnLibGPS2MeasurementChanged"
-
-local LOG_WARNING = "Warning"
-local LOG_NOTICE = "Notice"
-local LOG_DEBUG = "Debug"
-
-local POSITION_MIN = 0.085
-local POSITION_MAX = 0.915
-
-local TAMRIEL_MAP_INDEX = 1
-
-local rootMaps = lib.rootMaps or {}
-lib.rootMaps = rootMaps
-
---lib.debugMode = 1 -- TODO
-lib.mapMeasurements = lib.mapMeasurements or {}
-local mapMeasurements = lib.mapMeasurements
-lib.mapStack = lib.mapStack or {}
-local mapStack = lib.mapStack
-lib.suppressCount = lib.suppressCount or 0
-
-local MAP_PIN_TYPE_PLAYER_WAYPOINT = MAP_PIN_TYPE_PLAYER_WAYPOINT
-local currentWaypointX, currentWaypointY, currentWaypointMapId = 0, 0, nil
-local needWaypointRestore = false
-local orgSetMapToMapListIndex = nil
-local orgSetMapToPlayerLocation = nil
-local orgSetMapFloor = nil
-local orgProcessMapClick = nil
-local orgFunctions = {}
-local measuring = false
-
-SLASH_COMMANDS["/libgpsdebug"] = function(value)
-    lib.debugMode = (tonumber(value) == 1)
-    df("[%s] debug mode %s", LIB_NAME, lib.debugMode and "enabled" or "disabled")
-end
-
-local function LogMessage(type, message, ...)
-    if not lib.debugMode then return end
-    df("[%s] %s: %s", LIB_NAME, type, zo_strjoin(" ", message, ...))
-end
-
-local function GetAddon()
-    local addOn
-    local function errornous() addOn = 'a' + 1 end
-    local function errorHandler(err) addOn = string.match(err, "'GetAddon'.+user:/AddOns/(.-:.-):") end
-    xpcall(errornous, errorHandler)
-    return addOn
-end
-
-local function FinalizeMeasurement()
-    EVENT_MANAGER:UnregisterForUpdate(LIB_IDENTIFIER_FINALIZE)
-    while lib.suppressCount > 0 do
-        LMP:UnsuppressPing(MAP_PIN_TYPE_PLAYER_WAYPOINT)
-        lib.suppressCount = lib.suppressCount - 1
-    end
-    if needWaypointRestore then
-        LogMessage(LOG_DEBUG, "Update waypoint pin", LMP:GetMapPing(MAP_PIN_TYPE_PLAYER_WAYPOINT))
-        LMP:RefreshMapPin(MAP_PIN_TYPE_PLAYER_WAYPOINT)
-        needWaypointRestore = false
-    end
-    measuring = false
-    CALLBACK_MANAGER:FireCallbacks(lib.LIB_EVENT_STATE_CHANGED, measuring)
-end
-
-local function HandlePingEvent(pingType, pingTag, x, y, isPingOwner)
-    if(not isPingOwner or pingType ~= MAP_PIN_TYPE_PLAYER_WAYPOINT or not measuring) then return end
-    -- we delay our handler until all events have been fired and so that other addons can react to it first in case they use IsMeasuring
-    EVENT_MANAGER:UnregisterForUpdate(LIB_IDENTIFIER_FINALIZE)
-    EVENT_MANAGER:RegisterForUpdate(LIB_IDENTIFIER_FINALIZE, 0, FinalizeMeasurement)
-end
-
-local function GetPlayerPosition()
-    return GetMapPlayerPosition("player")
-end
-
-local function GetPlayerWaypoint()
-    return LMP:GetMapPing(MAP_PIN_TYPE_PLAYER_WAYPOINT)
-end
-
-local function SetMeasurementWaypoint(x, y)
-    -- this waypoint stays invisible for others
-    lib.suppressCount = lib.suppressCount + 1
-    LMP:SuppressPing(MAP_PIN_TYPE_PLAYER_WAYPOINT)
-    LMP:SetMapPing(MAP_PIN_TYPE_PLAYER_WAYPOINT, MAP_TYPE_LOCATION_CENTERED, x, y)
-end
-
-local function SetPlayerWaypoint(x, y)
-    LMP:SetMapPing(MAP_PIN_TYPE_PLAYER_WAYPOINT, MAP_TYPE_LOCATION_CENTERED, x, y)
-end
-
-local function RemovePlayerWaypoint()
-    LMP:RemoveMapPing(MAP_PIN_TYPE_PLAYER_WAYPOINT)
-end
-
-local function GetReferencePoints()
-    local x1, y1 = GetPlayerPosition()
-    local x2, y2 = GetPlayerWaypoint()
-    return x1, y1, x2, y2
-end
-
-local function IsMapMeasured(mapId)
-    return (mapMeasurements[mapId or GetMapTileTexture()] ~= nil)
-end
-
-local function StoreTamrielMapMeasurements()
-    -- no need to actually measure the world map
-    if (orgSetMapToMapListIndex(TAMRIEL_MAP_INDEX) ~= SET_MAP_RESULT_FAILED) then
-        local measurement = {
-            scaleX = 1,
-            scaleY = 1,
-            offsetX = 0,
-            offsetY = 0,
-            mapIndex = TAMRIEL_MAP_INDEX,
-            zoneIndex = GetCurrentMapZoneIndex()
-        }
-        mapMeasurements[GetMapTileTexture()] = measurement
-        rootMaps[TAMRIEL_MAP_INDEX] = measurement
-        return true
-    end
-
-    return false
-end
-
-local function CalculateMeasurements(mapId, localX, localY)
-    -- select the map corner farthest from the player position
-    local wpX, wpY = POSITION_MIN, POSITION_MIN
-    -- on some maps we cannot set the waypoint to the map border (e.g. Aurdion)
-    -- Opposite corner:
-    if (localX < 0.5) then wpX = POSITION_MAX end
-    if (localY < 0.5) then wpY = POSITION_MAX end
-
-    SetMeasurementWaypoint(wpX, wpY)
-
-    -- add local points to seen maps
-    local measurementPositions = {}
-    table.insert(measurementPositions, { mapId = mapId, pX = localX, pY = localY, wpX = wpX, wpY = wpY })
-
-    -- switch to zone map in order to get the mapIndex for the current location
-    local x1, y1, x2, y2
-    while not(GetMapType() == MAPTYPE_ZONE and GetMapContentType() ~= MAP_CONTENT_DUNGEON) do
-        if (MapZoomOut() ~= SET_MAP_RESULT_MAP_CHANGED) then break end
-        -- collect measurements for all maps we come through on our way to the zone map
-        x1, y1, x2, y2 = GetReferencePoints()
-        table.insert(measurementPositions, { mapId = GetMapTileTexture(), pX = x1, pY = y1, wpX = x2, wpY = y2 })
-    end
-
-    -- some non-zone maps like Eyevea zoom directly to the Tamriel map
-    local mapIndex = GetCurrentMapIndex() or TAMRIEL_MAP_INDEX
-    local zoneIndex = GetCurrentMapZoneIndex()
-
-    -- switch to world map so we can calculate the global map scale and offset
-    if orgSetMapToMapListIndex(TAMRIEL_MAP_INDEX) == SET_MAP_RESULT_FAILED then
-        -- failed to switch to the world map
-        LogMessage(LOG_NOTICE, "Could not switch to world map")
-        return
-    end
-
-    -- get the two reference points on the world map
-    x1, y1, x2, y2 = GetReferencePoints()
-
-    -- calculate scale and offset for all maps that we saw
-    local scaleX, scaleY, offsetX, offsetY
-    for _, m in ipairs(measurementPositions) do
-        if (mapMeasurements[m.mapId]) then break end -- we always go up in the hierarchy so we can stop once a measurement already exists
-        LogMessage(LOG_DEBUG, "Store map measurement for", m.mapId:sub(10, -7))
-        scaleX = (x2 - x1) / (m.wpX - m.pX)
-        scaleY = (y2 - y1) / (m.wpY - m.pY)
-        offsetX = x1 - m.pX * scaleX
-        offsetY = y1 - m.pY * scaleY
-        if (math.abs(scaleX - scaleY) > 1e-3) then
-            LogMessage(LOG_WARNING, "Current map measurement might be wrong", m.mapId:sub(10, -7), mapIndex, m.pX, m.pY, m.wpX, m.wpY, x1, y1, x2, y2, offsetX, offsetY, scaleX, scaleY)
-        end
-
-        -- store measurements
-        mapMeasurements[m.mapId] = {
-            scaleX = scaleX,
-            scaleY = scaleY,
-            offsetX = offsetX,
-            offsetY = offsetY,
-            mapIndex = mapIndex,
-            zoneIndex = zoneIndex
-        }
-    end
-    return mapIndex
-end
-
-local function StoreCurrentWaypoint()
-    currentWaypointX, currentWaypointY = GetPlayerWaypoint()
-    currentWaypointMapId = GetMapTileTexture()
-end
-
-local function ClearCurrentWaypoint()
-    currentWaypointX, currentWaypointY = 0, 0, nil
-end
-
-local function GetExtraMapMeasurement(extraMapIndex)
-    -- switch to the map
-    orgSetMapToMapListIndex(extraMapIndex)
-    local extraMapId = GetMapTileTexture()
-    if(not IsMapMeasured(extraMapId)) then
-        -- calculate the measurements of map without worrying about the waypoint
-        local mapIndex = CalculateMeasurements(extraMapId, GetPlayerPosition())
-        if (mapIndex ~= extraMapIndex) then
-            local name = GetMapInfo(extraMapIndex)
-            name = zo_strformat("<<C:1>>", name)
-            LogMessage(LOG_WARNING, "CalculateMeasurements returned different index while measuring ", name, " map. expected:", extraMapIndex, "actual:", mapIndex)
-            if (not IsMapMeasured(extraMapId)) then
-                LogMessage(LOG_WARNING, "Failed to measure ", name, " map.")
-                return
-            end
-        end
-    end
-    return mapMeasurements[extraMapId]
-end
-
-local function RestoreCurrentWaypoint()
-    if(not currentWaypointMapId) then
-        LogMessage(LOG_DEBUG, "Called RestoreCurrentWaypoint without calling StoreCurrentWaypoint.")
-        return
-    end
-
-    local wasSet = false
-    if (currentWaypointX ~= 0 or currentWaypointY ~= 0) then
-        -- calculate waypoint position on the worldmap
-        local measurements = mapMeasurements[currentWaypointMapId]
-        local x = currentWaypointX * measurements.scaleX + measurements.offsetX
-        local y = currentWaypointY * measurements.scaleY + measurements.offsetY
-
-        for rootMapIndex, measurements in pairs(rootMaps) do
-            if not measurements then
-                measurements = GetExtraMapMeasurement(rootMapIndex)
-                rootMaps[rootMapIndex] = measurements
-            end
-            if(measurements) then
-                if(x > measurements.offsetX and x < (measurements.offsetX + measurements.scaleX) and
-                    y > measurements.offsetY and y < (measurements.offsetY + measurements.scaleY)) then
-                    if(orgSetMapToMapListIndex(rootMapIndex) ~= SET_MAP_RESULT_FAILED) then
-                        -- calculate waypoint coodinates within root map
-                        x = (x - measurements.offsetX) / measurements.scaleX
-                        y = (y - measurements.offsetY) / measurements.scaleY
-                        SetPlayerWaypoint(x, y)
-                        wasSet = true
-                        break
-                    end
-                end
-            end
-        end
-        if (not wasSet) then
-            LogMessage(LOG_DEBUG, "Cannot reset waypoint because it was outside of our reach")
-        end
-    end
-
-    if(wasSet) then
-        LogMessage(LOG_DEBUG, "Waypoint was restored, request pin update")
-        needWaypointRestore = true -- we need to update the pin on the worldmap afterwards
-    else
-        RemovePlayerWaypoint()
-    end
-    ClearCurrentWaypoint()
-end
-
-local function ConnectToWorldMap()
-    lib.panAndZoom = ZO_WorldMap_GetPanAndZoom()
-    lib.mapPinManager = ZO_WorldMap_GetPinManager()
-    if (_G[DUMMY_PIN_TYPE]) then return end
-    ZO_WorldMap_AddCustomPin(DUMMY_PIN_TYPE, function(pinManager) end , nil, { level = 0, size = 0, texture = "" })
-    ZO_WorldMap_SetCustomPinEnabled(_G[DUMMY_PIN_TYPE], false)
-end
-
-local function HookSetMapToFunction(funcName)
-    local orgFunction = _G[funcName]
-    orgFunctions[funcName] = orgFunction
-    local function NewFunction(...)
-        local result = orgFunction(...)
-        if(result ~= SET_MAP_RESULT_MAP_FAILED and not IsMapMeasured()) then
-            LogMessage(LOG_DEBUG, funcName)
-
-            local success, mapResult = lib:CalculateMapMeasurements(false)
-            if(mapResult ~= SET_MAP_RESULT_CURRENT_MAP_UNCHANGED) then
-                result = mapResult
-            end
-            orgFunction(...)
-        end
-        -- All stuff is done before anyone triggers an "OnWorldMapChanged" event due to this result
-        return result
-    end
-    _G[funcName] = NewFunction
-end
-
-local function HookSetMapToPlayerLocation()
-    orgSetMapToPlayerLocation = SetMapToPlayerLocation
-    orgFunctions["SetMapToPlayerLocation"] = orgSetMapToPlayerLocation
-    local function NewSetMapToPlayerLocation(...)
-        if not DoesUnitExist("player") then return SET_MAP_RESULT_MAP_FAILED end
-        local result = orgSetMapToPlayerLocation(...)
-        if(result ~= SET_MAP_RESULT_MAP_FAILED and not IsMapMeasured()) then
-            LogMessage(LOG_DEBUG, "SetMapToPlayerLocation")
-
-            local success, mapResult = lib:CalculateMapMeasurements(false)
-            if(mapResult ~= SET_MAP_RESULT_CURRENT_MAP_UNCHANGED) then
-                result = mapResult
-            end
-            orgSetMapToPlayerLocation(...)
-        end
-        -- All stuff is done before anyone triggers an "OnWorldMapChanged" event due to this result
-        return result
-    end
-    SetMapToPlayerLocation = NewSetMapToPlayerLocation
-end
-
-local function HookSetMapToMapListIndex()
-    orgSetMapToMapListIndex = SetMapToMapListIndex
-    orgFunctions["SetMapToMapListIndex"] = orgSetMapToMapListIndex
-    local function NewSetMapToMapListIndex(mapIndex)
-        local result = orgSetMapToMapListIndex(mapIndex)
-        if(result ~= SET_MAP_RESULT_MAP_FAILED and not IsMapMeasured()) then
-            LogMessage(LOG_DEBUG, "SetMapToMapListIndex")
-
-            local success, mapResult = lib:CalculateMapMeasurements(false)
-            if(mapResult ~= SET_MAP_RESULT_CURRENT_MAP_UNCHANGED) then
-                result = mapResult
-            end
-            orgSetMapToMapListIndex(mapIndex)
-        end
-
-        -- All stuff is done before anyone triggers an "OnWorldMapChanged" event due to this result
-        return result
-    end
-    SetMapToMapListIndex = NewSetMapToMapListIndex
-end
-
-local function HookProcessMapClick()
-    orgProcessMapClick = ProcessMapClick
-    orgFunctions["ProcessMapClick"] = orgProcessMapClick
-    local function NewProcessMapClick(...)
-        local result = orgProcessMapClick(...)
-        if(result ~= SET_MAP_RESULT_MAP_FAILED and not IsMapMeasured()) then
-            LogMessage(LOG_DEBUG, "ProcessMapClick")
-            local success, mapResult = lib:CalculateMapMeasurements(true)
-            if(mapResult ~= SET_MAP_RESULT_CURRENT_MAP_UNCHANGED) then
-                result = mapResult
-            end
-            -- Returning is done via clicking already
-        end
-        return result
-    end
-    ProcessMapClick = NewProcessMapClick
-end
-
-local function HookSetMapFloor()
-    orgSetMapFloor = SetMapFloor
-    orgFunctions["SetMapFloor"] = orgSetMapFloor
-    local function NewSetMapFloor(...)
-        local result = orgSetMapFloor(...)
-        if result ~= SET_MAP_RESULT_MAP_FAILED and not IsMapMeasured() then
-            LogMessage(LOG_DEBUG, "SetMapFloor")
-            local success, mapResult = lib:CalculateMapMeasurements(true)
-            if(mapResult ~= SET_MAP_RESULT_CURRENT_MAP_UNCHANGED) then
-                result = mapResult
-            end
-            orgSetMapFloor(...)
-        end
-        return result
-    end
-    SetMapFloor = NewSetMapFloor
-end
-
-local function Initialize() -- wait until we have defined all functions
-    --- Unregister handler from older libGPS ( < 3)
-    EVENT_MANAGER:UnregisterForEvent("LibGPS2_SaveWaypoint", EVENT_PLAYER_DEACTIVATED)
-    EVENT_MANAGER:UnregisterForEvent("LibGPS2_RestoreWaypoint", EVENT_PLAYER_ACTIVATED)
-
-    --- Unregister handler from older libGPS ( <= 5.1)
-    EVENT_MANAGER:UnregisterForEvent(LIB_NAME .. "_Init", EVENT_PLAYER_ACTIVATED)
-    --- Unregister handler from older libGPS, as it is now managed by LibMapPing ( >= 6)
-    EVENT_MANAGER:UnregisterForEvent(LIB_NAME .. "_UnmuteMapPing", EVENT_MAP_PING)
-
-    if (lib.Unload) then
-        -- Undo action from older libGPS ( >= 5.2)
-        lib:Unload()
-        if (lib.suppressCount > 0) then
-            if lib.debugMode then zo_callLater(function() LogMessage(LOG_WARNING, "There is a measure in progress before loading is completed.") end, 2000) end
-            FinalizeMeasurement()
-        end
-    end
-
-    --- Register new Unload
-    function lib:Unload()
-        for funcName, func in pairs(orgFunctions) do
-            _G[funcName] = func
-        end
-
-        LMP:UnregisterCallback("AfterPingAdded", HandlePingEvent)
-        LMP:UnregisterCallback("AfterPingRemoved", HandlePingEvent)
-
-        rootMaps, mapMeasurements, mapStack = nil, nil, nil
-    end
-
-    ConnectToWorldMap()
-
-    HookSetMapToFunction("SetMapToQuestCondition")
-    HookSetMapToFunction("SetMapToQuestStepEnding")
-    HookSetMapToFunction("SetMapToQuestZone")
-    HookSetMapToPlayerLocation()
-    HookSetMapToMapListIndex()
-    HookProcessMapClick()
-    HookSetMapFloor()
-
-    StoreTamrielMapMeasurements()
-
-    local function addRootMap(zoneId)
-        local mapIndex = GetMapIndexByZoneId(zoneId)
-        if mapIndex then rootMaps[mapIndex] = false end
-    end
-    addRootMap(347) -- Coldhabour
-    addRootMap(980) -- Clockwork City
-    -- Any future extra dimension map here
-
-    SetMapToPlayerLocation() -- initial measurement so we can get back to where we are currently
-
-    LMP:RegisterCallback("AfterPingAdded", HandlePingEvent)
-    LMP:RegisterCallback("AfterPingRemoved", HandlePingEvent)
-end
-
------------------------- public functions ----------------------
-
---- Returns true as long as the player exists.
-function lib:IsReady()
-    return DoesUnitExist("player")
-end
-
---- Returns true if the library is currently doing any measurements.
-function lib:IsMeasuring()
-    return measuring
-end
-
---- Removes all cached measurement values.
-function lib:ClearMapMeasurements()
-    mapMeasurements = { }
-end
-
---- Removes the cached measurement values for the map that is currently active.
-function lib:ClearCurrentMapMeasurements()
-    local mapId = GetMapTileTexture()
-    mapMeasurements[mapId] = nil
-end
-
---- Returns a table with the measurement values for the active map or nil if the measurements could not be calculated for some reason.
---- The table contains scaleX, scaleY, offsetX, offsetY and mapIndex.
---- scaleX and scaleY are the dimensions of the active map on the Tamriel map.
---- offsetX and offsetY are the offset of the top left corner on the Tamriel map.
---- mapIndex is the mapIndex of the parent zone of the current map.
-function lib:GetCurrentMapMeasurements()
-    local mapId = GetMapTileTexture()
-    if (not mapMeasurements[mapId]) then
-        -- try to calculate the measurements if they are not yet available
-        lib:CalculateMapMeasurements()
-    end
-    return mapMeasurements[mapId]
-end
-
---- Returns the mapIndex and zoneIndex of the parent zone for the currently set map.
---- return[1] number - The mapIndex of the parent zone
---- return[2] number - The zoneIndex of the parent zone
-function lib:GetCurrentMapParentZoneIndices()
-    local measurements = lib:GetCurrentMapMeasurements()
-    local mapIndex = measurements.mapIndex
-    if(not measurements.zoneIndex) then
-        lib:PushCurrentMap()
-        SetMapToMapListIndex(mapIndex)
-        measurements.zoneIndex = GetCurrentMapZoneIndex()
-        lib:PopCurrentMap()
-    end
-    local zoneIndex = measurements.zoneIndex
-    return mapIndex, zoneIndex
-end
-
---- Calculates the measurements for the current map and all parent maps.
---- This method does nothing if there is already a cached measurement for the active map.
---- return[1] boolean - True, if a valid measurement was calculated
---- return[2] SetMapResultCode - Specifies if the map has changed or failed during measurement (independent of the actual result of the measurement)
-function lib:CalculateMapMeasurements(returnToInitialMap)
-    -- cosmic map cannot be measured (GetMapPlayerWaypoint returns 0,0)
-    if (GetMapType() == MAPTYPE_COSMIC) then return false, SET_MAP_RESULT_CURRENT_MAP_UNCHANGED end
-
-    -- no need to take measurements more than once
-    local mapId = GetMapTileTexture()
-    if (mapMeasurements[mapId] or mapId == "") then return false end
-
-    if (lib.debugMode) then
-        LogMessage("Called from", GetAddon(), "for", mapId)
-    end
-
-    -- get the player position on the current map
-    local localX, localY = GetPlayerPosition()
-    if (localX == 0 and localY == 0) then
-        -- cannot take measurements while player position is not initialized
-        return false, SET_MAP_RESULT_CURRENT_MAP_UNCHANGED
-    end
-
-    returnToInitialMap = (returnToInitialMap ~= false)
-
-    measuring = true
-    CALLBACK_MANAGER:FireCallbacks(lib.LIB_EVENT_STATE_CHANGED, measuring)
-
-    -- check some facts about the current map, so we can reset it later
-    -- local oldMapIsZoneMap, oldMapFloor, oldMapFloorCount
-    if returnToInitialMap then
-        lib:PushCurrentMap()
-    end
-
-    local hasWaypoint = LMP:HasMapPing(MAP_PIN_TYPE_PLAYER_WAYPOINT)
-    if(hasWaypoint) then StoreCurrentWaypoint() end
-
-    local mapIndex = CalculateMeasurements(mapId, localX, localY)
-
-    -- Until now, the waypoint was abused. Now the waypoint must be restored or removed again (not from Lua only).
-    if(hasWaypoint) then
-        RestoreCurrentWaypoint()
-    else
-        RemovePlayerWaypoint()
-    end
-
-    if (returnToInitialMap) then
-        local result = lib:PopCurrentMap()
-        return true, result
-    end
-
-    return true, (mapId == GetMapTileTexture()) and SET_MAP_RESULT_CURRENT_MAP_UNCHANGED or SET_MAP_RESULT_MAP_CHANGED
-end
-
---- Converts the given map coordinates on the current map into coordinates on the Tamriel map.
---- Returns x and y on the world map and the mapIndex of the parent zone
---- or nil if the measurements of the active map are not available.
-function lib:LocalToGlobal(x, y)
-    local measurements = lib:GetCurrentMapMeasurements()
-    if (measurements) then
-        x = x * measurements.scaleX + measurements.offsetX
-        y = y * measurements.scaleY + measurements.offsetY
-        return x, y, measurements.mapIndex
-    end
-end
-
---- Converts the given global coordinates into a position on the active map.
---- Returns x and y on the current map or nil if the measurements of the active map are not available.
-function lib:GlobalToLocal(x, y)
-    local measurements = lib:GetCurrentMapMeasurements()
-    if (measurements) then
-        x = (x - measurements.offsetX) / measurements.scaleX
-        y = (y - measurements.offsetY) / measurements.scaleY
-        return x, y
-    end
-end
-
---- Converts the given map coordinates on the specified zone map into coordinates on the Tamriel map.
---- This method is useful if you want to convert global positions from the old LibGPS version into the new format.
---- Returns x and y on the world map and the mapIndex of the parent zone
---- or nil if the measurements of the zone map are not available.
-function lib:ZoneToGlobal(mapIndex, x, y)
-    lib:GetCurrentMapMeasurements()
-    -- measurement done in here:
-    SetMapToMapListIndex(mapIndex)
-    x, y, mapIndex = lib:LocalToGlobal(x, y)
-    return x, y, mapIndex
-end
-
---- This function zooms and pans to the specified position on the active map.
-function lib:PanToMapPosition(x, y)
-    -- if we don't have access to the mapPinManager we cannot do anything
-    if (not self.mapPinManager) then return end
-    local mapPinManager = self.mapPinManager
-    -- create dummy pin
-    local pin = mapPinManager:CreatePin(_G[DUMMY_PIN_TYPE], "libgpsdummy", x, y)
-
-    self.panAndZoom:PanToPin(pin)
-
-    -- cleanup
-    mapPinManager:RemovePins(DUMMY_PIN_TYPE)
-end
-
-local function FakeZO_WorldMap_IsMapChangingAllowed() return true end
-local function FakeSetMapToMapListIndex() return SET_MAP_RESULT_MAP_CHANGED end
-local FakeCALLBACK_MANAGER = { FireCallbacks = function() end }
-
---- This function sets the current map as player chosen so it won't switch back to the previous map.
-function lib:SetPlayerChoseCurrentMap()
-    -- replace the original functions
-    local oldIsChangingAllowed = ZO_WorldMap_IsMapChangingAllowed
-    ZO_WorldMap_IsMapChangingAllowed = FakeZO_WorldMap_IsMapChangingAllowed
-
-    local oldSetMapToMapListIndex = SetMapToMapListIndex
-    SetMapToMapListIndex = FakeSetMapToMapListIndex
-
-    local oldCALLBACK_MANAGER = CALLBACK_MANAGER
-    CALLBACK_MANAGER = FakeCALLBACK_MANAGER
-
-    -- make our rigged call to set the player chosen flag
-    ZO_WorldMap_SetMapByIndex()
-
-    -- cleanup
-    ZO_WorldMap_IsMapChangingAllowed = oldIsChangingAllowed
-    SetMapToMapListIndex = oldSetMapToMapListIndex
-    CALLBACK_MANAGER = oldCALLBACK_MANAGER
-end
-
---- Sets the best matching root map: Tamriel, Cold Harbour or Clockwork City and what ever will come.
---- Returns SET_MAP_RESULT_FAILED, SET_MAP_RESULT_MAP_CHANGED depending on the result of the API calls.
-function lib:SetMapToRootMap(x, y)
-    local result = SET_MAP_RESULT_FAILED
-    for rootMapIndex, measurements in pairs(rootMaps) do
-        if (not measurements) then
-            measurements = GetExtraMapMeasurement(rootMapIndex)
-            rootMaps[rootMapIndex] = measurements
-            result = SET_MAP_RESULT_MAP_CHANGED
-        end
-        if (measurements) then
-            if (x > measurements.offsetX and x < (measurements.offsetX + measurements.scaleX) and
-                y > measurements.offsetY and y < (measurements.offsetY + measurements.scaleY)) then
-                if (orgSetMapToMapListIndex(rootMapIndex) ~= SET_MAP_RESULT_FAILED) then
-                    return SET_MAP_RESULT_MAP_CHANGED
-                end
-            end
-        end
-    end
-    return result
-end
-
---- Repeatedly calls ProcessMapClick on the given global position starting on the Tamriel map until nothing more would happen.
---- Returns SET_MAP_RESULT_FAILED, SET_MAP_RESULT_MAP_CHANGED or SET_MAP_RESULT_CURRENT_MAP_UNCHANGED depending on the result of the API calls.
-function lib:MapZoomInMax(x, y)
-    local result = lib:SetMapToRootMap(x, y)
-
-    if (result ~= SET_MAP_RESULT_FAILED) then
-        local localX, localY = lib:GlobalToLocal(x, y)
-
-        while WouldProcessMapClick(localX, localY) do
-            result = orgProcessMapClick(localX, localY)
-            if (result == SET_MAP_RESULT_FAILED) then break end
-            localX, localY = lib:GlobalToLocal(x, y)
-        end
-    end
-
-    return result
-end
-
---- Stores information about how we can back to this map on a stack.
--- There is no panAndZoom:GetCurrentOffset(), yet
-local function CalculateContainerAnchorOffsets()
-    local containerCenterX, containerCenterY = ZO_WorldMapContainer:GetCenter()
-    local scrollCenterX, scrollCenterY = ZO_WorldMapScroll:GetCenter()
-    return containerCenterX - scrollCenterX, containerCenterY - scrollCenterY
-end
-function lib:PushCurrentMap()
-    local wasPlayerLocation, targetMapTileTexture, currentMapFloor, currentMapFloorCount, currentMapIndex, zoom, offsetX, offsetY
-    currentMapIndex = GetCurrentMapIndex()
-    wasPlayerLocation = DoesCurrentMapMatchMapForPlayerLocation()
-    targetMapTileTexture = GetMapTileTexture()
-    currentMapFloor, currentMapFloorCount = GetMapFloorInfo()
-    zoom = self.panAndZoom:GetCurrentZoom()
-    offsetX, offsetY = CalculateContainerAnchorOffsets()
-
-    mapStack[#mapStack + 1] = { wasPlayerLocation, targetMapTileTexture, currentMapFloor, currentMapFloorCount, currentMapIndex, zoom, offsetX, offsetY }
-end
-
---- Switches to the map that was put on the stack last.
---- Returns SET_MAP_RESULT_FAILED, SET_MAP_RESULT_MAP_CHANGED or SET_MAP_RESULT_CURRENT_MAP_UNCHANGED depending on the result of the API calls.
-function lib:PopCurrentMap()
-    local result = SET_MAP_RESULT_FAILED
-    local data = table.remove(mapStack, #mapStack)
-    if(not data) then
-        LogMessage(LOG_DEBUG, "PopCurrentMap failed. No data on map stack.")
-        return result
-    end
-
-    local wasPlayerLocation, targetMapTileTexture, currentMapFloor, currentMapFloorCount, currentMapIndex, zoom, offsetX, offsetY = unpack(data)
-    local currentTileTexture = GetMapTileTexture()
-    if(currentTileTexture ~= targetMapTileTexture) then
-        if(wasPlayerLocation) then
-            result = orgSetMapToPlayerLocation()
-
-        elseif(currentMapIndex ~= nil and currentMapIndex > 0) then -- set to a zone map
-            result = orgSetMapToMapListIndex(currentMapIndex)
-
-        else -- here is where it gets tricky
-            local target = mapMeasurements[targetMapTileTexture]
-            if(not target) then -- always just return to player map if we cannot restore the previous map.
-                LogMessage(LOG_DEBUG, string.format("No measurement for \"%s\". Returning to player location.", targetMapTileTexture))
-                return orgSetMapToPlayerLocation()
-            end
-
-            -- switch to the parent zone
-            if(target.mapIndex == TAMRIEL_MAP_INDEX) then -- zone map has no mapIndex (e.g. Eyevea or Hew's Bane on first PTS patch for update 9)
-                -- switch to the tamriel map just in case
-                result = orgSetMapToMapListIndex(TAMRIEL_MAP_INDEX)
-                if(result == SET_MAP_RESULT_FAILED) then return result end
-                -- get global coordinates of target map center
-                local x = target.offsetX + (target.scaleX / 2)
-                local y = target.offsetY + (target.scaleY / 2)
-                if(not WouldProcessMapClick(x, y)) then
-                    LogMessage(LOG_DEBUG, string.format("Cannot process click at %s/%s on map \"%s\" in order to get to \"%s\". Returning to player location instead.", tostring(x), tostring(y), GetMapTileTexture(), targetMapTileTexture))
-                    return orgSetMapToPlayerLocation()
-                end
-                result = orgProcessMapClick(x, y)
-                if(result == SET_MAP_RESULT_FAILED) then return result end
-            else
-                result = orgSetMapToMapListIndex(target.mapIndex)
-                if(result == SET_MAP_RESULT_FAILED) then return result end
-            end
-
-            -- switch to the sub zone
-            currentTileTexture = GetMapTileTexture()
-            if(currentTileTexture ~= targetMapTileTexture) then
-                -- determine where on the zone map we have to click to get to the sub zone map
-                -- get global coordinates of target map center
-                local x = target.offsetX + (target.scaleX / 2)
-                local y = target.offsetY + (target.scaleY / 2)
-                -- transform to local coordinates
-                local current = mapMeasurements[currentTileTexture]
-                if(not current) then
-                    LogMessage(LOG_DEBUG, string.format("No measurement for \"%s\". Returning to player location.", currentTileTexture))
-                    return orgSetMapToPlayerLocation()
-                end
-
-                x = (x - current.offsetX) / current.scaleX
-                y = (y - current.offsetY) / current.scaleY
-
-                if(not WouldProcessMapClick(x, y)) then
-                    LogMessage(LOG_DEBUG, string.format("Cannot process click at %s/%s on map \"%s\" in order to get to \"%s\". Returning to player location instead.", tostring(x), tostring(y), GetMapTileTexture(), targetMapTileTexture))
-                    return orgSetMapToPlayerLocation()
-                end
-                result = orgProcessMapClick(x, y)
-                if(result == SET_MAP_RESULT_FAILED) then return result end
-            end
-
-            -- switch to the correct floor (e.g. Elden Root)
-            if (currentMapFloorCount > 0) then
-                result = orgSetMapFloor(currentMapFloor)
-            end
-            if (result ~= SET_MAP_RESULT_FAILED) then
-                lib.panAndZoom:SetCurrentZoom(zoom)
-                lib.panAndZoom:SetCurrentOffset(offsetX, offsetY)
-            end
-        end
-    else
-        result = SET_MAP_RESULT_CURRENT_MAP_UNCHANGED
-    end
-
-    return result
-end
-
-Initialize()
+-- LibGPS2 & its files © sirinsidiator                          --
+-- Distributed under The Artistic License 2.0 (see LICENSE)     --
+------------------------------------------------------------------
+
+local LIB_NAME = "LibGPS2"
+local lib = LibStub:NewLibrary(LIB_NAME, 14)
+
+if not lib then
+    return
+    -- already loaded and no upgrade necessary
+end
+
+local LMP = LibStub("LibMapPing", true)
+if(not LMP) then
+    error(string.format("[%s] Cannot load without LibMapPing", LIB_NAME))
+end
+
+local DUMMY_PIN_TYPE = LIB_NAME .. "DummyPin"
+local LIB_IDENTIFIER_FINALIZE = LIB_NAME .. "_Finalize"
+lib.LIB_EVENT_STATE_CHANGED = "OnLibGPS2MeasurementChanged"
+
+local LOG_WARNING = "Warning"
+local LOG_NOTICE = "Notice"
+local LOG_DEBUG = "Debug"
+
+local POSITION_MIN = 0.085
+local POSITION_MAX = 0.915
+
+local TAMRIEL_MAP_INDEX = 1
+
+local rootMaps = lib.rootMaps or {}
+lib.rootMaps = rootMaps
+
+--lib.debugMode = 1 -- TODO
+lib.mapMeasurements = lib.mapMeasurements or {}
+local mapMeasurements = lib.mapMeasurements
+lib.mapStack = lib.mapStack or {}
+local mapStack = lib.mapStack
+lib.suppressCount = lib.suppressCount or 0
+
+local MAP_PIN_TYPE_PLAYER_WAYPOINT = MAP_PIN_TYPE_PLAYER_WAYPOINT
+local currentWaypointX, currentWaypointY, currentWaypointMapId = 0, 0, nil
+local needWaypointRestore = false
+local orgSetMapToMapListIndex = nil
+local orgSetMapToPlayerLocation = nil
+local orgSetMapFloor = nil
+local orgProcessMapClick = nil
+local orgFunctions = {}
+local measuring = false
+
+SLASH_COMMANDS["/libgpsdebug"] = function(value)
+    lib.debugMode = (tonumber(value) == 1)
+    df("[%s] debug mode %s", LIB_NAME, lib.debugMode and "enabled" or "disabled")
+end
+
+local function LogMessage(type, message, ...)
+    if not lib.debugMode then return end
+    df("[%s] %s: %s", LIB_NAME, type, zo_strjoin(" ", message, ...))
+end
+
+local function GetAddon()
+    local addOn
+    local function errornous() addOn = 'a' + 1 end
+    local function errorHandler(err) addOn = string.match(err, "'GetAddon'.+user:/AddOns/(.-:.-):") end
+    xpcall(errornous, errorHandler)
+    return addOn
+end
+
+local function FinalizeMeasurement()
+    EVENT_MANAGER:UnregisterForUpdate(LIB_IDENTIFIER_FINALIZE)
+    while lib.suppressCount > 0 do
+        LMP:UnsuppressPing(MAP_PIN_TYPE_PLAYER_WAYPOINT)
+        lib.suppressCount = lib.suppressCount - 1
+    end
+    if needWaypointRestore then
+        LogMessage(LOG_DEBUG, "Update waypoint pin", LMP:GetMapPing(MAP_PIN_TYPE_PLAYER_WAYPOINT))
+        LMP:RefreshMapPin(MAP_PIN_TYPE_PLAYER_WAYPOINT)
+        needWaypointRestore = false
+    end
+    measuring = false
+    CALLBACK_MANAGER:FireCallbacks(lib.LIB_EVENT_STATE_CHANGED, measuring)
+end
+
+local function HandlePingEvent(pingType, pingTag, x, y, isPingOwner)
+    if(not isPingOwner or pingType ~= MAP_PIN_TYPE_PLAYER_WAYPOINT or not measuring) then return end
+    -- we delay our handler until all events have been fired and so that other addons can react to it first in case they use IsMeasuring
+    EVENT_MANAGER:UnregisterForUpdate(LIB_IDENTIFIER_FINALIZE)
+    EVENT_MANAGER:RegisterForUpdate(LIB_IDENTIFIER_FINALIZE, 0, FinalizeMeasurement)
+end
+
+local function GetPlayerPosition()
+    return GetMapPlayerPosition("player")
+end
+
+local function GetPlayerWaypoint()
+    return LMP:GetMapPing(MAP_PIN_TYPE_PLAYER_WAYPOINT)
+end
+
+local function SetMeasurementWaypoint(x, y)
+    -- this waypoint stays invisible for others
+    lib.suppressCount = lib.suppressCount + 1
+    LMP:SuppressPing(MAP_PIN_TYPE_PLAYER_WAYPOINT)
+    LMP:SetMapPing(MAP_PIN_TYPE_PLAYER_WAYPOINT, MAP_TYPE_LOCATION_CENTERED, x, y)
+end
+
+local function SetPlayerWaypoint(x, y)
+    LMP:SetMapPing(MAP_PIN_TYPE_PLAYER_WAYPOINT, MAP_TYPE_LOCATION_CENTERED, x, y)
+end
+
+local function RemovePlayerWaypoint()
+    LMP:RemoveMapPing(MAP_PIN_TYPE_PLAYER_WAYPOINT)
+end
+
+local function GetReferencePoints()
+    local x1, y1 = GetPlayerPosition()
+    local x2, y2 = GetPlayerWaypoint()
+    return x1, y1, x2, y2
+end
+
+local function IsMapMeasured(mapId)
+    return (mapMeasurements[mapId or GetMapTileTexture()] ~= nil)
+end
+
+local function StoreTamrielMapMeasurements()
+    -- no need to actually measure the world map
+    if (orgSetMapToMapListIndex(TAMRIEL_MAP_INDEX) ~= SET_MAP_RESULT_FAILED) then
+        local measurement = {
+            scaleX = 1,
+            scaleY = 1,
+            offsetX = 0,
+            offsetY = 0,
+            mapIndex = TAMRIEL_MAP_INDEX,
+            zoneIndex = GetCurrentMapZoneIndex()
+        }
+        mapMeasurements[GetMapTileTexture()] = measurement
+        rootMaps[TAMRIEL_MAP_INDEX] = measurement
+        return true
+    end
+
+    return false
+end
+
+local function CalculateMeasurements(mapId, localX, localY)
+    -- select the map corner farthest from the player position
+    local wpX, wpY = POSITION_MIN, POSITION_MIN
+    -- on some maps we cannot set the waypoint to the map border (e.g. Aurdion)
+    -- Opposite corner:
+    if (localX < 0.5) then wpX = POSITION_MAX end
+    if (localY < 0.5) then wpY = POSITION_MAX end
+
+    SetMeasurementWaypoint(wpX, wpY)
+
+    -- add local points to seen maps
+    local measurementPositions = {}
+    table.insert(measurementPositions, { mapId = mapId, pX = localX, pY = localY, wpX = wpX, wpY = wpY })
+
+    -- switch to zone map in order to get the mapIndex for the current location
+    local x1, y1, x2, y2
+    while not(GetMapType() == MAPTYPE_ZONE and GetMapContentType() ~= MAP_CONTENT_DUNGEON) do
+        if (MapZoomOut() ~= SET_MAP_RESULT_MAP_CHANGED) then break end
+        -- collect measurements for all maps we come through on our way to the zone map
+        x1, y1, x2, y2 = GetReferencePoints()
+        table.insert(measurementPositions, { mapId = GetMapTileTexture(), pX = x1, pY = y1, wpX = x2, wpY = y2 })
+    end
+
+    -- some non-zone maps like Eyevea zoom directly to the Tamriel map
+    local mapIndex = GetCurrentMapIndex() or TAMRIEL_MAP_INDEX
+    local zoneIndex = GetCurrentMapZoneIndex()
+
+    -- switch to world map so we can calculate the global map scale and offset
+    if orgSetMapToMapListIndex(TAMRIEL_MAP_INDEX) == SET_MAP_RESULT_FAILED then
+        -- failed to switch to the world map
+        LogMessage(LOG_NOTICE, "Could not switch to world map")
+        return
+    end
+
+    -- get the two reference points on the world map
+    x1, y1, x2, y2 = GetReferencePoints()
+
+    -- calculate scale and offset for all maps that we saw
+    local scaleX, scaleY, offsetX, offsetY
+    for _, m in ipairs(measurementPositions) do
+        if (mapMeasurements[m.mapId]) then break end -- we always go up in the hierarchy so we can stop once a measurement already exists
+        LogMessage(LOG_DEBUG, "Store map measurement for", m.mapId:sub(10, -7))
+        scaleX = (x2 - x1) / (m.wpX - m.pX)
+        scaleY = (y2 - y1) / (m.wpY - m.pY)
+        offsetX = x1 - m.pX * scaleX
+        offsetY = y1 - m.pY * scaleY
+        if (math.abs(scaleX - scaleY) > 1e-3) then
+            LogMessage(LOG_WARNING, "Current map measurement might be wrong", m.mapId:sub(10, -7), mapIndex, m.pX, m.pY, m.wpX, m.wpY, x1, y1, x2, y2, offsetX, offsetY, scaleX, scaleY)
+        end
+
+        -- store measurements
+        mapMeasurements[m.mapId] = {
+            scaleX = scaleX,
+            scaleY = scaleY,
+            offsetX = offsetX,
+            offsetY = offsetY,
+            mapIndex = mapIndex,
+            zoneIndex = zoneIndex
+        }
+    end
+    return mapIndex
+end
+
+local function StoreCurrentWaypoint()
+    currentWaypointX, currentWaypointY = GetPlayerWaypoint()
+    currentWaypointMapId = GetMapTileTexture()
+end
+
+local function ClearCurrentWaypoint()
+    currentWaypointX, currentWaypointY = 0, 0, nil
+end
+
+local function GetExtraMapMeasurement(extraMapIndex)
+    -- switch to the map
+    orgSetMapToMapListIndex(extraMapIndex)
+    local extraMapId = GetMapTileTexture()
+    if(not IsMapMeasured(extraMapId)) then
+        -- calculate the measurements of map without worrying about the waypoint
+        local mapIndex = CalculateMeasurements(extraMapId, GetPlayerPosition())
+        if (mapIndex ~= extraMapIndex) then
+            local name = GetMapInfo(extraMapIndex)
+            name = zo_strformat("<<C:1>>", name)
+            LogMessage(LOG_WARNING, "CalculateMeasurements returned different index while measuring ", name, " map. expected:", extraMapIndex, "actual:", mapIndex)
+            if (not IsMapMeasured(extraMapId)) then
+                LogMessage(LOG_WARNING, "Failed to measure ", name, " map.")
+                return
+            end
+        end
+    end
+    return mapMeasurements[extraMapId]
+end
+
+local function RestoreCurrentWaypoint()
+    if(not currentWaypointMapId) then
+        LogMessage(LOG_DEBUG, "Called RestoreCurrentWaypoint without calling StoreCurrentWaypoint.")
+        return
+    end
+
+    local wasSet = false
+    if (currentWaypointX ~= 0 or currentWaypointY ~= 0) then
+        -- calculate waypoint position on the worldmap
+        local measurements = mapMeasurements[currentWaypointMapId]
+        local x = currentWaypointX * measurements.scaleX + measurements.offsetX
+        local y = currentWaypointY * measurements.scaleY + measurements.offsetY
+
+        for rootMapIndex, measurements in pairs(rootMaps) do
+            if not measurements then
+                measurements = GetExtraMapMeasurement(rootMapIndex)
+                rootMaps[rootMapIndex] = measurements
+            end
+            if(measurements) then
+                if(x > measurements.offsetX and x < (measurements.offsetX + measurements.scaleX) and
+                    y > measurements.offsetY and y < (measurements.offsetY + measurements.scaleY)) then
+                    if(orgSetMapToMapListIndex(rootMapIndex) ~= SET_MAP_RESULT_FAILED) then
+                        -- calculate waypoint coodinates within root map
+                        x = (x - measurements.offsetX) / measurements.scaleX
+                        y = (y - measurements.offsetY) / measurements.scaleY
+                        SetPlayerWaypoint(x, y)
+                        wasSet = true
+                        break
+                    end
+                end
+            end
+        end
+        if (not wasSet) then
+            LogMessage(LOG_DEBUG, "Cannot reset waypoint because it was outside of our reach")
+        end
+    end
+
+    if(wasSet) then
+        LogMessage(LOG_DEBUG, "Waypoint was restored, request pin update")
+        needWaypointRestore = true -- we need to update the pin on the worldmap afterwards
+    else
+        RemovePlayerWaypoint()
+    end
+    ClearCurrentWaypoint()
+end
+
+local function ConnectToWorldMap()
+    lib.panAndZoom = ZO_WorldMap_GetPanAndZoom()
+    lib.mapPinManager = ZO_WorldMap_GetPinManager()
+    if (_G[DUMMY_PIN_TYPE]) then return end
+    ZO_WorldMap_AddCustomPin(DUMMY_PIN_TYPE, function(pinManager) end , nil, { level = 0, size = 0, texture = "" })
+    ZO_WorldMap_SetCustomPinEnabled(_G[DUMMY_PIN_TYPE], false)
+end
+
+local function HookSetMapToFunction(funcName)
+    local orgFunction = _G[funcName]
+    orgFunctions[funcName] = orgFunction
+    local function NewFunction(...)
+        local result = orgFunction(...)
+        if(result ~= SET_MAP_RESULT_MAP_FAILED and not IsMapMeasured()) then
+            LogMessage(LOG_DEBUG, funcName)
+
+            local success, mapResult = lib:CalculateMapMeasurements(false)
+            if(mapResult ~= SET_MAP_RESULT_CURRENT_MAP_UNCHANGED) then
+                result = mapResult
+            end
+            orgFunction(...)
+        end
+        -- All stuff is done before anyone triggers an "OnWorldMapChanged" event due to this result
+        return result
+    end
+    _G[funcName] = NewFunction
+end
+
+local function HookSetMapToPlayerLocation()
+    orgSetMapToPlayerLocation = SetMapToPlayerLocation
+    orgFunctions["SetMapToPlayerLocation"] = orgSetMapToPlayerLocation
+    local function NewSetMapToPlayerLocation(...)
+        if not DoesUnitExist("player") then return SET_MAP_RESULT_MAP_FAILED end
+        local result = orgSetMapToPlayerLocation(...)
+        if(result ~= SET_MAP_RESULT_MAP_FAILED and not IsMapMeasured()) then
+            LogMessage(LOG_DEBUG, "SetMapToPlayerLocation")
+
+            local success, mapResult = lib:CalculateMapMeasurements(false)
+            if(mapResult ~= SET_MAP_RESULT_CURRENT_MAP_UNCHANGED) then
+                result = mapResult
+            end
+            orgSetMapToPlayerLocation(...)
+        end
+        -- All stuff is done before anyone triggers an "OnWorldMapChanged" event due to this result
+        return result
+    end
+    SetMapToPlayerLocation = NewSetMapToPlayerLocation
+end
+
+local function HookSetMapToMapListIndex()
+    orgSetMapToMapListIndex = SetMapToMapListIndex
+    orgFunctions["SetMapToMapListIndex"] = orgSetMapToMapListIndex
+    local function NewSetMapToMapListIndex(mapIndex)
+        local result = orgSetMapToMapListIndex(mapIndex)
+        if(result ~= SET_MAP_RESULT_MAP_FAILED and not IsMapMeasured()) then
+            LogMessage(LOG_DEBUG, "SetMapToMapListIndex")
+
+            local success, mapResult = lib:CalculateMapMeasurements(false)
+            if(mapResult ~= SET_MAP_RESULT_CURRENT_MAP_UNCHANGED) then
+                result = mapResult
+            end
+            orgSetMapToMapListIndex(mapIndex)
+        end
+
+        -- All stuff is done before anyone triggers an "OnWorldMapChanged" event due to this result
+        return result
+    end
+    SetMapToMapListIndex = NewSetMapToMapListIndex
+end
+
+local function HookProcessMapClick()
+    orgProcessMapClick = ProcessMapClick
+    orgFunctions["ProcessMapClick"] = orgProcessMapClick
+    local function NewProcessMapClick(...)
+        local result = orgProcessMapClick(...)
+        if(result ~= SET_MAP_RESULT_MAP_FAILED and not IsMapMeasured()) then
+            LogMessage(LOG_DEBUG, "ProcessMapClick")
+            local success, mapResult = lib:CalculateMapMeasurements(true)
+            if(mapResult ~= SET_MAP_RESULT_CURRENT_MAP_UNCHANGED) then
+                result = mapResult
+            end
+            -- Returning is done via clicking already
+        end
+        return result
+    end
+    ProcessMapClick = NewProcessMapClick
+end
+
+local function HookSetMapFloor()
+    orgSetMapFloor = SetMapFloor
+    orgFunctions["SetMapFloor"] = orgSetMapFloor
+    local function NewSetMapFloor(...)
+        local result = orgSetMapFloor(...)
+        if result ~= SET_MAP_RESULT_MAP_FAILED and not IsMapMeasured() then
+            LogMessage(LOG_DEBUG, "SetMapFloor")
+            local success, mapResult = lib:CalculateMapMeasurements(true)
+            if(mapResult ~= SET_MAP_RESULT_CURRENT_MAP_UNCHANGED) then
+                result = mapResult
+            end
+            orgSetMapFloor(...)
+        end
+        return result
+    end
+    SetMapFloor = NewSetMapFloor
+end
+
+local function Initialize() -- wait until we have defined all functions
+    --- Unregister handler from older libGPS ( < 3)
+    EVENT_MANAGER:UnregisterForEvent("LibGPS2_SaveWaypoint", EVENT_PLAYER_DEACTIVATED)
+    EVENT_MANAGER:UnregisterForEvent("LibGPS2_RestoreWaypoint", EVENT_PLAYER_ACTIVATED)
+
+    --- Unregister handler from older libGPS ( <= 5.1)
+    EVENT_MANAGER:UnregisterForEvent(LIB_NAME .. "_Init", EVENT_PLAYER_ACTIVATED)
+    --- Unregister handler from older libGPS, as it is now managed by LibMapPing ( >= 6)
+    EVENT_MANAGER:UnregisterForEvent(LIB_NAME .. "_UnmuteMapPing", EVENT_MAP_PING)
+
+    if (lib.Unload) then
+        -- Undo action from older libGPS ( >= 5.2)
+        lib:Unload()
+        if (lib.suppressCount > 0) then
+            if lib.debugMode then zo_callLater(function() LogMessage(LOG_WARNING, "There is a measure in progress before loading is completed.") end, 2000) end
+            FinalizeMeasurement()
+        end
+    end
+
+    --- Register new Unload
+    function lib:Unload()
+        for funcName, func in pairs(orgFunctions) do
+            _G[funcName] = func
+        end
+
+        LMP:UnregisterCallback("AfterPingAdded", HandlePingEvent)
+        LMP:UnregisterCallback("AfterPingRemoved", HandlePingEvent)
+
+        rootMaps, mapMeasurements, mapStack = nil, nil, nil
+    end
+
+    ConnectToWorldMap()
+
+    HookSetMapToFunction("SetMapToQuestCondition")
+    HookSetMapToFunction("SetMapToQuestStepEnding")
+    HookSetMapToFunction("SetMapToQuestZone")
+    HookSetMapToPlayerLocation()
+    HookSetMapToMapListIndex()
+    HookProcessMapClick()
+    HookSetMapFloor()
+
+    StoreTamrielMapMeasurements()
+
+    local function addRootMap(zoneId)
+        local mapIndex = GetMapIndexByZoneId(zoneId)
+        if mapIndex then rootMaps[mapIndex] = false end
+    end
+    addRootMap(347) -- Coldhabour
+    addRootMap(980) -- Clockwork City
+    -- Any future extra dimension map here
+
+    SetMapToPlayerLocation() -- initial measurement so we can get back to where we are currently
+
+    LMP:RegisterCallback("AfterPingAdded", HandlePingEvent)
+    LMP:RegisterCallback("AfterPingRemoved", HandlePingEvent)
+end
+
+------------------------ public functions ----------------------
+
+--- Returns true as long as the player exists.
+function lib:IsReady()
+    return DoesUnitExist("player")
+end
+
+--- Returns true if the library is currently doing any measurements.
+function lib:IsMeasuring()
+    return measuring
+end
+
+--- Removes all cached measurement values.
+function lib:ClearMapMeasurements()
+    mapMeasurements = { }
+end
+
+--- Removes the cached measurement values for the map that is currently active.
+function lib:ClearCurrentMapMeasurements()
+    local mapId = GetMapTileTexture()
+    mapMeasurements[mapId] = nil
+end
+
+--- Returns a table with the measurement values for the active map or nil if the measurements could not be calculated for some reason.
+--- The table contains scaleX, scaleY, offsetX, offsetY and mapIndex.
+--- scaleX and scaleY are the dimensions of the active map on the Tamriel map.
+--- offsetX and offsetY are the offset of the top left corner on the Tamriel map.
+--- mapIndex is the mapIndex of the parent zone of the current map.
+function lib:GetCurrentMapMeasurements()
+    local mapId = GetMapTileTexture()
+    if (not mapMeasurements[mapId]) then
+        -- try to calculate the measurements if they are not yet available
+        lib:CalculateMapMeasurements()
+    end
+    return mapMeasurements[mapId]
+end
+
+--- Returns the mapIndex and zoneIndex of the parent zone for the currently set map.
+--- return[1] number - The mapIndex of the parent zone
+--- return[2] number - The zoneIndex of the parent zone
+function lib:GetCurrentMapParentZoneIndices()
+    local measurements = lib:GetCurrentMapMeasurements()
+    local mapIndex = measurements.mapIndex
+    if(not measurements.zoneIndex) then
+        lib:PushCurrentMap()
+        SetMapToMapListIndex(mapIndex)
+        measurements.zoneIndex = GetCurrentMapZoneIndex()
+        lib:PopCurrentMap()
+    end
+    local zoneIndex = measurements.zoneIndex
+    return mapIndex, zoneIndex
+end
+
+--- Calculates the measurements for the current map and all parent maps.
+--- This method does nothing if there is already a cached measurement for the active map.
+--- return[1] boolean - True, if a valid measurement was calculated
+--- return[2] SetMapResultCode - Specifies if the map has changed or failed during measurement (independent of the actual result of the measurement)
+function lib:CalculateMapMeasurements(returnToInitialMap)
+    -- cosmic map cannot be measured (GetMapPlayerWaypoint returns 0,0)
+    if (GetMapType() == MAPTYPE_COSMIC) then return false, SET_MAP_RESULT_CURRENT_MAP_UNCHANGED end
+
+    -- no need to take measurements more than once
+    local mapId = GetMapTileTexture()
+    if (mapMeasurements[mapId] or mapId == "") then return false end
+
+    if (lib.debugMode) then
+        LogMessage("Called from", GetAddon(), "for", mapId)
+    end
+
+    -- get the player position on the current map
+    local localX, localY = GetPlayerPosition()
+    if (localX == 0 and localY == 0) then
+        -- cannot take measurements while player position is not initialized
+        return false, SET_MAP_RESULT_CURRENT_MAP_UNCHANGED
+    end
+
+    returnToInitialMap = (returnToInitialMap ~= false)
+
+    measuring = true
+    CALLBACK_MANAGER:FireCallbacks(lib.LIB_EVENT_STATE_CHANGED, measuring)
+
+    -- check some facts about the current map, so we can reset it later
+    -- local oldMapIsZoneMap, oldMapFloor, oldMapFloorCount
+    if returnToInitialMap then
+        lib:PushCurrentMap()
+    end
+
+    local hasWaypoint = LMP:HasMapPing(MAP_PIN_TYPE_PLAYER_WAYPOINT)
+    if(hasWaypoint) then StoreCurrentWaypoint() end
+
+    local mapIndex = CalculateMeasurements(mapId, localX, localY)
+
+    -- Until now, the waypoint was abused. Now the waypoint must be restored or removed again (not from Lua only).
+    if(hasWaypoint) then
+        RestoreCurrentWaypoint()
+    else
+        RemovePlayerWaypoint()
+    end
+
+    if (returnToInitialMap) then
+        local result = lib:PopCurrentMap()
+        return true, result
+    end
+
+    return true, (mapId == GetMapTileTexture()) and SET_MAP_RESULT_CURRENT_MAP_UNCHANGED or SET_MAP_RESULT_MAP_CHANGED
+end
+
+--- Converts the given map coordinates on the current map into coordinates on the Tamriel map.
+--- Returns x and y on the world map and the mapIndex of the parent zone
+--- or nil if the measurements of the active map are not available.
+function lib:LocalToGlobal(x, y)
+    local measurements = lib:GetCurrentMapMeasurements()
+    if (measurements) then
+        x = x * measurements.scaleX + measurements.offsetX
+        y = y * measurements.scaleY + measurements.offsetY
+        return x, y, measurements.mapIndex
+    end
+end
+
+--- Converts the given global coordinates into a position on the active map.
+--- Returns x and y on the current map or nil if the measurements of the active map are not available.
+function lib:GlobalToLocal(x, y)
+    local measurements = lib:GetCurrentMapMeasurements()
+    if (measurements) then
+        x = (x - measurements.offsetX) / measurements.scaleX
+        y = (y - measurements.offsetY) / measurements.scaleY
+        return x, y
+    end
+end
+
+--- Converts the given map coordinates on the specified zone map into coordinates on the Tamriel map.
+--- This method is useful if you want to convert global positions from the old LibGPS version into the new format.
+--- Returns x and y on the world map and the mapIndex of the parent zone
+--- or nil if the measurements of the zone map are not available.
+function lib:ZoneToGlobal(mapIndex, x, y)
+    lib:GetCurrentMapMeasurements()
+    -- measurement done in here:
+    SetMapToMapListIndex(mapIndex)
+    x, y, mapIndex = lib:LocalToGlobal(x, y)
+    return x, y, mapIndex
+end
+
+--- This function zooms and pans to the specified position on the active map.
+function lib:PanToMapPosition(x, y)
+    -- if we don't have access to the mapPinManager we cannot do anything
+    if (not self.mapPinManager) then return end
+    local mapPinManager = self.mapPinManager
+    -- create dummy pin
+    local pin = mapPinManager:CreatePin(_G[DUMMY_PIN_TYPE], "libgpsdummy", x, y)
+
+    self.panAndZoom:PanToPin(pin)
+
+    -- cleanup
+    mapPinManager:RemovePins(DUMMY_PIN_TYPE)
+end
+
+local function FakeZO_WorldMap_IsMapChangingAllowed() return true end
+local function FakeSetMapToMapListIndex() return SET_MAP_RESULT_MAP_CHANGED end
+local FakeCALLBACK_MANAGER = { FireCallbacks = function() end }
+
+--- This function sets the current map as player chosen so it won't switch back to the previous map.
+function lib:SetPlayerChoseCurrentMap()
+    -- replace the original functions
+    local oldIsChangingAllowed = ZO_WorldMap_IsMapChangingAllowed
+    ZO_WorldMap_IsMapChangingAllowed = FakeZO_WorldMap_IsMapChangingAllowed
+
+    local oldSetMapToMapListIndex = SetMapToMapListIndex
+    SetMapToMapListIndex = FakeSetMapToMapListIndex
+
+    local oldCALLBACK_MANAGER = CALLBACK_MANAGER
+    CALLBACK_MANAGER = FakeCALLBACK_MANAGER
+
+    -- make our rigged call to set the player chosen flag
+    ZO_WorldMap_SetMapByIndex()
+
+    -- cleanup
+    ZO_WorldMap_IsMapChangingAllowed = oldIsChangingAllowed
+    SetMapToMapListIndex = oldSetMapToMapListIndex
+    CALLBACK_MANAGER = oldCALLBACK_MANAGER
+end
+
+--- Sets the best matching root map: Tamriel, Cold Harbour or Clockwork City and what ever will come.
+--- Returns SET_MAP_RESULT_FAILED, SET_MAP_RESULT_MAP_CHANGED depending on the result of the API calls.
+function lib:SetMapToRootMap(x, y)
+    local result = SET_MAP_RESULT_FAILED
+    for rootMapIndex, measurements in pairs(rootMaps) do
+        if (not measurements) then
+            measurements = GetExtraMapMeasurement(rootMapIndex)
+            rootMaps[rootMapIndex] = measurements
+            result = SET_MAP_RESULT_MAP_CHANGED
+        end
+        if (measurements) then
+            if (x > measurements.offsetX and x < (measurements.offsetX + measurements.scaleX) and
+                y > measurements.offsetY and y < (measurements.offsetY + measurements.scaleY)) then
+                if (orgSetMapToMapListIndex(rootMapIndex) ~= SET_MAP_RESULT_FAILED) then
+                    return SET_MAP_RESULT_MAP_CHANGED
+                end
+            end
+        end
+    end
+    return result
+end
+
+--- Repeatedly calls ProcessMapClick on the given global position starting on the Tamriel map until nothing more would happen.
+--- Returns SET_MAP_RESULT_FAILED, SET_MAP_RESULT_MAP_CHANGED or SET_MAP_RESULT_CURRENT_MAP_UNCHANGED depending on the result of the API calls.
+function lib:MapZoomInMax(x, y)
+    local result = lib:SetMapToRootMap(x, y)
+
+    if (result ~= SET_MAP_RESULT_FAILED) then
+        local localX, localY = lib:GlobalToLocal(x, y)
+
+        while WouldProcessMapClick(localX, localY) do
+            result = orgProcessMapClick(localX, localY)
+            if (result == SET_MAP_RESULT_FAILED) then break end
+            localX, localY = lib:GlobalToLocal(x, y)
+        end
+    end
+
+    return result
+end
+
+--- Stores information about how we can back to this map on a stack.
+-- There is no panAndZoom:GetCurrentOffset(), yet
+local function CalculateContainerAnchorOffsets()
+    local containerCenterX, containerCenterY = ZO_WorldMapContainer:GetCenter()
+    local scrollCenterX, scrollCenterY = ZO_WorldMapScroll:GetCenter()
+    return containerCenterX - scrollCenterX, containerCenterY - scrollCenterY
+end
+function lib:PushCurrentMap()
+    local wasPlayerLocation, targetMapTileTexture, currentMapFloor, currentMapFloorCount, currentMapIndex, zoom, offsetX, offsetY
+    currentMapIndex = GetCurrentMapIndex()
+    wasPlayerLocation = DoesCurrentMapMatchMapForPlayerLocation()
+    targetMapTileTexture = GetMapTileTexture()
+    currentMapFloor, currentMapFloorCount = GetMapFloorInfo()
+    zoom = self.panAndZoom:GetCurrentZoom()
+    offsetX, offsetY = CalculateContainerAnchorOffsets()
+
+    mapStack[#mapStack + 1] = { wasPlayerLocation, targetMapTileTexture, currentMapFloor, currentMapFloorCount, currentMapIndex, zoom, offsetX, offsetY }
+end
+
+--- Switches to the map that was put on the stack last.
+--- Returns SET_MAP_RESULT_FAILED, SET_MAP_RESULT_MAP_CHANGED or SET_MAP_RESULT_CURRENT_MAP_UNCHANGED depending on the result of the API calls.
+function lib:PopCurrentMap()
+    local result = SET_MAP_RESULT_FAILED
+    local data = table.remove(mapStack, #mapStack)
+    if(not data) then
+        LogMessage(LOG_DEBUG, "PopCurrentMap failed. No data on map stack.")
+        return result
+    end
+
+    local wasPlayerLocation, targetMapTileTexture, currentMapFloor, currentMapFloorCount, currentMapIndex, zoom, offsetX, offsetY = unpack(data)
+    local currentTileTexture = GetMapTileTexture()
+    if(currentTileTexture ~= targetMapTileTexture) then
+        if(wasPlayerLocation) then
+            result = orgSetMapToPlayerLocation()
+
+        elseif(currentMapIndex ~= nil and currentMapIndex > 0) then -- set to a zone map
+            result = orgSetMapToMapListIndex(currentMapIndex)
+
+        else -- here is where it gets tricky
+            local target = mapMeasurements[targetMapTileTexture]
+            if(not target) then -- always just return to player map if we cannot restore the previous map.
+                LogMessage(LOG_DEBUG, string.format("No measurement for \"%s\". Returning to player location.", targetMapTileTexture))
+                return orgSetMapToPlayerLocation()
+            end
+
+            -- switch to the parent zone
+            if(target.mapIndex == TAMRIEL_MAP_INDEX) then -- zone map has no mapIndex (e.g. Eyevea or Hew's Bane on first PTS patch for update 9)
+                -- switch to the tamriel map just in case
+                result = orgSetMapToMapListIndex(TAMRIEL_MAP_INDEX)
+                if(result == SET_MAP_RESULT_FAILED) then return result end
+                -- get global coordinates of target map center
+                local x = target.offsetX + (target.scaleX / 2)
+                local y = target.offsetY + (target.scaleY / 2)
+                if(not WouldProcessMapClick(x, y)) then
+                    LogMessage(LOG_DEBUG, string.format("Cannot process click at %s/%s on map \"%s\" in order to get to \"%s\". Returning to player location instead.", tostring(x), tostring(y), GetMapTileTexture(), targetMapTileTexture))
+                    return orgSetMapToPlayerLocation()
+                end
+                result = orgProcessMapClick(x, y)
+                if(result == SET_MAP_RESULT_FAILED) then return result end
+            else
+                result = orgSetMapToMapListIndex(target.mapIndex)
+                if(result == SET_MAP_RESULT_FAILED) then return result end
+            end
+
+            -- switch to the sub zone
+            currentTileTexture = GetMapTileTexture()
+            if(currentTileTexture ~= targetMapTileTexture) then
+                -- determine where on the zone map we have to click to get to the sub zone map
+                -- get global coordinates of target map center
+                local x = target.offsetX + (target.scaleX / 2)
+                local y = target.offsetY + (target.scaleY / 2)
+                -- transform to local coordinates
+                local current = mapMeasurements[currentTileTexture]
+                if(not current) then
+                    LogMessage(LOG_DEBUG, string.format("No measurement for \"%s\". Returning to player location.", currentTileTexture))
+                    return orgSetMapToPlayerLocation()
+                end
+
+                x = (x - current.offsetX) / current.scaleX
+                y = (y - current.offsetY) / current.scaleY
+
+                if(not WouldProcessMapClick(x, y)) then
+                    LogMessage(LOG_DEBUG, string.format("Cannot process click at %s/%s on map \"%s\" in order to get to \"%s\". Returning to player location instead.", tostring(x), tostring(y), GetMapTileTexture(), targetMapTileTexture))
+                    return orgSetMapToPlayerLocation()
+                end
+                result = orgProcessMapClick(x, y)
+                if(result == SET_MAP_RESULT_FAILED) then return result end
+            end
+
+            -- switch to the correct floor (e.g. Elden Root)
+            if (currentMapFloorCount > 0) then
+                result = orgSetMapFloor(currentMapFloor)
+            end
+            if (result ~= SET_MAP_RESULT_FAILED) then
+                lib.panAndZoom:SetCurrentZoom(zoom)
+                lib.panAndZoom:SetCurrentOffset(offsetX, offsetY)
+            end
+        end
+    else
+        result = SET_MAP_RESULT_CURRENT_MAP_UNCHANGED
+    end
+
+    return result
+end
+
+Initialize()
diff --git a/TaosGroupTools/libs/LibMapPing/LICENSE b/TaosGroupTools/libs/LibMapPing/LICENSE
index 12b3fde..3a510d0 100644
--- a/TaosGroupTools/libs/LibMapPing/LICENSE
+++ b/TaosGroupTools/libs/LibMapPing/LICENSE
@@ -1,201 +1,201 @@
-               The Artistic License 2.0
-
-           Copyright (c) 2016 sirinsidiator
-
-     Everyone is permitted to copy and distribute verbatim copies
-      of this license document, but changing it is not allowed.
-
-Preamble
-
-This license establishes the terms under which a given free software
-Package may be copied, modified, distributed, and/or redistributed.
-The intent is that the Copyright Holder maintains some artistic
-control over the development of that Package while still keeping the
-Package available as open source and free software.
-
-You are always permitted to make arrangements wholly outside of this
-license directly with the Copyright Holder of a given Package.  If the
-terms of this license do not permit the full use that you propose to
-make of the Package, you should contact the Copyright Holder and seek
-a different licensing arrangement.
-
-Definitions
-
-    "Copyright Holder" means the individual(s) or organization(s)
-    named in the copyright notice for the entire Package.
-
-    "Contributor" means any party that has contributed code or other
-    material to the Package, in accordance with the Copyright Holder's
-    procedures.
-
-    "You" and "your" means any person who would like to copy,
-    distribute, or modify the Package.
-
-    "Package" means the collection of files distributed by the
-    Copyright Holder, and derivatives of that collection and/or of
-    those files. A given Package may consist of either the Standard
-    Version, or a Modified Version.
-
-    "Distribute" means providing a copy of the Package or making it
-    accessible to anyone else, or in the case of a company or
-    organization, to others outside of your company or organization.
-
-    "Distributor Fee" means any fee that you charge for Distributing
-    this Package or providing support for this Package to another
-    party.  It does not mean licensing fees.
-
-    "Standard Version" refers to the Package if it has not been
-    modified, or has been modified only in ways explicitly requested
-    by the Copyright Holder.
-
-    "Modified Version" means the Package, if it has been changed, and
-    such changes were not explicitly requested by the Copyright
-    Holder.
-
-    "Original License" means this Artistic License as Distributed with
-    the Standard Version of the Package, in its current version or as
-    it may be modified by The Perl Foundation in the future.
-
-    "Source" form means the source code, documentation source, and
-    configuration files for the Package.
-
-    "Compiled" form means the compiled bytecode, object code, binary,
-    or any other form resulting from mechanical transformation or
-    translation of the Source form.
-
-
-Permission for Use and Modification Without Distribution
-
-(1)  You are permitted to use the Standard Version and create and use
-Modified Versions for any purpose without restriction, provided that
-you do not Distribute the Modified Version.
-
-
-Permissions for Redistribution of the Standard Version
-
-(2)  You may Distribute verbatim copies of the Source form of the
-Standard Version of this Package in any medium without restriction,
-either gratis or for a Distributor Fee, provided that you duplicate
-all of the original copyright notices and associated disclaimers.  At
-your discretion, such verbatim copies may or may not include a
-Compiled form of the Package.
-
-(3)  You may apply any bug fixes, portability changes, and other
-modifications made available from the Copyright Holder.  The resulting
-Package will still be considered the Standard Version, and as such
-will be subject to the Original License.
-
-
-Distribution of Modified Versions of the Package as Source
-
-(4)  You may Distribute your Modified Version as Source (either gratis
-or for a Distributor Fee, and with or without a Compiled form of the
-Modified Version) provided that you clearly document how it differs
-from the Standard Version, including, but not limited to, documenting
-any non-standard features, executables, or modules, and provided that
-you do at least ONE of the following:
-
-    (a)  make the Modified Version available to the Copyright Holder
-    of the Standard Version, under the Original License, so that the
-    Copyright Holder may include your modifications in the Standard
-    Version.
-
-    (b)  ensure that installation of your Modified Version does not
-    prevent the user installing or running the Standard Version. In
-    addition, the Modified Version must bear a name that is different
-    from the name of the Standard Version.
-
-    (c)  allow anyone who receives a copy of the Modified Version to
-    make the Source form of the Modified Version available to others
-    under
-
-    (i)  the Original License or
-
-    (ii)  a license that permits the licensee to freely copy,
-    modify and redistribute the Modified Version using the same
-    licensing terms that apply to the copy that the licensee
-    received, and requires that the Source form of the Modified
-    Version, and of any works derived from it, be made freely
-    available in that license fees are prohibited but Distributor
-    Fees are allowed.
-
-
-Distribution of Compiled Forms of the Standard Version
-or Modified Versions without the Source
-
-(5)  You may Distribute Compiled forms of the Standard Version without
-the Source, provided that you include complete instructions on how to
-get the Source of the Standard Version.  Such instructions must be
-valid at the time of your distribution.  If these instructions, at any
-time while you are carrying out such distribution, become invalid, you
-must provide new instructions on demand or cease further distribution.
-If you provide valid instructions or cease distribution within thirty
-days after you become aware that the instructions are invalid, then
-you do not forfeit any of your rights under this license.
-
-(6)  You may Distribute a Modified Version in Compiled form without
-the Source, provided that you comply with Section 4 with respect to
-the Source of the Modified Version.
-
-
-Aggregating or Linking the Package
-
-(7)  You may aggregate the Package (either the Standard Version or
-Modified Version) with other packages and Distribute the resulting
-aggregation provided that you do not charge a licensing fee for the
-Package.  Distributor Fees are permitted, and licensing fees for other
-components in the aggregation are permitted. The terms of this license
-apply to the use and Distribution of the Standard or Modified Versions
-as included in the aggregation.
-
-(8) You are permitted to link Modified and Standard Versions with
-other works, to embed the Package in a larger work of your own, or to
-build stand-alone binary or bytecode versions of applications that
-include the Package, and Distribute the result without restriction,
-provided the result does not expose a direct interface to the Package.
-
-
-Items That are Not Considered Part of a Modified Version
-
-(9) Works (including, but not limited to, modules and scripts) that
-merely extend or make use of the Package, do not, by themselves, cause
-the Package to be a Modified Version.  In addition, such works are not
-considered parts of the Package itself, and are not subject to the
-terms of this license.
-
-
-General Provisions
-
-(10)  Any use, modification, and distribution of the Standard or
-Modified Versions is governed by this Artistic License. By using,
-modifying or distributing the Package, you accept this license. Do not
-use, modify, or distribute the Package, if you do not accept this
-license.
-
-(11)  If your Modified Version has been derived from a Modified
-Version made by someone other than you, you are nevertheless required
-to ensure that your Modified Version complies with the requirements of
-this license.
-
-(12)  This license does not grant you the right to use any trademark,
-service mark, tradename, or logo of the Copyright Holder.
-
-(13)  This license includes the non-exclusive, worldwide,
-free-of-charge patent license to make, have made, use, offer to sell,
-sell, import and otherwise transfer the Package with respect to any
-patent claims licensable by the Copyright Holder that are necessarily
-infringed by the Package. If you institute patent litigation
-(including a cross-claim or counterclaim) against any party alleging
-that the Package constitutes direct or contributory patent
-infringement, then this Artistic License to you shall terminate on the
-date that such litigation is filed.
-
-(14)  Disclaimer of Warranty:
-THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS
-IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. THE IMPLIED
-WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
-NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY YOUR LOCAL
-LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR CONTRIBUTOR WILL
-BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
-DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE, EVEN IF
-ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+               The Artistic License 2.0
+
+           Copyright (c) 2016 sirinsidiator
+
+     Everyone is permitted to copy and distribute verbatim copies
+      of this license document, but changing it is not allowed.
+
+Preamble
+
+This license establishes the terms under which a given free software
+Package may be copied, modified, distributed, and/or redistributed.
+The intent is that the Copyright Holder maintains some artistic
+control over the development of that Package while still keeping the
+Package available as open source and free software.
+
+You are always permitted to make arrangements wholly outside of this
+license directly with the Copyright Holder of a given Package.  If the
+terms of this license do not permit the full use that you propose to
+make of the Package, you should contact the Copyright Holder and seek
+a different licensing arrangement.
+
+Definitions
+
+    "Copyright Holder" means the individual(s) or organization(s)
+    named in the copyright notice for the entire Package.
+
+    "Contributor" means any party that has contributed code or other
+    material to the Package, in accordance with the Copyright Holder's
+    procedures.
+
+    "You" and "your" means any person who would like to copy,
+    distribute, or modify the Package.
+
+    "Package" means the collection of files distributed by the
+    Copyright Holder, and derivatives of that collection and/or of
+    those files. A given Package may consist of either the Standard
+    Version, or a Modified Version.
+
+    "Distribute" means providing a copy of the Package or making it
+    accessible to anyone else, or in the case of a company or
+    organization, to others outside of your company or organization.
+
+    "Distributor Fee" means any fee that you charge for Distributing
+    this Package or providing support for this Package to another
+    party.  It does not mean licensing fees.
+
+    "Standard Version" refers to the Package if it has not been
+    modified, or has been modified only in ways explicitly requested
+    by the Copyright Holder.
+
+    "Modified Version" means the Package, if it has been changed, and
+    such changes were not explicitly requested by the Copyright
+    Holder.
+
+    "Original License" means this Artistic License as Distributed with
+    the Standard Version of the Package, in its current version or as
+    it may be modified by The Perl Foundation in the future.
+
+    "Source" form means the source code, documentation source, and
+    configuration files for the Package.
+
+    "Compiled" form means the compiled bytecode, object code, binary,
+    or any other form resulting from mechanical transformation or
+    translation of the Source form.
+
+
+Permission for Use and Modification Without Distribution
+
+(1)  You are permitted to use the Standard Version and create and use
+Modified Versions for any purpose without restriction, provided that
+you do not Distribute the Modified Version.
+
+
+Permissions for Redistribution of the Standard Version
+
+(2)  You may Distribute verbatim copies of the Source form of the
+Standard Version of this Package in any medium without restriction,
+either gratis or for a Distributor Fee, provided that you duplicate
+all of the original copyright notices and associated disclaimers.  At
+your discretion, such verbatim copies may or may not include a
+Compiled form of the Package.
+
+(3)  You may apply any bug fixes, portability changes, and other
+modifications made available from the Copyright Holder.  The resulting
+Package will still be considered the Standard Version, and as such
+will be subject to the Original License.
+
+
+Distribution of Modified Versions of the Package as Source
+
+(4)  You may Distribute your Modified Version as Source (either gratis
+or for a Distributor Fee, and with or without a Compiled form of the
+Modified Version) provided that you clearly document how it differs
+from the Standard Version, including, but not limited to, documenting
+any non-standard features, executables, or modules, and provided that
+you do at least ONE of the following:
+
+    (a)  make the Modified Version available to the Copyright Holder
+    of the Standard Version, under the Original License, so that the
+    Copyright Holder may include your modifications in the Standard
+    Version.
+
+    (b)  ensure that installation of your Modified Version does not
+    prevent the user installing or running the Standard Version. In
+    addition, the Modified Version must bear a name that is different
+    from the name of the Standard Version.
+
+    (c)  allow anyone who receives a copy of the Modified Version to
+    make the Source form of the Modified Version available to others
+    under
+
+    (i)  the Original License or
+
+    (ii)  a license that permits the licensee to freely copy,
+    modify and redistribute the Modified Version using the same
+    licensing terms that apply to the copy that the licensee
+    received, and requires that the Source form of the Modified
+    Version, and of any works derived from it, be made freely
+    available in that license fees are prohibited but Distributor
+    Fees are allowed.
+
+
+Distribution of Compiled Forms of the Standard Version
+or Modified Versions without the Source
+
+(5)  You may Distribute Compiled forms of the Standard Version without
+the Source, provided that you include complete instructions on how to
+get the Source of the Standard Version.  Such instructions must be
+valid at the time of your distribution.  If these instructions, at any
+time while you are carrying out such distribution, become invalid, you
+must provide new instructions on demand or cease further distribution.
+If you provide valid instructions or cease distribution within thirty
+days after you become aware that the instructions are invalid, then
+you do not forfeit any of your rights under this license.
+
+(6)  You may Distribute a Modified Version in Compiled form without
+the Source, provided that you comply with Section 4 with respect to
+the Source of the Modified Version.
+
+
+Aggregating or Linking the Package
+
+(7)  You may aggregate the Package (either the Standard Version or
+Modified Version) with other packages and Distribute the resulting
+aggregation provided that you do not charge a licensing fee for the
+Package.  Distributor Fees are permitted, and licensing fees for other
+components in the aggregation are permitted. The terms of this license
+apply to the use and Distribution of the Standard or Modified Versions
+as included in the aggregation.
+
+(8) You are permitted to link Modified and Standard Versions with
+other works, to embed the Package in a larger work of your own, or to
+build stand-alone binary or bytecode versions of applications that
+include the Package, and Distribute the result without restriction,
+provided the result does not expose a direct interface to the Package.
+
+
+Items That are Not Considered Part of a Modified Version
+
+(9) Works (including, but not limited to, modules and scripts) that
+merely extend or make use of the Package, do not, by themselves, cause
+the Package to be a Modified Version.  In addition, such works are not
+considered parts of the Package itself, and are not subject to the
+terms of this license.
+
+
+General Provisions
+
+(10)  Any use, modification, and distribution of the Standard or
+Modified Versions is governed by this Artistic License. By using,
+modifying or distributing the Package, you accept this license. Do not
+use, modify, or distribute the Package, if you do not accept this
+license.
+
+(11)  If your Modified Version has been derived from a Modified
+Version made by someone other than you, you are nevertheless required
+to ensure that your Modified Version complies with the requirements of
+this license.
+
+(12)  This license does not grant you the right to use any trademark,
+service mark, tradename, or logo of the Copyright Holder.
+
+(13)  This license includes the non-exclusive, worldwide,
+free-of-charge patent license to make, have made, use, offer to sell,
+sell, import and otherwise transfer the Package with respect to any
+patent claims licensable by the Copyright Holder that are necessarily
+infringed by the Package. If you institute patent litigation
+(including a cross-claim or counterclaim) against any party alleging
+that the Package constitutes direct or contributory patent
+infringement, then this Artistic License to you shall terminate on the
+date that such litigation is filed.
+
+(14)  Disclaimer of Warranty:
+THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS
+IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. THE IMPLIED
+WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
+NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY YOUR LOCAL
+LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR CONTRIBUTOR WILL
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/TaosGroupTools/libs/LibMapPing/LibMapPing.lua b/TaosGroupTools/libs/LibMapPing/LibMapPing.lua
index 14a55e1..b74fa2c 100644
--- a/TaosGroupTools/libs/LibMapPing/LibMapPing.lua
+++ b/TaosGroupTools/libs/LibMapPing/LibMapPing.lua
@@ -1,344 +1,344 @@
-local LIB_IDENTIFIER = "LibMapPing"
-local lib = LibStub:NewLibrary(LIB_IDENTIFIER, 6)
-
-if not lib then
-    return -- already loaded and no upgrade necessary
-end
-
-local function Log(message, ...)
-    df("[%s] %s", LIB_IDENTIFIER, message:format(...))
-end
-
-local MAP_PIN_TYPE_PLAYER_WAYPOINT = MAP_PIN_TYPE_PLAYER_WAYPOINT
-local MAP_PIN_TYPE_PING = MAP_PIN_TYPE_PING
-local MAP_PIN_TYPE_RALLY_POINT = MAP_PIN_TYPE_RALLY_POINT
-
-local MAP_PIN_TAG_PLAYER_WAYPOINT = "waypoint"
-local MAP_PIN_TAG_RALLY_POINT = "rally"
-local PING_CATEGORY = "pings"
-
-local PING_EVENT_WATCHDOG_TIME = 400 -- ms
-
-local MAP_PIN_TAG = {
-    [MAP_PIN_TYPE_PLAYER_WAYPOINT] = MAP_PIN_TAG_PLAYER_WAYPOINT,
-    --[MAP_PIN_TYPE_PING] = group pings have individual tags for each member
-    [MAP_PIN_TYPE_RALLY_POINT] = MAP_PIN_TAG_RALLY_POINT,
-}
-
-local originalPingMap, originalRemovePlayerWaypoint, originalRemoveRallyPoint
-local GET_MAP_PING_FUNCTION = {} -- is initialized in Load()
-local REMOVE_MAP_PING_FUNCTION = {} -- also initialized in Load()
-
---- MapPingState is an enumeration of the possible states of a map ping.
-lib.MAP_PING_NOT_SET = 0 --- There is no ping.
-lib.MAP_PING_NOT_SET_PENDING = 1 --- The ping has been removed, but EVENT_MAP_PING has not been processed.
-lib.MAP_PING_SET_PENDING = 2 --- A ping was added, but EVENT_MAP_PING has not been processed.
-lib.MAP_PING_SET = 3 --- There is a ping.
-
-lib.mutePing = lib.mutePing or {}
-lib.suppressPing = lib.suppressPing or {}
-lib.pingState = lib.pingState or {}
-lib.pendingPing = lib.pendingPing or {}
-lib.cm = lib.cm or ZO_CallbackObject:New()
-local g_mapPinManager = lib.mapPinManager
-
-local function GetPingTagFromType(pingType)
-    return MAP_PIN_TAG[pingType] or GetGroupUnitTagByIndex(GetGroupIndexByUnitTag("player")) or ""
-end
-
-local function GetKey(pingType, pingTag)
-    pingTag = pingTag or GetPingTagFromType(pingType)
-    return string.format("%d_%s", pingType, pingTag)
-end
-
--- TODO keep an eye on worldmap.lua for changes
-local function HandleMapPing(eventCode, pingEventType, pingType, pingTag, x, y, isPingOwner)
-    local key = GetKey(pingType, pingTag)
-    local data = lib.pendingPing[key]
-    if data and data[1] == pingEventType then
-        lib.pendingPing[key] = nil
-    end
-    if(pingEventType == PING_EVENT_ADDED) then
-        lib.cm:FireCallbacks("BeforePingAdded", pingType, pingTag, x, y, isPingOwner)
-        lib.pingState[key] = lib.MAP_PING_SET
-        g_mapPinManager:RemovePins(PING_CATEGORY, pingType, pingTag)
-        if(not lib:IsPingSuppressed(pingType, pingTag)) then
-            g_mapPinManager:CreatePin(pingType, pingTag, x, y)
-            if(isPingOwner and not lib:IsPingMuted(pingType, pingTag)) then
-                PlaySound(SOUNDS.MAP_PING)
-            end
-        end
-        lib.cm:FireCallbacks("AfterPingAdded", pingType, pingTag, x, y, isPingOwner)
-    elseif(pingEventType == PING_EVENT_REMOVED) then
-        lib.cm:FireCallbacks("BeforePingRemoved", pingType, pingTag, x, y, isPingOwner)
-        lib.pingState[key] = lib.MAP_PING_NOT_SET
-        g_mapPinManager:RemovePins(PING_CATEGORY, pingType, pingTag)
-        if (isPingOwner and not lib:IsPingSuppressed(pingType, pingTag) and not lib:IsPingMuted(pingType, pingTag)) then
-            PlaySound(SOUNDS.MAP_PING_REMOVE)
-        end
-        lib.cm:FireCallbacks("AfterPingRemoved", pingType, pingTag, x, y, isPingOwner)
-    end
-end
-
-local function HandleMapPingEventNotFired()
-    EVENT_MANAGER:UnregisterForUpdate(LIB_IDENTIFIER)
-    for key, data in pairs(lib.pendingPing) do
-        local pingEventType, pingType, x, y, zoneIndex = unpack(data)
-        local pingTag = GetPingTagFromType(pingType)
-        -- The event is delayed and thus may not match the current map anymore.
-        if GetCurrentMapZoneIndex() ~= zoneIndex then
-            -- The coords do not match the current map. Do not draw a pin.
-            lib:SuppressPing(pingType, pingTag) -- Will be set to zero afterwards, see below.
-        end
-        HandleMapPing(0, pingEventType, pingType, pingTag, x, y, true)
-        lib.pendingPing[key] = nil
-        lib.mutePing[key] = 0
-        lib.suppressPing[key] = 0
-    end
-end
-
-local function ResetEventWatchdog(key, ...)
-    lib.pendingPing[key] = {...}
-    EVENT_MANAGER:UnregisterForUpdate(LIB_IDENTIFIER)
-    EVENT_MANAGER:RegisterForUpdate(LIB_IDENTIFIER, PING_EVENT_WATCHDOG_TIME, HandleMapPingEventNotFired)
-end
-
-local function CustomPingMap(pingType, mapType, x, y)
-    if(pingType == MAP_PIN_TYPE_PING and not IsUnitGrouped("player")) then return end
-    local key = GetKey(pingType)
-    lib.pingState[key] = lib.MAP_PING_SET_PENDING
-    ResetEventWatchdog(key, PING_EVENT_ADDED, pingType, x, y, GetCurrentMapZoneIndex())
-    return originalPingMap(pingType, mapType, x, y)
-end
-
-local function CustomGetMapPlayerWaypoint()
-    if(lib:IsPingSuppressed(MAP_PIN_TYPE_PLAYER_WAYPOINT, MAP_PIN_TAG_PLAYER_WAYPOINT)) then
-        return 0, 0
-    end
-    return GET_MAP_PING_FUNCTION[MAP_PIN_TYPE_PLAYER_WAYPOINT]()
-end
-
-local function CustomGetMapPing(pingTag)
-    if(lib:IsPingSuppressed(MAP_PIN_TYPE_PING, pingTag)) then
-        return 0, 0
-    end
-    return GET_MAP_PING_FUNCTION[MAP_PIN_TYPE_PING](pingTag)
-end
-
-local function CustomGetMapRallyPoint()
-    if(lib:IsPingSuppressed(MAP_PIN_TYPE_RALLY_POINT, MAP_PIN_TAG_RALLY_POINT)) then
-        return 0, 0
-    end
-    return GET_MAP_PING_FUNCTION[MAP_PIN_TYPE_RALLY_POINT]()
-end
-
-local function CustomRemovePlayerWaypoint()
-    local key = GetKey(MAP_PIN_TYPE_PLAYER_WAYPOINT, MAP_PIN_TAG_PLAYER_WAYPOINT)
-    lib.pingState[key] = lib.MAP_PING_NOT_SET_PENDING
-    ResetEventWatchdog(key, PING_EVENT_REMOVED, MAP_PIN_TYPE_PLAYER_WAYPOINT, 0, 0, GetCurrentMapZoneIndex())
-    return originalRemovePlayerWaypoint()
-end
-
-local function CustomRemoveMapPing()
-    -- there is no such function for group pings, but we can set it to 0, 0 which effectively hides it
-    PingMap(MAP_PIN_TYPE_PING, MAP_TYPE_LOCATION_CENTERED, 0, 0)
-end
-
-local function CustomRemoveRallyPoint()
-    local key = GetKey(MAP_PIN_TYPE_RALLY_POINT, MAP_PIN_TAG_RALLY_POINT)
-    lib.pingState[key] = lib.MAP_PING_NOT_SET_PENDING
-    ResetEventWatchdog(key, PING_EVENT_REMOVED, MAP_PIN_TYPE_RALLY_POINT, 0, 0)
-    originalRemoveRallyPoint()
-end
-
---- Wrapper for PingMap.
---- pingType is one of the three possible MapDisplayPinType for map pings (MAP_PIN_TYPE_PLAYER_WAYPOINT, MAP_PIN_TYPE_PING or MAP_PIN_TYPE_RALLY_POINT).
---- mapType is usually MAP_TYPE_LOCATION_CENTERED.
---- x and y are the normalized coordinates on the current map.
-function lib:SetMapPing(pingType, mapType, x, y)
-    PingMap(pingType, mapType, x, y)
-end
-
---- Wrapper for the different ping removal functions.
---- For waypoints and rally points it calls their respective removal function
---- For group pings it just sets the position to 0, 0 as there is no function to clear them
-function lib:RemoveMapPing(pingType)
-    if(REMOVE_MAP_PING_FUNCTION[pingType]) then
-        REMOVE_MAP_PING_FUNCTION[pingType]()
-    end
-end
-
---- Wrapper for the different get ping functions. Returns coordinates regardless of their suppression state.
---- The game API functions return 0, 0 when the ping type is suppressed.
---- pingType is the same as for SetMapPing.
---- pingTag is optionally used if another group member's MAP_PIN_TYPE_PING should be returned (possible values: group1 .. group24).
-function lib:GetMapPing(pingType, pingTag)
-    local x, y = 0, 0
-    if(GET_MAP_PING_FUNCTION[pingType]) then
-        x, y = GET_MAP_PING_FUNCTION[pingType](pingTag or GetPingTagFromType(pingType))
-    end
-    return x, y
-end
-
---- Returns the MapPingState for the pingType and pingTag.
-function lib:GetMapPingState(pingType, pingTag)
-    local key = GetKey(pingType, pingTag)
-    local state = lib.pingState[key]
-    if state == nil then
-        local x, y = lib:GetMapPing(pingType, pingTag)
-        state = (x ~= 0 or y ~= 0) and lib.MAP_PING_SET or lib.MAP_PING_NOT_SET
-        lib.pingState[key] = state
-    end
-    return lib.pingState[key]
-end
---- Returns true if ping state is MAP_PING_SET_PENDING or MAP_PING_SET
-function lib:HasMapPing(pingType, pingTag)
-    local state = lib:GetMapPingState(pingType, pingTag)
-    return state == lib.MAP_PING_SET_PENDING or state == lib.MAP_PING_SET
-end
-
---- Refreshes the pin icon for the pingType on the worldmap
---- Returns true if the pin has been refreshed.
-function lib:RefreshMapPin(pingType, pingTag)
-    if(not g_mapPinManager) then
-        Log("PinManager not available. Using ZO_WorldMap_UpdateMap instead.")
-        ZO_WorldMap_UpdateMap()
-        return true
-    end
-
-    pingTag = pingTag or GetPingTagFromType(pingType)
-    g_mapPinManager:RemovePins(PING_CATEGORY, pingType, pingTag)
-
-    local x, y = lib:GetMapPing(pingType, pingTag)
-    if(lib:IsPositionOnMap(x, y)) then
-        g_mapPinManager:CreatePin(pingType, pingTag, x, y)
-        return true
-    end
-    return false
-end
-
---- Returns true if the normalized position is within 0 and 1.
-function lib:IsPositionOnMap(x, y)
-    return not (x < 0 or y < 0 or x > 1 or y > 1 or (x == 0 and y == 0))
-end
-
---- Mutes the map ping of the specified type, so it does not make a sound when it is set.
---- Do not forget to call UnmutePing later, otherwise the sound will be permanently muted!
-function lib:MutePing(pingType, pingTag)
-    local key = GetKey(pingType, pingTag)
-    local mute = lib.mutePing[key] or 0
-    lib.mutePing[key] = mute + 1
-end
-
---- Unmutes the map ping of the specified type.
---- Do not call this more often than you called MutePing, or you might interfere with other addons.
---- The sounds are played between the BeforePing* and AfterPing* callbacks are fired.
-function lib:UnmutePing(pingType, pingTag)
-    local key = GetKey(pingType, pingTag)
-    local mute = (lib.mutePing[key] or 0) - 1
-    if(mute < 0) then mute = 0 end
-    lib.mutePing[key] = mute
-end
-
---- Returns true if the map ping has been muted
-function lib:IsPingMuted(pingType, pingTag)
-    local key = GetKey(pingType, pingTag)
-    return lib.mutePing[key] and lib.mutePing[key] > 0
-end
-
---- Suppresses the map ping of the specified type, so that it neither makes a sound nor shows up on the map.
---- This also makes the API functions return 0, 0 for that ping.
---- In order to access the actual coordinates lib:GetMapPing has to be used.
---- Do not forget to call UnsuppressPing later, otherwise map pings won't work anymore for the user and other addons!
-function lib:SuppressPing(pingType, pingTag)
-    local key = GetKey(pingType, pingTag)
-    local suppress = lib.suppressPing[key] or 0
-    lib.suppressPing[key] = suppress + 1
-end
-
---- Unsuppresses the map ping so it shows up again
---- Do not call this more often than you called SuppressPing, or you might interfere with other addons.
-function lib:UnsuppressPing(pingType, pingTag)
-    local key = GetKey(pingType, pingTag)
-    local suppress = (lib.suppressPing[key] or 0) - 1
-    if(suppress < 0) then suppress = 0 end
-    lib.suppressPing[key] = suppress
-end
-
---- Returns true if the map ping has been suppressed
-function lib:IsPingSuppressed(pingType, pingTag)
-    local key = GetKey(pingType, pingTag)
-    return lib.suppressPing[key] and lib.suppressPing[key] > 0
-end
-
-local function InterceptMapPinManager()
-    if (g_mapPinManager) then return end
-    local orgRefreshCustomPins = ZO_WorldMapPins.RefreshCustomPins
-    function ZO_WorldMapPins:RefreshCustomPins()
-        g_mapPinManager = self
-        lib.mapPinManager = self
-    end
-    ZO_WorldMap_RefreshCustomPinsOfType()
-    ZO_WorldMapPins.RefreshCustomPins = orgRefreshCustomPins
-end
-
---- Register to callbacks from the library.
---- Valid events are BeforePingAdded, AfterPingAdded, BeforePingRemoved and AfterPingRemoved.
---- These are fired at certain points during handling EVENT_MAP_PING.
-function lib:RegisterCallback(eventName, callback)
-    lib.cm:RegisterCallback(eventName, callback)
-end
-
---- Unregister from callbacks. See lib:RegisterCallback.
-function lib:UnregisterCallback(eventName, callback)
-    lib.cm:UnregisterCallback(eventName, callback)
-end
-
-local function Unload()
-    EVENT_MANAGER:UnregisterForEvent(LIB_IDENTIFIER, EVENT_ADD_ON_LOADED)
-    EVENT_MANAGER:UnregisterForEvent(LIB_IDENTIFIER, EVENT_MAP_PING)
-    PingMap = originalPingMap
-    GetMapPlayerWaypoint = GET_MAP_PING_FUNCTION[MAP_PIN_TYPE_PLAYER_WAYPOINT]
-    GetMapPing = GET_MAP_PING_FUNCTION[MAP_PIN_TYPE_PING]
-    GetMapRallyPoint = GET_MAP_PING_FUNCTION[MAP_PIN_TYPE_RALLY_POINT]
-    RemovePlayerWaypoint = originalRemovePlayerWaypoint
-    RemoveRallyPoint = originalRemoveRallyPoint
-end
-
-local function Load()
-    InterceptMapPinManager()
-
-    originalPingMap = PingMap
-    PingMap = CustomPingMap
-
-    GET_MAP_PING_FUNCTION[MAP_PIN_TYPE_PLAYER_WAYPOINT] = GetMapPlayerWaypoint
-    GET_MAP_PING_FUNCTION[MAP_PIN_TYPE_PING] = GetMapPing
-    GET_MAP_PING_FUNCTION[MAP_PIN_TYPE_RALLY_POINT] = GetMapRallyPoint
-    GetMapPlayerWaypoint = CustomGetMapPlayerWaypoint
-    GetMapPing = CustomGetMapPing
-    GetMapRallyPoint = CustomGetMapRallyPoint
-
-    -- we want to use the altered versions in the library in order to set the correct ping state
-    -- so we need to also save the originals
-    originalRemovePlayerWaypoint = RemovePlayerWaypoint
-    originalRemoveRallyPoint = RemoveRallyPoint
-    RemovePlayerWaypoint = CustomRemovePlayerWaypoint
-    RemoveRallyPoint = CustomRemoveRallyPoint
-    REMOVE_MAP_PING_FUNCTION[MAP_PIN_TYPE_PLAYER_WAYPOINT] = CustomRemovePlayerWaypoint
-    REMOVE_MAP_PING_FUNCTION[MAP_PIN_TYPE_PING] = CustomRemoveMapPing -- has no real api equivalent
-    REMOVE_MAP_PING_FUNCTION[MAP_PIN_TYPE_RALLY_POINT] = CustomRemoveRallyPoint
-
-    EVENT_MANAGER:RegisterForEvent(LIB_IDENTIFIER, EVENT_ADD_ON_LOADED, function(_, addonName)
-        if(addonName == "ZO_Ingame") then
-            EVENT_MANAGER:UnregisterForEvent(LIB_IDENTIFIER, EVENT_ADD_ON_LOADED)
-            -- don't let worldmap do anything as we manage it instead
-            EVENT_MANAGER:UnregisterForEvent("ZO_WorldMap", EVENT_MAP_PING)
-            EVENT_MANAGER:RegisterForEvent(LIB_IDENTIFIER, EVENT_MAP_PING, HandleMapPing)
-        end
-    end)
-
-    lib.Unload = Unload
-end
-
-if(lib.Unload) then lib.Unload() end
-Load()
+local LIB_IDENTIFIER = "LibMapPing"
+local lib = LibStub:NewLibrary(LIB_IDENTIFIER, 6)
+
+if not lib then
+    return -- already loaded and no upgrade necessary
+end
+
+local function Log(message, ...)
+    df("[%s] %s", LIB_IDENTIFIER, message:format(...))
+end
+
+local MAP_PIN_TYPE_PLAYER_WAYPOINT = MAP_PIN_TYPE_PLAYER_WAYPOINT
+local MAP_PIN_TYPE_PING = MAP_PIN_TYPE_PING
+local MAP_PIN_TYPE_RALLY_POINT = MAP_PIN_TYPE_RALLY_POINT
+
+local MAP_PIN_TAG_PLAYER_WAYPOINT = "waypoint"
+local MAP_PIN_TAG_RALLY_POINT = "rally"
+local PING_CATEGORY = "pings"
+
+local PING_EVENT_WATCHDOG_TIME = 400 -- ms
+
+local MAP_PIN_TAG = {
+    [MAP_PIN_TYPE_PLAYER_WAYPOINT] = MAP_PIN_TAG_PLAYER_WAYPOINT,
+    --[MAP_PIN_TYPE_PING] = group pings have individual tags for each member
+    [MAP_PIN_TYPE_RALLY_POINT] = MAP_PIN_TAG_RALLY_POINT,
+}
+
+local originalPingMap, originalRemovePlayerWaypoint, originalRemoveRallyPoint
+local GET_MAP_PING_FUNCTION = {} -- is initialized in Load()
+local REMOVE_MAP_PING_FUNCTION = {} -- also initialized in Load()
+
+--- MapPingState is an enumeration of the possible states of a map ping.
+lib.MAP_PING_NOT_SET = 0 --- There is no ping.
+lib.MAP_PING_NOT_SET_PENDING = 1 --- The ping has been removed, but EVENT_MAP_PING has not been processed.
+lib.MAP_PING_SET_PENDING = 2 --- A ping was added, but EVENT_MAP_PING has not been processed.
+lib.MAP_PING_SET = 3 --- There is a ping.
+
+lib.mutePing = lib.mutePing or {}
+lib.suppressPing = lib.suppressPing or {}
+lib.pingState = lib.pingState or {}
+lib.pendingPing = lib.pendingPing or {}
+lib.cm = lib.cm or ZO_CallbackObject:New()
+local g_mapPinManager = lib.mapPinManager
+
+local function GetPingTagFromType(pingType)
+    return MAP_PIN_TAG[pingType] or GetGroupUnitTagByIndex(GetGroupIndexByUnitTag("player")) or ""
+end
+
+local function GetKey(pingType, pingTag)
+    pingTag = pingTag or GetPingTagFromType(pingType)
+    return string.format("%d_%s", pingType, pingTag)
+end
+
+-- TODO keep an eye on worldmap.lua for changes
+local function HandleMapPing(eventCode, pingEventType, pingType, pingTag, x, y, isPingOwner)
+    local key = GetKey(pingType, pingTag)
+    local data = lib.pendingPing[key]
+    if data and data[1] == pingEventType then
+        lib.pendingPing[key] = nil
+    end
+    if(pingEventType == PING_EVENT_ADDED) then
+        lib.cm:FireCallbacks("BeforePingAdded", pingType, pingTag, x, y, isPingOwner)
+        lib.pingState[key] = lib.MAP_PING_SET
+        g_mapPinManager:RemovePins(PING_CATEGORY, pingType, pingTag)
+        if(not lib:IsPingSuppressed(pingType, pingTag)) then
+            g_mapPinManager:CreatePin(pingType, pingTag, x, y)
+            if(isPingOwner and not lib:IsPingMuted(pingType, pingTag)) then
+                PlaySound(SOUNDS.MAP_PING)
+            end
+        end
+        lib.cm:FireCallbacks("AfterPingAdded", pingType, pingTag, x, y, isPingOwner)
+    elseif(pingEventType == PING_EVENT_REMOVED) then
+        lib.cm:FireCallbacks("BeforePingRemoved", pingType, pingTag, x, y, isPingOwner)
+        lib.pingState[key] = lib.MAP_PING_NOT_SET
+        g_mapPinManager:RemovePins(PING_CATEGORY, pingType, pingTag)
+        if (isPingOwner and not lib:IsPingSuppressed(pingType, pingTag) and not lib:IsPingMuted(pingType, pingTag)) then
+            PlaySound(SOUNDS.MAP_PING_REMOVE)
+        end
+        lib.cm:FireCallbacks("AfterPingRemoved", pingType, pingTag, x, y, isPingOwner)
+    end
+end
+
+local function HandleMapPingEventNotFired()
+    EVENT_MANAGER:UnregisterForUpdate(LIB_IDENTIFIER)
+    for key, data in pairs(lib.pendingPing) do
+        local pingEventType, pingType, x, y, zoneIndex = unpack(data)
+        local pingTag = GetPingTagFromType(pingType)
+        -- The event is delayed and thus may not match the current map anymore.
+        if GetCurrentMapZoneIndex() ~= zoneIndex then
+            -- The coords do not match the current map. Do not draw a pin.
+            lib:SuppressPing(pingType, pingTag) -- Will be set to zero afterwards, see below.
+        end
+        HandleMapPing(0, pingEventType, pingType, pingTag, x, y, true)
+        lib.pendingPing[key] = nil
+        lib.mutePing[key] = 0
+        lib.suppressPing[key] = 0
+    end
+end
+
+local function ResetEventWatchdog(key, ...)
+    lib.pendingPing[key] = {...}
+    EVENT_MANAGER:UnregisterForUpdate(LIB_IDENTIFIER)
+    EVENT_MANAGER:RegisterForUpdate(LIB_IDENTIFIER, PING_EVENT_WATCHDOG_TIME, HandleMapPingEventNotFired)
+end
+
+local function CustomPingMap(pingType, mapType, x, y)
+    if(pingType == MAP_PIN_TYPE_PING and not IsUnitGrouped("player")) then return end
+    local key = GetKey(pingType)
+    lib.pingState[key] = lib.MAP_PING_SET_PENDING
+    ResetEventWatchdog(key, PING_EVENT_ADDED, pingType, x, y, GetCurrentMapZoneIndex())
+    return originalPingMap(pingType, mapType, x, y)
+end
+
+local function CustomGetMapPlayerWaypoint()
+    if(lib:IsPingSuppressed(MAP_PIN_TYPE_PLAYER_WAYPOINT, MAP_PIN_TAG_PLAYER_WAYPOINT)) then
+        return 0, 0
+    end
+    return GET_MAP_PING_FUNCTION[MAP_PIN_TYPE_PLAYER_WAYPOINT]()
+end
+
+local function CustomGetMapPing(pingTag)
+    if(lib:IsPingSuppressed(MAP_PIN_TYPE_PING, pingTag)) then
+        return 0, 0
+    end
+    return GET_MAP_PING_FUNCTION[MAP_PIN_TYPE_PING](pingTag)
+end
+
+local function CustomGetMapRallyPoint()
+    if(lib:IsPingSuppressed(MAP_PIN_TYPE_RALLY_POINT, MAP_PIN_TAG_RALLY_POINT)) then
+        return 0, 0
+    end
+    return GET_MAP_PING_FUNCTION[MAP_PIN_TYPE_RALLY_POINT]()
+end
+
+local function CustomRemovePlayerWaypoint()
+    local key = GetKey(MAP_PIN_TYPE_PLAYER_WAYPOINT, MAP_PIN_TAG_PLAYER_WAYPOINT)
+    lib.pingState[key] = lib.MAP_PING_NOT_SET_PENDING
+    ResetEventWatchdog(key, PING_EVENT_REMOVED, MAP_PIN_TYPE_PLAYER_WAYPOINT, 0, 0, GetCurrentMapZoneIndex())
+    return originalRemovePlayerWaypoint()
+end
+
+local function CustomRemoveMapPing()
+    -- there is no such function for group pings, but we can set it to 0, 0 which effectively hides it
+    PingMap(MAP_PIN_TYPE_PING, MAP_TYPE_LOCATION_CENTERED, 0, 0)
+end
+
+local function CustomRemoveRallyPoint()
+    local key = GetKey(MAP_PIN_TYPE_RALLY_POINT, MAP_PIN_TAG_RALLY_POINT)
+    lib.pingState[key] = lib.MAP_PING_NOT_SET_PENDING
+    ResetEventWatchdog(key, PING_EVENT_REMOVED, MAP_PIN_TYPE_RALLY_POINT, 0, 0)
+    originalRemoveRallyPoint()
+end
+
+--- Wrapper for PingMap.
+--- pingType is one of the three possible MapDisplayPinType for map pings (MAP_PIN_TYPE_PLAYER_WAYPOINT, MAP_PIN_TYPE_PING or MAP_PIN_TYPE_RALLY_POINT).
+--- mapType is usually MAP_TYPE_LOCATION_CENTERED.
+--- x and y are the normalized coordinates on the current map.
+function lib:SetMapPing(pingType, mapType, x, y)
+    PingMap(pingType, mapType, x, y)
+end
+
+--- Wrapper for the different ping removal functions.
+--- For waypoints and rally points it calls their respective removal function
+--- For group pings it just sets the position to 0, 0 as there is no function to clear them
+function lib:RemoveMapPing(pingType)
+    if(REMOVE_MAP_PING_FUNCTION[pingType]) then
+        REMOVE_MAP_PING_FUNCTION[pingType]()
+    end
+end
+
+--- Wrapper for the different get ping functions. Returns coordinates regardless of their suppression state.
+--- The game API functions return 0, 0 when the ping type is suppressed.
+--- pingType is the same as for SetMapPing.
+--- pingTag is optionally used if another group member's MAP_PIN_TYPE_PING should be returned (possible values: group1 .. group24).
+function lib:GetMapPing(pingType, pingTag)
+    local x, y = 0, 0
+    if(GET_MAP_PING_FUNCTION[pingType]) then
+        x, y = GET_MAP_PING_FUNCTION[pingType](pingTag or GetPingTagFromType(pingType))
+    end
+    return x, y
+end
+
+--- Returns the MapPingState for the pingType and pingTag.
+function lib:GetMapPingState(pingType, pingTag)
+    local key = GetKey(pingType, pingTag)
+    local state = lib.pingState[key]
+    if state == nil then
+        local x, y = lib:GetMapPing(pingType, pingTag)
+        state = (x ~= 0 or y ~= 0) and lib.MAP_PING_SET or lib.MAP_PING_NOT_SET
+        lib.pingState[key] = state
+    end
+    return lib.pingState[key]
+end
+--- Returns true if ping state is MAP_PING_SET_PENDING or MAP_PING_SET
+function lib:HasMapPing(pingType, pingTag)
+    local state = lib:GetMapPingState(pingType, pingTag)
+    return state == lib.MAP_PING_SET_PENDING or state == lib.MAP_PING_SET
+end
+
+--- Refreshes the pin icon for the pingType on the worldmap
+--- Returns true if the pin has been refreshed.
+function lib:RefreshMapPin(pingType, pingTag)
+    if(not g_mapPinManager) then
+        Log("PinManager not available. Using ZO_WorldMap_UpdateMap instead.")
+        ZO_WorldMap_UpdateMap()
+        return true
+    end
+
+    pingTag = pingTag or GetPingTagFromType(pingType)
+    g_mapPinManager:RemovePins(PING_CATEGORY, pingType, pingTag)
+
+    local x, y = lib:GetMapPing(pingType, pingTag)
+    if(lib:IsPositionOnMap(x, y)) then
+        g_mapPinManager:CreatePin(pingType, pingTag, x, y)
+        return true
+    end
+    return false
+end
+
+--- Returns true if the normalized position is within 0 and 1.
+function lib:IsPositionOnMap(x, y)
+    return not (x < 0 or y < 0 or x > 1 or y > 1 or (x == 0 and y == 0))
+end
+
+--- Mutes the map ping of the specified type, so it does not make a sound when it is set.
+--- Do not forget to call UnmutePing later, otherwise the sound will be permanently muted!
+function lib:MutePing(pingType, pingTag)
+    local key = GetKey(pingType, pingTag)
+    local mute = lib.mutePing[key] or 0
+    lib.mutePing[key] = mute + 1
+end
+
+--- Unmutes the map ping of the specified type.
+--- Do not call this more often than you called MutePing, or you might interfere with other addons.
+--- The sounds are played between the BeforePing* and AfterPing* callbacks are fired.
+function lib:UnmutePing(pingType, pingTag)
+    local key = GetKey(pingType, pingTag)
+    local mute = (lib.mutePing[key] or 0) - 1
+    if(mute < 0) then mute = 0 end
+    lib.mutePing[key] = mute
+end
+
+--- Returns true if the map ping has been muted
+function lib:IsPingMuted(pingType, pingTag)
+    local key = GetKey(pingType, pingTag)
+    return lib.mutePing[key] and lib.mutePing[key] > 0
+end
+
+--- Suppresses the map ping of the specified type, so that it neither makes a sound nor shows up on the map.
+--- This also makes the API functions return 0, 0 for that ping.
+--- In order to access the actual coordinates lib:GetMapPing has to be used.
+--- Do not forget to call UnsuppressPing later, otherwise map pings won't work anymore for the user and other addons!
+function lib:SuppressPing(pingType, pingTag)
+    local key = GetKey(pingType, pingTag)
+    local suppress = lib.suppressPing[key] or 0
+    lib.suppressPing[key] = suppress + 1
+end
+
+--- Unsuppresses the map ping so it shows up again
+--- Do not call this more often than you called SuppressPing, or you might interfere with other addons.
+function lib:UnsuppressPing(pingType, pingTag)
+    local key = GetKey(pingType, pingTag)
+    local suppress = (lib.suppressPing[key] or 0) - 1
+    if(suppress < 0) then suppress = 0 end
+    lib.suppressPing[key] = suppress
+end
+
+--- Returns true if the map ping has been suppressed
+function lib:IsPingSuppressed(pingType, pingTag)
+    local key = GetKey(pingType, pingTag)
+    return lib.suppressPing[key] and lib.suppressPing[key] > 0
+end
+
+local function InterceptMapPinManager()
+    if (g_mapPinManager) then return end
+    local orgRefreshCustomPins = ZO_WorldMapPins.RefreshCustomPins
+    function ZO_WorldMapPins:RefreshCustomPins()
+        g_mapPinManager = self
+        lib.mapPinManager = self
+    end
+    ZO_WorldMap_RefreshCustomPinsOfType()
+    ZO_WorldMapPins.RefreshCustomPins = orgRefreshCustomPins
+end
+
+--- Register to callbacks from the library.
+--- Valid events are BeforePingAdded, AfterPingAdded, BeforePingRemoved and AfterPingRemoved.
+--- These are fired at certain points during handling EVENT_MAP_PING.
+function lib:RegisterCallback(eventName, callback)
+    lib.cm:RegisterCallback(eventName, callback)
+end
+
+--- Unregister from callbacks. See lib:RegisterCallback.
+function lib:UnregisterCallback(eventName, callback)
+    lib.cm:UnregisterCallback(eventName, callback)
+end
+
+local function Unload()
+    EVENT_MANAGER:UnregisterForEvent(LIB_IDENTIFIER, EVENT_ADD_ON_LOADED)
+    EVENT_MANAGER:UnregisterForEvent(LIB_IDENTIFIER, EVENT_MAP_PING)
+    PingMap = originalPingMap
+    GetMapPlayerWaypoint = GET_MAP_PING_FUNCTION[MAP_PIN_TYPE_PLAYER_WAYPOINT]
+    GetMapPing = GET_MAP_PING_FUNCTION[MAP_PIN_TYPE_PING]
+    GetMapRallyPoint = GET_MAP_PING_FUNCTION[MAP_PIN_TYPE_RALLY_POINT]
+    RemovePlayerWaypoint = originalRemovePlayerWaypoint
+    RemoveRallyPoint = originalRemoveRallyPoint
+end
+
+local function Load()
+    InterceptMapPinManager()
+
+    originalPingMap = PingMap
+    PingMap = CustomPingMap
+
+    GET_MAP_PING_FUNCTION[MAP_PIN_TYPE_PLAYER_WAYPOINT] = GetMapPlayerWaypoint
+    GET_MAP_PING_FUNCTION[MAP_PIN_TYPE_PING] = GetMapPing
+    GET_MAP_PING_FUNCTION[MAP_PIN_TYPE_RALLY_POINT] = GetMapRallyPoint
+    GetMapPlayerWaypoint = CustomGetMapPlayerWaypoint
+    GetMapPing = CustomGetMapPing
+    GetMapRallyPoint = CustomGetMapRallyPoint
+
+    -- we want to use the altered versions in the library in order to set the correct ping state
+    -- so we need to also save the originals
+    originalRemovePlayerWaypoint = RemovePlayerWaypoint
+    originalRemoveRallyPoint = RemoveRallyPoint
+    RemovePlayerWaypoint = CustomRemovePlayerWaypoint
+    RemoveRallyPoint = CustomRemoveRallyPoint
+    REMOVE_MAP_PING_FUNCTION[MAP_PIN_TYPE_PLAYER_WAYPOINT] = CustomRemovePlayerWaypoint
+    REMOVE_MAP_PING_FUNCTION[MAP_PIN_TYPE_PING] = CustomRemoveMapPing -- has no real api equivalent
+    REMOVE_MAP_PING_FUNCTION[MAP_PIN_TYPE_RALLY_POINT] = CustomRemoveRallyPoint
+
+    EVENT_MANAGER:RegisterForEvent(LIB_IDENTIFIER, EVENT_ADD_ON_LOADED, function(_, addonName)
+        if(addonName == "ZO_Ingame") then
+            EVENT_MANAGER:UnregisterForEvent(LIB_IDENTIFIER, EVENT_ADD_ON_LOADED)
+            -- don't let worldmap do anything as we manage it instead
+            EVENT_MANAGER:UnregisterForEvent("ZO_WorldMap", EVENT_MAP_PING)
+            EVENT_MANAGER:RegisterForEvent(LIB_IDENTIFIER, EVENT_MAP_PING, HandleMapPing)
+        end
+    end)
+
+    lib.Unload = Unload
+end
+
+if(lib.Unload) then lib.Unload() end
+Load()
diff --git a/TaosGroupTools/libs/LibStub/LibStub.lua b/TaosGroupTools/libs/LibStub/LibStub.lua
index 0e6bf67..4c35014 100644
--- a/TaosGroupTools/libs/LibStub/LibStub.lua
+++ b/TaosGroupTools/libs/LibStub/LibStub.lua
@@ -1,38 +1,38 @@
--- 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", 4
-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)")
-		if type(minor) ~= "number" then
-			minor = assert(tonumber(zo_strmatch(minor, "%d+%.?%d*")), "Minor version must either be a number or contain a number.")
-		end
-
-		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(strformat("Cannot find a library instance of %q.", 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
-
+-- 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", 4
+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)")
+		if type(minor) ~= "number" then
+			minor = assert(tonumber(zo_strmatch(minor, "%d+%.?%d*")), "Minor version must either be a number or contain a number.")
+		end
+
+		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(strformat("Cannot find a library instance of %q.", 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
+
 LibStub.SILENT = true
\ No newline at end of file
diff --git a/TaosGroupTools/logic/CommandsHandler.lua b/TaosGroupTools/logic/CommandsHandler.lua
index 5a9e67a..9f38004 100644
--- a/TaosGroupTools/logic/CommandsHandler.lua
+++ b/TaosGroupTools/logic/CommandsHandler.lua
@@ -1,132 +1,132 @@
---[[
-	Addon: Taos Group Ultimate
-	Author: TProg Taonnor
-	Created by @Taonnor
-]]--
-
---[[
-	Local variables
-]]--
-local LOG_ACTIVE = false
-local _logger = nil
-
---[[
-	Table TGU_CommandsHandler
-]]--
-TGU_CommandsHandler = {}
-TGU_CommandsHandler.__index = TGU_CommandsHandler
-
---[[
-	Table Members
-]]--
-TGU_CommandsHandler.Name = "TGU-CommandsHandler"
-
---[[
-	Called on /setgroupultimatestyle command
-]]--
-function TGU_CommandsHandler.SetGroupUltimateStyleCommand(style)
-	if (LOG_ACTIVE) then
-        _logger:logTrace("TGU_CommandsHandler.SetGroupUltimateStyleCommand")
-        _logger:logDebug("style", style)
-    end
-
-    if (style ~= nil and style ~= "") then
-        TGU_SettingsHandler.SetStyleSettings(style)
-    else
-        d("Invalid style: " .. tostring(style))
-    end
-end
-
---[[
-	Called on /setultimateid command
-]]--
-function TGU_CommandsHandler.SetUltimateIdCommand(groupName)
-	if (LOG_ACTIVE) then
-        _logger:logTrace("TGU_CommandsHandler.SetUltimateId")
-        _logger:logDebug("groupName", groupName)
-    end
-
-    if (groupName ~= nil and groupName ~= "") then
-        local ultimateGroup = TGU_UltimateGroupHandler.GetUltimateGroupByGroupName(groupName)
-
-        if (ultimateGroup ~= nil) then
-            TGU_SettingsHandler.SetStaticUltimateIDSettings(ultimateGroup.GroupAbilityId)
-        else
-            d("Invalid group name: " .. tostring(groupName))
-        end
-    else
-        d("Invalid group name: " .. tostring(groupName))
-    end
-end
-
---[[
-	Called on /setswimlaneid command
-]]--
-function TGU_CommandsHandler.SetSwimlaneIdCommand(option)
-	if (LOG_ACTIVE) then
-        _logger:logTrace("TGU_CommandsHandler.SetSwimlaneId")
-        _logger:logDebug("option", option)
-    end
-
-    -- Parse options
-    local options = {}
-    local arrayLength = 0
-    local searchResult = { string.match(option,"^(%S*)%s*(.-)$") }
-    for i, v in pairs(searchResult) do
-        if (v ~= nil and v ~= "") then
-            options[i] = string.lower(v)
-            arrayLength = i
-        end
-    end
-
-    if (arrayLength == 2) then
-        local swimlane = tonumber(options[1])
-        local swimlaneGroup = options[2]
-        local ultimateGroup = TGU_UltimateGroupHandler.GetUltimateGroupByGroupName(swimlaneGroup)
-
-        if (swimlane ~= nil and ultimateGroup ~= nil and swimlane >= 1 and swimlane <= 6) then
-            TGU_SettingsHandler.SetSwimlaneUltimateGroupIdSettings(swimlane, ultimateGroup)
-        else
-            d("Invalid options: " .. tostring(option))
-        end
-    else
-        d("Invalid options: " .. tostring(option))
-    end
-end
-
---[[
-	Called on /getultimategroups command
-]]--
-function TGU_CommandsHandler.GetUltimateGroupsCommand()
-    if (LOG_ACTIVE) then _logger:logTrace("TGU_CommandsHandler.GetUltimateGroupsCommand") end
-
-    local ultimateGroups = TGU_UltimateGroupHandler.GetUltimateGroups()
-
-    d("Ultimate Groups:")
-
-    for i, group in pairs(ultimateGroups) do
-        d(group.GroupName .. " - " .. group.GroupDescription)
-    end
-end
-
---[[
-	Initialize initializes TGU_CommandsHandler
-]]--
-function TGU_CommandsHandler.Initialize(logger)
-    if (LOG_ACTIVE) then
-        logger:logTrace("TGU_CommandsHandler.Initialize")
-        logger:logDebug("Commands active:")
-        logger:logDebug("/setgroupultimatestyle <STYLEID> - Sets the style (0 = SimpleList, 1 = SwimlaneList).")
-        logger:logDebug("/setultimateid <GROUPNAME> - Sets the static ultimate group; See /getultimategroups to get group names.")
-        logger:logDebug("/setswimlaneid <SWIMLANE> <GROUPNAME> - Sets the ultimate group of swimlane (1-6); See /getultimategroups to get group name.")
-        logger:logDebug("/getultimategroups - Gets all ultimate group names")
-    end
-
-    _logger = logger
-
-    -- Define commands
-    SLASH_COMMANDS["/setgroupultimatestyle"] = TGU_CommandsHandler.SetGroupUltimateStyleCommand
-    SLASH_COMMANDS["/setultimateid"] = TGU_CommandsHandler.SetUltimateIdCommand
-    SLASH_COMMANDS["/setswimlaneid"] = TGU_CommandsHandler.SetSwimlaneIdCommand
-    SLASH_COMMANDS["/getultimategroups"] = TGU_CommandsHandler.GetUltimateGroupsCommand
+--[[
+	Addon: Taos Group Ultimate
+	Author: TProg Taonnor
+	Created by @Taonnor
+]]--
+
+--[[
+	Local variables
+]]--
+local LOG_ACTIVE = false
+local _logger = nil
+
+--[[
+	Table TGU_CommandsHandler
+]]--
+TGU_CommandsHandler = {}
+TGU_CommandsHandler.__index = TGU_CommandsHandler
+
+--[[
+	Table Members
+]]--
+TGU_CommandsHandler.Name = "TGU-CommandsHandler"
+
+--[[
+	Called on /setgroupultimatestyle command
+]]--
+function TGU_CommandsHandler.SetGroupUltimateStyleCommand(style)
+	if (LOG_ACTIVE) then
+        _logger:logTrace("TGU_CommandsHandler.SetGroupUltimateStyleCommand")
+        _logger:logDebug("style", style)
+    end
+
+    if (style ~= nil and style ~= "") then
+        TGU_SettingsHandler.SetStyleSettings(style)
+    else
+        d("Invalid style: " .. tostring(style))
+    end
+end
+
+--[[
+	Called on /setultimateid command
+]]--
+function TGU_CommandsHandler.SetUltimateIdCommand(groupName)
+	if (LOG_ACTIVE) then
+        _logger:logTrace("TGU_CommandsHandler.SetUltimateId")
+        _logger:logDebug("groupName", groupName)
+    end
+
+    if (groupName ~= nil and groupName ~= "") then
+        local ultimateGroup = TGU_UltimateGroupHandler.GetUltimateGroupByGroupName(groupName)
+
+        if (ultimateGroup ~= nil) then
+            TGU_SettingsHandler.SetStaticUltimateIDSettings(ultimateGroup.GroupAbilityId)
+        else
+            d("Invalid group name: " .. tostring(groupName))
+        end
+    else
+        d("Invalid group name: " .. tostring(groupName))
+    end
+end
+
+--[[
+	Called on /setswimlaneid command
+]]--
+function TGU_CommandsHandler.SetSwimlaneIdCommand(option)
+	if (LOG_ACTIVE) then
+        _logger:logTrace("TGU_CommandsHandler.SetSwimlaneId")
+        _logger:logDebug("option", option)
+    end
+
+    -- Parse options
+    local options = {}
+    local arrayLength = 0
+    local searchResult = { string.match(option,"^(%S*)%s*(.-)$") }
+    for i, v in pairs(searchResult) do
+        if (v ~= nil and v ~= "") then
+            options[i] = string.lower(v)
+            arrayLength = i
+        end
+    end
+
+    if (arrayLength == 2) then
+        local swimlane = tonumber(options[1])
+        local swimlaneGroup = options[2]
+        local ultimateGroup = TGU_UltimateGroupHandler.GetUltimateGroupByGroupName(swimlaneGroup)
+
+        if (swimlane ~= nil and ultimateGroup ~= nil and swimlane >= 1 and swimlane <= 6) then
+            TGU_SettingsHandler.SetSwimlaneUltimateGroupIdSettings(swimlane, ultimateGroup)
+        else
+            d("Invalid options: " .. tostring(option))
+        end
+    else
+        d("Invalid options: " .. tostring(option))
+    end
+end
+
+--[[
+	Called on /getultimategroups command
+]]--
+function TGU_CommandsHandler.GetUltimateGroupsCommand()
+    if (LOG_ACTIVE) then _logger:logTrace("TGU_CommandsHandler.GetUltimateGroupsCommand") end
+
+    local ultimateGroups = TGU_UltimateGroupHandler.GetUltimateGroups()
+
+    d("Ultimate Groups:")
+
+    for i, group in pairs(ultimateGroups) do
+        d(group.GroupName .. " - " .. group.GroupDescription)
+    end
+end
+
+--[[
+	Initialize initializes TGU_CommandsHandler
+]]--
+function TGU_CommandsHandler.Initialize(logger)
+    if (LOG_ACTIVE) then
+        logger:logTrace("TGU_CommandsHandler.Initialize")
+        logger:logDebug("Commands active:")
+        logger:logDebug("/setgroupultimatestyle <STYLEID> - Sets the style (0 = SimpleList, 1 = SwimlaneList).")
+        logger:logDebug("/setultimateid <GROUPNAME> - Sets the static ultimate group; See /getultimategroups to get group names.")
+        logger:logDebug("/setswimlaneid <SWIMLANE> <GROUPNAME> - Sets the ultimate group of swimlane (1-6); See /getultimategroups to get group name.")
+        logger:logDebug("/getultimategroups - Gets all ultimate group names")
+    end
+
+    _logger = logger
+
+    -- Define commands
+    SLASH_COMMANDS["/setgroupultimatestyle"] = TGU_CommandsHandler.SetGroupUltimateStyleCommand
+    SLASH_COMMANDS["/setultimateid"] = TGU_CommandsHandler.SetUltimateIdCommand
+    SLASH_COMMANDS["/setswimlaneid"] = TGU_CommandsHandler.SetSwimlaneIdCommand
+    SLASH_COMMANDS["/getultimategroups"] = TGU_CommandsHandler.GetUltimateGroupsCommand
 end
\ No newline at end of file
diff --git a/TaosGroupTools/logic/GroupHandler.lua b/TaosGroupTools/logic/GroupHandler.lua
index 43a17c0..14092b9 100644
--- a/TaosGroupTools/logic/GroupHandler.lua
+++ b/TaosGroupTools/logic/GroupHandler.lua
@@ -1,95 +1,95 @@
---[[
-	Addon: Taos Group Ultimate
-	Author: TProg Taonnor
-	Created by @Taonnor
-]]--
-
---[[
-	Local variables
-]]--
-local LOG_ACTIVE = false
-local _logger = nil
-
---[[
-	Table TGU_GroupHandler
-]]--
-TGU_GroupHandler = {}
-TGU_GroupHandler.__index = TGU_GroupHandler
-
---[[
-	Table Members
-]]--
-TGU_GroupHandler.Name = "TGU-GroupHandler"
-TGU_GroupHandler.IsMocked = false
-TGU_GroupHandler.IsGrouped = false
-
---[[
-	Called when group member joined group
-]]--
-function TGU_GroupHandler.OnGroupMemberJoined()
-	if (LOG_ACTIVE) then _logger:logTrace("TGU_GroupHandler.OnGroupMemberJoined") end
-
-    CALLBACK_MANAGER:FireCallbacks(TGU_GROUP_CHANGED)
-
-    TGU_GroupHandler.OnGroupUpdate()
-end
-
---[[
-	Called when group member left group
-]]--
-function TGU_GroupHandler.OnGroupMemberLeft()
-	if (LOG_ACTIVE) then _logger:logTrace("TGU_GroupHandler.OnGroupMemberLeft") end
-
-    CALLBACK_MANAGER:FireCallbacks(TGU_GROUP_CHANGED)
-
-    TGU_GroupHandler.OnGroupUpdate()
-end
-
---[[
-	Called when groupUnitTags updated
-]]--
-function TGU_GroupHandler.OnGroupUpdate()
-	if (LOG_ACTIVE) then _logger:logTrace("TGU_GroupHandler.OnGroupUpdate") end
-
-    local isGrouped = IsUnitGrouped("player")
-
-    if (TGU_GroupHandler.IsMocked) then
-        isGrouped = true
-    end
-
-    if (isGrouped ~= TGU_GroupHandler.IsGrouped) then
-        TGU_GroupHandler.IsGrouped = isGrouped
-        CALLBACK_MANAGER:FireCallbacks(TGU_UNIT_GROUPED_CHANGED)
-    end
-end
-
---[[
-	Called on ???
-]]--
-function TGU_GroupHandler.OnUnitFrameUpdate()
-	if (LOG_ACTIVE) then _logger:logTrace("TGU_GroupHandler.OnUnitFrameUpdate") end
-
-    CALLBACK_MANAGER:FireCallbacks(TGU_GROUP_CHANGED)
-
-    TGU_GroupHandler.OnGroupUpdate()
-end
-
---[[
-	Initialize initializes TGU_GroupHandler
-]]--
-function TGU_GroupHandler.Initialize(logger, isMocked)
-    if (LOG_ACTIVE) then logger:logTrace("TGU_GroupHandler.Initialize") end
-
-    _logger = logger
-
-    TGU_GroupHandler.IsMocked = isMocked
-
-    -- Initial call
-	TGU_GroupHandler:OnGroupUpdate()
-
-	-- Register events
-	EVENT_MANAGER:RegisterForEvent(TGU_GroupHandler.Name, EVENT_GROUP_MEMBER_JOINED, TGU_GroupHandler.OnGroupMemberJoined)
-	EVENT_MANAGER:RegisterForEvent(TGU_GroupHandler.Name, EVENT_GROUP_MEMBER_LEFT, TGU_GroupHandler.OnGroupMemberLeft)
-	EVENT_MANAGER:RegisterForEvent(TGU_GroupHandler.Name, EVENT_GROUP_UPDATE, TGU_GroupHandler.OnGroupUpdate)
-	EVENT_MANAGER:RegisterForEvent(TGU_GroupHandler.Name, EVENT_UNIT_FRAME_UPDATE, TGU_GroupHandler.OnUnitFrameUpdate)
+--[[
+	Addon: Taos Group Ultimate
+	Author: TProg Taonnor
+	Created by @Taonnor
+]]--
+
+--[[
+	Local variables
+]]--
+local LOG_ACTIVE = false
+local _logger = nil
+
+--[[
+	Table TGU_GroupHandler
+]]--
+TGU_GroupHandler = {}
+TGU_GroupHandler.__index = TGU_GroupHandler
+
+--[[
+	Table Members
+]]--
+TGU_GroupHandler.Name = "TGU-GroupHandler"
+TGU_GroupHandler.IsMocked = false
+TGU_GroupHandler.IsGrouped = false
+
+--[[
+	Called when group member joined group
+]]--
+function TGU_GroupHandler.OnGroupMemberJoined()
+	if (LOG_ACTIVE) then _logger:logTrace("TGU_GroupHandler.OnGroupMemberJoined") end
+
+    CALLBACK_MANAGER:FireCallbacks(TGU_GROUP_CHANGED)
+
+    TGU_GroupHandler.OnGroupUpdate()
+end
+
+--[[
+	Called when group member left group
+]]--
+function TGU_GroupHandler.OnGroupMemberLeft()
+	if (LOG_ACTIVE) then _logger:logTrace("TGU_GroupHandler.OnGroupMemberLeft") end
+
+    CALLBACK_MANAGER:FireCallbacks(TGU_GROUP_CHANGED)
+
+    TGU_GroupHandler.OnGroupUpdate()
+end
+
+--[[
+	Called when groupUnitTags updated
+]]--
+function TGU_GroupHandler.OnGroupUpdate()
+	if (LOG_ACTIVE) then _logger:logTrace("TGU_GroupHandler.OnGroupUpdate") end
+
+    local isGrouped = IsUnitGrouped("player")
+
+    if (TGU_GroupHandler.IsMocked) then
+        isGrouped = true
+    end
+
+    if (isGrouped ~= TGU_GroupHandler.IsGrouped) then
+        TGU_GroupHandler.IsGrouped = isGrouped
+        CALLBACK_MANAGER:FireCallbacks(TGU_UNIT_GROUPED_CHANGED)
+    end
+end
+
+--[[
+	Called on ???
+]]--
+function TGU_GroupHandler.OnUnitFrameUpdate()
+	if (LOG_ACTIVE) then _logger:logTrace("TGU_GroupHandler.OnUnitFrameUpdate") end
+
+    CALLBACK_MANAGER:FireCallbacks(TGU_GROUP_CHANGED)
+
+    TGU_GroupHandler.OnGroupUpdate()
+end
+
+--[[
+	Initialize initializes TGU_GroupHandler
+]]--
+function TGU_GroupHandler.Initialize(logger, isMocked)
+    if (LOG_ACTIVE) then logger:logTrace("TGU_GroupHandler.Initialize") end
+
+    _logger = logger
+
+    TGU_GroupHandler.IsMocked = isMocked
+
+    -- Initial call
+	TGU_GroupHandler:OnGroupUpdate()
+
+	-- Register events
+	EVENT_MANAGER:RegisterForEvent(TGU_GroupHandler.Name, EVENT_GROUP_MEMBER_JOINED, TGU_GroupHandler.OnGroupMemberJoined)
+	EVENT_MANAGER:RegisterForEvent(TGU_GroupHandler.Name, EVENT_GROUP_MEMBER_LEFT, TGU_GroupHandler.OnGroupMemberLeft)
+	EVENT_MANAGER:RegisterForEvent(TGU_GroupHandler.Name, EVENT_GROUP_UPDATE, TGU_GroupHandler.OnGroupUpdate)
+	EVENT_MANAGER:RegisterForEvent(TGU_GroupHandler.Name, EVENT_UNIT_FRAME_UPDATE, TGU_GroupHandler.OnUnitFrameUpdate)
 end
\ No newline at end of file
diff --git a/TaosGroupTools/logic/MapPingHandler.lua b/TaosGroupTools/logic/MapPingHandler.lua
index c331226..a3fadc2 100644
--- a/TaosGroupTools/logic/MapPingHandler.lua
+++ b/TaosGroupTools/logic/MapPingHandler.lua
@@ -1,105 +1,105 @@
---[[
-	Addon: Taos Group Ultimate
-	Author: TProg Taonnor
-	Created by @Taonnor
-]]--
-
---[[
-	Local variables
-]]--
-local LOG_ACTIVE = false
-local _logger = nil
-
-local REFRESHRATE = 2000 -- ms; RegisterForUpdate is in miliseconds
-
---[[
-	Table TGU_MapPingHandler
-]]--
-TGU_MapPingHandler = {}
-TGU_MapPingHandler.__index = TGU_MapPingHandler
-
---[[
-	Table Members
-]]--
-TGU_MapPingHandler.Name = "TGU-MapPingHandler"
-TGU_MapPingHandler.IsMocked = false
-
---[[
-	Called on new data from Communication
-]]--
-function TGU_MapPingHandler.OnData(pingTag, abilityPing, relativeUltimate)
-    if (LOG_ACTIVE) then _logger:logTrace("TGU_MapPingHandler.OnData") end
-
-    local ultimateGroup = TGU_UltimateGroupHandler.GetUltimateGroupByAbilityPing(abilityPing)
-
-    if (ultimateGroup ~= nil and relativeUltimate ~= -1) then
-        local player = {}
-        local playerName = ""
-        local isPlayerDead = false
-
-        if (TGU_MapPingHandler.IsMocked == false) then
-            playerName = GetUnitName(pingTag)
-            isPlayerDead = IsUnitDead(pingTag)
-        else
-            playerName = pingTag
-            isPlayerDead = math.random() > 0.8
-        end
-
-        player.PingTag = pingTag
-        player.PlayerName = playerName
-        player.IsPlayerDead = isPlayerDead
-        player.UltimateGroup = ultimateGroup
-        player.UltimateName = GetAbilityName(ultimateGroup.GroupAbilityId)
-        player.UltimateIcon = GetAbilityIcon(ultimateGroup.GroupAbilityId)
-        player.RelativeUltimate = relativeUltimate
-
-        if (LOG_ACTIVE) then
-            _logger:logDebug("player.PingTag", player.PingTag)
-            _logger:logDebug("player.PlayerName", player.PlayerName)
-            _logger:logDebug("player.IsPlayerDead", player.IsPlayerDead)
-            _logger:logDebug("player.UltimateGroup.GroupName", player.UltimateGroup.GroupName)
-            _logger:logDebug("player.RelativeUltimate", player.RelativeUltimate)
-        end
-
-        CALLBACK_MANAGER:FireCallbacks(TGU_PLAYER_DATA_CHANGED, player)
-    else
-        _logger:logError("TGU_MapPingHandler.OnMapPing, Ping invalid ultimateGroup: " .. tostring(ultimateGroup) .. "; relativeUltimate: " .. tostring(relativeUltimate))
-    end
-end
-
---[[
-	Called on refresh of timer
-]]--
-function TGU_MapPingHandler.OnTimedUpdate(eventCode)
-    if (LOG_ACTIVE) then _logger:logTrace("TGU_MapPingHandler.OnTimedUpdate") end
-
-	if (IsUnitGrouped("player") == false and TGU_MapPingHandler.IsMocked == false) then return end -- only if player is in group and system is not mocked
-
-    local abilityGroup = TGU_UltimateGroupHandler.GetUltimateGroupByAbilityId(TGU_SettingsHandler.SavedVariables.StaticUltimateID)
-
-    if (abilityGroup ~= nil) then
-	    TGU_Communicator.SendData(abilityGroup)
-    else
-        _logger:logError("TGU_MapPingHandler.OnTimedUpdate, abilityGroup is nil, change ultimate. StaticID: " .. tostring(TGU_SettingsHandler.SavedVariables.StaticUltimateID))
-    end
-end
-
---[[
-	Initialize initializes TGU_MapPingHandler
-]]--
-function TGU_MapPingHandler.Initialize(logger, isMocked)
-    if (LOG_ACTIVE) then
-        logger:logTrace("TGU_MapPingHandler.Initialize")
-        logger:logDebug("isMocked", isMocked)
-    end
-
-    _logger = logger
-
-    TGU_MapPingHandler.IsMocked = isMocked
-
-    -- Register callbacks
-    CALLBACK_MANAGER:RegisterCallback(TGU_MAP_PING_CHANGED, TGU_MapPingHandler.OnData)
-
-	-- Start timer
-	EVENT_MANAGER:RegisterForUpdate(TGU_MapPingHandler.Name, REFRESHRATE, TGU_MapPingHandler.OnTimedUpdate)
+--[[
+	Addon: Taos Group Ultimate
+	Author: TProg Taonnor
+	Created by @Taonnor
+]]--
+
+--[[
+	Local variables
+]]--
+local LOG_ACTIVE = false
+local _logger = nil
+
+local REFRESHRATE = 2000 -- ms; RegisterForUpdate is in miliseconds
+
+--[[
+	Table TGU_MapPingHandler
+]]--
+TGU_MapPingHandler = {}
+TGU_MapPingHandler.__index = TGU_MapPingHandler
+
+--[[
+	Table Members
+]]--
+TGU_MapPingHandler.Name = "TGU-MapPingHandler"
+TGU_MapPingHandler.IsMocked = false
+
+--[[
+	Called on new data from Communication
+]]--
+function TGU_MapPingHandler.OnData(pingTag, abilityPing, relativeUltimate)
+    if (LOG_ACTIVE) then _logger:logTrace("TGU_MapPingHandler.OnData") end
+
+    local ultimateGroup = TGU_UltimateGroupHandler.GetUltimateGroupByAbilityPing(abilityPing)
+
+    if (ultimateGroup ~= nil and relativeUltimate ~= -1) then
+        local player = {}
+        local playerName = ""
+        local isPlayerDead = false
+
+        if (TGU_MapPingHandler.IsMocked == false) then
+            playerName = GetUnitName(pingTag)
+            isPlayerDead = IsUnitDead(pingTag)
+        else
+            playerName = pingTag
+            isPlayerDead = math.random() > 0.8
+        end
+
+        player.PingTag = pingTag
+        player.PlayerName = playerName
+        player.IsPlayerDead = isPlayerDead
+        player.UltimateGroup = ultimateGroup
+        player.UltimateName = GetAbilityName(ultimateGroup.GroupAbilityId)
+        player.UltimateIcon = GetAbilityIcon(ultimateGroup.GroupAbilityId)
+        player.RelativeUltimate = relativeUltimate
+
+        if (LOG_ACTIVE) then
+            _logger:logDebug("player.PingTag", player.PingTag)
+            _logger:logDebug("player.PlayerName", player.PlayerName)
+            _logger:logDebug("player.IsPlayerDead", player.IsPlayerDead)
+            _logger:logDebug("player.UltimateGroup.GroupName", player.UltimateGroup.GroupName)
+            _logger:logDebug("player.RelativeUltimate", player.RelativeUltimate)
+        end
+
+        CALLBACK_MANAGER:FireCallbacks(TGU_PLAYER_DATA_CHANGED, player)
+    else
+        _logger:logError("TGU_MapPingHandler.OnMapPing, Ping invalid ultimateGroup: " .. tostring(ultimateGroup) .. "; relativeUltimate: " .. tostring(relativeUltimate))
+    end
+end
+
+--[[
+	Called on refresh of timer
+]]--
+function TGU_MapPingHandler.OnTimedUpdate(eventCode)
+    if (LOG_ACTIVE) then _logger:logTrace("TGU_MapPingHandler.OnTimedUpdate") end
+
+	if (IsUnitGrouped("player") == false and TGU_MapPingHandler.IsMocked == false) then return end -- only if player is in group and system is not mocked
+
+    local abilityGroup = TGU_UltimateGroupHandler.GetUltimateGroupByAbilityId(TGU_SettingsHandler.SavedVariables.StaticUltimateID)
+
+    if (abilityGroup ~= nil) then
+	    TGU_Communicator.SendData(abilityGroup)
+    else
+        _logger:logError("TGU_MapPingHandler.OnTimedUpdate, abilityGroup is nil, change ultimate. StaticID: " .. tostring(TGU_SettingsHandler.SavedVariables.StaticUltimateID))
+    end
+end
+
+--[[
+	Initialize initializes TGU_MapPingHandler
+]]--
+function TGU_MapPingHandler.Initialize(logger, isMocked)
+    if (LOG_ACTIVE) then
+        logger:logTrace("TGU_MapPingHandler.Initialize")
+        logger:logDebug("isMocked", isMocked)
+    end
+
+    _logger = logger
+
+    TGU_MapPingHandler.IsMocked = isMocked
+
+    -- Register callbacks
+    CALLBACK_MANAGER:RegisterCallback(TGU_MAP_PING_CHANGED, TGU_MapPingHandler.OnData)
+
+	-- Start timer
+	EVENT_MANAGER:RegisterForUpdate(TGU_MapPingHandler.Name, REFRESHRATE, TGU_MapPingHandler.OnTimedUpdate)
 end
\ No newline at end of file
diff --git a/TaosGroupTools/logic/SettingsHandler.lua b/TaosGroupTools/logic/SettingsHandler.lua
index 26fe9b2..2b0aec3 100644
--- a/TaosGroupTools/logic/SettingsHandler.lua
+++ b/TaosGroupTools/logic/SettingsHandler.lua
@@ -1,222 +1,222 @@
---[[
-	Addon: Taos Group Ultimate
-	Author: TProg Taonnor
-	Created by @Taonnor
-]]--
-
---[[
-	Local variables
-]]--
-local LOG_ACTIVE = false
-local _logger = nil
-
-local SETTINGS_VERSION = 5
-
---[[
-	Table TGU_SettingsHandler
-]]--
-TGU_SettingsHandler = {}
-TGU_SettingsHandler.__index = TGU_SettingsHandler
-
---[[
-	Table Members
-]]--
-TGU_SettingsHandler.Name = "TGU-SettingsHandler"
-TGU_SettingsHandler.SettingsName = "TaosGroupUltimateSettings"
-TGU_SettingsHandler.SavedVariables = nil
-TGU_SettingsHandler.Default =
-{
-    ["PosX"] = 0,
-    ["PosY"] = 0,
-    ["SelectorPosX"] = 0,
-    ["SelectorPosY"] = 0,
-    ["OnlyAva"] = false,
-    ["IsSortingActive"] = false,
-    ["Movable"] = true,
-    ["Style"] = 3,
-    ["StaticUltimateID"] = 29861,
-    ["SwimlaneUltimateGroupIds"] =
-    {
-        [1] = 29861,
-        [2] = 27413,
-        [3] = 86536,
-        [4] = 86112,
-        [5] = 46537,
-        [6] = 46622,
-    },
-}
-
---[[
-	Sets SetStyleSettings and fires TGU-StyleChanged callbacks
-]]--
-function TGU_SettingsHandler.SetStyleSettings(style)
-    if (LOG_ACTIVE) then
-        _logger:logTrace("TGU_SettingsHandler.SetStyleSettings")
-        _logger:logDebug("style", style)
-    end
-
-    local numberStyle = tonumber(style)
-
-    if (numberStyle == 1 or numberStyle == 2 or numberStyle == 3) then
-        TGU_SettingsHandler.SavedVariables.Style = numberStyle
-
-        CALLBACK_MANAGER:FireCallbacks(TGU_STYLE_CHANGED)
-    else
-        _logger:logError("TGU_SettingsHandler.SetStyleSettings, invalid style " .. tostring(style))
-    end
-end
-
---[[
-	Sets MovableSettings and fires TGU-MovableChanged callbacks
-]]--
-function TGU_SettingsHandler.SetStaticUltimateIDSettings(staticUltimateID)
-    if (LOG_ACTIVE) then
-        _logger:logTrace("TGU_SettingsHandler.StaticUltimateIDSettings")
-        _logger:logDebug("staticUltimateID", staticUltimateID)
-    end
-
-    TGU_SettingsHandler.SavedVariables.StaticUltimateID = staticUltimateID
-
-    CALLBACK_MANAGER:FireCallbacks(TGU_STATIC_ULTIMATE_ID_CHANGED, staticUltimateID)
-end
-
---[[
-	Sets MovableSettings and fires TGU-MovableChanged callbacks
-]]--
-function TGU_SettingsHandler.SetSwimlaneUltimateGroupIdSettings(swimlane, ultimateGroup)
-    if (LOG_ACTIVE) then
-        _logger:logTrace("TGU_SettingsHandler.StaticUltimateIDSettings")
-        _logger:logDebug("swimlane", swimlane)
-        _logger:logDebug("ultimateGroup", ultimateGroup)
-    end
-
-    TGU_SettingsHandler.SavedVariables.SwimlaneUltimateGroupIds[swimlane] = ultimateGroup.GroupAbilityId
-
-    CALLBACK_MANAGER:FireCallbacks(TGU_SWIMLANE_ULTIMATE_GROUP_ID_CHANGED, swimlane, ultimateGroup)
-end
-
---[[
-	Sets MovableSettings and fires TGU-MovableChanged callbacks
-]]--
-function TGU_SettingsHandler.SetMovableSettings(movable)
-    if (LOG_ACTIVE) then
-        _logger:logTrace("TGU_SettingsHandler.SetMovableSettings")
-        _logger:logDebug("movable", movable)
-    end
-
-    TGU_SettingsHandler.SavedVariables.Movable = movable
-
-    CALLBACK_MANAGER:FireCallbacks(TGU_MOVABLE_CHANGED, movable)
-end
-
---[[
-	Sets MovableSettings and fires TGU-MovableChanged callbacks
-]]--
-function TGU_SettingsHandler.SetOnlyAvaSettings(onlyAva)
-    if (LOG_ACTIVE) then
-        _logger:logTrace("TGU_SettingsHandler.SetOnlyAvaSettings")
-        _logger:logDebug("onlyAva", onlyAva)
-    end
-
-    TGU_SettingsHandler.SavedVariables.OnlyAva = onlyAva
-
-    CALLBACK_MANAGER:FireCallbacks(TGU_IS_ZONE_CHANGED)
-end
-
---[[
-	Sets IsSortingActive settings
-]]--
-function TGU_SettingsHandler.SetIsSortingActiveSettings(isSortingActive)
-    if (LOG_ACTIVE) then
-        _logger:logTrace("TGU_SettingsHandler.SetIsLgsActiveSettings")
-        _logger:logDebug("isSortingActive", isSortingActive)
-    end
-
-    TGU_SettingsHandler.SavedVariables.IsSortingActive = isSortingActive
-end
-
---[[
-	Gets SimpleList visible in connection with selected style
-]]--
-function TGU_SettingsHandler.IsSimpleListVisible()
-    if (LOG_ACTIVE) then _logger:logTrace("TGU_SettingsHandler.IsSimpleListVisible") end
-    if (TGU_SettingsHandler.SavedVariables ~= nil) then
-        if (LOG_ACTIVE) then _logger:logDebug("style", TGU_SettingsHandler.SavedVariables.Style) end
-        return tonumber(TGU_SettingsHandler.SavedVariables.Style) == 1 and TGU_SettingsHandler.IsControlsVisible()
-    else
-        _logger:logError("TGU_SettingsHandler.SavedVariables is nil")
-        return false
-    end
-end
-
---[[
-	Gets SwimlaneList visible in connection with selected style
-]]--
-function TGU_SettingsHandler.IsSwimlaneListVisible()
-    if (LOG_ACTIVE) then _logger:logTrace("TGU_SettingsHandler.IsSwimlaneListVisible") end
-    if (TGU_SettingsHandler.SavedVariables ~= nil) then
-        if (LOG_ACTIVE) then _logger:logDebug("style", TGU_SettingsHandler.SavedVariables.Style) end
-        return tonumber(TGU_SettingsHandler.SavedVariables.Style) == 2 and TGU_SettingsHandler.IsControlsVisible()
-    else
-        _logger:logError("TGU_SettingsHandler.SavedVariables is nil")
-        return false
-    end
-end
-
---[[
-	Gets CompactSwimlaneList visible in connection with selected style
-]]--
-function TGU_SettingsHandler.IsCompactSwimlaneListVisible()
-    if (LOG_ACTIVE) then _logger:logTrace("TGU_SettingsHandler.IsCompactSwimlaneListVisible") end
-    if (TGU_SettingsHandler.SavedVariables ~= nil) then
-        if (LOG_ACTIVE) then _logger:logDebug("style", TGU_SettingsHandler.SavedVariables.Style) end
-        return tonumber(TGU_SettingsHandler.SavedVariables.Style) == 3 and TGU_SettingsHandler.IsControlsVisible()
-    else
-        _logger:logError("TGU_SettingsHandler.SavedVariables is nil")
-        return false
-    end
-end
-
---[[
-	Gets CompactSwimlaneList visible in connection with selected style
-]]--
-function TGU_SettingsHandler.IsControlsVisible()
-    if (LOG_ACTIVE) then _logger:logTrace("TGU_SettingsHandler.IsControlsVisible") end
-    if (TGU_SettingsHandler.SavedVariables ~= nil) then
-        if (LOG_ACTIVE) then _logger:logDebug("onlyAvA", TGU_SettingsHandler.SavedVariables.OnlyAva) end
-        if (TGU_SettingsHandler.SavedVariables.OnlyAva) then
-            _logger:logDebug("isPlayerInAvAWorld", IsPlayerInAvAWorld())
-            return IsPlayerInAvAWorld()
-        else
-            return true
-        end
-    else
-        _logger:logError("TGU_SettingsHandler.SavedVariables is nil")
-        return false
-    end
-end
-
---[[
-	OnPlayerActivated sends IsZoneChanged event
-]]--
-function TGU_SettingsHandler.OnPlayerActivated(eventCode)
-    if (LOG_ACTIVE) then
-        _logger:logTrace("TGU_SettingsHandler.OnPlayerActivated")
-    end
-
-    CALLBACK_MANAGER:FireCallbacks(TGU_IS_ZONE_CHANGED)
-end
-
---[[
-	Initialize loads SavedVariables
-]]--
-function TGU_SettingsHandler.Initialize(logger)
-    if (LOG_ACTIVE) then logger:logTrace("TGU_SettingsHandler.Initialize") end
-
-    _logger = logger
-
-    TGU_SettingsHandler.SavedVariables = ZO_SavedVars:NewAccountWide(TGU_SettingsHandler.SettingsName, SETTINGS_VERSION, nil, TGU_SettingsHandler.Default)
-
-    -- Register
-    EVENT_MANAGER:RegisterForEvent(TGU_SettingsHandler.Name, EVENT_PLAYER_ACTIVATED, TGU_SettingsHandler.OnPlayerActivated)
+--[[
+	Addon: Taos Group Ultimate
+	Author: TProg Taonnor
+	Created by @Taonnor
+]]--
+
+--[[
+	Local variables
+]]--
+local LOG_ACTIVE = false
+local _logger = nil
+
+local SETTINGS_VERSION = 5
+
+--[[
+	Table TGU_SettingsHandler
+]]--
+TGU_SettingsHandler = {}
+TGU_SettingsHandler.__index = TGU_SettingsHandler
+
+--[[
+	Table Members
+]]--
+TGU_SettingsHandler.Name = "TGU-SettingsHandler"
+TGU_SettingsHandler.SettingsName = "TaosGroupUltimateSettings"
+TGU_SettingsHandler.SavedVariables = nil
+TGU_SettingsHandler.Default =
+{
+    ["PosX"] = 0,
+    ["PosY"] = 0,
+    ["SelectorPosX"] = 0,
+    ["SelectorPosY"] = 0,
+    ["OnlyAva"] = false,
+    ["IsSortingActive"] = false,
+    ["Movable"] = true,
+    ["Style"] = 3,
+    ["StaticUltimateID"] = 29861,
+    ["SwimlaneUltimateGroupIds"] =
+    {
+        [1] = 29861,
+        [2] = 27413,
+        [3] = 86536,
+        [4] = 86112,
+        [5] = 46537,
+        [6] = 46622,
+    },
+}
+
+--[[
+	Sets SetStyleSettings and fires TGU-StyleChanged callbacks
+]]--
+function TGU_SettingsHandler.SetStyleSettings(style)
+    if (LOG_ACTIVE) then
+        _logger:logTrace("TGU_SettingsHandler.SetStyleSettings")
+        _logger:logDebug("style", style)
+    end
+
+    local numberStyle = tonumber(style)
+
+    if (numberStyle == 1 or numberStyle == 2 or numberStyle == 3) then
+        TGU_SettingsHandler.SavedVariables.Style = numberStyle
+
+        CALLBACK_MANAGER:FireCallbacks(TGU_STYLE_CHANGED)
+    else
+        _logger:logError("TGU_SettingsHandler.SetStyleSettings, invalid style " .. tostring(style))
+    end
+end
+
+--[[
+	Sets MovableSettings and fires TGU-MovableChanged callbacks
+]]--
+function TGU_SettingsHandler.SetStaticUltimateIDSettings(staticUltimateID)
+    if (LOG_ACTIVE) then
+        _logger:logTrace("TGU_SettingsHandler.StaticUltimateIDSettings")
+        _logger:logDebug("staticUltimateID", staticUltimateID)
+    end
+
+    TGU_SettingsHandler.SavedVariables.StaticUltimateID = staticUltimateID
+
+    CALLBACK_MANAGER:FireCallbacks(TGU_STATIC_ULTIMATE_ID_CHANGED, staticUltimateID)
+end
+
+--[[
+	Sets MovableSettings and fires TGU-MovableChanged callbacks
+]]--
+function TGU_SettingsHandler.SetSwimlaneUltimateGroupIdSettings(swimlane, ultimateGroup)
+    if (LOG_ACTIVE) then
+        _logger:logTrace("TGU_SettingsHandler.StaticUltimateIDSettings")
+        _logger:logDebug("swimlane", swimlane)
+        _logger:logDebug("ultimateGroup", ultimateGroup)
+    end
+
+    TGU_SettingsHandler.SavedVariables.SwimlaneUltimateGroupIds[swimlane] = ultimateGroup.GroupAbilityId
+
+    CALLBACK_MANAGER:FireCallbacks(TGU_SWIMLANE_ULTIMATE_GROUP_ID_CHANGED, swimlane, ultimateGroup)
+end
+
+--[[
+	Sets MovableSettings and fires TGU-MovableChanged callbacks
+]]--
+function TGU_SettingsHandler.SetMovableSettings(movable)
+    if (LOG_ACTIVE) then
+        _logger:logTrace("TGU_SettingsHandler.SetMovableSettings")
+        _logger:logDebug("movable", movable)
+    end
+
+    TGU_SettingsHandler.SavedVariables.Movable = movable
+
+    CALLBACK_MANAGER:FireCallbacks(TGU_MOVABLE_CHANGED, movable)
+end
+
+--[[
+	Sets MovableSettings and fires TGU-MovableChanged callbacks
+]]--
+function TGU_SettingsHandler.SetOnlyAvaSettings(onlyAva)
+    if (LOG_ACTIVE) then
+        _logger:logTrace("TGU_SettingsHandler.SetOnlyAvaSettings")
+        _logger:logDebug("onlyAva", onlyAva)
+    end
+
+    TGU_SettingsHandler.SavedVariables.OnlyAva = onlyAva
+
+    CALLBACK_MANAGER:FireCallbacks(TGU_IS_ZONE_CHANGED)
+end
+
+--[[
+	Sets IsSortingActive settings
+]]--
+function TGU_SettingsHandler.SetIsSortingActiveSettings(isSortingActive)
+    if (LOG_ACTIVE) then
+        _logger:logTrace("TGU_SettingsHandler.SetIsLgsActiveSettings")
+        _logger:logDebug("isSortingActive", isSortingActive)
+    end
+
+    TGU_SettingsHandler.SavedVariables.IsSortingActive = isSortingActive
+end
+
+--[[
+	Gets SimpleList visible in connection with selected style
+]]--
+function TGU_SettingsHandler.IsSimpleListVisible()
+    if (LOG_ACTIVE) then _logger:logTrace("TGU_SettingsHandler.IsSimpleListVisible") end
+    if (TGU_SettingsHandler.SavedVariables ~= nil) then
+        if (LOG_ACTIVE) then _logger:logDebug("style", TGU_SettingsHandler.SavedVariables.Style) end
+        return tonumber(TGU_SettingsHandler.SavedVariables.Style) == 1 and TGU_SettingsHandler.IsControlsVisible()
+    else
+        _logger:logError("TGU_SettingsHandler.SavedVariables is nil")
+        return false
+    end
+end
+
+--[[
+	Gets SwimlaneList visible in connection with selected style
+]]--
+function TGU_SettingsHandler.IsSwimlaneListVisible()
+    if (LOG_ACTIVE) then _logger:logTrace("TGU_SettingsHandler.IsSwimlaneListVisible") end
+    if (TGU_SettingsHandler.SavedVariables ~= nil) then
+        if (LOG_ACTIVE) then _logger:logDebug("style", TGU_SettingsHandler.SavedVariables.Style) end
+        return tonumber(TGU_SettingsHandler.SavedVariables.Style) == 2 and TGU_SettingsHandler.IsControlsVisible()
+    else
+        _logger:logError("TGU_SettingsHandler.SavedVariables is nil")
+        return false
+    end
+end
+
+--[[
+	Gets CompactSwimlaneList visible in connection with selected style
+]]--
+function TGU_SettingsHandler.IsCompactSwimlaneListVisible()
+    if (LOG_ACTIVE) then _logger:logTrace("TGU_SettingsHandler.IsCompactSwimlaneListVisible") end
+    if (TGU_SettingsHandler.SavedVariables ~= nil) then
+        if (LOG_ACTIVE) then _logger:logDebug("style", TGU_SettingsHandler.SavedVariables.Style) end
+        return tonumber(TGU_SettingsHandler.SavedVariables.Style) == 3 and TGU_SettingsHandler.IsControlsVisible()
+    else
+        _logger:logError("TGU_SettingsHandler.SavedVariables is nil")
+        return false
+    end
+end
+
+--[[
+	Gets CompactSwimlaneList visible in connection with selected style
+]]--
+function TGU_SettingsHandler.IsControlsVisible()
+    if (LOG_ACTIVE) then _logger:logTrace("TGU_SettingsHandler.IsControlsVisible") end
+    if (TGU_SettingsHandler.SavedVariables ~= nil) then
+        if (LOG_ACTIVE) then _logger:logDebug("onlyAvA", TGU_SettingsHandler.SavedVariables.OnlyAva) end
+        if (TGU_SettingsHandler.SavedVariables.OnlyAva) then
+            _logger:logDebug("isPlayerInAvAWorld", IsPlayerInAvAWorld())
+            return IsPlayerInAvAWorld()
+        else
+            return true
+        end
+    else
+        _logger:logError("TGU_SettingsHandler.SavedVariables is nil")
+        return false
+    end
+end
+
+--[[
+	OnPlayerActivated sends IsZoneChanged event
+]]--
+function TGU_SettingsHandler.OnPlayerActivated(eventCode)
+    if (LOG_ACTIVE) then
+        _logger:logTrace("TGU_SettingsHandler.OnPlayerActivated")
+    end
+
+    CALLBACK_MANAGER:FireCallbacks(TGU_IS_ZONE_CHANGED)
+end
+
+--[[
+	Initialize loads SavedVariables
+]]--
+function TGU_SettingsHandler.Initialize(logger)
+    if (LOG_ACTIVE) then logger:logTrace("TGU_SettingsHandler.Initialize") end
+
+    _logger = logger
+
+    TGU_SettingsHandler.SavedVariables = ZO_SavedVars:NewAccountWide(TGU_SettingsHandler.SettingsName, SETTINGS_VERSION, nil, TGU_SettingsHandler.Default)
+
+    -- Register
+    EVENT_MANAGER:RegisterForEvent(TGU_SettingsHandler.Name, EVENT_PLAYER_ACTIVATED, TGU_SettingsHandler.OnPlayerActivated)
 end
\ No newline at end of file
diff --git a/TaosGroupTools/logic/UltimateGroupHandler.lua b/TaosGroupTools/logic/UltimateGroupHandler.lua
index 70be69d..750914f 100644
--- a/TaosGroupTools/logic/UltimateGroupHandler.lua
+++ b/TaosGroupTools/logic/UltimateGroupHandler.lua
@@ -1,322 +1,322 @@
---[[
-	Addon: Taos Group Ultimate
-	Author: TProg Taonnor
-	Created by @Taonnor
-]]--
-
---[[
-	Local variables
-]]--
-local LOG_ACTIVE = false
-local _logger = nil
-
---[[
-	Table TGU_UltimateGroupHandler
-]]--
-TGU_UltimateGroupHandler = {}
-TGU_UltimateGroupHandler.__index = TGU_UltimateGroupHandler
-
---[[
-	Table Members
-]]--
-TGU_UltimateGroupHandler.Name = "TGU-UltimateGroupHandler"
-TGU_UltimateGroupHandler.UltimateGroups = nil
-
---[[
-	GetUltimateGroupByAbilityPing gets the ultimate group from given ability ping
-]]--
-function TGU_UltimateGroupHandler.GetUltimateGroupByAbilityPing(abilityPing)
-    if (LOG_ACTIVE) then
-        _logger:logTrace("TGU_UltimateGroupHandler.GetUltimateGroupByAbilityPing")
-        _logger:logDebug("abilityPing", abilityPing)
-    end
-
-    for i, group in pairs(TGU_UltimateGroupHandler.UltimateGroups) do
-        if (group.GroupAbilityPing == abilityPing) then
-            return group
-        end
-    end
-
-    -- not found
-    _logger:logError("AbilityId not found " .. tostring(abilityPing))
-
-    return nil
-end
-
---[[
-	GetUltimateGroupByAbilityId gets the ultimate group from given ability ID
-]]--
-function TGU_UltimateGroupHandler.GetUltimateGroupByAbilityId(abilityID)
-    if (LOG_ACTIVE) then
-        _logger:logTrace("TGU_UltimateGroupHandler.GetUltimateGroupByAbilityId")
-        _logger:logDebug("abilityID", abilityID)
-    end
-
-    for i, group in pairs(TGU_UltimateGroupHandler.UltimateGroups) do
-        if (group.GroupAbilityId == abilityID) then
-            return group
-        end
-    end
-
-    -- not found
-    _logger:logError("AbilityId not found " .. tostring(abilityID))
-
-    return nil
-end
-
---[[
-	GetUltimateGroupByGroupName gets the ultimate group from given group name
-]]--
-function TGU_UltimateGroupHandler.GetUltimateGroupByGroupName(groupName)
-    if (LOG_ACTIVE) then
-        _logger:logTrace("TGU_UltimateGroupHandler.GetUltimateGroupByGroupName")
-        _logger:logDebug("groupName", groupName)
-    end
-
-    for i, group in pairs(TGU_UltimateGroupHandler.UltimateGroups) do
-        if (string.lower(group.GroupName) == string.lower(groupName)) then
-            return group
-        end
-    end
-
-    -- not found
-    _logger:logError("GroupName not found " .. tostring(groupName))
-
-    return nil
-end
-
---[[
-	GetUltimateGroups gets all ultimate groups
-]]--
-function TGU_UltimateGroupHandler.GetUltimateGroups()
-    if (LOG_ACTIVE) then _logger:logTrace("TGU_UltimateGroupHandler.GetUltimateGroups") end
-
-    return TGU_UltimateGroupHandler.UltimateGroups
-end
-
---[[
-	CreateUltimateGroups Creates UltimateGroups array
-]]--
-function TGU_UltimateGroupHandler.CreateUltimateGroups()
-    if (LOG_ACTIVE) then _logger:logTrace("TGU_UltimateGroupHandler.CreateUltimateGroups") end
-
-    -- Sorc
-    local negate = {}
-    negate.GroupName = "NEGATE"
-    negate.GroupDescription = GetString(TGU_DESCRIPTIONS_NEGATE)
-    negate.GroupAbilityPing = 1
-    negate.GroupAbilityId = 29861
-
-    local atro = {}
-    atro.GroupName = "ATRO"
-    atro.GroupDescription = GetString(TGU_DESCRIPTIONS_ATRO)
-    atro.GroupAbilityPing = 2
-    atro.GroupAbilityId = 30553
-
-    local overload = {}
-    overload.GroupName = "OVER"
-    overload.GroupDescription = GetString(TGU_DESCRIPTIONS_OVER)
-    overload.GroupAbilityPing = 3
-    overload.GroupAbilityId = 30366
-
-    -- Templar
-    local sweep = {}
-    sweep.GroupName = "SWEEP"
-    sweep.GroupDescription = GetString(TGU_DESCRIPTIONS_SWEEP)
-    sweep.GroupAbilityPing = 4
-    sweep.GroupAbilityId = 23788
-
-    local nova = {}
-    nova.GroupName = "NOVA"
-    nova.GroupDescription = GetString(TGU_DESCRIPTIONS_NOVA)
-    nova.GroupAbilityPing = 5
-    nova.GroupAbilityId = 24301
-
-    local templarHeal = {}
-    templarHeal.GroupName = "TPHEAL"
-    templarHeal.GroupDescription = GetString(TGU_DESCRIPTIONS_TPHEAL)
-    templarHeal.GroupAbilityPing = 6
-    templarHeal.GroupAbilityId = 27413
-
-    -- DK
-    local standard = {}
-    standard.GroupName = "STAND"
-    standard.GroupDescription = GetString(TGU_DESCRIPTIONS_STAND)
-    standard.GroupAbilityPing = 7
-    standard.GroupAbilityId = 34021
-
-    local leap = {}
-    leap.GroupName = "LEAP"
-    leap.GroupDescription = GetString(TGU_DESCRIPTIONS_LEAP)
-    leap.GroupAbilityPing = 8
-    leap.GroupAbilityId = 33668
-
-    local magma = {}
-    magma.GroupName = "MAGMA"
-    magma.GroupDescription = GetString(TGU_DESCRIPTIONS_MAGMA)
-    magma.GroupAbilityPing = 9
-    magma.GroupAbilityId = 33841
-
-    -- NB
-    local stroke = {}
-    stroke.GroupName = "STROKE"
-    stroke.GroupDescription = GetString(TGU_DESCRIPTIONS_STROKE)
-    stroke.GroupAbilityPing = 10
-    stroke.GroupAbilityId = 37545
-
-    local veil = {}
-    veil.GroupName = "VEIL"
-    veil.GroupDescription = GetString(TGU_DESCRIPTIONS_VEIL)
-    veil.GroupAbilityPing = 11
-    veil.GroupAbilityId = 37713
-
-    local nbSoul = {}
-    nbSoul.GroupName = "NBSOUL"
-    nbSoul.GroupDescription = GetString(TGU_DESCRIPTIONS_NBSOUL)
-    nbSoul.GroupAbilityPing = 12
-    nbSoul.GroupAbilityId = 36207
-
-    -- Warden
-    -- BEAR not useful, its always up
-
-    local wardenIce = {}
-    wardenIce.GroupName = "FREEZE"
-    wardenIce.GroupDescription = GetString(TGU_DESCRIPTIONS_FREEZE)
-    wardenIce.GroupAbilityPing = 13
-    wardenIce.GroupAbilityId = 86112
-
-    local wardenHealing = {}
-    wardenHealing.GroupName = "WDHEAL"
-    wardenHealing.GroupDescription = GetString(TGU_DESCRIPTIONS_WDHEAL)
-    wardenHealing.GroupAbilityPing = 14
-    wardenHealing.GroupAbilityId = 93971
-
-    -- Destro
-    local staffIce = {}
-    staffIce.GroupName = "ICE"
-    staffIce.GroupDescription = GetString(TGU_DESCRIPTIONS_ICE)
-    staffIce.GroupAbilityPing = 15
-    staffIce.GroupAbilityId = 86542
-
-    local staffFire = {}
-    staffFire.GroupName = "FIRE"
-    staffFire.GroupDescription = GetString(TGU_DESCRIPTIONS_FIRE)
-    staffFire.GroupAbilityPing = 16
-    staffFire.GroupAbilityId = 86536
-
-    local staffLightning = {}
-    staffLightning.GroupName = "LIGHT"
-    staffLightning.GroupDescription = GetString(TGU_DESCRIPTIONS_LIGHT)
-    staffLightning.GroupAbilityPing = 17
-    staffLightning.GroupAbilityId = 86550
-
-    -- Restro
-    local staffHeal = {}
-    staffHeal.GroupName = "STHEAL"
-    staffHeal.GroupDescription = GetString(TGU_DESCRIPTIONS_STHEAL)
-    staffHeal.GroupAbilityPing = 18
-    staffHeal.GroupAbilityId = 86454
-
-    -- 2H
-    local twoHand = {}
-    twoHand.GroupName = "BERSERK"
-    twoHand.GroupDescription = GetString(TGU_DESCRIPTIONS_BERSERK)
-    twoHand.GroupAbilityPing = 19
-    twoHand.GroupAbilityId = 86284
-
-    -- SB
-    local shield = {}
-    shield.GroupName = "SHIELD"
-    shield.GroupDescription = GetString(TGU_DESCRIPTIONS_SHIELD)
-    shield.GroupAbilityPing = 20
-    shield.GroupAbilityId = 83292
-
-    -- DW
-    local dual = {}
-    dual.GroupName = "DUAL"
-    dual.GroupDescription = GetString(TGU_DESCRIPTIONS_DUAL)
-    dual.GroupAbilityPing = 21
-    dual.GroupAbilityId = 86410
-
-    -- BOW
-    local bow = {}
-    bow.GroupName = "BOW"
-    bow.GroupDescription = GetString(TGU_DESCRIPTIONS_BOW)
-    bow.GroupAbilityPing = 22
-    bow.GroupAbilityId = 86620
-
-    -- Soul
-    local soul = {}
-    soul.GroupName = "SOUL"
-    soul.GroupDescription = GetString(TGU_DESCRIPTIONS_SOUL)
-    soul.GroupAbilityPing = 23
-    soul.GroupAbilityId = 43109
-
-    -- Werewolf
-    local werewolf = {}
-    werewolf.GroupName = "WERE"
-    werewolf.GroupDescription = GetString(TGU_DESCRIPTIONS_WERE)
-    werewolf.GroupAbilityPing = 24
-    werewolf.GroupAbilityId = 42379
-
-    -- Vamp
-    local vamp = {}
-    vamp.GroupName = "VAMP"
-    vamp.GroupDescription = GetString(TGU_DESCRIPTIONS_VAMP)
-    vamp.GroupAbilityPing = 25
-    vamp.GroupAbilityId = 41937
-
-    -- Mageguild
-    local meteor = {}
-    meteor.GroupName = "METEOR"
-    meteor.GroupDescription = GetString(TGU_DESCRIPTIONS_METEOR)
-    meteor.GroupAbilityPing = 26
-    meteor.GroupAbilityId = 42492
-
-    -- Fighterguild
-    local dawnbreaker = {}
-    dawnbreaker.GroupName = "DAWN"
-    dawnbreaker.GroupDescription = GetString(TGU_DESCRIPTIONS_DAWN)
-    dawnbreaker.GroupAbilityPing = 27
-    dawnbreaker.GroupAbilityId = 42598
-
-    -- Support
-    local barrier = {}
-    barrier.GroupName = "BARRIER"
-    barrier.GroupDescription = GetString(TGU_DESCRIPTIONS_BARRIER)
-    barrier.GroupAbilityPing = 28
-    barrier.GroupAbilityId = 46622
-
-    -- Assault
-    local horn = {}
-    horn.GroupName = "HORN"
-    horn.GroupDescription = GetString(TGU_DESCRIPTIONS_HORN)
-    horn.GroupAbilityPing = 29
-    horn.GroupAbilityId = 46537
-
-    -- Add groups
-    TGU_UltimateGroupHandler.UltimateGroups =
-    {
-        negate, atro, overload,
-        sweep, nova, templarHeal,
-        standard, leap, magma,
-        stroke, veil, nbSoul,
-        wardenIce, wardenHealing,
-        staffIce, staffFire, staffLightning, staffHeal,
-        twoHand, shield, dual, bow,
-        soul, werewolf, vamp,
-        meteor, dawnbreaker,
-        barrier, horn
-    }
-end
-
---[[
-	Initialize initializes TGU_UltimateGroupHandler
-]]--
-function TGU_UltimateGroupHandler.Initialize(logger)
-    if (LOG_ACTIVE) then logger:logTrace("TGU_UltimateGroupHandler.Initialize") end
-
-    _logger = logger
-
-    TGU_UltimateGroupHandler.CreateUltimateGroups()
+--[[
+	Addon: Taos Group Ultimate
+	Author: TProg Taonnor
+	Created by @Taonnor
+]]--
+
+--[[
+	Local variables
+]]--
+local LOG_ACTIVE = false
+local _logger = nil
+
+--[[
+	Table TGU_UltimateGroupHandler
+]]--
+TGU_UltimateGroupHandler = {}
+TGU_UltimateGroupHandler.__index = TGU_UltimateGroupHandler
+
+--[[
+	Table Members
+]]--
+TGU_UltimateGroupHandler.Name = "TGU-UltimateGroupHandler"
+TGU_UltimateGroupHandler.UltimateGroups = nil
+
+--[[
+	GetUltimateGroupByAbilityPing gets the ultimate group from given ability ping
+]]--
+function TGU_UltimateGroupHandler.GetUltimateGroupByAbilityPing(abilityPing)
+    if (LOG_ACTIVE) then
+        _logger:logTrace("TGU_UltimateGroupHandler.GetUltimateGroupByAbilityPing")
+        _logger:logDebug("abilityPing", abilityPing)
+    end
+
+    for i, group in pairs(TGU_UltimateGroupHandler.UltimateGroups) do
+        if (group.GroupAbilityPing == abilityPing) then
+            return group
+        end
+    end
+
+    -- not found
+    _logger:logError("AbilityId not found " .. tostring(abilityPing))
+
+    return nil
+end
+
+--[[
+	GetUltimateGroupByAbilityId gets the ultimate group from given ability ID
+]]--
+function TGU_UltimateGroupHandler.GetUltimateGroupByAbilityId(abilityID)
+    if (LOG_ACTIVE) then
+        _logger:logTrace("TGU_UltimateGroupHandler.GetUltimateGroupByAbilityId")
+        _logger:logDebug("abilityID", abilityID)
+    end
+
+    for i, group in pairs(TGU_UltimateGroupHandler.UltimateGroups) do
+        if (group.GroupAbilityId == abilityID) then
+            return group
+        end
+    end
+
+    -- not found
+    _logger:logError("AbilityId not found " .. tostring(abilityID))
+
+    return nil
+end
+
+--[[
+	GetUltimateGroupByGroupName gets the ultimate group from given group name
+]]--
+function TGU_UltimateGroupHandler.GetUltimateGroupByGroupName(groupName)
+    if (LOG_ACTIVE) then
+        _logger:logTrace("TGU_UltimateGroupHandler.GetUltimateGroupByGroupName")
+        _logger:logDebug("groupName", groupName)
+    end
+
+    for i, group in pairs(TGU_UltimateGroupHandler.UltimateGroups) do
+        if (string.lower(group.GroupName) == string.lower(groupName)) then
+            return group
+        end
+    end
+
+    -- not found
+    _logger:logError("GroupName not found " .. tostring(groupName))
+
+    return nil
+end
+
+--[[
+	GetUltimateGroups gets all ultimate groups
+]]--
+function TGU_UltimateGroupHandler.GetUltimateGroups()
+    if (LOG_ACTIVE) then _logger:logTrace("TGU_UltimateGroupHandler.GetUltimateGroups") end
+
+    return TGU_UltimateGroupHandler.UltimateGroups
+end
+
+--[[
+	CreateUltimateGroups Creates UltimateGroups array
+]]--
+function TGU_UltimateGroupHandler.CreateUltimateGroups()
+    if (LOG_ACTIVE) then _logger:logTrace("TGU_UltimateGroupHandler.CreateUltimateGroups") end
+
+    -- Sorc
+    local negate = {}
+    negate.GroupName = "NEGATE"
+    negate.GroupDescription = GetString(TGU_DESCRIPTIONS_NEGATE)
+    negate.GroupAbilityPing = 1
+    negate.GroupAbilityId = 29861
+
+    local atro = {}
+    atro.GroupName = "ATRO"
+    atro.GroupDescription = GetString(TGU_DESCRIPTIONS_ATRO)
+    atro.GroupAbilityPing = 2
+    atro.GroupAbilityId = 30553
+
+    local overload = {}
+    overload.GroupName = "OVER"
+    overload.GroupDescription = GetString(TGU_DESCRIPTIONS_OVER)
+    overload.GroupAbilityPing = 3
+    overload.GroupAbilityId = 30366
+
+    -- Templar
+    local sweep = {}
+    sweep.GroupName = "SWEEP"
+    sweep.GroupDescription = GetString(TGU_DESCRIPTIONS_SWEEP)
+    sweep.GroupAbilityPing = 4
+    sweep.GroupAbilityId = 23788
+
+    local nova = {}
+    nova.GroupName = "NOVA"
+    nova.GroupDescription = GetString(TGU_DESCRIPTIONS_NOVA)
+    nova.GroupAbilityPing = 5
+    nova.GroupAbilityId = 24301
+
+    local templarHeal = {}
+    templarHeal.GroupName = "TPHEAL"
+    templarHeal.GroupDescription = GetString(TGU_DESCRIPTIONS_TPHEAL)
+    templarHeal.GroupAbilityPing = 6
+    templarHeal.GroupAbilityId = 27413
+
+    -- DK
+    local standard = {}
+    standard.GroupName = "STAND"
+    standard.GroupDescription = GetString(TGU_DESCRIPTIONS_STAND)
+    standard.GroupAbilityPing = 7
+    standard.GroupAbilityId = 34021
+
+    local leap = {}
+    leap.GroupName = "LEAP"
+    leap.GroupDescription = GetString(TGU_DESCRIPTIONS_LEAP)
+    leap.GroupAbilityPing = 8
+    leap.GroupAbilityId = 33668
+
+    local magma = {}
+    magma.GroupName = "MAGMA"
+    magma.GroupDescription = GetString(TGU_DESCRIPTIONS_MAGMA)
+    magma.GroupAbilityPing = 9
+    magma.GroupAbilityId = 33841
+
+    -- NB
+    local stroke = {}
+    stroke.GroupName = "STROKE"
+    stroke.GroupDescription = GetString(TGU_DESCRIPTIONS_STROKE)
+    stroke.GroupAbilityPing = 10
+    stroke.GroupAbilityId = 37545
+
+    local veil = {}
+    veil.GroupName = "VEIL"
+    veil.GroupDescription = GetString(TGU_DESCRIPTIONS_VEIL)
+    veil.GroupAbilityPing = 11
+    veil.GroupAbilityId = 37713
+
+    local nbSoul = {}
+    nbSoul.GroupName = "NBSOUL"
+    nbSoul.GroupDescription = GetString(TGU_DESCRIPTIONS_NBSOUL)
+    nbSoul.GroupAbilityPing = 12
+    nbSoul.GroupAbilityId = 36207
+
+    -- Warden
+    -- BEAR not useful, its always up
+
+    local wardenIce = {}
+    wardenIce.GroupName = "FREEZE"
+    wardenIce.GroupDescription = GetString(TGU_DESCRIPTIONS_FREEZE)
+    wardenIce.GroupAbilityPing = 13
+    wardenIce.GroupAbilityId = 86112
+
+    local wardenHealing = {}
+    wardenHealing.GroupName = "WDHEAL"
+    wardenHealing.GroupDescription = GetString(TGU_DESCRIPTIONS_WDHEAL)
+    wardenHealing.GroupAbilityPing = 14
+    wardenHealing.GroupAbilityId = 93971
+
+    -- Destro
+    local staffIce = {}
+    staffIce.GroupName = "ICE"
+    staffIce.GroupDescription = GetString(TGU_DESCRIPTIONS_ICE)
+    staffIce.GroupAbilityPing = 15
+    staffIce.GroupAbilityId = 86542
+
+    local staffFire = {}
+    staffFire.GroupName = "FIRE"
+    staffFire.GroupDescription = GetString(TGU_DESCRIPTIONS_FIRE)
+    staffFire.GroupAbilityPing = 16
+    staffFire.GroupAbilityId = 86536
+
+    local staffLightning = {}
+    staffLightning.GroupName = "LIGHT"
+    staffLightning.GroupDescription = GetString(TGU_DESCRIPTIONS_LIGHT)
+    staffLightning.GroupAbilityPing = 17
+    staffLightning.GroupAbilityId = 86550
+
+    -- Restro
+    local staffHeal = {}
+    staffHeal.GroupName = "STHEAL"
+    staffHeal.GroupDescription = GetString(TGU_DESCRIPTIONS_STHEAL)
+    staffHeal.GroupAbilityPing = 18
+    staffHeal.GroupAbilityId = 86454
+
+    -- 2H
+    local twoHand = {}
+    twoHand.GroupName = "BERSERK"
+    twoHand.GroupDescription = GetString(TGU_DESCRIPTIONS_BERSERK)
+    twoHand.GroupAbilityPing = 19
+    twoHand.GroupAbilityId = 86284
+
+    -- SB
+    local shield = {}
+    shield.GroupName = "SHIELD"
+    shield.GroupDescription = GetString(TGU_DESCRIPTIONS_SHIELD)
+    shield.GroupAbilityPing = 20
+    shield.GroupAbilityId = 83292
+
+    -- DW
+    local dual = {}
+    dual.GroupName = "DUAL"
+    dual.GroupDescription = GetString(TGU_DESCRIPTIONS_DUAL)
+    dual.GroupAbilityPing = 21
+    dual.GroupAbilityId = 86410
+
+    -- BOW
+    local bow = {}
+    bow.GroupName = "BOW"
+    bow.GroupDescription = GetString(TGU_DESCRIPTIONS_BOW)
+    bow.GroupAbilityPing = 22
+    bow.GroupAbilityId = 86620
+
+    -- Soul
+    local soul = {}
+    soul.GroupName = "SOUL"
+    soul.GroupDescription = GetString(TGU_DESCRIPTIONS_SOUL)
+    soul.GroupAbilityPing = 23
+    soul.GroupAbilityId = 43109
+
+    -- Werewolf
+    local werewolf = {}
+    werewolf.GroupName = "WERE"
+    werewolf.GroupDescription = GetString(TGU_DESCRIPTIONS_WERE)
+    werewolf.GroupAbilityPing = 24
+    werewolf.GroupAbilityId = 42379
+
+    -- Vamp
+    local vamp = {}
+    vamp.GroupName = "VAMP"
+    vamp.GroupDescription = GetString(TGU_DESCRIPTIONS_VAMP)
+    vamp.GroupAbilityPing = 25
+    vamp.GroupAbilityId = 41937
+
+    -- Mageguild
+    local meteor = {}
+    meteor.GroupName = "METEOR"
+    meteor.GroupDescription = GetString(TGU_DESCRIPTIONS_METEOR)
+    meteor.GroupAbilityPing = 26
+    meteor.GroupAbilityId = 42492
+
+    -- Fighterguild
+    local dawnbreaker = {}
+    dawnbreaker.GroupName = "DAWN"
+    dawnbreaker.GroupDescription = GetString(TGU_DESCRIPTIONS_DAWN)
+    dawnbreaker.GroupAbilityPing = 27
+    dawnbreaker.GroupAbilityId = 42598
+
+    -- Support
+    local barrier = {}
+    barrier.GroupName = "BARRIER"
+    barrier.GroupDescription = GetString(TGU_DESCRIPTIONS_BARRIER)
+    barrier.GroupAbilityPing = 28
+    barrier.GroupAbilityId = 46622
+
+    -- Assault
+    local horn = {}
+    horn.GroupName = "HORN"
+    horn.GroupDescription = GetString(TGU_DESCRIPTIONS_HORN)
+    horn.GroupAbilityPing = 29
+    horn.GroupAbilityId = 46537
+
+    -- Add groups
+    TGU_UltimateGroupHandler.UltimateGroups =
+    {
+        negate, atro, overload,
+        sweep, nova, templarHeal,
+        standard, leap, magma,
+        stroke, veil, nbSoul,
+        wardenIce, wardenHealing,
+        staffIce, staffFire, staffLightning, staffHeal,
+        twoHand, shield, dual, bow,
+        soul, werewolf, vamp,
+        meteor, dawnbreaker,
+        barrier, horn
+    }
+end
+
+--[[
+	Initialize initializes TGU_UltimateGroupHandler
+]]--
+function TGU_UltimateGroupHandler.Initialize(logger)
+    if (LOG_ACTIVE) then logger:logTrace("TGU_UltimateGroupHandler.Initialize") end
+
+    _logger = logger
+
+    TGU_UltimateGroupHandler.CreateUltimateGroups()
 end
\ No newline at end of file
diff --git a/TaosGroupTools/ui/CompactSwimlaneList.lua b/TaosGroupTools/ui/CompactSwimlaneList.lua
index cf1d7d0..8feed06 100644
--- a/TaosGroupTools/ui/CompactSwimlaneList.lua
+++ b/TaosGroupTools/ui/CompactSwimlaneList.lua
@@ -1,500 +1,500 @@
---[[
-	Addon: Taos Group Ultimate
-	Author: TProg Taonnor
-	Created by @Taonnor
-]]--
-
---[[
-	Local variables
-]]--
-local LOG_ACTIVE = false
-
-local SWIMLANES = 6
-local ROWS = 6
-local REFRESHRATE = 1000 -- ms; RegisterForUpdate is in miliseconds
-local TIMEOUT = 4 -- s; GetTimeStamp() is in seconds
-
-local _logger = nil
-local _control = nil
-
---[[
-	Table CompactSwimlaneList
-]]--
-TGU_CompactSwimlaneList = {}
-TGU_CompactSwimlaneList.__index = TGU_CompactSwimlaneList
-
---[[
-	Table Members
-]]--
-TGU_CompactSwimlaneList.Name = "TGU-CompactSwimlaneList"
-TGU_CompactSwimlaneList.IsMocked = false
-TGU_CompactSwimlaneList.Swimlanes = {}
-
---[[
-	Sets visibility of labels
-]]--
-function TGU_CompactSwimlaneList.RefreshList()
-	if (LOG_ACTIVE) then _logger:logTrace("TGU_CompactSwimlaneList.RefreshList") end
-
-    -- Check all swimlanes
-    for i,swimlane in ipairs(TGU_CompactSwimlaneList.Swimlanes) do
-        TGU_CompactSwimlaneList.ClearPlayersFromSwimlane(swimlane)
-	end
-end
-
---[[
-	Sorts swimlane
-]]--
-function TGU_CompactSwimlaneList.SortSwimlane(swimlane)
-	if (LOG_ACTIVE) then _logger:logTrace("TGU_CompactSwimlaneList.SortSwimlane") end
-
-    -- Comparer
-    function compare(playerLeft, playerRight)
-        if (playerLeft.RelativeUltimate == playerRight.RelativeUltimate) then
-            return playerLeft.PingTag < playerRight.PingTag
-        else
-            return playerLeft.RelativeUltimate > playerRight.RelativeUltimate
-        end
-    end
-
-    table.sort(swimlane.Players, compare)
-
-    -- Sort by name
-
-    -- Update sorted swimlane list
-    for i,swimlanePlayer in ipairs(swimlane.Players) do
-        TGU_CompactSwimlaneList.UpdateListRow(swimlane.SwimlaneControl:GetNamedChild("Row" .. i), swimlanePlayer)
-    end
-end
-
---[[
-	Updates list row
-]]--
-function TGU_CompactSwimlaneList.UpdateListRow(row, player)
-	if (LOG_ACTIVE) then
-        _logger:logTrace("TGU_CompactSwimlaneList.UpdateListRow")
-    end
-
-    local playerName = player.PlayerName
-    local nameLength = string.len(playerName)
-
-    if (nameLength > 6) then
-        playerName = string.sub(playerName, 0, 5) .. ".."
-    end
-
-    row:GetNamedChild("SenderNameValueLabel"):SetText(playerName)
-    row:GetNamedChild("RelativeUltimateStatusBar"):SetValue(player.RelativeUltimate)
-
-	if (player.IsPlayerDead) then
-        -- Dead Color
-        row:GetNamedChild("SenderNameValueLabel"):SetColor(0.5, 0.5, 0.5, 0.8)
-        row:GetNamedChild("RelativeUltimateStatusBar"):SetColor(0.8, 0.03, 0.03, 0.7)
-    elseif (player.RelativeUltimate == 100) then
-		-- Ready Color
-        row:GetNamedChild("SenderNameValueLabel"):SetColor(1, 1, 1, 1)
-        row:GetNamedChild("RelativeUltimateStatusBar"):SetColor(0.03, 0.7, 0.03, 0.7)
-	else
-		-- Inprogress Color
-        row:GetNamedChild("SenderNameValueLabel"):SetColor(1, 1, 1, 0.8)
-        row:GetNamedChild("RelativeUltimateStatusBar"):SetColor(0.03, 0.03, 0.7, 0.7)
-	end
-
-    row:SetHidden(false)
-end
-
---[[
-	Updates list row
-]]--
-function TGU_CompactSwimlaneList.UpdatePlayer(player)
-	if (LOG_ACTIVE) then
-        _logger:logTrace("TGU_CompactSwimlaneList.UpdatePlayer")
-    end
-
-	if (player) then
-        local swimLane = TGU_CompactSwimlaneList.GetSwimLane(player.UltimateGroup.GroupAbilityId)
-
-        if (swimLane) then
-            local row = TGU_CompactSwimlaneList.GetSwimLaneRow(swimLane, player.PlayerName)
-
-            -- Update timestamp
-            if (row ~= nil) then
-                for i,swimlanePlayer in ipairs(swimLane.Players) do
-		            if (swimlanePlayer.PlayerName == player.PlayerName) then
-                        swimlanePlayer.LastMapPingTimestamp = GetTimeStamp()
-                        swimlanePlayer.IsPlayerDead = player.IsPlayerDead
-                        swimlanePlayer.RelativeUltimate = player.RelativeUltimate
-                        break
-                    end
-	            end
-            else
-                -- Add new player
-                local nextFreeRow = 1
-
-                for i,player in ipairs(swimLane.Players) do
-		            nextFreeRow = nextFreeRow + 1
-	            end
-
-                if (nextFreeRow <= ROWS) then
-                    if (LOG_ACTIVE) then
-                        _logger:logDebug("TGU_CompactSwimlaneList.UpdatePlayer, add player " .. tostring(player.PlayerName) .. " to row " .. tostring(nextFreeRow))
-                    end
-
-                    player.LastMapPingTimestamp = GetTimeStamp()
-                    swimLane.Players[nextFreeRow] = player
-                    row = swimLane.SwimlaneControl:GetNamedChild("Row" .. nextFreeRow)
-                else
-                    if (LOG_ACTIVE) then _logger:logDebug("TGU_CompactSwimlaneList.UpdatePlayer, too much players for one swimlane " .. tostring(nextFreeRow)) end
-                end
-            end
-
-            -- Only update if player in a row
-            if (row ~= nil) then
-                if (TGU_SettingsHandler.SavedVariables.IsSortingActive) then
-                    -- Sort swimlane with all players
-                    TGU_CompactSwimlaneList.SortSwimlane(swimLane)
-                else
-                    -- Directly update row with player
-                    TGU_CompactSwimlaneList.UpdateListRow(row, player)
-                end
-            end
-        else
-            if (LOG_ACTIVE) then _logger:logDebug("TGU_CompactSwimlaneList.UpdatePlayer, swimlane not found for ultimategroup " .. tostring(ultimateGroup.GroupName)) end
-        end
-	end
-end
-
---[[
-	Get swimlane from current SwimLanes
-]]--
-function TGU_CompactSwimlaneList.GetSwimLane(ultimateGroupId)
-    if (LOG_ACTIVE) then
-        _logger:logTrace("TGU_CompactSwimlaneList.GetSwimLane")
-        _logger:logDebug("ultimateGroupId", ultimateGroupId)
-    end
-
-    if (ultimateGroupId ~= 0) then
-        for i,swimLane in ipairs(TGU_CompactSwimlaneList.Swimlanes) do
-		    if (swimLane.UltimateGroupId == ultimateGroupId) then
-                return swimLane
-            end
-	    end
-
-        if (LOG_ACTIVE) then _logger:logDebug("TGU_CompactSwimlaneList.GetSwimLane, swimLane not found " .. tostring(ultimateGroupId)) end
-        return nil
-    else
-        _logger:logError("TGU_CompactSwimlaneList.GetSwimLane, ultimateGroupId is 0")
-        return nil
-    end
-end
-
---[[
-	Get Player Row from current players in swimlane
-]]--
-function TGU_CompactSwimlaneList.GetSwimLaneRow(swimLane, playerName)
-    if (LOG_ACTIVE) then
-        _logger:logTrace("TGU_CompactSwimlaneList.GetSwimLaneRow")
-        _logger:logDebug("swimLane ID", swimLane.Id)
-    end
-
-    if (swimLane) then
-        for i,player in ipairs(swimLane.Players) do
-            if (LOG_ACTIVE) then _logger:logDebug(player.PlayerName .. " == " .. playerName) end
-		    if (player.PlayerName == playerName) then
-                return swimLane.SwimlaneControl:GetNamedChild("Row" .. i)
-            end
-	    end
-
-        if (LOG_ACTIVE) then _logger:logDebug("TGU_CompactSwimlaneList.GetSwimLane, player not found " .. tostring(playerName)) end
-        return nil
-    else
-        _logger:logError("TGU_CompactSwimlaneList.GetSwimLane, swimLane is nil")
-        return nil
-    end
-end
-
---[[
-	Clears all players in swimlane
-]]--
-function TGU_CompactSwimlaneList.ClearPlayersFromSwimlane(swimlane)
-    if (LOG_ACTIVE) then
-        _logger:logTrace("TGU_CompactSwimlaneList.ClearPlayersFromSwimlane")
-        _logger:logDebug("swimlane ID", swimlane.Id)
-    end
-
-    if (swimlane) then
-        for i=1, ROWS, 1 do
-            local row = swimlane.SwimlaneControl:GetNamedChild("Row" .. i)
-            local swimlanePlayer = swimlane.Players[i]
-
-            if (swimlanePlayer ~= nil) then
-                local isPlayerNotGrouped = IsUnitGrouped(swimlanePlayer.PingTag) == false
-
-                if (TGU_CompactSwimlaneList.IsMocked) then
-                    isPlayerNotGrouped = false
-                end
-
-                local isPlayerTimedOut = (GetTimeStamp() - swimlanePlayer.LastMapPingTimestamp) > TIMEOUT
-                local isPlayerUltimateNotCorrect = swimlane.UltimateGroupId ~= swimlanePlayer.UltimateGroup.GroupAbilityId
-
-                if (isPlayerNotGrouped or isPlayerTimedOut or isPlayerUltimateNotCorrect) then
-                    if (LOG_ACTIVE) then _logger:logDebug("Player invalid, hide row: " .. tostring(i)) end
-
-                    row:SetHidden(true)
-                    table.remove(swimlane.Players, i)
-                end
-            else
-                if (LOG_ACTIVE) then _logger:logDebug("Row empty, hide: " .. tostring(i)) end
-
-                row:SetHidden(true)
-            end
-        end
-    end
-end
-
---[[
-	SetControlMovable sets the Movable and MouseEnabled flag in UI elements
-]]--
-function TGU_CompactSwimlaneList.SetControlMovable(isMovable)
-    if (LOG_ACTIVE) then
-        _logger:logTrace("TGU_CompactSwimlaneList.SetControlMovable")
-        _logger:logDebug("isMovable", isMovable)
-    end
-
-    _control:GetNamedChild("MovableControl"):SetHidden(isMovable == false)
-
-    _control:SetMovable(isMovable)
-	_control:SetMouseEnabled(isMovable)
-end
-
---[[
-	RestorePosition sets TGU_CompactSwimlaneList on settings position
-]]--
-function TGU_CompactSwimlaneList.RestorePosition(posX, posY)
-    if (LOG_ACTIVE) then
-        _logger:logTrace("TGU_CompactSwimlaneList.RestorePosition")
-        _logger:logDebug("posX, posY", posX, posY)
-    end
-
-	_control:ClearAnchors()
-	_control:SetAnchor(TOPLEFT, GuiRoot, TOPLEFT, posX, posY)
-end
-
---[[
-	OnTGU_CompactSwimlaneListMoveStop saves current TGU_CompactSwimlaneList position to settings
-]]--
-function TGU_CompactSwimlaneList.OnCompactSwimlaneListMoveStop()
-    if (LOG_ACTIVE) then _logger:logTrace("TGU_CompactSwimlaneList.OnCompactSwimlaneListMoveStop") end
-
-	local left = _control:GetLeft()
-	local top = _control:GetTop()
-
-    TGU_SettingsHandler.SavedVariables.PosX = left
-    TGU_SettingsHandler.SavedVariables.PosY = top
-
-    if (LOG_ACTIVE) then
-        _logger:logDebug("PosX, PosY", TGU_SettingsHandler.SavedVariables.PosX, TGU_SettingsHandler.SavedVariables.PosY)
-    end
-end
-
---[[
-	SetControlHidden sets hidden on control
-]]--
-function TGU_CompactSwimlaneList.SetControlHidden(isHidden)
-    if (LOG_ACTIVE) then
-        _logger:logTrace("TGU_CompactSwimlaneList.SetControlHidden")
-        _logger:logDebug("isHidden", isHidden)
-    end
-
-    if (TGU_GroupHandler.IsGrouped) then
-        _control:SetHidden(isHidden)
-    else
-        _control:SetHidden(true)
-    end
-end
-
---[[
-	SetControlActive sets hidden on control
-]]--
-function TGU_CompactSwimlaneList.SetControlActive()
-    if (LOG_ACTIVE) then
-        _logger:logTrace("TGU_CompactSwimlaneList.SetControlActive")
-    end
-
-    local isHidden = TGU_SettingsHandler.IsCompactSwimlaneListVisible() == false
-    if (LOG_ACTIVE) then _logger:logDebug("isHidden", isHidden) end
-
-    TGU_CompactSwimlaneList.SetControlHidden(isHidden or CurrentHudHiddenState())
-
-    if (isHidden) then
-        -- Start timeout timer
-	    EVENT_MANAGER:UnregisterForUpdate(TGU_CompactSwimlaneList.Name)
-
-        CALLBACK_MANAGER:UnregisterCallback(TGU_GROUP_CHANGED, TGU_CompactSwimlaneList.RefreshList)
-        CALLBACK_MANAGER:UnregisterCallback(TGU_PLAYER_DATA_CHANGED, TGU_CompactSwimlaneList.UpdatePlayer)
-        CALLBACK_MANAGER:UnregisterCallback(TGU_MOVABLE_CHANGED, TGU_CompactSwimlaneList.SetControlMovable)
-        CALLBACK_MANAGER:UnregisterCallback(TGU_SWIMLANE_ULTIMATE_GROUP_ID_CHANGED, TGU_CompactSwimlaneList.SetSwimlaneUltimate)
-        CALLBACK_MANAGER:UnregisterCallback(TUI_HUD_HIDDEN_STATE_CHANGED, TGU_CompactSwimlaneList.SetControlHidden)
-
-        -- Invis
-        TGU_CompactSwimlaneList.SetControlHidden(isHidden)
-    else
-        TGU_CompactSwimlaneList.SetControlMovable(TGU_SettingsHandler.SavedVariables.Movable)
-        TGU_CompactSwimlaneList.RestorePosition(TGU_SettingsHandler.SavedVariables.PosX, TGU_SettingsHandler.SavedVariables.PosY)
-
-        -- Start timeout timer
-	    EVENT_MANAGER:RegisterForUpdate(TGU_CompactSwimlaneList.Name, REFRESHRATE, TGU_CompactSwimlaneList.RefreshList)
-
-        CALLBACK_MANAGER:RegisterCallback(TGU_GROUP_CHANGED, TGU_CompactSwimlaneList.RefreshList)
-        CALLBACK_MANAGER:RegisterCallback(TGU_PLAYER_DATA_CHANGED, TGU_CompactSwimlaneList.UpdatePlayer)
-        CALLBACK_MANAGER:RegisterCallback(TGU_MOVABLE_CHANGED, TGU_CompactSwimlaneList.SetControlMovable)
-        CALLBACK_MANAGER:RegisterCallback(TGU_SWIMLANE_ULTIMATE_GROUP_ID_CHANGED, TGU_CompactSwimlaneList.SetSwimlaneUltimate)
-        CALLBACK_MANAGER:RegisterCallback(TUI_HUD_HIDDEN_STATE_CHANGED, TGU_CompactSwimlaneList.SetControlHidden)
-    end
-end
-
---[[
-	OnSwimlaneHeaderClicked called on header clicked
-]]--
-function TGU_CompactSwimlaneList.OnSwimlaneHeaderClicked(button, swimlaneId)
-    if (LOG_ACTIVE) then
-        _logger:logTrace("TGU_CompactSwimlaneList.OnSwimlaneHeaderClicked")
-        _logger:logDebug("swimlaneId", swimlaneId)
-    end
-
-    if (button ~= nil) then
-        CALLBACK_MANAGER:RegisterCallback(TGU_SET_ULTIMATE_GROUP, TGU_CompactSwimlaneList.OnSetUltimateGroup)
-        CALLBACK_MANAGER:FireCallbacks(TGU_SHOW_ULTIMATE_GROUP_MENU, button, swimlaneId)
-    else
-        _logger:logError("TGU_CompactSwimlaneList.OnSwimlaneHeaderClicked, button nil")
-    end
-end
-
---[[
-	OnSetUltimateGroup called on header clicked
-]]--
-function TGU_CompactSwimlaneList.OnSetUltimateGroup(group, swimlaneId)
-    if (LOG_ACTIVE) then
-        _logger:logTrace("TGU_CompactSwimlaneList.OnSetUltimateGroup")
-        _logger:logDebug("group.GroupName, swimlaneId", group.GroupName, swimlaneId)
-    end
-
-    CALLBACK_MANAGER:UnregisterCallback(TGU_SET_ULTIMATE_GROUP, TGU_CompactSwimlaneList.OnSetUltimateGroup)
-
-    if (group ~= nil and swimlaneId ~= nil and swimlaneId >= 1 and swimlaneId <= 6) then
-        TGU_SettingsHandler.SetSwimlaneUltimateGroupIdSettings(swimlaneId, group)
-    else
-        _logger:logError("TGU_UltimateGroupMenu.ShowUltimateGroupMenu, group nil or swimlaneId invalid")
-    end
-end
-
---[[
-	SetSwimlaneUltimate sets the swimlane header icon in base of ultimateGroupId
-]]--
-function TGU_CompactSwimlaneList.SetSwimlaneUltimate(swimlaneId, ultimateGroup)
-    if (LOG_ACTIVE) then
-        _logger:logTrace("TGU_CompactSwimlaneList.SetSwimlaneUltimate")
-        _logger:logDebug("ultimateGroup.GroupName, swimlaneId", ultimateGroup.GroupName, swimlaneId)
-    end
-
-    local swimlaneObject = TGU_CompactSwimlaneList.Swimlanes[swimlaneId]
-    local iconControl = swimlaneObject.SwimlaneControl:GetNamedChild("Header"):GetNamedChild("SelectorButtonControl"):GetNamedChild("Icon")
-
-    if (ultimateGroup ~= nil and iconControl ~= nil) then
-        iconControl:SetTexture(GetAbilityIcon(ultimateGroup.GroupAbilityId))
-
-        swimlaneObject.UltimateGroupId = ultimateGroup.GroupAbilityId
-        TGU_CompactSwimlaneList.ClearPlayersFromSwimlane(swimlaneObject)
-    else
-        _logger:logError("TGU_CompactSwimlaneList.SetSwimlaneUltimateIcon, icon is " .. tostring(icon) .. ";" .. tostring(iconControl) .. ";" .. tostring(ultimateGroup))
-    end
-end
-
---[[
-	CreateCompactSwimlaneListHeaders creates swimlane list headers
-]]--
-function TGU_CompactSwimlaneList.CreateCompactSwimlaneListHeaders()
-    if (LOG_ACTIVE) then _logger:logTrace("TGU_CompactSwimlaneList.CreateCompactSwimlaneListHeaders") end
-
-	for i=1, SWIMLANES, 1 do
-        local ultimateGroupId = TGU_SettingsHandler.SavedVariables.SwimlaneUltimateGroupIds[i]
-        local ultimateGroup = TGU_UltimateGroupHandler.GetUltimateGroupByAbilityId(ultimateGroupId)
-
-        local swimlaneControlName = "Swimlane" .. tostring(i)
-        local swimlaneControl = _control:GetNamedChild(swimlaneControlName)
-
-        -- Add button
-        local button = swimlaneControl:GetNamedChild("Header"):GetNamedChild("SelectorButtonControl"):GetNamedChild("Button")
-        button:SetHandler("OnClicked", function() TGU_CompactSwimlaneList.OnSwimlaneHeaderClicked(button, i) end)
-
-        local swimLane = {}
-        swimLane.Id = i
-        swimLane.SwimlaneControl = swimlaneControl
-        swimLane.Players = {}
-
-        if (ultimateGroup ~= nil) then
-            if (LOG_ACTIVE) then
-                _logger:logDebug("Create Swimlane", i)
-                _logger:logDebug("ultimateGroup.GroupName", ultimateGroup.GroupName)
-                _logger:logDebug("swimlaneControlName", swimlaneControlName)
-            end
-
-            local icon = swimlaneControl:GetNamedChild("Header"):GetNamedChild("SelectorButtonControl"):GetNamedChild("Icon")
-            icon:SetTexture(GetAbilityIcon(ultimateGroup.GroupAbilityId))
-
-            swimLane.UltimateGroupId = ultimateGroup.GroupAbilityId
-        else
-            _logger:logError("TGU_CompactSwimlaneList.CreateCompactSwimlaneListHeaders, ultimateGroup nil.")
-        end
-
-        TGU_CompactSwimlaneList.CreateCompactSwimlaneListRows(swimlaneControl)
-	    TGU_CompactSwimlaneList.Swimlanes[i] = swimLane
-	end
-end
-
---[[
-	CreateCompactSwimlaneListRows creates swimlane lsit rows
-]]--
-function TGU_CompactSwimlaneList.CreateCompactSwimlaneListRows(swimlaneControl)
-    if (LOG_ACTIVE) then _logger:logTrace("TGU_CompactSwimlaneList.CreateCompactSwimlaneListRows") end
-
-    if (swimlaneControl ~= nil) then
-	    for i=1, ROWS, 1 do
-		    local row = CreateControlFromVirtual("$(parent)Row", swimlaneControl, "CompactGroupUltimateSwimlaneRow", i)
-            if (LOG_ACTIVE) then _logger:logDebug("Row created " .. row:GetName()) end
-
-		    row:SetHidden(true) -- initial not visible
-
-		    if (i == 1) then
-                row:SetAnchor(TOPLEFT, swimlaneControl, TOPLEFT, 0, 50)
-            elseif (i == 5) then -- Fix pixelbug, Why the hell ZOS?!
-                row:SetAnchor(TOPLEFT, lastRow, BOTTOMLEFT, 0, 0)
-            else
-				row:SetAnchor(TOPLEFT, lastRow, BOTTOMLEFT, 0, -1)
-			end
-
-		    lastRow = row
-	    end
-    else
-        _logger:logError("TGU_CompactSwimlaneList.CreateCompactSwimlaneListRows, swimlaneControl nil.")
-    end
-end
-
---[[
-	Initialize initializes TGU_CompactSwimlaneList
-]]--
-function TGU_CompactSwimlaneList.Initialize(logger, isMocked)
-    if (LOG_ACTIVE) then
-        logger:logTrace("TGU_CompactSwimlaneList.Initialize")
-    end
-
-    _logger = logger
-    _control = TGU_CompactSwimlaneListControl
-
-    TGU_CompactSwimlaneList.IsMocked = isMocked
-
-    TGU_CompactSwimlaneList.CreateCompactSwimlaneListHeaders()
-
-    CALLBACK_MANAGER:RegisterCallback(TGU_STYLE_CHANGED, TGU_CompactSwimlaneList.SetControlActive)
-    CALLBACK_MANAGER:RegisterCallback(TGU_IS_ZONE_CHANGED, TGU_CompactSwimlaneList.SetControlActive)
-    CALLBACK_MANAGER:RegisterCallback(TGU_UNIT_GROUPED_CHANGED, TGU_CompactSwimlaneList.SetControlActive)
+--[[
+	Addon: Taos Group Ultimate
+	Author: TProg Taonnor
+	Created by @Taonnor
+]]--
+
+--[[
+	Local variables
+]]--
+local LOG_ACTIVE = false
+
+local SWIMLANES = 6
+local ROWS = 6
+local REFRESHRATE = 1000 -- ms; RegisterForUpdate is in miliseconds
+local TIMEOUT = 4 -- s; GetTimeStamp() is in seconds
+
+local _logger = nil
+local _control = nil
+
+--[[
+	Table CompactSwimlaneList
+]]--
+TGU_CompactSwimlaneList = {}
+TGU_CompactSwimlaneList.__index = TGU_CompactSwimlaneList
+
+--[[
+	Table Members
+]]--
+TGU_CompactSwimlaneList.Name = "TGU-CompactSwimlaneList"
+TGU_CompactSwimlaneList.IsMocked = false
+TGU_CompactSwimlaneList.Swimlanes = {}
+
+--[[
+	Sets visibility of labels
+]]--
+function TGU_CompactSwimlaneList.RefreshList()
+	if (LOG_ACTIVE) then _logger:logTrace("TGU_CompactSwimlaneList.RefreshList") end
+
+    -- Check all swimlanes
+    for i,swimlane in ipairs(TGU_CompactSwimlaneList.Swimlanes) do
+        TGU_CompactSwimlaneList.ClearPlayersFromSwimlane(swimlane)
+	end
+end
+
+--[[
+	Sorts swimlane
+]]--
+function TGU_CompactSwimlaneList.SortSwimlane(swimlane)
+	if (LOG_ACTIVE) then _logger:logTrace("TGU_CompactSwimlaneList.SortSwimlane") end
+
+    -- Comparer
+    function compare(playerLeft, playerRight)
+        if (playerLeft.RelativeUltimate == playerRight.RelativeUltimate) then
+            return playerLeft.PingTag < playerRight.PingTag
+        else
+            return playerLeft.RelativeUltimate > playerRight.RelativeUltimate
+        end
+    end
+
+    table.sort(swimlane.Players, compare)
+
+    -- Sort by name
+
+    -- Update sorted swimlane list
+    for i,swimlanePlayer in ipairs(swimlane.Players) do
+        TGU_CompactSwimlaneList.UpdateListRow(swimlane.SwimlaneControl:GetNamedChild("Row" .. i), swimlanePlayer)
+    end
+end
+
+--[[
+	Updates list row
+]]--
+function TGU_CompactSwimlaneList.UpdateListRow(row, player)
+	if (LOG_ACTIVE) then
+        _logger:logTrace("TGU_CompactSwimlaneList.UpdateListRow")
+    end
+
+    local playerName = player.PlayerName
+    local nameLength = string.len(playerName)
+
+    if (nameLength > 6) then
+        playerName = string.sub(playerName, 0, 5) .. ".."
+    end
+
+    row:GetNamedChild("SenderNameValueLabel"):SetText(playerName)
+    row:GetNamedChild("RelativeUltimateStatusBar"):SetValue(player.RelativeUltimate)
+
+	if (player.IsPlayerDead) then
+        -- Dead Color
+        row:GetNamedChild("SenderNameValueLabel"):SetColor(0.5, 0.5, 0.5, 0.8)
+        row:GetNamedChild("RelativeUltimateStatusBar"):SetColor(0.8, 0.03, 0.03, 0.7)
+    elseif (player.RelativeUltimate == 100) then
+		-- Ready Color
+        row:GetNamedChild("SenderNameValueLabel"):SetColor(1, 1, 1, 1)
+        row:GetNamedChild("RelativeUltimateStatusBar"):SetColor(0.03, 0.7, 0.03, 0.7)
+	else
+		-- Inprogress Color
+        row:GetNamedChild("SenderNameValueLabel"):SetColor(1, 1, 1, 0.8)
+        row:GetNamedChild("RelativeUltimateStatusBar"):SetColor(0.03, 0.03, 0.7, 0.7)
+	end
+
+    row:SetHidden(false)
+end
+
+--[[
+	Updates list row
+]]--
+function TGU_CompactSwimlaneList.UpdatePlayer(player)
+	if (LOG_ACTIVE) then
+        _logger:logTrace("TGU_CompactSwimlaneList.UpdatePlayer")
+    end
+
+	if (player) then
+        local swimLane = TGU_CompactSwimlaneList.GetSwimLane(player.UltimateGroup.GroupAbilityId)
+
+        if (swimLane) then
+            local row = TGU_CompactSwimlaneList.GetSwimLaneRow(swimLane, player.PlayerName)
+
+            -- Update timestamp
+            if (row ~= nil) then
+                for i,swimlanePlayer in ipairs(swimLane.Players) do
+		            if (swimlanePlayer.PlayerName == player.PlayerName) then
+                        swimlanePlayer.LastMapPingTimestamp = GetTimeStamp()
+                        swimlanePlayer.IsPlayerDead = player.IsPlayerDead
+                        swimlanePlayer.RelativeUltimate = player.RelativeUltimate
+                        break
+                    end
+	            end
+            else
+                -- Add new player
+                local nextFreeRow = 1
+
+                for i,player in ipairs(swimLane.Players) do
+		            nextFreeRow = nextFreeRow + 1
+	            end
+
+                if (nextFreeRow <= ROWS) then
+                    if (LOG_ACTIVE) then
+                        _logger:logDebug("TGU_CompactSwimlaneList.UpdatePlayer, add player " .. tostring(player.PlayerName) .. " to row " .. tostring(nextFreeRow))
+                    end
+
+                    player.LastMapPingTimestamp = GetTimeStamp()
+                    swimLane.Players[nextFreeRow] = player
+                    row = swimLane.SwimlaneControl:GetNamedChild("Row" .. nextFreeRow)
+                else
+                    if (LOG_ACTIVE) then _logger:logDebug("TGU_CompactSwimlaneList.UpdatePlayer, too much players for one swimlane " .. tostring(nextFreeRow)) end
+                end
+            end
+
+            -- Only update if player in a row
+            if (row ~= nil) then
+                if (TGU_SettingsHandler.SavedVariables.IsSortingActive) then
+                    -- Sort swimlane with all players
+                    TGU_CompactSwimlaneList.SortSwimlane(swimLane)
+                else
+                    -- Directly update row with player
+                    TGU_CompactSwimlaneList.UpdateListRow(row, player)
+                end
+            end
+        else
+            if (LOG_ACTIVE) then _logger:logDebug("TGU_CompactSwimlaneList.UpdatePlayer, swimlane not found for ultimategroup " .. tostring(ultimateGroup.GroupName)) end
+        end
+	end
+end
+
+--[[
+	Get swimlane from current SwimLanes
+]]--
+function TGU_CompactSwimlaneList.GetSwimLane(ultimateGroupId)
+    if (LOG_ACTIVE) then
+        _logger:logTrace("TGU_CompactSwimlaneList.GetSwimLane")
+        _logger:logDebug("ultimateGroupId", ultimateGroupId)
+    end
+
+    if (ultimateGroupId ~= 0) then
+        for i,swimLane in ipairs(TGU_CompactSwimlaneList.Swimlanes) do
+		    if (swimLane.UltimateGroupId == ultimateGroupId) then
+                return swimLane
+            end
+	    end
+
+        if (LOG_ACTIVE) then _logger:logDebug("TGU_CompactSwimlaneList.GetSwimLane, swimLane not found " .. tostring(ultimateGroupId)) end
+        return nil
+    else
+        _logger:logError("TGU_CompactSwimlaneList.GetSwimLane, ultimateGroupId is 0")
+        return nil
+    end
+end
+
+--[[
+	Get Player Row from current players in swimlane
+]]--
+function TGU_CompactSwimlaneList.GetSwimLaneRow(swimLane, playerName)
+    if (LOG_ACTIVE) then
+        _logger:logTrace("TGU_CompactSwimlaneList.GetSwimLaneRow")
+        _logger:logDebug("swimLane ID", swimLane.Id)
+    end
+
+    if (swimLane) then
+        for i,player in ipairs(swimLane.Players) do
+            if (LOG_ACTIVE) then _logger:logDebug(player.PlayerName .. " == " .. playerName) end
+		    if (player.PlayerName == playerName) then
+                return swimLane.SwimlaneControl:GetNamedChild("Row" .. i)
+            end
+	    end
+
+        if (LOG_ACTIVE) then _logger:logDebug("TGU_CompactSwimlaneList.GetSwimLane, player not found " .. tostring(playerName)) end
+        return nil
+    else
+        _logger:logError("TGU_CompactSwimlaneList.GetSwimLane, swimLane is nil")
+        return nil
+    end
+end
+
+--[[
+	Clears all players in swimlane
+]]--
+function TGU_CompactSwimlaneList.ClearPlayersFromSwimlane(swimlane)
+    if (LOG_ACTIVE) then
+        _logger:logTrace("TGU_CompactSwimlaneList.ClearPlayersFromSwimlane")
+        _logger:logDebug("swimlane ID", swimlane.Id)
+    end
+
+    if (swimlane) then
+        for i=1, ROWS, 1 do
+            local row = swimlane.SwimlaneControl:GetNamedChild("Row" .. i)
+            local swimlanePlayer = swimlane.Players[i]
+
+            if (swimlanePlayer ~= nil) then
+                local isPlayerNotGrouped = IsUnitGrouped(swimlanePlayer.PingTag) == false
+
+                if (TGU_CompactSwimlaneList.IsMocked) then
+                    isPlayerNotGrouped = false
+                end
+
+                local isPlayerTimedOut = (GetTimeStamp() - swimlanePlayer.LastMapPingTimestamp) > TIMEOUT
+                local isPlayerUltimateNotCorrect = swimlane.UltimateGroupId ~= swimlanePlayer.UltimateGroup.GroupAbilityId
+
+                if (isPlayerNotGrouped or isPlayerTimedOut or isPlayerUltimateNotCorrect) then
+                    if (LOG_ACTIVE) then _logger:logDebug("Player invalid, hide row: " .. tostring(i)) end
+
+                    row:SetHidden(true)
+                    table.remove(swimlane.Players, i)
+                end
+            else
+                if (LOG_ACTIVE) then _logger:logDebug("Row empty, hide: " .. tostring(i)) end
+
+                row:SetHidden(true)
+            end
+        end
+    end
+end
+
+--[[
+	SetControlMovable sets the Movable and MouseEnabled flag in UI elements
+]]--
+function TGU_CompactSwimlaneList.SetControlMovable(isMovable)
+    if (LOG_ACTIVE) then
+        _logger:logTrace("TGU_CompactSwimlaneList.SetControlMovable")
+        _logger:logDebug("isMovable", isMovable)
+    end
+
+    _control:GetNamedChild("MovableControl"):SetHidden(isMovable == false)
+
+    _control:SetMovable(isMovable)
+	_control:SetMouseEnabled(isMovable)
+end
+
+--[[
+	RestorePosition sets TGU_CompactSwimlaneList on settings position
+]]--
+function TGU_CompactSwimlaneList.RestorePosition(posX, posY)
+    if (LOG_ACTIVE) then
+        _logger:logTrace("TGU_CompactSwimlaneList.RestorePosition")
+        _logger:logDebug("posX, posY", posX, posY)
+    end
+
+	_control:ClearAnchors()
+	_control:SetAnchor(TOPLEFT, GuiRoot, TOPLEFT, posX, posY)
+end
+
+--[[
+	OnTGU_CompactSwimlaneListMoveStop saves current TGU_CompactSwimlaneList position to settings
+]]--
+function TGU_CompactSwimlaneList.OnCompactSwimlaneListMoveStop()
+    if (LOG_ACTIVE) then _logger:logTrace("TGU_CompactSwimlaneList.OnCompactSwimlaneListMoveStop") end
+
+	local left = _control:GetLeft()
+	local top = _control:GetTop()
+
+    TGU_SettingsHandler.SavedVariables.PosX = left
+    TGU_SettingsHandler.SavedVariables.PosY = top
+
+    if (LOG_ACTIVE) then
+        _logger:logDebug("PosX, PosY", TGU_SettingsHandler.SavedVariables.PosX, TGU_SettingsHandler.SavedVariables.PosY)
+    end
+end
+
+--[[
+	SetControlHidden sets hidden on control
+]]--
+function TGU_CompactSwimlaneList.SetControlHidden(isHidden)
+    if (LOG_ACTIVE) then
+        _logger:logTrace("TGU_CompactSwimlaneList.SetControlHidden")
+        _logger:logDebug("isHidden", isHidden)
+    end
+
+    if (TGU_GroupHandler.IsGrouped) then
+        _control:SetHidden(isHidden)
+    else
+        _control:SetHidden(true)
+    end
+end
+
+--[[
+	SetControlActive sets hidden on control
+]]--
+function TGU_CompactSwimlaneList.SetControlActive()
+    if (LOG_ACTIVE) then
+        _logger:logTrace("TGU_CompactSwimlaneList.SetControlActive")
+    end
+
+    local isHidden = TGU_SettingsHandler.IsCompactSwimlaneListVisible() == false
+    if (LOG_ACTIVE) then _logger:logDebug("isHidden", isHidden) end
+
+    TGU_CompactSwimlaneList.SetControlHidden(isHidden or CurrentHudHiddenState())
+
+    if (isHidden) then
+        -- Start timeout timer
+	    EVENT_MANAGER:UnregisterForUpdate(TGU_CompactSwimlaneList.Name)
+
+        CALLBACK_MANAGER:UnregisterCallback(TGU_GROUP_CHANGED, TGU_CompactSwimlaneList.RefreshList)
+        CALLBACK_MANAGER:UnregisterCallback(TGU_PLAYER_DATA_CHANGED, TGU_CompactSwimlaneList.UpdatePlayer)
+        CALLBACK_MANAGER:UnregisterCallback(TGU_MOVABLE_CHANGED, TGU_CompactSwimlaneList.SetControlMovable)
+        CALLBACK_MANAGER:UnregisterCallback(TGU_SWIMLANE_ULTIMATE_GROUP_ID_CHANGED, TGU_CompactSwimlaneList.SetSwimlaneUltimate)
+        CALLBACK_MANAGER:UnregisterCallback(TUI_HUD_HIDDEN_STATE_CHANGED, TGU_CompactSwimlaneList.SetControlHidden)
+
+        -- Invis
+        TGU_CompactSwimlaneList.SetControlHidden(isHidden)
+    else
+        TGU_CompactSwimlaneList.SetControlMovable(TGU_SettingsHandler.SavedVariables.Movable)
+        TGU_CompactSwimlaneList.RestorePosition(TGU_SettingsHandler.SavedVariables.PosX, TGU_SettingsHandler.SavedVariables.PosY)
+
+        -- Start timeout timer
+	    EVENT_MANAGER:RegisterForUpdate(TGU_CompactSwimlaneList.Name, REFRESHRATE, TGU_CompactSwimlaneList.RefreshList)
+
+        CALLBACK_MANAGER:RegisterCallback(TGU_GROUP_CHANGED, TGU_CompactSwimlaneList.RefreshList)
+        CALLBACK_MANAGER:RegisterCallback(TGU_PLAYER_DATA_CHANGED, TGU_CompactSwimlaneList.UpdatePlayer)
+        CALLBACK_MANAGER:RegisterCallback(TGU_MOVABLE_CHANGED, TGU_CompactSwimlaneList.SetControlMovable)
+        CALLBACK_MANAGER:RegisterCallback(TGU_SWIMLANE_ULTIMATE_GROUP_ID_CHANGED, TGU_CompactSwimlaneList.SetSwimlaneUltimate)
+        CALLBACK_MANAGER:RegisterCallback(TUI_HUD_HIDDEN_STATE_CHANGED, TGU_CompactSwimlaneList.SetControlHidden)
+    end
+end
+
+--[[
+	OnSwimlaneHeaderClicked called on header clicked
+]]--
+function TGU_CompactSwimlaneList.OnSwimlaneHeaderClicked(button, swimlaneId)
+    if (LOG_ACTIVE) then
+        _logger:logTrace("TGU_CompactSwimlaneList.OnSwimlaneHeaderClicked")
+        _logger:logDebug("swimlaneId", swimlaneId)
+    end
+
+    if (button ~= nil) then
+        CALLBACK_MANAGER:RegisterCallback(TGU_SET_ULTIMATE_GROUP, TGU_CompactSwimlaneList.OnSetUltimateGroup)
+        CALLBACK_MANAGER:FireCallbacks(TGU_SHOW_ULTIMATE_GROUP_MENU, button, swimlaneId)
+    else
+        _logger:logError("TGU_CompactSwimlaneList.OnSwimlaneHeaderClicked, button nil")
+    end
+end
+
+--[[
+	OnSetUltimateGroup called on header clicked
+]]--
+function TGU_CompactSwimlaneList.OnSetUltimateGroup(group, swimlaneId)
+    if (LOG_ACTIVE) then
+        _logger:logTrace("TGU_CompactSwimlaneList.OnSetUltimateGroup")
+        _logger:logDebug("group.GroupName, swimlaneId", group.GroupName, swimlaneId)
+    end
+
+    CALLBACK_MANAGER:UnregisterCallback(TGU_SET_ULTIMATE_GROUP, TGU_CompactSwimlaneList.OnSetUltimateGroup)
+
+    if (group ~= nil and swimlaneId ~= nil and swimlaneId >= 1 and swimlaneId <= 6) then
+        TGU_SettingsHandler.SetSwimlaneUltimateGroupIdSettings(swimlaneId, group)
+    else
+        _logger:logError("TGU_UltimateGroupMenu.ShowUltimateGroupMenu, group nil or swimlaneId invalid")
+    end
+end
+
+--[[
+	SetSwimlaneUltimate sets the swimlane header icon in base of ultimateGroupId
+]]--
+function TGU_CompactSwimlaneList.SetSwimlaneUltimate(swimlaneId, ultimateGroup)
+    if (LOG_ACTIVE) then
+        _logger:logTrace("TGU_CompactSwimlaneList.SetSwimlaneUltimate")
+        _logger:logDebug("ultimateGroup.GroupName, swimlaneId", ultimateGroup.GroupName, swimlaneId)
+    end
+
+    local swimlaneObject = TGU_CompactSwimlaneList.Swimlanes[swimlaneId]
+    local iconControl = swimlaneObject.SwimlaneControl:GetNamedChild("Header"):GetNamedChild("SelectorButtonControl"):GetNamedChild("Icon")
+
+    if (ultimateGroup ~= nil and iconControl ~= nil) then
+        iconControl:SetTexture(GetAbilityIcon(ultimateGroup.GroupAbilityId))
+
+        swimlaneObject.UltimateGroupId = ultimateGroup.GroupAbilityId
+        TGU_CompactSwimlaneList.ClearPlayersFromSwimlane(swimlaneObject)
+    else
+        _logger:logError("TGU_CompactSwimlaneList.SetSwimlaneUltimateIcon, icon is " .. tostring(icon) .. ";" .. tostring(iconControl) .. ";" .. tostring(ultimateGroup))
+    end
+end
+
+--[[
+	CreateCompactSwimlaneListHeaders creates swimlane list headers
+]]--
+function TGU_CompactSwimlaneList.CreateCompactSwimlaneListHeaders()
+    if (LOG_ACTIVE) then _logger:logTrace("TGU_CompactSwimlaneList.CreateCompactSwimlaneListHeaders") end
+
+	for i=1, SWIMLANES, 1 do
+        local ultimateGroupId = TGU_SettingsHandler.SavedVariables.SwimlaneUltimateGroupIds[i]
+        local ultimateGroup = TGU_UltimateGroupHandler.GetUltimateGroupByAbilityId(ultimateGroupId)
+
+        local swimlaneControlName = "Swimlane" .. tostring(i)
+        local swimlaneControl = _control:GetNamedChild(swimlaneControlName)
+
+        -- Add button
+        local button = swimlaneControl:GetNamedChild("Header"):GetNamedChild("SelectorButtonControl"):GetNamedChild("Button")
+        button:SetHandler("OnClicked", function() TGU_CompactSwimlaneList.OnSwimlaneHeaderClicked(button, i) end)
+
+        local swimLane = {}
+        swimLane.Id = i
+        swimLane.SwimlaneControl = swimlaneControl
+        swimLane.Players = {}
+
+        if (ultimateGroup ~= nil) then
+            if (LOG_ACTIVE) then
+                _logger:logDebug("Create Swimlane", i)
+                _logger:logDebug("ultimateGroup.GroupName", ultimateGroup.GroupName)
+                _logger:logDebug("swimlaneControlName", swimlaneControlName)
+            end
+
+            local icon = swimlaneControl:GetNamedChild("Header"):GetNamedChild("SelectorButtonControl"):GetNamedChild("Icon")
+            icon:SetTexture(GetAbilityIcon(ultimateGroup.GroupAbilityId))
+
+            swimLane.UltimateGroupId = ultimateGroup.GroupAbilityId
+        else
+            _logger:logError("TGU_CompactSwimlaneList.CreateCompactSwimlaneListHeaders, ultimateGroup nil.")
+        end
+
+        TGU_CompactSwimlaneList.CreateCompactSwimlaneListRows(swimlaneControl)
+	    TGU_CompactSwimlaneList.Swimlanes[i] = swimLane
+	end
+end
+
+--[[
+	CreateCompactSwimlaneListRows creates swimlane lsit rows
+]]--
+function TGU_CompactSwimlaneList.CreateCompactSwimlaneListRows(swimlaneControl)
+    if (LOG_ACTIVE) then _logger:logTrace("TGU_CompactSwimlaneList.CreateCompactSwimlaneListRows") end
+
+    if (swimlaneControl ~= nil) then
+	    for i=1, ROWS, 1 do
+		    local row = CreateControlFromVirtual("$(parent)Row", swimlaneControl, "CompactGroupUltimateSwimlaneRow", i)
+            if (LOG_ACTIVE) then _logger:logDebug("Row created " .. row:GetName()) end
+
+		    row:SetHidden(true) -- initial not visible
+
+		    if (i == 1) then
+                row:SetAnchor(TOPLEFT, swimlaneControl, TOPLEFT, 0, 50)
+            elseif (i == 5) then -- Fix pixelbug, Why the hell ZOS?!
+                row:SetAnchor(TOPLEFT, lastRow, BOTTOMLEFT, 0, 0)
+            else
+				row:SetAnchor(TOPLEFT, lastRow, BOTTOMLEFT, 0, -1)
+			end
+
+		    lastRow = row
+	    end
+    else
+        _logger:logError("TGU_CompactSwimlaneList.CreateCompactSwimlaneListRows, swimlaneControl nil.")
+    end
+end
+
+--[[
+	Initialize initializes TGU_CompactSwimlaneList
+]]--
+function TGU_CompactSwimlaneList.Initialize(logger, isMocked)
+    if (LOG_ACTIVE) then
+        logger:logTrace("TGU_CompactSwimlaneList.Initialize")
+    end
+
+    _logger = logger
+    _control = TGU_CompactSwimlaneListControl
+
+    TGU_CompactSwimlaneList.IsMocked = isMocked
+
+    TGU_CompactSwimlaneList.CreateCompactSwimlaneListHeaders()
+
+    CALLBACK_MANAGER:RegisterCallback(TGU_STYLE_CHANGED, TGU_CompactSwimlaneList.SetControlActive)
+    CALLBACK_MANAGER:RegisterCallback(TGU_IS_ZONE_CHANGED, TGU_CompactSwimlaneList.SetControlActive)
+    CALLBACK_MANAGER:RegisterCallback(TGU_UNIT_GROUPED_CHANGED, TGU_CompactSwimlaneList.SetControlActive)
 end
\ No newline at end of file
diff --git a/TaosGroupTools/ui/CompactSwimlaneList.xml b/TaosGroupTools/ui/CompactSwimlaneList.xml
index 2e23311..2b7c426 100644
--- a/TaosGroupTools/ui/CompactSwimlaneList.xml
+++ b/TaosGroupTools/ui/CompactSwimlaneList.xml
@@ -1,40 +1,40 @@
-<GuiXml>
-	<Controls>
-		<TopLevelControl name="TGU_CompactSwimlaneListControl" hidden="true" layer="OVERLAY" level="1" allowBringToTop="false" mouseEnabled="true" movable="true" clampedToScreen="true">
-			<Anchor point="TOPLEFT" relativeTo="GuiRoot" relativePoint="TOPLEFT"/>
-			<Dimensions x="310" y="200" />
-			<OnMoveStop>TGU_CompactSwimlaneList:OnCompactSwimlaneListMoveStop()</OnMoveStop>
-
-			<Controls>
-        <Control name="$(parent)MovableControl" tier="PARENT" inherits="MovableBackdropControl">
-          <Anchor point="TOPLEFT"/>
-          <Anchor point="BOTTOMRIGHT" />
-        </Control>
-
-        <Control name="$(parent)Swimlane1" inherits="CompactGroupUltimateSwimlane">
-          <Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" offsetX="0" offsetY="0"/>
-        </Control>
-
-        <Control name="$(parent)Swimlane2" inherits="CompactGroupUltimateSwimlane">
-          <Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" offsetX="52" offsetY="0"/>
-        </Control>
-
-        <Control name="$(parent)Swimlane3" inherits="CompactGroupUltimateSwimlane">
-          <Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" offsetX="104" offsetY="0"/>
-        </Control>
-
-        <Control name="$(parent)Swimlane4" inherits="CompactGroupUltimateSwimlane">
-          <Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" offsetX="156" offsetY="0"/>
-        </Control>
-
-        <Control name="$(parent)Swimlane5" inherits="CompactGroupUltimateSwimlane">
-          <Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" offsetX="208" offsetY="0"/>
-        </Control>
-
-        <Control name="$(parent)Swimlane6" inherits="CompactGroupUltimateSwimlane">
-          <Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" offsetX="260" offsetY="0"/>
-        </Control>
-		  </Controls>
-	  </TopLevelControl>
-	</Controls>
+<GuiXml>
+	<Controls>
+		<TopLevelControl name="TGU_CompactSwimlaneListControl" hidden="true" layer="OVERLAY" level="1" allowBringToTop="false" mouseEnabled="true" movable="true" clampedToScreen="true">
+			<Anchor point="TOPLEFT" relativeTo="GuiRoot" relativePoint="TOPLEFT"/>
+			<Dimensions x="310" y="200" />
+			<OnMoveStop>TGU_CompactSwimlaneList:OnCompactSwimlaneListMoveStop()</OnMoveStop>
+
+			<Controls>
+        <Control name="$(parent)MovableControl" tier="PARENT" inherits="MovableBackdropControl">
+          <Anchor point="TOPLEFT"/>
+          <Anchor point="BOTTOMRIGHT" />
+        </Control>
+
+        <Control name="$(parent)Swimlane1" inherits="CompactGroupUltimateSwimlane">
+          <Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" offsetX="0" offsetY="0"/>
+        </Control>
+
+        <Control name="$(parent)Swimlane2" inherits="CompactGroupUltimateSwimlane">
+          <Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" offsetX="52" offsetY="0"/>
+        </Control>
+
+        <Control name="$(parent)Swimlane3" inherits="CompactGroupUltimateSwimlane">
+          <Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" offsetX="104" offsetY="0"/>
+        </Control>
+
+        <Control name="$(parent)Swimlane4" inherits="CompactGroupUltimateSwimlane">
+          <Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" offsetX="156" offsetY="0"/>
+        </Control>
+
+        <Control name="$(parent)Swimlane5" inherits="CompactGroupUltimateSwimlane">
+          <Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" offsetX="208" offsetY="0"/>
+        </Control>
+
+        <Control name="$(parent)Swimlane6" inherits="CompactGroupUltimateSwimlane">
+          <Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" offsetX="260" offsetY="0"/>
+        </Control>
+		  </Controls>
+	  </TopLevelControl>
+	</Controls>
 </GuiXml>
\ No newline at end of file
diff --git a/TaosGroupTools/ui/GroupUltimateSelector.lua b/TaosGroupTools/ui/GroupUltimateSelector.lua
index a8b52f0..3ef8028 100644
--- a/TaosGroupTools/ui/GroupUltimateSelector.lua
+++ b/TaosGroupTools/ui/GroupUltimateSelector.lua
@@ -1,187 +1,187 @@
---[[
-	Addon: Taos Group Ultimate
-	Author: TProg Taonnor
-	Created by @Taonnor
-]]--
-
---[[
-	Local variables
-]]--
-local LOG_ACTIVE = false
-
-local _logger = nil
-local _control = nil
-
---[[
-	Table GroupUltimateSelector
-]]--
-TGU_GroupUltimateSelector = {}
-TGU_GroupUltimateSelector.__index = TGU_GroupUltimateSelector
-
---[[
-	Table Members
-]]--
-
---[[
-	SetUltimateIcon sets the button icon in base of staticUltimateID
-]]--
-function TGU_GroupUltimateSelector.SetUltimateIcon(staticUltimateID)
-    if (LOG_ACTIVE) then
-        _logger:logTrace("TGU_GroupUltimateSelector.SetUltimateIcon")
-        _logger:logDebug("staticUltimateID", staticUltimateID)
-    end
-
-    local icon = "/esoui/art/icons/icon_missing.dds"
-
-    if (staticUltimateID ~= 0) then
-        icon = GetAbilityIcon(staticUltimateID)
-    end
-
-    local iconControl = _control:GetNamedChild("SelectorButtonControl"):GetNamedChild("Icon")
-
-    if (icon ~= nil and iconControl ~= nil) then
-        iconControl:SetTexture(icon)
-    else
-        _logger:logError("TGU_GroupUltimateSelector.SetUltimateIcon, icon is " .. tostring(icon) .. "; iconControl is " .. tostring(iconControl))
-    end
-end
-
---[[
-	SetControlMovable sets the Movable and MouseEnabled flag in UI elements
-]]--
-function TGU_GroupUltimateSelector.SetControlMovable(isMovable)
-    if (LOG_ACTIVE) then
-        _logger:logTrace("TGU_GroupUltimateSelector.SetControlMovable")
-        _logger:logDebug("isMovable", isMovable)
-    end
-
-    _control:GetNamedChild("MovableControl"):SetHidden(isMovable == false)
-
-    _control:SetMovable(isMovable)
-	_control:SetMouseEnabled(isMovable)
-end
-
---[[
-	RestorePosition sets TGU_GroupUltimateSelector on settings position
-]]--
-function TGU_GroupUltimateSelector.RestorePosition(posX, posY)
-    if (LOG_ACTIVE) then
-        _logger:logTrace("TGU_GroupUltimateSelector.RestorePosition")
-        _logger:logDebug("posX, posY", posX, posY)
-    end
-
-	_control:ClearAnchors()
-	_control:SetAnchor(TOPLEFT, GuiRoot, TOPLEFT, posX, posY)
-end
-
---[[
-	OnTGU_GroupUltimateSelectorMoveStop saves current TGU_GroupUltimateSelector position to settings
-]]--
-function TGU_GroupUltimateSelector.OnGroupUltimateSelectorMoveStop()
-    if (LOG_ACTIVE) then _logger:logTrace("TGU_GroupUltimateSelector.OnGroupUltimateSelectorMoveStop") end
-
-	local left = _control:GetLeft()
-	local top = _control:GetTop()
-
-    TGU_SettingsHandler.SavedVariables.SelectorPosX = left
-    TGU_SettingsHandler.SavedVariables.SelectorPosY = top
-
-    if (LOG_ACTIVE) then
-        _logger:logDebug("PosX, PosY", TGU_SettingsHandler.SavedVariables.SelectorPosX, TGU_SettingsHandler.SavedVariables.SelectorPosY)
-    end
-end
-
---[[
-	OnGroupUltimateSelectorClicked shows ultimate group menu
-]]--
-function TGU_GroupUltimateSelector.OnGroupUltimateSelectorClicked()
-    if (LOG_ACTIVE) then _logger:logTrace("TGU_GroupUltimateSelector.OnGroupUltimateSelectorClicked") end
-
-    local button = _control:GetNamedChild("SelectorButtonControl"):GetNamedChild("Button")
-
-    if (button ~= nil) then
-        CALLBACK_MANAGER:RegisterCallback(TGU_SET_ULTIMATE_GROUP, TGU_GroupUltimateSelector.OnSetUltimateGroup)
-        CALLBACK_MANAGER:FireCallbacks(TGU_SHOW_ULTIMATE_GROUP_MENU, button)
-    else
-        _logger:logError("TGU_GroupUltimateSelector.OnGroupUltimateSelectorClicked, button nil")
-    end
-end
-
---[[
-	OnSetUltimateGroup sets ultimate group for button
-]]--
-function TGU_GroupUltimateSelector.OnSetUltimateGroup(group)
-    if (LOG_ACTIVE) then
-        _logger:logTrace("TGU_GroupUltimateSelector.OnSetUltimateGroup")
-        _logger:logDebug("group.GroupName", group.GroupName)
-    end
-
-    CALLBACK_MANAGER:UnregisterCallback(TGU_SET_ULTIMATE_GROUP, TGU_GroupUltimateSelector.OnSetUltimateGroup)
-
-    if (group ~= nil) then
-        TGU_SettingsHandler.SetStaticUltimateIDSettings(group.GroupAbilityId)
-    else
-        _logger:logError("TGU_UltimateGroupMenu.ShowUltimateGroupMenu, group nil")
-    end
-end
-
---[[
-	SetControlHidden sets hidden on control
-]]--
-function TGU_GroupUltimateSelector.SetControlHidden(isHidden)
-    if (LOG_ACTIVE) then
-        _logger:logTrace("TGU_GroupUltimateSelector.SetControlHidden")
-        _logger:logDebug("isHidden", isHidden)
-    end
-
-    if (TGU_GroupHandler.IsGrouped) then
-        _control:SetHidden(isHidden)
-    else
-        _control:SetHidden(true)
-    end
-end
-
---[[
-	SetControlActive activates/deactivates control
-]]--
-function TGU_GroupUltimateSelector.SetControlActive()
-    if (LOG_ACTIVE) then
-        _logger:logTrace("TGU_GroupUltimateSelector.SetControlActive")
-    end
-
-    local isHidden = TGU_SettingsHandler.IsControlsVisible() == false
-    if (LOG_ACTIVE) then _logger:logDebug("isHidden", isHidden) end
-
-    TGU_GroupUltimateSelector.SetControlHidden(isHidden or CurrentHudHiddenState())
-
-    if (isHidden) then
-        CALLBACK_MANAGER:UnregisterCallback(TGU_MOVABLE_CHANGED, TGU_GroupUltimateSelector.SetControlMovable)
-        CALLBACK_MANAGER:UnregisterCallback(TGU_STATIC_ULTIMATE_ID_CHANGED, TGU_GroupUltimateSelector.SetUltimateIcon)
-        CALLBACK_MANAGER:UnregisterCallback(TUI_HUD_HIDDEN_STATE_CHANGED, TGU_GroupUltimateSelector.SetControlHidden)
-    else
-        TGU_GroupUltimateSelector.SetControlMovable(TGU_SettingsHandler.SavedVariables.Movable)
-        TGU_GroupUltimateSelector.RestorePosition(TGU_SettingsHandler.SavedVariables.SelectorPosX, TGU_SettingsHandler.SavedVariables.SelectorPosY)
-        TGU_GroupUltimateSelector.SetUltimateIcon(TGU_SettingsHandler.SavedVariables.StaticUltimateID)
-
-        CALLBACK_MANAGER:RegisterCallback(TGU_MOVABLE_CHANGED, TGU_GroupUltimateSelector.SetControlMovable)
-        CALLBACK_MANAGER:RegisterCallback(TGU_STATIC_ULTIMATE_ID_CHANGED, TGU_GroupUltimateSelector.SetUltimateIcon)
-        CALLBACK_MANAGER:RegisterCallback(TUI_HUD_HIDDEN_STATE_CHANGED, TGU_GroupUltimateSelector.SetControlHidden)
-    end
-end
-
---[[
-	Initialize initializes TGU_GroupUltimateSelector
-]]--
-function TGU_GroupUltimateSelector.Initialize(logger)
-    if (LOG_ACTIVE) then
-        logger:logTrace("TGU_GroupUltimateSelector.Initialize")
-    end
-
-    _logger = logger
-    _control = TGU_UltimateSelectorControl
-
-    TGU_GroupUltimateSelector.SetUltimateIcon(staticUltimateID)
-
-    CALLBACK_MANAGER:RegisterCallback(TGU_IS_ZONE_CHANGED, TGU_GroupUltimateSelector.SetControlActive)
-    CALLBACK_MANAGER:RegisterCallback(TGU_UNIT_GROUPED_CHANGED, TGU_GroupUltimateSelector.SetControlActive)
+--[[
+	Addon: Taos Group Ultimate
+	Author: TProg Taonnor
+	Created by @Taonnor
+]]--
+
+--[[
+	Local variables
+]]--
+local LOG_ACTIVE = false
+
+local _logger = nil
+local _control = nil
+
+--[[
+	Table GroupUltimateSelector
+]]--
+TGU_GroupUltimateSelector = {}
+TGU_GroupUltimateSelector.__index = TGU_GroupUltimateSelector
+
+--[[
+	Table Members
+]]--
+
+--[[
+	SetUltimateIcon sets the button icon in base of staticUltimateID
+]]--
+function TGU_GroupUltimateSelector.SetUltimateIcon(staticUltimateID)
+    if (LOG_ACTIVE) then
+        _logger:logTrace("TGU_GroupUltimateSelector.SetUltimateIcon")
+        _logger:logDebug("staticUltimateID", staticUltimateID)
+    end
+
+    local icon = "/esoui/art/icons/icon_missing.dds"
+
+    if (staticUltimateID ~= 0) then
+        icon = GetAbilityIcon(staticUltimateID)
+    end
+
+    local iconControl = _control:GetNamedChild("SelectorButtonControl"):GetNamedChild("Icon")
+
+    if (icon ~= nil and iconControl ~= nil) then
+        iconControl:SetTexture(icon)
+    else
+        _logger:logError("TGU_GroupUltimateSelector.SetUltimateIcon, icon is " .. tostring(icon) .. "; iconControl is " .. tostring(iconControl))
+    end
+end
+
+--[[
+	SetControlMovable sets the Movable and MouseEnabled flag in UI elements
+]]--
+function TGU_GroupUltimateSelector.SetControlMovable(isMovable)
+    if (LOG_ACTIVE) then
+        _logger:logTrace("TGU_GroupUltimateSelector.SetControlMovable")
+        _logger:logDebug("isMovable", isMovable)
+    end
+
+    _control:GetNamedChild("MovableControl"):SetHidden(isMovable == false)
+
+    _control:SetMovable(isMovable)
+	_control:SetMouseEnabled(isMovable)
+end
+
+--[[
+	RestorePosition sets TGU_GroupUltimateSelector on settings position
+]]--
+function TGU_GroupUltimateSelector.RestorePosition(posX, posY)
+    if (LOG_ACTIVE) then
+        _logger:logTrace("TGU_GroupUltimateSelector.RestorePosition")
+        _logger:logDebug("posX, posY", posX, posY)
+    end
+
+	_control:ClearAnchors()
+	_control:SetAnchor(TOPLEFT, GuiRoot, TOPLEFT, posX, posY)
+end
+
+--[[
+	OnTGU_GroupUltimateSelectorMoveStop saves current TGU_GroupUltimateSelector position to settings
+]]--
+function TGU_GroupUltimateSelector.OnGroupUltimateSelectorMoveStop()
+    if (LOG_ACTIVE) then _logger:logTrace("TGU_GroupUltimateSelector.OnGroupUltimateSelectorMoveStop") end
+
+	local left = _control:GetLeft()
+	local top = _control:GetTop()
+
+    TGU_SettingsHandler.SavedVariables.SelectorPosX = left
+    TGU_SettingsHandler.SavedVariables.SelectorPosY = top
+
+    if (LOG_ACTIVE) then
+        _logger:logDebug("PosX, PosY", TGU_SettingsHandler.SavedVariables.SelectorPosX, TGU_SettingsHandler.SavedVariables.SelectorPosY)
+    end
+end
+
+--[[
+	OnGroupUltimateSelectorClicked shows ultimate group menu
+]]--
+function TGU_GroupUltimateSelector.OnGroupUltimateSelectorClicked()
+    if (LOG_ACTIVE) then _logger:logTrace("TGU_GroupUltimateSelector.OnGroupUltimateSelectorClicked") end
+
+    local button = _control:GetNamedChild("SelectorButtonControl"):GetNamedChild("Button")
+
+    if (button ~= nil) then
+        CALLBACK_MANAGER:RegisterCallback(TGU_SET_ULTIMATE_GROUP, TGU_GroupUltimateSelector.OnSetUltimateGroup)
+        CALLBACK_MANAGER:FireCallbacks(TGU_SHOW_ULTIMATE_GROUP_MENU, button)
+    else
+        _logger:logError("TGU_GroupUltimateSelector.OnGroupUltimateSelectorClicked, button nil")
+    end
+end
+
+--[[
+	OnSetUltimateGroup sets ultimate group for button
+]]--
+function TGU_GroupUltimateSelector.OnSetUltimateGroup(group)
+    if (LOG_ACTIVE) then
+        _logger:logTrace("TGU_GroupUltimateSelector.OnSetUltimateGroup")
+        _logger:logDebug("group.GroupName", group.GroupName)
+    end
+
+    CALLBACK_MANAGER:UnregisterCallback(TGU_SET_ULTIMATE_GROUP, TGU_GroupUltimateSelector.OnSetUltimateGroup)
+
+    if (group ~= nil) then
+        TGU_SettingsHandler.SetStaticUltimateIDSettings(group.GroupAbilityId)
+    else
+        _logger:logError("TGU_UltimateGroupMenu.ShowUltimateGroupMenu, group nil")
+    end
+end
+
+--[[
+	SetControlHidden sets hidden on control
+]]--
+function TGU_GroupUltimateSelector.SetControlHidden(isHidden)
+    if (LOG_ACTIVE) then
+        _logger:logTrace("TGU_GroupUltimateSelector.SetControlHidden")
+        _logger:logDebug("isHidden", isHidden)
+    end
+
+    if (TGU_GroupHandler.IsGrouped) then
+        _control:SetHidden(isHidden)
+    else
+        _control:SetHidden(true)
+    end
+end
+
+--[[
+	SetControlActive activates/deactivates control
+]]--
+function TGU_GroupUltimateSelector.SetControlActive()
+    if (LOG_ACTIVE) then
+        _logger:logTrace("TGU_GroupUltimateSelector.SetControlActive")
+    end
+
+    local isHidden = TGU_SettingsHandler.IsControlsVisible() == false
+    if (LOG_ACTIVE) then _logger:logDebug("isHidden", isHidden) end
+
+    TGU_GroupUltimateSelector.SetControlHidden(isHidden or CurrentHudHiddenState())
+
+    if (isHidden) then
+        CALLBACK_MANAGER:UnregisterCallback(TGU_MOVABLE_CHANGED, TGU_GroupUltimateSelector.SetControlMovable)
+        CALLBACK_MANAGER:UnregisterCallback(TGU_STATIC_ULTIMATE_ID_CHANGED, TGU_GroupUltimateSelector.SetUltimateIcon)
+        CALLBACK_MANAGER:UnregisterCallback(TUI_HUD_HIDDEN_STATE_CHANGED, TGU_GroupUltimateSelector.SetControlHidden)
+    else
+        TGU_GroupUltimateSelector.SetControlMovable(TGU_SettingsHandler.SavedVariables.Movable)
+        TGU_GroupUltimateSelector.RestorePosition(TGU_SettingsHandler.SavedVariables.SelectorPosX, TGU_SettingsHandler.SavedVariables.SelectorPosY)
+        TGU_GroupUltimateSelector.SetUltimateIcon(TGU_SettingsHandler.SavedVariables.StaticUltimateID)
+
+        CALLBACK_MANAGER:RegisterCallback(TGU_MOVABLE_CHANGED, TGU_GroupUltimateSelector.SetControlMovable)
+        CALLBACK_MANAGER:RegisterCallback(TGU_STATIC_ULTIMATE_ID_CHANGED, TGU_GroupUltimateSelector.SetUltimateIcon)
+        CALLBACK_MANAGER:RegisterCallback(TUI_HUD_HIDDEN_STATE_CHANGED, TGU_GroupUltimateSelector.SetControlHidden)
+    end
+end
+
+--[[
+	Initialize initializes TGU_GroupUltimateSelector
+]]--
+function TGU_GroupUltimateSelector.Initialize(logger)
+    if (LOG_ACTIVE) then
+        logger:logTrace("TGU_GroupUltimateSelector.Initialize")
+    end
+
+    _logger = logger
+    _control = TGU_UltimateSelectorControl
+
+    TGU_GroupUltimateSelector.SetUltimateIcon(staticUltimateID)
+
+    CALLBACK_MANAGER:RegisterCallback(TGU_IS_ZONE_CHANGED, TGU_GroupUltimateSelector.SetControlActive)
+    CALLBACK_MANAGER:RegisterCallback(TGU_UNIT_GROUPED_CHANGED, TGU_GroupUltimateSelector.SetControlActive)
 end
\ No newline at end of file
diff --git a/TaosGroupTools/ui/GroupUltimateSelector.xml b/TaosGroupTools/ui/GroupUltimateSelector.xml
index e24dbc9..6d5807d 100644
--- a/TaosGroupTools/ui/GroupUltimateSelector.xml
+++ b/TaosGroupTools/ui/GroupUltimateSelector.xml
@@ -1,37 +1,37 @@
-<GuiXml>
-	<Controls>
-		<TopLevelControl name="TGU_UltimateSelectorControl" hidden="true" layer="OVERLAY" level="1" allowBringToTop="false" mouseEnabled="true" movable="true" clampedToScreen="true">
-			<Anchor point="TOPLEFT" relativeTo="GuiRoot" relativePoint="TOPLEFT"/>
-			<Dimensions x="75" y="75" />
-			<OnMoveStop>TGU_GroupUltimateSelector:OnGroupUltimateSelectorMoveStop()</OnMoveStop>
-
-			<Controls>
-        <Control name="$(parent)MovableControl" tier="PARENT" inherits="MovableBackdropControl">
-          <Anchor point="TOPLEFT"/>
-          <Anchor point="BOTTOMRIGHT" />
-        </Control>
-
-        <Control name="$(parent)SelectorButtonControl">
-          <Dimensions x="50" y="50"/>
-          <Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" offsetX="12.5" offsetY="12.5"/>
-          <Controls>
-            <Button name="$(parent)Button"
-                    ButtonState="BSTATE_NORMAL"
-                    tier="1">
-              <Dimensions x="50" y="50"/>
-              <Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT"/>
-              <Textures normal="/esoui/art/actionbar/abilityframe64_up.dds"
-                        pressed="/esoui/art/actionbar/abilityframe64_down.dds"
-                        mouseOver="/TaosGroupUltimate/textures/hover.dds"/>
-              <OnClicked>TGU_GroupUltimateSelector:OnGroupUltimateSelectorClicked()</OnClicked>
-            </Button>
-            <Texture name="$(parent)Icon" tier="0">
-              <Dimensions x="48" y="48"/>
-              <Anchor point="CENTER" relativeTo="$(parent)" relativePoint="CENTER"/>
-            </Texture>
-          </Controls>
-        </Control>
-		  </Controls>
-	  </TopLevelControl>
-	</Controls>
+<GuiXml>
+	<Controls>
+		<TopLevelControl name="TGU_UltimateSelectorControl" hidden="true" layer="OVERLAY" level="1" allowBringToTop="false" mouseEnabled="true" movable="true" clampedToScreen="true">
+			<Anchor point="TOPLEFT" relativeTo="GuiRoot" relativePoint="TOPLEFT"/>
+			<Dimensions x="75" y="75" />
+			<OnMoveStop>TGU_GroupUltimateSelector:OnGroupUltimateSelectorMoveStop()</OnMoveStop>
+
+			<Controls>
+        <Control name="$(parent)MovableControl" tier="PARENT" inherits="MovableBackdropControl">
+          <Anchor point="TOPLEFT"/>
+          <Anchor point="BOTTOMRIGHT" />
+        </Control>
+
+        <Control name="$(parent)SelectorButtonControl">
+          <Dimensions x="50" y="50"/>
+          <Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" offsetX="12.5" offsetY="12.5"/>
+          <Controls>
+            <Button name="$(parent)Button"
+                    ButtonState="BSTATE_NORMAL"
+                    tier="1">
+              <Dimensions x="50" y="50"/>
+              <Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT"/>
+              <Textures normal="/esoui/art/actionbar/abilityframe64_up.dds"
+                        pressed="/esoui/art/actionbar/abilityframe64_down.dds"
+                        mouseOver="/TaosGroupUltimate/textures/hover.dds"/>
+              <OnClicked>TGU_GroupUltimateSelector:OnGroupUltimateSelectorClicked()</OnClicked>
+            </Button>
+            <Texture name="$(parent)Icon" tier="0">
+              <Dimensions x="48" y="48"/>
+              <Anchor point="CENTER" relativeTo="$(parent)" relativePoint="CENTER"/>
+            </Texture>
+          </Controls>
+        </Control>
+		  </Controls>
+	  </TopLevelControl>
+	</Controls>
 </GuiXml>
\ No newline at end of file
diff --git a/TaosGroupTools/ui/SettingsWindow.lua b/TaosGroupTools/ui/SettingsWindow.lua
index bf8e6f1..31e0b21 100644
--- a/TaosGroupTools/ui/SettingsWindow.lua
+++ b/TaosGroupTools/ui/SettingsWindow.lua
@@ -1,119 +1,119 @@
---[[
-	Addon: Taos Group Ultimate
-	Author: TProg Taonnor
-	Created by @Taonnor
-]]--
-
---[[
-	Local variables
-]]--
-local LOG_ACTIVE = false
-
---[[
-	Table SettingsWindow
-]]--
-TGU_SettingsWindow = {}
-TGU_SettingsWindow.__index = TGU_SettingsWindow
-
---[[
-	Table Members
-]]--
-TGU_SettingsWindow.MainMenuName = "TaosGroupUltimateSettingsMainMenu"
-
---[[
-	Initialize creates settings window
-]]--
-function TGU_SettingsWindow.Initialize(logger, major, minor, patch)
-    if (LOG_ACTIVE) then
-        logger:logTrace("TGU_SettingsWindow.Initialize")
-        logger:logDebug("major, minor, patch", major, minor, patch)
-    end
-
-    local styleChoices = {
-        [1] = GetString(TGU_OPTIONS_STYLE_SIMPLE),
-        [2] = GetString(TGU_OPTIONS_STYLE_SWIM),
-        [3] = GetString(TGU_OPTIONS_STYLE_SHORT_SWIM),
-    }
-
-	local panelData = {
-		type = "panel",
-		name = "Taos Group Ultimate",
-		author = "TProg Taonnor",
-		version = major .. "." .. minor .. "." .. patch,
-		slashCommand = "/taosGroupUltimate",
-		registerForDefaults = true
-	}
-
-	local optionsData = {
-		[1] = {
-			type = "header",
-			name = GetString(TGU_OPTIONS_HEADER),
-		},
-		[2] = {
-			type = "checkbox",
-			name = GetString(TGU_OPTIONS_DRAG_LABEL),
-			tooltip = GetString(TGU_OPTIONS_DRAG_TOOLTIP),
-			getFunc =
-               function()
-                   return TGU_SettingsHandler.SavedVariables.Movable
-               end,
-			setFunc =
-               function(value)
-                   TGU_SettingsHandler.SetMovableSettings(value)
-			   end,
-			default = TGU_SettingsHandler.Default.Movable
-		},
-        [3] = {
-			type = "checkbox",
-			name = GetString(TGU_OPTIONS_ONLY_AVA_LABEL),
-			tooltip = GetString(TGU_OPTIONS_ONLY_AVA_TOOLTIP),
-			getFunc =
-               function()
-                   return TGU_SettingsHandler.SavedVariables.OnlyAva
-               end,
-			setFunc =
-               function(value)
-                   TGU_SettingsHandler.SetOnlyAvaSettings(value)
-			   end,
-			default = TGU_SettingsHandler.Default.OnlyAva
-		},
-        [4] = {
-			type = "checkbox",
-			name = GetString(TGU_OPTIONS_USE_SORTING_LABEL),
-			tooltip = GetString(TGU_OPTIONS_USE_SORTING_TOOLTIP),
-			getFunc =
-               function()
-                   return TGU_SettingsHandler.SavedVariables.IsSortingActive
-               end,
-			setFunc =
-               function(value)
-                   TGU_SettingsHandler.SetIsSortingActiveSettings(value)
-			   end,
-			default = TGU_SettingsHandler.Default.IsSortingActive
-		},
-        [5] = {
-			type = "dropdown",
-			name = GetString(TGU_OPTIONS_STYLE_LABEL),
-			tooltip = GetString(TGU_OPTIONS_STYLE_TOOLTIP),
-            choices = styleChoices,
-			getFunc =
-               function()
-                   return styleChoices[TGU_SettingsHandler.SavedVariables.Style]
-               end,
-			setFunc =
-               function(value)
-                   for index, name in ipairs(styleChoices) do
-                      if (name == value) then
-                         TGU_SettingsHandler.SetStyleSettings(index)
-                         break
-                      end
-                   end
-			   end,
-			default = styleChoices[TGU_SettingsHandler.Default.Style]
-		},
-	}
-
-	local LAM = LibStub("LibAddonMenu-2.0")
-	LAM:RegisterAddonPanel(TGU_SettingsWindow.MainMenuName, panelData)
-	LAM:RegisterOptionControls(TGU_SettingsWindow.MainMenuName, optionsData)
+--[[
+	Addon: Taos Group Ultimate
+	Author: TProg Taonnor
+	Created by @Taonnor
+]]--
+
+--[[
+	Local variables
+]]--
+local LOG_ACTIVE = false
+
+--[[
+	Table SettingsWindow
+]]--
+TGU_SettingsWindow = {}
+TGU_SettingsWindow.__index = TGU_SettingsWindow
+
+--[[
+	Table Members
+]]--
+TGU_SettingsWindow.MainMenuName = "TaosGroupUltimateSettingsMainMenu"
+
+--[[
+	Initialize creates settings window
+]]--
+function TGU_SettingsWindow.Initialize(logger, major, minor, patch)
+    if (LOG_ACTIVE) then
+        logger:logTrace("TGU_SettingsWindow.Initialize")
+        logger:logDebug("major, minor, patch", major, minor, patch)
+    end
+
+    local styleChoices = {
+        [1] = GetString(TGU_OPTIONS_STYLE_SIMPLE),
+        [2] = GetString(TGU_OPTIONS_STYLE_SWIM),
+        [3] = GetString(TGU_OPTIONS_STYLE_SHORT_SWIM),
+    }
+
+	local panelData = {
+		type = "panel",
+		name = "Taos Group Ultimate",
+		author = "TProg Taonnor",
+		version = major .. "." .. minor .. "." .. patch,
+		slashCommand = "/taosGroupUltimate",
+		registerForDefaults = true
+	}
+
+	local optionsData = {
+		[1] = {
+			type = "header",
+			name = GetString(TGU_OPTIONS_HEADER),
+		},
+		[2] = {
+			type = "checkbox",
+			name = GetString(TGU_OPTIONS_DRAG_LABEL),
+			tooltip = GetString(TGU_OPTIONS_DRAG_TOOLTIP),
+			getFunc =
+               function()
+                   return TGU_SettingsHandler.SavedVariables.Movable
+               end,
+			setFunc =
+               function(value)
+                   TGU_SettingsHandler.SetMovableSettings(value)
+			   end,
+			default = TGU_SettingsHandler.Default.Movable
+		},
+        [3] = {
+			type = "checkbox",
+			name = GetString(TGU_OPTIONS_ONLY_AVA_LABEL),
+			tooltip = GetString(TGU_OPTIONS_ONLY_AVA_TOOLTIP),
+			getFunc =
+               function()
+                   return TGU_SettingsHandler.SavedVariables.OnlyAva
+               end,
+			setFunc =
+               function(value)
+                   TGU_SettingsHandler.SetOnlyAvaSettings(value)
+			   end,
+			default = TGU_SettingsHandler.Default.OnlyAva
+		},
+        [4] = {
+			type = "checkbox",
+			name = GetString(TGU_OPTIONS_USE_SORTING_LABEL),
+			tooltip = GetString(TGU_OPTIONS_USE_SORTING_TOOLTIP),
+			getFunc =
+               function()
+                   return TGU_SettingsHandler.SavedVariables.IsSortingActive
+               end,
+			setFunc =
+               function(value)
+                   TGU_SettingsHandler.SetIsSortingActiveSettings(value)
+			   end,
+			default = TGU_SettingsHandler.Default.IsSortingActive
+		},
+        [5] = {
+			type = "dropdown",
+			name = GetString(TGU_OPTIONS_STYLE_LABEL),
+			tooltip = GetString(TGU_OPTIONS_STYLE_TOOLTIP),
+            choices = styleChoices,
+			getFunc =
+               function()
+                   return styleChoices[TGU_SettingsHandler.SavedVariables.Style]
+               end,
+			setFunc =
+               function(value)
+                   for index, name in ipairs(styleChoices) do
+                      if (name == value) then
+                         TGU_SettingsHandler.SetStyleSettings(index)
+                         break
+                      end
+                   end
+			   end,
+			default = styleChoices[TGU_SettingsHandler.Default.Style]
+		},
+	}
+
+	local LAM = LibStub("LibAddonMenu-2.0")
+	LAM:RegisterAddonPanel(TGU_SettingsWindow.MainMenuName, panelData)
+	LAM:RegisterOptionControls(TGU_SettingsWindow.MainMenuName, optionsData)
 end
\ No newline at end of file
diff --git a/TaosGroupTools/ui/SimpleList.lua b/TaosGroupTools/ui/SimpleList.lua
index f21af8f..fbf0fb8 100644
--- a/TaosGroupTools/ui/SimpleList.lua
+++ b/TaosGroupTools/ui/SimpleList.lua
@@ -1,321 +1,321 @@
---[[
-	Addon: Taos Group Ultimate
-	Author: TProg Taonnor
-	Created by @Taonnor
-]]--
-
---[[
-	Local variables
-]]--
-local LOG_ACTIVE = false
-
-local REFRESHRATE = 1000 -- ms; RegisterForUpdate is in miliseconds
-local TIMEOUT = 4 -- s; GetTimeStamp() is in seconds
-
-local _logger = nil
-local _control = nil
-local _players = {}
-
---[[
-	Table TGU_SimpleList
-]]--
-TGU_SimpleList = {}
-TGU_SimpleList.__index = TGU_SimpleList
-
---[[
-	Table Members
-]]--
-TGU_SimpleList.IsMocked = false
-
---[[
-	Sets visibility of labels
-]]--
-function TGU_SimpleList.RefreshList()
-	if (LOG_ACTIVE) then _logger:logTrace("TGU_SimpleList.RefreshList") end
-
-    for i=1, GROUP_SIZE_MAX, 1 do
-        local row = TGU_SimpleListControlContainerScrollChild:GetNamedChild("Row" .. i)
-        local listPlayer = _players[i]
-
-        if (listPlayer ~= nil) then
-            local isPlayerNotGrouped = IsUnitGrouped(listPlayer.PingTag) == false
-
-            if (TGU_SimpleList.IsMocked) then
-                isPlayerNotGrouped = false
-            end
-
-            local isPlayerTimedOut = (GetTimeStamp() - listPlayer.LastMapPingTimestamp) > TIMEOUT
-
-            if (isPlayerNotGrouped or isPlayerTimedOut) then
-                if (LOG_ACTIVE) then _logger:logDebug("Player invalid, hide row: " .. tostring(i)) end
-
-                row:SetHidden(true)
-                table.remove(_players, i)
-            end
-        else
-            if (LOG_ACTIVE) then _logger:logDebug("Row empty, hide: " .. tostring(i)) end
-            row:SetHidden(true)
-        end
-    end
-
-	if (TGU_SettingsHandler.SavedVariables.IsSortingActive) then
-		-- Sort list with all players
-		TGU_SimpleList.SortList()
-	end
-end
-
---[[
-	Sorts swimlane
-]]--
-function TGU_SimpleList.SortList()
-	if (LOG_ACTIVE) then _logger:logTrace("TGU_SimpleList.SortList") end
-
-    -- Comparer
-    function compare(playerLeft, playerRight)
-        if (playerLeft.RelativeUltimate == playerRight.RelativeUltimate) then
-            return playerLeft.PingTag < playerRight.PingTag
-        else
-            return playerLeft.RelativeUltimate > playerRight.RelativeUltimate
-        end
-    end
-
-    table.sort(_players, compare)
-
-    -- Update sorted swimlane list
-    for i,listPlayer in ipairs(_players) do
-        TGU_SimpleList.UpdateListRow(TGU_SimpleListControlContainerScrollChild:GetNamedChild("Row" .. i), listPlayer)
-    end
-end
-
---[[
-	Updates list row
-]]--
-function TGU_SimpleList.UpdateListRow(row, player)
-	if (LOG_ACTIVE) then
-        _logger:logTrace("TGU_SimpleList.UpdateListRow")
-    end
-
-    local localizedUltimateName = zo_strformat(SI_ABILITY_TOOLTIP_NAME, player.UltimateName)
-    local nameLength = string.len(localizedUltimateName)
-
-    if (nameLength > 22) then
-        localizedUltimateName = string.sub(localizedUltimateName, 0, 22) .. "..."
-    end
-
-    row:GetNamedChild("SenderNameValueLabel"):SetText(player.PlayerName)
-	row:GetNamedChild("UltimateValueLabel"):SetText(localizedUltimateName)
-	row:GetNamedChild("ReadyValueLabel"):SetText(player.RelativeUltimate)
-
-	if (player.IsPlayerDead) then
-        row:GetNamedChild("SenderNameValueLabel"):SetColor(1.0, 0.0, 0.0, 1)
-		row:GetNamedChild("UltimateValueLabel"):SetColor(1.0, 0.0, 0.0, 1)
-		row:GetNamedChild("ReadyValueLabel"):SetColor(1.0, 0.0, 0.0, 1)
-    elseif (player.RelativeUltimate == 100) then
-		row:GetNamedChild("SenderNameValueLabel"):SetColor(0.0, 1.0, 0.0, 1)
-		row:GetNamedChild("UltimateValueLabel"):SetColor(0.0, 1.0, 0.0, 1)
-		row:GetNamedChild("ReadyValueLabel"):SetColor(0.0, 1.0, 0.0, 1)
-	else
-		row:GetNamedChild("SenderNameValueLabel"):SetColor(1.0, 1.0, 1.0, 1)
-		row:GetNamedChild("UltimateValueLabel"):SetColor(1.0, 1.0, 1.0, 1)
-		row:GetNamedChild("ReadyValueLabel"):SetColor(1.0, 1.0, 1.0, 1)
-	end
-
-    if (row:IsHidden()) then
-		row:SetHidden(false)
-	end
-end
-
---[[
-	Updates list row
-]]--
-function TGU_SimpleList.UpdatePlayer(player)
-	if (LOG_ACTIVE) then
-        _logger:logTrace("TGU_SimpleList.UpdatePlayer")
-    end
-
-	if (player) then
-        local row = nil
-
-        for i,listPlayer in ipairs(_players) do
-            if (LOG_ACTIVE) then _logger:logDebug(listPlayer.PlayerName .. " == " .. player.PlayerName) end
-		    if (listPlayer.PlayerName == player.PlayerName) then
-                row = TGU_SimpleListControlContainerScrollChild:GetNamedChild("Row" .. i)
-            end
-	    end
-
-        -- Update timestamp
-		if (row ~= nil) then
-            for i,listPlayer in ipairs(_players) do
-		        if (listPlayer.PlayerName == player.PlayerName) then
-                    listPlayer.LastMapPingTimestamp = GetTimeStamp()
-                    listPlayer.IsPlayerDead = player.IsPlayerDead
-                    listPlayer.RelativeUltimate = player.RelativeUltimate
-                    break
-                end
-	        end
-        else
-            -- Add new player
-            local nextFreeRow = 1
-
-            for i,player in ipairs(_players) do
-		        nextFreeRow = nextFreeRow + 1
-	        end
-
-            if (nextFreeRow <= GROUP_SIZE_MAX) then
-                if (LOG_ACTIVE) then
-                    _logger:logDebug("TGU_SimpleList.UpdatePlayer, add player " .. tostring(player.PlayerName) .. " to row " .. tostring(nextFreeRow))
-                end
-
-                player.LastMapPingTimestamp = GetTimeStamp()
-                _players[nextFreeRow] = player
-                row = TGU_SimpleListControlContainerScrollChild:GetNamedChild("Row" .. nextFreeRow)
-            else
-                if (LOG_ACTIVE) then _logger:logDebug("TGU_SimpleList.UpdatePlayer, too much players for list" .. tostring(nextFreeRow)) end
-            end
-        end
-
-        -- Only update if player in a row
-        if (row ~= nil) then
-            -- Directly update row with player, sorting will be triggered on RefreshList
-			TGU_SimpleList.UpdateListRow(row, player)
-        end
-    end
-end
-
---[[
-	SetControlMovable sets the Movable and MouseEnabled flag in UI elements
-]]--
-function TGU_SimpleList.SetControlMovable(isMovable)
-    if (LOG_ACTIVE) then
-        _logger:logTrace("TGU_SimpleList.SetControlMovable")
-        _logger:logDebug("isMovable", isMovable)
-    end
-
-    _control:GetNamedChild("MovableControl"):SetHidden(isMovable == false)
-
-    _control:SetMovable(isMovable)
-	_control:SetMouseEnabled(isMovable)
-end
-
---[[
-	RestorePosition sets TGU_SimpleList on settings position
-]]--
-function TGU_SimpleList.RestorePosition(posX, posY)
-    if (LOG_ACTIVE) then
-        _logger:logTrace("TGU_SimpleList.RestorePosition")
-        _logger:logDebug("posX, posY", posX, posY)
-    end
-
-	_control:ClearAnchors()
-	_control:SetAnchor(TOPLEFT, GuiRoot, TOPLEFT, posX, posY)
-end
-
---[[
-	OnSimpleListMoveStop saves current TGU_SimpleList position to settings
-]]--
-function TGU_SimpleList.OnSimpleListMoveStop()
-    if (LOG_ACTIVE) then _logger:logTrace("TGU_SimpleList.OnSimpleListMoveStop") end
-
-	local left = _control:GetLeft()
-	local top = _control:GetTop()
-
-    TGU_SettingsHandler.SavedVariables.PosX = left
-    TGU_SettingsHandler.SavedVariables.PosY = top
-
-    if (LOG_ACTIVE) then
-        _logger:logDebug("PosX, PosY", TGU_SettingsHandler.SavedVariables.PosX, TGU_SettingsHandler.SavedVariables.PosY)
-    end
-end
-
---[[
-	SetControlHidden sets hidden on control
-]]--
-function TGU_SimpleList.SetControlHidden(isHidden)
-    if (LOG_ACTIVE) then
-        _logger:logTrace("TGU_SimpleList.SetControlHidden")
-        _logger:logDebug("isHidden", isHidden)
-    end
-
-    if (TGU_GroupHandler.IsGrouped) then
-        _control:SetHidden(isHidden)
-    else
-        _control:SetHidden(true)
-    end
-end
-
---[[
-	SetControlActive sets hidden on control
-]]--
-function TGU_SimpleList.SetControlActive()
-    if (LOG_ACTIVE) then
-        _logger:logTrace("TGU_SimpleList.SetControlActive")
-    end
-
-    local isHidden = TGU_SettingsHandler.IsSimpleListVisible() == false
-    if (LOG_ACTIVE) then _logger:logDebug("isHidden", isHidden) end
-
-    TGU_SimpleList.SetControlHidden(isHidden or CurrentHudHiddenState())
-
-    if (isHidden) then
-		-- Stop timeout timer
-	    EVENT_MANAGER:UnregisterForUpdate(TGU_SimpleList.Name)
-
-        CALLBACK_MANAGER:UnregisterCallback(TGU_GROUP_CHANGED, TGU_SimpleList.RefreshList)
-        CALLBACK_MANAGER:UnregisterCallback(TGU_PLAYER_DATA_CHANGED, TGU_SimpleList.UpdatePlayer)
-        CALLBACK_MANAGER:UnregisterCallback(TGU_MOVABLE_CHANGED, TGU_SimpleList.SetControlMovable)
-        CALLBACK_MANAGER:UnregisterCallback(TUI_HUD_HIDDEN_STATE_CHANGED, TGU_SimpleList.SetControlHidden)
-    else
-        TGU_SimpleList.SetControlMovable(TGU_SettingsHandler.SavedVariables.Movable)
-        TGU_SimpleList.RestorePosition(TGU_SettingsHandler.SavedVariables.PosX, TGU_SettingsHandler.SavedVariables.PosY)
-
-		-- Start timeout timer
-	    EVENT_MANAGER:RegisterForUpdate(TGU_SimpleList.Name, REFRESHRATE, TGU_SimpleList.RefreshList)
-
-        CALLBACK_MANAGER:RegisterCallback(TGU_GROUP_CHANGED, TGU_SimpleList.RefreshList)
-        CALLBACK_MANAGER:RegisterCallback(TGU_PLAYER_DATA_CHANGED, TGU_SimpleList.UpdatePlayer)
-        CALLBACK_MANAGER:RegisterCallback(TGU_MOVABLE_CHANGED, TGU_SimpleList.SetControlMovable)
-        CALLBACK_MANAGER:RegisterCallback(TUI_HUD_HIDDEN_STATE_CHANGED, TGU_SimpleList.SetControlHidden)
-    end
-end
-
---[[
-	CreateSimpleListRows creates simple list rows
-]]--
-function TGU_SimpleList.CreateSimpleListRows()
-    if (LOG_ACTIVE) then _logger:logTrace("TGU_SimpleList.CreateSimpleListRows") end
-
-	for i=1, GROUP_SIZE_MAX, 1 do
-		local row = CreateControlFromVirtual("$(parent)Row", TGU_SimpleListControlContainerScrollChild, "GroupUltimateSimpleListRow", i)
-        if (LOG_ACTIVE) then _logger:logDebug("Row created " .. row:GetName()) end
-
-		row:SetHidden(true) -- initial not visible
-
-		if i == 1 then
-            row:SetAnchor(TOPLEFT, TGU_SimpleListControlContainerScrollChild, TOPLEFT, 0, 0)
-        else
-            row:SetAnchor(TOP, lastRow, BOTTOM, 0, 0)
-        end
-
-		lastRow = row
-	end
-end
-
---[[
-	Initialize initializes TGU_SimpleList
-]]--
-function TGU_SimpleList.Initialize(logger, isMocked)
-    if (LOG_ACTIVE) then
-        logger:logTrace("TGU_SimpleList.Initialize")
-    end
-
-    _logger = logger
-    _control = TGU_SimpleListControl
-
-    TGU_SimpleList.IsMocked = isMocked
-
-    TGU_SimpleList.CreateSimpleListRows()
-
-    CALLBACK_MANAGER:RegisterCallback(TGU_STYLE_CHANGED, TGU_SimpleList.SetControlActive)
-    CALLBACK_MANAGER:RegisterCallback(TGU_IS_ZONE_CHANGED, TGU_SimpleList.SetControlActive)
-    CALLBACK_MANAGER:RegisterCallback(TGU_UNIT_GROUPED_CHANGED, TGU_SimpleList.SetControlActive)
+--[[
+	Addon: Taos Group Ultimate
+	Author: TProg Taonnor
+	Created by @Taonnor
+]]--
+
+--[[
+	Local variables
+]]--
+local LOG_ACTIVE = false
+
+local REFRESHRATE = 1000 -- ms; RegisterForUpdate is in miliseconds
+local TIMEOUT = 4 -- s; GetTimeStamp() is in seconds
+
+local _logger = nil
+local _control = nil
+local _players = {}
+
+--[[
+	Table TGU_SimpleList
+]]--
+TGU_SimpleList = {}
+TGU_SimpleList.__index = TGU_SimpleList
+
+--[[
+	Table Members
+]]--
+TGU_SimpleList.IsMocked = false
+
+--[[
+	Sets visibility of labels
+]]--
+function TGU_SimpleList.RefreshList()
+	if (LOG_ACTIVE) then _logger:logTrace("TGU_SimpleList.RefreshList") end
+
+    for i=1, GROUP_SIZE_MAX, 1 do
+        local row = TGU_SimpleListControlContainerScrollChild:GetNamedChild("Row" .. i)
+        local listPlayer = _players[i]
+
+        if (listPlayer ~= nil) then
+            local isPlayerNotGrouped = IsUnitGrouped(listPlayer.PingTag) == false
+
+            if (TGU_SimpleList.IsMocked) then
+                isPlayerNotGrouped = false
+            end
+
+            local isPlayerTimedOut = (GetTimeStamp() - listPlayer.LastMapPingTimestamp) > TIMEOUT
+
+            if (isPlayerNotGrouped or isPlayerTimedOut) then
+                if (LOG_ACTIVE) then _logger:logDebug("Player invalid, hide row: " .. tostring(i)) end
+
+                row:SetHidden(true)
+                table.remove(_players, i)
+            end
+        else
+            if (LOG_ACTIVE) then _logger:logDebug("Row empty, hide: " .. tostring(i)) end
+            row:SetHidden(true)
+        end
+    end
+
+	if (TGU_SettingsHandler.SavedVariables.IsSortingActive) then
+		-- Sort list with all players
+		TGU_SimpleList.SortList()
+	end
+end
+
+--[[
+	Sorts swimlane
+]]--
+function TGU_SimpleList.SortList()
+	if (LOG_ACTIVE) then _logger:logTrace("TGU_SimpleList.SortList") end
+
+    -- Comparer
+    function compare(playerLeft, playerRight)
+        if (playerLeft.RelativeUltimate == playerRight.RelativeUltimate) then
+            return playerLeft.PingTag < playerRight.PingTag
+        else
+            return playerLeft.RelativeUltimate > playerRight.RelativeUltimate
+        end
+    end
+
+    table.sort(_players, compare)
+
+    -- Update sorted swimlane list
+    for i,listPlayer in ipairs(_players) do
+        TGU_SimpleList.UpdateListRow(TGU_SimpleListControlContainerScrollChild:GetNamedChild("Row" .. i), listPlayer)
+    end
+end
+
+--[[
+	Updates list row
+]]--
+function TGU_SimpleList.UpdateListRow(row, player)
+	if (LOG_ACTIVE) then
+        _logger:logTrace("TGU_SimpleList.UpdateListRow")
+    end
+
+    local localizedUltimateName = zo_strformat(SI_ABILITY_TOOLTIP_NAME, player.UltimateName)
+    local nameLength = string.len(localizedUltimateName)
+
+    if (nameLength > 22) then
+        localizedUltimateName = string.sub(localizedUltimateName, 0, 22) .. "..."
+    end
+
+    row:GetNamedChild("SenderNameValueLabel"):SetText(player.PlayerName)
+	row:GetNamedChild("UltimateValueLabel"):SetText(localizedUltimateName)
+	row:GetNamedChild("ReadyValueLabel"):SetText(player.RelativeUltimate)
+
+	if (player.IsPlayerDead) then
+        row:GetNamedChild("SenderNameValueLabel"):SetColor(1.0, 0.0, 0.0, 1)
+		row:GetNamedChild("UltimateValueLabel"):SetColor(1.0, 0.0, 0.0, 1)
+		row:GetNamedChild("ReadyValueLabel"):SetColor(1.0, 0.0, 0.0, 1)
+    elseif (player.RelativeUltimate == 100) then
+		row:GetNamedChild("SenderNameValueLabel"):SetColor(0.0, 1.0, 0.0, 1)
+		row:GetNamedChild("UltimateValueLabel"):SetColor(0.0, 1.0, 0.0, 1)
+		row:GetNamedChild("ReadyValueLabel"):SetColor(0.0, 1.0, 0.0, 1)
+	else
+		row:GetNamedChild("SenderNameValueLabel"):SetColor(1.0, 1.0, 1.0, 1)
+		row:GetNamedChild("UltimateValueLabel"):SetColor(1.0, 1.0, 1.0, 1)
+		row:GetNamedChild("ReadyValueLabel"):SetColor(1.0, 1.0, 1.0, 1)
+	end
+
+    if (row:IsHidden()) then
+		row:SetHidden(false)
+	end
+end
+
+--[[
+	Updates list row
+]]--
+function TGU_SimpleList.UpdatePlayer(player)
+	if (LOG_ACTIVE) then
+        _logger:logTrace("TGU_SimpleList.UpdatePlayer")
+    end
+
+	if (player) then
+        local row = nil
+
+        for i,listPlayer in ipairs(_players) do
+            if (LOG_ACTIVE) then _logger:logDebug(listPlayer.PlayerName .. " == " .. player.PlayerName) end
+		    if (listPlayer.PlayerName == player.PlayerName) then
+                row = TGU_SimpleListControlContainerScrollChild:GetNamedChild("Row" .. i)
+            end
+	    end
+
+        -- Update timestamp
+		if (row ~= nil) then
+            for i,listPlayer in ipairs(_players) do
+		        if (listPlayer.PlayerName == player.PlayerName) then
+                    listPlayer.LastMapPingTimestamp = GetTimeStamp()
+                    listPlayer.IsPlayerDead = player.IsPlayerDead
+                    listPlayer.RelativeUltimate = player.RelativeUltimate
+                    break
+                end
+	        end
+        else
+            -- Add new player
+            local nextFreeRow = 1
+
+            for i,player in ipairs(_players) do
+		        nextFreeRow = nextFreeRow + 1
+	        end
+
+            if (nextFreeRow <= GROUP_SIZE_MAX) then
+                if (LOG_ACTIVE) then
+                    _logger:logDebug("TGU_SimpleList.UpdatePlayer, add player " .. tostring(player.PlayerName) .. " to row " .. tostring(nextFreeRow))
+                end
+
+                player.LastMapPingTimestamp = GetTimeStamp()
+                _players[nextFreeRow] = player
+                row = TGU_SimpleListControlContainerScrollChild:GetNamedChild("Row" .. nextFreeRow)
+            else
+                if (LOG_ACTIVE) then _logger:logDebug("TGU_SimpleList.UpdatePlayer, too much players for list" .. tostring(nextFreeRow)) end
+            end
+        end
+
+        -- Only update if player in a row
+        if (row ~= nil) then
+            -- Directly update row with player, sorting will be triggered on RefreshList
+			TGU_SimpleList.UpdateListRow(row, player)
+        end
+    end
+end
+
+--[[
+	SetControlMovable sets the Movable and MouseEnabled flag in UI elements
+]]--
+function TGU_SimpleList.SetControlMovable(isMovable)
+    if (LOG_ACTIVE) then
+        _logger:logTrace("TGU_SimpleList.SetControlMovable")
+        _logger:logDebug("isMovable", isMovable)
+    end
+
+    _control:GetNamedChild("MovableControl"):SetHidden(isMovable == false)
+
+    _control:SetMovable(isMovable)
+	_control:SetMouseEnabled(isMovable)
+end
+
+--[[
+	RestorePosition sets TGU_SimpleList on settings position
+]]--
+function TGU_SimpleList.RestorePosition(posX, posY)
+    if (LOG_ACTIVE) then
+        _logger:logTrace("TGU_SimpleList.RestorePosition")
+        _logger:logDebug("posX, posY", posX, posY)
+    end
+
+	_control:ClearAnchors()
+	_control:SetAnchor(TOPLEFT, GuiRoot, TOPLEFT, posX, posY)
+end
+
+--[[
+	OnSimpleListMoveStop saves current TGU_SimpleList position to settings
+]]--
+function TGU_SimpleList.OnSimpleListMoveStop()
+    if (LOG_ACTIVE) then _logger:logTrace("TGU_SimpleList.OnSimpleListMoveStop") end
+
+	local left = _control:GetLeft()
+	local top = _control:GetTop()
+
+    TGU_SettingsHandler.SavedVariables.PosX = left
+    TGU_SettingsHandler.SavedVariables.PosY = top
+
+    if (LOG_ACTIVE) then
+        _logger:logDebug("PosX, PosY", TGU_SettingsHandler.SavedVariables.PosX, TGU_SettingsHandler.SavedVariables.PosY)
+    end
+end
+
+--[[
+	SetControlHidden sets hidden on control
+]]--
+function TGU_SimpleList.SetControlHidden(isHidden)
+    if (LOG_ACTIVE) then
+        _logger:logTrace("TGU_SimpleList.SetControlHidden")
+        _logger:logDebug("isHidden", isHidden)
+    end
+
+    if (TGU_GroupHandler.IsGrouped) then
+        _control:SetHidden(isHidden)
+    else
+        _control:SetHidden(true)
+    end
+end
+
+--[[
+	SetControlActive sets hidden on control
+]]--
+function TGU_SimpleList.SetControlActive()
+    if (LOG_ACTIVE) then
+        _logger:logTrace("TGU_SimpleList.SetControlActive")
+    end
+
+    local isHidden = TGU_SettingsHandler.IsSimpleListVisible() == false
+    if (LOG_ACTIVE) then _logger:logDebug("isHidden", isHidden) end
+
+    TGU_SimpleList.SetControlHidden(isHidden or CurrentHudHiddenState())
+
+    if (isHidden) then
+		-- Stop timeout timer
+	    EVENT_MANAGER:UnregisterForUpdate(TGU_SimpleList.Name)
+
+        CALLBACK_MANAGER:UnregisterCallback(TGU_GROUP_CHANGED, TGU_SimpleList.RefreshList)
+        CALLBACK_MANAGER:UnregisterCallback(TGU_PLAYER_DATA_CHANGED, TGU_SimpleList.UpdatePlayer)
+        CALLBACK_MANAGER:UnregisterCallback(TGU_MOVABLE_CHANGED, TGU_SimpleList.SetControlMovable)
+        CALLBACK_MANAGER:UnregisterCallback(TUI_HUD_HIDDEN_STATE_CHANGED, TGU_SimpleList.SetControlHidden)
+    else
+        TGU_SimpleList.SetControlMovable(TGU_SettingsHandler.SavedVariables.Movable)
+        TGU_SimpleList.RestorePosition(TGU_SettingsHandler.SavedVariables.PosX, TGU_SettingsHandler.SavedVariables.PosY)
+
+		-- Start timeout timer
+	    EVENT_MANAGER:RegisterForUpdate(TGU_SimpleList.Name, REFRESHRATE, TGU_SimpleList.RefreshList)
+
+        CALLBACK_MANAGER:RegisterCallback(TGU_GROUP_CHANGED, TGU_SimpleList.RefreshList)
+        CALLBACK_MANAGER:RegisterCallback(TGU_PLAYER_DATA_CHANGED, TGU_SimpleList.UpdatePlayer)
+        CALLBACK_MANAGER:RegisterCallback(TGU_MOVABLE_CHANGED, TGU_SimpleList.SetControlMovable)
+        CALLBACK_MANAGER:RegisterCallback(TUI_HUD_HIDDEN_STATE_CHANGED, TGU_SimpleList.SetControlHidden)
+    end
+end
+
+--[[
+	CreateSimpleListRows creates simple list rows
+]]--
+function TGU_SimpleList.CreateSimpleListRows()
+    if (LOG_ACTIVE) then _logger:logTrace("TGU_SimpleList.CreateSimpleListRows") end
+
+	for i=1, GROUP_SIZE_MAX, 1 do
+		local row = CreateControlFromVirtual("$(parent)Row", TGU_SimpleListControlContainerScrollChild, "GroupUltimateSimpleListRow", i)
+        if (LOG_ACTIVE) then _logger:logDebug("Row created " .. row:GetName()) end
+
+		row:SetHidden(true) -- initial not visible
+
+		if i == 1 then
+            row:SetAnchor(TOPLEFT, TGU_SimpleListControlContainerScrollChild, TOPLEFT, 0, 0)
+        else
+            row:SetAnchor(TOP, lastRow, BOTTOM, 0, 0)
+        end
+
+		lastRow = row
+	end
+end
+
+--[[
+	Initialize initializes TGU_SimpleList
+]]--
+function TGU_SimpleList.Initialize(logger, isMocked)
+    if (LOG_ACTIVE) then
+        logger:logTrace("TGU_SimpleList.Initialize")
+    end
+
+    _logger = logger
+    _control = TGU_SimpleListControl
+
+    TGU_SimpleList.IsMocked = isMocked
+
+    TGU_SimpleList.CreateSimpleListRows()
+
+    CALLBACK_MANAGER:RegisterCallback(TGU_STYLE_CHANGED, TGU_SimpleList.SetControlActive)
+    CALLBACK_MANAGER:RegisterCallback(TGU_IS_ZONE_CHANGED, TGU_SimpleList.SetControlActive)
+    CALLBACK_MANAGER:RegisterCallback(TGU_UNIT_GROUPED_CHANGED, TGU_SimpleList.SetControlActive)
 end
\ No newline at end of file
diff --git a/TaosGroupTools/ui/SimpleList.xml b/TaosGroupTools/ui/SimpleList.xml
index 835116f..7c4a45f 100644
--- a/TaosGroupTools/ui/SimpleList.xml
+++ b/TaosGroupTools/ui/SimpleList.xml
@@ -1,32 +1,32 @@
-<GuiXml>
-	<Controls>
-		<TopLevelControl name="TGU_SimpleListControl" hidden="true" layer="OVERLAY" level="1" allowBringToTop="false" mouseEnabled="true" movable="true" clampedToScreen="true">
-			<Anchor point="TOPLEFT" relativeTo="GuiRoot" relativePoint="TOPLEFT"/>
-			<Dimensions x="500" y="520" />
-			<OnMoveStop>TGU_SimpleList:OnSimpleListMoveStop()</OnMoveStop>
-
-			<Controls>
-        <Control name="$(parent)MovableControl" tier="PARENT" inherits="MovableBackdropControl">
-          <Anchor point="TOPLEFT"/>
-          <Anchor point="BOTTOMRIGHT" />
-        </Control>
-
-				<!-- Header -->
-				<Label name="$(parent)SenderNameHeaderLabel" inherits="GroupUltimateHeaderLabel" text="Name">
-					<Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" offsetX="0" offsetY="0"/>
-				</Label>
-				<Label name="$(parent)UltimateHeaderLabel" inherits="GroupUltimateHeaderLabel" text="Ultimate">
-					<Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" offsetX="200" offsetY="0"/>
-				</Label>
-				<Label name="$(parent)ReadyHeaderLabel" inherits="GroupUltimateHeaderLabel" text="Ready %">
-					<Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" offsetX="400" offsetY="0"/>
-				</Label>
-
-				<Control name="$(parent)Container" inherits="ZO_ScrollContainer">
-                    <Anchor point="TOPLEFT" offsetY="20" />
-                    <Anchor point="BOTTOMRIGHT" />
-                </Control>
-		  </Controls>
-	  </TopLevelControl>
-	</Controls>
+<GuiXml>
+	<Controls>
+		<TopLevelControl name="TGU_SimpleListControl" hidden="true" layer="OVERLAY" level="1" allowBringToTop="false" mouseEnabled="true" movable="true" clampedToScreen="true">
+			<Anchor point="TOPLEFT" relativeTo="GuiRoot" relativePoint="TOPLEFT"/>
+			<Dimensions x="500" y="520" />
+			<OnMoveStop>TGU_SimpleList:OnSimpleListMoveStop()</OnMoveStop>
+
+			<Controls>
+        <Control name="$(parent)MovableControl" tier="PARENT" inherits="MovableBackdropControl">
+          <Anchor point="TOPLEFT"/>
+          <Anchor point="BOTTOMRIGHT" />
+        </Control>
+
+				<!-- Header -->
+				<Label name="$(parent)SenderNameHeaderLabel" inherits="GroupUltimateHeaderLabel" text="Name">
+					<Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" offsetX="0" offsetY="0"/>
+				</Label>
+				<Label name="$(parent)UltimateHeaderLabel" inherits="GroupUltimateHeaderLabel" text="Ultimate">
+					<Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" offsetX="200" offsetY="0"/>
+				</Label>
+				<Label name="$(parent)ReadyHeaderLabel" inherits="GroupUltimateHeaderLabel" text="Ready %">
+					<Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" offsetX="400" offsetY="0"/>
+				</Label>
+
+				<Control name="$(parent)Container" inherits="ZO_ScrollContainer">
+                    <Anchor point="TOPLEFT" offsetY="20" />
+                    <Anchor point="BOTTOMRIGHT" />
+                </Control>
+		  </Controls>
+	  </TopLevelControl>
+	</Controls>
 </GuiXml>
\ No newline at end of file
diff --git a/TaosGroupTools/ui/SwimlaneList.lua b/TaosGroupTools/ui/SwimlaneList.lua
index f4f223d..e76667a 100644
--- a/TaosGroupTools/ui/SwimlaneList.lua
+++ b/TaosGroupTools/ui/SwimlaneList.lua
@@ -1,501 +1,501 @@
---[[
-	Addon: Taos Group Ultimate
-	Author: TProg Taonnor
-	Created by @Taonnor
-]]--
-
---[[
-	Local variables
-]]--
-local LOG_ACTIVE = false
-
-local SWIMLANES = 6
-local ROWS = 6
-local REFRESHRATE = 1000 -- ms; RegisterForUpdate is in miliseconds
-local TIMEOUT = 4 -- s; GetTimeStamp() is in seconds
-
-local _logger = nil
-local _control = nil
-
---[[
-	Table TGU_SwimlaneList
-]]--
-TGU_SwimlaneList = {}
-TGU_SwimlaneList.__index = TGU_SwimlaneList
-
---[[
-	Table Members
-]]--
-TGU_SwimlaneList.Name = "TGU-SwimlaneList"
-TGU_SwimlaneList.IsMocked = false
-TGU_SwimlaneList.Swimlanes = {}
-
---[[
-	Sets visibility of labels
-]]--
-function TGU_SwimlaneList.RefreshList()
-	if (LOG_ACTIVE) then _logger:logTrace("TGU_SwimlaneList.RefreshList") end
-
-    -- Check all swimlanes
-    for i,swimlane in ipairs(TGU_SwimlaneList.Swimlanes) do
-        TGU_SwimlaneList.ClearPlayersFromSwimlane(swimlane)
-	end
-end
-
---[[
-	Sorts swimlane
-]]--
-function TGU_SwimlaneList.SortSwimlane(swimlane)
-	if (LOG_ACTIVE) then _logger:logTrace("TGU_SwimlaneList.SortSwimlane") end
-
-    -- Comparer
-    function compare(playerLeft, playerRight)
-        if (playerLeft.RelativeUltimate == playerRight.RelativeUltimate) then
-            return playerLeft.PingTag < playerRight.PingTag
-        else
-            return playerLeft.RelativeUltimate > playerRight.RelativeUltimate
-        end
-    end
-
-    table.sort(swimlane.Players, compare)
-
-    -- Update sorted swimlane list
-    for i,swimlanePlayer in ipairs(swimlane.Players) do
-        TGU_SwimlaneList.UpdateListRow(swimlane.SwimlaneControl:GetNamedChild("Row" .. i), swimlanePlayer)
-    end
-end
-
---[[
-	Updates list row
-]]--
-function TGU_SwimlaneList.UpdateListRow(row, player)
-	if (LOG_ACTIVE) then
-        _logger:logTrace("TGU_SwimlaneList.UpdateListRow")
-    end
-
-    local playerName = player.PlayerName
-    local nameLength = string.len(playerName)
-
-    if (nameLength > 10) then
-        playerName = string.sub(playerName, 0, 10) .. "..."
-    end
-
-    row:GetNamedChild("SenderNameValueLabel"):SetText(playerName)
-    row:GetNamedChild("RelativeUltimateStatusBar"):SetValue(player.RelativeUltimate)
-
-	if (player.IsPlayerDead) then
-        -- Dead Color
-        row:GetNamedChild("SenderNameValueLabel"):SetColor(0.5, 0.5, 0.5, 0.8)
-        row:GetNamedChild("RelativeUltimateStatusBar"):SetColor(0.8, 0.03, 0.03, 0.7)
-    elseif (player.RelativeUltimate == 100) then
-		-- Ready Color
-        row:GetNamedChild("SenderNameValueLabel"):SetColor(1, 1, 1, 1)
-        row:GetNamedChild("RelativeUltimateStatusBar"):SetColor(0.03, 0.7, 0.03, 0.7)
-	else
-		-- Inprogress Color
-        row:GetNamedChild("SenderNameValueLabel"):SetColor(1, 1, 1, 0.8)
-        row:GetNamedChild("RelativeUltimateStatusBar"):SetColor(0.03, 0.03, 0.7, 0.7)
-	end
-
-    if (row:IsHidden()) then
-		row:SetHidden(false)
-	end
-end
-
---[[
-	Updates list player
-]]--
-function TGU_SwimlaneList.UpdatePlayer(player)
-	if (LOG_ACTIVE) then
-        _logger:logTrace("TGU_SwimlaneList.UpdatePlayer")
-    end
-
-	if (player) then
-        local swimLane = TGU_SwimlaneList.GetSwimLane(player.UltimateGroup.GroupAbilityId)
-
-        if (swimLane) then
-            local row = TGU_SwimlaneList.GetSwimLaneRow(swimLane, player.PlayerName)
-
-            -- Update player
-            if (row ~= nil) then
-                for i,swimlanePlayer in ipairs(swimLane.Players) do
-		            if (swimlanePlayer.PlayerName == player.PlayerName) then
-                        swimlanePlayer.LastMapPingTimestamp = GetTimeStamp()
-                        swimlanePlayer.IsPlayerDead = player.IsPlayerDead
-                        swimlanePlayer.RelativeUltimate = player.RelativeUltimate
-                        break
-                    end
-	            end
-            else
-                -- Add new player
-                local nextFreeRow = 1
-
-                for i,player in ipairs(swimLane.Players) do
-		            nextFreeRow = nextFreeRow + 1
-	            end
-
-                if (nextFreeRow <= ROWS) then
-                    if (LOG_ACTIVE) then
-                        _logger:logDebug("TGU_SwimlaneList.UpdatePlayer, add player " .. tostring(player.PlayerName) .. " to row " .. tostring(nextFreeRow))
-                    end
-
-                    player.LastMapPingTimestamp = GetTimeStamp()
-                    swimLane.Players[nextFreeRow] = player
-                    row = swimLane.SwimlaneControl:GetNamedChild("Row" .. nextFreeRow)
-                else
-                    if (LOG_ACTIVE) then _logger:logDebug("TGU_SwimlaneList.UpdatePlayer, too much players for one swimlane " .. tostring(nextFreeRow)) end
-                end
-            end
-
-            -- Only update if player in a row
-            if (row ~= nil) then
-                if (TGU_SettingsHandler.SavedVariables.IsSortingActive) then
-                    -- Sort swimlane with all players
-                    TGU_SwimlaneList.SortSwimlane(swimLane)
-                else
-                    -- Directly update row with player
-                    TGU_SwimlaneList.UpdateListRow(row, player)
-                end
-            end
-        else
-            if (LOG_ACTIVE) then _logger:logDebug("TGU_SwimlaneList.UpdatePlayer, swimlane not found for ultimategroup " .. tostring(ultimateGroup.GroupName)) end
-        end
-	end
-end
-
---[[
-	Get swimlane from current SwimLanes
-]]--
-function TGU_SwimlaneList.GetSwimLane(ultimateGroupId)
-    if (LOG_ACTIVE) then
-        _logger:logTrace("TGU_SwimlaneList.GetSwimLane")
-        _logger:logDebug("ultimateGroupId", ultimateGroupId)
-    end
-
-    if (ultimateGroupId ~= 0) then
-        for i,swimLane in ipairs(TGU_SwimlaneList.Swimlanes) do
-		    if (swimLane.UltimateGroupId == ultimateGroupId) then
-                return swimLane
-            end
-	    end
-
-        if (LOG_ACTIVE) then _logger:logDebug("TGU_SwimlaneList.GetSwimLane, swimLane not found " .. tostring(ultimateGroupId)) end
-        return nil
-    else
-        _logger:logError("TGU_SwimlaneList.GetSwimLane, ultimateGroupId is 0")
-        return nil
-    end
-end
-
---[[
-	Get Player Row from current players in swimlane
-]]--
-function TGU_SwimlaneList.GetSwimLaneRow(swimLane, playerName)
-    if (LOG_ACTIVE) then
-        _logger:logTrace("TGU_SwimlaneList.GetSwimLaneRow")
-        _logger:logDebug("swimLane ID", swimLane.Id)
-    end
-
-    if (swimLane) then
-        for i,player in ipairs(swimLane.Players) do
-            if (LOG_ACTIVE) then _logger:logDebug(player.PlayerName .. " == " .. playerName) end
-		    if (player.PlayerName == playerName) then
-                return swimLane.SwimlaneControl:GetNamedChild("Row" .. i)
-            end
-	    end
-
-        if (LOG_ACTIVE) then _logger:logDebug("TGU_SwimlaneList.GetSwimLane, player not found " .. tostring(playerName)) end
-        return nil
-    else
-        _logger:logError("TGU_SwimlaneList.GetSwimLane, swimLane is nil")
-        return nil
-    end
-end
-
---[[
-	Clears all players in swimlane
-]]--
-function TGU_SwimlaneList.ClearPlayersFromSwimlane(swimlane)
-    if (LOG_ACTIVE) then
-        _logger:logTrace("TGU_SwimlaneList.ClearPlayersFromSwimlane")
-        _logger:logDebug("swimlane ID", swimlane.Id)
-    end
-
-    if (swimlane) then
-        for i=1, ROWS, 1 do
-            local row = swimlane.SwimlaneControl:GetNamedChild("Row" .. i)
-            local swimlanePlayer = swimlane.Players[i]
-
-            if (swimlanePlayer ~= nil) then
-                local isPlayerNotGrouped = IsUnitGrouped(swimlanePlayer.PingTag) == false
-
-                if (TGU_SwimlaneList.IsMocked) then
-                    isPlayerNotGrouped = false
-                end
-
-                local isPlayerTimedOut = (GetTimeStamp() - swimlanePlayer.LastMapPingTimestamp) > TIMEOUT
-                local isPlayerUltimateNotCorrect = swimlane.UltimateGroupId ~= swimlanePlayer.UltimateGroup.GroupAbilityId
-
-                if (isPlayerNotGrouped or isPlayerTimedOut or isPlayerUltimateNotCorrect) then
-                    if (LOG_ACTIVE) then _logger:logDebug("Player invalid, hide row: " .. tostring(i)) end
-
-                    table.remove(swimlane.Players, i)
-                    row:SetHidden(true)
-                end
-            else
-                if (LOG_ACTIVE) then _logger:logDebug("Row empty, hide: " .. tostring(i)) end
-                row:SetHidden(true)
-            end
-        end
-    end
-end
-
---[[
-	SetControlMovable sets the Movable and MouseEnabled flag in UI elements
-]]--
-function TGU_SwimlaneList.SetControlMovable(isMovable)
-    if (LOG_ACTIVE) then
-        _logger:logTrace("TGU_SwimlaneList.SetControlMovable")
-        _logger:logDebug("isMovable", isMovable)
-    end
-
-    _control:GetNamedChild("MovableControl"):SetHidden(isMovable == false)
-
-    _control:SetMovable(isMovable)
-	_control:SetMouseEnabled(isMovable)
-end
-
---[[
-	RestorePosition sets TGU_SwimlaneList on settings position
-]]--
-function TGU_SwimlaneList.RestorePosition(posX, posY)
-    if (LOG_ACTIVE) then
-        _logger:logTrace("TGU_SwimlaneList.RestorePosition")
-        _logger:logDebug("posX, posY", posX, posY)
-    end
-
-	_control:ClearAnchors()
-	_control:SetAnchor(TOPLEFT, GuiRoot, TOPLEFT, posX, posY)
-end
-
---[[
-	OnSwimlaneListMoveStop saves current TGU_SwimlaneList position to settings
-]]--
-function TGU_SwimlaneList.OnSwimlaneListMoveStop()
-    if (LOG_ACTIVE) then _logger:logTrace("TGU_SwimlaneList.OnSwimlaneListMoveStop") end
-
-	local left = _control:GetLeft()
-	local top = _control:GetTop()
-
-    TGU_SettingsHandler.SavedVariables.PosX = left
-    TGU_SettingsHandler.SavedVariables.PosY = top
-
-    if (LOG_ACTIVE) then
-        _logger:logDebug("PosX, PosY", TGU_SettingsHandler.SavedVariables.PosX, TGU_SettingsHandler.SavedVariables.PosY)
-    end
-end
-
---[[
-	SetControlHidden sets hidden on control
-]]--
-function TGU_SwimlaneList.SetControlHidden(isHidden)
-    if (LOG_ACTIVE) then
-        _logger:logTrace("TGU_SwimlaneList.SetControlHidden")
-        _logger:logDebug("isHidden", isHidden)
-    end
-
-    if (TGU_GroupHandler.IsGrouped) then
-        _control:SetHidden(isHidden)
-    else
-        _control:SetHidden(true)
-    end
-end
-
---[[
-	SetControlActive sets hidden on control
-]]--
-function TGU_SwimlaneList.SetControlActive()
-    if (LOG_ACTIVE) then
-        _logger:logTrace("TGU_SwimlaneList.SetControlActive")
-    end
-
-    local isHidden = TGU_SettingsHandler.IsSwimlaneListVisible() == false
-    if (LOG_ACTIVE) then _logger:logDebug("isHidden", isHidden) end
-
-    TGU_SwimlaneList.SetControlHidden(isHidden or CurrentHudHiddenState())
-
-    if (isHidden) then
-        -- Start timeout timer
-	    EVENT_MANAGER:UnregisterForUpdate(TGU_SwimlaneList.Name)
-
-        CALLBACK_MANAGER:UnregisterCallback(TGU_GROUP_CHANGED, TGU_SwimlaneList.RefreshList)
-        CALLBACK_MANAGER:UnregisterCallback(TGU_PLAYER_DATA_CHANGED, TGU_SwimlaneList.UpdatePlayer)
-        CALLBACK_MANAGER:UnregisterCallback(TGU_MOVABLE_CHANGED, TGU_SwimlaneList.SetControlMovable)
-        CALLBACK_MANAGER:UnregisterCallback(TGU_SWIMLANE_ULTIMATE_GROUP_ID_CHANGED, TGU_SwimlaneList.SetSwimlaneUltimate)
-        CALLBACK_MANAGER:UnregisterCallback(TUI_HUD_HIDDEN_STATE_CHANGED, TGU_SwimlaneList.SetControlHidden)
-    else
-        TGU_SwimlaneList.SetControlMovable(TGU_SettingsHandler.SavedVariables.Movable)
-        TGU_SwimlaneList.RestorePosition(TGU_SettingsHandler.SavedVariables.PosX, TGU_SettingsHandler.SavedVariables.PosY)
-
-        -- Start timeout timer
-	    EVENT_MANAGER:RegisterForUpdate(TGU_SwimlaneList.Name, REFRESHRATE, TGU_SwimlaneList.RefreshList)
-
-        CALLBACK_MANAGER:RegisterCallback(TGU_GROUP_CHANGED, TGU_SwimlaneList.RefreshList)
-        CALLBACK_MANAGER:RegisterCallback(TGU_PLAYER_DATA_CHANGED, TGU_SwimlaneList.UpdatePlayer)
-        CALLBACK_MANAGER:RegisterCallback(TGU_MOVABLE_CHANGED, TGU_SwimlaneList.SetControlMovable)
-        CALLBACK_MANAGER:RegisterCallback(TGU_SWIMLANE_ULTIMATE_GROUP_ID_CHANGED, TGU_SwimlaneList.SetSwimlaneUltimate)
-        CALLBACK_MANAGER:RegisterCallback(TUI_HUD_HIDDEN_STATE_CHANGED, TGU_SwimlaneList.SetControlHidden)
-    end
-end
-
---[[
-	OnSwimlaneHeaderClicked called on header clicked
-]]--
-function TGU_SwimlaneList.OnSwimlaneHeaderClicked(button, swimlaneId)
-    if (LOG_ACTIVE) then
-        _logger:logTrace("TGU_SwimlaneList.OnSwimlaneHeaderClicked")
-        _logger:logDebug("swimlaneId", swimlaneId)
-    end
-
-    if (button ~= nil) then
-        CALLBACK_MANAGER:RegisterCallback(TGU_SET_ULTIMATE_GROUP, TGU_SwimlaneList.OnSetUltimateGroup)
-        CALLBACK_MANAGER:FireCallbacks(TGU_SHOW_ULTIMATE_GROUP_MENU, button, swimlaneId)
-    else
-        _logger:logError("TGU_SwimlaneList.OnSwimlaneHeaderClicked, button nil")
-    end
-end
-
---[[
-	OnSetUltimateGroup called on header clicked
-]]--
-function TGU_SwimlaneList.OnSetUltimateGroup(group, swimlaneId)
-    if (LOG_ACTIVE) then
-        _logger:logTrace("TGU_SwimlaneList.OnSetUltimateGroup")
-        _logger:logDebug("group.GroupName, swimlaneId", group.GroupName, swimlaneId)
-    end
-
-    CALLBACK_MANAGER:UnregisterCallback(TGU_SET_ULTIMATE_GROUP, TGU_SwimlaneList.OnSetUltimateGroup)
-
-    if (group ~= nil and swimlaneId ~= nil and swimlaneId >= 1 and swimlaneId <= 6) then
-        TGU_SettingsHandler.SetSwimlaneUltimateGroupIdSettings(swimlaneId, group)
-    else
-        _logger:logError("TGU_UltimateGroupMenu.ShowUltimateGroupMenu, group nil or swimlaneId invalid")
-    end
-end
-
---[[
-	SetSwimlaneUltimate sets the swimlane header icon in base of ultimateGroupId
-]]--
-function TGU_SwimlaneList.SetSwimlaneUltimate(swimlaneId, ultimateGroup)
-    if (LOG_ACTIVE) then
-        _logger:logTrace("TGU_SwimlaneList.SetSwimlaneUltimate")
-        _logger:logDebug("ultimateGroup.GroupName, swimlaneId", ultimateGroup.GroupName, swimlaneId)
-    end
-
-    local swimlaneObject = TGU_SwimlaneList.Swimlanes[swimlaneId]
-    local iconControl = swimlaneObject.SwimlaneControl:GetNamedChild("Header"):GetNamedChild("SelectorButtonControl"):GetNamedChild("Icon")
-    local labelControl = swimlaneObject.SwimlaneControl:GetNamedChild("Header"):GetNamedChild("UltimateLabel")
-
-    if (ultimateGroup ~= nil and iconControl ~= nil and labelControl ~= nil) then
-        iconControl:SetTexture(GetAbilityIcon(ultimateGroup.GroupAbilityId))
-        labelControl:SetText(ultimateGroup.GroupName)
-
-        swimlaneObject.UltimateGroupId = ultimateGroup.GroupAbilityId
-        TGU_SwimlaneList.ClearPlayersFromSwimlane(swimlaneObject)
-    else
-        _logger:logError("TGU_SwimlaneList.SetSwimlaneUltimateIcon, icon is " .. tostring(icon) .. ";" .. tostring(iconControl) .. ";" .. tostring(ultimateGroup))
-    end
-end
-
---[[
-	CreateSwimLaneListHeaders creates swimlane list headers
-]]--
-function TGU_SwimlaneList.CreateSwimLaneListHeaders()
-    if (LOG_ACTIVE) then _logger:logTrace("TGU_SwimlaneList.CreateSwimLaneListHeaders") end
-
-	for i=1, SWIMLANES, 1 do
-        local ultimateGroupId = TGU_SettingsHandler.SavedVariables.SwimlaneUltimateGroupIds[i]
-        local ultimateGroup = TGU_UltimateGroupHandler.GetUltimateGroupByAbilityId(ultimateGroupId)
-
-        local swimlaneControlName = "Swimlane" .. tostring(i)
-        local swimlaneControl = _control:GetNamedChild(swimlaneControlName)
-
-        -- Add button
-        local button = swimlaneControl:GetNamedChild("Header"):GetNamedChild("SelectorButtonControl"):GetNamedChild("Button")
-        button:SetHandler("OnClicked", function() TGU_SwimlaneList.OnSwimlaneHeaderClicked(button, i) end)
-
-        local swimLane = {}
-        swimLane.Id = i
-        swimLane.SwimlaneControl = swimlaneControl
-        swimLane.Players = {}
-
-        if (ultimateGroup ~= nil) then
-            if (LOG_ACTIVE) then
-                _logger:logDebug("Create Swimlane", i)
-                _logger:logDebug("ultimateGroup.GroupName", ultimateGroup.GroupName)
-                _logger:logDebug("swimlaneControlName", swimlaneControlName)
-            end
-
-            local icon = swimlaneControl:GetNamedChild("Header"):GetNamedChild("SelectorButtonControl"):GetNamedChild("Icon")
-            icon:SetTexture(GetAbilityIcon(ultimateGroup.GroupAbilityId))
-
-            local label = swimlaneControl:GetNamedChild("Header"):GetNamedChild("UltimateLabel")
-            label:SetText(ultimateGroup.GroupName)
-
-            swimLane.UltimateGroupId = ultimateGroup.GroupAbilityId
-        else
-            _logger:logError("TGU_SwimlaneList.CreateSwimLaneListHeaders, ultimateGroup nil.")
-        end
-
-        TGU_SwimlaneList.CreateSwimlaneListRows(swimlaneControl)
-        TGU_SwimlaneList.Swimlanes[i] = swimLane
-	end
-end
-
---[[
-	CreateSwimlaneListRows creates swimlane lsit rows
-]]--
-function TGU_SwimlaneList.CreateSwimlaneListRows(swimlaneControl)
-    if (LOG_ACTIVE) then _logger:logTrace("TGU_SwimlaneList.CreateSwimlaneListRows") end
-
-    if (swimlaneControl ~= nil) then
-	    for i=1, ROWS, 1 do
-		    local row = CreateControlFromVirtual("$(parent)Row", swimlaneControl, "GroupUltimateSwimlaneRow", i)
-            if (LOG_ACTIVE) then _logger:logDebug("Row created " .. row:GetName()) end
-
-		    row:SetHidden(true) -- initial not visible
-
-		    if (i == 1) then
-                row:SetAnchor(TOPLEFT, swimlaneControl, TOPLEFT, 0, 25)
-            elseif (i == 5) then -- Fix pixelbug, Why the hell ZOS?!
-                row:SetAnchor(TOPLEFT, lastRow, BOTTOMLEFT, 0, 0)
-            else
-				row:SetAnchor(TOPLEFT, lastRow, BOTTOMLEFT, 0, -1)
-			end
-
-		    lastRow = row
-	    end
-    else
-        _logger:logError("TGU_SwimlaneList.CreateSwimlaneListRows, swimlaneControl nil.")
-    end
-end
-
---[[
-	Initialize initializes TGU_SwimlaneList
-]]--
-function TGU_SwimlaneList.Initialize(logger, isMocked)
-    if (LOG_ACTIVE) then
-        logger:logTrace("TGU_SwimlaneList.Initialize")
-    end
-
-    _logger = logger
-    _control = TGU_SwimlaneListControl
-
-    TGU_SwimlaneList.IsMocked = isMocked
-
-    TGU_SwimlaneList.CreateSwimLaneListHeaders()
-
-    CALLBACK_MANAGER:RegisterCallback(TGU_STYLE_CHANGED, TGU_SwimlaneList.SetControlActive)
-    CALLBACK_MANAGER:RegisterCallback(TGU_IS_ZONE_CHANGED, TGU_SwimlaneList.SetControlActive)
-    CALLBACK_MANAGER:RegisterCallback(TGU_UNIT_GROUPED_CHANGED, TGU_SwimlaneList.SetControlActive)
+--[[
+	Addon: Taos Group Ultimate
+	Author: TProg Taonnor
+	Created by @Taonnor
+]]--
+
+--[[
+	Local variables
+]]--
+local LOG_ACTIVE = false
+
+local SWIMLANES = 6
+local ROWS = 6
+local REFRESHRATE = 1000 -- ms; RegisterForUpdate is in miliseconds
+local TIMEOUT = 4 -- s; GetTimeStamp() is in seconds
+
+local _logger = nil
+local _control = nil
+
+--[[
+	Table TGU_SwimlaneList
+]]--
+TGU_SwimlaneList = {}
+TGU_SwimlaneList.__index = TGU_SwimlaneList
+
+--[[
+	Table Members
+]]--
+TGU_SwimlaneList.Name = "TGU-SwimlaneList"
+TGU_SwimlaneList.IsMocked = false
+TGU_SwimlaneList.Swimlanes = {}
+
+--[[
+	Sets visibility of labels
+]]--
+function TGU_SwimlaneList.RefreshList()
+	if (LOG_ACTIVE) then _logger:logTrace("TGU_SwimlaneList.RefreshList") end
+
+    -- Check all swimlanes
+    for i,swimlane in ipairs(TGU_SwimlaneList.Swimlanes) do
+        TGU_SwimlaneList.ClearPlayersFromSwimlane(swimlane)
+	end
+end
+
+--[[
+	Sorts swimlane
+]]--
+function TGU_SwimlaneList.SortSwimlane(swimlane)
+	if (LOG_ACTIVE) then _logger:logTrace("TGU_SwimlaneList.SortSwimlane") end
+
+    -- Comparer
+    function compare(playerLeft, playerRight)
+        if (playerLeft.RelativeUltimate == playerRight.RelativeUltimate) then
+            return playerLeft.PingTag < playerRight.PingTag
+        else
+            return playerLeft.RelativeUltimate > playerRight.RelativeUltimate
+        end
+    end
+
+    table.sort(swimlane.Players, compare)
+
+    -- Update sorted swimlane list
+    for i,swimlanePlayer in ipairs(swimlane.Players) do
+        TGU_SwimlaneList.UpdateListRow(swimlane.SwimlaneControl:GetNamedChild("Row" .. i), swimlanePlayer)
+    end
+end
+
+--[[
+	Updates list row
+]]--
+function TGU_SwimlaneList.UpdateListRow(row, player)
+	if (LOG_ACTIVE) then
+        _logger:logTrace("TGU_SwimlaneList.UpdateListRow")
+    end
+
+    local playerName = player.PlayerName
+    local nameLength = string.len(playerName)
+
+    if (nameLength > 10) then
+        playerName = string.sub(playerName, 0, 10) .. "..."
+    end
+
+    row:GetNamedChild("SenderNameValueLabel"):SetText(playerName)
+    row:GetNamedChild("RelativeUltimateStatusBar"):SetValue(player.RelativeUltimate)
+
+	if (player.IsPlayerDead) then
+        -- Dead Color
+        row:GetNamedChild("SenderNameValueLabel"):SetColor(0.5, 0.5, 0.5, 0.8)
+        row:GetNamedChild("RelativeUltimateStatusBar"):SetColor(0.8, 0.03, 0.03, 0.7)
+    elseif (player.RelativeUltimate == 100) then
+		-- Ready Color
+        row:GetNamedChild("SenderNameValueLabel"):SetColor(1, 1, 1, 1)
+        row:GetNamedChild("RelativeUltimateStatusBar"):SetColor(0.03, 0.7, 0.03, 0.7)
+	else
+		-- Inprogress Color
+        row:GetNamedChild("SenderNameValueLabel"):SetColor(1, 1, 1, 0.8)
+        row:GetNamedChild("RelativeUltimateStatusBar"):SetColor(0.03, 0.03, 0.7, 0.7)
+	end
+
+    if (row:IsHidden()) then
+		row:SetHidden(false)
+	end
+end
+
+--[[
+	Updates list player
+]]--
+function TGU_SwimlaneList.UpdatePlayer(player)
+	if (LOG_ACTIVE) then
+        _logger:logTrace("TGU_SwimlaneList.UpdatePlayer")
+    end
+
+	if (player) then
+        local swimLane = TGU_SwimlaneList.GetSwimLane(player.UltimateGroup.GroupAbilityId)
+
+        if (swimLane) then
+            local row = TGU_SwimlaneList.GetSwimLaneRow(swimLane, player.PlayerName)
+
+            -- Update player
+            if (row ~= nil) then
+                for i,swimlanePlayer in ipairs(swimLane.Players) do
+		            if (swimlanePlayer.PlayerName == player.PlayerName) then
+                        swimlanePlayer.LastMapPingTimestamp = GetTimeStamp()
+                        swimlanePlayer.IsPlayerDead = player.IsPlayerDead
+                        swimlanePlayer.RelativeUltimate = player.RelativeUltimate
+                        break
+                    end
+	            end
+            else
+                -- Add new player
+                local nextFreeRow = 1
+
+                for i,player in ipairs(swimLane.Players) do
+		            nextFreeRow = nextFreeRow + 1
+	            end
+
+                if (nextFreeRow <= ROWS) then
+                    if (LOG_ACTIVE) then
+                        _logger:logDebug("TGU_SwimlaneList.UpdatePlayer, add player " .. tostring(player.PlayerName) .. " to row " .. tostring(nextFreeRow))
+                    end
+
+                    player.LastMapPingTimestamp = GetTimeStamp()
+                    swimLane.Players[nextFreeRow] = player
+                    row = swimLane.SwimlaneControl:GetNamedChild("Row" .. nextFreeRow)
+                else
+                    if (LOG_ACTIVE) then _logger:logDebug("TGU_SwimlaneList.UpdatePlayer, too much players for one swimlane " .. tostring(nextFreeRow)) end
+                end
+            end
+
+            -- Only update if player in a row
+            if (row ~= nil) then
+                if (TGU_SettingsHandler.SavedVariables.IsSortingActive) then
+                    -- Sort swimlane with all players
+                    TGU_SwimlaneList.SortSwimlane(swimLane)
+                else
+                    -- Directly update row with player
+                    TGU_SwimlaneList.UpdateListRow(row, player)
+                end
+            end
+        else
+            if (LOG_ACTIVE) then _logger:logDebug("TGU_SwimlaneList.UpdatePlayer, swimlane not found for ultimategroup " .. tostring(ultimateGroup.GroupName)) end
+        end
+	end
+end
+
+--[[
+	Get swimlane from current SwimLanes
+]]--
+function TGU_SwimlaneList.GetSwimLane(ultimateGroupId)
+    if (LOG_ACTIVE) then
+        _logger:logTrace("TGU_SwimlaneList.GetSwimLane")
+        _logger:logDebug("ultimateGroupId", ultimateGroupId)
+    end
+
+    if (ultimateGroupId ~= 0) then
+        for i,swimLane in ipairs(TGU_SwimlaneList.Swimlanes) do
+		    if (swimLane.UltimateGroupId == ultimateGroupId) then
+                return swimLane
+            end
+	    end
+
+        if (LOG_ACTIVE) then _logger:logDebug("TGU_SwimlaneList.GetSwimLane, swimLane not found " .. tostring(ultimateGroupId)) end
+        return nil
+    else
+        _logger:logError("TGU_SwimlaneList.GetSwimLane, ultimateGroupId is 0")
+        return nil
+    end
+end
+
+--[[
+	Get Player Row from current players in swimlane
+]]--
+function TGU_SwimlaneList.GetSwimLaneRow(swimLane, playerName)
+    if (LOG_ACTIVE) then
+        _logger:logTrace("TGU_SwimlaneList.GetSwimLaneRow")
+        _logger:logDebug("swimLane ID", swimLane.Id)
+    end
+
+    if (swimLane) then
+        for i,player in ipairs(swimLane.Players) do
+            if (LOG_ACTIVE) then _logger:logDebug(player.PlayerName .. " == " .. playerName) end
+		    if (player.PlayerName == playerName) then
+                return swimLane.SwimlaneControl:GetNamedChild("Row" .. i)
+            end
+	    end
+
+        if (LOG_ACTIVE) then _logger:logDebug("TGU_SwimlaneList.GetSwimLane, player not found " .. tostring(playerName)) end
+        return nil
+    else
+        _logger:logError("TGU_SwimlaneList.GetSwimLane, swimLane is nil")
+        return nil
+    end
+end
+
+--[[
+	Clears all players in swimlane
+]]--
+function TGU_SwimlaneList.ClearPlayersFromSwimlane(swimlane)
+    if (LOG_ACTIVE) then
+        _logger:logTrace("TGU_SwimlaneList.ClearPlayersFromSwimlane")
+        _logger:logDebug("swimlane ID", swimlane.Id)
+    end
+
+    if (swimlane) then
+        for i=1, ROWS, 1 do
+            local row = swimlane.SwimlaneControl:GetNamedChild("Row" .. i)
+            local swimlanePlayer = swimlane.Players[i]
+
+            if (swimlanePlayer ~= nil) then
+                local isPlayerNotGrouped = IsUnitGrouped(swimlanePlayer.PingTag) == false
+
+                if (TGU_SwimlaneList.IsMocked) then
+                    isPlayerNotGrouped = false
+                end
+
+                local isPlayerTimedOut = (GetTimeStamp() - swimlanePlayer.LastMapPingTimestamp) > TIMEOUT
+                local isPlayerUltimateNotCorrect = swimlane.UltimateGroupId ~= swimlanePlayer.UltimateGroup.GroupAbilityId
+
+                if (isPlayerNotGrouped or isPlayerTimedOut or isPlayerUltimateNotCorrect) then
+                    if (LOG_ACTIVE) then _logger:logDebug("Player invalid, hide row: " .. tostring(i)) end
+
+                    table.remove(swimlane.Players, i)
+                    row:SetHidden(true)
+                end
+            else
+                if (LOG_ACTIVE) then _logger:logDebug("Row empty, hide: " .. tostring(i)) end
+                row:SetHidden(true)
+            end
+        end
+    end
+end
+
+--[[
+	SetControlMovable sets the Movable and MouseEnabled flag in UI elements
+]]--
+function TGU_SwimlaneList.SetControlMovable(isMovable)
+    if (LOG_ACTIVE) then
+        _logger:logTrace("TGU_SwimlaneList.SetControlMovable")
+        _logger:logDebug("isMovable", isMovable)
+    end
+
+    _control:GetNamedChild("MovableControl"):SetHidden(isMovable == false)
+
+    _control:SetMovable(isMovable)
+	_control:SetMouseEnabled(isMovable)
+end
+
+--[[
+	RestorePosition sets TGU_SwimlaneList on settings position
+]]--
+function TGU_SwimlaneList.RestorePosition(posX, posY)
+    if (LOG_ACTIVE) then
+        _logger:logTrace("TGU_SwimlaneList.RestorePosition")
+        _logger:logDebug("posX, posY", posX, posY)
+    end
+
+	_control:ClearAnchors()
+	_control:SetAnchor(TOPLEFT, GuiRoot, TOPLEFT, posX, posY)
+end
+
+--[[
+	OnSwimlaneListMoveStop saves current TGU_SwimlaneList position to settings
+]]--
+function TGU_SwimlaneList.OnSwimlaneListMoveStop()
+    if (LOG_ACTIVE) then _logger:logTrace("TGU_SwimlaneList.OnSwimlaneListMoveStop") end
+
+	local left = _control:GetLeft()
+	local top = _control:GetTop()
+
+    TGU_SettingsHandler.SavedVariables.PosX = left
+    TGU_SettingsHandler.SavedVariables.PosY = top
+
+    if (LOG_ACTIVE) then
+        _logger:logDebug("PosX, PosY", TGU_SettingsHandler.SavedVariables.PosX, TGU_SettingsHandler.SavedVariables.PosY)
+    end
+end
+
+--[[
+	SetControlHidden sets hidden on control
+]]--
+function TGU_SwimlaneList.SetControlHidden(isHidden)
+    if (LOG_ACTIVE) then
+        _logger:logTrace("TGU_SwimlaneList.SetControlHidden")
+        _logger:logDebug("isHidden", isHidden)
+    end
+
+    if (TGU_GroupHandler.IsGrouped) then
+        _control:SetHidden(isHidden)
+    else
+        _control:SetHidden(true)
+    end
+end
+
+--[[
+	SetControlActive sets hidden on control
+]]--
+function TGU_SwimlaneList.SetControlActive()
+    if (LOG_ACTIVE) then
+        _logger:logTrace("TGU_SwimlaneList.SetControlActive")
+    end
+
+    local isHidden = TGU_SettingsHandler.IsSwimlaneListVisible() == false
+    if (LOG_ACTIVE) then _logger:logDebug("isHidden", isHidden) end
+
+    TGU_SwimlaneList.SetControlHidden(isHidden or CurrentHudHiddenState())
+
+    if (isHidden) then
+        -- Start timeout timer
+	    EVENT_MANAGER:UnregisterForUpdate(TGU_SwimlaneList.Name)
+
+        CALLBACK_MANAGER:UnregisterCallback(TGU_GROUP_CHANGED, TGU_SwimlaneList.RefreshList)
+        CALLBACK_MANAGER:UnregisterCallback(TGU_PLAYER_DATA_CHANGED, TGU_SwimlaneList.UpdatePlayer)
+        CALLBACK_MANAGER:UnregisterCallback(TGU_MOVABLE_CHANGED, TGU_SwimlaneList.SetControlMovable)
+        CALLBACK_MANAGER:UnregisterCallback(TGU_SWIMLANE_ULTIMATE_GROUP_ID_CHANGED, TGU_SwimlaneList.SetSwimlaneUltimate)
+        CALLBACK_MANAGER:UnregisterCallback(TUI_HUD_HIDDEN_STATE_CHANGED, TGU_SwimlaneList.SetControlHidden)
+    else
+        TGU_SwimlaneList.SetControlMovable(TGU_SettingsHandler.SavedVariables.Movable)
+        TGU_SwimlaneList.RestorePosition(TGU_SettingsHandler.SavedVariables.PosX, TGU_SettingsHandler.SavedVariables.PosY)
+
+        -- Start timeout timer
+	    EVENT_MANAGER:RegisterForUpdate(TGU_SwimlaneList.Name, REFRESHRATE, TGU_SwimlaneList.RefreshList)
+
+        CALLBACK_MANAGER:RegisterCallback(TGU_GROUP_CHANGED, TGU_SwimlaneList.RefreshList)
+        CALLBACK_MANAGER:RegisterCallback(TGU_PLAYER_DATA_CHANGED, TGU_SwimlaneList.UpdatePlayer)
+        CALLBACK_MANAGER:RegisterCallback(TGU_MOVABLE_CHANGED, TGU_SwimlaneList.SetControlMovable)
+        CALLBACK_MANAGER:RegisterCallback(TGU_SWIMLANE_ULTIMATE_GROUP_ID_CHANGED, TGU_SwimlaneList.SetSwimlaneUltimate)
+        CALLBACK_MANAGER:RegisterCallback(TUI_HUD_HIDDEN_STATE_CHANGED, TGU_SwimlaneList.SetControlHidden)
+    end
+end
+
+--[[
+	OnSwimlaneHeaderClicked called on header clicked
+]]--
+function TGU_SwimlaneList.OnSwimlaneHeaderClicked(button, swimlaneId)
+    if (LOG_ACTIVE) then
+        _logger:logTrace("TGU_SwimlaneList.OnSwimlaneHeaderClicked")
+        _logger:logDebug("swimlaneId", swimlaneId)
+    end
+
+    if (button ~= nil) then
+        CALLBACK_MANAGER:RegisterCallback(TGU_SET_ULTIMATE_GROUP, TGU_SwimlaneList.OnSetUltimateGroup)
+        CALLBACK_MANAGER:FireCallbacks(TGU_SHOW_ULTIMATE_GROUP_MENU, button, swimlaneId)
+    else
+        _logger:logError("TGU_SwimlaneList.OnSwimlaneHeaderClicked, button nil")
+    end
+end
+
+--[[
+	OnSetUltimateGroup called on header clicked
+]]--
+function TGU_SwimlaneList.OnSetUltimateGroup(group, swimlaneId)
+    if (LOG_ACTIVE) then
+        _logger:logTrace("TGU_SwimlaneList.OnSetUltimateGroup")
+        _logger:logDebug("group.GroupName, swimlaneId", group.GroupName, swimlaneId)
+    end
+
+    CALLBACK_MANAGER:UnregisterCallback(TGU_SET_ULTIMATE_GROUP, TGU_SwimlaneList.OnSetUltimateGroup)
+
+    if (group ~= nil and swimlaneId ~= nil and swimlaneId >= 1 and swimlaneId <= 6) then
+        TGU_SettingsHandler.SetSwimlaneUltimateGroupIdSettings(swimlaneId, group)
+    else
+        _logger:logError("TGU_UltimateGroupMenu.ShowUltimateGroupMenu, group nil or swimlaneId invalid")
+    end
+end
+
+--[[
+	SetSwimlaneUltimate sets the swimlane header icon in base of ultimateGroupId
+]]--
+function TGU_SwimlaneList.SetSwimlaneUltimate(swimlaneId, ultimateGroup)
+    if (LOG_ACTIVE) then
+        _logger:logTrace("TGU_SwimlaneList.SetSwimlaneUltimate")
+        _logger:logDebug("ultimateGroup.GroupName, swimlaneId", ultimateGroup.GroupName, swimlaneId)
+    end
+
+    local swimlaneObject = TGU_SwimlaneList.Swimlanes[swimlaneId]
+    local iconControl = swimlaneObject.SwimlaneControl:GetNamedChild("Header"):GetNamedChild("SelectorButtonControl"):GetNamedChild("Icon")
+    local labelControl = swimlaneObject.SwimlaneControl:GetNamedChild("Header"):GetNamedChild("UltimateLabel")
+
+    if (ultimateGroup ~= nil and iconControl ~= nil and labelControl ~= nil) then
+        iconControl:SetTexture(GetAbilityIcon(ultimateGroup.GroupAbilityId))
+        labelControl:SetText(ultimateGroup.GroupName)
+
+        swimlaneObject.UltimateGroupId = ultimateGroup.GroupAbilityId
+        TGU_SwimlaneList.ClearPlayersFromSwimlane(swimlaneObject)
+    else
+        _logger:logError("TGU_SwimlaneList.SetSwimlaneUltimateIcon, icon is " .. tostring(icon) .. ";" .. tostring(iconControl) .. ";" .. tostring(ultimateGroup))
+    end
+end
+
+--[[
+	CreateSwimLaneListHeaders creates swimlane list headers
+]]--
+function TGU_SwimlaneList.CreateSwimLaneListHeaders()
+    if (LOG_ACTIVE) then _logger:logTrace("TGU_SwimlaneList.CreateSwimLaneListHeaders") end
+
+	for i=1, SWIMLANES, 1 do
+        local ultimateGroupId = TGU_SettingsHandler.SavedVariables.SwimlaneUltimateGroupIds[i]
+        local ultimateGroup = TGU_UltimateGroupHandler.GetUltimateGroupByAbilityId(ultimateGroupId)
+
+        local swimlaneControlName = "Swimlane" .. tostring(i)
+        local swimlaneControl = _control:GetNamedChild(swimlaneControlName)
+
+        -- Add button
+        local button = swimlaneControl:GetNamedChild("Header"):GetNamedChild("SelectorButtonControl"):GetNamedChild("Button")
+        button:SetHandler("OnClicked", function() TGU_SwimlaneList.OnSwimlaneHeaderClicked(button, i) end)
+
+        local swimLane = {}
+        swimLane.Id = i
+        swimLane.SwimlaneControl = swimlaneControl
+        swimLane.Players = {}
+
+        if (ultimateGroup ~= nil) then
+            if (LOG_ACTIVE) then
+                _logger:logDebug("Create Swimlane", i)
+                _logger:logDebug("ultimateGroup.GroupName", ultimateGroup.GroupName)
+                _logger:logDebug("swimlaneControlName", swimlaneControlName)
+            end
+
+            local icon = swimlaneControl:GetNamedChild("Header"):GetNamedChild("SelectorButtonControl"):GetNamedChild("Icon")
+            icon:SetTexture(GetAbilityIcon(ultimateGroup.GroupAbilityId))
+
+            local label = swimlaneControl:GetNamedChild("Header"):GetNamedChild("UltimateLabel")
+            label:SetText(ultimateGroup.GroupName)
+
+            swimLane.UltimateGroupId = ultimateGroup.GroupAbilityId
+        else
+            _logger:logError("TGU_SwimlaneList.CreateSwimLaneListHeaders, ultimateGroup nil.")
+        end
+
+        TGU_SwimlaneList.CreateSwimlaneListRows(swimlaneControl)
+        TGU_SwimlaneList.Swimlanes[i] = swimLane
+	end
+end
+
+--[[
+	CreateSwimlaneListRows creates swimlane lsit rows
+]]--
+function TGU_SwimlaneList.CreateSwimlaneListRows(swimlaneControl)
+    if (LOG_ACTIVE) then _logger:logTrace("TGU_SwimlaneList.CreateSwimlaneListRows") end
+
+    if (swimlaneControl ~= nil) then
+	    for i=1, ROWS, 1 do
+		    local row = CreateControlFromVirtual("$(parent)Row", swimlaneControl, "GroupUltimateSwimlaneRow", i)
+            if (LOG_ACTIVE) then _logger:logDebug("Row created " .. row:GetName()) end
+
+		    row:SetHidden(true) -- initial not visible
+
+		    if (i == 1) then
+                row:SetAnchor(TOPLEFT, swimlaneControl, TOPLEFT, 0, 25)
+            elseif (i == 5) then -- Fix pixelbug, Why the hell ZOS?!
+                row:SetAnchor(TOPLEFT, lastRow, BOTTOMLEFT, 0, 0)
+            else
+				row:SetAnchor(TOPLEFT, lastRow, BOTTOMLEFT, 0, -1)
+			end
+
+		    lastRow = row
+	    end
+    else
+        _logger:logError("TGU_SwimlaneList.CreateSwimlaneListRows, swimlaneControl nil.")
+    end
+end
+
+--[[
+	Initialize initializes TGU_SwimlaneList
+]]--
+function TGU_SwimlaneList.Initialize(logger, isMocked)
+    if (LOG_ACTIVE) then
+        logger:logTrace("TGU_SwimlaneList.Initialize")
+    end
+
+    _logger = logger
+    _control = TGU_SwimlaneListControl
+
+    TGU_SwimlaneList.IsMocked = isMocked
+
+    TGU_SwimlaneList.CreateSwimLaneListHeaders()
+
+    CALLBACK_MANAGER:RegisterCallback(TGU_STYLE_CHANGED, TGU_SwimlaneList.SetControlActive)
+    CALLBACK_MANAGER:RegisterCallback(TGU_IS_ZONE_CHANGED, TGU_SwimlaneList.SetControlActive)
+    CALLBACK_MANAGER:RegisterCallback(TGU_UNIT_GROUPED_CHANGED, TGU_SwimlaneList.SetControlActive)
 end
\ No newline at end of file
diff --git a/TaosGroupTools/ui/SwimlaneList.xml b/TaosGroupTools/ui/SwimlaneList.xml
index dd568d3..4db9a04 100644
--- a/TaosGroupTools/ui/SwimlaneList.xml
+++ b/TaosGroupTools/ui/SwimlaneList.xml
@@ -1,40 +1,40 @@
-<GuiXml>
-	<Controls>
-		<TopLevelControl name="TGU_SwimlaneListControl" hidden="true" layer="OVERLAY" level="1" allowBringToTop="false" mouseEnabled="true" movable="true" clampedToScreen="true">
-			<Anchor point="TOPLEFT" relativeTo="GuiRoot" relativePoint="TOPLEFT"/>
-			<Dimensions x="610" y="265" />
-			<OnMoveStop>TGU_SwimlaneList:OnSwimlaneListMoveStop()</OnMoveStop>
-
-			<Controls>
-        <Control name="$(parent)MovableControl" tier="PARENT" inherits="MovableBackdropControl">
-          <Anchor point="TOPLEFT"/>
-          <Anchor point="BOTTOMRIGHT" />
-        </Control>
-
-        <Control name="$(parent)Swimlane1" inherits="GroupUltimateSwimlane">
-          <Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" offsetX="0" offsetY="0"/>
-        </Control>
-
-        <Control name="$(parent)Swimlane2" inherits="GroupUltimateSwimlane">
-          <Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" offsetX="102" offsetY="0"/>
-        </Control>
-
-        <Control name="$(parent)Swimlane3" inherits="GroupUltimateSwimlane">
-          <Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" offsetX="204" offsetY="0"/>
-        </Control>
-
-        <Control name="$(parent)Swimlane4" inherits="GroupUltimateSwimlane">
-          <Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" offsetX="306" offsetY="0"/>
-        </Control>
-
-        <Control name="$(parent)Swimlane5" inherits="GroupUltimateSwimlane">
-          <Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" offsetX="408" offsetY="0"/>
-        </Control>
-
-        <Control name="$(parent)Swimlane6" inherits="GroupUltimateSwimlane">
-          <Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" offsetX="510" offsetY="0"/>
-        </Control>
-		  </Controls>
-	  </TopLevelControl>
-	</Controls>
+<GuiXml>
+	<Controls>
+		<TopLevelControl name="TGU_SwimlaneListControl" hidden="true" layer="OVERLAY" level="1" allowBringToTop="false" mouseEnabled="true" movable="true" clampedToScreen="true">
+			<Anchor point="TOPLEFT" relativeTo="GuiRoot" relativePoint="TOPLEFT"/>
+			<Dimensions x="610" y="265" />
+			<OnMoveStop>TGU_SwimlaneList:OnSwimlaneListMoveStop()</OnMoveStop>
+
+			<Controls>
+        <Control name="$(parent)MovableControl" tier="PARENT" inherits="MovableBackdropControl">
+          <Anchor point="TOPLEFT"/>
+          <Anchor point="BOTTOMRIGHT" />
+        </Control>
+
+        <Control name="$(parent)Swimlane1" inherits="GroupUltimateSwimlane">
+          <Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" offsetX="0" offsetY="0"/>
+        </Control>
+
+        <Control name="$(parent)Swimlane2" inherits="GroupUltimateSwimlane">
+          <Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" offsetX="102" offsetY="0"/>
+        </Control>
+
+        <Control name="$(parent)Swimlane3" inherits="GroupUltimateSwimlane">
+          <Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" offsetX="204" offsetY="0"/>
+        </Control>
+
+        <Control name="$(parent)Swimlane4" inherits="GroupUltimateSwimlane">
+          <Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" offsetX="306" offsetY="0"/>
+        </Control>
+
+        <Control name="$(parent)Swimlane5" inherits="GroupUltimateSwimlane">
+          <Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" offsetX="408" offsetY="0"/>
+        </Control>
+
+        <Control name="$(parent)Swimlane6" inherits="GroupUltimateSwimlane">
+          <Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" offsetX="510" offsetY="0"/>
+        </Control>
+		  </Controls>
+	  </TopLevelControl>
+	</Controls>
 </GuiXml>
\ No newline at end of file
diff --git a/TaosGroupTools/ui/UltimateGroupMenu.lua b/TaosGroupTools/ui/UltimateGroupMenu.lua
index 9484829..a70e53c 100644
--- a/TaosGroupTools/ui/UltimateGroupMenu.lua
+++ b/TaosGroupTools/ui/UltimateGroupMenu.lua
@@ -1,70 +1,70 @@
---[[
-	Addon: Taos Group Ultimate
-	Author: TProg Taonnor
-	Created by @Taonnor
-]]--
-
---[[
-	Local variables
-]]--
-local LOG_ACTIVE = false
-local _logger = nil
-
---[[
-	Table TGU_UltimateGroupMenu
-]]--
-TGU_UltimateGroupMenu = {}
-TGU_UltimateGroupMenu.__index = TGU_UltimateGroupMenu
-
---[[
-	Table Members
-]]--
-
---[[
-	SetUltimateGroup shows ultimate group menu
-]]--
-function TGU_UltimateGroupMenu.SetUltimateGroup(group, arg)
-    if (LOG_ACTIVE) then
-        _logger:logTrace("TGU_UltimateGroupMenu.SetultimateGroup")
-        _logger:logDebug("group.GroupName, arg", group.GroupName, arg)
-    end
-
-    CALLBACK_MANAGER:FireCallbacks(TGU_SET_ULTIMATE_GROUP, group, arg)
-end
-
---[[
-	ShowUltimateGroupMenu shows ultimate group menu
-]]--
-function TGU_UltimateGroupMenu.ShowUltimateGroupMenu(control, arg)
-    if (LOG_ACTIVE) then
-        _logger:logTrace("TGU_UltimateGroupMenu.ShowUltimateGroupMenu")
-        _logger:logDebug("arg", arg)
-    end
-
-    if (control ~= nil) then
-        ClearMenu()
-
-        local ultimateGroups = TGU_UltimateGroupHandler.GetUltimateGroups()
-
-        for i, group in pairs(ultimateGroups) do
-            AddMenuItem(group.GroupName .. " - " .. group.GroupDescription, function() TGU_UltimateGroupMenu.SetUltimateGroup(group, arg) end)
-        end
-
-        ShowMenu(control)
-    else
-        _logger:logError("TGU_UltimateGroupMenu.ShowUltimateGroupMenu, control nil")
-    end
-end
-
---[[
-	Initialize initializes TGU_UltimateGroupMenu
-]]--
-function TGU_UltimateGroupMenu.Initialize(logger)
-    if (LOG_ACTIVE) then
-        logger:logTrace("TGU_UltimateGroupMenu.Initialize")
-    end
-
-    _logger = logger
-
-    CALLBACK_MANAGER:RegisterCallback(TGU_SHOW_ULTIMATE_GROUP_MENU, TGU_UltimateGroupMenu.ShowUltimateGroupMenu)
+--[[
+	Addon: Taos Group Ultimate
+	Author: TProg Taonnor
+	Created by @Taonnor
+]]--
+
+--[[
+	Local variables
+]]--
+local LOG_ACTIVE = false
+local _logger = nil
+
+--[[
+	Table TGU_UltimateGroupMenu
+]]--
+TGU_UltimateGroupMenu = {}
+TGU_UltimateGroupMenu.__index = TGU_UltimateGroupMenu
+
+--[[
+	Table Members
+]]--
+
+--[[
+	SetUltimateGroup shows ultimate group menu
+]]--
+function TGU_UltimateGroupMenu.SetUltimateGroup(group, arg)
+    if (LOG_ACTIVE) then
+        _logger:logTrace("TGU_UltimateGroupMenu.SetultimateGroup")
+        _logger:logDebug("group.GroupName, arg", group.GroupName, arg)
+    end
+
+    CALLBACK_MANAGER:FireCallbacks(TGU_SET_ULTIMATE_GROUP, group, arg)
+end
+
+--[[
+	ShowUltimateGroupMenu shows ultimate group menu
+]]--
+function TGU_UltimateGroupMenu.ShowUltimateGroupMenu(control, arg)
+    if (LOG_ACTIVE) then
+        _logger:logTrace("TGU_UltimateGroupMenu.ShowUltimateGroupMenu")
+        _logger:logDebug("arg", arg)
+    end
+
+    if (control ~= nil) then
+        ClearMenu()
+
+        local ultimateGroups = TGU_UltimateGroupHandler.GetUltimateGroups()
+
+        for i, group in pairs(ultimateGroups) do
+            AddMenuItem(group.GroupName .. " - " .. group.GroupDescription, function() TGU_UltimateGroupMenu.SetUltimateGroup(group, arg) end)
+        end
+
+        ShowMenu(control)
+    else
+        _logger:logError("TGU_UltimateGroupMenu.ShowUltimateGroupMenu, control nil")
+    end
+end
+
+--[[
+	Initialize initializes TGU_UltimateGroupMenu
+]]--
+function TGU_UltimateGroupMenu.Initialize(logger)
+    if (LOG_ACTIVE) then
+        logger:logTrace("TGU_UltimateGroupMenu.Initialize")
+    end
+
+    _logger = logger
+
+    CALLBACK_MANAGER:RegisterCallback(TGU_SHOW_ULTIMATE_GROUP_MENU, TGU_UltimateGroupMenu.ShowUltimateGroupMenu)
 end
\ No newline at end of file
diff --git a/TaosGroupTools/ui/VirtualControls.xml b/TaosGroupTools/ui/VirtualControls.xml
index 31aa1b8..0940180 100644
--- a/TaosGroupTools/ui/VirtualControls.xml
+++ b/TaosGroupTools/ui/VirtualControls.xml
@@ -1,219 +1,219 @@
-<GuiXml>
-  <Font name="MoveActiveFont" font="$(BOLD_FONT)|18|soft-shadow-thin"/>
-  <Font name="SwimlaneListFont" font="$(MEDIUM_FONT)|16|soft-shadow-thin"/>
-  <Font name="SwimlaneListHeaderFont" font="$(BOLD_FONT)|21|soft-shadow-thin"/>
-  <Font name="GroupUltimateFont" font="$(MEDIUM_FONT)|16|soft-shadow-thin"/>
-  <Font name="GroupUltimateHeaderFont" font="$(BOLD_FONT)|16|soft-shadow-thin"/>
-  <Font name="CompactSwimlaneListFont" font="$(MEDIUM_FONT)|14|soft-shadow-thin"/>
-
-	<Controls>
-    <Label 	name="MoveActiveLabel"
-            width="48"
-            height="18"
-            font="MoveActiveFont"
-            inheritAlpha="true"
-            color="FFFFFF"
-            virtual="true"/>
-
-		<Label 	name="SwimlaneListLabel"
-				    width="90"
-				    height="20"
-				    font="SwimlaneListFont"
-				    inheritAlpha="true"
-				    color="FFFFFF"
-				    verticalAlignment="CENTER"
-				    horizontalAlignment="LEFT"
-				    virtual="true"/>
-
-    <Label 	name="SwimlaneListHeaderLabel"
-				    width="75"
-				    height="25"
-				    font="GroupUltimateHeaderFont"
-				    inheritAlpha="true"
-				    color="FFFFFF"
-				    verticalAlignment="TEXT_ALIGN_TOP"
-				    horizontalAlignment="TEXT_ALIGN_LEFT"
-				    virtual="true"/>
-
-    <Label 	name="GroupUltimateHeaderLabel"
-            width="200"
-            height="20"
-            font="GroupUltimateHeaderFont"
-            inheritAlpha="true"
-            color="FFFFFF"
-            verticalAlignment="TEXT_ALIGN_TOP"
-            horizontalAlignment="TEXT_ALIGN_LEFT"
-            text="Header"
-            virtual="true"/>
-
-    <Label 	name="GroupUltimateLabel"
-				    width="200"
-				    height="20"
-				    font="GroupUltimateFont"
-				    inheritAlpha="true"
-				    color="FFFFFF"
-				    verticalAlignment="TEXT_ALIGN_TOP"
-				    horizontalAlignment="TEXT_ALIGN_LEFT"
-				    text="Label"
-				    virtual="true"/>
-
-    <Label 	name="GroupUltimateShortLabel"
-				    width="100"
-				    height="20"
-				    font="GroupUltimateFont"
-				    inheritAlpha="true"
-				    color="FFFFFF"
-				    verticalAlignment="TEXT_ALIGN_TOP"
-				    horizontalAlignment="TEXT_ALIGN_LEFT"
-				    text="Label"
-				    virtual="true"/>
-
-    <Label 	name="CompactSwimlaneListLabel"
-				    width="90"
-				    height="20"
-				    font="CompactSwimlaneListFont"
-				    inheritAlpha="true"
-				    color="FFFFFF"
-				    verticalAlignment="CENTER"
-				    horizontalAlignment="LEFT"
-				    virtual="true"/>
-
-    <Control name="MovableBackdropControl" virtual="true">
-      <Controls>
-        <Backdrop name="$(parent)Backdrop" edgeColor="000000" centerColor="FF0000" alpha="0.4" >
-          <Anchor point="TOPLEFT"/>
-          <Anchor point="BOTTOMRIGHT" />
-          <Edge edgeSize="2" />
-        </Backdrop>
-        <Label name="$(parent)MoveActiveLabel" inherits="MoveActiveLabel" text="MOVE" tier="2">
-          <Anchor point="CENTER"/>
-        </Label>
-      </Controls>
-    </Control>
-
-    <Control name="GroupUltimateSwimlaneRow" virtual="true">
-			<Dimensions x="100" y="40" />
-			<Controls>
-        <Backdrop name="$(parent)Backdrop" edgeColor="000000" centerColor="8168A6" alpha="0.6" >
-          <Dimensions x="100" y="40" />
-          <Anchor point="TOPLEFT"  relativeTo="$(parent)" relativePoint="TOPLEFT"/>
-          <Edge edgeSize="1" />
-        </Backdrop>
-        <StatusBar name="$(parent)RelativeUltimateStatusBar" alpha="0.7" textureFile="/TaosGroupUltimate/textures/grainy.dds">
-          <Dimensions x="98" y="38" />
-          <Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" offsetX="1" offsetY="1"/>
-          <Limits min="0" max="100" />
-        </StatusBar>
-        <Label name="$(parent)SenderNameValueLabel" inherits="SwimlaneListLabel">
-          <Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" offsetX="5" offsetY="10"/>
-        </Label>
-			</Controls>
-		</Control>
-
-    <Control name="GroupUltimateSwimlaneHeader" virtual="true">
-      <Dimensions x="100" y="25" />
-      <Controls>
-        <Control name="$(parent)SelectorButtonControl">
-          <Dimensions x="25" y="25"/>
-          <Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" />
-          <Controls>
-            <Button name="$(parent)Button"
-                    ButtonState="BSTATE_NORMAL"
-                    tier="1">
-              <Dimensions x="25" y="25"/>
-              <Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT"/>
-              <Textures normal="/esoui/art/actionbar/abilityframe64_up.dds"
-                        pressed="/esoui/art/actionbar/abilityframe64_down.dds"
-                        mouseOver="/TaosGroupUltimate/textures/hover.dds"/>
-            </Button>
-            <Texture name="$(parent)Icon" tier="0">
-              <Dimensions x="24" y="24"/>
-              <Anchor point="CENTER" relativeTo="$(parent)" relativePoint="CENTER"/>
-            </Texture>
-          </Controls>
-        </Control>
-        <Label name="$(parent)UltimateLabel" inherits="SwimlaneListHeaderLabel" text="NEGATE">
-					<Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" offsetX="28" offsetY="3"/>
-				</Label>
-      </Controls>
-    </Control>
-
-    <Control name="GroupUltimateSwimlane" virtual="true">
-      <Dimensions x="100" y="300"/>
-      <Controls>
-        <Control name="$(parent)Header" inherits="GroupUltimateSwimlaneHeader">
-          <Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" offsetX="0" offsetY="0"/>
-        </Control>
-      </Controls>
-    </Control>
-
-    <Control name="GroupUltimateSimpleListRow" virtual="true">
-      <Dimensions y="20" />
-      <Controls>
-        <Label name="$(parent)SenderNameValueLabel" inherits="GroupUltimateLabel" text="-">
-          <Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" offsetX="0"/>
-        </Label>
-        <Label name="$(parent)UltimateValueLabel" inherits="GroupUltimateLabel" text="-">
-          <Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" offsetX="200"/>
-        </Label>
-        <Label name="$(parent)ReadyValueLabel" inherits="GroupUltimateShortLabel" text="0">
-          <Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" offsetX="400"/>
-        </Label>
-      </Controls>
-    </Control>
-
-    <Control name="CompactGroupUltimateSwimlaneRow" virtual="true">
-      <Dimensions x="50" y="25" />
-      <Controls>
-        <Backdrop name="$(parent)Backdrop" edgeColor="000000" centerColor="8168A6" alpha="0.6" >
-          <Dimensions x="50" y="25" />
-          <Anchor point="TOPLEFT"  relativeTo="$(parent)" relativePoint="TOPLEFT"/>
-          <Edge edgeSize="1" />
-        </Backdrop>
-        <StatusBar name="$(parent)RelativeUltimateStatusBar" alpha="0.7" textureFile="/TaosGroupUltimate/textures/grainy.dds">
-          <Dimensions x="48" y="23" />
-          <Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" offsetX="1" offsetY="1"/>
-          <Limits min="0" max="100" />
-        </StatusBar>
-        <Label name="$(parent)SenderNameValueLabel" inherits="CompactSwimlaneListLabel">
-          <Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" offsetX="3" offsetY="2"/>
-        </Label>
-      </Controls>
-    </Control>
-
-    <Control name="CompactGroupUltimateSwimlaneHeader" virtual="true">
-      <Dimensions x="50" y="50" />
-      <Controls>
-        <Control name="$(parent)SelectorButtonControl">
-          <Dimensions x="50" y="50"/>
-          <Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" />
-          <Controls>
-            <Button name="$(parent)Button"
-                    ButtonState="BSTATE_NORMAL"
-                    tier="1">
-              <Dimensions x="50" y="50"/>
-              <Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT"/>
-              <Textures normal="/esoui/art/actionbar/abilityframe64_up.dds"
-                        pressed="/esoui/art/actionbar/abilityframe64_down.dds"
-                        mouseOver="/TaosGroupUltimate/textures/hover.dds"/>
-            </Button>
-            <Texture name="$(parent)Icon" tier="0">
-              <Dimensions x="48" y="48"/>
-              <Anchor point="CENTER" relativeTo="$(parent)" relativePoint="CENTER"/>
-            </Texture>
-          </Controls>
-        </Control>
-      </Controls>
-    </Control>
-
-    <Control name="CompactGroupUltimateSwimlane" virtual="true">
-      <Dimensions x="50" y="150"/>
-      <Controls>
-        <Control name="$(parent)Header" inherits="CompactGroupUltimateSwimlaneHeader">
-          <Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" offsetX="0" offsetY="0"/>
-        </Control>
-      </Controls>
-    </Control>
-
-  </Controls>
+<GuiXml>
+  <Font name="MoveActiveFont" font="$(BOLD_FONT)|18|soft-shadow-thin"/>
+  <Font name="SwimlaneListFont" font="$(MEDIUM_FONT)|16|soft-shadow-thin"/>
+  <Font name="SwimlaneListHeaderFont" font="$(BOLD_FONT)|21|soft-shadow-thin"/>
+  <Font name="GroupUltimateFont" font="$(MEDIUM_FONT)|16|soft-shadow-thin"/>
+  <Font name="GroupUltimateHeaderFont" font="$(BOLD_FONT)|16|soft-shadow-thin"/>
+  <Font name="CompactSwimlaneListFont" font="$(MEDIUM_FONT)|14|soft-shadow-thin"/>
+
+	<Controls>
+    <Label 	name="MoveActiveLabel"
+            width="48"
+            height="18"
+            font="MoveActiveFont"
+            inheritAlpha="true"
+            color="FFFFFF"
+            virtual="true"/>
+
+		<Label 	name="SwimlaneListLabel"
+				    width="90"
+				    height="20"
+				    font="SwimlaneListFont"
+				    inheritAlpha="true"
+				    color="FFFFFF"
+				    verticalAlignment="CENTER"
+				    horizontalAlignment="LEFT"
+				    virtual="true"/>
+
+    <Label 	name="SwimlaneListHeaderLabel"
+				    width="75"
+				    height="25"
+				    font="GroupUltimateHeaderFont"
+				    inheritAlpha="true"
+				    color="FFFFFF"
+				    verticalAlignment="TEXT_ALIGN_TOP"
+				    horizontalAlignment="TEXT_ALIGN_LEFT"
+				    virtual="true"/>
+
+    <Label 	name="GroupUltimateHeaderLabel"
+            width="200"
+            height="20"
+            font="GroupUltimateHeaderFont"
+            inheritAlpha="true"
+            color="FFFFFF"
+            verticalAlignment="TEXT_ALIGN_TOP"
+            horizontalAlignment="TEXT_ALIGN_LEFT"
+            text="Header"
+            virtual="true"/>
+
+    <Label 	name="GroupUltimateLabel"
+				    width="200"
+				    height="20"
+				    font="GroupUltimateFont"
+				    inheritAlpha="true"
+				    color="FFFFFF"
+				    verticalAlignment="TEXT_ALIGN_TOP"
+				    horizontalAlignment="TEXT_ALIGN_LEFT"
+				    text="Label"
+				    virtual="true"/>
+
+    <Label 	name="GroupUltimateShortLabel"
+				    width="100"
+				    height="20"
+				    font="GroupUltimateFont"
+				    inheritAlpha="true"
+				    color="FFFFFF"
+				    verticalAlignment="TEXT_ALIGN_TOP"
+				    horizontalAlignment="TEXT_ALIGN_LEFT"
+				    text="Label"
+				    virtual="true"/>
+
+    <Label 	name="CompactSwimlaneListLabel"
+				    width="90"
+				    height="20"
+				    font="CompactSwimlaneListFont"
+				    inheritAlpha="true"
+				    color="FFFFFF"
+				    verticalAlignment="CENTER"
+				    horizontalAlignment="LEFT"
+				    virtual="true"/>
+
+    <Control name="MovableBackdropControl" virtual="true">
+      <Controls>
+        <Backdrop name="$(parent)Backdrop" edgeColor="000000" centerColor="FF0000" alpha="0.4" >
+          <Anchor point="TOPLEFT"/>
+          <Anchor point="BOTTOMRIGHT" />
+          <Edge edgeSize="2" />
+        </Backdrop>
+        <Label name="$(parent)MoveActiveLabel" inherits="MoveActiveLabel" text="MOVE" tier="2">
+          <Anchor point="CENTER"/>
+        </Label>
+      </Controls>
+    </Control>
+
+    <Control name="GroupUltimateSwimlaneRow" virtual="true">
+			<Dimensions x="100" y="40" />
+			<Controls>
+        <Backdrop name="$(parent)Backdrop" edgeColor="000000" centerColor="8168A6" alpha="0.6" >
+          <Dimensions x="100" y="40" />
+          <Anchor point="TOPLEFT"  relativeTo="$(parent)" relativePoint="TOPLEFT"/>
+          <Edge edgeSize="1" />
+        </Backdrop>
+        <StatusBar name="$(parent)RelativeUltimateStatusBar" alpha="0.7" textureFile="/TaosGroupUltimate/textures/grainy.dds">
+          <Dimensions x="98" y="38" />
+          <Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" offsetX="1" offsetY="1"/>
+          <Limits min="0" max="100" />
+        </StatusBar>
+        <Label name="$(parent)SenderNameValueLabel" inherits="SwimlaneListLabel">
+          <Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" offsetX="5" offsetY="10"/>
+        </Label>
+			</Controls>
+		</Control>
+
+    <Control name="GroupUltimateSwimlaneHeader" virtual="true">
+      <Dimensions x="100" y="25" />
+      <Controls>
+        <Control name="$(parent)SelectorButtonControl">
+          <Dimensions x="25" y="25"/>
+          <Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" />
+          <Controls>
+            <Button name="$(parent)Button"
+                    ButtonState="BSTATE_NORMAL"
+                    tier="1">
+              <Dimensions x="25" y="25"/>
+              <Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT"/>
+              <Textures normal="/esoui/art/actionbar/abilityframe64_up.dds"
+                        pressed="/esoui/art/actionbar/abilityframe64_down.dds"
+                        mouseOver="/TaosGroupUltimate/textures/hover.dds"/>
+            </Button>
+            <Texture name="$(parent)Icon" tier="0">
+              <Dimensions x="24" y="24"/>
+              <Anchor point="CENTER" relativeTo="$(parent)" relativePoint="CENTER"/>
+            </Texture>
+          </Controls>
+        </Control>
+        <Label name="$(parent)UltimateLabel" inherits="SwimlaneListHeaderLabel" text="NEGATE">
+					<Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" offsetX="28" offsetY="3"/>
+				</Label>
+      </Controls>
+    </Control>
+
+    <Control name="GroupUltimateSwimlane" virtual="true">
+      <Dimensions x="100" y="300"/>
+      <Controls>
+        <Control name="$(parent)Header" inherits="GroupUltimateSwimlaneHeader">
+          <Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" offsetX="0" offsetY="0"/>
+        </Control>
+      </Controls>
+    </Control>
+
+    <Control name="GroupUltimateSimpleListRow" virtual="true">
+      <Dimensions y="20" />
+      <Controls>
+        <Label name="$(parent)SenderNameValueLabel" inherits="GroupUltimateLabel" text="-">
+          <Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" offsetX="0"/>
+        </Label>
+        <Label name="$(parent)UltimateValueLabel" inherits="GroupUltimateLabel" text="-">
+          <Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" offsetX="200"/>
+        </Label>
+        <Label name="$(parent)ReadyValueLabel" inherits="GroupUltimateShortLabel" text="0">
+          <Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" offsetX="400"/>
+        </Label>
+      </Controls>
+    </Control>
+
+    <Control name="CompactGroupUltimateSwimlaneRow" virtual="true">
+      <Dimensions x="50" y="25" />
+      <Controls>
+        <Backdrop name="$(parent)Backdrop" edgeColor="000000" centerColor="8168A6" alpha="0.6" >
+          <Dimensions x="50" y="25" />
+          <Anchor point="TOPLEFT"  relativeTo="$(parent)" relativePoint="TOPLEFT"/>
+          <Edge edgeSize="1" />
+        </Backdrop>
+        <StatusBar name="$(parent)RelativeUltimateStatusBar" alpha="0.7" textureFile="/TaosGroupUltimate/textures/grainy.dds">
+          <Dimensions x="48" y="23" />
+          <Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" offsetX="1" offsetY="1"/>
+          <Limits min="0" max="100" />
+        </StatusBar>
+        <Label name="$(parent)SenderNameValueLabel" inherits="CompactSwimlaneListLabel">
+          <Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" offsetX="3" offsetY="2"/>
+        </Label>
+      </Controls>
+    </Control>
+
+    <Control name="CompactGroupUltimateSwimlaneHeader" virtual="true">
+      <Dimensions x="50" y="50" />
+      <Controls>
+        <Control name="$(parent)SelectorButtonControl">
+          <Dimensions x="50" y="50"/>
+          <Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" />
+          <Controls>
+            <Button name="$(parent)Button"
+                    ButtonState="BSTATE_NORMAL"
+                    tier="1">
+              <Dimensions x="50" y="50"/>
+              <Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT"/>
+              <Textures normal="/esoui/art/actionbar/abilityframe64_up.dds"
+                        pressed="/esoui/art/actionbar/abilityframe64_down.dds"
+                        mouseOver="/TaosGroupUltimate/textures/hover.dds"/>
+            </Button>
+            <Texture name="$(parent)Icon" tier="0">
+              <Dimensions x="48" y="48"/>
+              <Anchor point="CENTER" relativeTo="$(parent)" relativePoint="CENTER"/>
+            </Texture>
+          </Controls>
+        </Control>
+      </Controls>
+    </Control>
+
+    <Control name="CompactGroupUltimateSwimlane" virtual="true">
+      <Dimensions x="50" y="150"/>
+      <Controls>
+        <Control name="$(parent)Header" inherits="CompactGroupUltimateSwimlaneHeader">
+          <Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" offsetX="0" offsetY="0"/>
+        </Control>
+      </Controls>
+    </Control>
+
+  </Controls>
 </GuiXml>
\ No newline at end of file
diff --git a/TaosGroupTools/util/DebugLogger.lua b/TaosGroupTools/util/DebugLogger.lua
index 40350d8..151963f 100644
--- a/TaosGroupTools/util/DebugLogger.lua
+++ b/TaosGroupTools/util/DebugLogger.lua
@@ -1,153 +1,153 @@
---[[
-	Addon: util
-	Author: TProg Taonnor
-	Created by @Taonnor
-]]--
-
---[[
-	Class definition
-]]--
--- A table in hole lua workspace must be unique
--- The debug logger is global util table, used in several of my addons
-if (TaosDebugLogger == nil) then
-	TaosDebugLogger = {}
-	TaosDebugLogger.__index = TaosDebugLogger
-
-	setmetatable(TaosDebugLogger, {
-		__call = function (cls, ...)
-			return cls.new(...)
-		end,
-	})
-
-	--[[
-		Class constructor
-	]]--
-	function TaosDebugLogger.new(name, logCommand, traceActive, debugActive, errorActive, directPrint, catchLuaErrors)
-		local self = setmetatable({}, TaosDebugLogger)
-
-		-- class members
-		self.name = name
-		self.logCommand = logCommand
-		self.buffer = {}
-
-		self.TRACE_ACTIVE = traceActive
-		self.DEBUG_ACTIVE = debugActive
-		self.ERROR_ACTIVE = errorActive
-		self.DIRECT_PRINT = directPrint
-		self.CATCH_LUA_ERRORS = catchLuaErrors
-
-		SLASH_COMMANDS[self.logCommand] = self:CommandShowLogs()
-		EVENT_MANAGER:RegisterForEvent(self.name, EVENT_PLAYER_ACTIVATED, function(eventCode) self:OnPlayerActivated(eventCode) end)
-		EVENT_MANAGER:RegisterForEvent(self.name, EVENT_LUA_ERROR, function(eventCode, errorOutput) self:OnLuaError(eventCode, errorOutput) end)
-
-		return self
-	end
-
-	--[[
-		Trace
-	]]--
-	function TaosDebugLogger:logTrace(functionName)
-		if (self.TRACE_ACTIVE == false) then return end
-
-		local formatMessage = "[%s - TRACE] %s"
-		self:logMessage(formatMessage:format(GetTimeString(), functionName))
-	end
-
-	--[[
-		Debug
-	]]--
-	function TaosDebugLogger:logDebug(...)
-		if (self.DEBUG_ACTIVE == false) then return end
-
-		local formatMessage = "[%s - DEBUG] %s"
-		local msg = ""
-
-		for i = 1, select("#", ...) do
-			if (i == 1) then
-				msg = tostring(select(i, ...))
-			else
-				msg = msg .. "; " .. tostring(select(i, ...))
-			end
-		end
-
-		self:logMessage(formatMessage:format(GetTimeString(), msg))
-	end
-
-	--[[
-		Error
-	]]--
-	function TaosDebugLogger:logError(...)
-		if (self.ERROR_ACTIVE == false) then return end
-
-		local formatMessage = "[%s - ERROR] %s"
-		local msg = ""
-
-		for i = 1, select("#", ...) do
-			if (i == 1) then
-				msg = tostring(select(i, ...))
-			else
-				msg = msg .. "; " .. tostring(select(i, ...))
-			end
-		end
-
-		self:logMessage(formatMessage:format(GetTimeString(), msg))
-	end
-
-	--[[
-		Log
-	]]--
-	function TaosDebugLogger:logMessage(msg)
-		self:AddMessage(msg)
-
-		if (self.DIRECT_PRINT) then
-			d(msg)
-		end
-	end
-
-	--[[
-		Adds messages to buffer
-	]]--
-	function TaosDebugLogger:AddMessage(msg)
-		if(not msg or self.buffer == nil) then return end
-
-		local buf = self.buffer
-		buf[#buf + 1] = msg
-	end
-
-	--[[
-		Print buffered messages
-	]]--
-	function TaosDebugLogger:PrintMessages()
-		if(self.buffer == nil) then return end
-
-		for i,msg in ipairs(self.buffer) do
-			d(msg)
-		end
-	end
-
-	--[[
-		Prints buffered outputs
-	]]--
-	function TaosDebugLogger:OnPlayerActivated(eventCode)
-		self:PrintMessages()
-
-		EVENT_MANAGER:UnregisterForEvent(self.name, EVENT_PLAYER_ACTIVATED)
-	end
-
-	--[[
-		Catches lua errors
-	]]--
-	function TaosDebugLogger:OnLuaError(eventCode, errorOutput)
-		if (self.CATCH_LUA_ERRORS == false) then return end
-
-		self:logMessage(errorOutput)
-		ZO_UIErrors_HideCurrent()
-	end
-
-	--[[
-		Handles /tapslogs command
-	]]--
-	function TaosDebugLogger:CommandShowLogs()
-		self:PrintMessages()
-	end
+--[[
+	Addon: util
+	Author: TProg Taonnor
+	Created by @Taonnor
+]]--
+
+--[[
+	Class definition
+]]--
+-- A table in hole lua workspace must be unique
+-- The debug logger is global util table, used in several of my addons
+if (TaosDebugLogger == nil) then
+	TaosDebugLogger = {}
+	TaosDebugLogger.__index = TaosDebugLogger
+
+	setmetatable(TaosDebugLogger, {
+		__call = function (cls, ...)
+			return cls.new(...)
+		end,
+	})
+
+	--[[
+		Class constructor
+	]]--
+	function TaosDebugLogger.new(name, logCommand, traceActive, debugActive, errorActive, directPrint, catchLuaErrors)
+		local self = setmetatable({}, TaosDebugLogger)
+
+		-- class members
+		self.name = name
+		self.logCommand = logCommand
+		self.buffer = {}
+
+		self.TRACE_ACTIVE = traceActive
+		self.DEBUG_ACTIVE = debugActive
+		self.ERROR_ACTIVE = errorActive
+		self.DIRECT_PRINT = directPrint
+		self.CATCH_LUA_ERRORS = catchLuaErrors
+
+		SLASH_COMMANDS[self.logCommand] = self:CommandShowLogs()
+		EVENT_MANAGER:RegisterForEvent(self.name, EVENT_PLAYER_ACTIVATED, function(eventCode) self:OnPlayerActivated(eventCode) end)
+		EVENT_MANAGER:RegisterForEvent(self.name, EVENT_LUA_ERROR, function(eventCode, errorOutput) self:OnLuaError(eventCode, errorOutput) end)
+
+		return self
+	end
+
+	--[[
+		Trace
+	]]--
+	function TaosDebugLogger:logTrace(functionName)
+		if (self.TRACE_ACTIVE == false) then return end
+
+		local formatMessage = "[%s - TRACE] %s"
+		self:logMessage(formatMessage:format(GetTimeString(), functionName))
+	end
+
+	--[[
+		Debug
+	]]--
+	function TaosDebugLogger:logDebug(...)
+		if (self.DEBUG_ACTIVE == false) then return end
+
+		local formatMessage = "[%s - DEBUG] %s"
+		local msg = ""
+
+		for i = 1, select("#", ...) do
+			if (i == 1) then
+				msg = tostring(select(i, ...))
+			else
+				msg = msg .. "; " .. tostring(select(i, ...))
+			end
+		end
+
+		self:logMessage(formatMessage:format(GetTimeString(), msg))
+	end
+
+	--[[
+		Error
+	]]--
+	function TaosDebugLogger:logError(...)
+		if (self.ERROR_ACTIVE == false) then return end
+
+		local formatMessage = "[%s - ERROR] %s"
+		local msg = ""
+
+		for i = 1, select("#", ...) do
+			if (i == 1) then
+				msg = tostring(select(i, ...))
+			else
+				msg = msg .. "; " .. tostring(select(i, ...))
+			end
+		end
+
+		self:logMessage(formatMessage:format(GetTimeString(), msg))
+	end
+
+	--[[
+		Log
+	]]--
+	function TaosDebugLogger:logMessage(msg)
+		self:AddMessage(msg)
+
+		if (self.DIRECT_PRINT) then
+			d(msg)
+		end
+	end
+
+	--[[
+		Adds messages to buffer
+	]]--
+	function TaosDebugLogger:AddMessage(msg)
+		if(not msg or self.buffer == nil) then return end
+
+		local buf = self.buffer
+		buf[#buf + 1] = msg
+	end
+
+	--[[
+		Print buffered messages
+	]]--
+	function TaosDebugLogger:PrintMessages()
+		if(self.buffer == nil) then return end
+
+		for i,msg in ipairs(self.buffer) do
+			d(msg)
+		end
+	end
+
+	--[[
+		Prints buffered outputs
+	]]--
+	function TaosDebugLogger:OnPlayerActivated(eventCode)
+		self:PrintMessages()
+
+		EVENT_MANAGER:UnregisterForEvent(self.name, EVENT_PLAYER_ACTIVATED)
+	end
+
+	--[[
+		Catches lua errors
+	]]--
+	function TaosDebugLogger:OnLuaError(eventCode, errorOutput)
+		if (self.CATCH_LUA_ERRORS == false) then return end
+
+		self:logMessage(errorOutput)
+		ZO_UIErrors_HideCurrent()
+	end
+
+	--[[
+		Handles /tapslogs command
+	]]--
+	function TaosDebugLogger:CommandShowLogs()
+		self:PrintMessages()
+	end
 end
\ No newline at end of file
diff --git a/TaosGroupTools/util/UiHelper.lua b/TaosGroupTools/util/UiHelper.lua
index 4979efb..86c9c86 100644
--- a/TaosGroupTools/util/UiHelper.lua
+++ b/TaosGroupTools/util/UiHelper.lua
@@ -1,87 +1,87 @@
---[[
-	Addon: util
-	Author: TProg Taonnor
-	Created by @Taonnor
-]]--
-
---[[
-	Class definition (Static class)
-]]--
--- A table in hole lua workspace must be unique
--- The ui helper is global util table, used in several of my addons
--- The table is created as "static" class without constructor and static helper methods
-if (TaosUiHelper == nil) then
-	TaosUiHelper = {}
-	TaosUiHelper.__index = TaosUiHelper
-
-    -- Global Callback Variables
-    TUI_HUD_HIDDEN_STATE_CHANGED = "TUI-HudHiddenStateChange"
-
-	-- isHidden logic for hud scenes
-    -- This logic will fire TUI_HUD_HIDDEN_STATE_CHANGED if hud scenes not visible:
-    -- hud = true; hudui = true -> isHidden = true
-    -- hud = true; hudui = false -> isHidden = false
-    -- hud = false; hudui = true -> isHidden = false
-    -- hud = false; hudui = false -> isHidden = false
-    local internalHudSceneState = true
-    local internalHudUiSceneState = true
-    local internalHudHiddenState = true
-
-    --[[
-        CurrentHudHiddenState Gets the hidden state of hud/hudui
-    ]]--
-    function CurrentHudHiddenState()
-        return internalHudHiddenState
-    end
-
-    --[[
-        UpdateHiddenState updates the hidden state on base of hud/hudui state
-    ]]--
-    local function UpdateHiddenState()
-		local isHidden = internalHudSceneState and internalHudUiSceneState
-
-        if (isHidden ~= internalHudHiddenState) then
-            internalHudHiddenState = isHidden
-            CALLBACK_MANAGER:FireCallbacks(TUI_HUD_HIDDEN_STATE_CHANGED, isHidden)
-        end
-    end
-
-    --[[
-        HudSceneOnStateChange callback of hud OnStateChange
-    ]]--
-    local function HudSceneOnStateChange(oldState, newState)
-        if (newState == SCENE_HIDING) then
-            internalHudSceneState = true
-			-- make call async to catch both state changes before changing visibility
-			zo_callLater(UpdateHiddenState, 1)
-        elseif (newState == SCENE_SHOWING) then
-            internalHudSceneState = false
-			-- make call async to catch both state changes before changing visibility
-			zo_callLater(UpdateHiddenState, 1)
-        end
-    end
-
-    --[[
-        HudUiSceneOnStateChange callback of hudui OnStateChange
-    ]]--
-    local function HudUiSceneOnStateChange(oldState, newState)
-		if (newState == SCENE_HIDING) then
-            internalHudUiSceneState = true
-			-- make call async to catch both state changes before changing visibility
-			zo_callLater(UpdateHiddenState, 1)
-        elseif (newState == SCENE_SHOWING) then
-            internalHudUiSceneState = false
-			-- make call async to catch both state changes before changing visibility
-			zo_callLater(UpdateHiddenState, 1)
-        end
-    end
-
-    --[[
-        Register callbacks to scenes
-    ]]--
-     -- Reticle Scene
-    SCENE_MANAGER:GetScene("hud"):RegisterCallback("StateChange", HudSceneOnStateChange)
-     -- Mouse Scene
-    SCENE_MANAGER:GetScene("hudui"):RegisterCallback("StateChange", HudUiSceneOnStateChange)
-
+--[[
+	Addon: util
+	Author: TProg Taonnor
+	Created by @Taonnor
+]]--
+
+--[[
+	Class definition (Static class)
+]]--
+-- A table in hole lua workspace must be unique
+-- The ui helper is global util table, used in several of my addons
+-- The table is created as "static" class without constructor and static helper methods
+if (TaosUiHelper == nil) then
+	TaosUiHelper = {}
+	TaosUiHelper.__index = TaosUiHelper
+
+    -- Global Callback Variables
+    TUI_HUD_HIDDEN_STATE_CHANGED = "TUI-HudHiddenStateChange"
+
+	-- isHidden logic for hud scenes
+    -- This logic will fire TUI_HUD_HIDDEN_STATE_CHANGED if hud scenes not visible:
+    -- hud = true; hudui = true -> isHidden = true
+    -- hud = true; hudui = false -> isHidden = false
+    -- hud = false; hudui = true -> isHidden = false
+    -- hud = false; hudui = false -> isHidden = false
+    local internalHudSceneState = true
+    local internalHudUiSceneState = true
+    local internalHudHiddenState = true
+
+    --[[
+        CurrentHudHiddenState Gets the hidden state of hud/hudui
+    ]]--
+    function CurrentHudHiddenState()
+        return internalHudHiddenState
+    end
+
+    --[[
+        UpdateHiddenState updates the hidden state on base of hud/hudui state
+    ]]--
+    local function UpdateHiddenState()
+		local isHidden = internalHudSceneState and internalHudUiSceneState
+
+        if (isHidden ~= internalHudHiddenState) then
+            internalHudHiddenState = isHidden
+            CALLBACK_MANAGER:FireCallbacks(TUI_HUD_HIDDEN_STATE_CHANGED, isHidden)
+        end
+    end
+
+    --[[
+        HudSceneOnStateChange callback of hud OnStateChange
+    ]]--
+    local function HudSceneOnStateChange(oldState, newState)
+        if (newState == SCENE_HIDING) then
+            internalHudSceneState = true
+			-- make call async to catch both state changes before changing visibility
+			zo_callLater(UpdateHiddenState, 1)
+        elseif (newState == SCENE_SHOWING) then
+            internalHudSceneState = false
+			-- make call async to catch both state changes before changing visibility
+			zo_callLater(UpdateHiddenState, 1)
+        end
+    end
+
+    --[[
+        HudUiSceneOnStateChange callback of hudui OnStateChange
+    ]]--
+    local function HudUiSceneOnStateChange(oldState, newState)
+		if (newState == SCENE_HIDING) then
+            internalHudUiSceneState = true
+			-- make call async to catch both state changes before changing visibility
+			zo_callLater(UpdateHiddenState, 1)
+        elseif (newState == SCENE_SHOWING) then
+            internalHudUiSceneState = false
+			-- make call async to catch both state changes before changing visibility
+			zo_callLater(UpdateHiddenState, 1)
+        end
+    end
+
+    --[[
+        Register callbacks to scenes
+    ]]--
+     -- Reticle Scene
+    SCENE_MANAGER:GetScene("hud"):RegisterCallback("StateChange", HudSceneOnStateChange)
+     -- Mouse Scene
+    SCENE_MANAGER:GetScene("hudui"):RegisterCallback("StateChange", HudUiSceneOnStateChange)
+
 end
\ No newline at end of file