All pastes #802408 Raw Edit

Miscellany

public text v1 · immutable
#802408 ·published 2007-11-30 03:39 UTC
rendered paste body
---------------------------------------------------------------------------

local L = AceLibrary("AceLocale-2.2"):new("Aloft")
local SML = AceLibrary("SharedMedia-1.0")

-----------------------------------------------------------------------------

L:RegisterTranslations("enUS", function() return {
	["Aloft"] = true,

	["Show Nameplates"] = true,
	["Show nameplates for neutral or hostile units"] = true,
	["Show Friendly Nameplates"] = true,
	["Show nameplates for friendly units"] = true,
	
	["Data Options"] = true,
	["Data options"] = true,

	["Load Options"] = true,
	["Load options for Aloft modules"] = true,

	["Unable to determine module providing data: "] = true,
	["Internal error: Dependency list not resolved - cyclic dependency?"] = true,
} end)

L:RegisterTranslations("koKR", function() return {
	["Aloft"] = "Aloft",

	["Show Nameplates"] = "이름표 표시",
	["Show nameplates for neutral or hostile units"] = "중립적 혹은 적대적 대상에 대한 이름표를 표시합니다.",
	["Show Friendly Nameplates"] = "우호적 이름표 표시",
	["Show nameplates for friendly units"] = "우호적 대상에 대한 이름표를 표시합니다.",
	
	["Data Options"] = "데이터 설정",
	["Data options"] = "데이터에 대한 설정입니다.",

	["Load Options"] = "불러오기 설정",
	["Load options for Aloft modules"] = "Aloft 모듈들에 대한 불러오기 설정입니다.",

	["Unable to determine module providing data: "] = "데이터를 위한 모듈을 결정할 수 없습니다: ",
	["Internal error: Dependency list not resolved - cyclic dependency?"] = "내부적 오류: 목록 의존성이 해결되지 않았습니다 - 순환적인 의존성?",
} end)

L:RegisterTranslations("zhTW", function() return {
	["Aloft"] = "Aloft",

	["Show Nameplates"] = "顯示名牌",
	["Show nameplates for neutral or hostile units"] = "顯示中立或敵對單位的名牌",
	["Show Friendly Nameplates"] = "顯示友好名牌",
	["Show nameplates for friendly units"] = "顯示友好單位的名牌",
	
	["Data Options"] = "資料選項",
	["Data options"] = "資料選項",

	["Load Options"] = "載入選項",
	["Load options for Aloft modules"] = "Aloft 模組的載入選項",

	["Unable to determine module providing data: "] = "不能測定模組。資料: ",
	["Internal error: Dependency list not resolved - cyclic dependency?"] = "內部錯誤: 循環倚靠?",
} end)

L:RegisterTranslations("zhCN", function() return {
	["Aloft"] = "Aloft",

	["Show Nameplates"] = "显示姓名板",
	["Show nameplates for neutral or hostile units"] = "显示中立方或敌对方单位的姓名板",
	["Show Friendly Nameplates"] = "显示友好",
	["Show nameplates for friendly units"] = "显示友好方单位的姓名板",
	
	["Data Options"] = "信息设置",
	["Data options"] = "信息设置",

	["Load Options"] = "读入设置",
	["Load options for Aloft modules"] = "Aloft模块的加载设置",

	["Unable to determine module providing data: "] = "不能测定模块的资料",
	["Internal error: Dependency list not resolved - cyclic dependency?"] = "内部错误:无法确定需求设置,需求循环?",
} end)

-----------------------------------------------------------------------------

Aloft = AceLibrary("AceAddon-2.0"):new("AceEvent-2.0", "AceConsole-2.0", "AceDB-2.0", "AceModuleCore-2.0", "AceHook-2.1")
Aloft.version = "r"..("$Revision: 45867 $"):match("%d+")
Aloft.Debug = (UnitName("player") == "Roartindon") and function(_, text) ChatFrame3:AddMessage(text) end or function() end
Aloft:RegisterDB("AloftDB")

-----------------------------------------------------------------------------

Aloft.Options = 
{
	handler = Aloft,
	type = 'group',
	args = 
	{
		show =
		{
			type = 'toggle',
			name = L["Show Nameplates"],
			desc = L["Show nameplates for neutral or hostile units"],
			get = function() return NAMEPLATES_ON end,
			set = function(v) if v then ShowNameplates() else HideNameplates() end NAMEPLATES_ON = v end,
			order = 1
		},
		showFriendly =
		{
			type = 'toggle',
			name = L["Show Friendly Nameplates"],
			desc = L["Show nameplates for friendly units"],
			get = function() return FRIENDNAMEPLATES_ON end,
			set = function(v) if v then ShowFriendNameplates() else HideFriendNameplates() end FRIENDNAMEPLATES_ON = v end,
			order = 2
		},
		moduleSpacer  = 
		{
			type = 'header',
			order = 4,
		},
		dataSpacer = 
		{
			type = 'header',
			order = 500,
		},
		data =
		{
			type = 'group',
			name = L["Data Options"],
			desc = L["Data options"],
			order = 600,
			disabled = function() return not next(Aloft.Options.args.data.args) end,
			args = { },
		}
	},
}

Aloft:RegisterChatCommand("/aloft", Aloft.Options)

-----------------------------------------------------------------------------

Aloft.DataAvailableMethods = { }

-----------------------------------------------------------------------------

local numberOfWorldChildren = 0
local nameplateList = { }
local noTargetNameplate = false
local nameToNameplateMap = { }
local visibleNameplateList = { }
local dataSourceList = { }

-- Since pairs and select are used often and in loops, make them upvalues rather than a global lookup
local pairs, select = pairs, select

-----------------------------------------------------------------------------

local function UpdateAloftData(aloftData)
	for i = 1, #dataSourceList do
		dataSourceList[i]:UpdateData(aloftData)
	end
end

local function UpdateAloftTypeData(aloftData, healthBar)
	aloftData.originalHealthBarR, aloftData.originalHealthBarG, aloftData.originalHealthBarB, a = healthBar:GetStatusBarColor()
	local r, g, b = floor(aloftData.originalHealthBarR * 256), floor(aloftData.originalHealthBarG*256), floor(aloftData.originalHealthBarB*256)

	if r == 255 and g == 0 and b == 0 then
		aloftData.type = "hostile"
	elseif r == 0 and g == 0 and b == 255 then
		aloftData.type = "friendlyPlayer"
	elseif r == 0 and g == 255 and b == 0 then
		aloftData.type = "friendlyNPC"
	elseif r == 255 and g == 255 and b == 0 then
		aloftData.type = "neutral"
	else
--		Aloft:Debug("rgb = "..r.."."..g.."."..b)
	end
end

-----------------------------------------------------------------------------

function Aloft:LoadOptions()
	if Aloft.Options.args.loadOptions then
		for i = 1,GetNumAddOns() do
			if GetAddOnMetadata(i, "X-Aloft-Options") then
				LoadAddOn(i)
			end
		end
		Aloft.Options.args.loadOptions = nil
		self:TriggerEvent("Aloft:LoadOptions")
		collectgarbage('collect')
	end
end

-----------------------------------------------------------------------------

function Aloft:OnInitialize()
	for i = 1,GetNumAddOns() do
		if GetAddOnMetadata(i, "X-Aloft-Options") then
			Aloft.Options.args.loadOptions =
			{
				type = 'execute',
				name = L["Load Options"],
				desc = L["Load options for Aloft modules"],
				func = function() Aloft:LoadOptions() end,
				order = 5
			}
			break
		end
	end
end

function Aloft:OnEnable()
	for _, module in self:IterateModules() do
		self:ToggleModuleActive(module, true)
	end

	self:ScheduleRepeatingEvent("AloftUpdate", self.Update, 0.2, self)
	self:RegisterEvent("PLAYER_TARGET_CHANGED", function() noTargetNameplate = false end)

	self:DetermineDataSources()

	-- Reinstate hooks and update data - this is for when Aloft is disbaled, and later reenabled
	for aloftData in self:IterateNameplates() do
		self:HookNameplate(aloftData)
		if aloftData:IsShown() then
			visibleNameplateList[aloftData] = true
			self:OnNameplateShow(aloftData)
		end
	end
end

function Aloft:OnDisable()
	self:CancelScheduledEvent("AloftUpdate")

	for aloftData in self:IterateVisibleNameplates() do
		self:TriggerEvent("Aloft:OnNameplateHide", aloftData)
	end
	
	for _, module in self:IterateModules() do
		self:ToggleModuleActive(module, false)
		module:DisableDataSource()
	end
	
	self:RedisplayNameplates()
	self:RedisplayFriendlyNameplates()
end

function Aloft:OnProfileEnable()
	for aloftData in self:IterateVisibleNameplates() do
		self:TriggerEvent("Aloft:OnNameplateHide", aloftData)
	end

	for _, module in self:IterateModules() do
		if module.OnInitialize then
			module:OnInitialize()
		end
	end

	for _, module in self:IterateModules() do
		if module.OnEnable then
			module:OnEnable()
		end
	end

	self:DetermineDataSources()

	for aloftData in self:IterateVisibleNameplates() do
		UpdateAloftData(aloftData)
		self:TriggerEvent("Aloft:OnNameplateShow", aloftData)
	end
end

-----------------------------------------------------------------------------

local function IsNameplateFrame(frame)
	if frame:GetName() then
		return false
	end

	local overlayRegion = frame:GetRegions()
	if not overlayRegion or overlayRegion:GetObjectType() ~= "Texture" or overlayRegion:GetTexture() ~= "Interface\\Tooltips\\Nameplate-Border" then
		return false
	end
	
	return true
end

function Aloft:ProcessChildFrames(...)
	for i = 1, select('#', ...) do
		local frame = select(i, ...)
		if not frame.aloftData and IsNameplateFrame(frame) then 
			self:SetupNameplate(frame)
		end
	end
end

function Aloft:Update()
	local newNumberOfWorldChildren = WorldFrame:GetNumChildren()
	if newNumberOfWorldChildren ~= numberOfWorldChildren then
		numberOfWorldChildren = newNumberOfWorldChildren
		self:ProcessChildFrames(WorldFrame:GetChildren())
	end
	
	for aloftData in pairs(visibleNameplateList) do	
		local healthBar = aloftData.healthBar
		local r, g, b, a = healthBar:GetStatusBarColor()

		-- An awful awful update routine to stop the game overwriting our colors sporadically
		if r ~= aloftData.healthBarR or g ~= aloftData.healthBarG or b ~= aloftData.healthBarB or a ~= aloftData.healthBarA then
			UpdateAloftTypeData(aloftData, healthBar)
			self:TriggerEvent("Aloft:OnHealthBarColorChanged", aloftData)
		end		
	end
end

-----------------------------------------------------------------------------

function Aloft:IterateNameplates()
	return pairs(nameplateList)
end

function Aloft:IterateVisibleNameplates()
	return pairs(visibleNameplateList)
end

-----------------------------------------------------------------------------

local dataNameToModuleMap = { }
local moduleToModuleListMap = { }
local unavailableModuleList = { }

function Aloft:UpdateDataNameToModuleMap(module, ...)
	if not ... then return end
	for i = 1,select('#', ...) do
--		Aloft:Debug(string.format("Added data module: %s (%s)", tostring(select(i, ...)), tostring(module)))
		dataNameToModuleMap[select(i, ...)] = module
	end
	if not module:IsDataAvailable() then
--		Aloft:Debug(string.format("Flagging %s as unavailable\n", tostring(module)))
		unavailableModuleList[module] = true
	end
end

function Aloft:UpdateModuleToModuleListMap(module, ...)
	if not ... then return end
	
	local moduleList = { }
	moduleToModuleListMap[module] = moduleList
	
	for i = 1,select('#', ...) do
		local dataName = select(i, ...)
		local requiredModule = dataNameToModuleMap[dataName]
--		Aloft:Debug(string.format("%s requires %s", tostring(module), dataName))
		if requiredModule then
			moduleList[requiredModule] = true
		else
			self:Print(L["Unable to determine module providing data: "]..dataName)
		end
	end
end

function Aloft:RemoveRequiredModuleFromModuleToModuleListMap(requiredModule)
	for module,moduleList in pairs(moduleToModuleListMap) do
		moduleList[requiredModule] = nil
	end
end

function Aloft:DetermineDataSources()
	-- 1. Iterate all modules, flag them to be disabled and gather provided data
	for _,module in self:IterateModules() do
		module.needToDisable = true
		self:UpdateDataNameToModuleMap(module, module:ProvidesData())
	end

	-- Iterate all of the modules for data that they require, resolve them to the modules that provide them
	for name,module in self:IterateModules() do
		self:UpdateModuleToModuleListMap(module, module:RequiresData())
	end

	-- Clear our old data sources
	for i = 1, #dataSourceList do
		dataSourceList[i] = nil
	end

	-- Iterate to remove purely data modules
	local iterationCount = 0	-- A safety factor
	local requiredModules = { }
	while iterationCount < 100 and next(moduleToModuleListMap) do
		for module, moduleList in pairs(moduleToModuleListMap) do
			for requiredModule in pairs(moduleList) do
				requiredModules[requiredModule] = true
			end
		end
		local moduleRemoved = false
		for module, _ in pairs(moduleToModuleListMap) do
			if module:IsPurelyData() and not requiredModules[module] then
--				Aloft:Debug("Not required: "..tostring(module))
				moduleToModuleListMap[module] = nil
				self:RemoveRequiredModuleFromModuleToModuleListMap(module)
				moduleRemoved = true
			end
		end
		if not moduleRemoved then break end
		for k in pairs(requiredModules) do
			requiredModules[k] = nil
		end
		iterationCount = iterationCount + 1
	end

	-- Iterate our moduleToModuleListMap 
	iterationCount = 0	-- A safety factor
	while iterationCount < 100 and next(moduleToModuleListMap) do
		for module, moduleList in pairs(moduleToModuleListMap) do
			for requiredModule in pairs(moduleList) do
				if unavailableModuleList[requiredModule] then
--					Aloft:Debug(string.format("%s is unavailable. Flagging %s as unavailable", tostring(requiredModule), tostring(module)))
					unavailableModuleList[module] = true
					moduleList[requiredModule] = nil
				elseif not moduleToModuleListMap[requiredModule] then
					-- The required module isn't dependent on anything! Put it into our dataSourceList and remove from all modules that request it
					table.insert(dataSourceList, requiredModule)
					requiredModule:EnableDataSource()
					requiredModule.needToDisable = nil
--					Aloft:Debug(#dataSourceList..": "..tostring(requiredModule))

					self:RemoveRequiredModuleFromModuleToModuleListMap(requiredModule)
				end
			end
			if not next(moduleList) or unavailableModuleList[module] then
				moduleToModuleListMap[module] = nil
			end
		end
		iterationCount = iterationCount + 1
	end

	if next(moduleToModuleListMap) then
		self:Print(L["Internal error: Dependency list not resolved - cyclic dependency?"])
		for k in pairs(moduleToModuleListMap) do
			moduleToModuleListMap[k] = nil
		end
	end
	-- Empty our maps
	for k in pairs(dataNameToModuleMap) do
		dataNameToModuleMap[k] = nil
	end
	for k in pairs(unavailableModuleList) do
		unavailableModuleList[k] = nil
	end

	for _,module in self:IterateModules() do
		if module.needToDisable then
			module:DisableDataSource()
		end
	end
	
	for aloftData in self:IterateVisibleNameplates() do
		UpdateAloftData(aloftData)
	end
end

-----------------------------------------------------------------------------

function Aloft:GetTargetNameplate()
	if noTargetNameplate then return end

	-- Do quick lookup first
	local unitName = UnitName("target")
	if not unitName then return end
	
	local aloftData = nameToNameplateMap[unitName]
	if aloftData and aloftData.nameplateFrame:GetAlpha() == 1 then
		return aloftData
	end
	
	-- Do nameplate search
	for aloftData in pairs(visibleNameplateList) do
		if aloftData.nameplateFrame:GetAlpha() == 1 then
			-- And store it as our future reference
			nameToNameplateMap[unitName] = aloftData
			return aloftData
		end
	end
	noTargetNameplate = true
	return nil
end

function Aloft:GetNameplate(unitName)
	return nameToNameplateMap[unitName]
end

function Aloft:GetUnitNameplate(unitid)
	local unitName = UnitName(unitid)
	if unitName then
		return self:GetNameplate(unitName)
	end
end

-----------------------------------------------------------------------------

function Aloft:OnHealthBarValueChanged(aloftData, value, maxValue)
	if aloftData.nameplateFrame:IsShown() then
		aloftData.healthBarValue = value
		aloftData.healthBarMaxValue = maxValue
		self:TriggerEvent("Aloft:OnHealthBarValueChanged", aloftData)
	end
end

function Aloft:OnNameplateShow(aloftData)
	-- When a nameplate is first shown, grab some data about it
	local nameTextRegion = aloftData.nameTextRegion
	local newNameText = self.hooks[nameTextRegion].GetText(nameTextRegion)
	if aloftData.nameTextRegionText ~= newNameText then
		nameToNameplateMap[aloftData.name] = nil
		aloftData.name = newNameText
		nameToNameplateMap[aloftData.name] = aloftData
		aloftData.nameTextRegionText = name
	end

	local healthBar = aloftData.healthBar
	aloftData.healthBarValue = healthBar:GetValue()
	_, aloftData.healthBarMaxValue = healthBar:GetMinMaxValues()

	aloftData.isBoss = aloftData.bossIconRegion:IsShown()
	local levelTextRegion = aloftData.levelTextRegion
	if levelTextRegion:IsShown() then
		aloftData.level = tonumber(levelTextRegion:GetText()) or nil
		aloftData.levelTextR, aloftData.levelTextG, aloftData.levelTextB = levelTextRegion:GetTextColor()
	else
		aloftData.level = nil
		aloftData.levelTextR, aloftData.levelTextG, aloftData.levelTextB = 1, 1, 1
	end

	UpdateAloftTypeData(aloftData, healthBar)
	UpdateAloftData(aloftData)

	self:TriggerEvent("Aloft:OnNameplateShow", aloftData)
end

function Aloft:OnNameplateHide(aloftData)
	self:TriggerEvent("Aloft:OnNameplateHide", aloftData)

	nameToNameplateMap[aloftData.name] = nil
	aloftData.name = "[invalid]"
	aloftData.nameTextRegionText = nil
	aloftData.level = nil
	aloftData.type = "unknown"
end

function Aloft:OnCastBarValueChanged(aloftData, castBar)
	if castBar:IsShown() then
		self:TriggerEvent("Aloft:OnCastBarValueChanged", aloftData)
	end
end

function Aloft:OnCastBarShow(aloftData, castBar)
	self:TriggerEvent("Aloft:OnCastBarShow", aloftData)
end

function Aloft:OnCastBarHide(aloftData)
	self:TriggerEvent("Aloft:OnCastBarHide", aloftData)
end

-----------------------------------------------------------------------------

function Aloft:OnNameplateShowScript(this)
	local aloftData = this.aloftData
	noTargetNameplate = false
	visibleNameplateList[aloftData] = true
	self:OnNameplateShow(aloftData)
	self.hooks[this]["OnShow"](this)
end

function Aloft:OnNameplateHideScript(this)
	local aloftData = this.aloftData
	self:OnNameplateHide(aloftData)
	visibleNameplateList[aloftData] = nil
	self.hooks[this]["OnHide"](this)
end

function Aloft:OnHealthBarValueChangedScript(this, value)
	local _, maxValue = this:GetMinMaxValues()
	self:OnHealthBarValueChanged(this.aloftData, value, maxValue)
	self.hooks[this]["OnValueChanged"](this, value)
end

function Aloft:OnCastBarValueChangedScript(this, value)
	self:OnCastBarValueChanged(this.aloftData, this)
	self.hooks[this]["OnValueChanged"](this, value)
end

function Aloft:OnCastBarShowScript(this)
	self:OnCastBarShow(this.aloftData, this)
	self.hooks[this]["OnShow"](this)
end

function Aloft:OnCastBarHideScript(this)
	self:OnCastBarHide(this.aloftData)	
	self.hooks[this]["OnHide"](this)
end

-----------------------------------------------------------------------------

function Aloft:SetHealthBarColor(this, r, g, b, a)
	local aloftData = this.aloftData
	self.hooks[this].SetStatusBarColor(this, r, g, b, a)
	-- We read it back, because rounding seems to kick in and prevent them from being exactly what we sent..
	aloftData.healthBarR, aloftData.healthBarG, aloftData.healthBarB, aloftData.healthBarA = this:GetStatusBarColor()
end

function Aloft:SetNameTextRegionText(this, text)
	local aloftData = this.aloftData
	aloftData.nameTextRegionText = text
	self.hooks[this].SetText(this, text)
end

function Aloft:GetLevelTextFrameText(this)
	local level = this.aloftData.level
	if level then return level end
	return self.hooks[this].GetText(this)
end

function Aloft:GetNameTextRegionText(this)
	local name = this.aloftData.name
	if name ~= "[invalid]" then return name end
	return self.hooks[this].GetText(this)
end

-----------------------------------------------------------------------------

local function AloftDataIsTarget(aloftData) return aloftData.nameplateFrame:GetAlpha() == 1 and aloftData.name == UnitName("target") end
local function AloftDataIsShown(aloftData) return aloftData.nameplateFrame:IsShown() end

local function AloftDataCreateFontString(aloftData)	return aloftData.nameplateFrame:CreateFontString(nil, "OVERLAY") end
local function AloftDataCreateTexture(aloftData) return aloftData.nameplateFrame:CreateTexture(nil, "OVERLAY") end

function Aloft:SetupNameplate(frame)
	local aloftData = { type = "unknown", name = "[invalid]", IsTarget = AloftDataIsTarget, IsShown = AloftDataIsShown, CreateFontString = AloftDataCreateFontString, CreateTexture = AloftDataCreateTexture }
	frame.aloftData = aloftData
	noTargetNameplate = false
	nameplateList[aloftData] = true
	visibleNameplateList[aloftData] = true

	local overlayRegion, castBarOverlayRegion, spellIconRegion, highlightRegion, nameTextRegion, levelTextRegion, bossIconRegion, raidIconRegion = frame:GetRegions()
	local healthBar, castBar = frame:GetChildren()

	-- Reference all of these now
	aloftData.healthBar = healthBar
	aloftData.castBar = castBar
	
	aloftData.overlayRegion = overlayRegion
	aloftData.castBarOverlayRegion = castBarOverlayRegion
	aloftData.spellIconRegion = spellIconRegion
	aloftData.highlightRegion = highlightRegion
	aloftData.nameTextRegion = nameTextRegion
	aloftData.levelTextRegion = levelTextRegion
	aloftData.bossIconRegion = bossIconRegion
	aloftData.raidIconRegion = raidIconRegion
	
	aloftData.nameplateFrame = frame

	healthBar.aloftData = aloftData
	castBar.aloftData = aloftData
	nameTextRegion.aloftData = aloftData
	levelTextRegion.aloftData = aloftData

	self:HookNameplate(aloftData)

	self:TriggerEvent("Aloft:SetupFrame", aloftData)

	self:OnNameplateShow(aloftData)
	if castBar:IsShown() then
		self:OnCastBarShow(aloftData, castBar)
	end
end

function Aloft:HookNameplate(aloftData)
	local nameplateFrame = aloftData.nameplateFrame
	self:HookScript(nameplateFrame, "OnShow", "OnNameplateShowScript")
	self:HookScript(nameplateFrame, "OnHide", "OnNameplateHideScript")

	local healthBar = aloftData.healthBar
	self:HookScript(healthBar, "OnValueChanged", "OnHealthBarValueChangedScript")
	self:Hook(healthBar, "SetStatusBarColor", "SetHealthBarColor", true)

	local castBar = aloftData.castBar
	self:HookScript(castBar, "OnShow", "OnCastBarShowScript")
	self:HookScript(castBar, "OnHide", "OnCastBarHideScript")
	self:HookScript(castBar, "OnValueChanged", "OnCastBarValueChangedScript")

	local nameTextRegion = aloftData.nameTextRegion
	self:Hook(nameTextRegion, "SetText", "SetNameTextRegionText", true)
	self:Hook(nameTextRegion, "GetText", "GetNameTextRegionText", true)

	local levelTextRegion = aloftData.levelTextRegion
	self:Hook(levelTextRegion, "GetText", "GetLevelTextFrameText", true)
end

-----------------------------------------------------------------------------

function Aloft.modulePrototype:PrepareText(textFrame, data)
	textFrame:SetFont(SML:Fetch("font", data.font), data.fontSize, data.outline)
	if data.shadow then
		textFrame:SetShadowOffset(1, -1)
	else
		textFrame:SetShadowOffset(0, 0)
	end
	if data.alignment then
		textFrame:SetJustifyH(data.alignment)
	end
	if data.color then
		textFrame:SetTextColor(unpack(data.color))
	end
end

function Aloft.modulePrototype:PlaceFrame(frame, relativeFrame, data)
	if data.enable then
		frame:ClearAllPoints()
		frame:SetPoint(data.point, relativeFrame, data.relativeToPoint, data.offsetX, data.offsetY)
		
		if data.size then
			frame:SetWidth(data.size)
			frame:SetHeight(data.size)
		else
			if data.width then
				frame:SetWidth(data.width)
			end
			if data.height then
				frame:SetWidth(data.width)
			end
		end
		frame:SetAlpha(data.alpha or 1.0)
	else
		frame:SetAlpha(0)
	end
end

-----------------------------------------------------------------------------

-- Provide these functions mainly for Visibiltiy module. ReportStatusChanges can hook them so that the log doesn't fill up
function Aloft:RedisplayNameplates()
	HideNameplates()
	self:ScheduleEvent("AloftRedisplayNameplates", ShowNameplates, 0.1)
end

function Aloft:RedisplayFriendlyNameplates()
	HideFriendNameplates()
	self:ScheduleEvent("AloftRedisplayFriendNameplates", ShowFriendNameplates, 0.1)
end

-----------------------------------------------------------------------------

function Aloft.modulePrototype:RequiresData()
	-- eg. return "class", "isPetName", "isGuildMember"
end

function Aloft.modulePrototype:ProvidesData()
end

function Aloft.modulePrototype:EnableDataSource()
end

function Aloft:IsDataAvailable(name)
	local dataAvailableMethod = Aloft.DataAvailableMethods[name]
	if dataAvailableMethod then
--		Aloft:Debug(string.format("%s: %s", name, dataAvailableMethod() and "true" or "false"))
		return dataAvailableMethod()
	end
	return true
end

function Aloft.modulePrototype:IsPurelyData()
	return self:ProvidesData() ~= nil
end

function Aloft.modulePrototype:IsOneOfDataListAvailable(...)
	for i=1,select('#', ...) do
		if Aloft:IsDataAvailable(select(i, ...)) then
			return true
		end
	end
	return false
end

function Aloft.modulePrototype:IsDataAvailable()
	return self:IsOneOfDataListAvailable(self:ProvidesData())
end

function Aloft.modulePrototype:RemoveAloftData(name)
	for aloftData in Aloft:IterateNameplates() do
		aloftData[name] = nil
	end
end

function Aloft.modulePrototype:RemoveProvidedData(...)
	for i=1,select('#', ...) do
		self:RemoveAloftData(select(i, ...))
	end
end

function Aloft.modulePrototype:DisableDataSource()
	local dataName = self:ProvidesData()
	if dataName then
		self:UnregisterAllEvents()
		self:CancelAllScheduledEvents()
 		if self.UnhookAll then
			self:UnhookAll()
		end
		self:RemoveProvidedData(self:ProvidesData())
	end
end

function Aloft.modulePrototype:UpdateData(aloftData)
	DEFAULT_CHAT_FRAME:AddMessage("Internal error: DataSource's UpdateData not overriden.")
end

function Aloft.modulePrototype:CancelEvent(eventName)
	if self:IsEventRegistered(eventName) then
		self:UnregisterEvent(eventName)
	end
end

function Aloft.modulePrototype:IsArrayEqual(a, b)
	if #a ~= #b then return false end
	for i=1,#a do
		if a[i] ~= b[i] then return false end
	end
	return true
end

-----------------------------------------------------------------------------