code:
--[[
Name: AceHook-2.1
Revision: $Rev: 15814 $
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/AceHook-2.1
SVN: http://svn.wowace.com/root/trunk/Ace2/AceHook-2.1
Description: Mixin to allow for safe hooking of functions, methods, and scripts.
Dependencies: AceLibrary, AceOO-2.0
]]

local MAJOR_VERSION = "AceHook-2.1"
local MINOR_VERSION = "$Revision: 15814 $"

-- This ensures the code is only executed if the libary doesn't already exist, or is a newer version
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

--[[---------------------------------------------------------------------------------
  Create the library object
----------------------------------------------------------------------------------]]

local AceOO = AceLibrary:GetInstance("AceOO-2.0")
local AceHook = AceOO.Mixin {
								"Hook",
								"HookScript",
								"SecureHook",
								"Unhook",
								"UnhookAll",
								"HookReport",
								"IsHooked",
							}

--[[---------------------------------------------------------------------------------
  Library Definitions
----------------------------------------------------------------------------------]]

local protFuncs = {
	CameraOrSelectOrMoveStart = true, 	CameraOrSelectOrMoveStop = true,
	TurnOrActionStart = true,			TurnOrActionStop = true,
	PitchUpStart = true,				PitchUpStop = true,
	PitchDownStart = true,				PitchDownStop = true,
	MoveBackwardStart = true,			MoveBackwardStop = true,
	MoveForwardStart = true,			MoveForwardStop = true,
	Jump = true,						StrafeLeftStart = true,
	StrafeLeftStop = true,				StrafeRightStart = true,
	StrafeRightStop = true,				ToggleMouseMove = true,
	ToggleRun = true,					TurnLeftStart = true,
	TurnLeftStop = true,				TurnRightStart = true,
	TurnRightStop = true,
}

local function issecurevariable(x)
	if protFuncs[x] then
		return 1
	else
		return nil
	end
end

local _G = getfenv(0)

local function hooksecurefunc(arg1, arg2, arg3)
	if type(arg1) == "string" then
		arg1, arg2, arg3 = _G, arg1, arg2
	end
	local orig = arg1[arg2]
	arg1[arg2] = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
		local x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15,x16,x17,x18,x19,x20 = orig(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)

		arg3(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)

		return x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15,x16,x17,x18,x19,x20
	end
end

local protectedScripts = {
	OnClick = true,
}

local handlers, scripts, actives, registry

--[[---------------------------------------------------------------------------------
  Private definitions (Not exposed)
----------------------------------------------------------------------------------]]

local new, del
do
	local list = setmetatable({}, {__mode = "k"})
	function new()
		local t = next(list)
		if not t then
			return {}
		end
		list[t] = nil
		return t
	end

	function del(t)
		setmetatable(t, nil)
		for k in pairs(t) do
			t[k] = nil
		end
		list[t] = true
	end
end

local function createFunctionHook(self, func, handler, orig, secure)
	if not secure then
		if type(handler) == "string" then
			-- The handler is a method, need to self it
			local uid
			uid = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
				if actives[uid] then
					return self[handler](self, a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
				else
					return orig(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
				end
			end
			return uid
		else
			-- The handler is a function, just call it
			local uid
			uid = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
				if actives[uid] then
					return handler(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
				else
					return orig(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
				end
			end
			return uid
		end
	else
		-- secure hooks don't call the original method
		if type(handler) == "string" then
			-- The handler is a method, need to self it
			local uid
			uid = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
				if actives[uid] then
					return self[handler](self, a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
				end
			end
			return uid
		else
			-- The handler is a function, just call it
			local uid
			uid = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
				if actives[uid] then
					return handler(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
				end
			end
			return uid
		end
	end
end

local function createMethodHook(self, object, method, handler, orig, secure, script)
	if script then
		if type(handler) == "string" then
			local uid
			uid = function()
				if actives[uid] then
					return self[handler](self, object)
				else
					return orig()
				end
			end
			return uid
		else
			-- The handler is a function, just call it
			local uid
			uid = function()
				if actives[uid] then
					return handler(object)
				else
					return orig()
				end
			end
			return uid
		end
	elseif not secure then
		if type(handler) == "string" then
			local uid
			uid = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
				if actives[uid] then
					return self[handler](self, a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
				else
					return orig(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
				end
			end
			return uid
		else
			-- The handler is a function, just call it
			local uid
			uid = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
				if actives[uid] then
					return handler(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
				else
					return orig(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
				end
			end
			return uid
		end
	else
		-- secure hooks don't call the original method
		if type(handler) == "string" then
			local uid
			uid = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
				if actives[uid] then
					return self[handler](self, a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
				end
			end
			return uid
		else
			-- The handler is a function, just call it
			local uid
			uid = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
				if actives[uid] then
					return handler(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
				end
			end
			return uid
		end
	end
end

local function hookFunction(self, func, handler, secure)
	local orig = _G[func]

	if not orig or type(orig) ~= "function" then
		AceHook:error("Attempt to hook a non-existant function %q", func)
	end

	if not handler then
		handler = func
	end

	if registry[self][func] then
		local uid = registry[self][func]

		if actives[uid] then
			-- We have an active hook from this source.  Don't multi-hook
			AceHook:error("%q already has an active hook from this source.", func)
		end

		if handlers[uid] == handler then
			-- The hook is inactive, so reactivate it
			actives[uid] = true
			return
		else
			AceHook:error("There is a stale hook for %q can't hook or reactivate.", func)
		end
	end

	if type(handler) == "string" then
		if type(self[handler]) ~= "function" then
			AceHook:error("Could not find the the handler %q when hooking function %q", handler, func)
		end
	elseif type(handler) ~= "function" then
		AceHook:error("Could not find the handler you supplied when hooking %q", func)
	end

	local uid = createFunctionHook(self, func, handler, orig, secure)
	registry[self][func] = uid
	actives[uid] = true
	handlers[uid] = handler

	if not secure then
		_G[func] = uid
		self.hooks[func] = orig
	else
		hooksecurefunc(func, uid)
	end
end

local function unhookFunction(self, func)
	if not registry[self][func] then
		AceHook:error("Tried to unhook %q which is not currently hooked.", func)
	end

	local uid = registry[self][func]

	if actives[uid] then
		-- See if we own the global function
		if self.hooks[func] and _G[func] == uid then
			_G[func] = self.hooks[func]
			self.hooks[func] = nil
			registry[self][func] = nil
			handlers[uid] = nil
			scripts[uid] = nil
			actives[uid] = nil
			-- Magically all-done
		else
			actives[uid] = nil
		end
	end
end

local function hookMethod(self, obj, method, handler, script, secure)
	if not handler then
		handler = method
	end

	if not obj or type(obj) ~= "table" then
		AceHook:error("The object you supplied could not be found, or isn't a table.")
	end

	local uid = registry[self][obj] and registry[self][obj][method]
	if uid then
		if actives[uid] then
			-- We have an active hook from this source.  Don't multi-hook
			AceHook:error("%q already has an active hook from this source.", method)
		end

		if handlers[uid] == handler then
			-- The hook is inactive, reactivate it.
			actives[uid] = true
			return
		else
			AceHook:error("There is a stale hook for %q can't hook or reactivate.", method)
		end
	end

	if type(handler) == "string" then
		if type(self[handler]) ~= "function" then
			AceHook:error("Could not find the handler %q you supplied when hooking method %q", handler, method)
		end
	elseif type(handler) ~= "function" then
		AceHook:error("Could not find the handler you supplied when hooking method %q", method)
	end

	local orig
	if script then
		if not obj.GetScript then
			AceHook:error("The object you supplied does not have a GetScript method.")
		end
		if not obj:HasScript(method) then
			AceHook:error("The object you supplied doesn't allow the %q method.", method)
		end

		orig = obj:GetScript(method)
		if type(orig) ~= "function" then
			-- Sometimes there is not a original function for a script.
			orig = function() end
		end
	else
		orig = obj[method]
	end
	if not orig then
		AceHook:error("Could not find the method or script %q you are trying to hook.", method)
	end

	if not registry[self][obj] then
		registry[self][obj] = new()
	end

	if not self.hooks[obj] then
		self.hooks[obj] = new()
	end

	local uid = createMethodHook(self, obj, method, handler, orig, secure, script)
	registry[self][obj][method] = uid
	actives[uid] = true
	handlers[uid] = handler
	scripts[uid] = script and true or nil

	if script then
		obj:SetScript(method, uid)
		self.hooks[obj][method] = orig
	elseif not secure then
		obj[method] = uid
		self.hooks[obj][method] = orig
	else
		hooksecurefunc(obj, method, uid)
	end
end

local function unhookMethod(self, obj, method)
	if not registry[self][obj] or not registry[self][obj][method] then
		AceHook:error("Attempt to unhook a method %q that is not currently hooked.", method)
		return
	end

	local uid = registry[self][obj][method]

	if actives[uid] then
		if scripts[uid] then -- If this is a script
			if obj:GetScript(method) == uid then
				-- We own the script.  Revert to normal.
				obj:SetScript(method, self.hooks[obj][method])
				self.hooks[obj][method] = nil
				registry[self][obj][method] = nil
				handlers[uid] = nil
				scripts[uid] = nil
				actives[uid] = nil
			else
				actives[uid] = nil
			end
		else
			if self.hooks[obj][method] and obj[method] == uid then
				-- We own the method.  Revert to normal.
				obj[method] = self.hooks[obj][method]
				self.hooks[obj][method] = nil
				registry[self][obj][method] = nil
				handlers[uid] = nil
				actives[uid] = nil
			else
				actives[uid] = nil
			end
		end
	end
	if not next(self.hooks[obj]) then
		self.hooks[obj] = del(self.hooks[obj])
	end
	if not next(registry[self][obj]) then
		registry[self][obj] = del(registry[self][obj])
	end
end

-- ("function" [, handler] [, hookSecure]) or (object, "method" [, handler] [, hookSecure])
function AceHook:Hook(object, method, handler, hookSecure)
	if type(object) == "string" then
		method, handler, hookSecure = object, method, handler
		if handler == true then
			handler, hookSecure = nil, true
		end
		if not hookSecure and issecurevariable(method) then
			AceHook:error("Attempt to hook secure function %q. Use `SecureHook' or add `true' to the argument list to override.", method)
		end
		AceHook:argCheck(handler, 3, "function", "string", "nil")
		AceHook:argCheck(hookSecure, 4, "boolean", "nil")
		hookFunction(self, method, handler, false)
	else
		if handler == true then
			handler, hookSecure = nil, true
		end
		if not hookSecure and issecurevariable(object, method) then
			AceHook:error("Attempt to hook secure method %q. Use `SecureHook' or add `true' to the argument list to override.", method)
		end
		AceHook:argCheck(object, 2, "table")
		AceHook:argCheck(method, 3, "string")
		AceHook:argCheck(handler, 4, "function", "string", "nil")
		AceHook:argCheck(hookSecure, 5, "boolean", "nil")
		hookMethod(self, object, method, handler, false, false)
	end
end

-- ("function", handler) or (object, "method", handler)
function AceHook:SecureHook(object, method, handler)
	if type(object) == "string" then
		method, handler = object, method
		AceHook:argCheck(handler, 3, "function", "string", "nil")
		hookFunction(self, method, handler, true)
	else
		AceHook:argCheck(object, 2, "table")
		AceHook:argCheck(method, 3, "string")
		AceHook:argCheck(handler, 4, "function", "string", "nil")
		hookMethod(self, object, method, handler, false, true)
	end
end

function AceHook:HookScript(frame, script, handler)
	AceHook:argCheck(frame, 2, "table")
	if not frame[0] or type(frame.IsFrameType) ~= "function" then
		AceHook:error("Bad argument #2 to `HookScript'. Expected frame.")
	end
	AceHook:argCheck(script, 3, "string")
	AceHook:argCheck(handler, 4, "function", "string", "nil")
	hookMethod(self, frame, script, handler, true, false)
end

-- ("function") or (object, "method")
function AceHook:IsHooked(obj, method)
	if type(obj) == "string" then
		if registry[self][obj] and actives[registry[self][obj]] then
			return true, handlers[registry[self][obj]]
		end
	else
		AceHook:argCheck(obj, 2, "string", "table")
		AceHook:argCheck(method, 3, "string")
		if registry[self][obj] and registry[self][obj][method] and actives[registry[self][obj][method]] then
			return true, handlers[registry[self][obj][method]]
		end
	end

	return false, nil
end

-- ("function") or (object, "method")
function AceHook:Unhook(obj, method)
	if type(obj) == "string" then
		unhookFunction(self, obj)
	else
		AceHook:argCheck(obj, 2, "string", "table")
		AceHook:argCheck(method, 3, "string")
		unhookMethod(self, obj, method)
	end
end

function AceHook:UnhookAll()
	for key, value in pairs(registry[self]) do
		if type(key) == "table" then
			for method in pairs(value) do
				self:Unhook(key, method)
			end
		else
			self:Unhook(key)
		end
	end
end

function AceHook:HookReport()
	DEFAULT_CHAT_FRAME:AddMessage("This is a list of all active hooks for this object:")
	if not next(registry[self]) then
		DEFAULT_CHAT_FRAME:AddMessage("No hooks")
	end

	for key, value in pairs(registry[self]) do
		if type(value) == "table" then
			for method, uid in pairs(value) do
				DEFAULT_CHAT_FRAME:AddMessage(string.format("object: %s method: %q |cff%s|r%s", tostring(key), method, actives[uid] and "00ff00Active" or "ffff00Inactive", not self.hooks[key][method] and " |cff7f7fff-Secure-|r" or ""))
			end
		else
			DEFAULT_CHAT_FRAME:AddMessage(string.format("function: %q |cff%s|r%s", tostring(key), actives[value] and "00ff00Active" or "ffff00Inactive", not self.hooks[key] and " |cff7f7fff-Secure-|r" or ""))
		end
	end
end

function AceHook:OnInstanceInit(object)
	if not object.hooks then
		object.hooks = new()
	end
	if not registry[object] then
		registry[object] = new()
	end
end

AceHook.OnManualEmbed = AceHook.OnInstanceInit

function AceHook:OnEmbedDisable(target)
	self.UnhookAll(target)
end

local function activate(self, oldLib, oldDeactivate)
	AceHook = self

	self.handlers = oldLib and oldLib.handlers or {}
	self.registry = oldLib and oldLib.registry or {}
	self.scripts = oldLib and oldLib.scripts or {}
	self.actives = oldLib and oldLib.actives or {}

	handlers = self.handlers
	registry = self.registry
	scripts = self.scripts
	actives = self.actives

	AceHook.super.activate(self, oldLib, oldDeactivate)

	if oldDeactivate then
		oldDeactivate(oldLib)
	end
end

AceLibrary:Register(AceHook, MAJOR_VERSION, MINOR_VERSION, activate)


code:
--[[
Name: AceLibrary
Revision: $Rev: 17722 $
Developed by: The Ace Development Team (http://www.wowace.com/index.php/The_Ace_Development_Team)
Inspired By: Iriel (iriel@vigilance-committee.org)
Tekkub (tekkub@gmail.com)
Revision: $Rev: 17722 $
Website: http://www.wowace.com/
Documentation: http://www.wowace.com/index.php/AceLibrary
SVN: http://svn.wowace.com/root/trunk/Ace2/AceLibrary
Description: Versioning library to handle other library instances, upgrading,
and proper access.
It also provides a base for libraries to work off of, providing
proper error tools. It is handy because all the errors occur in the
file that called it, not in the library file itself.
Dependencies: None
]]

local ACELIBRARY_MAJOR = "AceLibrary"
local ACELIBRARY_MINOR = "$Revision: 17722 $"

if loadstring("return function(...) return ... end") and AceLibrary and AceLibrary:HasInstance(ACELIBRARY_MAJOR) then return end -- lua51 check
local table_setn
do
    local version = GetBuildInfo()
    if string.find(version, "^2%.") then
        -- 2.0.0
        table_setn = function() end
    else
        table_setn = table.setn
    end
end

local string_gfind = string.gmatch or string.gfind

local _G = getfenv(0)
local previous = _G[ACELIBRARY_MAJOR]
if previous and not previous:IsNewVersion(ACELIBRARY_MAJOR, ACELIBRARY_MINOR) then return end

local function safecall(func,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10)
    local success, err = pcall(func,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10)
    if not success then geterrorhandler()(err) end
end

-- @table AceLibrary
-- @brief System to handle all versioning of libraries.
local AceLibrary = {}
local AceLibrary_mt = {}
setmetatable(AceLibrary, AceLibrary_mt)

local tmp
local function error(self, message, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
    if type(self) ~= "table" then
        _G.error(string.format("Bad argument #1 to `error' (table expected, got %s)", type(self)), 2)
    end
    if not tmp then
        tmp = {}
    else
        for k in pairs(tmp) do tmp[k] = nil end
        table_setn(tmp, 0)
    end

    table.insert(tmp, a1)
    table.insert(tmp, a2)
    table.insert(tmp, a3)
    table.insert(tmp, a4)
    table.insert(tmp, a5)
    table.insert(tmp, a6)
    table.insert(tmp, a7)
    table.insert(tmp, a8)
    table.insert(tmp, a9)
    table.insert(tmp, a10)
    table.insert(tmp, a11)
    table.insert(tmp, a12)
    table.insert(tmp, a13)
    table.insert(tmp, a14)
    table.insert(tmp, a15)
    table.insert(tmp, a16)
    table.insert(tmp, a17)
    table.insert(tmp, a18)
    table.insert(tmp, a19)
    table.insert(tmp, a20)

    local stack = debugstack()
    if not message then
        local _,_,second = string.find(stack, "\n(.-)\n")
        message = "error raised! " .. second
    else
        for i = 1,table.getn(tmp) do
            tmp[i] = tostring(tmp[i])
        end
        for i = 1,10 do
            table.insert(tmp, "nil")
        end
        message = string.format(message, unpack(tmp))
    end

    if getmetatable(self) and getmetatable(self).__tostring then
        message = string.format("%s: %s", tostring(self), message)
    elseif type(rawget(self, 'GetLibraryVersion')) == "function" and AceLibrary:HasInstance(self:GetLibraryVersion()) then
        message = string.format("%s: %s", self:GetLibraryVersion(), message)
    elseif type(rawget(self, 'class')) == "table" and type(rawget(self.class, 'GetLibraryVersion')) == "function" and AceLibrary:HasInstance(self.class:GetLibraryVersion()) then
        message = string.format("%s: %s", self.class:GetLibraryVersion(), message)
    end

    local first = string.gsub(stack, "\n.*", "")
    local file = string.gsub(first, ".*\\(.*).lua:%d+: .*", "%1")
    file = string.gsub(file, "([%(%)%.%*%+%-%[%]%?%^%$%%])", "%%%1")

    local i = 0
    for s in string_gfind(stack, "\n([^\n]*)") do
        i = i + 1
        if not string.find(s, file .. "%.lua:%d+:") then
            file = string.gsub(s, "^.*\\(.*).lua:%d+: .*", "%1")
            file = string.gsub(file, "([%(%)%.%*%+%-%[%]%?%^%$%%])", "%%%1")
            break
        end
    end
    local j = 0
    for s in string_gfind(stack, "\n([^\n]*)") do
        j = j + 1
        if j > i and not string.find(s, file .. "%.lua:%d+:") then
            _G.error(message, j + 1)
            return
        end
    end
    _G.error(message, 2)
    return
end

local function assert(self, condition, message, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
    if not condition then
        if not message then
            local stack = debugstack()
            local _,_,second = string.find(stack, "\n(.-)\n")
            message = "assertion failed! " .. second
        end
        error(self, message, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
        return
    end
    return condition
end

local function argCheck(self, arg, num, kind, kind2, kind3, kind4, kind5)
    if type(num) ~= "number" then
        error(self, "Bad argument #3 to `argCheck' (number expected, got %s)", type(num))
    elseif type(kind) ~= "string" then
        error(self, "Bad argument #4 to `argCheck' (string expected, got %s)", type(kind))
    end
    local errored = false
    arg = type(arg)
    if arg ~= kind and arg ~= kind2 and arg ~= kind3 and arg ~= kind4 and arg ~= kind5 then
        local _,_,func = string.find(debugstack(), "`argCheck'.-([`<].-['>])")
        if not func then
            _,_,func = string.find(debugstack(), "([`<].-['>])")
        end
        if kind5 then
            error(self, "Bad argument #%s to %s (%s, %s, %s, %s, or %s expected, got %s)", tonumber(num) or 0/0, func, kind, kind2, kind3, kind4, kind5, arg)
        elseif kind4 then
            error(self, "Bad argument #%s to %s (%s, %s, %s, or %s expected, got %s)", tonumber(num) or 0/0, func, kind, kind2, kind3, kind4, arg)
        elseif kind3 then
            error(self, "Bad argument #%s to %s (%s, %s, or %s expected, got %s)", tonumber(num) or 0/0, func, kind, kind2, kind3, arg)
        elseif kind2 then
            error(self, "Bad argument #%s to %s (%s or %s expected, got %s)", tonumber(num) or 0/0, func, kind, kind2, arg)
        else
            error(self, "Bad argument #%s to %s (%s expected, got %s)", tonumber(num) or 0/0, func, kind, arg)
        end
    end
end

local function pcall(self, func, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
    a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20 = _G.pcall(func, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
    if not a1 then
        error(self, string.gsub(a2, ".-%.lua:%d-: ", ""))
    else
        return a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20
    end
end

local recurse = {}
local function addToPositions(t, major)
    if not AceLibrary.positions[t] or AceLibrary.positions[t] == major then
        rawset(t, recurse, true)
        AceLibrary.positions[t] = major
        for k,v in pairs(t) do
            if type(v) == "table" and not rawget(v, recurse) then
                addToPositions(v, major)
            end
            if type(k) == "table" and not rawget(k, recurse) then
                addToPositions(k, major)
            end
        end
        local mt = getmetatable(t)
        if mt and not rawget(mt, recurse) then
            addToPositions(mt, major)
        end
        rawset(t, recurse, nil)
    end
end

local function svnRevisionToNumber(text)
    if type(text) == "string" then
        if string.find(text, "^%$Revision: (%d+) %$$") then
            return tonumber((string.gsub(text, "^%$Revision: (%d+) %$$", "%1")))
        elseif string.find(text, "^%$Rev: (%d+) %$$") then
            return tonumber((string.gsub(text, "^%$Rev: (%d+) %$$", "%1")))
        elseif string.find(text, "^%$LastChangedRevision: (%d+) %$$") then
            return tonumber((string.gsub(text, "^%$LastChangedRevision: (%d+) %$$", "%1")))
        end
    elseif type(text) == "number" then
        return text
    end
    return nil
end

local crawlReplace
do
    local recurse = {}
    local function func(t, to, from)
        if recurse[t] then
            return
        end
        recurse[t] = true
        local mt = getmetatable(t)
        setmetatable(t, nil)
        rawset(t, to, rawget(t, from))
        rawset(t, from, nil)
        for k,v in pairs(t) do
            if v == from then
                t[k] = to
            elseif type(v) == "table" then
                if not recurse[v] then
                    func(v, to, from)
                end
            end

            if type(k) == "table" then
                if not recurse[k] then
                    func(k, to, from)
                end
            end
        end
        setmetatable(t, mt)
        if mt then
            if mt == from then
                setmetatable(t, to)
            elseif not recurse[mt] then
                func(mt, to, from)
            end
        end
    end
    function crawlReplace(t, to, from)
        func(t, to, from)
        for k in pairs(recurse) do
            recurse[k] = nil
        end
    end
end

-- @function destroyTable
-- @brief    remove all the contents of a table
-- @param t  table to destroy
local function destroyTable(t)
    setmetatable(t, nil)
    for k,v in pairs(t) do t[k] = nil end
    table_setn(t, 0)
end

local function isFrame(frame)
    return type(frame) == "table" and type(rawget(frame, 0)) == "userdata" and type(rawget(frame, 'IsFrameType')) == "function" and getmetatable(frame) and type(rawget(getmetatable(frame), '__index')) == "function"
end

local new, del
do
    local tables = setmetatable({}, {__mode = "k"})

    function new()
        local t = next(tables)
        if t then
            tables[t] = nil
            return t
        else
            return {}
        end
    end

    function del(t, depth)
        if depth and depth > 0 then
            for k,v in pairs(t) do
                if type(v) == "table" and not isFrame(v) then
                    del(v, depth - 1)
                end
            end
        end
        destroyTable(t)
        tables[t] = true
    end
end

-- @function   copyTable
-- @brief      Create a shallow copy of a table and return it.
-- @param from The table to copy from
-- @return     A shallow copy of the table
local function copyTable(from)
    local to = new()
    for k,v in pairs(from) do to[k] = v end
    table_setn(to, table.getn(from))
    setmetatable(to, getmetatable(from))
    return to
end

-- @function         deepTransfer
-- @brief            Fully transfer all data, keeping proper previous table
--                   backreferences stable.
-- @param to         The table with which data is to be injected into
-- @param from       The table whose data will be injected into the first
-- @param saveFields If available, a shallow copy of the basic data is saved
--                   in here.
-- @param list       The account of table references
-- @param list2      The current status on which tables have been traversed.
local deepTransfer
do
    -- @function   examine
    -- @brief      Take account of all the table references to be shared
    --             between the to and from tables.
    -- @param to   The table with which data is to be injected into
    -- @param from The table whose data will be injected into the first
    -- @param list An account of the table references
    local function examine(to, from, list, major)
        list[from] = to
        for k,v in pairs(from) do
            if rawget(to, k) and type(from[k]) == "table" and type(to[k]) == "table" and not list[from[k]] then
                if from[k] == to[k] then
                    list[from[k]] = to[k]
                elseif AceLibrary.positions[from[v]] ~= major and AceLibrary.positions[from[v]] then
                    list[from[k]] = from[k]
                elseif not list[from[k]] then
                    examine(to[k], from[k], list, major)
                end
            end
        end
        return list
    end

    function deepTransfer(to, from, saveFields, major, list, list2)
        setmetatable(to, nil)
        local createdList
        if not list then
            createdList = true
            list = new()
            list2 = new()
            examine(to, from, list, major)
        end
        list2[to] = to
        for k,v in pairs(to) do
            if type(rawget(from, k)) ~= "table" or type(v) ~= "table" or isFrame(v) then
                if saveFields then
                    saveFields[k] = v
                end
                to[k] = nil
            elseif v ~= _G then
                if saveFields then
                    saveFields[k] = copyTable(v)
                end
            end
        end
        for k in pairs(from) do
            if rawget(to, k) and to[k] ~= from[k] and AceLibrary.positions[to[k]] == major and from[k] ~= _G then
                if not list2[to[k]] then
                    deepTransfer(to[k], from[k], nil, major, list, list2)
                end
                to[k] = list[to[k]] or list2[to[k]]
            else
                rawset(to, k, from[k])
            end
        end
        table_setn(to, table.getn(from))
        setmetatable(to, getmetatable(from))
        local mt = getmetatable(to)
        if mt then
            if list[mt] then
                setmetatable(to, list[mt])
            elseif mt.__index and list[mt.__index] then
                mt.__index = list[mt.__index]
            end
        end
        destroyTable(from)
        if createdList then
            del(list)
            del(list2)
        end
    end
end

-- @method      TryToLoadStandalone
-- @brief       Attempt to find and load a standalone version of the requested library
-- @param major A string representing the major version
-- @return      If library is found, return values from the call to LoadAddOn are returned
--              If the library has been requested previously, nil is returned.
local function TryToLoadStandalone(major)
    if not AceLibrary.scannedlibs then AceLibrary.scannedlibs = {} end
    if AceLibrary.scannedlibs[major] then return end

    AceLibrary.scannedlibs[major] = true

    local name, _, _, enabled, loadable = GetAddOnInfo(major)
    if loadable then
        return LoadAddOn(name)
    end

    for i=1,GetNumAddOns() do
        if GetAddOnMetadata(i, "X-AceLibrary-"..major) then
            local name, _, _, enabled, loadable = GetAddOnInfo(i)
            if loadable then
                return LoadAddOn(name)
            end
        end
    end
end

-- @method      IsNewVersion
-- @brief       Obtain whether the supplied version would be an upgrade to the
--              current version. This allows for bypass code in library
--              declaration.
-- @param major A string representing the major version
-- @param minor An integer or an svn revision string representing the minor version
-- @return      whether the supplied version would be newer than what is
--              currently available.
function AceLibrary:IsNewVersion(major, minor)
    argCheck(self, major, 2, "string")
    TryToLoadStandalone(major)

    if type(minor) == "string" then
        local m = svnRevisionToNumber(minor)
        if m then
            minor = m
        else
            _G.error(string.format("Bad argument #3 to  `IsNewVersion'. Must be a number or SVN revision string. %q is not appropriate", minor), 2)
        end
    end
    argCheck(self, minor, 3, "number")
    local data = self.libs[major]
    if not data then
        return true
    end
    return data.minor < minor
end

-- @method      HasInstance
-- @brief       Returns whether an instance exists. This allows for optional support of a library.
-- @param major A string representing the major version.
-- @param minor (optional) An integer or an svn revision string representing the minor version.
-- @return      Whether an instance exists.
function AceLibrary:HasInstance(major, minor)
    argCheck(self, major, 2, "string")
    TryToLoadStandalone(major)

    if minor then
        if type(minor) == "string" then
            local m = svnRevisionToNumber(minor)
            if m then
                minor = m
            else
                _G.error(string.format("Bad argument #3 to  `HasInstance'. Must be a number or SVN revision string. %q is not appropriate", minor), 2)
            end
        end
        argCheck(self, minor, 3, "number")
        if not self.libs[major] then
            return
        end
        return self.libs[major].minor == minor
    end
    return self.libs[major] and true
end

-- @method      GetInstance
-- @brief       Returns the library with the given major/minor version.
-- @param major A string representing the major version.
-- @param minor (optional) An integer or an svn revision string representing the minor version.
-- @return      The library with the given major/minor version.
function AceLibrary:GetInstance(major, minor)
    argCheck(self, major, 2, "string")
    TryToLoadStandalone(major)

    local data = self.libs[major]
    if not data then
        _G.error(string.format("Cannot find a library instance of %s.", major), 2)
        return
    end
    if minor then
        if type(minor) == "string" then
            local m = svnRevisionToNumber(minor)
            if m then
                minor = m
            else
                _G.error(string.format("Bad argument #3 to  `GetInstance'. Must be a number or SVN revision string. %q is not appropriate", minor), 2)
            end
        end
        argCheck(self, minor, 2, "number")
        if data.minor ~= minor then
            _G.error(string.format("Cannot find a library instance of %s, minor version %d.", major, minor), 2)
            return
        end
    end
    return data.instance
end

-- Syntax sugar.  AceLibrary("FooBar-1.0")
AceLibrary_mt.__call = AceLibrary.GetInstance

local donothing

local AceEvent

-- @method               Register
-- @brief                Registers a new version of a given library.
-- @param newInstance    the library to register
-- @param major          the major version of the library
-- @param minor          the minor version of the library
-- @param activateFunc   (optional) A function to be called when the library is
--                       fully activated. Takes the arguments
--                       (newInstance [, oldInstance, oldDeactivateFunc]). If
--                       oldInstance is given, you should probably call
--                       oldDeactivateFunc(oldInstance).
-- @param deactivateFunc (optional) A function to be called by a newer library's
--                       activateFunc.
-- @param externalFunc   (optional) A function to be called whenever a new
--                       library is registered.
function AceLibrary:Register(newInstance, major, minor, activateFunc, deactivateFunc, externalFunc)
    argCheck(self, newInstance, 2, "table")
    argCheck(self, major, 3, "string")
    if type(minor) == "string" then
        local m = svnRevisionToNumber(minor)
        if m then
            minor = m
        else
            _G.error(string.format("Bad argument #4 to  `Register'. Must be a number or SVN revision string. %q is not appropriate", minor), 2)
        end
    end
    argCheck(self, minor, 4, "number")
    if math.floor(minor) ~= minor or minor < 0 then
        error(self, "Bad argument #4 to `Register' (integer >= 0 expected, got %s)", minor)
    end
    argCheck(self, activateFunc, 5, "function", "nil")
    argCheck(self, deactivateFunc, 6, "function", "nil")
    argCheck(self, externalFunc, 7, "function", "nil")
    if not deactivateFunc then
        if not donothing then
            donothing = function() end
        end
        deactivateFunc = donothing
    end
    local data = self.libs[major]
    if not data then
        -- This is new
        local instance = copyTable(newInstance)
        crawlReplace(instance, instance, newInstance)
        destroyTable(newInstance)
        if AceLibrary == newInstance then
            self = instance
            AceLibrary = instance
        end
        self.libs[major] = {
            instance = instance,
            minor = minor,
            deactivateFunc = deactivateFunc,
            externalFunc = externalFunc,
        }
        rawset(instance, 'GetLibraryVersion', function(self)
            return major, minor
        end)
        if not rawget(instance, 'error') then
            rawset(instance, 'error', error)
        end
        if not rawget(instance, 'assert') then
            rawset(instance, 'assert', assert)
        end
        if not rawget(instance, 'argCheck') then
            rawset(instance, 'argCheck', argCheck)
        end
        if not rawget(instance, 'pcall') then
            rawset(instance, 'pcall', pcall)
        end
        addToPositions(instance, major)
        if activateFunc then
            safecall(activateFunc, instance, nil, nil) -- no old version, so explicit nil
        end

        if externalFunc then
            for k,data in pairs(self.libs) do
                if k ~= major then
                    safecall(externalFunc, instance, k, data.instance)
                end
            end
        end

        for k,data in pairs(self.libs) do
            if k ~= major and data.externalFunc then
                safecall(data.externalFunc, data.instance, major, instance)
            end
        end
        if major == "AceEvent-2.0" then
            AceEvent = instance
        end
        if AceEvent then
            AceEvent.TriggerEvent(self, "AceLibrary_Register", major, instance)
        end

        return instance
    end
    local instance = data.instance
    if minor <= data.minor then
        -- This one is already obsolete, raise an error.
        _G.error(string.format("Obsolete library registered. %s is already registered at version %d. You are trying to register version %d. Hint: if not AceLibrary:IsNewVersion(%q, %d) then return end", major, data.minor, minor, major, minor), 2)
        return
    end
    -- This is an update
    local oldInstance = new()

    addToPositions(newInstance, major)
    local isAceLibrary = (AceLibrary == newInstance)
    local old_error, old_assert, old_argCheck, old_pcall
    if isAceLibrary then
        self = instance
        AceLibrary = instance

        old_error = instance.error
        old_assert = instance.assert
        old_argCheck = instance.argCheck
        old_pcall = instance.pcall

        self.error = error
        self.assert = assert
        self.argCheck = argCheck
        self.pcall = pcall
    end
    deepTransfer(instance, newInstance, oldInstance, major)
    crawlReplace(instance, instance, newInstance)
    local oldDeactivateFunc = data.deactivateFunc
    data.minor = minor
    data.deactivateFunc = deactivateFunc
    data.externalFunc = externalFunc
    rawset(instance, 'GetLibraryVersion', function(self)
        return major, minor
    end)
    if not rawget(instance, 'error') then
        rawset(instance, 'error', error)
    end
    if not rawget(instance, 'assert') then
        rawset(instance, 'assert', assert)
    end
    if not rawget(instance, 'argCheck') then
        rawset(instance, 'argCheck', argCheck)
    end
    if not rawget(instance, 'pcall') then
        rawset(instance, 'pcall', pcall)
    end
    if isAceLibrary then
        for _,v in pairs(self.libs) do
            local i = type(v) == "table" and v.instance
            if type(i) == "table" then
                if not rawget(i, 'error') or i.error == old_error then
                    rawset(i, 'error', error)
                end
                if not rawget(i, 'assert') or i.assert == old_assert then
                    rawset(i, 'assert', assert)
                end
                if not rawget(i, 'argCheck') or i.argCheck == old_argCheck then
                    rawset(i, 'argCheck', argCheck)
                end
                if not rawget(i, 'pcall') or i.pcall == old_pcall then
                    rawset(i, 'pcall', pcall)
                end
            end
        end
    end
    if activateFunc then
        safecall(activateFunc, instance, oldInstance, oldDeactivateFunc)
    else
        safecall(oldDeactivateFunc, oldInstance)
    end
    del(oldInstance)

    if externalFunc then
        for k,data in pairs(self.libs) do
            if k ~= major then
                safecall(externalFunc, instance, k, data.instance)
            end
        end
    end

    return instance
end

local iter
function AceLibrary:IterateLibraries()
    if not iter then
        local function iter(t, k)
            k = next(t, k)
            if not k then
                return nil
            else
                return k, t[k].instance
            end
        end
    end
    return iter, self.libs, nil
end

-- @function            Activate
-- @brief               The activateFunc for AceLibrary itself. Called when
--                      AceLibrary properly registers.
-- @param self          Reference to AceLibrary
-- @param oldLib        (optional) Reference to an old version of AceLibrary
-- @param oldDeactivate (optional) Function to deactivate the old lib
local function activate(self, oldLib, oldDeactivate)
    if not self.libs then
        if oldLib then
            self.libs = oldLib.libs
            self.scannedlibs = oldLib.scannedlibs
        end
        if not self.libs then
            self.libs = {}
        end
        if not self.scannedlibs then
            self.scannedlibs = {}
        end
    end
    if not self.positions then
        if oldLib then
            self.positions = oldLib.positions
        end
        if not self.positions then
            self.positions = setmetatable({}, { __mode = "k" })
        end
    end

    -- Expose the library in the global environment
    _G[ACELIBRARY_MAJOR] = self

    if oldDeactivate then
        oldDeactivate(oldLib)
    end
end

if not previous then
    previous = AceLibrary
end
if not previous.libs then
    previous.libs = {}
end
AceLibrary.libs = previous.libs
if not previous.positions then
    previous.positions = setmetatable({}, { __mode = "k" })
end
AceLibrary.positions = previous.positions
AceLibrary:Register(AceLibrary, ACELIBRARY_MAJOR, ACELIBRARY_MINOR, activate)

repo_name: SweedJesus/sjLinkSpoof

license: unknown


code:
--[[
Name: AceOO-2.0
Revision: $Rev: 17638 $
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/AceOO-2.0
SVN: http://svn.wowace.com/root/trunk/Ace2/AceOO-2.0
Description: Library to provide an object-orientation framework.
Dependencies: AceLibrary
]]

local MAJOR_VERSION = "AceOO-2.0"
local MINOR_VERSION = "$Revision: 17638 $"

-- This ensures the code is only executed if the libary doesn't already exist, or is a newer version
if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary.") end
if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end

if loadstring("return function(...) return ... end") and AceLibrary:HasInstance(MAJOR_VERSION) then return end -- lua51 check
local table_setn
do
	local version = GetBuildInfo()
	if string.find(version, "^2%.") then
		-- 2.0.0
		table_setn = function() end
	else
		table_setn = table.setn
	end
end

local AceOO = {
	error = AceLibrary.error,
	argCheck = AceLibrary.argCheck
}

-- @function	getuid
-- @brief		Obtain a unique string identifier for the object in question.
-- @param t		The object to obtain the uid for.
-- @return		The uid string.
local function pad(cap)
	return string.rep('0', 8 - string.len(cap)) .. cap
end
local function getuid(t)
	local mt = getmetatable(t)
	setmetatable(t, nil)
	local str = tostring(t)
	setmetatable(t, mt)
	local _,_,cap = string.find(str, '[^:]*: 0x(.*)$')
	if cap then return pad(cap) end
	_,_,cap = string.find(str, '[^:]*: (.*)$')
	if cap then return pad(cap) end
end

local function getlibrary(o)
	if type(o) == "table" then
		return o
	elseif type(o) == "string" then
		if not AceLibrary:HasInstance(o) then
			AceOO:error("Library %q does not exist.", o)
		end
		return AceLibrary(o)
	end
end

-- @function		Factory
-- @brief			Construct a factory for the creation of objects.
-- @param obj		The object whose init method will be called on the new factory
--					object.
-- @param newobj	The object whose init method will be called on the new
--					objects that the Factory creates, to initialize them.
-- @param (a1..a20) Arguments which will be passed to obj.init() in addition
--					to the Factory object.
-- @return			The new factory which creates a newobj when its new method is called,
--					or when it is called directly (__call metamethod).
local Factory
do
	local function new(obj, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12,
							a13, a14, a15, a16, a17, a18, a19, a20)
		local t = {}
		local uid = getuid(t)
		local l = getlibrary
		obj:init(t, l(a1), l(a2), l(a3), l(a4), l(a5), l(a6), l(a7),
					l(a8), l(a9), l(a10), l(a11), l(a12), l(a13),
					l(a14), l(a15), l(a16), l(a17), l(a18), l(a19),
					l(a20))
		t.uid = uid
		return t
	end

	local function createnew(self, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10,
									a11, a12, a13, a14, a15, a16, a17, a18,
									a19, a20)
		local o = self.prototype
		local l = getlibrary
		return new(o, l(a1), l(a2), l(a3), l(a4), l(a5), l(a6), l(a7),
						l(a8), l(a9), l(a10), l(a11), l(a12), l(a13),
						l(a14), l(a15), l(a16), l(a17), l(a18), l(a19),
						l(a20))
	end

	function Factory(obj, newobj, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10,
									a11, a12, a13, a14, a15, a16, a17, a18,
									a19, a20)
		local t = new(obj, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12,
							a13, a14, a15, a16, a17, a18, a19, a20)
		t.prototype = newobj
		t.new = createnew
		getmetatable(t).__call = t.new
		return t
	end
end

local function objtostring(self)
	if self.ToString then
		return self:ToString()
	elseif self.GetLibraryVersion then
		return (self:GetLibraryVersion())
	elseif self.super then
		local s = "Sub-" .. tostring(self.super)
		local first = true
		if self.interfaces then
			for interface in pairs(self.interfaces) do
				if first then
					s = s .. "(" .. tostring(interface)
					first = false
				else
					s = s .. ", " .. tostring(interface)
				end
			end
		end
		if self.mixins then
			for mixin in pairs(self.mixins) do
				if first then
					s = s .. tostring(mixin)
					first = false
				else
					s = s .. ", " .. tostring(mixin)
				end
			end
		end
		if first then
			if self.uid then
				return s .. ":" .. self.uid
			else
				return s
			end
		else
			return s .. ")"
		end
	else
		return self.uid and 'Subclass:' .. self.uid or 'Subclass'
	end
end

-- @table			Object
-- @brief			Base of all objects, including Class.
--
-- @method			init
-- @brief			Initialize a new object.
-- @param newobject The object to initialize
-- @param class		The class to make newobject inherit from
local Object
do
	Object = {}
	function Object:init(newobject, class)
		local parent = class or self
		if not rawget(newobject, 'uid') then
			newobject.uid = getuid(newobject)
		end
		local mt = {
			__index = parent,
			__tostring = objtostring,
		}
		setmetatable(newobject, mt)
	end
	Object.uid = getuid(Object)
	setmetatable(Object, { __tostring = function() return 'Object' end })
end

local Interface

local function validateInterface(object, interface)
	if not object.class and object.prototype then
		object = object.prototype
	end
	for k,v in pairs(interface.interface) do
		if tostring(type(object[k])) ~= v then
			return false
		end
	end
	if interface.superinterfaces then
		for superinterface in pairs(interface.superinterfaces) do
			if not validateInterface(object, superinterface) then
				return false
			end
		end
	end
	if type(object.class) == "table" and rawequal(object.class.prototype, object) then
		if not object.class.interfaces then
			rawset(object.class, 'interfaces', {})
		end
		object.class.interfaces[interface] = true
	elseif type(object.class) == "table" and type(object.class.prototype) == "table" then
		validateInterface(object.class.prototype, interface)
		-- check if class is proper, thus preventing future checks.
	end
	return true
end

-- @function		inherits
-- @brief			Return whether an Object or Class inherits from a given
--					parent.
-- @param object	Object or Class to check
-- @param parent	Parent to test inheritance from
-- @return			whether an Object or Class inherits from a given
--					parent.
local function inherits(object, parent)
	object = getlibrary(object)
	if type(parent) == "string" then
		if not AceLibrary:HasInstance(parent) then
			return false
		else
			parent = AceLibrary(parent)
		end
	end
	AceOO:argCheck(parent, 2, "table")
	if type(object) ~= "table" then
		return false
	end
	local current
	if object.class then
		current = object.class
	else
		current = object
	end
	if type(current) ~= "table" then
		return false
	end
	if rawequal(current, parent) then
		return true
	end
	if parent.class then
		while true do
			if rawequal(current, Object) then
				break
			end
			if current.mixins then
				for mixin in pairs(current.mixins) do
					if rawequal(mixin, parent) then
						return true
					end
				end
			end
			if current.interfaces then
				for interface in pairs(current.interfaces) do
					if rawequal(interface, parent) then
						return true
					end
				end
			end
			current = current.super
			if type(current) ~= "table" then
				break
			end
		end

		local isInterface = false
		local curr = parent.class
		while true do
			if rawequal(curr, Object) then
				break
			elseif rawequal(curr, Interface) then
				isInterface = true
				break
			end
			curr = curr.super
			if type(curr) ~= "table" then
				break
			end
		end
		return isInterface and validateInterface(object, parent)
	else
		while true do
			if rawequal(current, parent) then
				return true
			elseif rawequal(current, Object) then
				return false
			end
			current = current.super
			if type(current) ~= "table" then
				return false
			end
		end
	end
end

-- @table			Class
-- @brief			An object factory which sets up inheritence and supports
--					'mixins'.
--
-- @metamethod		Class call
-- @brief			Call ClassFactory:new() to create a new class.
--
-- @method			Class new
-- @brief			Construct a new object.
-- @param (a1..a20) Arguments to pass to the object init function.
-- @return			The new object.
--
-- @method			Class init
-- @brief			Initialize a new class.
-- @param parent	Superclass.
-- @param (a1..a20) Mixins.
--
-- @method			Class ToString
-- @return			A string representing the object, in this case 'Class'.
local initStatus
local Class
local Mixin
local autoEmbed = false
local function traverseInterfaces(bit, total)
	if bit.superinterfaces then
		for interface in pairs(bit.superinterfaces) do
			if not total[interface] then
				total[interface] = true
				traverseInterfaces(interface, total)
			end
		end
	end
end
local class_new
do
	Class = Factory(Object, setmetatable({}, {__index = Object}), Object)
	Class.super = Object

	local function protostring(t)
		return '<' .. tostring(t.class) .. ' prototype>'
	end
	local function classobjectstring(t)
		if t.ToString then
			return t:ToString()
		elseif t.GetLibraryVersion then
			return (t:GetLibraryVersion())
		else
			return '<' .. tostring(t.class) .. ' instance>'
		end
	end
	local function classobjectequal(self, other)
		if type(self) == "table" and self.Equals then
			return self:Equals(other)
		elseif type(other) == "table" and other.Equals then
			return other:Equals(self)
		elseif type(self) == "table" and self.CompareTo then
			return self:CompareTo(other) == 0
		elseif type(other) == "table" and other.CompareTo then
			return other:CompareTo(self) == 0
		else
			return rawequal(self, other)
		end
	end
	local function classobjectlessthan(self, other)
		if type(self) == "table" and self.IsLessThan then
			return self:IsLessThan(other)
		elseif type(other) == "table" and other.IsLessThanOrEqualTo then
			return not other:IsLessThanOrEqualTo(self)
		elseif type(self) == "table" and self.CompareTo then
			return self:CompareTo(other) < 0
		elseif type(other) == "table" and other.CompareTo then
			return other:CompareTo(self) > 0
		elseif type(other) == "table" and other.IsLessThan and other.Equals then
			return other:Equals(self) or other:IsLessThan(self)
		else
			AceOO:error("cannot compare two objects")
		end
	end
	local function classobjectlessthanequal(self, other)
		if type(self) == "table" and self.IsLessThanOrEqualTo then
			return self:IsLessThanOrEqualTo(other)
		elseif type(other) == "table" and other.IsLessThan then
			return not other:IsLessThan(self)
		elseif type(self) == "table" and self.CompareTo then
			return self:CompareTo(other) <= 0
		elseif type(other) == "table" and other.CompareTo then
			return other:CompareTo(self) >= 0
		elseif type(self) == "table" and self.IsLessThan and self.Equals then
			return self:Equals(other) or self:IsLessThan(other)
		else
			AceOO:error("cannot compare two incompatible objects")
		end
	end
	local function classobjectadd(self, other)
		if type(self) == "table" and self.Add then
			return self:Add(other)
		else
			AceOO:error("cannot add two incompatible objects")
		end
	end
	local function classobjectsub(self, other)
		if type(self) == "table" and self.Subtract then
			return self:Subtract(other)
		else
			AceOO:error("cannot subtract two incompatible objects")
		end
	end
	local function classobjectunm(self, other)
		if type(self) == "table" and self.UnaryNegation then
			return self:UnaryNegation(other)
		else
			AceOO:error("attempt to negate an incompatible object")
		end
	end
	local function classobjectmul(self, other)
		if type(self) == "table" and self.Multiply then
			return self:Multiply(other)
		else
			AceOO:error("cannot multiply two incompatible objects")
		end
	end
	local function classobjectdiv(self, other)
		if type(self) == "table" and self.Divide then
			return self:Divide(other)
		else
			AceOO:error("cannot divide two incompatible objects")
		end
	end
	local function classobjectpow(self, other)
		if type(self) == "table" and self.Exponent then
			return self:Exponent(other)
		else
			AceOO:error("cannot exponentiate two incompatible objects")
		end
	end
	local function classobjectconcat(self, other)
		if type(self) == "table" and self.Concatenate then
			return self:Concatenate(other)
		else
			AceOO:error("cannot concatenate two incompatible objects")
		end
	end
	function class_new(self, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12,
						a13, a14, a15, a16, a17, a18, a19, a20)
		if self.virtual then
			AceOO:error("Cannot instantiate a virtual class.")
		end

		local o = self.prototype
		local newobj = {}
		if o.class and o.class.instancemeta then
			setmetatable(newobj, o.class.instancemeta)
		else
			Object:init(newobj, o)
		end

		if self.interfaces and not self.interfacesVerified then
			-- Verify the interfaces

			for interface in pairs(self.interfaces) do
				for field,kind in pairs(interface.interface) do
					if tostring(type(newobj[field])) ~= kind then
						AceOO:error("Class did not satisfy all interfaces. %q is required to be a %s. It is a %s", field, kind, tostring(type(newobj[field])))
					end
				end
			end
			self.interfacesVerified = true
		end
		local tmp = initStatus
		initStatus = newobj
		newobj:init(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12,
						a13, a14, a15, a16, a17, a18, a19, a20)
		if initStatus then
			initStatus = tmp
			AceOO:error("Initialization not completed, be sure to call the superclass's init method.")
			return
		end
		initStatus = tmp
		return newobj
	end
	local classmeta = {
		__tostring = objtostring,
		__call = function(self, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12,
						a13, a14, a15, a16, a17, a18, a19, a20)
			return self:new(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12,
						a13, a14, a15, a16, a17, a18, a19, a20)
		end,
	}
	function Class:init(newclass, parent, a1, a2, a3, a4, a5, a6, a7, a8, a9,
											a10, a11, a12, a13, a14, a15, a16,
											a17, a18, a19, a20)
		parent = parent or self

		local total

		if parent.class then
			total = {
				parent, a1, a2, a3, a4, a5, a6, a7, a8, a9,
						a10, a11, a12, a13, a14, a15, a16,
						a17, a18, a19, a20
			}
			parent = self
		else
			total = {
				a1, a2, a3, a4, a5, a6, a7, a8, a9,
				a10, a11, a12, a13, a14, a15, a16,
				a17, a18, a19, a20
			}
		end
		if not inherits(parent, Class) then
			AceOO:error("Classes must inherit from a proper class")
		end
		if parent.sealed then
			AceOO:error("Cannot inherit from a sealed class")
		end
		for i,v in ipairs(total) do
			if inherits(v, Mixin) and v.class then
				if not newclass.mixins then
					newclass.mixins = {}
				end
				if newclass.mixins[v] then
					AceOO:error("Cannot explicitly inherit from the same mixin twice")
				end
				newclass.mixins[v] = true
			elseif inherits(v, Interface) and v.class then
				if not newclass.interfaces then
					newclass.interfaces = {}
				end
				if newclass.interfaces[v] then
					AceOO:error("Cannot explicitly inherit from the same interface twice")
				end
				newclass.interfaces[v] = true
			else
				AceOO:error("Classes can only inherit from one or zero classes and any number of mixins or interfaces")
			end
		end
		if parent.interfaces then
			if newclass.interfaces then
				for interface in pairs(parent.interfaces) do
					newclass.interfaces[interface] = true
				end
			else
				newclass.interfaces = parent.interfaces
			end
		end
		for k in pairs(total) do
			total[k] = nil
		end
		table_setn(total, 0)

		newclass.super = parent

		newclass.prototype = setmetatable(total, {
			__index = parent.prototype,
			__tostring = protostring,
		})
		total = nil

		newclass.instancemeta = {
			__index = newclass.prototype,
			__tostring = classobjectstring,
			__eq = classobjectequal,
			__lt = classobjectlessthan,
			__le = classobjectlessthanequal,
			__add = classobjectadd,
			__sub = classobjectsub,
			__unm = classobjectunm,
			__mul = classobjectmul,
			__div = classobjectdiv,
			__pow = classobjectpow,
			__concat = classobjectconcat,
		}

		setmetatable(newclass, classmeta)

		newclass.new = class_new

		if newclass.mixins then
			-- Fold in the mixins
			local err, msg
			for mixin in pairs(newclass.mixins) do
				local ret
				autoEmbed = true
				ret, msg = pcall(mixin.embed, mixin, newclass.prototype)
				autoEmbed = false
				if not ret then
					err = true
					break
				end
			end

			if err then
				local pt = newclass.prototype
				for k,v in pairs(pt) do
					pt[k] = nil
				end

				-- method conflict
				AceOO:error(msg)
			end
		end

		newclass.prototype.class = newclass

		if newclass.interfaces then
			for interface in pairs(newclass.interfaces) do
				traverseInterfaces(interface, newclass.interfaces)
			end
		end
		if newclass.mixins then
			for mixin in pairs(newclass.mixins) do
				if mixin.interfaces then
					if not newclass.interfaces then
						newclass.interfaces = {}
					end
					for interface in pairs(mixin.interfaces) do
						newclass.interfaces[interface] = true
					end
				end
			end
		end
	end
	function Class:ToString()
		if type(self.GetLibraryVersion) == "function" then
			return (self:GetLibraryVersion())
		else
			return "Class"
		end
	end

	local tmp
	function Class.prototype:init()
		if rawequal(self, initStatus) then
			initStatus = nil
		else
			AceOO:error("Improper self passed to init. You must do MyClass.super.prototype.init(self, ...)", 2)
		end
		self.uid = getuid(self)
		local current = self.class
		while true do
			if current == Class then
				break
			end
			if current.mixins then
				for mixin in pairs(current.mixins) do
					if type(mixin.OnInstanceInit) == "function" then
						mixin:OnInstanceInit(self)
					end
				end
			end
			current = current.super
		end
	end
end

-- @object	ClassFactory
-- @brief	A factory for creating classes.	Rarely used directly.
local ClassFactory = Factory(Object, Class, Object)

function Class:new(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11,
					a12, a13, a14, a15, a16, a17, a18, a19, a20)
	local x = ClassFactory:new(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11,
							a12, a13, a14, a15, a16, a17, a18, a19, a20)
	if AceOO.classes then
		AceOO.classes[x] = true
	end
	return x
end
getmetatable(Class).__call = Class.new

-- @class	Mixin
-- @brief	A class to create mixin objects, which contain methods that get
-- "mixed in" to class prototypes.
--
-- @object	Mixin prototype
-- @brief	The prototype that mixin objects inherit their methods from.
--
-- @method	Mixin prototype embed
-- @brief	Mix in the methods of our object which are listed in our interface
--		 to the supplied target table.
--
-- @method	Mixin prototype init
-- @brief	Initialize the mixin object.
-- @param	newobj	 The new object we're initializing.
-- @param	interface	The interface we implement (the list of methods our
--					prototype provides which should be mixed into the target
--					table by embed).
do
	Mixin = Class()
	function Mixin:ToString()
		if self.GetLibraryVersion then
			return (self:GetLibraryVersion())
		else
			return 'Mixin'
		end
	end
	local function _Embed(state, field, target)
		field = next(state.export, field)
		if field == nil then
			return
		end

		if rawget(target, field) or (target[field] and target[field] ~= state[field]) then
			AceOO:error("Method conflict in attempt to mixin. Field %q", field)
		end

		target[field] = state[field]

		local ret,msg = pcall(_Embed, state, field, target)
		if not ret then
			-- Mix in the next method according to the defined interface.	If that
			-- fails due to a conflict, re-raise to back out the previous mixed
			-- methods.

			target[field] = nil
			AceOO:error(msg)
		end
	end
	function Mixin.prototype:embed(target)
		local mt = getmetatable(target)
		setmetatable(target, nil)
		local err, msg = pcall(_Embed, self, nil, target)
		if not err then
			setmetatable(target, mt)
			AceOO:error(msg)
			return
		end
		if type(self.embedList) == "table" then
			self.embedList[target] = true
		end
		if type(target.class) ~= "table" then
			target[self] = true
		end
		if not autoEmbed and type(self.OnManualEmbed) == "function" then
			self:OnManualEmbed(target)
		end
		setmetatable(target, mt)
	end

	function Mixin.prototype:activate(oldLib, oldDeactivate)
		if oldLib and oldLib.embedList then
			for target in pairs(oldLib.embedList) do
				local mt = getmetatable(target)
				setmetatable(target, nil)
				for field in pairs(oldLib.export) do
					target[field] = nil
				end
				setmetatable(target, mt)
			end
			self.embedList = oldLib.embedList
			for target in pairs(self.embedList) do
				self:embed(target)
			end
		else
			self.embedList = setmetatable({}, {__mode="k"})
		end
	end

	function Mixin.prototype:init(export, a1, a2, a3, a4, a5, a6, a7, a8, a9,
											a10, a11, a12, a13, a14, a15, a16,
											a17, a18, a19, a20)
		AceOO:argCheck(export, 2, "table")
		for k,v in pairs(export) do
			if type(k) ~= "number" then
				AceOO:error("All keys to argument #2 must be numbers.")
			elseif type(v) ~= "string" then
				AceOO:error("All values to argument #2 must be strings.")
			end
		end
		local num = table.getn(export)
		for i = 1, num do
			local v = export[i]
			export[i] = nil
			export[v] = true
		end
		table_setn(export, 0)
		local interfaces
		if a1 then
			local l = getlibrary
			interfaces = { l(a1), l(a2), l(a3), l(a4), l(a5), l(a6), l(a7), l(a8),
				l(a9), l(a10), l(a11), l(a12), l(a13), l(a14), l(a15), l(a16),
				l(a17), l(a18), l(a19), l(a20) }
			for _,v in ipairs(interfaces) do
				if not v.class or not inherits(v, Interface) then
					AceOO:error("Mixins can inherit only from interfaces")
				end
			end
			local num = table.getn(interfaces)
			for i = 1, num do
				local v = interfaces[i]
				interfaces[i] = nil
				interfaces[v] = true
			end
			table_setn(interfaces, 0)
			for interface in pairs(interfaces) do
				traverseInterfaces(interface, interfaces)
			end
			for interface in pairs(interfaces) do
				for field,kind in pairs(interface.interface) do
					if kind ~= "nil" then
						local good = false
						for bit in pairs(export) do
							if bit == field then
								good = true
								break
							end
						end
						if not good then
							AceOO:error("Mixin does not fully accommodate field %q", field)
						end
					end
				end
			end
		end
		self.super = Mixin.prototype
		Mixin.super.prototype.init(self)
		self.export = export
		self.interfaces = interfaces
	end
end

-- @class Interface
-- @brief A class to create interfaces, which contain contracts that classes
--			which inherit from this must comply with.
--
-- @object Interface prototype
-- @brief	The prototype that interface objects must adhere to.
--
-- @method Interface prototype init
-- @brief	Initialize the mixin object.
-- @param	interface	The interface we contract (the hash of fields forced).
-- @param	(a1..a20)	Superinterfaces
do
	Interface = Class()
	function Interface:ToString()
		if self.GetLibraryVersion then
			return (self:GetLibraryVersion())
		else
			return 'Instance'
		end
	end
	function Interface.prototype:init(interface, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
		Interface.super.prototype.init(self)
		AceOO:argCheck(interface, 2, "table")
		for k,v in pairs(interface) do
			if type(k) ~= "string" then
				AceOO:error("All keys to argument #2 must be numbers.")
			elseif type(v) ~= "string" then
				AceOO:error("All values to argument #2 must be strings.")
			elseif v ~= "nil" and v ~= "string" and v ~= "number" and v ~= "table" and v ~= "function" then
				AceOO:error('All values to argument #2 must either be "nil", "string", "number", "table", or "function".')
			end
		end
		local l = getlibrary
		a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20 = l(a1), l(a2), l(a3), l(a4), l(a5), l(a6), l(a7), l(a8), l(a9), l(a10), l(a11), l(a12), l(a13), l(a14), l(a15), l(a16), l(a17), l(a18), l(a19), l(a20)
		if a1 then
			self.superinterfaces = {a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20}
			for k,v in ipairs(self.superinterfaces) do
				if not inherits(v, Interface) or not v.class then
					AceOO:error('Cannot provide a non-Interface to inherit from')
				end
			end
			local num = table.getn(self.superinterfaces)
			for i = 1, num do
				local v = self.superinterfaces[i]
				self.superinterfaces[i] = nil
				self.superinterfaces[v] = true
			end
			table_setn(self.superinterfaces, 0)
		end
		self.interface = interface
	end
end

-- @function Classpool
-- @brief	Obtain a read only class from our pool of classes, indexed by the
--		 superclass and mixins.
-- @param	sc		 The superclass of the class we want.
-- @param	(m1..m20)	Mixins of the class we want's objects.
-- @return A read only class from the class pool.
local Classpool
do
	local pool = setmetatable({}, {__mode = 'v'})
	local function newindex(k, v)
		AceOO:error('Attempt to modify a read-only class.')
	end
	local function protonewindex(k, v)
		AceOO:error('Attempt to modify a read-only class prototype.')
	end
	local function ts(bit)
		if type(bit) ~= "table" then
			return tostring(bit)
		elseif getmetatable(bit) and bit.__tostring then
			return tostring(bit)
		elseif type(bit.GetLibraryVersion) == "function" then
			return bit:GetLibraryVersion()
		else
			return tostring(bit)
		end
	end
	local t
	local function getcomplexuid(sc, m1, m2, m3, m4, m5, m6, m7, m8, m9,
		m10, m11, m12, m13, m14, m15, m16, m17, m18, m19, m20)
		if not t then t = {} end
		if sc then if sc.uid then table.insert(t, sc.uid) else AceOO:error("%s is not an appropriate class/mixin", ts(sc)) end
		if m1 then if m1.uid then table.insert(t, m1.uid) else AceOO:error("%s is not an appropriate mixin", ts(m1)) end
		if m2 then if m2.uid then table.insert(t, m2.uid) else AceOO:error("%s is not an appropriate mixin", ts(m2)) end
		if m3 then if m3.uid then table.insert(t, m3.uid) else AceOO:error("%s is not an appropriate mixin", ts(m3)) end
		if m4 then if m4.uid then table.insert(t, m4.uid) else AceOO:error("%s is not an appropriate mixin", ts(m4)) end
		if m5 then if m5.uid then table.insert(t, m5.uid) else AceOO:error("%s is not an appropriate mixin", ts(m5)) end
		if m6 then if m6.uid then table.insert(t, m6.uid) else AceOO:error("%s is not an appropriate mixin", ts(m6)) end
		if m7 then if m7.uid then table.insert(t, m7.uid) else AceOO:error("%s is not an appropriate mixin", ts(m7)) end
		if m8 then if m8.uid then table.insert(t, m8.uid) else AceOO:error("%s is not an appropriate mixin", ts(m8)) end
		if m9 then if m9.uid then table.insert(t, m9.uid) else AceOO:error("%s is not an appropriate mixin", ts(m9)) end
		if m10 then if m10.uid then table.insert(t, m10.uid) else AceOO:error("%s is not an appropriate mixin", ts(m10)) end
		if m11 then if m11.uid then table.insert(t, m11.uid) else AceOO:error("%s is not an appropriate mixin", ts(m11)) end
		if m12 then if m12.uid then table.insert(t, m12.uid) else AceOO:error("%s is not an appropriate mixin", ts(m12)) end
		if m13 then if m13.uid then table.insert(t, m13.uid) else AceOO:error("%s is not an appropriate mixin", ts(m13)) end
		if m14 then if m14.uid then table.insert(t, m14.uid) else AceOO:error("%s is not an appropriate mixin", ts(m14)) end
		if m15 then if m15.uid then table.insert(t, m15.uid) else AceOO:error("%s is not an appropriate mixin", ts(m15)) end
		if m16 then if m16.uid then table.insert(t, m16.uid) else AceOO:error("%s is not an appropriate mixin", ts(m16)) end
		if m17 then if m17.uid then table.insert(t, m17.uid) else AceOO:error("%s is not an appropriate mixin", ts(m17)) end
		if m18 then if m18.uid then table.insert(t, m18.uid) else AceOO:error("%s is not an appropriate mixin", ts(m18)) end
		if m19 then if m19.uid then table.insert(t, m19.uid) else AceOO:error("%s is not an appropriate mixin", ts(m19)) end
		if m20 then if m20.uid then table.insert(t, m20.uid) else AceOO:error("%s is not an appropriate mixin", ts(m20)) end
		end end end end end end end end end end end end end end end end end end end end end
		table.sort(t)
		local uid = table.concat(t, '')
		for k in pairs(t) do t[k] = nil end
		table_setn(t, 0)
		return uid
	end
	local classmeta
	function Classpool(sc, m1, m2, m3, m4, m5, m6, m7, m8, m9,
		m10, m11, m12, m13, m14, m15, m16,
		m17, m18, m19, m20)
		local l = getlibrary
		sc, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15, m16, m17, m18, m19, m20 = l(sc), l(m1), l(m2), l(m3), l(m4), l(m5), l(m6), l(m7), l(m8), l(m9), l(m10), l(m11), l(m12), l(m13), l(m14), l(m15), l(m16), l(m17), l(m18), l(m19), l(m20)
		if sc and sc.class then
			sc, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15, m16, m17, m18, m19, m20 = Class, sc, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15, m16, m17, m18, m19
		end
		sc = sc or Class
		local key = getcomplexuid(sc, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15, m16, m17, m18, m19, m20)
		if not pool[key] then
			local class = Class(sc, m1, m2, m3, m4, m5, m6, m7, m8, m9,
			m10, m11, m12, m13, m14, m15, m16, m17,
			m18, m19, m20)
			if not classmeta then
				classmeta = {}
				local mt = getmetatable(class)
				for k,v in pairs(mt) do
					classmeta[k] = v
				end
				classmeta.__newindex = newindex
			end
			-- Prevent the user from adding methods to this class.
			-- NOTE: I'm not preventing modifications of existing class members,
			-- but it's likely that only a truly malicious user will be doing so.
			class.sealed = true
			setmetatable(class, classmeta)
			getmetatable(class.prototype).__newindex = protonewindex
			pool[key] = class
		end
		return pool[key]
	end
end

AceOO.Factory = Factory
AceOO.Object = Object
AceOO.Class = Class
AceOO.Mixin = Mixin
AceOO.Interface = Interface
AceOO.Classpool = Classpool
AceOO.inherits = inherits

-- Library handling bits

local function activate(self, oldLib, oldDeactivate)
	AceOO = self
	Factory = self.Factory
	Object = self.Object
	Class = self.Class
	ClassFactory.prototype = Class
	Mixin = self.Mixin
	Interface = self.Interface
	Classpool = self.Classpool

	if oldLib then
		self.classes = oldLib.classes
	end
	if not self.classes then
		self.classes = setmetatable({}, {__mode="k"})
	else
		for class in pairs(self.classes) do
			class.new = class_new
		end
	end

	if oldDeactivate then
		oldDeactivate(oldLib)
	end
end

AceLibrary:Register(AceOO, MAJOR_VERSION, MINOR_VERSION, activate)
AceOO = AceLibrary(MAJOR_VERSION)


code:
sjLinkSpoof = AceLibrary("AceAddon-2.0"):new(
"AceConsole-2.0",
"AceDebug-2.0",
"AceDB-2.0",
"AceEvent-2.0",
"AceHook-2.1")

local PINK = "|cffff77ff[%s]|r"

local addon = sjLinkSpoof
local addonName = "sjLinkSpoof"
local db

function addon:OnInitialize()
    self:RegisterDB(addonName.."_DB")
    self:RegisterDefaults("profile", {
    })
    db = self.db.profile
    self:RegisterChatCommand({"/sjLinkSpoof", "/sjls"}, {
        type = "group",
        args = {
            show = {
                order = 1,
                name = "Show",
                desc = "Show sjLinkSpoof main frame",
                type = "execute",
                func = function()
                    self.main:Show()
                end
            }
        }
    })
    self:SetDebugging(db.debugging)
end

function addon:OnEnable()
    self:InitFrames()
    self:SetupHooks()
end

function addon:SetDebugging(set)
    db.debugging = set
    self.debugging= set
end

function addon:InitFrames()
    -- Main frame
    f = CreateFrame("Frame", addonName.."_Main", UIParent)
    self.main = f
    f:Hide()
    f:SetWidth(600)
    f:SetHeight(100)
    f:SetPoint("CENTER", 0, 0)
    f:SetMovable(true)
    f:EnableMouse(true)
    f:RegisterForDrag("LeftButton")
    f.background = f:CreateTexture(nil, "BACKGROUND")
    f.background:SetAllPoints()
    f.background:SetTexture(0.1, 0.1, 0.1, 0.5)
    f:SetScript("OnDragStart", function()
        self.main:StartMoving()
    end)
    f:SetScript("OnDragStop", function()
        self.main:StopMovingOrSizing()
    end)
    -- Close button
    f = CreateFrame("Button", addonName.."_CloseButton", self.main, "UIPanelCloseButton")
    self.closeButton = f
    f:SetPoint("TOPRIGHT", 4, 3)
    -- Clear button
    f = CreateFrame("Button", addonName.."_ClearButton", self.main, "UIPanelButtonTemplate")
    self.clearButton = f
    f:SetWidth(80)
    f:SetHeight(20)
    f:SetPoint("RIGHT", self.closeButton, "LEFT", 5, 1)
    f:SetText("Clear")
    f:SetScript("OnClick", function()
        self.editBox1:SetText("")
        self.editBox1:ClearFocus()
        self.editBox2:SetText("")
        self.editBox2:ClearFocus()
    end)
    -- Print button
    f = CreateFrame("Button", addonName.."_PrintButton", self.main, "UIPanelButtonTemplate")
    self.printButton = f
    f:SetWidth(80)
    f:SetHeight(20)
    f:SetPoint("RIGHT", self.clearButton, "LEFT", 0, 0)
    f:SetText("Print")
    f:SetScript("OnClick", function()
        self:Print(format("Link: %s", self.editBox1:GetText()))
    end)
    -- Edit box 1
    f = CreateFrame("EditBox", addonName.."_EditBox1", self.main, "InputBoxTemplate")
    self.editBox1 = f
    f:SetAutoFocus(false)
    f:SetWidth(590)
    f:SetHeight(20)
    f:SetPoint("TOPLEFT", 8, -24)
    f:SetScript("OnEditFocusGained", function()
        self.editBoxFocused = self.editBox1
    end)
    f:SetScript("OnEditFocusLost", function()
        self.editBoxFocused = nil
    end)
    f:SetScript("OnTextChanged", function()
        local text = gsub(self.editBox1:GetText(), "\124", "\124\124")
        self.editBox2:SetText(text)
    end)
    -- Edit box 2
    f = CreateFrame("EditBox", addonName.."_EditBox2", self.main, "InputBoxTemplate")
    self.editBox2 = f
    f:SetAutoFocus(false)
    f:SetWidth(590)
    f:SetHeight(20)
    f:SetPoint("TOP", self.editBox1, "BOTTOM", 0, 0)
    f:SetScript("OnEditFocusGained", function()
        self.editBoxFocused = self.editBox2
    end)
    f:SetScript("OnEditFocusLost", function()
        self.editBoxFocused = nil
    end)
    f:SetScript("OnTextChanged", function()
        local text = gsub(self.editBox2:GetText(), "\124\124", "\124")
        self.editBox1:SetText(text)
    end)
end

function addon:SetupHooks()
    -- Scrolling text frames
    self:Hook("SetItemRef", function(link, text, button)
        if
            strsub(link, 1, 6) ~= "player" and IsShiftKeyDown() and
            not ChatFrameEditBox:IsVisible() then
            if self.editBoxFocused then
                self.editBoxFocused:Insert(text)
            end
        end
        self.hooks["SetItemRef"](link, text, button)
    end)
    -- Container items
    self:Hook("ContainerFrameItemButton_OnClick", function(button, ignoreModifiers)
        if
            button == "LeftButton" and not ignoreModifiers and
            IsShiftKeyDown() and not ChatFrameEditBox:IsVisible() and
            GameTooltipTextLeft1:GetText() then
            if self.editBoxFocused then
                self.editBoxFocused:Insert(GetContainerItemLink(this:GetParent():GetID(), this:GetID()))
            end
        end
        self.hooks["ContainerFrameItemButton_OnClick"](button, ignoreModifiers)
    end)
end


code:
## Interface: 11200
## Title: |cffffff00s|r|cff007fffj|rLinkSpoof |cff7fff7f-Ace2-|r |cffff00001.12|r
## Author: SweedJesus (Miraculin @ Nostalrius)
## X-Website: https://sweedjesus.github.io/my-addons/
## OptionalDeps:
## SavedVariables: sjLinkSpoof_DB

libs\AceLibrary\AceLibrary.lua
libs\AceOO-2.0\AceOO-2.0.lua
libs\AceAddon-2.0\AceAddon-2.0.lua
libs\AceConsole-2.0\AceConsole-2.0.lua
libs\AceDB-2.0\AceDB-2.0.lua
libs\AceDebug-2.0\AceDebug-2.0.lua
libs\AceEvent-2.0\AceEvent-2.0.lua
libs\AceLocale-2.2\AceLocale-2.2.lua
libs\AceModuleCore-2.0\AceModuleCore-2.0.lua

sjLinkSpoof.lua


sourceType: module

semi-spacing: warn

semi-style: warn

eqeqeq: error

no-fallthrough: error

camelcase: warn

comma-spacing: error

comma-style: error

object-curly-newline: off

object-curly-spacing: error

no-var: error

spaced-comment: warn

no-undef: off

no-unused-vars: error

block-scoped-var: warn

dot-notation: off

linebreak-style: error unix

eol-last: warn

unicode-bom: error never

repo_name: Dhanze/fc


code:
*.bat	text eol=crlf
*.js	text eol=lf
*.tw	text eol=lf
*.sh	text eol=lf
*.py	text eol=lf
*.txt	text eol=lf

path: .gitattributes


code:
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
*.swp
*.swo

# C extensions
*.so

# Distribution / packaging
.Python
env/
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
*.egg-info/
.installed.cfg
*.egg
bin/*

# PyInstaller
#  Usually these files are written by a python script from a template
#  before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*,cover
.hypothesis/

# Translations
*.mo
*.pot

# Django stuff:
*.log
local_settings.py

# Flask stuff:
instance/
.webassets-cache

# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build/

# PyBuilder
target/

# IPython Notebook
.ipynb_checkpoints

# pyenv
.python-version

# celery beat schedule file
celerybeat-schedule

# dotenv
.env

# virtualenv
venv/
ENV/

# Spyder project settings
.spyderproject

# Rope project settings
.ropeproject

# Start.tw
src/config/start.tw

# eslint
node_modules
package-lock.json
package.json

# misc
fc-pregmod
*.outline
*.todo
TODO.txt
.vscode/settings.json
*.code-workspace

path: .gitignore


code:
Common problems:

How do I start the game?
-Run the compile file, go to folder "bin", click the "FC_Pregmod" and play. (Recommendation: Drag it into incognito mode)

I get an error on gamestart reading "Apologies! A fatal error has occurred. Aborting. Error: Unexpected token @ in JSON at position 0. Stack Trace: SyntaxError: Unexpected token @ in JSON at position 0 at JSON.parse (<anonymous>) at JSON.value" or some variant
-clear cookies

Everything is broken!
-Do not copy over your existing download as it may leave old files behind, replace it entirely

I can't save more than once or twice.
-Known issue caused by sugarcube level changes. Save to file doesn't have this problem and will likely avoid the first problem as well.
-It is possible to increase the memory utilized by your browser to delay this

I wish to report a sanityCheck issue.
-Great, however a large majority of the results are false positives coming from those specific sections being split over several lines in the name of readability and git grep's intentionally (http://git.661346.n2.nabble.com/bug-git-grep-P-and-multiline-mode-td7613900.html ) lacking support for multiline. An Attempt to add -Pzl (https://gitgud.io/pregmodfan/fc-pregmod/merge_requests/2108 ) created a sub condition black hole. What follows are examples of common false positives that can safely be ignored;

	[MissingClosingAngleBracket]src/art/vector/Generate_Stylesheet.tw:11:<<print "<style>."+_art_display_class+" {
		<<print "<style>."+_art_display_class+" {
	position: absolute;
	height: 100%;
	margin-left: auto;
	margin-right: auto;
	left: 0;
	right: 0;
	}

How to mod (basic doc):

1. All sources now in the src subdir, in separate files. 1 passage = 1 file.

2. Special files and dir's:
	- src/config		- configuration of the story is here.
	- src/config/start.tw	- contains list of .tw passage files, regenerated automatic, by building scripts. Do not change by hands. (original passage Start from pregmod renamed and moved to src/events/intro/introSummary.tw)
	- src/js/storyJS.tw		- special passage with [script] tag - contain all native JavaScript from pregmod.
	- devTools/tweeGo/targets/sugarcube-2/userlib.js - on original FC JS moved here (I deleted it after moving JS to storyJS.tw). Compare to storyJS.tw but do not copy file here. May conflict with src/js/storyJS.tw if copied.
	- src/pregmod		- I put all pregmod-only passages here.
	- .gitignore		- special file for git - to ignore some files. For example - compilation results.

3. Compilation:

	Windows:
	Run compile.bat - result will be file bin/FC_pregmod.html
	Second run of compile.but will overwrite bin/FC_pregmod.html without prompt.

	Linux:
	Ensure executable permission on file "devTools/tweeGo/tweego" (not tweego.exe!)
	Ensure executable permission on file "compile.sh"
	In the root dir of sources (where you see src, devTools, bin...) run command "./compile.sh" from console

	Mac:
	Not supported directly (I don't have access to Mac for testing).
	But you can use linux compilation script if you download tweego for mac from here: https://bitbucket.org/tmedwards/tweego/downloads/ and replace linux executable with mac executable in ./devTools/tweeGo/ folder. This is not tested though, so be warned.

4. Simple comparing and merging with original FC:

	Use meld tool. Place folder FreeCities (original FC sources tree) near FreeCitiesPregmod (this sources tree) and use command:
	meld FreeCities FreeCitiesPregmod
	or just select these folders in meld's GUI.

5. All modders will be very grateful if anyone who makes some changes to game with .html file also post their resulting src folder tree.

6. For contributors to pregmod: if you don't use git, then you need to post your version of src folder tree, not just produced FC_pregmod.html file!!! This html file can't be reverted to proper sources, and useless as contribution!

7. Git workflow:
	- Master branch is pregmod-master. Only Pregmodder can add something to it directly. Always contain his last public changes.
	- pregmod-dev - branch with experimental code mainly by pregmodfan.
	- Any contributions will be placed in separate branches like pregmod-mod-<something> (if it's ready to merge with master complete feature/mod) or pregmod-contrib-<something> if it's partial work until contributions is reviewed.

	Typical cycle with git:
	1. Make account on gitgud if you don't have usable one.
	2. Fork main repository through gitgud interface. (Or pull changes from main repo if you already have fork.)
	3. Clone your fork to local machine with git client (Or pull changes if already cloned.)
	4. Make you changes as you like, commit, and push result into your forked repository (with git client).
	5. Make merge request through gitgud interface.


code:
Further Development:
- specialized slave schools
- fortifications
- more levels for militia edict (further militarize society)
- conquering other arcologies?

Events:
- famous criminal escapes to the arcology, followed by another arcology's police force

Bugs:
- sometimes troop counts breaks
- sometimes rebel numbers have fractionary parts

Rules Assistant:
- find a way for the new intense drugs to fit in
- everything mentioned in https://gitgud.io/pregmodfan/fc-pregmod/issues/81

main.tw porting:
- slaveart
- createsimpletabs
- displaybuilding
- optionssortasappearsonmain
- resetassignmentfilter
- mainlinks
- arcology description
- office description
- slave summary
- use guard
- toychest
- walk past


code:
# How to vector art

Please read the whole document before starting to work.
Some aspects are not obvious right away.

Note: This does not actually describe how to be an artist.

## TL;DR

	killall inkscape
	artTools/vector_layer_split.py artTools/vector_source.svg tw src/art/vector/layers/
	compile
	python3 artTools/normalize_svg.py artTools/vector_source.svg
	git commit -a

## 1. Be an artist

Make changes to the vector_source.svg.
Inkscape was thoroughly tested.
Adobe Illustrator might work decently, too.

## 2. Respect the structure

While editing, keep the Layers in mind.

* In Inkscape, Layers are special "Inkscape groups".
* In Illustrator, Layers are groups with user-defined IDs.
* All Layers should have an ID that is globally unique
  (not just within their subtree).

* Please use anonymous groups only for the ease of editing. Remove all "helper" groups before finally saving the file.
* Anonymous groups can be used for continuous scaling (of e.g. boobs).

* Every asset that should go into a separate file needs to be in a labelled group
  (even if that is a group with only one shape).
* There are some globally available styles defined as CSS classes (e.g. skin, hair).
  Use them if your asset should be changed upon display.
  Do not set the style directly in the object.

## 3. Normalize the document (before committing)

**THIS IS IMPORTANT**

The various editors out there (Inkscape, Illustrator) may use different variants of formatting the SVG XML.
Use

	python3 normalize_svg.py vector_source.svg

before committing to normalize the format so git will not freak out about the changed indentation.

If you use Inkscape, please use in Edit → Settings → Input/Output → SVG-Output → Path Data → Optimized. This is the default.
In case your Editor uses another path data style which cannot be changed, please contact the other artists and developers via the issue tracker to find a new common ground.

What it does:
* Formats the SVG XML according to Pythons lxml module, regardless of editor.
* Adobe Illustrator uses group IDs as layer labels.
  Inkscape however uses layer labels and a separate, auto-generated group ID.
  normalize_svg.py overwrites the group ID with the Inkscape layer label
  so they are synchronized with Inkscape layer labels.
* Inkscape copies the global style definition into the object *every time*
  the object is edited. If an object references a CSS class AND at the same time
  has a local style, the local style is removed
  so global dynamic styling is possible later on.

Note: Behavior of Adobe Illustrator is untested.

## 4. Split the layers

For NoX original, deepmurk extensions, execute

	python3 vector_layer_split.py vector_deepmurk_primary.svg tw ../src/art/vector/layers/

For faraen revamped art (based on NoX original)

	python3 vector_revamp_layer_split.py vector_revamp_source.svg tw ../src/art/vector_revamp/layers/

. This application reads all groups in `vector_source.svg`.
Each group is stored in a separate file in the target directory `/src/art/vector/layers/`.
The group ID sets the file name. Therefore, the group ID **must not** contain spaces or any other weird characters.

Also consider:
* A group with ID ending in _ is ignored. Use Layers ending in _ to keep overview.
* A group with ID starting with "g" (Inkscape) or "XMLID" (Illustrator) is also ignored.
* Existing files are overwritten **without enquiry**.
* The target directory is not emptied. If a file is no longer needed, you should remove it manually.
* This procedure removes global definitions. This means, SVG filters are currently not supported.

Available output formats are `svg` and `tw`.
`svg` output exists for debug reasons.
`tw` embeds the SVG data into Twine files, but removes the global style definitions so they can be set during display.

## 5. Edit the code

`/src/art/` contains Twine code which shows the assets in the story.
There are many helpful comments in `/src/art/artWidgets.tw`.
The code also generates the previously removed global style definitions on the fly and per display.

## 6. Compile the story

Use the provided compile script (Windows batch or shell) for compiling.

## 7. Advanced usage

You can define multiple CSS classes to one element, e.g. "skin torso". When procedurally generating CSS, you can then set a global "skin" style, and a different one for "torso".

You can put variable text into the image using single quotes, e.g. "'+_tattooText+'". The single quotes *break* the quoting in Twine so the content of the Twine variable `_tattooText` is shown upon display. You can even align text on paths.

An anonymous group can be used for dynamic transformation. However, finding the correct parameters for the affine transformation matrix can be cumbersome. Use any method you see fit. See `src/art/vector/Boob.tw` for one example of the result.


code:
Apron
AttractiveLingerie
AttractiveLingerieForAPregnantWoman
BallGown
Battledress
Blouse
BodyOil
Bunny
Chains
ChattelHabit
Cheerleader
ClubslutNetting
ComfortableBodysuit
Conservative
Cutoffs
Cybersuit
FallenNunsHabit
FuckdollSuit
HalterTopDress
HaremGauze
Hijab
Huipil
Kimono
LatexCatsuit
Leotard
LongQipao
MaternityDress
MilitaryUniform
MiniDress
Monokini
NiceBusinessAttire
NiceMaid
NiceNurse
No
PenitentNunsHabit
RedArmyUniform
RestrictiveLatex
ScalemailBikini
SchoolgirlUniform
SchutzstaffelUniform
ShibariRopes
SlaveGown
Slutty
SluttyBusinessAttire
SluttyJewelry
SluttyMaid
SluttyNurse
SluttyQipao
SluttySchutzstaffelUniform
Spats
StretchPants
StringBikini
Succubus
Toga
UncomfortableStraps
Western


code:
#!/bin/bash

# This script reads all possible values of $slave.clothes as mentioned by the documentation.
# This script uses the actual implementation of the JS clothing2artSuffix function as defined in src/art/vector/Helper_Functions.tw
# This script outputs suffixes to be used in the SVG naming scheme
# This script is meant to be executed at the project root directory.
# This script depends on bash, grep, sed, paste and nodejs (so best executed on Linux, I guess)

# grep -Porh '(?<=\.clothes = )"[^"]+"' src/ | sort | uniq # searches sources for all clothes strings actually used in the game, even undocumented ones

(
  echo 'var window = {};'
  grep -v '^:' src/art/vector/Helper_Functions.tw
  echo -n 'Array('
  sed '/^clothes:/,/:/!d' "slave variables documentation - Pregmod.txt" | grep '"' | paste -sd,
  echo ').forEach(v => {console.log(window.clothing2artSuffix(v));});'
) | nodejs | sort


code:
#!/usr/bin/env python3

'''
Application for "normalizing" SVGs

These problems are addressed:

* Inkscape notoriously copies class styles into the object definitions.
https://bugs.launchpad.net/inkscape/+bug/167937

* Inkscape uses labels on layers. Layers are basically named groups.
  Inkscape does not sync the group id with the layer label.

Usage Example:
python3 inkscape_svg_fixup.py vector_source.svg
'''

import lxml.etree as etree
import sys

def fix(tree):
  # know namespaces
  ns = {
	'svg' : 'http://www.w3.org/2000/svg',
	'inkscape' : 'http://www.inkscape.org/namespaces/inkscape'
  }

  # find document global style definition
  # mangle it and interpret as python dictionary
  style_element = tree.find('./svg:style',namespaces=ns)
  style_definitions = style_element.text
  pythonic_style_definitions = '{'+style_definitions.\
  replace('\t.','"').replace('{','":"').replace('}','",').\
  replace('/*','#')+'}'
  styles = eval(pythonic_style_definitions)

  # go through all SVG elements
  for elem in tree.iter():
	if (elem.tag == etree.QName(ns['svg'], 'g')):
	  # compare inkscape label with group element ID
	  l = elem.get(etree.QName(ns['inkscape'], 'label'))
	  if l:
		i = elem.get('id')
		if (i != l):
		  print("Overwriting ID %s with Label %s..."%(i, l))
		  elem.set('id', l)

	# clean styles (for easier manual merging)
	style_string = elem.get('style')
	if style_string:
	  split_styles = style_string.strip('; ').split(';')
	  styles_pairs = [s.strip('; ').split(':') for s in split_styles]
	  filtered_pairs = [ (k,v) for k,v in styles_pairs if not (
		k.startswith('font-') or
		k.startswith('text-') or
		k.endswith('-spacing') or
		k in ["line-height", " direction", " writing", " baseline-shift", " white-space", " writing-mode"]
	  )]
	  split_styles = [':'.join(p) for p in filtered_pairs]
	  style_string = ';'.join(sorted(split_styles))
	  elem.attrib["style"] = style_string

	# remove all style attributes offending class styles
	s = elem.get('style')
	c = elem.get('class')
	if (c and s):
	  s = s.lower()
	  c = c.split(' ')[0] # regard main style only
	  cs = ''
	  if c in styles:
		cs = styles[c].strip('; ').lower()
	  if (c not in styles):
		print("Object id %s references unknown style class %s."%(i,c))
	  else:
		if (cs != s.strip('; ')):
		  print("Style %s removed from object id %s differed from class %s style %s."%(s,i,c,cs))
		del elem.attrib["style"]

if __name__ == "__main__":
  input_file = sys.argv[1]
  tree = etree.parse(input_file)
  fix(tree)
  # store SVG into file (input file is overwritten)
  svg = etree.tostring(tree, pretty_print=True)
  with open(input_file, 'wb') as f:
	f.write('<?xml version="1.0" encoding="UTF-8" standalone="no"?>\n'.encode("utf-8"))
	f.write(svg)