100 and status.treewidth > maxtreewidth then
+ self:SetTreeWidth(maxtreewidth, status.treesizable)
+ end
+ treeframe:SetMaxResize(maxtreewidth, 1600)
+ end,
+
+ ["OnHeightSet"] = function(self, height)
+ local content = self.content
+ local contentheight = height - 20
+ if contentheight < 0 then
+ contentheight = 0
+ end
+ content:SetHeight(contentheight)
+ content.height = contentheight
+ end,
+
+ ["SetTreeWidth"] = function(self, treewidth, resizable)
+ if not resizable then
+ if type(treewidth) == 'number' then
+ resizable = false
+ elseif type(treewidth) == 'boolean' then
+ resizable = treewidth
+ treewidth = DEFAULT_TREE_WIDTH
+ else
+ resizable = false
+ treewidth = DEFAULT_TREE_WIDTH
+ end
+ end
+ self.treeframe:SetWidth(treewidth)
+ self.dragger:EnableMouse(resizable)
+
+ local status = self.status or self.localstatus
+ status.treewidth = treewidth
+ status.treesizable = resizable
+
+ -- recalculate the content width
+ if status.fullwidth then
+ self:OnWidthSet(status.fullwidth)
+ end
+ end,
+
+ ["GetTreeWidth"] = function(self)
+ local status = self.status or self.localstatus
+ return status.treewidth or DEFAULT_TREE_WIDTH
+ end,
+
+ ["LayoutFinished"] = function(self, width, height)
+ if self.noAutoHeight then return end
+ self:SetHeight((height or 0) + 20)
+ end
+}
+
+--[[-----------------------------------------------------------------------------
+Constructor
+-------------------------------------------------------------------------------]]
+local PaneBackdrop = {
+ bgFile = "Interface\\ChatFrame\\ChatFrameBackground",
+ edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
+ tile = true, tileSize = 16, edgeSize = 16,
+ insets = { left = 3, right = 3, top = 5, bottom = 3 }
+}
+
+local DraggerBackdrop = {
+ bgFile = "Interface\\Tooltips\\UI-Tooltip-Background",
+ edgeFile = nil,
+ tile = true, tileSize = 16, edgeSize = 0,
+ insets = { left = 3, right = 3, top = 7, bottom = 7 }
+}
+
+local function Constructor()
+ local num = AceGUI:GetNextWidgetNum(Type)
+ local frame = CreateFrame("Frame", nil, UIParent)
+
+ local treeframe = CreateFrame("Frame", nil, frame)
+ treeframe:SetPoint("TOPLEFT")
+ treeframe:SetPoint("BOTTOMLEFT")
+ treeframe:SetWidth(DEFAULT_TREE_WIDTH)
+ treeframe:EnableMouseWheel(true)
+ treeframe:SetBackdrop(PaneBackdrop)
+ treeframe:SetBackdropColor(0.1, 0.1, 0.1, 0.5)
+ treeframe:SetBackdropBorderColor(0.4, 0.4, 0.4)
+ treeframe:SetResizable(true)
+ treeframe:SetMinResize(100, 1)
+ treeframe:SetMaxResize(400, 1600)
+ treeframe:SetScript("OnUpdate", FirstFrameUpdate)
+ treeframe:SetScript("OnSizeChanged", Tree_OnSizeChanged)
+ treeframe:SetScript("OnMouseWheel", Tree_OnMouseWheel)
+
+ local dragger = CreateFrame("Frame", nil, treeframe)
+ dragger:SetWidth(8)
+ dragger:SetPoint("TOP", treeframe, "TOPRIGHT")
+ dragger:SetPoint("BOTTOM", treeframe, "BOTTOMRIGHT")
+ dragger:SetBackdrop(DraggerBackdrop)
+ dragger:SetBackdropColor(1, 1, 1, 0)
+ dragger:SetScript("OnEnter", Dragger_OnEnter)
+ dragger:SetScript("OnLeave", Dragger_OnLeave)
+ dragger:SetScript("OnMouseDown", Dragger_OnMouseDown)
+ dragger:SetScript("OnMouseUp", Dragger_OnMouseUp)
+
+ local scrollbar = CreateFrame("Slider", ("AceConfigDialogTreeGroup%dScrollBar"):format(num), treeframe, "UIPanelScrollBarTemplate")
+ scrollbar:SetScript("OnValueChanged", nil)
+ scrollbar:SetPoint("TOPRIGHT", -10, -26)
+ scrollbar:SetPoint("BOTTOMRIGHT", -10, 26)
+ scrollbar:SetMinMaxValues(0,0)
+ scrollbar:SetValueStep(1)
+ scrollbar:SetValue(0)
+ scrollbar:SetWidth(16)
+ scrollbar:SetScript("OnValueChanged", OnScrollValueChanged)
+
+ local scrollbg = scrollbar:CreateTexture(nil, "BACKGROUND")
+ scrollbg:SetAllPoints(scrollbar)
+ scrollbg:SetColorTexture(0,0,0,0.4)
+
+ local border = CreateFrame("Frame",nil,frame)
+ border:SetPoint("TOPLEFT", treeframe, "TOPRIGHT")
+ border:SetPoint("BOTTOMRIGHT")
+ border:SetBackdrop(PaneBackdrop)
+ border:SetBackdropColor(0.1, 0.1, 0.1, 0.5)
+ border:SetBackdropBorderColor(0.4, 0.4, 0.4)
+
+ --Container Support
+ local content = CreateFrame("Frame", nil, border)
+ content:SetPoint("TOPLEFT", 10, -10)
+ content:SetPoint("BOTTOMRIGHT", -10, 10)
+
+ local widget = {
+ frame = frame,
+ lines = {},
+ levels = {},
+ buttons = {},
+ hasChildren = {},
+ localstatus = { groups = {}, scrollvalue = 0 },
+ filter = false,
+ treeframe = treeframe,
+ dragger = dragger,
+ scrollbar = scrollbar,
+ border = border,
+ content = content,
+ type = Type
+ }
+ for method, func in pairs(methods) do
+ widget[method] = func
+ end
+ treeframe.obj, dragger.obj, scrollbar.obj = widget, widget, widget
+
+ return AceGUI:RegisterAsContainer(widget)
+end
+
+AceGUI:RegisterWidgetType(Type, Constructor, Version)
diff --git a/Libs/AceGUI-3.0/widgets/AceGUIContainer-Window.lua b/Libs/AceGUI-3.0/widgets/AceGUIContainer-Window.lua
new file mode 100644
index 0000000..2e28a3d
--- /dev/null
+++ b/Libs/AceGUI-3.0/widgets/AceGUIContainer-Window.lua
@@ -0,0 +1,336 @@
+local AceGUI = LibStub("AceGUI-3.0")
+
+-- Lua APIs
+local pairs, assert, type = pairs, assert, type
+
+-- WoW APIs
+local PlaySound = PlaySound
+local CreateFrame, UIParent = CreateFrame, UIParent
+
+-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
+-- List them here for Mikk's FindGlobals script
+-- GLOBALS: GameFontNormal
+
+----------------
+-- Main Frame --
+----------------
+--[[
+ Events :
+ OnClose
+
+]]
+do
+ local Type = "Window"
+ local Version = 6
+
+ local function frameOnShow(this)
+ this.obj:Fire("OnShow")
+ end
+
+ local function frameOnClose(this)
+ this.obj:Fire("OnClose")
+ end
+
+ local function closeOnClick(this)
+ PlaySound(799) -- SOUNDKIT.GS_TITLE_OPTION_EXIT
+ this.obj:Hide()
+ end
+
+ local function frameOnMouseDown(this)
+ AceGUI:ClearFocus()
+ end
+
+ local function titleOnMouseDown(this)
+ this:GetParent():StartMoving()
+ AceGUI:ClearFocus()
+ end
+
+ local function frameOnMouseUp(this)
+ local frame = this:GetParent()
+ frame:StopMovingOrSizing()
+ local self = frame.obj
+ local status = self.status or self.localstatus
+ status.width = frame:GetWidth()
+ status.height = frame:GetHeight()
+ status.top = frame:GetTop()
+ status.left = frame:GetLeft()
+ end
+
+ local function sizerseOnMouseDown(this)
+ this:GetParent():StartSizing("BOTTOMRIGHT")
+ AceGUI:ClearFocus()
+ end
+
+ local function sizersOnMouseDown(this)
+ this:GetParent():StartSizing("BOTTOM")
+ AceGUI:ClearFocus()
+ end
+
+ local function sizereOnMouseDown(this)
+ this:GetParent():StartSizing("RIGHT")
+ AceGUI:ClearFocus()
+ end
+
+ local function sizerOnMouseUp(this)
+ this:GetParent():StopMovingOrSizing()
+ end
+
+ local function SetTitle(self,title)
+ self.titletext:SetText(title)
+ end
+
+ local function SetStatusText(self,text)
+ -- self.statustext:SetText(text)
+ end
+
+ local function Hide(self)
+ self.frame:Hide()
+ end
+
+ local function Show(self)
+ self.frame:Show()
+ end
+
+ local function OnAcquire(self)
+ self.frame:SetParent(UIParent)
+ self.frame:SetFrameStrata("FULLSCREEN_DIALOG")
+ self:ApplyStatus()
+ self:EnableResize(true)
+ self:Show()
+ end
+
+ local function OnRelease(self)
+ self.status = nil
+ for k in pairs(self.localstatus) do
+ self.localstatus[k] = nil
+ end
+ end
+
+ -- called to set an external table to store status in
+ local function SetStatusTable(self, status)
+ assert(type(status) == "table")
+ self.status = status
+ self:ApplyStatus()
+ end
+
+ local function ApplyStatus(self)
+ local status = self.status or self.localstatus
+ local frame = self.frame
+ self:SetWidth(status.width or 700)
+ self:SetHeight(status.height or 500)
+ if status.top and status.left then
+ frame:SetPoint("TOP",UIParent,"BOTTOM",0,status.top)
+ frame:SetPoint("LEFT",UIParent,"LEFT",status.left,0)
+ else
+ frame:SetPoint("CENTER",UIParent,"CENTER")
+ end
+ end
+
+ local function OnWidthSet(self, width)
+ local content = self.content
+ local contentwidth = width - 34
+ if contentwidth < 0 then
+ contentwidth = 0
+ end
+ content:SetWidth(contentwidth)
+ content.width = contentwidth
+ end
+
+
+ local function OnHeightSet(self, height)
+ local content = self.content
+ local contentheight = height - 57
+ if contentheight < 0 then
+ contentheight = 0
+ end
+ content:SetHeight(contentheight)
+ content.height = contentheight
+ end
+
+ local function EnableResize(self, state)
+ local func = state and "Show" or "Hide"
+ self.sizer_se[func](self.sizer_se)
+ self.sizer_s[func](self.sizer_s)
+ self.sizer_e[func](self.sizer_e)
+ end
+
+ local function Constructor()
+ local frame = CreateFrame("Frame",nil,UIParent)
+ local self = {}
+ self.type = "Window"
+
+ self.Hide = Hide
+ self.Show = Show
+ self.SetTitle = SetTitle
+ self.OnRelease = OnRelease
+ self.OnAcquire = OnAcquire
+ self.SetStatusText = SetStatusText
+ self.SetStatusTable = SetStatusTable
+ self.ApplyStatus = ApplyStatus
+ self.OnWidthSet = OnWidthSet
+ self.OnHeightSet = OnHeightSet
+ self.EnableResize = EnableResize
+
+ self.localstatus = {}
+
+ self.frame = frame
+ frame.obj = self
+ frame:SetWidth(700)
+ frame:SetHeight(500)
+ frame:SetPoint("CENTER",UIParent,"CENTER",0,0)
+ frame:EnableMouse()
+ frame:SetMovable(true)
+ frame:SetResizable(true)
+ frame:SetFrameStrata("FULLSCREEN_DIALOG")
+ frame:SetScript("OnMouseDown", frameOnMouseDown)
+
+ frame:SetScript("OnShow",frameOnShow)
+ frame:SetScript("OnHide",frameOnClose)
+ frame:SetMinResize(240,240)
+ frame:SetToplevel(true)
+
+ local titlebg = frame:CreateTexture(nil, "BACKGROUND")
+ titlebg:SetTexture(251966) -- Interface\\PaperDollInfoFrame\\UI-GearManager-Title-Background
+ titlebg:SetPoint("TOPLEFT", 9, -6)
+ titlebg:SetPoint("BOTTOMRIGHT", frame, "TOPRIGHT", -28, -24)
+
+ local dialogbg = frame:CreateTexture(nil, "BACKGROUND")
+ dialogbg:SetTexture(137056) -- Interface\\Tooltips\\UI-Tooltip-Background
+ dialogbg:SetPoint("TOPLEFT", 8, -24)
+ dialogbg:SetPoint("BOTTOMRIGHT", -6, 8)
+ dialogbg:SetVertexColor(0, 0, 0, .75)
+
+ local topleft = frame:CreateTexture(nil, "BORDER")
+ topleft:SetTexture(251963) -- Interface\\PaperDollInfoFrame\\UI-GearManager-Border
+ topleft:SetWidth(64)
+ topleft:SetHeight(64)
+ topleft:SetPoint("TOPLEFT")
+ topleft:SetTexCoord(0.501953125, 0.625, 0, 1)
+
+ local topright = frame:CreateTexture(nil, "BORDER")
+ topright:SetTexture(251963) -- Interface\\PaperDollInfoFrame\\UI-GearManager-Border
+ topright:SetWidth(64)
+ topright:SetHeight(64)
+ topright:SetPoint("TOPRIGHT")
+ topright:SetTexCoord(0.625, 0.75, 0, 1)
+
+ local top = frame:CreateTexture(nil, "BORDER")
+ top:SetTexture(251963) -- Interface\\PaperDollInfoFrame\\UI-GearManager-Border
+ top:SetHeight(64)
+ top:SetPoint("TOPLEFT", topleft, "TOPRIGHT")
+ top:SetPoint("TOPRIGHT", topright, "TOPLEFT")
+ top:SetTexCoord(0.25, 0.369140625, 0, 1)
+
+ local bottomleft = frame:CreateTexture(nil, "BORDER")
+ bottomleft:SetTexture(251963) -- Interface\\PaperDollInfoFrame\\UI-GearManager-Border
+ bottomleft:SetWidth(64)
+ bottomleft:SetHeight(64)
+ bottomleft:SetPoint("BOTTOMLEFT")
+ bottomleft:SetTexCoord(0.751953125, 0.875, 0, 1)
+
+ local bottomright = frame:CreateTexture(nil, "BORDER")
+ bottomright:SetTexture(251963) -- Interface\\PaperDollInfoFrame\\UI-GearManager-Border
+ bottomright:SetWidth(64)
+ bottomright:SetHeight(64)
+ bottomright:SetPoint("BOTTOMRIGHT")
+ bottomright:SetTexCoord(0.875, 1, 0, 1)
+
+ local bottom = frame:CreateTexture(nil, "BORDER")
+ bottom:SetTexture(251963) -- Interface\\PaperDollInfoFrame\\UI-GearManager-Border
+ bottom:SetHeight(64)
+ bottom:SetPoint("BOTTOMLEFT", bottomleft, "BOTTOMRIGHT")
+ bottom:SetPoint("BOTTOMRIGHT", bottomright, "BOTTOMLEFT")
+ bottom:SetTexCoord(0.376953125, 0.498046875, 0, 1)
+
+ local left = frame:CreateTexture(nil, "BORDER")
+ left:SetTexture(251963) -- Interface\\PaperDollInfoFrame\\UI-GearManager-Border
+ left:SetWidth(64)
+ left:SetPoint("TOPLEFT", topleft, "BOTTOMLEFT")
+ left:SetPoint("BOTTOMLEFT", bottomleft, "TOPLEFT")
+ left:SetTexCoord(0.001953125, 0.125, 0, 1)
+
+ local right = frame:CreateTexture(nil, "BORDER")
+ right:SetTexture(251963) -- Interface\\PaperDollInfoFrame\\UI-GearManager-Border
+ right:SetWidth(64)
+ right:SetPoint("TOPRIGHT", topright, "BOTTOMRIGHT")
+ right:SetPoint("BOTTOMRIGHT", bottomright, "TOPRIGHT")
+ right:SetTexCoord(0.1171875, 0.2421875, 0, 1)
+
+ local close = CreateFrame("Button", nil, frame, "UIPanelCloseButton")
+ close:SetPoint("TOPRIGHT", 2, 1)
+ close:SetScript("OnClick", closeOnClick)
+ self.closebutton = close
+ close.obj = self
+
+ local titletext = frame:CreateFontString(nil, "ARTWORK")
+ titletext:SetFontObject(GameFontNormal)
+ titletext:SetPoint("TOPLEFT", 12, -8)
+ titletext:SetPoint("TOPRIGHT", -32, -8)
+ self.titletext = titletext
+
+ local title = CreateFrame("Button", nil, frame)
+ title:SetPoint("TOPLEFT", titlebg)
+ title:SetPoint("BOTTOMRIGHT", titlebg)
+ title:EnableMouse()
+ title:SetScript("OnMouseDown",titleOnMouseDown)
+ title:SetScript("OnMouseUp", frameOnMouseUp)
+ self.title = title
+
+ local sizer_se = CreateFrame("Frame",nil,frame)
+ sizer_se:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",0,0)
+ sizer_se:SetWidth(25)
+ sizer_se:SetHeight(25)
+ sizer_se:EnableMouse()
+ sizer_se:SetScript("OnMouseDown",sizerseOnMouseDown)
+ sizer_se:SetScript("OnMouseUp", sizerOnMouseUp)
+ self.sizer_se = sizer_se
+
+ local line1 = sizer_se:CreateTexture(nil, "BACKGROUND")
+ self.line1 = line1
+ line1:SetWidth(14)
+ line1:SetHeight(14)
+ line1:SetPoint("BOTTOMRIGHT", -8, 8)
+ line1:SetTexture(137057) -- Interface\\Tooltips\\UI-Tooltip-Border
+ local x = 0.1 * 14/17
+ line1:SetTexCoord(0.05 - x, 0.5, 0.05, 0.5 + x, 0.05, 0.5 - x, 0.5 + x, 0.5)
+
+ local line2 = sizer_se:CreateTexture(nil, "BACKGROUND")
+ self.line2 = line2
+ line2:SetWidth(8)
+ line2:SetHeight(8)
+ line2:SetPoint("BOTTOMRIGHT", -8, 8)
+ line2:SetTexture(137057) -- Interface\\Tooltips\\UI-Tooltip-Border
+ local x = 0.1 * 8/17
+ line2:SetTexCoord(0.05 - x, 0.5, 0.05, 0.5 + x, 0.05, 0.5 - x, 0.5 + x, 0.5)
+
+ local sizer_s = CreateFrame("Frame",nil,frame)
+ sizer_s:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",-25,0)
+ sizer_s:SetPoint("BOTTOMLEFT",frame,"BOTTOMLEFT",0,0)
+ sizer_s:SetHeight(25)
+ sizer_s:EnableMouse()
+ sizer_s:SetScript("OnMouseDown",sizersOnMouseDown)
+ sizer_s:SetScript("OnMouseUp", sizerOnMouseUp)
+ self.sizer_s = sizer_s
+
+ local sizer_e = CreateFrame("Frame",nil,frame)
+ sizer_e:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",0,25)
+ sizer_e:SetPoint("TOPRIGHT",frame,"TOPRIGHT",0,0)
+ sizer_e:SetWidth(25)
+ sizer_e:EnableMouse()
+ sizer_e:SetScript("OnMouseDown",sizereOnMouseDown)
+ sizer_e:SetScript("OnMouseUp", sizerOnMouseUp)
+ self.sizer_e = sizer_e
+
+ --Container Support
+ local content = CreateFrame("Frame",nil,frame)
+ self.content = content
+ content.obj = self
+ content:SetPoint("TOPLEFT",frame,"TOPLEFT",12,-32)
+ content:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",-12,13)
+
+ AceGUI:RegisterAsContainer(self)
+ return self
+ end
+
+ AceGUI:RegisterWidgetType(Type,Constructor,Version)
+end
diff --git a/Libs/AceGUI-3.0/widgets/AceGUIWidget-Button.lua b/Libs/AceGUI-3.0/widgets/AceGUIWidget-Button.lua
new file mode 100644
index 0000000..0e286ca
--- /dev/null
+++ b/Libs/AceGUI-3.0/widgets/AceGUIWidget-Button.lua
@@ -0,0 +1,103 @@
+--[[-----------------------------------------------------------------------------
+Button Widget
+Graphical Button.
+-------------------------------------------------------------------------------]]
+local Type, Version = "Button", 24
+local AceGUI = LibStub and LibStub("AceGUI-3.0", true)
+if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end
+
+-- Lua APIs
+local pairs = pairs
+
+-- WoW APIs
+local _G = _G
+local PlaySound, CreateFrame, UIParent = PlaySound, CreateFrame, UIParent
+
+--[[-----------------------------------------------------------------------------
+Scripts
+-------------------------------------------------------------------------------]]
+local function Button_OnClick(frame, ...)
+ AceGUI:ClearFocus()
+ PlaySound(852) -- SOUNDKIT.IG_MAINMENU_OPTION
+ frame.obj:Fire("OnClick", ...)
+end
+
+local function Control_OnEnter(frame)
+ frame.obj:Fire("OnEnter")
+end
+
+local function Control_OnLeave(frame)
+ frame.obj:Fire("OnLeave")
+end
+
+--[[-----------------------------------------------------------------------------
+Methods
+-------------------------------------------------------------------------------]]
+local methods = {
+ ["OnAcquire"] = function(self)
+ -- restore default values
+ self:SetHeight(24)
+ self:SetWidth(200)
+ self:SetDisabled(false)
+ self:SetAutoWidth(false)
+ self:SetText()
+ end,
+
+ -- ["OnRelease"] = nil,
+
+ ["SetText"] = function(self, text)
+ self.text:SetText(text)
+ if self.autoWidth then
+ self:SetWidth(self.text:GetStringWidth() + 30)
+ end
+ end,
+
+ ["SetAutoWidth"] = function(self, autoWidth)
+ self.autoWidth = autoWidth
+ if self.autoWidth then
+ self:SetWidth(self.text:GetStringWidth() + 30)
+ end
+ end,
+
+ ["SetDisabled"] = function(self, disabled)
+ self.disabled = disabled
+ if disabled then
+ self.frame:Disable()
+ else
+ self.frame:Enable()
+ end
+ end
+}
+
+--[[-----------------------------------------------------------------------------
+Constructor
+-------------------------------------------------------------------------------]]
+local function Constructor()
+ local name = "AceGUI30Button" .. AceGUI:GetNextWidgetNum(Type)
+ local frame = CreateFrame("Button", name, UIParent, "UIPanelButtonTemplate")
+ frame:Hide()
+
+ frame:EnableMouse(true)
+ frame:SetScript("OnClick", Button_OnClick)
+ frame:SetScript("OnEnter", Control_OnEnter)
+ frame:SetScript("OnLeave", Control_OnLeave)
+
+ local text = frame:GetFontString()
+ text:ClearAllPoints()
+ text:SetPoint("TOPLEFT", 15, -1)
+ text:SetPoint("BOTTOMRIGHT", -15, 1)
+ text:SetJustifyV("MIDDLE")
+
+ local widget = {
+ text = text,
+ frame = frame,
+ type = Type
+ }
+ for method, func in pairs(methods) do
+ widget[method] = func
+ end
+
+ return AceGUI:RegisterAsWidget(widget)
+end
+
+AceGUI:RegisterWidgetType(Type, Constructor, Version)
diff --git a/Libs/AceGUI-3.0/widgets/AceGUIWidget-CheckBox.lua b/Libs/AceGUI-3.0/widgets/AceGUIWidget-CheckBox.lua
new file mode 100644
index 0000000..53ef618
--- /dev/null
+++ b/Libs/AceGUI-3.0/widgets/AceGUIWidget-CheckBox.lua
@@ -0,0 +1,296 @@
+--[[-----------------------------------------------------------------------------
+Checkbox Widget
+-------------------------------------------------------------------------------]]
+local Type, Version = "CheckBox", 26
+local AceGUI = LibStub and LibStub("AceGUI-3.0", true)
+if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end
+
+-- Lua APIs
+local select, pairs = select, pairs
+
+-- WoW APIs
+local PlaySound = PlaySound
+local CreateFrame, UIParent = CreateFrame, UIParent
+
+-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
+-- List them here for Mikk's FindGlobals script
+-- GLOBALS: SetDesaturation, GameFontHighlight
+
+--[[-----------------------------------------------------------------------------
+Support functions
+-------------------------------------------------------------------------------]]
+local function AlignImage(self)
+ local img = self.image:GetTexture()
+ self.text:ClearAllPoints()
+ if not img then
+ self.text:SetPoint("LEFT", self.checkbg, "RIGHT")
+ self.text:SetPoint("RIGHT")
+ else
+ self.text:SetPoint("LEFT", self.image, "RIGHT", 1, 0)
+ self.text:SetPoint("RIGHT")
+ end
+end
+
+--[[-----------------------------------------------------------------------------
+Scripts
+-------------------------------------------------------------------------------]]
+local function Control_OnEnter(frame)
+ frame.obj:Fire("OnEnter")
+end
+
+local function Control_OnLeave(frame)
+ frame.obj:Fire("OnLeave")
+end
+
+local function CheckBox_OnMouseDown(frame)
+ local self = frame.obj
+ if not self.disabled then
+ if self.image:GetTexture() then
+ self.text:SetPoint("LEFT", self.image,"RIGHT", 2, -1)
+ else
+ self.text:SetPoint("LEFT", self.checkbg, "RIGHT", 1, -1)
+ end
+ end
+ AceGUI:ClearFocus()
+end
+
+local function CheckBox_OnMouseUp(frame)
+ local self = frame.obj
+ if not self.disabled then
+ self:ToggleChecked()
+
+ if self.checked then
+ PlaySound(856) -- SOUNDKIT.IG_MAINMENU_OPTION_CHECKBOX_ON
+ else -- for both nil and false (tristate)
+ PlaySound(857) -- SOUNDKIT.IG_MAINMENU_OPTION_CHECKBOX_OFF
+ end
+
+ self:Fire("OnValueChanged", self.checked)
+ AlignImage(self)
+ end
+end
+
+--[[-----------------------------------------------------------------------------
+Methods
+-------------------------------------------------------------------------------]]
+local methods = {
+ ["OnAcquire"] = function(self)
+ self:SetType()
+ self:SetValue(false)
+ self:SetTriState(nil)
+ -- height is calculated from the width and required space for the description
+ self:SetWidth(200)
+ self:SetImage()
+ self:SetDisabled(nil)
+ self:SetDescription(nil)
+ end,
+
+ -- ["OnRelease"] = nil,
+
+ ["OnWidthSet"] = function(self, width)
+ if self.desc then
+ self.desc:SetWidth(width - 30)
+ if self.desc:GetText() and self.desc:GetText() ~= "" then
+ self:SetHeight(28 + self.desc:GetStringHeight())
+ end
+ end
+ end,
+
+ ["SetDisabled"] = function(self, disabled)
+ self.disabled = disabled
+ if disabled then
+ self.frame:Disable()
+ self.text:SetTextColor(0.5, 0.5, 0.5)
+ SetDesaturation(self.check, true)
+ if self.desc then
+ self.desc:SetTextColor(0.5, 0.5, 0.5)
+ end
+ else
+ self.frame:Enable()
+ self.text:SetTextColor(1, 1, 1)
+ if self.tristate and self.checked == nil then
+ SetDesaturation(self.check, true)
+ else
+ SetDesaturation(self.check, false)
+ end
+ if self.desc then
+ self.desc:SetTextColor(1, 1, 1)
+ end
+ end
+ end,
+
+ ["SetValue"] = function(self, value)
+ local check = self.check
+ self.checked = value
+ if value then
+ SetDesaturation(check, false)
+ check:Show()
+ else
+ --Nil is the unknown tristate value
+ if self.tristate and value == nil then
+ SetDesaturation(check, true)
+ check:Show()
+ else
+ SetDesaturation(check, false)
+ check:Hide()
+ end
+ end
+ self:SetDisabled(self.disabled)
+ end,
+
+ ["GetValue"] = function(self)
+ return self.checked
+ end,
+
+ ["SetTriState"] = function(self, enabled)
+ self.tristate = enabled
+ self:SetValue(self:GetValue())
+ end,
+
+ ["SetType"] = function(self, type)
+ local checkbg = self.checkbg
+ local check = self.check
+ local highlight = self.highlight
+
+ local size
+ if type == "radio" then
+ size = 16
+ checkbg:SetTexture(130843) -- Interface\\Buttons\\UI-RadioButton
+ checkbg:SetTexCoord(0, 0.25, 0, 1)
+ check:SetTexture(130843) -- Interface\\Buttons\\UI-RadioButton
+ check:SetTexCoord(0.25, 0.5, 0, 1)
+ check:SetBlendMode("ADD")
+ highlight:SetTexture(130843) -- Interface\\Buttons\\UI-RadioButton
+ highlight:SetTexCoord(0.5, 0.75, 0, 1)
+ else
+ size = 24
+ checkbg:SetTexture(130755) -- Interface\\Buttons\\UI-CheckBox-Up
+ checkbg:SetTexCoord(0, 1, 0, 1)
+ check:SetTexture(130751) -- Interface\\Buttons\\UI-CheckBox-Check
+ check:SetTexCoord(0, 1, 0, 1)
+ check:SetBlendMode("BLEND")
+ highlight:SetTexture(130753) -- Interface\\Buttons\\UI-CheckBox-Highlight
+ highlight:SetTexCoord(0, 1, 0, 1)
+ end
+ checkbg:SetHeight(size)
+ checkbg:SetWidth(size)
+ end,
+
+ ["ToggleChecked"] = function(self)
+ local value = self:GetValue()
+ if self.tristate then
+ --cycle in true, nil, false order
+ if value then
+ self:SetValue(nil)
+ elseif value == nil then
+ self:SetValue(false)
+ else
+ self:SetValue(true)
+ end
+ else
+ self:SetValue(not self:GetValue())
+ end
+ end,
+
+ ["SetLabel"] = function(self, label)
+ self.text:SetText(label)
+ end,
+
+ ["SetDescription"] = function(self, desc)
+ if desc then
+ if not self.desc then
+ local desc = self.frame:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
+ desc:ClearAllPoints()
+ desc:SetPoint("TOPLEFT", self.checkbg, "TOPRIGHT", 5, -21)
+ desc:SetWidth(self.frame.width - 30)
+ desc:SetPoint("RIGHT", self.frame, "RIGHT", -30, 0)
+ desc:SetJustifyH("LEFT")
+ desc:SetJustifyV("TOP")
+ self.desc = desc
+ end
+ self.desc:Show()
+ --self.text:SetFontObject(GameFontNormal)
+ self.desc:SetText(desc)
+ self:SetHeight(28 + self.desc:GetStringHeight())
+ else
+ if self.desc then
+ self.desc:SetText("")
+ self.desc:Hide()
+ end
+ --self.text:SetFontObject(GameFontHighlight)
+ self:SetHeight(24)
+ end
+ end,
+
+ ["SetImage"] = function(self, path, ...)
+ local image = self.image
+ image:SetTexture(path)
+
+ if image:GetTexture() then
+ local n = select("#", ...)
+ if n == 4 or n == 8 then
+ image:SetTexCoord(...)
+ else
+ image:SetTexCoord(0, 1, 0, 1)
+ end
+ end
+ AlignImage(self)
+ end
+}
+
+--[[-----------------------------------------------------------------------------
+Constructor
+-------------------------------------------------------------------------------]]
+local function Constructor()
+ local frame = CreateFrame("Button", nil, UIParent)
+ frame:Hide()
+
+ frame:EnableMouse(true)
+ frame:SetScript("OnEnter", Control_OnEnter)
+ frame:SetScript("OnLeave", Control_OnLeave)
+ frame:SetScript("OnMouseDown", CheckBox_OnMouseDown)
+ frame:SetScript("OnMouseUp", CheckBox_OnMouseUp)
+
+ local checkbg = frame:CreateTexture(nil, "ARTWORK")
+ checkbg:SetWidth(24)
+ checkbg:SetHeight(24)
+ checkbg:SetPoint("TOPLEFT")
+ checkbg:SetTexture(130755) -- Interface\\Buttons\\UI-CheckBox-Up
+
+ local check = frame:CreateTexture(nil, "OVERLAY")
+ check:SetAllPoints(checkbg)
+ check:SetTexture(130751) -- Interface\\Buttons\\UI-CheckBox-Check
+
+ local text = frame:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
+ text:SetJustifyH("LEFT")
+ text:SetHeight(18)
+ text:SetPoint("LEFT", checkbg, "RIGHT")
+ text:SetPoint("RIGHT")
+
+ local highlight = frame:CreateTexture(nil, "HIGHLIGHT")
+ highlight:SetTexture(130753) -- Interface\\Buttons\\UI-CheckBox-Highlight
+ highlight:SetBlendMode("ADD")
+ highlight:SetAllPoints(checkbg)
+
+ local image = frame:CreateTexture(nil, "OVERLAY")
+ image:SetHeight(16)
+ image:SetWidth(16)
+ image:SetPoint("LEFT", checkbg, "RIGHT", 1, 0)
+
+ local widget = {
+ checkbg = checkbg,
+ check = check,
+ text = text,
+ highlight = highlight,
+ image = image,
+ frame = frame,
+ type = Type
+ }
+ for method, func in pairs(methods) do
+ widget[method] = func
+ end
+
+ return AceGUI:RegisterAsWidget(widget)
+end
+
+AceGUI:RegisterWidgetType(Type, Constructor, Version)
diff --git a/Libs/AceGUI-3.0/widgets/AceGUIWidget-ColorPicker.lua b/Libs/AceGUI-3.0/widgets/AceGUIWidget-ColorPicker.lua
new file mode 100644
index 0000000..1101162
--- /dev/null
+++ b/Libs/AceGUI-3.0/widgets/AceGUIWidget-ColorPicker.lua
@@ -0,0 +1,190 @@
+--[[-----------------------------------------------------------------------------
+ColorPicker Widget
+-------------------------------------------------------------------------------]]
+local Type, Version = "ColorPicker", 25
+local AceGUI = LibStub and LibStub("AceGUI-3.0", true)
+if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end
+
+-- Lua APIs
+local pairs = pairs
+
+-- WoW APIs
+local CreateFrame, UIParent = CreateFrame, UIParent
+
+-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
+-- List them here for Mikk's FindGlobals script
+-- GLOBALS: ColorPickerFrame, OpacitySliderFrame
+
+--[[-----------------------------------------------------------------------------
+Support functions
+-------------------------------------------------------------------------------]]
+local function ColorCallback(self, r, g, b, a, isAlpha)
+ if not self.HasAlpha then
+ a = 1
+ end
+ self:SetColor(r, g, b, a)
+ if ColorPickerFrame:IsVisible() then
+ --colorpicker is still open
+ self:Fire("OnValueChanged", r, g, b, a)
+ else
+ --colorpicker is closed, color callback is first, ignore it,
+ --alpha callback is the final call after it closes so confirm now
+ if isAlpha then
+ self:Fire("OnValueConfirmed", r, g, b, a)
+ end
+ end
+end
+
+--[[-----------------------------------------------------------------------------
+Scripts
+-------------------------------------------------------------------------------]]
+local function Control_OnEnter(frame)
+ frame.obj:Fire("OnEnter")
+end
+
+local function Control_OnLeave(frame)
+ frame.obj:Fire("OnLeave")
+end
+
+local function ColorSwatch_OnClick(frame)
+ ColorPickerFrame:Hide()
+ local self = frame.obj
+ if not self.disabled then
+ ColorPickerFrame:SetFrameStrata("FULLSCREEN_DIALOG")
+ ColorPickerFrame:SetFrameLevel(frame:GetFrameLevel() + 10)
+ ColorPickerFrame:SetClampedToScreen(true)
+
+ ColorPickerFrame.func = function()
+ local r, g, b = ColorPickerFrame:GetColorRGB()
+ local a = 1 - OpacitySliderFrame:GetValue()
+ ColorCallback(self, r, g, b, a)
+ end
+
+ ColorPickerFrame.hasOpacity = self.HasAlpha
+ ColorPickerFrame.opacityFunc = function()
+ local r, g, b = ColorPickerFrame:GetColorRGB()
+ local a = 1 - OpacitySliderFrame:GetValue()
+ ColorCallback(self, r, g, b, a, true)
+ end
+
+ local r, g, b, a = self.r, self.g, self.b, self.a
+ if self.HasAlpha then
+ ColorPickerFrame.opacity = 1 - (a or 0)
+ end
+ ColorPickerFrame:SetColorRGB(r, g, b)
+
+ ColorPickerFrame.cancelFunc = function()
+ ColorCallback(self, r, g, b, a, true)
+ end
+
+ ColorPickerFrame:Show()
+ end
+ AceGUI:ClearFocus()
+end
+
+--[[-----------------------------------------------------------------------------
+Methods
+-------------------------------------------------------------------------------]]
+local methods = {
+ ["OnAcquire"] = function(self)
+ self:SetHeight(24)
+ self:SetWidth(200)
+ self:SetHasAlpha(false)
+ self:SetColor(0, 0, 0, 1)
+ self:SetDisabled(nil)
+ self:SetLabel(nil)
+ end,
+
+ -- ["OnRelease"] = nil,
+
+ ["SetLabel"] = function(self, text)
+ self.text:SetText(text)
+ end,
+
+ ["SetColor"] = function(self, r, g, b, a)
+ self.r = r
+ self.g = g
+ self.b = b
+ self.a = a or 1
+ self.colorSwatch:SetVertexColor(r, g, b, a)
+ end,
+
+ ["SetHasAlpha"] = function(self, HasAlpha)
+ self.HasAlpha = HasAlpha
+ end,
+
+ ["SetDisabled"] = function(self, disabled)
+ self.disabled = disabled
+ if self.disabled then
+ self.frame:Disable()
+ self.text:SetTextColor(0.5, 0.5, 0.5)
+ else
+ self.frame:Enable()
+ self.text:SetTextColor(1, 1, 1)
+ end
+ end
+}
+
+--[[-----------------------------------------------------------------------------
+Constructor
+-------------------------------------------------------------------------------]]
+local function Constructor()
+ local frame = CreateFrame("Button", nil, UIParent)
+ frame:Hide()
+
+ frame:EnableMouse(true)
+ frame:SetScript("OnEnter", Control_OnEnter)
+ frame:SetScript("OnLeave", Control_OnLeave)
+ frame:SetScript("OnClick", ColorSwatch_OnClick)
+
+ local colorSwatch = frame:CreateTexture(nil, "OVERLAY")
+ colorSwatch:SetWidth(19)
+ colorSwatch:SetHeight(19)
+ colorSwatch:SetTexture(130939) -- Interface\\ChatFrame\\ChatFrameColorSwatch
+ colorSwatch:SetPoint("LEFT")
+
+ local texture = frame:CreateTexture(nil, "BACKGROUND")
+ colorSwatch.background = texture
+ texture:SetWidth(16)
+ texture:SetHeight(16)
+ texture:SetColorTexture(1, 1, 1)
+ texture:SetPoint("CENTER", colorSwatch)
+ texture:Show()
+
+ local checkers = frame:CreateTexture(nil, "BACKGROUND")
+ colorSwatch.checkers = checkers
+ checkers:SetWidth(14)
+ checkers:SetHeight(14)
+ checkers:SetTexture(188523) -- Tileset\\Generic\\Checkers
+ checkers:SetTexCoord(.25, 0, 0.5, .25)
+ checkers:SetDesaturated(true)
+ checkers:SetVertexColor(1, 1, 1, 0.75)
+ checkers:SetPoint("CENTER", colorSwatch)
+ checkers:Show()
+
+ local text = frame:CreateFontString(nil,"OVERLAY","GameFontHighlight")
+ text:SetHeight(24)
+ text:SetJustifyH("LEFT")
+ text:SetTextColor(1, 1, 1)
+ text:SetPoint("LEFT", colorSwatch, "RIGHT", 2, 0)
+ text:SetPoint("RIGHT")
+
+ --local highlight = frame:CreateTexture(nil, "HIGHLIGHT")
+ --highlight:SetTexture(136810) -- Interface\\QuestFrame\\UI-QuestTitleHighlight
+ --highlight:SetBlendMode("ADD")
+ --highlight:SetAllPoints(frame)
+
+ local widget = {
+ colorSwatch = colorSwatch,
+ text = text,
+ frame = frame,
+ type = Type
+ }
+ for method, func in pairs(methods) do
+ widget[method] = func
+ end
+
+ return AceGUI:RegisterAsWidget(widget)
+end
+
+AceGUI:RegisterWidgetType(Type, Constructor, Version)
diff --git a/Libs/AceGUI-3.0/widgets/AceGUIWidget-DropDown-Items.lua b/Libs/AceGUI-3.0/widgets/AceGUIWidget-DropDown-Items.lua
new file mode 100644
index 0000000..7ae1401
--- /dev/null
+++ b/Libs/AceGUI-3.0/widgets/AceGUIWidget-DropDown-Items.lua
@@ -0,0 +1,471 @@
+--[[ $Id: AceGUIWidget-DropDown-Items.lua 1202 2019-05-15 23:11:22Z nevcairiel $ ]]--
+
+local AceGUI = LibStub("AceGUI-3.0")
+
+-- Lua APIs
+local select, assert = select, assert
+
+-- WoW APIs
+local PlaySound = PlaySound
+local CreateFrame = CreateFrame
+
+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 function fixstrata(strata, parent, ...)
+ local i = 1
+ local child = select(i, ...)
+ parent:SetFrameStrata(strata)
+ while child do
+ fixstrata(strata, child, child:GetChildren())
+ i = i + 1
+ child = select(i, ...)
+ end
+end
+
+-- ItemBase is the base "class" for all dropdown items.
+-- Each item has to use ItemBase.Create(widgetType) to
+-- create an initial 'self' value.
+-- ItemBase will add common functions and ui event handlers.
+-- Be sure to keep basic usage when you override functions.
+
+local ItemBase = {
+ -- NOTE: The ItemBase version is added to each item's version number
+ -- to ensure proper updates on ItemBase changes.
+ -- Use at least 1000er steps.
+ version = 1000,
+ counter = 0,
+}
+
+function ItemBase.Frame_OnEnter(this)
+ local self = this.obj
+
+ if self.useHighlight then
+ self.highlight:Show()
+ end
+ self:Fire("OnEnter")
+
+ if self.specialOnEnter then
+ self.specialOnEnter(self)
+ end
+end
+
+function ItemBase.Frame_OnLeave(this)
+ local self = this.obj
+
+ self.highlight:Hide()
+ self:Fire("OnLeave")
+
+ if self.specialOnLeave then
+ self.specialOnLeave(self)
+ end
+end
+
+-- exported, AceGUI callback
+function ItemBase.OnAcquire(self)
+ self.frame:SetToplevel(true)
+ self.frame:SetFrameStrata("FULLSCREEN_DIALOG")
+end
+
+-- exported, AceGUI callback
+function ItemBase.OnRelease(self)
+ self:SetDisabled(false)
+ self.pullout = nil
+ self.frame:SetParent(nil)
+ self.frame:ClearAllPoints()
+ self.frame:Hide()
+end
+
+-- exported
+-- NOTE: this is called by a Dropdown-Pullout.
+-- Do not call this method directly
+function ItemBase.SetPullout(self, pullout)
+ self.pullout = pullout
+
+ self.frame:SetParent(nil)
+ self.frame:SetParent(pullout.itemFrame)
+ self.parent = pullout.itemFrame
+ fixlevels(pullout.itemFrame, pullout.itemFrame:GetChildren())
+end
+
+-- exported
+function ItemBase.SetText(self, text)
+ self.text:SetText(text or "")
+end
+
+-- exported
+function ItemBase.GetText(self)
+ return self.text:GetText()
+end
+
+-- exported
+function ItemBase.SetPoint(self, ...)
+ self.frame:SetPoint(...)
+end
+
+-- exported
+function ItemBase.Show(self)
+ self.frame:Show()
+end
+
+-- exported
+function ItemBase.Hide(self)
+ self.frame:Hide()
+end
+
+-- exported
+function ItemBase.SetDisabled(self, disabled)
+ self.disabled = disabled
+ if disabled then
+ self.useHighlight = false
+ self.text:SetTextColor(.5, .5, .5)
+ else
+ self.useHighlight = true
+ self.text:SetTextColor(1, 1, 1)
+ end
+end
+
+-- exported
+-- NOTE: this is called by a Dropdown-Pullout.
+-- Do not call this method directly
+function ItemBase.SetOnLeave(self, func)
+ self.specialOnLeave = func
+end
+
+-- exported
+-- NOTE: this is called by a Dropdown-Pullout.
+-- Do not call this method directly
+function ItemBase.SetOnEnter(self, func)
+ self.specialOnEnter = func
+end
+
+function ItemBase.Create(type)
+ -- NOTE: Most of the following code is copied from AceGUI-3.0/Dropdown widget
+ local count = AceGUI:GetNextWidgetNum(type)
+ local frame = CreateFrame("Button", "AceGUI30DropDownItem"..count)
+ local self = {}
+ self.frame = frame
+ frame.obj = self
+ self.type = type
+
+ self.useHighlight = true
+
+ frame:SetHeight(17)
+ frame:SetFrameStrata("FULLSCREEN_DIALOG")
+
+ local text = frame:CreateFontString(nil,"OVERLAY","GameFontNormalSmall")
+ text:SetTextColor(1,1,1)
+ text:SetJustifyH("LEFT")
+ text:SetPoint("TOPLEFT",frame,"TOPLEFT",18,0)
+ text:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",-8,0)
+ self.text = text
+
+ local highlight = frame:CreateTexture(nil, "OVERLAY")
+ highlight:SetTexture(136810) -- Interface\\QuestFrame\\UI-QuestTitleHighlight
+ highlight:SetBlendMode("ADD")
+ highlight:SetHeight(14)
+ highlight:ClearAllPoints()
+ highlight:SetPoint("RIGHT",frame,"RIGHT",-3,0)
+ highlight:SetPoint("LEFT",frame,"LEFT",5,0)
+ highlight:Hide()
+ self.highlight = highlight
+
+ local check = frame:CreateTexture("OVERLAY")
+ check:SetWidth(16)
+ check:SetHeight(16)
+ check:SetPoint("LEFT",frame,"LEFT",3,-1)
+ check:SetTexture(130751) -- Interface\\Buttons\\UI-CheckBox-Check
+ check:Hide()
+ self.check = check
+
+ local sub = frame:CreateTexture("OVERLAY")
+ sub:SetWidth(16)
+ sub:SetHeight(16)
+ sub:SetPoint("RIGHT",frame,"RIGHT",-3,-1)
+ sub:SetTexture(130940) -- Interface\\ChatFrame\\ChatFrameExpandArrow
+ sub:Hide()
+ self.sub = sub
+
+ frame:SetScript("OnEnter", ItemBase.Frame_OnEnter)
+ frame:SetScript("OnLeave", ItemBase.Frame_OnLeave)
+
+ self.OnAcquire = ItemBase.OnAcquire
+ self.OnRelease = ItemBase.OnRelease
+
+ self.SetPullout = ItemBase.SetPullout
+ self.GetText = ItemBase.GetText
+ self.SetText = ItemBase.SetText
+ self.SetDisabled = ItemBase.SetDisabled
+
+ self.SetPoint = ItemBase.SetPoint
+ self.Show = ItemBase.Show
+ self.Hide = ItemBase.Hide
+
+ self.SetOnLeave = ItemBase.SetOnLeave
+ self.SetOnEnter = ItemBase.SetOnEnter
+
+ return self
+end
+
+-- Register a dummy LibStub library to retrieve the ItemBase, so other addons can use it.
+local IBLib = LibStub:NewLibrary("AceGUI-3.0-DropDown-ItemBase", ItemBase.version)
+if IBLib then
+ IBLib.GetItemBase = function() return ItemBase end
+end
+
+--[[
+ Template for items:
+
+-- Item:
+--
+do
+ local widgetType = "Dropdown-Item-"
+ local widgetVersion = 1
+
+ local function Constructor()
+ local self = ItemBase.Create(widgetType)
+
+ AceGUI:RegisterAsWidget(self)
+ return self
+ end
+
+ AceGUI:RegisterWidgetType(widgetType, Constructor, widgetVersion + ItemBase.version)
+end
+--]]
+
+-- Item: Header
+-- A single text entry.
+-- Special: Different text color and no highlight
+do
+ local widgetType = "Dropdown-Item-Header"
+ local widgetVersion = 1
+
+ local function OnEnter(this)
+ local self = this.obj
+ self:Fire("OnEnter")
+
+ if self.specialOnEnter then
+ self.specialOnEnter(self)
+ end
+ end
+
+ local function OnLeave(this)
+ local self = this.obj
+ self:Fire("OnLeave")
+
+ if self.specialOnLeave then
+ self.specialOnLeave(self)
+ end
+ end
+
+ -- exported, override
+ local function SetDisabled(self, disabled)
+ ItemBase.SetDisabled(self, disabled)
+ if not disabled then
+ self.text:SetTextColor(1, 1, 0)
+ end
+ end
+
+ local function Constructor()
+ local self = ItemBase.Create(widgetType)
+
+ self.SetDisabled = SetDisabled
+
+ self.frame:SetScript("OnEnter", OnEnter)
+ self.frame:SetScript("OnLeave", OnLeave)
+
+ self.text:SetTextColor(1, 1, 0)
+
+ AceGUI:RegisterAsWidget(self)
+ return self
+ end
+
+ AceGUI:RegisterWidgetType(widgetType, Constructor, widgetVersion + ItemBase.version)
+end
+
+-- Item: Execute
+-- A simple button
+do
+ local widgetType = "Dropdown-Item-Execute"
+ local widgetVersion = 1
+
+ local function Frame_OnClick(this, button)
+ local self = this.obj
+ if self.disabled then return end
+ self:Fire("OnClick")
+ if self.pullout then
+ self.pullout:Close()
+ end
+ end
+
+ local function Constructor()
+ local self = ItemBase.Create(widgetType)
+
+ self.frame:SetScript("OnClick", Frame_OnClick)
+
+ AceGUI:RegisterAsWidget(self)
+ return self
+ end
+
+ AceGUI:RegisterWidgetType(widgetType, Constructor, widgetVersion + ItemBase.version)
+end
+
+-- Item: Toggle
+-- Some sort of checkbox for dropdown menus.
+-- Does not close the pullout on click.
+do
+ local widgetType = "Dropdown-Item-Toggle"
+ local widgetVersion = 4
+
+ local function UpdateToggle(self)
+ if self.value then
+ self.check:Show()
+ else
+ self.check:Hide()
+ end
+ end
+
+ local function OnRelease(self)
+ ItemBase.OnRelease(self)
+ self:SetValue(nil)
+ end
+
+ local function Frame_OnClick(this, button)
+ local self = this.obj
+ if self.disabled then return end
+ self.value = not self.value
+ if self.value then
+ PlaySound(856) -- SOUNDKIT.IG_MAINMENU_OPTION_CHECKBOX_ON
+ else
+ PlaySound(857) -- SOUNDKIT.IG_MAINMENU_OPTION_CHECKBOX_OFF
+ end
+ UpdateToggle(self)
+ self:Fire("OnValueChanged", self.value)
+ end
+
+ -- exported
+ local function SetValue(self, value)
+ self.value = value
+ UpdateToggle(self)
+ end
+
+ -- exported
+ local function GetValue(self)
+ return self.value
+ end
+
+ local function Constructor()
+ local self = ItemBase.Create(widgetType)
+
+ self.frame:SetScript("OnClick", Frame_OnClick)
+
+ self.SetValue = SetValue
+ self.GetValue = GetValue
+ self.OnRelease = OnRelease
+
+ AceGUI:RegisterAsWidget(self)
+ return self
+ end
+
+ AceGUI:RegisterWidgetType(widgetType, Constructor, widgetVersion + ItemBase.version)
+end
+
+-- Item: Menu
+-- Shows a submenu on mouse over
+-- Does not close the pullout on click
+do
+ local widgetType = "Dropdown-Item-Menu"
+ local widgetVersion = 2
+
+ local function OnEnter(this)
+ local self = this.obj
+ self:Fire("OnEnter")
+
+ if self.specialOnEnter then
+ self.specialOnEnter(self)
+ end
+
+ self.highlight:Show()
+
+ if not self.disabled and self.submenu then
+ self.submenu:Open("TOPLEFT", self.frame, "TOPRIGHT", self.pullout:GetRightBorderWidth(), 0, self.frame:GetFrameLevel() + 100)
+ end
+ end
+
+ local function OnHide(this)
+ local self = this.obj
+ if self.submenu then
+ self.submenu:Close()
+ end
+ end
+
+ -- exported
+ local function SetMenu(self, menu)
+ assert(menu.type == "Dropdown-Pullout")
+ self.submenu = menu
+ end
+
+ -- exported
+ local function CloseMenu(self)
+ self.submenu:Close()
+ end
+
+ local function Constructor()
+ local self = ItemBase.Create(widgetType)
+
+ self.sub:Show()
+
+ self.frame:SetScript("OnEnter", OnEnter)
+ self.frame:SetScript("OnHide", OnHide)
+
+ self.SetMenu = SetMenu
+ self.CloseMenu = CloseMenu
+
+ AceGUI:RegisterAsWidget(self)
+ return self
+ end
+
+ AceGUI:RegisterWidgetType(widgetType, Constructor, widgetVersion + ItemBase.version)
+end
+
+-- Item: Separator
+-- A single line to separate items
+do
+ local widgetType = "Dropdown-Item-Separator"
+ local widgetVersion = 2
+
+ -- exported, override
+ local function SetDisabled(self, disabled)
+ ItemBase.SetDisabled(self, disabled)
+ self.useHighlight = false
+ end
+
+ local function Constructor()
+ local self = ItemBase.Create(widgetType)
+
+ self.SetDisabled = SetDisabled
+
+ local line = self.frame:CreateTexture(nil, "OVERLAY")
+ line:SetHeight(1)
+ line:SetColorTexture(.5, .5, .5)
+ line:SetPoint("LEFT", self.frame, "LEFT", 10, 0)
+ line:SetPoint("RIGHT", self.frame, "RIGHT", -10, 0)
+
+ self.text:Hide()
+
+ self.useHighlight = false
+
+ AceGUI:RegisterAsWidget(self)
+ return self
+ end
+
+ AceGUI:RegisterWidgetType(widgetType, Constructor, widgetVersion + ItemBase.version)
+end
diff --git a/Libs/AceGUI-3.0/widgets/AceGUIWidget-DropDown.lua b/Libs/AceGUI-3.0/widgets/AceGUIWidget-DropDown.lua
new file mode 100644
index 0000000..5b74046
--- /dev/null
+++ b/Libs/AceGUI-3.0/widgets/AceGUIWidget-DropDown.lua
@@ -0,0 +1,742 @@
+--[[ $Id: AceGUIWidget-DropDown.lua 1236 2020-04-16 07:36:45Z nevcairiel $ ]]--
+local AceGUI = LibStub("AceGUI-3.0")
+
+-- Lua APIs
+local min, max, floor = math.min, math.max, math.floor
+local select, pairs, ipairs, type, tostring = select, pairs, ipairs, type, tostring
+local tsort = table.sort
+
+-- WoW APIs
+local PlaySound = PlaySound
+local UIParent, CreateFrame = UIParent, CreateFrame
+local _G = _G
+
+-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
+-- List them here for Mikk's FindGlobals script
+-- GLOBALS: CLOSE
+
+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 function fixstrata(strata, parent, ...)
+ local i = 1
+ local child = select(i, ...)
+ parent:SetFrameStrata(strata)
+ while child do
+ fixstrata(strata, child, child:GetChildren())
+ i = i + 1
+ child = select(i, ...)
+ end
+end
+
+do
+ local widgetType = "Dropdown-Pullout"
+ local widgetVersion = 3
+
+ --[[ Static data ]]--
+
+ local backdrop = {
+ bgFile = "Interface\\ChatFrame\\ChatFrameBackground",
+ edgeFile = "Interface\\DialogFrame\\UI-DialogBox-Border",
+ edgeSize = 32,
+ tileSize = 32,
+ tile = true,
+ insets = { left = 11, right = 12, top = 12, bottom = 11 },
+ }
+ local sliderBackdrop = {
+ bgFile = "Interface\\Buttons\\UI-SliderBar-Background",
+ edgeFile = "Interface\\Buttons\\UI-SliderBar-Border",
+ tile = true, tileSize = 8, edgeSize = 8,
+ insets = { left = 3, right = 3, top = 3, bottom = 3 }
+ }
+
+ local defaultWidth = 200
+ local defaultMaxHeight = 600
+
+ --[[ UI Event Handlers ]]--
+
+ -- HACK: This should be no part of the pullout, but there
+ -- is no other 'clean' way to response to any item-OnEnter
+ -- Used to close Submenus when an other item is entered
+ local function OnEnter(item)
+ local self = item.pullout
+ for k, v in ipairs(self.items) do
+ if v.CloseMenu and v ~= item then
+ v:CloseMenu()
+ end
+ end
+ end
+
+ -- See the note in Constructor() for each scroll related function
+ local function OnMouseWheel(this, value)
+ this.obj:MoveScroll(value)
+ end
+
+ local function OnScrollValueChanged(this, value)
+ this.obj:SetScroll(value)
+ end
+
+ local function OnSizeChanged(this)
+ this.obj:FixScroll()
+ end
+
+ --[[ Exported methods ]]--
+
+ -- exported
+ local function SetScroll(self, value)
+ local status = self.scrollStatus
+ local frame, child = self.scrollFrame, self.itemFrame
+ local height, viewheight = frame:GetHeight(), child:GetHeight()
+
+ local offset
+ if height > viewheight then
+ offset = 0
+ else
+ offset = floor((viewheight - height) / 1000 * value)
+ end
+ child:ClearAllPoints()
+ child:SetPoint("TOPLEFT", frame, "TOPLEFT", 0, offset)
+ child:SetPoint("TOPRIGHT", frame, "TOPRIGHT", self.slider:IsShown() and -12 or 0, offset)
+ status.offset = offset
+ status.scrollvalue = value
+ end
+
+ -- exported
+ local function MoveScroll(self, value)
+ local status = self.scrollStatus
+ local frame, child = self.scrollFrame, self.itemFrame
+ local height, viewheight = frame:GetHeight(), child:GetHeight()
+
+ if height > viewheight then
+ self.slider:Hide()
+ else
+ self.slider:Show()
+ local diff = height - viewheight
+ local delta = 1
+ if value < 0 then
+ delta = -1
+ end
+ self.slider:SetValue(min(max(status.scrollvalue + delta*(1000/(diff/45)),0), 1000))
+ end
+ end
+
+ -- exported
+ local function FixScroll(self)
+ local status = self.scrollStatus
+ local frame, child = self.scrollFrame, self.itemFrame
+ local height, viewheight = frame:GetHeight(), child:GetHeight()
+ local offset = status.offset or 0
+
+ if viewheight < height then
+ self.slider:Hide()
+ child:SetPoint("TOPRIGHT", frame, "TOPRIGHT", 0, offset)
+ self.slider:SetValue(0)
+ else
+ self.slider:Show()
+ local value = (offset / (viewheight - height) * 1000)
+ if value > 1000 then value = 1000 end
+ self.slider:SetValue(value)
+ self:SetScroll(value)
+ if value < 1000 then
+ child:ClearAllPoints()
+ child:SetPoint("TOPLEFT", frame, "TOPLEFT", 0, offset)
+ child:SetPoint("TOPRIGHT", frame, "TOPRIGHT", -12, offset)
+ status.offset = offset
+ end
+ end
+ end
+
+ -- exported, AceGUI callback
+ local function OnAcquire(self)
+ self.frame:SetParent(UIParent)
+ --self.itemFrame:SetToplevel(true)
+ end
+
+ -- exported, AceGUI callback
+ local function OnRelease(self)
+ self:Clear()
+ self.frame:ClearAllPoints()
+ self.frame:Hide()
+ end
+
+ -- exported
+ local function AddItem(self, item)
+ self.items[#self.items + 1] = item
+
+ local h = #self.items * 16
+ self.itemFrame:SetHeight(h)
+ self.frame:SetHeight(min(h + 34, self.maxHeight)) -- +34: 20 for scrollFrame placement (10 offset) and +14 for item placement
+
+ item.frame:SetPoint("LEFT", self.itemFrame, "LEFT")
+ item.frame:SetPoint("RIGHT", self.itemFrame, "RIGHT")
+
+ item:SetPullout(self)
+ item:SetOnEnter(OnEnter)
+ end
+
+ -- exported
+ local function Open(self, point, relFrame, relPoint, x, y)
+ local items = self.items
+ local frame = self.frame
+ local itemFrame = self.itemFrame
+
+ frame:SetPoint(point, relFrame, relPoint, x, y)
+
+
+ local height = 8
+ for i, item in pairs(items) do
+ if i == 1 then
+ item:SetPoint("TOP", itemFrame, "TOP", 0, -2)
+ else
+ item:SetPoint("TOP", items[i-1].frame, "BOTTOM", 0, 1)
+ end
+
+ item:Show()
+
+ height = height + 16
+ end
+ itemFrame:SetHeight(height)
+ fixstrata("TOOLTIP", frame, frame:GetChildren())
+ frame:Show()
+ self:Fire("OnOpen")
+ end
+
+ -- exported
+ local function Close(self)
+ self.frame:Hide()
+ self:Fire("OnClose")
+ end
+
+ -- exported
+ local function Clear(self)
+ local items = self.items
+ for i, item in pairs(items) do
+ AceGUI:Release(item)
+ items[i] = nil
+ end
+ end
+
+ -- exported
+ local function IterateItems(self)
+ return ipairs(self.items)
+ end
+
+ -- exported
+ local function SetHideOnLeave(self, val)
+ self.hideOnLeave = val
+ end
+
+ -- exported
+ local function SetMaxHeight(self, height)
+ self.maxHeight = height or defaultMaxHeight
+ if self.frame:GetHeight() > height then
+ self.frame:SetHeight(height)
+ elseif (self.itemFrame:GetHeight() + 34) < height then
+ self.frame:SetHeight(self.itemFrame:GetHeight() + 34) -- see :AddItem
+ end
+ end
+
+ -- exported
+ local function GetRightBorderWidth(self)
+ return 6 + (self.slider:IsShown() and 12 or 0)
+ end
+
+ -- exported
+ local function GetLeftBorderWidth(self)
+ return 6
+ end
+
+ --[[ Constructor ]]--
+
+ local function Constructor()
+ local count = AceGUI:GetNextWidgetNum(widgetType)
+ local frame = CreateFrame("Frame", "AceGUI30Pullout"..count, UIParent)
+ local self = {}
+ self.count = count
+ self.type = widgetType
+ self.frame = frame
+ frame.obj = self
+
+ self.OnAcquire = OnAcquire
+ self.OnRelease = OnRelease
+
+ self.AddItem = AddItem
+ self.Open = Open
+ self.Close = Close
+ self.Clear = Clear
+ self.IterateItems = IterateItems
+ self.SetHideOnLeave = SetHideOnLeave
+
+ self.SetScroll = SetScroll
+ self.MoveScroll = MoveScroll
+ self.FixScroll = FixScroll
+
+ self.SetMaxHeight = SetMaxHeight
+ self.GetRightBorderWidth = GetRightBorderWidth
+ self.GetLeftBorderWidth = GetLeftBorderWidth
+
+ self.items = {}
+
+ self.scrollStatus = {
+ scrollvalue = 0,
+ }
+
+ self.maxHeight = defaultMaxHeight
+
+ frame:SetBackdrop(backdrop)
+ frame:SetBackdropColor(0, 0, 0)
+ frame:SetFrameStrata("FULLSCREEN_DIALOG")
+ frame:SetClampedToScreen(true)
+ frame:SetWidth(defaultWidth)
+ frame:SetHeight(self.maxHeight)
+ --frame:SetToplevel(true)
+
+ -- NOTE: The whole scroll frame code is copied from the AceGUI-3.0 widget ScrollFrame
+ local scrollFrame = CreateFrame("ScrollFrame", nil, frame)
+ local itemFrame = CreateFrame("Frame", nil, scrollFrame)
+
+ self.scrollFrame = scrollFrame
+ self.itemFrame = itemFrame
+
+ scrollFrame.obj = self
+ itemFrame.obj = self
+
+ local slider = CreateFrame("Slider", "AceGUI30PulloutScrollbar"..count, scrollFrame)
+ slider:SetOrientation("VERTICAL")
+ slider:SetHitRectInsets(0, 0, -10, 0)
+ slider:SetBackdrop(sliderBackdrop)
+ slider:SetWidth(8)
+ slider:SetThumbTexture("Interface\\Buttons\\UI-SliderBar-Button-Vertical")
+ slider:SetFrameStrata("FULLSCREEN_DIALOG")
+ self.slider = slider
+ slider.obj = self
+
+ scrollFrame:SetScrollChild(itemFrame)
+ scrollFrame:SetPoint("TOPLEFT", frame, "TOPLEFT", 6, -12)
+ scrollFrame:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -6, 12)
+ scrollFrame:EnableMouseWheel(true)
+ scrollFrame:SetScript("OnMouseWheel", OnMouseWheel)
+ scrollFrame:SetScript("OnSizeChanged", OnSizeChanged)
+ scrollFrame:SetToplevel(true)
+ scrollFrame:SetFrameStrata("FULLSCREEN_DIALOG")
+
+ itemFrame:SetPoint("TOPLEFT", scrollFrame, "TOPLEFT", 0, 0)
+ itemFrame:SetPoint("TOPRIGHT", scrollFrame, "TOPRIGHT", -12, 0)
+ itemFrame:SetHeight(400)
+ itemFrame:SetToplevel(true)
+ itemFrame:SetFrameStrata("FULLSCREEN_DIALOG")
+
+ slider:SetPoint("TOPLEFT", scrollFrame, "TOPRIGHT", -16, 0)
+ slider:SetPoint("BOTTOMLEFT", scrollFrame, "BOTTOMRIGHT", -16, 0)
+ slider:SetScript("OnValueChanged", OnScrollValueChanged)
+ slider:SetMinMaxValues(0, 1000)
+ slider:SetValueStep(1)
+ slider:SetValue(0)
+
+ scrollFrame:Show()
+ itemFrame:Show()
+ slider:Hide()
+
+ self:FixScroll()
+
+ AceGUI:RegisterAsWidget(self)
+ return self
+ end
+
+ AceGUI:RegisterWidgetType(widgetType, Constructor, widgetVersion)
+end
+
+do
+ local widgetType = "Dropdown"
+ local widgetVersion = 35
+
+ --[[ Static data ]]--
+
+ --[[ UI event handler ]]--
+
+ local function Control_OnEnter(this)
+ this.obj.button:LockHighlight()
+ this.obj:Fire("OnEnter")
+ end
+
+ local function Control_OnLeave(this)
+ this.obj.button:UnlockHighlight()
+ this.obj:Fire("OnLeave")
+ end
+
+ local function Dropdown_OnHide(this)
+ local self = this.obj
+ if self.open then
+ self.pullout:Close()
+ end
+ end
+
+ local function Dropdown_TogglePullout(this)
+ local self = this.obj
+ PlaySound(856) -- SOUNDKIT.IG_MAINMENU_OPTION_CHECKBOX_ON
+ if self.open then
+ self.open = nil
+ self.pullout:Close()
+ AceGUI:ClearFocus()
+ else
+ self.open = true
+ self.pullout:SetWidth(self.pulloutWidth or self.frame:GetWidth())
+ self.pullout:Open("TOPLEFT", self.frame, "BOTTOMLEFT", 0, self.label:IsShown() and -2 or 0)
+ AceGUI:SetFocus(self)
+ end
+ end
+
+ local function OnPulloutOpen(this)
+ local self = this.userdata.obj
+ local value = self.value
+
+ if not self.multiselect then
+ for i, item in this:IterateItems() do
+ item:SetValue(item.userdata.value == value)
+ end
+ end
+
+ self.open = true
+ self:Fire("OnOpened")
+ end
+
+ local function OnPulloutClose(this)
+ local self = this.userdata.obj
+ self.open = nil
+ self:Fire("OnClosed")
+ end
+
+ local function ShowMultiText(self)
+ local text
+ for i, widget in self.pullout:IterateItems() do
+ if widget.type == "Dropdown-Item-Toggle" then
+ if widget:GetValue() then
+ if text then
+ text = text..", "..widget:GetText()
+ else
+ text = widget:GetText()
+ end
+ end
+ end
+ end
+ self:SetText(text)
+ end
+
+ local function OnItemValueChanged(this, event, checked)
+ local self = this.userdata.obj
+
+ if self.multiselect then
+ self:Fire("OnValueChanged", this.userdata.value, checked)
+ ShowMultiText(self)
+ else
+ if checked then
+ self:SetValue(this.userdata.value)
+ self:Fire("OnValueChanged", this.userdata.value)
+ else
+ this:SetValue(true)
+ end
+ if self.open then
+ self.pullout:Close()
+ end
+ end
+ end
+
+ --[[ Exported methods ]]--
+
+ -- exported, AceGUI callback
+ local function OnAcquire(self)
+ local pullout = AceGUI:Create("Dropdown-Pullout")
+ self.pullout = pullout
+ pullout.userdata.obj = self
+ pullout:SetCallback("OnClose", OnPulloutClose)
+ pullout:SetCallback("OnOpen", OnPulloutOpen)
+ self.pullout.frame:SetFrameLevel(self.frame:GetFrameLevel() + 1)
+ fixlevels(self.pullout.frame, self.pullout.frame:GetChildren())
+
+ self:SetHeight(44)
+ self:SetWidth(200)
+ self:SetLabel()
+ self:SetPulloutWidth(nil)
+ self.list = {}
+ end
+
+ -- exported, AceGUI callback
+ local function OnRelease(self)
+ if self.open then
+ self.pullout:Close()
+ end
+ AceGUI:Release(self.pullout)
+ self.pullout = nil
+
+ self:SetText("")
+ self:SetDisabled(false)
+ self:SetMultiselect(false)
+
+ self.value = nil
+ self.list = nil
+ self.open = nil
+ self.hasClose = nil
+
+ self.frame:ClearAllPoints()
+ self.frame:Hide()
+ end
+
+ -- exported
+ local function SetDisabled(self, disabled)
+ self.disabled = disabled
+ if disabled then
+ self.text:SetTextColor(0.5,0.5,0.5)
+ self.button:Disable()
+ self.button_cover:Disable()
+ self.label:SetTextColor(0.5,0.5,0.5)
+ else
+ self.button:Enable()
+ self.button_cover:Enable()
+ self.label:SetTextColor(1,.82,0)
+ self.text:SetTextColor(1,1,1)
+ end
+ end
+
+ -- exported
+ local function ClearFocus(self)
+ if self.open then
+ self.pullout:Close()
+ end
+ end
+
+ -- exported
+ local function SetText(self, text)
+ self.text:SetText(text or "")
+ end
+
+ -- exported
+ local function SetLabel(self, text)
+ if text and text ~= "" then
+ self.label:SetText(text)
+ self.label:Show()
+ self.dropdown:SetPoint("TOPLEFT",self.frame,"TOPLEFT",-15,-14)
+ self:SetHeight(40)
+ self.alignoffset = 26
+ else
+ self.label:SetText("")
+ self.label:Hide()
+ self.dropdown:SetPoint("TOPLEFT",self.frame,"TOPLEFT",-15,0)
+ self:SetHeight(26)
+ self.alignoffset = 12
+ end
+ end
+
+ -- exported
+ local function SetValue(self, value)
+ self:SetText(self.list[value] or "")
+ self.value = value
+ end
+
+ -- exported
+ local function GetValue(self)
+ return self.value
+ end
+
+ -- exported
+ local function SetItemValue(self, item, value)
+ if not self.multiselect then return end
+ for i, widget in self.pullout:IterateItems() do
+ if widget.userdata.value == item then
+ if widget.SetValue then
+ widget:SetValue(value)
+ end
+ end
+ end
+ ShowMultiText(self)
+ end
+
+ -- exported
+ local function SetItemDisabled(self, item, disabled)
+ for i, widget in self.pullout:IterateItems() do
+ if widget.userdata.value == item then
+ widget:SetDisabled(disabled)
+ end
+ end
+ end
+
+ local function AddListItem(self, value, text, itemType)
+ if not itemType then itemType = "Dropdown-Item-Toggle" end
+ local exists = AceGUI:GetWidgetVersion(itemType)
+ if not exists then error(("The given item type, %q, does not exist within AceGUI-3.0"):format(tostring(itemType)), 2) end
+
+ local item = AceGUI:Create(itemType)
+ item:SetText(text)
+ item.userdata.obj = self
+ item.userdata.value = value
+ item:SetCallback("OnValueChanged", OnItemValueChanged)
+ self.pullout:AddItem(item)
+ end
+
+ local function AddCloseButton(self)
+ if not self.hasClose then
+ local close = AceGUI:Create("Dropdown-Item-Execute")
+ close:SetText(CLOSE)
+ self.pullout:AddItem(close)
+ self.hasClose = true
+ end
+ end
+
+ -- exported
+ local sortlist = {}
+ local function sortTbl(x,y)
+ local num1, num2 = tonumber(x), tonumber(y)
+ if num1 and num2 then -- numeric comparison, either two numbers or numeric strings
+ return num1 < num2
+ else -- compare everything else tostring'ed
+ return tostring(x) < tostring(y)
+ end
+ end
+ local function SetList(self, list, order, itemType)
+ self.list = list or {}
+ self.pullout:Clear()
+ self.hasClose = nil
+ if not list then return end
+
+ if type(order) ~= "table" then
+ for v in pairs(list) do
+ sortlist[#sortlist + 1] = v
+ end
+ tsort(sortlist, sortTbl)
+
+ for i, key in ipairs(sortlist) do
+ AddListItem(self, key, list[key], itemType)
+ sortlist[i] = nil
+ end
+ else
+ for i, key in ipairs(order) do
+ AddListItem(self, key, list[key], itemType)
+ end
+ end
+ if self.multiselect then
+ ShowMultiText(self)
+ AddCloseButton(self)
+ end
+ end
+
+ -- exported
+ local function AddItem(self, value, text, itemType)
+ self.list[value] = text
+ AddListItem(self, value, text, itemType)
+ end
+
+ -- exported
+ local function SetMultiselect(self, multi)
+ self.multiselect = multi
+ if multi then
+ ShowMultiText(self)
+ AddCloseButton(self)
+ end
+ end
+
+ -- exported
+ local function GetMultiselect(self)
+ return self.multiselect
+ end
+
+ local function SetPulloutWidth(self, width)
+ self.pulloutWidth = width
+ end
+
+ --[[ Constructor ]]--
+
+ local function Constructor()
+ local count = AceGUI:GetNextWidgetNum(widgetType)
+ local frame = CreateFrame("Frame", nil, UIParent)
+ local dropdown = CreateFrame("Frame", "AceGUI30DropDown"..count, frame, "UIDropDownMenuTemplate")
+
+ local self = {}
+ self.type = widgetType
+ self.frame = frame
+ self.dropdown = dropdown
+ self.count = count
+ frame.obj = self
+ dropdown.obj = self
+
+ self.OnRelease = OnRelease
+ self.OnAcquire = OnAcquire
+
+ self.ClearFocus = ClearFocus
+
+ self.SetText = SetText
+ self.SetValue = SetValue
+ self.GetValue = GetValue
+ self.SetList = SetList
+ self.SetLabel = SetLabel
+ self.SetDisabled = SetDisabled
+ self.AddItem = AddItem
+ self.SetMultiselect = SetMultiselect
+ self.GetMultiselect = GetMultiselect
+ self.SetItemValue = SetItemValue
+ self.SetItemDisabled = SetItemDisabled
+ self.SetPulloutWidth = SetPulloutWidth
+
+ self.alignoffset = 26
+
+ frame:SetScript("OnHide",Dropdown_OnHide)
+
+ dropdown:ClearAllPoints()
+ dropdown:SetPoint("TOPLEFT",frame,"TOPLEFT",-15,0)
+ dropdown:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",17,0)
+ dropdown:SetScript("OnHide", nil)
+
+ local left = _G[dropdown:GetName() .. "Left"]
+ local middle = _G[dropdown:GetName() .. "Middle"]
+ local right = _G[dropdown:GetName() .. "Right"]
+
+ middle:ClearAllPoints()
+ right:ClearAllPoints()
+
+ middle:SetPoint("LEFT", left, "RIGHT", 0, 0)
+ middle:SetPoint("RIGHT", right, "LEFT", 0, 0)
+ right:SetPoint("TOPRIGHT", dropdown, "TOPRIGHT", 0, 17)
+
+ local button = _G[dropdown:GetName() .. "Button"]
+ self.button = button
+ button.obj = self
+ button:SetScript("OnEnter",Control_OnEnter)
+ button:SetScript("OnLeave",Control_OnLeave)
+ button:SetScript("OnClick",Dropdown_TogglePullout)
+
+ local button_cover = CreateFrame("BUTTON",nil,self.frame)
+ self.button_cover = button_cover
+ button_cover.obj = self
+ button_cover:SetPoint("TOPLEFT",self.frame,"BOTTOMLEFT",0,25)
+ button_cover:SetPoint("BOTTOMRIGHT",self.frame,"BOTTOMRIGHT")
+ button_cover:SetScript("OnEnter",Control_OnEnter)
+ button_cover:SetScript("OnLeave",Control_OnLeave)
+ button_cover:SetScript("OnClick",Dropdown_TogglePullout)
+
+ local text = _G[dropdown:GetName() .. "Text"]
+ self.text = text
+ text.obj = self
+ text:ClearAllPoints()
+ text:SetPoint("RIGHT", right, "RIGHT" ,-43, 2)
+ text:SetPoint("LEFT", left, "LEFT", 25, 2)
+
+ local label = frame:CreateFontString(nil,"OVERLAY","GameFontNormalSmall")
+ label:SetPoint("TOPLEFT",frame,"TOPLEFT",0,0)
+ label:SetPoint("TOPRIGHT",frame,"TOPRIGHT",0,0)
+ label:SetJustifyH("LEFT")
+ label:SetHeight(18)
+ label:Hide()
+ self.label = label
+
+ AceGUI:RegisterAsWidget(self)
+ return self
+ end
+
+ AceGUI:RegisterWidgetType(widgetType, Constructor, widgetVersion)
+end
diff --git a/Libs/AceGUI-3.0/widgets/AceGUIWidget-EditBox.lua b/Libs/AceGUI-3.0/widgets/AceGUIWidget-EditBox.lua
new file mode 100644
index 0000000..29f7e00
--- /dev/null
+++ b/Libs/AceGUI-3.0/widgets/AceGUIWidget-EditBox.lua
@@ -0,0 +1,263 @@
+--[[-----------------------------------------------------------------------------
+EditBox Widget
+-------------------------------------------------------------------------------]]
+local Type, Version = "EditBox", 28
+local AceGUI = LibStub and LibStub("AceGUI-3.0", true)
+if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end
+
+-- Lua APIs
+local tostring, pairs = tostring, pairs
+
+-- WoW APIs
+local PlaySound = PlaySound
+local GetCursorInfo, ClearCursor, GetSpellInfo = GetCursorInfo, ClearCursor, GetSpellInfo
+local CreateFrame, UIParent = CreateFrame, UIParent
+local _G = _G
+
+-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
+-- List them here for Mikk's FindGlobals script
+-- GLOBALS: AceGUIEditBoxInsertLink, ChatFontNormal, OKAY
+
+--[[-----------------------------------------------------------------------------
+Support functions
+-------------------------------------------------------------------------------]]
+if not AceGUIEditBoxInsertLink then
+ -- upgradeable hook
+ hooksecurefunc("ChatEdit_InsertLink", function(...) return _G.AceGUIEditBoxInsertLink(...) end)
+end
+
+function _G.AceGUIEditBoxInsertLink(text)
+ for i = 1, AceGUI:GetWidgetCount(Type) do
+ local editbox = _G["AceGUI-3.0EditBox"..i]
+ if editbox and editbox:IsVisible() and editbox:HasFocus() then
+ editbox:Insert(text)
+ return true
+ end
+ end
+end
+
+local function ShowButton(self)
+ if not self.disablebutton then
+ self.button:Show()
+ self.editbox:SetTextInsets(0, 20, 3, 3)
+ end
+end
+
+local function HideButton(self)
+ self.button:Hide()
+ self.editbox:SetTextInsets(0, 0, 3, 3)
+end
+
+--[[-----------------------------------------------------------------------------
+Scripts
+-------------------------------------------------------------------------------]]
+local function Control_OnEnter(frame)
+ frame.obj:Fire("OnEnter")
+end
+
+local function Control_OnLeave(frame)
+ frame.obj:Fire("OnLeave")
+end
+
+local function Frame_OnShowFocus(frame)
+ frame.obj.editbox:SetFocus()
+ frame:SetScript("OnShow", nil)
+end
+
+local function EditBox_OnEscapePressed(frame)
+ AceGUI:ClearFocus()
+end
+
+local function EditBox_OnEnterPressed(frame)
+ local self = frame.obj
+ local value = frame:GetText()
+ local cancel = self:Fire("OnEnterPressed", value)
+ if not cancel then
+ PlaySound(856) -- SOUNDKIT.IG_MAINMENU_OPTION_CHECKBOX_ON
+ HideButton(self)
+ end
+end
+
+local function EditBox_OnReceiveDrag(frame)
+ local self = frame.obj
+ local type, id, info = GetCursorInfo()
+ local name
+ if type == "item" then
+ name = info
+ elseif type == "spell" then
+ name = GetSpellInfo(id, info)
+ elseif type == "macro" then
+ name = GetMacroInfo(id)
+ end
+ if name then
+ self:SetText(name)
+ self:Fire("OnEnterPressed", name)
+ ClearCursor()
+ HideButton(self)
+ AceGUI:ClearFocus()
+ end
+end
+
+local function EditBox_OnTextChanged(frame)
+ local self = frame.obj
+ local value = frame:GetText()
+ if tostring(value) ~= tostring(self.lasttext) then
+ self:Fire("OnTextChanged", value)
+ self.lasttext = value
+ ShowButton(self)
+ end
+end
+
+local function EditBox_OnFocusGained(frame)
+ AceGUI:SetFocus(frame.obj)
+end
+
+local function Button_OnClick(frame)
+ local editbox = frame.obj.editbox
+ editbox:ClearFocus()
+ EditBox_OnEnterPressed(editbox)
+end
+
+--[[-----------------------------------------------------------------------------
+Methods
+-------------------------------------------------------------------------------]]
+local methods = {
+ ["OnAcquire"] = function(self)
+ -- height is controlled by SetLabel
+ self:SetWidth(200)
+ self:SetDisabled(false)
+ self:SetLabel()
+ self:SetText()
+ self:DisableButton(false)
+ self:SetMaxLetters(0)
+ end,
+
+ ["OnRelease"] = function(self)
+ self:ClearFocus()
+ end,
+
+ ["SetDisabled"] = function(self, disabled)
+ self.disabled = disabled
+ if disabled then
+ self.editbox:EnableMouse(false)
+ self.editbox:ClearFocus()
+ self.editbox:SetTextColor(0.5,0.5,0.5)
+ self.label:SetTextColor(0.5,0.5,0.5)
+ else
+ self.editbox:EnableMouse(true)
+ self.editbox:SetTextColor(1,1,1)
+ self.label:SetTextColor(1,.82,0)
+ end
+ end,
+
+ ["SetText"] = function(self, text)
+ self.lasttext = text or ""
+ self.editbox:SetText(text or "")
+ self.editbox:SetCursorPosition(0)
+ HideButton(self)
+ end,
+
+ ["GetText"] = function(self, text)
+ return self.editbox:GetText()
+ end,
+
+ ["SetLabel"] = function(self, text)
+ if text and text ~= "" then
+ self.label:SetText(text)
+ self.label:Show()
+ self.editbox:SetPoint("TOPLEFT",self.frame,"TOPLEFT",7,-18)
+ self:SetHeight(44)
+ self.alignoffset = 30
+ else
+ self.label:SetText("")
+ self.label:Hide()
+ self.editbox:SetPoint("TOPLEFT",self.frame,"TOPLEFT",7,0)
+ self:SetHeight(26)
+ self.alignoffset = 12
+ end
+ end,
+
+ ["DisableButton"] = function(self, disabled)
+ self.disablebutton = disabled
+ if disabled then
+ HideButton(self)
+ end
+ end,
+
+ ["SetMaxLetters"] = function (self, num)
+ self.editbox:SetMaxLetters(num or 0)
+ end,
+
+ ["ClearFocus"] = function(self)
+ self.editbox:ClearFocus()
+ self.frame:SetScript("OnShow", nil)
+ end,
+
+ ["SetFocus"] = function(self)
+ self.editbox:SetFocus()
+ if not self.frame:IsShown() then
+ self.frame:SetScript("OnShow", Frame_OnShowFocus)
+ end
+ end,
+
+ ["HighlightText"] = function(self, from, to)
+ self.editbox:HighlightText(from, to)
+ end
+}
+
+--[[-----------------------------------------------------------------------------
+Constructor
+-------------------------------------------------------------------------------]]
+local function Constructor()
+ local num = AceGUI:GetNextWidgetNum(Type)
+ local frame = CreateFrame("Frame", nil, UIParent)
+ frame:Hide()
+
+ local editbox = CreateFrame("EditBox", "AceGUI-3.0EditBox"..num, frame, "InputBoxTemplate")
+ editbox:SetAutoFocus(false)
+ editbox:SetFontObject(ChatFontNormal)
+ editbox:SetScript("OnEnter", Control_OnEnter)
+ editbox:SetScript("OnLeave", Control_OnLeave)
+ editbox:SetScript("OnEscapePressed", EditBox_OnEscapePressed)
+ editbox:SetScript("OnEnterPressed", EditBox_OnEnterPressed)
+ editbox:SetScript("OnTextChanged", EditBox_OnTextChanged)
+ editbox:SetScript("OnReceiveDrag", EditBox_OnReceiveDrag)
+ editbox:SetScript("OnMouseDown", EditBox_OnReceiveDrag)
+ editbox:SetScript("OnEditFocusGained", EditBox_OnFocusGained)
+ editbox:SetTextInsets(0, 0, 3, 3)
+ editbox:SetMaxLetters(256)
+ editbox:SetPoint("BOTTOMLEFT", 6, 0)
+ editbox:SetPoint("BOTTOMRIGHT")
+ editbox:SetHeight(19)
+
+ local label = frame:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
+ label:SetPoint("TOPLEFT", 0, -2)
+ label:SetPoint("TOPRIGHT", 0, -2)
+ label:SetJustifyH("LEFT")
+ label:SetHeight(18)
+
+ local button = CreateFrame("Button", nil, editbox, "UIPanelButtonTemplate")
+ button:SetWidth(40)
+ button:SetHeight(20)
+ button:SetPoint("RIGHT", -2, 0)
+ button:SetText(OKAY)
+ button:SetScript("OnClick", Button_OnClick)
+ button:Hide()
+
+ local widget = {
+ alignoffset = 30,
+ editbox = editbox,
+ label = label,
+ button = button,
+ frame = frame,
+ type = Type
+ }
+ for method, func in pairs(methods) do
+ widget[method] = func
+ end
+ editbox.obj, button.obj = widget, widget
+
+ return AceGUI:RegisterAsWidget(widget)
+end
+
+AceGUI:RegisterWidgetType(Type, Constructor, Version)
diff --git a/Libs/AceGUI-3.0/widgets/AceGUIWidget-Heading.lua b/Libs/AceGUI-3.0/widgets/AceGUIWidget-Heading.lua
new file mode 100644
index 0000000..862ae88
--- /dev/null
+++ b/Libs/AceGUI-3.0/widgets/AceGUIWidget-Heading.lua
@@ -0,0 +1,78 @@
+--[[-----------------------------------------------------------------------------
+Heading Widget
+-------------------------------------------------------------------------------]]
+local Type, Version = "Heading", 20
+local AceGUI = LibStub and LibStub("AceGUI-3.0", true)
+if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end
+
+-- Lua APIs
+local pairs = pairs
+
+-- WoW APIs
+local CreateFrame, UIParent = CreateFrame, UIParent
+
+--[[-----------------------------------------------------------------------------
+Methods
+-------------------------------------------------------------------------------]]
+local methods = {
+ ["OnAcquire"] = function(self)
+ self:SetText()
+ self:SetFullWidth()
+ self:SetHeight(18)
+ end,
+
+ -- ["OnRelease"] = nil,
+
+ ["SetText"] = function(self, text)
+ self.label:SetText(text or "")
+ if text and text ~= "" then
+ self.left:SetPoint("RIGHT", self.label, "LEFT", -5, 0)
+ self.right:Show()
+ else
+ self.left:SetPoint("RIGHT", -3, 0)
+ self.right:Hide()
+ end
+ end
+}
+
+--[[-----------------------------------------------------------------------------
+Constructor
+-------------------------------------------------------------------------------]]
+local function Constructor()
+ local frame = CreateFrame("Frame", nil, UIParent)
+ frame:Hide()
+
+ local label = frame:CreateFontString(nil, "BACKGROUND", "GameFontNormal")
+ label:SetPoint("TOP")
+ label:SetPoint("BOTTOM")
+ label:SetJustifyH("CENTER")
+
+ local left = frame:CreateTexture(nil, "BACKGROUND")
+ left:SetHeight(8)
+ left:SetPoint("LEFT", 3, 0)
+ left:SetPoint("RIGHT", label, "LEFT", -5, 0)
+ left:SetTexture(137057) -- Interface\\Tooltips\\UI-Tooltip-Border
+ left:SetTexCoord(0.81, 0.94, 0.5, 1)
+
+ local right = frame:CreateTexture(nil, "BACKGROUND")
+ right:SetHeight(8)
+ right:SetPoint("RIGHT", -3, 0)
+ right:SetPoint("LEFT", label, "RIGHT", 5, 0)
+ right:SetTexture(137057) -- Interface\\Tooltips\\UI-Tooltip-Border
+ right:SetTexCoord(0.81, 0.94, 0.5, 1)
+
+ local widget = {
+ label = label,
+ left = left,
+ right = right,
+ frame = frame,
+ type = Type
+ }
+ for method, func in pairs(methods) do
+ widget[method] = func
+ end
+
+ return AceGUI:RegisterAsWidget(widget)
+end
+
+AceGUI:RegisterWidgetType(Type, Constructor, Version)
diff --git a/Libs/AceGUI-3.0/widgets/AceGUIWidget-Icon.lua b/Libs/AceGUI-3.0/widgets/AceGUIWidget-Icon.lua
new file mode 100644
index 0000000..378e813
--- /dev/null
+++ b/Libs/AceGUI-3.0/widgets/AceGUIWidget-Icon.lua
@@ -0,0 +1,140 @@
+--[[-----------------------------------------------------------------------------
+Icon Widget
+-------------------------------------------------------------------------------]]
+local Type, Version = "Icon", 21
+local AceGUI = LibStub and LibStub("AceGUI-3.0", true)
+if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end
+
+-- Lua APIs
+local select, pairs, print = select, pairs, print
+
+-- WoW APIs
+local CreateFrame, UIParent = CreateFrame, UIParent
+
+--[[-----------------------------------------------------------------------------
+Scripts
+-------------------------------------------------------------------------------]]
+local function Control_OnEnter(frame)
+ frame.obj:Fire("OnEnter")
+end
+
+local function Control_OnLeave(frame)
+ frame.obj:Fire("OnLeave")
+end
+
+local function Button_OnClick(frame, button)
+ frame.obj:Fire("OnClick", button)
+ AceGUI:ClearFocus()
+end
+
+--[[-----------------------------------------------------------------------------
+Methods
+-------------------------------------------------------------------------------]]
+local methods = {
+ ["OnAcquire"] = function(self)
+ self:SetHeight(110)
+ self:SetWidth(110)
+ self:SetLabel()
+ self:SetImage(nil)
+ self:SetImageSize(64, 64)
+ self:SetDisabled(false)
+ end,
+
+ -- ["OnRelease"] = nil,
+
+ ["SetLabel"] = function(self, text)
+ if text and text ~= "" then
+ self.label:Show()
+ self.label:SetText(text)
+ self:SetHeight(self.image:GetHeight() + 25)
+ else
+ self.label:Hide()
+ self:SetHeight(self.image:GetHeight() + 10)
+ end
+ end,
+
+ ["SetImage"] = function(self, path, ...)
+ local image = self.image
+ image:SetTexture(path)
+
+ if image:GetTexture() then
+ local n = select("#", ...)
+ if n == 4 or n == 8 then
+ image:SetTexCoord(...)
+ else
+ image:SetTexCoord(0, 1, 0, 1)
+ end
+ end
+ end,
+
+ ["SetImageSize"] = function(self, width, height)
+ self.image:SetWidth(width)
+ self.image:SetHeight(height)
+ --self.frame:SetWidth(width + 30)
+ if self.label:IsShown() then
+ self:SetHeight(height + 25)
+ else
+ self:SetHeight(height + 10)
+ end
+ end,
+
+ ["SetDisabled"] = function(self, disabled)
+ self.disabled = disabled
+ if disabled then
+ self.frame:Disable()
+ self.label:SetTextColor(0.5, 0.5, 0.5)
+ self.image:SetVertexColor(0.5, 0.5, 0.5, 0.5)
+ else
+ self.frame:Enable()
+ self.label:SetTextColor(1, 1, 1)
+ self.image:SetVertexColor(1, 1, 1, 1)
+ end
+ end
+}
+
+--[[-----------------------------------------------------------------------------
+Constructor
+-------------------------------------------------------------------------------]]
+local function Constructor()
+ local frame = CreateFrame("Button", nil, UIParent)
+ frame:Hide()
+
+ frame:EnableMouse(true)
+ frame:SetScript("OnEnter", Control_OnEnter)
+ frame:SetScript("OnLeave", Control_OnLeave)
+ frame:SetScript("OnClick", Button_OnClick)
+
+ local label = frame:CreateFontString(nil, "BACKGROUND", "GameFontHighlight")
+ label:SetPoint("BOTTOMLEFT")
+ label:SetPoint("BOTTOMRIGHT")
+ label:SetJustifyH("CENTER")
+ label:SetJustifyV("TOP")
+ label:SetHeight(18)
+
+ local image = frame:CreateTexture(nil, "BACKGROUND")
+ image:SetWidth(64)
+ image:SetHeight(64)
+ image:SetPoint("TOP", 0, -5)
+
+ local highlight = frame:CreateTexture(nil, "HIGHLIGHT")
+ highlight:SetAllPoints(image)
+ highlight:SetTexture(136580) -- Interface\\PaperDollInfoFrame\\UI-Character-Tab-Highlight
+ highlight:SetTexCoord(0, 1, 0.23, 0.77)
+ highlight:SetBlendMode("ADD")
+
+ local widget = {
+ label = label,
+ image = image,
+ frame = frame,
+ type = Type
+ }
+ for method, func in pairs(methods) do
+ widget[method] = func
+ end
+
+ widget.SetText = function(self, ...) print("AceGUI-3.0-Icon: SetText is deprecated! Use SetLabel instead!"); self:SetLabel(...) end
+
+ return AceGUI:RegisterAsWidget(widget)
+end
+
+AceGUI:RegisterWidgetType(Type, Constructor, Version)
diff --git a/Libs/AceGUI-3.0/widgets/AceGUIWidget-InteractiveLabel.lua b/Libs/AceGUI-3.0/widgets/AceGUIWidget-InteractiveLabel.lua
new file mode 100644
index 0000000..255dd97
--- /dev/null
+++ b/Libs/AceGUI-3.0/widgets/AceGUIWidget-InteractiveLabel.lua
@@ -0,0 +1,94 @@
+--[[-----------------------------------------------------------------------------
+InteractiveLabel Widget
+-------------------------------------------------------------------------------]]
+local Type, Version = "InteractiveLabel", 21
+local AceGUI = LibStub and LibStub("AceGUI-3.0", true)
+if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end
+
+-- Lua APIs
+local select, pairs = select, pairs
+
+--[[-----------------------------------------------------------------------------
+Scripts
+-------------------------------------------------------------------------------]]
+local function Control_OnEnter(frame)
+ frame.obj:Fire("OnEnter")
+end
+
+local function Control_OnLeave(frame)
+ frame.obj:Fire("OnLeave")
+end
+
+local function Label_OnClick(frame, button)
+ frame.obj:Fire("OnClick", button)
+ AceGUI:ClearFocus()
+end
+
+--[[-----------------------------------------------------------------------------
+Methods
+-------------------------------------------------------------------------------]]
+local methods = {
+ ["OnAcquire"] = function(self)
+ self:LabelOnAcquire()
+ self:SetHighlight()
+ self:SetHighlightTexCoord()
+ self:SetDisabled(false)
+ end,
+
+ -- ["OnRelease"] = nil,
+
+ ["SetHighlight"] = function(self, ...)
+ self.highlight:SetTexture(...)
+ end,
+
+ ["SetHighlightTexCoord"] = function(self, ...)
+ local c = select("#", ...)
+ if c == 4 or c == 8 then
+ self.highlight:SetTexCoord(...)
+ else
+ self.highlight:SetTexCoord(0, 1, 0, 1)
+ end
+ end,
+
+ ["SetDisabled"] = function(self,disabled)
+ self.disabled = disabled
+ if disabled then
+ self.frame:EnableMouse(false)
+ self.label:SetTextColor(0.5, 0.5, 0.5)
+ else
+ self.frame:EnableMouse(true)
+ self.label:SetTextColor(1, 1, 1)
+ end
+ end
+}
+
+--[[-----------------------------------------------------------------------------
+Constructor
+-------------------------------------------------------------------------------]]
+local function Constructor()
+ -- create a Label type that we will hijack
+ local label = AceGUI:Create("Label")
+
+ local frame = label.frame
+ frame:EnableMouse(true)
+ frame:SetScript("OnEnter", Control_OnEnter)
+ frame:SetScript("OnLeave", Control_OnLeave)
+ frame:SetScript("OnMouseDown", Label_OnClick)
+
+ local highlight = frame:CreateTexture(nil, "HIGHLIGHT")
+ highlight:SetTexture(nil)
+ highlight:SetAllPoints()
+ highlight:SetBlendMode("ADD")
+
+ label.highlight = highlight
+ label.type = Type
+ label.LabelOnAcquire = label.OnAcquire
+ for method, func in pairs(methods) do
+ label[method] = func
+ end
+
+ return label
+end
+
+AceGUI:RegisterWidgetType(Type, Constructor, Version)
+
diff --git a/Libs/AceGUI-3.0/widgets/AceGUIWidget-Keybinding.lua b/Libs/AceGUI-3.0/widgets/AceGUIWidget-Keybinding.lua
new file mode 100644
index 0000000..ec4cead
--- /dev/null
+++ b/Libs/AceGUI-3.0/widgets/AceGUIWidget-Keybinding.lua
@@ -0,0 +1,249 @@
+--[[-----------------------------------------------------------------------------
+Keybinding Widget
+Set Keybindings in the Config UI.
+-------------------------------------------------------------------------------]]
+local Type, Version = "Keybinding", 25
+local AceGUI = LibStub and LibStub("AceGUI-3.0", true)
+if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end
+
+-- Lua APIs
+local pairs = pairs
+
+-- WoW APIs
+local IsShiftKeyDown, IsControlKeyDown, IsAltKeyDown = IsShiftKeyDown, IsControlKeyDown, IsAltKeyDown
+local CreateFrame, UIParent = CreateFrame, UIParent
+
+-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
+-- List them here for Mikk's FindGlobals script
+-- GLOBALS: NOT_BOUND
+
+--[[-----------------------------------------------------------------------------
+Scripts
+-------------------------------------------------------------------------------]]
+
+local function Control_OnEnter(frame)
+ frame.obj:Fire("OnEnter")
+end
+
+local function Control_OnLeave(frame)
+ frame.obj:Fire("OnLeave")
+end
+
+local function Keybinding_OnClick(frame, button)
+ if button == "LeftButton" or button == "RightButton" then
+ local self = frame.obj
+ if self.waitingForKey then
+ frame:EnableKeyboard(false)
+ frame:EnableMouseWheel(false)
+ self.msgframe:Hide()
+ frame:UnlockHighlight()
+ self.waitingForKey = nil
+ else
+ frame:EnableKeyboard(true)
+ frame:EnableMouseWheel(true)
+ self.msgframe:Show()
+ frame:LockHighlight()
+ self.waitingForKey = true
+ end
+ end
+ AceGUI:ClearFocus()
+end
+
+local ignoreKeys = {
+ ["BUTTON1"] = true, ["BUTTON2"] = true,
+ ["UNKNOWN"] = true,
+ ["LSHIFT"] = true, ["LCTRL"] = true, ["LALT"] = true,
+ ["RSHIFT"] = true, ["RCTRL"] = true, ["RALT"] = true,
+}
+local function Keybinding_OnKeyDown(frame, key)
+ local self = frame.obj
+ if self.waitingForKey then
+ local keyPressed = key
+ if keyPressed == "ESCAPE" then
+ keyPressed = ""
+ else
+ if ignoreKeys[keyPressed] then return end
+ if IsShiftKeyDown() then
+ keyPressed = "SHIFT-"..keyPressed
+ end
+ if IsControlKeyDown() then
+ keyPressed = "CTRL-"..keyPressed
+ end
+ if IsAltKeyDown() then
+ keyPressed = "ALT-"..keyPressed
+ end
+ end
+
+ frame:EnableKeyboard(false)
+ frame:EnableMouseWheel(false)
+ self.msgframe:Hide()
+ frame:UnlockHighlight()
+ self.waitingForKey = nil
+
+ if not self.disabled then
+ self:SetKey(keyPressed)
+ self:Fire("OnKeyChanged", keyPressed)
+ end
+ end
+end
+
+local function Keybinding_OnMouseDown(frame, button)
+ if button == "LeftButton" or button == "RightButton" then
+ return
+ elseif button == "MiddleButton" then
+ button = "BUTTON3"
+ elseif button == "Button4" then
+ button = "BUTTON4"
+ elseif button == "Button5" then
+ button = "BUTTON5"
+ end
+ Keybinding_OnKeyDown(frame, button)
+end
+
+local function Keybinding_OnMouseWheel(frame, direction)
+ local button
+ if direction >= 0 then
+ button = "MOUSEWHEELUP"
+ else
+ button = "MOUSEWHEELDOWN"
+ end
+ Keybinding_OnKeyDown(frame, button)
+end
+
+--[[-----------------------------------------------------------------------------
+Methods
+-------------------------------------------------------------------------------]]
+local methods = {
+ ["OnAcquire"] = function(self)
+ self:SetWidth(200)
+ self:SetLabel("")
+ self:SetKey("")
+ self.waitingForKey = nil
+ self.msgframe:Hide()
+ self:SetDisabled(false)
+ self.button:EnableKeyboard(false)
+ self.button:EnableMouseWheel(false)
+ end,
+
+ -- ["OnRelease"] = nil,
+
+ ["SetDisabled"] = function(self, disabled)
+ self.disabled = disabled
+ if disabled then
+ self.button:Disable()
+ self.label:SetTextColor(0.5,0.5,0.5)
+ else
+ self.button:Enable()
+ self.label:SetTextColor(1,1,1)
+ end
+ end,
+
+ ["SetKey"] = function(self, key)
+ if (key or "") == "" then
+ self.button:SetText(NOT_BOUND)
+ self.button:SetNormalFontObject("GameFontNormal")
+ else
+ self.button:SetText(key)
+ self.button:SetNormalFontObject("GameFontHighlight")
+ end
+ end,
+
+ ["GetKey"] = function(self)
+ local key = self.button:GetText()
+ if key == NOT_BOUND then
+ key = nil
+ end
+ return key
+ end,
+
+ ["SetLabel"] = function(self, label)
+ self.label:SetText(label or "")
+ if (label or "") == "" then
+ self.alignoffset = nil
+ self:SetHeight(24)
+ else
+ self.alignoffset = 30
+ self:SetHeight(44)
+ end
+ end,
+}
+
+--[[-----------------------------------------------------------------------------
+Constructor
+-------------------------------------------------------------------------------]]
+
+local ControlBackdrop = {
+ bgFile = "Interface\\Tooltips\\UI-Tooltip-Background",
+ edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
+ tile = true, tileSize = 16, edgeSize = 16,
+ insets = { left = 3, right = 3, top = 3, bottom = 3 }
+}
+
+local function keybindingMsgFixWidth(frame)
+ frame:SetWidth(frame.msg:GetWidth() + 10)
+ frame:SetScript("OnUpdate", nil)
+end
+
+local function Constructor()
+ local name = "AceGUI30KeybindingButton" .. AceGUI:GetNextWidgetNum(Type)
+
+ local frame = CreateFrame("Frame", nil, UIParent)
+ local button = CreateFrame("Button", name, frame, "UIPanelButtonTemplate")
+
+ button:EnableMouse(true)
+ button:EnableMouseWheel(false)
+ button:RegisterForClicks("AnyDown")
+ button:SetScript("OnEnter", Control_OnEnter)
+ button:SetScript("OnLeave", Control_OnLeave)
+ button:SetScript("OnClick", Keybinding_OnClick)
+ button:SetScript("OnKeyDown", Keybinding_OnKeyDown)
+ button:SetScript("OnMouseDown", Keybinding_OnMouseDown)
+ button:SetScript("OnMouseWheel", Keybinding_OnMouseWheel)
+ button:SetPoint("BOTTOMLEFT")
+ button:SetPoint("BOTTOMRIGHT")
+ button:SetHeight(24)
+ button:EnableKeyboard(false)
+
+ local text = button:GetFontString()
+ text:SetPoint("LEFT", 7, 0)
+ text:SetPoint("RIGHT", -7, 0)
+
+ local label = frame:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
+ label:SetPoint("TOPLEFT")
+ label:SetPoint("TOPRIGHT")
+ label:SetJustifyH("CENTER")
+ label:SetHeight(18)
+
+ local msgframe = CreateFrame("Frame", nil, UIParent)
+ msgframe:SetHeight(30)
+ msgframe:SetBackdrop(ControlBackdrop)
+ msgframe:SetBackdropColor(0,0,0)
+ msgframe:SetFrameStrata("FULLSCREEN_DIALOG")
+ msgframe:SetFrameLevel(1000)
+ msgframe:SetToplevel(true)
+
+ local msg = msgframe:CreateFontString(nil, "OVERLAY", "GameFontNormal")
+ msg:SetText("Press a key to bind, ESC to clear the binding or click the button again to cancel.")
+ msgframe.msg = msg
+ msg:SetPoint("TOPLEFT", 5, -5)
+ msgframe:SetScript("OnUpdate", keybindingMsgFixWidth)
+ msgframe:SetPoint("BOTTOM", button, "TOP")
+ msgframe:Hide()
+
+ local widget = {
+ button = button,
+ label = label,
+ msgframe = msgframe,
+ frame = frame,
+ alignoffset = 30,
+ type = Type
+ }
+ for method, func in pairs(methods) do
+ widget[method] = func
+ end
+ button.obj = widget
+
+ return AceGUI:RegisterAsWidget(widget)
+end
+
+AceGUI:RegisterWidgetType(Type, Constructor, Version)
diff --git a/Libs/AceGUI-3.0/widgets/AceGUIWidget-Label.lua b/Libs/AceGUI-3.0/widgets/AceGUIWidget-Label.lua
new file mode 100644
index 0000000..5c75f3b
--- /dev/null
+++ b/Libs/AceGUI-3.0/widgets/AceGUIWidget-Label.lua
@@ -0,0 +1,179 @@
+--[[-----------------------------------------------------------------------------
+Label Widget
+Displays text and optionally an icon.
+-------------------------------------------------------------------------------]]
+local Type, Version = "Label", 27
+local AceGUI = LibStub and LibStub("AceGUI-3.0", true)
+if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end
+
+-- Lua APIs
+local max, select, pairs = math.max, select, pairs
+
+-- WoW APIs
+local CreateFrame, UIParent = CreateFrame, UIParent
+
+-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
+-- List them here for Mikk's FindGlobals script
+-- GLOBALS: GameFontHighlightSmall
+
+--[[-----------------------------------------------------------------------------
+Support functions
+-------------------------------------------------------------------------------]]
+
+local function UpdateImageAnchor(self)
+ if self.resizing then return end
+ local frame = self.frame
+ local width = frame.width or frame:GetWidth() or 0
+ local image = self.image
+ local label = self.label
+ local height
+
+ label:ClearAllPoints()
+ image:ClearAllPoints()
+
+ if self.imageshown then
+ local imagewidth = image:GetWidth()
+ if (width - imagewidth) < 200 or (label:GetText() or "") == "" then
+ -- image goes on top centered when less than 200 width for the text, or if there is no text
+ image:SetPoint("TOP")
+ label:SetPoint("TOP", image, "BOTTOM")
+ label:SetPoint("LEFT")
+ label:SetWidth(width)
+ height = image:GetHeight() + label:GetStringHeight()
+ else
+ -- image on the left
+ image:SetPoint("TOPLEFT")
+ if image:GetHeight() > label:GetStringHeight() then
+ label:SetPoint("LEFT", image, "RIGHT", 4, 0)
+ else
+ label:SetPoint("TOPLEFT", image, "TOPRIGHT", 4, 0)
+ end
+ label:SetWidth(width - imagewidth - 4)
+ height = max(image:GetHeight(), label:GetStringHeight())
+ end
+ else
+ -- no image shown
+ label:SetPoint("TOPLEFT")
+ label:SetWidth(width)
+ height = label:GetStringHeight()
+ end
+
+ -- avoid zero-height labels, since they can used as spacers
+ if not height or height == 0 then
+ height = 1
+ end
+
+ self.resizing = true
+ frame:SetHeight(height)
+ frame.height = height
+ self.resizing = nil
+end
+
+--[[-----------------------------------------------------------------------------
+Methods
+-------------------------------------------------------------------------------]]
+local methods = {
+ ["OnAcquire"] = function(self)
+ -- set the flag to stop constant size updates
+ self.resizing = true
+ -- height is set dynamically by the text and image size
+ self:SetWidth(200)
+ self:SetText()
+ self:SetImage(nil)
+ self:SetImageSize(16, 16)
+ self:SetColor()
+ self:SetFontObject()
+ self:SetJustifyH("LEFT")
+ self:SetJustifyV("TOP")
+
+ -- reset the flag
+ self.resizing = nil
+ -- run the update explicitly
+ UpdateImageAnchor(self)
+ end,
+
+ -- ["OnRelease"] = nil,
+
+ ["OnWidthSet"] = function(self, width)
+ UpdateImageAnchor(self)
+ end,
+
+ ["SetText"] = function(self, text)
+ self.label:SetText(text)
+ UpdateImageAnchor(self)
+ end,
+
+ ["SetColor"] = function(self, r, g, b)
+ if not (r and g and b) then
+ r, g, b = 1, 1, 1
+ end
+ self.label:SetVertexColor(r, g, b)
+ end,
+
+ ["SetImage"] = function(self, path, ...)
+ local image = self.image
+ image:SetTexture(path)
+
+ if image:GetTexture() then
+ self.imageshown = true
+ local n = select("#", ...)
+ if n == 4 or n == 8 then
+ image:SetTexCoord(...)
+ else
+ image:SetTexCoord(0, 1, 0, 1)
+ end
+ else
+ self.imageshown = nil
+ end
+ UpdateImageAnchor(self)
+ end,
+
+ ["SetFont"] = function(self, font, height, flags)
+ self.label:SetFont(font, height, flags)
+ UpdateImageAnchor(self)
+ end,
+
+ ["SetFontObject"] = function(self, font)
+ self:SetFont((font or GameFontHighlightSmall):GetFont())
+ end,
+
+ ["SetImageSize"] = function(self, width, height)
+ self.image:SetWidth(width)
+ self.image:SetHeight(height)
+ UpdateImageAnchor(self)
+ end,
+
+ ["SetJustifyH"] = function(self, justifyH)
+ self.label:SetJustifyH(justifyH)
+ end,
+
+ ["SetJustifyV"] = function(self, justifyV)
+ self.label:SetJustifyV(justifyV)
+ end,
+}
+
+--[[-----------------------------------------------------------------------------
+Constructor
+-------------------------------------------------------------------------------]]
+local function Constructor()
+ local frame = CreateFrame("Frame", nil, UIParent)
+ frame:Hide()
+
+ local label = frame:CreateFontString(nil, "BACKGROUND", "GameFontHighlightSmall")
+ local image = frame:CreateTexture(nil, "BACKGROUND")
+
+ -- create widget
+ local widget = {
+ label = label,
+ image = image,
+ frame = frame,
+ type = Type
+ }
+ for method, func in pairs(methods) do
+ widget[method] = func
+ end
+
+ return AceGUI:RegisterAsWidget(widget)
+end
+
+AceGUI:RegisterWidgetType(Type, Constructor, Version)
diff --git a/Libs/AceGUI-3.0/widgets/AceGUIWidget-MultiLineEditBox.lua b/Libs/AceGUI-3.0/widgets/AceGUIWidget-MultiLineEditBox.lua
new file mode 100644
index 0000000..f702110
--- /dev/null
+++ b/Libs/AceGUI-3.0/widgets/AceGUIWidget-MultiLineEditBox.lua
@@ -0,0 +1,366 @@
+local Type, Version = "MultiLineEditBox", 28
+local AceGUI = LibStub and LibStub("AceGUI-3.0", true)
+if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end
+
+-- Lua APIs
+local pairs = pairs
+
+-- WoW APIs
+local GetCursorInfo, GetSpellInfo, ClearCursor = GetCursorInfo, GetSpellInfo, ClearCursor
+local CreateFrame, UIParent = CreateFrame, UIParent
+local _G = _G
+
+-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
+-- List them here for Mikk's FindGlobals script
+-- GLOBALS: ACCEPT, ChatFontNormal
+
+--[[-----------------------------------------------------------------------------
+Support functions
+-------------------------------------------------------------------------------]]
+
+if not AceGUIMultiLineEditBoxInsertLink then
+ -- upgradeable hook
+ hooksecurefunc("ChatEdit_InsertLink", function(...) return _G.AceGUIMultiLineEditBoxInsertLink(...) end)
+end
+
+function _G.AceGUIMultiLineEditBoxInsertLink(text)
+ for i = 1, AceGUI:GetWidgetCount(Type) do
+ local editbox = _G[("MultiLineEditBox%uEdit"):format(i)]
+ if editbox and editbox:IsVisible() and editbox:HasFocus() then
+ editbox:Insert(text)
+ return true
+ end
+ end
+end
+
+
+local function Layout(self)
+ self:SetHeight(self.numlines * 14 + (self.disablebutton and 19 or 41) + self.labelHeight)
+
+ if self.labelHeight == 0 then
+ self.scrollBar:SetPoint("TOP", self.frame, "TOP", 0, -23)
+ else
+ self.scrollBar:SetPoint("TOP", self.label, "BOTTOM", 0, -19)
+ end
+
+ if self.disablebutton then
+ self.scrollBar:SetPoint("BOTTOM", self.frame, "BOTTOM", 0, 21)
+ self.scrollBG:SetPoint("BOTTOMLEFT", 0, 4)
+ else
+ self.scrollBar:SetPoint("BOTTOM", self.button, "TOP", 0, 18)
+ self.scrollBG:SetPoint("BOTTOMLEFT", self.button, "TOPLEFT")
+ end
+end
+
+--[[-----------------------------------------------------------------------------
+Scripts
+-------------------------------------------------------------------------------]]
+local function OnClick(self) -- Button
+ self = self.obj
+ self.editBox:ClearFocus()
+ if not self:Fire("OnEnterPressed", self.editBox:GetText()) then
+ self.button:Disable()
+ end
+end
+
+local function OnCursorChanged(self, _, y, _, cursorHeight) -- EditBox
+ self, y = self.obj.scrollFrame, -y
+ local offset = self:GetVerticalScroll()
+ if y < offset then
+ self:SetVerticalScroll(y)
+ else
+ y = y + cursorHeight - self:GetHeight()
+ if y > offset then
+ self:SetVerticalScroll(y)
+ end
+ end
+end
+
+local function OnEditFocusLost(self) -- EditBox
+ self:HighlightText(0, 0)
+ self.obj:Fire("OnEditFocusLost")
+end
+
+local function OnEnter(self) -- EditBox / ScrollFrame
+ self = self.obj
+ if not self.entered then
+ self.entered = true
+ self:Fire("OnEnter")
+ end
+end
+
+local function OnLeave(self) -- EditBox / ScrollFrame
+ self = self.obj
+ if self.entered then
+ self.entered = nil
+ self:Fire("OnLeave")
+ end
+end
+
+local function OnMouseUp(self) -- ScrollFrame
+ self = self.obj.editBox
+ self:SetFocus()
+ self:SetCursorPosition(self:GetNumLetters())
+end
+
+local function OnReceiveDrag(self) -- EditBox / ScrollFrame
+ local type, id, info = GetCursorInfo()
+ if type == "spell" then
+ info = GetSpellInfo(id, info)
+ elseif type ~= "item" then
+ return
+ end
+ ClearCursor()
+ self = self.obj
+ local editBox = self.editBox
+ if not editBox:HasFocus() then
+ editBox:SetFocus()
+ editBox:SetCursorPosition(editBox:GetNumLetters())
+ end
+ editBox:Insert(info)
+ self.button:Enable()
+end
+
+local function OnSizeChanged(self, width, height) -- ScrollFrame
+ self.obj.editBox:SetWidth(width)
+end
+
+local function OnTextChanged(self, userInput) -- EditBox
+ if userInput then
+ self = self.obj
+ self:Fire("OnTextChanged", self.editBox:GetText())
+ self.button:Enable()
+ end
+end
+
+local function OnTextSet(self) -- EditBox
+ self:HighlightText(0, 0)
+ self:SetCursorPosition(self:GetNumLetters())
+ self:SetCursorPosition(0)
+ self.obj.button:Disable()
+end
+
+local function OnVerticalScroll(self, offset) -- ScrollFrame
+ local editBox = self.obj.editBox
+ editBox:SetHitRectInsets(0, 0, offset, editBox:GetHeight() - offset - self:GetHeight())
+end
+
+local function OnShowFocus(frame)
+ frame.obj.editBox:SetFocus()
+ frame:SetScript("OnShow", nil)
+end
+
+local function OnEditFocusGained(frame)
+ AceGUI:SetFocus(frame.obj)
+ frame.obj:Fire("OnEditFocusGained")
+end
+
+--[[-----------------------------------------------------------------------------
+Methods
+-------------------------------------------------------------------------------]]
+local methods = {
+ ["OnAcquire"] = function(self)
+ self.editBox:SetText("")
+ self:SetDisabled(false)
+ self:SetWidth(200)
+ self:DisableButton(false)
+ self:SetNumLines()
+ self.entered = nil
+ self:SetMaxLetters(0)
+ end,
+
+ ["OnRelease"] = function(self)
+ self:ClearFocus()
+ end,
+
+ ["SetDisabled"] = function(self, disabled)
+ local editBox = self.editBox
+ if disabled then
+ editBox:ClearFocus()
+ editBox:EnableMouse(false)
+ editBox:SetTextColor(0.5, 0.5, 0.5)
+ self.label:SetTextColor(0.5, 0.5, 0.5)
+ self.scrollFrame:EnableMouse(false)
+ self.button:Disable()
+ else
+ editBox:EnableMouse(true)
+ editBox:SetTextColor(1, 1, 1)
+ self.label:SetTextColor(1, 0.82, 0)
+ self.scrollFrame:EnableMouse(true)
+ end
+ end,
+
+ ["SetLabel"] = function(self, text)
+ if text and text ~= "" then
+ self.label:SetText(text)
+ if self.labelHeight ~= 10 then
+ self.labelHeight = 10
+ self.label:Show()
+ end
+ elseif self.labelHeight ~= 0 then
+ self.labelHeight = 0
+ self.label:Hide()
+ end
+ Layout(self)
+ end,
+
+ ["SetNumLines"] = function(self, value)
+ if not value or value < 4 then
+ value = 4
+ end
+ self.numlines = value
+ Layout(self)
+ end,
+
+ ["SetText"] = function(self, text)
+ self.editBox:SetText(text)
+ end,
+
+ ["GetText"] = function(self)
+ return self.editBox:GetText()
+ end,
+
+ ["SetMaxLetters"] = function (self, num)
+ self.editBox:SetMaxLetters(num or 0)
+ end,
+
+ ["DisableButton"] = function(self, disabled)
+ self.disablebutton = disabled
+ if disabled then
+ self.button:Hide()
+ else
+ self.button:Show()
+ end
+ Layout(self)
+ end,
+
+ ["ClearFocus"] = function(self)
+ self.editBox:ClearFocus()
+ self.frame:SetScript("OnShow", nil)
+ end,
+
+ ["SetFocus"] = function(self)
+ self.editBox:SetFocus()
+ if not self.frame:IsShown() then
+ self.frame:SetScript("OnShow", OnShowFocus)
+ end
+ end,
+
+ ["HighlightText"] = function(self, from, to)
+ self.editBox:HighlightText(from, to)
+ end,
+
+ ["GetCursorPosition"] = function(self)
+ return self.editBox:GetCursorPosition()
+ end,
+
+ ["SetCursorPosition"] = function(self, ...)
+ return self.editBox:SetCursorPosition(...)
+ end,
+
+
+}
+
+--[[-----------------------------------------------------------------------------
+Constructor
+-------------------------------------------------------------------------------]]
+local backdrop = {
+ bgFile = [[Interface\Tooltips\UI-Tooltip-Background]],
+ edgeFile = [[Interface\Tooltips\UI-Tooltip-Border]], edgeSize = 16,
+ insets = { left = 4, right = 3, top = 4, bottom = 3 }
+}
+
+local function Constructor()
+ local frame = CreateFrame("Frame", nil, UIParent)
+ frame:Hide()
+
+ local widgetNum = AceGUI:GetNextWidgetNum(Type)
+
+ local label = frame:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
+ label:SetPoint("TOPLEFT", frame, "TOPLEFT", 0, -4)
+ label:SetPoint("TOPRIGHT", frame, "TOPRIGHT", 0, -4)
+ label:SetJustifyH("LEFT")
+ label:SetText(ACCEPT)
+ label:SetHeight(10)
+
+ local button = CreateFrame("Button", ("%s%dButton"):format(Type, widgetNum), frame, "UIPanelButtonTemplate")
+ button:SetPoint("BOTTOMLEFT", 0, 4)
+ button:SetHeight(22)
+ button:SetWidth(label:GetStringWidth() + 24)
+ button:SetText(ACCEPT)
+ button:SetScript("OnClick", OnClick)
+ button:Disable()
+
+ local text = button:GetFontString()
+ text:ClearAllPoints()
+ text:SetPoint("TOPLEFT", button, "TOPLEFT", 5, -5)
+ text:SetPoint("BOTTOMRIGHT", button, "BOTTOMRIGHT", -5, 1)
+ text:SetJustifyV("MIDDLE")
+
+ local scrollBG = CreateFrame("Frame", nil, frame)
+ scrollBG:SetBackdrop(backdrop)
+ scrollBG:SetBackdropColor(0, 0, 0)
+ scrollBG:SetBackdropBorderColor(0.4, 0.4, 0.4)
+
+ local scrollFrame = CreateFrame("ScrollFrame", ("%s%dScrollFrame"):format(Type, widgetNum), frame, "UIPanelScrollFrameTemplate")
+
+ local scrollBar = _G[scrollFrame:GetName() .. "ScrollBar"]
+ scrollBar:ClearAllPoints()
+ scrollBar:SetPoint("TOP", label, "BOTTOM", 0, -19)
+ scrollBar:SetPoint("BOTTOM", button, "TOP", 0, 18)
+ scrollBar:SetPoint("RIGHT", frame, "RIGHT")
+
+ scrollBG:SetPoint("TOPRIGHT", scrollBar, "TOPLEFT", 0, 19)
+ scrollBG:SetPoint("BOTTOMLEFT", button, "TOPLEFT")
+
+ scrollFrame:SetPoint("TOPLEFT", scrollBG, "TOPLEFT", 5, -6)
+ scrollFrame:SetPoint("BOTTOMRIGHT", scrollBG, "BOTTOMRIGHT", -4, 4)
+ scrollFrame:SetScript("OnEnter", OnEnter)
+ scrollFrame:SetScript("OnLeave", OnLeave)
+ scrollFrame:SetScript("OnMouseUp", OnMouseUp)
+ scrollFrame:SetScript("OnReceiveDrag", OnReceiveDrag)
+ scrollFrame:SetScript("OnSizeChanged", OnSizeChanged)
+ scrollFrame:HookScript("OnVerticalScroll", OnVerticalScroll)
+
+ local editBox = CreateFrame("EditBox", ("%s%dEdit"):format(Type, widgetNum), scrollFrame)
+ editBox:SetAllPoints()
+ editBox:SetFontObject(ChatFontNormal)
+ editBox:SetMultiLine(true)
+ editBox:EnableMouse(true)
+ editBox:SetAutoFocus(false)
+ editBox:SetCountInvisibleLetters(false)
+ editBox:SetScript("OnCursorChanged", OnCursorChanged)
+ editBox:SetScript("OnEditFocusLost", OnEditFocusLost)
+ editBox:SetScript("OnEnter", OnEnter)
+ editBox:SetScript("OnEscapePressed", editBox.ClearFocus)
+ editBox:SetScript("OnLeave", OnLeave)
+ editBox:SetScript("OnMouseDown", OnReceiveDrag)
+ editBox:SetScript("OnReceiveDrag", OnReceiveDrag)
+ editBox:SetScript("OnTextChanged", OnTextChanged)
+ editBox:SetScript("OnTextSet", OnTextSet)
+ editBox:SetScript("OnEditFocusGained", OnEditFocusGained)
+
+
+ scrollFrame:SetScrollChild(editBox)
+
+ local widget = {
+ button = button,
+ editBox = editBox,
+ frame = frame,
+ label = label,
+ labelHeight = 10,
+ numlines = 4,
+ scrollBar = scrollBar,
+ scrollBG = scrollBG,
+ scrollFrame = scrollFrame,
+ type = Type
+ }
+ for method, func in pairs(methods) do
+ widget[method] = func
+ end
+ button.obj, editBox.obj, scrollFrame.obj = widget, widget, widget
+
+ return AceGUI:RegisterAsWidget(widget)
+end
+
+AceGUI:RegisterWidgetType(Type, Constructor, Version)
diff --git a/Libs/AceGUI-3.0/widgets/AceGUIWidget-Slider.lua b/Libs/AceGUI-3.0/widgets/AceGUIWidget-Slider.lua
new file mode 100644
index 0000000..019c84e
--- /dev/null
+++ b/Libs/AceGUI-3.0/widgets/AceGUIWidget-Slider.lua
@@ -0,0 +1,284 @@
+--[[-----------------------------------------------------------------------------
+Slider Widget
+Graphical Slider, like, for Range values.
+-------------------------------------------------------------------------------]]
+local Type, Version = "Slider", 22
+local AceGUI = LibStub and LibStub("AceGUI-3.0", true)
+if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end
+
+-- Lua APIs
+local min, max, floor = math.min, math.max, math.floor
+local tonumber, pairs = tonumber, pairs
+
+-- WoW APIs
+local PlaySound = PlaySound
+local CreateFrame, UIParent = CreateFrame, UIParent
+
+-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
+-- List them here for Mikk's FindGlobals script
+-- GLOBALS: GameFontHighlightSmall
+
+--[[-----------------------------------------------------------------------------
+Support functions
+-------------------------------------------------------------------------------]]
+local function UpdateText(self)
+ local value = self.value or 0
+ if self.ispercent then
+ self.editbox:SetText(("%s%%"):format(floor(value * 1000 + 0.5) / 10))
+ else
+ self.editbox:SetText(floor(value * 100 + 0.5) / 100)
+ end
+end
+
+local function UpdateLabels(self)
+ local min, max = (self.min or 0), (self.max or 100)
+ if self.ispercent then
+ self.lowtext:SetFormattedText("%s%%", (min * 100))
+ self.hightext:SetFormattedText("%s%%", (max * 100))
+ else
+ self.lowtext:SetText(min)
+ self.hightext:SetText(max)
+ end
+end
+
+--[[-----------------------------------------------------------------------------
+Scripts
+-------------------------------------------------------------------------------]]
+local function Control_OnEnter(frame)
+ frame.obj:Fire("OnEnter")
+end
+
+local function Control_OnLeave(frame)
+ frame.obj:Fire("OnLeave")
+end
+
+local function Frame_OnMouseDown(frame)
+ frame.obj.slider:EnableMouseWheel(true)
+ AceGUI:ClearFocus()
+end
+
+local function Slider_OnValueChanged(frame, newvalue)
+ local self = frame.obj
+ if not frame.setup then
+ if self.step and self.step > 0 then
+ local min_value = self.min or 0
+ newvalue = floor((newvalue - min_value) / self.step + 0.5) * self.step + min_value
+ end
+ if newvalue ~= self.value and not self.disabled then
+ self.value = newvalue
+ self:Fire("OnValueChanged", newvalue)
+ end
+ if self.value then
+ UpdateText(self)
+ end
+ end
+end
+
+local function Slider_OnMouseUp(frame)
+ local self = frame.obj
+ self:Fire("OnMouseUp", self.value)
+end
+
+local function Slider_OnMouseWheel(frame, v)
+ local self = frame.obj
+ if not self.disabled then
+ local value = self.value
+ if v > 0 then
+ value = min(value + (self.step or 1), self.max)
+ else
+ value = max(value - (self.step or 1), self.min)
+ end
+ self.slider:SetValue(value)
+ end
+end
+
+local function EditBox_OnEscapePressed(frame)
+ frame:ClearFocus()
+end
+
+local function EditBox_OnEnterPressed(frame)
+ local self = frame.obj
+ local value = frame:GetText()
+ if self.ispercent then
+ value = value:gsub('%%', '')
+ value = tonumber(value) / 100
+ else
+ value = tonumber(value)
+ end
+
+ if value then
+ PlaySound(856) -- SOUNDKIT.IG_MAINMENU_OPTION_CHECKBOX_ON
+ self.slider:SetValue(value)
+ self:Fire("OnMouseUp", value)
+ end
+end
+
+local function EditBox_OnEnter(frame)
+ frame:SetBackdropBorderColor(0.5, 0.5, 0.5, 1)
+end
+
+local function EditBox_OnLeave(frame)
+ frame:SetBackdropBorderColor(0.3, 0.3, 0.3, 0.8)
+end
+
+--[[-----------------------------------------------------------------------------
+Methods
+-------------------------------------------------------------------------------]]
+local methods = {
+ ["OnAcquire"] = function(self)
+ self:SetWidth(200)
+ self:SetHeight(44)
+ self:SetDisabled(false)
+ self:SetIsPercent(nil)
+ self:SetSliderValues(0,100,1)
+ self:SetValue(0)
+ self.slider:EnableMouseWheel(false)
+ end,
+
+ -- ["OnRelease"] = nil,
+
+ ["SetDisabled"] = function(self, disabled)
+ self.disabled = disabled
+ if disabled then
+ self.slider:EnableMouse(false)
+ self.label:SetTextColor(.5, .5, .5)
+ self.hightext:SetTextColor(.5, .5, .5)
+ self.lowtext:SetTextColor(.5, .5, .5)
+ --self.valuetext:SetTextColor(.5, .5, .5)
+ self.editbox:SetTextColor(.5, .5, .5)
+ self.editbox:EnableMouse(false)
+ self.editbox:ClearFocus()
+ else
+ self.slider:EnableMouse(true)
+ self.label:SetTextColor(1, .82, 0)
+ self.hightext:SetTextColor(1, 1, 1)
+ self.lowtext:SetTextColor(1, 1, 1)
+ --self.valuetext:SetTextColor(1, 1, 1)
+ self.editbox:SetTextColor(1, 1, 1)
+ self.editbox:EnableMouse(true)
+ end
+ end,
+
+ ["SetValue"] = function(self, value)
+ self.slider.setup = true
+ self.slider:SetValue(value)
+ self.value = value
+ UpdateText(self)
+ self.slider.setup = nil
+ end,
+
+ ["GetValue"] = function(self)
+ return self.value
+ end,
+
+ ["SetLabel"] = function(self, text)
+ self.label:SetText(text)
+ end,
+
+ ["SetSliderValues"] = function(self, min, max, step)
+ local frame = self.slider
+ frame.setup = true
+ self.min = min
+ self.max = max
+ self.step = step
+ frame:SetMinMaxValues(min or 0,max or 100)
+ UpdateLabels(self)
+ frame:SetValueStep(step or 1)
+ if self.value then
+ frame:SetValue(self.value)
+ end
+ frame.setup = nil
+ end,
+
+ ["SetIsPercent"] = function(self, value)
+ self.ispercent = value
+ UpdateLabels(self)
+ UpdateText(self)
+ end
+}
+
+--[[-----------------------------------------------------------------------------
+Constructor
+-------------------------------------------------------------------------------]]
+local SliderBackdrop = {
+ bgFile = "Interface\\Buttons\\UI-SliderBar-Background",
+ edgeFile = "Interface\\Buttons\\UI-SliderBar-Border",
+ tile = true, tileSize = 8, edgeSize = 8,
+ insets = { left = 3, right = 3, top = 6, bottom = 6 }
+}
+
+local ManualBackdrop = {
+ bgFile = "Interface\\ChatFrame\\ChatFrameBackground",
+ edgeFile = "Interface\\ChatFrame\\ChatFrameBackground",
+ tile = true, edgeSize = 1, tileSize = 5,
+}
+
+local function Constructor()
+ local frame = CreateFrame("Frame", nil, UIParent)
+
+ frame:EnableMouse(true)
+ frame:SetScript("OnMouseDown", Frame_OnMouseDown)
+
+ local label = frame:CreateFontString(nil, "OVERLAY", "GameFontNormal")
+ label:SetPoint("TOPLEFT")
+ label:SetPoint("TOPRIGHT")
+ label:SetJustifyH("CENTER")
+ label:SetHeight(15)
+
+ local slider = CreateFrame("Slider", nil, frame)
+ slider:SetOrientation("HORIZONTAL")
+ slider:SetHeight(15)
+ slider:SetHitRectInsets(0, 0, -10, 0)
+ slider:SetBackdrop(SliderBackdrop)
+ slider:SetThumbTexture("Interface\\Buttons\\UI-SliderBar-Button-Horizontal")
+ slider:SetPoint("TOP", label, "BOTTOM")
+ slider:SetPoint("LEFT", 3, 0)
+ slider:SetPoint("RIGHT", -3, 0)
+ slider:SetValue(0)
+ slider:SetScript("OnValueChanged",Slider_OnValueChanged)
+ slider:SetScript("OnEnter", Control_OnEnter)
+ slider:SetScript("OnLeave", Control_OnLeave)
+ slider:SetScript("OnMouseUp", Slider_OnMouseUp)
+ slider:SetScript("OnMouseWheel", Slider_OnMouseWheel)
+
+ local lowtext = slider:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
+ lowtext:SetPoint("TOPLEFT", slider, "BOTTOMLEFT", 2, 3)
+
+ local hightext = slider:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
+ hightext:SetPoint("TOPRIGHT", slider, "BOTTOMRIGHT", -2, 3)
+
+ local editbox = CreateFrame("EditBox", nil, frame)
+ editbox:SetAutoFocus(false)
+ editbox:SetFontObject(GameFontHighlightSmall)
+ editbox:SetPoint("TOP", slider, "BOTTOM")
+ editbox:SetHeight(14)
+ editbox:SetWidth(70)
+ editbox:SetJustifyH("CENTER")
+ editbox:EnableMouse(true)
+ editbox:SetBackdrop(ManualBackdrop)
+ editbox:SetBackdropColor(0, 0, 0, 0.5)
+ editbox:SetBackdropBorderColor(0.3, 0.3, 0.30, 0.80)
+ editbox:SetScript("OnEnter", EditBox_OnEnter)
+ editbox:SetScript("OnLeave", EditBox_OnLeave)
+ editbox:SetScript("OnEnterPressed", EditBox_OnEnterPressed)
+ editbox:SetScript("OnEscapePressed", EditBox_OnEscapePressed)
+
+ local widget = {
+ label = label,
+ slider = slider,
+ lowtext = lowtext,
+ hightext = hightext,
+ editbox = editbox,
+ alignoffset = 25,
+ frame = frame,
+ type = Type
+ }
+ for method, func in pairs(methods) do
+ widget[method] = func
+ end
+ slider.obj, editbox.obj = widget, widget
+
+ return AceGUI:RegisterAsWidget(widget)
+end
+
+AceGUI:RegisterWidgetType(Type,Constructor,Version)
diff --git a/Libs/AceLocale-3.0/AceLocale-3.0.lua b/Libs/AceLocale-3.0/AceLocale-3.0.lua
new file mode 100644
index 0000000..e133781
--- /dev/null
+++ b/Libs/AceLocale-3.0/AceLocale-3.0.lua
@@ -0,0 +1,137 @@
+--- **AceLocale-3.0** manages localization in addons, allowing for multiple locale to be registered with fallback to the base locale for untranslated strings.
+-- @class file
+-- @name AceLocale-3.0
+-- @release $Id: AceLocale-3.0.lua 1035 2011-07-09 03:20:13Z kaelten $
+local MAJOR,MINOR = "AceLocale-3.0", 6
+
+local AceLocale, oldminor = LibStub:NewLibrary(MAJOR, MINOR)
+
+if not AceLocale then return end -- no upgrade needed
+
+-- Lua APIs
+local assert, tostring, error = assert, tostring, error
+local getmetatable, setmetatable, rawset, rawget = getmetatable, setmetatable, rawset, rawget
+
+-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
+-- List them here for Mikk's FindGlobals script
+-- GLOBALS: GAME_LOCALE, geterrorhandler
+
+local gameLocale = GetLocale()
+if gameLocale == "enGB" then
+ gameLocale = "enUS"
+end
+
+AceLocale.apps = AceLocale.apps or {} -- array of ["AppName"]=localetableref
+AceLocale.appnames = AceLocale.appnames or {} -- array of [localetableref]="AppName"
+
+-- This metatable is used on all tables returned from GetLocale
+local readmeta = {
+ __index = function(self, key) -- requesting totally unknown entries: fire off a nonbreaking error and return key
+ rawset(self, key, key) -- only need to see the warning once, really
+ geterrorhandler()(MAJOR..": "..tostring(AceLocale.appnames[self])..": Missing entry for '"..tostring(key).."'")
+ return key
+ end
+}
+
+-- This metatable is used on all tables returned from GetLocale if the silent flag is true, it does not issue a warning on unknown keys
+local readmetasilent = {
+ __index = function(self, key) -- requesting totally unknown entries: return key
+ rawset(self, key, key) -- only need to invoke this function once
+ return key
+ end
+}
+
+-- Remember the locale table being registered right now (it gets set by :NewLocale())
+-- NOTE: Do never try to register 2 locale tables at once and mix their definition.
+local registering
+
+-- local assert false function
+local assertfalse = function() assert(false) end
+
+-- This metatable proxy is used when registering nondefault locales
+local writeproxy = setmetatable({}, {
+ __newindex = function(self, key, value)
+ rawset(registering, key, value == true and key or value) -- assigning values: replace 'true' with key string
+ end,
+ __index = assertfalse
+})
+
+-- This metatable proxy is used when registering the default locale.
+-- It refuses to overwrite existing values
+-- Reason 1: Allows loading locales in any order
+-- Reason 2: If 2 modules have the same string, but only the first one to be
+-- loaded has a translation for the current locale, the translation
+-- doesn't get overwritten.
+--
+local writedefaultproxy = setmetatable({}, {
+ __newindex = function(self, key, value)
+ if not rawget(registering, key) then
+ rawset(registering, key, value == true and key or value)
+ end
+ end,
+ __index = assertfalse
+})
+
+--- Register a new locale (or extend an existing one) for the specified application.
+-- :NewLocale will return a table you can fill your locale into, or nil if the locale isn't needed for the players
+-- game locale.
+-- @paramsig application, locale[, isDefault[, silent]]
+-- @param application Unique name of addon / module
+-- @param locale Name of the locale to register, e.g. "enUS", "deDE", etc.
+-- @param isDefault If this is the default locale being registered (your addon is written in this language, generally enUS)
+-- @param silent If true, the locale will not issue warnings for missing keys. Must be set on the first locale registered. If set to "raw", nils will be returned for unknown keys (no metatable used).
+-- @usage
+-- -- enUS.lua
+-- local L = LibStub("AceLocale-3.0"):NewLocale("TestLocale", "enUS", true)
+-- L["string1"] = true
+--
+-- -- deDE.lua
+-- local L = LibStub("AceLocale-3.0"):NewLocale("TestLocale", "deDE")
+-- if not L then return end
+-- L["string1"] = "Zeichenkette1"
+-- @return Locale Table to add localizations to, or nil if the current locale is not required.
+function AceLocale:NewLocale(application, locale, isDefault, silent)
+
+ -- GAME_LOCALE allows translators to test translations of addons without having that wow client installed
+ local gameLocale = GAME_LOCALE or gameLocale
+
+ local app = AceLocale.apps[application]
+
+ if silent and app and getmetatable(app) ~= readmetasilent then
+ geterrorhandler()("Usage: NewLocale(application, locale[, isDefault[, silent]]): 'silent' must be specified for the first locale registered")
+ end
+
+ if not app then
+ if silent=="raw" then
+ app = {}
+ else
+ app = setmetatable({}, silent and readmetasilent or readmeta)
+ end
+ AceLocale.apps[application] = app
+ AceLocale.appnames[app] = application
+ end
+
+ if locale ~= gameLocale and not isDefault then
+ return -- nop, we don't need these translations
+ end
+
+ registering = app -- remember globally for writeproxy and writedefaultproxy
+
+ if isDefault then
+ return writedefaultproxy
+ end
+
+ return writeproxy
+end
+
+--- Returns localizations for the current locale (or default locale if translations are missing).
+-- Errors if nothing is registered (spank developer, not just a missing translation)
+-- @param application Unique name of addon / module
+-- @param silent If true, the locale is optional, silently return nil if it's not found (defaults to false, optional)
+-- @return The locale table for the current language.
+function AceLocale:GetLocale(application, silent)
+ if not silent and not AceLocale.apps[application] then
+ error("Usage: GetLocale(application[, silent]): 'application' - No locales registered for '"..tostring(application).."'", 2)
+ end
+ return AceLocale.apps[application]
+end
diff --git a/Libs/AceLocale-3.0/AceLocale-3.0.xml b/Libs/AceLocale-3.0/AceLocale-3.0.xml
new file mode 100644
index 0000000..bf023f0
--- /dev/null
+++ b/Libs/AceLocale-3.0/AceLocale-3.0.xml
@@ -0,0 +1,4 @@
+
+
+
diff --git a/Libs/AceTimer-3.0/AceTimer-3.0.lua b/Libs/AceTimer-3.0/AceTimer-3.0.lua
new file mode 100644
index 0000000..33b4da4
--- /dev/null
+++ b/Libs/AceTimer-3.0/AceTimer-3.0.lua
@@ -0,0 +1,278 @@
+--- **AceTimer-3.0** provides a central facility for registering timers.
+-- AceTimer supports one-shot timers and repeating timers. All timers are stored in an efficient
+-- data structure that allows easy dispatching and fast rescheduling. Timers can be registered
+-- or canceled at any time, even from within a running timer, without conflict or large overhead.\\
+-- AceTimer is currently limited to firing timers at a frequency of 0.01s as this is what the WoW timer API
+-- restricts us to.
+--
+-- All `:Schedule` functions will return a handle to the current timer, which you will need to store if you
+-- need to cancel the timer you just registered.
+--
+-- **AceTimer-3.0** can be embeded into your addon, either explicitly by calling AceTimer:Embed(MyAddon) or by
+-- specifying it as an embeded library in your AceAddon. All functions will be available on your addon object
+-- and can be accessed directly, without having to explicitly call AceTimer itself.\\
+-- It is recommended to embed AceTimer, otherwise you'll have to specify a custom `self` on all calls you
+-- make into AceTimer.
+-- @class file
+-- @name AceTimer-3.0
+-- @release $Id: AceTimer-3.0.lua 1202 2019-05-15 23:11:22Z nevcairiel $
+
+local MAJOR, MINOR = "AceTimer-3.0", 17 -- Bump minor on changes
+local AceTimer, oldminor = LibStub:NewLibrary(MAJOR, MINOR)
+
+if not AceTimer then return end -- No upgrade needed
+AceTimer.activeTimers = AceTimer.activeTimers or {} -- Active timer list
+local activeTimers = AceTimer.activeTimers -- Upvalue our private data
+
+-- Lua APIs
+local type, unpack, next, error, select = type, unpack, next, error, select
+-- WoW APIs
+local GetTime, C_TimerAfter = GetTime, C_Timer.After
+
+local function new(self, loop, func, delay, ...)
+ if delay < 0.01 then
+ delay = 0.01 -- Restrict to the lowest time that the C_Timer API allows us
+ end
+
+ local timer = {
+ object = self,
+ func = func,
+ looping = loop,
+ argsCount = select("#", ...),
+ delay = delay,
+ ends = GetTime() + delay,
+ ...
+ }
+
+ activeTimers[timer] = timer
+
+ -- Create new timer closure to wrap the "timer" object
+ timer.callback = function()
+ if not timer.cancelled then
+ if type(timer.func) == "string" then
+ -- We manually set the unpack count to prevent issues with an arg set that contains nil and ends with nil
+ -- e.g. local t = {1, 2, nil, 3, nil} print(#t) will result in 2, instead of 5. This fixes said issue.
+ timer.object[timer.func](timer.object, unpack(timer, 1, timer.argsCount))
+ else
+ timer.func(unpack(timer, 1, timer.argsCount))
+ end
+
+ if timer.looping and not timer.cancelled then
+ -- Compensate delay to get a perfect average delay, even if individual times don't match up perfectly
+ -- due to fps differences
+ local time = GetTime()
+ local delay = timer.delay - (time - timer.ends)
+ -- Ensure the delay doesn't go below the threshold
+ if delay < 0.01 then delay = 0.01 end
+ C_TimerAfter(delay, timer.callback)
+ timer.ends = time + delay
+ else
+ activeTimers[timer.handle or timer] = nil
+ end
+ end
+ end
+
+ C_TimerAfter(delay, timer.callback)
+ return timer
+end
+
+--- Schedule a new one-shot timer.
+-- The timer will fire once in `delay` seconds, unless canceled before.
+-- @param callback Callback function for the timer pulse (funcref or method name).
+-- @param delay Delay for the timer, in seconds.
+-- @param ... An optional, unlimited amount of arguments to pass to the callback function.
+-- @usage
+-- MyAddOn = LibStub("AceAddon-3.0"):NewAddon("MyAddOn", "AceTimer-3.0")
+--
+-- function MyAddOn:OnEnable()
+-- self:ScheduleTimer("TimerFeedback", 5)
+-- end
+--
+-- function MyAddOn:TimerFeedback()
+-- print("5 seconds passed")
+-- end
+function AceTimer:ScheduleTimer(func, delay, ...)
+ if not func or not delay then
+ error(MAJOR..": ScheduleTimer(callback, delay, args...): 'callback' and 'delay' must have set values.", 2)
+ end
+ if type(func) == "string" then
+ if type(self) ~= "table" then
+ error(MAJOR..": ScheduleTimer(callback, delay, args...): 'self' - must be a table.", 2)
+ elseif not self[func] then
+ error(MAJOR..": ScheduleTimer(callback, delay, args...): Tried to register '"..func.."' as the callback, but it doesn't exist in the module.", 2)
+ end
+ end
+ return new(self, nil, func, delay, ...)
+end
+
+--- Schedule a repeating timer.
+-- The timer will fire every `delay` seconds, until canceled.
+-- @param callback Callback function for the timer pulse (funcref or method name).
+-- @param delay Delay for the timer, in seconds.
+-- @param ... An optional, unlimited amount of arguments to pass to the callback function.
+-- @usage
+-- MyAddOn = LibStub("AceAddon-3.0"):NewAddon("MyAddOn", "AceTimer-3.0")
+--
+-- function MyAddOn:OnEnable()
+-- self.timerCount = 0
+-- self.testTimer = self:ScheduleRepeatingTimer("TimerFeedback", 5)
+-- end
+--
+-- function MyAddOn:TimerFeedback()
+-- self.timerCount = self.timerCount + 1
+-- print(("%d seconds passed"):format(5 * self.timerCount))
+-- -- run 30 seconds in total
+-- if self.timerCount == 6 then
+-- self:CancelTimer(self.testTimer)
+-- end
+-- end
+function AceTimer:ScheduleRepeatingTimer(func, delay, ...)
+ if not func or not delay then
+ error(MAJOR..": ScheduleRepeatingTimer(callback, delay, args...): 'callback' and 'delay' must have set values.", 2)
+ end
+ if type(func) == "string" then
+ if type(self) ~= "table" then
+ error(MAJOR..": ScheduleRepeatingTimer(callback, delay, args...): 'self' - must be a table.", 2)
+ elseif not self[func] then
+ error(MAJOR..": ScheduleRepeatingTimer(callback, delay, args...): Tried to register '"..func.."' as the callback, but it doesn't exist in the module.", 2)
+ end
+ end
+ return new(self, true, func, delay, ...)
+end
+
+--- Cancels a timer with the given id, registered by the same addon object as used for `:ScheduleTimer`
+-- Both one-shot and repeating timers can be canceled with this function, as long as the `id` is valid
+-- and the timer has not fired yet or was canceled before.
+-- @param id The id of the timer, as returned by `:ScheduleTimer` or `:ScheduleRepeatingTimer`
+function AceTimer:CancelTimer(id)
+ local timer = activeTimers[id]
+
+ if not timer then
+ return false
+ else
+ timer.cancelled = true
+ activeTimers[id] = nil
+ return true
+ end
+end
+
+--- Cancels all timers registered to the current addon object ('self')
+function AceTimer:CancelAllTimers()
+ for k,v in next, activeTimers do
+ if v.object == self then
+ AceTimer.CancelTimer(self, k)
+ end
+ end
+end
+
+--- Returns the time left for a timer with the given id, registered by the current addon object ('self').
+-- This function will return 0 when the id is invalid.
+-- @param id The id of the timer, as returned by `:ScheduleTimer` or `:ScheduleRepeatingTimer`
+-- @return The time left on the timer.
+function AceTimer:TimeLeft(id)
+ local timer = activeTimers[id]
+ if not timer then
+ return 0
+ else
+ return timer.ends - GetTime()
+ end
+end
+
+
+-- ---------------------------------------------------------------------
+-- Upgrading
+
+-- Upgrade from old hash-bucket based timers to C_Timer.After timers.
+if oldminor and oldminor < 10 then
+ -- disable old timer logic
+ AceTimer.frame:SetScript("OnUpdate", nil)
+ AceTimer.frame:SetScript("OnEvent", nil)
+ AceTimer.frame:UnregisterAllEvents()
+ -- convert timers
+ for object,timers in next, AceTimer.selfs do
+ for handle,timer in next, timers do
+ if type(timer) == "table" and timer.callback then
+ local newTimer
+ if timer.delay then
+ newTimer = AceTimer.ScheduleRepeatingTimer(timer.object, timer.callback, timer.delay, timer.arg)
+ else
+ newTimer = AceTimer.ScheduleTimer(timer.object, timer.callback, timer.when - GetTime(), timer.arg)
+ end
+ -- Use the old handle for old timers
+ activeTimers[newTimer] = nil
+ activeTimers[handle] = newTimer
+ newTimer.handle = handle
+ end
+ end
+ end
+ AceTimer.selfs = nil
+ AceTimer.hash = nil
+ AceTimer.debug = nil
+elseif oldminor and oldminor < 17 then
+ -- Upgrade from old animation based timers to C_Timer.After timers.
+ AceTimer.inactiveTimers = nil
+ AceTimer.frame = nil
+ local oldTimers = AceTimer.activeTimers
+ -- Clear old timer table and update upvalue
+ AceTimer.activeTimers = {}
+ activeTimers = AceTimer.activeTimers
+ for handle, timer in next, oldTimers do
+ local newTimer
+ -- Stop the old timer animation
+ local duration, elapsed = timer:GetDuration(), timer:GetElapsed()
+ timer:GetParent():Stop()
+ if timer.looping then
+ newTimer = AceTimer.ScheduleRepeatingTimer(timer.object, timer.func, duration, unpack(timer.args, 1, timer.argsCount))
+ else
+ newTimer = AceTimer.ScheduleTimer(timer.object, timer.func, duration - elapsed, unpack(timer.args, 1, timer.argsCount))
+ end
+ -- Use the old handle for old timers
+ activeTimers[newTimer] = nil
+ activeTimers[handle] = newTimer
+ newTimer.handle = handle
+ end
+
+ -- Migrate transitional handles
+ if oldminor < 13 and AceTimer.hashCompatTable then
+ for handle, id in next, AceTimer.hashCompatTable do
+ local t = activeTimers[id]
+ if t then
+ activeTimers[id] = nil
+ activeTimers[handle] = t
+ t.handle = handle
+ end
+ end
+ AceTimer.hashCompatTable = nil
+ end
+end
+
+-- ---------------------------------------------------------------------
+-- Embed handling
+
+AceTimer.embeds = AceTimer.embeds or {}
+
+local mixins = {
+ "ScheduleTimer", "ScheduleRepeatingTimer",
+ "CancelTimer", "CancelAllTimers",
+ "TimeLeft"
+}
+
+function AceTimer:Embed(target)
+ AceTimer.embeds[target] = true
+ for _,v in next, mixins do
+ target[v] = AceTimer[v]
+ end
+ return target
+end
+
+-- AceTimer:OnEmbedDisable(target)
+-- target (object) - target object that AceTimer is embedded in.
+--
+-- cancel all timers registered for the object
+function AceTimer:OnEmbedDisable(target)
+ target:CancelAllTimers()
+end
+
+for addon in next, AceTimer.embeds do
+ AceTimer:Embed(addon)
+end
diff --git a/Libs/AceTimer-3.0/AceTimer-3.0.xml b/Libs/AceTimer-3.0/AceTimer-3.0.xml
new file mode 100644
index 0000000..d5aee81
--- /dev/null
+++ b/Libs/AceTimer-3.0/AceTimer-3.0.xml
@@ -0,0 +1,4 @@
+
+
+
diff --git a/Libs/Bindings.xml b/Libs/Bindings.xml
new file mode 100644
index 0000000..77ab8c8
--- /dev/null
+++ b/Libs/Bindings.xml
@@ -0,0 +1,5 @@
+
+
+ ReloadUI()
+
+
diff --git a/Libs/CHANGES.txt b/Libs/CHANGES.txt
new file mode 100644
index 0000000..3b748d1
--- /dev/null
+++ b/Libs/CHANGES.txt
@@ -0,0 +1,58 @@
+------------------------------------------------------------------------
+r1236 | nevcairiel | 2020-04-16 07:36:45 +0000 (Thu, 16 Apr 2020) | 1 line
+Changed paths:
+ M /trunk/AceGUI-3.0/widgets/AceGUIWidget-DropDown.lua
+
+AceGUI-3.0: DropDown: Initialize the widget with an empty item list (instead of nil), this allows AddItem to be used right away (Fixes #542)
+------------------------------------------------------------------------
+r1235 | nevcairiel | 2020-04-15 10:27:55 +0000 (Wed, 15 Apr 2020) | 1 line
+Changed paths:
+ M /trunk/AceGUI-3.0/widgets/AceGUIContainer-TreeGroup.lua
+
+Cleanup debug
+------------------------------------------------------------------------
+r1234 | nevcairiel | 2020-04-15 10:14:35 +0000 (Wed, 15 Apr 2020) | 1 line
+Changed paths:
+ M /trunk/AceGUI-3.0/widgets/AceGUIContainer-TreeGroup.lua
+
+AceGUI-3.0: TreeGroup: Remove pre-8.0 compat
+------------------------------------------------------------------------
+r1233 | nevcairiel | 2020-04-15 10:09:47 +0000 (Wed, 15 Apr 2020) | 1 line
+Changed paths:
+ M /trunk/AceGUI-3.0/widgets/AceGUIWidget-Label.lua
+
+AceGUI-3.0: Label: Refresh anchoring after changing the font (Fixes #540)
+------------------------------------------------------------------------
+r1232 | nevcairiel | 2020-04-14 22:21:22 +0000 (Tue, 14 Apr 2020) | 1 line
+Changed paths:
+ M /trunk/AceConfig-3.0/AceConfigDialog-3.0/AceConfigDialog-3.0.lua
+
+AceConfigDialog-3.0: Prevent a dialog refresh when a multiselect control is closed during release (Fixes #539)
+------------------------------------------------------------------------
+r1231 | nevcairiel | 2020-04-14 22:20:36 +0000 (Tue, 14 Apr 2020) | 1 line
+Changed paths:
+ M /trunk/AceGUI-3.0/AceGUI-3.0.lua
+
+AceGUI-3.0: Add a getter to allow callers to check if a widget is currently being released, which can be used as part of some callbacks to alter behavior
+------------------------------------------------------------------------
+r1230 | funkydude | 2020-01-14 17:01:25 +0000 (Tue, 14 Jan 2020) | 1 line
+Changed paths:
+ M /trunk/Ace3.toc
+
+bump toc
+------------------------------------------------------------------------
+r1229 | funkydude | 2019-10-11 20:48:50 +0000 (Fri, 11 Oct 2019) | 1 line
+Changed paths:
+ M /trunk/Ace3.toc
+
+bump toc
+------------------------------------------------------------------------
+r1228 | nevcairiel | 2019-09-06 08:51:17 +0000 (Fri, 06 Sep 2019) | 3 lines
+Changed paths:
+ M /trunk/AceGUI-3.0/AceGUI-3.0.lua
+
+AceGUI-3.0: Avoid re-entrance into the Release function
+
+This can happen if a frame is released in a callback fired in the release process, and may in some cases throw errors.
+------------------------------------------------------------------------
+
diff --git a/Libs/CallbackHandler-1.0/CallbackHandler-1.0.lua b/Libs/CallbackHandler-1.0/CallbackHandler-1.0.lua
new file mode 100644
index 0000000..a8377fe
--- /dev/null
+++ b/Libs/CallbackHandler-1.0/CallbackHandler-1.0.lua
@@ -0,0 +1,212 @@
+--[[ $Id: CallbackHandler-1.0.lua 1186 2018-07-21 14:19:18Z nevcairiel $ ]]
+local MAJOR, MINOR = "CallbackHandler-1.0", 7
+local CallbackHandler = LibStub:NewLibrary(MAJOR, MINOR)
+
+if not CallbackHandler then return end -- No upgrade needed
+
+local meta = {__index = function(tbl, key) tbl[key] = {} return tbl[key] end}
+
+-- Lua APIs
+local tconcat = table.concat
+local assert, error, loadstring = assert, error, loadstring
+local setmetatable, rawset, rawget = setmetatable, rawset, rawget
+local next, select, pairs, type, tostring = next, select, pairs, type, tostring
+
+-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
+-- List them here for Mikk's FindGlobals script
+-- GLOBALS: geterrorhandler
+
+local xpcall = xpcall
+
+local function errorhandler(err)
+ return geterrorhandler()(err)
+end
+
+local function Dispatch(handlers, ...)
+ local index, method = next(handlers)
+ if not method then return end
+ repeat
+ xpcall(method, errorhandler, ...)
+ index, method = next(handlers, index)
+ until not method
+end
+
+--------------------------------------------------------------------------
+-- CallbackHandler:New
+--
+-- target - target object to embed public APIs in
+-- RegisterName - name of the callback registration API, default "RegisterCallback"
+-- UnregisterName - name of the callback unregistration API, default "UnregisterCallback"
+-- UnregisterAllName - name of the API to unregister all callbacks, default "UnregisterAllCallbacks". false == don't publish this API.
+
+function CallbackHandler:New(target, RegisterName, UnregisterName, UnregisterAllName)
+
+ RegisterName = RegisterName or "RegisterCallback"
+ UnregisterName = UnregisterName or "UnregisterCallback"
+ if UnregisterAllName==nil then -- false is used to indicate "don't want this method"
+ UnregisterAllName = "UnregisterAllCallbacks"
+ end
+
+ -- we declare all objects and exported APIs inside this closure to quickly gain access
+ -- to e.g. function names, the "target" parameter, etc
+
+
+ -- Create the registry object
+ local events = setmetatable({}, meta)
+ local registry = { recurse=0, events=events }
+
+ -- registry:Fire() - fires the given event/message into the registry
+ function registry:Fire(eventname, ...)
+ if not rawget(events, eventname) or not next(events[eventname]) then return end
+ local oldrecurse = registry.recurse
+ registry.recurse = oldrecurse + 1
+
+ Dispatch(events[eventname], eventname, ...)
+
+ registry.recurse = oldrecurse
+
+ if registry.insertQueue and oldrecurse==0 then
+ -- Something in one of our callbacks wanted to register more callbacks; they got queued
+ for eventname,callbacks in pairs(registry.insertQueue) do
+ local first = not rawget(events, eventname) or not next(events[eventname]) -- test for empty before. not test for one member after. that one member may have been overwritten.
+ for self,func in pairs(callbacks) do
+ events[eventname][self] = func
+ -- fire OnUsed callback?
+ if first and registry.OnUsed then
+ registry.OnUsed(registry, target, eventname)
+ first = nil
+ end
+ end
+ end
+ registry.insertQueue = nil
+ end
+ end
+
+ -- Registration of a callback, handles:
+ -- self["method"], leads to self["method"](self, ...)
+ -- self with function ref, leads to functionref(...)
+ -- "addonId" (instead of self) with function ref, leads to functionref(...)
+ -- all with an optional arg, which, if present, gets passed as first argument (after self if present)
+ target[RegisterName] = function(self, eventname, method, ... --[[actually just a single arg]])
+ if type(eventname) ~= "string" then
+ error("Usage: "..RegisterName.."(eventname, method[, arg]): 'eventname' - string expected.", 2)
+ end
+
+ method = method or eventname
+
+ local first = not rawget(events, eventname) or not next(events[eventname]) -- test for empty before. not test for one member after. that one member may have been overwritten.
+
+ if type(method) ~= "string" and type(method) ~= "function" then
+ error("Usage: "..RegisterName.."(\"eventname\", \"methodname\"): 'methodname' - string or function expected.", 2)
+ end
+
+ local regfunc
+
+ if type(method) == "string" then
+ -- self["method"] calling style
+ if type(self) ~= "table" then
+ error("Usage: "..RegisterName.."(\"eventname\", \"methodname\"): self was not a table?", 2)
+ elseif self==target then
+ error("Usage: "..RegisterName.."(\"eventname\", \"methodname\"): do not use Library:"..RegisterName.."(), use your own 'self'", 2)
+ elseif type(self[method]) ~= "function" then
+ error("Usage: "..RegisterName.."(\"eventname\", \"methodname\"): 'methodname' - method '"..tostring(method).."' not found on self.", 2)
+ end
+
+ if select("#",...)>=1 then -- this is not the same as testing for arg==nil!
+ local arg=select(1,...)
+ regfunc = function(...) self[method](self,arg,...) end
+ else
+ regfunc = function(...) self[method](self,...) end
+ end
+ else
+ -- function ref with self=object or self="addonId" or self=thread
+ if type(self)~="table" and type(self)~="string" and type(self)~="thread" then
+ error("Usage: "..RegisterName.."(self or \"addonId\", eventname, method): 'self or addonId': table or string or thread expected.", 2)
+ end
+
+ if select("#",...)>=1 then -- this is not the same as testing for arg==nil!
+ local arg=select(1,...)
+ regfunc = function(...) method(arg,...) end
+ else
+ regfunc = method
+ end
+ end
+
+
+ if events[eventname][self] or registry.recurse<1 then
+ -- if registry.recurse<1 then
+ -- we're overwriting an existing entry, or not currently recursing. just set it.
+ events[eventname][self] = regfunc
+ -- fire OnUsed callback?
+ if registry.OnUsed and first then
+ registry.OnUsed(registry, target, eventname)
+ end
+ else
+ -- we're currently processing a callback in this registry, so delay the registration of this new entry!
+ -- yes, we're a bit wasteful on garbage, but this is a fringe case, so we're picking low implementation overhead over garbage efficiency
+ registry.insertQueue = registry.insertQueue or setmetatable({},meta)
+ registry.insertQueue[eventname][self] = regfunc
+ end
+ end
+
+ -- Unregister a callback
+ target[UnregisterName] = function(self, eventname)
+ if not self or self==target then
+ error("Usage: "..UnregisterName.."(eventname): bad 'self'", 2)
+ end
+ if type(eventname) ~= "string" then
+ error("Usage: "..UnregisterName.."(eventname): 'eventname' - string expected.", 2)
+ end
+ if rawget(events, eventname) and events[eventname][self] then
+ events[eventname][self] = nil
+ -- Fire OnUnused callback?
+ if registry.OnUnused and not next(events[eventname]) then
+ registry.OnUnused(registry, target, eventname)
+ end
+ end
+ if registry.insertQueue and rawget(registry.insertQueue, eventname) and registry.insertQueue[eventname][self] then
+ registry.insertQueue[eventname][self] = nil
+ end
+ end
+
+ -- OPTIONAL: Unregister all callbacks for given selfs/addonIds
+ if UnregisterAllName then
+ target[UnregisterAllName] = function(...)
+ if select("#",...)<1 then
+ error("Usage: "..UnregisterAllName.."([whatFor]): missing 'self' or \"addonId\" to unregister events for.", 2)
+ end
+ if select("#",...)==1 and ...==target then
+ error("Usage: "..UnregisterAllName.."([whatFor]): supply a meaningful 'self' or \"addonId\"", 2)
+ end
+
+
+ for i=1,select("#",...) do
+ local self = select(i,...)
+ if registry.insertQueue then
+ for eventname, callbacks in pairs(registry.insertQueue) do
+ if callbacks[self] then
+ callbacks[self] = nil
+ end
+ end
+ end
+ for eventname, callbacks in pairs(events) do
+ if callbacks[self] then
+ callbacks[self] = nil
+ -- Fire OnUnused callback?
+ if registry.OnUnused and not next(callbacks) then
+ registry.OnUnused(registry, target, eventname)
+ end
+ end
+ end
+ end
+ end
+ end
+
+ return registry
+end
+
+
+-- CallbackHandler purposefully does NOT do explicit embedding. Nor does it
+-- try to upgrade old implicit embeds since the system is selfcontained and
+-- relies on closures to work.
+
diff --git a/Libs/CallbackHandler-1.0/CallbackHandler-1.0.xml b/Libs/CallbackHandler-1.0/CallbackHandler-1.0.xml
new file mode 100644
index 0000000..c107f88
--- /dev/null
+++ b/Libs/CallbackHandler-1.0/CallbackHandler-1.0.xml
@@ -0,0 +1,4 @@
+
+
+
diff --git a/Libs/LICENSE.txt b/Libs/LICENSE.txt
new file mode 100644
index 0000000..866fc34
--- /dev/null
+++ b/Libs/LICENSE.txt
@@ -0,0 +1,29 @@
+Copyright (c) 2007, Ace3 Development Team
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+ * Redistribution of a stand alone version is strictly prohibited without
+ prior written authorization from the Lead of the Ace3 Development Team.
+ * Neither the name of the Ace3 Development Team nor the names of its contributors
+ may be used to endorse or promote products derived from this software without
+ specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
\ No newline at end of file
diff --git a/Libs/LibDBIcon-1.0/LibDBIcon-1.0.lua b/Libs/LibDBIcon-1.0/LibDBIcon-1.0.lua
new file mode 100644
index 0000000..e865bdf
--- /dev/null
+++ b/Libs/LibDBIcon-1.0/LibDBIcon-1.0.lua
@@ -0,0 +1,470 @@
+
+-----------------------------------------------------------------------
+-- LibDBIcon-1.0
+--
+-- Allows addons to easily create a lightweight minimap icon as an alternative to heavier LDB displays.
+--
+
+local DBICON10 = "LibDBIcon-1.0"
+local DBICON10_MINOR = 43 -- Bump on changes
+if not LibStub then error(DBICON10 .. " requires LibStub.") end
+local ldb = LibStub("LibDataBroker-1.1", true)
+if not ldb then error(DBICON10 .. " requires LibDataBroker-1.1.") end
+local lib = LibStub:NewLibrary(DBICON10, DBICON10_MINOR)
+if not lib then return end
+
+lib.objects = lib.objects or {}
+lib.callbackRegistered = lib.callbackRegistered or nil
+lib.callbacks = lib.callbacks or LibStub("CallbackHandler-1.0"):New(lib)
+lib.notCreated = lib.notCreated or {}
+lib.radius = lib.radius or 5
+lib.tooltip = lib.tooltip or CreateFrame("GameTooltip", "LibDBIconTooltip", UIParent, "GameTooltipTemplate")
+local next, Minimap = next, Minimap
+local isDraggingButton = false
+
+function lib:IconCallback(event, name, key, value)
+ if lib.objects[name] then
+ if key == "icon" then
+ lib.objects[name].icon:SetTexture(value)
+ elseif key == "iconCoords" then
+ lib.objects[name].icon:UpdateCoord()
+ elseif key == "iconR" then
+ local _, g, b = lib.objects[name].icon:GetVertexColor()
+ lib.objects[name].icon:SetVertexColor(value, g, b)
+ elseif key == "iconG" then
+ local r, _, b = lib.objects[name].icon:GetVertexColor()
+ lib.objects[name].icon:SetVertexColor(r, value, b)
+ elseif key == "iconB" then
+ local r, g = lib.objects[name].icon:GetVertexColor()
+ lib.objects[name].icon:SetVertexColor(r, g, value)
+ end
+ end
+end
+if not lib.callbackRegistered then
+ ldb.RegisterCallback(lib, "LibDataBroker_AttributeChanged__icon", "IconCallback")
+ ldb.RegisterCallback(lib, "LibDataBroker_AttributeChanged__iconCoords", "IconCallback")
+ ldb.RegisterCallback(lib, "LibDataBroker_AttributeChanged__iconR", "IconCallback")
+ ldb.RegisterCallback(lib, "LibDataBroker_AttributeChanged__iconG", "IconCallback")
+ ldb.RegisterCallback(lib, "LibDataBroker_AttributeChanged__iconB", "IconCallback")
+ lib.callbackRegistered = true
+end
+
+local function getAnchors(frame)
+ local x, y = frame:GetCenter()
+ if not x or not y then return "CENTER" end
+ local hhalf = (x > UIParent:GetWidth()*2/3) and "RIGHT" or (x < UIParent:GetWidth()/3) and "LEFT" or ""
+ local vhalf = (y > UIParent:GetHeight()/2) and "TOP" or "BOTTOM"
+ return vhalf..hhalf, frame, (vhalf == "TOP" and "BOTTOM" or "TOP")..hhalf
+end
+
+local function onEnter(self)
+ if isDraggingButton then return end
+
+ for _, button in next, lib.objects do
+ if button.showOnMouseover then
+ button.fadeOut:Stop()
+ button:SetAlpha(1)
+ end
+ end
+
+ local obj = self.dataObject
+ if obj.OnTooltipShow then
+ lib.tooltip:SetOwner(self, "ANCHOR_NONE")
+ lib.tooltip:SetPoint(getAnchors(self))
+ obj.OnTooltipShow(lib.tooltip)
+ lib.tooltip:Show()
+ elseif obj.OnEnter then
+ obj.OnEnter(self)
+ end
+end
+
+local function onLeave(self)
+ lib.tooltip:Hide()
+
+ if not isDraggingButton then
+ for _, button in next, lib.objects do
+ if button.showOnMouseover then
+ button.fadeOut:Play()
+ end
+ end
+ end
+
+ local obj = self.dataObject
+ if obj.OnLeave then
+ obj.OnLeave(self)
+ end
+end
+
+--------------------------------------------------------------------------------
+
+local onDragStart, updatePosition
+
+do
+ local minimapShapes = {
+ ["ROUND"] = {true, true, true, true},
+ ["SQUARE"] = {false, false, false, false},
+ ["CORNER-TOPLEFT"] = {false, false, false, true},
+ ["CORNER-TOPRIGHT"] = {false, false, true, false},
+ ["CORNER-BOTTOMLEFT"] = {false, true, false, false},
+ ["CORNER-BOTTOMRIGHT"] = {true, false, false, false},
+ ["SIDE-LEFT"] = {false, true, false, true},
+ ["SIDE-RIGHT"] = {true, false, true, false},
+ ["SIDE-TOP"] = {false, false, true, true},
+ ["SIDE-BOTTOM"] = {true, true, false, false},
+ ["TRICORNER-TOPLEFT"] = {false, true, true, true},
+ ["TRICORNER-TOPRIGHT"] = {true, false, true, true},
+ ["TRICORNER-BOTTOMLEFT"] = {true, true, false, true},
+ ["TRICORNER-BOTTOMRIGHT"] = {true, true, true, false},
+ }
+
+ local rad, cos, sin, sqrt, max, min = math.rad, math.cos, math.sin, math.sqrt, math.max, math.min
+ function updatePosition(button, position)
+ local angle = rad(position or 225)
+ local x, y, q = cos(angle), sin(angle), 1
+ if x < 0 then q = q + 1 end
+ if y > 0 then q = q + 2 end
+ local minimapShape = GetMinimapShape and GetMinimapShape() or "ROUND"
+ local quadTable = minimapShapes[minimapShape]
+ local w = (Minimap:GetWidth() / 2) + lib.radius
+ local h = (Minimap:GetHeight() / 2) + lib.radius
+ if quadTable[q] then
+ x, y = x*w, y*h
+ else
+ local diagRadiusW = sqrt(2*(w)^2)-10
+ local diagRadiusH = sqrt(2*(h)^2)-10
+ x = max(-w, min(x*diagRadiusW, w))
+ y = max(-h, min(y*diagRadiusH, h))
+ end
+ button:SetPoint("CENTER", Minimap, "CENTER", x, y)
+ end
+end
+
+local function onClick(self, b)
+ if self.dataObject.OnClick then
+ self.dataObject.OnClick(self, b)
+ end
+end
+
+local function onMouseDown(self)
+ self.isMouseDown = true
+ self.icon:UpdateCoord()
+end
+
+local function onMouseUp(self)
+ self.isMouseDown = false
+ self.icon:UpdateCoord()
+end
+
+do
+ local deg, atan2 = math.deg, math.atan2
+ local function onUpdate(self)
+ local mx, my = Minimap:GetCenter()
+ local px, py = GetCursorPosition()
+ local scale = Minimap:GetEffectiveScale()
+ px, py = px / scale, py / scale
+ local pos = 225
+ if self.db then
+ pos = deg(atan2(py - my, px - mx)) % 360
+ self.db.minimapPos = pos
+ else
+ pos = deg(atan2(py - my, px - mx)) % 360
+ self.minimapPos = pos
+ end
+ updatePosition(self, pos)
+ end
+
+ function onDragStart(self)
+ self:LockHighlight()
+ self.isMouseDown = true
+ self.icon:UpdateCoord()
+ self:SetScript("OnUpdate", onUpdate)
+ isDraggingButton = true
+ lib.tooltip:Hide()
+ for _, button in next, lib.objects do
+ if button.showOnMouseover then
+ button.fadeOut:Stop()
+ button:SetAlpha(1)
+ end
+ end
+ end
+end
+
+local function onDragStop(self)
+ self:SetScript("OnUpdate", nil)
+ self.isMouseDown = false
+ self.icon:UpdateCoord()
+ self:UnlockHighlight()
+ isDraggingButton = false
+ for _, button in next, lib.objects do
+ if button.showOnMouseover then
+ button.fadeOut:Play()
+ end
+ end
+end
+
+local defaultCoords = {0, 1, 0, 1}
+local function updateCoord(self)
+ local coords = self:GetParent().dataObject.iconCoords or defaultCoords
+ local deltaX, deltaY = 0, 0
+ if not self:GetParent().isMouseDown then
+ deltaX = (coords[2] - coords[1]) * 0.05
+ deltaY = (coords[4] - coords[3]) * 0.05
+ end
+ self:SetTexCoord(coords[1] + deltaX, coords[2] - deltaX, coords[3] + deltaY, coords[4] - deltaY)
+end
+
+local function createButton(name, object, db)
+ local button = CreateFrame("Button", "LibDBIcon10_"..name, Minimap)
+ button.dataObject = object
+ button.db = db
+ button:SetFrameStrata("MEDIUM")
+ button:SetSize(31, 31)
+ button:SetFrameLevel(8)
+ button:RegisterForClicks("anyUp")
+ button:RegisterForDrag("LeftButton")
+ button:SetHighlightTexture(136477) --"Interface\\Minimap\\UI-Minimap-ZoomButton-Highlight"
+ local overlay = button:CreateTexture(nil, "OVERLAY")
+ overlay:SetSize(53, 53)
+ overlay:SetTexture(136430) --"Interface\\Minimap\\MiniMap-TrackingBorder"
+ overlay:SetPoint("TOPLEFT")
+ local background = button:CreateTexture(nil, "BACKGROUND")
+ background:SetSize(20, 20)
+ background:SetTexture(136467) --"Interface\\Minimap\\UI-Minimap-Background"
+ background:SetPoint("TOPLEFT", 7, -5)
+ local icon = button:CreateTexture(nil, "ARTWORK")
+ icon:SetSize(17, 17)
+ icon:SetTexture(object.icon)
+ icon:SetPoint("TOPLEFT", 7, -6)
+ button.icon = icon
+ button.isMouseDown = false
+
+ local r, g, b = icon:GetVertexColor()
+ icon:SetVertexColor(object.iconR or r, object.iconG or g, object.iconB or b)
+
+ icon.UpdateCoord = updateCoord
+ icon:UpdateCoord()
+
+ button:SetScript("OnEnter", onEnter)
+ button:SetScript("OnLeave", onLeave)
+ button:SetScript("OnClick", onClick)
+ if not db or not db.lock then
+ button:SetScript("OnDragStart", onDragStart)
+ button:SetScript("OnDragStop", onDragStop)
+ end
+ button:SetScript("OnMouseDown", onMouseDown)
+ button:SetScript("OnMouseUp", onMouseUp)
+
+ button.fadeOut = button:CreateAnimationGroup()
+ local animOut = button.fadeOut:CreateAnimation("Alpha")
+ animOut:SetOrder(1)
+ animOut:SetDuration(0.2)
+ animOut:SetFromAlpha(1)
+ animOut:SetToAlpha(0)
+ animOut:SetStartDelay(1)
+ button.fadeOut:SetToFinalAlpha(true)
+
+ lib.objects[name] = button
+
+ if lib.loggedIn then
+ updatePosition(button, db and db.minimapPos)
+ if not db or not db.hide then
+ button:Show()
+ else
+ button:Hide()
+ end
+ end
+ lib.callbacks:Fire("LibDBIcon_IconCreated", button, name) -- Fire 'Icon Created' callback
+end
+
+-- We could use a metatable.__index on lib.objects, but then we'd create
+-- the icons when checking things like :IsRegistered, which is not necessary.
+local function check(name)
+ if lib.notCreated[name] then
+ createButton(name, lib.notCreated[name][1], lib.notCreated[name][2])
+ lib.notCreated[name] = nil
+ end
+end
+
+-- Wait a bit with the initial positioning to let any GetMinimapShape addons
+-- load up.
+if not lib.loggedIn then
+ local f = CreateFrame("Frame")
+ f:SetScript("OnEvent", function(f)
+ for _, button in next, lib.objects do
+ updatePosition(button, button.db and button.db.minimapPos)
+ if not button.db or not button.db.hide then
+ button:Show()
+ else
+ button:Hide()
+ end
+ end
+ lib.loggedIn = true
+ f:SetScript("OnEvent", nil)
+ end)
+ f:RegisterEvent("PLAYER_LOGIN")
+end
+
+local function getDatabase(name)
+ return lib.notCreated[name] and lib.notCreated[name][2] or lib.objects[name].db
+end
+
+function lib:Register(name, object, db)
+ if not object.icon then error("Can't register LDB objects without icons set!") end
+ if lib.objects[name] or lib.notCreated[name] then error(DBICON10.. ": Object '".. name .."' is already registered.") end
+ if not db or not db.hide then
+ createButton(name, object, db)
+ else
+ lib.notCreated[name] = {object, db}
+ end
+end
+
+function lib:Lock(name)
+ if not lib:IsRegistered(name) then return end
+ if lib.objects[name] then
+ lib.objects[name]:SetScript("OnDragStart", nil)
+ lib.objects[name]:SetScript("OnDragStop", nil)
+ end
+ local db = getDatabase(name)
+ if db then
+ db.lock = true
+ end
+end
+
+function lib:Unlock(name)
+ if not lib:IsRegistered(name) then return end
+ if lib.objects[name] then
+ lib.objects[name]:SetScript("OnDragStart", onDragStart)
+ lib.objects[name]:SetScript("OnDragStop", onDragStop)
+ end
+ local db = getDatabase(name)
+ if db then
+ db.lock = nil
+ end
+end
+
+function lib:Hide(name)
+ if not lib.objects[name] then return end
+ lib.objects[name]:Hide()
+end
+
+function lib:Show(name)
+ check(name)
+ local button = lib.objects[name]
+ if button then
+ button:Show()
+ updatePosition(button, button.db and button.db.minimapPos or button.minimapPos)
+ end
+end
+
+function lib:IsRegistered(name)
+ return (lib.objects[name] or lib.notCreated[name]) and true or false
+end
+
+function lib:Refresh(name, db)
+ check(name)
+ local button = lib.objects[name]
+ if db then
+ button.db = db
+ end
+ updatePosition(button, button.db and button.db.minimapPos or button.minimapPos)
+ if not button.db or not button.db.hide then
+ button:Show()
+ else
+ button:Hide()
+ end
+ if not button.db or not button.db.lock then
+ button:SetScript("OnDragStart", onDragStart)
+ button:SetScript("OnDragStop", onDragStop)
+ else
+ button:SetScript("OnDragStart", nil)
+ button:SetScript("OnDragStop", nil)
+ end
+end
+
+function lib:GetMinimapButton(name)
+ return lib.objects[name]
+end
+
+do
+ local function OnMinimapEnter()
+ if isDraggingButton then return end
+ for _, button in next, lib.objects do
+ if button.showOnMouseover then
+ button.fadeOut:Stop()
+ button:SetAlpha(1)
+ end
+ end
+ end
+ local function OnMinimapLeave()
+ if isDraggingButton then return end
+ for _, button in next, lib.objects do
+ if button.showOnMouseover then
+ button.fadeOut:Play()
+ end
+ end
+ end
+ Minimap:HookScript("OnEnter", OnMinimapEnter)
+ Minimap:HookScript("OnLeave", OnMinimapLeave)
+
+ function lib:ShowOnEnter(name, value)
+ local button = lib.objects[name]
+ if button then
+ if value then
+ button.showOnMouseover = true
+ button.fadeOut:Stop()
+ button:SetAlpha(0)
+ else
+ button.showOnMouseover = false
+ button.fadeOut:Stop()
+ button:SetAlpha(1)
+ end
+ end
+ end
+end
+
+function lib:GetButtonList()
+ local t = {}
+ for name in next, lib.objects do
+ t[#t+1] = name
+ end
+ return t
+end
+
+function lib:SetButtonRadius(radius)
+ if type(radius) == "number" then
+ lib.radius = radius
+ for _, button in next, lib.objects do
+ updatePosition(button, button.db and button.db.minimapPos or button.minimapPos)
+ end
+ end
+end
+
+function lib:SetButtonToPosition(button, position)
+ updatePosition(lib.objects[button] or button, position)
+end
+
+-- Upgrade!
+for name, button in next, lib.objects do
+ local db = getDatabase(name)
+ if not db or not db.lock then
+ button:SetScript("OnDragStart", onDragStart)
+ button:SetScript("OnDragStop", onDragStop)
+ end
+ button:SetScript("OnEnter", onEnter)
+ button:SetScript("OnLeave", onLeave)
+ button:SetScript("OnClick", onClick)
+ button:SetScript("OnMouseDown", onMouseDown)
+ button:SetScript("OnMouseUp", onMouseUp)
+
+ if not button.fadeOut then -- Upgrade to 39
+ button.fadeOut = button:CreateAnimationGroup()
+ local animOut = button.fadeOut:CreateAnimation("Alpha")
+ animOut:SetOrder(1)
+ animOut:SetDuration(0.2)
+ animOut:SetFromAlpha(1)
+ animOut:SetToAlpha(0)
+ animOut:SetStartDelay(1)
+ button.fadeOut:SetToFinalAlpha(true)
+ end
+end
+lib:SetButtonRadius(lib.radius) -- Upgrade to 40
diff --git a/Libs/LibDBIcon-1.0/lib.xml b/Libs/LibDBIcon-1.0/lib.xml
new file mode 100644
index 0000000..5dca17c
--- /dev/null
+++ b/Libs/LibDBIcon-1.0/lib.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
diff --git a/Libs/LibDataBroker-1.1/LibDataBroker-1.1.lua b/Libs/LibDataBroker-1.1/LibDataBroker-1.1.lua
new file mode 100644
index 0000000..f47c0cd
--- /dev/null
+++ b/Libs/LibDataBroker-1.1/LibDataBroker-1.1.lua
@@ -0,0 +1,90 @@
+
+assert(LibStub, "LibDataBroker-1.1 requires LibStub")
+assert(LibStub:GetLibrary("CallbackHandler-1.0", true), "LibDataBroker-1.1 requires CallbackHandler-1.0")
+
+local lib, oldminor = LibStub:NewLibrary("LibDataBroker-1.1", 4)
+if not lib then return end
+oldminor = oldminor or 0
+
+
+lib.callbacks = lib.callbacks or LibStub:GetLibrary("CallbackHandler-1.0"):New(lib)
+lib.attributestorage, lib.namestorage, lib.proxystorage = lib.attributestorage or {}, lib.namestorage or {}, lib.proxystorage or {}
+local attributestorage, namestorage, callbacks = lib.attributestorage, lib.namestorage, lib.callbacks
+
+if oldminor < 2 then
+ lib.domt = {
+ __metatable = "access denied",
+ __index = function(self, key) return attributestorage[self] and attributestorage[self][key] end,
+ }
+end
+
+if oldminor < 3 then
+ lib.domt.__newindex = function(self, key, value)
+ if not attributestorage[self] then attributestorage[self] = {} end
+ if attributestorage[self][key] == value then return end
+ attributestorage[self][key] = value
+ local name = namestorage[self]
+ if not name then return end
+ callbacks:Fire("LibDataBroker_AttributeChanged", name, key, value, self)
+ callbacks:Fire("LibDataBroker_AttributeChanged_"..name, name, key, value, self)
+ callbacks:Fire("LibDataBroker_AttributeChanged_"..name.."_"..key, name, key, value, self)
+ callbacks:Fire("LibDataBroker_AttributeChanged__"..key, name, key, value, self)
+ end
+end
+
+if oldminor < 2 then
+ function lib:NewDataObject(name, dataobj)
+ if self.proxystorage[name] then return end
+
+ if dataobj then
+ assert(type(dataobj) == "table", "Invalid dataobj, must be nil or a table")
+ self.attributestorage[dataobj] = {}
+ for i,v in pairs(dataobj) do
+ self.attributestorage[dataobj][i] = v
+ dataobj[i] = nil
+ end
+ end
+ dataobj = setmetatable(dataobj or {}, self.domt)
+ self.proxystorage[name], self.namestorage[dataobj] = dataobj, name
+ self.callbacks:Fire("LibDataBroker_DataObjectCreated", name, dataobj)
+ return dataobj
+ end
+end
+
+if oldminor < 1 then
+ function lib:DataObjectIterator()
+ return pairs(self.proxystorage)
+ end
+
+ function lib:GetDataObjectByName(dataobjectname)
+ return self.proxystorage[dataobjectname]
+ end
+
+ function lib:GetNameByDataObject(dataobject)
+ return self.namestorage[dataobject]
+ end
+end
+
+if oldminor < 4 then
+ local next = pairs(attributestorage)
+ function lib:pairs(dataobject_or_name)
+ local t = type(dataobject_or_name)
+ assert(t == "string" or t == "table", "Usage: ldb:pairs('dataobjectname') or ldb:pairs(dataobject)")
+
+ local dataobj = self.proxystorage[dataobject_or_name] or dataobject_or_name
+ assert(attributestorage[dataobj], "Data object not found")
+
+ return next, attributestorage[dataobj], nil
+ end
+
+ local ipairs_iter = ipairs(attributestorage)
+ function lib:ipairs(dataobject_or_name)
+ local t = type(dataobject_or_name)
+ assert(t == "string" or t == "table", "Usage: ldb:ipairs('dataobjectname') or ldb:ipairs(dataobject)")
+
+ local dataobj = self.proxystorage[dataobject_or_name] or dataobject_or_name
+ assert(attributestorage[dataobj], "Data object not found")
+
+ return ipairs_iter, attributestorage[dataobj], 0
+ end
+end
diff --git a/Libs/LibDataBroker-1.1/README.textile b/Libs/LibDataBroker-1.1/README.textile
new file mode 100644
index 0000000..ef16fed
--- /dev/null
+++ b/Libs/LibDataBroker-1.1/README.textile
@@ -0,0 +1,13 @@
+LibDataBroker is a small WoW addon library designed to provide a "MVC":http://en.wikipedia.org/wiki/Model-view-controller interface for use in various addons.
+LDB's primary goal is to "detach" plugins for TitanPanel and FuBar from the display addon.
+Plugins can provide data into a simple table, and display addons can receive callbacks to refresh their display of this data.
+LDB also provides a place for addons to register "quicklaunch" functions, removing the need for authors to embed many large libraries to create minimap buttons.
+Users who do not wish to be "plagued" by these buttons simply do not install an addon to render them.
+
+Due to it's simple generic design, LDB can be used for any design where you wish to have an addon notified of changes to a table.
+
+h2. Links
+
+* "API documentation":http://github.com/tekkub/libdatabroker-1-1/wikis/api
+* "Data specifications":http://github.com/tekkub/libdatabroker-1-1/wikis/data-specifications
+* "Addons using LDB":http://github.com/tekkub/libdatabroker-1-1/wikis/addons-using-ldb
diff --git a/Libs/LibDeflate/LICENSE.txt b/Libs/LibDeflate/LICENSE.txt
new file mode 100644
index 0000000..7da0484
--- /dev/null
+++ b/Libs/LibDeflate/LICENSE.txt
@@ -0,0 +1,147 @@
+GNU GENERAL PUBLIC LICENSE
+Version 3, 29 June 2007
+Copyright (C) 2007 Free Software Foundation, Inc. http://fsf.org
+Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
+Preamble
+The GNU General Public License is a free, copyleft license for software and other kinds of works.
+The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too.
+When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things.
+To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others.
+For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights.
+Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it.
+For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions.
+Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users.
+Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free.
+The precise terms and conditions for copying, distribution and modification follow.
+TERMS AND CONDITIONS
+0. Definitions.
+“This License” refers to version 3 of the GNU General Public License.
+“Copyright” also means copyright-like laws that apply to other kinds of works, such as semiconductor masks.
+“The Program” refers to any copyrightable work licensed under this License. Each licensee is addressed as “you”. “Licensees” and “recipients” may be individuals or organizations.
+To “modify” a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a “modified version” of the earlier work or a work “based on” the earlier work.
+A “covered work” means either the unmodified Program or a work based on the Program.
+To “propagate” a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well.
+To “convey” a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying.
+An interactive user interface displays “Appropriate Legal Notices” to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion.
+1. Source Code.
+The “source code” for a work means the preferred form of the work for making modifications to it. “Object code” means any non-source form of a work.
+A “Standard Interface” means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language.
+The “System Libraries” of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A “Major Component”, in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it.
+The “Corresponding Source” for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work.
+The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source.
+The Corresponding Source for a work in source code form is that same work.
+2. Basic Permissions.
+All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law.
+You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you.
+Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary.
+3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures.
+When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures.
+4. Conveying Verbatim Copies.
+You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program.
+You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee.
+5. Conveying Modified Source Versions.
+You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions:
+
+- a) The work must carry prominent notices stating that you modified it, and giving a relevant date.
+- b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to “keep intact all notices”.
+- c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it.
+- d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so.
+
+A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an “aggregate” if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate.
+6. Conveying Non-Source Forms.
+You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways:
+
+- a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange.
+- b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge.
+- c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b.
+- d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements.
+- e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d.
+
+A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work.
+A “User Product” is either (1) a “consumer product”, which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, “normally used” refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product.
+“Installation Information” for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made.
+If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM).
+The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network.
+Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying.
+7. Additional Terms.
+“Additional permissions” are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions.
+When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission.
+Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms:
+
+- a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or
+- b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or
+- c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or
+- d) Limiting the use for publicity purposes of names of licensors or authors of the material; or
+- e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or
+- f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors.
+
+All other non-permissive additional terms are considered “further restrictions” within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying.
+If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms.
+Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way.
+8. Termination.
+You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11).
+However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation.
+Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice.
+Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10.
+9. Acceptance Not Required for Having Copies.
+You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so.
+10. Automatic Licensing of Downstream Recipients.
+Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License.
+An “entity transaction” is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts.
+You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it.
+11. Patents.
+A “contributor” is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's “contributor version”.
+A contributor's “essential patent claims” are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, “control” includes the right to grant patent sublicenses in a manner consistent with the requirements of this License.
+Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version.
+In the following three paragraphs, a “patent license” is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To “grant” such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party.
+If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. “Knowingly relying” means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid.
+If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it.
+A patent license is “discriminatory” if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007.
+Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law.
+12. No Surrender of Others' Freedom.
+If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program.
+13. Use with the GNU Affero General Public License.
+Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such.
+14. Revised Versions of this License.
+The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.
+Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License “or any later version” applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation.
+If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program.
+Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version.
+15. Disclaimer of Warranty.
+THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+16. Limitation of Liability.
+IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+17. Interpretation of Sections 15 and 16.
+If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee.
+END OF TERMS AND CONDITIONS
+How to Apply These Terms to Your New Programs
+If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms.
+To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the “copyright” line and a pointer to where the full notice is found.
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see http://www.gnu.org/licenses.
+
+Also add information on how to contact you by electronic and paper mail.
+If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode:
+ <program> Copyright (C) <year> <name of author>
+
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an “about box”.
+You should also get your employer (if you work as a programmer) or school, if any, to sign a “copyright disclaimer” for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see http://www.gnu.org/licenses.
+The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read http://www.gnu.org/philosophy/why-not-lgpl.html.
\ No newline at end of file
diff --git a/Libs/LibDeflate/LibDeflate.lua b/Libs/LibDeflate/LibDeflate.lua
new file mode 100644
index 0000000..43f4972
--- /dev/null
+++ b/Libs/LibDeflate/LibDeflate.lua
@@ -0,0 +1,3525 @@
+--[[--
+LibDeflate 1.0.1-release
+Pure Lua compressor and decompressor with high compression ratio using
+DEFLATE/zlib format.
+
+@file LibDeflate.lua
+@author Haoqian He (Github: SafeteeWoW; World of Warcraft: Safetyy-Illidan(US))
+@copyright LibDeflate <2018-2019> Haoqian He
+@license GNU LESSER GENERAL PUBLIC LICENSE Version 3 or later
+
+This library is implemented according to the following specifications.
+Report a bug if LibDeflate is not fully compliant with those specs.
+Both compressors and decompressors have been implemented in the library.
+1. RFC1950: DEFLATE Compressed Data Format Specification version 1.3
+https://tools.ietf.org/html/rfc1951
+2. RFC1951: ZLIB Compressed Data Format Specification version 3.3
+https://tools.ietf.org/html/rfc1950
+
+This library requires Lua 5.1/5.2/5.3/5.4 interpreter or LuaJIT v2.0+.
+This library does not have any dependencies.
+Note at the time of this release, Lua 5.4 final is not released yet.
+For Lua 5.4, This library is tested with its first beta version.
+
+This file "LibDeflate.lua" is the only source file of
+the library.
+Submit suggestions or report bugs to
+https://github.com/safeteeWow/LibDeflate/issues
+]]
+
+--[[
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with this program. If not, see https://www.gnu.org/licenses/.
+
+Credits and Disclaimer:
+The following projects are used to the help to test the correctness
+of this program. The code of this program (LibDeflate.lua) does not
+use their code directly, but uses their ideas and algorithms. Their original
+licenses shall be comply when used.
+
+1. zlib, by Jean-loup Gailly (compression) and Mark Adler (decompression).
+ http://www.zlib.net/
+ Licensed under zlib License. http://www.zlib.net/zlib_license.html
+ For the compression algorithm.
+2. puff, by Mark Adler. https://github.com/madler/zlib/tree/master/contrib/puff
+ Licensed under zlib License. http://www.zlib.net/zlib_license.html
+ For the decompression algorithm.
+3. LibCompress, by jjsheets and Galmok of European Stormrage (Horde)
+ https://www.wowace.com/projects/libcompress
+ Licensed under GPLv2.
+ https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ For the code to create customized codec.
+4. WeakAuras2,
+ https://github.com/WeakAuras/WeakAuras2
+ Licensed under GPLv2.
+ For the 6bit encoding and decoding.
+]]
+
+--[[
+ Curseforge auto-packaging replacements:
+
+ Project Date: 2019-11-23T19:12:27Z
+ Project Hash: 21ca927770daca746f8659ee8c429c7971f573cc
+ Project Version: 1.0.1-release
+--]]
+
+local LibDeflate
+
+do
+ -- Semantic version. all lowercase.
+ -- Suffix can be alpha1, alpha2, beta1, beta2, rc1, rc2, etc.
+ -- NOTE: Two version numbers needs to modify.
+ -- 1. On the top of LibDeflate.lua
+ -- 2. _VERSION
+ -- 3. _MINOR
+
+ -- version to store the official version of LibDeflate
+ local _VERSION = "1.0.1-release"
+
+ -- When MAJOR is changed, I should name it as LibDeflate2
+ local _MAJOR = "LibDeflate"
+
+ -- Update this whenever a new version, for LibStub version registration.
+ local _MINOR = 2
+
+ local _COPYRIGHT =
+ "LibDeflate ".._VERSION
+ .." Copyright (C) 2018-2019 Haoqian He."
+ .." License LGPLv3+: GNU Lesser General Public License version 3 or later"
+
+ -- Register in the World of Warcraft library "LibStub" if detected.
+ if LibStub then
+ local lib, minor = LibStub:GetLibrary(_MAJOR, true)
+ if lib and minor and minor >= _MINOR then -- No need to update.
+ return lib
+ else -- Update or first time register
+ LibDeflate = LibStub:NewLibrary(_MAJOR, _MINOR)
+ -- NOTE: It is important that new version has implemented
+ -- all exported APIs and tables in the old version,
+ -- so the old library is fully garbage collected,
+ -- and we 100% ensure the backward compatibility.
+ end
+ else -- "LibStub" is not detected.
+ LibDeflate = {}
+ end
+
+ LibDeflate._VERSION = _VERSION
+ LibDeflate._MAJOR = _MAJOR
+ LibDeflate._MINOR = _MINOR
+ LibDeflate._COPYRIGHT = _COPYRIGHT
+end
+
+-- localize Lua api for faster access.
+local assert = assert
+local error = error
+local pairs = pairs
+local string_byte = string.byte
+local string_char = string.char
+local string_find = string.find
+local string_gsub = string.gsub
+local string_sub = string.sub
+local table_concat = table.concat
+local table_sort = table.sort
+local tostring = tostring
+local type = type
+
+-- Converts i to 2^i, (0<=i<=32)
+-- This is used to implement bit left shift and bit right shift.
+-- "x >> y" in C: "(x-x%_pow2[y])/_pow2[y]" in Lua
+-- "x << y" in C: "x*_pow2[y]" in Lua
+local _pow2 = {}
+
+-- Converts any byte to a character, (0<=byte<=255)
+local _byte_to_char = {}
+
+-- _reverseBitsTbl[len][val] stores the bit reverse of
+-- the number with bit length "len" and value "val"
+-- For example, decimal number 6 with bits length 5 is binary 00110
+-- It's reverse is binary 01100,
+-- which is decimal 12 and 12 == _reverseBitsTbl[5][6]
+-- 1<=len<=9, 0<=val<=2^len-1
+-- The reason for 1<=len<=9 is that the max of min bitlen of huffman code
+-- of a huffman alphabet is 9?
+local _reverse_bits_tbl = {}
+
+-- Convert a LZ77 length (3<=len<=258) to
+-- a deflate literal/LZ77_length code (257<=code<=285)
+local _length_to_deflate_code = {}
+
+-- convert a LZ77 length (3<=len<=258) to
+-- a deflate literal/LZ77_length code extra bits.
+local _length_to_deflate_extra_bits = {}
+
+-- Convert a LZ77 length (3<=len<=258) to
+-- a deflate literal/LZ77_length code extra bit length.
+local _length_to_deflate_extra_bitlen = {}
+
+-- Convert a small LZ77 distance (1<=dist<=256) to a deflate code.
+local _dist256_to_deflate_code = {}
+
+-- Convert a small LZ77 distance (1<=dist<=256) to
+-- a deflate distance code extra bits.
+local _dist256_to_deflate_extra_bits = {}
+
+-- Convert a small LZ77 distance (1<=dist<=256) to
+-- a deflate distance code extra bit length.
+local _dist256_to_deflate_extra_bitlen = {}
+
+-- Convert a literal/LZ77_length deflate code to LZ77 base length
+-- The key of the table is (code - 256), 257<=code<=285
+local _literal_deflate_code_to_base_len = {
+ 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
+ 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258,
+}
+
+-- Convert a literal/LZ77_length deflate code to base LZ77 length extra bits
+-- The key of the table is (code - 256), 257<=code<=285
+local _literal_deflate_code_to_extra_bitlen = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,
+ 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0,
+}
+
+-- Convert a distance deflate code to base LZ77 distance. (0<=code<=29)
+local _dist_deflate_code_to_base_dist = {
+ [0] = 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
+ 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
+ 8193, 12289, 16385, 24577,
+}
+
+-- Convert a distance deflate code to LZ77 bits length. (0<=code<=29)
+local _dist_deflate_code_to_extra_bitlen = {
+ [0] = 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6,
+ 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13,
+}
+
+-- The code order of the first huffman header in the dynamic deflate block.
+-- See the page 12 of RFC1951
+local _rle_codes_huffman_bitlen_order = {16, 17, 18,
+ 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15,
+}
+
+-- The following tables are used by fixed deflate block.
+-- The value of these tables are assigned at the bottom of the source.
+
+-- The huffman code of the literal/LZ77_length deflate codes,
+-- in fixed deflate block.
+local _fix_block_literal_huffman_code
+
+-- Convert huffman code of the literal/LZ77_length to deflate codes,
+-- in fixed deflate block.
+local _fix_block_literal_huffman_to_deflate_code
+
+-- The bit length of the huffman code of literal/LZ77_length deflate codes,
+-- in fixed deflate block.
+local _fix_block_literal_huffman_bitlen
+
+-- The count of each bit length of the literal/LZ77_length deflate codes,
+-- in fixed deflate block.
+local _fix_block_literal_huffman_bitlen_count
+
+-- The huffman code of the distance deflate codes,
+-- in fixed deflate block.
+local _fix_block_dist_huffman_code
+
+-- Convert huffman code of the distance to deflate codes,
+-- in fixed deflate block.
+local _fix_block_dist_huffman_to_deflate_code
+
+-- The bit length of the huffman code of the distance deflate codes,
+-- in fixed deflate block.
+local _fix_block_dist_huffman_bitlen
+
+-- The count of each bit length of the huffman code of
+-- the distance deflate codes,
+-- in fixed deflate block.
+local _fix_block_dist_huffman_bitlen_count
+
+for i = 0, 255 do
+ _byte_to_char[i] = string_char(i)
+end
+
+do
+ local pow = 1
+ for i = 0, 32 do
+ _pow2[i] = pow
+ pow = pow * 2
+ end
+end
+
+for i = 1, 9 do
+ _reverse_bits_tbl[i] = {}
+ for j=0, _pow2[i+1]-1 do
+ local reverse = 0
+ local value = j
+ for _ = 1, i do
+ -- The following line is equivalent to "res | (code %2)" in C.
+ reverse = reverse - reverse%2
+ + (((reverse%2==1) or (value % 2) == 1) and 1 or 0)
+ value = (value-value%2)/2
+ reverse = reverse * 2
+ end
+ _reverse_bits_tbl[i][j] = (reverse-reverse%2)/2
+ end
+end
+
+-- The source code is written according to the pattern in the numbers
+-- in RFC1951 Page10.
+do
+ local a = 18
+ local b = 16
+ local c = 265
+ local bitlen = 1
+ for len = 3, 258 do
+ if len <= 10 then
+ _length_to_deflate_code[len] = len + 254
+ _length_to_deflate_extra_bitlen[len] = 0
+ elseif len == 258 then
+ _length_to_deflate_code[len] = 285
+ _length_to_deflate_extra_bitlen[len] = 0
+ else
+ if len > a then
+ a = a + b
+ b = b * 2
+ c = c + 4
+ bitlen = bitlen + 1
+ end
+ local t = len-a-1+b/2
+ _length_to_deflate_code[len] = (t-(t%(b/8)))/(b/8) + c
+ _length_to_deflate_extra_bitlen[len] = bitlen
+ _length_to_deflate_extra_bits[len] = t % (b/8)
+ end
+ end
+end
+
+-- The source code is written according to the pattern in the numbers
+-- in RFC1951 Page11.
+do
+ _dist256_to_deflate_code[1] = 0
+ _dist256_to_deflate_code[2] = 1
+ _dist256_to_deflate_extra_bitlen[1] = 0
+ _dist256_to_deflate_extra_bitlen[2] = 0
+
+ local a = 3
+ local b = 4
+ local code = 2
+ local bitlen = 0
+ for dist = 3, 256 do
+ if dist > b then
+ a = a * 2
+ b = b * 2
+ code = code + 2
+ bitlen = bitlen + 1
+ end
+ _dist256_to_deflate_code[dist] = (dist <= a) and code or (code+1)
+ _dist256_to_deflate_extra_bitlen[dist] = (bitlen < 0) and 0 or bitlen
+ if b >= 8 then
+ _dist256_to_deflate_extra_bits[dist] = (dist-b/2-1) % (b/4)
+ end
+ end
+end
+
+--- Calculate the Adler-32 checksum of the string.
+-- See RFC1950 Page 9 https://tools.ietf.org/html/rfc1950 for the
+-- definition of Adler-32 checksum.
+-- @param str [string] the input string to calcuate its Adler-32 checksum.
+-- @return [integer] The Adler-32 checksum, which is greater or equal to 0,
+-- and less than 2^32 (4294967296).
+function LibDeflate:Adler32(str)
+ -- This function is loop unrolled by better performance.
+ --
+ -- Here is the minimum code:
+ --
+ -- local a = 1
+ -- local b = 0
+ -- for i=1, #str do
+ -- local s = string.byte(str, i, i)
+ -- a = (a+s)%65521
+ -- b = (b+a)%65521
+ -- end
+ -- return b*65536+a
+ if type(str) ~= "string" then
+ error(("Usage: LibDeflate:Adler32(str):"
+ .." 'str' - string expected got '%s'."):format(type(str)), 2)
+ end
+ local strlen = #str
+
+ local i = 1
+ local a = 1
+ local b = 0
+ while i <= strlen - 15 do
+ local x1, x2, x3, x4, x5, x6, x7, x8,
+ x9, x10, x11, x12, x13, x14, x15, x16 = string_byte(str, i, i+15)
+ b = (b+16*a+16*x1+15*x2+14*x3+13*x4+12*x5+11*x6+10*x7+9*x8+8*x9
+ +7*x10+6*x11+5*x12+4*x13+3*x14+2*x15+x16)%65521
+ a = (a+x1+x2+x3+x4+x5+x6+x7+x8+x9+x10+x11+x12+x13+x14+x15+x16)%65521
+ i = i + 16
+ end
+ while (i <= strlen) do
+ local x = string_byte(str, i, i)
+ a = (a + x) % 65521
+ b = (b + a) % 65521
+ i = i + 1
+ end
+ return (b*65536+a) % 4294967296
+end
+
+-- Compare adler32 checksum.
+-- adler32 should be compared with a mod to avoid sign problem
+-- 4072834167 (unsigned) is the same adler32 as -222133129
+local function IsEqualAdler32(actual, expected)
+ return (actual % 4294967296) == (expected % 4294967296)
+end
+
+--- Create a preset dictionary.
+--
+-- This function is not fast, and the memory consumption of the produced
+-- dictionary is about 50 times of the input string. Therefore, it is suggestted
+-- to run this function only once in your program.
+--
+-- It is very important to know that if you do use a preset dictionary,
+-- compressors and decompressors MUST USE THE SAME dictionary. That is,
+-- dictionary must be created using the same string. If you update your program
+-- with a new dictionary, people with the old version won't be able to transmit
+-- data with people with the new version. Therefore, changing the dictionary
+-- must be very careful.
+--
+-- The parameters "strlen" and "adler32" add a layer of verification to ensure
+-- the parameter "str" is not modified unintentionally during the program
+-- development.
+--
+-- @usage local dict_str = "1234567890"
+--
+-- -- print(dict_str:len(), LibDeflate:Adler32(dict_str))
+-- -- Hardcode the print result below to verify it to avoid acciently
+-- -- modification of 'str' during the program development.
+-- -- string length: 10, Adler-32: 187433486,
+-- -- Don't calculate string length and its Adler-32 at run-time.
+--
+-- local dict = LibDeflate:CreateDictionary(dict_str, 10, 187433486)
+--
+-- @param str [string] The string used as the preset dictionary.
+-- You should put stuffs that frequently appears in the dictionary
+-- string and preferablely put more frequently appeared stuffs toward the end
+-- of the string.
+-- Empty string and string longer than 32768 bytes are not allowed.
+-- @param strlen [integer] The length of 'str'. Please pass in this parameter
+-- as a hardcoded constant, in order to verify the content of 'str'. The value
+-- of this parameter should be known before your program runs.
+-- @param adler32 [integer] The Adler-32 checksum of 'str'. Please pass in this
+-- parameter as a hardcoded constant, in order to verify the content of 'str'.
+-- The value of this parameter should be known before your program runs.
+-- @return [table] The dictionary used for preset dictionary compression and
+-- decompression.
+-- @raise error if 'strlen' does not match the length of 'str',
+-- or if 'adler32' does not match the Adler-32 checksum of 'str'.
+function LibDeflate:CreateDictionary(str, strlen, adler32)
+ if type(str) ~= "string" then
+ error(("Usage: LibDeflate:CreateDictionary(str, strlen, adler32):"
+ .." 'str' - string expected got '%s'."):format(type(str)), 2)
+ end
+ if type(strlen) ~= "number" then
+ error(("Usage: LibDeflate:CreateDictionary(str, strlen, adler32):"
+ .." 'strlen' - number expected got '%s'."):format(
+ type(strlen)), 2)
+ end
+ if type(adler32) ~= "number" then
+ error(("Usage: LibDeflate:CreateDictionary(str, strlen, adler32):"
+ .." 'adler32' - number expected got '%s'."):format(
+ type(adler32)), 2)
+ end
+ if strlen ~= #str then
+ error(("Usage: LibDeflate:CreateDictionary(str, strlen, adler32):"
+ .." 'strlen' does not match the actual length of 'str'."
+ .." 'strlen': %u, '#str': %u ."
+ .." Please check if 'str' is modified unintentionally.")
+ :format(strlen, #str))
+ end
+ if strlen == 0 then
+ error(("Usage: LibDeflate:CreateDictionary(str, strlen, adler32):"
+ .." 'str' - Empty string is not allowed."), 2)
+ end
+ if strlen > 32768 then
+ error(("Usage: LibDeflate:CreateDictionary(str, strlen, adler32):"
+ .." 'str' - string longer than 32768 bytes is not allowed."
+ .." Got %d bytes."):format(strlen), 2)
+ end
+ local actual_adler32 = self:Adler32(str)
+ if not IsEqualAdler32(adler32, actual_adler32) then
+ error(("Usage: LibDeflate:CreateDictionary(str, strlen, adler32):"
+ .." 'adler32' does not match the actual adler32 of 'str'."
+ .." 'adler32': %u, 'Adler32(str)': %u ."
+ .." Please check if 'str' is modified unintentionally.")
+ :format(adler32, actual_adler32))
+ end
+
+ local dictionary = {}
+ dictionary.adler32 = adler32
+ dictionary.hash_tables = {}
+ dictionary.string_table = {}
+ dictionary.strlen = strlen
+ local string_table = dictionary.string_table
+ local hash_tables = dictionary.hash_tables
+ string_table[1] = string_byte(str, 1, 1)
+ string_table[2] = string_byte(str, 2, 2)
+ if strlen >= 3 then
+ local i = 1
+ local hash = string_table[1]*256+string_table[2]
+ while i <= strlen - 2 - 3 do
+ local x1, x2, x3, x4 = string_byte(str, i+2, i+5)
+ string_table[i+2] = x1
+ string_table[i+3] = x2
+ string_table[i+4] = x3
+ string_table[i+5] = x4
+ hash = (hash*256+x1)%16777216
+ local t = hash_tables[hash]
+ if not t then t = {}; hash_tables[hash] = t end
+ t[#t+1] = i-strlen
+ i = i + 1
+ hash = (hash*256+x2)%16777216
+ t = hash_tables[hash]
+ if not t then t = {}; hash_tables[hash] = t end
+ t[#t+1] = i-strlen
+ i = i + 1
+ hash = (hash*256+x3)%16777216
+ t = hash_tables[hash]
+ if not t then t = {}; hash_tables[hash] = t end
+ t[#t+1] = i-strlen
+ i = i + 1
+ hash = (hash*256+x4)%16777216
+ t = hash_tables[hash]
+ if not t then t = {}; hash_tables[hash] = t end
+ t[#t+1] = i-strlen
+ i = i + 1
+ end
+ while i <= strlen - 2 do
+ local x = string_byte(str, i+2)
+ string_table[i+2] = x
+ hash = (hash*256+x)%16777216
+ local t = hash_tables[hash]
+ if not t then t = {}; hash_tables[hash] = t end
+ t[#t+1] = i-strlen
+ i = i + 1
+ end
+ end
+ return dictionary
+end
+
+-- Check if the dictionary is valid.
+-- @param dictionary The preset dictionary for compression and decompression.
+-- @return true if valid, false if not valid.
+-- @return if not valid, the error message.
+local function IsValidDictionary(dictionary)
+ if type(dictionary) ~= "table" then
+ return false, ("'dictionary' - table expected got '%s'.")
+ :format(type(dictionary))
+ end
+ if type(dictionary.adler32) ~= "number"
+ or type(dictionary.string_table) ~= "table"
+ or type(dictionary.strlen) ~= "number"
+ or dictionary.strlen <= 0
+ or dictionary.strlen > 32768
+ or dictionary.strlen ~= #dictionary.string_table
+ or type(dictionary.hash_tables) ~= "table"
+ then
+ return false, ("'dictionary' - corrupted dictionary.")
+ :format(type(dictionary))
+ end
+ return true, ""
+end
+
+--[[
+ key of the configuration table is the compression level,
+ and its value stores the compression setting.
+ These numbers come from zlib source code.
+
+ Higher compression level usually means better compression.
+ (Because LibDeflate uses a simplified version of zlib algorithm,
+ there is no guarantee that higher compression level does not create
+ bigger file than lower level, but I can say it's 99% likely)
+
+ Be careful with the high compression level. This is a pure lua
+ implementation compressor/decompressor, which is significant slower than
+ a C/C++ equivalant compressor/decompressor. Very high compression level
+ costs significant more CPU time, and usually compression size won't be
+ significant smaller when you increase compression level by 1, when the
+ level is already very high. Benchmark yourself if you can afford it.
+
+ See also https://github.com/madler/zlib/blob/master/doc/algorithm.txt,
+ https://github.com/madler/zlib/blob/master/deflate.c for more information.
+
+ The meaning of each field:
+ @field 1 use_lazy_evaluation:
+ true/false. Whether the program uses lazy evaluation.
+ See what is "lazy evaluation" in the link above.
+ lazy_evaluation improves ratio, but relatively slow.
+ @field 2 good_prev_length:
+ Only effective if lazy is set, Only use 1/4 of max_chain,
+ if prev length of lazy match is above this.
+ @field 3 max_insert_length/max_lazy_match:
+ If not using lazy evaluation,
+ insert new strings in the hash table only if the match length is not
+ greater than this length.
+ If using lazy evaluation, only continue lazy evaluation,
+ if previous match length is strictly smaller than this value.
+ @field 4 nice_length:
+ Number. Don't continue to go down the hash chain,
+ if match length is above this.
+ @field 5 max_chain:
+ Number. The maximum number of hash chains we look.
+--]]
+local _compression_level_configs = {
+ [0] = {false, nil, 0, 0, 0}, -- level 0, no compression
+ [1] = {false, nil, 4, 8, 4}, -- level 1, similar to zlib level 1
+ [2] = {false, nil, 5, 18, 8}, -- level 2, similar to zlib level 2
+ [3] = {false, nil, 6, 32, 32}, -- level 3, similar to zlib level 3
+ [4] = {true, 4, 4, 16, 16}, -- level 4, similar to zlib level 4
+ [5] = {true, 8, 16, 32, 32}, -- level 5, similar to zlib level 5
+ [6] = {true, 8, 16, 128, 128}, -- level 6, similar to zlib level 6
+ [7] = {true, 8, 32, 128, 256}, -- (SLOW) level 7, similar to zlib level 7
+ [8] = {true, 32, 128, 258, 1024} , --(SLOW) level 8,similar to zlib level 8
+ [9] = {true, 32, 258, 258, 4096},
+ -- (VERY SLOW) level 9, similar to zlib level 9
+}
+
+-- Check if the compression/decompression arguments is valid
+-- @param str The input string.
+-- @param check_dictionary if true, check if dictionary is valid.
+-- @param dictionary The preset dictionary for compression and decompression.
+-- @param check_configs if true, check if config is valid.
+-- @param configs The compression configuration table
+-- @return true if valid, false if not valid.
+-- @return if not valid, the error message.
+local function IsValidArguments(str,
+ check_dictionary, dictionary,
+ check_configs, configs)
+
+ if type(str) ~= "string" then
+ return false,
+ ("'str' - string expected got '%s'."):format(type(str))
+ end
+ if check_dictionary then
+ local dict_valid, dict_err = IsValidDictionary(dictionary)
+ if not dict_valid then
+ return false, dict_err
+ end
+ end
+ if check_configs then
+ local type_configs = type(configs)
+ if type_configs ~= "nil" and type_configs ~= "table" then
+ return false,
+ ("'configs' - nil or table expected got '%s'.")
+ :format(type(configs))
+ end
+ if type_configs == "table" then
+ for k, v in pairs(configs) do
+ if k ~= "level" and k ~= "strategy" then
+ return false,
+ ("'configs' - unsupported table key in the configs: '%s'.")
+ :format(k)
+ elseif k == "level" and not _compression_level_configs[v] then
+ return false,
+ ("'configs' - unsupported 'level': %s."):format(tostring(v))
+ elseif k == "strategy" and v ~= "fixed" and v ~= "huffman_only"
+ and v ~= "dynamic" then
+ -- random_block_type is for testing purpose
+ return false, ("'configs' - unsupported 'strategy': '%s'.")
+ :format(tostring(v))
+ end
+ end
+ end
+ end
+ return true, ""
+end
+
+
+
+--[[ --------------------------------------------------------------------------
+ Compress code
+--]] --------------------------------------------------------------------------
+
+-- partial flush to save memory
+local _FLUSH_MODE_MEMORY_CLEANUP = 0
+-- full flush with partial bytes
+local _FLUSH_MODE_OUTPUT = 1
+-- write bytes to get to byte boundary
+local _FLUSH_MODE_BYTE_BOUNDARY = 2
+-- no flush, just get num of bits written so far
+local _FLUSH_MODE_NO_FLUSH = 3
+
+--[[
+ Create an empty writer to easily write stuffs as the unit of bits.
+ Return values:
+ 1. WriteBits(code, bitlen):
+ 2. WriteString(str):
+ 3. Flush(mode):
+--]]
+local function CreateWriter()
+ local buffer_size = 0
+ local cache = 0
+ local cache_bitlen = 0
+ local total_bitlen = 0
+ local buffer = {}
+ -- When buffer is big enough, flush into result_buffer to save memory.
+ local result_buffer = {}
+
+ -- Write bits with value "value" and bit length of "bitlen" into writer.
+ -- @param value: The value being written
+ -- @param bitlen: The bit length of "value"
+ -- @return nil
+ local function WriteBits(value, bitlen)
+ cache = cache + value * _pow2[cache_bitlen]
+ cache_bitlen = cache_bitlen + bitlen
+ total_bitlen = total_bitlen + bitlen
+ -- Only bulk to buffer every 4 bytes. This is quicker.
+ if cache_bitlen >= 32 then
+ buffer_size = buffer_size + 1
+ buffer[buffer_size] =
+ _byte_to_char[cache % 256]
+ .._byte_to_char[((cache-cache%256)/256 % 256)]
+ .._byte_to_char[((cache-cache%65536)/65536 % 256)]
+ .._byte_to_char[((cache-cache%16777216)/16777216 % 256)]
+ local rshift_mask = _pow2[32 - cache_bitlen + bitlen]
+ cache = (value - value%rshift_mask)/rshift_mask
+ cache_bitlen = cache_bitlen - 32
+ end
+ end
+
+ -- Write the entire string into the writer.
+ -- @param str The string being written
+ -- @return nil
+ local function WriteString(str)
+ for _ = 1, cache_bitlen, 8 do
+ buffer_size = buffer_size + 1
+ buffer[buffer_size] = string_char(cache % 256)
+ cache = (cache-cache%256)/256
+ end
+ cache_bitlen = 0
+ buffer_size = buffer_size + 1
+ buffer[buffer_size] = str
+ total_bitlen = total_bitlen + #str*8
+ end
+
+ -- Flush current stuffs in the writer and return it.
+ -- This operation will free most of the memory.
+ -- @param mode See the descrtion of the constant and the source code.
+ -- @return The total number of bits stored in the writer right now.
+ -- for byte boundary mode, it includes the padding bits.
+ -- for output mode, it does not include padding bits.
+ -- @return Return the outputs if mode is output.
+ local function FlushWriter(mode)
+ if mode == _FLUSH_MODE_NO_FLUSH then
+ return total_bitlen
+ end
+
+ if mode == _FLUSH_MODE_OUTPUT
+ or mode == _FLUSH_MODE_BYTE_BOUNDARY then
+ -- Full flush, also output cache.
+ -- Need to pad some bits if cache_bitlen is not multiple of 8.
+ local padding_bitlen = (8 - cache_bitlen % 8) % 8
+
+ if cache_bitlen > 0 then
+ -- padding with all 1 bits, mainly because "\000" is not
+ -- good to be tranmitted. I do this so "\000" is a little bit
+ -- less frequent.
+ cache = cache - _pow2[cache_bitlen]
+ + _pow2[cache_bitlen+padding_bitlen]
+ for _ = 1, cache_bitlen, 8 do
+ buffer_size = buffer_size + 1
+ buffer[buffer_size] = _byte_to_char[cache % 256]
+ cache = (cache-cache%256)/256
+ end
+
+ cache = 0
+ cache_bitlen = 0
+ end
+ if mode == _FLUSH_MODE_BYTE_BOUNDARY then
+ total_bitlen = total_bitlen + padding_bitlen
+ return total_bitlen
+ end
+ end
+
+ local flushed = table_concat(buffer)
+ buffer = {}
+ buffer_size = 0
+ result_buffer[#result_buffer+1] = flushed
+
+ if mode == _FLUSH_MODE_MEMORY_CLEANUP then
+ return total_bitlen
+ else
+ return total_bitlen, table_concat(result_buffer)
+ end
+ end
+
+ return WriteBits, WriteString, FlushWriter
+end
+
+-- Push an element into a max heap
+-- @param heap A max heap whose max element is at index 1.
+-- @param e The element to be pushed. Assume element "e" is a table
+-- and comparison is done via its first entry e[1]
+-- @param heap_size current number of elements in the heap.
+-- NOTE: There may be some garbage stored in
+-- heap[heap_size+1], heap[heap_size+2], etc..
+-- @return nil
+local function MinHeapPush(heap, e, heap_size)
+ heap_size = heap_size + 1
+ heap[heap_size] = e
+ local value = e[1]
+ local pos = heap_size
+ local parent_pos = (pos-pos%2)/2
+
+ while (parent_pos >= 1 and heap[parent_pos][1] > value) do
+ local t = heap[parent_pos]
+ heap[parent_pos] = e
+ heap[pos] = t
+ pos = parent_pos
+ parent_pos = (parent_pos-parent_pos%2)/2
+ end
+end
+
+-- Pop an element from a max heap
+-- @param heap A max heap whose max element is at index 1.
+-- @param heap_size current number of elements in the heap.
+-- @return the poped element
+-- Note: This function does not change table size of "heap" to save CPU time.
+local function MinHeapPop(heap, heap_size)
+ local top = heap[1]
+ local e = heap[heap_size]
+ local value = e[1]
+ heap[1] = e
+ heap[heap_size] = top
+ heap_size = heap_size - 1
+
+ local pos = 1
+ local left_child_pos = pos * 2
+ local right_child_pos = left_child_pos + 1
+
+ while (left_child_pos <= heap_size) do
+ local left_child = heap[left_child_pos]
+ if (right_child_pos <= heap_size
+ and heap[right_child_pos][1] < left_child[1]) then
+ local right_child = heap[right_child_pos]
+ if right_child[1] < value then
+ heap[right_child_pos] = e
+ heap[pos] = right_child
+ pos = right_child_pos
+ left_child_pos = pos * 2
+ right_child_pos = left_child_pos + 1
+ else
+ break
+ end
+ else
+ if left_child[1] < value then
+ heap[left_child_pos] = e
+ heap[pos] = left_child
+ pos = left_child_pos
+ left_child_pos = pos * 2
+ right_child_pos = left_child_pos + 1
+ else
+ break
+ end
+ end
+ end
+
+ return top
+end
+
+-- Deflate defines a special huffman tree, which is unique once the bit length
+-- of huffman code of all symbols are known.
+-- @param bitlen_count Number of symbols with a specific bitlen
+-- @param symbol_bitlen The bit length of a symbol
+-- @param max_symbol The max symbol among all symbols,
+-- which is (number of symbols - 1)
+-- @param max_bitlen The max huffman bit length among all symbols.
+-- @return The huffman code of all symbols.
+local function GetHuffmanCodeFromBitlen(bitlen_counts, symbol_bitlens
+ , max_symbol, max_bitlen)
+ local huffman_code = 0
+ local next_codes = {}
+ local symbol_huffman_codes = {}
+ for bitlen = 1, max_bitlen do
+ huffman_code = (huffman_code+(bitlen_counts[bitlen-1] or 0))*2
+ next_codes[bitlen] = huffman_code
+ end
+ for symbol = 0, max_symbol do
+ local bitlen = symbol_bitlens[symbol]
+ if bitlen then
+ huffman_code = next_codes[bitlen]
+ next_codes[bitlen] = huffman_code + 1
+
+ -- Reverse the bits of huffman code,
+ -- because most signifant bits of huffman code
+ -- is stored first into the compressed data.
+ -- @see RFC1951 Page5 Section 3.1.1
+ if bitlen <= 9 then -- Have cached reverse for small bitlen.
+ symbol_huffman_codes[symbol] =
+ _reverse_bits_tbl[bitlen][huffman_code]
+ else
+ local reverse = 0
+ for _ = 1, bitlen do
+ reverse = reverse - reverse%2
+ + (((reverse%2==1)
+ or (huffman_code % 2) == 1) and 1 or 0)
+ huffman_code = (huffman_code-huffman_code%2)/2
+ reverse = reverse*2
+ end
+ symbol_huffman_codes[symbol] = (reverse-reverse%2)/2
+ end
+ end
+ end
+ return symbol_huffman_codes
+end
+
+-- A helper function to sort heap elements
+-- a[1], b[1] is the huffman frequency
+-- a[2], b[2] is the symbol value.
+local function SortByFirstThenSecond(a, b)
+ return a[1] < b[1] or
+ (a[1] == b[1] and a[2] < b[2])
+end
+
+-- Calculate the huffman bit length and huffman code.
+-- @param symbol_count: A table whose table key is the symbol, and table value
+-- is the symbol frenquency (nil means 0 frequency).
+-- @param max_bitlen: See description of return value.
+-- @param max_symbol: The maximum symbol
+-- @return a table whose key is the symbol, and the value is the huffman bit
+-- bit length. We guarantee that all bit length <= max_bitlen.
+-- For 0<=symbol<=max_symbol, table value could be nil if the frequency
+-- of the symbol is 0 or nil.
+-- @return a table whose key is the symbol, and the value is the huffman code.
+-- @return a number indicating the maximum symbol whose bitlen is not 0.
+local function GetHuffmanBitlenAndCode(symbol_counts, max_bitlen, max_symbol)
+ local heap_size
+ local max_non_zero_bitlen_symbol = -1
+ local leafs = {}
+ local heap = {}
+ local symbol_bitlens = {}
+ local symbol_codes = {}
+ local bitlen_counts = {}
+
+ --[[
+ tree[1]: weight, temporarily used as parent and bitLengths
+ tree[2]: symbol
+ tree[3]: left child
+ tree[4]: right child
+ --]]
+ local number_unique_symbols = 0
+ for symbol, count in pairs(symbol_counts) do
+ number_unique_symbols = number_unique_symbols + 1
+ leafs[number_unique_symbols] = {count, symbol}
+ end
+
+ if (number_unique_symbols == 0) then
+ -- no code.
+ return {}, {}, -1
+ elseif (number_unique_symbols == 1) then
+ -- Only one code. In this case, its huffman code
+ -- needs to be assigned as 0, and bit length is 1.
+ -- This is the only case that the return result
+ -- represents an imcomplete huffman tree.
+ local symbol = leafs[1][2]
+ symbol_bitlens[symbol] = 1
+ symbol_codes[symbol] = 0
+ return symbol_bitlens, symbol_codes, symbol
+ else
+ table_sort(leafs, SortByFirstThenSecond)
+ heap_size = number_unique_symbols
+ for i = 1, heap_size do
+ heap[i] = leafs[i]
+ end
+
+ while (heap_size > 1) do
+ -- Note: pop does not change table size of heap
+ local leftChild = MinHeapPop(heap, heap_size)
+ heap_size = heap_size - 1
+ local rightChild = MinHeapPop(heap, heap_size)
+ heap_size = heap_size - 1
+ local newNode =
+ {leftChild[1]+rightChild[1], -1, leftChild, rightChild}
+ MinHeapPush(heap, newNode, heap_size)
+ heap_size = heap_size + 1
+ end
+
+ -- Number of leafs whose bit length is greater than max_len.
+ local number_bitlen_overflow = 0
+
+ -- Calculate bit length of all nodes
+ local fifo = {heap[1], 0, 0, 0} -- preallocate some spaces.
+ local fifo_size = 1
+ local index = 1
+ heap[1][1] = 0
+ while (index <= fifo_size) do -- Breath first search
+ local e = fifo[index]
+ local bitlen = e[1]
+ local symbol = e[2]
+ local left_child = e[3]
+ local right_child = e[4]
+ if left_child then
+ fifo_size = fifo_size + 1
+ fifo[fifo_size] = left_child
+ left_child[1] = bitlen + 1
+ end
+ if right_child then
+ fifo_size = fifo_size + 1
+ fifo[fifo_size] = right_child
+ right_child[1] = bitlen + 1
+ end
+ index = index + 1
+
+ if (bitlen > max_bitlen) then
+ number_bitlen_overflow = number_bitlen_overflow + 1
+ bitlen = max_bitlen
+ end
+ if symbol >= 0 then
+ symbol_bitlens[symbol] = bitlen
+ max_non_zero_bitlen_symbol =
+ (symbol > max_non_zero_bitlen_symbol)
+ and symbol or max_non_zero_bitlen_symbol
+ bitlen_counts[bitlen] = (bitlen_counts[bitlen] or 0) + 1
+ end
+ end
+
+ -- Resolve bit length overflow
+ -- @see ZLib/trees.c:gen_bitlen(s, desc), for reference
+ if (number_bitlen_overflow > 0) then
+ repeat
+ local bitlen = max_bitlen - 1
+ while ((bitlen_counts[bitlen] or 0) == 0) do
+ bitlen = bitlen - 1
+ end
+ -- move one leaf down the tree
+ bitlen_counts[bitlen] = bitlen_counts[bitlen] - 1
+ -- move one overflow item as its brother
+ bitlen_counts[bitlen+1] = (bitlen_counts[bitlen+1] or 0) + 2
+ bitlen_counts[max_bitlen] = bitlen_counts[max_bitlen] - 1
+ number_bitlen_overflow = number_bitlen_overflow - 2
+ until (number_bitlen_overflow <= 0)
+
+ index = 1
+ for bitlen = max_bitlen, 1, -1 do
+ local n = bitlen_counts[bitlen] or 0
+ while (n > 0) do
+ local symbol = leafs[index][2]
+ symbol_bitlens[symbol] = bitlen
+ n = n - 1
+ index = index + 1
+ end
+ end
+ end
+
+ symbol_codes = GetHuffmanCodeFromBitlen(bitlen_counts, symbol_bitlens,
+ max_symbol, max_bitlen)
+ return symbol_bitlens, symbol_codes, max_non_zero_bitlen_symbol
+ end
+end
+
+-- Calculate the first huffman header in the dynamic huffman block
+-- @see RFC1951 Page 12
+-- @param lcode_bitlen: The huffman bit length of literal/LZ77_length.
+-- @param max_non_zero_bitlen_lcode: The maximum literal/LZ77_length symbol
+-- whose huffman bit length is not zero.
+-- @param dcode_bitlen: The huffman bit length of LZ77 distance.
+-- @param max_non_zero_bitlen_dcode: The maximum LZ77 distance symbol
+-- whose huffman bit length is not zero.
+-- @return The run length encoded codes.
+-- @return The extra bits. One entry for each rle code that needs extra bits.
+-- (code == 16 or 17 or 18).
+-- @return The count of appearance of each rle codes.
+local function RunLengthEncodeHuffmanBitlen(
+ lcode_bitlens,
+ max_non_zero_bitlen_lcode,
+ dcode_bitlens,
+ max_non_zero_bitlen_dcode)
+ local rle_code_tblsize = 0
+ local rle_codes = {}
+ local rle_code_counts = {}
+ local rle_extra_bits_tblsize = 0
+ local rle_extra_bits = {}
+ local prev = nil
+ local count = 0
+
+ -- If there is no distance code, assume one distance code of bit length 0.
+ -- RFC1951: One distance code of zero bits means that
+ -- there are no distance codes used at all (the data is all literals).
+ max_non_zero_bitlen_dcode = (max_non_zero_bitlen_dcode < 0)
+ and 0 or max_non_zero_bitlen_dcode
+ local max_code = max_non_zero_bitlen_lcode+max_non_zero_bitlen_dcode+1
+
+ for code = 0, max_code+1 do
+ local len = (code <= max_non_zero_bitlen_lcode)
+ and (lcode_bitlens[code] or 0)
+ or ((code <= max_code)
+ and (dcode_bitlens[code-max_non_zero_bitlen_lcode-1] or 0) or nil)
+ if len == prev then
+ count = count + 1
+ if len ~= 0 and count == 6 then
+ rle_code_tblsize = rle_code_tblsize + 1
+ rle_codes[rle_code_tblsize] = 16
+ rle_extra_bits_tblsize = rle_extra_bits_tblsize + 1
+ rle_extra_bits[rle_extra_bits_tblsize] = 3
+ rle_code_counts[16] = (rle_code_counts[16] or 0) + 1
+ count = 0
+ elseif len == 0 and count == 138 then
+ rle_code_tblsize = rle_code_tblsize + 1
+ rle_codes[rle_code_tblsize] = 18
+ rle_extra_bits_tblsize = rle_extra_bits_tblsize + 1
+ rle_extra_bits[rle_extra_bits_tblsize] = 127
+ rle_code_counts[18] = (rle_code_counts[18] or 0) + 1
+ count = 0
+ end
+ else
+ if count == 1 then
+ rle_code_tblsize = rle_code_tblsize + 1
+ rle_codes[rle_code_tblsize] = prev
+ rle_code_counts[prev] = (rle_code_counts[prev] or 0) + 1
+ elseif count == 2 then
+ rle_code_tblsize = rle_code_tblsize + 1
+ rle_codes[rle_code_tblsize] = prev
+ rle_code_tblsize = rle_code_tblsize + 1
+ rle_codes[rle_code_tblsize] = prev
+ rle_code_counts[prev] = (rle_code_counts[prev] or 0) + 2
+ elseif count >= 3 then
+ rle_code_tblsize = rle_code_tblsize + 1
+ local rleCode = (prev ~= 0) and 16 or (count <= 10 and 17 or 18)
+ rle_codes[rle_code_tblsize] = rleCode
+ rle_code_counts[rleCode] = (rle_code_counts[rleCode] or 0) + 1
+ rle_extra_bits_tblsize = rle_extra_bits_tblsize + 1
+ rle_extra_bits[rle_extra_bits_tblsize] =
+ (count <= 10) and (count - 3) or (count - 11)
+ end
+
+ prev = len
+ if len and len ~= 0 then
+ rle_code_tblsize = rle_code_tblsize + 1
+ rle_codes[rle_code_tblsize] = len
+ rle_code_counts[len] = (rle_code_counts[len] or 0) + 1
+ count = 0
+ else
+ count = 1
+ end
+ end
+ end
+
+ return rle_codes, rle_extra_bits, rle_code_counts
+end
+
+-- Load the string into a table, in order to speed up LZ77.
+-- Loop unrolled 16 times to speed this function up.
+-- @param str The string to be loaded.
+-- @param t The load destination
+-- @param start str[index] will be the first character to be loaded.
+-- @param end str[index] will be the last character to be loaded
+-- @param offset str[index] will be loaded into t[index-offset]
+-- @return t
+local function LoadStringToTable(str, t, start, stop, offset)
+ local i = start - offset
+ while i <= stop - 15 - offset do
+ t[i], t[i+1], t[i+2], t[i+3], t[i+4], t[i+5], t[i+6], t[i+7], t[i+8],
+ t[i+9], t[i+10], t[i+11], t[i+12], t[i+13], t[i+14], t[i+15] =
+ string_byte(str, i + offset, i + 15 + offset)
+ i = i + 16
+ end
+ while (i <= stop - offset) do
+ t[i] = string_byte(str, i + offset, i + offset)
+ i = i + 1
+ end
+ return t
+end
+
+-- Do LZ77 process. This function uses the majority of the CPU time.
+-- @see zlib/deflate.c:deflate_fast(), zlib/deflate.c:deflate_slow()
+-- @see https://github.com/madler/zlib/blob/master/doc/algorithm.txt
+-- This function uses the algorithms used above. You should read the
+-- algorithm.txt above to understand what is the hash function and the
+-- lazy evaluation.
+--
+-- The special optimization used here is hash functions used here.
+-- The hash function is just the multiplication of the three consective
+-- characters. So if the hash matches, it guarantees 3 characters are matched.
+-- This optimization can be implemented because Lua table is a hash table.
+--
+-- @param level integer that describes compression level.
+-- @param string_table table that stores the value of string to be compressed.
+-- The index of this table starts from 1.
+-- The caller needs to make sure all values needed by this function
+-- are loaded.
+-- Assume "str" is the origin input string into the compressor
+-- str[block_start]..str[block_end+3] needs to be loaded into
+-- string_table[block_start-offset]..string_table[block_end-offset]
+-- If dictionary is presented, the last 258 bytes of the dictionary
+-- needs to be loaded into sing_table[-257..0]
+-- (See more in the description of offset.)
+-- @param hash_tables. The table key is the hash value (0<=hash<=16777216=256^3)
+-- The table value is an array0 that stores the indexes of the
+-- input data string to be compressed, such that
+-- hash == str[index]*str[index+1]*str[index+2]
+-- Indexes are ordered in this array.
+-- @param block_start The indexes of the input data string to be compressed.
+-- that starts the LZ77 block.
+-- @param block_end The indexes of the input data string to be compressed.
+-- that stores the LZ77 block.
+-- @param offset str[index] is stored in string_table[index-offset],
+-- This offset is mainly an optimization to limit the index
+-- of string_table, so lua can access this table quicker.
+-- @param dictionary See LibDeflate:CreateDictionary
+-- @return literal/LZ77_length deflate codes.
+-- @return the extra bits of literal/LZ77_length deflate codes.
+-- @return the count of each literal/LZ77 deflate code.
+-- @return LZ77 distance deflate codes.
+-- @return the extra bits of LZ77 distance deflate codes.
+-- @return the count of each LZ77 distance deflate code.
+local function GetBlockLZ77Result(level, string_table, hash_tables, block_start,
+ block_end, offset, dictionary)
+ local config = _compression_level_configs[level]
+ local config_use_lazy
+ , config_good_prev_length
+ , config_max_lazy_match
+ , config_nice_length
+ , config_max_hash_chain =
+ config[1], config[2], config[3], config[4], config[5]
+
+ local config_max_insert_length = (not config_use_lazy)
+ and config_max_lazy_match or 2147483646
+ local config_good_hash_chain =
+ (config_max_hash_chain-config_max_hash_chain%4/4)
+
+ local hash
+
+ local dict_hash_tables
+ local dict_string_table
+ local dict_string_len = 0
+
+ if dictionary then
+ dict_hash_tables = dictionary.hash_tables
+ dict_string_table = dictionary.string_table
+ dict_string_len = dictionary.strlen
+ assert(block_start == 1)
+ if block_end >= block_start and dict_string_len >= 2 then
+ hash = dict_string_table[dict_string_len-1]*65536
+ + dict_string_table[dict_string_len]*256 + string_table[1]
+ local t = hash_tables[hash]
+ if not t then t = {}; hash_tables[hash] = t end
+ t[#t+1] = -1
+ end
+ if block_end >= block_start+1 and dict_string_len >= 1 then
+ hash = dict_string_table[dict_string_len]*65536
+ + string_table[1]*256 + string_table[2]
+ local t = hash_tables[hash]
+ if not t then t = {}; hash_tables[hash] = t end
+ t[#t+1] = 0
+ end
+ end
+
+ hash = (string_table[block_start-offset] or 0)*256
+ + (string_table[block_start+1-offset] or 0)
+
+ local lcodes = {}
+ local lcode_tblsize = 0
+ local lcodes_counts = {}
+ local dcodes = {}
+ local dcodes_tblsize = 0
+ local dcodes_counts = {}
+
+ local lextra_bits = {}
+ local lextra_bits_tblsize = 0
+ local dextra_bits = {}
+ local dextra_bits_tblsize = 0
+
+ local match_available = false
+ local prev_len
+ local prev_dist
+ local cur_len = 0
+ local cur_dist = 0
+
+ local index = block_start
+ local index_end = block_end + (config_use_lazy and 1 or 0)
+
+ -- the zlib source code writes separate code for lazy evaluation and
+ -- not lazy evaluation, which is easier to understand.
+ -- I put them together, so it is a bit harder to understand.
+ -- because I think this is easier for me to maintain it.
+ while (index <= index_end) do
+ local string_table_index = index - offset
+ prev_len = cur_len
+ prev_dist = cur_dist
+ cur_len = 0
+
+ hash = (hash*256+(string_table[string_table_index+2] or 0))%16777216
+
+ local chain_index
+ local cur_chain
+ local hash_chain = hash_tables[hash]
+ local chain_old_size
+ if not hash_chain then
+ chain_old_size = 0
+ hash_chain = {}
+ hash_tables[hash] = hash_chain
+ if dict_hash_tables then
+ cur_chain = dict_hash_tables[hash]
+ chain_index = cur_chain and #cur_chain or 0
+ else
+ chain_index = 0
+ end
+ else
+ chain_old_size = #hash_chain
+ cur_chain = hash_chain
+ chain_index = chain_old_size
+ end
+
+ if index <= block_end then
+ hash_chain[chain_old_size+1] = index
+ end
+
+ if (chain_index > 0 and index + 2 <= block_end
+ and (not config_use_lazy or prev_len < config_max_lazy_match)) then
+
+ local depth =
+ (config_use_lazy and prev_len >= config_good_prev_length)
+ and config_good_hash_chain or config_max_hash_chain
+
+ while chain_index >= 1 and depth > 0 do
+ local prev = cur_chain[chain_index]
+
+ if index - prev > 32768 then
+ break
+ end
+ if prev < index then
+ local j = 3
+
+ if prev >= -257 then
+ local prev_table_index = prev-offset
+ -- NOTE for author:
+ -- j < 258 and index + j <= block_end
+ -- This is the right condition
+ while (j < 258 and index + j <= block_end) do
+ if (string_table[prev_table_index+j]
+ == string_table[string_table_index+j]) then
+ j = j + 1
+ else
+ break
+ end
+ end
+ else
+ local prev_table_index = dict_string_len+prev
+ -- NOTE for author:
+ -- j < 258 and index + j <= block_end
+ -- This is the right condition
+ while (j < 258 and index + j <= block_end) do
+ if (dict_string_table[prev_table_index+j]
+ == string_table[string_table_index+j]) then
+ j = j + 1
+ else
+ break
+ end
+ end
+ end
+ if j > cur_len then
+ cur_len = j
+ cur_dist = index - prev
+ end
+ if cur_len >= config_nice_length then
+ break
+ end
+ end
+
+ chain_index = chain_index - 1
+ depth = depth - 1
+ if chain_index == 0 and prev > 0 and dict_hash_tables then
+ cur_chain = dict_hash_tables[hash]
+ chain_index = cur_chain and #cur_chain or 0
+ end
+ end
+ end
+
+ if not config_use_lazy then
+ prev_len, prev_dist = cur_len, cur_dist
+ end
+ if ((not config_use_lazy or match_available)
+ and (prev_len > 3 or (prev_len == 3 and prev_dist < 4096))
+ and cur_len <= prev_len )then
+ local code = _length_to_deflate_code[prev_len]
+ local length_extra_bits_bitlen =
+ _length_to_deflate_extra_bitlen[prev_len]
+ local dist_code, dist_extra_bits_bitlen, dist_extra_bits
+ if prev_dist <= 256 then -- have cached code for small distance.
+ dist_code = _dist256_to_deflate_code[prev_dist]
+ dist_extra_bits = _dist256_to_deflate_extra_bits[prev_dist]
+ dist_extra_bits_bitlen =
+ _dist256_to_deflate_extra_bitlen[prev_dist]
+ else
+ dist_code = 16
+ dist_extra_bits_bitlen = 7
+ local a = 384
+ local b = 512
+
+ while true do
+ if prev_dist <= a then
+ dist_extra_bits = (prev_dist-(b/2)-1) % (b/4)
+ break
+ elseif prev_dist <= b then
+ dist_extra_bits = (prev_dist-(b/2)-1) % (b/4)
+ dist_code = dist_code + 1
+ break
+ else
+ dist_code = dist_code + 2
+ dist_extra_bits_bitlen = dist_extra_bits_bitlen + 1
+ a = a*2
+ b = b*2
+ end
+ end
+ end
+ lcode_tblsize = lcode_tblsize + 1
+ lcodes[lcode_tblsize] = code
+ lcodes_counts[code] = (lcodes_counts[code] or 0) + 1
+
+ dcodes_tblsize = dcodes_tblsize + 1
+ dcodes[dcodes_tblsize] = dist_code
+ dcodes_counts[dist_code] = (dcodes_counts[dist_code] or 0) + 1
+
+ if length_extra_bits_bitlen > 0 then
+ local lenExtraBits = _length_to_deflate_extra_bits[prev_len]
+ lextra_bits_tblsize = lextra_bits_tblsize + 1
+ lextra_bits[lextra_bits_tblsize] = lenExtraBits
+ end
+ if dist_extra_bits_bitlen > 0 then
+ dextra_bits_tblsize = dextra_bits_tblsize + 1
+ dextra_bits[dextra_bits_tblsize] = dist_extra_bits
+ end
+
+ for i=index+1, index+prev_len-(config_use_lazy and 2 or 1) do
+ hash = (hash*256+(string_table[i-offset+2] or 0))%16777216
+ if prev_len <= config_max_insert_length then
+ hash_chain = hash_tables[hash]
+ if not hash_chain then
+ hash_chain = {}
+ hash_tables[hash] = hash_chain
+ end
+ hash_chain[#hash_chain+1] = i
+ end
+ end
+ index = index + prev_len - (config_use_lazy and 1 or 0)
+ match_available = false
+ elseif (not config_use_lazy) or match_available then
+ local code = string_table[config_use_lazy
+ and (string_table_index-1) or string_table_index]
+ lcode_tblsize = lcode_tblsize + 1
+ lcodes[lcode_tblsize] = code
+ lcodes_counts[code] = (lcodes_counts[code] or 0) + 1
+ index = index + 1
+ else
+ match_available = true
+ index = index + 1
+ end
+ end
+
+ -- Write "end of block" symbol
+ lcode_tblsize = lcode_tblsize + 1
+ lcodes[lcode_tblsize] = 256
+ lcodes_counts[256] = (lcodes_counts[256] or 0) + 1
+
+ return lcodes, lextra_bits, lcodes_counts, dcodes, dextra_bits
+ , dcodes_counts
+end
+
+-- Get the header data of dynamic block.
+-- @param lcodes_count The count of each literal/LZ77_length codes.
+-- @param dcodes_count The count of each Lz77 distance codes.
+-- @return a lots of stuffs.
+-- @see RFC1951 Page 12
+local function GetBlockDynamicHuffmanHeader(lcodes_counts, dcodes_counts)
+ local lcodes_huffman_bitlens, lcodes_huffman_codes
+ , max_non_zero_bitlen_lcode =
+ GetHuffmanBitlenAndCode(lcodes_counts, 15, 285)
+ local dcodes_huffman_bitlens, dcodes_huffman_codes
+ , max_non_zero_bitlen_dcode =
+ GetHuffmanBitlenAndCode(dcodes_counts, 15, 29)
+
+ local rle_deflate_codes, rle_extra_bits, rle_codes_counts =
+ RunLengthEncodeHuffmanBitlen(lcodes_huffman_bitlens
+ ,max_non_zero_bitlen_lcode, dcodes_huffman_bitlens
+ , max_non_zero_bitlen_dcode)
+
+ local rle_codes_huffman_bitlens, rle_codes_huffman_codes =
+ GetHuffmanBitlenAndCode(rle_codes_counts, 7, 18)
+
+ local HCLEN = 0
+ for i = 1, 19 do
+ local symbol = _rle_codes_huffman_bitlen_order[i]
+ local length = rle_codes_huffman_bitlens[symbol] or 0
+ if length ~= 0 then
+ HCLEN = i
+ end
+ end
+
+ HCLEN = HCLEN - 4
+ local HLIT = max_non_zero_bitlen_lcode + 1 - 257
+ local HDIST = max_non_zero_bitlen_dcode + 1 - 1
+ if HDIST < 0 then HDIST = 0 end
+
+ return HLIT, HDIST, HCLEN, rle_codes_huffman_bitlens
+ , rle_codes_huffman_codes, rle_deflate_codes, rle_extra_bits
+ , lcodes_huffman_bitlens, lcodes_huffman_codes
+ , dcodes_huffman_bitlens, dcodes_huffman_codes
+end
+
+-- Get the size of dynamic block without writing any bits into the writer.
+-- @param ... Read the source code of GetBlockDynamicHuffmanHeader()
+-- @return the bit length of the dynamic block
+local function GetDynamicHuffmanBlockSize(lcodes, dcodes, HCLEN
+ , rle_codes_huffman_bitlens, rle_deflate_codes
+ , lcodes_huffman_bitlens, dcodes_huffman_bitlens)
+
+ local block_bitlen = 17 -- 1+2+5+5+4
+ block_bitlen = block_bitlen + (HCLEN+4)*3
+
+ for i = 1, #rle_deflate_codes do
+ local code = rle_deflate_codes[i]
+ block_bitlen = block_bitlen + rle_codes_huffman_bitlens[code]
+ if code >= 16 then
+ block_bitlen = block_bitlen +
+ ((code == 16) and 2 or (code == 17 and 3 or 7))
+ end
+ end
+
+ local length_code_count = 0
+ for i = 1, #lcodes do
+ local code = lcodes[i]
+ local huffman_bitlen = lcodes_huffman_bitlens[code]
+ block_bitlen = block_bitlen + huffman_bitlen
+ if code > 256 then -- Length code
+ length_code_count = length_code_count + 1
+ if code > 264 and code < 285 then -- Length code with extra bits
+ local extra_bits_bitlen =
+ _literal_deflate_code_to_extra_bitlen[code-256]
+ block_bitlen = block_bitlen + extra_bits_bitlen
+ end
+ local dist_code = dcodes[length_code_count]
+ local dist_huffman_bitlen = dcodes_huffman_bitlens[dist_code]
+ block_bitlen = block_bitlen + dist_huffman_bitlen
+
+ if dist_code > 3 then -- dist code with extra bits
+ local dist_extra_bits_bitlen = (dist_code-dist_code%2)/2 - 1
+ block_bitlen = block_bitlen + dist_extra_bits_bitlen
+ end
+ end
+ end
+ return block_bitlen
+end
+
+-- Write dynamic block.
+-- @param ... Read the source code of GetBlockDynamicHuffmanHeader()
+local function CompressDynamicHuffmanBlock(WriteBits, is_last_block
+ , lcodes, lextra_bits, dcodes, dextra_bits, HLIT, HDIST, HCLEN
+ , rle_codes_huffman_bitlens, rle_codes_huffman_codes
+ , rle_deflate_codes, rle_extra_bits
+ , lcodes_huffman_bitlens, lcodes_huffman_codes
+ , dcodes_huffman_bitlens, dcodes_huffman_codes)
+
+ WriteBits(is_last_block and 1 or 0, 1) -- Last block identifier
+ WriteBits(2, 2) -- Dynamic Huffman block identifier
+
+ WriteBits(HLIT, 5)
+ WriteBits(HDIST, 5)
+ WriteBits(HCLEN, 4)
+
+ for i = 1, HCLEN+4 do
+ local symbol = _rle_codes_huffman_bitlen_order[i]
+ local length = rle_codes_huffman_bitlens[symbol] or 0
+ WriteBits(length, 3)
+ end
+
+ local rleExtraBitsIndex = 1
+ for i=1, #rle_deflate_codes do
+ local code = rle_deflate_codes[i]
+ WriteBits(rle_codes_huffman_codes[code]
+ , rle_codes_huffman_bitlens[code])
+ if code >= 16 then
+ local extraBits = rle_extra_bits[rleExtraBitsIndex]
+ WriteBits(extraBits, (code == 16) and 2 or (code == 17 and 3 or 7))
+ rleExtraBitsIndex = rleExtraBitsIndex + 1
+ end
+ end
+
+ local length_code_count = 0
+ local length_code_with_extra_count = 0
+ local dist_code_with_extra_count = 0
+
+ for i=1, #lcodes do
+ local deflate_codee = lcodes[i]
+ local huffman_code = lcodes_huffman_codes[deflate_codee]
+ local huffman_bitlen = lcodes_huffman_bitlens[deflate_codee]
+ WriteBits(huffman_code, huffman_bitlen)
+ if deflate_codee > 256 then -- Length code
+ length_code_count = length_code_count + 1
+ if deflate_codee > 264 and deflate_codee < 285 then
+ -- Length code with extra bits
+ length_code_with_extra_count = length_code_with_extra_count + 1
+ local extra_bits = lextra_bits[length_code_with_extra_count]
+ local extra_bits_bitlen =
+ _literal_deflate_code_to_extra_bitlen[deflate_codee-256]
+ WriteBits(extra_bits, extra_bits_bitlen)
+ end
+ -- Write distance code
+ local dist_deflate_code = dcodes[length_code_count]
+ local dist_huffman_code = dcodes_huffman_codes[dist_deflate_code]
+ local dist_huffman_bitlen =
+ dcodes_huffman_bitlens[dist_deflate_code]
+ WriteBits(dist_huffman_code, dist_huffman_bitlen)
+
+ if dist_deflate_code > 3 then -- dist code with extra bits
+ dist_code_with_extra_count = dist_code_with_extra_count + 1
+ local dist_extra_bits = dextra_bits[dist_code_with_extra_count]
+ local dist_extra_bits_bitlen =
+ (dist_deflate_code-dist_deflate_code%2)/2 - 1
+ WriteBits(dist_extra_bits, dist_extra_bits_bitlen)
+ end
+ end
+ end
+end
+
+-- Get the size of fixed block without writing any bits into the writer.
+-- @param lcodes literal/LZ77_length deflate codes
+-- @param decodes LZ77 distance deflate codes
+-- @return the bit length of the fixed block
+local function GetFixedHuffmanBlockSize(lcodes, dcodes)
+ local block_bitlen = 3
+ local length_code_count = 0
+ for i=1, #lcodes do
+ local code = lcodes[i]
+ local huffman_bitlen = _fix_block_literal_huffman_bitlen[code]
+ block_bitlen = block_bitlen + huffman_bitlen
+ if code > 256 then -- Length code
+ length_code_count = length_code_count + 1
+ if code > 264 and code < 285 then -- Length code with extra bits
+ local extra_bits_bitlen =
+ _literal_deflate_code_to_extra_bitlen[code-256]
+ block_bitlen = block_bitlen + extra_bits_bitlen
+ end
+ local dist_code = dcodes[length_code_count]
+ block_bitlen = block_bitlen + 5
+
+ if dist_code > 3 then -- dist code with extra bits
+ local dist_extra_bits_bitlen =
+ (dist_code-dist_code%2)/2 - 1
+ block_bitlen = block_bitlen + dist_extra_bits_bitlen
+ end
+ end
+ end
+ return block_bitlen
+end
+
+-- Write fixed block.
+-- @param lcodes literal/LZ77_length deflate codes
+-- @param decodes LZ77 distance deflate codes
+local function CompressFixedHuffmanBlock(WriteBits, is_last_block,
+ lcodes, lextra_bits, dcodes, dextra_bits)
+ WriteBits(is_last_block and 1 or 0, 1) -- Last block identifier
+ WriteBits(1, 2) -- Fixed Huffman block identifier
+ local length_code_count = 0
+ local length_code_with_extra_count = 0
+ local dist_code_with_extra_count = 0
+ for i=1, #lcodes do
+ local deflate_code = lcodes[i]
+ local huffman_code = _fix_block_literal_huffman_code[deflate_code]
+ local huffman_bitlen = _fix_block_literal_huffman_bitlen[deflate_code]
+ WriteBits(huffman_code, huffman_bitlen)
+ if deflate_code > 256 then -- Length code
+ length_code_count = length_code_count + 1
+ if deflate_code > 264 and deflate_code < 285 then
+ -- Length code with extra bits
+ length_code_with_extra_count = length_code_with_extra_count + 1
+ local extra_bits = lextra_bits[length_code_with_extra_count]
+ local extra_bits_bitlen =
+ _literal_deflate_code_to_extra_bitlen[deflate_code-256]
+ WriteBits(extra_bits, extra_bits_bitlen)
+ end
+ -- Write distance code
+ local dist_code = dcodes[length_code_count]
+ local dist_huffman_code = _fix_block_dist_huffman_code[dist_code]
+ WriteBits(dist_huffman_code, 5)
+
+ if dist_code > 3 then -- dist code with extra bits
+ dist_code_with_extra_count = dist_code_with_extra_count + 1
+ local dist_extra_bits = dextra_bits[dist_code_with_extra_count]
+ local dist_extra_bits_bitlen = (dist_code-dist_code%2)/2 - 1
+ WriteBits(dist_extra_bits, dist_extra_bits_bitlen)
+ end
+ end
+ end
+end
+
+-- Get the size of store block without writing any bits into the writer.
+-- @param block_start The start index of the origin input string
+-- @param block_end The end index of the origin input string
+-- @param Total bit lens had been written into the compressed result before,
+-- because store block needs to shift to byte boundary.
+-- @return the bit length of the fixed block
+local function GetStoreBlockSize(block_start, block_end, total_bitlen)
+ assert(block_end-block_start+1 <= 65535)
+ local block_bitlen = 3
+ total_bitlen = total_bitlen + 3
+ local padding_bitlen = (8-total_bitlen%8)%8
+ block_bitlen = block_bitlen + padding_bitlen
+ block_bitlen = block_bitlen + 32
+ block_bitlen = block_bitlen + (block_end - block_start + 1) * 8
+ return block_bitlen
+end
+
+-- Write the store block.
+-- @param ... lots of stuffs
+-- @return nil
+local function CompressStoreBlock(WriteBits, WriteString, is_last_block, str
+ , block_start, block_end, total_bitlen)
+ assert(block_end-block_start+1 <= 65535)
+ WriteBits(is_last_block and 1 or 0, 1) -- Last block identifer.
+ WriteBits(0, 2) -- Store block identifier.
+ total_bitlen = total_bitlen + 3
+ local padding_bitlen = (8-total_bitlen%8)%8
+ if padding_bitlen > 0 then
+ WriteBits(_pow2[padding_bitlen]-1, padding_bitlen)
+ end
+ local size = block_end - block_start + 1
+ WriteBits(size, 16)
+
+ -- Write size's one's complement
+ local comp = (255 - size % 256) + (255 - (size-size%256)/256)*256
+ WriteBits(comp, 16)
+
+ WriteString(str:sub(block_start, block_end))
+end
+
+-- Do the deflate
+-- Currently using a simple way to determine the block size
+-- (This is why the compression ratio is little bit worse than zlib when
+-- the input size is very large
+-- The first block is 64KB, the following block is 32KB.
+-- After each block, there is a memory cleanup operation.
+-- This is not a fast operation, but it is needed to save memory usage, so
+-- the memory usage does not grow unboundly. If the data size is less than
+-- 64KB, then memory cleanup won't happen.
+-- This function determines whether to use store/fixed/dynamic blocks by
+-- calculating the block size of each block type and chooses the smallest one.
+local function Deflate(configs, WriteBits, WriteString, FlushWriter, str
+ , dictionary)
+ local string_table = {}
+ local hash_tables = {}
+ local is_last_block = nil
+ local block_start
+ local block_end
+ local bitlen_written
+ local total_bitlen = FlushWriter(_FLUSH_MODE_NO_FLUSH)
+ local strlen = #str
+ local offset
+
+ local level
+ local strategy
+ if configs then
+ if configs.level then
+ level = configs.level
+ end
+ if configs.strategy then
+ strategy = configs.strategy
+ end
+ end
+
+ if not level then
+ if strlen < 2048 then
+ level = 7
+ elseif strlen > 65536 then
+ level = 3
+ else
+ level = 5
+ end
+ end
+
+ while not is_last_block do
+ if not block_start then
+ block_start = 1
+ block_end = 64*1024 - 1
+ offset = 0
+ else
+ block_start = block_end + 1
+ block_end = block_end + 32*1024
+ offset = block_start - 32*1024 - 1
+ end
+
+ if block_end >= strlen then
+ block_end = strlen
+ is_last_block = true
+ else
+ is_last_block = false
+ end
+
+ local lcodes, lextra_bits, lcodes_counts, dcodes, dextra_bits
+ , dcodes_counts
+
+ local HLIT, HDIST, HCLEN, rle_codes_huffman_bitlens
+ , rle_codes_huffman_codes, rle_deflate_codes
+ , rle_extra_bits, lcodes_huffman_bitlens, lcodes_huffman_codes
+ , dcodes_huffman_bitlens, dcodes_huffman_codes
+
+ local dynamic_block_bitlen
+ local fixed_block_bitlen
+ local store_block_bitlen
+
+ if level ~= 0 then
+
+ -- GetBlockLZ77 needs block_start to block_end+3 to be loaded.
+ LoadStringToTable(str, string_table, block_start, block_end + 3
+ , offset)
+ if block_start == 1 and dictionary then
+ local dict_string_table = dictionary.string_table
+ local dict_strlen = dictionary.strlen
+ for i=0, (-dict_strlen+1)<-257
+ and -257 or (-dict_strlen+1), -1 do
+ string_table[i] = dict_string_table[dict_strlen+i]
+ end
+ end
+
+ if strategy == "huffman_only" then
+ lcodes = {}
+ LoadStringToTable(str, lcodes, block_start, block_end
+ , block_start-1)
+ lextra_bits = {}
+ lcodes_counts = {}
+ lcodes[block_end - block_start+2] = 256 -- end of block
+ for i=1, block_end - block_start+2 do
+ local code = lcodes[i]
+ lcodes_counts[code] = (lcodes_counts[code] or 0) + 1
+ end
+ dcodes = {}
+ dextra_bits = {}
+ dcodes_counts = {}
+ else
+ lcodes, lextra_bits, lcodes_counts, dcodes, dextra_bits
+ , dcodes_counts = GetBlockLZ77Result(level, string_table
+ , hash_tables, block_start, block_end, offset, dictionary
+ )
+ end
+
+ HLIT, HDIST, HCLEN, rle_codes_huffman_bitlens
+ , rle_codes_huffman_codes, rle_deflate_codes
+ , rle_extra_bits, lcodes_huffman_bitlens, lcodes_huffman_codes
+ , dcodes_huffman_bitlens, dcodes_huffman_codes =
+ GetBlockDynamicHuffmanHeader(lcodes_counts, dcodes_counts)
+ dynamic_block_bitlen = GetDynamicHuffmanBlockSize(
+ lcodes, dcodes, HCLEN, rle_codes_huffman_bitlens
+ , rle_deflate_codes, lcodes_huffman_bitlens
+ , dcodes_huffman_bitlens)
+ fixed_block_bitlen = GetFixedHuffmanBlockSize(lcodes, dcodes)
+ end
+
+ store_block_bitlen = GetStoreBlockSize(block_start, block_end
+ , total_bitlen)
+
+ local min_bitlen = store_block_bitlen
+ min_bitlen = (fixed_block_bitlen and fixed_block_bitlen < min_bitlen)
+ and fixed_block_bitlen or min_bitlen
+ min_bitlen = (dynamic_block_bitlen
+ and dynamic_block_bitlen < min_bitlen)
+ and dynamic_block_bitlen or min_bitlen
+
+ if level == 0 or (strategy ~= "fixed" and strategy ~= "dynamic" and
+ store_block_bitlen == min_bitlen) then
+ CompressStoreBlock(WriteBits, WriteString, is_last_block
+ , str, block_start, block_end, total_bitlen)
+ total_bitlen = total_bitlen + store_block_bitlen
+ elseif strategy ~= "dynamic" and (
+ strategy == "fixed" or fixed_block_bitlen == min_bitlen) then
+ CompressFixedHuffmanBlock(WriteBits, is_last_block,
+ lcodes, lextra_bits, dcodes, dextra_bits)
+ total_bitlen = total_bitlen + fixed_block_bitlen
+ elseif strategy == "dynamic" or dynamic_block_bitlen == min_bitlen then
+ CompressDynamicHuffmanBlock(WriteBits, is_last_block, lcodes
+ , lextra_bits, dcodes, dextra_bits, HLIT, HDIST, HCLEN
+ , rle_codes_huffman_bitlens, rle_codes_huffman_codes
+ , rle_deflate_codes, rle_extra_bits
+ , lcodes_huffman_bitlens, lcodes_huffman_codes
+ , dcodes_huffman_bitlens, dcodes_huffman_codes)
+ total_bitlen = total_bitlen + dynamic_block_bitlen
+ end
+
+ if is_last_block then
+ bitlen_written = FlushWriter(_FLUSH_MODE_NO_FLUSH)
+ else
+ bitlen_written = FlushWriter(_FLUSH_MODE_MEMORY_CLEANUP)
+ end
+
+ assert(bitlen_written == total_bitlen)
+
+ -- Memory clean up, so memory consumption does not always grow linearly
+ -- , even if input string is > 64K.
+ -- Not a very efficient operation, but this operation won't happen
+ -- when the input data size is less than 64K.
+ if not is_last_block then
+ local j
+ if dictionary and block_start == 1 then
+ j = 0
+ while (string_table[j]) do
+ string_table[j] = nil
+ j = j - 1
+ end
+ end
+ dictionary = nil
+ j = 1
+ for i = block_end-32767, block_end do
+ string_table[j] = string_table[i-offset]
+ j = j + 1
+ end
+
+ for k, t in pairs(hash_tables) do
+ local tSize = #t
+ if tSize > 0 and block_end+1 - t[1] > 32768 then
+ if tSize == 1 then
+ hash_tables[k] = nil
+ else
+ local new = {}
+ local newSize = 0
+ for i = 2, tSize do
+ j = t[i]
+ if block_end+1 - j <= 32768 then
+ newSize = newSize + 1
+ new[newSize] = j
+ end
+ end
+ hash_tables[k] = new
+ end
+ end
+ end
+ end
+ end
+end
+
+--- The description to compression configuration table.
+-- Any field can be nil to use its default.
+-- Table with keys other than those below is an invalid table.
+-- @class table
+-- @name compression_configs
+-- @field level The compression level ranged from 0 to 9. 0 is no compression.
+-- 9 is the slowest but best compression. Use nil for default level.
+-- @field strategy The compression strategy. "fixed" to only use fixed deflate
+-- compression block. "dynamic" to only use dynamic block. "huffman_only" to
+-- do no LZ77 compression. Only do huffman compression.
+
+
+-- @see LibDeflate:CompressDeflate(str, configs)
+-- @see LibDeflate:CompressDeflateWithDict(str, dictionary, configs)
+local function CompressDeflateInternal(str, dictionary, configs)
+ local WriteBits, WriteString, FlushWriter = CreateWriter()
+ Deflate(configs, WriteBits, WriteString, FlushWriter, str, dictionary)
+ local total_bitlen, result = FlushWriter(_FLUSH_MODE_OUTPUT)
+ local padding_bitlen = (8-total_bitlen%8)%8
+ return result, padding_bitlen
+end
+
+-- @see LibDeflate:CompressZlib
+-- @see LibDeflate:CompressZlibWithDict
+local function CompressZlibInternal(str, dictionary, configs)
+ local WriteBits, WriteString, FlushWriter = CreateWriter()
+
+ local CM = 8 -- Compression method
+ local CINFO = 7 --Window Size = 32K
+ local CMF = CINFO*16+CM
+ WriteBits(CMF, 8)
+
+ local FDIST = dictionary and 1 or 0
+ local FLEVEL = 2 -- Default compression
+ local FLG = FLEVEL*64+FDIST*32
+ local FCHECK = (31-(CMF*256+FLG)%31)
+ -- The FCHECK value must be such that CMF and FLG,
+ -- when viewed as a 16-bit unsigned integer stored
+ -- in MSB order (CMF*256 + FLG), is a multiple of 31.
+ FLG = FLG + FCHECK
+ WriteBits(FLG, 8)
+
+ if FDIST == 1 then
+ local adler32 = dictionary.adler32
+ local byte0 = adler32 % 256
+ adler32 = (adler32 - byte0) / 256
+ local byte1 = adler32 % 256
+ adler32 = (adler32 - byte1) / 256
+ local byte2 = adler32 % 256
+ adler32 = (adler32 - byte2) / 256
+ local byte3 = adler32 % 256
+ WriteBits(byte3, 8)
+ WriteBits(byte2, 8)
+ WriteBits(byte1, 8)
+ WriteBits(byte0, 8)
+ end
+
+ Deflate(configs, WriteBits, WriteString, FlushWriter, str, dictionary)
+ FlushWriter(_FLUSH_MODE_BYTE_BOUNDARY)
+
+ local adler32 = LibDeflate:Adler32(str)
+
+ -- Most significant byte first
+ local byte3 = adler32%256
+ adler32 = (adler32 - byte3) / 256
+ local byte2 = adler32%256
+ adler32 = (adler32 - byte2) / 256
+ local byte1 = adler32%256
+ adler32 = (adler32 - byte1) / 256
+ local byte0 = adler32%256
+
+ WriteBits(byte0, 8)
+ WriteBits(byte1, 8)
+ WriteBits(byte2, 8)
+ WriteBits(byte3, 8)
+ local total_bitlen, result = FlushWriter(_FLUSH_MODE_OUTPUT)
+ local padding_bitlen = (8-total_bitlen%8)%8
+ return result, padding_bitlen
+end
+
+--- Compress using the raw deflate format.
+-- @param str [string] The data to be compressed.
+-- @param configs [table/nil] The configuration table to control the compression
+-- . If nil, use the default configuration.
+-- @return [string] The compressed data.
+-- @return [integer] The number of bits padded at the end of output.
+-- 0 <= bits < 8
+-- This means the most significant "bits" of the last byte of the returned
+-- compressed data are padding bits and they don't affect decompression.
+-- You don't need to use this value unless you want to do some postprocessing
+-- to the compressed data.
+-- @see compression_configs
+-- @see LibDeflate:DecompressDeflate
+function LibDeflate:CompressDeflate(str, configs)
+ local arg_valid, arg_err = IsValidArguments(str, false, nil, true, configs)
+ if not arg_valid then
+ error(("Usage: LibDeflate:CompressDeflate(str, configs): "
+ ..arg_err), 2)
+ end
+ return CompressDeflateInternal(str, nil, configs)
+end
+
+--- Compress using the raw deflate format with a preset dictionary.
+-- @param str [string] The data to be compressed.
+-- @param dictionary [table] The preset dictionary produced by
+-- LibDeflate:CreateDictionary
+-- @param configs [table/nil] The configuration table to control the compression
+-- . If nil, use the default configuration.
+-- @return [string] The compressed data.
+-- @return [integer] The number of bits padded at the end of output.
+-- 0 <= bits < 8
+-- This means the most significant "bits" of the last byte of the returned
+-- compressed data are padding bits and they don't affect decompression.
+-- You don't need to use this value unless you want to do some postprocessing
+-- to the compressed data.
+-- @see compression_configs
+-- @see LibDeflate:CreateDictionary
+-- @see LibDeflate:DecompressDeflateWithDict
+function LibDeflate:CompressDeflateWithDict(str, dictionary, configs)
+ local arg_valid, arg_err = IsValidArguments(str, true, dictionary
+ , true, configs)
+ if not arg_valid then
+ error(("Usage: LibDeflate:CompressDeflateWithDict"
+ .."(str, dictionary, configs): "
+ ..arg_err), 2)
+ end
+ return CompressDeflateInternal(str, dictionary, configs)
+end
+
+--- Compress using the zlib format.
+-- @param str [string] the data to be compressed.
+-- @param configs [table/nil] The configuration table to control the compression
+-- . If nil, use the default configuration.
+-- @return [string] The compressed data.
+-- @return [integer] The number of bits padded at the end of output.
+-- Should always be 0.
+-- Zlib formatted compressed data never has padding bits at the end.
+-- @see compression_configs
+-- @see LibDeflate:DecompressZlib
+function LibDeflate:CompressZlib(str, configs)
+ local arg_valid, arg_err = IsValidArguments(str, false, nil, true, configs)
+ if not arg_valid then
+ error(("Usage: LibDeflate:CompressZlib(str, configs): "
+ ..arg_err), 2)
+ end
+ return CompressZlibInternal(str, nil, configs)
+end
+
+--- Compress using the zlib format with a preset dictionary.
+-- @param str [string] the data to be compressed.
+-- @param dictionary [table] A preset dictionary produced
+-- by LibDeflate:CreateDictionary()
+-- @param configs [table/nil] The configuration table to control the compression
+-- . If nil, use the default configuration.
+-- @return [string] The compressed data.
+-- @return [integer] The number of bits padded at the end of output.
+-- Should always be 0.
+-- Zlib formatted compressed data never has padding bits at the end.
+-- @see compression_configs
+-- @see LibDeflate:CreateDictionary
+-- @see LibDeflate:DecompressZlibWithDict
+function LibDeflate:CompressZlibWithDict(str, dictionary, configs)
+ local arg_valid, arg_err = IsValidArguments(str, true, dictionary
+ , true, configs)
+ if not arg_valid then
+ error(("Usage: LibDeflate:CompressZlibWithDict"
+ .."(str, dictionary, configs): "
+ ..arg_err), 2)
+ end
+ return CompressZlibInternal(str, dictionary, configs)
+end
+
+--[[ --------------------------------------------------------------------------
+ Decompress code
+--]] --------------------------------------------------------------------------
+
+--[[
+ Create a reader to easily reader stuffs as the unit of bits.
+ Return values:
+ 1. ReadBits(bitlen)
+ 2. ReadBytes(bytelen, buffer, buffer_size)
+ 3. Decode(huffman_bitlen_count, huffman_symbol, min_bitlen)
+ 4. ReaderBitlenLeft()
+ 5. SkipToByteBoundary()
+--]]
+local function CreateReader(input_string)
+ local input = input_string
+ local input_strlen = #input_string
+ local input_next_byte_pos = 1
+ local cache_bitlen = 0
+ local cache = 0
+
+ -- Read some bits.
+ -- To improve speed, this function does not
+ -- check if the input has been exhausted.
+ -- Use ReaderBitlenLeft() < 0 to check it.
+ -- @param bitlen the number of bits to read
+ -- @return the data is read.
+ local function ReadBits(bitlen)
+ local rshift_mask = _pow2[bitlen]
+ local code
+ if bitlen <= cache_bitlen then
+ code = cache % rshift_mask
+ cache = (cache - code) / rshift_mask
+ cache_bitlen = cache_bitlen - bitlen
+ else -- Whether input has been exhausted is not checked.
+ local lshift_mask = _pow2[cache_bitlen]
+ local byte1, byte2, byte3, byte4 = string_byte(input
+ , input_next_byte_pos, input_next_byte_pos+3)
+ -- This requires lua number to be at least double ()
+ cache = cache + ((byte1 or 0)+(byte2 or 0)*256
+ + (byte3 or 0)*65536+(byte4 or 0)*16777216)*lshift_mask
+ input_next_byte_pos = input_next_byte_pos + 4
+ cache_bitlen = cache_bitlen + 32 - bitlen
+ code = cache % rshift_mask
+ cache = (cache - code) / rshift_mask
+ end
+ return code
+ end
+
+ -- Read some bytes from the reader.
+ -- Assume reader is on the byte boundary.
+ -- @param bytelen The number of bytes to be read.
+ -- @param buffer The byte read will be stored into this buffer.
+ -- @param buffer_size The buffer will be modified starting from
+ -- buffer[buffer_size+1], ending at buffer[buffer_size+bytelen-1]
+ -- @return the new buffer_size
+ local function ReadBytes(bytelen, buffer, buffer_size)
+ assert(cache_bitlen % 8 == 0)
+
+ local byte_from_cache = (cache_bitlen/8 < bytelen)
+ and (cache_bitlen/8) or bytelen
+ for _=1, byte_from_cache do
+ local byte = cache % 256
+ buffer_size = buffer_size + 1
+ buffer[buffer_size] = string_char(byte)
+ cache = (cache - byte) / 256
+ end
+ cache_bitlen = cache_bitlen - byte_from_cache*8
+ bytelen = bytelen - byte_from_cache
+ if (input_strlen - input_next_byte_pos - bytelen + 1) * 8
+ + cache_bitlen < 0 then
+ return -1 -- out of input
+ end
+ for i=input_next_byte_pos, input_next_byte_pos+bytelen-1 do
+ buffer_size = buffer_size + 1
+ buffer[buffer_size] = string_sub(input, i, i)
+ end
+
+ input_next_byte_pos = input_next_byte_pos + bytelen
+ return buffer_size
+ end
+
+ -- Decode huffman code
+ -- To improve speed, this function does not check
+ -- if the input has been exhausted.
+ -- Use ReaderBitlenLeft() < 0 to check it.
+ -- Credits for Mark Adler. This code is from puff:Decode()
+ -- @see puff:Decode(...)
+ -- @param huffman_bitlen_count
+ -- @param huffman_symbol
+ -- @param min_bitlen The minimum huffman bit length of all symbols
+ -- @return The decoded deflate code.
+ -- Negative value is returned if decoding fails.
+ local function Decode(huffman_bitlen_counts, huffman_symbols, min_bitlen)
+ local code = 0
+ local first = 0
+ local index = 0
+ local count
+ if min_bitlen > 0 then
+ if cache_bitlen < 15 and input then
+ local lshift_mask = _pow2[cache_bitlen]
+ local byte1, byte2, byte3, byte4 =
+ string_byte(input, input_next_byte_pos
+ , input_next_byte_pos+3)
+ -- This requires lua number to be at least double ()
+ cache = cache + ((byte1 or 0)+(byte2 or 0)*256
+ +(byte3 or 0)*65536+(byte4 or 0)*16777216)*lshift_mask
+ input_next_byte_pos = input_next_byte_pos + 4
+ cache_bitlen = cache_bitlen + 32
+ end
+
+ local rshift_mask = _pow2[min_bitlen]
+ cache_bitlen = cache_bitlen - min_bitlen
+ code = cache % rshift_mask
+ cache = (cache - code) / rshift_mask
+ -- Reverse the bits
+ code = _reverse_bits_tbl[min_bitlen][code]
+
+ count = huffman_bitlen_counts[min_bitlen]
+ if code < count then
+ return huffman_symbols[code]
+ end
+ index = count
+ first = count * 2
+ code = code * 2
+ end
+
+ for bitlen = min_bitlen+1, 15 do
+ local bit
+ bit = cache % 2
+ cache = (cache - bit) / 2
+ cache_bitlen = cache_bitlen - 1
+
+ code = (bit==1) and (code + 1 - code % 2) or code
+ count = huffman_bitlen_counts[bitlen] or 0
+ local diff = code - first
+ if diff < count then
+ return huffman_symbols[index + diff]
+ end
+ index = index + count
+ first = first + count
+ first = first * 2
+ code = code * 2
+ end
+ -- invalid literal/length or distance code
+ -- in fixed or dynamic block (run out of code)
+ return -10
+ end
+
+ local function ReaderBitlenLeft()
+ return (input_strlen - input_next_byte_pos + 1) * 8 + cache_bitlen
+ end
+
+ local function SkipToByteBoundary()
+ local skipped_bitlen = cache_bitlen%8
+ local rshift_mask = _pow2[skipped_bitlen]
+ cache_bitlen = cache_bitlen - skipped_bitlen
+ cache = (cache - cache % rshift_mask) / rshift_mask
+ end
+
+ return ReadBits, ReadBytes, Decode, ReaderBitlenLeft, SkipToByteBoundary
+end
+
+-- Create a deflate state, so I can pass in less arguments to functions.
+-- @param str the whole string to be decompressed.
+-- @param dictionary The preset dictionary. nil if not provided.
+-- This dictionary should be produced by LibDeflate:CreateDictionary(str)
+-- @return The decomrpess state.
+local function CreateDecompressState(str, dictionary)
+ local ReadBits, ReadBytes, Decode, ReaderBitlenLeft
+ , SkipToByteBoundary = CreateReader(str)
+ local state =
+ {
+ ReadBits = ReadBits,
+ ReadBytes = ReadBytes,
+ Decode = Decode,
+ ReaderBitlenLeft = ReaderBitlenLeft,
+ SkipToByteBoundary = SkipToByteBoundary,
+ buffer_size = 0,
+ buffer = {},
+ result_buffer = {},
+ dictionary = dictionary,
+ }
+ return state
+end
+
+-- Get the stuffs needed to decode huffman codes
+-- @see puff.c:construct(...)
+-- @param huffman_bitlen The huffman bit length of the huffman codes.
+-- @param max_symbol The maximum symbol
+-- @param max_bitlen The min huffman bit length of all codes
+-- @return zero or positive for success, negative for failure.
+-- @return The count of each huffman bit length.
+-- @return A table to convert huffman codes to deflate codes.
+-- @return The minimum huffman bit length.
+local function GetHuffmanForDecode(huffman_bitlens, max_symbol, max_bitlen)
+ local huffman_bitlen_counts = {}
+ local min_bitlen = max_bitlen
+ for symbol = 0, max_symbol do
+ local bitlen = huffman_bitlens[symbol] or 0
+ min_bitlen = (bitlen > 0 and bitlen < min_bitlen)
+ and bitlen or min_bitlen
+ huffman_bitlen_counts[bitlen] = (huffman_bitlen_counts[bitlen] or 0)+1
+ end
+
+ if huffman_bitlen_counts[0] == max_symbol+1 then -- No Codes
+ return 0, huffman_bitlen_counts, {}, 0 -- Complete, but decode will fail
+ end
+
+ local left = 1
+ for len = 1, max_bitlen do
+ left = left * 2
+ left = left - (huffman_bitlen_counts[len] or 0)
+ if left < 0 then
+ return left -- Over-subscribed, return negative
+ end
+ end
+
+ -- Generate offsets info symbol table for each length for sorting
+ local offsets = {}
+ offsets[1] = 0
+ for len = 1, max_bitlen-1 do
+ offsets[len + 1] = offsets[len] + (huffman_bitlen_counts[len] or 0)
+ end
+
+ local huffman_symbols = {}
+ for symbol = 0, max_symbol do
+ local bitlen = huffman_bitlens[symbol] or 0
+ if bitlen ~= 0 then
+ local offset = offsets[bitlen]
+ huffman_symbols[offset] = symbol
+ offsets[bitlen] = offsets[bitlen] + 1
+ end
+ end
+
+ -- Return zero for complete set, positive for incomplete set.
+ return left, huffman_bitlen_counts, huffman_symbols, min_bitlen
+end
+
+-- Decode a fixed or dynamic huffman blocks, excluding last block identifier
+-- and block type identifer.
+-- @see puff.c:codes()
+-- @param state decompression state that will be modified by this function.
+-- @see CreateDecompressState
+-- @param ... Read the source code
+-- @return 0 on success, other value on failure.
+local function DecodeUntilEndOfBlock(state, lcodes_huffman_bitlens
+ , lcodes_huffman_symbols, lcodes_huffman_min_bitlen
+ , dcodes_huffman_bitlens, dcodes_huffman_symbols
+ , dcodes_huffman_min_bitlen)
+ local buffer, buffer_size, ReadBits, Decode, ReaderBitlenLeft
+ , result_buffer =
+ state.buffer, state.buffer_size, state.ReadBits, state.Decode
+ , state.ReaderBitlenLeft, state.result_buffer
+ local dictionary = state.dictionary
+ local dict_string_table
+ local dict_strlen
+
+ local buffer_end = 1
+ if dictionary and not buffer[0] then
+ -- If there is a dictionary, copy the last 258 bytes into
+ -- the string_table to make the copy in the main loop quicker.
+ -- This is done only once per decompression.
+ dict_string_table = dictionary.string_table
+ dict_strlen = dictionary.strlen
+ buffer_end = -dict_strlen + 1
+ for i=0, (-dict_strlen+1)<-257 and -257 or (-dict_strlen+1), -1 do
+ buffer[i] = _byte_to_char[dict_string_table[dict_strlen+i]]
+ end
+ end
+
+ repeat
+ local symbol = Decode(lcodes_huffman_bitlens
+ , lcodes_huffman_symbols, lcodes_huffman_min_bitlen)
+ if symbol < 0 or symbol > 285 then
+ -- invalid literal/length or distance code in fixed or dynamic block
+ return -10
+ elseif symbol < 256 then -- Literal
+ buffer_size = buffer_size + 1
+ buffer[buffer_size] = _byte_to_char[symbol]
+ elseif symbol > 256 then -- Length code
+ symbol = symbol - 256
+ local bitlen = _literal_deflate_code_to_base_len[symbol]
+ bitlen = (symbol >= 8)
+ and (bitlen
+ + ReadBits(_literal_deflate_code_to_extra_bitlen[symbol]))
+ or bitlen
+ symbol = Decode(dcodes_huffman_bitlens, dcodes_huffman_symbols
+ , dcodes_huffman_min_bitlen)
+ if symbol < 0 or symbol > 29 then
+ -- invalid literal/length or distance code in fixed or dynamic block
+ return -10
+ end
+ local dist = _dist_deflate_code_to_base_dist[symbol]
+ dist = (dist > 4) and (dist
+ + ReadBits(_dist_deflate_code_to_extra_bitlen[symbol])) or dist
+
+ local char_buffer_index = buffer_size-dist+1
+ if char_buffer_index < buffer_end then
+ -- distance is too far back in fixed or dynamic block
+ return -11
+ end
+ if char_buffer_index >= -257 then
+ for _=1, bitlen do
+ buffer_size = buffer_size + 1
+ buffer[buffer_size] = buffer[char_buffer_index]
+ char_buffer_index = char_buffer_index + 1
+ end
+ else
+ char_buffer_index = dict_strlen + char_buffer_index
+ for _=1, bitlen do
+ buffer_size = buffer_size + 1
+ buffer[buffer_size] =
+ _byte_to_char[dict_string_table[char_buffer_index]]
+ char_buffer_index = char_buffer_index + 1
+ end
+ end
+ end
+
+ if ReaderBitlenLeft() < 0 then
+ return 2 -- available inflate data did not terminate
+ end
+
+ if buffer_size >= 65536 then
+ result_buffer[#result_buffer+1] =
+ table_concat(buffer, "", 1, 32768)
+ for i=32769, buffer_size do
+ buffer[i-32768] = buffer[i]
+ end
+ buffer_size = buffer_size - 32768
+ buffer[buffer_size+1] = nil
+ -- NOTE: buffer[32769..end] and buffer[-257..0] are not cleared.
+ -- This is why "buffer_size" variable is needed.
+ end
+ until symbol == 256
+
+ state.buffer_size = buffer_size
+
+ return 0
+end
+
+-- Decompress a store block
+-- @param state decompression state that will be modified by this function.
+-- @return 0 if succeeds, other value if fails.
+local function DecompressStoreBlock(state)
+ local buffer, buffer_size, ReadBits, ReadBytes, ReaderBitlenLeft
+ , SkipToByteBoundary, result_buffer =
+ state.buffer, state.buffer_size, state.ReadBits, state.ReadBytes
+ , state.ReaderBitlenLeft, state.SkipToByteBoundary, state.result_buffer
+
+ SkipToByteBoundary()
+ local bytelen = ReadBits(16)
+ if ReaderBitlenLeft() < 0 then
+ return 2 -- available inflate data did not terminate
+ end
+ local bytelenComp = ReadBits(16)
+ if ReaderBitlenLeft() < 0 then
+ return 2 -- available inflate data did not terminate
+ end
+
+ if bytelen % 256 + bytelenComp % 256 ~= 255 then
+ return -2 -- Not one's complement
+ end
+ if (bytelen-bytelen % 256)/256
+ + (bytelenComp-bytelenComp % 256)/256 ~= 255 then
+ return -2 -- Not one's complement
+ end
+
+ -- Note that ReadBytes will skip to the next byte boundary first.
+ buffer_size = ReadBytes(bytelen, buffer, buffer_size)
+ if buffer_size < 0 then
+ return 2 -- available inflate data did not terminate
+ end
+
+ -- memory clean up when there are enough bytes in the buffer.
+ if buffer_size >= 65536 then
+ result_buffer[#result_buffer+1] = table_concat(buffer, "", 1, 32768)
+ for i=32769, buffer_size do
+ buffer[i-32768] = buffer[i]
+ end
+ buffer_size = buffer_size - 32768
+ buffer[buffer_size+1] = nil
+ end
+ state.buffer_size = buffer_size
+ return 0
+end
+
+-- Decompress a fixed block
+-- @param state decompression state that will be modified by this function.
+-- @return 0 if succeeds other value if fails.
+local function DecompressFixBlock(state)
+ return DecodeUntilEndOfBlock(state
+ , _fix_block_literal_huffman_bitlen_count
+ , _fix_block_literal_huffman_to_deflate_code, 7
+ , _fix_block_dist_huffman_bitlen_count
+ , _fix_block_dist_huffman_to_deflate_code, 5)
+end
+
+-- Decompress a dynamic block
+-- @param state decompression state that will be modified by this function.
+-- @return 0 if success, other value if fails.
+local function DecompressDynamicBlock(state)
+ local ReadBits, Decode = state.ReadBits, state.Decode
+ local nlen = ReadBits(5) + 257
+ local ndist = ReadBits(5) + 1
+ local ncode = ReadBits(4) + 4
+ if nlen > 286 or ndist > 30 then
+ -- dynamic block code description: too many length or distance codes
+ return -3
+ end
+
+ local rle_codes_huffman_bitlens = {}
+
+ for i = 1, ncode do
+ rle_codes_huffman_bitlens[_rle_codes_huffman_bitlen_order[i]] =
+ ReadBits(3)
+ end
+
+ local rle_codes_err, rle_codes_huffman_bitlen_counts,
+ rle_codes_huffman_symbols, rle_codes_huffman_min_bitlen =
+ GetHuffmanForDecode(rle_codes_huffman_bitlens, 18, 7)
+ if rle_codes_err ~= 0 then -- Require complete code set here
+ -- dynamic block code description: code lengths codes incomplete
+ return -4
+ end
+
+ local lcodes_huffman_bitlens = {}
+ local dcodes_huffman_bitlens = {}
+ -- Read length/literal and distance code length tables
+ local index = 0
+ while index < nlen + ndist do
+ local symbol -- Decoded value
+ local bitlen -- Last length to repeat
+
+ symbol = Decode(rle_codes_huffman_bitlen_counts
+ , rle_codes_huffman_symbols, rle_codes_huffman_min_bitlen)
+
+ if symbol < 0 then
+ return symbol -- Invalid symbol
+ elseif symbol < 16 then
+ if index < nlen then
+ lcodes_huffman_bitlens[index] = symbol
+ else
+ dcodes_huffman_bitlens[index-nlen] = symbol
+ end
+ index = index + 1
+ else
+ bitlen = 0
+ if symbol == 16 then
+ if index == 0 then
+ -- dynamic block code description: repeat lengths
+ -- with no first length
+ return -5
+ end
+ if index-1 < nlen then
+ bitlen = lcodes_huffman_bitlens[index-1]
+ else
+ bitlen = dcodes_huffman_bitlens[index-nlen-1]
+ end
+ symbol = 3 + ReadBits(2)
+ elseif symbol == 17 then -- Repeat zero 3..10 times
+ symbol = 3 + ReadBits(3)
+ else -- == 18, repeat zero 11.138 times
+ symbol = 11 + ReadBits(7)
+ end
+ if index + symbol > nlen + ndist then
+ -- dynamic block code description:
+ -- repeat more than specified lengths
+ return -6
+ end
+ while symbol > 0 do -- Repeat last or zero symbol times
+ symbol = symbol - 1
+ if index < nlen then
+ lcodes_huffman_bitlens[index] = bitlen
+ else
+ dcodes_huffman_bitlens[index-nlen] = bitlen
+ end
+ index = index + 1
+ end
+ end
+ end
+
+ if (lcodes_huffman_bitlens[256] or 0) == 0 then
+ -- dynamic block code description: missing end-of-block code
+ return -9
+ end
+
+ local lcodes_err, lcodes_huffman_bitlen_counts
+ , lcodes_huffman_symbols, lcodes_huffman_min_bitlen =
+ GetHuffmanForDecode(lcodes_huffman_bitlens, nlen-1, 15)
+ --dynamic block code description: invalid literal/length code lengths,
+ -- Incomplete code ok only for single length 1 code
+ if (lcodes_err ~=0 and (lcodes_err < 0
+ or nlen ~= (lcodes_huffman_bitlen_counts[0] or 0)
+ +(lcodes_huffman_bitlen_counts[1] or 0))) then
+ return -7
+ end
+
+ local dcodes_err, dcodes_huffman_bitlen_counts
+ , dcodes_huffman_symbols, dcodes_huffman_min_bitlen =
+ GetHuffmanForDecode(dcodes_huffman_bitlens, ndist-1, 15)
+ -- dynamic block code description: invalid distance code lengths,
+ -- Incomplete code ok only for single length 1 code
+ if (dcodes_err ~=0 and (dcodes_err < 0
+ or ndist ~= (dcodes_huffman_bitlen_counts[0] or 0)
+ + (dcodes_huffman_bitlen_counts[1] or 0))) then
+ return -8
+ end
+
+ -- Build buffman table for literal/length codes
+ return DecodeUntilEndOfBlock(state, lcodes_huffman_bitlen_counts
+ , lcodes_huffman_symbols, lcodes_huffman_min_bitlen
+ , dcodes_huffman_bitlen_counts, dcodes_huffman_symbols
+ , dcodes_huffman_min_bitlen)
+end
+
+-- Decompress a deflate stream
+-- @param state: a decompression state
+-- @return the decompressed string if succeeds. nil if fails.
+local function Inflate(state)
+ local ReadBits = state.ReadBits
+
+ local is_last_block
+ while not is_last_block do
+ is_last_block = (ReadBits(1) == 1)
+ local block_type = ReadBits(2)
+ local status
+ if block_type == 0 then
+ status = DecompressStoreBlock(state)
+ elseif block_type == 1 then
+ status = DecompressFixBlock(state)
+ elseif block_type == 2 then
+ status = DecompressDynamicBlock(state)
+ else
+ return nil, -1 -- invalid block type (type == 3)
+ end
+ if status ~= 0 then
+ return nil, status
+ end
+ end
+
+ state.result_buffer[#state.result_buffer+1] =
+ table_concat(state.buffer, "", 1, state.buffer_size)
+ local result = table_concat(state.result_buffer)
+ return result
+end
+
+-- @see LibDeflate:DecompressDeflate(str)
+-- @see LibDeflate:DecompressDeflateWithDict(str, dictionary)
+local function DecompressDeflateInternal(str, dictionary)
+ local state = CreateDecompressState(str, dictionary)
+ local result, status = Inflate(state)
+ if not result then
+ return nil, status
+ end
+
+ local bitlen_left = state.ReaderBitlenLeft()
+ local bytelen_left = (bitlen_left - bitlen_left % 8) / 8
+ return result, bytelen_left
+end
+
+-- @see LibDeflate:DecompressZlib(str)
+-- @see LibDeflate:DecompressZlibWithDict(str)
+local function DecompressZlibInternal(str, dictionary)
+ local state = CreateDecompressState(str, dictionary)
+ local ReadBits = state.ReadBits
+
+ local CMF = ReadBits(8)
+ if state.ReaderBitlenLeft() < 0 then
+ return nil, 2 -- available inflate data did not terminate
+ end
+ local CM = CMF % 16
+ local CINFO = (CMF - CM) / 16
+ if CM ~= 8 then
+ return nil, -12 -- invalid compression method
+ end
+ if CINFO > 7 then
+ return nil, -13 -- invalid window size
+ end
+
+ local FLG = ReadBits(8)
+ if state.ReaderBitlenLeft() < 0 then
+ return nil, 2 -- available inflate data did not terminate
+ end
+ if (CMF*256+FLG)%31 ~= 0 then
+ return nil, -14 -- invalid header checksum
+ end
+
+ local FDIST = ((FLG-FLG%32)/32 % 2)
+ local FLEVEL = ((FLG-FLG%64)/64 % 4) -- luacheck: ignore FLEVEL
+
+ if FDIST == 1 then
+ if not dictionary then
+ return nil, -16 -- need dictonary, but dictionary is not provided.
+ end
+ local byte3 = ReadBits(8)
+ local byte2 = ReadBits(8)
+ local byte1 = ReadBits(8)
+ local byte0 = ReadBits(8)
+ local actual_adler32 = byte3*16777216+byte2*65536+byte1*256+byte0
+ if state.ReaderBitlenLeft() < 0 then
+ return nil, 2 -- available inflate data did not terminate
+ end
+ if not IsEqualAdler32(actual_adler32, dictionary.adler32) then
+ return nil, -17 -- dictionary adler32 does not match
+ end
+ end
+ local result, status = Inflate(state)
+ if not result then
+ return nil, status
+ end
+ state.SkipToByteBoundary()
+
+ local adler_byte0 = ReadBits(8)
+ local adler_byte1 = ReadBits(8)
+ local adler_byte2 = ReadBits(8)
+ local adler_byte3 = ReadBits(8)
+ if state.ReaderBitlenLeft() < 0 then
+ return nil, 2 -- available inflate data did not terminate
+ end
+
+ local adler32_expected = adler_byte0*16777216
+ + adler_byte1*65536 + adler_byte2*256 + adler_byte3
+ local adler32_actual = LibDeflate:Adler32(result)
+ if not IsEqualAdler32(adler32_expected, adler32_actual) then
+ return nil, -15 -- Adler32 checksum does not match
+ end
+
+ local bitlen_left = state.ReaderBitlenLeft()
+ local bytelen_left = (bitlen_left - bitlen_left % 8) / 8
+ return result, bytelen_left
+end
+
+--- Decompress a raw deflate compressed data.
+-- @param str [string] The data to be decompressed.
+-- @return [string/nil] If the decompression succeeds, return the decompressed
+-- data. If the decompression fails, return nil. You should check if this return
+-- value is non-nil to know if the decompression succeeds.
+-- @return [integer] If the decompression succeeds, return the number of
+-- unprocessed bytes in the input compressed data. This return value is a
+-- positive integer if the input data is a valid compressed data appended by an
+-- arbitary non-empty string. This return value is 0 if the input data does not
+-- contain any extra bytes.
+-- If the decompression fails (The first return value of this function is nil),
+-- this return value is undefined.
+-- @see LibDeflate:CompressDeflate
+function LibDeflate:DecompressDeflate(str)
+ local arg_valid, arg_err = IsValidArguments(str)
+ if not arg_valid then
+ error(("Usage: LibDeflate:DecompressDeflate(str): "
+ ..arg_err), 2)
+ end
+ return DecompressDeflateInternal(str)
+end
+
+--- Decompress a raw deflate compressed data with a preset dictionary.
+-- @param str [string] The data to be decompressed.
+-- @param dictionary [table] The preset dictionary used by
+-- LibDeflate:CompressDeflateWithDict when the compressed data is produced.
+-- Decompression and compression must use the same dictionary.
+-- Otherwise wrong decompressed data could be produced without generating any
+-- error.
+-- @return [string/nil] If the decompression succeeds, return the decompressed
+-- data. If the decompression fails, return nil. You should check if this return
+-- value is non-nil to know if the decompression succeeds.
+-- @return [integer] If the decompression succeeds, return the number of
+-- unprocessed bytes in the input compressed data. This return value is a
+-- positive integer if the input data is a valid compressed data appended by an
+-- arbitary non-empty string. This return value is 0 if the input data does not
+-- contain any extra bytes.
+-- If the decompression fails (The first return value of this function is nil),
+-- this return value is undefined.
+-- @see LibDeflate:CompressDeflateWithDict
+function LibDeflate:DecompressDeflateWithDict(str, dictionary)
+ local arg_valid, arg_err = IsValidArguments(str, true, dictionary)
+ if not arg_valid then
+ error(("Usage: LibDeflate:DecompressDeflateWithDict(str, dictionary): "
+ ..arg_err), 2)
+ end
+ return DecompressDeflateInternal(str, dictionary)
+end
+
+--- Decompress a zlib compressed data.
+-- @param str [string] The data to be decompressed
+-- @return [string/nil] If the decompression succeeds, return the decompressed
+-- data. If the decompression fails, return nil. You should check if this return
+-- value is non-nil to know if the decompression succeeds.
+-- @return [integer] If the decompression succeeds, return the number of
+-- unprocessed bytes in the input compressed data. This return value is a
+-- positive integer if the input data is a valid compressed data appended by an
+-- arbitary non-empty string. This return value is 0 if the input data does not
+-- contain any extra bytes.
+-- If the decompression fails (The first return value of this function is nil),
+-- this return value is undefined.
+-- @see LibDeflate:CompressZlib
+function LibDeflate:DecompressZlib(str)
+ local arg_valid, arg_err = IsValidArguments(str)
+ if not arg_valid then
+ error(("Usage: LibDeflate:DecompressZlib(str): "
+ ..arg_err), 2)
+ end
+ return DecompressZlibInternal(str)
+end
+
+--- Decompress a zlib compressed data with a preset dictionary.
+-- @param str [string] The data to be decompressed
+-- @param dictionary [table] The preset dictionary used by
+-- LibDeflate:CompressDeflateWithDict when the compressed data is produced.
+-- Decompression and compression must use the same dictionary.
+-- Otherwise wrong decompressed data could be produced without generating any
+-- error.
+-- @return [string/nil] If the decompression succeeds, return the decompressed
+-- data. If the decompression fails, return nil. You should check if this return
+-- value is non-nil to know if the decompression succeeds.
+-- @return [integer] If the decompression succeeds, return the number of
+-- unprocessed bytes in the input compressed data. This return value is a
+-- positive integer if the input data is a valid compressed data appended by an
+-- arbitary non-empty string. This return value is 0 if the input data does not
+-- contain any extra bytes.
+-- If the decompression fails (The first return value of this function is nil),
+-- this return value is undefined.
+-- @see LibDeflate:CompressZlibWithDict
+function LibDeflate:DecompressZlibWithDict(str, dictionary)
+ local arg_valid, arg_err = IsValidArguments(str, true, dictionary)
+ if not arg_valid then
+ error(("Usage: LibDeflate:DecompressZlibWithDict(str, dictionary): "
+ ..arg_err), 2)
+ end
+ return DecompressZlibInternal(str, dictionary)
+end
+
+-- Calculate the huffman code of fixed block
+do
+ _fix_block_literal_huffman_bitlen = {}
+ for sym=0, 143 do
+ _fix_block_literal_huffman_bitlen[sym] = 8
+ end
+ for sym=144, 255 do
+ _fix_block_literal_huffman_bitlen[sym] = 9
+ end
+ for sym=256, 279 do
+ _fix_block_literal_huffman_bitlen[sym] = 7
+ end
+ for sym=280, 287 do
+ _fix_block_literal_huffman_bitlen[sym] = 8
+ end
+
+ _fix_block_dist_huffman_bitlen = {}
+ for dist=0, 31 do
+ _fix_block_dist_huffman_bitlen[dist] = 5
+ end
+ local status
+ status, _fix_block_literal_huffman_bitlen_count
+ , _fix_block_literal_huffman_to_deflate_code =
+ GetHuffmanForDecode(_fix_block_literal_huffman_bitlen, 287, 9)
+ assert(status == 0)
+ status, _fix_block_dist_huffman_bitlen_count,
+ _fix_block_dist_huffman_to_deflate_code =
+ GetHuffmanForDecode(_fix_block_dist_huffman_bitlen, 31, 5)
+ assert(status == 0)
+
+ _fix_block_literal_huffman_code =
+ GetHuffmanCodeFromBitlen(_fix_block_literal_huffman_bitlen_count
+ , _fix_block_literal_huffman_bitlen, 287, 9)
+ _fix_block_dist_huffman_code =
+ GetHuffmanCodeFromBitlen(_fix_block_dist_huffman_bitlen_count
+ , _fix_block_dist_huffman_bitlen, 31, 5)
+end
+
+-- Encoding algorithms
+-- Prefix encoding algorithm
+-- implemented by Galmok of European Stormrage (Horde), galmok@gmail.com
+-- From LibCompress ,
+-- which is licensed under GPLv2
+-- The code has been modified by the author of LibDeflate.
+------------------------------------------------------------------------------
+
+-- to be able to match any requested byte value, the search
+-- string must be preprocessed characters to escape with %:
+-- ( ) . % + - * ? [ ] ^ $
+-- "illegal" byte values:
+-- 0 is replaces %z
+local _gsub_escape_table = {
+ ["\000"] = "%z", ["("] = "%(", [")"] = "%)", ["."] = "%.",
+ ["%"] = "%%", ["+"] = "%+", ["-"] = "%-", ["*"] = "%*",
+ ["?"] = "%?", ["["] = "%[", ["]"] = "%]", ["^"] = "%^",
+ ["$"] = "%$",
+}
+
+local function escape_for_gsub(str)
+ return str:gsub("([%z%(%)%.%%%+%-%*%?%[%]%^%$])", _gsub_escape_table)
+end
+
+--- Create a custom codec with encoder and decoder.
+-- This codec is used to convert an input string to make it not contain
+-- some specific bytes.
+-- This created codec and the parameters of this function do NOT take
+-- localization into account. One byte (0-255) in the string is exactly one
+-- character (0-255).
+-- Credits to LibCompress.
+-- @param reserved_chars [string] The created encoder will ensure encoded
+-- data does not contain any single character in reserved_chars. This parameter
+-- should be non-empty.
+-- @param escape_chars [string] The escape character(s) used in the created
+-- codec. The codec converts any character included in reserved\_chars /
+-- escape\_chars / map\_chars to (one escape char + one character not in
+-- reserved\_chars / escape\_chars / map\_chars).
+-- You usually only need to provide a length-1 string for this parameter.
+-- Length-2 string is only needed when
+-- reserved\_chars + escape\_chars + map\_chars is longer than 127.
+-- This parameter should be non-empty.
+-- @param map_chars [string] The created encoder will map every
+-- reserved\_chars:sub(i, i) (1 <= i <= #map\_chars) to map\_chars:sub(i, i).
+-- This parameter CAN be empty string.
+-- @return [table/nil] If the codec cannot be created, return nil.
+-- If the codec can be created according to the given
+-- parameters, return the codec, which is a encode/decode table.
+-- The table contains two functions:
+-- t:Encode(str) returns the encoded string.
+-- t:Decode(str) returns the decoded string if succeeds. nil if fails.
+-- @return [nil/string] If the codec is successfully created, return nil.
+-- If not, return a string that describes the reason why the codec cannot be
+-- created.
+-- @usage
+-- -- Create an encoder/decoder that maps all "\000" to "\003",
+-- -- and escape "\001" (and "\002" and "\003") properly
+-- local codec = LibDeflate:CreateCodec("\000\001", "\002", "\003")
+--
+-- local encoded = codec:Encode(SOME_STRING)
+-- -- "encoded" does not contain "\000" or "\001"
+-- local decoded = codec:Decode(encoded)
+-- -- assert(decoded == SOME_STRING)
+function LibDeflate:CreateCodec(reserved_chars, escape_chars
+ , map_chars)
+ -- select a default escape character
+ if type(reserved_chars) ~= "string"
+ or type(escape_chars) ~= "string"
+ or type(map_chars) ~= "string" then
+ error(
+ "Usage: LibDeflate:CreateCodec(reserved_chars,"
+ .." escape_chars, map_chars):"
+ .." All arguments must be string.", 2)
+ end
+
+ if escape_chars == "" then
+ return nil, "No escape characters supplied."
+ end
+ if #reserved_chars < #map_chars then
+ return nil, "The number of reserved characters must be"
+ .." at least as many as the number of mapped chars."
+ end
+ if reserved_chars == "" then
+ return nil, "No characters to encode."
+ end
+
+ local encode_bytes = reserved_chars..escape_chars..map_chars
+ -- build list of bytes not available as a suffix to a prefix byte
+ local taken = {}
+ for i = 1, #encode_bytes do
+ local byte = string_byte(encode_bytes, i, i)
+ if taken[byte] then -- Modified by LibDeflate:
+ return nil, "There must be no duplicate characters in the"
+ .." concatenation of reserved_chars, escape_chars and"
+ .." map_chars."
+ end
+ taken[byte] = true
+ end
+
+ -- Modified by LibDeflate:
+ -- Store the patterns and replacement in tables for later use.
+ -- This function is modified that loadstring() lua api is no longer used.
+ local decode_patterns = {}
+ local decode_repls = {}
+
+ -- the encoding can be a single gsub
+ -- , but the decoding can require multiple gsubs
+ local encode_search = {}
+ local encode_translate = {}
+
+ -- map single byte to single byte
+ if #map_chars > 0 then
+ local decode_search = {}
+ local decode_translate = {}
+ for i = 1, #map_chars do
+ local from = string_sub(reserved_chars, i, i)
+ local to = string_sub(map_chars, i, i)
+ encode_translate[from] = to
+ encode_search[#encode_search+1] = from
+ decode_translate[to] = from
+ decode_search[#decode_search+1] = to
+ end
+ decode_patterns[#decode_patterns+1] =
+ "([".. escape_for_gsub(table_concat(decode_search)).."])"
+ decode_repls[#decode_repls+1] = decode_translate
+ end
+
+ local escape_char_index = 1
+ local escape_char = string_sub(escape_chars
+ , escape_char_index, escape_char_index)
+ -- map single byte to double-byte
+ local r = 0 -- suffix char value to the escapeChar
+
+ local decode_search = {}
+ local decode_translate = {}
+ for i = 1, #encode_bytes do
+ local c = string_sub(encode_bytes, i, i)
+ if not encode_translate[c] then
+ -- this loop will update escapeChar and r
+ while r >= 256 or taken[r] do
+ -- Bug in LibCompress r81
+ -- while r < 256 and taken[r] do
+ r = r + 1
+ if r > 255 then -- switch to next escapeChar
+ decode_patterns[#decode_patterns+1] =
+ escape_for_gsub(escape_char)
+ .."(["
+ .. escape_for_gsub(table_concat(decode_search)).."])"
+ decode_repls[#decode_repls+1] = decode_translate
+
+ escape_char_index = escape_char_index + 1
+ escape_char = string_sub(escape_chars, escape_char_index
+ , escape_char_index)
+ r = 0
+ decode_search = {}
+ decode_translate = {}
+
+ -- Fixes Another bug in LibCompress r82.
+ -- LibCompress checks this error condition
+ -- right after "if r > 255 then"
+ -- This is why error case should also be tested.
+ if not escape_char or escape_char == "" then
+ -- actually I don't need to check
+ -- "not ecape_char", but what if Lua changes
+ -- the behavior of string.sub() in the future?
+ -- we are out of escape chars and we need more!
+ return nil, "Out of escape characters."
+ end
+ end
+ end
+
+ local char_r = _byte_to_char[r]
+ encode_translate[c] = escape_char..char_r
+ encode_search[#encode_search+1] = c
+ decode_translate[char_r] = c
+ decode_search[#decode_search+1] = char_r
+ r = r + 1
+ end
+ if i == #encode_bytes then
+ decode_patterns[#decode_patterns+1] =
+ escape_for_gsub(escape_char).."(["
+ .. escape_for_gsub(table_concat(decode_search)).."])"
+ decode_repls[#decode_repls+1] = decode_translate
+ end
+ end
+
+ local codec = {}
+
+ local encode_pattern = "(["
+ .. escape_for_gsub(table_concat(encode_search)).."])"
+ local encode_repl = encode_translate
+
+ function codec:Encode(str)
+ if type(str) ~= "string" then
+ error(("Usage: codec:Encode(str):"
+ .." 'str' - string expected got '%s'."):format(type(str)), 2)
+ end
+ return string_gsub(str, encode_pattern, encode_repl)
+ end
+
+ local decode_tblsize = #decode_patterns
+ local decode_fail_pattern = "(["
+ .. escape_for_gsub(reserved_chars).."])"
+
+ function codec:Decode(str)
+ if type(str) ~= "string" then
+ error(("Usage: codec:Decode(str):"
+ .." 'str' - string expected got '%s'."):format(type(str)), 2)
+ end
+ if string_find(str, decode_fail_pattern) then
+ return nil
+ end
+ for i = 1, decode_tblsize do
+ str = string_gsub(str, decode_patterns[i], decode_repls[i])
+ end
+ return str
+ end
+
+ return codec
+end
+
+local _addon_channel_codec
+
+local function GenerateWoWAddonChannelCodec()
+ return LibDeflate:CreateCodec("\000", "\001", "")
+end
+
+--- Encode the string to make it ready to be transmitted in World of
+-- Warcraft addon channel.
+-- The encoded string is guaranteed to contain no NULL ("\000") character.
+-- @param str [string] The string to be encoded.
+-- @return The encoded string.
+-- @see LibDeflate:DecodeForWoWAddonChannel
+function LibDeflate:EncodeForWoWAddonChannel(str)
+ if type(str) ~= "string" then
+ error(("Usage: LibDeflate:EncodeForWoWAddonChannel(str):"
+ .." 'str' - string expected got '%s'."):format(type(str)), 2)
+ end
+ if not _addon_channel_codec then
+ _addon_channel_codec = GenerateWoWAddonChannelCodec()
+ end
+ return _addon_channel_codec:Encode(str)
+end
+
+--- Decode the string produced by LibDeflate:EncodeForWoWAddonChannel
+-- @param str [string] The string to be decoded.
+-- @return [string/nil] The decoded string if succeeds. nil if fails.
+-- @see LibDeflate:EncodeForWoWAddonChannel
+function LibDeflate:DecodeForWoWAddonChannel(str)
+ if type(str) ~= "string" then
+ error(("Usage: LibDeflate:DecodeForWoWAddonChannel(str):"
+ .." 'str' - string expected got '%s'."):format(type(str)), 2)
+ end
+ if not _addon_channel_codec then
+ _addon_channel_codec = GenerateWoWAddonChannelCodec()
+ end
+ return _addon_channel_codec:Decode(str)
+end
+
+-- For World of Warcraft Chat Channel Encoding
+-- implemented by Galmok of European Stormrage (Horde), galmok@gmail.com
+-- From LibCompress ,
+-- which is licensed under GPLv2
+-- The code has been modified by the author of LibDeflate.
+-- Following byte values are not allowed:
+-- \000, s, S, \010, \013, \124, %
+-- Because SendChatMessage will error
+-- if an UTF8 multibyte character is incomplete,
+-- all character values above 127 have to be encoded to avoid this.
+-- This costs quite a bit of bandwidth (about 13-14%)
+-- Also, because drunken status is unknown for the received
+-- , strings used with SendChatMessage should be terminated with
+-- an identifying byte value, after which the server MAY add "...hic!"
+-- or as much as it can fit(!).
+-- Pass the identifying byte as a reserved character to this function
+-- to ensure the encoding doesn't contain that value.
+-- or use this: local message, match = arg1:gsub("^(.*)\029.-$", "%1")
+-- arg1 is message from channel, \029 is the string terminator
+-- , but may be used in the encoded datastream as well. :-)
+-- This encoding will expand data anywhere from:
+-- 0% (average with pure ascii text)
+-- 53.5% (average with random data valued zero to 255)
+-- 100% (only encoding data that encodes to two bytes)
+local function GenerateWoWChatChannelCodec()
+ local r = {}
+ for i = 128, 255 do
+ r[#r+1] = _byte_to_char[i]
+ end
+
+ local reserved_chars = "sS\000\010\013\124%"..table_concat(r)
+ return LibDeflate:CreateCodec(reserved_chars
+ , "\029\031", "\015\020")
+end
+
+local _chat_channel_codec
+
+--- Encode the string to make it ready to be transmitted in World of
+-- Warcraft chat channel.
+-- See also https://wow.gamepedia.com/ValidChatMessageCharacters
+-- @param str [string] The string to be encoded.
+-- @return [string] The encoded string.
+-- @see LibDeflate:DecodeForWoWChatChannel
+function LibDeflate:EncodeForWoWChatChannel(str)
+ if type(str) ~= "string" then
+ error(("Usage: LibDeflate:EncodeForWoWChatChannel(str):"
+ .." 'str' - string expected got '%s'."):format(type(str)), 2)
+ end
+ if not _chat_channel_codec then
+ _chat_channel_codec = GenerateWoWChatChannelCodec()
+ end
+ return _chat_channel_codec:Encode(str)
+end
+
+--- Decode the string produced by LibDeflate:EncodeForWoWChatChannel.
+-- @param str [string] The string to be decoded.
+-- @return [string/nil] The decoded string if succeeds. nil if fails.
+-- @see LibDeflate:EncodeForWoWChatChannel
+function LibDeflate:DecodeForWoWChatChannel(str)
+ if type(str) ~= "string" then
+ error(("Usage: LibDeflate:DecodeForWoWChatChannel(str):"
+ .." 'str' - string expected got '%s'."):format(type(str)), 2)
+ end
+ if not _chat_channel_codec then
+ _chat_channel_codec = GenerateWoWChatChannelCodec()
+ end
+ return _chat_channel_codec:Decode(str)
+end
+
+-- Credits to WeakAuras ,
+-- and Galmok (galmok@gmail.com) for the 6 bit encoding algorithm.
+-- The result of encoding will be 25% larger than the
+-- origin string, but every single byte of the encoding result will be
+-- printable characters as the following.
+local _byte_to_6bit_char = {
+ [0]="a", "b", "c", "d", "e", "f", "g", "h",
+ "i", "j", "k", "l", "m", "n", "o", "p",
+ "q", "r", "s", "t", "u", "v", "w", "x",
+ "y", "z", "A", "B", "C", "D", "E", "F",
+ "G", "H", "I", "J", "K", "L", "M", "N",
+ "O", "P", "Q", "R", "S", "T", "U", "V",
+ "W", "X", "Y", "Z", "0", "1", "2", "3",
+ "4", "5", "6", "7", "8", "9", "(", ")",
+}
+
+local _6bit_to_byte = {
+ [97]=0,[98]=1,[99]=2,[100]=3,[101]=4,[102]=5,[103]=6,[104]=7,
+ [105]=8,[106]=9,[107]=10,[108]=11,[109]=12,[110]=13,[111]=14,[112]=15,
+ [113]=16,[114]=17,[115]=18,[116]=19,[117]=20,[118]=21,[119]=22,[120]=23,
+ [121]=24,[122]=25,[65]=26,[66]=27,[67]=28,[68]=29,[69]=30,[70]=31,
+ [71]=32,[72]=33,[73]=34,[74]=35,[75]=36,[76]=37,[77]=38,[78]=39,
+ [79]=40,[80]=41,[81]=42,[82]=43,[83]=44,[84]=45,[85]=46,[86]=47,
+ [87]=48,[88]=49,[89]=50,[90]=51,[48]=52,[49]=53,[50]=54,[51]=55,
+ [52]=56,[53]=57,[54]=58,[55]=59,[56]=60,[57]=61,[40]=62,[41]=63,
+}
+
+--- Encode the string to make it printable.
+--
+-- Credis to WeakAuras2, this function is equivalant to the implementation
+-- it is using right now.
+-- The encoded string will be 25% larger than the origin string. However, every
+-- single byte of the encoded string will be one of 64 printable ASCII
+-- characters, which are can be easier copied, pasted and displayed.
+-- (26 lowercase letters, 26 uppercase letters, 10 numbers digits,
+-- left parenthese, or right parenthese)
+-- @param str [string] The string to be encoded.
+-- @return [string] The encoded string.
+function LibDeflate:EncodeForPrint(str)
+ if type(str) ~= "string" then
+ error(("Usage: LibDeflate:EncodeForPrint(str):"
+ .." 'str' - string expected got '%s'."):format(type(str)), 2)
+ end
+ local strlen = #str
+ local strlenMinus2 = strlen - 2
+ local i = 1
+ local buffer = {}
+ local buffer_size = 0
+ while i <= strlenMinus2 do
+ local x1, x2, x3 = string_byte(str, i, i+2)
+ i = i + 3
+ local cache = x1+x2*256+x3*65536
+ local b1 = cache % 64
+ cache = (cache - b1) / 64
+ local b2 = cache % 64
+ cache = (cache - b2) / 64
+ local b3 = cache % 64
+ local b4 = (cache - b3) / 64
+ buffer_size = buffer_size + 1
+ buffer[buffer_size] =
+ _byte_to_6bit_char[b1].._byte_to_6bit_char[b2]
+ .._byte_to_6bit_char[b3].._byte_to_6bit_char[b4]
+ end
+
+ local cache = 0
+ local cache_bitlen = 0
+ while i <= strlen do
+ local x = string_byte(str, i, i)
+ cache = cache + x * _pow2[cache_bitlen]
+ cache_bitlen = cache_bitlen + 8
+ i = i + 1
+ end
+ while cache_bitlen > 0 do
+ local bit6 = cache % 64
+ buffer_size = buffer_size + 1
+ buffer[buffer_size] = _byte_to_6bit_char[bit6]
+ cache = (cache - bit6) / 64
+ cache_bitlen = cache_bitlen - 6
+ end
+
+ return table_concat(buffer)
+end
+
+--- Decode the printable string produced by LibDeflate:EncodeForPrint.
+-- "str" will have its prefixed and trailing control characters or space
+-- removed before it is decoded, so it is easier to use if "str" comes form
+-- user copy and paste with some prefixed or trailing spaces.
+-- Then decode fails if the string contains any characters cant be produced by
+-- LibDeflate:EncodeForPrint. That means, decode fails if the string contains a
+-- characters NOT one of 26 lowercase letters, 26 uppercase letters,
+-- 10 numbers digits, left parenthese, or right parenthese.
+-- @param str [string] The string to be decoded
+-- @return [string/nil] The decoded string if succeeds. nil if fails.
+function LibDeflate:DecodeForPrint(str)
+ if type(str) ~= "string" then
+ error(("Usage: LibDeflate:DecodeForPrint(str):"
+ .." 'str' - string expected got '%s'."):format(type(str)), 2)
+ end
+ str = str:gsub("^[%c ]+", "")
+ str = str:gsub("[%c ]+$", "")
+
+ local strlen = #str
+ if strlen == 1 then
+ return nil
+ end
+ local strlenMinus3 = strlen - 3
+ local i = 1
+ local buffer = {}
+ local buffer_size = 0
+ while i <= strlenMinus3 do
+ local x1, x2, x3, x4 = string_byte(str, i, i+3)
+ x1 = _6bit_to_byte[x1]
+ x2 = _6bit_to_byte[x2]
+ x3 = _6bit_to_byte[x3]
+ x4 = _6bit_to_byte[x4]
+ if not (x1 and x2 and x3 and x4) then
+ return nil
+ end
+ i = i + 4
+ local cache = x1+x2*64+x3*4096+x4*262144
+ local b1 = cache % 256
+ cache = (cache - b1) / 256
+ local b2 = cache % 256
+ local b3 = (cache - b2) / 256
+ buffer_size = buffer_size + 1
+ buffer[buffer_size] =
+ _byte_to_char[b1].._byte_to_char[b2].._byte_to_char[b3]
+ end
+
+ local cache = 0
+ local cache_bitlen = 0
+ while i <= strlen do
+ local x = string_byte(str, i, i)
+ x = _6bit_to_byte[x]
+ if not x then
+ return nil
+ end
+ cache = cache + x * _pow2[cache_bitlen]
+ cache_bitlen = cache_bitlen + 6
+ i = i + 1
+ end
+
+ while cache_bitlen >= 8 do
+ local byte = cache % 256
+ buffer_size = buffer_size + 1
+ buffer[buffer_size] = _byte_to_char[byte]
+ cache = (cache - byte) / 256
+ cache_bitlen = cache_bitlen - 8
+ end
+
+ return table_concat(buffer)
+end
+
+local function InternalClearCache()
+ _chat_channel_codec = nil
+ _addon_channel_codec = nil
+end
+
+-- For test. Don't use the functions in this table for real application.
+-- Stuffs in this table is subject to change.
+LibDeflate.internals = {
+ LoadStringToTable = LoadStringToTable,
+ IsValidDictionary = IsValidDictionary,
+ IsEqualAdler32 = IsEqualAdler32,
+ _byte_to_6bit_char = _byte_to_6bit_char,
+ _6bit_to_byte = _6bit_to_byte,
+ InternalClearCache = InternalClearCache,
+}
+
+--[[-- Commandline options
+@class table
+@name CommandlineOptions
+@usage lua LibDeflate.lua [OPTION] [INPUT] [OUTPUT]
+\-0 store only. no compression.
+\-1 fastest compression.
+\-9 slowest and best compression.
+\-d do decompression instead of compression.
+\--dict specify the file that contains
+the entire preset dictionary.
+\-h give this help.
+\--strategy specify a special compression strategy.
+\-v print the version and copyright info.
+\--zlib use zlib format instead of raw deflate.
+]]
+
+-- currently no plan to support stdin and stdout.
+-- Because Lua in Windows does not set stdout with binary mode.
+if io and os and debug and _G.arg then
+ local io = io
+ local os = os
+ local debug = debug
+ local arg = _G.arg
+ local debug_info = debug.getinfo(1)
+ if debug_info.source == arg[0]
+ or debug_info.short_src == arg[0] then
+ -- We are indeed runnning THIS file from the commandline.
+ local input
+ local output
+ local i = 1
+ local status
+ local is_zlib = false
+ local is_decompress = false
+ local level
+ local strategy
+ local dictionary
+ while (arg[i]) do
+ local a = arg[i]
+ if a == "-h" then
+ print(LibDeflate._COPYRIGHT
+ .."\nUsage: lua LibDeflate.lua [OPTION] [INPUT] [OUTPUT]\n"
+ .." -0 store only. no compression.\n"
+ .." -1 fastest compression.\n"
+ .." -9 slowest and best compression.\n"
+ .." -d do decompression instead of compression.\n"
+ .." --dict specify the file that contains"
+ .." the entire preset dictionary.\n"
+ .." -h give this help.\n"
+ .." --strategy "
+ .." specify a special compression strategy.\n"
+ .." -v print the version and copyright info.\n"
+ .." --zlib use zlib format instead of raw deflate.\n")
+ os.exit(0)
+ elseif a == "-v" then
+ print(LibDeflate._COPYRIGHT)
+ os.exit(0)
+ elseif a:find("^%-[0-9]$") then
+ level = tonumber(a:sub(2, 2))
+ elseif a == "-d" then
+ is_decompress = true
+ elseif a == "--dict" then
+ i = i + 1
+ local dict_filename = arg[i]
+ if not dict_filename then
+ io.stderr:write("You must speicify the dict filename")
+ os.exit(1)
+ end
+ local dict_file, dict_status = io.open(dict_filename, "rb")
+ if not dict_file then
+ io.stderr:write(
+ ("LibDeflate: Cannot read the dictionary file '%s': %s")
+ :format(dict_filename, dict_status))
+ os.exit(1)
+ end
+ local dict_str = dict_file:read("*all")
+ dict_file:close()
+ -- In your lua program, you should pass in adler32 as a CONSTANT
+ -- , so it actually prevent you from modifying dictionary
+ -- unintentionally during the program development. I do this
+ -- here just because no convenient way to verify in commandline.
+ dictionary = LibDeflate:CreateDictionary(dict_str,
+ #dict_str, LibDeflate:Adler32(dict_str))
+ elseif a == "--strategy" then
+ -- Not sure if I should check error here
+ -- If I do, redudant code.
+ i = i + 1
+ strategy = arg[i]
+ elseif a == "--zlib" then
+ is_zlib = true
+ elseif a:find("^%-") then
+ io.stderr:write(("LibDeflate: Invalid argument: %s")
+ :format(a))
+ os.exit(1)
+ else
+ if not input then
+ input, status = io.open(a, "rb")
+ if not input then
+ io.stderr:write(
+ ("LibDeflate: Cannot read the file '%s': %s")
+ :format(a, tostring(status)))
+ os.exit(1)
+ end
+ elseif not output then
+ output, status = io.open(a, "wb")
+ if not output then
+ io.stderr:write(
+ ("LibDeflate: Cannot write the file '%s': %s")
+ :format(a, tostring(status)))
+ os.exit(1)
+ end
+ end
+ end
+ i = i + 1
+ end -- while (arg[i])
+
+ if not input or not output then
+ io.stderr:write("LibDeflate:"
+ .." You must specify both input and output files.")
+ os.exit(1)
+ end
+
+ local input_data = input:read("*all")
+ local configs = {
+ level = level,
+ strategy = strategy,
+ }
+ local output_data
+ if not is_decompress then
+ if not is_zlib then
+ if not dictionary then
+ output_data =
+ LibDeflate:CompressDeflate(input_data, configs)
+ else
+ output_data =
+ LibDeflate:CompressDeflateWithDict(input_data, dictionary
+ , configs)
+ end
+ else
+ if not dictionary then
+ output_data =
+ LibDeflate:CompressZlib(input_data, configs)
+ else
+ output_data =
+ LibDeflate:CompressZlibWithDict(input_data, dictionary
+ , configs)
+ end
+ end
+ else
+ if not is_zlib then
+ if not dictionary then
+ output_data = LibDeflate:DecompressDeflate(input_data)
+ else
+ output_data = LibDeflate:DecompressDeflateWithDict(
+ input_data, dictionary)
+ end
+ else
+ if not dictionary then
+ output_data = LibDeflate:DecompressZlib(input_data)
+ else
+ output_data = LibDeflate:DecompressZlibWithDict(
+ input_data, dictionary)
+ end
+ end
+ end
+
+ if not output_data then
+ io.stderr:write("LibDeflate: Decompress fails.")
+ os.exit(1)
+ end
+
+ output:write(output_data)
+ if input and input ~= io.stdin then
+ input:close()
+ end
+ if output and output ~= io.stdout then
+ output:close()
+ end
+
+ io.stderr:write(("Successfully writes %d bytes"):format(
+ output_data:len()))
+ os.exit(0)
+ end
+end
+
+return LibDeflate
diff --git a/Libs/LibDeflate/LibDeflate.toc b/Libs/LibDeflate/LibDeflate.toc
new file mode 100644
index 0000000..af5aeaf
--- /dev/null
+++ b/Libs/LibDeflate/LibDeflate.toc
@@ -0,0 +1,11 @@
+## Interface: 80205
+## Title: Lib: LibDeflate
+## Notes: Compressor and decompressor with high compression ratio using DEFLATE/zlib format.
+## Author: Haoqian He (WoW: Safetyy at Illidan-US (Horde))
+## Version: 1.0.1-release
+## X-Website: https://wow.curseforge.com/projects/libdeflate
+## X-Category: Library
+## X-License: LGPLv3
+
+LibStub\LibStub.lua
+lib.xml
diff --git a/Libs/LibDeflate/LibStub/LibStub.lua b/Libs/LibDeflate/LibStub/LibStub.lua
new file mode 100644
index 0000000..7e9b5cd
--- /dev/null
+++ b/Libs/LibDeflate/LibStub/LibStub.lua
@@ -0,0 +1,51 @@
+-- $Id: LibStub.lua 103 2014-10-16 03:02:50Z mikk $
+-- LibStub is a simple versioning stub meant for use in Libraries. http://www.wowace.com/addons/libstub/ for more info
+-- LibStub is hereby placed in the Public Domain
+-- Credits: Kaelten, Cladhaire, ckknight, Mikk, Ammo, Nevcairiel, joshborke
+local LIBSTUB_MAJOR, LIBSTUB_MINOR = "LibStub", 2 -- NEVER MAKE THIS AN SVN REVISION! IT NEEDS TO BE USABLE IN ALL REPOS!
+local LibStub = _G[LIBSTUB_MAJOR]
+
+-- Check to see is this version of the stub is obsolete
+if not LibStub or LibStub.minor < LIBSTUB_MINOR then
+ LibStub = LibStub or {libs = {}, minors = {} }
+ _G[LIBSTUB_MAJOR] = LibStub
+ LibStub.minor = LIBSTUB_MINOR
+
+ -- LibStub:NewLibrary(major, minor)
+ -- major (string) - the major version of the library
+ -- minor (string or number ) - the minor version of the library
+ --
+ -- returns nil if a newer or same version of the lib is already present
+ -- returns empty library object or old library object if upgrade is needed
+ function LibStub:NewLibrary(major, minor)
+ assert(type(major) == "string", "Bad argument #2 to `NewLibrary' (string expected)")
+ minor = assert(tonumber(strmatch(minor, "%d+")), "Minor version must either be a number or contain a number.")
+
+ local oldminor = self.minors[major]
+ if oldminor and oldminor >= minor then return nil end
+ self.minors[major], self.libs[major] = minor, self.libs[major] or {}
+ return self.libs[major], oldminor
+ end
+
+ -- LibStub:GetLibrary(major, [silent])
+ -- major (string) - the major version of the library
+ -- silent (boolean) - if true, library is optional, silently return nil if its not found
+ --
+ -- throws an error if the library can not be found (except silent is set)
+ -- returns the library object if found
+ function LibStub:GetLibrary(major, silent)
+ if not self.libs[major] and not silent then
+ error(("Cannot find a library instance of %q."):format(tostring(major)), 2)
+ end
+ return self.libs[major], self.minors[major]
+ end
+
+ -- LibStub:IterateLibraries()
+ --
+ -- Returns an iterator for the currently registered libraries
+ function LibStub:IterateLibraries()
+ return pairs(self.libs)
+ end
+
+ setmetatable(LibStub, { __call = LibStub.GetLibrary })
+end
diff --git a/Libs/LibDeflate/LibStub/LibStub.toc b/Libs/LibDeflate/LibStub/LibStub.toc
new file mode 100644
index 0000000..92e164f
--- /dev/null
+++ b/Libs/LibDeflate/LibStub/LibStub.toc
@@ -0,0 +1,9 @@
+## Interface: 70000
+## Title: Lib: LibStub
+## Notes: Universal Library Stub
+## Credits: Kaelten, Cladhaire, ckknight, Mikk, Ammo, Nevcairiel
+## X-Website: http://www.wowace.com/addons/libstub/
+## X-Category: Library
+## X-License: Public Domain
+
+LibStub.lua
diff --git a/Libs/LibDeflate/LibStub/tests/test.lua b/Libs/LibDeflate/LibStub/tests/test.lua
new file mode 100644
index 0000000..276ddab
--- /dev/null
+++ b/Libs/LibDeflate/LibStub/tests/test.lua
@@ -0,0 +1,41 @@
+debugstack = debug.traceback
+strmatch = string.match
+
+loadfile("../LibStub.lua")()
+
+local lib, oldMinor = LibStub:NewLibrary("Pants", 1) -- make a new thingy
+assert(lib) -- should return the library table
+assert(not oldMinor) -- should not return the old minor, since it didn't exist
+
+-- the following is to create data and then be able to check if the same data exists after the fact
+function lib:MyMethod()
+end
+local MyMethod = lib.MyMethod
+lib.MyTable = {}
+local MyTable = lib.MyTable
+
+local newLib, newOldMinor = LibStub:NewLibrary("Pants", 1) -- try to register a library with the same version, should silently fail
+assert(not newLib) -- should not return since out of date
+
+local newLib, newOldMinor = LibStub:NewLibrary("Pants", 0) -- try to register a library with a previous, should silently fail
+assert(not newLib) -- should not return since out of date
+
+local newLib, newOldMinor = LibStub:NewLibrary("Pants", 2) -- register a new version
+assert(newLib) -- library table
+assert(rawequal(newLib, lib)) -- should be the same reference as the previous
+assert(newOldMinor == 1) -- should return the minor version of the previous version
+
+assert(rawequal(lib.MyMethod, MyMethod)) -- verify that values were saved
+assert(rawequal(lib.MyTable, MyTable)) -- verify that values were saved
+
+local newLib, newOldMinor = LibStub:NewLibrary("Pants", "Blah 3 Blah") -- register a new version with a string minor version (instead of a number)
+assert(newLib) -- library table
+assert(newOldMinor == 2) -- previous version was 2
+
+local newLib, newOldMinor = LibStub:NewLibrary("Pants", "Blah 4 and please ignore 15 Blah") -- register a new version with a string minor version (instead of a number)
+assert(newLib)
+assert(newOldMinor == 3) -- previous version was 3 (even though it gave a string)
+
+local newLib, newOldMinor = LibStub:NewLibrary("Pants", 5) -- register a new library, using a normal number instead of a string
+assert(newLib)
+assert(newOldMinor == 4) -- previous version was 4 (even though it gave a string)
\ No newline at end of file
diff --git a/Libs/LibDeflate/LibStub/tests/test2.lua b/Libs/LibDeflate/LibStub/tests/test2.lua
new file mode 100644
index 0000000..eae7172
--- /dev/null
+++ b/Libs/LibDeflate/LibStub/tests/test2.lua
@@ -0,0 +1,27 @@
+debugstack = debug.traceback
+strmatch = string.match
+
+loadfile("../LibStub.lua")()
+
+for major, library in LibStub:IterateLibraries() do
+ -- check that MyLib doesn't exist yet, by iterating through all the libraries
+ assert(major ~= "MyLib")
+end
+
+assert(not LibStub:GetLibrary("MyLib", true)) -- check that MyLib doesn't exist yet by direct checking
+assert(not pcall(LibStub.GetLibrary, LibStub, "MyLib")) -- don't silently fail, thus it should raise an error.
+local lib = LibStub:NewLibrary("MyLib", 1) -- create the lib
+assert(lib) -- check it exists
+assert(rawequal(LibStub:GetLibrary("MyLib"), lib)) -- verify that :GetLibrary("MyLib") properly equals the lib reference
+
+assert(LibStub:NewLibrary("MyLib", 2)) -- create a new version
+
+local count=0
+for major, library in LibStub:IterateLibraries() do
+ -- check that MyLib exists somewhere in the libraries, by iterating through all the libraries
+ if major == "MyLib" then -- we found it!
+ count = count +1
+ assert(rawequal(library, lib)) -- verify that the references are equal
+ end
+end
+assert(count == 1) -- verify that we actually found it, and only once
diff --git a/Libs/LibDeflate/LibStub/tests/test3.lua b/Libs/LibDeflate/LibStub/tests/test3.lua
new file mode 100644
index 0000000..30f7b94
--- /dev/null
+++ b/Libs/LibDeflate/LibStub/tests/test3.lua
@@ -0,0 +1,14 @@
+debugstack = debug.traceback
+strmatch = string.match
+
+loadfile("../LibStub.lua")()
+
+local proxy = newproxy() -- non-string
+
+assert(not pcall(LibStub.NewLibrary, LibStub, proxy, 1)) -- should error, proxy is not a string, it's userdata
+local success, ret = pcall(LibStub.GetLibrary, proxy, true)
+assert(not success or not ret) -- either error because proxy is not a string or because it's not actually registered.
+
+assert(not pcall(LibStub.NewLibrary, LibStub, "Something", "No number in here")) -- should error, minor has no string in it.
+
+assert(not LibStub:GetLibrary("Something", true)) -- shouldn't've created it from the above statement
\ No newline at end of file
diff --git a/Libs/LibDeflate/LibStub/tests/test4.lua b/Libs/LibDeflate/LibStub/tests/test4.lua
new file mode 100644
index 0000000..43eb338
--- /dev/null
+++ b/Libs/LibDeflate/LibStub/tests/test4.lua
@@ -0,0 +1,41 @@
+debugstack = debug.traceback
+strmatch = string.match
+
+loadfile("../LibStub.lua")()
+
+
+-- Pretend like loaded libstub is old and doesn't have :IterateLibraries
+assert(LibStub.minor)
+LibStub.minor = LibStub.minor - 0.0001
+LibStub.IterateLibraries = nil
+
+loadfile("../LibStub.lua")()
+
+assert(type(LibStub.IterateLibraries)=="function")
+
+
+-- Now pretend that we're the same version -- :IterateLibraries should NOT be re-created
+LibStub.IterateLibraries = 123
+
+loadfile("../LibStub.lua")()
+
+assert(LibStub.IterateLibraries == 123)
+
+
+-- Now pretend that a newer version is loaded -- :IterateLibraries should NOT be re-created
+LibStub.minor = LibStub.minor + 0.0001
+
+loadfile("../LibStub.lua")()
+
+assert(LibStub.IterateLibraries == 123)
+
+
+-- Again with a huge number
+LibStub.minor = LibStub.minor + 1234567890
+
+loadfile("../LibStub.lua")()
+
+assert(LibStub.IterateLibraries == 123)
+
+
+print("OK")
\ No newline at end of file
diff --git a/Libs/LibDeflate/README.md b/Libs/LibDeflate/README.md
new file mode 100644
index 0000000..16ae102
--- /dev/null
+++ b/Libs/LibDeflate/README.md
@@ -0,0 +1,205 @@
+[![Build Status](https://www.travis-ci.org/SafeteeWoW/LibDeflate.svg?branch=master)](https://www.travis-ci.org/SafeteeWoW/LibDeflate)
+[![Build status](https://ci.appveyor.com/api/projects/status/owdccv4jrc0g1s2x/branch/master?svg=true&passingText=Windows%20Build%20passing&failingText=Windows%20Build%20failing)](https://ci.appveyor.com/project/SafeteeWoW/libdeflate/branch/master)
+[![AppVeyor tests branch](https://img.shields.io/appveyor/tests/SafeteeWoW/LibDeflate/master.svg)](https://ci.appveyor.com/project/SafeteeWoW/libdeflate/branch/master)
+[![codecov.io](http://codecov.io/github/safeteeWoW/LibDeflate/branch/master/graphs/badge.svg)](http://codecov.io/github/safeteeWoW/LibDeflate)
+[![license](https://img.shields.io/github/license/SafeteeWoW/LibDeflate)](LICENSE.txt)
+[![LuaRocks](https://img.shields.io/luarocks/v/SafeteeWoW/libdeflate)](http://luarocks.org/modules/SafeteeWoW/libdeflate)
+[![GitHub issues](https://img.shields.io/github/issues/SafeteeWoW/LibDeflate)](https://github.com/SafeteeWoW/LibDeflate/issues)
+
+# LibDeflate
+## Pure Lua compressor and decompressor with high compression ratio using DEFLATE/zlib format.
+
+Copyright (C) 2018-2019 Haoqian He
+
+## Introduction
+LibDeflate is pure Lua compressor and decompressor with high compression ratio,
+which compresses almost as good as [zlib](https://github.com/madler/zlib). The
+purpose of this project is to give a reasonable good compression when you only
+have access to a pure Lua environment, without accessing to Lua C bindings or
+any external Lua libraries. LibDeflate does not have any dependencies except you
+need to have a working Lua interpreter.
+
+LibDeflate uses the following compression formats:
+1. *DEFLATE*, as defined by the specification
+[RFC1951](https://tools.ietf.org/html/rfc1951). DEFLATE is the default compression method of ZIP.
+2. *zlib*, as defined by the specification
+[RFC1950](https://tools.ietf.org/html/rfc1950).
+zlib format uses DEFLATE formats to compress data and adds several bytes as
+headers and checksum.
+
+A simple C program utilizing [zlib](https://github.com/madler/zlib) should be
+compatible with LibDeflate. If you are not sure how to write this program,
+goto the [zlib](https://github.com/madler/zlib) repository, or read
+[tests/zdeflate.c](https://github.com/SafeteeWoW/LibDeflate/blob/master/tests/zdeflate.c) in this repository.
+
+## Supported Lua Versions
+LibDeflate supports and is fully tested under Lua 5.1/5.2/5.3/5.4, LuaJIT 2.0/2.1,
+for Linux, MaxOS and Windows. Click the Travis CI(Linux/MaxOS) and
+Appveyor(Windows) badge on the top of this README for the test results. Click
+the CodeCov badge to see the test coverage (should be 100%).
+Note at the time of this release, Lua 5.4 final is not released yet.
+For Lua 5.4, This library is tested with its first beta version.
+
+## Documentation
+[Documentation](https://safeteewow.github.io/LibDeflate/source/LibDeflate.lua.html) is hosted on Github.
+Beside run as a library, LibDeflate can also be run directly in commmandline.
+See the documentation for detail.
+
+## Limitation
+Though many performance optimization has been done in the source code, as a
+pure Lua implementation, the compression speed of LibDeflate is significantly
+slower than a C compressor. LibDeflate aims to compress small files, and it is
+suggested to not compress files with the order of several Megabytes. If you
+need to compress files hundreds of MetaBytes, please use a C compressor, or a
+Lua compressor with C binding.
+
+## Performance
+Below is a simple benchmark compared with another pure Lua compressor [LibCompress](https://www.wowace.com/projects/libcompress).
+More benchmarks can be viewed in the [documentation](https://safeteewow.github.io/LibDeflate/topics/benchmark.md.html).
+
++ Interpreter: Lua 5.1.5
++ Input data: [WeakAuras2 String](https://raw.githubusercontent.com/SafeteeWoW/LibDeflate/master/tests/data/warlockWeakAuras.txt), Size: 132462 bytes
+
+
+
+
+ |
+LibDeflate |
+LibDeflate |
+LibDeflate |
+LibCompress |
+LibCompress |
+LibCompress |
+
+
+
+
+ |
+CompressDeflate Level 1 |
+CompressDeflate Level 5 |
+CompressDeflate Level 8 |
+Compress |
+CompressLZW |
+CompressHuffman |
+
+
+compress ratio |
+3.15 |
+3.68 |
+3.71 |
+1.36 |
+1.20 |
+1.36 |
+
+
+compress time(ms) |
+70 |
+120 |
+200 |
+127 |
+58 |
+64 |
+
+
+decompress time(ms) |
+35 |
+32 |
+32 |
+62 |
+36 |
+62 |
+
+
+compress+decompress time(ms) |
+105 |
+152 |
+232 |
+189 |
+94 |
+126 |
+
+
+
+
+
+LibDeflate with compression level 1 compresses as fast as LibCompress, but already produces significantly smaller data than LibCompress. High compression level takes a bit more time to get better compression.
+
+## Download And Install
+
++ The [official repository](https://github.com/SafeteeWoW/LibDeflate) locates on Github.
+[LibDeflate.lua](https://github.com/SafeteeWoW/LibDeflate/blob/master/LibDeflate.lua) is the only file of LibDeflate. Copy the file
+to your LUA_PATH to install it.
+
++ To download as a World of Warcraft library, goto [LibDeflate Curseforge Page](https://wow.curseforge.com/projects/libdeflate)
+
++ You can also install via Luarocks using the command "luarocks install libdeflate"
+
++ To use after installation, ```require("LibDeflate")``` (case sensitive) in your Lua interpreter,
+or ```LibStub:GetLibrary("LibDeflate")``` (case sensitive) for World of Warcraft.
+
+
+## Usage
+```
+local LibDeflate
+if LibStub then -- You are using LibDeflate as WoW addon
+ LibDeflate = LibStub:GetLibrary("LibDeflate")
+else
+ LibDeflate = require("LibDeflate")
+end
+
+local example_input = "12123123412345123456123456712345678123456789"
+
+--- Compress using raw deflate format
+local compress_deflate = LibDeflate:CompressDeflate(example_input)
+
+-- decompress
+local decompress_deflate = LibDeflate:DecompressDeflate(compress_deflate)
+
+-- Check if the first return value of DecompressXXXX is non-nil to know if the
+-- decompression succeeds.
+if decompress_deflate == nil then
+ error("Decompression fails.")
+else
+ -- Decompression succeeds.
+ assert(example_input == decompress_deflate)
+end
+
+
+-- To transmit through WoW addon channel, data must be encoded so NULL ("\000")
+-- is not in the data.
+local data_to_trasmit_WoW_addon = LibDeflate:EncodeForWoWAddonChannel(
+ compress_deflate)
+-- When the receiver gets the data, decoded it first.
+local data_decoded_WoW_addon = LibDeflate:DecodeForWoWAddonChannel(
+ data_to_trasmit_WoW_addon)
+-- Then decomrpess it
+local decompress_deflate = LibDeflate:DecompressDeflate(data_decoded_WoW_addon)
+
+assert(decompress_deflate == example_input)
+
+-- The compressed output is not printable. EncodeForPrint will convert to
+-- a printable format, in case you want to export to the user to
+-- copy and paste. This encoding will make the data 25% bigger.
+local printable_compressed = LibDeflate:EncodeForPrint(compress_deflate)
+
+-- DecodeForPrint to convert back.
+-- DecodeForPrint will remove prefixed and trailing control or space characters
+-- in the string before decode it.
+assert(LibDeflate:DecodeForPrint(printable_compressed) == compress_deflate)
+```
+See Full examples in [examples/example.lua](https://github.com/SafeteeWoW/LibDeflate/blob/master/examples/example.lua)
+
+## License
+LibDeflate is licensed under GNU Lesser General Public License Version 3 or later.
+
+## Credits and Disclaimer
+
+The following projects are used to the help to test the correctness
+of this program. The code of the main program (LibDeflate.lua) does not
+use their code directly, but uses their ideas and algorithms. Their original
+licenses shall be comply when used.
+
+1. [zlib](http://www.zlib.net), by Jean-loup Gailly (compression) and Mark Adler (decompression). Licensed under [zlib License](http://www.zlib.net/zlib_license.html).
+2. [puff](https://github.com/madler/zlib/tree/master/contrib/puff), by Mark Adler. Licensed under zlib License.
+3. [LibCompress](https://www.wowace.com/projects/libcompress), by jjsheets and Galmok of European Stormrage (Horde). Licensed under GPLv2.
+4. [WeakAuras2](https://github.com/WeakAuras/WeakAuras2). Licensed under GPLv2.
\ No newline at end of file
diff --git a/Libs/LibDeflate/changelog.md b/Libs/LibDeflate/changelog.md
new file mode 100644
index 0000000..88a6656
--- /dev/null
+++ b/Libs/LibDeflate/changelog.md
@@ -0,0 +1,50 @@
+### v1.0.1-release
+
+* 2019/11/18
+* No functional change
+* Bump the World of Warcraft toc version to 80205
+* No longer "Load on Demand" in Warcraft toc, because this library does not consume much memory. This makes easier to load and test this library.
+* Change the license to LGPLv3 (Formerly GPLv3)
+
+
+### v1.0.0-release
+
+* 2018/7/30
+* Documentation updates.
+
+### v0.9.0-beta4
+
+* 2018/5/25
+* "DecodeForPrint" always remove prefixed or trailing control or space characters before decoding. This makes this API easier to use.
+
+### v0.9.0-beta3
+
+* 2018/5/23
+* Fix an issue in "DecodeForPrint" that certain undecodable string
+ could cause an Lua error.
+* Add an parameter to "DecodeForPrint". If set, remove trailing spaces in the
+input string before decode it.
+* Add input type checks for all encode/decode functions.
+
+### v0.9.0-beta2
+
+* 2018/5/22
+* API "Encode6Bit" is renamed to "EncodeForPrint"
+* API "Decode6Bit" is renamed to "DecodeForPrint"
+
+### v0.9.0-beta1
+
+* 2018/5/22
+* No change
+
+### v0.9.0-alpha2
+
+* 2018/5/21
+* Remove API LibDeflate:VerifyDictionary
+* Remove API LibDeflate:DictForWoW
+* Changed API LibDeflate:CreateDictionary
+
+### v0.9.0-alpha1
+
+* 2018/5/20
+* The first working version.
diff --git a/Libs/LibDeflate/lib.xml b/Libs/LibDeflate/lib.xml
new file mode 100644
index 0000000..0c5cc49
--- /dev/null
+++ b/Libs/LibDeflate/lib.xml
@@ -0,0 +1,4 @@
+
+
+
diff --git a/Libs/LibStub/LibStub.lua b/Libs/LibStub/LibStub.lua
new file mode 100644
index 0000000..0a41ac0
--- /dev/null
+++ b/Libs/LibStub/LibStub.lua
@@ -0,0 +1,30 @@
+-- LibStub is a simple versioning stub meant for use in Libraries. http://www.wowace.com/wiki/LibStub for more info
+-- LibStub is hereby placed in the Public Domain Credits: Kaelten, Cladhaire, ckknight, Mikk, Ammo, Nevcairiel, joshborke
+local LIBSTUB_MAJOR, LIBSTUB_MINOR = "LibStub", 2 -- NEVER MAKE THIS AN SVN REVISION! IT NEEDS TO BE USABLE IN ALL REPOS!
+local LibStub = _G[LIBSTUB_MAJOR]
+
+if not LibStub or LibStub.minor < LIBSTUB_MINOR then
+ LibStub = LibStub or {libs = {}, minors = {} }
+ _G[LIBSTUB_MAJOR] = LibStub
+ LibStub.minor = LIBSTUB_MINOR
+
+ function LibStub:NewLibrary(major, minor)
+ assert(type(major) == "string", "Bad argument #2 to `NewLibrary' (string expected)")
+ minor = assert(tonumber(strmatch(minor, "%d+")), "Minor version must either be a number or contain a number.")
+
+ local oldminor = self.minors[major]
+ if oldminor and oldminor >= minor then return nil end
+ self.minors[major], self.libs[major] = minor, self.libs[major] or {}
+ return self.libs[major], oldminor
+ end
+
+ function LibStub:GetLibrary(major, silent)
+ if not self.libs[major] and not silent then
+ error(("Cannot find a library instance of %q."):format(tostring(major)), 2)
+ end
+ return self.libs[major], self.minors[major]
+ end
+
+ function LibStub:IterateLibraries() return pairs(self.libs) end
+ setmetatable(LibStub, { __call = LibStub.GetLibrary })
+end
diff --git a/Libs/changelog.txt b/Libs/changelog.txt
new file mode 100644
index 0000000..3325a5d
--- /dev/null
+++ b/Libs/changelog.txt
@@ -0,0 +1,427 @@
+Ace3 Release - Revision r1227 (August 12th, 2019)
+-------------------------------------------------
+- AceConfigDialog-3.0: Fixed an issue with a missing template on WoW Classic (Ticket #517)
+
+Ace3 Release - Revision r1224 (August 1st, 2019)
+------------------------------------------------
+- AceConfigDialog-3.0: Replace use of StaticPopup with a custom frame to minimize taint
+- AceGUI-3.0: Ensure the OnLeave callback on various widgets still fires when its being released while visible (Ticket #505)
+- AceGUI-3.0: Various fixes for anchoring problems in WoW 8.2 (Ticket #512, and other reports)
+
+Ace3 Release - Revision r1214 (June 26th, 2019)
+-----------------------------------------------
+- AceConfigDialog-3.0: Exposed "select" type sorting control through a new "sorting" config table member
+- AceConfigDialog-3.0: Clear existing anchors on the GameTooltip before re-anchoring it for 8.2 compatibility
+- AceConfigDialog-3.0: Improved options table sorting algorithm to produce a stable result in certain edge cases (Ticket #501)
+- AceConfigRegistry-3.0: Added missing custom controls entries for all types added in the last release (Ticket #494)
+- AceGUI-3.0: ColorPicker: Removed usage of Show/HideUIPanel for WoW 8.2 compatibiliy
+- AceGUI-3.0: DropDown: Improved dropdown sorting algorithm to support tables with mixed string and numeric keys
+
+Ace3 Release - Revision r1200 (January 24th, 2019)
+--------------------------------------------------
+- Cleanup for WoW 8.0 improvements
+- AceGUI-3.0: Worked around various quirks in the WoW 8.0/8.1 API
+- AceGUI-3.0: ColorPicker: Store references to the background/checkers textures (Ticket #473)
+- AceConfigDialog-3.0: Allow custom controls for almost all option types (Ticket #475)
+
+Ace3 Release - Revision r1182 (July 17th, 2018)
+-----------------------------------------------
+- AceComm-3.0: Support numeric targets for custom channels (Ticket #419)
+- AceComm-3.0/ChatThrottleLib: Updated for WoW 8.0 API changes
+- AceConfig-3.0: Allow number values for the "width" parameter, which acts as a multiplier of the default width
+- AceConfigDialog-3.0: Enable iconCoords for the main level of a tree group (Ticket #417)
+- AceGUI-3.0: Implement a Table Layout (#442)
+- AceGUI-3.0: EditBox: Only try to handle dragging if a valid object is being dragged into the text box
+- AceGUI-3.0: Various fixes and work-arounds for WoW 8.0 changes
+
+Ace3 Release - Revision r1166 (August 29th, 2017)
+-------------------------------------------------
+- Updated any use of PlaySound to the new numeric constants (Fix for WoW 7.3)
+- AceConfigDialog-3.0: implement fallback validation feedback in a StaticPopup (Ticket #2)
+
+Ace3 Release - Revision r1158 (March 28th, 2017)
+------------------------------------------------
+- AceGUI-3.0: Fire OnShow events from Frame and Windows containers (Ticket #396)
+- AceGUI-3.0: Add SetJustifyV/H API to the Label/InteractiveLabel widgets (Ticket #397)
+
+Ace3 Release - Revision r1151 (July 18th, 2016)
+-----------------------------------------------
+- AceConfig-3.0: Allow specifying images using the numeric fileID (Ticket #389)
+- AceGUI-3.0: Use SetColorTexture in WoW 7.0
+- AceGUI-3.0: Expose the HighlightText API for EditBox and MultiLineEditBox Widgets (Ticket #378)
+- AceGUI-3.0: Keybinding: Support MouseWheel Up/Down bindings (Ticket #372)
+- AceGUI-3.0: TreeGroup: Refresh the tree in an OnUpdate once after Acquire, not only after creation (Fixes missing tree in WoW 7.0)
+- AceSerializer-3.0: Create consistent tokens for infinity, independent of the clients platform
+
+Ace3 Release - Revision r1134 (June 23rd, 2015)
+-----------------------------------------------
+- AceGUI-3.0: TreeGroup: Prevent Word Wrap in tree elements
+
+Ace3 Release - Revision r1128 (February 24th, 2015)
+---------------------------------------------------
+- AceGUI-3.0: Fixed an error in GameTooltip handling causing wrong formatting in some cases
+
+Ace3 Release - Revision r1122 (October 14th, 2014)
+--------------------------------------------------
+- AceDB-3.0: Now using the GetCurrentRegion() API to determine region-based profile keys (fix for WoW 6.0)
+- AceComm-3.0: Update ChatThrottleLib to v23
+- AceConfigDialog-3.0: Show a more meaningful title on Blizzard Options Groups (Ticket #353)
+- AceGUI-3.0: ColorPicker: Frame Level is automatically increased to ensure the color picker frame is above the option panel
+- AceGUI-3.0: DropDown: Properly disable all clickable areas when the dropdown is disabled (Ticket #360)
+- AceHook-3.0: Always use HookScript when hooking scripts in a secure fashion (Ticket #338)
+- AceTimer-3.0: New timer implementation based on C_Timer.After
+
+Ace3 Release - Revision r1109 (February 19th, 2014)
+---------------------------------------------------
+- AceComm-3.0: Ambiguate addon comm messages to restore behavior to be identical to pre-5.4.7
+- AceConfigRegistry-3.0: Added an option to skip options table validation on registering
+
+Ace3 Release - Revision r1104 (October 31st, 2013)
+--------------------------------------------------
+- AceGUI-3.0: Flow Layout: Added a safeguard to prevent an infinite loop in the layout engine
+- AceGUI-3.0: DropDown: Adjust its style slightly to closer resemble the Blizzard DropDown Widget
+- AceGUI-3.0: DropDown: API enhancements to specify the width of the pullout and be notified when its opened
+
+Ace3 Release - Revision r1098 (September 13th, 2013)
+----------------------------------------------------
+- AceDB-3.0: Switch characters to the default profile if their active profile is deleted (Ticket #324)
+- AceConfigDialog-3.0: Try to prevent static popup taint (Ticket #322)
+- AceGUI-3.0: Button: Add a new "Auto Width" option (Ticket #310)
+- AceGUI-3.0: DropDown: Make the entire DropDown widget clickable (Ticket #339)
+- AceGUI-3.0: EditBox: Allow dragging macros to the editbox (which will then contain the macros name) (Ticket #337)
+- AceGUI-3.0: Slider: Add a workaround for the broken slider steps in WoW 5.4 (Ticket #346)
+- AceGUI-3.0: TreeGroup: Fix an issue introduced by 5.4 broken scrollbars (Ticket #345)
+- AceHook-3.0: Allow hooking of AnimationGroup scripts (Ticket #314)
+
+Ace3 Release - Revision r1086 (May 21st, 2013)
+----------------------------------------------
+- AceAddon-3.0: Improved behavior when loading modules during game startup, ensures proper loading order
+
+Ace3 Release - Revision r1083 (March 4th, 2013)
+-----------------------------------------------
+- AceTimer-3.0: Fixed an issue that caused the parameter list passed to the callback to be cut off at nil values
+- AceGUI-3.0: InlineGroup: The title attribute is properly reset for recycled objects
+
+Ace3 Release - Revision r1078 (February 10th, 2013)
+---------------------------------------------------
+- AceTimer-3.0: Re-write based on AnimationTimers
+- AceHook-3.0: Improved checks/error messages when trying to hook a script on a "nil" frame
+- AceDBOptions-3.0: Added Italian locale
+- AceGUI-3.0: BlizOptionsGroup: Fixed the "default" button callback
+- AceGUI-3.0: Colorpicker: The colorpicker is now clamped to the screen
+
+Ace3 Release - Revision r1061 (August 27th, 2012)
+-------------------------------------------------
+- AceConfigDialog-3.0: Try to avoid potential taints in static popup dialogs
+- AceConfigDialog-3.0: Sort multiselects with "radio" style
+- AceGUI-3.0: Support for WoW 5.0
+- AceGUI-3.0: MultiLineEditBox: Support shift-click'ing items/spells/etc. into the editbox
+- AceGUI-3.0: Label: Fix text alignment (Ticket #301)
+- AceGUI-3.0: Checkbox: Description text on a disable checkbox should look disabled (Ticket #304)
+- AceGUI-3.0: Keybinding: Ensure the Keybinding popup is on the top level (Ticket #305)
+
+Ace3 Release - Revision r1041 (November 29th, 2011)
+---------------------------------------------------
+- AceDB-3.0: Added locale and factionrealmregion profile keys
+- AceSerializer-3.0: Removed support for NaN, as WoW 4.3 does no longer allow it.
+- AceGUI-3.0: Frame: Add :EnableResize (Ticket #214)
+
+Ace3 Release - Revision r1032 (June 29th, 2011)
+-----------------------------------------------
+- AceTab-3.0: Improvements to Match handling (Ticket #255 and #256)
+- AceGUI-3.0: DropDown layout fix with hidden labels (Ticket #234)
+
+Ace3 Release - Revision r1025 (April 27th, 2011)
+------------------------------------------------
+- AceComm-3.0: Updated for 4.1 changes - now handles RegisterAddonMessagePrefix internally for you.
+- AceGUI-3.0: TabGroup: Fixed width of tabs in 4.1(Ticket #243)
+
+Ace3 Release - Revision r1009 (February 9th, 2011)
+--------------------------------------------------
+- AceLocale-3.0: Fix erronous assumption that the default locale is always the first to be registered for the :NewLocale() "silent" flag. The flag must now be set on the FIRST locale to be registered.
+- AceLocale-3.0: The :NewLocale() "silent" flag may now be set to the string "raw", meaning nils are returned for unknown translations.
+- AceGUI-3.0: Fix the disabled state of Icon widgets
+- AceGUI-3.0: The header of the Frame widget now dynamically changes size to fit the text (Ticket #171)
+- AceGUI-3.0: Its now possible to define a custom order the elements in a dropdown widget
+- AceGUI-3.0: Improved widget focus behaviour across the board (Ticket #192, #193)
+- AceGUI-3.0: Fixed a bug that made it impossible to block the tree widget from being user resizable (Ticket #163)
+- AceGUI-3.0: Fixed a bug that caused TreeGroups to become unresponsive under certain conditions (Ticket #189, #202)
+- AceGUI-3.0: Enhanced the DropDown widget to allow it to be reused more easily.
+- AceConfigDialog-3.0: Select Groups now have the proper order in the dropdown (Ticket #184)
+- AceConfigDialog-3.0: Implemented "radio" style select boxes (Ticket #149)
+
+Ace3 Release - Revision r981 (October 27th, 2010)
+-------------------------------------------------
+- AceAddon-3.0: Fixed a library upgrading issue
+- AceAddon-3.0: Modules are now enabled in loading order
+- AceGUI-3.0: Keybinding: The widget will no longer steal keybindings even when inactive (Ticket #169)
+- AceGUI-3.0: EditBox: Fixed spell drag'n'drop
+
+
+Ace3 Release - Revision r971 (October 12th, 2010)
+-------------------------------------------------
+- Small fixes and adjustments for the 4.0 Content Patch.
+- AceGUI-3.0: ScrollFrame: Allow for a small margin of error when determining if the scroll bar should be shown.
+- AceGUI-3.0: Added new widget APIs: GetText for EditBox and DisableButton for MultiLineEditBox
+
+Ace3 Release - Revision r960 (July 20th, 2010)
+----------------------------------------------
+- AceGUI-3.0: Label: Reset Image Size and TexCoords on Acquire (Ticket #110)
+- AceGUI-3.0: CheckBox: Re-apply the disabled state after setting a value, so the visuals are correct in either case. (Ticket #107)
+- AceGUI-3.0: Icon: Fix the vertical size. It'll now properly scale with the image size, and not be fixed to about 110px. (Ticket #104)
+- AceGUI-3.0: External Containers (Frame, Window) should always start in a visible state. (Ticket #121)
+- AceGUI-3.0: Added Blizzard sounds to widgets (Ticket #120)
+- AceGUI-3.0: CheckBox: check for self.desc:GetText() being nil as well as "" to prevent setting the wrong height on the checkbox causing bouncing checkboxes.
+- AceGUI-3.0: Rewrite of the MultiLineEditBox (Ticket #68)
+- AceGUI-3.0: CheckBox: Fix alignment of the text in OnMouseDown when an image is set. (Ticket #142)
+- AceGUI-3.0: Add SetMaxLetters APIs to EB and MLEB (Ticket #135)
+- AceGUI-3.0: Frame: Add events for OnEnter/OnLeave of the statusbar (Ticket #139)
+- AceGUI-3.0: Major cleanups and refactoring in nearly all widgets and containers.
+- AceConfigDialog-3.0: Always obey the min/max values on range-type widgets (Ticket #114)
+- AceConfigDialog-3.0: Pass iconCoords set on groups in the options table to the tree widget (Ticket #111)
+- AceConfigDialog-3.0: Implement "softMin" and "softMax", allowing for a UI-specific minimum/maximum for range controls, while allowing manual input of values in the old min/max range. (Ticket #123)
+- AceConfigDialog-3.0: Don't close frames in CloseAll that are being opened after the CloseAll event was dispatched. (Ticket #132).
+- AceSerializer-3.0: Fix encoding & decoding of \030. (Ticket #115)
+- AceDB-3.0: Remove empty sections on logout, keeping the SV clean of more useless informations.
+- AceDBOptions-3.0.lua: Fix a string typo (Ticket #141)
+
+Ace3 Release - Revision r907 (December 16th, 2009)
+---------------------------------------------------
+- AceGUI-3.0: Frame: Properly save the width in the status table.
+- AceConfigCmd-3.0: Properly handle help output of inline groups with a different handler. (Ticket #101)
+- AceConfigDialog-3.0: Don't bail out and error when a dialogControl was invalid, instead show the error and fallback to the default control for that type.
+- AceConfigDialog-3.0: Fix a hickup with the OnUpdate script not getting upgraded properly in some situations.
+
+Ace3 Release - Revision r900 (December 8th, 2009)
+--------------------------------------------------
+- AceGUI-3.0: Alot of visual fixes regarding margins and general widget styles.
+- AceGUI-3.0: Ability to accept links for EditBox Widget (Ticket #21)
+- AceGUI-3.0: ScrollFrame: Hide the scrollbar when there is no overflowing content, and allow the Layout functions to use that space for widgets.
+- AceGUI-3.0: DropDown: Added a GetValue() API to the Widget (Ticket #69)
+- AceGUI-3.0: Button: Pass the arguments of the OnClick handler to the OnClick callback (Ticket #57)
+- AceGUI-3.0: add a Window container, basically a plain window with close button
+- AceGUI-3.0: Add support for inline descriptions to the checkbox widget.
+- AceGUI-3.0: Added an API to the Window container to disable the user-resizing of the same. (Ticket #80)
+- AceGUI-3.0: TreeGroup: Allow iconCoords to be passed for the tree elements. (Ticket #59)
+- AceGUI-3.0: Slider: Add a more visible backdrop/border around the manual input area (Ticket #98, #46)
+- AceGUI-3.0: Allow displaying a image in front of the checkbox label. (Ticket #82)
+- AceConfig-3.0: Added an experimental "descStyle" member to all option table nodes that allows you to control the way the description is presented.
+ Supported values are "tooltip" for the old behaviour, and "inline" for a inline display of the description, pending support in AceGUI-3.0 widgets.
+- AceConfigCmd-3.0: Properly parse functions and methods supplied for the "hidden" option table member. (Ticket #96)
+- AceConfigDialog-3.0: Fix the unpacking of the basepath arguments when internally calling :Open (Ticket #90)
+- AceConfigDialog-3.0: Properly refresh BlizOptions Windows which are registered with a path on NotifyChange. (Ticket #93)
+- AceConfigDialog-3.0: Allow image/imageCoords on toogle elements (Note that the width/height of the image on the toggle cannot be changed) (Ticket #82)
+- AceConfigDialog-3.0: Pass the groups "name" tag to DropDownGroups as the title. (Ticket #79)
+- AceDB-3.0: Remove the metatable from the DB before removing defaults, so we don't accidentally invoke it in the process. (Ticket #66)
+- AceDB-3.0: Don't save the profileKeys for namespaces, since we use the profile of the parent DB anyway. This will cut down on SV complexity when using alot of namespaces.
+- AceDB-3.0: Don't fire the OnProfileReset callback when copying a profile.
+- AceDBOptions-3.0: Show the current profile on the dialog. (Ticket #56)
+- AceComm-3.0: Add callbacks for message chunks going out the wire (via CTL). Useful for displaying progress for very large messages.
+- AceConsole-3.0: Add :Printf() so you don't have to do Print(format())
+
+Ace3 Beta - Revision 820 (August 7th, 2009)
+--------------------------------------------
+- AceComm-3.0: Updated ChatThrottleLib to v21
+- AceGUI-3.0: Fixed a glitch in the TabGroup code that caused tabs to be unresponsive under rare conditions. (Ticket #38)
+- AceGUI-3.0: Consistent "disabled" behaviour of all widgets. (Ticket #47)
+- AceGUI-3.0: Changed the way widgets are handled on release to avoid a crash in the game client. (Ticket #49)
+- AceGUI-3.0: Fixed a glitch in the button graphics. (Ticket #58)
+- AceGUI-3.0: Localized the "Close" Text on the Frame widget.
+
+Ace3 Beta - Revision 803 (April 14th, 2009)
+--------------------------------------------
+- AceConfig-3.0: Allow spaces in the keys of config tables. Spaces will be changed on the fly to underscores in AceConfigCmd-3.0 - there is no collision check in place, yet.
+- AceConfig-3.0: Support a "fontSize" attribute to the description type. Possible values are "small" (default), "medium" and "large".
+- AceConfigDialog-3.0: Fixed an error that would occur when calling InterfaceOptionsFrame_OpenToCategory from within an event handler in a Blizzard Options integrated frame. (Ticket #33)
+- AceConfigDialog-3.0: The "execute" type does now recognize the "image" attributes, and will display a clickable icon instead of the button when an image is supplied. (Ticket #35)
+- AceConfigDialog-3.0: Pass icons defined in the option table to the TreeGroup widget (Ticket #20)
+- AceConfigDialog-3.0: Fixed a bug that caused an empty group widget to be drawn if all groups were hidden.
+- AceConfigCmd-3.0: Improved the behaviour of select and multiselect elements. (Ticket #26)
+- AceDB-3.0: Add a GetNamespace function to the DB Objects which returns an existing namespace from the DB object.
+- AceGUI-3.0 Slider Widget: Properly show percentage values as min/max if isPercent is true. (Ticket #32)
+- AceGUI-3.0: Fixed an error in the TreeGroup Widget that caused execution to stop if no name was provided.
+- AceGUI-3.0: Fixed the behaviour of the MultiLineEditbox Widget (Accept button not clickable). (Ticket #28)
+- AceGUI-3.0: TabGroup: Set a maximum width for tabs based on the size of the widget. (Ticket #34)
+- AceGUI-3.0: Added a new InteractiveLabel with OnEnter/OnLeave/OnClick callbacks and a highlight texture
+- AceGUI-3.0: Add SetFont and SetFontObject functions to the Label widget (and the new InteractiveLabel)
+- AceGUI-3.0: Support icons in the TreeGroup display. (Ticket #20)
+- AceGUI-3.0: Added a new :SetRelativeWidth Widget-API that allows you to set the width of widgets relative to their container.
+- AceGUI-3.0: Alot of fixes, tweaks and consistency changes.
+
+Ace3 Beta - Revision 741 (Feb 15th, 2009)
+--------------------------------------------
+- AceDBOptions-3.0: Disable the "Copy From" and "Delete" dropdowns if there are no profiles to choose from. (Ticket #19)
+- AceGUI-3.0: Improve TabGroup visual style - only stretch them to the full width if they would use more then 75% of the exisiting space.
+- AceGUI-3.0: Added a third optional argument to :AddChild() to specify the position for the new widget. (Ticket #22)
+- AceConfigCmd-3.0: Improve help output when viewing groups.
+- AceConfigDialog-3.0: Refresh the Options Panel after a confirmation is canceled to reset the value to its previous value. (Ticket #23)
+- AceDB-3.0: Fix a data inconsistency when using false as a table key. (Ticket #25)
+
+Ace3 Beta - Revision 722 (Jan 4th, 2009)
+--------------------------------------------
+- AceHook-3.0: Fix :SecureHookScript to not fail on previously empty scripts since frame:HookScript does nothing at all in that case. (Ticket #16)
+- AceLocale-3.0: Implement 'silent' option for :NewLocale to disable the warnings on unknown entrys (Ticket #18)
+- AceTimer-3.0: Implement :TimeLeft(handle) function (Ticket #10)
+- AceGUI-3.0: Fix TabGroup tab resizing to be consistent
+- AceGUI-3.0: Fixed EditBox alignment when the label is disabled (Ticket #13)
+- AceDB-3.0: Implement OnProfileShutdown callback (Ticket #7)
+- AceDBOptions-3.0: Updated esES and ruRU locale
+
+Ace3 Beta - Revision 706 (Oct 18th, 2008)
+--------------------------------------------
+- First Beta release after WoWAce move
+- Removed WoW 2.4.x compat layer
+- AceGUI-3.0: Fix disabling of the Multiline Editbox
+- AceGUI-3.0: Improvements to the Keybinding Widget
+
+Ace3 Beta - Revision 81437 (Sept 6th, 2008)
+--------------------------------------------
+- AceConfigDialog-3.0: the confirm callback will now receive the new value that is being set (same signature as the validate callback)
+- AceConfigDialog-3.0: :Close and :CloseAll are now safe to call from within callbacks.
+- AceGUI-3.0: Added new methods to the widget base table, see ACE-205 for full reference
+- AceGUI-3.0: Various fixes to Widgets and recycling process
+- Now compatible with WoW 3.0 (compat layer is to be removed upon 3.0 release)
+
+
+Ace3 Beta - Revision 76325 (June 9th, 2008)
+--------------------------------------------
+- AceGUI-3.0: Finish Multiselect support for the Dropdown widget (nargiddley)
+- AceGUI-3.0: Re-write TabGroup layouting (nargiddley)
+- AceGUI-3.0: TreeGroup: Add :EnableButtonTooltips(enable) to make the default tooltips on the tree optional, enabled by default. (nargiddley)
+- AceGUI-3.0: TabGroup: Add OnTabEnter and OnTabLeave Callbacks (nargiddley)
+- AceConfigDialog-3.0: Add :SelectGroup(appName, ...) - Selects the group given by the path specified then refreshes open windows. (nargiddley)
+- AceConfigDialog-3.0: :Open now accepts an optional path, when given will open the window with only the given group and its children visible (nargiddley)
+- AceConfigDialog-3.0: :AddToBlizOptions now accepts an optional path, this will add the config page to display the specified group and its children only. (nargiddley)
+- AceConfigDialog-3.0: ACE-189: allow multiselect to be shown as a dropdown by setting dialogControl = "Dropdown" (nargiddley)
+- AceConfigDialog-3.0: Add Custom tooltips to the TreeGroup and TabGroup, shows both name and desc for the group. (nargiddley)
+- AceConfigCmd-3.0: ACE-195: Remove unneeded references to .confirm, will no longer error when .confirm is a boolean (nargiddley)
+- AceAddon-3.0: Allow for an optional first argument to NewAddon to be a table to be used as the base for the addon. (ammo)
+
+Ace3 Beta - Revision 74633 (May 19th, 2008)
+--------------------------------------------
+- AceTimer-3.0: ACE-173: don't error on nil handle for CancelTimer(), just bail out early. (ammo)
+- AceGUI-3.0: ACE-161, ACE-180, ACE-181: New and improved DropDown widget (originally coded by Borlox) (nargiddley,nevcairiel)
+- AceGUI-3.0: AceGUI will call OnWidthSet and OnHeightSet as frames resize (nargiddley)
+- AceGUI-3.0: TabGroup: Use OptionsFrameTabButtonTemplate for tabs (nargiddley)
+- AceGUI-3.0: TabGroup: Tabs now span multiple lines when there are too many to fit in the width of the frame (nargiddley)
+- AceGUI-3.0: TreeGroup: Tree is now sizable by dragging, orig patch by yssaril (nargiddley)
+- AceGUI-3.0: Flow layout will now reduce widgets width to fit rather than leaving them sticking out the side of container widgets (nargiddley)
+- AceGUI-3.0: Dropdowns will no longer be left open in the background when the frame is clicked or other widgets are activated (nargiddley)
+- AceGUI-3.0: ACE-159: Rename Release to OnRelease and Acquire to OnAcquire for widgets. (nargiddley)
+- AceGUI-3.0: ACE-171: add IsVisible and IsShown methods to the widget metatable (nargiddley)
+- AceGUI-3.0: ACE-164: add tooltips to tree to show full text of childs that got clipped (ammo)
+- AceGUI-3.0: ACE-174: make buttons in AceGUI-3.0 locale independant (ammo)
+- AceGUI-3.0: ACE-166: fix treegroup visual bug (ammo)
+- AceGUI-3.0: ACE-184: make numeric entry for slider more intuitive (ammo)
+- AceConfigCmd-3.0: ACE-172 - ignore description in cmd (ammo)
+- AceConsole-3.0: nolonger check for existance of slashcommands, overwrite where needed. Last one wins, this enables AddonLoader to X-LoadOn-Slash and override the slashcommand from AddonLoader slashcommand with an Ace3 one. (Ammo)
+
+Ace3 Beta - Revision 69509 (April 13th, 2008)
+---------------------------------------------
+- AceComm-3.0: turn off error messages when receiving invalid multi-part messages (its happening on login etc) (nevcairiel)
+- AceDBOptions-3.0: shorten info text at top to prevent scrollbars. (nevcairiel)
+- AceHook-3.0: ACE-162: fix unhooking of objects that were not actually hooked (nevcairiel)
+- AceDB-3.0: fire the DB callbacks after the namespaces changed their profile as well (nevcairiel)
+- AceDB-3.0: namespaces can now be individually reset using :ResetProfile() on the namespace directly (nevcairiel)
+- AceDB-3.0: added a optional argument to :ResetProfile to not populate the reset to all namespaces (so the main profile can reset individually without reseting all namespaces too) (nevcairiel)
+
+Ace3 Beta - Revision 66329 (March 27th, 2008)
+---------------------------------------------
+- Overall 2.4 clean ups - removing 2.4 checks and work arounds (nevcairiel)
+- AceBucket-3.0: clear the timer reference when unregistering a bucket to prevent a error when unregistering a bucket that was never fired (nevcairiel)
+- AceAddon-3.0: Bugfix when enabling/disabling modules from the parents OnEnable after disabling / enabling the parent addon. (ammo)
+- AceGUI-3.0: Don't parent the BlizOptionsGroup widget to UIParent and Hide it by default. Fixes stray controls on the screen. (nargiddley)
+- AceConfigDialog-3.0: Config windows without a default size won't incorrectly get a default size from a previously open window. (nargiddley)
+- AceDBOptions-3.0: added zhCN and zhTW locale (nevcairiel)
+
+Ace3 Beta - Revision 65665 (March 25th, 2008)
+---------------------------------------------
+- AceGUI-3.0: ACE-139: Changed all Widgets to resemble the Blizzard 2.4 Options Style (nevcairiel)
+- AceGUI-3.0: Fixed "List"-Layout not reporting new width to "fill"-mode widgets (mikk)
+- AceGUI-3.0: added :SetColor to the Label widget (nevcairiel)
+- AceGUI-3.0: ACE-132: ColorPicker: added checkers texture for better alpha channel display, and fixed "white"-texture bug (nevcairiel,nargiddley,ammo)
+- AceConfig-3.0: ACE-113: Added uiName, uiType, handler, option, type to the info table (nevcairiel,nargiddley)
+- AceConfigDialog-3.0: ACE-139: Adjusted for 2.4 options panels (nevcairiel)
+- AceConfigDialog-3.0: Use "width" parameter for the description widget (if present) (nevcairiel)
+- AceConfigDialog-3.0: ACE-135: Add support for specifying a rowcount for multiline editboxes (nargiddley)
+- AceConfigDialog-3.0: :AddToBlizOptions will return the frame registered so you can use it in InterfaceOptionsFrame_OpenToFrame (nevcairiel)
+- AceConfigCmd-3.0: handle "hidden" in help-output (nevcairiel)
+- AceHook-3.0: fix unhooking of secure hooks (nevcairiel)
+- AceDBOptions-3.0: add optional argument to GetOptionsTable(db[, noDefaultProfiles]) - if set to true will not show the default profiles in the profile selection (nevcairiel)
+- AceDBOptions-3.0: added koKR locale (nevcairiel)
+- Ace3 Standalone: Removed the "Ace3" Category from the 2.4 options panel (nevcairiel)
+
+Ace3 Beta - Revision 64176 (March 10th, 2008)
+---------------------------------------------
+- AceGUI-3.0: Improve Alpha handling for the ColorPicker widget, ColorPicker widget closes the ColorPickerFrame before opening to prevent values getting carried over (nargiddley)
+- AceGUI-3.0: The Slider widget will only react to the mousewheel after it has been clicked (anywhere including the label) to prevent accidental changes to the value when trying to scroll the container it is in (nargiddley)
+- AceGUI-3.0: The TreeGroup widget is scrollable with the mousewheel (nargiddley)
+- AceGUI-3.0: ACE-154: Fix frame levels in more cases to prevent widgets ending up behind their containers (nargiddley)
+- AceConfigDialog: Color picker obeys hasAlpha on the color type (nargiddley)
+- AceConfigDialog-3.0: ACE-155: Make sure that the selected group is type='group' when checking if it exists (nargiddley)
+- AceDBOptions-3.0: added frFR locale (nevcairiel)
+
+Ace3 Beta - Revision 63886 (March 8th, 2008)
+---------------------------------------------
+- AceDBOptions-3.0: new library to provide a Ace3Options table to control the AceDB-3.0 profiles (nevcairiel)
+- AceDB-3.0: add "silent" option to DeleteProfile and CopyProfile when we deal with namespaces (nevcairiel)
+- AceDB-3.0: implement library upgrade path (nevcairiel)
+- AceDB-3.0: ACE-146: fix problem with non-table values overruling ['*']-type defaults (nevcairiel)
+- AceConsole-3.0: treat |T|t texture links similar to |H|h|h links. (ammo)
+- AceGUI-3.0: Use Blizzard Templates for the EditBox and DropDown widget (nevcairiel)
+- AceBucket-3.0: ACE-150: callback is now optional, if not supplied will use the eventname as method name (only possible if one event is supplied, and not a event table) (nevcairiel)
+- tests: adjust tests for AceGUI and AceConsole changes (nevcairiel)
+
+Ace3 Beta - Revision 63220 (Feb 29th, 2008)
+---------------------------------------------
+- AceTimer-3.0: CancelAllTimers() now cancels silent (elkano)
+- AceConfigDialog: Add :SetDefaultSize(appName, width, height), sets the size the dialog will open to. Does not effect already open windows. (nargiddley)
+- AceConfigDialog: Fix typo in type check for range values (nargiddley)
+- AceGUI: ColorPicker widget will correctly fire OnValueChanged for the cancel event of the colorpicker popup. Reset ColorPicker's color on Acquire. (nargiddley)
+- AceGUI: Fix Spelling of Aquire -> Acquire for widgets, all custom widgets will need to be updated. A warning will be printed for widgets not upgraded yet. (nargiddley)
+- AceConfigCmd-3.0: add simple coloring to slashcommand output. (ammo)
+- AceConsole-3.0: add some color to :Print (ammo)
+- AceAddon-3.0: set error level on library embedding to point to the :NewAddon call (nevcairiel)
+
+Ace3 Beta - Revision 62182 (Feb 20th, 2008)
+---------------------------------------------
+- Ace3 StandAlone: Add a page to the Blizzard 2.4 Interface Options with icons to open dialogs for configs registered when installed standalone (nargiddley)
+- AceConfigDialog: type = 'description' now uses the fields image and imageCoords instead of icon and iconCoords, add imageWidth and imageHeight (nargiddley)
+- AceConfigDialog: Add :AddToBlizzardOptions(appName, name), this will add the specified config to the Blizzard Options pane new in 2.4. This will only be available if running on the 2.4 PTR (nargiddley)
+- AceDB: fix GetProfiles() when setting the same profile twice (nevcairiel)
+- AceDB: bail out of :SetProfile early when trying to set to the same profile (nevcairiel)
+- AceDB: add nil checks to metatable handling (nevcairiel)
+- AceDB: clear tables that are empty after defaults removal (nevcairiel)
+- AceGUI: Fix a couple of layout bugs causing the width of groups to be wrong (nargiddley)
+- AceGUI: Add Icon widget (nargiddley)
+- AceGUI: Allow room for the border in the BlizOptionsGroup widget (nargiddley)
+- AceGUI: Button and Keybinding use UIPanelButtonTemplate2 (nargiddley)
+- AceConsole-3.0: Fix bug where no table for [self] was created when registering weak commands (ammo)
+- AceTimer-3.0: add missing :OnEmbedDisable (ammo)
+- AceAddon-3.0: added :GetName() that will always return the "real" name of a addon or module object without any prefixes (nevcairiel)
+
+Ace3 Beta - Revision 60697 (Feb 9th, 2008)
+---------------------------------------------
+- CallbackHandler-1.0: remove unnecessary table creation if a event is fired thats not registered (nevcairiel)
+- AceAddon-3.0: fixed a bug with recursive addon loading (nevcairiel)
+- AceGUI: Update TabGroup's tablist format, tabs are selected by value not index (nargiddley)
+- AceGUI: Add MultiLineEditBox widget (nargiddley, originally by bam)
+- AceGUI: Small fix to the flow layout preventing controls overlapping in some cases (nargiddley)
+- AceConfigDialog: Implement control and dialogControl for types 'input' and 'select' (nargiddley)
+- AceConfigDialog: Add support for multiline = true on type = 'input' (nargiddley)
+- AceConfigDialog: Fix an error when all groups are hidden in a group with childGroups = 'select' (nargiddley)
+- AceConfigDialog: type = 'description' will now show .icon as an image with its text (nargiddley)
+- AceConfigDialog: multiline inputs are no longer forced to width = "full" (nargiddley)
+- AceConfigDialog: bug fix when loading without AceConsole present (nevcairiel)
+
+Ace3 Beta - Revision 60545 (Feb 7th, 2008)
+---------------------------------------------
+- AceGUI: SetToplevel(true) for the Frame widget, multiple open windows should play nice together now (nargiddley)
+- AceGUI: Move Frames to the FULLSCREEN_DIALOG strata (nargiddley)
+- AceGUI: Dropdown, Editbox and Keybinding labels grey out when disabled (nargiddley)
+- AceGUI: Add OnClick callback to the TreeGroup widget (nargiddley)
+- AceConfigDialog: Confirm popups will be above the config window (nargiddley)
+
+Ace3 Beta - Revision 60163 (Feb 3rd, 2008)
+---------------------------------------------
+- Initial Beta release
\ No newline at end of file
diff --git a/Locale/Locales.xml b/Locale/Locales.xml
new file mode 100644
index 0000000..3c7199f
--- /dev/null
+++ b/Locale/Locales.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Locale/enUS.lua b/Locale/enUS.lua
new file mode 100644
index 0000000..0c6e98e
--- /dev/null
+++ b/Locale/enUS.lua
@@ -0,0 +1,28 @@
+local L = LibStub("AceLocale-3.0"):NewLocale("ArcaniteCoordinator", "enUS", true, true)
+L = L or {}
+L["Arcanite Coordinator"] = true
+L["ArcaniteCoordinator"] = true
+L["optionsDesc"] = "Arcanite Coordinator Config (You can type /%s %s to open this)."
+L["Minimap Button"] = true
+L["minimapDesc"] = "Enable minimap button. Will require a /reload if hiding the button."
+
+-- Chat Messages
+L["oldVersionErr"] = "There is a new version of Arcanite Coordinator (%s), please update from curseforge/twitch!"
+L["minimapShown"] = "Minimap button shown."
+L["minimapHidden"] = "Minimap button hidden. (you will need to type /reload to show changes)"
+L["Players with cooldown ready"] = true
+L["Players on cooldown"] = true
+L["No known cooldowns"] = true
+
+-- Console
+L["arc"] = true
+L["config"] = true
+L["cds"] = true
+L["mmb"] = true
+L["configConsole"] = "Open/close configuration window."
+L["cooldownsConsole"] = "Print currently known guild Arcanite cooldowns."
+L["toggleMinimapConsole"] = "Toggle minimap button."
+
+-- Minimap Icon Text
+L["minimapLeftClickAction"] = "Left click to print known cooldowns."
+L["minimapRightClickAction"] = "Right click to access Arcanite Coordinator settings."
diff --git a/Locale/ruRU.lua b/Locale/ruRU.lua
new file mode 100644
index 0000000..81935c3
--- /dev/null
+++ b/Locale/ruRU.lua
@@ -0,0 +1,28 @@
+local L = LibStub("AceLocale-3.0"):NewLocale("ArcaniteCoordinator", "ruRU")
+L = L or {}
+L["Arcanite Coordinator"] = "Арканитовый Координатор"
+L["ArcaniteCoordinator"] = "АрканитовыйКоординатор"
+L["Minimap Button"] = "Кнопка на Миникарте"
+L["minimapDesc"] = "Включить кнопку на миникарте. Потребуется перезагрузка /reload, чтобы изменения вступили в силу."
+L["optionsDesc"] = "Настройки Арканитового Координатора (вы можете ввести /%s %s, чтобы открыть его)."
+
+-- Chat Messages
+L["minimapHidden"] = "Кнопка на миникарте скрыта. (вам нужно будет ввести /reload, чтобы изменения вступили в силу)"
+L["minimapShown"] = "Кнопка на миникарте отображается."
+L["No known cooldowns"] = "Нет известных кд"
+L["oldVersionErr"] = "Существует новая версия Арканитовый Координатор (%s), пожалуйста, обновите ее с curseforge/twitch!"
+L["Players on cooldown"] = "Игроки на кд"
+L["Players with cooldown ready"] = "Игроки с кд готовы к трансмутации"
+
+-- Console
+L["arc"] = "арк"
+L["cds"] = "овв"
+L["config"] = "настройки"
+L["configConsole"] = "Открыть/закрыть окно настроек."
+L["cooldownsConsole"] = "Отображает все известные кд в гильдии на Арканитовые слитки."
+L["mmb"] = "мкк"
+L["toggleMinimapConsole"] = "Переключить кнопку на миникарте."
+
+-- Minimap Icon Text
+L["minimapLeftClickAction"] = "Нажмите ЛКМ для отображения известных кд."
+L["minimapRightClickAction"] = "Нажмите ПКМ, чтобы перейти в меню настроек."
diff --git a/README.md b/README.md
index e04e976..c8969a8 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,2 @@
-# ArcaniteCoordinator
+# Arcanite Coordinator
Coordinates arcanite cooldowns within your guild
diff --git a/embeds.xml b/embeds.xml
new file mode 100644
index 0000000..6ff5580
--- /dev/null
+++ b/embeds.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+