Quantcast

Update Libra to v34.

Eyal Shilony [12-03-14 - 11:17]
Update Libra to v34.
Filename
Broker_StartMenu.toc
libs/Libra-27/AceDBControls.lua
libs/Libra-27/Addon.lua
libs/Libra-27/Button.lua
libs/Libra-27/Core.lua
libs/Libra-27/Dropdown.lua
libs/Libra-27/Editbox.lua
libs/Libra-27/Libra.xml
libs/Libra-27/OptionsFrame.lua
libs/Libra-27/ScrollFrame.lua
libs/Libra-27/Slider.lua
libs/Libra-27/UIPanel.lua
libs/Libra-34/AceDBControls.lua
libs/Libra-34/Addon.lua
libs/Libra-34/Button.lua
libs/Libra-34/Core.lua
libs/Libra-34/Dropdown.lua
libs/Libra-34/Editbox.lua
libs/Libra-34/Libra.xml
libs/Libra-34/OptionsFrame.lua
libs/Libra-34/ScrollFrame.lua
libs/Libra-34/Slider.lua
libs/Libra-34/UIPanel.lua
libs/Libra-34/Utils.lua
libs/LibraEx.lua
options/General.lua
options/UI.lua
diff --git a/Broker_StartMenu.toc b/Broker_StartMenu.toc
index 36b5940..0d1b2bf 100644
--- a/Broker_StartMenu.toc
+++ b/Broker_StartMenu.toc
@@ -17,7 +17,8 @@ libs\CallbackHandler-1.0.lua
 libs\LibDataBroker-1.1.lua
 libs\AceLocale-3.0\AceLocale-3.0.xml
 libs\AceDB-3.0\AceDB-3.0.xml
-libs\Libra-27\Libra.xml
+libs\Libra-34\Libra.xml
+libs\LibraEx.lua
 libs\EasyDisplay.lua

 loc\enUS.lua
diff --git a/libs/Libra-27/AceDBControls.lua b/libs/Libra-27/AceDBControls.lua
deleted file mode 100644
index 4f74fe3..0000000
--- a/libs/Libra-27/AceDBControls.lua
+++ /dev/null
@@ -1,346 +0,0 @@
-local Libra = LibStub("Libra")
-local Type, Version = "AceDBControls", 2
-if Libra:GetModuleVersion(Type) >= Version then return end
-
-Libra.modules[Type] = Libra.modules[Type] or {}
-
-local AceDBControls = Libra.modules[Type]
-AceDBControls.Prototype = AceDBControls.Prototype or CreateFrame("Frame")
-
-local Prototype = AceDBControls.Prototype
-local mt = {__index = Prototype}
-
-local L = {
-	default = "Default",
-	reset = "Reset profile",
-	new = "Create new profile",
-	choose = "Active profile",
-	copy = "Copy from",
-	delete = "Delete a profile",
-	delete_confirm = "Are you sure you want to delete the selected profile?",
-	profiles = "Profiles",
-
-	dual_profile = "Dual profile",
-	enabled = "Enable dual profile",
-}
-
-local LOCALE = GetLocale()
-if LOCALE == "deDE" then
-	L["default"] = "Standard"
-	L["reset"] = "Profil zur\195\188cksetzen"
-	L["new"] = "Neu"
-	L["choose"] = "Vorhandene Profile"
-	L["copy"] = "Kopieren von..."
-	L["delete"] = "Profil l\195\182schen"
-	L["delete_confirm"] = "Willst du das ausgew\195\164hlte Profil wirklich l\195\182schen?"
-	L["profiles"] = "Profile"
-
-	L["dual_profile"] = "Duales Profil"
-	L["enabled"] = "Aktiviere Duale Profile"
-elseif LOCALE == "frFR" then
-	L["default"] = "D\195\169faut"
-	L["reset"] = "R\195\169initialiser le profil"
-	L["new"] = "Nouveau"
-	L["choose"] = "Profils existants"
-	L["copy"] = "Copier \195\160 partir de"
-	L["delete"] = "Supprimer un profil"
-	L["delete_confirm"] = "Etes-vous s\195\187r de vouloir supprimer le profil s\195\169lectionn\195\169 ?"
-	L["profiles"] = "Profils"
-
-	L["dual_profile"] = 'Second profil'
-	L["enabled"] = 'Activez le second profil'
-elseif LOCALE == "koKR" then
-	L["default"] = "기본값"
-	L["reset"] = "프로필 초기화"
-	L["new"] = "새로운 프로필"
-	L["choose"] = "프로필 선택"
-	L["copy"] = "복사"
-	L["delete"] = "프로필 삭제"
-	L["delete_confirm"] = "정말로 선택한 프로필의 삭제를 원하십니까?"
-	L["profiles"] = "프로필"
-
-	L["dual_profile"] = "이중 프로필"
-	L["enabled"] = "이중 프로필 사용"
-elseif LOCALE == "esES" or LOCALE == "esMX" then
-	L["default"] = "Por defecto"
-	L["reset"] = "Reiniciar Perfil"
-	L["new"] = "Nuevo"
-	L["choose"] = "Perfiles existentes"
-	L["copy"] = "Copiar de"
-	L["delete"] = "Borrar un Perfil"
-	L["delete_confirm"] = "¿Estas seguro que quieres borrar el perfil seleccionado?"
-	L["profiles"] = "Perfiles"
-elseif LOCALE == "zhTW" then
-	L["default"] = "預設"
-	L["reset"] = "重置設定檔"
-	L["new"] = "新建"
-	L["choose"] = "現有的設定檔"
-	L["copy"] = "複製自"
-	L["delete"] = "刪除一個設定檔"
-	L["delete_confirm"] = "你確定要刪除所選擇的設定檔嗎?"
-	L["profiles"] = "設定檔"
-elseif LOCALE == "zhCN" then
-	L["default"] = "默认"
-	L["reset"] = "重置配置文件"
-	L["choose_desc"] = "你可以通过在文本框内输入一个名字创立一个新的配置文件,也可以选择一个已经存在的配置文件。"
-	L["new"] = "新建"
-	L["choose"] = "现有的配置文件"
-	L["copy"] = "复制自"
-	L["delete"] = "删除一个配置文件"
-	L["delete_confirm"] = "你确定要删除所选择的配置文件么?"
-	L["profiles"] = "配置文件"
-
-	L["dual_profile"] = "双重配置文件"
-	L["enabled"] = "开启双重配置文件"
-elseif LOCALE == "ruRU" then
-	L["default"] = "По умолчанию"
-	L["reset"] = "Сброс профиля"
-	L["new"] = "Новый"
-	L["choose"] = "Существующие профили"
-	L["copy"] = "Скопировать из"
-	L["delete"] = "Удалить профиль"
-	L["delete_confirm"] = "Вы уверены, что вы хотите удалить выбранный профиль?"
-	L["profiles"] = "Профили"
-
-	L["dual_profile"] = "Второй профиль"
-	L["enabled"] = "Включить двойной профиль"
-end
-
-local defaultProfiles = {}
-
-local function profileSort(a, b)
-	return a.value < b.value
-end
-
-local tempProfiles = {}
-
-local function getProfiles(db, common, nocurrent)
-	local profiles = {}
-
-	-- copy existing profiles into the table
-	local currentProfile = db:GetCurrentProfile()
-	for _, v in ipairs(db:GetProfiles(tempProfiles)) do
-		if not (nocurrent and v == currentProfile) then
-			profiles[v] = v
-		end
-	end
-
-	-- add our default profiles to choose from (or rename existing profiles)
-	for k, v in pairs(defaultProfiles) do
-		if (common or profiles[k]) and not (nocurrent and k == currentProfile) then
-			profiles[k] = v
-		end
-	end
-
-	local sortProfiles = {}
-	for k, v in pairs(profiles) do
-		tinsert(sortProfiles, {text = v, value = k})
-	end
-	sort(sortProfiles, profileSort)
-
-	return sortProfiles
-end
-
-local function dropdownOnClick(self, profile, func)
-	func(self.owner.db, profile)
-end
-
-local function initializeDropdown(self, level, menuList)
-	for i, v in ipairs(getProfiles(self.db, self.common, self.nocurrent)) do
-		local info = UIDropDownMenu_CreateInfo()
-		info.text = v.text
-		info.func = dropdownOnClick
-		info.arg1 = v.value
-		info.arg2 = self.func
-		info.checked = not self.nocurrent and (v.value == self.getCurrent(self.db))
-		info.notCheckable = self.nocurrent
-		self:AddButton(info)
-	end
-end
-
-local function createDropdown(parent)
-	local dropdown = Libra:CreateDropdown("Frame", parent)
-	dropdown:SetWidth(160)
-	dropdown:JustifyText("LEFT")
-	dropdown.initialize = initializeDropdown
-	return dropdown
-end
-
-local function menuButton_OnClick(self)
-	self.menu:Toggle()
-end
-
-local function createMenuButton(parent)
-	local button = Libra:CreateButton(parent)
-	button:SetScript("OnClick", menuButton_OnClick)
-	button.arrow:Show()
-	button:SetWidth(88)
-
-	local menu = Libra:CreateDropdown("Menu")
-	menu.relativeTo = button
-	menu.initialize = initializeDropdown
-	menu.nocurrent = true
-	menu.db = parent.db
-	button.menu = menu
-
-	return button
-end
-
-local createProfileScripts = {
-	OnEnterPressed = function(self)
-		self.db:SetProfile(self:GetText())
-		self:ClearFocus()
-	end,
-	OnEditFocusGained = function(self)
-		self:SetTextColor(1, 1, 1)
-	end,
-	OnEditFocusLost = function(self)
-		self:SetTextColor(0.5, 0.5, 0.5)
-		self:SetText("")
-	end,
-}
-
-local function enableDualProfileOnClick(self)
-	local checked = self:GetChecked()
-	self.db:SetDualSpecEnabled(checked)
-	self.dualProfile:SetEnabled(checked)
-end
-
-local function deleteProfile(db, profile)
-	StaticPopup_Show("DELETE_PROFILE", nil, nil, {db = db, profile = profile})
-end
-
-StaticPopupDialogs["DELETE_PROFILE"] = {
-	text = L.delete_confirm,
-	button1 = YES,
-	button2 = NO,
-	OnAccept = function(self, data)
-		data.db:DeleteProfile(data.profile)
-	end,
-}
-
-local function constructor(self, db, parent)
-	local frame = setmetatable(CreateFrame("Frame", nil, parent), mt)
-	frame:SetSize(192, 192)
-	frame.db = db
-
-	db.RegisterCallback(frame, "OnNewProfile")
-	db.RegisterCallback(frame, "OnProfileChanged")
-	db.RegisterCallback(frame, "OnProfileDeleted")
-
-	local keys = db.keys
-	defaultProfiles["Default"] = L.default
-	defaultProfiles[keys.char] = keys.char
-	defaultProfiles[keys.realm] = keys.realm
-	defaultProfiles[keys.class] = UnitClass("player")
-
-	local objects = {}
-
-	do	-- create the controls
-		local choose = createDropdown(frame)
-		choose:SetPoint("TOP")
-		choose.label:SetText(L.choose)
-		choose.func = db.SetProfile
-		choose.getCurrent = db.GetCurrentProfile
-		choose.common = true
-		objects.choose = choose
-
-		local newProfile = Libra:CreateEditbox(frame)
-		newProfile:SetPoint("TOPLEFT", choose, "BOTTOMLEFT", 24, -8)
-		newProfile:SetPoint("TOPRIGHT", choose, "BOTTOMRIGHT", -17, -8)
-		newProfile:SetTextColor(0.5, 0.5, 0.5)
-		newProfile:SetScript("OnEscapePressed", newProfile.ClearFocus)
-		for script, handler in pairs(createProfileScripts) do
-			newProfile:SetScript(script, handler)
-		end
-		objects.newProfile = newProfile
-
-		local label = newProfile:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
-		label:SetHeight(18)
-		label:SetPoint("BOTTOMLEFT", newProfile, "TOPLEFT", -5, -2)
-		label:SetPoint("BOTTOMRIGHT", newProfile, "TOPRIGHT", 0, -2)
-		label:SetJustifyH("LEFT")
-		label:SetText(L.new)
-
-		local copy = createMenuButton(frame)
-		copy:SetPoint("TOPLEFT", newProfile, "BOTTOMLEFT", -9, -4)
-		copy:SetText("Copy from")
-		copy.menu.func = db.CopyProfile
-		objects.copy = copy
-
-		local delete = createMenuButton(frame)
-		delete:SetPoint("TOPRIGHT", newProfile, "BOTTOMRIGHT", 4, -4)
-		delete:SetText("Delete")
-		delete.menu.func = deleteProfile
-		objects.delete = delete
-
-		local reset = Libra:CreateButton(frame)
-		reset:SetPoint("TOPLEFT", copy, "BOTTOM", 0, -4)
-		reset:SetPoint("TOPRIGHT", delete, "BOTTOM", 0, -4)
-		reset:SetScript("OnClick", function(self) self.db:ResetProfile() end)
-		reset:SetText(L.reset)
-		objects.reset = reset
-
-		local hasDualProfile = db:GetNamespace("LibDualSpec-1.0", true)
-		if hasDualProfile then
-			local isDualSpecEnabled = db:IsDualSpecEnabled()
-
-			local dualProfile = createDropdown(frame)
-			dualProfile:SetPoint("TOP", reset, "BOTTOM", 0, -28)
-			dualProfile:SetEnabled(isDualSpecEnabled)
-			dualProfile:SetText(db:GetDualSpecProfile())
-			dualProfile.func = db.SetDualSpecProfile
-			dualProfile.getCurrent = db.GetDualSpecProfile
-			dualProfile.common = true
-			objects.dualProfile = dualProfile
-
-			local enabled = CreateFrame("CheckButton", nil, frame, "OptionsBaseCheckButtonTemplate")
-			enabled:SetPoint("BOTTOMLEFT", dualProfile, "TOPLEFT", 16, 0)
-			enabled:SetPushedTextOffset(0, 0)
-			enabled:SetScript("OnClick", enableDualProfileOnClick)
-			enabled:SetChecked(isDualSpecEnabled)
-			enabled.tooltipText = L.enable_desc
-			enabled.dualProfile = dualProfile
-
-			local text = enabled:CreateFontString(nil, nil, "GameFontHighlight")
-			text:SetPoint("LEFT", enabled, "RIGHT", 0, 1)
-			text:SetText(L.enabled)
-
-			frame.hasDualProfile = true
-		end
-	end
-
-	for k, object in pairs(objects) do
-		object.db = db
-		frame[k] = object
-	end
-
-	frame.choose:SetText(db:GetCurrentProfile())
-
-	frame:CheckProfiles()
-
-	return frame
-end
-
-function Prototype:CheckProfiles()
-	local hasProfiles = not self:HasNoProfiles()
-	self.copy:SetEnabled(hasProfiles)
-	self.delete:SetEnabled(hasProfiles)
-end
-
-function Prototype:HasNoProfiles()
-	return next(getProfiles(self.db, nil, true)) == nil
-end
-
-function Prototype:OnProfileChanged(event, db, profile)
-	self.choose:SetText(profile)
-	self:CheckProfiles()
-	if self.hasDualProfile then
-		self.dualProfile:SetText(db:GetDualSpecProfile())
-	end
-end
-
-Prototype.OnNewProfile = Prototype.CheckProfiles
-Prototype.OnProfileDeleted = Prototype.CheckProfiles
-
-Libra:RegisterModule(Type, Version, constructor)
\ No newline at end of file
diff --git a/libs/Libra-27/Addon.lua b/libs/Libra-27/Addon.lua
deleted file mode 100644
index 41fe5c0..0000000
--- a/libs/Libra-27/Addon.lua
+++ /dev/null
@@ -1,152 +0,0 @@
-local Libra = LibStub("Libra")
-local Type, Version = "Addon", 3
-if Libra:GetModuleVersion(Type) >= Version then return end
-
-Libra.modules[Type] = Libra.modules[Type] or {}
-
-local object = Libra.modules[Type]
-object.frame = object.frame or CreateFrame("Frame")
-object.addons = object.addons or {}
-object.events = object.events or {}
-object.onUpdates = object.onUpdates or {}
-
-local function safecall(object, method, ...)
-	if object[method] then
-		object[method](object, ...)
-	end
-end
-
-object.frame:RegisterEvent("ADDON_LOADED")
-object.frame:SetScript("OnEvent", function(self, event, ...)
-	if event == "ADDON_LOADED" then
-		local addon = object.addons[...]
-		if addon then
-			safecall(addon, "OnInitialize")
-			addon.OnInitialize = nil
-			for i, module in addon:IterateModules() do
-				safecall(module, "OnInitialize")
-				module.OnInitialize = nil
-			end
-		end
-	end
-	for module, eventHandler in pairs(object.events[event]) do
-		eventHandler(module, ...)
-	end
-end)
-
-local function onUpdate(self, elapsed)
-	for module, update in pairs(object.onUpdates) do
-		update(module, elapsed)
-	end
-end
-
-setmetatable(object.events, {
-	__index = function(table, key)
-		local newTable = {}
-		table[key] = newTable
-		return newTable
-	end
-})
-
-local AddonPrototype = {}
-local ObjectPrototype = {}
-
-local function AddonEmbed(target)
-	for k, v in pairs(AddonPrototype) do
-		target[k] = v
-	end
-end
-
-local function ObjectEmbed(target)
-	for k, v in pairs(ObjectPrototype) do
-		target[k] = v
-	end
-end
-
-function Libra:NewAddon(name, addonObject)
-	if object.addons[name] then
-		error(format("Addon '%s' already exists.", name), 2)
-	end
-
-	local addon = addonObject or {}
-	addon.name = name
-	addon.modules = {}
-	AddonEmbed(addon)
-	ObjectEmbed(addon)
-	object.addons[name] = addon
-	return addon, name
-end
-
-function Libra:GetAddon(name)
-	return object.addons[name]
-end
-
-function AddonPrototype:NewModule(name, table)
-	if self:GetModule(name) then
-		error(format("Module '%s' already exists in %s.", name, self.name), 2)
-	end
-
-	local module = table or {}
-	ObjectEmbed(module)
-	module.name = name
-	tinsert(self.modules, module)
-	safecall(self, "OnModuleCreated", name, module)
-	return module, name
-end
-
-function AddonPrototype:GetModule(name)
-	for i, module in self:IterateModules() do
-		if module.name == name then
-			return module
-		end
-	end
-end
-
-function AddonPrototype:IterateModules()
-	return next, self.modules
-end
-
-function ObjectPrototype:RegisterEvent(event, handler)
-	if not next(object.events[event]) then
-		object.frame:RegisterEvent(event)
-	end
-	if type(handler) ~= "function" then
-		handler = self[handler] or self[event]
-	end
-	object.events[event][self] = handler
-end
-
-function ObjectPrototype:UnregisterEvent(event)
-	object.events[event][self] = nil
-	if not next(object.events[event]) then
-		object.frame:UnregisterEvent(event)
-	end
-end
-
-function ObjectPrototype:SetOnUpdate(handler)
-	if not next(object.onUpdates) then
-		object.frame:SetScript("OnUpdate", onUpdate)
-	end
-	if type(handler) ~= "function" then
-		handler = self[handler]
-	end
-	object.onUpdates[self] = handler
-end
-
-function ObjectPrototype:RemoveOnUpdate()
-	object.onUpdates[self] = nil
-	if not next(object.onUpdates) then
-		object.frame:SetScript("OnUpdate", nil)
-	end
-end
-
--- upgrade embeds
-for k, v in pairs(object.addons) do
-	AddonEmbed(v)
-	ObjectEmbed(v)
-	for i, v in v:IterateModules() do
-		ObjectEmbed(v)
-	end
-end
-
-Libra:RegisterModule(Type, Version)
\ No newline at end of file
diff --git a/libs/Libra-27/Button.lua b/libs/Libra-27/Button.lua
deleted file mode 100644
index 9ee43a7..0000000
--- a/libs/Libra-27/Button.lua
+++ /dev/null
@@ -1,27 +0,0 @@
-local Libra = LibStub("Libra")
-local Type, Version = "Button", 2
-if Libra:GetModuleVersion(Type) >= Version then return end
-
-local function onEnable(self)
-	self.arrow:SetDesaturated(false)
-end
-
-local function onDisable(self)
-	self.arrow:SetDesaturated(true)
-end
-
-local function constructor(self, parent)
-	local button = CreateFrame("Button", nil, parent, "UIMenuButtonStretchTemplate")
-	button:SetHeight(23)
-	button:SetScript("OnEnable", onEnable)
-	button:SetScript("OnDisable", onDisable)
-	button.arrow = button:CreateTexture()
-	button.arrow:SetSize(10, 12)
-	button.arrow:SetPoint("RIGHT", -5, 0)
-	button.arrow:SetTexture([[Interface\ChatFrame\ChatFrameExpandArrow]])
-	button.arrow:Hide()
-	button.Icon = button.arrow
-	return button
-end
-
-Libra:RegisterModule(Type, Version, constructor)
\ No newline at end of file
diff --git a/libs/Libra-27/Core.lua b/libs/Libra-27/Core.lua
deleted file mode 100644
index 109a07f..0000000
--- a/libs/Libra-27/Core.lua
+++ /dev/null
@@ -1,61 +0,0 @@
-local MAJOR, MINOR = "Libra", 1
-local lib = LibStub:NewLibrary(MAJOR, MINOR)
-
-if not lib then return end
-
-lib.modules = lib.modules or {}
-lib.moduleVersions = lib.moduleVersions or {}
-lib.widgets = lib.widgets or {}
-lib.widgetEmbeds = lib.widgetEmbeds or {}
-lib.namespaces = lib.namespaces or {}
-
-function lib:RegisterModule(object, version, constructor)
-	self.moduleVersions[object] = version
-	if constructor then
-		self.widgets[object] = constructor
-		self["Create"..object] = constructor
-		for k in pairs(self.widgetEmbeds) do
-			k["Create"..object] = constructor
-		end
-	end
-end
-
-function lib:GetModuleVersion(module)
-	return self.moduleVersions[module] or 0
-end
-
-function lib:Create(objectType, ...)
-	return lib.widgets[objectType](self, ...)
-end
-
-function lib:GetWidgetName(name)
-	name = name or "Generic"
-	local namespace = self.namespaces[name]
-	if not namespace then
-		local n = 0
-		namespace = function()
-			n = n + 1
-			return format("%sLibraWidget%d", name, n)
-		end
-		self.namespaces[name] = namespace
-	end
-	return namespace()
-end
-
-local mixins = {
-	"Create",
-}
-
-function lib:EmbedWidgets(target)
-	-- for i, v in ipairs(mixins) do
-		-- target[v] = self[v]
-	-- end
-	for k, v in pairs(self.widgets) do
-		target["Create"..k] = v
-	end
-	self.widgetEmbeds[target] = true
-end
-
-for k in pairs(lib.widgetEmbeds) do
-	lib:EmbedWidgets(k)
-end
\ No newline at end of file
diff --git a/libs/Libra-27/Dropdown.lua b/libs/Libra-27/Dropdown.lua
deleted file mode 100644
index ea80487..0000000
--- a/libs/Libra-27/Dropdown.lua
+++ /dev/null
@@ -1,548 +0,0 @@
-local Libra = LibStub("Libra")
-local Type, Version = "Dropdown", 12
-if Libra:GetModuleVersion(Type) >= Version then return end
-
-Libra.modules[Type] = Libra.modules[Type] or {}
-
-local Dropdown = Libra.modules[Type]
-Dropdown.Prototype = Dropdown.Prototype or CreateFrame("Frame")
-Dropdown.MenuPrototype = Dropdown.MenuPrototype or setmetatable({}, {__index = Dropdown.Prototype})
-Dropdown.FramePrototype = Dropdown.FramePrototype or setmetatable({}, {__index = Dropdown.Prototype})
-Dropdown.objects = Dropdown.objects or {}
-Dropdown.listData = Dropdown.listData or {}
-Dropdown.hookedLists = Dropdown.hookedLists or {}
-Dropdown.hookedButtons = Dropdown.hookedButtons or {}
-Dropdown.secureButtons = Dropdown.secureButtons or {}
-Dropdown.secureBin = Dropdown.secureBin or {}
-
-local menuMT = {__index = Dropdown.MenuPrototype}
-local frameMT = {__index = Dropdown.FramePrototype}
-
-local Prototype = Dropdown.Prototype
-local MenuPrototype = Dropdown.MenuPrototype
-local FramePrototype = Dropdown.FramePrototype
-local objects = Dropdown.objects
-local listData = Dropdown.listData
-
-local function setHeight() end
-
-local function constructor(self, type, parent, name)
-	local dropdown
-	if type == "Menu" then
-		-- adding a SetHeight dummy lets us use a simple table instead of a frame, no side effects noticed so far
-		dropdown = setmetatable({}, menuMT)
-		dropdown:SetDisplayMode("MENU")
-		dropdown.SetHeight = setHeight
-		dropdown.xOffset = 0
-		dropdown.yOffset = 0
-	end
-	if type == "Frame" then
-		name = name or Libra:GetWidgetName(self.name)
-		dropdown = setmetatable(CreateFrame("Frame", name, parent, "UIDropDownMenuTemplate"), frameMT)
-		dropdown:SetWidth(115)
-		dropdown.label = dropdown:CreateFontString(name.."Label", "BACKGROUND", "GameFontNormalSmall")
-		dropdown.label:SetPoint("BOTTOMLEFT", dropdown, "TOPLEFT", 16, 3)
-	end
-
-	objects[dropdown] = true
-
-	return dropdown
-end
-
-
-local methods = {
-	Refresh = UIDropDownMenu_Refresh,
-}
-
-for k, v in pairs(methods) do
-	Prototype[k] = v
-end
-
-function Prototype:AddButton(info, level)
-	info.owner = self
-	if info.icon and not info.iconOnly then
-		-- hack to properly increase button width for icon when .iconOnly is not set
-		info.padding = (info.padding or 0) + 10
-	end
-	self.displayMode = self._displayMode
-	self.selectedName = self._selectedName
-	self.selectedValue = self._selectedValue
-	self.selectedID = self._selectedID
-	UIDropDownMenu_AddButton(info, level)
-	self.displayMode = nil
-	self.selectedName = nil
-	self.selectedValue = nil
-	self.selectedID = nil
-end
-
-function Prototype:ToggleMenu(value, anchorName, xOffset, yOffset, menuList, level, ...)
-	ToggleDropDownMenu(level, value, self, anchorName, xOffset, yOffset, menuList, ...)
-end
-
-function Prototype:RebuildMenu(level)
-	level = level or 1
-	if self:IsMenuShown(level) then
-		-- hiding a menu will also hide all deeper level menus, so we'll check which ones are open and restore them afterwards
-		local maxLevel
-		for i = level, UIDROPDOWNMENU_MENU_LEVEL do
-			if _G["DropDownList"..i]:IsShown() then
-				maxLevel = i
-			else
-				break
-			end
-		end
-		self:HideMenu(level)
-		for i = level, maxLevel do
-			local listData = listData[i]
-			-- set .rebuild to indicate that we don't want to reset the scroll offset on the next ToggleDropDownMenu
-			self.rebuild = true
-			self:ToggleMenu(listData.value, listData.anchorName, listData.xOffset, listData.yOffset, listData.menuList, i, listData.button, listData.autoHideDelay)
-		end
-	end
-end
-
-function Prototype:HideMenu(level)
-	if UIDropDownMenu_GetCurrentDropDown() == self then
-		HideDropDownMenu(level)
-	end
-end
-
-function Prototype:CloseMenus(level)
-	if UIDropDownMenu_GetCurrentDropDown() == self then
-		CloseDropDownMenus(level)
-	end
-end
-
-function Prototype:IsMenuShown(level)
-	level = level or 1
-	local listFrame = _G["DropDownList"..level]
-	return UIDropDownMenu_GetCurrentDropDown() == self and listFrame and listFrame:IsShown()
-end
-
-function Prototype:SetSelectedName(name, useValue)
-	self._selectedName = name
-	self._selectedValue = nil
-	self._selectedID = nil
-	self.selectedName = name
-	self:Refresh(useValue)
-	self.selectedName = nil
-end
-
-function Prototype:SetSelectedValue(value, useValue)
-	self._selectedValue = value
-	self._selectedName = nil
-	self._selectedID = nil
-	self.selectedValue = value
-	self:Refresh(useValue)
-	self.selectedValue = nil
-end
-
-function Prototype:SetSelectedID(id, useValue)
-	self._selectedID = id
-	self._selectedName = nil
-	self._selectedValue = nil
-	self.selectedID = id
-	self:Refresh(useValue)
-	self.selectedID = nil
-end
-
-function Prototype:GetSelectedName()
-	return self._selectedName
-end
-
-function Prototype:GetSelectedValue()
-	return self._selectedValue
-end
-
-function Prototype:GetSelectedID()
-	if self._selectedID then
-		return self._selectedID
-	else
-		-- If no explicit selectedID then try to send the id of a selected value or name
-		for i=1, UIDROPDOWNMENU_MAXBUTTONS do
-			local button = _G["DropDownList"..UIDROPDOWNMENU_MENU_LEVEL.."Button"..i]
-			-- See if checked or not
-			if self:GetSelectedName() then
-				if button:GetText() == self:GetSelectedName() then
-					return i
-				end
-			elseif self:GetSelectedValue() then
-				if button.value == self:GetSelectedValue() then
-					return i
-				end
-			end
-		end
-	end
-end
-
-function Prototype:SetDisplayMode(mode)
-	self._displayMode = mode
-end
-
-
-local menuMethods = {
-	Toggle = Prototype.ToggleMenu,
-	Rebuild = Prototype.RebuildMenu,
-	Hide = Prototype.HideMenu,
-	Close = Prototype.CloseMenus,
-	IsShown = Prototype.IsMenuShown,
-}
-
-for k, v in pairs(menuMethods) do
-	MenuPrototype[k] = v
-end
-
-
-local frameMethods = {
-	Enable = UIDropDownMenu_EnableDropDown,
-	Disable = UIDropDownMenu_DisableDropDown,
-	IsEnabled = UIDropDownMenu_IsEnabled,
-	JustifyText = UIDropDownMenu_JustifyText,
-	SetButtonWidth = UIDropDownMenu_SetButtonWidth,
-	SetText = UIDropDownMenu_SetText,
-	GetText = UIDropDownMenu_GetText,
-}
-
-for k, v in pairs(frameMethods) do
-	FramePrototype[k] = v
-end
-
-local setWidth = Prototype.SetWidth
-
-function FramePrototype:SetWidth(width, padding)
-	_G[self:GetName().."Middle"]:SetWidth(width)
-	local defaultPadding = 25
-	if padding then
-		setWidth(self, width + padding)
-		_G[self:GetName().."Text"]:SetWidth(width)
-	else
-		setWidth(self, width + defaultPadding + defaultPadding)
-		_G[self:GetName().."Text"]:SetWidth(width - defaultPadding)
-	end
-	self.noResize = 1
-end
-
-function FramePrototype:SetLabel(text)
-	self.label:SetText(text)
-end
-
-function FramePrototype:SetEnabled(enable)
-	if enable then
-		self:Enable()
-	else
-		self:Disable()
-	end
-end
-
-
-local numShownButtons
-
-local function update(level)
-	local scroll = listData[level].scroll
-	for i = 1, UIDROPDOWNMENU_MAXBUTTONS do
-		local button = _G["DropDownList"..level.."Button"..i]
-		local _, _, _, x, y = button:GetPoint()
-		local y = -((button:GetID() - 1 - scroll) * UIDROPDOWNMENU_BUTTON_HEIGHT) - UIDROPDOWNMENU_BORDER_HEIGHT
-		button:SetPoint("TOPLEFT", x, y)
-		button:SetShown(i > scroll and i <= (numShownButtons + scroll))
-	end
-	Dropdown.scrollButtons[level].up:SetShown(scroll > 0)
-	Dropdown.scrollButtons[level].down:SetShown(scroll < _G["DropDownList"..level].numButtons - numShownButtons)
-end
-
-local function scroll(self, delta)
-	local level = self:GetID()
-	local listData = listData[level]
-	delta = (type(delta) == "number" and delta or self.delta)
-	if IsShiftKeyDown() then delta = delta * (numShownButtons - 1) end
-	listData.scroll = listData.scroll - (type(delta) == "number" and delta or self.delta)
-	listData.scroll = min(listData.scroll, (self.numButtons or self:GetParent().numButtons) - numShownButtons)
-	listData.scroll = max(listData.scroll, 0)
-	update(level)
-end
-
-local scrollScripts = {
-	OnEnter = function(self)
-		UIDropDownMenu_StopCounting(self:GetParent())
-	end,
-	OnLeave = function(self)
-		UIDropDownMenu_StartCounting(self:GetParent())
-	end,
-	OnMouseDown = function(self)
-		self.texture:SetPoint("CENTER", 1, -1)
-	end,
-	OnMouseUp = function(self)
-		self.texture:SetPoint("CENTER")
-	end,
-	OnHide = function(self)
-		self.texture:SetPoint("CENTER")
-		-- explicitly hide so that they are hidden for unmanaged dropdowns
-		self:Hide()
-	end,
-}
-
-local function createScrollButton(listFrame)
-	local level = listFrame:GetID()
-	local button = CreateFrame("Button", nil, listFrame)
-	button:SetSize(16, 16)
-	button:SetScript("OnClick", scroll)
-	for script, handler in pairs(scrollScripts) do
-		button:SetScript(script, handler)
-	end
-	button:SetID(level)
-	button.texture = button:CreateTexture()
-	button.texture:SetSize(16, 16)
-	button.texture:SetPoint("CENTER")
-	button.texture:SetTexture([[Interface\Calendar\MoreArrow]])
-	return button
-end
-
-local function createScrollButtons(listFrame)
-	local scrollUp = listFrame.scrollUp or createScrollButton(listFrame)
-	scrollUp:SetPoint("TOP")
-	scrollUp.delta = 1
-	scrollUp.texture:SetTexCoord(0, 1, 1, 0)
-	listFrame.scrollUp = scrollUp
-
-	local scrollDown = listFrame.scrollDown or createScrollButton(listFrame)
-	scrollDown:SetPoint("BOTTOM")
-	scrollDown.delta = -1
-	listFrame.scrollDown = scrollDown
-end
-
-Dropdown.scrollButtons = Dropdown.scrollButtons or setmetatable({}, {
-	__index = function(self, level)
-		local listFrame = _G["DropDownList"..level]
-		createScrollButtons(listFrame)
-		self[level] = {
-			up = listFrame.scrollUp,
-			down = listFrame.scrollDown,
-		}
-		return self[level]
-	end,
-})
-
-function Dropdown:ToggleDropDownMenuHook(level, value, dropdownFrame, anchorName, xOffset, yOffset, menuList, button, autoHideDelay)
-	level = level or 1
-	if level ~= 1 then
-		dropdownFrame = dropdownFrame or UIDROPDOWNMENU_OPEN_MENU
-	end
-	local listFrameName = "DropDownList"..level
-	local listFrame = _G[listFrameName]
-	if not objects[dropdownFrame] then
-		listFrame:SetScript("OnMouseWheel", nil)
-		return
-	end
-	if dropdownFrame and dropdownFrame._displayMode == "MENU" then
-		_G[listFrameName.."Backdrop"]:Hide()
-		_G[listFrameName.."MenuBackdrop"]:Show()
-	end
-
-	-- store all parameters per level so we can use them to rebuild the menu
-	listData[level] = listData[level] or {}
-	local listData = listData[level]
-	listData.value = value
-	listData.anchorName = anchorName
-	listData.xOffset = xOffset
-	listData.yOffset = yOffset
-	listData.menuList = menuList
-	listData.button = button
-	listData.autoHideDelay = autoHideDelay
-
-	numShownButtons = dropdownFrame.numShownButtons or floor((UIParent:GetHeight() - UIDROPDOWNMENU_BORDER_HEIGHT * 2) / UIDROPDOWNMENU_BUTTON_HEIGHT)
-	local scrollable = numShownButtons < listFrame.numButtons
-	if scrollable then
-		-- make scrollable
-		listData.scroll = listData.scroll or 0
-		if not dropdownFrame.rebuild then
-			listData.scroll = 0
-		end
-		listFrame:SetScript("OnMouseWheel", scroll)
-		listFrame:SetHeight((numShownButtons * UIDROPDOWNMENU_BUTTON_HEIGHT) + (UIDROPDOWNMENU_BORDER_HEIGHT * 2))
-		if listFrame:GetTop() > GetScreenHeight() then
-			local point, anchorFrame, relativePoint, x, y = listFrame:GetPoint()
-			local offTop = (GetScreenHeight() - listFrame:GetTop())-- / listFrame:GetScale()
-			listFrame:SetPoint(point, anchorFrame, relativePoint, x, y + offTop)
-		end
-		update(level)
-	else
-		if listFrame:GetTop() > GetScreenHeight() then
-			local point, anchorFrame, relativePoint, x, y = listFrame:GetPoint()
-			local offTop = (GetScreenHeight() - listFrame:GetTop())-- / listFrame:GetScale()
-			listFrame:SetPoint(point, anchorFrame, relativePoint, x, y + offTop)
-		end
-		listFrame:SetScript("OnMouseWheel", nil)
-		self.scrollButtons[level].up:Hide()
-		self.scrollButtons[level].down:Hide()
-	end
-	dropdownFrame.rebuild = nil
-end
-
-if not Dropdown.hookToggleDropDownMenu then
-	hooksecurefunc("ToggleDropDownMenu", function(...)
-		Dropdown:ToggleDropDownMenuHook(...)
-	end)
-	Dropdown.hookToggleDropDownMenu = true
-end
-
-function Dropdown:AddButtonHook(info, level)
-	if not objects[UIDropDownMenu_GetCurrentDropDown()] then return end
-	local listFrameName = "DropDownList"..(level or 1)
-	local listFrame = _G[listFrameName]
-	local button = _G[listFrameName.."Button"..(listFrame.numButtons)]
-	button.onEnter = info.onEnter
-	button.onLeave = info.onLeave
-	button.tooltipLines = info.tooltipLines
-	if info.attributes and not InCombatLockdown() then
-		local secureButton = self.secureBin[1]
-		tremove(Dropdown.secureBin, 1)
-		-- since this is a separate button, we need to set the disabled state on it too
-		secureButton:SetEnabled(not info.disabled)
-		secureButton:SetParent(button)
-		secureButton:SetAllPoints()
-		secureButton:Show()
-		-- clear existing attributes
-		for attribute in pairs(secureButton.attributes) do
-			secureButton:SetAttribute(attribute, nil)
-			secureButton.attributes[attribute] = nil
-		end
-		for attribute, value in pairs(info.attributes) do
-			secureButton:SetAttribute(attribute, value)
-			secureButton.attributes[attribute] = true
-		end
-		tinsert(Dropdown.secureButtons, secureButton)
-	end
-end
-
-if not Dropdown.hookAddButton then
-	hooksecurefunc("UIDropDownMenu_AddButton", function(...)
-		Dropdown:AddButtonHook(...)
-	end)
-	Dropdown.hookAddButton = true
-end
-
-local function onEnter(self)
-	if self.onEnter then
-		self:onEnter()
-	elseif self.tooltipLines and self.tooltipTitle then
-		GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
-		GameTooltip:AddLine(self.tooltipTitle, HIGHLIGHT_FONT_COLOR.r, HIGHLIGHT_FONT_COLOR.g, HIGHLIGHT_FONT_COLOR.b)
-		if self.tooltipText then
-			for line in self.tooltipText:gmatch("[^\n]+") do
-				GameTooltip:AddLine(line)
-			end
-		end
-		GameTooltip:Show()
-	end
-end
-
-local function onLeave(self)
-	if self.onLeave then
-		self:onLeave()
-	end
-end
-
-local function invisibleButtonOnEnter(self)
-	local parent = self:GetParent()
-	if parent.onEnter or parent.tooltipWhileDisabled then
-		onEnter(parent)
-	end
-end
-
-local function invisibleButtonOnLeave(self)
-	local parent = self:GetParent()
-	if parent.onLeave or parent.tooltipWhileDisabled then
-		onLeave(parent)
-	end
-end
-
-local function listOnHide(self)
-	if not InCombatLockdown() then
-		for i = #Dropdown.secureButtons, 1, -1 do
-			-- hide secure buttons attached to this list frame
-			local button = Dropdown.secureButtons[i]
-			if button:GetParent():GetParent() == self then
-				Dropdown:DismissSecureButton(button)
-				tremove(Dropdown.secureButtons, i)
-			end
-		end
-	end
-end
-
-function Dropdown:CreateFramesHook(numLevels, numButtons)
-	for level = 1, numLevels do
-		if not self.hookedLists[level] then
-			_G["DropDownList"..level]:HookScript("OnHide", listOnHide)
-			self.hookedLists[level] = true
-		end
-		self.hookedButtons[level] = self.hookedButtons[level] or {}
-		for i = 1, numButtons do
-			if not self.hookedButtons[level][i] then
-				_G["DropDownList"..level.."Button"..i]:HookScript("OnEnter", onEnter)
-				_G["DropDownList"..level.."Button"..i]:HookScript("OnLeave", onLeave)
-				_G["DropDownList"..level.."Button"..i.."InvisibleButton"]:HookScript("OnEnter", invisibleButtonOnEnter)
-				_G["DropDownList"..level.."Button"..i.."InvisibleButton"]:HookScript("OnLeave", invisibleButtonOnLeave)
-				self.hookedButtons[level][i] = true
-			end
-		end
-	end
-end
-
-if not Dropdown.hookCreateFrames then
-	Dropdown:CreateFramesHook(UIDROPDOWNMENU_MAXLEVELS, UIDROPDOWNMENU_MAXBUTTONS)
-	hooksecurefunc("UIDropDownMenu_CreateFrames", function(...)
-		Dropdown:CreateFramesHook(...)
-	end)
-	Dropdown.hookCreateFrames = true
-end
-
--- script handlers to mimic regular dropdown button behaviour
-local scripts = {
-	PreClick = function(self)
-		local parent = self:GetParent()
-		parent:GetScript("OnClick")(parent)
-	end,
-	OnMouseDown = function(self)
-		self:GetParent():SetButtonState("PUSHED")
-	end,
-	OnMouseUp = function(self)
-		self:GetParent():SetButtonState("NORMAL")
-	end,
-	OnEnter = function(self)
-		local parent = self:GetParent()
-		parent:GetScript("OnEnter")(parent)
-	end,
-	OnLeave = function(self)
-		local parent = self:GetParent()
-		parent:GetScript("OnLeave")(parent)
-	end,
-}
-
-setmetatable(Dropdown.secureBin, {
-	__index = function(self, index)
-		local button = CreateFrame("Button", nil, nil, "SecureActionButtonTemplate")
-		for script, handler in pairs(scripts) do
-			button:SetScript(script, handler)
-		end
-		button.attributes = {}
-		return button
-	end,
-})
-
-function Dropdown:DismissSecureButton(button)
-	button:Hide()
-	button:ClearAllPoints()
-	button:SetParent(nil)
-	tinsert(Dropdown.secureBin, button)
-end
-
-Dropdown.frame = Dropdown.frame or CreateFrame("Frame")
-Dropdown.frame:RegisterEvent("PLAYER_REGEN_DISABLED")
-Dropdown.frame:SetScript("OnEvent", function(self)
-	for i, button in ipairs(Dropdown.secureButtons) do
-		Dropdown:DismissSecureButton(button)
-	end
-	wipe(Dropdown.secureButtons)
-end)
-
-Libra:RegisterModule(Type, Version, constructor)
\ No newline at end of file
diff --git a/libs/Libra-27/Editbox.lua b/libs/Libra-27/Editbox.lua
deleted file mode 100644
index 8c41e7c..0000000
--- a/libs/Libra-27/Editbox.lua
+++ /dev/null
@@ -1,29 +0,0 @@
-local Libra = LibStub("Libra")
-local Type, Version = "Editbox", 2
-if Libra:GetModuleVersion(Type) >= Version then return end
-
-local function onEditFocusGained(self)
-	self:SetTextColor(1, 1, 1)
-end
-
-local function onEditFocusLost(self)
-	self:SetFontObject("ChatFontSmall")
-	self:SetTextColor(0.5, 0.5, 0.5)
-end
-
-local function constructor(self, parent, isSearchBox)
-	local name = Libra:GetWidgetName(self.name)
-	local editbox = CreateFrame("EditBox", name, parent, isSearchBox and "SearchBoxTemplate" or "InputBoxTemplate")
-	editbox:SetHeight(20)
-	editbox:SetAutoFocus(false)
-	editbox:SetFontObject("ChatFontSmall")
-	if isSearchBox then
-		editbox:SetTextColor(0.5, 0.5, 0.5)
-		editbox:HookScript("OnEditFocusGained", onEditFocusGained)
-		editbox:HookScript("OnEditFocusLost", onEditFocusLost)
-	end
-	_G[name] = nil
-	return editbox
-end
-
-Libra:RegisterModule(Type, Version, constructor)
\ No newline at end of file
diff --git a/libs/Libra-27/Libra.xml b/libs/Libra-27/Libra.xml
deleted file mode 100644
index 5282103..0000000
--- a/libs/Libra-27/Libra.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/
-..\FrameXML\UI.xsd">
-	<Script file="Core.lua"/>
-
-	<Script file="Addon.lua"/>
-	<Script file="UIPanel.lua"/>
-	<Script file="AceDBControls.lua"/>
-	<Script file="Button.lua"/>
-	<Script file="Dropdown.lua"/>
-	<Script file="Editbox.lua"/>
-	<Script file="OptionsFrame.lua"/>
-	<Script file="ScrollFrame.lua"/>
-	<Script file="Slider.lua"/>
-	<Script file="Templates.lua"/>
-</Ui>
\ No newline at end of file
diff --git a/libs/Libra-27/OptionsFrame.lua b/libs/Libra-27/OptionsFrame.lua
deleted file mode 100644
index aedba80..0000000
--- a/libs/Libra-27/OptionsFrame.lua
+++ /dev/null
@@ -1,497 +0,0 @@
-local Libra = LibStub("Libra")
-local Type, Version = "OptionsFrame", 3
-if Libra:GetModuleVersion(Type) >= math.huge then return end
-
-Libra.modules[Type] = Libra.modules[Type] or {}
-
-local Options = Libra.modules[Type]
-
-Options.Prototype = Options.Prototype or CreateFrame("Frame")
-Options.ParentPrototype = Options.ParentPrototype or {}
-
-local mt = {__index = Options.Prototype}
-local parentMT = {__index = setmetatable(Options.ParentPrototype, {__index = Options.Prototype})}
-
-local Prototype = Options.Prototype
-local ParentPrototype = Options.ParentPrototype
-
-local function createFrame(name, parent)
-	local frame = CreateFrame("Frame")
-	frame.name = name
-	frame.parent = parent
-	InterfaceOptions_AddCategory(frame)
-
-	local title = frame:CreateFontString(nil, nil, "GameFontNormalLarge")
-	title:SetPoint("TOPLEFT", 16, -16)
-	title:SetPoint("RIGHT", -16, 0)
-	title:SetJustifyH("LEFT")
-	title:SetJustifyV("TOP")
-	title:SetText(name)
-	frame.title = title
-
-	local desc = frame:CreateFontString(nil, nil, "GameFontHighlightSmall")
-	desc:SetHeight(32)
-	desc:SetPoint("TOPLEFT", frame.title, "BOTTOMLEFT", 0, -8)
-	desc:SetPoint("RIGHT", -31, 0)
-	desc:SetJustifyH("LEFT")
-	desc:SetJustifyV("TOP")
-	desc:SetNonSpaceWrap(true)
-	frame.desc = desc
-
-	return frame
-end
-
-local function constructor(self, name)
-	local frame = setmetatable(createFrame(name), parentMT)
-	frame.controls = {}
-	frame.allcontrols = {}
-	return frame
-end
-
-
-function ParentPrototype:AddSubCategory(name, inherit)
-	local frame = setmetatable(createFrame(name, self.name), mt)
-	if inherit then
-		frame.db = self.db
-		frame.useProfile = self.useProfile
-		frame.handler = self.handler
-		frame.allcontrols = self.allcontrols
-	else
-		frame.allcontrols = {}
-	end
-	frame.inherit = inherit
-	frame.controls = {}
-	self.subCategories = self.subCategories or {}
-	tinsert(self.subCategories, frame)
-	return frame
-end
-
-function Prototype:SetDescription(text)
-	self.desc:SetText(text)
-end
-
-function Prototype:SetDatabase(database, useProfile)
-	self.db = database
-	self.useProfile = useProfile
-	if self.subCategories then
-		for i, v in ipairs(self.subCategories) do
-			if v.inherit then
-				v.db = database
-			end
-		end
-	end
-end
-
-function Prototype:SetHandler(tbl)
-	self.handler = tbl
-	if self.subCategories then
-		for i, v in ipairs(self.subCategories) do
-			if v.inherit then
-				v.handler = tbl
-			end
-		end
-	end
-end
-
-
-local function getTable(control)
-	local tbl = control.parent.db
-	if control.parent.useProfile then
-		tbl = tbl.profile
-	end
-	if control.keyTable then
-		tbl = tbl[control.keyTable]
-	end
-	return tbl
-end
-
-local function getFunc(control, method, key, value)
-	local func = control[method]
-	if func then
-		local object = control
-		if type(func) == "string" then
-			object = control.parent.handler
-			func = object[func]
-		end
-		if key then
-			return true, func(object, key, value)
-		else
-			return true, func(object, value)
-		end
-		return true
-	end
-end
-
-local function set(self, value, key)
-	if not getFunc(self, "set", key, value) then
-		local tbl = getTable(self)
-		if tbl then
-			tbl[key or self.key] = value
-		end
-	end
-	getFunc(self, "func", key, value)
-	for key, control in pairs(self.parent.allcontrols) do
-		if control.disabled then
-			control:SetEnabled(not control.disabled())
-		end
-	end
-end
-
-local function get(self, key)
-	local hasFunc, value = getFunc(self, "get", key)
-	if hasFunc then
-		return value
-	else
-		local tbl = getTable(self)
-		if tbl then
-			return tbl[key or self.key]
-		end
-	end
-end
-
-local controls = {}
-
-do
-	local function onClick(self)
-		local checked = self:GetChecked()
-		PlaySound(checked and "igMainMenuOptionCheckBoxOn" or "igMainMenuOptionCheckBoxOff")
-		set(self, checked)
-	end
-
-	controls.CheckButton = function(parent)
-		local checkButton = CreateFrame("CheckButton", nil, parent, "OptionsBaseCheckButtonTemplate")
-		checkButton:SetNormalFontObject("GameFontHighlight")
-		checkButton:SetDisabledFontObject("GameFontDisable")
-		checkButton:SetPushedTextOffset(0, 0)
-		checkButton:SetScript("OnClick", onClick)
-		checkButton.SetValue = checkButton.SetChecked
-
-		checkButton.label = checkButton:CreateFontString()
-		checkButton.label:SetPoint("LEFT", checkButton, "RIGHT", 0, 1)
-		checkButton:SetFontString(checkButton.label)
-
-		return checkButton
-	end
-end
-
-do
-	local ColorPickerFrame = ColorPickerFrame
-
-	local function setColor(self, color)
-		self.swatch:SetVertexColor(color.r, color.g, color.b)
-	end
-
-	local function saveColor(self, r, g, b)
-		self.swatch:SetVertexColor(r, g, b)
-		local color = get(self)
-		color.r = r
-		color.g = g
-		color.b = b
-		set(self, color)
-	end
-
-	local function swatchFunc()
-		saveColor(ColorPickerFrame.extraInfo, ColorPickerFrame:GetColorRGB())
-	end
-
-	local function cancelFunc(prev)
-		saveColor(ColorPickerFrame.extraInfo, ColorPicker_GetPreviousValues())
-	end
-
-	local scripts = {
-		OnClick = function(self)
-			local info = UIDropDownMenu_CreateInfo()
-			local color = get(self)
-			info.r, info.g, info.b = color.r, color.g, color.b
-			info.swatchFunc = swatchFunc
-			info.cancelFunc = cancelFunc
-			info.extraInfo = self
-			OpenColorPicker(info)
-		end,
-
-		OnEnter = function(self)
-			self.bg:SetVertexColor(NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b)
-			if self.tooltipText then
-				GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
-				GameTooltip:SetText(self.tooltipText, nil, nil, nil, nil, true)
-			end
-		end,
-
-		OnLeave = function(self)
-			self.bg:SetVertexColor(HIGHLIGHT_FONT_COLOR.r, HIGHLIGHT_FONT_COLOR.g, HIGHLIGHT_FONT_COLOR.b)
-			GameTooltip:Hide()
-		end,
-
-		OnEnable = function(self)
-			if self:IsMouseOver() then
-				self:OnEnter()
-			else
-				self.bg:SetVertexColor(HIGHLIGHT_FONT_COLOR.r, HIGHLIGHT_FONT_COLOR.g, HIGHLIGHT_FONT_COLOR.b)
-			end
-		end,
-
-		OnDisable = function(self)
-			self.bg:SetVertexColor(GRAY_FONT_COLOR.r, GRAY_FONT_COLOR.g, GRAY_FONT_COLOR.b)
-		end,
-	}
-
-	controls.ColorButton = function(parent, data)
-		local colorButton = CreateFrame("Button", nil, parent)
-		colorButton:SetSize(16, 16)
-		colorButton:SetNormalFontObject("GameFontHighlight")
-		colorButton:SetDisabledFontObject("GameFontDisable")
-		colorButton:SetPushedTextOffset(0, 0)
-		for script, handler in pairs(scripts) do
-			colorButton:SetScript(script, handler)
-			colorButton[script] = handler
-		end
-		colorButton.SetValue = setColor
-
-		colorButton:SetNormalTexture([[Interface\ChatFrame\ChatFrameColorSwatch]])
-		colorButton.swatch = colorButton:GetNormalTexture()
-
-		colorButton.bg = colorButton:CreateTexture(nil, "BACKGROUND")
-		colorButton.bg:SetSize(14, 14)
-		colorButton.bg:SetPoint("CENTER")
-		colorButton.bg:SetTexture(HIGHLIGHT_FONT_COLOR.r, HIGHLIGHT_FONT_COLOR.g, HIGHLIGHT_FONT_COLOR.b)
-
-		colorButton.label = colorButton:CreateFontString()
-		colorButton.label:SetPoint("LEFT", colorButton, "RIGHT", 5, 1)
-		colorButton:SetFontString(colorButton.label)
-
-		return colorButton
-	end
-end
-
-do
-	local function onValueChanged(self, value, isUserInput)
-		if isUserInput then
-			set(self, value)
-		end
-		if self.isPercent then
-			self.currentValue:SetFormattedText("%.0f%%", value * 100)
-		else
-			self.currentValue:SetText(value)
-		end
-	end
-
-	local function onMinMaxChanged(self, min, max)
-		if self.minText or not self.isPercent then
-			self.min:SetText(self.minText or min)
-		else
-			self.min:SetFormattedText("%.0f%%", min * 100)
-		end
-		if self.maxText or not self.isPercent then
-			self.max:SetText(self.maxText or max)
-		else
-			self.max:SetFormattedText("%.0f%%", max * 100)
-		end
-	end
-
-	controls.Slider = function(parent, data)
-		local slider = Libra:CreateSlider(parent)
-		slider:SetScript("OnValueChanged", onValueChanged)
-		slider:SetScript("OnMinMaxChanged", onMinMaxChanged)
-		slider.isPercent = data.isPercent
-		slider.minText = data.minText
-		slider.maxText = data.maxText
-		slider:SetMinMaxValues(data.min, data.max)
-		slider:SetValueStep(data.step)
-		return slider
-	end
-end
-
-do
-	local function getValue(dropdown, property, value)
-		local properties = dropdown.properties
-		local property = properties and properties[property]
-		if not properties or not property then
-			return value
-		else
-			if type(property) == "function" then
-				return property(value)
-			elseif type(property) == "table" then
-				return property[value]
-			else
-				return property
-			end
-		end
-	end
-
-	local function setText(self, value)
-		self:SetText(getValue(self, "text", value))
-	end
-
-	local defaultProperties = {
-		"text",
-		"value",
-		"arg1",
-	}
-
-	local function onClick(self, arg1, arg2, checked)
-		if self.owner.multiSelect then
-			set(self.owner, checked, arg1)
-		else
-			self.owner:SetText(self:GetText())
-			set(self.owner, arg1)
-		end
-	end
-
-	local function checked(self)
-		if self.owner.multiSelect then
-			return get(self.owner, self.arg1)
-		else
-			return self.arg1 == get(self.owner)
-		end
-	end
-
-	local function initialize(self, level, menuList)
-		menuList = menuList or self.menulist
-		if type(menuList) == "function" then
-			menuList = menuList()
-		end
-		for i, v in ipairs(menuList) do
-			local info = UIDropDownMenu_CreateInfo()
-			info.func = onClick
-			info.checked = checked
-			info.isNotRadio = self.multiSelect
-			for i, propertyName in ipairs(defaultProperties) do
-				info[propertyName] = getValue(self, propertyName, v)
-			end
-			if self.properties then
-				for propertyName in pairs(self.properties) do
-					info[propertyName] = getValue(self, propertyName, v)
-				end
-			end
-			self:AddButton(info)
-		end
-	end
-
-	controls.Dropdown = function(parent, data)
-		local dropdown = Libra:CreateDropdown("Frame", parent)
-		dropdown:JustifyText("LEFT")
-		dropdown.SetValue = setText
-		dropdown.initialize = data.initialize or initialize
-		dropdown.menulist = data.menuList
-		dropdown.multiSelect = data.multiSelect
-		if data.properties then
-			dropdown.properties = {}
-			for k, v in pairs(data.properties) do
-				dropdown.properties[k] = v
-			end
-		end
-		return dropdown
-	end
-end
-
-do
-	controls.Separator = function(parent, data)
-		local separator = parent:CreateTexture()
-		separator:SetHeight(1)
-		separator:SetPoint("LEFT")
-		separator:SetPoint("RIGHT", -31, 0)
-		separator:SetTexture(0.8, 0.8, 0.8, 0.5)
-		return separator
-	end
-end
-
-do
-	controls.Custom = function(parent, data)
-		local custom = CreateFrame("Frame", nil, parent)
-		custom.label = custom:CreateFontString(nil, nil, "GameFontNormal")
-		custom.label:SetPoint("LEFT", custom, "RIGHT", 0, 1)
-		return custom
-	end
-end
-
-
-local objectData = {
-	CheckButton = {
-		x = -2,
-		y = -16,
-		bottomOffset = 8,
-	},
-	ColorButton = {
-		x = 3,
-		y = -21,
-		bottomOffset = 3,
-	},
-	Slider = {
-		x = 7,
-		y = -27,
-		bottomOffset = -5,
-	},
-	Dropdown = {
-		x = -15,
-		y = -32,
-		bottomOffset = 8,
-	},
-	Separator = {
-		x = 0,
-		y = -15,
-		bottomOffset = 0,
-	},
-	Custom = {
-		x = 0,
-		y = -20,
-		bottomOffset = 0,
-	}
-}
-
-function Prototype:CreateOptions(options)
-	for i, option in ipairs(options) do
-		local control = controls[option.type](self, option)
-		local data = objectData[option.type]
-		if i == 1 then
-			control:SetPoint("TOPLEFT", self.desc, "BOTTOMLEFT", data.x, data.y + 8)
-		elseif option.newColumn then
-			control:SetPoint("TOPLEFT", self.desc, "BOTTOM", data.x - 2, data.y + 8)
-		else
-			local previousOption = options[i - 1]
-			local previousData = objectData[previousOption.type]
-			control:SetPoint("TOPLEFT", self.controls[i - 1], "BOTTOMLEFT", data.x - previousData.x, data.y + previousData.bottomOffset - (option.padding or 0))
-		end
-		if option.width then
-			control:SetWidth(option.width)
-		end
-		control.parent = self
-		if control.label then
-			control.label:SetText(option.text)
-		end
-		control.tooltipText = option.tooltip
-		control.key = option.key
-		control.keyTable = option.keyTable
-		control.set = option.set
-		control.get = option.get
-		control.func = option.func
-		control.disabled = option.disabled
-		if option.init then
-			option.init(control)
-		end
-		tinsert(self.controls, control)
-		tinsert(self.allcontrols, control)
-	end
-end
-
-function Prototype:SetupControls()
-	for i, control in ipairs(self.allcontrols) do
-		local value = get(control)
-		if value then
-			control:SetValue(value)
-			getFunc(control, "func", key, value)
-		end
-		if control.disabled then
-			control:SetEnabled(not control.disabled())
-		end
-	end
-end
-
-function Prototype:GetControlByKey(key, keyTable)
-	for i, control in ipairs(self.allcontrols) do
-		if control.key == key and control.keyTable == keyTable then
-			return control
-		end
-	end
-end
-
-Libra:RegisterModule(Type, Version, constructor)
\ No newline at end of file
diff --git a/libs/Libra-27/ScrollFrame.lua b/libs/Libra-27/ScrollFrame.lua
deleted file mode 100644
index dfd8541..0000000
--- a/libs/Libra-27/ScrollFrame.lua
+++ /dev/null
@@ -1,137 +0,0 @@
-local Libra = LibStub("Libra")
-local Type, Version = "ScrollFrame", 6
-if Libra:GetModuleVersion(Type) >= Version then return end
-
-Libra.modules[Type] = Libra.modules[Type] or {}
-
-local ScrollFrame = Libra.modules[Type]
-
-ScrollFrame.FauxPrototype = ScrollFrame.FauxPrototype or CreateFrame("ScrollFrame")
-ScrollFrame.HybridPrototype = ScrollFrame.HybridPrototype or CreateFrame("ScrollFrame")
-
-local fauxMT = {__index = ScrollFrame.FauxPrototype}
-local hybridMT = {__index = ScrollFrame.HybridPrototype}
-
-local HybridPrototype = ScrollFrame.HybridPrototype
-
-local function fauxOnVerticalScroll(self, offset)
-	self.Scrollbar:SetValue(offset)
-	self.offset = floor((offset / self.buttonHeight) + 0.5)
-	self:Update()
-end
-
-local function update(self)
-	local offset = self:GetOffset()
-	local numItems = self.getNumItems()
-	for i, button in ipairs(self.buttons) do
-		local index = offset + i
-		if index <= numItems then
-			self.updateButton(button, index)
-		end
-		button:SetShown(index <= numItems)
-	end
-	local totalHeight = numItems * self.buttonHeight
-	local displayedHeight = #self.buttons * self.buttonHeight
-	if self.dynamic then
-		totalHeight = self.dynamic()
-	elseif self.largeButtonTop then
-		totalHeight = totalHeight - (self.buttonHeight - self.largeButtonHeight)
-	end
-	HybridScrollFrame_Update(self, totalHeight, displayedHeight)
-	if self.onScroll then
-		self:onScroll()
-	end
-end
-
-local function constructor(self, type, parent, name)
-	local scrollFrame
-	if type == "Faux" then
-		scrollFrame = setmetatable(CreateFrame("ScrollFrame", name, parent, "FauxScrollFrameTemplate"), fauxMT)
-		scrollFrame:SetScript("OnVerticalScroll", fauxOnVerticalScroll)
-	end
-	if type == "Hybrid" then
-		name = name or Libra:GetWidgetName(self.name)
-		scrollFrame = setmetatable(CreateFrame("ScrollFrame", name, parent, "HybridScrollFrameTemplate"), hybridMT)
-		scrollFrame.update = function() update(scrollFrame) end
-		scrollFrame.scrollBar = CreateFrame("Slider", nil, scrollFrame, "HybridScrollBarTemplate")
-	end
-
-	return scrollFrame
-end
-
-
-local fauxMethods = {
-	Update = FauxScrollFrame_Update,
-	SetOffset = FauxScrollFrame_SetOffset,
-	GetOffset = FauxScrollFrame_GetOffset,
-}
-
-for k, v in pairs(fauxMethods) do
-	ScrollFrame.FauxPrototype[k] = v
-end
-
-local hybridMethods = {
-	-- Update = HybridScrollFrame_Update,
-	-- SetOffset = HybridScrollFrame_SetOffset,
-	GetOffset = HybridScrollFrame_GetOffset,
-	CollapseButton = HybridScrollFrame_CollapseButton,
-}
-
-for k, v in pairs(hybridMethods) do
-	ScrollFrame.HybridPrototype[k] = v
-end
-
-local function setHeader(self)
-	self:SetHeight(self.parent.headerHeight)
-end
-
-local function resetHeight(self)
-	self:SetHeight(self.parent.buttonHeightReal)
-end
-
-function HybridPrototype:CreateButtons()
-	self.buttons = self.buttons or {}
-	local scrollChild = self.scrollChild
-	local numButtons = ceil(self:GetHeight() / self.buttonHeightReal) + 1
-	for i = #self.buttons + 1, numButtons do
-		local button = self.createButton(scrollChild)
-		if i == 1 then
-			button:SetPoint(self.initialPoint or "TOPLEFT", scrollChild, self.initialRelative or "TOPLEFT", (self.initialOffsetX or 0), (self.initialOffsetY or 0))
-		else
-			button:SetPoint(self.point or "TOPLEFT", self.buttons[i - 1], self.relativePoint or "BOTTOMLEFT", (self.offsetX or 0), (self.offsetY or 0))
-		end
-		button:SetHeight(self.buttonHeightReal)
-		button.SetHeader = setHeader
-		button.ResetHeight = resetHeight
-		button.parent = self
-		self.buttons[i] = button
-	end
-
-	self.buttonHeight = self.buttonHeightReal - (self.offsetY or 0)
-
-	scrollChild:SetWidth(self:GetWidth())
-	scrollChild:SetHeight(numButtons * self.buttonHeightReal)
-	self:SetVerticalScroll(0)
-	self:UpdateScrollChildRect()
-
-	local scrollBar = self.scrollBar
-	scrollBar:SetMinMaxValues(0, numButtons * self.buttonHeightReal)
-	scrollBar.buttonHeight = self.buttonHeightReal
-	scrollBar:SetValueStep(self.buttonHeightReal)
-	scrollBar:SetStepsPerPage(numButtons - 2)
-	scrollBar:SetValue(0)
-end
-
-function HybridPrototype:SetButtonHeight(height)
-	self.buttonHeightReal = height
-end
-
-function HybridPrototype:SetHeaderHeight(height)
-	self.headerHeight = height
-end
-
-function HybridPrototype:ExpandButton(numButtons)
-	HybridScrollFrame_ExpandButton(self, numButtons * self.buttonHeight, self.headerHeight)
-end
-
-Libra:RegisterModule(Type, Version, constructor)
\ No newline at end of file
diff --git a/libs/Libra-27/Slider.lua b/libs/Libra-27/Slider.lua
deleted file mode 100644
index 20202de..0000000
--- a/libs/Libra-27/Slider.lua
+++ /dev/null
@@ -1,54 +0,0 @@
-local Libra = LibStub("Libra")
-local Type, Version = "Slider", 2
-if Libra:GetModuleVersion(Type) >= Version then return end
-
-local backdrop = {
-	bgFile = [[Interface\Buttons\UI-SliderBar-Background]],
-	edgeFile = [[Interface\Buttons\UI-SliderBar-Border]],
-	edgeSize = 8,
-	insets = {left = 3, right = 3, top = 6, bottom = 6}
-}
-
-local function onEnter(self)
-	if self:IsEnabled() then
-		if self.tooltipText then
-			GameTooltip:SetOwner(self, self.tooltipOwnerPoint or "ANCHOR_RIGHT")
-			GameTooltip:SetText(self.tooltipText, nil, nil, nil, nil, true)
-		end
-		if self.tooltipRequirement then
-			GameTooltip:AddLine(self.tooltipRequirement, 1.0, 1.0, 1.0)
-			GameTooltip:Show()
-		end
-	end
-end
-
-local function onLeave(self)
-	GameTooltip:Hide()
-end
-
-local function constructor(self, parent)
-	local slider = CreateFrame("Slider", nil, parent)
-	slider:SetSize(144, 17)
-	slider:SetBackdrop(backdrop)
-	slider:SetThumbTexture([[Interface\Buttons\UI-SliderBar-Button-Horizontal]])
-	slider:SetOrientation("HORIZONTAL")
-	slider:SetObeyStepOnDrag(true)
-	slider:SetScript("OnEnter", onEnter)
-	slider:SetScript("OnLeave", onLeave)
-
-	slider.label = slider:CreateFontString(nil, nil, "GameFontNormal")
-	slider.label:SetPoint("BOTTOM", slider, "TOP")
-
-	slider.min = slider:CreateFontString(nil, nil, "GameFontHighlightSmall")
-	slider.min:SetPoint("TOPLEFT", slider, "BOTTOMLEFT", -4, 3)
-
-	slider.max = slider:CreateFontString(nil, nil, "GameFontHighlightSmall")
-	slider.max:SetPoint("TOPRIGHT", slider, "BOTTOMRIGHT", 4, 3)
-
-	slider.currentValue = slider:CreateFontString(nil, "BACKGROUND", "GameFontHighlightSmall")
-	slider.currentValue:SetPoint("CENTER", 0, -15)
-
-	return slider
-end
-
-Libra:RegisterModule(Type, Version, constructor)
\ No newline at end of file
diff --git a/libs/Libra-27/UIPanel.lua b/libs/Libra-27/UIPanel.lua
deleted file mode 100644
index 049a76f..0000000
--- a/libs/Libra-27/UIPanel.lua
+++ /dev/null
@@ -1,102 +0,0 @@
-local Libra = LibStub("Libra")
-local Type, Version = "UIPanel", 1
-if Libra:GetModuleVersion(Type) >= Version then return end
-
-Libra.modules[Type] = Libra.modules[Type] or {}
-
-local UIPanel = Libra.modules[Type]
-UIPanel.Prototype = UIPanel.Prototype or CreateFrame("Frame")
-
-local Prototype = UIPanel.Prototype
-local mt = {__index = Prototype}
-
-local function safecall(object, method, ...)
-	if object[method] then
-		object[method](object, ...)
-	end
-end
-
-local function constructor(self, name)
-	name = name or Libra:GetWidgetName(self.name)
-	local panel = setmetatable(CreateFrame("Frame", name, UIParent, "ButtonFrameTemplate"), mt)
-
-	tinsert(UISpecialFrames, name)
-	UIPanelWindows[name] = {
-		area = "left",
-		pushable = 1,
-		whileDead = true,
-	}
-
-	return panel
-end
-
-
-local methods = {
-	ShowPortrait = ButtonFrameTemplate_ShowPortrait,
-	HidePortrait = ButtonFrameTemplate_HidePortrait,
-	ShowAttic = ButtonFrameTemplate_ShowAttic,
-	HideAttic = ButtonFrameTemplate_HideAttic,
-	ShowButtonBar = ButtonFrameTemplate_ShowButtonBar,
-
-	GetSelectedTab = PanelTemplates_GetSelectedTab,
-	UpdateTabs = PanelTemplates_UpdateTabs,
-	EnableTab = PanelTemplates_EnableTab,
-	DisableTab = PanelTemplates_DisableTab,
-	-- GetTabWidth = PanelTemplates_GetTabWidth,
-	-- TabResize = PanelTemplates_TabResize,
-}
-
-for k, v in pairs(methods) do
-	Prototype[k] = v
-end
-
-function Prototype:SetTitleText(text)
-	self.TitleText:SetText(text)
-end
-
-function Prototype:HideButtonBar()
-	ButtonFrameTemplate_HideButtonBar(self)
-	self.Inset:SetPoint("BOTTOMRIGHT", PANEL_INSET_RIGHT_OFFSET, PANEL_INSET_BOTTOM_OFFSET + 2)
-end
-
-
-local function onClick(self)
-	self:GetParent():SelectTab(self:GetID())
-	PlaySound("igCharacterInfoTab")
-end
-
-function Prototype:CreateTab(name)
-	self.tabs = self.tabs or {}
-	if type(name) == "number" then
-		error("Tab name may not be a number.", 2)
-	end
-	-- if type(name) == "number" then
-		-- error(format("%s already has a tab named '%s'.", self:GetName(), name), 2)
-	-- end
-	local tabs = self.tabs
-	local numTabs = #tabs + 1
-	local tab = CreateFrame("Button", self:GetName().."Tab"..numTabs, self, "CharacterFrameTabButtonTemplate")
-	if numTabs == 1 then
-		tab:SetPoint("BOTTOMLEFT", 19, -30)
-	else
-		tab:SetPoint("LEFT", tabs[numTabs - 1], "RIGHT", -15, 0)
-	end
-	tab:SetID(numTabs)
-	tab:SetScript("OnClick", onClick)
-	tabs[numTabs] = tab
-	self.numTabs = numTabs
-	return tab
-end
-
-function Prototype:SelectTab(id)
-	local selectedTab = self:GetSelectedTab()
-	if selectedTab then
-		safecall(self, "OnTabDeselected", selectedTab)
-	end
-	self.selectedTab = id
-	self:UpdateTabs()
-	safecall(self, "OnTabSelected", id)
-end
-
-
-Libra:RegisterModule(Type, Version, constructor)
\ No newline at end of file
diff --git a/libs/Libra-34/AceDBControls.lua b/libs/Libra-34/AceDBControls.lua
new file mode 100644
index 0000000..d0cfc8c
--- /dev/null
+++ b/libs/Libra-34/AceDBControls.lua
@@ -0,0 +1,352 @@
+local Libra = LibStub("Libra")
+local Type, Version = "AceDBControls", 4
+if Libra:GetModuleVersion(Type) >= Version then return end
+
+Libra.modules[Type] = Libra.modules[Type] or {}
+
+local AceDBControls = Libra.modules[Type]
+AceDBControls.Prototype = AceDBControls.Prototype or CreateFrame("Frame")
+
+local Prototype = AceDBControls.Prototype
+local mt = {__index = Prototype}
+
+local L = {
+	default = "Default",
+	reset = "Reset profile",
+	new = "Create new profile",
+	choose = "Active profile",
+	copy = "Copy from",
+	delete = "Delete a profile",
+	delete_confirm = "Are you sure you want to delete the selected profile?",
+	profiles = "Profiles",
+
+	dual_profile = "Dual profile",
+	enabled = "Enable dual profile",
+}
+
+local LOCALE = GetLocale()
+if LOCALE == "deDE" then
+	L["default"] = "Standard"
+	L["reset"] = "Profil zur\195\188cksetzen"
+	L["new"] = "Neu"
+	L["choose"] = "Vorhandene Profile"
+	L["copy"] = "Kopieren von..."
+	L["delete"] = "Profil l\195\182schen"
+	L["delete_confirm"] = "Willst du das ausgew\195\164hlte Profil wirklich l\195\182schen?"
+	L["profiles"] = "Profile"
+
+	L["dual_profile"] = "Duales Profil"
+	L["enabled"] = "Aktiviere Duale Profile"
+elseif LOCALE == "frFR" then
+	L["default"] = "D\195\169faut"
+	L["reset"] = "R\195\169initialiser le profil"
+	L["new"] = "Nouveau"
+	L["choose"] = "Profils existants"
+	L["copy"] = "Copier \195\160 partir de"
+	L["delete"] = "Supprimer un profil"
+	L["delete_confirm"] = "Etes-vous s\195\187r de vouloir supprimer le profil s\195\169lectionn\195\169 ?"
+	L["profiles"] = "Profils"
+
+	L["dual_profile"] = 'Second profil'
+	L["enabled"] = 'Activez le second profil'
+elseif LOCALE == "koKR" then
+	L["default"] = "기본값"
+	L["reset"] = "프로필 초기화"
+	L["new"] = "새로운 프로필"
+	L["choose"] = "프로필 선택"
+	L["copy"] = "복사"
+	L["delete"] = "프로필 삭제"
+	L["delete_confirm"] = "정말로 선택한 프로필의 삭제를 원하십니까?"
+	L["profiles"] = "프로필"
+
+	L["dual_profile"] = "이중 프로필"
+	L["enabled"] = "이중 프로필 사용"
+elseif LOCALE == "esES" or LOCALE == "esMX" then
+	L["default"] = "Por defecto"
+	L["reset"] = "Reiniciar Perfil"
+	L["new"] = "Nuevo"
+	L["choose"] = "Perfiles existentes"
+	L["copy"] = "Copiar de"
+	L["delete"] = "Borrar un Perfil"
+	L["delete_confirm"] = "¿Estas seguro que quieres borrar el perfil seleccionado?"
+	L["profiles"] = "Perfiles"
+elseif LOCALE == "zhTW" then
+	L["default"] = "預設"
+	L["reset"] = "重置設定檔"
+	L["new"] = "新建"
+	L["choose"] = "現有的設定檔"
+	L["copy"] = "複製自"
+	L["delete"] = "刪除一個設定檔"
+	L["delete_confirm"] = "你確定要刪除所選擇的設定檔嗎?"
+	L["profiles"] = "設定檔"
+elseif LOCALE == "zhCN" then
+	L["default"] = "默认"
+	L["reset"] = "重置配置文件"
+	L["choose_desc"] = "你可以通过在文本框内输入一个名字创立一个新的配置文件,也可以选择一个已经存在的配置文件。"
+	L["new"] = "新建"
+	L["choose"] = "现有的配置文件"
+	L["copy"] = "复制自"
+	L["delete"] = "删除一个配置文件"
+	L["delete_confirm"] = "你确定要删除所选择的配置文件么?"
+	L["profiles"] = "配置文件"
+
+	L["dual_profile"] = "双重配置文件"
+	L["enabled"] = "开启双重配置文件"
+elseif LOCALE == "ruRU" then
+	L["default"] = "По умолчанию"
+	L["reset"] = "Сброс профиля"
+	L["new"] = "Новый"
+	L["choose"] = "Существующие профили"
+	L["copy"] = "Скопировать из"
+	L["delete"] = "Удалить профиль"
+	L["delete_confirm"] = "Вы уверены, что вы хотите удалить выбранный профиль?"
+	L["profiles"] = "Профили"
+
+	L["dual_profile"] = "Второй профиль"
+	L["enabled"] = "Включить двойной профиль"
+end
+
+local defaultProfiles = {}
+
+local function profileSort(a, b)
+	return a.value < b.value
+end
+
+local tempProfiles = {}
+
+local function getProfiles(db, common, nocurrent)
+	local profiles = {}
+
+	-- copy existing profiles into the table
+	local currentProfile = db:GetCurrentProfile()
+	for _, v in ipairs(db:GetProfiles(tempProfiles)) do
+		if not (nocurrent and v == currentProfile) then
+			profiles[v] = v
+		end
+	end
+
+	-- add our default profiles to choose from (or rename existing profiles)
+	for k, v in pairs(defaultProfiles) do
+		if (common or profiles[k]) and not (nocurrent and k == currentProfile) then
+			profiles[k] = v
+		end
+	end
+
+	local sortProfiles = {}
+	for k, v in pairs(profiles) do
+		tinsert(sortProfiles, {text = v, value = k})
+	end
+	sort(sortProfiles, profileSort)
+
+	return sortProfiles
+end
+
+local function dropdownOnClick(self, profile, func)
+	func(self.owner.db, profile, self.owner)
+end
+
+local function initializeDropdown(self, level, menuList)
+	for i, v in ipairs(getProfiles(self.db, self.common, self.nocurrent)) do
+		local info = UIDropDownMenu_CreateInfo()
+		info.text = v.text
+		info.func = dropdownOnClick
+		info.arg1 = v.value
+		info.arg2 = self.func
+		info.checked = not self.nocurrent and (v.value == self.getCurrent(self.db))
+		info.notCheckable = self.nocurrent
+		self:AddButton(info)
+	end
+end
+
+local function createDropdown(parent)
+	local dropdown = Libra:CreateDropdown("Frame", parent)
+	dropdown:SetWidth(160)
+	dropdown:JustifyText("LEFT")
+	dropdown.initialize = initializeDropdown
+	return dropdown
+end
+
+local function menuButton_OnClick(self)
+	self.menu:Toggle()
+end
+
+local function createMenuButton(parent)
+	local button = Libra:CreateButton(parent)
+	button:SetScript("OnClick", menuButton_OnClick)
+	button.arrow:Show()
+	button:SetWidth(88)
+
+	local menu = Libra:CreateDropdown("Menu")
+	menu.relativeTo = button
+	menu.initialize = initializeDropdown
+	menu.nocurrent = true
+	menu.db = parent.db
+	button.menu = menu
+
+	return button
+end
+
+local createProfileScripts = {
+	OnEnterPressed = function(self)
+		self.db:SetProfile(self:GetText())
+		self:ClearFocus()
+	end,
+	OnEditFocusGained = function(self)
+		self:SetTextColor(1, 1, 1)
+	end,
+	OnEditFocusLost = function(self)
+		self:SetTextColor(0.5, 0.5, 0.5)
+		self:SetText("")
+	end,
+}
+
+local function enableDualProfileOnClick(self)
+	local checked = self:GetChecked()
+	self.db:SetDualSpecEnabled(checked)
+	self.dualProfile:SetEnabled(checked)
+end
+
+local function dualProfileOnClick(db, profile, frame)
+	db:SetDualSpecProfile(profile)
+	frame:SetText(profile)
+end
+
+local function deleteProfile(db, profile)
+	StaticPopup_Show("DELETE_PROFILE", nil, nil, {db = db, profile = profile})
+end
+
+StaticPopupDialogs["DELETE_PROFILE"] = {
+	text = L.delete_confirm,
+	button1 = YES,
+	button2 = NO,
+	OnAccept = function(self, data)
+		data.db:DeleteProfile(data.profile)
+	end,
+}
+
+local function constructor(self, db, parent)
+	local frame = setmetatable(CreateFrame("Frame", nil, parent), mt)
+	frame:SetSize(192, 192)
+	frame.db = db
+
+	db.RegisterCallback(frame, "OnNewProfile")
+	db.RegisterCallback(frame, "OnProfileChanged")
+	db.RegisterCallback(frame, "OnProfileDeleted")
+
+	local keys = db.keys
+	defaultProfiles["Default"] = L.default
+	defaultProfiles[keys.char] = keys.char
+	defaultProfiles[keys.realm] = keys.realm
+	defaultProfiles[keys.class] = UnitClass("player")
+
+	local objects = {}
+
+	do	-- create the controls
+		local choose = createDropdown(frame)
+		choose:SetPoint("TOP")
+		choose.label:SetText(L.choose)
+		choose.func = db.SetProfile
+		choose.getCurrent = db.GetCurrentProfile
+		choose.common = true
+		objects.choose = choose
+
+		local newProfile = Libra:CreateEditbox(frame)
+		newProfile:SetPoint("TOPLEFT", choose, "BOTTOMLEFT", 24, -8)
+		newProfile:SetPoint("TOPRIGHT", choose, "BOTTOMRIGHT", -17, -8)
+		newProfile:SetTextColor(0.5, 0.5, 0.5)
+		newProfile:SetScript("OnEscapePressed", newProfile.ClearFocus)
+		for script, handler in pairs(createProfileScripts) do
+			newProfile:SetScript(script, handler)
+		end
+		objects.newProfile = newProfile
+
+		local label = newProfile:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
+		label:SetHeight(18)
+		label:SetPoint("BOTTOMLEFT", newProfile, "TOPLEFT", -5, -2)
+		label:SetPoint("BOTTOMRIGHT", newProfile, "TOPRIGHT", 0, -2)
+		label:SetJustifyH("LEFT")
+		label:SetText(L.new)
+
+		local copy = createMenuButton(frame)
+		copy:SetPoint("TOPLEFT", newProfile, "BOTTOMLEFT", -9, -4)
+		copy:SetText("Copy from")
+		copy.menu.func = db.CopyProfile
+		objects.copy = copy
+
+		local delete = createMenuButton(frame)
+		delete:SetPoint("TOPRIGHT", newProfile, "BOTTOMRIGHT", 4, -4)
+		delete:SetText("Delete")
+		delete.menu.func = deleteProfile
+		objects.delete = delete
+
+		local reset = Libra:CreateButton(frame)
+		reset:SetPoint("TOPLEFT", copy, "BOTTOM", 0, -4)
+		reset:SetPoint("TOPRIGHT", delete, "BOTTOM", 0, -4)
+		reset:SetScript("OnClick", function(self) self.db:ResetProfile() end)
+		reset:SetText(L.reset)
+		objects.reset = reset
+
+		local hasDualProfile = db:GetNamespace("LibDualSpec-1.0", true)
+		if hasDualProfile then
+			local isDualSpecEnabled = db:IsDualSpecEnabled()
+
+			local dualProfile = createDropdown(frame)
+			dualProfile:SetPoint("TOP", reset, "BOTTOM", 0, -28)
+			dualProfile:SetEnabled(isDualSpecEnabled)
+			dualProfile:SetText(db:GetDualSpecProfile())
+			dualProfile.func = dualProfileOnClick
+			dualProfile.getCurrent = db.GetDualSpecProfile
+			dualProfile.common = true
+			objects.dualProfile = dualProfile
+
+			local enabled = CreateFrame("CheckButton", nil, frame, "OptionsBaseCheckButtonTemplate")
+			enabled:SetPoint("BOTTOMLEFT", dualProfile, "TOPLEFT", 16, 0)
+			enabled:SetPushedTextOffset(0, 0)
+			enabled:SetScript("OnClick", enableDualProfileOnClick)
+			enabled:SetChecked(isDualSpecEnabled)
+			enabled.tooltipText = L.enable_desc
+			enabled.dualProfile = dualProfile
+			objects.dualEnabled = enabled
+
+			local text = enabled:CreateFontString(nil, nil, "GameFontHighlight")
+			text:SetPoint("LEFT", enabled, "RIGHT", 0, 1)
+			text:SetText(L.enabled)
+
+			frame.hasDualProfile = true
+		end
+	end
+
+	for k, object in pairs(objects) do
+		object.db = db
+		frame[k] = object
+	end
+
+	frame.choose:SetText(db:GetCurrentProfile())
+
+	frame:CheckProfiles()
+
+	return frame
+end
+
+function Prototype:CheckProfiles()
+	local hasProfiles = not self:HasNoProfiles()
+	self.copy:SetEnabled(hasProfiles)
+	self.delete:SetEnabled(hasProfiles)
+end
+
+function Prototype:HasNoProfiles()
+	return next(getProfiles(self.db, nil, true)) == nil
+end
+
+function Prototype:OnProfileChanged(event, db, profile)
+	self.choose:SetText(profile)
+	self:CheckProfiles()
+	if self.hasDualProfile then
+		self.dualProfile:SetText(db:GetDualSpecProfile())
+	end
+end
+
+Prototype.OnNewProfile = Prototype.CheckProfiles
+Prototype.OnProfileDeleted = Prototype.CheckProfiles
+
+Libra:RegisterModule(Type, Version, constructor)
\ No newline at end of file
diff --git a/libs/Libra-34/Addon.lua b/libs/Libra-34/Addon.lua
new file mode 100644
index 0000000..415b774
--- /dev/null
+++ b/libs/Libra-34/Addon.lua
@@ -0,0 +1,193 @@
+local Libra = LibStub("Libra")
+local Type, Version = "Addon", 3
+if Libra:GetModuleVersion(Type) >= Version then return end
+
+Libra.modules[Type] = Libra.modules[Type] or {}
+
+local object = Libra.modules[Type]
+object.frame = object.frame or CreateFrame("Frame")
+object.addons = object.addons or {}
+object.events = object.events or {}
+object.onUpdates = object.onUpdates or {}
+object.defaults = object.defaults or {}
+
+local function safecall(object, method, ...)
+	if object[method] then
+		object[method](object, ...)
+	end
+end
+
+local function removeDefaults(tbl, defaults)
+	for k, v in pairs(defaults) do
+		if type(v) == "table" then
+			removeDefaults(tbl[k], v)
+			if not next(tbl[k]) then
+				tbl[k] = nil
+			end
+		elseif v == tbl[k] then
+			tbl[k] = nil
+		end
+	end
+end
+
+object.frame:RegisterEvent("ADDON_LOADED")
+object.frame:RegisterEvent("PLAYER_LOGOUT")
+object.frame:SetScript("OnEvent", function(self, event, ...)
+	if event == "ADDON_LOADED" then
+		local addon = object.addons[...]
+		if addon then
+			safecall(addon, "OnInitialize")
+			addon.OnInitialize = nil
+			for i, module in addon:IterateModules() do
+				safecall(module, "OnInitialize")
+				module.OnInitialize = nil
+			end
+		end
+	end
+	if event == "PLAYER_LOGOUT" then
+		for tbl, defaults in pairs(object.defaults) do
+			removeDefaults(tbl, defaults)
+		end
+	end
+	for module, eventHandler in pairs(object.events[event]) do
+		eventHandler(module, ...)
+	end
+end)
+
+local function onUpdate(self, elapsed)
+	for module, update in pairs(object.onUpdates) do
+		update(module, elapsed)
+	end
+end
+
+setmetatable(object.events, {
+	__index = function(table, key)
+		local newTable = {}
+		table[key] = newTable
+		return newTable
+	end
+})
+
+local AddonPrototype = {}
+local ObjectPrototype = {}
+
+local function AddonEmbed(target)
+	for k, v in pairs(AddonPrototype) do
+		target[k] = v
+	end
+end
+
+local function ObjectEmbed(target)
+	for k, v in pairs(ObjectPrototype) do
+		target[k] = v
+	end
+end
+
+function Libra:NewAddon(name, addonObject)
+	if object.addons[name] then
+		error(format("Addon '%s' already exists.", name), 2)
+	end
+
+	local addon = addonObject or {}
+	addon.name = name
+	addon.modules = {}
+	AddonEmbed(addon)
+	ObjectEmbed(addon)
+	object.addons[name] = addon
+	return addon, name
+end
+
+function Libra:GetAddon(name)
+	return object.addons[name]
+end
+
+function AddonPrototype:NewModule(name, table)
+	if self:GetModule(name) then
+		error(format("Module '%s' already exists in %s.", name, self.name), 2)
+	end
+
+	local module = table or {}
+	ObjectEmbed(module)
+	module.name = name
+	tinsert(self.modules, module)
+	safecall(self, "OnModuleCreated", name, module)
+	return module, name
+end
+
+function AddonPrototype:GetModule(name)
+	for i, module in self:IterateModules() do
+		if module.name == name then
+			return module
+		end
+	end
+end
+
+function AddonPrototype:IterateModules()
+	return next, self.modules
+end
+
+local function copyDefaults(src, dst)
+	if not src then return {} end
+	if not dst then dst = {} end
+	for k, v in pairs(src) do
+		if type(v) == "table" then
+			dst[k] = copyDefaults(v, dst[k])
+		elseif type(v) ~= type(dst[k]) then
+			dst[k] = v
+		end
+	end
+	return dst
+end
+
+function AddonPrototype:CreateDB(global, defaults)
+	local db = _G[global]
+	db = copyDefaults(defaults, db)
+	_G[global] = db
+	object.defaults[db] = defaults
+	return db
+end
+
+function ObjectPrototype:RegisterEvent(event, handler)
+	if not next(object.events[event]) then
+		object.frame:RegisterEvent(event)
+	end
+	if type(handler) ~= "function" then
+		handler = self[handler] or self[event]
+	end
+	object.events[event][self] = handler
+end
+
+function ObjectPrototype:UnregisterEvent(event)
+	object.events[event][self] = nil
+	if not next(object.events[event]) then
+		object.frame:UnregisterEvent(event)
+	end
+end
+
+function ObjectPrototype:SetOnUpdate(handler)
+	if not next(object.onUpdates) then
+		object.frame:SetScript("OnUpdate", onUpdate)
+	end
+	if type(handler) ~= "function" then
+		handler = self[handler]
+	end
+	object.onUpdates[self] = handler
+end
+
+function ObjectPrototype:RemoveOnUpdate()
+	object.onUpdates[self] = nil
+	if not next(object.onUpdates) then
+		object.frame:SetScript("OnUpdate", nil)
+	end
+end
+
+-- upgrade embeds
+for k, v in pairs(object.addons) do
+	AddonEmbed(v)
+	ObjectEmbed(v)
+	for i, v in v:IterateModules() do
+		ObjectEmbed(v)
+	end
+end
+
+Libra:RegisterModule(Type, Version)
\ No newline at end of file
diff --git a/libs/Libra-34/Button.lua b/libs/Libra-34/Button.lua
new file mode 100644
index 0000000..9ee43a7
--- /dev/null
+++ b/libs/Libra-34/Button.lua
@@ -0,0 +1,27 @@
+local Libra = LibStub("Libra")
+local Type, Version = "Button", 2
+if Libra:GetModuleVersion(Type) >= Version then return end
+
+local function onEnable(self)
+	self.arrow:SetDesaturated(false)
+end
+
+local function onDisable(self)
+	self.arrow:SetDesaturated(true)
+end
+
+local function constructor(self, parent)
+	local button = CreateFrame("Button", nil, parent, "UIMenuButtonStretchTemplate")
+	button:SetHeight(23)
+	button:SetScript("OnEnable", onEnable)
+	button:SetScript("OnDisable", onDisable)
+	button.arrow = button:CreateTexture()
+	button.arrow:SetSize(10, 12)
+	button.arrow:SetPoint("RIGHT", -5, 0)
+	button.arrow:SetTexture([[Interface\ChatFrame\ChatFrameExpandArrow]])
+	button.arrow:Hide()
+	button.Icon = button.arrow
+	return button
+end
+
+Libra:RegisterModule(Type, Version, constructor)
\ No newline at end of file
diff --git a/libs/Libra-34/Core.lua b/libs/Libra-34/Core.lua
new file mode 100644
index 0000000..70f2943
--- /dev/null
+++ b/libs/Libra-34/Core.lua
@@ -0,0 +1,65 @@
+local MAJOR, MINOR = "Libra", 2
+local lib = LibStub:NewLibrary(MAJOR, MINOR)
+
+if not lib then return end
+
+lib.modules = lib.modules or {}
+lib.moduleVersions = lib.moduleVersions or {}
+lib.embeds = lib.widgetEmbeds or lib.embeds or {}
+lib.methods = lib.methods or {}
+lib.controls = lib.widgets or lib.controls or {}
+lib.namespaces = lib.namespaces or {}
+
+function lib:RegisterModule(object, version, constructor)
+	self.moduleVersions[object] = version
+	if constructor then
+		self.controls[object] = constructor
+		self["Create"..object] = constructor
+		for k in pairs(self.embeds) do
+			k["Create"..object] = constructor
+		end
+	end
+end
+
+function lib:GetModuleVersion(module)
+	return self.moduleVersions[module] or 0
+end
+
+function lib:RegisterMethods(tbl)
+	for k, v in pairs(tbl) do
+		for target in pairs(self.embeds) do
+			target[k] = v
+		end
+		self.methods[k] = v
+	end
+end
+
+function lib:GetWidgetName(name)
+	name = name or "Generic"
+	local namespace = self.namespaces[name]
+	if not namespace then
+		local n = 0
+		namespace = function()
+			n = n + 1
+			return format("%sLibraWidget%d", name, n)
+		end
+		self.namespaces[name] = namespace
+	end
+	return namespace()
+end
+
+function lib:Embed(target)
+	for k, v in pairs(self.methods) do
+		target[k] = v
+	end
+	for k, v in pairs(self.controls) do
+		target["Create"..k] = v
+	end
+	self.embeds[target] = true
+end
+
+lib.EmbedWidgets = lib.Embed
+
+for k in pairs(lib.embeds) do
+	lib:Embed(k)
+end
\ No newline at end of file
diff --git a/libs/Libra-34/Dropdown.lua b/libs/Libra-34/Dropdown.lua
new file mode 100644
index 0000000..ea80487
--- /dev/null
+++ b/libs/Libra-34/Dropdown.lua
@@ -0,0 +1,548 @@
+local Libra = LibStub("Libra")
+local Type, Version = "Dropdown", 12
+if Libra:GetModuleVersion(Type) >= Version then return end
+
+Libra.modules[Type] = Libra.modules[Type] or {}
+
+local Dropdown = Libra.modules[Type]
+Dropdown.Prototype = Dropdown.Prototype or CreateFrame("Frame")
+Dropdown.MenuPrototype = Dropdown.MenuPrototype or setmetatable({}, {__index = Dropdown.Prototype})
+Dropdown.FramePrototype = Dropdown.FramePrototype or setmetatable({}, {__index = Dropdown.Prototype})
+Dropdown.objects = Dropdown.objects or {}
+Dropdown.listData = Dropdown.listData or {}
+Dropdown.hookedLists = Dropdown.hookedLists or {}
+Dropdown.hookedButtons = Dropdown.hookedButtons or {}
+Dropdown.secureButtons = Dropdown.secureButtons or {}
+Dropdown.secureBin = Dropdown.secureBin or {}
+
+local menuMT = {__index = Dropdown.MenuPrototype}
+local frameMT = {__index = Dropdown.FramePrototype}
+
+local Prototype = Dropdown.Prototype
+local MenuPrototype = Dropdown.MenuPrototype
+local FramePrototype = Dropdown.FramePrototype
+local objects = Dropdown.objects
+local listData = Dropdown.listData
+
+local function setHeight() end
+
+local function constructor(self, type, parent, name)
+	local dropdown
+	if type == "Menu" then
+		-- adding a SetHeight dummy lets us use a simple table instead of a frame, no side effects noticed so far
+		dropdown = setmetatable({}, menuMT)
+		dropdown:SetDisplayMode("MENU")
+		dropdown.SetHeight = setHeight
+		dropdown.xOffset = 0
+		dropdown.yOffset = 0
+	end
+	if type == "Frame" then
+		name = name or Libra:GetWidgetName(self.name)
+		dropdown = setmetatable(CreateFrame("Frame", name, parent, "UIDropDownMenuTemplate"), frameMT)
+		dropdown:SetWidth(115)
+		dropdown.label = dropdown:CreateFontString(name.."Label", "BACKGROUND", "GameFontNormalSmall")
+		dropdown.label:SetPoint("BOTTOMLEFT", dropdown, "TOPLEFT", 16, 3)
+	end
+
+	objects[dropdown] = true
+
+	return dropdown
+end
+
+
+local methods = {
+	Refresh = UIDropDownMenu_Refresh,
+}
+
+for k, v in pairs(methods) do
+	Prototype[k] = v
+end
+
+function Prototype:AddButton(info, level)
+	info.owner = self
+	if info.icon and not info.iconOnly then
+		-- hack to properly increase button width for icon when .iconOnly is not set
+		info.padding = (info.padding or 0) + 10
+	end
+	self.displayMode = self._displayMode
+	self.selectedName = self._selectedName
+	self.selectedValue = self._selectedValue
+	self.selectedID = self._selectedID
+	UIDropDownMenu_AddButton(info, level)
+	self.displayMode = nil
+	self.selectedName = nil
+	self.selectedValue = nil
+	self.selectedID = nil
+end
+
+function Prototype:ToggleMenu(value, anchorName, xOffset, yOffset, menuList, level, ...)
+	ToggleDropDownMenu(level, value, self, anchorName, xOffset, yOffset, menuList, ...)
+end
+
+function Prototype:RebuildMenu(level)
+	level = level or 1
+	if self:IsMenuShown(level) then
+		-- hiding a menu will also hide all deeper level menus, so we'll check which ones are open and restore them afterwards
+		local maxLevel
+		for i = level, UIDROPDOWNMENU_MENU_LEVEL do
+			if _G["DropDownList"..i]:IsShown() then
+				maxLevel = i
+			else
+				break
+			end
+		end
+		self:HideMenu(level)
+		for i = level, maxLevel do
+			local listData = listData[i]
+			-- set .rebuild to indicate that we don't want to reset the scroll offset on the next ToggleDropDownMenu
+			self.rebuild = true
+			self:ToggleMenu(listData.value, listData.anchorName, listData.xOffset, listData.yOffset, listData.menuList, i, listData.button, listData.autoHideDelay)
+		end
+	end
+end
+
+function Prototype:HideMenu(level)
+	if UIDropDownMenu_GetCurrentDropDown() == self then
+		HideDropDownMenu(level)
+	end
+end
+
+function Prototype:CloseMenus(level)
+	if UIDropDownMenu_GetCurrentDropDown() == self then
+		CloseDropDownMenus(level)
+	end
+end
+
+function Prototype:IsMenuShown(level)
+	level = level or 1
+	local listFrame = _G["DropDownList"..level]
+	return UIDropDownMenu_GetCurrentDropDown() == self and listFrame and listFrame:IsShown()
+end
+
+function Prototype:SetSelectedName(name, useValue)
+	self._selectedName = name
+	self._selectedValue = nil
+	self._selectedID = nil
+	self.selectedName = name
+	self:Refresh(useValue)
+	self.selectedName = nil
+end
+
+function Prototype:SetSelectedValue(value, useValue)
+	self._selectedValue = value
+	self._selectedName = nil
+	self._selectedID = nil
+	self.selectedValue = value
+	self:Refresh(useValue)
+	self.selectedValue = nil
+end
+
+function Prototype:SetSelectedID(id, useValue)
+	self._selectedID = id
+	self._selectedName = nil
+	self._selectedValue = nil
+	self.selectedID = id
+	self:Refresh(useValue)
+	self.selectedID = nil
+end
+
+function Prototype:GetSelectedName()
+	return self._selectedName
+end
+
+function Prototype:GetSelectedValue()
+	return self._selectedValue
+end
+
+function Prototype:GetSelectedID()
+	if self._selectedID then
+		return self._selectedID
+	else
+		-- If no explicit selectedID then try to send the id of a selected value or name
+		for i=1, UIDROPDOWNMENU_MAXBUTTONS do
+			local button = _G["DropDownList"..UIDROPDOWNMENU_MENU_LEVEL.."Button"..i]
+			-- See if checked or not
+			if self:GetSelectedName() then
+				if button:GetText() == self:GetSelectedName() then
+					return i
+				end
+			elseif self:GetSelectedValue() then
+				if button.value == self:GetSelectedValue() then
+					return i
+				end
+			end
+		end
+	end
+end
+
+function Prototype:SetDisplayMode(mode)
+	self._displayMode = mode
+end
+
+
+local menuMethods = {
+	Toggle = Prototype.ToggleMenu,
+	Rebuild = Prototype.RebuildMenu,
+	Hide = Prototype.HideMenu,
+	Close = Prototype.CloseMenus,
+	IsShown = Prototype.IsMenuShown,
+}
+
+for k, v in pairs(menuMethods) do
+	MenuPrototype[k] = v
+end
+
+
+local frameMethods = {
+	Enable = UIDropDownMenu_EnableDropDown,
+	Disable = UIDropDownMenu_DisableDropDown,
+	IsEnabled = UIDropDownMenu_IsEnabled,
+	JustifyText = UIDropDownMenu_JustifyText,
+	SetButtonWidth = UIDropDownMenu_SetButtonWidth,
+	SetText = UIDropDownMenu_SetText,
+	GetText = UIDropDownMenu_GetText,
+}
+
+for k, v in pairs(frameMethods) do
+	FramePrototype[k] = v
+end
+
+local setWidth = Prototype.SetWidth
+
+function FramePrototype:SetWidth(width, padding)
+	_G[self:GetName().."Middle"]:SetWidth(width)
+	local defaultPadding = 25
+	if padding then
+		setWidth(self, width + padding)
+		_G[self:GetName().."Text"]:SetWidth(width)
+	else
+		setWidth(self, width + defaultPadding + defaultPadding)
+		_G[self:GetName().."Text"]:SetWidth(width - defaultPadding)
+	end
+	self.noResize = 1
+end
+
+function FramePrototype:SetLabel(text)
+	self.label:SetText(text)
+end
+
+function FramePrototype:SetEnabled(enable)
+	if enable then
+		self:Enable()
+	else
+		self:Disable()
+	end
+end
+
+
+local numShownButtons
+
+local function update(level)
+	local scroll = listData[level].scroll
+	for i = 1, UIDROPDOWNMENU_MAXBUTTONS do
+		local button = _G["DropDownList"..level.."Button"..i]
+		local _, _, _, x, y = button:GetPoint()
+		local y = -((button:GetID() - 1 - scroll) * UIDROPDOWNMENU_BUTTON_HEIGHT) - UIDROPDOWNMENU_BORDER_HEIGHT
+		button:SetPoint("TOPLEFT", x, y)
+		button:SetShown(i > scroll and i <= (numShownButtons + scroll))
+	end
+	Dropdown.scrollButtons[level].up:SetShown(scroll > 0)
+	Dropdown.scrollButtons[level].down:SetShown(scroll < _G["DropDownList"..level].numButtons - numShownButtons)
+end
+
+local function scroll(self, delta)
+	local level = self:GetID()
+	local listData = listData[level]
+	delta = (type(delta) == "number" and delta or self.delta)
+	if IsShiftKeyDown() then delta = delta * (numShownButtons - 1) end
+	listData.scroll = listData.scroll - (type(delta) == "number" and delta or self.delta)
+	listData.scroll = min(listData.scroll, (self.numButtons or self:GetParent().numButtons) - numShownButtons)
+	listData.scroll = max(listData.scroll, 0)
+	update(level)
+end
+
+local scrollScripts = {
+	OnEnter = function(self)
+		UIDropDownMenu_StopCounting(self:GetParent())
+	end,
+	OnLeave = function(self)
+		UIDropDownMenu_StartCounting(self:GetParent())
+	end,
+	OnMouseDown = function(self)
+		self.texture:SetPoint("CENTER", 1, -1)
+	end,
+	OnMouseUp = function(self)
+		self.texture:SetPoint("CENTER")
+	end,
+	OnHide = function(self)
+		self.texture:SetPoint("CENTER")
+		-- explicitly hide so that they are hidden for unmanaged dropdowns
+		self:Hide()
+	end,
+}
+
+local function createScrollButton(listFrame)
+	local level = listFrame:GetID()
+	local button = CreateFrame("Button", nil, listFrame)
+	button:SetSize(16, 16)
+	button:SetScript("OnClick", scroll)
+	for script, handler in pairs(scrollScripts) do
+		button:SetScript(script, handler)
+	end
+	button:SetID(level)
+	button.texture = button:CreateTexture()
+	button.texture:SetSize(16, 16)
+	button.texture:SetPoint("CENTER")
+	button.texture:SetTexture([[Interface\Calendar\MoreArrow]])
+	return button
+end
+
+local function createScrollButtons(listFrame)
+	local scrollUp = listFrame.scrollUp or createScrollButton(listFrame)
+	scrollUp:SetPoint("TOP")
+	scrollUp.delta = 1
+	scrollUp.texture:SetTexCoord(0, 1, 1, 0)
+	listFrame.scrollUp = scrollUp
+
+	local scrollDown = listFrame.scrollDown or createScrollButton(listFrame)
+	scrollDown:SetPoint("BOTTOM")
+	scrollDown.delta = -1
+	listFrame.scrollDown = scrollDown
+end
+
+Dropdown.scrollButtons = Dropdown.scrollButtons or setmetatable({}, {
+	__index = function(self, level)
+		local listFrame = _G["DropDownList"..level]
+		createScrollButtons(listFrame)
+		self[level] = {
+			up = listFrame.scrollUp,
+			down = listFrame.scrollDown,
+		}
+		return self[level]
+	end,
+})
+
+function Dropdown:ToggleDropDownMenuHook(level, value, dropdownFrame, anchorName, xOffset, yOffset, menuList, button, autoHideDelay)
+	level = level or 1
+	if level ~= 1 then
+		dropdownFrame = dropdownFrame or UIDROPDOWNMENU_OPEN_MENU
+	end
+	local listFrameName = "DropDownList"..level
+	local listFrame = _G[listFrameName]
+	if not objects[dropdownFrame] then
+		listFrame:SetScript("OnMouseWheel", nil)
+		return
+	end
+	if dropdownFrame and dropdownFrame._displayMode == "MENU" then
+		_G[listFrameName.."Backdrop"]:Hide()
+		_G[listFrameName.."MenuBackdrop"]:Show()
+	end
+
+	-- store all parameters per level so we can use them to rebuild the menu
+	listData[level] = listData[level] or {}
+	local listData = listData[level]
+	listData.value = value
+	listData.anchorName = anchorName
+	listData.xOffset = xOffset
+	listData.yOffset = yOffset
+	listData.menuList = menuList
+	listData.button = button
+	listData.autoHideDelay = autoHideDelay
+
+	numShownButtons = dropdownFrame.numShownButtons or floor((UIParent:GetHeight() - UIDROPDOWNMENU_BORDER_HEIGHT * 2) / UIDROPDOWNMENU_BUTTON_HEIGHT)
+	local scrollable = numShownButtons < listFrame.numButtons
+	if scrollable then
+		-- make scrollable
+		listData.scroll = listData.scroll or 0
+		if not dropdownFrame.rebuild then
+			listData.scroll = 0
+		end
+		listFrame:SetScript("OnMouseWheel", scroll)
+		listFrame:SetHeight((numShownButtons * UIDROPDOWNMENU_BUTTON_HEIGHT) + (UIDROPDOWNMENU_BORDER_HEIGHT * 2))
+		if listFrame:GetTop() > GetScreenHeight() then
+			local point, anchorFrame, relativePoint, x, y = listFrame:GetPoint()
+			local offTop = (GetScreenHeight() - listFrame:GetTop())-- / listFrame:GetScale()
+			listFrame:SetPoint(point, anchorFrame, relativePoint, x, y + offTop)
+		end
+		update(level)
+	else
+		if listFrame:GetTop() > GetScreenHeight() then
+			local point, anchorFrame, relativePoint, x, y = listFrame:GetPoint()
+			local offTop = (GetScreenHeight() - listFrame:GetTop())-- / listFrame:GetScale()
+			listFrame:SetPoint(point, anchorFrame, relativePoint, x, y + offTop)
+		end
+		listFrame:SetScript("OnMouseWheel", nil)
+		self.scrollButtons[level].up:Hide()
+		self.scrollButtons[level].down:Hide()
+	end
+	dropdownFrame.rebuild = nil
+end
+
+if not Dropdown.hookToggleDropDownMenu then
+	hooksecurefunc("ToggleDropDownMenu", function(...)
+		Dropdown:ToggleDropDownMenuHook(...)
+	end)
+	Dropdown.hookToggleDropDownMenu = true
+end
+
+function Dropdown:AddButtonHook(info, level)
+	if not objects[UIDropDownMenu_GetCurrentDropDown()] then return end
+	local listFrameName = "DropDownList"..(level or 1)
+	local listFrame = _G[listFrameName]
+	local button = _G[listFrameName.."Button"..(listFrame.numButtons)]
+	button.onEnter = info.onEnter
+	button.onLeave = info.onLeave
+	button.tooltipLines = info.tooltipLines
+	if info.attributes and not InCombatLockdown() then
+		local secureButton = self.secureBin[1]
+		tremove(Dropdown.secureBin, 1)
+		-- since this is a separate button, we need to set the disabled state on it too
+		secureButton:SetEnabled(not info.disabled)
+		secureButton:SetParent(button)
+		secureButton:SetAllPoints()
+		secureButton:Show()
+		-- clear existing attributes
+		for attribute in pairs(secureButton.attributes) do
+			secureButton:SetAttribute(attribute, nil)
+			secureButton.attributes[attribute] = nil
+		end
+		for attribute, value in pairs(info.attributes) do
+			secureButton:SetAttribute(attribute, value)
+			secureButton.attributes[attribute] = true
+		end
+		tinsert(Dropdown.secureButtons, secureButton)
+	end
+end
+
+if not Dropdown.hookAddButton then
+	hooksecurefunc("UIDropDownMenu_AddButton", function(...)
+		Dropdown:AddButtonHook(...)
+	end)
+	Dropdown.hookAddButton = true
+end
+
+local function onEnter(self)
+	if self.onEnter then
+		self:onEnter()
+	elseif self.tooltipLines and self.tooltipTitle then
+		GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
+		GameTooltip:AddLine(self.tooltipTitle, HIGHLIGHT_FONT_COLOR.r, HIGHLIGHT_FONT_COLOR.g, HIGHLIGHT_FONT_COLOR.b)
+		if self.tooltipText then
+			for line in self.tooltipText:gmatch("[^\n]+") do
+				GameTooltip:AddLine(line)
+			end
+		end
+		GameTooltip:Show()
+	end
+end
+
+local function onLeave(self)
+	if self.onLeave then
+		self:onLeave()
+	end
+end
+
+local function invisibleButtonOnEnter(self)
+	local parent = self:GetParent()
+	if parent.onEnter or parent.tooltipWhileDisabled then
+		onEnter(parent)
+	end
+end
+
+local function invisibleButtonOnLeave(self)
+	local parent = self:GetParent()
+	if parent.onLeave or parent.tooltipWhileDisabled then
+		onLeave(parent)
+	end
+end
+
+local function listOnHide(self)
+	if not InCombatLockdown() then
+		for i = #Dropdown.secureButtons, 1, -1 do
+			-- hide secure buttons attached to this list frame
+			local button = Dropdown.secureButtons[i]
+			if button:GetParent():GetParent() == self then
+				Dropdown:DismissSecureButton(button)
+				tremove(Dropdown.secureButtons, i)
+			end
+		end
+	end
+end
+
+function Dropdown:CreateFramesHook(numLevels, numButtons)
+	for level = 1, numLevels do
+		if not self.hookedLists[level] then
+			_G["DropDownList"..level]:HookScript("OnHide", listOnHide)
+			self.hookedLists[level] = true
+		end
+		self.hookedButtons[level] = self.hookedButtons[level] or {}
+		for i = 1, numButtons do
+			if not self.hookedButtons[level][i] then
+				_G["DropDownList"..level.."Button"..i]:HookScript("OnEnter", onEnter)
+				_G["DropDownList"..level.."Button"..i]:HookScript("OnLeave", onLeave)
+				_G["DropDownList"..level.."Button"..i.."InvisibleButton"]:HookScript("OnEnter", invisibleButtonOnEnter)
+				_G["DropDownList"..level.."Button"..i.."InvisibleButton"]:HookScript("OnLeave", invisibleButtonOnLeave)
+				self.hookedButtons[level][i] = true
+			end
+		end
+	end
+end
+
+if not Dropdown.hookCreateFrames then
+	Dropdown:CreateFramesHook(UIDROPDOWNMENU_MAXLEVELS, UIDROPDOWNMENU_MAXBUTTONS)
+	hooksecurefunc("UIDropDownMenu_CreateFrames", function(...)
+		Dropdown:CreateFramesHook(...)
+	end)
+	Dropdown.hookCreateFrames = true
+end
+
+-- script handlers to mimic regular dropdown button behaviour
+local scripts = {
+	PreClick = function(self)
+		local parent = self:GetParent()
+		parent:GetScript("OnClick")(parent)
+	end,
+	OnMouseDown = function(self)
+		self:GetParent():SetButtonState("PUSHED")
+	end,
+	OnMouseUp = function(self)
+		self:GetParent():SetButtonState("NORMAL")
+	end,
+	OnEnter = function(self)
+		local parent = self:GetParent()
+		parent:GetScript("OnEnter")(parent)
+	end,
+	OnLeave = function(self)
+		local parent = self:GetParent()
+		parent:GetScript("OnLeave")(parent)
+	end,
+}
+
+setmetatable(Dropdown.secureBin, {
+	__index = function(self, index)
+		local button = CreateFrame("Button", nil, nil, "SecureActionButtonTemplate")
+		for script, handler in pairs(scripts) do
+			button:SetScript(script, handler)
+		end
+		button.attributes = {}
+		return button
+	end,
+})
+
+function Dropdown:DismissSecureButton(button)
+	button:Hide()
+	button:ClearAllPoints()
+	button:SetParent(nil)
+	tinsert(Dropdown.secureBin, button)
+end
+
+Dropdown.frame = Dropdown.frame or CreateFrame("Frame")
+Dropdown.frame:RegisterEvent("PLAYER_REGEN_DISABLED")
+Dropdown.frame:SetScript("OnEvent", function(self)
+	for i, button in ipairs(Dropdown.secureButtons) do
+		Dropdown:DismissSecureButton(button)
+	end
+	wipe(Dropdown.secureButtons)
+end)
+
+Libra:RegisterModule(Type, Version, constructor)
\ No newline at end of file
diff --git a/libs/Libra-34/Editbox.lua b/libs/Libra-34/Editbox.lua
new file mode 100644
index 0000000..28358a2
--- /dev/null
+++ b/libs/Libra-34/Editbox.lua
@@ -0,0 +1,12 @@
+local Libra = LibStub("Libra")
+local Type, Version = "Editbox", 3
+if Libra:GetModuleVersion(Type) >= Version then return end
+
+local function constructor(self, parent, isSearchBox)
+	local editbox = CreateFrame("EditBox", nil, parent, isSearchBox and "SearchBoxTemplate" or "InputBoxTemplate")
+	editbox:SetHeight(20)
+	editbox:SetAutoFocus(false)
+	return editbox
+end
+
+Libra:RegisterModule(Type, Version, constructor)
\ No newline at end of file
diff --git a/libs/Libra-34/Libra.xml b/libs/Libra-34/Libra.xml
new file mode 100644
index 0000000..02a5fb6
--- /dev/null
+++ b/libs/Libra-34/Libra.xml
@@ -0,0 +1,16 @@
+<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/
+..\FrameXML\UI.xsd">
+	<Script file="Core.lua"/>
+
+	<Script file="Addon.lua"/>
+	<Script file="AceDBControls.lua"/>
+	<Script file="Button.lua"/>
+	<Script file="Dropdown.lua"/>
+	<Script file="Editbox.lua"/>
+	<Script file="OptionsFrame.lua"/>
+	<Script file="ScrollFrame.lua"/>
+	<Script file="Slider.lua"/>
+	<Script file="Templates.lua"/>
+	<Script file="UIPanel.lua"/>
+	<Script file="Utils.lua"/>
+</Ui>
\ No newline at end of file
diff --git a/libs/Libra-34/OptionsFrame.lua b/libs/Libra-34/OptionsFrame.lua
new file mode 100644
index 0000000..e9dfeab
--- /dev/null
+++ b/libs/Libra-34/OptionsFrame.lua
@@ -0,0 +1,475 @@
+local Libra = LibStub("Libra")
+local Type, Version = "OptionsFrame", 4
+if Libra:GetModuleVersion(Type) >= Version then return end
+
+Libra.modules[Type] = Libra.modules[Type] or {}
+
+local Options = Libra.modules[Type]
+
+Options.Prototype = Options.Prototype or CreateFrame("Frame")
+Options.ParentPrototype = Options.ParentPrototype or {}
+Options.controls = Options.controls or {}
+Options.controlData = Options.controlData or {}
+
+local mt = {__index = Options.Prototype}
+local parentMT = {__index = setmetatable(Options.ParentPrototype, {__index = Options.Prototype})}
+
+local Prototype = Options.Prototype
+local ParentPrototype = Options.ParentPrototype
+
+local function createFrame(name, parent)
+	local frame = CreateFrame("Frame")
+	frame.name = name
+	frame.parent = parent
+	InterfaceOptions_AddCategory(frame)
+
+	local title = frame:CreateFontString(nil, nil, "GameFontNormalLarge")
+	title:SetPoint("TOPLEFT", 16, -16)
+	title:SetPoint("RIGHT", -16, 0)
+	title:SetJustifyH("LEFT")
+	title:SetJustifyV("TOP")
+	title:SetText(name)
+	frame.title = title
+
+	local desc = frame:CreateFontString(nil, nil, "GameFontHighlightSmall")
+	desc:SetHeight(32)
+	desc:SetPoint("TOPLEFT", frame.title, "BOTTOMLEFT", 0, -8)
+	desc:SetPoint("RIGHT", -31, 0)
+	desc:SetJustifyH("LEFT")
+	desc:SetJustifyV("TOP")
+	desc:SetNonSpaceWrap(true)
+	frame.desc = desc
+
+	return frame
+end
+
+local function constructor(self, name)
+	local frame = setmetatable(createFrame(name), parentMT)
+	frame.controls = {}
+	frame.allcontrols = {}
+	return frame
+end
+
+
+function ParentPrototype:AddSubCategory(name, inherit)
+	local frame = setmetatable(createFrame(name, self.name), mt)
+	if inherit then
+		frame.db = self.db
+		frame.useProfile = self.useProfile
+		frame.handler = self.handler
+		frame.allcontrols = self.allcontrols
+	else
+		frame.allcontrols = {}
+	end
+	frame.inherit = inherit
+	frame.controls = {}
+	self.subCategories = self.subCategories or {}
+	tinsert(self.subCategories, frame)
+	return frame
+end
+
+function Prototype:SetDescription(text)
+	self.desc:SetText(text)
+end
+
+function Prototype:SetDatabase(database, useProfile)
+	self.db = database
+	self.useProfile = useProfile
+	if self.subCategories then
+		for i, v in ipairs(self.subCategories) do
+			if v.inherit then
+				v.db = database
+			end
+		end
+	end
+end
+
+function Prototype:SetHandler(tbl)
+	self.handler = tbl
+	if self.subCategories then
+		for i, v in ipairs(self.subCategories) do
+			if v.inherit then
+				v.handler = tbl
+			end
+		end
+	end
+end
+
+
+local function getTable(control)
+	local tbl = control.parent.db
+	if control.parent.useProfile then
+		tbl = tbl.profile
+	end
+	if control.keyTable then
+		tbl = tbl[control.keyTable]
+	end
+	return tbl
+end
+
+local function getFunc(control, method, key, value)
+	local func = control[method]
+	if func then
+		local object = control
+		if type(func) == "string" then
+			object = control.parent.handler
+			func = object[func]
+		end
+		if key then
+			return true, func(object, key, value)
+		else
+			return true, func(object, value)
+		end
+		return true
+	end
+end
+
+local function set(self, value, key)
+	if not getFunc(self, "set", key, value) then
+		local tbl = getTable(self)
+		if tbl then
+			tbl[key or self.key] = value
+		end
+	end
+	getFunc(self, "func", key, value)
+	for key, control in pairs(self.parent.allcontrols) do
+		if control.disabled then
+			control:SetEnabled(not control.disabled())
+		end
+	end
+end
+
+local function get(self, key)
+	local hasFunc, value = getFunc(self, "get", key)
+	if hasFunc then
+		return value
+	else
+		local tbl = getTable(self)
+		if tbl then
+			return tbl[key or self.key]
+		end
+	end
+end
+
+local controls = Options.controls
+local controlData = Options.controlData
+
+do	-- CheckButton
+	controlData.CheckButton = {
+		x = -2,
+		y = -16,
+		bottomOffset = 8,
+	}
+
+	local function onClick(self)
+		local checked = self:GetChecked()
+		PlaySound(checked and "igMainMenuOptionCheckBoxOn" or "igMainMenuOptionCheckBoxOff")
+		set(self, checked)
+	end
+
+	controls.CheckButton = function(parent)
+		local checkButton = CreateFrame("CheckButton", nil, parent, "OptionsBaseCheckButtonTemplate")
+		checkButton:SetNormalFontObject("GameFontHighlight")
+		checkButton:SetDisabledFontObject("GameFontDisable")
+		checkButton:SetPushedTextOffset(0, 0)
+		checkButton:SetScript("OnClick", onClick)
+		checkButton.SetValue = checkButton.SetChecked
+
+		checkButton.label = checkButton:CreateFontString()
+		checkButton.label:SetPoint("LEFT", checkButton, "RIGHT", 0, 1)
+		checkButton:SetFontString(checkButton.label)
+
+		return checkButton
+	end
+end
+
+do	-- ColorButton
+	controlData.ColorButton = {
+		x = 3,
+		y = -21,
+		bottomOffset = 3,
+	}
+
+	local ColorPickerFrame = ColorPickerFrame
+
+	local function setColor(self, color)
+		self.swatch:SetVertexColor(color.r, color.g, color.b)
+	end
+
+	local function saveColor(self, r, g, b)
+		self.swatch:SetVertexColor(r, g, b)
+		local color = get(self)
+		color.r = r
+		color.g = g
+		color.b = b
+		set(self, color)
+	end
+
+	local function swatchFunc()
+		saveColor(ColorPickerFrame.extraInfo, ColorPickerFrame:GetColorRGB())
+	end
+
+	local function cancelFunc(prev)
+		saveColor(ColorPickerFrame.extraInfo, ColorPicker_GetPreviousValues())
+	end
+
+	local scripts = {
+		OnClick = function(self)
+			local info = UIDropDownMenu_CreateInfo()
+			local color = get(self)
+			info.r, info.g, info.b = color.r, color.g, color.b
+			info.swatchFunc = swatchFunc
+			info.cancelFunc = cancelFunc
+			info.extraInfo = self
+			OpenColorPicker(info)
+		end,
+
+		OnEnter = function(self)
+			self.bg:SetVertexColor(NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b)
+			if self.tooltipText then
+				GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
+				GameTooltip:SetText(self.tooltipText, nil, nil, nil, nil, true)
+			end
+		end,
+
+		OnLeave = function(self)
+			self.bg:SetVertexColor(HIGHLIGHT_FONT_COLOR.r, HIGHLIGHT_FONT_COLOR.g, HIGHLIGHT_FONT_COLOR.b)
+			GameTooltip:Hide()
+		end,
+
+		OnEnable = function(self)
+			if self:IsMouseOver() then
+				self:OnEnter()
+			else
+				self.bg:SetVertexColor(HIGHLIGHT_FONT_COLOR.r, HIGHLIGHT_FONT_COLOR.g, HIGHLIGHT_FONT_COLOR.b)
+			end
+		end,
+
+		OnDisable = function(self)
+			self.bg:SetVertexColor(GRAY_FONT_COLOR.r, GRAY_FONT_COLOR.g, GRAY_FONT_COLOR.b)
+		end,
+	}
+
+	controls.ColorButton = function(parent, data)
+		local colorButton = CreateFrame("Button", nil, parent)
+		colorButton:SetSize(16, 16)
+		colorButton:SetNormalFontObject("GameFontHighlight")
+		colorButton:SetDisabledFontObject("GameFontDisable")
+		colorButton:SetPushedTextOffset(0, 0)
+		for script, handler in pairs(scripts) do
+			colorButton:SetScript(script, handler)
+			colorButton[script] = handler
+		end
+		colorButton.SetValue = setColor
+
+		colorButton:SetNormalTexture([[Interface\ChatFrame\ChatFrameColorSwatch]])
+		colorButton.swatch = colorButton:GetNormalTexture()
+
+		colorButton.bg = colorButton:CreateTexture(nil, "BACKGROUND")
+		colorButton.bg:SetSize(14, 14)
+		colorButton.bg:SetPoint("CENTER")
+		colorButton.bg:SetTexture(HIGHLIGHT_FONT_COLOR.r, HIGHLIGHT_FONT_COLOR.g, HIGHLIGHT_FONT_COLOR.b)
+
+		colorButton.label = colorButton:CreateFontString()
+		colorButton.label:SetPoint("LEFT", colorButton, "RIGHT", 5, 1)
+		colorButton:SetFontString(colorButton.label)
+
+		return colorButton
+	end
+end
+
+do	-- Slider
+	controlData.Slider = {
+		x = 7,
+		y = -27,
+		bottomOffset = -5,
+	}
+
+	local function onValueChanged(self, value, isUserInput)
+		if isUserInput then
+			set(self, value)
+		end
+		if self.isPercent then
+			self.currentValue:SetFormattedText("%.0f%%", value * 100)
+		else
+			self.currentValue:SetText(value)
+		end
+	end
+
+	local function onMinMaxChanged(self, min, max)
+		if self.minText or not self.isPercent then
+			self.min:SetText(self.minText or min)
+		else
+			self.min:SetFormattedText("%.0f%%", min * 100)
+		end
+		if self.maxText or not self.isPercent then
+			self.max:SetText(self.maxText or max)
+		else
+			self.max:SetFormattedText("%.0f%%", max * 100)
+		end
+	end
+
+	controls.Slider = function(parent, data)
+		local slider = Libra:CreateSlider(parent)
+		slider:SetScript("OnValueChanged", onValueChanged)
+		slider:SetScript("OnMinMaxChanged", onMinMaxChanged)
+		slider.isPercent = data.isPercent
+		slider.minText = data.minText
+		slider.maxText = data.maxText
+		slider:SetMinMaxValues(data.min, data.max)
+		slider:SetValueStep(data.step)
+		return slider
+	end
+end
+
+do	-- Dropdown
+	controlData.Dropdown = {
+		x = -15,
+		y = -32,
+		bottomOffset = 8,
+	}
+
+	local function getValue(dropdown, property, value)
+		local properties = dropdown.properties
+		local property = properties and properties[property]
+		if not properties or not property then
+			return value
+		else
+			if type(property) == "function" then
+				return property(value)
+			elseif type(property) == "table" then
+				return property[value]
+			else
+				return property
+			end
+		end
+	end
+
+	local function setText(self, value)
+		self:SetText(getValue(self, "text", value))
+	end
+
+	local defaultProperties = {
+		"text",
+		"value",
+		"arg1",
+	}
+
+	local function onClick(self, arg1, arg2, checked)
+		if self.owner.multiSelect then
+			set(self.owner, checked, arg1)
+		else
+			self.owner:SetText(self:GetText())
+			set(self.owner, arg1)
+		end
+	end
+
+	local function checked(self)
+		if self.owner.multiSelect then
+			return get(self.owner, self.arg1)
+		else
+			return self.arg1 == get(self.owner)
+		end
+	end
+
+	local function initialize(self, level, menuList)
+		menuList = menuList or self.menulist
+		if type(menuList) == "function" then
+			menuList = menuList()
+		end
+		for i, v in ipairs(menuList) do
+			local info = UIDropDownMenu_CreateInfo()
+			info.func = onClick
+			info.checked = checked
+			info.isNotRadio = self.multiSelect
+			for i, propertyName in ipairs(defaultProperties) do
+				info[propertyName] = getValue(self, propertyName, v)
+			end
+			if self.properties then
+				for propertyName in pairs(self.properties) do
+					info[propertyName] = getValue(self, propertyName, v)
+				end
+			end
+			self:AddButton(info)
+		end
+	end
+
+	controls.Dropdown = function(parent, data)
+		local dropdown = Libra:CreateDropdown("Frame", parent)
+		dropdown:JustifyText("LEFT")
+		dropdown.SetValue = setText
+		dropdown.initialize = data.initialize or initialize
+		dropdown.menulist = data.menuList
+		dropdown.multiSelect = data.multiSelect
+		if data.properties then
+			dropdown.properties = {}
+			for k, v in pairs(data.properties) do
+				dropdown.properties[k] = v
+			end
+		end
+		return dropdown
+	end
+end
+
+function Libra:AddControlType(type, constructor, controlData)
+	if Options.controls[type] then
+		error(format("Control type '%s' already exists.", type), 2)
+	end
+	Options.controls[type] = constructor
+	controlData = controlData or {}
+	controlData.x = controlData.x or 0
+	controlData.y = controlData.y or 0
+	controlData.bottomOffset = controlData.bottomOffset or 0
+	Options.controlData[type] = controlData
+end
+
+function Prototype:CreateOptions(options)
+	for i, option in ipairs(options) do
+		local control = controls[option.type](self, option)
+		local data = controlData[option.type]
+		if i == 1 then
+			control:SetPoint("TOPLEFT", self.desc, "BOTTOMLEFT", data.x, data.y + 8)
+		elseif option.newColumn then
+			control:SetPoint("TOPLEFT", self.desc, "BOTTOM", data.x - 2, data.y + 8)
+		else
+			local previousOption = options[i - 1]
+			local previousData = controlData[previousOption.type]
+			control:SetPoint("TOPLEFT", self.controls[i - 1], "BOTTOMLEFT", data.x - previousData.x, data.y + previousData.bottomOffset - (option.padding or 0))
+		end
+		if option.width then
+			control:SetWidth(option.width)
+		end
+		control.parent = self
+		control.label:SetText(option.text)
+		control.tooltipText = option.tooltip
+		control.key = option.key
+		control.keyTable = option.keyTable
+		control.set = option.set
+		control.get = option.get
+		control.func = option.func
+		control.disabled = option.disabled
+		tinsert(self.controls, control)
+		tinsert(self.allcontrols, control)
+	end
+end
+
+function Prototype:SetupControls()
+	for i, control in ipairs(self.allcontrols) do
+		local value = get(control)
+		control:SetValue(value)
+		getFunc(control, "func", key, value)
+		if control.disabled then
+			control:SetEnabled(not control.disabled())
+		end
+	end
+end
+
+function Prototype:GetControlByKey(key, keyTable)
+	for i, control in ipairs(self.allcontrols) do
+		if control.key == key and control.keyTable == keyTable then
+			return control
+		end
+	end
+end
+
+Libra:RegisterModule(Type, Version, constructor)
\ No newline at end of file
diff --git a/libs/Libra-34/ScrollFrame.lua b/libs/Libra-34/ScrollFrame.lua
new file mode 100644
index 0000000..dfd8541
--- /dev/null
+++ b/libs/Libra-34/ScrollFrame.lua
@@ -0,0 +1,137 @@
+local Libra = LibStub("Libra")
+local Type, Version = "ScrollFrame", 6
+if Libra:GetModuleVersion(Type) >= Version then return end
+
+Libra.modules[Type] = Libra.modules[Type] or {}
+
+local ScrollFrame = Libra.modules[Type]
+
+ScrollFrame.FauxPrototype = ScrollFrame.FauxPrototype or CreateFrame("ScrollFrame")
+ScrollFrame.HybridPrototype = ScrollFrame.HybridPrototype or CreateFrame("ScrollFrame")
+
+local fauxMT = {__index = ScrollFrame.FauxPrototype}
+local hybridMT = {__index = ScrollFrame.HybridPrototype}
+
+local HybridPrototype = ScrollFrame.HybridPrototype
+
+local function fauxOnVerticalScroll(self, offset)
+	self.Scrollbar:SetValue(offset)
+	self.offset = floor((offset / self.buttonHeight) + 0.5)
+	self:Update()
+end
+
+local function update(self)
+	local offset = self:GetOffset()
+	local numItems = self.getNumItems()
+	for i, button in ipairs(self.buttons) do
+		local index = offset + i
+		if index <= numItems then
+			self.updateButton(button, index)
+		end
+		button:SetShown(index <= numItems)
+	end
+	local totalHeight = numItems * self.buttonHeight
+	local displayedHeight = #self.buttons * self.buttonHeight
+	if self.dynamic then
+		totalHeight = self.dynamic()
+	elseif self.largeButtonTop then
+		totalHeight = totalHeight - (self.buttonHeight - self.largeButtonHeight)
+	end
+	HybridScrollFrame_Update(self, totalHeight, displayedHeight)
+	if self.onScroll then
+		self:onScroll()
+	end
+end
+
+local function constructor(self, type, parent, name)
+	local scrollFrame
+	if type == "Faux" then
+		scrollFrame = setmetatable(CreateFrame("ScrollFrame", name, parent, "FauxScrollFrameTemplate"), fauxMT)
+		scrollFrame:SetScript("OnVerticalScroll", fauxOnVerticalScroll)
+	end
+	if type == "Hybrid" then
+		name = name or Libra:GetWidgetName(self.name)
+		scrollFrame = setmetatable(CreateFrame("ScrollFrame", name, parent, "HybridScrollFrameTemplate"), hybridMT)
+		scrollFrame.update = function() update(scrollFrame) end
+		scrollFrame.scrollBar = CreateFrame("Slider", nil, scrollFrame, "HybridScrollBarTemplate")
+	end
+
+	return scrollFrame
+end
+
+
+local fauxMethods = {
+	Update = FauxScrollFrame_Update,
+	SetOffset = FauxScrollFrame_SetOffset,
+	GetOffset = FauxScrollFrame_GetOffset,
+}
+
+for k, v in pairs(fauxMethods) do
+	ScrollFrame.FauxPrototype[k] = v
+end
+
+local hybridMethods = {
+	-- Update = HybridScrollFrame_Update,
+	-- SetOffset = HybridScrollFrame_SetOffset,
+	GetOffset = HybridScrollFrame_GetOffset,
+	CollapseButton = HybridScrollFrame_CollapseButton,
+}
+
+for k, v in pairs(hybridMethods) do
+	ScrollFrame.HybridPrototype[k] = v
+end
+
+local function setHeader(self)
+	self:SetHeight(self.parent.headerHeight)
+end
+
+local function resetHeight(self)
+	self:SetHeight(self.parent.buttonHeightReal)
+end
+
+function HybridPrototype:CreateButtons()
+	self.buttons = self.buttons or {}
+	local scrollChild = self.scrollChild
+	local numButtons = ceil(self:GetHeight() / self.buttonHeightReal) + 1
+	for i = #self.buttons + 1, numButtons do
+		local button = self.createButton(scrollChild)
+		if i == 1 then
+			button:SetPoint(self.initialPoint or "TOPLEFT", scrollChild, self.initialRelative or "TOPLEFT", (self.initialOffsetX or 0), (self.initialOffsetY or 0))
+		else
+			button:SetPoint(self.point or "TOPLEFT", self.buttons[i - 1], self.relativePoint or "BOTTOMLEFT", (self.offsetX or 0), (self.offsetY or 0))
+		end
+		button:SetHeight(self.buttonHeightReal)
+		button.SetHeader = setHeader
+		button.ResetHeight = resetHeight
+		button.parent = self
+		self.buttons[i] = button
+	end
+
+	self.buttonHeight = self.buttonHeightReal - (self.offsetY or 0)
+
+	scrollChild:SetWidth(self:GetWidth())
+	scrollChild:SetHeight(numButtons * self.buttonHeightReal)
+	self:SetVerticalScroll(0)
+	self:UpdateScrollChildRect()
+
+	local scrollBar = self.scrollBar
+	scrollBar:SetMinMaxValues(0, numButtons * self.buttonHeightReal)
+	scrollBar.buttonHeight = self.buttonHeightReal
+	scrollBar:SetValueStep(self.buttonHeightReal)
+	scrollBar:SetStepsPerPage(numButtons - 2)
+	scrollBar:SetValue(0)
+end
+
+function HybridPrototype:SetButtonHeight(height)
+	self.buttonHeightReal = height
+end
+
+function HybridPrototype:SetHeaderHeight(height)
+	self.headerHeight = height
+end
+
+function HybridPrototype:ExpandButton(numButtons)
+	HybridScrollFrame_ExpandButton(self, numButtons * self.buttonHeight, self.headerHeight)
+end
+
+Libra:RegisterModule(Type, Version, constructor)
\ No newline at end of file
diff --git a/libs/Libra-34/Slider.lua b/libs/Libra-34/Slider.lua
new file mode 100644
index 0000000..20202de
--- /dev/null
+++ b/libs/Libra-34/Slider.lua
@@ -0,0 +1,54 @@
+local Libra = LibStub("Libra")
+local Type, Version = "Slider", 2
+if Libra:GetModuleVersion(Type) >= Version then return end
+
+local backdrop = {
+	bgFile = [[Interface\Buttons\UI-SliderBar-Background]],
+	edgeFile = [[Interface\Buttons\UI-SliderBar-Border]],
+	edgeSize = 8,
+	insets = {left = 3, right = 3, top = 6, bottom = 6}
+}
+
+local function onEnter(self)
+	if self:IsEnabled() then
+		if self.tooltipText then
+			GameTooltip:SetOwner(self, self.tooltipOwnerPoint or "ANCHOR_RIGHT")
+			GameTooltip:SetText(self.tooltipText, nil, nil, nil, nil, true)
+		end
+		if self.tooltipRequirement then
+			GameTooltip:AddLine(self.tooltipRequirement, 1.0, 1.0, 1.0)
+			GameTooltip:Show()
+		end
+	end
+end
+
+local function onLeave(self)
+	GameTooltip:Hide()
+end
+
+local function constructor(self, parent)
+	local slider = CreateFrame("Slider", nil, parent)
+	slider:SetSize(144, 17)
+	slider:SetBackdrop(backdrop)
+	slider:SetThumbTexture([[Interface\Buttons\UI-SliderBar-Button-Horizontal]])
+	slider:SetOrientation("HORIZONTAL")
+	slider:SetObeyStepOnDrag(true)
+	slider:SetScript("OnEnter", onEnter)
+	slider:SetScript("OnLeave", onLeave)
+
+	slider.label = slider:CreateFontString(nil, nil, "GameFontNormal")
+	slider.label:SetPoint("BOTTOM", slider, "TOP")
+
+	slider.min = slider:CreateFontString(nil, nil, "GameFontHighlightSmall")
+	slider.min:SetPoint("TOPLEFT", slider, "BOTTOMLEFT", -4, 3)
+
+	slider.max = slider:CreateFontString(nil, nil, "GameFontHighlightSmall")
+	slider.max:SetPoint("TOPRIGHT", slider, "BOTTOMRIGHT", 4, 3)
+
+	slider.currentValue = slider:CreateFontString(nil, "BACKGROUND", "GameFontHighlightSmall")
+	slider.currentValue:SetPoint("CENTER", 0, -15)
+
+	return slider
+end
+
+Libra:RegisterModule(Type, Version, constructor)
\ No newline at end of file
diff --git a/libs/Libra-34/UIPanel.lua b/libs/Libra-34/UIPanel.lua
new file mode 100644
index 0000000..049a76f
--- /dev/null
+++ b/libs/Libra-34/UIPanel.lua
@@ -0,0 +1,102 @@
+local Libra = LibStub("Libra")
+local Type, Version = "UIPanel", 1
+if Libra:GetModuleVersion(Type) >= Version then return end
+
+Libra.modules[Type] = Libra.modules[Type] or {}
+
+local UIPanel = Libra.modules[Type]
+UIPanel.Prototype = UIPanel.Prototype or CreateFrame("Frame")
+
+local Prototype = UIPanel.Prototype
+local mt = {__index = Prototype}
+
+local function safecall(object, method, ...)
+	if object[method] then
+		object[method](object, ...)
+	end
+end
+
+local function constructor(self, name)
+	name = name or Libra:GetWidgetName(self.name)
+	local panel = setmetatable(CreateFrame("Frame", name, UIParent, "ButtonFrameTemplate"), mt)
+
+	tinsert(UISpecialFrames, name)
+	UIPanelWindows[name] = {
+		area = "left",
+		pushable = 1,
+		whileDead = true,
+	}
+
+	return panel
+end
+
+
+local methods = {
+	ShowPortrait = ButtonFrameTemplate_ShowPortrait,
+	HidePortrait = ButtonFrameTemplate_HidePortrait,
+	ShowAttic = ButtonFrameTemplate_ShowAttic,
+	HideAttic = ButtonFrameTemplate_HideAttic,
+	ShowButtonBar = ButtonFrameTemplate_ShowButtonBar,
+
+	GetSelectedTab = PanelTemplates_GetSelectedTab,
+	UpdateTabs = PanelTemplates_UpdateTabs,
+	EnableTab = PanelTemplates_EnableTab,
+	DisableTab = PanelTemplates_DisableTab,
+	-- GetTabWidth = PanelTemplates_GetTabWidth,
+	-- TabResize = PanelTemplates_TabResize,
+}
+
+for k, v in pairs(methods) do
+	Prototype[k] = v
+end
+
+function Prototype:SetTitleText(text)
+	self.TitleText:SetText(text)
+end
+
+function Prototype:HideButtonBar()
+	ButtonFrameTemplate_HideButtonBar(self)
+	self.Inset:SetPoint("BOTTOMRIGHT", PANEL_INSET_RIGHT_OFFSET, PANEL_INSET_BOTTOM_OFFSET + 2)
+end
+
+
+local function onClick(self)
+	self:GetParent():SelectTab(self:GetID())
+	PlaySound("igCharacterInfoTab")
+end
+
+function Prototype:CreateTab(name)
+	self.tabs = self.tabs or {}
+	if type(name) == "number" then
+		error("Tab name may not be a number.", 2)
+	end
+	-- if type(name) == "number" then
+		-- error(format("%s already has a tab named '%s'.", self:GetName(), name), 2)
+	-- end
+	local tabs = self.tabs
+	local numTabs = #tabs + 1
+	local tab = CreateFrame("Button", self:GetName().."Tab"..numTabs, self, "CharacterFrameTabButtonTemplate")
+	if numTabs == 1 then
+		tab:SetPoint("BOTTOMLEFT", 19, -30)
+	else
+		tab:SetPoint("LEFT", tabs[numTabs - 1], "RIGHT", -15, 0)
+	end
+	tab:SetID(numTabs)
+	tab:SetScript("OnClick", onClick)
+	tabs[numTabs] = tab
+	self.numTabs = numTabs
+	return tab
+end
+
+function Prototype:SelectTab(id)
+	local selectedTab = self:GetSelectedTab()
+	if selectedTab then
+		safecall(self, "OnTabDeselected", selectedTab)
+	end
+	self.selectedTab = id
+	self:UpdateTabs()
+	safecall(self, "OnTabSelected", id)
+end
+
+
+Libra:RegisterModule(Type, Version, constructor)
\ No newline at end of file
diff --git a/libs/Libra-34/Utils.lua b/libs/Libra-34/Utils.lua
new file mode 100644
index 0000000..4edee27
--- /dev/null
+++ b/libs/Libra-34/Utils.lua
@@ -0,0 +1,45 @@
+local Libra = LibStub("Libra")
+local Type, Version = "Utils", 1
+if Libra:GetModuleVersion(Type) >= Version then return end
+
+Libra.modules[Type] = Libra.modules[Type] or {}
+
+local object = Libra.modules[Type]
+object.api = object.api or {}
+object.frame = object.frame or CreateFrame("Frame")
+object.connectedRealms = object.connectedRealms or {}
+object.connectedRealmsSorted = object.connectedRealmsSorted or {}
+
+object.frame:RegisterEvent("PLAYER_LOGIN")
+object.frame:SetScript("OnEvent", function(self, event, ...)
+	object[event](object, ...)
+	self:UnregisterEvent(event)
+end)
+
+function object:PLAYER_LOGIN()
+	local _, realm = UnitFullName("player")
+	self.myRealm = realm
+	self.connectedRealmsSorted = GetAutoCompleteRealms() or {}
+	for i, v in ipairs(self.connectedRealmsSorted) do
+		self.connectedRealms[v] = true
+		if v == self.myRealm then
+			tremove(self.connectedRealmsSorted, i)
+		end
+	end
+	sort(self.connectedRealmsSorted)
+	tinsert(self.connectedRealmsSorted, 1, self.myRealm)
+end
+
+function object.api:IsConnectedRealm(realm, includeOwn)
+	if not realm then return end
+	realm = realm:gsub("[ -]", "")
+	return (realm ~= object.myRealm or includeOwn) and object.connectedRealms[realm]
+end
+
+function object.api:GetConnectedRealms()
+	return object.connectedRealmsSorted
+end
+
+Libra:RegisterMethods(object.api)
+
+Libra:RegisterModule(Type, Version)
\ No newline at end of file
diff --git a/libs/LibraEx.lua b/libs/LibraEx.lua
new file mode 100644
index 0000000..0dbb4ec
--- /dev/null
+++ b/libs/LibraEx.lua
@@ -0,0 +1,37 @@
+local Libra = LibStub("Libra")
+
+local separatorControl = {
+    func = function(parent, data)
+		local separator = parent:CreateTexture()
+		separator.SetValue = function() end
+		separator:SetHeight(1)
+		separator:SetPoint("LEFT")
+		separator:SetPoint("RIGHT", -31, 0)
+		separator:SetTexture(0.8, 0.8, 0.8, 0.5)
+		return separator
+	end,
+    data = {
+		x = 0,
+		y = -15,
+		bottomOffset = 0,
+	}
+}
+
+Libra:AddControlType("Separator", separatorControl.func, separatorControl.data)
+
+local customControl = {
+    func = function(parent, data)
+        local custom = CreateFrame("Frame", nil, parent)
+		custom.SetValue = function() end
+        custom.label = custom:CreateFontString(nil, nil, "GameFontNormal")
+        custom.label:SetPoint("LEFT", custom, "RIGHT", 0, 1)
+        return custom
+    end,
+    data = {
+        x = 0,
+        y = -20,
+        bottomOffset = 0,
+    }
+}
+
+Libra:AddControlType("Custom", customControl.func, customControl.data)
\ No newline at end of file
diff --git a/options/General.lua b/options/General.lua
index e452fc0..db7b52c 100644
--- a/options/General.lua
+++ b/options/General.lua
@@ -88,9 +88,9 @@ function options:OnInitialize()
 		{
 			type = "Custom",
 			text = L["Tooltip"],
-			init = function(control)
+			--[[init = function(control)
 				ui:CreateSeparator(control)
-			end
+			end]]
 		},
 		{
 			type = "CheckButton",
diff --git a/options/UI.lua b/options/UI.lua
index 6119b05..5de8f8e 100644
--- a/options/UI.lua
+++ b/options/UI.lua
@@ -192,7 +192,6 @@ function ui:CreateList(parent)

 	function list:Update()
 		for i, item in ipairs(items) do
-			item:Deselect()
 			local child = item.frame
 			child:ClearAllPoints()
 			if i == 1 then