
local detailsFramework = _G ["DetailsFramework"]
if (not detailsFramework or not DetailsFrameworkCanLoad) then
	return
end

local _
--lua locals
local rawset = rawset --lua local
local rawget = rawget --lua local
local setmetatable = setmetatable --lua local
local unpack = table.unpack or unpack --lua local
local type = type --lua local
local floor = math.floor --lua local
local loadstring = loadstring --lua local
local CreateFrame = CreateFrame

-- TWW compatibility:
local GetSpellInfo = GetSpellInfo or function(spellID) if not spellID then return nil end local si = C_Spell.GetSpellInfo(spellID) if si then return si.name, nil, si.iconID, si.castTime, si.minRange, si.maxRange, si.spellID, si.originalIconID end end
local GetNumSpellTabs = GetNumSpellTabs or C_SpellBook.GetNumSpellBookSkillLines
local GetSpellTabInfo = GetSpellTabInfo or function(tabLine) local skillLine = C_SpellBook.GetSpellBookSkillLineInfo(tabLine) if skillLine then return skillLine.name, skillLine.iconID, skillLine.itemIndexOffset, skillLine.numSpellBookItems, skillLine.isGuild, skillLine.offSpecID end end
local SPELLBOOK_BANK_PLAYER = Enum.SpellBookSpellBank and Enum.SpellBookSpellBank.Player or "player"
local SpellBookItemTypeMap = Enum.SpellBookItemType and {[Enum.SpellBookItemType.Spell] = "SPELL", [Enum.SpellBookItemType.None] = "NONE", [Enum.SpellBookItemType.Flyout] = "FLYOUT", [Enum.SpellBookItemType.FutureSpell] = "FUTURESPELL", [Enum.SpellBookItemType.PetAction] = "PETACTION" } or {}
local GetSpellBookItemInfo = GetSpellBookItemInfo or function(...) local si = C_SpellBook.GetSpellBookItemInfo(...) if si then return SpellBookItemTypeMap[si.itemType] or "NONE", (si.itemType == Enum.SpellBookItemType.Flyout or si.itemType == Enum.SpellBookItemType.PetAction) and si.actionID or si.spellID or si.actionID, si end end
local GetSpellBookItemTexture = GetSpellBookItemTexture or function(...) return C_SpellBook.GetSpellBookItemTexture(...) end
local GetSpellTexture = GetSpellTexture or function(...) return C_Spell.GetSpellTexture(...) end

local IS_WOW_PROJECT_MAINLINE = WOW_PROJECT_ID == WOW_PROJECT_MAINLINE
local IS_WOW_PROJECT_NOT_MAINLINE = WOW_PROJECT_ID ~= WOW_PROJECT_MAINLINE
local IS_WOW_PROJECT_CLASSIC_ERA = WOW_PROJECT_ID == WOW_PROJECT_CLASSIC

local CastInfo = detailsFramework.CastInfo

local PixelUtil = PixelUtil or DFPixelUtil

local UnitGroupRolesAssigned = detailsFramework.UnitGroupRolesAssigned

local cleanfunction = function() end
local APIFrameFunctions

do
	local metaPrototype = {
		WidgetType = "panel",
		dversion = detailsFramework.dversion,
	}

	--check if there's a metaPrototype already existing
	if (_G[detailsFramework.GlobalWidgetControlNames["panel"]]) then
		--get the already existing metaPrototype
		local oldMetaPrototype = _G[detailsFramework.GlobalWidgetControlNames ["panel"]]
		--check if is older
		if ( (not oldMetaPrototype.dversion) or (oldMetaPrototype.dversion < detailsFramework.dversion) ) then
			--the version is older them the currently loading one
			--copy the new values into the old metatable
			for funcName, _ in pairs(metaPrototype) do
				oldMetaPrototype[funcName] = metaPrototype[funcName]
			end
		end
	else
		--first time loading the framework
		_G[detailsFramework.GlobalWidgetControlNames["panel"]] = metaPrototype
	end
end

local PanelMetaFunctions = _G[detailsFramework.GlobalWidgetControlNames["panel"]]
detailsFramework:Mixin(PanelMetaFunctions, detailsFramework.ScriptHookMixin)

--default options for the frame layout
---@class df_framelayout_options : table
---@field amount_per_line number? 4
---@field start_x number? 2
---@field start_y number? -2
---@field is_vertical boolean? true if vertical, false if horizontal
---@field grow_right boolean? true to grow right, false to grow left
---@field grow_down boolean? true to grow down, false to grow up
---@field anchor_to_child boolean? true to anchor to the previous frame instead of coordinate
---@field anchor_point df_framelayout_point? "topleft"
---@field anchor_relative df_framelayout_point? "topleft"
---@field offset_x number? 100
---@field offset_y number? 20
---@field width number? 0
---@field min_width number? 0
---@field height number? 0
---@field break_if_hidden boolean? true to stop if encounters a hidden frame
---@field use__width boolean? if true it'll use the __width from the widget as the offset_x

local default_framelayout_options = {
	amount_per_line = 4,
	start_x = 2,
	start_y = -2,
	is_vertical = false,
	grow_right = true, --on vertical (if not grow next line left)
	grow_down = true, --on horizontal (if not grow next line up)
	anchor_to_child = false, --if true set the point to the previous frame instead of coordinate
	anchor_point = "topleft",
	anchor_relative = "topleft",
	offset_x = 100,
	use__width = false, --__width from the widget
	offset_y = 20,
	width = 1,
	min_width = 0,
	height = 1,
	break_if_hidden = true, --stop if encounters a hidden frame
}

---@alias df_framelayout_point
---| "top"
---| "bottom"
---| "left"
---| "right"

---@class df_framelayout : table
---@field AnchorTo fun(self:uiobject, anchor:uiobject, point:df_framelayout_point, x:number?, y:number?)
---@field ArrangeFrames fun(self:uiobject, frameList:table<uiobject>[], options:df_framelayout_options?)


--mixin for frame layout
detailsFramework.LayoutFrame = {
	AnchorTo = function(self, anchor, point, x, y)
		if (point == "top") then
			self:ClearAllPoints()
			self:SetPoint("bottom", anchor, "top", x or 0, y or 0)

		elseif (point == "bottom") then
			self:ClearAllPoints()
			self:SetPoint("top", anchor, "bottom", x or 0, y or 0)

		elseif (point == "left") then
			self:ClearAllPoints()
			self:SetPoint("right", anchor, "left", x or 0, y or 0)

		elseif (point == "right") then
			self:ClearAllPoints()
			self:SetPoint("left", anchor, "right", x or 0, y or 0)
		end
	end,

	ArrangeFrames = function(self, frameList, options)
		if (not frameList) then
			frameList = {self:GetChildren()}
		end

		options = options or {}
		detailsFramework.table.deploy(options, default_framelayout_options)

		local breakLine = options.amount_per_line + 1
		local currentX, currentY = options.start_x, options.start_y
		local offsetX, offsetY = options.offset_x, options.offset_y
		local anchorPoint = options.anchor_point
		local anchorAt = options.anchor_relative
		local latestFrame = self
		local firstRowFrame = frameList[1]

		if (options.is_vertical) then
			for i = 1, #frameList do
				local thisFrame =  frameList[i]
				if (options.break_if_hidden and not thisFrame:IsShown()) then
					break
				end
				thisFrame:ClearAllPoints()

				if (options.anchor_to_child) then
					if (i == breakLine) then
						if (options.grow_right) then
							thisFrame:SetPoint("topleft", firstRowFrame, "topright", offsetX, 0)
						else
							thisFrame:SetPoint("topright", firstRowFrame, "topleft", -offsetX, 0)
						end
						firstRowFrame = thisFrame
						latestFrame = thisFrame
						breakLine = breakLine + options.amount_per_line
					else
						thisFrame:SetPoint(anchorPoint, latestFrame, i == 1 and "topleft" or anchorAt, offsetX, i == 1 and 0 or offsetY)
						latestFrame = thisFrame
					end
				else
					if (i == breakLine) then
						if (options.grow_right) then
							currentX = currentX + offsetX
						else
							currentX = currentX - offsetX
						end
						currentY = options.start_y

						firstRowFrame = thisFrame
						breakLine = breakLine + options.amount_per_line
					end

					thisFrame:SetPoint(anchorPoint, self, anchorAt, currentX, currentY)

					if (options.use__height) then --use the childframe.__width
						currentY = currentY - thisFrame.__height

					elseif (options.min_height) then
						currentY = currentY - math.max(options.min_height, offsetY)
					else
						currentY = currentY - offsetY
					end
				end
			end

		else
			for i = 1, #frameList do
				local thisFrame =  frameList[i]
				if (options.break_if_hidden and not thisFrame:IsShown()) then
					break
				end

				thisFrame:ClearAllPoints()

				if (options.anchor_to_child) then
					if (i == breakLine) then
						if (options.grow_down) then
							thisFrame:SetPoint("topleft", firstRowFrame, "bottomleft", 0, -offsetY)
						else
							thisFrame:SetPoint("bottomleft", firstRowFrame, "topleft", 0, offsetY)
						end
						firstRowFrame = thisFrame
						latestFrame = thisFrame
						breakLine = breakLine + options.amount_per_line
					else
						thisFrame:SetPoint(anchorPoint, latestFrame, i == 1 and "topleft" or anchorAt, i == 1 and 0 or offsetX, offsetY)
						latestFrame = thisFrame
					end
				else
					if (i == breakLine) then
						if (options.grow_down) then
							currentY = currentY - offsetY
						else
							currentY = currentY + offsetY
						end
						currentX = options.start_x

						firstRowFrame = thisFrame
						breakLine = breakLine + options.amount_per_line
					end

					thisFrame:SetPoint(anchorPoint, self, anchorAt, currentX, currentY)

					if (options.use__width) then --use the childframe.__width
						currentX = currentX + thisFrame.__width

					elseif (options.min_width) then
						currentX = currentX + math.max(options.min_width, offsetX)
					else
						currentX = currentX + offsetX
					end
				end
			end
		end
	end
}


------------------------------------------------------------------------------------------------------------
--metatables
	PanelMetaFunctions.__call = function(_table, value)
		--nothing to do
		return true
	end

------------------------------------------------------------------------------------------------------------
--members
	--tooltip
	local gmember_tooltip = function(_object)
		return _object:GetTooltip()
	end
	--shown
	local gmember_shown = function(_object)
		return _object:IsShown()
	end
	--backdrop color
	local gmember_color = function(_object)
		return _object.frame:GetBackdropColor()
	end
	--backdrop table
	local gmember_backdrop = function(_object)
		return _object.frame:GetBackdrop()
	end
	--frame width
	local gmember_width = function(_object)
		return _object.frame:GetWidth()
	end
	--frame height
	local gmember_height = function(_object)
		return _object.frame:GetHeight()
	end
	--locked
	local gmember_locked = function(_object)
		return rawget(_object, "is_locked")
	end

	PanelMetaFunctions.GetMembers = PanelMetaFunctions.GetMembers or {}
	PanelMetaFunctions.GetMembers ["tooltip"] = gmember_tooltip
	PanelMetaFunctions.GetMembers ["shown"] = gmember_shown
	PanelMetaFunctions.GetMembers ["color"] = gmember_color
	PanelMetaFunctions.GetMembers ["backdrop"] = gmember_backdrop
	PanelMetaFunctions.GetMembers ["width"] = gmember_width
	PanelMetaFunctions.GetMembers ["height"] = gmember_height
	PanelMetaFunctions.GetMembers ["locked"] = gmember_locked

	PanelMetaFunctions.__index = function(object, key)
		local func = PanelMetaFunctions.GetMembers[key]
		if (func) then
			return func(object, key)
		end

		local fromMe = rawget(object, key)
		if (fromMe) then
			return fromMe
		end

		return PanelMetaFunctions[key]
	end

	--tooltip
	local smember_tooltip = function(_object, _value)
		return _object:SetTooltip (_value)
	end
	--show
	local smember_show = function(_object, _value)
		if (_value) then
			return _object:Show()
		else
			return _object:Hide()
		end
	end
	--hide
	local smember_hide = function(_object, _value)
		if (not _value) then
			return _object:Show()
		else
			return _object:Hide()
		end
	end
	--backdrop color
	local smember_color = function(_object, _value)
		local _value1, _value2, _value3, _value4 = detailsFramework:ParseColors(_value)
		return _object:SetBackdropColor(_value1, _value2, _value3, _value4)
	end
	--frame width
	local smember_width = function(_object, _value)
		return _object.frame:SetWidth(_value)
	end
	--frame height
	local smember_height = function(_object, _value)
		return _object.frame:SetHeight(_value)
	end

	--locked
	local smember_locked = function(_object, _value)
		if (_value) then
			_object.frame:SetMovable(false)
			return rawset(_object, "is_locked", true)
		else
			_object.frame:SetMovable(true)
			rawset(_object, "is_locked", false)
			return
		end
	end

	--backdrop
	local smember_backdrop = function(_object, _value)
		return _object.frame:SetBackdrop(_value)
	end

	--close with right button
	local smember_right_close = function(_object, _value)
		return rawset(_object, "rightButtonClose", _value)
	end

	PanelMetaFunctions.SetMembers = PanelMetaFunctions.SetMembers or {}
	PanelMetaFunctions.SetMembers["tooltip"] = smember_tooltip
	PanelMetaFunctions.SetMembers["show"] = smember_show
	PanelMetaFunctions.SetMembers["hide"] = smember_hide
	PanelMetaFunctions.SetMembers["color"] = smember_color
	PanelMetaFunctions.SetMembers["backdrop"] = smember_backdrop
	PanelMetaFunctions.SetMembers["width"] = smember_width
	PanelMetaFunctions.SetMembers["height"] = smember_height
	PanelMetaFunctions.SetMembers["locked"] = smember_locked
	PanelMetaFunctions.SetMembers["close_with_right"] = smember_right_close

	PanelMetaFunctions.__newindex = function(_table, _key, _value)
		local func = PanelMetaFunctions.SetMembers [_key]
		if (func) then
			return func (_table, _value)
		else
			return rawset(_table, _key, _value)
		end
	end

------------------------------------------------------------------------------------------------------------
--methods

--right click to close
	function PanelMetaFunctions:CreateRightClickLabel(textType, width, height, showCloseText)
		local text
		width = width or 20
		height = height or 20

		if (showCloseText) then
			text = showCloseText
		else
			if (textType) then
				textType = string.lower(textType)
				if (textType == "short") then
					text = "close window"

				elseif (textType == "medium") then
					text = "close window"

				elseif (textType == "large") then
					text = "close window"
				end
			else
				text = "close window"
			end
		end

		return detailsFramework:NewLabel(self, _, "$parentRightMouseToClose", nil, "|TInterface\\TUTORIALFRAME\\UI-TUTORIAL-FRAME:" .. width .. ":" .. height .. ":0:1:512:512:8:70:328:409|t " .. text)
	end

--show & hide
	function PanelMetaFunctions:Show()
		self.frame:Show()
	end

	function PanelMetaFunctions:Hide()
		self.frame:Hide()
	end

-- setpoint
	function PanelMetaFunctions:SetPoint(v1, v2, v3, v4, v5)
		v1, v2, v3, v4, v5 = detailsFramework:CheckPoints (v1, v2, v3, v4, v5, self)
		if (not v1) then
			print("Invalid parameter for SetPoint")
			return
		end
		return self.widget:SetPoint(v1, v2, v3, v4, v5)
	end

-- sizes
	function PanelMetaFunctions:SetSize(w, h)
		if (w) then
			self.frame:SetWidth(w)
		end
		if (h) then
			self.frame:SetHeight(h)
		end
	end

-- clear
	function PanelMetaFunctions:HideWidgets()
		for widgetName, widgetSelf in pairs(self) do
			if (type(widgetSelf) == "table" and widgetSelf.dframework) then
				widgetSelf:Hide()
			end
		end
	end

-- backdrop
	function PanelMetaFunctions:SetBackdrop(background, edge, tilesize, edgesize, tile, left, right, top, bottom)

		if (type(background) == "boolean" and not background) then
			return self.frame:SetBackdrop(nil)

		elseif (type(background) == "table") then
			self.frame:SetBackdrop(background)

		else
			local currentBackdrop = self.frame:GetBackdrop() or {edgeFile="Interface\\DialogFrame\\UI-DialogBox-Border", bgFile="Interface\\DialogFrame\\UI-DialogBox-Background", tile=true, tileSize=16, edgeSize=16, insets={left=1, right=0, top=0, bottom=0}}
			currentBackdrop.bgFile = background or currentBackdrop.bgFile
			currentBackdrop.edgeFile = edgeFile or currentBackdrop.edgeFile
			currentBackdrop.tileSize = tilesize or currentBackdrop.tileSize
			currentBackdrop.edgeSize = edgesize or currentBackdrop.edgeSize
			currentBackdrop.tile = tile or currentBackdrop.tile
			currentBackdrop.insets.left = left or currentBackdrop.insets.left
			currentBackdrop.insets.right = left or currentBackdrop.insets.right
			currentBackdrop.insets.top = left or currentBackdrop.insets.top
			currentBackdrop.insets.bottom = left or currentBackdrop.insets.bottom
			self.frame:SetBackdrop(currentBackdrop)
		end
	end

-- backdropcolor
	function PanelMetaFunctions:SetBackdropColor(color, arg2, arg3, arg4)
		if (arg2) then
			self.frame:SetBackdropColor(color, arg2, arg3, arg4 or 1)
		else
			local _value1, _value2, _value3, _value4 = detailsFramework:ParseColors(color)
			self.frame:SetBackdropColor(_value1, _value2, _value3, _value4)
		end
	end

-- border color
	function PanelMetaFunctions:SetBackdropBorderColor(color, arg2, arg3, arg4)
		if (arg2) then
			return self.frame:SetBackdropBorderColor(color, arg2, arg3, arg4)
		end
		local _value1, _value2, _value3, _value4 = detailsFramework:ParseColors(color)
		self.frame:SetBackdropBorderColor(_value1, _value2, _value3, _value4)
	end

-- tooltip
	function PanelMetaFunctions:SetTooltip (tooltip)
		if (tooltip) then
			return rawset(self, "have_tooltip", tooltip)
		else
			return rawset(self, "have_tooltip", nil)
		end
	end
	function PanelMetaFunctions:GetTooltip()
		return rawget(self, "have_tooltip")
	end

-- frame levels
	function PanelMetaFunctions:GetFrameLevel()
		return self.widget:GetFrameLevel()
	end
	function PanelMetaFunctions:SetFrameLevel(level, frame)
		if (not frame) then
			return self.widget:SetFrameLevel(level)
		else
			local framelevel = frame:GetFrameLevel (frame) + level
			return self.widget:SetFrameLevel(framelevel)
		end
	end

-- frame stratas
	function PanelMetaFunctions:GetFrameStrata()
		return self.widget:GetFrameStrata()
	end
	function PanelMetaFunctions:SetFrameStrata(strata)
		if (type(strata) == "table") then
			self.widget:SetFrameStrata(strata:GetFrameStrata())
		else
			self.widget:SetFrameStrata(strata)
		end
	end

------------------------------------------------------------------------------------------------------------
--scripts

	local OnEnter = function(frame)
		local capsule = frame.MyObject
		local kill = capsule:RunHooksForWidget("OnEnter", frame, capsule)
		if (kill) then
			return
		end

		if (frame.MyObject.have_tooltip) then
			GameCooltip2:Reset()
			GameCooltip2:SetType ("tooltip")
			GameCooltip2:SetColor ("main", "transparent")
			GameCooltip2:AddLine(frame.MyObject.have_tooltip)
			GameCooltip2:SetOwner(frame)
			GameCooltip2:ShowCooltip()
		end
	end

	local OnLeave = function(frame)
		local capsule = frame.MyObject
		local kill = capsule:RunHooksForWidget("OnLeave", frame, capsule)
		if (kill) then
			return
		end

		if (frame.MyObject.have_tooltip) then
			GameCooltip2:ShowMe(false)
		end

	end

	local OnHide = function(frame)
		local capsule = frame.MyObject
		local kill = capsule:RunHooksForWidget("OnHide", frame, capsule)
		if (kill) then
			return
		end
	end

	local OnShow = function(frame)
		local capsule = frame.MyObject
		local kill = capsule:RunHooksForWidget("OnShow", frame, capsule)
		if (kill) then
			return
		end
	end

	local OnMouseDown = function(frame, button)
		local capsule = frame.MyObject
		local kill = capsule:RunHooksForWidget("OnMouseDown", frame, button, capsule)
		if (kill) then
			return
		end

		if (frame.MyObject.container == UIParent) then
			if (not frame.isLocked and frame:IsMovable()) then
				frame.isMoving = true
				frame:StartMoving()
			end

		elseif (not frame.MyObject.container.isLocked and frame.MyObject.container:IsMovable()) then
			if (not frame.isLocked and frame:IsMovable()) then
				frame.MyObject.container.isMoving = true
				frame.MyObject.container:StartMoving()
			end
		end


	end

	local OnMouseUp = function(frame, button)
		local capsule = frame.MyObject
		local kill = capsule:RunHooksForWidget("OnMouseUp", frame, button, capsule)
		if (kill) then
			return
		end

		if (button == "RightButton" and frame.MyObject.rightButtonClose) then
			frame.MyObject:Hide()
		end

		if (frame.MyObject.container == UIParent) then
			if (frame.isMoving) then
				frame:StopMovingOrSizing()
				frame.isMoving = false
			end
		else
			if (frame.MyObject.container.isMoving) then
				frame.MyObject.container:StopMovingOrSizing()
				frame.MyObject.container.isMoving = false
			end
		end
	end

------------------------------------------------------------------------------------------------------------
--object constructor
function detailsFramework:CreatePanel (parent, w, h, backdrop, backdropcolor, bordercolor, member, name)
	return detailsFramework:NewPanel(parent, parent, name, member, w, h, backdrop, backdropcolor, bordercolor)
end

function detailsFramework:NewPanel(parent, container, name, member, w, h, backdrop, backdropcolor, bordercolor)

	if (not name) then
		name = "DetailsFrameworkPanelNumber" .. detailsFramework.PanelCounter
		detailsFramework.PanelCounter = detailsFramework.PanelCounter + 1

	elseif (not parent) then
		parent = UIParent
	end
	if (not container) then
		container = parent
	end

	if (name:find("$parent")) then
		name = name:gsub("$parent", parent:GetName())
	end

	local PanelObject = {type = "panel", dframework = true}

	if (member) then
		parent [member] = PanelObject
	end

	if (parent.dframework) then
		parent = parent.widget
	end
	if (container.dframework) then
		container = container.widget
	end

	--default members:
		--misc
		PanelObject.is_locked = true
		PanelObject.container = container
		PanelObject.rightButtonClose = false

	PanelObject.frame = CreateFrame("frame", name, parent,"BackdropTemplate")
	PanelObject.frame:SetSize(100, 100)
	PanelObject.frame.Gradient = {
					["OnEnter"] = {0.3, 0.3, 0.3, 0.5},
					["OnLeave"] = {0.9, 0.7, 0.7, 1}
	}
	PanelObject.frame:SetBackdrop({bgFile = [[Interface\DialogFrame\UI-DialogBox-Background]], edgeFile = "Interface\\DialogFrame\\UI-DialogBox-Border", edgeSize = 10, tileSize = 64, tile = true})

	PanelObject.widget = PanelObject.frame
	PanelObject.frame.MyObject = PanelObject

	if (not APIFrameFunctions) then
		APIFrameFunctions = {}
		local idx = getmetatable(PanelObject.frame).__index
		for funcName, funcAddress in pairs(idx) do
			if (not PanelMetaFunctions [funcName]) then
				PanelMetaFunctions [funcName] = function(object, ...)
					local x = loadstring ( "return _G['"..object.frame:GetName().."']:"..funcName.."(...)")
					return x (...)
				end
			end
		end
	end

	PanelObject.frame:SetWidth(w or 100)
	PanelObject.frame:SetHeight(h or 100)

	PanelObject.HookList = {
		OnEnter = {},
		OnLeave = {},
		OnHide = {},
		OnShow = {},
		OnMouseDown = {},
		OnMouseUp = {},
	}

	--hooks
		PanelObject.frame:SetScript("OnEnter", OnEnter)
		PanelObject.frame:SetScript("OnLeave", OnLeave)
		PanelObject.frame:SetScript("OnHide", OnHide)
		PanelObject.frame:SetScript("OnShow", OnShow)
		PanelObject.frame:SetScript("OnMouseDown", OnMouseDown)
		PanelObject.frame:SetScript("OnMouseUp", OnMouseUp)

	setmetatable(PanelObject, PanelMetaFunctions)

	if (backdrop) then
		PanelObject:SetBackdrop(backdrop)
	elseif (type(backdrop) == "boolean") then
		PanelObject.frame:SetBackdrop(nil)
	end

	if (backdropcolor) then
		PanelObject:SetBackdropColor(backdropcolor)
	end

	if (bordercolor) then
		PanelObject:SetBackdropBorderColor(bordercolor)
	end

	return PanelObject
end

------------fill panel

local button_on_enter = function(self)
	self.MyObject._icon:SetBlendMode("ADD")
	if (self.MyObject.onenter_func) then
		pcall(self.MyObject.onenter_func, self.MyObject)
	end
end
local button_on_leave = function(self)
	self.MyObject._icon:SetBlendMode("BLEND")
	if (self.MyObject.onleave_func) then
		pcall(self.MyObject.onleave_func, self.MyObject)
	end
end

local add_row = function(self, t, need_update)
	local index = #self.rows+1

	local thisrow = detailsFramework:NewPanel(self, self, "$parentHeader_" .. self._name .. index, nil, 1, 20)
	thisrow.backdrop = {bgFile = [[Interface\Tooltips\UI-Tooltip-Background]]}
	thisrow.color = {.3, .3, .3, .9}
	thisrow.type = t.type
	thisrow.func = t.func
	thisrow.name = t.name
	thisrow.notext = t.notext
	thisrow.icon = t.icon
	thisrow.iconalign = t.iconalign

	thisrow.hidden = t.hidden or false

	thisrow.onenter = t.onenter
	thisrow.onleave = t.onleave

	local text = detailsFramework:NewLabel(thisrow, nil, self._name .. "$parentLabel" .. index, "text")
	text:SetPoint("left", thisrow, "left", 2, 0)
	text:SetText(t.name)

	table.insert(self._raw_rows, t)
	table.insert(self.rows, thisrow)

	if (need_update) then
		self:AlignRows()
	end
end

local align_rows = function(self)

	local rows_shown = 0
	for index, row in ipairs(self.rows) do
		if (not row.hidden) then
			rows_shown = rows_shown + 1
		end
	end

	local cur_width = 1
	local row_width = self._width / max(rows_shown, 0.0001)


	local sindex = 1

	wipe (self._anchors)

	for index, row in ipairs(self.rows) do
		if (not row.hidden) then
			if (self._autowidth) then
				if (self._raw_rows [index].width) then
					row.width = self._raw_rows [index].width
				else
					row.width = row_width
				end
				row:SetPoint("topleft", self, "topleft", cur_width, -1)
				table.insert(self._anchors, cur_width)
				cur_width = cur_width + row_width + 1
			else
				row:SetPoint("topleft", self, "topleft", cur_width, -1)
				row.width = self._raw_rows [index].width
				table.insert(self._anchors, cur_width)
				cur_width = cur_width + self._raw_rows [index].width + 1
			end

			row:Show()

			local rowType = row.type

			if (rowType == "text") then
				for i = 1, #self.scrollframe.lines do
					local line = self.scrollframe.lines [i]
					local text = table.remove(line.text_available)
					if (not text) then
						self:CreateRowText (line)
						text = table.remove(line.text_available)
					end
					table.insert(line.text_inuse, text)
					text:SetPoint("left", line, "left", self._anchors [#self._anchors], 0)
					text:SetWidth(row.width)

					detailsFramework:SetFontSize(text, row.textsize or 10)
					text:SetJustifyH(row.textalign or "left")
				end
			elseif (rowType == "entry") then
				for i = 1, #self.scrollframe.lines do
					local line = self.scrollframe.lines [i]
					local entry = table.remove(line.entry_available)
					if (not entry) then
						self:CreateRowEntry (line)
						entry = table.remove(line.entry_available)
					end
					table.insert(line.entry_inuse, entry)
					entry:SetPoint("left", line, "left", self._anchors [#self._anchors], 0)
					if (sindex == rows_shown) then
						entry:SetWidth(row.width - 25)
					else
						entry:SetWidth(row.width)
					end
					entry.func = row.func

					entry.onenter_func = nil
					entry.onleave_func = nil

					if (row.onenter) then
						entry.onenter_func = row.onenter
					end
					if (row.onleave) then
						entry.onleave_func = row.onleave
					end
				end

			elseif (rowType == "checkbox") then
				for i = 1, #self.scrollframe.lines do
					local line = self.scrollframe.lines [i]
					local checkbox = table.remove(line.checkbox_available)
					if (not checkbox) then
						self:CreateCheckbox (line)
						checkbox = table.remove(line.checkbox_available)
					end

					table.insert(line.checkbox_inuse, checkbox)

					checkbox:SetPoint("left", line, "left", self._anchors [#self._anchors] + ((row.width - 20) / 2), 0)
					if (sindex == rows_shown) then
						checkbox:SetWidth(20)
						--checkbox:SetWidth(row.width - 25)
					else
						checkbox:SetWidth(20)
					end

					checkbox.onenter_func = nil
					checkbox.onleave_func = nil
				end

			elseif (rowType == "button") then
				for i = 1, #self.scrollframe.lines do
					local line = self.scrollframe.lines [i]
					local button = table.remove(line.button_available)
					if (not button) then
						self:CreateRowButton (line)
						button = table.remove(line.button_available)
					end
					table.insert(line.button_inuse, button)
					button:SetPoint("left", line, "left", self._anchors [#self._anchors], 0)
					if (sindex == rows_shown) then
						button:SetWidth(row.width - 25)
					else
						button:SetWidth(row.width)
					end

					if (row.icon) then
						button._icon.texture = row.icon
						button._icon:ClearAllPoints()
						if (row.iconalign) then
							if (row.iconalign == "center") then
								button._icon:SetPoint("center", button, "center")
							elseif (row.iconalign == "right") then
								button._icon:SetPoint("right", button, "right")
							end
						else
							button._icon:SetPoint("left", button, "left")
						end
					end

					if (row.name and not row.notext) then
						button._text:SetPoint("left", button._icon, "right", 2, 0)
						button._text.text = row.name
					end

					button.onenter_func = nil
					button.onleave_func = nil

					if (row.onenter) then
						button.onenter_func = row.onenter
					end
					if (row.onleave) then
						button.onleave_func = row.onleave
					end

				end
			elseif (rowType == "icon") then
				for i = 1, #self.scrollframe.lines do
					local line = self.scrollframe.lines [i]
					local icon = table.remove(line.icon_available)
					if (not icon) then
						self:CreateRowIcon (line)
						icon = table.remove(line.icon_available)
					end
					table.insert(line.icon_inuse, icon)
					icon:SetPoint("left", line, "left", self._anchors [#self._anchors] + ( ((row.width or 22) - 22) / 2), 0)
					icon.func = row.func
				end

			elseif (rowType == "texture") then
				for i = 1, #self.scrollframe.lines do
					local line = self.scrollframe.lines [i]
					local texture = table.remove(line.texture_available)
					if (not texture) then
						self:CreateRowTexture (line)
						texture = table.remove(line.texture_available)
					end
					table.insert(line.texture_inuse, texture)
					texture:SetPoint("left", line, "left", self._anchors [#self._anchors] + ( ((row.width or 22) - 22) / 2), 0)
				end

			end

			sindex = sindex + 1
		else
			row:Hide()
		end
	end

	if (#self.rows > 0) then
		if (self._autowidth) then
			self.rows [#self.rows]:SetWidth(row_width - rows_shown + 1)
		else
			self.rows [#self.rows]:SetWidth(self._raw_rows [rows_shown].width - rows_shown + 1)
		end
	end

	self.showing_amt = rows_shown
end

local update_rows = function(self, updated_rows)

	for i = 1, #updated_rows do
		local t = updated_rows [i]
		local raw = self._raw_rows [i]

		if (not raw) then
			self:AddRow (t)
		else
			raw.name = t.name
			raw.hidden = t.hidden or false
			raw.textsize = t.textsize
			raw.textalign = t.textalign

			local widget = self.rows [i]
			widget.name = t.name
			widget.textsize = t.textsize
			widget.textalign = t.textalign
			widget.hidden = t.hidden or false

			--
			widget.onenter = t.onenter
			widget.onleave = t.onleave
			--

			widget.text:SetText(t.name)
			detailsFramework:SetFontSize(widget.text, raw.textsize or 10)
			widget.text:SetJustifyH(raw.textalign or "left")
		end
	end

	for i = #updated_rows+1, #self._raw_rows do
		local raw = self._raw_rows [i]
		local widget = self.rows [i]
		raw.hidden = true
		widget.hidden = true
	end

	for index, row in ipairs(self.scrollframe.lines) do
		for i = #row.text_inuse, 1, -1 do
			table.insert(row.text_available, table.remove(row.text_inuse, i))
		end
		for i = 1, #row.text_available do
			row.text_available[i]:Hide()
		end

		for i = #row.entry_inuse, 1, -1 do
			table.insert(row.entry_available, table.remove(row.entry_inuse, i))
		end
		for i = 1, #row.entry_available do
			row.entry_available[i]:Hide()
		end

		for i = #row.button_inuse, 1, -1 do
			table.insert(row.button_available, table.remove(row.button_inuse, i))
		end
		for i = 1, #row.button_available do
			row.button_available[i]:Hide()
		end

		for i = #row.checkbox_inuse, 1, -1 do
			table.insert(row.checkbox_available, table.remove(row.checkbox_inuse, i))
		end
		for i = 1, #row.checkbox_available do
			row.checkbox_available[i]:Hide()
		end

		for i = #row.icon_inuse, 1, -1 do
			table.insert(row.icon_available, table.remove(row.icon_inuse, i))
		end
		for i = 1, #row.icon_available do
			row.icon_available[i]:Hide()
		end

		for i = #row.texture_inuse, 1, -1 do
			table.insert(row.texture_available, table.remove(row.texture_inuse, i))
		end
		for i = 1, #row.texture_available do
			row.texture_available[i]:Hide()
		end
	end

	self.current_header = updated_rows

	self:AlignRows()

end

local create_panel_text = function(self, row)
	row.text_total = row.text_total + 1
	local text = detailsFramework:NewLabel(row, nil, self._name .. "$parentLabel" .. row.text_total, "text" .. row.text_total)
	table.insert(row.text_available, text)
end

local create_panel_entry = function(self, row)
	row.entry_total = row.entry_total + 1
	local editbox = detailsFramework:NewTextEntry(row, nil, "$parentEntry" .. row.entry_total, "entry", 120, 20)
	editbox.align = "left"

	editbox:SetHook("OnEnterPressed", function()
		editbox.widget.focuslost = true
		editbox:ClearFocus()
		editbox.func (editbox.index, editbox.text)
		return true
	end)

	editbox:SetHook("OnEnter", function()
		if (editbox.onenter_func) then
			pcall(editbox.onenter_func, editbox)
		end
	end)
	editbox:SetHook("OnLeave", function()
		if (editbox.onleave_func) then
			pcall(editbox.onleave_func, editbox)
		end
	end)

	editbox.editbox.current_bordercolor = {1, 1, 1, 0.1}

	editbox:SetTemplate(detailsFramework:GetTemplate("dropdown", "OPTIONS_DROPDOWN_TEMPLATE"))
	editbox:SetBackdropColor(.2, .2, .2, 0.7)

	table.insert(row.entry_available, editbox)
end

local create_panel_checkbox = function(self, row)
	--row.checkbox_available
	row.checkbox_total = row.checkbox_total + 1

	local switch = detailsFramework:NewSwitch (row, nil, "$parentCheckBox" .. row.checkbox_total, nil, 20, 20, nil, nil, false)
	switch:SetAsCheckBox()
	switch:SetTemplate(detailsFramework:GetTemplate("switch", "OPTIONS_CHECKBOX_TEMPLATE"))

	table.insert(row.checkbox_available, switch)
end

local create_panel_button = function(self, row)
	row.button_total = row.button_total + 1
	local button = detailsFramework:NewButton(row, nil, "$parentButton" .. row.button_total, "button" .. row.button_total, 120, 20)

	--create icon and the text
	local icon = detailsFramework:NewImage(button, nil, 20, 20)
	local text = detailsFramework:NewLabel(button)

	button._icon = icon
	button._text = text

	button:SetHook("OnEnter", button_on_enter)
	button:SetHook("OnLeave", button_on_leave)

	table.insert(row.button_available, button)
end

local icon_onclick = function(texture, iconbutton)
	iconbutton._icon.texture = texture
	iconbutton.func (iconbutton.index, texture)
end

local create_panel_icon = function(self, row)
	row.icon_total = row.icon_total + 1
	local iconbutton = detailsFramework:NewButton(row, nil, "$parentIconButton" .. row.icon_total, "iconbutton", 22, 20)

	iconbutton:SetHook("OnEnter", button_on_enter)
	iconbutton:SetHook("OnLeave", button_on_leave)

	iconbutton:SetHook("OnMouseUp", function()
		detailsFramework:IconPick (icon_onclick, true, iconbutton)
		return true
	end)

	local icon = detailsFramework:NewImage(iconbutton, nil, 20, 20, "artwork", nil, "_icon", "$parentIcon" .. row.icon_total)
	iconbutton._icon = icon

	icon:SetPoint("center", iconbutton, "center", 0, 0)

	table.insert(row.icon_available, iconbutton)
end

local create_panel_texture = function(self, row)
	row.texture_total = row.texture_total + 1
	local texture = detailsFramework:NewImage(row, nil, 20, 20, "artwork", nil, "_icon" .. row.texture_total, "$parentIcon" .. row.texture_total)
	table.insert(row.texture_available, texture)
end

local set_fill_function = function(self, func)
	self._fillfunc = func
end
local set_total_function = function(self, func)
	self._totalfunc = func
end
local drop_header_function = function(self)
	wipe (self.rows)
end

local fillpanel_update_size = function(self, elapsed)
	local panel = self.MyObject

	panel._width = panel:GetWidth()
	panel._height = panel:GetHeight()

	panel:UpdateRowAmount()
	if (panel.current_header) then
		update_rows (panel, panel.current_header)
	end
	panel:Refresh()

	self:SetScript("OnUpdate", nil)
end

 -- ~fillpanel
  --alias
function detailsFramework:CreateFillPanel(parent, rows, w, h, total_lines, fill_row, autowidth, options, member, name)
	return detailsFramework:NewFillPanel(parent, rows, name, member, w, h, total_lines, fill_row, autowidth, options)
end

function detailsFramework:NewFillPanel(parent, rows, name, member, w, h, total_lines, fill_row, autowidth, options)
	local panel = detailsFramework:NewPanel(parent, parent, name, member, w, h)
	panel.backdrop = nil

	options = options or {rowheight = 20}
	panel.rows = {}

	panel.AddRow = add_row
	panel.AlignRows = align_rows
	panel.UpdateRows = update_rows
	panel.CreateRowText = create_panel_text
	panel.CreateRowEntry = create_panel_entry
	panel.CreateRowButton = create_panel_button
	panel.CreateCheckbox = create_panel_checkbox
	panel.CreateRowIcon = create_panel_icon
	panel.CreateRowTexture = create_panel_texture
	panel.SetFillFunction = set_fill_function
	panel.SetTotalFunction = set_total_function
	panel.DropHeader = drop_header_function

	panel._name = name
	panel._width = w
	panel._height = h
	panel._raw_rows = {}
	panel._anchors = {}
	panel._fillfunc = fill_row
	panel._totalfunc = total_lines
	panel._autowidth = autowidth

	panel:SetScript("OnSizeChanged", function()
		panel:SetScript("OnUpdate", fillpanel_update_size)
	end)

	for index, t in ipairs(rows) do
		panel.AddRow(panel, t)
	end

	local refresh_fillbox = function(self)
		local offset = FauxScrollFrame_GetOffset(self)
		local filled_lines = panel._totalfunc(panel)

		for index = 1, #self.lines do
			local row = self.lines [index]
			if (index <= filled_lines) then

				local real_index = index + offset
				local results = panel._fillfunc (real_index, panel)

				if (results and results [1]) then
					row:Show()

					local text, entry, button, icon, texture, checkbox = 1, 1, 1, 1, 1, 1

					for index, t in ipairs(panel.rows) do
						if (not t.hidden) then
							if (t.type == "text") then
								local fontstring = row.text_inuse [text]
								text = text + 1
								fontstring:SetText(results [index])
								fontstring.index = real_index
								fontstring:Show()

							elseif (t.type == "entry") then
								local entrywidget = row.entry_inuse [entry]
								entry = entry + 1
								entrywidget.index = real_index

								if (type(results [index]) == "table") then
									entrywidget:SetText(results [index].text)
									entrywidget.id = results [index].id
									entrywidget.data1 = results [index].data1
									entrywidget.data2 = results [index].data2
								else
									entrywidget:SetText(results [index])
								end

								entrywidget:SetCursorPosition(0)

								entrywidget:Show()

							elseif (t.type == "checkbox") then
								local checkboxwidget = row.checkbox_inuse [button]
								checkbox = checkbox + 1
								checkboxwidget.index = real_index
								checkboxwidget:SetValue(results [index])

								local func = function()
									t.func (real_index, index)
									panel:Refresh()
								end
								checkboxwidget.OnSwitch = func

							elseif (t.type == "button") then
								local buttonwidget = row.button_inuse [button]
								button = button + 1
								buttonwidget.index = real_index

								if (type(results [index]) == "table") then
									if (results [index].text) then
										buttonwidget:SetText(results [index].text)
									end

									if (results [index].icon) then
										buttonwidget._icon:SetTexture(results [index].icon)
									end

									if (results [index].func) then
										local func = function()
											t.func (real_index, results [index].value)
											panel:Refresh()
										end
										buttonwidget:SetClickFunction(func)
									else
										local func = function()
											t.func (real_index, index)
											panel:Refresh()
										end
										buttonwidget:SetClickFunction(func)
									end

									buttonwidget.id = results [index].id
									buttonwidget.data1 = results [index].data1
									buttonwidget.data2 = results [index].data2

								else
									local func = function()
										t.func (real_index, index)
										panel:Refresh()
									end
									buttonwidget:SetClickFunction(func)
									buttonwidget:SetText(results [index])
								end

								buttonwidget:Show()

							elseif (t.type == "icon") then
								local iconwidget = row.icon_inuse [icon]
								icon = icon + 1

								iconwidget.line = index
								iconwidget.index = real_index

								if (type(results [index]) == "string") then
									local result = results [index]:gsub(".-%\\", "")
									iconwidget._icon.texture = results [index]
									iconwidget._icon:SetTexCoord(0.1, .9, 0.1, .9)

								elseif (type(results [index]) == "table") then
									iconwidget._icon:SetTexture(results [index].texture)

									local textCoord = results [index].texcoord
									if (textCoord) then
										iconwidget._icon:SetTexCoord(unpack(textCoord))
									else
										iconwidget._icon:SetTexCoord(0.1, .9, 0.1, .9)
									end

									local color = results [index].color
									if (color) then
										local r, g, b, a = detailsFramework:ParseColors(color)
										iconwidget._icon:SetVertexColor(r, g, b, a)
									else
										iconwidget._icon:SetVertexColor(1, 1, 1, 1)
									end
								else
									iconwidget._icon:SetTexture(results [index])
									iconwidget._icon:SetTexCoord(0.1, .9, 0.1, .9)
								end

								iconwidget:Show()

							elseif (t.type == "texture") then
								local texturewidget = row.texture_inuse [texture]
								texture = texture + 1

								texturewidget.line = index
								texturewidget.index = real_index

								if (type(results [index]) == "string") then
									local result = results [index]:gsub(".-%\\", "")
									texturewidget.texture = results [index]

								elseif (type(results [index]) == "table") then
									texturewidget:SetTexture(results [index].texture)

									local textCoord = results [index].texcoord
									if (textCoord) then
										texturewidget:SetTexCoord(unpack(textCoord))
									else
										texturewidget:SetTexCoord(0, 1, 0, 1)
									end

									local color = results [index].color
									if (color) then
										local r, g, b, a = detailsFramework:ParseColors(color)
										texturewidget:SetVertexColor(r, g, b, a)
									else
										texturewidget:SetVertexColor(1, 1, 1, 1)
									end

								else
									texturewidget:SetTexture(results [index])
								end

								texturewidget:Show()
							end
						end
					end

				else
					row:Hide()
				end
			else
				row:Hide()
			end
		end
	end

	function panel:Refresh()
		if (type(panel._totalfunc) == "boolean") then
			--not yet initialized
			return
		end
		local filled_lines = panel._totalfunc (panel)
		local scroll_total_lines = #panel.scrollframe.lines
		local line_height = options.rowheight
		refresh_fillbox (panel.scrollframe)
		FauxScrollFrame_Update (panel.scrollframe, filled_lines, scroll_total_lines, line_height)
		panel.scrollframe:Show()
	end

	local scrollframe = CreateFrame("scrollframe", name .. "Scroll", panel.widget, "FauxScrollFrameTemplate", "BackdropTemplate")
	scrollframe:SetScript("OnVerticalScroll", function(self, offset) FauxScrollFrame_OnVerticalScroll (self, offset, 20, panel.Refresh) end)
	scrollframe:SetPoint("topleft", panel.widget, "topleft", 0, -21)
	scrollframe:SetPoint("topright", panel.widget, "topright", -23, -21)
	scrollframe:SetPoint("bottomleft", panel.widget, "bottomleft")
	scrollframe:SetPoint("bottomright", panel.widget, "bottomright", -23, 0)
	scrollframe:SetSize(w, h)
	panel.scrollframe = scrollframe
	scrollframe.lines = {}

	detailsFramework:ReskinSlider(scrollframe)

	--create lines
	function panel:UpdateRowAmount()
		local size = options.rowheight
		local amount = math.floor(((panel._height-21) / size))

		for i = #scrollframe.lines+1, amount do
			local row = CreateFrame("frame", panel:GetName() .. "Row_" .. i, panel.widget,"BackdropTemplate")
			row:SetSize(1, size)
			row.color = {1, 1, 1, .2}

			row:SetBackdrop({bgFile = [[Interface\Tooltips\UI-Tooltip-Background]]})

			if (i%2 == 0) then
				row:SetBackdropColor(.5, .5, .5, 0.2)
			else
				row:SetBackdropColor(1, 1, 1, 0.00)
			end

			row:SetPoint("topleft", scrollframe, "topleft", 0, (i-1) * size * -1)
			row:SetPoint("topright", scrollframe, "topright", 0, (i-1) * size * -1)
			table.insert(scrollframe.lines, row)

			row.text_available = {}
			row.text_inuse = {}
			row.text_total = 0

			row.entry_available = {}
			row.entry_inuse = {}
			row.entry_total = 0

			row.button_available = {}
			row.button_inuse = {}
			row.button_total = 0

			row.checkbox_available = {}
			row.checkbox_inuse = {}
			row.checkbox_total = 0

			row.icon_available = {}
			row.icon_inuse = {}
			row.icon_total = 0

			row.texture_available = {}
			row.texture_inuse = {}
			row.texture_total = 0
		end
	end
	panel:UpdateRowAmount()

	panel.AlignRows (panel)

	return panel
end


------------color pick
local _, _, _, toc = GetBuildInfo()
local ColorPickerFrame = ColorPickerFrame --stop warnings

if ((ColorPickerFrame and ColorPickerFrame.SetupColorPickerAndShow) or toc >= 100205) then -- maybe fallback to only check CPF in the future
	local color_pick_func = function(...)
		local r, g, b = ColorPickerFrame:GetColorRGB()
		local a = ColorPickerFrame:GetColorAlpha()
		ColorPickerFrame:dcallback (r, g, b, a, ColorPickerFrame.dframe)
	end

	local color_pick_func_cancel = function()
		local r, g, b, a = ColorPickerFrame.previousValues.r, ColorPickerFrame.previousValues.g, ColorPickerFrame.previousValues.b, ColorPickerFrame.previousValues.a
		ColorPickerFrame:dcallback (r, g, b, a, ColorPickerFrame.dframe)
	end

	function detailsFramework:ColorPick(frame, r, g, b, alpha, callback)

		ColorPickerFrame:ClearAllPoints()
		ColorPickerFrame:SetPoint("bottomleft", frame, "topright", 0, 0)

		ColorPickerFrame.dcallback = callback
		ColorPickerFrame.dframe = frame

		ColorPickerFrame.func = color_pick_func
		ColorPickerFrame.opacityFunc = color_pick_func
		ColorPickerFrame.cancelFunc = color_pick_func_cancel

		ColorPickerFrame.opacity = alpha
		ColorPickerFrame.hasOpacity = alpha and true

		ColorPickerFrame.previousValues = {r, g, b}
		ColorPickerFrame.previousAlpha = alpha
		ColorPickerFrame:SetParent(UIParent)
		ColorPickerFrame:SetFrameStrata("tooltip")

		local info = {
			swatchFunc = color_pick_func,
			hasOpacity = alpha and true,
			opacityFunc = color_pick_func,
			opacity = alpha,
			previousValues = {r = r, g = g, b = b, a = alpha},
			cancelFunc = color_pick_func_cancel,
			r = r,
			g = g,
			b = b,
		}
		--OpenColorPicker(info)
		ColorPickerFrame:SetupColorPickerAndShow(info)

	end
else
	local color_pick_func = function()
		local r, g, b = ColorPickerFrame:GetColorRGB()
		local a = OpacitySliderFrame:GetValue()
		a = math.abs(a - 1)
		ColorPickerFrame:dcallback (r, g, b, a, ColorPickerFrame.dframe)
	end
	local color_pick_func_cancel = function()
		ColorPickerFrame:SetColorRGB (unpack(ColorPickerFrame.previousValues))
		local r, g, b = ColorPickerFrame:GetColorRGB()
		local a = OpacitySliderFrame:GetValue()
		a = math.abs(a - 1)
		ColorPickerFrame:dcallback (r, g, b, a, ColorPickerFrame.dframe)
	end

	function detailsFramework:ColorPick (frame, r, g, b, alpha, callback)

		ColorPickerFrame:ClearAllPoints()
		ColorPickerFrame:SetPoint("bottomleft", frame, "topright", 0, 0)

		ColorPickerFrame.dcallback = callback
		ColorPickerFrame.dframe = frame

		ColorPickerFrame.func = color_pick_func
		ColorPickerFrame.opacityFunc = color_pick_func
		ColorPickerFrame.cancelFunc = color_pick_func_cancel

		alpha = math.abs(alpha - 1)
		ColorPickerFrame.opacity = alpha
		ColorPickerFrame.hasOpacity = alpha and true

		ColorPickerFrame.previousValues = {r, g, b}
		ColorPickerFrame:SetParent(UIParent)
		ColorPickerFrame:SetFrameStrata("tooltip")
		ColorPickerFrame:SetColorRGB (r, g, b)
		ColorPickerFrame:Show()
	end
end





------------icon pick
function detailsFramework:IconPick (callback, close_when_select, param1, param2)

	if (not detailsFramework.IconPickFrame) then

		local string_lower = string.lower

		detailsFramework.IconPickFrame = CreateFrame("frame", "DetailsFrameworkIconPickFrame", UIParent, "BackdropTemplate")
		table.insert(UISpecialFrames, "DetailsFrameworkIconPickFrame")
		detailsFramework.IconPickFrame:SetFrameStrata("FULLSCREEN")

		detailsFramework.IconPickFrame:SetPoint("center", UIParent, "center")
		detailsFramework.IconPickFrame:SetWidth(416)
		detailsFramework.IconPickFrame:SetHeight(350)
		detailsFramework.IconPickFrame:EnableMouse(true)
		detailsFramework.IconPickFrame:SetMovable(true)

		detailsFramework:CreateTitleBar (detailsFramework.IconPickFrame, "Details! Framework Icon Picker")

		detailsFramework.IconPickFrame:SetBackdrop({edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1, bgFile = [[Interface\Tooltips\UI-Tooltip-Background]], tileSize = 64, tile = true})
		detailsFramework.IconPickFrame:SetBackdropBorderColor(0, 0, 0)
		detailsFramework.IconPickFrame:SetBackdropColor(24/255, 24/255, 24/255, .8)
		detailsFramework.IconPickFrame:SetFrameLevel(5000)

		detailsFramework.IconPickFrame:SetScript("OnMouseDown", function(self)
			if (not self.isMoving) then
				detailsFramework.IconPickFrame:StartMoving()
				self.isMoving = true
			end
		end)

		detailsFramework.IconPickFrame:SetScript("OnMouseUp", function(self)
			if (self.isMoving) then
				detailsFramework.IconPickFrame:StopMovingOrSizing()
				self.isMoving = nil
			end
		end)

		detailsFramework.IconPickFrame.emptyFunction = function() end
		detailsFramework.IconPickFrame.callback = detailsFramework.IconPickFrame.emptyFunction

		detailsFramework.IconPickFrame.preview =  CreateFrame("frame", nil, UIParent, "BackdropTemplate")
		detailsFramework.IconPickFrame.preview:SetFrameStrata("tooltip")
		detailsFramework.IconPickFrame.preview:SetFrameLevel(6001)
		detailsFramework.IconPickFrame.preview:SetSize(76, 76)

		local preview_image_bg = detailsFramework:NewImage(detailsFramework.IconPickFrame.preview, nil, 76, 76)
		preview_image_bg:SetDrawLayer("background", 0)
		preview_image_bg:SetAllPoints(detailsFramework.IconPickFrame.preview)
		preview_image_bg:SetColorTexture(0, 0, 0)

		local preview_image = detailsFramework:NewImage(detailsFramework.IconPickFrame.preview, nil, 76, 76)
		preview_image:SetAllPoints(detailsFramework.IconPickFrame.preview)

		detailsFramework.IconPickFrame.preview.icon = preview_image
		detailsFramework.IconPickFrame.preview:Hide()

		--serach
		detailsFramework.IconPickFrame.searchLabel =  detailsFramework:NewLabel(detailsFramework.IconPickFrame, nil, "$parentSearchBoxLabel", nil, "Search:")
		detailsFramework.IconPickFrame.searchLabel:SetPoint("topleft", detailsFramework.IconPickFrame, "topleft", 12, -36)
		detailsFramework.IconPickFrame.searchLabel:SetTemplate(detailsFramework:GetTemplate("font", "ORANGE_FONT_TEMPLATE"))
		detailsFramework.IconPickFrame.searchLabel.fontsize = 12

		detailsFramework.IconPickFrame.search = detailsFramework:NewTextEntry(detailsFramework.IconPickFrame, nil, "$parentSearchBox", nil, 140, 20)
		detailsFramework.IconPickFrame.search:SetPoint("left", detailsFramework.IconPickFrame.searchLabel, "right", 2, 0)
		detailsFramework.IconPickFrame.search:SetTemplate(detailsFramework:GetTemplate("dropdown", "OPTIONS_DROPDOWN_TEMPLATE"))

		detailsFramework.IconPickFrame.search:SetHook("OnTextChanged", function()
			detailsFramework.IconPickFrame.searching = detailsFramework.IconPickFrame.search:GetText()
			if (detailsFramework.IconPickFrame.searching == "") then
				detailsFramework.IconPickFrameScroll:Show()
				detailsFramework.IconPickFrame.searching = nil
				detailsFramework.IconPickFrameScroll.RefreshIcons()
			else
				detailsFramework.IconPickFrameScroll:Hide()
				FauxScrollFrame_SetOffset (detailsFramework.IconPickFrame, 1)
				detailsFramework.IconPickFrame.last_filter_index = 1
				detailsFramework.IconPickFrameScroll.RefreshIcons()
			end
		end)

		--manually enter the icon path
		detailsFramework.IconPickFrame.customIcon = detailsFramework:CreateLabel(detailsFramework.IconPickFrame, "Icon Path:", detailsFramework:GetTemplate("font", "ORANGE_FONT_TEMPLATE"))
		detailsFramework.IconPickFrame.customIcon:SetPoint("bottomleft", detailsFramework.IconPickFrame, "bottomleft", 12, 16)
		detailsFramework.IconPickFrame.customIcon.fontsize = 12

		detailsFramework.IconPickFrame.customIconEntry = detailsFramework:CreateTextEntry(detailsFramework.IconPickFrame, function()end, 200, 20, "CustomIconEntry", _, _, detailsFramework:GetTemplate("dropdown", "OPTIONS_DROPDOWN_TEMPLATE"))
		detailsFramework.IconPickFrame.customIconEntry:SetPoint("left", detailsFramework.IconPickFrame.customIcon, "right", 2, 0)

		detailsFramework.IconPickFrame.customIconEntry:SetHook("OnTextChanged", function()
			detailsFramework.IconPickFrame.preview:SetPoint("bottom", detailsFramework.IconPickFrame.customIconEntry.widget, "top", 0, 2)
			detailsFramework.IconPickFrame.preview.icon:SetTexture(detailsFramework.IconPickFrame.customIconEntry:GetText())
			detailsFramework.IconPickFrame.preview:Show()
		end)

		detailsFramework.IconPickFrame.customIconEntry:SetHook("OnEnter", function()
			detailsFramework.IconPickFrame.preview:SetPoint("bottom", detailsFramework.IconPickFrame.customIconEntry.widget, "top", 0, 2)
			detailsFramework.IconPickFrame.preview.icon:SetTexture(detailsFramework.IconPickFrame.customIconEntry:GetText())
			detailsFramework.IconPickFrame.preview:Show()
		end)

		--close button
		local close_button = CreateFrame("button", nil, detailsFramework.IconPickFrame, "UIPanelCloseButton", "BackdropTemplate")
		close_button:SetWidth(32)
		close_button:SetHeight(32)
		close_button:SetPoint("TOPRIGHT", detailsFramework.IconPickFrame, "TOPRIGHT", -8, -7)
		close_button:SetFrameLevel(close_button:GetFrameLevel()+2)
		close_button:SetAlpha(0) --just hide, it is used below

		--accept custom icon button
		local accept_custom_icon = function()
			local path = detailsFramework.IconPickFrame.customIconEntry:GetText()

			detailsFramework:QuickDispatch(detailsFramework.IconPickFrame.callback, path, detailsFramework.IconPickFrame.param1, detailsFramework.IconPickFrame.param2)

			if (detailsFramework.IconPickFrame.click_close) then
				close_button:Click()
			end
		end

		detailsFramework.IconPickFrame.customIconAccept = detailsFramework:CreateButton(detailsFramework.IconPickFrame, accept_custom_icon, 82, 20, "Accept", nil, nil, nil, nil, nil, nil, detailsFramework:GetTemplate("button", "OPTIONS_BUTTON_TEMPLATE"), detailsFramework:GetTemplate("font", "ORANGE_FONT_TEMPLATE"))
		detailsFramework.IconPickFrame.customIconAccept:SetPoint("left", detailsFramework.IconPickFrame.customIconEntry, "right", 2, 0)

		--fill with icons
		local MACRO_ICON_FILENAMES = {}
		local SPELLNAMES_CACHE = {}
		local SPELLIDS_CACHE = {}

		detailsFramework.IconPickFrame:SetScript("OnShow", function()
			MACRO_ICON_FILENAMES[1] = "INV_MISC_QUESTIONMARK"
			SPELLNAMES_CACHE[1] = "INV_MISC_QUESTIONMARK"
			SPELLIDS_CACHE[1] = IS_WOW_PROJECT_MAINLINE and 74008 or 25675
			local index = 2

			for i = 1, GetNumSpellTabs() do
				local tab, tabTex, offset, numSpells, _ = GetSpellTabInfo(i)
				offset = offset + 1
				local tabEnd = offset + numSpells

				for j = offset, tabEnd - 1 do
					--to get spell info by slot, you have to pass in a pet argument
					local spellType, ID, si = GetSpellBookItemInfo(j, SPELLBOOK_BANK_PLAYER)
					if (spellType ~= "FLYOUT") then
						MACRO_ICON_FILENAMES [index] = si and si.iconID or GetSpellBookItemTexture(j, SPELLBOOK_BANK_PLAYER) or 0
						SPELLNAMES_CACHE [index] = si and si.name or GetSpellInfo(ID)
						SPELLIDS_CACHE [index] = si and (si.spellID or si.actionID) or ID
						index = index + 1

					elseif (spellType == "FLYOUT") then
						local _, _, numSlots, isKnown = GetFlyoutInfo(ID)
						if (isKnown and numSlots > 0) then
							for k = 1, numSlots do
								local spellID, overrideSpellID, isKnown = GetFlyoutSlotInfo(ID, k)
								if (isKnown) then
									MACRO_ICON_FILENAMES [index] = GetSpellTexture(spellID) or 0
									SPELLNAMES_CACHE [index] = GetSpellInfo(spellID)
									SPELLIDS_CACHE [index] = spellID
									index = index + 1
								end
							end
						end
					end
				end
			end

			GetLooseMacroItemIcons(MACRO_ICON_FILENAMES)
			GetLooseMacroIcons(MACRO_ICON_FILENAMES)
			GetMacroIcons(MACRO_ICON_FILENAMES)
			GetMacroItemIcons(MACRO_ICON_FILENAMES)

			--reset the custom icon text entry
			detailsFramework.IconPickFrame.customIconEntry:SetText("")
			--reset the search text entry
			detailsFramework.IconPickFrame.search:SetText("")
		end)

		detailsFramework.IconPickFrame:SetScript("OnHide", function()
			wipe(MACRO_ICON_FILENAMES)
			wipe(SPELLNAMES_CACHE)
			detailsFramework.IconPickFrame.preview:Hide()
			collectgarbage()
		end)

		detailsFramework.IconPickFrame.buttons = {}

		local onClickFunction = function(self)

			detailsFramework:QuickDispatch(detailsFramework.IconPickFrame.callback, self.icon:GetTexture(), detailsFramework.IconPickFrame.param1, detailsFramework.IconPickFrame.param2)

			if (detailsFramework.IconPickFrame.click_close) then
				close_button:Click()
			end
		end

		local onEnter = function(self)
			detailsFramework.IconPickFrame.preview:SetPoint("bottom", self, "top", 0, 2)
			detailsFramework.IconPickFrame.preview.icon:SetTexture(self.icon:GetTexture())
			detailsFramework.IconPickFrame.preview:Show()
			self.icon:SetBlendMode("ADD")
		end
		local onLeave = function(self)
			detailsFramework.IconPickFrame.preview:Hide()
			self.icon:SetBlendMode("BLEND")
		end

		local backdrop = {bgFile = [[Interface\Tooltips\UI-Tooltip-Background]], tile = true, tileSize = 16,
		insets = {left = 0, right = 0, top = 0, bottom = 0}, edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1}

		for _, button in ipairs(detailsFramework.IconPickFrame.buttons) do
			button:SetBackdropBorderColor(0, 0, 0, 1)
		end

		local width = 412
		local height = 248
		local linesAmount = 6
		local lineHeight = 40

		local updateIconScroll = function(self, data, offset, totalLines)
            for i = 1, totalLines do
                local index = i + offset
                local iconsInThisLine = data[index]
				if (iconsInThisLine) then
					local line = self:GetLine(i)
                    for o = 1, #iconsInThisLine do
						local _, _, texture = GetSpellInfo(iconsInThisLine[o])
						if (texture) then
							line.buttons[o].icon:SetTexture(texture)
							line.buttons[o].texture = texture
						else
							local lineIcon = iconsInThisLine[o]
							if type(lineIcon) == "string" and not string.find(lineIcon, "^[Ii]nterface") then
								lineIcon = "Interface/ICONS/" .. lineIcon
							end
							line.buttons[o].icon:SetTexture(lineIcon)
							line.buttons[o].texture = lineIcon
						end
					end
					
					for o = #iconsInThisLine+1, 10 do -- cleanup unused
						line.buttons[o].icon:SetTexture(nil)
						line.buttons[o].texture = nil
					end
				end
			end
		end

		local lower = string.lower

		local scroll = detailsFramework:CreateScrollBox(detailsFramework.IconPickFrame, "DetailsFrameworkIconPickFrameScroll", updateIconScroll, {}, width, height, linesAmount, lineHeight)
		detailsFramework:ReskinSlider(scroll)
		scroll:SetPoint("topleft", detailsFramework.IconPickFrame, "topleft", 2, -58)

		function scroll.RefreshIcons()
			--build icon list
			local iconList = {}
			local numMacroIcons = #MACRO_ICON_FILENAMES

			local filter
			if (detailsFramework.IconPickFrame.searching) then
				filter = lower(detailsFramework.IconPickFrame.searching)
			end

			if (filter and filter ~= "") then
				local index
				local currentTable
				for i = 1, #SPELLNAMES_CACHE do
					if (SPELLNAMES_CACHE[i] and SPELLNAMES_CACHE[i]:lower():find(filter)) then
						if (not index) then
							index = 1
							local t = {}
							iconList[#iconList+1] = t
							currentTable = t
						end

						currentTable[index] = SPELLIDS_CACHE[i] --SPELLNAMES_CACHE[i] --spellName won't work in 11.0, use IDs instead.

						index = index + 1
						if (index == 11) then
							index = nil
						end
					end

				end
				
				for i = 1, #MACRO_ICON_FILENAMES do
					if (MACRO_ICON_FILENAMES[i] and type(MACRO_ICON_FILENAMES[i]) == "string" and MACRO_ICON_FILENAMES[i]:lower():find(filter)) then
						if (not index) then
							index = 1
							local t = {}
							iconList[#iconList+1] = t
							currentTable = t
						end

						currentTable[index] = MACRO_ICON_FILENAMES[i]

						index = index + 1
						if (index == 11) then
							index = nil
						end
					end

				end
			else
				for i = 1, #SPELLNAMES_CACHE, 10 do
					local t = {}
					iconList[#iconList+1] = t
					for o = i, i+9 do
						if (SPELLNAMES_CACHE[o]) then
							t[#t+1] = SPELLIDS_CACHE[o] --SPELLNAMES_CACHE[o] --spellName won't work in 11.0, use IDs instead.
						end
					end
				end

				for i = 1, #MACRO_ICON_FILENAMES, 10 do
					local t = {}
					iconList[#iconList+1] = t
					for o = i, i+9 do
						if (MACRO_ICON_FILENAMES[o]) then
							t[#t+1] = MACRO_ICON_FILENAMES[o]
						end
					end
				end
			end

			--set data and refresh
			scroll:SetData(iconList)
			scroll:Refresh()
		end

		--create the lines and button of the scroll box
		for i = 1, linesAmount do
			scroll:CreateLine(function(self, index)
				local line = CreateFrame("button", "$parentLine" .. index, self, "BackdropTemplate")
				line:SetPoint("topleft", self, "topleft", 1, -((index-1)*(lineHeight+1)) - 1)
				line:SetSize(width - 2, lineHeight)
				line:SetBackdrop({bgFile = [[Interface\Tooltips\UI-Tooltip-Background]], tileSize = 64, tile = true})
				line:SetBackdropColor(.2, .2, .2, .5)
				line.buttons = {}

				local lastButton

				for o = 1, 10 do
					local button = CreateFrame("button", "$parentIcon" .. o, line)
					if (not lastButton) then
						button:SetPoint("left", line, "left", 0, 0)
					else
						button:SetPoint("left", lastButton, "right", 1, 0)
					end
					button:SetSize(lineHeight, lineHeight)
					button.icon = button:CreateTexture("$parentIcon", "overlay")
					button.icon:SetAllPoints()
					button.icon:SetTexCoord(.1, .9, .1, .9)
					line.buttons[o] = button

					button:SetScript("OnEnter", onEnter)
					button:SetScript("OnLeave", onLeave)
					button:SetScript("OnClick", onClickFunction)

					lastButton = button
				end

				return line
			end)
		end

		detailsFramework.IconPickFrameScroll = scroll
		detailsFramework.IconPickFrame:Hide()
	end

	detailsFramework.IconPickFrame.param1, detailsFramework.IconPickFrame.param2 = param1, param2
	detailsFramework.IconPickFrame:Show()
	detailsFramework.IconPickFrame.callback = callback or detailsFramework.IconPickFrame.emptyFunction
	detailsFramework.IconPickFrame.click_close = close_when_select
	detailsFramework.IconPickFrameScroll.RefreshIcons()

end

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

function detailsFramework:ShowPanicWarning (text)
	if (not detailsFramework.PanicWarningWindow) then
		detailsFramework.PanicWarningWindow = CreateFrame("frame", "DetailsFrameworkPanicWarningWindow", UIParent, "BackdropTemplate")
		detailsFramework.PanicWarningWindow:SetHeight(80)
		detailsFramework.PanicWarningWindow:SetBackdrop({bgFile = [[Interface\Tooltips\UI-Tooltip-Background]], tileSize = 64, tile = true})
		detailsFramework.PanicWarningWindow:SetBackdropColor(1, 0, 0, 0.2)
		detailsFramework.PanicWarningWindow:SetPoint("topleft", UIParent, "topleft", 0, -250)
		detailsFramework.PanicWarningWindow:SetPoint("topright", UIParent, "topright", 0, -250)

		detailsFramework.PanicWarningWindow.text = detailsFramework.PanicWarningWindow:CreateFontString(nil, "overlay", "GameFontNormal")
		detailsFramework.PanicWarningWindow.text:SetPoint("center", detailsFramework.PanicWarningWindow, "center")
		detailsFramework.PanicWarningWindow.text:SetTextColor(1, 0.6, 0)
	end

	detailsFramework.PanicWarningWindow.text:SetText(text)
	detailsFramework.PanicWarningWindow:Show()
end

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

local on_drag_start = function(self)
	if (not self.bIsDragging) then
		self.bIsDragging = true
		self:StartMoving()
	end
end

local on_drag_stop = function(self)
	self.bIsDragging = false
	self:StopMovingOrSizing()
end

function detailsFramework:MakeDraggable(frame)
	frame:SetMovable(true)
	frame:EnableMouse(true)
	frame:RegisterForDrag("LeftButton")
	frame:SetScript("OnDragStart", on_drag_start)
	frame:SetScript("OnDragStop", on_drag_stop)
end

local simple_panel_mouse_down = function(self, button)
	if (button == "RightButton") then
		if (self.IsMoving) then
			self.IsMoving = false
			self:StopMovingOrSizing()
			if (self.db and self.db.position) then
				detailsFramework:SavePositionOnScreen (self)
			end
		end
		if (not self.DontRightClickClose) then
			self:Hide()
		end
		return
	end
	if (not self.IsMoving and not self.IsLocked) then
		self.IsMoving = true
		self:StartMoving()
	end
end
local simple_panel_mouse_up = function(self, button)
	if (self.IsMoving) then
		self.IsMoving = false
		self:StopMovingOrSizing()
		if (self.db and self.db.position) then
			detailsFramework:SavePositionOnScreen (self)
		end
	end
end
local simple_panel_settitle = function(self, title)
	self.Title:SetText(title)
end

local simple_panel_close_click = function(self)
	self:GetParent():GetParent():Hide()
end

local SimplePanel_frame_backdrop = {edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1, bgFile = [[Interface\Tooltips\UI-Tooltip-Background]], tileSize = 64, tile = true}
local SimplePanel_frame_backdrop_color = {0, 0, 0, 0.9}
local SimplePanel_frame_backdrop_border_color = {0, 0, 0, 1}

--with_label was making the frame stay in place while its parent moves
--the slider was anchoring to with_label and here here were anchoring the slider again
---@class df_scalebar : slider
---@field thumb texture
function detailsFramework:CreateScaleBar(frame, config, bNoRightClick) --~scale
	---@type df_scalebar
	local scaleBar, text = detailsFramework:CreateSlider(frame, 120, 14, 0.6, 1.6, 0.1, config.scale, true, "ScaleBar", nil, "Scale:", detailsFramework:GetTemplate("slider", "OPTIONS_SLIDER_TEMPLATE"), detailsFramework:GetTemplate("font", "ORANGE_FONT_TEMPLATE"))
	scaleBar.thumb:SetWidth(24)
	scaleBar:SetValueStep(0.05)
	scaleBar:SetObeyStepOnDrag(true)
	scaleBar.mouseDown = false
	rawset(scaleBar, "lockdown", true)

	--create a custom editbox to enter the scale from text
	local editbox = CreateFrame("editbox", nil, scaleBar.widget, "BackdropTemplate")
	editbox:SetSize(40, 20)
	editbox:SetJustifyH("center")
	editbox:SetBackdrop({bgFile = [[Interface\ACHIEVEMENTFRAME\UI-GuildAchievement-Parchment-Horizontal-Desaturated]],
	edgeFile = [[Interface\Buttons\WHITE8X8]],
	tile = true, edgeSize = 1, tileSize = 64})
	editbox:SetFontObject("GameFontHighlightSmall")
	editbox:SetBackdropColor(0, 0, 0, 1)

	editbox:SetScript("OnEditFocusGained", function()
	end)

	editbox:SetScript("OnEnterPressed", function()
		editbox:ClearFocus()
		editbox:Hide()
		local text = editbox:GetText()
		local newScale = detailsFramework.TextToFloor(text)

		if (newScale) then
			config.scale = newScale
			scaleBar:SetValue(newScale)
			frame:SetScale(newScale)
			editbox.defaultValue = newScale
		end
	end)

	editbox:SetScript("OnEscapePressed", function()
		if (bNoRightClick) then
			return
		end
		editbox:ClearFocus()
		editbox:Hide()
		editbox:SetText(editbox.defaultValue)
	end)

	scaleBar:SetScript("OnMouseDown", function(_, mouseButton)
		if (mouseButton == "LeftButton" or (mouseButton == "RightButton" and bNoRightClick)) then
			scaleBar.mouseDown  = true

		elseif (mouseButton == "RightButton") then
			if (bNoRightClick) then
				return
			end
			editbox:Show()
			editbox:SetAllPoints()
			editbox:SetText(config.scale)
			editbox:SetFocus(true)
			editbox.defaultValue = config.scale
		end
	end)

	scaleBar:SetScript("OnMouseUp", function(_, mouseButton)
		if (mouseButton == "LeftButton" or (mouseButton == "RightButton" and bNoRightClick)) then
			scaleBar.mouseDown  = false
			frame:SetScale(config.scale)
			editbox.defaultValue = config.scale
		end
	end)

	text:SetPoint("topleft", frame, "topleft", 12, -7)
	scaleBar:SetFrameLevel(detailsFramework.FRAMELEVEL_OVERLAY)
	scaleBar.OnValueChanged = function(_, _, value)
		if (scaleBar.mouseDown) then
			config.scale = value
		end
	end

	scaleBar:SetAlpha(0.70)
	editbox.defaultValue = config.scale
	editbox:SetFocus(false)
	editbox:SetAutoFocus(false)
	editbox:ClearFocus()

	C_Timer.After(1, function()
		editbox:SetFocus(false)
		editbox:SetAutoFocus(false)
		editbox:ClearFocus()
	end)

	return scaleBar
end

local no_options = {}

---create a simple panel with a title bar, a close button and a background
---already has onmousedown and onmouseup scripts to make it movable
---the panelOptions table can be used to set some options:
---NoScripts = false, --if true, won't set OnMouseDown and OnMouseUp (won't be movable)
---NoTUISpecialFrame = false, --if true, won't add the frame to 'UISpecialFrames'
---DontRightClickClose = false, --if true, won't make the frame close when clicked with the right mouse button
---UseScaleBar = false, --if true, will create a scale bar in the top left corner (require a table on 'db' to save the scale)
---UseStatusBar = false, --if true, creates a status bar at the bottom of the frame (frame.StatusBar)
---NoCloseButton = false, --if true, won't show the close button
---NoTitleBar = false, --if true, don't create the title bar
---RoundedCorners = false, --use rounded corners if true
---@class simplepanel : frame
---@field TitleBar frame
---@field Title fontstring
---@field Close button
---@field SetTitle fun(self: simplepanel, title: string)

---@param parent frame the parent frame
---@param width number? the width of the panel
---@param height number? the height of the panel
---@param title string? a string to show in the title bar
---@param frameName string? the name of the frame
---@param panelOptions table? a table with options described above
---@param savedVariableTable table? a table to save the scale of the panel
---@return simplepanel
function detailsFramework:CreateSimplePanel(parent, width, height, title, frameName, panelOptions, savedVariableTable)
	--create a saved variable table if the savedVariableTable has been not passed within the function call
	if (savedVariableTable and frameName and not savedVariableTable[frameName]) then
		savedVariableTable[frameName] = {
			scale = 1
		}
	end

	--create a frame name if the frameName has been not passed within the function call
	if (not frameName) then
		frameName = "DetailsFrameworkSimplePanel" .. detailsFramework.SimplePanelCounter
		detailsFramework.SimplePanelCounter = detailsFramework.SimplePanelCounter + 1
	end

	--default parent is UIParent
	if (not parent) then
		parent = _G["UIParent"]
	end

	--default options
	panelOptions = panelOptions or no_options

	--create the frame
	local simplePanel = CreateFrame("frame", frameName, _G["UIParent"],"BackdropTemplate")
	simplePanel:SetSize(width or 400, height or 250)
	simplePanel:SetPoint("center", _G["UIParent"], "center", 0, 0)
	simplePanel:SetFrameStrata("FULLSCREEN")
	simplePanel:EnableMouse()
	simplePanel:SetMovable(true)

	--set the backdrop
	if (panelOptions.RoundedCorners) then
		local tRoundedCornerPreset = {
			roundness = 3,
			color = {.1, .1, .1, 0.98},
			border_color = {.05, .05, .05, 0.834},
			use_titlebar = true,
			titlebar_height = 26,
		}
		detailsFramework:AddRoundedCornersToFrame(simplePanel, tRoundedCornerPreset)
	else
		simplePanel:SetBackdrop(SimplePanel_frame_backdrop)
		simplePanel:SetBackdropColor(unpack(SimplePanel_frame_backdrop_color))
		simplePanel:SetBackdropBorderColor(unpack(SimplePanel_frame_backdrop_border_color))
	end

	simplePanel.DontRightClickClose = panelOptions.DontRightClickClose

	if (not panelOptions.NoTUISpecialFrame) then
		table.insert(UISpecialFrames, frameName)
	end

	if (panelOptions.UseStatusBar and not panelOptions.RoundedCorners) then
		local statusBar = detailsFramework:CreateStatusBar(simplePanel)
		simplePanel.StatusBar = statusBar
	end

	local titleBar = CreateFrame("frame", frameName .. "TitleBar", simplePanel, "BackdropTemplate")

	if (panelOptions.RoundedCorners) then
		--a key named "TitleBar" is created by the rounded corners function
		simplePanel.TitleBar:SetColor(.2, .2, .2, 0.4)
		simplePanel.TitleBar:SetBorderCornerColor(0, 0, 0, 0)

	else
		simplePanel.TitleBar = titleBar
		titleBar:SetPoint("topleft", simplePanel, "topleft", 2, -3)
		titleBar:SetPoint("topright", simplePanel, "topright", -2, -3)
		titleBar:SetHeight(20)
		titleBar:SetBackdrop(SimplePanel_frame_backdrop)
		titleBar:SetBackdropColor(.2, .2, .2, 1)
		titleBar:SetBackdropBorderColor(0, 0, 0, 1)
	end

	local close = CreateFrame("button", frameName and frameName .. "CloseButton", titleBar)
	close:SetFrameLevel(detailsFramework.FRAMELEVEL_OVERLAY)
	close:SetSize(16, 16)

	close:SetNormalTexture([[Interface\GLUES\LOGIN\Glues-CheckBox-Check]])
	close:SetHighlightTexture([[Interface\GLUES\LOGIN\Glues-CheckBox-Check]])
	close:SetPushedTexture([[Interface\GLUES\LOGIN\Glues-CheckBox-Check]])
	close:GetNormalTexture():SetDesaturated(true)
	close:GetHighlightTexture():SetDesaturated(true)
	close:GetPushedTexture():SetDesaturated(true)

	close:SetAlpha(0.7)
	close:SetScript("OnClick", simple_panel_close_click)
	simplePanel.Close = close
	simplePanel.closeButton = close

	local titleText = titleBar:CreateFontString(frameName and frameName .. "Title", "overlay", "GameFontNormal")
	titleText:SetTextColor(.8, .8, .8, 1)
	titleText:SetText(title or "")
	simplePanel.Title = titleText

	if (panelOptions.UseScaleBar and savedVariableTable and savedVariableTable[frameName]) then
		detailsFramework:CreateScaleBar(simplePanel, savedVariableTable[frameName])
		simplePanel:SetScale(savedVariableTable[frameName].scale)
	end

	simplePanel.Title:SetPoint("center", titleBar, "center")
	simplePanel.Close:SetPoint("right", titleBar, "right", -2, 0)

	if (panelOptions.NoCloseButton or panelOptions.RoundedCorners) then
		simplePanel.Close:Hide()
	end

	if (panelOptions.NoTitleBar) then
		simplePanel.TitleBar:Hide()
	end

	if (not panelOptions.NoScripts) then
		simplePanel:SetScript("OnMouseDown", simple_panel_mouse_down)
		simplePanel:SetScript("OnMouseUp", simple_panel_mouse_up)
	end

	simplePanel.SetTitle = simple_panel_settitle

	return simplePanel
end

local Panel1PxBackdrop = {bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", tile = true, tileSize = 64,
edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1, insets = {left = 2, right = 2, top = 3, bottom = 3}}

local Panel1PxOnClickClose = function(self)
	self:GetParent():Hide()
end
local Panel1PxOnToggleLock = function(self)
	if (self.IsLocked) then
		self.IsLocked = false
		self:SetMovable(true)
		self:EnableMouse(true)
		self.Lock:GetNormalTexture():SetTexCoord(16/64, 32/64, 0, 1)
		self.Lock:GetHighlightTexture():SetTexCoord(16/32, 32/64, 0, 1)
		self.Lock:GetPushedTexture():SetTexCoord(16/64, 32/64, 0, 1)
		if (self.OnUnlock) then
			self:OnUnlock()
		end
		if (self.db) then
			self.db.IsLocked = self.IsLocked
		end
	else
		self.IsLocked = true
		self:SetMovable(false)
		self:EnableMouse(false)
		self.Lock:GetNormalTexture():SetTexCoord(0/64, 16/64, 0, 1)
		self.Lock:GetHighlightTexture():SetTexCoord(0/64, 16/64, 0, 1)
		self.Lock:GetPushedTexture():SetTexCoord(0/64, 16/64, 0, 1)
		if (self.OnLock) then
			self:OnLock()
		end
		if (self.db) then
			self.db.IsLocked = self.IsLocked
		end
	end
end
local Panel1PxOnClickLock = function(self)
	local f = self:GetParent()
	Panel1PxOnToggleLock (f)
end
local Panel1PxSetTitle = function(self, text)
	self.Title:SetText(text or "")
end

local Panel1PxSetLocked= function(self, lock_state)
	if (type(lock_state) ~= "boolean") then
		return
	end
	if (lock_state) then
		-- lock it
		self.IsLocked = false
		Panel1PxOnClickLock (self.Lock)
	else
		-- unlockit
		self.IsLocked = true
		Panel1PxOnClickLock (self.Lock)
	end
end

local Panel1PxReadConfig = function(self)
	local db = self.db
	if (db) then
		db.IsLocked = db.IsLocked or false
		self.IsLocked = db.IsLocked
		db.position = db.position or {x = 0, y = 0}
		db.position.x = db.position.x or 0
		db.position.y = db.position.y or 0
		detailsFramework:RestoreFramePosition (self)
	end
end

function detailsFramework:SavePositionOnScreen (frame)
	if (frame.db and frame.db.position) then
		local x, y = detailsFramework:GetPositionOnScreen (frame)
		--print("saving...", x, y, frame:GetName())
		if (x and y) then
			frame.db.position.x, frame.db.position.y = x, y
		end
	end
end

function detailsFramework:GetPositionOnScreen (frame)
	local xOfs, yOfs = frame:GetCenter()
	if (not xOfs) then
		return
	end
	local scale = frame:GetEffectiveScale()
	local UIscale = UIParent:GetScale()
	xOfs = xOfs*scale - GetScreenWidth()*UIscale/2
	yOfs = yOfs*scale - GetScreenHeight()*UIscale/2
	return xOfs/UIscale, yOfs/UIscale
end

function detailsFramework:RestoreFramePosition (frame)
	if (frame.db and frame.db.position) then
		local scale, UIscale = frame:GetEffectiveScale(), UIParent:GetScale()
		frame:ClearAllPoints()
		frame.db.position.x = frame.db.position.x or 0
		frame.db.position.y = frame.db.position.y or 0
		frame:SetPoint("center", UIParent, "center", frame.db.position.x * UIscale / scale, frame.db.position.y * UIscale / scale)
	end
end

local Panel1PxSavePosition= function(self)
	detailsFramework:SavePositionOnScreen (self)
end

local Panel1PxHasPosition = function(self)
	local db = self.db
	if (db) then
		if (db.position and db.position.x and (db.position.x ~= 0 or db.position.y ~= 0)) then
			return true
		end
	end
end

function detailsFramework:Create1PxPanel(parent, width, height, title, name, config, titleAnchor, noSpecialFrame)
	local newFrame = CreateFrame("frame", name, parent or UIParent, "BackdropTemplate")
	newFrame:SetSize(width or 100, height or 75)
	newFrame:SetPoint("center", UIParent, "center", 0, 0)

	if (name and not noSpecialFrame) then
		table.insert(UISpecialFrames, name)
	end

	newFrame:SetScript("OnMouseDown", simple_panel_mouse_down)
	newFrame:SetScript("OnMouseUp", simple_panel_mouse_up)

	newFrame:SetBackdrop(Panel1PxBackdrop)
	newFrame:SetBackdropColor(0, 0, 0, 0.5)

	newFrame.IsLocked = (config and config.IsLocked ~= nil and config.IsLocked) or false
	newFrame:SetMovable(true)
	newFrame:EnableMouse(true)
	newFrame:SetUserPlaced (true)

	newFrame.db = config
	Panel1PxReadConfig(newFrame)

	local closeButton = CreateFrame("button", name and name .. "CloseButton", newFrame, "BackdropTemplate")
	closeButton:SetSize(16, 16)
	closeButton:SetNormalTexture([[Interface\GLUES\LOGIN\Glues-CheckBox-Check]])
	closeButton:SetHighlightTexture([[Interface\GLUES\LOGIN\Glues-CheckBox-Check]])
	closeButton:SetPushedTexture([[Interface\GLUES\LOGIN\Glues-CheckBox-Check]])
	closeButton:GetNormalTexture():SetDesaturated(true)
	closeButton:GetHighlightTexture():SetDesaturated(true)
	closeButton:GetPushedTexture():SetDesaturated(true)
	closeButton:SetAlpha(0.7)

	local lockButton = CreateFrame("button", name and name .. "LockButton", newFrame, "BackdropTemplate")
	lockButton:SetSize(16, 16)
	lockButton:SetNormalTexture([[Interface\GLUES\CharacterSelect\Glues-AddOn-Icons]])
	lockButton:SetHighlightTexture([[Interface\GLUES\CharacterSelect\Glues-AddOn-Icons]])
	lockButton:SetPushedTexture([[Interface\GLUES\CharacterSelect\Glues-AddOn-Icons]])
	lockButton:GetNormalTexture():SetDesaturated(true)
	lockButton:GetHighlightTexture():SetDesaturated(true)
	lockButton:GetPushedTexture():SetDesaturated(true)
	lockButton:SetAlpha(0.7)

	closeButton:SetPoint("topright", newFrame, "topright", -3, -3)
	lockButton:SetPoint("right", closeButton, "left", 3, 0)

	closeButton:SetScript("OnClick", Panel1PxOnClickClose)
	lockButton:SetScript("OnClick", Panel1PxOnClickLock)

	local titleString = newFrame:CreateFontString(name and name .. "Title", "overlay", "GameFontNormal")
	titleString:SetPoint("topleft", newFrame, "topleft", 5, -5)

	detailsFramework.Language.SetTextIfLocTableOrDefault(titleString, title or "")

	if (titleAnchor) then
		if (titleAnchor == "top") then
			titleString:ClearAllPoints()
			titleString:SetPoint("bottomleft", newFrame, "topleft", 0, 0)

			closeButton:ClearAllPoints()
			closeButton:SetPoint("bottomright", newFrame, "topright", 0, 0)
		end
		newFrame.title_anchor = titleAnchor
	end

	newFrame.SetTitle = Panel1PxSetTitle
	newFrame.Title = titleString
	newFrame.Lock = lockButton
	newFrame.Close = closeButton
	newFrame.HasPosition = Panel1PxHasPosition
	newFrame.SavePosition = Panel1PxSavePosition

	newFrame.IsLocked = not newFrame.IsLocked
	newFrame.SetLocked = Panel1PxSetLocked
	Panel1PxOnToggleLock(newFrame)

	return newFrame
end

------------------------------------------------------------------------------------------------------------------------------------------------
-- ~prompt
function detailsFramework:HidePromptPanel(promptName)
	if (detailsFramework.promtp_panel) then
		if (promptName) then
			if (detailsFramework.promtp_panel.promptName == promptName) then
				detailsFramework.promtp_panel:Hide()
				detailsFramework.promtp_panel.promptName = nil
			end
		else
			detailsFramework.promtp_panel:Hide()
			detailsFramework.promtp_panel.promptName = nil
		end
	end
end

---show a prompt to the player with a question (message) and two buttons "yes" and "no"
---@param message string the question to show to the player
---@param trueCallback function if the player clicks on "yes"
---@param falseCallback function if the player clicks on "no"
---@param dontOverride boolean|nil if true, won't show another prompt if theres already a shown prompt
---@param width number|nil width of the prompt frame, if ommited, will use the default width 400
---@param promptName string|nil set a name to the prompt, used on HidePromptPanel(promptName)
function detailsFramework:ShowPromptPanel(message, trueCallback, falseCallback, dontOverride, width, promptName)
	if (not DetailsFrameworkPromptSimple) then
		local promptFrame = CreateFrame("frame", "DetailsFrameworkPromptSimple", UIParent, "BackdropTemplate")
		promptFrame:SetSize(400, 80)
		promptFrame:SetFrameStrata("FULLSCREEN")
		promptFrame:SetPoint("center", UIParent, "center", 0, 300)
		detailsFramework:ApplyStandardBackdrop(promptFrame)
		table.insert(UISpecialFrames, "DetailsFrameworkPromptSimple")

		detailsFramework:CreateTitleBar(promptFrame, "Prompt!")
		detailsFramework:ApplyStandardBackdrop(promptFrame)

		local prompt = promptFrame:CreateFontString(nil, "overlay", "GameFontNormal")
		prompt:SetPoint("top", promptFrame, "top", 0, -28)
		prompt:SetJustifyH("center")
		promptFrame.prompt = prompt

		local button_text_template = detailsFramework:GetTemplate("font", "OPTIONS_FONT_TEMPLATE")
		local options_dropdown_template = detailsFramework:GetTemplate("dropdown", "OPTIONS_DROPDOWN_TEMPLATE")

		local buttonTrue = detailsFramework:CreateButton(promptFrame, nil, 60, 20, "Yes", nil, nil, nil, nil, nil, nil, options_dropdown_template)
		buttonTrue:SetPoint("bottomright", promptFrame, "bottomright", -5, 5)
		promptFrame.button_true = buttonTrue

		local buttonFalse = detailsFramework:CreateButton(promptFrame, nil, 60, 20, "No", nil, nil, nil, nil, nil, nil, options_dropdown_template)
		buttonFalse:SetPoint("bottomleft", promptFrame, "bottomleft", 5, 5)
		promptFrame.button_false = buttonFalse

		buttonTrue:SetClickFunction(function()
			local my_func = buttonTrue.true_function
			if (my_func) then
				local okey, errormessage = pcall(my_func, true)
				if (not okey) then
					print("error:", errormessage)
				end
				promptFrame:Hide()
			end
		end)

		buttonFalse:SetClickFunction(function()
			local my_func = buttonFalse.false_function
			if (my_func) then
				local okey, errormessage = pcall(my_func, true)
				if (not okey) then
					print("error:", errormessage)
				end
				promptFrame:Hide()
			end
		end)

		promptFrame.ShowAnimation = detailsFramework:CreateAnimationHub(promptFrame, function()
			promptFrame:SetBackdropBorderColor(0, 0, 0, 0)
			promptFrame.TitleBar:SetBackdropBorderColor(0, 0, 0, 0)
		end,
		function()
			promptFrame:SetBackdropBorderColor(0, 0, 0, 1)
			promptFrame.TitleBar:SetBackdropBorderColor(0, 0, 0, 1)
		end)

		detailsFramework:CreateAnimation(promptFrame.ShowAnimation, "scale", 1, .075, .2, .2, 1.1, 1.1, "center", 0, 0)
		detailsFramework:CreateAnimation(promptFrame.ShowAnimation, "scale", 2, .075, 1, 1, .90, .90, "center", 0, 0)

		promptFrame.FlashTexture = promptFrame:CreateTexture(nil, "overlay")
		promptFrame.FlashTexture:SetColorTexture(1, 1, 1, 1)
		promptFrame.FlashTexture:SetAllPoints()

		promptFrame.FlashAnimation = detailsFramework:CreateAnimationHub(promptFrame.FlashTexture, function() promptFrame.FlashTexture:Show() end, function() promptFrame.FlashTexture:Hide() end)
		detailsFramework:CreateAnimation(promptFrame.FlashAnimation, "alpha", 1, .075, 0, .25)
		detailsFramework:CreateAnimation(promptFrame.FlashAnimation, "alpha", 2, .075, .35, 0)

		promptFrame:Hide()
		detailsFramework.promtp_panel = promptFrame
	end

	assert(type(trueCallback) == "function" and type(falseCallback) == "function", "ShowPromptPanel expects two functions.")

	if (dontOverride) then
		if (detailsFramework.promtp_panel:IsShown()) then
			return
		end
	end

	if (width) then
		detailsFramework.promtp_panel:SetWidth(width)
	else
		detailsFramework.promtp_panel:SetWidth(400)
	end

	detailsFramework.promtp_panel.promptName = promptName

	detailsFramework.promtp_panel.prompt:SetText(message)
	detailsFramework.promtp_panel.button_true.true_function = trueCallback
	detailsFramework.promtp_panel.button_false.false_function = falseCallback

	detailsFramework.promtp_panel:Show()

	detailsFramework.promtp_panel.ShowAnimation:Play()
	detailsFramework.promtp_panel.FlashAnimation:Play()
end


function detailsFramework:ShowTextPromptPanel(message, callback)
	if (not detailsFramework.text_prompt_panel) then
		local promptFrame = CreateFrame("frame", "DetailsFrameworkPrompt", UIParent, "BackdropTemplate")
		promptFrame:SetSize(400, 120)
		promptFrame:SetFrameStrata("FULLSCREEN")
		promptFrame:SetPoint("center", UIParent, "center", 0, 100)
		promptFrame:EnableMouse(true)
		promptFrame:SetMovable(true)
		promptFrame:RegisterForDrag ("LeftButton")
		promptFrame:SetScript("OnDragStart", function() promptFrame:StartMoving() end)
		promptFrame:SetScript("OnDragStop", function() promptFrame:StopMovingOrSizing() end)
		promptFrame:SetScript("OnMouseDown", function(self, button) if (button == "RightButton") then promptFrame.EntryBox:ClearFocus() promptFrame:Hide() end end)
		table.insert(UISpecialFrames, "DetailsFrameworkPrompt")

		detailsFramework:CreateTitleBar (promptFrame, "Prompt!")
		detailsFramework:ApplyStandardBackdrop(promptFrame)

		local prompt = promptFrame:CreateFontString(nil, "overlay", "GameFontNormal")
		prompt:SetPoint("top", promptFrame, "top", 0, -25)
		prompt:SetJustifyH("center")
		prompt:SetSize(360, 36)
		promptFrame.prompt = prompt

		local button_text_template = detailsFramework:GetTemplate("font", "OPTIONS_FONT_TEMPLATE")
		local options_dropdown_template = detailsFramework:GetTemplate("dropdown", "OPTIONS_DROPDOWN_TEMPLATE")

		local textbox = detailsFramework:CreateTextEntry(promptFrame, function()end, 380, 20, "textbox", nil, nil, options_dropdown_template)
		textbox:SetPoint("topleft", promptFrame, "topleft", 10, -60)
		promptFrame.EntryBox = textbox

		local buttonTrue = detailsFramework:CreateButton(promptFrame, nil, 60, 20, "Okay", nil, nil, nil, nil, nil, nil, options_dropdown_template)
		buttonTrue:SetPoint("bottomright", promptFrame, "bottomright", -10, 5)
		promptFrame.button_true = buttonTrue

		local buttonFalse = detailsFramework:CreateButton(promptFrame, function() promptFrame.textbox:ClearFocus() promptFrame:Hide() end, 60, 20, "Cancel", nil, nil, nil, nil, nil, nil, options_dropdown_template)
		buttonFalse:SetPoint("bottomleft", promptFrame, "bottomleft", 10, 5)
		promptFrame.button_false = buttonFalse

		local executeCallback = function()
			local my_func = buttonTrue.true_function
			if (my_func) then
				local okey, errormessage = pcall(my_func, textbox:GetText())
				textbox:ClearFocus()
				if (not okey) then
					print("error:", errormessage)
				end
				promptFrame:Hide()
			end
		end

		buttonTrue:SetClickFunction(function()
			executeCallback()
		end)

		textbox:SetHook("OnEnterPressed", function()
			executeCallback()
		end)

		promptFrame:Hide()
		detailsFramework.text_prompt_panel = promptFrame
	end

	detailsFramework.text_prompt_panel:Show()
	DetailsFrameworkPrompt.EntryBox:SetText("")
	detailsFramework.text_prompt_panel.prompt:SetText(message)
	detailsFramework.text_prompt_panel.button_true.true_function = callback
	detailsFramework.text_prompt_panel.textbox:SetFocus(true)
end


------------------------------------------------------------------------------------------------------------------------------------------------
--chart panel -- ~chart

local chart_panel_backdrop = {bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", tile = true, tileSize = 16,
edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", edgeSize = 32, insets = {left = 5, right = 5, top = 5, bottom = 5}}

local chart_panel_align_timelabels = function(self, elapsed_time)
	self.TimeScale = elapsed_time

	local linha = self.TimeLabels[17]
	local minutes, seconds = math.floor(elapsed_time / 60), math.floor(elapsed_time % 60)
	if (seconds < 10) then
		seconds = "0" .. seconds
	end

	if (minutes > 0) then
		if (minutes < 10) then
			minutes = "0" .. minutes
		end
		linha:SetText(minutes .. ":" .. seconds)
	else
		linha:SetText("00:" .. seconds)
	end

	local time_div = elapsed_time / 16 --786 -- 49.125

	for i = 2, 16 do
		local linha = self.TimeLabels [i]
		local this_time = time_div * (i-1)
		local minutes, seconds = math.floor(this_time / 60), math.floor(this_time % 60)

		if (seconds < 10) then
			seconds = "0" .. seconds
		end

		if (minutes > 0) then
			if (minutes < 10) then
				minutes = "0" .. minutes
			end
			linha:SetText(minutes .. ":" .. seconds)
		else
			linha:SetText("00:" .. seconds)
		end
	end
end

local chart_panel_set_scale = function(self, amt, func, text)
	if (type(amt) ~= "number") then
		return
	end

	--each line amount, then multiply the line index by this number
	local piece = amt / 8

	for i = 1, 8 do
		if (func) then
			self ["dpsamt" .. math.abs(i-9)]:SetText(func (piece*i))
		else
			if (piece*i > 1) then
				self ["dpsamt" .. math.abs(i-9)]:SetText(detailsFramework.FormatNumber (piece*i))
			else
				self ["dpsamt" .. math.abs(i-9)]:SetText(format("%.3f", piece*i))
			end
		end
	end
end

local chart_panel_can_move = function(self, can)
	self.can_move = can
end

local chart_panel_overlay_reset = function(self)
	self.OverlaysAmount = 1
	for index, pack in ipairs(self.Overlays) do
		for index2, texture in ipairs(pack) do
			texture:Hide()
		end
	end
end

local chart_panel_reset = function(self)
	self.Graphic:ResetData()
	self.Graphic.max_value = 0

	self.TimeScale = nil
	self.BoxLabelsAmount = 1
	table.wipe(self.GData)
	table.wipe(self.OData)

	for index, box in ipairs(self.BoxLabels) do
		box.check:Hide()
		box.button:Hide()
		box.box:Hide()
		box.text:Hide()
		box.border:Hide()
		box.showing = false
	end

	chart_panel_overlay_reset (self)
end

local chart_panel_enable_line = function(f, thisbox)
	local index = thisbox.index
	local boxType = thisbox.type

	if (thisbox.enabled) then
		--disable
		thisbox.check:Hide()
		thisbox.enabled = false
	else
		--enable
		thisbox.check:Show()
		thisbox.enabled = true
	end

	if (boxType == "graphic") then
		f.Graphic:ResetData()
		f.Graphic.max_value = 0

		local max = 0
		local max_time = 0

		for index, box in ipairs(f.BoxLabels) do
			if (box.type == boxType and box.showing and box.enabled) then
				local data = f.GData[index]

				f.Graphic:AddDataSeries(data[1], data[2], nil, data[3])

				if (data[4] > max) then
					max = data[4]
				end
				if (data [5] > max_time) then
					max_time = data [5]
				end
			end
		end

		f:SetScale(max)
		f:SetTime (max_time)

	elseif (boxType == "overlay") then
		chart_panel_overlay_reset (f)

		for index, box in ipairs(f.BoxLabels) do
			if (box.type == boxType and box.showing and box.enabled) then
				f:AddOverlay(box.index)
			end
		end
	end
end

local create_box = function(self, next_box)
	local thisbox = {}
	self.BoxLabels [next_box] = thisbox

	local box = detailsFramework:NewImage(self.Graphic, nil, 16, 16, "border")
	local text = detailsFramework:NewLabel(self.Graphic)

	local border = detailsFramework:NewImage(self.Graphic, [[Interface\DialogFrame\UI-DialogBox-Gold-Corner]], 30, 30, "artwork")
	border:SetPoint("center", box, "center", -3, -4)
	border:SetTexture([[Interface\DialogFrame\UI-DialogBox-Gold-Corner]])

	local checktexture = detailsFramework:NewImage(self.Graphic, [[Interface\Buttons\UI-CheckBox-Check]], 18, 18, "overlay")
	checktexture:SetPoint("center", box, "center", 0, -1)
	checktexture:SetTexture([[Interface\Buttons\UI-CheckBox-Check]])

	thisbox.box = box
	thisbox.text = text
	thisbox.border = border
	thisbox.check = checktexture
	thisbox.enabled = true

	local button = CreateFrame("button", nil, self.Graphic, "BackdropTemplate")
	button:SetSize(20, 20)
	button:SetScript("OnClick", function()
		chart_panel_enable_line (self, thisbox)
	end)
	button:SetPoint("topleft", box.widget or box, "topleft", 0, 0)
	button:SetPoint("bottomright", box.widget or box, "bottomright", 0, 0)

	button:SetBackdrop({edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1, bgFile = [[Interface\Tooltips\UI-Tooltip-Background]], tileSize = 64, tile = true})
	button:SetBackdropColor(0, 0, 0, 0.0)
	button:SetBackdropBorderColor(0, 0, 0, 1)

	thisbox.button = button

	thisbox.box:SetPoint("right", text, "left", -4, 0)

	if (next_box == 1) then
		thisbox.text:SetPoint("topright", self, "topright", -35, -16)
	else
		thisbox.text:SetPoint("right", self.BoxLabels [next_box-1].box, "left", -17, 0)
	end

	return thisbox

end

local realign_labels = function(self)
	if (not self.ShowHeader) then
		for _, box in ipairs(self.BoxLabels) do
			box.check:Hide()
			box.button:Hide()
			box.border:Hide()
			box.box:Hide()
			box.text:Hide()
		end
		return
	end

	local width = self:GetWidth() - 108

	local first_box = self.BoxLabels [1]
	first_box.text:SetPoint("topright", self, "topright", -35, -16)

	local line_width = first_box.text:GetStringWidth() + 26

	for i = 2, #self.BoxLabels do

		local box = self.BoxLabels [i]

		if (box.box:IsShown()) then

			line_width = line_width + box.text:GetStringWidth() + 26

			if (line_width > width) then
				line_width = box.text:GetStringWidth() + 26
				box.text:SetPoint("topright", self, "topright", -35, -40)
			else
				box.text:SetPoint("right", self.BoxLabels [i-1].box, "left", -27, 0)
			end
		else
			break
		end
	end

	if (self.HeaderOnlyIndicator) then
		for _, box in ipairs(self.BoxLabels) do
				box.check:Hide()
			box.button:Hide()
		end
		return
	end
end

local chart_panel_add_label = function(self, color, name, type, number)
	local next_box = self.BoxLabelsAmount
	local thisbox = self.BoxLabels [next_box]

	if (not thisbox) then
		thisbox = create_box (self, next_box)
	end

	self.BoxLabelsAmount = self.BoxLabelsAmount + 1

	thisbox.type = type
	thisbox.index = number

	thisbox.box:SetColorTexture(unpack(color))
	thisbox.text:SetText(name)

	thisbox.check:Show()
	thisbox.button:Show()
	thisbox.border:Hide()
	thisbox.box:Show()
	thisbox.text:Show()

	thisbox.showing = true
	thisbox.enabled = true

	realign_labels(self)
end

local line_default_color = {1, 1, 1}
local draw_overlay = function(self, this_overlay, overlayData, color)

	local pixel = self.Graphic:GetWidth() / self.TimeScale
	local index = 1
	local r, g, b, a = unpack(color or line_default_color)

	for i = 1, #overlayData, 2 do
		local aura_start = overlayData [i]
		local aura_end = overlayData [i+1]

		local this_block = this_overlay [index]
		if (not this_block) then
			this_block = self.Graphic:CreateTexture(nil, "border")
			table.insert(this_overlay, this_block)
		end
		this_block:SetHeight(self.Graphic:GetHeight())

		this_block:SetPoint("left", self.Graphic, "left", pixel * aura_start, 0)
		if (aura_end) then
			this_block:SetWidth((aura_end-aura_start)*pixel)
		else
			--malformed table
			this_block:SetWidth(pixel*5)
		end

		this_block:SetColorTexture(r, g, b, a or 0.25)
		this_block:Show()

		index = index + 1
	end
end

local chart_panel_add_overlay = function(self, overlayData, color, name, icon)
	if (not self.TimeScale) then
		error("Use SetTime (time) before adding an overlay.")
	end

	if (type(overlayData) == "number") then
		local overlay_index = overlayData
		draw_overlay (self, self.Overlays [self.OverlaysAmount], self.OData [overlay_index][1], self.OData [overlay_index][2])
	else
		local this_overlay = self.Overlays [self.OverlaysAmount]
		if (not this_overlay) then
			this_overlay = {}
			table.insert(self.Overlays, this_overlay)
		end

		draw_overlay (self, this_overlay, overlayData, color)

		table.insert(self.OData, {overlayData, color or line_default_color})
		if (name and self.HeaderShowOverlays) then
			self:AddLabel (color or line_default_color, name, "overlay", #self.OData)
		end
	end

	self.OverlaysAmount = self.OverlaysAmount + 1
end

-- Define the tricube weight function
function calc_cubeweight (i, j, d)
    local w = ( 1 - math.abs((j-i)/d)^3)^3
    if w < 0 then
        w = 0
    end
    return w
end

local calc_lowess_smoothing = function(self, data, bandwidth)
	local length = #data
	local newData = {}

	for i = 1, length do
		local A = 0
		local B = 0
		local C = 0
		local D = 0
		local E = 0

		-- Calculate span of values to be included in the regression
		local jmin = floor(i-bandwidth/2)
		local jmax = ceil (i+bandwidth/2)
		if jmin < 1 then
			jmin = 1
		end
		if jmax > length then
			jmax = length
		end

		-- For all the values in the span, compute the weight and then the linear fit

		for j = jmin, jmax do
			local w = calc_cubeweight (i, j, bandwidth/2)
			local x = j
			local y = data [j]

			A = A + w*x
			B = B + w*y
			C = C + w*x^2
			D = D + w*x*y
			E = E + w
		end

		-- Calculate a (slope) and b (offset) for the linear fit
		local a = (A*B-D*E)/(A^2 - C*E)
		local b = (A*D-B*C)/(A^2 - C*E)

		-- Calculate the smoothed value by the formula y=a*x+b (x <- i)
		newData [i] = a*i+b
	end
	return newData
end

local calc_stddev = function(self, data)
	local total = 0
	for i = 1, #data do
		total = total + data[i]
	end
	local mean = total / #data

	local totalDistance = 0
	for i = 1, #data do
		totalDistance = totalDistance + ((data[i] - mean) ^ 2)
	end

	local deviation = math.sqrt (totalDistance / #data)
	return deviation
end

local SMA_table = {}
local SMA_max = 0
local reset_SMA = function()
	table.wipe(SMA_table)
	SMA_max = 0
end

local calc_SMA
calc_SMA = function(a, b, ...)
	if (b) then
		return calc_SMA (a + b, ...)
	else
		return a
	end
end

local do_SMA = function(value, max_value)
	if (#SMA_table == 10) then
		table.remove(SMA_table, 1)
	end

	SMA_table [#SMA_table + 1] = value

	local new_value = calc_SMA (unpack(SMA_table)) / #SMA_table

	if (new_value > SMA_max) then
		SMA_max = new_value
		return new_value, SMA_max
	else
		return new_value
	end
end

local chart_panel_onresize = function(self)
	local width, height = self:GetSize()
	local spacement = width - 78 - 60
	spacement = spacement / 16

	for i = 1, 17 do
		local label = self.TimeLabels [i]
		label:SetPoint("bottomleft", self, "bottomleft", 78 + ((i-1)*spacement), self.TimeLabelsHeight)
		label.line:SetHeight(height - 45)
	end

	local spacement = (self.Graphic:GetHeight()) / 8
	for i = 1, 8 do
		self ["dpsamt"..i]:SetPoint("TOPLEFT", self, "TOPLEFT", 27, -25 + (-(spacement* (i-1))) )
		self ["dpsamt"..i].line:SetWidth(width-20)
	end

	self.Graphic:SetSize(width - 135, height - 67)
	self.Graphic:SetPoint("topleft", self, "topleft", 108, -35)
end

local chart_panel_add_data = function(self, graphicData, color, name, elapsedTime, lineTexture, smoothLevel, firstIndex)
	local chartPanel = self --chartPanel from the framework CreateChartPanel
	local LibGraphChartFrame = self.Graphic

	local builtData = {}
	local maxValue = graphicData.max_value
	local scaleWidth = 1 / LibGraphChartFrame:GetWidth()
	local content = graphicData

	--smooth the start and end of the chart
	table.insert(content, 1, 0)
	table.insert(content, 1, 0)
	table.insert(content, #content+1, 0)
	table.insert(content, #content+1, 0)

	local index = 3
	local graphMaxDps = math.max(LibGraphChartFrame.max_value, maxValue)

	--do smoothness progress
	if (not smoothLevel) then
		while (index <= #content-2) do
			local value = (content[index-2] + content[index-1] + content[index] + content[index+1] + content[index+2]) / 5 --normalize
			builtData[#builtData+1] = {scaleWidth * (index-2), value / graphMaxDps} -- x and y coords
			index = index + 1
		end

	elseif (smoothLevel == "SHORT") then
		while (index <= #content-2) do
			local value = (content[index] + content[index+1]) / 2
			builtData [#builtData+1] = {scaleWidth*(index-2), value}
			builtData [#builtData+1] = {scaleWidth*(index-2), value}
			index = index + 2
		end

	elseif (smoothLevel == "SMA") then
		reset_SMA()
		while (index <= #content-2) do
			local value, is_new_max_value = do_SMA(content[index], maxValue)
			if (is_new_max_value) then
				maxValue = is_new_max_value
			end
			builtData [#builtData+1] = {scaleWidth * (index-2), value} -- x and y coords
			index = index + 1
		end

	elseif (smoothLevel == -1) then
		while (index <= #content-2) do
			local current = content[index]

			local minus_2 = content[index-2] * 0.6
			local minus_1 = content[index-1] * 0.8
			local plus_1 = content[index+1] * 0.8
			local plus_2 = content[index+2] * 0.6

			local value = (current + minus_2 + minus_1 + plus_1 + plus_2) / 5 --normalize
			builtData [#builtData+1] = {scaleWidth * (index-2), value / graphMaxDps} -- x and y coords
			index = index + 1
		end

	elseif (smoothLevel == 1) then
		index = 2
		while (index <= #content-1) do
			local value = (content[index-1]+content[index]+content[index+1])/3 --normalize
			builtData [#builtData+1] = {scaleWidth*(index-1), value/graphMaxDps} -- x and y coords
			index = index + 1
		end

	elseif (smoothLevel == 2) then
		index = 1
		while (index <= #content) do
			local value = content[index] --do not normalize
			builtData [#builtData+1] = {scaleWidth*(index), value/graphMaxDps} -- x and y coords
			index = index + 1
		end
	end

	table.remove(content, 1)
	table.remove(content, 1)
	table.remove(content, #graphicData)
	table.remove(content, #graphicData)

	if (maxValue > LibGraphChartFrame.max_value) then
		--normalize previous data
		if (LibGraphChartFrame.max_value > 0) then
			local normalizePercent = LibGraphChartFrame.max_value / maxValue
			for dataIndex, Data in ipairs(LibGraphChartFrame.Data) do
				local Points = Data.Points
				for i = 1, #Points do
					Points[i][2] = Points[i][2] * normalizePercent
				end
			end
		end

		LibGraphChartFrame.max_value = maxValue
		chartPanel:SetScale(maxValue)
	end

	table.insert(chartPanel.GData, {builtData, color or line_default_color, lineTexture, maxValue, elapsedTime})
	if (name) then
		chartPanel:AddLabel(color or line_default_color, name, "graphic", #chartPanel.GData)
	end

	local newLineTexture = "Interface\\AddOns\\Details\\Libs\\LibGraph-2.0\\line"

	if (firstIndex) then
		table.insert(LibGraphChartFrame.Data, 1, {Points = builtData, Color = color or line_default_color, lineTexture = newLineTexture, ElapsedTime = elapsedTime})
		LibGraphChartFrame.NeedsUpdate = true
	else
		LibGraphChartFrame:AddDataSeries(builtData, color or line_default_color, nil, newLineTexture)
		LibGraphChartFrame.Data[#LibGraphChartFrame.Data].ElapsedTime = elapsedTime
	end

	local maxTime = 0
	for _, data in ipairs(LibGraphChartFrame.Data) do
		if (data.ElapsedTime > maxTime) then
			maxTime = data.ElapsedTime
		end
	end

	chartPanel:SetTime(maxTime)
	chart_panel_onresize(chartPanel)
end


local chart_panel_vlines_on = function(self)
	for i = 1, 17 do
		local label = self.TimeLabels[i]
		label.line:Show()
	end
end

local chart_panel_vlines_off = function(self)
	for i = 1, 17 do
		local label = self.TimeLabels[i]
		label.line:Hide()
	end
end

local chart_panel_set_title = function(self, title)
	self.chart_title.text = title
end

local chart_panel_mousedown = function(self, button)
	if (button == "LeftButton" and self.can_move) then
		if (not self.isMoving) then
			self:StartMoving()
			self.isMoving = true
		end
	elseif (button == "RightButton" and not self.no_right_click_close) then
		if (not self.isMoving) then
			self:Hide()
		end
	end
end

local chart_panel_mouseup = function(self, button)
	if (button == "LeftButton" and self.isMoving) then
		self:StopMovingOrSizing()
		self.isMoving = nil
	end
end

local chart_panel_hide_close_button = function(self)
	self.CloseButton:Hide()
end

local chart_panel_right_click_close = function(self, value)
	if (type(value) == "boolean") then
		if (value) then
			self.no_right_click_close = nil
		else
			self.no_right_click_close = true
		end
	end
end

function detailsFramework:CreateChartPanel(parent, width, height, name)
	if (not name) then
		name = "DFPanel" .. detailsFramework.PanelCounter
		detailsFramework.PanelCounter = detailsFramework.PanelCounter + 1
	end

	parent = parent or UIParent
	width = width or 800
	height = height or 500

	local chartFrame = CreateFrame("frame", name, parent, "BackdropTemplate")
	chartFrame:SetSize(width or 500, height or 400)
	chartFrame:EnableMouse(true)
	chartFrame:SetMovable(true)

	chartFrame:SetScript("OnMouseDown", chart_panel_mousedown)
	chartFrame:SetScript("OnMouseUp", chart_panel_mouseup)

	chartFrame:SetBackdrop(chart_panel_backdrop)
	chartFrame:SetBackdropColor(.3, .3, .3, .3)

	local closeButton = CreateFrame("Button", nil, chartFrame, "UIPanelCloseButton", "BackdropTemplate")
	closeButton:SetWidth(32)
	closeButton:SetHeight(32)
	closeButton:SetPoint("TOPRIGHT",  chartFrame, "TOPRIGHT", -3, -7)
	closeButton:SetFrameLevel(chartFrame:GetFrameLevel()+1)
	closeButton:SetAlpha(0.9)
	chartFrame.CloseButton = closeButton

	local title = detailsFramework:NewLabel(chartFrame, nil, "$parentTitle", "chart_title", "Chart!", nil, 20, {1, 1, 0})
	title:SetPoint("topleft", chartFrame, "topleft", 110, -13)

	chartFrame.Overlays = {}
	chartFrame.OverlaysAmount = 1

	chartFrame.BoxLabels = {}
	chartFrame.BoxLabelsAmount = 1

	chartFrame.ShowHeader = true
	chartFrame.HeaderOnlyIndicator = false
	chartFrame.HeaderShowOverlays = true

	--graphic
		local g = LibStub:GetLibrary("LibGraph-2.0"):CreateGraphLine (name .. "Graphic", chartFrame, "topleft","topleft", 108, -35, width - 120, height - 67)
		g:SetXAxis (-1,1)
		g:SetYAxis (-1,1)
		g:SetGridSpacing (false, false)
		g:SetGridColor ({0.5,0.5,0.5,0.3})
		g:SetAxisDrawing (false,false)
		g:SetAxisColor({1.0,1.0,1.0,1.0})
		g:SetAutoScale (true)
		g:SetLineTexture ("smallline")
		g:SetBorderSize ("right", 0.001)
		g:SetBorderSize ("left", 0.000)
		g:SetBorderSize ("top", 0.002)
		g:SetBorderSize ("bottom", 0.001)
		g.VerticalLines = {}
		g.max_value = 0

		g:SetLineTexture ("line")

		chartFrame.Graphic = g
		chartFrame.GData = {}
		chartFrame.OData = {}
		chartFrame.ChartFrames = {}

	--div lines
		for i = 1, 8, 1 do
			local line = g:CreateTexture(nil, "overlay")
			line:SetColorTexture(1, 1, 1, .05)
			line:SetWidth(670)
			line:SetHeight(1.1)

			local s = chartFrame:CreateFontString(nil, "overlay", "GameFontHighlightSmall")
			chartFrame ["dpsamt"..i] = s
			s:SetText("100k")
			s:SetPoint("topleft", chartFrame, "topleft", 27, -61 + (-(24.6*i)))

			line:SetPoint("topleft", s, "bottom", -27, 0)
			line:SetPoint("topright", g, "right", 0, 0)
			s.line = line
		end

	--create time labels and the bottom texture to use as a background to these labels
		chartFrame.TimeLabels = {}
		chartFrame.TimeLabelsHeight = 16

		for i = 1, 17 do
			local timeString = chartFrame:CreateFontString(nil, "overlay", "GameFontHighlightSmall")
			timeString:SetText("00:00")
			timeString:SetPoint("bottomleft", chartFrame, "bottomleft", 78 + ((i-1)*36), chartFrame.TimeLabelsHeight)
			chartFrame.TimeLabels [i] = timeString

			local line = chartFrame:CreateTexture(nil, "border")
			line:SetSize(1, height-45)
			line:SetColorTexture(1, 1, 1, .1)
			line:SetPoint("bottomleft", timeString, "topright", 0, -10)
			line:Hide()
			timeString.line = line
		end

		local bottom_texture = detailsFramework:NewImage(chartFrame, nil, 702, 25, "background", nil, nil, "$parentBottomTexture")
		bottom_texture:SetColorTexture(.1, .1, .1, .7)
		bottom_texture:SetPoint("topright", g, "bottomright", 0, 0)
		bottom_texture:SetPoint("bottomleft", chartFrame, "bottomleft", 8, 12)



	chartFrame.SetTime = chart_panel_align_timelabels
	chartFrame.EnableVerticalLines = chart_panel_vlines_on
	chartFrame.DisableVerticalLines = chart_panel_vlines_off
	chartFrame.SetTitle = chart_panel_set_title
	chartFrame.SetScale = chart_panel_set_scale
	chartFrame.Reset = chart_panel_reset
	chartFrame.AddLine = chart_panel_add_data
	chartFrame.CanMove = chart_panel_can_move
	chartFrame.AddLabel = chart_panel_add_label
	chartFrame.AddOverlay = chart_panel_add_overlay
	chartFrame.HideCloseButton = chart_panel_hide_close_button
	chartFrame.RightClickClose = chart_panel_right_click_close
	chartFrame.CalcStdDev = calc_stddev
	chartFrame.CalcLowessSmoothing = calc_lowess_smoothing

	chartFrame:SetScript("OnSizeChanged", chart_panel_onresize)
	chart_panel_onresize(chartFrame)

	return chartFrame
end

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- ~gframe
local gframe_on_enter_line = function(self)
	self:SetBackdropColor(0, 0, 0, 0)

	local parent = self:GetParent()
	local ball = self.ball
	ball:SetBlendMode("ADD")

	local on_enter = parent._onenter_line
	if (on_enter) then
		return on_enter (self, parent)
	end
end

local gframe_on_leave_line = function(self)
	self:SetBackdropColor(0, 0, 0, .6)

	local parent = self:GetParent()
	local ball = self.ball
	ball:SetBlendMode("BLEND")

	local on_leave = parent._onleave_line
	if (on_leave) then
		return on_leave (self, parent)
	end
end

local gframe_create_line = function(self)
	local index = #self._lines+1

	local f = CreateFrame("frame", nil, self, "BackdropTemplate")
	self._lines [index] = f
	f.id = index
	f:SetScript("OnEnter", gframe_on_enter_line)
	f:SetScript("OnLeave", gframe_on_leave_line)

	f:SetWidth(self._linewidth)

	if (index == 1) then
		f:SetPoint("topleft", self, "topleft")
		f:SetPoint("bottomleft", self, "bottomleft")
	else
		local previous_line = self._lines [index-1]
		f:SetPoint("topleft", previous_line, "topright")
		f:SetPoint("bottomleft", previous_line, "bottomright")
	end

	local t = f:CreateTexture(nil, "background")
	t:SetWidth(1)
	t:SetPoint("topright", f, "topright")
	t:SetPoint("bottomright", f, "bottomright")
	t:SetColorTexture(1, 1, 1, .1)
	f.grid = t

	local b = f:CreateTexture(nil, "overlay")
	b:SetTexture([[Interface\COMMON\Indicator-Yellow]])
	b:SetSize(16, 16)
	f.ball = b
	local anchor = CreateFrame("frame", nil, f, "BackdropTemplate")
	anchor:SetAllPoints(b)
	b.tooltip_anchor = anchor

	local spellicon = f:CreateTexture(nil, "artwork")
	spellicon:SetPoint("bottom", b, "bottom", 0, 10)
	spellicon:SetSize(16, 16)
	f.spellicon = spellicon

	local text = f:CreateFontString(nil, "overlay", "GameFontNormal")
	local textBackground = f:CreateTexture(nil, "artwork")
	textBackground:SetSize(30, 16)
	textBackground:SetColorTexture(0, 0, 0, 0.5)
	textBackground:SetPoint("bottom", f.ball, "top", 0, -6)
	text:SetPoint("center", textBackground, "center")
	detailsFramework:SetFontSize(text, 10)
	f.text = text
	f.textBackground = textBackground

	local timeline = f:CreateFontString(nil, "overlay", "GameFontNormal")
	timeline:SetPoint("bottomright", f, "bottomright", -2, 0)
	detailsFramework:SetFontSize(timeline, 8)
	f.timeline = timeline

	return f
end

local gframe_getline = function(self, index)
	local line = self._lines [index]
	if (not line) then
		line = gframe_create_line (self)
	end
	return line
end

local gframe_reset = function(self)
	for i, line in ipairs(self._lines) do
		line:Hide()
	end
	if (self.GraphLib_Lines_Used) then
		for i = #self.GraphLib_Lines_Used, 1, -1 do
			local line = table.remove(self.GraphLib_Lines_Used)
			table.insert(self.GraphLib_Lines, line)
			line:Hide()
		end
	end
end

local gframe_update = function(self, lines)
	local g = LibStub:GetLibrary ("LibGraph-2.0")
	local h = self:GetHeight()/100
	local amtlines = #lines
	local linewidth = self._linewidth

	local max_value = 0
	for i = 1, amtlines do
		if (lines [i].value > max_value) then
			max_value = lines [i].value
		end
	end

	self.MaxValue = max_value

	local o = 1
	local lastvalue = self:GetHeight()/2
	max_value = math.max(max_value, 0.0000001)

	for i = 1, min (amtlines, self._maxlines) do

		local data = lines [i]

		local pvalue = data.value / max_value * 100
		if (pvalue > 98) then
			pvalue = 98
		end
		pvalue = pvalue * h

		g:DrawLine (self, (o-1)*linewidth, lastvalue, o*linewidth, pvalue, linewidth, {1, 1, 1, 1}, "overlay")
		lastvalue = pvalue

		local line = self:GetLine (i)
		line:Show()
		line.ball:Show()

		line.ball:SetPoint("bottomleft", self, "bottomleft", (o*linewidth)-8, pvalue-8)
		line.spellicon:SetTexture(nil)
		line.timeline:SetText(data.text)
		line.timeline:Show()

		if (data.utext) then
			line.text:Show()
			line.textBackground:Show()
			line.text:SetText(data.utext)
		else
			line.text:Hide()
			line.textBackground:Hide()
		end

		line.data = data

		o = o + 1
	end
end

function detailsFramework:CreateGFrame(parent, width, height, lineWidth, onEnter, onLeave, member, name)
	local newGraphicFrame = CreateFrame("frame", name, parent, "BackdropTemplate")
	newGraphicFrame:SetSize(width or 450, height or 150)

	if (member) then
		parent[member] = newGraphicFrame
	end

	newGraphicFrame.CreateLine = gframe_create_line
	newGraphicFrame.GetLine = gframe_getline
	newGraphicFrame.Reset = gframe_reset
	newGraphicFrame.UpdateLines = gframe_update

	newGraphicFrame.MaxValue = 0

	newGraphicFrame._lines = {}

	newGraphicFrame._onenter_line = onEnter
	newGraphicFrame._onleave_line = onLeave

	newGraphicFrame._linewidth = lineWidth or 50
	newGraphicFrame._maxlines = floor(newGraphicFrame:GetWidth() / newGraphicFrame._linewidth)

	return newGraphicFrame
end


------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--options tabs and buttons -dot

function detailsFramework:FindHighestParent(self)
	local highestParent
	if (self:GetParent() == UIParent) then
		highestParent = self
	end

	if (not highestParent) then
		highestParent = self
		for i = 1, 6 do
			local parent = highestParent:GetParent()
			if (parent == UIParent) then
				break
			else
				highestParent = parent
			end
		end
	end

	return highestParent
end

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- ~right ~click to ~close

---comment
---@param parent uiobject
---@param xOffset number?
---@param yOffset number?
---@param color any?
---@param fontSize number?
---@return df_label
function detailsFramework:CreateRightClickToClose(parent, xOffset, yOffset, color, fontSize)
	--default values
	xOffset = xOffset or 0
	yOffset = yOffset or 0
	color = color or "white"
	fontSize = fontSize or 10

	local label = detailsFramework:CreateLabel(parent, "right click to close", fontSize, color)
	label:SetPoint("bottomright", parent, "bottomright", -4 + xOffset, 5 + yOffset)

	return label
end


------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- ~listbox

local simple_list_box_ResetWidgets = function(self)
	for _, widget in ipairs(self.widgets) do
		widget:Hide()
	end
	self.nextWidget = 1
end

local simple_list_box_onenter = function(self, capsule)
	self:GetParent().options.onenter (self, capsule, capsule.value)
end

local simple_list_box_onleave = function(self, capsule)
	self:GetParent().options.onleave (self, capsule, capsule.value)
	GameTooltip:Hide()
end

local simple_list_box_GetOrCreateWidget = function(self)
	local index = self.nextWidget
	local widget = self.widgets [index]
	if (not widget) then
		widget = detailsFramework:CreateButton(self, function()end, self.options.width, self.options.row_height, "", nil, nil, nil, nil, nil, nil, detailsFramework:GetTemplate("button", "OPTIONS_BUTTON_TEMPLATE"))
		widget:SetHook("OnEnter", simple_list_box_onenter)
		widget:SetHook("OnLeave", simple_list_box_onleave)
		widget.textcolor = self.options.textcolor
		widget.textsize = self.options.text_size
		widget.onleave_backdrop = self.options.backdrop_color

		widget.XButton = detailsFramework:CreateButton(widget, function()end, 16, 16)
		widget.XButton:SetPoint("topright", widget.widget, "topright")
		widget.XButton:SetIcon ([[Interface\BUTTONS\UI-Panel-MinimizeButton-Up]], 16, 16, "overlay", nil, nil, 0, -4, 0, false)
		widget.XButton.icon:SetDesaturated(true)

		if (not self.options.show_x_button) then
			widget.XButton:Hide()
		end

		table.insert(self.widgets, widget)
	end
	self.nextWidget = self.nextWidget + 1
	return widget
end

local simple_list_box_RefreshWidgets = function(self)
	self:ResetWidgets()
	local amt = 0
	for value, _ in pairs(self.list_table) do
		local widget = self:GetOrCreateWidget()
		widget:SetPoint("topleft", self, "topleft", 1, -self.options.row_height * (self.nextWidget-2) - 4)
		widget:SetPoint("topright", self, "topright", -1, -self.options.row_height * (self.nextWidget-2) - 4)

		widget:SetClickFunction(self.func, value)

		if (self.options.show_x_button) then
			widget.XButton:SetClickFunction(self.options.x_button_func, value)
			widget.XButton.value = value
			widget.XButton:Show()
		else
			widget.XButton:Hide()
		end

		widget.value = value

		if (self.options.icon) then
			if (type(self.options.icon) == "string" or type(self.options.icon) == "number") then
				local coords = type(self.options.iconcoords) == "table" and self.options.iconcoords or {0, 1, 0, 1}
				widget:SetIcon (self.options.icon, self.options.row_height - 2, self.options.row_height - 2, "overlay", coords)

			elseif (type(self.options.icon) == "function") then
				local icon = self.options.icon (value)
				if (icon) then
					local coords = type(self.options.iconcoords) == "table" and self.options.iconcoords or {0, 1, 0, 1}
					widget:SetIcon (icon, self.options.row_height - 2, self.options.row_height - 2, "overlay", coords)
				end
			end
		else
			widget:SetIcon ("", self.options.row_height, self.options.row_height)
		end

		if (self.options.text) then
			if (type(self.options.text) == "function") then
				local text = self.options.text (value)
				if (text) then
					widget:SetText(text)
				else
					widget:SetText("")
				end
			else
				widget:SetText(self.options.text or "")
			end
		else
			widget:SetText("")
		end

		widget.value = value

		local r, g, b, a = detailsFramework:ParseColors(self.options.backdrop_color)
		widget:SetBackdropColor(r, g, b, a)

		widget:Show()
		amt = amt + 1
	end
	if (amt == 0) then
		self.EmptyLabel:Show()
	else
		self.EmptyLabel:Hide()
	end
end

local backdrop = {bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", tile = true, tileSize = 16, edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1}
local default_options = {
	height = 400,
	row_height = 16,
	width = 230,
	icon = false,
	text = "",
	text_size = 10,
	textcolor = "wheat",

	backdrop_color = {1, 1, 1, .5},
	panel_border_color = {0, 0, 0, 0.5},

	onenter = function(self, capsule)
		if (capsule) then
			capsule.textcolor = "white"
		end
	end,
	onleave = function(self, capsule)
		if (capsule) then
			capsule.textcolor = self:GetParent().options.textcolor
		end
		GameTooltip:Hide()
	end,
}

local simple_list_box_SetData = function(self, t)
	self.list_table = t
end

function detailsFramework:CreateSimpleListBox(parent, name, title, emptyText, listTable, onClick, options)
	local scroll = CreateFrame("frame", name, parent, "BackdropTemplate")

	scroll.ResetWidgets = simple_list_box_ResetWidgets
	scroll.GetOrCreateWidget = simple_list_box_GetOrCreateWidget
	scroll.Refresh = simple_list_box_RefreshWidgets
	scroll.SetData = simple_list_box_SetData
	scroll.nextWidget = 1
	scroll.list_table = listTable

	scroll.func = function(self, button, value)
		detailsFramework:QuickDispatch(onClick, value)
		scroll:Refresh()
	end
	scroll.widgets = {}

	detailsFramework:ApplyStandardBackdrop(scroll)

	scroll.options = options or {}
	self.table.deploy(scroll.options, default_options)

	if (scroll.options.x_button_func) then
		local original_X_function = scroll.options.x_button_func
		scroll.options.x_button_func = function(self, button, value)
			detailsFramework:QuickDispatch(original_X_function, value)
			scroll:Refresh()
		end
	end

	scroll:SetBackdropBorderColor(unpack(scroll.options.panel_border_color))

	scroll:SetSize(scroll.options.width + 2, scroll.options.height)

	local name = detailsFramework:CreateLabel(scroll, title, 12, "silver")
	name:SetTemplate(detailsFramework:GetTemplate("font", "OPTIONS_FONT_TEMPLATE"))
	name:SetPoint("bottomleft", scroll, "topleft", 0, 2)
	scroll.Title = name

	local emptyLabel = detailsFramework:CreateLabel(scroll, emptyText, 12, "gray")
	emptyLabel:SetAlpha(.6)
	emptyLabel:SetSize(scroll.options.width-10, scroll.options.height)
	emptyLabel:SetPoint("center", 0, 0)
	emptyLabel:Hide()
	emptyLabel.align = "center"
	scroll.EmptyLabel = emptyLabel

	return scroll
end


------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- ~scrollbox

---@class df_scrollbox : scrollframe, df_sortmixin, df_scrollboxmixin
---@field data table
---@field Header df_headerframe?
---@field LineAmount number
---@field LineHeight number
---@field IsFauxScroll boolean?
---@field HideScrollBar boolean?
---@field Frames frame[]
---@field ReajustNumFrames boolean?
---@field DontHideChildrenOnPreRefresh boolean
---@field refresh_func fun(self:df_scrollbox, data:table, offset:number, numlines:number)
---@field Refresh fun(self:df_scrollbox)
---@field CreateLineFunc fun(self:df_scrollbox, index:number)?
---@field CreateLine fun(self:df_scrollbox, func:function)
---@field SetData fun(self:df_scrollbox, data:table)
---@field GetData fun(self:df_scrollbox): table
---@field OnSetData fun(self:df_scrollbox, data:table)? if exists, this function is called after the SetData with the same parameters
---@field ScrollBar statusbar
---@field RefreshMe fun(...:any) virtual, implement if the data need to be manipulated, must call :SetData() and :Refresh()

---create a scrollbox with the methods :Refresh() :SetData() :CreateLine()
---@param parent table
---@param name string
---@param refreshFunc function
---@param data table
---@param width number
---@param height number
---@param lineAmount number
---@param lineHeight number
---@param createLineFunc function?
---@param autoAmount boolean?
---@param noScroll boolean?
---@param noBackdrop boolean?
---@return df_scrollbox
function detailsFramework:CreateScrollBox(parent, name, refreshFunc, data, width, height, lineAmount, lineHeight, createLineFunc, autoAmount, noScroll, noBackdrop)
	--create the scrollframe, it is the base of the scrollbox
	---@type df_scrollbox
	local scroll = CreateFrame("scrollframe", name, parent, "FauxScrollFrameTemplate, BackdropTemplate")

	--apply the standard background color
	if (not noBackdrop) then
		detailsFramework:ApplyStandardBackdrop(scroll)
	end

	scroll:SetSize(width, height)
	scroll.LineAmount = lineAmount
	scroll.LineHeight = lineHeight
	scroll.IsFauxScroll = true
	scroll.HideScrollBar = noScroll
	scroll.Frames = {}
	scroll.ReajustNumFrames = autoAmount
	scroll.CreateLineFunc = createLineFunc
	scroll.DontHideChildrenOnPreRefresh = false

	detailsFramework:Mixin(scroll, detailsFramework.SortFunctions)
	detailsFramework:Mixin(scroll, detailsFramework.ScrollBoxFunctions)

	scroll.refresh_func = refreshFunc
	scroll.data = data

	scroll:SetScript("OnVerticalScroll", scroll.OnVerticalScroll)
	scroll:SetScript("OnSizeChanged", detailsFramework.ScrollBoxFunctions.OnSizeChanged)

	return scroll
end


------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- ~resizers

--these options are copied to the object with object:BuildOptionsTable()
---@class df_resizergrip_options
---@field width number?
---@field height number?
---@field use_default_scripts boolean?
---@field should_mirror_left_texture boolean?
---@field normal_texture string|number?
---@field highlight_texture string|number?
---@field pushed_texture string|number?

local rezieGripOptions = {
	width = 32,
	height = 32,
	use_default_scripts = true,
	should_mirror_left_texture = true,
	normal_texture = [[Interface\CHATFRAME\UI-ChatIM-SizeGrabber-Up]],
	highlight_texture = [[Interface\CHATFRAME\UI-ChatIM-SizeGrabber-Highlight]],
	pushed_texture = [[Interface\CHATFRAME\UI-ChatIM-SizeGrabber-Down]],
}

---@class df_resizergrip : button, df_optionsmixin
---@field options df_resizergrip_options

---create the two resize grips for a frame, one in the bottom left and another in the bottom right
---@param parent frame
---@param options df_resizergrip_options?
---@param leftResizerName string|nil
---@param rightResizerName string|nil
---@return frame, frame
function detailsFramework:CreateResizeGrips(parent, options, leftResizerName, rightResizerName)
	local parentName = parent:GetName()

	---@type df_resizergrip
	local leftResizer = _G.CreateFrame("button", leftResizerName or (parentName and "$parentLeftResizer"), parent, "BackdropTemplate")
	---@type df_resizergrip
	local rightResizer = _G.CreateFrame("button", rightResizerName or (parentName and "$parentRightResizer"), parent, "BackdropTemplate")

	leftResizer:SetFrameLevel(parent:GetFrameLevel() + 20)
	rightResizer:SetFrameLevel(parent:GetFrameLevel() + 20)

	detailsFramework:Mixin(leftResizer, detailsFramework.OptionsFunctions)
	detailsFramework:Mixin(rightResizer, detailsFramework.OptionsFunctions)
	leftResizer:BuildOptionsTable(rezieGripOptions, options)
	rightResizer:BuildOptionsTable(rezieGripOptions, options)

	leftResizer:SetPoint("bottomleft", parent, "bottomleft", 0, 0)
	rightResizer:SetPoint("bottomright", parent, "bottomright", 0, 0)
	leftResizer:SetSize(leftResizer.options.width, leftResizer.options.height)
	rightResizer:SetSize(leftResizer.options.width, leftResizer.options.height)

	rightResizer:SetNormalTexture(rightResizer.options.normal_texture)
	rightResizer:SetHighlightTexture(rightResizer.options.highlight_texture)
	rightResizer:SetPushedTexture(rightResizer.options.pushed_texture)

	leftResizer:SetNormalTexture(leftResizer.options.normal_texture)
	leftResizer:SetHighlightTexture(leftResizer.options.highlight_texture)
	leftResizer:SetPushedTexture(leftResizer.options.pushed_texture)

	if (leftResizer.options.should_mirror_left_texture) then
		leftResizer:GetNormalTexture():SetTexCoord(1, 0, 0, 1)
		leftResizer:GetHighlightTexture():SetTexCoord(1, 0, 0, 1)
		leftResizer:GetPushedTexture():SetTexCoord(1, 0, 0, 1)
	end

	if (leftResizer.options.use_default_scripts) then
		leftResizer:SetScript("OnMouseDown", function(self)
			parent:StartSizing("BOTTOMLEFT")
		end)
		leftResizer:SetScript("OnMouseUp", function(self)
			parent:StopMovingOrSizing()
		end)

		rightResizer:SetScript("OnMouseDown", function(self)
			parent:StartSizing("BOTTOMRIGHT")
		end)
		rightResizer:SetScript("OnMouseUp", function(self)
			parent:StopMovingOrSizing()
		end)
	end

	return leftResizer, rightResizer
end


------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- ~standard backdrop
---this is the standard backdrop for detailsframework, it's a dark-ish color semi transparent with a thin opaque black border
---for the background it uses UI-Tooltip-Background with detailsFramework:GetDefaultBackdropColor() color
---for the border it uses Interface\Buttons\WHITE8X8
---also creates an additional texture frame.__background = texture with the same setting of the backdrop background
---@param self table
---@param frame frame
---@param bUseSolidColor boolean?
---@param alphaScale number?
function detailsFramework:ApplyStandardBackdrop(frame, bUseSolidColor, alphaScale)
	alphaScale = alphaScale or 0.95

	if (not frame.SetBackdrop)then
		--print(debugstack(1,2,1))
		Mixin(frame, BackdropTemplateMixin)
	end

	local red, green, blue, alpha = detailsFramework:GetDefaultBackdropColor()

	if (bUseSolidColor) then
		frame:SetBackdrop({edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1, bgFile = [[Interface\Buttons\WHITE8X8]], tileSize = 32, tile = true})
		frame:SetBackdropColor(red, green, blue, 0.872)
		frame:SetBackdropBorderColor(0, 0, 0, 0.95)

	else
		frame:SetBackdrop({edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1, bgFile = [[Interface\Tooltips\UI-Tooltip-Background]], tileSize = 64, tile = true})
		frame:SetBackdropColor(red, green, blue, alpha * alphaScale)
		frame:SetBackdropBorderColor(0, 0, 0, 0.95)
	end

	if (not frame.__background) then
		frame.__background = frame:CreateTexture(nil, "border", nil, -6)
		frame.__background:SetColorTexture(red, green, blue)
		frame.__background:SetAllPoints()
	end

	frame.__background:SetAlpha(alpha * alphaScale)
end

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
---boss selector

---@class df_bossscrollselector : df_scrollbox
---@field options df_bossscrollselector_options
---@field callback function
---@field callback_args any[]
---@field SetCallback fun(self:df_bossscrollselector, callback:function, ...)

---@class df_bossscrollselector_options : table
---@field width number
---@field height number
---@field line_height number
---@field line_amount number
---@field show_icon boolean
---@field show_name boolean
---@field name_size number
---@field name_color any
---@field icon_coords table
---@field icon_size table

---@class df_bossscrollselector_line : button
---@field index number
---@field bossId number
---@field bossIcon texture
---@field bossName fontstring
---@field bossRaidName fontstring
---@field selectedInidicator texture

---@type df_bossscrollselector_options
local bossSelectorDefaultOptions = {
	width = 200,
	height = 400,
	line_height = 40,
	line_amount = 10,
	show_icon = true,
	icon_coords = {0, 1, 0, 1},
	icon_size = {70, 36},
	show_name = false,
	name_size = 10,
	name_color = "wheat",
}

detailsFramework.BossScrollSelectorMixin = {
	---@param self df_bossscrollselector
	---@param index number
	---@return frame
	CreateLine = function(self, index)
		---@type df_bossscrollselector_line
		local line = CreateFrame("button", "$parentLine" .. index, self, "BackdropTemplate")

		line:SetPoint("topleft", self, "topleft", 1, -((index-1) * (self.options.line_height+1)) - 1)
		line:SetSize(self.options.width - 2, self.options.line_height)
		line:RegisterForClicks("LeftButtonDown", "RightButtonDown")
		detailsFramework:ApplyStandardBackdrop(line)

		--line:SetScript("OnEnter", onEnterBossLine)
		--line:SetScript("OnLeave", onLeaveBossLine)

		line.index = index

		local selectedInidicator = line:CreateTexture(nil, "border")
		selectedInidicator:SetPoint("topleft", line, "topleft", 1, -1)
		selectedInidicator:SetPoint("bottomright", line, "bottomright", -1, 1)
		selectedInidicator:SetColorTexture(1, 1, 1, 0.4)
		selectedInidicator:Hide()
		line.selectedInidicator = selectedInidicator

		--boss icon
		local bossIcon = line:CreateTexture("$parentIcon", "overlay")
		bossIcon:SetSize(self.options.line_height + 30, self.options.line_height-4)
		bossIcon:SetPoint("left", line, "left", 2, 0)
		line.bossIcon = bossIcon

		local bossName = line:CreateFontString(nil, "overlay", "GameFontNormal")
		local bossRaid = line:CreateFontString(nil, "overlay", "GameFontNormal")
		bossName:SetPoint("left", bossIcon, "right", -8, 6)
		bossRaid:SetPoint("topleft", bossName, "bottomleft", 0, -2)
		detailsFramework:SetFontSize(bossName, 10)
		detailsFramework:SetFontSize(bossRaid, 9)
		detailsFramework:SetFontColor(bossRaid, "silver")

		detailsFramework:CreateHighlightTexture(line)

		line.bossName = bossName
		line.bossRaidName = bossRaid

		return line
	end,

	---@param self df_bossscrollselector
	---@param data df_encounterinfo[]
	---@param offset number
	---@param totalLines number
	Refresh = function(self, data, offset, totalLines)
		--update boss scroll
		for i = 1, totalLines do
			local index = i + offset
			local thisData = data[index]
			if (thisData) then
				---@type df_bossscrollselector_line
				---@diagnostic disable-next-line: assign-type-mismatch
				local line = self:GetLine(i)

				local instanceId = thisData.instanceId
				---@type df_instanceinfo
				local instanceData = detailsFramework.Ejc.GetInstanceInfo(instanceId)

				local bossName = thisData.name
				local bossRaidName = instanceData.name
				local bossIcon = thisData.creatureIcon
				local bossIconCoords = thisData.creatureIconCoords
				local bossId = thisData.journalEncounterId

				--update the line
				line.bossName:SetText(bossName)
				line.bossName:SetPoint("left", line.bossIcon, "right", -8, 6)
				detailsFramework:TruncateText(line.bossName, 130)
				line.bossRaidName:SetText(bossRaidName)
				detailsFramework:TruncateText(line.bossRaidName, 130)

				line.bossIcon:SetTexture(bossIcon)
				line.bossIcon:SetSize(unpack(self.options.icon_size))
				line.bossIcon:SetTexCoord(unpack(bossIconCoords))

				line.bossIcon:SetPoint("left", line, "left", 2, 0)
				line.bossName:Show()
				line.bossRaidName:Show()

				line.bossId = bossId
				line.index = index
				line:Show()
			end
		end
	end,

	SetCallback = function(self, callback, ...)
		self.callback_args = {...}
		self.callback = callback

		local function onClick(line)
			callback(line.index, unpack(self.callback_args))
		end

		local allLines = self:GetLines()
		for index, line in ipairs(allLines) do
			line:SetScript("OnClick", onClick)
		end
	end
}

---create a scrollbox with a list of bosses from an instance
---@param instanceId any accept instanceId, ejInstanceId or instanceName
---@param parent uiobject
---@param name string|nil
---@param options df_bossscrollselector_options?
---@param callback function? the function to call when a boss is clicked
---@param ... any additional arguments to pass to the callback
---@return df_bossscrollselector
function detailsFramework:CreateBossScrollSelectorForInstance(instanceId, parent, name, options, callback, ...)
	local refreshFunc = detailsFramework.BossScrollSelectorMixin.Refresh
	local createLineFunc = detailsFramework.BossScrollSelectorMixin.CreateLine

	---@type df_encounterinfo[]
	local arrayOfBosses = detailsFramework.Ejc.GetAllEncountersFromInstance(instanceId)

	options = options or {}
	---@cast options df_bossscrollselector_options
	detailsFramework.table.deploy(options, bossSelectorDefaultOptions)

	---@type df_bossscrollselector
	---@diagnostic disable-next-line: assign-type-mismatch
	local bossScrollFrame = detailsFramework:CreateScrollBox(parent, name, refreshFunc, arrayOfBosses, options.width, options.height, options.line_amount, options.line_amount)
	bossScrollFrame.options = options
	bossScrollFrame.SetCallback = detailsFramework.BossScrollSelectorMixin.SetCallback

	--create the scrollbox lines
	for i = 1, options.line_amount do
		bossScrollFrame:CreateLine(createLineFunc)
	end

	if (callback) then
		bossScrollFrame:SetCallback(callback, ...)
	end

	detailsFramework:ReskinSlider(bossScrollFrame)
	detailsFramework:ApplyStandardBackdrop(bossScrollFrame)

	bossScrollFrame:Refresh()
	return bossScrollFrame
end


------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- ~title bar

detailsFramework.TitleFunctions = {
	SetTitle = function(self, titleText, titleColor, font, size)
		local titleLabel = self.TitleLabel or self.Text

		titleLabel:SetText(titleText or titleLabel:GetText())

		if (titleColor) then
			local r, g, b, a = detailsFramework:ParseColors(titleColor)
			titleLabel:SetTextColor(r, g, b, a)
		end

		if (font) then
			detailsFramework:SetFontFace (titleLabel, font)
		end

		if (size) then
			detailsFramework:SetFontSize(titleLabel, size)
		end
	end
}

---@class df_titlebar : frame
---@field TitleBar frame
---@field TitleLabel fontstring
---@field CloseButton button
---@field SetTitle fun(self:df_titlebar, titleText:string, titleColor:any, font:string, size:number)

---create a title bar with a font string in the center and a close button in the right side
---@param parent frame
---@param titleText string
---@return df_titlebar
function detailsFramework:CreateTitleBar(parent, titleText)
	local titleBar = CreateFrame("frame", parent:GetName() and parent:GetName() .. "TitleBar" or nil, parent, "BackdropTemplate")
	titleBar:SetPoint("topleft", parent, "topleft", 2, -3)
	titleBar:SetPoint("topright", parent, "topright", -2, -3)
	titleBar:SetHeight(20)
	titleBar:SetBackdrop(SimplePanel_frame_backdrop) --it's an upload from this file
	titleBar:SetBackdropColor(.2, .2, .2, 1)
	titleBar:SetBackdropBorderColor(0, 0, 0, 1)

	local closeButton = CreateFrame("button", titleBar:GetName() and titleBar:GetName() .. "CloseButton" or nil, titleBar, "BackdropTemplate")
	closeButton:SetSize(16, 16)

	closeButton:SetNormalTexture([[Interface\GLUES\LOGIN\Glues-CheckBox-Check]])
	closeButton:SetHighlightTexture([[Interface\GLUES\LOGIN\Glues-CheckBox-Check]])
	closeButton:SetPushedTexture([[Interface\GLUES\LOGIN\Glues-CheckBox-Check]])
	closeButton:GetNormalTexture():SetDesaturated(true)
	closeButton:GetHighlightTexture():SetDesaturated(true)
	closeButton:GetPushedTexture():SetDesaturated(true)

	closeButton:SetAlpha(0.7)
	closeButton:SetScript("OnClick", simple_panel_close_click) --upvalue from this file

	local titleLabel = titleBar:CreateFontString(titleBar:GetName() and titleBar:GetName() .. "TitleText" or nil, "overlay", "GameFontNormal")
	titleLabel:SetTextColor(detailsFramework:ParseColors("gold"))
	titleLabel:SetText(titleText or "")

	--anchors
	closeButton:SetPoint("right", titleBar, "right", -2, 0)
	titleLabel:SetPoint("center", titleBar, "center")

	--members
	parent.TitleBar = titleBar
	parent.CloseButton = closeButton
	parent.TitleLabel = titleLabel
	parent.SetTitle = titleBar.SetTitle

	titleBar.TitleBar = titleBar --to fit documentation
	titleBar.CloseButton = closeButton
	titleBar.Text = titleLabel

	detailsFramework:Mixin(parent, detailsFramework.TitleFunctions)

	return titleBar
end

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--radio group

local default_radiogroup_options = {
	width = 1,
	height = 1,
	backdrop = {edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1, bgFile = [[Interface\Tooltips\UI-Tooltip-Background]], tileSize = 64, tile = true},
	backdrop_color = {0, 0, 0, 0.2},
	backdrop_border_color = {0.1, 0.1, 0.1, .2},
	is_radio = false,
	text_color = {1, 0.8196, 0, 1},
	text_size = 10,
	text_outline = "NONE",
}

---@class df_radiogroup_checkbox : df_checkbox
---@field Label df_label
---@field Icon texture
---@field _optionid number
---@field _set function
---@field _callback function
---@field _param any
---@field __width number
---@field __height number

---@class df_radiogroupmixin : table
---@field allCheckBoxes df_radiogroup_checkbox[]
---@field SetFadeState fun(self:df_checkboxgroup, state:boolean)
---@field Disable fun(self:df_checkboxgroup)
---@field Enable fun(self:df_checkboxgroup)
---@field DeselectAll fun(self:df_checkboxgroup)
---@field Select fun(self:df_checkboxgroup, checkboxId:number)
---@field GetSelected fun(self:df_checkboxgroup):number
---@field FadeIn fun(self:df_checkboxgroup)
---@field FadeOut fun(self:df_checkboxgroup)
---@field GetAllCheckboxes fun(self:df_checkboxgroup):df_radiogroup_checkbox[]
---@field ExecuteOnAllCheckboxes fun(self:df_checkboxgroup, func:function) fun(radioGroup, checkbox, param, optionId)
---@field GetCheckbox fun(self:df_checkboxgroup, checkboxId:number):df_radiogroup_checkbox
---@field CreateCheckbox fun(self:df_checkboxgroup):df_radiogroup_checkbox
---@field ResetAllCheckboxes fun(self:df_checkboxgroup)
---@field RadioOnClick fun(checkbox:df_radiogroup_checkbox, fixedParam:any, value:boolean)
---@field RefreshCheckbox fun(self:df_checkboxgroup, checkbox:df_radiogroup_checkbox, optionTable:table, optionId:number)

local radio_checkbox_onclick_extraspace = function(self)
	self:GetParent():GetObject():OnSwitch(self:GetParent():GetObject()._param, not self:GetParent():GetObject():GetValue()) --as the parent of self is a Switch object from DetailsFramework, it need to run :GetObject() to get the capsule object
	self:GetParent():GetObject():GetParent():Refresh()
end

---@type df_radiogroupmixin
detailsFramework.RadioGroupCoreFunctions = {
	allCheckBoxes = {},

	Disable = function(self)
		local checkBoxList = self:GetAllCheckboxes()
		for _, checkbox in ipairs(checkBoxList) do
			checkbox:Disable()
		end
	end,

	Enable = function(self)
		local checkBoxList = self:GetAllCheckboxes()
		for _, checkbox in ipairs(checkBoxList) do
			checkbox:Enable()
		end
	end,

	DeselectAll = function(self)
		local checkBoxList = self:GetAllCheckboxes()
		for _, checkbox in ipairs(checkBoxList) do
			checkbox:SetValue(false)
		end
	end,

	FadeIn = function(self)
		local checkBoxList = self:GetAllCheckboxes()
		for _, checkbox in ipairs(checkBoxList) do
			checkbox:SetAlpha(1)
		end
	end,

	FadeOut = function(self)
		local checkBoxList = self:GetAllCheckboxes()
		for _, checkbox in ipairs(checkBoxList) do
			checkbox:SetAlpha(.7)
		end
	end,

	SetFadeState = function(self, state)
		if (state) then
			self:FadeIn()
		else
			self:FadeOut()
		end
	end,

	GetAllCheckboxes = function(self)
		return self.allCheckBoxes
	end,

	GetCheckbox = function(self, checkboxId)
		local allCheckboxes = self:GetAllCheckboxes()
		local checkbox = allCheckboxes[checkboxId]
		if (not checkbox) then
			checkbox = self:CreateCheckbox()
		end
		return checkbox
	end,

	CreateCheckbox = function(self)
		local checkbox = detailsFramework:CreateSwitch(self, function()end, false)
		checkbox:SetTemplate(detailsFramework:GetTemplate("switch", "OPTIONS_CHECKBOX_BRIGHT_TEMPLATE"))
		checkbox:SetAsCheckBox()

		local extraSpaceToClick = CreateFrame("button", "$parentExtraSpaceToClick", checkbox.widget)
		extraSpaceToClick:SetPoint("topleft", checkbox.widget, "topright", 0, 0)
		extraSpaceToClick:SetPoint("bottomleft", checkbox.widget, "bottomright", 0, 0)
		extraSpaceToClick:SetScript("OnClick", radio_checkbox_onclick_extraspace)
		checkbox.extraSpaceToClick = extraSpaceToClick

		if (self.options.rounded_corner_preset) then
			checkbox:SetBackdrop(nil)
			detailsFramework:AddRoundedCornersToFrame(checkbox, self.options.rounded_corner_preset)
		end

		if (self.options.checked_texture) then
			checkbox:SetCheckedTexture(self.options.checked_texture, self.options.checked_texture_offset_x, self.options.checked_texture_offset_y)
		end

		checkbox.Icon = detailsFramework:CreateImage(checkbox, "", 16, 16)
		checkbox.Label = detailsFramework:CreateLabel(checkbox, "")
		self.allCheckBoxes[#self.allCheckBoxes + 1] = checkbox

		if (self.options.on_create_checkbox) then
			--use dispatch
			detailsFramework:QuickDispatch(self.options.on_create_checkbox, self, checkbox)
		end

		return checkbox
	end,

	ExecuteOnAllCheckboxes = function(self, func)
		local checkBoxList = self:GetAllCheckboxes()
		for _, checkbox in ipairs(checkBoxList) do
			detailsFramework:QuickDispatch(func, self, checkbox, checkbox._param, checkbox._optionid)
		end
	end,

	ResetAllCheckboxes = function(self)
		local radioCheckboxes = self:GetAllCheckboxes()
		for i = 1, #radioCheckboxes do
			local checkBox = radioCheckboxes[i]
			checkBox:Hide()
		end
	end,

	--if the list of checkboxes are a radio group
	RadioOnClick = function(checkbox, fixedParam, value)
		--turn off all checkboxes
		---@type df_checkboxgroup
		local radioGroup = checkbox:GetParent()
		radioGroup:DeselectAll()

		--turn on the clicked checkbox
		checkbox:SetValue(true)

		--callback
		if (checkbox._callback) then
			detailsFramework:QuickDispatch(checkbox._callback, fixedParam, checkbox._optionid)
		end

		if (radioGroup.options.on_click_option) then
			detailsFramework:QuickDispatch(radioGroup.options.on_click_option, radioGroup, checkbox, checkbox._param, checkbox._optionid)
		end
	end,

	RefreshCheckbox = function(self, checkbox, optionTable, optionId)
		checkbox = checkbox.GetCapsule and checkbox:GetCapsule() or checkbox
		---@cast checkbox df_radiogroup_checkbox

		local width, height = optionTable.width or 20, optionTable.height or 20
		checkbox:SetSize(width, height)

		local setFunc = self.options.is_radio and self.RadioOnClick or optionTable.set
		checkbox:SetSwitchFunction(setFunc)
		checkbox._callback = optionTable.callback
		checkbox._set = self.options.is_radio and optionTable.callback or optionTable.set
		checkbox._optionid = optionId
		checkbox._param = optionTable.param or optionId
		checkbox:SetFixedParameter(optionTable.param or optionId)

		local bIsChecked = (type(optionTable.selected) == "boolean" and optionTable.selected) or (type(optionTable.get) == "function" and detailsFramework:Dispatch(optionTable.get)) or false
		checkbox:SetValue(bIsChecked)

		checkbox.Label.text = optionTable.name
		checkbox.Label.textsize = optionTable.text_size or self.options.text_size
		checkbox.Label.textcolor = self.options.text_color
		checkbox.Label.outline = self.options.text_outline

		checkbox.Label:ClearAllPoints()

		if (optionTable.texture) then
			checkbox.Icon:SetTexture(optionTable.texture)
			checkbox.Icon:SetSize(width, height)
			checkbox.Icon:SetPoint("left", checkbox, "right", self.AnchorOptions.icon_offset_x, 0)

			if (self.options.text_padding) then
				checkbox.Label:SetPoint("left", checkbox.Icon, "right", self.options.text_padding, 0)
			else
				checkbox.Label:SetPoint("left", checkbox.Icon, "right", 2, 0)
			end

			checkbox.tooltip = optionTable.tooltip

			if (optionTable.texcoord) then
				checkbox.Icon:SetTexCoord(unpack(optionTable.texcoord))
			else
				checkbox.Icon:SetTexCoord(0, 1, 0, 1)
			end

			if (optionTable.mask) then
				if (not checkbox.Icon.Mask) then
					checkbox.Icon.Mask = checkbox:CreateMaskTexture(nil, "overlay")
					checkbox.Icon.Mask:SetAllPoints(checkbox.Icon.widget)
					checkbox.Icon.Mask:SetTexture(optionTable.mask)
					checkbox.Icon:AddMaskTexture(checkbox.Icon.Mask)
				end
				checkbox.Icon.Mask:SetTexture(optionTable.mask)
			else
				--checkbox.Icon:SetMask("")
				if (checkbox.Icon.Mask) then
					checkbox.Icon.Mask:SetTexture("")
				end
			end
		else
			checkbox.Icon:SetTexture("")
			if (self.options.text_padding) then
				checkbox.Label:SetPoint("left", checkbox, "right", self.options.text_padding, 0)
			else
				checkbox.Label:SetPoint("left", checkbox, "right", 2, 0)
			end
		end

		checkbox.__width = width + (checkbox.Icon:IsShown() and (checkbox.Icon:GetWidth() + (self.AnchorOptions.icon_offset_x or 0))) + (checkbox.Label:GetUnboundedStringWidth()) + (self.options.text_padding or 2)
		checkbox.widget.__width = checkbox.__width

		checkbox.__height = height + (checkbox.Icon:IsShown() and (checkbox.Icon:GetHeight() + 2))
		checkbox.widget.__height = checkbox.__height

		if (optionTable.checkbox_template) then
			checkbox:SetTemplate(optionTable.checkbox_template)
		end
	end,

	Refresh = function(self)
		self:ResetAllCheckboxes()
		local radioOptions = self:GetOptions()
		local totalWidth = 0
		local maxHeight = 0

		for optionId, optionsTable in ipairs(radioOptions) do
			local checkbox = self:GetCheckbox(optionId)
			checkbox.OptionID = optionId
			checkbox:Show()
			self:RefreshCheckbox(checkbox, optionsTable, optionId)
			totalWidth = totalWidth + checkbox.__width

			checkbox.extraSpaceToClick:SetWidth(checkbox.__width - checkbox:GetWidth()) -- total __width contains checkbox size which we don't need here

			if (checkbox:GetHeight() > maxHeight) then
				maxHeight = checkbox:GetHeight()
			end
		end

		if (self.AnchorOptions.min_width) then
			totalWidth = math.max(self.AnchorOptions.min_width * #radioOptions, totalWidth)
		end

		if (not self.AnchorOptions.width) then
			self:SetWidth(totalWidth)
		else
			self:SetWidth(self.AnchorOptions.width)
		end

		if (not self.AnchorOptions.height) then
			self:SetHeight(maxHeight)
		else
			self:SetHeight(self.AnchorOptions.height)
		end

		self.AnchorOptions.start_y = -5

		--sending false to automatically use the radio group children
		self:ArrangeFrames(false, self.AnchorOptions)
	end,

	Select = function(self, option)
		local allCheckBoxes = self:GetAllCheckboxes()
		local thisCheckbox = allCheckBoxes[option]
		if (thisCheckbox) then
			local callbackFunc = thisCheckbox:GetSwitchFunction()
			if (callbackFunc) then
				detailsFramework.RadioGroupCoreFunctions.RadioOnClick(thisCheckbox, thisCheckbox:GetFixedParameter(), true)
			end
		end
	end,

	GetSelected = function(self)
		local allCheckBoxes = self:GetAllCheckboxes()
		for i = 1, #allCheckBoxes do
			local thisCheckbox = allCheckBoxes[i]
			if (thisCheckbox:GetValue()) then
				return thisCheckbox._optionid, thisCheckbox:GetFixedParameter()
			end
		end
		return 0
	end,

	SetOptions = function(self, radioOptions)
		self.RadioOptionsTable = radioOptions
		self:Refresh()
	end,

	GetOptions = function(self)
		return self.RadioOptionsTable
	end,
}

---@class df_radiooptions : table
---@field name string|table? can be a regular string or a locTable
---@field get fun()?
---@field set fun(self:df_radiooptions, param, value)?
---@field param any?
---@field texture string?
---@field texcoord table?
---@field mask any?
---@field width number?
---@field height number?
---@field text_size number?
---@field callback fun()?
---@field backdrop table?
---@field backdrop_color table?
---@field backdrop_border_color table?
---@field checkbox_template string?
---@field on_click_option fun(self:df_checkboxgroup, checkbox:df_radiogroup_checkbox, param:any, optionId:number)?
---@field on_create_checkbox fun(self:df_checkboxgroup, checkbox:df_radiogroup_checkbox)?

--[=[
	radionOptions: an index table with options for the radio group {name = "", set = func (self, param, value), param = value, get = func, texture = "", texcoord = {}}
		set function receives as self the checkbox, use :GetParent() to get the radion group frame
		if get function return nil or false the checkbox isn't checked
	name: the name of the frame
	options: override options for default_radiogroup_options table
	anchorOptions: override options for default_framelayout_options table
--]=]

---@class df_checkboxgroup : frame, df_optionsmixin, df_radiogroupmixin, df_framelayout

---@param parent frame
---@param radioOptions table
---@param name string?
---@param options table?
---@param anchorOptions table?
---@return df_checkboxgroup
function detailsFramework:CreateCheckboxGroup(parent, radioOptions, name, options, anchorOptions)
	local newCheckboxGroup = CreateFrame("frame", name, parent, "BackdropTemplate")

	detailsFramework:Mixin(newCheckboxGroup, detailsFramework.OptionsFunctions)
	detailsFramework:Mixin(newCheckboxGroup, detailsFramework.RadioGroupCoreFunctions)
	detailsFramework:Mixin(newCheckboxGroup, detailsFramework.LayoutFrame)

	newCheckboxGroup.allCheckBoxes = {}

	newCheckboxGroup:BuildOptionsTable(default_radiogroup_options, options)

	newCheckboxGroup:SetBackdrop(newCheckboxGroup.options.backdrop)
	newCheckboxGroup:SetBackdropColor(unpack(newCheckboxGroup.options.backdrop_color))
	newCheckboxGroup:SetBackdropBorderColor(unpack(newCheckboxGroup.options.backdrop_border_color))

	newCheckboxGroup.AnchorOptions = anchorOptions or {}

	if (newCheckboxGroup.options.title) then
		local titleLabel = detailsFramework:CreateLabel(newCheckboxGroup, newCheckboxGroup.options.title, detailsFramework:GetTemplate("font", "ORANGE_FONT_TEMPLATE"))
		titleLabel:SetPoint("bottomleft", newCheckboxGroup, "topleft", 0, 2)
		newCheckboxGroup.Title = titleLabel
	end

	newCheckboxGroup:SetOptions(radioOptions)

	return newCheckboxGroup
end

function detailsFramework:CreateRadionGroup(parent, radioOptions, name, options, anchorOptions) --alias for miss spelled old function
	return detailsFramework:CreateRadioGroup(parent, radioOptions, name, options, anchorOptions)
end

---@class df_radiogroup : frame, df_optionsmixin, df_radiogroupmixin, df_framelayout

function detailsFramework:CreateRadioGroup(parent, radioOptions, name, options, anchorOptions)
	options = options or {}
	options.is_radio = true
	return detailsFramework:CreateCheckboxGroup(parent, radioOptions, name, options, anchorOptions)
end




------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--simple data scroll

detailsFramework.DataScrollFunctions = {
	RefreshScroll = function(self, data, offset, totalLines)
		local filter = self.Filter
		local currentData = {}
		if (type(filter) == "string" and filter ~= "") then
			for i = 1, #data do
				for o = 1, #data[i] do
					if (data[i][o]:find(filter)) then
						table.insert(currentData, data[i])
						break
					end
				end
			end
		else
			currentData = data
		end

		if (self.SortAlphabetical) then
			table.sort (currentData, function(t1, t2) return t1[1] < t2[1] end)
		end

		--update the scroll
		for i = 1, totalLines do
			local index = i + offset
			local thisData = currentData [index]
			if (thisData) then
				local line = self:GetLine (i)
				line:Update (index, thisData)
			end
		end
	end,

	CreateLine = function(self, index)
		--create a new line
		local line = CreateFrame("button", "$parentLine" .. index, self, "BackdropTemplate")
		line.Update = self.options.update_line_func

		--set its parameters
		line:SetPoint("topleft", self, "topleft", 1, -((index-1) * (self.options.line_height+1)) - 1)
		line:SetSize(self.options.width - 2, self.options.line_height)
		line:RegisterForClicks ("LeftButtonDown", "RightButtonDown")

		line:SetScript("OnEnter",	self.options.on_enter)
		line:SetScript("OnLeave",	self.options.on_leave)
		line:SetScript("OnClick",	self.options.on_click)

		line:SetBackdrop(self.options.backdrop)
		line:SetBackdropColor(unpack(self.options.backdrop_color))
		line:SetBackdropBorderColor(unpack(self.options.backdrop_border_color))

		local title = detailsFramework:CreateLabel(line, "", detailsFramework:GetTemplate("font", self.options.title_template))
		local date = detailsFramework:CreateLabel(line, "", detailsFramework:GetTemplate("font", self.options.title_template))
		local text = detailsFramework:CreateLabel(line, "", detailsFramework:GetTemplate("font", self.options.text_tempate))

		title.textsize = 14
		date.textsize = 14
		text:SetSize(self.options.width - 20, self.options.line_height)
		text:SetJustifyV ("top")

		--setup anchors
		if (self.options.show_title) then
			title:SetPoint("topleft", line, "topleft", 2, 0)
			date:SetPoint("topright", line, "topright", -2, 0)
			text:SetPoint("topleft", title, "bottomleft", 0, -4)
		else
			text:SetPoint("topleft", line, "topleft", 2, 0)
		end

		line.Title = title
		line.Date = date
		line.Text = text

		line.backdrop_color = self.options.backdrop_color or {.1, .1, .1, .3}
		line.backdrop_color_highlight = self.options.backdrop_color_highlight or {.3, .3, .3, .5}

		return line
	end,

	LineOnEnter = function(self)
		self:SetBackdropColor(unpack(self.backdrop_color_highlight))
	end,
	LineOnLeave = function(self)
		self:SetBackdropColor(unpack(self.backdrop_color))
	end,

	OnClick = function(self)

	end,

	UpdateLine = function(line, lineIndex, data)
		local parent = line:GetParent()

		if (parent.options.show_title) then
			line.Title.text = data [2] or ""
			line.Date.text = data [3] or ""
			line.Text.text = data [4] or ""
		else
			line.Text.text = data [2] or ""
		end

		if (line:GetParent().OnUpdateLineHook) then
			detailsFramework:CoreDispatch((line:GetName() or "ScrollBoxDataScrollUpdateLineHook") .. ":UpdateLineHook()", line:GetParent().OnUpdateLineHook, line, lineIndex, data)
		end
	end,
}

local default_datascroll_options = {
	width = 400,
	height = 700,
	line_amount = 10,
	line_height = 20,

	show_title = true,

	backdrop = {edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1, bgFile = [[Interface\Tooltips\UI-Tooltip-Background]], tileSize = 64, tile = true},
	backdrop_color = {0, 0, 0, 0.2},
	backdrop_color_highlight = {.2, .2, .2, 0.4},
	backdrop_border_color = {0.1, 0.1, 0.1, .2},

	title_template = "ORANGE_FONT_TEMPLATE",
	text_tempate = "OPTIONS_FONT_TEMPLATE",

	create_line_func = detailsFramework.DataScrollFunctions.CreateLine,
	update_line_func = detailsFramework.DataScrollFunctions.UpdateLine,
	refresh_func = detailsFramework.DataScrollFunctions.RefreshScroll,
	on_enter = detailsFramework.DataScrollFunctions.LineOnEnter,
	on_leave = detailsFramework.DataScrollFunctions.LineOnLeave,
	on_click =  detailsFramework.DataScrollFunctions.OnClick,

	data = {},
}

--[=[
	Create a scroll frame to show text in an organized way
	Functions in the options table can be overritten to customize the layout
	@parent = the parent of the frame
	@name = the frame name to use in the CreateFrame call
	@options = options table to override default values from the table above
--]=]
function detailsFramework:CreateDataScrollFrame (parent, name, options)
	--call the mixin with a dummy table to built the default options before the frame creation
	--this is done because CreateScrollBox needs parameters at creation time
	local optionsTable = {}
	detailsFramework.OptionsFunctions.BuildOptionsTable (optionsTable, default_datascroll_options, options)
	optionsTable = optionsTable.options

	--scroll frame
	local newScroll = detailsFramework:CreateScrollBox (parent, name, optionsTable.refresh_func, optionsTable.data, optionsTable.width, optionsTable.height, optionsTable.line_amount, optionsTable.line_height)
	detailsFramework:ReskinSlider(newScroll)

	detailsFramework:Mixin(newScroll, detailsFramework.OptionsFunctions)
	detailsFramework:Mixin(newScroll, detailsFramework.LayoutFrame)

	newScroll:BuildOptionsTable (default_datascroll_options, options)

	--create the scrollbox lines
	for i = 1, newScroll.options.line_amount do
		newScroll:CreateLine (newScroll.options.create_line_func)
	end

	newScroll:Refresh()

	return newScroll
end

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--"WHAT's NEW" window

local default_newsframe_options = {
	width = 400,
	height = 700,

	line_amount = 16,
	line_height = 40,

	backdrop = {edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1, bgFile = [[Interface\Tooltips\UI-Tooltip-Background]], tileSize = 64, tile = true},
	backdrop_color = {0, 0, 0, 0.2},
	backdrop_border_color = {0.1, 0.1, 0.1, .2},

	title = "What's New?",
	show_title = true,
}

detailsFramework.NewsFrameFunctions = {

}

--[=[
	Get the amount of news that the player didn't see yet
	@newsTable = an indexed table of tables
	@lastNewsTime = last time the player opened the news window
--]=]
function detailsFramework:GetNumNews (newsTable, lastNewsTime)
	local now = time()
	local nonReadNews = 0

	for _, news in ipairs(newsTable) do
		if (news[1] > lastNewsTime) then
			nonReadNews = nonReadNews + 1
		end
	end

	return nonReadNews
end

--[=[
	Creates a panel with a scroll to show texts organized in separated lines
	@parent =  the parent of the frame
	@name = the frame name to use in the CreateFrame call
	@options = options table to override default values from the table above
	@newsTable = an indexed table of tables
	@db = (optional) an empty table from the addon database to store the position of the frame between game sessions
--]=]
function detailsFramework:CreateNewsFrame (parent, name, options, newsTable, db)

	local f = detailsFramework:CreateSimplePanel(parent, 400, 700, options and options.title or default_newsframe_options.title, name, {UseScaleBar = db and true}, db)
	f:SetFrameStrata("MEDIUM")
	detailsFramework:ApplyStandardBackdrop(f)

	detailsFramework:Mixin(f, detailsFramework.OptionsFunctions)
	detailsFramework:Mixin(f, detailsFramework.LayoutFrame)

	f:BuildOptionsTable (default_newsframe_options, options)

	f:SetSize(f.options.width, f.options.height)
	f:SetBackdrop(f.options.backdrop)
	f:SetBackdropColor(unpack(f.options.backdrop_color))
	f:SetBackdropBorderColor(unpack(f.options.backdrop_border_color))

	local scrollOptions = {
		data = newsTable,
		width = f.options.width - 32, --frame distance from walls and scroll bar space
		height = f.options.height - 40 + (not f.options.show_title and 20 or 0),
		line_amount = f.options.line_amount,
		line_height = f.options.line_height,
	}
	local newsScroll = detailsFramework:CreateDataScrollFrame (f, "$parentScroll", scrollOptions)

	if (not f.options.show_title) then
		f.TitleBar:Hide()
		newsScroll:SetPoint("topleft", f, "topleft", 5, -10)
	else
		newsScroll:SetPoint("topleft", f, "topleft", 5, -30)
	end

	f.NewsScroll = newsScroll

	return f
end



------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--statusbar info

--[[
	authorTable = {
		{
			authorName = "author name 1",
			link = "twitter.com/author1Handle",
		}
	}
]]

function detailsFramework:BuildStatusbarAuthorInfo(f, addonBy, authorsNameString)
	local authorName = detailsFramework:CreateLabel(f, "" .. (addonBy or "An addon by ") .. "|cFFFFFFFF" .. (authorsNameString or "Terciob") .. "|r")
	authorName.textcolor = "silver"
	local discordLabel = detailsFramework:CreateLabel(f, "Discord: ")
	discordLabel.textcolor = "silver"

	local options_dropdown_template = detailsFramework:GetTemplate("dropdown", "OPTIONS_DROPDOWN_TEMPLATE")
	local discordTextEntry = detailsFramework:CreateTextEntry(f, function()end, 200, 18, "DiscordTextBox", _, _, options_dropdown_template)
	discordTextEntry:SetText("https://discord.gg/AGSzAZX")
	discordTextEntry:SetFrameLevel(5000)

	authorName:SetPoint("left", f, "left", 2, 0)
	discordLabel:SetPoint("left", authorName, "right", 20, 0)
	discordTextEntry:SetPoint("left", discordLabel, "right", 2, 0)

	--format
	authorName:SetAlpha(.6)
	discordLabel:SetAlpha(.6)
	discordTextEntry:SetAlpha(.6)
	discordTextEntry:SetBackdropBorderColor(1, 1, 1, 0)

	discordTextEntry:SetHook("OnEditFocusGained", function()
		discordTextEntry:HighlightText()
	end)

	f.authorName = authorName
	f.discordLabel = discordLabel
	f.discordTextEntry = discordTextEntry
end

local statusbar_default_options = {
	attach = "bottom", --bottomleft from statusbar attach to bottomleft of the frame | other option is "top": topleft attach to bottomleft
}

function detailsFramework:CreateStatusBar(f, options)
	local statusBar = CreateFrame("frame", nil, f, "BackdropTemplate")

	detailsFramework:Mixin(statusBar, detailsFramework.OptionsFunctions)
	detailsFramework:Mixin(statusBar, detailsFramework.LayoutFrame)

	statusBar:BuildOptionsTable (statusbar_default_options, options)

	if (statusBar.options.attach == "bottom") then
		statusBar:SetPoint("bottomleft", f, "bottomleft")
		statusBar:SetPoint("bottomright", f, "bottomright")

	else
		statusBar:SetPoint("topleft", f, "bottomleft")
		statusBar:SetPoint("topright", f, "bottomright")
	end

	statusBar:SetHeight(20)
	detailsFramework:ApplyStandardBackdrop(statusBar)
	statusBar:SetAlpha(0.8)

	return statusBar
end


------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--border frame

--[=[
	DF:CreateBorderFrame (parent, name)
	creates a frame with 4 child textures attached to each one of the 4 sides of a frame
	@parent = parent frame to pass to CreateFrame function
	@name = name of the frame, if omitted a random name is created
--]=]

detailsFramework.BorderFunctions = {
	SetBorderColor = function(self, r, g, b, a)
		r, g, b, a = detailsFramework:ParseColors(r, g, b, a)
		for _, texture in ipairs(self.allTextures) do
			texture:SetVertexColor(r, g, b, a)
		end
	end,

	SetBorderThickness = function(self, newThickness)
		PixelUtil.SetWidth (self.leftBorder, newThickness, newThickness)
		PixelUtil.SetWidth (self.rightBorder, newThickness, newThickness)
		PixelUtil.SetHeight(self.topBorder, newThickness, newThickness)
		PixelUtil.SetHeight(self.bottomBorder, newThickness, newThickness)
	end,

	WidgetType = "border",
}

-- ~borderframe
function detailsFramework:CreateBorderFrame(parent, name)
	local parentName = name or ("DetailsFrameworkBorderFrame" .. tostring(math.random(1, 100000000)))

	local f = CreateFrame("frame", parentName, parent, "BackdropTemplate")
	detailsFramework:Mixin(f, detailsFramework.FrameFunctions)
	f:SetFrameLevel(f:GetFrameLevel()+1)
	f:SetAllPoints()

	detailsFramework:Mixin(f, detailsFramework.BorderFunctions)

	f.allTextures = {}

	--create left border
		local leftBorder = f:CreateTexture(nil, "overlay")
		leftBorder:SetDrawLayer("overlay", 7)
		leftBorder:SetColorTexture(1, 1, 1, 1)
		table.insert(f.allTextures, leftBorder)
		f.leftBorder = leftBorder
		PixelUtil.SetPoint(leftBorder, "topright", f, "topleft", 0, 1, 0, 1)
		PixelUtil.SetPoint(leftBorder, "bottomright", f, "bottomleft", 0, -1, 0, -1)
		PixelUtil.SetWidth (leftBorder, 1, 1)

	--create right border
		local rightBorder = f:CreateTexture(nil, "overlay")
		rightBorder:SetDrawLayer("overlay", 7)
		rightBorder:SetColorTexture(1, 1, 1, 1)
		table.insert(f.allTextures, rightBorder)
		f.rightBorder = rightBorder
		PixelUtil.SetPoint(rightBorder, "topleft", f, "topright", 0, 1, 0, 1)
		PixelUtil.SetPoint(rightBorder, "bottomleft", f, "bottomright", 0, -1, 0, -1)
		PixelUtil.SetWidth (rightBorder, 1, 1)

	--create top border
		local topBorder = f:CreateTexture(nil, "overlay")
		topBorder:SetDrawLayer("overlay", 7)
		topBorder:SetColorTexture(1, 1, 1, 1)
		table.insert(f.allTextures, topBorder)
		f.topBorder = topBorder
		PixelUtil.SetPoint(topBorder, "bottomleft", f, "topleft", 0, 0, 0, 0)
		PixelUtil.SetPoint(topBorder, "bottomright", f, "topright", 0, 0, 0, 0)
		PixelUtil.SetHeight(topBorder, 1, 1)

	--create  border
		local bottomBorder = f:CreateTexture(nil, "overlay")
		bottomBorder:SetDrawLayer("overlay", 7)
		bottomBorder:SetColorTexture(1, 1, 1, 1)
		table.insert(f.allTextures, bottomBorder)
		f.bottomBorder = bottomBorder
		PixelUtil.SetPoint(bottomBorder, "topleft", f, "bottomleft", 0, 0, 0, 0)
		PixelUtil.SetPoint(bottomBorder, "topright", f, "bottomright", 0, 0, 0, 0)
		PixelUtil.SetHeight(bottomBorder, 1, 1)

	return f
end

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--error message box

function detailsFramework:ShowErrorMessage (errorMessage, titleText)

	if (not detailsFramework.ErrorMessagePanel) then
		local f = CreateFrame("frame", "DetailsFrameworkErrorMessagePanel", UIParent, "BackdropTemplate")
		f:SetSize(400, 120)
		f:SetFrameStrata("FULLSCREEN")
		f:SetPoint("center", UIParent, "center", 0, 100)
		f:EnableMouse(true)
		f:SetMovable(true)
		f:RegisterForDrag ("LeftButton")
		f:SetScript("OnDragStart", function() f:StartMoving() end)
		f:SetScript("OnDragStop", function() f:StopMovingOrSizing() end)
		f:SetScript("OnMouseDown", function(self, button) if (button == "RightButton") then f:Hide() end end)
		table.insert(UISpecialFrames, "DetailsFrameworkErrorMessagePanel")
		detailsFramework.ErrorMessagePanel = f

		detailsFramework:CreateTitleBar (f, "Details! Framework Error!")
		detailsFramework:ApplyStandardBackdrop(f)

		local errorLabel = f:CreateFontString(nil, "overlay", "GameFontNormal")
		errorLabel:SetPoint("top", f, "top", 0, -25)
		errorLabel:SetJustifyH("center")
		errorLabel:SetSize(360, 66)
		f.errorLabel = errorLabel

		local button_text_template = detailsFramework:GetTemplate("font", "OPTIONS_FONT_TEMPLATE")
		local options_dropdown_template = detailsFramework:GetTemplate("dropdown", "OPTIONS_DROPDOWN_TEMPLATE")

		local closeButton = detailsFramework:CreateButton(f, nil, 60, 20, "close", nil, nil, nil, nil, nil, nil, options_dropdown_template)
		closeButton:SetPoint("bottom", f, "bottom", 0, 5)
		f.closeButton = closeButton

		closeButton:SetClickFunction(function()
			f:Hide()
		end)

		f.ShowAnimation = detailsFramework:CreateAnimationHub (f, function()
			f:SetBackdropBorderColor(0, 0, 0, 0)
			f.TitleBar:SetBackdropBorderColor(0, 0, 0, 0)
		end, function()
			f:SetBackdropBorderColor(0, 0, 0, 1)
			f.TitleBar:SetBackdropBorderColor(0, 0, 0, 1)
		end)
		detailsFramework:CreateAnimation(f.ShowAnimation, "scale", 1, .075, .2, .2, 1.1, 1.1, "center", 0, 0)
		detailsFramework:CreateAnimation(f.ShowAnimation, "scale", 2, .075, 1, 1, .90, .90, "center", 0, 0)

		f.FlashTexture = f:CreateTexture(nil, "overlay")
		f.FlashTexture:SetColorTexture(1, 1, 1, 1)
		f.FlashTexture:SetAllPoints()

		f.FlashAnimation = detailsFramework:CreateAnimationHub (f.FlashTexture, function() f.FlashTexture:Show() end, function() f.FlashTexture:Hide() end)
		detailsFramework:CreateAnimation(f.FlashAnimation, "alpha", 1, .075, 0, .05)
		detailsFramework:CreateAnimation(f.FlashAnimation, "alpha", 2, .075, .1, 0)

		f:Hide()
	end

	detailsFramework.ErrorMessagePanel:Show()
	detailsFramework.ErrorMessagePanel.errorLabel:SetText(errorMessage)
	detailsFramework.ErrorMessagePanel.TitleLabel:SetText(titleText)
	detailsFramework.ErrorMessagePanel.ShowAnimation:Play()
	detailsFramework.ErrorMessagePanel.FlashAnimation:Play()
end

--[[
	DF:SetPointOffsets(frame, xOffset, yOffset)

	Set an offset into the already existing offset of the frame
	If passed xOffset:1 and yOffset:1 and the frame has 1 -1,  the new offset will be 2 -2
	This function is great to create a 1 knob for distance

	@frame: a frame to have the offsets changed
	@xOffset: the amount to apply into the x offset
	@yOffset: the amount to apply into the y offset
--]]
function detailsFramework:SetPointOffsets(frame, xOffset, yOffset)
	for i = 1, frame:GetNumPoints() do
		local anchor1, anchorTo, anchor2, x, y = frame:GetPoint(i)
		x = x or 0
		y = y or 0

		if (x >= 0) then
			xOffset = x + xOffset

		elseif (x < 0) then
			xOffset = x - xOffset
		end

		if (y >= 0) then
			yOffset = y + yOffset

		elseif (y < 0) then
			yOffset = y - yOffset
		end

		frame:SetPoint(anchor1, anchorTo, anchor2, xOffset, yOffset)
	end
end

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--list box

detailsFramework.ListboxFunctions = {
	scrollRefresh = function(self, data, offset, totalLines)
		for i = 1, totalLines do
			local index = i + offset
			local lineData = data[index] --what is shown in the textentries, array

			if (lineData) then
				local line = self:GetLine(i)
				line.dataIndex = index
				line.deleteButton:SetClickFunction(detailsFramework.ListboxFunctions.deleteEntry, data, index)
				line.indexText:SetText(index)

				local amountEntries = #lineData
				for o = 1, amountEntries do
					--data
					local textEntry = line.widgets[o]
					textEntry.dataTable = lineData
					textEntry.dataTableIndex = o
					local text = lineData[o]
					textEntry:SetText(text)
				end
			end
		end
	end,

	addEntry = function(self)
		local frameCanvas = self:GetParent()
		local data = frameCanvas.data
		local newEntry = {}
		for i = 1, frameCanvas.headerLength do
			table.insert(newEntry, "")
		end
		table.insert(data, newEntry)
		frameCanvas.scrollBox:Refresh()
	end,

	deleteEntry = function(self, button, data, index)
		table.remove(data, index)
		--get the line, get the scrollframe
		self:GetParent():GetParent():Refresh()
	end,

	createScrollLine = function(self, index)
		local listBox = self:GetParent()
		local line = CreateFrame("frame", self:GetName().. "line_" .. index, self, "BackdropTemplate")

		line:SetPoint("topleft", self, "topleft", 1, -((index-1)*(self.lineHeight+1)) - 1)
		line:SetSize(self:GetWidth() - 28, self.lineHeight) -- -28 space for the scrollbar

		local options = listBox.options
		line:SetBackdrop(options.line_backdrop)
		line:SetBackdropColor(unpack(options.line_backdrop_color))
		line:SetBackdropBorderColor(unpack(options.line_backdrop_border_color))

		detailsFramework:Mixin(line, detailsFramework.HeaderFunctions)

		line.widgets = {}

		for i = 1, (listBox.headerLength+2) do --+2 to add the delete button and index
			local headerColumn = listBox.headerTable[i]

			if (headerColumn.isDelete) then
				local deleteButton = detailsFramework:CreateButton(line, detailsFramework.ListboxFunctions.deleteEntry, 20, self.lineHeight, "X", listBox.data, index, nil, nil, nil, nil, detailsFramework:GetTemplate("button", "OPTIONS_BUTTON_TEMPLATE"), detailsFramework:GetTemplate("font", "ORANGE_FONT_TEMPLATE"))
				line.deleteButton = deleteButton
				line:AddFrameToHeaderAlignment(deleteButton)

			elseif (headerColumn.isIndex) then
				local indexText = detailsFramework:CreateLabel(line)
				line.indexText = indexText
				line:AddFrameToHeaderAlignment(indexText)

			elseif (headerColumn.text) then
				local template = detailsFramework.table.copy({}, detailsFramework:GetTemplate("dropdown", "OPTIONS_DROPDOWN_TEMPLATE"))
				template.backdropcolor = {.1, .1, .1, .7}
				template.backdropbordercolor = {.2, .2, .2, .6}

				local textEntry = detailsFramework:CreateTextEntry(line, function()end, headerColumn.width, self.lineHeight, nil, nil, nil, template)
				textEntry:SetHook("OnEditFocusGained", function() textEntry:HighlightText(0) end)
				textEntry:SetHook("OnEditFocusLost", function()
					textEntry:HighlightText(0, 0)
					local text = textEntry.text
					local dataTable = textEntry.dataTable
					dataTable[textEntry.dataTableIndex] = text
				end)
				table.insert(line.widgets, textEntry)
				line:AddFrameToHeaderAlignment(textEntry)
			end
		end

		line:AlignWithHeader(listBox.header, "left")
		return line
	end,

	SetData = function(frameCanvas, newData)
		if (type(newData) ~= "table") then
			error("ListBox:SetData received an invalid newData on parameter 2.")
			return
		end

		frameCanvas.data = newData
		frameCanvas.scrollBox:SetData(newData)
		frameCanvas.scrollBox:Refresh()
	end,
}

local listbox_options = {
	width = 800,
	height = 600,
	auto_width = true,
	line_height = 16,
	line_backdrop = {bgFile = [[Interface\Tooltips\UI-Tooltip-Background]], tileSize = 64, tile = true},
	line_backdrop_color = {.1, .1, .1, .6},
	line_backdrop_border_color = {0, 0, 0, .5},
}

--@parent: parent frame
--@name: name of the frame to be created
--@data: table with current data to fill the column, this table are also used for values changed or added
--@options: table with options to overwrite the default setting from 'listbox_options'
--@header: a table to create a header widget
--@header_options: a table with options to overwrite the default header options
function detailsFramework:CreateListBox(parent, name, data, options, headerTable, headerOptions)

	options = options or {}
	name = name or "ListboxUnamed_" .. (math.random(100000, 1000000))

	--canvas
	local frameCanvas = CreateFrame("scrollframe", name, parent, "BackdropTemplate")
	detailsFramework:Mixin(frameCanvas, detailsFramework.ListboxFunctions)
	detailsFramework:Mixin(frameCanvas, detailsFramework.OptionsFunctions)
	detailsFramework:Mixin(frameCanvas, detailsFramework.LayoutFrame)
	frameCanvas.headerTable = headerTable

	if (not data or type(data) ~= "table") then
		error("CreateListBox() parameter 3 'data' must be a table.")
	end

	frameCanvas.data = data
	frameCanvas.lines = {}
	detailsFramework:ApplyStandardBackdrop(frameCanvas)
	frameCanvas:BuildOptionsTable(listbox_options, options)

	--header
		--check for default values in the header
		headerTable = headerTable or {
			{text = "Spell Id", width = 70},
			{text = "Spell Name", width = 70},
		}
		headerOptions = headerOptions or {
			padding = 2,
		}

		--each header is an entry in the data, if the header has 4 indexes the data has sub tables with 4 indexes as well
		frameCanvas.headerLength = #headerTable

		--add the detele line column into the header frame
		table.insert(headerTable, 1, {text = "#", width = 20, isIndex = true}) --isDelete signals the createScrollLine() to make the delete button for the line
		table.insert(headerTable, {text = "Delete", width = 50, isDelete = true}) --isDelete signals the createScrollLine() to make the delete button for the line

		local header = detailsFramework:CreateHeader(frameCanvas, headerTable, headerOptions)
		--set the header point
		header:SetPoint("topleft", frameCanvas, "topleft", 5, -5)
		frameCanvas.header = header

	--auto size
		if (frameCanvas.options.auto_width) then
			local width = 10 --padding 5 on each side
			width = width + 20 --scrollbar reserved space
			local headerPadding = headerOptions.padding or 0

			for _, header in pairs(headerTable) do
				if (header.width) then
					width = width + header.width + headerPadding
				end
			end

			frameCanvas.options.width = width
			frameCanvas:SetWidth(width)
		end

		local width = frameCanvas.options.width
		local height = frameCanvas.options.height

		frameCanvas:SetSize(frameCanvas.options.width, height)

	--scroll frame
		local lineHeight = frameCanvas.options.line_height
		--calc the size of the space occupied by the add button, header etc
		local lineAmount = floor((height - 60) / lineHeight)

		-- -12 is padding: 5 on top, 7 bottom, 2 header scrollbar blank space | -24 to leave space to the add button
		local scrollBox = detailsFramework:CreateScrollBox(frameCanvas, "$parentScrollbox", frameCanvas.scrollRefresh, data, width-4, height - header:GetHeight() - 12 - 24, lineAmount, lineHeight)
		scrollBox:SetPoint("topleft", header, "bottomleft", 0, -2)
		scrollBox:SetPoint("topright", header, "bottomright", 0, -2) -- -20 for the scrollbar
		detailsFramework:ReskinSlider(scrollBox)
		scrollBox.lineHeight = lineHeight
		scrollBox.lineAmount = lineAmount
		frameCanvas.scrollBox = scrollBox

		for i = 1, lineAmount do
			scrollBox:CreateLine(frameCanvas.createScrollLine)
		end

		scrollBox:Refresh()

	--add line button
		local addLineButton = detailsFramework:CreateButton(frameCanvas, detailsFramework.ListboxFunctions.addEntry, 80, 20, "Add", nil, nil, nil, nil, nil, nil, detailsFramework:GetTemplate("button", "OPTIONS_BUTTON_TEMPLATE"), detailsFramework:GetTemplate("font", "ORANGE_FONT_TEMPLATE"))
		addLineButton:SetPoint("topleft", scrollBox, "bottomleft", 0, -4)

	return frameCanvas
end

--[=[ -- test case

    local pframe = ListBoxTest or CreateFrame("frame", "ListBoxTest", UIParent)
    pframe:SetSize(900, 700)
    pframe:SetPoint("left")

    local data = {{254154, "spell name 1", 45}, {299154, "spell name 2", 05}, {354154, "spell name 3", 99}}
    local headerTable = {
        {text = "spell id", width = 120},
        {text = "spell name", width = 180},
        {text = "number", width = 90},
    }

    local listbox = DetailsFramework:CreateListBox(pframe, "$parentlistbox", data, nil, headerTable, nil)
    listbox:SetPoint("topleft", pframe, "topleft", 10, -10)

--]=]

--[=[

--show data panel
function detailsFramework:ShowData(data)
	if (not DetailsFrameworkDataPanel) then
		local mainFrame = detailsFramework:CreateSimplePanel(UIParent, 800, 600, "Show Data", "DetailsFrameworkShowDataPanel")
		mainFrame:SetPoint("center", UIParent, "center", 0, 0)

		--header
		local headerTable = {
			{text = "", width = 75},
			{text = "", width = 75},
			{text = "", width = 75},
			{text = "", width = 75},
			{text = "", width = 75},
			{text = "", width = 75},
			{text = "", width = 75},
			{text = "", width = 75},
			{text = "", width = 75},
			{text = "", width = 75},
			{text = "", width = 75},
			{text = "", width = 75},
		}
		local headerOptions = {
			padding = 2,
		}

		mainFrame.Header = DF:CreateHeader(mainFrame, headerTable, headerOptions)
		mainFrame.Header:SetPoint("topleft", mainFrame, "topleft", 5, headerY)

		local refreshFunc = function(self, data, offset, totalLines) --~refresh
			local ToK = Details:GetCurrentToKFunction()

			for i = 1, totalLines do
				local index = i + offset
				local thisData = data[index]
				if (thisData) then
					local line = self:GetLine(i)
				end
			end
		end

		local createLineFunc = function(self, index)
			local line = CreateFrame("button", "$parentLine" .. index, self,"BackdropTemplate")
			line:SetPoint("topleft", self, "topleft", 1, -((index-1)*(scroll_line_height+1)) - 1)
			line:SetSize(scroll_width - 2, scroll_line_height)

			line:SetBackdrop({bgFile = [[Interface\Tooltips\UI-Tooltip-Background]], tileSize = 64, tile = true})
			line:SetBackdropColor(unpack(backdrop_color))
			-- ~createline --~line
			DF:Mixin(line, DF.HeaderFunctions)

			--columns
			local column1 = DF:CreateTextEntry(line, function()end, DetailsScrollDamage.Header:GetColumnWidth(2), scroll_line_height, _, _, _, dropdownTemplate)
			local column1 = DF:CreateTextEntry(line, function()end, DetailsScrollDamage.Header:GetColumnWidth(2), scroll_line_height, _, _, _, dropdownTemplate)
			local column1 = DF:CreateTextEntry(line, function()end, DetailsScrollDamage.Header:GetColumnWidth(2), scroll_line_height, _, _, _, dropdownTemplate)
			local column1 = DF:CreateTextEntry(line, function()end, DetailsScrollDamage.Header:GetColumnWidth(2), scroll_line_height, _, _, _, dropdownTemplate)
			local column1 = DF:CreateTextEntry(line, function()end, DetailsScrollDamage.Header:GetColumnWidth(2), scroll_line_height, _, _, _, dropdownTemplate)
			local column1 = DF:CreateTextEntry(line, function()end, DetailsScrollDamage.Header:GetColumnWidth(2), scroll_line_height, _, _, _, dropdownTemplate)

			line:AddFrameToHeaderAlignment(icon)
			line:AddFrameToHeaderAlignment(spellNameText)
			line:AddFrameToHeaderAlignment(damageText)
			line:AddFrameToHeaderAlignment(timeText)
			line:AddFrameToHeaderAlignment(spellIDText)

			line:AlignWithHeader(DetailsScrollDamage.Header, "left")

			line.Icon = icon
			line.IconFrame = iconFrame
			line.DamageText = damageText
			line.TimeText = timeText
			line.SpellIDText = spellIDText
			line.SpellNameText = spellNameText

			return line
		end
	end

	local dataPanel = DetailsFrameworkDataPanel
	local scrollBox = dataPanel.ScrollBox
	scrollBox:SetData(data)
	scrollBox:Refresh()

end

--]=]