local _, addon = ... local CallbackRegistry = {}; CallbackRegistry.events = {}; addon.CallbackRegistry = CallbackRegistry; local tinsert = table.insert; local tremove = table.remove; local type = type; local ipairs = ipairs; --[[ callbackType: 1. Function func(owner) 2. Method owner:func() --]] function CallbackRegistry:Register(event, func, owner, prioritized) if not self.events[event] then self.events[event] = {}; end local callbackType; if type(func) == "string" then callbackType = 2; else callbackType = 1; end if prioritized then tinsert(self.events[event], 1, {callbackType, func, owner}) else tinsert(self.events[event], {callbackType, func, owner}) end end function CallbackRegistry:Trigger(event, ...) if self.events[event] then for _, cb in ipairs(self.events[event]) do if cb[1] == 1 then if cb[3] then cb[2](cb[3], ...); else cb[2](...); end else cb[3][cb[2]](cb[3], ...); end end end end function CallbackRegistry:UnregisterCallback(event, callback, owner) if self.events[event] then local callbacks = self.events[event]; local i = 1; local cb = callbacks[i]; if type(callback) == "string" then if owner then while cb do if cb[1] == 2 and cb[2] == callback and cb[3] == owner then tremove(callbacks, i); else i = i + 1; end cb = callbacks[i]; end else while cb do if cb[1] == 2 and cb[2] == callback then tremove(callbacks, i); else i = i + 1; end cb = callbacks[i]; end end else while cb do if cb[1] == 1 and cb[2] == callback then tremove(callbacks, i); else i = i + 1; end cb = callbacks[i]; end end end end function CallbackRegistry:UnregisterEvent(event) self.events[event] = nil; end function CallbackRegistry:RegisterTutorial(tutorialFlag, func, owner) local event = "Tutorial."..tutorialFlag; self:Register(event, func, owner); end local Processor = CreateFrame("Frame"); local function Processor_OnUpdate(self, elapsed) self:SetScript("OnUpdate", nil); if self.triggerQueue and self.anyDelayedTrigger then self.anyDelayedTrigger = nil; for event, args in pairs(self.triggerQueue) do CallbackRegistry:Trigger(event, args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8]); end self.triggerQueue = nil; end end function CallbackRegistry:TriggerOnNextUpdate(event, ...) --Use Case: in case Lua error appears and clogs other important processes --Currently used by HelpTip if self.events[event] then if not Processor.anyDelayedTrigger then Processor.triggerQueue = {}; Processor.anyDelayedTrigger = true; end Processor.triggerQueue[event] = {...}; Processor:SetScript("OnUpdate", Processor_OnUpdate); end end do --UIParent OnShow/OnHide local frame = CreateFrame("Frame", nil, UIParent); frame:SetScript("OnShow", function() CallbackRegistry:Trigger("UIParent.Show"); end); frame:SetScript("OnHide", function() CallbackRegistry:Trigger("UIParent.Hide"); end); end do --AsyncCallback local EL = CreateFrame("Frame"); --LoadQuestAPI is not available in 60 Classic --In this case we will run all callbacks when the time is up EL.LoadQuest = C_QuestLog.RequestLoadQuestByID; EL.LoadItem = C_Item.RequestLoadItemDataByID; EL.LoadSpell = C_Spell.RequestLoadSpellData; function EL:RunAllCallbacks(list) for id, callbacks in pairs(list) do for _, callbackInfo in ipairs(callbacks) do if (callbackInfo.oneTime and not callbackInfo.processed) or (callbackInfo.oneTime == false) then callbackInfo.processed = true; callbackInfo.func(id); end end end end function EL:OnEvent(event, ...) local id, success = ... local list; if event == "QUEST_DATA_LOAD_RESULT" then list = self.questCallbacks; elseif event == "ITEM_DATA_LOAD_RESULT" then list = self.itemCallbacks; elseif event == "SPELL_DATA_LOAD_RESULT" then list = self.spellCallbacks; elseif event == "TOOLTIP_DATA_UPDATE" then list = self.npcCallbacks; success = true; end if list and id and success then if list[id] then for _, callbackInfo in ipairs(list[id]) do if (callbackInfo.oneTime and not callbackInfo.processed) or (callbackInfo.oneTime == false) then callbackInfo.processed = true; callbackInfo.func(id); end end end end self.t = 0; --Reset close count down end EL:SetScript("OnEvent", EL.OnEvent); function EL:OnUpdate(elapsed) self.t = self.t + elapsed; if self.t > 0.5 then self.t = nil; self:SetScript("OnUpdate", nil); if self.questCallbacks then if self.LoadQuest then self:UnregisterEvent("QUEST_DATA_LOAD_RESULT"); end if self.runCallbackAfter then self:RunAllCallbacks(self.questCallbacks); end self.questCallbacks = nil; end if self.itemCallbacks then if self.LoadItem then self:UnregisterEvent("ITEM_DATA_LOAD_RESULT"); end self:RunAllCallbacks(self.itemCallbacks); self.itemCallbacks = nil; end if self.spellCallbacks then if self.LoadSpell then self:UnregisterEvent("SPELL_DATA_LOAD_RESULT"); end self:RunAllCallbacks(self.spellCallbacks); self.spellCallbacks = nil; end if self.npcCallbacks then if self.LoadNPC then self:UnregisterEvent("TOOLTIP_DATA_UPDATE"); end self:RunAllCallbacks(self.npcCallbacks); self.npcCallbacks = nil; end end end function EL:AddCallback(key, id, callback, oneTime) if not self[key] then self[key] = {}; end if not self[key][id] then self[key][id] = {}; end if oneTime == nil then oneTime = true; end local callbackInfo = { func = callback, oneTime = oneTime, processed = false, }; tinsert(self[key][id], callbackInfo); end function CallbackRegistry:LoadQuest(id, callback, oneTime) EL:AddCallback("questCallbacks", id, callback, oneTime); if EL.LoadQuest then EL:RegisterEvent("QUEST_DATA_LOAD_RESULT"); EL.LoadQuest(id); else EL.runCallbackAfter = true; end EL.t = 0; EL:SetScript("OnUpdate", EL.OnUpdate); end function CallbackRegistry:LoadItem(id, callback, oneTime) EL:AddCallback("itemCallbacks", id, callback, oneTime); if EL.LoadItem then EL:RegisterEvent("ITEM_DATA_LOAD_RESULT"); EL.LoadItem(id); else EL.runCallbackAfter = true; end EL.t = 0; EL:SetScript("OnUpdate", EL.OnUpdate); end function CallbackRegistry:LoadSpell(id, callback, oneTime) EL:AddCallback("spellCallbacks", id, callback, oneTime); if EL.LoadSpell then EL:RegisterEvent("SPELL_DATA_LOAD_RESULT"); EL.LoadSpell(id); else EL.runCallbackAfter = true; end EL.t = 0; EL:SetScript("OnUpdate", EL.OnUpdate); end if C_TooltipInfo then function CallbackRegistry:LoadNPC(creatureID, callback, isRequery) local tooltipData = addon.TooltipAPI.GetHyperlink("unit:Creature-0-0-0-0-"..creatureID); local dataInstanceID, newCallback; if tooltipData and tooltipData.lines then local name = tooltipData.lines[1].leftText; if name and name ~= "" then callback(creatureID, name); else dataInstanceID = tooltipData.dataInstanceID; newCallback = function() local tooltipData = addon.TooltipAPI.GetHyperlink("unit:Creature-0-0-0-0-"..creatureID); local name = tooltipData.lines[1].leftText; if name and name ~= "" then callback(creatureID, name); end end end elseif not isRequery then dataInstanceID = 0; newCallback = function() local tooltipData = addon.TooltipAPI.GetHyperlink("unit:Creature-0-0-0-0-"..creatureID); local name = tooltipData and tooltipData.lines and tooltipData.lines[1].leftText; if name and name ~= "" then callback(creatureID, name); end end end if dataInstanceID and newCallback then EL:AddCallback("npcCallbacks", dataInstanceID, newCallback); EL:RegisterEvent("TOOLTIP_DATA_UPDATE"); if not EL.t then EL.t = 0; end if EL.t > 0 then --Extend the shutdown countdown because we usually acquire NPC name during log-in EL.t = -0.5; end EL:SetScript("OnUpdate", EL.OnUpdate); end end else function CallbackRegistry:LoadNPC() end end end do --Public methods for addon compatibility (NOT IMPLEMENTED) --Private local IsCallbackAllowed = {}; local EventObject = {}; local PublicCallbacks = {}; local function AddSupportedPublicCallback(event, objectGetter) IsCallbackAllowed[event] = true; if objectGetter then if EventObject[event] then addon.API.PrintMessage(string.format("Public Event (%s) already has a owner")); else EventObject[event] = objectGetter; end end end addon.AddSupportedPublicCallback = AddSupportedPublicCallback; function CallbackRegistry:TriggerExternalEvent(event, ...) if IsCallbackAllowed[event] and PublicCallbacks[event] then local objectGetter = EventObject[event]; local obj; if objectGetter then obj = objectGetter(); end for _, cb in ipairs(PublicCallbacks[event]) do cb(obj, ...); end end end --Public local function RegisterCallback(event, callback) --payloads: isRegistered, isNew if event and type(event) == "string" and callback and type(callback) == "function" then if not PublicCallbacks[event] then PublicCallbacks[event] = {}; end for _, cb in ipairs(PublicCallbacks[event]) do if cb == callback then return true, false end end table.insert(PublicCallbacks[event], callback); return true, true end return false, false end --DialogueUIAPI.RegisterCallback = RegisterCallback; end