|
|
|
|
local apiV, AB, MAJ, REV, ext, T = {}, {}, 2, 41, ...
|
|
|
|
|
if T.ActionBook then return end
|
|
|
|
|
apiV[MAJ], ext, T.Kindred = AB, {Kindred=T.Kindred}
|
|
|
|
|
|
|
|
|
|
local function assert(condition, err, ...)
|
|
|
|
|
return condition or error(tostring(err):format(...), 3)((0)[0])
|
|
|
|
|
end
|
|
|
|
|
local safequote do
|
|
|
|
|
local r = {u="\\117", ["{"]="\\123", ["}"]="\\125"}
|
|
|
|
|
function safequote(s)
|
|
|
|
|
return (("%q"):format(s):gsub("[{}u]", r))
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
local listToString do
|
|
|
|
|
local function walk(p, n, a, ...)
|
|
|
|
|
if n == 0 then
|
|
|
|
|
return p
|
|
|
|
|
end
|
|
|
|
|
local at, c = type(a), (p == "" and "" or ", ")
|
|
|
|
|
if at == "string" then
|
|
|
|
|
p = p .. c .. safequote(a)
|
|
|
|
|
elseif at == "number" then
|
|
|
|
|
p = p .. c .. ("%g"):format(a)
|
|
|
|
|
else
|
|
|
|
|
-- RE unpack does not accept start/finish indices, so replace nil/invalid values by a special table.
|
|
|
|
|
p = p .. c .. (at == "boolean" and (a and "true" or "false") or "_NIL")
|
|
|
|
|
end
|
|
|
|
|
return walk(p, n-1, ...)
|
|
|
|
|
end
|
|
|
|
|
function listToString(...)
|
|
|
|
|
return walk('', select("#", ...), ...)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
local istore do
|
|
|
|
|
local function write(t, n, i, a, b, ...) -- overwrites by up to 1 element
|
|
|
|
|
if n > 0 then
|
|
|
|
|
t[i], t[i+1] = a, b
|
|
|
|
|
return write(t, n-2, i+2, ...)
|
|
|
|
|
end
|
|
|
|
|
return i+n-1
|
|
|
|
|
end
|
|
|
|
|
local base, api, inner = newproxy(true), {}, setmetatable({}, {__mode="k"}), {}
|
|
|
|
|
function istore()
|
|
|
|
|
local r, d = newproxy(base), {[0]=0}
|
|
|
|
|
inner[r] = d
|
|
|
|
|
return r
|
|
|
|
|
end
|
|
|
|
|
function api:reset()
|
|
|
|
|
local h = inner[self]
|
|
|
|
|
h[0] = 0, wipe(h)
|
|
|
|
|
end
|
|
|
|
|
function api:insert(...)
|
|
|
|
|
local h, n = inner[self], select("#", ...)
|
|
|
|
|
if n > 0 then
|
|
|
|
|
local c = h[0]
|
|
|
|
|
h[0], h[-c-1] = c+1, write(h, n, h[-c]+1, ...)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
function api:filter(func, farg)
|
|
|
|
|
local h, c, ni, first = inner[self], 0, 0, 0
|
|
|
|
|
for i=1, h[0] do
|
|
|
|
|
local last = h[-i]
|
|
|
|
|
if securecall(func, farg, unpack(h, first+1, last)) then
|
|
|
|
|
c, h[-c-1] = c + 1, ni + last-first
|
|
|
|
|
for j=1,last == h[-c] and 0 or (last-first) do
|
|
|
|
|
h[ni+j] = h[first+j]
|
|
|
|
|
end
|
|
|
|
|
ni = h[-c]
|
|
|
|
|
end
|
|
|
|
|
first = last
|
|
|
|
|
end
|
|
|
|
|
h[0] = c
|
|
|
|
|
end
|
|
|
|
|
function api:size()
|
|
|
|
|
return inner[self][0]
|
|
|
|
|
end
|
|
|
|
|
function api:get(i)
|
|
|
|
|
local h = inner[self]
|
|
|
|
|
if i > 0 and h[-i] then
|
|
|
|
|
return unpack(h, (i == 1 and 0 or h[-i+1]) + 1, h[-i])
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
local meta = getmetatable(base)
|
|
|
|
|
meta.__index, meta.__len, meta.__call = api, api.size, api.get
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local L do
|
|
|
|
|
local LT, LR = {}, newproxy(true)
|
|
|
|
|
local function lookup(_, k, t)
|
|
|
|
|
return LT[k] or t or k
|
|
|
|
|
end
|
|
|
|
|
local LM = getmetatable(LR)
|
|
|
|
|
LM.__index, LM.__call, LM.__metatable = lookup, lookup, false
|
|
|
|
|
T.ActionBook = {L=LR, LW=LT}
|
|
|
|
|
L = T.ActionBook.L
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local actionCallbacks, core, coreEnv = {}, CreateFrame("Frame", nil, nil, "SecureHandlerBaseTemplate") do
|
|
|
|
|
core:SetFrameRef("KR", ext.Kindred:compatible(1,0):seclib())
|
|
|
|
|
local function uniqueName(s)
|
|
|
|
|
local bni, bn = 1 repeat
|
|
|
|
|
bn, bni = "AB!" .. bni .. s, bni + 1
|
|
|
|
|
until GetClickFrame(bn) == nil
|
|
|
|
|
return bn
|
|
|
|
|
end
|
|
|
|
|
for s in ("0123456789QWERTYUIOP"):gmatch(".") do
|
|
|
|
|
local b = CreateFrame("Button", uniqueName(s), core, "SecureActionButtonTemplate")
|
|
|
|
|
b:SetAttribute("pressAndHoldAction", 1)
|
|
|
|
|
core:WrapScript(b, "OnClick",
|
|
|
|
|
[[-- AB:OnClick_Pre
|
|
|
|
|
local slot = tonumber(button)
|
|
|
|
|
local at = actInfo[slot]
|
|
|
|
|
local at1 = type(at) == "table" and at[1]
|
|
|
|
|
if at == "icall" then
|
|
|
|
|
owner:CallMethod("icall", slot)
|
|
|
|
|
elseif at1 == "attribute" then
|
|
|
|
|
busy[self], idle[self] = at
|
|
|
|
|
for i=3, 2+at[2], 2 do
|
|
|
|
|
self:SetAttribute(at[i], at[i+1])
|
|
|
|
|
end
|
|
|
|
|
return nil, at1
|
|
|
|
|
elseif at1 == "recall" then
|
|
|
|
|
busy[self], idle[self] = at
|
|
|
|
|
local m = at[2]:RunAttribute(select(3, unpack(at)))
|
|
|
|
|
if type(m) == "string" and m ~= "" then
|
|
|
|
|
self:SetAttribute("type", "macro")
|
|
|
|
|
self:SetAttribute("macrotext", m)
|
|
|
|
|
return nil, "rem"
|
|
|
|
|
end
|
|
|
|
|
idle[self], busy[self] = self:GetName()
|
|
|
|
|
end
|
|
|
|
|
return false
|
|
|
|
|
]], [[-- AB:OnClick_Post
|
|
|
|
|
local at = busy[self]
|
|
|
|
|
idle[self], busy[self] = self:GetName()
|
|
|
|
|
if message == "attribute" then
|
|
|
|
|
for i=3, 2+at[2], 2 do
|
|
|
|
|
self:SetAttribute(at[i], nil)
|
|
|
|
|
end
|
|
|
|
|
elseif message == "rem" then
|
|
|
|
|
self:SetAttribute("type", nil)
|
|
|
|
|
self:SetAttribute("macrotext", nil)
|
|
|
|
|
end
|
|
|
|
|
]])
|
|
|
|
|
end
|
|
|
|
|
core:SetAttribute("LM", uniqueName("LM"))
|
|
|
|
|
core:WrapScript(CreateFrame("Button", core:GetAttribute("LM"), nil, "SecureFrameTemplate"), "OnClick", [=[-- AB_LockMan:OnClick
|
|
|
|
|
local d, lockID, v = button:match("^([lu])(%d+)")
|
|
|
|
|
lockID = lockID and (lockID + 0)
|
|
|
|
|
local v = (d == "l" and cndLockMap or cndLockRes)[lockID]
|
|
|
|
|
if d == "l" and v then
|
|
|
|
|
local ov = KR:GetAttribute("cndLock")
|
|
|
|
|
KR:SetAttribute("cndLock", v)
|
|
|
|
|
cndLockRes[lockID], cndLockMap[lockID] = ov == nil and _NIL or ov, nil
|
|
|
|
|
elseif d == "u" and v then
|
|
|
|
|
if v == _NIL then v = nil end
|
|
|
|
|
KR:SetAttribute("cndLock", v)
|
|
|
|
|
cndLockCount, cndLockRes[lockID] = cndLockCount - 1, nil
|
|
|
|
|
end
|
|
|
|
|
]=])
|
|
|
|
|
core:Execute([=[-- AB_Init
|
|
|
|
|
collections, tokens, metadata, actConditionals, tokConditionals = newtable(), newtable(), newtable(), newtable(), newtable()
|
|
|
|
|
actInfo, busy, idle, _NIL, sidCastID = newtable(), newtable(), newtable(), newtable(), 30 + math.random(9)
|
|
|
|
|
actInfo[sidCastID] = newtable("attribute", 6, "spell",nil, "unit",nil, "type","spell")
|
|
|
|
|
for _, c in pairs(self:GetChildList(newtable())) do idle[c] = c:GetName() end
|
|
|
|
|
colStack, idxStack, ecStack, etStack, onStack, outCount = newtable(), newtable(), newtable(), newtable(), newtable(), newtable()
|
|
|
|
|
cndLockNext, cndLockMap, cndLockRes, cndLockCount = 36000, newtable(), newtable(), 0
|
|
|
|
|
KR, LMname = self:GetFrameRef("KR"), self:GetAttribute("LM")
|
|
|
|
|
]=])
|
|
|
|
|
coreEnv = GetManagedEnvironment(core)
|
|
|
|
|
end
|
|
|
|
|
core:SetAttribute("GetCollectionContent", [[-- AB:GetCollectionContent(slot)
|
|
|
|
|
local i, ret, root, col, idx, aid, ecol = 1, "", tonumber((...)) or 0
|
|
|
|
|
wipe(outCount) wipe(onStack)
|
|
|
|
|
colStack[i], idxStack[i], ecStack[i], etStack[i] = root, 1, nil, nil
|
|
|
|
|
repeat
|
|
|
|
|
col, idx, ecol = colStack[i], idxStack[i], ecStack[i]
|
|
|
|
|
if idx == 1 and not outCount[col] then
|
|
|
|
|
if outCount[col] == nil then
|
|
|
|
|
self:CallMethod("notifyCollectionOpen", col)
|
|
|
|
|
end
|
|
|
|
|
if not ecol then
|
|
|
|
|
onStack[col], outCount[col] = true, 0
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
aid, idxStack[i] = collections[col][idx], idx + 1
|
|
|
|
|
if aid then
|
|
|
|
|
local tok = tokens[col][idx]
|
|
|
|
|
local check1, check2 = actConditionals[aid], tokConditionals[tok]
|
|
|
|
|
if (check1 == nil or KR:RunAttribute("EvaluateCmdOptions", check1) or "hide") ~= "hide" and
|
|
|
|
|
(check2 == nil or KR:RunAttribute("EvaluateCmdOptions", check2) or "hide") ~= "hide" then
|
|
|
|
|
local isCollection, etok = collections[aid], etStack[i]
|
|
|
|
|
local tem = isCollection and metadata["tokEmbed-" .. tok]
|
|
|
|
|
local isEmbed = isCollection and (tem == nil and metadata["embed-" .. aid] or tem)
|
|
|
|
|
if isEmbed then
|
|
|
|
|
local canEmbed = true
|
|
|
|
|
for j=1, i-1 do
|
|
|
|
|
if colStack[j] == aid then
|
|
|
|
|
canEmbed = false
|
|
|
|
|
break
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
if canEmbed then
|
|
|
|
|
i, colStack[i+1], idxStack[i+1], ecStack[i+1], etStack[i+1] = i + 1, aid, 1, ecol or col, ":" .. (etok and tok .. etok or tok)
|
|
|
|
|
end
|
|
|
|
|
elseif isCollection and not outCount[aid] then
|
|
|
|
|
i, idxStack[i], colStack[i+1], idxStack[i+1], ecStack[i+1], etStack[i+1] = i + 1, idx, aid, 1, false, nil
|
|
|
|
|
elseif (outCount[aid] or 1) > 0 or onStack[aid] then
|
|
|
|
|
local col = ecol or col
|
|
|
|
|
local nid = outCount[col] + 1
|
|
|
|
|
local pm = metadata["prMode-" .. tok] or 0
|
|
|
|
|
local tok = etok and tok .. etok or tok
|
|
|
|
|
ret = ret .. "\n" .. col .. " " .. nid .. " " .. aid .. " " .. tok .. " " .. pm
|
|
|
|
|
outCount[col] = nid
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
if not ecol then
|
|
|
|
|
onStack[col] = nil
|
|
|
|
|
local openAction = (i == 1 or (outCount[col] or 0) > 0) and metadata["openAction-" .. col]
|
|
|
|
|
if openAction then
|
|
|
|
|
local openToken = metadata["openToken-" .. col] or ("AOOA::" .. col)
|
|
|
|
|
ret = ret .. "\n" .. col .. " 0 " .. openAction .. " " .. openToken .. " 0"
|
|
|
|
|
if collections[openAction] and not outCount[openAction] then
|
|
|
|
|
i, colStack[i], idxStack[i], ecStack[i], etStack[i] = i + 1, openAction, 1, false, nil
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
i = i - 1
|
|
|
|
|
end
|
|
|
|
|
until i == 0
|
|
|
|
|
return ret, metadata["openAction-" .. root] -- 2nd return is deprecated; use idx 0 actions in ret
|
|
|
|
|
]])
|
|
|
|
|
core:SetAttribute("UseAction", [[-- AB:UseAction(slot[, "cndLock"])
|
|
|
|
|
local slot, cndLock = ...
|
|
|
|
|
local at, lockID = actInfo[slot]
|
|
|
|
|
if not at then return "" end
|
|
|
|
|
if type(cndLock) == "string" then
|
|
|
|
|
lockID, cndLockCount, cndLockNext = cndLockNext, cndLockCount + 1, (cndLockNext % 1e9) + 1
|
|
|
|
|
cndLockMap[lockID], cndLockRes[lockID] = cndLock, nil
|
|
|
|
|
end
|
|
|
|
|
local _, name = next(idle)
|
|
|
|
|
return (lockID and "/click %3$s l%4$d\n/click %1$s %2$d 1\n/click %3$s u%4$d" or "/click %s %d 1"):format(name, slot, LMname, lockID)
|
|
|
|
|
]])
|
|
|
|
|
core:SetAttribute("CastSpellByID", [[-- AB:CastSpellByID(sid[, "target"])
|
|
|
|
|
local at = actInfo[sidCastID]
|
|
|
|
|
at[4], at[6] = ...
|
|
|
|
|
return self:RunAttribute("UseAction", sidCastID)
|
|
|
|
|
]])
|
|
|
|
|
core:SetAttribute("ComputeModifierLock", [[-- DEPRECATED[2212/Y5]: AB:ComputeModifierLock
|
|
|
|
|
local mod, lock = ...
|
|
|
|
|
local lock, modState = KR:RunAttribute("ComputeConditionalLock", mod, false, lock)
|
|
|
|
|
return modState, lock
|
|
|
|
|
]])
|
|
|
|
|
|
|
|
|
|
function core:notifyCollectionOpen(id)
|
|
|
|
|
AB:NotifyObservers("internal.collection.preopen", id)
|
|
|
|
|
end
|
|
|
|
|
function core:icall(id)
|
|
|
|
|
local t = actionCallbacks[id]
|
|
|
|
|
local ttype = type(t)
|
|
|
|
|
if ttype == "table" then
|
|
|
|
|
t[1](unpack(t,3,t[2]))
|
|
|
|
|
elseif ttype == "function" then
|
|
|
|
|
t()
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
local DeferExecute do
|
|
|
|
|
local q = {}
|
|
|
|
|
function DeferExecute(block)
|
|
|
|
|
if InCombatLockdown() then
|
|
|
|
|
q[#q+1] = block
|
|
|
|
|
elseif type(block) == "function" then
|
|
|
|
|
securecall(block)
|
|
|
|
|
else
|
|
|
|
|
core:Execute(block)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
core:SetScript("OnEvent", function()
|
|
|
|
|
for i=1,#q do
|
|
|
|
|
q[i] = DeferExecute(q[i])
|
|
|
|
|
end
|
|
|
|
|
end)
|
|
|
|
|
core:RegisterEvent("PLAYER_REGEN_ENABLED")
|
|
|
|
|
end
|
|
|
|
|
local checkEntryToken, reserveEntryToken do
|
|
|
|
|
local etCollection, etIndex = {}, {}
|
|
|
|
|
local rtKey, rtNS = {}, {}
|
|
|
|
|
function checkEntryToken(tok, colId, newIdx)
|
|
|
|
|
local c, r = etCollection[tok]
|
|
|
|
|
if c and c ~= colId then
|
|
|
|
|
return false
|
|
|
|
|
end
|
|
|
|
|
r, etCollection[tok], etIndex[tok] = etIndex[tok], colId, newIdx
|
|
|
|
|
return r or newIdx
|
|
|
|
|
end
|
|
|
|
|
function reserveEntryToken(tok, colId, ns, key)
|
|
|
|
|
local c = etCollection[tok]
|
|
|
|
|
if c then
|
|
|
|
|
return c == colId
|
|
|
|
|
end
|
|
|
|
|
local rk, rn = rtKey[tok], rtNS[tok]
|
|
|
|
|
if rk == nil then
|
|
|
|
|
rtKey[tok], rtNS[tok] = key, ns
|
|
|
|
|
return true
|
|
|
|
|
end
|
|
|
|
|
return rk == key and rn == ns
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local actionCreators, actionDescribers, actionFlags, actionNumArgs, categories = {}, {}, {}, {}, {}
|
|
|
|
|
local nextActionId, allocatedActions, allocatedActionType, allocatedActionArg = 42, {}, {}, {}
|
|
|
|
|
|
|
|
|
|
local createHandlers, updateHandlers = {}, {}
|
|
|
|
|
function createHandlers.attribute(id, cnt, ...)
|
|
|
|
|
DeferExecute(("actInfo[%d] = newtable(%s)"):format(id, listToString("attribute", cnt, ...)))
|
|
|
|
|
return true
|
|
|
|
|
end
|
|
|
|
|
function createHandlers.func(id, cnt, func, ...)
|
|
|
|
|
if type(func) ~= "function" then
|
|
|
|
|
return false, "Callback expected, got %s", type(func)
|
|
|
|
|
end
|
|
|
|
|
DeferExecute(("actInfo[%d] = 'icall'"):format(id))
|
|
|
|
|
actionCallbacks[id] = cnt > 1 and {func, cnt+1, ...} or func
|
|
|
|
|
return true
|
|
|
|
|
end
|
|
|
|
|
function createHandlers.recall(id, _count, handle, attr, ...)
|
|
|
|
|
if type(handle) ~= "table" or type(attr) ~= "string" then
|
|
|
|
|
return false, "Frame handle, attribute method expected; got %s/%s", type(handle), type(attr)
|
|
|
|
|
elseif type(handle.IsProtected) ~= "function" or type(handle.GetAttribute) ~= "function" then
|
|
|
|
|
return false, "Frame handle expected; got %s", type(handle)
|
|
|
|
|
elseif not select(2, handle:IsProtected()) then
|
|
|
|
|
return false, "Callback frame must be explicitly protected"
|
|
|
|
|
elseif type(handle:GetAttribute(attr)) ~= "string" then
|
|
|
|
|
return false, "Callback snippet expected in attribute %q, got %s", attr, type(handle:GetAttribute(attr))
|
|
|
|
|
end
|
|
|
|
|
local s = ("local t = newtable(%s)\n t[2] = self:GetFrameRef('recall-ref')\nactInfo[%d] = t"):format(listToString("recall", nil, attr, ...), id)
|
|
|
|
|
DeferExecute(function()
|
|
|
|
|
core:SetFrameRef('recall-ref', handle)
|
|
|
|
|
core:Execute(s)
|
|
|
|
|
end)
|
|
|
|
|
return true
|
|
|
|
|
end
|
|
|
|
|
function updateHandlers.collection(id, _count, idList)
|
|
|
|
|
if not (type(idList) == "table") then
|
|
|
|
|
return false, "Expected table specifying collection actions, got %q", type(idList)
|
|
|
|
|
elseif idList.__openAction and not allocatedActions[idList.__openAction] then
|
|
|
|
|
return false, "Collection __openAction key does not specify a valid action"
|
|
|
|
|
end
|
|
|
|
|
local spec, tokens, tail = "", "", ""
|
|
|
|
|
for i=1,#idList do
|
|
|
|
|
local tok = idList[i]
|
|
|
|
|
if type(tok) ~= "string" then
|
|
|
|
|
return false, "Collection entry #%d: unsupported entry token type (%s)", i, type(tok)
|
|
|
|
|
end
|
|
|
|
|
local aid, vis, emb, pmode = idList[tok], idList['__visibility-' .. tok], idList['__embed-' .. tok], idList['__pmode-' .. tok]
|
|
|
|
|
if not tok:match("^[A-Za-z][A-Za-z0-9_=/]*$") then
|
|
|
|
|
return false, "Collection entry #%d: improper entry token format (%s)", i, tok
|
|
|
|
|
elseif allocatedActions[aid] == nil then
|
|
|
|
|
return false, "Collection entry #%d: unallocated action id (%s:%s)", i, type(idList[i]), tostring(idList[i])
|
|
|
|
|
elseif vis ~= nil and type(vis) ~= "string" then
|
|
|
|
|
return false, "Collection entry #%d: improper visibility conditional type (%s)", i, type(vis)
|
|
|
|
|
elseif emb ~= nil and type(emb) ~= "boolean" then
|
|
|
|
|
return false, "Collection entry #%d: improper embed flag type (%s)", i, type(emb)
|
|
|
|
|
elseif pmode ~= nil and (type(pmode) ~= "number" or pmode % 1 > 0 or pmode < 0) then
|
|
|
|
|
return false, "Collection entry #%d: improper presentation mode type"
|
|
|
|
|
end
|
|
|
|
|
local uqi = checkEntryToken(tok, id, i)
|
|
|
|
|
if not uqi then
|
|
|
|
|
return false, "Collection entry #%d: entry token not globally unique (%s)", i, tok
|
|
|
|
|
elseif uqi ~= i and idList[uqi] == tok then
|
|
|
|
|
return false, "Collection entry #%d: entry token not locally unique (%s)", i, tok
|
|
|
|
|
end
|
|
|
|
|
vis = vis and safequote(vis) or "nil"
|
|
|
|
|
emb = emb == nil and "nil" or tostring(emb)
|
|
|
|
|
pmode = pmode and safequote(("%.0f"):format(pmode + 1 - pmode % 2)) or "nil"
|
|
|
|
|
local qtok = safequote(tok)
|
|
|
|
|
tail = ('%s\ntk = %s; tokConditionals[tk], metadata["tokEmbed-" .. tk], metadata["prMode-" .. tk] = %s, %s, %s'):format(tail, qtok, vis, emb, pmode)
|
|
|
|
|
if i <= 240 then
|
|
|
|
|
spec, tokens = spec .. aid .. ", ", tokens .. qtok .. ", "
|
|
|
|
|
else
|
|
|
|
|
tail = ('%s\nnc[%d], nt[%2$d] = %d, tk'):format(tail, i, aid)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
local openAction = type(idList.__openAction) == "number" and idList.__openAction or "nil"
|
|
|
|
|
local openToken = idList.__openToken
|
|
|
|
|
openToken = type(openToken) == "string" and openToken:match("^[A-Za-z][A-Za-z0-9_=/]*$") and safequote(openToken) or "nil"
|
|
|
|
|
local embed = type(idList.__embed) == "boolean" and tostring(idList.__embed) or "nil"
|
|
|
|
|
DeferExecute(([[-- AB_updateCollectionHandler
|
|
|
|
|
local id, nc, nt, tk = %d, newtable(%s nil), newtable(%s nil)
|
|
|
|
|
collections[id], tokens[id], metadata['openAction-' .. id], metadata['embed-' .. id], metadata['openToken-' .. id] = nc, nt, %s, %s, %s;
|
|
|
|
|
%s]]):format(id, spec, tokens, openAction, embed, openToken, tail))
|
|
|
|
|
return true
|
|
|
|
|
end
|
|
|
|
|
function createHandlers.clone(id, _count, id2)
|
|
|
|
|
DeferExecute(("local d,s = %d, %d; actInfo[d], actConditionals[d] = actInfo[s], actConditionals[s]"):format(id, id2))
|
|
|
|
|
return true
|
|
|
|
|
end
|
|
|
|
|
local function nullInfoFunc() return false end
|
|
|
|
|
|
|
|
|
|
local getActionIdent, getActionArgs do
|
|
|
|
|
function getActionIdent(identTable)
|
|
|
|
|
local itt = type(identTable)
|
|
|
|
|
if itt == "string" then
|
|
|
|
|
return identTable, nil
|
|
|
|
|
elseif itt == "table" and type(identTable[1]) == "string" then
|
|
|
|
|
return identTable[1], identTable
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
local function private_unpack(t, a, b)
|
|
|
|
|
if a <= b then
|
|
|
|
|
return t[a], private_unpack(t, a+1, b)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
function getActionArgs(it, ...)
|
|
|
|
|
if it then
|
|
|
|
|
return private_unpack(it, 2, 1+(actionNumArgs[it[1]] or 1))
|
|
|
|
|
end
|
|
|
|
|
return ...
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
local categoryAliases = {}
|
|
|
|
|
local function getCategoryName(id)
|
|
|
|
|
local LM = id == (#categories+1) and L"Miscellaneous"
|
|
|
|
|
return categories[id] or (LM and categories[LM] and LM) or nil
|
|
|
|
|
end
|
|
|
|
|
local function getCategoryTable(name)
|
|
|
|
|
name = categoryAliases[name] or name
|
|
|
|
|
local r = categories[name]
|
|
|
|
|
if not r then
|
|
|
|
|
r = {}
|
|
|
|
|
categories[name] = r
|
|
|
|
|
if name ~= L"Miscellaneous" then
|
|
|
|
|
categories[#categories + 1] = name
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
return r
|
|
|
|
|
end
|
|
|
|
|
local function getActionDescription(q, ident, at, ...)
|
|
|
|
|
local df = actionDescribers[ident]
|
|
|
|
|
if df == nil then
|
|
|
|
|
elseif actionFlags[ident] == 1 then
|
|
|
|
|
return df(q, getActionArgs(at, ...))
|
|
|
|
|
else
|
|
|
|
|
return df(getActionArgs(at, ...))
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local editorPanels, createEditorHost = {} do
|
|
|
|
|
local copyActionKeys, clearActionKeys do
|
|
|
|
|
local function copy(t, copies)
|
|
|
|
|
local into = {}
|
|
|
|
|
copies = copies or {}
|
|
|
|
|
copies[t] = into
|
|
|
|
|
for k,v in pairs(t) do
|
|
|
|
|
k = type(k) == "table" and (copies[k] or copy(k, copies)) or k
|
|
|
|
|
v = type(v) == "table" and (copies[v] or copy(v, copies)) or v
|
|
|
|
|
into[k] = v
|
|
|
|
|
end
|
|
|
|
|
return into, copies
|
|
|
|
|
end
|
|
|
|
|
local function copyKeys(src, dst, lib, n, k, ...)
|
|
|
|
|
if n == 0 then return dst end
|
|
|
|
|
local v = src and src[k]
|
|
|
|
|
if lib and lib[v] ~= nil then
|
|
|
|
|
v = lib[v]
|
|
|
|
|
elseif type(v) == "table" then
|
|
|
|
|
v, lib = copy(v, lib)
|
|
|
|
|
end
|
|
|
|
|
dst[k] = v
|
|
|
|
|
return copyKeys(src, dst, lib, n-1, ...)
|
|
|
|
|
end
|
|
|
|
|
local function prefixCount(...)
|
|
|
|
|
return select("#", ...), ...
|
|
|
|
|
end
|
|
|
|
|
local function allActionKeys(src, ak)
|
|
|
|
|
local nk = next(src, ak)
|
|
|
|
|
if nk == nil then
|
|
|
|
|
return
|
|
|
|
|
elseif type(nk) == "number" then
|
|
|
|
|
return nk, allActionKeys(src, nk)
|
|
|
|
|
end
|
|
|
|
|
return allActionKeys(src, nk)
|
|
|
|
|
end
|
|
|
|
|
function copyActionKeys(src, dst)
|
|
|
|
|
return copyKeys(src, dst, nil, prefixCount(allActionKeys(src)))
|
|
|
|
|
end
|
|
|
|
|
function clearActionKeys(dst)
|
|
|
|
|
copyKeys(nil, dst, nil, prefixCount(allActionKeys(dst)))
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
local hdata, hhproto = {}, newproxy(true) do
|
|
|
|
|
local hhapi, m = {}, getmetatable(hhproto)
|
|
|
|
|
m.__index, m.__metatable = hhapi, false
|
|
|
|
|
local function pmcall(s, m, ...)
|
|
|
|
|
return true, s[m](s, ...)
|
|
|
|
|
end
|
|
|
|
|
function hhapi:SetAction(actionTable)
|
|
|
|
|
assert(type(actionTable) == "table", 'Syntax: ok = hh:SetAction(actionTable)')
|
|
|
|
|
local d, ed = hdata[self], editorPanels[actionTable[1]]
|
|
|
|
|
if not ed then return end
|
|
|
|
|
local oed, oaction = d.editor, d.action
|
|
|
|
|
d.editor, d.action = nil
|
|
|
|
|
d.rq:Hide()
|
|
|
|
|
if oed and oed ~= ed then
|
|
|
|
|
securecall(oed.Release, oed, d.host)
|
|
|
|
|
end
|
|
|
|
|
--[[ There's a fair bit of copying involved here to avoid sharing table
|
|
|
|
|
references between the host and the editor panel. This serves to
|
|
|
|
|
protect both parties from each other. The host's copy cannot be
|
|
|
|
|
modified by the editor except when the host explicitly requests that
|
|
|
|
|
via :GetAction (limited to the action's option keys!). The copy the
|
|
|
|
|
editor panel receives is in principle solely its own, although other
|
|
|
|
|
code could call SetAction directly (with arbitrary arguments). ]]
|
|
|
|
|
local edCopy = copyActionKeys(actionTable, {})
|
|
|
|
|
if not securecall(pmcall, ed, "SetAction", d.host, edCopy) then
|
|
|
|
|
securecall(ed.Release, ed, d.host)
|
|
|
|
|
return false
|
|
|
|
|
end
|
|
|
|
|
d.editor = ed
|
|
|
|
|
--[[ Keep a separate copy in case the editor is stolen from the host without
|
|
|
|
|
a :OnEditorRelease notification. When reacquiring the editor panel, it
|
|
|
|
|
is safe to keep the copy we're restoring from -- barring shenanigans,
|
|
|
|
|
it is immutable. ]]
|
|
|
|
|
d.action = oaction == actionTable and oaction or copyActionKeys(actionTable, {})
|
|
|
|
|
return true
|
|
|
|
|
end
|
|
|
|
|
function hhapi:GetAction(into)
|
|
|
|
|
assert(type(into) == "table", 'Syntax: intoTable? = hh:GetAction(intoTable)')
|
|
|
|
|
local d, src = hdata[self]
|
|
|
|
|
if d.editor and d.editor:IsOwned(d.host) then
|
|
|
|
|
src = {}
|
|
|
|
|
d.editor:GetAction(src)
|
|
|
|
|
d.action = copyActionKeys(src, {})
|
|
|
|
|
elseif d.action then
|
|
|
|
|
src = d.action
|
|
|
|
|
end
|
|
|
|
|
if src and actionCreators[src[1]] then
|
|
|
|
|
clearActionKeys(into)
|
|
|
|
|
copyActionKeys(src, into)
|
|
|
|
|
return into
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
function hhapi:Clear()
|
|
|
|
|
local d = hdata[self]
|
|
|
|
|
local ed = d.editor
|
|
|
|
|
d.editor, d.action = nil
|
|
|
|
|
d.rq:Hide()
|
|
|
|
|
if ed then
|
|
|
|
|
securecall(ed.Release, ed, d.host)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
function hhapi:IsCurrentEditor(ed)
|
|
|
|
|
local d = hdata[self]
|
|
|
|
|
return ed and ed == d.editor and ed:IsOwned(d.host) and true or false
|
|
|
|
|
end
|
|
|
|
|
function hhapi:GetTabFocusWidget(which)
|
|
|
|
|
local d = hdata[self]
|
|
|
|
|
local ed = d.editor
|
|
|
|
|
return ed and type(ed.GetTabFocusWidget) == "function" and ed:IsOwned(d.host) and ed:GetTabFocusWidget(which) or nil
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
local function hf_OnEditorRelease(hf, ed)
|
|
|
|
|
local d = hdata[hf]
|
|
|
|
|
if ed and ed == d.editor then
|
|
|
|
|
local ac = {}
|
|
|
|
|
ed:GetAction(ac)
|
|
|
|
|
d.action = copyActionKeys(ac, {})
|
|
|
|
|
d.editor = nil
|
|
|
|
|
d.rq:Show()
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
local function hf_OnReaquireClick(self)
|
|
|
|
|
local d = hdata[self:GetParent()]
|
|
|
|
|
PlaySound(SOUNDKIT.U_CHAT_SCROLL_BUTTON)
|
|
|
|
|
if d.action then
|
|
|
|
|
d.hh:SetAction(d.action)
|
|
|
|
|
local w = d.hh:GetTabFocusWidget(0)
|
|
|
|
|
if w then
|
|
|
|
|
w:SetFocus()
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
function createEditorHost(parent)
|
|
|
|
|
local hf = CreateFrame("Frame", nil, parent) do
|
|
|
|
|
hf.OnEditorRelease = hf_OnEditorRelease
|
|
|
|
|
end
|
|
|
|
|
local rq = CreateFrame("Button", nil, hf, "UIPanelButtonTemplate") do
|
|
|
|
|
rq:Hide()
|
|
|
|
|
rq:SetSize(30, 26)
|
|
|
|
|
rq:SetPoint("CENTER")
|
|
|
|
|
rq:SetText("|A:UI-RefreshButton:0:0|a")
|
|
|
|
|
rq:SetScript("OnClick", hf_OnReaquireClick)
|
|
|
|
|
end
|
|
|
|
|
local hd, hh = {host=hf, hh=0, rq=rq}, newproxy(hhproto)
|
|
|
|
|
hd.hh = hh
|
|
|
|
|
hdata[hh], hdata[hf] = hd, hd
|
|
|
|
|
return hf, hh
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
do -- AB:CreateToken()
|
|
|
|
|
local seq, dict, dictLength, prefix = 262143, "qwer1tyui2opas3dfgh4jklz5xcvb6nmQWE7RTYU8IOPA9SDFG0HJKL=ZXCV/BNM", 64
|
|
|
|
|
local function encode(n)
|
|
|
|
|
local ret, d = ""
|
|
|
|
|
repeat
|
|
|
|
|
d = n % dictLength
|
|
|
|
|
ret, n = dict:sub(d+1, d+1) .. ret, (n - d) / dictLength
|
|
|
|
|
until n == 0
|
|
|
|
|
return ret
|
|
|
|
|
end
|
|
|
|
|
function AB:CreateToken()
|
|
|
|
|
if seq > 262142 then
|
|
|
|
|
prefix, seq = "ABu" .. encode(time()*100+(math.floor(GetTime()*100)%100)), 0
|
|
|
|
|
end
|
|
|
|
|
seq = seq + 1
|
|
|
|
|
return prefix .. encode(seq)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
function AB:ReserveToken(token, ns, key, collectionID)
|
|
|
|
|
assert(key ~= nil and (collectionID == nil or type(collectionID) == "number"), 'Syntax: ok = ActionBook:ReserveToken("token", ns, key, collectionID or nil)')
|
|
|
|
|
if type(token) ~= "string" or not token:match("^[A-Za-z][A-Za-z0-9_=/]*$") then
|
|
|
|
|
return false, 'unacceptable-token'
|
|
|
|
|
end
|
|
|
|
|
return reserveEntryToken(token, collectionID, ns, key)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function AB:GetActionSlot(actionType, ...)
|
|
|
|
|
local ident, at = getActionIdent(actionType)
|
|
|
|
|
local id = actionCreators[ident] and actionCreators[ident](getActionArgs(at, ...))
|
|
|
|
|
if allocatedActions[id] then
|
|
|
|
|
return id
|
|
|
|
|
end
|
|
|
|
|
assert(ident, 'Syntax: actionId = ActionBook:GetActionSlot(actionTable or "actionType", ...)')
|
|
|
|
|
end
|
|
|
|
|
function AB:GetActionDescription(actionType, ...)
|
|
|
|
|
local ident, at = getActionIdent(actionType)
|
|
|
|
|
assert(ident, 'Syntax: typeName, actionName, icon, ext, tipFunc, tipArg, actionType = ActionBook:GetActionDescription(actionTable or "actionType", ...)')
|
|
|
|
|
return getActionDescription(nil, ident, at, ...)
|
|
|
|
|
end
|
|
|
|
|
function AB:GetActionNumArgs(actionType)
|
|
|
|
|
local ident = getActionIdent(actionType) or error('Syntax: numArgs? = ActionBook:GetActionNumArgs(actionTable or "actionType")', 2)
|
|
|
|
|
return actionNumArgs[ident]
|
|
|
|
|
end
|
|
|
|
|
function AB:GetActionListDescription(actionType, ...)
|
|
|
|
|
local ident, at = getActionIdent(actionType)
|
|
|
|
|
assert(ident, 'Syntax: typeName, actionName, icon, ext, tipFunc, tipArg, actionType = ActionBook:GetActionListDescription(actionTable or "actionType", ...)')
|
|
|
|
|
return getActionDescription("list-query", ident, at, ...)
|
|
|
|
|
end
|
|
|
|
|
function AB:GetSlotInfo(id, cndLock)
|
|
|
|
|
assert(type(id) == "number" and (cndLock == nil or type(cndLock) == "string"), 'Syntax: usable, state, icon, caption, count, cdLeft, cdLength, tipFunc, tipArg, ext = ActionBook:GetSlotInfo(slot, "cndLockState"?)')
|
|
|
|
|
if allocatedActions[id] then
|
|
|
|
|
return allocatedActions[id](allocatedActionArg[id], cndLock)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
function AB:GetSlotImplementation(id)
|
|
|
|
|
assert(type(id) == "number", "Syntax: actionType, colEntryCount, colEmbedDefault = ActionBook:GetSlotImplementation(slot)")
|
|
|
|
|
local aType = allocatedActions[id] and allocatedActionType[id]
|
|
|
|
|
local colData = coreEnv.collections[id]
|
|
|
|
|
return aType, colData and #colData or nil, colData and coreEnv.metadata['embed-' .. id]
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function AB:RegisterActionType(actionType, create, describe, numArgs, useListDescribe)
|
|
|
|
|
assert(type(actionType) == "string" and type(create) == "function" and type(describe) == "function" and type(numArgs) == "number"
|
|
|
|
|
and (numArgs >= 0 and numArgs % 1 == 0)
|
|
|
|
|
and (useListDescribe == nil or type(useListDescribe) == "boolean")
|
|
|
|
|
, 'Syntax: ActionBook:RegisterActionType("actionType", createFunc, describeFunc, numArgs, useListDescribe?)')
|
|
|
|
|
assert(not actionCreators[actionType], "Identifier %q is already registered", actionType)
|
|
|
|
|
actionCreators[actionType], actionDescribers[actionType], actionFlags[actionType] = create, describe, useListDescribe and 1 or nil
|
|
|
|
|
actionNumArgs[actionType] = numArgs
|
|
|
|
|
end
|
|
|
|
|
function AB:CreateActionSlot(infoFunc, infoArg, implType, ...)
|
|
|
|
|
local as, an = 1, select("#", ...)
|
|
|
|
|
local cnd if implType == "conditional" then
|
|
|
|
|
as, an, cnd, implType = as + 2, an - 2, select(as, ...)
|
|
|
|
|
assert(type(cnd) == "string", "Conditional options expected, got %s", type(cnd))
|
|
|
|
|
end
|
|
|
|
|
assert(type(implType) == "string" and (infoFunc == nil or type(infoFunc) == "function"), 'Syntax: slot = ActionBook:CreateAction(infoFunc, infoArg, "implType", ...)')
|
|
|
|
|
local cf, id = assert(createHandlers[implType] or updateHandlers[implType], "Implementation type %q is not creatable", implType), nextActionId
|
|
|
|
|
nextActionId, allocatedActionType[id], allocatedActionArg[id] = id + 1, implType, infoArg
|
|
|
|
|
assert(cf(id, an, select(as, ...)))
|
|
|
|
|
allocatedActions[id] = infoFunc or nullInfoFunc
|
|
|
|
|
if cnd then DeferExecute(("actConditionals[%d] = %s"):format(id, safequote(cnd))) end
|
|
|
|
|
return id
|
|
|
|
|
end
|
|
|
|
|
function AB:UpdateActionSlot(id, ...)
|
|
|
|
|
assert(type(id) == "number", "Syntax: ActionBook:UpdateActionSlot(slot, ...)")
|
|
|
|
|
local at = assert(allocatedActionType[id], "Action %d does not exist", id)
|
|
|
|
|
local uf = assert(updateHandlers[at], "Action type %q is not updatable", at)
|
|
|
|
|
assert(uf(id, select("#", ...), ...))
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function AB:AddCategoryAlias(name, aliasOf)
|
|
|
|
|
assert(type(name) == "string" and type(aliasOf) == "string", 'Syntax: ActionBook:AddCategoryAlias("name", "aliasOf")')
|
|
|
|
|
assert(name == aliasOf or categories[name] == nil, "Category %q already exists.", name)
|
|
|
|
|
categoryAliases[name] = categoryAliases[name] or aliasOf
|
|
|
|
|
end
|
|
|
|
|
function AB:AugmentCategory(name, augFunc)
|
|
|
|
|
assert(type(name) == "string" and type(augFunc) == "function" and name ~= "*", 'Syntax: ActionBook:AugmentCategory("name", augFunc)')
|
|
|
|
|
table.insert(getCategoryTable(name), augFunc)
|
|
|
|
|
end
|
|
|
|
|
function AB:AddActionToCategory(name, actionType, ...)
|
|
|
|
|
assert(type(name) == "string" and type(actionType) == "string" and name ~= "*", 'Syntax: ActionBook:AddActionToCategory("name", "actionType"[, ...])')
|
|
|
|
|
local ct = getCategoryTable(name)
|
|
|
|
|
ct.extra = ct.extra or istore()
|
|
|
|
|
ct.extra:insert(actionType, ...)
|
|
|
|
|
end
|
|
|
|
|
function AB:GetNumCategories()
|
|
|
|
|
return #categories + (categories[L"Miscellaneous"] and 1 or 0)
|
|
|
|
|
end
|
|
|
|
|
function AB:GetCategoryInfo(id)
|
|
|
|
|
assert(type(id) == "number", 'Syntax: name = ActionBook:GetCategoryInfo(index)')
|
|
|
|
|
return getCategoryName(id)
|
|
|
|
|
end
|
|
|
|
|
function AB:GetCategoryContents(id, into)
|
|
|
|
|
assert(type(id) == "number", 'Syntax: contents = ActionBook:GetCategoryContents(index[, into])')
|
|
|
|
|
local cname, ret = assert(getCategoryName(id), 'Invalid category index'), into or istore()
|
|
|
|
|
local co = categories[cname]
|
|
|
|
|
local ex, addToRet = co.extra, #co > 0 and function(...) return ret:insert(...) end
|
|
|
|
|
for i=1,#co do
|
|
|
|
|
securecall(co[i], cname, addToRet)
|
|
|
|
|
end
|
|
|
|
|
for i=1,ex and #ex or 0 do
|
|
|
|
|
ret:insert(ex(i))
|
|
|
|
|
end
|
|
|
|
|
return ret
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local notifyCount, observers = 1, {["*"]={}, ["internal.collection.preopen"] = {}}
|
|
|
|
|
function AB:NotifyObservers(ident, data)
|
|
|
|
|
assert(type(ident) == "string", 'Syntax: ActionBook:NotifyObservers("identifier"[, data])')
|
|
|
|
|
assert(actionCreators[ident] or observers[ident] ~= nil, "Identifier %q is not registered", ident)
|
|
|
|
|
notifyCount = (notifyCount + 1) % 4503599627370495
|
|
|
|
|
for i=ident == "*" or not observers[ident] and 1 or 2, 1, -1 do
|
|
|
|
|
for k,v in pairs(observers[i == 1 and "*" or ident]) do
|
|
|
|
|
securecall(k, v, ident, data)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
function AB:AddObserver(ident, callback, selfarg)
|
|
|
|
|
assert(type(ident) == "string" and type(callback) == "function", 'Syntax: ActionBook:AddObserver("identifier", callbackFunc, callbackSelfArg)')
|
|
|
|
|
assert(ident == "*" or actionCreators[ident] or observers[ident], "Identifier %q is not registered", ident)
|
|
|
|
|
if observers[ident] == nil then observers[ident] = {} end
|
|
|
|
|
observers[ident][callback] = selfarg == nil and true or selfarg
|
|
|
|
|
end
|
|
|
|
|
function AB:GetLastObserverUpdateToken(ident)
|
|
|
|
|
assert(type(ident) == "string", 'Syntax: token = ActionBook:GetLastObserverUpdateToken("identifier")')
|
|
|
|
|
assert(ident == "*" or actionCreators[ident], "Identifier %q is not registered", ident)
|
|
|
|
|
return notifyCount
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function AB:RegisterEditorPanel(actionType, editorPanel)
|
|
|
|
|
assert(type(actionType) == "string" and type(editorPanel) == "table", 'Syntax: ActionBook:RegisterEditorPanel("actionType", editorPanel)')
|
|
|
|
|
assert(actionCreators[actionType] ~= nil, "actionType %q is not registered", actionType)
|
|
|
|
|
assert(editorPanels[actionType] == nil, "An editor for %q is already registered", actionType)
|
|
|
|
|
assert(
|
|
|
|
|
type(editorPanel.SetAction) == "function" and
|
|
|
|
|
type(editorPanel.GetAction) == "function" and
|
|
|
|
|
type(editorPanel.IsOwned) == "function" and
|
|
|
|
|
type(editorPanel.Release) == "function",
|
|
|
|
|
"Required editor panel API methods not implemented")
|
|
|
|
|
editorPanels[actionType] = editorPanel
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function AB:seclib()
|
|
|
|
|
return core
|
|
|
|
|
end
|
|
|
|
|
function AB:compatible(module, maj, rev, ...)
|
|
|
|
|
if ext[module] then
|
|
|
|
|
return ext[module]:compatible(maj, rev, ...)
|
|
|
|
|
elseif type(module) == "number" and type(maj) == "number" and rev == nil then
|
|
|
|
|
maj, rev = module, maj -- Oh hey, it's us!
|
|
|
|
|
return rev <= REV and apiV[maj], MAJ, REV
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
-- HIDDEN, UNSUPPORTED METHODS: May vanish at any time.
|
|
|
|
|
local hum = {L=L}
|
|
|
|
|
setmetatable(AB, {__index=hum})
|
|
|
|
|
hum.HUM = hum
|
|
|
|
|
function hum:CreateEditorHost(parent)
|
|
|
|
|
assert(type(parent) == "table" and parent[0], 'Syntax: hostWidget = ActionBook:CreateEditorHost(parent)')
|
|
|
|
|
return createEditorHost(parent)
|
|
|
|
|
end
|
|
|
|
|
function hum:RegisterModule(key, api)
|
|
|
|
|
assert(type(key) == "string" and type(api) == "table" and type(api.compatible) == "function", 'Syntax: ActionBook:RegisterModule("key", apiTable)')
|
|
|
|
|
assert(ext[key] == nil, 'Duplicate module registration')
|
|
|
|
|
ext[key] = api
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
T.ActionBook.compatible, ext.ActionBook = AB.compatible, T.ActionBook
|