diff libs/AceConsole-2.0/AceConsole-2.0.lua @ 1:c11ca1d8ed91

Version 0.1
author Flick <flickerstreak@gmail.com>
date Tue, 20 Mar 2007 21:03:57 +0000
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libs/AceConsole-2.0/AceConsole-2.0.lua	Tue Mar 20 21:03:57 2007 +0000
@@ -0,0 +1,2058 @@
+--[[
+Name: AceConsole-2.0
+Revision: $Rev: 19865 $
+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/root/trunk/Ace2/AceConsole-2.0
+Description: Mixin to allow for input/output capabilities. This uses the
+             AceOptions data table format to determine input.
+             http://wiki.wowace.com/index.php/AceOptions_data_table
+Dependencies: AceLibrary, AceOO-2.0
+]]
+
+local MAJOR_VERSION = "AceConsole-2.0"
+local MINOR_VERSION = "$Revision: 19865 $"
+
+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
+
+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
+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
+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
+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>" -- fix
+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
+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%s|r] 是一個不符合規定的選項,對 |cffffff7f%s|r"
+	IS_NOT_A_VALID_VALUE_FOR = "[|cffffff7f%s|r] 是一個不符合規定的數值,對 |cffffff7f%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-KEY>" -- 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>"
+end
+
+local NONE = NONE or "None"
+
+local AceOO = AceLibrary("AceOO-2.0")
+local AceEvent
+
+local AceConsole = AceOO.Mixin { "Print", "PrintComma", "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
+		(frame or DEFAULT_CHAT_FRAME):AddMessage(text, r, g, b, nil, delay or 5)
+	else
+		(frame or DEFAULT_CHAT_FRAME):AddMessage("|cffffff78" .. tostring(name) .. ":|r " .. text, r, g, b, nil, delay or 5)
+	end
+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 string.format("<%s:%s>", t:GetObjectType(), t:GetName() or "(anon)")
+		end
+	end
+	return real_tostring(t)
+end
+
+local function _tostring(...)
+	if select('#', ...) < 1 then
+		return
+	end
+	return tostring((...)), _tostring(select(2, ...))
+end
+function AceConsole:CustomPrint(r, g, b, frame, delay, connector, a1, ...)
+	if tostring(a1):find("%%") and select('#', ...) >= 1 then
+		local success, text = pcall(string.format, _tostring(a1, ...))
+		if success then
+			print(text, self, r, g, b, frame or self.printFrame, delay)
+			return
+		end
+	end
+	print((connector or " "):join(_tostring(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
+
+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
+	
+	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()
+			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, string.format(OPTION_HANDLER_NOT_FOUND, f))
+				end
+				hidden = handler[f](handler)
+				if neg then
+					hidden = not hidden
+				end
+			end
+		end
+		if hidden then
+			disabled = true
+		elseif disabled then
+			if type(disabled) == "function" then
+				disabled = disabled()
+			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, string.format(OPTION_HANDLER_NOT_FOUND, f))
+				end
+				disabled = handler[f](handler)
+				if neg then
+					disabled = not disabled
+				end
+			end
+		end
+		if not disabled then
+			local next = work[index] and work[index]:lower()
+			if next then
+				for k,v in pairs(options.args) do
+					local good = false
+					if k:lower() == next then
+						good = true
+					elseif type(v.aliases) == "table" then
+						for _,alias in ipairs(v.aliases) do
+							if alias:lower() == next then
+								good = true
+								break
+							end
+						end
+					elseif type(v.aliases) == "string" and v.aliases:lower() == next then
+						good = true
+					end
+					if good then
+						return findTableLevel(options.handler or self, v, chat, text, index + 1, options.pass and options or nil)
+					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, passTable and work[index - 1]
+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 string.format("%q is not a proper function", 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 string.format("%q is not a proper function", 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 string.format("%q is not a proper function", 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 string.format("%q is not a proper function", 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 string.format("%q is not a proper function", 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 string.format("%q is not a proper function", 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 string.format("%q is not a proper function", 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 string.format("%q is not a proper function", 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
+	else
+		if kind == "group" then
+			return 'cannot have "type" = "group" as a subgroup of a passing group', position
+		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
+		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
+	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) ~= "string" then
+				return '"args" keys must be strings', position
+			elseif k:find("%s") then
+				return string.format('"args" keys must not include spaces. %q is not appropriate.', k), position
+			elseif k:len() == 0 then
+				return '"args" keys must not be 0-length strings.', position
+			end
+			if type(v) ~= "table" then
+				return '"args" values must be tables', position and position .. "." .. k or k
+			end
+			local newposition
+			if position then
+				newposition = position .. ".args." .. k
+			else
+				newposition = "args." .. k
+			end
+			local err, pos = validateOptions(v, newposition, baseOptions, options.pass)
+			if err then
+				return err, pos
+			end
+		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: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 printUsage(self, handler, realOptions, options, path, args, 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()
+		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, string.format(OPTION_HANDLER_NOT_FOUND, f))
+			end
+			hidden = handler[f](handler)
+			if neg then
+				hidden = not hidden
+			end
+		end
+	end
+	if hidden then
+		disabled = true
+	elseif disabled then
+		if type(disabled) == "function" then
+			disabled = disabled()
+		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, string.format(OPTION_HANDLER_NOT_FOUND, f))
+			end
+			disabled = handler[f](handler)
+			if neg then
+				disabled = not disabled
+			end
+		end
+	end
+	local kind = (options.type or "group"):lower()
+	if disabled then
+		print(string.format(OPTION_IS_DISABLED, path), realOptions.cmdName or realOptions.name or self)
+	elseif kind == "text" then
+		local var
+		if passTable then
+			if not passTable.get then
+			elseif 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, string.format(OPTION_HANDLER_NOT_FOUND, passTable.get))
+				end
+				var = handler[passTable.get](handler, passValue)
+			end
+		else
+			if not options.get then
+			elseif type(options.get) == "function" then
+				var = options.get()
+			else
+				local handler = options.handler or handler
+				if type(handler[options.get]) ~= "function" then
+					AceConsole:error("%s: %s", handler, string.format(OPTION_HANDLER_NOT_FOUND, options.get))
+				end
+				var = handler[options.get](handler)
+			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
+				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
+				usage = "{" .. table.concat(order, " || ") .. "}"
+				for k in pairs(order) do
+					order[k] = nil
+				end
+			end
+			var = options.validate[var] or var
+		elseif options.validate == "keybinding" then
+			usage = KEYBINDING_USAGE
+		else
+			usage = options.usage or "<value>"
+		end
+		if not quiet then
+			print(string.format("|cffffff7f%s:|r %s %s", USAGE, path, usage), realOptions.cmdName or realOptions.name or self)
+		end
+		if (passTable and passTable.get) or options.get then
+			print(string.format(options.current or IS_CURRENTLY_SET_TO, 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, string.format(OPTION_HANDLER_NOT_FOUND, passTable.get))
+				end
+				var = handler[passTable.get](handler, passValue)
+			end
+		else
+			if type(options.get) == "function" then
+				var = options.get()
+			else
+				local handler = options.handler or handler
+				if type(handler[options.get]) ~= "function" then
+					AceConsole:error("%s: %s", handler, string.format(OPTION_HANDLER_NOT_FOUND, options.get))
+				end
+				var = handler[options.get](handler)
+			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 = string.format("(%s%s%s)", min, bit, max)
+		if not quiet then
+			print(string.format("|cffffff7f%s:|r %s %s", USAGE, path, usage), realOptions.cmdName or realOptions.name or self)
+		end
+		print(string.format(options.current or IS_CURRENTLY_SET_TO, 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, string.format(OPTION_HANDLER_NOT_FOUND, 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) < tostring(b)
+					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) < tostring(b)
+						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(string.format("|cffffff7f%s:|r %s %s", 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(string.format("|cffffff7f%s:|r %s %s", USAGE, path, "{" .. table.concat(order, " || ") .. "}"))
+					else
+						print(string.format("|cffffff7f%s:|r %s %s", USAGE, path, "{" .. table.concat(order, " || ") .. "}"), realOptions.cmdName or realOptions.name or self)
+					end
+				else
+					if options.desc then
+						print(string.format("|cffffff7f%s:|r %s %s", USAGE, path, "{" .. table.concat(order, " || ") .. "}"), realOptions.cmdName or realOptions.name or self)
+						print(tostring(options.desc))
+					else
+						print(string.format("|cffffff7f%s:|r %s %s", USAGE, path, "{" .. table.concat(order, " || ") .. "}"), realOptions.cmdName or realOptions.name or self)
+					end
+				end
+			end
+			for _,k in ipairs(order) do
+				local v = options.args[k]
+				if v then
+					local disabled = v.disabled
+					if disabled then
+						if type(disabled) == "function" then
+							disabled = disabled()
+						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, string.format(OPTION_HANDLER_NOT_FOUND, f))
+							end
+							disabled = handler[f](handler)
+							if neg then
+								disabled = not disabled
+							end
+						end
+					end
+					if type(v.aliases) == "table" then
+						k = k .. " || " .. table.concat(v.aliases, " || ")
+					elseif type(v.aliases) == "string" then
+						k = k .. " || " .. v.aliases
+					end
+					if v.get then
+						local a1,a2,a3,a4
+						if type(v.get) == "function" then
+							if options.pass then
+								a1,a2,a3,a4 = v.get(k)
+							else
+								a1,a2,a3,a4 = v.get()
+							end
+						else
+							local handler = v.handler or handler
+							local f = v.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, string.format(OPTION_HANDLER_NOT_FOUND, f))
+							end
+							if options.pass then
+								a1,a2,a3,a4 = handler[f](handler, k)
+							else
+								a1,a2,a3,a4 = handler[f](handler)
+							end
+							if neg then
+								a1 = not a1
+							end
+						end
+						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 = string.format("|c%02x%02x%02x%02x%02x%02x%02x%02x|r", 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 = string.format("|cff%02x%02x%02x%02x%02x%02x|r", 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
+							s = tostring(v.validate[a1] or a1 or NONE)
+						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(string.format("|cffcfcfcf - %s: [%s] %s|r", k, s, desc))
+						else
+							print(string.format(" - |cffffff7f%s: [|r%s|cffffff7f]|r %s", 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(string.format("|cffcfcfcf - %s: %s", k, desc))
+						else
+							print(string.format(" - |cffffff7f%s:|r %s", k, v.desc or NONE))
+						end
+					end
+				end
+			end
+			for k in pairs(order) do
+				order[k] = nil
+			end
+		else
+			if options.desc then
+				desc = options.desc
+				print(string.format("|cffffff7f%s:|r %s", 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(string.format("|cffffff7f%s:|r %s", USAGE, path))
+			else
+				print(string.format("|cffffff7f%s:|r %s", USAGE, path), realOptions.cmdName or realOptions.name or self)
+			end
+			print(NO_OPTIONS_AVAILABLE)
+		end
+	end
+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)
+	
+	local hidden, disabled = options.cmdHidden or options.hidden, options.disabled
+	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, string.format(OPTION_HANDLER_NOT_FOUND, f))
+			end
+			hidden = handler[f](handler)
+			if neg then
+				hidden = not hidden
+			end
+		end
+	end
+	if hidden then
+		disabled = true
+	elseif disabled then
+		if type(disabled) == "function" then
+			disabled = disabled()
+		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, string.format(OPTION_HANDLER_NOT_FOUND, f))
+			end
+			disabled = handler[f](handler)
+			if neg then
+				disabled = not disabled
+			end
+		end
+	end
+	local _G_this = this
+	local kind = (options.type or "group"):lower()
+	if disabled then
+		print(string.format(OPTION_IS_DISABLED, 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, string.format(OPTION_HANDLER_NOT_FOUND, 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(string.format(options.error or IS_NOT_A_VALID_OPTION_FOR, tostring(table.concat(args, " ")), path), realOptions.cmdName or realOptions.name or self)
+					print(string.format("|cffffff7f%s:|r %s %s", USAGE, path, usage))
+					return
+				end
+			end
+			
+			local var
+			if passTable then
+				if not passTable.get then
+				elseif type(passTable.get) == "function" then
+					var = passTable.get(passValue)
+				else
+					if type(handler[passTable.get]) ~= "function" then
+						AceConsole:error("%s: %s", handler, string.format(OPTION_HANDLER_NOT_FOUND, passTable.get))
+					end
+					var = handler[passTable.get](handler, passValue)
+				end
+			else
+				if not options.get then
+				elseif type(options.get) == "function" then
+					var = options.get()
+				else
+					if type(handler[options.get]) ~= "function" then
+						AceConsole:error("%s: %s", handler, string.format(OPTION_HANDLER_NOT_FOUND, options.get))
+					end
+					var = handler[options.get](handler)
+				end
+			end
+			
+			if var ~= args[1] then
+				if passTable then
+					if type(passTable.set) == "function" then
+						passTable.set(passValue, unpack(args))
+					else
+						if type(handler[passTable.set]) ~= "function" then
+							AceConsole:error("%s: %s", handler, string.format(OPTION_HANDLER_NOT_FOUND, passTable.set))
+						end
+						handler[passTable.set](handler, passTable.set, unpack(args))
+					end
+				else
+					if type(options.set) == "function" then
+						options.set(unpack(args))
+					else
+						if type(handler[options.set]) ~= "function" then
+							AceConsole:error("%s: %s", handler, string.format(OPTION_HANDLER_NOT_FOUND, options.set))
+						end
+						handler[options.set](handler, unpack(args))
+					end
+				end
+			end
+		end
+		
+		if #args > 0 then
+			local var
+			if passTable then
+				if not passTable.get then
+				elseif type(passTable.get) == "function" then
+					var = passTable.get(passValue)
+				else
+					if type(handler[passTable.get]) ~= "function" then
+						AceConsole:error("%s: %s", handler, string.format(OPTION_HANDLER_NOT_FOUND, passTable.get))
+					end
+					var = handler[passTable.get](handler, passValue)
+				end
+			else
+				if not options.get then
+				elseif type(options.get) == "function" then
+					var = options.get()
+				else
+					if type(handler[options.get]) ~= "function" then
+						AceConsole:error("%s: %s", handler, string.format(OPTION_HANDLER_NOT_FOUND, options.get))
+					end
+					var = handler[options.get](handler)
+				end
+			end
+			if type(options.validate) == "table" then
+				var = options.validate[var] or var
+			end
+			if (passTable and passTable.get) or options.get then
+				print(string.format(options.message or IS_NOW_SET_TO, 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)
+			return
+		end
+	elseif kind == "execute" then
+		if passTable then
+			if type(passFunc) == "function" then
+				set(passValue)
+			else
+				if type(handler[passFunc]) ~= "function" then
+					AceConsole:error("%s: %s", handler, string.format(OPTION_HANDLER_NOT_FOUND, passFunc))
+				end
+				handler[passFunc](handler, passValue)
+			end
+		else
+			local ret, msg
+			if type(options.func) == "function" then
+				options.func()
+			else
+				local handler = options.handler or self
+				if type(handler[options.func]) ~= "function" then
+					AceConsole:error("%s: %s", handler, string.format(OPTION_HANDLER_NOT_FOUND, options.func))
+				end
+				handler[options.func](handler)
+			end
+		end
+	elseif kind == "toggle" then
+		local var
+		if passTable then
+			if type(passTable.get) == "function" then
+				var = passTable.get(passValue)
+			else
+				local f = passTable.get
+				local neg = f:match("^~(.-)$")
+				if neg then
+					f = neg
+				end
+				if type(handler[f]) ~= "function" then
+					AceConsole:error("%s: %s", handler, string.format(OPTION_HANDLER_NOT_FOUND, f))
+				end
+				var = handler[f](handler, passValue)
+				if neg then
+					var = not var
+				end
+			end
+			if type(passTable.set) == "function" then
+				passTable.set(passValue, not var)
+			else
+				if type(handler[passTable.set]) ~= "function" then
+					AceConsole:error("%s: %s", handler, string.format(OPTION_HANDLER_NOT_FOUND, passTable.set))
+				end
+				handler[passTable.set](handler, passValue, not var)
+			end
+			if type(passTable.get) == "function" then
+				var = passTable.get(passValue)
+			else
+				local f = passTable.get
+				local neg = f:match("^~(.-)$")
+				if neg then
+					f = neg
+				end
+				var = handler[f](handler, passValue)
+				if neg then
+					var = not var
+				end
+			end
+		else
+			local handler = options.handler or self
+			if type(options.get) == "function" then
+				var = options.get()
+			else
+				local f = options.get
+				local neg = f:match("^~(.-)$")
+				if neg then
+					f = neg
+				end
+				if type(handler[f]) ~= "function" then
+					AceConsole:error("%s: %s", handler, string.format(OPTION_HANDLER_NOT_FOUND, f))
+				end
+				var = handler[f](handler)
+				if neg then
+					var = not var
+				end
+			end
+			if type(options.set) == "function" then
+				options.set(not var)
+			else
+				if type(handler[options.set]) ~= "function" then
+					AceConsole:error("%s: %s", handler, string.format(OPTION_HANDLER_NOT_FOUND, options.set))
+				end
+				handler[options.set](handler, not var)
+			end
+			if type(options.get) == "function" then
+				var = options.get()
+			else
+				local f = options.get
+				local neg = f:match("^~(.-)$")
+				if neg then
+					f = neg
+				end
+				var = handler[f](handler)
+				if neg then
+					var = not var
+				end
+			end
+		end
+		
+		print(string.format(options.message or IS_NOW_SET_TO, 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 = string.format("(%s%s%s)", min, bit, max)
+				print(string.format(options.error or IS_NOT_A_VALID_VALUE_FOR, tostring(arg), path), realOptions.cmdName or realOptions.name or self)
+				print(string.format("|cffffff7f%s:|r %s %s", USAGE, path, usage))
+				return
+			end
+			
+			local var
+			if passTable then
+				if type(passTable.get) == "function" then
+					var = passTable.get(passValue)
+				else
+					if type(handler[passTable.get]) ~= "function" then
+						AceConsole:error("%s: %s", handler, string.format(OPTION_HANDLER_NOT_FOUND, passTable.get))
+					end
+					var = handler[passTable.get](handler, passValue)
+				end
+			else
+				if type(options.get) == "function" then
+					var = options.get()
+				else
+					local handler = options.handler or self
+					if type(handler[options.get]) ~= "function" then
+						AceConsole:error("%s: %s", handler, string.format(OPTION_HANDLER_NOT_FOUND, options.get))
+					end
+					var = handler[options.get](handler)
+				end
+			end
+			
+			if var ~= arg then
+				if passTable then
+					if type(passTable.set) == "function" then
+						passTable.set(passValue, arg)
+					else
+						if type(handler[passTable.set]) ~= "function" then
+							AceConsole:error("%s: %s", handler, string.format(OPTION_HANDLER_NOT_FOUND, passTable.set))
+						end
+						handler[passTable.set](handler, passValue, arg)
+					end
+				else
+					if type(options.set) == "function" then
+						options.set(arg)
+					else
+						local handler = options.handler or self
+						if type(handler[options.set]) ~= "function" then
+							AceConsole:error("%s: %s", handler, string.format(OPTION_HANDLER_NOT_FOUND, options.set))
+						end
+						handler[options.set](handler, arg)
+					end
+				end
+			end
+		end
+		
+		if arg then
+			local var
+			if passTable then
+				if type(passTable.get) == "function" then
+					var = passTable.get(passValue)
+				else
+					if type(handler[passTable.get]) ~= "function" then
+						AceConsole:error("%s: %s", handler, string.format(OPTION_HANDLER_NOT_FOUND, passTable.get))
+					end
+					var = handler[passTable.get](handler, passValue)
+				end
+			else
+				if type(options.get) == "function" then
+					var = options.get()
+				else
+					local handler = options.handler or self
+					if type(handler[options.get]) ~= "function" then
+						AceConsole:error("%s: %s", handler, string.format(OPTION_HANDLER_NOT_FOUND, options.get))
+					end
+					var = handler[options.get](handler)
+				end
+			end
+			
+			if var and options.isPercent then
+				var = tostring(var * 100) .. "%"
+			end
+			print(string.format(options.message or IS_NOW_SET_TO, 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)
+			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(string.format(options.error or IS_NOT_A_VALID_OPTION_FOR, table.concat(args, ' '), path), realOptions.cmdName or realOptions.name or self)
+				print(string.format("|cffffff7f%s:|r %s {0-1} {0-1} {0-1}%s", USAGE, path, options.hasAlpha and " {0-1}" or ""))
+				return
+			end
+			if passTable then
+				if type(passTable.set) == "function" then
+					passTable.set(passValue, r,g,b,a)
+				else
+					if type(handler[passTable.set]) ~= "function" then
+						AceConsole:error("%s: %s", handler, string.format(OPTION_HANDLER_NOT_FOUND, passTable.set))
+					end
+					handler[passTable.set](handler, passValue, r,g,b,a)
+				end
+			else
+				if type(options.set) == "function" then
+					options.set(r,g,b,a)
+				else
+					if type(handler[options.set]) ~= "function" then
+						AceConsole:error("%s: %s", handler, string.format(OPTION_HANDLER_NOT_FOUND, options.set))
+					end
+					handler[options.set](handler, r,g,b,a)
+				end
+			end
+			
+			local r,g,b,a
+			if passTable then
+				if type(passTable.get) == "function" then
+					r,g,b,a = passTable.get(passValue)
+				else
+					if type(handler[passTable.get]) ~= "function" then
+						AceConsole:error("%s: %s", handler, string.format(OPTION_HANDLER_NOT_FOUND, passTable.get))
+					end
+					r,g,b,a = handler[passTable.get](handler, passValue)
+				end
+			else
+				if type(options.get) == "function" then
+					r,g,b,a = options.get()
+				else
+					if type(handler[options.get]) ~= "function" then
+						AceConsole:error("%s: %s", handler, string.format(OPTION_HANDLER_NOT_FOUND, options.get))
+					end
+					r,g,b,a = handler[options.get](handler)
+				end
+			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 = string.format("|c%02x%02x%02x%02x%02x%02x%02x%02x|r", a*255, r*255, g*255, b*255, r*255, g*255, b*255, a*255)
+				else
+					s = string.format("|cff%02x%02x%02x%02x%02x%02x|r", r*255, g*255, b*255, r*255, g*255, b*255)
+				end
+			else
+				s = NONE
+			end
+			print(string.format(options.message or IS_NOW_SET_TO, tostring(options.cmdName or options.name), s), realOptions.cmdName or realOptions.name or self)
+		else
+			local r,g,b,a
+			if passTable then
+				if type(passTable.get) == "function" then
+					r,g,b,a = passTable.get(passValue)
+				else
+					if type(handler[passTable.get]) ~= "function" then
+						AceConsole:error("%s: %s", handler, string.format(OPTION_HANDLER_NOT_FOUND, passTable.get))
+					end
+					r,g,b,a = handler[passTable.get](handler, passValue)
+				end
+			else
+				if type(options.get) == "function" then
+					r,g,b,a = options.get()
+				else
+					if type(handler[options.get]) ~= "function" then
+						AceConsole:error("%s: %s", handler, string.format(OPTION_HANDLER_NOT_FOUND, options.get))
+					end
+					r,g,b,a = handler[options.get](handler)
+				end
+			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, string.format(OPTION_HANDLER_NOT_FOUND, 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, string.format(OPTION_HANDLER_NOT_FOUND, 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 = string.format("|c%02x%02x%02x%02x%02x%02x%02x%02x|r", a*255, r*255, g*255, b*255, r*255, g*255, b*255, a*255)
+									else
+										s = string.format("|cff%02x%02x%02x%02x%02x%02x|r", r*255, g*255, b*255, r*255, g*255, b*255)
+									end
+								else
+									s = NONE
+								end
+								print(string.format(t.message, 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 = passTable and passTable.set or options.set
+			t.get = passTable and passTable.get or options.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, string.format(OPTION_HANDLER_NOT_FOUND, 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, string.format(OPTION_HANDLER_NOT_FOUND, 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, string.format(OPTION_HANDLER_NOT_FOUND, 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, string.format(OPTION_HANDLER_NOT_FOUND, 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)
+		else
+			-- invalid argument
+			print(string.format(options.error or IS_NOT_A_VALID_OPTION_FOR, args[1], path), realOptions.cmdName or realOptions.name or self)
+		end
+		return
+	end
+	this = _G_this
+	if Dewdrop then
+		Dewdrop:Refresh(1)
+		Dewdrop:Refresh(2)
+		Dewdrop:Refresh(3)
+		Dewdrop:Refresh(4)
+		Dewdrop:Refresh(5)
+	end
+end
+
+local external
+function AceConsole:RegisterChatCommand(slashCommands, options, name)
+	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 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
+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 class then
+		self:error("Cannot retrieve AceOptions tables from a non-object argument #2")
+	end
+	while class and class ~= AceOO.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
+	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 ac = AceLibrary("AceConsole-2.0")
+			local name, cmd, path = ac:TabCompleteInfo(cmdpath:sub(1, pos))
+
+			if not ac.registry[name] then
+				return false
+			else
+				local validArgs = findTableLevel(ac, ac.registry[name], cmd, path or "")
+				if validArgs.args then
+					for arg in pairs(validArgs.args) do
+						table.insert(t, arg)
+					end
+				end
+			end
+		end, function(u, matches, gcs, cmdpath)
+			local ac = AceLibrary("AceConsole-2.0")
+			local name, cmd, path = ac:TabCompleteInfo(cmdpath)
+			if ac.registry[name] then
+				local validArgs, path2, argwork = findTableLevel(ac, ac.registry[name], cmd, path)
+				printUsage(ac, validArgs.handler, ac.registry[name], validArgs, path2, 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({ "/print" }, function(text)
+		local f, err = loadstring("AceLibrary('AceConsole-2.0'):PrintComma(" .. 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)