diff --git a/Leatrix_Plus.lua b/Leatrix_Plus.lua
index 5a93846..6eee154 100644
--- a/Leatrix_Plus.lua
+++ b/Leatrix_Plus.lua
@@ -9,6 +9,7 @@
-- Leatrix Plus
----------------------------------------------------------------------
+ LibCompat = LibStub:GetLibrary("LibCompat-1.0")
-- Create global table
_G.LeaPlusDB = _G.LeaPlusDB or {}
@@ -30,7 +31,7 @@
local gameversion, gamebuild, gamedate, gametocversion = GetBuildInfo()
if gametocversion and gametocversion < 30000 or gametocversion > 39999 then
-- Game client is not Wow Classic
- C_Timer.After(2, function()
+ LibCompat.After(2, function()
print(L["LEATRIX PLUS: WRONG VERSION INSTALLED!"])
end)
return
@@ -125,7 +126,7 @@
-- Add background color
eFrame.t = eFrame:CreateTexture(nil, "BACKGROUND")
eFrame.t:SetAllPoints()
- eFrame.t:SetColorTexture(0.05, 0.05, 0.05, 0.9)
+ -- eFrame.t:SetColorTexture(0.05, 0.05, 0.05, 0.9)
-- Add copy title
eFrame.f = eFrame:CreateFontString(nil, 'ARTWORK', 'GameFontNormalLarge')
eFrame.f:SetPoint("TOPLEFT", x, y)
@@ -163,7 +164,7 @@
eFrame.b:SetAutoFocus(true)
eFrame.b:SetAltArrowKeyMode(true)
-- Editbox texture
- eFrame.t = CreateFrame("FRAME", nil, eFrame.b, "BackdropTemplate")
+ eFrame.t = CreateFrame("FRAME", nil, eFrame.b)
eFrame.t:SetBackdrop({bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", tile = false, tileSize = 16, edgeSize = 16, insets = { left = 5, right = 5, top = 5, bottom = 5 }})
eFrame.t:SetPoint("LEFT", -6, 0)
eFrame.t:SetWidth(eFrame.b:GetWidth() + 6)
@@ -172,7 +173,7 @@
-- Handler
eFrame.b:SetScript("OnKeyDown", function(void, key)
if key == "C" and IsControlKeyDown() then
- C_Timer.After(0.1, function()
+ LibCompat.After(0.1, function()
eFrame:Hide()
ActionStatus_DisplayMessage(L["Copied to clipboard."], true)
if LeaPlusLC.FactoryEditBoxFocusChat then
@@ -1178,9 +1179,9 @@
end)
-- Unmute sounds when logging in
- for void, soundID in pairs(muteLogoutTable) do
- UnmuteSoundFile(soundID)
- end
+ -- for void, soundID in pairs(muteLogoutTable) do
+ -- UnmuteSoundFile(soundID)
+ -- end
end
@@ -1423,7 +1424,7 @@
mEB.t = mEB:CreateTexture(nil, "BACKGROUND")
mEB.t:SetPoint(mEB:GetPoint())
mEB.t:SetSize(mEB:GetSize())
- mEB.t:SetColorTexture(0.05, 0.05, 0.05, 1.0)
+ -- mEB.t:SetColorTexture(0.05, 0.05, 0.05, 1.0)
-- Create hidden font string (used for setting width of editbox)
mEB.z = mEB:CreateFontString(nil, 'ARTWORK', 'GameFontNormal')
@@ -1483,7 +1484,7 @@
-- ElvUI fix to move Wowhead link inside the quest log frame
if LeaPlusLC.ElvUI then
- C_Timer.After(0.1, function()
+ LibCompat.After(0.1, function()
QuestLogTitleText:ClearAllPoints()
QuestLogTitleText:SetPoint("TOPLEFT", QuestLogFrame, "TOPLEFT", 32, -18)
if QuestLogTitleText:GetStringWidth() > 200 then
@@ -2270,7 +2271,7 @@
StartMsg.s = StartMsg:CreateTexture(nil, "BACKGROUND")
StartMsg.s:SetAllPoints()
- StartMsg.s:SetColorTexture(0.1, 0.1, 0.1, 1.0)
+ -- StartMsg.s:SetColorTexture(0.1, 0.1, 0.1, 1.0)
StartMsg.f = StartMsg:CreateFontString(nil, "ARTWORK", "GameFontNormalLarge")
StartMsg.f:SetAllPoints();
@@ -2293,12 +2294,12 @@
ticker._remainingIterations = ticker._remainingIterations - 1
end
if (not ticker._remainingIterations or ticker._remainingIterations > 0) then
- C_Timer.After(duration, ticker._callback)
+ LibCompat.After(duration, ticker._callback)
end
end
end
end
- C_Timer.After(duration, ticker._callback)
+ LibCompat.After(duration, ticker._callback)
return ticker
end
@@ -2358,7 +2359,7 @@
-- Show help button for exclusions
LeaPlusLC:CreateHelpButton("SellJunkExcludeHelpButton", SellJunkFrame, titleTX, "Enter item IDs separated by commas. Item IDs can be found in item tooltips while this panel is showing.|n|nJunk items entered here will not be sold automatically.|n|nWhite items entered here will be sold automatically.|n|nThe editbox tooltip will show you more information about the items you have entered.")
- local eb = CreateFrame("Frame", nil, SellJunkFrame, "BackdropTemplate")
+ local eb = CreateFrame("Frame", nil, SellJunkFrame)
eb:SetSize(200, 180)
eb:SetPoint("TOPLEFT", 350, -92)
eb:SetBackdrop({
@@ -2369,7 +2370,7 @@
})
eb:SetBackdropBorderColor(1.0, 0.85, 0.0, 0.5)
- eb.scroll = CreateFrame("ScrollFrame", nil, eb, "UIPanelScrollFrameTemplate")
+ eb.scroll = CreateFrame("ScrollFrame", nil, eb)
eb.scroll:SetPoint("TOPLEFT", eb, 12, -10)
eb.scroll:SetPoint("BOTTOMRIGHT", eb, -30, 10)
@@ -2519,7 +2520,7 @@
eb.Text:HookScript("OnTextChanged", MakeTooltipString)
eb.Text:HookScript("OnTextChanged", function()
- C_Timer.After(0.1, function()
+ LibCompat.After(0.1, function()
MakeTooltipString()
end)
end)
@@ -2760,7 +2761,7 @@
end)
else
-- If combat log is undocked, do nothing but show warning
- C_Timer.After(1, function()
+ LibCompat.After(1, function()
LeaPlusLC:Print("Combat log cannot be hidden while undocked.")
end)
end
@@ -2848,7 +2849,7 @@
if CompactRaidFrameManagerDisplayFrameHiddenModeToggle then
-- Create a border for the button
- local cBackdrop = CreateFrame("Frame", nil, CompactRaidFrameManagerDisplayFrameHiddenModeToggle, "BackdropTemplate")
+ local cBackdrop = CreateFrame("Frame", nil, CompactRaidFrameManagerDisplayFrameHiddenModeToggle)
cBackdrop:SetAllPoints()
cBackdrop.backdropInfo = {edgeFile = "Interface/Tooltips/UI-Tooltip-Border", tile = false, tileSize = 0, edgeSize = 16, insets = {left = 0, right = 0, top = 0, bottom = 0}}
cBackdrop:ApplyBackdrop()
@@ -3288,7 +3289,7 @@
DeathDura:SetScript("OnEvent", function(self, event)
ShowDuraStats("status")
DeathDura:UnregisterEvent("PLAYER_DEAD")
- C_Timer.After(2, function()
+ LibCompat.After(2, function()
DeathDura:RegisterEvent("PLAYER_DEAD")
end)
end)
@@ -3464,7 +3465,7 @@
LeaPlusLC:CreateHelpButton("MuteGameSoundsCustomHelpButton", MuteCustomPanel, titleTX, "Enter sound file IDs separated by comma then click the Mute button.|n|nIf you wish, you can enter a brief note for each file ID but do not include numbers in your notes.|n|nFor example, you can enter 'DevAura 569679, RetAura 568744' to mute the Devotion Aura and Retribution Aura spells.|n|nUse Leatrix Sounds to find, test and play sound file IDs.")
-- Add large editbox
- local eb = CreateFrame("Frame", nil, MuteCustomPanel, "BackdropTemplate")
+ local eb = CreateFrame("Frame", nil, MuteCustomPanel)
eb:SetSize(548, 180)
eb:SetPoint("TOPLEFT", 10, -92)
eb:SetBackdrop({
@@ -3475,7 +3476,7 @@
})
eb:SetBackdropBorderColor(1.0, 0.85, 0.0, 0.5)
- eb.scroll = CreateFrame("ScrollFrame", nil, eb, "UIPanelScrollFrameTemplate")
+ eb.scroll = CreateFrame("ScrollFrame", nil, eb)
eb.scroll:SetPoint("TOPLEFT", eb, 12, -10)
eb.scroll:SetPoint("BOTTOMRIGHT", eb, -30, 10)
@@ -3680,7 +3681,7 @@
VehicleSeatIndicator:SetScale(LeaPlusLC["VehicleScale"])
-- Create drag frame
- local dragframe = CreateFrame("FRAME", nil, nil, "BackdropTemplate")
+ local dragframe = CreateFrame("FRAME", nil, nil)
dragframe:SetPoint("CENTER", vehicleHolder, "CENTER", 0, 1)
dragframe:SetBackdropColor(0.0, 0.5, 1.0)
dragframe:SetBackdrop({edgeFile = "Interface/Tooltips/UI-Tooltip-Border", tile = false, tileSize = 0, edgeSize = 16, insets = { left = 0, right = 0, top = 0, bottom = 0}})
@@ -3690,7 +3691,7 @@
dragframe.t = dragframe:CreateTexture()
dragframe.t:SetAllPoints()
- dragframe.t:SetColorTexture(0.0, 1.0, 0.0, 0.5)
+ -- dragframe.t:SetColorTexture(0.0, 1.0, 0.0, 0.5)
dragframe.t:SetAlpha(0.5)
dragframe.f = dragframe:CreateFontString(nil, 'ARTWORK', 'GameFontNormalLarge')
@@ -4090,7 +4091,7 @@
end)
-- Add large editbox
- local eb = CreateFrame("Frame", nil, ExcludedButtonsPanel, "BackdropTemplate")
+ local eb = CreateFrame("Frame", nil, ExcludedButtonsPanel)
eb:SetSize(548, 180)
eb:SetPoint("TOPLEFT", 10, -92)
eb:SetBackdrop({
@@ -4101,7 +4102,7 @@
})
eb:SetBackdropBorderColor(1.0, 0.85, 0.0, 0.5)
- eb.scroll = CreateFrame("ScrollFrame", nil, eb, "UIPanelScrollFrameTemplate")
+ eb.scroll = CreateFrame("ScrollFrame", nil, eb)
eb.scroll:SetPoint("TOPLEFT", eb, 12, -10)
eb.scroll:SetPoint("BOTTOMRIGHT", eb, -30, 10)
@@ -4216,7 +4217,7 @@
do
-- Create frame
- local pFrame = CreateFrame("FRAME", nil, Minimap, "BackdropTemplate")
+ local pFrame = CreateFrame("FRAME", nil, Minimap)
pFrame:SetSize(100, 20)
-- Set position
@@ -4268,7 +4269,7 @@
-- Hide frame after 5 seconds
pFrame:Show()
pingTime = GetTime()
- C_Timer.After(5, function()
+ LibCompat.After(5, function()
if GetTime() - pingTime >= 5 then
pFrame:Hide()
end
@@ -4359,7 +4360,7 @@
LeaPlusCB["HideMiniAddonButtons"].tiptext = LeaPlusCB["HideMiniAddonButtons"].tiptext .. "|n|n|cff00AAFF" .. L["Cannot be used with Combine addon buttons."]
-- Create button frame (parenting to cluster ensures bFrame scales correctly)
- local bFrame = CreateFrame("FRAME", nil, MinimapCluster, "BackdropTemplate")
+ local bFrame = CreateFrame("FRAME", nil, MinimapCluster)
bFrame:ClearAllPoints()
bFrame:SetPoint("TOPLEFT", Minimap, "TOPRIGHT", 4, 4)
bFrame:Hide()
@@ -4372,7 +4373,7 @@
local ButtonFrameTicker
bFrame:HookScript("OnShow", function()
if ButtonFrameTicker then ButtonFrameTicker:Cancel() end
- ButtonFrameTicker = C_Timer.NewTicker(2, function()
+ ButtonFrameTicker = LibCompat.NewTicker(2, function()
if ItemRackMenuFrame and ItemRackMenuFrame:IsShown() and ItemRackMenuFrame:IsMouseOver() then return end
if not bFrame:IsMouseOver() and not Minimap:IsMouseOver() then
bFrame:Hide()
@@ -4529,7 +4530,7 @@
_G.GetMinimapShape = function() return "SQUARE" end
-- Create black border around map
- local miniBorder = CreateFrame("Frame", nil, Minimap, "BackdropTemplate")
+ local miniBorder = CreateFrame("Frame", nil, Minimap)
miniBorder:SetPoint("TOPLEFT", -3, 3)
miniBorder:SetPoint("BOTTOMRIGHT", 3, -3)
miniBorder:SetAlpha(0.8)
@@ -4594,7 +4595,7 @@
-- Debug buttons
local LeaPlusMiniMapDebug = nil
if LeaPlusMiniMapDebug then
- C_Timer.After(2, function()
+ LibCompat.After(2, function()
MiniMapMailFrame:Show()
MiniMapBattlefieldFrame:Show()
MiniMapWorldMapButton:Show()
@@ -4616,7 +4617,7 @@
end
-- Refresh buttons
- C_Timer.After(0.1, SetButtonRad)
+ LibCompat.After(0.1, SetButtonRad)
else
@@ -4732,8 +4733,8 @@
end
-- Run the function a few times on startup
- C_Timer.NewTicker(2, MakeButtons, 3)
- C_Timer.After(0.1, MakeButtons)
+ LibCompat.NewTicker(2, MakeButtons, 3)
+ LibCompat.After(0.1, MakeButtons)
end
@@ -4869,7 +4870,7 @@
MinimapBorderTop:SetPoint("TOP", MinimapBackdrop, "TOP", 0, 20)
-- Refresh buttons
- C_Timer.After(0.1, SetButtonRad)
+ LibCompat.After(0.1, SetButtonRad)
-- Function to set zone text bar
local function SetZoneTextBar()
@@ -5136,7 +5137,7 @@
-- Combine addon buttons: Hide new LibDBIcon icons
if LeaPlusLC["CombineAddonButtons"] == "On" then
- --C_Timer.After(0.1, function() -- Removed for now
+ --LibCompat.After(0.1, function() -- Removed for now
local buttonName = strlower(name)
if not strfind(strlower(LeaPlusDB["MiniExcludeList"]), buttonName) then
if button.db and not button.db.hide then
@@ -5233,7 +5234,7 @@
DurabilityFrame:SetScale(LeaPlusLC["DurabilityScale"])
-- Create drag frame
- local dragframe = CreateFrame("FRAME", nil, nil, "BackdropTemplate")
+ local dragframe = CreateFrame("FRAME", nil, nil)
dragframe:SetPoint("CENTER", durabilityHolder, "CENTER", 0, 1)
dragframe:SetBackdropColor(0.0, 0.5, 1.0)
dragframe:SetBackdrop({edgeFile = "Interface/Tooltips/UI-Tooltip-Border", tile = false, tileSize = 0, edgeSize = 16, insets = { left = 0, right = 0, top = 0, bottom = 0}})
@@ -5243,7 +5244,7 @@
dragframe.t = dragframe:CreateTexture()
dragframe.t:SetAllPoints()
- dragframe.t:SetColorTexture(0.0, 1.0, 0.0, 0.5)
+ -- dragframe.t:SetColorTexture(0.0, 1.0, 0.0, 0.5)
dragframe.t:SetAlpha(0.5)
dragframe.f = dragframe:CreateFontString(nil, 'ARTWORK', 'GameFontNormalLarge')
@@ -5408,7 +5409,7 @@
MirrorTimer1:SetScale(LeaPlusLC["TimerScale"])
-- Create drag frame
- local dragframe = CreateFrame("FRAME", nil, nil, "BackdropTemplate")
+ local dragframe = CreateFrame("FRAME", nil, nil)
dragframe:SetPoint("TOPRIGHT", MirrorTimer1, "TOPRIGHT", 0, 2.5)
dragframe:SetBackdropColor(0.0, 0.5, 1.0)
dragframe:SetBackdrop({edgeFile = "Interface/Tooltips/UI-Tooltip-Border", tile = false, tileSize = 0, edgeSize = 16, insets = { left = 0, right = 0, top = 0, bottom = 0 }})
@@ -5418,7 +5419,7 @@
dragframe.t = dragframe:CreateTexture()
dragframe.t:SetAllPoints()
- dragframe.t:SetColorTexture(0.0, 1.0, 0.0, 0.5)
+ -- dragframe.t:SetColorTexture(0.0, 1.0, 0.0, 0.5)
dragframe.t:SetAlpha(0.5)
dragframe.f = dragframe:CreateFontString(nil, 'ARTWORK', 'GameFontNormalLarge')
@@ -5646,7 +5647,7 @@
end)
-- Debug
- -- C_Timer.After(2, function() PVPReadyDialog_Display(self, 1, "Warsong Gulch", 0, "BATTLEGROUND", "", "DAMAGER"); bar:Show() end)
+ -- LibCompat.After(2, function() PVPReadyDialog_Display(self, 1, "Warsong Gulch", 0, "BATTLEGROUND", "", "DAMAGER"); bar:Show() end)
end
@@ -5681,7 +5682,7 @@
-- Add background color
editFrame.t = editFrame:CreateTexture(nil, "BACKGROUND")
editFrame.t:SetAllPoints()
- editFrame.t:SetColorTexture(0.00, 0.00, 0.0, 0.6)
+ -- editFrame.t:SetColorTexture(0.00, 0.00, 0.0, 0.6)
-- Set textures
editFrame.LeftTex:SetTexture(editFrame.RightTex:GetTexture()); editFrame.LeftTex:SetTexCoord(1, 0, 0, 1)
@@ -5701,7 +5702,7 @@
titleFrame.CharCount:Hide()
titleFrame.t = titleFrame:CreateTexture(nil, "BACKGROUND")
titleFrame.t:SetAllPoints()
- titleFrame.t:SetColorTexture(0.00, 0.00, 0.0, 0.6)
+ -- titleFrame.t:SetColorTexture(0.00, 0.00, 0.0, 0.6)
titleFrame.LeftTex:SetTexture(titleFrame.RightTex:GetTexture()); titleFrame.LeftTex:SetTexCoord(1, 0, 0, 1)
titleFrame.BottomTex:SetTexture(titleFrame.TopTex:GetTexture()); titleFrame.BottomTex:SetTexCoord(0, 1, 1, 0)
titleFrame.BottomRightTex:SetTexture(titleFrame.TopRightTex:GetTexture()); titleFrame.BottomRightTex:SetTexCoord(0, 1, 1, 0)
@@ -5854,7 +5855,7 @@
-- Handle flight time not correct or flight does not exist in database
local timeStart = GetTime()
- C_Timer.After(1, function()
+ LibCompat.After(1, function()
if UnitOnTaxi("player") then
-- Player is on a taxi so register when taxi lands
flightFrame:RegisterEvent("PLAYER_CONTROL_GAINED")
@@ -5924,7 +5925,7 @@
local speed = -2
if LeaPlusLC["FlightBarSpeech"] == "On" then
- C_Timer.After(1, function()
+ LibCompat.After(1, function()
C_VoiceChat.SpeakText(0, L["Flight commenced."], destination, speed, GetCVar("Sound_MasterVolume") * 100)
end)
mybar:AddUpdateFunction(function(bar)
@@ -6585,7 +6586,7 @@
local resTimer = GetCorpseRecoveryDelay()
if resTimer and resTimer > 0 then
-- Resurrect has a delay so wait before resurrecting
- C_Timer.After(resTimer + 1, function()
+ LibCompat.After(resTimer + 1, function()
if not UnitAffectingCombat(arg1) or LeaPlusLC["AutoResNoCombat"] == "Off" then
if LeaPlusLC["AutoAcceptRes"] == "On" then
if not DoNotAcceptResurrect() then
@@ -6753,7 +6754,7 @@
LeaPlusCB["ShowHelm"]:HookScript("OnClick", function()
LeaPlusCB["ShowHelm"]:Disable()
LeaPlusCB["ShowHelm"]:SetAlpha(1.0)
- C_Timer.After(0.5, function()
+ LibCompat.After(0.5, function()
if ShowingHelm() then
ShowHelm(false)
else
@@ -6770,7 +6771,7 @@
LeaPlusCB["ShowCloak"]:HookScript("OnClick", function()
LeaPlusCB["ShowCloak"]:Disable()
LeaPlusCB["ShowCloak"]:SetAlpha(1.0)
- C_Timer.After(0.5, function()
+ LibCompat.After(0.5, function()
if ShowingCloak() then
ShowCloak(false)
else
@@ -7042,7 +7043,7 @@
playerActor:SetUnit("player")
-- Set animation
playerActor:SetAnimation(0)
- C_Timer.After(0.1,function()
+ LibCompat.After(0.1,function()
playerActor:SetAnimation(animTable[math.floor(LeaPlusCB["DressupAnim"]:GetValue() + 0.5)], 0, 1, 1)
end)
end)
@@ -7056,7 +7057,7 @@
if UnitIsPlayer("target") then
DressUpFrame.DressUpModel:SetUnit("target")
DressUpFrame.DressUpModel:Undress()
- C_Timer.After(0.01, function()
+ LibCompat.After(0.01, function()
for i = 1, 19 do
local itemName = GetInventoryItemID("player", i)
if itemName then
@@ -7079,7 +7080,7 @@
playerActor:SetUnit("target")
-- Set animation
playerActor:SetAnimation(0)
- C_Timer.After(0.1,function()
+ LibCompat.After(0.1,function()
playerActor:SetAnimation(animTable[math.floor(LeaPlusCB["DressupAnim"]:GetValue() + 0.5)], 0, 1, 1)
end)
end
@@ -7113,10 +7114,10 @@
LeaPlusCB["DressUpTargetSelfBtn"]:SetScript("OnEvent", function()
DressUpFrame.DressUpModel:SetUnit("player")
DressUpFrame.DressUpModel:Undress()
- C_Timer.After(0.01, function()
+ LibCompat.After(0.01, function()
for i = 1, 19 do
local itemName = GetInventoryItemID("target", i)
- C_Timer.After(0.01, function()
+ LibCompat.After(0.01, function()
if itemName then
DressUpFrame.DressUpModel:TryOn("item:" .. itemName)
end
@@ -7201,7 +7202,7 @@
-- CharacterStatsTBC is installed
RunScript('CSC_HideStatsPanel()')
if startup then
- C_Timer.After(0.1, function()
+ LibCompat.After(0.1, function()
CharacterModelFrame:ClearAllPoints()
CharacterModelFrame:SetPoint("TOPLEFT", PaperDollFrame, 66, -76)
CharacterModelFrame:SetPoint("BOTTOMRIGHT", PaperDollFrame, -86, 134)
@@ -7226,7 +7227,7 @@
-- CharacterStatsTBC is installed
RunScript('CSC_ShowStatsPanel()')
if startup then
- C_Timer.After(0.1, function()
+ LibCompat.After(0.1, function()
CharacterModelFrame:ClearAllPoints()
CharacterModelFrame:SetPoint("TOPLEFT", PaperDollFrame, 66, -76)
CharacterModelFrame:SetPoint("BOTTOMRIGHT", PaperDollFrame, -86, 243)
@@ -7451,7 +7452,7 @@
end
-- Release automatically
local delay = LeaPlusLC["AutoReleaseDelay"] / 1000
- C_Timer.After(delay, function()
+ LibCompat.After(delay, function()
local dialog = StaticPopup_Visible("DEATH")
if dialog then
if IsShiftKeyDown() then
@@ -7801,13 +7802,13 @@
if setting and LeaPlusLC["SetWeatherDensity"] == "On" then
if setting == "graphicsParticleDensity" then
if GetCVar("WeatherDensity") ~= LeaPlusLC["WeatherLevel"] then
- C_Timer.After(0.1, function()
+ LibCompat.After(0.1, function()
SetCVar("WeatherDensity", LeaPlusLC["WeatherLevel"])
end)
end
elseif setting == "raidGraphicsParticleDensity" then
if GetCVar("RAIDweatherDensity") ~= LeaPlusLC["WeatherLevel"] then
- C_Timer.After(0.1, function()
+ LibCompat.After(0.1, function()
SetCVar("RAIDweatherDensity", LeaPlusLC["WeatherLevel"])
end)
end
@@ -8487,7 +8488,7 @@
SetCVar("chatClassColorOverride", "0")
- C_Timer.After(0.1, function()
+ LibCompat.After(0.1, function()
-- Set local channel colors and lock checkboxes
for i = 1, 18 do
@@ -8660,14 +8661,10 @@
if arg1 == "RightButton" then
-- No modifier key toggles the options panel
- if LeaPlusLC:IsPlusShowing() then
- LeaPlusLC:HideFrames()
- LeaPlusLC:HideConfigPanels()
- else
- LeaPlusLC:HideFrames()
- LeaPlusLC["PageF"]:Show()
- end
- LeaPlusLC["Page" .. LeaPlusLC["LeaStartPage"]]:Show()
+ -- if LeaPlusLC:IsPlusShowing() then
+ReloadUI()
+ -- end
+ -- LeaPlusLC["Page" .. LeaPlusLC["LeaStartPage"]]:Show()
end
@@ -9021,7 +9018,7 @@
end)
-- Create drag frame
- local dragframe = CreateFrame("FRAME", nil, nil, "BackdropTemplate")
+ local dragframe = CreateFrame("FRAME", nil, nil)
dragframe:SetPoint("TOPRIGHT", BuffFrame, "TOPRIGHT", 0, 2.5)
dragframe:SetBackdropColor(0.0, 0.5, 1.0)
dragframe:SetBackdrop({edgeFile = "Interface/Tooltips/UI-Tooltip-Border", tile = false, tileSize = 0, edgeSize = 16, insets = { left = 0, right = 0, top = 0, bottom = 0 }})
@@ -9031,7 +9028,7 @@
dragframe.t = dragframe:CreateTexture()
dragframe.t:SetAllPoints()
- dragframe.t:SetColorTexture(0.0, 1.0, 0.0, 0.5)
+ -- dragframe.t:SetColorTexture(0.0, 1.0, 0.0, 0.5)
dragframe.t:SetAlpha(0.5)
dragframe.f = dragframe:CreateFontString(nil, 'ARTWORK', 'GameFontNormalLarge')
@@ -9271,7 +9268,7 @@
if not currentframe then
-- No frame selected so select the player frame
currentframe = PlayerFrame:GetName()
- LeaPlusLC["DragPlayerFrame"].t:SetColorTexture(0.0, 1.0, 0.0,0.5)
+ -- LeaPlusLC["DragPlayerFrame"].t:SetColorTexture(0.0, 1.0, 0.0,0.5)
end
-- Set the scale slider value to the selected frame
LeaPlusCB["FrameScale"]:SetValue(LeaPlusDB["Frames"][currentframe]["Scale"])
@@ -9385,7 +9382,7 @@
-- Create drag frames
local function LeaPlusMakeDrag(dragframe,realframe)
- local dragframe = CreateFrame("Frame", nil, nil, "BackdropTemplate")
+ local dragframe = CreateFrame("Frame", nil, nil)
LeaPlusLC[dragframe] = dragframe
dragframe:SetSize(realframe:GetSize())
dragframe:SetPoint("TOP", realframe, "TOP", 0, 2.5)
@@ -9415,9 +9412,9 @@
-- Set all drag frames to blue then tint the selected frame to green
for k,v in pairs(FrameTable) do
- LeaPlusLC[k].t:SetColorTexture(0.0, 0.5, 1.0, 0.5)
+ -- LeaPlusLC[k].t:SetColorTexture(0.0, 0.5, 1.0, 0.5)
end
- dragframe.t:SetColorTexture(0.0, 1.0, 0.0, 0.5)
+ -- dragframe.t:SetColorTexture(0.0, 1.0, 0.0, 0.5)
-- Set currentframe variable to selected frame and set the scale slider value
currentframe = realframe:GetName()
@@ -9432,7 +9429,7 @@
dragframe.t = dragframe:CreateTexture()
dragframe.t:SetAllPoints()
- dragframe.t:SetColorTexture(0.0, 0.5, 1.0, 0.5)
+ -- dragframe.t:SetColorTexture(0.0, 0.5, 1.0, 0.5)
dragframe.t:SetAlpha(0.5)
dragframe.f = dragframe:CreateFontString(nil, 'ARTWORK', 'GameFontNormalLarge')
@@ -9583,7 +9580,7 @@
UIWidgetTopCenterContainerFrame:SetScale(LeaPlusLC["WidgetScale"])
-- Create drag frame
- local dragframe = CreateFrame("FRAME", nil, nil, "BackdropTemplate")
+ local dragframe = CreateFrame("FRAME", nil, nil)
dragframe:SetPoint("CENTER", topCenterHolder, "CENTER", 0, 1)
dragframe:SetBackdropColor(0.0, 0.5, 1.0)
dragframe:SetBackdrop({edgeFile = "Interface/Tooltips/UI-Tooltip-Border", tile = false, tileSize = 0, edgeSize = 16, insets = { left = 0, right = 0, top = 0, bottom = 0}})
@@ -9593,7 +9590,7 @@
dragframe.t = dragframe:CreateTexture()
dragframe.t:SetAllPoints()
- dragframe.t:SetColorTexture(0.0, 1.0, 0.0, 0.5)
+ -- dragframe.t:SetColorTexture(0.0, 1.0, 0.0, 0.5)
dragframe.t:SetAlpha(0.5)
dragframe.f = dragframe:CreateFontString(nil, 'ARTWORK', 'GameFontNormalLarge')
@@ -9787,7 +9784,7 @@
FocusFrame:SetScale(LeaPlusLC["FocusScale"])
-- Create drag frame
- local dragframe = CreateFrame("FRAME", nil, nil, "BackdropTemplate")
+ local dragframe = CreateFrame("FRAME", nil, nil)
dragframe:SetBackdropColor(0.0, 0.5, 1.0)
dragframe:SetBackdrop({edgeFile = "Interface/Tooltips/UI-Tooltip-Border", tile = false, tileSize = 0, edgeSize = 16, insets = { left = 0, right = 0, top = 0, bottom = 0}})
dragframe:SetToplevel(true)
@@ -9796,7 +9793,7 @@
dragframe.t = dragframe:CreateTexture()
dragframe.t:SetAllPoints()
- dragframe.t:SetColorTexture(0.0, 1.0, 0.0, 0.5)
+ -- dragframe.t:SetColorTexture(0.0, 1.0, 0.0, 0.5)
dragframe.t:SetAlpha(0.5)
dragframe.f = dragframe:CreateFontString(nil, 'ARTWORK', 'GameFontNormalLarge')
@@ -10127,7 +10124,7 @@
-- Add background color
editFrame.t = editFrame:CreateTexture(nil, "BACKGROUND")
editFrame.t:SetAllPoints()
- editFrame.t:SetColorTexture(0.00, 0.00, 0.0, 0.6)
+ -- editFrame.t:SetColorTexture(0.00, 0.00, 0.0, 0.6)
-- Set textures
editFrame.LeftTex:SetTexture(editFrame.RightTex:GetTexture()); editFrame.LeftTex:SetTexCoord(1, 0, 0, 1)
@@ -10147,7 +10144,7 @@
titleFrame.CharCount:Hide()
titleFrame.t = titleFrame:CreateTexture(nil, "BACKGROUND")
titleFrame.t:SetAllPoints()
- titleFrame.t:SetColorTexture(0.00, 0.00, 0.0, 0.6)
+ -- titleFrame.t:SetColorTexture(0.00, 0.00, 0.0, 0.6)
titleFrame.LeftTex:SetTexture(titleFrame.RightTex:GetTexture()); titleFrame.LeftTex:SetTexCoord(1, 0, 0, 1)
titleFrame.BottomTex:SetTexture(titleFrame.TopTex:GetTexture()); titleFrame.BottomTex:SetTexCoord(0, 1, 1, 0)
titleFrame.BottomRightTex:SetTexture(titleFrame.TopRightTex:GetTexture()); titleFrame.BottomRightTex:SetTexCoord(0, 1, 1, 0)
@@ -10295,7 +10292,7 @@
end
titleFrame.m:SetText(L["Messages"] .. ": " .. totalMsgCount)
editFrame:SetVerticalScroll(0)
- C_Timer.After(0.1, function() editFrame.ScrollBar.ScrollDownButton:Click() end)
+ LibCompat.After(0.1, function() editFrame.ScrollBar.ScrollDownButton:Click() end)
editFrame:Show()
editBox:ClearFocus()
end
@@ -10740,7 +10737,7 @@
LT["ColorBlind"] = GetCVar("colorblindMode")
-- Create drag frame
- local TipDrag = CreateFrame("Frame", nil, UIParent, "BackdropTemplate")
+ local TipDrag = CreateFrame("Frame", nil, UIParent)
TipDrag:SetToplevel(true);
TipDrag:SetClampedToScreen(false);
TipDrag:SetSize(130, 64);
@@ -10761,7 +10758,7 @@
-- Create texture
TipDrag.t = TipDrag:CreateTexture();
TipDrag.t:SetAllPoints();
- TipDrag.t:SetColorTexture(0.0, 0.5, 1.0, 0.5);
+ -- TipDrag.t:SetColorTexture(0.0, 0.5, 1.0, 0.5);
TipDrag.t:SetAlpha(0.5);
---------------------------------------------------------------------------------------------------------
@@ -11471,10 +11468,18 @@
if LeaPlusLC["ViewPortEnable"] == "On" then
-- Create border textures
- local BordTop = WorldFrame:CreateTexture(nil, "ARTWORK"); BordTop:SetColorTexture(0, 0, 0, 1); BordTop:SetPoint("TOPLEFT", UIParent, "TOPLEFT", 0, 0); BordTop:SetPoint("TOPRIGHT", UIParent, "TOPRIGHT", 0, 0)
- local BordBot = WorldFrame:CreateTexture(nil, "ARTWORK"); BordBot:SetColorTexture(0, 0, 0, 1); BordBot:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT", 0, 0); BordBot:SetPoint("BOTTOMRIGHT", UIParent, "BOTTOMRIGHT", 0, 0)
- local BordLeft = WorldFrame:CreateTexture(nil, "ARTWORK"); BordLeft:SetColorTexture(0, 0, 0, 1); BordLeft:SetPoint("TOPLEFT", UIParent, "TOPLEFT", 0, 0); BordLeft:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT", 0, 0)
- local BordRight = WorldFrame:CreateTexture(nil, "ARTWORK"); BordRight:SetColorTexture(0, 0, 0, 1); BordRight:SetPoint("TOPRIGHT", UIParent, "TOPRIGHT", 0, 0); BordRight:SetPoint("BOTTOMRIGHT", UIParent, "BOTTOMRIGHT", 0, 0)
+ local BordTop = WorldFrame:CreateTexture(nil, "ARTWORK");
+ -- BordTop:SetColorTexture(0, 0, 0, 1);
+ BordTop:SetPoint("TOPLEFT", UIParent, "TOPLEFT", 0, 0); BordTop:SetPoint("TOPRIGHT", UIParent, "TOPRIGHT", 0, 0)
+ local BordBot = WorldFrame:CreateTexture(nil, "ARTWORK");
+ -- BordBot:SetColorTexture(0, 0, 0, 1);
+ BordBot:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT", 0, 0); BordBot:SetPoint("BOTTOMRIGHT", UIParent, "BOTTOMRIGHT", 0, 0)
+ local BordLeft = WorldFrame:CreateTexture(nil, "ARTWORK");
+ -- BordLeft:SetColorTexture(0, 0, 0, 1);
+ BordLeft:SetPoint("TOPLEFT", UIParent, "TOPLEFT", 0, 0); BordLeft:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT", 0, 0)
+ local BordRight = WorldFrame:CreateTexture(nil, "ARTWORK");
+ -- BordRight:SetColorTexture(0, 0, 0, 1);
+ BordRight:SetPoint("TOPRIGHT", UIParent, "TOPRIGHT", 0, 0); BordRight:SetPoint("BOTTOMRIGHT", UIParent, "BOTTOMRIGHT", 0, 0)
-- Create viewport configuration panel
local SideViewport = LeaPlusLC:CreatePanel("Enable viewport", "SideViewport")
@@ -11713,7 +11718,7 @@
-- Show first run message
if not LeaPlusDB["FirstRunMessageSeen"] then
- C_Timer.After(1, function()
+ LibCompat.After(1, function()
LeaPlusLC:Print(L["Enter"] .. " |cff00ff00" .. "/run leaplus()" .. "|r " .. L["or click the minimap button to open Leatrix Plus."])
LeaPlusDB["FirstRunMessageSeen"] = true
end)
@@ -11781,14 +11786,14 @@
-- Plot vertical lines
for i = 0, wline do
local t = LeaPlusLC.grid:CreateTexture(nil, 'BACKGROUND')
- if i == wline / 2 then t:SetColorTexture(1, 0, 0, 0.5) else t:SetColorTexture(0, 0, 0, 0.5) end
+ -- if i == wline / 2 then t:SetColorTexture(1, 0, 0, 0.5) else t:SetColorTexture(0, 0, 0, 0.5) end
t:SetPoint('TOPLEFT', grid, 'TOPLEFT', i * w / wline - 1, 0)
t:SetPoint('BOTTOMRIGHT', grid, 'BOTTOMLEFT', i * w / wline + 1, 0)
end
-- Plot horizontal lines
for i = 0, hline do
local t = LeaPlusLC.grid:CreateTexture(nil, 'BACKGROUND')
- if i == hline / 2 then t:SetColorTexture(1, 0, 0, 0.5) else t:SetColorTexture(0, 0, 0, 0.5) end
+ -- if i == hline / 2 then t:SetColorTexture(1, 0, 0, 0.5) else t:SetColorTexture(0, 0, 0, 0.5) end
t:SetPoint('TOPLEFT', grid, 'TOPLEFT', 0, -i * h / hline + 1)
t:SetPoint('BOTTOMRIGHT', grid, 'TOPRIGHT', 0, -i * h / hline - 1)
end
@@ -11896,14 +11901,14 @@
-- Create hover texture
mbtn.t = mbtn:CreateTexture(nil, "BACKGROUND")
- mbtn.t:SetColorTexture(0.3, 0.3, 0.00, 0.8)
+ -- mbtn.t:SetColorTexture(0.3, 0.3, 0.00, 0.8)
mbtn.t:SetAlpha(0.7)
mbtn.t:SetAllPoints()
mbtn.t:Hide()
-- Create highlight texture
mbtn.s = mbtn:CreateTexture(nil, "BACKGROUND")
- mbtn.s:SetColorTexture(0.3, 0.3, 0.00, 0.8)
+ -- mbtn.s:SetColorTexture(0.3, 0.3, 0.00, 0.8)
mbtn.s:SetAlpha(1.0)
mbtn.s:SetAllPoints()
mbtn.s:Hide()
@@ -12046,7 +12051,7 @@
if strfind(playlist[tracknumber], "#") then
if strfind(playlist[tracknumber], ".mp3") then
-- Track is a sound file with track time so create track timer
- LeaPlusLC.TrackTimer = C_Timer.NewTimer(trackTime + 1, function()
+ LeaPlusLC.TrackTimer = LibCompat.NewTimer(trackTime + 1, function()
if musicHandle then StopSound(musicHandle) end
if tracknumber == #playlist then
-- Playlist is at the end, restart from first track
@@ -12259,7 +12264,7 @@
button.t:SetPoint("TOPLEFT", button, 0, 0)
button.t:SetSize(516, 16)
- button.t:SetColorTexture(0.3, 0.3, 0.0, 0.8)
+ -- button.t:SetColorTexture(0.3, 0.3, 0.0, 0.8)
button.t:SetAlpha(0.7)
button.t:Hide()
@@ -12268,7 +12273,7 @@
button.s:SetPoint("TOPLEFT", button, 0, 0)
button.s:SetSize(516, 16)
- button.s:SetColorTexture(0.3, 0.4, 0.00, 0.6)
+ -- button.s:SetColorTexture(0.3, 0.4, 0.00, 0.6)
button.s:Hide()
button:SetScript("OnEnter", function()
@@ -12297,7 +12302,7 @@
-- Restart player if it stopped between tracks during loading screen
if playlist and tracknumber and playlist[tracknumber] and not willPlay and not musicHandle then
tracknumber = tracknumber - 1
- C_Timer.After(0.1, PlayTrack)
+ LibCompat.After(0.1, PlayTrack)
end
end
end)
@@ -12523,7 +12528,7 @@
local id, player = BNGetFriendInviteInfo(i)
if id and player then
BNDeclineFriendInvite(id)
- C_Timer.After(0.1, function()
+ LibCompat.After(0.1, function()
LeaPlusLC:Print(L["A friend request from"] .. " " .. player .. " " .. L["was automatically declined."])
end)
end
@@ -12710,7 +12715,7 @@
local sName = C_SummonInfo.GetSummonConfirmSummoner()
local sLocation = C_SummonInfo.GetSummonConfirmAreaName()
LeaPlusLC:Print(L["The summon from"] .. " " .. sName .. " (" .. sLocation .. ") " .. L["will be automatically accepted in 10 seconds unless cancelled."])
- C_Timer.After(10, function()
+ LibCompat.After(10, function()
local sNameNew = C_SummonInfo.GetSummonConfirmSummoner()
local sLocationNew = C_SummonInfo.GetSummonConfirmAreaName()
if sName == sNameNew and sLocation == sLocationNew then
@@ -13634,7 +13639,7 @@
-- Set the background color
Side.t = Side:CreateTexture(nil, "BACKGROUND")
Side.t:SetAllPoints()
- Side.t:SetColorTexture(0.05, 0.05, 0.05, 0.9)
+ -- Side.t:SetColorTexture(0.05, 0.05, 0.05, 0.9)
-- Add a close Button
Side.c = CreateFrame("Button", nil, Side, "UIPanelCloseButton")
@@ -13888,7 +13893,7 @@
eb:SetScript("OnEnterPressed", eb.ClearFocus)
-- Add editbox border and backdrop
- eb.f = CreateFrame("FRAME", nil, eb, "BackdropTemplate")
+ eb.f = CreateFrame("FRAME", nil, eb)
eb.f:SetBackdrop({bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", tile = false, tileSize = 16, edgeSize = 16, insets = { left = 5, right = 5, top = 5, bottom = 5 }})
eb.f:SetPoint("LEFT", -6, 0)
eb.f:SetWidth(eb:GetWidth()+6)
@@ -13946,11 +13951,11 @@
mbtn:GetHighlightTexture():SetTexCoord(0, 0.125, 0.4375, 0.5)
-- Hide the default textures
- mbtn:HookScript("OnShow", function() mbtn.Left:Hide(); mbtn.Middle:Hide(); mbtn.Right:Hide() end)
- mbtn:HookScript("OnEnable", function() mbtn.Left:Hide(); mbtn.Middle:Hide(); mbtn.Right:Hide() end)
- mbtn:HookScript("OnDisable", function() mbtn.Left:Hide(); mbtn.Middle:Hide(); mbtn.Right:Hide() end)
- mbtn:HookScript("OnMouseDown", function() mbtn.Left:Hide(); mbtn.Middle:Hide(); mbtn.Right:Hide() end)
- mbtn:HookScript("OnMouseUp", function() mbtn.Left:Hide(); mbtn.Middle:Hide(); mbtn.Right:Hide() end)
+ -- mbtn:HookScript("OnShow", function() mbtn.Left:Hide(); mbtn.Middle:Hide(); mbtn.Right:Hide() end)
+ -- mbtn:HookScript("OnEnable", function() mbtn.Left:Hide(); mbtn.Middle:Hide(); mbtn.Right:Hide() end)
+ -- mbtn:HookScript("OnDisable", function() mbtn.Left:Hide(); mbtn.Middle:Hide(); mbtn.Right:Hide() end)
+ -- mbtn:HookScript("OnMouseDown", function() mbtn.Left:Hide(); mbtn.Middle:Hide(); mbtn.Right:Hide() end)
+ -- mbtn:HookScript("OnMouseUp", function() mbtn.Left:Hide(); mbtn.Middle:Hide(); mbtn.Right:Hide() end)
end
@@ -13993,7 +13998,7 @@
dbtn:SetScript("OnLeave", GameTooltip_Hide)
-- Create dropdown list
- local ddlist = CreateFrame("Frame",nil,frame, "BackdropTemplate")
+ local ddlist = CreateFrame("Frame",nil,frame)
LeaPlusCB["ListFrame"..ddname] = ddlist
ddlist:SetPoint("TOP",0,-42)
ddlist:SetWidth(frame:GetWidth())
@@ -14031,7 +14036,7 @@
dditem.t = dditem:CreateTexture(nil, "BACKGROUND")
dditem.t:SetAllPoints()
- dditem.t:SetColorTexture(0.3, 0.3, 0.00, 0.8)
+ -- dditem.t:SetColorTexture(0.3, 0.3, 0.00, 0.8)
dditem.t:Hide();
dditem:SetScript("OnEnter", function() dditem.t:Show() end)
@@ -14101,7 +14106,7 @@
-- Add background color
PageF.t = PageF:CreateTexture(nil, "BACKGROUND")
PageF.t:SetAllPoints()
- PageF.t:SetColorTexture(0.05, 0.05, 0.05, 0.9)
+ -- PageF.t:SetColorTexture(0.05, 0.05, 0.05, 0.9)
-- Add textures
LeaPlusLC:CreateBar("FootTexture", PageF, 570, 48, "BOTTOM", 0.5, 0.5, 0.5, 1.0, "Interface\\ACHIEVEMENTFRAME\\UI-GuildAchievement-Parchment-Horizontal-Desaturated.png")
@@ -14243,7 +14248,7 @@
if tonumber(arg1) and tonumber(arg1) < 999999999 then
local questCompleted = C_QuestLog.IsQuestFlaggedCompleted(arg1)
local questTitle = C_QuestLog.GetQuestInfo(arg1) or L["Unknown"]
- C_Timer.After(0.5, function()
+ LibCompat.After(0.5, function()
local questTitle = C_QuestLog.GetQuestInfo(arg1) or L["Unknown"]
if questCompleted then
LeaPlusLC:Print(questTitle .. " (" .. arg1 .. "):" .. "|cffffffff " .. L["Completed."])
@@ -14798,7 +14803,8 @@
if not LeaPlusLC.HelpFrame then
local frame = CreateFrame("FRAME", nil, UIParent)
frame:SetSize(570, 340); frame:SetFrameStrata("FULLSCREEN_DIALOG"); frame:SetFrameLevel(100)
- frame.tex = frame:CreateTexture(nil, "BACKGROUND"); frame.tex:SetAllPoints(); frame.tex:SetColorTexture(0.05, 0.05, 0.05, 0.9)
+ frame.tex = frame:CreateTexture(nil, "BACKGROUND"); frame.tex:SetAllPoints();
+ -- frame.tex:SetColorTexture(0.05, 0.05, 0.05, 0.9)
frame.close = CreateFrame("Button", nil, frame, "UIPanelCloseButton"); frame.close:SetSize(30, 30); frame.close:SetPoint("TOPRIGHT", 0, 0); frame.close:SetScript("OnClick", function() frame:Hide() end)
frame:ClearAllPoints(); frame:SetPoint("CENTER", UIParent, "CENTER", 0, 0)
frame:SetClampedToScreen(true)
@@ -15124,7 +15130,7 @@
end
end
-- Reinvite
- C_Timer.After(0.1, function()
+ LibCompat.After(0.1, function()
for k, v in pairs(groupNames) do
C_PartyInfo.InviteUnit(v)
end
@@ -15142,7 +15148,8 @@
-- Panel frame
local frame = CreateFrame("FRAME", nil, UIParent)
frame:SetSize(294, 86); frame:SetFrameStrata("FULLSCREEN_DIALOG"); frame:SetFrameLevel(100); frame:SetScale(2)
- frame.tex = frame:CreateTexture(nil, "BACKGROUND"); frame.tex:SetAllPoints(); frame.tex:SetColorTexture(0.05, 0.05, 0.05, 0.9)
+ frame.tex = frame:CreateTexture(nil, "BACKGROUND"); frame.tex:SetAllPoints();
+ -- frame.tex:SetColorTexture(0.05, 0.05, 0.05, 0.9)
frame.close = CreateFrame("Button", nil, frame, "UIPanelCloseButton"); frame.close:SetSize(30, 30); frame.close:SetPoint("TOPRIGHT", 0, 0); frame.close:SetScript("OnClick", function() frame:Hide() end)
frame:ClearAllPoints(); frame:SetPoint("CENTER", UIParent, "CENTER", 0, 0)
frame:SetClampedToScreen(true)
@@ -15177,7 +15184,7 @@
if endSound then
if endSound > 3000000 then endSound = 3000000 endBox:SetText(endSound) end
frame.btn:SetText("WAIT")
- C_Timer.After(0.1, function()
+ LibCompat.After(0.1, function()
for i = 1, 3000000 do
MuteSoundFile(i)
end
@@ -15190,7 +15197,7 @@
else
frame.btn:SetText("INVALID")
frame.btn:EnableMouse(false)
- C_Timer.After(2, function()
+ LibCompat.After(2, function()
frame.btn:SetText("SET LIMIT")
frame.btn:EnableMouse(true)
end)
@@ -15203,7 +15210,7 @@
frame.MuteAllBtn:SetPoint("TOPLEFT", frame.btn, "TOPRIGHT", 20, 0)
frame.MuteAllBtn:SetScript("OnClick", function()
frame.MuteAllBtn:SetText("WAIT")
- C_Timer.After(0.1, function()
+ LibCompat.After(0.1, function()
for i = 1, 3000000 do
MuteSoundFile(i)
end
@@ -15219,7 +15226,7 @@
frame.UnmuteAllBtn:SetPoint("TOPLEFT", frame.MuteAllBtn, "BOTTOMLEFT", 0, -10)
frame.UnmuteAllBtn:SetScript("OnClick", function()
frame.UnmuteAllBtn:SetText("WAIT")
- C_Timer.After(0.1, function()
+ LibCompat.After(0.1, function()
for i = 1, 3000000 do
UnmuteSoundFile(i)
end
@@ -15695,13 +15702,13 @@
mbtn.t = mbtn:CreateTexture(nil, "BACKGROUND")
mbtn.t:SetAllPoints()
- mbtn.t:SetColorTexture(0.3, 0.3, 0.00, 0.8)
+ -- mbtn.t:SetColorTexture(0.3, 0.3, 0.00, 0.8)
mbtn.t:SetAlpha(0.7)
mbtn.t:Hide()
mbtn.s = mbtn:CreateTexture(nil, "BACKGROUND")
mbtn.s:SetAllPoints()
- mbtn.s:SetColorTexture(0.3, 0.3, 0.00, 0.8)
+ -- mbtn.s:SetColorTexture(0.3, 0.3, 0.00, 0.8)
mbtn.s:Hide()
mbtn.f = mbtn:CreateFontString(nil, 'ARTWORK', 'GameFontNormal')
diff --git a/Leatrix_Plus.toc b/Leatrix_Plus.toc
index ee3936f..d8f4058 100644
--- a/Leatrix_Plus.toc
+++ b/Leatrix_Plus.toc
@@ -1,4 +1,4 @@
-## Interface: 30401
+## Interface: 30300
## Title: Leatrix Plus
## Title-zhCN: Leatrix Plus [|cffeda55f功能百宝箱|r]
@@ -16,6 +16,13 @@
## X-Curse-Project-ID: 463863
+libs\LibStub\LibStub.lua
+libs\LibCompat-1.0\lib.xml
+Libs\CallbackHandler-1.0\CallbackHandler-1.0.lua
+Libs\LibDataBroker-1.1\LibDataBroker-1.1.lua
+Libs\LibDBIcon-1.0\LibDBIcon-1.0.lua
+
+
Leatrix_Plus_Library.lua
Leatrix_Plus_Locale.lua
Leatrix_Plus_Media.lua
diff --git a/Leatrix_Plus_Library.lua b/Leatrix_Plus_Library.lua
index c6c8a5c..2831d5d 100644
--- a/Leatrix_Plus_Library.lua
+++ b/Leatrix_Plus_Library.lua
@@ -622,9 +622,9 @@ local function createButton(name, object, db)
button.dataObject = object
button.db = db
button:SetFrameStrata("MEDIUM")
- button:SetFixedFrameStrata(true)
+ -- button:SetFixedFrameStrata(true)
button:SetFrameLevel(8)
- button:SetFixedFrameLevel(true)
+ -- button:SetFixedFrameLevel(true)
button:SetSize(31, 31)
button:RegisterForClicks("anyUp")
button:RegisterForDrag("LeftButton")
@@ -678,12 +678,12 @@ local function createButton(name, object, db)
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)
+ -- animOut:SetOrder(1)
+ -- animOut:SetDuration(0.2)
+ -- animOut:SetFromAlpha(1)
+ -- animOut:SetToAlpha(0)
+ -- animOut:SetStartDelay(1)
+ -- button.fadeOut:SetToFinalAlpha(true)
lib.objects[name] = button
@@ -893,7 +893,7 @@ lib:SetButtonRadius(lib.radius) -- Upgrade to 40
-- LibDBIconEnd
end
-LeaLibDBIcon()
+-- LeaLibDBIcon() -- disabled in 3.3.5, was causing red things
----------------------------------------------------------------------
diff --git a/Leatrix_Plus_Wrath.toc b/Leatrix_Plus_Wrath.toc
index ee3936f..d8f4058 100644
--- a/Leatrix_Plus_Wrath.toc
+++ b/Leatrix_Plus_Wrath.toc
@@ -1,4 +1,4 @@
-## Interface: 30401
+## Interface: 30300
## Title: Leatrix Plus
## Title-zhCN: Leatrix Plus [|cffeda55f功能百宝箱|r]
@@ -16,6 +16,13 @@
## X-Curse-Project-ID: 463863
+libs\LibStub\LibStub.lua
+libs\LibCompat-1.0\lib.xml
+Libs\CallbackHandler-1.0\CallbackHandler-1.0.lua
+Libs\LibDataBroker-1.1\LibDataBroker-1.1.lua
+Libs\LibDBIcon-1.0\LibDBIcon-1.0.lua
+
+
Leatrix_Plus_Library.lua
Leatrix_Plus_Locale.lua
Leatrix_Plus_Media.lua
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..712bf7e
--- /dev/null
+++ b/libs/CallbackHandler-1.0/CallbackHandler-1.0.lua
@@ -0,0 +1,240 @@
+--[[ $Id: CallbackHandler-1.0.lua 895 2009-12-06 16:28:55Z nevcairiel $ ]]
+local MAJOR, MINOR = "CallbackHandler-1.0", 5
+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 CreateDispatcher(argCount)
+ local code = [[
+ local next, xpcall, eh = ...
+
+ local method, ARGS
+ local function call() method(ARGS) end
+
+ local function dispatch(handlers, ...)
+ local index
+ index, method = next(handlers)
+ if not method then return end
+ local OLD_ARGS = ARGS
+ ARGS = ...
+ repeat
+ xpcall(call, eh)
+ index, method = next(handlers, index)
+ until not method
+ ARGS = OLD_ARGS
+ end
+
+ return dispatch
+ ]]
+
+ local ARGS, OLD_ARGS = {}, {}
+ for i = 1, argCount do ARGS[i], OLD_ARGS[i] = "arg"..i, "old_arg"..i end
+ code = code:gsub("OLD_ARGS", tconcat(OLD_ARGS, ", ")):gsub("ARGS", tconcat(ARGS, ", "))
+ return assert(loadstring(code, "safecall Dispatcher["..argCount.."]"))(next, xpcall, errorhandler)
+end
+
+local Dispatchers = setmetatable({}, {__index=function(self, argCount)
+ local dispatcher = CreateDispatcher(argCount)
+ rawset(self, argCount, dispatcher)
+ return dispatcher
+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, OnUsed, OnUnused)
+ -- TODO: Remove this after beta has gone out
+ assert(not OnUsed and not OnUnused, "ACE-80: OnUsed/OnUnused are deprecated. Callbacks are now done to registry.OnUsed and registry.OnUnused")
+
+ 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
+
+ Dispatchers[select('#', ...) + 1](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"
+ if type(self)~="table" and type(self)~="string" then
+ error("Usage: "..RegisterName.."(self or \"addonId\", eventname, method): 'self or addonId': table or string 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..876df83
--- /dev/null
+++ b/libs/CallbackHandler-1.0/CallbackHandler-1.0.xml
@@ -0,0 +1,4 @@
+
+
+
\ No newline at end of file
diff --git a/libs/LibCompat-1.0/Backdrop/Backdrop.lua b/libs/LibCompat-1.0/Backdrop/Backdrop.lua
new file mode 100644
index 0000000..c51a689
--- /dev/null
+++ b/libs/LibCompat-1.0/Backdrop/Backdrop.lua
@@ -0,0 +1,523 @@
+BACKDROP_TOOLTIP_8_8_1111 = {
+ bgFile = "Interface\\Tooltips\\UI-Tooltip-Background",
+ edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
+ tile = true,
+ tileEdge = true,
+ tileSize = 8,
+ edgeSize = 8,
+ insets = {left = 1, right = 1, top = 1, bottom = 1}
+}
+BACKDROP_TOOLTIP_8_12_1111 = {
+ bgFile = "Interface\\Tooltips\\UI-Tooltip-Background",
+ edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
+ tile = true,
+ tileEdge = true,
+ tileSize = 8,
+ edgeSize = 12,
+ insets = {left = 1, right = 1, top = 1, bottom = 1}
+}
+BACKDROP_TOOLTIP_16_16_5555 = {
+ bgFile = "Interface\\Tooltips\\UI-Tooltip-Background",
+ edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
+ tile = true,
+ tileEdge = true,
+ tileSize = 16,
+ edgeSize = 16,
+ insets = {left = 5, right = 5, top = 5, bottom = 5}
+}
+BACKDROP_TOOLTIP_12_12_4444 = {
+ bgFile = "Interface\\Tooltips\\UI-Tooltip-Background",
+ edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
+ tile = true,
+ tileEdge = true,
+ tileSize = 12,
+ edgeSize = 12,
+ insets = {left = 4, right = 4, top = 4, bottom = 4}
+}
+BACKDROP_TOOLTIP_0_16 = {
+ edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
+ edgeSize = 16,
+ tileEdge = true
+}
+BACKDROP_TOOLTIP_0_12_0055 = {
+ edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
+ edgeSize = 12,
+ tileEdge = true,
+ insets = {left = 0, right = 0, top = 5, bottom = 5}
+}
+BACKDROP_TOOLTIP_0_16_5555 = {
+ edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
+ edgeSize = 16,
+ tileEdge = true,
+ insets = {left = 5, right = 5, top = 5, bottom = 5}
+}
+BACKDROP_ACHIEVEMENTS_0_64 = {
+ edgeFile = "Interface\\AchievementFrame\\UI-Achievement-WoodBorder",
+ edgeSize = 64,
+ tileEdge = true
+}
+BACKDROP_ARENA_32_32 = {
+ bgFile = "Interface\\CharacterFrame\\UI-Party-Background",
+ edgeFile = "Interface\\ArenaEnemyFrame\\UI-Arena-Border",
+ tile = true,
+ tileEdge = true,
+ tileSize = 32,
+ edgeSize = 32,
+ insets = {left = 32, right = 32, top = 32, bottom = 32}
+}
+BACKDROP_DIALOG_32_32 = {
+ bgFile = "Interface\\DialogFrame\\UI-DialogBox-Background",
+ edgeFile = "Interface\\DialogFrame\\UI-DialogBox-Border",
+ tile = true,
+ tileEdge = true,
+ tileSize = 32,
+ edgeSize = 32,
+ insets = {left = 11, right = 12, top = 12, bottom = 11}
+}
+BACKDROP_GOLD_DIALOG_32_32 = {
+ bgFile = "Interface\\DialogFrame\\UI-DialogBox-Background",
+ edgeFile = "Interface\\DialogFrame\\UI-DialogBox-Gold-Border",
+ tile = true,
+ tileEdge = true,
+ tileSize = 32,
+ edgeSize = 32,
+ insets = {left = 11, right = 12, top = 12, bottom = 11}
+}
+BACKDROP_WATERMARK_DIALOG_0_16 = {
+ edgeFile = "Interface\\DialogFrame\\UI-DialogBox-TestWatermark-Border",
+ tileEdge = true,
+ edgeSize = 16
+}
+BACKDROP_SLIDER_8_8 = {
+ bgFile = "Interface\\Buttons\\UI-SliderBar-Background",
+ edgeFile = "Interface\\Buttons\\UI-SliderBar-Border",
+ tile = true,
+ tileEdge = true,
+ tileSize = 8,
+ edgeSize = 8,
+ insets = {left = 3, right = 3, top = 6, bottom = 6}
+}
+BACKDROP_PARTY_32_32 = {
+ bgFile = "Interface\\CharacterFrame\\UI-Party-Background",
+ edgeFile = "Interface\\CharacterFrame\\UI-Party-Border",
+ tile = true,
+ tileEdge = true,
+ tileSize = 32,
+ edgeSize = 32,
+ insets = {left = 32, right = 32, top = 32, bottom = 32}
+}
+BACKDROP_TOAST_12_12 = {
+ bgFile = "Interface\\FriendsFrame\\UI-Toast-Background",
+ edgeFile = "Interface\\FriendsFrame\\UI-Toast-Border",
+ tile = true,
+ tileEdge = true,
+ tileSize = 12,
+ edgeSize = 12,
+ insets = {left = 5, right = 5, top = 5, bottom = 5}
+}
+BACKDROP_CALLOUT_GLOW_0_16 = {
+ edgeFile = "Interface\\TutorialFrame\\UI-TutorialFrame-CalloutGlow",
+ edgeSize = 16,
+ tileEdge = true
+}
+BACKDROP_CALLOUT_GLOW_0_20 = {
+ edgeFile = "Interface\\TutorialFrame\\UI-TutorialFrame-CalloutGlow",
+ edgeSize = 20,
+ tileEdge = true
+}
+BACKDROP_GLUE_TOOLTIP_16_16 = {
+ bgFile = "Interface\\Glues\\Common\\Glue-Tooltip-Background",
+ edgeFile = "Interface\\Glues\\Common\\Glue-Tooltip-Border",
+ tile = true,
+ tileEdge = true,
+ tileSize = 16,
+ edgeSize = 16,
+ insets = {left = 10, right = 5, top = 4, bottom = 9}
+}
+BACKDROP_GLUE_TOOLTIP_0_16 = {
+ edgeFile = "Interface\\Glues\\Common\\Glue-Tooltip-Border",
+ tileEdge = true,
+ edgeSize = 16
+}
+BACKDROP_MIXED_TOOLTIP_16_16 = {
+ bgFile = "Interface\\Tooltips\\UI-Tooltip-Background",
+ edgeFile = "Interface\\Glues\\Common\\Glue-Tooltip-Border",
+ tile = true,
+ tileEdge = true,
+ tileSize = 16,
+ edgeSize = 16,
+ insets = {left = 10, right = 5, top = 4, bottom = 9}
+}
+BACKDROP_TEXT_PANEL_0_16 = {
+ edgeFile = "Interface\\Glues\\Common\\TextPanel-Border",
+ tileEdge = true,
+ edgeSize = 16
+}
+BackdropTemplateMixin = {}
+local coordStart = 0.0625
+local coordEnd = 1 - coordStart
+local textureUVs = {
+ -- keys have to match pieceNames in nineSliceSetup table
+ TopLeftCorner = {
+ setWidth = true,
+ setHeight = true,
+ ULx = 0.5078125,
+ ULy = coordStart,
+ LLx = 0.5078125,
+ LLy = coordEnd,
+ URx = 0.6171875,
+ URy = coordStart,
+ LRx = 0.6171875,
+ LRy = coordEnd
+ },
+ TopRightCorner = {
+ setWidth = true,
+ setHeight = true,
+ ULx = 0.6328125,
+ ULy = coordStart,
+ LLx = 0.6328125,
+ LLy = coordEnd,
+ URx = 0.7421875,
+ URy = coordStart,
+ LRx = 0.7421875,
+ LRy = coordEnd
+ },
+ BottomLeftCorner = {
+ setWidth = true,
+ setHeight = true,
+ ULx = 0.7578125,
+ ULy = coordStart,
+ LLx = 0.7578125,
+ LLy = coordEnd,
+ URx = 0.8671875,
+ URy = coordStart,
+ LRx = 0.8671875,
+ LRy = coordEnd
+ },
+ BottomRightCorner = {
+ setWidth = true,
+ setHeight = true,
+ ULx = 0.8828125,
+ ULy = coordStart,
+ LLx = 0.8828125,
+ LLy = coordEnd,
+ URx = 0.9921875,
+ URy = coordStart,
+ LRx = 0.9921875,
+ LRy = coordEnd
+ },
+ TopEdge = {
+ setHeight = true,
+ ULx = 0.2578125,
+ ULy = "repeatX",
+ LLx = 0.3671875,
+ LLy = "repeatX",
+ URx = 0.2578125,
+ URy = coordStart,
+ LRx = 0.3671875,
+ LRy = coordStart
+ },
+ BottomEdge = {
+ setHeight = true,
+ ULx = 0.3828125,
+ ULy = "repeatX",
+ LLx = 0.4921875,
+ LLy = "repeatX",
+ URx = 0.3828125,
+ URy = coordStart,
+ LRx = 0.4921875,
+ LRy = coordStart
+ },
+ LeftEdge = {
+ setWidth = true,
+ ULx = 0.0078125,
+ ULy = coordStart,
+ LLx = 0.0078125,
+ LLy = "repeatY",
+ URx = 0.1171875,
+ URy = coordStart,
+ LRx = 0.1171875,
+ LRy = "repeatY"
+ },
+ RightEdge = {
+ setWidth = true,
+ ULx = 0.1328125,
+ ULy = coordStart,
+ LLx = 0.1328125,
+ LLy = "repeatY",
+ URx = 0.2421875,
+ URy = coordStart,
+ LRx = 0.2421875,
+ LRy = "repeatY"
+ },
+ Center = {ULx = 0, ULy = 0, LLx = 0, LLy = "repeatY", URx = "repeatX", URy = 0, LRx = "repeatX", LRy = "repeatY"}
+}
+local defaultEdgeSize = 39 -- the old default
+function BackdropTemplateMixin:OnBackdropLoaded()
+ if self.backdropInfo then
+ -- check for invalid info
+ if not self.backdropInfo.edgeFile and not self.backdropInfo.bgFile then
+ self.backdropInfo = nil
+ return
+ end
+ self:ApplyBackdrop()
+ do
+ local r, g, b = 1, 1, 1
+ if self.backdropColor then
+ r, g, b = self.backdropColor:GetRGB()
+ end
+ local a = self.backdropColorAlpha or 1
+ self:SetBackdropColor(r, g, b, a)
+ end
+ do
+ local r, g, b = 1, 1, 1
+ if self.backdropBorderColor then
+ r, g, b = self.backdropBorderColor:GetRGB()
+ end
+ local a = self.backdropBorderColorAlpha or 1
+ self:SetBackdropBorderColor(r, g, b, a)
+ end
+ if self.backdropBorderBlendMode then
+ self:SetBorderBlendMode(self.backdropBorderBlendMode)
+ end
+ end
+end
+function BackdropTemplateMixin:OnBackdropSizeChanged()
+ if self.backdropInfo then
+ self:SetupTextureCoordinates()
+ end
+end
+function BackdropTemplateMixin:GetEdgeSize()
+ if self.backdropInfo.edgeSize and self.backdropInfo.edgeSize > 0 then
+ return self.backdropInfo.edgeSize
+ else
+ return defaultEdgeSize
+ end
+end
+local function GetBackdropCoordValue(coord, pieceSetup, repeatX, repeatY)
+ local value = pieceSetup[coord]
+ if value == "repeatX" then
+ return repeatX
+ elseif value == "repeatY" then
+ return repeatY
+ else
+ return value
+ end
+end
+local function SetupBackdropTextureCoordinates(region, pieceSetup, repeatX, repeatY)
+ region:SetTexCoord(
+ GetBackdropCoordValue("ULx", pieceSetup, repeatX, repeatY),
+ GetBackdropCoordValue("ULy", pieceSetup, repeatX, repeatY),
+ GetBackdropCoordValue("LLx", pieceSetup, repeatX, repeatY),
+ GetBackdropCoordValue("LLy", pieceSetup, repeatX, repeatY),
+ GetBackdropCoordValue("URx", pieceSetup, repeatX, repeatY),
+ GetBackdropCoordValue("URy", pieceSetup, repeatX, repeatY),
+ GetBackdropCoordValue("LRx", pieceSetup, repeatX, repeatY),
+ GetBackdropCoordValue("LRy", pieceSetup, repeatX, repeatY)
+ )
+end
+function BackdropTemplateMixin:SetupTextureCoordinates()
+ local width = self:GetWidth()
+ local height = self:GetHeight()
+ local effectiveScale = self:GetEffectiveScale()
+ local edgeSize = self:GetEdgeSize()
+ local edgeRepeatX = max(0, (width / edgeSize) * effectiveScale - 2 - coordStart)
+ local edgeRepeatY = max(0, (height / edgeSize) * effectiveScale - 2 - coordStart)
+ for pieceName, pieceSetup in pairs(textureUVs) do
+ local region = self[pieceName]
+ if region then
+ if pieceName == "Center" then
+ local repeatX = 1
+ local repeatY = 1
+ if self.backdropInfo.tile then
+ local divisor = self.backdropInfo.tileSize
+ if not divisor or divisor == 0 then
+ divisor = edgeSize
+ end
+ if divisor ~= 0 then
+ repeatX = (width / divisor) * effectiveScale
+ repeatY = (height / divisor) * effectiveScale
+ end
+ end
+ SetupBackdropTextureCoordinates(region, pieceSetup, repeatX, repeatY)
+ else
+ SetupBackdropTextureCoordinates(region, pieceSetup, edgeRepeatX, edgeRepeatY)
+ end
+ end
+ end
+end
+function BackdropTemplateMixin:SetupPieceVisuals(piece, setupInfo, pieceLayout)
+ local textureInfo = textureUVs[setupInfo.pieceName]
+ local tileVerts = false
+ local file
+ if setupInfo.pieceName == "Center" then
+ file = self.backdropInfo.bgFile
+ tileVerts = self.backdropInfo.tile
+ else
+ if self.backdropInfo.tileEdge ~= false then
+ tileVerts = true
+ end
+ file = self.backdropInfo.edgeFile
+ end
+ piece:SetTexture(file, tileVerts, tileVerts)
+ local cornerWidth = textureInfo.setWidth and self:GetEdgeSize() or 0
+ local cornerHeight = textureInfo.setHeight and self:GetEdgeSize() or 0
+ piece:SetSize(cornerWidth, cornerHeight)
+end
+function BackdropTemplateMixin:SetBorderBlendMode(blendMode)
+ if not self.backdropInfo then
+ return
+ end
+ for pieceName in pairs(textureUVs) do
+ local region = self[pieceName]
+ if region and pieceName ~= "Center" then
+ region:SetBlendMode(blendMode)
+ end
+ end
+end
+function BackdropTemplateMixin:HasBackdropInfo(backdropInfo)
+ return self.backdropInfo == backdropInfo
+end
+function BackdropTemplateMixin:ClearBackdrop()
+ if self.backdropInfo then
+ for pieceName in pairs(textureUVs) do
+ local region = self[pieceName]
+ if region then
+ region:SetTexture(nil)
+ end
+ end
+ self.backdropInfo = nil
+ end
+end
+function BackdropTemplateMixin:ApplyBackdrop()
+ local x, y, x1, y1 = 0, 0, 0, 0
+ if self.backdropInfo.bgFile then
+ local edgeSize = self:GetEdgeSize()
+ x = -edgeSize
+ y = edgeSize
+ x1 = edgeSize
+ y1 = -edgeSize
+ local insets = self.backdropInfo.insets
+ if insets then
+ x = x + (insets.left or 0)
+ y = y - (insets.top or 0)
+ x1 = x1 - (insets.right or 0)
+ y1 = y1 + (insets.bottom or 0)
+ end
+ end
+ local layout = {
+ TopLeftCorner = {},
+ TopRightCorner = {},
+ BottomLeftCorner = {},
+ BottomRightCorner = {},
+ TopEdge = {},
+ BottomEdge = {},
+ LeftEdge = {},
+ RightEdge = {},
+ Center = {layer = "BACKGROUND", x = x, y = y, x1 = x1, y1 = y1},
+ setupPieceVisualsFunction = BackdropTemplateMixin.SetupPieceVisuals
+ }
+ NineSliceUtil.ApplyLayout(self, layout)
+ self:SetBackdropColor(1, 1, 1, 1)
+ self:SetBackdropBorderColor(1, 1, 1, 1)
+ self:SetupTextureCoordinates()
+end
+-- backwards compatibility API starts here
+function BackdropTemplateMixin:SetBackdrop(backdropInfo)
+ if backdropInfo then
+ if self:HasBackdropInfo(backdropInfo) then
+ return
+ end
+ if not backdropInfo.edgeFile and not backdropInfo.bgFile then
+ self:ClearBackdrop()
+ return
+ end
+ self.backdropInfo = backdropInfo
+ self:ApplyBackdrop()
+ else
+ self:ClearBackdrop()
+ end
+end
+function BackdropTemplateMixin:GetBackdrop()
+ if self.backdropInfo then
+ -- make a copy because it will be altered to match old API output
+ local backdropInfo = CopyTable(self.backdropInfo)
+ -- fill in defaults
+ if not backdropInfo.bgFile then
+ backdropInfo.bgFile = ""
+ end
+ if not backdropInfo.edgeFile then
+ backdropInfo.edgeFile = ""
+ end
+ if backdropInfo.tile == nil then
+ backdropInfo.tile = false
+ end
+ if backdropInfo.tileSize == nil then
+ backdropInfo.tileSize = 0
+ end
+ if backdropInfo.tileEdge == nil then
+ backdropInfo.tileEdge = true
+ end
+ if not backdropInfo.edgeSize then
+ backdropInfo.edgeSize = self:GetEdgeSize()
+ end
+ if not backdropInfo.insets then
+ backdropInfo.insets = {}
+ end
+ if not backdropInfo.insets.left then
+ backdropInfo.insets.left = 0
+ end
+ if not backdropInfo.insets.right then
+ backdropInfo.insets.right = 0
+ end
+ if not backdropInfo.insets.top then
+ backdropInfo.insets.top = 0
+ end
+ if not backdropInfo.insets.bottom then
+ backdropInfo.insets.bottom = 0
+ end
+ return backdropInfo
+ end
+ return nil
+end
+function BackdropTemplateMixin:GetBackdropColor()
+ if not self.backdropInfo then
+ return
+ end
+ if self.Center then
+ return self.Center:GetVertexColor()
+ end
+end
+function BackdropTemplateMixin:SetBackdropColor(r, g, b, a)
+ if not self.backdropInfo then
+ -- Ideally this would throw an error here but the old API just failed silently
+ return
+ end
+ if self.Center then
+ self.Center:SetVertexColor(r, g, b, a or 1)
+ end
+end
+function BackdropTemplateMixin:GetBackdropBorderColor()
+ if not self.backdropInfo then
+ return
+ end
+ -- return the vertex color of any valid region
+ for pieceName in pairs(textureUVs) do
+ local region = self[pieceName]
+ if region and pieceName ~= "Center" then
+ return region:GetVertexColor()
+ end
+ end
+end
+function BackdropTemplateMixin:SetBackdropBorderColor(r, g, b, a)
+ if not self.backdropInfo then
+ -- Ideally this would throw an error here but the old API just failed silently
+ return
+ end
+ for pieceName in pairs(textureUVs) do
+ local region = self[pieceName]
+ if region and pieceName ~= "Center" then
+ region:SetVertexColor(r, g, b, a or 1)
+ end
+ end
+end
diff --git a/libs/LibCompat-1.0/Backdrop/Backdrop.xml b/libs/LibCompat-1.0/Backdrop/Backdrop.xml
new file mode 100644
index 0000000..822cb7c
--- /dev/null
+++ b/libs/LibCompat-1.0/Backdrop/Backdrop.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/libs/LibCompat-1.0/LibCompat-1.0.lua b/libs/LibCompat-1.0/LibCompat-1.0.lua
new file mode 100644
index 0000000..63ad153
--- /dev/null
+++ b/libs/LibCompat-1.0/LibCompat-1.0.lua
@@ -0,0 +1,965 @@
+--
+-- **LibCompat-1.0** provided few handy functions that can be embed to addons.
+-- This library was originally created for Skada as of 1.8.50.
+-- @author: Kader B (https://github.com/bkader)
+--
+
+local MAJOR, MINOR = "LibCompat-1.0", 16
+local LibCompat, oldminor = LibStub:NewLibrary(MAJOR, MINOR)
+if not LibCompat then return end
+
+LibCompat.embeds = LibCompat.embeds or {}
+
+local pairs, ipairs, select, type = pairs, ipairs, select, type
+local tinsert, tremove, tconcat, wipe = table.insert, table.remove, table.concat, wipe
+local floor, ceil, max = math.floor, math.ceil, math.max
+local setmetatable, format = setmetatable, string.format
+local CreateFrame = CreateFrame
+
+-------------------------------------------------------------------------------
+
+do
+ local tostring = tostring
+
+ local tmp = {}
+ local function Print(self, frame, ...)
+ local n = 0
+ if self ~= LibCompat then
+ n = n + 1
+ tmp[n] = "|cff33ff99" .. tostring(self) .. "|r:"
+ end
+ for i = 1, select("#", ...) do
+ n = n + 1
+ tmp[n] = tostring(select(i, ...))
+ end
+ frame:AddMessage(tconcat(tmp, " ", 1, n))
+ end
+
+ function LibCompat:Print(...)
+ local frame = ...
+ if type(frame) == "table" and frame.AddMessage then
+ return Print(self, frame, select(2, ...))
+ end
+ return Print(self, DEFAULT_CHAT_FRAME, ...)
+ end
+
+ function LibCompat:Printf(...)
+ local frame = ...
+ if type(frame) == "table" and frame.AddMessage then
+ return Print(self, frame, format(select(2, ...)))
+ else
+ return Print(self, DEFAULT_CHAT_FRAME, format(...))
+ end
+ end
+end
+
+-------------------------------------------------------------------------------
+
+do
+ local pcall = pcall
+
+ local function DispatchError(err)
+ print("|cffff9900Error|r:" .. (err or ""))
+ end
+
+ function LibCompat.QuickDispatch(func, ...)
+ if type(func) ~= "function" then
+ return
+ end
+ local ok, err = pcall(func, ...)
+ if not ok then
+ DispatchError(err)
+ return
+ end
+ return true
+ end
+end
+
+-------------------------------------------------------------------------------
+
+do
+ local function SafePack(...)
+ local tbl = {...}
+ tbl.n = select("#", ...)
+ return tbl
+ end
+
+ local function SafeUnpack(tbl)
+ return unpack(tbl, 1, tbl.n)
+ end
+
+ local function tLength(tbl)
+ local len = 0
+ for _ in pairs(tbl) do
+ len = len + 1
+ end
+ return len
+ end
+
+ -- copies a table from another
+ local function tCopy(to, from, ...)
+ for k, v in pairs(from) do
+ local skip = false
+ if ... then
+ for i, j in ipairs(...) do
+ if j == k then
+ skip = true
+ break
+ end
+ end
+ end
+ if not skip then
+ if type(v) == "table" then
+ to[k] = {}
+ tCopy(to[k], v, ...)
+ else
+ to[k] = v
+ end
+ end
+ end
+ end
+
+ local function tAppendAll(tbl, elems)
+ for _, elem in ipairs(elems) do
+ tinsert(tbl, elem)
+ end
+ end
+
+ local weaktable = {__mode = "v"}
+ local function WeakTable(t)
+ return setmetatable(wipe(t or {}), weaktable)
+ end
+
+ -- Shamelessly copied from Omen - thanks!
+ local tablePool = setmetatable({}, {__mode = "kv"})
+
+ -- get a new table
+ local function newTable(...)
+ local t = next(tablePool)
+ if t then
+ tablePool[t] = nil
+ for i = 1, select("#", ...) do
+ t[i] = select(i, ...)
+ end
+ return t
+ else
+ return {...}
+ end
+ end
+
+ -- delete table and return to pool
+ local function delTable(t, recursive)
+ if type(t) == "table" then
+ for k, v in pairs(t) do
+ if recursive and type(v) == "table" then
+ delTable(v, recursive)
+ end
+ t[k] = nil
+ end
+ t[true] = true
+ t[true] = nil
+ setmetatable(t, nil)
+ tablePool[t] = true
+ end
+ return nil
+ end
+
+ LibCompat.SafePack = SafePack
+ LibCompat.SafeUnpack = SafeUnpack
+ LibCompat.tLength = tLength
+ LibCompat.tCopy = tCopy
+ LibCompat.tAppendAll = tAppendAll
+ LibCompat.WeakTable = WeakTable
+ LibCompat.newTable = newTable
+ LibCompat.delTable = delTable
+end
+
+-------------------------------------------------------------------------------
+
+do
+ local function Round(val)
+ return (val < 0.0) and ceil(val - 0.5) or floor(val + 0.5)
+ end
+
+ local function Square(val)
+ return val * val
+ end
+
+ local function Clamp(val, minval, maxval)
+ return (val > maxval) and maxval or (val < minval) and minval or val
+ end
+
+ local function WithinRange(val, minval, maxval)
+ return val >= minval and val <= maxval
+ end
+
+ local function WithinRangeExclusive(val, minval, maxval)
+ return val > minval and val < maxval
+ end
+
+ LibCompat.Round = Round
+ LibCompat.Square = Square
+ LibCompat.Clamp = Clamp
+ LibCompat.WithinRange = WithinRange
+ LibCompat.WithinRangeExclusive = WithinRangeExclusive
+end
+
+-------------------------------------------------------------------------------
+
+do
+ local GetNumRaidMembers, GetNumPartyMembers = GetNumRaidMembers, GetNumPartyMembers
+ local UnitExists, UnitAffectingCombat, UnitIsDeadOrGhost = UnitExists, UnitAffectingCombat, UnitIsDeadOrGhost
+ local UnitHealth, UnitHealthMax = UnitHealth, UnitHealthMax
+ local UnitPower, UnitPowerMax = UnitPower, UnitPowerMax
+
+ local function IsInRaid()
+ return (GetNumRaidMembers() > 0)
+ end
+
+ local function IsInGroup()
+ return (GetNumRaidMembers() > 0 or GetNumPartyMembers() > 0)
+ end
+
+ local function GetNumGroupMembers()
+ return IsInRaid() and GetNumRaidMembers() or GetNumPartyMembers()
+ end
+
+ local function GetNumSubgroupMembers()
+ return GetNumPartyMembers()
+ end
+
+ local function GetGroupTypeAndCount()
+ if IsInRaid() then
+ return "raid", 1, GetNumRaidMembers()
+ elseif IsInGroup() then
+ return "party", 0, GetNumPartyMembers()
+ else
+ return nil, 0, 0
+ end
+ end
+
+ local UnitIterator, roster, _
+ do
+ local rmem, pmem, step, count
+
+ local function SelfIterator()
+ while step do
+ local unit, owner
+ if step == 1 then
+ unit, owner, step = "player", nil, 2
+ elseif step == 2 then
+ unit, owner, step = "playerpet", "player", nil
+ end
+ if unit and UnitExists(unit) then
+ return unit, owner
+ end
+ end
+ end
+
+ local function PartyIterator()
+ while step do
+ local unit, owner
+ if step <= 2 then
+ unit, owner = SelfIterator()
+ step = step or 3
+ elseif step == 3 then
+ unit, owner, step = format("party%d", count), nil, 4
+ elseif step == 4 then
+ unit, owner = format("partypet%d", count), format("party%d", count)
+ count = count + 1
+ step = count <= pmem and 3 or nil
+ end
+ if unit and UnitExists(unit) then
+ return unit, owner
+ end
+ end
+ end
+
+ local function RaidIterator()
+ while step do
+ local unit, owner
+ if step == 1 then
+ unit, owner, step = format("raid%d", count), nil, 2
+ elseif step == 2 then
+ unit, owner = format("raidpet%d", count), format("raid%d", count)
+ count = count + 1
+ step = count <= rmem and 1 or nil
+ end
+ if unit and UnitExists(unit) then
+ return unit, owner
+ end
+ end
+ end
+
+ function UnitIterator()
+ rmem, step = GetNumRaidMembers(), 1
+ if rmem == 0 then
+ pmem = GetNumPartyMembers()
+ if pmem == 0 then
+ return SelfIterator, false
+ end
+ count = 1
+ return PartyIterator, false
+ end
+ count = 1
+ return RaidIterator, true
+ end
+ end
+
+ local function IsGroupDead()
+ roster, _ = UnitIterator()
+ for unit in roster do
+ if not UnitIsDeadOrGhost(unit) then
+ return false
+ end
+ end
+ return true
+ end
+
+ local function IsGroupInCombat()
+ roster, _ = UnitIterator()
+ for unit in roster do
+ if UnitAffectingCombat(unit) then
+ return true
+ end
+ end
+ return false
+ end
+
+ local function GroupIterator(func, ...)
+ roster, _ = UnitIterator()
+ for unit, owner in roster do
+ LibCompat.QuickDispatch(func, unit, owner, ...)
+ end
+ end
+
+ local function GetUnitIdFromGUID(guid, specific)
+ if specific == nil or specific == "boss" then
+ for i = 1, 4 do
+ if UnitExists("boss" .. i) and UnitGUID("boss" .. i) == guid then
+ return "boss" .. i
+ end
+ end
+ end
+
+ if specific == nil or specific == "player" then
+ if UnitExists("target") and UnitGUID("target") == guid then
+ return "target"
+ elseif UnitExists("focus") and UnitGUID("focus") == guid then
+ return "focus"
+ elseif UnitExists("targettarget") and UnitGUID("targettarget") == guid then
+ return "targettarget"
+ elseif UnitExists("focustarget") and UnitGUID("focustarget") == guid then
+ return "focustarget"
+ elseif UnitExists("mouseover") and UnitGUID("mouseover") == guid then
+ return "mouseover"
+ end
+ end
+
+ if specific == nil or specific == "group" then
+ roster, _ = UnitIterator()
+ for unit in roster do
+ if UnitGUID(unit) == guid then
+ return unit
+ elseif UnitExists(unit .. "target") and UnitGUID(unit .. "target") == guid then
+ return unit .. "target"
+ end
+ end
+ end
+ end
+
+ local function GetClassFromGUID(guid)
+ local unit = GetUnitIdFromGUID(guid)
+ local class
+ if unit and unit:find("pet") then
+ class = "PET"
+ elseif unit and unit:find("boss") then
+ class = "BOSS"
+ elseif unit then
+ class = select(2, UnitClass(unit))
+ end
+ return class, unit
+ end
+
+ local function GetCreatureId(guid)
+ return guid and tonumber(guid:sub(9, 12), 16) or 0
+ end
+
+ local function GetUnitCreatureId(unit)
+ return GetCreatureId(UnitGUID(unit))
+ end
+
+ local function UnitHealthInfo(unit, guid)
+ unit = unit or guid and GetUnitIdFromGUID(guid)
+ local percent, health, maxhealth
+ if unit and UnitExists(unit) then
+ health, maxhealth = UnitHealth(unit), UnitHealthMax(unit)
+ if health and maxhealth then
+ percent = 100 * health / max(1, maxhealth)
+ end
+ end
+ return percent, health, maxhealth
+ end
+
+ local function UnitPowerInfo(unit, guid, powerType)
+ unit = unit or guid and GetUnitIdFromGUID(guid)
+ local percent, power, maxpower
+ if unit and UnitExists(unit) then
+ power, maxpower = UnitPower(unit, powerType), UnitPowerMax(unit, powerType)
+ if power and maxpower then
+ percent = 100 * power / max(1, maxpower)
+ end
+ end
+ return percent, power, maxpower
+ end
+
+ local function UnitFullName(unit)
+ local name, realm = UnitName(unit)
+ local namerealm = realm and realm ~= "" and name .. "-" .. realm or name
+ return namerealm
+ end
+
+ LibCompat.IsInRaid = IsInRaid
+ LibCompat.IsInGroup = IsInGroup
+ LibCompat.GetNumGroupMembers = GetNumGroupMembers
+ LibCompat.GetNumSubgroupMembers = GetNumSubgroupMembers
+ LibCompat.GetGroupTypeAndCount = GetGroupTypeAndCount
+ LibCompat.IsGroupDead = IsGroupDead
+ LibCompat.IsGroupInCombat = IsGroupInCombat
+ LibCompat.GroupIterator = GroupIterator
+ LibCompat.UnitIterator = UnitIterator
+ LibCompat.GetUnitIdFromGUID = GetUnitIdFromGUID
+ LibCompat.GetClassFromGUID = GetClassFromGUID
+ LibCompat.GetCreatureId = GetCreatureId
+ LibCompat.GetUnitCreatureId = GetUnitCreatureId
+ LibCompat.UnitHealthInfo = UnitHealthInfo
+ LibCompat.UnitHealthPercent = UnitHealthInfo -- backward compatibility
+ LibCompat.UnitPowerInfo = UnitPowerInfo
+ LibCompat.UnitFullName = UnitFullName
+end
+
+-------------------------------------------------------------------------------
+
+do
+ local IsRaidLeader, GetPartyLeaderIndex = IsRaidLeader, GetPartyLeaderIndex
+ local GetRealNumRaidMembers, GetRaidRosterInfo = GetRealNumRaidMembers, GetRaidRosterInfo
+
+ local function UnitIsGroupLeader(unit)
+ if LibCompat.IsInRaid() then
+ if unit == "player" then
+ return IsRaidLeader()
+ end
+
+ local rank = select(2, GetRaidRosterInfo(unit:match("%d+")))
+ return (rank and rank == 2)
+ end
+
+ if unit == "player" then
+ return (GetPartyLeaderIndex() == 0)
+ end
+ local index = unit:match("%d+")
+ return (index and index == GetPartyLeaderIndex())
+ end
+
+ local function UnitIsGroupAssistant(unit)
+ for i = 1, GetRealNumRaidMembers() do
+ local name, rank = GetRaidRosterInfo(i)
+ if name == UnitName(unit) then
+ return (rank == 1)
+ end
+ end
+ return false
+ end
+
+ LibCompat.UnitIsGroupLeader = UnitIsGroupLeader
+ LibCompat.UnitIsGroupAssistant = UnitIsGroupAssistant
+end
+
+-------------------------------------------------------------------------------
+-- Class Colors
+
+do
+ local classColorsTable
+ local colors = CUSTOM_CLASS_COLORS or RAID_CLASS_COLORS
+
+ local function GetClassColorsTable()
+ if not classColorsTable then
+ -- add missing class color strings
+ colors.DEATHKNIGHT.colorStr = "ffc41f3b"
+ colors.DRUID.colorStr = "ffff7d0a"
+ colors.HUNTER.colorStr = "ffabd473"
+ colors.MAGE.colorStr = "ff3fc7eb"
+ colors.PALADIN.colorStr = "fff58cba"
+ colors.PRIEST.colorStr = "ffffffff"
+ colors.ROGUE.colorStr = "fffff569"
+ colors.SHAMAN.colorStr = "ff0070de"
+ colors.WARLOCK.colorStr = "ff8788ee"
+ colors.WARRIOR.colorStr = "ffc79c6e"
+
+ -- cache it once and for all.
+ classColorsTable = {}
+ for class, tbl in pairs(colors) do
+ classColorsTable[class] = tbl
+ end
+ end
+
+ return classColorsTable
+ end
+
+ local function GetClassColorObj(class)
+ classColorsTable = classColorsTable or GetClassColorsTable()
+ return class and classColorsTable[class]
+ end
+
+ local function GetClassColor(class)
+ local obj = GetClassColorObj(class)
+ if obj then
+ return obj.r, obj.g, obj.b, obj.colorStr
+ end
+ return 1, 1, 1, "ffffffff"
+ end
+
+ LibCompat.GetClassColorsTable = GetClassColorsTable
+ LibCompat.GetClassColorObj = GetClassColorObj
+ LibCompat.GetClassColor = GetClassColor
+end
+
+-------------------------------------------------------------------------------
+-- C_Timer mimic
+
+do
+ local TickerPrototype, waitTable = {}, {}
+ local TickerMetatable = {__index = TickerPrototype, __metatable = true}
+
+ local waitFrame = LibCompat_TimerFrame or CreateFrame("Frame", "LibCompat_TimerFrame", UIParent)
+ waitFrame:SetScript("OnUpdate", function(self, elapsed)
+ local total = #waitTable
+ for i = 1, total do
+ local ticker = waitTable[i]
+
+ if ticker then
+ if ticker._cancelled then
+ tremove(waitTable, i)
+ elseif ticker._delay > elapsed then
+ ticker._delay = ticker._delay - elapsed
+ i = i + 1
+ else
+ ticker._callback(ticker, LibCompat.SafeUnpack(ticker._args))
+
+ if ticker._iterations == -1 then
+ ticker._delay = ticker._duration
+ i = i + 1
+ elseif ticker._iterations > 1 then
+ ticker._iterations = ticker._iterations - 1
+ ticker._delay = ticker._duration
+ i = i + 1
+ elseif ticker._iterations == 1 then
+ tremove(waitTable, i)
+ total = total - 1
+ end
+ end
+ end
+ end
+
+ if #waitTable == 0 then
+ self:Hide()
+ end
+ end)
+
+ local function AddDelayedCall(ticker, oldTicker)
+ if oldTicker and type(oldTicker) == "table" then
+ ticker = oldTicker
+ end
+
+ tinsert(waitTable, ticker)
+ waitFrame:Show()
+ end
+
+ local function CreateTicker(duration, callback, iterations, ...)
+ local ticker = setmetatable({}, TickerMetatable)
+
+ ticker._iterations = iterations or -1
+ ticker._duration = duration
+ ticker._delay = duration
+ ticker._callback = callback
+ ticker._args = LibCompat.SafePack(...)
+
+ AddDelayedCall(ticker)
+ return ticker
+ end
+
+ function TickerPrototype:IsCancelled()
+ return self._cancelled
+ end
+
+ function TickerPrototype:Cancel()
+ self._cancelled = true
+ end
+
+ local function After(duration, callback, ...)
+ AddDelayedCall({
+ _iterations = 1,
+ _delay = duration,
+ _callback = callback,
+ _args = LibCompat.SafePack(...)
+ })
+ end
+
+ local function NewTimer(duration, callback, ...)
+ return CreateTicker(duration, callback, 1, ...)
+ end
+
+ local function NewTicker(duration, callback, iterations, ...)
+ return CreateTicker(duration, callback, iterations, ...)
+ end
+
+ local function CancelTimer(ticker)
+ if ticker and type(ticker.Cancel) == "function" then
+ ticker:Cancel()
+ end
+ return nil -- return nil to assign input reference
+ end
+
+ LibCompat.After = After
+ LibCompat.NewTimer = NewTimer
+ LibCompat.NewTicker = NewTicker
+ LibCompat.CancelTimer = CancelTimer
+end
+
+-------------------------------------------------------------------------------
+
+do
+ local GetSpellInfo, GetSpellLink = GetSpellInfo, GetSpellLink
+
+ local custom = {
+ [3] = {ACTION_ENVIRONMENTAL_DAMAGE_FALLING, "Interface\\Icons\\ability_rogue_quickrecovery"},
+ [4] = {ACTION_ENVIRONMENTAL_DAMAGE_DROWNING, "Interface\\Icons\\spell_shadow_demonbreath"},
+ [5] = {ACTION_ENVIRONMENTAL_DAMAGE_FATIGUE, "Interface\\Icons\\ability_creature_cursed_05"},
+ [6] = {ACTION_ENVIRONMENTAL_DAMAGE_FIRE, "Interface\\Icons\\spell_fire_fire"},
+ [7] = {ACTION_ENVIRONMENTAL_DAMAGE_LAVA, "Interface\\Icons\\spell_shaman_lavaflow"},
+ [8] = {ACTION_ENVIRONMENTAL_DAMAGE_SLIME, "Interface\\Icons\\inv_misc_slime_01"}
+ }
+
+ local function _GetSpellInfo(spellid)
+ local res1, res2, res3, res4, res5, res6, res7, res8, res9
+ if spellid then
+ if custom[spellid] then
+ res1, res3 = custom[spellid][1], custom[spellid][2]
+ else
+ res1, res2, res3, res4, res5, res6, res7, res8, res9 = GetSpellInfo(spellid)
+ if spellid == 75 then
+ res3 = "Interface\\Icons\\INV_Weapon_Bow_07"
+ elseif spellid == 6603 then
+ res1, res3 = MELEE, "Interface\\Icons\\INV_Sword_04"
+ end
+ end
+ end
+ return res1, res2, res3, res4, res5, res6, res7, res8, res9
+ end
+
+ local function _GetSpellLink(spellid)
+ if not custom[spellid] then
+ return GetSpellLink(spellid)
+ end
+ end
+
+ LibCompat.GetSpellInfo = _GetSpellInfo
+ LibCompat.GetSpellLink = _GetSpellLink
+end
+
+-------------------------------------------------------------------------------
+
+do
+ local band, rshift, lshift = bit.band, bit.rshift, bit.lshift
+ local byte, char = string.byte, string.char
+
+ local function HexEncode(str, title)
+ local hex = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"}
+ local t = (title and title ~= "") and {format("[=== %s ===]", title)} or {}
+ local j = 0
+ for i = 1, #str do
+ if j <= 0 then
+ t[#t + 1], j = "\n", 32
+ end
+ j = j - 1
+
+ local b = byte(str, i)
+ t[#t + 1] = hex[band(b, 15) + 1]
+ t[#t + 1] = hex[band(rshift(b, 4), 15) + 1]
+ end
+ if title and title ~= "" then
+ t[#t + 1] = "\n" .. t[1]
+ end
+ return tconcat(t)
+ end
+
+ local function HexDecode(str)
+ str = str:gsub("%[.-%]", ""):gsub("[^0123456789ABCDEF]", "")
+ if (#str == 0) or (#str % 2 ~= 0) then
+ return false, "Invalid Hex string"
+ end
+
+ local t, bl, bh = {}
+ local i = 1
+ repeat
+ bl = byte(str, i)
+ bl = bl >= 65 and bl - 55 or bl - 48
+ i = i + 1
+ bh = byte(str, i)
+ bh = bh >= 65 and bh - 55 or bh - 48
+ i = i + 1
+ t[#t + 1] = char(lshift(bh, 4) + bl)
+ until i >= #str
+ return tconcat(t)
+ end
+
+ local function EscapeStr(str)
+ local res = ""
+ for i = 1, str:len() do
+ local n = str:sub(i, i)
+ res = res .. n
+ if n == "|" then
+ res = res .. "\124"
+ end
+ end
+ return (res ~= "") and res or str
+ end
+
+ LibCompat.HexEncode = HexEncode
+ LibCompat.HexDecode = HexDecode
+ LibCompat.EscapeStr = EscapeStr
+end
+
+-------------------------------------------------------------------------------
+
+do
+ local LGT = LibStub("LibGroupTalents-1.0")
+ local UnitClass, MAX_TALENT_TABS = UnitClass, MAX_TALENT_TABS or 3
+ local GetActiveTalentGroup, GetTalentTabInfo = GetActiveTalentGroup, GetTalentTabInfo
+ local LGTRoleTable = {melee = "DAMAGER", caster = "DAMAGER", healer = "HEALER", tank = "TANK"}
+
+ -- list of class to specs
+ local specsTable = {
+ ["MAGE"] = {62, 63, 64},
+ ["PRIEST"] = {256, 257, 258},
+ ["ROGUE"] = {259, 260, 261},
+ ["WARLOCK"] = {265, 266, 267},
+ ["WARRIOR"] = {71, 72, 73},
+ ["PALADIN"] = {65, 66, 70},
+ ["DEATHKNIGHT"] = {250, 251, 252},
+ ["DRUID"] = {102, 103, 104, 105},
+ ["HUNTER"] = {253, 254, 255},
+ ["SHAMAN"] = {262, 263, 264}
+ }
+
+ local function GetSpecialization(isInspect, isPet, specGroup)
+ local currentSpecGroup = GetActiveTalentGroup(isInspect, isPet) or (specGroup or 1)
+ local points, specname, specid = 0, nil, nil
+
+ for i = 1, MAX_TALENT_TABS do
+ local name, _, pointsSpent = GetTalentTabInfo(i, isInspect, isPet, currentSpecGroup)
+ if points <= pointsSpent then
+ points = pointsSpent
+ specname = name
+ specid = i
+ end
+ end
+ return specid, specname, points
+ end
+
+ -- checks if the feral druid is a cat or tank spec
+ local function GetDruidSpec(unit)
+ -- 57881 : Natural Reaction -- used by druid tanks
+ local points = LGT:UnitHasTalent(unit, LibCompat.GetSpellInfo(57881), LGT:GetActiveTalentGroup(unit))
+ return (points and points > 0) and 3 or 2
+ end
+
+ local function GetInspectSpecialization(unit, class)
+ local spec -- start with nil
+
+ if unit and UnitExists(unit) then
+ class = class or select(2, UnitClass(unit))
+ if class and specsTable[class] then
+ local talentGroup = LGT:GetActiveTalentGroup(unit)
+ local maxPoints, index = 0, 0
+
+ for i = 1, MAX_TALENT_TABS do
+ local _, _, pointsSpent = LGT:GetTalentTabInfo(unit, i, talentGroup)
+ if pointsSpent ~= nil then
+ if maxPoints < pointsSpent then
+ maxPoints = pointsSpent
+ if class == "DRUID" and i >= 2 then
+ if i == 3 then
+ index = 4
+ elseif i == 2 then
+ index = GetDruidSpec(unit)
+ end
+ else
+ index = i
+ end
+ end
+ end
+ end
+ spec = specsTable[class][index]
+ end
+ end
+
+ return spec
+ end
+
+ local function GetSpecializationRole(unit)
+ return LGTRoleTable[LGT:GetUnitRole(unit or "player")] or "NONE"
+ end
+
+ local function GetSpecializationInfo(specIndex, isInspect, isPet, specGroup)
+ local name, icon, _, background = GetTalentTabInfo(specIndex, isInspect, isPet, specGroup)
+ local id, role
+ if isInspect and UnitExists("target") then
+ id, role = GetInspectSpecialization("target"), GetSpecializationRole("target")
+ else
+ id, role = GetInspectSpecialization("player"), GetSpecializationRole("player")
+ end
+ return id, name, nil, icon, background, role
+ end
+
+ local function UnitGroupRolesAssigned(unit)
+ return LGTRoleTable[LGT:GetUnitRole(unit or "player")] or "NONE"
+ end
+
+ local function GetUnitRole(unit)
+ return LGTRoleTable[LGT:GetUnitRole(unit or "player")] or "NONE"
+ end
+
+ local function GetGUIDRole(guid)
+ return LGTRoleTable[LGT:GetGUIDRole(guid)] or "NONE"
+ end
+
+ LibCompat.GetSpecialization = GetSpecialization
+ LibCompat.GetInspectSpecialization = GetInspectSpecialization
+ LibCompat.GetSpecializationRole = GetSpecializationRole
+ LibCompat.GetSpecializationInfo = GetSpecializationInfo
+
+ LibCompat.UnitGroupRolesAssigned = UnitGroupRolesAssigned
+ LibCompat.GetUnitRole = UnitGroupRolesAssigned
+ LibCompat.GetGUIDRole = GetGUIDRole
+ LibCompat.GetUnitSpec = GetInspectSpecialization
+
+ -- functions that simply replaced other api functions
+ LibCompat.GetNumSpecializations = GetNumTalentTabs
+ LibCompat.GetNumSpecGroups = GetNumTalentGroups
+ LibCompat.GetNumUnspentTalents = GetUnspentTalentPoints
+ LibCompat.GetActiveSpecGroup = GetActiveTalentGroup
+ LibCompat.SetActiveSpecGroup = SetActiveTalentGroup
+end
+
+-------------------------------------------------------------------------------
+
+do
+ local C_PvP = {}
+ local IsInInstance, instanceType = IsInInstance, nil
+
+ function C_PvP.IsPvPMap()
+ instanceType = select(2, IsInInstance())
+ return (instanceType == "pvp" or instanceType == "arena")
+ end
+
+ function C_PvP.IsBattleground()
+ instanceType = select(2, IsInInstance())
+ return (instanceType == "pvp")
+ end
+
+ function C_PvP.IsArena()
+ instanceType = select(2, IsInInstance())
+ return (instanceType == "arena")
+ end
+
+ LibCompat.IsInPvP = C_PvP.IsPvPMap
+ LibCompat.C_PvP = C_PvP
+end
+
+-------------------------------------------------------------------------------
+
+local mixins = {
+ "QuickDispatch",
+ -- table util
+ "SafePack",
+ "SafeUnpack",
+ "tLength",
+ "tCopy",
+ "tAppendAll",
+ "WeakTable",
+ "newTable",
+ "delTable",
+ -- math util
+ "Round",
+ "Square",
+ "Clamp",
+ "WithinRange",
+ "WithinRangeExclusive",
+ -- roster util
+ "IsInRaid",
+ "IsInGroup",
+ "IsInPvP",
+ "GetNumGroupMembers",
+ "GetNumSubgroupMembers",
+ "GetGroupTypeAndCount",
+ "IsGroupDead",
+ "IsGroupInCombat",
+ "GroupIterator",
+ "UnitIterator",
+ "UnitFullName",
+ "C_PvP",
+ -- unit util
+ "GetUnitIdFromGUID",
+ "GetClassFromGUID",
+ "GetCreatureId",
+ "GetUnitCreatureId",
+ "UnitHealthInfo",
+ "UnitHealthPercent", -- backward compatibility
+ "UnitPowerInfo",
+ "UnitIsGroupLeader",
+ "UnitIsGroupAssistant",
+ "GetUnitSpec", -- backward compatibility
+ "GetSpecialization",
+ "GetInspectSpecialization",
+ "GetSpecializationRole",
+ "GetNumSpecializations",
+ "GetSpecializationInfo",
+ "UnitGroupRolesAssigned",
+ "GetNumSpecGroups",
+ "GetNumUnspentTalents",
+ "GetActiveSpecGroup",
+ "SetActiveSpecGroup",
+ "GetUnitRole",
+ "GetGUIDRole",
+ -- timer util
+ "After",
+ "NewTimer",
+ "NewTicker",
+ "CancelTimer",
+ -- spell util
+ "GetSpellInfo",
+ "GetSpellLink",
+ -- misc util
+ "HexEncode",
+ "HexDecode",
+ "EscapeStr",
+ "GetClassColorsTable",
+ "GetClassColorObj",
+ "GetClassColor",
+ "Print",
+ "Printf"
+}
+
+function LibCompat:Embed(target)
+ for _, v in pairs(mixins) do
+ target[v] = self[v]
+ end
+ target.locale = target.locale or GetLocale()
+ self.embeds[target] = true
+ return target
+end
+
+for addon in pairs(LibCompat.embeds) do
+ LibCompat:Embed(addon)
+end
diff --git a/libs/LibCompat-1.0/LibCompat-1.0.toc b/libs/LibCompat-1.0/LibCompat-1.0.toc
new file mode 100644
index 0000000..f8f2ecd
--- /dev/null
+++ b/libs/LibCompat-1.0/LibCompat-1.0.toc
@@ -0,0 +1,20 @@
+## Title: Lib: Compat-1.0
+## Notes: Brings extra useful functions, some from later expansions and others as utilties.
+
+## Author: Kader
+## X-Email: bkader@mail.com
+## X-Donate: bkader@mail.com
+
+## Interface: 30300
+## Version: 16
+
+## X-Category: Library
+## X-Website: https://github.com/bkader/LibCompat-1.0
+
+## Dependencies:
+## X-Embeds: LibStub, CallbackHandler-1.0, LibTalentQuery-1.0, LibGroupTalents-1.0
+## OptionalDeps: LibStub, CallbackHandler-1.0
+## DefaultState: Enabled
+## LoadOnDemand: 0
+
+lib.xml
\ No newline at end of file
diff --git a/libs/LibCompat-1.0/Libs/CallbackHandler-1.0/CallbackHandler-1.0.lua b/libs/LibCompat-1.0/Libs/CallbackHandler-1.0/CallbackHandler-1.0.lua
new file mode 100644
index 0000000..040bac1
--- /dev/null
+++ b/libs/LibCompat-1.0/Libs/CallbackHandler-1.0/CallbackHandler-1.0.lua
@@ -0,0 +1,237 @@
+--[[ $Id: CallbackHandler-1.0.lua 18 2014-10-16 02:52:20Z mikk $ ]]
+local MAJOR, MINOR = "CallbackHandler-1.0", 6
+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 CreateDispatcher(argCount)
+ local code = [[
+ local next, xpcall, eh = ...
+
+ local method, ARGS
+ local function call() method(ARGS) end
+
+ local function dispatch(handlers, ...)
+ local index
+ index, method = next(handlers)
+ if not method then return end
+ local OLD_ARGS = ARGS
+ ARGS = ...
+ repeat
+ xpcall(call, eh)
+ index, method = next(handlers, index)
+ until not method
+ ARGS = OLD_ARGS
+ end
+
+ return dispatch
+ ]]
+
+ local ARGS, OLD_ARGS = {}, {}
+ for i = 1, argCount do ARGS[i], OLD_ARGS[i] = "arg"..i, "old_arg"..i end
+ code = code:gsub("OLD_ARGS", tconcat(OLD_ARGS, ", ")):gsub("ARGS", tconcat(ARGS, ", "))
+ return assert(loadstring(code, "safecall Dispatcher["..argCount.."]"))(next, xpcall, errorhandler)
+end
+
+local Dispatchers = setmetatable({}, {__index=function(self, argCount)
+ local dispatcher = CreateDispatcher(argCount)
+ rawset(self, argCount, dispatcher)
+ return dispatcher
+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
+
+ Dispatchers[select('#', ...) + 1](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.
\ No newline at end of file
diff --git a/libs/LibCompat-1.0/Libs/LibGroupTalents-1.0/LibGroupTalents-1.0.lua b/libs/LibCompat-1.0/Libs/LibGroupTalents-1.0/LibGroupTalents-1.0.lua
new file mode 100644
index 0000000..e3a0cc2
--- /dev/null
+++ b/libs/LibCompat-1.0/Libs/LibGroupTalents-1.0/LibGroupTalents-1.0.lua
@@ -0,0 +1,1727 @@
+--[[
+Name: LibGroupTalents-1.0
+Revision: $Rev: 55 $
+Author: Zek
+Documentation: http://wowace.com/wiki/LibGroupTalents-1.0
+SVN: svn://svn.wowace.com/wow/libgrouptalents-1-0/mainline/trunk
+Description: Talent Library abstraction layer to provide easy interface to the lower level system
+Dependancies: LibStub, CallbackHandler-1.0, LibTalentQuery-1.0
+License: GPL v3
+
+Purpose:
+ LibGroupTalents-1.0 is intended to do the following basic functions usually handled at the mod level.
+
+ - Maintain a raid wide table of talents, automatically updated on roster changes, notifying you on talent receipts.
+ - Provide easy access to talent queries (spec weight, spec name, specific talent presence)
+ - Monitor talent changes in players, and notify of changes (respec, talent swap, update after out of sight, level up)
+ - Monitor player roles, and notify of changes (melee, tank, healer, caster)
+ - Communicate directly with itself to other users to update talents via addon channel when possible
+
+Notes:
+ The LibTalentQuery-1.0 dependancy must be included before LibGroupTalents-1.0 in any lib.xml or mod side TOC declarations.
+
+Functions:
+ UnitHasTalent(unit, talentName[, group])-- Returns: Points spent in talent or nil
+ GUIDHasTalent(guid, talentName[, group])-- As UnitHasTalent
+ GetUnitTalentSpec(unitid[, group]) -- Returns: Dominant Tree, spent1, spent2, spent3
+ GetGUIDTalentSpec(guid[, group]) -- As GetUnitTalentSpec
+ GetUnitTalents(unit, refresh) -- Returns: Raw talent information in form of table of 3 strings of points spent. The refresh arg will force a re-query of the unit's talents
+ GetGUIDTalents(guid, refresh) -- As GetUnitTalents
+ GetUnitRole(unit) -- Returns one of: "melee", "caster", "healer", "tank"
+ GetGUIDRole(guid) -- As GetUnitRole
+ RefreshTalentsByUnit(unit) -- Force a refresh of talents for the specific unit
+ RefreshTalentsByGUID(guid) -- Force a refresh of talents for the specific player GUID
+ GetTreeNames(class) -- Returns: The three talent tree names for that class (Note: These return values are only valid after a player of that class has been inspected)
+ GetTreeIcons(class) -- Returns: The three talent tree icons for that class (Note: As above)
+ GetTalentCount() -- Returns: Talent info got, Talent info missing
+ GetTalentMissingNames() -- Returns: Comma delimited list of player names we're missing talents for
+ GetClassTalentInfo(class, talentName) -- Returns: Max Rank, Icon, Tab, Tier, Column, Tree Index
+ GetUnitStorageString(unit) -- Returns: An encoded data string containing talent information for the player which can be stored by mods to set in later sessions using SetStorageString()
+ GetGUIDStorageString(guid) -- As GetUnitStorageString
+ SetStorageString(talentString) -- Returns: true on success (applicable). Any second return value indicates the data was invalid and should not be kept
+ GetUnitGlyphs(unit[, group]) -- Returns: Up to 6 spell IDs for the currently assigned Glyphs (Note: For the moment, we can only see the glyphs of players running LibGroupTalents-1.0)
+ GetGUIDGlyphs(guid[, group]) -- As GetUnitGlyphs
+ UnitHasGlyph(unit, glyph [, group]) -- Returns: true if the player has the glyph associated with spellID or spellName (Note: For the moment, we can only see the glyphs of players running LibGroupTalents-1.0)
+ GUIDHasGlyph(unit, glyph [, group]) -- As UnitHasGlyph
+ PurgeAndRescanTalents() -- Wipe current roster of all talents and rescan from start
+
+Convenience Functions (Similar to Blizzard API functions, but callable with a unit ID):
+ GetActiveTalentGroup(unit) -- Returns: Active talent group for unit
+ GetNumTalentGroups(unit) -- Returns: Number of talent groups for unit
+ GetNumTalentTabs(unit) -- Returns: Number of talent tabs. Here's a clue; it's going to be 3...
+ GetTalentTabInfo(unit, tab[, group]) -- Returns: Tree Name, Tree Icon, Points Spent, Tree Background
+ GetNumTalents(unit, tab) -- Returns: Number of talents for specified tree
+ GetTalentInfo(unit, tab, index[, group])-- Returns: Talent Name, Icon, Tier, Column, Points Spent, Max Rank (Note that preview return values are not given unless called with "player")
+ GetUnspentTalentPoints(unit[, group]) -- Returns: Number of un-spent talent points for the unit
+
+Events:
+ LibGroupTalents_Update(guid, unit, newSpec, n1, n2, n3 [, oldSpec, o1, o2, o3]) -- Received updated talents. If it's a respec, or old set is know, it passes the old info also (this is not sent if new talent scan is same as previous)
+ LibGroupTalents_UpdateComplete(guid1, guid2[, ...]) -- Sent when there are no more pending talent reads due (passes all GUIDs that were updated since last time this event was fired)
+ LibGroupTalents_Add(guid, unit, name, realm) -- Unit added to talent roster (Talents not necessarily available yet, but this is the mod's chance to feed talents using SetStorageString)
+ LibGroupTalents_Remove(guid, name, realm) -- Unit removed from talent roster (This is your last chance to store talents if required using GetUnitStorageString)
+ LibGroupTalents_RoleChange(guid, unit, newrole, oldrole) -- Roles are: "melee", "caster", "healer", "tank"
+ LibGroupTalents_GlyphUpdate(guid, unit) -- Fired when a player's glyphs change (Note: For the moment, we can only see the glyphs of players running LibGroupTalents-1.0)
+
+]]
+
+local TalentQuery = LibStub("LibTalentQuery-1.0")
+
+local MAJOR, MINOR = "LibGroupTalents-1.0", tonumber(("$Rev: 55 $"):match("(%d+)"))
+local lib = LibStub:NewLibrary(MAJOR, MINOR)
+if not lib then return end
+
+local ChatThrottleLib = _G.ChatThrottleLib
+
+lib.roster = lib.roster or {}
+lib.classTalentData = lib.classTalentData or {}
+lib.batch = lib.batch or {}
+lib.pendingStorageStrings = lib.pendingStorageStrings or {}
+
+local function UnitFullName(unit)
+ local name, realm = UnitName(unit)
+ local namerealm = realm and realm ~= "" and name .. "-" .. realm or name
+ return namerealm
+end
+
+local function RosterInfoFullName(info)
+ local name, realm = info.name, info.realm
+ local namerealm = realm and realm ~= "" and name .. "-" .. realm or name
+ return namerealm
+end
+
+local specChangers = {}
+for index,spellid in ipairs(_G.TALENT_ACTIVATION_SPELLS) do
+ specChangers[GetSpellInfo(spellid)] = index
+end
+
+local frame = lib.frame
+if (not frame) then
+ frame = CreateFrame("Frame", "LibGroupTalents_Frame")
+ lib.frame = frame
+end
+frame:UnregisterAllEvents()
+frame:RegisterEvent("RAID_ROSTER_UPDATE")
+frame:RegisterEvent("PARTY_MEMBERS_CHANGED")
+frame:RegisterEvent("UNIT_NAME_UPDATE")
+frame:RegisterEvent("PLAYER_TALENT_UPDATE")
+frame:RegisterEvent("UNIT_LEVEL")
+frame:RegisterEvent("UNIT_AURA") -- Always get a UNIT_AURA when a unit's UnitIsVisible() changes
+frame:RegisterEvent("CHAT_MSG_ADDON")
+frame:RegisterEvent("UNIT_SPELLCAST_SUCCEEDED")
+frame:RegisterEvent("PLAYER_LOGIN")
+frame:RegisterEvent("GLYPH_ADDED")
+frame:RegisterEvent("GLYPH_REMOVED")
+frame:RegisterEvent("GLYPH_UPDATED")
+
+frame:SetScript("OnEvent", function(self, event, ...)
+ return lib[event](lib, ...)
+end)
+
+if not lib.events then
+ lib.events = LibStub("CallbackHandler-1.0"):New(lib)
+end
+
+local next, select, pairs, type = next, select, pairs, type
+local new, del, deepDel
+do
+ local list = setmetatable({},{__mode='k'})
+ function new(...)
+ local t = next(list)
+ if t then
+ list[t] = nil
+ for i = 1, select('#', ...) do
+ t[i] = select(i, ...)
+ end
+ return t
+ else
+ return {...}
+ end
+ end
+ function del(t)
+ if (t) then
+ wipe(t)
+ t[''] = true
+ t[''] = nil
+ list[t] = true
+ end
+ end
+ function deepDel(t)
+ if (t) then
+ for k,v in pairs(t) do
+ if type(v) == "table" then
+ deepDel(v)
+ end
+ t[k] = nil
+ end
+ t[''] = true
+ t[''] = nil
+ list[t] = true
+ end
+ end
+end
+
+do
+ local delay = 0
+ frame:SetScript("OnUpdate", function(self, elapsed)
+ if (lib.raidRosterUpdate) then
+ lib.raidRosterUpdate = nil
+ lib:OnRaidRosterUpdate()
+ end
+
+ if (lib.refreshCheckTimer) then
+ lib.refreshCheckTimer = lib.refreshCheckTimer - elapsed
+ if (lib.refreshCheckTimer < 0) then
+ lib.refreshCheckTimer = nil
+ lib:CheckForMissingTalents()
+ end
+ end
+
+ if (lib.talentTimers) then
+ delay = delay + elapsed
+ if (delay > 1) then
+ delay = 0
+ local now = GetTime()
+ local triggers
+ for guid,when in pairs(lib.talentTimers) do
+ if (now > when) then
+ -- Pass to second table to process, because RefreshTimers can affect this talentTimers table
+ -- So it's important we're not still iterating it at the time
+ if (not triggers) then
+ triggers = new()
+ end
+ triggers[guid] = true
+ lib.talentTimers[guid] = nil
+ if (not next(lib.talentTimers)) then
+ lib.talentTimers = del(lib.talentTimers)
+ break
+ end
+ end
+ end
+
+ if (triggers) then
+ for guid in pairs(triggers) do
+ lib:RefreshTalentsByGUID(guid)
+ end
+ del(triggers)
+ end
+ end
+ end
+
+ if (not lib.talentTimers and not lib.refreshCheckTimer) then
+ self:Hide()
+ end
+ end)
+end
+frame:Show()
+lib.raidRosterUpdate = true
+
+-- GetGUIDTalentsRaw
+local function GetGUIDTalentsRaw(guid, group)
+ local r = guid and lib.roster[guid]
+ return r and r.talents and r.talents[group or r.active], r
+end
+
+-- PLAYER_LOGIN
+function lib:PLAYER_LOGIN()
+ ChatThrottleLib = _G.ChatThrottleLib
+ lib.PLAYER_LOGIN = nil
+end
+
+-- RAID_ROSTER_UPDATE
+function lib:RAID_ROSTER_UPDATE()
+ self.raidRosterUpdate = true
+ frame:Show()
+end
+lib.PARTY_MEMBERS_CHANGED = lib.RAID_ROSTER_UPDATE
+
+-- UNIT_NAME_UPDATE
+function lib:UNIT_NAME_UPDATE(unit)
+ local guid = unit and UnitGUID(unit)
+ local r = guid and self.roster[guid]
+ if (r) then
+ local needsAdd = r.name == UNKNOWN
+ r.name, r.realm = UnitName(unit)
+ if (r.realm == "") then
+ r.realm = nil
+ end
+ r.class = select(2, UnitClass(unit))
+ r.level = UnitLevel(unit)
+ if (not r.talents) then
+ if (needsAdd) then
+ self.events:Fire("LibGroupTalents_Add", guid, unit, r.name, r.realm)
+ end
+
+ self:CheckForMissingTalents()
+ end
+ end
+end
+
+-- AnyPending
+local function AnyPending()
+ local checkUpdate
+ for guid,info in pairs(lib.roster) do
+ local namerealm = RosterInfoFullName(info)
+ if (UnitIsConnected(namerealm)) then
+ if (lib.wasOffline) then
+ lib.wasOffline[guid] = nil
+ end
+ if (not info.talents or info.refresh) then
+ return true
+ end
+ else
+ if (not lib.wasOffline) then
+ lib.wasOffline = new()
+ end
+ lib.wasOffline[guid] = true
+ end
+ end
+ if (lib.wasOffline and not next(lib.wasOffline)) then
+ lib.wasOffline = del(lib.wasOffline)
+ end
+end
+
+-- CheckForUpdateComplete
+local function CheckForUpdateComplete()
+ -- When all pending updates are complete, send an event to notify nothing else is due
+ if (next(lib.batch)) then
+ if (not AnyPending()) then
+ lib.events:Fire("LibGroupTalents_UpdateComplete", unpack(lib.batch))
+ wipe(lib.batch)
+ end
+ end
+end
+
+-- UNIT_LEVEL
+function lib:UNIT_LEVEL(unit)
+ if (UnitInParty(unit) or UnitInRaid(unit)) then
+ local guid = UnitGUID(unit)
+ local r = guid and self.roster[guid]
+ if (r) then
+ r.level = UnitLevel(unit)
+ self:RefreshTalentsByUnit(unit)
+ end
+ end
+end
+
+-- UNIT_AURA
+function lib:UNIT_AURA(unit)
+ local guid = UnitGUID(unit)
+ if (not UnitIsVisible(unit) or (self.wasOffline and self.wasOffline[guid])) then
+ if (not self.outOfSight) then
+ self.outOfSight = {}
+ end
+ self.outOfSight[guid] = true
+ self:RefreshTalentsByGUID(guid)
+ end
+end
+
+-- OnRaidRosterUpdate
+function lib:OnRaidRosterUpdate()
+ local instanceType = select(2, IsInInstance())
+ if (instanceType == "pvp" or instanceType == "arena") then
+ self.distribution = "BATTLEGROUND"
+ else
+ if (GetNumRaidMembers() > 0) then
+ self.distribution = "RAID"
+ elseif (GetNumPartyMembers() > 0) then
+ self.distribution = "PARTY"
+ else
+ self.distribution = nil
+ end
+ end
+ if (self.distribution) then
+ if (self.sentHello ~= self.distribution) then
+ self.sentHello = self.distribution
+ self:SendCommMessage("HELLO "..MINOR, nil, self.distribution)
+ end
+ else
+ self.sentHello = nil
+ self.talentThrottle = del(self.talentThrottle)
+ self.wasOffline = del(self.wasOffline)
+ self.outOfSight = del(self.outOfSight)
+ wipe(self.pendingStorageStrings)
+ end
+
+ -- Now check for roster changes
+ local subtractions = new()
+ local additions = new()
+ local changes = new()
+
+ if (self.roster) then
+ for guid,info in pairs(self.roster) do
+ subtractions[guid] = info.level or 0
+ end
+ end
+
+ for unit in self:IterateRoster() do
+ local guid = UnitGUID(unit)
+ if (guid) then
+ local n = self.roster[guid]
+ if (not n) then
+ n = new()
+ self.roster[guid] = n
+ end
+
+ n.name, n.realm = UnitName(unit)
+ if (n.realm == "") then
+ n.realm = nil -- Fix this already..
+ end
+ n.level = UnitLevel(unit)
+ n.class = select(2, UnitClass(unit))
+ n.unit = unit
+
+ if (subtractions[guid]) then
+ if (subtractions[guid] ~= n.level) then
+ changes[guid] = unit -- Level changed, needs a rescan
+ end
+
+ subtractions[guid] = nil
+ else
+ if (n.name ~= UNKNOWN) then
+ self.events:Fire("LibGroupTalents_Add", guid, unit, n.name, n.realm)
+ end
+ additions[guid] = unit
+ end
+ end
+ end
+
+ if (next(additions)) then
+ for guid,unit in pairs(additions) do
+ self:GetUnitTalents(unit)
+ end
+ end
+
+ if (next(changes)) then
+ for guid,unit in pairs(changes) do
+ self:GetUnitTalents(unit)
+ end
+ end
+
+ if (next(subtractions)) then
+ for guid in pairs(subtractions) do
+ local r = self.roster[guid]
+ if (r) then
+ self.events:Fire("LibGroupTalents_Remove", guid, r.name, r.realm)
+ self.roster[guid] = deepDel(r)
+
+ local classStorageStrings = self.pendingStorageStrings[r.class]
+ if (classStorageStrings) then
+ classStorageStrings[guid] = del(classStorageStrings[guid])
+ if (not next(classStorageStrings)) then
+ self.pendingStorageStrings[r.class] = del(self.pendingStorageStrings[r.class])
+ end
+ end
+ end
+ end
+
+ CheckForUpdateComplete()
+ end
+
+ del(additions)
+ del(subtractions)
+ del(changes)
+
+ self:CheckForMissingTalents()
+end
+
+-- ValidateUnit
+local function ValidateUnit(r, guid)
+ local unit = r.unit
+ if (UnitGUID(unit) ~= guid) then
+ local name = r.name .. (r.realm and "-" or "") .. (r.realm or "")
+ local index = UnitInRaid(name)
+ if (index) then
+ r.unit = "raid"..index
+ return true
+ else
+ if (UnitGUID("player") == guid) then
+ r.unit = "player"
+ return true
+
+ elseif (UnitInParty(name)) then
+ for i = 1,4 do
+ if (UnitGUID("party"..i) == guid) then
+ r.unit = "party"..i
+ return true
+ end
+ end
+ end
+ end
+ return
+ end
+
+ return true
+end
+
+-- CountTree
+local function CountTree(branch)
+ local count = 0
+ for i = 1,#branch do
+ count = count + branch:byte(i) - 48
+ end
+ return count
+end
+
+-- TalentWeight
+local function TalentWeight(talents, class)
+ if (talents and #talents == 3 and class) then
+ local c1, c2, c3 = CountTree(talents[1]), CountTree(talents[2]), CountTree(talents[3])
+
+ local weight = 1
+ if (c2 > c1 and c2 > c3) then
+ weight = 2
+ elseif (c3 > c1 and c3 > c2) then
+ weight = 3
+ end
+
+ local data = lib.classTalentData[class]
+ if (data and data[weight]) then
+ return data[weight].name, c1, c2, c3
+ end
+
+ return weight, c1, c2, c3
+ end
+ return nil, 0, 0, 0
+end
+
+do
+-- First segment: Player ID (from GUID), Name, level, class, activePage, TalentString
+-- Subsequent: spec number, talentString()
+
+ -- crc
+ local function crc32(str)
+ local val = tonumber((select(2, GetBuildInfo()))) -- Use WoW build as CRC base
+ for i = 1,#str do
+ val = bit.band(val * 2 + str:byte(i), 0xFFFF)
+ end
+ return val
+ end
+
+ -- GetUnitStorageString
+ function lib:GetUnitStorageString(unit)
+ return self:GetGUIDStorageString(UnitGUID(unit))
+ end
+
+ -- GetGUIDStorageString
+ -- Make a storage string for mods to store talents.
+ -- Rules: 1) Your own realm only 2) Their talents are complete (nothing unspent)
+ function lib:GetGUIDStorageString(guid)
+ local r = self.roster[guid]
+ if (r) then
+ local id
+ local playerGUID = UnitGUID("player")
+ if (playerGUID:sub(1, 6) == guid:sub(1, 6)) then
+ -- Same realm code, so just trim it off. This is likely always true from what I've seen
+ id = format("%X", tonumber(guid:sub(7), 16))
+ else
+ id = guid:sub(4)
+ end
+
+ if (r.talents and r.active and not r.realm and (not r.unspent or not r.unspent[r.active])) then
+ if (r.level < 1) then
+ r.level = UnitLevel(r.name) or 0
+ end
+ local str = format("%s,%d,%s,%d,%d", id, r.level, r.class, r.active, r.numActive)
+ for i = 1,r.numActive do
+ local t = r.talents[i]
+ if (t) then
+ str = format("%s;%d,%s", str, i, table.concat(t, "-"))
+ end
+ end
+ return format("%s;%d", str, crc32(str))
+ end
+ end
+ end
+
+ -- SetStorageString
+ function lib:SetStorageString(str, comms)
+ local ret, retInfo
+ if (str) then
+ local parts = new(strsplit(";", str))
+ if (#parts >= 2) then
+ local strCRC = tonumber(parts[#parts])
+ local temp = table.concat(parts, ";", 1, #parts - 1)
+ if (crc32(temp) == strCRC) then
+ local part1 = new(strsplit(",", parts[1]))
+
+ while true do
+ local guid
+ local id = part1[1]
+ if (id:len() < 12) then
+ -- Trimmed GUID, we'll prefix it with our own GUID's realm code
+ guid = format("%s%012X", UnitGUID("player"):sub(1, 6), tonumber(id, 16))
+ else
+ guid = format("0x0%015s", id)
+ end
+
+ local r = self.roster[guid]
+ if (not r) then
+ retInfo = format("Unexpected SetStorageString for ID %s", guid)
+ ret = true -- Still return true, we just didn't want this string yet
+ break
+ elseif (r.name == UNKNOWN) then
+ retInfo = format("Premature SetStorageString for ID %s", guid)
+ ret = true -- Still return true, we just didn't want this string yet
+ break
+ end
+ if (r.talents) then
+ -- We've already received talents for this player
+ ret = true -- Still return true, we just didn't want this string because we have their talents
+ break
+ end
+
+ if (not self.classTalentData[r.class]) then
+ -- Received a storage string for a class that we've not yet been able to scan
+ -- the talent trees for. We store this until we have that data
+ local classStorageStrings = self.pendingStorageStrings[r.class]
+ if (not classStorageStrings) then
+ classStorageStrings = new()
+ self.pendingStorageStrings[r.class] = classStorageStrings
+ end
+ classStorageStrings[guid] = str
+ ret = true
+ break
+ end
+
+ local level = tonumber(part1[2])
+ local class = part1[3]
+ local active = tonumber(part1[4])
+ local numActive = tonumber(part1[5])
+
+ if (r.level < 1) then
+ r.level = UnitLevel(r.name) or 0
+ end
+ if (level ~= r.level and r.level > 1) then
+ -- Won't accept talents for mismatched levels (but ignore errors reading the UnitLevel early)
+ retInfo = "Wrong level"
+ break
+ end
+ if (not r.class and class) then
+ -- If we don't have the class, but the storage string does, we'll take it
+ r.class = class
+ end
+ if (class ~= r.class) then
+ -- Class doesn't match, probably a char delete/remake or xrealm
+ retInfo = format("Wrong class: expected %q, got %q", tostring(r.class), tostring(class))
+ break
+ end
+
+ -- Now the talent trees
+ local talents = new()
+ for i = 2,#parts - 1 do
+ local partN = new(strsplit(",", parts[i]))
+ if (#partN == 2) then
+ local specNumber = tonumber(partN[1])
+ local specTalents = new(strsplit("-", partN[2]))
+
+ if (specNumber and #specTalents >= 3) then
+ talents[specNumber] = specTalents
+ else
+ del(specTalents)
+ talents = del(talents)
+ retInfo = "Invalid talent specs in tree "..i
+ break
+ end
+ end
+ end
+
+ if (talents) then
+ r.talents = talents
+ r.active = active
+ r.numActive = numActive
+ if (comms ~= r.name) then
+ -- If comms part sends player name along with packet, then we'll skip the refresh later
+ -- which we'd normally do when Storage is set via app startup
+ r.refresh = true
+ else
+ r.refresh = nil
+ end
+
+ ValidateUnit(r, guid)
+ local newSpec, n1, n2, n3 = TalentWeight(r.talents[r.active], r.class)
+ self.events:Fire("LibGroupTalents_Update", guid, r.unit, newSpec, n1, n2, n3)
+ self:GetGUIDRole(guid, true)
+ ret = true
+ end
+ break
+ end
+
+ del(part1)
+ else
+ retInfo = "Invalid string"
+ end
+ end
+ del(parts)
+ end
+
+ return ret, retInfo
+ end
+end
+
+-- GetClassTalentData
+-- Builds an internal table for talent name -> tree/index lookups.
+function GetClassTalentData(unit)
+ local _, class = UnitClass(unit)
+ if (class) then
+ local data = lib.classTalentData[class]
+ if (not data) then
+ local isnotplayer = not UnitIsUnit("player", unit)
+ if (GetNumTalentTabs(isnotplayer) > 0) then
+ data = new()
+
+ for tab = 1, GetNumTalentTabs(isnotplayer) do
+ local tree = new()
+ local _
+ tree.name, tree.icon, _, tree.background = GetTalentTabInfo(tab, isnotplayer)
+ tinsert(data, tree)
+
+ tree.list = new()
+ for i = 1,GetNumTalents(tab, isnotplayer) do
+ local name, icon, tier, column, currentRank, maxRank = GetTalentInfo(tab, i, isnotplayer)
+ if (name) then
+ local entry = new()
+ entry.name = name
+ entry.icon = icon
+ entry.tier = tier
+ entry.column = column
+ entry.maxRank = maxRank
+ entry.index = i
+ entry.treeIndex = tab
+ tinsert(tree.list, entry)
+ if (not data.list) then
+ data.list = new()
+ end
+ data.list[name] = entry
+ end
+ end
+ end
+
+ if (next(data)) then
+ lib.classTalentData[class] = data
+
+ --for guid,r in pairs(lib.roster) do
+ -- if (r.class == class and r.talents) then
+ -- -- We picked up class talent data for a class after receiving talents for them via comms
+ -- -- So, we fire an Update event for any members of the class we already have so that
+ -- -- talents can now be interpreted correctly.
+ -- local spec, n1, n2, n3 = TalentWeight(r.talents[r.active], class)
+ -- lib.events:Fire("LibGroupTalents_Update", guid, unit, spec, n1, n2, n3)
+ -- end
+ --end
+
+ local classStorageStrings = lib.pendingStorageStrings[class]
+ if (classStorageStrings) then
+ local unitGUID = UnitGUID(unit)
+ for guid, str in pairs(classStorageStrings) do
+ if (guid ~= unitGUID) then
+ lib:SetStorageString(str)
+ end
+ end
+ lib.pendingStorageStrings[class] = del(lib.pendingStorageStrings[class])
+ end
+ else
+ deepDel(data)
+ end
+ end
+ end
+ end
+end
+
+-- GetTreeNames
+function lib:GetTreeNames(class)
+ local info = self.classTalentData[class]
+ if (info) then
+ return info[1].name, info[2].name, info[3].name
+ end
+end
+
+-- GetTreeIcons
+function lib:GetTreeIcons(class)
+ local info = self.classTalentData[class]
+ if (info) then
+ return info[1].icon, info[2].icon, info[3].icon
+ end
+end
+
+-- ReadTalentGroup
+local function ReadTalentGroup(isnotplayer, group, class)
+ local numTabs = GetNumTalentTabs(isnotplayer)
+ if (numTabs and numTabs >= 3 and GetNumTalents(1, isnotplayer) > 0) then
+ local ctd = lib.classTalentData[class]
+--[===[@debug@
+assert(ctd and ctd[1] and ctd[2] and ctd[3])
+assert(ctd[1].list and ctd[2].list and ctd[3].list)
+--@end-debug@]===]
+
+ local n = new()
+ for tab = 1, numTabs do
+ local branchLength = GetNumTalents(tab, isnotplayer, nil, group)
+ if (branchLength ~= #ctd[tab].list) then
+ -- Tab tree size is not what we expected for this class
+ del(n)
+ return
+ end
+
+ local t = new()
+ local trim
+ for i = 1,branchLength do
+ local name, icon, tier, column, currentRank, maxRank = GetTalentInfo(tab, i, isnotplayer, nil, group)
+ tinsert(t, currentRank)
+ if (currentRank > 0) then
+ trim = i -- We strip off trailing zeros from talent strings to save storage space
+ end
+ end
+
+ tinsert(n, table.concat(t, nil, 1, trim or 0))
+ del(t)
+ end
+
+ return n
+ end
+end
+
+-- TalentQuery_Ready
+function lib:TalentQuery_Ready_Outsider(e, name, realm, unit)
+ self:TalentQuery_Ready(e, name, realm, unit)
+end
+
+-- TalentQuery_Ready
+function lib:TalentQuery_Ready(e, name, realm, unit)
+ GetClassTalentData(unit)
+
+ local guid = unit and UnitGUID(unit)
+ local r = guid and self.roster[guid]
+ if (r) then
+ local namerealm = realm and realm ~= "" and name .. "-" .. realm or name
+ local isnotplayer = not UnitIsUnit(unit, "player")
+
+ if (GetTalentTabInfo(1, isnotplayer)) then
+ local active = GetActiveTalentGroup(isnotplayer)
+ local numActive = GetNumTalentGroups(isnotplayer)
+ local listUnspent, invalid
+ local talents = new()
+
+ for group = 1,numActive do
+ local n = ReadTalentGroup(isnotplayer, group, r.class)
+ if (n and #n >= 3) then
+ talents[group] = n
+ else
+ invalid = true
+ break
+ end
+
+ local unspent = GetUnspentTalentPoints(isnotplayer, nil, group)
+ if (unspent and unspent > 0) then
+ if (not listUnspent) then
+ listUnspent = new()
+ end
+ listUnspent[group] = unspent
+ end
+ end
+
+ if (isnotplayer and (invalid or (listUnspent and listUnspent[active] or 0) > 0)) then
+ -- Unit didn't have all their points spent in active group, so we'll have another look in 10 seconds
+ -- Don't need to check when it's "player" because we get PLAYER_TALENT_UPDATE event on changes
+ self:TriggerRefreshTalents(guid, 10)
+ end
+
+ if (not invalid) then
+ if (active > numActive) then
+ -- May be better to discard instead? We'll see
+ active = 1
+ end
+ self:OnReceiveTalents(guid, unit, talents, active, numActive, listUnspent)
+ end
+ end
+ end
+end
+TalentQuery.RegisterCallback(lib, "TalentQuery_Ready")
+TalentQuery.RegisterCallback(lib, "TalentQuery_Ready_Outsider")
+
+-- GetUnitTalentSpec
+function lib:GetUnitTalentSpec(unit, group)
+ return self:GetGUIDTalentSpec(UnitGUID(unit), group)
+end
+
+-- GetGUIDTalentSpec
+function lib:GetGUIDTalentSpec(guid, group)
+ local talents, r = GetGUIDTalentsRaw(guid, group)
+ if (talents) then
+ return TalentWeight(talents, r.class)
+ end
+end
+
+-- CompareTalents
+local function CompareTalents(tree1, tree2)
+ if ((tree1 ~= nil) ~= (tree2 ~= nil)) then
+ return
+ end
+ if (tree1 and tree2 and #tree1 == #tree2) then
+ for i = 1,#tree1 do
+ if (tree1[i] ~= tree2[i]) then
+ return
+ end
+ end
+ return true
+ end
+end
+
+-- OnReceiveTalents
+function lib:OnReceiveTalents(guid, unit, talents, active, numActive, listUnspent)
+ local r = self.roster[guid]
+ if (r) then
+ if (active ~= r.active or numActive ~= r.numActive or not CompareTalents(talents and talents[active], r.talents and r.talents[r.active])) then
+ local oldTalents
+ if (r.talents) then
+ oldTalents = r.talents[r.active]
+ end
+ del(r.unspent)
+
+ r.talents = talents
+ r.active = active
+ r.numActive = numActive
+ r.unspent = listUnspent
+
+ local newSpec, n1, n2, n3 = TalentWeight(r.talents[active], r.class)
+
+ local fired
+ if (oldTalents) then
+ -- For those cases when we didn't have the alternate talents for a player for whatever reason.
+ -- Maybe they just picked up dual talent spec, or gated to trainer to respec.
+ local oldSpec, o1, o2, o3 = TalentWeight(oldTalents, r.class)
+
+ if (o1 ~= n1 or o2 ~= n2 or o3 ~= n3) then
+ self.events:Fire("LibGroupTalents_Update", guid, unit, newSpec, n1, n2, n3, oldSpec, o1, o2, o3)
+ fired = true
+ end
+ end
+
+ if (not fired) then
+ self.events:Fire("LibGroupTalents_Update", guid, unit, newSpec, n1, n2, n3)
+ end
+ self:GetGUIDRole(guid, true)
+
+ tinsert(self.batch, guid)
+ CheckForUpdateComplete()
+
+ oldTalents = del(oldTalents)
+ return
+ end
+ end
+ del(talents)
+end
+
+-- OnReceiveGlyphs
+function lib:OnReceiveGlyphs(guid, sender, glyphs)
+ local r = self.roster[guid]
+ if (r) then
+ if (ValidateUnit(r, guid)) then
+ local oldGlyphs
+ if (r.glyphs) then
+ oldGlyphs = r.glyphs[r.active]
+ r.glyphs = del(r.glyphs)
+ end
+
+ r.glyphs = glyphs
+
+ local newGlyphs = r.glyphs and r.glyphs[r.active]
+ if (newGlyphs ~= oldGlyphs) then
+ self.events:Fire("LibGroupTalents_GlyphUpdate", guid, r.unit)
+ end
+ return
+ end
+ end
+
+ del(glyphs)
+end
+
+-- GetUnitGlyphs
+function lib:GetUnitGlyphs(unit, group)
+ return self:GetGUIDGlyphs(UnitGUID(unit), group)
+end
+
+-- GetGUIDGlyphs
+function lib:GetGUIDGlyphs(guid, group)
+ local r = self.roster[guid]
+ if (r) then
+ local g = r.glyphs and r.glyphs[group or r.active]
+ if (g) then
+ local temp = new(strsplit(",", g))
+ for i,str in ipairs(temp) do
+ temp[i] = tonumber(str)
+ end
+ local a, b, c, d, e, f = unpack(temp)
+ del(temp)
+ return a, b, c, d, e, f
+ end
+ end
+end
+
+-- UnitHasGlyph
+function lib:UnitHasGlyph(unit, glyphID, group)
+ return lib:GUIDHasGlyph(UnitGUID(unit), glyphID, group)
+end
+
+-- GUIDHasGlyph
+function lib:GUIDHasGlyph(guid, glyphID, group)
+ local ret
+ local r = self.roster[guid]
+ if (r) then
+ local g = r.glyphs and r.glyphs[group or r.active]
+ if (g) then
+ local temp = new(strsplit(",", g))
+ for i,str in ipairs(temp) do
+ local id = tonumber(str)
+ if (type(glyphID) == "number") then
+ if (glyphID == id) then
+ ret = true
+ break
+ end
+ else
+ if (glyphID == GetSpellInfo(id)) then
+ ret = true
+ break
+ end
+ end
+ end
+ del(temp)
+ end
+ end
+ return ret
+end
+
+-- GLYPH_ADDED
+function lib:GLYPH_ADDED(index, a, b, c)
+ self:RefreshPlayerGlyphs()
+end
+
+-- GLYPH_REMOVED
+function lib:GLYPH_REMOVED(index, a, b, c)
+ self:RefreshPlayerGlyphs()
+end
+
+-- GLYPH_UPDATED
+function lib:GLYPH_UPDATED(index, a, b, c)
+ self:RefreshPlayerGlyphs()
+end
+
+-- RefreshPlayerGlyphs
+function lib:RefreshPlayerGlyphs()
+ local guid = UnitGUID("player")
+ local r = self.roster[guid]
+ if (not r) then
+ return
+ end
+
+ local glyphs = new()
+ local any
+ for talentGroup = 1,GetNumTalentGroups() do
+ local list = new()
+ for i = 1,GetNumGlyphSockets() do
+ local enabled, glyphType, glyphSpell, icon = GetGlyphSocketInfo(i, talentGroup)
+ if (enabled and glyphType and glyphSpell) then
+ tinsert(list, glyphSpell)
+ any = true
+ end
+ end
+ glyphs[talentGroup] = table.concat(list, ",")
+ del(list)
+ end
+
+ local oldGlyphs = r.glyphs
+ if (any) then
+ r.glyphs = glyphs
+ else
+ del(glyphs)
+ end
+
+ local change = (oldGlyphs and oldGlyphs[r.active]) ~= (r.glyphs and r.glyphs[r.active])
+ if (change) then
+ self:SendMyGlyphs()
+ self.events:Fire("LibGroupTalents_GlyphUpdate", guid, "player")
+ end
+
+ del(oldGlyphs)
+end
+
+-- PLAYER_TALENT_UPDATE
+function lib:PLAYER_TALENT_UPDATE()
+ self:TriggerRefreshTalents(UnitGUID("player"), 2)
+end
+
+-- UNIT_SPELLCAST_SUCCEEDED
+function lib:UNIT_SPELLCAST_SUCCEEDED(unit, spell)
+ local newActiveGroup = specChangers[spell]
+ if (newActiveGroup) then
+ local guid = UnitGUID(unit)
+ local r = guid and self.roster[guid]
+ if (r) then
+ if (newActiveGroup == r.active) then
+ -- We obviously didn't see them switch from this set
+ self:GetGUIDRole(guid, true)
+ return
+ end
+
+ if (r.talents) then
+ local oldSet = r.talents[r.active]
+ local newSet = r.talents[newActiveGroup]
+ if (oldSet and newSet) then
+ -- We have the other talent set, so no need to refresh anything. Just compare and notify
+ r.active = newActiveGroup
+
+ local oldSpec, o1, o2, o3 = TalentWeight(oldSet, r.class)
+ local newSpec, n1, n2, n3 = TalentWeight(newSet, r.class)
+
+ if (o1 ~= n1 or o2 ~= n2 or o3 ~= n3) then
+ self.events:Fire("LibGroupTalents_Update", guid, unit, newSpec, n1, n2, n3, oldSpec, o1, o2, o3)
+ else
+ self.events:Fire("LibGroupTalents_Update", guid, unit, newSpec, n1, n2, n3)
+ end
+ self:GetGUIDRole(guid, true)
+ return
+ end
+ end
+
+ -- If we get this far, then someone probably gated to respec
+ self:RefreshTalentsByGUID(guid)
+ end
+ end
+end
+
+-- TriggerRefreshTalents
+function lib:TriggerRefreshTalents(guid, delay)
+ if (not self.talentTimers) then
+ self.talentTimers = new()
+ end
+ if (guid) then
+ self.talentTimers[guid] = GetTime() + delay
+ frame:Show()
+ end
+end
+
+-- RefreshTalentsByUnit
+function lib:RefreshTalentsByUnit(unit)
+ local guid = UnitGUID(unit)
+ if (guid) then
+ self:RefreshTalentsByGUID(guid)
+ end
+end
+
+-- RefreshTalentsByGUID
+function lib:RefreshTalentsByGUID(guid)
+ local r = self.roster[guid]
+ if (not r) then
+ return
+ end
+ if (not ValidateUnit(r, guid)) then
+ return
+ end
+
+ if (self.talentTimers) then
+ self.talentTimers[guid] = nil
+ end
+
+ if (not self.talentThrottle) then
+ self.talentThrottle = {}
+ end
+ for guidThrottle,when in pairs(self.talentThrottle) do
+ if (when < GetTime() - 5) then
+ self.talentThrottle[guidThrottle] = nil
+ elseif (guid == guidThrottle) then
+ return
+ end
+ end
+ self.talentThrottle[guid] = GetTime()
+
+ if (self.commQueried) then
+ self.commQueried[guid] = nil
+ if (not next(self.commQueried)) then
+ self.commQueried = del(self.commQueried)
+ end
+ end
+
+ r.refresh = true
+ self:CheckForMissingTalents()
+
+ if (UnitGUID("player") == guid) then
+ self:SendMyTalents()
+ end
+end
+
+-- CheckForMissingTalents
+function lib:CheckForMissingTalents()
+ local any
+ for guid,info in pairs(self.roster) do
+ local namerealm = RosterInfoFullName(info)
+ if (not info.talents or (not UnitIsVisible(namerealm) and UnitExists(namerealm)) or info.refresh) then
+ any = true
+ info.refresh = nil
+ self:GetUnitTalents(info.unit, true)
+ end
+ end
+
+ if (any) then
+ lib.refreshCheckTimer = 15
+ frame:Show()
+ end
+end
+
+do
+ local survivalOfTheFittest = GetSpellInfo(33853) -- Survival of the Fittest
+ local protectorOfThePack = GetSpellInfo(57873) -- Protector of the Pack
+ local dkBladeBarrier = GetSpellInfo(49182) -- Blade Barrier
+ local dkToughness = GetSpellInfo(49042) -- Toughness
+ local dkAnticipation = GetSpellInfo(55129) -- Anticipation
+
+ -- GetUnitRole
+ function lib:GetUnitRole(unit, reset)
+ local guid = UnitGUID(unit)
+ if (guid) then
+ return self:GetGUIDRole(guid, reset)
+ end
+ end
+
+ -- GetGUIDRole
+ function lib:GetGUIDRole(guid, reset)
+ local r = guid and self.roster[guid]
+ if (not r) then
+ return
+ end
+ if (r.role and not reset) then
+ return r.role
+ end
+ if (not ValidateUnit(r, guid)) then
+ return
+ end
+
+ local class = r.class
+ local role
+
+ local unit = r.unit
+ if (class == "ROGUE" or class == "HUNTER") then
+ role = "melee"
+ elseif (class == "MAGE" or class == "WARLOCK") then
+ role = "caster"
+ elseif (r.talents and r.talents[r.active]) then
+ if (class == "DEATHKNIGHT") then
+ local score = self:GUIDHasTalent(guid, dkBladeBarrier) and 1 or 0
+ score = score + (self:GUIDHasTalent(guid, dkToughness) and 1 or 0)
+ score = score + (self:GUIDHasTalent(guid, dkAnticipation) and 1 or 0)
+ role = score >= 2 and "tank" or "melee" -- Has 2 of the 3 tanking talents at least
+
+ else
+ local specName, t1, t2, t3 = TalentWeight(r.talents[r.active], class)
+
+ if (class == "PRIEST") then
+ role = ((t1 + t2) > t3) and "healer" or "caster"
+ elseif (class == "WARRIOR") then
+ role = ((t1 + t2) > t3) and "melee" or "tank"
+ else
+ local heavy = (t1 > t2 and t1 > t3 and 1) or (t2 > t1 and t2 > t3 and 2) or (t3 > t1 and t3 > t2 and 3) or 0
+ if (class == "PALADIN") then
+ role = heavy == 1 and "healer" or heavy == 2 and "tank" or heavy == 3 and "melee"
+
+ elseif (class == "DRUID") then
+ if (heavy == 2) then
+ if (self:GUIDHasTalent(guid, survivalOfTheFittest) and self:GUIDHasTalent(guid, protectorOfThePack)) then
+ role = "tank"
+ else
+ role = "melee"
+ end
+ else
+ role = heavy == 1 and "caster" or "healer"
+ end
+
+ elseif (class == "SHAMAN") then
+ role = heavy == 1 and "caster" or heavy == 2 and "melee" or heavy == 3 and "healer"
+ end
+ end
+ end
+ end
+
+ local oldrole = r.role
+ r.role = role
+
+ if (role and role ~= oldrole) then
+ self.events:Fire("LibGroupTalents_RoleChange", guid, unit, role, oldrole)
+ end
+ return role
+ end
+end
+
+-- GetUnitTalents
+function lib:GetUnitTalents(unit, refetch)
+ local guid = UnitGUID(unit)
+ if (not guid) then
+ return
+ end
+ return self:GetGUIDTalents(guid, refetch)
+end
+
+-- CanCommQuery
+local function CanCommQuery(guid)
+ if (not lib.commQueried or not lib.commQueried[guid]) then
+ if (not lib.commQueried) then
+ lib.commQueried = new()
+ end
+ lib.commQueried[guid] = true
+ return true
+ end
+end
+
+-- GetGUIDTalents
+function lib:GetGUIDTalents(guid, refetch)
+ local r = self.roster[guid]
+ if (not r) then
+ return
+ end
+ if (not ValidateUnit(r, guid)) then
+ return
+ end
+
+ local unit = r.unit
+ local name, realm = UnitName(unit)
+ local activeTalents = r.talents and r.talents[r.active]
+
+ if (activeTalents) then
+ -- If someone is out of sight, we won't catch their talent swap spell cast, so we'll invalidate them here and recheck talents
+ if ((not UnitIsVisible(unit) and UnitIsConnected(unit)) or (self.outOfSight and self.outOfSight[guid])) then
+ if (not r.version) then
+ refetch = true
+ end
+ end
+ end
+
+ if (not activeTalents or refetch) then
+ if (UnitIsUnit("player", unit)) then
+ self:RefreshPlayerGlyphs()
+ self:TalentQuery_Ready(nil, name, nil, unit)
+
+ elseif ((UnitInRaid(unit) or UnitInParty(unit)) and UnitIsConnected(unit)) then
+ TalentQuery:Query(unit)
+
+ local namerealm = UnitFullName(unit)
+ if (not r.talents and not r.requested) then
+ -- Don't need to query on a 'refetch' because they'll send changes anyway via comms
+ local skipGlyphs
+ if (not UnitIsVisible(unit) or not CanInspect(unit)) then
+ if (r.version) then
+ if (CanCommQuery(guid)) then
+ -- We request talents via comms for anyone that may be out of inspect range
+ self:SendCommMessage("REQUESTTALENTS", namerealm)
+ r.requested = true
+ skipGlyphs = true
+ end
+ end
+ end
+ end
+
+ if (not r.glyphs and not skipGlyphs) then
+ if (r.version and r.version >= 15) then
+ if (CanCommQuery(guid)) then
+ -- They're in range to inspect, but we'll still want to ask for their glyphs
+ self:SendCommMessage("REQUESTGLYPHS", namerealm)
+ end
+ end
+ end
+ end
+
+ if (self.outOfSight) then
+ self.outOfSight[guid] = nil
+ end
+ end
+
+ return activeTalents
+end
+
+-- SendCommMessage
+function lib:SendCommMessage(msg, target, channel)
+ if (msg) then
+ if (ChatThrottleLib) then
+ ChatThrottleLib:SendAddonMessage("NORMAL", MAJOR, msg, channel or "WHISPER", target)
+ else
+ SendAddonMessage(MAJOR, msg, channel or "WHISPER", target)
+ end
+ end
+end
+
+-- Throttle - Purposely local to here
+-- Abuse prevention. Yes, who would abuse addon comms? Noone would make a macro to crash a mod user would they. Right?
+-- Well, this one time, at band camp. Someone thought it was super funny to make a macro that DCd PallyPower users
+local throttle
+local function Throttle(sender, key)
+ if (not throttle) then
+ throttle = {}
+ end
+ local s = throttle[sender]
+ if (not s) then
+ s = {}
+ throttle[sender] = s
+ end
+
+ if ((s[key] or 0) < GetTime() - 4.5) then
+ -- Same message key only allowable once every 4.5 secs from 1 person (Respec cast time is 5 seconds)
+ s[key] = GetTime()
+ return true
+ end
+end
+
+-- CHAT_MSG_ADDON
+function lib:CHAT_MSG_ADDON(prefix, msg, channel, sender)
+ if (prefix == MAJOR) then
+ if (sender == UnitName("player")) then
+ return
+ elseif (not UnitInRaid(sender) and not UnitInParty(sender)) then
+ return
+ end
+
+ local guid = UnitGUID(sender)
+ if (not guid) then
+ return
+ end
+ local r = self.roster[guid]
+ if (not r) then
+ return
+ end
+
+ local cmd, str = msg:match("^(%a+) *(.*)$")
+ if (not cmd) then
+ return
+ end
+
+ if (cmd == "TALENTS") then
+ -- Talents come in form of:
+ local t = r.talents
+ r.talents = nil -- SetStorageString won't overwrite talents usually, but we want it to here, without providing a means to do it easily with an arg from a mod
+ if (not self:SetStorageString(str, sender)) then
+ r.talents = t
+ else
+ deepDel(t)
+ end
+
+ elseif (cmd == "GLYPHS") then
+ local invalid
+ local pages = new(strsplit(";", str))
+ local glyphs = new()
+ for page,info in ipairs(pages) do
+ local list = new(strsplit(",", info))
+ local tab = tonumber(tremove(list, 1))
+ if (tab) then
+ glyphs[tab] = table.concat(list, ",")
+ del(list)
+ else
+ invalid = true
+ del(glyphs)
+ del(list)
+ break
+ end
+ end
+ if (not invalid) then
+ self:OnReceiveGlyphs(guid, sender, glyphs)
+ end
+ del(pages)
+
+ elseif (cmd == "REQUESTTALENTS") then
+ if (Throttle(sender, "REQUESTTALENTS")) then
+ if ((r.version or 0) < 39) then
+ if (lib.sentToOld and lib.sentToOld[guid]) then
+ return
+ end
+ if (not lib.sentToOld) then
+ lib.sentToOld = new()
+ end
+ lib.sentToOld[guid] = time()
+ end
+
+ self:SendMyTalents(sender)
+ self:SendMyGlyphs(sender)
+ end
+
+ elseif (cmd == "REQUESTGLYPHS") then
+ if (Throttle(sender, "REQUESTGLYPHS")) then
+ self:SendMyGlyphs(sender)
+ end
+
+ elseif (cmd == "HELLO") then
+ r.version = tonumber(str)
+ if (channel ~= "WHISPER") then
+ if (lib.sentToOld) then
+ lib.sentToOld[guid] = nil
+ end
+ if (UnitIsConnected(sender) and Throttle(sender, "HELLO")) then
+ self:SendCommMessage("HELLO "..MINOR, sender)
+ self:SendMyGlyphs(sender)
+ end
+ end
+ end
+ end
+end
+
+-- SendMy
+local function SendMy(sender, str)
+ if (sender) then
+ if (UnitIsConnected(sender)) then
+ lib:SendCommMessage(str, sender)
+ end
+ else
+ for guid,info in pairs(lib.roster) do
+ if (info.version) then
+ local namerealm = RosterInfoFullName(info)
+ if (UnitIsConnected(namerealm)) then
+ lib:SendCommMessage(str, namerealm)
+ end
+ end
+ end
+ end
+end
+
+-- SendMyTalents
+function lib:SendMyTalents(sender)
+ if (sender or self:UserCount() > 0) then
+ local str = self:GetGUIDStorageString(UnitGUID("player"))
+ if (str) then
+ SendMy(sender, "TALENTS "..str)
+ end
+ end
+end
+
+-- SendMyGlyphs
+function lib:SendMyGlyphs(sender)
+ if (sender or self:UserCount() > 0) then
+ local r = self.roster[UnitGUID("player")]
+ if (r and r.glyphs) then
+ local str = "GLYPHS "
+ local i = 1
+ for tab,g in pairs(r.glyphs) do
+ local temp = format("%d,%s", tab, g)
+ str = str .. (i > 1 and ";" or "") .. temp
+ i = i + 1
+ end
+ SendMy(sender, str)
+ end
+ end
+end
+
+-- UserCount
+function lib:UserCount()
+ local count = 0
+ for guid,info in pairs(self.roster) do
+ if (info.version and not UnitIsUnit("player", RosterInfoFullName(info))) then
+ count = count + 1
+ end
+ end
+ return count
+end
+
+-- UnitHasTalent
+-- eg: lib:UnitHasTalent("player", GetSpellInfo(talentSpellID))
+-- Returns: nil, or number of points spent into talent
+-- If the talent group is not specified, then the active talent group is used
+function lib:UnitHasTalent(unit, talentName, group)
+ return unit and self:GUIDHasTalent(UnitGUID(unit), talentName, group)
+end
+
+-- GUIDHasTalent
+-- Returns: nil, or number of points spent into talent
+function lib:GUIDHasTalent(guid, talentName, group)
+ local talents, r = GetGUIDTalentsRaw(guid, group)
+ if (talents and r.class) then
+ local data = self.classTalentData[r.class]
+ if (data) then
+ local info = data.list and data.list[talentName]
+ if (info) then
+ local str = talents[info.treeIndex]
+ if (str) then
+ local amount = (str:byte(info.index) or 48) - 48
+ return (amount or 0) > 0 and amount or nil
+ end
+ end
+ end
+ end
+end
+
+-- GetClassTalentInfo
+function lib:GetClassTalentInfo(class, talentName)
+-- Returns: Max Rank, Icon, Tab, Tier, Column, Tree Index
+ local data = self.classTalentData[class]
+ if (data) then
+ local info = data.list and data.list[talentName]
+ if (info) then
+ return info.maxRank, info.icon, info.treeIndex, info.column, info.tier, info.index
+ end
+ end
+end
+
+-- GetActiveTalentGroup
+function lib:GetActiveTalentGroup(unit)
+ if (UnitIsUnit(unit, "player")) then
+ return GetActiveTalentGroup()
+ else
+ local guid = unit and UnitGUID(unit)
+ local r = guid and self.roster[guid]
+ return r and r.active or nil
+ end
+end
+
+-- GetNumTalentGroups
+function lib:GetNumTalentGroups(unit)
+ if (UnitIsUnit(unit, "player")) then
+ return GetNumTalentGroups()
+ else
+ local guid = unit and UnitGUID(unit)
+ local r = guid and self.roster[guid]
+ return r and r.numActive or nil
+ end
+end
+
+-- GetTalentTabInfo
+function lib:GetTalentTabInfo(unit, tab, group)
+ if (UnitIsUnit(unit, "player")) then
+ return GetTalentTabInfo(tab, nil, nil, group or GetActiveTalentGroup())
+ else
+ local guid = unit and UnitGUID(unit)
+ local r = guid and self.roster[guid]
+ if (r and r.class) then
+ local ctd = self.classTalentData[r.class]
+ if (ctd and tab >= 1 and tab <= #ctd) then
+ local spec, c1, c2, c3 = self:GetGUIDTalentSpec(guid, group)
+ return ctd[tab].name, ctd[tab].icon, tab == 1 and c1 or tab == 2 and c2 or c3, ctd[tab].background, 0
+ end
+ end
+ end
+end
+
+-- GetNumTalents
+function lib:GetNumTalents(unit, tab)
+ if (UnitIsUnit(unit, "player")) then
+ return GetNumTalents(tab)
+ else
+ local _, class = UnitClass(unit)
+ if (class) then
+ local ctd = self.classTalentData[class]
+ if (ctd and tab >= 1 and tab <= #ctd) then
+ return #ctd[tab].list
+ end
+ end
+ end
+end
+
+-- GetTalentInfo
+function lib:GetTalentInfo(unit, tab, index, group)
+ if (UnitIsUnit(unit, "player")) then
+ return GetTalentInfo(tab, index, nil, nil, group or GetActiveTalentGroup())
+ else
+ local _, class = UnitClass(unit)
+ if (class) then
+ local ctd = self.classTalentData[class]
+ if (ctd and tab >= 1 and tab <= #ctd) then
+ local info = ctd[tab].list[index]
+ if (info) then
+ local spent = self:UnitHasTalent(unit, info.name, group)
+ return info.name, info.icon, info.tier, info.column, spent or 0, info.maxRank
+ end
+ end
+ end
+ end
+end
+
+-- GetNumTalentTabs
+function lib:GetNumTalentTabs()
+ return GetNumTalentTabs()
+end
+
+-- GetNumTalentTabs
+function lib:GetUnspentTalentPoints(unit, group)
+ if (UnitIsUnit(unit, "player")) then
+ return GetUnspentTalentPoints(nil, nil, group)
+ else
+ local guid = unit and UnitGUID(unit)
+ local r = guid and self.roster[guid]
+ if (r) then
+ return r.unspent and r.unspent[group or r.active or 1]
+ end
+ end
+end
+
+-- GetTalentCount
+function lib:GetTalentCount()
+ local count, missing = 0, 0
+ for guid,info in pairs(self.roster) do
+ if (info.talents) then
+ count = count + 1
+ else
+ missing = missing + 1
+ end
+ end
+ return count, missing
+end
+
+-- GetTalentMissingNames
+function lib:GetTalentMissingNames()
+ local list = new()
+ for unit in self:IterateRoster() do
+ local guid = UnitGUID(unit)
+ local r = guid and self.roster[guid]
+ if (not r or not r.talents) then
+ tinsert(list, UnitFullName(unit))
+ end
+ end
+ local ret
+ if (next(list)) then
+ ret = table.concat(list, ",")
+ end
+ del(list)
+ return ret
+end
+
+-- PurgeAndRescanTalents
+function lib:PurgeAndRescanTalents()
+ if (self.roster) then
+ wipe(self.pendingStorageStrings)
+ for guid,info in pairs(self.roster) do
+ info.talents = del(info.talents)
+ info.active = nil
+ info.numActive = nil
+ info.requested = nil
+ end
+ end
+ self:CheckForMissingTalents()
+end
+
+-- Roster iterator
+do
+ local function iter(t)
+ local key = t.id
+ local ret
+ if (t.mode == "raid") then
+ if (key > t.r) then
+ del(t)
+ return nil
+ end
+ ret = "raid"..key
+ else
+ if (key > t.p) then
+ del(t)
+ return nil
+ end
+ ret = key == 0 and "player" or "party"..key
+ end
+ t.id = key + 1
+ return ret
+ end
+
+ -- IterateRoster
+ function lib:IterateRoster()
+ local t = new()
+ if (GetNumRaidMembers() > 0) then
+ t.mode = "raid"
+ t.id = 1
+ t.r = GetNumRaidMembers()
+ else
+ t.mode = "party"
+ t.id = 0
+ t.p = GetNumPartyMembers()
+ end
+ return iter, t
+ end
+end
\ No newline at end of file
diff --git a/libs/LibCompat-1.0/Libs/LibGroupTalents-1.0/LibTalentQuery-1.0.lua b/libs/LibCompat-1.0/Libs/LibGroupTalents-1.0/LibTalentQuery-1.0.lua
new file mode 100644
index 0000000..c82d4f3
--- /dev/null
+++ b/libs/LibCompat-1.0/Libs/LibGroupTalents-1.0/LibTalentQuery-1.0.lua
@@ -0,0 +1,358 @@
+--[[
+Name: LibTalentQuery-1.0
+Revision: $Rev: 84 $
+Author: Rich Martel (richmartel@gmail.com)
+Documentation: http://wowace.com/wiki/LibTalentQuery-1.0
+SVN: svn://svn.wowace.com/wow/libtalentquery-1-0/mainline/trunk
+Description: Library to help with querying unit talents
+Dependancies: LibStub, CallbackHandler-1.0
+License: LGPL v2.1
+
+Example Usage:
+ local TalentQuery = LibStub:GetLibrary("LibTalentQuery-1.0")
+ TalentQuery.RegisterCallback(self, "TalentQuery_Ready")
+
+ local raidTalents = {}
+ ...
+ TalentQuery:Query(unit)
+ ...
+ function MyAddon:TalentQuery_Ready(e, name, realm, unitid)
+ local isnotplayer = not UnitIsUnit(unitid, "player")
+ local spec = {}
+ for tab = 1, GetNumTalentTabs(isnotplayer) do
+ local treename, _, pointsspent = GetTalentTabInfo(tab, isnotplayer)
+ tinsert(spec, pointsspent)
+ end
+ raidTalents[UnitGUID(unitid)] = spec
+ end
+]]
+
+local MAJOR, MINOR = "LibTalentQuery-1.0", 90000 + tonumber(("$Rev: 84 $"):match("(%d+)"))
+
+local lib = LibStub:NewLibrary(MAJOR, MINOR)
+if not lib then return end
+
+local INSPECTDELAY = 1
+local INSPECTTIMEOUT = 5
+if not lib.events then
+ lib.events = LibStub("CallbackHandler-1.0"):New(lib)
+end
+
+local validateTrees
+local enteredWorld = IsLoggedIn()
+local frame = lib.frame
+if not frame then
+ frame = CreateFrame("Frame", MAJOR .. "_Frame")
+ lib.frame = frame
+end
+frame:UnregisterAllEvents()
+frame:RegisterEvent("INSPECT_TALENT_READY")
+frame:RegisterEvent("PLAYER_ENTERING_WORLD")
+frame:RegisterEvent("PLAYER_LEAVING_WORLD")
+frame:RegisterEvent("PLAYER_LOGIN")
+frame:SetScript("OnEvent", function(this, event, ...)
+ return lib[event](lib, ...)
+end)
+
+do
+ local lastUpdateTime = 0
+ frame:SetScript("OnUpdate", function(this, elapsed)
+ lastUpdateTime = lastUpdateTime + elapsed
+ if lastUpdateTime > INSPECTDELAY then
+ lib:CheckInspectQueue()
+ lastUpdateTime = 0
+ end
+ end)
+ frame:Hide()
+end
+
+local inspectQueue = lib.inspectQueue or {}
+lib.inspectQueue = inspectQueue
+local garbageQueue = lib.garbageQueue or {} -- Added a second queue to things. Inspects that initially fail are now
+lib.garbageQueue = garbageQueue -- thrown into second queue will will be processed once main queue is empty
+
+if next(inspectQueue) then
+ frame:Show()
+end
+
+local UnitIsPlayer = _G.UnitIsPlayer
+local UnitName = _G.UnitName
+local UnitExists = _G.UnitExists
+local UnitGUID = _G.UnitGUID
+local GetNumRaidMembers = _G.GetNumRaidMembers
+local GetNumPartyMembers = _G.GetNumPartyMembers
+local UnitIsVisible = _G.UnitIsVisible
+local UnitIsConnected = _G.UnitIsConnected
+local UnitCanAttack = _G.UnitCanAttack
+local CanInspect = _G.CanInspect
+
+local function UnitFullName(unit)
+ local name, realm = UnitName(unit)
+ local namerealm = realm and realm ~= "" and name .. "-" .. realm or name
+ return namerealm
+end
+
+-- GuidToUnitID
+local function GuidToUnitID(guid)
+ local prefix, min, max = "raid", 1, GetNumRaidMembers()
+ if max == 0 then
+ prefix, min, max = "party", 0, GetNumPartyMembers()
+ end
+
+ -- Prioritise getting direct units first because other players targets
+ -- can change between notify and event which can bugger things up
+ for i = min, max do
+ local unit = i == 0 and "player" or prefix .. i
+ if (UnitGUID(unit) == guid) then
+ return unit
+ end
+ end
+
+ -- This properly detects target units
+ if (UnitGUID("target") == guid) then
+ return "target"
+ elseif (UnitGUID("focus") == guid) then
+ return "focus"
+ elseif (UnitGUID("mouseover") == guid) then
+ return "mouseover"
+ end
+
+ for i = min, max + 3 do
+ local unit
+ if i == 0 then
+ unit = "player"
+ elseif i == max + 1 then
+ unit = "target"
+ elseif i == max + 2 then
+ unit = "focus"
+ elseif i == max + 3 then
+ unit = "mouseover"
+ else
+ unit = prefix .. i
+ end
+ if (UnitGUID(unit .. "target") == guid) then
+ return unit .. "target"
+ elseif (i <= max and UnitGUID(unit.."pettarget") == guid) then
+ return unit .. "pettarget"
+ end
+ end
+ return nil
+end
+
+-- Query
+function lib:Query(unit)
+ if (UnitLevel(unit) < 10 or UnitName(unit) == UNKNOWN) then
+ return
+ end
+
+ self.lastQueuedInspectReceived = nil
+ if UnitIsUnit(unit, "player") then
+ self.events:Fire("TalentQuery_Ready", UnitName("player"), nil, "player")
+ else
+ if type(unit) ~= "string" then
+ error(("Bad argument #2 to 'Query'. Expected %q, received %q (%s)"):format("string", type(unit), tostring(unit)), 2)
+ elseif not UnitExists(unit) or not UnitIsPlayer(unit) then
+ error(("Bad argument #2 to 'Query'. %q is not a valid player unit"):format(tostring(unit)), 2)
+ elseif not UnitExists(unit) or not UnitIsPlayer(unit) then
+ error(("Bad argument #2 to 'Query'. %q does not require a server query before reading talents"):format("player"), 2)
+ else
+ local name = UnitFullName(unit)
+ if (not inspectQueue[name]) then
+ inspectQueue[name] = UnitGUID(unit)
+ garbageQueue[name] = nil
+ end
+ frame:Show()
+ end
+ end
+end
+
+-- CheckInspectQueue
+-- Originally, it would wait until no pending NotifyInspect() were expected, and then do it's own.
+-- It was also only bother looking at ready results if it had triggered the Notify for that occasion.
+-- For the changes I've done, no assumption is made about which mod is performing NotifyInspect().
+-- We note the name, unit, time of any inspects done whether from this queue or any other source,
+-- we remove from our queue any we were expecting, and use a seperate event in case extra talent
+-- info is any time wanted (opportunistic refreshes etc) - Zeksie, 20th May 2009
+function lib:CheckInspectQueue()
+ if (_G.InspectFrame and _G.InspectFrame:IsShown()) then
+ return
+ end
+
+ if (not self.lastInspectTime or self.lastInspectTime < GetTime() - INSPECTTIMEOUT) then
+ self.lastInspectPending = 0
+ end
+
+ if (self.lastInspectPending > 0 or not enteredWorld) then
+ return
+ end
+
+ if (self.lastQueuedInspectReceived and self.lastQueuedInspectReceived < GetTime() - 60) then
+ -- No queued results received for a minute, so purge the queue as invalid and move on with our lives
+ self.lastQueuedInspectReceived = nil
+ inspectQueue = {}
+ lib.inspectQueue = inspectQueue
+ garbageQueue = {}
+ lib.garbageQueue = garbageQueue
+ frame:Hide()
+ return
+ end
+
+ for name,guid in pairs(inspectQueue) do
+ local unit = GuidToUnitID(guid)
+ if (not unit) then
+ inspectQueue[name] = nil
+ else
+ if (UnitIsVisible(unit) and UnitIsConnected(unit) and not UnitCanAttack("player", unit) and not UnitCanAttack(unit, "player") and CanInspect(unit) and UnitClass(unit)) then
+ NotifyInspect(unit)
+ break
+ else
+ garbageQueue[name] = guid -- Not available, throw into secondary queue and continue
+ inspectQueue[name] = nil
+ end
+ end
+ end
+
+ if (not next(inspectQueue)) then
+ if (next(garbageQueue)) then
+ -- Retry initially failed inspects
+ lib.inspectQueue = garbageQueue
+ inspectQueue = lib.inspectQueue
+ lib.garbageQueue = {}
+ garbageQueue = lib.garbageQueue
+ else
+ frame:Hide()
+ end
+ end
+end
+
+-- NotifyInspect
+if not lib.NotifyInspect then -- don't hook twice
+ hooksecurefunc("NotifyInspect", function(...) return lib:NotifyInspect(...) end)
+end
+function lib:NotifyInspect(unit)
+ if (not (UnitExists(unit) and UnitIsVisible(unit) and UnitIsConnected(unit) and CheckInteractDistance(unit, 4))) then
+ return
+ end
+ self.lastInspectUnit = unit
+ self.lastInspectGUID = UnitGUID(unit)
+ self.lastInspectTime = GetTime()
+ self.lastInspectName = UnitFullName(unit)
+ self.lastInspectPending = self.lastInspectPending + 1
+ local isnotplayer = not UnitIsUnit("player", unit)
+ self.lastInspectTree = GetTalentTabInfo(1, isnotplayer) -- Talent tree names are available immediately
+end
+
+-- Reset
+function lib:Reset()
+ self.lastInspectPending = 0
+ self.lastInspectUnit = nil
+ self.lastInspectTime = nil
+ self.lastInspectName = nil
+ self.lastInspectGUID = nil
+ self.lastInspectTree = nil
+end
+
+-- INSPECT_TALENT_READY
+function lib:INSPECT_TALENT_READY()
+ self.lastInspectPending = self.lastInspectPending - 1
+
+ -- Results are valid only when we have received as many events as we have posted notifies
+ if (self.lastInspectName and self.lastInspectPending == 0) then
+ -- Check unit ID is still pointing to same actual unit
+ if (UnitGUID(self.lastInspectUnit) == self.lastInspectGUID) then
+ local guid = inspectQueue[self.lastInspectName]
+ inspectQueue[self.lastInspectName] = nil
+
+ local name, realm = strsplit("-", self.lastInspectName)
+
+ self.lastQueuedInspectReceived = GetTime()
+
+ -- Notify of expected talent results
+ local isnotplayer = not UnitIsUnit("player", self.lastInspectName)
+ local group = GetActiveTalentGroup(isnotplayer)
+ local tree1, _, spent1 = GetTalentTabInfo(1, isnotplayer, nil, group)
+ if (tree1 ~= self.lastInspectTree) then
+ -- Expected talent tree name to be the same as it was when we triggered the NotifyInspect()
+ garbageQueue[self.lastInspectName] = self.lastInspectGUID
+ self:Reset()
+ self:CheckInspectQueue()
+ return
+
+ elseif (validateTrees) then
+ -- Double checking here. Check the tree name matches what we expect for this class
+ local _, class = UnitClass(self.lastInspectUnit)
+ if (tree1 ~= validateTrees[class]) then
+ garbageQueue[self.lastInspectName] = self.lastInspectGUID
+ self:Reset()
+ self:CheckInspectQueue()
+ return
+ end
+ end
+
+ local tree2, _, spent2 = GetTalentTabInfo(2, isnotplayer, nil, group)
+ local tree3, _, spent3 = GetTalentTabInfo(3, isnotplayer, nil, group)
+ if ((spent1 or 0) + (spent2 or 0) + (spent3 or 0) > 0) then
+ if (guid) then
+ -- It was in our queue
+ self.events:Fire("TalentQuery_Ready", name, realm, self.lastInspectUnit)
+ else
+ -- Also notify of non-expected ones, as it's entirely useful to refresh them if they're there
+ -- It is up to the receiving applicating to determine whether they want to receive the information
+ self.events:Fire("TalentQuery_Ready_Outsider", name, realm, self.lastInspectUnit)
+ end
+ else
+ -- Tree came back with zero points spent, probably an issue while logging in
+ garbageQueue[self.lastInspectName] = guid
+ end
+ end
+
+ self:Reset()
+ self:CheckInspectQueue()
+ end
+end
+
+function lib:PLAYER_ENTERING_WORLD()
+ -- We can't inspect other's talents until now
+ -- We just get 0/0/0 back even though we get an INSPECT_TALENT_READY event
+ enteredWorld = true
+end
+
+function lib:PLAYER_LEAVING_WORLD()
+ enteredWorld = nil
+end
+
+function lib:PLAYER_LOGIN()
+ validateTrees = {
+ DRUID = "Balance",
+ PRIEST = "Discipline",
+ ROGUE = "Assassination",
+ HUNTER = "Beast Mastery",
+ WARLOCK = "Affliction",
+ WARRIOR = "Arms",
+ DEATHKNIGHT = "Blood",
+ PALADIN = "Holy",
+ SHAMAN = "Elemental",
+ MAGE = "Arcane",
+ }
+
+ if (GetLocale() ~= "enUS" and GetLocale() ~= "enGB") then
+ -- LibBabble-TalentTree-3.0 only loaded if present and not enUS
+ local LBT = LibStub("LibBabble-TalentTree-3.0", true)
+ if (not LBT) then
+ LoadAddOn("LibBabble-TalentTree-3.0")
+ LBT = LibStub("LibBabble-TalentTree-3.0", true)
+ end
+ LBT = LBT and LBT:GetLookupTable()
+ if (LBT) then
+ for class,tree1 in pairs(validateTrees) do
+ validateTrees[class] = LBT[tree1]
+ end
+ else
+ validateTrees = nil
+ end
+ end
+
+ self.PLAYER_LOGIN = nil
+end
+
+lib:Reset()
diff --git a/libs/LibCompat-1.0/Libs/LibGroupTalents-1.0/lib.xml b/libs/LibCompat-1.0/Libs/LibGroupTalents-1.0/lib.xml
new file mode 100644
index 0000000..b1925e1
--- /dev/null
+++ b/libs/LibCompat-1.0/Libs/LibGroupTalents-1.0/lib.xml
@@ -0,0 +1,5 @@
+
+
+
+
\ No newline at end of file
diff --git a/libs/LibCompat-1.0/Libs/LibStub/LibStub.lua b/libs/LibCompat-1.0/Libs/LibStub/LibStub.lua
new file mode 100644
index 0000000..0357260
--- /dev/null
+++ b/libs/LibCompat-1.0/Libs/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
\ No newline at end of file
diff --git a/libs/LibCompat-1.0/Templates.xml b/libs/LibCompat-1.0/Templates.xml
new file mode 100644
index 0000000..18e9799
--- /dev/null
+++ b/libs/LibCompat-1.0/Templates.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/libs/LibCompat-1.0/lib.xml b/libs/LibCompat-1.0/lib.xml
new file mode 100644
index 0000000..6b62f17
--- /dev/null
+++ b/libs/LibCompat-1.0/lib.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
\ 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..7a05649
--- /dev/null
+++ b/libs/LibDBIcon-1.0/LibDBIcon-1.0.lua
@@ -0,0 +1,263 @@
+--[[
+Name: DBIcon-1.0
+Revision: $Rev: 13 $
+Author(s): Rabbit (rabbit.magtheridon@gmail.com)
+Description: Allows addons to register to recieve a lightweight minimap icon as an alternative to more heavy LDB displays.
+Dependencies: LibStub
+License: GPL v2 or later.
+]]
+
+--[[
+Copyright (C) 2008-2010 Rabbit
+
+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 2
+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, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+]]
+
+-----------------------------------------------------------------------
+-- DBIcon-1.0
+--
+-- Disclaimer: Most of this code was ripped from Barrel but fixed, streamlined
+-- and cleaned up a lot so that it no longer sucks.
+--
+
+local DBICON10 = "LibDBIcon-1.0"
+local DBICON10_MINOR = tonumber(("$Rev: 13 $"):match("(%d+)"))
+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.disabled = lib.disabled or nil
+lib.objects = lib.objects or {}
+lib.callbackRegistered = lib.callbackRegistered or nil
+lib.notCreated = lib.notCreated or {}
+
+function lib:IconCallback(event, name, key, value, dataobj)
+ if lib.objects[name] then
+ lib.objects[name].icon:SetTexture(dataobj.icon)
+ end
+end
+if not lib.callbackRegistered then
+ ldb.RegisterCallback(lib, "LibDataBroker_AttributeChanged__icon", "IconCallback")
+ lib.callbackRegistered = true
+end
+
+-- Tooltip code ripped from StatBlockCore by Funkydude
+local function getAnchors(frame)
+ local x,y = frame:GetCenter()
+ if not x or not y then return "TOPLEFT", "BOTTOMLEFT" 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 self.isMoving then return end
+ local obj = self.dataObject
+ if obj.OnTooltipShow then
+ GameTooltip:SetOwner(self, "ANCHOR_NONE")
+ GameTooltip:SetPoint(getAnchors(self))
+ obj.OnTooltipShow(GameTooltip)
+ GameTooltip:Show()
+ elseif obj.OnEnter then
+ obj.OnEnter(self)
+ end
+end
+
+local function onLeave(self)
+ local obj = self.dataObject
+ GameTooltip:Hide()
+ if obj.OnLeave then obj.OnLeave(self) end
+end
+
+--------------------------------------------------------------------------------
+
+local minimapShapes = {
+ ["ROUND"] = {true, true, true, true},
+ ["SQUARE"] = {false, false, false, false},
+ ["CORNER-TOPLEFT"] = {true, false, false, false},
+ ["CORNER-TOPRIGHT"] = {false, false, true, false},
+ ["CORNER-BOTTOMLEFT"] = {false, true, false, false},
+ ["CORNER-BOTTOMRIGHT"] = {false, false, false, true},
+ ["SIDE-LEFT"] = {true, true, false, false},
+ ["SIDE-RIGHT"] = {false, false, true, true},
+ ["SIDE-TOP"] = {true, false, true, false},
+ ["SIDE-BOTTOM"] = {false, true, false, true},
+ ["TRICORNER-TOPLEFT"] = {true, true, true, false},
+ ["TRICORNER-TOPRIGHT"] = {true, false, true, true},
+ ["TRICORNER-BOTTOMLEFT"] = {true, true, false, true},
+ ["TRICORNER-BOTTOMRIGHT"] = {false, true, true, true},
+}
+
+local function updatePosition(button)
+ local angle = math.rad(button.db.minimapPos or 225)
+ local x, y, q = math.cos(angle), math.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]
+ if quadTable[q] then
+ x, y = x*80, y*80
+ else
+ local diagRadius = 103.13708498985 --math.sqrt(2*(80)^2)-10
+ x = math.max(-80, math.min(x*diagRadius, 80))
+ y = math.max(-80, math.min(y*diagRadius, 80))
+ end
+ button:SetPoint("CENTER", Minimap, "CENTER", x, y)
+end
+
+local function onClick(self, b) if self.dataObject.OnClick then self.dataObject.OnClick(self, b) end end
+local function onMouseDown(self) self.icon:SetTexCoord(0, 1, 0, 1) end
+local function onMouseUp(self) self.icon:SetTexCoord(0.05, 0.95, 0.05, 0.95) end
+
+local function onUpdate(self)
+ local mx, my = Minimap:GetCenter()
+ local px, py = GetCursorPosition()
+ local scale = Minimap:GetEffectiveScale()
+ px, py = px / scale, py / scale
+ self.db.minimapPos = math.deg(math.atan2(py - my, px - mx)) % 360
+ updatePosition(self)
+end
+
+local function onDragStart(self)
+ self:LockHighlight()
+ self.icon:SetTexCoord(0, 1, 0, 1)
+ self:SetScript("OnUpdate", onUpdate)
+ self.isMoving = true
+ GameTooltip:Hide()
+end
+
+local function onDragStop(self)
+ self:SetScript("OnUpdate", nil)
+ self.icon:SetTexCoord(0.05, 0.95, 0.05, 0.95)
+ self:UnlockHighlight()
+ self.isMoving = nil
+end
+
+local function createButton(name, object, db)
+ local button = CreateFrame("Button", "LibDBIcon10_"..name, Minimap)
+ button.dataObject = object
+ button.db = db
+ button:SetFrameStrata("MEDIUM")
+ button:SetWidth(31); button:SetHeight(31)
+ button:SetFrameLevel(8)
+ button:RegisterForClicks("anyUp")
+ button:RegisterForDrag("LeftButton")
+ button:SetHighlightTexture("Interface\\Minimap\\UI-Minimap-ZoomButton-Highlight")
+ local overlay = button:CreateTexture(nil, "OVERLAY")
+ overlay:SetWidth(53); overlay:SetHeight(53)
+ overlay:SetTexture("Interface\\Minimap\\MiniMap-TrackingBorder")
+ overlay:SetPoint("TOPLEFT")
+ local icon = button:CreateTexture(nil, "BACKGROUND")
+ icon:SetWidth(20); icon:SetHeight(20)
+ icon:SetTexture(object.icon)
+ icon:SetTexCoord(0.05, 0.95, 0.05, 0.95)
+ icon:SetPoint("TOPLEFT", 7, -5)
+ button.icon = icon
+
+ button:SetScript("OnEnter", onEnter)
+ button:SetScript("OnLeave", onLeave)
+ button:SetScript("OnClick", onClick)
+ button:SetScript("OnDragStart", onDragStart)
+ button:SetScript("OnDragStop", onDragStop)
+ button:SetScript("OnMouseDown", onMouseDown)
+ button:SetScript("OnMouseUp", onMouseUp)
+
+ lib.objects[name] = button
+
+ if lib.loggedIn then
+ updatePosition(button)
+ if not db.hide then button:Show()
+ else button:Hide() end
+ end
+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
+
+lib.loggedIn = lib.loggedIn or false
+-- 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()
+ for _, object in pairs(lib.objects) do
+ updatePosition(object)
+ if not lib.disabled and not object.db.hide then object:Show()
+ else object:Hide() end
+ end
+ lib.loggedIn = true
+ f:SetScript("OnEvent", nil)
+ f = nil
+ end)
+ f:RegisterEvent("PLAYER_LOGIN")
+end
+
+function lib:Register(name, object, db)
+ if lib.disabled then return end
+ 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("Already registered, nubcake.") end
+ if not db or not db.hide then
+ createButton(name, object, db)
+ else
+ lib.notCreated[name] = {object, db}
+ end
+end
+
+function lib:Hide(name)
+ if not lib.objects[name] then return end
+ lib.objects[name]:Hide()
+end
+function lib:Show(name)
+ if lib.disabled then return end
+ check(name)
+ lib.objects[name]:Show()
+ updatePosition(lib.objects[name])
+end
+function lib:IsRegistered(name)
+ return (lib.objects[name] or lib.notCreated[name]) and true or false
+end
+function lib:Refresh(name, db)
+ if lib.disabled then return end
+ check(name)
+ local button = lib.objects[name]
+ if db then button.db = db end
+ updatePosition(button)
+end
+
+function lib:EnableLibrary()
+ lib.disabled = nil
+ for name, object in pairs(lib.objects) do
+ if not object.db or (object.db and not object.db.hide) then
+ object:Show()
+ updatePosition(object)
+ end
+ end
+end
+
+function lib:DisableLibrary()
+ lib.disabled = true
+ for name, object in pairs(lib.objects) do
+ object:Hide()
+ end
+end
+
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/LibStub/LibStub.lua b/libs/LibStub/LibStub.lua
new file mode 100644
index 0000000..ae1900e
--- /dev/null
+++ b/libs/LibStub/LibStub.lua
@@ -0,0 +1,51 @@
+-- $Id: LibStub.lua 76 2007-09-03 01:50:17Z mikk $
+-- 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]
+
+-- 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/LibStub/LibStub.toc b/libs/LibStub/LibStub.toc
new file mode 100644
index 0000000..d169255
--- /dev/null
+++ b/libs/LibStub/LibStub.toc
@@ -0,0 +1,13 @@
+## Interface: 40200
+## 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
+## X-Curse-Packaged-Version: 1.0.2-40200
+## X-Curse-Project-Name: LibStub
+## X-Curse-Project-ID: libstub
+## X-Curse-Repository-ID: wow/libstub/mainline
+
+LibStub.lua
diff --git a/libs/LibStub/tests/test.lua b/libs/LibStub/tests/test.lua
new file mode 100644
index 0000000..276ddab
--- /dev/null
+++ b/libs/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/LibStub/tests/test2.lua b/libs/LibStub/tests/test2.lua
new file mode 100644
index 0000000..eae7172
--- /dev/null
+++ b/libs/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/LibStub/tests/test3.lua b/libs/LibStub/tests/test3.lua
new file mode 100644
index 0000000..30f7b94
--- /dev/null
+++ b/libs/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/LibStub/tests/test4.lua b/libs/LibStub/tests/test4.lua
new file mode 100644
index 0000000..43eb338
--- /dev/null
+++ b/libs/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