Quantcast

update ouf

rawoil [04-15-22 - 02:24]
update ouf
Filename
oUF/LICENSE
oUF/blizzard.lua
oUF/colors.lua
oUF/combatevents.lua
oUF/elements/additionalpower.lua
oUF/elements/auras.lua
oUF/elements/castbar.lua
oUF/elements/classpower.lua
oUF/elements/healprediction.lua
oUF/elements/health.lua
oUF/elements/phaseindicator.lua
oUF/elements/power.lua
oUF/elements/raidroleindicator.lua
oUF/elements/range.lua
oUF/elements/readycheckindicator.lua
oUF/elements/tags.lua
oUF/elements/threatindicator.lua
oUF/events.lua
oUF/factory.lua
oUF/finalize.lua
oUF/init.lua
oUF/libs/LibHealComm-4.0/LibHealComm-4.0.lua
oUF/libs/LibHealComm-4.0/LibHealComm-4.0.xml
oUF/oUF.toc
oUF/oUF.xml
oUF/ouf.lua
oUF/private.lua
oUF/units.lua
oUF_Simple/modules/oUF_EnergyManaRegen.lua
oUF_Simple/modules/oUF_Swing.lua
diff --git a/oUF/LICENSE b/oUF/LICENSE
index baea223..476398b 100644
--- a/oUF/LICENSE
+++ b/oUF/LICENSE
@@ -1,7 +1,7 @@
-Copyright (c) 2006-2020 Trond A Ekseth <troeks@gmail.com>
-Copyright (c) 2016-2020 Val Voronov <i.lightspark@gmail.com>
-Copyright (c) 2016-2020 Adrian L Lange <contact@p3lim.net>
-Copyright (c) 2016-2020 Rainrider <rainrider.wow@gmail.com>
+Copyright (c) 2006-2022 Trond A Ekseth <troeks@gmail.com>
+Copyright (c) 2016-2022 Val Voronov <i.lightspark@gmail.com>
+Copyright (c) 2016-2022 Adrian L Lange <contact@p3lim.net>
+Copyright (c) 2016-2022 Rainrider <rainrider.wow@gmail.com>

 Permission is hereby granted, free of charge, to any person
 obtaining a copy of this software and associated documentation
diff --git a/oUF/blizzard.lua b/oUF/blizzard.lua
index 48d1a2f..e90aae8 100644
--- a/oUF/blizzard.lua
+++ b/oUF/blizzard.lua
@@ -1,14 +1,14 @@
-local parent, ns = ...
+local _, ns = ...
 local oUF = ns.oUF

 -- sourced from Blizzard_ArenaUI/Blizzard_ArenaUI.lua
-local MAX_ARENA_ENEMIES = MAX_ARENA_ENEMIES or 5
+local MAX_ARENA_ENEMIES = _G.MAX_ARENA_ENEMIES or 5

 -- sourced from FrameXML/TargetFrame.lua
-local MAX_BOSS_FRAMES = MAX_BOSS_FRAMES or 5
+local MAX_BOSS_FRAMES = _G.MAX_BOSS_FRAMES or 5

 -- sourced from FrameXML/PartyMemberFrame.lua
-local MAX_PARTY_MEMBERS = MAX_PARTY_MEMBERS or 4
+local MAX_PARTY_MEMBERS = _G.MAX_PARTY_MEMBERS or 4

 local hiddenParent = CreateFrame('Frame', nil, UIParent)
 hiddenParent:SetAllPoints()
@@ -69,10 +69,10 @@ function oUF:DisableBlizzard(unit)

 		-- For the damn vehicle support:
 		PlayerFrame:RegisterEvent('PLAYER_ENTERING_WORLD')
-		PlayerFrame:RegisterEvent('UNIT_ENTERING_VEHICLE')
-		PlayerFrame:RegisterEvent('UNIT_ENTERED_VEHICLE')
-		PlayerFrame:RegisterEvent('UNIT_EXITING_VEHICLE')
-		PlayerFrame:RegisterEvent('UNIT_EXITED_VEHICLE')
+		--PlayerFrame:RegisterEvent('UNIT_ENTERING_VEHICLE')
+		--PlayerFrame:RegisterEvent('UNIT_ENTERED_VEHICLE')
+		--PlayerFrame:RegisterEvent('UNIT_EXITING_VEHICLE')
+		--PlayerFrame:RegisterEvent('UNIT_EXITED_VEHICLE')

 		-- User placed frames don't animate
 		PlayerFrame:SetUserPlaced(true)
@@ -87,7 +87,7 @@ function oUF:DisableBlizzard(unit)
 		handleFrame(TargetofFocusFrame)
 	elseif(unit == 'targettarget') then
 		handleFrame(TargetFrameToT)
-	elseif(unit:match('boss%d?$')) then
+--[[elseif(unit:match('boss%d?$')) then
 		local id = unit:match('boss(%d)')
 		if(id) then
 			handleFrame('Boss' .. id .. 'TargetFrame')
@@ -95,7 +95,7 @@ function oUF:DisableBlizzard(unit)
 			for i = 1, MAX_BOSS_FRAMES do
 				handleFrame(string.format('Boss%dTargetFrame', i))
 			end
-		end
+		end]]
 	elseif(unit:match('party%d?$')) then
 		local id = unit:match('party(%d)')
 		if(id) then
@@ -116,7 +116,7 @@ function oUF:DisableBlizzard(unit)
 		end

 		-- Blizzard_ArenaUI should not be loaded
-		Arena_LoadUI = function() end
+		_G.Arena_LoadUI = function() end
 		SetCVar('showArenaEnemyFrames', '0', 'SHOW_ARENA_ENEMY_FRAMES_TEXT')
 	elseif(unit:match('nameplate%d+$')) then
 		local frame = C_NamePlate.GetNamePlateForUnit(unit)
@@ -129,4 +129,4 @@ function oUF:DisableBlizzard(unit)
 			handleFrame(frame.UnitFrame, true)
 		end
 	end
-end
+end
\ No newline at end of file
diff --git a/oUF/colors.lua b/oUF/colors.lua
index 771c958..9bfe2b9 100644
--- a/oUF/colors.lua
+++ b/oUF/colors.lua
@@ -38,7 +38,11 @@ local colors = {
 	debuff = {},
 	reaction = {},
 	power = {},
-	threat = {},
+	happiness = {
+		[1] = {1, 0, 0}, -- need.... | unhappy
+		[2] = {1, 1, 0}, -- new..... | content
+		[3] = {0, 1, 0}, -- colors.. | happy
+	},
 }

 -- We do this because people edit the vars directly, and changing the default
@@ -117,16 +121,6 @@ colors.power[16] = colors.power.ARCANE_CHARGES
 colors.power[17] = colors.power.FURY
 colors.power[18] = colors.power.PAIN

--- alternate power, sourced from FrameXML/CompactUnitFrame.lua
-colors.power.ALTERNATE = {0.7, 0.7, 0.6}
-colors.power[10] = colors.power.ALTERNATE
-
-for i = 0, 3 do
-	if GetThreatStatusColor then
-		colors.threat[i] = {GetThreatStatusColor(i)}
-	end
-end
-
 local function colorsAndPercent(a, b, ...)
 	if(a <= 0 or b == 0) then
 		return nil, ...
@@ -271,4 +265,4 @@ oUF.colors = colors
 oUF.useHCYColorGradient = false

 frame_metatable.__index.colors = colors
-frame_metatable.__index.ColorGradient = oUF.ColorGradient
+frame_metatable.__index.ColorGradient = oUF.ColorGradient
\ No newline at end of file
diff --git a/oUF/combatevents.lua b/oUF/combatevents.lua
new file mode 100644
index 0000000..d54ad3c
--- /dev/null
+++ b/oUF/combatevents.lua
@@ -0,0 +1,116 @@
+local _, ns = ...
+local oUF = ns.oUF
+local Private = oUF.Private
+
+local argcheck = Private.argcheck
+local frame_metatable = Private.frame_metatable
+
+local CombatLogGetCurrentEventInfo = _G.CombatLogGetCurrentEventInfo
+
+local event_metatable = {
+	__call = function(funcs, ...)
+		for self, func in next, funcs do
+			if (self:IsVisible()) then
+				func(self, ...)
+			end
+		end
+	end,
+}
+
+local self_metatable = {
+	__call = function(funcs, self, ...)
+		for _, func in next, funcs do
+			func(self, ...)
+		end
+	end
+}
+
+local listener = CreateFrame('Frame')
+listener.activeEvents = 0
+
+local function filter(_, event, ...)
+	if(listener[event]) then
+		listener[event](event, ...)
+	end
+end
+
+listener:SetScript('OnEvent', function(self, event)
+	filter(CombatLogGetCurrentEventInfo())
+end)
+
+--[[ CombatEvents: frame:RegisterCombatEvent(event, handler)
+Used to register a frame for a combat log event and add an event handler.
+
+* self     - frame that will be registered for the given event
+* event    - name of the combat log event to register (string)
+* handler  - function which will be executed when the combat log event fires. Multiple handlers can be added for the
+             same frame and event (function)
+--]]
+function frame_metatable.__index:RegisterCombatEvent(event, handler)
+	argcheck(event, 2, 'string')
+	argcheck(handler, 3, 'function')
+
+	if(not listener[event]) then
+		listener[event] = setmetatable({}, event_metatable)
+		listener.activeEvents = listener.activeEvents + 1
+	end
+
+	local current = listener[event][self]
+
+	if(current) then
+		for _, func in next, current do
+			if(func == handler) then return end
+		end
+
+		table.insert(current, handler)
+	else
+		-- even with a single handler we want to make sure the frame is visible
+		listener[event][self] = setmetatable({handler}, self_metatable)
+	end
+
+	if(listener.activeEvents > 0) then
+		listener:RegisterEvent('COMBAT_LOG_EVENT_UNFILTERED')
+	end
+end
+
+--[[ CombatEvents: frame:UnregisterCombatEvent(event, handler)
+Used to remove a function from the event handler list for a combat log event.
+
+* self    - the frame registered for the event
+* event   - name of the registered combat log event (string)
+* handler - function to be removed from the list of event handlers
+--]]
+function frame_metatable.__index:UnregisterCombatEvent(event, handler)
+	argcheck(event, 2, 'string')
+
+	if(not listener[event]) then return end
+
+	local cleanUp = false
+	local current = listener[event][self]
+	if(current) then
+		for i, func in next, current do
+			if(func == handler) then
+				current[i] = nil
+
+				break
+			end
+		end
+
+		if(not next(current)) then
+			cleanUp = true
+		end
+	end
+
+	if(cleanUp) then
+		listener[event][self] = nil
+
+		if(not next(listener[event])) then
+			listener[event] = nil
+			listener.activeEvents = listener.activeEvents - 1
+
+			if(listener.activeEvents <= 0) then
+				listener:UnregisterEvent('COMBAT_LOG_EVENT_UNFILTERED')
+			end
+		end
+	end
+end
diff --git a/oUF/elements/additionalpower.lua b/oUF/elements/additionalpower.lua
index bcd70fb..8fbaa52 100644
--- a/oUF/elements/additionalpower.lua
+++ b/oUF/elements/additionalpower.lua
@@ -1,8 +1,7 @@
 --[[
 # Element: Additional Power Bar

-Handles the visibility and updating of a status bar that displays the player's additional power, such as Mana for
-Balance druids.
+Handles the visibility and updating of a status bar that displays the player's additional power, such as Mana for druids.

 ## Widget

@@ -18,18 +17,18 @@ A default texture will be applied if the widget is a StatusBar and doesn't have

 ## Options

-.frequentUpdates - Indicates whether to use UNIT_POWER_FREQUENT instead UNIT_POWER_UPDATE to update the bar (boolean)
-.displayPairs    - Use to override display pairs. (table)
-.smoothGradient  - 9 color values to be used with the .colorSmooth option (table)
+.frequentUpdates                  - Indicates whether to use UNIT_POWER_FREQUENT instead UNIT_POWER_UPDATE to update the
+                                    bar (boolean)
+.smoothGradient                   - 9 color values to be used with the .colorSmooth option (table)

 The following options are listed by priority. The first check that returns true decides the color of the bar.

-.colorPower  - Use `self.colors.power[token]` to color the bar based on the player's additional power type
-               (boolean)
-.colorClass  - Use `self.colors.class[class]` to color the bar based on unit class. `class` is defined by the
-               second return of [UnitClass](http://wowprogramming.com/docs/api/UnitClass.html) (boolean)
-.colorSmooth - Use `self.colors.smooth` to color the bar with a smooth gradient based on the player's current
-               additional power percentage (boolean)
+.colorPower        - Use `self.colors.power[token]` to color the bar based on the player's additional power type
+                     (boolean)
+.colorClass        - Use `self.colors.class[class]` to color the bar based on unit class. `class` is defined by the
+                     second return of [UnitClass](http://wowprogramming.com/docs/api/UnitClass.html) (boolean)
+.colorSmooth       - Use `self.colors.smooth` to color the bar with a smooth gradient based on the player's current
+                     additional power percentage (boolean)

 ## Sub-Widget Options

@@ -54,25 +53,21 @@ The following options are listed by priority. The first check that returns true
     self.AdditionalPower = AdditionalPower
 --]]

+if(select(2, UnitClass('player')) ~= 'DRUID') then return end
+
 local _, ns = ...
 local oUF = ns.oUF

-local _, playerClass = UnitClass('player')
-
--- sourced from FrameXML/AlternatePowerBar.lua
-local ADDITIONAL_POWER_BAR_NAME = ADDITIONAL_POWER_BAR_NAME or 'MANA'
-local ADDITIONAL_POWER_BAR_INDEX = ADDITIONAL_POWER_BAR_INDEX or 0
-local ALT_MANA_BAR_PAIR_DISPLAY_INFO = ALT_MANA_BAR_PAIR_DISPLAY_INFO
-
-local function UpdateColor(self, event, unit, powerType)
-	if(not (unit and UnitIsUnit(unit, 'player') and powerType == ADDITIONAL_POWER_BAR_NAME)) then return end
+local function UpdateColor(self, event, unit, powertype)
+	if(not (unit and unit == 'player') and powertype == 'MANA') then return end
 	local element = self.AdditionalPower

 	local r, g, b, t
 	if(element.colorPower) then
-		t = self.colors.power[ADDITIONAL_POWER_BAR_INDEX]
-	elseif(element.colorClass) then
-		t = self.colors.class[playerClass]
+		t = self.colors.power[0]
+	elseif(element.colorClass and UnitIsPlayer(unit)) then
+		local _, class = UnitClass(unit)
+		t = self.colors.class[class]
 	elseif(element.colorSmooth) then
 		r, g, b = self:ColorGradient(element.cur or 1, element.max or 1, unpack(element.smoothGradient or self.colors.smooth))
 	end
@@ -91,23 +86,27 @@ local function UpdateColor(self, event, unit, powerType)
 		end
 	end

-	--[[ Callback: AdditionalPower:PostUpdateColor(r, g, b)
-	Called after the element color has been updated.
-
-	* self - the AdditionalPower element
-	* r    - the red component of the used color (number)[0-1]
-	* g    - the green component of the used color (number)[0-1]
-	* b    - the blue component of the used color (number)[0-1]
-	--]]
 	if(element.PostUpdateColor) then
-		element:PostUpdateColor(r, g, b)
+		element:PostUpdateColor(unit, r, g, b)
 	end
 end

-local function Update(self, event, unit, powerType)
-	if(not (unit and UnitIsUnit(unit, 'player') and powerType == ADDITIONAL_POWER_BAR_NAME)) then return end
-	local element = self.AdditionalPower
+local function ColorPath(self, ...)
+	--[[ Override: AdditionalPower.UpdateColor(self, event, unit, ...)
+	Used to completely override the internal function for updating the widgets' colors.

+	* self  - the parent object
+	* event - the event triggering the update (string)
+	* unit  - the unit accompanying the event (string)
+	* ...   - the arguments accompanying the event
+	--]]
+	(self.AdditionalPower.UpdateColor or UpdateColor) (self, ...)
+end
+
+local function Update(self, event, unit, powertype)
+	if(not (unit and unit == 'player') and powertype == 'MANA') then return end
+
+	local element = self.AdditionalPower
 	--[[ Callback: AdditionalPower:PreUpdate(unit)
 	Called before the element has been updated.

@@ -118,22 +117,25 @@ local function Update(self, event, unit, powerType)
 		element:PreUpdate(unit)
 	end

-	local cur, max = UnitPower('player', ADDITIONAL_POWER_BAR_INDEX), UnitPowerMax('player', ADDITIONAL_POWER_BAR_INDEX)
+	local cur = UnitPower('player', 0)
+	local max = UnitPowerMax('player', 0)
+
 	element:SetMinMaxValues(0, max)
 	element:SetValue(cur)

 	element.cur = cur
 	element.max = max

-	--[[ Callback: AdditionalPower:PostUpdate(cur, max)
+	--[[ Callback: AdditionalPower:PostUpdate(unit, cur, max)
 	Called after the element has been updated.

 	* self - the AdditionalPower element
+	* unit - the unit for which the update has been triggered (string)
 	* cur  - the current value of the player's additional power (number)
 	* max  - the maximum value of the player's additional power (number)
 	--]]
 	if(element.PostUpdate) then
-		return element:PostUpdate(cur, max)
+		return element:PostUpdate(unit, cur, max)
 	end
 end

@@ -146,17 +148,9 @@ local function Path(self, ...)
 	* unit  - the unit accompanying the event (string)
 	* ...   - the arguments accompanying the event
 	--]]
-	(self.AdditionalPower.Override or Update) (self, ...);
+	(self.AdditionalPower.Override or Update) (self, ...)

-	--[[ Override: AdditionalPower.UpdateColor(self, event, unit, ...)
-	Used to completely override the internal function for updating the widgets' colors.
-
-	* self  - the parent object
-	* event - the event triggering the update (string)
-	* unit  - the unit accompanying the event (string)
-	* ...   - the arguments accompanying the event
-	--]]
-	(self.AdditionalPower.UpdateColor or UpdateColor) (self, ...)
+	ColorPath(self, ...)
 end

 local function ElementEnable(self)
@@ -172,58 +166,36 @@ local function ElementEnable(self)

 	element:Show()

-	element.__isEnabled = true
-	Path(self, 'ElementEnable', 'player', ADDITIONAL_POWER_BAR_NAME)
+	Path(self, 'ElementEnable', 'player', 'MANA')
 end

 local function ElementDisable(self)
 	local element = self.AdditionalPower

+	if(element.frequentUpdates) then
+		self:RegisterEvent('UNIT_POWER_FREQUENT', Path)
+	else
+		self:RegisterEvent('UNIT_POWER_UPDATE', Path)
+	end
+
 	self:UnregisterEvent('UNIT_MAXPOWER', Path)
-	self:UnregisterEvent('UNIT_POWER_FREQUENT', Path)
-	self:UnregisterEvent('UNIT_POWER_UPDATE', Path)

-	element:Hide()
+	self.AdditionalPower:Hide()

-	element.__isEnabled = false
-	Path(self, 'ElementDisable', 'player', ADDITIONAL_POWER_BAR_NAME)
+	Path(self, 'ElementDisable', 'player', 'MANA')
 end

-local function Visibility(self, event, unit)
-	local element = self.AdditionalPower
+local function Visibility(self)
 	local shouldEnable

-	if(not UnitHasVehicleUI('player')) then
-		if(UnitPowerMax(unit, ADDITIONAL_POWER_BAR_INDEX) ~= 0) then
-			if(element.displayPairs[playerClass]) then
-				local powerType = UnitPowerType(unit)
-				shouldEnable = element.displayPairs[playerClass][powerType]
-			end
-		end
+	if((UnitPowerType('player') ~= 0) and (UnitPowerMax('player', 0) ~= 0)) then
+		shouldEnable = true
 	end

-	local isEnabled = element.__isEnabled
-
-	if(shouldEnable and not isEnabled) then
+	if(shouldEnable) then
 		ElementEnable(self)
-
-		--[[ Callback: AdditionalPower:PostVisibility(isVisible)
-		Called after the element's visibility has been changed.
-
-		* self      - the AdditionalPower element
-		* isVisible - the current visibility state of the element (boolean)
-		--]]
-		if(element.PostVisibility) then
-			element:PostVisibility(true)
-		end
-	elseif(not shouldEnable and (isEnabled or isEnabled == nil)) then
+	else
 		ElementDisable(self)
-
-		if(element.PostVisibility) then
-			element:PostVisibility(false)
-		end
-	elseif(shouldEnable and isEnabled) then
-		Path(self, event, unit, ADDITIONAL_POWER_BAR_NAME)
 	end
 end

@@ -235,47 +207,22 @@ local function VisibilityPath(self, ...)
 	* event - the event triggering the update (string)
 	* unit  - the unit accompanying the event (string)
 	--]]
-	(self.AdditionalPower.OverrideVisibility or Visibility) (self, ...)
+	return (self.AdditionalPower.OverrideVisibility or Visibility) (self, ...)
 end

 local function ForceUpdate(element)
-	VisibilityPath(element.__owner, 'ForceUpdate', element.__owner.unit)
-end
-
---[[ Power:SetFrequentUpdates(state, isForced)
-Used to toggle frequent updates.
-
-* self  - the Power element
-* state - the desired state (boolean)
-* isForced - forces the event update even if the state wasn't changed (boolean)
---]]
-local function SetFrequentUpdates(element, state, isForced)
-	if(element.frequentUpdates ~= state or isForced) then
-		element.frequentUpdates = state
-		if(state) then
-			element.__owner:UnregisterEvent('UNIT_POWER_UPDATE', Path)
-			element.__owner:RegisterEvent('UNIT_POWER_FREQUENT', Path)
-		else
-			element.__owner:UnregisterEvent('UNIT_POWER_FREQUENT', Path)
-			element.__owner:RegisterEvent('UNIT_POWER_UPDATE', Path)
-		end
-	end
+	return VisibilityPath(element.__owner, 'ForceUpdate', element.__owner.unit)
 end

 local function Enable(self, unit)
 	local element = self.AdditionalPower
-	if(element and UnitIsUnit(unit, 'player')) then
+	if(element and unit == 'player') then
 		element.__owner = self
 		element.ForceUpdate = ForceUpdate
-		element.SetFrequentUpdates = SetFrequentUpdates

 		self:RegisterEvent('UNIT_DISPLAYPOWER', VisibilityPath)

-		if(not element.displayPairs) then
-			element.displayPairs = CopyTable(ALT_MANA_BAR_PAIR_DISPLAY_INFO)
-		end
-
-		if(element:IsObjectType('StatusBar') and not (element:GetStatusBarTexture() or element:GetStatusBarAtlas())) then
+		if(element:IsObjectType('StatusBar') and not element:GetStatusBarTexture()) then
 			element:SetStatusBarTexture([[Interface\TargetingFrame\UI-StatusBar]])
 		end

@@ -292,4 +239,4 @@ local function Disable(self)
 	end
 end

-oUF:AddElement('AdditionalPower', VisibilityPath, Enable, Disable)
+oUF:AddElement('AdditionalPower', VisibilityPath, Enable, Disable)
\ No newline at end of file
diff --git a/oUF/elements/auras.lua b/oUF/elements/auras.lua
index 3d2d52a..d1ee6a5 100644
--- a/oUF/elements/auras.lua
+++ b/oUF/elements/auras.lua
@@ -74,24 +74,17 @@ local VISIBLE = 1
 local HIDDEN = 0

 local function UpdateTooltip(self)
-	if(GameTooltip:IsForbidden()) then return end
-
 	GameTooltip:SetUnitAura(self:GetParent().__owner.unit, self:GetID(), self.filter)
 end

 local function onEnter(self)
-	if(GameTooltip:IsForbidden() or not self:IsVisible()) then return end
+	if(not self:IsVisible()) then return end

-	-- Avoid parenting GameTooltip to frames with anchoring restrictions,
-	-- otherwise it'll inherit said restrictions which will cause issues with
-	-- its further positioning, clamping, etc
-	GameTooltip:SetOwner(self, self:GetParent().__restricted and 'ANCHOR_CURSOR' or self:GetParent().tooltipAnchor)
+	GameTooltip:SetOwner(self, self:GetParent().tooltipAnchor)
 	self:UpdateTooltip()
 end

 local function onLeave()
-	if(GameTooltip:IsForbidden()) then return end
-
 	GameTooltip:Hide()
 end

@@ -151,9 +144,7 @@ local function customFilter(element, unit, button, name)
 end

 local function updateIcon(element, unit, index, offset, filter, isDebuff, visible)
-	local name, texture, count, debuffType, duration, expiration, caster, isStealable,
-		nameplateShowSelf, spellID, canApply, isBossDebuff, casterIsPlayer, nameplateShowAll,
-		timeMod, effect1, effect2, effect3 = UnitAura(unit, index, filter)
+	local name, texture, count, debuffType, duration, expiration, caster, isStealable, nameplateShowSelf, spellID, canApply, isBossDebuff, casterIsPlayer, nameplateShowAll, timeMod, effect1, effect2, effect3 = UnitAura(unit, index, filter)

 	if(name) then
 		local position = visible + offset + 1
@@ -492,13 +483,19 @@ local function Enable(self)
 		local buffs = self.Buffs
 		if(buffs) then
 			buffs.__owner = self
-			-- check if there's any anchoring restrictions
-			buffs.__restricted = not pcall(self.GetCenter, self)
 			buffs.ForceUpdate = ForceUpdate

 			buffs.createdIcons = buffs.createdIcons or 0
 			buffs.anchoredIcons = 0
-			buffs.tooltipAnchor = buffs.tooltipAnchor or 'ANCHOR_BOTTOMRIGHT'
+
+			-- Avoid parenting GameTooltip to frames with anchoring restrictions,
+			-- otherwise it'll inherit said restrictions which will cause issues
+			-- with its further positioning, clamping, etc
+			if(not pcall(self.GetCenter, self)) then
+				buffs.tooltipAnchor = 'ANCHOR_CURSOR'
+			else
+				buffs.tooltipAnchor = buffs.tooltipAnchor or 'ANCHOR_BOTTOMRIGHT'
+			end

 			buffs:Show()
 		end
@@ -506,13 +503,19 @@ local function Enable(self)
 		local debuffs = self.Debuffs
 		if(debuffs) then
 			debuffs.__owner = self
-			-- check if there's any anchoring restrictions
-			debuffs.__restricted = not pcall(self.GetCenter, self)
 			debuffs.ForceUpdate = ForceUpdate

 			debuffs.createdIcons = debuffs.createdIcons or 0
 			debuffs.anchoredIcons = 0
-			debuffs.tooltipAnchor = debuffs.tooltipAnchor or 'ANCHOR_BOTTOMRIGHT'
+
+			-- Avoid parenting GameTooltip to frames with anchoring restrictions,
+			-- otherwise it'll inherit said restrictions which will cause issues
+			-- with its further positioning, clamping, etc
+			if(not pcall(self.GetCenter, self)) then
+				debuffs.tooltipAnchor = 'ANCHOR_CURSOR'
+			else
+				debuffs.tooltipAnchor = debuffs.tooltipAnchor or 'ANCHOR_BOTTOMRIGHT'
+			end

 			debuffs:Show()
 		end
@@ -520,13 +523,19 @@ local function Enable(self)
 		local auras = self.Auras
 		if(auras) then
 			auras.__owner = self
-			-- check if there's any anchoring restrictions
-			auras.__restricted = not pcall(self.GetCenter, self)
 			auras.ForceUpdate = ForceUpdate

 			auras.createdIcons = auras.createdIcons or 0
 			auras.anchoredIcons = 0
-			auras.tooltipAnchor = auras.tooltipAnchor or 'ANCHOR_BOTTOMRIGHT'
+
+			-- Avoid parenting GameTooltip to frames with anchoring restrictions,
+			-- otherwise it'll inherit said restrictions which will cause issues
+			-- with its further positioning, clamping, etc
+			if(not pcall(self.GetCenter, self)) then
+				auras.tooltipAnchor = 'ANCHOR_CURSOR'
+			else
+				auras.tooltipAnchor = auras.tooltipAnchor or 'ANCHOR_BOTTOMRIGHT'
+			end

 			auras:Show()
 		end
@@ -545,4 +554,4 @@ local function Disable(self)
 	end
 end

-oUF:AddElement('Auras', Update, Enable, Disable)
+oUF:AddElement('Auras', Update, Enable, Disable)
\ No newline at end of file
diff --git a/oUF/elements/castbar.lua b/oUF/elements/castbar.lua
index f43797f..a4e8cb1 100644
--- a/oUF/elements/castbar.lua
+++ b/oUF/elements/castbar.lua
@@ -2,6 +2,7 @@
 # Element: Castbar

 Handles the visibility and updating of spell castbars.
+Based upon oUF_Castbar by starlon.

 ## Widget

@@ -9,12 +10,11 @@ Castbar - A `StatusBar` to represent spell cast/channel progress.

 ## Sub-Widgets

-.Icon     - A `Texture` to represent spell icon.
-.SafeZone - A `Texture` to represent latency.
-.Shield   - A `Texture` to represent if it's possible to interrupt or spell steal.
-.Spark    - A `Texture` to represent the castbar's edge.
 .Text     - A `FontString` to represent spell name.
+.Icon     - A `Texture` to represent spell icon.
 .Time     - A `FontString` to represent spell duration.
+.Shield   - A `Texture` to represent if it's possible to interrupt or spell steal.
+.SafeZone - A `Texture` to represent latency.

 ## Notes

@@ -22,17 +22,16 @@ A default texture will be applied to the StatusBar and Texture widgets if they d

 ## Options

-.timeToHold      - Indicates for how many seconds the castbar should be visible after a _FAILED or _INTERRUPTED
-                   event. Defaults to 0 (number)
-.hideTradeSkills - Makes the element ignore casts related to crafting professions (boolean)
+.timeToHold - indicates for how many seconds the castbar should be visible after a _FAILED or _INTERRUPTED
+              event. Defaults to 0 (number)

 ## Attributes

-.castID           - A globally unique identifier of the currently cast spell (string?)
-.casting          - Indicates whether the current spell is an ordinary cast (boolean)
-.channeling       - Indicates whether the current spell is a channeled cast (boolean)
-.notInterruptible - Indicates whether the current spell is interruptible (boolean)
-.spellID          - The spell identifier of the currently cast/channeled spell (number)
+.castID           - a globally unique identifier of the currently cast spell (string?)
+.casting          - indicates whether the current spell is an ordinary cast (boolean)
+.channeling       - indicates whether the current spell is a channeled cast (boolean)
+.notInterruptible - indicates whether the current spell is interruptible (boolean)
+.spellID          - the spell identifier of the currently cast/channeled spell (number)

 ## Examples

@@ -52,7 +51,6 @@ A default texture will be applied to the StatusBar and Texture widgets if they d
     local Spark = Castbar:CreateTexture(nil, 'OVERLAY')
     Spark:SetSize(20, 20)
     Spark:SetBlendMode('ADD')
-    Spark:SetPoint('CENTER', Castbar:GetStatusBarTexture(), 'RIGHT', 0, 0)

     -- Add a timer
     local Time = Castbar:CreateFontString(nil, 'OVERLAY', 'GameFontNormalSmall')
@@ -85,291 +83,423 @@ A default texture will be applied to the StatusBar and Texture widgets if they d
     Castbar.SafeZone = SafeZone
     self.Castbar = Castbar
 --]]
-
 local _, ns = ...
 local oUF = ns.oUF

-local FALLBACK_ICON = 136243 -- Interface\ICONS\Trade_Engineering
+local GetNetStats = GetNetStats
+local GetTime = GetTime
+
+local function updateSafeZone(self)
+	local safeZone = self.SafeZone
+	local width = self:GetWidth()
+	local _, _, _, ms = GetNetStats()

-local function resetAttributes(self)
-	self.castID = nil
-	self.casting = nil
-	self.channeling = nil
-	self.notInterruptible = nil
-	self.spellID = nil
+	local safeZoneRatio = (ms / 1e3) / self.max
+	if(safeZoneRatio > 1) then
+		safeZoneRatio = 1
+	end
+
+	safeZone:SetWidth(width * safeZoneRatio)
 end

-local function CastStart(self, event, unit)
-	if(self.unit ~= unit) then return end
+local function UNIT_SPELLCAST_START(self, event, unit)
+	if(self.unit ~= unit and self.realUnit ~= unit) then return end

 	local element = self.Castbar

-	local name, _, texture, startTime, endTime, isTradeSkill, castID, notInterruptible, spellID = UnitCastingInfo(unit)
-	event = 'UNIT_SPELLCAST_START'
-	if(not name) then
-		name, _, texture, startTime, endTime, isTradeSkill, notInterruptible, spellID = UnitChannelInfo(unit)
-		event = 'UNIT_SPELLCAST_CHANNEL_START'
-	end
+	local name, text, texture, startTime, endTime, _, castID, notInterruptible, spellID = UnitCastingInfo(unit)

-	if(not name or (isTradeSkill and element.hideTradeSkills)) then
-		resetAttributes(element)
-		element:Hide()
-
-		return
+	if(not name) then
+		return element:Hide()
 	end

-	endTime = endTime / 1000
-	startTime = startTime / 1000
+	endTime = endTime / 1e3
+	startTime = startTime / 1e3
+	local max = endTime - startTime

-	element.max = endTime - startTime
-	element.startTime = startTime
+	element.castID = castID
+	element.duration = GetTime() - startTime
+	element.max = max
 	element.delay = 0
-	element.casting = event == 'UNIT_SPELLCAST_START'
-	element.channeling = event == 'UNIT_SPELLCAST_CHANNEL_START'
+	element.casting = true
 	element.notInterruptible = notInterruptible
 	element.holdTime = 0
-	element.castID = castID
 	element.spellID = spellID

-	if(element.casting) then
-		element.duration = GetTime() - startTime
-	else
-		element.duration = endTime - GetTime()
-	end
-
-	element:SetMinMaxValues(0, element.max)
-	element:SetValue(element.duration)
+	element:SetMinMaxValues(0, max)
+	element:SetValue(0)

-	if(element.Icon) then element.Icon:SetTexture(texture or FALLBACK_ICON) end
-	if(element.Shield) then element.Shield:SetShown(notInterruptible) end
-	if(element.Spark) then element.Spark:Show() end
-	if(element.Text) then element.Text:SetText(name) end
+	if(element.Text) then element.Text:SetText(text) end
+	if(element.Icon) then element.Icon:SetTexture(texture) end
 	if(element.Time) then element.Time:SetText() end

-	local safeZone = element.SafeZone
-	if(safeZone) then
-		local isHoriz = element:GetOrientation() == 'HORIZONTAL'
+	local shield = element.Shield
+	if(shield and notInterruptible) then
+		shield:Show()
+	elseif(shield) then
+		shield:Hide()
+	end

-		safeZone:ClearAllPoints()
-		safeZone:SetPoint(isHoriz and 'TOP' or 'LEFT')
-		safeZone:SetPoint(isHoriz and 'BOTTOM' or 'RIGHT')
+	local sf = element.SafeZone
+	if(sf) then
+		sf:ClearAllPoints()
+		sf:SetPoint(element:GetReverseFill() and 'LEFT' or 'RIGHT')
+		sf:SetPoint('TOP')
+		sf:SetPoint('BOTTOM')
+		updateSafeZone(element)
+	end

-		if(element.casting) then
-			safeZone:SetPoint(element:GetReverseFill() and (isHoriz and 'LEFT' or 'BOTTOM') or (isHoriz and 'RIGHT' or 'TOP'))
-		else
-			safeZone:SetPoint(element:GetReverseFill() and (isHoriz and 'RIGHT' or 'TOP') or (isHoriz and 'LEFT' or 'BOTTOM'))
-		end
+	--[[ Callback: Castbar:PostCastStart(unit, name)
+	Called after the element has been updated upon a spell cast start.

-		local ratio = (select(4, GetNetStats()) / 1000) / element.max
-		if(ratio > 1) then
-			ratio = 1
-		elseif(ratio == 0) then
-			ratio = 0.001
-		end
+	* self - the Castbar widget
+	* unit - unit for which the update has been triggered (string)
+	* name - name of the spell being cast (string)
+	--]]
+	if(element.PostCastStart) then
+		element:PostCastStart(unit, name)
+	end
+	element:Show()
+end
+
+local function UNIT_SPELLCAST_FAILED(self, event, unit, castID)
+	if(self.unit ~= unit and self.realUnit ~= unit) then return end

-		safeZone[isHoriz and 'SetWidth' or 'SetHeight'](safeZone, element[isHoriz and 'GetWidth' or 'GetHeight'](element) * ratio)
+	local element = self.Castbar
+	if(element.castID ~= castID) then
+		return
 	end

-	--[[ Callback: Castbar:PostCastStart(unit)
-	Called after the element has been updated upon a spell cast or channel start.
+	local text = element.Text
+	if(text) then
+		text:SetText(FAILED)
+	end
+
+	element.casting = nil
+	element.notInterruptible = nil
+	element.holdTime = element.timeToHold or 0
+
+	--[[ Callback: Castbar:PostCastFailed(unit)
+	Called after the element has been updated upon a failed spell cast.

 	* self - the Castbar widget
-	* unit - the unit for which the update has been triggered (string)
+	* unit - unit for which the update has been triggered (string)
 	--]]
-	if(element.PostCastStart) then
-		element:PostCastStart(unit)
+	if(element.PostCastFailed) then
+		return element:PostCastFailed(unit)
 	end
-
-	element:Show()
 end

-local function CastUpdate(self, event, unit, castID, spellID)
-	if(self.unit ~= unit) then return end
+local function UNIT_SPELLCAST_INTERRUPTED(self, event, unit, castID)
+	if(self.unit ~= unit and self.realUnit ~= unit) then return end

 	local element = self.Castbar
-	if(not element:IsShown() or element.castID ~= castID or element.spellID ~= spellID) then
+	if(element.castID ~= castID) then
 		return
 	end

-	local name, startTime, endTime, _
-	if(event == 'UNIT_SPELLCAST_DELAYED') then
-		name, _, _, startTime, endTime = UnitCastingInfo(unit)
-	else
-		name, _, _, startTime, endTime = UnitChannelInfo(unit)
+	local text = element.Text
+	if(text) then
+		text:SetText(INTERRUPTED)
 	end

-	if(not name) then return end
+	element.casting = nil
+	element.channeling = nil
+	element.holdTime = element.timeToHold or 0

-	endTime = endTime / 1000
-	startTime = startTime / 1000
+	--[[ Callback: Castbar:PostCastInterrupted(unit)
+	Called after the element has been updated upon an interrupted spell cast.

-	local delta
-	if(element.casting) then
-		delta = startTime - element.startTime
+	* self - the Castbar widget
+	* unit - unit for which the update has been triggered (string)
+	--]]
+	if(element.PostCastInterrupted) then
+		return element:PostCastInterrupted(unit)
+	end
+end

-		element.duration = GetTime() - startTime
-	else
-		delta = element.startTime - startTime
+local function UNIT_SPELLCAST_NOT_INTERRUPTIBLE(self, event, unit)
+	if(self.unit ~= unit and self.realUnit ~= unit) then return end

-		element.duration = endTime - GetTime()
+	local element = self.Castbar
+	local shield = element.Shield
+	if(shield) then
+		shield:Show()
 	end

-	if(delta < 0) then
-		delta = 0
+	element.notInterruptible = true
+
+	--[[ Callback: Castbar:PostCastNotInterruptible(unit)
+	Called after the element has been updated when a spell cast has become non-interruptible.
+
+	* self - the Castbar widget
+	* unit - unit for which the update has been triggered (string)
+	--]]
+	if(element.PostCastNotInterruptible) then
+		return element:PostCastNotInterruptible(unit)
 	end
+end

-	element.max = endTime - startTime
-	element.startTime = startTime
-	element.delay = element.delay + delta
+local function UNIT_SPELLCAST_DELAYED(self, event, unit)
+	if(self.unit ~= unit and self.realUnit ~= unit) then return end

-	element:SetMinMaxValues(0, element.max)
-	element:SetValue(element.duration)
+	local element = self.Castbar
+	local name, _, _, startTime = UnitCastingInfo(unit)
+	if(not startTime or not element:IsShown()) then return end
+
+	local duration = GetTime() - (startTime / 1000)
+	if(duration < 0) then duration = 0 end
+
+	element.delay = element.delay + element.duration - duration
+	element.duration = duration

-	--[[ Callback: Castbar:PostCastUpdate(unit)
-	Called after the element has been updated when a spell cast or channel has been updated.
+	element:SetValue(duration)
+
+	--[[ Callback: Castbar:PostCastDelayed(unit, name)
+	Called after the element has been updated when a spell cast has been delayed.

 	* self - the Castbar widget
-	* unit - the unit that the update has been triggered (string)
+	* unit - unit that the update has been triggered (string)
+	* name - name of the delayed spell (string)
 	--]]
-	if(element.PostCastUpdate) then
-		return element:PostCastUpdate(unit)
+	if(element.PostCastDelayed) then
+		return element:PostCastDelayed(unit, name)
 	end
 end

-local function CastStop(self, event, unit, castID, spellID)
-	if(self.unit ~= unit) then return end
+local function UNIT_SPELLCAST_STOP(self, event, unit, castID)
+	if(self.unit ~= unit and self.realUnit ~= unit) then return end

 	local element = self.Castbar
-	if(not element:IsShown() or element.castID ~= castID or element.spellID ~= spellID) then
+	if(element.castID ~= castID) then
 		return
 	end

-	resetAttributes(element)
+	element.casting = nil
+	element.notInterruptible = nil

-	--[[ Callback: Castbar:PostCastStop(unit, spellID)
-	Called after the element has been updated when a spell cast or channel has stopped.
+	--[[ Callback: Castbar:PostCastStop(unit)
+	Called after the element has been updated when a spell cast has finished.

-	* self    - the Castbar widget
-	* unit    - the unit for which the update has been triggered (string)
-	* spellID - the ID of the spell (number)
+	* self - the Castbar widget
+	* unit - unit for which the update has been triggered (string)
 	--]]
 	if(element.PostCastStop) then
-		return element:PostCastStop(unit, spellID)
+		return element:PostCastStop(unit)
 	end
 end

-local function CastFail(self, event, unit, castID, spellID)
-	if(self.unit ~= unit) then return end
+local function UNIT_SPELLCAST_CHANNEL_START(self, event, unit, _, spellID)
+	if(self.unit ~= unit and self.realUnit ~= unit) then return end

 	local element = self.Castbar
-	if(not element:IsShown() or element.castID ~= castID or element.spellID ~= spellID) then
+
+	local name, _, texture, startTime, endTime, _, notInterruptible, spellID = UnitChannelInfo(unit)
+
+	if(not name) then
 		return
 	end

-	if(element.Text) then
-		element.Text:SetText(event == 'UNIT_SPELLCAST_FAILED' and FAILED or INTERRUPTED)
-	end
+	endTime = endTime / 1e3
+	startTime = startTime / 1e3
+	local max = (endTime - startTime)
+	local duration = endTime - GetTime()

-	if(element.Spark) then element.Spark:Hide() end
+	element.duration = duration
+	element.max = max
+	element.delay = 0
+	element.channeling = true
+	element.notInterruptible = notInterruptible
+	element.holdTime = 0
+	element.spellID = spellID

-	element.holdTime = element.timeToHold or 0
+	-- We have to do this, as it's possible for spell casts to never have _STOP
+	-- executed or be fully completed by the OnUpdate handler before CHANNEL_START
+	-- is called.
+	element.casting = nil
+	element.castID = nil
+
+	element:SetMinMaxValues(0, max)
+	element:SetValue(duration)
+
+	if(element.Text) then element.Text:SetText(name) end
+	if(element.Icon) then element.Icon:SetTexture(texture) end
+	if(element.Time) then element.Time:SetText() end

-	resetAttributes(element)
-	element:SetValue(element.max)
+	local shield = element.Shield
+	if(shield and notInterruptible) then
+		shield:Show()
+	elseif(shield) then
+		shield:Hide()
+	end
+
+	local sf = element.SafeZone
+	if(sf) then
+		sf:ClearAllPoints()
+		sf:SetPoint(element:GetReverseFill() and 'RIGHT' or 'LEFT')
+		sf:SetPoint('TOP')
+		sf:SetPoint('BOTTOM')
+		updateSafeZone(element)
+	end

-	--[[ Callback: Castbar:PostCastFail(unit, spellID)
-	Called after the element has been updated upon a failed or interrupted spell cast.
+	--[[ Callback: Castbar:PostChannelStart(unit, name)
+	Called after the element has been updated upon a spell channel start.

-	* self    - the Castbar widget
-	* unit    - the unit for which the update has been triggered (string)
-	* spellID - the ID of the spell (number)
+	* self - the Castbar widget
+	* unit - unit for which the update has been triggered (string)
+	* name - name of the channeled spell (string)
 	--]]
-	if(element.PostCastFail) then
-		return element:PostCastFail(unit, spellID)
+	if(element.PostChannelStart) then
+		element:PostChannelStart(unit, name)
 	end
+	element:Show()
 end

-local function CastInterruptible(self, event, unit)
-	if(self.unit ~= unit) then return end
+local function UNIT_SPELLCAST_CHANNEL_UPDATE(self, event, unit)
+	if(self.unit ~= unit and self.realUnit ~= unit) then return end

 	local element = self.Castbar
-	if(not element:IsShown()) then return end
+	local name, _, _, startTime, endTime = UnitChannelInfo(unit)
+	if(not name or not element:IsShown()) then
+		return
+	end

-	element.notInterruptible = event == 'UNIT_SPELLCAST_NOT_INTERRUPTIBLE'
+	local duration = (endTime / 1000) - GetTime()

-	if(element.Shield) then element.Shield:SetShown(element.notInterruptible) end
+	element.delay = element.delay + element.duration - duration
+	element.duration = duration
+	element.max = (endTime - startTime) / 1000

-	--[[ Callback: Castbar:PostCastInterruptible(unit)
-	Called after the element has been updated when a spell cast has become interruptible or uninterruptible.
+	element:SetMinMaxValues(0, element.max)
+	element:SetValue(duration)
+
+	--[[ Callback: Castbar:PostChannelUpdate(unit, name)
+	Called after the element has been updated after a channeled spell has been delayed or interrupted.

 	* self - the Castbar widget
-	* unit - the unit for which the update has been triggered (string)
+	* unit - unit for which the update has been triggered (string)
+	* name - name of the channeled spell (string)
 	--]]
-	if(element.PostCastInterruptible) then
-		return element:PostCastInterruptible(unit)
+	if(element.PostChannelUpdate) then
+		return element:PostChannelUpdate(unit, name)
+	end
+end
+
+local function UNIT_SPELLCAST_CHANNEL_STOP(self, event, unit)
+	if(self.unit ~= unit and self.realUnit ~= unit) then return end
+
+	local element = self.Castbar
+	if(element:IsShown()) then
+		element.channeling = nil
+		element.notInterruptible = nil
+
+		--[[ Callback: Castbar:PostChannelStop(unit)
+		Called after the element has been updated after a channeled spell has been completed.
+
+		* self - the Castbar widget
+		* unit - unit for which the update has been triggered (string)
+		--]]
+		if(element.PostChannelStop) then
+			return element:PostChannelStop(unit)
+		end
 	end
 end

 local function onUpdate(self, elapsed)
-	if(self.casting or self.channeling) then
-		local isCasting = self.casting
-		if(isCasting) then
-			self.duration = self.duration + elapsed
-			if(self.duration >= self.max) then
-				local spellID = self.spellID
-
-				resetAttributes(self)
-				self:Hide()
-
-				if(self.PostCastStop) then
-					self:PostCastStop(self.__owner.unit, spellID)
-				end
+	if(self.casting) then
+		local duration = self.duration + elapsed
+		if(duration >= self.max) then
+			self.casting = nil
+			self:Hide()
+
+			if(self.PostCastStop) then self:PostCastStop(self.__owner.unit) end
+			return
+		end

-				return
+		if(self.Time) then
+			if(self.delay ~= 0) then
+				if(self.CustomDelayText) then
+					self:CustomDelayText(duration)
+				else
+					self.Time:SetFormattedText('%.1f|cffff0000-%.1f|r', duration, self.delay)
+				end
+			else
+				if(self.CustomTimeText) then
+					self:CustomTimeText(duration)
+				else
+					self.Time:SetFormattedText('%.1f', duration)
+				end
 			end
-		else
-			self.duration = self.duration - elapsed
-			if(self.duration <= 0) then
-				local spellID = self.spellID
+		end

-				resetAttributes(self)
-				self:Hide()
+		self.duration = duration
+		self:SetValue(duration)

-				if(self.PostCastStop) then
-					self:PostCastStop(self.__owner.unit, spellID)
-				end
+		if(self.Spark) then
+			local horiz = self.horizontal
+			local size = self[horiz and 'GetWidth' or 'GetHeight'](self)

-				return
+			local offset = (duration / self.max) * size
+			if(self:GetReverseFill()) then
+				offset = size - offset
 			end
+
+			self.Spark:SetPoint('CENTER', self, horiz and 'LEFT' or 'BOTTOM', horiz and offset or 0, horiz and 0 or offset)
+		end
+	elseif(self.channeling) then
+		local duration = self.duration - elapsed
+
+		if(duration <= 0) then
+			self.channeling = nil
+			self:Hide()
+
+			if(self.PostChannelStop) then self:PostChannelStop(self.__owner.unit) end
+			return
 		end

 		if(self.Time) then
 			if(self.delay ~= 0) then
 				if(self.CustomDelayText) then
-					self:CustomDelayText(self.duration)
+					self:CustomDelayText(duration)
 				else
-					self.Time:SetFormattedText('%.1f|cffff0000%s%.2f|r', self.duration, isCasting and '+' or '-', self.delay)
+					self.Time:SetFormattedText('%.1f|cffff0000-%.1f|r', duration, self.delay)
 				end
 			else
 				if(self.CustomTimeText) then
-					self:CustomTimeText(self.duration)
+					self:CustomTimeText(duration)
 				else
-					self.Time:SetFormattedText('%.1f', self.duration)
+					self.Time:SetFormattedText('%.1f', duration)
 				end
 			end
 		end

-		self:SetValue(self.duration)
+		self.duration = duration
+		self:SetValue(duration)
+		if(self.Spark) then
+			local horiz = self.horizontal
+			local size = self[horiz and 'GetWidth' or 'GetHeight'](self)
+
+			local offset = (duration / self.max) * size
+			if(self:GetReverseFill()) then
+				offset = size - offset
+			end
+
+			self.Spark:SetPoint('CENTER', self, horiz and 'LEFT' or 'BOTTOM', horiz and offset or 0, horiz and 0 or offset)
+		end
 	elseif(self.holdTime > 0) then
 		self.holdTime = self.holdTime - elapsed
 	else
-		resetAttributes(self)
+		self.casting = nil
+		self.castID = nil
+		self.channeling = nil
+
 		self:Hide()
 	end
 end

-local function Update(...)
-	CastStart(...)
+local function Update(self, ...)
+	UNIT_SPELLCAST_START(self, ...)
+	return UNIT_SPELLCAST_CHANNEL_START(self, ...)
 end

 local function ForceUpdate(element)
@@ -378,31 +508,33 @@ end

 local function Enable(self, unit)
 	local element = self.Castbar
-	if(element and unit and not unit:match('%wtarget$')) then
+	if(element) then
 		element.__owner = self
 		element.ForceUpdate = ForceUpdate

-		self:RegisterEvent('UNIT_SPELLCAST_START', CastStart)
-		self:RegisterEvent('UNIT_SPELLCAST_CHANNEL_START', CastStart)
-		self:RegisterEvent('UNIT_SPELLCAST_STOP', CastStop)
-		self:RegisterEvent('UNIT_SPELLCAST_CHANNEL_STOP', CastStop)
-		self:RegisterEvent('UNIT_SPELLCAST_DELAYED', CastUpdate)
-		self:RegisterEvent('UNIT_SPELLCAST_CHANNEL_UPDATE', CastUpdate)
-		self:RegisterEvent('UNIT_SPELLCAST_FAILED', CastFail)
-		self:RegisterEvent('UNIT_SPELLCAST_INTERRUPTED', CastFail)
-
-		if oUF.isRetail then
-			self:RegisterEvent('UNIT_SPELLCAST_INTERRUPTIBLE', CastInterruptible)
-			self:RegisterEvent('UNIT_SPELLCAST_NOT_INTERRUPTIBLE', CastInterruptible)
+		if(not (unit and unit:match'%wtarget$')) then
+			self:RegisterEvent('UNIT_SPELLCAST_START', UNIT_SPELLCAST_START)
+			self:RegisterEvent('UNIT_SPELLCAST_FAILED', UNIT_SPELLCAST_FAILED)
+			self:RegisterEvent('UNIT_SPELLCAST_STOP', UNIT_SPELLCAST_STOP)
+			self:RegisterEvent('UNIT_SPELLCAST_INTERRUPTED', UNIT_SPELLCAST_INTERRUPTED)
+			self:RegisterEvent('UNIT_SPELLCAST_DELAYED', UNIT_SPELLCAST_DELAYED)
+			self:RegisterEvent('UNIT_SPELLCAST_CHANNEL_START', UNIT_SPELLCAST_CHANNEL_START)
+			self:RegisterEvent('UNIT_SPELLCAST_CHANNEL_UPDATE', UNIT_SPELLCAST_CHANNEL_UPDATE)
+			self:RegisterEvent('UNIT_SPELLCAST_CHANNEL_STOP', UNIT_SPELLCAST_CHANNEL_STOP)
 		end

+		element.horizontal = element:GetOrientation() == 'HORIZONTAL'
 		element.holdTime = 0
-
 		element:SetScript('OnUpdate', element.OnUpdate or onUpdate)

-		if(self.unit == 'player' and not (self.hasChildren or self.isChild or self.isNamePlate)) then
-			CastingBarFrame_SetUnit(CastingBarFrame, nil)
-			CastingBarFrame_SetUnit(PetCastingBarFrame, nil)
+		if(self.unit == 'player') then
+			CastingBarFrame:UnregisterAllEvents()
+			CastingBarFrame.Show = CastingBarFrame.Hide
+			CastingBarFrame:Hide()
+
+			PetCastingBarFrame:UnregisterAllEvents()
+			PetCastingBarFrame.Show = PetCastingBarFrame.Hide
+			PetCastingBarFrame:Hide()
 		end

 		if(element:IsObjectType('StatusBar') and not element:GetStatusBarTexture()) then
@@ -424,8 +556,6 @@ local function Enable(self, unit)
 			safeZone:SetColorTexture(1, 0, 0)
 		end

-		element:Hide()
-
 		return true
 	end
 end
@@ -435,27 +565,17 @@ local function Disable(self)
 	if(element) then
 		element:Hide()

-		self:UnregisterEvent('UNIT_SPELLCAST_START', CastStart)
-		self:UnregisterEvent('UNIT_SPELLCAST_CHANNEL_START', CastStart)
-		self:UnregisterEvent('UNIT_SPELLCAST_DELAYED', CastUpdate)
-		self:UnregisterEvent('UNIT_SPELLCAST_CHANNEL_UPDATE', CastUpdate)
-		self:UnregisterEvent('UNIT_SPELLCAST_STOP', CastStop)
-		self:UnregisterEvent('UNIT_SPELLCAST_CHANNEL_STOP', CastStop)
-		self:UnregisterEvent('UNIT_SPELLCAST_FAILED', CastFail)
-		self:UnregisterEvent('UNIT_SPELLCAST_INTERRUPTED', CastFail)
-
-		if oUF.isRetail then
-			self:UnregisterEvent('UNIT_SPELLCAST_INTERRUPTIBLE', CastInterruptible)
-			self:UnregisterEvent('UNIT_SPELLCAST_NOT_INTERRUPTIBLE', CastInterruptible)
-		end
+		self:UnregisterEvent('UNIT_SPELLCAST_START', UNIT_SPELLCAST_START)
+		self:UnregisterEvent('UNIT_SPELLCAST_FAILED', UNIT_SPELLCAST_FAILED)
+		self:UnregisterEvent('UNIT_SPELLCAST_STOP', UNIT_SPELLCAST_STOP)
+		self:UnregisterEvent('UNIT_SPELLCAST_INTERRUPTED', UNIT_SPELLCAST_INTERRUPTED)
+		self:UnregisterEvent('UNIT_SPELLCAST_DELAYED', UNIT_SPELLCAST_DELAYED)
+		self:UnregisterEvent('UNIT_SPELLCAST_CHANNEL_START', UNIT_SPELLCAST_CHANNEL_START)
+		self:UnregisterEvent('UNIT_SPELLCAST_CHANNEL_UPDATE', UNIT_SPELLCAST_CHANNEL_UPDATE)
+		self:UnregisterEvent('UNIT_SPELLCAST_CHANNEL_STOP', UNIT_SPELLCAST_CHANNEL_STOP)

 		element:SetScript('OnUpdate', nil)
-
-		if(self.unit == 'player' and not (self.hasChildren or self.isChild or self.isNamePlate)) then
-			CastingBarFrame_OnLoad(CastingBarFrame, 'player', true, false)
-			PetCastingBarFrame_OnLoad(PetCastingBarFrame)
-		end
 	end
 end

-oUF:AddElement('Castbar', Update, Enable, Disable)
+oUF:AddElement('Castbar', Update, Enable, Disable)
\ No newline at end of file
diff --git a/oUF/elements/classpower.lua b/oUF/elements/classpower.lua
index 445b37b..c2cc7aa 100644
--- a/oUF/elements/classpower.lua
+++ b/oUF/elements/classpower.lua
@@ -50,21 +50,13 @@ local oUF = ns.oUF
 local _, PlayerClass = UnitClass('player')

 -- sourced from FrameXML/Constants.lua
-local SPEC_MAGE_ARCANE = SPEC_MAGE_ARCANE or 1
-local SPEC_MONK_WINDWALKER = SPEC_MONK_WINDWALKER or 3
-local SPEC_PALADIN_RETRIBUTION = SPEC_PALADIN_RETRIBUTION or 3
-local SPEC_WARLOCK_DESTRUCTION = SPEC_WARLOCK_DESTRUCTION or 3
 local SPELL_POWER_ENERGY = Enum.PowerType.Energy or 3
 local SPELL_POWER_COMBO_POINTS = Enum.PowerType.ComboPoints or 4
-local SPELL_POWER_SOUL_SHARDS = Enum.PowerType.SoulShards or 7
-local SPELL_POWER_HOLY_POWER = Enum.PowerType.HolyPower or 9
-local SPELL_POWER_CHI = Enum.PowerType.Chi or 12
-local SPELL_POWER_ARCANE_CHARGES = Enum.PowerType.ArcaneCharges or 16

 -- Holds the class specific stuff.
 local ClassPowerID, ClassPowerType
 local ClassPowerEnable, ClassPowerDisable
-local RequireSpec, RequirePower, RequireSpell
+local RequirePower, RequireSpell

 local function UpdateColor(element, powerType)
 	local color = element.__owner.colors.power[powerType]
@@ -79,23 +71,16 @@ local function UpdateColor(element, powerType)
 			bg:SetVertexColor(r * mu, g * mu, b * mu)
 		end
 	end
-
-	--[[ Callback: ClassPower:PostUpdateColor(r, g, b)
-	Called after the element color has been updated.
-
-	* self - the ClassPower element
-	* r    - the red component of the used color (number)[0-1]
-	* g    - the green component of the used color (number)[0-1]
-	* b    - the blue component of the used color (number)[0-1]
-	--]]
-	if(element.PostUpdateColor) then
-		element:PostUpdateColor(r, g, b)
-	end
 end

 local function Update(self, event, unit, powerType)
-	if(not (unit and (UnitIsUnit(unit, 'player') and (not powerType or powerType == ClassPowerType)
-		or unit == 'vehicle' and powerType == 'COMBO_POINTS'))) then
+	if event == "PLAYER_TARGET_CHANGED" then
+		unit, powerType = "player", "COMBO_POINTS"
+	elseif powerType == "ENERGY" then
+		powerType = "COMBO_POINTS" -- sometimes powerType return ENERGY for the first combo point
+	end
+
+	if (not (unit and (UnitIsUnit(unit, 'player') and powerType == ClassPowerType))) then
 		return
 	end

@@ -110,30 +95,17 @@ local function Update(self, event, unit, powerType)
 		element:PreUpdate()
 	end

-	local cur, max, mod, oldMax, chargedIndex
+	local cur, max, mod, oldMax
 	if(event ~= 'ClassPowerDisable') then
-		local powerID = unit == 'vehicle' and SPELL_POWER_COMBO_POINTS or ClassPowerID
-		cur = UnitPower(unit, powerID, true)
+		local powerID = ClassPowerID
+		--cur = UnitPower(unit, powerID, true)
+		cur = GetComboPoints(unit, "target")	-- has to use GetComboPoints in classic
 		max = UnitPowerMax(unit, powerID)
 		mod = UnitPowerDisplayMod(powerID)

 		-- mod should never be 0, but according to Blizz code it can actually happen
 		cur = mod == 0 and 0 or cur / mod

-		-- BUG: Destruction is supposed to show partial soulshards, but Affliction and Demonology should only show full ones
-		if(ClassPowerType == 'SOUL_SHARDS' and GetSpecialization() ~= SPEC_WARLOCK_DESTRUCTION) then
-			cur = cur - cur % 1
-		end
-
-		if(PlayerClass == 'ROGUE') then
-			local chargedPoints = GetUnitChargedPowerPoints(unit)
-			-- according to Blizzard there will only be one
-			chargedIndex = chargedPoints and chargedPoints[1]
-
-			-- UNIT_POWER_POINT_CHARGE doesn't provide a power type
-			powerType = powerType or ClassPowerType
-		end
-
 		local numActive = cur + 0.9
 		for i = 1, max do
 			if(i > numActive) then
@@ -165,10 +137,9 @@ local function Update(self, event, unit, powerType)
 	* max           - the maximum amount of power (number)
 	* hasMaxChanged - indicates whether the maximum amount has changed since the last update (boolean)
 	* powerType     - the active power type (string)
-	* chargedIndex  - the index of the currently charged power point (number?)
 	--]]
 	if(element.PostUpdate) then
-		return element:PostUpdate(cur, max, oldMax ~= max, powerType, chargedIndex)
+		return element:PostUpdate(cur, max, oldMax ~= max, powerType)
 	end
 end

@@ -188,26 +159,21 @@ local function Visibility(self, event, unit)
 	local element = self.ClassPower
 	local shouldEnable

-	if(UnitHasVehicleUI('player')) then
-		shouldEnable = PlayerVehicleHasComboPoints()
-		unit = 'vehicle'
-	elseif(ClassPowerID) then
-		if(not RequireSpec or RequireSpec == GetSpecialization()) then
-			-- use 'player' instead of unit because 'SPELLS_CHANGED' is a unitless event
-			if(not RequirePower or RequirePower == UnitPowerType('player')) then
-				if(not RequireSpell or IsPlayerSpell(RequireSpell)) then
-					self:UnregisterEvent('SPELLS_CHANGED', Visibility)
-					shouldEnable = true
-					unit = 'player'
-				else
-					self:RegisterEvent('SPELLS_CHANGED', Visibility, true)
-				end
+	if(ClassPowerID) then
+		-- use 'player' instead of unit because 'SPELLS_CHANGED' is a unitless event
+		if(not RequirePower or RequirePower == UnitPowerType('player')) then
+			if(not RequireSpell or IsPlayerSpell(RequireSpell)) then
+				self:UnregisterEvent('SPELLS_CHANGED', Visibility)
+				shouldEnable = true
+				unit = 'player'
+			else
+				self:RegisterEvent('SPELLS_CHANGED', Visibility, true)
 			end
 		end
 	end

-	local isEnabled = element.__isEnabled
-	local powerType = unit == 'vehicle' and 'COMBO_POINTS' or ClassPowerType
+	local isEnabled = element.isEnabled
+	local powerType = ClassPowerType

 	if(shouldEnable) then
 		--[[ Override: ClassPower:UpdateColor(powerType)
@@ -221,22 +187,8 @@ local function Visibility(self, event, unit)

 	if(shouldEnable and not isEnabled) then
 		ClassPowerEnable(self)
-
-		--[[ Callback: ClassPower:PostVisibility(isVisible)
-		Called after the element's visibility has been changed.
-
-		* self      - the ClassPower element
-		* isVisible - the current visibility state of the element (boolean)
-		--]]
-		if(element.PostVisibility) then
-			element:PostVisibility(true)
-		end
 	elseif(not shouldEnable and (isEnabled or isEnabled == nil)) then
 		ClassPowerDisable(self)
-
-		if(element.PostVisibility) then
-			element:PostVisibility(false)
-		end
 	elseif(shouldEnable and isEnabled) then
 		Path(self, event, unit, powerType)
 	end
@@ -260,57 +212,36 @@ end
 do
 	function ClassPowerEnable(self)
 		self:RegisterEvent('UNIT_POWER_FREQUENT', Path)
+		self:RegisterEvent('PLAYER_TARGET_CHANGED', Path, true)
 		self:RegisterEvent('UNIT_MAXPOWER', Path)

-		if(PlayerClass == 'ROGUE') then
-			self:RegisterEvent('UNIT_POWER_POINT_CHARGE', Path)
-		end
-
-		self.ClassPower.__isEnabled = true
+		self.ClassPower.isEnabled = true

-		if(UnitHasVehicleUI('player')) then
-			Path(self, 'ClassPowerEnable', 'vehicle', 'COMBO_POINTS')
-		else
-			Path(self, 'ClassPowerEnable', 'player', ClassPowerType)
-		end
+		Path(self, 'ClassPowerEnable', 'player', ClassPowerType)
 	end

 	function ClassPowerDisable(self)
 		self:UnregisterEvent('UNIT_POWER_FREQUENT', Path)
+		self:UnregisterEvent('PLAYER_TARGET_CHANGED', Path)
 		self:UnregisterEvent('UNIT_MAXPOWER', Path)
-		self:UnregisterEvent('UNIT_POWER_POINT_CHARGE', Path)

 		local element = self.ClassPower
 		for i = 1, #element do
 			element[i]:Hide()
 		end

-		element.__isEnabled = false
+		self.ClassPower.isEnabled = false
 		Path(self, 'ClassPowerDisable', 'player', ClassPowerType)
 	end

-	if(PlayerClass == 'MONK') then
-		ClassPowerID = SPELL_POWER_CHI
-		ClassPowerType = 'CHI'
-		RequireSpec = SPEC_MONK_WINDWALKER
-	elseif(PlayerClass == 'PALADIN') then
-		ClassPowerID = SPELL_POWER_HOLY_POWER
-		ClassPowerType = 'HOLY_POWER'
-	elseif(PlayerClass == 'WARLOCK') then
-		ClassPowerID = SPELL_POWER_SOUL_SHARDS
-		ClassPowerType = 'SOUL_SHARDS'
-	elseif(PlayerClass == 'ROGUE' or PlayerClass == 'DRUID') then
+	if(PlayerClass == 'ROGUE' or PlayerClass == 'DRUID') then
 		ClassPowerID = SPELL_POWER_COMBO_POINTS
 		ClassPowerType = 'COMBO_POINTS'

 		if(PlayerClass == 'DRUID') then
 			RequirePower = SPELL_POWER_ENERGY
-			RequireSpell = 5221 -- Shred
+			RequireSpell = 768 -- Cat Form
 		end
-	elseif(PlayerClass == 'MAGE') then
-		ClassPowerID = SPELL_POWER_ARCANE_CHARGES
-		ClassPowerType = 'ARCANE_CHARGES'
-		RequireSpec = SPEC_MAGE_ARCANE
 	end
 end

@@ -321,10 +252,6 @@ local function Enable(self, unit)
 		element.__max = #element
 		element.ForceUpdate = ForceUpdate

-		if(RequireSpec or RequireSpell) then
-			self:RegisterEvent('PLAYER_TALENT_UPDATE', VisibilityPath, true)
-		end
-
 		if(RequirePower) then
 			self:RegisterEvent('UNIT_DISPLAYPOWER', VisibilityPath)
 		end
@@ -335,7 +262,7 @@ local function Enable(self, unit)
 		for i = 1, #element do
 			local bar = element[i]
 			if(bar:IsObjectType('StatusBar')) then
-				if(not (bar:GetStatusBarTexture() or bar:GetStatusBarAtlas())) then
+				if(not bar:GetStatusBarTexture()) then
 					bar:SetStatusBarTexture([[Interface\TargetingFrame\UI-StatusBar]])
 				end

@@ -351,10 +278,9 @@ local function Disable(self)
 	if(self.ClassPower) then
 		ClassPowerDisable(self)

-		self:UnregisterEvent('PLAYER_TALENT_UPDATE', VisibilityPath)
 		self:UnregisterEvent('UNIT_DISPLAYPOWER', VisibilityPath)
 		self:UnregisterEvent('SPELLS_CHANGED', Visibility)
 	end
 end

-oUF:AddElement('ClassPower', VisibilityPath, Enable, Disable)
+oUF:AddElement('ClassPower', VisibilityPath, Enable, Disable)
\ No newline at end of file
diff --git a/oUF/elements/healprediction.lua b/oUF/elements/healprediction.lua
new file mode 100644
index 0000000..1d85f26
--- /dev/null
+++ b/oUF/elements/healprediction.lua
@@ -0,0 +1,140 @@
+local _, ns = ...
+local oUF = ns.oUF
+
+local myGUID = UnitGUID('player')
+local HealComm = LibStub("LibHealComm-4.0")
+
+local function UpdateFillBar(previousTexture, bar, amount, ratio)
+	if amount <= 0 then
+		bar:Hide()
+		return previousTexture
+	end
+
+	bar:SetPoint("TOPLEFT", previousTexture, "TOPRIGHT", 0, 0)
+	bar:SetPoint("BOTTOMLEFT", previousTexture, "BOTTOMRIGHT", 0, 0)
+	bar:SetWidth(amount * ratio)
+	bar:Show()
+	return bar
+end
+
+local function Update(self, event, unit)
+	if(self.unit ~= unit) then return end
+
+	local hp = self.HealPredictionAndAbsorb
+	if(hp.PreUpdate) then hp:PreUpdate(unit) end
+
+	local guid = UnitGUID(unit)
+
+	local myIncomingHeal = UnitGetIncomingHeals(unit, 'player') or 0
+	local allIncomingHeal = UnitGetIncomingHeals(unit) or 0
+	local allHot = HealComm:GetHealAmount(guid, hp.healType) or 0
+	local myHot = (HealComm:GetHealAmount(guid, hp.healType, nil, myGUID) or 0) * (HealComm:GetHealModifier(myGUID) or 1)
+	local health, maxHealth = UnitHealth(unit), UnitHealthMax(unit)
+	local ratio = self.Health:GetWidth() / maxHealth
+
+	allIncomingHeal = allIncomingHeal + allHot
+	myIncomingHeal = myIncomingHeal + myHot
+
+	if(health + allIncomingHeal > maxHealth * hp.maxOverflow) then
+		allIncomingHeal = maxHealth * hp.maxOverflow - health
+	end
+
+	if(allIncomingHeal < myIncomingHeal) then
+		myIncomingHeal = allIncomingHeal
+		allIncomingHeal = 0
+	else
+		allIncomingHeal = allIncomingHeal - myIncomingHeal
+	end
+
+	if UnitIsDeadOrGhost(unit) then
+		myIncomingHeal, allIncomingHeal = 0, 0
+	end
+
+	local previousTexture = self.Health:GetStatusBarTexture()
+	previousTexture = UpdateFillBar(previousTexture, hp.myBar, myIncomingHeal, ratio)
+	previousTexture = UpdateFillBar(previousTexture, hp.otherBar, allIncomingHeal, ratio)
+
+	if(hp.PostUpdate) then
+		return hp:PostUpdate(unit)
+	end
+end
+
+local function Path(self, ...)
+	return (self.HealPredictionAndAbsorb.Override or Update) (self, ...)
+end
+
+local ForceUpdate = function(element)
+	return Path(element.__owner, 'ForceUpdate', element.__owner.unit)
+end
+
+local function Enable(self)
+	local hp = self.HealPredictionAndAbsorb
+	if(hp) then
+		hp.__owner = self
+		hp.ForceUpdate = ForceUpdate
+		hp.healType = hp.healType or HealComm.OVERTIME_AND_BOMB_HEALS
+
+		self:RegisterEvent('UNIT_MAXHEALTH', Path)
+		self:RegisterEvent('UNIT_HEALTH_FREQUENT', Path)
+		self:RegisterEvent('UNIT_HEAL_PREDICTION', Path)
+
+		local function HealCommUpdate(...)
+			if self.HealPredictionAndAbsorb and self:IsVisible() then
+				for i = 1, select('#', ...) do
+					if self.unit and UnitGUID(self.unit) == select(i, ...) then
+						Path(self, nil, self.unit)
+					end
+				end
+			end
+		end
+
+		local function HealComm_Heal_Update(event, casterGUID, spellID, healType, _, ...)
+			HealCommUpdate(...)
+		end
+
+		local function HealComm_Modified(event, guid)
+			HealCommUpdate(guid)
+		end
+
+		HealComm.RegisterCallback(hp, 'HealComm_HealStarted', HealComm_Heal_Update)
+		HealComm.RegisterCallback(hp, 'HealComm_HealUpdated', HealComm_Heal_Update)
+		HealComm.RegisterCallback(hp, 'HealComm_HealDelayed', HealComm_Heal_Update)
+		HealComm.RegisterCallback(hp, 'HealComm_HealStopped', HealComm_Heal_Update)
+		HealComm.RegisterCallback(hp, 'HealComm_ModifierChanged', HealComm_Modified)
+		HealComm.RegisterCallback(hp, 'HealComm_GUIDDisappeared', HealComm_Modified)
+
+		if(not hp.maxOverflow) then
+			hp.maxOverflow = 1.05
+		end
+
+		if(hp.myBar and hp.myBar:IsObjectType'Texture' and not hp.myBar:GetTexture()) then
+			hp.myBar:SetTexture([[Interface\TargetingFrame\UI-StatusBar]])
+		end
+		if(hp.otherBar and hp.otherBar:IsObjectType'Texture' and not hp.otherBar:GetTexture()) then
+			hp.otherBar:SetTexture([[Interface\TargetingFrame\UI-StatusBar]])
+		end
+
+		return true
+	end
+end
+
+local function Disable(self)
+	local hp = self.HealPredictionAndAbsorb
+	if(hp) then
+		hp.myBar:Hide()
+		hp.otherBar:Hide()
+
+		HealComm.UnregisterCallback(hp, 'HealComm_HealStarted')
+		HealComm.UnregisterCallback(hp, 'HealComm_HealUpdated')
+		HealComm.UnregisterCallback(hp, 'HealComm_HealDelayed')
+		HealComm.UnregisterCallback(hp, 'HealComm_HealStopped')
+		HealComm.UnregisterCallback(hp, 'HealComm_ModifierChanged')
+		HealComm.UnregisterCallback(hp, 'HealComm_GUIDDisappeared')
+
+		self:UnregisterEvent('UNIT_MAXHEALTH', Path)
+		self:UnregisterEvent('UNIT_HEALTH_FREQUENT', Path)
+		self:UnregisterEvent('UNIT_HEAL_PREDICTION', Path)
+	end
+end
+
+oUF:AddElement('HealPredictionAndAbsorb', Path, Enable, Disable)
\ No newline at end of file
diff --git a/oUF/elements/health.lua b/oUF/elements/health.lua
index 9822178..8ef1500 100644
--- a/oUF/elements/health.lua
+++ b/oUF/elements/health.lua
@@ -23,10 +23,8 @@ A default texture will be applied if the widget is a StatusBar and doesn't have

 The following options are listed by priority. The first check that returns true decides the color of the bar.

-.colorDisconnected - Use `self.colors.disconnected` to color the bar if the unit is offline (boolean)
 .colorTapping      - Use `self.colors.tapping` to color the bar if the unit isn't tapped by the player (boolean)
-.colorThreat       - Use `self.colors.threat[threat]` to color the bar based on the unit's threat status. `threat` is
-                     defined by the first return of [UnitThreatSituation](https://wow.gamepedia.com/API_UnitThreatSituation) (boolean)
+.colorDisconnected - Use `self.colors.disconnected` to color the bar if the unit is offline (boolean)
 .colorClass        - Use `self.colors.class[class]` to color the bar based on unit class. `class` is defined by the
                      second return of [UnitClass](http://wowprogramming.com/docs/api/UnitClass.html) (boolean)
 .colorClassNPC     - Use `self.colors.class[class]` to color the bar if the unit is a NPC (boolean)
@@ -47,33 +45,37 @@ The following options are listed by priority. The first check that returns true

 .multiplier - Used to tint the background based on the main widgets R, G and B values. Defaults to 1 (number)[0-1]

+## Attributes
+
+.disconnected - Indicates whether the unit is disconnected (boolean)
+
 ## Examples

-    -- Position and size
-    local Health = CreateFrame('StatusBar', nil, self)
-    Health:SetHeight(20)
-    Health:SetPoint('TOP')
-    Health:SetPoint('LEFT')
-    Health:SetPoint('RIGHT')
-
-    -- Add a background
-    local Background = Health:CreateTexture(nil, 'BACKGROUND')
-    Background:SetAllPoints(Health)
-    Background:SetTexture(1, 1, 1, .5)
-
-    -- Options
-    Health.colorTapping = true
-    Health.colorDisconnected = true
-    Health.colorClass = true
-    Health.colorReaction = true
-    Health.colorHealth = true
-
-    -- Make the background darker.
-    Background.multiplier = .5
-
-    -- Register it with oUF
-    Health.bg = Background
-    self.Health = Health
+	-- Position and size
+	local Health = CreateFrame('StatusBar', nil, self)
+	Health:SetHeight(20)
+	Health:SetPoint('TOP')
+	Health:SetPoint('LEFT')
+	Health:SetPoint('RIGHT')
+
+	-- Add a background
+	local Background = Health:CreateTexture(nil, 'BACKGROUND')
+	Background:SetAllPoints(Health)
+	Background:SetTexture(1, 1, 1, .5)
+
+	-- Options
+	Health.colorTapping = true
+	Health.colorDisconnected = true
+	Health.colorClass = true
+	Health.colorReaction = true
+	Health.colorHealth = true
+
+	-- Make the background darker.
+	Background.multiplier = .5
+
+	-- Register it with oUF
+	Health.bg = Background
+	self.Health = Health
 --]]

 local _, ns = ...
@@ -87,17 +89,15 @@ local function UpdateColor(self, event, unit)
 	local element = self.Health

 	local r, g, b, t
-	if(element.colorDisconnected and not UnitIsConnected(unit)) then
+	if(element.colorDisconnected and element.disconnected) then
 		t = self.colors.disconnected
 	elseif(element.colorTapping and not UnitPlayerControlled(unit) and UnitIsTapDenied(unit)) then
 		t = self.colors.tapped
-	elseif(element.colorHappiness and UnitIsUnit(unit, "pet") and GetPetHappiness and GetPetHappiness()) then
+	elseif(element.colorHappiness and unit == "pet" and GetPetHappiness()) then
 		t = self.colors.happiness[GetPetHappiness()]
-	elseif(element.colorThreat and not UnitPlayerControlled(unit) and UnitThreatSituation('player', unit)) then
-		t =  self.colors.threat[UnitThreatSituation('player', unit)]
-	elseif(element.colorClass and UnitIsPlayer(unit))
-		or (element.colorClassNPC and not UnitIsPlayer(unit))
-		or (element.colorClassPet and UnitPlayerControlled(unit) and not UnitIsPlayer(unit)) then
+	elseif(element.colorClass and UnitIsPlayer(unit)) or
+		(element.colorClassNPC and not UnitIsPlayer(unit)) or
+		(element.colorClassPet and UnitPlayerControlled(unit) and not UnitIsPlayer(unit)) then
 		local _, class = UnitClass(unit)
 		t = self.colors.class[class]
 	elseif(element.colorSelection and unitSelectionType(unit, element.considerSelectionInCombatHostile)) then
@@ -124,15 +124,6 @@ local function UpdateColor(self, event, unit)
 		end
 	end

-	--[[ Callback: Health:PostUpdateColor(unit, r, g, b)
-	Called after the element color has been updated.
-
-	* self - the Health element
-	* unit - the unit for which the update has been triggered (string)
-	* r    - the red component of the used color (number)[0-1]
-	* g    - the green component of the used color (number)[0-1]
-	* b    - the blue component of the used color (number)[0-1]
-	--]]
 	if(element.PostUpdateColor) then
 		element:PostUpdateColor(unit, r, g, b)
 	end
@@ -164,27 +155,18 @@ local function Update(self, event, unit)
 	end

 	local cur, max = UnitHealth(unit), UnitHealthMax(unit)
-
-	if element.smoothing then
-		element:SetMinMaxSmoothedValue(0, max)
+	local disconnected = not UnitIsConnected(unit)
+	element:SetMinMaxValues(0, max)

-		if(UnitIsConnected(unit)) then
-			element:SetSmoothedValue(cur)
-		else
-			element:SetSmoothedValue(max)
-		end
+	if(disconnected) then
+		element:SetValue(max)
 	else
-		element:SetMinMaxValues(0, max)
-
-		if(UnitIsConnected(unit)) then
-			element:SetValue(cur)
-		else
-			element:SetValue(max)
-		end
+		element:SetValue(cur)
 	end

 	element.cur = cur
 	element.max = max
+	element.disconnected = disconnected

 	--[[ Callback: Health:PostUpdate(unit, cur, max)
 	Called after the element has been updated.
@@ -199,7 +181,7 @@ local function Update(self, event, unit)
 	end
 end

-local function Path(self, ...)
+local function Path(self, event, ...)
 	--[[ Override: Health.Override(self, event, unit)
 	Used to completely override the internal update function.

@@ -207,83 +189,45 @@ local function Path(self, ...)
 	* event - the event triggering the update (string)
 	* unit  - the unit accompanying the event (string)
 	--]]
-	(self.Health.Override or Update) (self, ...);
+	(self.Health.Override or Update) (self, event, ...);

-	ColorPath(self, ...)
+	ColorPath(self, event, ...)
 end

 local function ForceUpdate(element)
 	Path(element.__owner, 'ForceUpdate', element.__owner.unit)
 end

---[[ Health:SetColorDisconnected(state, isForced)
-Used to toggle coloring if the unit is offline.
-
-* self     - the Health element
-* state    - the desired state (boolean)
-* isForced - forces the event update even if the state wasn't changed (boolean)
---]]
-local function SetColorDisconnected(element, state, isForced)
-	if(element.colorDisconnected ~= state or isForced) then
-		element.colorDisconnected = state
-		if(state) then
-			element.__owner:RegisterEvent('UNIT_CONNECTION', ColorPath)
-		else
-			element.__owner:UnregisterEvent('UNIT_CONNECTION', ColorPath)
-		end
-	end
-end
-
---[[ Health:SetColorSelection(state, isForced)
-Used to toggle coloring by the unit's selection.
+local onUpdateElapsed, onUpdateWait = 0, 0.25
+local function onUpdateHealth(self, elapsed)
+	if onUpdateElapsed > onUpdateWait then
+		Path(self.__owner, 'OnUpdate', self.__owner.unit)

-* self     - the Health element
-* state    - the desired state (boolean)
-* isForced - forces the event update even if the state wasn't changed (boolean)
---]]
-local function SetColorSelection(element, state, isForced)
-	if(element.colorSelection ~= state or isForced) then
-		element.colorSelection = state
-		if(state) then
-			element.__owner:RegisterEvent('UNIT_FLAGS', ColorPath)
-		else
-			element.__owner:UnregisterEvent('UNIT_FLAGS', ColorPath)
-		end
+		onUpdateElapsed = 0
+	else
+		onUpdateElapsed = onUpdateElapsed + elapsed
 	end
 end

---[[ Health:SetColorTapping(state, isForced)
-Used to toggle coloring if the unit isn't tapped by the player.
-
-* self     - the Health element
-* state    - the desired state (boolean)
-* isForced - forces the event update even if the state wasn't changed (boolean)
---]]
-local function SetColorTapping(element, state, isForced)
-	if(element.colorTapping ~= state or isForced) then
-		element.colorTapping = state
-		if(state) then
-			element.__owner:RegisterEvent('UNIT_FACTION', ColorPath)
-		else
-			element.__owner:UnregisterEvent('UNIT_FACTION', ColorPath)
-		end
-	end
+local function SetHealthUpdateSpeed(self, state)
+	if state < .1 then state = .1 end
+	onUpdateWait = state
 end

---[[ Health:SetColorThreat(state, isForced)
-Used to toggle coloring by the unit's threat status.
+local function SetHealthUpdateMethod(self, state, force)
+	if self.effectiveHealth ~= state or force then
+		self.effectiveHealth = state

-* self     - the Health element
-* state    - the desired state (boolean)
-* isForced - forces the event update even if the state wasn't changed (boolean)
---]]
-local function SetColorThreat(element, state, isForced)
-	if(element.colorThreat ~= state or isForced) then
-		element.colorThreat = state
-		if(state) then
-			element.__owner:RegisterEvent('UNIT_THREAT_LIST_UPDATE', ColorPath)
+		if state then
+			self.Health:SetScript('OnUpdate', onUpdateHealth)
+			self:UnregisterEvent('UNIT_HEALTH_FREQUENT', Path)
+			self:UnregisterEvent('UNIT_HEALTH', Path)
+			self:UnregisterEvent('UNIT_MAXHEALTH', Path)
 		else
-			element.__owner:UnregisterEvent('UNIT_THREAT_LIST_UPDATE', ColorPath)
+			self.Health:SetScript('OnUpdate', nil)
+			self:RegisterEvent('UNIT_HEALTH', Path) -- Needed for Pet Battles
+			self:RegisterEvent('UNIT_HEALTH_FREQUENT', Path)
+			self:RegisterEvent('UNIT_MAXHEALTH', Path)
 		end
 	end
 end
@@ -293,41 +237,23 @@ local function Enable(self, unit)
 	if(element) then
 		element.__owner = self
 		element.ForceUpdate = ForceUpdate
-		element.SetColorDisconnected = SetColorDisconnected
-		element.SetColorSelection = SetColorSelection
-		element.SetColorTapping = SetColorTapping
-		element.SetColorThreat = SetColorThreat
+
+		self.SetHealthUpdateSpeed = SetHealthUpdateSpeed
+		self.SetHealthUpdateMethod = SetHealthUpdateMethod
+		SetHealthUpdateMethod(self, self.effectiveHealth, true)

 		if(element.colorDisconnected) then
 			self:RegisterEvent('UNIT_CONNECTION', ColorPath)
-		end
-
-		if(element.colorSelection) then
-			self:RegisterEvent('UNIT_FLAGS', ColorPath)
+			self:RegisterEvent('PARTY_MEMBER_ENABLE', ColorPath)
+			self:RegisterEvent('PARTY_MEMBER_DISABLE', ColorPath)
 		end

 		if(element.colorTapping) then
 			self:RegisterEvent('UNIT_FACTION', ColorPath)
 		end
+		self:RegisterEvent('UNIT_HAPPINESS', ColorPath)

-		if(element.colorThreat) then
-			self:RegisterEvent('UNIT_THREAT_LIST_UPDATE', ColorPath)
-		end
-
-		if(element.smoothing) then
-			element.SetSmoothedValue = SmoothStatusBarMixin.SetSmoothedValue
-			element.SetMinMaxSmoothedValue = SmoothStatusBarMixin.SetMinMaxSmoothedValue
-		end
-
-		if oUF.Retail then
-			self:RegisterEvent('UNIT_HEALTH', Path)
-		else
-			self:RegisterEvent('UNIT_HEALTH_FREQUENT', Path)
-		end
-
-		self:RegisterEvent('UNIT_MAXHEALTH', Path)
-
-		if(element:IsObjectType('StatusBar') and not (element:GetStatusBarTexture() or element:GetStatusBarAtlas())) then
+		if(element:IsObjectType('StatusBar') and not element:GetStatusBarTexture()) then
 			element:SetStatusBarTexture([[Interface\TargetingFrame\UI-StatusBar]])
 		end

@@ -342,13 +268,17 @@ local function Disable(self)
 	if(element) then
 		element:Hide()

+		element:SetScript('OnUpdate', nil)
+		self:UnregisterEvent('UNIT_HEALTH_FREQUENT', Path)
 		self:UnregisterEvent('UNIT_HEALTH', Path)
 		self:UnregisterEvent('UNIT_MAXHEALTH', Path)
+
 		self:UnregisterEvent('UNIT_CONNECTION', ColorPath)
 		self:UnregisterEvent('UNIT_FACTION', ColorPath)
-		self:UnregisterEvent('UNIT_FLAGS', ColorPath)
-		self:UnregisterEvent('UNIT_THREAT_LIST_UPDATE', ColorPath)
+		self:UnregisterEvent('PARTY_MEMBER_ENABLE', ColorPath)
+		self:UnregisterEvent('PARTY_MEMBER_DISABLE', ColorPath)
+		self:UnregisterEvent('UNIT_HAPPINESS', ColorPath)
 	end
 end

-oUF:AddElement('Health', Path, Enable, Disable)
+oUF:AddElement('Health', Path, Enable, Disable)
\ No newline at end of file
diff --git a/oUF/elements/phaseindicator.lua b/oUF/elements/phaseindicator.lua
index 4242a7f..24ac51d 100644
--- a/oUF/elements/phaseindicator.lua
+++ b/oUF/elements/phaseindicator.lua
@@ -7,27 +7,16 @@ Toggles the visibility of an indicator based on the unit's phasing relative to t

 PhaseIndicator - Any UI widget.

-## Sub-Widgets
-
-Icon - A `Texture` to represent the phased status.
-
 ## Notes

 A default texture will be applied if the widget is a Texture and doesn't have a texture or a color set.
-OnEnter and OnLeave script handlers will be set to display a Tooltip if the widget is mouse enabled and does not have
-OnEnter and/or OnLeave handlers.

 ## Examples

     -- Position and size
-    local PhaseIndicator = CreateFrame('Frame', nil, self)
+    local PhaseIndicator = self:CreateTexture(nil, 'OVERLAY')
     PhaseIndicator:SetSize(16, 16)
     PhaseIndicator:SetPoint('TOPLEFT', self)
-    PhaseIndicator:EnableMouse(true)
-
-    local Icon = PhaseIndicator:CreateTexture(nil, 'OVERLAY')
-    Icon:SetAllPoints()
-    PhaseIndicator.Icon = Icon

     -- Register it with oUF
     self.PhaseIndicator = PhaseIndicator
@@ -36,32 +25,6 @@ OnEnter and/or OnLeave handlers.
 local _, ns = ...
 local oUF = ns.oUF

---[[ Override: PhaseIndicator:UpdateTooltip()
-Used to populate the tooltip when the widget is hovered.
-
-* self - the PhaseIndicator widget
---]]
-local function UpdateTooltip(element)
-	local text = PartyUtil.GetPhasedReasonString(element.reason, element.__owner.unit)
-	if(text) then
-		GameTooltip:SetText(text, nil, nil, nil, nil, true)
-		GameTooltip:Show()
-	end
-end
-
-local function onEnter(element)
-	if(not element:IsVisible()) then return end
-
-	if(element.reason) then
-		GameTooltip:SetOwner(element, 'ANCHOR_BOTTOMRIGHT')
-		element:UpdateTooltip()
-	end
-end
-
-local function onLeave()
-	GameTooltip:Hide()
-end
-
 local function Update(self, event, unit)
 	if(self.unit ~= unit) then return end

@@ -76,26 +39,21 @@ local function Update(self, event, unit)
 		element:PreUpdate()
 	end

-	-- BUG: UnitPhaseReason returns wrong data for friendly NPCs in phased scenarios like WM or Chromie Time
-	-- https://github.com/Stanzilla/WoWUIBugs/issues/49
-	local phaseReason = UnitIsPlayer(unit) and UnitIsConnected(unit) and UnitPhaseReason(unit) or nil
-	if(phaseReason) then
+	local isInSamePhase = UnitInPhase(unit)
+	if(not isInSamePhase and UnitIsPlayer(unit) and UnitIsConnected(unit)) then
 		element:Show()
 	else
 		element:Hide()
 	end

-	element.reason = phaseReason
-
-	--[[ Callback: PhaseIndicator:PostUpdate(isInSamePhase, phaseReason)
+	--[[ Callback: PhaseIndicator:PostUpdate(isInSamePhase)
 	Called after the element has been updated.

 	* self          - the PhaseIndicator element
 	* isInSamePhase - indicates whether the unit is in the same phase as the player (boolean)
-	* phaseReason   - the reason why the unit is in a different phase (number?)
 	--]]
 	if(element.PostUpdate) then
-		return element:PostUpdate(not phaseReason, phaseReason)
+		return element:PostUpdate(isInSamePhase)
 	end
 end

@@ -122,21 +80,8 @@ local function Enable(self)

 		self:RegisterEvent('UNIT_PHASE', Path)

-		local icon = (element.Icon or element)
-		if(icon:IsObjectType('Texture') and not icon:GetTexture()) then
-			icon:SetTexture([[Interface\TargetingFrame\UI-PhasingIcon]])
-		end
-
-		if(element.IsMouseEnabled and element:IsMouseEnabled()) then
-			if(not element:GetScript('OnEnter')) then
-				element:SetScript('OnEnter', onEnter)
-			end
-
-			if(not element:GetScript('OnLeave')) then
-				element:SetScript('OnLeave', onLeave)
-			end
-
-			element.UpdateTooltip = element.UpdateTooltip or UpdateTooltip
+		if(element:IsObjectType('Texture') and not element:GetTexture()) then
+			element:SetTexture([[Interface\TargetingFrame\UI-PhasingIcon]])
 		end

 		return true
@@ -152,4 +97,4 @@ local function Disable(self)
 	end
 end

-oUF:AddElement('PhaseIndicator', Path, Enable, Disable)
+oUF:AddElement('PhaseIndicator', Path, Enable, Disable)
\ No newline at end of file
diff --git a/oUF/elements/power.lua b/oUF/elements/power.lua
index 8767086..873618b 100644
--- a/oUF/elements/power.lua
+++ b/oUF/elements/power.lua
@@ -108,9 +108,9 @@ type and zero for the minimum value.
 --]]
 local function GetDisplayPower(element)
 	local unit = element.__owner.unit
-	local barInfo = GetUnitPowerBarInfo(unit)
-	if(barInfo and barInfo.showOnRaid and (UnitInParty(unit) or UnitInRaid(unit))) then
-		return ALTERNATE_POWER_INDEX, barInfo.minPower
+	local _, min, _, _, _, _, showOnRaid = UnitAlternatePowerInfo(unit)
+	if(showOnRaid) then
+		return ALTERNATE_POWER_INDEX, min
 	end
 end

@@ -121,10 +121,13 @@ local function UpdateColor(self, event, unit)
 	local pType, pToken, altR, altG, altB = UnitPowerType(unit)

 	local r, g, b, t
+	local happiness = GetPetHappiness()
 	if(element.colorDisconnected and not UnitIsConnected(unit)) then
 		t = self.colors.disconnected
 	elseif(element.colorTapping and not UnitPlayerControlled(unit) and UnitIsTapDenied(unit)) then
 		t = self.colors.tapped
+	elseif(element.colorHappiness and unit == "pet" and happiness) then
+		t = self.colors.happiness[happiness]
 	elseif(element.colorThreat and not UnitPlayerControlled(unit) and UnitThreatSituation('player', unit)) then
 		t =  self.colors.threat[UnitThreatSituation('player', unit)]
 	elseif(element.colorPower) then
@@ -219,23 +222,12 @@ local function Update(self, event, unit)
 	end

 	local cur, max = UnitPower(unit, displayType), UnitPowerMax(unit, displayType)
-
-	if element.smoothing then
-		element:SetMinMaxSmoothedValue(min or 0, max)
+	element:SetMinMaxValues(min or 0, max)

-		if(UnitIsConnected(unit)) then
-			element:SetSmoothedValue(cur)
-		else
-			element:SetSmoothedValue(max)
-		end
+	if(UnitIsConnected(unit)) then
+		element:SetValue(cur)
 	else
-		element:SetMinMaxValues(min or 0, max)
-
-		if(UnitIsConnected(unit)) then
-			element:SetValue(cur)
-		else
-			element:SetValue(max)
-		end
+		element:SetValue(max)
 	end

 	element.cur = cur
@@ -399,16 +391,12 @@ local function Enable(self)
 		else
 			self:RegisterEvent('UNIT_POWER_UPDATE', Path)
 		end
-
-		if(element.smoothing) then
-			element.SetSmoothedValue = SmoothStatusBarMixin.SetSmoothedValue
-			element.SetMinMaxSmoothedValue = SmoothStatusBarMixin.SetMinMaxSmoothedValue
-		end

 		self:RegisterEvent('UNIT_DISPLAYPOWER', Path)
 		self:RegisterEvent('UNIT_MAXPOWER', Path)
 		self:RegisterEvent('UNIT_POWER_BAR_HIDE', Path)
 		self:RegisterEvent('UNIT_POWER_BAR_SHOW', Path)
+		self:RegisterEvent('UNIT_HAPPINESS', Path)

 		if(element:IsObjectType('StatusBar') and not (element:GetStatusBarTexture() or element:GetStatusBarAtlas())) then
 			element:SetStatusBarTexture([[Interface\TargetingFrame\UI-StatusBar]])
@@ -435,6 +423,7 @@ local function Disable(self)
 		self:UnregisterEvent('UNIT_POWER_BAR_SHOW', Path)
 		self:UnregisterEvent('UNIT_POWER_FREQUENT', Path)
 		self:UnregisterEvent('UNIT_POWER_UPDATE', Path)
+		self:UnregisterEvent('UNIT_HAPPINESS', Path)
 		self:UnregisterEvent('UNIT_CONNECTION', ColorPath)
 		self:UnregisterEvent('UNIT_FACTION', ColorPath)
 		self:UnregisterEvent('UNIT_FLAGS', ColorPath)
@@ -442,4 +431,4 @@ local function Disable(self)
 	end
 end

-oUF:AddElement('Power', Path, Enable, Disable)
+oUF:AddElement('Power', Path, Enable, Disable)
\ No newline at end of file
diff --git a/oUF/elements/raidroleindicator.lua b/oUF/elements/raidroleindicator.lua
index 6bdb92c..ed63d82 100644
--- a/oUF/elements/raidroleindicator.lua
+++ b/oUF/elements/raidroleindicator.lua
@@ -42,10 +42,7 @@ local function Update(self, event)
 	end

 	local role, isShown
-	--[[ classic disabled
-	if(UnitInRaid(unit) and not UnitHasVehicleUI(unit)) then
-	]]
-	if UnitInRaid(unit) then
+	if(UnitInRaid(unit)) then
 		if(GetPartyAssignment('MAINTANK', unit)) then
 			isShown = true
 			element:SetTexture(MAINTANK_ICON)
diff --git a/oUF/elements/range.lua b/oUF/elements/range.lua
index f68e920..071e32d 100644
--- a/oUF/elements/range.lua
+++ b/oUF/elements/range.lua
@@ -33,15 +33,6 @@ local OnRangeFrame

 local UnitInRange, UnitIsConnected = UnitInRange, UnitIsConnected

-local RangeSpellID = {
-	["PALADIN"] = 19750,
-	["SHAMAN"] = 25357,
-	["DRUID"] = 774,
-	["PRIEST"] = 2050,
-	["WARLOCK"] = 5697,
-	["MAGE"] = 475,
-}
-
 local function Update(self, event)
 	local element = self.Range
 	local unit = self.unit
@@ -58,29 +49,7 @@ local function Update(self, event)
 	local inRange, checkedRange
 	local connected = UnitIsConnected(unit)
 	if(connected) then
-		if oUF.Retail then
-			inRange, checkedRange = UnitInRange(unit)
-		else
-			local Spell = RangeSpellID[select(2, UnitClass("player"))]
-			local IsFriend = UnitIsFriend(unit, "player")
-
-			if IsFriend and Spell and IsSpellKnown(Spell) then
-				local name, rank, icon, castTime, minRange, maxRange, spellId = GetSpellInfo(Spell)
-				local IsSpellInRangeFromPlayer = IsSpellInRange(name, unit)
-
-				if IsSpellInRangeFromPlayer == 1 then
-					inRange = true
-					checkedRange = true
-				else
-					inRange = false
-					checkedRange = true
-				end
-			else
-				inRange = CheckInteractDistance(unit, 4)
-				checkedRange = true
-			end
-		end
-
+		inRange, checkedRange = UnitInRange(unit)
 		if(checkedRange and not inRange) then
 			self:SetAlpha(element.outsideAlpha)
 		else
diff --git a/oUF/elements/readycheckindicator.lua b/oUF/elements/readycheckindicator.lua
index 60eac4c..1906301 100644
--- a/oUF/elements/readycheckindicator.lua
+++ b/oUF/elements/readycheckindicator.lua
@@ -122,7 +122,7 @@ end

 local function Enable(self, unit)
 	local element = self.ReadyCheckIndicator
-	unit = unit and unit:match('(%a+)%d*$')
+	unit = unit and unit:match('(%a+)%d*')
 	if(element and (unit == 'party' or unit == 'raid')) then
 		element.__owner = self
 		element.ForceUpdate = ForceUpdate
diff --git a/oUF/elements/tags.lua b/oUF/elements/tags.lua
index ae10d79..1d51f06 100644
--- a/oUF/elements/tags.lua
+++ b/oUF/elements/tags.lua
@@ -12,25 +12,15 @@ A FontString to hold a tag string. Unlike other elements, this widget must not h
 ## Notes

 A `Tag` is a Lua string consisting of a function name surrounded by square brackets. The tag will be replaced by the
-output of the function and displayed as text on the font string widget with that the tag has been registered.
+output of the function and displayed as text on the font string widget with that the tag has been registered. Literals
+can be pre- or appended by separating them with a `>` before or `<` after the function name. The literals will be only
+displayed when the function returns a non-nil value. I.e. `"[perhp<%]"` will display the current health as a percentage
+of the maximum health followed by the % sign.

-A `Tag String` is a Lua string consisting of one or multiple tags with optional literals and parameters around them.
-Each tag will be updated individually and the output will follow the tags order. Literals will be displayed in the
-output string regardless of whether the surrounding tag functions return a value. I.e. `"[curhp]/[maxhp]"` will resolve
-to something like `2453/5000`.
-
-There's also an optional prefix and suffix that are separated from the tag name by `$>` and `<$` respectively,
-for example, `"[==$>name<$==]"` will resolve to `==Thrall==`, and `"[perhp<$%]"` will resole to `100%`, however, said
-affixes will only be added if the tag function returns a non-empty string, if it returns `nil` or `""` affixes will be
-omitted.
-
-Additionally, it's possible to pass optional arguments to a tag function to alter its behaviour. Optional arguments are
-defined via `()` at the end of a tag and separated by commas (`,`). For example, `"[name(a,r,g,s)]"`, in this case 4
-additional arguments, `"a"`, `"r"`, `"g"`, and `"s"` will be passed to the name tag function, what to do with them,
-however, is up to a developer to decide.
-
-The full tag syntax looks like this: `"[prefix$>tag<$suffix(a,r,g,s)]"`. The order of optional elements is important,
-while they can be independently omitted, they can't be reordered.
+A `Tag String` is a Lua string consisting of one or multiple tags with optional literals between them. Each tag will be
+updated individually and the output will follow the tags order. Literals will be displayed in the output string
+regardless of whether the surrounding tag functions return a value. I.e. `"[curhp]/[maxhp]"` will resolve to something
+like `2453/5000`.

 A `Tag Function` is used to replace a single tag in a tag string by its output. A tag function receives only two
 arguments - the unit and the realUnit of the unit frame used to register the tag (see Options for further details). The
@@ -47,9 +37,9 @@ in the `oUF.Tags.SharedEvents` table as follows: `oUF.Tags.SharedEvents.EVENT_NA
 .overrideUnit    - if specified on the font string widget, the frame's realUnit will be passed as the second argument to
                    every tag function whose name is contained in the relevant tag string. Otherwise the second argument
                    is always nil (boolean)
-.frequentUpdates - defines how often the corresponding tag function(s) should be called. This will override the events
-                   for the tag(s), if any. If the value is a number, it is taken as a time interval in seconds. If the
-                   value is a boolean, the time interval is set to 0.5 seconds (number or boolean)
+.frequentUpdates - defines how often the correspondig tag function(s) should be called. This will override the events for
+                   the tag(s), if any. If the value is a number, it is taken as a time interval in seconds. If the value
+                   is a boolean, the time interval is set to 0.5 seconds (number or boolean)

 ## Attributes

@@ -57,8 +47,6 @@ in the `oUF.Tags.SharedEvents` table as follows: `oUF.Tags.SharedEvents.EVENT_NA

 ## Examples

-### Example 1
-
     -- define the tag function
     oUF.Tags.Methods['mylayout:threatname'] = function(unit, realUnit)
         local color = _TAGS['threatcolor'](unit)
@@ -75,39 +63,15 @@ in the `oUF.Tags.SharedEvents` table as follows: `oUF.Tags.SharedEvents.EVENT_NA

     -- register the tag on the text widget with oUF
     self:Tag(info, '[mylayout:threatname]')
-
-### Example 2
-
-    -- define the tag function that accepts optional arguments
-    oUF.Tags.Methods['mylayout:name'] = function(unit, realUnit, ...)
-        local name = _TAGS['name'](unit, realUnit)
-        local length = tonumber(...)
-        if(length) then
-            return name:sub(1, length) -- please note, this code doesn't support UTF-8 chars
-        else
-            return name
-        end
-    end
-
-    -- add the events
-    oUF.Tags.Events['mylayout:name'] = 'UNIT_NAME_UPDATE'
-
-    -- create the text widget
-    local info = self.Health:CreateFontString(nil, 'OVERLAY', 'GameFontNormal')
-    info:SetPoint('LEFT')
-
-    -- register the tag on the text widget with oUF
-    self:Tag(info, '[mylayout:name(5)]') -- the output will be shortened to 5 characters
-    -- self:Tag(info, '[mylayout:name]') -- alternative, the output won't be adjusted
-    -- self:Tag(info, '[mylayout:name(10)]') -- alternative, the output will be shortened to 10 characters
 --]]

 local _, ns = ...
 local oUF = ns.oUF
 local Private = oUF.Private

-local xpcall = Private.xpcall
+local nierror = Private.nierror
 local unitExists = Private.unitExists
+local xpcall = Private.xpcall

 local _PATTERN = '%[..-%]+'

@@ -146,17 +110,6 @@ local tagStrings = {
 		end
 	end]],

-	['arenaspec'] = [[function(u)
-		local id = u:match('arena(%d)$')
-		if(id) then
-			local specID = GetArenaOpponentSpec(tonumber(id))
-			if(specID and specID > 0) then
-				local _, specName = GetSpecializationInfoByID(specID)
-				return specName
-			end
-		end
-	end]],
-
 	['chi'] = [[function()
 		if(GetSpecialization() == SPEC_MONK_WINDWALKER) then
 			local num = UnitPower('player', Enum.PowerType.Chi)
@@ -258,9 +211,6 @@ local tagStrings = {

 	['level'] = [[function(u)
 		local l = UnitLevel(u)
-		if(UnitIsWildBattlePet and UnitIsWildBattlePet(u) or UnitIsWildBattlePet and UnitIsBattlePetCompanion(u)) then
-			l = UnitBattlePetLevel(u)
-		end

 		if(l > 0) then
 			return l
@@ -334,7 +284,7 @@ local tagStrings = {
 					return Hex(altR, altG, altB)
 				end
 			else
-				return Hex(_COLORS.power[pType] or _COLORS.power.MANA)
+				return Hex(_COLORS.power[pType])
 			end
 		end

@@ -536,7 +486,6 @@ _ENV._VARS = vars
 local tagEvents = {
 	['affix']               = 'UNIT_CLASSIFICATION_CHANGED',
 	['arcanecharges']       = 'UNIT_POWER_UPDATE PLAYER_TALENT_UPDATE',
-	['arenaspec']           = 'ARENA_PREP_OPPONENT_SPECIALIZATIONS',
 	['chi']                 = 'UNIT_POWER_UPDATE PLAYER_TALENT_UPDATE',
 	['classification']      = 'UNIT_CLASSIFICATION_CHANGED',
 	['cpoints']             = 'UNIT_POWER_FREQUENT PLAYER_TARGET_CHANGED',
@@ -571,17 +520,13 @@ local tagEvents = {
 	['smartlevel']          = 'UNIT_LEVEL PLAYER_LEVEL_UP UNIT_CLASSIFICATION_CHANGED',
 	['soulshards']          = 'UNIT_POWER_UPDATE',
 	['status']              = 'UNIT_HEALTH PLAYER_UPDATE_RESTING UNIT_CONNECTION',
-	['threat']              = 'UNIT_THREAT_SITUATION_UPDATE',
-	['threatcolor']         = 'UNIT_THREAT_SITUATION_UPDATE',
 }

 local unitlessEvents = {
-	ARENA_PREP_OPPONENT_SPECIALIZATIONS = true,
 	GROUP_ROSTER_UPDATE = true,
 	NEUTRAL_FACTION_SELECT_RESULT = true,
 	PARTY_LEADER_CHANGED = true,
 	PLAYER_LEVEL_UP = true,
-	PLAYER_TALENT_UPDATE = true,
 	PLAYER_TARGET_CHANGED = true,
 	PLAYER_UPDATE_RESTING = true,
 	RUNE_POWER_UPDATE = true,
@@ -644,10 +589,10 @@ local tagPool = {}
 local funcPool = {}
 local tmp = {}

-local function getBracketData(tag)
-	-- full tag syntax: '[prefix$>tag-name<$suffix(a,r,g,s)]'
-	local suffixEnd = (tag:match('()%(') or -1) - 1

+-- full tag syntax: '[prefix$>tag-name<$suffix(a,r,g,s)]'
+-- for a small test case see https://github.com/oUF-wow/oUF/pull/602
+local function getBracketData(tag)
 	local prefixEnd, prefixOffset = tag:match('()$>'), 1
 	if(not prefixEnd) then
 		prefixEnd = 1
@@ -656,6 +601,7 @@ local function getBracketData(tag)
 		prefixOffset = 3
 	end

+	local suffixEnd = (tag:match('()%(', prefixOffset + 1) or -1) - 1
 	local suffixStart, suffixOffset = tag:match('<$()', prefixEnd), 1
 	if(not suffixStart) then
 		suffixStart = suffixEnd + 1
@@ -663,7 +609,11 @@ local function getBracketData(tag)
 		suffixOffset = 3
 	end

-	return tag:sub(prefixEnd + prefixOffset, suffixStart - suffixOffset), prefixEnd, suffixStart, suffixEnd, tag:match('%((.-)%)')
+	return tag:sub(prefixEnd + prefixOffset, suffixStart - suffixOffset),
+		prefixEnd,
+		suffixStart,
+		suffixEnd,
+		tag:match('%((.-)%)', suffixOffset + 1)
 end

 local function getTagFunc(tagstr)
@@ -671,6 +621,12 @@ local function getTagFunc(tagstr)
 	if(not func) then
 		local format, numTags = tagstr:gsub('%%', '%%%%'):gsub(_PATTERN, '%%s')
 		local args = {}
+		local idx = 1
+
+		local format_ = {}
+		for i = 1, numTags do
+			format_[i] = '%s'
+		end

 		for bracket in tagstr:gmatch(_PATTERN) do
 			local tagFunc = funcPool[bracket] or tags[bracket:sub(2, -2)]
@@ -678,14 +634,14 @@ local function getTagFunc(tagstr)
 				local tagName, prefixEnd, suffixStart, suffixEnd, customArgs = getBracketData(bracket)
 				local tag = tags[tagName]
 				if(tag) then
-					if(prefixEnd ~= 1 and suffixStart - suffixEnd ~= 1) then
-						local prefix = bracket:sub(2, prefixEnd)
-						local suffix = bracket:sub(suffixStart, suffixEnd)
+					if(prefixEnd ~= 1 or suffixStart - suffixEnd ~= 1) then
+						local prefix = prefixEnd ~= 1 and bracket:sub(2, prefixEnd) or ''
+						local suffix = suffixStart - suffixEnd ~= 1 and bracket:sub(suffixStart, suffixEnd) or ''

 						tagFunc = function(unit, realUnit)
 							local str
 							if(customArgs) then
-								str = tag(unit, realUnit, strsplit(',', customArgs))
+								str = tag(unit, realUnit, string.split(',', customArgs))
 							else
 								str = tag(unit, realUnit)
 							end
@@ -694,41 +650,11 @@ local function getTagFunc(tagstr)
 								return prefix .. str .. suffix
 							end
 						end
-					elseif(prefixEnd ~= 1) then
-						local prefix = bracket:sub(2, prefixEnd)
-
-						tagFunc = function(unit, realUnit)
-							local str
-							if(customArgs) then
-								str = tag(unit, realUnit, strsplit(',', customArgs))
-							else
-								str = tag(unit, realUnit)
-							end
-
-							if(str and str ~= '') then
-								return prefix .. str
-							end
-						end
-					elseif(suffixStart - suffixEnd ~= 1) then
-						local suffix = bracket:sub(suffixStart, suffixEnd)
-
-						tagFunc = function(unit, realUnit)
-							local str
-							if(customArgs) then
-								str = tag(unit, realUnit, strsplit(',', customArgs))
-							else
-								str = tag(unit, realUnit)
-							end
-
-							if(str and str ~= '') then
-								return str .. suffix
-							end
-						end
 					else
 						tagFunc = function(unit, realUnit)
 							local str
 							if(customArgs) then
-								str = tag(unit, realUnit, strsplit(',', customArgs))
+								str = tag(unit, realUnit, string.split(',', customArgs))
 							else
 								str = tag(unit, realUnit)
 							end
@@ -745,8 +671,16 @@ local function getTagFunc(tagstr)

 			if(tagFunc) then
 				table.insert(args, tagFunc)
+
+				idx = idx + 1
 			else
-				return error(string.format('Attempted to use invalid tag %s.', bracket), 3)
+				nierror(string.format('Attempted to use invalid tag %s.', bracket))
+
+				format_[idx] = bracket
+				format = format:format(unpack(format_, 1, numTags))
+				format_[idx] = '%s'
+
+				numTags = numTags - 1
 			end
 		end

@@ -960,4 +894,4 @@ oUF.Tags = {

 oUF:RegisterMetaFunction('Tag', Tag)
 oUF:RegisterMetaFunction('Untag', Untag)
-oUF:RegisterMetaFunction('UpdateTags', Update)
+oUF:RegisterMetaFunction('UpdateTags', Update)
\ No newline at end of file
diff --git a/oUF/elements/threatindicator.lua b/oUF/elements/threatindicator.lua
index 75d7331..6eb6fd3 100644
--- a/oUF/elements/threatindicator.lua
+++ b/oUF/elements/threatindicator.lua
@@ -34,6 +34,20 @@ local Private = oUF.Private

 local unitExists = Private.unitExists

+if not GetThreatStatusColor then
+	function GetThreatStatusColor(status)
+		if status == 3 then
+			return 1, 0, 0
+		elseif status == 2 then
+			return 1, .6, 0
+		elseif status == 1 then
+			return 1, 1, .47
+		else
+			return .69, .69, .69
+		end
+	end
+end
+
 local function Update(self, event, unit)
 	if(unit ~= self.unit) then return end

@@ -61,7 +75,7 @@ local function Update(self, event, unit)

 	local r, g, b
 	if(status and status > 0) then
-		r, g, b = unpack(self.colors.threat[status])
+		r, g, b = GetThreatStatusColor(status)

 		if(element.SetVertexColor) then
 			element:SetVertexColor(r, g, b)
diff --git a/oUF/events.lua b/oUF/events.lua
index 9ebdf17..98db72a 100644
--- a/oUF/events.lua
+++ b/oUF/events.lua
@@ -4,7 +4,6 @@ local Private = oUF.Private

 local argcheck = Private.argcheck
 local error = Private.error
-local validateEvent = Private.validateEvent
 local validateUnit = Private.validateUnit
 local frame_metatable = Private.frame_metatable

@@ -17,12 +16,6 @@ local isEventRegistered = frame_metatable.__index.IsEventRegistered
 -- to update unit frames correctly, some events need to be registered for
 -- a specific combination of primary and secondary units
 local secondaryUnits = {
-	UNIT_ENTERED_VEHICLE = {
-		pet = 'player',
-	},
-	UNIT_EXITED_VEHICLE = {
-		pet = 'player',
-	},
 	UNIT_PET = {
 		pet = 'player',
 	},
@@ -105,8 +98,8 @@ function frame_metatable.__index:RegisterEvent(event, func, unitless)
 	argcheck(func, 3, 'function')

 	local curev = self[event]
+	local kind = type(curev)
 	if(curev) then
-		local kind = type(curev)
 		if(kind == 'function' and curev ~= func) then
 			self[event] = setmetatable({curev, func}, event_metatable)
 		elseif(kind == 'table') then
@@ -120,12 +113,11 @@ function frame_metatable.__index:RegisterEvent(event, func, unitless)
 		if(unitless or self.__eventless) then
 			-- re-register the event in case we have mixed registration
 			registerEvent(self, event)
-
 			if(self.unitEvents) then
 				self.unitEvents[event] = nil
 			end
 		end
-	elseif(validateEvent(event)) then
+	else
 		self[event] = func

 		if(not self:GetScript('OnEvent')) then
@@ -137,7 +129,6 @@ function frame_metatable.__index:RegisterEvent(event, func, unitless)
 		else
 			self.unitEvents = self.unitEvents or {}
 			self.unitEvents[event] = true
-
 			-- UpdateUnits will take care of unit event registration for header
 			-- units in case we don't have a valid unit yet
 			local unit1, unit2 = self.unit
@@ -187,4 +178,4 @@ function frame_metatable.__index:UnregisterEvent(event, func)

 		unregisterEvent(self, event)
 	end
-end
+end
\ No newline at end of file
diff --git a/oUF/factory.lua b/oUF/factory.lua
index 59b73e9..f862eba 100644
--- a/oUF/factory.lua
+++ b/oUF/factory.lua
@@ -4,68 +4,45 @@ local Private = oUF.Private

 local argcheck = Private.argcheck

-local queue = {}
-local factory = CreateFrame('Frame')
-factory:SetScript('OnEvent', function(self, event, ...)
+local _QUEUE = {}
+local _FACTORY = CreateFrame'Frame'
+_FACTORY:SetScript('OnEvent', function(self, event, ...)
 	return self[event](self, event, ...)
 end)

-factory:RegisterEvent('PLAYER_LOGIN')
-factory.active = true
+_FACTORY:RegisterEvent'PLAYER_LOGIN'
+_FACTORY.active = true

-function factory:PLAYER_LOGIN()
+function _FACTORY:PLAYER_LOGIN()
 	if(not self.active) then return end

-	for _, func in next, queue do
+	for _, func in next, _QUEUE do
 		func(oUF)
 	end

 	-- Avoid creating dupes.
-	wipe(queue)
+	wipe(_QUEUE)
 end

---[[ Factory: oUF:Factory(func)
-Used to call a function directly if the current character is logged in and the factory is active. Else the function is
-queued up to be executed at a later time (upon PLAYER_LOGIN by default).
-
-* self - the global oUF object
-* func - function to be executed or delayed (function)
---]]
 function oUF:Factory(func)
 	argcheck(func, 2, 'function')

 	-- Call the function directly if we're active and logged in.
-	if(IsLoggedIn() and factory.active) then
+	if(IsLoggedIn() and _FACTORY.active) then
 		return func(self)
 	else
-		table.insert(queue, func)
+		table.insert(_QUEUE, func)
 	end
 end

---[[ Factory: oUF:EnableFactory()
-Used to enable the factory.
-
-* self - the global oUF object
---]]
 function oUF:EnableFactory()
-	factory.active = true
+	_FACTORY.active = true
 end

---[[ Factory: oUF:DisableFactory()
-Used to disable the factory.
-
-* self - the global oUF object
---]]
 function oUF:DisableFactory()
-	factory.active = nil
+	_FACTORY.active = nil
 end

---[[ Factory: oUF:RunFactoryQueue()
-Used to try to execute queued up functions. The current player must be logged in and the factory must be active for
-this to succeed.
-
-* self - the global oUF object
---]]
 function oUF:RunFactoryQueue()
-	factory:PLAYER_LOGIN()
-end
+	_FACTORY:PLAYER_LOGIN()
+end
\ No newline at end of file
diff --git a/oUF/finalize.lua b/oUF/finalize.lua
index 9355d19..dec0bc1 100644
--- a/oUF/finalize.lua
+++ b/oUF/finalize.lua
@@ -1,4 +1,4 @@
 local parent, ns = ...

 -- It's named Private for a reason!
-ns.oUF.Private = nil
+ns.oUF.Private = nil
\ No newline at end of file
diff --git a/oUF/init.lua b/oUF/init.lua
index 2e2672f..dabbd5d 100644
--- a/oUF/init.lua
+++ b/oUF/init.lua
@@ -1,8 +1,3 @@
 local parent, ns = ...
 ns.oUF = {}
-ns.oUF.Private = {}
-
-ns.oUF.isRetail = WOW_PROJECT_ID == WOW_PROJECT_MAINLINE
-ns.oUF.isClassic = WOW_PROJECT_ID == WOW_PROJECT_CLASSIC
-ns.oUF.isTBC = WOW_PROJECT_ID == WOW_PROJECT_BURNING_CRUSADE_CLASSIC
-ns.oUF.isWotLK = false
+ns.oUF.Private = {}
\ No newline at end of file
diff --git a/oUF/libs/LibHealComm-4.0/LibHealComm-4.0.lua b/oUF/libs/LibHealComm-4.0/LibHealComm-4.0.lua
index 21bf729..2a74cc1 100644
--- a/oUF/libs/LibHealComm-4.0/LibHealComm-4.0.lua
+++ b/oUF/libs/LibHealComm-4.0/LibHealComm-4.0.lua
@@ -723,25 +723,35 @@ end

 --[[
 	What the different callbacks do:
+
 	AuraHandler: Specific aura tracking needed for this class, who has Beacon up on them and such
+
 	ResetChargeData: Due to spell "queuing" you can't always rely on aura data for buffs that last one or two casts, for example Divine Favor (+100% crit, one spell)
 	if you cast Holy Light and queue Flash of Light the library would still see they have Divine Favor and give them crits on both spells. The reset means that the flag that indicates
 	they have the aura can be killed and if they interrupt the cast then it will call this and let you reset the flags.
+
 	What happens in terms of what the client thinks and what actually is, is something like this:
+
 	UNIT_SPELLCAST_START, Holy Light -> Divine Favor up
 	UNIT_SPELLCAST_SUCCEEDED, Holy Light -> Divine Favor up (But it was really used)
 	UNIT_SPELLCAST_START, Flash of Light -> Divine Favor up (It's not actually up but auras didn't update)
 	UNIT_AURA -> Divine Favor up (Split second where it still thinks it's up)
 	UNIT_AURA -> Divine Favor faded (Client catches up and realizes it's down)
+
 	CalculateHealing: Calculates the healing value, does all the formula calculations talent modifiers and such
+
 	CalculateHotHealing: Used specifically for calculating the heals of hots
+
 	GetHealTargets: Who the heal is going to hit, used for setting extra targets for Beacon of Light + Paladin heal or Prayer of Healing.
 	The returns should either be:
+
 	"compressedGUID1,compressedGUID2,compressedGUID3,compressedGUID4", healthAmount
 	Or if you need to set specific healing values for one GUID it should be
 	"compressedGUID1,healthAmount1,compressedGUID2,healAmount2,compressedGUID3,healAmount3", -1
+
 	The latter is for cases like Glyph of Healing Wave where you need a heal for 1,000 on A and a heal for 200 on the player for B without sending 2 events.
 	The -1 tells the library to look in the GUId list for the heal amounts
+
 	**NOTE** Any GUID returned from GetHealTargets must be compressed through a call to compressGUID[guid]
 ]]

@@ -757,7 +767,7 @@ local function getBaseHealAmount(spellData, spellName, spellID, spellRank)
 	if type(average) == "number" then
 		return average
 	end
-	local requiresLevel = spellData.levels[spellRank]
+	local requiresLevel = spellData.levels[spellRank] or spellData.levels[1] -- needs review
 	return average[min(playerLevel - requiresLevel + 1, #average)]
 end

@@ -1133,8 +1143,7 @@ if( playerClass == "PRIEST" ) then
 		hotData[Renew] = {coeff = 1, interval = 3, ticks = 5, levels = {8, 14, 20, 26, 32, 38, 44, 50, 56, 60, 65, 70}, averages = {
 			45, 100, 175, 245, 315, 400, 510, 650, 810, 970, 1010, 1110 }}
 		hotData[GreaterHealHot] = hotData[Renew]
-
-		if Renewal then
+		if isTBC then -- prevent error on Classic Era realms
 			hotData[Renewal] = {coeff = 0, interval = 3, ticks = 3, levels = {70}, averages = {150}}
 		end

@@ -2178,7 +2187,7 @@ function HealComm:COMBAT_LOG_EVENT_UNFILTERED(...)

 			updateRecord(pending, destGUID, amount, stack, endTime, ticksLeft)

-			if( pending.isMultiTarget ) then
+			if( pending.isMultiTarget ) and sourceGUID then
 				bucketHeals[sourceGUID] = bucketHeals[sourceGUID] or {}
 				bucketHeals[sourceGUID][spellID] = bucketHeals[sourceGUID][spellID] or {}

diff --git a/oUF/libs/LibHealComm-4.0/LibHealComm-4.0.xml b/oUF/libs/LibHealComm-4.0/LibHealComm-4.0.xml
index 730fcb3..3f70d20 100644
--- a/oUF/libs/LibHealComm-4.0/LibHealComm-4.0.xml
+++ b/oUF/libs/LibHealComm-4.0/LibHealComm-4.0.xml
@@ -1,5 +1,4 @@
-<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/
-..\FrameXML\UI.xsd">
+<Ui xmlns="http://www.blizzard.com/wow/ui/">
 	<Script file="ChatThrottleLib.lua"/>
 	<Script file="LibHealComm-4.0.lua"/>
 </Ui>
\ No newline at end of file
diff --git a/oUF/oUF.toc b/oUF/oUF.toc
index a9336dd..822f8b7 100644
--- a/oUF/oUF.toc
+++ b/oUF/oUF.toc
@@ -19,6 +19,7 @@ init.lua
 private.lua
 ouf.lua
 events.lua
+combatevents.lua
 factory.lua
 blizzard.lua
 units.lua
diff --git a/oUF/oUF.xml b/oUF/oUF.xml
index 5e8faf2..043ccc6 100644
--- a/oUF/oUF.xml
+++ b/oUF/oUF.xml
@@ -3,84 +3,39 @@
 	<Script file="private.lua"/>
 	<Script file="ouf.lua"/>
 	<Script file="events.lua"/>
+	<Script file="combatevents.lua"/>
 	<Script file="factory.lua"/>
 	<Script file="blizzard.lua"/>
 	<Script file="units.lua"/>
 	<Script file="colors.lua"/>

+	<Script file='Plugins\RaidDebuffs.lua' />
+
 	<Script file="elements\additionalpower.lua"/>
-	<Script file="elements\alternativepower.lua"/>
 	<Script file="elements\assistantindicator.lua"/>
 	<Script file="elements\auras.lua"/>
 	<Script file="elements\castbar.lua"/>
 	<Script file="elements\classpower.lua"/>
 	<Script file="elements\combatindicator.lua"/>
-	<Script file="elements\grouproleindicator.lua"/>
+	<Script file="elements\healprediction.lua"/>
 	<Script file="elements\health.lua"/>
-	<Script file="elements\healthprediction.lua"/>
 	<Script file="elements\leaderindicator.lua"/>
+	<Script file="elements\masterlooterindicator.lua"/>
 	<Script file="elements\phaseindicator.lua"/>
 	<Script file="elements\portrait.lua"/>
 	<Script file="elements\power.lua"/>
-	<Script file="elements\powerprediction.lua"/>
-	<Script file="elements\pvpclassificationindicator.lua"/>
-	<Script file="elements\pvpindicator.lua"/>
-	<Script file="elements\questindicator.lua"/>
 	<Script file="elements\raidroleindicator.lua"/>
 	<Script file="elements\raidtargetindicator.lua"/>
 	<Script file="elements\range.lua"/>
 	<Script file="elements\readycheckindicator.lua"/>
 	<Script file="elements\restingindicator.lua"/>
 	<Script file="elements\resurrectindicator.lua"/>
-	<Script file="elements\runes.lua"/>
-	<Script file="elements\stagger.lua"/>
-	<Script file="elements\summonindicator.lua"/>
 	<Script file="elements\tags.lua"/>
 	<Script file="elements\threatindicator.lua"/>
-	<Script file="elements\totems.lua"/>
-
-	<Script file="finalize.lua"/>
-
-	<!--
-		Sub-object as a child of the parent unit frame:
-	<Button name="oUF_HeaderTargetTemplate" inherits="SecureUnitButtonTemplate" virtual="true">
-		<Frames>
-			<Button name="$parentTarget" inherits="SecureUnitButtonTemplate">
-				<Attributes>
-					<Attribute name="unitsuffix" type="string" value="target"/>
-					<Attribute name="useparent-unit" type="boolean" value="true"/>
-				</Attributes>
-			</Button>
-		</Frames>
-	</Button>

-		Separate unit template example:
-	<Button name="oUF_HeaderSeparateSubOjectsTemplate" inherits="SecureUnitButtonTemplate" virtual="true">
-		<Attributes>
-			<Attribute name="oUF-onlyProcessChildren" type="boolean" value="true"/>
-		</Attributes>
+	<Script file='Plugins\FloatingCombatFeedback.lua' />
+	<Script file='Plugins\Swing.lua' />
+	<Script file='Plugins\EnergyManaRegen.lua' />

-		<Frames>
-			<Button name="$parentUnit" inherits="SecureUnitButtonTemplate">
-				<Attributes>
-					<Attribute name="useparent-unit" type="boolean" value="true"/>
-				</Attributes>
-			</Button>
-
-			<Button name="$parentPet" inherits="SecureUnitButtonTemplate">
-				<Attributes>
-					<Attribute name="unitsuffix" type="string" value="pet"/>
-					<Attribute name="useparent-unit" type="boolean" value="true"/>
-				</Attributes>
-			</Button>
-
-			<Button name="$parentTarget" inherits="SecureUnitButtonTemplate">
-				<Attributes>
-					<Attribute name="unitsuffix" type="string" value="target"/>
-					<Attribute name="useparent-unit" type="boolean" value="true"/>
-				</Attributes>
-			</Button>
-		</Frames>
-	</Button>
-	-->
-</Ui>
+	<Script file="finalize.lua"/>
+</Ui>
\ No newline at end of file
diff --git a/oUF/ouf.lua b/oUF/ouf.lua
index 91c5e55..9342a92 100644
--- a/oUF/ouf.lua
+++ b/oUF/ouf.lua
@@ -24,10 +24,6 @@ PetBattleFrameHider:SetAllPoints()
 PetBattleFrameHider:SetFrameStrata('LOW')
 RegisterStateDriver(PetBattleFrameHider, 'visibility', '[petbattle] hide; show')

-oUF.Retail = WOW_PROJECT_ID == WOW_PROJECT_MAINLINE
-oUF.BCC = WOW_PROJECT_ID == WOW_PROJECT_BURNING_CRUSADE_CLASSIC
-oUF.Classic = WOW_PROJECT_ID == WOW_PROJECT_CLASSIC
-
 -- updating of "invalid" units.
 local function enableTargetUpdate(object)
 	object.onUpdateFrequency = object.onUpdateFrequency or .5
@@ -58,10 +54,6 @@ local function updateActiveUnit(self, event, unit)
 		realUnit = 'target'
 	end

-	if(modUnit == 'pet' and realUnit ~= 'pet') then
-		modUnit = 'vehicle'
-	end
-
 	if(not unitExists(modUnit)) then return end

 	-- Change the active unit and run a full update.
@@ -136,12 +128,10 @@ for k, v in next, {
 		if(not enabled) then return end

 		local update = elements[name].update
-		if(update) then
-			for k, func in next, self.__elements do
-				if(func == update) then
-					table.remove(self.__elements, k)
-					break
-				end
+		for k, func in next, self.__elements do
+			if(func == update) then
+				table.remove(self.__elements, k)
+				break
 			end
 		end

@@ -287,9 +277,6 @@ local function initObject(unit, style, styleFunc, header, ...)
 		end

 		if(not (suffix == 'target' or objectUnit and objectUnit:match('target'))) then
-			object:RegisterEvent('UNIT_ENTERED_VEHICLE', updateActiveUnit)
-			object:RegisterEvent('UNIT_EXITED_VEHICLE', updateActiveUnit)
-
 			-- We don't need to register UNIT_PET for the player unit. We register it
 			-- mainly because UNIT_EXITED_VEHICLE and UNIT_ENTERED_VEHICLE doesn't always
 			-- have pet information when they fire for party and raid units.
@@ -303,11 +290,6 @@ local function initObject(unit, style, styleFunc, header, ...)
 			object:SetAttribute('*type1', 'target')
 			object:SetAttribute('*type2', 'togglemenu')

-			-- No need to enable this for *target frames.
-			if(not (unit:match('target') or suffix == 'target')) then
-				object:SetAttribute('toggleForVehicle', true)
-			end
-
 			-- Other boss and target units are handled by :HandleUnit().
 			if(suffix == 'target') then
 				enableTargetUpdate(object)
@@ -646,7 +628,7 @@ do
 		local name = overrideName or generateName(nil, ...)
 		local header = CreateFrame('Frame', name, PetBattleFrameHider, template)

-		header:SetAttribute('template', 'SecureUnitButtonTemplate, SecureHandlerStateTemplate, SecureHandlerEnterLeaveTemplate')
+		header:SetAttribute('template', 'SecureUnitButtonTemplate, SecureHandlerStateTemplate, SecureHandlerEnterLeaveTemplate, SecureHandlerShowHideTemplate, SecureHandlerMouseUpDownTemplate')
 		for i = 1, select('#', ...), 2 do
 			local att, val = select(i, ...)
 			if(not att) then break end
@@ -662,35 +644,6 @@ do

 		-- We set it here so layouts can't directly override it.
 		header:SetAttribute('initialConfigFunction', initialConfigFunction)
-		header:SetAttribute('_initialAttributeNames', '_onenter,_onleave,refreshUnitChange,_onstate-vehicleui')
-		header:SetAttribute('_initialAttribute-_onenter', [[
-			local snippet = self:GetAttribute('clickcast_onenter')
-			if(snippet) then
-				self:Run(snippet)
-			end
-		]])
-		header:SetAttribute('_initialAttribute-_onleave', [[
-			local snippet = self:GetAttribute('clickcast_onleave')
-			if(snippet) then
-				self:Run(snippet)
-			end
-		]])
-		header:SetAttribute('_initialAttribute-refreshUnitChange', [[
-			local unit = self:GetAttribute('unit')
-			if(unit) then
-				RegisterStateDriver(self, 'vehicleui', '[@' .. unit .. ',unithasvehicleui]vehicle; novehicle')
-			else
-				UnregisterStateDriver(self, 'vehicleui')
-			end
-		]])
-		header:SetAttribute('_initialAttribute-_onstate-vehicleui', [[
-			local unit = self:GetAttribute('unit')
-			if(newstate == 'vehicle' and unit and UnitPlayerOrPetInRaid(unit) and not UnitTargetsVehicleInRaidUI(unit)) then
-				self:SetAttribute('toggleForVehicle', false)
-			else
-				self:SetAttribute('toggleForVehicle', true)
-			end
-		]])
 		header:SetAttribute('oUF-headerType', isPetHeader and 'pet' or 'group')

 		if(Clique) then
@@ -729,7 +682,7 @@ oUF implements some of its own attributes. These can be supplied by the layout,

 * oUF-enableArenaPrep - can be used to toggle arena prep support. Defaults to true (boolean)
 --]]
-function oUF:Spawn(unit, overrideName)
+function oUF:Spawn(unit, overrideName, noHandle)
 	argcheck(unit, 2, 'string')
 	if(not style) then return error('Unable to create frame. No styles have been registered.') end

@@ -739,7 +692,7 @@ function oUF:Spawn(unit, overrideName)
 	local object = CreateFrame('Button', name, PetBattleFrameHider, 'SecureUnitButtonTemplate')
 	Private.UpdateUnits(object, unit)

-	self:DisableBlizzard(unit)
+	if not noHandle then self:DisableBlizzard(unit) end
 	walkObject(object, unit)

 	object:SetAttribute('unit', unit)
@@ -813,8 +766,6 @@ function oUF:SpawnNamePlates(namePrefix, nameplateCallback, nameplateCVars)
 		elseif(event == 'NAME_PLATE_UNIT_ADDED' and unit) then
 			local nameplate = C_NamePlate.GetNamePlateForUnit(unit)
 			if(not nameplate) then return end
-
-			local widgets = UnitWidgetSet and UnitWidgetSet(unit)

 			if(not nameplate.unitFrame) then
 				nameplate.style = style
@@ -822,10 +773,6 @@ function oUF:SpawnNamePlates(namePrefix, nameplateCallback, nameplateCVars)
 				nameplate.unitFrame = CreateFrame('Button', prefix..nameplate:GetName(), nameplate)
 				nameplate.unitFrame:EnableMouse(false)
 				nameplate.unitFrame.isNamePlate = true
-
-				if nameplate.UnitFrame.WidgetContainer then
-					nameplate.UnitFrame.WidgetContainer:SetParent(WorldFrame)
-				end

 				Private.UpdateUnits(nameplate.unitFrame, unit)

@@ -833,12 +780,6 @@ function oUF:SpawnNamePlates(namePrefix, nameplateCallback, nameplateCVars)
 			else
 				Private.UpdateUnits(nameplate.unitFrame, unit)
 			end
-
-			if widgets then
-				nameplate.unitFrame:SetAlpha(0)
-			else
-				nameplate.unitFrame:SetAlpha(1)
-			end

 			nameplate.unitFrame:SetAttribute('unit', unit)

@@ -867,15 +808,15 @@ Used to register an element with oUF.

 * self    - the global oUF object
 * name    - unique name of the element (string)
-* update  - used to update the element (function)
-* enable  - used to enable the element for a given unit frame and unit (function)
-* disable - used to disable the element for a given unit frame (function)
+* update  - used to update the element (function?)
+* enable  - used to enable the element for a given unit frame and unit (function?)
+* disable - used to disable the element for a given unit frame (function?)
 --]]
 function oUF:AddElement(name, update, enable, disable)
 	argcheck(name, 2, 'string')
 	argcheck(update, 3, 'function', 'nil')
-	argcheck(enable, 4, 'function')
-	argcheck(disable, 5, 'function')
+	argcheck(enable, 4, 'function', 'nil')
+	argcheck(disable, 5, 'function', 'nil')

 	if(elements[name]) then return error('Element [%s] is already registered.', name) end
 	elements[name] = {
diff --git a/oUF/private.lua b/oUF/private.lua
index b18a0e8..9d31559 100644
--- a/oUF/private.lua
+++ b/oUF/private.lua
@@ -21,6 +21,10 @@ function Private.error(...)
 	Private.print('|cffff0000Error:|r ' .. string.format(...))
 end

+function Private.nierror(...)
+	return geterrorhandler()(...)
+end
+
 function Private.unitExists(unit)
 	return unit and (UnitExists(unit) or ShowBossFrameWhenUninteractable(unit))
 end
@@ -62,19 +66,15 @@ function Private.unitSelectionType(unit, considerHostile)
 	end
 end

-local function errorHandler(...)
-	return geterrorhandler()(...)
-end
-
 function Private.xpcall(func, ...)
-	return xpcall(func, errorHandler, ...)
+	return xpcall(func, Private.nierror, ...)
 end

 function Private.validateEvent(event)
-	local isOK = xpcall(validator.RegisterEvent, errorHandler, validator, event)
+	local isOK = xpcall(validator.RegisterEvent, Private.nierror, validator, event)
 	if(isOK) then
 		validator:UnregisterEvent(event)
 	end

 	return isOK
-end
+end
\ No newline at end of file
diff --git a/oUF/units.lua b/oUF/units.lua
index ebcf9d0..517090f 100644
--- a/oUF/units.lua
+++ b/oUF/units.lua
@@ -4,168 +4,6 @@ local Private = oUF.Private

 local enableTargetUpdate = Private.enableTargetUpdate

-local function updateArenaPreparationElements(self, event, elementName, specID)
-	if not oUF.Retail then
-		return
-	end
-
-	local element = self[elementName]
-	if(element and self:IsElementEnabled(elementName)) then
-		if(element.OverrideArenaPreparation) then
-			--[[ Override: Health.OverrideArenaPreparation(self, event, specID)
-			Used to completely override the internal update function for arena preparation.
-
-			* self   - the parent object
-			* event  - the event triggering the update (string)
-			* specID - the specialization ID for the opponent (number)
-			--]]
-			--[[ Override: Power.OverrideArenaPreparation(self, event, specID)
-			Used to completely override the internal update function for arena preparation.
-
-			* self   - the parent object
-			* event  - the event triggering the update (string)
-			* specID - the specialization ID for the opponent (number)
-			--]]
-			element.OverrideArenaPreparation(self, event, specID)
-			return
-		end
-
-		element:SetMinMaxValues(0, 1)
-		element:SetValue(1)
-		if(element.UpdateColorArenaPreparation) then
-			--[[ Override: Health:UpdateColor(specID)
-			Used to completely override the internal function for updating the widget's colors
-			during arena preparation.
-
-			* self   - the Health element
-			* specID - the specialization ID for the opponent (number)
-			--]]
-			--[[ Override: Power:UpdateColor(specID)
-			Used to completely override the internal function for updating the widget's colors
-			during arena preparation.
-
-			* self   - the Power element
-			* specID - the specialization ID for the opponent (number)
-			--]]
-			element:UpdateColorArenaPreparation(specID)
-		else
-			-- this section just replicates the color options available to the Health and Power elements
-			local r, g, b, t, _
-			-- if(element.colorPower and elementName == 'Power') then
-				-- FIXME: no idea if we can get power type here without the unit
-			if(element.colorClass) then
-				local _, _, _, _, _, class = GetSpecializationInfoByID(specID)
-				t = self.colors.class[class]
-			elseif(element.colorReaction) then
-				t = self.colors.reaction[2]
-			elseif(element.colorSmooth) then
-				_, _, _, _, _, _, r, g, b = unpack(element.smoothGradient or self.colors.smooth)
-			elseif(element.colorHealth and elementName == 'Health') then
-				t = self.colors.health
-			end
-
-			if(t) then
-				r, g, b = t[1], t[2], t[3]
-			end
-
-			if(r or g or b) then
-				element:SetStatusBarColor(r, g, b)
-
-				local bg = element.bg
-				if(bg) then
-					local mu = bg.multiplier or 1
-					bg:SetVertexColor(r * mu, g * mu, b * mu)
-				end
-			end
-		end
-
-		if(element.PostUpdateArenaPreparation) then
-			--[[ Callback: Health:PostUpdateArenaPreparation(event, specID)
-			Called after the element has been updated during arena preparation.
-
-			* self   - the Health element
-			* event  - the event triggering the update (string)
-			* specID - the specialization ID for the opponent (number)
-			--]]
-			--[[ Callback: Power:PostUpdateArenaPreparation(event, specID)
-			Called after the element has been updated during arena preparation.
-
-			* self   - the Power element
-			* event  - the event triggering the update (string)
-			* specID - the specialization ID for the opponent (number)
-			--]]
-			element:PostUpdateArenaPreparation(event, specID)
-		end
-	end
-end
-
-local function updateArenaPreparation(self, event)
-	if(not self:GetAttribute('oUF-enableArenaPrep')) then
-		return
-	end
-
-	if(event == 'ARENA_OPPONENT_UPDATE' and not self:IsEnabled()) then
-		self:Enable()
-		self:UpdateAllElements('ArenaPreparation')
-		self:UnregisterEvent(event, updateArenaPreparation)
-
-		-- show elements that don't handle their own visibility
-		if(self:IsElementEnabled('Auras')) then
-			if(self.Auras) then self.Auras:Show() end
-			if(self.Buffs) then self.Buffs:Show() end
-			if(self.Debuffs) then self.Debuffs:Show() end
-		end
-
-		if(self.Portrait and self:IsElementEnabled('Portrait')) then
-			self.Portrait:Show()
-		end
-	elseif(event == 'PLAYER_ENTERING_WORLD' and not UnitExists(self.unit)) then
-		-- semi-recursive call for when the player zones into an arena
-		updateArenaPreparation(self, 'ARENA_PREP_OPPONENT_SPECIALIZATIONS')
-	elseif(event == 'ARENA_PREP_OPPONENT_SPECIALIZATIONS') then
-		if(self.PreUpdate) then
-			self:PreUpdate(event)
-		end
-
-		local id = tonumber(self.id)
-		if(not self:IsEnabled() and GetNumArenaOpponentSpecs() < id) then
-			-- hide the object if the opponent leaves
-			self:Hide()
-		end
-
-		local specID = GetArenaOpponentSpec(id)
-		if(specID) then
-			if(self:IsEnabled()) then
-				-- disable the unit watch so we can forcefully show the object ourselves
-				self:Disable()
-				self:RegisterEvent('ARENA_OPPONENT_UPDATE', updateArenaPreparation)
-			end
-
-			-- update Health and Power (if available) with "fake" data
-			updateArenaPreparationElements(self, event, 'Health', specID)
-			updateArenaPreparationElements(self, event, 'Power', specID)
-
-			-- hide all other (relevant) elements (they have no effect during arena prep)
-			if(self.Auras) then self.Auras:Hide() end
-			if(self.Buffs) then self.Buffs:Hide() end
-			if(self.Debuffs) then self.Debuffs:Hide() end
-			if(self.Castbar) then self.Castbar:Hide() end
-			if(self.CombatIndicator) then self.CombatIndicator:Hide() end
-			if(self.GroupRoleIndicator) then self.GroupRoleIndicator:Hide() end
-			if(self.Portrait) then self.Portrait:Hide() end
-			if(self.PvPIndicator) then self.PvPIndicator:Hide() end
-			if(self.RaidTargetIndicator) then self.RaidTargetIndicator:Hide() end
-
-			self:Show()
-			self:UpdateTags()
-		end
-
-		if(self.PostUpdate) then
-			self:PostUpdate(event)
-		end
-	end
-end
-
 -- Handles unit specific actions.
 function oUF:HandleUnit(object, unit)
 	local unit = object.unit or unit
@@ -175,19 +13,9 @@ function oUF:HandleUnit(object, unit)
 		object:RegisterEvent('UPDATE_MOUSEOVER_UNIT', object.UpdateAllElements, true)
 	elseif(unit == 'focus') then
 		object:RegisterEvent('PLAYER_FOCUS_CHANGED', object.UpdateAllElements, true)
-	elseif(unit:match('boss%d?$')) then
-		object:RegisterEvent('INSTANCE_ENCOUNTER_ENGAGE_UNIT', object.UpdateAllElements, true)
-		object:RegisterEvent('UNIT_TARGETABLE_CHANGED', object.UpdateAllElements)
 	elseif(unit:match('arena%d?$')) then
 		object:RegisterEvent('ARENA_OPPONENT_UPDATE', object.UpdateAllElements, true)
-
-		if oUF.Retail then
-			object:RegisterEvent('ARENA_PREP_OPPONENT_SPECIALIZATIONS', updateArenaPreparation, true)
-			object:SetAttribute('oUF-enableArenaPrep', true)
-			-- the event handler only fires for visible frames, so we have to hook it for arena prep
-			object:HookScript('OnEvent', updateArenaPreparation)
-		end
-	elseif(unit:match('%w+target')) then
+	elseif(unit and unit:match('%w+target')) then
 		enableTargetUpdate(object)
 	end
-end
+end
\ No newline at end of file
diff --git a/oUF_Simple/modules/oUF_EnergyManaRegen.lua b/oUF_Simple/modules/oUF_EnergyManaRegen.lua
index bc7d60f..cf43d06 100644
--- a/oUF_Simple/modules/oUF_EnergyManaRegen.lua
+++ b/oUF_Simple/modules/oUF_EnergyManaRegen.lua
@@ -1,125 +1,117 @@
-local A, L = ...
-local oUF = L.oUF or oUF
-if not oUF then return end
-
-local _G = _G
-local GetTime = GetTime
-local UnitPower = UnitPower
-local UnitClass = UnitClass
-local tonumber = tonumber
-local UnitPowerType = UnitPowerType
-local UnitPowerMax = UnitPowerMax
-local GetSpellPowerCost = GetSpellPowerCost
-
+local _, ns = ...
+local oUF = ns.oUF or oUF
 local LastTickTime = GetTime()
-local TickDelay = 2.025 -- Average tick time is slightly over 2 seconds
+local TickValue = 2
 local CurrentValue = UnitPower('player')
 local LastValue = CurrentValue
-local myClass = select(2, UnitClass('player'))
+local allowPowerEvent = true
+local myClass = select(2, UnitClass("player"))
 local Mp5Delay = 5
-local Mp5DelayWillEnd = nil
 local Mp5IgnoredSpells = {
-	[18182] = true, -- Improved Life Tap 1
-	[18183] = true, -- Improved Life Tap 2
-	[1454] = true, -- Life Tap 1
-	[1455] = true, -- Life Tap 2
-	[1456] = true, -- Life Tap 3
-	[11687] = true, -- Life Tap 4
-	[11688] = true, -- Life Tap 5
-	[11689] = true, -- Life Tap 6
+	[11689] = true, -- life tap 6
+	[11688] = true, -- life tap 5
+	[11687] = true, -- life tap 4
+	[1456] = true, -- life tap 3
+	[1455] = true, -- life tap 2
+	[1454] = true, -- life tap 1
+	[18182] = true, -- improved life tap 1
+	[18183] = true, -- improved life tap 2
+}
+local rangeWeaponSpells = {
+	[75] = true, -- auto shot
+	[5019] = true, -- shoot
 }
-
--- Sets tick time to the last possible time based on the last tick
-local UpdateTickTime = function(now)
-	LastTickTime = now - ((now - LastTickTime) % TickDelay)
-end

 local Update = function(self, elapsed)
 	local element = self.EnergyManaRegen
+
 	element.sinceLastUpdate = (element.sinceLastUpdate or 0) + (tonumber(elapsed) or 0)

 	if element.sinceLastUpdate > 0.01 then
-		local powerType = UnitPowerType('player')
+		local powerType = UnitPowerType("player")
+
 		if powerType ~= Enum.PowerType.Energy and powerType ~= Enum.PowerType.Mana then
 			element.Spark:Hide()
 			return
 		end

 		CurrentValue = UnitPower('player', powerType)
-		local MaxPower = UnitPowerMax('player', powerType)
-		local Now = GetTime()
-
-		if powerType == Enum.PowerType.Mana then
-			if CurrentValue >= MaxPower then
-				element:SetValue(0)
-				element.Spark:Hide()
-				return
-			end

-			-- Sync last tick time after 5 seconds are over
-			if Mp5DelayWillEnd and Mp5DelayWillEnd < Now then
-				Mp5DelayWillEnd = nil
-				UpdateTickTime(Now)
-			end
-		elseif powerType == Enum.PowerType.Energy then
-			-- If energy is not full we just wait for the next tick
-			if Now >= LastTickTime + TickDelay and CurrentValue >= MaxPower then
-				UpdateTickTime(Now)
-			end
+		if powerType == Enum.PowerType.Mana and (not CurrentValue or CurrentValue >= UnitPowerMax('player', Enum.PowerType.Mana)) then
+			element:SetValue(0)
+			element.Spark:Hide()
+			return
 		end

-		if Mp5DelayWillEnd and powerType == Enum.PowerType.Mana then
-			-- Show 5 second indicator
-			element.Spark:Show()
-			element:SetMinMaxValues(0, Mp5Delay)
-			element.Spark:SetVertexColor(1, 1, 0, 1)
-			element:SetValue(Mp5DelayWillEnd - Now)
-		else
-			-- Show tick indicator
-			element.Spark:Show()
-			element:SetMinMaxValues(0, TickDelay)
-			element.Spark:SetVertexColor(1, 1, 1, 1)
-			element:SetValue(Now - LastTickTime)
-		end
+		local Now = GetTime() or 0
+		if not (Now == nil) then
+			local Timer = Now - LastTickTime

-		element.sinceLastUpdate = 0
-	end
-end
+			if (CurrentValue > LastValue) or powerType == Enum.PowerType.Energy and (Now >= LastTickTime + 2) then
+				LastTickTime = Now
+			end

-local OnUnitPowerUpdate = function()
-	local powerType = UnitPowerType('player')
-	if powerType ~= Enum.PowerType.Mana and powerType ~= Enum.PowerType.Energy then
-		return
-	end
+			if Timer > 0 then
+				element.Spark:Show()
+				element:SetMinMaxValues(0, 2)
+				element.Spark:SetVertexColor(1, 1, 1, 1)
+				element:SetValue(Timer)
+				allowPowerEvent = true
+
+				LastValue = CurrentValue
+			elseif Timer < 0 then
+				-- if negative, it's mp5delay
+				element.Spark:Show()
+				element:SetMinMaxValues(0, Mp5Delay)
+				element.Spark:SetVertexColor(1, 1, 0, 1)
+
+				element:SetValue(math.abs(Timer))
+			end

-	-- We also register ticks from mp5 gear within the 5-second-rule to get a more accurate sync later.
-	-- Unfortunately this registers a tick when a mana pot or life tab is used.
-	local CurrentValue = UnitPower('player', powerType)
-	if CurrentValue > LastValue then
-		LastTickTime = GetTime()
+			element.sinceLastUpdate = 0
+		end
 	end
-	LastValue = CurrentValue
 end

-local OnUnitSpellcastSucceeded = function(_, _, _, _, spellID)
-	local powerType = UnitPowerType('player')
+local EventHandler = function(self, event, _, _, spellID)
+	local powerType = UnitPowerType("player")
+
 	if powerType ~= Enum.PowerType.Mana then
 		return
 	end

-	local spellCost = false
-	local costTable = GetSpellPowerCost(spellID)
-	for _, costInfo in next, costTable do
-		if costInfo.cost then
-			spellCost = true
+	if event == 'UNIT_POWER_UPDATE' and allowPowerEvent then
+		local Time = GetTime()
+
+		TickValue = Time - LastTickTime
+
+		if TickValue > 5 then
+			if powerType == Enum.PowerType.Mana and InCombatLockdown() then
+				TickValue = 5
+			else
+				TickValue = 2
+			end
 		end
-	end

-	if not spellCost or Mp5IgnoredSpells[spellID] then
-		return
+		LastTickTime = Time
 	end

-	Mp5DelayWillEnd = GetTime() + 5
+	if event == 'UNIT_SPELLCAST_SUCCEEDED' and not rangeWeaponSpells[spellID] then
+		local spellCost = false
+		local costTable = GetSpellPowerCost(spellID)
+		for _, costInfo in next, costTable do
+			if costInfo.cost then
+				spellCost = true
+			end
+		end
+
+		if (CurrentValue < LastValue) and (not spellCost or Mp5IgnoredSpells[spellID]) then
+			return
+		end
+
+		LastTickTime = GetTime() + 5
+		allowPowerEvent = false
+	end
 end

 local Path = function(self, ...)
@@ -130,7 +122,7 @@ local Enable = function(self, unit)
 	local element = self.EnergyManaRegen
 	local Power = self.Power

-	if (unit == 'player') and element and Power and myClass ~= 'WARRIOR' then
+	if (unit == "player") and element and Power and myClass ~= 'WARRIOR' then
 		element.__owner = self

 		if(element:IsObjectType('StatusBar')) then
@@ -142,13 +134,15 @@ local Enable = function(self, unit)
 		local spark = element.Spark
 		if(spark and spark:IsObjectType('Texture')) then
 			spark:SetTexture([[Interface\CastingBar\UI-CastingBar-Spark]])
-			spark:SetSize(5, 5)
 			spark:SetBlendMode('ADD')
-			spark:SetPoint('CENTER', element:GetStatusBarTexture(), 'RIGHT')
+			spark:SetPoint("TOPLEFT", element:GetStatusBarTexture(), "TOPRIGHT", -3, 3)
+			spark:SetPoint("BOTTOMRIGHT", element:GetStatusBarTexture(), "BOTTOMRIGHT", 3, -3)
 		end

-		self:RegisterEvent('UNIT_SPELLCAST_SUCCEEDED', OnUnitSpellcastSucceeded)
-		self:RegisterEvent('UNIT_POWER_UPDATE', OnUnitPowerUpdate)
+		self:RegisterEvent("PLAYER_REGEN_ENABLED", EventHandler, true)
+		self:RegisterEvent("PLAYER_REGEN_DISABLED", EventHandler, true)
+		self:RegisterEvent("UNIT_SPELLCAST_SUCCEEDED", EventHandler)
+		self:RegisterEvent("UNIT_POWER_UPDATE", EventHandler)

 		element:SetScript('OnUpdate', function(_, elapsed) Path(self, elapsed) end)

@@ -161,14 +155,16 @@ local Disable = function(self)
 	local Power = self.Power

 	if (Power) and (element) then
-		self:UnregisterEvent('UNIT_SPELLCAST_SUCCEEDED', OnUnitSpellcastSucceeded)
-		self:UnregisterEvent('UNIT_POWER_UPDATE', OnUnitPowerUpdate)
+		self:UnregisterEvent("PLAYER_REGEN_ENABLED", EventHandler, true)
+		self:UnregisterEvent("PLAYER_REGEN_DISABLED", EventHandler, true)
+		self:UnregisterEvent("UNIT_SPELLCAST_SUCCEEDED", EventHandler)
+		self:UnregisterEvent("UNIT_POWER_UPDATE", EventHandler)

 		element.Spark:Hide()
-		element:SetScript('OnUpdate', nil)
+		element:SetScript("OnUpdate", nil)

 		return false
 	end
 end

-oUF:AddElement('EnergyManaRegen', Path, Enable, Disable)
\ No newline at end of file
+oUF:AddElement("EnergyManaRegen", Path, Enable, Disable)
\ No newline at end of file
diff --git a/oUF_Simple/modules/oUF_Swing.lua b/oUF_Simple/modules/oUF_Swing.lua
index 848e088..1c38c9f 100644
--- a/oUF_Simple/modules/oUF_Swing.lua
+++ b/oUF_Simple/modules/oUF_Swing.lua
@@ -1,10 +1,9 @@
-------------------------
+-------------------------
 -- oUF_Swing, by p3lim
 -- NDui MOD
 -------------------------
-local A, L = ...
-local oUF = L.oUF or oUF
-if not oUF then return end
+local _, ns = ...
+local oUF = ns.oUF or oUF

 local select = select
 local GetTime = GetTime
@@ -22,16 +21,6 @@ local playerGUID = UnitGUID("player")
 local AUTO_CAST_TIME = .65
 local delayTime = 0

-local function Round(number, idp)
-	idp = idp or 0
-	local mult = 10 ^ idp
-	return floor(number * mult + .5) / mult
-end
-
-local function RoundPercent(percent)
-	return Round(percent, 2)
-end
-
 local function SwingStopped(element)
 	local bar = element.__owner
 	local swing = bar.Twohand
@@ -52,7 +41,7 @@ end
 local function UpdateBarValue(self, value)
 	self:SetValue(value)

-	if self.Text then
+	if self.Text and self.Text:IsShown() then
 		if self.__owner.OverrideText then
 			self.__owner.OverrideText(self, value)
 		else
@@ -108,8 +97,7 @@ do
 			end
 		end

-		local spell = UnitCastingInfo("player")
-		if slam == spell then
+		if UnitCastingInfo("player") == slam then
 			-- slamelapsed: time to add for one slam
 			slamelapsed = slamelapsed + elapsed
 			-- slamtime: needed for meleeing hack (see some lines above)
@@ -212,14 +200,14 @@ local function MeleeChange(self, _, unit)
 	else
 		if ohspeed then
 			if swingMH.speed and swingMH.speed ~= mhspeed then
-				local percentage = RoundPercent(((swingMH.max or 10) - now) / swingMH.speed)
+				local percentage = ((swingMH.max or 10) - now) / (swingMH.speed)
 				swingMH.min = now - mhspeed * (1 - percentage)
 				swingMH.max = now + mhspeed * percentage
 				UpdateBarMinMaxValues(swingMH)
 				swingMH.speed = mhspeed
 			end
 			if swingOH.speed and swingOH.speed ~= ohspeed then
-				local percentage = RoundPercent(((swingOH.max or 10)- now) / swingOH.speed)
+				local percentage = ((swingOH.max or 10)- now) / (swingOH.speed)
 				swingOH.min = now - ohspeed * (1 - percentage)
 				swingOH.max = now + ohspeed * percentage
 				UpdateBarMinMaxValues(swingOH)
@@ -227,7 +215,7 @@ local function MeleeChange(self, _, unit)
 			end
 		else
 			if swing.max and swing.speed ~= mhspeed then
-				local percentage = RoundPercent((swing.max - now) / swing.speed)
+				local percentage = (swing.max - now) / (swing.speed)
 				swing.min = now - mhspeed * (1 - percentage)
 				swing.max = now + mhspeed * percentage
 				UpdateBarMinMaxValues(swing)
@@ -257,7 +245,7 @@ local function RangedChange(self, _, unit)
 		swing:SetScript("OnUpdate", OnDurationUpdate)
 	else
 		if swing.speed ~= speed then
-			local percentage = RoundPercent((swing.max - now) / swing.speed)
+			local percentage = (swing.max - now) / (swing.speed)
 			swing.min = now - speed * (1 - percentage)
 			swing.max = now + speed * percentage
 			swing.speed = speed
@@ -347,6 +335,14 @@ local function Melee(self, event, _, sourceGUID)
 	lasthit = now
 end

+local function GetHasteMult(speed, now, percentage)
+	if percentage == 1 then
+		return 0
+	else
+		return (speed - now) * percentage / (1 - percentage)
+	end
+end
+
 local function ParryHaste(self, ...)
 	local destGUID, _, _, _, missType = select(7, ...)

@@ -364,39 +360,39 @@ local function ParryHaste(self, ...)

 	-- needed calculations, so the timer doesnt jump on parryhaste
 	if dualwield then
-		local percentage = RoundPercent((swingMH.max - now) / swingMH.speed)
+		local percentage = (swingMH.max - now) / swingMH.speed

 		if percentage > .6 then
 			swingMH.max = now + swingMH.speed * .6
-			swingMH.min = now - (swingMH.max - now) * percentage / (1 - percentage)
+			swingMH.min = now - GetHasteMult(swingMH.max, now, percentage)
 			UpdateBarMinMaxValues(swingMH)
 		elseif percentage > .2 then
 			swingMH.max = now + swingMH.speed * .2
-			swingMH.min = now - (swingMH.max - now) * percentage / (1 - percentage)
+			swingMH.min = now - GetHasteMult(swingMH.max, now, percentage)
 			UpdateBarMinMaxValues(swingMH)
 		end

-		percentage = RoundPercent((swingOH.max - now) / swingOH.speed)
+		percentage = (swingOH.max - now) / swingOH.speed

 		if percentage > .6 then
 			swingOH.max = now + swingOH.speed * .6
-			swingOH.min = now - (swingOH.max - now) * percentage / (1 - percentage)
+			swingOH.min = now - GetHasteMult(swingOH.max, now, percentage)
 			UpdateBarMinMaxValues(swingOH)
 		elseif percentage > .2 then
 			swingOH.max = now + swingOH.speed * .2
-			swingOH.min = now - (swingOH.max - now) * percentage / (1 - percentage)
+			swingOH.min = now - GetHasteMult(swingOH.max, now, percentage)
 			UpdateBarMinMaxValues(swingOH)
 		end
 	else
-		local percentage = RoundPercent((swing.max - now) / swing.speed)
+		local percentage = (swing.max - now) / swing.speed

 		if percentage > .6 then
 			swing.max = now + swing.speed * .6
-			swing.min = now - (swing.max - now) * percentage / (1 - percentage)
+			swing.min = now - GetHasteMult(swing.max, now, percentage)
 			UpdateBarMinMaxValues(swing)
 		elseif percentage > .2 then
 			swing.max = now + swing.speed * .2
-			swing.min = now - (swing.max - now) * percentage / (1 - percentage)
+			swing.min = now - GetHasteMult(swing.max, now, percentage)
 			UpdateBarMinMaxValues(swing)
 		end
 	end