Quantcast

Use initial capital in filenames

Andrew Mordecai [07-15-16 - 06:08]
Use initial capital in filenames
Filename
Config.lua
Elements/BurningEmbers.lua
Elements/DemonicFury.lua
Functions.lua
Layout.lua
Tags.lua
config.lua
functions.lua
includes/DemonicFury.lua
layout.lua
oUF_Stardust.toc
tags.lua
diff --git a/Config.lua b/Config.lua
new file mode 100644
index 0000000..dd368dd
--- /dev/null
+++ b/Config.lua
@@ -0,0 +1,58 @@
+--[[-----------------------------------------------------------------------
+	oUF: Stardust - a layout for the oUF framework
+	Copyright (c) 2016 Andrew Mordecai <armordecai@protonmail.ch>
+	This code is released under the zlib license; see LICENSE for details.
+-------------------------------------------------------------------------]]
+
+local _, Stardust = ...
+
+Stardust.config = {
+	-- Style
+	barTexture = "Qulight",
+	bgMultiplier = 0.35,
+	numberFont = "Blizzard Bold",
+	textFont = "Blizzard",
+
+	-- Layout
+	width = 220,
+	height = 32,
+	power = true,
+	powerHeight = 5,
+
+	-- Unit Specific
+	units = {
+		pet = {
+			width = 0.5
+		},
+		targettarget = {
+			width = 0.5,
+			power = false
+		},
+		focustarget = {
+			width = 0.5,
+			power = false
+		},
+		party = {
+			width = 0.5
+		},
+		partypet = {
+			width = 0.25,
+			power = false
+		}
+	}
+}
+
+Stardust.colors = {
+	power = {
+		MANA = { 0, 0.8, 1 }
+	},
+	totems = {
+		[1] = { 0.6, 1,   0.2 }, -- Earth
+		[2] = { 1,   0.6, 0.2 }, -- Fire
+		[3] = { 0.2, 0.8, 1   }, -- Water
+		[4] = { 0.8, 0.4, 1   }, -- Air
+	}
+}
+
+setmetatable(Stardust.colors, oUF.colors)
+setmetatable(Stardust.colors.power, oUF.colors.power)
diff --git a/Elements/BurningEmbers.lua b/Elements/BurningEmbers.lua
new file mode 100644
index 0000000..395dfe2
--- /dev/null
+++ b/Elements/BurningEmbers.lua
@@ -0,0 +1,173 @@
+--[[--------------------------------------------------------------------
+	oUF_Phanx
+	Fully-featured PVE-oriented layout for oUF.
+	Copyright (c) 2008-2015 Phanx <addons@phanx.net>. All rights reserved.
+	http://www.wowinterface.com/downloads/info13993-oUF_Phanx.html
+	http://www.curse.com/addons/wow/ouf-phanx
+	https://github.com/Phanx/oUF_Phanx
+------------------------------------------------------------------------
+	Element to display burning embers on oUF frames.
+
+	You may embed this module in your own layout, but please do not
+	distribute it as a standalone plugin.
+----------------------------------------------------------------------]]
+
+local _, ns = ...
+local oUF = ns.oUF or oUF
+assert(oUF, "BurningEmbers element requires oUF")
+
+local MAX_POWER_PER_EMBER = MAX_POWER_PER_EMBER
+local SPEC_WARLOCK_DESTRUCTION = SPEC_WARLOCK_DESTRUCTION
+local SPELL_POWER_BURNING_EMBERS = SPELL_POWER_BURNING_EMBERS
+local WARLOCK_BURNING_EMBERS = WARLOCK_BURNING_EMBERS
+
+local UpdateVisibility, Update, Path, ForceUpdate, Enable, Disable
+
+function UpdateVisibility(self, event)
+	local element = self.BurningEmbers
+
+	if UnitHasVehicleUI("player") or GetSpecialization() ~= SPEC_WARLOCK_DESTRUCTION or not IsPlayerSpell(WARLOCK_BURNING_EMBERS) then
+		self:UnregisterEvent("UNIT_DISPLAYPOWER", Path)
+		self:UnregisterEvent("UNIT_POWER", Path)
+
+		element.__disabled = true
+
+		for i = 1, #element do
+			element[i]:Hide()
+		end
+		if element.Hide then
+			element:Hide()
+		end
+
+		return
+	end
+
+	element.__disabled = nil
+
+	if element.Show then
+		element:Show()
+	end
+
+	self:RegisterEvent("UNIT_DISPLAYPOWER", Path)
+	self:RegisterEvent("UNIT_POWER", Path)
+
+	Update(self, "UpdateVisibility", "player")
+end
+
+function Update(self, event, unit, powerType)
+	if powerType and powerType ~= "BURNING_EMBERS" then return end
+	local element = self.BurningEmbers
+	if element.__disabled then return end
+
+	if element.PreUpdate then
+		element:PreUpdate()
+	end
+
+	local embers = UnitPower("player", SPELL_POWER_BURNING_EMBERS, true)
+	local embersMax = UnitPowerMax("player", SPELL_POWER_BURNING_EMBERS, true)
+
+	local whole = floor(embers / MAX_POWER_PER_EMBER)
+	local wholeMax = floor(embersMax / MAX_POWER_PER_EMBER)
+	local parts = embers % MAX_POWER_PER_EMBER
+
+	if element.SetValue then
+		element:SetMinMaxValues(0, embersMax)
+		element:SetValue(embers)
+	else
+		local rest = embers
+		for i = 1, #element do
+			local ember = element[i]
+			ember.activated = false
+			if i > wholeMax then
+				--print(i, "Unused")
+				ember:Hide()
+			elseif rest >= MAX_POWER_PER_EMBER then
+				--print(i, "Full")
+				if ember.SetValue then
+					ember:SetMinMaxValues(0, MAX_POWER_PER_EMBER)
+					ember:SetValue(MAX_POWER_PER_EMBER)
+				else
+					ember:SetAlpha(1)
+				end
+				ember.activated = true
+				ember:Show()
+				rest = rest - MAX_POWER_PER_EMBER
+			elseif rest > 0 then
+				--print(i, "Partial", rest, parts)
+				if ember.SetValue then
+					ember:SetMinMaxValues(0, MAX_POWER_PER_EMBER)
+					ember:SetValue(rest)
+				else
+					ember:SetAlpha(1 - (0.75 * (rest / MAX_POWER_PER_EMBER)))
+				end
+				ember:Show()
+				rest = 0
+			else
+				--print(i, "Empty")
+				if ember.SetValue then
+					ember:SetMinMaxValues(0, MAX_POWER_PER_EMBER)
+					ember:SetValue(0)
+				else
+					ember:SetAlpha(0.25)
+				end
+				ember:Show()
+			end
+		end
+	end
+
+	if element.PostUpdate then
+		element:PostUpdate(embers, embersMax, SPELL_POWER_BURNING_EMBERS)
+	end
+end
+
+function Path(self, ...)
+	return (self.BurningEmbers.Override or Update)(self, ...)
+end
+
+function ForceUpdate(element)
+	return Path(element.__owner, "ForceUpdate", element.__owner.unit)
+end
+
+function Enable(self)
+	local element = self.BurningEmbers
+	if not element or self.unit ~= "player" then return end
+
+	element.__name = "BurningEmbers"
+	element.__owner = self
+	element.ForceUpdate = ForceUpdate
+
+	for i = 1, #element do
+		local ember = element[i]
+		if ember.GetStatusBarTexture and not ember:GetStatusBarTexture() then
+			ember:SetStatusBarTexture([[Interface\TargetingFrame\UI-StatusBar]])
+		end
+	end
+
+	self:RegisterEvent("PLAYER_TALENT_UPDATE", UpdateVisibility, true)
+	self:RegisterEvent("UNIT_ENTERING_VEHICLE", UpdateVisibility)
+	self:RegisterEvent("UNIT_EXITED_VEHICLE", UpdateVisibility)
+
+	UpdateVisibility(self, "Enable")
+	return true
+end
+
+function Disable(self)
+	local element = self.BurningEmbers
+	if not element then return end
+
+	self:UnregisterEvent("UNIT_DISPLAYPOWER", Path)
+	self:UnregisterEvent("UNIT_POWER", Path)
+
+	self:UnregisterEvent("PLAYER_TALENT_UPDATE", UpdateVisibility)
+	self:UnregisterEvent("UNIT_ENTERING_VEHICLE", UpdateVisibility)
+	self:UnregisterEvent("UNIT_EXITED_VEHICLE", UpdateVisibility)
+
+	for i = 1, #element do
+		element[i]:Hide()
+	end
+	if element.Hide then
+		element:Hide()
+	end
+end
+
+oUF:AddElement("BurningEmbers", Path, Enable, Disable)
diff --git a/Elements/DemonicFury.lua b/Elements/DemonicFury.lua
new file mode 100644
index 0000000..ef8c32a
--- /dev/null
+++ b/Elements/DemonicFury.lua
@@ -0,0 +1,116 @@
+--[[--------------------------------------------------------------------
+	oUF_Phanx
+	Fully-featured PVE-oriented layout for oUF.
+	Copyright (c) 2008-2015 Phanx <addons@phanx.net>. All rights reserved.
+	http://www.wowinterface.com/downloads/info13993-oUF_Phanx.html
+	http://www.curse.com/addons/wow/ouf-phanx
+	https://github.com/Phanx/oUF_Phanx
+------------------------------------------------------------------------
+	Element to display demonic fury on oUF frames.
+
+	You may embed this module in your own layout, but please do not
+	distribute it as a standalone plugin.
+----------------------------------------------------------------------]]
+
+local _, ns = ...
+local oUF = ns.oUF or oUF
+assert(oUF, "DemonicFury element requires oUF")
+
+local METAMORPHOSIS_FORM = 22
+local SPEC_WARLOCK_DEMONOLOGY = SPEC_WARLOCK_DEMONOLOGY
+local SPELL_POWER_DEMONIC_FURY = SPELL_POWER_DEMONIC_FURY
+
+oUF.colors.power.DEMONIC_FURY = { 132/255, 41/255, 235/255 }
+oUF.colors.power.METAMORPHOSIS = { 132/255, 235/255, 41/255 }
+
+local UpdateVisibility, Update, Path, ForceUpdate, Enable, Disable
+
+function UpdateVisibility(self, event, unit)
+	local element = self.DemonicFury
+
+	if UnitHasVehicleUI("player") or GetSpecialization() ~= SPEC_WARLOCK_DEMONOLOGY then
+		self:UnregisterEvent("SHAPESHIFT_FORM_CHANGED", Path)
+		self:UnregisterEvent("UNIT_POWER", Path)
+		element:Hide()
+		return
+	end
+
+	element:Show()
+	self:RegisterEvent("SHAPESHIFT_FORM_CHANGED", Path, true)
+	self:RegisterEvent("UNIT_POWER", Path)
+
+	Update(self, "UpdateVisibility", "player")
+end
+
+function Update(self, event, unit, powerType)
+	if powerType and powerType ~= "DEMONIC_FURY" then return end
+	local element = self.DemonicFury
+	if not element:IsShown() then return end
+
+	if element.PreUpdate then
+		element:PreUpdate()
+	end
+
+	local fury = UnitPower("player", SPELL_POWER_DEMONIC_FURY)
+	local maxFury = UnitPowerMax("player", SPELL_POWER_DEMONIC_FURY)
+	local inMetamorphosis = GetShapeshiftFormID() == METAMORPHOSIS_FORM
+	local color = oUF.colors.power[inMetamorphosis and "METAMORPHOSIS" or "DEMONIC_FURY"]
+	local r, g, b = color[1], color[2], color[3]
+
+	element:SetMinMaxValues(0, maxFury)
+	element:SetValue(fury)
+	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
+
+	if element.PostUpdate then
+		element:PostUpdate(fury, maxFury, inMetamorphosis)
+	end
+end
+
+function Path(self, ...)
+	return (self.DemonicFury.Override or Update)(self, ...)
+end
+
+function ForceUpdate(element)
+	return Path(element.__owner, "ForceUpdate", element.__owner.unit)
+end
+
+function Enable(self, unit)
+	local element = self.DemonicFury
+	if not element or self.unit ~= "player" then return end
+
+	element.__owner = self
+	element.ForceUpdate = ForceUpdate
+
+	if element.GetStatusBarTexture and not element:GetStatusBarTexture() then
+		element:SetStatusBarTexture([[Interface\TargetingFrame\UI-StatusBar]])
+	end
+
+	self:RegisterEvent("PLAYER_TALENT_UPDATE", UpdateVisibility, true)
+	self:RegisterEvent("UNIT_ENTERING_VEHICLE", UpdateVisibility)
+	self:RegisterEvent("UNIT_EXITED_VEHICLE", UpdateVisibility)
+
+	UpdateVisibility(self, "Enable", "player")
+	return true
+end
+
+function Disable(self)
+	local element = self.DemonicFury
+	if not element then return end
+
+	self:UnregisterEvent("SHAPESHIFT_FORM_CHANGED", Path)
+	self:UnregisterEvent("UNIT_POWER", Path)
+
+	self:UnregisterEvent("PLAYER_TALENT_UPDATE", UpdateVisibility)
+	self:UnregisterEvent("UNIT_ENTERING_VEHICLE", UpdateVisibility)
+	self:UnregisterEvent("UNIT_EXITED_VEHICLE", UpdateVisibility)
+
+	element:Hide()
+end
+
+oUF:AddElement("DemonicFury", Path, Enable, Disable)
diff --git a/Functions.lua b/Functions.lua
new file mode 100644
index 0000000..224e4f1
--- /dev/null
+++ b/Functions.lua
@@ -0,0 +1,212 @@
+--[[-----------------------------------------------------------------------
+	oUF: Stardust - a layout for the oUF framework
+	Copyright (c) 2016 Andrew Mordecai <armordecai@protonmail.ch>
+	This code is released under the zlib license; see LICENSE for details.
+-------------------------------------------------------------------------]]
+
+local _, Stardust = ...
+
+---------------------------
+-- Health
+---------------------------
+
+function Stardust.PostUpdateHealth(self, unit, cur, max)
+	self:SetValue(max - cur)
+	self.text:SetText(cur < max and -max + cur or "")
+	self.bg:SetVertexColor(0.15, 0.15, 0.15)
+end
+
+---------------------------
+-- Power
+-- DruidMana
+-- DemonicFury
+---------------------------
+
+function Stardust.PostUpdatePower(self, unit, cur, max)
+	self:SetShown(max > 0)
+	self.text:SetText(cur)
+end
+
+---------------------------
+-- ClassIcons
+---------------------------
+
+function Stardust.ResizeClassIcons(element)
+	local COUNT = element.__max
+	local SPACING = 3
+	local widthTotal = ((Stardust.config.width - SPACING - SPACING) * 0.6) - (COUNT * SPACING)
+	local widthEach = floor(widthTotal / COUNT + 0.5)
+	for i = 1, COUNT do
+		element[i]:SetWidth(widthEach)
+	end
+end
+
+function Stardust.PostUpdateClassIcons(element, cur, max, maxChanged, event)
+	if maxChanged then
+		Stardust.ResizeClassIcons(element)
+	end
+end
+
+---------------------------
+-- Combo points
+---------------------------
+
+function Stardust.PostUpdateCPoints(element, cur)
+	-- The number of combo points isn't dynamic, so they only need to be resized once per frame.
+	element.PreUpdate = nil
+	Stardust.ResizeClassIcons(element)
+end
+
+---------------------------
+-- Runes
+---------------------------
+
+function Stardust.PostUpdateRuneType(element, rune, index, alt)
+	local r, g, b = rune:GetStatusBarColor()
+	rune.bg:SetVertexColor(r * 0.35, g * 0.35, b * 0.35)
+end
+
+function Stardust.PostUpdateRune(element, rune, index, start, duration, ready)
+	-- The number of runes isn't dynamic, so they only need to be resized once per frame.
+	element.PostUpdateRune = nil
+	Stardust.ResizeClassIcons(element)
+end
+
+---------------------------
+-- Totems
+-- Partly adapted from oUF_Phanx
+---------------------------
+
+local function OnUpdateTotem(bar, elapsed)
+	local t = bar.duration - elapsed
+	if t > 0 then
+		bar.duration = t
+		bar:SetValue(t)
+	else
+		bar:SetValue(0)
+	end
+end
+
+function Stardust.PostUpdateTotem(element, index, _, name, start, duration)
+	local bar = element[index]
+	bar.duration, bar.max = duration, duration
+	if duration > 0 then
+		bar:SetMinMaxValues(0, duration)
+		bar:SetScript("OnUpdate", OnUpdateTotem)
+	else
+		bar:SetScript("OnUpdate", nil)
+		bar:SetValue(0)
+	end
+end
+
+function Stardust.PreUpdateTotems(element, index)
+	-- The number of totems isn't dynamic, so they only need to be resized once per frame.
+	element.PreUpdate = nil
+	Stardust.ResizeClassIcons(element)
+end
+
+---------------------------
+-- Status icons
+---------------------------
+
+function Stardust.PostUpdateStatusIcon(self)
+	if not self:IsShown() then return end
+	local icons, last = self.__owner.StatusIcons
+	for i = 1, #icons do
+		local icon = icons[i]
+		if icon == self then
+			break
+		elseif icon:IsShown() then
+			last = icon
+		end
+	end
+	if last then
+		self:SetPoint("LEFT", last, "RIGHT", 0, 0)
+	else
+		self:SetPoint("LEFT", self.__owner.Health, "TOPLEFT", 3, 0)
+	end
+end
+
+function Stardust.PostUpdateCombat(self)
+	self.__owner.Resting:ForceUpdate()
+end
+
+function Stardust.PostUpdateResting(self)
+	self.__owner.Combat:ForceUpdate()
+end
+
+---------------------------
+-- Glow
+---------------------------
+
+local GLOW_SEGMENTS = {
+	{ "TOPLEFT", 0, 1/3, 0, 1/3, -1, 1 },
+	{ "TOPRIGHT", 2/3, 1, 0, 1/3, 1, 1 },
+	{ "BOTTOMRIGHT", 2/3, 1, 2/3, 1, 1, -1 },
+	{ "BOTTOMLEFT", 0, 1/3, 2/3, 1, -1, -1 },
+	{ "TOP", 1/3, 2/3, 0, 1/3, 0, 1 },
+	{ "RIGHT", 2/3, 1, 1/3, 2/3, 1, 0 },
+	{ "BOTTOM", 1/3, 2/3, 2/3, 1, 0, -1 },
+	{ "LEFT", 0, 1/3, 1/3, 2/3, -1, 0 },
+}
+
+function Stardust.SetGlowColor(self, r, g, b, a)
+	for i = 1, #self.Glow do
+		self.Glow[i]:SetVertexColor(r, g, b, a)
+	end
+end
+
+function Stardust.SetGlowSize(self, size, offset)
+	local Glow = self.Glow
+	for i = 1, #Glow do
+		Glow[i]:SetSize(size, size)
+	end
+
+	local d = d or floor(size * 2 / 3 + 0.5)
+	Glow[1]:SetPoint("TOPLEFT", -d, d)
+	Glow[2]:SetPoint("TOPRIGHT", d, d)
+	Glow[3]:SetPoint("BOTTOMRIGHT", d, -d)
+	Glow[4]:SetPoint("BOTTOMLEFT", -d, -d)
+
+	Glow[5]:SetShown(Glow[2]:GetLeft() > Glow[1]:GetRight())
+	Glow[6]:SetShown(Glow[2]:GetBottom() > Glow[3]:GetTop())
+	Glow[7]:SetShown(Glow[3]:GetLeft() > Glow[4]:GetLeft())
+	Glow[8]:SetShown(Glow[1]:GetBottom() > Glow[4]:GetTop())
+end
+
+function Stardust.CreateGlow(self)
+	local Glow = {}
+	for i = 1, #GLOW_SEGMENTS do
+		local seg = GLOW_SEGMENTS[i]
+		local tex = self:CreateTexture(nil, "BACKGROUND")
+		tex:SetTexture("Interface\\AddOns\\oUF_Stardust\\textures\\glow")
+		tex:SetTexCoord(seg[2], seg[3], seg[4], seg[5])
+		tex:SetVertexColor(0, 0, 0)
+		Glow[i] = tex
+	end
+
+	-- TOP
+	Glow[5]:SetPoint("LEFT", Glow[1], "RIGHT")
+	Glow[5]:SetPoint("RIGHT", Glow[2], "LEFT")
+
+	-- RIGHT
+	Glow[6]:SetPoint("TOP", Glow[2], "BOTTOM")
+	Glow[6]:SetPoint("BOTTOM", Glow[3], "TOP")
+
+	-- BOTTOM
+	Glow[7]:SetPoint("LEFT", Glow[4], "RIGHT")
+	Glow[7]:SetPoint("RIGHT", Glow[3], "LEFT")
+
+	-- LEFT
+	Glow[8]:SetPoint("TOP", Glow[1], "BOTTOM")
+	Glow[8]:SetPoint("BOTTOM", Glow[4], "TOP")
+
+	SetGlowSize(self, 12)
+
+	self.Glow = Glow
+	self.SetBackdropBorderColor = SetGlowColor
+	self.SetGlowColor = SetGlowColor
+	self.SetGlowSize = SetGlowSize
+	return Glow
+end
+
diff --git a/Layout.lua b/Layout.lua
new file mode 100644
index 0000000..8de2d1a
--- /dev/null
+++ b/Layout.lua
@@ -0,0 +1,380 @@
+--[[-----------------------------------------------------------------------
+	oUF: Stardust - a layout for the oUF framework
+	Copyright (c) 2016 Andrew Mordecai <armordecai@protonmail.ch>
+	This code is released under the zlib license; see LICENSE for details.
+-------------------------------------------------------------------------]]
+
+local _, Stardust = ...
+local config = Stardust.config
+local colors = Stardust.colors
+
+local _, class = UnitClass("player")
+local LibSharedMedia = LibStub("LibSharedMedia-3.0")
+
+---------------------------
+-- Widget creation
+---------------------------
+
+local function NewStatusBar(parent, textFont, textSize, noBG, noBackdrop)
+	local TEXTURE = LibSharedMedia:Fetch("statusbar", config.barTexture)
+
+	local bar = CreateFrame("StatusBar", nil, parent)
+	bar:SetStatusBarTexture(TEXTURE)
+
+	if textFont and textSize then
+		bar.value = bar:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
+		bar.value:SetFont(LibSharedMedia:Fetch("font", textSize))
+		bar.value:SetPoint("CENTER")
+	end
+
+	if not noBG then
+		bar.bg = bar:CreateTexture(nil, "BORDER")
+		bar.bg:SetAllPoints()
+		bar.bg:SetTexture(TEXTURE)
+		bar.bg.multiplier = config.bgMultiplier
+	end
+
+	if not noBackdrop then
+		bar.backdrop = Health:CreateTexture(nil, "BACKDROP")
+		bar.backdrop:SetPoint("BOTTOMLEFT", -1, -1)
+		bar.backdrop:SetPoint("TOPRIGHT", 1, 1)
+		bar.backdrop:SetTexture(0, 0, 0)
+	end
+
+	return bar
+end
+
+local function NewBarGroup(parent, count, width, height)
+	local group = { __max = count }
+
+	local color = Stardust.colors.class[class]
+	local r, g, b = color[1], color[2], color[3]
+
+	for i = 1, count do
+		local bar = NewStatusBar(parent)
+		bar:SetSize(width, height)
+		bar:SetMinMaxValues(0, 1)
+		bar:SetValue(1)
+
+		if not noColor then
+			local mu = bar.bg.multiplier
+			bar:SetStatusBarColor(r, g, b)
+			bar.bg:SetVertexColor(r * mu, g * mu, b * mu)
+		end
+
+		-- ClassIcons expects texture objects
+		bar.SetVertexColor = bar.SetStatusBarColor
+
+		if i > 1 then
+			bar:SetPoint("LEFT", group[i-1], "RIGHT", 3, 0)
+		end
+
+		group[i] = bar
+	end
+
+	return group
+end
+
+local function AddStatusIcon(self, element, size)
+	local icon = self.Health:CreateTexture(nil, "OVERLAY")
+	icon:SetPoint("LEFT", self.Health, "TOPLEFT", 3, 0)
+	icon:SetSize(size or 16, size or 16)
+
+	self.StatusIcons = self.StatusIcons or {}
+	tinsert(self.StatusIcons, icon)
+
+	icon.PostUpdate = Stardust.PostUpdateStatusIcon
+	self[element] = icon
+	return icon
+end
+
+---------------------------
+-- Frame setup
+---------------------------
+
+local function ApplyStyle(self, unit, isSingle)
+	unit = unit:gsub("%d+", "")
+
+	local uconfig = config.units[unit]
+
+	---------------------------
+	-- Frame
+	---------------------------
+
+	self:SetScript("OnEnter", UnitFrame_OnEnter)
+	self:SetScript("OnLeave", UnitFrame_OnLeave)
+
+	self:RegisterForClicks("AnyUp")
+
+	if (isSingle) then
+		self:SetSize(config.width * (uconfig and uconfig.width or 1), config.height * (uconfig and uconfig.height or 1))
+	end
+
+	---------------------------
+	-- Outer glow
+	---------------------------
+
+	self.Glow = CreateGlow(self)
+
+	---------------------------
+	-- Health bar
+	---------------------------
+
+	local Health = NewStatusBar(self, config.numberFont, 25)
+	Health:SetPoint("BOTTOMLEFT")
+	Health:SetPoint("TOPRIGHT")
+	Health:SetReverseFill(true)
+
+	Health.value:ClearAllPoints()
+	Health.value:SetPoint("RIGHT", Health, "TOPRIGHT", 0, 3)
+
+	Health.colorClass = true
+	Health.colorDisconnected = true
+	Health.colorReaction = true
+	Health.colorTapping = true
+	Health.frequentUpdates = true
+	Health.PostUpdate = Stardust.PostUpdateHealth
+	self.Health = Health
+
+	---------------------------
+	-- Power bar
+	---------------------------
+
+	local Power = NewStatusBar(Health, config.numberFont, 15)
+	Power:SetPoint("BOTTOMLEFT", 3, 3)
+	Power:SetPoint("BOTTOMRIGHT", -3, 3)
+	Power:SetHeight(config.powerHeight)
+
+	Power.colorDisconnected = true
+	Power.colorPower = true
+	Power.PostUpdate = Stardust.PostUpdatePower
+	self.Power = Power
+
+	---------------------------
+	-- Secondary resources
+	---------------------------
+
+	if class == "PALADIN" or class == "PRIEST" or class == "WARLOCK" then
+		local ClassIcons = NewBarGroup(self.Health, 6, 30, config.powerHeight)
+		ClassIcons[1]:SetPoint("TOPLEFT", self.Health, 3, -3)
+		ClassIcons.PostUpdate = Stardust.PostUpdateClassIcons
+		self.ClassIcons = ClassIcons
+	end
+
+	if class == "DRUID" or class == "ROGUE" then
+		local CPoints = NewBarGroup(self.Health, 5, 30, config.powerHeight)
+		CPoints[1]:SetPoint("TOPLEFT", self.Health, 3, -3)
+		CPoints.PostUpdate = Stardust.PostUpdateCPoints
+		self.CPoints = CPoints
+	end
+
+	if class == "DEATHKNIGHT" then
+		local Runes = NewBarGroup(self.Health, 6, 30, config.powerHeight)
+		Runes[1]:SetPoint("TOPLEFT", self.Health, 3, -3)
+		Runes.PostUpdateRune = Stardust.PostUpdateRune
+		Runes.PostUpdateRuneType = Stardust.PostUpdateRuneType
+		self.Runes = Runes
+	end
+
+	if class == "DRUID" or class == "SHAMAN" then
+	    -- TODO: This can also show death knight ghouls.
+	    -- How do these work? Are they important?
+	    -- How to show them at the same time as runes?
+		local Totems = NewBarGroup(self.Health, 4, 30, config.powerHeight)
+		Totems[1]:SetPoint("TOPLEFT", self.Health, 3, -3)
+		if class == "SHAMAN" then
+			local mu = config.bgMultiplier
+			for i = 1, 4 do
+				local totem = Totems[i]
+				totem:EnableMouse(true)
+
+				local color = Stardust.colors.totems[i]
+				local r, g, b = color[1], color[2], color[3]
+				totem:SetStatusBarColor(r, g, b)
+				totem.bg:SetVertexColor(r * mu, b * mu, g * mu)
+
+				local value = totem:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
+				value:SetFont(LibSharedMedia:Fetch("font", config.numberFont), 15)
+				value:SetPoint("CENTER")
+			end
+		end
+		Totems.PreUpdate = Stardust.PreUpdateTotems
+		self.Totems = Totems
+	end
+
+	-- TODO: burning embers
+
+	---------------------------
+	-- Druid mana
+	-- Warlock demonic fury
+	---------------------------
+
+	if class == "DRUID" or class == "WARLOCK" then
+		local bar = NewStatusBar(Health)
+		bar:SetPoint("TOPLEFT", 3, -3)
+		bar:SetPoint("TOPRIGHT", -3, -3)
+		bar:SetHeight(config.powerHeight)
+		if class == "DRUID" then
+			bar:SetPoint("TOPLEFT", self.Totems[4], 3, 0)
+			bar.colorPower = true
+			bar.PostUpdate = PostUpdatePower
+			self.DruidMana = bar
+		else
+			bar.colorPower = true
+			bar.PostUpdate = PostUpdatePower
+			self.DemonicFury = bar
+		end
+	end
+
+	---------------------------
+	-- TODO
+	---------------------------
+
+	-- AltPowerBar
+	-- EclipseBar
+	-- HealPrediction
+	-- Stagger
+
+	-- Castbar
+
+	-- Auras
+
+	---------------------------
+	-- Name text
+	---------------------------
+
+	local Name = self:CreateFontString(nil, "OVERLAY", "GameFontNormal")
+	Name:SetPoint("BOTTOMLEFT", Power, "TOPLEFT", 0, 3)
+	Name:SetFont(LibSharedMedia:Fetch("font", config.valueFont), 14)
+	self:Tag(Name, "[unitcolor][name]")
+	self.Name = Name
+
+	---------------------------
+	-- Group status icons
+	---------------------------
+
+	AddStatusIcon("LFDRole")
+	AddStatusIcon("RaidRole") -- maintank, mainassist
+	AddStatusIcon("Leader")
+	AddStatusIcon("Assistant")
+	AddStatusIcon("MasterLooter")
+	AddStatusIcon("PvP")
+
+	---------------------------
+	-- Combat icon
+	---------------------------
+
+	if unit == "player" then
+		local Combat = self.Health:CreateTexture(nil, "OVERLAY")
+		Combat:SetSize(16, 16)
+		Combat:SetPoint("RIGHT", self.Health, "TOPRIGHT", -3, 0)
+		Combat.PostUpdate = Stardust.PostUpdateCombat
+		self.Combat = Combat
+
+		local Resting = self.Health:CreateTexture(nil, "OVERLAY")
+		Resting:SetSize(16, 16)
+		Resting:SetPoint("RIGHT", self.Health, "TOPRIGHT", -3, 0)
+		Resting.PostUpdate = Stardust.PostUpdateResting
+		self.Resting = Resting
+	end
+
+	---------------------------
+	-- Phasing icon
+	---------------------------
+
+	if not unit:match(".+target$") and not unit:match(".+pet") then
+		local PhaseIcon = self.Health:CreateTexture(nil, "OVERLAY")
+		PhaseIcon:SetPoint("TOP")
+		PhaseIcon:SetPoint("BOTTOM")
+		PhaseIcon:SetWidth(config.height * 2.5)
+		PhaseIcon:SetTexture("Interface\\Icons\\Spell_Frost_Stun")
+		PhaseIcon:SetTexCoord(0.05, 0.95, 0.5 - 0.25 * 0.9, 0.5 + 0.25 * 0.9)
+		PhaseIcon:SetAlpha(0.5)
+		PhaseIcon:SetBlendMode("ADD")
+		PhaseIcon:SetDesaturated(true)
+		PhaseIcon:SetVertexColor(0.4, 0.8, 1)
+		self.PhaseIcon = PhaseIcon
+	end
+
+	---------------------------
+	-- Quest mob icon
+	---------------------------
+
+	if unit == "target" then
+		local QuestIcon = self.Health:CreateTexture(nil, "OVERLAY")
+		QuestIcon:SetSize(16, 16)
+		QuestIcon:SetPoint("RIGHT", self.Health, "TOPRIGHT", -3, 0)
+		self.QuestIcon = QuestIcon
+	end
+
+	---------------------------
+	-- Raid target icon
+	---------------------------
+
+	if unit == "player" or unit == "target" or unit == "party" then
+		local RaidIcon = self.Health:CreateTexture(nil, "OVERLAY")
+		RaidIcon:SetSize(24, 24)
+		RaidIcon:SetPoint("CENTER", 0, 3) -- TODO
+		self.RaidIcon = RaidIcon
+	end
+
+	---------------------------
+	-- Ready Check icon
+	---------------------------
+
+	if unit == "player" or unit == "target" or unit == "party" then
+		local ReadyCheck = self.Health:CreateTexture(nil, "OVERLAY")
+		ReadyCheck:SetSize(24, 24)
+		ReadyCheck:SetPoint("CENTER", 0, 3) -- TODO
+		self.ReadyCheck = ReadyCheck
+	end
+
+	---------------------------
+	-- Resurrection icon
+	---------------------------
+
+	if not unit:match(".+target$") and not unit:match("pet") then
+		local ResurrectIcon = self.Health:CreateTexture(nil, "OVERLAY")
+		ResurrectIcon:SetSize(24, 24)
+		ResurrectIcon:SetPoint("CENTER", 0, 3) -- TODO
+		self.ResurrectIcon = ResurrectIcon
+	end
+
+	---------------------------
+	-- Threat level icon
+	---------------------------
+
+	if unit == "target" or unit == "focus" then
+		local Threat = self.Health:CreateTexture(nil, "OVERLAY")
+		Threat:SetSize(16, 16)
+		Threat:SetPoint("CENTER", 0, 3) -- TODO
+		self.Threat = Threat
+	end
+
+	---------------------------
+	-- Range
+	---------------------------
+
+	self.Range = {
+		insideAlpha = 1,
+		outsideAlpha = 0.5
+	}
+end
+
+oUF:Factory(function(self)
+	self:RegisterStyle("Stardust", ApplyStyle)
+	self:SetActiveStyle("Stardust")
+
+	local player = self:Spawn("player")
+	player:SetPoint("BOTTOMRIGHT", UIParent, "BOTTOM", -175, 235)
+
+	local pet = self:Spawn("pet")
+	pet:SetPoint("TOPRIGHT", player, "BOTTOMRIGHT", 0, -6)
+
+	local target = self:Spawn("target")
+	target:SetPoint("BOTTOMLEFT", UIParent, "BOTTOM", 175, 235)
+
+	local targettarget = self:Spawn("targettarget")
+	targettarget:SetPoint("TOPLEFT", target, "BOTTOMLEFT", 0, -6)
+end)
+
diff --git a/Tags.lua b/Tags.lua
new file mode 100644
index 0000000..329b254
--- /dev/null
+++ b/Tags.lua
@@ -0,0 +1,51 @@
+--[[-----------------------------------------------------------------------
+	oUF: Stardust - a layout for the oUF framework
+	Copyright (c) 2016 Andrew Mordecai <armordecai@protonmail.ch>
+	This code is released under the zlib license; see LICENSE for details.
+-------------------------------------------------------------------------]]
+
+local _, Stardust = ...
+
+oUF.Tags.Events["unitcolor"] = "UNIT_HEALTH UNIT_CLASSIFICATION_CHANGED UNIT_CONNECTION UNIT_FACTION UNIT_REACTION"
+oUF.Tags.Methods["unitcolor"] = function(unit)
+	local color
+	if UnitIsDeadOrGhost(unit) or not UnitIsConnected(unit) then
+		color = oUF.colors.disconnected
+	elseif UnitIsPlayer(unit) then
+		local _, class = UnitClass(unit)
+		color = oUF.colors.class[class]
+	elseif UnitIsTapped(unit) and not UnitIsTappedByPlayer(unit) and not UnitIsTappedByAllThreatList(unit) then
+		color = oUF.colors.tapped
+	elseif UnitIsEnemy(unit, "player") then
+		color = oUF.colors.reaction[1]
+	else
+		color = oUF.colors.reaction[UnitReaction(unit, "player") or 5]
+	end
+	return color and ("|cff%02x%02x%02x"):format(color[1] * 255, color[2] * 255, color[3] * 255) or "|cffffffff"
+end
+
+oUF.Tags.Events["powercolor"] = "UNIT_DISPLAYPOWER"
+oUF.Tags.Methods["powercolor"] = function(unit)
+	local _, type = UnitPowerType(unit)
+	local color = ns.colors.power[type] or ns.colors.power.FUEL
+	return format("|cff%02x%02x%02x", color[1] * 255, color[2] * 255, color[3] * 255)
+end
+
+local threatcolors = {
+	[0] = "|cffffffff",
+	[1] = "|cffffff33",
+	[2] = "|cffff9933",
+	[3] = "|cffff3333",
+}
+oUF.Tags.Events["threatpct"] = "UNIT_THREAT_LIST_UPDATE"
+oUF.Tags.Methods["threatpct"] = function(unit)
+	local isTanking, status, percentage, rawPercentage = UnitDetailedThreatSituation("player", unit)
+	local pct = rawPercentage
+	if isTanking then
+		pct = UnitThreatPercentageOfLead("player", unit)
+	end
+	if pct and pct > 0 and pct < 200 then
+		return format("%s%d%%", threatcolors[status] or threatcolors[0], pct + 0.5)
+	end
+end
+
diff --git a/config.lua b/config.lua
deleted file mode 100644
index dd368dd..0000000
--- a/config.lua
+++ /dev/null
@@ -1,58 +0,0 @@
---[[-----------------------------------------------------------------------
-	oUF: Stardust - a layout for the oUF framework
-	Copyright (c) 2016 Andrew Mordecai <armordecai@protonmail.ch>
-	This code is released under the zlib license; see LICENSE for details.
--------------------------------------------------------------------------]]
-
-local _, Stardust = ...
-
-Stardust.config = {
-	-- Style
-	barTexture = "Qulight",
-	bgMultiplier = 0.35,
-	numberFont = "Blizzard Bold",
-	textFont = "Blizzard",
-
-	-- Layout
-	width = 220,
-	height = 32,
-	power = true,
-	powerHeight = 5,
-
-	-- Unit Specific
-	units = {
-		pet = {
-			width = 0.5
-		},
-		targettarget = {
-			width = 0.5,
-			power = false
-		},
-		focustarget = {
-			width = 0.5,
-			power = false
-		},
-		party = {
-			width = 0.5
-		},
-		partypet = {
-			width = 0.25,
-			power = false
-		}
-	}
-}
-
-Stardust.colors = {
-	power = {
-		MANA = { 0, 0.8, 1 }
-	},
-	totems = {
-		[1] = { 0.6, 1,   0.2 }, -- Earth
-		[2] = { 1,   0.6, 0.2 }, -- Fire
-		[3] = { 0.2, 0.8, 1   }, -- Water
-		[4] = { 0.8, 0.4, 1   }, -- Air
-	}
-}
-
-setmetatable(Stardust.colors, oUF.colors)
-setmetatable(Stardust.colors.power, oUF.colors.power)
diff --git a/functions.lua b/functions.lua
deleted file mode 100644
index 224e4f1..0000000
--- a/functions.lua
+++ /dev/null
@@ -1,212 +0,0 @@
---[[-----------------------------------------------------------------------
-	oUF: Stardust - a layout for the oUF framework
-	Copyright (c) 2016 Andrew Mordecai <armordecai@protonmail.ch>
-	This code is released under the zlib license; see LICENSE for details.
--------------------------------------------------------------------------]]
-
-local _, Stardust = ...
-
----------------------------
--- Health
----------------------------
-
-function Stardust.PostUpdateHealth(self, unit, cur, max)
-	self:SetValue(max - cur)
-	self.text:SetText(cur < max and -max + cur or "")
-	self.bg:SetVertexColor(0.15, 0.15, 0.15)
-end
-
----------------------------
--- Power
--- DruidMana
--- DemonicFury
----------------------------
-
-function Stardust.PostUpdatePower(self, unit, cur, max)
-	self:SetShown(max > 0)
-	self.text:SetText(cur)
-end
-
----------------------------
--- ClassIcons
----------------------------
-
-function Stardust.ResizeClassIcons(element)
-	local COUNT = element.__max
-	local SPACING = 3
-	local widthTotal = ((Stardust.config.width - SPACING - SPACING) * 0.6) - (COUNT * SPACING)
-	local widthEach = floor(widthTotal / COUNT + 0.5)
-	for i = 1, COUNT do
-		element[i]:SetWidth(widthEach)
-	end
-end
-
-function Stardust.PostUpdateClassIcons(element, cur, max, maxChanged, event)
-	if maxChanged then
-		Stardust.ResizeClassIcons(element)
-	end
-end
-
----------------------------
--- Combo points
----------------------------
-
-function Stardust.PostUpdateCPoints(element, cur)
-	-- The number of combo points isn't dynamic, so they only need to be resized once per frame.
-	element.PreUpdate = nil
-	Stardust.ResizeClassIcons(element)
-end
-
----------------------------
--- Runes
----------------------------
-
-function Stardust.PostUpdateRuneType(element, rune, index, alt)
-	local r, g, b = rune:GetStatusBarColor()
-	rune.bg:SetVertexColor(r * 0.35, g * 0.35, b * 0.35)
-end
-
-function Stardust.PostUpdateRune(element, rune, index, start, duration, ready)
-	-- The number of runes isn't dynamic, so they only need to be resized once per frame.
-	element.PostUpdateRune = nil
-	Stardust.ResizeClassIcons(element)
-end
-
----------------------------
--- Totems
--- Partly adapted from oUF_Phanx
----------------------------
-
-local function OnUpdateTotem(bar, elapsed)
-	local t = bar.duration - elapsed
-	if t > 0 then
-		bar.duration = t
-		bar:SetValue(t)
-	else
-		bar:SetValue(0)
-	end
-end
-
-function Stardust.PostUpdateTotem(element, index, _, name, start, duration)
-	local bar = element[index]
-	bar.duration, bar.max = duration, duration
-	if duration > 0 then
-		bar:SetMinMaxValues(0, duration)
-		bar:SetScript("OnUpdate", OnUpdateTotem)
-	else
-		bar:SetScript("OnUpdate", nil)
-		bar:SetValue(0)
-	end
-end
-
-function Stardust.PreUpdateTotems(element, index)
-	-- The number of totems isn't dynamic, so they only need to be resized once per frame.
-	element.PreUpdate = nil
-	Stardust.ResizeClassIcons(element)
-end
-
----------------------------
--- Status icons
----------------------------
-
-function Stardust.PostUpdateStatusIcon(self)
-	if not self:IsShown() then return end
-	local icons, last = self.__owner.StatusIcons
-	for i = 1, #icons do
-		local icon = icons[i]
-		if icon == self then
-			break
-		elseif icon:IsShown() then
-			last = icon
-		end
-	end
-	if last then
-		self:SetPoint("LEFT", last, "RIGHT", 0, 0)
-	else
-		self:SetPoint("LEFT", self.__owner.Health, "TOPLEFT", 3, 0)
-	end
-end
-
-function Stardust.PostUpdateCombat(self)
-	self.__owner.Resting:ForceUpdate()
-end
-
-function Stardust.PostUpdateResting(self)
-	self.__owner.Combat:ForceUpdate()
-end
-
----------------------------
--- Glow
----------------------------
-
-local GLOW_SEGMENTS = {
-	{ "TOPLEFT", 0, 1/3, 0, 1/3, -1, 1 },
-	{ "TOPRIGHT", 2/3, 1, 0, 1/3, 1, 1 },
-	{ "BOTTOMRIGHT", 2/3, 1, 2/3, 1, 1, -1 },
-	{ "BOTTOMLEFT", 0, 1/3, 2/3, 1, -1, -1 },
-	{ "TOP", 1/3, 2/3, 0, 1/3, 0, 1 },
-	{ "RIGHT", 2/3, 1, 1/3, 2/3, 1, 0 },
-	{ "BOTTOM", 1/3, 2/3, 2/3, 1, 0, -1 },
-	{ "LEFT", 0, 1/3, 1/3, 2/3, -1, 0 },
-}
-
-function Stardust.SetGlowColor(self, r, g, b, a)
-	for i = 1, #self.Glow do
-		self.Glow[i]:SetVertexColor(r, g, b, a)
-	end
-end
-
-function Stardust.SetGlowSize(self, size, offset)
-	local Glow = self.Glow
-	for i = 1, #Glow do
-		Glow[i]:SetSize(size, size)
-	end
-
-	local d = d or floor(size * 2 / 3 + 0.5)
-	Glow[1]:SetPoint("TOPLEFT", -d, d)
-	Glow[2]:SetPoint("TOPRIGHT", d, d)
-	Glow[3]:SetPoint("BOTTOMRIGHT", d, -d)
-	Glow[4]:SetPoint("BOTTOMLEFT", -d, -d)
-
-	Glow[5]:SetShown(Glow[2]:GetLeft() > Glow[1]:GetRight())
-	Glow[6]:SetShown(Glow[2]:GetBottom() > Glow[3]:GetTop())
-	Glow[7]:SetShown(Glow[3]:GetLeft() > Glow[4]:GetLeft())
-	Glow[8]:SetShown(Glow[1]:GetBottom() > Glow[4]:GetTop())
-end
-
-function Stardust.CreateGlow(self)
-	local Glow = {}
-	for i = 1, #GLOW_SEGMENTS do
-		local seg = GLOW_SEGMENTS[i]
-		local tex = self:CreateTexture(nil, "BACKGROUND")
-		tex:SetTexture("Interface\\AddOns\\oUF_Stardust\\textures\\glow")
-		tex:SetTexCoord(seg[2], seg[3], seg[4], seg[5])
-		tex:SetVertexColor(0, 0, 0)
-		Glow[i] = tex
-	end
-
-	-- TOP
-	Glow[5]:SetPoint("LEFT", Glow[1], "RIGHT")
-	Glow[5]:SetPoint("RIGHT", Glow[2], "LEFT")
-
-	-- RIGHT
-	Glow[6]:SetPoint("TOP", Glow[2], "BOTTOM")
-	Glow[6]:SetPoint("BOTTOM", Glow[3], "TOP")
-
-	-- BOTTOM
-	Glow[7]:SetPoint("LEFT", Glow[4], "RIGHT")
-	Glow[7]:SetPoint("RIGHT", Glow[3], "LEFT")
-
-	-- LEFT
-	Glow[8]:SetPoint("TOP", Glow[1], "BOTTOM")
-	Glow[8]:SetPoint("BOTTOM", Glow[4], "TOP")
-
-	SetGlowSize(self, 12)
-
-	self.Glow = Glow
-	self.SetBackdropBorderColor = SetGlowColor
-	self.SetGlowColor = SetGlowColor
-	self.SetGlowSize = SetGlowSize
-	return Glow
-end
-
diff --git a/includes/DemonicFury.lua b/includes/DemonicFury.lua
deleted file mode 100644
index ef8c32a..0000000
--- a/includes/DemonicFury.lua
+++ /dev/null
@@ -1,116 +0,0 @@
---[[--------------------------------------------------------------------
-	oUF_Phanx
-	Fully-featured PVE-oriented layout for oUF.
-	Copyright (c) 2008-2015 Phanx <addons@phanx.net>. All rights reserved.
-	http://www.wowinterface.com/downloads/info13993-oUF_Phanx.html
-	http://www.curse.com/addons/wow/ouf-phanx
-	https://github.com/Phanx/oUF_Phanx
-------------------------------------------------------------------------
-	Element to display demonic fury on oUF frames.
-
-	You may embed this module in your own layout, but please do not
-	distribute it as a standalone plugin.
-----------------------------------------------------------------------]]
-
-local _, ns = ...
-local oUF = ns.oUF or oUF
-assert(oUF, "DemonicFury element requires oUF")
-
-local METAMORPHOSIS_FORM = 22
-local SPEC_WARLOCK_DEMONOLOGY = SPEC_WARLOCK_DEMONOLOGY
-local SPELL_POWER_DEMONIC_FURY = SPELL_POWER_DEMONIC_FURY
-
-oUF.colors.power.DEMONIC_FURY = { 132/255, 41/255, 235/255 }
-oUF.colors.power.METAMORPHOSIS = { 132/255, 235/255, 41/255 }
-
-local UpdateVisibility, Update, Path, ForceUpdate, Enable, Disable
-
-function UpdateVisibility(self, event, unit)
-	local element = self.DemonicFury
-
-	if UnitHasVehicleUI("player") or GetSpecialization() ~= SPEC_WARLOCK_DEMONOLOGY then
-		self:UnregisterEvent("SHAPESHIFT_FORM_CHANGED", Path)
-		self:UnregisterEvent("UNIT_POWER", Path)
-		element:Hide()
-		return
-	end
-
-	element:Show()
-	self:RegisterEvent("SHAPESHIFT_FORM_CHANGED", Path, true)
-	self:RegisterEvent("UNIT_POWER", Path)
-
-	Update(self, "UpdateVisibility", "player")
-end
-
-function Update(self, event, unit, powerType)
-	if powerType and powerType ~= "DEMONIC_FURY" then return end
-	local element = self.DemonicFury
-	if not element:IsShown() then return end
-
-	if element.PreUpdate then
-		element:PreUpdate()
-	end
-
-	local fury = UnitPower("player", SPELL_POWER_DEMONIC_FURY)
-	local maxFury = UnitPowerMax("player", SPELL_POWER_DEMONIC_FURY)
-	local inMetamorphosis = GetShapeshiftFormID() == METAMORPHOSIS_FORM
-	local color = oUF.colors.power[inMetamorphosis and "METAMORPHOSIS" or "DEMONIC_FURY"]
-	local r, g, b = color[1], color[2], color[3]
-
-	element:SetMinMaxValues(0, maxFury)
-	element:SetValue(fury)
-	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
-
-	if element.PostUpdate then
-		element:PostUpdate(fury, maxFury, inMetamorphosis)
-	end
-end
-
-function Path(self, ...)
-	return (self.DemonicFury.Override or Update)(self, ...)
-end
-
-function ForceUpdate(element)
-	return Path(element.__owner, "ForceUpdate", element.__owner.unit)
-end
-
-function Enable(self, unit)
-	local element = self.DemonicFury
-	if not element or self.unit ~= "player" then return end
-
-	element.__owner = self
-	element.ForceUpdate = ForceUpdate
-
-	if element.GetStatusBarTexture and not element:GetStatusBarTexture() then
-		element:SetStatusBarTexture([[Interface\TargetingFrame\UI-StatusBar]])
-	end
-
-	self:RegisterEvent("PLAYER_TALENT_UPDATE", UpdateVisibility, true)
-	self:RegisterEvent("UNIT_ENTERING_VEHICLE", UpdateVisibility)
-	self:RegisterEvent("UNIT_EXITED_VEHICLE", UpdateVisibility)
-
-	UpdateVisibility(self, "Enable", "player")
-	return true
-end
-
-function Disable(self)
-	local element = self.DemonicFury
-	if not element then return end
-
-	self:UnregisterEvent("SHAPESHIFT_FORM_CHANGED", Path)
-	self:UnregisterEvent("UNIT_POWER", Path)
-
-	self:UnregisterEvent("PLAYER_TALENT_UPDATE", UpdateVisibility)
-	self:UnregisterEvent("UNIT_ENTERING_VEHICLE", UpdateVisibility)
-	self:UnregisterEvent("UNIT_EXITED_VEHICLE", UpdateVisibility)
-
-	element:Hide()
-end
-
-oUF:AddElement("DemonicFury", Path, Enable, Disable)
diff --git a/layout.lua b/layout.lua
deleted file mode 100644
index 8de2d1a..0000000
--- a/layout.lua
+++ /dev/null
@@ -1,380 +0,0 @@
---[[-----------------------------------------------------------------------
-	oUF: Stardust - a layout for the oUF framework
-	Copyright (c) 2016 Andrew Mordecai <armordecai@protonmail.ch>
-	This code is released under the zlib license; see LICENSE for details.
--------------------------------------------------------------------------]]
-
-local _, Stardust = ...
-local config = Stardust.config
-local colors = Stardust.colors
-
-local _, class = UnitClass("player")
-local LibSharedMedia = LibStub("LibSharedMedia-3.0")
-
----------------------------
--- Widget creation
----------------------------
-
-local function NewStatusBar(parent, textFont, textSize, noBG, noBackdrop)
-	local TEXTURE = LibSharedMedia:Fetch("statusbar", config.barTexture)
-
-	local bar = CreateFrame("StatusBar", nil, parent)
-	bar:SetStatusBarTexture(TEXTURE)
-
-	if textFont and textSize then
-		bar.value = bar:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
-		bar.value:SetFont(LibSharedMedia:Fetch("font", textSize))
-		bar.value:SetPoint("CENTER")
-	end
-
-	if not noBG then
-		bar.bg = bar:CreateTexture(nil, "BORDER")
-		bar.bg:SetAllPoints()
-		bar.bg:SetTexture(TEXTURE)
-		bar.bg.multiplier = config.bgMultiplier
-	end
-
-	if not noBackdrop then
-		bar.backdrop = Health:CreateTexture(nil, "BACKDROP")
-		bar.backdrop:SetPoint("BOTTOMLEFT", -1, -1)
-		bar.backdrop:SetPoint("TOPRIGHT", 1, 1)
-		bar.backdrop:SetTexture(0, 0, 0)
-	end
-
-	return bar
-end
-
-local function NewBarGroup(parent, count, width, height)
-	local group = { __max = count }
-
-	local color = Stardust.colors.class[class]
-	local r, g, b = color[1], color[2], color[3]
-
-	for i = 1, count do
-		local bar = NewStatusBar(parent)
-		bar:SetSize(width, height)
-		bar:SetMinMaxValues(0, 1)
-		bar:SetValue(1)
-
-		if not noColor then
-			local mu = bar.bg.multiplier
-			bar:SetStatusBarColor(r, g, b)
-			bar.bg:SetVertexColor(r * mu, g * mu, b * mu)
-		end
-
-		-- ClassIcons expects texture objects
-		bar.SetVertexColor = bar.SetStatusBarColor
-
-		if i > 1 then
-			bar:SetPoint("LEFT", group[i-1], "RIGHT", 3, 0)
-		end
-
-		group[i] = bar
-	end
-
-	return group
-end
-
-local function AddStatusIcon(self, element, size)
-	local icon = self.Health:CreateTexture(nil, "OVERLAY")
-	icon:SetPoint("LEFT", self.Health, "TOPLEFT", 3, 0)
-	icon:SetSize(size or 16, size or 16)
-
-	self.StatusIcons = self.StatusIcons or {}
-	tinsert(self.StatusIcons, icon)
-
-	icon.PostUpdate = Stardust.PostUpdateStatusIcon
-	self[element] = icon
-	return icon
-end
-
----------------------------
--- Frame setup
----------------------------
-
-local function ApplyStyle(self, unit, isSingle)
-	unit = unit:gsub("%d+", "")
-
-	local uconfig = config.units[unit]
-
-	---------------------------
-	-- Frame
-	---------------------------
-
-	self:SetScript("OnEnter", UnitFrame_OnEnter)
-	self:SetScript("OnLeave", UnitFrame_OnLeave)
-
-	self:RegisterForClicks("AnyUp")
-
-	if (isSingle) then
-		self:SetSize(config.width * (uconfig and uconfig.width or 1), config.height * (uconfig and uconfig.height or 1))
-	end
-
-	---------------------------
-	-- Outer glow
-	---------------------------
-
-	self.Glow = CreateGlow(self)
-
-	---------------------------
-	-- Health bar
-	---------------------------
-
-	local Health = NewStatusBar(self, config.numberFont, 25)
-	Health:SetPoint("BOTTOMLEFT")
-	Health:SetPoint("TOPRIGHT")
-	Health:SetReverseFill(true)
-
-	Health.value:ClearAllPoints()
-	Health.value:SetPoint("RIGHT", Health, "TOPRIGHT", 0, 3)
-
-	Health.colorClass = true
-	Health.colorDisconnected = true
-	Health.colorReaction = true
-	Health.colorTapping = true
-	Health.frequentUpdates = true
-	Health.PostUpdate = Stardust.PostUpdateHealth
-	self.Health = Health
-
-	---------------------------
-	-- Power bar
-	---------------------------
-
-	local Power = NewStatusBar(Health, config.numberFont, 15)
-	Power:SetPoint("BOTTOMLEFT", 3, 3)
-	Power:SetPoint("BOTTOMRIGHT", -3, 3)
-	Power:SetHeight(config.powerHeight)
-
-	Power.colorDisconnected = true
-	Power.colorPower = true
-	Power.PostUpdate = Stardust.PostUpdatePower
-	self.Power = Power
-
-	---------------------------
-	-- Secondary resources
-	---------------------------
-
-	if class == "PALADIN" or class == "PRIEST" or class == "WARLOCK" then
-		local ClassIcons = NewBarGroup(self.Health, 6, 30, config.powerHeight)
-		ClassIcons[1]:SetPoint("TOPLEFT", self.Health, 3, -3)
-		ClassIcons.PostUpdate = Stardust.PostUpdateClassIcons
-		self.ClassIcons = ClassIcons
-	end
-
-	if class == "DRUID" or class == "ROGUE" then
-		local CPoints = NewBarGroup(self.Health, 5, 30, config.powerHeight)
-		CPoints[1]:SetPoint("TOPLEFT", self.Health, 3, -3)
-		CPoints.PostUpdate = Stardust.PostUpdateCPoints
-		self.CPoints = CPoints
-	end
-
-	if class == "DEATHKNIGHT" then
-		local Runes = NewBarGroup(self.Health, 6, 30, config.powerHeight)
-		Runes[1]:SetPoint("TOPLEFT", self.Health, 3, -3)
-		Runes.PostUpdateRune = Stardust.PostUpdateRune
-		Runes.PostUpdateRuneType = Stardust.PostUpdateRuneType
-		self.Runes = Runes
-	end
-
-	if class == "DRUID" or class == "SHAMAN" then
-	    -- TODO: This can also show death knight ghouls.
-	    -- How do these work? Are they important?
-	    -- How to show them at the same time as runes?
-		local Totems = NewBarGroup(self.Health, 4, 30, config.powerHeight)
-		Totems[1]:SetPoint("TOPLEFT", self.Health, 3, -3)
-		if class == "SHAMAN" then
-			local mu = config.bgMultiplier
-			for i = 1, 4 do
-				local totem = Totems[i]
-				totem:EnableMouse(true)
-
-				local color = Stardust.colors.totems[i]
-				local r, g, b = color[1], color[2], color[3]
-				totem:SetStatusBarColor(r, g, b)
-				totem.bg:SetVertexColor(r * mu, b * mu, g * mu)
-
-				local value = totem:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
-				value:SetFont(LibSharedMedia:Fetch("font", config.numberFont), 15)
-				value:SetPoint("CENTER")
-			end
-		end
-		Totems.PreUpdate = Stardust.PreUpdateTotems
-		self.Totems = Totems
-	end
-
-	-- TODO: burning embers
-
-	---------------------------
-	-- Druid mana
-	-- Warlock demonic fury
-	---------------------------
-
-	if class == "DRUID" or class == "WARLOCK" then
-		local bar = NewStatusBar(Health)
-		bar:SetPoint("TOPLEFT", 3, -3)
-		bar:SetPoint("TOPRIGHT", -3, -3)
-		bar:SetHeight(config.powerHeight)
-		if class == "DRUID" then
-			bar:SetPoint("TOPLEFT", self.Totems[4], 3, 0)
-			bar.colorPower = true
-			bar.PostUpdate = PostUpdatePower
-			self.DruidMana = bar
-		else
-			bar.colorPower = true
-			bar.PostUpdate = PostUpdatePower
-			self.DemonicFury = bar
-		end
-	end
-
-	---------------------------
-	-- TODO
-	---------------------------
-
-	-- AltPowerBar
-	-- EclipseBar
-	-- HealPrediction
-	-- Stagger
-
-	-- Castbar
-
-	-- Auras
-
-	---------------------------
-	-- Name text
-	---------------------------
-
-	local Name = self:CreateFontString(nil, "OVERLAY", "GameFontNormal")
-	Name:SetPoint("BOTTOMLEFT", Power, "TOPLEFT", 0, 3)
-	Name:SetFont(LibSharedMedia:Fetch("font", config.valueFont), 14)
-	self:Tag(Name, "[unitcolor][name]")
-	self.Name = Name
-
-	---------------------------
-	-- Group status icons
-	---------------------------
-
-	AddStatusIcon("LFDRole")
-	AddStatusIcon("RaidRole") -- maintank, mainassist
-	AddStatusIcon("Leader")
-	AddStatusIcon("Assistant")
-	AddStatusIcon("MasterLooter")
-	AddStatusIcon("PvP")
-
-	---------------------------
-	-- Combat icon
-	---------------------------
-
-	if unit == "player" then
-		local Combat = self.Health:CreateTexture(nil, "OVERLAY")
-		Combat:SetSize(16, 16)
-		Combat:SetPoint("RIGHT", self.Health, "TOPRIGHT", -3, 0)
-		Combat.PostUpdate = Stardust.PostUpdateCombat
-		self.Combat = Combat
-
-		local Resting = self.Health:CreateTexture(nil, "OVERLAY")
-		Resting:SetSize(16, 16)
-		Resting:SetPoint("RIGHT", self.Health, "TOPRIGHT", -3, 0)
-		Resting.PostUpdate = Stardust.PostUpdateResting
-		self.Resting = Resting
-	end
-
-	---------------------------
-	-- Phasing icon
-	---------------------------
-
-	if not unit:match(".+target$") and not unit:match(".+pet") then
-		local PhaseIcon = self.Health:CreateTexture(nil, "OVERLAY")
-		PhaseIcon:SetPoint("TOP")
-		PhaseIcon:SetPoint("BOTTOM")
-		PhaseIcon:SetWidth(config.height * 2.5)
-		PhaseIcon:SetTexture("Interface\\Icons\\Spell_Frost_Stun")
-		PhaseIcon:SetTexCoord(0.05, 0.95, 0.5 - 0.25 * 0.9, 0.5 + 0.25 * 0.9)
-		PhaseIcon:SetAlpha(0.5)
-		PhaseIcon:SetBlendMode("ADD")
-		PhaseIcon:SetDesaturated(true)
-		PhaseIcon:SetVertexColor(0.4, 0.8, 1)
-		self.PhaseIcon = PhaseIcon
-	end
-
-	---------------------------
-	-- Quest mob icon
-	---------------------------
-
-	if unit == "target" then
-		local QuestIcon = self.Health:CreateTexture(nil, "OVERLAY")
-		QuestIcon:SetSize(16, 16)
-		QuestIcon:SetPoint("RIGHT", self.Health, "TOPRIGHT", -3, 0)
-		self.QuestIcon = QuestIcon
-	end
-
-	---------------------------
-	-- Raid target icon
-	---------------------------
-
-	if unit == "player" or unit == "target" or unit == "party" then
-		local RaidIcon = self.Health:CreateTexture(nil, "OVERLAY")
-		RaidIcon:SetSize(24, 24)
-		RaidIcon:SetPoint("CENTER", 0, 3) -- TODO
-		self.RaidIcon = RaidIcon
-	end
-
-	---------------------------
-	-- Ready Check icon
-	---------------------------
-
-	if unit == "player" or unit == "target" or unit == "party" then
-		local ReadyCheck = self.Health:CreateTexture(nil, "OVERLAY")
-		ReadyCheck:SetSize(24, 24)
-		ReadyCheck:SetPoint("CENTER", 0, 3) -- TODO
-		self.ReadyCheck = ReadyCheck
-	end
-
-	---------------------------
-	-- Resurrection icon
-	---------------------------
-
-	if not unit:match(".+target$") and not unit:match("pet") then
-		local ResurrectIcon = self.Health:CreateTexture(nil, "OVERLAY")
-		ResurrectIcon:SetSize(24, 24)
-		ResurrectIcon:SetPoint("CENTER", 0, 3) -- TODO
-		self.ResurrectIcon = ResurrectIcon
-	end
-
-	---------------------------
-	-- Threat level icon
-	---------------------------
-
-	if unit == "target" or unit == "focus" then
-		local Threat = self.Health:CreateTexture(nil, "OVERLAY")
-		Threat:SetSize(16, 16)
-		Threat:SetPoint("CENTER", 0, 3) -- TODO
-		self.Threat = Threat
-	end
-
-	---------------------------
-	-- Range
-	---------------------------
-
-	self.Range = {
-		insideAlpha = 1,
-		outsideAlpha = 0.5
-	}
-end
-
-oUF:Factory(function(self)
-	self:RegisterStyle("Stardust", ApplyStyle)
-	self:SetActiveStyle("Stardust")
-
-	local player = self:Spawn("player")
-	player:SetPoint("BOTTOMRIGHT", UIParent, "BOTTOM", -175, 235)
-
-	local pet = self:Spawn("pet")
-	pet:SetPoint("TOPRIGHT", player, "BOTTOMRIGHT", 0, -6)
-
-	local target = self:Spawn("target")
-	target:SetPoint("BOTTOMLEFT", UIParent, "BOTTOM", 175, 235)
-
-	local targettarget = self:Spawn("targettarget")
-	targettarget:SetPoint("TOPLEFT", target, "BOTTOMLEFT", 0, -6)
-end)
-
diff --git a/oUF_Stardust.toc b/oUF_Stardust.toc
index 0e3b336..0848834 100644
--- a/oUF_Stardust.toc
+++ b/oUF_Stardust.toc
@@ -7,14 +7,15 @@
 ## X-Email: armordecai@protonmail.ch
 ## X-License: zlib/libpng License

-libs/LibStub.lua
-libs/CallbackHandler-1.0.lua
-libs/LibSharedMedia-3.0.lua
+Libs/LibStub.lua
+Libs/CallbackHandler-1.0.lua
+Libs/LibSharedMedia-3.0.lua

-includes/DemonicFury.lua
+Elements/BurningEmbers.lua
+Elements/DemonicFury.lua

-config.lua
-functions.lua
-tags.lua
+Config.lua
+Functions.lua
+Tags.lua

-layout.lua
+Layout.lua
diff --git a/tags.lua b/tags.lua
deleted file mode 100644
index 329b254..0000000
--- a/tags.lua
+++ /dev/null
@@ -1,51 +0,0 @@
---[[-----------------------------------------------------------------------
-	oUF: Stardust - a layout for the oUF framework
-	Copyright (c) 2016 Andrew Mordecai <armordecai@protonmail.ch>
-	This code is released under the zlib license; see LICENSE for details.
--------------------------------------------------------------------------]]
-
-local _, Stardust = ...
-
-oUF.Tags.Events["unitcolor"] = "UNIT_HEALTH UNIT_CLASSIFICATION_CHANGED UNIT_CONNECTION UNIT_FACTION UNIT_REACTION"
-oUF.Tags.Methods["unitcolor"] = function(unit)
-	local color
-	if UnitIsDeadOrGhost(unit) or not UnitIsConnected(unit) then
-		color = oUF.colors.disconnected
-	elseif UnitIsPlayer(unit) then
-		local _, class = UnitClass(unit)
-		color = oUF.colors.class[class]
-	elseif UnitIsTapped(unit) and not UnitIsTappedByPlayer(unit) and not UnitIsTappedByAllThreatList(unit) then
-		color = oUF.colors.tapped
-	elseif UnitIsEnemy(unit, "player") then
-		color = oUF.colors.reaction[1]
-	else
-		color = oUF.colors.reaction[UnitReaction(unit, "player") or 5]
-	end
-	return color and ("|cff%02x%02x%02x"):format(color[1] * 255, color[2] * 255, color[3] * 255) or "|cffffffff"
-end
-
-oUF.Tags.Events["powercolor"] = "UNIT_DISPLAYPOWER"
-oUF.Tags.Methods["powercolor"] = function(unit)
-	local _, type = UnitPowerType(unit)
-	local color = ns.colors.power[type] or ns.colors.power.FUEL
-	return format("|cff%02x%02x%02x", color[1] * 255, color[2] * 255, color[3] * 255)
-end
-
-local threatcolors = {
-	[0] = "|cffffffff",
-	[1] = "|cffffff33",
-	[2] = "|cffff9933",
-	[3] = "|cffff3333",
-}
-oUF.Tags.Events["threatpct"] = "UNIT_THREAT_LIST_UPDATE"
-oUF.Tags.Methods["threatpct"] = function(unit)
-	local isTanking, status, percentage, rawPercentage = UnitDetailedThreatSituation("player", unit)
-	local pct = rawPercentage
-	if isTanking then
-		pct = UnitThreatPercentageOfLead("player", unit)
-	end
-	if pct and pct > 0 and pct < 200 then
-		return format("%s%d%%", threatcolors[status] or threatcolors[0], pct + 0.5)
-	end
-end
-