diff --git a/init/libs.xml b/init/libs.xml
index 1adadb2e..99b31c29 100644
--- a/init/libs.xml
+++ b/init/libs.xml
@@ -7,4 +7,5 @@
+
diff --git a/libs/libtotem.lua b/libs/libtotem.lua
new file mode 100644
index 00000000..5b444bc7
--- /dev/null
+++ b/libs/libtotem.lua
@@ -0,0 +1,174 @@
+-- load pfUI environment
+setfenv(1, pfUI:GetEnvironment())
+
+--[[ libtotem ]]--
+-- A pfUI library that tries to emulate the TotemAPI that was introduced in Patch 2.4.
+-- It detects and saves all current totems of the player and returns information based
+-- on the totem slot ID. The function GetTotemInfo is supposed to work as it would
+-- on later expansions.
+--
+-- GetTotemInfo(id)
+-- Returns totem informations on the givent totem slot
+-- active, name, start, duration, icon
+
+-- return instantly if we're not on a vanilla client
+if pfUI.client > 11200 then return end
+
+-- return instantly when another libdebuff is already active
+if pfUI.api.libtotem then return end
+
+MAX_TOTEMS = MAX_TOTEMS or 4
+FIRE_TOTEM_SLOT = FIRE_TOTEM_SLOT or 1
+EARTH_TOTEM_SLOT = EARTH_TOTEM_SLOT or 2
+WATER_TOTEM_SLOT = WATER_TOTEM_SLOT or 3
+AIR_TOTEM_SLOT = AIR_TOTEM_SLOT or 4
+
+local _, class = UnitClass("player")
+
+local libtotem
+local queue = { ["slot"] = nil, ["name"] = nil, ["start"] = nil, ["duration"] = nil, ["icon"] = nil }
+local active = { [1] = {}, [2] = {}, [3] = {}, [4] = {} }
+local totems = {
+ [FIRE_TOTEM_SLOT] = {
+ --Fire Nova Totem (Fire)
+ ["Spell_Fire_SealOfFire"] = {[-1] = 5},
+ --Flametongue Totem (Fire)
+ ["Spell_Nature_GuardianWard"] = {[-1] = 120},
+ --Frost Resistance Totem (Fire)
+ ["Spell_FrostResistanceTotem_01"] = {[-1] = 120},
+ --Magma Totem (Fire)
+ ["Spell_Fire_SelfDestruct"] = {[-1] = 20},
+ --Searing Totem (Fire)
+ ["Spell_Fire_SearingTotem"] = {[-1] = 55,[1] = 30,[2] = 35,[3] = 40,[4] = 45,[5] = 50,[6] = 55},
+ },
+ [EARTH_TOTEM_SLOT] = {
+ --Earthbind Totem (Earth)
+ ["Spell_Nature_StrengthOfEarthTotem02"] = {[-1] = 45},
+ --Stoneclaw Totem (Earth)
+ ["Spell_Nature_StoneClawTotem"] = {[-1] = 15},
+ --Stoneskin Totem (Earth)
+ ["Spell_Nature_StoneSkinTotem"] = {[-1] = 120},
+ --Strength of Earth Totem (Earth)
+ ["Spell_Nature_EarthBindTotem"] = {[-1] = 120},
+ --Tremor Totem (Earth)
+ ["Spell_Nature_TremorTotem"] = {[-1] = 120},
+ },
+ [WATER_TOTEM_SLOT] = {
+ -- Disease Cleansing Totem (Water)
+ ["Spell_Nature_DiseaseCleansingTotem"] = {[-1] = 120},
+ --Fire Resistance Totem (Water)
+ ["Spell_FireResistanceTotem_01"] = {[-1] = 120},
+ --Healing Stream Totem (Water)
+ ["INV_Spear_04"] = {[-1] = 60},
+ --Mana Spring Totem (Water)
+ ["Spell_Nature_ManaRegenTotem"] = {[-1] = 60},
+ --Mana Tide Totem (Water)
+ ["Spell_Frost_SummonWaterElemental"] = {[-1] = 12},
+ --Poison Cleansing Totem (Water)
+ ["Spell_Nature_PoisonCleansingTotem"] = {[-1] = 120},
+ },
+ [AIR_TOTEM_SLOT] = {
+ --Grace of Air Totem (Air)
+ ["Spell_Nature_InvisibilityTotem"] = {[-1] = 120},
+ --Grounding Totem (Air)
+ ["Spell_Nature_GroundingTotem"] = {[-1] = 45},
+ --Nature Resistance Totem (Air)
+ ["Spell_Nature_NatureResistanceTotem"] = {[-1] = 120},
+ --Tranquil Air Totem (Air)
+ ["Spell_Nature_Brilliance"] = {[-1] = 120},
+ --Windfury Totem (Air)
+ ["Spell_Nature_Windfury"] = {[-1] = 120},
+ --Windwall Totem (Air)
+ ["Spell_Nature_EarthBind"] = {[-1] = 120},
+ },
+}
+
+GetTotemInfo = function(id)
+ if not active[id] or not active[id].name then return end
+ if active[id].start + active[id].duration - GetTime() < 0 then
+ libtotem:Clean(id)
+ return nil
+ end
+
+ return 1, active[id].name, active[id].start, active[id].duration, active[id].icon
+end
+
+pfUI.api.libtotem = libtotem
+
+if class ~= "SHAMAN" then return end
+
+libtotem = CreateFrame("Frame")
+libtotem:RegisterEvent("SPELLCAST_STOP")
+libtotem:RegisterEvent("PLAYER_DEAD")
+libtotem:SetScript("OnEvent", function()
+ if event == "PLAYER_DEAD" then
+ for i = 1,4 do
+ libtotem:Clean(i)
+ end
+ elseif event == "SPELLCAST_STOP" then
+ if queue.slot and queue.name then
+ active[queue.slot].name = queue.name
+ active[queue.slot].duration = queue.duration
+ active[queue.slot].icon = queue.icon
+ active[queue.slot].start = GetTime()
+ end
+
+ queue.slot = nil
+ queue.name = nil
+ end
+end)
+
+libtotem.Clean = function(self, slot)
+ active[slot].name = nil
+ active[slot].start = nil
+ active[slot].duration = nil
+ active[slot].icon = nil
+end
+
+libtotem.CheckAddQueue = function(self, name, rank, icon)
+ for slot = 1, 4 do
+ for texture, data in pairs(totems[slot]) do
+ if string.find(icon, texture, 1) then
+ if rank then -- try to obtain plain rank number
+ _, _, rank = string.find(rank,"%s(%d+)")
+ end
+
+ queue.slot = slot
+ queue.name = name
+ queue.icon = icon
+ if rank and tonumber(rank) and data[tonumber(rank)] then
+ queue.duration = data[tonumber(rank)]
+ else
+ queue.duration = data[-1]
+ end
+
+ return true
+ end
+ end
+ end
+
+ return nil
+end
+
+-- Check for totem spell casts
+hooksecurefunc("CastSpell", function(id, bookType)
+ local name, rank, icon = libspell.GetSpellInfo(id, bookType)
+ if not name then return end
+ if libtotem:CheckAddQueue(name, rank, icon) then return end
+end, true)
+
+hooksecurefunc("CastSpellByName", function(effect, target)
+ local name, rank, icon = libspell.GetSpellInfo(effect)
+ if not name then return end
+ if libtotem:CheckAddQueue(name, rank, icon) then return end
+end, true)
+
+local scanner = libtipscan:GetScanner("prediction")
+hooksecurefunc("UseAction", function(slot, target, selfcast)
+ if GetActionText(slot) or not IsCurrentAction(slot) then return end
+ scanner:SetAction(slot)
+ local name, rank = scanner:Line(1)
+ local icon = GetActionTexture(slot)
+ if not name then return end
+ if libtotem:CheckAddQueue(name, rank, icon) then return end
+end, true)