--[[
Librarian is a library used to manage localization, packages, scripts, animations and data embedded
into the SVUI core addon.

It's main purpose is to keep all methods and logic needed to properly keep
core add-ins functioning outside of the core object.

It is also modifyiing LibStub to give me dominating control over which libraries are
allowed to be created and loaded regardless of versioning or timing.  

The reasoning for this is due to the potential for other addon to get loaded earlier
and embed newer versions of lib dependencies which can be devastating.                                                                   
--]]
local _G            = getfenv(0)
local select        = _G.select;
local assert        = _G.assert;
local type          = _G.type;
local error         = _G.error;
local pairs         = _G.pairs;
local next          = _G.next;
local ipairs        = _G.ipairs;
local loadstring    = _G.loadstring;
local setmetatable  = _G.setmetatable;
local rawset        = _G.rawset;
local rawget        = _G.rawget;
local tostring      = _G.tostring;
local tonumber      = _G.tonumber;
local tostring      = _G.tostring;
local xpcall        = _G.xpcall;
local table         = _G.table;
local tconcat       = table.concat;
local tremove       = table.remove;
local tinsert       = table.insert;
local strmatch      = _G.strmatch;
local table_sort    = table.sort;
local bit           = _G.bit;
local band          = bit.band;
local math          = _G.math;
local min,max,abs   = math.min,math.max,math.abs;

local UIParent = _G.UIParent;
local GetScreenWidth = _G.GetScreenWidth;
local GetScreenHeight = _G.GetScreenHeight;
local IsAltKeyDown = _G.IsAltKeyDown;

local MAX_MINOR     = 999999;
local Librarian     = _G.Librarian;

if not Librarian then

    Librarian = { libs = {} };

    _G.Librarian = Librarian;
    
    function Librarian:NewLibrary(libName)
        assert(type(libName) == "string", "Missing Library Name")
        self.libs[libName] = self.libs[libName] or {}
        return self.libs[libName]
    end
    
    function Librarian:Fetch(libName, silent)
        if not self.libs[libName] and not silent then
            error(("Cannot find a library instance of %q."):format(tostring(libName)), 2)
        end
        return self.libs[libName]
    end

    setmetatable(Librarian, { __call = Librarian.Fetch })

    local LibStub = _G.LibStub;
    local dead = function() return end

    if not LibStub then
        LibStub         = {libs = {}, minors = {}};
        _G.LibStub      = LibStub;
        
        function LibStub:GetLibrary(major, silent)
            if not self.libs[major] and not silent then
                error(("Cannot find a library instance of %q."):format(tostring(major)), 2)
            end
            return self.libs[major], self.minors[major]
        end
        
        function LibStub:IterateLibraries() return pairs(self.libs) end
        setmetatable(LibStub, { __call = LibStub.GetLibrary })
    end

    local LibStubNew = function(self, major, minor, replace)
        if(type(major) ~= "string") then return end
        local oldminor = self.minors[major] or 0
        if(not replace) then
            minor = tonumber(strmatch(minor, "%d+"))
            if(not minor) then minor = 1 end
            if(oldminor >= minor) then return nil end
            self.minors[major], self.libs[major] = minor, self.libs[major] or {}
        else
            minor = MAX_MINOR
            self.minors[major], self.libs[major] = minor, {}
        end
        return self.libs[major], oldminor
    end

    local LibStubKill = function(self, major, silent)
        if not self.libs[major] and not silent then
            error(("Cannot find a library instance of %q."):format(tostring(major)), 2)
        end
        for key,obj in pairs(self.libs) do
            if(key:find(tostring(major))) then
                self.libs[key] = nil
            end
        end
    end

    local LibStubLock = function(self, major, silent)
        if(self.locked[major]) then return end
        for key,obj in pairs(self.libs) do
            if(key:find(tostring(major)) and (not self.recovery[key])) then
                self.locked[major] = true
                self.recovery[key] = {}
                for k,v in pairs(obj) do
                    if(type(v) == 'function') then
                        self.recovery[key][k] = v
                        v = dead
                    end
                end
            end
        end
    end

    local LibStubUnlock = function(self, major, silent)
        if(not self.locked[major]) then return end
        for key,obj in pairs(self.libs) do
            if(key:find(tostring(major)) and (self.recovery[key])) then
                for k,v in pairs(self.recovery[key]) do
                    obj[k] = v
                end
                self.locked[major] = nil
                self.recovery[key] = nil
            end
        end
    end

    LibStub.minor       = MAX_MINOR;
    LibStub.recovery    = {};
    LibStub.locked      = {};
    LibStub.NewLibrary  = LibStubNew;
    LibStub.Kill        = LibStubKill;
    LibStub.Lock        = LibStubLock;
    LibStub.Unlock      = LibStubUnlock;
end

local geterrorhandler   = _G.geterrorhandler;
local CB_MAJOR          = "CallbackHandler-1.0";
local CB                = LibStub:NewLibrary(CB_MAJOR, MAX_MINOR, true)
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 _META={__index=function(a,b)a[b]={}return a[b]end}
local function _err(a) return geterrorhandler()(a) end
local function _set(a)local b=_CODE;local c,d={},{}for e=1,a do c[e],d[e]="arg"..e,"old_arg"..e end;b=b:gsub("OLD_ARGS",tconcat(d,", ")):gsub("ARGS",tconcat(c,", "))return assert(loadstring(b,"safecall Dispatcher["..a.."]"))(next,xpcall,_err)end
local Dispatchers=setmetatable({},{__index=function(a,b)local c=_set(b)rawset(a,b,c)return c end})
function CB:New(a,b,c,d)b=b or"RegisterCallback"c=c or"UnregisterCallback"if d==nil then d="UnregisterAllCallbacks"end;local e=setmetatable({},_META)local registry={recurse=0,events=e}function registry:Fire(f,...)if not rawget(e,f)or not next(e[f])then return end;local g=registry.recurse;registry.recurse=g+1;Dispatchers[select('#',...)+1](e[f],f,...)registry.recurse=g;if registry.insertQueue and g==0 then for f,h in pairs(registry.insertQueue)do local i=not rawget(e,f)or not next(e[f])for j,k in pairs(h)do e[f][j]=k;if i and registry.OnUsed then registry.OnUsed(registry,a,f)i=nil end end end;registry.insertQueue=nil end end;a[b]=function(j,f,l,...)if type(f)~="string"then return end;l=l or f;local i=not rawget(e,f)or not next(e[f])if type(l)~="string"and type(l)~="function"then return end;local m;if type(l)=="string"then if type(j)~="table"or j==a or type(j[l])~="function"then return end;if select("#",...)>=1 then local n=select(1,...)m=function(...)j[l](j,n,...)end else m=function(...)j[l](j,...)end end else if type(j)~="table"and type(j)~="string"and type(j)~="thread"then return end;if select("#",...)>=1 then local n=select(1,...)m=function(...)l(n,...)end else m=l end end;if e[f][j]or registry.recurse<1 then e[f][j]=m;if registry.OnUsed and i then registry.OnUsed(registry,a,f)end else registry.insertQueue=registry.insertQueue or setmetatable({},_META)registry.insertQueue[f][j]=m end end;a[c]=function(j,f)if not j or j==a or type(f)~="string"then return end;if rawget(e,f)and e[f][j]then e[f][j]=nil;if registry.OnUnused and not next(e[f])then registry.OnUnused(registry,a,f)end end;if registry.insertQueue and rawget(registry.insertQueue,f)and registry.insertQueue[f][j]then registry.insertQueue[f][j]=nil end end;if d then a[d]=function(...)if select("#",...)<1 or select("#",...)==1 and...==a then return end;for o=1,select("#",...)do local j=select(o,...)if registry.insertQueue then for f,h in pairs(registry.insertQueue)do if h[j]then h[j]=nil end end end;for f,h in pairs(e)do if h[j]then h[j]=nil;if registry.OnUnused and not next(h)then registry.OnUnused(registry,a,f)end end end end end end;return registry end
local LSM_MAJOR = "LibSharedMedia-3.0";
local LSM = LibStub:NewLibrary(LSM_MAJOR, MAX_MINOR, true)
local locale=GetLocale()local b;local c=0;
LSM.LOCALE_BIT_koKR=1;LSM.LOCALE_BIT_ruRU=2;LSM.LOCALE_BIT_zhCN=4;LSM.LOCALE_BIT_zhTW=8;LSM.LOCALE_BIT_western=128;LSM.callbacks=LSM.callbacks or CB:New(LSM)LSM.DefaultMedia=LSM.DefaultMedia or{}LSM.MediaList=LSM.MediaList or{}LSM.MediaTable=LSM.MediaTable or{}LSM.MediaType=LSM.MediaType or{}LSM.OverrideMedia=LSM.OverrideMedia or{}LSM.MediaType.BACKGROUND="background"LSM.MediaType.BORDER="border"LSM.MediaType.FONT="font"LSM.MediaType.STATUSBAR="statusbar"LSM.MediaType.SOUND="sound"if not LSM.MediaTable.background then LSM.MediaTable.background={}end;LSM.MediaTable.background["Blizzard Dialog Background"]=[[Interface\DialogFrame\UI-DialogBox-Background]]LSM.MediaTable.background["Blizzard Dialog Background Dark"]=[[Interface\DialogFrame\UI-DialogBox-Background-Dark]]LSM.MediaTable.background["Blizzard Dialog Background Gold"]=[[Interface\DialogFrame\UI-DialogBox-Gold-Background]]LSM.MediaTable.background["Blizzard Low Health"]=[[Interface\FullScreenTextures\LowHealth]]LSM.MediaTable.background["Blizzard Marble"]=[[Interface\FrameGeneral\UI-Background-Rock]]LSM.MediaTable.background["Blizzard Out of Control"]=[[Interface\FullScreenTextures\OutOfControl]]LSM.MediaTable.background["Blizzard Parchment"]=[[Interface\AchievementFrame\UI-Achievement-Parchment-Horizontal]]LSM.MediaTable.background["Blizzard Parchment 2"]=[[Interface\AchievementFrame\UI-GuildAchievement-Parchment-Horizontal]]LSM.MediaTable.background["Blizzard Rock"]=[[Interface\FrameGeneral\UI-Background-Rock]]LSM.MediaTable.background["Blizzard Tabard Background"]=[[Interface\TabardFrame\TabardFrameBackground]]LSM.MediaTable.background["Blizzard Tooltip"]=[[Interface\Tooltips\UI-Tooltip-Background]]LSM.MediaTable.background["Solid"]=[[Interface\Buttons\WHITE8X8]]if not LSM.MediaTable.border then LSM.MediaTable.border={}end;LSM.MediaTable.border["None"]=[[Interface\None]]LSM.MediaTable.border["Blizzard Achievement Wood"]=[[Interface\AchievementFrame\UI-Achievement-WoodBorder]]LSM.MediaTable.border["Blizzard Chat Bubble"]=[[Interface\Tooltips\ChatBubble-Backdrop]]LSM.MediaTable.border["Blizzard Dialog"]=[[Interface\DialogFrame\UI-DialogBox-Border]]LSM.MediaTable.border["Blizzard Dialog Gold"]=[[Interface\DialogFrame\UI-DialogBox-Gold-Border]]LSM.MediaTable.border["Blizzard Party"]=[[Interface\CHARACTERFRAME\UI-Party-Border]]LSM.MediaTable.border["Blizzard Tooltip"]=[[Interface\Tooltips\UI-Tooltip-Border]]if not LSM.MediaTable.font then LSM.MediaTable.font={}end;local d=LSM.MediaTable.font;if locale=="koKR"then c=LSM.LOCALE_BIT_koKR;d["굵은 글꼴"]=[[Fonts\2002B.TTF]]d["기본 글꼴"]=[[Fonts\2002.TTF]]d["데미지 글꼴"]=[[Fonts\K_Damage.TTF]]d["퀘스트 글꼴"]=[[Fonts\K_Pagetext.TTF]]LSM.DefaultMedia["font"]="기본 글꼴"elseif locale=="zhCN"then c=LSM.LOCALE_BIT_zhCN;d["伤害数字"]=[[Fonts\ARKai_DB.ttf]]d["默认"]=[[Fonts\ARKai_T.ttf]]d["聊天"]=[[Fonts\ARHei.ttf]]LSM.DefaultMedia["font"]="默认"elseif locale=="zhTW"then c=LSM.LOCALE_BIT_zhTW;d["提示訊息"]=[[Fonts\bHEI00M.ttf]]d["聊天"]=[[Fonts\bHEI01B.ttf]]d["傷害數字"]=[[Fonts\bKAI00M.ttf]]d["預設"]=[[Fonts\bLEI00D.ttf]]LSM.DefaultMedia["font"]="預設"elseif locale=="ruRU"then c=LSM.LOCALE_BIT_ruRU;d["Arial Narrow"]=[[Fonts\ARIALN.TTF]]d["Nimrod MT"]=[[Fonts\NIM_____.ttf]]LSM.DefaultMedia.font="Arial Narrow"else c=LSM.LOCALE_BIT_western;b=true;d["Arial Narrow"]=[[Fonts\ARIALN.TTF]]d["Friz Quadrata TT"]=[[Fonts\FRIZQT__.TTF]]d["Morpheus"]=[[Fonts\MORPHEUS.TTF]]d["Skurri"]=[[Fonts\SKURRI.TTF]]LSM.DefaultMedia.font="Arial Narrow"end;if not LSM.MediaTable.statusbar then LSM.MediaTable.statusbar={}end;LSM.MediaTable.statusbar["Blizzard"]=[[Interface\TargetingFrame\UI-StatusBar]]LSM.MediaTable.statusbar["Blizzard Character Skills Bar"]=[[Interface\PaperDollInfoFrame\UI-Character-Skills-Bar]]LSM.DefaultMedia.statusbar="Blizzard"if not LSM.MediaTable.sound then LSM.MediaTable.sound={}end;LSM.MediaTable.sound["None"]=[[Interface\Quiet.ogg]]LSM.DefaultMedia.sound="None"local e=LSM.DefaultMedia;local f=LSM.MediaList;local g=LSM.MediaTable;local h=LSM.OverrideMedia;local function i(j)local k=g[j]if not k then return end;if not f[j]then f[j]={}end;local l=f[j]local m=0;for n in pairs(k)do m=m+1;l[m]=n end;table_sort(l)end;
function LSM:Register(j,o,p,q)if type(j)~="string"then error(LSM_MAJOR..":Register(mediatype, key, data, langmask) - mediatype must be string, got "..type(j))end;if type(o)~="string"then error(LSM_MAJOR..":Register(mediatype, key, data, langmask) - key must be string, got "..type(o))end;j=j:lower()if j==LSM.MediaType.FONT and q and band(q,c)==0 or not(q or b)then return false end;if not g[j]then g[j]={}end;local k=g[j]if k[o]then return false end;k[o]=p;i(j)self.callbacks:Fire("LibSharedMedia_Registered",j,o)return true end;
function LSM:Fetch(j,o,r)local s=g[j]local t=h[j]local u=s and t and s[t]or s[o]or not r and e[j]and s[e[j]]or nil;return u end;
function LSM:IsValid(j,o)return g[j]and not o or g[j][o]and true or false end;
function LSM:HashTable(j)return g[j]end;
function LSM:List(j)if not g[j]then return nil end;if not f[j]then i(j)end;return f[j]end;
function LSM:GetGlobal(j)return h[j]end;
function LSM:SetGlobal(j,o)if not g[j]then return false end;h[j]=o and g[j][o]and o or nil;self.callbacks:Fire("LibSharedMedia_SetGlobal",j,h[j])return true end;
function LSM:GetDefault(j)return e[j]end;
function LSM:SetDefault(j,o)if g[j]and g[j][o]and not e[j]then e[j]=o;return true else return false end end
local BROKER_MAJOR, MINOR = "LibDataBroker-1.1";
local BROKER = LibStub:NewLibrary(BROKER_MAJOR, MAX_MINOR, true)
BROKER.callbacks=BROKER.callbacks or LibStub:GetLibrary("CallbackHandler-1.0"):New(BROKER)BROKER.attributestorage,BROKER.namestorage,BROKER.proxystorage=BROKER.attributestorage or{},BROKER.namestorage or{},BROKER.proxystorage or{}local attributestorage,namestorage,callbacks=BROKER.attributestorage,BROKER.namestorage,BROKER.callbacks;BROKER.domt={__metatable="access denied",__index=function(a,b)return attributestorage[a]and attributestorage[a][b]end}BROKER.domt.__newindex=function(a,b,c)if not attributestorage[a]then attributestorage[a]={}end;if attributestorage[a][b]==c then return end;attributestorage[a][b]=c;local d=namestorage[a]if not d then return end;callbacks:Fire("LibDataBroker_AttributeChanged",d,b,c,a)callbacks:Fire("LibDataBroker_AttributeChanged_"..d,d,b,c,a)callbacks:Fire("LibDataBroker_AttributeChanged_"..d.."_"..b,d,b,c,a)callbacks:Fire("LibDataBroker_AttributeChanged__"..b,d,b,c,a)end;
function BROKER:NewDataObject(d,e)if self.proxystorage[d]then return end;if e then assert(type(e)=="table","Invalid dataobj, must be nil or a table")self.attributestorage[e]={}for f,g in pairs(e)do self.attributestorage[e][f]=g;e[f]=nil end end;e=setmetatable(e or{},self.domt)self.proxystorage[d],self.namestorage[e]=e,d;self.callbacks:Fire("LibDataBroker_DataObjectCreated",d,e)return e end;
function BROKER:DataObjectIterator()return pairs(self.proxystorage)end;
function BROKER:GetDataObjectByName(h)return self.proxystorage[h]end;
function BROKER:GetNameByDataObject(i)return self.namestorage[i]end;
local j=pairs(attributestorage)
function BROKER:pairs(k)local l=type(k)assert(l=="string"or l=="table","Usage: ldb:pairs('dataobjectname') or ldb:pairs(dataobject)")local e=self.proxystorage[k]or k;assert(attributestorage[e],"Data object not found")return j,attributestorage[e],nil end;
local m=ipairs(attributestorage)
function BROKER:ipairs(k)local l=type(k)assert(l=="string"or l=="table","Usage: ldb:ipairs('dataobjectname') or ldb:ipairs(dataobject)")local e=self.proxystorage[k]or k;assert(attributestorage[e],"Data object not found")return m,attributestorage[e],0 end
local WINDOW_MAJOR = "LibWindow-1.1";
local WINDOW = LibStub:NewLibrary(WINDOW_MAJOR,MAX_MINOR,true);
WINDOW.utilFrame = WINDOW.utilFrame or CreateFrame("Frame")
WINDOW.delayedSavePosition = WINDOW.delayedSavePosition or {}
WINDOW.windowData = WINDOW.windowData or {} 
WINDOW.embeds = WINDOW.embeds or {}
local mixins = {}
local function a(b,c)local names=WINDOW.windowData[b].names;if names then if names[c]then return names[c]end;if names.prefix then return names.prefix..c end end;return c end;
local function d(b,c,e)WINDOW.windowData[b].storage[a(b,c)]=e end;
local function f(b,c)return WINDOW.windowData[b].storage[a(b,c)]end;
WINDOW.utilFrame:SetScript("OnUpdate",function(g)g:Hide()for b,h in pairs(WINDOW.delayedSavePosition)do WINDOW.delayedSavePosition[b]=nil;WINDOW.SavePosition(b)end end)
local function i(b)WINDOW.delayedSavePosition[b]=true;WINDOW.utilFrame:Show()end;
mixins["RegisterConfig"]=true;
function WINDOW.RegisterConfig(b,storage,names)if not WINDOW.windowData[b]then WINDOW.windowData[b]={}end;WINDOW.windowData[b].names=names;WINDOW.windowData[b].storage=storage end;local j={GetWidth=function()return GetScreenWidth()*UIParent:GetScale()end,GetHeight=function()return GetScreenHeight()*UIParent:GetScale()end,GetScale=function()return 1 end}mixins["SavePosition"]=true;
function WINDOW.SavePosition(b)local k=b:GetParent()if not k then k=j elseif k~=UIParent then return end;local l=b:GetScale()local m,n=b:GetLeft()*l,b:GetTop()*l;local o,p=b:GetRight()*l,b:GetBottom()*l;local q,r=k:GetWidth(),k:GetHeight()local s,t,u;if m<q-o and m<abs((m+o)/2-q/2)then s=m;u="LEFT"elseif q-o<abs((m+o)/2-q/2)then s=o-q;u="RIGHT"else s=(m+o)/2-q/2;u=""end;if p<r-n and p<abs((p+n)/2-r/2)then t=p;u="BOTTOM"..u elseif r-n<abs((p+n)/2-r/2)then t=n-r;u="TOP"..u else t=(p+n)/2-r/2 end;if u==""then u="CENTER"end;d(b,"x",s)d(b,"y",t)d(b,"point",u)d(b,"scale",l)b:ClearAllPoints()b:SetPoint(u,b:GetParent(),u,s/l,t/l)end;mixins["RestorePosition"]=true;
function WINDOW.RestorePosition(b)local s=f(b,"x")local t=f(b,"y")local u=f(b,"point")local l=f(b,"scale")if l then (b.lw11origSetScale or b.SetScale)(b,l)else l=b:GetScale()end;if not s or not t then s=0;t=0;u="CENTER"end;s=s/l;t=t/l;b:ClearAllPoints()if not u and t==0 then u="CENTER"end;if not u then b:SetPoint("TOPLEFT",b:GetParent(),"BOTTOMLEFT",s,t)i(b)return end;b:SetPoint(u,b:GetParent(),u,s,t)end;mixins["SetScale"]=true;
function WINDOW.SetScale(b,v)d(b,"scale",v)(b.lw11origSetScale or b.SetScale)(b,v)WINDOW.RestorePosition(b)end;
function WINDOW.OnDragStart(b)WINDOW.windowData[b].isDragging=true;b:StartMoving()end;
function WINDOW.OnDragStop(b)b:StopMovingOrSizing()WINDOW.SavePosition(b)WINDOW.windowData[b].isDragging=false;if WINDOW.windowData[b].altEnable and not IsAltKeyDown()then b:EnableMouse(false)end end;local function w(...)return WINDOW.OnDragStart(...)end;
local function x(...)return WINDOW.OnDragStop(...)end;mixins["MakeDraggable"]=true;
function WINDOW.MakeDraggable(b)assert(WINDOW.windowData[b])b:SetMovable(true)b:SetScript("OnDragStart",w)b:SetScript("OnDragStop",x)b:RegisterForDrag("LeftButton")end;
function WINDOW.OnMouseWheel(b,y)local v=f(b,"scale")if y<0 then v=max(v*0.9,0.1)else v=min(v/0.9,3)end;WINDOW.SetScale(b,v)end;
local function z(...)return WINDOW.OnMouseWheel(...)end;
mixins["EnableMouseWheelScaling"]=true;
function WINDOW.EnableMouseWheelScaling(b)b:SetScript("OnMouseWheel",z)end;WINDOW.utilFrame:SetScript("OnEvent",function(g,A,B,C)if A=="MODIFIER_STATE_CHANGED"then if B=="LALT"or B=="RALT"then for b,h in pairs(WINDOW.altEnabledFrames)do if not WINDOW.windowData[b].isDragging then b:EnableMouse(C==1)end end end end end)mixins["EnableMouseOnAlt"]=true;
function WINDOW.EnableMouseOnAlt(b)assert(WINDOW.windowData[b])WINDOW.windowData[b].altEnable=true;b:EnableMouse(not not IsAltKeyDown())if not WINDOW.altEnabledFrames then WINDOW.altEnabledFrames={}WINDOW.utilFrame:RegisterEvent("MODIFIER_STATE_CHANGED")end;WINDOW.altEnabledFrames[b]=true end;
function WINDOW:Embed(D)if not D or not D[0]or not D.GetObjectType then error("Usage: WINDOW:Embed(frame)",1)end;D.lw11origSetScale=D.SetScale;for c,h in pairs(mixins)do D[c]=self[c]end;WINDOW.embeds[D]=true;return D end;for D,h in pairs(WINDOW.embeds)do WINDOW:Embed(D)end