mirror of
https://github.com/Relintai/Relintais-Enemy-Kooldown-Tracker-TBC.git
synced 2025-02-04 16:16:09 +01:00
704 lines
18 KiB
Lua
704 lines
18 KiB
Lua
--[[ $Id: AceGUI-3.0.lua 81438 2008-09-06 13:44:36Z nevcairiel $ ]]
|
|
local ACEGUI_MAJOR, ACEGUI_MINOR = "AceGUI-3.0", 16
|
|
local AceGUI, oldminor = LibStub:NewLibrary(ACEGUI_MAJOR, ACEGUI_MINOR)
|
|
|
|
if not AceGUI then return end -- No upgrade needed
|
|
|
|
--local con = LibStub("AceConsole-3.0",true)
|
|
|
|
AceGUI.WidgetRegistry = AceGUI.WidgetRegistry or {}
|
|
AceGUI.LayoutRegistry = AceGUI.LayoutRegistry or {}
|
|
AceGUI.WidgetBase = AceGUI.WidgetBase or {}
|
|
AceGUI.WidgetContainerBase = AceGUI.WidgetContainerBase or {}
|
|
AceGUI.WidgetVersions = AceGUI.WidgetVersions or {}
|
|
|
|
-- local upvalues
|
|
local WidgetRegistry = AceGUI.WidgetRegistry
|
|
local LayoutRegistry = AceGUI.LayoutRegistry
|
|
local WidgetVersions = AceGUI.WidgetVersions
|
|
|
|
local pcall = pcall
|
|
local select = select
|
|
local pairs = pairs
|
|
local ipairs = ipairs
|
|
local type = type
|
|
local assert = assert
|
|
local tinsert = tinsert
|
|
local tremove = tremove
|
|
local CreateFrame = CreateFrame
|
|
local UIParent = UIParent
|
|
|
|
--[[
|
|
xpcall safecall implementation
|
|
]]
|
|
local xpcall = xpcall
|
|
|
|
local function errorhandler(err)
|
|
return geterrorhandler()(err)
|
|
end
|
|
|
|
local function CreateDispatcher(argCount)
|
|
local code = [[
|
|
local xpcall, eh = ...
|
|
local method, ARGS
|
|
local function call() return method(ARGS) end
|
|
|
|
local function dispatch(func, ...)
|
|
method = func
|
|
if not method then return end
|
|
ARGS = ...
|
|
return xpcall(call, eh)
|
|
end
|
|
|
|
return dispatch
|
|
]]
|
|
|
|
local ARGS = {}
|
|
for i = 1, argCount do ARGS[i] = "arg"..i end
|
|
code = code:gsub("ARGS", table.concat(ARGS, ", "))
|
|
return assert(loadstring(code, "safecall Dispatcher["..argCount.."]"))(xpcall, errorhandler)
|
|
end
|
|
|
|
local Dispatchers = setmetatable({}, {__index=function(self, argCount)
|
|
local dispatcher = CreateDispatcher(argCount)
|
|
rawset(self, argCount, dispatcher)
|
|
return dispatcher
|
|
end})
|
|
Dispatchers[0] = function(func)
|
|
return xpcall(func, errorhandler)
|
|
end
|
|
|
|
local function safecall(func, ...)
|
|
return Dispatchers[select('#', ...)](func, ...)
|
|
end
|
|
|
|
-- Recycling functions
|
|
local new, del
|
|
do
|
|
AceGUI.objPools = AceGUI.objPools or {}
|
|
local objPools = AceGUI.objPools
|
|
--Returns a new instance, if none are available either returns a new table or calls the given contructor
|
|
function new(type,constructor,...)
|
|
if not type then
|
|
type = "table"
|
|
end
|
|
if not objPools[type] then
|
|
objPools[type] = {}
|
|
end
|
|
local newObj = tremove(objPools[type])
|
|
if not newObj then
|
|
newObj = constructor and constructor(...) or {}
|
|
end
|
|
return newObj
|
|
end
|
|
-- Releases an instance to the Pool
|
|
function del(obj,type)
|
|
if not type then
|
|
type = "table"
|
|
end
|
|
if not objPools[type] then
|
|
objPools[type] = {}
|
|
end
|
|
for i,v in ipairs(objPools[type]) do
|
|
if v == obj then
|
|
error("Attempt to Release Widget that is already released")
|
|
return
|
|
end
|
|
end
|
|
tinsert(objPools[type],obj)
|
|
end
|
|
end
|
|
|
|
|
|
-------------------
|
|
-- API Functions --
|
|
-------------------
|
|
|
|
-- Gets a widget Object
|
|
|
|
function AceGUI:Create(type)
|
|
local reg = WidgetRegistry
|
|
if reg[type] then
|
|
local widget = new(type,reg[type])
|
|
|
|
if rawget(widget,'Acquire') then
|
|
widget.OnAcquire = widget.Acquire
|
|
widget.Acquire = nil
|
|
elseif rawget(widget,'Aquire') then
|
|
widget.OnAcquire = widget.Aquire
|
|
widget.Aquire = nil
|
|
end
|
|
|
|
if rawget(widget,'Release') then
|
|
widget.OnRelease = rawget(widget,'Release')
|
|
widget.Release = nil
|
|
end
|
|
|
|
if widget.OnAcquire then
|
|
widget:OnAcquire()
|
|
else
|
|
error(("Widget type %s doesn't supply an OnAcquire Function"):format(type))
|
|
end
|
|
safecall(widget.ResumeLayout, widget)
|
|
return widget
|
|
end
|
|
end
|
|
|
|
-- Releases a widget Object
|
|
function AceGUI:Release(widget)
|
|
safecall( widget.PauseLayout, widget )
|
|
widget:Fire("OnRelease")
|
|
safecall( widget.ReleaseChildren, widget )
|
|
|
|
if widget.OnRelease then
|
|
widget:OnRelease()
|
|
else
|
|
error(("Widget type %s doesn't supply an OnRelease Function"):format(type))
|
|
end
|
|
for k in pairs(widget.userdata) do
|
|
widget.userdata[k] = nil
|
|
end
|
|
for k in pairs(widget.events) do
|
|
widget.events[k] = nil
|
|
end
|
|
widget.width = nil
|
|
--widget.frame:SetParent(nil)
|
|
widget.frame:ClearAllPoints()
|
|
widget.frame:Hide()
|
|
widget.frame:SetParent(nil)
|
|
if widget.content then
|
|
widget.content.width = nil
|
|
widget.content.height = nil
|
|
end
|
|
del(widget,widget.type)
|
|
end
|
|
|
|
-----------
|
|
-- Focus --
|
|
-----------
|
|
|
|
-----
|
|
-- Called when a widget has taken focus
|
|
-- e.g. Dropdowns opening, Editboxes gaining kb focus
|
|
-----
|
|
function AceGUI:SetFocus(widget)
|
|
if self.FocusedWidget and self.FocusedWidget ~= widget then
|
|
safecall(self.FocusedWidget.ClearFocus, self.FocusedWidget)
|
|
end
|
|
self.FocusedWidget = widget
|
|
end
|
|
|
|
-----
|
|
-- Called when something has happened that could cause widgets with focus to drop it
|
|
-- e.g. titlebar of a frame being clicked
|
|
-----
|
|
function AceGUI:ClearFocus()
|
|
if self.FocusedWidget then
|
|
safecall(self.FocusedWidget.ClearFocus, self.FocusedWidget)
|
|
self.FocusedWidget = nil
|
|
end
|
|
end
|
|
|
|
-------------
|
|
-- Widgets --
|
|
-------------
|
|
--[[
|
|
Widgets must provide the following functions
|
|
OnAcquire() - Called when the object is acquired, should set everything to a default hidden state
|
|
OnRelease() - Called when the object is Released, should remove any anchors and hide the Widget
|
|
|
|
And the following members
|
|
frame - the frame or derivitive object that will be treated as the widget for size and anchoring purposes
|
|
type - the type of the object, same as the name given to :RegisterWidget()
|
|
|
|
Widgets contain a table called userdata, this is a safe place to store data associated with the wigdet
|
|
It will be cleared automatically when a widget is released
|
|
Placing values directly into a widget object should be avoided
|
|
|
|
If the Widget can act as a container for other Widgets the following
|
|
content - frame or derivitive that children will be anchored to
|
|
|
|
The Widget can supply the following Optional Members
|
|
:OnWidthSet(width) - Called when the width of the widget is changed
|
|
:OnHeightSet(height) - Called when the height of the widget is changed
|
|
Widgets should not use the OnSizeChanged events of thier frame or content members, use these methods instead
|
|
AceGUI already sets a handler to the event
|
|
:OnLayoutFinished(width, height) - called after a layout has finished, the width and height will be the width and height of the
|
|
area used for controls. These can be nil if the layout used the existing size to layout the controls.
|
|
|
|
]]
|
|
|
|
--------------------------
|
|
-- Widget Base Template --
|
|
--------------------------
|
|
do
|
|
local function fixlevels(parent,...)
|
|
local i = 1
|
|
local child = select(i, ...)
|
|
while child do
|
|
child:SetFrameLevel(parent:GetFrameLevel()+1)
|
|
fixlevels(child, child:GetChildren())
|
|
i = i + 1
|
|
child = select(i, ...)
|
|
end
|
|
end
|
|
|
|
local WidgetBase = AceGUI.WidgetBase
|
|
|
|
WidgetBase.SetParent = function(self, parent)
|
|
local frame = self.frame
|
|
frame:SetParent(nil)
|
|
frame:SetParent(parent.content)
|
|
self.parent = parent
|
|
fixlevels(parent.frame,parent.frame:GetChildren())
|
|
end
|
|
|
|
WidgetBase.SetCallback = function(self, name, func)
|
|
if type(func) == "function" then
|
|
self.events[name] = func
|
|
end
|
|
end
|
|
|
|
WidgetBase.Fire = function(self, name, ...)
|
|
if self.events[name] then
|
|
local success, ret = safecall(self.events[name], self, name, ...)
|
|
if success then
|
|
return ret
|
|
end
|
|
end
|
|
end
|
|
|
|
WidgetBase.SetWidth = function(self, width)
|
|
self.frame:SetWidth(width)
|
|
self.frame.width = width
|
|
if self.OnWidthSet then
|
|
self:OnWidthSet(width)
|
|
end
|
|
end
|
|
|
|
WidgetBase.SetHeight = function(self, height)
|
|
self.frame:SetHeight(height)
|
|
self.frame.height = height
|
|
if self.OnHeightSet then
|
|
self:OnHeightSet(height)
|
|
end
|
|
end
|
|
|
|
WidgetBase.IsVisible = function(self)
|
|
return self.frame:IsVisible()
|
|
end
|
|
|
|
WidgetBase.IsShown= function(self)
|
|
return self.frame:IsShown()
|
|
end
|
|
|
|
WidgetBase.Release = function(self)
|
|
AceGUI:Release(self)
|
|
end
|
|
|
|
WidgetBase.SetPoint = function(self, ...)
|
|
return self.frame:SetPoint(...)
|
|
end
|
|
|
|
WidgetBase.ClearAllPoints = function(self)
|
|
return self.frame:ClearAllPoints()
|
|
end
|
|
|
|
WidgetBase.GetNumPoints = function(self)
|
|
return self.frame:GetNumPoints()
|
|
end
|
|
|
|
WidgetBase.GetPoint = function(self, ...)
|
|
return self.frame:GetPoint(...)
|
|
end
|
|
|
|
WidgetBase.GetUserDataTable = function(self)
|
|
return self.userdata
|
|
end
|
|
|
|
WidgetBase.SetUserData = function(self, key, value)
|
|
self.userdata[key] = value
|
|
end
|
|
|
|
WidgetBase.GetUserData = function(self, key)
|
|
return self.userdata[key]
|
|
end
|
|
|
|
WidgetBase.IsFullHeight = function(self)
|
|
return self.height == "fill"
|
|
end
|
|
|
|
WidgetBase.SetFullHeight = function(self, isFull)
|
|
if isFull then
|
|
self.height = "fill"
|
|
else
|
|
self.height = nil
|
|
end
|
|
end
|
|
|
|
WidgetBase.IsFullWidth = function(self)
|
|
return self.width == "fill"
|
|
end
|
|
|
|
WidgetBase.SetFullWidth = function(self, isFull)
|
|
if isFull then
|
|
self.width = "fill"
|
|
else
|
|
self.width = nil
|
|
end
|
|
end
|
|
|
|
-- local function LayoutOnUpdate(this)
|
|
-- this:SetScript("OnUpdate",nil)
|
|
-- this.obj:PerformLayout()
|
|
-- end
|
|
|
|
local WidgetContainerBase = AceGUI.WidgetContainerBase
|
|
|
|
WidgetContainerBase.PauseLayout = function(self)
|
|
self.LayoutPaused = true
|
|
end
|
|
|
|
WidgetContainerBase.ResumeLayout = function(self)
|
|
self.LayoutPaused = nil
|
|
end
|
|
|
|
WidgetContainerBase.PerformLayout = function(self)
|
|
if self.LayoutPaused then
|
|
return
|
|
end
|
|
safecall(self.LayoutFunc,self.content, self.children)
|
|
end
|
|
|
|
--call this function to layout, makes sure layed out objects get a frame to get sizes etc
|
|
WidgetContainerBase.DoLayout = function(self)
|
|
self:PerformLayout()
|
|
-- if not self.parent then
|
|
-- self.frame:SetScript("OnUpdate", LayoutOnUpdate)
|
|
-- end
|
|
end
|
|
|
|
WidgetContainerBase.AddChild = function(self, child)
|
|
tinsert(self.children,child)
|
|
child:SetParent(self)
|
|
child.frame:Show()
|
|
self:DoLayout()
|
|
end
|
|
|
|
WidgetContainerBase.ReleaseChildren = function(self)
|
|
local children = self.children
|
|
for i in ipairs(children) do
|
|
AceGUI:Release(children[i])
|
|
children[i] = nil
|
|
end
|
|
end
|
|
|
|
WidgetContainerBase.SetLayout = function(self, Layout)
|
|
self.LayoutFunc = AceGUI:GetLayout(Layout)
|
|
end
|
|
|
|
local function FrameResize(this)
|
|
local self = this.obj
|
|
if this:GetWidth() and this:GetHeight() then
|
|
if self.OnWidthSet then
|
|
self:OnWidthSet(this:GetWidth())
|
|
end
|
|
if self.OnHeightSet then
|
|
self:OnHeightSet(this:GetHeight())
|
|
end
|
|
end
|
|
end
|
|
|
|
local function ContentResize(this)
|
|
if this:GetWidth() and this:GetHeight() then
|
|
this.width = this:GetWidth()
|
|
this.height = this:GetHeight()
|
|
this.obj:DoLayout()
|
|
end
|
|
end
|
|
|
|
setmetatable(WidgetContainerBase,{__index=WidgetBase})
|
|
|
|
--One of these function should be called on each Widget Instance as part of its creation process
|
|
function AceGUI:RegisterAsContainer(widget)
|
|
widget.children = {}
|
|
widget.userdata = {}
|
|
widget.events = {}
|
|
widget.base = WidgetContainerBase
|
|
widget.content.obj = widget
|
|
widget.frame.obj = widget
|
|
widget.content:SetScript("OnSizeChanged",ContentResize)
|
|
widget.frame:SetScript("OnSizeChanged",FrameResize)
|
|
setmetatable(widget,{__index=WidgetContainerBase})
|
|
widget:SetLayout("List")
|
|
end
|
|
|
|
function AceGUI:RegisterAsWidget(widget)
|
|
widget.userdata = {}
|
|
widget.events = {}
|
|
widget.base = WidgetBase
|
|
widget.frame.obj = widget
|
|
widget.frame:SetScript("OnSizeChanged",FrameResize)
|
|
setmetatable(widget,{__index=WidgetBase})
|
|
end
|
|
end
|
|
|
|
|
|
|
|
|
|
------------------
|
|
-- Widget API --
|
|
------------------
|
|
-- Registers a widget Constructor, this function returns a new instance of the Widget
|
|
function AceGUI:RegisterWidgetType(Name, Constructor, Version)
|
|
assert(type(Constructor) == "function")
|
|
assert(type(Version) == "number")
|
|
|
|
local oldVersion = WidgetVersions[Name]
|
|
if oldVersion and oldVersion >= Version then return end
|
|
|
|
WidgetVersions[Name] = Version
|
|
WidgetRegistry[Name] = Constructor
|
|
end
|
|
|
|
-- Registers a Layout Function
|
|
function AceGUI:RegisterLayout(Name, LayoutFunc)
|
|
assert(type(LayoutFunc) == "function")
|
|
if type(Name) == "string" then
|
|
Name = Name:upper()
|
|
end
|
|
LayoutRegistry[Name] = LayoutFunc
|
|
end
|
|
|
|
function AceGUI:GetLayout(Name)
|
|
if type(Name) == "string" then
|
|
Name = Name:upper()
|
|
end
|
|
return LayoutRegistry[Name]
|
|
end
|
|
|
|
AceGUI.counts = AceGUI.counts or {}
|
|
|
|
function AceGUI:GetNextWidgetNum(type)
|
|
if not self.counts[type] then
|
|
self.counts[type] = 0
|
|
end
|
|
self.counts[type] = self.counts[type] + 1
|
|
return self.counts[type]
|
|
end
|
|
|
|
--[[ Widget Template
|
|
|
|
--------------------------
|
|
-- Widget Name --
|
|
--------------------------
|
|
do
|
|
local Type = "Type"
|
|
|
|
local function OnAcquire(self)
|
|
|
|
end
|
|
|
|
local function OnRelease(self)
|
|
self.frame:ClearAllPoints()
|
|
self.frame:Hide()
|
|
end
|
|
|
|
|
|
local function Constructor()
|
|
local frame = CreateFrame("Frame",nil,UIParent)
|
|
local self = {}
|
|
self.type = Type
|
|
|
|
self.OnRelease = OnRelease
|
|
self.OnAcquire = OnAcquire
|
|
|
|
self.frame = frame
|
|
frame.obj = self
|
|
|
|
--Container Support
|
|
--local content = CreateFrame("Frame",nil,frame)
|
|
--self.content = content
|
|
|
|
--AceGUI:RegisterAsContainer(self)
|
|
AceGUI:RegisterAsWidget(self)
|
|
return self
|
|
end
|
|
|
|
AceGUI:RegisterWidgetType(Type,Constructor)
|
|
end
|
|
|
|
|
|
]]
|
|
|
|
-------------
|
|
-- Layouts --
|
|
-------------
|
|
|
|
--[[
|
|
A Layout is a func that takes 2 parameters
|
|
content - the frame that widgets will be placed inside
|
|
children - a table containing the widgets to layout
|
|
|
|
]]
|
|
|
|
-- Very simple Layout, Children are stacked on top of each other down the left side
|
|
AceGUI:RegisterLayout("List",
|
|
function(content, children)
|
|
|
|
local height = 0
|
|
local width = content.width or content:GetWidth() or 0
|
|
for i, child in ipairs(children) do
|
|
|
|
|
|
local frame = child.frame
|
|
frame:ClearAllPoints()
|
|
frame:Show()
|
|
if i == 1 then
|
|
frame:SetPoint("TOPLEFT",content,"TOPLEFT",0,0)
|
|
else
|
|
frame:SetPoint("TOPLEFT",children[i-1].frame,"BOTTOMLEFT",0,0)
|
|
end
|
|
|
|
if child.width == "fill" then
|
|
child:SetWidth(width)
|
|
frame:SetPoint("RIGHT",content,"RIGHT")
|
|
if child.OnWidthSet then
|
|
child:OnWidthSet(content.width or content:GetWidth())
|
|
end
|
|
if child.DoLayout then
|
|
child:DoLayout()
|
|
end
|
|
end
|
|
|
|
height = height + (frame.height or frame:GetHeight() or 0)
|
|
end
|
|
safecall( content.obj.LayoutFinished, content.obj, nil, height )
|
|
end
|
|
)
|
|
|
|
-- A single control fills the whole content area
|
|
AceGUI:RegisterLayout("Fill",
|
|
function(content, children)
|
|
if children[1] then
|
|
children[1]:SetWidth(content:GetWidth() or 0)
|
|
children[1]:SetHeight(content:GetHeight() or 0)
|
|
children[1].frame:SetAllPoints(content)
|
|
children[1].frame:Show()
|
|
safecall( content.obj.LayoutFinished, content.obj, nil, children[1].frame:GetHeight() )
|
|
end
|
|
end
|
|
)
|
|
|
|
AceGUI:RegisterLayout("Flow",
|
|
function(content, children)
|
|
--used height so far
|
|
local height = 0
|
|
--width used in the current row
|
|
local usedwidth = 0
|
|
--height of the current row
|
|
local rowheight = 0
|
|
local rowoffset = 0
|
|
local lastrowoffset
|
|
|
|
local width = content.width or content:GetWidth() or 0
|
|
|
|
--control at the start of the row
|
|
local rowstart
|
|
local rowstartoffset
|
|
local lastrowstart
|
|
local isfullheight
|
|
|
|
local frameoffset
|
|
local lastframeoffset
|
|
local oversize
|
|
for i, child in ipairs(children) do
|
|
oversize = nil
|
|
local frame = child.frame
|
|
local frameheight = frame.height or frame:GetHeight() or 0
|
|
local framewidth = frame.width or frame:GetWidth() or 0
|
|
lastframeoffset = frameoffset
|
|
frameoffset = child.alignoffset or (frameheight / 2)
|
|
|
|
frame:Show()
|
|
frame:ClearAllPoints()
|
|
if i == 1 then
|
|
-- anchor the first control to the top left
|
|
--frame:SetPoint("TOPLEFT",content,"TOPLEFT",0,0)
|
|
rowheight = frameheight
|
|
rowoffset = frameoffset
|
|
rowstart = frame
|
|
rowstartoffset = frameoffset
|
|
usedwidth = framewidth
|
|
if usedwidth > width then
|
|
oversize = true
|
|
end
|
|
else
|
|
-- if there isn't available width for the control start a new row
|
|
-- if a control is "fill" it will be on a row of its own full width
|
|
if usedwidth == 0 or ((framewidth) + usedwidth > width) or child.width == "fill" then
|
|
--anchor the previous row, we will now know its height and offset
|
|
rowstart:SetPoint("TOPLEFT",content,"TOPLEFT",0,-(height+(rowoffset-rowstartoffset)+3))
|
|
height = height + rowheight + 3
|
|
--save this as the rowstart so we can anchor it after the row is complete and we have the max height and offset of controls in it
|
|
rowstart = frame
|
|
rowstartoffset = frameoffset
|
|
rowheight = frameheight
|
|
rowoffset = frameoffset
|
|
usedwidth = frame.width or frame:GetWidth()
|
|
if usedwidth > width then
|
|
oversize = true
|
|
end
|
|
-- put the control on the current row, adding it to the width and checking if the height needs to be increased
|
|
else
|
|
--handles cases where the new height is higher than either control because of the offsets
|
|
--math.max(rowheight-rowoffset+frameoffset, frameheight-frameoffset+rowoffset)
|
|
|
|
--offset is always the larger of the two offsets
|
|
rowoffset = math.max(rowoffset, frameoffset)
|
|
|
|
rowheight = math.max(rowheight,rowoffset+(frameheight/2))
|
|
frame:SetPoint("TOPLEFT",children[i-1].frame,"TOPRIGHT",0,frameoffset-lastframeoffset)
|
|
usedwidth = framewidth + usedwidth
|
|
end
|
|
end
|
|
|
|
if child.width == "fill" then
|
|
child:SetWidth(width)
|
|
frame:SetPoint("RIGHT",content,"RIGHT",0,0)
|
|
|
|
usedwidth = 0
|
|
rowstart = frame
|
|
rowstartoffset = frameoffset
|
|
|
|
if child.DoLayout then
|
|
child:DoLayout()
|
|
end
|
|
rowheight = frame.height or frame:GetHeight() or 0
|
|
rowoffset = child.alignoffset or (rowheight / 2)
|
|
rowstartoffset = rowoffset
|
|
elseif oversize then
|
|
if width > 1 then
|
|
frame:SetPoint("RIGHT",content,"RIGHT",0,0)
|
|
end
|
|
end
|
|
|
|
if child.height == "fill" then
|
|
frame:SetPoint("BOTTOM",content,"BOTTOM")
|
|
isfullheight = true
|
|
break
|
|
end
|
|
end
|
|
|
|
--anchor the last row, if its full height needs a special case since its height has just been changed by the anchor
|
|
if isfullheight then
|
|
rowstart:SetPoint("TOPLEFT",content,"TOPLEFT",0,-height)
|
|
elseif rowstart then
|
|
rowstart:SetPoint("TOPLEFT",content,"TOPLEFT",0,-(height+(rowoffset-rowstartoffset)+3))
|
|
end
|
|
|
|
height = height + rowheight + 3
|
|
safecall( content.obj.LayoutFinished, content.obj, nil, height )
|
|
end
|
|
)
|