diff modules/FuBar_ReActionFu/lib/LibFuBarPlugin-3.0/LibFuBarPlugin-3.0.lua @ 30:0d95ce7a9ec2

- added Ace3 externs - converted ReAction_ConfigUI to use blizzard interface addons panel via AceConfigDialog-3.0 - partially converted FuBar module to LibRock, deprecated it (going to remove it entirely later) - cleaned up a couple other tidbits
author Flick <flickerstreak@gmail.com>
date Wed, 02 Apr 2008 23:31:13 +0000
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/FuBar_ReActionFu/lib/LibFuBarPlugin-3.0/LibFuBarPlugin-3.0.lua	Wed Apr 02 23:31:13 2008 +0000
@@ -0,0 +1,2145 @@
+--[[
+Name: LibFuBarPlugin-3.0
+Revision: $Rev: 63707 $
+Developed by: ckknight (ckknight@gmail.com)
+Website: http://www.wowace.com/
+Description: Plugin for FuBar.
+Dependencies: LibRock-1.0
+License: LGPL v2.1
+]]
+
+local MAJOR_VERSION = "LibFuBarPlugin-3.0"
+local MINOR_VERSION = tonumber(("$Revision: 63707 $"):match("(%d+)")) - 60000
+
+if not Rock then error(MAJOR_VERSION .. " requires LibRock-1.0") end
+
+local FuBarPlugin, oldLib = Rock:NewLibrary(MAJOR_VERSION, MINOR_VERSION)
+if not FuBarPlugin then
+	return
+end
+
+local SHOW_FUBAR_ICON = "Show FuBar icon"
+local SHOW_FUBAR_ICON_DESC = "Show the FuBar plugin's icon on the panel."
+local SHOW_FUBAR_TEXT = "Show FuBar text"
+local SHOW_FUBAR_TEXT_DESC = "Show the FuBar plugin's text on the panel."
+local SHOW_COLORED_FUBAR_TEXT = "Show colored FuBar text"
+local SHOW_COLORED_FUBAR_TEXT_DESC = "Allow the FuBar plugin to color its text on the panel."
+local DETACH_FUBAR_TOOLTIP = "Detach FuBar tooltip"
+local DETACH_FUBAR_TOOLTIP_DESC = "Detach the FuBar tooltip from the panel."
+local LOCK_FUBAR_TOOLTIP = "Lock tooltip"
+local LOCK_FUBAR_TOOLTIP_DESC = "Lock the tooltips position. When the tooltip is locked, you must use Alt to access it with your mouse."
+local POSITION_ON_FUBAR = "Position on FuBar"
+local POSITION_ON_FUBAR_DESC = "Position the FuBar plugin on the panel."
+local POSITION_LEFT = "Left"
+local POSITION_RIGHT = "Right"
+local POSITION_CENTER = "Center"
+local ATTACH_PLUGIN_TO_MINIMAP = "Attach FuBar plugin to minimap"
+local ATTACH_PLUGIN_TO_MINIMAP_DESC = "Attach the FuBar plugin to the minimap instead of the panel."
+local HIDE_FUBAR_PLUGIN = "Hide FuBar plugin"
+local HIDE_MINIMAP_BUTTON = "Hide minimap button"
+local HIDE_FUBAR_PLUGIN_DESC = "Hide the FuBar plugin from the panel or minimap, leaving the addon running."
+local OTHER = "Other"
+local CLOSE = "Close"
+local CLOSE_DESC = "Close the menu."
+
+if GetLocale() == "zhCN" then
+	SHOW_FUBAR_ICON = "显示FuBar图标"
+	SHOW_FUBAR_ICON_DESC = "在面板上显示FuBar插件的图标."
+	SHOW_FUBAR_TEXT = "显示FuBar文字"
+	SHOW_FUBAR_TEXT_DESC = "在面板上显示Fubar插件文字标题"
+	SHOW_COLORED_FUBAR_TEXT = "显示彩色文字"
+	SHOW_COLORED_FUBAR_TEXT_DESC = "允许插件显示彩色文字."
+	DETACH_FUBAR_TOOLTIP = "独立提示信息"
+	DETACH_FUBAR_TOOLTIP_DESC = "从面板上独立显示信息"
+	LOCK_FUBAR_TOOLTIP = "锁定提示信息"
+	LOCK_FUBAR_TOOLTIP_DESC = "锁定提示信息位置.当提示信息被锁定时,你必须要按Alt-鼠标方可查看."
+	POSITION_ON_FUBAR = "位置"
+	POSITION_ON_FUBAR_DESC = "FuBar插件在面板上的位置."
+	POSITION_LEFT = "居左"
+	POSITION_RIGHT = "居右"
+	POSITION_CENTER = "居中"
+	ATTACH_PLUGIN_TO_MINIMAP = "依附在小地图"
+	ATTACH_PLUGIN_TO_MINIMAP_DESC = "插件图标依附在小地图而不显示在面板上."
+	HIDE_FUBAR_PLUGIN = "隐藏FuBar插件"
+	HIDE_MINIMAP_BUTTON = "隐藏小地图按钮"
+	HIDE_FUBAR_PLUGIN_DESC = "隐藏在面板或小地图上的FuBar插件,暂定插件工作."
+	OTHER = "其他"
+	CLOSE = "关闭"
+	LOSE_DESC = "关闭目录."
+elseif GetLocale() == "zhTW" then
+	SHOW_FUBAR_ICON = "顯示圖示"
+	SHOW_FUBAR_ICON_DESC = "在面板上顯示插件圖示。"
+	SHOW_FUBAR_TEXT = "顯示文字"
+	SHOW_FUBAR_TEXT_DESC = "在面板上顯示插件文字。"
+	SHOW_COLORED_FUBAR_TEXT = "允許彩色文字"
+	SHOW_COLORED_FUBAR_TEXT_DESC = "允許插件在面板上使用彩色文字。"
+	DETACH_FUBAR_TOOLTIP = "獨立提示訊息"
+	DETACH_FUBAR_TOOLTIP_DESC = "從面板上獨立提示訊息。"
+	LOCK_FUBAR_TOOLTIP = "鎖定提示訊息"
+	LOCK_FUBAR_TOOLTIP_DESC = "鎖定提示訊息位置。當提示訊息鎖定時,需要用Alt鍵使用提示訊息的功能。"
+	POSITION_ON_FUBAR = "位置"
+	POSITION_ON_FUBAR_DESC = "插件在面板上的位置。"
+	POSITION_LEFT = "靠左"
+	POSITION_RIGHT = "靠右"
+	POSITION_CENTER = "置中"
+	ATTACH_PLUGIN_TO_MINIMAP = "依附在小地圖"
+	ATTACH_PLUGIN_TO_MINIMAP_DESC = "插件圖標依附在小地圖而不顯示在面板上。"
+	HIDE_FUBAR_PLUGIN = "隱藏插件"
+	HIDE_MINIMAP_BUTTON = "隱藏小地圖按鈕"
+	HIDE_FUBAR_PLUGIN_DESC = "在面板或小地圖上隱藏該插件,但保持執行狀態。"
+	OTHER = "其他"
+	CLOSE = "關閉"
+	CLOSE_DESC = "關閉選單。"
+elseif GetLocale() == "koKR" then
+	SHOW_FUBAR_ICON = "FuBar 아이콘 표시"
+	SHOW_FUBAR_ICON_DESC = "FuBar 패널에 플러그인 아이콘을 표시합니다."
+	SHOW_FUBAR_TEXT = "FuBar 텍스트 표시"
+	SHOW_FUBAR_TEXT_DESC = "FuBar 페널에 플러그인 텍스트를 표시합니다."
+	SHOW_COLORED_FUBAR_TEXT = "색상화된 FuBar 텍스트 표시"
+	SHOW_COLORED_FUBAR_TEXT_DESC = "패널의 FuBar 플러그인의 텍스트 색상을 허용합니다."
+	DETACH_FUBAR_TOOLTIP = "FuBar 툴팁 분리"
+	DETACH_FUBAR_TOOLTIP_DESC = "패널에서 FuBar 툴팁을 분리합니다."
+	LOCK_FUBAR_TOOLTIP = "툴팁 고정"
+	LOCK_FUBAR_TOOLTIP_DESC = "툴팁 위치를 고정시킵니다. 툴팁이 고정되어 있을때, 마우스로 접근하기 위해 Alt키를 사용하여야 합니다."
+	POSITION_ON_FUBAR = "FuBar 위치"
+	POSITION_ON_FUBAR_DESC = "패널 위의 FuBar 플러그인의 위치를 설정합니다."
+	POSITION_LEFT = "좌측"
+	POSITION_RIGHT = "우측"
+	POSITION_CENTER = "중앙"
+	ATTACH_PLUGIN_TO_MINIMAP = "FuBar 플러그인 미니맵 표시"
+	ATTACH_PLUGIN_TO_MINIMAP_DESC = "FuBar 플러그인을 패널 대신 미니맵에 표시합니다."
+	HIDE_FUBAR_PLUGIN = "FuBar 플러그인 숨김"
+	HIDE_MINIMAP_BUTTON = "미니맵 버튼 숨김"
+	HIDE_FUBAR_PLUGIN_DESC = "FuBar 플러그인을 패널이나 미니맵으로 부터 숨김니다."
+	OTHER = "기타"
+	CLOSE = "닫기"
+	CLOSE_DESC = "메뉴를 닫습니다."
+elseif GetLocale() == "frFR" then
+	SHOW_FUBAR_ICON = "Afficher l'icône FuBar"
+	SHOW_FUBAR_ICON_DESC = "Affiche l'icône du plugin FuBar sur le panneau."
+	SHOW_FUBAR_TEXT = "Afficher le texte FuBar"
+	SHOW_FUBAR_TEXT_DESC = "Affiche le texte du plugin FuBar sur le panneau."
+	SHOW_COLORED_FUBAR_TEXT = "Afficher le texte FuBar coloré"
+	SHOW_COLORED_FUBAR_TEXT_DESC = "Autorise le plugin FuBar à colorer son texte sur le panneau."
+	DETACH_FUBAR_TOOLTIP = "Détacher l'infobulle FuBar"
+	DETACH_FUBAR_TOOLTIP_DESC = "Détache l'infobulle FuBar du panneau."
+	LOCK_FUBAR_TOOLTIP = "Verrouiller l'infobulle"
+	LOCK_FUBAR_TOOLTIP_DESC = "Verrouille l'infobulle dans sa position actuelle. Quand l'infobulle est verrouillée, vous devez utiliser la touche Alt pour y interagir avec la souris."
+	POSITION_ON_FUBAR = "Position sur FuBar"
+	POSITION_ON_FUBAR_DESC = "Position du plugin FuBar sur le panneau."
+	POSITION_LEFT = "Gauche"
+	POSITION_RIGHT = "Droite"
+	POSITION_CENTER = "Centre"
+	ATTACH_PLUGIN_TO_MINIMAP = "Attacher le plugin FuBar sur la minicarte"
+	ATTACH_PLUGIN_TO_MINIMAP_DESC = "Attache le plugin FuBar sur la minicarte au lieu du panneau."
+	HIDE_FUBAR_PLUGIN = "Masquer le plugin FuBar"
+	HIDE_MINIMAP_BUTTON = "Masquer le bouton de la minicarte"
+	HIDE_FUBAR_PLUGIN_DESC = "Masque le plugin FuBar du panneau ou de la minicarte, laissant l'addon fonctionner."
+	OTHER = "Autre"
+	CLOSE = "Fermer"
+	CLOSE_DESC = "Ferme le menu."
+end
+
+-- #AUTODOC_NAMESPACE FuBarPlugin
+
+local precondition, argCheck = Rock:GetContractFunctions(MAJOR_VERSION, "precondition", "argCheck")
+local newList, del = Rock:GetRecyclingFunctions(MAJOR_VERSION, "newList", "del")
+
+FuBarPlugin.pluginToFrame = oldLib and oldLib.pluginToFrame or {}
+local pluginToFrame = FuBarPlugin.pluginToFrame
+FuBarPlugin.pluginToMinimapFrame = oldLib and oldLib.pluginToMinimapFrame or {}
+local pluginToMinimapFrame = FuBarPlugin.pluginToMinimapFrame
+FuBarPlugin.pluginToPanel = oldLib and oldLib.pluginToPanel or {}
+local pluginToPanel = FuBarPlugin.pluginToPanel
+FuBarPlugin.pluginToOptions = oldLib and oldLib.pluginToOptions or {}
+local pluginToOptions = FuBarPlugin.pluginToOptions
+FuBarPlugin.folderNames = oldLib and oldLib.folderNames or {}
+local folderNames = FuBarPlugin.folderNames
+
+local Tablet20
+local Dewdrop20
+
+FuBarPlugin.MinimapContainer = oldLib and oldLib.MinimapContainer or {}
+local MinimapContainer = FuBarPlugin.MinimapContainer
+
+local epsilon = 1e-5
+
+-- #AUTODOC_NAMESPACE FuBarPlugin
+
+--[[---------------------------------------------------------------------------
+Notes:
+	*Set metadata about a certain plugin.
+	; tooltipType : string -
+	: "GameTooltip"
+	:: Use Blizzard's GameTooltip. (default if not given)
+	: "Tablet-2.0"
+	:: Use Tablet-2.0.
+	: "Custom"
+	:: LibFuBarPlugin-3.0 will not provide any extra mechanisms, all done manually.
+	; configType : string -
+	: "LibRockConfig-1.0"
+	:: Use LibRockConfig-1.0 to show configuration. (default if not given)
+	: "Dewdrop-2.0"
+	:: Use Dewdrop-2.0.
+	; hasNoText : boolean - If set to true, then it will be a text-less frame.
+	; iconPath : string - the path of the icon to show.
+	; hasNoColor : boolean - If set to true, then it is assumed that no color will be in the text (and thus not show the menu item)
+	; cannotHideText : boolean - If set to true, then the menu item to hide text will not be shown.
+	; overrideMenu : boolean - If set to true, then the menu will not show any of the standard menu items
+	; hideMenuTitle : boolean - If set to true, the plugins name will not be added to the top of the menu as a header.
+	; defaultPosition : string -
+	: "LEFT"
+	::show on the left. (default if not given)
+	: "CENTER"
+	::show in the center.
+	: "RIGHT"
+	::show on the right.
+	: "MINIMAP"
+	::show on the minimap.
+	; defaultMinimapPosition : number - Angle on the minimap, in degrees. [0, 360)
+	; clickableTooltip : boolean - Whether you can drag your mouse onto the tooltip and click a line
+	; tooltipHiddenWhenEmpty : boolean - Whether the detached tooltip is hidden when it is empty.
+	; cannotDetachTooltip : boolean - Whether the tooltip cannot be detached from the plugin text.
+	::Normally, a tooltip can detach (if using Tablet-2.0). This should be set if there is no relevant data in the tooltip.
+	; independentProfile : boolean - If set to true, then the profile setting will not be stripped from .OnMenuRequest, and FuBar will not set the plugin's profile when it changes.
+	::non-FuBar-centric plugins should set this to true.
+Arguments:
+	string - the key to set
+	value - the value to set said key to.
+Example:
+	self:SetFuBarOption('tooltipType', "Tablet-2.0")
+-----------------------------------------------------------------------------]]
+function FuBarPlugin:SetFuBarOption(key, value)
+	local pluginToOptions_self = pluginToOptions[self]
+	if not pluginToOptions_self then
+		pluginToOptions_self = {}
+		pluginToOptions[self] = pluginToOptions_self
+	end
+
+	pluginToOptions_self[key] = value
+
+	if key == 'tooltipType' then
+		if value == "Tablet-2.0" then
+			Tablet20 = Rock("Tablet-2.0", false, true)
+			if not Tablet20 then
+				error(("Cannot specify %q = %q if %q is not loaded."):format(key, value, value), 2)
+			end
+		end
+	end
+	if key == 'configType' then
+		if value == "Dewdrop-2.0" then
+			Dewdrop20 = Rock("Dewdrop-2.0", false, true)
+			if not Dewdrop20 then
+				error(("Cannot specify %q = %q if %q is not loaded."):format(key, value, value), 2)
+			end
+		end
+	end
+end
+precondition(FuBarPlugin, 'SetFuBarOption', function(self, key, value)
+	argCheck(self, 1, "table")
+	argCheck(key, 2, "string")
+	argCheck(value, 3, "string", "number", "boolean")
+
+	if pluginToOptions[self] and pluginToOptions[self][key] ~= nil then
+		error(("Bad argument #2 to `SetFuBarOption'. Cannot specify %q more than once."):format(key), 3)
+	end
+end)
+
+local function getPluginOption(object, key, default)
+	local pluginToOptions_object = pluginToOptions[object]
+	if pluginToOptions_object == nil then
+		return default
+	end
+	local value = pluginToOptions_object[key]
+	if value == nil then
+		return default
+	end
+	return value
+end
+
+local good = nil
+local function CheckFuBar()
+	if not good then
+		if FuBar then
+			local version = FuBar.version
+			if type(version) == "string" then
+			 	local num = version:match("^(%d+%.?%d*)")
+				if num then
+					num = tonumber(num)
+					good = num >= 3
+				end
+			end
+		end
+	end
+	return good
+end
+
+--[[---------------------------------------------------------------------------
+Returns:
+	string - the localized name of the plugin, not including the "FuBar - " part.
+Example
+	local title = self:GetTitle()
+-----------------------------------------------------------------------------]]
+function FuBarPlugin:GetTitle()
+	local name = self.title or self.name
+	if type(name) ~= "string" then
+		error("You must provide self.title or self.name", 2)
+	end
+	local title = name:match("[Ff][Uu][Bb][Aa][Rr]%s*%-%s*(.-)%s*$") or name
+	return title:gsub("|c%x%x%x%x%x%x%x%x", ""):gsub("|r", "")
+end
+
+--[[---------------------------------------------------------------------------
+Returns:
+	string - name of the plugin.
+Notes:
+	This is here for FuBar core to communicate properly.
+Example:
+	local name = self:GetName()
+-----------------------------------------------------------------------------]]
+function FuBarPlugin:GetName()
+	return self.name
+end
+
+--[[---------------------------------------------------------------------------
+Returns:
+	string - category of the plugin.
+Notes:
+	This is here for FuBar core to communicate properly.
+Example:
+	local category = self:GetCategory()
+-----------------------------------------------------------------------------]]
+function FuBarPlugin:GetCategory()
+	return self.category or OTHER
+end
+
+--[[---------------------------------------------------------------------------
+Returns:
+	frame - frame for the plugin.
+Notes:
+	This is here for FuBar core to communicate properly.
+Example:
+	local frame = self:GetFrame()
+-----------------------------------------------------------------------------]]
+function FuBarPlugin:GetFrame()
+	return pluginToFrame[self]
+end
+
+--[[---------------------------------------------------------------------------
+Returns:
+	object - panel for the plugin.
+Notes:
+	This is here for FuBar core to communicate properly.
+Example:
+	local panel = self:GetPanel()
+-----------------------------------------------------------------------------]]
+function FuBarPlugin:GetPanel()
+	return pluginToPanel[self]
+end
+
+local function getLazyDatabaseValueDefault(object, value, ...)
+	local object_db = object.db
+	if type(object_db) ~= "table" then
+		return value
+	end
+	local current = object_db.profile
+	for i = 1, select('#', ...) do
+		-- traverse through, make sure tables exist.
+		if type(current) ~= "table" then
+			return value
+		end
+		current = current[(select(i, ...))]
+	end
+	if current == nil then
+		return value
+	else
+		return current
+	end
+end
+
+local function getLazyDatabaseValue(object, ...)
+	return getLazyDatabaseValueDefault(object, nil, ...)
+end
+
+local function setLazyDatabaseValue(object, value, ...)
+	local object_db = object.db
+	if type(object_db) ~= "table" then
+		return nil
+	end
+	local current = object_db.profile
+	if type(current) ~= "table" then
+		return nil
+	end
+	local n = select('#', ...)
+	for i = 1, n-1 do
+		-- traverse through, create tables if necessary.
+		local nextOne = current[(select(i, ...))]
+		if type(nextOne) ~= "table" then
+			if nextOne ~= nil then
+				return nil
+			end
+			nextOne = {}
+			current[(select(i, ...))] = nextOne
+		end
+		current = nextOne
+	end
+	current[select(n, ...)] = value
+	return true
+end
+
+--[[---------------------------------------------------------------------------
+Returns:
+	boolean - whether the text has color applied.
+Example:
+	local colored = self:IsFuBarTextColored()
+-----------------------------------------------------------------------------]]
+function FuBarPlugin:IsFuBarTextColored()
+	return not getLazyDatabaseValue(self, 'uncolored')
+end
+
+--[[---------------------------------------------------------------------------
+Notes:
+	Toggles whether the text has color applied
+Example:
+	self:ToggleTextColored()
+-----------------------------------------------------------------------------]]
+function FuBarPlugin:ToggleFuBarTextColored()
+	if not setLazyDatabaseValue(self, not getLazyDatabaseValue(self, 'uncolored') or nil, 'uncolored') then
+		error(("%s: Cannot change text color if self.db is not available."):format(self:GetTitle()), 2)
+	end
+	self:UpdateFuBarText()
+end
+
+--[[---------------------------------------------------------------------------
+Returns:
+	boolean - whether the plugin is attached to the minimap.
+Example:
+	local attached = self:IsMinimapAttached()
+-----------------------------------------------------------------------------]]
+function FuBarPlugin:IsFuBarMinimapAttached()
+	if not CheckFuBar() then
+		return true
+	end
+	return pluginToPanel[self] == MinimapContainer
+end
+
+--[[---------------------------------------------------------------------------
+Notes:
+	Toggles whether the plugin is attached to the minimap.
+Example:
+	self:ToggleMinimapAttached()
+-----------------------------------------------------------------------------]]
+function FuBarPlugin:ToggleFuBarMinimapAttached()
+	if CheckFuBar() and not getPluginOption(self, 'cannotAttachToMinimap', false) then
+		local panel = pluginToPanel[self]
+		local value = panel == MinimapContainer
+		if value then
+			panel:RemovePlugin(self)
+			local defaultPosition = getPluginOption(self, 'defaultPosition', "LEFT")
+			FuBar:GetPanel(1):AddPlugin(self, nil, defaultPosition == "MINIMAP" and "LEFT" or defaultPosition)
+		else
+			if panel then
+				panel:RemovePlugin(self)
+			end
+			MinimapContainer:AddPlugin(self)
+		end
+	end
+end
+
+--[[---------------------------------------------------------------------------
+Notes:
+	Calls :UpdateFuBarText() and :UpdateFuBarTooltip(), in that order.
+Example:
+	self:UpdateFuBarPlugin()
+-----------------------------------------------------------------------------]]
+function FuBarPlugin:UpdateFuBarPlugin()
+	self:UpdateFuBarText()
+	self:UpdateFuBarTooltip()
+end
+
+--[[---------------------------------------------------------------------------
+Notes:
+	* Calls :OnUpdateFuBarText() if it is available and the plugin is not disabled.
+	* It is expected to update the icon in :OnUpdateFuBarText as well as text.
+Example:
+	self:UpdateFuBarText()
+-----------------------------------------------------------------------------]]
+function FuBarPlugin:UpdateFuBarText()
+	if type(self.OnUpdateFuBarText) == "function" then
+		if not self:IsDisabled() then
+			self:OnUpdateFuBarText()
+		end
+	elseif self:IsFuBarTextShown() then
+		self:SetFuBarText(self:GetTitle())
+	end
+end
+
+local function Tablet20_point(frame)
+	if frame:GetTop() > GetScreenHeight() / 2 then
+		local x = frame:GetCenter()
+		if x < GetScreenWidth() / 3 then
+			return "TOPLEFT", "BOTTOMLEFT"
+		elseif x < GetScreenWidth() * 2 / 3 then
+			return "TOP", "BOTTOM"
+		else
+			return "TOPRIGHT", "BOTTOMRIGHT"
+		end
+	else
+		local x = frame:GetCenter()
+		if x < GetScreenWidth() / 3 then
+			return "BOTTOMLEFT", "TOPLEFT"
+		elseif x < GetScreenWidth() * 2 / 3 then
+			return "BOTTOM", "TOP"
+		else
+			return "BOTTOMRIGHT", "TOPRIGHT"
+		end
+	end
+end
+
+local function RegisterTablet20(self)
+	local frame = pluginToFrame[self]
+	if not Tablet20:IsRegistered(frame) then
+		local db = getLazyDatabaseValue(self)
+		if db and not db.detachedTooltip then
+			db.detachedTooltip = {}
+		end
+		Tablet20:Register(frame,
+			'children', function()
+				Tablet20:SetTitle(self:GetTitle())
+				if type(self.OnUpdateFuBarTooltip) == "function" then
+					if not self:IsDisabled() then
+						self:OnUpdateFuBarTooltip()
+					end
+				end
+			end,
+			'clickable', getPluginOption(self, 'clickableTooltip', false),
+			'data', CheckFuBar() and FuBar.db.profile.tooltip or db and db.detachedTooltip or {},
+			'detachedData', db and db.detachedTooltip or {},
+			'point', Tablet20_point,
+			'menu', self.OnMenuRequest and function(level, value, valueN_1, valueN_2, valueN_3, valueN_4)
+				if level == 1 then
+					local name = tostring(self)
+					if not name:find('^table:') then
+						name = name:gsub("|c%x%x%x%x%x%x%x%x(.-)|r", "%1")
+						Rock("Dewdrop-2.0"):AddLine(
+							'text', name,
+							'isTitle', true
+						)
+					end
+				end
+				if type(self.OnMenuRequest) == "function" then
+					self:OnMenuRequest(level, value, true, valueN_1, valueN_2, valueN_3, valueN_4)
+				elseif type(self.OnMenuRequest) == "table" then
+					Rock("Dewdrop-2.0"):FeedAceOptionsTable(self.OnMenuRequest)
+				end
+			end,
+			'hideWhenEmpty', getPluginOption(self, 'tooltipHiddenWhenEmpty', false)
+		)
+		local func = pluginToFrame[self]:GetScript("OnEnter")
+		frame:SetScript("OnEnter", function(this, ...)
+			-- HACK
+			func(this, ...)
+
+			if FuBar and FuBar.IsHidingTooltipsInCombat and FuBar:IsHidingTooltipsInCombat() and InCombatLockdown() then
+				if Tablet20:IsAttached(this) then
+					Tablet20:Close(this)
+				end
+			end
+		end)
+	end
+end
+
+--[[---------------------------------------------------------------------------
+Notes:
+	Calls :OnUpdateFuBarTooltip() if it is available, the plugin is not disabled, and the tooltip is shown.
+Example:
+	self:UpdateFuBarTooltip()
+-----------------------------------------------------------------------------]]
+function FuBarPlugin:UpdateFuBarTooltip()
+	local tooltipType = getPluginOption(self, 'tooltipType', "GameTooltip")
+
+	if tooltipType == "GameTooltip" then
+		local frame = self:IsFuBarMinimapAttached() and pluginToMinimapFrame[self] or pluginToFrame[self]
+		if not GameTooltip:IsOwned(frame) then
+			return
+		end
+		GameTooltip:Hide()
+
+		local anchor
+		if frame:GetTop() > GetScreenHeight() / 2 then
+			local x = frame:GetCenter()
+			if x < GetScreenWidth() / 2 then
+				anchor = "ANCHOR_BOTTOMRIGHT"
+			else
+				anchor = "ANCHOR_BOTTOMLEFT"
+			end
+		else
+			local x = frame:GetCenter()
+			if x < GetScreenWidth() / 2 then
+				anchor = "ANCHOR_TOPLEFT"
+			else
+				anchor = "ANCHOR_TOPRIGHT"
+			end
+		end
+		GameTooltip:SetOwner(frame, anchor)
+		if type(self.OnUpdateFuBarTooltip) == "function" and not self:IsDisabled() then
+			self:OnUpdateFuBarTooltip()
+		end
+		GameTooltip:Show()
+		return
+	elseif tooltipType == "Custom" then
+		if type(self.OnUpdateFuBarTooltip) == "function" and not self:IsDisabled() then
+			self:OnUpdateFuBarTooltip()
+		end
+		return
+	elseif tooltipType == "Tablet-2.0" then
+		RegisterTablet20(self)
+		if self:IsFuBarMinimapAttached() and not self:IsFuBarTooltipDetached() and pluginToMinimapFrame[self] then
+			Tablet20:Refresh(pluginToMinimapFrame[self])
+		else
+			Tablet20:Refresh(pluginToFrame[self])
+		end
+	elseif tooltipType == "None" then
+		return
+	else
+		error(("Unknown %s option for %q: %q"):format(MAJOR_VERSION, 'tooltipType', tostring(tooltipType)), 2)
+	end
+end
+
+--[[---------------------------------------------------------------------------
+Notes:
+	Shows the plugin, enables the plugin if previously disabled, and calls :UpdateFuBarPlugin().
+Example:
+	self:Show()
+-----------------------------------------------------------------------------]]
+function FuBarPlugin:Show(panelId)
+	if pluginToFrame[self]:IsShown() or (pluginToMinimapFrame[self] and pluginToMinimapFrame[self]:IsShown()) then
+		return
+	end
+	if panelId ~= false then
+		setLazyDatabaseValue(self, nil, 'hidden')
+	end
+	if self.IsActive and not self:IsActive() then
+		self.panelIdTmp = panelId
+		self:ToggleActive()
+		self.panelIdTmp = nil
+		setLazyDatabaseValue(self, nil, 'disabled')
+	elseif not getLazyDatabaseValue(self, 'hidden') then
+		if panelId == 0 or not CheckFuBar() then
+			MinimapContainer:AddPlugin(self)
+		else
+			FuBar:ShowPlugin(self, panelId or self.panelIdTmp)
+		end
+		if not getPluginOption(self, 'userDefinedFrame', false) then
+			if not self:IsFuBarTextShown() then
+				local text = pluginToFrame[self].text
+				text:SetText("")
+				text:SetWidth(epsilon)
+				text:Hide()
+			end
+			if not self:IsFuBarIconShown() then
+				local icon = pluginToFrame[self].icon
+				icon:SetWidth(epsilon)
+				icon:Hide()
+			end
+		end
+		self:UpdateFuBarPlugin()
+	end
+end
+
+--[[---------------------------------------------------------------------------
+Notes:
+	Hides the plugin, disables the plugin if cannot hide without standby.
+Arguments:
+	[optional] boolean - internal variable. Do not set this.
+Example:
+	self:Hide()
+-----------------------------------------------------------------------------]]
+function FuBarPlugin:Hide(check)
+	if not pluginToFrame[self]:IsShown() and (not pluginToMinimapFrame[self] or not pluginToMinimapFrame[self]:IsShown()) then
+		return
+	end
+	local hideWithoutStandby = getPluginOption(self, 'hideWithoutStandby', false)
+	if hideWithoutStandby and check ~= false then
+		setLazyDatabaseValue(self, true, 'hidden')
+	end
+	if not hideWithoutStandby then
+		if getPluginOption(self, 'tooltipType', "GameTooltip") == "Tablet-2.0" and not getPluginOption(self, 'cannotDetachTooltip', false) and self:IsFuBarTooltipDetached() and getLazyDatabaseValue(self, 'detachedTooltip', 'detached') then
+			self:ReattachTooltip()
+			setLazyDatabaseValue(self, true, 'detachedTooltip', 'detached')
+		end
+		if self.IsActive and self:IsActive() and self.ToggleActive and (not CheckFuBar() or not FuBar:IsChangingProfile()) then
+			self:ToggleActive()
+		end
+	end
+	if pluginToPanel[self] then
+		pluginToPanel[self]:RemovePlugin(self)
+	end
+	pluginToFrame[self]:Hide()
+	if pluginToMinimapFrame[self] then
+		pluginToMinimapFrame[self]:Hide()
+	end
+end
+
+--[[---------------------------------------------------------------------------
+Notes:
+	Sets the path to the icon for the plugin.
+Arguments:
+	string or nil - The path to the icon. If nil, then no icon.
+Example:
+	self:SetFuBarIcon("Interface\\AddOns\\MyAddon\\otherIcon")
+-----------------------------------------------------------------------------]]
+function FuBarPlugin:SetFuBarIcon(path)
+	if not path then
+		return
+	end
+	if not pluginToFrame[self] or not pluginToFrame[self].icon then
+		return
+	end
+	if path:match([[^Interface\Icons\]]) then
+		pluginToFrame[self].icon:SetTexCoord(0.05, 0.95, 0.05, 0.95)
+	else
+		pluginToFrame[self].icon:SetTexCoord(0, 1, 0, 1)
+	end
+	pluginToFrame[self].icon:SetTexture(path)
+	if pluginToMinimapFrame[self] and pluginToMinimapFrame[self].icon then
+		if path:match([[^Interface\Icons\]]) then
+			pluginToMinimapFrame[self].icon:SetTexCoord(0.05, 0.95, 0.05, 0.95)
+		else
+			pluginToMinimapFrame[self].icon:SetTexCoord(0, 1, 0, 1)
+		end
+		pluginToMinimapFrame[self].icon:SetTexture(path)
+	end
+end
+precondition(FuBarPlugin, 'SetFuBarIcon', function(self, path)
+	if not path then
+		return
+	end
+	argCheck(path, 2, "string", "nil")
+	if not getPluginOption(self, 'iconPath', false) then
+		error(("%s: Cannot set icon unless 'iconPath' is set."):format(self:GetTitle()), 3)
+	end
+end)
+
+--[[---------------------------------------------------------------------------
+Returns:
+	string or nil - The path to the icon for the plugin. If nil, then no icon.
+Example:
+	local path = self:GetFuBarIcon()
+-----------------------------------------------------------------------------]]
+function FuBarPlugin:GetFuBarIcon()
+	if getPluginOption(self, 'iconPath', false) then
+		return pluginToFrame[self] and pluginToFrame[self].icon and pluginToFrame[self].icon:GetTexture()
+	end
+end
+
+--[[---------------------------------------------------------------------------
+Notes:
+	Checks the current width of the icon and text, then updates frame to expand/shrink to it if necessary.
+Arguments:
+	[optional] boolean - if true, Shrink/expand no matter what, otherwise if the width is less than 8 pixels smaller, don't shrink.
+Example:
+	self:CheckWidth(true)
+-----------------------------------------------------------------------------]]
+function FuBarPlugin:CheckWidth(force)
+	local frame = pluginToFrame[self]
+	if not frame then
+		return
+	end
+	local icon = frame.icon
+	local text = frame.text
+	if (not icon or not icon:IsShown()) and (not text or not text:IsShown()) then
+		return
+	end
+
+	local db = getLazyDatabaseValue(self)
+
+	if (db and not self:IsFuBarIconShown()) or not getPluginOption(self, 'iconPath', false) then
+		icon:SetWidth(epsilon)
+	end
+	local width
+	if not getPluginOption(self, 'hasNoText', false) then
+		text:SetHeight(0)
+		text:SetWidth(500)
+		width = text:GetStringWidth() + 1
+		text:SetWidth(width)
+		text:SetHeight(text:GetHeight())
+	end
+	local panel = pluginToPanel[self]
+	if getPluginOption(self, 'hasNoText', false) or not text:IsShown() then
+		frame:SetWidth(icon:GetWidth())
+		if panel and panel:GetPluginSide(self) == "CENTER" then
+			panel:UpdateCenteredPosition()
+		end
+	elseif force or not frame.textWidth or frame.textWidth < width or frame.textWidth - 8 > width then
+		frame.textWidth = width
+		text:SetWidth(width)
+		if icon and icon:IsShown() then
+			frame:SetWidth(width + icon:GetWidth())
+		else
+			frame:SetWidth(width)
+		end
+		if panel and panel:GetPluginSide(self) == "CENTER" then
+			panel:UpdateCenteredPosition()
+		end
+	end
+end
+precondition(FuBarPlugin, 'CheckWidth', function(self, force)
+	argCheck(force, 2, "boolean", "nil")
+end)
+
+--[[---------------------------------------------------------------------------
+Notes:
+	Sets the text of the plugin. Should only be called from within :OnFuBarUpdateText()
+Arguments:
+	string - text to set the plugin to. If not given, set to title.
+Example:
+	myAddon.OnFuBarUpdateText = function(self)
+		self:SetFuBarText("Hello")
+	fend
+-----------------------------------------------------------------------------]]
+function FuBarPlugin:SetFuBarText(text)
+	local frame = pluginToFrame[self]
+	if not frame or not frame.text then
+		return
+	end
+	if text == "" then
+		if getPluginOption(self, 'iconPath', false) then
+			self:ShowFuBarIcon()
+		else
+			text = self:GetTitle()
+		end
+	end
+	if not self:IsFuBarTextColored() then
+		text = text:gsub("|c%x%x%x%x%x%x%x%x", ""):gsub("|r", "")
+	end
+	frame.text:SetText(text)
+	self:CheckWidth()
+end
+precondition(FuBarPlugin, 'SetFuBarText', function(self, text)
+	local frame = pluginToFrame[self]
+	if not frame or not frame.text then
+		return
+	end
+	if getPluginOption(self, 'hasNoText', false) then
+		error(("%s: Cannot set text if 'hasNoText' has been set."):format(self:GetTitle()), 3)
+	end
+	argCheck(text, 2, "string", "number")
+end)
+
+--[[---------------------------------------------------------------------------
+Returns:
+	string - The current text of the plugin.
+Example:
+	local text = self:GetFuBarText()
+-----------------------------------------------------------------------------]]
+function FuBarPlugin:GetFuBarText()
+	local frame = pluginToFrame[self]
+	if not frame or not frame.text then
+		error(("%s: Cannot get text without a text frame."):format(self:GetTitle()), 2)
+	end
+	if not getPluginOption(self, 'hasNoText', false) then
+		return frame.text:GetText() or ""
+	end
+end
+
+--[[---------------------------------------------------------------------------
+Returns:
+	boolean - whether the icon for the plugin is showing.
+Example:
+	local isIconShowing = self:IsFuBarIconShown()
+-----------------------------------------------------------------------------]]
+function FuBarPlugin:IsFuBarIconShown()
+	if not getPluginOption(self, 'iconPath', false) then
+		return false
+	elseif getPluginOption(self, 'hasNoText', false) then
+		return true
+	end
+	return not not getLazyDatabaseValueDefault(self, true, 'showIcon')
+end
+
+--[[---------------------------------------------------------------------------
+Notes:
+	Toggles whether the icon for the plugin is showing.
+Example:
+	self:ToggleFuBarIconShown()
+-----------------------------------------------------------------------------]]
+function FuBarPlugin:ToggleFuBarIconShown()
+	local frame = pluginToFrame[self]
+	local icon = frame and frame.icon
+	local text = frame and frame.text
+	if not icon then
+		error(("%s: Cannot toggle icon without an icon frame."):format(self:GetTitle()), 2)
+	elseif not text then
+		error(("%s: Cannot toggle icon without a text frame."):format(self:GetTitle()), 2)
+	elseif not getPluginOption(self, 'iconPath', false) then
+		error(("%s: Cannot show icon unless 'iconPath' is set."):format(self:GetTitle()), 2)
+	elseif getPluginOption(self, 'hasNoText', false) then
+		error(("%s: Cannot show icon if 'hasNoText' is set."):format(self:GetTitle()), 2)
+	elseif not getLazyDatabaseValue(self) then
+		error(("%s: Cannot hide icon if self.db is not available."):format(self:GetTitle()), 2)
+	end
+	local value = not self:IsFuBarIconShown()
+	setLazyDatabaseValue(self, value, 'showIcon')
+	if value then
+		if not self:IsFuBarTextShown() and text:IsShown() and text:GetText() == self:GetTitle() then
+			text:Hide()
+			text:SetText("")
+		end
+		icon:Show()
+		icon:SetWidth(pluginToFrame[self].icon:GetHeight())
+		self:UpdateFuBarText()
+	else
+		if not text:IsShown() or not text:GetText() or text:GetText() == "" then
+			text:Show()
+			text:SetText(self:GetTitle())
+		end
+		icon:Hide()
+		icon:SetWidth(epsilon)
+	end
+	self:CheckWidth(true)
+	return value
+end
+
+--[[---------------------------------------------------------------------------
+Notes:
+	Shows the icon of the plugin if hidden.
+Example:
+	self:ShowFuBarIcon()
+-----------------------------------------------------------------------------]]
+function FuBarPlugin:ShowFuBarIcon()
+	if not self:IsFuBarIconShown() then
+		self:ToggleFuBarIconShown()
+	end
+end
+
+--[[---------------------------------------------------------------------------
+Notes:
+	Hides the icon of the plugin if shown.
+Example:
+	self:HideFuBarIcon()
+-----------------------------------------------------------------------------]]
+function FuBarPlugin:HideFuBarIcon()
+	if self:IsFuBarIconShown() then
+		self:ToggleFuBarIconShown()
+	end
+end
+
+--[[---------------------------------------------------------------------------
+Returns:
+	boolean - whether the text for the plugin is showing.
+Example:
+	local isTextShowing = self:IsFuBarTextShown()
+-----------------------------------------------------------------------------]]
+function FuBarPlugin:IsFuBarTextShown()
+	if getPluginOption(self, 'hasNoText', false) then
+		return false
+	elseif not getPluginOption(self, 'iconPath', false) then
+		return true
+	end
+	return not not getLazyDatabaseValueDefault(self, true, 'showText')
+end
+
+--[[---------------------------------------------------------------------------
+Notes:
+	Toggles whether the text for the plugin is showing.
+Example:
+	self:ToggleFuBarTextShown()
+-----------------------------------------------------------------------------]]
+function FuBarPlugin:ToggleFuBarTextShown()
+	local frame = pluginToFrame[self]
+	local icon = frame and frame.icon
+	local text = frame and frame.text
+	if not icon then
+		error(("%s: Cannot toggle text without an icon frame."):format(self:GetTitle()), 2)
+	elseif not text then
+		error(("%s: Cannot toggle text without a text frame."):format(self:GetTitle()), 2)
+	elseif getPluginOption(self, 'cannotHideText', false) then
+		error(("%s: Cannot toggle text if 'cannotHideText' is set."):format(self:GetTitle()), 2)
+	elseif not getPluginOption(self, 'iconPath', false) then
+		error(("%s: Cannot toggle text unless 'iconPath' is set."):format(self:GetTitle()), 2)
+	elseif getPluginOption(self, 'hasNoText', false) then
+		error(("%s: Cannot toggle text if 'hasNoText' is set."):format(self:GetTitle()), 2)
+	elseif not getLazyDatabaseValue(self) then
+		error(("%s: Cannot toggle text if self.db is not available."):format(self:GetTitle()), 2)
+	end
+	local value = not self:IsFuBarTextShown()
+	setLazyDatabaseValue(self, value, 'showText')
+	if value then
+		text:Show()
+		self:UpdateFuBarText()
+	else
+		text:SetText("")
+		text:SetWidth(epsilon)
+		text:Hide()
+		self:ShowFuBarIcon()
+	end
+	self:CheckWidth(true)
+	return value
+end
+
+--[[---------------------------------------------------------------------------
+Notes:
+	Shows the text of the plugin if hidden.
+Example:
+	self:ShowFuBarText()
+-----------------------------------------------------------------------------]]
+function FuBarPlugin:ShowFuBarText()
+	if not self:IsFuBarTextShown() then
+		self:ToggleFuBarTextShown()
+	end
+end
+
+--[[---------------------------------------------------------------------------
+Notes:
+	Hides the text of the plugin if shown.
+Example:
+	self:HideFuBarText()
+-----------------------------------------------------------------------------]]
+function FuBarPlugin:HideFuBarText()
+	if self:IsFuBarTextShown() then
+		self:ToggleFuBarTextShown()
+	end
+end
+
+--[[---------------------------------------------------------------------------
+Returns:
+	string - default position of the plugin.
+Notes:
+	This is here for FuBar core to communicate properly.
+Example:
+	local pos = self:GetDefaultPosition()
+-----------------------------------------------------------------------------]]
+function FuBarPlugin:GetDefaultPosition()
+	return getPluginOption(self, 'defaultPosition', "LEFT")
+end
+
+--[[---------------------------------------------------------------------------
+Returns:
+	boolean - Whether the tooltip is detached.
+Example:
+	local detached = self:IsFuBarTooltipDetached()
+-----------------------------------------------------------------------------]]
+function FuBarPlugin:IsFuBarTooltipDetached()
+	local tooltipType = getPluginOption(self, 'tooltipType', "GameTooltip")
+	if tooltipType ~= "Tablet-2.0" then
+		return
+	end
+
+	RegisterTablet20(self)
+	return not Tablet20:IsAttached(pluginToFrame[self])
+end
+
+--[[---------------------------------------------------------------------------
+Notes:
+	Toggles whether the tooltip is detached.
+Example:
+	self:ToggleFuBarTooltipDetached()
+-----------------------------------------------------------------------------]]
+function FuBarPlugin:ToggleFuBarTooltipDetached()
+	local tooltipType = getPluginOption(self, 'tooltipType', "GameTooltip")
+	if tooltipType ~= "Tablet-2.0" then
+		return
+	end
+
+	RegisterTablet20(self)
+	if Tablet20:IsAttached(pluginToFrame[self]) then
+		Tablet20:Detach(pluginToFrame[self])
+	else
+		Tablet20:Attach(pluginToFrame[self])
+	end
+end
+
+--[[---------------------------------------------------------------------------
+Notes:
+	* Detaches the tooltip from the plugin.
+	* This does nothing if already detached.
+Example:
+	self:DetachFuBarTooltip()
+-----------------------------------------------------------------------------]]
+function FuBarPlugin:DetachFuBarTooltip()
+	if not self:IsFuBarTooltipDetached() then
+		self:ToggleFuBarTooltipDetached()
+	end
+end
+
+--[[---------------------------------------------------------------------------
+Notes:
+	Reattaches the tooltip to the plugin.
+	This does nothing if already attached.
+Example:
+	self:ReattachFuBarTooltip()
+-----------------------------------------------------------------------------]]
+function FuBarPlugin:ReattachFuBarTooltip()
+	if self:IsFuBarTooltipDetached() then
+		self:ToggleFuBarTooltipDetached()
+	end
+end
+
+local function IsCorrectPanel(panel)
+	if type(panel) ~= "table" then
+		return false
+	elseif type(panel.AddPlugin) ~= "function" then
+		return false
+	elseif type(panel.RemovePlugin) ~= "function" then
+		return false
+	elseif type(panel.GetNumPlugins) ~= "function" then
+		return false
+	elseif type(panel:GetNumPlugins()) ~= "number" then
+		return false
+	elseif type(panel.GetPlugin) ~= "function" then
+		return false
+	elseif type(panel.HasPlugin) ~= "function" then
+		return false
+	elseif type(panel.GetPluginSide) ~= "function" then
+		return false
+	end
+	return true
+end
+
+-- #NODOC
+-- this is used internally by FuBar
+function FuBarPlugin:SetPanel(panel)
+	pluginToPanel[self] = panel
+end
+precondition(FuBarPlugin, 'SetPanel', function(self, panel)
+	argCheck(panel, 2, "table", "nil")
+	if panel and not IsCorrectPanel(panel) then
+		error("Bad argument #2 to `SetPanel'. Panel does not have the correct API.", 3)
+	end
+end)
+
+-- #NODOC
+-- this is used internally by FuBar
+function FuBarPlugin:SetFontSize(size)
+	if getPluginOption(self, 'userDefinedFrame', false) then
+		error(("%sYou must provide a :SetFontSize(size) method if you have 'userDefinedFrame' set."):format(self.name and self.name .. ": " or ""), 2)
+	end
+	if getPluginOption(self, 'iconPath', false) then
+		local frame = pluginToFrame[self]
+		local icon = frame and frame.icon
+		if not icon then
+			error(("%sno icon frame found."):format(self.name and self.name .. ": " or ""), 2)
+		end
+		icon:SetWidth(size + 3)
+		icon:SetHeight(size + 3)
+	end
+	if not getPluginOption(self, 'hasNoText', false) then
+		local frame = pluginToFrame[self]
+		local text = frame and frame.text
+		if not text then
+			error(("%sno text frame found."):format(self.name and self.name .. ": " or ""), 2)
+		end
+		local font, _, flags = text:GetFont()
+		text:SetFont(font, size, flags)
+	end
+	self:CheckWidth()
+end
+
+local function IsLoadOnDemand(plugin)
+	return IsAddOnLoadOnDemand(folderNames[plugin] or "")
+end
+
+-- #NODOC
+-- this is used internally by FuBar.
+function FuBarPlugin:IsDisabled()
+	return type(self.IsActive) == "function" and not self:IsActive() or false
+end
+
+function FuBarPlugin:OnEmbed(target)
+	local folder = Rock.addonToFolder[target]
+	if not folder then
+		for i = 6, 3, -1 do
+			folder = debugstack(i, 1, 0):match([[\AddOns\(.*)\]])
+			if folder then
+				break
+			end
+		end
+	end
+	folderNames[target] = folder
+end
+
+local frame_OnClick, frame_OnDoubleClick, frame_OnMouseDown, frame_OnMouseUp, frame_OnReceiveDrag, frame_OnEnter, frame_OnLeave
+--[[---------------------------------------------------------------------------
+Arguments:
+	[optional] string - name of the frame
+Returns:
+	frame - a frame with the basic scripts to be considered a plugin frame.
+Example:
+	MyPlugin.frame = MyPlugin:CreateBasicPluginFrame("FuBar_MyPluginFrame")
+-----------------------------------------------------------------------------]]
+function FuBarPlugin:CreateBasicPluginFrame(name)
+	local frame = CreateFrame("Button", name, UIParent)
+	frame:SetFrameStrata("HIGH")
+	frame:SetFrameLevel(7)
+	frame:EnableMouse(true)
+	frame:EnableMouseWheel(true)
+	frame:SetMovable(true)
+	frame:SetWidth(150)
+	frame:SetHeight(24)
+	frame:SetPoint("CENTER", UIParent, "CENTER")
+	frame.self = self
+	if not frame_OnEnter then
+		function frame_OnEnter(this)
+			local self = this.self
+			local tooltipType = getPluginOption(self, 'tooltipType', "GameTooltip")
+			if tooltipType == "GameTooltip" then
+				GameTooltip:SetOwner(self:IsFuBarMinimapAttached() and pluginToMinimapFrame[self] or pluginToFrame[self], "ANCHOR_CURSOR")
+				self:UpdateFuBarTooltip()
+			end
+			if type(self.OnFuBarEnter) == "function" then
+				self:OnFuBarEnter()
+			end
+		end
+	end
+	frame:SetScript("OnEnter", frame_OnEnter)
+	if not frame_OnLeave then
+		function frame_OnLeave(this)
+			local self = this.self
+			if type(self.OnFuBarLeave) == "function" then
+				self:OnFuBarLeave()
+			end
+			local tooltipType = getPluginOption(self, 'tooltipType', "GameTooltip")
+			if tooltipType == "GameTooltip" and GameTooltip:IsOwned(self:IsFuBarMinimapAttached() and pluginToMinimapFrame[self] or pluginToFrame[self]) then
+				GameTooltip:Hide()
+			end
+		end
+	end
+	frame:SetScript("OnLeave", frame_OnLeave)
+	if not frame_OnClick then
+		function frame_OnClick(this, button)
+			local self = this.self
+			if self:IsFuBarMinimapAttached() and this.dragged then return end
+			if type(self.OnFuBarClick) == "function" then
+				self:OnFuBarClick(button)
+			end
+		end
+	end
+	frame:SetScript("OnClick", frame_OnClick)
+	if not frame_OnDoubleClick then
+		function frame_OnDoubleClick(this, button)
+			local self = this.self
+			if type(self.OnFuBarDoubleClick) == "function" then
+				self:OnFuBarDoubleClick(button)
+			end
+		end
+	end
+	frame:SetScript("OnDoubleClick", frame_OnDoubleClick)
+	if not frame_OnMouseDown then
+		function frame_OnMouseDown(this, button)
+			local self = this.self
+			if button == "RightButton" and not IsShiftKeyDown() and not IsControlKeyDown() and not IsAltKeyDown() then
+				self:OpenMenu()
+				return
+			else
+				if type(self.OnFuBarMouseDown) == "function" then
+					self:OnFuBarMouseDown(button)
+				end
+			end
+		end
+	end
+	frame:SetScript("OnMouseDown", frame_OnMouseDown)
+	if not frame_OnMouseUp then
+		function frame_OnMouseUp(this, button)
+			local self = this.self
+			if type(self.OnFuBarMouseUp) == "function" then
+				self:OnFuBarMouseUp(button)
+			end
+		end
+	end
+	frame:SetScript("OnMouseUp", frame_OnMouseUp)
+	if not frame_OnReceiveDrag then
+		function frame_OnReceiveDrag(this)
+			local self = this.self
+			if (self:IsFuBarMinimapAttached() and not this.dragged) and type(self.OnReceiveDrag) == "function" then
+				self:OnFuBarReceiveDrag()
+			end
+		end
+	end
+	frame:SetScript("OnReceiveDrag", frame_OnReceiveDrag)
+	return frame
+end
+
+local child_OnEnter, child_OnLeave, child_OnClick, child_OnDoubleClick, child_OnMouseDown, child_OnMouseUp, child_OnReceiveDrag
+--[[---------------------------------------------------------------------------
+Arguments:
+	string - type of the frame, e.g. "Frame", "Button", etc.
+	[optional] string - name of the frame
+	[optional] frame - parent frame
+Returns:
+	frame - a child frame that can be manipulated and used
+Example:
+	local child = self:CreatePluginChildFrame("Frame", nil, self.frame)
+-----------------------------------------------------------------------------]]
+function FuBarPlugin:CreatePluginChildFrame(frameType, name, parent)
+	local child = CreateFrame(frameType, name, parent)
+	if parent then
+		child:SetFrameLevel(parent:GetFrameLevel() + 2)
+	end
+	child.self = self
+	if not child_OnEnter then
+		function child_OnEnter(this, ...)
+			local self = this.self
+			local frame = pluginToFrame[self]
+			if frame:GetScript("OnEnter") then
+				frame:GetScript("OnEnter")(frame, ...)
+			end
+		end
+	end
+	child:SetScript("OnEnter", child_OnEnter)
+	if not child_OnLeave then
+		function child_OnLeave(this, ...)
+			local self = this.self
+			local frame = pluginToFrame[self]
+			if frame:GetScript("OnLeave") then
+				frame:GetScript("OnLeave")(frame, ...)
+			end
+		end
+	end
+	child:SetScript("OnLeave", child_OnLeave)
+	if child:HasScript("OnClick") then
+		if not child_OnClick then
+			function child_OnClick(this, ...)
+				local self = this.self
+				local frame = pluginToFrame[self]
+				if frame:HasScript("OnClick") and frame:GetScript("OnClick") then
+					frame:GetScript("OnClick")(frame, ...)
+				end
+			end
+		end
+		child:SetScript("OnClick", child_OnClick)
+	end
+	if child:HasScript("OnDoubleClick") then
+		if not child_OnDoubleClick then
+			function child_OnDoubleClick(this, ...)
+				local self = this.self
+				local frame = pluginToFrame[self]
+				if frame:HasScript("OnDoubleClick") and frame:GetScript("OnDoubleClick") then
+					frame:GetScript("OnDoubleClick")(frame, ...)
+				end
+			end
+		end
+		child:SetScript("OnDoubleClick", child_OnDoubleClick)
+	end
+	if not child_OnMouseDown then
+		function child_OnMouseDown(this, ...)
+			local self = this.self
+			local frame = pluginToFrame[self]
+			if frame:HasScript("OnMouseDown") and frame:GetScript("OnMouseDown") then
+				frame:GetScript("OnMouseDown")(frame, ...)
+			end
+		end
+	end
+	child:SetScript("OnMouseDown", child_OnMouseDown)
+	if not child_OnMouseUp then
+		function child_OnMouseUp(this, ...)
+			local self = this.self
+			local frame = pluginToFrame[self]
+			if frame:HasScript("OnMouseUp") and frame:GetScript("OnMouseUp") then
+				frame:GetScript("OnMouseUp")(frame, ...)
+			end
+		end
+	end
+	child:SetScript("OnMouseUp", child_OnMouseUp)
+	if not child_OnReceiveDrag then
+		function child_OnReceiveDrag(this, ...)
+			local self = this.self
+			local frame = pluginToFrame[self]
+			if frame:HasScript("OnReceiveDrag") and frame:GetScript("OnReceiveDrag") then
+				frame:GetScript("OnReceiveDrag")(frame, ...)
+			end
+		end
+	end
+	child:SetScript("OnReceiveDrag", child_OnReceiveDrag)
+	return child
+end
+precondition(FuBarPlugin, 'CreatePluginChildFrame', function(self, frameType, name, parent)
+	if not pluginToFrame[self] then
+		error(("%sYou must have self.frame declared in order to add child frames."):format(self.name and self.name .. ": " or ""), 3)
+	end
+	argCheck(frameType, 2, "string")
+end)
+
+--[[---------------------------------------------------------------------------
+Notes:
+	Opens the configuration menu associated with this plugin.
+Example:
+	self:OpenMenu()
+-----------------------------------------------------------------------------]]
+function FuBarPlugin:OpenMenu(frame)
+	if not frame then
+		frame = self:IsFuBarMinimapAttached() and pluginToMinimapFrame[self] or pluginToFrame[self]
+	end
+	if not frame:IsVisible() then
+		frame = UIParent
+	end
+	local configType = getPluginOption(self, 'configType', "LibRockConfig-1.0")
+	if configType == "Dewdrop-2.0" then
+		if not frame or not self:GetFrame() or Dewdrop20:IsOpen(frame) then
+			Dewdrop20:Close()
+			return
+		end
+		local tooltipType = getPluginOption(self, 'tooltipType', "GameTooltip")
+		if tooltipType == "GameTooltip" then
+			if GameTooltip:IsOwned(frame) then
+				GameTooltip:Hide()
+			end
+		elseif tooltipType == "Custom" and type(self.CloseTooltip) == "function" then
+			self:CloseTooltip()
+		elseif tooltipType == "Tablet-2.0" and Tablet20 then
+			Tablet20:Close()
+		end
+
+		if not Dewdrop20:IsRegistered(self:GetFrame()) then
+			if type(self.OnMenuRequest) == "table" and (not self.OnMenuRequest.handler or self.OnMenuRequest.handler == self) and self.OnMenuRequest.type == "group" then
+				Dewdrop20:InjectAceOptionsTable(self, self.OnMenuRequest)
+				if self.OnMenuRequest.args and CheckFuBar() and not getPluginOption(self, 'independentProfile', false) then
+					self.OnMenuRequest.args.profile = nil
+					if self.OnMenuRequest.extraArgs then
+						self.OnMenuRequest.extraArgs.profile = nil
+					end
+				end
+			end
+			Dewdrop20:Register(self:GetFrame(),
+				'children', type(self.OnMenuRequest) == "table" and self.OnMenuRequest or function(level, value, valueN_1, valueN_2, valueN_3, valueN_4)
+					if level == 1 then
+						if not getPluginOption(self, 'hideMenuTitle', false) then
+							Dewdrop20:AddLine(
+								'text', self:GetTitle(),
+								'isTitle', true
+							)
+						end
+
+						if self.OnMenuRequest then
+							self:OnMenuRequest(level, value, false, valueN_1, valueN_2, valueN_3, valueN_4)
+						end
+
+						if not getPluginOption(self, 'overrideMenu', false) then
+							if self.MenuSettings and not getPluginOption(self, 'hideMenuTitle', false) then
+								Dewdrop20:AddLine()
+							end
+							self:AddImpliedMenuOptions()
+						end
+					else
+						if not getPluginOption(self, 'overrideMenu', false) and self:AddImpliedMenuOptions() then
+						else
+							if self.OnMenuRequest then
+								self:OnMenuRequest(level, value, false, valueN_1, valueN_2, valueN_3, valueN_4)
+							end
+						end
+					end
+					if level == 1 then
+						Dewdrop20:AddLine(
+							'text', CLOSE,
+							'tooltipTitle', CLOSE,
+							'tooltipText', CLOSE_DESC,
+							'func', Dewdrop.Close,
+							'arg1', Dewdrop
+						)
+					end
+				end,
+				'point', function(frame)
+					local x, y = frame:GetCenter()
+					local leftRight
+					if x < GetScreenWidth() / 2 then
+						leftRight = "LEFT"
+					else
+						leftRight = "RIGHT"
+					end
+					if y < GetScreenHeight() / 2 then
+						return "BOTTOM" .. leftRight, "TOP" .. leftRight
+					else
+						return "TOP" .. leftRight, "BOTTOM" .. leftRight
+					end
+				end,
+				'dontHook', true
+			)
+		end
+		if frame == self:GetFrame() then
+			Dewdrop20:Open(self:GetFrame())
+		elseif frame ~= UIParent then
+			Dewdrop20:Open(frame, self:GetFrame())
+		else
+			Dewdrop20:Open(frame, self:GetFrame(), 'cursorX', true, 'cursorY', true)
+		end
+	elseif configType == "LibRockConfig-1.0" then
+		local RockConfig = Rock("LibRockConfig-1.0", false, true)
+		if RockConfig then
+			RockConfig.OpenConfigMenu(self)
+		end
+	else
+		-- TODO: add more possibilities
+	end
+end
+
+function FuBarPlugin.OnEmbedInitialize(FuBarPlugin, self)
+	if not self.frame then
+		local name = MAJOR_VERSION .. "_" .. self:GetTitle() .. "_" .. "Frame"
+		local frame = _G[name]
+		if not frame or not _G[name .. "Text"] or not _G[name .. "Icon"] then
+			frame = FuBarPlugin.CreateBasicPluginFrame(self, name)
+
+			local icon = frame:CreateTexture(name .. "Icon", "ARTWORK")
+			frame.icon = icon
+			icon:SetWidth(16)
+			icon:SetHeight(16)
+			icon:SetPoint("LEFT", frame, "LEFT")
+
+			local text = frame:CreateFontString(name .. "Text", "ARTWORK")
+			frame.text = text
+			text:SetWidth(134)
+			text:SetHeight(24)
+			text:SetPoint("LEFT", icon, "RIGHT", 0, 1)
+			text:SetFontObject(GameFontNormal)
+		end
+		pluginToFrame[self] = frame
+	else
+		pluginToFrame[self] = self.frame
+		if not pluginToOptions[self] then
+			pluginToOptions[self] = {}
+		end
+		pluginToOptions[self].userDefinedFrame = true
+	end
+
+	local frame = pluginToFrame[self]
+	frame.plugin = self
+	frame:SetParent(UIParent)
+	frame:SetPoint("RIGHT", UIParent, "LEFT", -5, 0)
+	frame:Hide()
+
+	local iconPath = getPluginOption(self, 'iconPath', false)
+	if iconPath then
+		self:SetFuBarIcon(iconPath)
+	end
+
+	if CheckFuBar() then
+		FuBar:RegisterPlugin(self)
+	end
+end
+
+
+local CheckShow = function(self, panelId)
+	if not pluginToFrame[self]:IsShown() and (not pluginToMinimapFrame[self] or not pluginToMinimapFrame[self]:IsShown()) then
+		self:Show(panelId)
+	end
+end
+
+local schedules = {}
+local f = CreateFrame("Frame")
+f:SetScript("OnUpdate", function(this)
+	for i,v in ipairs(schedules) do
+		local success, ret = pcall(unpack(v))
+		if not success then
+			geterrorhandler()(ret)
+		end
+		schedules[i] = del(v)
+	end
+	f:Hide()
+end)
+
+local recheckPlugins
+local AceConsole
+function FuBarPlugin.OnEmbedEnable(FuBarPlugin, self, first)
+	if not getPluginOption(self, 'userDefinedFrame', false) then
+		local icon = pluginToFrame[self].icon
+		if self:IsFuBarIconShown() then
+			icon:Show()
+		else
+			icon:Hide()
+		end
+	end
+	self:CheckWidth(true)
+
+	if not getPluginOption(self, 'hideWithoutStandby', false) or (getLazyDatabaseValue(self) and not getLazyDatabaseValue(self, 'hidden')) then
+		if not first then
+			CheckShow(self, self.panelIdTmp)
+		else
+			schedules[#schedules+1] = newList(CheckShow, self, self.panelIdTmp)
+			f:Show()
+		end
+	end
+
+	local tooltipType = getPluginOption(self, 'tooltipType', "GameTooltip")
+	if tooltipType == "Tablet-2.0" and not getPluginOption(self, 'cannotDetachTooltip', false) and getLazyDatabaseValue(self, 'detachedTooltip', 'detached') then
+		schedules[#schedules+1] = newList(self.DetachFuBarTooltip, self)
+		f:Show()
+	end
+
+	if IsLoadOnDemand(self) and CheckFuBar() then
+		if not FuBar.db.profile.loadOnDemand then
+			FuBar.db.profile.loadOnDemand = {}
+		end
+		if not FuBar.db.profile.loadOnDemand[folderNames[self]] then
+			FuBar.db.profile.loadOnDemand[folderNames[self]] = {}
+		end
+		FuBar.db.profile.loadOnDemand[folderNames[self]].disabled = nil
+	end
+	--[[
+	if CheckFuBar() and AceLibrary:HasInstance("AceConsole-2.0") then
+		if not recheckPlugins then
+			if not AceConsole then
+				AceConsole = AceLibrary("AceConsole-2.0")
+			end
+			recheckPlugins = function()
+				for k,v in pairs(AceConsole.registry) do
+					if type(v) == "table" and v.args and AceOO.inherits(v.handler, FuBarPlugin) and not v.handler.independentProfile then
+						v.args.profile = nil
+					end
+				end
+			end
+		end
+		FuBarPlugin:ScheduleEvent("FuBarPlugin-recheckPlugins", recheckPlugins, 0)
+	end
+	]]
+end
+
+function FuBarPlugin.OnEmbedDisable(FuBarPlugin, self)
+	self:Hide(false)
+
+	if IsLoadOnDemand(self) and CheckFuBar() then
+		if not FuBar.db.profile.loadOnDemand then
+			FuBar.db.profile.loadOnDemand = {}
+		end
+		if not FuBar.db.profile.loadOnDemand[folderNames[self]] then
+			FuBar.db.profile.loadOnDemand[folderNames[self]] = {}
+		end
+		FuBar.db.profile.loadOnDemand[folderNames[self]].disabled = true
+	end
+end
+
+function FuBarPlugin.OnEmbedProfileEnable(FuBarPlugin, self)
+	self:UpdateFuBarPlugin()
+	if getLazyDatabaseValue(self) then
+		if not getLazyDatabaseValue(self, 'detachedTooltip') then
+			setLazyDatabaseValue(self, {}, 'detachedTooltip')
+		end
+		local tooltipType = getPluginOption(self, 'tooltipType', "GameTooltip")
+		if tooltipType == "Tablet-2.0" and Tablet20 then
+			if Tablet20.registry[pluginToFrame[self]] then
+				Tablet20:UpdateDetachedData(pluginToFrame[self], getLazyDatabaseValue(self, 'detachedTooltip'))
+			else
+				RegisterTablet20(self)
+			end
+		end
+		if MinimapContainer:HasPlugin(self) then
+			MinimapContainer:ReadjustLocation(self)
+		end
+	end
+end
+
+-- #NODOC
+function FuBarPlugin.GetEmbedRockConfigOptions(FuBarPlugin, self)
+	return 'icon', {
+		type = 'boolean',
+		name = SHOW_FUBAR_ICON,
+		desc = SHOW_FUBAR_ICON_DESC,
+		set = "ToggleFuBarIconShown",
+		get = "IsFuBarIconShown",
+		hidden = function()
+			return not getPluginOption(self, 'iconPath', false) or getPluginOption(self, 'hasNoText', false) or self:IsDisabled() or self:IsFuBarMinimapAttached() or not getLazyDatabaseValue(self)
+		end,
+		order = -13.7,
+		handler = self,
+	}, 'text', {
+		type = 'boolean',
+		name = SHOW_FUBAR_TEXT,
+		desc = SHOW_FUBAR_TEXT_DESC,
+		set = "ToggleFuBarTextShown",
+		get = "IsFuBarTextShown",
+		hidden = function()
+			return getPluginOption(self, 'cannotHideText', false) or not getPluginOption(self, 'iconPath', false) or getPluginOption(self, 'hasNoText') or self:IsDisabled() or self:IsFuBarMinimapAttached() or not getLazyDatabaseValue(self)
+		end,
+		order = -13.6,
+		handler = self,
+	}, 'colorText', {
+		type = 'boolean',
+		name = SHOW_COLORED_FUBAR_TEXT,
+		desc = SHOW_COLORED_FUBAR_TEXT_DESC,
+		set = "ToggleFuBarTextColored",
+		get = "IsFuBarTextColored",
+		hidden = function()
+			return getPluginOption(self, 'userDefinedFrame', false) or getPluginOption(self, 'hasNoText', false) or getPluginOption(self, 'hasNoColor', false) or self:IsDisabled() or self:IsFuBarMinimapAttached() or not getLazyDatabaseValue(self)
+		end,
+		order = -13.5,
+		handler = self,
+	}, 'detachTooltip', {
+		type = 'boolean',
+		name = DETACH_FUBAR_TOOLTIP,
+		desc = DETACH_FUBAR_TOOLTIP_DESC,
+		get = "IsFuBarTooltipDetached",
+		set = "ToggleFuBarTooltipDetached",
+		hidden = function()
+			return not Tablet20 or getPluginOption(self, 'tooltipType', "GameTooltip") ~= "Tablet-2.0" or self:IsDisabled()
+		end,
+		order = -13.4,
+		handler = self,
+	}, 'lockTooltip', {
+		type = 'boolean',
+		name = LOCK_FUBAR_TOOLTIP,
+		desc = LOCK_FUBAR_TOOLTIP_DESC,
+		get = function()
+			return Tablet20:IsLocked(pluginToFrame[self])
+		end,
+		set = function()
+			return Tablet20:ToggleLocked(pluginToFrame[self])
+		end,
+		disabled = function()
+			return not self:IsFuBarTooltipDetached()
+		end,
+		hidden = function()
+			return not Tablet20 or getPluginOption(self, 'tooltipType', "GameTooltip") ~= "Tablet-2.0" or getPluginOption(self, 'cannotDetachTooltip', false) or self:IsDisabled()
+		end,
+		order = -13.3,
+		handler = self,
+	}, 'position', {
+		type = 'choice',
+		name = POSITION_ON_FUBAR,
+		desc = POSITION_ON_FUBAR_DESC,
+		choices = {
+			LEFT = POSITION_LEFT,
+			CENTER = POSITION_CENTER,
+			RIGHT = POSITION_RIGHT
+		},
+		choiceSort = {
+			"LEFT",
+			"CENTER",
+			"RIGHT",
+		},
+		get = function()
+			return self:GetPanel() and self:GetPanel():GetPluginSide(self)
+		end,
+		set = function(value)
+			if self:GetPanel() then
+				self:GetPanel():SetPluginSide(self, value)
+			end
+		end,
+		hidden = function()
+			return self:IsFuBarMinimapAttached() or self:IsDisabled() or not pluginToPanel[self]
+		end,
+		order = -13.2,
+		handler = self,
+	}, 'minimapAttach', {
+		type = 'boolean',
+		name = ATTACH_PLUGIN_TO_MINIMAP,
+		desc = ATTACH_PLUGIN_TO_MINIMAP_DESC,
+		get = "IsFuBarMinimapAttached",
+		set = "ToggleFuBarMinimapAttached",
+		hidden = function()
+			return (getPluginOption(self, 'cannotAttachToMinimap', false) and not self:IsFuBarMinimapAttached()) or not CheckFuBar() or self:IsDisabled()
+		end,
+		order = -13.1,
+		handler = self,
+	}, 'hide', {
+		type = 'boolean',
+		name = function()
+			if self:IsFuBarMinimapAttached() then
+				return HIDE_MINIMAP_BUTTON
+			else
+				return HIDE_FUBAR_PLUGIN
+			end
+		end,
+		desc = HIDE_FUBAR_PLUGIN_DESC,
+		get = function()
+			return not pluginToFrame[self]:IsShown() and (not pluginToMinimapFrame[self] or not pluginToMinimapFrame[self]:IsShown())
+		end,
+		set = function(value)
+			if not value then
+				self:Show()
+			else
+				self:Hide()
+			end
+		end,
+		hidden = function()
+			return not getPluginOption(self, 'hideWithoutStandby', false) or self:IsDisabled()
+		end,
+		order = -13,
+		handler = self,
+	}
+end
+
+local plugins = MinimapContainer.plugins or {}
+for k in pairs(MinimapContainer) do
+	MinimapContainer[k] = nil
+end
+MinimapContainer.plugins = plugins
+
+local minimap_OnMouseDown, minimap_OnMouseUp
+function MinimapContainer:AddPlugin(plugin)
+	if CheckFuBar() and FuBar:IsChangingProfile() then
+		return
+	end
+	if pluginToPanel[plugin] then
+		pluginToPanel[plugin]:RemovePlugin(plugin)
+	end
+	pluginToPanel[plugin] = self
+	if not pluginToMinimapFrame[plugin] then
+		local frame = CreateFrame("Button", pluginToFrame[plugin]:GetName() .. "MinimapButton", Minimap)
+		pluginToMinimapFrame[plugin] = frame
+		plugin.minimapFrame = frame
+		frame.plugin = plugin
+		frame:SetWidth(31)
+		frame:SetHeight(31)
+		frame:SetFrameStrata("BACKGROUND")
+		frame:SetFrameLevel(4)
+		frame:SetHighlightTexture("Interface\\Minimap\\UI-Minimap-ZoomButton-Highlight")
+		local icon = frame:CreateTexture(frame:GetName() .. "Icon", "BACKGROUND")
+		plugin.minimapIcon = icon
+		local path = plugin:GetFuBarIcon() or (pluginToFrame[plugin].icon and pluginToFrame[plugin].icon:GetTexture()) or "Interface\\Icons\\INV_Misc_QuestionMark"
+		icon:SetTexture(path)
+		if path:sub(1, 16) == "Interface\\Icons\\" then
+			icon:SetTexCoord(0.07, 0.93, 0.07, 0.93)
+		else
+			icon:SetTexCoord(0, 1, 0, 1)
+		end
+		icon:SetWidth(20)
+		icon:SetHeight(20)
+		icon:SetPoint("TOPLEFT", frame, "TOPLEFT", 7, -5)
+		local overlay = frame:CreateTexture(frame:GetName() .. "Overlay","OVERLAY")
+		overlay:SetTexture("Interface\\Minimap\\MiniMap-TrackingBorder")
+		overlay:SetWidth(53)
+		overlay:SetHeight(53)
+		overlay:SetPoint("TOPLEFT",frame,"TOPLEFT")
+		frame:EnableMouse(true)
+		frame:RegisterForClicks("LeftButtonUp")
+
+		frame.self = plugin
+		if not frame_OnEnter then
+			function frame_OnEnter(this)
+				if type(this.self.OnFuBarEnter) == "function" then
+					this.self:OnFuBarEnter()
+				end
+			end
+		end
+		frame:SetScript("OnEnter", frame_OnEnter)
+		if not frame_OnLeave then
+			function frame_OnLeave(this)
+				if type(this.self.OnFuBarLeave) == "function" then
+					this.self:OnFuBarLeave()
+				end
+			end
+		end
+		frame:SetScript("OnLeave", frame_OnLeave)
+		if not frame_OnClick then
+			function frame_OnClick(this, arg1)
+				if this.self:IsMinimapAttached() and this.dragged then return end
+				if type(this.self.OnFuBarClick) == "function" then
+					this.self:OnFuBarClick(arg1)
+				end
+			end
+		end
+		frame:SetScript("OnClick", frame_OnClick)
+		if not frame_OnDoubleClick then
+			function frame_OnDoubleClick(this, arg1)
+				if type(this.self.OnFuBarDoubleClick) == "function" then
+					this.self:OnFuBarDoubleClick(arg1)
+				end
+			end
+		end
+		frame:SetScript("OnDoubleClick", frame_OnDoubleClick)
+		if not frame_OnReceiveDrag then
+			function frame_OnReceiveDrag(this)
+				if this.self:IsMinimapAttached() and this.dragged then return end
+				if type(this.self.OnFuBarReceiveDrag) == "function" then
+					this.self:OnFuBarReceiveDrag()
+				end
+			end
+		end
+		frame:SetScript("OnReceiveDrag", frame_OnReceiveDrag)
+		if not minimap_OnMouseDown then
+			function minimap_OnMouseDown(this, arg1)
+				this.dragged = false
+				if arg1 == "LeftButton" and not IsShiftKeyDown() and not IsControlKeyDown() and not IsAltKeyDown() then
+					HideDropDownMenu(1)
+					if type(this.self.OnFuBarMouseDown) == "function" then
+						this.self:OnFuBarMouseDown(arg1)
+					end
+				elseif arg1 == "RightButton" and not IsShiftKeyDown() and not IsControlKeyDown() and not IsAltKeyDown() then
+					this.self:OpenMenu(this)
+				else
+					HideDropDownMenu(1)
+					if type(this.self.OnFuBarMouseDown) == "function" then
+						this.self:OnFuBarMouseDown(arg1)
+					end
+				end
+				if this.self.OnFuBarClick or this.self.OnFuBarMouseDown or this.self.OnFuBarMouseUp or this.self.OnFuBarDoubleClick then
+					if this.self.minimapIcon:GetTexture():sub(1, 16) == "Interface\\Icons\\" then
+						this.self.minimapIcon:SetTexCoord(0.14, 0.86, 0.14, 0.86)
+					else
+						this.self.minimapIcon:SetTexCoord(0.1, 0.9, 0.1, 0.9)
+					end
+				end
+			end
+		end
+		frame:SetScript("OnMouseDown", minimap_OnMouseDown)
+		if not minimap_OnMouseUp then
+			function minimap_OnMouseUp(this, arg1)
+				if not this.dragged and type(this.self.OnFuBarMouseUp) == "function" then
+					this.self:OnFuBarMouseUp(arg1)
+				end
+				if this.self.minimapIcon:GetTexture():sub(1, 16) == "Interface\\Icons\\" then
+					this.self.minimapIcon:SetTexCoord(0.05, 0.95, 0.05, 0.95)
+				else
+					this.self.minimapIcon:SetTexCoord(0, 1, 0, 1)
+				end
+			end
+		end
+		frame:SetScript("OnMouseUp", minimap_OnMouseUp)
+		frame:RegisterForDrag("LeftButton")
+		frame:SetScript("OnDragStart", self.OnDragStart)
+		frame:SetScript("OnDragStop", self.OnDragStop)
+
+		if getPluginOption(plugin, 'tooltipType', "GameTooltip") == "Tablet-2.0" then
+			-- Note that we have to do this after :SetScript("OnEnter"), etc,
+			-- so that Tablet-2.0 can override it properly.
+			RegisterTablet20(plugin)
+			Tablet20:Register(frame, pluginToFrame[plugin])
+		end
+	end
+	pluginToFrame[plugin]:Hide()
+	pluginToMinimapFrame[plugin]:Show()
+	self:ReadjustLocation(plugin)
+	table.insert(self.plugins, plugin)
+	local exists = false
+	return true
+end
+
+function MinimapContainer:RemovePlugin(index)
+	if CheckFuBar() and FuBar:IsChangingProfile() then
+		return
+	end
+	if type(index) == "table" then
+		index = self:IndexOfPlugin(index)
+		if not index then
+			return
+		end
+	end
+	local t = self.plugins
+	local plugin = t[index]
+	assert(pluginToPanel[plugin] == self, "Plugin has improper panel field")
+	plugin:SetPanel(nil)
+	table.remove(t, index)
+	return true
+end
+
+function MinimapContainer:ReadjustLocation(plugin)
+	local frame = pluginToMinimapFrame[plugin]
+	if plugin.db and plugin.db.profile.minimapPositionWild then
+		frame:SetPoint("CENTER", UIParent, "BOTTOMLEFT", plugin.db.profile.minimapPositionX, plugin.db.profile.minimapPositionY)
+	elseif not plugin.db and plugin.minimapPositionWild then
+		frame:SetPoint("CENTER", UIParent, "BOTTOMLEFT", plugin.minimapPositionX, plugin.minimapPositionY)
+	else
+		local position
+		if plugin.db then
+			position = plugin.db.profile.minimapPosition or getPluginOption(plugin, 'defaultMinimapPosition', nil) or math.random(1, 360)
+		else
+			position = plugin.minimapPosition or getPluginOption(plugin, 'defaultMinimapPosition', nil) or math.random(1, 360)
+		end
+		local angle = math.rad(position or 0)
+		local x,y
+		local minimapShape = GetMinimapShape and GetMinimapShape() or "ROUND"
+		local cos = math.cos(angle)
+		local sin = math.sin(angle)
+
+		local round = true
+		if minimapShape == "ROUND" then
+			-- do nothing
+		elseif minimapShape == "SQUARE" then
+			round = false
+		elseif minimapShape == "CORNER-TOPRIGHT" then
+			if cos < 0 or sin < 0 then
+				round = false
+			end
+		elseif minimapShape == "CORNER-TOPLEFT" then
+			if cos > 0 or sin < 0 then
+				round = false
+			end
+		elseif minimapShape == "CORNER-BOTTOMRIGHT" then
+			if cos < 0 or sin > 0 then
+				round = false
+			end
+		elseif minimapShape == "CORNER-BOTTOMLEFT" then
+			if cos > 0 or sin > 0 then
+				round = false
+			end
+		elseif minimapShape == "SIDE-LEFT" then
+			if cos > 0 then
+				round = false
+			end
+		elseif minimapShape == "SIDE-RIGHT" then
+			if cos < 0 then
+				round = false
+			end
+		elseif minimapShape == "SIDE-TOP" then
+			if sin < 0 then
+				round = false
+			end
+		elseif minimapShape == "SIDE-BOTTOM" then
+			if sin > 0 then
+				round = false
+			end
+		elseif minimapShape == "TRICORNER-TOPRIGHT" then
+			if cos < 0 and sin < 0 then
+				round = false
+			end
+		elseif minimapShape == "TRICORNER-TOPLEFT" then
+			if cos > 0 and sin < 0 then
+				round = false
+			end
+		elseif minimapShape == "TRICORNER-BOTTOMRIGHT" then
+			if cos < 0 and sin > 0 then
+				round = false
+			end
+		elseif minimapShape == "TRICORNER-BOTTOMLEFT" then
+			if cos > 0 and sin > 0 then
+				round = false
+			end
+		end
+
+		if round then
+			x = cos * 80
+			y = sin * 80
+		else
+			x = 80 * 2^0.5 * cos
+			y = 80 * 2^0.5 * sin
+			if x < -80 then
+				x = -80
+			elseif x > 80 then
+				x = 80
+			end
+			if y < -80 then
+				y = -80
+			elseif y > 80 then
+				y = 80
+			end
+		end
+		frame:SetPoint("CENTER", Minimap, "CENTER", x, y)
+	end
+end
+
+function MinimapContainer:GetPlugin(index)
+	return self.plugins[index]
+end
+
+function MinimapContainer:GetNumPlugins()
+	return #self.plugins
+end
+
+function MinimapContainer:IndexOfPlugin(plugin)
+	for i,p in ipairs(self.plugins) do
+		if p == plugin then
+			return i, "MINIMAP"
+		end
+	end
+end
+
+function MinimapContainer:HasPlugin(plugin)
+	return self:IndexOfPlugin(plugin) ~= nil
+end
+
+function MinimapContainer:GetPluginSide(plugin)
+	local index = self:IndexOfPlugin(plugin)
+	assert(index, "Plugin not in panel")
+	return "MINIMAP"
+end
+
+function MinimapContainer.OnDragStart(this)
+	this.dragged = true
+	this:LockHighlight()
+	this:SetScript("OnUpdate", MinimapContainer.OnUpdate)
+	if this.self.minimapIcon:GetTexture():sub(1, 16) == "Interface\\Icons\\" then
+		this.self.minimapIcon:SetTexCoord(0.05, 0.95, 0.05, 0.95)
+	else
+		this.self.minimapIcon:SetTexCoord(0, 1, 0, 1)
+	end
+end
+
+function MinimapContainer.OnDragStop(this)
+	this:SetScript("OnUpdate", nil)
+	this:UnlockHighlight()
+end
+
+function MinimapContainer.OnUpdate(this, elapsed)
+	if not IsAltKeyDown() then
+		local mx, my = Minimap:GetCenter()
+		local px, py = GetCursorPosition()
+		local scale = UIParent:GetEffectiveScale()
+		px, py = px / scale, py / scale
+		local position = math.deg(math.atan2(py - my, px - mx))
+		if position <= 0 then
+			position = position + 360
+		elseif position > 360 then
+			position = position - 360
+		end
+		if this.self.db then
+			this.self.db.profile.minimapPosition = position
+			this.self.db.profile.minimapPositionX = nil
+			this.self.db.profile.minimapPositionY = nil
+			this.self.db.profile.minimapPositionWild = nil
+		else
+			this.self.minimapPosition = position
+			this.self.minimapPositionX = nil
+			this.self.minimapPositionY = nil
+			this.self.minimapPositionWild = nil
+		end
+	else
+		local px, py = GetCursorPosition()
+		local scale = UIParent:GetEffectiveScale()
+		px, py = px / scale, py / scale
+		if this.self.db then
+			this.self.db.profile.minimapPositionX = px
+			this.self.db.profile.minimapPositionY = py
+			this.self.db.profile.minimapPosition = nil
+			this.self.db.profile.minimapPositionWild = true
+		else
+			this.self.minimapPositionX = px
+			this.self.minimapPositionY = py
+			this.self.minimapPosition = nil
+			this.self.minimapPositionWild = true
+		end
+	end
+	MinimapContainer:ReadjustLocation(this.self)
+end
+
+FuBarPlugin:SetExportedMethods(
+	"SetFuBarOption",
+	"GetTitle",
+	"GetName",
+	"GetCategory",
+	"SetFontSize",
+	"GetFrame",
+	"Show",
+	"Hide",
+	"GetPanel",
+	"IsFuBarTextColored",
+	"ToggleFuBarTextColored",
+	"IsFuBarMinimapAttached",
+	"ToggleFuBarMinimapAttached",
+	"UpdateFuBarPlugin",
+	"UpdateFuBarText",
+	"UpdateFuBarTooltip",
+	"SetFuBarIcon",
+	"GetFuBarIcon",
+	"CheckWidth",
+	"SetFuBarText",
+	"GetFuBarText",
+	"IsFuBarIconShown",
+	"ToggleFuBarIconShown",
+	"ShowFuBarIcon",
+	"HideFuBarIcon",
+	"IsFuBarTextShown",
+	"ToggleFuBarTextShown",
+	"ShowFuBarText",
+	"HideFuBarText",
+	"IsFuBarTooltipDetached",
+	"ToggleFuBarTooltipDetached",
+	"DetachFuBarTooltip",
+	"ReattachFuBarTooltip",
+	"GetDefaultPosition",
+	"SetPanel",
+	"IsDisabled",
+	"CreateBasicPluginFrame",
+	"CreatePluginChildFrame",
+	"OpenMenu"
+)
+
+Rock:FinalizeLibrary(MAJOR_VERSION)