You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

834 lines
31 KiB

5 months ago
local MAJ, REV, COMPAT, _, T = 2, 46, select(4,GetBuildInfo()), ...
if T.SkipLocalActionBook or T.ActionBook then return end
5 months ago
local EV, WR = T.Evie, T.Ware
assert(EV and WR and 1, 'Incompatible library bundle')
local apiV, AB, ext, RWsec = {}, {}, {Kindred=T.Kindred}, nil
apiV[MAJ], T.Kindred = AB
5 months ago
local PYROLYSIS = COMPAT ~= 40400
5 years ago
local function assert(condition, err, ...)
return condition or error(tostring(err):format(...), 3)((0)[0])
5 years ago
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
5 years ago
end
local LM = getmetatable(LR)
LM.__index, LM.__call, LM.__metatable = lookup, lookup, false
T.ActionBook = {L=LR, LW=LT}
L = T.ActionBook.L
5 years ago
end
5 months ago
local actionCallbacks, core = {}, CreateFrame("Frame", nil, nil, "SecureHandlerBaseTemplate")
local coreEnvW = WR.GetRestrictedEnvironment(core)
local coreEnv = WR.GetBackingRestrictedTable(coreEnvW) do
coreEnvW.KR = ext.Kindred:compatible(1,0):seclib()
core:SetAttribute("execID", 1e3)
local function uniqueName(s)
5 years ago
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]
owner:SetAttribute("execID", (tonumber((owner:GetAttribute("execID"))) or 0) % 2^42 + 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()
5 years ago
end
return false
]], [[-- AB:OnClick_Post
local at = busy[self]
5 years ago
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)
5 years ago
end
]])
end
5 months ago
coreEnvW.LMname = uniqueName("LM")
core:SetAttribute("PY", PYROLYSIS)
5 months ago
core:WrapScript(CreateFrame("Button", coreEnvW.LMname, 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
5 years ago
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
pendingNotify, nextNotifyId = newtable(), 45000
5 months ago
PY = self:GetAttribute("PY")
5 years ago
]=])
coreEnv = GetManagedEnvironment(core)
end
core:SetAttribute("GetCollectionContent", [[-- AB:GetCollectionContent(slot)
5 years ago
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
5 years ago
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]
5 years ago
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)
5 years ago
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
5 years ago
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
5 years ago
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
3 years ago
local openToken = metadata["openToken-" .. col] or ("AOOA::" .. col)
ret = ret .. "\n" .. col .. " 0 " .. openAction .. " " .. openToken .. " 0"
3 years ago
if collections[openAction] and not outCount[openAction] then
i, colStack[i], idxStack[i], ecStack[i], etStack[i] = i + 1, openAction, 1, false, nil
3 years ago
end
5 years ago
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, at1 = actInfo[slot]
if not at then return "" end
local at1, execIdMonitor = at[1], true
if at1 == "macrotext" then
return at[2], false
elseif type(cndLock) == "string" then
lockID, cndLockCount, cndLockNext = cndLockNext, cndLockCount + 1, (cndLockNext % 1e9) + 1
cndLockMap[lockID], cndLockRes[lockID] = cndLock, nil
end
if at1 == "recall" and PY then
if lockID then
cndLockMap[lockID], cndLockRes[lockID] = nil, KR:GetAttribute("cndLock")
KR:SetAttribute("cndLock", cndLock)
end
local m, notifyKey = at[2]:RunAttribute(select(3, unpack(at)))
if notifyKey then
pendingNotify[nextNotifyId], notifyKey, nextNotifyId = newtable(notifyKey, at[2]), nextNotifyId, (nextNotifyId % 2^24) + 1
end
if lockID then
KR:SetAttribute("cndLock", cndLockRes[lockID])
cndLockCount, cndLockRes[lockID] = cndLockCount - 1, nil
end
return type(m) == "string" and m ~= "" and m or nil, false, notifyKey
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), true
]])
core:SetAttribute("NotifyPostUse", [[-- AB:NotifyPostUse("token")
local ni = pendingNotify[...]
pendingNotify[... or 0] = nil
if ni[1] then
ni[2]:RunAttribute("OnNotifyPostUse", ni[1])
end
5 years ago
]])
core:SetAttribute("CastSpellByID", [[-- AB:CastSpellByID(sid[, "target"])
5 years ago
local at = actInfo[sidCastID]
at[4], at[6] = ...
return self:RunAttribute("UseAction", sidCastID)
]])
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
5 months ago
local UnlockedCall do
local q, qn = {}, 1
local function packcall(pack)
return pack[1](unpack(pack, 3, pack[2])) and false
5 years ago
end
5 months ago
function EV:PLAYER_REGEN_ENABLED()
local i = 1
while i < qn do
i, q[i] = i+1, securecall(packcall, q[i]) or nil
5 years ago
end
5 months ago
qn = 1
end
local function pack(f, ...)
qn, q[qn] = qn + 1, {f, 2+select("#", ...), ...}
end
function UnlockedCall(f, ...)
return ((InCombatLockdown() or qn > 1) and pack or securecall)(f, ...)
end
5 years ago
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
5 years ago
local actionCreators, actionDescribers, actionFlags, actionNumArgs, categories = {}, {}, {}, {}, {}
5 years ago
local nextActionId, allocatedActions, allocatedActionType, allocatedActionArg = 42, {}, {}, {}
local createHandlers, updateHandlers = {}, {}
function createHandlers.attribute(id, cnt, ...)
5 months ago
UnlockedCall(WR.newtable, coreEnvW.actInfo, id, "attribute", cnt, ...)
5 years ago
return true
end
5 months ago
local function re_set_actInfo_icall(id)
coreEnvW.actInfo[id] = "icall"
end
5 years ago
function createHandlers.func(id, cnt, func, ...)
if type(func) ~= "function" then
return false, "Callback expected, got %s", type(func)
end
5 months ago
UnlockedCall(re_set_actInfo_icall, id)
5 years ago
actionCallbacks[id] = cnt > 1 and {func, cnt+1, ...} or func
return true
end
function createHandlers.recall(id, _count, handle, attr, ...)
5 months ago
if not C_Widget.IsFrameWidget(handle) or type(attr) ~= "string" then
5 years ago
return false, "Frame handle, attribute method expected; got %s/%s", type(handle), type(attr)
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
5 months ago
UnlockedCall(WR.newtable, coreEnvW.actInfo, id, "recall", handle, attr, ...)
5 years ago
return true
end
function createHandlers.macrotext(id, _cnt, mt)
if type(mt) ~= "string" then
return false, "macrotext: string expected, got %s", type(mt)
end
5 months ago
UnlockedCall(WR.newtable, coreEnvW.actInfo, id, "macrotext", mt)
return true
end
function createHandlers.reslash(id, _cnt, slash, varg, target)
if type(slash) ~= "string" then
return false, "reslash: command string expected, got %s", type(slash)
elseif varg ~= nil and type(varg) ~= "string" then
return false, "reslash: message string expected, got %s", type(varg)
elseif target ~= nil and type(target) ~= "string" then
return false, "reslash: target string expected, got %s", type(target)
elseif not RWsec then
return false, "reslash: Rewire not available"
end
return createHandlers.recall(id, target and 5 or 4, RWsec, "RunSlashCmd", slash, varg or "", select(target and 1 or 2, target))
end
function createHandlers.retext(id, _cnt, retext)
if type(retext) ~= "string" then
return false, "retext: string expected, got %s", type(retext)
elseif not RWsec then
return false, "retext: Rewire not available"
end
return createHandlers.recall(id, 5, RWsec, "RunMacro", retext, false, true)
end
5 months ago
local function re_set_collection(id, pack)
local nc, nt = WR.newtable(coreEnvW.collections, id), WR.newtable(coreEnvW.tokens, id)
local m, v = coreEnvW.metadata, coreEnvW.tokConditionals
for i=1, #pack do
local tok = pack[i]
local vkey, ekey, pkey = '__visibility-' .. tok, '__embed-' .. tok, '__pmode-' .. tok
nt[i], nc[i], v[tok] = tok, pack[tok], pack[vkey]
m["tokEmbed-" .. tok], m["prMode-" .. tok] = pack[ekey], pack[pkey]
end
m['openAction-' .. id], m['embed-' .. id], m['openToken-' .. id] = pack.__openAction, pack.__embed, pack.__openToken
end
5 years ago
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
5 months ago
local pack = {}
5 years ago
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
5 months ago
local vkey, ekey, pkey = '__visibility-' .. tok, '__embed-' .. tok, '__pmode-' .. tok
local aid, vis, emb, pmode = idList[tok], idList[vkey], idList[ekey], idList[pkey]
if not tok:match("^[A-Za-z][A-Za-z0-9_=/]*$") then
return false, "Collection entry #%d: improper entry token format (%s)", i, tok
5 years ago
elseif allocatedActions[aid] == nil then
return false, "Collection entry #%d: unallocated action id (%s:%s)", i, type(idList[i]), tostring(idList[i])
5 years ago
elseif vis ~= nil and type(vis) ~= "string" then
return false, "Collection entry #%d: improper visibility conditional type (%s)", i, type(vis)
5 years ago
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
5 years ago
end
5 months ago
pack[i], pack[tok], pack[vkey], pack[ekey], pack[pkey] = tok, aid, vis, emb, pmode and (pmode + 1 - pmode % 2)
5 years ago
end
3 years ago
local openToken = idList.__openToken
5 months ago
pack.__openAction = type(idList.__openAction) == "number" and idList.__openAction or nil
pack.__openToken = type(openToken) == "string" and openToken:match("^[A-Za-z][A-Za-z0-9_=/]*$") or nil
pack.__embed = type(idList.__embed) == "boolean" and idList.__embed or nil
UnlockedCall(re_set_collection, id, pack)
5 years ago
return true
end
5 months ago
local function re_set_actInfo_clone(id, id2)
coreEnvW.actInfo[id], coreEnvW.actConditionals[id] = coreEnvW.actInfo[id2], coreEnvW.actConditionals[id2]
end
5 years ago
function createHandlers.clone(id, _count, id2)
5 months ago
UnlockedCall(re_set_actInfo_clone, id, id2)
5 years ago
return true
end
local function nullInfoFunc() return false end
5 months ago
local function re_set_actConditional(id, cnd)
coreEnvW.actConditionals[id] = cnd
end
5 years ago
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
5 years ago
function getActionArgs(it, ...)
if it then
return private_unpack(it, 2, 1+(actionNumArgs[it[1]] or 1))
5 years ago
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
5 years ago
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
5 years ago
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
5 years ago
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
5 years ago
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, ...)
5 years ago
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"?)')
5 years ago
if allocatedActions[id] then
return allocatedActions[id](allocatedActionArg[id], cndLock)
5 years ago
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?)')
5 years ago
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
5 years ago
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))
5 years ago
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
5 months ago
if cnd then
UnlockedCall(re_set_actConditional, id, cnd)
end
5 years ago
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("#", ...), ...))
5 years ago
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
if key == "Rewire" then
local RW = api:compatible(1, 34)
RWsec = RW and RW:seclib()
5 months ago
coreEnvW.RW = RWsec
end
end
T.ActionBook.compatible, ext.ActionBook = AB.compatible, T.ActionBook