if not WeakAuras.IsLibsOK() then return end local AddonName, Private = ... local WeakAuras = WeakAuras; local L = WeakAuras.L; Private.regionPrototype = {}; -- Alpha function Private.regionPrototype.AddAlphaToDefault(default) default.alpha = 1.0; end -- Adjusted Duration function Private.regionPrototype.AddAdjustedDurationToDefault(default) default.useAdjustededMax = false; default.useAdjustededMin = false; end function Private.regionPrototype.AddAdjustedDurationOptions(options, data, order) options.useAdjustededMin = { type = "toggle", width = WeakAuras.normalWidth, name = L["Set Minimum Progress"], desc = L["Values/Remaining Time below this value are displayed as no progress."], order = order }; options.adjustedMin = { type = "input", validate = WeakAuras.ValidateNumericOrPercent, width = WeakAuras.normalWidth, order = order + 0.01, name = L["Minimum"], hidden = function() return not data.useAdjustededMin end, desc = L["Enter static or relative values with %"] }; options.useAdjustedMinSpacer = { type = "description", width = WeakAuras.normalWidth, name = "", order = order + 0.02, hidden = function() return not (not data.useAdjustededMin and data.useAdjustededMax) end, }; options.useAdjustededMax = { type = "toggle", width = WeakAuras.normalWidth, name = L["Set Maximum Progress"], desc = L["Values/Remaining Time above this value are displayed as full progress."], order = order + 0.03 }; options.adjustedMax = { type = "input", width = WeakAuras.normalWidth, validate = WeakAuras.ValidateNumericOrPercent, order = order + 0.04, name = L["Maximum"], hidden = function() return not data.useAdjustededMax end, desc = L["Enter static or relative values with %"] }; options.useAdjustedMaxSpacer = { type = "description", width = WeakAuras.normalWidth, name = "", order = order + 0.05, hidden = function() return not (data.useAdjustededMin and not data.useAdjustededMax) end, }; return options; end local screenWidth, screenHeight = math.ceil(GetScreenWidth() / 20) * 20, math.ceil(GetScreenHeight() / 20) * 20; function Private.GetAnchorsForData(parentData, type) local result if not parentData.controlledChildren then if not Private.regionOptions[parentData.regionType] then return end local anchors if Private.regionOptions[parentData.regionType].getAnchors then anchors = Private.regionOptions[parentData.regionType].getAnchors(parentData) else anchors = Private.default_types_for_anchor end for anchorId, anchorData in pairs(anchors) do if anchorData.type == type then result = result or {} result[anchorId] = anchorData.display end end end return result end -- Sound / Chat Message / Custom Code function Private.regionPrototype.AddProperties(properties, defaultsForRegion) properties["sound"] = { display = L["Sound"], action = "SoundPlay", type = "sound", }; properties["chat"] = { display = L["Chat Message"], action = "SendChat", type = "chat", }; properties["customcode"] = { display = L["Run Custom Code"], action = "RunCode", type = "customcode" } properties["xOffsetRelative"] = { display = L["Relative X-Offset"], setter = "SetXOffsetRelative", type = "number", softMin = -screenWidth, softMax = screenWidth, bigStep = 1 } properties["yOffsetRelative"] = { display = L["Relative Y-Offset"], setter = "SetYOffsetRelative", type = "number", softMin = -screenHeight, softMax = screenHeight, bigStep = 1 } properties["glowexternal"] = { display = L["Glow External Element"], action = "GlowExternal", type = "glowexternal" } if (defaultsForRegion and defaultsForRegion.alpha) then properties["alpha"] = { display = L["Alpha"], setter = "SetRegionAlpha", type = "number", min = 0, max = 1, bigStep = 0.01, isPercent = true } end end local function SoundRepeatStop(self) Private.StartProfileSystem("sound"); if (self.soundRepeatTimer) then WeakAuras.timer:CancelTimer(self.soundRepeatTimer); self.soundRepeatTimer = nil; end Private.StopProfileSystem("sound"); end --[[ local function SoundStop(self) Private.StartProfileSystem("sound"); if (self.soundHandle) then StopSound(self.soundHandle); end Private.StopProfileSystem("sound"); end ]] local function SoundPlayHelper(self) Private.StartProfileSystem("sound"); local options = self.soundOptions; if (not options or options.sound_type == "Stop") then Private.StopProfileSystem("sound"); return; end if (WeakAuras.IsOptionsOpen() or Private.SquelchingActions()) then Private.StopProfileSystem("sound"); return; end if (options.sound == " custom") then local ok, _, handle = pcall(PlaySoundFile, options.sound_path, options.sound_channel or "Master") --if ok then --self.soundHandle = handle --end elseif (options.sound == " KitID") then local ok, _, handle = pcall(PlaySound, options.sound_kit_id, options.sound_channel or "Master") --if ok then --self.soundHandle = handle --end else local ok, _, handle = pcall(PlaySoundFile, options.sound, options.sound_channel or "Master") --if ok then --self.soundHandle = handle --end end Private.StopProfileSystem("sound"); end local function hasSound(options) --if options.sound_type == "Stop" then --return true --end if (options.sound == " custom") then if (options.sound_path and options.sound_path ~= "") then return true end elseif (options.sound == " KitID") then if (options.sound_kit_id and options.sound_kit_id ~= "") then return true end else if options.sound and options.sound ~= "" then return true end end return false end local function SoundPlay(self, options) if (not options or WeakAuras.IsOptionsOpen()) then return end Private.StartProfileSystem("sound"); if not hasSound(options) then Private.StopProfileSystem("sound") return end --self:SoundStop(); self:SoundRepeatStop(); self.soundOptions = options; SoundPlayHelper(self); local loop = options.do_loop or options.sound_type == "Loop"; if (loop and options.sound_repeat) then self.soundRepeatTimer = WeakAuras.timer:ScheduleRepeatingTimer(SoundPlayHelper, options.sound_repeat, self); end Private.StopProfileSystem("sound"); end local function SendChat(self, options) if (not options or WeakAuras.IsOptionsOpen()) then return end Private.HandleChatAction(options.message_type, options.message, options.message_dest, options.message_dest_isunit, options.message_channel, options.r, options.g, options.b, self, options.message_custom, nil, options.message_formaters); end local function RunCode(self, func) if func and not WeakAuras.IsOptionsOpen() then Private.ActivateAuraEnvironment(self.id, self.cloneId, self.state, self.states); xpcall(func, Private.GetErrorHandlerId(self.id, L["Custom Condition Code"])); Private.ActivateAuraEnvironment(nil); end end local function GlowExternal(self, options) if (not options or WeakAuras.IsOptionsOpen()) then return end Private.HandleGlowAction(options, self) end local function UpdatePosition(self) if (not self.anchorPoint or not self.relativeTo or not self.relativePoint) then return; end local xOffset = self.xOffset + (self.xOffsetAnim or 0) + (self.xOffsetRelative or 0) local yOffset = self.yOffset + (self.yOffsetAnim or 0) + (self.yOffsetRelative or 0) self:RealClearAllPoints(); local ok, ret = pcall(self.SetPoint, self, self.anchorPoint, self.relativeTo, self.relativePoint, xOffset, yOffset); if not ok then Private.GetErrorHandlerId(self.id, L["Update Position"]) end end local function ResetPosition(self) self.anchorPoint = nil; self.relativeTo = nil; self.relativePoint = nil; end local function SetAnchor(self, anchorPoint, relativeTo, relativePoint) if self.anchorPoint == anchorPoint and self.relativeTo == relativeTo and self.relativePoint == relativePoint then return end self.anchorPoint = anchorPoint; self.relativeTo = relativeTo; self.relativePoint = relativePoint; UpdatePosition(self); end local function SetOffset(self, xOffset, yOffset) if (self.xOffset == xOffset and self.yOffset == yOffset) then return; end self.xOffset = xOffset; self.yOffset = yOffset; UpdatePosition(self); end local function SetXOffset(self, xOffset) self:SetOffset(xOffset, self:GetYOffset()); end local function SetYOffset(self, yOffset) self:SetOffset(self:GetXOffset(), yOffset); end local function GetXOffset(self) return self.xOffset; end local function GetYOffset(self) return self.yOffset; end local function SetOffsetRelative(self, xOffsetRelative, yOffsetRelative) if (self.xOffsetRelative == xOffsetRelative and self.yOffsetRelative == yOffsetRelative) then return end self.xOffsetRelative = xOffsetRelative self.yOffsetRelative = yOffsetRelative UpdatePosition(self) end local function SetXOffsetRelative(self, xOffsetRelative) self:SetOffsetRelative(xOffsetRelative, self:GetYOffsetRelative()) end local function SetYOffsetRelative(self, yOffsetRelative) self:SetOffsetRelative(self:GetXOffsetRelative(), yOffsetRelative) end local function GetXOffsetRelative(self) return self.xOffsetRelative end local function GetYOffsetRelative(self) return self.yOffsetRelative end local function SetOffsetAnim(self, xOffset, yOffset) if (self.xOffsetAnim == xOffset and self.yOffsetAnim == yOffset) then return; end self.xOffsetAnim = xOffset; self.yOffsetAnim = yOffset; UpdatePosition(self); end local function SetRegionAlpha(self, alpha) if (self.alpha == alpha) then return; end self.alpha = alpha; self:SetAlpha(self.animAlpha or self.alpha or 1); self.subRegionEvents:Notify("AlphaChanged") end local function GetRegionAlpha(self) return self.animAlpha or self.alpha or 1; end local function SetAnimAlpha(self, alpha) if (self.animAlpha == alpha) then return; end self.animAlpha = alpha; if (WeakAuras.IsOptionsOpen()) then local ok = pcall(self.SetAlpha, self, max(self.animAlpha or self.alpha or 1, 0.5)) if not ok then Private.GetErrorHandlerId(self.id, L["Custom Fade Animation"]) end else local ok = pcall(self.SetAlpha, self, self.animAlpha or self.alpha or 1) if not ok then Private.GetErrorHandlerId(self.id, L["Custom Fade Animation"]) end end self.subRegionEvents:Notify("AlphaChanged") end local function SetTriggerProvidesTimer(self, timerTick) self.triggerProvidesTimer = timerTick self:UpdateTimerTick() end local function TimerTickForRegion(region) Private.StartProfileSystem("timer tick") Private.StartProfileAura(region.id); region.subRegionEvents:Notify("TimerTick") Private.StopProfileAura(region.id); Private.StopProfileSystem("timer tick") end local function UpdateTimerTick(self) if self.triggerProvidesTimer and self.subRegionEvents:HasSubscribers("TimerTick") and self.toShow then if not self:GetScript("OnUpdate") then self:SetScript("OnUpdate", function() TimerTickForRegion(self) end); end else if self:GetScript("OnUpdate") then self:SetScript("OnUpdate", nil); end end end local function UpdateFrameTick(self) if self.subRegionEvents:HasSubscribers("FrameTick") and self.toShow then Private.FrameTick:AddSubscriber("FrameTick", self) else Private.FrameTick:RemoveSubscriber("FrameTick", self) end end local function FrameTick(self) Private.StartProfileAura(self.id) self.values.lastCustomTextUpdate = nil self.subRegionEvents:Notify("FrameTick") Private.StopProfileAura(self.id) end local function AnchorSubRegion(self, subRegion, anchorType, selfPoint, anchorPoint, anchorXOffset, anchorYOffset) subRegion:ClearAllPoints() if anchorType == "point" then local xOffset = anchorXOffset or 0 local yOffset = anchorYOffset or 0 subRegion:SetPoint(Private.point_types[selfPoint] and selfPoint or "CENTER", self, Private.point_types[anchorPoint] and anchorPoint or "CENTER", xOffset, yOffset) else anchorXOffset = anchorXOffset or 0 anchorYOffset = anchorYOffset or 0 subRegion:SetPoint("bottomleft", self, "bottomleft", -anchorXOffset, -anchorYOffset) subRegion:SetPoint("topright", self, "topright", anchorXOffset, anchorYOffset) end end Private.regionPrototype.AnchorSubRegion = AnchorSubRegion function Private.regionPrototype.create(region) local defaultsForRegion = Private.regionTypes[region.regionType] and Private.regionTypes[region.regionType].default; region.SoundPlay = SoundPlay; region.SoundRepeatStop = SoundRepeatStop; region.SendChat = SendChat; region.RunCode = RunCode; region.GlowExternal = GlowExternal; region.SetAnchor = SetAnchor; region.SetOffset = SetOffset; region.SetXOffset = SetXOffset; region.SetYOffset = SetYOffset; region.GetXOffset = GetXOffset; region.GetYOffset = GetYOffset; region.SetOffsetRelative = SetOffsetRelative region.SetXOffsetRelative = SetXOffsetRelative region.SetYOffsetRelative = SetYOffsetRelative region.GetXOffsetRelative = GetXOffsetRelative region.GetYOffsetRelative = GetYOffsetRelative region.SetOffsetAnim = SetOffsetAnim; region.ResetPosition = ResetPosition; region.RealClearAllPoints = region.ClearAllPoints; region.ClearAllPoints = function() region:RealClearAllPoints(); region:ResetPosition(); end if (defaultsForRegion and defaultsForRegion.alpha) then region.SetRegionAlpha = SetRegionAlpha; region.GetRegionAlpha = GetRegionAlpha; end region.SetAnimAlpha = SetAnimAlpha; region.SetTriggerProvidesTimer = SetTriggerProvidesTimer region.UpdateTimerTick = UpdateTimerTick region.UpdateFrameTick = UpdateFrameTick region.FrameTick = FrameTick region.subRegionEvents = Private.CreateSubscribableObject() region.AnchorSubRegion = AnchorSubRegion region.values = {} -- For SubText region:SetPoint("CENTER", UIParent, "CENTER") end -- SetDurationInfo function Private.regionPrototype.modify(parent, region, data) region.state = nil region.states = nil region.subRegionEvents:ClearSubscribers() region.subRegionEvents:ClearCallbacks() Private.FrameTick:RemoveSubscriber("FrameTick", region) local defaultsForRegion = Private.regionTypes[data.regionType] and Private.regionTypes[data.regionType].default; if region.SetRegionAlpha then region:SetRegionAlpha(data.alpha) end local hasAdjustedMin = defaultsForRegion and defaultsForRegion.useAdjustededMin ~= nil and data.useAdjustededMin and data.adjustedMin; local hasAdjustedMax = defaultsForRegion and defaultsForRegion.useAdjustededMax ~= nil and data.useAdjustededMax and data.adjustedMax; region.adjustedMin = nil region.adjustedMinRel = nil region.adjustedMinRelPercent = nil region.adjustedMax = nil region.adjustedMaxRel = nil region.adjustedMaxRelPercent = nil if (hasAdjustedMin) then local percent = string.match(data.adjustedMin, "(%d+)%%") if percent then region.adjustedMinRelPercent = tonumber(percent) / 100 else region.adjustedMin = tonumber(data.adjustedMin); end end if (hasAdjustedMax) then local percent = string.match(data.adjustedMax, "(%d+)%%") if percent then region.adjustedMaxRelPercent = tonumber(percent) / 100 else region.adjustedMax = tonumber(data.adjustedMax) end end region:SetOffset(data.xOffset or 0, data.yOffset or 0); region:SetOffsetRelative(0, 0) region:SetOffsetAnim(0, 0); if data.anchorFrameType == "CUSTOM" and data.customAnchor then region.customAnchorFunc = WeakAuras.LoadFunction("return " .. data.customAnchor) else region.customAnchorFunc = nil end if not parent or parent.regionType ~= "dynamicgroup" then if -- Don't anchor single Auras that with custom anchoring, -- these will be anchored in expand not ( data.anchorFrameType == "CUSTOM" or data.anchorFrameType == "UNITFRAME" or data.anchorFrameType == "NAMEPLATE" ) -- Group Auras that will never be expanded, so those need -- to be always anchored here or data.regionType == "dynamicgroup" or data.regionType == "group" then Private.AnchorFrame(data, region, parent); end end region.startFormatters = Private.CreateFormatters(data.actions.start.message, function(key, default) local fullKey = "message_format_" .. key if data.actions.start[fullKey] == nil then data.actions.start[fullKey] = default end return data.actions.start[fullKey] end, true) region.finishFormatters = Private.CreateFormatters(data.actions.finish.message, function(key, default) local fullKey = "message_format_" .. key if data.actions.finish[fullKey] == nil then data.actions.finish[fullKey] = default end return data.actions.finish[fullKey] end, true) end function Private.regionPrototype.modifyFinish(parent, region, data) -- Sync subRegions if region.subRegions then for index, subRegion in pairs(region.subRegions) do Private.subRegionTypes[subRegion.type].release(subRegion) end wipe(region.subRegions) end if data.subRegions then region.subRegions = region.subRegions or {} local subRegionTypes = {} for index, subRegionData in pairs(data.subRegions) do if Private.subRegionTypes[subRegionData.type] then local subRegion = Private.subRegionTypes[subRegionData.type].acquire() subRegion.type = subRegionData.type if subRegion then Private.subRegionTypes[subRegionData.type].modify(region, subRegion, data, subRegionData, not subRegionTypes[subRegionData.type]) subRegionTypes[subRegionData.type] = true end tinsert(region.subRegions, subRegion) end end end region.subRegionEvents:SetOnSubscriptionStatusChanged("TimerTick", function() region:UpdateTimerTick() end) region:UpdateTimerTick() region.subRegionEvents:SetOnSubscriptionStatusChanged("FrameTick", function() region:UpdateFrameTick() end) region:UpdateFrameTick() Private.ApplyFrameLevel(region) end local function SetProgressValue(region, value, total) local adjustMin = region.adjustedMin or 0; local max = region.adjustedMax or total; region:SetValue(value - adjustMin, max - adjustMin); end local regionsForFrameTick = {} local frameForFrameTick = CreateFrame("Frame"); Private.frames["Frame Tick Frame"] = frameForFrameTick Private.FrameTick = Private.CreateSubscribableObject() Private.FrameTick.OnUpdateHandler = function() if WeakAuras.IsOptionsOpen() then return end Private.StartProfileSystem("frame tick") Private.FrameTick:Notify("FrameTick") Private.StopProfileSystem("frame tick") end Private.FrameTick:SetOnSubscriptionStatusChanged("FrameTick", function() if Private.FrameTick:HasSubscribers("FrameTick") then frameForFrameTick:SetScript("OnUpdate", Private.FrameTick.OnUpdateHandler); else frameForFrameTick:SetScript("OnUpdate", nil) end end) local function TimerTickForSetDuration(self) local duration = self.duration local adjustMin = self.adjustedMin or 0; local max if duration == 0 then max = 0 elseif self.adjustedMax then max = self.adjustedMax else max = duration end self:SetTime(max - adjustMin, self.expirationTime - adjustMin, self.inverse); end function Private.regionPrototype.AddSetDurationInfo(region) if (region.SetValue and region.SetTime) then region.generatedSetDurationInfo = true; -- WeakAuras no longer calls SetDurationInfo, but some people do that, -- In that case we also need to overwrite TimerTick region.SetDurationInfo = function(self, duration, expirationTime, customValue, inverse) self.duration = duration or 0 self.expirationTime = expirationTime; self.inverse = inverse; if customValue then SetProgressValue(region, duration, expirationTime); region.TimerTick = nil region.subRegionEvents:RemoveSubscriber("TimerTick", self) else local adjustMin = region.adjustedMin or 0; region:SetTime((duration ~= 0 and region.adjustedMax or duration) - adjustMin, expirationTime - adjustMin, inverse); region.TimerTick = TimerTickForSetDuration region.subRegionEvents:AddSubscriber("TimerTick", self, true) end end elseif (region.generatedSetDurationInfo) then region.generatedSetDurationInfo = nil; region.SetDurationInfo = nil; end end -- Expand/Collapse function function Private.regionPrototype.AddExpandFunction(data, region, cloneId, parent, parentRegionType) local uid = data.uid local id = data.id local inDynamicGroup = parentRegionType == "dynamicgroup"; local inGroup = parentRegionType == "group"; local startMainAnimation = function() Private.Animate("display", uid, "main", data.animation.main, region, false, nil, true, cloneId); end function region:OptionsClosed() region:EnableMouse(false) region:SetScript("OnMouseDown", nil) end function region:ClickToPick() region:EnableMouse(true) region:SetScript("OnMouseDown", function() WeakAuras.PickDisplay(region.id, nil, true) end) if region.GetFrameStrata and region:GetFrameStrata() == "TOOLTIP" then region:SetFrameStrata("HIGH") end end local hideRegion; if(inDynamicGroup) then hideRegion = function() if region.PreHide then region:PreHide() end Private.RunConditions(region, uid, true) region.subRegionEvents:Notify("PreHide") if region:IsProtected() then if InCombatLockdown() then Private.AuraWarnings.UpdateWarning(uid, "protected_frame_error", "error", L["Cannot change secure frame in combat lockdown. Find more information:\nhttps://github.com/WeakAuras/WeakAuras2/wiki/Protected-Frames"], true) else Private.AuraWarnings.UpdateWarning(uid, "protected_frame", "warning", L["Secure frame detected. Find more information:\nhttps://github.com/WeakAuras/WeakAuras2/wiki/Protected-Frames"]) region:Hide() end else Private.AuraWarnings.UpdateWarning(uid, "protected_frame") region:Hide() end region.states = nil region.state = nil if (cloneId) then Private.ReleaseClone(region.id, cloneId, data.regionType); parent:RemoveChild(id, cloneId) else parent:DeactivateChild(id, cloneId); end end else hideRegion = function() if region.PreHide then region:PreHide() end Private.RunConditions(region, uid, true) region.subRegionEvents:Notify("PreHide") if region:IsProtected() then if InCombatLockdown() then Private.AuraWarnings.UpdateWarning(uid, "protected_frame_error", "error", L["Cannot change secure frame in combat lockdown. Find more information:\nhttps://github.com/WeakAuras/WeakAuras2/wiki/Protected-Frames"], true) else Private.AuraWarnings.UpdateWarning(uid, "protected_frame", "warning", L["Secure frame detected. Find more information:\nhttps://github.com/WeakAuras/WeakAuras2/wiki/Protected-Frames"]) region:Hide() end else Private.AuraWarnings.UpdateWarning(uid, "protected_frame") region:Hide() end region.states = nil region.state = nil if (cloneId) then Private.ReleaseClone(region.id, cloneId, data.regionType); end end end if(inDynamicGroup) then function region:Collapse() if (not region.toShow) then return; end region.toShow = false; Private.PerformActions(data, "finish", region); if (not Private.Animate("display", data.uid, "finish", data.animation.finish, region, false, hideRegion, nil, cloneId)) then hideRegion(); end if (region.SoundRepeatStop) then region:SoundRepeatStop(); end region:UpdateFrameTick() region:UpdateTimerTick() end function region:Expand() if (region.toShow) then return; end region.toShow = true; if(region.PreShow) then region:PreShow(); end region.subRegionEvents:Notify("PreShow") Private.ApplyFrameLevel(region) if region:IsProtected() then if InCombatLockdown() then Private.AuraWarnings.UpdateWarning(uid, "protected_frame_error", "error", L["Cannot change secure frame in combat lockdown. Find more information:\nhttps://github.com/WeakAuras/WeakAuras2/wiki/Protected-Frames"], true) else Private.AuraWarnings.UpdateWarning(uid, "protected_frame", "warning", L["Secure frame detected. Find more information:\nhttps://github.com/WeakAuras/WeakAuras2/wiki/Protected-Frames"]) region:Show() end else Private.AuraWarnings.UpdateWarning(uid, "protected_frame") region:Show() end Private.PerformActions(data, "start", region); if not(Private.Animate("display", data.uid, "start", data.animation.start, region, true, startMainAnimation, nil, cloneId)) then startMainAnimation(); end parent:ActivateChild(data.id, cloneId); region:UpdateFrameTick() region:UpdateTimerTick() end elseif not(data.controlledChildren) then function region:Collapse() if (not region.toShow) then return; end region.toShow = false; Private.PerformActions(data, "finish", region); if (not Private.Animate("display", data.uid, "finish", data.animation.finish, region, false, hideRegion, nil, cloneId)) then hideRegion(); end if inGroup then parent:UpdateBorder(region); end if (region.SoundRepeatStop) then region:SoundRepeatStop(); end region:UpdateFrameTick() region:UpdateTimerTick() end function region:Expand() if data.anchorFrameType == "SELECTFRAME" or data.anchorFrameType == "CUSTOM" or data.anchorFrameType == "UNITFRAME" or data.anchorFrameType == "NAMEPLATE" then Private.AnchorFrame(data, region, parent); end if (region.toShow) then return; end region.toShow = true if(region.PreShow) then region:PreShow(); end region.subRegionEvents:Notify("PreShow") Private.ApplyFrameLevel(region) if region:IsProtected() then if InCombatLockdown() then Private.AuraWarnings.UpdateWarning(uid, "protected_frame_error", "error", L["Cannot change secure frame in combat lockdown. Find more information:\nhttps://github.com/WeakAuras/WeakAuras2/wiki/Protected-Frames"], true) else Private.AuraWarnings.UpdateWarning(uid, "protected_frame", "warning", L["Secure frame detected. Find more information:\nhttps://github.com/WeakAuras/WeakAuras2/wiki/Protected-Frames"]) region:Show() end else Private.AuraWarnings.UpdateWarning(uid, "protected_frame") region:Show() end Private.PerformActions(data, "start", region); if not(Private.Animate("display", data.uid, "start", data.animation.start, region, true, startMainAnimation, nil, cloneId)) then startMainAnimation(); end if inGroup then parent:UpdateBorder(region); end region:UpdateFrameTick() region:UpdateTimerTick() end end -- Stubs that allow for polymorphism if not region.Collapse then function region:Collapse() end end if not region.Expand then function region:Expand() end end if not region.Pause then function region:Pause() self.paused = true end end if not region.Resume then function region:Resume() self.paused = nil end end end do local function move_condition_subregions(data, offset, afterPos) if data.conditions then for conditionIndex, condition in ipairs(data.conditions) do if type(condition.changes) == "table" then for changeIndex, change in ipairs(condition.changes) do if change.property then local subRegionIndex, property = change.property:match("^sub%.(%d+)%.(.*)") subRegionIndex = tonumber(subRegionIndex) if subRegionIndex and property then if (subRegionIndex >= afterPos) then change.property = "sub." .. subRegionIndex + offset .. "." .. property end end end end end end end end function Private.EnforceSubregionExists(data, subregionType) data.subRegions = data.subRegions or {} -- search and save indexes of matching subregions local indexes = {} for index, subRegionData in ipairs(data.subRegions) do if subRegionData.type == subregionType then table.insert(indexes, index) end end -- add if missing if #indexes == 0 then tinsert(data.subRegions, 1, { ["type"] = subregionType }) move_condition_subregions(data, 1, 1) -- delete duplicate elseif #indexes > 1 then for i = #indexes, 2, -1 do table.remove(data.subRegions, indexes[i]) move_condition_subregions(data, -1, indexes[i]) end end end end