changeset 4:1c3534391efb

- Added timeout for stampers.
author Tercio
date Mon, 02 Mar 2015 21:28:33 -0300
parents 4a3ffc2ee399
children 789bf9e40966
files Hansgar_And_Franzok_Assist.lua Hansgar_And_Franzok_Assist.toc Libs/AceComm-3.0/AceComm-3.0.lua Libs/AceComm-3.0/AceComm-3.0.xml Libs/AceComm-3.0/ChatThrottleLib.lua Libs/AceTimer-3.0/AceTimer-3.0.lua Libs/AceTimer-3.0/AceTimer-3.0.xml Libs/CallbackHandler-1.0/CallbackHandler-1.0.lua Libs/CallbackHandler-1.0/CallbackHandler-1.0.xml Libs/LibStub/LibStub.lua Libs/LibStub/LibStub.toc Libs/LibStub/tests/test.lua Libs/LibStub/tests/test2.lua Libs/LibStub/tests/test3.lua Libs/LibStub/tests/test4.lua Libs/libs.xml
diffstat 16 files changed, 1807 insertions(+), 10 deletions(-) [+]
line wrap: on
line diff
--- a/Hansgar_And_Franzok_Assist.lua	Tue Feb 24 14:53:29 2015 -0300
+++ b/Hansgar_And_Franzok_Assist.lua	Mon Mar 02 21:28:33 2015 -0300
@@ -3,10 +3,14 @@
 local UnitExists = UnitExists
 local GetPlayerMapPosition = GetPlayerMapPosition
 local UnitHealth = UnitHealth
+local GetNumGroupMembers = GetNumGroupMembers
+local abs = abs
 
 local f = CreateFrame ("frame", "Hansgar_And_Franzok_Assist", UIParent)
 f:SetFrameStrata ("DIALOG")
 
+local tframe = CreateFrame ("frame", "Hansgar_And_Franzok_Assist_PTrack", UIParent)
+
 f:SetSize (155, 156)
 f:SetBackdrop ({bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", tile = true, tileSize = 16, insets = {left = -1, right = -1, top = -1, bottom = -1},
 edgeFile = "Interface\\AddOns\\Hansgar_And_Franzok_Assist\\border_2", edgeSize = 8})
@@ -44,8 +48,67 @@
 
 --
 
+local player_pos_frame = CreateFrame ("frame", "Hansgar_And_Franzok_Assist_DanceBar", UIParent)
+player_pos_frame:SetPoint ("topleft", player_bar, "bottomleft", 0, -3)
+player_pos_frame:SetPoint ("topright", player_bar, "bottomright", 0, -3)
+player_pos_frame:SetHeight (14)
+player_pos_frame:SetBackdrop ({bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", tile = true, tileSize = 16, insets = {left = -1, right = -1, top = -1, bottom = -1},
+edgeFile = "Interface\\AddOns\\Hansgar_And_Franzok_Assist\\border_2", edgeSize = 8})
+player_pos_frame:SetBackdropColor (0, 0, 0, 1)
+player_pos_frame:Hide()
+
+--red
+local t1 = player_pos_frame:CreateTexture (nil, "artwork")
+t1:SetPoint ("left", player_pos_frame, "left")
+t1:SetSize (player_pos_frame:GetWidth()*0.30, 14)
+t1:SetTexture (1, 1, 1)
+--t1:SetTexCoord (260/512, 430/512, 29/256, 82/256)
+t1:SetVertexColor (1, 0.2, 0.2, 0.4)
+
+--green
+local t2 = player_pos_frame:CreateTexture (nil, "artwork")
+t2:SetPoint ("left", t1, "right")
+t2:SetSize (player_pos_frame:GetWidth()*0.15, 14)
+t2:SetTexture (0.2, 1, 0.2, 0.4)
+
+--red
+local middle = player_pos_frame:CreateTexture (nil, "artwork")
+middle:SetPoint ("left", t2, "right")
+middle:SetSize (player_pos_frame:GetWidth()*0.10, 14)
+middle:SetTexture (1, 1, 1)
+--middle:SetTexCoord (260/512, 430/512, 29/256, 82/256)
+middle:SetVertexColor (1, 0.2, 0.2, 0.4)
+
+--green
+local t3 = player_pos_frame:CreateTexture (nil, "artwork")
+t3:SetPoint ("left", middle, "right")
+t3:SetSize (player_pos_frame:GetWidth()*0.15, 14)
+t3:SetTexture (0.2, 1, 0.2, 0.4)
+
+--red
+local t4 = player_pos_frame:CreateTexture (nil, "artwork")
+t4:SetPoint ("left", t3, "right")
+t4:SetSize (player_pos_frame:GetWidth()*0.30, 14)
+t4:SetTexture (1, 1, 1)
+--t4:SetTexCoord (260/512, 430/512, 29/256, 82/256)
+t4:SetVertexColor (1, 0.2, 0.2, 0.4)
+
+local div = player_pos_frame:CreateTexture (nil, "overlay")
+div:SetPoint ("left", player_pos_frame, "left", 0, 0)
+div:SetTexture (1, 1, 1, 1)
+div:SetSize (1, 16)
+div:Hide()
+--
+
+local AceTimer = LibStub:GetLibrary ("AceTimer-3.0")
+AceTimer:Embed (f)
+local AceComm = LibStub:GetLibrary ("AceComm-3.0")
+AceComm:Embed (f)
+
 local db
 
+f.block_tracker = {}
+
 frame_event:SetFrameStrata ("FULLSCREEN")
 
 frame_event:SetScript ("OnEvent", function (self, event, ...)
@@ -57,8 +120,15 @@
 			db = {}
 			Hansgar_And_Franzok_DB = db
 		end
+		--
 		db.STAMPERS_DELAY = db.STAMPERS_DELAY or 5
-		
+		if (db.CD_NUMBER == nil) then
+			db.CD_NUMBER = false
+		end
+		if (db.SHOW_DANCE == nil) then
+			db.SHOW_DANCE = true
+		end
+		--
 		SLASH_Hansgar_And_Franzok_Assist1, SLASH_Hansgar_And_Franzok_Assist2 = "/hansgar", "/franzok"
 		function SlashCmdList.Hansgar_And_Franzok_Assist (msg, editbox)
 		
@@ -75,18 +145,40 @@
 				
 			elseif (command == "test" or command == "show") then
 				if (f.StampersPhase) then
+					f:EndTrackPlayerPosition()
 					return f:StopTracking()
 				end
+				
 				f:StartTracking()
+				f:StartTrackPlayerPosition()
 				
 			elseif (command == "hide") then
 				if (f.StampersPhase) then
 					return f:StopTracking()
 				end
+				f:EndTrackPlayerPosition()
 
+			elseif (command == "dance") then
+				db.SHOW_DANCE = not db.SHOW_DANCE
+				if (db.SHOW_DANCE) then
+					if (f.on_encounter) then
+						f:StartTrackPlayerPosition()
+					end
+					print ("|cFFFFAA00Hansgar and Franzok Assist|r dance bars enabled.")
+				else
+					f:EndTrackPlayerPosition()
+					print ("|cFFFFAA00Hansgar and Franzok Assist|r dance bars disabled.")
+				end
+			
+			elseif (command == "cooldown") then
+				db.CD_NUMBER = not db.CD_NUMBER
+				f:RefreshCooldownSettings()
+				
 			else
-				print ("|cFFFFAA00Hansgar and Franzok Assist|r |cFF00FF00v0.4|r Commands:")
+				print ("|cFFFFAA00Hansgar and Franzok Assist|r |cFF00FF00v0.5|r Commands:")
 				print ("|cFFFFFF00/hansgar delay <time>|r: time in seconds until the percentage goes from 0 to 100.")
+				print ("|cFFFFFF00/hansgar cooldown|r: show the countdown for each stamper go back up to the ceiling.")
+				--print ("|cFFFFFF00/hansgar dance|r: toggle dance bar (used to dodge regular stampers and searing plates).")
 				print ("|cFFFFFF00/hansgar test|r: active the addon on test mode.")
 				print ("|cFFFFFF00/hansgar show|r: show the window and start test mode.")
 				print ("|cFFFFFF00/hansgar hide|r: hide the window.")
@@ -96,6 +188,24 @@
 	elseif (event == "ENCOUNTER_START" or event == "ENCOUNTER_END") then
 	
 		local encounterID, encounterName, difficultyID, raidSize = select (1, ...)
+		
+		if (encounterID == 1693) then
+			if (event == "ENCOUNTER_START") then
+				f.on_encounter = true
+			elseif (event == "ENCOUNTER_END") then
+				f.on_encounter = false
+			end
+		end
+		
+		if (encounterID == 1693 and db.SHOW_DANCE) then
+			if (event == "ENCOUNTER_START") then
+				SetMapToCurrentZone()
+				f:StartTrackPlayerPosition()
+			elseif (event == "ENCOUNTER_END") then
+				f:EndTrackPlayerPosition()
+			end
+		end
+		
 		if (encounterID == 1693 and difficultyID == 16) then
 		
 			if (event == "ENCOUNTER_START") then
@@ -113,6 +223,8 @@
 				if (f.StampersPhase) then
 					f:StopTracking()
 				end
+				
+				f:EndTrackPlayerPosition()
 			end
 		end
 	end
@@ -137,7 +249,9 @@
 local frame_tracker = CreateFrame ("frame", "Hansgar_And_Franzok_AssistTracker", UIParent)
 local on_update_tracker = function (self, elapsed)
 	
-	for i = 1, GetNumGroupMembers() do
+	local raid_size = GetNumGroupMembers()
+	
+	for i = 1, raid_size do
 		local x, y = GetPlayerMapPosition ("raid"..i)
 		if (UnitExists ("raid"..i) and UnitHealth ("raid"..i) > 1 and x and y) then
 			local block = f:WhichBlock (x, y)
@@ -152,7 +266,7 @@
 	
 	local px, py = GetPlayerMapPosition ("player")
 	local player_block = f:WhichBlock (px, py)
-	if (player_block) then
+	if (player_block and raid_size > 0) then
 		
 		local time_limit_at = f.block_tracker [player_block] + db.STAMPERS_DELAY
 		local time_now = GetTime()
@@ -236,23 +350,53 @@
 			self:SetScript ("OnUpdate", nil)
 			self.stamper_icon:Show()
 			self.number:Hide()
+			self.cooldown:SetCooldown (GetTime(), 37 - db.STAMPERS_DELAY, 0, 0)
 		end
 	end
 end
+
+function f:UnPaint (block)
+	f:ResetBlock (block)
+end
 function f:Paint (block)
 	block.step = 0
 	block.total_time = 0
 	block:SetScript ("OnUpdate", painting)
+	local unpaint = f:ScheduleTimer ("UnPaint", 37, block)
+	block.unpaint_process = unpaint
+end
+
+function f:ResetBlock (block)
+	block:SetScript ("OnUpdate", nil)
+	block:SetBackdropColor (.8, .8, .8, 0.5)
+	block.number:SetText (block.id)
+	block.number:SetTextColor (1, 1, 1, 0.5)
+	block.number:Show()
+	block.stamper_icon:Hide()
+	block.cooldown:SetCooldown (0, 0, 0, 0)
+	
+	f.block_tracker [block.id] = nil
+	if (block.unpaint_process) then
+		f:CancelTimer (block.unpaint_process)
+		block.unpaint_process = nil
+	end
 end
 
 function f:ResetBlocks()
 	for _, block in ipairs (f.all_blocks) do
-		block:SetScript ("OnUpdate", nil)
-		block:SetBackdropColor (.8, .8, .8, 0.5)
-		block.number:SetText (block.id)
-		block.number:SetTextColor (1, 1, 1, 0.5)
-		block.number:Show()
-		block.stamper_icon:Hide()
+		f:ResetBlock (block)
+	end
+end
+
+function f:RefreshCooldownSettings()
+	for _, block in ipairs (f.all_blocks) do
+		if (not db.CD_NUMBER) then
+			block.cooldown:SetHideCountdownNumbers (true)
+			block.cooldown:SetDrawEdge (false)
+		else
+			block.cooldown:SetHideCountdownNumbers (false)
+			block.cooldown:SetDrawEdge (true)
+		end
 	end
 end
 
@@ -280,6 +424,17 @@
 		block:SetScript ("OnMouseDown", on_mouse_down)
 		block:SetScript ("OnMouseUp", on_mouse_up)
 		
+		local cooldown = CreateFrame ("cooldown", "Hansgar_And_Franzok_Assist_BlockCooldown" .. i, block, "CooldownFrameTemplate")
+		cooldown:SetAllPoints()
+		cooldown:SetFrameLevel (block:GetFrameLevel()+2)
+		
+		if (not db.CD_NUMBER) then
+			cooldown:SetHideCountdownNumbers (true)
+			cooldown:SetDrawEdge (false)
+		end
+		
+		block.cooldown = cooldown
+		
 		block.id = i
 		
 		local number = block:CreateFontString (nil, "artwork", "GameFontHighlight")
@@ -310,6 +465,117 @@
 	
 end
 
+local safe_track = {
+	--space 1
+	{
+		block = {x1 = 0.50154542922974, x2 = 0.49563668874741},
+		left = {x1 = 0.49963343143463, x2 = 0.49963343143463 - 0.000935000000000}, 
+		right = {x1 = 0.49710285663605, x2 = 0.49710285663605 + 0.001012229919432}, 
+		-- {x1 = 0.49963343143463, y1 = 0.73492467403412} -- {x1 = 0.49710285663605, y1 = 0.74445152282715}
+	},
+	--space 2
+	{
+		block = {x1 = 0.4858917593956, x2 = 0.48044270277023},
+		left = {x1 = 0.48433673381805, x2 = 0.48433673381805 - 0.00091059207916}, 
+		right = {x1 = 0.48206025362015, x2 = 0.48206025362015 + 0.00075059207916},
+		-- {x1 = 0.48433673381805, y1 = 0.74292266368866} -- {x1 = 0.48206025362015, y1 = 0.78930181264877}
+	},
+	--space 3
+	{
+		block = {x1 = 0.47047740221024, x2 = 0.4648859500885},
+		left = {x1 = 0.46893924474716, x2 = 0.46893924474716 - 0.001032948493956},  
+		right = {x1 = 0.46635687351227, x2 = 0.46635687351227 + 0.001032948493956},
+		--{x1 = 0.46893924474716, y1 = 0.7981019616127} -- {x1 = 0.46635687351227, y1 = 0.73558133840561}
+	},
+	--space 4
+	{
+		block = {x1 = 0.45503282546997, x2 = 0.44976264238358},
+		left = {x1 = 0.4533554315567, x2 = 0.4533554315567 - 0.000774573974608}, 
+		right = {x1 = 0.45124399662018, x2 = 0.45124399662018 + 0.000770009999999},
+		--{x1 = 0.4533554315567, y1 = 0.74078941345215} -- {x1 = 0.45124399662018, y1 = 0.74088287353516}
+	}
+}
+Hansgar_safe_track = safe_track
+
+--	/hansgar test
+--	/run Hansgar_safe_track [1].block.x1 = 0.50154542922974
+
+local red_alpha_disabled = 0.15
+local red_alpha_enabled = 0.5
+
+local green_alpha_disabled = 0.05
+local green_alpha_enabled = 0.9
+
+local track_function = function (self, elapsed)
+	
+	local x, _ = GetPlayerMapPosition ("player")
+	local block
+	
+	for i = 1, #safe_track do
+		local loc = safe_track [i]
+		if (x >= loc.block.x2 and x <= loc.block.x1) then
+			block = i
+			break
+		end
+	end
+	
+	if (block) then
+	
+		player_pos_frame:Show()
+		block = safe_track [block]
+		
+		if (x >= block.left.x2 and x <= block.left.x1) then
+			t2:SetTexture (0.1, 1, 0.1, green_alpha_enabled)
+			t3:SetTexture (0.2, 1, 0.2, green_alpha_disabled)
+			
+			t1:SetVertexColor (1, 0.2, 0.2, red_alpha_disabled) --red
+			t4:SetVertexColor (1, 0.2, 0.2, red_alpha_disabled) --red 
+			middle:SetVertexColor (1, 0.2, 0.2, red_alpha_disabled) --red
+			
+		elseif (x <= block.right.x2 and x >= block.right.x1) then
+			t3:SetTexture (0.1, 1, 0.1, green_alpha_enabled)
+			t2:SetTexture (0.2, 1, 0.2, green_alpha_disabled)
+			
+			t1:SetVertexColor (1, 0.2, 0.2, red_alpha_disabled) --red
+			t4:SetVertexColor (1, 0.2, 0.2, red_alpha_disabled) --red 
+			middle:SetVertexColor (1, 0.2, 0.2, red_alpha_disabled) --red
+			
+		else
+			t1:SetVertexColor (1, 0.2, 0.2, red_alpha_enabled) --red
+			t4:SetVertexColor (1, 0.2, 0.2, red_alpha_enabled) --red 
+			middle:SetVertexColor (1, 0.2, 0.2, red_alpha_enabled) --red
+			
+			t2:SetTexture (0.2, 1, 0.2, green_alpha_disabled)
+			t3:SetTexture (0.2, 1, 0.2, green_alpha_disabled)
+			
+		end
+		
+		--x = x - block.block.x2
+		--local at = abs ((x / (block.block.x1 - block.block.x2) * 100) - 100)
+		--div:SetPoint ("left", player_pos_frame, "left", self.width_pixel * at, 0)
+
+	else
+		player_pos_frame:Hide()
+	end
+end
+
+function f:StartTrackPlayerPosition()
+
+	--> under development
+	if (true) then
+		return
+	end
+
+	player_pos_frame:Show()
+	tframe.width = player_pos_frame:GetWidth()
+	tframe.width_pixel = tframe.width / 100
+	tframe:SetScript ("OnUpdate", track_function)
+end
+function f:EndTrackPlayerPosition()
+	player_pos_frame:Hide()
+	tframe:SetScript ("OnUpdate", nil)
+end
+
 local locs = {
 	--block 1:
 	{x1 = 0.51103663444519, y1 = 0.79726493358612, x2 = 0.50061076879501, y2 = 0.8241291642189},
--- a/Hansgar_And_Franzok_Assist.toc	Tue Feb 24 14:53:29 2015 -0300
+++ b/Hansgar_And_Franzok_Assist.toc	Mon Mar 02 21:28:33 2015 -0300
@@ -3,4 +3,8 @@
 ## Notes: Helps with Smart Stampers on Mythic during this encounter.
 ## SavedVariables: Hansgar_And_Franzok_DB
 
+#@no-lib-strip@
+Libs\libs.xml
+#@end-no-lib-strip@
+
 Hansgar_And_Franzok_Assist.lua
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Libs/AceComm-3.0/AceComm-3.0.lua	Mon Mar 02 21:28:33 2015 -0300
@@ -0,0 +1,302 @@
+--- **AceComm-3.0** allows you to send messages of unlimited length over the addon comm channels.
+-- It'll automatically split the messages into multiple parts and rebuild them on the receiving end.\\
+-- **ChatThrottleLib** is of course being used to avoid being disconnected by the server.
+--
+-- **AceComm-3.0** can be embeded into your addon, either explicitly by calling AceComm:Embed(MyAddon) or by 
+-- specifying it as an embeded library in your AceAddon. All functions will be available on your addon object
+-- and can be accessed directly, without having to explicitly call AceComm itself.\\
+-- It is recommended to embed AceComm, otherwise you'll have to specify a custom `self` on all calls you
+-- make into AceComm.
+-- @class file
+-- @name AceComm-3.0
+-- @release $Id: AceComm-3.0.lua 1107 2014-02-19 16:40:32Z nevcairiel $
+
+--[[ AceComm-3.0
+
+TODO: Time out old data rotting around from dead senders? Not a HUGE deal since the number of possible sender names is somewhat limited.
+
+]]
+
+local MAJOR, MINOR = "AceComm-3.0", 9
+
+local AceComm,oldminor = LibStub:NewLibrary(MAJOR, MINOR)
+
+if not AceComm then return end
+
+local CallbackHandler = LibStub:GetLibrary("CallbackHandler-1.0")
+local CTL = assert(ChatThrottleLib, "AceComm-3.0 requires ChatThrottleLib")
+
+-- Lua APIs
+local type, next, pairs, tostring = type, next, pairs, tostring
+local strsub, strfind = string.sub, string.find
+local match = string.match
+local tinsert, tconcat = table.insert, table.concat
+local error, assert = error, assert
+
+-- WoW APIs
+local Ambiguate = Ambiguate
+
+-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
+-- List them here for Mikk's FindGlobals script
+-- GLOBALS: LibStub, DEFAULT_CHAT_FRAME, geterrorhandler, RegisterAddonMessagePrefix
+
+AceComm.embeds = AceComm.embeds or {}
+
+-- for my sanity and yours, let's give the message type bytes some names
+local MSG_MULTI_FIRST = "\001"
+local MSG_MULTI_NEXT  = "\002"
+local MSG_MULTI_LAST  = "\003"
+local MSG_ESCAPE = "\004"
+
+-- remove old structures (pre WoW 4.0)
+AceComm.multipart_origprefixes = nil
+AceComm.multipart_reassemblers = nil
+
+-- the multipart message spool: indexed by a combination of sender+distribution+
+AceComm.multipart_spool = AceComm.multipart_spool or {} 
+
+--- Register for Addon Traffic on a specified prefix
+-- @param prefix A printable character (\032-\255) classification of the message (typically AddonName or AddonNameEvent), max 16 characters
+-- @param method Callback to call on message reception: Function reference, or method name (string) to call on self. Defaults to "OnCommReceived"
+function AceComm:RegisterComm(prefix, method)
+	if method == nil then
+		method = "OnCommReceived"
+	end
+
+	if #prefix > 16 then -- TODO: 15?
+		error("AceComm:RegisterComm(prefix,method): prefix length is limited to 16 characters")
+	end
+	RegisterAddonMessagePrefix(prefix)
+
+	return AceComm._RegisterComm(self, prefix, method)	-- created by CallbackHandler
+end
+
+local warnedPrefix=false
+
+--- Send a message over the Addon Channel
+-- @param prefix A printable character (\032-\255) classification of the message (typically AddonName or AddonNameEvent)
+-- @param text Data to send, nils (\000) not allowed. Any length.
+-- @param distribution Addon channel, e.g. "RAID", "GUILD", etc; see SendAddonMessage API
+-- @param target Destination for some distributions; see SendAddonMessage API
+-- @param prio OPTIONAL: ChatThrottleLib priority, "BULK", "NORMAL" or "ALERT". Defaults to "NORMAL".
+-- @param callbackFn OPTIONAL: callback function to be called as each chunk is sent. receives 3 args: the user supplied arg (see next), the number of bytes sent so far, and the number of bytes total to send.
+-- @param callbackArg: OPTIONAL: first arg to the callback function. nil will be passed if not specified.
+function AceComm:SendCommMessage(prefix, text, distribution, target, prio, callbackFn, callbackArg)
+	prio = prio or "NORMAL"	-- pasta's reference implementation had different prio for singlepart and multipart, but that's a very bad idea since that can easily lead to out-of-sequence delivery!
+	if not( type(prefix)=="string" and
+			type(text)=="string" and
+			type(distribution)=="string" and
+			(target==nil or type(target)=="string") and
+			(prio=="BULK" or prio=="NORMAL" or prio=="ALERT") 
+		) then
+		error('Usage: SendCommMessage(addon, "prefix", "text", "distribution"[, "target"[, "prio"[, callbackFn, callbackarg]]])', 2)
+	end
+
+	local textlen = #text
+	local maxtextlen = 255  -- Yes, the max is 255 even if the dev post said 256. I tested. Char 256+ get silently truncated. /Mikk, 20110327
+	local queueName = prefix..distribution..(target or "")
+
+	local ctlCallback = nil
+	if callbackFn then
+		ctlCallback = function(sent)
+			return callbackFn(callbackArg, sent, textlen)
+		end
+	end
+	
+	local forceMultipart
+	if match(text, "^[\001-\009]") then -- 4.1+: see if the first character is a control character
+		-- we need to escape the first character with a \004
+		if textlen+1 > maxtextlen then	-- would we go over the size limit?
+			forceMultipart = true	-- just make it multipart, no escape problems then
+		else
+			text = "\004" .. text
+		end
+	end
+
+	if not forceMultipart and textlen <= maxtextlen then
+		-- fits all in one message
+		CTL:SendAddonMessage(prio, prefix, text, distribution, target, queueName, ctlCallback, textlen)
+	else
+		maxtextlen = maxtextlen - 1	-- 1 extra byte for part indicator in prefix(4.0)/start of message(4.1)
+
+		-- first part
+		local chunk = strsub(text, 1, maxtextlen)
+		CTL:SendAddonMessage(prio, prefix, MSG_MULTI_FIRST..chunk, distribution, target, queueName, ctlCallback, maxtextlen)
+
+		-- continuation
+		local pos = 1+maxtextlen
+
+		while pos+maxtextlen <= textlen do
+			chunk = strsub(text, pos, pos+maxtextlen-1)
+			CTL:SendAddonMessage(prio, prefix, MSG_MULTI_NEXT..chunk, distribution, target, queueName, ctlCallback, pos+maxtextlen-1)
+			pos = pos + maxtextlen
+		end
+
+		-- final part
+		chunk = strsub(text, pos)
+		CTL:SendAddonMessage(prio, prefix, MSG_MULTI_LAST..chunk, distribution, target, queueName, ctlCallback, textlen)
+	end
+end
+
+
+----------------------------------------
+-- Message receiving
+----------------------------------------
+
+do
+	local compost = setmetatable({}, {__mode = "k"})
+	local function new()
+		local t = next(compost)
+		if t then 
+			compost[t]=nil
+			for i=#t,3,-1 do	-- faster than pairs loop. don't even nil out 1/2 since they'll be overwritten
+				t[i]=nil
+			end
+			return t
+		end
+		
+		return {}
+	end
+	
+	local function lostdatawarning(prefix,sender,where)
+		DEFAULT_CHAT_FRAME:AddMessage(MAJOR..": Warning: lost network data regarding '"..tostring(prefix).."' from '"..tostring(sender).."' (in "..where..")")
+	end
+
+	function AceComm:OnReceiveMultipartFirst(prefix, message, distribution, sender)
+		local key = prefix.."\t"..distribution.."\t"..sender	-- a unique stream is defined by the prefix + distribution + sender
+		local spool = AceComm.multipart_spool
+		
+		--[[
+		if spool[key] then 
+			lostdatawarning(prefix,sender,"First")
+			-- continue and overwrite
+		end
+		--]]
+		
+		spool[key] = message  -- plain string for now
+	end
+
+	function AceComm:OnReceiveMultipartNext(prefix, message, distribution, sender)
+		local key = prefix.."\t"..distribution.."\t"..sender	-- a unique stream is defined by the prefix + distribution + sender
+		local spool = AceComm.multipart_spool
+		local olddata = spool[key]
+		
+		if not olddata then
+			--lostdatawarning(prefix,sender,"Next")
+			return
+		end
+
+		if type(olddata)~="table" then
+			-- ... but what we have is not a table. So make it one. (Pull a composted one if available)
+			local t = new()
+			t[1] = olddata    -- add old data as first string
+			t[2] = message    -- and new message as second string
+			spool[key] = t    -- and put the table in the spool instead of the old string
+		else
+			tinsert(olddata, message)
+		end
+	end
+
+	function AceComm:OnReceiveMultipartLast(prefix, message, distribution, sender)
+		local key = prefix.."\t"..distribution.."\t"..sender	-- a unique stream is defined by the prefix + distribution + sender
+		local spool = AceComm.multipart_spool
+		local olddata = spool[key]
+		
+		if not olddata then
+			--lostdatawarning(prefix,sender,"End")
+			return
+		end
+
+		spool[key] = nil
+		
+		if type(olddata) == "table" then
+			-- if we've received a "next", the spooled data will be a table for rapid & garbage-free tconcat
+			tinsert(olddata, message)
+			AceComm.callbacks:Fire(prefix, tconcat(olddata, ""), distribution, sender)
+			compost[olddata] = true
+		else
+			-- if we've only received a "first", the spooled data will still only be a string
+			AceComm.callbacks:Fire(prefix, olddata..message, distribution, sender)
+		end
+	end
+end
+
+
+
+
+
+
+----------------------------------------
+-- Embed CallbackHandler
+----------------------------------------
+
+if not AceComm.callbacks then
+	AceComm.callbacks = CallbackHandler:New(AceComm,
+						"_RegisterComm",
+						"UnregisterComm",
+						"UnregisterAllComm")
+end
+
+AceComm.callbacks.OnUsed = nil
+AceComm.callbacks.OnUnused = nil
+
+local function OnEvent(self, event, prefix, message, distribution, sender)
+	if event == "CHAT_MSG_ADDON" then
+		sender = Ambiguate(sender, "none")
+		local control, rest = match(message, "^([\001-\009])(.*)")
+		if control then
+			if control==MSG_MULTI_FIRST then
+				AceComm:OnReceiveMultipartFirst(prefix, rest, distribution, sender)
+			elseif control==MSG_MULTI_NEXT then
+				AceComm:OnReceiveMultipartNext(prefix, rest, distribution, sender)
+			elseif control==MSG_MULTI_LAST then
+				AceComm:OnReceiveMultipartLast(prefix, rest, distribution, sender)
+			elseif control==MSG_ESCAPE then
+				AceComm.callbacks:Fire(prefix, rest, distribution, sender)
+			else
+				-- unknown control character, ignore SILENTLY (dont warn unnecessarily about future extensions!)
+			end
+		else
+			-- single part: fire it off immediately and let CallbackHandler decide if it's registered or not
+			AceComm.callbacks:Fire(prefix, message, distribution, sender)
+		end
+	else
+		assert(false, "Received "..tostring(event).." event?!")
+	end
+end
+
+AceComm.frame = AceComm.frame or CreateFrame("Frame", "AceComm30Frame")
+AceComm.frame:SetScript("OnEvent", OnEvent)
+AceComm.frame:UnregisterAllEvents()
+AceComm.frame:RegisterEvent("CHAT_MSG_ADDON")
+
+
+----------------------------------------
+-- Base library stuff
+----------------------------------------
+
+local mixins = {
+	"RegisterComm",
+	"UnregisterComm",
+	"UnregisterAllComm",
+	"SendCommMessage",
+}
+
+-- Embeds AceComm-3.0 into the target object making the functions from the mixins list available on target:..
+-- @param target target object to embed AceComm-3.0 in
+function AceComm:Embed(target)
+	for k, v in pairs(mixins) do
+		target[v] = self[v]
+	end
+	self.embeds[target] = true
+	return target
+end
+
+function AceComm:OnEmbedDisable(target)
+	target:UnregisterAllComm()
+end
+
+-- Update embeds
+for target, v in pairs(AceComm.embeds) do
+	AceComm:Embed(target)
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Libs/AceComm-3.0/AceComm-3.0.xml	Mon Mar 02 21:28:33 2015 -0300
@@ -0,0 +1,5 @@
+<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/
+..\FrameXML\UI.xsd">
+	<Script file="ChatThrottleLib.lua"/>
+	<Script file="AceComm-3.0.lua"/>
+</Ui>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Libs/AceComm-3.0/ChatThrottleLib.lua	Mon Mar 02 21:28:33 2015 -0300
@@ -0,0 +1,524 @@
+--
+-- ChatThrottleLib by Mikk
+--
+-- Manages AddOn chat output to keep player from getting kicked off.
+--
+-- ChatThrottleLib:SendChatMessage/:SendAddonMessage functions that accept 
+-- a Priority ("BULK", "NORMAL", "ALERT") as well as prefix for SendChatMessage.
+--
+-- Priorities get an equal share of available bandwidth when fully loaded.
+-- Communication channels are separated on extension+chattype+destination and
+-- get round-robinned. (Destination only matters for whispers and channels,
+-- obviously)
+--
+-- Will install hooks for SendChatMessage and SendAddonMessage to measure
+-- bandwidth bypassing the library and use less bandwidth itself.
+--
+--
+-- Fully embeddable library. Just copy this file into your addon directory,
+-- add it to the .toc, and it's done.
+--
+-- Can run as a standalone addon also, but, really, just embed it! :-)
+--
+-- LICENSE: ChatThrottleLib is released into the Public Domain
+--
+
+local CTL_VERSION = 23
+
+local _G = _G
+
+if _G.ChatThrottleLib then
+	if _G.ChatThrottleLib.version >= CTL_VERSION then
+		-- There's already a newer (or same) version loaded. Buh-bye.
+		return
+	elseif not _G.ChatThrottleLib.securelyHooked then
+		print("ChatThrottleLib: Warning: There's an ANCIENT ChatThrottleLib.lua (pre-wow 2.0, <v16) in an addon somewhere. Get the addon updated or copy in a newer ChatThrottleLib.lua (>=v16) in it!")
+		-- ATTEMPT to unhook; this'll behave badly if someone else has hooked...
+		-- ... and if someone has securehooked, they can kiss that goodbye too... >.<
+		_G.SendChatMessage = _G.ChatThrottleLib.ORIG_SendChatMessage
+		if _G.ChatThrottleLib.ORIG_SendAddonMessage then
+			_G.SendAddonMessage = _G.ChatThrottleLib.ORIG_SendAddonMessage
+		end
+	end
+	_G.ChatThrottleLib.ORIG_SendChatMessage = nil
+	_G.ChatThrottleLib.ORIG_SendAddonMessage = nil
+end
+
+if not _G.ChatThrottleLib then
+	_G.ChatThrottleLib = {}
+end
+
+ChatThrottleLib = _G.ChatThrottleLib  -- in case some addon does "local ChatThrottleLib" above us and we're copypasted (AceComm-2, sigh)
+local ChatThrottleLib = _G.ChatThrottleLib
+
+ChatThrottleLib.version = CTL_VERSION
+
+
+
+------------------ TWEAKABLES -----------------
+
+ChatThrottleLib.MAX_CPS = 800			  -- 2000 seems to be safe if NOTHING ELSE is happening. let's call it 800.
+ChatThrottleLib.MSG_OVERHEAD = 40		-- Guesstimate overhead for sending a message; source+dest+chattype+protocolstuff
+
+ChatThrottleLib.BURST = 4000				-- WoW's server buffer seems to be about 32KB. 8KB should be safe, but seen disconnects on _some_ servers. Using 4KB now.
+
+ChatThrottleLib.MIN_FPS = 20				-- Reduce output CPS to half (and don't burst) if FPS drops below this value
+
+
+local setmetatable = setmetatable
+local table_remove = table.remove
+local tostring = tostring
+local GetTime = GetTime
+local math_min = math.min
+local math_max = math.max
+local next = next
+local strlen = string.len
+local GetFramerate = GetFramerate
+local strlower = string.lower
+local unpack,type,pairs,wipe = unpack,type,pairs,wipe
+local UnitInRaid,UnitInParty = UnitInRaid,UnitInParty
+
+
+-----------------------------------------------------------------------
+-- Double-linked ring implementation
+
+local Ring = {}
+local RingMeta = { __index = Ring }
+
+function Ring:New()
+	local ret = {}
+	setmetatable(ret, RingMeta)
+	return ret
+end
+
+function Ring:Add(obj)	-- Append at the "far end" of the ring (aka just before the current position)
+	if self.pos then
+		obj.prev = self.pos.prev
+		obj.prev.next = obj
+		obj.next = self.pos
+		obj.next.prev = obj
+	else
+		obj.next = obj
+		obj.prev = obj
+		self.pos = obj
+	end
+end
+
+function Ring:Remove(obj)
+	obj.next.prev = obj.prev
+	obj.prev.next = obj.next
+	if self.pos == obj then
+		self.pos = obj.next
+		if self.pos == obj then
+			self.pos = nil
+		end
+	end
+end
+
+
+
+-----------------------------------------------------------------------
+-- Recycling bin for pipes 
+-- A pipe is a plain integer-indexed queue of messages
+-- Pipes normally live in Rings of pipes  (3 rings total, one per priority)
+
+ChatThrottleLib.PipeBin = nil -- pre-v19, drastically different
+local PipeBin = setmetatable({}, {__mode="k"})
+
+local function DelPipe(pipe)
+	PipeBin[pipe] = true
+end
+
+local function NewPipe()
+	local pipe = next(PipeBin)
+	if pipe then
+		wipe(pipe)
+		PipeBin[pipe] = nil
+		return pipe
+	end
+	return {}
+end
+
+
+
+
+-----------------------------------------------------------------------
+-- Recycling bin for messages
+
+ChatThrottleLib.MsgBin = nil -- pre-v19, drastically different
+local MsgBin = setmetatable({}, {__mode="k"})
+
+local function DelMsg(msg)
+	msg[1] = nil
+	-- there's more parameters, but they're very repetetive so the string pool doesn't suffer really, and it's faster to just not delete them.
+	MsgBin[msg] = true
+end
+
+local function NewMsg()
+	local msg = next(MsgBin)
+	if msg then
+		MsgBin[msg] = nil
+		return msg
+	end
+	return {}
+end
+
+
+-----------------------------------------------------------------------
+-- ChatThrottleLib:Init
+-- Initialize queues, set up frame for OnUpdate, etc
+
+
+function ChatThrottleLib:Init()	
+
+	-- Set up queues
+	if not self.Prio then
+		self.Prio = {}
+		self.Prio["ALERT"] = { ByName = {}, Ring = Ring:New(), avail = 0 }
+		self.Prio["NORMAL"] = { ByName = {}, Ring = Ring:New(), avail = 0 }
+		self.Prio["BULK"] = { ByName = {}, Ring = Ring:New(), avail = 0 }
+	end
+
+	-- v4: total send counters per priority
+	for _, Prio in pairs(self.Prio) do
+		Prio.nTotalSent = Prio.nTotalSent or 0
+	end
+
+	if not self.avail then
+		self.avail = 0 -- v5
+	end
+	if not self.nTotalSent then
+		self.nTotalSent = 0 -- v5
+	end
+
+
+	-- Set up a frame to get OnUpdate events
+	if not self.Frame then
+		self.Frame = CreateFrame("Frame")
+		self.Frame:Hide()
+	end
+	self.Frame:SetScript("OnUpdate", self.OnUpdate)
+	self.Frame:SetScript("OnEvent", self.OnEvent)	-- v11: Monitor P_E_W so we can throttle hard for a few seconds
+	self.Frame:RegisterEvent("PLAYER_ENTERING_WORLD")
+	self.OnUpdateDelay = 0
+	self.LastAvailUpdate = GetTime()
+	self.HardThrottlingBeginTime = GetTime()	-- v11: Throttle hard for a few seconds after startup
+
+	-- Hook SendChatMessage and SendAddonMessage so we can measure unpiped traffic and avoid overloads (v7)
+	if not self.securelyHooked then
+		-- Use secure hooks as of v16. Old regular hook support yanked out in v21.
+		self.securelyHooked = true
+		--SendChatMessage
+		hooksecurefunc("SendChatMessage", function(...)
+			return ChatThrottleLib.Hook_SendChatMessage(...)
+		end)
+		--SendAddonMessage
+		hooksecurefunc("SendAddonMessage", function(...)
+			return ChatThrottleLib.Hook_SendAddonMessage(...)
+		end)
+	end
+	self.nBypass = 0
+end
+
+
+-----------------------------------------------------------------------
+-- ChatThrottleLib.Hook_SendChatMessage / .Hook_SendAddonMessage
+
+local bMyTraffic = false
+
+function ChatThrottleLib.Hook_SendChatMessage(text, chattype, language, destination, ...)
+	if bMyTraffic then
+		return
+	end
+	local self = ChatThrottleLib
+	local size = strlen(tostring(text or "")) + strlen(tostring(destination or "")) + self.MSG_OVERHEAD
+	self.avail = self.avail - size
+	self.nBypass = self.nBypass + size	-- just a statistic
+end
+function ChatThrottleLib.Hook_SendAddonMessage(prefix, text, chattype, destination, ...)
+	if bMyTraffic then
+		return
+	end
+	local self = ChatThrottleLib
+	local size = tostring(text or ""):len() + tostring(prefix or ""):len();
+	size = size + tostring(destination or ""):len() + self.MSG_OVERHEAD
+	self.avail = self.avail - size
+	self.nBypass = self.nBypass + size	-- just a statistic
+end
+
+
+
+-----------------------------------------------------------------------
+-- ChatThrottleLib:UpdateAvail
+-- Update self.avail with how much bandwidth is currently available
+
+function ChatThrottleLib:UpdateAvail()
+	local now = GetTime()
+	local MAX_CPS = self.MAX_CPS;
+	local newavail = MAX_CPS * (now - self.LastAvailUpdate)
+	local avail = self.avail
+
+	if now - self.HardThrottlingBeginTime < 5 then
+		-- First 5 seconds after startup/zoning: VERY hard clamping to avoid irritating the server rate limiter, it seems very cranky then
+		avail = math_min(avail + (newavail*0.1), MAX_CPS*0.5)
+		self.bChoking = true
+	elseif GetFramerate() < self.MIN_FPS then		-- GetFrameRate call takes ~0.002 secs
+		avail = math_min(MAX_CPS, avail + newavail*0.5)
+		self.bChoking = true		-- just a statistic
+	else
+		avail = math_min(self.BURST, avail + newavail)
+		self.bChoking = false
+	end
+
+	avail = math_max(avail, 0-(MAX_CPS*2))	-- Can go negative when someone is eating bandwidth past the lib. but we refuse to stay silent for more than 2 seconds; if they can do it, we can.
+
+	self.avail = avail
+	self.LastAvailUpdate = now
+
+	return avail
+end
+
+
+-----------------------------------------------------------------------
+-- Despooling logic
+-- Reminder:
+-- - We have 3 Priorities, each containing a "Ring" construct ...
+-- - ... made up of N "Pipe"s (1 for each destination/pipename)
+-- - and each pipe contains messages
+
+function ChatThrottleLib:Despool(Prio)
+	local ring = Prio.Ring
+	while ring.pos and Prio.avail > ring.pos[1].nSize do
+		local msg = table_remove(ring.pos, 1)
+		if not ring.pos[1] then  -- did we remove last msg in this pipe?
+			local pipe = Prio.Ring.pos
+			Prio.Ring:Remove(pipe)
+			Prio.ByName[pipe.name] = nil
+			DelPipe(pipe)
+		else
+			Prio.Ring.pos = Prio.Ring.pos.next
+		end
+		local didSend=false
+		local lowerDest = strlower(msg[3] or "")
+		if lowerDest == "raid" and not UnitInRaid("player") then
+			-- do nothing
+		elseif lowerDest == "party" and not UnitInParty("player") then
+			-- do nothing
+		else
+			Prio.avail = Prio.avail - msg.nSize
+			bMyTraffic = true
+			msg.f(unpack(msg, 1, msg.n))
+			bMyTraffic = false
+			Prio.nTotalSent = Prio.nTotalSent + msg.nSize
+			DelMsg(msg)
+			didSend = true
+		end
+		-- notify caller of delivery (even if we didn't send it)
+		if msg.callbackFn then
+			msg.callbackFn (msg.callbackArg, didSend)
+		end
+		-- USER CALLBACK MAY ERROR
+	end
+end
+
+
+function ChatThrottleLib.OnEvent(this,event)
+	-- v11: We know that the rate limiter is touchy after login. Assume that it's touchy after zoning, too.
+	local self = ChatThrottleLib
+	if event == "PLAYER_ENTERING_WORLD" then
+		self.HardThrottlingBeginTime = GetTime()	-- Throttle hard for a few seconds after zoning
+		self.avail = 0
+	end
+end
+
+
+function ChatThrottleLib.OnUpdate(this,delay)
+	local self = ChatThrottleLib
+
+	self.OnUpdateDelay = self.OnUpdateDelay + delay
+	if self.OnUpdateDelay < 0.08 then
+		return
+	end
+	self.OnUpdateDelay = 0
+
+	self:UpdateAvail()
+
+	if self.avail < 0  then
+		return -- argh. some bastard is spewing stuff past the lib. just bail early to save cpu.
+	end
+
+	-- See how many of our priorities have queued messages (we only have 3, don't worry about the loop)
+	local n = 0
+	for prioname,Prio in pairs(self.Prio) do
+		if Prio.Ring.pos or Prio.avail < 0 then 
+			n = n + 1 
+		end
+	end
+
+	-- Anything queued still?
+	if n<1 then
+		-- Nope. Move spillover bandwidth to global availability gauge and clear self.bQueueing
+		for prioname, Prio in pairs(self.Prio) do
+			self.avail = self.avail + Prio.avail
+			Prio.avail = 0
+		end
+		self.bQueueing = false
+		self.Frame:Hide()
+		return
+	end
+
+	-- There's stuff queued. Hand out available bandwidth to priorities as needed and despool their queues
+	local avail = self.avail/n
+	self.avail = 0
+
+	for prioname, Prio in pairs(self.Prio) do
+		if Prio.Ring.pos or Prio.avail < 0 then
+			Prio.avail = Prio.avail + avail
+			if Prio.Ring.pos and Prio.avail > Prio.Ring.pos[1].nSize then
+				self:Despool(Prio)
+				-- Note: We might not get here if the user-supplied callback function errors out! Take care!
+			end
+		end
+	end
+
+end
+
+
+
+
+-----------------------------------------------------------------------
+-- Spooling logic
+
+function ChatThrottleLib:Enqueue(prioname, pipename, msg)
+	local Prio = self.Prio[prioname]
+	local pipe = Prio.ByName[pipename]
+	if not pipe then
+		self.Frame:Show()
+		pipe = NewPipe()
+		pipe.name = pipename
+		Prio.ByName[pipename] = pipe
+		Prio.Ring:Add(pipe)
+	end
+
+	pipe[#pipe + 1] = msg
+
+	self.bQueueing = true
+end
+
+function ChatThrottleLib:SendChatMessage(prio, prefix,   text, chattype, language, destination, queueName, callbackFn, callbackArg)
+	if not self or not prio or not prefix or not text or not self.Prio[prio] then
+		error('Usage: ChatThrottleLib:SendChatMessage("{BULK||NORMAL||ALERT}", "prefix", "text"[, "chattype"[, "language"[, "destination"]]]', 2)
+	end
+	if callbackFn and type(callbackFn)~="function" then
+		error('ChatThrottleLib:ChatMessage(): callbackFn: expected function, got '..type(callbackFn), 2)
+	end
+
+	local nSize = text:len()
+
+	if nSize>255 then
+		error("ChatThrottleLib:SendChatMessage(): message length cannot exceed 255 bytes", 2)
+	end
+
+	nSize = nSize + self.MSG_OVERHEAD
+
+	-- Check if there's room in the global available bandwidth gauge to send directly
+	if not self.bQueueing and nSize < self:UpdateAvail() then
+		self.avail = self.avail - nSize
+		bMyTraffic = true
+		_G.SendChatMessage(text, chattype, language, destination)
+		bMyTraffic = false
+		self.Prio[prio].nTotalSent = self.Prio[prio].nTotalSent + nSize
+		if callbackFn then
+			callbackFn (callbackArg, true)
+		end
+		-- USER CALLBACK MAY ERROR
+		return
+	end
+
+	-- Message needs to be queued
+	local msg = NewMsg()
+	msg.f = _G.SendChatMessage
+	msg[1] = text
+	msg[2] = chattype or "SAY"
+	msg[3] = language
+	msg[4] = destination
+	msg.n = 4
+	msg.nSize = nSize
+	msg.callbackFn = callbackFn
+	msg.callbackArg = callbackArg
+
+	self:Enqueue(prio, queueName or (prefix..(chattype or "SAY")..(destination or "")), msg)
+end
+
+
+function ChatThrottleLib:SendAddonMessage(prio, prefix, text, chattype, target, queueName, callbackFn, callbackArg)
+	if not self or not prio or not prefix or not text or not chattype or not self.Prio[prio] then
+		error('Usage: ChatThrottleLib:SendAddonMessage("{BULK||NORMAL||ALERT}", "prefix", "text", "chattype"[, "target"])', 2)
+	end
+	if callbackFn and type(callbackFn)~="function" then
+		error('ChatThrottleLib:SendAddonMessage(): callbackFn: expected function, got '..type(callbackFn), 2)
+	end
+
+	local nSize = text:len();
+
+	if RegisterAddonMessagePrefix then
+		if nSize>255 then
+			error("ChatThrottleLib:SendAddonMessage(): message length cannot exceed 255 bytes", 2)
+		end
+	else
+		nSize = nSize + prefix:len() + 1
+		if nSize>255 then
+			error("ChatThrottleLib:SendAddonMessage(): prefix + message length cannot exceed 254 bytes", 2)
+		end
+	end
+
+	nSize = nSize + self.MSG_OVERHEAD;
+
+	-- Check if there's room in the global available bandwidth gauge to send directly
+	if not self.bQueueing and nSize < self:UpdateAvail() then
+		self.avail = self.avail - nSize
+		bMyTraffic = true
+		_G.SendAddonMessage(prefix, text, chattype, target)
+		bMyTraffic = false
+		self.Prio[prio].nTotalSent = self.Prio[prio].nTotalSent + nSize
+		if callbackFn then
+			callbackFn (callbackArg, true)
+		end
+		-- USER CALLBACK MAY ERROR
+		return
+	end
+
+	-- Message needs to be queued
+	local msg = NewMsg()
+	msg.f = _G.SendAddonMessage
+	msg[1] = prefix
+	msg[2] = text
+	msg[3] = chattype
+	msg[4] = target
+	msg.n = (target~=nil) and 4 or 3;
+	msg.nSize = nSize
+	msg.callbackFn = callbackFn
+	msg.callbackArg = callbackArg
+
+	self:Enqueue(prio, queueName or (prefix..chattype..(target or "")), msg)
+end
+
+
+
+
+-----------------------------------------------------------------------
+-- Get the ball rolling!
+
+ChatThrottleLib:Init()
+
+--[[ WoWBench debugging snippet
+if(WOWB_VER) then
+	local function SayTimer()
+		print("SAY: "..GetTime().." "..arg1)
+	end
+	ChatThrottleLib.Frame:SetScript("OnEvent", SayTimer)
+	ChatThrottleLib.Frame:RegisterEvent("CHAT_MSG_SAY")
+end
+]]
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Libs/AceTimer-3.0/AceTimer-3.0.lua	Mon Mar 02 21:28:33 2015 -0300
@@ -0,0 +1,276 @@
+--- **AceTimer-3.0** provides a central facility for registering timers.
+-- AceTimer supports one-shot timers and repeating timers. All timers are stored in an efficient
+-- data structure that allows easy dispatching and fast rescheduling. Timers can be registered
+-- or canceled at any time, even from within a running timer, without conflict or large overhead.\\
+-- AceTimer is currently limited to firing timers at a frequency of 0.01s as this is what the WoW timer API
+-- restricts us to.
+--
+-- All `:Schedule` functions will return a handle to the current timer, which you will need to store if you
+-- need to cancel the timer you just registered.
+--
+-- **AceTimer-3.0** can be embeded into your addon, either explicitly by calling AceTimer:Embed(MyAddon) or by
+-- specifying it as an embeded library in your AceAddon. All functions will be available on your addon object
+-- and can be accessed directly, without having to explicitly call AceTimer itself.\\
+-- It is recommended to embed AceTimer, otherwise you'll have to specify a custom `self` on all calls you
+-- make into AceTimer.
+-- @class file
+-- @name AceTimer-3.0
+-- @release $Id: AceTimer-3.0.lua 1119 2014-10-14 17:23:29Z nevcairiel $
+
+local MAJOR, MINOR = "AceTimer-3.0", 17 -- Bump minor on changes
+local AceTimer, oldminor = LibStub:NewLibrary(MAJOR, MINOR)
+
+if not AceTimer then return end -- No upgrade needed
+AceTimer.activeTimers = AceTimer.activeTimers or {} -- Active timer list
+local activeTimers = AceTimer.activeTimers -- Upvalue our private data
+
+-- Lua APIs
+local type, unpack, next, error, select = type, unpack, next, error, select
+-- WoW APIs
+local GetTime, C_TimerAfter = GetTime, C_Timer.After
+
+local function new(self, loop, func, delay, ...)
+	if delay < 0.01 then
+		delay = 0.01 -- Restrict to the lowest time that the C_Timer API allows us
+	end
+
+	local timer = {...}
+	timer.object = self
+	timer.func = func
+	timer.looping = loop
+	timer.argsCount = select("#", ...)
+	timer.delay = delay
+	timer.ends = GetTime() + delay
+
+	activeTimers[timer] = timer
+
+	-- Create new timer closure to wrap the "timer" object
+	timer.callback = function() 
+		if not timer.cancelled then
+			if type(timer.func) == "string" then
+				-- We manually set the unpack count to prevent issues with an arg set that contains nil and ends with nil
+				-- e.g. local t = {1, 2, nil, 3, nil} print(#t) will result in 2, instead of 5. This fixes said issue.
+				timer.object[timer.func](timer.object, unpack(timer, 1, timer.argsCount))
+			else
+				timer.func(unpack(timer, 1, timer.argsCount))
+			end
+
+			if timer.looping and not timer.cancelled then
+				-- Compensate delay to get a perfect average delay, even if individual times don't match up perfectly
+				-- due to fps differences
+				local time = GetTime()
+				local delay = timer.delay - (time - timer.ends)
+				-- Ensure the delay doesn't go below the threshold
+				if delay < 0.01 then delay = 0.01 end
+				C_TimerAfter(delay, timer.callback)
+				timer.ends = time + delay
+			else
+				activeTimers[timer.handle or timer] = nil
+			end
+		end
+	end
+
+	C_TimerAfter(delay, timer.callback)
+	return timer
+end
+
+--- Schedule a new one-shot timer.
+-- The timer will fire once in `delay` seconds, unless canceled before.
+-- @param callback Callback function for the timer pulse (funcref or method name).
+-- @param delay Delay for the timer, in seconds.
+-- @param ... An optional, unlimited amount of arguments to pass to the callback function.
+-- @usage
+-- MyAddOn = LibStub("AceAddon-3.0"):NewAddon("MyAddOn", "AceTimer-3.0")
+--
+-- function MyAddOn:OnEnable()
+--   self:ScheduleTimer("TimerFeedback", 5)
+-- end
+--
+-- function MyAddOn:TimerFeedback()
+--   print("5 seconds passed")
+-- end
+function AceTimer:ScheduleTimer(func, delay, ...)
+	if not func or not delay then
+		error(MAJOR..": ScheduleTimer(callback, delay, args...): 'callback' and 'delay' must have set values.", 2)
+	end
+	if type(func) == "string" then
+		if type(self) ~= "table" then
+			error(MAJOR..": ScheduleTimer(callback, delay, args...): 'self' - must be a table.", 2)
+		elseif not self[func] then
+			error(MAJOR..": ScheduleTimer(callback, delay, args...): Tried to register '"..func.."' as the callback, but it doesn't exist in the module.", 2)
+		end
+	end
+	return new(self, nil, func, delay, ...)
+end
+
+--- Schedule a repeating timer.
+-- The timer will fire every `delay` seconds, until canceled.
+-- @param callback Callback function for the timer pulse (funcref or method name).
+-- @param delay Delay for the timer, in seconds.
+-- @param ... An optional, unlimited amount of arguments to pass to the callback function.
+-- @usage
+-- MyAddOn = LibStub("AceAddon-3.0"):NewAddon("MyAddOn", "AceTimer-3.0")
+--
+-- function MyAddOn:OnEnable()
+--   self.timerCount = 0
+--   self.testTimer = self:ScheduleRepeatingTimer("TimerFeedback", 5)
+-- end
+--
+-- function MyAddOn:TimerFeedback()
+--   self.timerCount = self.timerCount + 1
+--   print(("%d seconds passed"):format(5 * self.timerCount))
+--   -- run 30 seconds in total
+--   if self.timerCount == 6 then
+--     self:CancelTimer(self.testTimer)
+--   end
+-- end
+function AceTimer:ScheduleRepeatingTimer(func, delay, ...)
+	if not func or not delay then
+		error(MAJOR..": ScheduleRepeatingTimer(callback, delay, args...): 'callback' and 'delay' must have set values.", 2)
+	end
+	if type(func) == "string" then
+		if type(self) ~= "table" then
+			error(MAJOR..": ScheduleRepeatingTimer(callback, delay, args...): 'self' - must be a table.", 2)
+		elseif not self[func] then
+			error(MAJOR..": ScheduleRepeatingTimer(callback, delay, args...): Tried to register '"..func.."' as the callback, but it doesn't exist in the module.", 2)
+		end
+	end
+	return new(self, true, func, delay, ...)
+end
+
+--- Cancels a timer with the given id, registered by the same addon object as used for `:ScheduleTimer`
+-- Both one-shot and repeating timers can be canceled with this function, as long as the `id` is valid
+-- and the timer has not fired yet or was canceled before.
+-- @param id The id of the timer, as returned by `:ScheduleTimer` or `:ScheduleRepeatingTimer`
+function AceTimer:CancelTimer(id)
+	local timer = activeTimers[id]
+
+	if not timer then
+		return false
+	else
+		timer.cancelled = true
+		activeTimers[id] = nil
+		return true
+	end
+end
+
+--- Cancels all timers registered to the current addon object ('self')
+function AceTimer:CancelAllTimers()
+	for k,v in pairs(activeTimers) do
+		if v.object == self then
+			AceTimer.CancelTimer(self, k)
+		end
+	end
+end
+
+--- Returns the time left for a timer with the given id, registered by the current addon object ('self').
+-- This function will return 0 when the id is invalid.
+-- @param id The id of the timer, as returned by `:ScheduleTimer` or `:ScheduleRepeatingTimer`
+-- @return The time left on the timer.
+function AceTimer:TimeLeft(id)
+	local timer = activeTimers[id]
+	if not timer then
+		return 0
+	else
+		return timer.ends - GetTime()
+	end
+end
+
+
+-- ---------------------------------------------------------------------
+-- Upgrading
+
+-- Upgrade from old hash-bucket based timers to C_Timer.After timers.
+if oldminor and oldminor < 10 then
+	-- disable old timer logic
+	AceTimer.frame:SetScript("OnUpdate", nil)
+	AceTimer.frame:SetScript("OnEvent", nil)
+	AceTimer.frame:UnregisterAllEvents()
+	-- convert timers
+	for object,timers in pairs(AceTimer.selfs) do
+		for handle,timer in pairs(timers) do
+			if type(timer) == "table" and timer.callback then
+				local newTimer
+				if timer.delay then
+					newTimer = AceTimer.ScheduleRepeatingTimer(timer.object, timer.callback, timer.delay, timer.arg)
+				else
+					newTimer = AceTimer.ScheduleTimer(timer.object, timer.callback, timer.when - GetTime(), timer.arg)
+				end
+				-- Use the old handle for old timers
+				activeTimers[newTimer] = nil
+				activeTimers[handle] = newTimer
+				newTimer.handle = handle
+			end
+		end
+	end
+	AceTimer.selfs = nil
+	AceTimer.hash = nil
+	AceTimer.debug = nil
+elseif oldminor and oldminor < 17 then
+	-- Upgrade from old animation based timers to C_Timer.After timers.
+	AceTimer.inactiveTimers = nil
+	AceTimer.frame = nil
+	local oldTimers = AceTimer.activeTimers
+	-- Clear old timer table and update upvalue
+	AceTimer.activeTimers = {}
+	activeTimers = AceTimer.activeTimers
+	for handle, timer in pairs(oldTimers) do
+		local newTimer
+		-- Stop the old timer animation
+		local duration, elapsed = timer:GetDuration(), timer:GetElapsed()
+		timer:GetParent():Stop()
+		if timer.looping then
+			newTimer = AceTimer.ScheduleRepeatingTimer(timer.object, timer.func, duration, unpack(timer.args, 1, timer.argsCount))
+		else
+			newTimer = AceTimer.ScheduleTimer(timer.object, timer.func, duration - elapsed, unpack(timer.args, 1, timer.argsCount))
+		end
+		-- Use the old handle for old timers
+		activeTimers[newTimer] = nil
+		activeTimers[handle] = newTimer
+		newTimer.handle = handle
+	end
+
+	-- Migrate transitional handles
+	if oldminor < 13 and AceTimer.hashCompatTable then
+		for handle, id in pairs(AceTimer.hashCompatTable) do
+			local t = activeTimers[id]
+			if t then
+				activeTimers[id] = nil
+				activeTimers[handle] = t
+				t.handle = handle
+			end
+		end
+		AceTimer.hashCompatTable = nil
+	end
+end
+
+-- ---------------------------------------------------------------------
+-- Embed handling
+
+AceTimer.embeds = AceTimer.embeds or {}
+
+local mixins = {
+	"ScheduleTimer", "ScheduleRepeatingTimer",
+	"CancelTimer", "CancelAllTimers",
+	"TimeLeft"
+}
+
+function AceTimer:Embed(target)
+	AceTimer.embeds[target] = true
+	for _,v in pairs(mixins) do
+		target[v] = AceTimer[v]
+	end
+	return target
+end
+
+-- AceTimer:OnEmbedDisable(target)
+-- target (object) - target object that AceTimer is embedded in.
+--
+-- cancel all timers registered for the object
+function AceTimer:OnEmbedDisable(target)
+	target:CancelAllTimers()
+end
+
+for addon in pairs(AceTimer.embeds) do
+	AceTimer:Embed(addon)
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Libs/AceTimer-3.0/AceTimer-3.0.xml	Mon Mar 02 21:28:33 2015 -0300
@@ -0,0 +1,4 @@
+<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/
+..\FrameXML\UI.xsd">
+	<Script file="AceTimer-3.0.lua"/>
+</Ui>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Libs/CallbackHandler-1.0/CallbackHandler-1.0.lua	Mon Mar 02 21:28:33 2015 -0300
@@ -0,0 +1,240 @@
+--[[ $Id: CallbackHandler-1.0.lua 965 2010-08-09 00:47:52Z 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, 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" 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.
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Libs/CallbackHandler-1.0/CallbackHandler-1.0.xml	Mon Mar 02 21:28:33 2015 -0300
@@ -0,0 +1,4 @@
+<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/
+..\FrameXML\UI.xsd">
+	<Script file="CallbackHandler-1.0.lua"/>
+</Ui>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Libs/LibStub/LibStub.lua	Mon Mar 02 21:28:33 2015 -0300
@@ -0,0 +1,30 @@
+-- LibStub is a simple versioning stub meant for use in Libraries.  http://www.wowace.com/wiki/LibStub for more info
+-- LibStub is hereby placed in the Public Domain Credits: Kaelten, Cladhaire, ckknight, Mikk, Ammo, Nevcairiel, joshborke
+local LIBSTUB_MAJOR, LIBSTUB_MINOR = "LibStub", 2  -- NEVER MAKE THIS AN SVN REVISION! IT NEEDS TO BE USABLE IN ALL REPOS!
+local LibStub = _G[LIBSTUB_MAJOR]
+
+if not LibStub or LibStub.minor < LIBSTUB_MINOR then
+	LibStub = LibStub or {libs = {}, minors = {} }
+	_G[LIBSTUB_MAJOR] = LibStub
+	LibStub.minor = LIBSTUB_MINOR
+	
+	function LibStub:NewLibrary(major, minor)
+		assert(type(major) == "string", "Bad argument #2 to `NewLibrary' (string expected)")
+		minor = assert(tonumber(strmatch(minor, "%d+")), "Minor version must either be a number or contain a number.")
+		
+		local oldminor = self.minors[major]
+		if oldminor and oldminor >= minor then return nil end
+		self.minors[major], self.libs[major] = minor, self.libs[major] or {}
+		return self.libs[major], oldminor
+	end
+	
+	function LibStub:GetLibrary(major, silent)
+		if not self.libs[major] and not silent then
+			error(("Cannot find a library instance of %q."):format(tostring(major)), 2)
+		end
+		return self.libs[major], self.minors[major]
+	end
+	
+	function LibStub:IterateLibraries() return pairs(self.libs) end
+	setmetatable(LibStub, { __call = LibStub.GetLibrary })
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Libs/LibStub/LibStub.toc	Mon Mar 02 21:28:33 2015 -0300
@@ -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: r95
+## X-Curse-Project-Name: LibStub
+## X-Curse-Project-ID: libstub
+## X-Curse-Repository-ID: wow/libstub/mainline
+
+LibStub.lua
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Libs/LibStub/tests/test.lua	Mon Mar 02 21:28:33 2015 -0300
@@ -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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Libs/LibStub/tests/test2.lua	Mon Mar 02 21:28:33 2015 -0300
@@ -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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Libs/LibStub/tests/test3.lua	Mon Mar 02 21:28:33 2015 -0300
@@ -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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Libs/LibStub/tests/test4.lua	Mon Mar 02 21:28:33 2015 -0300
@@ -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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Libs/libs.xml	Mon Mar 02 21:28:33 2015 -0300
@@ -0,0 +1,6 @@
+<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/..\FrameXML\UI.xsd">
+	<Script file="LibStub\LibStub.lua"/>
+	<Include file="CallbackHandler-1.0\CallbackHandler-1.0.xml"/>
+	<Include file="AceComm-3.0\AceComm-3.0.xml" />
+	<Include file="AceTimer-3.0\AceTimer-3.0.xml" />
+</Ui>
\ No newline at end of file