diff lib/AceConsole-2.0/AceConsole-2.0.lua @ 22:1b9323256a1b

Merging in 1.0 dev tree
author Flick <flickerstreak@gmail.com>
date Fri, 07 Mar 2008 22:10:55 +0000
parents libs/AceConsole-2.0/AceConsole-2.0.lua@c11ca1d8ed91
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/AceConsole-2.0/AceConsole-2.0.lua	Fri Mar 07 22:10:55 2008 +0000
@@ -0,0 +1,2675 @@
+--[[
+Name: AceConsole-2.0
+Revision: $Rev: 48940 $
+Developed by: The Ace Development Team (http://www.wowace.com/index.php/The_Ace_Development_Team)
+Inspired By: Ace 1.x by Turan (turan@gryphon.com)
+Website: http://www.wowace.com/
+Documentation: http://www.wowace.com/index.php/AceConsole-2.0
+SVN: http://svn.wowace.com/wowace/trunk/Ace2/AceConsole-2.0
+Description: Mixin to allow for input/output capabilities. This uses the
+             AceOptions data table format to determine input.
+             http://www.wowace.com/index.php/AceOptions_data_table
+Dependencies: AceLibrary, AceOO-2.0
+License: LGPL v2.1
+]]
+
+local MAJOR_VERSION = "AceConsole-2.0"
+local MINOR_VERSION = "$Revision: 48940 $"
+
+if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary.") end
+if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end
+
+if not AceLibrary:HasInstance("AceOO-2.0") then error(MAJOR_VERSION .. " requires AceOO-2.0.") end
+
+-- #AUTODOC_NAMESPACE AceConsole
+
+local MAP_ONOFF, USAGE, IS_CURRENTLY_SET_TO, IS_NOW_SET_TO, IS_NOT_A_VALID_OPTION_FOR, IS_NOT_A_VALID_VALUE_FOR, NO_OPTIONS_AVAILABLE, OPTION_HANDLER_NOT_FOUND, OPTION_HANDLER_NOT_VALID, OPTION_IS_DISABLED, KEYBINDING_USAGE, DEFAULT_CONFIRM_MESSAGE
+if GetLocale() == "deDE" then
+	MAP_ONOFF = { [false] = "|cffff0000Aus|r", [true] = "|cff00ff00An|r" }
+	USAGE = "Benutzung"
+	IS_CURRENTLY_SET_TO = "|cffffff7f%s|r steht momentan auf |cffffff7f[|r%s|cffffff7f]|r"
+	IS_NOW_SET_TO = "|cffffff7f%s|r ist nun auf |cffffff7f[|r%s|cffffff7f]|r gesetzt"
+	IS_NOT_A_VALID_OPTION_FOR = "[|cffffff7f%s|r] ist keine g\195\188ltige Option f\195\188r |cffffff7f%s|r"
+	IS_NOT_A_VALID_VALUE_FOR = "[|cffffff7f%s|r] ist kein g\195\188ltiger Wert f\195\188r |cffffff7f%s|r"
+	NO_OPTIONS_AVAILABLE = "Keine Optionen verfügbar"
+	OPTION_HANDLER_NOT_FOUND = "Optionen handler |cffffff7f%q|r nicht gefunden."
+	OPTION_HANDLER_NOT_VALID = "Optionen handler nicht g\195\188ltig."
+	OPTION_IS_DISABLED = "Option |cffffff7f%s|r deaktiviert."
+	KEYBINDING_USAGE = "<ALT-CTRL-SHIFT-KEY>" -- fix
+	DEFAULT_CONFIRM_MESSAGE = "Are you sure you want to perform `%s'?" -- fix
+elseif GetLocale() == "frFR" then
+	MAP_ONOFF = { [false] = "|cffff0000Inactif|r", [true] = "|cff00ff00Actif|r" }
+	USAGE = "Utilisation"
+	IS_CURRENTLY_SET_TO = "|cffffff7f%s|r est actuellement positionn\195\169 sur |cffffff7f[|r%s|cffffff7f]|r"
+	IS_NOW_SET_TO = "|cffffff7f%s|r est maintenant positionn\195\169 sur |cffffff7f[|r%s|cffffff7f]|r"
+	IS_NOT_A_VALID_OPTION_FOR = "[|cffffff7f%s|r] n'est pas une option valide pour |cffffff7f%s|r"
+	IS_NOT_A_VALID_VALUE_FOR = "[|cffffff7f%s|r] n'est pas une valeur valide pour |cffffff7f%s|r"
+	NO_OPTIONS_AVAILABLE = "Pas d'options disponibles"
+	OPTION_HANDLER_NOT_FOUND = "Le gestionnaire d'option |cffffff7f%q|r n'a pas \195\169t\195\169 trouv\195\169."
+	OPTION_HANDLER_NOT_VALID = "Le gestionnaire d'option n'est pas valide."
+	OPTION_IS_DISABLED = "L'option |cffffff7f%s|r est d\195\169sactiv\195\169e."
+	KEYBINDING_USAGE = "<ALT-CTRL-SHIFT-KEY>" -- fix
+	DEFAULT_CONFIRM_MESSAGE = "Are you sure you want to perform `%s'?" -- fix
+elseif GetLocale() == "koKR" then
+	MAP_ONOFF = { [false] = "|cffff0000끔|r", [true] = "|cff00ff00켬|r" }
+	USAGE = "사용법"
+	IS_CURRENTLY_SET_TO = "|cffffff7f%s|r|1은;는; 현재 상태는 |cffffff7f[|r%s|cffffff7f]|r|1으로;로; 설정되어 있습니다."
+	IS_NOW_SET_TO = "|cffffff7f%s|r|1을;를; |cffffff7f[|r%s|cffffff7f]|r 상태로 변경합니다."
+	IS_NOT_A_VALID_OPTION_FOR = "[|cffffff7f%s|r]|1은;는; |cffffff7f%s|r에서 사용 불가능한 설정입니다."
+	IS_NOT_A_VALID_VALUE_FOR = "[|cffffff7f%s|r]|1은;는; |cffffff7f%s|r에서 사용 불가능한 설정 값입니다."
+	NO_OPTIONS_AVAILABLE = "가능한 설정이 없습니다."
+	OPTION_HANDLER_NOT_FOUND = "설정 조정 값인 |cffffff7f%q|r|1을;를; 찾지 못했습니다."
+	OPTION_HANDLER_NOT_VALID = "설정 조정 값이 올바르지 않습니다."
+	OPTION_IS_DISABLED = "|cffffff7f%s|r 설정은 사용할 수 없습니다."
+	KEYBINDING_USAGE = "<ALT-CTRL-SHIFT-KEY>"
+	DEFAULT_CONFIRM_MESSAGE = "정말 당신은 `%s'|1을;를; 하시겠습니까?"
+elseif GetLocale() == "zhCN" then
+	MAP_ONOFF = { [false] = "|cffff0000\229\133\179\233\151\173|r", [true] = "|cff00ff00\229\188\128\229\144\175|r" }
+	USAGE = "\231\148\168\230\179\149"
+	IS_CURRENTLY_SET_TO = "|cffffff7f%s|r \229\189\147\229\137\141\232\162\171\232\174\190\231\189\174 |cffffff7f[|r%s|cffffff7f]|r"
+	IS_NOW_SET_TO = "|cffffff7f%s|r \231\142\176\229\156\168\232\162\171\232\174\190\231\189\174\228\184\186 |cffffff7f[|r%s|cffffff7f]|r"
+	IS_NOT_A_VALID_OPTION_FOR = "[|cffffff7f%s|r] \228\184\141\230\152\175\228\184\128\228\184\170\230\156\137\230\149\136\231\154\132\233\128\137\233\161\185 \228\184\186 |cffffff7f%s|r"
+	IS_NOT_A_VALID_VALUE_FOR = "[|cffffff7f%s|r] \228\184\141\230\152\175\228\184\128\228\184\170\230\156\137\230\149\136\229\128\188 \228\184\186 |cffffff7f%s|r"
+	NO_OPTIONS_AVAILABLE = "\230\178\161\230\156\137\233\128\137\233\161\185\229\143\175\231\148\168"
+	OPTION_HANDLER_NOT_FOUND = "\233\128\137\233\161\185\229\164\132\231\144\134\231\168\139\229\186\143 |cffffff7f%q|r \230\178\161\230\159\165\230\137\190."
+	OPTION_HANDLER_NOT_VALID = "\233\128\137\233\161\185\229\164\132\231\144\134\231\168\139\229\186\143 \230\151\160\230\149\136."
+	OPTION_IS_DISABLED = "\233\128\137\233\161\185 |cffffff7f%s|r \228\184\141\229\174\140\230\149\180."
+	KEYBINDING_USAGE = "<ALT-CTRL-SHIFT-KEY>" -- fix
+	DEFAULT_CONFIRM_MESSAGE = "Are you sure you want to perform `%s'?" -- fix
+elseif GetLocale() == "zhTW" then
+	MAP_ONOFF = { [false] = "|cffff0000關閉|r", [true] = "|cff00ff00開啟|r" }
+	USAGE = "用法"
+	IS_CURRENTLY_SET_TO = "|cffffff7f%s|r目前的設定為|cffffff7f[|r%s|cffffff7f]|r"
+	IS_NOW_SET_TO = "|cffffff7f%s|r現在被設定為|cffffff7f[|r%s|cffffff7f]|r"
+	IS_NOT_A_VALID_OPTION_FOR = "對於|cffffff7f%2$s|r,[|cffffff7f%1$s|r]是一個不符合規定的選項"
+	IS_NOT_A_VALID_VALUE_FOR = "對於|cffffff7f%2$s|r,[|cffffff7f%1$s|r]是一個不符合規定的數值"
+	NO_OPTIONS_AVAILABLE = "沒有可用的選項"
+	OPTION_HANDLER_NOT_FOUND = "找不到|cffffff7f%q|r選項處理器。"
+	OPTION_HANDLER_NOT_VALID = "選項處理器不符合規定。"
+	OPTION_IS_DISABLED = "|cffffff7f%s|r已被停用。"
+	KEYBINDING_USAGE = "<Alt-Ctrl-Shift-鍵>"
+	DEFAULT_CONFIRM_MESSAGE = "是否執行「%s」?"
+elseif GetLocale() == "esES" then
+	MAP_ONOFF = { [false] = "|cffff0000Desactivado|r", [true] = "|cff00ff00Activado|r" }
+	USAGE = "Uso"
+	IS_CURRENTLY_SET_TO = "|cffffff7f%s|r est\195\161 establecido actualmente a |cffffff7f[|r%s|cffffff7f]|r"
+	IS_NOW_SET_TO = "|cffffff7f%s|r se ha establecido a |cffffff7f[|r%s|cffffff7f]|r"
+	IS_NOT_A_VALID_OPTION_FOR = "[|cffffff7f%s|r] no es una opci\195\179n valida para |cffffff7f%s|r"
+	IS_NOT_A_VALID_VALUE_FOR = "[|cffffff7f%s|r] no es un valor v\195\161lido para |cffffff7f%s|r"
+	NO_OPTIONS_AVAILABLE = "No hay opciones disponibles"
+	OPTION_HANDLER_NOT_FOUND = "Gestor de opciones |cffffff7f%q|r no encontrado."
+	OPTION_HANDLER_NOT_VALID = "Gestor de opciones no v\195\161lido."
+	OPTION_IS_DISABLED = "La opci\195\179n |cffffff7f%s|r est\195\161 desactivada."
+	KEYBINDING_USAGE = "<ALT-CTRL-SHIFT-KEY>"
+	DEFAULT_CONFIRM_MESSAGE = "Are you sure you want to perform `%s'?" -- fix
+else -- enUS
+	MAP_ONOFF = { [false] = "|cffff0000Off|r", [true] = "|cff00ff00On|r" }
+	USAGE = "Usage"
+	IS_CURRENTLY_SET_TO = "|cffffff7f%s|r is currently set to |cffffff7f[|r%s|cffffff7f]|r"
+	IS_NOW_SET_TO = "|cffffff7f%s|r is now set to |cffffff7f[|r%s|cffffff7f]|r"
+	IS_NOT_A_VALID_OPTION_FOR = "[|cffffff7f%s|r] is not a valid option for |cffffff7f%s|r"
+	IS_NOT_A_VALID_VALUE_FOR = "[|cffffff7f%s|r] is not a valid value for |cffffff7f%s|r"
+	NO_OPTIONS_AVAILABLE = "No options available"
+	OPTION_HANDLER_NOT_FOUND = "Option handler |cffffff7f%q|r not found."
+	OPTION_HANDLER_NOT_VALID = "Option handler not valid."
+	OPTION_IS_DISABLED = "Option |cffffff7f%s|r is disabled."
+	KEYBINDING_USAGE = "<ALT-CTRL-SHIFT-KEY>"
+	DEFAULT_CONFIRM_MESSAGE = "Are you sure you want to perform `%s'?"
+end
+
+local NONE = NONE or "None"
+
+local AceOO = AceLibrary("AceOO-2.0")
+local AceEvent
+
+local AceConsole = AceOO.Mixin { "Print", "PrintComma", "PrintLiteral", "CustomPrint", "RegisterChatCommand" }
+local Dewdrop
+
+local _G = getfenv(0)
+
+local function print(text, name, r, g, b, frame, delay)
+	if not text or text:len() == 0 then
+		text = " "
+	end
+	if not name or name == AceConsole then
+	else
+		text = "|cffffff78" .. tostring(name) .. ":|r " .. text
+	end
+	local last_color
+	for t in text:gmatch("[^\n]+") do
+		(frame or DEFAULT_CHAT_FRAME):AddMessage(last_color and "|cff" .. last_color .. t or t, r, g, b, nil, delay or 5)
+		if not last_color or t:find("|r") or t:find("|c") then
+			last_color = t:match(".*|c[fF][fF](%x%x%x%x%x%x)[^|]-$")
+		end
+	end
+	return text
+end
+
+local real_tostring = tostring
+
+local function tostring(t)
+	if type(t) == "table" then
+		if type(rawget(t, 0)) == "userdata" and type(t.GetObjectType) == "function" then
+			return ("<%s:%s>"):format(t:GetObjectType(), t:GetName() or "(anon)")
+		end
+	end
+	return real_tostring(t)
+end
+
+local getkeystring
+
+local function isList(t)
+	local n = #t
+	for k,v in pairs(t) do
+		if type(k) ~= "number" then
+			return false
+		elseif k < 1 or k > n then
+			return false
+		end
+	end
+	return true
+end
+
+local findGlobal = setmetatable({}, {__index=function(self, t)
+	for k,v in pairs(_G) do
+		if v == t then
+			k = tostring(k)
+			self[v] = k
+			return k
+		end
+	end
+	self[t] = false
+	return false
+end})
+
+local recurse = {}
+local timeToEnd
+local GetTime = GetTime
+local type = type
+
+local new, del
+do
+	local cache = setmetatable({},{__mode='k'})
+	function new()
+		local t = next(cache)
+		if t then
+			cache[t] = nil
+			return t
+		else
+			return {}
+		end
+	end
+
+	function del(t)
+		for k in pairs(t) do
+			t[k] = nil
+		end
+		cache[t] = true
+		return nil
+	end
+end
+
+local function ignoreCaseSort(alpha, bravo)
+	if not alpha or not bravo then
+		return false
+	end
+	return tostring(alpha):lower() < tostring(bravo):lower()
+end
+
+local function specialSort(alpha, bravo)
+	if alpha == nil or bravo == nil then
+		return false
+	end
+	local type_alpha, type_bravo = type(alpha), type(bravo)
+	if type_alpha ~= type_bravo then
+		return type_alpha < type_bravo
+	end
+	if type_alpha == "string" then
+		return alpha:lower() < bravo:lower()
+	elseif type_alpha == "number" then
+		return alpha < bravo
+	elseif type_alpha == "table" then
+		return #alpha < #bravo
+	elseif type_alpha == "boolean" then
+		return not alpha
+	else
+		return false
+	end
+end
+
+local function escapeChar(c)
+    return ("\\%03d"):format(c:byte())
+end
+
+local function literal_tostring_prime(t, depth)
+	if type(t) == "string" then
+		return ("|cff00ff00%q|r"):format((t:gsub("|", "||"))):gsub("[\001-\012\014-\031\128-\255]", escapeChar)
+	elseif type(t) == "table" then
+		if t == _G then
+			return "|cffffea00_G|r"
+		end
+		if type(rawget(t, 0)) == "userdata" and type(t.GetObjectType) == "function" then
+			return ("|cffffea00<%s:%s>|r"):format(t:GetObjectType(), t:GetName() or "(anon)")
+		end
+		if next(t) == nil then
+			local mt = getmetatable(t)
+			if type(mt) == "table" and type(mt.__raw) == "table" then
+				t = mt.__raw
+			end
+		end
+		if recurse[t] then
+			local g = findGlobal[t]
+			if g then
+				return ("|cff9f9f9f<Recursion _G[%q]>|r"):format(g)
+			else
+				return ("|cff9f9f9f<Recursion %s>|r"):format(real_tostring(t):gsub("|", "||"))
+			end
+		elseif GetTime() > timeToEnd then
+			local g = findGlobal[t]
+			if g then
+				return ("|cff9f9f9f<Timeout _G[%q]>|r"):format(g)
+			else
+				return ("|cff9f9f9f<Timeout %s>|r"):format(real_tostring(t):gsub("|", "||"))
+			end
+		elseif depth >= 2 then
+			local g = findGlobal[t]
+			if g then
+				return ("|cff9f9f9f<_G[%q]>|r"):format(g)
+			else
+				return ("|cff9f9f9f<%s>|r"):format(real_tostring(t):gsub("|", "||"))
+			end
+		end
+		recurse[t] = true
+		if next(t) == nil then
+			return "{}"
+		elseif next(t, (next(t))) == nil then
+			local k, v = next(t)
+			if k == 1 then
+				return "{ " .. literal_tostring_prime(v, depth+1) .. " }"
+			else
+				return "{ " .. getkeystring(k, depth+1) .. " = " .. literal_tostring_prime(v, depth+1) .. " }"
+			end
+		end
+		local s
+		local g = findGlobal[t]
+		if g then
+			s = ("{ |cff9f9f9f-- _G[%q]|r\n"):format(g)
+		else
+			s = "{ |cff9f9f9f-- " .. real_tostring(t):gsub("|", "||") .. "|r\n"
+		end
+		if isList(t) then
+			for i = 1, #t do
+				s = s .. ("    "):rep(depth+1) .. literal_tostring_prime(t[i], depth+1) .. (i == #t and "\n" or ",\n")
+			end
+		else
+			local tmp = new()
+			for k in pairs(t) do
+				tmp[#tmp+1] = k
+			end
+			table.sort(tmp, specialSort)
+			for i,k in ipairs(tmp) do
+				tmp[i] = nil
+				local v = t[k]
+				s = s .. ("    "):rep(depth+1) .. getkeystring(k, depth+1) .. " = " .. literal_tostring_prime(v, depth+1) .. (tmp[i+1] == nil and "\n" or ",\n")
+			end
+			tmp = del(tmp)
+		end
+		if g then
+			s = s .. ("    "):rep(depth) .. string.format("} |cff9f9f9f-- _G[%q]|r", g)
+		else
+			s = s .. ("    "):rep(depth) .. "} |cff9f9f9f-- " .. real_tostring(t):gsub("|", "||")
+		end
+		return s
+	end
+	if type(t) == "number" then
+		return "|cffff7fff" .. real_tostring(t) .. "|r"
+	elseif type(t) == "boolean" then
+		return "|cffff9100" .. real_tostring(t) .. "|r"
+	elseif t == nil then
+		return "|cffff7f7f" .. real_tostring(t) .. "|r"
+	else
+		return "|cffffea00" .. real_tostring(t) .. "|r"
+	end
+end
+
+function getkeystring(t, depth)
+	if type(t) == "string" then
+		if t:find("^[%a_][%a%d_]*$") then
+			return "|cff7fd5ff" .. t .. "|r"
+		end
+	end
+	return "[" .. literal_tostring_prime(t, depth) .. "]"
+end
+
+local get_stringed_args
+do
+	local function g(value, ...)
+		if select('#', ...) == 0 then
+			return literal_tostring_prime(value, 1)
+		end
+		return literal_tostring_prime(value, 1) .. ", " .. g(...)
+	end
+
+	local function f(success, ...)
+		if not success then
+			return
+		end
+		return g(...)
+	end
+
+	function get_stringed_args(func, ...)
+		return f(pcall(func, ...))
+	end
+end
+
+local function literal_tostring_frame(t)
+	local s = ("|cffffea00<%s:%s|r\n"):format(t:GetObjectType(), t:GetName() or "(anon)")
+	local __index = getmetatable(t).__index
+	local tmp, tmp2, tmp3 = new(), new(), new()
+	for k in pairs(t) do
+		if k ~= 0 then
+			tmp3[k] = true
+			tmp2[k] = true
+		end
+	end
+	for k in pairs(__index) do
+		tmp2[k] = true
+	end
+	for k in pairs(tmp2) do
+		tmp[#tmp+1] = k
+		tmp2[k] = nil
+	end
+	table.sort(tmp, ignoreCaseSort)
+	local first = true
+	for i,k in ipairs(tmp) do
+		local v = t[k]
+		local good = true
+		if k == "GetPoint" then
+			for i = 1, t:GetNumPoints() do
+				if not first then
+					s = s .. ",\n"
+				else
+					first = false
+				end
+				s = s .. "    " .. getkeystring(k, 1) .. "(" .. literal_tostring_prime(i, 1) .. ") => " .. get_stringed_args(v, t, i)
+			end
+		elseif type(v) == "function" and type(k) == "string" and (k:find("^Is") or k:find("^Get") or k:find("^Can")) then
+			local q = get_stringed_args(v, t)
+			if q then
+				if not first then
+					s = s .. ",\n"
+				else
+					first = false
+				end
+				s = s .. "    " .. getkeystring(k, 1) .. "() => " .. q
+			end
+		elseif type(v) ~= "function" or (type(v) == "function" and type(k) == "string" and tmp3[k]) then
+			if not first then
+				s = s .. ",\n"
+			else
+				first = false
+			end
+			s = s .. "    " .. getkeystring(k, 1) .. " = " .. literal_tostring_prime(v, 1)
+		else
+			good = false
+		end
+	end
+	tmp, tmp2, tmp3 = del(tmp), del(tmp2), del(tmp3)
+	s = s .. "\n|cffffea00>|r"
+	return s
+end
+
+local function literal_tostring(t, only)
+	timeToEnd = GetTime() + 0.2
+	local s
+	if only and type(t) == "table" and type(rawget(t, 0)) == "userdata" and type(t.GetObjectType) == "function" then
+		s = literal_tostring_frame(t)
+	else
+		s = literal_tostring_prime(t, 0)
+	end
+	for k,v in pairs(recurse) do
+		recurse[k] = nil
+	end
+	for k,v in pairs(findGlobal) do
+		findGlobal[k] = nil
+	end
+	return s
+end
+
+local function tostring_args(a1, ...)
+	if select('#', ...) < 1 then
+		return tostring(a1)
+	end
+	return tostring(a1), tostring_args(...)
+end
+
+local function literal_tostring_args(a1, ...)
+	if select('#', ...) < 1 then
+		return literal_tostring(a1)
+	end
+	return literal_tostring(a1), literal_tostring_args(...)
+end
+
+function AceConsole:CustomPrint(r, g, b, frame, delay, connector, a1, ...)
+	if connector == true then
+		local s
+		if select('#', ...) == 0 then
+			s = literal_tostring(a1, true)
+		else
+			s = (", "):join(literal_tostring_args(a1, ...))
+		end
+		return print(s, self, r, g, b, frame or self.printFrame, delay)
+	elseif tostring(a1):find("%%") and select('#', ...) >= 1 then
+		local success, text = pcall(string.format, tostring_args(a1, ...))
+		if success then
+			return print(text, self, r, g, b, frame or self.printFrame, delay)
+		end
+	end
+	return print((connector or " "):join(tostring_args(a1, ...)), self, r, g, b, frame or self.printFrame, delay)
+end
+
+function AceConsole:Print(...)
+	return AceConsole.CustomPrint(self, nil, nil, nil, nil, nil, " ", ...)
+end
+
+function AceConsole:PrintComma(...)
+	return AceConsole.CustomPrint(self, nil, nil, nil, nil, nil, ", ", ...)
+end
+
+function AceConsole:PrintLiteral(...)
+	return AceConsole.CustomPrint(self, nil, nil, nil, nil, nil, true, ...)
+end
+
+local work
+local argwork
+
+local function findTableLevel(self, options, chat, text, index, passTable)
+	if not index then
+		index = 1
+		if work then
+			for k,v in pairs(work) do
+				work[k] = nil
+			end
+			for k,v in pairs(argwork) do
+				argwork[k] = nil
+			end
+		else
+			work = {}
+			argwork = {}
+		end
+		local len = text:len()
+		local count
+		repeat
+			text, count = text:gsub("(|cff%x%x%x%x%x%x|Hitem:%d-:%d-:%d-:%d-|h%[[^%]]-) (.-%]|h|r)", "%1\001%2")
+		until count == 0
+		text = text:gsub("(%]|h|r)(|cff%x%x%x%x%x%x|Hitem:%d-:%d-:%d-:%d-|h%[)", "%1 %2")
+		for token in text:gmatch("([^%s]+)") do
+			local token = token
+			local num = tonumber(token)
+			if num then
+				token = num
+			else
+				token = token:gsub("\001", " ")
+			end
+			table.insert(work, token)
+		end
+	end
+
+	local path = chat
+	for i = 1, index - 1 do
+		path = path .. " " .. tostring(work[i])
+	end
+
+	local passValue = options.passValue or (passTable and work[index-1])
+	passTable = passTable or options
+
+	if type(options.args) == "table" then
+		local disabled, hidden = options.disabled, options.cmdHidden or options.hidden
+		if hidden then
+			if type(hidden) == "function" then
+				hidden = hidden(passValue)
+			elseif type(hidden) == "string" then
+				local handler = options.handler or self
+				local f = hidden
+				local neg = f:match("^~(.-)$")
+				if neg then
+					f = neg
+				end
+				if type(handler[f]) ~= "function" then
+					AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(f)))
+				end
+				hidden = handler[f](handler, passValue)
+				if neg then
+					hidden = not hidden
+				end
+			end
+		end
+		if hidden then
+			disabled = true
+		elseif disabled then
+			if type(disabled) == "function" then
+				disabled = disabled(passValue)
+			elseif type(disabled) == "string" then
+				local handler = options.handler or self
+				local f = disabled
+				local neg = f:match("^~(.-)$")
+				if neg then
+					f = neg
+				end
+				if type(handler[f]) ~= "function" then
+					AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(f)))
+				end
+				disabled = handler[f](handler, passValue)
+				if neg then
+					disabled = not disabled
+				end
+			end
+		end
+		if not disabled then
+			local next = work[index] and tostring(work[index]):lower()
+			local next_num = tonumber(next)
+			if next then
+				for k,v in pairs(options.args) do
+					local good = false
+					if tostring(k):gsub("%s", "-"):lower() == next then
+						good = true
+					elseif k == next_num then
+						good = true
+					elseif type(v.aliases) == "table" then
+						for _,alias in ipairs(v.aliases) do
+							if alias:gsub("%s", "-"):lower() == next then
+								good = true
+								break
+							end
+						end
+					elseif type(v.aliases) == "string" and v.aliases:gsub("%s", "-"):lower() == next then
+						good = true
+					end
+					if good then
+						work[index] = k -- revert it back to its original form as supplied in args
+						if options.pass then
+							passTable = passTable or options
+							if options.get and options.set then
+								passTable = options
+							end
+						else
+							passTable = nil
+						end
+						return findTableLevel(options.handler or self, v, chat, text, index + 1, passTable)
+					end
+				end
+			end
+		end
+	end
+	for i = index, #work do
+		table.insert(argwork, work[i])
+	end
+	return options, path, argwork, options.handler or self, passTable, passValue
+end
+
+local function validateOptionsMethods(self, options, position)
+	if type(options) ~= "table" then
+		return "Options must be a table.", position
+	end
+	self = options.handler or self
+	if options.type == "execute" then
+		if options.func and type(options.func) ~= "string" and type(options.func) ~= "function" then
+			return "func must be a string or function", position
+		end
+		if options.func and type(options.func) == "string" and type(self[options.func]) ~= "function" then
+			return ("%q is not a proper function"):format(tostring(options.func)), position
+		end
+	else
+		if options.get then
+			if type(options.get) ~= "string" and type(options.get) ~= "function" then
+				return "get must be a string or function", position
+			end
+			if type(options.get) == "string" then
+				local f = options.get
+				if options.type == "toggle" then
+					f = f:match("^~(.-)$") or f
+				end
+				if type(self[f]) ~= "function" then
+					return ("%q is not a proper function"):format(tostring(f)), position
+				end
+			end
+		end
+		if options.set then
+			if type(options.set) ~= "string" and type(options.set) ~= "function" then
+				return "set must be a string or function", position
+			end
+			if type(options.set) == "string" and type(self[options.set]) ~= "function" then
+				return ("%q is not a proper function"):format(tostring(options.set)), position
+			end
+		end
+		if options.validate and type(options.validate) ~= "table" and options.validate ~= "keybinding" then
+			if type(options.validate) ~= "string" and type(options.validate) ~= "function" then
+				return "validate must be a string or function", position
+			end
+			if type(options.validate) == "string" and type(self[options.validate]) ~= "function" then
+				return ("%q is not a proper function"):format(tostring(options.validate)), position
+			end
+		end
+	end
+	if options.disabled and type(options.disabled) == "string" then
+		local f = options.disabled
+		f = f:match("^~(.-)$") or f
+		if type(self[f]) ~= "function" then
+			return ("%q is not a proper function"):format(tostring(f)), position
+		end
+	end
+	if options.cmdHidden and type(options.cmdHidden) == "string" then
+		local f = options.cmdHidden
+		f = f:match("^~(.-)$") or f
+		if type(self[f]) ~= "function" then
+			return ("%q is not a proper function"):format(tostring(f)), position
+		end
+	end
+	if options.guiHidden and type(options.guiHidden) == "string" then
+		local f = options.guiHidden
+		f = f:match("^~(.-)$") or f
+		if type(self[f]) ~= "function" then
+			return ("%q is not a proper function"):format(tostring(f)), position
+		end
+	end
+	if options.hidden and type(options.hidden) == "string" then
+		local f = options.hidden
+		f = f:match("^~(.-)$") or f
+		if type(self[f]) ~= "function" then
+			return ("%q is not a proper function"):format(tostring(f)), position
+		end
+	end
+	if options.type == "group" and type(options.args) == "table" then
+		for k,v in pairs(options.args) do
+			if type(v) == "table" then
+				local newposition
+				if position then
+					newposition = position .. ".args." .. k
+				else
+					newposition = "args." .. k
+				end
+				local err, pos = validateOptionsMethods(self, v, newposition)
+				if err then
+					return err, pos
+				end
+			end
+		end
+	end
+end
+
+local function validateOptions(options, position, baseOptions, fromPass)
+	if not baseOptions then
+		baseOptions = options
+	end
+	if type(options) ~= "table" then
+		return "Options must be a table.", position
+	end
+	local kind = options.type
+	if type(kind) ~= "string" then
+		return '"type" must be a string.', position
+	elseif kind ~= "group" and kind ~= "range" and kind ~= "text" and kind ~= "execute" and kind ~= "toggle" and kind ~= "color" and kind ~= "header" then
+		return '"type" must either be "range", "text", "group", "toggle", "execute", "color", or "header".', position
+	end
+	if options.aliases then
+		if type(options.aliases) ~= "table" and type(options.aliases) ~= "string" then
+			return '"alias" must be a table or string', position
+		end
+	end
+	if not fromPass then
+		if kind == "execute" then
+			if type(options.func) ~= "string" and type(options.func) ~= "function" then
+				return '"func" must be a string or function', position
+			end
+		elseif kind == "range" or kind == "text" or kind == "toggle" then
+			if type(options.set) ~= "string" and type(options.set) ~= "function" then
+				return '"set" must be a string or function', position
+			end
+			if kind == "text" and options.get == false then
+			elseif type(options.get) ~= "string" and type(options.get) ~= "function" then
+				return '"get" must be a string or function', position
+			end
+		elseif kind == "group" and options.pass then
+			if options.pass ~= true then
+				return '"pass" must be either nil, true, or false', position
+			end
+			if not options.func then
+				if type(options.set) ~= "string" and type(options.set) ~= "function" then
+					return '"set" must be a string or function', position
+				end
+				if type(options.get) ~= "string" and type(options.get) ~= "function" then
+					return '"get" must be a string or function', position
+				end
+			elseif type(options.func) ~= "string" and type(options.func) ~= "function" then
+				return '"func" must be a string or function', position
+			end
+		end
+	end
+	if options ~= baseOptions then
+		if kind == "header" then
+		elseif type(options.desc) ~= "string" then
+			return '"desc" must be a string', position
+		elseif options.desc:len() == 0 then
+			return '"desc" cannot be a 0-length string', position
+		end
+	end
+
+	if options ~= baseOptions or kind == "range" or kind == "text" or kind == "toggle" or kind == "color" then
+		if options.type == "header" and not options.cmdName and not options.name then
+		elseif options.cmdName then
+			if type(options.cmdName) ~= "string" then
+				return '"cmdName" must be a string or nil', position
+			elseif options.cmdName:len() == 0 then
+				return '"cmdName" cannot be a 0-length string', position
+			end
+			if type(options.guiName) ~= "string" then
+				if not options.guiNameIsMap then
+					return '"guiName" must be a string or nil', position
+				end
+			elseif options.guiName:len() == 0 then
+				return '"guiName" cannot be a 0-length string', position
+			end
+		else
+			if type(options.name) ~= "string" then
+				return '"name" must be a string', position
+			elseif options.name:len() == 0 then
+				return '"name" cannot be a 0-length string', position
+			end
+		end
+	end
+	if options.guiNameIsMap then
+		if type(options.guiNameIsMap) ~= "boolean" then
+			return '"guiNameIsMap" must be a boolean or nil', position
+		elseif options.type ~= "toggle" then
+			return 'if "guiNameIsMap" is true, then "type" must be set to \'toggle\'', position
+		elseif type(options.map) ~= "table" then
+			return '"map" must be a table', position
+		end
+	end
+	if options.message and type(options.message) ~= "string" then
+		return '"message" must be a string or nil', position
+	end
+	if options.error and type(options.error) ~= "string" then
+		return '"error" must be a string or nil', position
+	end
+	if options.current and type(options.current) ~= "string" then
+		return '"current" must be a string or nil', position
+	end
+	if options.order then
+		if type(options.order) ~= "number" or (-1 < options.order and options.order < 0.999) then
+			return '"order" must be a non-zero number or nil', position
+		end
+	end
+	if options.disabled then
+		if type(options.disabled) ~= "function" and type(options.disabled) ~= "string" and options.disabled ~= true then
+			return '"disabled" must be a function, string, or boolean', position
+		end
+	end
+	if options.cmdHidden then
+		if type(options.cmdHidden) ~= "function" and type(options.cmdHidden) ~= "string" and options.cmdHidden ~= true then
+			return '"cmdHidden" must be a function, string, or boolean', position
+		end
+	end
+	if options.guiHidden then
+		if type(options.guiHidden) ~= "function" and type(options.guiHidden) ~= "string" and options.guiHidden ~= true then
+			return '"guiHidden" must be a function, string, or boolean', position
+		end
+	end
+	if options.hidden then
+		if type(options.hidden) ~= "function" and type(options.hidden) ~= "string" and options.hidden ~= true then
+			return '"hidden" must be a function, string, or boolean', position
+		end
+	end
+	if kind == "text" then
+		if type(options.validate) == "table" then
+			local t = options.validate
+			local iTable = nil
+			for k,v in pairs(t) do
+				if type(k) == "number" then
+					if iTable == nil then
+						iTable = true
+					elseif not iTable then
+						return '"validate" must either have all keys be indexed numbers or strings', position
+					elseif k < 1 or k > #t then
+						return '"validate" numeric keys must be indexed properly. >= 1 and <= #validate', position
+					end
+				else
+					if iTable == nil then
+						iTable = false
+					elseif iTable then
+						return '"validate" must either have all keys be indexed numbers or strings', position
+					end
+				end
+				if type(v) ~= "string" then
+					return '"validate" values must all be strings', position
+				end
+			end
+			if options.multiToggle and options.multiToggle ~= true then
+				return '"multiToggle" must be a boolean or nil if "validate" is a table', position
+			end
+		elseif options.validate == "keybinding" then
+
+		else
+			if type(options.usage) ~= "string" then
+				return '"usage" must be a string', position
+			elseif options.validate and type(options.validate) ~= "string" and type(options.validate) ~= "function" then
+				return '"validate" must be a string, function, or table', position
+			end
+		end
+		if options.multiToggle and type(options.validate) ~= "table" then
+			return '"validate" must be a table if "multiToggle" is true', position
+		end
+	elseif kind == "range" then
+		if options.min or options.max then
+			if type(options.min) ~= "number" then
+				return '"min" must be a number', position
+			elseif type(options.max) ~= "number" then
+				return '"max" must be a number', position
+			elseif options.min >= options.max then
+				return '"min" must be less than "max"', position
+			end
+		end
+		if options.step then
+			if type(options.step) ~= "number" then
+				return '"step" must be a number', position
+			elseif options.step < 0 then
+				return '"step" must be nonnegative', position
+			end
+		end
+		if options.isPercent and options.isPercent ~= true then
+			return '"isPercent" must either be nil, true, or false', position
+		end
+	elseif kind == "toggle" then
+		if options.map then
+			if type(options.map) ~= "table" then
+				return '"map" must be a table', position
+			elseif type(options.map[true]) ~= "string" then
+				return '"map[true]" must be a string', position
+			elseif type(options.map[false]) ~= "string" then
+				return '"map[false]" must be a string', position
+			end
+		end
+	elseif kind == "color" then
+		if options.hasAlpha and options.hasAlpha ~= true then
+			return '"hasAlpha" must be nil, true, or false', position
+		end
+	elseif kind == "group" then
+		if options.pass and options.pass ~= true then
+			return '"pass" must be nil, true, or false', position
+		end
+		if type(options.args) ~= "table" then
+			return '"args" must be a table', position
+		end
+		for k,v in pairs(options.args) do
+			if type(k) ~= "number" then
+				if type(k) ~= "string" then
+					return '"args" keys must be strings or numbers', position
+				elseif k:len() == 0 then
+					return '"args" keys must not be 0-length strings.', position
+				end
+			end
+			if type(v) ~= "table" then
+				if type(k) == "number" then
+					return '"args" values must be tables', position and position .. "[" .. k .. "]" or "[" .. k .. "]"
+				else
+					return '"args" values must be tables', position and position .. "." .. k or k
+				end
+			end
+			local newposition
+			if type(k) == "number" then
+				newposition = position and position .. ".args[" .. k .. "]" or "args[" .. k .. "]"
+			else
+				newposition = position and position .. ".args." .. k or "args." .. k
+			end
+			local err, pos = validateOptions(v, newposition, baseOptions, options.pass)
+			if err then
+				return err, pos
+			end
+		end
+	elseif kind == "execute" then
+		if type(options.confirm) ~= "string" and type(options.confirm) ~= "boolean" and type(options.confirm) ~= "nil" then
+			return '"confirm" must be a string, boolean, or nil', position
+		end
+	end
+end
+
+local colorTable
+local colorFunc
+local colorCancelFunc
+
+local function keybindingValidateFunc(text)
+	if text == nil or text == "NONE" then
+		return nil
+	end
+	text = text:upper()
+	local shift, ctrl, alt
+	local modifier
+	while true do
+		if text == "-" then
+			break
+		end
+		modifier, text = strsplit('-', text, 2)
+		if text then
+			if modifier ~= "SHIFT" and modifier ~= "CTRL" and modifier ~= "ALT" then
+				return false
+			end
+			if modifier == "SHIFT" then
+				if shift then
+					return false
+				end
+				shift = true
+			end
+			if modifier == "CTRL" then
+				if ctrl then
+					return false
+				end
+				ctrl = true
+			end
+			if modifier == "ALT" then
+				if alt then
+					return false
+				end
+				alt = true
+			end
+		else
+			text = modifier
+			break
+		end
+	end
+	if not text:find("^F%d+$") and text ~= "CAPSLOCK" and text:len() ~= 1 and (text:byte() < 128 or text:len() > 4) and not _G["KEY_" .. text] then
+		return false
+	end
+	local s = text
+	if shift then
+		s = "SHIFT-" .. s
+	end
+	if ctrl then
+		s = "CTRL-" .. s
+	end
+	if alt then
+		s = "ALT-" .. s
+	end
+	return s
+end
+AceConsole.keybindingValidateFunc = keybindingValidateFunc
+
+local order
+
+local mysort_args
+local mysort
+
+local function icaseSort(alpha, bravo)
+	if type(alpha) == "number" and type(bravo) == "number" then
+		return alpha < bravo
+	end
+	return tostring(alpha):lower() < tostring(bravo):lower()
+end
+
+local tmp = {}
+local function printUsage(self, handler, realOptions, options, path, args, passValue, quiet, filter)
+	if filter then
+		filter = "^" .. filter:gsub("([%(%)%.%*%+%-%[%]%?%^%$%%])", "%%%1")
+	end
+	local hidden, disabled = options.cmdHidden or options.hidden, options.disabled
+	if hidden then
+		if type(hidden) == "function" then
+			hidden = hidden(options.passValue)
+		elseif type(hidden) == "string" then
+			local f = hidden
+			local neg = f:match("^~(.-)$")
+			if neg then
+				f = neg
+			end
+			if type(handler[f]) ~= "function" then
+				AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(f)))
+			end
+			hidden = handler[f](handler, options.passValue)
+			if neg then
+				hidden = not hidden
+			end
+		end
+	end
+	if hidden then
+		disabled = true
+	elseif disabled then
+		if type(disabled) == "function" then
+			disabled = disabled(options.passValue)
+		elseif type(disabled) == "string" then
+			local f = disabled
+			local neg = f:match("^~(.-)$")
+			if neg then
+				f = neg
+			end
+			if type(handler[f]) ~= "function" then
+				AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(f)))
+			end
+			disabled = handler[f](handler, options.passValue)
+			if neg then
+				disabled = not disabled
+			end
+		end
+	end
+	local kind = (options.type or "group"):lower()
+	if disabled then
+		print(OPTION_IS_DISABLED:format(path), realOptions.cmdName or realOptions.name or self)
+	elseif kind == "text" then
+		local var
+		local multiToggle
+		for k in pairs(tmp) do
+			tmp[k] = nil
+		end
+		if passTable then
+			multiToggle = passTable.multiToggle
+			if not passTable.get then
+			elseif type(passTable.get) == "function" then
+				if not multiToggle then
+					var = passTable.get(passValue)
+				else
+					var = tmp
+					for k,v in pairs(options.validate) do
+						local val = type(k) ~= "number" and k or v
+						if passValue == nil then
+							var[val] = passTable.get(val) or nil
+						else
+							var[val] = passTable.get(passValue, val) or nil
+						end
+					end
+				end
+			else
+				local handler = passTable.handler or handler
+				if type(handler[passTable.get]) ~= "function" then
+					AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(passTable.get)))
+				end
+				var = handler[passTable.get](handler, passValue)
+				if not multiToggle then
+					var = handler[passTable.get](handler, passValue)
+				else
+					var = tmp
+					for k,v in pairs(options.validate) do
+						local val = type(k) ~= "number" and k or v
+						if passValue == nil then
+							var[val] = handler[passTable.get](handler, val) or nil
+						else
+							var[val] = handler[passTable.get](handler, passValue, val) or nil
+						end
+					end
+				end
+			end
+		else
+			multiToggle = options.multiToggle
+			if not options.get then
+			elseif type(options.get) == "function" then
+				if not multiToggle then
+					var = options.get(passValue)
+				else
+					var = tmp
+					for k,v in pairs(options.validate) do
+						local val = type(k) ~= "number" and k or v
+						if passValue == nil then
+							var[val] = options.get(val) or nil
+						else
+							var[val] = options.get(passValue, val) or nil
+						end
+					end
+				end
+			else
+				local handler = options.handler or handler
+				if type(handler[options.get]) ~= "function" then
+					AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(options.get)))
+				end
+				if not multiToggle then
+					var = handler[options.get](handler, passValue)
+				else
+					var = tmp
+					for k,v in pairs(options.validate) do
+						local val = type(k) ~= "number" and k or v
+						if passValue == nil then
+							var[val] = handler[options.get](handler, val) or nil
+						else
+							var[val] = handler[options.get](handler, passValue, val) or nil
+						end
+					end
+				end
+			end
+		end
+
+		local usage
+		if type(options.validate) == "table" then
+			if filter then
+				if not order then
+					order = {}
+				end
+				for k,v in pairs(options.validate) do
+					if v:find(filter) then
+						table.insert(order, v)
+					end
+				end
+				table.sort(order, icaseSort)
+				usage = "{" .. table.concat(order, " || ") .. "}"
+				for k in pairs(order) do
+					order[k] = nil
+				end
+			else
+				if not order then
+					order = {}
+				end
+				for k,v in pairs(options.validate) do
+					table.insert(order, v)
+				end
+				table.sort(order, icaseSort)
+				usage = "{" .. table.concat(order, " || ") .. "}"
+				for k in pairs(order) do
+					order[k] = nil
+				end
+			end
+			if multiToggle then
+				if not next(var) then
+					var = NONE
+				else
+					if not order then
+						order = {}
+					end
+					for k in pairs(var) do
+						if options.validate[k] then
+							order[#order+1] = options.validate[k]
+						else
+							for _,v in pairs(options.validate) do
+								if v == k or (type(v) == "string" and type(k) == "string" and v:lower() == k:lower()) then
+									order[#order+1] = v
+									break
+								end
+							end
+						end
+					end
+					table.sort(order, icaseSort)
+					var = table.concat(order, ", ")
+					for k in pairs(order) do
+						order[k] = nil
+					end
+				end
+			else
+				var = options.validate[var] or var
+			end
+		elseif options.validate == "keybinding" then
+			usage = KEYBINDING_USAGE
+		else
+			usage = options.usage or "<value>"
+		end
+		if not quiet then
+			print(("|cffffff7f%s:|r %s %s"):format(USAGE, path, usage), realOptions.cmdName or realOptions.name or self)
+		end
+		if (passTable and passTable.get) or options.get then
+			print((options.current or IS_CURRENTLY_SET_TO):format(tostring(options.cmdName or options.name), tostring(var or NONE)))
+		end
+	elseif kind == "range" then
+		local var
+		if passTable then
+			if type(passTable.get) == "function" then
+				var = passTable.get(passValue)
+			else
+				local handler = passTable.handler or handler
+				if type(handler[passTable.get]) ~= "function" then
+					AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(passTable.get)))
+				end
+				var = handler[passTable.get](handler, passValue)
+			end
+		else
+			if type(options.get) == "function" then
+				var = options.get(passValue)
+			else
+				local handler = options.handler or handler
+				if type(handler[options.get]) ~= "function" then
+					AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(options.get)))
+				end
+				var = handler[options.get](handler, passValue)
+			end
+		end
+
+		local usage
+		local min = options.min or 0
+		local max = options.max or 1
+		if options.isPercent then
+			min, max = min * 100, max * 100
+			var = tostring(var * 100) .. "%"
+		end
+		local bit = "-"
+		if min < 0 or max < 0 then
+			bit = " - "
+		end
+		usage = ("(%s%s%s)"):format(min, bit, max)
+		if not quiet then
+			print(("|cffffff7f%s:|r %s %s"):format(USAGE, path, usage), realOptions.cmdName or realOptions.name or self)
+		end
+		print((options.current or IS_CURRENTLY_SET_TO):format(tostring(options.cmdName or options.name), tostring(var or NONE)))
+	elseif kind == "group" then
+		local usage
+		if next(options.args) then
+			if not order then
+				order = {}
+			end
+			for k,v in pairs(options.args) do
+				if v.type ~= "header" then
+					local hidden = v.cmdHidden or v.hidden
+					if hidden then
+						if type(hidden) == "function" then
+							hidden = hidden()
+						elseif type(hidden) == "string" then
+							local f = hidden
+							local neg = f:match("^~(.-)$")
+							if neg then
+								f = neg
+							end
+							if type(handler[f]) ~= "function" then
+								AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(f)))
+							end
+							hidden = handler[f](handler)
+							if neg then
+								hidden = not hidden
+							end
+						end
+					end
+					if not hidden then
+						if filter then
+							if k:find(filter) then
+								table.insert(order, k)
+							elseif type(v.aliases) == "table" then
+								for _,bit in ipairs(v.aliases) do
+									if bit:find(filter) then
+										table.insert(order, k)
+										break
+									end
+								end
+							elseif type(v.aliases) == "string" then
+								if v.aliases:find(filter) then
+									table.insert(order, k)
+								end
+							end
+						else
+							table.insert(order, k)
+						end
+					end
+				end
+			end
+			if not mysort then
+				mysort = function(a, b)
+					local alpha, bravo = mysort_args[a], mysort_args[b]
+					local alpha_order = alpha and alpha.order or 100
+					local bravo_order = bravo and bravo.order or 100
+					if alpha_order == bravo_order then
+						return tostring(a):lower() < tostring(b):lower()
+					else
+						if alpha_order < 0 then
+							if bravo_order > 0 then
+								return false
+							end
+						else
+							if bravo_order < 0 then
+								return true
+							end
+						end
+						if alpha_order > 0 and bravo_order > 0 then
+							return tostring(a):lower() < tostring(b):lower()
+						end
+						return alpha_order < bravo_order
+					end
+				end
+			end
+			mysort_args = options.args
+			table.sort(order, mysort)
+			mysort_args = nil
+			if not quiet then
+				if options == realOptions then
+					if options.desc then
+						print(tostring(options.desc), realOptions.cmdName or realOptions.name or self)
+						print(("|cffffff7f%s:|r %s %s"):format(USAGE, path, "{" .. table.concat(order, " || ") .. "}"))
+					elseif self.description or self.notes then
+						print(tostring(self.description or self.notes), realOptions.cmdName or realOptions.name or self)
+						print(("|cffffff7f%s:|r %s %s"):format(USAGE, path, "{" .. table.concat(order, " || ") .. "}"))
+					else
+						print(("|cffffff7f%s:|r %s %s"):format(USAGE, path, "{" .. table.concat(order, " || ") .. "}"), realOptions.cmdName or realOptions.name or self)
+					end
+				else
+					if options.desc then
+						print(("|cffffff7f%s:|r %s %s"):format(USAGE, path, "{" .. table.concat(order, " || ") .. "}"), realOptions.cmdName or realOptions.name or self)
+						print(tostring(options.desc))
+					else
+						print(("|cffffff7f%s:|r %s %s"):format(USAGE, path, "{" .. table.concat(order, " || ") .. "}"), realOptions.cmdName or realOptions.name or self)
+					end
+				end
+			end
+			local passTable = options.pass and options or nil
+			for _,k in ipairs(order) do
+				local passValue = passTable and k or nil
+				local real_k = k
+				local v = options.args[k]
+				if v then
+					local v_p = passTable or v
+					if v.get and v.set then
+						v_p = v
+						passValue = nil
+					end
+					if v.passValue then
+						passValue = v.passValue
+					end
+					local k = tostring(k):gsub("%s", "-")
+					local disabled = v.disabled
+					if disabled then
+						if type(disabled) == "function" then
+							disabled = disabled(passValue)
+						elseif type(disabled) == "string" then
+							local f = disabled
+							local neg = f:match("^~(.-)$")
+							if neg then
+								f = neg
+							end
+							if type(handler[f]) ~= "function" then
+								AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(f)))
+							end
+							disabled = handler[f](handler, passValue)
+							if neg then
+								disabled = not disabled
+							end
+						end
+					end
+					if type(v.aliases) == "table" then
+						for _,s in ipairs(v.aliases) do
+							k = k .. " || " .. s:gsub("%s", "-")
+						end
+					elseif type(v.aliases) == "string" then
+						k = k .. " || " .. v.aliases:gsub("%s", "-")
+					end
+					if v_p.get then
+						local a1,a2,a3,a4
+						local multiToggle = v_p.type == "text" and v_p.multiToggle
+						for k in pairs(tmp) do
+							tmp[k] = nil
+						end
+						if type(v_p.get) == "function" then
+							if multiToggle then
+								a1 = tmp
+								for k,v in pairs(v.validate) do
+									local val = type(k) ~= "number" and k or v
+									if passValue == nil then
+										a1[val] = v_p.get(val) or nil
+									else
+										a1[val] = v_p.get(passValue, val) or nil
+									end
+								end
+							else
+								a1,a2,a3,a4 = v_p.get(passValue)
+							end
+						else
+							local handler = v_p.handler or handler
+							local f = v_p.get
+							local neg
+							if v.type == "toggle" then
+								neg = f:match("^~(.-)$")
+								if neg then
+									f = neg
+								end
+							end
+							if type(handler[f]) ~= "function" then
+								AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(f)))
+							end
+							if multiToggle then
+								a1 = tmp
+								for k,v in pairs(v.validate) do
+									local val = type(k) ~= "number" and k or v
+									if passValue == nil then
+										a1[val] = handler[f](handler, val) or nil
+									else
+										a1[val] = handler[f](handler, passValue, val) or nil
+									end
+								end
+							else
+								a1,a2,a3,a4 = handler[f](handler, passValue)
+							end
+							if neg then
+								a1 = not a1
+							end
+						end
+						local s
+						if v.type == "color" then
+							if v.hasAlpha then
+								if not a1 or not a2 or not a3 or not a4 then
+									s = NONE
+								else
+									s = ("|c%02x%02x%02x%02x%02x%02x%02x%02x|r"):format(a4*255, a1*255, a2*255, a3*255, a4*255, a1*255, a2*255, a3*255)
+								end
+							else
+								if not a1 or not a2 or not a3 then
+									s = NONE
+								else
+									s = ("|cff%02x%02x%02x%02x%02x%02x|r"):format(a1*255, a2*255, a3*255, a1*255, a2*255, a3*255)
+								end
+							end
+						elseif v.type == "toggle" then
+							if v.map then
+								s = tostring(v.map[a1 and true or false] or NONE)
+							else
+								s = tostring(MAP_ONOFF[a1 and true or false] or NONE)
+							end
+						elseif v.type == "range" then
+							if v.isPercent then
+								s = tostring(a1 * 100) .. "%"
+							else
+								s = tostring(a1)
+							end
+						elseif v.type == "text" and type(v.validate) == "table" then
+							if multiToggle then
+								if not next(a1) then
+									s = NONE
+								else
+									s = ''
+									for k in pairs(a1) do
+										if v.validate[k] then
+											if s == '' then
+												s = v.validate[k]
+											else
+												s = s .. ', ' .. v.validate[k]
+											end
+										else
+											for _,u in pairs(v.validate) do
+												if u == k or (type(v) == "string" and type(k) == "string" and v:lower() == k:lower()) then
+													if s == '' then
+														s = u
+													else
+														s = s .. ', ' .. u
+													end
+													break
+												end
+											end
+										end
+									end
+								end
+							else
+								s = tostring(v.validate[a1] or a1 or NONE)
+							end
+						else
+							s = tostring(a1 or NONE)
+						end
+						if disabled then
+							local s = s:gsub("|cff%x%x%x%x%x%x(.-)|r", "%1")
+							local desc = (v.desc or NONE):gsub("|cff%x%x%x%x%x%x(.-)|r", "%1")
+							print(("|cffcfcfcf - %s: [%s] %s|r"):format(k, s, desc))
+						else
+							print((" - |cffffff7f%s: [|r%s|cffffff7f]|r %s"):format(k, s, v.desc or NONE))
+						end
+					else
+						if disabled then
+							local desc = (v.desc or NONE):gsub("|cff%x%x%x%x%x%x(.-)|r", "%1")
+							print(("|cffcfcfcf - %s: %s"):format(k, desc))
+						else
+							print((" - |cffffff7f%s:|r %s"):format(k, v.desc or NONE))
+						end
+					end
+				end
+			end
+			for k in pairs(order) do
+				order[k] = nil
+			end
+		else
+			if options.desc then
+				print(("|cffffff7f%s:|r %s"):format(USAGE, path), realOptions.cmdName or realOptions.name or self)
+				print(tostring(options.desc))
+			elseif options == realOptions and (self.description or self.notes) then
+				print(tostring(self.description or self.notes), realOptions.cmdName or realOptions.name or self)
+				print(("|cffffff7f%s:|r %s"):format(USAGE, path))
+			else
+				print(("|cffffff7f%s:|r %s"):format(USAGE, path), realOptions.cmdName or realOptions.name or self)
+			end
+			print(NO_OPTIONS_AVAILABLE)
+		end
+	end
+end
+
+local function confirmPopup(message, func, ...)
+	if not StaticPopupDialogs["ACECONSOLE20_CONFIRM_DIALOG"] then
+		StaticPopupDialogs["ACECONSOLE20_CONFIRM_DIALOG"] = {}
+	end
+	local t = StaticPopupDialogs["ACECONSOLE20_CONFIRM_DIALOG"]
+	for k in pairs(t) do
+		t[k] = nil
+	end
+	t.text = message
+	t.button1 = ACCEPT or "Accept"
+	t.button2 = CANCEL or "Cancel"
+	t.OnAccept = function()
+		func(unpack(t))
+	end
+	for i = 1, select('#', ...) do
+		t[i] = select(i, ...)
+	end
+	t.timeout = 0
+	t.whileDead = 1
+	t.hideOnEscape = 1
+
+	StaticPopup_Show("ACECONSOLE20_CONFIRM_DIALOG")
+end
+
+local function handlerFunc(self, chat, msg, options)
+	if not msg then
+		msg = ""
+	else
+		msg = msg:gsub("^%s*(.-)%s*$", "%1")
+		msg = msg:gsub("%s+", " ")
+	end
+
+	local realOptions = options
+	local options, path, args, handler, passTable, passValue = findTableLevel(self, options, chat, msg)
+	if options.type == "execute" then
+		if options.func then
+			passTable = nil
+		end
+	else
+		if options.get and options.set then
+			passTable = nil
+		end
+	end
+	passValue = options.passValue or passTable and passValue
+
+	local hidden, disabled = options.cmdHidden or options.hidden, options.disabled
+	if hidden then
+		if type(hidden) == "function" then
+			hidden = hidden(passValue)
+		elseif type(hidden) == "string" then
+			local f = hidden
+			local neg = f:match("^~(.-)$")
+			if neg then
+				f = neg
+			end
+			if type(handler[f]) ~= "function" then
+				AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(f)))
+			end
+			hidden = handler[f](handler, passValue)
+			if neg then
+				hidden = not hidden
+			end
+		end
+	end
+	if hidden then
+		disabled = true
+	elseif disabled then
+		if type(disabled) == "function" then
+			disabled = disabled(passValue)
+		elseif type(disabled) == "string" then
+			local f = disabled
+			local neg = f:match("^~(.-)$")
+			if neg then
+				f = neg
+			end
+			if type(handler[f]) ~= "function" then
+				AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(f)))
+			end
+			disabled = handler[f](handler, passValue)
+			if neg then
+				disabled = not disabled
+			end
+		end
+	end
+	local _G_this = this
+	local kind = (options.type or "group"):lower()
+	local options_p = passTable or options
+	if disabled then
+		print(OPTION_IS_DISABLED:format(path), realOptions.cmdName or realOptions.name or self)
+	elseif kind == "text" then
+		if #args > 0 then
+			if (type(options.validate) == "table" and #args > 1) or (type(options.validate) ~= "table" and not options.input) then
+				local arg = table.concat(args, " ")
+				for k,v in pairs(args) do
+					args[k] = nil
+				end
+				args[1] = arg
+			end
+			if options.validate then
+				local good
+				if type(options.validate) == "function" then
+					good = options.validate(unpack(args))
+				elseif type(options.validate) == "table" then
+					local arg = args[1]
+					arg = tostring(arg):lower()
+					for k,v in pairs(options.validate) do
+						if v:lower() == arg then
+							args[1] = type(k) == "string" and k or v
+							good = true
+							break
+						end
+					end
+					if not good and type((next(options.validate))) == "string" then
+						for k,v in pairs(options.validate) do
+							if type(k) == "string" and k:lower() == arg then
+								args[1] = k
+								good = true
+								break
+							end
+						end
+					end
+				elseif options.validate == "keybinding" then
+					good = keybindingValidateFunc(unpack(args))
+					if good ~= false then
+						args[1] = good
+					end
+				else
+					if type(handler[options.validate]) ~= "function" then
+						AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(options.validate)))
+					end
+					good = handler[options.validate](handler, unpack(args))
+				end
+				if not good then
+					local usage
+					if type(options.validate) == "table" then
+						if not order then
+							order = {}
+						end
+						for k,v in pairs(options.validate) do
+							table.insert(order, v)
+						end
+						usage = "{" .. table.concat(order, " || ") .. "}"
+						for k in pairs(order) do
+							order[k] = nil
+						end
+					elseif options.validate == "keybinding" then
+						usage = KEYBINDING_USAGE
+					else
+						usage = options.usage or "<value>"
+					end
+					print((options.error or IS_NOT_A_VALID_OPTION_FOR):format(tostring(table.concat(args, " ")), path), realOptions.cmdName or realOptions.name or self)
+					print(("|cffffff7f%s:|r %s %s"):format(USAGE, path, usage))
+					return
+				end
+			end
+
+			local var
+			local multiToggle
+			for k in pairs(tmp) do
+				tmp[k] = nil
+			end
+			multiToggle = options_p.multiToggle
+			if not options_p.get then
+			elseif type(options_p.get) == "function" then
+				if multiToggle then
+					var = tmp
+					for k,v in pairs(options.validate) do
+						local val = type(k) ~= "number" and k or v
+						if passValue then
+							var[val] = options_p.get(passValue, val) or nil
+						else
+							var[val] = options_p.get(val) or nil
+						end
+					end
+				else
+					var = options_p.get(passValue)
+				end
+			else
+				if type(handler[options_p.get]) ~= "function" then
+					AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(options_p.get)))
+				end
+				if multiToggle then
+					var = tmp
+					for k,v in pairs(options.validate) do
+						local val = type(k) ~= "number" and k or v
+						if passValue then
+							var[val] = handler[options_p.get](handler, passValue, val) or nil
+						else
+							var[val] = handler[options_p.get](handler, val) or nil
+						end
+					end
+				else
+					var = handler[options_p.get](handler, passValue)
+				end
+			end
+
+			if multiToggle or var ~= args[1] then
+				if multiToggle then
+					local current = var[args[1]]
+					if current == nil and type(args[1]) == "string" then
+						for k in pairs(var) do
+							if type(k) == "string" and k:lower() == args[1]:lower() then
+								current = true
+								break
+							end
+						end
+					end
+					args[2] = not current
+				end
+				if type(options_p.set) == "function" then
+					if passValue then
+						options_p.set(passValue, unpack(args))
+					else
+						options_p.set(unpack(args))
+					end
+				else
+					if type(handler[options_p.set]) ~= "function" then
+						AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(options_p.set)))
+					end
+					if passValue then
+						handler[options_p.set](handler, passValue, unpack(args))
+					else
+						handler[options_p.set](handler, unpack(args))
+					end
+				end
+			end
+		end
+
+		if #args > 0 then
+			local var
+			local multiToggle
+			for k in pairs(tmp) do
+				tmp[k] = nil
+			end
+			multiToggle = options_p.multiToggle
+			if not options_p.get then
+			elseif type(options_p.get) == "function" then
+				if multiToggle then
+					var = tmp
+					for k,v in pairs(options_p.validate) do
+						local val = type(k) ~= "number" and k or v
+						if passValue then
+							var[val] = options_p.get(passValue, val) or nil
+						else
+							var[val] = options_p.get(val) or nil
+						end
+					end
+				else
+					var = options_p.get(passValue)
+				end
+			else
+				if type(handler[options_p.get]) ~= "function" then
+					AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(options_p.get)))
+				end
+				if multiToggle then
+					var = tmp
+					for k,v in pairs(options.validate) do
+						local val = type(k) ~= "number" and k or v
+						if passValue then
+							var[val] = handler[options_p.get](handler, passValue, val) or nil
+						else
+							var[val] = handler[options_p.get](handler, val) or nil
+						end
+					end
+				else
+					var = handler[options_p.get](handler, passValue)
+				end
+			end
+			if multiToggle then
+				if not next(var) then
+					var = NONE
+				else
+					if not order then
+						order = {}
+					end
+					for k in pairs(var) do
+						if options.validate[k] then
+							order[#order+1] = options.validate[k]
+						else
+							for _,v in pairs(options.validate) do
+								if v == k or (type(v) == "string" and type(k) == "string" and v:lower() == k:lower()) then
+									order[#order+1] = v
+									break
+								end
+							end
+						end
+					end
+					table.sort(order, icaseSort)
+					var = table.concat(order, ", ")
+					for k in pairs(order) do
+						order[k] = nil
+					end
+				end
+			elseif type(options.validate) == "table" then
+				var = options.validate[var] or var
+			end
+			if options_p.get then
+				print((options.message or IS_NOW_SET_TO):format(tostring(options.cmdName or options.name), tostring(var or NONE)), realOptions.cmdName or realOptions.name or self)
+			end
+			if var == args[1] then
+				return
+			end
+		else
+			printUsage(self, handler, realOptions, options, path, args, passValue)
+			return
+		end
+	elseif kind == "execute" then
+		local confirm = options.confirm
+		if confirm == true then
+			confirm = DEFAULT_CONFIRM_MESSAGE:format(options.desc or options.name or UNKNOWN or "Unknown")
+		end
+		if type(options_p.func) == "function" then
+			if confirm then
+				confirmPopup(confirm, options_p.func, passValue)
+			else
+				options_p.func(passValue)
+			end
+		else
+			if type(handler[options_p.func]) ~= "function" then
+				AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(options_p.func)))
+			end
+			if confirm then
+				confirmPopup(confirm, handler[options_p.func], handler, passValue)
+			else
+				handler[options_p.func](handler, passValue)
+			end
+		end
+	elseif kind == "toggle" then
+		local var
+		if type(options_p.get) == "function" then
+			var = options_p.get(passValue)
+		else
+			local f = options_p.get
+			local neg = f:match("^~(.-)$")
+			if neg then
+				f = neg
+			end
+			if type(handler[f]) ~= "function" then
+				AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(f)))
+			end
+			var = handler[f](handler, passValue)
+			if neg then
+				var = not var
+			end
+		end
+		if type(options_p.set) == "function" then
+			if passValue then
+				options_p.set(passValue, not var)
+			else
+				options_p.set(not var)
+			end
+		else
+			if type(handler[options_p.set]) ~= "function" then
+				AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(options_p.set)))
+			end
+			if passValue then
+				handler[options_p.set](handler, passValue, not var)
+			else
+				handler[options_p.set](handler, not var)
+			end
+		end
+		if type(options_p.get) == "function" then
+			var = options_p.get(passValue)
+		else
+			local f = options_p.get
+			local neg = f:match("^~(.-)$")
+			if neg then
+				f = neg
+			end
+			var = handler[f](handler, passValue)
+			if neg then
+				var = not var
+			end
+		end
+
+		print((options.message or IS_NOW_SET_TO):format(tostring(options.cmdName or options.name), (options.map or MAP_ONOFF)[var and true or false] or NONE), realOptions.cmdName or realOptions.name or self)
+	elseif kind == "range" then
+		local arg
+		if #args <= 1 then
+			arg = args[1]
+		else
+			arg = table.concat(args, " ")
+		end
+
+		if arg then
+			local min = options.min or 0
+			local max = options.max or 1
+			local good = false
+			if type(arg) == "number" then
+				if options.isPercent then
+					arg = arg / 100
+				end
+
+				if arg >= min and arg <= max then
+					good = true
+				end
+
+				if good and type(options.step) == "number" and options.step > 0 then
+					local step = options.step
+					arg = math.floor((arg - min) / step + 0.5) * step + min
+					if arg > max then
+						arg = max
+					elseif arg < min then
+						arg = min
+					end
+				end
+			end
+			if not good then
+				local usage
+				local min = options.min or 0
+				local max = options.max or 1
+				if options.isPercent then
+					min, max = min * 100, max * 100
+				end
+				local bit = "-"
+				if min < 0 or max < 0 then
+					bit = " - "
+				end
+				usage = ("(%s%s%s)"):format(min, bit, max)
+				print((options.error or IS_NOT_A_VALID_VALUE_FOR):format(tostring(arg), path), realOptions.cmdName or realOptions.name or self)
+				print(("|cffffff7f%s:|r %s %s"):format(USAGE, path, usage))
+				return
+			end
+
+			local var
+			if type(options_p.get) == "function" then
+				var = options_p.get(passValue)
+			else
+				if type(handler[options_p.get]) ~= "function" then
+					AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(options_p.get)))
+				end
+				var = handler[options_p.get](handler, passValue)
+			end
+
+			if var ~= arg then
+				if type(options_p.set) == "function" then
+					if passValue then
+						options_p.set(passValue, arg)
+					else
+						options_p.set(arg)
+					end
+				else
+					if type(handler[options_p.set]) ~= "function" then
+						AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(options_p.set)))
+					end
+					if passValue then
+						handler[options_p.set](handler, passValue, arg)
+					else
+						handler[options_p.set](handler, arg)
+					end
+				end
+			end
+		end
+
+		if arg then
+			local var
+			if type(options_p.get) == "function" then
+				var = options_p.get(passValue)
+			else
+				if type(handler[options_p.get]) ~= "function" then
+					AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(options_p.get)))
+				end
+				var = handler[options_p.get](handler, passValue)
+			end
+
+			if var and options.isPercent then
+				var = tostring(var * 100) .. "%"
+			end
+			print((options.message or IS_NOW_SET_TO):format(tostring(options.cmdName or options.name), tostring(var or NONE)), realOptions.cmdName or realOptions.name or self)
+			if var == arg then
+				return
+			end
+		else
+			printUsage(self, handler, realOptions, options, path, args, passValue)
+			return
+		end
+	elseif kind == "color" then
+		if #args > 0 then
+			local r,g,b,a
+			if #args == 1 then
+				local arg = tostring(args[1])
+				if options.hasAlpha then
+					if arg:len() == 8 and arg:find("^%x*$")  then
+						r,g,b,a = tonumber(arg:sub(1, 2), 16) / 255, tonumber(arg:sub(3, 4), 16) / 255, tonumber(arg:sub(5, 6), 16) / 255, tonumber(arg:sub(7, 8), 16) / 255
+					end
+				else
+					if arg:len() == 6 and arg:find("^%x*$") then
+						r,g,b = tonumber(arg:sub(1, 2), 16) / 255, tonumber(arg:sub(3, 4), 16) / 255, tonumber(arg:sub(5, 6), 16) / 255
+					end
+				end
+			elseif #args == 4 and options.hasAlpha then
+				local a1,a2,a3,a4 = args[1], args[2], args[3], args[4]
+				if type(a1) == "number" and type(a2) == "number" and type(a3) == "number" and type(a4) == "number" and a1 <= 1 and a2 <= 1 and a3 <= 1 and a4 <= 1 then
+					r,g,b,a = a1,a2,a3,a4
+				elseif (type(a1) == "number" or a1:len() == 2) and a1:find("^%x*$") and (type(a2) == "number" or a2:len() == 2) and a2:find("^%x*$") and (type(a3) == "number" or a3:len() == 2) and a3:find("^%x*$") and (type(a4) == "number" or a4:len() == 2) and a4:find("^%x*$") then
+					r,g,b,a = tonumber(a1, 16) / 255, tonumber(a2, 16) / 255, tonumber(a3, 16) / 255, tonumber(a4, 16) / 255
+				end
+			elseif #args == 3 and not options.hasAlpha then
+				local a1,a2,a3 = args[1], args[2], args[3]
+				if type(a1) == "number" and type(a2) == "number" and type(a3) == "number" and a1 <= 1 and a2 <= 1 and a3 <= 1 then
+					r,g,b = a1,a2,a3
+				elseif (type(a1) == "number" or a1:len() == 2) and a1:find("^%x*$") and (type(a2) == "number" or a2:len() == 2) and a2:find("^%x*$") and (type(a3) == "number" or a3:len() == 2) and a3:find("^%x*$") then
+					r,g,b = tonumber(a1, 16) / 255, tonumber(a2, 16) / 255, tonumber(a3, 16) / 255
+				end
+			end
+			if not r then
+				print((options.error or IS_NOT_A_VALID_OPTION_FOR):format(table.concat(args, ' '), path), realOptions.cmdName or realOptions.name or self)
+				print(("|cffffff7f%s:|r %s {0-1} {0-1} {0-1}%s"):format(USAGE, path, options.hasAlpha and " {0-1}" or ""))
+				return
+			end
+
+			if type(options_p.set) == "function" then
+				if passValue then
+					options_p.set(passValue, r,g,b,a)
+				else
+					options_p.set(r,g,b,a)
+				end
+			else
+				if type(handler[options_p.set]) ~= "function" then
+					AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(options_p.set)))
+				end
+				if passValue then
+					handler[options_p.set](handler, passValue, r,g,b,a)
+				else
+					handler[options_p.set](handler, r,g,b,a)
+				end
+			end
+
+			local r,g,b,a
+			if type(options_p.get) == "function" then
+				r,g,b,a = options_p.get(passValue)
+			else
+				if type(handler[options_p.get]) ~= "function" then
+					AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(options_p.get)))
+				end
+				r,g,b,a = handler[options_p.get](handler, passValue)
+			end
+
+			local s
+			if type(r) == "number" and type(g) == "number" and type(b) == "number" then
+				if options.hasAlpha and type(a) == "number" then
+					s = ("|c%02x%02x%02x%02x%02x%02x%02x%02x|r"):format(a*255, r*255, g*255, b*255, r*255, g*255, b*255, a*255)
+				else
+					s = ("|cff%02x%02x%02x%02x%02x%02x|r"):format(r*255, g*255, b*255, r*255, g*255, b*255)
+				end
+			else
+				s = NONE
+			end
+			print((options.message or IS_NOW_SET_TO):format(tostring(options.cmdName or options.name), s), realOptions.cmdName or realOptions.name or self)
+		else
+			local r,g,b,a
+			if type(options_p.get) == "function" then
+				r,g,b,a = options_p.get(passValue)
+			else
+				if type(handler[options_p.get]) ~= "function" then
+					AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(options_p.get)))
+				end
+				r,g,b,a = handler[options_p.get](handler, passValue)
+			end
+
+			if not colorTable then
+				colorTable = {}
+				local t = colorTable
+
+				if ColorPickerOkayButton then
+					local ColorPickerOkayButton_OnClick = ColorPickerOkayButton:GetScript("OnClick")
+					ColorPickerOkayButton:SetScript("OnClick", function()
+						if ColorPickerOkayButton_OnClick then
+							ColorPickerOkayButton_OnClick()
+						end
+						if t.active then
+							ColorPickerFrame.cancelFunc = nil
+							ColorPickerFrame.func = nil
+							ColorPickerFrame.opacityFunc = nil
+							local r,g,b,a
+							if t.passValue then
+								if type(t.get) == "function" then
+									r,g,b,a = t.get(t.passValue)
+								else
+									if type(t.handler[t.get]) ~= "function" then
+										AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(t.get)))
+									end
+									r,g,b,a = t.handler[t.get](t.handler, t.passValue)
+								end
+							else
+								if type(t.get) == "function" then
+									r,g,b,a = t.get()
+								else
+									if type(t.handler[t.get]) ~= "function" then
+										AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(t.get)))
+									end
+									r,g,b,a = t.handler[t.get](t.handler)
+								end
+							end
+							if r ~= t.r or g ~= t.g or b ~= t.b or (t.hasAlpha and a ~= t.a) then
+								local s
+								if type(r) == "number" and type(g) == "number" and type(b) == "number" then
+									if t.hasAlpha and type(a) == "number" then
+										s = ("|c%02x%02x%02x%02x%02x%02x%02x%02x|r"):format(a*255, r*255, g*255, b*255, r*255, g*255, b*255, a*255)
+									else
+										s = ("|cff%02x%02x%02x%02x%02x%02x|r"):format(r*255, g*255, b*255, r*255, g*255, b*255)
+									end
+								else
+									s = NONE
+								end
+								print(t.message:format(tostring(t.name), s), t.realOptions.cmdName or t.realOptions.name or self)
+							end
+							for k,v in pairs(t) do
+								t[k] = nil
+							end
+						end
+					end)
+				end
+			else
+				for k,v in pairs(colorTable) do
+					colorTable[k] = nil
+				end
+			end
+
+			if type(r) ~= "number" or type(g) ~= "number" or type(b) ~= "number" then
+				r,g,b = 1, 1, 1
+			end
+			if type(a) ~= "number" then
+				a = 1
+			end
+			local t = colorTable
+			t.r = r
+			t.g = g
+			t.b = b
+			if hasAlpha then
+				t.a = a
+			end
+			t.realOptions = realOptions
+			t.hasAlpha = options.hasAlpha
+			t.handler = handler
+			t.set = options_p.set
+			t.get = options_p.get
+			t.name = options.cmdName or options.name
+			t.message = options.message or IS_NOW_SET_TO
+			t.passValue = passValue
+			t.active = true
+
+			if not colorFunc then
+				colorFunc = function()
+					local r,g,b = ColorPickerFrame:GetColorRGB()
+					if t.hasAlpha then
+						local a = 1 - OpacitySliderFrame:GetValue()
+						if type(t.set) == "function" then
+							if t.passValue then
+								t.set(t.passValue, r,g,b,a)
+							else
+								t.set(r,g,b,a)
+							end
+						else
+							if type(t.handler[t.set]) ~= "function" then
+								AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(t.set)))
+							end
+							if t.passValue then
+								t.handler[t.set](t.handler, t.passValue, r,g,b,a)
+							else
+								t.handler[t.set](t.handler, r,g,b,a)
+							end
+						end
+					else
+						if type(t.set) == "function" then
+							if t.passValue then
+								t.set(t.passValue, r,g,b)
+							else
+								t.set(r,g,b)
+							end
+						else
+							if type(t.handler[t.set]) ~= "function" then
+								AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(t.set)))
+							end
+							if t.passValue then
+								t.handler[t.set](t.handler, t.passValue, r,g,b)
+							else
+								t.handler[t.set](t.handler, r,g,b)
+							end
+						end
+					end
+				end
+			end
+
+			ColorPickerFrame.func = colorFunc
+			ColorPickerFrame.hasOpacity = options.hasAlpha
+			if options.hasAlpha then
+				ColorPickerFrame.opacityFunc = ColorPickerFrame.func
+				ColorPickerFrame.opacity = 1 - a
+			end
+			ColorPickerFrame:SetColorRGB(r,g,b)
+
+			if not colorCancelFunc then
+				colorCancelFunc = function()
+					if t.hasAlpha then
+						if type(t.set) == "function" then
+							if t.passValue then
+								t.set(t.passValue, t.r,t.g,t.b,t.a)
+							else
+								t.set(t.r,t.g,t.b,t.a)
+							end
+						else
+							if type(t.handler[t.get]) ~= "function" then
+								AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(t.get)))
+							end
+							if t.passValue then
+								t.handler[t.set](t.handler, t.passValue, t.r,t.g,t.b,t.a)
+							else
+								t.handler[t.set](t.handler, t.r,t.g,t.b,t.a)
+							end
+						end
+					else
+						if type(t.set) == "function" then
+							if t.passValue then
+								t.set(t.passValue, t.r,t.g,t.b)
+							else
+								t.set(t.r,t.g,t.b)
+							end
+						else
+							if type(t.handler[t.set]) ~= "function" then
+								AceConsole:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(t.set)))
+							end
+							if t.passValue then
+								t.handler[t.set](t.handler, t.passValue, t.r,t.g,t.b)
+							else
+								t.handler[t.set](t.handler, t.r,t.g,t.b)
+							end
+						end
+					end
+					for k,v in pairs(t) do
+						t[k] = nil
+					end
+					ColorPickerFrame.cancelFunc = nil
+					ColorPickerFrame.func = nil
+					ColorPickerFrame.opacityFunc = nil
+				end
+			end
+
+			ColorPickerFrame.cancelFunc = colorCancelFunc
+
+			ShowUIPanel(ColorPickerFrame)
+		end
+		return
+	elseif kind == "group" then
+		if #args == 0 then
+			printUsage(self, handler, realOptions, options, path, args, passValue)
+		else
+			-- invalid argument
+			print((options.error or IS_NOT_A_VALID_OPTION_FOR):format(args[1], path), realOptions.cmdName or realOptions.name or self)
+		end
+		return
+	end
+	this = _G_this
+	if Dewdrop then
+		Dewdrop:Refresh()
+	end
+end
+
+local external
+local tmp
+function AceConsole:RegisterChatCommand(...) -- slashCommands, options, name
+	local slashCommands, options, name
+	if type((...)) == "string" then
+		if not tmp then
+			tmp = {}
+		else
+			for i in ipairs(tmp) do
+				tmp[i] = nil
+			end
+		end
+		for i = 1, select('#', ...)+1 do
+			local v = select(i, ...)
+			if type(v) == "string" then
+				tmp[#tmp+1] = v
+			else
+				slashCommands = tmp
+				options = v
+				name = select(i+1, ...)
+				break
+			end
+		end
+	else
+		slashCommands, options, name = ...
+	end
+	if type(slashCommands) ~= "table" and slashCommands ~= false then
+		AceConsole:error("Bad argument #2 to `RegisterChatCommand' (expected table, got %s)", type(slashCommands))
+	end
+	if not slashCommands and type(name) ~= "string" then
+		AceConsole:error("Bad argument #4 to `RegisterChatCommand' (expected string, got %s)", type(name))
+	end
+	if type(options) ~= "table" and type(options) ~= "function" and options ~= nil then
+		AceConsole:error("Bad argument #3 to `RegisterChatCommand' (expected table, function, or nil, got %s)", type(options))
+	end
+	if name then
+		if type(name) ~= "string" then
+			AceConsole:error("Bad argument #4 to `RegisterChatCommand' (expected string or nil, got %s)", type(name))
+		elseif not name:find("^%w+$") or name:upper() ~= name or name:len() == 0 then
+			AceConsole:error("Argument #4 must be an uppercase, letters-only string with at least 1 character")
+		end
+	end
+	if slashCommands then
+		if #slashCommands == 0 then
+			AceConsole:error("Argument #2 to `RegisterChatCommand' must include at least one string")
+		end
+
+		for k,v in pairs(slashCommands) do
+			if type(k) ~= "number" then
+				AceConsole:error("All keys in argument #2 to `RegisterChatCommand' must be numbers")
+			end
+			if type(v) ~= "string" then
+				AceConsole:error("All values in argument #2 to `RegisterChatCommand' must be strings")
+			elseif not v:find("^/[A-Za-z][A-Za-z0-9_]*$") then
+				AceConsole:error("All values in argument #2 to `RegisterChatCommand' must be in the form of \"/word\"")
+			end
+		end
+	end
+
+	if not options then
+		options = {
+			type = 'group',
+			args = {},
+			handler = self
+		}
+	end
+
+	if type(options) == "table" then
+		local err, position = validateOptions(options)
+		if err then
+			if position then
+				AceConsole:error(position .. ": " .. err)
+			else
+				AceConsole:error(err)
+			end
+		end
+
+		if not options.handler then
+			options.handler = self
+		end
+
+		if options.handler == self and options.type:lower() == "group" and self.class then
+			AceConsole:InjectAceOptionsTable(self, options)
+		end
+	end
+
+	local chat
+	if slashCommands then
+		chat = slashCommands[1]
+	else
+		chat = _G["SLASH_"..name..1]
+	end
+
+	local handler
+	if type(options) == "function" then
+		handler = options
+		for k,v in pairs(_G) do
+			if handler == v then
+				local k = k
+				handler = function(msg)
+					return _G[k](msg)
+				end
+			end
+		end
+	else
+		function handler(msg)
+			handlerFunc(self, chat, msg, options)
+		end
+	end
+
+	if not _G.SlashCmdList then
+		_G.SlashCmdList = {}
+	end
+
+	if not name and type(slashCommands) == "table" and type(slashCommands[1]) == "string" then
+		name = slashCommands[1]:gsub("%A", ""):upper()
+	end
+
+	if not name then
+		local A = ('A'):byte()
+		repeat
+			name = string.char(math.random(26) + A - 1) .. string.char(math.random(26) + A - 1) .. string.char(math.random(26) + A - 1) .. string.char(math.random(26) + A - 1) .. string.char(math.random(26) + A - 1) .. string.char(math.random(26) + A - 1) .. string.char(math.random(26) + A - 1) .. string.char(math.random(26) + A - 1)
+		until not _G.SlashCmdList[name]
+	end
+
+	if slashCommands then
+		if _G.SlashCmdList[name] then
+			local i = 0
+			while true do
+				i = i + 1
+				if _G["SLASH_"..name..i] then
+					_G["SLASH_"..name..i] = nil
+				else
+					break
+				end
+			end
+		end
+
+		local i = 0
+		for _,command in ipairs(slashCommands) do
+			i = i + 1
+			_G["SLASH_"..name..i] = command
+			if command:lower() ~= command then
+				i = i + 1
+				_G["SLASH_"..name..i] = command:lower()
+			end
+		end
+	end
+	_G.SlashCmdList[name] = handler
+	if self ~= AceConsole and self.slashCommand == nil then
+		self.slashCommand = chat
+	end
+
+	if not AceEvent and AceLibrary:HasInstance("AceEvent-2.0") then
+		external(AceConsole, "AceEvent-2.0", AceLibrary("AceEvent-2.0"))
+	end
+	if AceEvent then
+		if not AceConsole.nextAddon then
+			AceConsole.nextAddon = {}
+		end
+		if type(options) == "table" then
+			AceConsole.nextAddon[self] = options
+			if not self.playerLogin then
+				AceConsole:RegisterEvent("PLAYER_LOGIN", "PLAYER_LOGIN", true)
+			end
+		end
+	end
+
+	AceConsole.registry[name] = options
+
+	if slashCommands == tmp then
+		for i in ipairs(tmp) do
+			tmp[i] = nil
+		end
+	end
+end
+
+function AceConsole:InjectAceOptionsTable(handler, options)
+	self:argCheck(handler, 2, "table")
+	self:argCheck(options, 3, "table")
+	if options.type:lower() ~= "group" then
+		self:error('Cannot inject into options table argument #3 if its type is not "group"')
+	end
+	if options.handler ~= nil and options.handler ~= handler then
+		self:error("Cannot inject into options table argument #3 if it has a different handler than argument #2")
+	end
+	options.handler = handler
+	local class = handler.class
+	if not AceLibrary:HasInstance("AceOO-2.0") or not class then
+		if Rock then
+			-- possible Rock object
+			for mixin in Rock:IterateObjectMixins(handler) do
+				if type(mixin.GetAceOptionsDataTable) == "function" then
+					local t = mixin:GetAceOptionsDataTable(handler)
+					for k,v in pairs(t) do
+						if type(options.args) ~= "table" then
+							options.args = {}
+						end
+						if options.args[k] == nil then
+							options.args[k] = v
+						end
+					end
+				end
+			end
+		end
+	else
+		-- Ace2 object
+		while class and class ~= AceLibrary("AceOO-2.0").Class do
+			if type(class.GetAceOptionsDataTable) == "function" then
+				local t = class:GetAceOptionsDataTable(handler)
+				for k,v in pairs(t) do
+					if type(options.args) ~= "table" then
+						options.args = {}
+					end
+					if options.args[k] == nil then
+						options.args[k] = v
+					end
+				end
+			end
+			local mixins = class.mixins
+			if mixins then
+				for mixin in pairs(mixins) do
+					if type(mixin.GetAceOptionsDataTable) == "function" then
+						local t = mixin:GetAceOptionsDataTable(handler)
+						for k,v in pairs(t) do
+							if type(options.args) ~= "table" then
+								options.args = {}
+							end
+							if options.args[k] == nil then
+								options.args[k] = v
+							end
+						end
+					end
+				end
+			end
+			class = class.super
+		end
+	end
+	
+	return options
+end
+
+function AceConsole:PLAYER_LOGIN()
+	self.playerLogin = true
+	for addon, options in pairs(self.nextAddon) do
+		local err, position = validateOptionsMethods(addon, options)
+		if err then
+			if position then
+				geterrorhandler()(tostring(addon) .. ": AceConsole: " .. position .. ": " .. err)
+			else
+				geterrorhandler()(tostring(addon) .. ": AceConsole: " .. err)
+			end
+		end
+		self.nextAddon[addon] = nil
+	end
+end
+
+function AceConsole:TabCompleteInfo(cmdpath)
+	local cmd =  cmdpath:match("(/%S+)")
+	if not cmd then
+		return
+	end
+	local path = cmdpath:sub(cmd:len() + 2)
+	for name in pairs(SlashCmdList) do --global
+		if AceConsole.registry[name] then
+			local i = 0
+			while true do
+				i = i + 1
+				local scmd = _G["SLASH_"..name..i]
+				if not scmd then break end
+				if cmd == scmd then
+					return name, cmd, path
+				end
+			end
+		end
+	end
+end
+
+function external(self, major, instance)
+	if major == "AceEvent-2.0" then
+		if not AceEvent then
+			AceEvent = instance
+
+			AceEvent:embed(self)
+		end
+	elseif major == "AceTab-2.0" then
+		instance:RegisterTabCompletion("AceConsole", "%/.*", function(t, cmdpath, pos)
+			local name, cmd, path = self:TabCompleteInfo(cmdpath:sub(1, pos))
+
+			if not self.registry[name] then
+				return false
+			else
+				local validArgs, _, _, handler = findTableLevel(self, self.registry[name], cmd, path or "")
+				if validArgs.args then
+					for arg, v in pairs(validArgs.args) do
+						local hidden = v.hidden
+						local handler = v.handler or handler
+						if hidden then
+							if type(hidden) == "function" then
+								hidden = hidden(v.passValue)
+							elseif type(hidden) == "string" then
+								local f = hidden
+								local neg = f:match("^~(.-)$")
+								if neg then
+									f = neg
+								end
+								if type(handler[f]) ~= "function" then
+									self:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(f)))
+								end
+								hidden = handler[f](handler, v.passValue)
+								if neg then
+									hidden = not hidden
+								end
+							end
+						end
+						local disabled = hidden or v.disabled
+						if disabled then
+							if type(disabled) == "function" then
+								disabled = disabled(v.passValue)
+							elseif type(disabled) == "string" then
+								local f = disabled
+								local neg = f:match("^~(.-)$")
+								if neg then
+									f = neg
+								end
+								if type(handler[f]) ~= "function" then
+									self:error("%s: %s", handler, OPTION_HANDLER_NOT_FOUND:format(tostring(f)))
+								end
+								disabled = handler[f](handler, v.passValue)
+								if neg then
+									disabled = not disabled
+								end
+							end
+						end
+						if not hidden and not disabled and v.type ~= "header" then
+							table.insert(t, (tostring(arg):gsub("%s", "-")))
+						end
+					end
+				end
+			end
+		end, function(u, matches, gcs, cmdpath)
+			local name, cmd, path = self:TabCompleteInfo(cmdpath)
+			if self.registry[name] then
+				local validArgs, path2, argwork, handler = findTableLevel(self, self.registry[name], cmd, path)
+				printUsage(self, validArgs.handler or handler, self.registry[name], validArgs, path2, argwork, argwork[#argwork], not gcs or gcs ~= "", gcs)
+			end
+		end)
+	elseif major == "Dewdrop-2.0" then
+		Dewdrop = instance
+	end
+end
+
+local function activate(self, oldLib, oldDeactivate)
+	AceConsole = self
+
+	if oldLib then
+		self.registry = oldLib.registry
+		self.nextAddon = oldLib.nextAddon
+	end
+
+	if not self.registry then
+		self.registry = {}
+	else
+		for name,options in pairs(self.registry) do
+			self:RegisterChatCommand(false, options, name)
+		end
+	end
+
+	self:RegisterChatCommand("/reload", "/rl", "/reloadui", ReloadUI, "RELOAD")
+	self:RegisterChatCommand("/gm", ToggleHelpFrame, "GM")
+	local t = { "/print", "/echo" }
+	local _,_,_,enabled,loadable = GetAddOnInfo("DevTools")
+	if not enabled and not loadable then
+		table.insert(t, "/dump")
+	end
+	self:RegisterChatCommand(t, function(text)
+		text = text:trim():match("^(.-);*$")
+		local f, err = loadstring("AceLibrary('AceConsole-2.0'):PrintLiteral(" .. text .. ")")
+		if not f then
+			self:Print("|cffff0000Error:|r", err)
+		else
+			f()
+		end
+	end, "PRINT")
+
+	self:activate(oldLib, oldDeactivate)
+
+	if oldDeactivate then
+		oldDeactivate(oldLib)
+	end
+end
+
+AceLibrary:Register(AceConsole, MAJOR_VERSION, MINOR_VERSION, activate, nil, external)