added options to _framerate.

Pawkette [03-15-14 - 16:37]
added options to _framerate.
added LAM support.
added LMP support.
Filename
DejaVuSansMono.ttf
LICENSE
_framerate.lua
_framerate.txt
_framerateConfig.lua
fonts/DejaVuSansMono.ttf
fonts/LICENSE
libs/LibAddonMenu-1.0/LibAddonMenu-1.0.lua
libs/LibMediaProvider-1.0/LibMediaProvider-1.0.lua
libs/LibStub/LibStub.lua
diff --git a/DejaVuSansMono.ttf b/DejaVuSansMono.ttf
deleted file mode 100644
index 8b7bb2a..0000000
Binary files a/DejaVuSansMono.ttf and /dev/null differ
diff --git a/LICENSE b/LICENSE
deleted file mode 100644
index 254e2cc..0000000
--- a/LICENSE
+++ /dev/null
@@ -1,99 +0,0 @@
-Fonts are (c) Bitstream (see below). DejaVu changes are in public domain.
-Glyphs imported from Arev fonts are (c) Tavmjong Bah (see below)
-
-Bitstream Vera Fonts Copyright
-------------------------------
-
-Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is
-a trademark of Bitstream, Inc.
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of the fonts accompanying this license ("Fonts") and associated
-documentation files (the "Font Software"), to reproduce and distribute the
-Font Software, including without limitation the rights to use, copy, merge,
-publish, distribute, and/or sell copies of the Font Software, and to permit
-persons to whom the Font Software is furnished to do so, subject to the
-following conditions:
-
-The above copyright and trademark notices and this permission notice shall
-be included in all copies of one or more of the Font Software typefaces.
-
-The Font Software may be modified, altered, or added to, and in particular
-the designs of glyphs or characters in the Fonts may be modified and
-additional glyphs or characters may be added to the Fonts, only if the fonts
-are renamed to names not containing either the words "Bitstream" or the word
-"Vera".
-
-This License becomes null and void to the extent applicable to Fonts or Font
-Software that has been modified and is distributed under the "Bitstream
-Vera" names.
-
-The Font Software may be sold as part of a larger software package but no
-copy of one or more of the Font Software typefaces may be sold by itself.
-
-THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT,
-TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME
-FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING
-ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES,
-WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
-THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE
-FONT SOFTWARE.
-
-Except as contained in this notice, the names of Gnome, the Gnome
-Foundation, and Bitstream Inc., shall not be used in advertising or
-otherwise to promote the sale, use or other dealings in this Font Software
-without prior written authorization from the Gnome Foundation or Bitstream
-Inc., respectively. For further information, contact: fonts at gnome dot
-org.
-
-Arev Fonts Copyright
-------------------------------
-
-Copyright (c) 2006 by Tavmjong Bah. All Rights Reserved.
-
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of the fonts accompanying this license ("Fonts") and
-associated documentation files (the "Font Software"), to reproduce
-and distribute the modifications to the Bitstream Vera Font Software,
-including without limitation the rights to use, copy, merge, publish,
-distribute, and/or sell copies of the Font Software, and to permit
-persons to whom the Font Software is furnished to do so, subject to
-the following conditions:
-
-The above copyright and trademark notices and this permission notice
-shall be included in all copies of one or more of the Font Software
-typefaces.
-
-The Font Software may be modified, altered, or added to, and in
-particular the designs of glyphs or characters in the Fonts may be
-modified and additional glyphs or characters may be added to the
-Fonts, only if the fonts are renamed to names not containing either
-the words "Tavmjong Bah" or the word "Arev".
-
-This License becomes null and void to the extent applicable to Fonts
-or Font Software that has been modified and is distributed under the
-"Tavmjong Bah Arev" names.
-
-The Font Software may be sold as part of a larger software package but
-no copy of one or more of the Font Software typefaces may be sold by
-itself.
-
-THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
-OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL
-TAVMJONG BAH BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
-DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
-OTHER DEALINGS IN THE FONT SOFTWARE.
-
-Except as contained in this notice, the name of Tavmjong Bah shall not
-be used in advertising or otherwise to promote the sale, use or other
-dealings in this Font Software without prior written authorization
-from Tavmjong Bah. For further information, contact: tavmjong @ free
-. fr.
-
-$Id: LICENSE 2133 2007-11-28 02:46:28Z lechimp $
diff --git a/_framerate.lua b/_framerate.lua
index 4079bb4..2b833cd 100644
--- a/_framerate.lua
+++ b/_framerate.lua
@@ -27,22 +27,18 @@ THE SOFTWARE.
 ]]
 -------------------------------------------------

-local ranges =
-{
-    OKAY = 30,
-    GOOD = 60
-}
-
-local colors =
-{
-    BAD = { r = 255, g = 0, b = 0 },
-    OKAY = { r = 255, g = 255, b = 0 },
-    GOOD = { r = 0, g = 255, b = 0 }
-}
-
-local _framerate = ZO_Object:Subclass()
-_framerate.color = colors.GOOD
-_framerate.frame_rate = 0
+local CBM = CALLBACK_MANAGER
+local ranges = { okay = 30, good = 60 }
+local strformat = string.format
+
+local function clamp( val, min, max )
+    return zo_max( zo_min( val, max ), min )
+end
+
+local _framerate        = ZO_Object:Subclass()
+_framerate.colors       = setmetatable( {}, { __mode = 'kv' } )
+_framerate.frame_rate   = 0
+_framerate.last_time    = 0

 function _framerate:New( ... )
     local result = ZO_Object.New( self )
@@ -51,29 +47,54 @@ function _framerate:New( ... )
 end

 function _framerate:Initialize( control )
-    self.control = control
-    self.label = control:GetNamedChild( '_Label' )
-    self.label:SetFont( string.format( '%s|%d|soft-shadow-thin', [[_framerate/DejaVuSansMono.ttf]], 10 ) )
+    self.control  = control
+    self.label    = control:GetNamedChild( '_Label' )
+
+    self.control:RegisterForEvent( EVENT_ADD_ON_LOADED, function( _, addon ) self:AddonLoaded( addon ) end )
+end
+
+function _framerate:AddonLoaded( addon )
+    if ( addon ~= '_framerate' ) then
+        return
+    end
+
+    CBM:RegisterCallback( '_FRAMERATE_COLORS_CHANGED', function( ... ) self:OnColorsChanged( ... ) end )
+    CBM:RegisterCallback( '_FRAMERATE_FONT_CHANGED', function( ... ) self:OnFontChanged( ... ) end )

-    self.control:SetHandler( 'OnUpdate', function() self:OnUpdate() end )
+    CBM:FireCallbacks( '_FRAMERATE_LOADED' )
+    self.control:SetHandler( 'OnUpdate', function( _, frameTime ) self:OnUpdate( frameTime ) end )
 end

-function _framerate:OnUpdate()
+function _framerate:OnColorsChanged( good, okay, bad )
+    self.colors = { bad, okay, good }
+end
+
+function _framerate:OnFontChanged( newFont )
+    self.label:SetFont( newFont )
+end
+
+function _framerate:OnUpdate( frameTime )
+    if ( frameTime - self.last_time < 0.5 ) then
+        return
+    end
+
     if ( GetFramerate() == self.frame_rate ) then
         return
     end
+
+    self.last_time = frameTime

     self.frame_rate = GetFramerate()
-    if ( self.frame_rate < ranges.OKAY ) then
-        self.color = colors.BAD
-    elseif ( self.frame_rate < ranges.GOOD ) then
-        self.color = colors.OKAY
-    else
-        self.color = colors.GOOD
+
+    local level = 1
+    if ( self.frame_rate >= ranges.good ) then
+        level = 3
+    elseif ( self.frame_rate >= ranges.okay ) then
+        level = 2
     end

-    self.label:SetText( string.format( 'FPS: %.1f', self.frame_rate ) )
-    self.label:SetColor( self.color.r, self.color.g, self.color.b )
+    self.label:SetText( strformat( 'FPS: %.1f', self.frame_rate ) )
+    self.label:SetColor( unpack( self.colors[ level ] ) )
 end

 function Initialized( self )
diff --git a/_framerate.txt b/_framerate.txt
index 33a35b9..74867c0 100644
--- a/_framerate.txt
+++ b/_framerate.txt
@@ -1,6 +1,13 @@
 ## Title: _framerate
 ## APIVersion: 100000
+## OptionalDependsOn: LibStub LibMediaProvider-1.0 LibAddonMenu-1.0
+## SavedVariables: FRAMERATE_DB

+libs/libstub/libstub.lua
+libs/libmediaprovider-1.0/libmediaprovider-1.0.lua
+libs/libaddonmenu-1.0/libaddonmenu-1.0.lua
+
+_framerateConfig.lua
 _framerate.lua
 _framerate.xml
 Bindings.xml
\ No newline at end of file
diff --git a/_framerateConfig.lua b/_framerateConfig.lua
new file mode 100644
index 0000000..c111f39
--- /dev/null
+++ b/_framerateConfig.lua
@@ -0,0 +1,102 @@
+local LAM = LibStub( 'LibAddonMenu-1.0' )
+if ( not LAM ) then return end
+
+local LMP = LibStub( 'LibMediaProvider-1.0' )
+if ( not LMP ) then return end
+LMP:Register( LMP.MediaType.FONT, 'DejaVu Sans Mono', [[_framerate/fonts/dejavusansmono.ttf]] )
+
+local CBM = CALLBACK_MANAGER
+local _framerateConfig = {}
+
+local defaults =
+{
+    font_face = 'DejaVu Sans Mono',
+    font_size = '10',
+    font_decoration = 'shadow',
+
+    colors =
+    {
+        bad     = { 255, 0, 0, 1 },
+        okay    = { 255, 255, 0, 1 },
+        good    = { 0, 255, 0, 1  }
+    }
+}
+
+local decorations = { 'none', 'soft-shadow-thin', 'soft-shadow-thick', 'shadow' }
+
+function _framerateConfig:FormatFont( face, size, decoration )
+    local format = '%s|%d'
+    if ( decoration ~= 'none' ) then
+        format = format .. '|%s'
+    end
+
+    return string.format( format, LMP:Fetch( LMP.MediaType.FONT, face ), size, decoration )
+end
+
+function _framerateConfig:OnLoaded()
+    self.db             = ZO_SavedVars:NewAccountWide( 'FRAMERATE_DB', 1.0, nil, defaults )
+    self.config_panel   = LAM:CreateControlPanel( '_framerate_config', '_framerate' )
+
+    self:CreateConfig()
+
+    self:OnColorsChanged()
+    self:OnFontChanged()
+end
+
+function _framerateConfig:CreateConfig()
+    LAM:AddHeader( self.config_panel, '_framerate_header_font', 'Font' )
+
+    LAM:AddDropdown( self.config_panel, '_framerate_font', 'Font:', '',  LMP:List( LMP.MediaType.FONT ),
+        function() return self.db.font_face end,
+        function( face )
+            self.db.font_face = face
+            self:OnFontChanged()
+        end )
+
+    LAM:AddSlider( self.config_panel, '_framerate_size', 'Size:', '', 8, 50, 1,
+        function() return self.db.font_size end,
+        function( size )
+            self.db.font_size = size
+            self:OnFontChanged()
+        end )
+
+    LAM:AddDropdown( self.config_panel, '_framerate_decoration', 'Decoration:', '', decorations,
+        function() return self.db.font_decoration end,
+        function( deco )
+            self.db.font_decoration = deco
+            self:OnFontChanged()
+        end )
+
+    LAM:AddHeader( self.config_panel, '_framerate_header_colors', 'Colors' )
+
+    LAM:AddColorPicker( self.config_panel, '_framerate_color_good', 'Good:', '',
+        function() return unpack( self.db.colors.good ) end,
+        function( r, g, b, a )
+            self.db.colors.good = { r,g,b,a }
+            self:OnColorsChanged()
+        end )
+
+    LAM:AddColorPicker( self.config_panel, '_framerate_color_okay', 'Okay:', '',
+        function() return unpack( self.db.colors.okay ) end,
+        function( r, g, b, a )
+            self.db.colors.okay = { r,g,b,a }
+            self:OnColorsChanged()
+        end )
+
+    LAM:AddColorPicker( self.config_panel, '_framerate_color_bad', 'Bad:', '',
+        function() return unpack( self.db.colors.bad ) end,
+        function( r, g, b, a )
+            self.db.colors.bad = { r,g,b,a }
+            self:OnColorsChanged()
+        end )
+end
+
+function _framerateConfig:OnColorsChanged()
+    CBM:FireCallbacks( '_FRAMERATE_COLORS_CHANGED', self.db.colors.good, self.db.colors.okay, self.db.colors.bad )
+end
+
+function _framerateConfig:OnFontChanged()
+    CBM:FireCallbacks( '_FRAMERATE_FONT_CHANGED', self:FormatFont( self.db.font_face, self.db.font_size, self.db.font_decoration ) )
+end
+
+CBM:RegisterCallback( '_FRAMERATE_LOADED', function() _framerateConfig:OnLoaded() end )
\ No newline at end of file
diff --git a/fonts/DejaVuSansMono.ttf b/fonts/DejaVuSansMono.ttf
new file mode 100644
index 0000000..8b7bb2a
Binary files /dev/null and b/fonts/DejaVuSansMono.ttf differ
diff --git a/fonts/LICENSE b/fonts/LICENSE
new file mode 100644
index 0000000..254e2cc
--- /dev/null
+++ b/fonts/LICENSE
@@ -0,0 +1,99 @@
+Fonts are (c) Bitstream (see below). DejaVu changes are in public domain.
+Glyphs imported from Arev fonts are (c) Tavmjong Bah (see below)
+
+Bitstream Vera Fonts Copyright
+------------------------------
+
+Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is
+a trademark of Bitstream, Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of the fonts accompanying this license ("Fonts") and associated
+documentation files (the "Font Software"), to reproduce and distribute the
+Font Software, including without limitation the rights to use, copy, merge,
+publish, distribute, and/or sell copies of the Font Software, and to permit
+persons to whom the Font Software is furnished to do so, subject to the
+following conditions:
+
+The above copyright and trademark notices and this permission notice shall
+be included in all copies of one or more of the Font Software typefaces.
+
+The Font Software may be modified, altered, or added to, and in particular
+the designs of glyphs or characters in the Fonts may be modified and
+additional glyphs or characters may be added to the Fonts, only if the fonts
+are renamed to names not containing either the words "Bitstream" or the word
+"Vera".
+
+This License becomes null and void to the extent applicable to Fonts or Font
+Software that has been modified and is distributed under the "Bitstream
+Vera" names.
+
+The Font Software may be sold as part of a larger software package but no
+copy of one or more of the Font Software typefaces may be sold by itself.
+
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT,
+TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME
+FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING
+ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
+THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE
+FONT SOFTWARE.
+
+Except as contained in this notice, the names of Gnome, the Gnome
+Foundation, and Bitstream Inc., shall not be used in advertising or
+otherwise to promote the sale, use or other dealings in this Font Software
+without prior written authorization from the Gnome Foundation or Bitstream
+Inc., respectively. For further information, contact: fonts at gnome dot
+org.
+
+Arev Fonts Copyright
+------------------------------
+
+Copyright (c) 2006 by Tavmjong Bah. All Rights Reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the fonts accompanying this license ("Fonts") and
+associated documentation files (the "Font Software"), to reproduce
+and distribute the modifications to the Bitstream Vera Font Software,
+including without limitation the rights to use, copy, merge, publish,
+distribute, and/or sell copies of the Font Software, and to permit
+persons to whom the Font Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright and trademark notices and this permission notice
+shall be included in all copies of one or more of the Font Software
+typefaces.
+
+The Font Software may be modified, altered, or added to, and in
+particular the designs of glyphs or characters in the Fonts may be
+modified and additional glyphs or characters may be added to the
+Fonts, only if the fonts are renamed to names not containing either
+the words "Tavmjong Bah" or the word "Arev".
+
+This License becomes null and void to the extent applicable to Fonts
+or Font Software that has been modified and is distributed under the
+"Tavmjong Bah Arev" names.
+
+The Font Software may be sold as part of a larger software package but
+no copy of one or more of the Font Software typefaces may be sold by
+itself.
+
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL
+TAVMJONG BAH BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+OTHER DEALINGS IN THE FONT SOFTWARE.
+
+Except as contained in this notice, the name of Tavmjong Bah shall not
+be used in advertising or otherwise to promote the sale, use or other
+dealings in this Font Software without prior written authorization
+from Tavmjong Bah. For further information, contact: tavmjong @ free
+. fr.
+
+$Id: LICENSE 2133 2007-11-28 02:46:28Z lechimp $
diff --git a/libs/LibAddonMenu-1.0/LibAddonMenu-1.0.lua b/libs/LibAddonMenu-1.0/LibAddonMenu-1.0.lua
new file mode 100644
index 0000000..8e0b61d
--- /dev/null
+++ b/libs/LibAddonMenu-1.0/LibAddonMenu-1.0.lua
@@ -0,0 +1,373 @@
+local MAJOR, MINOR = "LibAddonMenu-1.0", 5
+local lam, oldminor = LibStub:NewLibrary(MAJOR, MINOR)
+if not lam then return end	--the same or newer version of this lib is already loaded into memory
+
+--UPVALUES--
+lam.lastAddedControl = {}
+local lastAddedControl = lam.lastAddedControl
+local wm = GetWindowManager()
+local strformat = string.format
+local tostring = tostring
+local round = zo_round
+local optionsWindow = ZO_OptionsWindowSettingsScrollChild
+
+--maybe return the controls from the creation functions?
+
+function lam:CreateControlPanel(controlPanelID, controlPanelName)
+	local panelID
+
+	if _G[controlPanelID] then
+		panelID = _G[controlPanelID]
+		return panelID
+	end
+
+	ZO_OptionsWindow_AddUserPanel(controlPanelID, controlPanelName)
+
+	--disables Defaults button because we don't need it, but keybind still works :/ ...
+	panelID = _G[controlPanelID]
+	ZO_PreHook("ZO_OptionsWindow_ChangePanels", function(panel)
+			local enable = (panel ~=  panelID)
+			ZO_OptionsWindowResetToDefaultButton:SetEnabled(enable)
+			ZO_OptionsWindowResetToDefaultButton:SetKeybindEnabled(enable)
+		end)
+
+	return panelID
+end
+
+function lam:AddHeader(panelID, controlName, text)
+	local header = wm:CreateControlFromVirtual(controlName, optionsWindow, lastAddedControl[panelID] and "ZO_Options_SectionTitle_WithDivider" or "ZO_Options_SectionTitle")
+	if lastAddedControl[panelID] then
+		header:SetAnchor(TOPLEFT, lastAddedControl[panelID], BOTTOMLEFT, 0, 15)
+	else
+		header:SetAnchor(TOPLEFT)
+	end
+	header.controlType = OPTIONS_SECTION_TITLE
+	header.panel = panelID
+	header.text = text
+
+	ZO_OptionsWindow_InitializeControl(header)
+
+	lastAddedControl[panelID] = header
+
+	return header
+end
+
+
+--To-Do list:
+--extra sub-options window out to the right?? (or maybe addon list?)
+--find alternatives to handler hooks
+
+function lam:AddSlider(panelID, controlName, text, tooltip, minValue, maxValue, step, getFunc, setFunc, warning, warningText)
+	local slider = wm:CreateControlFromVirtual(controlName, optionsWindow, "ZO_Options_Slider")
+	slider:SetAnchor(TOPLEFT, lastAddedControl[panelID], BOTTOMLEFT, 0, 6)
+	slider.controlType = OPTIONS_SLIDER
+	slider.system = SETTING_TYPE_UI
+	slider.panel = panelID
+	slider.text = text
+	slider.tooltipText = tooltip
+	slider.showValue = true
+	slider.showValueMin = minValue
+	slider.showValueMax = maxValue
+	local range = maxValue - minValue
+	local slidercontrol = slider:GetNamedChild("Slider")
+	local slidervalue = slider:GetNamedChild("ValueLabel")
+	slidercontrol:SetValueStep(1/range * step)
+	slider:SetHandler("OnShow", function()
+			local curValue = getFunc()
+			slidercontrol:SetValue((curValue - minValue)/range)
+			slidervalue:SetText(tostring(curValue))
+		end)
+	slidercontrol:SetHandler("OnValueChanged", function (self, value)
+			self:SetValue(value)
+			value = round(value*range + minValue)
+			slidervalue:SetText(strformat("%d", value))
+		end)
+	slidercontrol:SetHandler("OnSliderReleased", function(self, value)
+			value = round(value*range + minValue)
+			setFunc(value)
+		end)
+
+	if warning then
+		slider.warning = wm:CreateControlFromVirtual(controlName.."WarningIcon", slider, "ZO_Options_WarningIcon")
+		slider.warning:SetAnchor(RIGHT, slidercontrol, LEFT, -5, 0)
+		slider.warning.tooltipText = warningText
+	end
+
+	ZO_OptionsWindow_InitializeControl(slider)
+
+	lastAddedControl[panelID] = slider
+
+	return slider
+end
+
+function lam:AddDropdown(panelID, controlName, text, tooltip, validChoices, getFunc, setFunc, warning, warningText)
+	local dropdown = wm:CreateControlFromVirtual(controlName, optionsWindow, "ZO_Options_Dropdown")
+	dropdown:SetAnchor(TOPLEFT, lastAddedControl[panelID], BOTTOMLEFT, 0, 6)
+	dropdown.controlType = OPTIONS_DROPDOWN
+	dropdown.system = SETTING_TYPE_UI
+	dropdown.panel = panelID
+	dropdown.text = text
+	dropdown.tooltipText = tooltip
+	dropdown.valid = validChoices
+	local dropmenu = ZO_ComboBox_ObjectFromContainer(GetControl(dropdown, "Dropdown"))
+	local setText = dropmenu.m_selectedItemText.SetText
+	local selectedName
+	ZO_PreHookHandler(dropmenu.m_selectedItemText, "OnTextChanged", function(self)
+			if dropmenu.m_selectedItemData then
+				selectedName = dropmenu.m_selectedItemData.name
+				setText(self, selectedName)
+				setFunc(selectedName)
+			end
+		end)
+	dropdown:SetHandler("OnShow", function()
+			dropmenu:SetSelectedItem(getFunc())
+		end)
+
+	if warning then
+		dropdown.warning = wm:CreateControlFromVirtual(controlName.."WarningIcon", dropdown, "ZO_Options_WarningIcon")
+		dropdown.warning:SetAnchor(RIGHT, dropdown:GetNamedChild("Dropdown"), LEFT, -5, 0)
+		dropdown.warning.tooltipText = warningText
+	end
+
+	ZO_OptionsWindow_InitializeControl(dropdown)
+
+	lastAddedControl[panelID] = dropdown
+
+	return dropdown
+end
+
+function lam:AddCheckbox(panelID, controlName, text, tooltip, getFunc, setFunc, warning, warningText)
+	local checkbox = wm:CreateControlFromVirtual(controlName, optionsWindow, "ZO_Options_Checkbox")
+	checkbox:SetAnchor(TOPLEFT, lastAddedControl[panelID], BOTTOMLEFT, 0, 6)
+	checkbox.controlType = OPTIONS_CHECKBOX
+	checkbox.system = SETTING_TYPE_UI
+	checkbox.settingId = _G[strformat("SETTING_%s", controlName)]
+	checkbox.panel = panelID
+	checkbox.text = text
+	checkbox.tooltipText = tooltip
+
+	local checkboxButton = checkbox:GetNamedChild("Checkbox")
+
+	ZO_PreHookHandler(checkbox, "OnShow", function()
+			checkboxButton:SetState(getFunc() and 1 or 0)
+			checkboxButton:toggleFunction(getFunc())
+		end)
+	ZO_PreHookHandler(checkboxButton, "OnClicked", function() setFunc(not getFunc()) end)
+
+	if warning then
+		checkbox.warning = wm:CreateControlFromVirtual(controlName.."WarningIcon", checkbox, "ZO_Options_WarningIcon")
+		checkbox.warning:SetAnchor(RIGHT, checkboxButton, LEFT, -5, 0)
+		checkbox.warning.tooltipText = warningText
+	end
+
+	ZO_OptionsWindow_InitializeControl(checkbox)
+
+	lastAddedControl[panelID] = checkbox
+
+	return checkbox
+end
+
+function lam:AddColorPicker(panelID, controlName, text, tooltip, getFunc, setFunc, warning, warningText)
+	local colorpicker = wm:CreateTopLevelWindow(controlName)
+	colorpicker:SetParent(optionsWindow)
+	colorpicker:SetAnchor(TOPLEFT, lastAddedControl[panelID], BOTTOMLEFT, 0, 10)
+	colorpicker:SetResizeToFitDescendents(true)
+	colorpicker:SetWidth(510)
+	colorpicker:SetMouseEnabled(true)
+
+	colorpicker.label = wm:CreateControl(controlName.."Label", colorpicker, CT_LABEL)
+	local label = colorpicker.label
+	label:SetDimensions(300, 26)
+	label:SetAnchor(TOPLEFT)
+	label:SetFont("ZoFontWinH4")
+	label:SetWrapMode(TEXT_WRAP_MODE_ELLIPSIS)
+	label:SetText(text)
+
+	colorpicker.color = wm:CreateControl(controlName.."Color", colorpicker, CT_CONTROL)
+	local color = colorpicker.color
+	color:SetDimensions(200,26)
+	color:SetAnchor(RIGHT)
+
+	color.thumb = wm:CreateControl(controlName.."ColorThumb", color, CT_TEXTURE)
+	local thumb = color.thumb
+	thumb:SetDimensions(36, 18)
+	thumb:SetAnchor(LEFT, color, LEFT, 4, 0)
+	local r, g, b, a = getFunc()
+	thumb:SetColor(r, g, b, a or 1)
+
+	color.border = wm:CreateControl(controlName.."ColorBorder", color, CT_TEXTURE)
+	local border = color.border
+	border:SetTexture("EsoUI\\Art\\ChatWindow\\chatOptions_bgColSwatch_frame.dds")
+	border:SetTextureCoords(0, .625, 0, .8125)
+	border:SetDimensions(40, 22)
+	border:SetAnchor(CENTER, thumb, CENTER, 0, 0)
+
+	local ColorPickerCallback
+	if not ColorPickerCallback then
+		ColorPickerCallback = function(r, g, b, a)
+			thumb:SetColor(r, g, b, a or 1)
+			setFunc(r, g, b, a)
+		end
+	end
+
+	colorpicker.controlType = OPTIONS_CUSTOM
+	colorpicker.customSetupFunction = function(colorpicker)
+			colorpicker:SetHandler("OnMouseUp", function(self, btn, upInside)
+					if upInside then
+						local r, g, b, a = getFunc()
+						COLOR_PICKER:Show(ColorPickerCallback, r, g, b, a, text)
+					end
+				end)
+		end
+	colorpicker.panel = panelID
+	colorpicker.tooltipText = tooltip
+	colorpicker:SetHandler("OnMouseEnter", ZO_Options_OnMouseEnter)
+	colorpicker:SetHandler("OnMouseExit", ZO_Options_OnMouseExit)
+
+	if warning then
+		colorpicker.warning = wm:CreateControlFromVirtual(controlName.."WarningIcon", colorpicker, "ZO_Options_WarningIcon")
+		colorpicker.warning:SetAnchor(RIGHT, colorpicker:GetNamedChild("Color"), LEFT, -5, 0)
+		colorpicker.warning.tooltipText = warningText
+	end
+
+	ZO_OptionsWindow_InitializeControl(colorpicker)
+
+	lastAddedControl[panelID] = colorpicker
+
+	return colorpicker
+end
+
+function lam:AddEditBox(panelID, controlName, text, tooltip, isMultiLine, getFunc, setFunc, warning, warningText)
+	local editbox = wm:CreateTopLevelWindow(controlName)
+	editbox:SetParent(optionsWindow)
+	editbox:SetAnchor(TOPLEFT, lastAddedControl[panelID], BOTTOMLEFT, 0, 10)
+	editbox:SetResizeToFitDescendents(true)
+	editbox:SetWidth(510)
+	editbox:SetMouseEnabled(true)
+
+	editbox.label = wm:CreateControl(controlName.."Label", editbox, CT_LABEL)
+	local label = editbox.label
+	label:SetDimensions(300, 26)
+	label:SetAnchor(TOPLEFT)
+	label:SetFont("ZoFontWinH4")
+	label:SetWrapMode(TEXT_WRAP_MODE_ELLIPSIS)
+	label:SetText(text)
+
+	editbox.bg = wm:CreateControlFromVirtual(controlName.."BG", editbox, "ZO_EditBackdrop")
+	local bg = editbox.bg
+	bg:SetDimensions(200,isMultiLine and 100 or 24)
+	bg:SetAnchor(RIGHT)
+	editbox.edit = wm:CreateControlFromVirtual(controlName.."Edit", bg, isMultiLine and "ZO_DefaultEditMultiLineForBackdrop" or "ZO_DefaultEditForBackdrop")
+	editbox.edit:SetText(getFunc())
+	editbox.edit:SetHandler("OnFocusLost", function(self) setFunc(self:GetText()) end)
+
+
+	editbox.panel = panelID
+	editbox.tooltipText = tooltip
+	editbox:SetHandler("OnMouseEnter", ZO_Options_OnMouseEnter)
+	editbox:SetHandler("OnMouseExit", ZO_Options_OnMouseExit)
+
+	if warning then
+		editbox.warning = wm:CreateControlFromVirtual(controlName.."WarningIcon", editbox, "ZO_Options_WarningIcon")
+		editbox.warning:SetAnchor(TOPRIGHT, editbox:GetNamedChild("BG"), TOPLEFT, -5, 0)
+		editbox.warning.tooltipText = warningText
+	end
+
+	ZO_OptionsWindow_InitializeControl(editbox)
+
+	lastAddedControl[panelID] = editbox
+
+	return editbox
+end
+
+function lam:AddButton(panelID, controlName, text, tooltip, onClick, warning, warningText)
+	local button = wm:CreateTopLevelWindow(controlName)
+	button:SetParent(optionsWindow)
+	button:SetAnchor(TOPLEFT, lastAddedControl[panelID], BOTTOMLEFT, 0, 6)
+	button:SetDimensions(510, 28)
+	button:SetMouseEnabled(true)
+
+	button.btn = wm:CreateControlFromVirtual(controlName.."Button", button, "ZO_DefaultButton")
+	local btn = button.btn
+	btn:SetAnchor(TOPRIGHT)
+	btn:SetWidth(200)
+	btn:SetText(text)
+	btn:SetHandler("OnClicked", onClick)
+
+	button.controlType = OPTIONS_CUSTOM
+	button.customSetupFunction = function() end	--move handlers into this function? (since I created a function...)
+	button.panel = panelID
+	btn.tooltipText = tooltip
+	btn:SetHandler("OnMouseEnter", ZO_Options_OnMouseEnter)
+	btn:SetHandler("OnMouseExit", ZO_Options_OnMouseExit)
+
+	if warning then
+		button.warning = wm:CreateControlFromVirtual(controlName.."WarningIcon", button, "ZO_Options_WarningIcon")
+		button.warning:SetAnchor(RIGHT, btn, LEFT, -5, 0)
+		button.warning.tooltipText = warningText
+	end
+
+	ZO_OptionsWindow_InitializeControl(button)
+
+	lastAddedControl[panelID] = button
+
+	return button
+end
+
+function lam:AddDescription(panelID, controlName, text, titleText)
+	local textBox = wm:CreateTopLevelWindow(controlName)
+	textBox:SetParent(optionsWindow)
+	textBox:SetAnchor(TOPLEFT, lastAddedControl[panelID], BOTTOMLEFT, 0, 10)
+	textBox:SetResizeToFitDescendents(true)
+	textBox:SetWidth(510)
+
+	if titleText then
+		textBox.title = wm:CreateControl(controlName.."Title", textBox, CT_LABEL)
+		local title = textBox.title
+		title:SetWidth(510)
+		title:SetAnchor(TOPLEFT, textBox, TOPLEFT)
+		title:SetFont("ZoFontWinH4")
+		title:SetText(headerText)
+	end
+
+	textBox.desc = wm:CreateControl(controlName.."Text", textBox, CT_LABEL)
+	local desc = textBox.desc
+	desc:SetWidth(510)
+	if titleText then
+		desc:SetAnchor(TOPLEFT, textBox.title, BOTTOMLEFT)
+	else
+		desc:SetAnchor(TOPLEFT)
+	end
+	desc:SetVerticalAlignment(TEXT_ALIGN_TOP)
+	desc:SetFont("ZoFontGame")
+	desc:SetText(text)
+
+	textBox.controlType = OPTIONS_CUSTOM
+	textBox.panel = panelID
+
+	ZO_OptionsWindow_InitializeControl(textBox)
+
+	lastAddedControl[panelID] = textBox
+
+	return textBox
+end
+
+
+--test controls & examples--
+--[[local controlPanelID = lam:CreateControlPanel("ZAM_ADDON_OPTIONS", "ZAM Addons")
+lam:AddHeader(controlPanelID, "ZAM_Addons_TESTADDON", "TEST ADDON")
+lam:AddDescription(controlPanelID, "ZAM_Addons_TESTDESC", "This is a test description.", "Header")
+lam:AddSlider(controlPanelID, "ZAM_TESTSLIDER", "Test slider", "Adjust the slider.", 1, 10, 1, function() return 7 end, function(value) end, true, "needs UI reload")
+lam:AddDropdown(controlPanelID, "ZAM_TESTDROPDOWN", "Test Dropdown", "Pick something!", {"thing 1", "thing 2", "thing 3"}, function() return "thing 2" end, function(self,valueString) print(valueString) end)
+local checkbox1 = true
+lam:AddCheckbox(controlPanelID, "ZAM_TESTCHECKBOX", "Test Checkbox", "On or off?", function() return checkbox1 end, function(value) checkbox1 = not checkbox1 print(value, checkbox1) end)
+lam:AddColorPicker(controlPanelID, "ZAM_TESTCOLORPICKER", "Test color picker", "What's your favorite color?", function() return 1, 1, 0 end, function(r,g,b) print(r,g,b) end)
+lam:AddEditBox(controlPanelID, "ZAM_TESTEDITBOX", "Test Edit Box", "This is a tooltip!", false, function() return "hi" end, function(text) print(text) end)
+lam:AddHeader(controlPanelID, "ZAM_Addons_TESTADDON2", "TEST ADDON 2")
+local checkbox2 = false
+lam:AddCheckbox(controlPanelID, "ZAM_TESTCHECKBOX2", "Test Checkbox 2", "On or off?", function() return checkbox2 end, function(value) checkbox2 = not checkbox2 print(value, checkbox2) end)
+lam:AddButton(controlPanelID, "ZAM_TESTBUTTON", "Test Button", "Click me", function() print("hi") end, true, "oh noez!")
+lam:AddEditBox(controlPanelID, "ZAM_TESTEDITBOX2", "Test Edit Box 2", "This is a tooltip!", true, function() return "hi" end, function(text) print(text) end, true, "warning text")
+lam:AddSlider(controlPanelID, "ZAM_TESTSLIDER2", "Test slider 2", "Adjust the slider.", 50, 100, 10, function() return 80 end, function(value) end)
+lam:AddDropdown(controlPanelID, "ZAM_TESTDROPDOWN2", "Test Dropdown 2", "Pick something!", {"thing 4", "thing 5", "thing 6"}, function() return "thing 6" end, function(self,valueString) print(valueString) end)
+]]--
\ No newline at end of file
diff --git a/libs/LibMediaProvider-1.0/LibMediaProvider-1.0.lua b/libs/LibMediaProvider-1.0/LibMediaProvider-1.0.lua
new file mode 100644
index 0000000..2ad539c
--- /dev/null
+++ b/libs/LibMediaProvider-1.0/LibMediaProvider-1.0.lua
@@ -0,0 +1,165 @@
+--LibMediaProvider-1.0 is inspired by and borrows from LibSharedMedia-3.0 for World of Warcraft by Elkano
+--LibSharedMedia-3.0 and LibMediaProvider-1.0 are under the LGPL-2.1 license
+
+local MAJOR, MINOR = "LibMediaProvider-1.0", 1 -- remember to increase manually on changes
+local LMP = LibStub:NewLibrary(MAJOR, MINOR)
+if not LMP then return end
+
+local cm = CALLBACK_MANAGER
+local tinsert = table.insert
+local tsort = table.sort
+local pairs = pairs
+
+LMP.DefaultMedia = {}
+LMP.MediaList = {}
+LMP.MediaTable = {}
+LMP.MediaType = {}
+local defaultMedia = LMP.DefaultMedia
+local mediaList = LMP.MediaList
+local mediaTable = LMP.MediaTable
+LMP.MediaType = {
+	BACKGROUND = "background",	-- background textures
+	BORDER = "border",			-- border textures
+	FONT = "font",				-- fonts
+	STATUSBAR = "statusbar",	-- statusbar textures
+	SOUND = "sound",			-- sound files
+}
+
+--DEFAULT UI MEDIA--
+-- BACKGROUND
+LMP.MediaTable.background = {}
+--commented out because it still leaves a white texture behind - addons can use alpha to hide the background
+--LMP.MediaTable.background["None"]				= ""
+LMP.MediaTable.background["ESO Black"]			= "EsoUI/Art/Miscellaneous/borderedinset_center.dds"
+LMP.MediaTable.background["ESO Chat"]			= "EsoUI/Art/chatwindow/chat_bg_center.dds"
+LMP.MediaTable.background["ESO Gray"]			= "EsoUI/Art/itemtooltip/simpleprogbarbg_center.dds"
+LMP.MediaTable.background["Solid"]				= "EsoUI/Art/miscellaneous/progressbar_genericfill_tall.dds"
+LMP.DefaultMedia.background = "None"
+
+-- BORDER
+LMP.MediaTable.border = {}
+--commented out because it still leaves a white texture behind - addons can use alpha to hide the border
+--LMP.MediaTable.border["None"]					= ""
+LMP.MediaTable.border["ESO Gold"]				= "EsoUI/Art/Miscellaneous/borderedinsettransparent_edgefile.dds"
+LMP.MediaTable.border["ESO Chat"]				= "EsoUI/Art/chatwindow/chat_bg_edge.dds"
+LMP.MediaTable.border["ESO Rounded"]			= "EsoUI/Art/miscellaneous/interactkeyframe_edge.dds"
+LMP.MediaTable.border["ESO Blue Highlight"]		= "EsoUI/Art/miscellaneous/textentry_highlight_edge.dds"
+LMP.MediaTable.border["ESO Blue Glow"]			= "EsoUI/Art/crafting/crafting_tooltip_glow_edge_blue64.dds"
+LMP.MediaTable.border["ESO Red Glow"]			= "EsoUI/Art/crafting/crafting_tooltip_glow_edge_red64.dds"
+LMP.MediaTable.border["ESO Red Overlay"]		= "EsoUI/Art/uicombatoverlay/uicombatoverlayedge.dds"
+LMP.DefaultMedia.border = "None"
+
+-- FONT
+LMP.MediaTable.font = {}
+LMP.MediaTable.font["ProseAntique"]				= "EsoUI/Common/Fonts/ProseAntiquePSMT.otf"
+LMP.MediaTable.font["Arial Narrow"]				= "EsoUI/Common/Fonts/arialn.ttf"
+LMP.MediaTable.font["Consolas"]					= "EsoUI/Common/Fonts/consola.ttf"
+LMP.MediaTable.font["ESO Cartographer"]			= "EsoUI/Common/Fonts/esocartographer-bold.otf"
+LMP.MediaTable.font["Fontin Bold"]				= "EsoUI/Common/Fonts/fontin_sans_b.otf"
+LMP.MediaTable.font["Fontin Italic"]			= "EsoUI/Common/Fonts/fontin_sans_i.otf"
+LMP.MediaTable.font["Fontin Regular"]			= "EsoUI/Common/Fonts/fontin_sans_r.otf"
+LMP.MediaTable.font["Fontin SmallCaps"]			= "EsoUI/Common/Fonts/fontin_sans_sc.otf"
+LMP.MediaTable.font["Skyrim Handwritten"]		= "EsoUI/Common/Fonts/Handwritten_Bold.otf"
+LMP.MediaTable.font["Trajan Pro"]				= "EsoUI/Common/Fonts/trajanpro-regular.otf"
+LMP.MediaTable.font["Univers 55"]				= "EsoUI/Common/Fonts/univers55.otf"
+LMP.MediaTable.font["Univers 57"]				= "EsoUI/Common/Fonts/univers57.otf"
+LMP.MediaTable.font["Univers 67"]				= "EsoUI/Common/Fonts/univers67.otf"
+LMP.DefaultMedia.font = "Arial Narrow"
+
+-- STATUSBAR
+LMP.MediaTable.statusbar = {}
+LMP.MediaTable.statusbar["ESO Basic"]			= "EsoUI/Art/miscellaneous/progressbar_genericfill_tall.dds"
+LMP.DefaultMedia.statusbar = "ESO Basic"
+
+-- SOUND
+LMP.MediaTable.sound = {}
+LMP.MediaTable.sound["None"]					= ""
+LMP.MediaTable.sound["AvA Gate Open"]			= SOUNDS.AVA_GATE_OPENED
+LMP.MediaTable.sound["AvA Gate Close"]			= SOUNDS.AVA_GATE_CLOSED
+LMP.MediaTable.sound["Emperor Coronated"]		= SOUNDS.EMPEROR_CORONATED_DAGGERFALL
+LMP.MediaTable.sound["Level Up"]				= SOUNDS.LEVEL_UP
+LMP.MediaTable.sound["Skill Gained"]			= SOUNDS.SKILL_GAINED
+LMP.MediaTable.sound["Ability Purchased"]		= SOUNDS.ABILITY_SKILL_PURCHASED
+LMP.MediaTable.sound["Book Acquired"]			= SOUNDS.BOOK_ACQUIRED
+LMP.MediaTable.sound["Unlock"]					= SOUNDS.LOCKPICKING_UNLOCKED
+LMP.MediaTable.sound["Enchanting Extract"]		= SOUNDS.ENCHANTING_EXTRACT_START_ANIM
+LMP.MediaTable.sound["Enchanting Create"]		= SOUNDS.ENCHANTING_CREATE_TOOLTIP_GLOW
+LMP.MediaTable.sound["Blacksmith Improve"]		= SOUNDS.BLACKSMITH_IMPROVE_TOOLTIP_GLOW_SUCCESS
+LMP.DefaultMedia.sound = "None"
+
+local function rebuildMediaList(mediatype)
+	local mtable = mediaTable[mediatype]
+	if not mtable then return end
+	if not mediaList[mediatype] then mediaList[mediatype] = {} end
+	local mlist = mediaList[mediatype]
+	-- list can only get larger, so simply overwrite it
+	local i = 0
+	for k in pairs(mtable) do
+		i = i + 1
+		mlist[i] = k
+	end
+	tsort(mlist)
+end
+
+function LMP:Register(mediatype, key, data)
+	if type(mediatype) ~= "string" then
+		error(MAJOR..":Register(mediatype, key, data) - mediatype must be string, got "..type(mediatype))
+	end
+	if type(key) ~= "string" then
+		error(MAJOR..":Register(mediatype, key, data) - key must be string, got "..type(key))
+	end
+	mediatype = mediatype:lower()
+	if not mediaTable[mediatype] then
+		mediaTable[mediatype] = {}
+	end
+
+	local mtable = mediaTable[mediatype]
+	if mtable[key] then
+		return false
+	end
+	mtable[key] = data
+	rebuildMediaList(mediatype)
+	cm:FireCallbacks("LibMediaProvider_Registered", mediatype, key)
+	return true
+end
+
+function LMP:Fetch(mediatype, key)
+	local mtt = mediaTable[mediatype]
+	local result = mtt and mtt[key]
+	return result ~= "" and result or nil
+end
+
+function LMP:IsValid(mediatype, key)
+	return mediaTable[mediatype] and (not key or mediaTable[mediatype][key]) and true or false
+end
+
+function LMP:HashTable(mediatype)
+	return mediaTable[mediatype]
+end
+
+--Will this work with ESO's dropdowns?
+--Does something else need to be done here?
+function LMP:List(mediatype)
+	if not mediaTable[mediatype] then
+		return nil
+	end
+	if not mediaList[mediatype] then
+		rebuildMediaList(mediatype)
+	end
+	return mediaList[mediatype]
+end
+
+function LMP:GetDefault(mediatype)
+	return defaultMedia[mediatype]
+end
+
+function LMP:SetDefault(mediatype, key)
+	if mediaTable[mediatype] and mediaTable[mediatype][key] and not defaultMedia[mediatype] then
+		defaultMedia[mediatype] = key
+		return true
+	else
+		return false
+	end
+end
+
+
diff --git a/libs/LibStub/LibStub.lua b/libs/LibStub/LibStub.lua
new file mode 100644
index 0000000..4c509a5
--- /dev/null
+++ b/libs/LibStub/LibStub.lua
@@ -0,0 +1,34 @@
+-- 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
+-- LibStub developed for World of Warcraft by above members of the WowAce community.
+-- Ported to Elder Scrolls Online by Seerah
+
+local LIBSTUB_MAJOR, LIBSTUB_MINOR = "LibStub", 1  -- NEVER MAKE THIS AN SVN REVISION! IT NEEDS TO BE USABLE IN ALL REPOS!
+local LibStub = _G[LIBSTUB_MAJOR]
+
+local strformat = string.format
+if not LibStub or LibStub.minor < LIBSTUB_MINOR then
+	LibStub = LibStub or {libs = {}, minors = {} }
+	_G[LIBSTUB_MAJOR] = LibStub
+	LibStub.minor = LIBSTUB_MINOR
+
+	function LibStub:NewLibrary(major, minor)
+		assert(type(major) == "string", "Bad argument #2 to `NewLibrary' (string expected)")
+		minor = assert(tonumber(zo_strmatch(minor, "%d+")), "Minor version must either be a number or contain a number.")
+
+		local oldminor = self.minors[major]
+		if oldminor and oldminor >= minor then return nil end
+		self.minors[major], self.libs[major] = minor, self.libs[major] or {}
+		return self.libs[major], oldminor
+	end
+
+	function LibStub:GetLibrary(major, silent)
+		if not self.libs[major] and not silent then
+			error(("Cannot find a library instance of %q."):strformat(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